1 /* Copyright (c) 2015-2017 the Civetweb developers
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:
10 * The above copyright notice and this permission notice shall be included in
11 * all copies or substantial portions of the Software.
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
23 #ifndef _CRT_SECURE_NO_WARNINGS
24 #define _CRT_SECURE_NO_WARNINGS
34 #include <sys/types.h>
37 #include "public_server.h"
42 #define test_sleep(x) (Sleep((x)*1000))
45 #define test_sleep(x) (sleep(x))
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)
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
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"
66 locate_path(const char *a_path
)
68 static char r_path
[256];
72 sprintf(r_path
, "%s\\", a_path
);
75 sprintf(r_path
, "..\\..\\..\\%s\\", a_path
);
76 /* TODO: the different paths
77 * used in the different test
78 * system is an unsolved
83 sprintf(r_path
, "%s/", a_path
);
88 a_path
); // TODO: fix path in CI test environment
96 #define locate_resources() locate_path("resources")
97 #define locate_test_exes() locate_path("output")
101 locate_ssl_cert(void)
103 static char cert_path
[256];
104 const char *res
= locate_resources();
107 ck_assert(res
!= NULL
);
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 */
113 strcpy(cert_path
, res
);
114 strcat(cert_path
, "ssl_cert.pem");
120 wait_not_null(void *volatile *data
)
123 for (i
= 0; i
< 100; i
++) {
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"
139 ck_abort_msg("wait_not_null failed (%i sec)", i
);
143 #if defined(__MINGW32__) || defined(__GNUC__)
144 #pragma GCC diagnostic pop
149 START_TEST(test_the_test_environment
)
156 const char *ssl_cert
= locate_ssl_cert();
158 memset(wd
, 0, sizeof(wd
));
159 memset(buf
, 0, sizeof(buf
));
161 /* Get the current working directory */
163 (void)GetCurrentDirectoryA(sizeof(wd
), wd
);
164 wd
[sizeof(wd
) - 1] = 0;
166 (void)getcwd(wd
, sizeof(wd
));
167 wd
[sizeof(wd
) - 1] = 0;
170 /* Check the pem file */
174 strcat(buf
, ssl_cert
);
175 f
= fopen(buf
, "rb");
179 strcat(buf
, ssl_cert
);
186 fprintf(stderr
, "%s not found", buf
);
189 /* Check the test dir */
192 strcat(buf
, "\\test");
195 strcat(buf
, "/test");
198 memset(&st
, 0, sizeof(st
));
199 ret
= stat(buf
, &st
);
202 fprintf(stderr
, "%s not found", buf
);
207 /* Try to copy the files required for AppVeyor */
208 #if defined(_WIN64) || defined(__MINGW64__)
209 (void)system("cmd /c copy C:\\OpenSSL-Win64\\libeay32.dll libeay32.dll");
210 (void)system("cmd /c copy C:\\OpenSSL-Win64\\libssl32.dll libssl32.dll");
211 (void)system("cmd /c copy C:\\OpenSSL-Win64\\ssleay32.dll ssleay32.dll");
212 (void)system("cmd /c copy C:\\OpenSSL-Win64\\libeay32.dll libeay64.dll");
213 (void)system("cmd /c copy C:\\OpenSSL-Win64\\libssl32.dll libssl64.dll");
214 (void)system("cmd /c copy C:\\OpenSSL-Win64\\ssleay32.dll ssleay64.dll");
216 (void)system("cmd /c copy C:\\OpenSSL-Win32\\libeay32.dll libeay32.dll");
217 (void)system("cmd /c copy C:\\OpenSSL-Win32\\libssl32.dll libssl32.dll");
218 (void)system("cmd /c copy C:\\OpenSSL-Win32\\ssleay32.dll ssleay32.dll");
225 static void *threading_data
= 0;
228 test_thread_func_t(void *param
)
230 ck_assert_ptr_eq(param
, &threading_data
);
231 ck_assert_ptr_eq(threading_data
, NULL
);
232 threading_data
= &threading_data
;
237 START_TEST(test_threading
)
241 threading_data
= NULL
;
244 ok
= mg_start_thread(test_thread_func_t
, &threading_data
);
245 ck_assert_int_eq(ok
, 0);
247 wait_not_null(&threading_data
);
248 ck_assert_ptr_eq(threading_data
, &threading_data
);
254 log_msg_func(const struct mg_connection
*conn
, const char *message
)
256 struct mg_context
*ctx
;
259 ck_assert(conn
!= NULL
);
260 ctx
= mg_get_context(conn
);
261 ck_assert(ctx
!= NULL
);
262 ud
= (char *)mg_get_user_data(ctx
);
264 strncpy(ud
, message
, 255);
268 printf("LOG_MSG_FUNC: %s\n", message
);
271 return 1; /* Return 1 means "already handled" */
276 test_log_message(const struct mg_connection
*conn
, const char *message
)
280 printf("LOG_MESSAGE: %s\n", message
);
283 return 0; /* Return 0 means "not yet handled" */
287 static struct mg_context
*
288 test_mg_start(const struct mg_callbacks
*callbacks
,
290 const char **configuration_options
)
292 struct mg_context
*ctx
;
293 struct mg_callbacks cb
;
296 memcpy(&cb
, callbacks
, sizeof(cb
));
298 memset(&cb
, 0, sizeof(cb
));
301 if (cb
.log_message
== NULL
) {
302 cb
.log_message
= test_log_message
;
306 test_sleep(SLEEP_BEFORE_MG_START
);
308 ctx
= mg_start(&cb
, user_data
, configuration_options
);
311 /* Give the server some time to start in the test VM */
312 /* Don't need to do this if mg_start failed */
313 test_sleep(SLEEP_AFTER_MG_START
);
322 test_mg_stop(struct mg_context
*ctx
)
325 /* For unknown reasons, there are sporadic hands
326 * for OSX if mark_point is called here */
327 test_sleep(SLEEP_BEFORE_MG_STOP
);
329 test_sleep(SLEEP_AFTER_MG_STOP
);
332 test_sleep(SLEEP_BEFORE_MG_STOP
);
336 test_sleep(SLEEP_AFTER_MG_STOP
);
343 test_mg_start_stop_http_server_impl(int ipv6
)
345 struct mg_context
*ctx
;
346 const char *OPTIONS
[16];
348 const char *localhost_name
= ((ipv6
) ? "[::1]" : "127.0.0.1");
350 #if defined(MG_LEGACY_INTERFACE)
355 struct mg_callbacks callbacks
;
358 struct mg_connection
*client_conn
;
359 char client_err
[256];
360 const struct mg_request_info
*client_ri
;
362 struct mg_server_ports portinfo
[8];
366 #if !defined(NO_FILES)
367 OPTIONS
[optcnt
++] = "document_root";
368 OPTIONS
[optcnt
++] = ".";
370 OPTIONS
[optcnt
++] = "listening_ports";
371 OPTIONS
[optcnt
++] = ((ipv6
) ? "+8080" : "8080");
374 #if defined(MG_LEGACY_INTERFACE)
375 memset(ports
, 0, sizeof(ports
));
376 memset(ssl
, 0, sizeof(ssl
));
378 memset(portinfo
, 0, sizeof(portinfo
));
379 memset(&callbacks
, 0, sizeof(callbacks
));
380 memset(errmsg
, 0, sizeof(errmsg
));
382 callbacks
.log_message
= log_msg_func
;
384 ctx
= test_mg_start(&callbacks
, (void *)errmsg
, OPTIONS
);
386 ck_assert_str_eq(errmsg
, "");
387 ck_assert(ctx
!= NULL
);
389 #if defined(MG_LEGACY_INTERFACE)
390 ports_cnt
= mg_get_ports(ctx
, 16, ports
, ssl
);
391 ck_assert_uint_eq(ports_cnt
, 1);
392 ck_assert_int_eq(ports
[0], 8080);
393 ck_assert_int_eq(ssl
[0], 0);
394 ck_assert_int_eq(ports
[1], 0);
395 ck_assert_int_eq(ssl
[1], 0);
398 ret
= mg_get_server_ports(ctx
, 0, portinfo
);
399 ck_assert_int_lt(ret
, 0);
400 ck_assert_int_eq(portinfo
[0].protocol
, 0);
401 ck_assert_int_eq(portinfo
[0].port
, 0);
402 ck_assert_int_eq(portinfo
[0].is_ssl
, 0);
403 ck_assert_int_eq(portinfo
[0].is_redirect
, 0);
404 ck_assert_int_eq(portinfo
[1].protocol
, 0);
405 ck_assert_int_eq(portinfo
[1].port
, 0);
406 ck_assert_int_eq(portinfo
[1].is_ssl
, 0);
407 ck_assert_int_eq(portinfo
[1].is_redirect
, 0);
409 ret
= mg_get_server_ports(ctx
, 4, portinfo
);
410 ck_assert_int_eq(ret
, 1);
412 ck_assert_int_eq(portinfo
[0].protocol
, 3);
414 ck_assert_int_eq(portinfo
[0].protocol
, 1);
416 ck_assert_int_eq(portinfo
[0].port
, 8080);
417 ck_assert_int_eq(portinfo
[0].is_ssl
, 0);
418 ck_assert_int_eq(portinfo
[0].is_redirect
, 0);
419 ck_assert_int_eq(portinfo
[1].protocol
, 0);
420 ck_assert_int_eq(portinfo
[1].port
, 0);
421 ck_assert_int_eq(portinfo
[1].is_ssl
, 0);
422 ck_assert_int_eq(portinfo
[1].is_redirect
, 0);
426 /* HTTP 1.0 GET request */
427 memset(client_err
, 0, sizeof(client_err
));
428 client_conn
= mg_connect_client(
429 localhost_name
, 8080, 0, client_err
, sizeof(client_err
));
431 ck_assert_str_eq(client_err
, "");
432 ck_assert(client_conn
!= NULL
);
434 mg_printf(client_conn
, "GET / HTTP/1.0\r\n\r\n");
436 mg_get_response(client_conn
, client_err
, sizeof(client_err
), 10000);
437 ck_assert_int_ge(client_res
, 0);
438 ck_assert_str_eq(client_err
, "");
439 client_ri
= mg_get_request_info(client_conn
);
440 ck_assert(client_ri
!= NULL
);
442 #if defined(NO_FILES)
443 ck_assert_str_eq(client_ri
->local_uri
, "404");
445 ck_assert_str_eq(client_ri
->local_uri
, "200");
446 /* TODO: ck_assert_str_eq(client_ri->request_method, "HTTP/1.0"); */
447 client_res
= (int)mg_read(client_conn
, client_err
, sizeof(client_err
));
448 ck_assert_int_gt(client_res
, 0);
449 ck_assert_int_le(client_res
, sizeof(client_err
));
451 mg_close_connection(client_conn
);
455 /* HTTP 1.1 GET request */
456 memset(client_err
, 0, sizeof(client_err
));
457 client_conn
= mg_connect_client(
458 localhost_name
, 8080, 0, client_err
, sizeof(client_err
));
460 ck_assert_str_eq(client_err
, "");
461 ck_assert(client_conn
!= NULL
);
463 mg_printf(client_conn
, "GET / HTTP/1.1\r\n");
464 mg_printf(client_conn
, "Host: localhost:8080\r\n");
465 mg_printf(client_conn
, "Connection: close\r\n\r\n");
467 mg_get_response(client_conn
, client_err
, sizeof(client_err
), 10000);
468 ck_assert_int_ge(client_res
, 0);
469 ck_assert_str_eq(client_err
, "");
470 client_ri
= mg_get_request_info(client_conn
);
471 ck_assert(client_ri
!= NULL
);
473 #if defined(NO_FILES)
474 ck_assert_str_eq(client_ri
->local_uri
, "404");
476 ck_assert_str_eq(client_ri
->local_uri
, "200");
477 /* TODO: ck_assert_str_eq(client_ri->request_method, "HTTP/1.0"); */
478 client_res
= (int)mg_read(client_conn
, client_err
, sizeof(client_err
));
479 ck_assert_int_gt(client_res
, 0);
480 ck_assert_int_le(client_res
, sizeof(client_err
));
482 mg_close_connection(client_conn
);
487 /* HTTP 1.7 GET request - this HTTP version does not exist */
488 memset(client_err
, 0, sizeof(client_err
));
489 client_conn
= mg_connect_client(
490 localhost_name
, 8080, 0, client_err
, sizeof(client_err
));
492 ck_assert_str_eq(client_err
, "");
493 ck_assert(client_conn
!= NULL
);
495 mg_printf(client_conn
, "GET / HTTP/1.7\r\n");
496 mg_printf(client_conn
, "Host: localhost:8080\r\n");
497 mg_printf(client_conn
, "Connection: close\r\n\r\n");
499 mg_get_response(client_conn
, client_err
, sizeof(client_err
), 10000);
500 ck_assert_int_ge(client_res
, 0);
501 ck_assert_str_eq(client_err
, "");
502 client_ri
= mg_get_request_info(client_conn
);
503 ck_assert(client_ri
!= NULL
);
505 /* Response must be 505 HTTP Version not supported */
506 ck_assert_str_eq(client_ri
->local_uri
, "505");
507 mg_close_connection(client_conn
);
512 /* HTTP request with multiline header.
513 * Multiline header are obsolete with RFC 7230 section-3.2.4
514 * and must return "400 Bad Request" */
515 memset(client_err
, 0, sizeof(client_err
));
516 client_conn
= mg_connect_client(
517 localhost_name
, 8080, 0, client_err
, sizeof(client_err
));
519 ck_assert_str_eq(client_err
, "");
520 ck_assert(client_conn
!= NULL
);
522 mg_printf(client_conn
, "GET / HTTP/1.1\r\n");
523 mg_printf(client_conn
, "Host: localhost:8080\r\n");
524 mg_printf(client_conn
, "X-Obsolete-Header: something\r\nfor nothing\r\n");
525 mg_printf(client_conn
, "Connection: close\r\n\r\n");
527 mg_get_response(client_conn
, client_err
, sizeof(client_err
), 10000);
528 ck_assert_int_ge(client_res
, 0);
529 ck_assert_str_eq(client_err
, "");
530 client_ri
= mg_get_request_info(client_conn
);
531 ck_assert(client_ri
!= NULL
);
533 /* Response must be 400 Bad Request */
534 ck_assert_str_eq(client_ri
->local_uri
, "400");
535 mg_close_connection(client_conn
);
545 START_TEST(test_mg_start_stop_http_server
)
548 test_mg_start_stop_http_server_impl(0);
554 START_TEST(test_mg_start_stop_http_server_ipv6
)
557 #if defined(USE_IPV6)
558 test_mg_start_stop_http_server_impl(1);
565 START_TEST(test_mg_start_stop_https_server
)
569 struct mg_context
*ctx
;
571 #if defined(MG_LEGACY_INTERFACE)
576 struct mg_callbacks callbacks
;
579 const char *OPTIONS
[8]; /* initializer list here is rejected by CI test */
581 const char *ssl_cert
= locate_ssl_cert();
583 struct mg_connection
*client_conn
;
584 char client_err
[256];
585 const struct mg_request_info
*client_ri
;
587 struct mg_server_ports portinfo
[8];
589 ck_assert(ssl_cert
!= NULL
);
591 memset((void *)OPTIONS
, 0, sizeof(OPTIONS
));
592 #if !defined(NO_FILES)
593 OPTIONS
[opt_idx
++] = "document_root";
594 OPTIONS
[opt_idx
++] = ".";
596 OPTIONS
[opt_idx
++] = "listening_ports";
597 OPTIONS
[opt_idx
++] = "8080r,8443s";
598 OPTIONS
[opt_idx
++] = "ssl_certificate";
599 OPTIONS
[opt_idx
++] = ssl_cert
;
601 ck_assert_int_le(opt_idx
, (int)(sizeof(OPTIONS
) / sizeof(OPTIONS
[0])));
602 ck_assert(OPTIONS
[sizeof(OPTIONS
) / sizeof(OPTIONS
[0]) - 1] == NULL
);
603 ck_assert(OPTIONS
[sizeof(OPTIONS
) / sizeof(OPTIONS
[0]) - 2] == NULL
);
605 #if defined(MG_LEGACY_INTERFACE)
606 memset(ports
, 0, sizeof(ports
));
607 memset(ssl
, 0, sizeof(ssl
));
609 memset(portinfo
, 0, sizeof(portinfo
));
610 memset(&callbacks
, 0, sizeof(callbacks
));
611 memset(errmsg
, 0, sizeof(errmsg
));
613 callbacks
.log_message
= log_msg_func
;
615 ctx
= test_mg_start(&callbacks
, (void *)errmsg
, OPTIONS
);
617 ck_assert_str_eq(errmsg
, "");
618 ck_assert(ctx
!= NULL
);
620 #if defined(MG_LEGACY_INTERFACE)
621 ports_cnt
= mg_get_ports(ctx
, 16, ports
, ssl
);
622 ck_assert_uint_eq(ports_cnt
, 2);
623 ck_assert_int_eq(ports
[0], 8080);
624 ck_assert_int_eq(ssl
[0], 0);
625 ck_assert_int_eq(ports
[1], 8443);
626 ck_assert_int_eq(ssl
[1], 1);
627 ck_assert_int_eq(ports
[2], 0);
628 ck_assert_int_eq(ssl
[2], 0);
631 ret
= mg_get_server_ports(ctx
, 0, portinfo
);
632 ck_assert_int_lt(ret
, 0);
633 ck_assert_int_eq(portinfo
[0].protocol
, 0);
634 ck_assert_int_eq(portinfo
[0].port
, 0);
635 ck_assert_int_eq(portinfo
[0].is_ssl
, 0);
636 ck_assert_int_eq(portinfo
[0].is_redirect
, 0);
637 ck_assert_int_eq(portinfo
[1].protocol
, 0);
638 ck_assert_int_eq(portinfo
[1].port
, 0);
639 ck_assert_int_eq(portinfo
[1].is_ssl
, 0);
640 ck_assert_int_eq(portinfo
[1].is_redirect
, 0);
642 ret
= mg_get_server_ports(ctx
, 4, portinfo
);
643 ck_assert_int_eq(ret
, 2);
644 ck_assert_int_eq(portinfo
[0].protocol
, 1);
645 ck_assert_int_eq(portinfo
[0].port
, 8080);
646 ck_assert_int_eq(portinfo
[0].is_ssl
, 0);
647 ck_assert_int_eq(portinfo
[0].is_redirect
, 1);
648 ck_assert_int_eq(portinfo
[1].protocol
, 1);
649 ck_assert_int_eq(portinfo
[1].port
, 8443);
650 ck_assert_int_eq(portinfo
[1].is_ssl
, 1);
651 ck_assert_int_eq(portinfo
[1].is_redirect
, 0);
652 ck_assert_int_eq(portinfo
[2].protocol
, 0);
653 ck_assert_int_eq(portinfo
[2].port
, 0);
654 ck_assert_int_eq(portinfo
[2].is_ssl
, 0);
655 ck_assert_int_eq(portinfo
[2].is_redirect
, 0);
659 memset(client_err
, 0, sizeof(client_err
));
661 mg_connect_client("127.0.0.1", 8443, 1, client_err
, sizeof(client_err
));
663 ck_assert_str_eq(client_err
, "");
664 ck_assert(client_conn
!= NULL
);
666 mg_printf(client_conn
, "GET / HTTP/1.0\r\n\r\n");
668 mg_get_response(client_conn
, client_err
, sizeof(client_err
), 10000);
669 ck_assert_int_ge(client_res
, 0);
670 ck_assert_str_eq(client_err
, "");
671 client_ri
= mg_get_request_info(client_conn
);
672 ck_assert(client_ri
!= NULL
);
674 #if defined(NO_FILES)
675 ck_assert_str_eq(client_ri
->local_uri
, "404");
677 ck_assert_str_eq(client_ri
->local_uri
, "200");
678 /* TODO: ck_assert_str_eq(client_ri->request_method, "HTTP/1.0"); */
679 client_res
= (int)mg_read(client_conn
, client_err
, sizeof(client_err
));
680 ck_assert_int_gt(client_res
, 0);
681 ck_assert_int_le(client_res
, sizeof(client_err
));
683 mg_close_connection(client_conn
);
694 START_TEST(test_mg_server_and_client_tls
)
698 struct mg_context
*ctx
;
701 struct mg_server_ports ports
[16];
702 struct mg_callbacks callbacks
;
705 struct mg_connection
*client_conn
;
706 char client_err
[256];
707 const struct mg_request_info
*client_ri
;
709 struct mg_client_options client_options
;
711 const char *OPTIONS
[32]; /* initializer list here is rejected by CI test */
713 char server_cert
[256];
714 char client_cert
[256];
715 const char *res_dir
= locate_resources();
717 ck_assert(res_dir
!= NULL
);
718 strcpy(server_cert
, res_dir
);
719 strcpy(client_cert
, res_dir
);
721 strcat(server_cert
, "cert\\server.pem");
722 strcat(client_cert
, "cert\\client.pem");
724 strcat(server_cert
, "cert/server.pem");
725 strcat(client_cert
, "cert/client.pem");
728 memset((void *)OPTIONS
, 0, sizeof(OPTIONS
));
729 #if !defined(NO_FILES)
730 OPTIONS
[opt_idx
++] = "document_root";
731 OPTIONS
[opt_idx
++] = ".";
733 OPTIONS
[opt_idx
++] = "listening_ports";
734 OPTIONS
[opt_idx
++] = "8080r,8443s";
735 OPTIONS
[opt_idx
++] = "ssl_certificate";
736 OPTIONS
[opt_idx
++] = server_cert
;
737 OPTIONS
[opt_idx
++] = "ssl_verify_peer";
738 OPTIONS
[opt_idx
++] = "yes";
739 OPTIONS
[opt_idx
++] = "ssl_ca_file";
740 OPTIONS
[opt_idx
++] = client_cert
;
742 ck_assert_int_le(opt_idx
, (int)(sizeof(OPTIONS
) / sizeof(OPTIONS
[0])));
743 ck_assert(OPTIONS
[sizeof(OPTIONS
) / sizeof(OPTIONS
[0]) - 1] == NULL
);
744 ck_assert(OPTIONS
[sizeof(OPTIONS
) / sizeof(OPTIONS
[0]) - 2] == NULL
);
746 memset(ports
, 0, sizeof(ports
));
747 memset(&callbacks
, 0, sizeof(callbacks
));
748 memset(errmsg
, 0, sizeof(errmsg
));
750 callbacks
.log_message
= log_msg_func
;
752 ctx
= test_mg_start(&callbacks
, (void *)errmsg
, OPTIONS
);
754 ck_assert_str_eq(errmsg
, "");
755 ck_assert(ctx
!= NULL
);
757 ports_cnt
= mg_get_server_ports(ctx
, 16, ports
);
758 ck_assert_int_eq(ports_cnt
, 2);
759 ck_assert_int_eq(ports
[0].protocol
, 1);
760 ck_assert_int_eq(ports
[0].port
, 8080);
761 ck_assert_int_eq(ports
[0].is_ssl
, 0);
762 ck_assert_int_eq(ports
[0].is_redirect
, 1);
763 ck_assert_int_eq(ports
[1].protocol
, 1);
764 ck_assert_int_eq(ports
[1].port
, 8443);
765 ck_assert_int_eq(ports
[1].is_ssl
, 1);
766 ck_assert_int_eq(ports
[1].is_redirect
, 0);
767 ck_assert_int_eq(ports
[2].protocol
, 0);
768 ck_assert_int_eq(ports
[2].port
, 0);
769 ck_assert_int_eq(ports
[2].is_ssl
, 0);
770 ck_assert_int_eq(ports
[2].is_redirect
, 0);
774 memset(client_err
, 0, sizeof(client_err
));
776 mg_connect_client("127.0.0.1", 8443, 1, client_err
, sizeof(client_err
));
778 ck_assert_str_ne(client_err
, "");
779 ck_assert(client_conn
== NULL
);
781 memset(client_err
, 0, sizeof(client_err
));
782 memset(&client_options
, 0, sizeof(client_options
));
783 client_options
.host
= "127.0.0.1";
784 client_options
.port
= 8443;
785 client_options
.client_cert
= client_cert
;
786 client_options
.server_cert
= server_cert
;
788 client_conn
= mg_connect_client_secure(&client_options
,
792 ck_assert_str_eq(client_err
, "");
793 ck_assert(client_conn
!= NULL
);
795 mg_printf(client_conn
, "GET / HTTP/1.0\r\n\r\n");
797 mg_get_response(client_conn
, client_err
, sizeof(client_err
), 10000);
798 ck_assert_int_ge(client_res
, 0);
799 ck_assert_str_eq(client_err
, "");
800 client_ri
= mg_get_request_info(client_conn
);
801 ck_assert(client_ri
!= NULL
);
803 #if defined(NO_FILES)
804 ck_assert_str_eq(client_ri
->local_uri
, "404");
806 ck_assert_str_eq(client_ri
->local_uri
, "200");
807 /* TODO: ck_assert_str_eq(client_ri->request_method, "HTTP/1.0"); */
808 client_res
= (int)mg_read(client_conn
, client_err
, sizeof(client_err
));
809 ck_assert_int_gt(client_res
, 0);
810 ck_assert_int_le(client_res
, sizeof(client_err
));
812 mg_close_connection(client_conn
);
814 /* TODO: A client API using a client certificate is missing */
825 static struct mg_context
*g_ctx
;
828 request_test_handler(struct mg_connection
*conn
, void *cbdata
)
832 const struct mg_request_info
*ri
;
833 struct mg_context
*ctx
;
835 void *dummy
= malloc(1);
837 ctx
= mg_get_context(conn
);
838 ud
= mg_get_user_data(ctx
);
839 ri
= mg_get_request_info(conn
);
841 ck_assert(ri
!= NULL
);
842 ck_assert(ctx
== g_ctx
);
843 ck_assert(ud
== &g_ctx
);
845 ck_assert(dummy
!= NULL
);
847 mg_set_user_connection_data(conn
, (void *)&dummy
);
848 cud
= mg_get_user_connection_data(conn
);
849 ck_assert_ptr_eq((void *)cud
, (void *)&dummy
);
851 mg_set_user_connection_data(conn
, (void *)NULL
);
852 cud
= mg_get_user_connection_data(conn
);
853 ck_assert_ptr_eq((void *)cud
, (void *)NULL
);
857 ck_assert_ptr_eq((void *)cbdata
, (void *)(ptrdiff_t)7);
858 strcpy(chunk_data
, "123456789A123456789B123456789C");
861 "HTTP/1.1 200 OK\r\n"
862 "Transfer-Encoding: chunked\r\n"
863 "Content-Type: text/plain\r\n\r\n");
865 for (i
= 1; i
<= 10; i
++) {
866 mg_printf(conn
, "%x\r\n", i
);
867 mg_write(conn
, chunk_data
, (unsigned)i
);
868 mg_printf(conn
, "\r\n");
871 mg_printf(conn
, "0\r\n\r\n");
879 /****************************************************************************/
880 /* WEBSOCKET SERVER */
881 /****************************************************************************/
882 static const char *websocket_welcome_msg
= "websocket welcome\n";
883 static const size_t websocket_welcome_msg_len
=
884 18 /* strlen(websocket_welcome_msg) */;
885 static const char *websocket_goodbye_msg
= "websocket bye\n";
886 static const size_t websocket_goodbye_msg_len
=
887 14 /* strlen(websocket_goodbye_msg) */;
892 WS_TEST_TRACE(const char *f
, ...)
900 #define WS_TEST_TRACE(...)
905 websock_server_connect(const struct mg_connection
*conn
, void *udata
)
909 ck_assert_ptr_eq((void *)udata
, (void *)(ptrdiff_t)7531);
910 WS_TEST_TRACE("Server: Websocket connected\n");
913 return 0; /* return 0 to accept every connection */
918 websock_server_ready(struct mg_connection
*conn
, void *udata
)
920 ck_assert_ptr_eq((void *)udata
, (void *)(ptrdiff_t)7531);
921 ck_assert_ptr_ne((void *)conn
, (void *)NULL
);
922 WS_TEST_TRACE("Server: Websocket ready\n");
924 /* Send websocket welcome message */
925 mg_lock_connection(conn
);
926 mg_websocket_write(conn
,
927 WEBSOCKET_OPCODE_TEXT
,
928 websocket_welcome_msg
,
929 websocket_welcome_msg_len
);
930 mg_unlock_connection(conn
);
932 WS_TEST_TRACE("Server: Websocket ready X\n");
937 #define long_ws_buf_len_16 (500)
938 #define long_ws_buf_len_64 (70000)
939 static char long_ws_buf
[long_ws_buf_len_64
];
943 websock_server_data(struct mg_connection
*conn
,
951 ck_assert_ptr_eq((void *)udata
, (void *)(ptrdiff_t)7531);
952 WS_TEST_TRACE("Server: Got %u bytes from the client\n", (unsigned)data_len
);
954 if (data_len
== 3 && !memcmp(data
, "bye", 3)) {
955 /* Send websocket goodbye message */
956 mg_lock_connection(conn
);
957 mg_websocket_write(conn
,
958 WEBSOCKET_OPCODE_TEXT
,
959 websocket_goodbye_msg
,
960 websocket_goodbye_msg_len
);
961 mg_unlock_connection(conn
);
962 } else if (data_len
== 5 && !memcmp(data
, "data1", 5)) {
963 mg_lock_connection(conn
);
964 mg_websocket_write(conn
, WEBSOCKET_OPCODE_TEXT
, "ok1", 3);
965 mg_unlock_connection(conn
);
966 } else if (data_len
== 5 && !memcmp(data
, "data2", 5)) {
967 mg_lock_connection(conn
);
968 mg_websocket_write(conn
, WEBSOCKET_OPCODE_TEXT
, "ok 2", 4);
969 mg_unlock_connection(conn
);
970 } else if (data_len
== 5 && !memcmp(data
, "data3", 5)) {
971 mg_lock_connection(conn
);
972 mg_websocket_write(conn
, WEBSOCKET_OPCODE_TEXT
, "ok - 3", 6);
973 mg_unlock_connection(conn
);
974 } else if (data_len
== long_ws_buf_len_16
) {
975 ck_assert(memcmp(data
, long_ws_buf
, long_ws_buf_len_16
) == 0);
976 mg_lock_connection(conn
);
977 mg_websocket_write(conn
,
978 WEBSOCKET_OPCODE_BINARY
,
981 mg_unlock_connection(conn
);
982 } else if (data_len
== long_ws_buf_len_64
) {
983 ck_assert(memcmp(data
, long_ws_buf
, long_ws_buf_len_64
) == 0);
984 mg_lock_connection(conn
);
985 mg_websocket_write(conn
,
986 WEBSOCKET_OPCODE_BINARY
,
989 mg_unlock_connection(conn
);
992 #if defined(__MINGW32__) || defined(__GNUC__)
993 #pragma GCC diagnostic push
994 #pragma GCC diagnostic ignored "-Wunreachable-code"
997 #pragma clang diagnostic push
998 #pragma clang diagnostic ignored "-Wunreachable-code"
1001 ck_abort_msg("Got unexpected message from websocket client");
1007 #pragma clang diagnostic pop
1009 #if defined(__MINGW32__) || defined(__GNUC__)
1010 #pragma GCC diagnostic pop
1015 return 1; /* return 1 to keep the connetion open */
1020 websock_server_close(const struct mg_connection
*conn
, void *udata
)
1023 ck_assert_ptr_eq((void *)udata
, (void *)(ptrdiff_t)7531);
1024 WS_TEST_TRACE("Server: Close connection\n");
1026 /* Can not send a websocket goodbye message here -
1027 * the connection is already closed */
1037 /****************************************************************************/
1038 /* WEBSOCKET CLIENT */
1039 /****************************************************************************/
1040 struct tclient_data
{
1049 websocket_client_data_handler(struct mg_connection
*conn
,
1055 struct mg_context
*ctx
= mg_get_context(conn
);
1056 struct tclient_data
*pclient_data
=
1057 (struct tclient_data
*)mg_get_user_data(ctx
);
1059 ck_assert_ptr_eq(user_data
, (void *)pclient_data
);
1061 ck_assert(pclient_data
!= NULL
);
1062 ck_assert_int_gt(flags
, 128);
1063 ck_assert_int_lt(flags
, 128 + 16);
1064 ck_assert((flags
== (int)(128 | WEBSOCKET_OPCODE_BINARY
))
1065 || (flags
== (int)(128 | WEBSOCKET_OPCODE_TEXT
)));
1067 if (flags
== (int)(128 | WEBSOCKET_OPCODE_TEXT
)) {
1069 "Client %i received %lu bytes text data from server: %.*s\n",
1070 pclient_data
->clientId
,
1071 (unsigned long)data_len
,
1075 WS_TEST_TRACE("Client %i received %lu bytes binary data from\n",
1076 pclient_data
->clientId
,
1077 (unsigned long)data_len
);
1080 pclient_data
->data
= malloc(data_len
);
1081 ck_assert(pclient_data
->data
!= NULL
);
1082 memcpy(pclient_data
->data
, data
, data_len
);
1083 pclient_data
->len
= data_len
;
1092 websocket_client_close_handler(const struct mg_connection
*conn
,
1095 struct mg_context
*ctx
= mg_get_context(conn
);
1096 struct tclient_data
*pclient_data
=
1097 (struct tclient_data
*)mg_get_user_data(ctx
);
1100 ck_assert_ptr_eq(user_data
, (void *)pclient_data
);
1102 ck_assert(pclient_data
!= NULL
);
1104 WS_TEST_TRACE("Client %i: Close handler\n", pclient_data
->clientId
);
1105 pclient_data
->closed
++;
1111 pclient_data
->closed
++;
1113 #endif /* __MACH__ */
1116 #endif /* USE_WEBSOCKET */
1119 START_TEST(test_request_handlers
)
1122 struct mg_context
*ctx
;
1123 struct mg_connection
*client_conn
;
1124 const struct mg_request_info
*ri
;
1126 char buf
[1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 10 + 8];
1127 const char *expected
=
1128 "112123123412345123456123456712345678123456789123456789A";
1130 const char *request
= "GET /U7 HTTP/1.0\r\n\r\n";
1131 #if defined(USE_IPV6) && defined(NO_SSL)
1132 const char *HTTP_PORT
= "8084,[::]:8086";
1133 short ipv4_port
= 8084;
1134 short ipv6_port
= 8086;
1135 #elif !defined(USE_IPV6) && defined(NO_SSL)
1136 const char *HTTP_PORT
= "8084";
1137 short ipv4_port
= 8084;
1138 #elif defined(USE_IPV6) && !defined(NO_SSL)
1139 const char *HTTP_PORT
= "8084,[::]:8086,8194r,[::]:8196r,8094s,[::]:8096s";
1140 short ipv4_port
= 8084;
1141 short ipv4s_port
= 8094;
1142 short ipv4r_port
= 8194;
1143 short ipv6_port
= 8086;
1144 short ipv6s_port
= 8096;
1145 short ipv6r_port
= 8196;
1146 #elif !defined(USE_IPV6) && !defined(NO_SSL)
1147 const char *HTTP_PORT
= "8084,8194r,8094s";
1148 short ipv4_port
= 8084;
1149 short ipv4s_port
= 8094;
1150 short ipv4r_port
= 8194;
1153 const char *OPTIONS
[16];
1156 const char *plain_file_content
;
1157 const char *encoded_file_content
;
1158 const char *cgi_script_content
;
1159 const char *expected_cgi_result
;
1163 #if !defined(NO_SSL)
1164 const char *ssl_cert
= locate_ssl_cert();
1167 #if defined(USE_WEBSOCKET)
1168 struct tclient_data ws_client1_data
= {NULL
, 0, 0, 1};
1169 struct tclient_data ws_client2_data
= {NULL
, 0, 0, 2};
1170 struct tclient_data ws_client3_data
= {NULL
, 0, 0, 3};
1171 struct tclient_data ws_client4_data
= {NULL
, 0, 0, 4};
1172 struct mg_connection
*ws_client1_conn
= NULL
;
1173 struct mg_connection
*ws_client2_conn
= NULL
;
1174 struct mg_connection
*ws_client3_conn
= NULL
;
1175 struct mg_connection
*ws_client4_conn
= NULL
;
1183 memset((void *)OPTIONS
, 0, sizeof(OPTIONS
));
1184 OPTIONS
[opt_idx
++] = "listening_ports";
1185 OPTIONS
[opt_idx
++] = HTTP_PORT
;
1186 OPTIONS
[opt_idx
++] = "authentication_domain";
1187 OPTIONS
[opt_idx
++] = "test.domain";
1188 #if !defined(NO_FILES)
1189 OPTIONS
[opt_idx
++] = "document_root";
1190 OPTIONS
[opt_idx
++] = ".";
1193 ck_assert(ssl_cert
!= NULL
);
1194 OPTIONS
[opt_idx
++] = "ssl_certificate";
1195 OPTIONS
[opt_idx
++] = ssl_cert
;
1197 OPTIONS
[opt_idx
++] = "cgi_environment";
1198 cgi_env_opt
= (char *)calloc(1, 4096 /* CGI_ENVIRONMENT_SIZE */);
1199 ck_assert_ptr_ne(cgi_env_opt
, NULL
);
1200 cgi_env_opt
[0] = 'x';
1201 cgi_env_opt
[1] = '=';
1202 memset(cgi_env_opt
+ 2, 'y', 4090); /* Add large env field, so the server
1203 * must reallocate buffers. */
1204 OPTIONS
[opt_idx
++] = cgi_env_opt
;
1206 OPTIONS
[opt_idx
++] = "num_threads";
1207 OPTIONS
[opt_idx
++] = "2";
1210 ck_assert_int_le(opt_idx
, (int)(sizeof(OPTIONS
) / sizeof(OPTIONS
[0])));
1211 ck_assert(OPTIONS
[sizeof(OPTIONS
) / sizeof(OPTIONS
[0]) - 1] == NULL
);
1212 ck_assert(OPTIONS
[sizeof(OPTIONS
) / sizeof(OPTIONS
[0]) - 2] == NULL
);
1214 ctx
= test_mg_start(NULL
, &g_ctx
, OPTIONS
);
1216 ck_assert(ctx
!= NULL
);
1219 opt
= mg_get_option(ctx
, "listening_ports");
1220 ck_assert_str_eq(opt
, HTTP_PORT
);
1222 opt
= mg_get_option(ctx
, "cgi_environment");
1223 ck_assert_ptr_ne(opt
, cgi_env_opt
);
1224 ck_assert_int_eq((int)opt
[0], (int)cgi_env_opt
[0]);
1225 ck_assert_int_eq((int)opt
[1], (int)cgi_env_opt
[1]);
1226 ck_assert_int_eq((int)opt
[2], (int)cgi_env_opt
[2]);
1227 ck_assert_int_eq((int)opt
[3], (int)cgi_env_opt
[3]);
1228 /* full length string compare will reach limit in the implementation
1229 * of the check unit test framework */
1231 size_t len_check_1
= strlen(opt
);
1232 size_t len_check_2
= strlen(cgi_env_opt
);
1233 ck_assert_uint_eq(len_check_1
, len_check_2
);
1236 /* We don't need the original anymore, the server has a private copy */
1239 opt
= mg_get_option(ctx
, "throttle");
1240 ck_assert_str_eq(opt
, "");
1242 opt
= mg_get_option(ctx
, "unknown_option_name");
1243 ck_assert(opt
== NULL
);
1245 for (i
= 0; i
< 1000; i
++) {
1246 sprintf(uri
, "/U%u", i
);
1247 mg_set_request_handler(ctx
, uri
, request_test_handler
, NULL
);
1249 for (i
= 500; i
< 800; i
++) {
1250 sprintf(uri
, "/U%u", i
);
1251 mg_set_request_handler(ctx
, uri
, NULL
, (void *)(ptrdiff_t)1);
1253 for (i
= 600; i
>= 0; i
--) {
1254 sprintf(uri
, "/U%u", i
);
1255 mg_set_request_handler(ctx
, uri
, NULL
, (void *)(ptrdiff_t)2);
1257 for (i
= 750; i
<= 1000; i
++) {
1258 sprintf(uri
, "/U%u", i
);
1259 mg_set_request_handler(ctx
, uri
, NULL
, (void *)(ptrdiff_t)3);
1261 for (i
= 5; i
< 9; i
++) {
1262 sprintf(uri
, "/U%u", i
);
1263 mg_set_request_handler(ctx
,
1265 request_test_handler
,
1266 (void *)(ptrdiff_t)i
);
1269 #ifdef USE_WEBSOCKET
1270 mg_set_websocket_handler(ctx
,
1272 websock_server_connect
,
1273 websock_server_ready
,
1274 websock_server_data
,
1275 websock_server_close
,
1276 (void *)(ptrdiff_t)7531);
1279 /* Try to load non existing file */
1280 client_conn
= mg_download("localhost",
1286 "GET /file/not/found HTTP/1.0\r\n\r\n");
1287 ck_assert(client_conn
!= NULL
);
1288 ri
= mg_get_request_info(client_conn
);
1290 ck_assert(ri
!= NULL
);
1291 ck_assert_str_eq(ri
->local_uri
, "404");
1292 mg_close_connection(client_conn
);
1294 /* Get data from callback */
1295 client_conn
= mg_download(
1296 "localhost", ipv4_port
, 0, ebuf
, sizeof(ebuf
), "%s", request
);
1297 ck_assert(client_conn
!= NULL
);
1298 ri
= mg_get_request_info(client_conn
);
1300 ck_assert(ri
!= NULL
);
1301 ck_assert_str_eq(ri
->local_uri
, "200");
1302 i
= mg_read(client_conn
, buf
, sizeof(buf
));
1303 ck_assert_int_eq(i
, (int)strlen(expected
));
1305 ck_assert_str_eq(buf
, expected
);
1306 mg_close_connection(client_conn
);
1308 /* Get data from callback using http://127.0.0.1 */
1309 client_conn
= mg_download(
1310 "127.0.0.1", ipv4_port
, 0, ebuf
, sizeof(ebuf
), "%s", request
);
1311 ck_assert(client_conn
!= NULL
);
1312 ri
= mg_get_request_info(client_conn
);
1314 ck_assert(ri
!= NULL
);
1315 ck_assert_str_eq(ri
->local_uri
, "200");
1316 i
= mg_read(client_conn
, buf
, sizeof(buf
));
1317 if ((i
>= 0) && ((size_t)i
< sizeof(buf
))) {
1321 "ERROR: test_request_handlers: read returned %i (>=0, <%i)",
1325 ck_assert((int)i
< (int)sizeof(buf
));
1327 ck_assert_int_eq(i
, (int)strlen(expected
));
1329 ck_assert_str_eq(buf
, expected
);
1330 mg_close_connection(client_conn
);
1332 #if defined(USE_IPV6)
1333 /* Get data from callback using http://[::1] */
1335 mg_download("[::1]", ipv6_port
, 0, ebuf
, sizeof(ebuf
), "%s", request
);
1336 ck_assert(client_conn
!= NULL
);
1337 ri
= mg_get_request_info(client_conn
);
1339 ck_assert(ri
!= NULL
);
1340 ck_assert_str_eq(ri
->local_uri
, "200");
1341 i
= mg_read(client_conn
, buf
, sizeof(buf
));
1342 ck_assert_int_eq(i
, (int)strlen(expected
));
1344 ck_assert_str_eq(buf
, expected
);
1345 mg_close_connection(client_conn
);
1348 #if !defined(NO_SSL)
1349 /* Get data from callback using https://127.0.0.1 */
1350 client_conn
= mg_download(
1351 "127.0.0.1", ipv4s_port
, 1, ebuf
, sizeof(ebuf
), "%s", request
);
1352 ck_assert(client_conn
!= NULL
);
1353 ri
= mg_get_request_info(client_conn
);
1355 ck_assert(ri
!= NULL
);
1356 ck_assert_str_eq(ri
->local_uri
, "200");
1357 i
= mg_read(client_conn
, buf
, sizeof(buf
));
1358 ck_assert_int_eq(i
, (int)strlen(expected
));
1360 ck_assert_str_eq(buf
, expected
);
1361 mg_close_connection(client_conn
);
1363 /* Get redirect from callback using http://127.0.0.1 */
1364 client_conn
= mg_download(
1365 "127.0.0.1", ipv4r_port
, 0, ebuf
, sizeof(ebuf
), "%s", request
);
1366 ck_assert(client_conn
!= NULL
);
1367 ri
= mg_get_request_info(client_conn
);
1369 ck_assert(ri
!= NULL
);
1370 ck_assert_str_eq(ri
->local_uri
, "302");
1371 i
= mg_read(client_conn
, buf
, sizeof(buf
));
1372 ck_assert_int_eq(i
, -1);
1373 mg_close_connection(client_conn
);
1376 #if defined(USE_IPV6) && !defined(NO_SSL)
1377 /* Get data from callback using https://[::1] */
1379 mg_download("[::1]", ipv6s_port
, 1, ebuf
, sizeof(ebuf
), "%s", request
);
1380 ck_assert(client_conn
!= NULL
);
1381 ri
= mg_get_request_info(client_conn
);
1383 ck_assert(ri
!= NULL
);
1384 ck_assert_str_eq(ri
->local_uri
, "200");
1385 i
= mg_read(client_conn
, buf
, sizeof(buf
));
1386 ck_assert_int_eq(i
, (int)strlen(expected
));
1388 ck_assert_str_eq(buf
, expected
);
1389 mg_close_connection(client_conn
);
1391 /* Get redirect from callback using http://127.0.0.1 */
1393 mg_download("[::1]", ipv6r_port
, 0, ebuf
, sizeof(ebuf
), "%s", request
);
1394 ck_assert(client_conn
!= NULL
);
1395 ri
= mg_get_request_info(client_conn
);
1397 ck_assert(ri
!= NULL
);
1398 ck_assert_str_eq(ri
->local_uri
, "302");
1399 i
= mg_read(client_conn
, buf
, sizeof(buf
));
1400 ck_assert_int_eq(i
, -1);
1401 mg_close_connection(client_conn
);
1404 /* It seems to be impossible to find out what the actual working
1405 * directory of the CI test environment is. Before breaking another
1406 * dozen of builds by trying blindly with different paths, just
1407 * create the file here */
1409 f
= fopen("test.txt", "wb");
1411 f
= fopen("test.txt", "w");
1413 plain_file_content
= "simple text file\n";
1414 fwrite(plain_file_content
, 17, 1, f
);
1418 f
= fopen("test_gz.txt.gz", "wb");
1420 f
= fopen("test_gz.txt.gz", "w");
1422 encoded_file_content
= "\x1f\x8b\x08\x08\xf8\x9d\xcb\x55\x00\x00"
1424 "\x00\x01\x11\x00\xee\xff"
1426 "\x0a\x34\x5f\xcc\x49\x11\x00\x00\x00";
1427 fwrite(encoded_file_content
, 1, 52, f
);
1431 f
= fopen("test.cgi", "wb");
1432 cgi_script_content
= "#!test.cgi.cmd\r\n";
1433 fwrite(cgi_script_content
, strlen(cgi_script_content
), 1, f
);
1435 f
= fopen("test.cgi.cmd", "w");
1436 cgi_script_content
= "@echo off\r\n"
1437 "echo Connection: close\r\n"
1438 "echo Content-Type: text/plain\r\n"
1442 fwrite(cgi_script_content
, strlen(cgi_script_content
), 1, f
);
1445 f
= fopen("test.cgi", "w");
1446 cgi_script_content
= "#!/bin/sh\n\n"
1447 "printf \"Connection: close\\r\\n\"\n"
1448 "printf \"Content-Type: text/plain\\r\\n\"\n"
1449 "printf \"\\r\\n\"\n"
1450 "printf \"CGI test\\r\\n\"\n"
1452 (void)fwrite(cgi_script_content
, strlen(cgi_script_content
), 1, f
);
1454 (void)system("chmod a+x test.cgi");
1456 expected_cgi_result
= "CGI test";
1458 /* Get static data */
1459 client_conn
= mg_download("localhost",
1465 "GET /test.txt HTTP/1.0\r\n\r\n");
1466 ck_assert(client_conn
!= NULL
);
1467 ri
= mg_get_request_info(client_conn
);
1469 ck_assert(ri
!= NULL
);
1471 #if defined(NO_FILES)
1472 ck_assert_str_eq(ri
->local_uri
, "404");
1474 ck_assert_str_eq(ri
->local_uri
, "200");
1475 i
= mg_read(client_conn
, buf
, sizeof(buf
));
1476 ck_assert_int_eq(i
, 17);
1477 if ((i
>= 0) && (i
< (int)sizeof(buf
))) {
1480 ck_assert_str_eq(buf
, plain_file_content
);
1482 mg_close_connection(client_conn
);
1485 /* Check if CGI test executable exists */
1486 memset(&st
, 0, sizeof(st
));
1489 /* TODO: not yet available */
1490 sprintf(ebuf
, "%scgi_test.cgi", locate_test_exes());
1492 sprintf(ebuf
, "%scgi_test.cgi", locate_test_exes());
1494 if (stat(ebuf
, &st
) != 0) {
1495 fprintf(stderr
, "\nFile %s not found\n", ebuf
);
1497 "This file needs to be compiled manually before "
1498 "starting the test\n");
1500 "e.g. by gcc test/cgi_test.c -o output/cgi_test.cgi\n\n");
1502 /* Abort test with diagnostic message */
1503 ck_abort_msg("Mandatory file %s must be built before starting the test",
1509 /* Test with CGI test executable */
1511 sprintf(cmd_buf
, "copy %s\\cgi_test.cgi cgi_test.exe", locate_test_exes());
1513 sprintf(cmd_buf
, "cp %s/cgi_test.cgi cgi_test.cgi", locate_test_exes());
1515 (void)system(cmd_buf
);
1517 #if !defined(NO_CGI) && !defined(NO_FILES) && !defined(_WIN32)
1518 /* TODO: add test for windows, check with POST */
1519 client_conn
= mg_download(
1526 "POST /cgi_test.cgi/x/y.z HTTP/1.0\r\nContent-Length: 3\r\n\r\nABC");
1527 ck_assert(client_conn
!= NULL
);
1528 ri
= mg_get_request_info(client_conn
);
1530 ck_assert(ri
!= NULL
);
1531 ck_assert_str_eq(ri
->local_uri
, "200");
1532 mg_close_connection(client_conn
);
1535 /* Get zipped static data - will not work if Accept-Encoding is not set */
1536 client_conn
= mg_download("localhost",
1542 "GET /test_gz.txt HTTP/1.0\r\n\r\n");
1543 ck_assert(client_conn
!= NULL
);
1544 ri
= mg_get_request_info(client_conn
);
1546 ck_assert(ri
!= NULL
);
1547 ck_assert_str_eq(ri
->local_uri
, "404");
1548 mg_close_connection(client_conn
);
1550 /* Get zipped static data - with Accept-Encoding */
1551 client_conn
= mg_download(
1558 "GET /test_gz.txt HTTP/1.0\r\nAccept-Encoding: gzip\r\n\r\n");
1559 ck_assert(client_conn
!= NULL
);
1560 ri
= mg_get_request_info(client_conn
);
1562 ck_assert(ri
!= NULL
);
1564 #if defined(NO_FILES)
1565 ck_assert_str_eq(ri
->local_uri
, "404");
1567 ck_assert_str_eq(ri
->local_uri
, "200");
1568 i
= mg_read(client_conn
, buf
, sizeof(buf
));
1569 ck_assert_int_eq(i
, 52);
1570 if ((i
>= 0) && (i
< (int)sizeof(buf
))) {
1573 ck_assert_int_eq(ri
->content_length
, 52);
1574 ck_assert_str_eq(buf
, encoded_file_content
);
1576 mg_close_connection(client_conn
);
1578 /* Get CGI generated data */
1579 #if !defined(NO_CGI)
1580 client_conn
= mg_download("localhost",
1586 "GET /test.cgi HTTP/1.0\r\n\r\n");
1587 ck_assert(client_conn
!= NULL
);
1588 ri
= mg_get_request_info(client_conn
);
1590 ck_assert(ri
!= NULL
);
1592 #if defined(NO_FILES)
1593 ck_assert_str_eq(ri
->local_uri
, "404");
1595 (void)expected_cgi_result
;
1596 (void)cgi_script_content
;
1598 i
= mg_read(client_conn
, buf
, sizeof(buf
));
1599 if ((i
>= 0) && (i
< (int)sizeof(buf
))) {
1600 while ((i
> 0) && ((buf
[i
- 1] == '\r') || (buf
[i
- 1] == '\n'))) {
1605 /* ck_assert_int_eq(i, (int)strlen(expected_cgi_result)); */
1606 ck_assert_str_eq(buf
, expected_cgi_result
);
1607 ck_assert_str_eq(ri
->local_uri
, "200");
1608 mg_close_connection(client_conn
);
1612 (void)expected_cgi_result
;
1613 (void)cgi_script_content
;
1616 /* Get directory listing */
1617 client_conn
= mg_download("localhost",
1623 "GET / HTTP/1.0\r\n\r\n");
1624 ck_assert(client_conn
!= NULL
);
1625 ri
= mg_get_request_info(client_conn
);
1627 ck_assert(ri
!= NULL
);
1628 #if defined(NO_FILES)
1629 ck_assert_str_eq(ri
->local_uri
, "404");
1631 ck_assert_str_eq(ri
->local_uri
, "200");
1632 i
= mg_read(client_conn
, buf
, sizeof(buf
));
1635 ck_assert_str_eq(buf
, "<html>");
1637 mg_close_connection(client_conn
);
1639 /* POST to static file (will not work) */
1640 client_conn
= mg_download("localhost",
1646 "POST /test.txt HTTP/1.0\r\n\r\n");
1647 ck_assert(client_conn
!= NULL
);
1648 ri
= mg_get_request_info(client_conn
);
1650 ck_assert(ri
!= NULL
);
1651 #if defined(NO_FILES)
1652 ck_assert_str_eq(ri
->local_uri
, "404");
1654 ck_assert_str_eq(ri
->local_uri
, "405");
1655 i
= mg_read(client_conn
, buf
, sizeof(buf
));
1658 ck_assert_str_eq(buf
, "Error 405: Method Not Allowed");
1660 mg_close_connection(client_conn
);
1662 /* PUT to static file (will not work) */
1663 client_conn
= mg_download("localhost",
1669 "PUT /test.txt HTTP/1.0\r\n\r\n");
1670 ck_assert(client_conn
!= NULL
);
1671 ri
= mg_get_request_info(client_conn
);
1673 ck_assert(ri
!= NULL
);
1674 #if defined(NO_FILES)
1675 ck_assert_str_eq(ri
->local_uri
, "405"); /* method not allowed */
1677 ck_assert_str_eq(ri
->local_uri
, "401"); /* not authorized */
1679 mg_close_connection(client_conn
);
1682 /* Get data from callback using mg_connect_client instead of mg_download */
1683 memset(ebuf
, 0, sizeof(ebuf
));
1685 mg_connect_client("127.0.0.1", ipv4_port
, 0, ebuf
, sizeof(ebuf
));
1687 ck_assert_str_eq(ebuf
, "");
1688 ck_assert(client_conn
!= NULL
);
1690 mg_printf(client_conn
, "%s", request
);
1692 i
= mg_get_response(client_conn
, ebuf
, sizeof(ebuf
), 10000);
1693 ck_assert_int_ge(i
, 0);
1694 ck_assert_str_eq(ebuf
, "");
1696 ri
= mg_get_request_info(client_conn
);
1698 ck_assert(ri
!= NULL
);
1699 ck_assert_str_eq(ri
->local_uri
, "200");
1700 i
= mg_read(client_conn
, buf
, sizeof(buf
));
1701 ck_assert_int_eq(i
, (int)strlen(expected
));
1703 ck_assert_str_eq(buf
, expected
);
1704 mg_close_connection(client_conn
);
1706 /* Get data from callback using mg_connect_client and absolute URI */
1707 memset(ebuf
, 0, sizeof(ebuf
));
1709 mg_connect_client("localhost", ipv4_port
, 0, ebuf
, sizeof(ebuf
));
1711 ck_assert_str_eq(ebuf
, "");
1712 ck_assert(client_conn
!= NULL
);
1714 mg_printf(client_conn
,
1715 "GET http://test.domain:%d/U7 HTTP/1.0\r\n\r\n",
1718 i
= mg_get_response(client_conn
, ebuf
, sizeof(ebuf
), 10000);
1719 ck_assert_int_ge(i
, 0);
1720 ck_assert_str_eq(ebuf
, "");
1722 ri
= mg_get_request_info(client_conn
);
1724 ck_assert(ri
!= NULL
);
1725 ck_assert_str_eq(ri
->local_uri
, "200");
1726 i
= mg_read(client_conn
, buf
, sizeof(buf
));
1727 ck_assert_int_eq(i
, (int)strlen(expected
));
1729 ck_assert_str_eq(buf
, expected
);
1730 mg_close_connection(client_conn
);
1732 /* Get data from callback using mg_connect_client and absolute URI with a
1734 memset(ebuf
, 0, sizeof(ebuf
));
1736 mg_connect_client("localhost", ipv4_port
, 0, ebuf
, sizeof(ebuf
));
1738 ck_assert_str_eq(ebuf
, "");
1739 ck_assert(client_conn
!= NULL
);
1741 mg_printf(client_conn
,
1742 "GET http://subdomain.test.domain:%d/U7 HTTP/1.0\r\n\r\n",
1745 i
= mg_get_response(client_conn
, ebuf
, sizeof(ebuf
), 10000);
1746 ck_assert_int_ge(i
, 0);
1747 ck_assert_str_eq(ebuf
, "");
1749 ri
= mg_get_request_info(client_conn
);
1751 ck_assert(ri
!= NULL
);
1752 ck_assert_str_eq(ri
->local_uri
, "200");
1753 i
= mg_read(client_conn
, buf
, sizeof(buf
));
1754 ck_assert_int_eq(i
, (int)strlen(expected
));
1756 ck_assert_str_eq(buf
, expected
);
1757 mg_close_connection(client_conn
);
1760 /* Websocket test */
1761 #ifdef USE_WEBSOCKET
1762 /* Then connect a first client */
1764 mg_connect_websocket_client("localhost",
1771 websocket_client_data_handler
,
1772 websocket_client_close_handler
,
1775 ck_assert(ws_client1_conn
!= NULL
);
1778 &(ws_client1_data
.data
)); /* Wait for the websocket welcome message */
1779 ck_assert_int_eq(ws_client1_data
.closed
, 0);
1780 ck_assert_int_eq(ws_client2_data
.closed
, 0);
1781 ck_assert_int_eq(ws_client3_data
.closed
, 0);
1782 ck_assert(ws_client2_data
.data
== NULL
);
1783 ck_assert_uint_eq(ws_client2_data
.len
, 0);
1784 ck_assert(ws_client1_data
.data
!= NULL
);
1785 ck_assert_uint_eq(ws_client1_data
.len
, websocket_welcome_msg_len
);
1786 ck_assert(!memcmp(ws_client1_data
.data
,
1787 websocket_welcome_msg
,
1788 websocket_welcome_msg_len
));
1789 free(ws_client1_data
.data
);
1790 ws_client1_data
.data
= NULL
;
1791 ws_client1_data
.len
= 0;
1793 mg_websocket_client_write(ws_client1_conn
,
1794 WEBSOCKET_OPCODE_TEXT
,
1800 .data
)); /* Wait for the websocket acknowledge message */
1801 ck_assert_int_eq(ws_client1_data
.closed
, 0);
1802 ck_assert_int_eq(ws_client2_data
.closed
, 0);
1803 ck_assert(ws_client2_data
.data
== NULL
);
1804 ck_assert_uint_eq(ws_client2_data
.len
, 0);
1805 ck_assert(ws_client1_data
.data
!= NULL
);
1806 ck_assert_uint_eq(ws_client1_data
.len
, 3);
1807 ck_assert(!memcmp(ws_client1_data
.data
, "ok1", 3));
1808 free(ws_client1_data
.data
);
1809 ws_client1_data
.data
= NULL
;
1810 ws_client1_data
.len
= 0;
1812 /* Now connect a second client */
1815 mg_connect_websocket_client("[::1]",
1822 websocket_client_data_handler
,
1823 websocket_client_close_handler
,
1827 mg_connect_websocket_client("127.0.0.1",
1834 websocket_client_data_handler
,
1835 websocket_client_close_handler
,
1838 ck_assert(ws_client2_conn
!= NULL
);
1841 &(ws_client2_data
.data
)); /* Wait for the websocket welcome message */
1842 ck_assert(ws_client1_data
.closed
== 0);
1843 ck_assert(ws_client2_data
.closed
== 0);
1844 ck_assert(ws_client1_data
.data
== NULL
);
1845 ck_assert(ws_client1_data
.len
== 0);
1846 ck_assert(ws_client2_data
.data
!= NULL
);
1847 ck_assert(ws_client2_data
.len
== websocket_welcome_msg_len
);
1848 ck_assert(!memcmp(ws_client2_data
.data
,
1849 websocket_welcome_msg
,
1850 websocket_welcome_msg_len
));
1851 free(ws_client2_data
.data
);
1852 ws_client2_data
.data
= NULL
;
1853 ws_client2_data
.len
= 0;
1855 mg_websocket_client_write(ws_client1_conn
,
1856 WEBSOCKET_OPCODE_TEXT
,
1862 .data
)); /* Wait for the websocket acknowledge message */
1864 ck_assert(ws_client1_data
.closed
== 0);
1865 ck_assert(ws_client2_data
.closed
== 0);
1866 ck_assert(ws_client2_data
.data
== NULL
);
1867 ck_assert(ws_client2_data
.len
== 0);
1868 ck_assert(ws_client1_data
.data
!= NULL
);
1869 ck_assert(ws_client1_data
.len
== 4);
1870 ck_assert(!memcmp(ws_client1_data
.data
, "ok 2", 4));
1871 free(ws_client1_data
.data
);
1872 ws_client1_data
.data
= NULL
;
1873 ws_client1_data
.len
= 0;
1875 mg_websocket_client_write(ws_client1_conn
, WEBSOCKET_OPCODE_TEXT
, "bye", 3);
1878 &(ws_client1_data
.data
)); /* Wait for the websocket goodbye message */
1880 ck_assert(ws_client1_data
.closed
== 0);
1881 ck_assert(ws_client2_data
.closed
== 0);
1882 ck_assert(ws_client2_data
.data
== NULL
);
1883 ck_assert(ws_client2_data
.len
== 0);
1884 ck_assert(ws_client1_data
.data
!= NULL
);
1885 ck_assert(ws_client1_data
.len
== websocket_goodbye_msg_len
);
1886 ck_assert(!memcmp(ws_client1_data
.data
,
1887 websocket_goodbye_msg
,
1888 websocket_goodbye_msg_len
));
1889 free(ws_client1_data
.data
);
1890 ws_client1_data
.data
= NULL
;
1891 ws_client1_data
.len
= 0;
1893 ck_assert(ws_client1_data
.closed
== 0); /* Not closed */
1895 mg_close_connection(ws_client1_conn
);
1897 test_sleep(3); /* Won't get any message */
1899 ck_assert(ws_client1_data
.closed
== 1); /* Closed */
1901 ck_assert(ws_client2_data
.closed
== 0);
1902 ck_assert(ws_client1_data
.data
== NULL
);
1903 ck_assert(ws_client1_data
.len
== 0);
1904 ck_assert(ws_client2_data
.data
== NULL
);
1905 ck_assert(ws_client2_data
.len
== 0);
1907 mg_websocket_client_write(ws_client2_conn
, WEBSOCKET_OPCODE_TEXT
, "bye", 3);
1910 &(ws_client2_data
.data
)); /* Wait for the websocket goodbye message */
1912 ck_assert(ws_client1_data
.closed
== 1);
1913 ck_assert(ws_client2_data
.closed
== 0);
1914 ck_assert(ws_client1_data
.data
== NULL
);
1915 ck_assert(ws_client1_data
.len
== 0);
1916 ck_assert(ws_client2_data
.data
!= NULL
);
1917 ck_assert(ws_client2_data
.len
== websocket_goodbye_msg_len
);
1918 ck_assert(!memcmp(ws_client2_data
.data
,
1919 websocket_goodbye_msg
,
1920 websocket_goodbye_msg_len
));
1921 free(ws_client2_data
.data
);
1922 ws_client2_data
.data
= NULL
;
1923 ws_client2_data
.len
= 0;
1925 mg_close_connection(ws_client2_conn
);
1927 test_sleep(3); /* Won't get any message */
1929 ck_assert(ws_client1_data
.closed
== 1);
1930 ck_assert(ws_client2_data
.closed
== 1);
1931 ck_assert(ws_client1_data
.data
== NULL
);
1932 ck_assert(ws_client1_data
.len
== 0);
1933 ck_assert(ws_client2_data
.data
== NULL
);
1934 ck_assert(ws_client2_data
.len
== 0);
1936 /* Connect client 3 */
1938 mg_connect_websocket_client("localhost",
1950 websocket_client_data_handler
,
1951 websocket_client_close_handler
,
1954 ck_assert(ws_client3_conn
!= NULL
);
1957 &(ws_client3_data
.data
)); /* Wait for the websocket welcome message */
1958 ck_assert(ws_client1_data
.closed
== 1);
1959 ck_assert(ws_client2_data
.closed
== 1);
1960 ck_assert(ws_client3_data
.closed
== 0);
1961 ck_assert(ws_client1_data
.data
== NULL
);
1962 ck_assert(ws_client1_data
.len
== 0);
1963 ck_assert(ws_client2_data
.data
== NULL
);
1964 ck_assert(ws_client2_data
.len
== 0);
1965 ck_assert(ws_client3_data
.data
!= NULL
);
1966 ck_assert(ws_client3_data
.len
== websocket_welcome_msg_len
);
1967 ck_assert(!memcmp(ws_client3_data
.data
,
1968 websocket_welcome_msg
,
1969 websocket_welcome_msg_len
));
1970 free(ws_client3_data
.data
);
1971 ws_client3_data
.data
= NULL
;
1972 ws_client3_data
.len
= 0;
1974 /* Write long data (16 bit size header) */
1975 mg_websocket_client_write(ws_client3_conn
,
1976 WEBSOCKET_OPCODE_BINARY
,
1978 long_ws_buf_len_16
);
1980 /* Wait for the response */
1981 wait_not_null(&(ws_client3_data
.data
));
1983 ck_assert_int_eq((int)ws_client3_data
.len
, (int)long_ws_buf_len_16
);
1984 ck_assert(!memcmp(ws_client3_data
.data
, long_ws_buf
, long_ws_buf_len_16
));
1985 free(ws_client3_data
.data
);
1986 ws_client3_data
.data
= NULL
;
1987 ws_client3_data
.len
= 0;
1989 /* Write long data (64 bit size header) */
1990 mg_websocket_client_write(ws_client3_conn
,
1991 WEBSOCKET_OPCODE_BINARY
,
1993 long_ws_buf_len_64
);
1995 /* Wait for the response */
1996 wait_not_null(&(ws_client3_data
.data
));
1998 ck_assert_int_eq((int)ws_client3_data
.len
, (int)long_ws_buf_len_64
);
1999 ck_assert(!memcmp(ws_client3_data
.data
, long_ws_buf
, long_ws_buf_len_64
));
2000 free(ws_client3_data
.data
);
2001 ws_client3_data
.data
= NULL
;
2002 ws_client3_data
.len
= 0;
2004 /* Disconnect client 3 */
2005 ck_assert(ws_client3_data
.closed
== 0);
2006 mg_close_connection(ws_client3_conn
);
2007 ck_assert(ws_client3_data
.closed
== 1);
2009 /* Connect client 4 */
2011 mg_connect_websocket_client("localhost",
2023 websocket_client_data_handler
,
2024 websocket_client_close_handler
,
2027 ck_assert(ws_client4_conn
!= NULL
);
2030 &(ws_client4_data
.data
)); /* Wait for the websocket welcome message */
2031 ck_assert(ws_client1_data
.closed
== 1);
2032 ck_assert(ws_client2_data
.closed
== 1);
2033 ck_assert(ws_client3_data
.closed
== 1);
2034 ck_assert(ws_client4_data
.closed
== 0);
2035 ck_assert(ws_client4_data
.data
!= NULL
);
2036 ck_assert(ws_client4_data
.len
== websocket_welcome_msg_len
);
2037 ck_assert(!memcmp(ws_client4_data
.data
,
2038 websocket_welcome_msg
,
2039 websocket_welcome_msg_len
));
2040 free(ws_client4_data
.data
);
2041 ws_client4_data
.data
= NULL
;
2042 ws_client4_data
.len
= 0;
2044 /* stop the server without closing this connection */
2048 /* Close the server */
2053 #ifdef USE_WEBSOCKET
2054 for (i
= 0; i
< 100; i
++) {
2056 if (ws_client3_data
.closed
!= 0) {
2062 ck_assert_int_eq(ws_client4_data
.closed
, 1);
2064 /* Free data in ws_client4_conn */
2065 mg_close_connection(ws_client4_conn
);
2073 static int g_field_found_return
= -999;
2076 field_found(const char *key
,
2077 const char *filename
,
2082 ck_assert_ptr_ne(key
, NULL
);
2083 ck_assert_ptr_ne(filename
, NULL
);
2084 ck_assert_ptr_ne(path
, NULL
);
2085 ck_assert_uint_gt(pathlen
, 128);
2086 ck_assert_ptr_eq(user_data
, (void *)&g_field_found_return
);
2088 ck_assert((g_field_found_return
== FORM_FIELD_STORAGE_GET
)
2089 || (g_field_found_return
== FORM_FIELD_STORAGE_STORE
)
2090 || (g_field_found_return
== FORM_FIELD_STORAGE_SKIP
)
2091 || (g_field_found_return
== FORM_FIELD_STORAGE_ABORT
));
2093 ck_assert_str_ne(key
, "dontread");
2095 if (!strcmp(key
, "break_field_handler")) {
2096 return FORM_FIELD_STORAGE_ABORT
;
2098 if (!strcmp(key
, "continue_field_handler")) {
2099 return FORM_FIELD_STORAGE_SKIP
;
2102 if (g_field_found_return
== FORM_FIELD_STORAGE_STORE
) {
2103 strncpy(path
, key
, pathlen
- 8);
2104 strcat(path
, ".txt");
2109 return g_field_found_return
;
2113 static int g_field_step
;
2116 field_get(const char *key
,
2117 const char *value_untruncated
,
2121 /* Copy the untruncated value, so string compare functions can be used. */
2122 /* The check unit test library does not have build in memcmp functions. */
2123 char *value
= (char *)malloc(valuelen
+ 1);
2124 ck_assert(value
!= NULL
);
2125 memcpy(value
, value_untruncated
, valuelen
);
2126 value
[valuelen
] = 0;
2128 ck_assert_ptr_eq(user_data
, (void *)&g_field_found_return
);
2129 ck_assert_int_ge(g_field_step
, 0);
2132 switch (g_field_step
) {
2134 ck_assert_str_eq(key
, "textin");
2135 ck_assert_uint_eq(valuelen
, 4);
2136 ck_assert_str_eq(value
, "text");
2139 ck_assert_str_eq(key
, "passwordin");
2140 ck_assert_uint_eq(valuelen
, 0);
2141 ck_assert_str_eq(value
, "");
2144 ck_assert_str_eq(key
, "radio1");
2145 ck_assert_uint_eq(valuelen
, 4);
2146 ck_assert_str_eq(value
, "val1");
2149 ck_assert_str_eq(key
, "radio2");
2150 ck_assert_uint_eq(valuelen
, 4);
2151 ck_assert_str_eq(value
, "val1");
2154 ck_assert_str_eq(key
, "check1");
2155 ck_assert_uint_eq(valuelen
, 4);
2156 ck_assert_str_eq(value
, "val1");
2159 ck_assert_str_eq(key
, "numberin");
2160 ck_assert_uint_eq(valuelen
, 1);
2161 ck_assert_str_eq(value
, "1");
2164 ck_assert_str_eq(key
, "datein");
2165 ck_assert_uint_eq(valuelen
, 8);
2166 ck_assert_str_eq(value
, "1.1.2016");
2169 ck_assert_str_eq(key
, "colorin");
2170 ck_assert_uint_eq(valuelen
, 7);
2171 ck_assert_str_eq(value
, "#80ff00");
2174 ck_assert_str_eq(key
, "rangein");
2175 ck_assert_uint_eq(valuelen
, 1);
2176 ck_assert_str_eq(value
, "3");
2179 ck_assert_str_eq(key
, "monthin");
2180 ck_assert_uint_eq(valuelen
, 0);
2181 ck_assert_str_eq(value
, "");
2184 ck_assert_str_eq(key
, "weekin");
2185 ck_assert_uint_eq(valuelen
, 0);
2186 ck_assert_str_eq(value
, "");
2189 ck_assert_str_eq(key
, "timein");
2190 ck_assert_uint_eq(valuelen
, 0);
2191 ck_assert_str_eq(value
, "");
2194 ck_assert_str_eq(key
, "datetimen");
2195 ck_assert_uint_eq(valuelen
, 0);
2196 ck_assert_str_eq(value
, "");
2199 ck_assert_str_eq(key
, "datetimelocalin");
2200 ck_assert_uint_eq(valuelen
, 0);
2201 ck_assert_str_eq(value
, "");
2204 ck_assert_str_eq(key
, "emailin");
2205 ck_assert_uint_eq(valuelen
, 0);
2206 ck_assert_str_eq(value
, "");
2209 ck_assert_str_eq(key
, "searchin");
2210 ck_assert_uint_eq(valuelen
, 0);
2211 ck_assert_str_eq(value
, "");
2214 ck_assert_str_eq(key
, "telin");
2215 ck_assert_uint_eq(valuelen
, 0);
2216 ck_assert_str_eq(value
, "");
2219 ck_assert_str_eq(key
, "urlin");
2220 ck_assert_uint_eq(valuelen
, 0);
2221 ck_assert_str_eq(value
, "");
2224 ck_assert_str_eq(key
, "filein");
2225 ck_assert_uint_eq(valuelen
, 0);
2226 ck_assert_str_eq(value
, "");
2229 ck_assert_str_eq(key
, "filesin");
2230 ck_assert_uint_eq(valuelen
, 0);
2231 ck_assert_str_eq(value
, "");
2234 ck_assert_str_eq(key
, "selectin");
2235 ck_assert_uint_eq(valuelen
, 4);
2236 ck_assert_str_eq(value
, "opt1");
2239 ck_assert_str_eq(key
, "message");
2240 ck_assert_uint_eq(valuelen
, 23);
2241 ck_assert_str_eq(value
, "Text area default text.");
2244 ck_abort_msg("field_get called with g_field_step == %i",
2255 static const char *myfile_content
= "Content of myfile.txt\r\n";
2256 static const int myfile_content_rep
= 500;
2260 field_store(const char *path
, long long file_size
, void *user_data
)
2263 ck_assert_ptr_eq(user_data
, (void *)&g_field_found_return
);
2264 ck_assert_int_ge(g_field_step
, 100);
2267 switch (g_field_step
) {
2269 ck_assert_str_eq(path
, "storeme.txt");
2270 ck_assert_int_eq(file_size
, 9);
2271 f
= fopen(path
, "r");
2272 ck_assert_ptr_ne(f
, NULL
);
2275 int i
= (int)fread(buf
, 1, 31, f
);
2276 ck_assert_int_eq(i
, 9);
2278 ck_assert_str_eq(buf
, "storetest");
2282 ck_assert_str_eq(path
, "file2store.txt");
2283 ck_assert_uint_eq(23, strlen(myfile_content
));
2284 ck_assert_int_eq(file_size
, 23 * myfile_content_rep
);
2286 f
= fopen(path
, "rb");
2288 f
= fopen(path
, "r");
2290 ck_assert_ptr_ne(f
, NULL
);
2294 for (r
= 0; r
< myfile_content_rep
; r
++) {
2295 i
= (int)fread(buf
, 1, 23, f
);
2296 ck_assert_int_eq(i
, 23);
2297 ck_assert_str_eq(buf
, myfile_content
);
2299 i
= (int)fread(buf
, 1, 23, f
);
2300 ck_assert_int_eq(i
, 0);
2305 ck_abort_msg("field_get called with g_field_step == %i",
2315 FormGet(struct mg_connection
*conn
, void *cbdata
)
2317 const struct mg_request_info
*req_info
= mg_get_request_info(conn
);
2319 struct mg_form_data_handler fdh
= {field_found
, field_get
, NULL
, NULL
};
2323 ck_assert(req_info
!= NULL
);
2325 mg_printf(conn
, "HTTP/1.0 200 OK\r\nContent-Type: text/plain\r\n\r\n");
2326 fdh
.user_data
= (void *)&g_field_found_return
;
2328 /* Call the form handler */
2330 g_field_found_return
= FORM_FIELD_STORAGE_GET
;
2331 ret
= mg_handle_form_request(conn
, &fdh
);
2332 g_field_found_return
= -888;
2333 ck_assert_int_eq(ret
, 22);
2334 ck_assert_int_eq(g_field_step
, 22);
2335 mg_printf(conn
, "%i\r\n", ret
);
2336 g_field_step
= 1000;
2345 FormStore(struct mg_connection
*conn
,
2348 int field_step_expected
)
2350 const struct mg_request_info
*req_info
= mg_get_request_info(conn
);
2352 struct mg_form_data_handler fdh
= {field_found
,
2359 ck_assert(req_info
!= NULL
);
2361 mg_printf(conn
, "HTTP/1.0 200 OK\r\nContent-Type: text/plain\r\n\r\n");
2362 fdh
.user_data
= (void *)&g_field_found_return
;
2364 /* Call the form handler */
2366 g_field_found_return
= FORM_FIELD_STORAGE_STORE
;
2367 ret
= mg_handle_form_request(conn
, &fdh
);
2368 ck_assert_int_eq(ret
, ret_expected
);
2369 ck_assert_int_eq(g_field_step
, field_step_expected
);
2370 mg_printf(conn
, "%i\r\n", ret
);
2371 g_field_step
= 1000;
2380 FormStore1(struct mg_connection
*conn
, void *cbdata
)
2383 return FormStore(conn
, cbdata
, 3, 101);
2388 FormStore2(struct mg_connection
*conn
, void *cbdata
)
2391 return FormStore(conn
, cbdata
, 4, 102);
2396 send_chunk_stringl(struct mg_connection
*conn
,
2398 unsigned int chunk_len
)
2406 /* First store the length information in a text buffer. */
2407 sprintf(lenbuf
, "%x\r\n", chunk_len
);
2408 lenbuf_len
= strlen(lenbuf
);
2410 /* Then send length information, chunk and terminating \r\n. */
2411 ret
= mg_write(conn
, lenbuf
, lenbuf_len
);
2412 ck_assert_int_eq(ret
, (int)lenbuf_len
);
2414 ret
= mg_write(conn
, chunk
, chunk_len
);
2415 ck_assert_int_eq(ret
, (int)chunk_len
);
2417 ret
= mg_write(conn
, "\r\n", 2);
2418 ck_assert_int_eq(ret
, 2);
2423 send_chunk_string(struct mg_connection
*conn
, const char *chunk
)
2426 send_chunk_stringl(conn
, chunk
, (unsigned int)strlen(chunk
));
2431 START_TEST(test_handle_form
)
2433 struct mg_context
*ctx
;
2434 struct mg_connection
*client_conn
;
2435 const struct mg_request_info
*ri
;
2436 const char *OPTIONS
[8];
2440 const char *multipart_body
;
2441 const char *boundary
;
2442 size_t body_len
, body_sent
, chunk_len
;
2447 memset((void *)OPTIONS
, 0, sizeof(OPTIONS
));
2448 OPTIONS
[opt_idx
++] = "listening_ports";
2449 OPTIONS
[opt_idx
++] = "8884";
2450 ck_assert_int_le(opt_idx
, (int)(sizeof(OPTIONS
) / sizeof(OPTIONS
[0])));
2451 ck_assert(OPTIONS
[sizeof(OPTIONS
) / sizeof(OPTIONS
[0]) - 1] == NULL
);
2452 ck_assert(OPTIONS
[sizeof(OPTIONS
) / sizeof(OPTIONS
[0]) - 2] == NULL
);
2454 ctx
= test_mg_start(NULL
, &g_ctx
, OPTIONS
);
2456 ck_assert(ctx
!= NULL
);
2459 opt
= mg_get_option(ctx
, "listening_ports");
2460 ck_assert_str_eq(opt
, "8884");
2462 mg_set_request_handler(ctx
, "/handle_form", FormGet
, NULL
);
2463 mg_set_request_handler(ctx
, "/handle_form_store", FormStore1
, NULL
);
2464 mg_set_request_handler(ctx
, "/handle_form_store2", FormStore2
, NULL
);
2468 /* Handle form: "GET" */
2469 client_conn
= mg_download("localhost",
2476 "?textin=text&passwordin=&radio1=val1"
2477 "&radio2=val1&check1=val1&numberin=1"
2478 "&datein=1.1.2016&colorin=%2380ff00"
2479 "&rangein=3&monthin=&weekin=&timein="
2480 "&datetimen=&datetimelocalin=&emailin="
2481 "&searchin=&telin=&urlin=&filein="
2482 "&filesin=&selectin=opt1"
2483 "&message=Text+area+default+text. "
2485 "Host: localhost:8884\r\n"
2486 "Connection: close\r\n\r\n");
2487 ck_assert(client_conn
!= NULL
);
2488 for (sleep_cnt
= 0; sleep_cnt
< 30; sleep_cnt
++) {
2490 if (g_field_step
== 1000) {
2494 ri
= mg_get_request_info(client_conn
);
2496 ck_assert(ri
!= NULL
);
2497 ck_assert_str_eq(ri
->local_uri
, "200");
2498 mg_close_connection(client_conn
);
2500 /* Handle form: "POST x-www-form-urlencoded" */
2502 mg_download("localhost",
2508 "POST /handle_form HTTP/1.1\r\n"
2509 "Host: localhost:8884\r\n"
2510 "Connection: close\r\n"
2511 "Content-Type: application/x-www-form-urlencoded\r\n"
2512 "Content-Length: 263\r\n"
2514 "textin=text&passwordin=&radio1=val1&radio2=val1"
2515 "&check1=val1&numberin=1&datein=1.1.2016"
2516 "&colorin=%2380ff00&rangein=3&monthin=&weekin="
2517 "&timein=&datetimen=&datetimelocalin=&emailin="
2518 "&searchin=&telin=&urlin=&filein=&filesin="
2519 "&selectin=opt1&message=Text+area+default+text.");
2520 ck_assert(client_conn
!= NULL
);
2521 for (sleep_cnt
= 0; sleep_cnt
< 30; sleep_cnt
++) {
2523 if (g_field_step
== 1000) {
2527 ri
= mg_get_request_info(client_conn
);
2529 ck_assert(ri
!= NULL
);
2530 ck_assert_str_eq(ri
->local_uri
, "200");
2531 mg_close_connection(client_conn
);
2533 /* Handle form: "POST multipart/form-data" */
2535 "--multipart-form-data-boundary--see-RFC-2388\r\n"
2536 "Content-Disposition: form-data; name=\"textin\"\r\n"
2539 "--multipart-form-data-boundary--see-RFC-2388\r\n"
2540 "Content-Disposition: form-data; name=\"passwordin\"\r\n"
2543 "--multipart-form-data-boundary--see-RFC-2388\r\n"
2544 "Content-Disposition: form-data; name=\"radio1\"\r\n"
2547 "--multipart-form-data-boundary--see-RFC-2388\r\n"
2548 "Content-Disposition: form-data; name=radio2\r\n"
2551 "--multipart-form-data-boundary--see-RFC-2388\r\n"
2552 "Content-Disposition: form-data; name=\"check1\"\r\n"
2555 "--multipart-form-data-boundary--see-RFC-2388\r\n"
2556 "Content-Disposition: form-data; name=\"numberin\"\r\n"
2559 "--multipart-form-data-boundary--see-RFC-2388\r\n"
2560 "Content-Disposition: form-data; name=\"datein\"\r\n"
2563 "--multipart-form-data-boundary--see-RFC-2388\r\n"
2564 "Content-Disposition: form-data; name=\"colorin\"\r\n"
2567 "--multipart-form-data-boundary--see-RFC-2388\r\n"
2568 "Content-Disposition: form-data; name=\"rangein\"\r\n"
2571 "--multipart-form-data-boundary--see-RFC-2388\r\n"
2572 "Content-Disposition: form-data; name=\"monthin\"\r\n"
2575 "--multipart-form-data-boundary--see-RFC-2388\r\n"
2576 "Content-Disposition: form-data; name=\"weekin\"\r\n"
2579 "--multipart-form-data-boundary--see-RFC-2388\r\n"
2580 "Content-Disposition: form-data; name=\"timein\"\r\n"
2583 "--multipart-form-data-boundary--see-RFC-2388\r\n"
2584 "Content-Disposition: form-data; name=\"datetimen\"\r\n"
2587 "--multipart-form-data-boundary--see-RFC-2388\r\n"
2588 "Content-Disposition: form-data; name=\"datetimelocalin\"\r\n"
2591 "--multipart-form-data-boundary--see-RFC-2388\r\n"
2592 "Content-Disposition: form-data; name=\"emailin\"\r\n"
2595 "--multipart-form-data-boundary--see-RFC-2388\r\n"
2596 "Content-Disposition: form-data; name=\"searchin\"\r\n"
2599 "--multipart-form-data-boundary--see-RFC-2388\r\n"
2600 "Content-Disposition: form-data; name=\"telin\"\r\n"
2603 "--multipart-form-data-boundary--see-RFC-2388\r\n"
2604 "Content-Disposition: form-data; name=\"urlin\"\r\n"
2607 "--multipart-form-data-boundary--see-RFC-2388\r\n"
2608 "Content-Disposition: form-data; name=\"filein\"; filename=\"\"\r\n"
2609 "Content-Type: application/octet-stream\r\n"
2612 "--multipart-form-data-boundary--see-RFC-2388\r\n"
2613 "Content-Disposition: form-data; name=filesin; filename=\r\n"
2614 "Content-Type: application/octet-stream\r\n"
2617 "--multipart-form-data-boundary--see-RFC-2388\r\n"
2618 "Content-Disposition: form-data; name=\"selectin\"\r\n"
2621 "--multipart-form-data-boundary--see-RFC-2388\r\n"
2622 "Content-Disposition: form-data; name=\"message\"\r\n"
2624 "Text area default text.\r\n"
2625 "--multipart-form-data-boundary--see-RFC-2388--\r\n";
2626 body_len
= strlen(multipart_body
);
2627 ck_assert_uint_eq(body_len
, 2368); /* not required */
2630 mg_download("localhost",
2635 "POST /handle_form HTTP/1.1\r\n"
2636 "Host: localhost:8884\r\n"
2637 "Connection: close\r\n"
2638 "Content-Type: multipart/form-data; "
2639 "boundary=multipart-form-data-boundary--see-RFC-2388\r\n"
2640 "Content-Length: %u\r\n"
2642 (unsigned int)body_len
,
2645 ck_assert(client_conn
!= NULL
);
2646 for (sleep_cnt
= 0; sleep_cnt
< 30; sleep_cnt
++) {
2648 if (g_field_step
== 1000) {
2652 ri
= mg_get_request_info(client_conn
);
2654 ck_assert(ri
!= NULL
);
2655 ck_assert_str_eq(ri
->local_uri
, "200");
2656 mg_close_connection(client_conn
);
2659 /* Handle form: "POST multipart/form-data" with chunked transfer encoding */
2661 mg_download("localhost",
2667 "POST /handle_form HTTP/1.1\r\n"
2668 "Host: localhost:8884\r\n"
2669 "Connection: close\r\n"
2670 "Content-Type: multipart/form-data; "
2671 "boundary=multipart-form-data-boundary--see-RFC-2388\r\n"
2672 "Transfer-Encoding: chunked\r\n"
2675 ck_assert(client_conn
!= NULL
);
2677 body_len
= strlen(multipart_body
);
2680 while (body_len
> body_sent
) {
2681 if (chunk_len
> (body_len
- body_sent
)) {
2682 chunk_len
= body_len
- body_sent
;
2684 ck_assert_int_gt((int)chunk_len
, 0);
2685 mg_printf(client_conn
, "%x\r\n", (unsigned int)chunk_len
);
2686 mg_write(client_conn
, multipart_body
+ body_sent
, chunk_len
);
2687 mg_printf(client_conn
, "\r\n");
2688 body_sent
+= chunk_len
;
2689 chunk_len
= (chunk_len
% 40) + 1;
2691 mg_printf(client_conn
, "0\r\n");
2693 for (sleep_cnt
= 0; sleep_cnt
< 30; sleep_cnt
++) {
2695 if (g_field_step
== 1000) {
2699 ri
= mg_get_request_info(client_conn
);
2701 ck_assert(ri
!= NULL
);
2702 ck_assert_str_eq(ri
->local_uri
, "200");
2703 mg_close_connection(client_conn
);
2705 /* Handle form: "POST multipart/form-data" with chunked transfer
2706 * encoding, using a quoted boundary string */
2707 client_conn
= mg_download(
2714 "POST /handle_form HTTP/1.1\r\n"
2715 "Host: localhost:8884\r\n"
2716 "Connection: close\r\n"
2717 "Content-Type: multipart/form-data; "
2718 "boundary=\"multipart-form-data-boundary--see-RFC-2388\"\r\n"
2719 "Transfer-Encoding: chunked\r\n"
2722 ck_assert(client_conn
!= NULL
);
2724 body_len
= strlen(multipart_body
);
2727 while (body_len
> body_sent
) {
2728 if (chunk_len
> (body_len
- body_sent
)) {
2729 chunk_len
= body_len
- body_sent
;
2731 ck_assert_int_gt((int)chunk_len
, 0);
2732 mg_printf(client_conn
, "%x\r\n", (unsigned int)chunk_len
);
2733 mg_write(client_conn
, multipart_body
+ body_sent
, chunk_len
);
2734 mg_printf(client_conn
, "\r\n");
2735 body_sent
+= chunk_len
;
2736 chunk_len
= (chunk_len
% 40) + 1;
2738 mg_printf(client_conn
, "0\r\n");
2740 for (sleep_cnt
= 0; sleep_cnt
< 30; sleep_cnt
++) {
2742 if (g_field_step
== 1000) {
2746 ri
= mg_get_request_info(client_conn
);
2748 ck_assert(ri
!= NULL
);
2749 ck_assert_str_eq(ri
->local_uri
, "200");
2750 mg_close_connection(client_conn
);
2753 /* Now test form_store */
2755 /* First test with GET */
2756 client_conn
= mg_download("localhost",
2762 "GET /handle_form_store"
2763 "?storeme=storetest"
2764 "&continue_field_handler=ignore"
2765 "&break_field_handler=abort"
2768 "Host: localhost:8884\r\n"
2769 "Connection: close\r\n\r\n");
2771 ck_assert(client_conn
!= NULL
);
2773 for (sleep_cnt
= 0; sleep_cnt
< 30; sleep_cnt
++) {
2775 if (g_field_step
== 1000) {
2779 ri
= mg_get_request_info(client_conn
);
2781 ck_assert(ri
!= NULL
);
2782 ck_assert_str_eq(ri
->local_uri
, "200");
2783 mg_close_connection(client_conn
);
2786 /* Handle form: "POST x-www-form-urlencoded", chunked, store */
2788 mg_download("localhost",
2794 "POST /handle_form_store HTTP/1.0\r\n"
2795 "Host: localhost:8884\r\n"
2796 "Connection: close\r\n"
2797 "Content-Type: application/x-www-form-urlencoded\r\n"
2798 "Transfer-Encoding: chunked\r\n"
2800 ck_assert(client_conn
!= NULL
);
2802 send_chunk_string(client_conn
, "storeme=store");
2803 send_chunk_string(client_conn
, "test&");
2804 send_chunk_string(client_conn
, "continue_field_handler=ignore");
2805 send_chunk_string(client_conn
, "&br");
2807 send_chunk_string(client_conn
, "eak_field_handler=abort&");
2808 send_chunk_string(client_conn
, "dontread=xyz");
2809 mg_printf(client_conn
, "0\r\n");
2811 for (sleep_cnt
= 0; sleep_cnt
< 30; sleep_cnt
++) {
2813 if (g_field_step
== 1000) {
2817 ri
= mg_get_request_info(client_conn
);
2819 ck_assert(ri
!= NULL
);
2820 ck_assert_str_eq(ri
->local_uri
, "200");
2821 mg_close_connection(client_conn
);
2823 /* Handle form: "POST multipart/form-data", chunked, store */
2825 mg_download("localhost",
2831 "POST /handle_form_store HTTP/1.0\r\n"
2832 "Host: localhost:8884\r\n"
2833 "Connection: close\r\n"
2834 "Content-Type: multipart/form-data; "
2835 "boundary=multipart-form-data-boundary--see-RFC-2388\r\n"
2836 "Transfer-Encoding: chunked\r\n"
2838 ck_assert(client_conn
!= NULL
);
2840 send_chunk_string(client_conn
, "--multipart-form-data-boundary");
2841 send_chunk_string(client_conn
, "--see-RFC-2388\r\n");
2842 send_chunk_string(client_conn
, "Content-Disposition: form-data; ");
2843 send_chunk_string(client_conn
, "name=\"storeme\"\r\n");
2844 send_chunk_string(client_conn
, "\r\n");
2845 send_chunk_string(client_conn
, "storetest\r\n");
2847 send_chunk_string(client_conn
, "--multipart-form-data-boundary-");
2848 send_chunk_string(client_conn
, "-see-RFC-2388\r\n");
2849 send_chunk_string(client_conn
, "Content-Disposition: form-data; ");
2850 send_chunk_string(client_conn
, "name=\"continue_field_handler\"\r\n");
2851 send_chunk_string(client_conn
, "\r\n");
2852 send_chunk_string(client_conn
, "ignore\r\n");
2854 send_chunk_string(client_conn
, "--multipart-form-data-boundary-");
2855 send_chunk_string(client_conn
, "-see-RFC-2388\r\n");
2856 send_chunk_string(client_conn
, "Content-Disposition: form-data; ");
2857 send_chunk_string(client_conn
, "name=\"break_field_handler\"\r\n");
2858 send_chunk_string(client_conn
, "\r\n");
2859 send_chunk_string(client_conn
, "abort\r\n");
2861 send_chunk_string(client_conn
, "--multipart-form-data-boundary-");
2862 send_chunk_string(client_conn
, "-see-RFC-2388\r\n");
2863 send_chunk_string(client_conn
, "Content-Disposition: form-data; ");
2864 send_chunk_string(client_conn
, "name=\"dontread\"\r\n");
2865 send_chunk_string(client_conn
, "\r\n");
2866 send_chunk_string(client_conn
, "xyz\r\n");
2867 send_chunk_string(client_conn
, "--multipart-form-data-boundary");
2868 send_chunk_string(client_conn
, "--see-RFC-2388--\r\n");
2869 mg_printf(client_conn
, "0\r\n");
2871 for (sleep_cnt
= 0; sleep_cnt
< 30; sleep_cnt
++) {
2873 if (g_field_step
== 1000) {
2877 ri
= mg_get_request_info(client_conn
);
2879 ck_assert(ri
!= NULL
);
2880 ck_assert_str_eq(ri
->local_uri
, "200");
2881 mg_close_connection(client_conn
);
2884 /* Handle form: "POST multipart/form-data", chunked, store, with files */
2886 mg_download("localhost",
2892 "POST /handle_form_store2 HTTP/1.0\r\n"
2893 "Host: localhost:8884\r\n"
2894 "Connection: close\r\n"
2895 "Content-Type: multipart/form-data; "
2896 "boundary=multipart-form-data-boundary--see-RFC-2388\r\n"
2897 "Transfer-Encoding: chunked\r\n"
2899 ck_assert(client_conn
!= NULL
);
2901 boundary
= "--multipart-form-data-boundary--see-RFC-2388\r\n";
2903 send_chunk_string(client_conn
, boundary
);
2904 send_chunk_string(client_conn
, "Content-Disposition: form-data; ");
2905 send_chunk_string(client_conn
, "name=\"storeme\"\r\n");
2906 send_chunk_string(client_conn
, "\r\n");
2907 send_chunk_string(client_conn
, "storetest\r\n");
2909 send_chunk_string(client_conn
, boundary
);
2910 send_chunk_string(client_conn
, "Content-Disposition: form-data; ");
2911 send_chunk_string(client_conn
, "name=\"continue_field_handler\";");
2912 send_chunk_string(client_conn
, "filename=\"file_ignored.txt\"\r\n");
2913 send_chunk_string(client_conn
, "Content-Type: ");
2914 send_chunk_string(client_conn
, "application/octet-stream\r\n");
2915 send_chunk_string(client_conn
, "X-Ignored-Header: xyz\r\n");
2916 send_chunk_string(client_conn
, "\r\n");
2918 /* send some kilobyte of data */
2919 /* sending megabytes to localhost does not allways work in CI test
2920 * environments (depending on the network stack) */
2923 send_chunk_string(client_conn
, "ignore\r\n");
2925 /* send some strings that are almost boundaries */
2926 for (chunk_len
= 1; chunk_len
< strlen(boundary
); chunk_len
++) {
2927 /* chunks from 1 byte to strlen(boundary)-1 */
2928 send_chunk_stringl(client_conn
, boundary
, (unsigned int)chunk_len
);
2929 body_sent
+= chunk_len
;
2931 } while (body_sent
< 8 * 1024);
2932 send_chunk_string(client_conn
, "\r\n");
2934 send_chunk_string(client_conn
, boundary
);
2935 send_chunk_string(client_conn
, "Content-Disposition: form-data; ");
2936 send_chunk_string(client_conn
, "name=\"file2store\";");
2937 send_chunk_string(client_conn
, "filename=\"myfile.txt\"\r\n");
2938 send_chunk_string(client_conn
, "Content-Type: ");
2939 send_chunk_string(client_conn
, "application/octet-stream\r\n");
2940 send_chunk_string(client_conn
, "X-Ignored-Header: xyz\r\n");
2941 send_chunk_string(client_conn
, "\r\n");
2942 for (body_sent
= 0; (int)body_sent
< (int)myfile_content_rep
; body_sent
++) {
2943 send_chunk_string(client_conn
, myfile_content
);
2945 send_chunk_string(client_conn
, "\r\n");
2947 send_chunk_string(client_conn
, boundary
);
2948 send_chunk_string(client_conn
, "Content-Disposition: form-data; ");
2949 send_chunk_string(client_conn
, "name=\"break_field_handler\"\r\n");
2950 send_chunk_string(client_conn
, "\r\n");
2951 send_chunk_string(client_conn
, "abort\r\n");
2953 send_chunk_string(client_conn
, boundary
);
2954 send_chunk_string(client_conn
, "Content-Disposition: form-data; ");
2955 send_chunk_string(client_conn
, "name=\"dontread\"\r\n");
2956 send_chunk_string(client_conn
, "\r\n");
2957 send_chunk_string(client_conn
, "xyz\r\n");
2958 send_chunk_string(client_conn
, "--multipart-form-data-boundary");
2959 send_chunk_string(client_conn
, "--see-RFC-2388--\r\n");
2960 mg_printf(client_conn
, "0\r\n");
2962 for (sleep_cnt
= 0; sleep_cnt
< 30; sleep_cnt
++) {
2964 if (g_field_step
== 1000) {
2968 ri
= mg_get_request_info(client_conn
);
2970 ck_assert(ri
!= NULL
);
2971 ck_assert_str_eq(ri
->local_uri
, "200");
2972 mg_close_connection(client_conn
);
2975 /* Close the server */
2983 START_TEST(test_http_auth
)
2985 #if !defined(NO_FILES)
2986 const char *OPTIONS
[] = {
2991 #if !defined(NO_CACHING)
2992 "static_file_max_age",
2995 "put_delete_auth_file",
2996 "put_delete_auth_file.csv",
3000 struct mg_context
*ctx
;
3001 struct mg_connection
*client_conn
;
3002 char client_err
[256], nonce
[256];
3003 const struct mg_request_info
*client_ri
;
3006 const char *passwd_file
= ".htpasswd";
3007 const char *test_file
= "test_http_auth.test_file.txt";
3008 const char *test_content
= "test_http_auth test_file content";
3010 const char *doc_root
;
3011 const char *auth_request
;
3015 char HA1
[256], HA2
[256], HA
[256];
3016 char HA1_md5_buf
[33], HA2_md5_buf
[33], HA_md5_buf
[33];
3017 char *HA1_md5_ret
, *HA2_md5_ret
, *HA_md5_ret
;
3018 const char *nc
= "00000001";
3019 const char *cnonce
= "6789ABCD";
3023 /* Start with default options */
3024 ctx
= test_mg_start(NULL
, NULL
, OPTIONS
);
3026 ck_assert(ctx
!= NULL
);
3027 domain
= mg_get_option(ctx
, "authentication_domain");
3028 ck_assert(domain
!= NULL
);
3029 len
= strlen(domain
);
3030 ck_assert_uint_gt(len
, 0);
3031 ck_assert_uint_lt(len
, 64);
3032 doc_root
= mg_get_option(ctx
, "document_root");
3033 ck_assert_str_eq(doc_root
, ".");
3035 /* Create a default file in the document root */
3036 f
= fopen(test_file
, "w");
3038 fprintf(f
, "%s", test_content
);
3041 ck_abort_msg("Cannot create file %s", test_file
);
3044 (void)remove(passwd_file
);
3045 (void)remove("put_delete_auth_file.csv");
3047 client_res
= mg_modify_passwords_file("put_delete_auth_file.csv",
3051 ck_assert_int_eq(client_res
, 1);
3053 /* Read file before a .htpasswd file has been created */
3054 memset(client_err
, 0, sizeof(client_err
));
3056 mg_connect_client("127.0.0.1", 8080, 0, client_err
, sizeof(client_err
));
3058 ck_assert_str_eq(client_err
, "");
3059 ck_assert(client_conn
!= NULL
);
3061 mg_printf(client_conn
, "GET /%s HTTP/1.0\r\n\r\n", test_file
);
3063 mg_get_response(client_conn
, client_err
, sizeof(client_err
), 10000);
3064 ck_assert_int_ge(client_res
, 0);
3065 ck_assert_str_eq(client_err
, "");
3066 client_ri
= mg_get_request_info(client_conn
);
3067 ck_assert(client_ri
!= NULL
);
3069 ck_assert_str_eq(client_ri
->local_uri
, "200");
3070 client_res
= (int)mg_read(client_conn
, client_err
, sizeof(client_err
));
3071 ck_assert_int_gt(client_res
, 0);
3072 ck_assert_int_le(client_res
, sizeof(client_err
));
3073 ck_assert_str_eq(client_err
, test_content
);
3074 mg_close_connection(client_conn
);
3078 /* Create a .htpasswd file */
3079 client_res
= mg_modify_passwords_file(passwd_file
, domain
, "user", "pass");
3080 ck_assert_int_eq(client_res
, 1);
3082 client_res
= mg_modify_passwords_file(NULL
, domain
, "user", "pass");
3083 ck_assert_int_eq(client_res
, 0); /* Filename is required */
3087 /* Repeat test after .htpasswd is created */
3088 memset(client_err
, 0, sizeof(client_err
));
3090 mg_connect_client("127.0.0.1", 8080, 0, client_err
, sizeof(client_err
));
3092 ck_assert_str_eq(client_err
, "");
3093 ck_assert(client_conn
!= NULL
);
3095 mg_printf(client_conn
, "GET /%s HTTP/1.0\r\n\r\n", test_file
);
3097 mg_get_response(client_conn
, client_err
, sizeof(client_err
), 10000);
3098 ck_assert_int_ge(client_res
, 0);
3099 ck_assert_str_eq(client_err
, "");
3100 client_ri
= mg_get_request_info(client_conn
);
3101 ck_assert(client_ri
!= NULL
);
3103 ck_assert_str_eq(client_ri
->local_uri
, "401");
3105 auth_request
= NULL
;
3106 for (i
= 0; i
< client_ri
->num_headers
; i
++) {
3107 if (!mg_strcasecmp(client_ri
->http_headers
[i
].name
,
3108 "WWW-Authenticate")) {
3109 ck_assert_ptr_eq(auth_request
, NULL
);
3110 auth_request
= client_ri
->http_headers
[i
].value
;
3111 ck_assert_ptr_ne(auth_request
, NULL
);
3114 ck_assert_ptr_ne(auth_request
, NULL
);
3115 str
= "Digest qop=\"auth\", realm=\"";
3117 ck_assert(!mg_strncasecmp(auth_request
, str
, len
));
3118 ck_assert(!strncmp(auth_request
+ len
, domain
, strlen(domain
)));
3119 len
+= strlen(domain
);
3120 str
= "\", nonce=\"";
3121 ck_assert(!strncmp(auth_request
+ len
, str
, strlen(str
)));
3123 str
= strchr(auth_request
+ len
, '\"');
3124 ck_assert_ptr_ne(str
, NULL
);
3125 ck_assert_ptr_ne(str
, auth_request
+ len
);
3126 /* nonce is from including (auth_request + len) to excluding (str) */
3127 ck_assert_int_gt((ptrdiff_t)(str
) - (ptrdiff_t)(auth_request
+ len
), 0);
3128 ck_assert_int_lt((ptrdiff_t)(str
) - (ptrdiff_t)(auth_request
+ len
),
3129 (ptrdiff_t)sizeof(nonce
));
3130 memset(nonce
, 0, sizeof(nonce
));
3133 (size_t)((ptrdiff_t)(str
) - (ptrdiff_t)(auth_request
+ len
)));
3134 memset(HA1
, 0, sizeof(HA1
));
3135 memset(HA2
, 0, sizeof(HA2
));
3136 memset(HA
, 0, sizeof(HA
));
3137 memset(HA1_md5_buf
, 0, sizeof(HA1_md5_buf
));
3138 memset(HA2_md5_buf
, 0, sizeof(HA2_md5_buf
));
3139 memset(HA_md5_buf
, 0, sizeof(HA_md5_buf
));
3141 sprintf(HA1
, "%s:%s:%s", "user", domain
, "pass");
3142 sprintf(HA2
, "%s:/%s", "GET", test_file
);
3143 HA1_md5_ret
= mg_md5(HA1_md5_buf
, HA1
, NULL
);
3144 HA2_md5_ret
= mg_md5(HA2_md5_buf
, HA2
, NULL
);
3146 ck_assert_ptr_eq(HA1_md5_ret
, HA1_md5_buf
);
3147 ck_assert_ptr_eq(HA2_md5_ret
, HA2_md5_buf
);
3149 HA_md5_ret
= mg_md5(HA_md5_buf
, "user", ":", domain
, ":", "pass", NULL
);
3150 ck_assert_ptr_eq(HA_md5_ret
, HA_md5_buf
);
3151 ck_assert_str_eq(HA1_md5_ret
, HA_md5_buf
);
3153 HA_md5_ret
= mg_md5(HA_md5_buf
, "GET", ":", "/", test_file
, NULL
);
3154 ck_assert_ptr_eq(HA_md5_ret
, HA_md5_buf
);
3155 ck_assert_str_eq(HA2_md5_ret
, HA_md5_buf
);
3157 HA_md5_ret
= mg_md5(HA_md5_buf
,
3170 ck_assert_ptr_eq(HA_md5_ret
, HA_md5_buf
);
3172 mg_close_connection(client_conn
);
3174 /* Retry with authorization */
3175 memset(client_err
, 0, sizeof(client_err
));
3177 mg_connect_client("127.0.0.1", 8080, 0, client_err
, sizeof(client_err
));
3179 ck_assert_str_eq(client_err
, "");
3180 ck_assert(client_conn
!= NULL
);
3182 mg_printf(client_conn
, "GET /%s HTTP/1.0\r\n", test_file
);
3183 mg_printf(client_conn
,
3184 "Authorization: Digest "
3192 "response=\"%s\"\r\n\r\n",
3201 mg_get_response(client_conn
, client_err
, sizeof(client_err
), 10000);
3202 ck_assert_int_ge(client_res
, 0);
3203 ck_assert_str_eq(client_err
, "");
3204 client_ri
= mg_get_request_info(client_conn
);
3205 ck_assert(client_ri
!= NULL
);
3207 ck_assert_str_eq(client_ri
->local_uri
, "200");
3208 client_res
= (int)mg_read(client_conn
, client_err
, sizeof(client_err
));
3209 ck_assert_int_gt(client_res
, 0);
3210 ck_assert_int_le(client_res
, sizeof(client_err
));
3211 ck_assert_str_eq(client_err
, test_content
);
3212 mg_close_connection(client_conn
);
3216 /* Retry DELETE with authorization of a user not authorized for DELETE */
3217 memset(client_err
, 0, sizeof(client_err
));
3219 mg_connect_client("127.0.0.1", 8080, 0, client_err
, sizeof(client_err
));
3221 ck_assert_str_eq(client_err
, "");
3222 ck_assert(client_conn
!= NULL
);
3224 mg_printf(client_conn
, "DELETE /%s HTTP/1.0\r\n", test_file
);
3225 mg_printf(client_conn
,
3226 "Authorization: Digest "
3234 "response=\"%s\"\r\n\r\n",
3243 mg_get_response(client_conn
, client_err
, sizeof(client_err
), 10000);
3244 ck_assert_int_ge(client_res
, 0);
3245 ck_assert_str_eq(client_err
, "");
3246 client_ri
= mg_get_request_info(client_conn
);
3247 ck_assert(client_ri
!= NULL
);
3249 ck_assert_str_eq(client_ri
->local_uri
, "401");
3250 mg_close_connection(client_conn
);
3254 /* Remove the user from the .htpasswd file again */
3255 client_res
= mg_modify_passwords_file(passwd_file
, domain
, "user", NULL
);
3256 ck_assert_int_eq(client_res
, 1);
3261 /* Try to access the file again. Expected: 401 error */
3262 memset(client_err
, 0, sizeof(client_err
));
3264 mg_connect_client("127.0.0.1", 8080, 0, client_err
, sizeof(client_err
));
3266 ck_assert_str_eq(client_err
, "");
3267 ck_assert(client_conn
!= NULL
);
3269 mg_printf(client_conn
, "GET /%s HTTP/1.0\r\n\r\n", test_file
);
3271 mg_get_response(client_conn
, client_err
, sizeof(client_err
), 10000);
3272 ck_assert_int_ge(client_res
, 0);
3273 ck_assert_str_eq(client_err
, "");
3274 client_ri
= mg_get_request_info(client_conn
);
3275 ck_assert(client_ri
!= NULL
);
3277 ck_assert_str_eq(client_ri
->local_uri
, "401");
3278 mg_close_connection(client_conn
);
3283 /* Now remove the password file */
3284 (void)remove(passwd_file
);
3288 /* Access to the file must work like before */
3289 memset(client_err
, 0, sizeof(client_err
));
3291 mg_connect_client("127.0.0.1", 8080, 0, client_err
, sizeof(client_err
));
3293 ck_assert_str_eq(client_err
, "");
3294 ck_assert(client_conn
!= NULL
);
3296 mg_printf(client_conn
, "GET /%s HTTP/1.0\r\n\r\n", test_file
);
3298 mg_get_response(client_conn
, client_err
, sizeof(client_err
), 10000);
3299 ck_assert_int_ge(client_res
, 0);
3300 ck_assert_str_eq(client_err
, "");
3301 client_ri
= mg_get_request_info(client_conn
);
3302 ck_assert(client_ri
!= NULL
);
3304 ck_assert_str_eq(client_ri
->local_uri
, "200");
3305 client_res
= (int)mg_read(client_conn
, client_err
, sizeof(client_err
));
3306 ck_assert_int_gt(client_res
, 0);
3307 ck_assert_int_le(client_res
, sizeof(client_err
));
3308 ck_assert_str_eq(client_err
, test_content
);
3309 mg_close_connection(client_conn
);
3314 /* Stop the server and clean up */
3316 (void)remove(test_file
);
3317 (void)remove(passwd_file
);
3318 (void)remove("put_delete_auth_file.csv");
3326 START_TEST(test_keep_alive
)
3328 struct mg_context
*ctx
;
3329 const char *OPTIONS
[] =
3330 { "listening_ports",
3332 "request_timeout_ms",
3334 "enable_keep_alive",
3336 #if !defined(NO_FILES)
3339 "enable_directory_listing",
3344 struct mg_connection
*client_conn
;
3345 char client_err
[256];
3346 const struct mg_request_info
*client_ri
;
3348 const char *connection_header
;
3352 ctx
= test_mg_start(NULL
, NULL
, OPTIONS
);
3354 ck_assert(ctx
!= NULL
);
3356 /* HTTP 1.1 GET request */
3357 memset(client_err
, 0, sizeof(client_err
));
3359 mg_connect_client("127.0.0.1", 8081, 0, client_err
, sizeof(client_err
));
3361 ck_assert_str_eq(client_err
, "");
3362 ck_assert(client_conn
!= NULL
);
3364 mg_printf(client_conn
,
3365 "GET / HTTP/1.1\r\nHost: "
3366 "localhost:8081\r\nConnection: keep-alive\r\n\r\n");
3368 mg_get_response(client_conn
, client_err
, sizeof(client_err
), 10000);
3369 ck_assert_int_ge(client_res
, 0);
3370 ck_assert_str_eq(client_err
, "");
3371 client_ri
= mg_get_request_info(client_conn
);
3372 ck_assert(client_ri
!= NULL
);
3374 #if defined(NO_FILES)
3375 ck_assert_str_eq(client_ri
->local_uri
, "404");
3377 ck_assert_str_eq(client_ri
->local_uri
, "403");
3380 connection_header
= 0;
3381 for (i
= 0; i
< client_ri
->num_headers
; i
++) {
3382 if (!mg_strcasecmp(client_ri
->http_headers
[i
].name
, "Connection")) {
3383 ck_assert_ptr_eq(connection_header
, NULL
);
3384 connection_header
= client_ri
->http_headers
[i
].value
;
3385 ck_assert_ptr_ne(connection_header
, NULL
);
3388 /* Error replies will close the connection, even if keep-alive is set. */
3389 ck_assert_ptr_ne(connection_header
, NULL
);
3390 ck_assert_str_eq(connection_header
, "close");
3391 mg_close_connection(client_conn
);
3395 /* TODO: request a file and keep alive
3396 * (will only work if NO_FILES is not set). */
3398 /* Stop the server and clean up */
3406 START_TEST(test_error_handling
)
3408 struct mg_context
*ctx
;
3411 char bad_thread_num
[32] = "badnumber";
3413 struct mg_callbacks callbacks
;
3416 struct mg_connection
*client_conn
;
3417 char client_err
[256];
3418 const struct mg_request_info
*client_ri
;
3421 const char *OPTIONS
[32];
3426 #if !defined(NO_FILES)
3427 OPTIONS
[opt_cnt
++] = "document_root";
3428 OPTIONS
[opt_cnt
++] = ".";
3430 OPTIONS
[opt_cnt
++] = "error_pages";
3431 OPTIONS
[opt_cnt
++] = "./";
3432 OPTIONS
[opt_cnt
++] = "listening_ports";
3433 OPTIONS
[opt_cnt
++] = "8080";
3434 OPTIONS
[opt_cnt
++] = "num_threads";
3435 OPTIONS
[opt_cnt
++] = bad_thread_num
;
3436 OPTIONS
[opt_cnt
++] = "unknown_option";
3437 OPTIONS
[opt_cnt
++] = "unknown_option_value";
3438 OPTIONS
[opt_cnt
] = NULL
;
3440 memset(&callbacks
, 0, sizeof(callbacks
));
3442 callbacks
.log_message
= log_msg_func
;
3444 /* test with unknown option */
3445 memset(errmsg
, 0, sizeof(errmsg
));
3446 ctx
= test_mg_start(&callbacks
, (void *)errmsg
, OPTIONS
);
3448 /* Details of errmsg may vary, but it may not be empty */
3449 ck_assert_str_ne(errmsg
, "");
3450 ck_assert(ctx
== NULL
);
3451 ck_assert_str_eq(errmsg
, "Invalid option: unknown_option");
3453 /* Remove invalid option */
3454 for (i
= 0; OPTIONS
[i
]; i
++) {
3455 if (strstr(OPTIONS
[i
], "unknown_option")) {
3460 /* Test with bad num_thread option */
3461 memset(errmsg
, 0, sizeof(errmsg
));
3462 ctx
= test_mg_start(&callbacks
, (void *)errmsg
, OPTIONS
);
3464 /* Details of errmsg may vary, but it may not be empty */
3465 ck_assert_str_ne(errmsg
, "");
3466 ck_assert(ctx
== NULL
);
3467 ck_assert_str_eq(errmsg
, "Invalid number of worker threads");
3469 /* Set to a number - but use a number above the limit */
3470 #ifdef MAX_WORKER_THREADS
3471 sprintf(bad_thread_num
, "%u", MAX_WORKER_THREADS
+ 1);
3473 sprintf(bad_thread_num
, "%lu", 1000000000lu);
3476 /* Test with bad num_thread option */
3477 memset(errmsg
, 0, sizeof(errmsg
));
3478 ctx
= test_mg_start(&callbacks
, (void *)errmsg
, OPTIONS
);
3480 /* Details of errmsg may vary, but it may not be empty */
3481 ck_assert_str_ne(errmsg
, "");
3482 ck_assert(ctx
== NULL
);
3483 ck_assert_str_eq(errmsg
, "Too many worker threads");
3486 /* HTTP 1.0 GET request - server is not running */
3487 memset(client_err
, 0, sizeof(client_err
));
3489 mg_connect_client("127.0.0.1", 8080, 0, client_err
, sizeof(client_err
));
3490 ck_assert(client_conn
== NULL
);
3492 /* Error message detail may vary - it may not be empty ans should contain
3493 * some information "connect" failed */
3494 ck_assert_str_ne(client_err
, "");
3495 ck_assert(strstr(client_err
, "connect"));
3498 /* This time start the server with a valid configuration */
3499 sprintf(bad_thread_num
, "%i", 1);
3500 memset(errmsg
, 0, sizeof(errmsg
));
3501 ctx
= test_mg_start(&callbacks
, (void *)errmsg
, OPTIONS
);
3503 ck_assert_str_eq(errmsg
, "");
3504 ck_assert(ctx
!= NULL
);
3507 /* Server is running now */
3510 /* Remove error files (in case they exist) */
3511 (void)remove("error.htm");
3512 (void)remove("error4xx.htm");
3513 (void)remove("error404.htm");
3516 /* Ask for something not existing - should get default 404 */
3517 memset(client_err
, 0, sizeof(client_err
));
3519 mg_connect_client("127.0.0.1", 8080, 0, client_err
, sizeof(client_err
));
3521 ck_assert_str_eq(client_err
, "");
3522 ck_assert(client_conn
!= NULL
);
3524 mg_printf(client_conn
, "GET /something/not/existing HTTP/1.0\r\n\r\n");
3526 mg_get_response(client_conn
, client_err
, sizeof(client_err
), 10000);
3527 ck_assert_int_ge(client_res
, 0);
3528 ck_assert_str_eq(client_err
, "");
3529 client_ri
= mg_get_request_info(client_conn
);
3530 ck_assert(client_ri
!= NULL
);
3532 ck_assert_str_eq(client_ri
->local_uri
, "404");
3533 mg_close_connection(client_conn
);
3536 /* Create an error.htm file */
3537 f
= fopen("error.htm", "wt");
3538 ck_assert(f
!= NULL
);
3539 (void)fprintf(f
, "err-all");
3543 /* Ask for something not existing - should get error.htm */
3544 memset(client_err
, 0, sizeof(client_err
));
3546 mg_connect_client("127.0.0.1", 8080, 0, client_err
, sizeof(client_err
));
3548 ck_assert_str_eq(client_err
, "");
3549 ck_assert(client_conn
!= NULL
);
3551 mg_printf(client_conn
, "GET /something/not/existing HTTP/1.0\r\n\r\n");
3553 mg_get_response(client_conn
, client_err
, sizeof(client_err
), 10000);
3554 ck_assert_int_ge(client_res
, 0);
3555 ck_assert_str_eq(client_err
, "");
3556 client_ri
= mg_get_request_info(client_conn
);
3557 ck_assert(client_ri
!= NULL
);
3559 ck_assert_str_eq(client_ri
->local_uri
, "200");
3561 client_res
= (int)mg_read(client_conn
, client_err
, sizeof(client_err
));
3562 mg_close_connection(client_conn
);
3563 ck_assert_int_eq(client_res
, 7);
3565 ck_assert_str_eq(client_err
, "err-all");
3568 /* Create an error4xx.htm file */
3569 f
= fopen("error4xx.htm", "wt");
3570 ck_assert(f
!= NULL
);
3571 (void)fprintf(f
, "err-4xx");
3575 /* Ask for something not existing - should get error4xx.htm */
3576 memset(client_err
, 0, sizeof(client_err
));
3578 mg_connect_client("127.0.0.1", 8080, 0, client_err
, sizeof(client_err
));
3580 ck_assert_str_eq(client_err
, "");
3581 ck_assert(client_conn
!= NULL
);
3583 mg_printf(client_conn
, "GET /something/not/existing HTTP/1.0\r\n\r\n");
3585 mg_get_response(client_conn
, client_err
, sizeof(client_err
), 10000);
3586 ck_assert_int_ge(client_res
, 0);
3587 ck_assert_str_eq(client_err
, "");
3588 client_ri
= mg_get_request_info(client_conn
);
3589 ck_assert(client_ri
!= NULL
);
3591 ck_assert_str_eq(client_ri
->local_uri
, "200");
3593 client_res
= (int)mg_read(client_conn
, client_err
, sizeof(client_err
));
3594 mg_close_connection(client_conn
);
3595 ck_assert_int_eq(client_res
, 7);
3597 ck_assert_str_eq(client_err
, "err-4xx");
3600 /* Create an error404.htm file */
3601 f
= fopen("error404.htm", "wt");
3602 ck_assert(f
!= NULL
);
3603 (void)fprintf(f
, "err-404");
3607 /* Ask for something not existing - should get error404.htm */
3608 memset(client_err
, 0, sizeof(client_err
));
3610 mg_connect_client("127.0.0.1", 8080, 0, client_err
, sizeof(client_err
));
3612 ck_assert_str_eq(client_err
, "");
3613 ck_assert(client_conn
!= NULL
);
3615 mg_printf(client_conn
, "GET /something/not/existing HTTP/1.0\r\n\r\n");
3617 mg_get_response(client_conn
, client_err
, sizeof(client_err
), 10000);
3618 ck_assert_int_ge(client_res
, 0);
3619 ck_assert_str_eq(client_err
, "");
3620 client_ri
= mg_get_request_info(client_conn
);
3621 ck_assert(client_ri
!= NULL
);
3623 ck_assert_str_eq(client_ri
->local_uri
, "200");
3625 client_res
= (int)mg_read(client_conn
, client_err
, sizeof(client_err
));
3626 mg_close_connection(client_conn
);
3627 ck_assert_int_eq(client_res
, 7);
3629 ck_assert_str_eq(client_err
, "err-404");
3633 /* Ask in a malformed way - should get error4xx.htm */
3634 memset(client_err
, 0, sizeof(client_err
));
3636 mg_connect_client("127.0.0.1", 8080, 0, client_err
, sizeof(client_err
));
3638 ck_assert_str_eq(client_err
, "");
3639 ck_assert(client_conn
!= NULL
);
3641 mg_printf(client_conn
, "Gimme some file!\r\n\r\n");
3643 mg_get_response(client_conn
, client_err
, sizeof(client_err
), 10000);
3644 ck_assert_int_ge(client_res
, 0);
3645 ck_assert_str_eq(client_err
, "");
3646 client_ri
= mg_get_request_info(client_conn
);
3647 ck_assert(client_ri
!= NULL
);
3649 ck_assert_str_eq(client_ri
->local_uri
, "200");
3651 client_res
= (int)mg_read(client_conn
, client_err
, sizeof(client_err
));
3652 mg_close_connection(client_conn
);
3653 ck_assert_int_eq(client_res
, 7);
3655 ck_assert_str_eq(client_err
, "err-4xx");
3659 /* Remove all error files created by this test */
3660 (void)remove("error.htm");
3661 (void)remove("error4xx.htm");
3662 (void)remove("error404.htm");
3665 /* Stop the server */
3669 /* HTTP 1.1 GET request - must not work, since server is already stopped */
3670 memset(client_err
, 0, sizeof(client_err
));
3672 mg_connect_client("127.0.0.1", 8080, 0, client_err
, sizeof(client_err
));
3674 ck_assert(client_conn
== NULL
);
3675 ck_assert_str_ne(client_err
, "");
3684 START_TEST(test_error_log_file
)
3687 struct mg_context
*ctx
;
3688 const char *OPTIONS
[32];
3692 struct mg_connection
*client
;
3693 char client_err_buf
[256];
3694 char client_data_buf
[256];
3695 const struct mg_request_info
*client_ri
;
3697 /* File content check var */
3704 /* Set options and start server */
3705 OPTIONS
[opt_cnt
++] = "listening_ports";
3706 OPTIONS
[opt_cnt
++] = "8080";
3707 OPTIONS
[opt_cnt
++] = "error_log_file";
3708 OPTIONS
[opt_cnt
++] = "error.log";
3709 OPTIONS
[opt_cnt
++] = "access_log_file";
3710 OPTIONS
[opt_cnt
++] = "access.log";
3711 #if !defined(NO_FILES)
3712 OPTIONS
[opt_cnt
++] = "document_root";
3713 OPTIONS
[opt_cnt
++] = ".";
3715 OPTIONS
[opt_cnt
] = NULL
;
3717 ctx
= test_mg_start(NULL
, 0, OPTIONS
);
3718 ck_assert(ctx
!= NULL
);
3720 /* Remove log files (they may exist from previous incomplete runs of
3722 (void)remove("error.log");
3723 (void)remove("access.log");
3725 /* connect client */
3726 memset(client_err_buf
, 0, sizeof(client_err_buf
));
3727 memset(client_data_buf
, 0, sizeof(client_data_buf
));
3729 client
= mg_download("127.0.0.1",
3733 sizeof(client_err_buf
),
3734 "GET /not_existing_file.ext HTTP/1.0\r\n\r\n");
3736 ck_assert(ctx
!= NULL
);
3737 ck_assert_str_eq(client_err_buf
, "");
3739 client_ri
= mg_get_request_info(client
);
3741 ck_assert(client_ri
!= NULL
);
3742 ck_assert_str_eq(client_ri
->local_uri
, "404");
3744 /* Close the client connection */
3745 mg_close_connection(client
);
3747 /* Stop the server */
3751 /* Check access.log */
3752 memset(buf
, 0, sizeof(buf
));
3753 f
= fopen("access.log", "r");
3754 ck_assert_msg(f
!= NULL
, "Cannot open access log file");
3755 ok
= (NULL
!= fgets(buf
, sizeof(buf
) - 1, f
));
3757 ck_assert_msg(ok
, "Cannot read access log file");
3758 len
= (int)strlen(buf
);
3759 ck_assert_int_gt(len
, 0);
3760 ok
= (NULL
!= strstr(buf
, "not_existing_file.ext"));
3761 ck_assert_msg(ok
, "Did not find uri in access log file");
3762 ok
= (NULL
!= strstr(buf
, "404"));
3763 ck_assert_msg(ok
, "Did not find HTTP status code in access log file");
3765 /* Check error.log */
3766 memset(buf
, 0, sizeof(buf
));
3767 f
= fopen("error.log", "r");
3769 (void)fgets(buf
, sizeof(buf
) - 1, f
);
3772 ck_assert_msg(f
== NULL
,
3773 "Should not create error log file on 404, but got [%s]",
3776 /* Remove log files */
3777 (void)remove("error.log");
3778 (void)remove("access.log");
3780 /* Start server with bad options */
3781 ck_assert_str_eq(OPTIONS
[0], "listening_ports");
3782 OPTIONS
[1] = "bad port syntax";
3784 ctx
= test_mg_start(NULL
, 0, OPTIONS
);
3787 "Should not be able to start server with bad port configuration");
3789 /* Check access.log */
3790 memset(buf
, 0, sizeof(buf
));
3791 f
= fopen("access.log", "r");
3793 (void)fgets(buf
, sizeof(buf
) - 1, f
);
3798 "Should not create access log file if start fails, but got [%s]",
3801 /* Check error.log */
3802 memset(buf
, 0, sizeof(buf
));
3803 f
= fopen("error.log", "r");
3804 ck_assert_msg(f
!= NULL
, "Cannot open access log file");
3805 ok
= (NULL
!= fgets(buf
, sizeof(buf
) - 1, f
));
3807 ck_assert_msg(ok
, "Cannot read access log file");
3808 len
= (int)strlen(buf
);
3809 ck_assert_int_gt(len
, 0);
3810 ok
= (NULL
!= strstr(buf
, "port"));
3811 ck_assert_msg(ok
, "Did not find port as error reason in error log file");
3814 /* Remove log files */
3815 (void)remove("error.log");
3816 (void)remove("access.log");
3824 test_throttle_begin_request(struct mg_connection
*conn
)
3826 const struct mg_request_info
*ri
;
3827 long unsigned len
= 1024 * 10;
3828 const char *block
= "0123456789";
3829 unsigned long i
, blocklen
;
3831 ck_assert(conn
!= NULL
);
3832 ri
= mg_get_request_info(conn
);
3833 ck_assert(ri
!= NULL
);
3835 ck_assert_str_eq(ri
->request_method
, "GET");
3836 ck_assert_str_eq(ri
->request_uri
, "/throttle");
3837 ck_assert_str_eq(ri
->local_uri
, "/throttle");
3838 ck_assert_str_eq(ri
->http_version
, "1.0");
3839 ck_assert_str_eq(ri
->query_string
, "q");
3840 ck_assert_str_eq(ri
->remote_addr
, "127.0.0.1");
3843 "HTTP/1.1 200 OK\r\n"
3844 "Content-Length: %lu\r\n"
3845 "Connection: close\r\n\r\n",
3848 blocklen
= (unsigned long)strlen(block
);
3850 for (i
= 0; i
< len
; i
+= blocklen
) {
3851 mg_write(conn
, block
, blocklen
);
3856 return 987; /* Not a valid HTTP response code,
3857 * but it should be written to the log and passed to
3863 test_throttle_end_request(const struct mg_connection
*conn
,
3864 int reply_status_code
)
3866 const struct mg_request_info
*ri
;
3868 ck_assert(conn
!= NULL
);
3869 ri
= mg_get_request_info(conn
);
3870 ck_assert(ri
!= NULL
);
3872 ck_assert_str_eq(ri
->request_method
, "GET");
3873 ck_assert_str_eq(ri
->request_uri
, "/throttle");
3874 ck_assert_str_eq(ri
->local_uri
, "/throttle");
3875 ck_assert_str_eq(ri
->http_version
, "1.0");
3876 ck_assert_str_eq(ri
->query_string
, "q");
3877 ck_assert_str_eq(ri
->remote_addr
, "127.0.0.1");
3879 ck_assert_int_eq(reply_status_code
, 987);
3883 START_TEST(test_throttle
)
3886 struct mg_context
*ctx
;
3887 struct mg_callbacks callbacks
;
3888 const char *OPTIONS
[32];
3892 struct mg_connection
*client
;
3893 char client_err_buf
[256];
3894 char client_data_buf
[256];
3895 const struct mg_request_info
*client_ri
;
3905 /* Set options and start server */
3906 #if !defined(NO_FILES)
3907 OPTIONS
[opt_cnt
++] = "document_root";
3908 OPTIONS
[opt_cnt
++] = ".";
3910 OPTIONS
[opt_cnt
++] = "listening_ports";
3911 OPTIONS
[opt_cnt
++] = "8080";
3912 OPTIONS
[opt_cnt
++] = "throttle";
3913 OPTIONS
[opt_cnt
++] = "*=1k";
3914 OPTIONS
[opt_cnt
] = NULL
;
3916 memset(&callbacks
, 0, sizeof(callbacks
));
3917 callbacks
.begin_request
= test_throttle_begin_request
;
3918 callbacks
.end_request
= test_throttle_end_request
;
3920 ctx
= test_mg_start(&callbacks
, 0, OPTIONS
);
3921 ck_assert(ctx
!= NULL
);
3923 /* connect client */
3924 memset(client_err_buf
, 0, sizeof(client_err_buf
));
3925 memset(client_data_buf
, 0, sizeof(client_data_buf
));
3927 strcpy(client_err_buf
, "reset-content");
3928 client
= mg_download("127.0.0.1",
3932 sizeof(client_err_buf
),
3933 "GET /throttle?q HTTP/1.0\r\n\r\n");
3935 ck_assert(ctx
!= NULL
);
3936 ck_assert_str_eq(client_err_buf
, "");
3938 client_ri
= mg_get_request_info(client
);
3940 ck_assert(client_ri
!= NULL
);
3941 ck_assert_str_eq(client_ri
->local_uri
, "200");
3943 ck_assert_int_eq(client_ri
->content_length
, 1024 * 10);
3947 while (data_read
< client_ri
->content_length
) {
3948 r
= mg_read(client
, client_data_buf
, sizeof(client_data_buf
));
3949 ck_assert_int_ge(r
, 0);
3953 dt
= difftime(t1
, t0
) * 1000.0; /* Elapsed time in ms - in most systems
3954 * only with second resolution */
3956 /* Time estimation: Data size is 10 kB, with 1 kB/s speed limit.
3957 * The first block (1st kB) is transferred immediately, the second
3958 * block (2nd kB) one second later, the third block (3rd kB) two
3959 * seconds later, .. the last block (10th kB) nine seconds later.
3960 * The resolution of time measurement using the "time" C library
3961 * function is 1 second, so we should add +/- one second tolerance.
3962 * Thus, download of 10 kB with 1 kB/s should not be faster than
3965 /* Check if there are at least 8 seconds */
3966 ck_assert_int_ge((int)dt
, 8 * 1000);
3968 /* Nothing left to read */
3969 r
= mg_read(client
, client_data_buf
, sizeof(client_data_buf
));
3970 ck_assert_int_eq(r
, 0);
3972 /* Close the client connection */
3973 mg_close_connection(client
);
3975 /* Stop the server */
3983 START_TEST(test_init_library
)
3985 unsigned f_avail
, f_ret
;
3989 f_avail
= mg_check_feature(0xFF);
3990 f_ret
= mg_init_library(f_avail
);
3991 ck_assert_uint_eq(f_ret
, f_avail
);
3996 #define LARGE_FILE_SIZE (1024 * 1024 * 10)
3999 test_large_file_begin_request(struct mg_connection
*conn
)
4001 const struct mg_request_info
*ri
;
4002 long unsigned len
= LARGE_FILE_SIZE
;
4003 const char *block
= "0123456789";
4007 ck_assert(conn
!= NULL
);
4008 ri
= mg_get_request_info(conn
);
4009 ck_assert(ri
!= NULL
);
4011 ck_assert_str_eq(ri
->request_method
, "GET");
4012 ck_assert_str_eq(ri
->http_version
, "1.1");
4013 ck_assert_str_eq(ri
->remote_addr
, "127.0.0.1");
4014 ck_assert_ptr_eq(ri
->query_string
, NULL
);
4015 ck_assert_ptr_ne(ri
->local_uri
, NULL
);
4018 "HTTP/1.1 200 OK\r\n"
4019 "Content-Length: %lu\r\n"
4020 "Connection: close\r\n\r\n",
4023 blocklen
= strlen(block
);
4025 for (i
= 0; i
< len
; i
+= blocklen
) {
4026 mg_write(conn
, block
, blocklen
);
4035 START_TEST(test_large_file
)
4038 struct mg_context
*ctx
;
4039 struct mg_callbacks callbacks
;
4040 const char *OPTIONS
[32];
4042 #if !defined(NO_SSL)
4043 const char *ssl_cert
= locate_ssl_cert();
4045 char errmsg
[256] = {0};
4048 struct mg_connection
*client
;
4049 char client_err_buf
[256];
4050 char client_data_buf
[256];
4051 const struct mg_request_info
*client_ri
;
4054 int retry
, retry_ok_cnt
, retry_fail_cnt
;
4058 /* Set options and start server */
4059 #if !defined(NO_FILES)
4060 OPTIONS
[opt_cnt
++] = "document_root";
4061 OPTIONS
[opt_cnt
++] = ".";
4064 OPTIONS
[opt_cnt
++] = "listening_ports";
4065 OPTIONS
[opt_cnt
++] = "8080";
4067 OPTIONS
[opt_cnt
++] = "listening_ports";
4068 OPTIONS
[opt_cnt
++] = "8443s";
4069 OPTIONS
[opt_cnt
++] = "ssl_certificate";
4070 OPTIONS
[opt_cnt
++] = ssl_cert
;
4072 /* The Apple builds on Travis CI seem to have problems with TLS1.x
4073 * Allow SSLv3 and TLS */
4074 OPTIONS
[opt_cnt
++] = "ssl_protocol_version";
4075 OPTIONS
[opt_cnt
++] = "2";
4077 /* The Linux builds on Travis CI work fine with TLS1.2 */
4078 OPTIONS
[opt_cnt
++] = "ssl_protocol_version";
4079 OPTIONS
[opt_cnt
++] = "4";
4081 ck_assert(ssl_cert
!= NULL
);
4083 OPTIONS
[opt_cnt
] = NULL
;
4086 memset(&callbacks
, 0, sizeof(callbacks
));
4087 callbacks
.begin_request
= test_large_file_begin_request
;
4088 callbacks
.log_message
= log_msg_func
;
4090 ctx
= test_mg_start(&callbacks
, (void *)errmsg
, OPTIONS
);
4091 ck_assert_str_eq(errmsg
, "");
4092 ck_assert(ctx
!= NULL
);
4094 /* Try downloading several times */
4097 for (retry
= 0; retry
< 3; retry
++) {
4099 /* connect client */
4100 memset(client_err_buf
, 0, sizeof(client_err_buf
));
4101 memset(client_data_buf
, 0, sizeof(client_data_buf
));
4104 mg_download("127.0.0.1",
4113 sizeof(client_err_buf
),
4114 "GET /large.file HTTP/1.1\r\nHost: 127.0.0.1\r\n\r\n");
4116 ck_assert(client
!= NULL
);
4117 ck_assert_str_eq(client_err_buf
, "");
4119 client_ri
= mg_get_request_info(client
);
4121 ck_assert(client_ri
!= NULL
);
4122 ck_assert_str_eq(client_ri
->local_uri
, "200");
4124 ck_assert_int_eq(client_ri
->content_length
, LARGE_FILE_SIZE
);
4127 while (data_read
< client_ri
->content_length
) {
4128 r
= mg_read(client
, client_data_buf
, sizeof(client_data_buf
));
4136 /* Nothing left to read */
4137 r
= mg_read(client
, client_data_buf
, sizeof(client_data_buf
));
4139 ck_assert_int_eq(r
, -1);
4142 ck_assert_int_eq(r
, 0);
4146 /* Close the client connection */
4147 mg_close_connection(client
);
4151 // TODO: Check this problem on AppVeyor
4152 // ck_assert_int_le(retry_fail_cnt, 2);
4153 // ck_assert_int_ge(retry_ok_cnt, 1);
4155 ck_assert_int_eq(retry_fail_cnt
, 0);
4156 ck_assert_int_eq(retry_ok_cnt
, 3);
4159 /* Stop the server */
4167 static int test_mg_store_body_con_len
= 20000;
4171 test_mg_store_body_put_delete_handler(struct mg_connection
*conn
, void *ignored
)
4173 char path
[4096] = {0};
4174 const struct mg_request_info
*info
= mg_get_request_info(conn
);
4181 sprintf(path
, "./%s", info
->local_uri
);
4182 rc
= mg_store_body(conn
, path
);
4184 ck_assert_int_eq(test_mg_store_body_con_len
, rc
);
4188 "HTTP/1.1 500 Internal Server Error\r\n"
4189 "Content-Type:text/plain;charset=UTF-8\r\n"
4190 "Connection:close\r\n\r\n"
4194 mg_close_connection(conn
);
4196 /* Debug output for tests */
4197 printf("mg_store_body(%s) failed (ret: %ld)\n", path
, (long)rc
);
4203 "HTTP/1.1 200 OK\r\n"
4204 "Content-Type:text/plain;charset=UTF-8\r\n"
4205 "Connection:close\r\n\r\n"
4206 "%s OK (%ld bytes saved)\n",
4209 mg_close_connection(conn
);
4211 /* Debug output for tests */
4212 printf("mg_store_body(%s) OK (%ld bytes)\n", path
, (long)rc
);
4221 test_mg_store_body_begin_request_callback(struct mg_connection
*conn
)
4223 const struct mg_request_info
*info
= mg_get_request_info(conn
);
4227 /* Debug output for tests */
4228 printf("test_mg_store_body_begin_request_callback called (%s)\n",
4229 info
->request_method
);
4231 if ((strcmp(info
->request_method
, "PUT") == 0)
4232 || (strcmp(info
->request_method
, "DELETE") == 0)) {
4233 return test_mg_store_body_put_delete_handler(conn
, NULL
);
4242 START_TEST(test_mg_store_body
)
4245 char client_err_buf
[256];
4246 char client_data_buf
[1024];
4247 struct mg_connection
*client
;
4248 const struct mg_request_info
*client_ri
;
4250 char check_data
[256];
4252 char errmsg
[256] = {0};
4254 /* Server context handle */
4255 struct mg_context
*ctx
;
4256 struct mg_callbacks callbacks
;
4257 const char *options
[] = {
4258 #if !defined(NO_FILES)
4262 #if !defined(NO_CACHING)
4263 "static_file_max_age",
4275 memset(&callbacks
, 0, sizeof(callbacks
));
4276 callbacks
.begin_request
= test_mg_store_body_begin_request_callback
;
4277 callbacks
.log_message
= log_msg_func
;
4279 /* Initialize the library */
4282 /* Start the server */
4283 ctx
= mg_start(&callbacks
, (void *)errmsg
, options
);
4284 ck_assert_str_eq(errmsg
, "");
4285 ck_assert(ctx
!= NULL
);
4287 /* Run the server for 15 seconds */
4290 /* Call a test client */
4291 client
= mg_connect_client(
4292 "127.0.0.1", 8082, 0, client_err_buf
, sizeof(client_err_buf
));
4294 ck_assert_str_eq(client_err_buf
, "");
4295 ck_assert(client
!= NULL
);
4298 "PUT /%s HTTP/1.0\r\nContent-Length: %i\r\n\r\n",
4299 "test_file_name.txt",
4300 test_mg_store_body_con_len
);
4303 while (r
< test_mg_store_body_con_len
) {
4304 int l
= mg_write(client
, "1234567890", 10);
4305 ck_assert_int_eq(l
, 10);
4309 r
= mg_get_response(client
, client_err_buf
, sizeof(client_err_buf
), 10000);
4310 ck_assert_int_ge(r
, 0);
4311 ck_assert_str_eq(client_err_buf
, "");
4313 client_ri
= mg_get_request_info(client
);
4314 ck_assert(client_ri
!= NULL
);
4316 /* Response must be 200 OK */
4317 ck_assert_ptr_ne(client_ri
->request_uri
, NULL
);
4318 ck_assert_str_eq(client_ri
->request_uri
, "200");
4320 /* Read PUT response */
4321 r
= mg_read(client
, client_data_buf
, sizeof(client_data_buf
) - 1);
4322 ck_assert_int_gt(r
, 0);
4323 client_data_buf
[r
] = 0;
4325 sprintf(check_data
, "(%i bytes saved)", test_mg_store_body_con_len
);
4326 check_ptr
= strstr(client_data_buf
, check_data
);
4327 ck_assert_ptr_ne(check_ptr
, NULL
);
4329 mg_close_connection(client
);
4331 /* Run the server for 5 seconds */
4334 /* Stop the server */
4337 /* Un-initialize the library */
4345 #if defined(MG_USE_OPEN_FILE) && !defined(NO_FILES)
4347 #define FILE_IN_MEM_SIZE (1024 * 100)
4348 static char *file_in_mem_data
;
4351 test_file_in_memory_open_file(const struct mg_connection
*conn
,
4352 const char *file_path
,
4357 if (strcmp(file_path
, "./file_in_mem") == 0) {
4358 /* File is in memory */
4359 *file_size
= FILE_IN_MEM_SIZE
;
4360 return file_in_mem_data
;
4362 /* File is not in memory */
4368 START_TEST(test_file_in_memory
)
4371 struct mg_context
*ctx
;
4372 struct mg_callbacks callbacks
;
4373 const char *OPTIONS
[32];
4375 #if !defined(NO_SSL)
4376 const char *ssl_cert
= locate_ssl_cert();
4380 struct mg_connection
*client
;
4381 char client_err_buf
[256];
4382 char client_data_buf
[256];
4383 const struct mg_request_info
*client_ri
;
4387 /* Prepare test data */
4388 file_in_mem_data
= (char *)malloc(FILE_IN_MEM_SIZE
);
4389 ck_assert_ptr_ne(file_in_mem_data
, NULL
);
4390 for (r
= 0; r
< FILE_IN_MEM_SIZE
; r
++) {
4391 file_in_mem_data
[r
] = (char)(r
);
4394 /* Set options and start server */
4395 OPTIONS
[opt_cnt
++] = "document_root";
4396 OPTIONS
[opt_cnt
++] = ".";
4398 OPTIONS
[opt_cnt
++] = "listening_ports";
4399 OPTIONS
[opt_cnt
++] = "8080";
4401 OPTIONS
[opt_cnt
++] = "listening_ports";
4402 OPTIONS
[opt_cnt
++] = "8443s";
4403 OPTIONS
[opt_cnt
++] = "ssl_certificate";
4404 OPTIONS
[opt_cnt
++] = ssl_cert
;
4405 ck_assert(ssl_cert
!= NULL
);
4407 OPTIONS
[opt_cnt
] = NULL
;
4410 memset(&callbacks
, 0, sizeof(callbacks
));
4411 callbacks
.open_file
= test_file_in_memory_open_file
;
4413 ctx
= test_mg_start(&callbacks
, 0, OPTIONS
);
4414 ck_assert(ctx
!= NULL
);
4416 /* connect client */
4417 memset(client_err_buf
, 0, sizeof(client_err_buf
));
4418 memset(client_data_buf
, 0, sizeof(client_data_buf
));
4421 mg_download("127.0.0.1",
4430 sizeof(client_err_buf
),
4431 "GET /file_in_mem HTTP/1.1\r\nHost: 127.0.0.1\r\n\r\n");
4433 ck_assert(client
!= NULL
);
4434 ck_assert_str_eq(client_err_buf
, "");
4436 client_ri
= mg_get_request_info(client
);
4438 ck_assert(client_ri
!= NULL
);
4439 ck_assert_str_eq(client_ri
->local_uri
, "200");
4441 ck_assert_int_eq(client_ri
->content_length
, FILE_IN_MEM_SIZE
);
4444 while (data_read
< client_ri
->content_length
) {
4445 r
= mg_read(client
, client_data_buf
, sizeof(client_data_buf
));
4447 for (i
= 0; i
< r
; i
++) {
4448 ck_assert_int_eq((int)client_data_buf
[i
],
4449 (int)file_in_mem_data
[data_read
+ i
]);
4455 /* Nothing left to read */
4456 r
= mg_read(client
, client_data_buf
, sizeof(client_data_buf
));
4457 ck_assert_int_eq(r
, 0);
4459 /* Close the client connection */
4460 mg_close_connection(client
);
4462 /* Stop the server */
4465 /* Free test data */
4466 free(file_in_mem_data
);
4467 file_in_mem_data
= NULL
;
4471 #else /* defined(MG_USE_OPEN_FILE) */
4473 START_TEST(test_file_in_memory
)
4483 minimal_http_https_client_impl(const char *server
,
4489 struct mg_connection
*client
;
4490 char client_err_buf
[256];
4491 char client_data_buf
[256];
4492 const struct mg_request_info
*client_ri
;
4498 client
= mg_connect_client(
4499 server
, port
, use_ssl
, client_err_buf
, sizeof(client_err_buf
));
4501 ck_assert_str_eq(client_err_buf
, "");
4502 ck_assert(client
!= NULL
);
4504 mg_printf(client
, "GET /%s HTTP/1.0\r\n\r\n", uri
);
4506 r
= mg_get_response(client
, client_err_buf
, sizeof(client_err_buf
), 10000);
4507 ck_assert_int_ge(r
, 0);
4508 ck_assert_str_eq(client_err_buf
, "");
4510 client_ri
= mg_get_request_info(client
);
4511 ck_assert(client_ri
!= NULL
);
4513 /* e.g.: ck_assert_str_eq(client_ri->request_uri, "200"); */
4514 ck_assert_ptr_ne(client_ri
->request_uri
, NULL
);
4515 r
= (int)strlen(client_ri
->request_uri
);
4516 ck_assert_int_eq(r
, 3);
4519 while (data_read
< client_ri
->content_length
) {
4520 r
= mg_read(client
, client_data_buf
, sizeof(client_data_buf
));
4526 /* Nothing left to read */
4527 r
= mg_read(client
, client_data_buf
, sizeof(client_data_buf
));
4528 ck_assert_int_eq(r
, 0);
4532 mg_close_connection(client
);
4539 minimal_http_client_impl(const char *server
, uint16_t port
, const char *uri
)
4541 minimal_http_https_client_impl(server
, port
, 0, uri
);
4545 #if !defined(NO_SSL)
4547 minimal_https_client_impl(const char *server
, uint16_t port
, const char *uri
)
4549 minimal_http_https_client_impl(server
, port
, 1, uri
);
4554 START_TEST(test_minimal_client
)
4558 /* Initialize the library */
4563 /* Call a test client */
4564 minimal_http_client_impl("192.30.253.113" /* www.github.com */,
4566 "civetweb/civetweb/");
4570 /* Un-initialize the library */
4579 minimal_test_request_handler(struct mg_connection
*conn
, void *cbdata
)
4581 const char *msg
= (const char *)cbdata
;
4582 unsigned long len
= (unsigned long)strlen(msg
);
4587 "HTTP/1.1 200 OK\r\n"
4588 "Content-Length: %lu\r\n"
4589 "Content-Type: text/plain\r\n"
4590 "Connection: close\r\n\r\n",
4593 mg_write(conn
, msg
, len
);
4601 START_TEST(test_minimal_http_server_callback
)
4603 /* This test should ensure the minimum server example in
4604 * docs/Embedding.md is still running. */
4606 /* Server context handle */
4607 struct mg_context
*ctx
;
4611 /* Initialize the library */
4614 /* Start the server */
4615 ctx
= test_mg_start(NULL
, 0, NULL
);
4616 ck_assert(ctx
!= NULL
);
4618 /* Add some handler */
4619 mg_set_request_handler(ctx
,
4621 minimal_test_request_handler
,
4622 (void *)"Hello world");
4623 mg_set_request_handler(ctx
,
4625 minimal_test_request_handler
,
4626 (void *)"Number eight");
4628 /* Run the server for 15 seconds */
4631 /* Call a test client */
4632 minimal_http_client_impl("127.0.0.1", 8080, "/hello");
4634 /* Run the server for 15 seconds */
4638 /* Stop the server */
4641 /* Un-initialize the library */
4649 START_TEST(test_minimal_https_server_callback
)
4651 #if !defined(NO_SSL)
4652 /* This test should show a HTTPS server with enhanced
4653 * security settings.
4656 * https://hynek.me/articles/hardening-your-web-servers-ssl-ciphers/
4659 * https://securityheaders.io/
4660 * https://www.htbridge.com/ssl/
4661 * https://www.htbridge.com/websec/
4662 * https://www.ssllabs.com/ssltest/
4663 * https://www.qualys.com/forms/freescan/
4666 /* Server context handle */
4667 struct mg_context
*ctx
;
4669 /* Server start parameters for HTTPS */
4670 const char *OPTIONS
[32];
4673 /* HTTPS port - required */
4674 OPTIONS
[opt_idx
++] = "listening_ports";
4675 OPTIONS
[opt_idx
++] = "8443s";
4677 /* path to certificate file - required */
4678 OPTIONS
[opt_idx
++] = "ssl_certificate";
4679 OPTIONS
[opt_idx
++] = locate_ssl_cert();
4681 #if defined(LOCAL_TEST) || defined(_WIN32)
4682 /* Do not set this on Travis CI, since the build containers
4683 * contain older SSL libraries */
4685 /* set minimum SSL version to TLS 1.2 - recommended */
4686 OPTIONS
[opt_idx
++] = "ssl_protocol_version";
4687 OPTIONS
[opt_idx
++] = "4";
4689 /* set some modern ciphers - recommended */
4690 OPTIONS
[opt_idx
++] = "ssl_cipher_list";
4691 OPTIONS
[opt_idx
++] = "ECDH+AESGCM+AES256:!aNULL:!MD5:!DSS";
4694 /* set "HTTPS only" header - recommended */
4695 OPTIONS
[opt_idx
++] = "strict_transport_security_max_age";
4696 OPTIONS
[opt_idx
++] = "31622400";
4698 /* end of options - required */
4699 OPTIONS
[opt_idx
] = NULL
;
4703 /* Initialize the library */
4707 /* Start the server */
4708 ctx
= test_mg_start(NULL
, 0, OPTIONS
);
4709 ck_assert(ctx
!= NULL
);
4711 /* Add some handler */
4712 mg_set_request_handler(ctx
,
4714 minimal_test_request_handler
,
4715 (void *)"Hello world");
4716 mg_set_request_handler(ctx
,
4718 minimal_test_request_handler
,
4719 (void *)"Number eight");
4721 /* Run the server for 15 seconds */
4724 /* Call a test client */
4725 minimal_https_client_impl("127.0.0.1", 8443, "/hello");
4727 /* Run the server for 15 seconds */
4731 /* Stop the server */
4734 /* Un-initialize the library */
4742 #if !defined(REPLACE_CHECK_FOR_LOCAL_DEBUGGING)
4744 make_public_server_suite(void)
4746 Suite
*const suite
= suite_create("PublicServer");
4748 TCase
*const tcase_checktestenv
= tcase_create("Check test environment");
4749 TCase
*const tcase_initlib
= tcase_create("Init library");
4750 TCase
*const tcase_startthreads
= tcase_create("Start threads");
4751 TCase
*const tcase_minimal_svr
= tcase_create("Minimal Server");
4752 TCase
*const tcase_minimal_cli
= tcase_create("Minimal Client");
4753 TCase
*const tcase_startstophttp
= tcase_create("Start Stop HTTP Server");
4754 TCase
*const tcase_startstophttp_ipv6
=
4755 tcase_create("Start Stop HTTP Server IPv6");
4756 TCase
*const tcase_startstophttps
= tcase_create("Start Stop HTTPS Server");
4757 TCase
*const tcase_serverandclienttls
= tcase_create("TLS Server Client");
4758 TCase
*const tcase_serverrequests
= tcase_create("Server Requests");
4759 TCase
*const tcase_storebody
= tcase_create("Store Body");
4760 TCase
*const tcase_handle_form
= tcase_create("Handle Form");
4761 TCase
*const tcase_http_auth
= tcase_create("HTTP Authentication");
4762 TCase
*const tcase_keep_alive
= tcase_create("HTTP Keep Alive");
4763 TCase
*const tcase_error_handling
= tcase_create("Error handling");
4764 TCase
*const tcase_throttle
= tcase_create("Limit speed");
4765 TCase
*const tcase_large_file
= tcase_create("Large file");
4766 TCase
*const tcase_file_in_mem
= tcase_create("File in memory");
4769 tcase_add_test(tcase_checktestenv
, test_the_test_environment
);
4770 tcase_set_timeout(tcase_checktestenv
, civetweb_min_test_timeout
);
4771 suite_add_tcase(suite
, tcase_checktestenv
);
4773 tcase_add_test(tcase_initlib
, test_init_library
);
4774 tcase_set_timeout(tcase_initlib
, civetweb_min_test_timeout
);
4775 suite_add_tcase(suite
, tcase_initlib
);
4777 tcase_add_test(tcase_startthreads
, test_threading
);
4778 tcase_set_timeout(tcase_startthreads
, civetweb_min_test_timeout
);
4779 suite_add_tcase(suite
, tcase_startthreads
);
4781 tcase_add_test(tcase_minimal_svr
, test_minimal_http_server_callback
);
4782 tcase_add_test(tcase_minimal_svr
, test_minimal_https_server_callback
);
4783 tcase_set_timeout(tcase_minimal_svr
, civetweb_min_server_test_timeout
);
4784 suite_add_tcase(suite
, tcase_minimal_svr
);
4786 tcase_add_test(tcase_minimal_cli
, test_minimal_client
);
4787 tcase_set_timeout(tcase_minimal_cli
, civetweb_min_server_test_timeout
);
4788 suite_add_tcase(suite
, tcase_minimal_cli
);
4790 tcase_add_test(tcase_startstophttp
, test_mg_start_stop_http_server
);
4791 tcase_set_timeout(tcase_startstophttp
, civetweb_min_server_test_timeout
);
4792 suite_add_tcase(suite
, tcase_startstophttp
);
4794 tcase_add_test(tcase_startstophttp_ipv6
,
4795 test_mg_start_stop_http_server_ipv6
);
4796 tcase_set_timeout(tcase_startstophttp_ipv6
,
4797 civetweb_min_server_test_timeout
);
4798 suite_add_tcase(suite
, tcase_startstophttp_ipv6
);
4800 tcase_add_test(tcase_startstophttps
, test_mg_start_stop_https_server
);
4801 tcase_set_timeout(tcase_startstophttps
, civetweb_min_server_test_timeout
);
4802 suite_add_tcase(suite
, tcase_startstophttps
);
4804 tcase_add_test(tcase_serverandclienttls
, test_mg_server_and_client_tls
);
4805 tcase_set_timeout(tcase_serverandclienttls
,
4806 civetweb_min_server_test_timeout
);
4807 suite_add_tcase(suite
, tcase_serverandclienttls
);
4809 tcase_add_test(tcase_serverrequests
, test_request_handlers
);
4810 tcase_set_timeout(tcase_serverrequests
, civetweb_mid_server_test_timeout
);
4811 suite_add_tcase(suite
, tcase_serverrequests
);
4813 tcase_add_test(tcase_storebody
, test_mg_store_body
);
4814 tcase_set_timeout(tcase_storebody
, civetweb_mid_server_test_timeout
);
4815 suite_add_tcase(suite
, tcase_storebody
);
4817 tcase_add_test(tcase_handle_form
, test_handle_form
);
4818 tcase_set_timeout(tcase_handle_form
, civetweb_mid_server_test_timeout
);
4819 suite_add_tcase(suite
, tcase_handle_form
);
4821 tcase_add_test(tcase_http_auth
, test_http_auth
);
4822 tcase_set_timeout(tcase_http_auth
, civetweb_min_server_test_timeout
);
4823 suite_add_tcase(suite
, tcase_http_auth
);
4825 tcase_add_test(tcase_keep_alive
, test_keep_alive
);
4826 tcase_set_timeout(tcase_keep_alive
, civetweb_mid_server_test_timeout
);
4827 suite_add_tcase(suite
, tcase_keep_alive
);
4829 tcase_add_test(tcase_error_handling
, test_error_handling
);
4830 tcase_add_test(tcase_error_handling
, test_error_log_file
);
4831 tcase_set_timeout(tcase_error_handling
, civetweb_mid_server_test_timeout
);
4832 suite_add_tcase(suite
, tcase_error_handling
);
4834 tcase_add_test(tcase_throttle
, test_throttle
);
4835 tcase_set_timeout(tcase_throttle
, civetweb_mid_server_test_timeout
);
4836 suite_add_tcase(suite
, tcase_throttle
);
4838 tcase_add_test(tcase_large_file
, test_large_file
);
4839 tcase_set_timeout(tcase_large_file
, civetweb_mid_server_test_timeout
);
4840 suite_add_tcase(suite
, tcase_large_file
);
4842 tcase_add_test(tcase_file_in_mem
, test_file_in_memory
);
4843 tcase_set_timeout(tcase_file_in_mem
, civetweb_mid_server_test_timeout
);
4844 suite_add_tcase(suite
, tcase_file_in_mem
);
4851 #ifdef REPLACE_CHECK_FOR_LOCAL_DEBUGGING
4852 /* Used to debug test cases without using the check framework */
4853 /* Build command for Linux:
4854 gcc test/public_server.c src/civetweb.c -I include/ -I test/ -l pthread -l dl -D
4855 LOCAL_TEST -D REPLACE_CHECK_FOR_LOCAL_DEBUGGING -D MAIN_PUBLIC_SERVER=main
4858 static int chk_ok
= 0;
4859 static int chk_failed
= 0;
4863 MAIN_PUBLIC_SERVER(void)
4865 unsigned f_avail
= mg_check_feature(0xFF);
4866 unsigned f_ret
= mg_init_library(f_avail
);
4867 ck_assert_uint_eq(f_ret
, f_avail
);
4869 test_the_test_environment(0);
4872 test_minimal_client(0);
4874 test_mg_start_stop_http_server(0);
4875 test_mg_start_stop_https_server(0);
4876 test_request_handlers(0);
4877 test_mg_store_body(0);
4878 test_mg_server_and_client_tls(0);
4879 test_handle_form(0);
4882 test_error_handling(0);
4883 test_error_log_file(0);
4886 test_file_in_memory(0);
4890 printf("\nok: %i\nfailed: %i\n\n", chk_ok
, chk_failed
);
4894 _ck_assert_failed(const char *file
, int line
, const char *expr
, ...)
4898 fprintf(stderr
, "Error: %s, line %i\n", file
, line
); /* breakpoint here ! */
4899 vfprintf(stderr
, expr
, va
);
4900 fprintf(stderr
, "\n\n");
4906 _ck_assert_msg(int cond
, const char *file
, int line
, const char *expr
, ...)
4916 fprintf(stderr
, "Error: %s, line %i\n", file
, line
); /* breakpoint here ! */
4917 vfprintf(stderr
, expr
, va
);
4918 fprintf(stderr
, "\n\n");
4924 _mark_point(const char *file
, int line
)
4930 tcase_fn_start(const char *fname
, const char *file
, int line
)
4933 void suite_add_tcase(Suite
*s
, TCase
*tc
){};
4934 void _tcase_add_test(TCase
*tc
,
4938 int allowed_exit_value
,
4942 tcase_create(const char *name
)
4947 suite_create(const char *name
)
4951 void tcase_set_timeout(TCase
*tc
, double timeout
){};