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