]> git.proxmox.com Git - ceph.git/blame - ceph/src/civetweb/test/public_server.c
update sources to v12.1.0
[ceph.git] / ceph / src / civetweb / test / public_server.c
CommitLineData
7c673cae
FG
1/* Copyright (c) 2015-2016 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 <time.h>
33#include <sys/types.h>
34#include <sys/stat.h>
35
36#include "public_server.h"
37#include <civetweb.h>
38
39#if defined(_WIN32)
40#include <Windows.h>
41#define test_sleep(x) (Sleep((x)*1000))
42#else
43#include <unistd.h>
44#define test_sleep(x) (sleep(x))
45#endif
46
47/* This unit test file uses the excellent Check unit testing library.
48 * The API documentation is available here:
49 * http://check.sourceforge.net/doc/check_html/index.html
50 */
51
52
53static const char *
54locate_path(const char *a_path)
55{
56 static char r_path[256];
57
58#ifdef _WIN32
59#ifdef LOCAL_TEST
60 sprintf(r_path, "%s\\", a_path);
61#else
62 /* Appveyor */
63 sprintf(r_path, "..\\..\\..\\%s\\", a_path);
64/* TODO: the different paths
65 * used in the different test
66 * system is an unsolved
67 * problem. */
68#endif
69#else
70#ifdef LOCAL_TEST
71 sprintf(r_path, "%s/", a_path);
72#else
73 /* Travis */
74 sprintf(r_path,
75 "../../%s/",
76 a_path); // TODO: fix path in CI test environment
77#endif
78#endif
79
80 return r_path;
81}
82
83
84#define locate_resources() locate_path("resources")
85#define locate_test_exes() locate_path("output")
86
87
88static const char *
89locate_ssl_cert(void)
90{
91 static char cert_path[256];
92 const char *res = locate_resources();
93 size_t l;
94
95 ck_assert(res != NULL);
96 l = strlen(res);
97 ck_assert_uint_gt(l, 0);
98 ck_assert_uint_lt(l, 100); /* assume there is enough space left in our
99 typical 255 character string buffers */
100
101 strcpy(cert_path, res);
102 strcat(cert_path, "ssl_cert.pem");
103 return cert_path;
104}
105
106
107static int
108wait_not_null(void *volatile *data)
109{
110 int i;
111 for (i = 0; i < 100; i++) {
112 mark_point();
113 test_sleep(1);
114
115 if (*data != NULL) {
116 mark_point();
117 return 1;
118 }
119 }
120
121#if defined(__MINGW32__) || defined(__GNUC__)
122#pragma GCC diagnostic push
123#pragma GCC diagnostic ignored "-Wunreachable-code"
124#endif
125#ifdef __clang__
126#pragma clang diagnostic push
127#pragma clang diagnostic ignored "-Wunreachable-code"
128#endif
129
130 ck_abort_msg("wait_not_null failed");
131
132 return 0;
133
134#ifdef __clang__
135#pragma clang diagnostic pop
136#endif
137#if defined(__MINGW32__) || defined(__GNUC__)
138#pragma GCC diagnostic pop
139#endif
140}
141
142
143START_TEST(test_the_test_environment)
144{
145 char wd[300];
146 char buf[500];
147 FILE *f;
148 struct stat st;
149 int ret;
150 const char *ssl_cert = locate_ssl_cert();
151
152 memset(wd, 0, sizeof(wd));
153 memset(buf, 0, sizeof(buf));
154
155/* Get the current working directory */
156#ifdef _WIN32
157 (void)GetCurrentDirectoryA(sizeof(wd), wd);
158 wd[sizeof(wd) - 1] = 0;
159#else
160 (void)getcwd(wd, sizeof(wd));
161 wd[sizeof(wd) - 1] = 0;
162#endif
163
164/* Check the pem file */
165#ifdef _WIN32
166 strcpy(buf, wd);
167 strcat(buf, "\\");
168 strcat(buf, ssl_cert);
169 f = fopen(buf, "rb");
170#else
171 strcpy(buf, wd);
172 strcat(buf, "/");
173 strcat(buf, ssl_cert);
174 f = fopen(buf, "r");
175#endif
176
177 if (f) {
178 fclose(f);
179 } else {
180 fprintf(stderr, "%s not found", buf);
181 }
182
183/* Check the test dir */
184#ifdef _WIN32
185 strcpy(buf, wd);
186 strcat(buf, "\\test");
187#else
188 strcpy(buf, wd);
189 strcat(buf, "/test");
190#endif
191
192 memset(&st, 0, sizeof(st));
193 ret = stat(buf, &st);
194
195 if (ret) {
196 fprintf(stderr, "%s not found", buf);
197 }
198
199
200#ifdef _WIN32
201/* Try to copy the files required for AppVeyor */
202#if defined(_WIN64) || defined(__MINGW64__)
203 system("cmd /c copy C:\\OpenSSL-Win64\\libeay32.dll libeay32.dll");
204 system("cmd /c copy C:\\OpenSSL-Win64\\libssl32.dll libssl32.dll");
205 system("cmd /c copy C:\\OpenSSL-Win64\\ssleay32.dll ssleay32.dll");
206 system("cmd /c copy C:\\OpenSSL-Win64\\libeay32.dll libeay64.dll");
207 system("cmd /c copy C:\\OpenSSL-Win64\\libssl32.dll libssl64.dll");
208 system("cmd /c copy C:\\OpenSSL-Win64\\ssleay32.dll ssleay64.dll");
209#else
210 system("cmd /c copy C:\\OpenSSL-Win32\\libeay32.dll libeay32.dll");
211 system("cmd /c copy C:\\OpenSSL-Win32\\libssl32.dll libssl32.dll");
212 system("cmd /c copy C:\\OpenSSL-Win32\\ssleay32.dll ssleay32.dll");
213#endif
214#endif
215}
216END_TEST
217
218
219static void *threading_data;
220
221static void *
222test_thread_func_t(void *param)
223{
224 ck_assert_ptr_eq(param, &threading_data);
225 ck_assert_ptr_eq(threading_data, NULL);
226 threading_data = &threading_data;
227 return NULL;
228}
229
230
231START_TEST(test_threading)
232{
233 int ok;
234
235 threading_data = NULL;
236
237 ok = mg_start_thread(test_thread_func_t, &threading_data);
238 ck_assert_int_eq(ok, 0);
239
240 wait_not_null(&threading_data);
241 ck_assert_ptr_eq(threading_data, &threading_data);
242}
243END_TEST
244
245
246static int
247log_msg_func(const struct mg_connection *conn, const char *message)
248{
249 struct mg_context *ctx;
250 char *ud;
251
252 ck_assert(conn != NULL);
253 ctx = mg_get_context(conn);
254 ck_assert(ctx != NULL);
255 ud = (char *)mg_get_user_data(ctx);
256
257 strncpy(ud, message, 255);
258 ud[255] = 0;
259 return 1;
260}
261
262
263START_TEST(test_mg_start_stop_http_server)
264{
265 struct mg_context *ctx;
266 const char *OPTIONS[] = {
267#if !defined(NO_FILES)
268 "document_root",
269 ".",
270#endif
271 "listening_ports",
272 "8080",
273 NULL,
274 };
275 size_t ports_cnt;
276 int ports[16];
277 int ssl[16];
278 struct mg_callbacks callbacks;
279 char errmsg[256];
280
281 struct mg_connection *client_conn;
282 char client_err[256];
283 const struct mg_request_info *client_ri;
284 int client_res, ret;
285 struct mg_server_ports portinfo[8];
286
287 memset(ports, 0, sizeof(ports));
288 memset(ssl, 0, sizeof(ssl));
289 memset(portinfo, 0, sizeof(portinfo));
290 memset(&callbacks, 0, sizeof(callbacks));
291 memset(errmsg, 0, sizeof(errmsg));
292
293 callbacks.log_message = log_msg_func;
294
295 mark_point();
296 ctx = mg_start(&callbacks, (void *)errmsg, OPTIONS);
297 test_sleep(1);
298
299 ck_assert_str_eq(errmsg, "");
300 ck_assert(ctx != NULL);
301
302 ports_cnt = mg_get_ports(ctx, 16, ports, ssl);
303 ck_assert_uint_eq(ports_cnt, 1);
304 ck_assert_int_eq(ports[0], 8080);
305 ck_assert_int_eq(ssl[0], 0);
306 ck_assert_int_eq(ports[1], 0);
307 ck_assert_int_eq(ssl[1], 0);
308
309 ret = mg_get_server_ports(ctx, 0, portinfo);
310 ck_assert_int_lt(ret, 0);
311 ck_assert_int_eq(portinfo[0].protocol, 0);
312 ck_assert_int_eq(portinfo[0].port, 0);
313 ck_assert_int_eq(portinfo[0].is_ssl, 0);
314 ck_assert_int_eq(portinfo[0].is_redirect, 0);
315 ck_assert_int_eq(portinfo[1].protocol, 0);
316 ck_assert_int_eq(portinfo[1].port, 0);
317 ck_assert_int_eq(portinfo[1].is_ssl, 0);
318 ck_assert_int_eq(portinfo[1].is_redirect, 0);
319
320 ret = mg_get_server_ports(ctx, 4, portinfo);
321 ck_assert_int_eq(ret, 1);
322 ck_assert_int_eq(portinfo[0].protocol, 1);
323 ck_assert_int_eq(portinfo[0].port, 8080);
324 ck_assert_int_eq(portinfo[0].is_ssl, 0);
325 ck_assert_int_eq(portinfo[0].is_redirect, 0);
326 ck_assert_int_eq(portinfo[1].protocol, 0);
327 ck_assert_int_eq(portinfo[1].port, 0);
328 ck_assert_int_eq(portinfo[1].is_ssl, 0);
329 ck_assert_int_eq(portinfo[1].is_redirect, 0);
330
331 test_sleep(1);
332
333 /* HTTP 1.0 GET request */
334 memset(client_err, 0, sizeof(client_err));
335 client_conn =
336 mg_connect_client("127.0.0.1", 8080, 0, client_err, sizeof(client_err));
337 ck_assert(client_conn != NULL);
338 ck_assert_str_eq(client_err, "");
339 mg_printf(client_conn, "GET / HTTP/1.0\r\n\r\n");
340 client_res =
341 mg_get_response(client_conn, client_err, sizeof(client_err), 10000);
342 ck_assert_int_ge(client_res, 0);
343 ck_assert_str_eq(client_err, "");
344 client_ri = mg_get_request_info(client_conn);
345 ck_assert(client_ri != NULL);
346
347#if defined(NO_FILES)
348 ck_assert_str_eq(client_ri->uri, "404");
349#else
350 ck_assert_str_eq(client_ri->uri, "200");
351 /* TODO: ck_assert_str_eq(client_ri->request_method, "HTTP/1.0"); */
352 client_res = (int)mg_read(client_conn, client_err, sizeof(client_err));
353 ck_assert_int_gt(client_res, 0);
354 ck_assert_int_le(client_res, sizeof(client_err));
355#endif
356 mg_close_connection(client_conn);
357
358 test_sleep(1);
359
360 /* HTTP 1.1 GET request */
361 memset(client_err, 0, sizeof(client_err));
362 client_conn =
363 mg_connect_client("127.0.0.1", 8080, 0, client_err, sizeof(client_err));
364 ck_assert(client_conn != NULL);
365 ck_assert_str_eq(client_err, "");
366 mg_printf(client_conn, "GET / HTTP/1.1\r\n");
367 mg_printf(client_conn, "Host: localhost:8080\r\n");
368 mg_printf(client_conn, "Connection: close\r\n\r\n");
369 client_res =
370 mg_get_response(client_conn, client_err, sizeof(client_err), 10000);
371 ck_assert_int_ge(client_res, 0);
372 ck_assert_str_eq(client_err, "");
373 client_ri = mg_get_request_info(client_conn);
374 ck_assert(client_ri != NULL);
375
376#if defined(NO_FILES)
377 ck_assert_str_eq(client_ri->uri, "404");
378#else
379 ck_assert_str_eq(client_ri->uri, "200");
380 /* TODO: ck_assert_str_eq(client_ri->request_method, "HTTP/1.0"); */
381 client_res = (int)mg_read(client_conn, client_err, sizeof(client_err));
382 ck_assert_int_gt(client_res, 0);
383 ck_assert_int_le(client_res, sizeof(client_err));
384#endif
385 mg_close_connection(client_conn);
386
387 test_sleep(1);
388
389
390 /* HTTP 1.7 GET request - this HTTP version does not exist */
391 memset(client_err, 0, sizeof(client_err));
392 client_conn =
393 mg_connect_client("127.0.0.1", 8080, 0, client_err, sizeof(client_err));
394 ck_assert(client_conn != NULL);
395 ck_assert_str_eq(client_err, "");
396 mg_printf(client_conn, "GET / HTTP/1.7\r\n");
397 mg_printf(client_conn, "Host: localhost:8080\r\n");
398 mg_printf(client_conn, "Connection: close\r\n\r\n");
399 client_res =
400 mg_get_response(client_conn, client_err, sizeof(client_err), 10000);
401 ck_assert_int_ge(client_res, 0);
402 ck_assert_str_eq(client_err, "");
403 client_ri = mg_get_request_info(client_conn);
404 ck_assert(client_ri != NULL);
405
406 /* Response must be 505 HTTP Version not supported */
407 ck_assert_str_eq(client_ri->uri, "505");
408 mg_close_connection(client_conn);
409
410 test_sleep(1);
411
412
413 /* End test */
414 mg_stop(ctx);
415}
416END_TEST
417
418
419START_TEST(test_mg_start_stop_https_server)
420{
421#ifndef NO_SSL
422
423 struct mg_context *ctx;
424
425 size_t ports_cnt;
426 int ports[16];
427 int ssl[16];
428 struct mg_callbacks callbacks;
429 char errmsg[256];
430
431 const char *OPTIONS[8]; /* initializer list here is rejected by CI test */
432 int opt_idx = 0;
433 const char *ssl_cert = locate_ssl_cert();
434
435 struct mg_connection *client_conn;
436 char client_err[256];
437 const struct mg_request_info *client_ri;
438 int client_res, ret;
439 struct mg_server_ports portinfo[8];
440
441 ck_assert(ssl_cert != NULL);
442
443 memset((void *)OPTIONS, 0, sizeof(OPTIONS));
444#if !defined(NO_FILES)
445 OPTIONS[opt_idx++] = "document_root";
446 OPTIONS[opt_idx++] = ".";
447#endif
448 OPTIONS[opt_idx++] = "listening_ports";
449 OPTIONS[opt_idx++] = "8080r,8443s";
450 OPTIONS[opt_idx++] = "ssl_certificate";
451 OPTIONS[opt_idx++] = ssl_cert;
452
453 ck_assert_int_le(opt_idx, (int)(sizeof(OPTIONS) / sizeof(OPTIONS[0])));
454 ck_assert(OPTIONS[sizeof(OPTIONS) / sizeof(OPTIONS[0]) - 1] == NULL);
455 ck_assert(OPTIONS[sizeof(OPTIONS) / sizeof(OPTIONS[0]) - 2] == NULL);
456
457 memset(ports, 0, sizeof(ports));
458 memset(ssl, 0, sizeof(ssl));
459 memset(portinfo, 0, sizeof(portinfo));
460 memset(&callbacks, 0, sizeof(callbacks));
461 memset(errmsg, 0, sizeof(errmsg));
462
463 callbacks.log_message = log_msg_func;
464
465 mark_point();
466 ctx = mg_start(&callbacks, (void *)errmsg, OPTIONS);
467 test_sleep(1);
468 ck_assert_str_eq(errmsg, "");
469 ck_assert(ctx != NULL);
470
471 ports_cnt = mg_get_ports(ctx, 16, ports, ssl);
472 ck_assert_uint_eq(ports_cnt, 2);
473 ck_assert_int_eq(ports[0], 8080);
474 ck_assert_int_eq(ssl[0], 0);
475 ck_assert_int_eq(ports[1], 8443);
476 ck_assert_int_eq(ssl[1], 1);
477 ck_assert_int_eq(ports[2], 0);
478 ck_assert_int_eq(ssl[2], 0);
479
480
481 ret = mg_get_server_ports(ctx, 0, portinfo);
482 ck_assert_int_lt(ret, 0);
483 ck_assert_int_eq(portinfo[0].protocol, 0);
484 ck_assert_int_eq(portinfo[0].port, 0);
485 ck_assert_int_eq(portinfo[0].is_ssl, 0);
486 ck_assert_int_eq(portinfo[0].is_redirect, 0);
487 ck_assert_int_eq(portinfo[1].protocol, 0);
488 ck_assert_int_eq(portinfo[1].port, 0);
489 ck_assert_int_eq(portinfo[1].is_ssl, 0);
490 ck_assert_int_eq(portinfo[1].is_redirect, 0);
491
492 ret = mg_get_server_ports(ctx, 4, portinfo);
493 ck_assert_int_eq(ret, 2);
494 ck_assert_int_eq(portinfo[0].protocol, 1);
495 ck_assert_int_eq(portinfo[0].port, 8080);
496 ck_assert_int_eq(portinfo[0].is_ssl, 0);
497 ck_assert_int_eq(portinfo[0].is_redirect, 1);
498 ck_assert_int_eq(portinfo[1].protocol, 1);
499 ck_assert_int_eq(portinfo[1].port, 8443);
500 ck_assert_int_eq(portinfo[1].is_ssl, 1);
501 ck_assert_int_eq(portinfo[1].is_redirect, 0);
502 ck_assert_int_eq(portinfo[2].protocol, 0);
503 ck_assert_int_eq(portinfo[2].port, 0);
504 ck_assert_int_eq(portinfo[2].is_ssl, 0);
505 ck_assert_int_eq(portinfo[2].is_redirect, 0);
506
507 test_sleep(1);
508
509 memset(client_err, 0, sizeof(client_err));
510 client_conn =
511 mg_connect_client("127.0.0.1", 8443, 1, client_err, sizeof(client_err));
512 ck_assert(client_conn != NULL);
513 ck_assert_str_eq(client_err, "");
514 mg_printf(client_conn, "GET / HTTP/1.0\r\n\r\n");
515 client_res =
516 mg_get_response(client_conn, client_err, sizeof(client_err), 10000);
517 ck_assert_int_ge(client_res, 0);
518 ck_assert_str_eq(client_err, "");
519 client_ri = mg_get_request_info(client_conn);
520 ck_assert(client_ri != NULL);
521
522#if defined(NO_FILES)
523 ck_assert_str_eq(client_ri->uri, "404");
524#else
525 ck_assert_str_eq(client_ri->uri, "200");
526 /* TODO: ck_assert_str_eq(client_ri->request_method, "HTTP/1.0"); */
527 client_res = (int)mg_read(client_conn, client_err, sizeof(client_err));
528 ck_assert_int_gt(client_res, 0);
529 ck_assert_int_le(client_res, sizeof(client_err));
530#endif
531 mg_close_connection(client_conn);
532
533 test_sleep(1);
534
535 mg_stop(ctx);
536#endif
537}
538END_TEST
539
540
541START_TEST(test_mg_server_and_client_tls)
542{
543#ifndef NO_SSL
544
545 struct mg_context *ctx;
546
547 int ports_cnt;
548 struct mg_server_ports ports[16];
549 struct mg_callbacks callbacks;
550 char errmsg[256];
551
552 struct mg_connection *client_conn;
553 char client_err[256];
554 const struct mg_request_info *client_ri;
555 int client_res;
556 struct mg_client_options client_options;
557
558 const char *OPTIONS[32]; /* initializer list here is rejected by CI test */
559 int opt_idx = 0;
560 char server_cert[256];
561 char client_cert[256];
562 const char *res_dir = locate_resources();
563
564 ck_assert(res_dir != NULL);
565 strcpy(server_cert, res_dir);
566 strcpy(client_cert, res_dir);
567#ifdef _WIN32
568 strcat(server_cert, "cert\\server.pem");
569 strcat(client_cert, "cert\\client.pem");
570#else
571 strcat(server_cert, "cert/server.pem");
572 strcat(client_cert, "cert/client.pem");
573#endif
574
575 memset((void *)OPTIONS, 0, sizeof(OPTIONS));
576#if !defined(NO_FILES)
577 OPTIONS[opt_idx++] = "document_root";
578 OPTIONS[opt_idx++] = ".";
579#endif
580 OPTIONS[opt_idx++] = "listening_ports";
581 OPTIONS[opt_idx++] = "8080r,8443s";
582 OPTIONS[opt_idx++] = "ssl_certificate";
583 OPTIONS[opt_idx++] = server_cert;
584 OPTIONS[opt_idx++] = "ssl_verify_peer";
585 OPTIONS[opt_idx++] = "yes";
586 OPTIONS[opt_idx++] = "ssl_ca_file";
587 OPTIONS[opt_idx++] = client_cert;
588
589 ck_assert_int_le(opt_idx, (int)(sizeof(OPTIONS) / sizeof(OPTIONS[0])));
590 ck_assert(OPTIONS[sizeof(OPTIONS) / sizeof(OPTIONS[0]) - 1] == NULL);
591 ck_assert(OPTIONS[sizeof(OPTIONS) / sizeof(OPTIONS[0]) - 2] == NULL);
592
593 memset(ports, 0, sizeof(ports));
594 memset(&callbacks, 0, sizeof(callbacks));
595 memset(errmsg, 0, sizeof(errmsg));
596
597 callbacks.log_message = log_msg_func;
598
599 mark_point();
600 ctx = mg_start(&callbacks, (void *)errmsg, OPTIONS);
601 test_sleep(1);
602 ck_assert_str_eq(errmsg, "");
603 ck_assert(ctx != NULL);
604
605 ports_cnt = mg_get_server_ports(ctx, 16, ports);
606 ck_assert_int_eq(ports_cnt, 2);
607 ck_assert_int_eq(ports[0].protocol, 1);
608 ck_assert_int_eq(ports[0].port, 8080);
609 ck_assert_int_eq(ports[0].is_ssl, 0);
610 ck_assert_int_eq(ports[0].is_redirect, 1);
611 ck_assert_int_eq(ports[1].protocol, 1);
612 ck_assert_int_eq(ports[1].port, 8443);
613 ck_assert_int_eq(ports[1].is_ssl, 1);
614 ck_assert_int_eq(ports[1].is_redirect, 0);
615 ck_assert_int_eq(ports[2].protocol, 0);
616 ck_assert_int_eq(ports[2].port, 0);
617 ck_assert_int_eq(ports[2].is_ssl, 0);
618 ck_assert_int_eq(ports[2].is_redirect, 0);
619
620 test_sleep(1);
621
622 memset(client_err, 0, sizeof(client_err));
623 client_conn =
624 mg_connect_client("127.0.0.1", 8443, 1, client_err, sizeof(client_err));
625 ck_assert(client_conn == NULL);
626 ck_assert_str_ne(client_err, "");
627
628 memset(client_err, 0, sizeof(client_err));
629 memset(&client_options, 0, sizeof(client_options));
630 client_options.host = "127.0.0.1";
631 client_options.port = 8443;
632 client_options.client_cert = client_cert;
633 client_options.server_cert = server_cert;
634
635 client_conn = mg_connect_client_secure(&client_options,
636 client_err,
637 sizeof(client_err));
638 ck_assert(client_conn != NULL);
639 ck_assert_str_eq(client_err, "");
640 mg_printf(client_conn, "GET / HTTP/1.0\r\n\r\n");
641 client_res =
642 mg_get_response(client_conn, client_err, sizeof(client_err), 10000);
643 ck_assert_int_ge(client_res, 0);
644 ck_assert_str_eq(client_err, "");
645 client_ri = mg_get_request_info(client_conn);
646 ck_assert(client_ri != NULL);
647
648#if defined(NO_FILES)
649 ck_assert_str_eq(client_ri->uri, "404");
650#else
651 ck_assert_str_eq(client_ri->uri, "200");
652 /* TODO: ck_assert_str_eq(client_ri->request_method, "HTTP/1.0"); */
653 client_res = (int)mg_read(client_conn, client_err, sizeof(client_err));
654 ck_assert_int_gt(client_res, 0);
655 ck_assert_int_le(client_res, sizeof(client_err));
656#endif
657 mg_close_connection(client_conn);
658
659 /* TODO: A client API using a client certificate is missing */
660
661 test_sleep(1);
662
663 mg_stop(ctx);
664#endif
665}
666END_TEST
667
668
669static struct mg_context *g_ctx;
670
671static int
672request_test_handler(struct mg_connection *conn, void *cbdata)
673{
674 int i;
675 char chunk_data[32];
676 const struct mg_request_info *ri;
677 struct mg_context *ctx;
678 void *ud, *cud;
679
680 ctx = mg_get_context(conn);
681 ud = mg_get_user_data(ctx);
682 ri = mg_get_request_info(conn);
683
684 ck_assert(ri != NULL);
685 ck_assert(ctx == g_ctx);
686 ck_assert(ud == &g_ctx);
687
688 mg_set_user_connection_data(conn, (void *)6543);
689 cud = mg_get_user_connection_data(conn);
690 ck_assert_ptr_eq((void *)cud, (void *)6543);
691
692 ck_assert_ptr_eq((void *)cbdata, (void *)7);
693 strcpy(chunk_data, "123456789A123456789B123456789C");
694
695 mg_printf(conn,
696 "HTTP/1.1 200 OK\r\n"
697 "Transfer-Encoding: chunked\r\n"
698 "Content-Type: text/plain\r\n\r\n");
699
700 for (i = 1; i <= 10; i++) {
701 mg_printf(conn, "%x\r\n", i);
702 mg_write(conn, chunk_data, (unsigned)i);
703 mg_printf(conn, "\r\n");
704 }
705
706 mg_printf(conn, "0\r\n\r\n");
707
708 return 1;
709}
710
711#ifdef USE_WEBSOCKET
712/****************************************************************************/
713/* WEBSOCKET SERVER */
714/****************************************************************************/
715static const char *websocket_welcome_msg = "websocket welcome\n";
716static const size_t websocket_welcome_msg_len =
717 18 /* strlen(websocket_welcome_msg) */;
718static const char *websocket_goodbye_msg = "websocket bye\n";
719static const size_t websocket_goodbye_msg_len =
720 14 /* strlen(websocket_goodbye_msg) */;
721
722
723static int
724websock_server_connect(const struct mg_connection *conn, void *udata)
725{
726 (void)conn;
727
728 ck_assert_ptr_eq((void *)udata, (void *)7531);
729 printf("Server: Websocket connected\n");
730
731 return 0; /* return 0 to accept every connection */
732}
733
734
735static void
736websock_server_ready(struct mg_connection *conn, void *udata)
737{
738 ck_assert_ptr_eq((void *)udata, (void *)7531);
739 printf("Server: Websocket ready\n");
740
741 /* Send websocket welcome message */
742 mg_lock_connection(conn);
743 mg_websocket_write(conn,
744 WEBSOCKET_OPCODE_TEXT,
745 websocket_welcome_msg,
746 websocket_welcome_msg_len);
747 mg_unlock_connection(conn);
748
749 printf("Server: Websocket ready X\n");
750}
751
752
753static int
754websock_server_data(struct mg_connection *conn,
755 int bits,
756 char *data,
757 size_t data_len,
758 void *udata)
759{
760 (void)bits;
761
762 ck_assert_ptr_eq((void *)udata, (void *)7531);
763 printf("Server: Got %u bytes from the client\n", (unsigned)data_len);
764
765 if (data_len == 3 && !memcmp(data, "bye", 3)) {
766 /* Send websocket goodbye message */
767 mg_lock_connection(conn);
768 mg_websocket_write(conn,
769 WEBSOCKET_OPCODE_TEXT,
770 websocket_goodbye_msg,
771 websocket_goodbye_msg_len);
772 mg_unlock_connection(conn);
773 } else if (data_len == 5 && !memcmp(data, "data1", 5)) {
774 mg_lock_connection(conn);
775 mg_websocket_write(conn, WEBSOCKET_OPCODE_TEXT, "ok1", 3);
776 mg_unlock_connection(conn);
777 } else if (data_len == 5 && !memcmp(data, "data2", 5)) {
778 mg_lock_connection(conn);
779 mg_websocket_write(conn, WEBSOCKET_OPCODE_TEXT, "ok 2", 4);
780 mg_unlock_connection(conn);
781 } else if (data_len == 5 && !memcmp(data, "data3", 5)) {
782 mg_lock_connection(conn);
783 mg_websocket_write(conn, WEBSOCKET_OPCODE_TEXT, "ok - 3", 6);
784 mg_unlock_connection(conn);
785 } else {
786
787#if defined(__MINGW32__) || defined(__GNUC__)
788#pragma GCC diagnostic push
789#pragma GCC diagnostic ignored "-Wunreachable-code"
790#endif
791#ifdef __clang__
792#pragma clang diagnostic push
793#pragma clang diagnostic ignored "-Wunreachable-code"
794#endif
795
796 ck_abort_msg("Got unexpected message from websocket client");
797
798
799 return 0;
800
801#ifdef __clang__
802#pragma clang diagnostic pop
803#endif
804#if defined(__MINGW32__) || defined(__GNUC__)
805#pragma GCC diagnostic pop
806#endif
807 }
808
809 return 1; /* return 1 to keep the connetion open */
810}
811
812static void
813websock_server_close(const struct mg_connection *conn, void *udata)
814{
815 (void)conn;
816
817 ck_assert_ptr_eq((void *)udata, (void *)7531);
818 printf("Server: Close connection\n");
819
820 /* Can not send a websocket goodbye message here - the connection is already
821 * closed */
822}
823
824
825/****************************************************************************/
826/* WEBSOCKET CLIENT */
827/****************************************************************************/
828struct tclient_data {
829 void *data;
830 size_t len;
831 int closed;
832};
833
834
835static int
836websocket_client_data_handler(struct mg_connection *conn,
837 int flags,
838 char *data,
839 size_t data_len,
840 void *user_data)
841{
842 struct mg_context *ctx = mg_get_context(conn);
843 struct tclient_data *pclient_data =
844 (struct tclient_data *)mg_get_user_data(ctx);
845
846 (void)user_data; /* TODO: check this */
847
848 ck_assert(pclient_data != NULL);
849 ck_assert_int_eq(flags, (int)(128 | 1));
850
851 printf("Client received data from server: ");
852 fwrite(data, 1, data_len, stdout);
853 printf("\n");
854
855 pclient_data->data = malloc(data_len);
856 ck_assert(pclient_data->data != NULL);
857 memcpy(pclient_data->data, data, data_len);
858 pclient_data->len = data_len;
859
860 return 1;
861}
862
863
864static void
865websocket_client_close_handler(const struct mg_connection *conn,
866 void *user_data)
867{
868 struct mg_context *ctx = mg_get_context(conn);
869 struct tclient_data *pclient_data =
870 (struct tclient_data *)mg_get_user_data(ctx);
871
872 (void)user_data; /* TODO: check this */
873
874 ck_assert(pclient_data != NULL);
875
876 printf("Client: Close handler\n");
877 pclient_data->closed++;
878}
879#endif
880
881
882START_TEST(test_request_handlers)
883{
884 char ebuf[100];
885 struct mg_context *ctx;
886 struct mg_connection *client_conn;
887 const struct mg_request_info *ri;
888 char uri[64];
889 char buf[1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 10 + 8];
890 const char *expected =
891 "112123123412345123456123456712345678123456789123456789A";
892 int i;
893 const char *request = "GET /U7 HTTP/1.0\r\n\r\n";
894#if defined(USE_IPV6) && defined(NO_SSL)
895 const char *HTTP_PORT = "8084,[::]:8086";
896 short ipv4_port = 8084;
897 short ipv6_port = 8086;
898#elif !defined(USE_IPV6) && defined(NO_SSL)
899 const char *HTTP_PORT = "8084";
900 short ipv4_port = 8084;
901#elif defined(USE_IPV6) && !defined(NO_SSL)
902 const char *HTTP_PORT = "8084,[::]:8086,8194r,[::]:8196r,8094s,[::]:8096s";
903 short ipv4_port = 8084;
904 short ipv4s_port = 8094;
905 short ipv4r_port = 8194;
906 short ipv6_port = 8086;
907 short ipv6s_port = 8096;
908 short ipv6r_port = 8196;
909#elif !defined(USE_IPV6) && !defined(NO_SSL)
910 const char *HTTP_PORT = "8084,8194r,8094s";
911 short ipv4_port = 8084;
912 short ipv4s_port = 8094;
913 short ipv4r_port = 8194;
914#endif
915
916 const char *OPTIONS[16];
917 const char *opt;
918 FILE *f;
919 const char *plain_file_content;
920 const char *encoded_file_content;
921 const char *cgi_script_content;
922 const char *expected_cgi_result;
923 int opt_idx = 0;
924
925#if !defined(NO_SSL)
926 const char *ssl_cert = locate_ssl_cert();
927#endif
928
929#if defined(USE_WEBSOCKET)
930 struct tclient_data ws_client1_data = {NULL, 0, 0};
931 struct tclient_data ws_client2_data = {NULL, 0, 0};
932 struct tclient_data ws_client3_data = {NULL, 0, 0};
933 struct mg_connection *ws_client1_conn = NULL;
934 struct mg_connection *ws_client2_conn = NULL;
935 struct mg_connection *ws_client3_conn = NULL;
936#endif
937
938 char cmd_buf[256];
939
940 memset((void *)OPTIONS, 0, sizeof(OPTIONS));
941 OPTIONS[opt_idx++] = "listening_ports";
942 OPTIONS[opt_idx++] = HTTP_PORT;
943 OPTIONS[opt_idx++] = "authentication_domain";
944 OPTIONS[opt_idx++] = "test.domain";
945#if !defined(NO_FILES)
946 OPTIONS[opt_idx++] = "document_root";
947 OPTIONS[opt_idx++] = ".";
948#endif
949#ifndef NO_SSL
950 ck_assert(ssl_cert != NULL);
951 OPTIONS[opt_idx++] = "ssl_certificate";
952 OPTIONS[opt_idx++] = ssl_cert;
953#endif
954 ck_assert_int_le(opt_idx, (int)(sizeof(OPTIONS) / sizeof(OPTIONS[0])));
955 ck_assert(OPTIONS[sizeof(OPTIONS) / sizeof(OPTIONS[0]) - 1] == NULL);
956 ck_assert(OPTIONS[sizeof(OPTIONS) / sizeof(OPTIONS[0]) - 2] == NULL);
957
958 mark_point();
959 ctx = mg_start(NULL, &g_ctx, OPTIONS);
960 ck_assert(ctx != NULL);
961 g_ctx = ctx;
962
963 opt = mg_get_option(ctx, "listening_ports");
964 ck_assert_str_eq(opt, HTTP_PORT);
965 opt = mg_get_option(ctx, "cgi_environment");
966 ck_assert_str_eq(opt, "");
967 opt = mg_get_option(ctx, "unknown_option_name");
968 ck_assert(opt == NULL);
969
970 for (i = 0; i < 1000; i++) {
971 sprintf(uri, "/U%u", i);
972 mg_set_request_handler(ctx, uri, request_test_handler, NULL);
973 }
974 for (i = 500; i < 800; i++) {
975 sprintf(uri, "/U%u", i);
976 mg_set_request_handler(ctx, uri, NULL, (void *)1);
977 }
978 for (i = 600; i >= 0; i--) {
979 sprintf(uri, "/U%u", i);
980 mg_set_request_handler(ctx, uri, NULL, (void *)2);
981 }
982 for (i = 750; i <= 1000; i++) {
983 sprintf(uri, "/U%u", i);
984 mg_set_request_handler(ctx, uri, NULL, (void *)3);
985 }
986 for (i = 5; i < 9; i++) {
987 sprintf(uri, "/U%u", i);
988 mg_set_request_handler(ctx,
989 uri,
990 request_test_handler,
991 (void *)(ptrdiff_t)i);
992 }
993
994#ifdef USE_WEBSOCKET
995 mg_set_websocket_handler(ctx,
996 "/websocket",
997 websock_server_connect,
998 websock_server_ready,
999 websock_server_data,
1000 websock_server_close,
1001 (void *)7531);
1002#endif
1003
1004 /* Try to load non existing file */
1005 client_conn = mg_download("localhost",
1006 ipv4_port,
1007 0,
1008 ebuf,
1009 sizeof(ebuf),
1010 "%s",
1011 "GET /file/not/found HTTP/1.0\r\n\r\n");
1012 ck_assert(client_conn != NULL);
1013 ri = mg_get_request_info(client_conn);
1014
1015 ck_assert(ri != NULL);
1016 ck_assert_str_eq(ri->uri, "404");
1017 mg_close_connection(client_conn);
1018
1019 /* Get data from callback */
1020 client_conn = mg_download(
1021 "localhost", ipv4_port, 0, ebuf, sizeof(ebuf), "%s", request);
1022 ck_assert(client_conn != NULL);
1023 ri = mg_get_request_info(client_conn);
1024
1025 ck_assert(ri != NULL);
1026 ck_assert_str_eq(ri->uri, "200");
1027 i = mg_read(client_conn, buf, sizeof(buf));
1028 ck_assert_int_eq(i, (int)strlen(expected));
1029 buf[i] = 0;
1030 ck_assert_str_eq(buf, expected);
1031 mg_close_connection(client_conn);
1032
1033 /* Get data from callback using http://127.0.0.1 */
1034 client_conn = mg_download(
1035 "127.0.0.1", ipv4_port, 0, ebuf, sizeof(ebuf), "%s", request);
1036 ck_assert(client_conn != NULL);
1037 ri = mg_get_request_info(client_conn);
1038
1039 ck_assert(ri != NULL);
1040 ck_assert_str_eq(ri->uri, "200");
1041 i = mg_read(client_conn, buf, sizeof(buf));
1042 if ((i >= 0) && ((size_t)i < sizeof(buf))) {
1043 buf[i] = 0;
1044 } else {
1045 ck_abort_msg(
1046 "ERROR: test_request_handlers: read returned %i (>=0, <%i)",
1047 (int)i,
1048 (int)sizeof(buf));
1049 }
1050 ck_assert((int)i < (int)sizeof(buf));
1051 ck_assert(i > 0);
1052 ck_assert_int_eq(i, (int)strlen(expected));
1053 buf[i] = 0;
1054 ck_assert_str_eq(buf, expected);
1055 mg_close_connection(client_conn);
1056
1057#if defined(USE_IPV6)
1058 /* Get data from callback using http://[::1] */
1059 client_conn =
1060 mg_download("[::1]", ipv6_port, 0, ebuf, sizeof(ebuf), "%s", request);
1061 ck_assert(client_conn != NULL);
1062 ri = mg_get_request_info(client_conn);
1063
1064 ck_assert(ri != NULL);
1065 ck_assert_str_eq(ri->uri, "200");
1066 i = mg_read(client_conn, buf, sizeof(buf));
1067 ck_assert_int_eq(i, (int)strlen(expected));
1068 buf[i] = 0;
1069 ck_assert_str_eq(buf, expected);
1070 mg_close_connection(client_conn);
1071#endif
1072
1073#if !defined(NO_SSL)
1074 /* Get data from callback using https://127.0.0.1 */
1075 client_conn = mg_download(
1076 "127.0.0.1", ipv4s_port, 1, ebuf, sizeof(ebuf), "%s", request);
1077 ck_assert(client_conn != NULL);
1078 ri = mg_get_request_info(client_conn);
1079
1080 ck_assert(ri != NULL);
1081 ck_assert_str_eq(ri->uri, "200");
1082 i = mg_read(client_conn, buf, sizeof(buf));
1083 ck_assert_int_eq(i, (int)strlen(expected));
1084 buf[i] = 0;
1085 ck_assert_str_eq(buf, expected);
1086 mg_close_connection(client_conn);
1087
1088 /* Get redirect from callback using http://127.0.0.1 */
1089 client_conn = mg_download(
1090 "127.0.0.1", ipv4r_port, 0, ebuf, sizeof(ebuf), "%s", request);
1091 ck_assert(client_conn != NULL);
1092 ri = mg_get_request_info(client_conn);
1093
1094 ck_assert(ri != NULL);
1095 ck_assert_str_eq(ri->uri, "302");
1096 i = mg_read(client_conn, buf, sizeof(buf));
1097 ck_assert_int_eq(i, -1);
1098 mg_close_connection(client_conn);
1099#endif
1100
1101#if defined(USE_IPV6) && !defined(NO_SSL)
1102 /* Get data from callback using https://[::1] */
1103 client_conn =
1104 mg_download("[::1]", ipv6s_port, 1, ebuf, sizeof(ebuf), "%s", request);
1105 ck_assert(client_conn != NULL);
1106 ri = mg_get_request_info(client_conn);
1107
1108 ck_assert(ri != NULL);
1109 ck_assert_str_eq(ri->uri, "200");
1110 i = mg_read(client_conn, buf, sizeof(buf));
1111 ck_assert_int_eq(i, (int)strlen(expected));
1112 buf[i] = 0;
1113 ck_assert_str_eq(buf, expected);
1114 mg_close_connection(client_conn);
1115
1116 /* Get redirect from callback using http://127.0.0.1 */
1117 client_conn =
1118 mg_download("[::1]", ipv6r_port, 0, ebuf, sizeof(ebuf), "%s", request);
1119 ck_assert(client_conn != NULL);
1120 ri = mg_get_request_info(client_conn);
1121
1122 ck_assert(ri != NULL);
1123 ck_assert_str_eq(ri->uri, "302");
1124 i = mg_read(client_conn, buf, sizeof(buf));
1125 ck_assert_int_eq(i, -1);
1126 mg_close_connection(client_conn);
1127#endif
1128
1129/* It seems to be impossible to find out what the actual working
1130 * directory of the CI test environment is. Before breaking another
1131 * dozen of builds by trying blindly with different paths, just
1132 * create the file here */
1133#ifdef _WIN32
1134 f = fopen("test.txt", "wb");
1135#else
1136 f = fopen("test.txt", "w");
1137#endif
1138 plain_file_content = "simple text file\n";
1139 fwrite(plain_file_content, 17, 1, f);
1140 fclose(f);
1141
1142#ifdef _WIN32
1143 f = fopen("test_gz.txt.gz", "wb");
1144#else
1145 f = fopen("test_gz.txt.gz", "w");
1146#endif
1147 encoded_file_content = "\x1f\x8b\x08\x08\xf8\x9d\xcb\x55\x00\x00"
1148 "test_gz.txt"
1149 "\x00\x01\x11\x00\xee\xff"
1150 "zipped text file"
1151 "\x0a\x34\x5f\xcc\x49\x11\x00\x00\x00";
1152 fwrite(encoded_file_content, 1, 52, f);
1153 fclose(f);
1154
1155#ifdef _WIN32
1156 f = fopen("test.cgi", "wb");
1157 cgi_script_content = "#!test.cgi.cmd\r\n";
1158 fwrite(cgi_script_content, strlen(cgi_script_content), 1, f);
1159 fclose(f);
1160 f = fopen("test.cgi.cmd", "w");
1161 cgi_script_content = "@echo off\r\n"
1162 "echo Connection: close\r\n"
1163 "echo Content-Type: text/plain\r\n"
1164 "echo.\r\n"
1165 "echo CGI test\r\n"
1166 "\r\n";
1167 fwrite(cgi_script_content, strlen(cgi_script_content), 1, f);
1168 fclose(f);
1169#else
1170 f = fopen("test.cgi", "w");
1171 cgi_script_content = "#!/bin/sh\n\n"
1172 "printf \"Connection: close\\r\\n\"\n"
1173 "printf \"Content-Type: text/plain\\r\\n\"\n"
1174 "printf \"\\r\\n\"\n"
1175 "printf \"CGI test\\r\\n\"\n"
1176 "\n";
1177 fwrite(cgi_script_content, strlen(cgi_script_content), 1, f);
1178 fclose(f);
1179 system("chmod a+x test.cgi");
1180#endif
1181 expected_cgi_result = "CGI test";
1182
1183 /* Get static data */
1184 client_conn = mg_download("localhost",
1185 ipv4_port,
1186 0,
1187 ebuf,
1188 sizeof(ebuf),
1189 "%s",
1190 "GET /test.txt HTTP/1.0\r\n\r\n");
1191 ck_assert(client_conn != NULL);
1192 ri = mg_get_request_info(client_conn);
1193
1194 ck_assert(ri != NULL);
1195
1196#if defined(NO_FILES)
1197 ck_assert_str_eq(ri->uri, "404");
1198#else
1199 ck_assert_str_eq(ri->uri, "200");
1200 i = mg_read(client_conn, buf, sizeof(buf));
1201 ck_assert_int_eq(i, 17);
1202 if ((i >= 0) && (i < (int)sizeof(buf))) {
1203 buf[i] = 0;
1204 }
1205 ck_assert_str_eq(buf, plain_file_content);
1206#endif
1207 mg_close_connection(client_conn);
1208
1209
1210/* Test with CGI test executable */
1211#if defined(_WIN32)
1212 sprintf(cmd_buf, "copy %s\\cgi_test.cgi cgi_test.exe", locate_test_exes());
1213#else
1214 sprintf(cmd_buf, "cp %s/cgi_test.cgi cgi_test.cgi", locate_test_exes());
1215#endif
1216 system(cmd_buf);
1217
1218#if !defined(NO_CGI) && !defined(NO_FILES) && !defined(_WIN32)
1219 /* TODO: add test for windows, check with POST */
1220 client_conn = mg_download(
1221 "localhost",
1222 ipv4_port,
1223 0,
1224 ebuf,
1225 sizeof(ebuf),
1226 "%s",
1227 "POST /cgi_test.cgi HTTP/1.0\r\nContent-Length: 3\r\n\r\nABC");
1228 ck_assert(client_conn != NULL);
1229 ri = mg_get_request_info(client_conn);
1230
1231 ck_assert(ri != NULL);
1232 ck_assert_str_eq(ri->uri, "200");
1233 mg_close_connection(client_conn);
1234#endif
1235
1236 /* Get zipped static data - will not work if Accept-Encoding is not set */
1237 client_conn = mg_download("localhost",
1238 ipv4_port,
1239 0,
1240 ebuf,
1241 sizeof(ebuf),
1242 "%s",
1243 "GET /test_gz.txt HTTP/1.0\r\n\r\n");
1244 ck_assert(client_conn != NULL);
1245 ri = mg_get_request_info(client_conn);
1246
1247 ck_assert(ri != NULL);
1248
1249 ck_assert_str_eq(ri->uri, "404");
1250 mg_close_connection(client_conn);
1251
1252 /* Get zipped static data - with Accept-Encoding */
1253 client_conn = mg_download(
1254 "localhost",
1255 ipv4_port,
1256 0,
1257 ebuf,
1258 sizeof(ebuf),
1259 "%s",
1260 "GET /test_gz.txt HTTP/1.0\r\nAccept-Encoding: gzip\r\n\r\n");
1261 ck_assert(client_conn != NULL);
1262 ri = mg_get_request_info(client_conn);
1263
1264 ck_assert(ri != NULL);
1265
1266#if defined(NO_FILES)
1267 ck_assert_str_eq(ri->uri, "404");
1268#else
1269 ck_assert_str_eq(ri->uri, "200");
1270 i = mg_read(client_conn, buf, sizeof(buf));
1271 ck_assert_int_eq(i, 52);
1272 if ((i >= 0) && (i < (int)sizeof(buf))) {
1273 buf[i] = 0;
1274 }
1275 ck_assert_int_eq(ri->content_length, 52);
1276 ck_assert_str_eq(buf, encoded_file_content);
1277#endif
1278 mg_close_connection(client_conn);
1279
1280/* Get CGI generated data */
1281#if !defined(NO_CGI)
1282 client_conn = mg_download("localhost",
1283 ipv4_port,
1284 0,
1285 ebuf,
1286 sizeof(ebuf),
1287 "%s",
1288 "GET /test.cgi HTTP/1.0\r\n\r\n");
1289 ck_assert(client_conn != NULL);
1290 ri = mg_get_request_info(client_conn);
1291
1292 ck_assert(ri != NULL);
1293
1294#if defined(NO_FILES)
1295 ck_assert_str_eq(ri->uri, "404");
1296
1297 (void)expected_cgi_result;
1298 (void)cgi_script_content;
1299#else
1300 i = mg_read(client_conn, buf, sizeof(buf));
1301 if ((i >= 0) && (i < (int)sizeof(buf))) {
1302 while ((i > 0) && ((buf[i - 1] == '\r') || (buf[i - 1] == '\n'))) {
1303 i--;
1304 }
1305 buf[i] = 0;
1306 }
1307 /* ck_assert_int_eq(i, (int)strlen(expected_cgi_result)); */
1308 ck_assert_str_eq(buf, expected_cgi_result);
1309 ck_assert_str_eq(ri->uri, "200");
1310 mg_close_connection(client_conn);
1311#endif
1312
1313#else
1314 (void)expected_cgi_result;
1315 (void)cgi_script_content;
1316#endif
1317
1318 /* Get directory listing */
1319 client_conn = mg_download("localhost",
1320 ipv4_port,
1321 0,
1322 ebuf,
1323 sizeof(ebuf),
1324 "%s",
1325 "GET / HTTP/1.0\r\n\r\n");
1326 ck_assert(client_conn != NULL);
1327 ri = mg_get_request_info(client_conn);
1328
1329 ck_assert(ri != NULL);
1330#if defined(NO_FILES)
1331 ck_assert_str_eq(ri->uri, "404");
1332#else
1333 ck_assert_str_eq(ri->uri, "200");
1334 i = mg_read(client_conn, buf, sizeof(buf));
1335 ck_assert(i > 6);
1336 buf[6] = 0;
1337 ck_assert_str_eq(buf, "<html>");
1338#endif
1339 mg_close_connection(client_conn);
1340
1341 /* POST to static file (will not work) */
1342 client_conn = mg_download("localhost",
1343 ipv4_port,
1344 0,
1345 ebuf,
1346 sizeof(ebuf),
1347 "%s",
1348 "POST /test.txt HTTP/1.0\r\n\r\n");
1349 ck_assert(client_conn != NULL);
1350 ri = mg_get_request_info(client_conn);
1351
1352 ck_assert(ri != NULL);
1353#if defined(NO_FILES)
1354 ck_assert_str_eq(ri->uri, "404");
1355#else
1356 ck_assert_str_eq(ri->uri, "405");
1357 i = mg_read(client_conn, buf, sizeof(buf));
1358 ck_assert(i >= 29);
1359 buf[29] = 0;
1360 ck_assert_str_eq(buf, "Error 405: Method Not Allowed");
1361#endif
1362 mg_close_connection(client_conn);
1363
1364 /* PUT to static file (will not work) */
1365 client_conn = mg_download("localhost",
1366 ipv4_port,
1367 0,
1368 ebuf,
1369 sizeof(ebuf),
1370 "%s",
1371 "PUT /test.txt HTTP/1.0\r\n\r\n");
1372 ck_assert(client_conn != NULL);
1373 ri = mg_get_request_info(client_conn);
1374
1375 ck_assert(ri != NULL);
1376#if defined(NO_FILES)
1377 ck_assert_str_eq(ri->uri, "405"); /* method not allowed */
1378#else
1379 ck_assert_str_eq(ri->uri, "401"); /* not authorized */
1380#endif
1381 mg_close_connection(client_conn);
1382
1383
1384 /* Get data from callback using mg_connect_client instead of mg_download */
1385 memset(ebuf, 0, sizeof(ebuf));
1386 client_conn =
1387 mg_connect_client("127.0.0.1", ipv4_port, 0, ebuf, sizeof(ebuf));
1388 ck_assert(client_conn != NULL);
1389 ck_assert_str_eq(ebuf, "");
1390
1391 mg_printf(client_conn, "%s", request);
1392
1393 i = mg_get_response(client_conn, ebuf, sizeof(ebuf), 10000);
1394 ck_assert_int_ge(i, 0);
1395 ck_assert_str_eq(ebuf, "");
1396
1397 ri = mg_get_request_info(client_conn);
1398
1399 ck_assert(ri != NULL);
1400 ck_assert_str_eq(ri->uri, "200");
1401 i = mg_read(client_conn, buf, sizeof(buf));
1402 ck_assert_int_eq(i, (int)strlen(expected));
1403 buf[i] = 0;
1404 ck_assert_str_eq(buf, expected);
1405 mg_close_connection(client_conn);
1406
1407 /* Get data from callback using mg_connect_client and absolute URI */
1408 memset(ebuf, 0, sizeof(ebuf));
1409 client_conn =
1410 mg_connect_client("localhost", ipv4_port, 0, ebuf, sizeof(ebuf));
1411 ck_assert(client_conn != NULL);
1412 ck_assert_str_eq(ebuf, "");
1413
1414 mg_printf(client_conn,
1415 "GET http://test.domain:%d/U7 HTTP/1.0\r\n\r\n",
1416 ipv4_port);
1417
1418 i = mg_get_response(client_conn, ebuf, sizeof(ebuf), 10000);
1419 ck_assert_int_ge(i, 0);
1420 ck_assert_str_eq(ebuf, "");
1421
1422 ri = mg_get_request_info(client_conn);
1423
1424 ck_assert(ri != NULL);
1425 ck_assert_str_eq(ri->uri, "200");
1426 i = mg_read(client_conn, buf, sizeof(buf));
1427 ck_assert_int_eq(i, (int)strlen(expected));
1428 buf[i] = 0;
1429 ck_assert_str_eq(buf, expected);
1430 mg_close_connection(client_conn);
1431
31f18b77
FG
1432 /* Get data from callback using mg_connect_client and absolute URI with a
1433 * sub-domain */
1434 memset(ebuf, 0, sizeof(ebuf));
1435 client_conn =
1436 mg_connect_client("localhost", ipv4_port, 0, ebuf, sizeof(ebuf));
1437 ck_assert(client_conn != NULL);
1438 ck_assert_str_eq(ebuf, "");
1439
1440 mg_printf(client_conn,
1441 "GET http://subdomain.test.domain:%d/U7 HTTP/1.0\r\n\r\n",
1442 ipv4_port);
1443
1444 i = mg_get_response(client_conn, ebuf, sizeof(ebuf), 10000);
1445 ck_assert_int_ge(i, 0);
1446 ck_assert_str_eq(ebuf, "");
1447
1448 ri = mg_get_request_info(client_conn);
1449
1450 ck_assert(ri != NULL);
1451 ck_assert_str_eq(ri->uri, "200");
1452 i = mg_read(client_conn, buf, sizeof(buf));
1453 ck_assert_int_eq(i, (int)strlen(expected));
1454 buf[i] = 0;
1455 ck_assert_str_eq(buf, expected);
1456 mg_close_connection(client_conn);
1457
7c673cae
FG
1458
1459/* Websocket test */
1460#ifdef USE_WEBSOCKET
1461 /* Then connect a first client */
1462 ws_client1_conn =
1463 mg_connect_websocket_client("localhost",
1464 ipv4_port,
1465 0,
1466 ebuf,
1467 sizeof(ebuf),
1468 "/websocket",
1469 NULL,
1470 websocket_client_data_handler,
1471 websocket_client_close_handler,
1472 &ws_client1_data);
1473
1474 ck_assert(ws_client1_conn != NULL);
1475
1476 wait_not_null(
1477 &(ws_client1_data.data)); /* Wait for the websocket welcome message */
1478 ck_assert_int_eq(ws_client1_data.closed, 0);
1479 ck_assert_int_eq(ws_client2_data.closed, 0);
1480 ck_assert_int_eq(ws_client3_data.closed, 0);
1481 ck_assert(ws_client2_data.data == NULL);
1482 ck_assert_uint_eq(ws_client2_data.len, 0);
1483 ck_assert(ws_client1_data.data != NULL);
1484 ck_assert_uint_eq(ws_client1_data.len, websocket_welcome_msg_len);
1485 ck_assert(!memcmp(ws_client1_data.data,
1486 websocket_welcome_msg,
1487 websocket_welcome_msg_len));
1488 free(ws_client1_data.data);
1489 ws_client1_data.data = NULL;
1490 ws_client1_data.len = 0;
1491
1492 mg_websocket_client_write(ws_client1_conn,
1493 WEBSOCKET_OPCODE_TEXT,
1494 "data1",
1495 5);
1496
1497 wait_not_null(
1498 &(ws_client1_data
1499 .data)); /* Wait for the websocket acknowledge message */
1500 ck_assert_int_eq(ws_client1_data.closed, 0);
1501 ck_assert_int_eq(ws_client2_data.closed, 0);
1502 ck_assert(ws_client2_data.data == NULL);
1503 ck_assert_uint_eq(ws_client2_data.len, 0);
1504 ck_assert(ws_client1_data.data != NULL);
1505 ck_assert_uint_eq(ws_client1_data.len, 3);
1506 ck_assert(!memcmp(ws_client1_data.data, "ok1", 3));
1507 free(ws_client1_data.data);
1508 ws_client1_data.data = NULL;
1509 ws_client1_data.len = 0;
1510
1511/* Now connect a second client */
1512#ifdef USE_IPV6
1513 ws_client2_conn =
1514 mg_connect_websocket_client("[::1]",
1515 ipv6_port,
1516 0,
1517 ebuf,
1518 sizeof(ebuf),
1519 "/websocket",
1520 NULL,
1521 websocket_client_data_handler,
1522 websocket_client_close_handler,
1523 &ws_client2_data);
1524#else
1525 ws_client2_conn =
1526 mg_connect_websocket_client("127.0.0.1",
1527 ipv4_port,
1528 0,
1529 ebuf,
1530 sizeof(ebuf),
1531 "/websocket",
1532 NULL,
1533 websocket_client_data_handler,
1534 websocket_client_close_handler,
1535 &ws_client2_data);
1536#endif
1537 ck_assert(ws_client2_conn != NULL);
1538
1539 wait_not_null(
1540 &(ws_client2_data.data)); /* Wait for the websocket welcome message */
1541 ck_assert(ws_client1_data.closed == 0);
1542 ck_assert(ws_client2_data.closed == 0);
1543 ck_assert(ws_client1_data.data == NULL);
1544 ck_assert(ws_client1_data.len == 0);
1545 ck_assert(ws_client2_data.data != NULL);
1546 ck_assert(ws_client2_data.len == websocket_welcome_msg_len);
1547 ck_assert(!memcmp(ws_client2_data.data,
1548 websocket_welcome_msg,
1549 websocket_welcome_msg_len));
1550 free(ws_client2_data.data);
1551 ws_client2_data.data = NULL;
1552 ws_client2_data.len = 0;
1553
1554 mg_websocket_client_write(ws_client1_conn,
1555 WEBSOCKET_OPCODE_TEXT,
1556 "data2",
1557 5);
1558
1559 wait_not_null(
1560 &(ws_client1_data
1561 .data)); /* Wait for the websocket acknowledge message */
1562
1563 ck_assert(ws_client1_data.closed == 0);
1564 ck_assert(ws_client2_data.closed == 0);
1565 ck_assert(ws_client2_data.data == NULL);
1566 ck_assert(ws_client2_data.len == 0);
1567 ck_assert(ws_client1_data.data != NULL);
1568 ck_assert(ws_client1_data.len == 4);
1569 ck_assert(!memcmp(ws_client1_data.data, "ok 2", 4));
1570 free(ws_client1_data.data);
1571 ws_client1_data.data = NULL;
1572 ws_client1_data.len = 0;
1573
1574 mg_websocket_client_write(ws_client1_conn, WEBSOCKET_OPCODE_TEXT, "bye", 3);
1575
1576 wait_not_null(
1577 &(ws_client1_data.data)); /* Wait for the websocket goodbye message */
1578
1579 ck_assert(ws_client1_data.closed == 0);
1580 ck_assert(ws_client2_data.closed == 0);
1581 ck_assert(ws_client2_data.data == NULL);
1582 ck_assert(ws_client2_data.len == 0);
1583 ck_assert(ws_client1_data.data != NULL);
1584 ck_assert(ws_client1_data.len == websocket_goodbye_msg_len);
1585 ck_assert(!memcmp(ws_client1_data.data,
1586 websocket_goodbye_msg,
1587 websocket_goodbye_msg_len));
1588 free(ws_client1_data.data);
1589 ws_client1_data.data = NULL;
1590 ws_client1_data.len = 0;
1591
1592 mg_close_connection(ws_client1_conn);
1593
1594 test_sleep(3); /* Won't get any message */
1595
1596 ck_assert(ws_client1_data.closed == 1);
1597 ck_assert(ws_client2_data.closed == 0);
1598 ck_assert(ws_client1_data.data == NULL);
1599 ck_assert(ws_client1_data.len == 0);
1600 ck_assert(ws_client2_data.data == NULL);
1601 ck_assert(ws_client2_data.len == 0);
1602
1603 mg_websocket_client_write(ws_client2_conn, WEBSOCKET_OPCODE_TEXT, "bye", 3);
1604
1605 wait_not_null(
1606 &(ws_client2_data.data)); /* Wait for the websocket goodbye message */
1607
1608 ck_assert(ws_client1_data.closed == 1);
1609 ck_assert(ws_client2_data.closed == 0);
1610 ck_assert(ws_client1_data.data == NULL);
1611 ck_assert(ws_client1_data.len == 0);
1612 ck_assert(ws_client2_data.data != NULL);
1613 ck_assert(ws_client2_data.len == websocket_goodbye_msg_len);
1614 ck_assert(!memcmp(ws_client2_data.data,
1615 websocket_goodbye_msg,
1616 websocket_goodbye_msg_len));
1617 free(ws_client2_data.data);
1618 ws_client2_data.data = NULL;
1619 ws_client2_data.len = 0;
1620
1621 mg_close_connection(ws_client2_conn);
1622
1623 test_sleep(3); /* Won't get any message */
1624
1625 ck_assert(ws_client1_data.closed == 1);
1626 ck_assert(ws_client2_data.closed == 1);
1627 ck_assert(ws_client1_data.data == NULL);
1628 ck_assert(ws_client1_data.len == 0);
1629 ck_assert(ws_client2_data.data == NULL);
1630 ck_assert(ws_client2_data.len == 0);
1631
1632 /* Connect client 3 */
1633 ws_client3_conn =
1634 mg_connect_websocket_client("localhost",
1635#if defined(NO_SSL)
1636 ipv4_port,
1637 0,
1638#else
1639 ipv4s_port,
1640 1,
1641#endif
1642 ebuf,
1643 sizeof(ebuf),
1644 "/websocket",
1645 NULL,
1646 websocket_client_data_handler,
1647 websocket_client_close_handler,
1648 &ws_client3_data);
1649
1650 ck_assert(ws_client3_conn != NULL);
1651
1652 wait_not_null(
1653 &(ws_client3_data.data)); /* Wait for the websocket welcome message */
1654 ck_assert(ws_client1_data.closed == 1);
1655 ck_assert(ws_client2_data.closed == 1);
1656 ck_assert(ws_client3_data.closed == 0);
1657 ck_assert(ws_client1_data.data == NULL);
1658 ck_assert(ws_client1_data.len == 0);
1659 ck_assert(ws_client2_data.data == NULL);
1660 ck_assert(ws_client2_data.len == 0);
1661 ck_assert(ws_client3_data.data != NULL);
1662 ck_assert(ws_client3_data.len == websocket_welcome_msg_len);
1663 ck_assert(!memcmp(ws_client3_data.data,
1664 websocket_welcome_msg,
1665 websocket_welcome_msg_len));
1666 free(ws_client3_data.data);
1667 ws_client3_data.data = NULL;
1668 ws_client3_data.len = 0;
1669#endif
1670
1671 /* Close the server */
1672 g_ctx = NULL;
1673 mg_stop(ctx);
1674 mark_point();
1675
1676#ifdef USE_WEBSOCKET
1677 for (i = 0; i < 100; i++) {
1678 test_sleep(1);
1679 if (ws_client3_data.closed != 0) {
1680 mark_point();
1681 break;
1682 }
1683 }
1684
1685 ck_assert_int_eq(ws_client3_data.closed, 1);
1686#endif
1687}
1688END_TEST
1689
1690
1691static int g_field_found_return = -999;
1692
1693static int
1694field_found(const char *key,
1695 const char *filename,
1696 char *path,
1697 size_t pathlen,
1698 void *user_data)
1699{
1700 ck_assert_ptr_ne(key, NULL);
1701 ck_assert_ptr_ne(filename, NULL);
1702 ck_assert_ptr_ne(path, NULL);
1703 ck_assert_uint_gt(pathlen, 128);
1704 ck_assert_ptr_eq(user_data, (void *)&g_field_found_return);
1705
1706 ck_assert((g_field_found_return == FORM_FIELD_STORAGE_GET)
1707 || (g_field_found_return == FORM_FIELD_STORAGE_STORE)
1708 || (g_field_found_return == FORM_FIELD_STORAGE_SKIP)
1709 || (g_field_found_return == FORM_FIELD_STORAGE_ABORT));
1710
1711 ck_assert_str_ne(key, "dontread");
1712
1713 if (!strcmp(key, "break_field_handler")) {
1714 return FORM_FIELD_STORAGE_ABORT;
1715 }
1716 if (!strcmp(key, "continue_field_handler")) {
1717 return FORM_FIELD_STORAGE_SKIP;
1718 }
1719
1720 if (g_field_found_return == FORM_FIELD_STORAGE_STORE) {
1721 strncpy(path, key, pathlen - 8);
1722 strcat(path, ".txt");
1723 }
1724
1725 return g_field_found_return;
1726}
1727
1728
1729static int g_field_step;
1730
1731static int
1732field_get(const char *key, const char *value, size_t valuelen, void *user_data)
1733{
1734 ck_assert_ptr_eq(user_data, (void *)&g_field_found_return);
1735 ck_assert_int_ge(g_field_step, 0);
1736
1737 ++g_field_step;
1738 switch (g_field_step) {
1739 case 1:
1740 ck_assert_str_eq(key, "textin");
1741 ck_assert_uint_eq(valuelen, 4);
1742 ck_assert_str_eq(value, "text");
1743 break;
1744 case 2:
1745 ck_assert_str_eq(key, "passwordin");
1746 ck_assert_uint_eq(valuelen, 0);
1747 ck_assert_str_eq(value, "");
1748 break;
1749 case 3:
1750 ck_assert_str_eq(key, "radio1");
1751 ck_assert_uint_eq(valuelen, 4);
1752 ck_assert_str_eq(value, "val1");
1753 break;
1754 case 4:
1755 ck_assert_str_eq(key, "radio2");
1756 ck_assert_uint_eq(valuelen, 4);
1757 ck_assert_str_eq(value, "val1");
1758 break;
1759 case 5:
1760 ck_assert_str_eq(key, "check1");
1761 ck_assert_uint_eq(valuelen, 4);
1762 ck_assert_str_eq(value, "val1");
1763 break;
1764 case 6:
1765 ck_assert_str_eq(key, "numberin");
1766 ck_assert_uint_eq(valuelen, 1);
1767 ck_assert_str_eq(value, "1");
1768 break;
1769 case 7:
1770 ck_assert_str_eq(key, "datein");
1771 ck_assert_uint_eq(valuelen, 8);
1772 ck_assert_str_eq(value, "1.1.2016");
1773 break;
1774 case 8:
1775 ck_assert_str_eq(key, "colorin");
1776 ck_assert_uint_eq(valuelen, 7);
1777 ck_assert_str_eq(value, "#80ff00");
1778 break;
1779 case 9:
1780 ck_assert_str_eq(key, "rangein");
1781 ck_assert_uint_eq(valuelen, 1);
1782 ck_assert_str_eq(value, "3");
1783 break;
1784 case 10:
1785 ck_assert_str_eq(key, "monthin");
1786 ck_assert_uint_eq(valuelen, 0);
1787 ck_assert_str_eq(value, "");
1788 break;
1789 case 11:
1790 ck_assert_str_eq(key, "weekin");
1791 ck_assert_uint_eq(valuelen, 0);
1792 ck_assert_str_eq(value, "");
1793 break;
1794 case 12:
1795 ck_assert_str_eq(key, "timein");
1796 ck_assert_uint_eq(valuelen, 0);
1797 ck_assert_str_eq(value, "");
1798 break;
1799 case 13:
1800 ck_assert_str_eq(key, "datetimen");
1801 ck_assert_uint_eq(valuelen, 0);
1802 ck_assert_str_eq(value, "");
1803 break;
1804 case 14:
1805 ck_assert_str_eq(key, "datetimelocalin");
1806 ck_assert_uint_eq(valuelen, 0);
1807 ck_assert_str_eq(value, "");
1808 break;
1809 case 15:
1810 ck_assert_str_eq(key, "emailin");
1811 ck_assert_uint_eq(valuelen, 0);
1812 ck_assert_str_eq(value, "");
1813 break;
1814 case 16:
1815 ck_assert_str_eq(key, "searchin");
1816 ck_assert_uint_eq(valuelen, 0);
1817 ck_assert_str_eq(value, "");
1818 break;
1819 case 17:
1820 ck_assert_str_eq(key, "telin");
1821 ck_assert_uint_eq(valuelen, 0);
1822 ck_assert_str_eq(value, "");
1823 break;
1824 case 18:
1825 ck_assert_str_eq(key, "urlin");
1826 ck_assert_uint_eq(valuelen, 0);
1827 ck_assert_str_eq(value, "");
1828 break;
1829 case 19:
1830 ck_assert_str_eq(key, "filein");
1831 ck_assert_uint_eq(valuelen, 0);
1832 ck_assert_str_eq(value, "");
1833 break;
1834 case 20:
1835 ck_assert_str_eq(key, "filesin");
1836 ck_assert_uint_eq(valuelen, 0);
1837 ck_assert_str_eq(value, "");
1838 break;
1839 case 21:
1840 ck_assert_str_eq(key, "selectin");
1841 ck_assert_uint_eq(valuelen, 4);
1842 ck_assert_str_eq(value, "opt1");
1843 break;
1844 case 22:
1845 ck_assert_str_eq(key, "message");
1846 ck_assert_uint_eq(valuelen, 23);
1847 ck_assert_str_eq(value, "Text area default text.");
1848 break;
1849 default:
1850 ck_abort_msg("field_get called with g_field_step == %i",
1851 (int)g_field_step);
1852 }
1853
1854 return 0;
1855}
1856
1857
1858static const char *myfile_content = "Content of myfile.txt\r\n";
1859static const int myfile_content_rep = 50000;
1860
1861
1862static int
1863field_store(const char *path, long long file_size, void *user_data)
1864{
1865 FILE *f;
1866 ck_assert_ptr_eq(user_data, (void *)&g_field_found_return);
1867 ck_assert_int_ge(g_field_step, 100);
1868
1869 ++g_field_step;
1870 switch (g_field_step) {
1871 case 101:
1872 ck_assert_str_eq(path, "storeme.txt");
1873 ck_assert_int_eq(file_size, 9);
1874 f = fopen(path, "r");
1875 ck_assert_ptr_ne(f, NULL);
1876 if (f) {
1877 char buf[32] = {0};
1878 int i = (int)fread(buf, 1, 31, f);
1879 ck_assert_int_eq(i, 9);
1880 fclose(f);
1881 ck_assert_str_eq(buf, "storetest");
1882 }
1883 break;
1884 case 102:
1885 ck_assert_str_eq(path, "file2store.txt");
1886 ck_assert_uint_eq(23, strlen(myfile_content));
1887 ck_assert_int_eq(file_size, 23 * myfile_content_rep);
1888#ifdef _WIN32
1889 f = fopen(path, "rb");
1890#else
1891 f = fopen(path, "r");
1892#endif
1893 ck_assert_ptr_ne(f, NULL);
1894 if (f) {
1895 char buf[32] = {0};
1896 int r, i;
1897 for (r = 0; r < myfile_content_rep; r++) {
1898 i = (int)fread(buf, 1, 23, f);
1899 ck_assert_int_eq(i, 23);
1900 ck_assert_str_eq(buf, myfile_content);
1901 }
1902 i = (int)fread(buf, 1, 23, f);
1903 ck_assert_int_eq(i, 0);
1904 fclose(f);
1905 }
1906 break;
1907 default:
1908 ck_abort_msg("field_get called with g_field_step == %i",
1909 (int)g_field_step);
1910 }
1911
1912 return 0;
1913}
1914
1915
1916static int
1917FormGet(struct mg_connection *conn, void *cbdata)
1918{
1919 const struct mg_request_info *req_info = mg_get_request_info(conn);
1920 int ret;
1921 struct mg_form_data_handler fdh = {field_found, field_get, NULL, NULL};
1922
1923 (void)cbdata;
1924
1925 ck_assert(req_info != NULL);
1926
1927 mg_printf(conn, "HTTP/1.0 200 OK\r\nContent-Type: text/plain\r\n\r\n");
1928 fdh.user_data = (void *)&g_field_found_return;
1929
1930 /* Call the form handler */
1931 g_field_step = 0;
1932 g_field_found_return = FORM_FIELD_STORAGE_GET;
1933 ret = mg_handle_form_request(conn, &fdh);
1934 g_field_found_return = -888;
1935 ck_assert_int_eq(ret, 22);
1936 ck_assert_int_eq(g_field_step, 22);
1937 mg_printf(conn, "%i\r\n", ret);
1938 g_field_step = 1000;
1939
1940 return 1;
1941}
1942
1943
1944static int
1945FormStore(struct mg_connection *conn,
1946 void *cbdata,
1947 int ret_expected,
1948 int field_step_expected)
1949{
1950 const struct mg_request_info *req_info = mg_get_request_info(conn);
1951 int ret;
1952 struct mg_form_data_handler fdh = {field_found,
1953 field_get,
1954 field_store,
1955 NULL};
1956
1957 (void)cbdata;
1958
1959 ck_assert(req_info != NULL);
1960
1961 mg_printf(conn, "HTTP/1.0 200 OK\r\nContent-Type: text/plain\r\n\r\n");
1962 fdh.user_data = (void *)&g_field_found_return;
1963
1964 /* Call the form handler */
1965 g_field_step = 100;
1966 g_field_found_return = FORM_FIELD_STORAGE_STORE;
1967 ret = mg_handle_form_request(conn, &fdh);
1968 ck_assert_int_eq(ret, ret_expected);
1969 ck_assert_int_eq(g_field_step, field_step_expected);
1970 mg_printf(conn, "%i\r\n", ret);
1971 g_field_step = 1000;
1972
1973 return 1;
1974}
1975
1976
1977static int
1978FormStore1(struct mg_connection *conn, void *cbdata)
1979{
1980 return FormStore(conn, cbdata, 3, 101);
1981}
1982
1983
1984static int
1985FormStore2(struct mg_connection *conn, void *cbdata)
1986{
1987 return FormStore(conn, cbdata, 4, 102);
1988}
1989
1990
1991static void
1992send_chunk_string(struct mg_connection *conn, const char *txt)
1993{
1994 unsigned int chunk_len = (unsigned int)strlen(txt);
1995 mg_printf(conn, "%x\r\n", chunk_len);
1996 mg_write(conn, txt, chunk_len);
1997 mg_printf(conn, "\r\n");
1998}
1999
2000
2001START_TEST(test_handle_form)
2002{
2003 struct mg_context *ctx;
2004 struct mg_connection *client_conn;
2005 const struct mg_request_info *ri;
2006 const char *OPTIONS[8];
2007 const char *opt;
2008 int opt_idx = 0;
2009 char ebuf[100];
2010 const char *multipart_body;
2011 const char *boundary;
2012 size_t body_len, body_sent, chunk_len;
2013 int sleep_cnt;
2014
2015 memset((void *)OPTIONS, 0, sizeof(OPTIONS));
2016 OPTIONS[opt_idx++] = "listening_ports";
2017 OPTIONS[opt_idx++] = "8884";
2018 ck_assert_int_le(opt_idx, (int)(sizeof(OPTIONS) / sizeof(OPTIONS[0])));
2019 ck_assert(OPTIONS[sizeof(OPTIONS) / sizeof(OPTIONS[0]) - 1] == NULL);
2020 ck_assert(OPTIONS[sizeof(OPTIONS) / sizeof(OPTIONS[0]) - 2] == NULL);
2021
2022 mark_point();
2023 ctx = mg_start(NULL, &g_ctx, OPTIONS);
2024 ck_assert(ctx != NULL);
2025 g_ctx = ctx;
2026
2027 opt = mg_get_option(ctx, "listening_ports");
2028 ck_assert_str_eq(opt, "8884");
2029
2030 mg_set_request_handler(ctx, "/handle_form", FormGet, (void *)0);
2031 mg_set_request_handler(ctx, "/handle_form_store", FormStore1, (void *)0);
2032 mg_set_request_handler(ctx, "/handle_form_store2", FormStore2, (void *)0);
2033
2034 test_sleep(1);
2035
2036 /* Handle form: "GET" */
2037 client_conn = mg_download("localhost",
2038 8884,
2039 0,
2040 ebuf,
2041 sizeof(ebuf),
2042 "%s",
2043 "GET /handle_form"
2044 "?textin=text&passwordin=&radio1=val1"
2045 "&radio2=val1&check1=val1&numberin=1"
2046 "&datein=1.1.2016&colorin=%2380ff00"
2047 "&rangein=3&monthin=&weekin=&timein="
2048 "&datetimen=&datetimelocalin=&emailin="
2049 "&searchin=&telin=&urlin=&filein="
2050 "&filesin=&selectin=opt1"
2051 "&message=Text+area+default+text. "
2052 "HTTP/1.0\r\n"
2053 "Host: localhost:8884\r\n"
2054 "Connection: close\r\n\r\n");
2055 ck_assert(client_conn != NULL);
2056 for (sleep_cnt = 0; sleep_cnt < 30; sleep_cnt++) {
2057 test_sleep(1);
2058 if (g_field_step == 1000) {
2059 break;
2060 }
2061 }
2062 ri = mg_get_request_info(client_conn);
2063
2064 ck_assert(ri != NULL);
2065 ck_assert_str_eq(ri->uri, "200");
2066 mg_close_connection(client_conn);
2067
2068 /* Handle form: "POST x-www-form-urlencoded" */
2069 client_conn =
2070 mg_download("localhost",
2071 8884,
2072 0,
2073 ebuf,
2074 sizeof(ebuf),
2075 "%s",
2076 "POST /handle_form HTTP/1.1\r\n"
2077 "Host: localhost:8884\r\n"
2078 "Connection: close\r\n"
2079 "Content-Type: application/x-www-form-urlencoded\r\n"
2080 "Content-Length: 263\r\n"
2081 "\r\n"
2082 "textin=text&passwordin=&radio1=val1&radio2=val1"
2083 "&check1=val1&numberin=1&datein=1.1.2016"
2084 "&colorin=%2380ff00&rangein=3&monthin=&weekin="
2085 "&timein=&datetimen=&datetimelocalin=&emailin="
2086 "&searchin=&telin=&urlin=&filein=&filesin="
2087 "&selectin=opt1&message=Text+area+default+text.");
2088 ck_assert(client_conn != NULL);
2089 for (sleep_cnt = 0; sleep_cnt < 30; sleep_cnt++) {
2090 test_sleep(1);
2091 if (g_field_step == 1000) {
2092 break;
2093 }
2094 }
2095 ri = mg_get_request_info(client_conn);
2096
2097 ck_assert(ri != NULL);
2098 ck_assert_str_eq(ri->uri, "200");
2099 mg_close_connection(client_conn);
2100
2101 /* Handle form: "POST multipart/form-data" */
2102 multipart_body =
2103 "--multipart-form-data-boundary--see-RFC-2388\r\n"
2104 "Content-Disposition: form-data; name=\"textin\"\r\n"
2105 "\r\n"
2106 "text\r\n"
2107 "--multipart-form-data-boundary--see-RFC-2388\r\n"
2108 "Content-Disposition: form-data; name=\"passwordin\"\r\n"
2109 "\r\n"
2110 "\r\n"
2111 "--multipart-form-data-boundary--see-RFC-2388\r\n"
2112 "Content-Disposition: form-data; name=\"radio1\"\r\n"
2113 "\r\n"
2114 "val1\r\n"
2115 "--multipart-form-data-boundary--see-RFC-2388\r\n"
2116 "Content-Disposition: form-data; name=\"radio2\"\r\n"
2117 "\r\n"
2118 "val1\r\n"
2119 "--multipart-form-data-boundary--see-RFC-2388\r\n"
2120 "Content-Disposition: form-data; name=\"check1\"\r\n"
2121 "\r\n"
2122 "val1\r\n"
2123 "--multipart-form-data-boundary--see-RFC-2388\r\n"
2124 "Content-Disposition: form-data; name=\"numberin\"\r\n"
2125 "\r\n"
2126 "1\r\n"
2127 "--multipart-form-data-boundary--see-RFC-2388\r\n"
2128 "Content-Disposition: form-data; name=\"datein\"\r\n"
2129 "\r\n"
2130 "1.1.2016\r\n"
2131 "--multipart-form-data-boundary--see-RFC-2388\r\n"
2132 "Content-Disposition: form-data; name=\"colorin\"\r\n"
2133 "\r\n"
2134 "#80ff00\r\n"
2135 "--multipart-form-data-boundary--see-RFC-2388\r\n"
2136 "Content-Disposition: form-data; name=\"rangein\"\r\n"
2137 "\r\n"
2138 "3\r\n"
2139 "--multipart-form-data-boundary--see-RFC-2388\r\n"
2140 "Content-Disposition: form-data; name=\"monthin\"\r\n"
2141 "\r\n"
2142 "\r\n"
2143 "--multipart-form-data-boundary--see-RFC-2388\r\n"
2144 "Content-Disposition: form-data; name=\"weekin\"\r\n"
2145 "\r\n"
2146 "\r\n"
2147 "--multipart-form-data-boundary--see-RFC-2388\r\n"
2148 "Content-Disposition: form-data; name=\"timein\"\r\n"
2149 "\r\n"
2150 "\r\n"
2151 "--multipart-form-data-boundary--see-RFC-2388\r\n"
2152 "Content-Disposition: form-data; name=\"datetimen\"\r\n"
2153 "\r\n"
2154 "\r\n"
2155 "--multipart-form-data-boundary--see-RFC-2388\r\n"
2156 "Content-Disposition: form-data; name=\"datetimelocalin\"\r\n"
2157 "\r\n"
2158 "\r\n"
2159 "--multipart-form-data-boundary--see-RFC-2388\r\n"
2160 "Content-Disposition: form-data; name=\"emailin\"\r\n"
2161 "\r\n"
2162 "\r\n"
2163 "--multipart-form-data-boundary--see-RFC-2388\r\n"
2164 "Content-Disposition: form-data; name=\"searchin\"\r\n"
2165 "\r\n"
2166 "\r\n"
2167 "--multipart-form-data-boundary--see-RFC-2388\r\n"
2168 "Content-Disposition: form-data; name=\"telin\"\r\n"
2169 "\r\n"
2170 "\r\n"
2171 "--multipart-form-data-boundary--see-RFC-2388\r\n"
2172 "Content-Disposition: form-data; name=\"urlin\"\r\n"
2173 "\r\n"
2174 "\r\n"
2175 "--multipart-form-data-boundary--see-RFC-2388\r\n"
2176 "Content-Disposition: form-data; name=\"filein\"; filename=\"\"\r\n"
2177 "Content-Type: application/octet-stream\r\n"
2178 "\r\n"
2179 "\r\n"
2180 "--multipart-form-data-boundary--see-RFC-2388\r\n"
2181 "Content-Disposition: form-data; name=\"filesin\"; filename=\"\"\r\n"
2182 "Content-Type: application/octet-stream\r\n"
2183 "\r\n"
2184 "\r\n"
2185 "--multipart-form-data-boundary--see-RFC-2388\r\n"
2186 "Content-Disposition: form-data; name=\"selectin\"\r\n"
2187 "\r\n"
2188 "opt1\r\n"
2189 "--multipart-form-data-boundary--see-RFC-2388\r\n"
2190 "Content-Disposition: form-data; name=\"message\"\r\n"
2191 "\r\n"
2192 "Text area default text.\r\n"
2193 "--multipart-form-data-boundary--see-RFC-2388--\r\n";
2194 body_len = strlen(multipart_body);
2195 ck_assert_uint_eq(body_len, 2374); /* not required */
2196
2197 client_conn =
2198 mg_download("localhost",
2199 8884,
2200 0,
2201 ebuf,
2202 sizeof(ebuf),
2203 "POST /handle_form HTTP/1.1\r\n"
2204 "Host: localhost:8884\r\n"
2205 "Connection: close\r\n"
2206 "Content-Type: multipart/form-data; "
2207 "boundary=multipart-form-data-boundary--see-RFC-2388\r\n"
2208 "Content-Length: %u\r\n"
2209 "\r\n%s",
2210 (unsigned int)body_len,
2211 multipart_body);
2212
2213 ck_assert(client_conn != NULL);
2214 for (sleep_cnt = 0; sleep_cnt < 30; sleep_cnt++) {
2215 test_sleep(1);
2216 if (g_field_step == 1000) {
2217 break;
2218 }
2219 }
2220 ri = mg_get_request_info(client_conn);
2221
2222 ck_assert(ri != NULL);
2223 ck_assert_str_eq(ri->uri, "200");
2224 mg_close_connection(client_conn);
2225
2226
2227 /* Handle form: "POST multipart/form-data" with chunked transfer encoding */
2228 client_conn =
2229 mg_download("localhost",
2230 8884,
2231 0,
2232 ebuf,
2233 sizeof(ebuf),
2234 "%s",
2235 "POST /handle_form HTTP/1.1\r\n"
2236 "Host: localhost:8884\r\n"
2237 "Connection: close\r\n"
2238 "Content-Type: multipart/form-data; "
2239 "boundary=multipart-form-data-boundary--see-RFC-2388\r\n"
2240 "Transfer-Encoding: chunked\r\n"
2241 "\r\n");
2242
2243 ck_assert(client_conn != NULL);
2244
2245 body_len = strlen(multipart_body);
2246 chunk_len = 1;
2247 body_sent = 0;
2248 while (body_len > body_sent) {
2249 if (chunk_len > (body_len - body_sent)) {
2250 chunk_len = body_len - body_sent;
2251 }
2252 ck_assert_int_gt((int)chunk_len, 0);
2253 mg_printf(client_conn, "%x\r\n", (unsigned int)chunk_len);
2254 mg_write(client_conn, multipart_body + body_sent, chunk_len);
2255 mg_printf(client_conn, "\r\n");
2256 body_sent += chunk_len;
2257 chunk_len = (chunk_len % 40) + 1;
2258 }
2259 mg_printf(client_conn, "0\r\n");
2260
2261 for (sleep_cnt = 0; sleep_cnt < 30; sleep_cnt++) {
2262 test_sleep(1);
2263 if (g_field_step == 1000) {
2264 break;
2265 }
2266 }
2267 ri = mg_get_request_info(client_conn);
2268
2269 ck_assert(ri != NULL);
2270 ck_assert_str_eq(ri->uri, "200");
2271 mg_close_connection(client_conn);
2272
2273
2274 /* Now test form_store */
2275
2276 /* First test with GET */
2277 client_conn = mg_download("localhost",
2278 8884,
2279 0,
2280 ebuf,
2281 sizeof(ebuf),
2282 "%s",
2283 "GET /handle_form_store"
2284 "?storeme=storetest"
2285 "&continue_field_handler=ignore"
2286 "&break_field_handler=abort"
2287 "&dontread=xyz "
2288 "HTTP/1.0\r\n"
2289 "Host: localhost:8884\r\n"
2290 "Connection: close\r\n\r\n");
2291
2292 ck_assert(client_conn != NULL);
2293
2294 for (sleep_cnt = 0; sleep_cnt < 30; sleep_cnt++) {
2295 test_sleep(1);
2296 if (g_field_step == 1000) {
2297 break;
2298 }
2299 }
2300 ri = mg_get_request_info(client_conn);
2301
2302 ck_assert(ri != NULL);
2303 ck_assert_str_eq(ri->uri, "200");
2304 mg_close_connection(client_conn);
2305
2306
2307 /* Handle form: "POST x-www-form-urlencoded", chunked, store */
2308 client_conn =
2309 mg_download("localhost",
2310 8884,
2311 0,
2312 ebuf,
2313 sizeof(ebuf),
2314 "%s",
2315 "POST /handle_form_store HTTP/1.0\r\n"
2316 "Host: localhost:8884\r\n"
2317 "Connection: close\r\n"
2318 "Content-Type: application/x-www-form-urlencoded\r\n"
2319 "Transfer-Encoding: chunked\r\n"
2320 "\r\n");
2321 ck_assert(client_conn != NULL);
2322
2323 send_chunk_string(client_conn, "storeme=store");
2324 send_chunk_string(client_conn, "test&");
2325 send_chunk_string(client_conn, "continue_field_handler=ignore");
2326 send_chunk_string(client_conn, "&br");
2327 test_sleep(1);
2328 send_chunk_string(client_conn, "eak_field_handler=abort&");
2329 send_chunk_string(client_conn, "dontread=xyz");
2330 mg_printf(client_conn, "0\r\n");
2331
2332 for (sleep_cnt = 0; sleep_cnt < 30; sleep_cnt++) {
2333 test_sleep(1);
2334 if (g_field_step == 1000) {
2335 break;
2336 }
2337 }
2338 ri = mg_get_request_info(client_conn);
2339
2340 ck_assert(ri != NULL);
2341 ck_assert_str_eq(ri->uri, "200");
2342 mg_close_connection(client_conn);
2343
2344 /* Handle form: "POST multipart/form-data", chunked, store */
2345 client_conn =
2346 mg_download("localhost",
2347 8884,
2348 0,
2349 ebuf,
2350 sizeof(ebuf),
2351 "%s",
2352 "POST /handle_form_store HTTP/1.0\r\n"
2353 "Host: localhost:8884\r\n"
2354 "Connection: close\r\n"
2355 "Content-Type: multipart/form-data; "
2356 "boundary=multipart-form-data-boundary--see-RFC-2388\r\n"
2357 "Transfer-Encoding: chunked\r\n"
2358 "\r\n");
2359 ck_assert(client_conn != NULL);
2360
2361 send_chunk_string(client_conn, "--multipart-form-data-boundary");
2362 send_chunk_string(client_conn, "--see-RFC-2388\r\n");
2363 send_chunk_string(client_conn, "Content-Disposition: form-data; ");
2364 send_chunk_string(client_conn, "name=\"storeme\"\r\n");
2365 send_chunk_string(client_conn, "\r\n");
2366 send_chunk_string(client_conn, "storetest\r\n");
2367
2368 send_chunk_string(client_conn, "--multipart-form-data-boundary-");
2369 send_chunk_string(client_conn, "-see-RFC-2388\r\n");
2370 send_chunk_string(client_conn, "Content-Disposition: form-data; ");
2371 send_chunk_string(client_conn, "name=\"continue_field_handler\"\r\n");
2372 send_chunk_string(client_conn, "\r\n");
2373 send_chunk_string(client_conn, "ignore\r\n");
2374
2375 send_chunk_string(client_conn, "--multipart-form-data-boundary-");
2376 send_chunk_string(client_conn, "-see-RFC-2388\r\n");
2377 send_chunk_string(client_conn, "Content-Disposition: form-data; ");
2378 send_chunk_string(client_conn, "name=\"break_field_handler\"\r\n");
2379 send_chunk_string(client_conn, "\r\n");
2380 send_chunk_string(client_conn, "abort\r\n");
2381
2382 send_chunk_string(client_conn, "--multipart-form-data-boundary-");
2383 send_chunk_string(client_conn, "-see-RFC-2388\r\n");
2384 send_chunk_string(client_conn, "Content-Disposition: form-data; ");
2385 send_chunk_string(client_conn, "name=\"dontread\"\r\n");
2386 send_chunk_string(client_conn, "\r\n");
2387 send_chunk_string(client_conn, "xyz\r\n");
2388 send_chunk_string(client_conn, "--multipart-form-data-boundary");
2389 send_chunk_string(client_conn, "--see-RFC-2388--\r\n");
2390 mg_printf(client_conn, "0\r\n");
2391
2392 for (sleep_cnt = 0; sleep_cnt < 30; sleep_cnt++) {
2393 test_sleep(1);
2394 if (g_field_step == 1000) {
2395 break;
2396 }
2397 }
2398 ri = mg_get_request_info(client_conn);
2399
2400 ck_assert(ri != NULL);
2401 ck_assert_str_eq(ri->uri, "200");
2402 mg_close_connection(client_conn);
2403
2404
2405 /* Handle form: "POST multipart/form-data", chunked, store, with files */
2406 client_conn =
2407 mg_download("localhost",
2408 8884,
2409 0,
2410 ebuf,
2411 sizeof(ebuf),
2412 "%s",
2413 "POST /handle_form_store2 HTTP/1.0\r\n"
2414 "Host: localhost:8884\r\n"
2415 "Connection: close\r\n"
2416 "Content-Type: multipart/form-data; "
2417 "boundary=multipart-form-data-boundary--see-RFC-2388\r\n"
2418 "Transfer-Encoding: chunked\r\n"
2419 "\r\n");
2420 ck_assert(client_conn != NULL);
2421
2422 boundary = "--multipart-form-data-boundary--see-RFC-2388\r\n";
2423
2424 send_chunk_string(client_conn, boundary);
2425 send_chunk_string(client_conn, "Content-Disposition: form-data; ");
2426 send_chunk_string(client_conn, "name=\"storeme\"\r\n");
2427 send_chunk_string(client_conn, "\r\n");
2428 send_chunk_string(client_conn, "storetest\r\n");
2429
2430 send_chunk_string(client_conn, boundary);
2431 send_chunk_string(client_conn, "-see-RFC-2388\r\n");
2432 send_chunk_string(client_conn, "Content-Disposition: form-data; ");
2433 send_chunk_string(client_conn, "name=\"continue_field_handler\";");
2434 send_chunk_string(client_conn, "filename=\"file_ignored.txt\"\r\n");
2435 send_chunk_string(client_conn, "Content-Type: ");
2436 send_chunk_string(client_conn, "application/octet-stream\r\n");
2437 send_chunk_string(client_conn, "X-Ignored-Header: xyz\r\n");
2438 send_chunk_string(client_conn, "\r\n");
2439
2440 /* send some megabyte of data */
2441 body_sent = 0;
2442 do {
2443 send_chunk_string(client_conn, "ignore\r\n");
2444 body_sent += 8;
2445 /* send some strings that are almost boundaries */
2446 for (chunk_len = 1; chunk_len < strlen(boundary); chunk_len++) {
2447 /* chunks from 1 byte to strlen(boundary)-1 */
2448 mg_printf(client_conn, "%x\r\n", (unsigned int)chunk_len);
2449 mg_write(client_conn, boundary, chunk_len);
2450 mg_printf(client_conn, "\r\n");
2451 body_sent += chunk_len;
2452 }
2453 } while (body_sent < 1024 * 1024);
2454 send_chunk_string(client_conn, "\r\n");
2455
2456 send_chunk_string(client_conn, boundary);
2457 send_chunk_string(client_conn, "-see-RFC-2388\r\n");
2458 send_chunk_string(client_conn, "Content-Disposition: form-data; ");
2459 send_chunk_string(client_conn, "name=\"file2store\";");
2460 send_chunk_string(client_conn, "filename=\"myfile.txt\"\r\n");
2461 send_chunk_string(client_conn, "Content-Type: ");
2462 send_chunk_string(client_conn, "application/octet-stream\r\n");
2463 send_chunk_string(client_conn, "X-Ignored-Header: xyz\r\n");
2464 send_chunk_string(client_conn, "\r\n");
2465 for (body_sent = 0; (int)body_sent < (int)myfile_content_rep; body_sent++) {
2466 send_chunk_string(client_conn, myfile_content);
2467 }
2468 send_chunk_string(client_conn, "\r\n");
2469
2470 send_chunk_string(client_conn, boundary);
2471 send_chunk_string(client_conn, "Content-Disposition: form-data; ");
2472 send_chunk_string(client_conn, "name=\"break_field_handler\"\r\n");
2473 send_chunk_string(client_conn, "\r\n");
2474 send_chunk_string(client_conn, "abort\r\n");
2475
2476 send_chunk_string(client_conn, boundary);
2477 send_chunk_string(client_conn, "Content-Disposition: form-data; ");
2478 send_chunk_string(client_conn, "name=\"dontread\"\r\n");
2479 send_chunk_string(client_conn, "\r\n");
2480 send_chunk_string(client_conn, "xyz\r\n");
2481 send_chunk_string(client_conn, "--multipart-form-data-boundary");
2482 send_chunk_string(client_conn, "--see-RFC-2388--\r\n");
2483 mg_printf(client_conn, "0\r\n");
2484
2485 for (sleep_cnt = 0; sleep_cnt < 30; sleep_cnt++) {
2486 test_sleep(1);
2487 if (g_field_step == 1000) {
2488 break;
2489 }
2490 }
2491 ri = mg_get_request_info(client_conn);
2492
2493 ck_assert(ri != NULL);
2494 ck_assert_str_eq(ri->uri, "200");
2495 mg_close_connection(client_conn);
2496
2497
2498 /* Close the server */
2499 g_ctx = NULL;
2500 mg_stop(ctx);
2501 mark_point();
2502}
2503END_TEST
2504
2505
2506START_TEST(test_http_auth)
2507{
2508#if !defined(NO_FILES)
2509 const char *OPTIONS[] = {
2510 "document_root",
2511 ".",
2512 "listening_ports",
2513 "8080",
2514#if !defined(NO_CACHING)
2515 "static_file_max_age",
2516 "0",
2517#endif
2518 NULL,
2519 };
2520 struct mg_context *ctx;
2521 struct mg_connection *client_conn;
2522 char client_err[256], nonce[256];
2523 const struct mg_request_info *client_ri;
2524 int client_res;
2525 FILE *f;
2526 const char *passwd_file = ".htpasswd";
2527 const char *test_file = "test_http_auth.test_file.txt";
2528 const char *test_content = "test_http_auth test_file content";
2529 const char *domain;
2530 const char *doc_root;
2531 const char *auth_request;
2532 const char *str;
2533 size_t len;
2534 int i;
2535 char HA1[256], HA2[256], HA[256];
2536 char HA1_md5_buf[33], HA2_md5_buf[33], HA_md5_buf[33];
2537 char *HA1_md5_ret, *HA2_md5_ret, *HA_md5_ret;
2538 const char *nc = "00000001";
2539 const char *cnonce = "6789ABCD";
2540
2541
2542 /* Start with default options */
2543 mark_point();
2544 ctx = mg_start(NULL, NULL, OPTIONS);
2545 test_sleep(1);
2546
2547 ck_assert(ctx != NULL);
2548 domain = mg_get_option(ctx, "authentication_domain");
2549 ck_assert(domain != NULL);
2550 len = strlen(domain);
2551 ck_assert_uint_gt(len, 0);
2552 ck_assert_uint_lt(len, 64);
2553 doc_root = mg_get_option(ctx, "document_root");
2554 ck_assert_str_eq(doc_root, ".");
2555
2556 /* Create a default file in the document root */
2557 f = fopen(test_file, "w");
2558 if (f) {
2559 fprintf(f, "%s", test_content);
2560 fclose(f);
2561 } else {
2562 ck_abort_msg("Cannot create file %s", test_file);
2563 }
2564
2565 remove(passwd_file);
2566
2567 /* Read file before a .htpasswd file has been created */
2568 memset(client_err, 0, sizeof(client_err));
2569 client_conn =
2570 mg_connect_client("127.0.0.1", 8080, 0, client_err, sizeof(client_err));
2571 ck_assert(client_conn != NULL);
2572 ck_assert_str_eq(client_err, "");
2573 mg_printf(client_conn, "GET /%s HTTP/1.0\r\n\r\n", test_file);
2574 client_res =
2575 mg_get_response(client_conn, client_err, sizeof(client_err), 10000);
2576 ck_assert_int_ge(client_res, 0);
2577 ck_assert_str_eq(client_err, "");
2578 client_ri = mg_get_request_info(client_conn);
2579 ck_assert(client_ri != NULL);
2580
2581 ck_assert_str_eq(client_ri->uri, "200");
2582 client_res = (int)mg_read(client_conn, client_err, sizeof(client_err));
2583 ck_assert_int_gt(client_res, 0);
2584 ck_assert_int_le(client_res, sizeof(client_err));
2585 ck_assert_str_eq(client_err, test_content);
2586 mg_close_connection(client_conn);
2587
2588 test_sleep(1);
2589
2590 /* Create a .htpasswd file */
2591 client_res = mg_modify_passwords_file(passwd_file, domain, "user", "pass");
2592 ck_assert_int_eq(client_res, 1);
2593
2594 client_res = mg_modify_passwords_file(NULL, domain, "user", "pass");
2595 ck_assert_int_eq(client_res, 0); /* Filename is required */
2596
2597 test_sleep(1);
2598
2599 /* Repeat test after .htpasswd is created */
2600 memset(client_err, 0, sizeof(client_err));
2601 client_conn =
2602 mg_connect_client("127.0.0.1", 8080, 0, client_err, sizeof(client_err));
2603 ck_assert(client_conn != NULL);
2604 ck_assert_str_eq(client_err, "");
2605 mg_printf(client_conn, "GET /%s HTTP/1.0\r\n\r\n", test_file);
2606 client_res =
2607 mg_get_response(client_conn, client_err, sizeof(client_err), 10000);
2608 ck_assert_int_ge(client_res, 0);
2609 ck_assert_str_eq(client_err, "");
2610 client_ri = mg_get_request_info(client_conn);
2611 ck_assert(client_ri != NULL);
2612
2613 ck_assert_str_eq(client_ri->uri, "401");
2614
2615 auth_request = NULL;
2616 for (i = 0; i < client_ri->num_headers; i++) {
2617 if (!mg_strcasecmp(client_ri->http_headers[i].name,
2618 "WWW-Authenticate")) {
2619 ck_assert_ptr_eq(auth_request, NULL);
2620 auth_request = client_ri->http_headers[i].value;
2621 ck_assert_ptr_ne(auth_request, NULL);
2622 }
2623 }
2624 ck_assert_ptr_ne(auth_request, NULL);
2625 str = "Digest qop=\"auth\", realm=\"";
2626 len = strlen(str);
2627 ck_assert(!mg_strncasecmp(auth_request, str, len));
2628 ck_assert(!strncmp(auth_request + len, domain, strlen(domain)));
2629 len += strlen(domain);
2630 str = "\", nonce=\"";
2631 ck_assert(!strncmp(auth_request + len, str, strlen(str)));
2632 len += strlen(str);
2633 str = strchr(auth_request + len, '\"');
2634 ck_assert_ptr_ne(str, NULL);
2635 ck_assert_ptr_ne(str, auth_request + len);
2636 /* nonce is from including (auth_request + len) to excluding (str) */
2637 ck_assert_int_gt((ptrdiff_t)(str) - (ptrdiff_t)(auth_request + len), 0);
2638 ck_assert_int_lt((ptrdiff_t)(str) - (ptrdiff_t)(auth_request + len),
2639 (ptrdiff_t)sizeof(nonce));
2640 memset(nonce, 0, sizeof(nonce));
2641 memcpy(nonce,
2642 auth_request + len,
2643 (size_t)((ptrdiff_t)(str) - (ptrdiff_t)(auth_request + len)));
2644 memset(HA1, 0, sizeof(HA1));
2645 memset(HA2, 0, sizeof(HA2));
2646 memset(HA, 0, sizeof(HA));
2647 memset(HA1_md5_buf, 0, sizeof(HA1_md5_buf));
2648 memset(HA2_md5_buf, 0, sizeof(HA2_md5_buf));
2649 memset(HA_md5_buf, 0, sizeof(HA_md5_buf));
2650
2651 sprintf(HA1, "%s:%s:%s", "user", domain, "pass");
2652 sprintf(HA2, "%s:/%s", "GET", test_file);
2653 HA1_md5_ret = mg_md5(HA1_md5_buf, HA1, NULL);
2654 HA2_md5_ret = mg_md5(HA2_md5_buf, HA2, NULL);
2655
2656 ck_assert_ptr_eq(HA1_md5_ret, HA1_md5_buf);
2657 ck_assert_ptr_eq(HA2_md5_ret, HA2_md5_buf);
2658
2659 HA_md5_ret = mg_md5(HA_md5_buf, "user", ":", domain, ":", "pass", NULL);
2660 ck_assert_ptr_eq(HA_md5_ret, HA_md5_buf);
2661 ck_assert_str_eq(HA1_md5_ret, HA_md5_buf);
2662
2663 HA_md5_ret = mg_md5(HA_md5_buf, "GET", ":", "/", test_file, NULL);
2664 ck_assert_ptr_eq(HA_md5_ret, HA_md5_buf);
2665 ck_assert_str_eq(HA2_md5_ret, HA_md5_buf);
2666
2667 HA_md5_ret = mg_md5(HA_md5_buf,
2668 HA1_md5_buf,
2669 ":",
2670 nonce,
2671 ":",
2672 nc,
2673 ":",
2674 cnonce,
2675 ":",
2676 "auth",
2677 ":",
2678 HA2_md5_buf,
2679 NULL);
2680 ck_assert_ptr_eq(HA_md5_ret, HA_md5_buf);
2681
2682 /* Retry with Authorization */
2683 memset(client_err, 0, sizeof(client_err));
2684 client_conn =
2685 mg_connect_client("127.0.0.1", 8080, 0, client_err, sizeof(client_err));
2686 ck_assert(client_conn != NULL);
2687 ck_assert_str_eq(client_err, "");
2688 mg_printf(client_conn, "GET /%s HTTP/1.0\r\n", test_file);
2689 mg_printf(client_conn,
2690 "Authorization: Digest "
2691 "username=\"%s\", "
2692 "realm=\"%s\", "
2693 "nonce=\"%s\", "
2694 "uri=\"/%s\", "
2695 "qop=auth, "
2696 "nc=%s, "
2697 "cnonce=\"%s\", "
2698 "response=\"%s\"\r\n\r\n",
2699 "user",
2700 domain,
2701 nonce,
2702 test_file,
2703 nc,
2704 cnonce,
2705 HA_md5_buf);
2706 client_res =
2707 mg_get_response(client_conn, client_err, sizeof(client_err), 10000);
2708 ck_assert_int_ge(client_res, 0);
2709 ck_assert_str_eq(client_err, "");
2710 client_ri = mg_get_request_info(client_conn);
2711 ck_assert(client_ri != NULL);
2712
2713 ck_assert_str_eq(client_ri->uri, "200");
2714 client_res = (int)mg_read(client_conn, client_err, sizeof(client_err));
2715 ck_assert_int_gt(client_res, 0);
2716 ck_assert_int_le(client_res, sizeof(client_err));
2717 ck_assert_str_eq(client_err, test_content);
2718 mg_close_connection(client_conn);
2719
2720 test_sleep(1);
2721
2722
2723 /* Remove the user from the .htpasswd file again */
2724 client_res = mg_modify_passwords_file(passwd_file, domain, "user", NULL);
2725 ck_assert_int_eq(client_res, 1);
2726
2727 test_sleep(1);
2728
2729
2730 /* Try to access the file again. Expected: 401 error */
2731 memset(client_err, 0, sizeof(client_err));
2732 client_conn =
2733 mg_connect_client("127.0.0.1", 8080, 0, client_err, sizeof(client_err));
2734 ck_assert(client_conn != NULL);
2735 ck_assert_str_eq(client_err, "");
2736 mg_printf(client_conn, "GET /%s HTTP/1.0\r\n\r\n", test_file);
2737 client_res =
2738 mg_get_response(client_conn, client_err, sizeof(client_err), 10000);
2739 ck_assert_int_ge(client_res, 0);
2740 ck_assert_str_eq(client_err, "");
2741 client_ri = mg_get_request_info(client_conn);
2742 ck_assert(client_ri != NULL);
2743
2744 ck_assert_str_eq(client_ri->uri, "401");
2745 mg_close_connection(client_conn);
2746
2747 test_sleep(1);
2748
2749
2750 /* Now remove the password file */
2751 remove(passwd_file);
2752 test_sleep(1);
2753
2754
2755 /* Access to the file must work like before */
2756 memset(client_err, 0, sizeof(client_err));
2757 client_conn =
2758 mg_connect_client("127.0.0.1", 8080, 0, client_err, sizeof(client_err));
2759 ck_assert(client_conn != NULL);
2760 ck_assert_str_eq(client_err, "");
2761 mg_printf(client_conn, "GET /%s HTTP/1.0\r\n\r\n", test_file);
2762 client_res =
2763 mg_get_response(client_conn, client_err, sizeof(client_err), 10000);
2764 ck_assert_int_ge(client_res, 0);
2765 ck_assert_str_eq(client_err, "");
2766 client_ri = mg_get_request_info(client_conn);
2767 ck_assert(client_ri != NULL);
2768
2769 ck_assert_str_eq(client_ri->uri, "200");
2770 client_res = (int)mg_read(client_conn, client_err, sizeof(client_err));
2771 ck_assert_int_gt(client_res, 0);
2772 ck_assert_int_le(client_res, sizeof(client_err));
2773 ck_assert_str_eq(client_err, test_content);
2774 mg_close_connection(client_conn);
2775
2776 test_sleep(1);
2777
2778
2779 /* Stop the server and clean up */
2780 mg_stop(ctx);
2781 remove(test_file);
2782
2783#endif
2784}
2785END_TEST
2786
2787
2788Suite *
2789make_public_server_suite(void)
2790{
2791 Suite *const suite = suite_create("PublicServer");
2792
2793 TCase *const tcase_checktestenv = tcase_create("Check test environment");
2794 TCase *const tcase_startthreads = tcase_create("Start threads");
2795 TCase *const tcase_startstophttp = tcase_create("Start Stop HTTP Server");
2796 TCase *const tcase_startstophttps = tcase_create("Start Stop HTTPS Server");
2797 TCase *const tcase_serverandclienttls = tcase_create("TLS Server Client");
2798 TCase *const tcase_serverrequests = tcase_create("Server Requests");
2799 TCase *const tcase_handle_form = tcase_create("Handle Form");
2800 TCase *const tcase_http_auth = tcase_create("HTTP Authentication");
2801
2802 tcase_add_test(tcase_checktestenv, test_the_test_environment);
2803 tcase_set_timeout(tcase_checktestenv, civetweb_min_test_timeout);
2804 suite_add_tcase(suite, tcase_checktestenv);
2805
2806 tcase_add_test(tcase_startthreads, test_threading);
2807 tcase_set_timeout(tcase_startthreads, civetweb_min_test_timeout);
2808 suite_add_tcase(suite, tcase_startthreads);
2809
2810 tcase_add_test(tcase_startstophttp, test_mg_start_stop_http_server);
2811 tcase_set_timeout(tcase_startstophttp, civetweb_min_test_timeout);
2812 suite_add_tcase(suite, tcase_startstophttp);
2813
2814 tcase_add_test(tcase_startstophttps, test_mg_start_stop_https_server);
2815 tcase_set_timeout(tcase_startstophttps, civetweb_min_test_timeout);
2816 suite_add_tcase(suite, tcase_startstophttps);
2817
2818 tcase_add_test(tcase_serverandclienttls, test_mg_server_and_client_tls);
2819 tcase_set_timeout(tcase_serverandclienttls, civetweb_min_test_timeout);
2820 suite_add_tcase(suite, tcase_serverandclienttls);
2821
2822 tcase_add_test(tcase_serverrequests, test_request_handlers);
2823 tcase_set_timeout(tcase_serverrequests, 120);
2824 suite_add_tcase(suite, tcase_serverrequests);
2825
2826 tcase_add_test(tcase_handle_form, test_handle_form);
2827 tcase_set_timeout(tcase_handle_form, 300);
2828 suite_add_tcase(suite, tcase_handle_form);
2829
2830 tcase_add_test(tcase_http_auth, test_http_auth);
2831 tcase_set_timeout(tcase_http_auth, 60);
2832 suite_add_tcase(suite, tcase_http_auth);
2833
2834 return suite;
2835}
2836
2837
2838#ifdef REPLACE_CHECK_FOR_LOCAL_DEBUGGING
2839/* Used to debug test cases without using the check framework */
2840
2841static int chk_ok = 0;
2842static int chk_failed = 0;
2843
2844
2845void
2846MAIN_PUBLIC_SERVER(void)
2847{
2848 /*
2849 test_the_test_environment(0);
2850 test_threading(0);
2851 */
2852 test_mg_start_stop_http_server(0);
31f18b77 2853 // test_mg_start_stop_https_server(0);
7c673cae 2854 test_request_handlers(0);
31f18b77
FG
2855 // test_mg_server_and_client_tls(0);
2856 // test_handle_form(0);
2857 // test_http_auth(0);
2858 test_keep_alive(0);
7c673cae
FG
2859
2860 printf("\nok: %i\nfailed: %i\n\n", chk_ok, chk_failed);
2861}
2862
2863void
2864_ck_assert_failed(const char *file, int line, const char *expr, ...)
2865{
2866 va_list va;
2867 va_start(va, expr);
2868 fprintf(stderr, "Error: %s, line %i\n", file, line); /* breakpoint here ! */
2869 vfprintf(stderr, expr, va);
2870 fprintf(stderr, "\n\n");
2871 va_end(va);
2872 chk_failed++;
2873}
2874
2875void
2876_ck_assert_msg(int cond, const char *file, int line, const char *expr, ...)
2877{
2878 va_list va;
2879
2880 if (cond) {
2881 chk_ok++;
2882 return;
2883 }
2884
2885 va_start(va, expr);
2886 fprintf(stderr, "Error: %s, line %i\n", file, line); /* breakpoint here ! */
2887 vfprintf(stderr, expr, va);
2888 fprintf(stderr, "\n\n");
2889 va_end(va);
2890 chk_failed++;
2891}
2892
2893void
2894_mark_point(const char *file, int line)
2895{
2896 chk_ok++;
2897}
2898
2899void
2900tcase_fn_start(const char *fname, const char *file, int line)
2901{
2902}
2903void suite_add_tcase(Suite *s, TCase *tc){};
2904void _tcase_add_test(TCase *tc,
2905 TFun tf,
2906 const char *fname,
2907 int _signal,
2908 int allowed_exit_value,
2909 int start,
2910 int end){};
2911TCase *
2912tcase_create(const char *name)
2913{
2914 return NULL;
2915};
2916Suite *
2917suite_create(const char *name)
2918{
2919 return NULL;
2920};
2921void tcase_set_timeout(TCase *tc, double timeout){};
2922
2923#endif