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 session_timeout.c
21 * @brief  tests closing sessions after set timeout. Openssl is used for
22 * 			client
23 * @author Andrey Uzunov
24 */
25
26#include "platform.h"
27#include "microspdy.h"
28#include "stdio.h"
29#include <sys/wait.h>
30#include <ctype.h>
31#include "common.h"
32#include <sys/time.h>
33#include <sys/stat.h>
34#include "../microspdy/internal.h"
35
36#define TIMEOUT 2
37#define SELECT_MS_TIMEOUT 20
38
39int port;
40
41pid_t parent;
42pid_t child;
43
44int run = 1;
45int chunk_size=1;
46int new_session;
47int closed_session;
48int do_sleep;
49
50
51
52static unsigned long long
53monotonic_time (void)
54{
55#ifdef HAVE_CLOCK_GETTIME
56#ifdef CLOCK_MONOTONIC
57  struct timespec ts;
58  if (0 == clock_gettime (CLOCK_MONOTONIC, &ts))
59    return ts.tv_sec * 1000 + ts.tv_nsec / 1000000;
60#endif
61#endif
62  return time (NULL) * 1000;
63}
64
65
66static void
67killchild(char *msg)
68{
69	printf("%s\n",msg);
70	kill(child, SIGKILL);
71	exit(1);
72}
73
74
75static void
76killparent(char *msg)
77{
78	printf("%s\n",msg);
79	kill(parent, SIGKILL);
80	_exit(1);
81}
82
83
84static void
85new_session_cb (void *cls,
86                struct SPDY_Session * session)
87{
88  (void)cls;
89  (void)session;
90
91	if(!new_session)do_sleep = 1;
92	new_session = 1;
93	printf("new session\n");
94}
95
96
97static void
98closed_session_cb (void *cls,
99                   struct SPDY_Session * session,
100                   int by_client)
101{
102  (void)cls;
103  (void)session;
104
105	printf("closed_session_cb called\n");
106
107	if(SPDY_YES == by_client)
108	{
109		killchild("closed by the client");
110	}
111	if(closed_session)
112	{
113		killchild("closed_session_cb called twice");
114	}
115
116	closed_session = 1;
117}
118
119
120static int
121parentproc()
122{
123	int childstatus;
124	unsigned long long timeoutlong=0;
125	struct timeval timeout;
126	int ret;
127	fd_set read_fd_set;
128	fd_set write_fd_set;
129	fd_set except_fd_set;
130	int maxfd = -1;
131	struct SPDY_Daemon *daemon;
132  unsigned long long  beginning = 0;
133	unsigned long long now;
134
135	SPDY_init();
136
137	daemon = SPDY_start_daemon(port,
138								DATA_DIR "cert-and-key.pem",
139								DATA_DIR "cert-and-key.pem",
140								&new_session_cb,
141								&closed_session_cb,
142								NULL,
143								NULL,
144								NULL,
145								SPDY_DAEMON_OPTION_SESSION_TIMEOUT,
146								TIMEOUT,
147								SPDY_DAEMON_OPTION_END);
148
149	if(NULL==daemon){
150		printf("no daemon\n");
151		return 1;
152	}
153
154	do
155	{
156		do_sleep=0;
157		FD_ZERO(&read_fd_set);
158		FD_ZERO(&write_fd_set);
159		FD_ZERO(&except_fd_set);
160
161		ret = SPDY_get_timeout(daemon, &timeoutlong);
162
163		if(new_session && !closed_session)
164		{
165			if(SPDY_NO == ret)
166			{
167				killchild("SPDY_get_timeout returned wrong SPDY_NO");
168			}
169			/*if(timeoutlong)
170			{
171				killchild("SPDY_get_timeout returned wrong timeout");
172			}*/
173			now = monotonic_time ();
174      if(now - beginning > TIMEOUT*1000 + SELECT_MS_TIMEOUT)
175      {
176        printf("Started at: %llums\n",beginning);
177        printf("Now is: %llums\n",now);
178        printf("Timeout is: %i\n",TIMEOUT);
179        printf("Select Timeout is: %ims\n",SELECT_MS_TIMEOUT);
180        printf("SPDY_get_timeout gave: %llums\n",timeoutlong);
181				killchild("Timeout passed but session was not closed");
182      }
183      if(timeoutlong > beginning + TIMEOUT *1000)
184      {
185        printf("Started at: %llums\n",beginning);
186        printf("Now is: %llums\n",now);
187        printf("Timeout is: %i\n",TIMEOUT);
188        printf("Select Timeout is: %ims\n",SELECT_MS_TIMEOUT);
189        printf("SPDY_get_timeout gave: %llums\n",timeoutlong);
190				killchild("SPDY_get_timeout returned wrong timeout");
191      }
192		}
193		else
194		{
195			if(SPDY_YES == ret)
196			{
197				killchild("SPDY_get_timeout returned wrong SPDY_YES");
198			}
199		}
200
201		if(SPDY_NO == ret || timeoutlong >= 1000)
202		{
203			timeout.tv_sec = 1;
204      timeout.tv_usec = 0;
205		}
206		else
207		{
208			timeout.tv_sec = timeoutlong / 1000;
209      timeout.tv_usec = (timeoutlong % 1000) * 1000;
210		}
211
212    //ignore values
213    timeout.tv_sec = 0;
214    timeout.tv_usec = SELECT_MS_TIMEOUT * 1000;
215
216		maxfd = SPDY_get_fdset (daemon,
217								&read_fd_set,
218								&write_fd_set,
219								&except_fd_set);
220
221		ret = select(maxfd+1, &read_fd_set, &write_fd_set, &except_fd_set, &timeout);
222
223		switch(ret) {
224			case -1:
225				printf("select error: %i\n", errno);
226				break;
227			case 0:
228				/*if(new_session)
229				{
230					killchild("select returned wrong number");
231				}*/
232				break;
233			default:
234				SPDY_run(daemon);
235        if(0 == beginning)
236        {
237	  beginning = monotonic_time ();
238        }
239				/*if(do_sleep)
240				{
241					sleep(TIMEOUT);
242					do_sleep = 0;
243				}*/
244			break;
245		}
246	}
247	while(waitpid(child,&childstatus,WNOHANG) != child);
248
249	if(!new_session || !closed_session)
250	{
251		killchild("child is dead, callback wasn't called");
252	}
253
254	ret = SPDY_get_timeout(daemon, &timeoutlong);
255
256	if(SPDY_YES == ret)
257	{
258		killchild("SPDY_get_timeout returned wrong SPDY_YES after child died");
259	}
260
261	SPDY_stop_daemon(daemon);
262
263	SPDY_deinit();
264
265	return 0;
266}
267
268
269static int
270childproc()
271{
272	pid_t devnull;
273	int out;
274
275	out=dup(1);
276        if (-1 == out)
277          abort();
278	//close(0);
279	close(1);
280	close(2);
281	/*devnull = open("/dev/null", O_RDONLY);
282	if (0 != devnull)
283	{
284		dup2(devnull, 0);
285		close(devnull);
286	}*/
287	devnull = open("/dev/null", O_WRONLY);
288        if (-1 == devnull)
289          abort ();
290	if (1 != devnull)
291	{
292		dup2(devnull, 1);
293		close(devnull);
294	}
295	devnull = open("/dev/null", O_WRONLY);
296        if (-1 == devnull)
297          abort ();
298	if (2 != devnull)
299	{
300		dup2(devnull, 2);
301		close(devnull);
302	}
303	char *uri;
304	asprintf (&uri, "127.0.0.1:%i", port);
305	execlp ("openssl", "openssl", "s_client", "-connect", uri, NULL);
306	close(1);
307	dup2(out,1);
308	close(out);
309	killparent ("executing openssl failed");
310	return 1;
311}
312
313
314int
315main()
316{
317	port = get_port(11123);
318	parent = getpid();
319
320	child = fork();
321	if (-1 == child)
322	{
323		fprintf(stderr, "can't fork, error %d\n", errno);
324		exit(EXIT_FAILURE);
325	}
326
327	if (child == 0)
328	{
329		int ret = childproc();
330		_exit(ret);
331	}
332	else
333	{
334		int ret = parentproc();
335		exit(ret);
336	}
337	return 1;
338}
339