]> git.proxmox.com Git - ceph.git/blob - ceph/src/civetweb/test/public_server.c
import 12.2.13 release
[ceph.git] / ceph / src / civetweb / test / public_server.c
1 /* Copyright (c) 2015-2017 the Civetweb developers
2 *
3 * Permission is hereby granted, free of charge, to any person obtaining a copy
4 * of this software and associated documentation files (the "Software"), to deal
5 * in the Software without restriction, including without limitation the rights
6 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7 * copies of the Software, and to permit persons to whom the Software is
8 * furnished to do so, subject to the following conditions:
9 *
10 * The above copyright notice and this permission notice shall be included in
11 * all copies or substantial portions of the Software.
12 *
13 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19 * THE SOFTWARE.
20 */
21
22 #ifdef _MSC_VER
23 #ifndef _CRT_SECURE_NO_WARNINGS
24 #define _CRT_SECURE_NO_WARNINGS
25 #endif
26 #endif
27
28 #include <stdlib.h>
29 #include <stdio.h>
30 #include <stdarg.h>
31 #include <string.h>
32 #include <stdint.h>
33 #include <time.h>
34 #include <sys/types.h>
35 #include <sys/stat.h>
36
37 #include "public_server.h"
38 #include <civetweb.h>
39
40 #if defined(_WIN32)
41 #include <windows.h>
42 #define test_sleep(x) (Sleep((x)*1000))
43 #else
44 #include <unistd.h>
45 #define test_sleep(x) (sleep(x))
46 #endif
47
48 #define SLEEP_BEFORE_MG_START (1)
49 #define SLEEP_AFTER_MG_START (3)
50 #define SLEEP_BEFORE_MG_STOP (1)
51 #define SLEEP_AFTER_MG_STOP (5)
52
53 /* This unit test file uses the excellent Check unit testing library.
54 * The API documentation is available here:
55 * http://check.sourceforge.net/doc/check_html/index.html
56 */
57
58 #if defined(__MINGW32__) || defined(__GNUC__)
59 /* Return codes of the tested functions are evaluated. Checking all other
60 * functions, used only to prepare the test environment seems redundant.
61 * If they fail, the test fails anyway. */
62 #pragma GCC diagnostic ignored "-Wunused-result"
63 #endif
64
65 static const char *
66 locate_path(const char *a_path)
67 {
68 static char r_path[256];
69
70 #ifdef _WIN32
71 #ifdef LOCAL_TEST
72 sprintf(r_path, "%s\\", a_path);
73 #else
74 /* Appveyor */
75 sprintf(r_path, "..\\..\\..\\%s\\", a_path);
76 /* TODO: the different paths
77 * used in the different test
78 * system is an unsolved
79 * problem. */
80 #endif
81 #else
82 #ifdef LOCAL_TEST
83 sprintf(r_path, "%s/", a_path);
84 #else
85 /* Travis */
86 sprintf(r_path,
87 "../../%s/",
88 a_path); // TODO: fix path in CI test environment
89 #endif
90 #endif
91
92 return r_path;
93 }
94
95
96 #define locate_resources() locate_path("resources")
97 #define locate_test_exes() locate_path("output")
98
99
100 static const char *
101 locate_ssl_cert(void)
102 {
103 static char cert_path[256];
104 const char *res = locate_resources();
105 size_t l;
106
107 ck_assert(res != NULL);
108 l = strlen(res);
109 ck_assert_uint_gt(l, 0);
110 ck_assert_uint_lt(l, 100); /* assume there is enough space left in our
111 typical 255 character string buffers */
112
113 strcpy(cert_path, res);
114 strcat(cert_path, "ssl_cert.pem");
115 return cert_path;
116 }
117
118
119 static int
120 wait_not_null(void *volatile *data)
121 {
122 int i;
123 for (i = 0; i < 100; i++) {
124 mark_point();
125 test_sleep(1);
126
127 if (*data != NULL) {
128 mark_point();
129 return 1;
130 }
131 }
132
133 #if defined(__MINGW32__) || defined(__GNUC__)
134 #pragma GCC diagnostic push
135 #pragma GCC diagnostic ignored "-Wunreachable-code"
136 #pragma GCC diagnostic ignored "-Wunreachable-code-return"
137 #endif
138
139 ck_abort_msg("wait_not_null failed (%i sec)", i);
140
141 return 0;
142
143 #if defined(__MINGW32__) || defined(__GNUC__)
144 #pragma GCC diagnostic pop
145 #endif
146 }
147
148
149 START_TEST(test_the_test_environment)
150 {
151 char wd[300];
152 char buf[500];
153 FILE *f;
154 struct stat st;
155 int ret;
156 const char *ssl_cert = locate_ssl_cert();
157
158 memset(wd, 0, sizeof(wd));
159 memset(buf, 0, sizeof(buf));
160
161 /* Get the current working directory */
162 #ifdef _WIN32
163 (void)GetCurrentDirectoryA(sizeof(wd), wd);
164 wd[sizeof(wd) - 1] = 0;
165 #else
166 (void)getcwd(wd, sizeof(wd));
167 wd[sizeof(wd) - 1] = 0;
168 #endif
169
170 /* Check the pem file */
171 #ifdef _WIN32
172 strcpy(buf, wd);
173 strcat(buf, "\\");
174 strcat(buf, ssl_cert);
175 f = fopen(buf, "rb");
176 #else
177 strcpy(buf, wd);
178 strcat(buf, "/");
179 strcat(buf, ssl_cert);
180 f = fopen(buf, "r");
181 #endif
182
183 if (f) {
184 fclose(f);
185 } else {
186 fprintf(stderr, "%s not found", buf);
187 }
188
189 /* Check the test dir */
190 #ifdef _WIN32
191 strcpy(buf, wd);
192 strcat(buf, "\\test");
193 #else
194 strcpy(buf, wd);
195 strcat(buf, "/test");
196 #endif
197
198 memset(&st, 0, sizeof(st));
199 ret = stat(buf, &st);
200
201 if (ret) {
202 fprintf(stderr, "%s not found", buf);
203 }
204
205
206 #ifdef _WIN32
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");
215 #else
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");
219 #endif
220 #endif
221 }
222 END_TEST
223
224
225 static void *threading_data = 0;
226
227 static void *
228 test_thread_func_t(void *param)
229 {
230 ck_assert_ptr_eq(param, &threading_data);
231 ck_assert_ptr_eq(threading_data, NULL);
232 threading_data = &threading_data;
233 return NULL;
234 }
235
236
237 START_TEST(test_threading)
238 {
239 int ok;
240
241 threading_data = NULL;
242 mark_point();
243
244 ok = mg_start_thread(test_thread_func_t, &threading_data);
245 ck_assert_int_eq(ok, 0);
246
247 wait_not_null(&threading_data);
248 ck_assert_ptr_eq(threading_data, &threading_data);
249 }
250 END_TEST
251
252
253 static int
254 log_msg_func(const struct mg_connection *conn, const char *message)
255 {
256 struct mg_context *ctx;
257 char *ud;
258
259 ck_assert(conn != NULL);
260 ctx = mg_get_context(conn);
261 ck_assert(ctx != NULL);
262 ud = (char *)mg_get_user_data(ctx);
263
264 strncpy(ud, message, 255);
265 ud[255] = 0;
266 mark_point();
267
268 printf("LOG_MSG_FUNC: %s\n", message);
269 mark_point();
270
271 return 1; /* Return 1 means "already handled" */
272 }
273
274
275 static int
276 test_log_message(const struct mg_connection *conn, const char *message)
277 {
278 (void)conn;
279
280 printf("LOG_MESSAGE: %s\n", message);
281 mark_point();
282
283 return 0; /* Return 0 means "not yet handled" */
284 }
285
286
287 static struct mg_context *
288 test_mg_start(const struct mg_callbacks *callbacks,
289 void *user_data,
290 const char **configuration_options)
291 {
292 struct mg_context *ctx;
293 struct mg_callbacks cb;
294
295 if (callbacks) {
296 memcpy(&cb, callbacks, sizeof(cb));
297 } else {
298 memset(&cb, 0, sizeof(cb));
299 }
300
301 if (cb.log_message == NULL) {
302 cb.log_message = test_log_message;
303 }
304
305 mark_point();
306 test_sleep(SLEEP_BEFORE_MG_START);
307 mark_point();
308 ctx = mg_start(&cb, user_data, configuration_options);
309 mark_point();
310 if (ctx) {
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);
314 }
315 mark_point();
316
317 return ctx;
318 }
319
320
321 static void
322 test_mg_stop(struct mg_context *ctx)
323 {
324 #ifdef __MACH__
325 /* For unknown reasons, there are sporadic hands
326 * for OSX if mark_point is called here */
327 test_sleep(SLEEP_BEFORE_MG_STOP);
328 mg_stop(ctx);
329 test_sleep(SLEEP_AFTER_MG_STOP);
330 #else
331 mark_point();
332 test_sleep(SLEEP_BEFORE_MG_STOP);
333 mark_point();
334 mg_stop(ctx);
335 mark_point();
336 test_sleep(SLEEP_AFTER_MG_STOP);
337 mark_point();
338 #endif
339 }
340
341
342 static void
343 test_mg_start_stop_http_server_impl(int ipv6)
344 {
345 struct mg_context *ctx;
346 const char *OPTIONS[16];
347 int optcnt = 0;
348 const char *localhost_name = ((ipv6) ? "[::1]" : "127.0.0.1");
349
350 #if defined(MG_LEGACY_INTERFACE)
351 size_t ports_cnt;
352 int ports[16];
353 int ssl[16];
354 #endif
355 struct mg_callbacks callbacks;
356 char errmsg[256];
357
358 struct mg_connection *client_conn;
359 char client_err[256];
360 const struct mg_request_info *client_ri;
361 int client_res, ret;
362 struct mg_server_ports portinfo[8];
363
364 mark_point();
365
366 #if !defined(NO_FILES)
367 OPTIONS[optcnt++] = "document_root";
368 OPTIONS[optcnt++] = ".";
369 #endif
370 OPTIONS[optcnt++] = "listening_ports";
371 OPTIONS[optcnt++] = ((ipv6) ? "+8080" : "8080");
372 OPTIONS[optcnt] = 0;
373
374 #if defined(MG_LEGACY_INTERFACE)
375 memset(ports, 0, sizeof(ports));
376 memset(ssl, 0, sizeof(ssl));
377 #endif
378 memset(portinfo, 0, sizeof(portinfo));
379 memset(&callbacks, 0, sizeof(callbacks));
380 memset(errmsg, 0, sizeof(errmsg));
381
382 callbacks.log_message = log_msg_func;
383
384 ctx = test_mg_start(&callbacks, (void *)errmsg, OPTIONS);
385
386 ck_assert_str_eq(errmsg, "");
387 ck_assert(ctx != NULL);
388
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);
396 #endif
397
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);
408
409 ret = mg_get_server_ports(ctx, 4, portinfo);
410 ck_assert_int_eq(ret, 1);
411 if (ipv6) {
412 ck_assert_int_eq(portinfo[0].protocol, 3);
413 } else {
414 ck_assert_int_eq(portinfo[0].protocol, 1);
415 }
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);
423
424 test_sleep(1);
425
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));
430
431 ck_assert_str_eq(client_err, "");
432 ck_assert(client_conn != NULL);
433
434 mg_printf(client_conn, "GET / HTTP/1.0\r\n\r\n");
435 client_res =
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);
441
442 #if defined(NO_FILES)
443 ck_assert_str_eq(client_ri->local_uri, "404");
444 #else
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));
450 #endif
451 mg_close_connection(client_conn);
452
453 test_sleep(1);
454
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));
459
460 ck_assert_str_eq(client_err, "");
461 ck_assert(client_conn != NULL);
462
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");
466 client_res =
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);
472
473 #if defined(NO_FILES)
474 ck_assert_str_eq(client_ri->local_uri, "404");
475 #else
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));
481 #endif
482 mg_close_connection(client_conn);
483
484 test_sleep(1);
485
486
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));
491
492 ck_assert_str_eq(client_err, "");
493 ck_assert(client_conn != NULL);
494
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");
498 client_res =
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);
504
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);
508
509 test_sleep(1);
510
511
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));
518
519 ck_assert_str_eq(client_err, "");
520 ck_assert(client_conn != NULL);
521
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");
526 client_res =
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);
532
533 /* Response must be 400 Bad Request */
534 ck_assert_str_eq(client_ri->local_uri, "400");
535 mg_close_connection(client_conn);
536
537 test_sleep(1);
538
539 /* End test */
540 test_mg_stop(ctx);
541 mark_point();
542 }
543
544
545 START_TEST(test_mg_start_stop_http_server)
546 {
547 mark_point();
548 test_mg_start_stop_http_server_impl(0);
549 mark_point();
550 }
551 END_TEST
552
553
554 START_TEST(test_mg_start_stop_http_server_ipv6)
555 {
556 mark_point();
557 #if defined(USE_IPV6)
558 test_mg_start_stop_http_server_impl(1);
559 #endif
560 mark_point();
561 }
562 END_TEST
563
564
565 START_TEST(test_mg_start_stop_https_server)
566 {
567 #ifndef NO_SSL
568
569 struct mg_context *ctx;
570
571 #if defined(MG_LEGACY_INTERFACE)
572 size_t ports_cnt;
573 int ports[16];
574 int ssl[16];
575 #endif
576 struct mg_callbacks callbacks;
577 char errmsg[256];
578
579 const char *OPTIONS[8]; /* initializer list here is rejected by CI test */
580 int opt_idx = 0;
581 const char *ssl_cert = locate_ssl_cert();
582
583 struct mg_connection *client_conn;
584 char client_err[256];
585 const struct mg_request_info *client_ri;
586 int client_res, ret;
587 struct mg_server_ports portinfo[8];
588
589 ck_assert(ssl_cert != NULL);
590
591 memset((void *)OPTIONS, 0, sizeof(OPTIONS));
592 #if !defined(NO_FILES)
593 OPTIONS[opt_idx++] = "document_root";
594 OPTIONS[opt_idx++] = ".";
595 #endif
596 OPTIONS[opt_idx++] = "listening_ports";
597 OPTIONS[opt_idx++] = "8080r,8443s";
598 OPTIONS[opt_idx++] = "ssl_certificate";
599 OPTIONS[opt_idx++] = ssl_cert;
600
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);
604
605 #if defined(MG_LEGACY_INTERFACE)
606 memset(ports, 0, sizeof(ports));
607 memset(ssl, 0, sizeof(ssl));
608 #endif
609 memset(portinfo, 0, sizeof(portinfo));
610 memset(&callbacks, 0, sizeof(callbacks));
611 memset(errmsg, 0, sizeof(errmsg));
612
613 callbacks.log_message = log_msg_func;
614
615 ctx = test_mg_start(&callbacks, (void *)errmsg, OPTIONS);
616
617 ck_assert_str_eq(errmsg, "");
618 ck_assert(ctx != NULL);
619
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);
629 #endif
630
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);
641
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);
656
657 test_sleep(1);
658
659 memset(client_err, 0, sizeof(client_err));
660 client_conn =
661 mg_connect_client("127.0.0.1", 8443, 1, client_err, sizeof(client_err));
662
663 ck_assert_str_eq(client_err, "");
664 ck_assert(client_conn != NULL);
665
666 mg_printf(client_conn, "GET / HTTP/1.0\r\n\r\n");
667 client_res =
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);
673
674 #if defined(NO_FILES)
675 ck_assert_str_eq(client_ri->local_uri, "404");
676 #else
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));
682 #endif
683 mg_close_connection(client_conn);
684
685 test_sleep(1);
686
687 test_mg_stop(ctx);
688 mark_point();
689 #endif
690 }
691 END_TEST
692
693
694 START_TEST(test_mg_server_and_client_tls)
695 {
696 #ifndef NO_SSL
697
698 struct mg_context *ctx;
699
700 int ports_cnt;
701 struct mg_server_ports ports[16];
702 struct mg_callbacks callbacks;
703 char errmsg[256];
704
705 struct mg_connection *client_conn;
706 char client_err[256];
707 const struct mg_request_info *client_ri;
708 int client_res;
709 struct mg_client_options client_options;
710
711 const char *OPTIONS[32]; /* initializer list here is rejected by CI test */
712 int opt_idx = 0;
713 char server_cert[256];
714 char client_cert[256];
715 const char *res_dir = locate_resources();
716
717 ck_assert(res_dir != NULL);
718 strcpy(server_cert, res_dir);
719 strcpy(client_cert, res_dir);
720 #ifdef _WIN32
721 strcat(server_cert, "cert\\server.pem");
722 strcat(client_cert, "cert\\client.pem");
723 #else
724 strcat(server_cert, "cert/server.pem");
725 strcat(client_cert, "cert/client.pem");
726 #endif
727
728 memset((void *)OPTIONS, 0, sizeof(OPTIONS));
729 #if !defined(NO_FILES)
730 OPTIONS[opt_idx++] = "document_root";
731 OPTIONS[opt_idx++] = ".";
732 #endif
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;
741
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);
745
746 memset(ports, 0, sizeof(ports));
747 memset(&callbacks, 0, sizeof(callbacks));
748 memset(errmsg, 0, sizeof(errmsg));
749
750 callbacks.log_message = log_msg_func;
751
752 ctx = test_mg_start(&callbacks, (void *)errmsg, OPTIONS);
753
754 ck_assert_str_eq(errmsg, "");
755 ck_assert(ctx != NULL);
756
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);
771
772 test_sleep(1);
773
774 memset(client_err, 0, sizeof(client_err));
775 client_conn =
776 mg_connect_client("127.0.0.1", 8443, 1, client_err, sizeof(client_err));
777
778 ck_assert_str_ne(client_err, "");
779 ck_assert(client_conn == NULL);
780
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;
787
788 client_conn = mg_connect_client_secure(&client_options,
789 client_err,
790 sizeof(client_err));
791
792 ck_assert_str_eq(client_err, "");
793 ck_assert(client_conn != NULL);
794
795 mg_printf(client_conn, "GET / HTTP/1.0\r\n\r\n");
796 client_res =
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);
802
803 #if defined(NO_FILES)
804 ck_assert_str_eq(client_ri->local_uri, "404");
805 #else
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));
811 #endif
812 mg_close_connection(client_conn);
813
814 /* TODO: A client API using a client certificate is missing */
815
816 test_sleep(1);
817
818 test_mg_stop(ctx);
819 #endif
820 mark_point();
821 }
822 END_TEST
823
824
825 static struct mg_context *g_ctx;
826
827 static int
828 request_test_handler(struct mg_connection *conn, void *cbdata)
829 {
830 int i;
831 char chunk_data[32];
832 const struct mg_request_info *ri;
833 struct mg_context *ctx;
834 void *ud, *cud;
835 void *dummy = malloc(1);
836
837 ctx = mg_get_context(conn);
838 ud = mg_get_user_data(ctx);
839 ri = mg_get_request_info(conn);
840
841 ck_assert(ri != NULL);
842 ck_assert(ctx == g_ctx);
843 ck_assert(ud == &g_ctx);
844
845 ck_assert(dummy != NULL);
846
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);
850
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);
854
855 free(dummy);
856
857 ck_assert_ptr_eq((void *)cbdata, (void *)(ptrdiff_t)7);
858 strcpy(chunk_data, "123456789A123456789B123456789C");
859
860 mg_printf(conn,
861 "HTTP/1.1 200 OK\r\n"
862 "Transfer-Encoding: chunked\r\n"
863 "Content-Type: text/plain\r\n\r\n");
864
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");
869 }
870
871 mg_printf(conn, "0\r\n\r\n");
872 mark_point();
873
874 return 1;
875 }
876
877
878 #ifdef USE_WEBSOCKET
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) */;
888
889
890 #if defined(DEBUG)
891 static void
892 WS_TEST_TRACE(const char *f, ...)
893 {
894 va_list l;
895 va_start(l, f);
896 vprintf(f, l);
897 va_end(l);
898 }
899 #else
900 #define WS_TEST_TRACE(...)
901 #endif
902
903
904 static int
905 websock_server_connect(const struct mg_connection *conn, void *udata)
906 {
907 (void)conn;
908
909 ck_assert_ptr_eq((void *)udata, (void *)(ptrdiff_t)7531);
910 WS_TEST_TRACE("Server: Websocket connected\n");
911 mark_point();
912
913 return 0; /* return 0 to accept every connection */
914 }
915
916
917 static void
918 websock_server_ready(struct mg_connection *conn, void *udata)
919 {
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");
923
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);
931
932 WS_TEST_TRACE("Server: Websocket ready X\n");
933 mark_point();
934 }
935
936
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];
940
941
942 static int
943 websock_server_data(struct mg_connection *conn,
944 int bits,
945 char *data,
946 size_t data_len,
947 void *udata)
948 {
949 (void)bits;
950
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);
953
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,
979 long_ws_buf,
980 long_ws_buf_len_16);
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,
987 long_ws_buf,
988 long_ws_buf_len_64);
989 mg_unlock_connection(conn);
990 } else {
991
992 #if defined(__MINGW32__) || defined(__GNUC__)
993 #pragma GCC diagnostic push
994 #pragma GCC diagnostic ignored "-Wunreachable-code"
995 #endif
996 #ifdef __clang__
997 #pragma clang diagnostic push
998 #pragma clang diagnostic ignored "-Wunreachable-code"
999 #endif
1000
1001 ck_abort_msg("Got unexpected message from websocket client");
1002
1003
1004 return 0;
1005
1006 #ifdef __clang__
1007 #pragma clang diagnostic pop
1008 #endif
1009 #if defined(__MINGW32__) || defined(__GNUC__)
1010 #pragma GCC diagnostic pop
1011 #endif
1012 }
1013 mark_point();
1014
1015 return 1; /* return 1 to keep the connetion open */
1016 }
1017
1018
1019 static void
1020 websock_server_close(const struct mg_connection *conn, void *udata)
1021 {
1022 #ifndef __MACH__
1023 ck_assert_ptr_eq((void *)udata, (void *)(ptrdiff_t)7531);
1024 WS_TEST_TRACE("Server: Close connection\n");
1025
1026 /* Can not send a websocket goodbye message here -
1027 * the connection is already closed */
1028
1029 mark_point();
1030 #endif
1031
1032 (void)conn;
1033 (void)udata;
1034 }
1035
1036
1037 /****************************************************************************/
1038 /* WEBSOCKET CLIENT */
1039 /****************************************************************************/
1040 struct tclient_data {
1041 void *data;
1042 size_t len;
1043 int closed;
1044 int clientId;
1045 };
1046
1047
1048 static int
1049 websocket_client_data_handler(struct mg_connection *conn,
1050 int flags,
1051 char *data,
1052 size_t data_len,
1053 void *user_data)
1054 {
1055 struct mg_context *ctx = mg_get_context(conn);
1056 struct tclient_data *pclient_data =
1057 (struct tclient_data *)mg_get_user_data(ctx);
1058
1059 ck_assert_ptr_eq(user_data, (void *)pclient_data);
1060
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)));
1066
1067 if (flags == (int)(128 | WEBSOCKET_OPCODE_TEXT)) {
1068 WS_TEST_TRACE(
1069 "Client %i received %lu bytes text data from server: %.*s\n",
1070 pclient_data->clientId,
1071 (unsigned long)data_len,
1072 (int)data_len,
1073 data);
1074 } else {
1075 WS_TEST_TRACE("Client %i received %lu bytes binary data from\n",
1076 pclient_data->clientId,
1077 (unsigned long)data_len);
1078 }
1079
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;
1084
1085 mark_point();
1086
1087 return 1;
1088 }
1089
1090
1091 static void
1092 websocket_client_close_handler(const struct mg_connection *conn,
1093 void *user_data)
1094 {
1095 struct mg_context *ctx = mg_get_context(conn);
1096 struct tclient_data *pclient_data =
1097 (struct tclient_data *)mg_get_user_data(ctx);
1098
1099 #ifndef __MACH__
1100 ck_assert_ptr_eq(user_data, (void *)pclient_data);
1101
1102 ck_assert(pclient_data != NULL);
1103
1104 WS_TEST_TRACE("Client %i: Close handler\n", pclient_data->clientId);
1105 pclient_data->closed++;
1106
1107 mark_point();
1108 #else
1109
1110 (void)user_data;
1111 pclient_data->closed++;
1112
1113 #endif /* __MACH__ */
1114 }
1115
1116 #endif /* USE_WEBSOCKET */
1117
1118
1119 START_TEST(test_request_handlers)
1120 {
1121 char ebuf[100];
1122 struct mg_context *ctx;
1123 struct mg_connection *client_conn;
1124 const struct mg_request_info *ri;
1125 char uri[64];
1126 char buf[1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 10 + 8];
1127 const char *expected =
1128 "112123123412345123456123456712345678123456789123456789A";
1129 int i;
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;
1151 #endif
1152
1153 const char *OPTIONS[16];
1154 const char *opt;
1155 FILE *f;
1156 const char *plain_file_content;
1157 const char *encoded_file_content;
1158 const char *cgi_script_content;
1159 const char *expected_cgi_result;
1160 int opt_idx = 0;
1161 struct stat st;
1162
1163 #if !defined(NO_SSL)
1164 const char *ssl_cert = locate_ssl_cert();
1165 #endif
1166
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;
1176 #endif
1177
1178 char cmd_buf[256];
1179 char *cgi_env_opt;
1180
1181 mark_point();
1182
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++] = ".";
1191 #endif
1192 #ifndef NO_SSL
1193 ck_assert(ssl_cert != NULL);
1194 OPTIONS[opt_idx++] = "ssl_certificate";
1195 OPTIONS[opt_idx++] = ssl_cert;
1196 #endif
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;
1205
1206 OPTIONS[opt_idx++] = "num_threads";
1207 OPTIONS[opt_idx++] = "2";
1208
1209
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);
1213
1214 ctx = test_mg_start(NULL, &g_ctx, OPTIONS);
1215
1216 ck_assert(ctx != NULL);
1217 g_ctx = ctx;
1218
1219 opt = mg_get_option(ctx, "listening_ports");
1220 ck_assert_str_eq(opt, HTTP_PORT);
1221
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 */
1230 {
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);
1234 }
1235
1236 /* We don't need the original anymore, the server has a private copy */
1237 free(cgi_env_opt);
1238
1239 opt = mg_get_option(ctx, "throttle");
1240 ck_assert_str_eq(opt, "");
1241
1242 opt = mg_get_option(ctx, "unknown_option_name");
1243 ck_assert(opt == NULL);
1244
1245 for (i = 0; i < 1000; i++) {
1246 sprintf(uri, "/U%u", i);
1247 mg_set_request_handler(ctx, uri, request_test_handler, NULL);
1248 }
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);
1252 }
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);
1256 }
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);
1260 }
1261 for (i = 5; i < 9; i++) {
1262 sprintf(uri, "/U%u", i);
1263 mg_set_request_handler(ctx,
1264 uri,
1265 request_test_handler,
1266 (void *)(ptrdiff_t)i);
1267 }
1268
1269 #ifdef USE_WEBSOCKET
1270 mg_set_websocket_handler(ctx,
1271 "/websocket",
1272 websock_server_connect,
1273 websock_server_ready,
1274 websock_server_data,
1275 websock_server_close,
1276 (void *)(ptrdiff_t)7531);
1277 #endif
1278
1279 /* Try to load non existing file */
1280 client_conn = mg_download("localhost",
1281 ipv4_port,
1282 0,
1283 ebuf,
1284 sizeof(ebuf),
1285 "%s",
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);
1289
1290 ck_assert(ri != NULL);
1291 ck_assert_str_eq(ri->local_uri, "404");
1292 mg_close_connection(client_conn);
1293
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);
1299
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));
1304 buf[i] = 0;
1305 ck_assert_str_eq(buf, expected);
1306 mg_close_connection(client_conn);
1307
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);
1313
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))) {
1318 buf[i] = 0;
1319 } else {
1320 ck_abort_msg(
1321 "ERROR: test_request_handlers: read returned %i (>=0, <%i)",
1322 (int)i,
1323 (int)sizeof(buf));
1324 }
1325 ck_assert((int)i < (int)sizeof(buf));
1326 ck_assert(i > 0);
1327 ck_assert_int_eq(i, (int)strlen(expected));
1328 buf[i] = 0;
1329 ck_assert_str_eq(buf, expected);
1330 mg_close_connection(client_conn);
1331
1332 #if defined(USE_IPV6)
1333 /* Get data from callback using http://[::1] */
1334 client_conn =
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);
1338
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));
1343 buf[i] = 0;
1344 ck_assert_str_eq(buf, expected);
1345 mg_close_connection(client_conn);
1346 #endif
1347
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);
1354
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));
1359 buf[i] = 0;
1360 ck_assert_str_eq(buf, expected);
1361 mg_close_connection(client_conn);
1362
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);
1368
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);
1374 #endif
1375
1376 #if defined(USE_IPV6) && !defined(NO_SSL)
1377 /* Get data from callback using https://[::1] */
1378 client_conn =
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);
1382
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));
1387 buf[i] = 0;
1388 ck_assert_str_eq(buf, expected);
1389 mg_close_connection(client_conn);
1390
1391 /* Get redirect from callback using http://127.0.0.1 */
1392 client_conn =
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);
1396
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);
1402 #endif
1403
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 */
1408 #ifdef _WIN32
1409 f = fopen("test.txt", "wb");
1410 #else
1411 f = fopen("test.txt", "w");
1412 #endif
1413 plain_file_content = "simple text file\n";
1414 fwrite(plain_file_content, 17, 1, f);
1415 fclose(f);
1416
1417 #ifdef _WIN32
1418 f = fopen("test_gz.txt.gz", "wb");
1419 #else
1420 f = fopen("test_gz.txt.gz", "w");
1421 #endif
1422 encoded_file_content = "\x1f\x8b\x08\x08\xf8\x9d\xcb\x55\x00\x00"
1423 "test_gz.txt"
1424 "\x00\x01\x11\x00\xee\xff"
1425 "zipped text file"
1426 "\x0a\x34\x5f\xcc\x49\x11\x00\x00\x00";
1427 fwrite(encoded_file_content, 1, 52, f);
1428 fclose(f);
1429
1430 #ifdef _WIN32
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);
1434 fclose(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"
1439 "echo.\r\n"
1440 "echo CGI test\r\n"
1441 "\r\n";
1442 fwrite(cgi_script_content, strlen(cgi_script_content), 1, f);
1443 fclose(f);
1444 #else
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"
1451 "\n";
1452 (void)fwrite(cgi_script_content, strlen(cgi_script_content), 1, f);
1453 (void)fclose(f);
1454 (void)system("chmod a+x test.cgi");
1455 #endif
1456 expected_cgi_result = "CGI test";
1457
1458 /* Get static data */
1459 client_conn = mg_download("localhost",
1460 ipv4_port,
1461 0,
1462 ebuf,
1463 sizeof(ebuf),
1464 "%s",
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);
1468
1469 ck_assert(ri != NULL);
1470
1471 #if defined(NO_FILES)
1472 ck_assert_str_eq(ri->local_uri, "404");
1473 #else
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))) {
1478 buf[i] = 0;
1479 }
1480 ck_assert_str_eq(buf, plain_file_content);
1481 #endif
1482 mg_close_connection(client_conn);
1483
1484
1485 /* Check if CGI test executable exists */
1486 memset(&st, 0, sizeof(st));
1487
1488 #if defined(_WIN32)
1489 /* TODO: not yet available */
1490 sprintf(ebuf, "%scgi_test.cgi", locate_test_exes());
1491 #else
1492 sprintf(ebuf, "%scgi_test.cgi", locate_test_exes());
1493
1494 if (stat(ebuf, &st) != 0) {
1495 fprintf(stderr, "\nFile %s not found\n", ebuf);
1496 fprintf(stderr,
1497 "This file needs to be compiled manually before "
1498 "starting the test\n");
1499 fprintf(stderr,
1500 "e.g. by gcc test/cgi_test.c -o output/cgi_test.cgi\n\n");
1501
1502 /* Abort test with diagnostic message */
1503 ck_abort_msg("Mandatory file %s must be built before starting the test",
1504 ebuf);
1505 }
1506 #endif
1507
1508
1509 /* Test with CGI test executable */
1510 #if defined(_WIN32)
1511 sprintf(cmd_buf, "copy %s\\cgi_test.cgi cgi_test.exe", locate_test_exes());
1512 #else
1513 sprintf(cmd_buf, "cp %s/cgi_test.cgi cgi_test.cgi", locate_test_exes());
1514 #endif
1515 (void)system(cmd_buf);
1516
1517 #if !defined(NO_CGI) && !defined(NO_FILES) && !defined(_WIN32)
1518 /* TODO: add test for windows, check with POST */
1519 client_conn = mg_download(
1520 "localhost",
1521 ipv4_port,
1522 0,
1523 ebuf,
1524 sizeof(ebuf),
1525 "%s",
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);
1529
1530 ck_assert(ri != NULL);
1531 ck_assert_str_eq(ri->local_uri, "200");
1532 mg_close_connection(client_conn);
1533 #endif
1534
1535 /* Get zipped static data - will not work if Accept-Encoding is not set */
1536 client_conn = mg_download("localhost",
1537 ipv4_port,
1538 0,
1539 ebuf,
1540 sizeof(ebuf),
1541 "%s",
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);
1545
1546 ck_assert(ri != NULL);
1547 ck_assert_str_eq(ri->local_uri, "404");
1548 mg_close_connection(client_conn);
1549
1550 /* Get zipped static data - with Accept-Encoding */
1551 client_conn = mg_download(
1552 "localhost",
1553 ipv4_port,
1554 0,
1555 ebuf,
1556 sizeof(ebuf),
1557 "%s",
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);
1561
1562 ck_assert(ri != NULL);
1563
1564 #if defined(NO_FILES)
1565 ck_assert_str_eq(ri->local_uri, "404");
1566 #else
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))) {
1571 buf[i] = 0;
1572 }
1573 ck_assert_int_eq(ri->content_length, 52);
1574 ck_assert_str_eq(buf, encoded_file_content);
1575 #endif
1576 mg_close_connection(client_conn);
1577
1578 /* Get CGI generated data */
1579 #if !defined(NO_CGI)
1580 client_conn = mg_download("localhost",
1581 ipv4_port,
1582 0,
1583 ebuf,
1584 sizeof(ebuf),
1585 "%s",
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);
1589
1590 ck_assert(ri != NULL);
1591
1592 #if defined(NO_FILES)
1593 ck_assert_str_eq(ri->local_uri, "404");
1594
1595 (void)expected_cgi_result;
1596 (void)cgi_script_content;
1597 #else
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'))) {
1601 i--;
1602 }
1603 buf[i] = 0;
1604 }
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);
1609 #endif
1610
1611 #else
1612 (void)expected_cgi_result;
1613 (void)cgi_script_content;
1614 #endif
1615
1616 /* Get directory listing */
1617 client_conn = mg_download("localhost",
1618 ipv4_port,
1619 0,
1620 ebuf,
1621 sizeof(ebuf),
1622 "%s",
1623 "GET / HTTP/1.0\r\n\r\n");
1624 ck_assert(client_conn != NULL);
1625 ri = mg_get_request_info(client_conn);
1626
1627 ck_assert(ri != NULL);
1628 #if defined(NO_FILES)
1629 ck_assert_str_eq(ri->local_uri, "404");
1630 #else
1631 ck_assert_str_eq(ri->local_uri, "200");
1632 i = mg_read(client_conn, buf, sizeof(buf));
1633 ck_assert(i > 6);
1634 buf[6] = 0;
1635 ck_assert_str_eq(buf, "<html>");
1636 #endif
1637 mg_close_connection(client_conn);
1638
1639 /* POST to static file (will not work) */
1640 client_conn = mg_download("localhost",
1641 ipv4_port,
1642 0,
1643 ebuf,
1644 sizeof(ebuf),
1645 "%s",
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);
1649
1650 ck_assert(ri != NULL);
1651 #if defined(NO_FILES)
1652 ck_assert_str_eq(ri->local_uri, "404");
1653 #else
1654 ck_assert_str_eq(ri->local_uri, "405");
1655 i = mg_read(client_conn, buf, sizeof(buf));
1656 ck_assert(i >= 29);
1657 buf[29] = 0;
1658 ck_assert_str_eq(buf, "Error 405: Method Not Allowed");
1659 #endif
1660 mg_close_connection(client_conn);
1661
1662 /* PUT to static file (will not work) */
1663 client_conn = mg_download("localhost",
1664 ipv4_port,
1665 0,
1666 ebuf,
1667 sizeof(ebuf),
1668 "%s",
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);
1672
1673 ck_assert(ri != NULL);
1674 #if defined(NO_FILES)
1675 ck_assert_str_eq(ri->local_uri, "405"); /* method not allowed */
1676 #else
1677 ck_assert_str_eq(ri->local_uri, "401"); /* not authorized */
1678 #endif
1679 mg_close_connection(client_conn);
1680
1681
1682 /* Get data from callback using mg_connect_client instead of mg_download */
1683 memset(ebuf, 0, sizeof(ebuf));
1684 client_conn =
1685 mg_connect_client("127.0.0.1", ipv4_port, 0, ebuf, sizeof(ebuf));
1686
1687 ck_assert_str_eq(ebuf, "");
1688 ck_assert(client_conn != NULL);
1689
1690 mg_printf(client_conn, "%s", request);
1691
1692 i = mg_get_response(client_conn, ebuf, sizeof(ebuf), 10000);
1693 ck_assert_int_ge(i, 0);
1694 ck_assert_str_eq(ebuf, "");
1695
1696 ri = mg_get_request_info(client_conn);
1697
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));
1702 buf[i] = 0;
1703 ck_assert_str_eq(buf, expected);
1704 mg_close_connection(client_conn);
1705
1706 /* Get data from callback using mg_connect_client and absolute URI */
1707 memset(ebuf, 0, sizeof(ebuf));
1708 client_conn =
1709 mg_connect_client("localhost", ipv4_port, 0, ebuf, sizeof(ebuf));
1710
1711 ck_assert_str_eq(ebuf, "");
1712 ck_assert(client_conn != NULL);
1713
1714 mg_printf(client_conn,
1715 "GET http://test.domain:%d/U7 HTTP/1.0\r\n\r\n",
1716 ipv4_port);
1717
1718 i = mg_get_response(client_conn, ebuf, sizeof(ebuf), 10000);
1719 ck_assert_int_ge(i, 0);
1720 ck_assert_str_eq(ebuf, "");
1721
1722 ri = mg_get_request_info(client_conn);
1723
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));
1728 buf[i] = 0;
1729 ck_assert_str_eq(buf, expected);
1730 mg_close_connection(client_conn);
1731
1732 /* Get data from callback using mg_connect_client and absolute URI with a
1733 * sub-domain */
1734 memset(ebuf, 0, sizeof(ebuf));
1735 client_conn =
1736 mg_connect_client("localhost", ipv4_port, 0, ebuf, sizeof(ebuf));
1737
1738 ck_assert_str_eq(ebuf, "");
1739 ck_assert(client_conn != NULL);
1740
1741 mg_printf(client_conn,
1742 "GET http://subdomain.test.domain:%d/U7 HTTP/1.0\r\n\r\n",
1743 ipv4_port);
1744
1745 i = mg_get_response(client_conn, ebuf, sizeof(ebuf), 10000);
1746 ck_assert_int_ge(i, 0);
1747 ck_assert_str_eq(ebuf, "");
1748
1749 ri = mg_get_request_info(client_conn);
1750
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));
1755 buf[i] = 0;
1756 ck_assert_str_eq(buf, expected);
1757 mg_close_connection(client_conn);
1758
1759
1760 /* Websocket test */
1761 #ifdef USE_WEBSOCKET
1762 /* Then connect a first client */
1763 ws_client1_conn =
1764 mg_connect_websocket_client("localhost",
1765 ipv4_port,
1766 0,
1767 ebuf,
1768 sizeof(ebuf),
1769 "/websocket",
1770 NULL,
1771 websocket_client_data_handler,
1772 websocket_client_close_handler,
1773 &ws_client1_data);
1774
1775 ck_assert(ws_client1_conn != NULL);
1776
1777 wait_not_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;
1792
1793 mg_websocket_client_write(ws_client1_conn,
1794 WEBSOCKET_OPCODE_TEXT,
1795 "data1",
1796 5);
1797
1798 wait_not_null(
1799 &(ws_client1_data
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;
1811
1812 /* Now connect a second client */
1813 #ifdef USE_IPV6
1814 ws_client2_conn =
1815 mg_connect_websocket_client("[::1]",
1816 ipv6_port,
1817 0,
1818 ebuf,
1819 sizeof(ebuf),
1820 "/websocket",
1821 NULL,
1822 websocket_client_data_handler,
1823 websocket_client_close_handler,
1824 &ws_client2_data);
1825 #else
1826 ws_client2_conn =
1827 mg_connect_websocket_client("127.0.0.1",
1828 ipv4_port,
1829 0,
1830 ebuf,
1831 sizeof(ebuf),
1832 "/websocket",
1833 NULL,
1834 websocket_client_data_handler,
1835 websocket_client_close_handler,
1836 &ws_client2_data);
1837 #endif
1838 ck_assert(ws_client2_conn != NULL);
1839
1840 wait_not_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;
1854
1855 mg_websocket_client_write(ws_client1_conn,
1856 WEBSOCKET_OPCODE_TEXT,
1857 "data2",
1858 5);
1859
1860 wait_not_null(
1861 &(ws_client1_data
1862 .data)); /* Wait for the websocket acknowledge message */
1863
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;
1874
1875 mg_websocket_client_write(ws_client1_conn, WEBSOCKET_OPCODE_TEXT, "bye", 3);
1876
1877 wait_not_null(
1878 &(ws_client1_data.data)); /* Wait for the websocket goodbye message */
1879
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;
1892
1893 ck_assert(ws_client1_data.closed == 0); /* Not closed */
1894
1895 mg_close_connection(ws_client1_conn);
1896
1897 test_sleep(3); /* Won't get any message */
1898
1899 ck_assert(ws_client1_data.closed == 1); /* Closed */
1900
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);
1906
1907 mg_websocket_client_write(ws_client2_conn, WEBSOCKET_OPCODE_TEXT, "bye", 3);
1908
1909 wait_not_null(
1910 &(ws_client2_data.data)); /* Wait for the websocket goodbye message */
1911
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;
1924
1925 mg_close_connection(ws_client2_conn);
1926
1927 test_sleep(3); /* Won't get any message */
1928
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);
1935
1936 /* Connect client 3 */
1937 ws_client3_conn =
1938 mg_connect_websocket_client("localhost",
1939 #if defined(NO_SSL)
1940 ipv4_port,
1941 0,
1942 #else
1943 ipv4s_port,
1944 1,
1945 #endif
1946 ebuf,
1947 sizeof(ebuf),
1948 "/websocket",
1949 NULL,
1950 websocket_client_data_handler,
1951 websocket_client_close_handler,
1952 &ws_client3_data);
1953
1954 ck_assert(ws_client3_conn != NULL);
1955
1956 wait_not_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;
1973
1974 /* Write long data (16 bit size header) */
1975 mg_websocket_client_write(ws_client3_conn,
1976 WEBSOCKET_OPCODE_BINARY,
1977 long_ws_buf,
1978 long_ws_buf_len_16);
1979
1980 /* Wait for the response */
1981 wait_not_null(&(ws_client3_data.data));
1982
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;
1988
1989 /* Write long data (64 bit size header) */
1990 mg_websocket_client_write(ws_client3_conn,
1991 WEBSOCKET_OPCODE_BINARY,
1992 long_ws_buf,
1993 long_ws_buf_len_64);
1994
1995 /* Wait for the response */
1996 wait_not_null(&(ws_client3_data.data));
1997
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;
2003
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);
2008
2009 /* Connect client 4 */
2010 ws_client4_conn =
2011 mg_connect_websocket_client("localhost",
2012 #if defined(NO_SSL)
2013 ipv4_port,
2014 0,
2015 #else
2016 ipv4s_port,
2017 1,
2018 #endif
2019 ebuf,
2020 sizeof(ebuf),
2021 "/websocket",
2022 NULL,
2023 websocket_client_data_handler,
2024 websocket_client_close_handler,
2025 &ws_client4_data);
2026
2027 ck_assert(ws_client4_conn != NULL);
2028
2029 wait_not_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;
2043
2044 /* stop the server without closing this connection */
2045
2046 #endif
2047
2048 /* Close the server */
2049 g_ctx = NULL;
2050 test_mg_stop(ctx);
2051 mark_point();
2052
2053 #ifdef USE_WEBSOCKET
2054 for (i = 0; i < 100; i++) {
2055 test_sleep(1);
2056 if (ws_client3_data.closed != 0) {
2057 mark_point();
2058 break;
2059 }
2060 }
2061
2062 ck_assert_int_eq(ws_client4_data.closed, 1);
2063
2064 /* Free data in ws_client4_conn */
2065 mg_close_connection(ws_client4_conn);
2066
2067 #endif
2068 mark_point();
2069 }
2070 END_TEST
2071
2072
2073 static int g_field_found_return = -999;
2074
2075 static int
2076 field_found(const char *key,
2077 const char *filename,
2078 char *path,
2079 size_t pathlen,
2080 void *user_data)
2081 {
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);
2087
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));
2092
2093 ck_assert_str_ne(key, "dontread");
2094
2095 if (!strcmp(key, "break_field_handler")) {
2096 return FORM_FIELD_STORAGE_ABORT;
2097 }
2098 if (!strcmp(key, "continue_field_handler")) {
2099 return FORM_FIELD_STORAGE_SKIP;
2100 }
2101
2102 if (g_field_found_return == FORM_FIELD_STORAGE_STORE) {
2103 strncpy(path, key, pathlen - 8);
2104 strcat(path, ".txt");
2105 }
2106
2107 mark_point();
2108
2109 return g_field_found_return;
2110 }
2111
2112
2113 static int g_field_step;
2114
2115 static int
2116 field_get(const char *key,
2117 const char *value_untruncated,
2118 size_t valuelen,
2119 void *user_data)
2120 {
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;
2127
2128 ck_assert_ptr_eq(user_data, (void *)&g_field_found_return);
2129 ck_assert_int_ge(g_field_step, 0);
2130
2131 ++g_field_step;
2132 switch (g_field_step) {
2133 case 1:
2134 ck_assert_str_eq(key, "textin");
2135 ck_assert_uint_eq(valuelen, 4);
2136 ck_assert_str_eq(value, "text");
2137 break;
2138 case 2:
2139 ck_assert_str_eq(key, "passwordin");
2140 ck_assert_uint_eq(valuelen, 0);
2141 ck_assert_str_eq(value, "");
2142 break;
2143 case 3:
2144 ck_assert_str_eq(key, "radio1");
2145 ck_assert_uint_eq(valuelen, 4);
2146 ck_assert_str_eq(value, "val1");
2147 break;
2148 case 4:
2149 ck_assert_str_eq(key, "radio2");
2150 ck_assert_uint_eq(valuelen, 4);
2151 ck_assert_str_eq(value, "val1");
2152 break;
2153 case 5:
2154 ck_assert_str_eq(key, "check1");
2155 ck_assert_uint_eq(valuelen, 4);
2156 ck_assert_str_eq(value, "val1");
2157 break;
2158 case 6:
2159 ck_assert_str_eq(key, "numberin");
2160 ck_assert_uint_eq(valuelen, 1);
2161 ck_assert_str_eq(value, "1");
2162 break;
2163 case 7:
2164 ck_assert_str_eq(key, "datein");
2165 ck_assert_uint_eq(valuelen, 8);
2166 ck_assert_str_eq(value, "1.1.2016");
2167 break;
2168 case 8:
2169 ck_assert_str_eq(key, "colorin");
2170 ck_assert_uint_eq(valuelen, 7);
2171 ck_assert_str_eq(value, "#80ff00");
2172 break;
2173 case 9:
2174 ck_assert_str_eq(key, "rangein");
2175 ck_assert_uint_eq(valuelen, 1);
2176 ck_assert_str_eq(value, "3");
2177 break;
2178 case 10:
2179 ck_assert_str_eq(key, "monthin");
2180 ck_assert_uint_eq(valuelen, 0);
2181 ck_assert_str_eq(value, "");
2182 break;
2183 case 11:
2184 ck_assert_str_eq(key, "weekin");
2185 ck_assert_uint_eq(valuelen, 0);
2186 ck_assert_str_eq(value, "");
2187 break;
2188 case 12:
2189 ck_assert_str_eq(key, "timein");
2190 ck_assert_uint_eq(valuelen, 0);
2191 ck_assert_str_eq(value, "");
2192 break;
2193 case 13:
2194 ck_assert_str_eq(key, "datetimen");
2195 ck_assert_uint_eq(valuelen, 0);
2196 ck_assert_str_eq(value, "");
2197 break;
2198 case 14:
2199 ck_assert_str_eq(key, "datetimelocalin");
2200 ck_assert_uint_eq(valuelen, 0);
2201 ck_assert_str_eq(value, "");
2202 break;
2203 case 15:
2204 ck_assert_str_eq(key, "emailin");
2205 ck_assert_uint_eq(valuelen, 0);
2206 ck_assert_str_eq(value, "");
2207 break;
2208 case 16:
2209 ck_assert_str_eq(key, "searchin");
2210 ck_assert_uint_eq(valuelen, 0);
2211 ck_assert_str_eq(value, "");
2212 break;
2213 case 17:
2214 ck_assert_str_eq(key, "telin");
2215 ck_assert_uint_eq(valuelen, 0);
2216 ck_assert_str_eq(value, "");
2217 break;
2218 case 18:
2219 ck_assert_str_eq(key, "urlin");
2220 ck_assert_uint_eq(valuelen, 0);
2221 ck_assert_str_eq(value, "");
2222 break;
2223 case 19:
2224 ck_assert_str_eq(key, "filein");
2225 ck_assert_uint_eq(valuelen, 0);
2226 ck_assert_str_eq(value, "");
2227 break;
2228 case 20:
2229 ck_assert_str_eq(key, "filesin");
2230 ck_assert_uint_eq(valuelen, 0);
2231 ck_assert_str_eq(value, "");
2232 break;
2233 case 21:
2234 ck_assert_str_eq(key, "selectin");
2235 ck_assert_uint_eq(valuelen, 4);
2236 ck_assert_str_eq(value, "opt1");
2237 break;
2238 case 22:
2239 ck_assert_str_eq(key, "message");
2240 ck_assert_uint_eq(valuelen, 23);
2241 ck_assert_str_eq(value, "Text area default text.");
2242 break;
2243 default:
2244 ck_abort_msg("field_get called with g_field_step == %i",
2245 (int)g_field_step);
2246 }
2247
2248 free(value);
2249 mark_point();
2250
2251 return 0;
2252 }
2253
2254
2255 static const char *myfile_content = "Content of myfile.txt\r\n";
2256 static const int myfile_content_rep = 500;
2257
2258
2259 static int
2260 field_store(const char *path, long long file_size, void *user_data)
2261 {
2262 FILE *f;
2263 ck_assert_ptr_eq(user_data, (void *)&g_field_found_return);
2264 ck_assert_int_ge(g_field_step, 100);
2265
2266 ++g_field_step;
2267 switch (g_field_step) {
2268 case 101:
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);
2273 if (f) {
2274 char buf[32] = {0};
2275 int i = (int)fread(buf, 1, 31, f);
2276 ck_assert_int_eq(i, 9);
2277 fclose(f);
2278 ck_assert_str_eq(buf, "storetest");
2279 }
2280 break;
2281 case 102:
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);
2285 #ifdef _WIN32
2286 f = fopen(path, "rb");
2287 #else
2288 f = fopen(path, "r");
2289 #endif
2290 ck_assert_ptr_ne(f, NULL);
2291 if (f) {
2292 char buf[32] = {0};
2293 int r, i;
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);
2298 }
2299 i = (int)fread(buf, 1, 23, f);
2300 ck_assert_int_eq(i, 0);
2301 fclose(f);
2302 }
2303 break;
2304 default:
2305 ck_abort_msg("field_get called with g_field_step == %i",
2306 (int)g_field_step);
2307 }
2308 mark_point();
2309
2310 return 0;
2311 }
2312
2313
2314 static int
2315 FormGet(struct mg_connection *conn, void *cbdata)
2316 {
2317 const struct mg_request_info *req_info = mg_get_request_info(conn);
2318 int ret;
2319 struct mg_form_data_handler fdh = {field_found, field_get, NULL, NULL};
2320
2321 (void)cbdata;
2322
2323 ck_assert(req_info != NULL);
2324
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;
2327
2328 /* Call the form handler */
2329 g_field_step = 0;
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;
2337
2338 mark_point();
2339
2340 return 1;
2341 }
2342
2343
2344 static int
2345 FormStore(struct mg_connection *conn,
2346 void *cbdata,
2347 int ret_expected,
2348 int field_step_expected)
2349 {
2350 const struct mg_request_info *req_info = mg_get_request_info(conn);
2351 int ret;
2352 struct mg_form_data_handler fdh = {field_found,
2353 field_get,
2354 field_store,
2355 NULL};
2356
2357 (void)cbdata;
2358
2359 ck_assert(req_info != NULL);
2360
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;
2363
2364 /* Call the form handler */
2365 g_field_step = 100;
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;
2372
2373 mark_point();
2374
2375 return 1;
2376 }
2377
2378
2379 static int
2380 FormStore1(struct mg_connection *conn, void *cbdata)
2381 {
2382 mark_point();
2383 return FormStore(conn, cbdata, 3, 101);
2384 }
2385
2386
2387 static int
2388 FormStore2(struct mg_connection *conn, void *cbdata)
2389 {
2390 mark_point();
2391 return FormStore(conn, cbdata, 4, 102);
2392 }
2393
2394
2395 static void
2396 send_chunk_stringl(struct mg_connection *conn,
2397 const char *chunk,
2398 unsigned int chunk_len)
2399 {
2400 char lenbuf[16];
2401 size_t lenbuf_len;
2402 int ret;
2403
2404 mark_point();
2405
2406 /* First store the length information in a text buffer. */
2407 sprintf(lenbuf, "%x\r\n", chunk_len);
2408 lenbuf_len = strlen(lenbuf);
2409
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);
2413
2414 ret = mg_write(conn, chunk, chunk_len);
2415 ck_assert_int_eq(ret, (int)chunk_len);
2416
2417 ret = mg_write(conn, "\r\n", 2);
2418 ck_assert_int_eq(ret, 2);
2419 }
2420
2421
2422 static void
2423 send_chunk_string(struct mg_connection *conn, const char *chunk)
2424 {
2425 mark_point();
2426 send_chunk_stringl(conn, chunk, (unsigned int)strlen(chunk));
2427 mark_point();
2428 }
2429
2430
2431 START_TEST(test_handle_form)
2432 {
2433 struct mg_context *ctx;
2434 struct mg_connection *client_conn;
2435 const struct mg_request_info *ri;
2436 const char *OPTIONS[8];
2437 const char *opt;
2438 int opt_idx = 0;
2439 char ebuf[100];
2440 const char *multipart_body;
2441 const char *boundary;
2442 size_t body_len, body_sent, chunk_len;
2443 int sleep_cnt;
2444
2445 mark_point();
2446
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);
2453
2454 ctx = test_mg_start(NULL, &g_ctx, OPTIONS);
2455
2456 ck_assert(ctx != NULL);
2457 g_ctx = ctx;
2458
2459 opt = mg_get_option(ctx, "listening_ports");
2460 ck_assert_str_eq(opt, "8884");
2461
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);
2465
2466 test_sleep(1);
2467
2468 /* Handle form: "GET" */
2469 client_conn = mg_download("localhost",
2470 8884,
2471 0,
2472 ebuf,
2473 sizeof(ebuf),
2474 "%s",
2475 "GET /handle_form"
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. "
2484 "HTTP/1.0\r\n"
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++) {
2489 test_sleep(1);
2490 if (g_field_step == 1000) {
2491 break;
2492 }
2493 }
2494 ri = mg_get_request_info(client_conn);
2495
2496 ck_assert(ri != NULL);
2497 ck_assert_str_eq(ri->local_uri, "200");
2498 mg_close_connection(client_conn);
2499
2500 /* Handle form: "POST x-www-form-urlencoded" */
2501 client_conn =
2502 mg_download("localhost",
2503 8884,
2504 0,
2505 ebuf,
2506 sizeof(ebuf),
2507 "%s",
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"
2513 "\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++) {
2522 test_sleep(1);
2523 if (g_field_step == 1000) {
2524 break;
2525 }
2526 }
2527 ri = mg_get_request_info(client_conn);
2528
2529 ck_assert(ri != NULL);
2530 ck_assert_str_eq(ri->local_uri, "200");
2531 mg_close_connection(client_conn);
2532
2533 /* Handle form: "POST multipart/form-data" */
2534 multipart_body =
2535 "--multipart-form-data-boundary--see-RFC-2388\r\n"
2536 "Content-Disposition: form-data; name=\"textin\"\r\n"
2537 "\r\n"
2538 "text\r\n"
2539 "--multipart-form-data-boundary--see-RFC-2388\r\n"
2540 "Content-Disposition: form-data; name=\"passwordin\"\r\n"
2541 "\r\n"
2542 "\r\n"
2543 "--multipart-form-data-boundary--see-RFC-2388\r\n"
2544 "Content-Disposition: form-data; name=\"radio1\"\r\n"
2545 "\r\n"
2546 "val1\r\n"
2547 "--multipart-form-data-boundary--see-RFC-2388\r\n"
2548 "Content-Disposition: form-data; name=radio2\r\n"
2549 "\r\n"
2550 "val1\r\n"
2551 "--multipart-form-data-boundary--see-RFC-2388\r\n"
2552 "Content-Disposition: form-data; name=\"check1\"\r\n"
2553 "\r\n"
2554 "val1\r\n"
2555 "--multipart-form-data-boundary--see-RFC-2388\r\n"
2556 "Content-Disposition: form-data; name=\"numberin\"\r\n"
2557 "\r\n"
2558 "1\r\n"
2559 "--multipart-form-data-boundary--see-RFC-2388\r\n"
2560 "Content-Disposition: form-data; name=\"datein\"\r\n"
2561 "\r\n"
2562 "1.1.2016\r\n"
2563 "--multipart-form-data-boundary--see-RFC-2388\r\n"
2564 "Content-Disposition: form-data; name=\"colorin\"\r\n"
2565 "\r\n"
2566 "#80ff00\r\n"
2567 "--multipart-form-data-boundary--see-RFC-2388\r\n"
2568 "Content-Disposition: form-data; name=\"rangein\"\r\n"
2569 "\r\n"
2570 "3\r\n"
2571 "--multipart-form-data-boundary--see-RFC-2388\r\n"
2572 "Content-Disposition: form-data; name=\"monthin\"\r\n"
2573 "\r\n"
2574 "\r\n"
2575 "--multipart-form-data-boundary--see-RFC-2388\r\n"
2576 "Content-Disposition: form-data; name=\"weekin\"\r\n"
2577 "\r\n"
2578 "\r\n"
2579 "--multipart-form-data-boundary--see-RFC-2388\r\n"
2580 "Content-Disposition: form-data; name=\"timein\"\r\n"
2581 "\r\n"
2582 "\r\n"
2583 "--multipart-form-data-boundary--see-RFC-2388\r\n"
2584 "Content-Disposition: form-data; name=\"datetimen\"\r\n"
2585 "\r\n"
2586 "\r\n"
2587 "--multipart-form-data-boundary--see-RFC-2388\r\n"
2588 "Content-Disposition: form-data; name=\"datetimelocalin\"\r\n"
2589 "\r\n"
2590 "\r\n"
2591 "--multipart-form-data-boundary--see-RFC-2388\r\n"
2592 "Content-Disposition: form-data; name=\"emailin\"\r\n"
2593 "\r\n"
2594 "\r\n"
2595 "--multipart-form-data-boundary--see-RFC-2388\r\n"
2596 "Content-Disposition: form-data; name=\"searchin\"\r\n"
2597 "\r\n"
2598 "\r\n"
2599 "--multipart-form-data-boundary--see-RFC-2388\r\n"
2600 "Content-Disposition: form-data; name=\"telin\"\r\n"
2601 "\r\n"
2602 "\r\n"
2603 "--multipart-form-data-boundary--see-RFC-2388\r\n"
2604 "Content-Disposition: form-data; name=\"urlin\"\r\n"
2605 "\r\n"
2606 "\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"
2610 "\r\n"
2611 "\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"
2615 "\r\n"
2616 "\r\n"
2617 "--multipart-form-data-boundary--see-RFC-2388\r\n"
2618 "Content-Disposition: form-data; name=\"selectin\"\r\n"
2619 "\r\n"
2620 "opt1\r\n"
2621 "--multipart-form-data-boundary--see-RFC-2388\r\n"
2622 "Content-Disposition: form-data; name=\"message\"\r\n"
2623 "\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 */
2628
2629 client_conn =
2630 mg_download("localhost",
2631 8884,
2632 0,
2633 ebuf,
2634 sizeof(ebuf),
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"
2641 "\r\n%s",
2642 (unsigned int)body_len,
2643 multipart_body);
2644
2645 ck_assert(client_conn != NULL);
2646 for (sleep_cnt = 0; sleep_cnt < 30; sleep_cnt++) {
2647 test_sleep(1);
2648 if (g_field_step == 1000) {
2649 break;
2650 }
2651 }
2652 ri = mg_get_request_info(client_conn);
2653
2654 ck_assert(ri != NULL);
2655 ck_assert_str_eq(ri->local_uri, "200");
2656 mg_close_connection(client_conn);
2657
2658
2659 /* Handle form: "POST multipart/form-data" with chunked transfer encoding */
2660 client_conn =
2661 mg_download("localhost",
2662 8884,
2663 0,
2664 ebuf,
2665 sizeof(ebuf),
2666 "%s",
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"
2673 "\r\n");
2674
2675 ck_assert(client_conn != NULL);
2676
2677 body_len = strlen(multipart_body);
2678 chunk_len = 1;
2679 body_sent = 0;
2680 while (body_len > body_sent) {
2681 if (chunk_len > (body_len - body_sent)) {
2682 chunk_len = body_len - body_sent;
2683 }
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;
2690 }
2691 mg_printf(client_conn, "0\r\n");
2692
2693 for (sleep_cnt = 0; sleep_cnt < 30; sleep_cnt++) {
2694 test_sleep(1);
2695 if (g_field_step == 1000) {
2696 break;
2697 }
2698 }
2699 ri = mg_get_request_info(client_conn);
2700
2701 ck_assert(ri != NULL);
2702 ck_assert_str_eq(ri->local_uri, "200");
2703 mg_close_connection(client_conn);
2704
2705 /* Handle form: "POST multipart/form-data" with chunked transfer
2706 * encoding, using a quoted boundary string */
2707 client_conn = mg_download(
2708 "localhost",
2709 8884,
2710 0,
2711 ebuf,
2712 sizeof(ebuf),
2713 "%s",
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"
2720 "\r\n");
2721
2722 ck_assert(client_conn != NULL);
2723
2724 body_len = strlen(multipart_body);
2725 chunk_len = 1;
2726 body_sent = 0;
2727 while (body_len > body_sent) {
2728 if (chunk_len > (body_len - body_sent)) {
2729 chunk_len = body_len - body_sent;
2730 }
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;
2737 }
2738 mg_printf(client_conn, "0\r\n");
2739
2740 for (sleep_cnt = 0; sleep_cnt < 30; sleep_cnt++) {
2741 test_sleep(1);
2742 if (g_field_step == 1000) {
2743 break;
2744 }
2745 }
2746 ri = mg_get_request_info(client_conn);
2747
2748 ck_assert(ri != NULL);
2749 ck_assert_str_eq(ri->local_uri, "200");
2750 mg_close_connection(client_conn);
2751
2752
2753 /* Now test form_store */
2754
2755 /* First test with GET */
2756 client_conn = mg_download("localhost",
2757 8884,
2758 0,
2759 ebuf,
2760 sizeof(ebuf),
2761 "%s",
2762 "GET /handle_form_store"
2763 "?storeme=storetest"
2764 "&continue_field_handler=ignore"
2765 "&break_field_handler=abort"
2766 "&dontread=xyz "
2767 "HTTP/1.0\r\n"
2768 "Host: localhost:8884\r\n"
2769 "Connection: close\r\n\r\n");
2770
2771 ck_assert(client_conn != NULL);
2772
2773 for (sleep_cnt = 0; sleep_cnt < 30; sleep_cnt++) {
2774 test_sleep(1);
2775 if (g_field_step == 1000) {
2776 break;
2777 }
2778 }
2779 ri = mg_get_request_info(client_conn);
2780
2781 ck_assert(ri != NULL);
2782 ck_assert_str_eq(ri->local_uri, "200");
2783 mg_close_connection(client_conn);
2784
2785
2786 /* Handle form: "POST x-www-form-urlencoded", chunked, store */
2787 client_conn =
2788 mg_download("localhost",
2789 8884,
2790 0,
2791 ebuf,
2792 sizeof(ebuf),
2793 "%s",
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"
2799 "\r\n");
2800 ck_assert(client_conn != NULL);
2801
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");
2806 test_sleep(1);
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");
2810
2811 for (sleep_cnt = 0; sleep_cnt < 30; sleep_cnt++) {
2812 test_sleep(1);
2813 if (g_field_step == 1000) {
2814 break;
2815 }
2816 }
2817 ri = mg_get_request_info(client_conn);
2818
2819 ck_assert(ri != NULL);
2820 ck_assert_str_eq(ri->local_uri, "200");
2821 mg_close_connection(client_conn);
2822
2823 /* Handle form: "POST multipart/form-data", chunked, store */
2824 client_conn =
2825 mg_download("localhost",
2826 8884,
2827 0,
2828 ebuf,
2829 sizeof(ebuf),
2830 "%s",
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"
2837 "\r\n");
2838 ck_assert(client_conn != NULL);
2839
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");
2846
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");
2853
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");
2860
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");
2870
2871 for (sleep_cnt = 0; sleep_cnt < 30; sleep_cnt++) {
2872 test_sleep(1);
2873 if (g_field_step == 1000) {
2874 break;
2875 }
2876 }
2877 ri = mg_get_request_info(client_conn);
2878
2879 ck_assert(ri != NULL);
2880 ck_assert_str_eq(ri->local_uri, "200");
2881 mg_close_connection(client_conn);
2882
2883
2884 /* Handle form: "POST multipart/form-data", chunked, store, with files */
2885 client_conn =
2886 mg_download("localhost",
2887 8884,
2888 0,
2889 ebuf,
2890 sizeof(ebuf),
2891 "%s",
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"
2898 "\r\n");
2899 ck_assert(client_conn != NULL);
2900
2901 boundary = "--multipart-form-data-boundary--see-RFC-2388\r\n";
2902
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");
2908
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");
2917
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) */
2921 body_sent = 0;
2922 do {
2923 send_chunk_string(client_conn, "ignore\r\n");
2924 body_sent += 8;
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;
2930 }
2931 } while (body_sent < 8 * 1024);
2932 send_chunk_string(client_conn, "\r\n");
2933
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);
2944 }
2945 send_chunk_string(client_conn, "\r\n");
2946
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");
2952
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");
2961
2962 for (sleep_cnt = 0; sleep_cnt < 30; sleep_cnt++) {
2963 test_sleep(1);
2964 if (g_field_step == 1000) {
2965 break;
2966 }
2967 }
2968 ri = mg_get_request_info(client_conn);
2969
2970 ck_assert(ri != NULL);
2971 ck_assert_str_eq(ri->local_uri, "200");
2972 mg_close_connection(client_conn);
2973
2974
2975 /* Close the server */
2976 g_ctx = NULL;
2977 test_mg_stop(ctx);
2978 mark_point();
2979 }
2980 END_TEST
2981
2982
2983 START_TEST(test_http_auth)
2984 {
2985 #if !defined(NO_FILES)
2986 const char *OPTIONS[] = {
2987 "document_root",
2988 ".",
2989 "listening_ports",
2990 "8080",
2991 #if !defined(NO_CACHING)
2992 "static_file_max_age",
2993 "0",
2994 #endif
2995 "put_delete_auth_file",
2996 "put_delete_auth_file.csv",
2997 NULL,
2998 };
2999
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;
3004 int client_res;
3005 FILE *f;
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";
3009 const char *domain;
3010 const char *doc_root;
3011 const char *auth_request;
3012 const char *str;
3013 size_t len;
3014 int i;
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";
3020
3021 mark_point();
3022
3023 /* Start with default options */
3024 ctx = test_mg_start(NULL, NULL, OPTIONS);
3025
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, ".");
3034
3035 /* Create a default file in the document root */
3036 f = fopen(test_file, "w");
3037 if (f) {
3038 fprintf(f, "%s", test_content);
3039 fclose(f);
3040 } else {
3041 ck_abort_msg("Cannot create file %s", test_file);
3042 }
3043
3044 (void)remove(passwd_file);
3045 (void)remove("put_delete_auth_file.csv");
3046
3047 client_res = mg_modify_passwords_file("put_delete_auth_file.csv",
3048 domain,
3049 "admin",
3050 "adminpass");
3051 ck_assert_int_eq(client_res, 1);
3052
3053 /* Read file before a .htpasswd file has been created */
3054 memset(client_err, 0, sizeof(client_err));
3055 client_conn =
3056 mg_connect_client("127.0.0.1", 8080, 0, client_err, sizeof(client_err));
3057
3058 ck_assert_str_eq(client_err, "");
3059 ck_assert(client_conn != NULL);
3060
3061 mg_printf(client_conn, "GET /%s HTTP/1.0\r\n\r\n", test_file);
3062 client_res =
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);
3068
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);
3075
3076 test_sleep(1);
3077
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);
3081
3082 client_res = mg_modify_passwords_file(NULL, domain, "user", "pass");
3083 ck_assert_int_eq(client_res, 0); /* Filename is required */
3084
3085 test_sleep(1);
3086
3087 /* Repeat test after .htpasswd is created */
3088 memset(client_err, 0, sizeof(client_err));
3089 client_conn =
3090 mg_connect_client("127.0.0.1", 8080, 0, client_err, sizeof(client_err));
3091
3092 ck_assert_str_eq(client_err, "");
3093 ck_assert(client_conn != NULL);
3094
3095 mg_printf(client_conn, "GET /%s HTTP/1.0\r\n\r\n", test_file);
3096 client_res =
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);
3102
3103 ck_assert_str_eq(client_ri->local_uri, "401");
3104
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);
3112 }
3113 }
3114 ck_assert_ptr_ne(auth_request, NULL);
3115 str = "Digest qop=\"auth\", realm=\"";
3116 len = strlen(str);
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)));
3122 len += 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));
3131 memcpy(nonce,
3132 auth_request + len,
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));
3140
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);
3145
3146 ck_assert_ptr_eq(HA1_md5_ret, HA1_md5_buf);
3147 ck_assert_ptr_eq(HA2_md5_ret, HA2_md5_buf);
3148
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);
3152
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);
3156
3157 HA_md5_ret = mg_md5(HA_md5_buf,
3158 HA1_md5_buf,
3159 ":",
3160 nonce,
3161 ":",
3162 nc,
3163 ":",
3164 cnonce,
3165 ":",
3166 "auth",
3167 ":",
3168 HA2_md5_buf,
3169 NULL);
3170 ck_assert_ptr_eq(HA_md5_ret, HA_md5_buf);
3171
3172 mg_close_connection(client_conn);
3173
3174 /* Retry with authorization */
3175 memset(client_err, 0, sizeof(client_err));
3176 client_conn =
3177 mg_connect_client("127.0.0.1", 8080, 0, client_err, sizeof(client_err));
3178
3179 ck_assert_str_eq(client_err, "");
3180 ck_assert(client_conn != NULL);
3181
3182 mg_printf(client_conn, "GET /%s HTTP/1.0\r\n", test_file);
3183 mg_printf(client_conn,
3184 "Authorization: Digest "
3185 "username=\"%s\", "
3186 "realm=\"%s\", "
3187 "nonce=\"%s\", "
3188 "uri=\"/%s\", "
3189 "qop=auth, "
3190 "nc=%s, "
3191 "cnonce=\"%s\", "
3192 "response=\"%s\"\r\n\r\n",
3193 "user",
3194 domain,
3195 nonce,
3196 test_file,
3197 nc,
3198 cnonce,
3199 HA_md5_buf);
3200 client_res =
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);
3206
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);
3213
3214 test_sleep(1);
3215
3216 /* Retry DELETE with authorization of a user not authorized for DELETE */
3217 memset(client_err, 0, sizeof(client_err));
3218 client_conn =
3219 mg_connect_client("127.0.0.1", 8080, 0, client_err, sizeof(client_err));
3220
3221 ck_assert_str_eq(client_err, "");
3222 ck_assert(client_conn != NULL);
3223
3224 mg_printf(client_conn, "DELETE /%s HTTP/1.0\r\n", test_file);
3225 mg_printf(client_conn,
3226 "Authorization: Digest "
3227 "username=\"%s\", "
3228 "realm=\"%s\", "
3229 "nonce=\"%s\", "
3230 "uri=\"/%s\", "
3231 "qop=auth, "
3232 "nc=%s, "
3233 "cnonce=\"%s\", "
3234 "response=\"%s\"\r\n\r\n",
3235 "user",
3236 domain,
3237 nonce,
3238 test_file,
3239 nc,
3240 cnonce,
3241 HA_md5_buf);
3242 client_res =
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);
3248
3249 ck_assert_str_eq(client_ri->local_uri, "401");
3250 mg_close_connection(client_conn);
3251
3252 test_sleep(1);
3253
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);
3257
3258 test_sleep(1);
3259
3260
3261 /* Try to access the file again. Expected: 401 error */
3262 memset(client_err, 0, sizeof(client_err));
3263 client_conn =
3264 mg_connect_client("127.0.0.1", 8080, 0, client_err, sizeof(client_err));
3265
3266 ck_assert_str_eq(client_err, "");
3267 ck_assert(client_conn != NULL);
3268
3269 mg_printf(client_conn, "GET /%s HTTP/1.0\r\n\r\n", test_file);
3270 client_res =
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);
3276
3277 ck_assert_str_eq(client_ri->local_uri, "401");
3278 mg_close_connection(client_conn);
3279
3280 test_sleep(1);
3281
3282
3283 /* Now remove the password file */
3284 (void)remove(passwd_file);
3285 test_sleep(1);
3286
3287
3288 /* Access to the file must work like before */
3289 memset(client_err, 0, sizeof(client_err));
3290 client_conn =
3291 mg_connect_client("127.0.0.1", 8080, 0, client_err, sizeof(client_err));
3292
3293 ck_assert_str_eq(client_err, "");
3294 ck_assert(client_conn != NULL);
3295
3296 mg_printf(client_conn, "GET /%s HTTP/1.0\r\n\r\n", test_file);
3297 client_res =
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);
3303
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);
3310
3311 test_sleep(1);
3312
3313
3314 /* Stop the server and clean up */
3315 test_mg_stop(ctx);
3316 (void)remove(test_file);
3317 (void)remove(passwd_file);
3318 (void)remove("put_delete_auth_file.csv");
3319
3320 #endif
3321 mark_point();
3322 }
3323 END_TEST
3324
3325
3326 START_TEST(test_keep_alive)
3327 {
3328 struct mg_context *ctx;
3329 const char *OPTIONS[] =
3330 { "listening_ports",
3331 "8081",
3332 "request_timeout_ms",
3333 "10000",
3334 "enable_keep_alive",
3335 "yes",
3336 #if !defined(NO_FILES)
3337 "document_root",
3338 ".",
3339 "enable_directory_listing",
3340 "no",
3341 #endif
3342 NULL };
3343
3344 struct mg_connection *client_conn;
3345 char client_err[256];
3346 const struct mg_request_info *client_ri;
3347 int client_res, i;
3348 const char *connection_header;
3349
3350 mark_point();
3351
3352 ctx = test_mg_start(NULL, NULL, OPTIONS);
3353
3354 ck_assert(ctx != NULL);
3355
3356 /* HTTP 1.1 GET request */
3357 memset(client_err, 0, sizeof(client_err));
3358 client_conn =
3359 mg_connect_client("127.0.0.1", 8081, 0, client_err, sizeof(client_err));
3360
3361 ck_assert_str_eq(client_err, "");
3362 ck_assert(client_conn != NULL);
3363
3364 mg_printf(client_conn,
3365 "GET / HTTP/1.1\r\nHost: "
3366 "localhost:8081\r\nConnection: keep-alive\r\n\r\n");
3367 client_res =
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);
3373
3374 #if defined(NO_FILES)
3375 ck_assert_str_eq(client_ri->local_uri, "404");
3376 #else
3377 ck_assert_str_eq(client_ri->local_uri, "403");
3378 #endif
3379
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);
3386 }
3387 }
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);
3392
3393 test_sleep(1);
3394
3395 /* TODO: request a file and keep alive
3396 * (will only work if NO_FILES is not set). */
3397
3398 /* Stop the server and clean up */
3399 test_mg_stop(ctx);
3400
3401 mark_point();
3402 }
3403 END_TEST
3404
3405
3406 START_TEST(test_error_handling)
3407 {
3408 struct mg_context *ctx;
3409 FILE *f;
3410
3411 char bad_thread_num[32] = "badnumber";
3412
3413 struct mg_callbacks callbacks;
3414 char errmsg[256];
3415
3416 struct mg_connection *client_conn;
3417 char client_err[256];
3418 const struct mg_request_info *client_ri;
3419 int client_res, i;
3420
3421 const char *OPTIONS[32];
3422 int opt_cnt = 0;
3423
3424 mark_point();
3425
3426 #if !defined(NO_FILES)
3427 OPTIONS[opt_cnt++] = "document_root";
3428 OPTIONS[opt_cnt++] = ".";
3429 #endif
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;
3439
3440 memset(&callbacks, 0, sizeof(callbacks));
3441
3442 callbacks.log_message = log_msg_func;
3443
3444 /* test with unknown option */
3445 memset(errmsg, 0, sizeof(errmsg));
3446 ctx = test_mg_start(&callbacks, (void *)errmsg, OPTIONS);
3447
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");
3452
3453 /* Remove invalid option */
3454 for (i = 0; OPTIONS[i]; i++) {
3455 if (strstr(OPTIONS[i], "unknown_option")) {
3456 OPTIONS[i] = 0;
3457 }
3458 }
3459
3460 /* Test with bad num_thread option */
3461 memset(errmsg, 0, sizeof(errmsg));
3462 ctx = test_mg_start(&callbacks, (void *)errmsg, OPTIONS);
3463
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");
3468
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);
3472 #else
3473 sprintf(bad_thread_num, "%lu", 1000000000lu);
3474 #endif
3475
3476 /* Test with bad num_thread option */
3477 memset(errmsg, 0, sizeof(errmsg));
3478 ctx = test_mg_start(&callbacks, (void *)errmsg, OPTIONS);
3479
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");
3484
3485
3486 /* HTTP 1.0 GET request - server is not running */
3487 memset(client_err, 0, sizeof(client_err));
3488 client_conn =
3489 mg_connect_client("127.0.0.1", 8080, 0, client_err, sizeof(client_err));
3490 ck_assert(client_conn == NULL);
3491
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"));
3496
3497
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);
3502
3503 ck_assert_str_eq(errmsg, "");
3504 ck_assert(ctx != NULL);
3505
3506
3507 /* Server is running now */
3508 test_sleep(1);
3509
3510 /* Remove error files (in case they exist) */
3511 (void)remove("error.htm");
3512 (void)remove("error4xx.htm");
3513 (void)remove("error404.htm");
3514
3515
3516 /* Ask for something not existing - should get default 404 */
3517 memset(client_err, 0, sizeof(client_err));
3518 client_conn =
3519 mg_connect_client("127.0.0.1", 8080, 0, client_err, sizeof(client_err));
3520
3521 ck_assert_str_eq(client_err, "");
3522 ck_assert(client_conn != NULL);
3523
3524 mg_printf(client_conn, "GET /something/not/existing HTTP/1.0\r\n\r\n");
3525 client_res =
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);
3531
3532 ck_assert_str_eq(client_ri->local_uri, "404");
3533 mg_close_connection(client_conn);
3534 test_sleep(1);
3535
3536 /* Create an error.htm file */
3537 f = fopen("error.htm", "wt");
3538 ck_assert(f != NULL);
3539 (void)fprintf(f, "err-all");
3540 (void)fclose(f);
3541
3542
3543 /* Ask for something not existing - should get error.htm */
3544 memset(client_err, 0, sizeof(client_err));
3545 client_conn =
3546 mg_connect_client("127.0.0.1", 8080, 0, client_err, sizeof(client_err));
3547
3548 ck_assert_str_eq(client_err, "");
3549 ck_assert(client_conn != NULL);
3550
3551 mg_printf(client_conn, "GET /something/not/existing HTTP/1.0\r\n\r\n");
3552 client_res =
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);
3558
3559 ck_assert_str_eq(client_ri->local_uri, "200");
3560
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);
3564 client_err[8] = 0;
3565 ck_assert_str_eq(client_err, "err-all");
3566 test_sleep(1);
3567
3568 /* Create an error4xx.htm file */
3569 f = fopen("error4xx.htm", "wt");
3570 ck_assert(f != NULL);
3571 (void)fprintf(f, "err-4xx");
3572 (void)fclose(f);
3573
3574
3575 /* Ask for something not existing - should get error4xx.htm */
3576 memset(client_err, 0, sizeof(client_err));
3577 client_conn =
3578 mg_connect_client("127.0.0.1", 8080, 0, client_err, sizeof(client_err));
3579
3580 ck_assert_str_eq(client_err, "");
3581 ck_assert(client_conn != NULL);
3582
3583 mg_printf(client_conn, "GET /something/not/existing HTTP/1.0\r\n\r\n");
3584 client_res =
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);
3590
3591 ck_assert_str_eq(client_ri->local_uri, "200");
3592
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);
3596 client_err[8] = 0;
3597 ck_assert_str_eq(client_err, "err-4xx");
3598 test_sleep(1);
3599
3600 /* Create an error404.htm file */
3601 f = fopen("error404.htm", "wt");
3602 ck_assert(f != NULL);
3603 (void)fprintf(f, "err-404");
3604 (void)fclose(f);
3605
3606
3607 /* Ask for something not existing - should get error404.htm */
3608 memset(client_err, 0, sizeof(client_err));
3609 client_conn =
3610 mg_connect_client("127.0.0.1", 8080, 0, client_err, sizeof(client_err));
3611
3612 ck_assert_str_eq(client_err, "");
3613 ck_assert(client_conn != NULL);
3614
3615 mg_printf(client_conn, "GET /something/not/existing HTTP/1.0\r\n\r\n");
3616 client_res =
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);
3622
3623 ck_assert_str_eq(client_ri->local_uri, "200");
3624
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);
3628 client_err[8] = 0;
3629 ck_assert_str_eq(client_err, "err-404");
3630 test_sleep(1);
3631
3632
3633 /* Ask in a malformed way - should get error4xx.htm */
3634 memset(client_err, 0, sizeof(client_err));
3635 client_conn =
3636 mg_connect_client("127.0.0.1", 8080, 0, client_err, sizeof(client_err));
3637
3638 ck_assert_str_eq(client_err, "");
3639 ck_assert(client_conn != NULL);
3640
3641 mg_printf(client_conn, "Gimme some file!\r\n\r\n");
3642 client_res =
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);
3648
3649 ck_assert_str_eq(client_ri->local_uri, "200");
3650
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);
3654 client_err[8] = 0;
3655 ck_assert_str_eq(client_err, "err-4xx");
3656 test_sleep(1);
3657
3658
3659 /* Remove all error files created by this test */
3660 (void)remove("error.htm");
3661 (void)remove("error4xx.htm");
3662 (void)remove("error404.htm");
3663
3664
3665 /* Stop the server */
3666 test_mg_stop(ctx);
3667
3668
3669 /* HTTP 1.1 GET request - must not work, since server is already stopped */
3670 memset(client_err, 0, sizeof(client_err));
3671 client_conn =
3672 mg_connect_client("127.0.0.1", 8080, 0, client_err, sizeof(client_err));
3673
3674 ck_assert(client_conn == NULL);
3675 ck_assert_str_ne(client_err, "");
3676
3677 test_sleep(1);
3678
3679 mark_point();
3680 }
3681 END_TEST
3682
3683
3684 START_TEST(test_error_log_file)
3685 {
3686 /* Server var */
3687 struct mg_context *ctx;
3688 const char *OPTIONS[32];
3689 int opt_cnt = 0;
3690
3691 /* Client var */
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;
3696
3697 /* File content check var */
3698 FILE *f;
3699 char buf[1024];
3700 int len, ok;
3701
3702 mark_point();
3703
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++] = ".";
3714 #endif
3715 OPTIONS[opt_cnt] = NULL;
3716
3717 ctx = test_mg_start(NULL, 0, OPTIONS);
3718 ck_assert(ctx != NULL);
3719
3720 /* Remove log files (they may exist from previous incomplete runs of
3721 * this test) */
3722 (void)remove("error.log");
3723 (void)remove("access.log");
3724
3725 /* connect client */
3726 memset(client_err_buf, 0, sizeof(client_err_buf));
3727 memset(client_data_buf, 0, sizeof(client_data_buf));
3728
3729 client = mg_download("127.0.0.1",
3730 8080,
3731 0,
3732 client_err_buf,
3733 sizeof(client_err_buf),
3734 "GET /not_existing_file.ext HTTP/1.0\r\n\r\n");
3735
3736 ck_assert(ctx != NULL);
3737 ck_assert_str_eq(client_err_buf, "");
3738
3739 client_ri = mg_get_request_info(client);
3740
3741 ck_assert(client_ri != NULL);
3742 ck_assert_str_eq(client_ri->local_uri, "404");
3743
3744 /* Close the client connection */
3745 mg_close_connection(client);
3746
3747 /* Stop the server */
3748 test_mg_stop(ctx);
3749
3750
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));
3756 (void)fclose(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");
3764
3765 /* Check error.log */
3766 memset(buf, 0, sizeof(buf));
3767 f = fopen("error.log", "r");
3768 if (f) {
3769 (void)fgets(buf, sizeof(buf) - 1, f);
3770 fclose(f);
3771 }
3772 ck_assert_msg(f == NULL,
3773 "Should not create error log file on 404, but got [%s]",
3774 buf);
3775
3776 /* Remove log files */
3777 (void)remove("error.log");
3778 (void)remove("access.log");
3779
3780 /* Start server with bad options */
3781 ck_assert_str_eq(OPTIONS[0], "listening_ports");
3782 OPTIONS[1] = "bad port syntax";
3783
3784 ctx = test_mg_start(NULL, 0, OPTIONS);
3785 ck_assert_msg(
3786 ctx == NULL,
3787 "Should not be able to start server with bad port configuration");
3788
3789 /* Check access.log */
3790 memset(buf, 0, sizeof(buf));
3791 f = fopen("access.log", "r");
3792 if (f) {
3793 (void)fgets(buf, sizeof(buf) - 1, f);
3794 fclose(f);
3795 }
3796 ck_assert_msg(
3797 f == NULL,
3798 "Should not create access log file if start fails, but got [%s]",
3799 buf);
3800
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));
3806 (void)fclose(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");
3812
3813
3814 /* Remove log files */
3815 (void)remove("error.log");
3816 (void)remove("access.log");
3817
3818 mark_point();
3819 }
3820 END_TEST
3821
3822
3823 static int
3824 test_throttle_begin_request(struct mg_connection *conn)
3825 {
3826 const struct mg_request_info *ri;
3827 long unsigned len = 1024 * 10;
3828 const char *block = "0123456789";
3829 unsigned long i, blocklen;
3830
3831 ck_assert(conn != NULL);
3832 ri = mg_get_request_info(conn);
3833 ck_assert(ri != NULL);
3834
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");
3841
3842 mg_printf(conn,
3843 "HTTP/1.1 200 OK\r\n"
3844 "Content-Length: %lu\r\n"
3845 "Connection: close\r\n\r\n",
3846 len);
3847
3848 blocklen = (unsigned long)strlen(block);
3849
3850 for (i = 0; i < len; i += blocklen) {
3851 mg_write(conn, block, blocklen);
3852 }
3853
3854 mark_point();
3855
3856 return 987; /* Not a valid HTTP response code,
3857 * but it should be written to the log and passed to
3858 * end_request. */
3859 }
3860
3861
3862 static void
3863 test_throttle_end_request(const struct mg_connection *conn,
3864 int reply_status_code)
3865 {
3866 const struct mg_request_info *ri;
3867
3868 ck_assert(conn != NULL);
3869 ri = mg_get_request_info(conn);
3870 ck_assert(ri != NULL);
3871
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");
3878
3879 ck_assert_int_eq(reply_status_code, 987);
3880 }
3881
3882
3883 START_TEST(test_throttle)
3884 {
3885 /* Server var */
3886 struct mg_context *ctx;
3887 struct mg_callbacks callbacks;
3888 const char *OPTIONS[32];
3889 int opt_cnt = 0;
3890
3891 /* Client var */
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;
3896
3897 /* timing test */
3898 int r, data_read;
3899 time_t t0, t1;
3900 double dt;
3901
3902 mark_point();
3903
3904
3905 /* Set options and start server */
3906 #if !defined(NO_FILES)
3907 OPTIONS[opt_cnt++] = "document_root";
3908 OPTIONS[opt_cnt++] = ".";
3909 #endif
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;
3915
3916 memset(&callbacks, 0, sizeof(callbacks));
3917 callbacks.begin_request = test_throttle_begin_request;
3918 callbacks.end_request = test_throttle_end_request;
3919
3920 ctx = test_mg_start(&callbacks, 0, OPTIONS);
3921 ck_assert(ctx != NULL);
3922
3923 /* connect client */
3924 memset(client_err_buf, 0, sizeof(client_err_buf));
3925 memset(client_data_buf, 0, sizeof(client_data_buf));
3926
3927 strcpy(client_err_buf, "reset-content");
3928 client = mg_download("127.0.0.1",
3929 8080,
3930 0,
3931 client_err_buf,
3932 sizeof(client_err_buf),
3933 "GET /throttle?q HTTP/1.0\r\n\r\n");
3934
3935 ck_assert(ctx != NULL);
3936 ck_assert_str_eq(client_err_buf, "");
3937
3938 client_ri = mg_get_request_info(client);
3939
3940 ck_assert(client_ri != NULL);
3941 ck_assert_str_eq(client_ri->local_uri, "200");
3942
3943 ck_assert_int_eq(client_ri->content_length, 1024 * 10);
3944
3945 data_read = 0;
3946 t0 = time(NULL);
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);
3950 data_read += r;
3951 }
3952 t1 = time(NULL);
3953 dt = difftime(t1, t0) * 1000.0; /* Elapsed time in ms - in most systems
3954 * only with second resolution */
3955
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
3963 * 8 seconds. */
3964
3965 /* Check if there are at least 8 seconds */
3966 ck_assert_int_ge((int)dt, 8 * 1000);
3967
3968 /* Nothing left to read */
3969 r = mg_read(client, client_data_buf, sizeof(client_data_buf));
3970 ck_assert_int_eq(r, 0);
3971
3972 /* Close the client connection */
3973 mg_close_connection(client);
3974
3975 /* Stop the server */
3976 test_mg_stop(ctx);
3977
3978 mark_point();
3979 }
3980 END_TEST
3981
3982
3983 START_TEST(test_init_library)
3984 {
3985 unsigned f_avail, f_ret;
3986
3987 mark_point();
3988
3989 f_avail = mg_check_feature(0xFF);
3990 f_ret = mg_init_library(f_avail);
3991 ck_assert_uint_eq(f_ret, f_avail);
3992 }
3993 END_TEST
3994
3995
3996 #define LARGE_FILE_SIZE (1024 * 1024 * 10)
3997
3998 static int
3999 test_large_file_begin_request(struct mg_connection *conn)
4000 {
4001 const struct mg_request_info *ri;
4002 long unsigned len = LARGE_FILE_SIZE;
4003 const char *block = "0123456789";
4004 uint64_t i;
4005 size_t blocklen;
4006
4007 ck_assert(conn != NULL);
4008 ri = mg_get_request_info(conn);
4009 ck_assert(ri != NULL);
4010
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);
4016
4017 mg_printf(conn,
4018 "HTTP/1.1 200 OK\r\n"
4019 "Content-Length: %lu\r\n"
4020 "Connection: close\r\n\r\n",
4021 len);
4022
4023 blocklen = strlen(block);
4024
4025 for (i = 0; i < len; i += blocklen) {
4026 mg_write(conn, block, blocklen);
4027 }
4028
4029 mark_point();
4030
4031 return 200;
4032 }
4033
4034
4035 START_TEST(test_large_file)
4036 {
4037 /* Server var */
4038 struct mg_context *ctx;
4039 struct mg_callbacks callbacks;
4040 const char *OPTIONS[32];
4041 int opt_cnt = 0;
4042 #if !defined(NO_SSL)
4043 const char *ssl_cert = locate_ssl_cert();
4044 #endif
4045 char errmsg[256] = {0};
4046
4047 /* Client var */
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;
4052 int64_t data_read;
4053 int r;
4054 int retry, retry_ok_cnt, retry_fail_cnt;
4055
4056 mark_point();
4057
4058 /* Set options and start server */
4059 #if !defined(NO_FILES)
4060 OPTIONS[opt_cnt++] = "document_root";
4061 OPTIONS[opt_cnt++] = ".";
4062 #endif
4063 #if defined(NO_SSL)
4064 OPTIONS[opt_cnt++] = "listening_ports";
4065 OPTIONS[opt_cnt++] = "8080";
4066 #else
4067 OPTIONS[opt_cnt++] = "listening_ports";
4068 OPTIONS[opt_cnt++] = "8443s";
4069 OPTIONS[opt_cnt++] = "ssl_certificate";
4070 OPTIONS[opt_cnt++] = ssl_cert;
4071 #ifdef __MACH__
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";
4076 #else
4077 /* The Linux builds on Travis CI work fine with TLS1.2 */
4078 OPTIONS[opt_cnt++] = "ssl_protocol_version";
4079 OPTIONS[opt_cnt++] = "4";
4080 #endif
4081 ck_assert(ssl_cert != NULL);
4082 #endif
4083 OPTIONS[opt_cnt] = NULL;
4084
4085
4086 memset(&callbacks, 0, sizeof(callbacks));
4087 callbacks.begin_request = test_large_file_begin_request;
4088 callbacks.log_message = log_msg_func;
4089
4090 ctx = test_mg_start(&callbacks, (void *)errmsg, OPTIONS);
4091 ck_assert_str_eq(errmsg, "");
4092 ck_assert(ctx != NULL);
4093
4094 /* Try downloading several times */
4095 retry_ok_cnt = 0;
4096 retry_fail_cnt = 0;
4097 for (retry = 0; retry < 3; retry++) {
4098 int fail = 0;
4099 /* connect client */
4100 memset(client_err_buf, 0, sizeof(client_err_buf));
4101 memset(client_data_buf, 0, sizeof(client_data_buf));
4102
4103 client =
4104 mg_download("127.0.0.1",
4105 #if defined(NO_SSL)
4106 8080,
4107 0,
4108 #else
4109 8443,
4110 1,
4111 #endif
4112 client_err_buf,
4113 sizeof(client_err_buf),
4114 "GET /large.file HTTP/1.1\r\nHost: 127.0.0.1\r\n\r\n");
4115
4116 ck_assert(client != NULL);
4117 ck_assert_str_eq(client_err_buf, "");
4118
4119 client_ri = mg_get_request_info(client);
4120
4121 ck_assert(client_ri != NULL);
4122 ck_assert_str_eq(client_ri->local_uri, "200");
4123
4124 ck_assert_int_eq(client_ri->content_length, LARGE_FILE_SIZE);
4125
4126 data_read = 0;
4127 while (data_read < client_ri->content_length) {
4128 r = mg_read(client, client_data_buf, sizeof(client_data_buf));
4129 if (r < 0) {
4130 fail = 1;
4131 break;
4132 };
4133 data_read += r;
4134 }
4135
4136 /* Nothing left to read */
4137 r = mg_read(client, client_data_buf, sizeof(client_data_buf));
4138 if (fail) {
4139 ck_assert_int_eq(r, -1);
4140 retry_fail_cnt++;
4141 } else {
4142 ck_assert_int_eq(r, 0);
4143 retry_ok_cnt++;
4144 }
4145
4146 /* Close the client connection */
4147 mg_close_connection(client);
4148 }
4149
4150 #if defined(_WIN32)
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);
4154 #else
4155 ck_assert_int_eq(retry_fail_cnt, 0);
4156 ck_assert_int_eq(retry_ok_cnt, 3);
4157 #endif
4158
4159 /* Stop the server */
4160 test_mg_stop(ctx);
4161
4162 mark_point();
4163 }
4164 END_TEST
4165
4166
4167 static int test_mg_store_body_con_len = 20000;
4168
4169
4170 static int
4171 test_mg_store_body_put_delete_handler(struct mg_connection *conn, void *ignored)
4172 {
4173 char path[4096] = {0};
4174 const struct mg_request_info *info = mg_get_request_info(conn);
4175 int64_t rc;
4176
4177 (void)ignored;
4178
4179 mark_point();
4180
4181 sprintf(path, "./%s", info->local_uri);
4182 rc = mg_store_body(conn, path);
4183
4184 ck_assert_int_eq(test_mg_store_body_con_len, rc);
4185
4186 if (rc < 0) {
4187 mg_printf(conn,
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"
4191 "%s (ret: %ld)\n",
4192 path,
4193 (long)rc);
4194 mg_close_connection(conn);
4195
4196 /* Debug output for tests */
4197 printf("mg_store_body(%s) failed (ret: %ld)\n", path, (long)rc);
4198
4199 return 500;
4200 }
4201
4202 mg_printf(conn,
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",
4207 path,
4208 (long)rc);
4209 mg_close_connection(conn);
4210
4211 /* Debug output for tests */
4212 printf("mg_store_body(%s) OK (%ld bytes)\n", path, (long)rc);
4213
4214 mark_point();
4215
4216 return 200;
4217 }
4218
4219
4220 static int
4221 test_mg_store_body_begin_request_callback(struct mg_connection *conn)
4222 {
4223 const struct mg_request_info *info = mg_get_request_info(conn);
4224
4225 mark_point();
4226
4227 /* Debug output for tests */
4228 printf("test_mg_store_body_begin_request_callback called (%s)\n",
4229 info->request_method);
4230
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);
4234 }
4235
4236 mark_point();
4237
4238 return 0;
4239 }
4240
4241
4242 START_TEST(test_mg_store_body)
4243 {
4244 /* Client data */
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;
4249 int r;
4250 char check_data[256];
4251 char *check_ptr;
4252 char errmsg[256] = {0};
4253
4254 /* Server context handle */
4255 struct mg_context *ctx;
4256 struct mg_callbacks callbacks;
4257 const char *options[] = {
4258 #if !defined(NO_FILES)
4259 "document_root",
4260 ".",
4261 #endif
4262 #if !defined(NO_CACHING)
4263 "static_file_max_age",
4264 "0",
4265 #endif
4266 "listening_ports",
4267 "127.0.0.1:8082",
4268 "num_threads",
4269 "1",
4270 NULL
4271 };
4272
4273 mark_point();
4274
4275 memset(&callbacks, 0, sizeof(callbacks));
4276 callbacks.begin_request = test_mg_store_body_begin_request_callback;
4277 callbacks.log_message = log_msg_func;
4278
4279 /* Initialize the library */
4280 mg_init_library(0);
4281
4282 /* Start the server */
4283 ctx = mg_start(&callbacks, (void *)errmsg, options);
4284 ck_assert_str_eq(errmsg, "");
4285 ck_assert(ctx != NULL);
4286
4287 /* Run the server for 15 seconds */
4288 test_sleep(15);
4289
4290 /* Call a test client */
4291 client = mg_connect_client(
4292 "127.0.0.1", 8082, 0, client_err_buf, sizeof(client_err_buf));
4293
4294 ck_assert_str_eq(client_err_buf, "");
4295 ck_assert(client != NULL);
4296
4297 mg_printf(client,
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);
4301
4302 r = 0;
4303 while (r < test_mg_store_body_con_len) {
4304 int l = mg_write(client, "1234567890", 10);
4305 ck_assert_int_eq(l, 10);
4306 r += 10;
4307 }
4308
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, "");
4312
4313 client_ri = mg_get_request_info(client);
4314 ck_assert(client_ri != NULL);
4315
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");
4319
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;
4324
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);
4328
4329 mg_close_connection(client);
4330
4331 /* Run the server for 5 seconds */
4332 test_sleep(5);
4333
4334 /* Stop the server */
4335 test_mg_stop(ctx);
4336
4337 /* Un-initialize the library */
4338 mg_exit_library();
4339
4340 mark_point();
4341 }
4342 END_TEST
4343
4344
4345 #if defined(MG_USE_OPEN_FILE) && !defined(NO_FILES)
4346
4347 #define FILE_IN_MEM_SIZE (1024 * 100)
4348 static char *file_in_mem_data;
4349
4350 static const char *
4351 test_file_in_memory_open_file(const struct mg_connection *conn,
4352 const char *file_path,
4353 size_t *file_size)
4354 {
4355 (void)conn;
4356
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;
4361 } else {
4362 /* File is not in memory */
4363 return NULL;
4364 }
4365 }
4366
4367
4368 START_TEST(test_file_in_memory)
4369 {
4370 /* Server var */
4371 struct mg_context *ctx;
4372 struct mg_callbacks callbacks;
4373 const char *OPTIONS[32];
4374 int opt_cnt = 0;
4375 #if !defined(NO_SSL)
4376 const char *ssl_cert = locate_ssl_cert();
4377 #endif
4378
4379 /* Client var */
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;
4384 int64_t data_read;
4385 int r, i;
4386
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);
4392 }
4393
4394 /* Set options and start server */
4395 OPTIONS[opt_cnt++] = "document_root";
4396 OPTIONS[opt_cnt++] = ".";
4397 #if defined(NO_SSL)
4398 OPTIONS[opt_cnt++] = "listening_ports";
4399 OPTIONS[opt_cnt++] = "8080";
4400 #else
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);
4406 #endif
4407 OPTIONS[opt_cnt] = NULL;
4408
4409
4410 memset(&callbacks, 0, sizeof(callbacks));
4411 callbacks.open_file = test_file_in_memory_open_file;
4412
4413 ctx = test_mg_start(&callbacks, 0, OPTIONS);
4414 ck_assert(ctx != NULL);
4415
4416 /* connect client */
4417 memset(client_err_buf, 0, sizeof(client_err_buf));
4418 memset(client_data_buf, 0, sizeof(client_data_buf));
4419
4420 client =
4421 mg_download("127.0.0.1",
4422 #if defined(NO_SSL)
4423 8080,
4424 0,
4425 #else
4426 8443,
4427 1,
4428 #endif
4429 client_err_buf,
4430 sizeof(client_err_buf),
4431 "GET /file_in_mem HTTP/1.1\r\nHost: 127.0.0.1\r\n\r\n");
4432
4433 ck_assert(client != NULL);
4434 ck_assert_str_eq(client_err_buf, "");
4435
4436 client_ri = mg_get_request_info(client);
4437
4438 ck_assert(client_ri != NULL);
4439 ck_assert_str_eq(client_ri->local_uri, "200");
4440
4441 ck_assert_int_eq(client_ri->content_length, FILE_IN_MEM_SIZE);
4442
4443 data_read = 0;
4444 while (data_read < client_ri->content_length) {
4445 r = mg_read(client, client_data_buf, sizeof(client_data_buf));
4446 if (r > 0) {
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]);
4450 }
4451 data_read += r;
4452 }
4453 }
4454
4455 /* Nothing left to read */
4456 r = mg_read(client, client_data_buf, sizeof(client_data_buf));
4457 ck_assert_int_eq(r, 0);
4458
4459 /* Close the client connection */
4460 mg_close_connection(client);
4461
4462 /* Stop the server */
4463 test_mg_stop(ctx);
4464
4465 /* Free test data */
4466 free(file_in_mem_data);
4467 file_in_mem_data = NULL;
4468 }
4469 END_TEST
4470
4471 #else /* defined(MG_USE_OPEN_FILE) */
4472
4473 START_TEST(test_file_in_memory)
4474 {
4475 mark_point();
4476 }
4477 END_TEST
4478
4479 #endif
4480
4481
4482 static void
4483 minimal_http_https_client_impl(const char *server,
4484 uint16_t port,
4485 int use_ssl,
4486 const char *uri)
4487 {
4488 /* Client var */
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;
4493 int64_t data_read;
4494 int r;
4495
4496 mark_point();
4497
4498 client = mg_connect_client(
4499 server, port, use_ssl, client_err_buf, sizeof(client_err_buf));
4500
4501 ck_assert_str_eq(client_err_buf, "");
4502 ck_assert(client != NULL);
4503
4504 mg_printf(client, "GET /%s HTTP/1.0\r\n\r\n", uri);
4505
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, "");
4509
4510 client_ri = mg_get_request_info(client);
4511 ck_assert(client_ri != NULL);
4512
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);
4517
4518 data_read = 0;
4519 while (data_read < client_ri->content_length) {
4520 r = mg_read(client, client_data_buf, sizeof(client_data_buf));
4521 if (r > 0) {
4522 data_read += r;
4523 }
4524 }
4525
4526 /* Nothing left to read */
4527 r = mg_read(client, client_data_buf, sizeof(client_data_buf));
4528 ck_assert_int_eq(r, 0);
4529
4530 mark_point();
4531
4532 mg_close_connection(client);
4533
4534 mark_point();
4535 }
4536
4537
4538 static void
4539 minimal_http_client_impl(const char *server, uint16_t port, const char *uri)
4540 {
4541 minimal_http_https_client_impl(server, port, 0, uri);
4542 }
4543
4544
4545 #if !defined(NO_SSL)
4546 static void
4547 minimal_https_client_impl(const char *server, uint16_t port, const char *uri)
4548 {
4549 minimal_http_https_client_impl(server, port, 1, uri);
4550 }
4551 #endif
4552
4553
4554 START_TEST(test_minimal_client)
4555 {
4556 mark_point();
4557
4558 /* Initialize the library */
4559 mg_init_library(0);
4560
4561 mark_point();
4562
4563 /* Call a test client */
4564 minimal_http_client_impl("192.30.253.113" /* www.github.com */,
4565 80,
4566 "civetweb/civetweb/");
4567
4568 mark_point();
4569
4570 /* Un-initialize the library */
4571 mg_exit_library();
4572
4573 mark_point();
4574 }
4575 END_TEST
4576
4577
4578 static int
4579 minimal_test_request_handler(struct mg_connection *conn, void *cbdata)
4580 {
4581 const char *msg = (const char *)cbdata;
4582 unsigned long len = (unsigned long)strlen(msg);
4583
4584 mark_point();
4585
4586 mg_printf(conn,
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",
4591 len);
4592
4593 mg_write(conn, msg, len);
4594
4595 mark_point();
4596
4597 return 200;
4598 }
4599
4600
4601 START_TEST(test_minimal_http_server_callback)
4602 {
4603 /* This test should ensure the minimum server example in
4604 * docs/Embedding.md is still running. */
4605
4606 /* Server context handle */
4607 struct mg_context *ctx;
4608
4609 mark_point();
4610
4611 /* Initialize the library */
4612 mg_init_library(0);
4613
4614 /* Start the server */
4615 ctx = test_mg_start(NULL, 0, NULL);
4616 ck_assert(ctx != NULL);
4617
4618 /* Add some handler */
4619 mg_set_request_handler(ctx,
4620 "/hello",
4621 minimal_test_request_handler,
4622 (void *)"Hello world");
4623 mg_set_request_handler(ctx,
4624 "/8",
4625 minimal_test_request_handler,
4626 (void *)"Number eight");
4627
4628 /* Run the server for 15 seconds */
4629 test_sleep(10);
4630
4631 /* Call a test client */
4632 minimal_http_client_impl("127.0.0.1", 8080, "/hello");
4633
4634 /* Run the server for 15 seconds */
4635 test_sleep(5);
4636
4637
4638 /* Stop the server */
4639 test_mg_stop(ctx);
4640
4641 /* Un-initialize the library */
4642 mg_exit_library();
4643
4644 mark_point();
4645 }
4646 END_TEST
4647
4648
4649 START_TEST(test_minimal_https_server_callback)
4650 {
4651 #if !defined(NO_SSL)
4652 /* This test should show a HTTPS server with enhanced
4653 * security settings.
4654 *
4655 * Articles:
4656 * https://hynek.me/articles/hardening-your-web-servers-ssl-ciphers/
4657 *
4658 * Scanners:
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/
4664 */
4665
4666 /* Server context handle */
4667 struct mg_context *ctx;
4668
4669 /* Server start parameters for HTTPS */
4670 const char *OPTIONS[32];
4671 int opt_idx = 0;
4672
4673 /* HTTPS port - required */
4674 OPTIONS[opt_idx++] = "listening_ports";
4675 OPTIONS[opt_idx++] = "8443s";
4676
4677 /* path to certificate file - required */
4678 OPTIONS[opt_idx++] = "ssl_certificate";
4679 OPTIONS[opt_idx++] = locate_ssl_cert();
4680
4681 #if defined(LOCAL_TEST) || defined(_WIN32)
4682 /* Do not set this on Travis CI, since the build containers
4683 * contain older SSL libraries */
4684
4685 /* set minimum SSL version to TLS 1.2 - recommended */
4686 OPTIONS[opt_idx++] = "ssl_protocol_version";
4687 OPTIONS[opt_idx++] = "4";
4688
4689 /* set some modern ciphers - recommended */
4690 OPTIONS[opt_idx++] = "ssl_cipher_list";
4691 OPTIONS[opt_idx++] = "ECDH+AESGCM+AES256:!aNULL:!MD5:!DSS";
4692 #endif
4693
4694 /* set "HTTPS only" header - recommended */
4695 OPTIONS[opt_idx++] = "strict_transport_security_max_age";
4696 OPTIONS[opt_idx++] = "31622400";
4697
4698 /* end of options - required */
4699 OPTIONS[opt_idx] = NULL;
4700
4701 mark_point();
4702
4703 /* Initialize the library */
4704 mg_init_library(0);
4705
4706
4707 /* Start the server */
4708 ctx = test_mg_start(NULL, 0, OPTIONS);
4709 ck_assert(ctx != NULL);
4710
4711 /* Add some handler */
4712 mg_set_request_handler(ctx,
4713 "/hello",
4714 minimal_test_request_handler,
4715 (void *)"Hello world");
4716 mg_set_request_handler(ctx,
4717 "/8",
4718 minimal_test_request_handler,
4719 (void *)"Number eight");
4720
4721 /* Run the server for 15 seconds */
4722 test_sleep(10);
4723
4724 /* Call a test client */
4725 minimal_https_client_impl("127.0.0.1", 8443, "/hello");
4726
4727 /* Run the server for 15 seconds */
4728 test_sleep(5);
4729
4730
4731 /* Stop the server */
4732 test_mg_stop(ctx);
4733
4734 /* Un-initialize the library */
4735 mg_exit_library();
4736 #endif
4737 mark_point();
4738 }
4739 END_TEST
4740
4741
4742 #if !defined(REPLACE_CHECK_FOR_LOCAL_DEBUGGING)
4743 Suite *
4744 make_public_server_suite(void)
4745 {
4746 Suite *const suite = suite_create("PublicServer");
4747
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");
4767
4768
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);
4772
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);
4776
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);
4780
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);
4785
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);
4789
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);
4793
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);
4799
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);
4803
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);
4808
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);
4812
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);
4816
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);
4820
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);
4824
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);
4828
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);
4833
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);
4837
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);
4841
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);
4845
4846 return suite;
4847 }
4848 #endif
4849
4850
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
4856 */
4857
4858 static int chk_ok = 0;
4859 static int chk_failed = 0;
4860
4861
4862 void
4863 MAIN_PUBLIC_SERVER(void)
4864 {
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);
4868
4869 test_the_test_environment(0);
4870 test_threading(0);
4871
4872 test_minimal_client(0);
4873
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);
4880 test_http_auth(0);
4881 test_keep_alive(0);
4882 test_error_handling(0);
4883 test_error_log_file(0);
4884 test_throttle(0);
4885 test_large_file(0);
4886 test_file_in_memory(0);
4887
4888 mg_exit_library();
4889
4890 printf("\nok: %i\nfailed: %i\n\n", chk_ok, chk_failed);
4891 }
4892
4893 void
4894 _ck_assert_failed(const char *file, int line, const char *expr, ...)
4895 {
4896 va_list va;
4897 va_start(va, expr);
4898 fprintf(stderr, "Error: %s, line %i\n", file, line); /* breakpoint here ! */
4899 vfprintf(stderr, expr, va);
4900 fprintf(stderr, "\n\n");
4901 va_end(va);
4902 chk_failed++;
4903 }
4904
4905 void
4906 _ck_assert_msg(int cond, const char *file, int line, const char *expr, ...)
4907 {
4908 va_list va;
4909
4910 if (cond) {
4911 chk_ok++;
4912 return;
4913 }
4914
4915 va_start(va, expr);
4916 fprintf(stderr, "Error: %s, line %i\n", file, line); /* breakpoint here ! */
4917 vfprintf(stderr, expr, va);
4918 fprintf(stderr, "\n\n");
4919 va_end(va);
4920 chk_failed++;
4921 }
4922
4923 void
4924 _mark_point(const char *file, int line)
4925 {
4926 chk_ok++;
4927 }
4928
4929 void
4930 tcase_fn_start(const char *fname, const char *file, int line)
4931 {
4932 }
4933 void suite_add_tcase(Suite *s, TCase *tc){};
4934 void _tcase_add_test(TCase *tc,
4935 TFun tf,
4936 const char *fname,
4937 int _signal,
4938 int allowed_exit_value,
4939 int start,
4940 int end){};
4941 TCase *
4942 tcase_create(const char *name)
4943 {
4944 return NULL;
4945 };
4946 Suite *
4947 suite_create(const char *name)
4948 {
4949 return NULL;
4950 };
4951 void tcase_set_timeout(TCase *tc, double timeout){};
4952
4953 #endif