diff options
Diffstat (limited to 'src/microspdy/daemon.c')
-rw-r--r-- | src/microspdy/daemon.c | 515 |
1 files changed, 515 insertions, 0 deletions
diff --git a/src/microspdy/daemon.c b/src/microspdy/daemon.c new file mode 100644 index 00000000..47e49ea1 --- /dev/null +++ b/src/microspdy/daemon.c | |||
@@ -0,0 +1,515 @@ | |||
1 | /* | ||
2 | This file is part of libmicrospdy | ||
3 | Copyright (C) 2012 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 daemon.c | ||
21 | * @brief daemon functionality | ||
22 | * @author Andrey Uzunov | ||
23 | */ | ||
24 | |||
25 | #include "platform.h" | ||
26 | #include "structures.h" | ||
27 | #include "internal.h" | ||
28 | #include "session.h" | ||
29 | #include "tls.h" | ||
30 | |||
31 | |||
32 | /** | ||
33 | * Default implementation of the panic function, | ||
34 | * prints an error message and aborts. | ||
35 | * | ||
36 | * @param cls unused | ||
37 | * @param file name of the file with the problem | ||
38 | * @param line line number with the problem | ||
39 | * @param reason error message with details | ||
40 | */ | ||
41 | static void | ||
42 | spdyf_panic_std (void *cls, | ||
43 | const char *file, | ||
44 | unsigned int line, | ||
45 | const char *reason) | ||
46 | { | ||
47 | (void)cls; | ||
48 | fprintf (stdout, "Fatal error in libmicrospdy %s:%u: %s\n", | ||
49 | file, line, reason); | ||
50 | //raise(SIGINT); //used for gdb | ||
51 | abort (); | ||
52 | } | ||
53 | |||
54 | |||
55 | /** | ||
56 | * Global handler for fatal errors. | ||
57 | */ | ||
58 | SPDY_PanicCallback spdyf_panic = &spdyf_panic_std; | ||
59 | |||
60 | |||
61 | /** | ||
62 | * Global closure argument for "spdyf_panic". | ||
63 | */ | ||
64 | void *spdyf_panic_cls; | ||
65 | |||
66 | |||
67 | /** | ||
68 | * Free resources associated with all closed connections. | ||
69 | * (destroy responses, free buffers, etc.). | ||
70 | * | ||
71 | * @param daemon daemon to clean up | ||
72 | */ | ||
73 | static void | ||
74 | spdyf_cleanup_sessions (struct SPDY_Daemon *daemon) | ||
75 | { | ||
76 | struct SPDY_Session *session; | ||
77 | |||
78 | while (NULL != (session = daemon->cleanup_head)) | ||
79 | { | ||
80 | DLL_remove (daemon->cleanup_head, | ||
81 | daemon->cleanup_tail, | ||
82 | session); | ||
83 | |||
84 | SPDYF_session_destroy(session); | ||
85 | } | ||
86 | } | ||
87 | |||
88 | |||
89 | /** | ||
90 | * Closing of all connections handled by the daemon. | ||
91 | * | ||
92 | * @param daemon SPDY daemon | ||
93 | */ | ||
94 | static void | ||
95 | spdyf_close_all_sessions (struct SPDY_Daemon *daemon) | ||
96 | { | ||
97 | struct SPDY_Session *session; | ||
98 | |||
99 | while (NULL != (session = daemon->sessions_head)) | ||
100 | { | ||
101 | //prepare GOAWAY frame | ||
102 | SPDYF_prepare_goaway(session, SPDY_GOAWAY_STATUS_OK, true); | ||
103 | //try to send the frame (it is best effort, so it will maybe sent) | ||
104 | SPDYF_session_write(session,true); | ||
105 | SPDYF_session_close(session); | ||
106 | } | ||
107 | |||
108 | spdyf_cleanup_sessions(daemon); | ||
109 | } | ||
110 | |||
111 | |||
112 | /** | ||
113 | * Parse a list of options given as varargs. | ||
114 | * | ||
115 | * @param daemon the daemon to initialize | ||
116 | * @param valist the options | ||
117 | * @return SPDY_YES on success, SPDY_NO on error | ||
118 | */ | ||
119 | static int | ||
120 | spdyf_parse_options_va (struct SPDY_Daemon *daemon, | ||
121 | va_list valist) | ||
122 | { | ||
123 | enum SPDY_DAEMON_OPTION opt; | ||
124 | |||
125 | while (SPDY_DAEMON_OPTION_END != (opt = (enum SPDY_DAEMON_OPTION) va_arg (valist, int))) | ||
126 | { | ||
127 | if(opt & daemon->options) | ||
128 | { | ||
129 | SPDYF_DEBUG("Daemon option %i used twice",opt); | ||
130 | return SPDY_NO; | ||
131 | } | ||
132 | daemon->options |= opt; | ||
133 | |||
134 | switch (opt) | ||
135 | { | ||
136 | case SPDY_DAEMON_OPTION_SESSION_TIMEOUT: | ||
137 | daemon->session_timeout = va_arg (valist, unsigned int); | ||
138 | break; | ||
139 | case SPDY_DAEMON_OPTION_SOCK_ADDR: | ||
140 | daemon->address = va_arg (valist, struct sockaddr *); | ||
141 | break; | ||
142 | case SPDY_DAEMON_OPTION_FLAGS: | ||
143 | daemon->flags = va_arg (valist, enum SPDY_DAEMON_FLAG); | ||
144 | break; | ||
145 | default: | ||
146 | SPDYF_DEBUG("Wrong option for the daemon %i",opt); | ||
147 | return SPDY_NO; | ||
148 | } | ||
149 | } | ||
150 | return SPDY_YES; | ||
151 | } | ||
152 | |||
153 | |||
154 | void | ||
155 | SPDY_set_panic_func (SPDY_PanicCallback cb, | ||
156 | void *cls) | ||
157 | { | ||
158 | spdyf_panic = cb; | ||
159 | spdyf_panic_cls = cls; | ||
160 | } | ||
161 | |||
162 | |||
163 | struct SPDY_Daemon * | ||
164 | SPDYF_start_daemon_va (uint16_t port, | ||
165 | const char *certfile, | ||
166 | const char *keyfile, | ||
167 | SPDY_NewSessionCallback nscb, | ||
168 | SPDY_SessionClosedCallback sccb, | ||
169 | SPDY_NewRequestCallback nrcb, | ||
170 | SPDY_NewPOSTDataCallback npdcb, | ||
171 | SPDYF_NewStreamCallback fnscb, | ||
172 | void * cls, | ||
173 | void * fcls, | ||
174 | va_list valist) | ||
175 | { | ||
176 | struct SPDY_Daemon *daemon = NULL; | ||
177 | int afamily; | ||
178 | int option_on = 1; | ||
179 | int ret; | ||
180 | struct sockaddr_in* servaddr4 = NULL; | ||
181 | #if HAVE_INET6 | ||
182 | struct sockaddr_in6* servaddr6 = NULL; | ||
183 | #endif | ||
184 | socklen_t addrlen; | ||
185 | |||
186 | if (NULL == (daemon = malloc (sizeof (struct SPDY_Daemon)))) | ||
187 | { | ||
188 | SPDYF_DEBUG("malloc"); | ||
189 | return NULL; | ||
190 | } | ||
191 | memset (daemon, 0, sizeof (struct SPDY_Daemon)); | ||
192 | daemon->socket_fd = -1; | ||
193 | daemon->port = port; | ||
194 | if (NULL == (daemon->certfile = strdup (certfile))) | ||
195 | { | ||
196 | SPDYF_DEBUG("str"); | ||
197 | goto free_and_fail; | ||
198 | } | ||
199 | if (NULL == (daemon->keyfile = strdup (keyfile))) | ||
200 | { | ||
201 | SPDYF_DEBUG("str"); | ||
202 | goto free_and_fail; | ||
203 | } | ||
204 | daemon->new_session_cb = nscb; | ||
205 | daemon->session_closed_cb = sccb; | ||
206 | daemon->new_request_cb = nrcb; | ||
207 | daemon->new_post_data_cb = npdcb; | ||
208 | daemon->cls = cls; | ||
209 | daemon->fcls = fcls; | ||
210 | daemon->fnew_stream_cb = fnscb; | ||
211 | |||
212 | if(SPDY_YES != spdyf_parse_options_va (daemon, valist)) | ||
213 | { | ||
214 | SPDYF_DEBUG("parse"); | ||
215 | goto free_and_fail; | ||
216 | } | ||
217 | |||
218 | if(!port && NULL == daemon->address) | ||
219 | { | ||
220 | SPDYF_DEBUG("Port is 0"); | ||
221 | goto free_and_fail; | ||
222 | } | ||
223 | |||
224 | #if HAVE_INET6 | ||
225 | //handling IPv6 | ||
226 | if((daemon->flags & SPDY_DAEMON_FLAG_ONLY_IPV6) | ||
227 | && NULL != daemon->address && AF_INET6 != daemon->address->sa_family) | ||
228 | { | ||
229 | SPDYF_DEBUG("SPDY_DAEMON_FLAG_ONLY_IPV6 set but IPv4 address provided"); | ||
230 | goto free_and_fail; | ||
231 | } | ||
232 | |||
233 | if(NULL == daemon->address) | ||
234 | { | ||
235 | addrlen = sizeof (struct sockaddr_in6); | ||
236 | |||
237 | if (NULL == (servaddr6 = malloc (addrlen))) | ||
238 | { | ||
239 | SPDYF_DEBUG("malloc"); | ||
240 | goto free_and_fail; | ||
241 | } | ||
242 | memset (servaddr6, 0, addrlen); | ||
243 | servaddr6->sin6_family = AF_INET6; | ||
244 | servaddr6->sin6_addr = in6addr_any; | ||
245 | servaddr6->sin6_port = htons (port); | ||
246 | daemon->address = (struct sockaddr *) servaddr6; | ||
247 | } | ||
248 | |||
249 | afamily = AF_INET6 == daemon->address->sa_family ? PF_INET6 : PF_INET; | ||
250 | #else | ||
251 | //handling IPv4 | ||
252 | if(daemon->flags & SPDY_DAEMON_FLAG_ONLY_IPV6) | ||
253 | { | ||
254 | SPDYF_DEBUG("SPDY_DAEMON_FLAG_ONLY_IPV6 set but no support"); | ||
255 | goto free_and_fail; | ||
256 | } | ||
257 | |||
258 | if(NULL == daemon->address) | ||
259 | { | ||
260 | addrlen = sizeof (struct sockaddr_in); | ||
261 | |||
262 | if (NULL == (servaddr4 = malloc (addrlen))) | ||
263 | { | ||
264 | SPDYF_DEBUG("malloc"); | ||
265 | goto free_and_fail; | ||
266 | } | ||
267 | memset (servaddr4, 0, addrlen); | ||
268 | servaddr4->sin_family = AF_INET; | ||
269 | servaddr4->sin_addr = INADDR_ANY; | ||
270 | servaddr4->sin_port = htons (port); | ||
271 | daemon->address = (struct sockaddr *) servaddr4; | ||
272 | } | ||
273 | |||
274 | afamily = PF_INET; | ||
275 | #endif | ||
276 | |||
277 | daemon->socket_fd = socket (afamily, SOCK_STREAM, 0); | ||
278 | if (-1 == daemon->socket_fd) | ||
279 | { | ||
280 | SPDYF_DEBUG("sock"); | ||
281 | goto free_and_fail; | ||
282 | } | ||
283 | |||
284 | //setting option for the socket to reuse address | ||
285 | ret = setsockopt(daemon->socket_fd, SOL_SOCKET, SO_REUSEADDR, &option_on, sizeof(option_on)); | ||
286 | if(ret) | ||
287 | { | ||
288 | SPDYF_DEBUG("WARNING: SO_REUSEADDR was not set for the server"); | ||
289 | } | ||
290 | |||
291 | #if HAVE_INET6 | ||
292 | if(daemon->flags & SPDY_DAEMON_FLAG_ONLY_IPV6) | ||
293 | { | ||
294 | ret = setsockopt(daemon->socket_fd, IPPROTO_IPV6, IPV6_V6ONLY, &option_on, sizeof(option_on)); | ||
295 | if(ret) | ||
296 | { | ||
297 | SPDYF_DEBUG("setsockopt with IPPROTO_IPV6 failed"); | ||
298 | goto free_and_fail; | ||
299 | } | ||
300 | } | ||
301 | #endif | ||
302 | |||
303 | if (-1 == bind (daemon->socket_fd, daemon->address, addrlen)) | ||
304 | { | ||
305 | SPDYF_DEBUG("bind %i",errno); | ||
306 | goto free_and_fail; | ||
307 | } | ||
308 | |||
309 | if (listen (daemon->socket_fd, 20) < 0) | ||
310 | { | ||
311 | SPDYF_DEBUG("listen %i",errno); | ||
312 | goto free_and_fail; | ||
313 | } | ||
314 | |||
315 | if(SPDY_YES != SPDYF_tls_init(daemon)) | ||
316 | { | ||
317 | SPDYF_DEBUG("tls"); | ||
318 | goto free_and_fail; | ||
319 | } | ||
320 | |||
321 | return daemon; | ||
322 | |||
323 | //for GOTO | ||
324 | free_and_fail: | ||
325 | if(daemon->socket_fd > 0) | ||
326 | close (daemon->socket_fd); | ||
327 | |||
328 | free(servaddr4); | ||
329 | #if HAVE_INET6 | ||
330 | free(servaddr6); | ||
331 | #endif | ||
332 | if(NULL != daemon->certfile) | ||
333 | free(daemon->certfile); | ||
334 | if(NULL != daemon->keyfile) | ||
335 | free(daemon->keyfile); | ||
336 | free (daemon); | ||
337 | |||
338 | return NULL; | ||
339 | } | ||
340 | |||
341 | |||
342 | void | ||
343 | SPDYF_stop_daemon (struct SPDY_Daemon *daemon) | ||
344 | { | ||
345 | SPDYF_tls_deinit(daemon); | ||
346 | |||
347 | shutdown (daemon->socket_fd, SHUT_RDWR); | ||
348 | spdyf_close_all_sessions (daemon); | ||
349 | close (daemon->socket_fd); | ||
350 | |||
351 | if(!(SPDY_DAEMON_OPTION_SOCK_ADDR & daemon->options)) | ||
352 | free(daemon->address); | ||
353 | |||
354 | free(daemon->certfile); | ||
355 | free(daemon->keyfile); | ||
356 | |||
357 | free(daemon); | ||
358 | } | ||
359 | |||
360 | |||
361 | int | ||
362 | SPDYF_get_timeout (struct SPDY_Daemon *daemon, | ||
363 | unsigned long long *timeout) | ||
364 | { | ||
365 | time_t earliest_deadline = 0; | ||
366 | time_t now; | ||
367 | struct SPDY_Session *pos; | ||
368 | bool have_timeout; | ||
369 | |||
370 | if(0 == daemon->session_timeout) | ||
371 | return SPDY_NO; | ||
372 | |||
373 | now = SPDYF_monotonic_time(); | ||
374 | have_timeout = false; | ||
375 | for (pos = daemon->sessions_head; NULL != pos; pos = pos->next) | ||
376 | { | ||
377 | if ( (! have_timeout) || | ||
378 | (earliest_deadline > pos->last_activity + daemon->session_timeout) ) | ||
379 | earliest_deadline = pos->last_activity + daemon->session_timeout; | ||
380 | |||
381 | have_timeout = true; | ||
382 | |||
383 | if (SPDY_YES == SPDYF_tls_is_pending(pos)) | ||
384 | { | ||
385 | earliest_deadline = 0; | ||
386 | break; | ||
387 | } | ||
388 | } | ||
389 | |||
390 | if (!have_timeout) | ||
391 | return SPDY_NO; | ||
392 | if (earliest_deadline < now) | ||
393 | *timeout = 0; | ||
394 | else | ||
395 | //*timeout = 1000 * (1 + earliest_deadline - now); | ||
396 | *timeout = earliest_deadline - now; | ||
397 | |||
398 | return SPDY_YES; | ||
399 | } | ||
400 | |||
401 | |||
402 | int | ||
403 | SPDYF_get_fdset (struct SPDY_Daemon *daemon, | ||
404 | fd_set *read_fd_set, | ||
405 | fd_set *write_fd_set, | ||
406 | fd_set *except_fd_set, | ||
407 | bool all) | ||
408 | { | ||
409 | (void)except_fd_set; | ||
410 | struct SPDY_Session *pos; | ||
411 | int fd; | ||
412 | int max_fd = -1; | ||
413 | |||
414 | fd = daemon->socket_fd; | ||
415 | if (-1 != fd) | ||
416 | { | ||
417 | FD_SET (fd, read_fd_set); | ||
418 | /* update max file descriptor */ | ||
419 | max_fd = fd; | ||
420 | } | ||
421 | |||
422 | for (pos = daemon->sessions_head; NULL != pos; pos = pos->next) | ||
423 | { | ||
424 | fd = pos->socket_fd; | ||
425 | FD_SET(fd, read_fd_set); | ||
426 | if(all | ||
427 | || NULL != pos->response_queue_head //frames pending | ||
428 | || NULL != pos->write_buffer //part of last frame pending | ||
429 | || SPDY_SESSION_STATUS_CLOSING == pos->status //the session is about to be closed | ||
430 | || daemon->session_timeout //timeout passed for the session | ||
431 | && (pos->last_activity + daemon->session_timeout < SPDYF_monotonic_time()) | ||
432 | || SPDY_YES == SPDYF_tls_is_pending(pos) //data in TLS' read buffer pending | ||
433 | || ((pos->read_buffer_offset - pos->read_buffer_beginning) > 0) // data in lib's read buffer pending | ||
434 | ) | ||
435 | FD_SET(fd, write_fd_set); | ||
436 | if(fd > max_fd) | ||
437 | max_fd = fd; | ||
438 | } | ||
439 | |||
440 | return max_fd; | ||
441 | } | ||
442 | |||
443 | |||
444 | void | ||
445 | SPDYF_run (struct SPDY_Daemon *daemon) | ||
446 | { | ||
447 | struct SPDY_Session *pos; | ||
448 | struct SPDY_Session *next; | ||
449 | int num_ready; | ||
450 | fd_set rs; | ||
451 | fd_set ws; | ||
452 | fd_set es; | ||
453 | int max; | ||
454 | struct timeval timeout; | ||
455 | int ds; | ||
456 | |||
457 | timeout.tv_sec = 0; | ||
458 | timeout.tv_usec = 0; | ||
459 | FD_ZERO (&rs); | ||
460 | FD_ZERO (&ws); | ||
461 | FD_ZERO (&es); | ||
462 | //here we need really all descriptors to see later which are ready | ||
463 | max = SPDYF_get_fdset(daemon,&rs,&ws,&es, true); | ||
464 | |||
465 | num_ready = select (max + 1, &rs, &ws, &es, &timeout); | ||
466 | |||
467 | if(num_ready < 1) | ||
468 | return; | ||
469 | |||
470 | if ( (-1 != (ds = daemon->socket_fd)) && | ||
471 | (FD_ISSET (ds, &rs)) ){ | ||
472 | SPDYF_session_accept(daemon); | ||
473 | } | ||
474 | |||
475 | next = daemon->sessions_head; | ||
476 | while (NULL != (pos = next)) | ||
477 | { | ||
478 | next = pos->next; | ||
479 | ds = pos->socket_fd; | ||
480 | if (ds != -1) | ||
481 | { | ||
482 | //fill the read buffer | ||
483 | if (FD_ISSET (ds, &rs) || SPDYF_tls_is_pending(pos)){ | ||
484 | SPDYF_session_read(pos); | ||
485 | } | ||
486 | |||
487 | //do something with the data in read buffer | ||
488 | if(SPDY_NO == SPDYF_session_idle(pos)) | ||
489 | { | ||
490 | //the session was closed, cannot write anymore | ||
491 | //continue; | ||
492 | } | ||
493 | |||
494 | //write whatever has been put to the response queue | ||
495 | //during read or idle operation, something might be put | ||
496 | //on the response queue, thus call write operation | ||
497 | if (FD_ISSET (ds, &ws)){ | ||
498 | if(SPDY_NO == SPDYF_session_write(pos, false)) | ||
499 | { | ||
500 | //SPDYF_session_close(pos); | ||
501 | //continue; | ||
502 | } | ||
503 | } | ||
504 | |||
505 | /* the response queue has been flushed for half closed | ||
506 | * connections, so let close them */ | ||
507 | /*if(pos->read_closed) | ||
508 | { | ||
509 | SPDYF_session_close(pos); | ||
510 | }*/ | ||
511 | } | ||
512 | } | ||
513 | |||
514 | spdyf_cleanup_sessions(daemon); | ||
515 | } | ||