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