aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorEvgeny Grin (Karlson2k) <k2k@narod.ru>2022-04-25 22:52:27 +0300
committerEvgeny Grin (Karlson2k) <k2k@narod.ru>2022-04-25 22:52:27 +0300
commit54e123587e440e183eb5b7fbd6dcd1bebbc795dd (patch)
tree8f3afc1da46eca98fc8669b3b5e75c504fa14e01
parentefdd6e0ffbc597a95165bd328d3acd608cdcaa5b (diff)
downloadlibmicrohttpd-54e123587e440e183eb5b7fbd6dcd1bebbc795dd.tar.gz
libmicrohttpd-54e123587e440e183eb5b7fbd6dcd1bebbc795dd.zip
set_test_panic: added new test
-rw-r--r--README3
-rw-r--r--src/microhttpd/Makefile.am8
-rw-r--r--src/microhttpd/test_set_panic.c1672
3 files changed, 1680 insertions, 3 deletions
diff --git a/README b/README
index 7d355292..a330e191 100644
--- a/README
+++ b/README
@@ -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
86In particular, the following functions are not covered by 'make check': 86In 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
212if HAVE_POSIX_THREADS 213if HAVE_POSIX_THREADS
213if ENABLE_UPGRADE 214if 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
489test_client_put_chunked_steps_hard_close_LDADD = \ 490test_client_put_chunked_steps_hard_close_LDADD = \
490 libmicrohttpd.la 491 libmicrohttpd.la
492
493test_set_panic_SOURCES = \
494 test_set_panic.c
495test_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 */
220static 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 */
257static int verbose; /**< Be verbose */
258static uint16_t global_port; /**< MHD daemons listen port number */
259
260static void
261test_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
276static void
277test_global_cleanup (void)
278{
279}
280
281
282/**
283 * Change socket to blocking.
284 *
285 * @param fd the socket to manipulate
286 */
287static void
288make_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 */
315static void
316make_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
338enum _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
353struct _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
375struct _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
382void
383_MHD_dumbClient_set_send_limits (struct _MHD_dumbClient *clnt,
384 size_t step_size, size_t max_total_send);
385
386void
387_MHD_dumbClient_start_connect (struct _MHD_dumbClient *clnt);
388
389int
390_MHD_dumbClient_is_req_sent (struct _MHD_dumbClient *clnt);
391
392int
393_MHD_dumbClient_process (struct _MHD_dumbClient *clnt);
394
395void
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 */
408int
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 */
418int
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 */
426void
427_MHD_dumbClient_close (struct _MHD_dumbClient *clnt);
428
429
430struct _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
600void
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 */
610static 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
634void
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 */
643static 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 */
659static 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 */
704static void
705_MHD_dumbClient_recv_reply (struct _MHD_dumbClient *clnt)
706{
707 (void) clnt;
708 externalErrorExitDesc ("Not implemented for this test");
709}
710
711
712int
713_MHD_dumbClient_is_req_sent (struct _MHD_dumbClient *clnt)
714{
715 return DUMB_CLIENT_REQ_SENT <= clnt->stage;
716}
717
718
719/* internal */
720static 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 */
734static 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 */
753static 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 */
763static 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 */
778static 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 */
801int
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
846void
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 */
878int
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 */
903int
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 */
960void
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
975static void
976socket_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
992struct 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
1005static enum MHD_Result
1006ahcCheck (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 = &marker;
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
1103struct 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 */
1139static int
1140performQueryExternal (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 */
1294static int
1295doClientQueryInThread (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 */
1336static unsigned int
1337performTestQueries (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
1374enum 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
1383enum 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. */
1393static unsigned int
1394testNumThreadsForPool (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
1404static void
1405myPanicCallback (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
1436static struct MHD_Daemon *
1437startTestMhdDaemon (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
1504static unsigned int
1505testExternalGet (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 */
1519static unsigned int
1520testInternalGet (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
1535static int
1536testMultithreadedGet (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
1550static unsigned int
1551testMultithreadedPoolGet (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
1567int
1568main (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}