1 /* 2 This file is part of libmicrospdy 3 Copyright Copyright (C) 2013 Andrey Uzunov 4 5 This program is free software: you can redistribute it and/or modify 6 it under the terms of the GNU General Public License as published by 7 the Free Software Foundation, either version 3 of the License, or 8 (at your option) any later version. 9 10 This program is distributed in the hope that it will be useful, 11 but WITHOUT ANY WARRANTY; without even the implied warranty of 12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 GNU General Public License for more details. 14 15 You should have received a copy of the GNU General Public License 16 along with this program. If not, see <http://www.gnu.org/licenses/>. 17 */ 18 19 /** 20 * @file fileserver.c 21 * @brief Simple example how the lib can be used for serving 22 * files directly read from the system 23 * @author Andrey Uzunov 24 */ 25 26 //for asprintf 27 #define _GNU_SOURCE 28 29 #include <unistd.h> 30 #include <stdlib.h> 31 #include <stdint.h> 32 #include <stdbool.h> 33 #include <string.h> 34 #include <stdio.h> 35 #include <ctype.h> 36 #include <errno.h> 37 #include "microspdy.h" 38 #include "time.h" 39 40 41 int run = 1; 42 char* basedir; 43 44 45 #define GET_MIME_TYPE(fname, mime) do {\ 46 unsigned int __len = strlen(fname);\ 47 if (__len < 4 || '.' != (fname)[__len - 4]) \ 48 { \ 49 (mime) = strdup("application/octet-stream");\ 50 printf("MIME for %s is applic...\n", (fname));\ 51 }\ 52 else {\ 53 const char * __ext = &(fname)[__len - 3];\ 54 if(0 == strcmp(__ext, "jpg")) (mime) = strdup("image/jpeg");\ 55 else if(0 == strcmp(__ext, "png")) (mime) = strdup("image/png");\ 56 else if(0 == strcmp(__ext, "css")) (mime) = strdup("text/css");\ 57 else if(0 == strcmp(__ext, "gif")) (mime) = strdup("image/gif");\ 58 else if(0 == strcmp(__ext, "htm")) (mime) = strdup("text/html");\ 59 else \ 60 { \ 61 (mime) = strdup("application/octet-stream");\ 62 printf("MIME for %s is applic...\n", (fname));\ 63 }\ 64 }\ 65 if(NULL == (mime))\ 66 {\ 67 printf("no memory\n");\ 68 abort();\ 69 }\ 70 } while (0) 71 72 73 static const char *DAY_NAMES[] = 74 { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" }; 75 76 static const char *MONTH_NAMES[] = 77 { "Jan", "Feb", "Mar", "Apr", "May", "Jun", 78 "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" }; 79 80 //taken from http://stackoverflow.com/questions/2726975/how-can-i-generate-an-rfc1123-date-string-from-c-code-win32 81 //and modified for linux 82 char *Rfc1123_DateTimeNow() 83 { 84 const int RFC1123_TIME_LEN = 29; 85 time_t t; 86 struct tm tm; 87 char * buf = malloc(RFC1123_TIME_LEN+1); 88 89 if (NULL == buf) 90 return NULL; 91 time(&t); 92 gmtime_r( &t, &tm); 93 94 strftime(buf, RFC1123_TIME_LEN+1, "---, %d --- %Y %H:%M:%S GMT", &tm); 95 memcpy(buf, DAY_NAMES[tm.tm_wday], 3); 96 memcpy(buf+8, MONTH_NAMES[tm.tm_mon], 3); 97 98 return buf; 99 } 100 101 102 ssize_t 103 response_callback (void *cls, 104 void *buffer, 105 size_t max, 106 bool *more) 107 { 108 FILE *fd =(FILE*)cls; 109 110 int ret = fread(buffer,1,max,fd); 111 *more = feof(fd) == 0; 112 113 //if(!(*more)) 114 // fclose(fd); 115 116 return ret; 117 } 118 119 120 void 121 response_done_callback(void *cls, 122 struct SPDY_Response *response, 123 struct SPDY_Request *request, 124 enum SPDY_RESPONSE_RESULT status, 125 bool streamopened) 126 { 127 (void)streamopened; 128 (void)status; 129 //printf("answer for %s was sent\n", (char *)cls); 130 131 /*if(SPDY_RESPONSE_RESULT_SUCCESS != status) 132 { 133 printf("answer for %s was NOT sent, %i\n", (char *)cls,status); 134 }*/ 135 136 SPDY_destroy_request(request); 137 SPDY_destroy_response(response); 138 if(NULL!=cls)fclose(cls); 139 } 140 141 void 142 standard_request_handler(void *cls, 143 struct SPDY_Request * request, 144 uint8_t priority, 145 const char *method, 146 const char *path, 147 const char *version, 148 const char *host, 149 const char *scheme, 150 struct SPDY_NameValue * headers, 151 bool more) 152 { 153 (void)cls; 154 (void)request; 155 (void)priority; 156 (void)host; 157 (void)scheme; 158 (void)headers; 159 (void)method; 160 (void)version; 161 (void)more; 162 163 struct SPDY_Response *response=NULL; 164 struct SPDY_NameValue *resp_headers; 165 char *fname; 166 char *fsize; 167 char *mime=NULL; 168 char *date=NULL; 169 ssize_t filesize = -666; 170 FILE *fd = NULL; 171 int ret = -666; 172 173 //printf("received request for '%s %s %s'\n", method, path, version); 174 if(strlen(path) > 1 && NULL == strstr(path, "..") && '/' == path[0]) 175 { 176 asprintf(&fname,"%s%s",basedir,path); 177 if(0 == access(fname, R_OK)) 178 { 179 if(NULL == (fd = fopen(fname,"r")) 180 || 0 != (ret = fseek(fd, 0L, SEEK_END)) 181 || -1 == (filesize = ftell(fd)) 182 || 0 != (ret = fseek(fd, 0L, SEEK_SET))) 183 { 184 printf("Error on opening %s\n%p %i %zd\n",fname, fd, ret, filesize); 185 response = SPDY_build_response(SPDY_HTTP_INTERNAL_SERVER_ERROR,NULL,SPDY_HTTP_VERSION_1_1,NULL,NULL,0); 186 } 187 else 188 { 189 if(NULL == (resp_headers = SPDY_name_value_create())) 190 { 191 printf("SPDY_name_value_create failed\n"); 192 abort(); 193 } 194 195 date = Rfc1123_DateTimeNow(); 196 if(NULL == date 197 || SPDY_YES != SPDY_name_value_add(resp_headers,SPDY_HTTP_HEADER_DATE,date)) 198 { 199 printf("SPDY_name_value_add or Rfc1123_DateTimeNow failed\n"); 200 abort(); 201 } 202 free(date); 203 204 if(-1 == asprintf(&fsize, "%zd", filesize) 205 || SPDY_YES != SPDY_name_value_add(resp_headers,SPDY_HTTP_HEADER_CONTENT_LENGTH,fsize)) 206 { 207 printf("SPDY_name_value_add or asprintf failed\n"); 208 abort(); 209 } 210 free(fsize); 211 212 GET_MIME_TYPE(path,mime); 213 if(SPDY_YES != SPDY_name_value_add(resp_headers,SPDY_HTTP_HEADER_CONTENT_TYPE,mime)) 214 { 215 printf("SPDY_name_value_add failed\n"); 216 abort(); 217 } 218 free(mime); 219 220 if(SPDY_YES != SPDY_name_value_add(resp_headers,SPDY_HTTP_HEADER_SERVER,"libmicrospdy/fileserver")) 221 { 222 printf("SPDY_name_value_add failed\n"); 223 abort(); 224 } 225 226 response = SPDY_build_response_with_callback(200,NULL, 227 SPDY_HTTP_VERSION_1_1,resp_headers,&response_callback,fd,SPDY_MAX_SUPPORTED_FRAME_SIZE); 228 SPDY_name_value_destroy(resp_headers); 229 } 230 231 if(NULL==response){ 232 printf("no response obj\n"); 233 abort(); 234 } 235 236 if(SPDY_queue_response(request,response,true,false,&response_done_callback,fd)!=SPDY_YES) 237 { 238 printf("queue\n"); 239 abort(); 240 } 241 242 free(fname); 243 return; 244 } 245 free(fname); 246 } 247 248 if(strcmp(path,"/close")==0) 249 { 250 run = 0; 251 } 252 253 response = SPDY_build_response(SPDY_HTTP_NOT_FOUND,NULL,SPDY_HTTP_VERSION_1_1,NULL,NULL,0); 254 printf("Not found %s\n",path); 255 256 if(NULL==response){ 257 printf("no response obj\n"); 258 abort(); 259 } 260 261 if(SPDY_queue_response(request,response,true,false,&response_done_callback,NULL)!=SPDY_YES) 262 { 263 printf("queue\n"); 264 abort(); 265 } 266 } 267 268 int 269 main (int argc, char *const *argv) 270 { 271 unsigned long long timeoutlong=0; 272 struct timeval timeout; 273 int ret; 274 fd_set read_fd_set; 275 fd_set write_fd_set; 276 fd_set except_fd_set; 277 int maxfd = -1; 278 struct SPDY_Daemon *daemon; 279 280 if(argc != 5) 281 { 282 printf("Usage: %s cert-file key-file base-dir port\n", argv[0]); 283 return 1; 284 } 285 286 SPDY_init(); 287 288 daemon = SPDY_start_daemon(atoi(argv[4]), 289 argv[1], 290 argv[2], 291 NULL, 292 NULL, 293 &standard_request_handler, 294 NULL, 295 NULL, 296 SPDY_DAEMON_OPTION_SESSION_TIMEOUT, 297 1800, 298 SPDY_DAEMON_OPTION_END); 299 300 if(NULL==daemon){ 301 printf("no daemon\n"); 302 return 1; 303 } 304 305 basedir = argv[3]; 306 307 do 308 { 309 FD_ZERO(&read_fd_set); 310 FD_ZERO(&write_fd_set); 311 FD_ZERO(&except_fd_set); 312 313 ret = SPDY_get_timeout(daemon, &timeoutlong); 314 if(SPDY_NO == ret || timeoutlong > 1000) 315 { 316 timeout.tv_sec = 1; 317 timeout.tv_usec = 0; 318 } 319 else 320 { 321 timeout.tv_sec = timeoutlong / 1000; 322 timeout.tv_usec = (timeoutlong % 1000) * 1000; 323 } 324 325 maxfd = SPDY_get_fdset (daemon, 326 &read_fd_set, 327 &write_fd_set, 328 &except_fd_set); 329 330 ret = select(maxfd+1, &read_fd_set, &write_fd_set, &except_fd_set, &timeout); 331 332 switch(ret) { 333 case -1: 334 printf("select error: %i\n", errno); 335 break; 336 case 0: 337 338 break; 339 default: 340 SPDY_run(daemon); 341 342 break; 343 } 344 } 345 while(run); 346 347 SPDY_stop_daemon(daemon); 348 349 SPDY_deinit(); 350 351 return 0; 352 } 353 354