aboutsummaryrefslogtreecommitdiff
path: root/src/microspdy/daemon.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/microspdy/daemon.c')
-rw-r--r--src/microspdy/daemon.c515
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 */
41static void
42spdyf_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 */
58SPDY_PanicCallback spdyf_panic = &spdyf_panic_std;
59
60
61/**
62 * Global closure argument for "spdyf_panic".
63 */
64void *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 */
73static void
74spdyf_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 */
94static void
95spdyf_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 */
119static int
120spdyf_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
154void
155SPDY_set_panic_func (SPDY_PanicCallback cb,
156 void *cls)
157{
158 spdyf_panic = cb;
159 spdyf_panic_cls = cls;
160}
161
162
163struct SPDY_Daemon *
164SPDYF_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
342void
343SPDYF_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
361int
362SPDYF_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
402int
403SPDYF_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
444void
445SPDYF_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}