/* This file is part of libmicrospdy Copyright Copyright (C) 2013 Andrey Uzunov This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see <http://www.gnu.org/licenses/>. */ /** * @file session_timeout.c * @brief tests closing sessions after set timeout. Openssl is used for * client * @author Andrey Uzunov */ #include "platform.h" #include "microspdy.h" #include "stdio.h" #include <sys/wait.h> #include <ctype.h> #include "common.h" #include <sys/time.h> #include <sys/stat.h> #include "../microspdy/internal.h" #define TIMEOUT 2 #define SELECT_MS_TIMEOUT 20 int port; pid_t parent; pid_t child; int run = 1; int chunk_size=1; int new_session; int closed_session; int do_sleep; static unsigned long long monotonic_time (void) { #ifdef HAVE_CLOCK_GETTIME #ifdef CLOCK_MONOTONIC struct timespec ts; if (0 == clock_gettime (CLOCK_MONOTONIC, &ts)) return ts.tv_sec * 1000 + ts.tv_nsec / 1000000; #endif #endif return time (NULL) * 1000; } static void killchild(char *msg) { printf("%s\n",msg); kill(child, SIGKILL); exit(1); } static void killparent(char *msg) { printf("%s\n",msg); kill(parent, SIGKILL); _exit(1); } static void new_session_cb (void *cls, struct SPDY_Session * session) { (void)cls; (void)session; if(!new_session)do_sleep = 1; new_session = 1; printf("new session\n"); } static void closed_session_cb (void *cls, struct SPDY_Session * session, int by_client) { (void)cls; (void)session; printf("closed_session_cb called\n"); if(SPDY_YES == by_client) { killchild("closed by the client"); } if(closed_session) { killchild("closed_session_cb called twice"); } closed_session = 1; } static int parentproc() { int childstatus; unsigned long long timeoutlong=0; struct timeval timeout; int ret; fd_set read_fd_set; fd_set write_fd_set; fd_set except_fd_set; int maxfd = -1; struct SPDY_Daemon *daemon; unsigned long long beginning = 0; unsigned long long now; SPDY_init(); daemon = SPDY_start_daemon(port, DATA_DIR "cert-and-key.pem", DATA_DIR "cert-and-key.pem", &new_session_cb, &closed_session_cb, NULL, NULL, NULL, SPDY_DAEMON_OPTION_SESSION_TIMEOUT, TIMEOUT, SPDY_DAEMON_OPTION_END); if(NULL==daemon){ printf("no daemon\n"); return 1; } do { do_sleep=0; FD_ZERO(&read_fd_set); FD_ZERO(&write_fd_set); FD_ZERO(&except_fd_set); ret = SPDY_get_timeout(daemon, &timeoutlong); if(new_session && !closed_session) { if(SPDY_NO == ret) { killchild("SPDY_get_timeout returned wrong SPDY_NO"); } /*if(timeoutlong) { killchild("SPDY_get_timeout returned wrong timeout"); }*/ now = monotonic_time (); if(now - beginning > TIMEOUT*1000 + SELECT_MS_TIMEOUT) { printf("Started at: %llums\n",beginning); printf("Now is: %llums\n",now); printf("Timeout is: %i\n",TIMEOUT); printf("Select Timeout is: %ims\n",SELECT_MS_TIMEOUT); printf("SPDY_get_timeout gave: %llums\n",timeoutlong); killchild("Timeout passed but session was not closed"); } if(timeoutlong > beginning + TIMEOUT *1000) { printf("Started at: %llums\n",beginning); printf("Now is: %llums\n",now); printf("Timeout is: %i\n",TIMEOUT); printf("Select Timeout is: %ims\n",SELECT_MS_TIMEOUT); printf("SPDY_get_timeout gave: %llums\n",timeoutlong); killchild("SPDY_get_timeout returned wrong timeout"); } } else { if(SPDY_YES == ret) { killchild("SPDY_get_timeout returned wrong SPDY_YES"); } } if(SPDY_NO == ret || timeoutlong >= 1000) { timeout.tv_sec = 1; timeout.tv_usec = 0; } else { timeout.tv_sec = timeoutlong / 1000; timeout.tv_usec = (timeoutlong % 1000) * 1000; } //ignore values timeout.tv_sec = 0; timeout.tv_usec = SELECT_MS_TIMEOUT * 1000; maxfd = SPDY_get_fdset (daemon, &read_fd_set, &write_fd_set, &except_fd_set); ret = select(maxfd+1, &read_fd_set, &write_fd_set, &except_fd_set, &timeout); switch(ret) { case -1: printf("select error: %i\n", errno); break; case 0: /*if(new_session) { killchild("select returned wrong number"); }*/ break; default: SPDY_run(daemon); if(0 == beginning) { beginning = monotonic_time (); } /*if(do_sleep) { sleep(TIMEOUT); do_sleep = 0; }*/ break; } } while(waitpid(child,&childstatus,WNOHANG) != child); if(!new_session || !closed_session) { killchild("child is dead, callback wasn't called"); } ret = SPDY_get_timeout(daemon, &timeoutlong); if(SPDY_YES == ret) { killchild("SPDY_get_timeout returned wrong SPDY_YES after child died"); } SPDY_stop_daemon(daemon); SPDY_deinit(); return 0; } static int childproc() { pid_t devnull; int out; out=dup(1); if (-1 == out) abort(); //close(0); close(1); close(2); /*devnull = open("/dev/null", O_RDONLY); if (0 != devnull) { dup2(devnull, 0); close(devnull); }*/ devnull = open("/dev/null", O_WRONLY); if (-1 == devnull) abort (); if (1 != devnull) { dup2(devnull, 1); close(devnull); } devnull = open("/dev/null", O_WRONLY); if (-1 == devnull) abort (); if (2 != devnull) { dup2(devnull, 2); close(devnull); } char *uri; asprintf (&uri, "127.0.0.1:%i", port); execlp ("openssl", "openssl", "s_client", "-connect", uri, NULL); close(1); dup2(out,1); close(out); killparent ("executing openssl failed"); return 1; } int main() { port = get_port(11123); parent = getpid(); child = fork(); if (-1 == child) { fprintf(stderr, "can't fork, error %d\n", errno); exit(EXIT_FAILURE); } if (child == 0) { int ret = childproc(); _exit(ret); } else { int ret = parentproc(); exit(ret); } return 1; }