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