• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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
Rfc1123_DateTimeNow()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
response_callback(void * cls,void * buffer,size_t max,bool * more)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
response_done_callback(void * cls,struct SPDY_Response * response,struct SPDY_Request * request,enum SPDY_RESPONSE_RESULT status,bool streamopened)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
standard_request_handler(void * cls,struct SPDY_Request * request,uint8_t priority,const char * method,const char * path,const char * version,const char * host,const char * scheme,struct SPDY_NameValue * headers,bool more)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
main(int argc,char * const * argv)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