]>
Commit | Line | Data |
---|---|---|
11fdf7f2 | 1 | /* Copyright (c) 2015-2017 the Civetweb developers |
7c673cae FG |
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> | |
11fdf7f2 | 32 | #include <stdint.h> |
7c673cae FG |
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) | |
11fdf7f2 | 41 | #include <windows.h> |
7c673cae FG |
42 | #define test_sleep(x) (Sleep((x)*1000)) |
43 | #else | |
44 | #include <unistd.h> | |
45 | #define test_sleep(x) (sleep(x)) | |
46 | #endif | |
47 | ||
11fdf7f2 TL |
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 | ||
7c673cae FG |
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 | ||
11fdf7f2 TL |
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 | |
7c673cae FG |
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" | |
11fdf7f2 | 136 | #pragma GCC diagnostic ignored "-Wunreachable-code-return" |
7c673cae FG |
137 | #endif |
138 | ||
11fdf7f2 | 139 | ck_abort_msg("wait_not_null failed (%i sec)", i); |
7c673cae FG |
140 | |
141 | return 0; | |
142 | ||
7c673cae FG |
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__) | |
11fdf7f2 TL |
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"); | |
7c673cae | 215 | #else |
11fdf7f2 TL |
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"); | |
7c673cae FG |
219 | #endif |
220 | #endif | |
221 | } | |
222 | END_TEST | |
223 | ||
224 | ||
11fdf7f2 | 225 | static void *threading_data = 0; |
7c673cae FG |
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; | |
11fdf7f2 | 242 | mark_point(); |
7c673cae FG |
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; | |
11fdf7f2 TL |
266 | mark_point(); |
267 | ||
268 | printf("LOG_MSG_FUNC: %s\n", message); | |
269 | mark_point(); | |
270 | ||
271 | return 1; /* Return 1 means "already handled" */ | |
7c673cae FG |
272 | } |
273 | ||
274 | ||
11fdf7f2 TL |
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) | |
7c673cae FG |
291 | { |
292 | struct mg_context *ctx; | |
11fdf7f2 TL |
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(); | |
7c673cae | 338 | #endif |
11fdf7f2 TL |
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) | |
7c673cae FG |
351 | size_t ports_cnt; |
352 | int ports[16]; | |
353 | int ssl[16]; | |
11fdf7f2 | 354 | #endif |
7c673cae FG |
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 | ||
11fdf7f2 TL |
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) | |
7c673cae FG |
375 | memset(ports, 0, sizeof(ports)); |
376 | memset(ssl, 0, sizeof(ssl)); | |
11fdf7f2 | 377 | #endif |
7c673cae FG |
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 | ||
11fdf7f2 | 384 | ctx = test_mg_start(&callbacks, (void *)errmsg, OPTIONS); |
7c673cae FG |
385 | |
386 | ck_assert_str_eq(errmsg, ""); | |
387 | ck_assert(ctx != NULL); | |
388 | ||
11fdf7f2 | 389 | #if defined(MG_LEGACY_INTERFACE) |
7c673cae FG |
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); | |
11fdf7f2 | 396 | #endif |
7c673cae FG |
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); | |
11fdf7f2 TL |
411 | if (ipv6) { |
412 | ck_assert_int_eq(portinfo[0].protocol, 3); | |
413 | } else { | |
414 | ck_assert_int_eq(portinfo[0].protocol, 1); | |
415 | } | |
7c673cae FG |
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)); | |
11fdf7f2 TL |
428 | client_conn = mg_connect_client( |
429 | localhost_name, 8080, 0, client_err, sizeof(client_err)); | |
430 | ||
7c673cae | 431 | ck_assert_str_eq(client_err, ""); |
11fdf7f2 TL |
432 | ck_assert(client_conn != NULL); |
433 | ||
7c673cae FG |
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) | |
11fdf7f2 | 443 | ck_assert_str_eq(client_ri->local_uri, "404"); |
7c673cae | 444 | #else |
11fdf7f2 | 445 | ck_assert_str_eq(client_ri->local_uri, "200"); |
7c673cae FG |
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)); | |
11fdf7f2 TL |
457 | client_conn = mg_connect_client( |
458 | localhost_name, 8080, 0, client_err, sizeof(client_err)); | |
459 | ||
7c673cae | 460 | ck_assert_str_eq(client_err, ""); |
11fdf7f2 TL |
461 | ck_assert(client_conn != NULL); |
462 | ||
7c673cae FG |
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) | |
11fdf7f2 | 474 | ck_assert_str_eq(client_ri->local_uri, "404"); |
7c673cae | 475 | #else |
11fdf7f2 | 476 | ck_assert_str_eq(client_ri->local_uri, "200"); |
7c673cae FG |
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)); | |
11fdf7f2 TL |
489 | client_conn = mg_connect_client( |
490 | localhost_name, 8080, 0, client_err, sizeof(client_err)); | |
491 | ||
7c673cae | 492 | ck_assert_str_eq(client_err, ""); |
11fdf7f2 TL |
493 | ck_assert(client_conn != NULL); |
494 | ||
7c673cae FG |
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 */ | |
11fdf7f2 | 506 | ck_assert_str_eq(client_ri->local_uri, "505"); |
7c673cae FG |
507 | mg_close_connection(client_conn); |
508 | ||
509 | test_sleep(1); | |
510 | ||
511 | ||
11fdf7f2 TL |
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 | ||
7c673cae | 539 | /* End test */ |
11fdf7f2 TL |
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(); | |
7c673cae FG |
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 | ||
11fdf7f2 | 571 | #if defined(MG_LEGACY_INTERFACE) |
7c673cae FG |
572 | size_t ports_cnt; |
573 | int ports[16]; | |
574 | int ssl[16]; | |
11fdf7f2 | 575 | #endif |
7c673cae FG |
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 | ||
11fdf7f2 | 605 | #if defined(MG_LEGACY_INTERFACE) |
7c673cae FG |
606 | memset(ports, 0, sizeof(ports)); |
607 | memset(ssl, 0, sizeof(ssl)); | |
11fdf7f2 | 608 | #endif |
7c673cae FG |
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 | ||
11fdf7f2 TL |
615 | ctx = test_mg_start(&callbacks, (void *)errmsg, OPTIONS); |
616 | ||
7c673cae FG |
617 | ck_assert_str_eq(errmsg, ""); |
618 | ck_assert(ctx != NULL); | |
619 | ||
11fdf7f2 | 620 | #if defined(MG_LEGACY_INTERFACE) |
7c673cae FG |
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); | |
11fdf7f2 | 629 | #endif |
7c673cae FG |
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)); | |
11fdf7f2 | 662 | |
7c673cae | 663 | ck_assert_str_eq(client_err, ""); |
11fdf7f2 TL |
664 | ck_assert(client_conn != NULL); |
665 | ||
7c673cae FG |
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) | |
11fdf7f2 | 675 | ck_assert_str_eq(client_ri->local_uri, "404"); |
7c673cae | 676 | #else |
11fdf7f2 | 677 | ck_assert_str_eq(client_ri->local_uri, "200"); |
7c673cae FG |
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 | ||
11fdf7f2 TL |
687 | test_mg_stop(ctx); |
688 | mark_point(); | |
7c673cae FG |
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 | ||
11fdf7f2 TL |
752 | ctx = test_mg_start(&callbacks, (void *)errmsg, OPTIONS); |
753 | ||
7c673cae FG |
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)); | |
11fdf7f2 | 777 | |
7c673cae | 778 | ck_assert_str_ne(client_err, ""); |
11fdf7f2 | 779 | ck_assert(client_conn == NULL); |
7c673cae FG |
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)); | |
11fdf7f2 | 791 | |
7c673cae | 792 | ck_assert_str_eq(client_err, ""); |
11fdf7f2 TL |
793 | ck_assert(client_conn != NULL); |
794 | ||
7c673cae FG |
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) | |
11fdf7f2 | 804 | ck_assert_str_eq(client_ri->local_uri, "404"); |
7c673cae | 805 | #else |
11fdf7f2 | 806 | ck_assert_str_eq(client_ri->local_uri, "200"); |
7c673cae FG |
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 | ||
11fdf7f2 | 818 | test_mg_stop(ctx); |
7c673cae | 819 | #endif |
11fdf7f2 | 820 | mark_point(); |
7c673cae FG |
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; | |
11fdf7f2 | 835 | void *dummy = malloc(1); |
7c673cae FG |
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 | ||
11fdf7f2 TL |
845 | ck_assert(dummy != NULL); |
846 | ||
847 | mg_set_user_connection_data(conn, (void *)&dummy); | |
7c673cae | 848 | cud = mg_get_user_connection_data(conn); |
11fdf7f2 | 849 | ck_assert_ptr_eq((void *)cud, (void *)&dummy); |
7c673cae | 850 | |
11fdf7f2 TL |
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); | |
7c673cae FG |
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"); | |
11fdf7f2 | 872 | mark_point(); |
7c673cae FG |
873 | |
874 | return 1; | |
875 | } | |
876 | ||
11fdf7f2 | 877 | |
7c673cae FG |
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 | ||
11fdf7f2 TL |
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 | ||
7c673cae FG |
904 | static int |
905 | websock_server_connect(const struct mg_connection *conn, void *udata) | |
906 | { | |
907 | (void)conn; | |
908 | ||
11fdf7f2 TL |
909 | ck_assert_ptr_eq((void *)udata, (void *)(ptrdiff_t)7531); |
910 | WS_TEST_TRACE("Server: Websocket connected\n"); | |
911 | mark_point(); | |
7c673cae FG |
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 | { | |
11fdf7f2 TL |
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"); | |
7c673cae FG |
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 | ||
11fdf7f2 TL |
932 | WS_TEST_TRACE("Server: Websocket ready X\n"); |
933 | mark_point(); | |
7c673cae FG |
934 | } |
935 | ||
936 | ||
11fdf7f2 TL |
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 | ||
7c673cae FG |
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 | ||
11fdf7f2 TL |
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); | |
7c673cae FG |
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); | |
11fdf7f2 TL |
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); | |
7c673cae FG |
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 | } | |
11fdf7f2 | 1013 | mark_point(); |
7c673cae FG |
1014 | |
1015 | return 1; /* return 1 to keep the connetion open */ | |
1016 | } | |
1017 | ||
11fdf7f2 | 1018 | |
7c673cae FG |
1019 | static void |
1020 | websock_server_close(const struct mg_connection *conn, void *udata) | |
1021 | { | |
11fdf7f2 TL |
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 */ | |
7c673cae | 1028 | |
11fdf7f2 TL |
1029 | mark_point(); |
1030 | #endif | |
7c673cae | 1031 | |
11fdf7f2 TL |
1032 | (void)conn; |
1033 | (void)udata; | |
7c673cae FG |
1034 | } |
1035 | ||
1036 | ||
1037 | /****************************************************************************/ | |
1038 | /* WEBSOCKET CLIENT */ | |
1039 | /****************************************************************************/ | |
1040 | struct tclient_data { | |
1041 | void *data; | |
1042 | size_t len; | |
1043 | int closed; | |
11fdf7f2 | 1044 | int clientId; |
7c673cae FG |
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 | ||
11fdf7f2 | 1059 | ck_assert_ptr_eq(user_data, (void *)pclient_data); |
7c673cae FG |
1060 | |
1061 | ck_assert(pclient_data != NULL); | |
11fdf7f2 TL |
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 | } | |
7c673cae FG |
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 | ||
11fdf7f2 TL |
1085 | mark_point(); |
1086 | ||
7c673cae FG |
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 | ||
11fdf7f2 TL |
1099 | #ifndef __MACH__ |
1100 | ck_assert_ptr_eq(user_data, (void *)pclient_data); | |
7c673cae FG |
1101 | |
1102 | ck_assert(pclient_data != NULL); | |
1103 | ||
11fdf7f2 TL |
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; | |
7c673cae | 1111 | pclient_data->closed++; |
11fdf7f2 TL |
1112 | |
1113 | #endif /* __MACH__ */ | |
7c673cae | 1114 | } |
11fdf7f2 TL |
1115 | |
1116 | #endif /* USE_WEBSOCKET */ | |
7c673cae FG |
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; | |
11fdf7f2 | 1161 | struct stat st; |
7c673cae FG |
1162 | |
1163 | #if !defined(NO_SSL) | |
1164 | const char *ssl_cert = locate_ssl_cert(); | |
1165 | #endif | |
1166 | ||
1167 | #if defined(USE_WEBSOCKET) | |
11fdf7f2 TL |
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}; | |
7c673cae FG |
1172 | struct mg_connection *ws_client1_conn = NULL; |
1173 | struct mg_connection *ws_client2_conn = NULL; | |
1174 | struct mg_connection *ws_client3_conn = NULL; | |
11fdf7f2 | 1175 | struct mg_connection *ws_client4_conn = NULL; |
7c673cae FG |
1176 | #endif |
1177 | ||
1178 | char cmd_buf[256]; | |
11fdf7f2 TL |
1179 | char *cgi_env_opt; |
1180 | ||
1181 | mark_point(); | |
7c673cae FG |
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 | |
11fdf7f2 TL |
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 | ||
7c673cae FG |
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 | ||
11fdf7f2 TL |
1214 | ctx = test_mg_start(NULL, &g_ctx, OPTIONS); |
1215 | ||
7c673cae FG |
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); | |
11fdf7f2 | 1221 | |
7c673cae | 1222 | opt = mg_get_option(ctx, "cgi_environment"); |
11fdf7f2 TL |
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"); | |
7c673cae | 1240 | ck_assert_str_eq(opt, ""); |
11fdf7f2 | 1241 | |
7c673cae FG |
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); | |
11fdf7f2 | 1251 | mg_set_request_handler(ctx, uri, NULL, (void *)(ptrdiff_t)1); |
7c673cae FG |
1252 | } |
1253 | for (i = 600; i >= 0; i--) { | |
1254 | sprintf(uri, "/U%u", i); | |
11fdf7f2 | 1255 | mg_set_request_handler(ctx, uri, NULL, (void *)(ptrdiff_t)2); |
7c673cae FG |
1256 | } |
1257 | for (i = 750; i <= 1000; i++) { | |
1258 | sprintf(uri, "/U%u", i); | |
11fdf7f2 | 1259 | mg_set_request_handler(ctx, uri, NULL, (void *)(ptrdiff_t)3); |
7c673cae FG |
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, | |
11fdf7f2 | 1276 | (void *)(ptrdiff_t)7531); |
7c673cae FG |
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); | |
11fdf7f2 | 1291 | ck_assert_str_eq(ri->local_uri, "404"); |
7c673cae FG |
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); | |
11fdf7f2 | 1301 | ck_assert_str_eq(ri->local_uri, "200"); |
7c673cae FG |
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); | |
11fdf7f2 | 1315 | ck_assert_str_eq(ri->local_uri, "200"); |
7c673cae FG |
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); | |
11fdf7f2 | 1340 | ck_assert_str_eq(ri->local_uri, "200"); |
7c673cae FG |
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); | |
11fdf7f2 | 1356 | ck_assert_str_eq(ri->local_uri, "200"); |
7c673cae FG |
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); | |
11fdf7f2 | 1370 | ck_assert_str_eq(ri->local_uri, "302"); |
7c673cae FG |
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); | |
11fdf7f2 | 1384 | ck_assert_str_eq(ri->local_uri, "200"); |
7c673cae FG |
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); | |
11fdf7f2 | 1398 | ck_assert_str_eq(ri->local_uri, "302"); |
7c673cae FG |
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"; | |
11fdf7f2 TL |
1452 | (void)fwrite(cgi_script_content, strlen(cgi_script_content), 1, f); |
1453 | (void)fclose(f); | |
1454 | (void)system("chmod a+x test.cgi"); | |
7c673cae FG |
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) | |
11fdf7f2 | 1472 | ck_assert_str_eq(ri->local_uri, "404"); |
7c673cae | 1473 | #else |
11fdf7f2 | 1474 | ck_assert_str_eq(ri->local_uri, "200"); |
7c673cae FG |
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 | ||
11fdf7f2 TL |
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 | ||
7c673cae FG |
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 | |
11fdf7f2 | 1515 | (void)system(cmd_buf); |
7c673cae FG |
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", | |
11fdf7f2 | 1526 | "POST /cgi_test.cgi/x/y.z HTTP/1.0\r\nContent-Length: 3\r\n\r\nABC"); |
7c673cae FG |
1527 | ck_assert(client_conn != NULL); |
1528 | ri = mg_get_request_info(client_conn); | |
1529 | ||
1530 | ck_assert(ri != NULL); | |
11fdf7f2 | 1531 | ck_assert_str_eq(ri->local_uri, "200"); |
7c673cae FG |
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); | |
11fdf7f2 | 1547 | ck_assert_str_eq(ri->local_uri, "404"); |
7c673cae FG |
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) | |
11fdf7f2 | 1565 | ck_assert_str_eq(ri->local_uri, "404"); |
7c673cae | 1566 | #else |
11fdf7f2 | 1567 | ck_assert_str_eq(ri->local_uri, "200"); |
7c673cae FG |
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) | |
11fdf7f2 | 1593 | ck_assert_str_eq(ri->local_uri, "404"); |
7c673cae FG |
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); | |
11fdf7f2 | 1607 | ck_assert_str_eq(ri->local_uri, "200"); |
7c673cae FG |
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) | |
11fdf7f2 | 1629 | ck_assert_str_eq(ri->local_uri, "404"); |
7c673cae | 1630 | #else |
11fdf7f2 | 1631 | ck_assert_str_eq(ri->local_uri, "200"); |
7c673cae FG |
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) | |
11fdf7f2 | 1652 | ck_assert_str_eq(ri->local_uri, "404"); |
7c673cae | 1653 | #else |
11fdf7f2 | 1654 | ck_assert_str_eq(ri->local_uri, "405"); |
7c673cae FG |
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) | |
11fdf7f2 | 1675 | ck_assert_str_eq(ri->local_uri, "405"); /* method not allowed */ |
7c673cae | 1676 | #else |
11fdf7f2 | 1677 | ck_assert_str_eq(ri->local_uri, "401"); /* not authorized */ |
7c673cae FG |
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)); | |
11fdf7f2 | 1686 | |
7c673cae | 1687 | ck_assert_str_eq(ebuf, ""); |
11fdf7f2 | 1688 | ck_assert(client_conn != NULL); |
7c673cae FG |
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); | |
11fdf7f2 | 1699 | ck_assert_str_eq(ri->local_uri, "200"); |
7c673cae FG |
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)); | |
11fdf7f2 | 1710 | |
7c673cae | 1711 | ck_assert_str_eq(ebuf, ""); |
11fdf7f2 | 1712 | ck_assert(client_conn != NULL); |
7c673cae FG |
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); | |
11fdf7f2 | 1725 | ck_assert_str_eq(ri->local_uri, "200"); |
7c673cae FG |
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 | ||
31f18b77 FG |
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)); | |
11fdf7f2 | 1737 | |
31f18b77 | 1738 | ck_assert_str_eq(ebuf, ""); |
11fdf7f2 | 1739 | ck_assert(client_conn != NULL); |
31f18b77 FG |
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); | |
11fdf7f2 | 1752 | ck_assert_str_eq(ri->local_uri, "200"); |
31f18b77 FG |
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 | ||
7c673cae FG |
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 | ||
11fdf7f2 TL |
1893 | ck_assert(ws_client1_data.closed == 0); /* Not closed */ |
1894 | ||
7c673cae FG |
1895 | mg_close_connection(ws_client1_conn); |
1896 | ||
1897 | test_sleep(3); /* Won't get any message */ | |
1898 | ||
11fdf7f2 TL |
1899 | ck_assert(ws_client1_data.closed == 1); /* Closed */ |
1900 | ||
7c673cae FG |
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; | |
7c673cae | 1973 | |
11fdf7f2 TL |
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 | |
7c673cae FG |
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 | ||
11fdf7f2 TL |
2107 | mark_point(); |
2108 | ||
7c673cae FG |
2109 | return g_field_found_return; |
2110 | } | |
2111 | ||
2112 | ||
2113 | static int g_field_step; | |
2114 | ||
2115 | static int | |
11fdf7f2 TL |
2116 | field_get(const char *key, |
2117 | const char *value_untruncated, | |
2118 | size_t valuelen, | |
2119 | void *user_data) | |
7c673cae | 2120 | { |
11fdf7f2 TL |
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 | ||
7c673cae FG |
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 | ||
11fdf7f2 TL |
2248 | free(value); |
2249 | mark_point(); | |
2250 | ||
7c673cae FG |
2251 | return 0; |
2252 | } | |
2253 | ||
2254 | ||
2255 | static const char *myfile_content = "Content of myfile.txt\r\n"; | |
11fdf7f2 | 2256 | static const int myfile_content_rep = 500; |
7c673cae FG |
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 | } | |
11fdf7f2 | 2308 | mark_point(); |
7c673cae FG |
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 | ||
11fdf7f2 TL |
2338 | mark_point(); |
2339 | ||
7c673cae FG |
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 | ||
11fdf7f2 TL |
2373 | mark_point(); |
2374 | ||
7c673cae FG |
2375 | return 1; |
2376 | } | |
2377 | ||
2378 | ||
2379 | static int | |
2380 | FormStore1(struct mg_connection *conn, void *cbdata) | |
2381 | { | |
11fdf7f2 | 2382 | mark_point(); |
7c673cae FG |
2383 | return FormStore(conn, cbdata, 3, 101); |
2384 | } | |
2385 | ||
2386 | ||
2387 | static int | |
2388 | FormStore2(struct mg_connection *conn, void *cbdata) | |
2389 | { | |
11fdf7f2 | 2390 | mark_point(); |
7c673cae FG |
2391 | return FormStore(conn, cbdata, 4, 102); |
2392 | } | |
2393 | ||
2394 | ||
2395 | static void | |
11fdf7f2 TL |
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) | |
7c673cae | 2424 | { |
11fdf7f2 TL |
2425 | mark_point(); |
2426 | send_chunk_stringl(conn, chunk, (unsigned int)strlen(chunk)); | |
2427 | mark_point(); | |
7c673cae FG |
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 | ||
11fdf7f2 TL |
2445 | mark_point(); |
2446 | ||
7c673cae FG |
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 | ||
11fdf7f2 TL |
2454 | ctx = test_mg_start(NULL, &g_ctx, OPTIONS); |
2455 | ||
7c673cae FG |
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 | ||
11fdf7f2 TL |
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); | |
7c673cae FG |
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); | |
11fdf7f2 | 2497 | ck_assert_str_eq(ri->local_uri, "200"); |
7c673cae FG |
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); | |
11fdf7f2 | 2530 | ck_assert_str_eq(ri->local_uri, "200"); |
7c673cae FG |
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" | |
11fdf7f2 | 2548 | "Content-Disposition: form-data; name=radio2\r\n" |
7c673cae FG |
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" | |
11fdf7f2 | 2613 | "Content-Disposition: form-data; name=filesin; filename=\r\n" |
7c673cae FG |
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); | |
11fdf7f2 | 2627 | ck_assert_uint_eq(body_len, 2368); /* not required */ |
7c673cae FG |
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); | |
11fdf7f2 | 2655 | ck_assert_str_eq(ri->local_uri, "200"); |
7c673cae FG |
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); | |
11fdf7f2 TL |
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"); | |
7c673cae FG |
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); | |
11fdf7f2 | 2782 | ck_assert_str_eq(ri->local_uri, "200"); |
7c673cae FG |
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); | |
11fdf7f2 | 2820 | ck_assert_str_eq(ri->local_uri, "200"); |
7c673cae FG |
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); | |
11fdf7f2 | 2880 | ck_assert_str_eq(ri->local_uri, "200"); |
7c673cae FG |
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); | |
7c673cae FG |
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 | ||
11fdf7f2 TL |
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) */ | |
7c673cae FG |
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 */ | |
11fdf7f2 | 2928 | send_chunk_stringl(client_conn, boundary, (unsigned int)chunk_len); |
7c673cae FG |
2929 | body_sent += chunk_len; |
2930 | } | |
11fdf7f2 | 2931 | } while (body_sent < 8 * 1024); |
7c673cae FG |
2932 | send_chunk_string(client_conn, "\r\n"); |
2933 | ||
2934 | send_chunk_string(client_conn, boundary); | |
7c673cae FG |
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); | |
11fdf7f2 | 2971 | ck_assert_str_eq(ri->local_uri, "200"); |
7c673cae FG |
2972 | mg_close_connection(client_conn); |
2973 | ||
2974 | ||
2975 | /* Close the server */ | |
2976 | g_ctx = NULL; | |
11fdf7f2 | 2977 | test_mg_stop(ctx); |
7c673cae FG |
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 | |
11fdf7f2 TL |
2995 | "put_delete_auth_file", |
2996 | "put_delete_auth_file.csv", | |
7c673cae FG |
2997 | NULL, |
2998 | }; | |
11fdf7f2 | 2999 | |
7c673cae FG |
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 | ||
11fdf7f2 | 3021 | mark_point(); |
7c673cae FG |
3022 | |
3023 | /* Start with default options */ | |
11fdf7f2 | 3024 | ctx = test_mg_start(NULL, NULL, OPTIONS); |
7c673cae FG |
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 | ||
11fdf7f2 TL |
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); | |
7c673cae FG |
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)); | |
11fdf7f2 | 3057 | |
7c673cae | 3058 | ck_assert_str_eq(client_err, ""); |
11fdf7f2 TL |
3059 | ck_assert(client_conn != NULL); |
3060 | ||
7c673cae FG |
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 | ||
11fdf7f2 | 3069 | ck_assert_str_eq(client_ri->local_uri, "200"); |
7c673cae FG |
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)); | |
11fdf7f2 | 3091 | |
7c673cae | 3092 | ck_assert_str_eq(client_err, ""); |
11fdf7f2 TL |
3093 | ck_assert(client_conn != NULL); |
3094 | ||
7c673cae FG |
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 | ||
11fdf7f2 | 3103 | ck_assert_str_eq(client_ri->local_uri, "401"); |
7c673cae FG |
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 | ||
11fdf7f2 TL |
3172 | mg_close_connection(client_conn); |
3173 | ||
3174 | /* Retry with authorization */ | |
7c673cae FG |
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)); | |
11fdf7f2 | 3178 | |
7c673cae | 3179 | ck_assert_str_eq(client_err, ""); |
11fdf7f2 TL |
3180 | ck_assert(client_conn != NULL); |
3181 | ||
7c673cae FG |
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 | ||
11fdf7f2 | 3207 | ck_assert_str_eq(client_ri->local_uri, "200"); |
7c673cae FG |
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 | ||
11fdf7f2 TL |
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); | |
7c673cae FG |
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)); | |
11fdf7f2 | 3265 | |
7c673cae | 3266 | ck_assert_str_eq(client_err, ""); |
11fdf7f2 TL |
3267 | ck_assert(client_conn != NULL); |
3268 | ||
7c673cae FG |
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 | ||
11fdf7f2 | 3277 | ck_assert_str_eq(client_ri->local_uri, "401"); |
7c673cae FG |
3278 | mg_close_connection(client_conn); |
3279 | ||
3280 | test_sleep(1); | |
3281 | ||
3282 | ||
3283 | /* Now remove the password file */ | |
11fdf7f2 | 3284 | (void)remove(passwd_file); |
7c673cae FG |
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)); | |
11fdf7f2 | 3292 | |
7c673cae | 3293 | ck_assert_str_eq(client_err, ""); |
11fdf7f2 TL |
3294 | ck_assert(client_conn != NULL); |
3295 | ||
7c673cae FG |
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 | ||
11fdf7f2 | 3304 | ck_assert_str_eq(client_ri->local_uri, "200"); |
7c673cae FG |
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 */ | |
11fdf7f2 TL |
3315 | test_mg_stop(ctx); |
3316 | (void)remove(test_file); | |
3317 | (void)remove(passwd_file); | |
3318 | (void)remove("put_delete_auth_file.csv"); | |
7c673cae FG |
3319 | |
3320 | #endif | |
11fdf7f2 | 3321 | mark_point(); |
7c673cae FG |
3322 | } |
3323 | END_TEST | |
3324 | ||
3325 | ||
11fdf7f2 | 3326 | START_TEST(test_keep_alive) |
7c673cae | 3327 | { |
11fdf7f2 TL |
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 }; | |
7c673cae | 3343 | |
11fdf7f2 TL |
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; | |
7c673cae | 3349 | |
11fdf7f2 | 3350 | mark_point(); |
7c673cae | 3351 | |
11fdf7f2 | 3352 | ctx = test_mg_start(NULL, NULL, OPTIONS); |
7c673cae | 3353 | |
11fdf7f2 | 3354 | ck_assert(ctx != NULL); |
7c673cae | 3355 | |
11fdf7f2 TL |
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)); | |
7c673cae | 3360 | |
11fdf7f2 TL |
3361 | ck_assert_str_eq(client_err, ""); |
3362 | ck_assert(client_conn != NULL); | |
7c673cae | 3363 | |
11fdf7f2 TL |
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); | |
7c673cae | 3373 | |
11fdf7f2 TL |
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 | |
7c673cae | 3379 | |
11fdf7f2 TL |
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); | |
7c673cae | 3392 | |
11fdf7f2 | 3393 | test_sleep(1); |
7c673cae | 3394 | |
11fdf7f2 TL |
3395 | /* TODO: request a file and keep alive |
3396 | * (will only work if NO_FILES is not set). */ | |
7c673cae | 3397 | |
11fdf7f2 TL |
3398 | /* Stop the server and clean up */ |
3399 | test_mg_stop(ctx); | |
7c673cae | 3400 | |
11fdf7f2 TL |
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; | |
7c673cae FG |
4860 | |
4861 | ||
4862 | void | |
4863 | MAIN_PUBLIC_SERVER(void) | |
4864 | { | |
11fdf7f2 TL |
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 | ||
7c673cae | 4874 | test_mg_start_stop_http_server(0); |
11fdf7f2 | 4875 | test_mg_start_stop_https_server(0); |
7c673cae | 4876 | test_request_handlers(0); |
11fdf7f2 TL |
4877 | test_mg_store_body(0); |
4878 | test_mg_server_and_client_tls(0); | |
4879 | test_handle_form(0); | |
4880 | test_http_auth(0); | |
31f18b77 | 4881 | test_keep_alive(0); |
11fdf7f2 TL |
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(); | |
7c673cae FG |
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 |