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 request_response_with_callback.c
21  * @brief  tests responses with callbacks
22  * @author Andrey Uzunov
23  */
24 
25 #include "platform.h"
26 #include "microspdy.h"
27 #include "stdio.h"
28 #include <sys/wait.h>
29 #include <ctype.h>
30 #include "common.h"
31 #include <sys/time.h>
32 #include <sys/stat.h>
33 
34 int port;
35 
36 pid_t parent;
37 pid_t child;
38 
39 int run = 1;
40 int chunk_size=1;
41 
42 void
killchild()43 killchild()
44 {
45 	kill(child, SIGKILL);
46 	exit(1);
47 }
48 
49 void
killparent()50 killparent()
51 {
52 	kill(parent, SIGKILL);
53 	_exit(1);
54 }
55 
56 ssize_t
response_callback(void * cls,void * buffer,size_t max,bool * more)57 response_callback (void *cls,
58 						void *buffer,
59 						size_t max,
60 						bool *more)
61 {
62 	FILE *fd =(FILE*)cls;
63 
64 	size_t n;
65 	if(chunk_size % 2)
66 		n = chunk_size;
67 	else
68 		n = max - chunk_size;
69 
70 	if(n < 1) n = 1;
71 	else if (n > max) n=max;
72 	chunk_size++;
73 
74 	int ret = fread(buffer,1,n,fd);
75 	*more = feof(fd) == 0;
76 
77 	//printf("more is %i\n",*more);
78 
79 	if(!(*more))
80 		fclose(fd);
81 
82 	return ret;
83 }
84 
85 
86 void
response_done_callback(void * cls,struct SPDY_Response * response,struct SPDY_Request * request,enum SPDY_RESPONSE_RESULT status,bool streamopened)87 response_done_callback(void *cls,
88 								struct SPDY_Response * response,
89 								struct SPDY_Request * request,
90 								enum SPDY_RESPONSE_RESULT status,
91 						bool streamopened)
92 {
93   (void)status;
94   (void)streamopened;
95 
96 	printf("answer for %s was sent\n", (char*)cls);
97 
98 	SPDY_destroy_request(request);
99 	SPDY_destroy_response(response);
100 	free(cls);
101 
102 	run = 0;
103 }
104 
105 void
standard_request_handler(void * cls,struct SPDY_Request * request,uint8_t priority,const char * method,const char * path,const char * version,const char * host,const char * scheme,struct SPDY_NameValue * headers,bool more)106 standard_request_handler(void *cls,
107 						struct SPDY_Request * request,
108 						uint8_t priority,
109                         const char *method,
110                         const char *path,
111                         const char *version,
112                         const char *host,
113                         const char *scheme,
114 						struct SPDY_NameValue * headers,
115             bool more)
116 {
117 	(void)cls;
118 	(void)request;
119 	(void)priority;
120 	(void)host;
121 	(void)scheme;
122 	(void)headers;
123 	(void)method;
124 	(void)version;
125 	(void)more;
126 
127 	struct SPDY_Response *response=NULL;
128 	struct SPDY_NameValue *resp_headers;
129 
130 	printf("received request for '%s %s %s'\n", method, path, version);
131 
132 		FILE *fd = fopen(DATA_DIR "spdy-draft.txt","r");
133 
134 		if(NULL == (resp_headers = SPDY_name_value_create()))
135 		{
136 			fprintf(stdout,"SPDY_name_value_create failed\n");
137 			killchild();
138 		}
139 		if(SPDY_YES != SPDY_name_value_add(resp_headers,SPDY_HTTP_HEADER_CONTENT_TYPE,"text/plain"))
140 		{
141 			fprintf(stdout,"SPDY_name_value_add failed\n");
142 			killchild();
143 		}
144 
145 		response = SPDY_build_response_with_callback(200,NULL,
146 			SPDY_HTTP_VERSION_1_1,resp_headers,&response_callback,fd,SPDY_MAX_SUPPORTED_FRAME_SIZE);
147 		SPDY_name_value_destroy(resp_headers);
148 
149 	if(NULL==response){
150 		fprintf(stdout,"no response obj\n");
151 			killchild();
152 	}
153 
154 	void *clspath = strdup(path);
155 
156 	if(SPDY_queue_response(request,response,true,false,&response_done_callback,clspath)!=SPDY_YES)
157 	{
158 		fprintf(stdout,"queue\n");
159 			killchild();
160 	}
161 }
162 
163 int
parentproc()164 parentproc()
165 {
166 	int childstatus;
167 	unsigned long long timeoutlong=0;
168 	struct timeval timeout;
169 	int ret;
170 	fd_set read_fd_set;
171 	fd_set write_fd_set;
172 	fd_set except_fd_set;
173 	int maxfd = -1;
174 	struct SPDY_Daemon *daemon;
175 
176 	SPDY_init();
177 
178 	daemon = SPDY_start_daemon(port,
179 								DATA_DIR "cert-and-key.pem",
180 								DATA_DIR "cert-and-key.pem",
181 								NULL,
182 								NULL,
183 								&standard_request_handler,
184 								NULL,
185 								NULL,
186 								SPDY_DAEMON_OPTION_SESSION_TIMEOUT,
187 								1800,
188 								SPDY_DAEMON_OPTION_END);
189 
190 	if(NULL==daemon){
191 		printf("no daemon\n");
192 		return 1;
193 	}
194 
195 	do
196 	{
197 		FD_ZERO(&read_fd_set);
198 		FD_ZERO(&write_fd_set);
199 		FD_ZERO(&except_fd_set);
200 
201 		ret = SPDY_get_timeout(daemon, &timeoutlong);
202 		if(SPDY_NO == ret || timeoutlong > 1000)
203 		{
204 			timeout.tv_sec = 1;
205       timeout.tv_usec = 0;
206 		}
207 		else
208 		{
209 			timeout.tv_sec = timeoutlong / 1000;
210 			timeout.tv_usec = (timeoutlong % 1000) * 1000;
211 		}
212 
213 		maxfd = SPDY_get_fdset (daemon,
214 								&read_fd_set,
215 								&write_fd_set,
216 								&except_fd_set);
217 
218 		ret = select(maxfd+1, &read_fd_set, &write_fd_set, &except_fd_set, &timeout);
219 
220 		switch(ret) {
221 			case -1:
222 				printf("select error: %i\n", errno);
223 				break;
224 			case 0:
225 
226 				break;
227 			default:
228 				SPDY_run(daemon);
229 
230 			break;
231 		}
232 	}
233 	while(waitpid(child,&childstatus,WNOHANG) != child);
234 
235 	SPDY_stop_daemon(daemon);
236 
237 	SPDY_deinit();
238 
239 	return WEXITSTATUS(childstatus);
240 }
241 
242 #define MD5_LEN 32
243 
244 int
md5(char * cmd,char * md5_sum)245 md5(char *cmd, char *md5_sum)
246 {
247     FILE *p = popen(cmd, "r");
248     if (p == NULL) return 0;
249 
250     int i, ch;
251     for (i = 0; i < MD5_LEN && isxdigit(ch = fgetc(p)); i++) {
252         *md5_sum++ = ch;
253     }
254 
255     *md5_sum = '\0';
256     pclose(p);
257     return i == MD5_LEN;
258 }
259 
260 int
childproc()261 childproc()
262 {
263 	char *cmd1;
264 	char *cmd2;
265 	char md5_sum1[33];
266 	char md5_sum2[33];
267 	int ret;
268 	struct timeval tv1;
269 	struct timeval tv2;
270 	struct stat st;
271 	//int secs;
272 	uint64_t usecs;
273 
274 	asprintf(&cmd1, "spdycat https://127.0.0.1:%i/ | md5sum",port);
275 	asprintf(&cmd2, "md5sum " DATA_DIR "spdy-draft.txt");
276 
277 	gettimeofday(&tv1, NULL);
278 	md5(cmd1,md5_sum1);
279 	gettimeofday(&tv2, NULL);
280 	md5(cmd2,md5_sum2);
281 
282 	printf("downloaded file md5: %s\n", md5_sum1);
283 	printf("original   file md5: %s\n", md5_sum2);
284 	ret = strcmp(md5_sum1, md5_sum2);
285 
286 	if(0 == ret && 0 == stat(DATA_DIR "spdy-draft.txt", &st))
287 	{
288 		usecs = (uint64_t)1000000 * (uint64_t)(tv2.tv_sec - tv1.tv_sec) + tv2.tv_usec - tv1.tv_usec;
289 		printf("%lld bytes read in %llu usecs\n", (long long)st.st_size, (long long unsigned )usecs);
290 	}
291 
292 	return ret;
293 }
294 
295 
296 int
main()297 main()
298 {
299 	port = get_port(11123);
300 	parent = getpid();
301 
302 	child = fork();
303 	if (-1 == child)
304 	{
305 		fprintf(stderr, "can't fork, error %d\n", errno);
306 		exit(EXIT_FAILURE);
307 	}
308 
309 	if (child == 0)
310 	{
311 		int ret = childproc();
312 		_exit(ret);
313 	}
314 	else
315 	{
316 		int ret = parentproc();
317 		exit(ret);
318 	}
319 	return 1;
320 }
321