aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorEvgeny Grin (Karlson2k) <k2k@narod.ru>2021-10-17 11:53:21 +0300
committerEvgeny Grin (Karlson2k) <k2k@narod.ru>2021-10-17 11:53:21 +0300
commitec89305a37db6a39010b8311b8e7a8608621362d (patch)
treec7ce3286250e87ba48c7899b810fd1edc0bb8ec7
parente2a49611e067feff63f51b2f4594f0bea01367f4 (diff)
downloadlibmicrohttpd-ec89305a37db6a39010b8311b8e7a8608621362d.tar.gz
libmicrohttpd-ec89305a37db6a39010b8311b8e7a8608621362d.zip
Added two tests with non-standard symbols in requests
-rw-r--r--src/testcurl/.gitignore2
-rw-r--r--src/testcurl/Makefile.am9
-rw-r--r--src/testcurl/test_tricky.c1158
3 files changed, 1169 insertions, 0 deletions
diff --git a/src/testcurl/.gitignore b/src/testcurl/.gitignore
index 5f06c4fa..45b3fd0d 100644
--- a/src/testcurl/.gitignore
+++ b/src/testcurl/.gitignore
@@ -147,3 +147,5 @@ core
147/test_toolarge_reply_header_name 147/test_toolarge_reply_header_name
148/test_toolarge_reply_header_value 148/test_toolarge_reply_header_value
149/test_toolarge_reply_headers 149/test_toolarge_reply_headers
150/test_tricky_url
151/test_tricky_header2 \ No newline at end of file
diff --git a/src/testcurl/Makefile.am b/src/testcurl/Makefile.am
index f5b651c3..40a9fb13 100644
--- a/src/testcurl/Makefile.am
+++ b/src/testcurl/Makefile.am
@@ -107,6 +107,8 @@ check_PROGRAMS = \
107 test_toolarge_reply_header_name \ 107 test_toolarge_reply_header_name \
108 test_toolarge_reply_header_value \ 108 test_toolarge_reply_header_value \
109 test_toolarge_reply_headers \ 109 test_toolarge_reply_headers \
110 test_tricky_url \
111 test_tricky_header2 \
110 test_large_put \ 112 test_large_put \
111 test_get11 \ 113 test_get11 \
112 test_get_iovec11 \ 114 test_get_iovec11 \
@@ -455,3 +457,10 @@ test_toolarge_reply_header_value_SOURCES = \
455 457
456test_toolarge_reply_headers_SOURCES = \ 458test_toolarge_reply_headers_SOURCES = \
457 test_toolarge.c ../microhttpd/test_helpers.h 459 test_toolarge.c ../microhttpd/test_helpers.h
460
461test_tricky_url_SOURCES = \
462 test_tricky.c ../microhttpd/test_helpers.h
463
464test_tricky_header2_SOURCES = \
465 test_tricky.c ../microhttpd/test_helpers.h
466 \ No newline at end of file
diff --git a/src/testcurl/test_tricky.c b/src/testcurl/test_tricky.c
new file mode 100644
index 00000000..72b7485e
--- /dev/null
+++ b/src/testcurl/test_tricky.c
@@ -0,0 +1,1158 @@
1/*
2 This file is part of libmicrohttpd
3 Copyright (C) 2014-2021 Evgeny Grin (Karlson2k)
4 Copyright (C) 2007, 2009, 2011 Christian Grothoff
5
6 libmicrohttpd is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published
8 by the Free Software Foundation; either version 2, or (at your
9 option) any later version.
10
11 libmicrohttpd is distributed in the hope that it will be useful, but
12 WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 General Public License for more details.
15
16 You should have received a copy of the GNU General Public License
17 along with libmicrohttpd; see the file COPYING. If not, write to the
18 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
19 Boston, MA 02110-1301, USA.
20*/
21/**
22 * @file test_toolarge.c
23 * @brief Testcase for handling of untypical data.
24 * @author Karlson2k (Evgeny Grin)
25 * @author Christian Grothoff
26 */
27#include "MHD_config.h"
28#include "platform.h"
29#include <curl/curl.h>
30#include <microhttpd.h>
31#include <stdlib.h>
32#include <string.h>
33#include <time.h>
34#include "test_helpers.h"
35#include "mhd_sockets.h" /* only macros used */
36
37#ifdef HAVE_STRINGS_H
38#include <strings.h>
39#endif /* HAVE_STRINGS_H */
40
41#ifdef _WIN32
42#ifndef WIN32_LEAN_AND_MEAN
43#define WIN32_LEAN_AND_MEAN 1
44#endif /* !WIN32_LEAN_AND_MEAN */
45#include <windows.h>
46#endif
47
48#ifndef WINDOWS
49#include <unistd.h>
50#include <sys/socket.h>
51#endif
52
53#ifdef HAVE_LIMITS_H
54#include <limits.h>
55#endif /* HAVE_LIMITS_H */
56
57#if defined(MHD_CPU_COUNT) && (MHD_CPU_COUNT + 0) < 2
58#undef MHD_CPU_COUNT
59#endif
60#if ! defined(MHD_CPU_COUNT)
61#define MHD_CPU_COUNT 2
62#endif
63#if MHD_CPU_COUNT > 32
64#undef MHD_CPU_COUNT
65/* Limit to reasonable value */
66#define MHD_CPU_COUNT 32
67#endif /* MHD_CPU_COUNT > 32 */
68
69
70#if defined(HAVE___FUNC__)
71#define externalErrorExit(ignore) \
72 _externalErrorExit_func(NULL, __func__, __LINE__)
73#define externalErrorExitDesc(errDesc) \
74 _externalErrorExit_func(errDesc, __func__, __LINE__)
75#define libcurlErrorExit(ignore) \
76 _libcurlErrorExit_func(NULL, __func__, __LINE__)
77#define libcurlErrorExitDesc(errDesc) \
78 _libcurlErrorExit_func(errDesc, __func__, __LINE__)
79#define mhdErrorExit(ignore) \
80 _mhdErrorExit_func(NULL, __func__, __LINE__)
81#define mhdErrorExitDesc(errDesc) \
82 _mhdErrorExit_func(errDesc, __func__, __LINE__)
83#elif defined(HAVE___FUNCTION__)
84#define externalErrorExit(ignore) \
85 _externalErrorExit_func(NULL, __FUNCTION__, __LINE__)
86#define externalErrorExitDesc(errDesc) \
87 _externalErrorExit_func(errDesc, __FUNCTION__, __LINE__)
88#define libcurlErrorExit(ignore) \
89 _libcurlErrorExit_func(NULL, __FUNCTION__, __LINE__)
90#define libcurlErrorExitDesc(errDesc) \
91 _libcurlErrorExit_func(errDesc, __FUNCTION__, __LINE__)
92#define mhdErrorExit(ignore) \
93 _mhdErrorExit_func(NULL, __FUNCTION__, __LINE__)
94#define mhdErrorExitDesc(errDesc) \
95 _mhdErrorExit_func(errDesc, __FUNCTION__, __LINE__)
96#else
97#define externalErrorExit(ignore) _externalErrorExit_func(NULL, NULL, __LINE__)
98#define externalErrorExitDesc(errDesc) \
99 _externalErrorExit_func(errDesc, NULL, __LINE__)
100#define libcurlErrorExit(ignore) _libcurlErrorExit_func(NULL, NULL, __LINE__)
101#define libcurlErrorExitDesc(errDesc) \
102 _libcurlErrorExit_func(errDesc, NULL, __LINE__)
103#define mhdErrorExit(ignore) _mhdErrorExit_func(NULL, NULL, __LINE__)
104#define mhdErrorExitDesc(errDesc) _mhdErrorExit_func(errDesc, NULL, __LINE__)
105#endif
106
107
108_MHD_NORETURN static void
109_externalErrorExit_func (const char *errDesc, const char *funcName, int lineNum)
110{
111 if ((NULL != errDesc) && (0 != errDesc[0]))
112 fprintf (stderr, "%s", errDesc);
113 else
114 fprintf (stderr, "System or external library call failed");
115 if ((NULL != funcName) && (0 != funcName[0]))
116 fprintf (stderr, " in %s", funcName);
117 if (0 < lineNum)
118 fprintf (stderr, " at line %d", lineNum);
119
120 fprintf (stderr, ".\nLast errno value: %d (%s)\n", (int) errno,
121 strerror (errno));
122#ifdef MHD_WINSOCK_SOCKETS
123 fprintf (stderr, "WSAGetLastError() value: %d\n", (int) WSAGetLastError ());
124#endif /* MHD_WINSOCK_SOCKETS */
125 fflush (stderr);
126 exit (99);
127}
128
129
130static char libcurl_errbuf[CURL_ERROR_SIZE] = "";
131
132_MHD_NORETURN static void
133_libcurlErrorExit_func (const char *errDesc, const char *funcName, int lineNum)
134{
135 if ((NULL != errDesc) && (0 != errDesc[0]))
136 fprintf (stderr, "%s", errDesc);
137 else
138 fprintf (stderr, "CURL library call failed");
139 if ((NULL != funcName) && (0 != funcName[0]))
140 fprintf (stderr, " in %s", funcName);
141 if (0 < lineNum)
142 fprintf (stderr, " at line %d", lineNum);
143
144 fprintf (stderr, ".\nLast errno value: %d (%s)\n", (int) errno,
145 strerror (errno));
146 if (0 != libcurl_errbuf[0])
147 fprintf (stderr, "Last libcurl error details: %s\n", libcurl_errbuf);
148
149 fflush (stderr);
150 exit (99);
151}
152
153
154_MHD_NORETURN static void
155_mhdErrorExit_func (const char *errDesc, const char *funcName, int lineNum)
156{
157 if ((NULL != errDesc) && (0 != errDesc[0]))
158 fprintf (stderr, "%s", errDesc);
159 else
160 fprintf (stderr, "MHD unexpected error");
161 if ((NULL != funcName) && (0 != funcName[0]))
162 fprintf (stderr, " in %s", funcName);
163 if (0 < lineNum)
164 fprintf (stderr, " at line %d", lineNum);
165
166 fprintf (stderr, ".\nLast errno value: %d (%s)\n", (int) errno,
167 strerror (errno));
168
169 fflush (stderr);
170 exit (8);
171}
172
173
174/* Could be increased to facilitate debugging */
175#define TIMEOUTS_VAL 5
176
177#define EXPECTED_URI_BASE_PATH "/a"
178
179#define EXPECTED_URI_BASE_PATH_TRICKY "/one\rtwo"
180
181#define URL_SCHEME "http:/" "/"
182
183#define URL_HOST "127.0.0.1"
184
185#define URL_SCHEME_HOST URL_SCHEME URL_HOST
186
187#define HEADER1_NAME "First"
188#define HEADER1_VALUE "1st"
189#define HEADER1 HEADER1_NAME ": " HEADER1_VALUE
190#define HEADER2_NAME "Second"
191#define HEADER2CR_VALUE "2\rnd"
192#define HEADER2CR HEADER2_NAME ": " HEADER2CR_VALUE
193/* Use headers when it would be properly supported by MHD
194#define HEADER3CR_NAME "Thi\rrd"
195#define HEADER3CR_VALUE "3r\rd"
196#define HEADER3CR HEADER3CR_NAME ": " HEADER3CR_VALUE
197*/
198#define HEADER4_NAME "Normal"
199#define HEADER4_VALUE "it's fine"
200#define HEADER4 HEADER4_NAME ": " HEADER4_VALUE
201
202/* Global parameters */
203static int verbose; /**< Be verbose */
204static int oneone; /**< If false use HTTP/1.0 for requests*/
205static int global_port; /**< MHD daemons listen port number */
206static int response_timeout_val = TIMEOUTS_VAL;
207
208static int tricky_url; /**< Tricky request URL */
209static int tricky_header2; /**< Tricky request header2 */
210
211/* Current test parameters */
212/* * Moved to local variables * */
213
214/* Static helper variables */
215/* * None for this test * */
216
217static void
218test_global_init (void)
219{
220 libcurl_errbuf[0] = 0;
221
222 if (0 != curl_global_init (CURL_GLOBAL_WIN32))
223 externalErrorExit ();
224}
225
226
227static void
228test_global_cleanup (void)
229{
230 curl_global_cleanup ();
231}
232
233
234struct headers_check_result
235{
236 int dummy; /* no checks in this test */
237};
238
239
240size_t
241lcurl_hdr_callback (char *buffer, size_t size, size_t nitems,
242 void *userdata)
243{
244 const size_t data_size = size * nitems;
245 struct headers_check_result *check_res =
246 (struct headers_check_result *) userdata;
247
248 /* no checks in this test */
249 (void) check_res; (void) buffer;
250
251 return data_size;
252}
253
254
255struct lcurl_data_cb_param
256{
257 char *buf;
258 size_t pos;
259 size_t size;
260};
261
262
263static size_t
264copyBuffer (void *ptr, size_t size, size_t nmemb, void *ctx)
265{
266 struct lcurl_data_cb_param *cbc = ctx;
267
268 if (cbc->pos + size * nmemb > cbc->size)
269 externalErrorExit (); /* overflow */
270 memcpy (&cbc->buf[cbc->pos], ptr, size * nmemb);
271 cbc->pos += size * nmemb;
272 return size * nmemb;
273}
274
275
276struct check_uri_cls
277{
278 const char *volatile uri;
279};
280
281static void *
282check_uri_cb (void *cls,
283 const char *uri,
284 struct MHD_Connection *con)
285{
286 struct check_uri_cls *param = (struct check_uri_cls *) cls;
287 (void) con;
288
289 if (0 != strcmp (param->uri,
290 uri))
291 {
292 fprintf (stderr,
293 "Wrong URI: `%s', line: %d\n",
294 uri, __LINE__);
295 exit (22);
296 }
297 return NULL;
298}
299
300
301struct mhd_header_checker_param
302{
303 int found_header1;
304 int found_header2;
305 int found_header4;
306};
307
308enum MHD_Result
309headerCheckerInterator (void *cls,
310 enum MHD_ValueKind kind,
311 const char *key,
312 size_t key_size,
313 const char *value,
314 size_t value_size)
315{
316 struct mhd_header_checker_param *const param =
317 (struct mhd_header_checker_param *) cls;
318
319 if (NULL == param)
320 mhdErrorExitDesc ("cls parameter is NULL");
321
322 if (MHD_HEADER_KIND != kind)
323 return MHD_YES; /* Continue iteration */
324
325 if (0 == key_size)
326 mhdErrorExitDesc ("Zero key length");
327
328 if ((strlen (HEADER1_NAME) == key_size) &&
329 (0 == memcmp (key, HEADER1_NAME, key_size)))
330 {
331 if ((strlen (HEADER1_VALUE) == value_size) &&
332 (0 == memcmp (value, HEADER1_VALUE, value_size)))
333 param->found_header1 = 1;
334 else
335 fprintf (stderr, "Unexpected header value: '%.*s', expected: '%s'\n",
336 (int) value_size, value, HEADER1_VALUE);
337 }
338 else if ((strlen (HEADER2_NAME) == key_size) &&
339 (0 == memcmp (key, HEADER2_NAME, key_size)))
340 {
341 if ((strlen (HEADER2CR_VALUE) == value_size) &&
342 (0 == memcmp (value, HEADER2CR_VALUE, value_size)))
343 param->found_header2 = 1;
344 else
345 fprintf (stderr, "Unexpected header value: '%.*s', expected: '%s'\n",
346 (int) value_size, value, HEADER2CR_VALUE);
347 }
348 else if ((strlen (HEADER4_NAME) == key_size) &&
349 (0 == memcmp (key, HEADER4_NAME, key_size)))
350 {
351 if ((strlen (HEADER4_VALUE) == value_size) &&
352 (0 == memcmp (value, HEADER4_VALUE, value_size)))
353 param->found_header4 = 1;
354 else
355 fprintf (stderr, "Unexpected header value: '%.*s', expected: '%s'\n",
356 (int) value_size, value, HEADER4_VALUE);
357 }
358 return MHD_YES;
359}
360
361
362struct ahc_cls_type
363{
364 const char *volatile rp_data;
365 volatile size_t rp_data_size;
366 struct mhd_header_checker_param header_check_param;
367 const char *volatile rq_method;
368 const char *volatile rq_url;
369};
370
371
372static enum MHD_Result
373ahcCheck (void *cls,
374 struct MHD_Connection *connection,
375 const char *url,
376 const char *method,
377 const char *version,
378 const char *upload_data, size_t *upload_data_size,
379 void **con_cls)
380{
381 static int ptr;
382 struct MHD_Response *response;
383 enum MHD_Result ret;
384 struct ahc_cls_type *const param = (struct ahc_cls_type *) cls;
385
386 if (0 != strcmp (version, MHD_HTTP_VERSION_1_1))
387 mhdErrorExitDesc ("Unexpected HTTP version");
388
389 if (0 != strcmp (url, param->rq_url))
390 mhdErrorExitDesc ("Unexpected URI");
391
392 if (NULL != upload_data)
393 mhdErrorExitDesc ("'upload_data' is not NULL");
394
395 if (NULL == upload_data_size)
396 mhdErrorExitDesc ("'upload_data_size' pointer is not NULL");
397
398 if (0 != *upload_data_size)
399 mhdErrorExitDesc ("'*upload_data_size' value is not zero");
400
401 if (NULL == param)
402 mhdErrorExitDesc ("cls parameter is NULL");
403
404 if (0 != strcmp (param->rq_method, method))
405 mhdErrorExitDesc ("Unexpected request method");
406
407 if (&ptr != *con_cls)
408 {
409 *con_cls = &ptr;
410 return MHD_YES;
411 }
412 *con_cls = NULL;
413
414 if (1 > MHD_get_connection_values_n (connection, MHD_HEADER_KIND,
415 &headerCheckerInterator,
416 &param->header_check_param))
417 mhdErrorExitDesc ("Wrong number of headers in the request");
418
419 response = MHD_create_response_from_buffer (param->rp_data_size,
420 (void*) param->rp_data,
421 MHD_RESPMEM_MUST_COPY);
422 if (NULL == response)
423 mhdErrorExitDesc ("Failed to create response");
424
425 ret = MHD_queue_response (connection,
426 MHD_HTTP_OK,
427 response);
428 MHD_destroy_response (response);
429 if (MHD_YES != ret)
430 mhdErrorExitDesc ("Failed to queue response");
431
432 return ret;
433}
434
435
436struct curlQueryParams
437{
438 /* Destination path for CURL query */
439 const char *queryPath;
440
441#if CURL_AT_LEAST_VERSION (7, 62, 0)
442 CURLU *url;
443#endif /* CURL_AT_LEAST_VERSION(7, 62, 0) */
444
445 /* Custom query method, NULL for default */
446 const char *method;
447
448 /* Destination port for CURL query */
449 int queryPort;
450
451 /* List of additional request headers */
452 struct curl_slist *headers;
453
454 /* CURL query result error flag */
455 volatile int queryError;
456
457 /* Response HTTP code, zero if no response */
458 volatile int responseCode;
459};
460
461
462static CURL *
463curlEasyInitForTest (struct curlQueryParams *p,
464 struct lcurl_data_cb_param *dcbp,
465 struct headers_check_result *hdr_chk_result)
466{
467 CURL *c;
468
469 c = curl_easy_init ();
470 if (NULL == c)
471 libcurlErrorExitDesc ("curl_easy_init() failed");
472
473 if ((CURLE_OK != curl_easy_setopt (c, CURLOPT_NOSIGNAL, 1L)) ||
474 (CURLE_OK != curl_easy_setopt (c, CURLOPT_URL, p->queryPath)) ||
475 (CURLE_OK != curl_easy_setopt (c, CURLOPT_PORT, (long) p->queryPort)) ||
476 (CURLE_OK != curl_easy_setopt (c, CURLOPT_WRITEFUNCTION,
477 &copyBuffer)) ||
478 (CURLE_OK != curl_easy_setopt (c, CURLOPT_WRITEDATA, dcbp)) ||
479 (CURLE_OK != curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT,
480 (long) response_timeout_val)) ||
481 (CURLE_OK != curl_easy_setopt (c, CURLOPT_TIMEOUT,
482 (long) response_timeout_val)) ||
483 (CURLE_OK != curl_easy_setopt (c, CURLOPT_ERRORBUFFER,
484 libcurl_errbuf)) ||
485 (CURLE_OK != curl_easy_setopt (c, CURLOPT_HEADERFUNCTION,
486 lcurl_hdr_callback)) ||
487 (CURLE_OK != curl_easy_setopt (c, CURLOPT_HEADERDATA,
488 hdr_chk_result)) ||
489 (CURLE_OK != curl_easy_setopt (c, CURLOPT_PATH_AS_IS,
490 (long) 1)) ||
491 (CURLE_OK != curl_easy_setopt (c, CURLOPT_FAILONERROR, 1L)) ||
492 (oneone) ?
493 (CURLE_OK != curl_easy_setopt (c, CURLOPT_HTTP_VERSION,
494 CURL_HTTP_VERSION_1_1)) :
495 (CURLE_OK != curl_easy_setopt (c, CURLOPT_HTTP_VERSION,
496 CURL_HTTP_VERSION_1_0)))
497 libcurlErrorExitDesc ("curl_easy_setopt() failed");
498
499 if (CURLE_OK != curl_easy_setopt (c, CURLOPT_CUSTOMREQUEST, p->method))
500 libcurlErrorExitDesc ("curl_easy_setopt() failed");
501
502 if (CURLE_OK != curl_easy_setopt (c, CURLOPT_HTTPHEADER, p->headers))
503 libcurlErrorExitDesc ("curl_easy_setopt() failed");
504
505#if CURL_AT_LEAST_VERSION (7, 62, 0)
506 if (NULL != p->url)
507 {
508 if (CURLE_OK != curl_easy_setopt (c, CURLOPT_CURLU, p->url))
509 libcurlErrorExitDesc ("curl_easy_setopt() failed");
510 }
511#endif /* CURL_AT_LEAST_VERSION(7, 62, 0) */
512 return c;
513}
514
515
516static CURLcode
517performQueryExternal (struct MHD_Daemon *d, CURL *c)
518{
519 CURLM *multi;
520 time_t start;
521 struct timeval tv;
522 CURLcode ret;
523
524 ret = CURLE_FAILED_INIT; /* will be replaced with real result */
525 multi = NULL;
526 multi = curl_multi_init ();
527 if (multi == NULL)
528 libcurlErrorExitDesc ("curl_multi_init() failed");
529 if (CURLM_OK != curl_multi_add_handle (multi, c))
530 libcurlErrorExitDesc ("curl_multi_add_handle() failed");
531
532 start = time (NULL);
533 while (time (NULL) - start <= TIMEOUTS_VAL)
534 {
535 fd_set rs;
536 fd_set ws;
537 fd_set es;
538 MHD_socket maxMhdSk;
539 int maxCurlSk;
540 int running;
541
542 maxMhdSk = MHD_INVALID_SOCKET;
543 maxCurlSk = -1;
544 FD_ZERO (&rs);
545 FD_ZERO (&ws);
546 FD_ZERO (&es);
547 if (NULL != multi)
548 {
549 curl_multi_perform (multi, &running);
550 if (0 == running)
551 {
552 struct CURLMsg *msg;
553 int msgLeft;
554 int totalMsgs = 0;
555 do
556 {
557 msg = curl_multi_info_read (multi, &msgLeft);
558 if (NULL == msg)
559 libcurlErrorExitDesc ("curl_multi_info_read() failed");
560 totalMsgs++;
561 if (CURLMSG_DONE == msg->msg)
562 ret = msg->data.result;
563 } while (msgLeft > 0);
564 if (1 != totalMsgs)
565 {
566 fprintf (stderr,
567 "curl_multi_info_read returned wrong "
568 "number of results (%d).\n",
569 totalMsgs);
570 externalErrorExit ();
571 }
572 curl_multi_remove_handle (multi, c);
573 curl_multi_cleanup (multi);
574 multi = NULL;
575 }
576 else
577 {
578 if (CURLM_OK != curl_multi_fdset (multi, &rs, &ws, &es, &maxCurlSk))
579 libcurlErrorExitDesc ("curl_multi_fdset() failed");
580 }
581 }
582 if (NULL == multi)
583 { /* libcurl has finished, check whether MHD still needs to perform cleanup */
584 unsigned long long to;
585 if ((MHD_YES != MHD_get_timeout (d, &to)) || (0 != to))
586 break; /* MHD finished as well */
587 }
588 if (MHD_YES != MHD_get_fdset (d, &rs, &ws, &es, &maxMhdSk))
589 mhdErrorExitDesc ("MHD_get_fdset() failed");
590 tv.tv_sec = 0;
591 tv.tv_usec = 1000;
592#ifdef MHD_POSIX_SOCKETS
593 if (maxMhdSk > maxCurlSk)
594 maxCurlSk = maxMhdSk;
595#endif /* MHD_POSIX_SOCKETS */
596 if (-1 == select (maxCurlSk + 1, &rs, &ws, &es, &tv))
597 {
598#ifdef MHD_POSIX_SOCKETS
599 if (EINTR != errno)
600 externalErrorExitDesc ("Unexpected select() error");
601#else
602 if ((WSAEINVAL != WSAGetLastError ()) ||
603 (0 != rs.fd_count) || (0 != ws.fd_count) || (0 != es.fd_count) )
604 externalErrorExitDesc ("Unexpected select() error");
605 Sleep (1);
606#endif
607 }
608 if (MHD_YES != MHD_run_from_select (d, &rs, &ws, &es))
609 mhdErrorExitDesc ("MHD_run_from_select() failed");
610 }
611
612 return ret;
613}
614
615
616/* Returns zero for successful response and non-zero for failed response */
617static int
618doCurlQueryInThread (struct MHD_Daemon *d,
619 struct curlQueryParams *p,
620 struct headers_check_result *hdr_res,
621 const char *expected_data,
622 size_t expected_data_size)
623{
624 const union MHD_DaemonInfo *dinfo;
625 CURL *c;
626 struct lcurl_data_cb_param dcbp;
627 CURLcode errornum;
628 int use_external_poll;
629 long resp_code;
630
631 dinfo = MHD_get_daemon_info (d, MHD_DAEMON_INFO_FLAGS);
632 if (NULL == dinfo)
633 mhdErrorExitDesc ("MHD_get_daemon_info() failed");
634 use_external_poll = (0 == (dinfo->flags
635 & MHD_USE_INTERNAL_POLLING_THREAD));
636
637 if (NULL == p->queryPath
638#if CURL_AT_LEAST_VERSION (7, 62, 0)
639 && NULL == p->url
640#endif /* CURL_AT_LEAST_VERSION(7, 62, 0) */
641 )
642 abort ();
643
644 if (0 == p->queryPort)
645 abort ();
646
647 /* Test must not fail due to test's internal buffer shortage */
648 dcbp.size = expected_data_size * 2 + 1;
649 dcbp.buf = malloc (dcbp.size);
650 if (NULL == dcbp.buf)
651 externalErrorExit ();
652 dcbp.pos = 0;
653
654 memset (hdr_res, 0, sizeof(*hdr_res));
655
656 c = curlEasyInitForTest (p,
657 &dcbp, hdr_res);
658
659 if (! use_external_poll)
660 errornum = curl_easy_perform (c);
661 else
662 errornum = performQueryExternal (d, c);
663
664 if (CURLE_OK != curl_easy_getinfo (c, CURLINFO_RESPONSE_CODE, &resp_code))
665 libcurlErrorExitDesc ("curl_easy_getinfo() failed");
666
667 p->responseCode = (int) resp_code;
668 if ((CURLE_OK == errornum) && (200 != resp_code))
669 {
670 fprintf (stderr,
671 "Got reply with unexpected status code: %d\n",
672 p->responseCode);
673 mhdErrorExit ();
674 }
675
676 if (CURLE_OK != errornum)
677 {
678 if ((CURLE_GOT_NOTHING != errornum) && (CURLE_RECV_ERROR != errornum)
679 && (CURLE_HTTP_RETURNED_ERROR != errornum))
680 {
681 if (CURLE_OPERATION_TIMEDOUT == errornum)
682 mhdErrorExitDesc ("Request was aborted due to timeout");
683 fprintf (stderr, "libcurl returned expected error: %s\n",
684 curl_easy_strerror (errornum));
685 mhdErrorExitDesc ("Request failed due to unexpected error");
686 }
687 p->queryError = 1;
688 if ((0 != resp_code) &&
689 ((499 < resp_code) || (400 > resp_code))) /* TODO: add all expected error codes */
690 {
691 fprintf (stderr,
692 "Got reply with unexpected status code: %ld\n",
693 resp_code);
694 mhdErrorExit ();
695 }
696 }
697 else
698 {
699 if (dcbp.pos != expected_data_size)
700 mhdErrorExit ("libcurl reports wrong size of MHD reply body data");
701 else if (0 != memcmp (expected_data, dcbp.buf,
702 expected_data_size))
703 mhdErrorExit ("libcurl reports wrong MHD reply body data");
704 else
705 p->queryError = 0;
706 }
707
708 curl_easy_cleanup (c);
709 free (dcbp.buf);
710
711 return p->queryError;
712}
713
714
715/* Perform test queries, shut down MHD daemon, and free parameters */
716static int
717performTestQueries (struct MHD_Daemon *d, int d_port,
718 struct ahc_cls_type *ahc_param,
719 struct check_uri_cls *uri_cb_param)
720{
721 struct curlQueryParams qParam;
722 int ret = 0; /* Return value */
723 struct headers_check_result rp_headers_check;
724 struct curl_slist *curl_headers;
725 curl_headers = NULL;
726
727 /* Common parameters, to be individually overridden by specific test cases */
728 qParam.queryPort = d_port;
729 qParam.method = NULL; /* Use libcurl default: GET */
730 qParam.queryPath = URL_SCHEME_HOST EXPECTED_URI_BASE_PATH;
731#if CURL_AT_LEAST_VERSION (7, 62, 0)
732 qParam.url = NULL;
733#endif /* CURL_AT_LEAST_VERSION(7, 62, 0) */
734 qParam.headers = NULL; /* No additional headers */
735 uri_cb_param->uri = EXPECTED_URI_BASE_PATH;
736 ahc_param->rq_url = EXPECTED_URI_BASE_PATH;
737 ahc_param->rq_method = "GET"; /* Default expected method */
738
739 ahc_param->rp_data = "~";
740 ahc_param->rp_data_size = 1;
741
742 curl_headers = curl_slist_append (curl_headers, HEADER1);
743 if (NULL == curl_headers)
744 externalErrorExit ();
745 curl_headers = curl_slist_append (curl_headers, HEADER4);
746 if (NULL == curl_headers)
747 externalErrorExit ();
748 qParam.headers = curl_headers;
749
750 memset (&ahc_param->header_check_param, 0,
751 sizeof (ahc_param->header_check_param));
752
753 if (tricky_url)
754 {
755#if CURL_AT_LEAST_VERSION (7, 62, 0)
756 qParam.url = curl_url ();
757 if (NULL == qParam.url)
758 externalErrorExit ();
759
760 if ((CURLUE_OK != curl_url_set (qParam.url, CURLUPART_SCHEME, "http", 0)) ||
761 (CURLUE_OK != curl_url_set (qParam.url, CURLUPART_HOST, URL_HOST,
762 CURLU_PATH_AS_IS | CURLU_ALLOW_SPACE)) ||
763 (CURLUE_OK != curl_url_set (qParam.url, CURLUPART_PATH,
764 EXPECTED_URI_BASE_PATH_TRICKY, 0)))
765 libcurlErrorExit ();
766
767 qParam.queryPath = NULL;
768 uri_cb_param->uri = EXPECTED_URI_BASE_PATH_TRICKY;
769 ahc_param->rq_url = EXPECTED_URI_BASE_PATH_TRICKY;
770
771 if (0 != doCurlQueryInThread (d, &qParam, &rp_headers_check,
772 ahc_param->rp_data,
773 ahc_param->rp_data_size))
774 {
775 /* TODO: Allow fail only if relevant MHD mode set */
776 if (0 == qParam.responseCode)
777 {
778 fprintf (stderr, "Request failed without any valid response.\n");
779 ret = 1;
780 }
781 else
782 {
783 if (verbose)
784 printf ("Request failed with %d response code.\n",
785 qParam.responseCode);
786 (void) qParam.responseCode; /* TODO: check for the right response code */
787 ret = 0;
788 }
789 }
790 else
791 {
792 if (200 != qParam.responseCode)
793 {
794 fprintf (stderr, "Request succeed with wrong response code: %d.\n",
795 qParam.responseCode);
796 ret = 1;
797 }
798 else
799 {
800 ret = 0;
801 if (verbose)
802 printf ("Request succeed.\n");
803 }
804
805 if (! ahc_param->header_check_param.found_header1)
806 mhdErrorExitDesc ("Required header1 was not detected in request");
807 if (! ahc_param->header_check_param.found_header4)
808 mhdErrorExitDesc ("Required header4 was not detected in request");
809 }
810
811#else
812 fprintf ("This test requires libcurl version 7.62.0 or newer.\n");
813 abort ();
814#endif /* CURL_AT_LEAST_VERSION(7, 62, 0) */
815 }
816 else if (tricky_header2)
817 {
818 /* Reset libcurl headers */
819 qParam.headers = NULL;
820 curl_slist_free_all (curl_headers);
821 curl_headers = NULL;
822
823 /* Set special libcurl headers */
824 curl_headers = curl_slist_append (curl_headers, HEADER1);
825 if (NULL == curl_headers)
826 externalErrorExit ();
827 curl_headers = curl_slist_append (curl_headers, HEADER2CR);
828 if (NULL == curl_headers)
829 externalErrorExit ();
830 curl_headers = curl_slist_append (curl_headers, HEADER4);
831 if (NULL == curl_headers)
832 externalErrorExit ();
833 qParam.headers = curl_headers;
834
835 if (0 != doCurlQueryInThread (d, &qParam, &rp_headers_check,
836 ahc_param->rp_data,
837 ahc_param->rp_data_size))
838 {
839 /* TODO: Allow fail only if relevant MHD mode set */
840 if (0 == qParam.responseCode)
841 {
842 fprintf (stderr, "Request failed without any valid response.\n");
843 ret = 1;
844 }
845 else
846 {
847 if (verbose)
848 printf ("Request failed with %d response code.\n",
849 qParam.responseCode);
850 (void) qParam.responseCode; /* TODO: check for the right response code */
851 ret = 0;
852 }
853 }
854 else
855 {
856 if (200 != qParam.responseCode)
857 {
858 fprintf (stderr, "Request succeed with wrong response code: %d.\n",
859 qParam.responseCode);
860 ret = 1;
861 }
862 else
863 {
864 ret = 0;
865 if (verbose)
866 printf ("Request succeed.\n");
867 }
868
869 if (! ahc_param->header_check_param.found_header1)
870 mhdErrorExitDesc ("Required header1 was not detected in request");
871 if (! ahc_param->header_check_param.found_header2)
872 mhdErrorExitDesc ("Required header2 was not detected in request");
873 if (! ahc_param->header_check_param.found_header4)
874 mhdErrorExitDesc ("Required header4 was not detected in request");
875 }
876 }
877 else
878 externalErrorExitDesc ("No valid test test was selected");
879
880 MHD_stop_daemon (d);
881 curl_slist_free_all (curl_headers);
882 free (uri_cb_param);
883 free (ahc_param);
884
885 return ret;
886}
887
888
889enum testMhdThreadsType
890{
891 testMhdThreadExternal = 0,
892 testMhdThreadInternal = MHD_USE_INTERNAL_POLLING_THREAD,
893 testMhdThreadInternalPerConnection = MHD_USE_THREAD_PER_CONNECTION
894 | MHD_USE_INTERNAL_POLLING_THREAD,
895 testMhdThreadInternalPool
896};
897
898enum testMhdPollType
899{
900 testMhdPollBySelect = 0,
901 testMhdPollByPoll = MHD_USE_POLL,
902 testMhdPollByEpoll = MHD_USE_EPOLL,
903 testMhdPollAuto = MHD_USE_AUTO
904};
905
906/* Get number of threads for thread pool depending
907 * on used poll function and test type. */
908static unsigned int
909testNumThreadsForPool (enum testMhdPollType pollType)
910{
911 int numThreads = MHD_CPU_COUNT;
912 (void) pollType; /* Don't care about pollType for this test */
913 return numThreads; /* No practical limit for non-cleanup test */
914}
915
916
917static struct MHD_Daemon *
918startTestMhdDaemon (enum testMhdThreadsType thrType,
919 enum testMhdPollType pollType, int *pport,
920 struct ahc_cls_type **ahc_param,
921 struct check_uri_cls **uri_cb_param)
922{
923 struct MHD_Daemon *d;
924 const union MHD_DaemonInfo *dinfo;
925
926 if ((NULL == ahc_param) || (NULL == uri_cb_param))
927 abort ();
928
929 *ahc_param = (struct ahc_cls_type *) malloc (sizeof(struct ahc_cls_type));
930 if (NULL == *ahc_param)
931 externalErrorExit ();
932 *uri_cb_param =
933 (struct check_uri_cls *) malloc (sizeof(struct check_uri_cls));
934 if (NULL == *uri_cb_param)
935 externalErrorExit ();
936
937 if ( (0 == *pport) &&
938 (MHD_NO == MHD_is_feature_supported (MHD_FEATURE_AUTODETECT_BIND_PORT)) )
939 {
940 *pport = 4150;
941 if (tricky_url)
942 *pport += 1;
943 if (tricky_header2)
944 *pport += 2;
945 if (! oneone)
946 *pport += 16;
947 }
948
949 if (testMhdThreadInternalPool != thrType)
950 d = MHD_start_daemon (((int) thrType) | ((int) pollType)
951 | (verbose ? MHD_USE_ERROR_LOG : 0),
952 *pport, NULL, NULL,
953 &ahcCheck, *ahc_param,
954 MHD_OPTION_URI_LOG_CALLBACK, &check_uri_cb,
955 *uri_cb_param,
956 MHD_OPTION_END);
957 else
958 d = MHD_start_daemon (MHD_USE_INTERNAL_POLLING_THREAD | ((int) pollType)
959 | (verbose ? MHD_USE_ERROR_LOG : 0),
960 *pport, NULL, NULL,
961 &ahcCheck, *ahc_param,
962 MHD_OPTION_THREAD_POOL_SIZE,
963 testNumThreadsForPool (pollType),
964 MHD_OPTION_URI_LOG_CALLBACK, &check_uri_cb,
965 *uri_cb_param,
966 MHD_OPTION_END);
967
968 if (NULL == d)
969 {
970 fprintf (stderr, "Failed to start MHD daemon, errno=%d.\n", errno);
971 abort ();
972 }
973
974 if (0 == *pport)
975 {
976 dinfo = MHD_get_daemon_info (d, MHD_DAEMON_INFO_BIND_PORT);
977 if ((NULL == dinfo) || (0 == dinfo->port) )
978 {
979 fprintf (stderr, "MHD_get_daemon_info() failed.\n");
980 abort ();
981 }
982 *pport = (int) dinfo->port;
983 if (0 == global_port)
984 global_port = *pport; /* Reuse the same port for all tests */
985 }
986
987 return d;
988}
989
990
991/* Test runners */
992
993
994static int
995testExternalGet (void)
996{
997 struct MHD_Daemon *d;
998 int d_port = global_port; /* Daemon's port */
999 struct ahc_cls_type *ahc_param;
1000 struct check_uri_cls *uri_cb_param;
1001
1002 d = startTestMhdDaemon (testMhdThreadExternal, testMhdPollBySelect, &d_port,
1003 &ahc_param, &uri_cb_param);
1004
1005 return performTestQueries (d, d_port, ahc_param, uri_cb_param);
1006}
1007
1008
1009static int
1010testInternalGet (enum testMhdPollType pollType)
1011{
1012 struct MHD_Daemon *d;
1013 int d_port = global_port; /* Daemon's port */
1014 struct ahc_cls_type *ahc_param;
1015 struct check_uri_cls *uri_cb_param;
1016
1017 d = startTestMhdDaemon (testMhdThreadInternal, pollType, &d_port,
1018 &ahc_param, &uri_cb_param);
1019
1020 return performTestQueries (d, d_port, ahc_param, uri_cb_param);
1021}
1022
1023
1024static int
1025testMultithreadedGet (enum testMhdPollType pollType)
1026{
1027 struct MHD_Daemon *d;
1028 int d_port = global_port; /* Daemon's port */
1029 struct ahc_cls_type *ahc_param;
1030 struct check_uri_cls *uri_cb_param;
1031
1032 d = startTestMhdDaemon (testMhdThreadInternalPerConnection, pollType, &d_port,
1033 &ahc_param, &uri_cb_param);
1034 return performTestQueries (d, d_port, ahc_param, uri_cb_param);
1035}
1036
1037
1038static int
1039testMultithreadedPoolGet (enum testMhdPollType pollType)
1040{
1041 struct MHD_Daemon *d;
1042 int d_port = global_port; /* Daemon's port */
1043 struct ahc_cls_type *ahc_param;
1044 struct check_uri_cls *uri_cb_param;
1045
1046 d = startTestMhdDaemon (testMhdThreadInternalPool, pollType, &d_port,
1047 &ahc_param, &uri_cb_param);
1048 return performTestQueries (d, d_port, ahc_param, uri_cb_param);
1049}
1050
1051
1052int
1053main (int argc, char *const *argv)
1054{
1055 unsigned int errorCount = 0;
1056 unsigned int test_result = 0;
1057 verbose = 0;
1058
1059 if ((NULL == argv) || (0 == argv[0]))
1060 return 99;
1061 oneone = ! has_in_name (argv[0], "10");
1062 tricky_url = has_in_name (argv[0], "_url") ? 1 : 0;
1063 tricky_header2 = has_in_name (argv[0], "_header2") ? 1 : 0;
1064 if (1 != tricky_url + tricky_header2)
1065 return 99;
1066 verbose = ! has_param (argc, argv, "-q") || has_param (argc, argv, "--quiet");
1067
1068#if ! CURL_AT_LEAST_VERSION (7, 62, 0)
1069 if (tricky_url)
1070 {
1071 fprintf ("This test requires libcurl version 7.62.0 or newer.\n");
1072 return 77;
1073 }
1074#endif /* ! CURL_AT_LEAST_VERSION(7, 62, 0) */
1075
1076 test_global_init ();
1077
1078 /* Could be set to non-zero value to enforce using specific port
1079 * in the test */
1080 global_port = 0;
1081 test_result = testExternalGet ();
1082 if (test_result)
1083 fprintf (stderr, "FAILED: testExternalGet () - %u.\n", test_result);
1084 else if (verbose)
1085 printf ("PASSED: testExternalGet ().\n");
1086 errorCount += test_result;
1087 if (MHD_YES == MHD_is_feature_supported (MHD_FEATURE_THREADS))
1088 {
1089 test_result = testInternalGet (testMhdPollAuto);
1090 if (test_result)
1091 fprintf (stderr, "FAILED: testInternalGet (testMhdPollAuto) - %u.\n",
1092 test_result);
1093 else if (verbose)
1094 printf ("PASSED: testInternalGet (testMhdPollBySelect).\n");
1095 errorCount += test_result;
1096#ifdef _MHD_HEAVY_TESTS
1097 /* Actually tests are not heavy, but took too long to complete while
1098 * not really provide any additional results. */
1099 test_result = testInternalGet (testMhdPollBySelect);
1100 if (test_result)
1101 fprintf (stderr, "FAILED: testInternalGet (testMhdPollBySelect) - %u.\n",
1102 test_result);
1103 else if (verbose)
1104 printf ("PASSED: testInternalGet (testMhdPollBySelect).\n");
1105 errorCount += test_result;
1106 test_result = testMultithreadedPoolGet (testMhdPollBySelect);
1107 if (test_result)
1108 fprintf (stderr,
1109 "FAILED: testMultithreadedPoolGet (testMhdPollBySelect) - %u.\n",
1110 test_result);
1111 else if (verbose)
1112 printf ("PASSED: testMultithreadedPoolGet (testMhdPollBySelect).\n");
1113 errorCount += test_result;
1114 test_result = testMultithreadedGet (testMhdPollBySelect);
1115 if (test_result)
1116 fprintf (stderr,
1117 "FAILED: testMultithreadedGet (testMhdPollBySelect) - %u.\n",
1118 test_result);
1119 else if (verbose)
1120 printf ("PASSED: testMultithreadedGet (testMhdPollBySelect).\n");
1121 errorCount += test_result;
1122 if (MHD_YES == MHD_is_feature_supported (MHD_FEATURE_POLL))
1123 {
1124 test_result = testInternalGet (testMhdPollByPoll);
1125 if (test_result)
1126 fprintf (stderr, "FAILED: testInternalGet (testMhdPollByPoll) - %u.\n",
1127 test_result);
1128 else if (verbose)
1129 printf ("PASSED: testInternalGet (testMhdPollByPoll).\n");
1130 errorCount += test_result;
1131 }
1132 if (MHD_YES == MHD_is_feature_supported (MHD_FEATURE_EPOLL))
1133 {
1134 test_result = testInternalGet (testMhdPollByEpoll);
1135 if (test_result)
1136 fprintf (stderr, "FAILED: testInternalGet (testMhdPollByEpoll) - %u.\n",
1137 test_result);
1138 else if (verbose)
1139 printf ("PASSED: testInternalGet (testMhdPollByEpoll).\n");
1140 errorCount += test_result;
1141 }
1142#else
1143 /* Mute compiler warnings */
1144 (void) testMultithreadedGet;
1145 (void) testMultithreadedPoolGet;
1146#endif /* _MHD_HEAVY_TESTS */
1147 }
1148 if (0 != errorCount)
1149 fprintf (stderr,
1150 "Error (code: %u)\n",
1151 errorCount);
1152 else if (verbose)
1153 printf ("All tests passed.\n");
1154
1155 test_global_cleanup ();
1156
1157 return (errorCount == 0) ? 0 : 1; /* 0 == pass */
1158}