diff options
author | Evgeny Grin (Karlson2k) <k2k@narod.ru> | 2022-04-25 22:52:27 +0300 |
---|---|---|
committer | Evgeny Grin (Karlson2k) <k2k@narod.ru> | 2022-04-25 22:52:27 +0300 |
commit | 54e123587e440e183eb5b7fbd6dcd1bebbc795dd (patch) | |
tree | 8f3afc1da46eca98fc8669b3b5e75c504fa14e01 | |
parent | efdd6e0ffbc597a95165bd328d3acd608cdcaa5b (diff) | |
download | libmicrohttpd-54e123587e440e183eb5b7fbd6dcd1bebbc795dd.tar.gz libmicrohttpd-54e123587e440e183eb5b7fbd6dcd1bebbc795dd.zip |
set_test_panic: added new test
-rw-r--r-- | README | 3 | ||||
-rw-r--r-- | src/microhttpd/Makefile.am | 8 | ||||
-rw-r--r-- | src/microhttpd/test_set_panic.c | 1672 |
3 files changed, 1680 insertions, 3 deletions
@@ -84,9 +84,8 @@ library stable, we should have testcases for the following features: | |||
84 | - MHD basic and digest authentication | 84 | - MHD basic and digest authentication |
85 | 85 | ||
86 | In particular, the following functions are not covered by 'make check': | 86 | In particular, the following functions are not covered by 'make check': |
87 | - mhd_panic_std (daemon.c); special case (abort) | 87 | - mhd_panic_std (mhd_panic.c); special case (abort) |
88 | - parse_options (daemon.c) | 88 | - parse_options (daemon.c) |
89 | - MHD_set_panic_func (daemon.c) | ||
90 | - MHD_get_version (daemon.c) | 89 | - MHD_get_version (daemon.c) |
91 | 90 | ||
92 | 91 | ||
diff --git a/src/microhttpd/Makefile.am b/src/microhttpd/Makefile.am index 6a08fe70..8db55a9a 100644 --- a/src/microhttpd/Makefile.am +++ b/src/microhttpd/Makefile.am | |||
@@ -207,7 +207,8 @@ check_PROGRAMS = \ | |||
207 | test_client_put_chunked_steps_shutdown \ | 207 | test_client_put_chunked_steps_shutdown \ |
208 | test_client_put_chunked_steps_close \ | 208 | test_client_put_chunked_steps_close \ |
209 | test_client_put_chunked_steps_hard_close \ | 209 | test_client_put_chunked_steps_hard_close \ |
210 | test_options | 210 | test_options \ |
211 | test_set_panic | ||
211 | 212 | ||
212 | if HAVE_POSIX_THREADS | 213 | if HAVE_POSIX_THREADS |
213 | if ENABLE_UPGRADE | 214 | if ENABLE_UPGRADE |
@@ -488,3 +489,8 @@ test_client_put_chunked_steps_hard_close_SOURCES = \ | |||
488 | test_client_put_stop.c | 489 | test_client_put_stop.c |
489 | test_client_put_chunked_steps_hard_close_LDADD = \ | 490 | test_client_put_chunked_steps_hard_close_LDADD = \ |
490 | libmicrohttpd.la | 491 | libmicrohttpd.la |
492 | |||
493 | test_set_panic_SOURCES = \ | ||
494 | test_set_panic.c | ||
495 | test_set_panic_LDADD = \ | ||
496 | libmicrohttpd.la | ||
diff --git a/src/microhttpd/test_set_panic.c b/src/microhttpd/test_set_panic.c new file mode 100644 index 00000000..2d81c620 --- /dev/null +++ b/src/microhttpd/test_set_panic.c | |||
@@ -0,0 +1,1672 @@ | |||
1 | /* | ||
2 | This file is part of libmicrohttpd | ||
3 | Copyright (C) 2021-2022 Evgeny Grin (Karlson2k) | ||
4 | |||
5 | libmicrohttpd is free software; you can redistribute it and/or modify | ||
6 | it under the terms of the GNU General Public License as published | ||
7 | by the Free Software Foundation; either version 2, or (at your | ||
8 | option) any later version. | ||
9 | |||
10 | libmicrohttpd is distributed in the hope that it will be useful, but | ||
11 | WITHOUT ANY WARRANTY; without even the implied warranty of | ||
12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
13 | General Public License for more details. | ||
14 | |||
15 | You should have received a copy of the GNU General Public License | ||
16 | along with libmicrohttpd; see the file COPYING. If not, write to the | ||
17 | Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, | ||
18 | Boston, MA 02110-1301, USA. | ||
19 | */ | ||
20 | /** | ||
21 | * @file test_set_panic.c | ||
22 | * @brief Testcase for MHD_set_panic_func() | ||
23 | * @author Karlson2k (Evgeny Grin) | ||
24 | * @author Christian Grothoff | ||
25 | */ | ||
26 | #include "MHD_config.h" | ||
27 | #include "platform.h" | ||
28 | #include <microhttpd.h> | ||
29 | #include <stdlib.h> | ||
30 | #include <string.h> | ||
31 | #include <time.h> | ||
32 | |||
33 | #ifdef HAVE_STRINGS_H | ||
34 | #include <strings.h> | ||
35 | #endif /* HAVE_STRINGS_H */ | ||
36 | |||
37 | #ifdef _WIN32 | ||
38 | #ifndef WIN32_LEAN_AND_MEAN | ||
39 | #define WIN32_LEAN_AND_MEAN 1 | ||
40 | #endif /* !WIN32_LEAN_AND_MEAN */ | ||
41 | #include <windows.h> | ||
42 | #endif | ||
43 | |||
44 | #ifndef WINDOWS | ||
45 | #include <unistd.h> | ||
46 | #include <sys/socket.h> | ||
47 | #endif | ||
48 | |||
49 | #ifdef HAVE_LIMITS_H | ||
50 | #include <limits.h> | ||
51 | #endif /* HAVE_LIMITS_H */ | ||
52 | |||
53 | #ifdef HAVE_SIGNAL_H | ||
54 | #include <signal.h> | ||
55 | #endif /* HAVE_SIGNAL_H */ | ||
56 | |||
57 | #ifdef HAVE_SYSCTL | ||
58 | #ifdef HAVE_SYS_TYPES_H | ||
59 | #include <sys/types.h> | ||
60 | #endif /* HAVE_SYS_TYPES_H */ | ||
61 | #ifdef HAVE_SYS_SYSCTL_H | ||
62 | #include <sys/sysctl.h> | ||
63 | #endif /* HAVE_SYS_SYSCTL_H */ | ||
64 | #ifdef HAVE_SYS_SOCKET_H | ||
65 | #include <sys/socket.h> | ||
66 | #endif /* HAVE_SYS_SOCKET_H */ | ||
67 | #ifdef HAVE_NETINET_IN_H | ||
68 | #include <netinet/in.h> | ||
69 | #endif /* HAVE_NETINET_IN_H */ | ||
70 | #ifdef HAVE_NETINET_IP_H | ||
71 | #include <netinet/ip.h> | ||
72 | #endif /* HAVE_NETINET_IP_H */ | ||
73 | #ifdef HAVE_NETINET_IP_ICMP_H | ||
74 | #include <netinet/ip_icmp.h> | ||
75 | #endif /* HAVE_NETINET_IP_ICMP_H */ | ||
76 | #ifdef HAVE_NETINET_ICMP_VAR_H | ||
77 | #include <netinet/icmp_var.h> | ||
78 | #endif /* HAVE_NETINET_ICMP_VAR_H */ | ||
79 | #endif /* HAVE_SYSCTL */ | ||
80 | |||
81 | #include <stdio.h> | ||
82 | |||
83 | #include "mhd_sockets.h" /* only macros used */ | ||
84 | #include "test_helpers.h" | ||
85 | #include "mhd_assert.h" | ||
86 | |||
87 | #if defined(MHD_CPU_COUNT) && (MHD_CPU_COUNT + 0) < 2 | ||
88 | #undef MHD_CPU_COUNT | ||
89 | #endif | ||
90 | #if ! defined(MHD_CPU_COUNT) | ||
91 | #define MHD_CPU_COUNT 2 | ||
92 | #endif | ||
93 | #if MHD_CPU_COUNT > 32 | ||
94 | #undef MHD_CPU_COUNT | ||
95 | /* Limit to reasonable value */ | ||
96 | #define MHD_CPU_COUNT 32 | ||
97 | #endif /* MHD_CPU_COUNT > 32 */ | ||
98 | |||
99 | #ifndef MHD_STATICSTR_LEN_ | ||
100 | /** | ||
101 | * Determine length of static string / macro strings at compile time. | ||
102 | */ | ||
103 | #define MHD_STATICSTR_LEN_(macro) (sizeof(macro) / sizeof(char) - 1) | ||
104 | #endif /* ! MHD_STATICSTR_LEN_ */ | ||
105 | |||
106 | #ifndef _MHD_INSTRMACRO | ||
107 | /* Quoted macro parameter */ | ||
108 | #define _MHD_INSTRMACRO(a) #a | ||
109 | #endif /* ! _MHD_INSTRMACRO */ | ||
110 | #ifndef _MHD_STRMACRO | ||
111 | /* Quoted expanded macro parameter */ | ||
112 | #define _MHD_STRMACRO(a) _MHD_INSTRMACRO (a) | ||
113 | #endif /* ! _MHD_STRMACRO */ | ||
114 | |||
115 | |||
116 | /* Could be increased to facilitate debugging */ | ||
117 | #define TIMEOUTS_VAL 5 | ||
118 | |||
119 | /* Time in ms to wait for final packets to be delivered */ | ||
120 | #define FINAL_PACKETS_MS 20 | ||
121 | |||
122 | #define EXPECTED_URI_BASE_PATH "/a" | ||
123 | |||
124 | #define REQ_HOST "localhost" | ||
125 | |||
126 | #define REQ_METHOD "PUT" | ||
127 | |||
128 | #define REQ_BODY "Some content data." | ||
129 | |||
130 | #define REQ_LINE_END "\r\n" | ||
131 | |||
132 | /* Mandatory request headers */ | ||
133 | #define REQ_HEADER_HOST_NAME "Host" | ||
134 | #define REQ_HEADER_HOST_VALUE REQ_HOST | ||
135 | #define REQ_HEADER_HOST \ | ||
136 | REQ_HEADER_HOST_NAME ": " REQ_HEADER_HOST_VALUE REQ_LINE_END | ||
137 | #define REQ_HEADER_UA_NAME "User-Agent" | ||
138 | #define REQ_HEADER_UA_VALUE "dummyclient/0.9" | ||
139 | #define REQ_HEADER_UA REQ_HEADER_UA_NAME ": " REQ_HEADER_UA_VALUE REQ_LINE_END | ||
140 | |||
141 | /* Optional request headers */ | ||
142 | #define REQ_HEADER_CT_NAME "Content-Type" | ||
143 | #define REQ_HEADER_CT_VALUE "text/plain" | ||
144 | #define REQ_HEADER_CT REQ_HEADER_CT_NAME ": " REQ_HEADER_CT_VALUE REQ_LINE_END | ||
145 | |||
146 | |||
147 | #if defined(HAVE___FUNC__) | ||
148 | #define externalErrorExit(ignore) \ | ||
149 | _externalErrorExit_func(NULL, __func__, __LINE__) | ||
150 | #define externalErrorExitDesc(errDesc) \ | ||
151 | _externalErrorExit_func(errDesc, __func__, __LINE__) | ||
152 | #define mhdErrorExit(ignore) \ | ||
153 | _mhdErrorExit_func(NULL, __func__, __LINE__) | ||
154 | #define mhdErrorExitDesc(errDesc) \ | ||
155 | _mhdErrorExit_func(errDesc, __func__, __LINE__) | ||
156 | #elif defined(HAVE___FUNCTION__) | ||
157 | #define externalErrorExit(ignore) \ | ||
158 | _externalErrorExit_func(NULL, __FUNCTION__, __LINE__) | ||
159 | #define externalErrorExitDesc(errDesc) \ | ||
160 | _externalErrorExit_func(errDesc, __FUNCTION__, __LINE__) | ||
161 | #define mhdErrorExit(ignore) \ | ||
162 | _mhdErrorExit_func(NULL, __FUNCTION__, __LINE__) | ||
163 | #define mhdErrorExitDesc(errDesc) \ | ||
164 | _mhdErrorExit_func(errDesc, __FUNCTION__, __LINE__) | ||
165 | #else | ||
166 | #define externalErrorExit(ignore) _externalErrorExit_func(NULL, NULL, __LINE__) | ||
167 | #define externalErrorExitDesc(errDesc) \ | ||
168 | _externalErrorExit_func(errDesc, NULL, __LINE__) | ||
169 | #define mhdErrorExit(ignore) _mhdErrorExit_func(NULL, NULL, __LINE__) | ||
170 | #define mhdErrorExitDesc(errDesc) _mhdErrorExit_func(errDesc, NULL, __LINE__) | ||
171 | #endif | ||
172 | |||
173 | |||
174 | _MHD_NORETURN static void | ||
175 | _externalErrorExit_func (const char *errDesc, const char *funcName, int lineNum) | ||
176 | { | ||
177 | if ((NULL != errDesc) && (0 != errDesc[0])) | ||
178 | fprintf (stderr, "%s", errDesc); | ||
179 | else | ||
180 | fprintf (stderr, "System or external library call failed"); | ||
181 | if ((NULL != funcName) && (0 != funcName[0])) | ||
182 | fprintf (stderr, " in %s", funcName); | ||
183 | if (0 < lineNum) | ||
184 | fprintf (stderr, " at line %d", lineNum); | ||
185 | |||
186 | fprintf (stderr, ".\nLast errno value: %d (%s)\n", (int) errno, | ||
187 | strerror (errno)); | ||
188 | #ifdef MHD_WINSOCK_SOCKETS | ||
189 | fprintf (stderr, "WSAGetLastError() value: %d\n", (int) WSAGetLastError ()); | ||
190 | #endif /* MHD_WINSOCK_SOCKETS */ | ||
191 | fflush (stderr); | ||
192 | exit (99); | ||
193 | } | ||
194 | |||
195 | |||
196 | _MHD_NORETURN static void | ||
197 | _mhdErrorExit_func (const char *errDesc, const char *funcName, int lineNum) | ||
198 | { | ||
199 | if ((NULL != errDesc) && (0 != errDesc[0])) | ||
200 | fprintf (stderr, "%s", errDesc); | ||
201 | else | ||
202 | fprintf (stderr, "MHD unexpected error"); | ||
203 | if ((NULL != funcName) && (0 != funcName[0])) | ||
204 | fprintf (stderr, " in %s", funcName); | ||
205 | if (0 < lineNum) | ||
206 | fprintf (stderr, " at line %d", lineNum); | ||
207 | |||
208 | fprintf (stderr, ".\nLast errno value: %d (%s)\n", (int) errno, | ||
209 | strerror (errno)); | ||
210 | |||
211 | fflush (stderr); | ||
212 | exit (8); | ||
213 | } | ||
214 | |||
215 | |||
216 | /** | ||
217 | * Pause execution for specified number of milliseconds. | ||
218 | * @param ms the number of milliseconds to sleep | ||
219 | */ | ||
220 | static void | ||
221 | _MHD_sleep (uint32_t ms) | ||
222 | { | ||
223 | #if defined(_WIN32) | ||
224 | Sleep (ms); | ||
225 | #elif defined(HAVE_NANOSLEEP) | ||
226 | struct timespec slp = {ms / 1000, (ms % 1000) * 1000000}; | ||
227 | struct timespec rmn; | ||
228 | int num_retries = 0; | ||
229 | while (0 != nanosleep (&slp, &rmn)) | ||
230 | { | ||
231 | if (EINTR != errno) | ||
232 | externalErrorExit (); | ||
233 | if (num_retries++ > 8) | ||
234 | break; | ||
235 | slp = rmn; | ||
236 | } | ||
237 | #elif defined(HAVE_USLEEP) | ||
238 | uint64_t us = ms * 1000; | ||
239 | do | ||
240 | { | ||
241 | uint64_t this_sleep; | ||
242 | if (999999 < us) | ||
243 | this_sleep = 999999; | ||
244 | else | ||
245 | this_sleep = us; | ||
246 | /* Ignore return value as it could be void */ | ||
247 | usleep (this_sleep); | ||
248 | us -= this_sleep; | ||
249 | } while (us > 0); | ||
250 | #else | ||
251 | externalErrorExitDesc ("No sleep function available on this system"); | ||
252 | #endif | ||
253 | } | ||
254 | |||
255 | |||
256 | /* Global parameters */ | ||
257 | static int verbose; /**< Be verbose */ | ||
258 | static uint16_t global_port; /**< MHD daemons listen port number */ | ||
259 | |||
260 | static void | ||
261 | test_global_init (void) | ||
262 | { | ||
263 | if (MHD_YES != MHD_is_feature_supported (MHD_FEATURE_AUTOSUPPRESS_SIGPIPE)) | ||
264 | { | ||
265 | #if defined(HAVE_SIGNAL_H) && defined(SIGPIPE) | ||
266 | if (SIG_ERR == signal (SIGPIPE, SIG_IGN)) | ||
267 | externalErrorExitDesc ("Error suppressing SIGPIPE signal"); | ||
268 | #else /* ! HAVE_SIGNAL_H || ! SIGPIPE */ | ||
269 | fprintf (stderr, "Cannot suppress SIGPIPE signal.\n"); | ||
270 | /* exit (77); */ | ||
271 | #endif | ||
272 | } | ||
273 | } | ||
274 | |||
275 | |||
276 | static void | ||
277 | test_global_cleanup (void) | ||
278 | { | ||
279 | } | ||
280 | |||
281 | |||
282 | /** | ||
283 | * Change socket to blocking. | ||
284 | * | ||
285 | * @param fd the socket to manipulate | ||
286 | */ | ||
287 | static void | ||
288 | make_blocking (MHD_socket fd) | ||
289 | { | ||
290 | #if defined(MHD_POSIX_SOCKETS) | ||
291 | int flags; | ||
292 | |||
293 | flags = fcntl (fd, F_GETFL); | ||
294 | if (-1 == flags) | ||
295 | externalErrorExitDesc ("Cannot make socket non-blocking"); | ||
296 | if ((flags & ~O_NONBLOCK) != flags) | ||
297 | { | ||
298 | if (-1 == fcntl (fd, F_SETFL, flags & ~O_NONBLOCK)) | ||
299 | externalErrorExitDesc ("Cannot make socket non-blocking"); | ||
300 | } | ||
301 | #elif defined(MHD_WINSOCK_SOCKETS) | ||
302 | unsigned long flags = 0; | ||
303 | |||
304 | if (0 != ioctlsocket (fd, (int) FIONBIO, &flags)) | ||
305 | externalErrorExitDesc ("Cannot make socket non-blocking"); | ||
306 | #endif /* MHD_WINSOCK_SOCKETS */ | ||
307 | } | ||
308 | |||
309 | |||
310 | /** | ||
311 | * Change socket to non-blocking. | ||
312 | * | ||
313 | * @param fd the socket to manipulate | ||
314 | */ | ||
315 | static void | ||
316 | make_nonblocking (MHD_socket fd) | ||
317 | { | ||
318 | #if defined(MHD_POSIX_SOCKETS) | ||
319 | int flags; | ||
320 | |||
321 | flags = fcntl (fd, F_GETFL); | ||
322 | if (-1 == flags) | ||
323 | externalErrorExitDesc ("Cannot make socket non-blocking"); | ||
324 | if ((flags | O_NONBLOCK) != flags) | ||
325 | { | ||
326 | if (-1 == fcntl (fd, F_SETFL, flags | O_NONBLOCK)) | ||
327 | externalErrorExitDesc ("Cannot make socket non-blocking"); | ||
328 | } | ||
329 | #elif defined(MHD_WINSOCK_SOCKETS) | ||
330 | unsigned long flags = 1; | ||
331 | |||
332 | if (0 != ioctlsocket (fd, (int) FIONBIO, &flags)) | ||
333 | externalErrorExitDesc ("Cannot make socket non-blocking"); | ||
334 | #endif /* MHD_WINSOCK_SOCKETS */ | ||
335 | } | ||
336 | |||
337 | |||
338 | enum _MHD_clientStage | ||
339 | { | ||
340 | DUMB_CLIENT_INIT = 0, | ||
341 | DUMB_CLIENT_CONNECTING, | ||
342 | DUMB_CLIENT_CONNECTED, | ||
343 | DUMB_CLIENT_REQ_SENDING, | ||
344 | DUMB_CLIENT_REQ_SENT, | ||
345 | DUMB_CLIENT_HEADER_RECVEIVING, | ||
346 | DUMB_CLIENT_HEADER_RECVEIVED, | ||
347 | DUMB_CLIENT_BODY_RECVEIVING, | ||
348 | DUMB_CLIENT_BODY_RECVEIVED, | ||
349 | DUMB_CLIENT_FINISHING, | ||
350 | DUMB_CLIENT_FINISHED | ||
351 | }; | ||
352 | |||
353 | struct _MHD_dumbClient | ||
354 | { | ||
355 | MHD_socket sckt; /**< the socket to communicate */ | ||
356 | |||
357 | int sckt_nonblock; /**< non-zero if socket is non-blocking */ | ||
358 | |||
359 | uint16_t port; /**< the port to connect to */ | ||
360 | |||
361 | char *send_buf; /**< the buffer for the request, malloced */ | ||
362 | |||
363 | size_t req_size; /**< the size of the request, including header */ | ||
364 | |||
365 | size_t send_off; /**< the number of bytes already sent */ | ||
366 | |||
367 | enum _MHD_clientStage stage; | ||
368 | |||
369 | /* the test-specific variables */ | ||
370 | size_t single_send_size; /**< the maximum number of bytes to be sent by | ||
371 | single send() */ | ||
372 | size_t send_size_limit; /**< the total number of send bytes limit */ | ||
373 | }; | ||
374 | |||
375 | struct _MHD_dumbClient * | ||
376 | _MHD_dumbClient_create (uint16_t port, const char *method, const char *url, | ||
377 | const char *add_headers, | ||
378 | const uint8_t *req_body, size_t req_body_size, | ||
379 | int chunked); | ||
380 | |||
381 | |||
382 | void | ||
383 | _MHD_dumbClient_set_send_limits (struct _MHD_dumbClient *clnt, | ||
384 | size_t step_size, size_t max_total_send); | ||
385 | |||
386 | void | ||
387 | _MHD_dumbClient_start_connect (struct _MHD_dumbClient *clnt); | ||
388 | |||
389 | int | ||
390 | _MHD_dumbClient_is_req_sent (struct _MHD_dumbClient *clnt); | ||
391 | |||
392 | int | ||
393 | _MHD_dumbClient_process (struct _MHD_dumbClient *clnt); | ||
394 | |||
395 | void | ||
396 | _MHD_dumbClient_get_fdsets (struct _MHD_dumbClient *clnt, | ||
397 | MHD_socket *maxsckt, | ||
398 | fd_set *rs, fd_set *ws, fd_set *es); | ||
399 | |||
400 | |||
401 | /** | ||
402 | * Process the client data with send()/recv() as needed based on | ||
403 | * information in fd_sets. | ||
404 | * @param clnt the client to process | ||
405 | * @return non-zero if client finished processing the request, | ||
406 | * zero otherwise. | ||
407 | */ | ||
408 | int | ||
409 | _MHD_dumbClient_process_from_fdsets (struct _MHD_dumbClient *clnt, | ||
410 | fd_set *rs, fd_set *ws, fd_set *es); | ||
411 | |||
412 | /** | ||
413 | * Perform full request. | ||
414 | * @param clnt the client to run | ||
415 | * @return zero if client finished processing the request, | ||
416 | * non-zero if timeout is reached. | ||
417 | */ | ||
418 | int | ||
419 | _MHD_dumbClient_perform (struct _MHD_dumbClient *clnt); | ||
420 | |||
421 | |||
422 | /** | ||
423 | * Close the client and free internally allocated resources. | ||
424 | * @param clnt the client to close | ||
425 | */ | ||
426 | void | ||
427 | _MHD_dumbClient_close (struct _MHD_dumbClient *clnt); | ||
428 | |||
429 | |||
430 | struct _MHD_dumbClient * | ||
431 | _MHD_dumbClient_create (uint16_t port, const char *method, const char *url, | ||
432 | const char *add_headers, | ||
433 | const uint8_t *req_body, size_t req_body_size, | ||
434 | int chunked) | ||
435 | { | ||
436 | struct _MHD_dumbClient *clnt; | ||
437 | size_t method_size; | ||
438 | size_t url_size; | ||
439 | size_t add_hdrs_size; | ||
440 | size_t buf_alloc_size; | ||
441 | char *send_buf; | ||
442 | mhd_assert (0 != port); | ||
443 | mhd_assert (NULL != req_body || 0 == req_body_size); | ||
444 | mhd_assert (0 == req_body_size || NULL != req_body); | ||
445 | |||
446 | clnt = (struct _MHD_dumbClient *) malloc (sizeof(struct _MHD_dumbClient)); | ||
447 | if (NULL == clnt) | ||
448 | externalErrorExit (); | ||
449 | memset (clnt, 0, sizeof(struct _MHD_dumbClient)); | ||
450 | clnt->sckt = socket (AF_INET, SOCK_STREAM, IPPROTO_TCP); | ||
451 | if (MHD_INVALID_SOCKET == clnt->sckt) | ||
452 | externalErrorExitDesc ("Cannot create the client socket"); | ||
453 | |||
454 | #ifdef MHD_socket_nosignal_ | ||
455 | if (! MHD_socket_nosignal_ (clnt->sckt)) | ||
456 | externalErrorExitDesc ("Cannot suppress SIGPIPE on the client socket"); | ||
457 | #endif /* MHD_socket_nosignal_ */ | ||
458 | |||
459 | clnt->sckt_nonblock = 0; | ||
460 | if (clnt->sckt_nonblock) | ||
461 | make_nonblocking (clnt->sckt); | ||
462 | else | ||
463 | make_blocking (clnt->sckt); | ||
464 | |||
465 | if (1) | ||
466 | { /* Always set TCP NODELAY */ | ||
467 | const MHD_SCKT_OPT_BOOL_ on_val = 1; | ||
468 | |||
469 | if (0 != setsockopt (clnt->sckt, IPPROTO_TCP, TCP_NODELAY, | ||
470 | (const void *) &on_val, sizeof (on_val))) | ||
471 | externalErrorExitDesc ("Cannot set TCP_NODELAY option"); | ||
472 | } | ||
473 | |||
474 | clnt->port = port; | ||
475 | |||
476 | if (NULL != method) | ||
477 | method_size = strlen (method); | ||
478 | else | ||
479 | { | ||
480 | method = MHD_HTTP_METHOD_GET; | ||
481 | method_size = MHD_STATICSTR_LEN_ (MHD_HTTP_METHOD_GET); | ||
482 | } | ||
483 | mhd_assert (0 != method_size); | ||
484 | if (NULL != url) | ||
485 | url_size = strlen (url); | ||
486 | else | ||
487 | { | ||
488 | url = "/"; | ||
489 | url_size = 1; | ||
490 | } | ||
491 | mhd_assert (0 != url_size); | ||
492 | add_hdrs_size = (NULL == add_headers) ? 0 : strlen (add_headers); | ||
493 | buf_alloc_size = 1024 + method_size + url_size | ||
494 | + add_hdrs_size + req_body_size; | ||
495 | send_buf = (char *) malloc (buf_alloc_size); | ||
496 | if (NULL == send_buf) | ||
497 | externalErrorExit (); | ||
498 | |||
499 | clnt->req_size = 0; | ||
500 | /* Form the request line */ | ||
501 | memcpy (send_buf + clnt->req_size, method, method_size); | ||
502 | clnt->req_size += method_size; | ||
503 | send_buf[clnt->req_size++] = ' '; | ||
504 | memcpy (send_buf + clnt->req_size, url, url_size); | ||
505 | clnt->req_size += url_size; | ||
506 | send_buf[clnt->req_size++] = ' '; | ||
507 | memcpy (send_buf + clnt->req_size, MHD_HTTP_VERSION_1_1, | ||
508 | MHD_STATICSTR_LEN_ (MHD_HTTP_VERSION_1_1)); | ||
509 | clnt->req_size += MHD_STATICSTR_LEN_ (MHD_HTTP_VERSION_1_1); | ||
510 | send_buf[clnt->req_size++] = '\r'; | ||
511 | send_buf[clnt->req_size++] = '\n'; | ||
512 | /* Form the header */ | ||
513 | memcpy (send_buf + clnt->req_size, REQ_HEADER_HOST, | ||
514 | MHD_STATICSTR_LEN_ (REQ_HEADER_HOST)); | ||
515 | clnt->req_size += MHD_STATICSTR_LEN_ (REQ_HEADER_HOST); | ||
516 | memcpy (send_buf + clnt->req_size, REQ_HEADER_UA, | ||
517 | MHD_STATICSTR_LEN_ (REQ_HEADER_UA)); | ||
518 | clnt->req_size += MHD_STATICSTR_LEN_ (REQ_HEADER_UA); | ||
519 | if ((NULL != req_body) || chunked) | ||
520 | { | ||
521 | if (! chunked) | ||
522 | { | ||
523 | int prn_size; | ||
524 | memcpy (send_buf + clnt->req_size, MHD_HTTP_HEADER_CONTENT_LENGTH ": ", | ||
525 | MHD_STATICSTR_LEN_ (MHD_HTTP_HEADER_CONTENT_LENGTH ": ")); | ||
526 | clnt->req_size += MHD_STATICSTR_LEN_ ( | ||
527 | MHD_HTTP_HEADER_CONTENT_LENGTH ": "); | ||
528 | prn_size = snprintf (send_buf + clnt->req_size, | ||
529 | (buf_alloc_size - clnt->req_size), | ||
530 | "%u", (unsigned int) req_body_size); | ||
531 | if (0 >= prn_size) | ||
532 | externalErrorExit (); | ||
533 | if ((unsigned int) prn_size >= buf_alloc_size - clnt->req_size) | ||
534 | externalErrorExit (); | ||
535 | clnt->req_size += (unsigned int) prn_size; | ||
536 | send_buf[clnt->req_size++] = '\r'; | ||
537 | send_buf[clnt->req_size++] = '\n'; | ||
538 | } | ||
539 | else | ||
540 | { | ||
541 | memcpy (send_buf + clnt->req_size, | ||
542 | MHD_HTTP_HEADER_TRANSFER_ENCODING ": chunked\r\n", | ||
543 | MHD_STATICSTR_LEN_ (MHD_HTTP_HEADER_TRANSFER_ENCODING \ | ||
544 | ": chunked\r\n")); | ||
545 | clnt->req_size += MHD_STATICSTR_LEN_ (MHD_HTTP_HEADER_TRANSFER_ENCODING \ | ||
546 | ": chunked\r\n"); | ||
547 | } | ||
548 | } | ||
549 | if (0 != add_hdrs_size) | ||
550 | { | ||
551 | memcpy (send_buf + clnt->req_size, add_headers, add_hdrs_size); | ||
552 | clnt->req_size += add_hdrs_size; | ||
553 | } | ||
554 | /* Terminate header */ | ||
555 | send_buf[clnt->req_size++] = '\r'; | ||
556 | send_buf[clnt->req_size++] = '\n'; | ||
557 | |||
558 | /* Add body (if any) */ | ||
559 | if (! chunked) | ||
560 | { | ||
561 | if (0 != req_body_size) | ||
562 | { | ||
563 | memcpy (send_buf + clnt->req_size, req_body, req_body_size); | ||
564 | clnt->req_size += req_body_size; | ||
565 | } | ||
566 | } | ||
567 | else | ||
568 | { | ||
569 | if (0 != req_body_size) | ||
570 | { | ||
571 | int prn_size; | ||
572 | prn_size = snprintf (send_buf + clnt->req_size, | ||
573 | (buf_alloc_size - clnt->req_size), | ||
574 | "%x", (unsigned int) req_body_size); | ||
575 | if (0 >= prn_size) | ||
576 | externalErrorExit (); | ||
577 | if ((unsigned int) prn_size >= buf_alloc_size - clnt->req_size) | ||
578 | externalErrorExit (); | ||
579 | clnt->req_size += (unsigned int) prn_size; | ||
580 | send_buf[clnt->req_size++] = '\r'; | ||
581 | send_buf[clnt->req_size++] = '\n'; | ||
582 | memcpy (send_buf + clnt->req_size, req_body, req_body_size); | ||
583 | clnt->req_size += req_body_size; | ||
584 | send_buf[clnt->req_size++] = '\r'; | ||
585 | send_buf[clnt->req_size++] = '\n'; | ||
586 | } | ||
587 | send_buf[clnt->req_size++] = '0'; | ||
588 | send_buf[clnt->req_size++] = '\r'; | ||
589 | send_buf[clnt->req_size++] = '\n'; | ||
590 | send_buf[clnt->req_size++] = '\r'; | ||
591 | send_buf[clnt->req_size++] = '\n'; | ||
592 | } | ||
593 | mhd_assert (clnt->req_size < buf_alloc_size); | ||
594 | clnt->send_buf = send_buf; | ||
595 | |||
596 | return clnt; | ||
597 | } | ||
598 | |||
599 | |||
600 | void | ||
601 | _MHD_dumbClient_set_send_limits (struct _MHD_dumbClient *clnt, | ||
602 | size_t step_size, size_t max_total_send) | ||
603 | { | ||
604 | clnt->single_send_size = step_size; | ||
605 | clnt->send_size_limit = max_total_send; | ||
606 | } | ||
607 | |||
608 | |||
609 | /* internal */ | ||
610 | static void | ||
611 | _MHD_dumbClient_connect_init (struct _MHD_dumbClient *clnt) | ||
612 | { | ||
613 | struct sockaddr_in sa; | ||
614 | mhd_assert (DUMB_CLIENT_INIT == clnt->stage); | ||
615 | |||
616 | sa.sin_family = AF_INET; | ||
617 | sa.sin_port = htons (clnt->port); | ||
618 | sa.sin_addr.s_addr = htonl (INADDR_LOOPBACK); | ||
619 | |||
620 | if (0 != connect (clnt->sckt, (struct sockaddr *) &sa, sizeof(sa))) | ||
621 | { | ||
622 | const int err = MHD_socket_get_error_ (); | ||
623 | if ( (MHD_SCKT_ERR_IS_ (err, MHD_SCKT_EINPROGRESS_)) || | ||
624 | (MHD_SCKT_ERR_IS_EAGAIN_ (err))) | ||
625 | clnt->stage = DUMB_CLIENT_CONNECTING; | ||
626 | else | ||
627 | externalErrorExitDesc ("Cannot 'connect()' the client socket"); | ||
628 | } | ||
629 | else | ||
630 | clnt->stage = DUMB_CLIENT_CONNECTED; | ||
631 | } | ||
632 | |||
633 | |||
634 | void | ||
635 | _MHD_dumbClient_start_connect (struct _MHD_dumbClient *clnt) | ||
636 | { | ||
637 | mhd_assert (DUMB_CLIENT_INIT == clnt->stage); | ||
638 | _MHD_dumbClient_connect_init (clnt); | ||
639 | } | ||
640 | |||
641 | |||
642 | /* internal */ | ||
643 | static void | ||
644 | _MHD_dumbClient_connect_finish (struct _MHD_dumbClient *clnt) | ||
645 | { | ||
646 | int err = 0; | ||
647 | socklen_t err_size = sizeof(err); | ||
648 | mhd_assert (DUMB_CLIENT_CONNECTING == clnt->stage); | ||
649 | if (0 != getsockopt (clnt->sckt, SOL_SOCKET, SO_ERROR, | ||
650 | (void *) &err, &err_size)) | ||
651 | externalErrorExitDesc ("'getsockopt()' call failed"); | ||
652 | if (0 != err) | ||
653 | externalErrorExitDesc ("Socket connect() failed"); | ||
654 | clnt->stage = DUMB_CLIENT_CONNECTED; | ||
655 | } | ||
656 | |||
657 | |||
658 | /* internal */ | ||
659 | static void | ||
660 | _MHD_dumbClient_send_req (struct _MHD_dumbClient *clnt) | ||
661 | { | ||
662 | size_t send_size; | ||
663 | ssize_t res; | ||
664 | mhd_assert (DUMB_CLIENT_CONNECTED <= clnt->stage); | ||
665 | mhd_assert (DUMB_CLIENT_REQ_SENT > clnt->stage); | ||
666 | mhd_assert (clnt->req_size > clnt->send_off); | ||
667 | |||
668 | send_size = (((0 != clnt->send_size_limit) && | ||
669 | (clnt->req_size > clnt->send_size_limit)) ? | ||
670 | clnt->send_size_limit : clnt->req_size) - clnt->send_off; | ||
671 | mhd_assert (0 != send_size); | ||
672 | if ((0 != clnt->single_send_size) && | ||
673 | (clnt->single_send_size < send_size)) | ||
674 | send_size = clnt->single_send_size; | ||
675 | |||
676 | res = MHD_send_ (clnt->sckt, clnt->send_buf + clnt->send_off, send_size); | ||
677 | |||
678 | if (res < 0) | ||
679 | { | ||
680 | const int err = MHD_socket_get_error_ (); | ||
681 | if (MHD_SCKT_ERR_IS_EAGAIN_ (err)) | ||
682 | return; | ||
683 | if (MHD_SCKT_ERR_IS_EINTR_ (err)) | ||
684 | return; | ||
685 | if (MHD_SCKT_ERR_IS_REMOTE_DISCNN_ (err)) | ||
686 | mhdErrorExitDesc ("The connection was aborted by MHD"); | ||
687 | if (MHD_SCKT_ERR_IS_ (err, MHD_SCKT_EPIPE_)) | ||
688 | mhdErrorExitDesc ("The connection was shut down on MHD side"); | ||
689 | externalErrorExitDesc ("Unexpected network error"); | ||
690 | } | ||
691 | clnt->send_off += (size_t) res; | ||
692 | mhd_assert (clnt->send_off <= clnt->req_size); | ||
693 | mhd_assert (clnt->send_off <= clnt->send_size_limit || \ | ||
694 | 0 == clnt->send_size_limit); | ||
695 | if (clnt->req_size == clnt->send_off) | ||
696 | clnt->stage = DUMB_CLIENT_REQ_SENT; | ||
697 | if ((0 != clnt->send_size_limit) && | ||
698 | (clnt->send_size_limit == clnt->send_off)) | ||
699 | clnt->stage = DUMB_CLIENT_FINISHING; | ||
700 | } | ||
701 | |||
702 | |||
703 | /* internal */ | ||
704 | static void | ||
705 | _MHD_dumbClient_recv_reply (struct _MHD_dumbClient *clnt) | ||
706 | { | ||
707 | (void) clnt; | ||
708 | externalErrorExitDesc ("Not implemented for this test"); | ||
709 | } | ||
710 | |||
711 | |||
712 | int | ||
713 | _MHD_dumbClient_is_req_sent (struct _MHD_dumbClient *clnt) | ||
714 | { | ||
715 | return DUMB_CLIENT_REQ_SENT <= clnt->stage; | ||
716 | } | ||
717 | |||
718 | |||
719 | /* internal */ | ||
720 | static void | ||
721 | _MHD_dumbClient_socket_close (struct _MHD_dumbClient *clnt) | ||
722 | { | ||
723 | if (MHD_INVALID_SOCKET != clnt->sckt) | ||
724 | { | ||
725 | if (! MHD_socket_close_ (clnt->sckt)) | ||
726 | externalErrorExitDesc ("Unexpected error while closing " \ | ||
727 | "the client socket"); | ||
728 | clnt->sckt = MHD_INVALID_SOCKET; | ||
729 | } | ||
730 | } | ||
731 | |||
732 | |||
733 | /* internal */ | ||
734 | static void | ||
735 | _MHD_dumbClient_finalize (struct _MHD_dumbClient *clnt) | ||
736 | { | ||
737 | if (MHD_INVALID_SOCKET != clnt->sckt) | ||
738 | { | ||
739 | if (0 != shutdown (clnt->sckt, SHUT_WR)) | ||
740 | { | ||
741 | const int err = MHD_socket_get_error_ (); | ||
742 | if (! MHD_SCKT_ERR_IS_ (err, MHD_SCKT_ENOTCONN_) && | ||
743 | ! MHD_SCKT_ERR_IS_REMOTE_DISCNN_ (err)) | ||
744 | mhdErrorExitDesc ("Unexpected error when shutting down " \ | ||
745 | "the client socket"); | ||
746 | } | ||
747 | } | ||
748 | clnt->stage = DUMB_CLIENT_FINISHED; | ||
749 | } | ||
750 | |||
751 | |||
752 | /* internal */ | ||
753 | static int | ||
754 | _MHD_dumbClient_needs_send (const struct _MHD_dumbClient *clnt) | ||
755 | { | ||
756 | return ((DUMB_CLIENT_CONNECTING <= clnt->stage) && | ||
757 | (DUMB_CLIENT_REQ_SENT > clnt->stage)) || | ||
758 | (DUMB_CLIENT_FINISHING == clnt->stage); | ||
759 | } | ||
760 | |||
761 | |||
762 | /* internal */ | ||
763 | static int | ||
764 | _MHD_dumbClient_needs_recv (const struct _MHD_dumbClient *clnt) | ||
765 | { | ||
766 | return (DUMB_CLIENT_HEADER_RECVEIVING <= clnt->stage) && | ||
767 | (DUMB_CLIENT_BODY_RECVEIVED > clnt->stage); | ||
768 | } | ||
769 | |||
770 | |||
771 | /* internal */ | ||
772 | /** | ||
773 | * Check whether the client needs unconditionally process the data. | ||
774 | * @param clnt the client to check | ||
775 | * @return non-zero if client needs unconditionally process the data, | ||
776 | * zero otherwise. | ||
777 | */ | ||
778 | static int | ||
779 | _MHD_dumbClient_needs_process (const struct _MHD_dumbClient *clnt) | ||
780 | { | ||
781 | switch (clnt->stage) | ||
782 | { | ||
783 | case DUMB_CLIENT_INIT: | ||
784 | case DUMB_CLIENT_REQ_SENT: | ||
785 | case DUMB_CLIENT_HEADER_RECVEIVED: | ||
786 | case DUMB_CLIENT_BODY_RECVEIVED: | ||
787 | return ! 0; | ||
788 | default: | ||
789 | return 0; | ||
790 | } | ||
791 | return 0; /* Should be unreachable */ | ||
792 | } | ||
793 | |||
794 | |||
795 | /** | ||
796 | * Process the client data with send()/recv() as needed. | ||
797 | * @param clnt the client to process | ||
798 | * @return non-zero if client finished processing the request, | ||
799 | * zero otherwise. | ||
800 | */ | ||
801 | int | ||
802 | _MHD_dumbClient_process (struct _MHD_dumbClient *clnt) | ||
803 | { | ||
804 | do | ||
805 | { | ||
806 | switch (clnt->stage) | ||
807 | { | ||
808 | case DUMB_CLIENT_INIT: | ||
809 | _MHD_dumbClient_connect_init (clnt); | ||
810 | break; | ||
811 | case DUMB_CLIENT_CONNECTING: | ||
812 | _MHD_dumbClient_connect_finish (clnt); | ||
813 | break; | ||
814 | case DUMB_CLIENT_CONNECTED: | ||
815 | case DUMB_CLIENT_REQ_SENDING: | ||
816 | _MHD_dumbClient_send_req (clnt); | ||
817 | break; | ||
818 | case DUMB_CLIENT_REQ_SENT: | ||
819 | mhd_assert (0); | ||
820 | clnt->stage = DUMB_CLIENT_HEADER_RECVEIVING; | ||
821 | break; | ||
822 | case DUMB_CLIENT_HEADER_RECVEIVING: | ||
823 | _MHD_dumbClient_recv_reply (clnt); | ||
824 | break; | ||
825 | case DUMB_CLIENT_HEADER_RECVEIVED: | ||
826 | clnt->stage = DUMB_CLIENT_BODY_RECVEIVING; | ||
827 | break; | ||
828 | case DUMB_CLIENT_BODY_RECVEIVING: | ||
829 | _MHD_dumbClient_recv_reply (clnt); | ||
830 | break; | ||
831 | case DUMB_CLIENT_BODY_RECVEIVED: | ||
832 | clnt->stage = DUMB_CLIENT_FINISHING; | ||
833 | break; | ||
834 | case DUMB_CLIENT_FINISHING: | ||
835 | _MHD_dumbClient_finalize (clnt); | ||
836 | break; | ||
837 | default: | ||
838 | mhd_assert (0); | ||
839 | mhdErrorExit (); | ||
840 | } | ||
841 | } while (_MHD_dumbClient_needs_process (clnt)); | ||
842 | return DUMB_CLIENT_FINISHED == clnt->stage; | ||
843 | } | ||
844 | |||
845 | |||
846 | void | ||
847 | _MHD_dumbClient_get_fdsets (struct _MHD_dumbClient *clnt, | ||
848 | MHD_socket *maxsckt, | ||
849 | fd_set *rs, fd_set *ws, fd_set *es) | ||
850 | { | ||
851 | mhd_assert (NULL != rs); | ||
852 | mhd_assert (NULL != ws); | ||
853 | mhd_assert (NULL != es); | ||
854 | if (DUMB_CLIENT_FINISHED > clnt->stage) | ||
855 | { | ||
856 | if (MHD_INVALID_SOCKET != clnt->sckt) | ||
857 | { | ||
858 | if ( (MHD_INVALID_SOCKET == *maxsckt) || | ||
859 | (clnt->sckt > *maxsckt) ) | ||
860 | *maxsckt = clnt->sckt; | ||
861 | if (_MHD_dumbClient_needs_recv (clnt)) | ||
862 | FD_SET (clnt->sckt, rs); | ||
863 | if (_MHD_dumbClient_needs_send (clnt)) | ||
864 | FD_SET (clnt->sckt, ws); | ||
865 | FD_SET (clnt->sckt, es); | ||
866 | } | ||
867 | } | ||
868 | } | ||
869 | |||
870 | |||
871 | /** | ||
872 | * Process the client data with send()/recv() as needed based on | ||
873 | * information in fd_sets. | ||
874 | * @param clnt the client to process | ||
875 | * @return non-zero if client finished processing the request, | ||
876 | * zero otherwise. | ||
877 | */ | ||
878 | int | ||
879 | _MHD_dumbClient_process_from_fdsets (struct _MHD_dumbClient *clnt, | ||
880 | fd_set *rs, fd_set *ws, fd_set *es) | ||
881 | { | ||
882 | if (_MHD_dumbClient_needs_process (clnt)) | ||
883 | return _MHD_dumbClient_process (clnt); | ||
884 | else if (MHD_INVALID_SOCKET != clnt->sckt) | ||
885 | { | ||
886 | if (_MHD_dumbClient_needs_recv (clnt) && FD_ISSET (clnt->sckt, rs)) | ||
887 | return _MHD_dumbClient_process (clnt); | ||
888 | else if (_MHD_dumbClient_needs_send (clnt) && FD_ISSET (clnt->sckt, ws)) | ||
889 | return _MHD_dumbClient_process (clnt); | ||
890 | else if (FD_ISSET (clnt->sckt, es)) | ||
891 | return _MHD_dumbClient_process (clnt); | ||
892 | } | ||
893 | return DUMB_CLIENT_FINISHED == clnt->stage; | ||
894 | } | ||
895 | |||
896 | |||
897 | /** | ||
898 | * Perform full request. | ||
899 | * @param clnt the client to run | ||
900 | * @return zero if client finished processing the request, | ||
901 | * non-zero if timeout is reached. | ||
902 | */ | ||
903 | int | ||
904 | _MHD_dumbClient_perform (struct _MHD_dumbClient *clnt) | ||
905 | { | ||
906 | time_t start; | ||
907 | time_t now; | ||
908 | start = time (NULL); | ||
909 | now = start; | ||
910 | do | ||
911 | { | ||
912 | fd_set rs; | ||
913 | fd_set ws; | ||
914 | fd_set es; | ||
915 | MHD_socket maxMhdSk; | ||
916 | struct timeval tv; | ||
917 | |||
918 | FD_ZERO (&rs); | ||
919 | FD_ZERO (&ws); | ||
920 | FD_ZERO (&es); | ||
921 | |||
922 | if (! _MHD_dumbClient_needs_process (clnt)) | ||
923 | { | ||
924 | maxMhdSk = MHD_INVALID_SOCKET; | ||
925 | _MHD_dumbClient_get_fdsets (clnt, &maxMhdSk, &rs, &ws, &es); | ||
926 | mhd_assert (now >= start); | ||
927 | #ifndef _WIN32 | ||
928 | tv.tv_sec = (time_t) (TIMEOUTS_VAL * 2 - (now - start) + 1); | ||
929 | #else | ||
930 | tv.tv_sec = (long) (TIMEOUTS_VAL * 2 - (now - start) + 1); | ||
931 | #endif | ||
932 | tv.tv_usec = 250 * 1000; | ||
933 | if (-1 == select ((int) (maxMhdSk + 1), &rs, &ws, &es, &tv)) | ||
934 | { | ||
935 | #ifdef MHD_POSIX_SOCKETS | ||
936 | if (EINTR != errno) | ||
937 | externalErrorExitDesc ("Unexpected select() error"); | ||
938 | #else /* ! MHD_POSIX_SOCKETS */ | ||
939 | mhd_assert ((0 != rs.fd_count) || (0 != ws.fd_count) || \ | ||
940 | (0 != es.fd_count)); | ||
941 | externalErrorExitDesc ("Unexpected select() error"); | ||
942 | _MHD_sleep ((uint32_t) (tv.tv_sec * 1000 + tv.tv_usec / 1000)); | ||
943 | #endif /* ! MHD_POSIX_SOCKETS */ | ||
944 | continue; | ||
945 | } | ||
946 | if (_MHD_dumbClient_process_from_fdsets (clnt, &rs, &ws, &es)) | ||
947 | return 0; | ||
948 | } | ||
949 | /* Use double timeout value here as MHD must catch timeout situations | ||
950 | * in this test. Timeout in client as a last resort. */ | ||
951 | } while ((now = time (NULL)) - start <= (TIMEOUTS_VAL * 2)); | ||
952 | return 1; | ||
953 | } | ||
954 | |||
955 | |||
956 | /** | ||
957 | * Close the client and free internally allocated resources. | ||
958 | * @param clnt the client to close | ||
959 | */ | ||
960 | void | ||
961 | _MHD_dumbClient_close (struct _MHD_dumbClient *clnt) | ||
962 | { | ||
963 | if (DUMB_CLIENT_FINISHED != clnt->stage) | ||
964 | _MHD_dumbClient_finalize (clnt); | ||
965 | _MHD_dumbClient_socket_close (clnt); | ||
966 | if (NULL != clnt->send_buf) | ||
967 | { | ||
968 | free ((void *) clnt->send_buf); | ||
969 | clnt->send_buf = NULL; | ||
970 | } | ||
971 | free (clnt); | ||
972 | } | ||
973 | |||
974 | |||
975 | static void | ||
976 | socket_cb (void *cls, | ||
977 | struct MHD_Connection *c, | ||
978 | void **socket_context, | ||
979 | enum MHD_ConnectionNotificationCode toe) | ||
980 | { | ||
981 | (void) cls; /* Unused */ | ||
982 | (void) socket_context; /* Unused */ | ||
983 | (void) toe; /* Unused */ | ||
984 | |||
985 | MHD_suspend_connection (c); /* Should trigger panic */ | ||
986 | mhdErrorExitDesc ("Function \"MHD_suspend_connection()\" succeed, while " \ | ||
987 | "it must fail as daemon was started without MHD_ALLOW_SUSPEND_RESUME " \ | ||
988 | "flag"); | ||
989 | } | ||
990 | |||
991 | |||
992 | struct ahc_cls_type | ||
993 | { | ||
994 | const char *volatile rp_data; | ||
995 | volatile size_t rp_data_size; | ||
996 | const char *volatile rq_method; | ||
997 | const char *volatile rq_url; | ||
998 | const char *volatile req_body; | ||
999 | volatile unsigned int cb_called; /* Non-zero indicates that callback was called at least one time */ | ||
1000 | size_t req_body_size; /**< The number of bytes in @a req_body */ | ||
1001 | size_t req_body_uploaded; /* Updated by callback */ | ||
1002 | }; | ||
1003 | |||
1004 | |||
1005 | static enum MHD_Result | ||
1006 | ahcCheck (void *cls, | ||
1007 | struct MHD_Connection *connection, | ||
1008 | const char *url, | ||
1009 | const char *method, | ||
1010 | const char *version, | ||
1011 | const char *upload_data, size_t *upload_data_size, | ||
1012 | void **req_cls) | ||
1013 | { | ||
1014 | static int marker; | ||
1015 | enum MHD_Result ret; | ||
1016 | struct ahc_cls_type *const param = (struct ahc_cls_type *) cls; | ||
1017 | (void) connection; /* Unused */ | ||
1018 | |||
1019 | if (NULL == param) | ||
1020 | mhdErrorExitDesc ("cls parameter is NULL"); | ||
1021 | param->cb_called++; | ||
1022 | |||
1023 | if (0 != strcmp (version, MHD_HTTP_VERSION_1_1)) | ||
1024 | mhdErrorExitDesc ("Unexpected HTTP version"); | ||
1025 | |||
1026 | if (0 != strcmp (url, param->rq_url)) | ||
1027 | mhdErrorExitDesc ("Unexpected URI"); | ||
1028 | |||
1029 | if (0 != strcmp (param->rq_method, method)) | ||
1030 | mhdErrorExitDesc ("Unexpected request method"); | ||
1031 | |||
1032 | if (NULL == upload_data_size) | ||
1033 | mhdErrorExitDesc ("'upload_data_size' pointer is NULL"); | ||
1034 | |||
1035 | if (0 != *upload_data_size) | ||
1036 | { | ||
1037 | const char *const upload_body = param->req_body; | ||
1038 | if (NULL == upload_data) | ||
1039 | mhdErrorExitDesc ("'upload_data' is NULL while " \ | ||
1040 | "'*upload_data_size' value is not zero"); | ||
1041 | if (NULL == upload_body) | ||
1042 | mhdErrorExitDesc ("'*upload_data_size' value is not zero " \ | ||
1043 | "while no request body is expected"); | ||
1044 | if (param->req_body_uploaded + *upload_data_size > param->req_body_size) | ||
1045 | { | ||
1046 | fprintf (stderr, "Too large upload body received. Got %u, expected %u", | ||
1047 | (unsigned int) (param->req_body_uploaded + *upload_data_size), | ||
1048 | (unsigned int) param->req_body_size); | ||
1049 | mhdErrorExit (); | ||
1050 | } | ||
1051 | if (0 != memcmp (upload_data, upload_body + param->req_body_uploaded, | ||
1052 | *upload_data_size)) | ||
1053 | { | ||
1054 | fprintf (stderr, "Unexpected request body at offset %u: " \ | ||
1055 | "'%.*s', expected: '%.*s'\n", | ||
1056 | (unsigned int) param->req_body_uploaded, | ||
1057 | (int) *upload_data_size, upload_data, | ||
1058 | (int) *upload_data_size, upload_body + param->req_body_uploaded); | ||
1059 | mhdErrorExit (); | ||
1060 | } | ||
1061 | param->req_body_uploaded += *upload_data_size; | ||
1062 | *upload_data_size = 0; | ||
1063 | } | ||
1064 | |||
1065 | if (&marker != *req_cls) | ||
1066 | { | ||
1067 | /* The first call of the callback for this connection */ | ||
1068 | mhd_assert (NULL == upload_data); | ||
1069 | param->req_body_uploaded = 0; | ||
1070 | |||
1071 | *req_cls = ▮ | ||
1072 | return MHD_YES; | ||
1073 | } | ||
1074 | |||
1075 | if (NULL != upload_data) | ||
1076 | return MHD_YES; /* Full request has not been received so far */ | ||
1077 | |||
1078 | #if 0 /* Code unused in this test */ | ||
1079 | struct MHD_Response *response; | ||
1080 | response = MHD_create_response_from_buffer (param->rp_data_size, | ||
1081 | (void *) param->rp_data, | ||
1082 | MHD_RESPMEM_MUST_COPY); | ||
1083 | if (NULL == response) | ||
1084 | mhdErrorExitDesc ("Failed to create response"); | ||
1085 | |||
1086 | ret = MHD_queue_response (connection, | ||
1087 | MHD_HTTP_OK, | ||
1088 | response); | ||
1089 | MHD_destroy_response (response); | ||
1090 | if (MHD_YES != ret) | ||
1091 | mhdErrorExitDesc ("Failed to queue response"); | ||
1092 | #else | ||
1093 | if (NULL == upload_data) | ||
1094 | mhdErrorExitDesc ("Full request received, " \ | ||
1095 | "while incomplete request expected"); | ||
1096 | ret = MHD_NO; | ||
1097 | #endif | ||
1098 | |||
1099 | return ret; | ||
1100 | } | ||
1101 | |||
1102 | |||
1103 | struct simpleQueryParams | ||
1104 | { | ||
1105 | /* Destination path for HTTP query */ | ||
1106 | const char *queryPath; | ||
1107 | |||
1108 | /* Custom query method, NULL for default */ | ||
1109 | const char *method; | ||
1110 | |||
1111 | /* Destination port for HTTP query */ | ||
1112 | uint16_t queryPort; | ||
1113 | |||
1114 | /* Additional request headers, static */ | ||
1115 | const char *headers; | ||
1116 | |||
1117 | /* NULL for request without body */ | ||
1118 | const uint8_t *req_body; | ||
1119 | size_t req_body_size; | ||
1120 | |||
1121 | /* Non-zero to use chunked encoding for request body */ | ||
1122 | int chunked; | ||
1123 | |||
1124 | /* Max size of data for single 'send()' call */ | ||
1125 | size_t step_size; | ||
1126 | |||
1127 | /* Limit for total amount of sent data */ | ||
1128 | size_t total_send_max; | ||
1129 | |||
1130 | /* HTTP query result error flag */ | ||
1131 | volatile int queryError; | ||
1132 | |||
1133 | /* Response HTTP code, zero if no response */ | ||
1134 | volatile int responseCode; | ||
1135 | }; | ||
1136 | |||
1137 | |||
1138 | /* returns non-zero if timed-out */ | ||
1139 | static int | ||
1140 | performQueryExternal (struct MHD_Daemon *d, struct _MHD_dumbClient *clnt) | ||
1141 | { | ||
1142 | time_t start; | ||
1143 | struct timeval tv; | ||
1144 | int ret; | ||
1145 | const union MHD_DaemonInfo *di; | ||
1146 | MHD_socket lstn_sk; | ||
1147 | int client_accepted; | ||
1148 | int full_req_recieved; | ||
1149 | int full_req_sent; | ||
1150 | int some_data_recieved; | ||
1151 | |||
1152 | di = MHD_get_daemon_info (d, MHD_DAEMON_INFO_LISTEN_FD); | ||
1153 | if (NULL == di) | ||
1154 | mhdErrorExitDesc ("Cannot get listener socket"); | ||
1155 | lstn_sk = di->listen_fd; | ||
1156 | |||
1157 | ret = 1; /* will be replaced with real result */ | ||
1158 | client_accepted = 0; | ||
1159 | |||
1160 | _MHD_dumbClient_start_connect (clnt); | ||
1161 | |||
1162 | full_req_recieved = 0; | ||
1163 | some_data_recieved = 0; | ||
1164 | start = time (NULL); | ||
1165 | do | ||
1166 | { | ||
1167 | fd_set rs; | ||
1168 | fd_set ws; | ||
1169 | fd_set es; | ||
1170 | MHD_socket maxMhdSk; | ||
1171 | int num_ready; | ||
1172 | int do_client; /**< Process data in client */ | ||
1173 | |||
1174 | maxMhdSk = MHD_INVALID_SOCKET; | ||
1175 | FD_ZERO (&rs); | ||
1176 | FD_ZERO (&ws); | ||
1177 | FD_ZERO (&es); | ||
1178 | if (NULL == clnt) | ||
1179 | { | ||
1180 | /* client has finished, check whether MHD is still | ||
1181 | * processing any connections */ | ||
1182 | unsigned long long to; | ||
1183 | full_req_sent = 1; | ||
1184 | do_client = 0; | ||
1185 | if (client_accepted && (MHD_YES != MHD_get_timeout (d, &to))) | ||
1186 | { | ||
1187 | ret = 0; | ||
1188 | break; /* MHD finished as well */ | ||
1189 | } | ||
1190 | } | ||
1191 | else | ||
1192 | { | ||
1193 | full_req_sent = _MHD_dumbClient_is_req_sent (clnt); | ||
1194 | if (! full_req_sent) | ||
1195 | do_client = 1; /* Request hasn't been sent yet, send the data */ | ||
1196 | else | ||
1197 | { | ||
1198 | /* All request data has been sent. | ||
1199 | * Client will close the socket as the next step. */ | ||
1200 | if (full_req_recieved) | ||
1201 | do_client = 1; /* All data has been received by the MHD */ | ||
1202 | else if (some_data_recieved) | ||
1203 | { | ||
1204 | /* at least something was received by the MHD */ | ||
1205 | do_client = 1; | ||
1206 | } | ||
1207 | else | ||
1208 | { | ||
1209 | /* The MHD must receive at least something before closing | ||
1210 | * the connection. */ | ||
1211 | do_client = 0; | ||
1212 | } | ||
1213 | } | ||
1214 | |||
1215 | if (do_client) | ||
1216 | _MHD_dumbClient_get_fdsets (clnt, &maxMhdSk, &rs, &ws, &es); | ||
1217 | } | ||
1218 | if (MHD_YES != MHD_get_fdset (d, &rs, &ws, &es, &maxMhdSk)) | ||
1219 | mhdErrorExitDesc ("MHD_get_fdset() failed"); | ||
1220 | if (do_client) | ||
1221 | { | ||
1222 | tv.tv_sec = 1; | ||
1223 | tv.tv_usec = 250 * 1000; | ||
1224 | } | ||
1225 | else | ||
1226 | { /* Request completely sent but not yet fully received */ | ||
1227 | tv.tv_sec = 0; | ||
1228 | tv.tv_usec = FINAL_PACKETS_MS * 1000; | ||
1229 | } | ||
1230 | num_ready = select ((int) (maxMhdSk + 1), &rs, &ws, &es, &tv); | ||
1231 | if (-1 == num_ready) | ||
1232 | { | ||
1233 | #ifdef MHD_POSIX_SOCKETS | ||
1234 | if (EINTR != errno) | ||
1235 | externalErrorExitDesc ("Unexpected select() error"); | ||
1236 | #else | ||
1237 | if ((WSAEINVAL != WSAGetLastError ()) || | ||
1238 | (0 != rs.fd_count) || (0 != ws.fd_count) || (0 != es.fd_count) ) | ||
1239 | externalErrorExitDesc ("Unexpected select() error"); | ||
1240 | _MHD_sleep ((uint32_t) (tv.tv_sec * 1000 + tv.tv_usec / 1000)); | ||
1241 | #endif | ||
1242 | continue; | ||
1243 | } | ||
1244 | if (0 == num_ready) | ||
1245 | { /* select() finished by timeout, looks like no more packets are pending */ | ||
1246 | if (do_client) | ||
1247 | externalErrorExitDesc ("Timeout waiting for sockets"); | ||
1248 | if (full_req_sent && (! full_req_recieved)) | ||
1249 | full_req_recieved = 1; | ||
1250 | } | ||
1251 | if (full_req_recieved) | ||
1252 | mhdErrorExitDesc ("Full request has been received by MHD, while it " | ||
1253 | "must be aborted by the panic function"); | ||
1254 | if (MHD_YES != MHD_run_from_select (d, &rs, &ws, &es)) | ||
1255 | mhdErrorExitDesc ("MHD_run_from_select() failed"); | ||
1256 | if (! client_accepted) | ||
1257 | client_accepted = FD_ISSET (lstn_sk, &rs); | ||
1258 | else | ||
1259 | { /* Client connection was already accepted by MHD */ | ||
1260 | if (! some_data_recieved) | ||
1261 | { | ||
1262 | if (! do_client) | ||
1263 | { | ||
1264 | if (0 != num_ready) | ||
1265 | { /* Connection was accepted before, "ready" socket means data */ | ||
1266 | some_data_recieved = 1; | ||
1267 | } | ||
1268 | } | ||
1269 | else | ||
1270 | { | ||
1271 | if (2 == num_ready) | ||
1272 | some_data_recieved = 1; | ||
1273 | else if ((1 == num_ready) && | ||
1274 | ((MHD_INVALID_SOCKET == clnt->sckt) || | ||
1275 | ! FD_ISSET (clnt->sckt, &ws))) | ||
1276 | some_data_recieved = 1; | ||
1277 | } | ||
1278 | } | ||
1279 | } | ||
1280 | if (do_client) | ||
1281 | { | ||
1282 | if (_MHD_dumbClient_process_from_fdsets (clnt, &rs, &ws, &es)) | ||
1283 | clnt = NULL; | ||
1284 | } | ||
1285 | /* Use double timeout value here so MHD would be able to catch timeout | ||
1286 | * internally */ | ||
1287 | } while (time (NULL) - start <= (TIMEOUTS_VAL * 2)); | ||
1288 | |||
1289 | return ret; | ||
1290 | } | ||
1291 | |||
1292 | |||
1293 | /* Returns zero for successful response and non-zero for failed response */ | ||
1294 | static int | ||
1295 | doClientQueryInThread (struct MHD_Daemon *d, | ||
1296 | struct simpleQueryParams *p) | ||
1297 | { | ||
1298 | const union MHD_DaemonInfo *dinfo; | ||
1299 | struct _MHD_dumbClient *c; | ||
1300 | int errornum; | ||
1301 | int use_external_poll; | ||
1302 | |||
1303 | dinfo = MHD_get_daemon_info (d, MHD_DAEMON_INFO_FLAGS); | ||
1304 | if (NULL == dinfo) | ||
1305 | mhdErrorExitDesc ("MHD_get_daemon_info() failed"); | ||
1306 | use_external_poll = (0 == (dinfo->flags | ||
1307 | & MHD_USE_INTERNAL_POLLING_THREAD)); | ||
1308 | |||
1309 | if (0 == p->queryPort) | ||
1310 | externalErrorExit (); | ||
1311 | |||
1312 | c = _MHD_dumbClient_create (p->queryPort, p->method, p->queryPath, | ||
1313 | p->headers, p->req_body, p->req_body_size, | ||
1314 | p->chunked); | ||
1315 | _MHD_dumbClient_set_send_limits (c, p->step_size, p->total_send_max); | ||
1316 | |||
1317 | /* 'internal' polling should not be used in this test */ | ||
1318 | mhd_assert (use_external_poll); | ||
1319 | if (! use_external_poll) | ||
1320 | errornum = _MHD_dumbClient_perform (c); | ||
1321 | else | ||
1322 | errornum = performQueryExternal (d, c); | ||
1323 | |||
1324 | if (errornum) | ||
1325 | fprintf (stderr, "Request timeout out.\n"); | ||
1326 | else | ||
1327 | mhdErrorExitDesc ("Request succeed, but it must fail"); | ||
1328 | |||
1329 | _MHD_dumbClient_close (c); | ||
1330 | |||
1331 | return errornum; | ||
1332 | } | ||
1333 | |||
1334 | |||
1335 | /* Perform test queries, shut down MHD daemon, and free parameters */ | ||
1336 | static unsigned int | ||
1337 | performTestQueries (struct MHD_Daemon *d, uint16_t d_port, | ||
1338 | struct ahc_cls_type *ahc_param) | ||
1339 | { | ||
1340 | struct simpleQueryParams qParam; | ||
1341 | |||
1342 | /* Common parameters, to be individually overridden by specific test cases | ||
1343 | * if needed */ | ||
1344 | qParam.queryPort = d_port; | ||
1345 | qParam.method = MHD_HTTP_METHOD_PUT; | ||
1346 | qParam.queryPath = EXPECTED_URI_BASE_PATH; | ||
1347 | qParam.headers = REQ_HEADER_CT; | ||
1348 | qParam.req_body = (const uint8_t *) REQ_BODY; | ||
1349 | qParam.req_body_size = MHD_STATICSTR_LEN_ (REQ_BODY); | ||
1350 | qParam.chunked = 0; | ||
1351 | qParam.step_size = 0; | ||
1352 | |||
1353 | ahc_param->rq_url = EXPECTED_URI_BASE_PATH; | ||
1354 | ahc_param->rq_method = MHD_HTTP_METHOD_PUT; | ||
1355 | ahc_param->rp_data = "~"; | ||
1356 | ahc_param->rp_data_size = 1; | ||
1357 | ahc_param->req_body = (const char *) qParam.req_body; | ||
1358 | ahc_param->req_body_size = qParam.req_body_size; | ||
1359 | |||
1360 | /* Make sure that maximum size is tested */ | ||
1361 | /* To be updated by callbacks */ | ||
1362 | ahc_param->cb_called = 0; | ||
1363 | |||
1364 | if (0 != doClientQueryInThread (d, &qParam)) | ||
1365 | fprintf (stderr, "FAILED: client query failed."); | ||
1366 | |||
1367 | MHD_stop_daemon (d); | ||
1368 | free (ahc_param); | ||
1369 | |||
1370 | return 1; /* Always error if reached this point */ | ||
1371 | } | ||
1372 | |||
1373 | |||
1374 | enum testMhdThreadsType | ||
1375 | { | ||
1376 | testMhdThreadExternal = 0, | ||
1377 | testMhdThreadInternal = MHD_USE_INTERNAL_POLLING_THREAD, | ||
1378 | testMhdThreadInternalPerConnection = MHD_USE_THREAD_PER_CONNECTION | ||
1379 | | MHD_USE_INTERNAL_POLLING_THREAD, | ||
1380 | testMhdThreadInternalPool | ||
1381 | }; | ||
1382 | |||
1383 | enum testMhdPollType | ||
1384 | { | ||
1385 | testMhdPollBySelect = 0, | ||
1386 | testMhdPollByPoll = MHD_USE_POLL, | ||
1387 | testMhdPollByEpoll = MHD_USE_EPOLL, | ||
1388 | testMhdPollAuto = MHD_USE_AUTO | ||
1389 | }; | ||
1390 | |||
1391 | /* Get number of threads for thread pool depending | ||
1392 | * on used poll function and test type. */ | ||
1393 | static unsigned int | ||
1394 | testNumThreadsForPool (enum testMhdPollType pollType) | ||
1395 | { | ||
1396 | unsigned int numThreads = MHD_CPU_COUNT; | ||
1397 | (void) pollType; /* Don't care about pollType for this test */ | ||
1398 | return numThreads; /* No practical limit for non-cleanup test */ | ||
1399 | } | ||
1400 | |||
1401 | |||
1402 | #define PANIC_MAGIC_CHECK 1133 | ||
1403 | |||
1404 | static void | ||
1405 | myPanicCallback (void *cls, | ||
1406 | const char *file, | ||
1407 | unsigned int line, | ||
1408 | const char *reason) | ||
1409 | { | ||
1410 | int *const param = (int *) cls; | ||
1411 | if (NULL == cls) | ||
1412 | mhdErrorExitDesc ("The 'cls' parameter is NULL"); | ||
1413 | if (PANIC_MAGIC_CHECK != *param) | ||
1414 | mhdErrorExitDesc ("Wrong '*cls' value"); | ||
1415 | #ifdef HAVE_MESSAGES | ||
1416 | if (NULL == file) | ||
1417 | mhdErrorExitDesc ("The 'file' parameter is NULL"); | ||
1418 | if (NULL == reason) | ||
1419 | mhdErrorExitDesc ("The 'reason' parameter is NULL"); | ||
1420 | #else /* ! HAVE_MESSAGES */ | ||
1421 | if (NULL != file) | ||
1422 | mhdErrorExitDesc ("The 'file' parameter is not NULL"); | ||
1423 | if (NULL != reason) | ||
1424 | mhdErrorExitDesc ("The 'reason' parameter is not NULL"); | ||
1425 | #endif /* ! HAVE_MESSAGES */ | ||
1426 | fflush (stderr); | ||
1427 | fflush (stdout); | ||
1428 | printf ("User panic function has been called from file '%s' at line '%u' " | ||
1429 | "with the reason:\n%s", file, line, | ||
1430 | ((NULL != reason) ? reason : "(NULL)\n")); | ||
1431 | fflush (stdout); | ||
1432 | exit (0); | ||
1433 | } | ||
1434 | |||
1435 | |||
1436 | static struct MHD_Daemon * | ||
1437 | startTestMhdDaemon (enum testMhdThreadsType thrType, | ||
1438 | enum testMhdPollType pollType, uint16_t *pport, | ||
1439 | struct ahc_cls_type **ahc_param) | ||
1440 | { | ||
1441 | struct MHD_Daemon *d; | ||
1442 | const union MHD_DaemonInfo *dinfo; | ||
1443 | static int magic_panic_param = PANIC_MAGIC_CHECK; | ||
1444 | |||
1445 | if (NULL == ahc_param) | ||
1446 | externalErrorExit (); | ||
1447 | |||
1448 | *ahc_param = (struct ahc_cls_type *) malloc (sizeof(struct ahc_cls_type)); | ||
1449 | if (NULL == *ahc_param) | ||
1450 | externalErrorExit (); | ||
1451 | |||
1452 | if ( (0 == *pport) && | ||
1453 | (MHD_NO == MHD_is_feature_supported (MHD_FEATURE_AUTODETECT_BIND_PORT)) ) | ||
1454 | { | ||
1455 | *pport = 4190; | ||
1456 | } | ||
1457 | |||
1458 | MHD_set_panic_func (&myPanicCallback, (void *) &magic_panic_param); | ||
1459 | |||
1460 | if (testMhdThreadInternalPool != thrType) | ||
1461 | d = MHD_start_daemon (((unsigned int) thrType) | ((unsigned int) pollType) | ||
1462 | | (verbose ? MHD_USE_ERROR_LOG : 0), | ||
1463 | *pport, NULL, NULL, | ||
1464 | &ahcCheck, *ahc_param, | ||
1465 | MHD_OPTION_NOTIFY_CONNECTION, &socket_cb, | ||
1466 | NULL, | ||
1467 | MHD_OPTION_CONNECTION_TIMEOUT, | ||
1468 | (unsigned) TIMEOUTS_VAL, | ||
1469 | MHD_OPTION_END); | ||
1470 | else | ||
1471 | d = MHD_start_daemon (MHD_USE_INTERNAL_POLLING_THREAD | ||
1472 | | ((unsigned int) pollType) | ||
1473 | | (verbose ? MHD_USE_ERROR_LOG : 0), | ||
1474 | *pport, NULL, NULL, | ||
1475 | &ahcCheck, *ahc_param, | ||
1476 | MHD_OPTION_THREAD_POOL_SIZE, | ||
1477 | testNumThreadsForPool (pollType), | ||
1478 | MHD_OPTION_NOTIFY_CONNECTION, &socket_cb, | ||
1479 | NULL, | ||
1480 | MHD_OPTION_CONNECTION_TIMEOUT, | ||
1481 | (unsigned) TIMEOUTS_VAL, | ||
1482 | MHD_OPTION_END); | ||
1483 | |||
1484 | if (NULL == d) | ||
1485 | mhdErrorExitDesc ("Failed to start MHD daemon"); | ||
1486 | |||
1487 | if (0 == *pport) | ||
1488 | { | ||
1489 | dinfo = MHD_get_daemon_info (d, MHD_DAEMON_INFO_BIND_PORT); | ||
1490 | if ((NULL == dinfo) || (0 == dinfo->port)) | ||
1491 | mhdErrorExitDesc ("MHD_get_daemon_info() failed"); | ||
1492 | *pport = dinfo->port; | ||
1493 | if (0 == global_port) | ||
1494 | global_port = *pport; /* Reuse the same port for all tests */ | ||
1495 | } | ||
1496 | |||
1497 | return d; | ||
1498 | } | ||
1499 | |||
1500 | |||
1501 | /* Test runners */ | ||
1502 | |||
1503 | |||
1504 | static unsigned int | ||
1505 | testExternalGet (void) | ||
1506 | { | ||
1507 | struct MHD_Daemon *d; | ||
1508 | uint16_t d_port = global_port; /* Daemon's port */ | ||
1509 | struct ahc_cls_type *ahc_param; | ||
1510 | |||
1511 | d = startTestMhdDaemon (testMhdThreadExternal, testMhdPollBySelect, &d_port, | ||
1512 | &ahc_param); | ||
1513 | |||
1514 | return performTestQueries (d, d_port, ahc_param); | ||
1515 | } | ||
1516 | |||
1517 | |||
1518 | #if 0 /* disabled runners, not suitable for this test */ | ||
1519 | static unsigned int | ||
1520 | testInternalGet (enum testMhdPollType pollType) | ||
1521 | { | ||
1522 | struct MHD_Daemon *d; | ||
1523 | uint16_t d_port = global_port; /* Daemon's port */ | ||
1524 | struct ahc_cls_type *ahc_param; | ||
1525 | struct check_uri_cls *uri_cb_param; | ||
1526 | struct term_notif_cb_param *term_result; | ||
1527 | |||
1528 | d = startTestMhdDaemon (testMhdThreadInternal, pollType, &d_port, | ||
1529 | &ahc_param, &uri_cb_param, &term_result); | ||
1530 | |||
1531 | return performTestQueries (d, d_port, ahc_param, uri_cb_param, term_result); | ||
1532 | } | ||
1533 | |||
1534 | |||
1535 | static int | ||
1536 | testMultithreadedGet (enum testMhdPollType pollType) | ||
1537 | { | ||
1538 | struct MHD_Daemon *d; | ||
1539 | uint16_t d_port = global_port; /* Daemon's port */ | ||
1540 | struct ahc_cls_type *ahc_param; | ||
1541 | struct check_uri_cls *uri_cb_param; | ||
1542 | struct term_notif_cb_param *term_result; | ||
1543 | |||
1544 | d = startTestMhdDaemon (testMhdThreadInternalPerConnection, pollType, &d_port, | ||
1545 | &ahc_param, &uri_cb_param); | ||
1546 | return performTestQueries (d, d_port, ahc_param, uri_cb_param, term_result); | ||
1547 | } | ||
1548 | |||
1549 | |||
1550 | static unsigned int | ||
1551 | testMultithreadedPoolGet (enum testMhdPollType pollType) | ||
1552 | { | ||
1553 | struct MHD_Daemon *d; | ||
1554 | uint16_t d_port = global_port; /* Daemon's port */ | ||
1555 | struct ahc_cls_type *ahc_param; | ||
1556 | struct check_uri_cls *uri_cb_param; | ||
1557 | struct term_notif_cb_param *term_result; | ||
1558 | |||
1559 | d = startTestMhdDaemon (testMhdThreadInternalPool, pollType, &d_port, | ||
1560 | &ahc_param, &uri_cb_param); | ||
1561 | return performTestQueries (d, d_port, ahc_param, uri_cb_param, term_result); | ||
1562 | } | ||
1563 | |||
1564 | |||
1565 | #endif /* disabled runners, not suitable for this test */ | ||
1566 | |||
1567 | int | ||
1568 | main (int argc, char *const *argv) | ||
1569 | { | ||
1570 | unsigned int errorCount = 0; | ||
1571 | unsigned int test_result = 0; | ||
1572 | verbose = 0; | ||
1573 | |||
1574 | (void) has_in_name; /* Unused, mute compiler warning */ | ||
1575 | if ((NULL == argv) || (0 == argv[0])) | ||
1576 | return 99; | ||
1577 | verbose = ! (has_param (argc, argv, "-q") || | ||
1578 | has_param (argc, argv, "--quiet") || | ||
1579 | has_param (argc, argv, "-s") || | ||
1580 | has_param (argc, argv, "--silent")); | ||
1581 | |||
1582 | test_global_init (); | ||
1583 | |||
1584 | /* Could be set to non-zero value to enforce using specific port | ||
1585 | * in the test */ | ||
1586 | global_port = 0; | ||
1587 | test_result = testExternalGet (); | ||
1588 | if (test_result) | ||
1589 | fprintf (stderr, "FAILED: testExternalGet (). Result: %u.\n", test_result); | ||
1590 | else if (verbose) | ||
1591 | printf ("PASSED: testExternalGet ().\n"); | ||
1592 | errorCount += test_result; | ||
1593 | #if 0 /* disabled runners, not suitable for this test */ | ||
1594 | if (MHD_YES == MHD_is_feature_supported (MHD_FEATURE_THREADS)) | ||
1595 | { | ||
1596 | test_result = testInternalGet (testMhdPollAuto); | ||
1597 | if (test_result) | ||
1598 | fprintf (stderr, "FAILED: testInternalGet (testMhdPollAuto). " | ||
1599 | "Result: %u.\n", | ||
1600 | test_result); | ||
1601 | else if (verbose) | ||
1602 | printf ("PASSED: testInternalGet (testMhdPollBySelect).\n"); | ||
1603 | errorCount += test_result; | ||
1604 | #ifdef _MHD_HEAVY_TESTS | ||
1605 | /* Actually tests are not heavy, but took too long to complete while | ||
1606 | * not really provide any additional results. */ | ||
1607 | test_result = testInternalGet (testMhdPollBySelect); | ||
1608 | if (test_result) | ||
1609 | fprintf (stderr, "FAILED: testInternalGet (testMhdPollBySelect). " | ||
1610 | "Result: %u.\n", | ||
1611 | test_result); | ||
1612 | else if (verbose) | ||
1613 | printf ("PASSED: testInternalGet (testMhdPollBySelect).\n"); | ||
1614 | errorCount += test_result; | ||
1615 | test_result = testMultithreadedPoolGet (testMhdPollBySelect); | ||
1616 | if (test_result) | ||
1617 | fprintf (stderr, | ||
1618 | "FAILED: testMultithreadedPoolGet (testMhdPollBySelect). " | ||
1619 | "Result: %u.\n", | ||
1620 | test_result); | ||
1621 | else if (verbose) | ||
1622 | printf ("PASSED: testMultithreadedPoolGet (testMhdPollBySelect).\n"); | ||
1623 | errorCount += test_result; | ||
1624 | test_result = testMultithreadedGet (testMhdPollBySelect); | ||
1625 | if (test_result) | ||
1626 | fprintf (stderr, | ||
1627 | "FAILED: testMultithreadedGet (testMhdPollBySelect). " | ||
1628 | "Result: %u.\n", | ||
1629 | test_result); | ||
1630 | else if (verbose) | ||
1631 | printf ("PASSED: testMultithreadedGet (testMhdPollBySelect).\n"); | ||
1632 | errorCount += test_result; | ||
1633 | if (MHD_YES == MHD_is_feature_supported (MHD_FEATURE_POLL)) | ||
1634 | { | ||
1635 | test_result = testInternalGet (testMhdPollByPoll); | ||
1636 | if (test_result) | ||
1637 | fprintf (stderr, "FAILED: testInternalGet (testMhdPollByPoll). " | ||
1638 | "Result: %u.\n", | ||
1639 | test_result); | ||
1640 | else if (verbose) | ||
1641 | printf ("PASSED: testInternalGet (testMhdPollByPoll).\n"); | ||
1642 | errorCount += test_result; | ||
1643 | } | ||
1644 | if (MHD_YES == MHD_is_feature_supported (MHD_FEATURE_EPOLL)) | ||
1645 | { | ||
1646 | test_result = testInternalGet (testMhdPollByEpoll); | ||
1647 | if (test_result) | ||
1648 | fprintf (stderr, "FAILED: testInternalGet (testMhdPollByEpoll). " | ||
1649 | "Result: %u.\n", | ||
1650 | test_result); | ||
1651 | else if (verbose) | ||
1652 | printf ("PASSED: testInternalGet (testMhdPollByEpoll).\n"); | ||
1653 | errorCount += test_result; | ||
1654 | } | ||
1655 | #else | ||
1656 | /* Mute compiler warnings */ | ||
1657 | (void) testMultithreadedGet; | ||
1658 | (void) testMultithreadedPoolGet; | ||
1659 | #endif /* _MHD_HEAVY_TESTS */ | ||
1660 | } | ||
1661 | #endif /* disabled runners, not suitable for this test */ | ||
1662 | if (0 != errorCount) | ||
1663 | fprintf (stderr, | ||
1664 | "Error (code: %u)\n", | ||
1665 | errorCount); | ||
1666 | else if (verbose) | ||
1667 | printf ("All tests passed.\n"); | ||
1668 | |||
1669 | test_global_cleanup (); | ||
1670 | |||
1671 | return (errorCount == 0) ? 0 : 1; /* 0 == pass */ | ||
1672 | } | ||