1 /* Copyright (c) 2015-2018 the Civetweb developers
2  *
3  * Permission is hereby granted, free of charge, to any person obtaining a copy
4  * of this software and associated documentation files (the "Software"), to deal
5  * in the Software without restriction, including without limitation the rights
6  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7  * copies of the Software, and to permit persons to whom the Software is
8  * furnished to do so, subject to the following conditions:
9  *
10  * The above copyright notice and this permission notice shall be included in
11  * all copies or substantial portions of the Software.
12  *
13  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19  * THE SOFTWARE.
20  */
21 
22 #ifdef _MSC_VER
23 #ifndef _CRT_SECURE_NO_WARNINGS
24 #define _CRT_SECURE_NO_WARNINGS
25 #endif
26 #endif
27 
28 #include <stdarg.h>
29 #include <stdint.h>
30 #include <stdio.h>
31 #include <stdlib.h>
32 #include <string.h>
33 #include <sys/stat.h>
34 #include <sys/types.h>
35 #include <time.h>
36 
37 #include "public_server.h"
38 #include <civetweb.h>
39 
40 #if defined(_WIN32)
41 #include <windows.h>
42 #define test_sleep(x) (Sleep((x)*1000))
43 #else
44 #include <unistd.h>
45 #define test_sleep(x) (sleep(x))
46 #endif
47 
48 #define SLEEP_BEFORE_MG_START (1)
49 #define SLEEP_AFTER_MG_START (3)
50 #define SLEEP_BEFORE_MG_STOP (1)
51 #define SLEEP_AFTER_MG_STOP (5)
52 
53 /* This unit test file uses the excellent Check unit testing library.
54  * The API documentation is available here:
55  * http://check.sourceforge.net/doc/check_html/index.html
56  */
57 
58 #if defined(__MINGW32__) || defined(__GNUC__)
59 /* Return codes of the tested functions are evaluated. Checking all other
60  * functions, used only to prepare the test environment seems redundant.
61  * If they fail, the test fails anyway. */
62 #pragma GCC diagnostic ignored "-Wunused-result"
63 #endif
64 
65 static const char *
locate_path(const char * a_path)66 locate_path(const char *a_path)
67 {
68 	static char r_path[256];
69 
70 #ifdef _WIN32
71 #ifdef LOCAL_TEST
72 	sprintf(r_path, "%s\\", a_path);
73 #else
74 	/* Appveyor */
75 	sprintf(r_path, "..\\..\\..\\%s\\", a_path);
76 /* TODO: the different paths
77  * used in the different test
78  * system is an unsolved
79  * problem. */
80 #endif
81 #else
82 #ifdef LOCAL_TEST
83 	sprintf(r_path, "%s/", a_path);
84 #else
85 	/* Travis */
86 	sprintf(r_path,
87 	        "../../%s/",
88 	        a_path); // TODO: fix path in CI test environment
89 #endif
90 #endif
91 
92 	return r_path;
93 }
94 
95 
96 #define locate_resources() locate_path("resources")
97 #define locate_test_exes() locate_path("output")
98 
99 
100 static const char *
locate_ssl_cert(void)101 locate_ssl_cert(void)
102 {
103 	static char cert_path[256];
104 	const char *res = locate_resources();
105 	size_t l;
106 
107 	ck_assert(res != NULL);
108 	l = strlen(res);
109 	ck_assert_uint_gt(l, 0);
110 	ck_assert_uint_lt(l, 100); /* assume there is enough space left in our
111 	                              typical 255 character string buffers */
112 
113 	strcpy(cert_path, res);
114 	strcat(cert_path, "ssl_cert.pem");
115 	return cert_path;
116 }
117 
118 
119 static int
wait_not_null(void * volatile * data)120 wait_not_null(void *volatile *data)
121 {
122 	int i;
123 	for (i = 0; i < 100; i++) {
124 		mark_point();
125 		test_sleep(1);
126 
127 		if (*data != NULL) {
128 			mark_point();
129 			return 1;
130 		}
131 	}
132 
133 #if defined(__MINGW32__) || defined(__GNUC__)
134 #pragma GCC diagnostic push
135 #pragma GCC diagnostic ignored "-Wunreachable-code"
136 #pragma GCC diagnostic ignored "-Wunreachable-code-return"
137 #endif
138 
139 	ck_abort_msg("wait_not_null failed (%i sec)", i);
140 
141 	return 0;
142 
143 #if defined(__MINGW32__) || defined(__GNUC__)
144 #pragma GCC diagnostic pop
145 #endif
146 }
147 
148 
START_TEST(test_the_test_environment)149 START_TEST(test_the_test_environment)
150 {
151 	char wd[300];
152 	char buf[500];
153 	FILE *f;
154 	struct stat st;
155 	int ret;
156 	const char *ssl_cert = locate_ssl_cert();
157 
158 	memset(wd, 0, sizeof(wd));
159 	memset(buf, 0, sizeof(buf));
160 
161 /* Get the current working directory */
162 #ifdef _WIN32
163 	(void)GetCurrentDirectoryA(sizeof(wd), wd);
164 	wd[sizeof(wd) - 1] = 0;
165 #else
166 	(void)getcwd(wd, sizeof(wd));
167 	wd[sizeof(wd) - 1] = 0;
168 #endif
169 
170 /* Check the pem file */
171 #ifdef _WIN32
172 	strcpy(buf, wd);
173 	strcat(buf, "\\");
174 	strcat(buf, ssl_cert);
175 	f = fopen(buf, "rb");
176 #else
177 	strcpy(buf, wd);
178 	strcat(buf, "/");
179 	strcat(buf, ssl_cert);
180 	f = fopen(buf, "r");
181 #endif
182 
183 	if (f) {
184 		fclose(f);
185 	} else {
186 		fprintf(stderr, "Certificate %s not found\n", buf);
187 		exit(1); /* some path is not correct --> test will not work */
188 	}
189 
190 
191 #ifdef _WIN32
192 /* Try to copy the files required for AppVeyor */
193 #if defined(_WIN64) || defined(__MINGW64__)
194 	(void)system("cmd /c copy C:\\OpenSSL-Win64\\libeay32.dll libeay32.dll");
195 	(void)system("cmd /c copy C:\\OpenSSL-Win64\\libssl32.dll libssl32.dll");
196 	(void)system("cmd /c copy C:\\OpenSSL-Win64\\ssleay32.dll ssleay32.dll");
197 	(void)system("cmd /c copy C:\\OpenSSL-Win64\\libeay32.dll libeay64.dll");
198 	(void)system("cmd /c copy C:\\OpenSSL-Win64\\libssl32.dll libssl64.dll");
199 	(void)system("cmd /c copy C:\\OpenSSL-Win64\\ssleay32.dll ssleay64.dll");
200 #else
201 	(void)system("cmd /c copy C:\\OpenSSL-Win32\\libeay32.dll libeay32.dll");
202 	(void)system("cmd /c copy C:\\OpenSSL-Win32\\libssl32.dll libssl32.dll");
203 	(void)system("cmd /c copy C:\\OpenSSL-Win32\\ssleay32.dll ssleay32.dll");
204 #endif
205 #endif
206 }
207 END_TEST
208 
209 
210 static void *threading_data = 0;
211 
212 static void *
test_thread_func_t(void * param)213 test_thread_func_t(void *param)
214 {
215 	ck_assert_ptr_eq(param, &threading_data);
216 	ck_assert_ptr_eq(threading_data, NULL);
217 	threading_data = &threading_data;
218 	return NULL;
219 }
220 
221 
START_TEST(test_threading)222 START_TEST(test_threading)
223 {
224 	int ok;
225 
226 	threading_data = NULL;
227 	mark_point();
228 
229 	ok = mg_start_thread(test_thread_func_t, &threading_data);
230 	ck_assert_int_eq(ok, 0);
231 
232 	wait_not_null(&threading_data);
233 	ck_assert_ptr_eq(threading_data, &threading_data);
234 }
235 END_TEST
236 
237 
238 static int
log_msg_func(const struct mg_connection * conn,const char * message)239 log_msg_func(const struct mg_connection *conn, const char *message)
240 {
241 	struct mg_context *ctx;
242 	char *ud;
243 
244 	ck_assert(conn != NULL);
245 	ctx = mg_get_context(conn);
246 	ck_assert(ctx != NULL);
247 	ud = (char *)mg_get_user_data(ctx);
248 
249 	strncpy(ud, message, 255);
250 	ud[255] = 0;
251 	mark_point();
252 
253 	printf("LOG_MSG_FUNC: %s\n", message);
254 	mark_point();
255 
256 	return 1; /* Return 1 means "already handled" */
257 }
258 
259 
260 static int
test_log_message(const struct mg_connection * conn,const char * message)261 test_log_message(const struct mg_connection *conn, const char *message)
262 {
263 	(void)conn;
264 
265 	printf("LOG_MESSAGE: %s\n", message);
266 	mark_point();
267 
268 	return 0; /* Return 0 means "not yet handled" */
269 }
270 
271 
272 static struct mg_context *
test_mg_start(const struct mg_callbacks * callbacks,void * user_data,const char ** configuration_options)273 test_mg_start(const struct mg_callbacks *callbacks,
274               void *user_data,
275               const char **configuration_options)
276 {
277 	struct mg_context *ctx;
278 	struct mg_callbacks cb;
279 
280 	if (callbacks) {
281 		memcpy(&cb, callbacks, sizeof(cb));
282 	} else {
283 		memset(&cb, 0, sizeof(cb));
284 	}
285 
286 	if (cb.log_message == NULL) {
287 		cb.log_message = test_log_message;
288 	}
289 
290 	mark_point();
291 	test_sleep(SLEEP_BEFORE_MG_START);
292 	mark_point();
293 	ctx = mg_start(&cb, user_data, configuration_options);
294 	mark_point();
295 	if (ctx) {
296 		/* Give the server some time to start in the test VM */
297 		/* Don't need to do this if mg_start failed */
298 		test_sleep(SLEEP_AFTER_MG_START);
299 	}
300 	mark_point();
301 
302 	return ctx;
303 }
304 
305 
306 static void
test_mg_stop(struct mg_context * ctx)307 test_mg_stop(struct mg_context *ctx)
308 {
309 #ifdef __MACH__
310 	/* For unknown reasons, there are sporadic hands
311 	 * for OSX if mark_point is called here */
312 	test_sleep(SLEEP_BEFORE_MG_STOP);
313 	mg_stop(ctx);
314 	test_sleep(SLEEP_AFTER_MG_STOP);
315 #else
316 	mark_point();
317 	test_sleep(SLEEP_BEFORE_MG_STOP);
318 	mark_point();
319 	mg_stop(ctx);
320 	mark_point();
321 	test_sleep(SLEEP_AFTER_MG_STOP);
322 	mark_point();
323 #endif
324 }
325 
326 
327 static void
test_mg_start_stop_http_server_impl(int ipv6,int bound)328 test_mg_start_stop_http_server_impl(int ipv6, int bound)
329 {
330 	struct mg_context *ctx;
331 	const char *OPTIONS[16];
332 	int optcnt = 0;
333 	const char *localhost_name = ((ipv6) ? "[::1]" : "127.0.0.1");
334 
335 #if defined(MG_LEGACY_INTERFACE)
336 	size_t ports_cnt;
337 	int ports[16];
338 	int ssl[16];
339 #endif
340 	struct mg_callbacks callbacks;
341 	char errmsg[256];
342 
343 	struct mg_connection *client_conn;
344 	char client_err[256];
345 	const struct mg_response_info *client_ri;
346 	int client_res, ret;
347 	struct mg_server_port portinfo[8];
348 
349 	mark_point();
350 
351 #if !defined(NO_FILES)
352 	OPTIONS[optcnt++] = "document_root";
353 	OPTIONS[optcnt++] = ".";
354 #endif
355 	OPTIONS[optcnt++] = "listening_ports";
356 	if (bound) {
357 		OPTIONS[optcnt++] = ((ipv6) ? "[::1]:+8080" : "127.0.0.1:8080");
358 	} else {
359 		OPTIONS[optcnt++] = ((ipv6) ? "+8080" : "8080");
360 		/* Test also tcp_nodelay - this option is not related
361 		 * to interface binding, it's just tested here in this
362 		 * combination to keep the number of tests smaller and
363 		 * the test duration shorter.
364 		 */
365 		OPTIONS[optcnt++] = "tcp_nodelay";
366 		OPTIONS[optcnt++] = "1";
367 	}
368 
369 	OPTIONS[optcnt] = 0;
370 
371 #if defined(MG_LEGACY_INTERFACE)
372 	memset(ports, 0, sizeof(ports));
373 	memset(ssl, 0, sizeof(ssl));
374 #endif
375 	memset(portinfo, 0, sizeof(portinfo));
376 	memset(&callbacks, 0, sizeof(callbacks));
377 	memset(errmsg, 0, sizeof(errmsg));
378 
379 	callbacks.log_message = log_msg_func;
380 
381 	ctx = test_mg_start(&callbacks, (void *)errmsg, OPTIONS);
382 
383 	ck_assert_str_eq(errmsg, "");
384 	ck_assert(ctx != NULL);
385 
386 #if defined(MG_LEGACY_INTERFACE)
387 	ports_cnt = mg_get_ports(ctx, 16, ports, ssl);
388 	ck_assert_uint_eq(ports_cnt, 1);
389 	ck_assert_int_eq(ports[0], 8080);
390 	ck_assert_int_eq(ssl[0], 0);
391 	ck_assert_int_eq(ports[1], 0);
392 	ck_assert_int_eq(ssl[1], 0);
393 #endif
394 
395 	ret = mg_get_server_ports(ctx, 0, portinfo);
396 	ck_assert_int_lt(ret, 0);
397 	ck_assert_int_eq(portinfo[0].protocol, 0);
398 	ck_assert_int_eq(portinfo[0].port, 0);
399 	ck_assert_int_eq(portinfo[0].is_ssl, 0);
400 	ck_assert_int_eq(portinfo[0].is_redirect, 0);
401 	ck_assert_int_eq(portinfo[1].protocol, 0);
402 	ck_assert_int_eq(portinfo[1].port, 0);
403 	ck_assert_int_eq(portinfo[1].is_ssl, 0);
404 	ck_assert_int_eq(portinfo[1].is_redirect, 0);
405 
406 	ret = mg_get_server_ports(ctx, 4, portinfo);
407 	ck_assert_int_eq(ret, 1);
408 	if (ipv6) {
409 		ck_assert_int_eq(portinfo[0].protocol, 3);
410 	} else {
411 		ck_assert_int_eq(portinfo[0].protocol, 1);
412 	}
413 	ck_assert_int_eq(portinfo[0].port, 8080);
414 	ck_assert_int_eq(portinfo[0].is_ssl, 0);
415 	ck_assert_int_eq(portinfo[0].is_redirect, 0);
416 	ck_assert_int_eq(portinfo[1].protocol, 0);
417 	ck_assert_int_eq(portinfo[1].port, 0);
418 	ck_assert_int_eq(portinfo[1].is_ssl, 0);
419 	ck_assert_int_eq(portinfo[1].is_redirect, 0);
420 
421 	test_sleep(1);
422 
423 	/* HTTP 1.0 GET request */
424 	memset(client_err, 0, sizeof(client_err));
425 	client_conn = mg_connect_client(
426 	    localhost_name, 8080, 0, client_err, sizeof(client_err));
427 
428 	ck_assert_str_eq(client_err, "");
429 	ck_assert(client_conn != NULL);
430 
431 	mg_printf(client_conn, "GET / HTTP/1.0\r\n\r\n");
432 	client_res =
433 	    mg_get_response(client_conn, client_err, sizeof(client_err), 10000);
434 	ck_assert_int_ge(client_res, 0);
435 	ck_assert_str_eq(client_err, "");
436 	client_ri = mg_get_response_info(client_conn);
437 	ck_assert(client_ri != NULL);
438 
439 #if defined(NO_FILES)
440 	ck_assert_int_eq(client_ri->status_code, 404);
441 #else
442 	ck_assert_int_eq(client_ri->status_code, 200);
443 	/* TODO: ck_assert_str_eq(client_ri->request_method, "HTTP/1.0"); */
444 	client_res = (int)mg_read(client_conn, client_err, sizeof(client_err));
445 	ck_assert_int_gt(client_res, 0);
446 	ck_assert_int_le(client_res, sizeof(client_err));
447 #endif
448 	mg_close_connection(client_conn);
449 
450 	test_sleep(1);
451 
452 	/* HTTP 1.1 GET request */
453 	memset(client_err, 0, sizeof(client_err));
454 	client_conn = mg_connect_client(
455 	    localhost_name, 8080, 0, client_err, sizeof(client_err));
456 
457 	ck_assert_str_eq(client_err, "");
458 	ck_assert(client_conn != NULL);
459 
460 	mg_printf(client_conn, "GET / HTTP/1.1\r\n");
461 	mg_printf(client_conn, "Host: localhost:8080\r\n");
462 	mg_printf(client_conn, "Connection: close\r\n\r\n");
463 	client_res =
464 	    mg_get_response(client_conn, client_err, sizeof(client_err), 10000);
465 	ck_assert_int_ge(client_res, 0);
466 	ck_assert_str_eq(client_err, "");
467 	client_ri = mg_get_response_info(client_conn);
468 	ck_assert(client_ri != NULL);
469 
470 #if defined(NO_FILES)
471 	ck_assert_int_eq(client_ri->status_code, 404);
472 #else
473 	ck_assert_int_eq(client_ri->status_code, 200);
474 	/* TODO: ck_assert_str_eq(client_ri->request_method, "HTTP/1.0"); */
475 	client_res = (int)mg_read(client_conn, client_err, sizeof(client_err));
476 	ck_assert_int_gt(client_res, 0);
477 	ck_assert_int_le(client_res, sizeof(client_err));
478 #endif
479 	mg_close_connection(client_conn);
480 
481 	test_sleep(1);
482 
483 
484 	/* HTTP 1.7 GET request - this HTTP version does not exist  */
485 	memset(client_err, 0, sizeof(client_err));
486 	client_conn = mg_connect_client(
487 	    localhost_name, 8080, 0, client_err, sizeof(client_err));
488 
489 	ck_assert_str_eq(client_err, "");
490 	ck_assert(client_conn != NULL);
491 
492 	mg_printf(client_conn, "GET / HTTP/1.7\r\n");
493 	mg_printf(client_conn, "Host: localhost:8080\r\n");
494 	mg_printf(client_conn, "Connection: close\r\n\r\n");
495 	client_res =
496 	    mg_get_response(client_conn, client_err, sizeof(client_err), 10000);
497 	ck_assert_int_ge(client_res, 0);
498 	ck_assert_str_eq(client_err, "");
499 	client_ri = mg_get_response_info(client_conn);
500 	ck_assert(client_ri != NULL);
501 
502 	/* Response must be 505 HTTP Version not supported */
503 	ck_assert_int_eq(client_ri->status_code, 505);
504 	mg_close_connection(client_conn);
505 
506 	test_sleep(1);
507 
508 
509 	/* HTTP request with multiline header.
510 	 * Multiline header are obsolete with RFC 7230 section-3.2.4
511 	 * and must return "400 Bad Request" */
512 	memset(client_err, 0, sizeof(client_err));
513 	client_conn = mg_connect_client(
514 	    localhost_name, 8080, 0, client_err, sizeof(client_err));
515 
516 	ck_assert_str_eq(client_err, "");
517 	ck_assert(client_conn != NULL);
518 
519 	mg_printf(client_conn, "GET / HTTP/1.1\r\n");
520 	mg_printf(client_conn, "Host: localhost:8080\r\n");
521 	mg_printf(client_conn, "X-Obsolete-Header: something\r\nfor nothing\r\n");
522 	mg_printf(client_conn, "Connection: close\r\n\r\n");
523 	client_res =
524 	    mg_get_response(client_conn, client_err, sizeof(client_err), 10000);
525 	ck_assert_int_ge(client_res, 0);
526 	ck_assert_str_eq(client_err, "");
527 	client_ri = mg_get_response_info(client_conn);
528 	ck_assert(client_ri != NULL);
529 
530 	/* Response must be 400 Bad Request */
531 	ck_assert_int_eq(client_ri->status_code, 400);
532 	mg_close_connection(client_conn);
533 
534 	test_sleep(1);
535 
536 	/* End test */
537 	test_mg_stop(ctx);
538 	mark_point();
539 }
540 
541 
START_TEST(test_mg_start_stop_http_server)542 START_TEST(test_mg_start_stop_http_server)
543 {
544 	mark_point();
545 	test_mg_start_stop_http_server_impl(0, 0);
546 	mark_point();
547 	test_mg_start_stop_http_server_impl(0, 1);
548 	mark_point();
549 }
550 END_TEST
551 
552 
START_TEST(test_mg_start_stop_http_server_ipv6)553 START_TEST(test_mg_start_stop_http_server_ipv6)
554 {
555 	mark_point();
556 #if defined(USE_IPV6)
557 	test_mg_start_stop_http_server_impl(1, 0);
558 	mark_point();
559 	test_mg_start_stop_http_server_impl(1, 1);
560 #endif
561 	mark_point();
562 }
563 END_TEST
564 
565 
START_TEST(test_mg_start_stop_https_server)566 START_TEST(test_mg_start_stop_https_server)
567 {
568 #ifndef NO_SSL
569 
570 	struct mg_context *ctx;
571 
572 #if defined(MG_LEGACY_INTERFACE)
573 	size_t ports_cnt;
574 	int ports[16];
575 	int ssl[16];
576 #endif
577 	struct mg_callbacks callbacks;
578 	char errmsg[256];
579 
580 	const char *OPTIONS[8]; /* initializer list here is rejected by CI test */
581 	int opt_idx = 0;
582 	const char *ssl_cert = locate_ssl_cert();
583 
584 	struct mg_connection *client_conn;
585 	char client_err[256];
586 	const struct mg_response_info *client_ri;
587 	int client_res, ret;
588 	struct mg_server_port portinfo[8];
589 
590 	ck_assert(ssl_cert != NULL);
591 
592 	memset((void *)OPTIONS, 0, sizeof(OPTIONS));
593 #if !defined(NO_FILES)
594 	OPTIONS[opt_idx++] = "document_root";
595 	OPTIONS[opt_idx++] = ".";
596 #endif
597 	OPTIONS[opt_idx++] = "listening_ports";
598 	OPTIONS[opt_idx++] = "8080r,8443s";
599 	OPTIONS[opt_idx++] = "ssl_certificate";
600 	OPTIONS[opt_idx++] = ssl_cert;
601 
602 	ck_assert_int_le(opt_idx, (int)(sizeof(OPTIONS) / sizeof(OPTIONS[0])));
603 	ck_assert(OPTIONS[sizeof(OPTIONS) / sizeof(OPTIONS[0]) - 1] == NULL);
604 	ck_assert(OPTIONS[sizeof(OPTIONS) / sizeof(OPTIONS[0]) - 2] == NULL);
605 
606 #if defined(MG_LEGACY_INTERFACE)
607 	memset(ports, 0, sizeof(ports));
608 	memset(ssl, 0, sizeof(ssl));
609 #endif
610 	memset(portinfo, 0, sizeof(portinfo));
611 	memset(&callbacks, 0, sizeof(callbacks));
612 	memset(errmsg, 0, sizeof(errmsg));
613 
614 	callbacks.log_message = log_msg_func;
615 
616 	ctx = test_mg_start(&callbacks, (void *)errmsg, OPTIONS);
617 
618 	ck_assert_str_eq(errmsg, "");
619 	ck_assert(ctx != NULL);
620 
621 #if defined(MG_LEGACY_INTERFACE)
622 	ports_cnt = mg_get_ports(ctx, 16, ports, ssl);
623 	ck_assert_uint_eq(ports_cnt, 2);
624 	ck_assert_int_eq(ports[0], 8080);
625 	ck_assert_int_eq(ssl[0], 0);
626 	ck_assert_int_eq(ports[1], 8443);
627 	ck_assert_int_eq(ssl[1], 1);
628 	ck_assert_int_eq(ports[2], 0);
629 	ck_assert_int_eq(ssl[2], 0);
630 #endif
631 
632 	ret = mg_get_server_ports(ctx, 0, portinfo);
633 	ck_assert_int_lt(ret, 0);
634 	ck_assert_int_eq(portinfo[0].protocol, 0);
635 	ck_assert_int_eq(portinfo[0].port, 0);
636 	ck_assert_int_eq(portinfo[0].is_ssl, 0);
637 	ck_assert_int_eq(portinfo[0].is_redirect, 0);
638 	ck_assert_int_eq(portinfo[1].protocol, 0);
639 	ck_assert_int_eq(portinfo[1].port, 0);
640 	ck_assert_int_eq(portinfo[1].is_ssl, 0);
641 	ck_assert_int_eq(portinfo[1].is_redirect, 0);
642 
643 	ret = mg_get_server_ports(ctx, 4, portinfo);
644 	ck_assert_int_eq(ret, 2);
645 	ck_assert_int_eq(portinfo[0].protocol, 1);
646 	ck_assert_int_eq(portinfo[0].port, 8080);
647 	ck_assert_int_eq(portinfo[0].is_ssl, 0);
648 	ck_assert_int_eq(portinfo[0].is_redirect, 1);
649 	ck_assert_int_eq(portinfo[1].protocol, 1);
650 	ck_assert_int_eq(portinfo[1].port, 8443);
651 	ck_assert_int_eq(portinfo[1].is_ssl, 1);
652 	ck_assert_int_eq(portinfo[1].is_redirect, 0);
653 	ck_assert_int_eq(portinfo[2].protocol, 0);
654 	ck_assert_int_eq(portinfo[2].port, 0);
655 	ck_assert_int_eq(portinfo[2].is_ssl, 0);
656 	ck_assert_int_eq(portinfo[2].is_redirect, 0);
657 
658 	test_sleep(1);
659 
660 	memset(client_err, 0, sizeof(client_err));
661 	client_conn =
662 	    mg_connect_client("127.0.0.1", 8443, 1, client_err, sizeof(client_err));
663 
664 	ck_assert_str_eq(client_err, "");
665 	ck_assert(client_conn != NULL);
666 
667 	mg_printf(client_conn, "GET / HTTP/1.0\r\n\r\n");
668 	client_res =
669 	    mg_get_response(client_conn, client_err, sizeof(client_err), 10000);
670 	ck_assert_int_ge(client_res, 0);
671 	ck_assert_str_eq(client_err, "");
672 	client_ri = mg_get_response_info(client_conn);
673 	ck_assert(client_ri != NULL);
674 
675 #if defined(NO_FILES)
676 	ck_assert_int_eq(client_ri->status_code, 404);
677 #else
678 	ck_assert_int_eq(client_ri->status_code, 200);
679 	/* TODO: ck_assert_str_eq(client_ri->request_method, "HTTP/1.0"); */
680 	client_res = (int)mg_read(client_conn, client_err, sizeof(client_err));
681 	ck_assert_int_gt(client_res, 0);
682 	ck_assert_int_le(client_res, sizeof(client_err));
683 #endif
684 	mg_close_connection(client_conn);
685 
686 	test_sleep(1);
687 
688 	test_mg_stop(ctx);
689 	mark_point();
690 #endif
691 }
692 END_TEST
693 
694 
START_TEST(test_mg_server_and_client_tls)695 START_TEST(test_mg_server_and_client_tls)
696 {
697 #ifndef NO_SSL
698 
699 	struct mg_context *ctx;
700 
701 	int ports_cnt;
702 	struct mg_server_port ports[16];
703 	struct mg_callbacks callbacks;
704 	char errmsg[256];
705 
706 	struct mg_connection *client_conn;
707 	char client_err[256];
708 	const struct mg_response_info *client_ri;
709 	int client_res;
710 	struct mg_client_options client_options;
711 
712 	const char *OPTIONS[32]; /* initializer list here is rejected by CI test */
713 	int opt_idx = 0;
714 	char server_cert[256];
715 	char client_cert[256];
716 	const char *res_dir = locate_resources();
717 
718 	ck_assert(res_dir != NULL);
719 	strcpy(server_cert, res_dir);
720 	strcpy(client_cert, res_dir);
721 #ifdef _WIN32
722 	strcat(server_cert, "cert\\server.pem");
723 	strcat(client_cert, "cert\\client.pem");
724 #else
725 	strcat(server_cert, "cert/server.pem");
726 	strcat(client_cert, "cert/client.pem");
727 #endif
728 
729 	memset((void *)OPTIONS, 0, sizeof(OPTIONS));
730 #if !defined(NO_FILES)
731 	OPTIONS[opt_idx++] = "document_root";
732 	OPTIONS[opt_idx++] = ".";
733 #endif
734 	OPTIONS[opt_idx++] = "listening_ports";
735 	OPTIONS[opt_idx++] = "8080r,8443s";
736 	OPTIONS[opt_idx++] = "ssl_certificate";
737 	OPTIONS[opt_idx++] = server_cert;
738 	OPTIONS[opt_idx++] = "ssl_verify_peer";
739 	OPTIONS[opt_idx++] = "yes";
740 	OPTIONS[opt_idx++] = "ssl_ca_file";
741 	OPTIONS[opt_idx++] = client_cert;
742 
743 	ck_assert_int_le(opt_idx, (int)(sizeof(OPTIONS) / sizeof(OPTIONS[0])));
744 	ck_assert(OPTIONS[sizeof(OPTIONS) / sizeof(OPTIONS[0]) - 1] == NULL);
745 	ck_assert(OPTIONS[sizeof(OPTIONS) / sizeof(OPTIONS[0]) - 2] == NULL);
746 
747 	memset(ports, 0, sizeof(ports));
748 	memset(&callbacks, 0, sizeof(callbacks));
749 	memset(errmsg, 0, sizeof(errmsg));
750 
751 	callbacks.log_message = log_msg_func;
752 
753 	ctx = test_mg_start(&callbacks, (void *)errmsg, OPTIONS);
754 
755 	ck_assert_str_eq(errmsg, "");
756 	ck_assert(ctx != NULL);
757 
758 	ports_cnt = mg_get_server_ports(ctx, 16, ports);
759 	ck_assert_int_eq(ports_cnt, 2);
760 	ck_assert_int_eq(ports[0].protocol, 1);
761 	ck_assert_int_eq(ports[0].port, 8080);
762 	ck_assert_int_eq(ports[0].is_ssl, 0);
763 	ck_assert_int_eq(ports[0].is_redirect, 1);
764 	ck_assert_int_eq(ports[1].protocol, 1);
765 	ck_assert_int_eq(ports[1].port, 8443);
766 	ck_assert_int_eq(ports[1].is_ssl, 1);
767 	ck_assert_int_eq(ports[1].is_redirect, 0);
768 	ck_assert_int_eq(ports[2].protocol, 0);
769 	ck_assert_int_eq(ports[2].port, 0);
770 	ck_assert_int_eq(ports[2].is_ssl, 0);
771 	ck_assert_int_eq(ports[2].is_redirect, 0);
772 
773 	test_sleep(1);
774 
775 	memset(client_err, 0, sizeof(client_err));
776 	client_conn =
777 	    mg_connect_client("127.0.0.1", 8443, 1, client_err, sizeof(client_err));
778 
779 	ck_assert_str_ne(client_err, "");
780 	ck_assert(client_conn == NULL);
781 
782 	memset(client_err, 0, sizeof(client_err));
783 	memset(&client_options, 0, sizeof(client_options));
784 	client_options.host = "127.0.0.1";
785 	client_options.port = 8443;
786 	client_options.client_cert = client_cert;
787 	client_options.server_cert = server_cert;
788 
789 	client_conn = mg_connect_client_secure(&client_options,
790 	                                       client_err,
791 	                                       sizeof(client_err));
792 
793 	ck_assert_str_eq(client_err, "");
794 	ck_assert(client_conn != NULL);
795 
796 	mg_printf(client_conn, "GET / HTTP/1.0\r\n\r\n");
797 	client_res =
798 	    mg_get_response(client_conn, client_err, sizeof(client_err), 10000);
799 	ck_assert_int_ge(client_res, 0);
800 	ck_assert_str_eq(client_err, "");
801 	client_ri = mg_get_response_info(client_conn);
802 	ck_assert(client_ri != NULL);
803 
804 #if defined(NO_FILES)
805 	ck_assert_int_eq(client_ri->status_code, 404);
806 #else
807 	ck_assert_int_eq(client_ri->status_code, 200);
808 	/* TODO: ck_assert_str_eq(client_ri->request_method, "HTTP/1.0"); */
809 	client_res = (int)mg_read(client_conn, client_err, sizeof(client_err));
810 	ck_assert_int_gt(client_res, 0);
811 	ck_assert_int_le(client_res, sizeof(client_err));
812 #endif
813 	mg_close_connection(client_conn);
814 
815 	/* TODO: A client API using a client certificate is missing */
816 
817 	test_sleep(1);
818 
819 	test_mg_stop(ctx);
820 #endif
821 	mark_point();
822 }
823 END_TEST
824 
825 
826 static struct mg_context *g_ctx;
827 
828 static int
request_test_handler(struct mg_connection * conn,void * cbdata)829 request_test_handler(struct mg_connection *conn, void *cbdata)
830 {
831 	int i;
832 	char chunk_data[32];
833 	const struct mg_request_info *ri;
834 	struct mg_context *ctx;
835 	void *ud, *cud;
836 	void *dummy = malloc(1);
837 
838 	ctx = mg_get_context(conn);
839 	ud = mg_get_user_data(ctx);
840 	ri = mg_get_request_info(conn);
841 
842 	ck_assert(ri != NULL);
843 	ck_assert(ctx == g_ctx);
844 	ck_assert(ud == &g_ctx);
845 
846 	ck_assert(dummy != NULL);
847 
848 	mg_set_user_connection_data(conn, (void *)&dummy);
849 	cud = mg_get_user_connection_data(conn);
850 	ck_assert_ptr_eq((void *)cud, (void *)&dummy);
851 
852 	mg_set_user_connection_data(conn, (void *)NULL);
853 	cud = mg_get_user_connection_data(conn);
854 	ck_assert_ptr_eq((void *)cud, (void *)NULL);
855 
856 	free(dummy);
857 
858 	ck_assert_ptr_eq((void *)cbdata, (void *)(ptrdiff_t)7);
859 	strcpy(chunk_data, "123456789A123456789B123456789C");
860 
861 	mg_printf(conn,
862 	          "HTTP/1.1 200 OK\r\n"
863 	          "Transfer-Encoding: chunked\r\n"
864 	          "Content-Type: text/plain\r\n\r\n");
865 
866 	for (i = 1; i <= 10; i++) {
867 		mg_printf(conn, "%x\r\n", i);
868 		mg_write(conn, chunk_data, (unsigned)i);
869 		mg_printf(conn, "\r\n");
870 	}
871 
872 	mg_printf(conn, "0\r\n\r\n");
873 	mark_point();
874 
875 	return 1;
876 }
877 
878 
879 /* Return the same as request_test_handler using new interfaces */
880 static int
request_test_handler2(struct mg_connection * conn,void * cbdata)881 request_test_handler2(struct mg_connection *conn, void *cbdata)
882 {
883 	int i;
884 	const char *chunk_data = "123456789A123456789B123456789C";
885 	const struct mg_request_info *ri;
886 	struct mg_context *ctx;
887 	void *ud;
888 
889 	ctx = mg_get_context(conn);
890 	ud = mg_get_user_data(ctx);
891 	ri = mg_get_request_info(conn);
892 
893 	ck_assert(ri != NULL);
894 	ck_assert(ctx == g_ctx);
895 	ck_assert(ud == &g_ctx);
896 
897 	mg_send_http_ok(conn, "text/plain", -1);
898 
899 	for (i = 1; i <= 10; i++) {
900 		mg_send_chunk(conn, chunk_data, (unsigned)i);
901 	}
902 
903 	mg_send_chunk(conn, 0, 0);
904 	mark_point();
905 
906 	return 200;
907 }
908 
909 
910 #ifdef USE_WEBSOCKET
911 /****************************************************************************/
912 /* WEBSOCKET SERVER                                                         */
913 /****************************************************************************/
914 static const char *websocket_welcome_msg = "websocket welcome\n";
915 static const size_t websocket_welcome_msg_len =
916     18 /* strlen(websocket_welcome_msg) */;
917 static const char *websocket_goodbye_msg = "websocket bye\n";
918 static const size_t websocket_goodbye_msg_len =
919     14 /* strlen(websocket_goodbye_msg) */;
920 
921 
922 #if defined(DEBUG)
923 static void
WS_TEST_TRACE(const char * f,...)924 WS_TEST_TRACE(const char *f, ...)
925 {
926 	va_list l;
927 	va_start(l, f);
928 	vprintf(f, l);
929 	va_end(l);
930 }
931 #else
932 #define WS_TEST_TRACE(...)
933 #endif
934 
935 
936 static int
websock_server_connect(const struct mg_connection * conn,void * udata)937 websock_server_connect(const struct mg_connection *conn, void *udata)
938 {
939 	(void)conn;
940 
941 	ck_assert_ptr_eq((void *)udata, (void *)(ptrdiff_t)7531);
942 	WS_TEST_TRACE("Server: Websocket connected\n");
943 	mark_point();
944 
945 	return 0; /* return 0 to accept every connection */
946 }
947 
948 
949 static void
websock_server_ready(struct mg_connection * conn,void * udata)950 websock_server_ready(struct mg_connection *conn, void *udata)
951 {
952 	ck_assert_ptr_eq((void *)udata, (void *)(ptrdiff_t)7531);
953 	ck_assert_ptr_ne((void *)conn, (void *)NULL);
954 	WS_TEST_TRACE("Server: Websocket ready\n");
955 
956 	/* Send websocket welcome message */
957 	mg_lock_connection(conn);
958 	mg_websocket_write(conn,
959 	                   MG_WEBSOCKET_OPCODE_TEXT,
960 	                   websocket_welcome_msg,
961 	                   websocket_welcome_msg_len);
962 	mg_unlock_connection(conn);
963 
964 	WS_TEST_TRACE("Server: Websocket ready X\n");
965 	mark_point();
966 }
967 
968 
969 #define long_ws_buf_len_16 (500)
970 #define long_ws_buf_len_64 (70000)
971 static char long_ws_buf[long_ws_buf_len_64];
972 
973 
974 static int
websock_server_data(struct mg_connection * conn,int bits,char * data,size_t data_len,void * udata)975 websock_server_data(struct mg_connection *conn,
976                     int bits,
977                     char *data,
978                     size_t data_len,
979                     void *udata)
980 {
981 	(void)bits;
982 
983 	ck_assert_ptr_eq((void *)udata, (void *)(ptrdiff_t)7531);
984 	WS_TEST_TRACE("Server: Got %u bytes from the client\n", (unsigned)data_len);
985 
986 	if (data_len == 3 && !memcmp(data, "bye", 3)) {
987 		/* Send websocket goodbye message */
988 		mg_lock_connection(conn);
989 		mg_websocket_write(conn,
990 		                   MG_WEBSOCKET_OPCODE_TEXT,
991 		                   websocket_goodbye_msg,
992 		                   websocket_goodbye_msg_len);
993 		mg_unlock_connection(conn);
994 	} else if (data_len == 5 && !memcmp(data, "data1", 5)) {
995 		mg_lock_connection(conn);
996 		mg_websocket_write(conn, MG_WEBSOCKET_OPCODE_TEXT, "ok1", 3);
997 		mg_unlock_connection(conn);
998 	} else if (data_len == 5 && !memcmp(data, "data2", 5)) {
999 		mg_lock_connection(conn);
1000 		mg_websocket_write(conn, MG_WEBSOCKET_OPCODE_TEXT, "ok 2", 4);
1001 		mg_unlock_connection(conn);
1002 	} else if (data_len == 5 && !memcmp(data, "data3", 5)) {
1003 		mg_lock_connection(conn);
1004 		mg_websocket_write(conn, MG_WEBSOCKET_OPCODE_TEXT, "ok - 3", 6);
1005 		mg_unlock_connection(conn);
1006 	} else if (data_len == long_ws_buf_len_16) {
1007 		ck_assert(memcmp(data, long_ws_buf, long_ws_buf_len_16) == 0);
1008 		mg_lock_connection(conn);
1009 		mg_websocket_write(conn,
1010 		                   MG_WEBSOCKET_OPCODE_BINARY,
1011 		                   long_ws_buf,
1012 		                   long_ws_buf_len_16);
1013 		mg_unlock_connection(conn);
1014 	} else if (data_len == long_ws_buf_len_64) {
1015 		ck_assert(memcmp(data, long_ws_buf, long_ws_buf_len_64) == 0);
1016 		mg_lock_connection(conn);
1017 		mg_websocket_write(conn,
1018 		                   MG_WEBSOCKET_OPCODE_BINARY,
1019 		                   long_ws_buf,
1020 		                   long_ws_buf_len_64);
1021 		mg_unlock_connection(conn);
1022 	} else {
1023 
1024 #if defined(__MINGW32__) || defined(__GNUC__)
1025 #pragma GCC diagnostic push
1026 #pragma GCC diagnostic ignored "-Wunreachable-code"
1027 #endif
1028 #ifdef __clang__
1029 #pragma clang diagnostic push
1030 #pragma clang diagnostic ignored "-Wunreachable-code"
1031 #endif
1032 
1033 		ck_abort_msg("Got unexpected message from websocket client");
1034 
1035 
1036 		return 0;
1037 
1038 #ifdef __clang__
1039 #pragma clang diagnostic pop
1040 #endif
1041 #if defined(__MINGW32__) || defined(__GNUC__)
1042 #pragma GCC diagnostic pop
1043 #endif
1044 	}
1045 	mark_point();
1046 
1047 	return 1; /* return 1 to keep the connetion open */
1048 }
1049 
1050 
1051 static void
websock_server_close(const struct mg_connection * conn,void * udata)1052 websock_server_close(const struct mg_connection *conn, void *udata)
1053 {
1054 #ifndef __MACH__
1055 	ck_assert_ptr_eq((void *)udata, (void *)(ptrdiff_t)7531);
1056 	WS_TEST_TRACE("Server: Close connection\n");
1057 
1058 	/* Can not send a websocket goodbye message here -
1059 	 * the connection is already closed */
1060 
1061 	mark_point();
1062 #endif
1063 
1064 	(void)conn;
1065 	(void)udata;
1066 }
1067 
1068 
1069 /****************************************************************************/
1070 /* WEBSOCKET CLIENT                                                         */
1071 /****************************************************************************/
1072 struct tclient_data {
1073 	void *data;
1074 	size_t len;
1075 	int closed;
1076 	int clientId;
1077 };
1078 
1079 
1080 static int
websocket_client_data_handler(struct mg_connection * conn,int flags,char * data,size_t data_len,void * user_data)1081 websocket_client_data_handler(struct mg_connection *conn,
1082                               int flags,
1083                               char *data,
1084                               size_t data_len,
1085                               void *user_data)
1086 {
1087 	struct mg_context *ctx = mg_get_context(conn);
1088 	struct tclient_data *pclient_data =
1089 	    (struct tclient_data *)mg_get_user_data(ctx);
1090 
1091 	ck_assert_ptr_eq(user_data, (void *)pclient_data);
1092 
1093 	ck_assert(pclient_data != NULL);
1094 	ck_assert_int_gt(flags, 128);
1095 	ck_assert_int_lt(flags, 128 + 16);
1096 	ck_assert((flags == (int)(128 | MG_WEBSOCKET_OPCODE_BINARY))
1097 	          || (flags == (int)(128 | MG_WEBSOCKET_OPCODE_TEXT)));
1098 
1099 	if (flags == (int)(128 | MG_WEBSOCKET_OPCODE_TEXT)) {
1100 		WS_TEST_TRACE(
1101 		    "Client %i received %lu bytes text data from server: %.*s\n",
1102 		    pclient_data->clientId,
1103 		    (unsigned long)data_len,
1104 		    (int)data_len,
1105 		    data);
1106 	} else {
1107 		WS_TEST_TRACE("Client %i received %lu bytes binary data from\n",
1108 		              pclient_data->clientId,
1109 		              (unsigned long)data_len);
1110 	}
1111 
1112 	pclient_data->data = malloc(data_len);
1113 	ck_assert(pclient_data->data != NULL);
1114 	memcpy(pclient_data->data, data, data_len);
1115 	pclient_data->len = data_len;
1116 
1117 	mark_point();
1118 
1119 	return 1;
1120 }
1121 
1122 
1123 static void
websocket_client_close_handler(const struct mg_connection * conn,void * user_data)1124 websocket_client_close_handler(const struct mg_connection *conn,
1125                                void *user_data)
1126 {
1127 	struct mg_context *ctx = mg_get_context(conn);
1128 	struct tclient_data *pclient_data =
1129 	    (struct tclient_data *)mg_get_user_data(ctx);
1130 
1131 #ifndef __MACH__
1132 	ck_assert_ptr_eq(user_data, (void *)pclient_data);
1133 
1134 	ck_assert(pclient_data != NULL);
1135 
1136 	WS_TEST_TRACE("Client %i: Close handler\n", pclient_data->clientId);
1137 	pclient_data->closed++;
1138 
1139 	mark_point();
1140 #else
1141 
1142 	(void)user_data;
1143 	pclient_data->closed++;
1144 
1145 #endif /* __MACH__ */
1146 }
1147 
1148 #endif /* USE_WEBSOCKET */
1149 
1150 
START_TEST(test_request_handlers)1151 START_TEST(test_request_handlers)
1152 {
1153 	char ebuf[1024];
1154 	struct mg_context *ctx;
1155 	struct mg_connection *client_conn;
1156 	const struct mg_response_info *client_ri;
1157 	char uri[64];
1158 	char buf[1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 10 + 8];
1159 	const char *expected =
1160 	    "112123123412345123456123456712345678123456789123456789A";
1161 	int i;
1162 	const char *request = "GET /U7 HTTP/1.0\r\n\r\n";
1163 #if defined(USE_IPV6) && defined(NO_SSL)
1164 	const char *HTTP_PORT = "8084,[::]:8086";
1165 	short ipv4_port = 8084;
1166 	short ipv6_port = 8086;
1167 #elif !defined(USE_IPV6) && defined(NO_SSL)
1168 	const char *HTTP_PORT = "8084";
1169 	short ipv4_port = 8084;
1170 #elif defined(USE_IPV6) && !defined(NO_SSL)
1171 	const char *HTTP_PORT = "8084,[::]:8086,8194r,[::]:8196r,8094s,[::]:8096s";
1172 	short ipv4_port = 8084;
1173 	short ipv4s_port = 8094;
1174 	short ipv4r_port = 8194;
1175 	short ipv6_port = 8086;
1176 	short ipv6s_port = 8096;
1177 	short ipv6r_port = 8196;
1178 #elif !defined(USE_IPV6) && !defined(NO_SSL)
1179 	const char *HTTP_PORT = "8084,8194r,8094s";
1180 	short ipv4_port = 8084;
1181 	short ipv4s_port = 8094;
1182 	short ipv4r_port = 8194;
1183 #endif
1184 
1185 	const char *OPTIONS[16];
1186 	const char *opt;
1187 	FILE *f;
1188 	const char *plain_file_content;
1189 	const char *cgi_script_content;
1190 	const char *expected_cgi_result;
1191 	int opt_idx = 0;
1192 	struct stat st;
1193 
1194 	const char encoded_file_content[] = "\x1f\x8b\x08\x08\xf8"
1195 	                                    "\x9d\xcb\x55\x00\x00"
1196 	                                    "test_gz.txt"
1197 	                                    "\x00\x01\x11\x00\xee\xff"
1198 	                                    "zipped text file"
1199 	                                    "\x0a\x34\x5f\xcc\x49"
1200 	                                    "\x11\x00\x00\x00";
1201 	size_t encoded_file_content_len = sizeof(encoded_file_content);
1202 
1203 
1204 #if !defined(NO_SSL)
1205 	const char *ssl_cert = locate_ssl_cert();
1206 #endif
1207 
1208 #if defined(USE_WEBSOCKET)
1209 	struct tclient_data ws_client1_data = {NULL, 0, 0, 1};
1210 	struct tclient_data ws_client2_data = {NULL, 0, 0, 2};
1211 	struct tclient_data ws_client3_data = {NULL, 0, 0, 3};
1212 	struct tclient_data ws_client4_data = {NULL, 0, 0, 4};
1213 	struct mg_connection *ws_client1_conn = NULL;
1214 	struct mg_connection *ws_client2_conn = NULL;
1215 	struct mg_connection *ws_client3_conn = NULL;
1216 	struct mg_connection *ws_client4_conn = NULL;
1217 #endif
1218 
1219 	char cmd_buf[1024];
1220 	char *cgi_env_opt;
1221 
1222 	mark_point();
1223 
1224 	memset((void *)OPTIONS, 0, sizeof(OPTIONS));
1225 	OPTIONS[opt_idx++] = "listening_ports";
1226 	OPTIONS[opt_idx++] = HTTP_PORT;
1227 	OPTIONS[opt_idx++] = "authentication_domain";
1228 	OPTIONS[opt_idx++] = "test.domain";
1229 #if !defined(NO_FILES)
1230 	OPTIONS[opt_idx++] = "document_root";
1231 	OPTIONS[opt_idx++] = ".";
1232 #endif
1233 #ifndef NO_SSL
1234 	ck_assert(ssl_cert != NULL);
1235 	OPTIONS[opt_idx++] = "ssl_certificate";
1236 	OPTIONS[opt_idx++] = ssl_cert;
1237 #endif
1238 	OPTIONS[opt_idx++] = "cgi_environment";
1239 	cgi_env_opt = (char *)calloc(1, 4096 /* CGI_ENVIRONMENT_SIZE */);
1240 	ck_assert(cgi_env_opt != NULL);
1241 	cgi_env_opt[0] = 'x';
1242 	cgi_env_opt[1] = '=';
1243 	memset(cgi_env_opt + 2, 'y', 4090); /* Add large env field, so the server
1244 	                                     * must reallocate buffers. */
1245 	OPTIONS[opt_idx++] = cgi_env_opt;
1246 
1247 	OPTIONS[opt_idx++] = "num_threads";
1248 	OPTIONS[opt_idx++] = "2";
1249 
1250 
1251 	ck_assert_int_le(opt_idx, (int)(sizeof(OPTIONS) / sizeof(OPTIONS[0])));
1252 	ck_assert(OPTIONS[sizeof(OPTIONS) / sizeof(OPTIONS[0]) - 1] == NULL);
1253 	ck_assert(OPTIONS[sizeof(OPTIONS) / sizeof(OPTIONS[0]) - 2] == NULL);
1254 
1255 	ctx = test_mg_start(NULL, &g_ctx, OPTIONS);
1256 
1257 	ck_assert(ctx != NULL);
1258 	g_ctx = ctx;
1259 
1260 	opt = mg_get_option(ctx, "listening_ports");
1261 	ck_assert_str_eq(opt, HTTP_PORT);
1262 
1263 	opt = mg_get_option(ctx, "cgi_environment");
1264 	ck_assert_ptr_ne(opt, cgi_env_opt);
1265 	ck_assert_int_eq((int)opt[0], (int)cgi_env_opt[0]);
1266 	ck_assert_int_eq((int)opt[1], (int)cgi_env_opt[1]);
1267 	ck_assert_int_eq((int)opt[2], (int)cgi_env_opt[2]);
1268 	ck_assert_int_eq((int)opt[3], (int)cgi_env_opt[3]);
1269 	/* full length string compare will reach limit in the implementation
1270 	 * of the check unit test framework */
1271 	{
1272 		size_t len_check_1 = strlen(opt);
1273 		size_t len_check_2 = strlen(cgi_env_opt);
1274 		ck_assert_uint_eq(len_check_1, len_check_2);
1275 	}
1276 
1277 	/* We don't need the original anymore, the server has a private copy */
1278 	free(cgi_env_opt);
1279 
1280 	opt = mg_get_option(ctx, "throttle");
1281 	ck_assert_str_eq(opt, "");
1282 
1283 	opt = mg_get_option(ctx, "unknown_option_name");
1284 	ck_assert(opt == NULL);
1285 
1286 	for (i = 0; i < 1000; i++) {
1287 		sprintf(uri, "/U%u", i);
1288 		mg_set_request_handler(ctx, uri, request_test_handler, NULL);
1289 	}
1290 	for (i = 500; i < 800; i++) {
1291 		sprintf(uri, "/U%u", i);
1292 		mg_set_request_handler(ctx, uri, NULL, (void *)(ptrdiff_t)1);
1293 	}
1294 	for (i = 600; i >= 0; i--) {
1295 		sprintf(uri, "/U%u", i);
1296 		mg_set_request_handler(ctx, uri, NULL, (void *)(ptrdiff_t)2);
1297 	}
1298 	for (i = 750; i <= 1000; i++) {
1299 		sprintf(uri, "/U%u", i);
1300 		mg_set_request_handler(ctx, uri, NULL, (void *)(ptrdiff_t)3);
1301 	}
1302 	for (i = 5; i < 9; i++) {
1303 		sprintf(uri, "/U%u", i);
1304 		mg_set_request_handler(ctx,
1305 		                       uri,
1306 		                       request_test_handler,
1307 		                       (void *)(ptrdiff_t)i);
1308 	}
1309 
1310 	mg_set_request_handler(ctx, "/handler2", request_test_handler2, NULL);
1311 
1312 #ifdef USE_WEBSOCKET
1313 	mg_set_websocket_handler(ctx,
1314 	                         "/websocket",
1315 	                         websock_server_connect,
1316 	                         websock_server_ready,
1317 	                         websock_server_data,
1318 	                         websock_server_close,
1319 	                         (void *)(ptrdiff_t)7531);
1320 #endif
1321 
1322 	/* Try to load non existing file */
1323 	client_conn = mg_download("localhost",
1324 	                          ipv4_port,
1325 	                          0,
1326 	                          ebuf,
1327 	                          sizeof(ebuf),
1328 	                          "%s",
1329 	                          "GET /file/not/found HTTP/1.0\r\n\r\n");
1330 	ck_assert(client_conn != NULL);
1331 	client_ri = mg_get_response_info(client_conn);
1332 
1333 	ck_assert(client_ri != NULL);
1334 	ck_assert_int_eq(client_ri->status_code, 404);
1335 	mg_close_connection(client_conn);
1336 
1337 	/* Get data from callback */
1338 	client_conn = mg_download(
1339 	    "localhost", ipv4_port, 0, ebuf, sizeof(ebuf), "%s", request);
1340 	ck_assert(client_conn != NULL);
1341 	client_ri = mg_get_response_info(client_conn);
1342 
1343 	ck_assert(client_ri != NULL);
1344 	ck_assert_int_eq(client_ri->status_code, 200);
1345 	i = mg_read(client_conn, buf, sizeof(buf));
1346 	ck_assert_int_eq(i, (int)strlen(expected));
1347 	buf[i] = 0;
1348 	ck_assert_str_eq(buf, expected);
1349 	mg_close_connection(client_conn);
1350 
1351 	/* Get data from callback using http://127.0.0.1 */
1352 	client_conn = mg_download(
1353 	    "127.0.0.1", ipv4_port, 0, ebuf, sizeof(ebuf), "%s", request);
1354 	ck_assert(client_conn != NULL);
1355 	client_ri = mg_get_response_info(client_conn);
1356 
1357 	ck_assert(client_ri != NULL);
1358 	ck_assert_int_eq(client_ri->status_code, 200);
1359 	i = mg_read(client_conn, buf, sizeof(buf));
1360 	if ((i >= 0) && ((size_t)i < sizeof(buf))) {
1361 		buf[i] = 0;
1362 	} else {
1363 		ck_abort_msg(
1364 		    "ERROR: test_request_handlers: read returned %i (>=0, <%i)",
1365 		    (int)i,
1366 		    (int)sizeof(buf));
1367 	}
1368 	ck_assert((int)i < (int)sizeof(buf));
1369 	ck_assert(i > 0);
1370 	ck_assert_int_eq(i, (int)strlen(expected));
1371 	buf[i] = 0;
1372 	ck_assert_str_eq(buf, expected);
1373 	mg_close_connection(client_conn);
1374 
1375 #if defined(USE_IPV6)
1376 	/* Get data from callback using http://[::1] */
1377 	client_conn =
1378 	    mg_download("[::1]", ipv6_port, 0, ebuf, sizeof(ebuf), "%s", request);
1379 	ck_assert(client_conn != NULL);
1380 	client_ri = mg_get_response_info(client_conn);
1381 
1382 	ck_assert(client_ri != NULL);
1383 	ck_assert_int_eq(client_ri->status_code, 200);
1384 	i = mg_read(client_conn, buf, sizeof(buf));
1385 	ck_assert_int_eq(i, (int)strlen(expected));
1386 	buf[i] = 0;
1387 	ck_assert_str_eq(buf, expected);
1388 	mg_close_connection(client_conn);
1389 #endif
1390 
1391 #if !defined(NO_SSL)
1392 	/* Get data from callback using https://127.0.0.1 */
1393 	client_conn = mg_download(
1394 	    "127.0.0.1", ipv4s_port, 1, ebuf, sizeof(ebuf), "%s", request);
1395 	ck_assert(client_conn != NULL);
1396 	client_ri = mg_get_response_info(client_conn);
1397 
1398 	ck_assert(client_ri != NULL);
1399 	ck_assert_int_eq(client_ri->status_code, 200);
1400 	i = mg_read(client_conn, buf, sizeof(buf));
1401 	ck_assert_int_eq(i, (int)strlen(expected));
1402 	buf[i] = 0;
1403 	ck_assert_str_eq(buf, expected);
1404 	mg_close_connection(client_conn);
1405 
1406 	/* Get redirect from callback using http://127.0.0.1 */
1407 	client_conn = mg_download(
1408 	    "127.0.0.1", ipv4r_port, 0, ebuf, sizeof(ebuf), "%s", request);
1409 	ck_assert(client_conn != NULL);
1410 	client_ri = mg_get_response_info(client_conn);
1411 
1412 	ck_assert(client_ri != NULL);
1413 	ck_assert((client_ri->status_code == 301) || (client_ri->status_code == 302)
1414 	          || (client_ri->status_code == 303)
1415 	          || (client_ri->status_code == 307)
1416 	          || (client_ri->status_code == 308)); /* is a redirect code */
1417 	/*
1418 	// A redirect may have a body, or not
1419 	i = mg_read(client_conn, buf, sizeof(buf));
1420 	ck_assert_int_eq(i, -1);
1421 	*/
1422 	mg_close_connection(client_conn);
1423 #endif
1424 
1425 #if defined(USE_IPV6) && !defined(NO_SSL)
1426 	/* Get data from callback using https://[::1] */
1427 	client_conn =
1428 	    mg_download("[::1]", ipv6s_port, 1, ebuf, sizeof(ebuf), "%s", request);
1429 	ck_assert(client_conn != NULL);
1430 	client_ri = mg_get_response_info(client_conn);
1431 
1432 	ck_assert(client_ri != NULL);
1433 	ck_assert_int_eq(client_ri->status_code, 200);
1434 	i = mg_read(client_conn, buf, sizeof(buf));
1435 	ck_assert_int_eq(i, (int)strlen(expected));
1436 	buf[i] = 0;
1437 	ck_assert_str_eq(buf, expected);
1438 	mg_close_connection(client_conn);
1439 
1440 	/* Get redirect from callback using http://127.0.0.1 */
1441 	client_conn =
1442 	    mg_download("[::1]", ipv6r_port, 0, ebuf, sizeof(ebuf), "%s", request);
1443 	ck_assert(client_conn != NULL);
1444 	client_ri = mg_get_response_info(client_conn);
1445 
1446 	ck_assert(client_ri != NULL);
1447 	ck_assert((client_ri->status_code == 301) || (client_ri->status_code == 302)
1448 	          || (client_ri->status_code == 303)
1449 	          || (client_ri->status_code == 307)
1450 	          || (client_ri->status_code == 308)); /* is a redirect code */
1451 	/*
1452 	// A redirect may have a body, or not
1453 	i = mg_read(client_conn, buf, sizeof(buf));
1454 	ck_assert_int_eq(i, -1);
1455 	*/
1456 	mg_close_connection(client_conn);
1457 #endif
1458 
1459 /* It seems to be impossible to find out what the actual working
1460  * directory of the CI test environment is. Before breaking another
1461  * dozen of builds by trying blindly with different paths, just
1462  * create the file here */
1463 #ifdef _WIN32
1464 	f = fopen("test.txt", "wb");
1465 #else
1466 	f = fopen("test.txt", "w");
1467 #endif
1468 	ck_assert(f != NULL);
1469 	plain_file_content = "simple text file\n";
1470 	i = (int)strlen(plain_file_content);
1471 	ck_assert_int_eq(i, 17);
1472 
1473 	fwrite(plain_file_content, i, 1, f);
1474 	fclose(f);
1475 
1476 #ifdef _WIN32
1477 	f = fopen("test_gz.txt.gz", "wb");
1478 #else
1479 	f = fopen("test_gz.txt.gz", "w");
1480 #endif
1481 	ck_assert(f != NULL);
1482 	ck_assert_uint_ge(encoded_file_content_len, 52);
1483 	encoded_file_content_len = 52;
1484 	fwrite(encoded_file_content, 1, encoded_file_content_len, f);
1485 	fclose(f);
1486 
1487 #ifdef _WIN32
1488 	f = fopen("test.cgi", "wb");
1489 	cgi_script_content = "#!test.cgi.cmd\r\n";
1490 	fwrite(cgi_script_content, strlen(cgi_script_content), 1, f);
1491 	fclose(f);
1492 	f = fopen("test.cgi.cmd", "w");
1493 	cgi_script_content = "@echo off\r\n"
1494 	                     "echo Connection: close\r\n"
1495 	                     "echo Content-Type: text/plain\r\n"
1496 	                     "echo.\r\n"
1497 	                     "echo CGI test\r\n"
1498 	                     "\r\n";
1499 	fwrite(cgi_script_content, strlen(cgi_script_content), 1, f);
1500 	fclose(f);
1501 #else
1502 	f = fopen("test.cgi", "w");
1503 	cgi_script_content = "#!/bin/sh\n\n"
1504 	                     "printf \"Connection: close\\r\\n\"\n"
1505 	                     "printf \"Content-Type: text/plain\\r\\n\"\n"
1506 	                     "printf \"\\r\\n\"\n"
1507 	                     "printf \"CGI test\\r\\n\"\n"
1508 	                     "\n";
1509 	(void)fwrite(cgi_script_content, strlen(cgi_script_content), 1, f);
1510 	(void)fclose(f);
1511 	(void)system("chmod a+x test.cgi");
1512 #endif
1513 	expected_cgi_result = "CGI test";
1514 
1515 	/* Get static data */
1516 	client_conn = mg_download("localhost",
1517 	                          ipv4_port,
1518 	                          0,
1519 	                          ebuf,
1520 	                          sizeof(ebuf),
1521 	                          "%s",
1522 	                          "GET /test.txt HTTP/1.0\r\n\r\n");
1523 	ck_assert(client_conn != NULL);
1524 	client_ri = mg_get_response_info(client_conn);
1525 
1526 	ck_assert(client_ri != NULL);
1527 
1528 #if defined(NO_FILES)
1529 	ck_assert_int_eq(client_ri->status_code, 404);
1530 #else
1531 	ck_assert_int_eq(client_ri->status_code, 200);
1532 	i = mg_read(client_conn, buf, sizeof(buf));
1533 	ck_assert_int_eq(i, 17);
1534 	if ((i >= 0) && (i < (int)sizeof(buf))) {
1535 		buf[i] = 0;
1536 	}
1537 	ck_assert_str_eq(buf, plain_file_content);
1538 #endif
1539 	mg_close_connection(client_conn);
1540 
1541 
1542 	/* Check if CGI test executable exists */
1543 	memset(&st, 0, sizeof(st));
1544 
1545 #if defined(_WIN32)
1546 	/* TODO: not yet available */
1547 	sprintf(ebuf, "%scgi_test.cgi", locate_test_exes());
1548 #else
1549 	sprintf(ebuf, "%scgi_test.cgi", locate_test_exes());
1550 
1551 	if (stat(ebuf, &st) != 0) {
1552 		fprintf(stderr, "\nFile %s not found\n", ebuf);
1553 		fprintf(stderr,
1554 		        "This file needs to be compiled manually before "
1555 		        "starting the test\n");
1556 		fprintf(stderr,
1557 		        "e.g. by gcc test/cgi_test.c -o output/cgi_test.cgi\n\n");
1558 
1559 		/* Abort test with diagnostic message */
1560 		ck_abort_msg("Mandatory file %s must be built before starting the test",
1561 		             ebuf);
1562 	}
1563 #endif
1564 
1565 
1566 /* Test with CGI test executable */
1567 #if defined(_WIN32)
1568 	sprintf(cmd_buf, "copy %s\\cgi_test.cgi cgi_test.exe", locate_test_exes());
1569 #else
1570 	sprintf(cmd_buf, "cp %s/cgi_test.cgi cgi_test.cgi", locate_test_exes());
1571 #endif
1572 	(void)system(cmd_buf);
1573 
1574 #if !defined(NO_CGI) && !defined(NO_FILES) && !defined(_WIN32)
1575 	/* TODO: add test for windows, check with POST */
1576 	client_conn = mg_download(
1577 	    "localhost",
1578 	    ipv4_port,
1579 	    0,
1580 	    ebuf,
1581 	    sizeof(ebuf),
1582 	    "%s",
1583 	    "POST /cgi_test.cgi/x/y.z HTTP/1.0\r\nContent-Length: 3\r\n\r\nABC");
1584 	ck_assert(client_conn != NULL);
1585 	client_ri = mg_get_response_info(client_conn);
1586 
1587 	ck_assert(client_ri != NULL);
1588 	ck_assert_int_eq(client_ri->status_code, 200);
1589 	mg_close_connection(client_conn);
1590 #endif
1591 
1592 	/* Get zipped static data - will not work if Accept-Encoding is not set */
1593 	client_conn = mg_download("localhost",
1594 	                          ipv4_port,
1595 	                          0,
1596 	                          ebuf,
1597 	                          sizeof(ebuf),
1598 	                          "%s",
1599 	                          "GET /test_gz.txt HTTP/1.0\r\n\r\n");
1600 	ck_assert(client_conn != NULL);
1601 	client_ri = mg_get_response_info(client_conn);
1602 
1603 	ck_assert(client_ri != NULL);
1604 	ck_assert_int_eq(client_ri->status_code, 404);
1605 	mg_close_connection(client_conn);
1606 
1607 	/* Get zipped static data - with Accept-Encoding */
1608 	client_conn = mg_download(
1609 	    "localhost",
1610 	    ipv4_port,
1611 	    0,
1612 	    ebuf,
1613 	    sizeof(ebuf),
1614 	    "%s",
1615 	    "GET /test_gz.txt HTTP/1.0\r\nAccept-Encoding: gzip\r\n\r\n");
1616 	ck_assert(client_conn != NULL);
1617 	client_ri = mg_get_response_info(client_conn);
1618 
1619 	ck_assert(client_ri != NULL);
1620 
1621 #if defined(NO_FILES)
1622 	ck_assert_int_eq(client_ri->status_code, 404);
1623 #else
1624 	ck_assert_int_eq(client_ri->status_code, 200);
1625 	i = mg_read(client_conn, buf, sizeof(buf));
1626 	ck_assert_int_eq(i, 52);
1627 	if ((i >= 0) && (i < (int)sizeof(buf))) {
1628 		buf[i] = 0;
1629 	}
1630 	ck_assert_int_eq(client_ri->content_length, 52);
1631 	ck_assert_str_eq(buf, encoded_file_content);
1632 #endif
1633 	mg_close_connection(client_conn);
1634 
1635 /* Get CGI generated data */
1636 #if !defined(NO_CGI)
1637 	client_conn = mg_download("localhost",
1638 	                          ipv4_port,
1639 	                          0,
1640 	                          ebuf,
1641 	                          sizeof(ebuf),
1642 	                          "%s",
1643 	                          "GET /test.cgi HTTP/1.0\r\n\r\n");
1644 	ck_assert(client_conn != NULL);
1645 	client_ri = mg_get_response_info(client_conn);
1646 
1647 	ck_assert(client_ri != NULL);
1648 
1649 #if defined(NO_FILES)
1650 	ck_assert_int_eq(client_ri->status_code, 404);
1651 
1652 	(void)expected_cgi_result;
1653 	(void)cgi_script_content;
1654 #else
1655 	i = mg_read(client_conn, buf, sizeof(buf));
1656 	if ((i >= 0) && (i < (int)sizeof(buf))) {
1657 		while ((i > 0) && ((buf[i - 1] == '\r') || (buf[i - 1] == '\n'))) {
1658 			i--;
1659 		}
1660 		buf[i] = 0;
1661 	}
1662 	/* ck_assert_int_eq(i, (int)strlen(expected_cgi_result)); */
1663 	ck_assert_str_eq(buf, expected_cgi_result);
1664 	ck_assert_int_eq(client_ri->status_code, 200);
1665 	mg_close_connection(client_conn);
1666 #endif
1667 
1668 #else
1669 	(void)expected_cgi_result;
1670 	(void)cgi_script_content;
1671 #endif
1672 
1673 	/* Get directory listing */
1674 	client_conn = mg_download("localhost",
1675 	                          ipv4_port,
1676 	                          0,
1677 	                          ebuf,
1678 	                          sizeof(ebuf),
1679 	                          "%s",
1680 	                          "GET / HTTP/1.0\r\n\r\n");
1681 	ck_assert(client_conn != NULL);
1682 	client_ri = mg_get_response_info(client_conn);
1683 
1684 	ck_assert(client_ri != NULL);
1685 #if defined(NO_FILES)
1686 	ck_assert_int_eq(client_ri->status_code, 404);
1687 #else
1688 	ck_assert_int_eq(client_ri->status_code, 200);
1689 	i = mg_read(client_conn, buf, sizeof(buf));
1690 	ck_assert(i > 6);
1691 	buf[6] = 0;
1692 	ck_assert_str_eq(buf, "<html>");
1693 #endif
1694 	mg_close_connection(client_conn);
1695 
1696 	/* POST to static file (will not work) */
1697 	client_conn = mg_download("localhost",
1698 	                          ipv4_port,
1699 	                          0,
1700 	                          ebuf,
1701 	                          sizeof(ebuf),
1702 	                          "%s",
1703 	                          "POST /test.txt HTTP/1.0\r\n\r\n");
1704 	ck_assert(client_conn != NULL);
1705 	client_ri = mg_get_response_info(client_conn);
1706 
1707 	ck_assert(client_ri != NULL);
1708 #if defined(NO_FILES)
1709 	ck_assert_int_eq(client_ri->status_code, 404);
1710 #else
1711 	ck_assert_int_eq(client_ri->status_code, 405);
1712 	i = mg_read(client_conn, buf, sizeof(buf));
1713 	ck_assert(i >= 29);
1714 	buf[29] = 0;
1715 	ck_assert_str_eq(buf, "Error 405: Method Not Allowed");
1716 #endif
1717 	mg_close_connection(client_conn);
1718 
1719 	/* PUT to static file (will not work) */
1720 	client_conn = mg_download("localhost",
1721 	                          ipv4_port,
1722 	                          0,
1723 	                          ebuf,
1724 	                          sizeof(ebuf),
1725 	                          "%s",
1726 	                          "PUT /test.txt HTTP/1.0\r\n\r\n");
1727 	ck_assert(client_conn != NULL);
1728 	client_ri = mg_get_response_info(client_conn);
1729 
1730 	ck_assert(client_ri != NULL);
1731 #if defined(NO_FILES)
1732 	ck_assert_int_eq(client_ri->status_code, 405); /* method not allowed */
1733 #else
1734 	ck_assert_int_eq(client_ri->status_code, 401); /* not authorized */
1735 #endif
1736 	mg_close_connection(client_conn);
1737 
1738 
1739 	/* Get data from callback using mg_connect_client instead of mg_download */
1740 	memset(ebuf, 0, sizeof(ebuf));
1741 	client_conn =
1742 	    mg_connect_client("127.0.0.1", ipv4_port, 0, ebuf, sizeof(ebuf));
1743 
1744 	ck_assert_str_eq(ebuf, "");
1745 	ck_assert(client_conn != NULL);
1746 
1747 	mg_printf(client_conn, "%s", request);
1748 
1749 	i = mg_get_response(client_conn, ebuf, sizeof(ebuf), 10000);
1750 	ck_assert_int_ge(i, 0);
1751 	ck_assert_str_eq(ebuf, "");
1752 
1753 	client_ri = mg_get_response_info(client_conn);
1754 
1755 	ck_assert(client_ri != NULL);
1756 	ck_assert_int_eq(client_ri->status_code, 200);
1757 	i = mg_read(client_conn, buf, sizeof(buf));
1758 	ck_assert_int_eq(i, (int)strlen(expected));
1759 	buf[i] = 0;
1760 	ck_assert_str_eq(buf, expected);
1761 	mg_close_connection(client_conn);
1762 
1763 	/* Get data from callback using mg_connect_client and absolute URI */
1764 	memset(ebuf, 0, sizeof(ebuf));
1765 	client_conn =
1766 	    mg_connect_client("localhost", ipv4_port, 0, ebuf, sizeof(ebuf));
1767 
1768 	ck_assert_str_eq(ebuf, "");
1769 	ck_assert(client_conn != NULL);
1770 
1771 	mg_printf(client_conn,
1772 	          "GET http://test.domain:%d/U7 HTTP/1.0\r\n\r\n",
1773 	          ipv4_port);
1774 
1775 	i = mg_get_response(client_conn, ebuf, sizeof(ebuf), 10000);
1776 	ck_assert_int_ge(i, 0);
1777 	ck_assert_str_eq(ebuf, "");
1778 
1779 	client_ri = mg_get_response_info(client_conn);
1780 
1781 	ck_assert(client_ri != NULL);
1782 	ck_assert_int_eq(client_ri->status_code, 200);
1783 	i = mg_read(client_conn, buf, sizeof(buf));
1784 	ck_assert_int_eq(i, (int)strlen(expected));
1785 	buf[i] = 0;
1786 	ck_assert_str_eq(buf, expected);
1787 	mg_close_connection(client_conn);
1788 
1789 	/* Get data from callback using mg_connect_client and absolute URI with a
1790 	 * sub-domain */
1791 	memset(ebuf, 0, sizeof(ebuf));
1792 	client_conn =
1793 	    mg_connect_client("localhost", ipv4_port, 0, ebuf, sizeof(ebuf));
1794 
1795 	ck_assert_str_eq(ebuf, "");
1796 	ck_assert(client_conn != NULL);
1797 
1798 	mg_printf(client_conn,
1799 	          "GET http://subdomain.test.domain:%d/U7 HTTP/1.0\r\n\r\n",
1800 	          ipv4_port);
1801 
1802 	i = mg_get_response(client_conn, ebuf, sizeof(ebuf), 10000);
1803 	ck_assert_int_ge(i, 0);
1804 	ck_assert_str_eq(ebuf, "");
1805 
1806 	client_ri = mg_get_response_info(client_conn);
1807 
1808 	ck_assert(client_ri != NULL);
1809 	ck_assert_int_eq(client_ri->status_code, 200);
1810 	i = mg_read(client_conn, buf, sizeof(buf));
1811 	ck_assert_int_eq(i, (int)strlen(expected));
1812 	buf[i] = 0;
1813 	ck_assert_str_eq(buf, expected);
1814 	mg_close_connection(client_conn);
1815 
1816 	/* Get data from handler2 */
1817 	client_conn =
1818 	    mg_connect_client("localhost", ipv4_port, 0, ebuf, sizeof(ebuf));
1819 
1820 	ck_assert_str_eq(ebuf, "");
1821 	ck_assert(client_conn != NULL);
1822 
1823 	mg_printf(client_conn,
1824 	          "GET /handler2 HTTP/1.1\r\n"
1825 	          "Host: localhost\r\n"
1826 	          "\r\n",
1827 	          ipv4_port);
1828 
1829 	i = mg_get_response(client_conn, ebuf, sizeof(ebuf), 10000);
1830 	ck_assert_int_ge(i, 0);
1831 	ck_assert_str_eq(ebuf, "");
1832 
1833 	client_ri = mg_get_response_info(client_conn);
1834 
1835 	ck_assert(client_ri != NULL);
1836 	ck_assert_int_eq(client_ri->status_code, 200);
1837 	i = mg_read(client_conn, buf, sizeof(buf));
1838 	ck_assert_int_eq(i, (int)strlen(expected));
1839 	buf[i] = 0;
1840 	ck_assert_str_eq(buf, expected);
1841 	mg_close_connection(client_conn);
1842 
1843 	/* Get data non existing handler (will return 404) */
1844 	client_conn =
1845 	    mg_connect_client("localhost", ipv4_port, 0, ebuf, sizeof(ebuf));
1846 
1847 	ck_assert_str_eq(ebuf, "");
1848 	ck_assert(client_conn != NULL);
1849 
1850 	mg_printf(client_conn,
1851 	          "GET /unknown_url HTTP/1.1\r\n"
1852 	          "Host: localhost\r\n"
1853 	          "\r\n",
1854 	          ipv4_port);
1855 
1856 	i = mg_get_response(client_conn, ebuf, sizeof(ebuf), 10000);
1857 	ck_assert_int_ge(i, 0);
1858 	ck_assert_str_eq(ebuf, "");
1859 
1860 	client_ri = mg_get_response_info(client_conn);
1861 
1862 	ck_assert(client_ri != NULL);
1863 	ck_assert_int_eq(client_ri->status_code, 404);
1864 	mg_close_connection(client_conn);
1865 
1866 	/* Get data from handler2, but only read a part of it */
1867 	client_conn =
1868 	    mg_connect_client("localhost", ipv4_port, 0, ebuf, sizeof(ebuf));
1869 
1870 	ck_assert_str_eq(ebuf, "");
1871 	ck_assert(client_conn != NULL);
1872 
1873 	mg_printf(client_conn,
1874 	          "GET /handler2 HTTP/1.1\r\n"
1875 	          "Host: localhost\r\n"
1876 	          "\r\n",
1877 	          ipv4_port);
1878 
1879 	i = mg_get_response(client_conn, ebuf, sizeof(ebuf), 10000);
1880 	ck_assert_int_ge(i, 0);
1881 	ck_assert_str_eq(ebuf, "");
1882 
1883 	client_ri = mg_get_response_info(client_conn);
1884 
1885 	ck_assert(client_ri != NULL);
1886 	ck_assert_int_eq(client_ri->status_code, 200);
1887 	i = mg_read(client_conn, buf, 7);
1888 	ck_assert_int_eq(i, 7);
1889 	ck_assert(0 == memcmp(buf, expected, 7));
1890 	mg_close_connection(client_conn);
1891 
1892 
1893 /* Websocket test */
1894 #ifdef USE_WEBSOCKET
1895 	/* Then connect a first client */
1896 	ws_client1_conn =
1897 	    mg_connect_websocket_client("localhost",
1898 	                                ipv4_port,
1899 	                                0,
1900 	                                ebuf,
1901 	                                sizeof(ebuf),
1902 	                                "/websocket",
1903 	                                NULL,
1904 	                                websocket_client_data_handler,
1905 	                                websocket_client_close_handler,
1906 	                                &ws_client1_data);
1907 
1908 	ck_assert(ws_client1_conn != NULL);
1909 
1910 	wait_not_null(
1911 	    &(ws_client1_data.data)); /* Wait for the websocket welcome message */
1912 	ck_assert_int_eq(ws_client1_data.closed, 0);
1913 	ck_assert_int_eq(ws_client2_data.closed, 0);
1914 	ck_assert_int_eq(ws_client3_data.closed, 0);
1915 	ck_assert(ws_client2_data.data == NULL);
1916 	ck_assert_uint_eq(ws_client2_data.len, 0);
1917 	ck_assert(ws_client1_data.data != NULL);
1918 	ck_assert_uint_eq(ws_client1_data.len, websocket_welcome_msg_len);
1919 	ck_assert(!memcmp(ws_client1_data.data,
1920 	                  websocket_welcome_msg,
1921 	                  websocket_welcome_msg_len));
1922 	free(ws_client1_data.data);
1923 	ws_client1_data.data = NULL;
1924 	ws_client1_data.len = 0;
1925 
1926 	mg_websocket_client_write(ws_client1_conn,
1927 	                          MG_WEBSOCKET_OPCODE_TEXT,
1928 	                          "data1",
1929 	                          5);
1930 
1931 	wait_not_null(&(
1932 	    ws_client1_data.data)); /* Wait for the websocket acknowledge message */
1933 	ck_assert_int_eq(ws_client1_data.closed, 0);
1934 	ck_assert_int_eq(ws_client2_data.closed, 0);
1935 	ck_assert(ws_client2_data.data == NULL);
1936 	ck_assert_uint_eq(ws_client2_data.len, 0);
1937 	ck_assert(ws_client1_data.data != NULL);
1938 	ck_assert_uint_eq(ws_client1_data.len, 3);
1939 	ck_assert(!memcmp(ws_client1_data.data, "ok1", 3));
1940 	free(ws_client1_data.data);
1941 	ws_client1_data.data = NULL;
1942 	ws_client1_data.len = 0;
1943 
1944 /* Now connect a second client */
1945 #ifdef USE_IPV6
1946 	ws_client2_conn =
1947 	    mg_connect_websocket_client("[::1]",
1948 	                                ipv6_port,
1949 	                                0,
1950 	                                ebuf,
1951 	                                sizeof(ebuf),
1952 	                                "/websocket",
1953 	                                NULL,
1954 	                                websocket_client_data_handler,
1955 	                                websocket_client_close_handler,
1956 	                                &ws_client2_data);
1957 #else
1958 	ws_client2_conn =
1959 	    mg_connect_websocket_client("127.0.0.1",
1960 	                                ipv4_port,
1961 	                                0,
1962 	                                ebuf,
1963 	                                sizeof(ebuf),
1964 	                                "/websocket",
1965 	                                NULL,
1966 	                                websocket_client_data_handler,
1967 	                                websocket_client_close_handler,
1968 	                                &ws_client2_data);
1969 #endif
1970 	ck_assert(ws_client2_conn != NULL);
1971 
1972 	wait_not_null(
1973 	    &(ws_client2_data.data)); /* Wait for the websocket welcome message */
1974 	ck_assert(ws_client1_data.closed == 0);
1975 	ck_assert(ws_client2_data.closed == 0);
1976 	ck_assert(ws_client1_data.data == NULL);
1977 	ck_assert(ws_client1_data.len == 0);
1978 	ck_assert(ws_client2_data.data != NULL);
1979 	ck_assert(ws_client2_data.len == websocket_welcome_msg_len);
1980 	ck_assert(!memcmp(ws_client2_data.data,
1981 	                  websocket_welcome_msg,
1982 	                  websocket_welcome_msg_len));
1983 	free(ws_client2_data.data);
1984 	ws_client2_data.data = NULL;
1985 	ws_client2_data.len = 0;
1986 
1987 	mg_websocket_client_write(ws_client1_conn,
1988 	                          MG_WEBSOCKET_OPCODE_TEXT,
1989 	                          "data2",
1990 	                          5);
1991 
1992 	wait_not_null(&(
1993 	    ws_client1_data.data)); /* Wait for the websocket acknowledge message */
1994 
1995 	ck_assert(ws_client1_data.closed == 0);
1996 	ck_assert(ws_client2_data.closed == 0);
1997 	ck_assert(ws_client2_data.data == NULL);
1998 	ck_assert(ws_client2_data.len == 0);
1999 	ck_assert(ws_client1_data.data != NULL);
2000 	ck_assert(ws_client1_data.len == 4);
2001 	ck_assert(!memcmp(ws_client1_data.data, "ok 2", 4));
2002 	free(ws_client1_data.data);
2003 	ws_client1_data.data = NULL;
2004 	ws_client1_data.len = 0;
2005 
2006 	mg_websocket_client_write(ws_client1_conn,
2007 	                          MG_WEBSOCKET_OPCODE_TEXT,
2008 	                          "bye",
2009 	                          3);
2010 
2011 	wait_not_null(
2012 	    &(ws_client1_data.data)); /* Wait for the websocket goodbye message */
2013 
2014 	ck_assert(ws_client1_data.closed == 0);
2015 	ck_assert(ws_client2_data.closed == 0);
2016 	ck_assert(ws_client2_data.data == NULL);
2017 	ck_assert(ws_client2_data.len == 0);
2018 	ck_assert(ws_client1_data.data != NULL);
2019 	ck_assert(ws_client1_data.len == websocket_goodbye_msg_len);
2020 	ck_assert(!memcmp(ws_client1_data.data,
2021 	                  websocket_goodbye_msg,
2022 	                  websocket_goodbye_msg_len));
2023 	free(ws_client1_data.data);
2024 	ws_client1_data.data = NULL;
2025 	ws_client1_data.len = 0;
2026 
2027 	ck_assert(ws_client1_data.closed == 0); /* Not closed */
2028 
2029 	mg_close_connection(ws_client1_conn);
2030 
2031 	test_sleep(3); /* Won't get any message */
2032 
2033 	ck_assert(ws_client1_data.closed == 1); /* Closed */
2034 
2035 	ck_assert(ws_client2_data.closed == 0);
2036 	ck_assert(ws_client1_data.data == NULL);
2037 	ck_assert(ws_client1_data.len == 0);
2038 	ck_assert(ws_client2_data.data == NULL);
2039 	ck_assert(ws_client2_data.len == 0);
2040 
2041 	mg_websocket_client_write(ws_client2_conn,
2042 	                          MG_WEBSOCKET_OPCODE_TEXT,
2043 	                          "bye",
2044 	                          3);
2045 
2046 	wait_not_null(
2047 	    &(ws_client2_data.data)); /* Wait for the websocket goodbye message */
2048 
2049 	ck_assert(ws_client1_data.closed == 1);
2050 	ck_assert(ws_client2_data.closed == 0);
2051 	ck_assert(ws_client1_data.data == NULL);
2052 	ck_assert(ws_client1_data.len == 0);
2053 	ck_assert(ws_client2_data.data != NULL);
2054 	ck_assert(ws_client2_data.len == websocket_goodbye_msg_len);
2055 	ck_assert(!memcmp(ws_client2_data.data,
2056 	                  websocket_goodbye_msg,
2057 	                  websocket_goodbye_msg_len));
2058 	free(ws_client2_data.data);
2059 	ws_client2_data.data = NULL;
2060 	ws_client2_data.len = 0;
2061 
2062 	mg_close_connection(ws_client2_conn);
2063 
2064 	test_sleep(3); /* Won't get any message */
2065 
2066 	ck_assert(ws_client1_data.closed == 1);
2067 	ck_assert(ws_client2_data.closed == 1);
2068 	ck_assert(ws_client1_data.data == NULL);
2069 	ck_assert(ws_client1_data.len == 0);
2070 	ck_assert(ws_client2_data.data == NULL);
2071 	ck_assert(ws_client2_data.len == 0);
2072 
2073 	/* Connect client 3 */
2074 	ws_client3_conn =
2075 	    mg_connect_websocket_client("localhost",
2076 #if defined(NO_SSL)
2077 	                                ipv4_port,
2078 	                                0,
2079 #else
2080 	                                ipv4s_port,
2081 	                                1,
2082 #endif
2083 	                                ebuf,
2084 	                                sizeof(ebuf),
2085 	                                "/websocket",
2086 	                                NULL,
2087 	                                websocket_client_data_handler,
2088 	                                websocket_client_close_handler,
2089 	                                &ws_client3_data);
2090 
2091 	ck_assert(ws_client3_conn != NULL);
2092 
2093 	wait_not_null(
2094 	    &(ws_client3_data.data)); /* Wait for the websocket welcome message */
2095 	ck_assert(ws_client1_data.closed == 1);
2096 	ck_assert(ws_client2_data.closed == 1);
2097 	ck_assert(ws_client3_data.closed == 0);
2098 	ck_assert(ws_client1_data.data == NULL);
2099 	ck_assert(ws_client1_data.len == 0);
2100 	ck_assert(ws_client2_data.data == NULL);
2101 	ck_assert(ws_client2_data.len == 0);
2102 	ck_assert(ws_client3_data.data != NULL);
2103 	ck_assert(ws_client3_data.len == websocket_welcome_msg_len);
2104 	ck_assert(!memcmp(ws_client3_data.data,
2105 	                  websocket_welcome_msg,
2106 	                  websocket_welcome_msg_len));
2107 	free(ws_client3_data.data);
2108 	ws_client3_data.data = NULL;
2109 	ws_client3_data.len = 0;
2110 
2111 	/* Write long data (16 bit size header) */
2112 	mg_websocket_client_write(ws_client3_conn,
2113 	                          MG_WEBSOCKET_OPCODE_BINARY,
2114 	                          long_ws_buf,
2115 	                          long_ws_buf_len_16);
2116 
2117 	/* Wait for the response */
2118 	wait_not_null(&(ws_client3_data.data));
2119 
2120 	ck_assert_int_eq((int)ws_client3_data.len, (int)long_ws_buf_len_16);
2121 	ck_assert(!memcmp(ws_client3_data.data, long_ws_buf, long_ws_buf_len_16));
2122 	free(ws_client3_data.data);
2123 	ws_client3_data.data = NULL;
2124 	ws_client3_data.len = 0;
2125 
2126 	/* Write long data (64 bit size header) */
2127 	mg_websocket_client_write(ws_client3_conn,
2128 	                          MG_WEBSOCKET_OPCODE_BINARY,
2129 	                          long_ws_buf,
2130 	                          long_ws_buf_len_64);
2131 
2132 	/* Wait for the response */
2133 	wait_not_null(&(ws_client3_data.data));
2134 
2135 	ck_assert_int_eq((int)ws_client3_data.len, (int)long_ws_buf_len_64);
2136 	ck_assert(!memcmp(ws_client3_data.data, long_ws_buf, long_ws_buf_len_64));
2137 	free(ws_client3_data.data);
2138 	ws_client3_data.data = NULL;
2139 	ws_client3_data.len = 0;
2140 
2141 	/* Disconnect client 3 */
2142 	ck_assert(ws_client3_data.closed == 0);
2143 	mg_close_connection(ws_client3_conn);
2144 	ck_assert(ws_client3_data.closed == 1);
2145 
2146 	/* Connect client 4 */
2147 	ws_client4_conn =
2148 	    mg_connect_websocket_client("localhost",
2149 #if defined(NO_SSL)
2150 	                                ipv4_port,
2151 	                                0,
2152 #else
2153 	                                ipv4s_port,
2154 	                                1,
2155 #endif
2156 	                                ebuf,
2157 	                                sizeof(ebuf),
2158 	                                "/websocket",
2159 	                                NULL,
2160 	                                websocket_client_data_handler,
2161 	                                websocket_client_close_handler,
2162 	                                &ws_client4_data);
2163 
2164 	ck_assert(ws_client4_conn != NULL);
2165 
2166 	wait_not_null(
2167 	    &(ws_client4_data.data)); /* Wait for the websocket welcome message */
2168 	ck_assert(ws_client1_data.closed == 1);
2169 	ck_assert(ws_client2_data.closed == 1);
2170 	ck_assert(ws_client3_data.closed == 1);
2171 	ck_assert(ws_client4_data.closed == 0);
2172 	ck_assert(ws_client4_data.data != NULL);
2173 	ck_assert(ws_client4_data.len == websocket_welcome_msg_len);
2174 	ck_assert(!memcmp(ws_client4_data.data,
2175 	                  websocket_welcome_msg,
2176 	                  websocket_welcome_msg_len));
2177 	free(ws_client4_data.data);
2178 	ws_client4_data.data = NULL;
2179 	ws_client4_data.len = 0;
2180 
2181 	/* stop the server without closing this connection */
2182 
2183 #endif
2184 
2185 	/* Close the server */
2186 	g_ctx = NULL;
2187 	test_mg_stop(ctx);
2188 	mark_point();
2189 
2190 #ifdef USE_WEBSOCKET
2191 	for (i = 0; i < 100; i++) {
2192 		test_sleep(1);
2193 		if (ws_client3_data.closed != 0) {
2194 			mark_point();
2195 			break;
2196 		}
2197 	}
2198 
2199 	ck_assert_int_eq(ws_client4_data.closed, 1);
2200 
2201 	/* Free data in ws_client4_conn */
2202 	mg_close_connection(ws_client4_conn);
2203 
2204 #endif
2205 	mark_point();
2206 }
2207 END_TEST
2208 
2209 
2210 static int g_field_found_return = -999;
2211 
2212 static int
field_found(const char * key,const char * filename,char * path,size_t pathlen,void * user_data)2213 field_found(const char *key,
2214             const char *filename,
2215             char *path,
2216             size_t pathlen,
2217             void *user_data)
2218 {
2219 	ck_assert_ptr_ne(key, NULL);
2220 	ck_assert_ptr_ne(filename, NULL);
2221 	ck_assert_ptr_ne(path, NULL);
2222 	ck_assert_uint_gt(pathlen, 128);
2223 	ck_assert_ptr_eq(user_data, (void *)&g_field_found_return);
2224 
2225 	ck_assert((g_field_found_return == MG_FORM_FIELD_STORAGE_GET)
2226 	          || (g_field_found_return == MG_FORM_FIELD_STORAGE_STORE)
2227 	          || (g_field_found_return == MG_FORM_FIELD_STORAGE_SKIP)
2228 	          || (g_field_found_return == MG_FORM_FIELD_STORAGE_ABORT));
2229 
2230 	ck_assert_str_ne(key, "dontread");
2231 
2232 	if (!strcmp(key, "break_field_handler")) {
2233 		return MG_FORM_FIELD_STORAGE_ABORT;
2234 	}
2235 	if (!strcmp(key, "continue_field_handler")) {
2236 		return MG_FORM_FIELD_STORAGE_SKIP;
2237 	}
2238 
2239 	if (g_field_found_return == MG_FORM_FIELD_STORAGE_STORE) {
2240 		strncpy(path, key, pathlen - 8);
2241 		strcat(path, ".txt");
2242 	}
2243 
2244 	mark_point();
2245 
2246 	return g_field_found_return;
2247 }
2248 
2249 
2250 static int g_field_step;
2251 
2252 static int
field_get(const char * key,const char * value_untruncated,size_t valuelen,void * user_data)2253 field_get(const char *key,
2254           const char *value_untruncated,
2255           size_t valuelen,
2256           void *user_data)
2257 {
2258 	/* Copy the untruncated value, so string compare functions can be used. */
2259 	/* The check unit test library does not have build in memcmp functions. */
2260 	char *value = (char *)malloc(valuelen + 1);
2261 	ck_assert(value != NULL);
2262 	memcpy(value, value_untruncated, valuelen);
2263 	value[valuelen] = 0;
2264 
2265 	ck_assert_ptr_eq(user_data, (void *)&g_field_found_return);
2266 	ck_assert_int_ge(g_field_step, 0);
2267 
2268 	++g_field_step;
2269 	switch (g_field_step) {
2270 	case 1:
2271 		ck_assert_str_eq(key, "textin");
2272 		ck_assert_uint_eq(valuelen, 4);
2273 		ck_assert_str_eq(value, "text");
2274 		break;
2275 	case 2:
2276 		ck_assert_str_eq(key, "passwordin");
2277 		ck_assert_uint_eq(valuelen, 0);
2278 		ck_assert_str_eq(value, "");
2279 		break;
2280 	case 3:
2281 		ck_assert_str_eq(key, "radio1");
2282 		ck_assert_uint_eq(valuelen, 4);
2283 		ck_assert_str_eq(value, "val1");
2284 		break;
2285 	case 4:
2286 		ck_assert_str_eq(key, "radio2");
2287 		ck_assert_uint_eq(valuelen, 4);
2288 		ck_assert_str_eq(value, "val1");
2289 		break;
2290 	case 5:
2291 		ck_assert_str_eq(key, "check1");
2292 		ck_assert_uint_eq(valuelen, 4);
2293 		ck_assert_str_eq(value, "val1");
2294 		break;
2295 	case 6:
2296 		ck_assert_str_eq(key, "numberin");
2297 		ck_assert_uint_eq(valuelen, 1);
2298 		ck_assert_str_eq(value, "1");
2299 		break;
2300 	case 7:
2301 		ck_assert_str_eq(key, "datein");
2302 		ck_assert_uint_eq(valuelen, 8);
2303 		ck_assert_str_eq(value, "1.1.2016");
2304 		break;
2305 	case 8:
2306 		ck_assert_str_eq(key, "colorin");
2307 		ck_assert_uint_eq(valuelen, 7);
2308 		ck_assert_str_eq(value, "#80ff00");
2309 		break;
2310 	case 9:
2311 		ck_assert_str_eq(key, "rangein");
2312 		ck_assert_uint_eq(valuelen, 1);
2313 		ck_assert_str_eq(value, "3");
2314 		break;
2315 	case 10:
2316 		ck_assert_str_eq(key, "monthin");
2317 		ck_assert_uint_eq(valuelen, 0);
2318 		ck_assert_str_eq(value, "");
2319 		break;
2320 	case 11:
2321 		ck_assert_str_eq(key, "weekin");
2322 		ck_assert_uint_eq(valuelen, 0);
2323 		ck_assert_str_eq(value, "");
2324 		break;
2325 	case 12:
2326 		ck_assert_str_eq(key, "timein");
2327 		ck_assert_uint_eq(valuelen, 0);
2328 		ck_assert_str_eq(value, "");
2329 		break;
2330 	case 13:
2331 		ck_assert_str_eq(key, "datetimen");
2332 		ck_assert_uint_eq(valuelen, 0);
2333 		ck_assert_str_eq(value, "");
2334 		break;
2335 	case 14:
2336 		ck_assert_str_eq(key, "datetimelocalin");
2337 		ck_assert_uint_eq(valuelen, 0);
2338 		ck_assert_str_eq(value, "");
2339 		break;
2340 	case 15:
2341 		ck_assert_str_eq(key, "emailin");
2342 		ck_assert_uint_eq(valuelen, 0);
2343 		ck_assert_str_eq(value, "");
2344 		break;
2345 	case 16:
2346 		ck_assert_str_eq(key, "searchin");
2347 		ck_assert_uint_eq(valuelen, 0);
2348 		ck_assert_str_eq(value, "");
2349 		break;
2350 	case 17:
2351 		ck_assert_str_eq(key, "telin");
2352 		ck_assert_uint_eq(valuelen, 0);
2353 		ck_assert_str_eq(value, "");
2354 		break;
2355 	case 18:
2356 		ck_assert_str_eq(key, "urlin");
2357 		ck_assert_uint_eq(valuelen, 0);
2358 		ck_assert_str_eq(value, "");
2359 		break;
2360 	case 19:
2361 		ck_assert_str_eq(key, "filein");
2362 		ck_assert_uint_eq(valuelen, 0);
2363 		ck_assert_str_eq(value, "");
2364 		break;
2365 	case 20:
2366 		ck_assert_str_eq(key, "filesin");
2367 		ck_assert_uint_eq(valuelen, 0);
2368 		ck_assert_str_eq(value, "");
2369 		break;
2370 	case 21:
2371 		ck_assert_str_eq(key, "selectin");
2372 		ck_assert_uint_eq(valuelen, 4);
2373 		ck_assert_str_eq(value, "opt1");
2374 		break;
2375 	case 22:
2376 		ck_assert_str_eq(key, "message");
2377 		ck_assert_uint_eq(valuelen, 23);
2378 		ck_assert_str_eq(value, "Text area default text.");
2379 		break;
2380 	default:
2381 		ck_abort_msg("field_get called with g_field_step == %i",
2382 		             (int)g_field_step);
2383 	}
2384 
2385 	free(value);
2386 	mark_point();
2387 
2388 	return 0;
2389 }
2390 
2391 
2392 static const char *myfile_content = "Content of myfile.txt\r\n";
2393 static const int myfile_content_rep = 500;
2394 static int myfile_content_len = 23; /* (int)strlen(myfile_content); */
2395 
2396 
2397 static int
field_store(const char * path,long long file_size,void * user_data)2398 field_store(const char *path, long long file_size, void *user_data)
2399 {
2400 	FILE *f;
2401 
2402 	ck_assert_int_eq(myfile_content_len, 23);
2403 	ck_assert_int_eq(myfile_content_len, (int)strlen(myfile_content));
2404 
2405 	ck_assert_ptr_eq(user_data, (void *)&g_field_found_return);
2406 	ck_assert_int_ge(g_field_step, 100);
2407 
2408 	++g_field_step;
2409 	switch (g_field_step) {
2410 	case 101:
2411 		ck_assert_str_eq(path, "storeme.txt");
2412 		ck_assert_int_eq(file_size, 9);
2413 		f = fopen(path, "r");
2414 		ck_assert_ptr_ne(f, NULL);
2415 		if (f) {
2416 			char buf[32] = {0};
2417 			int i = (int)fread(buf, 1, sizeof(buf) - 1, f);
2418 			ck_assert_int_eq(i, 9);
2419 			fclose(f);
2420 			ck_assert_str_eq(buf, "storetest");
2421 		}
2422 		break;
2423 	case 102:
2424 		ck_assert_str_eq(path, "file2store.txt");
2425 		ck_assert_int_eq(myfile_content_len, (int)strlen(myfile_content));
2426 		ck_assert_int_eq(file_size, myfile_content_len * myfile_content_rep);
2427 #ifdef _WIN32
2428 		f = fopen(path, "rb");
2429 #else
2430 		f = fopen(path, "r");
2431 #endif
2432 		ck_assert_ptr_ne(f, NULL);
2433 		if (f) {
2434 			char buf[32] = {0};
2435 			int r, i;
2436 			for (r = 0; r < myfile_content_rep; r++) {
2437 				i = (int)fread(buf, 1, myfile_content_len, f);
2438 				ck_assert_int_eq(i, myfile_content_len);
2439 				ck_assert_str_eq(buf, myfile_content);
2440 			}
2441 			i = (int)fread(buf, 1, myfile_content_len, f);
2442 			ck_assert_int_eq(i, 0);
2443 			fclose(f);
2444 		}
2445 		break;
2446 	default:
2447 		ck_abort_msg("field_get called with g_field_step == %i",
2448 		             (int)g_field_step);
2449 	}
2450 	mark_point();
2451 
2452 	return 0;
2453 }
2454 
2455 
2456 static int
FormGet(struct mg_connection * conn,void * cbdata)2457 FormGet(struct mg_connection *conn, void *cbdata)
2458 {
2459 	const struct mg_request_info *req_info = mg_get_request_info(conn);
2460 	int ret;
2461 	struct mg_form_data_handler fdh = {field_found, field_get, NULL, NULL};
2462 
2463 	(void)cbdata;
2464 
2465 	ck_assert(req_info != NULL);
2466 
2467 	mg_printf(conn, "HTTP/1.0 200 OK\r\nContent-Type: text/plain\r\n\r\n");
2468 	fdh.user_data = (void *)&g_field_found_return;
2469 
2470 	/* Call the form handler */
2471 	g_field_step = 0;
2472 	g_field_found_return = MG_FORM_FIELD_STORAGE_GET;
2473 	ret = mg_handle_form_request(conn, &fdh);
2474 	g_field_found_return = -888;
2475 	ck_assert_int_eq(ret, 22);
2476 	ck_assert_int_eq(g_field_step, 22);
2477 	mg_printf(conn, "%i\r\n", ret);
2478 	g_field_step = 1000;
2479 
2480 	mark_point();
2481 
2482 	return 1;
2483 }
2484 
2485 
2486 static int
FormStore(struct mg_connection * conn,void * cbdata,int ret_expected,int field_step_expected)2487 FormStore(struct mg_connection *conn,
2488           void *cbdata,
2489           int ret_expected,
2490           int field_step_expected)
2491 {
2492 	const struct mg_request_info *req_info = mg_get_request_info(conn);
2493 	int ret;
2494 	struct mg_form_data_handler fdh = {field_found,
2495 	                                   field_get,
2496 	                                   field_store,
2497 	                                   NULL};
2498 
2499 	(void)cbdata;
2500 
2501 	ck_assert(req_info != NULL);
2502 
2503 	mg_printf(conn, "HTTP/1.0 200 OK\r\nContent-Type: text/plain\r\n\r\n");
2504 	fdh.user_data = (void *)&g_field_found_return;
2505 
2506 	/* Call the form handler */
2507 	g_field_step = 100;
2508 	g_field_found_return = MG_FORM_FIELD_STORAGE_STORE;
2509 	ret = mg_handle_form_request(conn, &fdh);
2510 	ck_assert_int_eq(ret, ret_expected);
2511 	ck_assert_int_eq(g_field_step, field_step_expected);
2512 	mg_printf(conn, "%i\r\n", ret);
2513 	g_field_step = 1000;
2514 
2515 	mark_point();
2516 
2517 	return 1;
2518 }
2519 
2520 
2521 static int
FormStore1(struct mg_connection * conn,void * cbdata)2522 FormStore1(struct mg_connection *conn, void *cbdata)
2523 {
2524 	mark_point();
2525 	return FormStore(conn, cbdata, 3, 101);
2526 }
2527 
2528 
2529 static int
FormStore2(struct mg_connection * conn,void * cbdata)2530 FormStore2(struct mg_connection *conn, void *cbdata)
2531 {
2532 	mark_point();
2533 	return FormStore(conn, cbdata, 4, 102);
2534 }
2535 
2536 
2537 static void
send_chunk_stringl(struct mg_connection * conn,const char * chunk,unsigned int chunk_len)2538 send_chunk_stringl(struct mg_connection *conn,
2539                    const char *chunk,
2540                    unsigned int chunk_len)
2541 {
2542 	char lenbuf[16];
2543 	size_t lenbuf_len;
2544 	int ret;
2545 
2546 	mark_point();
2547 
2548 	/* First store the length information in a text buffer. */
2549 	sprintf(lenbuf, "%x\r\n", chunk_len);
2550 	lenbuf_len = strlen(lenbuf);
2551 
2552 	/* Then send length information, chunk and terminating \r\n. */
2553 	ret = mg_write(conn, lenbuf, lenbuf_len);
2554 	ck_assert_int_eq(ret, (int)lenbuf_len);
2555 
2556 	ret = mg_write(conn, chunk, chunk_len);
2557 	ck_assert_int_eq(ret, (int)chunk_len);
2558 
2559 	ret = mg_write(conn, "\r\n", 2);
2560 	ck_assert_int_eq(ret, 2);
2561 }
2562 
2563 
2564 static void
send_chunk_string(struct mg_connection * conn,const char * chunk)2565 send_chunk_string(struct mg_connection *conn, const char *chunk)
2566 {
2567 	mark_point();
2568 	send_chunk_stringl(conn, chunk, (unsigned int)strlen(chunk));
2569 	mark_point();
2570 }
2571 
2572 
START_TEST(test_handle_form)2573 START_TEST(test_handle_form)
2574 {
2575 	struct mg_context *ctx;
2576 	struct mg_connection *client_conn;
2577 	const struct mg_response_info *client_ri;
2578 	const char *OPTIONS[8];
2579 	const char *opt;
2580 	int opt_idx = 0;
2581 	char ebuf[1024];
2582 	const char *multipart_body;
2583 	const char *boundary;
2584 	size_t body_len, body_sent, chunk_len, bound_len;
2585 	int sleep_cnt;
2586 
2587 	mark_point();
2588 
2589 	memset((void *)OPTIONS, 0, sizeof(OPTIONS));
2590 	OPTIONS[opt_idx++] = "listening_ports";
2591 	OPTIONS[opt_idx++] = "8884";
2592 	ck_assert_int_le(opt_idx, (int)(sizeof(OPTIONS) / sizeof(OPTIONS[0])));
2593 	ck_assert(OPTIONS[sizeof(OPTIONS) / sizeof(OPTIONS[0]) - 1] == NULL);
2594 	ck_assert(OPTIONS[sizeof(OPTIONS) / sizeof(OPTIONS[0]) - 2] == NULL);
2595 
2596 	ctx = test_mg_start(NULL, &g_ctx, OPTIONS);
2597 
2598 	ck_assert(ctx != NULL);
2599 	g_ctx = ctx;
2600 
2601 	opt = mg_get_option(ctx, "listening_ports");
2602 	ck_assert_str_eq(opt, "8884");
2603 
2604 	mg_set_request_handler(ctx, "/handle_form", FormGet, NULL);
2605 	mg_set_request_handler(ctx, "/handle_form_store", FormStore1, NULL);
2606 	mg_set_request_handler(ctx, "/handle_form_store2", FormStore2, NULL);
2607 
2608 	test_sleep(1);
2609 
2610 	/* Handle form: "GET" */
2611 	client_conn = mg_download("localhost",
2612 	                          8884,
2613 	                          0,
2614 	                          ebuf,
2615 	                          sizeof(ebuf),
2616 	                          "%s",
2617 	                          "GET /handle_form"
2618 	                          "?textin=text&passwordin=&radio1=val1"
2619 	                          "&radio2=val1&check1=val1&numberin=1"
2620 	                          "&datein=1.1.2016&colorin=%2380ff00"
2621 	                          "&rangein=3&monthin=&weekin=&timein="
2622 	                          "&datetimen=&datetimelocalin=&emailin="
2623 	                          "&searchin=&telin=&urlin=&filein="
2624 	                          "&filesin=&selectin=opt1"
2625 	                          "&message=Text+area+default+text. "
2626 	                          "HTTP/1.0\r\n"
2627 	                          "Host: localhost:8884\r\n"
2628 	                          "Connection: close\r\n\r\n");
2629 	ck_assert(client_conn != NULL);
2630 	for (sleep_cnt = 0; sleep_cnt < 30; sleep_cnt++) {
2631 		test_sleep(1);
2632 		if (g_field_step == 1000) {
2633 			break;
2634 		}
2635 	}
2636 	client_ri = mg_get_response_info(client_conn);
2637 
2638 	ck_assert(client_ri != NULL);
2639 	ck_assert_int_eq(client_ri->status_code, 200);
2640 	mg_close_connection(client_conn);
2641 
2642 	/* Handle form: "POST x-www-form-urlencoded" */
2643 	client_conn =
2644 	    mg_download("localhost",
2645 	                8884,
2646 	                0,
2647 	                ebuf,
2648 	                sizeof(ebuf),
2649 	                "%s",
2650 	                "POST /handle_form HTTP/1.1\r\n"
2651 	                "Host: localhost:8884\r\n"
2652 	                "Connection: close\r\n"
2653 	                "Content-Type: application/x-www-form-urlencoded\r\n"
2654 	                "Content-Length: 263\r\n"
2655 	                "\r\n"
2656 	                "textin=text&passwordin=&radio1=val1&radio2=val1"
2657 	                "&check1=val1&numberin=1&datein=1.1.2016"
2658 	                "&colorin=%2380ff00&rangein=3&monthin=&weekin="
2659 	                "&timein=&datetimen=&datetimelocalin=&emailin="
2660 	                "&searchin=&telin=&urlin=&filein=&filesin="
2661 	                "&selectin=opt1&message=Text+area+default+text.");
2662 	ck_assert(client_conn != NULL);
2663 	for (sleep_cnt = 0; sleep_cnt < 30; sleep_cnt++) {
2664 		test_sleep(1);
2665 		if (g_field_step == 1000) {
2666 			break;
2667 		}
2668 	}
2669 	client_ri = mg_get_response_info(client_conn);
2670 
2671 	ck_assert(client_ri != NULL);
2672 	ck_assert_int_eq(client_ri->status_code, 200);
2673 	mg_close_connection(client_conn);
2674 
2675 	/* Handle form: "POST multipart/form-data" */
2676 	multipart_body =
2677 	    "--multipart-form-data-boundary--see-RFC-2388\r\n"
2678 	    "Content-Disposition: form-data; name=\"textin\"\r\n"
2679 	    "\r\n"
2680 	    "text\r\n"
2681 	    "--multipart-form-data-boundary--see-RFC-2388\r\n"
2682 	    "Content-Disposition: form-data; name=\"passwordin\"\r\n"
2683 	    "\r\n"
2684 	    "\r\n"
2685 	    "--multipart-form-data-boundary--see-RFC-2388\r\n"
2686 	    "Content-Disposition: form-data; name=\"radio1\"\r\n"
2687 	    "\r\n"
2688 	    "val1\r\n"
2689 	    "--multipart-form-data-boundary--see-RFC-2388\r\n"
2690 	    "Content-Disposition: form-data; name=radio2\r\n"
2691 	    "\r\n"
2692 	    "val1\r\n"
2693 	    "--multipart-form-data-boundary--see-RFC-2388\r\n"
2694 	    "Content-Disposition: form-data; name=\"check1\"\r\n"
2695 	    "\r\n"
2696 	    "val1\r\n"
2697 	    "--multipart-form-data-boundary--see-RFC-2388\r\n"
2698 	    "Content-Disposition: form-data; name=\"numberin\"\r\n"
2699 	    "\r\n"
2700 	    "1\r\n"
2701 	    "--multipart-form-data-boundary--see-RFC-2388\r\n"
2702 	    "Content-Disposition: form-data; name=\"datein\"\r\n"
2703 	    "\r\n"
2704 	    "1.1.2016\r\n"
2705 	    "--multipart-form-data-boundary--see-RFC-2388\r\n"
2706 	    "Content-Disposition: form-data; name=\"colorin\"\r\n"
2707 	    "\r\n"
2708 	    "#80ff00\r\n"
2709 	    "--multipart-form-data-boundary--see-RFC-2388\r\n"
2710 	    "Content-Disposition: form-data; name=\"rangein\"\r\n"
2711 	    "\r\n"
2712 	    "3\r\n"
2713 	    "--multipart-form-data-boundary--see-RFC-2388\r\n"
2714 	    "Content-Disposition: form-data; name=\"monthin\"\r\n"
2715 	    "\r\n"
2716 	    "\r\n"
2717 	    "--multipart-form-data-boundary--see-RFC-2388\r\n"
2718 	    "Content-Disposition: form-data; name=\"weekin\"\r\n"
2719 	    "\r\n"
2720 	    "\r\n"
2721 	    "--multipart-form-data-boundary--see-RFC-2388\r\n"
2722 	    "Content-Disposition: form-data; name=\"timein\"\r\n"
2723 	    "\r\n"
2724 	    "\r\n"
2725 	    "--multipart-form-data-boundary--see-RFC-2388\r\n"
2726 	    "Content-Disposition: form-data; name=\"datetimen\"\r\n"
2727 	    "\r\n"
2728 	    "\r\n"
2729 	    "--multipart-form-data-boundary--see-RFC-2388\r\n"
2730 	    "Content-Disposition: form-data; name=\"datetimelocalin\"\r\n"
2731 	    "\r\n"
2732 	    "\r\n"
2733 	    "--multipart-form-data-boundary--see-RFC-2388\r\n"
2734 	    "Content-Disposition: form-data; name=\"emailin\"\r\n"
2735 	    "\r\n"
2736 	    "\r\n"
2737 	    "--multipart-form-data-boundary--see-RFC-2388\r\n"
2738 	    "Content-Disposition: form-data; name=\"searchin\"\r\n"
2739 	    "\r\n"
2740 	    "\r\n"
2741 	    "--multipart-form-data-boundary--see-RFC-2388\r\n"
2742 	    "Content-Disposition: form-data; name=\"telin\"\r\n"
2743 	    "\r\n"
2744 	    "\r\n"
2745 	    "--multipart-form-data-boundary--see-RFC-2388\r\n"
2746 	    "Content-Disposition: form-data; name=\"urlin\"\r\n"
2747 	    "\r\n"
2748 	    "\r\n"
2749 	    "--multipart-form-data-boundary--see-RFC-2388\r\n"
2750 	    "Content-Disposition: form-data; name=\"filein\"; filename=\"\"\r\n"
2751 	    "Content-Type: application/octet-stream\r\n"
2752 	    "\r\n"
2753 	    "\r\n"
2754 	    "--multipart-form-data-boundary--see-RFC-2388\r\n"
2755 	    "Content-Disposition: form-data; name=filesin; filename=\r\n"
2756 	    "Content-Type: application/octet-stream\r\n"
2757 	    "\r\n"
2758 	    "\r\n"
2759 	    "--multipart-form-data-boundary--see-RFC-2388\r\n"
2760 	    "Content-Disposition: form-data; name=\"selectin\"\r\n"
2761 	    "\r\n"
2762 	    "opt1\r\n"
2763 	    "--multipart-form-data-boundary--see-RFC-2388\r\n"
2764 	    "Content-Disposition: form-data; name=\"message\"\r\n"
2765 	    "\r\n"
2766 	    "Text area default text.\r\n"
2767 	    "--multipart-form-data-boundary--see-RFC-2388--\r\n";
2768 	body_len = strlen(multipart_body);
2769 	ck_assert_uint_eq(body_len, 2368); /* not required */
2770 
2771 	client_conn =
2772 	    mg_download("localhost",
2773 	                8884,
2774 	                0,
2775 	                ebuf,
2776 	                sizeof(ebuf),
2777 	                "POST /handle_form HTTP/1.1\r\n"
2778 	                "Host: localhost:8884\r\n"
2779 	                "Connection: close\r\n"
2780 	                "Content-Type: multipart/form-data; "
2781 	                "boundary=multipart-form-data-boundary--see-RFC-2388\r\n"
2782 	                "Content-Length: %u\r\n"
2783 	                "\r\n%s",
2784 	                (unsigned int)body_len,
2785 	                multipart_body);
2786 
2787 	ck_assert(client_conn != NULL);
2788 	for (sleep_cnt = 0; sleep_cnt < 30; sleep_cnt++) {
2789 		test_sleep(1);
2790 		if (g_field_step == 1000) {
2791 			break;
2792 		}
2793 	}
2794 	client_ri = mg_get_response_info(client_conn);
2795 
2796 	ck_assert(client_ri != NULL);
2797 	ck_assert_int_eq(client_ri->status_code, 200);
2798 	mg_close_connection(client_conn);
2799 
2800 
2801 	/* Handle form: "POST multipart/form-data" with chunked transfer encoding */
2802 	client_conn =
2803 	    mg_download("localhost",
2804 	                8884,
2805 	                0,
2806 	                ebuf,
2807 	                sizeof(ebuf),
2808 	                "%s",
2809 	                "POST /handle_form HTTP/1.1\r\n"
2810 	                "Host: localhost:8884\r\n"
2811 	                "Connection: close\r\n"
2812 	                "Content-Type: multipart/form-data; "
2813 	                "boundary=multipart-form-data-boundary--see-RFC-2388\r\n"
2814 	                "Transfer-Encoding: chunked\r\n"
2815 	                "\r\n");
2816 
2817 	ck_assert(client_conn != NULL);
2818 
2819 	body_len = strlen(multipart_body);
2820 	chunk_len = 1;
2821 	body_sent = 0;
2822 	while (body_len > body_sent) {
2823 		if (chunk_len > (body_len - body_sent)) {
2824 			chunk_len = body_len - body_sent;
2825 		}
2826 		ck_assert_int_gt((int)chunk_len, 0);
2827 		mg_printf(client_conn, "%x\r\n", (unsigned int)chunk_len);
2828 		mg_write(client_conn, multipart_body + body_sent, chunk_len);
2829 		mg_printf(client_conn, "\r\n");
2830 		body_sent += chunk_len;
2831 		chunk_len = (chunk_len % 40) + 1;
2832 	}
2833 	mg_printf(client_conn, "0\r\n\r\n");
2834 
2835 	for (sleep_cnt = 0; sleep_cnt < 30; sleep_cnt++) {
2836 		test_sleep(1);
2837 		if (g_field_step == 1000) {
2838 			break;
2839 		}
2840 	}
2841 	client_ri = mg_get_response_info(client_conn);
2842 
2843 	ck_assert(client_ri != NULL);
2844 	ck_assert_int_eq(client_ri->status_code, 200);
2845 	mg_close_connection(client_conn);
2846 
2847 	/* Handle form: "POST multipart/form-data" with chunked transfer
2848 	 * encoding, using a quoted boundary string */
2849 	client_conn = mg_download(
2850 	    "localhost",
2851 	    8884,
2852 	    0,
2853 	    ebuf,
2854 	    sizeof(ebuf),
2855 	    "%s",
2856 	    "POST /handle_form HTTP/1.1\r\n"
2857 	    "Host: localhost:8884\r\n"
2858 	    "Connection: close\r\n"
2859 	    "Content-Type: multipart/form-data; "
2860 	    "boundary=\"multipart-form-data-boundary--see-RFC-2388\"\r\n"
2861 	    "Transfer-Encoding: chunked\r\n"
2862 	    "\r\n");
2863 
2864 	ck_assert(client_conn != NULL);
2865 
2866 	body_len = strlen(multipart_body);
2867 	chunk_len = 1;
2868 	body_sent = 0;
2869 	while (body_len > body_sent) {
2870 		if (chunk_len > (body_len - body_sent)) {
2871 			chunk_len = body_len - body_sent;
2872 		}
2873 		ck_assert_int_gt((int)chunk_len, 0);
2874 		mg_printf(client_conn, "%x\r\n", (unsigned int)chunk_len);
2875 		mg_write(client_conn, multipart_body + body_sent, chunk_len);
2876 		mg_printf(client_conn, "\r\n");
2877 		body_sent += chunk_len;
2878 		chunk_len = (chunk_len % 40) + 1;
2879 	}
2880 	mg_printf(client_conn, "0\r\n\r\n");
2881 
2882 	for (sleep_cnt = 0; sleep_cnt < 30; sleep_cnt++) {
2883 		test_sleep(1);
2884 		if (g_field_step == 1000) {
2885 			break;
2886 		}
2887 	}
2888 	client_ri = mg_get_response_info(client_conn);
2889 
2890 	ck_assert(client_ri != NULL);
2891 	ck_assert_int_eq(client_ri->status_code, 200);
2892 	mg_close_connection(client_conn);
2893 
2894 
2895 	/* Now test form_store */
2896 
2897 	/* First test with GET */
2898 	client_conn = mg_download("localhost",
2899 	                          8884,
2900 	                          0,
2901 	                          ebuf,
2902 	                          sizeof(ebuf),
2903 	                          "%s",
2904 	                          "GET /handle_form_store"
2905 	                          "?storeme=storetest"
2906 	                          "&continue_field_handler=ignore"
2907 	                          "&break_field_handler=abort"
2908 	                          "&dontread=xyz "
2909 	                          "HTTP/1.0\r\n"
2910 	                          "Host: localhost:8884\r\n"
2911 	                          "Connection: close\r\n\r\n");
2912 
2913 	ck_assert(client_conn != NULL);
2914 
2915 	for (sleep_cnt = 0; sleep_cnt < 30; sleep_cnt++) {
2916 		test_sleep(1);
2917 		if (g_field_step == 1000) {
2918 			break;
2919 		}
2920 	}
2921 	client_ri = mg_get_response_info(client_conn);
2922 
2923 	ck_assert(client_ri != NULL);
2924 	ck_assert_int_eq(client_ri->status_code, 200);
2925 	mg_close_connection(client_conn);
2926 
2927 
2928 	/* Handle form: "POST x-www-form-urlencoded", chunked, store */
2929 	client_conn =
2930 	    mg_download("localhost",
2931 	                8884,
2932 	                0,
2933 	                ebuf,
2934 	                sizeof(ebuf),
2935 	                "%s",
2936 	                "POST /handle_form_store HTTP/1.0\r\n"
2937 	                "Host: localhost:8884\r\n"
2938 	                "Connection: close\r\n"
2939 	                "Content-Type: application/x-www-form-urlencoded\r\n"
2940 	                "Transfer-Encoding: chunked\r\n"
2941 	                "\r\n");
2942 	ck_assert(client_conn != NULL);
2943 
2944 	send_chunk_string(client_conn, "storeme=store");
2945 	send_chunk_string(client_conn, "test&");
2946 	send_chunk_string(client_conn, "continue_field_handler=ignore");
2947 	send_chunk_string(client_conn, "&br");
2948 	test_sleep(1);
2949 	send_chunk_string(client_conn, "eak_field_handler=abort&");
2950 	send_chunk_string(client_conn, "dontread=xyz");
2951 	mg_printf(client_conn, "0\r\n\r\n");
2952 
2953 	for (sleep_cnt = 0; sleep_cnt < 30; sleep_cnt++) {
2954 		test_sleep(1);
2955 		if (g_field_step == 1000) {
2956 			break;
2957 		}
2958 	}
2959 	client_ri = mg_get_response_info(client_conn);
2960 
2961 	ck_assert(client_ri != NULL);
2962 	ck_assert_int_eq(client_ri->status_code, 200);
2963 	mg_close_connection(client_conn);
2964 
2965 	/* Handle form: "POST multipart/form-data", chunked, store */
2966 	client_conn =
2967 	    mg_download("localhost",
2968 	                8884,
2969 	                0,
2970 	                ebuf,
2971 	                sizeof(ebuf),
2972 	                "%s",
2973 	                "POST /handle_form_store HTTP/1.0\r\n"
2974 	                "Host: localhost:8884\r\n"
2975 	                "Connection: close\r\n"
2976 	                "Content-Type: multipart/form-data; "
2977 	                "boundary=multipart-form-data-boundary--see-RFC-2388\r\n"
2978 	                "Transfer-Encoding: chunked\r\n"
2979 	                "\r\n");
2980 	ck_assert(client_conn != NULL);
2981 
2982 	send_chunk_string(client_conn, "--multipart-form-data-boundary");
2983 	send_chunk_string(client_conn, "--see-RFC-2388\r\n");
2984 	send_chunk_string(client_conn, "Content-Disposition: form-data; ");
2985 	send_chunk_string(client_conn, "name=\"storeme\"\r\n");
2986 	send_chunk_string(client_conn, "\r\n");
2987 	send_chunk_string(client_conn, "storetest\r\n");
2988 
2989 	send_chunk_string(client_conn, "--multipart-form-data-boundary-");
2990 	send_chunk_string(client_conn, "-see-RFC-2388\r\n");
2991 	send_chunk_string(client_conn, "Content-Disposition: form-data; ");
2992 	send_chunk_string(client_conn, "name=\"continue_field_handler\"\r\n");
2993 	send_chunk_string(client_conn, "\r\n");
2994 	send_chunk_string(client_conn, "ignore\r\n");
2995 
2996 	send_chunk_string(client_conn, "--multipart-form-data-boundary-");
2997 	send_chunk_string(client_conn, "-see-RFC-2388\r\n");
2998 	send_chunk_string(client_conn, "Content-Disposition: form-data; ");
2999 	send_chunk_string(client_conn, "name=\"break_field_handler\"\r\n");
3000 	send_chunk_string(client_conn, "\r\n");
3001 	send_chunk_string(client_conn, "abort\r\n");
3002 
3003 	send_chunk_string(client_conn, "--multipart-form-data-boundary-");
3004 	send_chunk_string(client_conn, "-see-RFC-2388\r\n");
3005 	send_chunk_string(client_conn, "Content-Disposition: form-data; ");
3006 	send_chunk_string(client_conn, "name=\"dontread\"\r\n");
3007 	send_chunk_string(client_conn, "\r\n");
3008 	send_chunk_string(client_conn, "xyz\r\n");
3009 	send_chunk_string(client_conn, "--multipart-form-data-boundary");
3010 	send_chunk_string(client_conn, "--see-RFC-2388--\r\n");
3011 	mg_printf(client_conn, "0\r\n\r\n");
3012 
3013 	for (sleep_cnt = 0; sleep_cnt < 30; sleep_cnt++) {
3014 		test_sleep(1);
3015 		if (g_field_step == 1000) {
3016 			break;
3017 		}
3018 	}
3019 	client_ri = mg_get_response_info(client_conn);
3020 
3021 	ck_assert(client_ri != NULL);
3022 	ck_assert_int_eq(client_ri->status_code, 200);
3023 	mg_close_connection(client_conn);
3024 
3025 
3026 	/* Handle form: "POST multipart/form-data", chunked, store, with files */
3027 	client_conn =
3028 	    mg_download("localhost",
3029 	                8884,
3030 	                0,
3031 	                ebuf,
3032 	                sizeof(ebuf),
3033 	                "%s",
3034 	                "POST /handle_form_store2 HTTP/1.0\r\n"
3035 	                "Host: localhost:8884\r\n"
3036 	                "Connection: close\r\n"
3037 	                "Content-Type: multipart/form-data; "
3038 	                "boundary=multipart-form-data-boundary--see-RFC-2388\r\n"
3039 	                "Transfer-Encoding: chunked\r\n"
3040 	                "\r\n");
3041 	ck_assert(client_conn != NULL);
3042 
3043 	boundary = "--multipart-form-data-boundary--see-RFC-2388\r\n";
3044 
3045 	send_chunk_string(client_conn, boundary);
3046 	send_chunk_string(client_conn, "Content-Disposition: form-data; ");
3047 	send_chunk_string(client_conn, "name=\"storeme\"\r\n");
3048 	send_chunk_string(client_conn, "\r\n");
3049 	send_chunk_string(client_conn, "storetest\r\n");
3050 
3051 	send_chunk_string(client_conn, boundary);
3052 	send_chunk_string(client_conn, "Content-Disposition: form-data; ");
3053 	send_chunk_string(client_conn, "name=\"continue_field_handler\";");
3054 	send_chunk_string(client_conn, "filename=\"file_ignored.txt\"\r\n");
3055 	send_chunk_string(client_conn, "Content-Type: ");
3056 	send_chunk_string(client_conn, "application/octet-stream\r\n");
3057 	send_chunk_string(client_conn, "X-Ignored-Header: xyz\r\n");
3058 	send_chunk_string(client_conn, "\r\n");
3059 
3060 	/* send some kilobyte of data */
3061 	/* sending megabytes to localhost does not always work in CI test
3062 	 * environments (depending on the network stack) */
3063 	body_sent = 0;
3064 	bound_len = strlen(boundary);
3065 	do {
3066 		send_chunk_string(client_conn, "ignore\r\n");
3067 		body_sent += 8;
3068 		/* send some strings that are almost boundaries */
3069 		for (chunk_len = 1; chunk_len < bound_len; chunk_len++) {
3070 			/* chunks from 1 byte to strlen(boundary)-1 */
3071 			send_chunk_stringl(client_conn, boundary, (unsigned int)chunk_len);
3072 			body_sent += chunk_len;
3073 		}
3074 	} while (body_sent < 8 * 1024);
3075 	send_chunk_string(client_conn, "\r\n");
3076 
3077 	send_chunk_string(client_conn, boundary);
3078 	send_chunk_string(client_conn, "Content-Disposition: form-data; ");
3079 	send_chunk_string(client_conn, "name=\"file2store\";");
3080 	send_chunk_string(client_conn, "filename=\"myfile.txt\"\r\n");
3081 	send_chunk_string(client_conn, "Content-Type: ");
3082 	send_chunk_string(client_conn, "application/octet-stream\r\n");
3083 	send_chunk_string(client_conn, "X-Ignored-Header: xyz\r\n");
3084 	send_chunk_string(client_conn, "\r\n");
3085 	for (body_sent = 0; (int)body_sent < (int)myfile_content_rep; body_sent++) {
3086 		send_chunk_string(client_conn, myfile_content);
3087 	}
3088 	send_chunk_string(client_conn, "\r\n");
3089 
3090 	send_chunk_string(client_conn, boundary);
3091 	send_chunk_string(client_conn, "Content-Disposition: form-data; ");
3092 	send_chunk_string(client_conn, "name=\"break_field_handler\"\r\n");
3093 	send_chunk_string(client_conn, "\r\n");
3094 	send_chunk_string(client_conn, "abort\r\n");
3095 
3096 	send_chunk_string(client_conn, boundary);
3097 	send_chunk_string(client_conn, "Content-Disposition: form-data; ");
3098 	send_chunk_string(client_conn, "name=\"dontread\"\r\n");
3099 	send_chunk_string(client_conn, "\r\n");
3100 	send_chunk_string(client_conn, "xyz\r\n");
3101 	send_chunk_string(client_conn, "--multipart-form-data-boundary");
3102 	send_chunk_string(client_conn, "--see-RFC-2388--\r\n");
3103 	mg_printf(client_conn, "0\r\n\r\n");
3104 
3105 	for (sleep_cnt = 0; sleep_cnt < 30; sleep_cnt++) {
3106 		test_sleep(1);
3107 		if (g_field_step == 1000) {
3108 			break;
3109 		}
3110 	}
3111 	client_ri = mg_get_response_info(client_conn);
3112 
3113 	ck_assert(client_ri != NULL);
3114 	ck_assert_int_eq(client_ri->status_code, 200);
3115 	mg_close_connection(client_conn);
3116 
3117 
3118 	/* Close the server */
3119 	g_ctx = NULL;
3120 	test_mg_stop(ctx);
3121 	mark_point();
3122 }
3123 END_TEST
3124 
3125 
START_TEST(test_http_auth)3126 START_TEST(test_http_auth)
3127 {
3128 #if !defined(NO_FILES)
3129 	const char *OPTIONS[] = {
3130 		"document_root",
3131 		".",
3132 		"listening_ports",
3133 		"8080",
3134 #if !defined(NO_CACHING)
3135 		"static_file_max_age",
3136 		"0",
3137 #endif
3138 		"put_delete_auth_file",
3139 		"put_delete_auth_file.csv",
3140 		NULL,
3141 	};
3142 
3143 	struct mg_context *ctx;
3144 	struct mg_connection *client_conn;
3145 	char client_err[256], nonce[256];
3146 	const struct mg_response_info *client_ri;
3147 	int client_res;
3148 	FILE *f;
3149 	const char *passwd_file = ".htpasswd";
3150 	const char *test_file = "test_http_auth.test_file.txt";
3151 	const char *test_content = "test_http_auth test_file content";
3152 	const char *domain;
3153 	const char *doc_root;
3154 	const char *auth_request;
3155 	const char *str;
3156 	size_t len;
3157 	int i;
3158 	char HA1[256], HA2[256];
3159 	char HA1_md5_buf[33], HA2_md5_buf[33], HA_md5_buf[33];
3160 	char *HA1_md5_ret, *HA2_md5_ret, *HA_md5_ret;
3161 	const char *nc = "00000001";
3162 	const char *cnonce = "6789ABCD";
3163 
3164 	mark_point();
3165 
3166 	/* Start with default options */
3167 	ctx = test_mg_start(NULL, NULL, OPTIONS);
3168 
3169 	ck_assert(ctx != NULL);
3170 	domain = mg_get_option(ctx, "authentication_domain");
3171 	ck_assert(domain != NULL);
3172 	len = strlen(domain);
3173 	ck_assert_uint_gt(len, 0);
3174 	ck_assert_uint_lt(len, 64);
3175 	doc_root = mg_get_option(ctx, "document_root");
3176 	ck_assert_str_eq(doc_root, ".");
3177 
3178 	/* Create a default file in the document root */
3179 	f = fopen(test_file, "w");
3180 	if (f) {
3181 		fprintf(f, "%s", test_content);
3182 		fclose(f);
3183 	} else {
3184 		ck_abort_msg("Cannot create file %s", test_file);
3185 	}
3186 
3187 	(void)remove(passwd_file);
3188 	(void)remove("put_delete_auth_file.csv");
3189 
3190 	client_res = mg_modify_passwords_file("put_delete_auth_file.csv",
3191 	                                      domain,
3192 	                                      "admin",
3193 	                                      "adminpass");
3194 	ck_assert_int_eq(client_res, 1);
3195 
3196 	/* Read file before a .htpasswd file has been created */
3197 	memset(client_err, 0, sizeof(client_err));
3198 	client_conn =
3199 	    mg_connect_client("127.0.0.1", 8080, 0, client_err, sizeof(client_err));
3200 
3201 	ck_assert_str_eq(client_err, "");
3202 	ck_assert(client_conn != NULL);
3203 
3204 	mg_printf(client_conn, "GET /%s HTTP/1.0\r\n\r\n", test_file);
3205 	client_res =
3206 	    mg_get_response(client_conn, client_err, sizeof(client_err), 10000);
3207 	ck_assert_int_ge(client_res, 0);
3208 	ck_assert_str_eq(client_err, "");
3209 	client_ri = mg_get_response_info(client_conn);
3210 	ck_assert(client_ri != NULL);
3211 
3212 	ck_assert_int_eq(client_ri->status_code, 200);
3213 	client_res = (int)mg_read(client_conn, client_err, sizeof(client_err));
3214 	ck_assert_int_gt(client_res, 0);
3215 	ck_assert_int_le(client_res, sizeof(client_err));
3216 	ck_assert_str_eq(client_err, test_content);
3217 	mg_close_connection(client_conn);
3218 
3219 	test_sleep(1);
3220 
3221 	/* Create a .htpasswd file */
3222 	client_res = mg_modify_passwords_file(passwd_file, domain, "user", "pass");
3223 	ck_assert_int_eq(client_res, 1);
3224 
3225 	client_res = mg_modify_passwords_file(NULL, domain, "user", "pass");
3226 	ck_assert_int_eq(client_res, 0); /* Filename is required */
3227 
3228 	test_sleep(1);
3229 
3230 	/* Repeat test after .htpasswd is created */
3231 	memset(client_err, 0, sizeof(client_err));
3232 	client_conn =
3233 	    mg_connect_client("127.0.0.1", 8080, 0, client_err, sizeof(client_err));
3234 
3235 	ck_assert_str_eq(client_err, "");
3236 	ck_assert(client_conn != NULL);
3237 
3238 	mg_printf(client_conn, "GET /%s HTTP/1.0\r\n\r\n", test_file);
3239 	client_res =
3240 	    mg_get_response(client_conn, client_err, sizeof(client_err), 10000);
3241 	ck_assert_int_ge(client_res, 0);
3242 	ck_assert_str_eq(client_err, "");
3243 	client_ri = mg_get_response_info(client_conn);
3244 	ck_assert(client_ri != NULL);
3245 
3246 	ck_assert_int_eq(client_ri->status_code, 401);
3247 
3248 	auth_request = NULL;
3249 	for (i = 0; i < client_ri->num_headers; i++) {
3250 		if (!mg_strcasecmp(client_ri->http_headers[i].name,
3251 		                   "WWW-Authenticate")) {
3252 			ck_assert_ptr_eq(auth_request, NULL);
3253 			auth_request = client_ri->http_headers[i].value;
3254 			ck_assert_ptr_ne(auth_request, NULL);
3255 		}
3256 	}
3257 	ck_assert_ptr_ne(auth_request, NULL);
3258 	str = "Digest qop=\"auth\", realm=\"";
3259 	len = strlen(str);
3260 	ck_assert(!mg_strncasecmp(auth_request, str, len));
3261 	ck_assert(!strncmp(auth_request + len, domain, strlen(domain)));
3262 	len += strlen(domain);
3263 	str = "\", nonce=\"";
3264 	ck_assert(!strncmp(auth_request + len, str, strlen(str)));
3265 	len += strlen(str);
3266 	str = strchr(auth_request + len, '\"');
3267 	ck_assert_ptr_ne(str, NULL);
3268 	ck_assert_ptr_ne(str, auth_request + len);
3269 	/* nonce is from including (auth_request + len) to excluding (str) */
3270 	ck_assert_int_gt((ptrdiff_t)(str) - (ptrdiff_t)(auth_request + len), 0);
3271 	ck_assert_int_lt((ptrdiff_t)(str) - (ptrdiff_t)(auth_request + len),
3272 	                 (ptrdiff_t)sizeof(nonce));
3273 	memset(nonce, 0, sizeof(nonce));
3274 	memcpy(nonce,
3275 	       auth_request + len,
3276 	       (size_t)((ptrdiff_t)(str) - (ptrdiff_t)(auth_request + len)));
3277 	memset(HA1, 0, sizeof(HA1));
3278 	memset(HA2, 0, sizeof(HA2));
3279 	memset(HA1_md5_buf, 0, sizeof(HA1_md5_buf));
3280 	memset(HA2_md5_buf, 0, sizeof(HA2_md5_buf));
3281 	memset(HA_md5_buf, 0, sizeof(HA_md5_buf));
3282 
3283 	sprintf(HA1, "%s:%s:%s", "user", domain, "pass");
3284 	sprintf(HA2, "%s:/%s", "GET", test_file);
3285 	HA1_md5_ret = mg_md5(HA1_md5_buf, HA1, NULL);
3286 	HA2_md5_ret = mg_md5(HA2_md5_buf, HA2, NULL);
3287 
3288 	ck_assert_ptr_eq(HA1_md5_ret, HA1_md5_buf);
3289 	ck_assert_ptr_eq(HA2_md5_ret, HA2_md5_buf);
3290 
3291 	HA_md5_ret = mg_md5(HA_md5_buf, "user", ":", domain, ":", "pass", NULL);
3292 	ck_assert_ptr_eq(HA_md5_ret, HA_md5_buf);
3293 	ck_assert_str_eq(HA1_md5_ret, HA_md5_buf);
3294 
3295 	HA_md5_ret = mg_md5(HA_md5_buf, "GET", ":", "/", test_file, NULL);
3296 	ck_assert_ptr_eq(HA_md5_ret, HA_md5_buf);
3297 	ck_assert_str_eq(HA2_md5_ret, HA_md5_buf);
3298 
3299 	HA_md5_ret = mg_md5(HA_md5_buf,
3300 	                    HA1_md5_buf,
3301 	                    ":",
3302 	                    nonce,
3303 	                    ":",
3304 	                    nc,
3305 	                    ":",
3306 	                    cnonce,
3307 	                    ":",
3308 	                    "auth",
3309 	                    ":",
3310 	                    HA2_md5_buf,
3311 	                    NULL);
3312 	ck_assert_ptr_eq(HA_md5_ret, HA_md5_buf);
3313 
3314 	mg_close_connection(client_conn);
3315 
3316 	/* Retry with authorization */
3317 	memset(client_err, 0, sizeof(client_err));
3318 	client_conn =
3319 	    mg_connect_client("127.0.0.1", 8080, 0, client_err, sizeof(client_err));
3320 
3321 	ck_assert_str_eq(client_err, "");
3322 	ck_assert(client_conn != NULL);
3323 
3324 	mg_printf(client_conn, "GET /%s HTTP/1.0\r\n", test_file);
3325 	mg_printf(client_conn,
3326 	          "Authorization: Digest "
3327 	          "username=\"%s\", "
3328 	          "realm=\"%s\", "
3329 	          "nonce=\"%s\", "
3330 	          "uri=\"/%s\", "
3331 	          "qop=auth, "
3332 	          "nc=%s, "
3333 	          "cnonce=\"%s\", "
3334 	          "response=\"%s\"\r\n\r\n",
3335 	          "user",
3336 	          domain,
3337 	          nonce,
3338 	          test_file,
3339 	          nc,
3340 	          cnonce,
3341 	          HA_md5_buf);
3342 	client_res =
3343 	    mg_get_response(client_conn, client_err, sizeof(client_err), 10000);
3344 	ck_assert_int_ge(client_res, 0);
3345 	ck_assert_str_eq(client_err, "");
3346 	client_ri = mg_get_response_info(client_conn);
3347 	ck_assert(client_ri != NULL);
3348 
3349 	ck_assert_int_eq(client_ri->status_code, 200);
3350 	client_res = (int)mg_read(client_conn, client_err, sizeof(client_err));
3351 	ck_assert_int_gt(client_res, 0);
3352 	ck_assert_int_le(client_res, sizeof(client_err));
3353 	ck_assert_str_eq(client_err, test_content);
3354 	mg_close_connection(client_conn);
3355 
3356 	test_sleep(1);
3357 
3358 	/* Retry DELETE with authorization of a user not authorized for DELETE */
3359 	memset(client_err, 0, sizeof(client_err));
3360 	client_conn =
3361 	    mg_connect_client("127.0.0.1", 8080, 0, client_err, sizeof(client_err));
3362 
3363 	ck_assert_str_eq(client_err, "");
3364 	ck_assert(client_conn != NULL);
3365 
3366 	mg_printf(client_conn, "DELETE /%s HTTP/1.0\r\n", test_file);
3367 	mg_printf(client_conn,
3368 	          "Authorization: Digest "
3369 	          "username=\"%s\", "
3370 	          "realm=\"%s\", "
3371 	          "nonce=\"%s\", "
3372 	          "uri=\"/%s\", "
3373 	          "qop=auth, "
3374 	          "nc=%s, "
3375 	          "cnonce=\"%s\", "
3376 	          "response=\"%s\"\r\n\r\n",
3377 	          "user",
3378 	          domain,
3379 	          nonce,
3380 	          test_file,
3381 	          nc,
3382 	          cnonce,
3383 	          HA_md5_buf);
3384 	client_res =
3385 	    mg_get_response(client_conn, client_err, sizeof(client_err), 10000);
3386 	ck_assert_int_ge(client_res, 0);
3387 	ck_assert_str_eq(client_err, "");
3388 	client_ri = mg_get_response_info(client_conn);
3389 	ck_assert(client_ri != NULL);
3390 
3391 	ck_assert_int_eq(client_ri->status_code, 401);
3392 	mg_close_connection(client_conn);
3393 
3394 	test_sleep(1);
3395 
3396 	/* Remove the user from the .htpasswd file again */
3397 	client_res = mg_modify_passwords_file(passwd_file, domain, "user", NULL);
3398 	ck_assert_int_eq(client_res, 1);
3399 
3400 	test_sleep(1);
3401 
3402 
3403 	/* Try to access the file again. Expected: 401 error */
3404 	memset(client_err, 0, sizeof(client_err));
3405 	client_conn =
3406 	    mg_connect_client("127.0.0.1", 8080, 0, client_err, sizeof(client_err));
3407 
3408 	ck_assert_str_eq(client_err, "");
3409 	ck_assert(client_conn != NULL);
3410 
3411 	mg_printf(client_conn, "GET /%s HTTP/1.0\r\n\r\n", test_file);
3412 	client_res =
3413 	    mg_get_response(client_conn, client_err, sizeof(client_err), 10000);
3414 	ck_assert_int_ge(client_res, 0);
3415 	ck_assert_str_eq(client_err, "");
3416 	client_ri = mg_get_response_info(client_conn);
3417 	ck_assert(client_ri != NULL);
3418 
3419 	ck_assert_int_eq(client_ri->status_code, 401);
3420 	mg_close_connection(client_conn);
3421 
3422 	test_sleep(1);
3423 
3424 
3425 	/* Now remove the password file */
3426 	(void)remove(passwd_file);
3427 	test_sleep(1);
3428 
3429 
3430 	/* Access to the file must work like before */
3431 	memset(client_err, 0, sizeof(client_err));
3432 	client_conn =
3433 	    mg_connect_client("127.0.0.1", 8080, 0, client_err, sizeof(client_err));
3434 
3435 	ck_assert_str_eq(client_err, "");
3436 	ck_assert(client_conn != NULL);
3437 
3438 	mg_printf(client_conn, "GET /%s HTTP/1.0\r\n\r\n", test_file);
3439 	client_res =
3440 	    mg_get_response(client_conn, client_err, sizeof(client_err), 10000);
3441 	ck_assert_int_ge(client_res, 0);
3442 	ck_assert_str_eq(client_err, "");
3443 	client_ri = mg_get_response_info(client_conn);
3444 	ck_assert(client_ri != NULL);
3445 
3446 	ck_assert_int_eq(client_ri->status_code, 200);
3447 	client_res = (int)mg_read(client_conn, client_err, sizeof(client_err));
3448 	ck_assert_int_gt(client_res, 0);
3449 	ck_assert_int_le(client_res, sizeof(client_err));
3450 	ck_assert_str_eq(client_err, test_content);
3451 	mg_close_connection(client_conn);
3452 
3453 	test_sleep(1);
3454 
3455 
3456 	/* Stop the server and clean up */
3457 	test_mg_stop(ctx);
3458 	(void)remove(test_file);
3459 	(void)remove(passwd_file);
3460 	(void)remove("put_delete_auth_file.csv");
3461 
3462 #endif
3463 	mark_point();
3464 }
3465 END_TEST
3466 
3467 
START_TEST(test_keep_alive)3468 START_TEST(test_keep_alive)
3469 {
3470 	struct mg_context *ctx;
3471 	const char *OPTIONS[] =
3472 	{ "listening_ports",
3473 	  "8081",
3474 	  "request_timeout_ms",
3475 	  "10000",
3476 	  "enable_keep_alive",
3477 	  "yes",
3478 #if !defined(NO_FILES)
3479 	  "document_root",
3480 	  ".",
3481 	  "enable_directory_listing",
3482 	  "no",
3483 #endif
3484 	  NULL };
3485 
3486 	struct mg_connection *client_conn;
3487 	char client_err[256];
3488 	const struct mg_response_info *client_ri;
3489 	int client_res, i;
3490 	const char *connection_header;
3491 
3492 	mark_point();
3493 
3494 	ctx = test_mg_start(NULL, NULL, OPTIONS);
3495 
3496 	ck_assert(ctx != NULL);
3497 
3498 	/* HTTP 1.1 GET request */
3499 	memset(client_err, 0, sizeof(client_err));
3500 	client_conn =
3501 	    mg_connect_client("127.0.0.1", 8081, 0, client_err, sizeof(client_err));
3502 
3503 	ck_assert_str_eq(client_err, "");
3504 	ck_assert(client_conn != NULL);
3505 
3506 	mg_printf(client_conn,
3507 	          "GET / HTTP/1.1\r\nHost: "
3508 	          "localhost:8081\r\nConnection: keep-alive\r\n\r\n");
3509 	client_res =
3510 	    mg_get_response(client_conn, client_err, sizeof(client_err), 10000);
3511 	ck_assert_int_ge(client_res, 0);
3512 	ck_assert_str_eq(client_err, "");
3513 	client_ri = mg_get_response_info(client_conn);
3514 	ck_assert(client_ri != NULL);
3515 
3516 #if defined(NO_FILES)
3517 	ck_assert_int_eq(client_ri->status_code, 404);
3518 #else
3519 	ck_assert_int_eq(client_ri->status_code, 403);
3520 #endif
3521 
3522 	connection_header = 0;
3523 	for (i = 0; i < client_ri->num_headers; i++) {
3524 		if (!mg_strcasecmp(client_ri->http_headers[i].name, "Connection")) {
3525 			ck_assert_ptr_eq(connection_header, NULL);
3526 			connection_header = client_ri->http_headers[i].value;
3527 			ck_assert_ptr_ne(connection_header, NULL);
3528 		}
3529 	}
3530 	/* Error replies will close the connection, even if keep-alive is set. */
3531 	ck_assert_ptr_ne(connection_header, NULL);
3532 	ck_assert_str_eq(connection_header, "close");
3533 	mg_close_connection(client_conn);
3534 
3535 	test_sleep(1);
3536 
3537 	/* TODO: request a file and keep alive
3538 	 * (will only work if NO_FILES is not set). */
3539 
3540 	/* Stop the server and clean up */
3541 	test_mg_stop(ctx);
3542 
3543 	mark_point();
3544 }
3545 END_TEST
3546 
3547 
START_TEST(test_error_handling)3548 START_TEST(test_error_handling)
3549 {
3550 	struct mg_context *ctx;
3551 	FILE *f;
3552 
3553 	char bad_thread_num[32] = "badnumber";
3554 
3555 	struct mg_callbacks callbacks;
3556 	char errmsg[256];
3557 
3558 	struct mg_connection *client_conn;
3559 	char client_err[256];
3560 	const struct mg_response_info *client_ri;
3561 	int client_res, i;
3562 
3563 	const char *OPTIONS[32];
3564 	int opt_cnt = 0;
3565 
3566 	mark_point();
3567 
3568 #if !defined(NO_FILES)
3569 	OPTIONS[opt_cnt++] = "document_root";
3570 	OPTIONS[opt_cnt++] = ".";
3571 #endif
3572 	OPTIONS[opt_cnt++] = "error_pages";
3573 	OPTIONS[opt_cnt++] = "./";
3574 	OPTIONS[opt_cnt++] = "listening_ports";
3575 	OPTIONS[opt_cnt++] = "8080";
3576 	OPTIONS[opt_cnt++] = "num_threads";
3577 	OPTIONS[opt_cnt++] = bad_thread_num;
3578 	OPTIONS[opt_cnt++] = "unknown_option";
3579 	OPTIONS[opt_cnt++] = "unknown_option_value";
3580 	OPTIONS[opt_cnt] = NULL;
3581 
3582 	memset(&callbacks, 0, sizeof(callbacks));
3583 
3584 	callbacks.log_message = log_msg_func;
3585 
3586 	/* test with unknown option */
3587 	memset(errmsg, 0, sizeof(errmsg));
3588 	ctx = test_mg_start(&callbacks, (void *)errmsg, OPTIONS);
3589 
3590 	/* Details of errmsg may vary, but it may not be empty */
3591 	ck_assert_str_ne(errmsg, "");
3592 	ck_assert(ctx == NULL);
3593 	ck_assert_str_eq(errmsg, "Invalid option: unknown_option");
3594 
3595 	/* Remove invalid option */
3596 	for (i = 0; OPTIONS[i]; i++) {
3597 		if (strstr(OPTIONS[i], "unknown_option")) {
3598 			OPTIONS[i] = 0;
3599 		}
3600 	}
3601 
3602 	/* Test with bad num_thread option */
3603 	memset(errmsg, 0, sizeof(errmsg));
3604 	ctx = test_mg_start(&callbacks, (void *)errmsg, OPTIONS);
3605 
3606 	/* Details of errmsg may vary, but it may not be empty */
3607 	ck_assert_str_ne(errmsg, "");
3608 	ck_assert(ctx == NULL);
3609 	ck_assert_str_eq(errmsg, "Invalid number of worker threads");
3610 
3611 /* Set to a number - but use a number above the limit */
3612 #ifdef MAX_WORKER_THREADS
3613 	sprintf(bad_thread_num, "%u", MAX_WORKER_THREADS + 1);
3614 #else
3615 	sprintf(bad_thread_num, "%lu", 1000000000lu);
3616 #endif
3617 
3618 	/* Test with bad num_thread option */
3619 	memset(errmsg, 0, sizeof(errmsg));
3620 	ctx = test_mg_start(&callbacks, (void *)errmsg, OPTIONS);
3621 
3622 	/* Details of errmsg may vary, but it may not be empty */
3623 	ck_assert_str_ne(errmsg, "");
3624 	ck_assert(ctx == NULL);
3625 	ck_assert_str_eq(errmsg, "Too many worker threads");
3626 
3627 
3628 	/* HTTP 1.0 GET request - server is not running */
3629 	memset(client_err, 0, sizeof(client_err));
3630 	client_conn =
3631 	    mg_connect_client("127.0.0.1", 8080, 0, client_err, sizeof(client_err));
3632 	ck_assert(client_conn == NULL);
3633 
3634 	/* Error message detail may vary - it may not be empty and should contain
3635 	 * some information "connect" failed */
3636 	ck_assert_str_ne(client_err, "");
3637 	ck_assert(strstr(client_err, "connect"));
3638 
3639 
3640 	/* This time start the server with a valid configuration */
3641 	sprintf(bad_thread_num, "%i", 1);
3642 	memset(errmsg, 0, sizeof(errmsg));
3643 	ctx = test_mg_start(&callbacks, (void *)errmsg, OPTIONS);
3644 
3645 	ck_assert_str_eq(errmsg, "");
3646 	ck_assert(ctx != NULL);
3647 
3648 
3649 	/* Server is running now */
3650 	test_sleep(1);
3651 
3652 	/* Remove error files (in case they exist) */
3653 	(void)remove("error.htm");
3654 	(void)remove("error4xx.htm");
3655 	(void)remove("error404.htm");
3656 
3657 
3658 	/* Ask for something not existing - should get default 404 */
3659 	memset(client_err, 0, sizeof(client_err));
3660 	client_conn =
3661 	    mg_connect_client("127.0.0.1", 8080, 0, client_err, sizeof(client_err));
3662 
3663 	ck_assert_str_eq(client_err, "");
3664 	ck_assert(client_conn != NULL);
3665 
3666 	mg_printf(client_conn, "GET /something/not/existing HTTP/1.0\r\n\r\n");
3667 	client_res =
3668 	    mg_get_response(client_conn, client_err, sizeof(client_err), 10000);
3669 	ck_assert_int_ge(client_res, 0);
3670 	ck_assert_str_eq(client_err, "");
3671 	client_ri = mg_get_response_info(client_conn);
3672 	ck_assert(client_ri != NULL);
3673 
3674 	ck_assert_int_eq(client_ri->status_code, 404);
3675 	mg_close_connection(client_conn);
3676 	test_sleep(1);
3677 
3678 	/* Create an error.htm file */
3679 	f = fopen("error.htm", "wt");
3680 	ck_assert(f != NULL);
3681 	(void)fprintf(f, "err-all");
3682 	(void)fclose(f);
3683 
3684 
3685 	/* Ask for something not existing - should get error.htm */
3686 	memset(client_err, 0, sizeof(client_err));
3687 	client_conn =
3688 	    mg_connect_client("127.0.0.1", 8080, 0, client_err, sizeof(client_err));
3689 
3690 	ck_assert_str_eq(client_err, "");
3691 	ck_assert(client_conn != NULL);
3692 
3693 	mg_printf(client_conn, "GET /something/not/existing HTTP/1.0\r\n\r\n");
3694 	client_res =
3695 	    mg_get_response(client_conn, client_err, sizeof(client_err), 10000);
3696 	ck_assert_int_ge(client_res, 0);
3697 	ck_assert_str_eq(client_err, "");
3698 	client_ri = mg_get_response_info(client_conn);
3699 	ck_assert(client_ri != NULL);
3700 
3701 	ck_assert_int_eq(client_ri->status_code, 200);
3702 
3703 	client_res = (int)mg_read(client_conn, client_err, sizeof(client_err));
3704 	mg_close_connection(client_conn);
3705 	ck_assert_int_eq(client_res, 7);
3706 	client_err[8] = 0;
3707 	ck_assert_str_eq(client_err, "err-all");
3708 	test_sleep(1);
3709 
3710 	/* Create an error4xx.htm file */
3711 	f = fopen("error4xx.htm", "wt");
3712 	ck_assert(f != NULL);
3713 	(void)fprintf(f, "err-4xx");
3714 	(void)fclose(f);
3715 
3716 
3717 	/* Ask for something not existing - should get error4xx.htm */
3718 	memset(client_err, 0, sizeof(client_err));
3719 	client_conn =
3720 	    mg_connect_client("127.0.0.1", 8080, 0, client_err, sizeof(client_err));
3721 
3722 	ck_assert_str_eq(client_err, "");
3723 	ck_assert(client_conn != NULL);
3724 
3725 	mg_printf(client_conn, "GET /something/not/existing HTTP/1.0\r\n\r\n");
3726 	client_res =
3727 	    mg_get_response(client_conn, client_err, sizeof(client_err), 10000);
3728 	ck_assert_int_ge(client_res, 0);
3729 	ck_assert_str_eq(client_err, "");
3730 	client_ri = mg_get_response_info(client_conn);
3731 	ck_assert(client_ri != NULL);
3732 
3733 	ck_assert_int_eq(client_ri->status_code, 200);
3734 
3735 	client_res = (int)mg_read(client_conn, client_err, sizeof(client_err));
3736 	mg_close_connection(client_conn);
3737 	ck_assert_int_eq(client_res, 7);
3738 	client_err[8] = 0;
3739 	ck_assert_str_eq(client_err, "err-4xx");
3740 	test_sleep(1);
3741 
3742 	/* Create an error404.htm file */
3743 	f = fopen("error404.htm", "wt");
3744 	ck_assert(f != NULL);
3745 	(void)fprintf(f, "err-404");
3746 	(void)fclose(f);
3747 
3748 
3749 	/* Ask for something not existing - should get error404.htm */
3750 	memset(client_err, 0, sizeof(client_err));
3751 	client_conn =
3752 	    mg_connect_client("127.0.0.1", 8080, 0, client_err, sizeof(client_err));
3753 
3754 	ck_assert_str_eq(client_err, "");
3755 	ck_assert(client_conn != NULL);
3756 
3757 	mg_printf(client_conn, "GET /something/not/existing HTTP/1.0\r\n\r\n");
3758 	client_res =
3759 	    mg_get_response(client_conn, client_err, sizeof(client_err), 10000);
3760 	ck_assert_int_ge(client_res, 0);
3761 	ck_assert_str_eq(client_err, "");
3762 	client_ri = mg_get_response_info(client_conn);
3763 	ck_assert(client_ri != NULL);
3764 
3765 	ck_assert_int_eq(client_ri->status_code, 200);
3766 
3767 	client_res = (int)mg_read(client_conn, client_err, sizeof(client_err));
3768 	mg_close_connection(client_conn);
3769 	ck_assert_int_eq(client_res, 7);
3770 	client_err[8] = 0;
3771 	ck_assert_str_eq(client_err, "err-404");
3772 	test_sleep(1);
3773 
3774 
3775 	/* Ask in a malformed way - should get error4xx.htm */
3776 	memset(client_err, 0, sizeof(client_err));
3777 	client_conn =
3778 	    mg_connect_client("127.0.0.1", 8080, 0, client_err, sizeof(client_err));
3779 
3780 	ck_assert_str_eq(client_err, "");
3781 	ck_assert(client_conn != NULL);
3782 
3783 	mg_printf(client_conn, "Gimme some file!\r\n\r\n");
3784 	client_res =
3785 	    mg_get_response(client_conn, client_err, sizeof(client_err), 10000);
3786 	ck_assert_int_ge(client_res, 0);
3787 	ck_assert_str_eq(client_err, "");
3788 	client_ri = mg_get_response_info(client_conn);
3789 	ck_assert(client_ri != NULL);
3790 
3791 	ck_assert_int_eq(client_ri->status_code, 200);
3792 
3793 	client_res = (int)mg_read(client_conn, client_err, sizeof(client_err));
3794 	mg_close_connection(client_conn);
3795 	ck_assert_int_eq(client_res, 7);
3796 	client_err[8] = 0;
3797 	ck_assert_str_eq(client_err, "err-4xx");
3798 	test_sleep(1);
3799 
3800 
3801 	/* Remove all error files created by this test */
3802 	(void)remove("error.htm");
3803 	(void)remove("error4xx.htm");
3804 	(void)remove("error404.htm");
3805 
3806 
3807 	/* Stop the server */
3808 	test_mg_stop(ctx);
3809 
3810 
3811 	/* HTTP 1.1 GET request - must not work, since server is already stopped  */
3812 	memset(client_err, 0, sizeof(client_err));
3813 	client_conn =
3814 	    mg_connect_client("127.0.0.1", 8080, 0, client_err, sizeof(client_err));
3815 
3816 	ck_assert(client_conn == NULL);
3817 	ck_assert_str_ne(client_err, "");
3818 
3819 	test_sleep(1);
3820 
3821 	mark_point();
3822 }
3823 END_TEST
3824 
3825 
START_TEST(test_error_log_file)3826 START_TEST(test_error_log_file)
3827 {
3828 	/* Server var */
3829 	struct mg_context *ctx;
3830 	const char *OPTIONS[32];
3831 	int opt_cnt = 0;
3832 
3833 	/* Client var */
3834 	struct mg_connection *client;
3835 	char client_err_buf[256];
3836 	char client_data_buf[256];
3837 	const struct mg_response_info *client_ri;
3838 
3839 	/* File content check var */
3840 	FILE *f;
3841 	char buf[1024];
3842 	int len, ok;
3843 
3844 	mark_point();
3845 
3846 	/* Set options and start server */
3847 	OPTIONS[opt_cnt++] = "listening_ports";
3848 	OPTIONS[opt_cnt++] = "8080";
3849 	OPTIONS[opt_cnt++] = "error_log_file";
3850 	OPTIONS[opt_cnt++] = "error.log";
3851 	OPTIONS[opt_cnt++] = "access_log_file";
3852 	OPTIONS[opt_cnt++] = "access.log";
3853 #if !defined(NO_FILES)
3854 	OPTIONS[opt_cnt++] = "document_root";
3855 	OPTIONS[opt_cnt++] = ".";
3856 #endif
3857 	OPTIONS[opt_cnt] = NULL;
3858 
3859 	ctx = test_mg_start(NULL, 0, OPTIONS);
3860 	ck_assert(ctx != NULL);
3861 
3862 	/* Remove log files (they may exist from previous incomplete runs of
3863 	 * this test) */
3864 	(void)remove("error.log");
3865 	(void)remove("access.log");
3866 
3867 	/* connect client */
3868 	memset(client_err_buf, 0, sizeof(client_err_buf));
3869 	memset(client_data_buf, 0, sizeof(client_data_buf));
3870 
3871 	client = mg_download("127.0.0.1",
3872 	                     8080,
3873 	                     0,
3874 	                     client_err_buf,
3875 	                     sizeof(client_err_buf),
3876 	                     "GET /not_existing_file.ext HTTP/1.0\r\n\r\n");
3877 
3878 	ck_assert(ctx != NULL);
3879 	ck_assert_str_eq(client_err_buf, "");
3880 
3881 	client_ri = mg_get_response_info(client);
3882 
3883 	/* Check status - should be 404 Not Found */
3884 	ck_assert(client_ri != NULL);
3885 	ck_assert_int_eq(client_ri->status_code, 404);
3886 
3887 	/* Get body data (could exist, but does not have to) */
3888 	len = mg_read(client, client_data_buf, sizeof(client_data_buf));
3889 	ck_assert_int_ge(len, 0);
3890 
3891 	/* Close the client connection */
3892 	mg_close_connection(client);
3893 
3894 	/* Stop the server */
3895 	test_mg_stop(ctx);
3896 
3897 
3898 	/* Check access.log */
3899 	memset(buf, 0, sizeof(buf));
3900 	f = fopen("access.log", "r");
3901 	ck_assert_msg(f != NULL, "Cannot open access log file");
3902 	ok = (NULL != fgets(buf, sizeof(buf) - 1, f));
3903 	(void)fclose(f);
3904 	ck_assert_msg(ok, "Cannot read access log file");
3905 	len = (int)strlen(buf);
3906 	ck_assert_int_gt(len, 0);
3907 	ok = (NULL != strstr(buf, "not_existing_file.ext"));
3908 	ck_assert_msg(ok, "Did not find uri in access log file");
3909 	ok = (NULL != strstr(buf, "404"));
3910 	ck_assert_msg(ok, "Did not find HTTP status code in access log file");
3911 
3912 	/* Check error.log */
3913 	memset(buf, 0, sizeof(buf));
3914 	f = fopen("error.log", "r");
3915 	if (f) {
3916 		(void)fgets(buf, sizeof(buf) - 1, f);
3917 		fclose(f);
3918 	}
3919 	ck_assert_msg(f == NULL,
3920 	              "Should not create error log file on 404, but got [%s]",
3921 	              buf);
3922 
3923 	/* Remove log files */
3924 	(void)remove("error.log");
3925 	(void)remove("access.log");
3926 
3927 	/* Start server with bad options */
3928 	ck_assert_str_eq(OPTIONS[0], "listening_ports");
3929 	OPTIONS[1] = "bad !"; /* no r or s in string */
3930 
3931 	ctx = test_mg_start(NULL, 0, OPTIONS);
3932 	ck_assert_msg(
3933 	    ctx == NULL,
3934 	    "Should not be able to start server with bad port configuration");
3935 
3936 	/* Check access.log */
3937 	memset(buf, 0, sizeof(buf));
3938 	f = fopen("access.log", "r");
3939 	if (f) {
3940 		(void)fgets(buf, sizeof(buf) - 1, f);
3941 		fclose(f);
3942 	}
3943 	ck_assert_msg(
3944 	    f == NULL,
3945 	    "Should not create access log file if start fails, but got [%s]",
3946 	    buf);
3947 
3948 	/* Check error.log */
3949 	memset(buf, 0, sizeof(buf));
3950 	f = fopen("error.log", "r");
3951 	ck_assert_msg(f != NULL, "Cannot open access log file");
3952 	ok = (NULL != fgets(buf, sizeof(buf) - 1, f));
3953 	(void)fclose(f);
3954 	ck_assert_msg(ok, "Cannot read access log file");
3955 	len = (int)strlen(buf);
3956 	ck_assert_int_gt(len, 0);
3957 	ok = (NULL != strstr(buf, "port"));
3958 	ck_assert_msg(ok, "Did not find port as error reason in error log file");
3959 
3960 
3961 	/* Remove log files */
3962 	(void)remove("error.log");
3963 	(void)remove("access.log");
3964 
3965 	mark_point();
3966 }
3967 END_TEST
3968 
3969 
3970 static int
test_throttle_begin_request(struct mg_connection * conn)3971 test_throttle_begin_request(struct mg_connection *conn)
3972 {
3973 	const struct mg_request_info *ri;
3974 	long unsigned len = 1024 * 10;
3975 	const char *block = "0123456789";
3976 	unsigned long i, blocklen;
3977 
3978 	ck_assert(conn != NULL);
3979 	ri = mg_get_request_info(conn);
3980 	ck_assert(ri != NULL);
3981 
3982 	ck_assert_str_eq(ri->request_method, "GET");
3983 	ck_assert_str_eq(ri->request_uri, "/throttle");
3984 	ck_assert_str_eq(ri->local_uri, "/throttle");
3985 	ck_assert_str_eq(ri->http_version, "1.0");
3986 	ck_assert_str_eq(ri->query_string, "q");
3987 	ck_assert_str_eq(ri->remote_addr, "127.0.0.1");
3988 
3989 	mg_printf(conn,
3990 	          "HTTP/1.1 200 OK\r\n"
3991 	          "Content-Length: %lu\r\n"
3992 	          "Connection: close\r\n\r\n",
3993 	          len);
3994 
3995 	blocklen = (unsigned long)strlen(block);
3996 
3997 	for (i = 0; i < len; i += blocklen) {
3998 		mg_write(conn, block, blocklen);
3999 	}
4000 
4001 	mark_point();
4002 
4003 	return 987; /* Not a valid HTTP response code,
4004 	             * but it should be written to the log and passed to
4005 	             * end_request. */
4006 }
4007 
4008 
4009 static void
test_throttle_end_request(const struct mg_connection * conn,int reply_status_code)4010 test_throttle_end_request(const struct mg_connection *conn,
4011                           int reply_status_code)
4012 {
4013 	const struct mg_request_info *ri;
4014 
4015 	ck_assert(conn != NULL);
4016 	ri = mg_get_request_info(conn);
4017 	ck_assert(ri != NULL);
4018 
4019 	ck_assert_str_eq(ri->request_method, "GET");
4020 	ck_assert_str_eq(ri->request_uri, "/throttle");
4021 	ck_assert_str_eq(ri->local_uri, "/throttle");
4022 	ck_assert_str_eq(ri->http_version, "1.0");
4023 	ck_assert_str_eq(ri->query_string, "q");
4024 	ck_assert_str_eq(ri->remote_addr, "127.0.0.1");
4025 
4026 	ck_assert_int_eq(reply_status_code, 987);
4027 }
4028 
4029 
START_TEST(test_throttle)4030 START_TEST(test_throttle)
4031 {
4032 	/* Server var */
4033 	struct mg_context *ctx;
4034 	struct mg_callbacks callbacks;
4035 	const char *OPTIONS[32];
4036 	int opt_cnt = 0;
4037 
4038 	/* Client var */
4039 	struct mg_connection *client;
4040 	char client_err_buf[256];
4041 	char client_data_buf[256];
4042 	const struct mg_response_info *client_ri;
4043 
4044 	/* timing test */
4045 	int r, data_read;
4046 	time_t t0, t1;
4047 	double dt;
4048 
4049 	mark_point();
4050 
4051 
4052 /* Set options and start server */
4053 #if !defined(NO_FILES)
4054 	OPTIONS[opt_cnt++] = "document_root";
4055 	OPTIONS[opt_cnt++] = ".";
4056 #endif
4057 	OPTIONS[opt_cnt++] = "listening_ports";
4058 	OPTIONS[opt_cnt++] = "8080";
4059 	OPTIONS[opt_cnt++] = "throttle";
4060 	OPTIONS[opt_cnt++] = "*=1k";
4061 	OPTIONS[opt_cnt] = NULL;
4062 
4063 	memset(&callbacks, 0, sizeof(callbacks));
4064 	callbacks.begin_request = test_throttle_begin_request;
4065 	callbacks.end_request = test_throttle_end_request;
4066 
4067 	ctx = test_mg_start(&callbacks, 0, OPTIONS);
4068 	ck_assert(ctx != NULL);
4069 
4070 	/* connect client */
4071 	memset(client_err_buf, 0, sizeof(client_err_buf));
4072 	memset(client_data_buf, 0, sizeof(client_data_buf));
4073 
4074 	strcpy(client_err_buf, "reset-content");
4075 	client = mg_download("127.0.0.1",
4076 	                     8080,
4077 	                     0,
4078 	                     client_err_buf,
4079 	                     sizeof(client_err_buf),
4080 	                     "GET /throttle?q HTTP/1.0\r\n\r\n");
4081 
4082 	ck_assert(ctx != NULL);
4083 	ck_assert_str_eq(client_err_buf, "");
4084 
4085 	client_ri = mg_get_response_info(client);
4086 
4087 	ck_assert(client_ri != NULL);
4088 	ck_assert_int_eq(client_ri->status_code, 200);
4089 
4090 	ck_assert_int_eq(client_ri->content_length, 1024 * 10);
4091 
4092 	data_read = 0;
4093 	t0 = time(NULL);
4094 	while (data_read < client_ri->content_length) {
4095 		r = mg_read(client, client_data_buf, sizeof(client_data_buf));
4096 		ck_assert_int_ge(r, 0);
4097 		data_read += r;
4098 	}
4099 	t1 = time(NULL);
4100 	dt = difftime(t1, t0) * 1000.0; /* Elapsed time in ms - in most systems
4101 	                                 * only with second resolution */
4102 
4103 	/* Time estimation: Data size is 10 kB, with 1 kB/s speed limit.
4104 	 * The first block (1st kB) is transferred immediately, the second
4105 	 * block (2nd kB) one second later, the third block (3rd kB) two
4106 	 * seconds later, .. the last block (10th kB) nine seconds later.
4107 	 * The resolution of time measurement using the "time" C library
4108 	 * function is 1 second, so we should add +/- one second tolerance.
4109 	 * Thus, download of 10 kB with 1 kB/s should not be faster than
4110 	 * 8 seconds. */
4111 
4112 	/* Check if there are at least 8 seconds */
4113 	ck_assert_int_ge((int)dt, 8 * 1000);
4114 
4115 	/* Nothing left to read */
4116 	r = mg_read(client, client_data_buf, sizeof(client_data_buf));
4117 	ck_assert_int_eq(r, 0);
4118 
4119 	/* Close the client connection */
4120 	mg_close_connection(client);
4121 
4122 	/* Stop the server */
4123 	test_mg_stop(ctx);
4124 
4125 	mark_point();
4126 }
4127 END_TEST
4128 
4129 
START_TEST(test_init_library)4130 START_TEST(test_init_library)
4131 {
4132 	unsigned f_avail, f_ret;
4133 
4134 	mark_point();
4135 
4136 	f_avail = mg_check_feature(0xFF);
4137 	f_ret = mg_init_library(f_avail);
4138 	ck_assert_uint_eq(f_ret, f_avail);
4139 }
4140 END_TEST
4141 
4142 
4143 #define LARGE_FILE_SIZE (1024 * 1024 * 10)
4144 
4145 static int
test_large_file_begin_request(struct mg_connection * conn)4146 test_large_file_begin_request(struct mg_connection *conn)
4147 {
4148 	const struct mg_request_info *ri;
4149 	long unsigned len = LARGE_FILE_SIZE;
4150 	const char *block = "0123456789";
4151 	uint64_t i;
4152 	size_t blocklen;
4153 
4154 	ck_assert(conn != NULL);
4155 	ri = mg_get_request_info(conn);
4156 	ck_assert(ri != NULL);
4157 
4158 	ck_assert_str_eq(ri->request_method, "GET");
4159 	ck_assert_str_eq(ri->http_version, "1.1");
4160 	ck_assert_str_eq(ri->remote_addr, "127.0.0.1");
4161 	ck_assert_ptr_eq(ri->query_string, NULL);
4162 	ck_assert_ptr_ne(ri->local_uri, NULL);
4163 
4164 	mg_printf(conn,
4165 	          "HTTP/1.1 200 OK\r\n"
4166 	          "Content-Length: %lu\r\n"
4167 	          "Connection: close\r\n\r\n",
4168 	          len);
4169 
4170 	blocklen = strlen(block);
4171 
4172 	for (i = 0; i < len; i += blocklen) {
4173 		mg_write(conn, block, blocklen);
4174 	}
4175 
4176 	mark_point();
4177 
4178 	return 200;
4179 }
4180 
4181 
START_TEST(test_large_file)4182 START_TEST(test_large_file)
4183 {
4184 	/* Server var */
4185 	struct mg_context *ctx;
4186 	struct mg_callbacks callbacks;
4187 	const char *OPTIONS[32];
4188 	int opt_cnt = 0;
4189 #if !defined(NO_SSL)
4190 	const char *ssl_cert = locate_ssl_cert();
4191 #endif
4192 	char errmsg[256] = {0};
4193 
4194 	/* Client var */
4195 	struct mg_connection *client;
4196 	char client_err_buf[256];
4197 	char client_data_buf[256];
4198 	const struct mg_response_info *client_ri;
4199 	int64_t data_read;
4200 	int r;
4201 	int retry, retry_ok_cnt, retry_fail_cnt;
4202 
4203 	mark_point();
4204 
4205 /* Set options and start server */
4206 #if !defined(NO_FILES)
4207 	OPTIONS[opt_cnt++] = "document_root";
4208 	OPTIONS[opt_cnt++] = ".";
4209 #endif
4210 #if defined(NO_SSL)
4211 	OPTIONS[opt_cnt++] = "listening_ports";
4212 	OPTIONS[opt_cnt++] = "8080";
4213 #else
4214 	OPTIONS[opt_cnt++] = "listening_ports";
4215 	OPTIONS[opt_cnt++] = "8443s";
4216 	OPTIONS[opt_cnt++] = "ssl_certificate";
4217 	OPTIONS[opt_cnt++] = ssl_cert;
4218 #ifdef __MACH__
4219 	/* The Apple builds on Travis CI seem to have problems with TLS1.x
4220 	 * Allow SSLv3 and TLS */
4221 	OPTIONS[opt_cnt++] = "ssl_protocol_version";
4222 	OPTIONS[opt_cnt++] = "2";
4223 #else
4224 	/* The Linux builds on Travis CI work fine with TLS1.2 */
4225 	OPTIONS[opt_cnt++] = "ssl_protocol_version";
4226 	OPTIONS[opt_cnt++] = "4";
4227 #endif
4228 	ck_assert(ssl_cert != NULL);
4229 #endif
4230 	OPTIONS[opt_cnt] = NULL;
4231 
4232 
4233 	memset(&callbacks, 0, sizeof(callbacks));
4234 	callbacks.begin_request = test_large_file_begin_request;
4235 	callbacks.log_message = log_msg_func;
4236 
4237 	ctx = test_mg_start(&callbacks, (void *)errmsg, OPTIONS);
4238 	ck_assert_str_eq(errmsg, "");
4239 	ck_assert(ctx != NULL);
4240 
4241 	/* Try downloading several times */
4242 	retry_ok_cnt = 0;
4243 	retry_fail_cnt = 0;
4244 	for (retry = 0; retry < 3; retry++) {
4245 		int fail = 0;
4246 		/* connect client */
4247 		memset(client_err_buf, 0, sizeof(client_err_buf));
4248 		memset(client_data_buf, 0, sizeof(client_data_buf));
4249 
4250 		client =
4251 		    mg_download("127.0.0.1",
4252 #if defined(NO_SSL)
4253 		                8080,
4254 		                0,
4255 #else
4256 		                8443,
4257 		                1,
4258 #endif
4259 		                client_err_buf,
4260 		                sizeof(client_err_buf),
4261 		                "GET /large.file HTTP/1.1\r\nHost: 127.0.0.1\r\n\r\n");
4262 
4263 		ck_assert(client != NULL);
4264 		ck_assert_str_eq(client_err_buf, "");
4265 
4266 		client_ri = mg_get_response_info(client);
4267 
4268 		ck_assert(client_ri != NULL);
4269 		ck_assert_int_eq(client_ri->status_code, 200);
4270 
4271 		ck_assert_int_eq(client_ri->content_length, LARGE_FILE_SIZE);
4272 
4273 		data_read = 0;
4274 		while (data_read < client_ri->content_length) {
4275 			r = mg_read(client, client_data_buf, sizeof(client_data_buf));
4276 			if (r < 0) {
4277 				fail = 1;
4278 				break;
4279 			};
4280 			data_read += r;
4281 		}
4282 
4283 		/* Nothing left to read */
4284 		r = mg_read(client, client_data_buf, sizeof(client_data_buf));
4285 		if (fail) {
4286 			ck_assert_int_eq(r, -1);
4287 			retry_fail_cnt++;
4288 		} else {
4289 			ck_assert_int_eq(r, 0);
4290 			retry_ok_cnt++;
4291 		}
4292 
4293 		/* Close the client connection */
4294 		mg_close_connection(client);
4295 	}
4296 
4297 #if defined(_WIN32)
4298 // TODO: Check this problem on AppVeyor
4299 // ck_assert_int_le(retry_fail_cnt, 2);
4300 // ck_assert_int_ge(retry_ok_cnt, 1);
4301 #else
4302 	ck_assert_int_eq(retry_fail_cnt, 0);
4303 	ck_assert_int_eq(retry_ok_cnt, 3);
4304 #endif
4305 
4306 	/* Stop the server */
4307 	test_mg_stop(ctx);
4308 
4309 	mark_point();
4310 }
4311 END_TEST
4312 
4313 
4314 static int test_mg_store_body_con_len = 20000;
4315 
4316 
4317 static int
test_mg_store_body_put_delete_handler(struct mg_connection * conn,void * ignored)4318 test_mg_store_body_put_delete_handler(struct mg_connection *conn, void *ignored)
4319 {
4320 	char path[4096] = {0};
4321 	const struct mg_request_info *info = mg_get_request_info(conn);
4322 	int64_t rc;
4323 
4324 	(void)ignored;
4325 
4326 	mark_point();
4327 
4328 	sprintf(path, "./%s", info->local_uri);
4329 	rc = mg_store_body(conn, path);
4330 
4331 	ck_assert_int_eq(test_mg_store_body_con_len, rc);
4332 
4333 	if (rc < 0) {
4334 		mg_printf(conn,
4335 		          "HTTP/1.1 500 Internal Server Error\r\n"
4336 		          "Content-Type:text/plain;charset=UTF-8\r\n"
4337 		          "Connection:close\r\n\r\n"
4338 		          "%s (ret: %ld)\n",
4339 		          path,
4340 		          (long)rc);
4341 		mg_close_connection(conn);
4342 
4343 		/* Debug output for tests */
4344 		printf("mg_store_body(%s) failed (ret: %ld)\n", path, (long)rc);
4345 
4346 		return 500;
4347 	}
4348 
4349 	mg_printf(conn,
4350 	          "HTTP/1.1 200 OK\r\n"
4351 	          "Content-Type:text/plain;charset=UTF-8\r\n"
4352 	          "Connection:close\r\n\r\n"
4353 	          "%s OK (%ld bytes saved)\n",
4354 	          path,
4355 	          (long)rc);
4356 	mg_close_connection(conn);
4357 
4358 	/* Debug output for tests */
4359 	printf("mg_store_body(%s) OK (%ld bytes)\n", path, (long)rc);
4360 
4361 	mark_point();
4362 
4363 	return 200;
4364 }
4365 
4366 
4367 static int
test_mg_store_body_begin_request_callback(struct mg_connection * conn)4368 test_mg_store_body_begin_request_callback(struct mg_connection *conn)
4369 {
4370 	const struct mg_request_info *info = mg_get_request_info(conn);
4371 
4372 	mark_point();
4373 
4374 	/* Debug output for tests */
4375 	printf("test_mg_store_body_begin_request_callback called (%s)\n",
4376 	       info->request_method);
4377 
4378 	if ((strcmp(info->request_method, "PUT") == 0)
4379 	    || (strcmp(info->request_method, "DELETE") == 0)) {
4380 		return test_mg_store_body_put_delete_handler(conn, NULL);
4381 	}
4382 
4383 	mark_point();
4384 
4385 	return 0;
4386 }
4387 
4388 
START_TEST(test_mg_store_body)4389 START_TEST(test_mg_store_body)
4390 {
4391 	/* Client data */
4392 	char client_err_buf[256];
4393 	char client_data_buf[1024];
4394 	struct mg_connection *client;
4395 	const struct mg_response_info *client_ri;
4396 	int r;
4397 	char check_data[256];
4398 	char *check_ptr;
4399 	char errmsg[256] = {0};
4400 
4401 	/* Server context handle */
4402 	struct mg_context *ctx;
4403 	struct mg_callbacks callbacks;
4404 	const char *options[] = {
4405 #if !defined(NO_FILES)
4406 		"document_root",
4407 		".",
4408 #endif
4409 #if !defined(NO_CACHING)
4410 		"static_file_max_age",
4411 		"0",
4412 #endif
4413 		"listening_ports",
4414 		"127.0.0.1:8082",
4415 		"num_threads",
4416 		"1",
4417 		NULL
4418 	};
4419 
4420 	mark_point();
4421 
4422 	memset(&callbacks, 0, sizeof(callbacks));
4423 	callbacks.begin_request = test_mg_store_body_begin_request_callback;
4424 	callbacks.log_message = log_msg_func;
4425 
4426 	/* Initialize the library */
4427 	mg_init_library(0);
4428 
4429 	/* Start the server */
4430 	ctx = mg_start(&callbacks, (void *)errmsg, options);
4431 	ck_assert_str_eq(errmsg, "");
4432 	ck_assert(ctx != NULL);
4433 
4434 	/* Run the server for 15 seconds */
4435 	test_sleep(15);
4436 
4437 	/* Call a test client */
4438 	client = mg_connect_client(
4439 	    "127.0.0.1", 8082, 0, client_err_buf, sizeof(client_err_buf));
4440 
4441 	ck_assert_str_eq(client_err_buf, "");
4442 	ck_assert(client != NULL);
4443 
4444 	mg_printf(client,
4445 	          "PUT /%s HTTP/1.0\r\nContent-Length: %i\r\n\r\n",
4446 	          "test_file_name.txt",
4447 	          test_mg_store_body_con_len);
4448 
4449 	r = 0;
4450 	while (r < test_mg_store_body_con_len) {
4451 		int l = mg_write(client, "1234567890", 10);
4452 		ck_assert_int_eq(l, 10);
4453 		r += 10;
4454 	}
4455 
4456 	r = mg_get_response(client, client_err_buf, sizeof(client_err_buf), 10000);
4457 	ck_assert_int_ge(r, 0);
4458 	ck_assert_str_eq(client_err_buf, "");
4459 
4460 	client_ri = mg_get_response_info(client);
4461 	ck_assert(client_ri != NULL);
4462 
4463 	/* Response must be 200 OK  */
4464 	ck_assert_int_eq(client_ri->status_code, 200);
4465 
4466 	/* Read PUT response */
4467 	r = mg_read(client, client_data_buf, sizeof(client_data_buf) - 1);
4468 	ck_assert_int_gt(r, 0);
4469 	client_data_buf[r] = 0;
4470 
4471 	sprintf(check_data, "(%i bytes saved)", test_mg_store_body_con_len);
4472 	check_ptr = strstr(client_data_buf, check_data);
4473 	ck_assert_ptr_ne(check_ptr, NULL);
4474 
4475 	mg_close_connection(client);
4476 
4477 	/* Run the server for 5 seconds */
4478 	test_sleep(5);
4479 
4480 	/* Stop the server */
4481 	test_mg_stop(ctx);
4482 
4483 	/* Un-initialize the library */
4484 	mg_exit_library();
4485 
4486 	mark_point();
4487 }
4488 END_TEST
4489 
4490 
4491 #if defined(MG_USE_OPEN_FILE) && !defined(NO_FILES)
4492 
4493 #define FILE_IN_MEM_SIZE (1024 * 100)
4494 static char *file_in_mem_data;
4495 
4496 static const char *
test_file_in_memory_open_file(const struct mg_connection * conn,const char * file_path,size_t * file_size)4497 test_file_in_memory_open_file(const struct mg_connection *conn,
4498                               const char *file_path,
4499                               size_t *file_size)
4500 {
4501 	(void)conn;
4502 
4503 	if (strcmp(file_path, "./file_in_mem") == 0) {
4504 		/* File is in memory */
4505 		*file_size = FILE_IN_MEM_SIZE;
4506 		return file_in_mem_data;
4507 	} else {
4508 		/* File is not in memory */
4509 		return NULL;
4510 	}
4511 }
4512 
4513 
START_TEST(test_file_in_memory)4514 START_TEST(test_file_in_memory)
4515 {
4516 	/* Server var */
4517 	struct mg_context *ctx;
4518 	struct mg_callbacks callbacks;
4519 	const char *OPTIONS[32];
4520 	int opt_cnt = 0;
4521 #if !defined(NO_SSL)
4522 	const char *ssl_cert = locate_ssl_cert();
4523 #endif
4524 
4525 	/* Client var */
4526 	struct mg_connection *client;
4527 	char client_err_buf[256];
4528 	char client_data_buf[256];
4529 	const struct mg_request_info *client_ri;
4530 	int64_t data_read;
4531 	int r, i;
4532 
4533 	/* Prepare test data */
4534 	file_in_mem_data = (char *)malloc(FILE_IN_MEM_SIZE);
4535 	ck_assert_ptr_ne(file_in_mem_data, NULL);
4536 	for (r = 0; r < FILE_IN_MEM_SIZE; r++) {
4537 		file_in_mem_data[r] = (char)(r);
4538 	}
4539 
4540 	/* Set options and start server */
4541 	OPTIONS[opt_cnt++] = "document_root";
4542 	OPTIONS[opt_cnt++] = ".";
4543 #if defined(NO_SSL)
4544 	OPTIONS[opt_cnt++] = "listening_ports";
4545 	OPTIONS[opt_cnt++] = "8080";
4546 #else
4547 	OPTIONS[opt_cnt++] = "listening_ports";
4548 	OPTIONS[opt_cnt++] = "8443s";
4549 	OPTIONS[opt_cnt++] = "ssl_certificate";
4550 	OPTIONS[opt_cnt++] = ssl_cert;
4551 	ck_assert(ssl_cert != NULL);
4552 #endif
4553 	OPTIONS[opt_cnt] = NULL;
4554 
4555 
4556 	memset(&callbacks, 0, sizeof(callbacks));
4557 	callbacks.open_file = test_file_in_memory_open_file;
4558 
4559 	ctx = test_mg_start(&callbacks, 0, OPTIONS);
4560 	ck_assert(ctx != NULL);
4561 
4562 	/* connect client */
4563 	memset(client_err_buf, 0, sizeof(client_err_buf));
4564 	memset(client_data_buf, 0, sizeof(client_data_buf));
4565 
4566 	client =
4567 	    mg_download("127.0.0.1",
4568 #if defined(NO_SSL)
4569 	                8080,
4570 	                0,
4571 #else
4572 	                8443,
4573 	                1,
4574 #endif
4575 	                client_err_buf,
4576 	                sizeof(client_err_buf),
4577 	                "GET /file_in_mem HTTP/1.1\r\nHost: 127.0.0.1\r\n\r\n");
4578 
4579 	ck_assert(client != NULL);
4580 	ck_assert_str_eq(client_err_buf, "");
4581 
4582 	client_ri = mg_get_response_info(client);
4583 
4584 	ck_assert(client_ri != NULL);
4585 	ck_assert_int_eq(client_ri->status_code, 200);
4586 
4587 	ck_assert_int_eq(client_ri->content_length, FILE_IN_MEM_SIZE);
4588 
4589 	data_read = 0;
4590 	while (data_read < client_ri->content_length) {
4591 		r = mg_read(client, client_data_buf, sizeof(client_data_buf));
4592 		if (r > 0) {
4593 			for (i = 0; i < r; i++) {
4594 				ck_assert_int_eq((int)client_data_buf[i],
4595 				                 (int)file_in_mem_data[data_read + i]);
4596 			}
4597 			data_read += r;
4598 		}
4599 	}
4600 
4601 	/* Nothing left to read */
4602 	r = mg_read(client, client_data_buf, sizeof(client_data_buf));
4603 	ck_assert_int_eq(r, 0);
4604 
4605 	/* Close the client connection */
4606 	mg_close_connection(client);
4607 
4608 	/* Stop the server */
4609 	test_mg_stop(ctx);
4610 
4611 	/* Free test data */
4612 	free(file_in_mem_data);
4613 	file_in_mem_data = NULL;
4614 }
4615 END_TEST
4616 
4617 #else /* defined(MG_USE_OPEN_FILE) */
4618 
4619 START_TEST(test_file_in_memory)
4620 {
4621 	mark_point();
4622 }
4623 END_TEST
4624 
4625 #endif
4626 
4627 
4628 static void
minimal_http_https_client_impl(const char * server,uint16_t port,int use_ssl,const char * uri)4629 minimal_http_https_client_impl(const char *server,
4630                                uint16_t port,
4631                                int use_ssl,
4632                                const char *uri)
4633 {
4634 	/* Client var */
4635 	struct mg_connection *client;
4636 	char client_err_buf[256];
4637 	char client_data_buf[256];
4638 	const struct mg_response_info *client_ri;
4639 	int64_t data_read;
4640 	int r;
4641 
4642 	mark_point();
4643 
4644 	client = mg_connect_client(
4645 	    server, port, use_ssl, client_err_buf, sizeof(client_err_buf));
4646 
4647 	if ((client == NULL) || (0 != strcmp(client_err_buf, ""))) {
4648 		ck_abort_msg("%s connection to server [%s] port [%u] failed: [%s]",
4649 		             use_ssl ? "HTTPS" : "HTTP",
4650 		             server,
4651 		             port,
4652 		             client_err_buf);
4653 	}
4654 
4655 	mg_printf(client, "GET /%s HTTP/1.0\r\n\r\n", uri);
4656 
4657 	r = mg_get_response(client, client_err_buf, sizeof(client_err_buf), 10000);
4658 
4659 	if ((r < 0) || (0 != strcmp(client_err_buf, ""))) {
4660 		ck_abort_msg(
4661 		    "%s connection to server [%s] port [%u] did not respond: [%s]",
4662 		    use_ssl ? "HTTPS" : "HTTP",
4663 		    server,
4664 		    port,
4665 		    client_err_buf);
4666 	}
4667 
4668 	client_ri = mg_get_response_info(client);
4669 	ck_assert(client_ri != NULL);
4670 
4671 	/* Check for status code 200 OK or 30? moved */
4672 	if ((client_ri->status_code < 300) || (client_ri->status_code > 308)) {
4673 		ck_assert_int_eq(client_ri->status_code, 200);
4674 	}
4675 
4676 	data_read = 0;
4677 	while (data_read < client_ri->content_length) {
4678 		r = mg_read(client, client_data_buf, sizeof(client_data_buf));
4679 		if (r > 0) {
4680 			data_read += r;
4681 		}
4682 	}
4683 
4684 	/* Nothing left to read */
4685 	r = mg_read(client, client_data_buf, sizeof(client_data_buf));
4686 	ck_assert_int_eq(r, 0);
4687 
4688 	mark_point();
4689 
4690 	mg_close_connection(client);
4691 
4692 	mark_point();
4693 }
4694 
4695 
4696 static void
minimal_http_client_impl(const char * server,uint16_t port,const char * uri)4697 minimal_http_client_impl(const char *server, uint16_t port, const char *uri)
4698 {
4699 	minimal_http_https_client_impl(server, port, 0, uri);
4700 }
4701 
4702 
4703 #if !defined(NO_SSL)
4704 static void
minimal_https_client_impl(const char * server,uint16_t port,const char * uri)4705 minimal_https_client_impl(const char *server, uint16_t port, const char *uri)
4706 {
4707 	minimal_http_https_client_impl(server, port, 1, uri);
4708 }
4709 #endif
4710 
4711 
START_TEST(test_minimal_client)4712 START_TEST(test_minimal_client)
4713 {
4714 	mark_point();
4715 
4716 	/* Initialize the library */
4717 	mg_init_library(0);
4718 
4719 	mark_point();
4720 
4721 	/* Call a test client */
4722 	minimal_http_client_impl("192.30.253.113" /* www.github.com */,
4723 	                         80,
4724 	                         "civetweb/civetweb/");
4725 
4726 	mark_point();
4727 
4728 	/* Un-initialize the library */
4729 	mg_exit_library();
4730 
4731 	mark_point();
4732 }
4733 END_TEST
4734 
4735 
START_TEST(test_minimal_tls_client)4736 START_TEST(test_minimal_tls_client)
4737 {
4738 	mark_point();
4739 
4740 #if !defined(NO_SSL) /* dont run https test if SSL is not enabled */
4741 
4742 #if (!defined(__MACH__) || defined(LOCAL_TEST)) && !defined(OPENSSL_API_1_1)
4743 	/* dont run on Travis OSX worker with OpenSSL 1.0 */
4744 
4745 	/* Initialize the library */
4746 	mg_init_library(2);
4747 
4748 	mark_point();
4749 
4750 	/* Call a test client */
4751 	minimal_https_client_impl("192.30.253.113" /* www.github.com */,
4752 	                          443,
4753 	                          "civetweb/civetweb/");
4754 
4755 	mark_point();
4756 
4757 	/* Un-initialize the library */
4758 	mg_exit_library();
4759 
4760 #endif
4761 #endif
4762 
4763 	mark_point();
4764 }
4765 END_TEST
4766 
4767 
4768 static int
minimal_test_request_handler(struct mg_connection * conn,void * cbdata)4769 minimal_test_request_handler(struct mg_connection *conn, void *cbdata)
4770 {
4771 	const char *msg = (const char *)cbdata;
4772 	unsigned long len = (unsigned long)strlen(msg);
4773 
4774 	mark_point();
4775 
4776 	mg_printf(conn,
4777 	          "HTTP/1.1 200 OK\r\n"
4778 	          "Content-Length: %lu\r\n"
4779 	          "Content-Type: text/plain\r\n"
4780 	          "Connection: close\r\n\r\n",
4781 	          len);
4782 
4783 	mg_write(conn, msg, len);
4784 
4785 	mark_point();
4786 
4787 	return 200;
4788 }
4789 
4790 
START_TEST(test_minimal_http_server_callback)4791 START_TEST(test_minimal_http_server_callback)
4792 {
4793 	/* This test should ensure the minimum server example in
4794 	 * docs/Embedding.md is still running. */
4795 
4796 	/* Server context handle */
4797 	struct mg_context *ctx;
4798 
4799 	mark_point();
4800 
4801 	/* Initialize the library */
4802 	mg_init_library(0);
4803 
4804 	/* Start the server */
4805 	ctx = test_mg_start(NULL, 0, NULL);
4806 	ck_assert(ctx != NULL);
4807 
4808 	/* Add some handler */
4809 	mg_set_request_handler(ctx,
4810 	                       "/hello",
4811 	                       minimal_test_request_handler,
4812 	                       (void *)"Hello world");
4813 	mg_set_request_handler(ctx,
4814 	                       "/8",
4815 	                       minimal_test_request_handler,
4816 	                       (void *)"Number eight");
4817 
4818 	/* Run the server for 15 seconds */
4819 	test_sleep(10);
4820 
4821 	/* Call a test client */
4822 	minimal_http_client_impl("127.0.0.1", 8080, "/hello");
4823 
4824 	/* Run the server for 15 seconds */
4825 	test_sleep(5);
4826 
4827 
4828 	/* Stop the server */
4829 	test_mg_stop(ctx);
4830 
4831 	/* Un-initialize the library */
4832 	mg_exit_library();
4833 
4834 	mark_point();
4835 }
4836 END_TEST
4837 
4838 
START_TEST(test_minimal_https_server_callback)4839 START_TEST(test_minimal_https_server_callback)
4840 {
4841 #if !defined(NO_SSL)
4842 	/* This test should show a HTTPS server with enhanced
4843 	 * security settings.
4844 	 *
4845 	 * Articles:
4846 	 * https://hynek.me/articles/hardening-your-web-servers-ssl-ciphers/
4847 	 *
4848 	 * Scanners:
4849 	 * https://securityheaders.io/
4850 	 * https://www.htbridge.com/ssl/
4851 	 * https://www.htbridge.com/websec/
4852 	 * https://www.ssllabs.com/ssltest/
4853 	 * https://www.qualys.com/forms/freescan/
4854 	 */
4855 
4856 	/* Server context handle */
4857 	struct mg_context *ctx;
4858 
4859 	/* Server start parameters for HTTPS */
4860 	const char *OPTIONS[32];
4861 	int opt_idx = 0;
4862 
4863 	/* HTTPS port - required */
4864 	OPTIONS[opt_idx++] = "listening_ports";
4865 	OPTIONS[opt_idx++] = "8443s";
4866 
4867 	/* path to certificate file - required */
4868 	OPTIONS[opt_idx++] = "ssl_certificate";
4869 	OPTIONS[opt_idx++] = locate_ssl_cert();
4870 
4871 #if defined(LOCAL_TEST) || defined(_WIN32)
4872 	/* Do not set this on Travis CI, since the build containers
4873 	 * contain older SSL libraries */
4874 
4875 	/* set minimum SSL version to TLS 1.2 - recommended */
4876 	OPTIONS[opt_idx++] = "ssl_protocol_version";
4877 	OPTIONS[opt_idx++] = "4";
4878 
4879 	/* set some modern ciphers - recommended */
4880 	OPTIONS[opt_idx++] = "ssl_cipher_list";
4881 	OPTIONS[opt_idx++] = "ECDH+AESGCM+AES256:!aNULL:!MD5:!DSS";
4882 #endif
4883 
4884 	/* set "HTTPS only" header - recommended */
4885 	OPTIONS[opt_idx++] = "strict_transport_security_max_age";
4886 	OPTIONS[opt_idx++] = "31622400";
4887 
4888 	/* end of options - required */
4889 	OPTIONS[opt_idx] = NULL;
4890 
4891 	mark_point();
4892 
4893 	/* Initialize the library */
4894 	mg_init_library(0);
4895 
4896 
4897 	/* Start the server */
4898 	ctx = test_mg_start(NULL, 0, OPTIONS);
4899 	ck_assert(ctx != NULL);
4900 
4901 	/* Add some handler */
4902 	mg_set_request_handler(ctx,
4903 	                       "/hello",
4904 	                       minimal_test_request_handler,
4905 	                       (void *)"Hello world");
4906 	mg_set_request_handler(ctx,
4907 	                       "/8",
4908 	                       minimal_test_request_handler,
4909 	                       (void *)"Number eight");
4910 
4911 	/* Run the server for 15 seconds */
4912 	test_sleep(10);
4913 
4914 	/* Call a test client */
4915 	minimal_https_client_impl("127.0.0.1", 8443, "/hello");
4916 
4917 	/* Run the server for 15 seconds */
4918 	test_sleep(5);
4919 
4920 
4921 	/* Stop the server */
4922 	test_mg_stop(ctx);
4923 
4924 	/* Un-initialize the library */
4925 	mg_exit_library();
4926 #endif
4927 	mark_point();
4928 }
4929 END_TEST
4930 
4931 
4932 #if !defined(REPLACE_CHECK_FOR_LOCAL_DEBUGGING)
4933 Suite *
make_public_server_suite(void)4934 make_public_server_suite(void)
4935 {
4936 	Suite *const suite = suite_create("PublicServer");
4937 
4938 	TCase *const tcase_checktestenv = tcase_create("Check test environment");
4939 	TCase *const tcase_initlib = tcase_create("Init library");
4940 	TCase *const tcase_startthreads = tcase_create("Start threads");
4941 	TCase *const tcase_minimal_http_svr = tcase_create("Minimal HTTP Server");
4942 	TCase *const tcase_minimal_https_svr = tcase_create("Minimal HTTPS Server");
4943 	TCase *const tcase_minimal_http_cli = tcase_create("Minimal HTTP Client");
4944 	TCase *const tcase_minimal_https_cli = tcase_create("Minimal HTTPS Client");
4945 	TCase *const tcase_startstophttp = tcase_create("Start Stop HTTP Server");
4946 	TCase *const tcase_startstophttp_ipv6 =
4947 	    tcase_create("Start Stop HTTP Server IPv6");
4948 	TCase *const tcase_startstophttps = tcase_create("Start Stop HTTPS Server");
4949 	TCase *const tcase_serverandclienttls = tcase_create("TLS Server Client");
4950 	TCase *const tcase_serverrequests = tcase_create("Server Requests");
4951 	TCase *const tcase_storebody = tcase_create("Store Body");
4952 	TCase *const tcase_handle_form = tcase_create("Handle Form");
4953 	TCase *const tcase_http_auth = tcase_create("HTTP Authentication");
4954 	TCase *const tcase_keep_alive = tcase_create("HTTP Keep Alive");
4955 	TCase *const tcase_error_handling = tcase_create("Error handling");
4956 	TCase *const tcase_error_log = tcase_create("Error logging");
4957 	TCase *const tcase_throttle = tcase_create("Limit speed");
4958 	TCase *const tcase_large_file = tcase_create("Large file");
4959 	TCase *const tcase_file_in_mem = tcase_create("File in memory");
4960 
4961 
4962 	tcase_add_test(tcase_checktestenv, test_the_test_environment);
4963 	tcase_set_timeout(tcase_checktestenv, civetweb_min_test_timeout);
4964 	suite_add_tcase(suite, tcase_checktestenv);
4965 
4966 	tcase_add_test(tcase_initlib, test_init_library);
4967 	tcase_set_timeout(tcase_initlib, civetweb_min_test_timeout);
4968 	suite_add_tcase(suite, tcase_initlib);
4969 
4970 	tcase_add_test(tcase_startthreads, test_threading);
4971 	tcase_set_timeout(tcase_startthreads, civetweb_min_test_timeout);
4972 	suite_add_tcase(suite, tcase_startthreads);
4973 
4974 	tcase_add_test(tcase_minimal_http_svr, test_minimal_http_server_callback);
4975 	tcase_set_timeout(tcase_minimal_http_svr, civetweb_min_server_test_timeout);
4976 	suite_add_tcase(suite, tcase_minimal_http_svr);
4977 
4978 	tcase_add_test(tcase_minimal_https_svr, test_minimal_https_server_callback);
4979 	tcase_set_timeout(tcase_minimal_https_svr,
4980 	                  civetweb_min_server_test_timeout);
4981 	suite_add_tcase(suite, tcase_minimal_https_svr);
4982 
4983 	tcase_add_test(tcase_minimal_http_cli, test_minimal_client);
4984 	tcase_set_timeout(tcase_minimal_http_cli, civetweb_min_server_test_timeout);
4985 	suite_add_tcase(suite, tcase_minimal_http_cli);
4986 
4987 	tcase_add_test(tcase_minimal_https_cli, test_minimal_tls_client);
4988 	tcase_set_timeout(tcase_minimal_https_cli,
4989 	                  civetweb_min_server_test_timeout);
4990 	suite_add_tcase(suite, tcase_minimal_https_cli);
4991 
4992 	tcase_add_test(tcase_startstophttp, test_mg_start_stop_http_server);
4993 	tcase_set_timeout(tcase_startstophttp, civetweb_min_server_test_timeout);
4994 	suite_add_tcase(suite, tcase_startstophttp);
4995 
4996 	tcase_add_test(tcase_startstophttp_ipv6,
4997 	               test_mg_start_stop_http_server_ipv6);
4998 	tcase_set_timeout(tcase_startstophttp_ipv6,
4999 	                  civetweb_min_server_test_timeout);
5000 	suite_add_tcase(suite, tcase_startstophttp_ipv6);
5001 
5002 	tcase_add_test(tcase_startstophttps, test_mg_start_stop_https_server);
5003 	tcase_set_timeout(tcase_startstophttps, civetweb_min_server_test_timeout);
5004 	suite_add_tcase(suite, tcase_startstophttps);
5005 
5006 	tcase_add_test(tcase_serverandclienttls, test_mg_server_and_client_tls);
5007 	tcase_set_timeout(tcase_serverandclienttls,
5008 	                  civetweb_min_server_test_timeout);
5009 	suite_add_tcase(suite, tcase_serverandclienttls);
5010 
5011 	tcase_add_test(tcase_serverrequests, test_request_handlers);
5012 	tcase_set_timeout(tcase_serverrequests, civetweb_mid_server_test_timeout);
5013 	suite_add_tcase(suite, tcase_serverrequests);
5014 
5015 	tcase_add_test(tcase_storebody, test_mg_store_body);
5016 	tcase_set_timeout(tcase_storebody, civetweb_mid_server_test_timeout);
5017 	suite_add_tcase(suite, tcase_storebody);
5018 
5019 	tcase_add_test(tcase_handle_form, test_handle_form);
5020 	tcase_set_timeout(tcase_handle_form, civetweb_mid_server_test_timeout);
5021 	suite_add_tcase(suite, tcase_handle_form);
5022 
5023 	tcase_add_test(tcase_http_auth, test_http_auth);
5024 	tcase_set_timeout(tcase_http_auth, civetweb_min_server_test_timeout);
5025 	suite_add_tcase(suite, tcase_http_auth);
5026 
5027 	tcase_add_test(tcase_keep_alive, test_keep_alive);
5028 	tcase_set_timeout(tcase_keep_alive, civetweb_mid_server_test_timeout);
5029 	suite_add_tcase(suite, tcase_keep_alive);
5030 
5031 	tcase_add_test(tcase_error_handling, test_error_handling);
5032 	tcase_set_timeout(tcase_error_handling, civetweb_mid_server_test_timeout);
5033 	suite_add_tcase(suite, tcase_error_handling);
5034 
5035 	tcase_add_test(tcase_error_log, test_error_log_file);
5036 	tcase_set_timeout(tcase_error_log, civetweb_mid_server_test_timeout);
5037 	suite_add_tcase(suite, tcase_error_log);
5038 
5039 	tcase_add_test(tcase_throttle, test_throttle);
5040 	tcase_set_timeout(tcase_throttle, civetweb_mid_server_test_timeout);
5041 	suite_add_tcase(suite, tcase_throttle);
5042 
5043 	tcase_add_test(tcase_large_file, test_large_file);
5044 	tcase_set_timeout(tcase_large_file, civetweb_mid_server_test_timeout);
5045 	suite_add_tcase(suite, tcase_large_file);
5046 
5047 	tcase_add_test(tcase_file_in_mem, test_file_in_memory);
5048 	tcase_set_timeout(tcase_file_in_mem, civetweb_mid_server_test_timeout);
5049 	suite_add_tcase(suite, tcase_file_in_mem);
5050 
5051 	return suite;
5052 }
5053 #endif
5054 
5055 
5056 #ifdef REPLACE_CHECK_FOR_LOCAL_DEBUGGING
5057 /* Used to debug test cases without using the check framework */
5058 /* Build command for Linux:
5059 gcc test/public_server.c src/civetweb.c -I include/ -I test/ -l pthread -l dl -D
5060 LOCAL_TEST -D REPLACE_CHECK_FOR_LOCAL_DEBUGGING -D MAIN_PUBLIC_SERVER=main
5061 */
5062 
5063 static int chk_ok = 0;
5064 static int chk_failed = 0;
5065 
5066 
5067 void
MAIN_PUBLIC_SERVER(void)5068 MAIN_PUBLIC_SERVER(void)
5069 {
5070 	unsigned f_avail = mg_check_feature(0xFF);
5071 	unsigned f_ret = mg_init_library(f_avail);
5072 	ck_assert_uint_eq(f_ret, f_avail);
5073 
5074 	test_handle_form(0);
5075 
5076 	test_the_test_environment(0);
5077 	test_threading(0);
5078 
5079 	test_minimal_client(0);
5080 
5081 	test_mg_start_stop_http_server(0);
5082 	test_mg_start_stop_https_server(0);
5083 	test_request_handlers(0);
5084 	test_mg_store_body(0);
5085 	test_mg_server_and_client_tls(0);
5086 	test_handle_form(0);
5087 	test_http_auth(0);
5088 	test_keep_alive(0);
5089 	test_error_handling(0);
5090 	test_error_log_file(0);
5091 	test_throttle(0);
5092 	test_large_file(0);
5093 	test_file_in_memory(0);
5094 
5095 	mg_exit_library();
5096 
5097 	printf("\nok: %i\nfailed: %i\n\n", chk_ok, chk_failed);
5098 }
5099 
5100 void
_ck_assert_failed(const char * file,int line,const char * expr,...)5101 _ck_assert_failed(const char *file, int line, const char *expr, ...)
5102 {
5103 	va_list va;
5104 	va_start(va, expr);
5105 	fprintf(stderr, "Error: %s, line %i\n", file, line); /* breakpoint here ! */
5106 	vfprintf(stderr, expr, va);
5107 	fprintf(stderr, "\n\n");
5108 	va_end(va);
5109 	chk_failed++;
5110 }
5111 
5112 void
_ck_assert_msg(int cond,const char * file,int line,const char * expr,...)5113 _ck_assert_msg(int cond, const char *file, int line, const char *expr, ...)
5114 {
5115 	va_list va;
5116 
5117 	if (cond) {
5118 		chk_ok++;
5119 		return;
5120 	}
5121 
5122 	va_start(va, expr);
5123 	fprintf(stderr, "Error: %s, line %i\n", file, line); /* breakpoint here ! */
5124 	vfprintf(stderr, expr, va);
5125 	fprintf(stderr, "\n\n");
5126 	va_end(va);
5127 	chk_failed++;
5128 }
5129 
5130 void
_mark_point(const char * file,int line)5131 _mark_point(const char *file, int line)
5132 {
5133 	chk_ok++;
5134 }
5135 
5136 void
tcase_fn_start(const char * fname,const char * file,int line)5137 tcase_fn_start(const char *fname, const char *file, int line)
5138 {
5139 }
suite_add_tcase(Suite * s,TCase * tc)5140 void suite_add_tcase(Suite *s, TCase *tc){};
_tcase_add_test(TCase * tc,TFun tf,const char * fname,int _signal,int allowed_exit_value,int start,int end)5141 void _tcase_add_test(TCase *tc,
5142                      TFun tf,
5143                      const char *fname,
5144                      int _signal,
5145                      int allowed_exit_value,
5146                      int start,
5147                      int end){};
5148 TCase *
tcase_create(const char * name)5149 tcase_create(const char *name)
5150 {
5151 	return NULL;
5152 };
5153 Suite *
suite_create(const char * name)5154 suite_create(const char *name)
5155 {
5156 	return NULL;
5157 };
tcase_set_timeout(TCase * tc,double timeout)5158 void tcase_set_timeout(TCase *tc, double timeout){};
5159 
5160 #endif
5161