Home | History | Annotate | Download | only in examples
      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