Home | History | Annotate | Download | only in microspdy
      1 /*
      2     This file is part of libmicrospdy
      3     Copyright 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 microspdy/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 "io.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) * 1000;
    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 			case SPDY_DAEMON_OPTION_IO_SUBSYSTEM:
    146 				daemon->io_subsystem = va_arg (valist, enum SPDY_IO_SUBSYSTEM);
    147 				break;
    148 			case SPDY_DAEMON_OPTION_MAX_NUM_FRAMES:
    149 				daemon->max_num_frames = va_arg (valist, uint32_t);
    150 				break;
    151 			default:
    152 				SPDYF_DEBUG("Wrong option for the daemon %i",opt);
    153 				return SPDY_NO;
    154 		}
    155 	}
    156 	return SPDY_YES;
    157 }
    158 
    159 
    160 void
    161 SPDY_set_panic_func (SPDY_PanicCallback cb,
    162 					void *cls)
    163 {
    164 	spdyf_panic = cb;
    165 	spdyf_panic_cls = cls;
    166 }
    167 
    168 
    169 struct SPDY_Daemon *
    170 SPDYF_start_daemon_va (uint16_t port,
    171 					const char *certfile,
    172 					const char *keyfile,
    173 					SPDY_NewSessionCallback nscb,
    174 					SPDY_SessionClosedCallback sccb,
    175 					SPDY_NewRequestCallback nrcb,
    176 					SPDY_NewDataCallback npdcb,
    177 					SPDYF_NewStreamCallback fnscb,
    178 					SPDYF_NewDataCallback fndcb,
    179 					void * cls,
    180 					void * fcls,
    181 					va_list valist)
    182 {
    183 	struct SPDY_Daemon *daemon = NULL;
    184 	int afamily;
    185 	int option_on = 1;
    186 	int ret;
    187 	struct sockaddr_in* servaddr4 = NULL;
    188 #if HAVE_INET6
    189 	struct sockaddr_in6* servaddr6 = NULL;
    190 #endif
    191 	socklen_t addrlen;
    192 
    193 	if (NULL == (daemon = malloc (sizeof (struct SPDY_Daemon))))
    194 	{
    195 		SPDYF_DEBUG("malloc");
    196 		return NULL;
    197 	}
    198 	memset (daemon, 0, sizeof (struct SPDY_Daemon));
    199 	daemon->socket_fd = -1;
    200 	daemon->port = port;
    201 
    202 	if(SPDY_YES != spdyf_parse_options_va (daemon, valist))
    203 	{
    204 		SPDYF_DEBUG("parse");
    205 		goto free_and_fail;
    206 	}
    207 
    208   if(0 == daemon->max_num_frames)
    209     daemon->max_num_frames = SPDYF_NUM_SENT_FRAMES_AT_ONCE;
    210 
    211 	if(!port && NULL == daemon->address)
    212 	{
    213 		SPDYF_DEBUG("Port is 0");
    214 		goto free_and_fail;
    215 	}
    216   if(0 == daemon->io_subsystem)
    217     daemon->io_subsystem = SPDY_IO_SUBSYSTEM_OPENSSL;
    218 
    219   if(SPDY_YES != SPDYF_io_set_daemon(daemon, daemon->io_subsystem))
    220 		goto free_and_fail;
    221 
    222   if(SPDY_IO_SUBSYSTEM_RAW != daemon->io_subsystem)
    223   {
    224     if (NULL == certfile
    225       || NULL == (daemon->certfile = strdup (certfile)))
    226     {
    227       SPDYF_DEBUG("strdup (certfile)");
    228       goto free_and_fail;
    229     }
    230     if (NULL == keyfile
    231       || NULL == (daemon->keyfile = strdup (keyfile)))
    232     {
    233       SPDYF_DEBUG("strdup (keyfile)");
    234       goto free_and_fail;
    235     }
    236   }
    237 
    238 	daemon->new_session_cb = nscb;
    239 	daemon->session_closed_cb = sccb;
    240 	daemon->new_request_cb = nrcb;
    241 	daemon->received_data_cb = npdcb;
    242 	daemon->cls = cls;
    243 	daemon->fcls = fcls;
    244 	daemon->fnew_stream_cb = fnscb;
    245 	daemon->freceived_data_cb = fndcb;
    246 
    247 #if HAVE_INET6
    248 	//handling IPv6
    249 	if((daemon->flags & SPDY_DAEMON_FLAG_ONLY_IPV6)
    250 		&& NULL != daemon->address && AF_INET6 != daemon->address->sa_family)
    251 	{
    252 		SPDYF_DEBUG("SPDY_DAEMON_FLAG_ONLY_IPV6 set but IPv4 address provided");
    253 		goto free_and_fail;
    254 	}
    255 
    256   addrlen = sizeof (struct sockaddr_in6);
    257 
    258 	if(NULL == daemon->address)
    259 	{
    260 		if (NULL == (servaddr6 = malloc (addrlen)))
    261 		{
    262 			SPDYF_DEBUG("malloc");
    263 			goto free_and_fail;
    264 		}
    265 		memset (servaddr6, 0, addrlen);
    266 		servaddr6->sin6_family = AF_INET6;
    267 		servaddr6->sin6_addr = in6addr_any;
    268 		servaddr6->sin6_port = htons (port);
    269 		daemon->address = (struct sockaddr *) servaddr6;
    270 	}
    271 
    272   if(AF_INET6 == daemon->address->sa_family)
    273   {
    274     afamily = PF_INET6;
    275   }
    276   else
    277   {
    278     afamily = PF_INET;
    279   }
    280 #else
    281 	//handling IPv4
    282 	if(daemon->flags & SPDY_DAEMON_FLAG_ONLY_IPV6)
    283 	{
    284 		SPDYF_DEBUG("SPDY_DAEMON_FLAG_ONLY_IPV6 set but no support");
    285 		goto free_and_fail;
    286 	}
    287 
    288   addrlen = sizeof (struct sockaddr_in);
    289 
    290 	if(NULL == daemon->address)
    291 	{
    292 		if (NULL == (servaddr4 = malloc (addrlen)))
    293 		{
    294 			SPDYF_DEBUG("malloc");
    295 			goto free_and_fail;
    296 		}
    297 		memset (servaddr4, 0, addrlen);
    298 		servaddr4->sin_family = AF_INET;
    299 		servaddr4->sin_addr = INADDR_ANY;
    300 		servaddr4->sin_port = htons (port);
    301 		daemon->address = (struct sockaddr *) servaddr4;
    302 	}
    303 
    304 	afamily = PF_INET;
    305 #endif
    306 
    307 	daemon->socket_fd = socket (afamily, SOCK_STREAM, 0);
    308 	if (-1 == daemon->socket_fd)
    309 	{
    310 		SPDYF_DEBUG("sock");
    311 		goto free_and_fail;
    312 	}
    313 
    314 	//setting option for the socket to reuse address
    315 	ret = setsockopt(daemon->socket_fd, SOL_SOCKET, SO_REUSEADDR, &option_on, sizeof(option_on));
    316 	if(ret)
    317 	{
    318 		SPDYF_DEBUG("WARNING: SO_REUSEADDR was not set for the server");
    319 	}
    320 
    321 #if HAVE_INET6
    322 	if(daemon->flags & SPDY_DAEMON_FLAG_ONLY_IPV6)
    323 	{
    324 		ret = setsockopt(daemon->socket_fd, IPPROTO_IPV6, IPV6_V6ONLY, &option_on, sizeof(option_on));
    325 		if(ret)
    326 		{
    327 			SPDYF_DEBUG("setsockopt with IPPROTO_IPV6 failed");
    328 			goto free_and_fail;
    329 		}
    330 	}
    331 #endif
    332 
    333 	if (-1 == bind (daemon->socket_fd, daemon->address, addrlen))
    334 	{
    335 		SPDYF_DEBUG("bind %i",errno);
    336 		goto free_and_fail;
    337 	}
    338 
    339 	if (listen (daemon->socket_fd, 20) < 0)
    340 	{
    341 		SPDYF_DEBUG("listen %i",errno);
    342 		goto free_and_fail;
    343 	}
    344 
    345 	if(SPDY_YES != daemon->fio_init(daemon))
    346 	{
    347 		SPDYF_DEBUG("tls");
    348 		goto free_and_fail;
    349 	}
    350 
    351 	return daemon;
    352 
    353 	//for GOTO
    354 	free_and_fail:
    355 	if(daemon->socket_fd > 0)
    356 		(void)close (daemon->socket_fd);
    357 
    358 	free(servaddr4);
    359 #if HAVE_INET6
    360 	free(servaddr6);
    361 #endif
    362 	if(NULL != daemon->certfile)
    363 		free(daemon->certfile);
    364 	if(NULL != daemon->keyfile)
    365 		free(daemon->keyfile);
    366 	free (daemon);
    367 
    368 	return NULL;
    369 }
    370 
    371 
    372 void
    373 SPDYF_stop_daemon (struct SPDY_Daemon *daemon)
    374 {
    375 	daemon->fio_deinit(daemon);
    376 
    377 	shutdown (daemon->socket_fd, SHUT_RDWR);
    378 	spdyf_close_all_sessions (daemon);
    379 	(void)close (daemon->socket_fd);
    380 
    381 	if(!(SPDY_DAEMON_OPTION_SOCK_ADDR & daemon->options))
    382 		free(daemon->address);
    383 
    384 	free(daemon->certfile);
    385 	free(daemon->keyfile);
    386 
    387 	free(daemon);
    388 }
    389 
    390 
    391 int
    392 SPDYF_get_timeout (struct SPDY_Daemon *daemon,
    393 		     unsigned long long *timeout)
    394 {
    395 	unsigned long long earliest_deadline = 0;
    396 	unsigned long long now;
    397 	struct SPDY_Session *pos;
    398 	bool have_timeout;
    399 
    400 	if(0 == daemon->session_timeout)
    401 		return SPDY_NO;
    402 
    403 	now = SPDYF_monotonic_time();
    404 	have_timeout = false;
    405 	for (pos = daemon->sessions_head; NULL != pos; pos = pos->next)
    406 	{
    407 		if ( (! have_timeout) ||
    408 			(earliest_deadline > pos->last_activity + daemon->session_timeout) )
    409 			earliest_deadline = pos->last_activity + daemon->session_timeout;
    410 
    411 		have_timeout = true;
    412 
    413 		if (SPDY_YES == pos->fio_is_pending(pos))
    414 		{
    415 			earliest_deadline = 0;
    416 			break;
    417 		}
    418 	}
    419 
    420 	if (!have_timeout)
    421 		return SPDY_NO;
    422 	if (earliest_deadline <= now)
    423 		*timeout = 0;
    424 	else
    425 		*timeout = earliest_deadline - now;
    426 
    427 	return SPDY_YES;
    428 }
    429 
    430 
    431 int
    432 SPDYF_get_fdset (struct SPDY_Daemon *daemon,
    433 				fd_set *read_fd_set,
    434 				fd_set *write_fd_set,
    435 				fd_set *except_fd_set,
    436 				bool all)
    437 {
    438 	(void)except_fd_set;
    439 	struct SPDY_Session *pos;
    440 	int fd;
    441 	int max_fd = -1;
    442 
    443 	fd = daemon->socket_fd;
    444 	if (-1 != fd)
    445 	{
    446 		FD_SET (fd, read_fd_set);
    447 		/* update max file descriptor */
    448 		max_fd = fd;
    449 	}
    450 
    451 	for (pos = daemon->sessions_head; NULL != pos; pos = pos->next)
    452 	{
    453 		fd = pos->socket_fd;
    454 		FD_SET(fd, read_fd_set);
    455 		if (all
    456 		    || (NULL != pos->response_queue_head) //frames pending
    457 		    || (NULL != pos->write_buffer) //part of last frame pending
    458 		    || (SPDY_SESSION_STATUS_CLOSING == pos->status) //the session is about to be closed
    459 		    || (daemon->session_timeout //timeout passed for the session
    460 			&& (pos->last_activity + daemon->session_timeout < SPDYF_monotonic_time()))
    461 		    || (SPDY_YES == pos->fio_is_pending(pos)) //data in TLS' read buffer pending
    462 		    || ((pos->read_buffer_offset - pos->read_buffer_beginning) > 0) // data in lib's read buffer pending
    463 		    )
    464 			FD_SET(fd, write_fd_set);
    465 		if(fd > max_fd)
    466 			max_fd = fd;
    467 	}
    468 
    469 	return max_fd;
    470 }
    471 
    472 
    473 void
    474 SPDYF_run (struct SPDY_Daemon *daemon)
    475 {
    476 	struct SPDY_Session *pos;
    477 	struct SPDY_Session *next;
    478 	int num_ready;
    479 	fd_set rs;
    480 	fd_set ws;
    481 	fd_set es;
    482 	int max;
    483 	struct timeval timeout;
    484 	int ds;
    485 
    486 	timeout.tv_sec = 0;
    487 	timeout.tv_usec = 0;
    488 	FD_ZERO (&rs);
    489 	FD_ZERO (&ws);
    490 	FD_ZERO (&es);
    491 	//here we need really all descriptors to see later which are ready
    492 	max = SPDYF_get_fdset(daemon,&rs,&ws,&es, true);
    493 
    494 	num_ready = select (max + 1, &rs, &ws, &es, &timeout);
    495 
    496 	if(num_ready < 1)
    497 		return;
    498 
    499 	if ( (-1 != (ds = daemon->socket_fd)) &&
    500 		(FD_ISSET (ds, &rs)) ){
    501 		SPDYF_session_accept(daemon);
    502 	}
    503 
    504 	next = daemon->sessions_head;
    505 	while (NULL != (pos = next))
    506 	{
    507 		next = pos->next;
    508 		ds = pos->socket_fd;
    509 		if (ds != -1)
    510 		{
    511 			//fill the read buffer
    512 			if (FD_ISSET (ds, &rs) || pos->fio_is_pending(pos)){
    513 				SPDYF_session_read(pos);
    514 			}
    515 
    516 			//do something with the data in read buffer
    517 			if(SPDY_NO == SPDYF_session_idle(pos))
    518 			{
    519 				//the session was closed, cannot write anymore
    520 				//continue;
    521 			}
    522 
    523 			//write whatever has been put to the response queue
    524 			//during read or idle operation, something might be put
    525 			//on the response queue, thus call write operation
    526 			if (FD_ISSET (ds, &ws)){
    527 				if(SPDY_NO == SPDYF_session_write(pos, false))
    528 				{
    529 					//SPDYF_session_close(pos);
    530 					//continue;
    531 				}
    532 			}
    533 
    534 			/* the response queue has been flushed for half closed
    535 			 * connections, so let close them */
    536 			/*if(pos->read_closed)
    537 			{
    538 				SPDYF_session_close(pos);
    539 			}*/
    540 		}
    541 	}
    542 
    543 	spdyf_cleanup_sessions(daemon);
    544 }
    545