aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorEvgeny Grin (Karlson2k) <k2k@narod.ru>2021-10-03 20:06:01 +0300
committerEvgeny Grin (Karlson2k) <k2k@narod.ru>2021-10-03 20:07:28 +0300
commit4be55403c1219b6473fee8874bb00f531f95bd67 (patch)
treeebad890dfcf40d552a87864ee60903b071287fcb /src
parentd7cac578837064baabe1284072be1ceb34dae23e (diff)
downloadlibmicrohttpd-4be55403c1219b6473fee8874bb00f531f95bd67.tar.gz
libmicrohttpd-4be55403c1219b6473fee8874bb00f531f95bd67.zip
Added set of test test_toolarge
Added a set of tests for checking of possible buffer overflow / overrun but using range of sizes for request and response elements starting for valid ranges. Tests are designed with thorough and detailed checking of all parameters provided and detected by MHD.
Diffstat (limited to 'src')
-rw-r--r--src/testcurl/Makefile.am32
-rw-r--r--src/testcurl/test_toolarge.c1619
2 files changed, 1651 insertions, 0 deletions
diff --git a/src/testcurl/Makefile.am b/src/testcurl/Makefile.am
index 6c109eb0..f5b651c3 100644
--- a/src/testcurl/Makefile.am
+++ b/src/testcurl/Makefile.am
@@ -99,6 +99,14 @@ check_PROGRAMS = \
99 test_process_headers \ 99 test_process_headers \
100 test_process_arguments \ 100 test_process_arguments \
101 test_parse_cookies \ 101 test_parse_cookies \
102 test_toolarge_method \
103 test_toolarge_url \
104 test_toolarge_request_header_name \
105 test_toolarge_request_header_value \
106 test_toolarge_request_headers \
107 test_toolarge_reply_header_name \
108 test_toolarge_reply_header_value \
109 test_toolarge_reply_headers \
102 test_large_put \ 110 test_large_put \
103 test_get11 \ 111 test_get11 \
104 test_get_iovec11 \ 112 test_get_iovec11 \
@@ -423,3 +431,27 @@ test_termination_SOURCES = \
423 431
424test_timeout_SOURCES = \ 432test_timeout_SOURCES = \
425 test_timeout.c mhd_has_in_name.h 433 test_timeout.c mhd_has_in_name.h
434
435test_toolarge_method_SOURCES = \
436 test_toolarge.c ../microhttpd/test_helpers.h
437
438test_toolarge_url_SOURCES = \
439 test_toolarge.c ../microhttpd/test_helpers.h
440
441test_toolarge_request_header_name_SOURCES = \
442 test_toolarge.c ../microhttpd/test_helpers.h
443
444test_toolarge_request_header_value_SOURCES = \
445 test_toolarge.c ../microhttpd/test_helpers.h
446
447test_toolarge_request_headers_SOURCES = \
448 test_toolarge.c ../microhttpd/test_helpers.h
449
450test_toolarge_reply_header_name_SOURCES = \
451 test_toolarge.c ../microhttpd/test_helpers.h
452
453test_toolarge_reply_header_value_SOURCES = \
454 test_toolarge.c ../microhttpd/test_helpers.h
455
456test_toolarge_reply_headers_SOURCES = \
457 test_toolarge.c ../microhttpd/test_helpers.h
diff --git a/src/testcurl/test_toolarge.c b/src/testcurl/test_toolarge.c
new file mode 100644
index 00000000..daa8d7d8
--- /dev/null
+++ b/src/testcurl/test_toolarge.c
@@ -0,0 +1,1619 @@
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 data larger then buffers.
24 * @details Testcases for handling of various situations when data cannot fit
25 * the buffers. Address sanitizers and debug asserts should increase
26 * number of problems detected by this test.
27 * Tests start with valid sizes to ensure that normal data is processed
28 * correctly and then sizes are monotonically increased to ensure that
29 * overflow is handled correctly at all stages in all codepaths.
30 * Tests with valid sizes are repeated several times to ensure that
31 * tests are failed because of overflow, but because of second run.
32 * @author Karlson2k (Evgeny Grin)
33 * @author Christian Grothoff
34 */
35#include "MHD_config.h"
36#include "platform.h"
37#include <curl/curl.h>
38#include <microhttpd.h>
39#include <stdlib.h>
40#include <string.h>
41#include <time.h>
42#include "test_helpers.h"
43#include "mhd_sockets.h" /* only macros used */
44
45#ifdef HAVE_STRINGS_H
46#include <strings.h>
47#endif /* HAVE_STRINGS_H */
48
49#ifdef _WIN32
50#ifndef WIN32_LEAN_AND_MEAN
51#define WIN32_LEAN_AND_MEAN 1
52#endif /* !WIN32_LEAN_AND_MEAN */
53#include <windows.h>
54#endif
55
56#ifndef WINDOWS
57#include <unistd.h>
58#include <sys/socket.h>
59#endif
60
61#ifdef HAVE_LIMITS_H
62#include <limits.h>
63#endif /* HAVE_LIMITS_H */
64
65#if defined(MHD_CPU_COUNT) && (MHD_CPU_COUNT + 0) < 2
66#undef MHD_CPU_COUNT
67#endif
68#if ! defined(MHD_CPU_COUNT)
69#define MHD_CPU_COUNT 2
70#endif
71#if MHD_CPU_COUNT > 32
72#undef MHD_CPU_COUNT
73/* Limit to reasonable value */
74#define MHD_CPU_COUNT 32
75#endif /* MHD_CPU_COUNT > 32 */
76
77
78#if defined(HAVE___FUNC__)
79#define externalErrorExit(ignore) \
80 _externalErrorExit_func(NULL, __func__, __LINE__)
81#define externalErrorExitDesc(errDesc) \
82 _externalErrorExit_func(errDesc, __func__, __LINE__)
83#define libcurlErrorExit(ignore) \
84 _libcurlErrorExit_func(NULL, __func__, __LINE__)
85#define libcurlErrorExitDesc(errDesc) \
86 _libcurlErrorExit_func(errDesc, __func__, __LINE__)
87#define mhdErrorExit(ignore) \
88 _mhdErrorExit_func(NULL, __func__, __LINE__)
89#define mhdErrorExitDesc(errDesc) \
90 _mhdErrorExit_func(errDesc, __func__, __LINE__)
91#elif defined(HAVE___FUNCTION__)
92#define externalErrorExit(ignore) \
93 _externalErrorExit_func(NULL, __FUNCTION__, __LINE__)
94#define externalErrorExitDesc(errDesc) \
95 _externalErrorExit_func(errDesc, __FUNCTION__, __LINE__)
96#define libcurlErrorExit(ignore) \
97 _libcurlErrorExit_func(NULL, __FUNCTION__, __LINE__)
98#define libcurlErrorExitDesc(errDesc) \
99 _libcurlErrorExit_func(errDesc, __FUNCTION__, __LINE__)
100#define mhdErrorExit(ignore) \
101 _mhdErrorExit_func(NULL, __FUNCTION__, __LINE__)
102#define mhdErrorExitDesc(errDesc) \
103 _mhdErrorExit_func(errDesc, __FUNCTION__, __LINE__)
104#else
105#define externalErrorExit(ignore) _externalErrorExit_func(NULL, NULL, __LINE__)
106#define externalErrorExitDesc(errDesc) \
107 _externalErrorExit_func(errDesc, NULL, __LINE__)
108#define libcurlErrorExit(ignore) _libcurlErrorExit_func(NULL, NULL, __LINE__)
109#define libcurlErrorExitDesc(errDesc) \
110 _libcurlErrorExit_func(errDesc, NULL, __LINE__)
111#define mhdErrorExit(ignore) _mhdErrorExit_func(NULL, NULL, __LINE__)
112#define mhdErrorExitDesc(errDesc) _mhdErrorExit_func(errDesc, NULL, __LINE__)
113#endif
114
115
116static void
117_externalErrorExit_func (const char *errDesc, const char *funcName, int lineNum)
118{
119 if ((NULL != errDesc) && (0 != errDesc[0]))
120 fprintf (stderr, "%s", errDesc);
121 else
122 fprintf (stderr, "System or external library call failed");
123 if ((NULL != funcName) && (0 != funcName[0]))
124 fprintf (stderr, " in %s", funcName);
125 if (0 < lineNum)
126 fprintf (stderr, " at line %d", lineNum);
127
128 fprintf (stderr, ".\nLast errno value: %d (%s)\n", (int) errno,
129 strerror (errno));
130#ifdef MHD_WINSOCK_SOCKETS
131 fprintf (stderr, "WSAGetLastError() value: %d\n", (int) WSAGetLastError ());
132#endif /* MHD_WINSOCK_SOCKETS */
133 fflush (stderr);
134 exit (99);
135}
136
137
138static char libcurl_errbuf[CURL_ERROR_SIZE] = "";
139
140static void
141_libcurlErrorExit_func (const char *errDesc, const char *funcName, int lineNum)
142{
143 if ((NULL != errDesc) && (0 != errDesc[0]))
144 fprintf (stderr, "%s", errDesc);
145 else
146 fprintf (stderr, "CURL library call failed");
147 if ((NULL != funcName) && (0 != funcName[0]))
148 fprintf (stderr, " in %s", funcName);
149 if (0 < lineNum)
150 fprintf (stderr, " at line %d", lineNum);
151
152 fprintf (stderr, ".\nLast errno value: %d (%s)\n", (int) errno,
153 strerror (errno));
154 if (0 != libcurl_errbuf[0])
155 fprintf (stderr, "Last libcurl error details: %s\n", libcurl_errbuf);
156
157 fflush (stderr);
158 exit (99);
159}
160
161
162static void
163_mhdErrorExit_func (const char *errDesc, const char *funcName, int lineNum)
164{
165 if ((NULL != errDesc) && (0 != errDesc[0]))
166 fprintf (stderr, "%s", errDesc);
167 else
168 fprintf (stderr, "MHD unexpected error");
169 if ((NULL != funcName) && (0 != funcName[0]))
170 fprintf (stderr, " in %s", funcName);
171 if (0 < lineNum)
172 fprintf (stderr, " at line %d", lineNum);
173
174 fprintf (stderr, ".\nLast errno value: %d (%s)\n", (int) errno,
175 strerror (errno));
176
177 fflush (stderr);
178 exit (8);
179}
180
181
182/* Could be increased to facilitate debugging */
183#define TIMEOUTS_VAL 500
184
185#define EXPECTED_URI_BASE_PATH "/a"
186
187#define URL_SCHEME_HOST "http:/" "/127.0.0.1"
188
189#define N1_HEADER_NAME "n"
190#define N1_HEADER_VALUE "1"
191#define N1_HEADER N1_HEADER_NAME ": " N1_HEADER_VALUE
192#define N1_HEADER_CRLF N1_HEADER "\r\n"
193
194#define BUFFER_SIZE 1024
195
196/* The size of the test element that must pass the test */
197#define TEST_OK_SIZE (BUFFER_SIZE - 384)
198
199/* The size of the test element where tests are started */
200#define TEST_START_SIZE (TEST_OK_SIZE - 16)
201
202/* The size of the test element that must definitely fail */
203#define TEST_FAIL_SIZE (BUFFER_SIZE + 32)
204
205/* Special value for request many headers test.
206 * MHD uses the same buffer to store headers strings and pointers to the strings
207 * so allocation is multiplied for small request header. */
208/* The size of the test element that must pass the test */
209#define TEST_RQ_N1_OK_SIZE 50
210
211/* The size of the test element where tests are started */
212#define TEST_RQ_N1_START_SIZE (TEST_RQ_N1_OK_SIZE - 32)
213
214/* Global parameters */
215static int verbose; /**< Be verbose */
216static int oneone; /**< If false use HTTP/1.0 for requests*/
217static int global_port; /**< MHD daemons listen port number */
218static int large_req_method; /**< Large request method */
219static int large_req_url; /**< Large request URL */
220static int large_req_header_name; /**< Large request single header name */
221static int large_req_header_value; /**< Large request single header value */
222static int large_req_headers; /**< Large request headers */
223static int large_rsp_header_name; /**< Large response single header name */
224static int large_rsp_header_value; /**< Large response single header value */
225static int large_rsp_headers; /**< Large response headers */
226static int response_timeout_val = TIMEOUTS_VAL;
227
228/* Current test parameters */
229/* * Moved to local variables * */
230
231/* Static helper variables */
232/* * None for this test * */
233
234static void
235test_global_init (void)
236{
237 libcurl_errbuf[0] = 0;
238
239 if (0 != curl_global_init (CURL_GLOBAL_WIN32))
240 externalErrorExit ();
241}
242
243
244static void
245test_global_cleanup (void)
246{
247 curl_global_cleanup ();
248}
249
250
251struct headers_check_result
252{
253 unsigned int num_n1_headers;
254 unsigned int large_header_name_size;
255 unsigned int large_header_value_size;
256 int large_header_valid;
257};
258
259size_t
260lcurl_hdr_callback (char *buffer, size_t size, size_t nitems,
261 void *userdata)
262{
263 const size_t data_size = size * nitems;
264 struct headers_check_result *check_res =
265 (struct headers_check_result *) userdata;
266
267 if ((6 == data_size) && (0 == memcmp (N1_HEADER_CRLF, buffer, 6)))
268 check_res->num_n1_headers++;
269 else if ((5 <= data_size) && ('0' == buffer[0]))
270 {
271 const char *const col_ptr = strstr (buffer, ": ");
272 if (0 != check_res->large_header_value_size)
273 mhdErrorExitDesc ("Expected only one large header, " \
274 "but found two large headers in the reply");
275 check_res->large_header_valid = 0;
276 if (NULL != col_ptr)
277 {
278 const char *const name = buffer;
279 const size_t name_len = col_ptr - buffer;
280 const size_t val_pos = name_len + 2;
281 const size_t val_len = data_size - val_pos - 2; /* 2 = strlen("\r\n") */
282 const char *const value = buffer + val_pos;
283 size_t i;
284 check_res->large_header_name_size = name_len;
285 check_res->large_header_value_size = val_len;
286 check_res->large_header_valid = 1; /* To be reset if any problem found */
287 for (i = 1; i < name_len; i++)
288 if ('a' + (char) (i % ('z' - 'a' + 1)) != name[i])
289 {
290 fprintf (stderr, "Wrong sequence in reply header name " \
291 "at position %u. Expected '%c', got '%c'\n",
292 (unsigned int) i,
293 'a' + (char) (i % ('z' - 'a' + 1)),
294 name[i]);
295 check_res->large_header_valid = 0;
296 break;
297 }
298 for (i = 0; i < val_len; i++)
299 if ('Z' - (char) (i % ('Z' - 'A' + 1)) != value[i])
300 {
301 fprintf (stderr, "Wrong sequence in reply header value " \
302 "at position %u. Expected '%c', got '%c'\n",
303 (unsigned int) i,
304 'Z' - (char) (i % ('Z' - 'A' + 1)),
305 value[i]);
306 check_res->large_header_valid = 0;
307 break;
308 }
309 }
310 }
311
312 return data_size;
313}
314
315
316struct lcurl_data_cb_param
317{
318 char *buf;
319 size_t pos;
320 size_t size;
321};
322
323
324static size_t
325copyBuffer (void *ptr, size_t size, size_t nmemb, void *ctx)
326{
327 struct lcurl_data_cb_param *cbc = ctx;
328
329 if (cbc->pos + size * nmemb > cbc->size)
330 externalErrorExit (); /* overflow */
331 memcpy (&cbc->buf[cbc->pos], ptr, size * nmemb);
332 cbc->pos += size * nmemb;
333 return size * nmemb;
334}
335
336
337struct check_uri_cls
338{
339 const char *volatile uri;
340};
341
342static void *
343check_uri_cb (void *cls,
344 const char *uri,
345 struct MHD_Connection *con)
346{
347 struct check_uri_cls *param = (struct check_uri_cls *) cls;
348 (void) con;
349
350 if (0 != strcmp (param->uri,
351 uri))
352 {
353 fprintf (stderr,
354 "Wrong URI: `%s', line: %d\n",
355 uri, __LINE__);
356 exit (22);
357 }
358 return NULL;
359}
360
361
362struct mhd_header_checker_param
363{
364 unsigned int num_n1_headers;
365 unsigned int large_header_name_size;
366 unsigned int large_header_value_size;
367 int large_header_valid;
368};
369
370enum MHD_Result
371headerCheckerInterator (void *cls,
372 enum MHD_ValueKind kind,
373 const char *key,
374 size_t key_size,
375 const char *value,
376 size_t value_size)
377{
378 struct mhd_header_checker_param *const param =
379 (struct mhd_header_checker_param *) cls;
380
381 if (NULL == param)
382 mhdErrorExitDesc ("cls parameter is NULL");
383
384 if (MHD_HEADER_KIND != kind)
385 return MHD_YES; /* Continue iteration */
386
387 if (0 == key_size)
388 mhdErrorExitDesc ("Zero key length");
389
390 if ((1 == key_size) && (1 == value_size) &&
391 ('n' == key[0]) && ('1' == value[0]))
392 param->num_n1_headers++;
393 else if ('0' == key[0])
394 { /* Found 'large' header */
395 size_t i;
396 param->large_header_name_size = key_size;
397 param->large_header_value_size = value_size;
398 param->large_header_valid = 1;
399 for (i = 1; i < key_size; i++)
400 if ('a' + (char) (i % ('z' - 'a' + 1)) != key[i])
401 {
402 fprintf (stderr, "Wrong sequence in request header name " \
403 "at position %u. Expected '%c', got '%c'\n",
404 (unsigned int) i,
405 'a' + (char) (i % ('z' - 'a' + 1)),
406 key[i]);
407 param->large_header_valid = 0;
408 break;
409 }
410 for (i = 0; i < value_size; i++)
411 if ('Z' - (char) (i % ('Z' - 'A' + 1)) != value[i])
412 {
413 fprintf (stderr, "Wrong sequence in request header value " \
414 "at position %u. Expected '%c', got '%c'\n",
415 (unsigned int) i,
416 'Z' - (char) (i % ('Z' - 'A' + 1)),
417 value[i]);
418 param->large_header_valid = 0;
419 break;
420 }
421 }
422 return MHD_YES;
423}
424
425
426struct ahc_cls_type
427{
428 const char *volatile rp_data;
429 volatile size_t rp_data_size;
430 volatile size_t rp_num_n1_hdrs;
431 volatile size_t rp_large_hdr_name_size;
432 volatile size_t rp_large_hdr_value_size;
433 struct mhd_header_checker_param header_check_param;
434 const char *volatile rq_method;
435 const char *volatile rq_url;
436};
437
438
439static enum MHD_Result
440ahcCheck (void *cls,
441 struct MHD_Connection *connection,
442 const char *url,
443 const char *method,
444 const char *version,
445 const char *upload_data, size_t *upload_data_size,
446 void **con_cls)
447{
448 static int ptr;
449 struct MHD_Response *response;
450 enum MHD_Result ret;
451 struct ahc_cls_type *const param = (struct ahc_cls_type *) cls;
452 size_t i;
453
454 if (0 != strcmp (version, MHD_HTTP_VERSION_1_1))
455 mhdErrorExitDesc ("Unexpected HTTP version");
456
457 if (0 != strcmp (url, param->rq_url))
458 mhdErrorExitDesc ("Unexpected URI");
459
460 if (NULL != upload_data)
461 mhdErrorExitDesc ("'upload_data' is not NULL");
462
463 if (NULL == upload_data_size)
464 mhdErrorExitDesc ("'upload_data_size' pointer is not NULL");
465
466 if (0 != *upload_data_size)
467 mhdErrorExitDesc ("'*upload_data_size' value is not zero");
468
469 if (NULL == param)
470 mhdErrorExitDesc ("cls parameter is NULL");
471
472 if (0 != strcmp (param->rq_method, method))
473 mhdErrorExitDesc ("Unexpected request method");
474
475 if (&ptr != *con_cls)
476 {
477 *con_cls = &ptr;
478 return MHD_YES;
479 }
480 *con_cls = NULL;
481
482 if (1 > MHD_get_connection_values_n (connection, MHD_HEADER_KIND,
483 &headerCheckerInterator,
484 &param->header_check_param))
485 mhdErrorExitDesc ("Wrong number of headers in the request");
486
487 response = MHD_create_response_from_buffer (param->rp_data_size,
488 (void*) param->rp_data,
489 MHD_RESPMEM_MUST_COPY);
490 if (NULL == response)
491 mhdErrorExitDesc ("Failed to create response");
492
493 for (i = 0; i < param->rp_num_n1_hdrs; i++)
494 if (MHD_YES != MHD_add_response_header (response,
495 N1_HEADER_NAME,
496 N1_HEADER_VALUE))
497 mhdErrorExitDesc ("Cannot add header");
498
499 if (0 != param->rp_large_hdr_name_size)
500 {
501 const size_t large_hdr_name_size = param->rp_large_hdr_name_size;
502 char *large_hrd_name;
503 const size_t large_hdr_value_size = param->rp_large_hdr_value_size;
504 char *large_hrd_value;
505
506 large_hrd_name = malloc (large_hdr_name_size + 1);
507 if (NULL == large_hrd_name)
508 externalErrorExit ();
509 if (0 != large_hdr_value_size)
510 large_hrd_value = malloc (large_hdr_value_size + 1);
511 else
512 large_hrd_value = NULL;
513
514 if ((0 != large_hdr_value_size) && (NULL == large_hrd_value))
515 externalErrorExit ();
516 large_hrd_name[0] = '0'; /* Name starts with zero for unique identification */
517 for (i = 1; i < large_hdr_name_size; i++)
518 large_hrd_name[i] = 'a' + i % ('z' - 'a' + 1);
519 large_hrd_name[large_hdr_name_size] = 0;
520 for (i = 0; i < large_hdr_value_size; i++)
521 large_hrd_value[i] = 'Z' - i % ('Z' - 'A' + 1);
522 large_hrd_value[large_hdr_value_size] = 0;
523 if (MHD_YES != MHD_add_response_header (response,
524 large_hrd_name,
525 large_hrd_value))
526 mhdErrorExitDesc ("Cannot add large header");
527
528 if (NULL != large_hrd_value)
529 free (large_hrd_value);
530 free (large_hrd_name);
531 }
532
533 ret = MHD_queue_response (connection,
534 MHD_HTTP_OK,
535 response);
536 MHD_destroy_response (response);
537 if (MHD_YES != ret)
538 mhdErrorExitDesc ("Failed to queue response");
539
540 return ret;
541}
542
543
544static CURL *
545curlEasyInitForTest (const char *queryPath, const char *method,
546 int port,
547 struct lcurl_data_cb_param *dcbp,
548 struct headers_check_result *hdr_chk_result,
549 struct curl_slist *headers)
550{
551 CURL *c;
552
553 c = curl_easy_init ();
554 if (NULL == c)
555 libcurlErrorExitDesc ("curl_easy_init() failed");
556
557 if ((CURLE_OK != curl_easy_setopt (c, CURLOPT_NOSIGNAL, 1L)) ||
558 (CURLE_OK != curl_easy_setopt (c, CURLOPT_URL, queryPath)) ||
559 (CURLE_OK != curl_easy_setopt (c, CURLOPT_PORT, (long) port)) ||
560 (CURLE_OK != curl_easy_setopt (c, CURLOPT_WRITEFUNCTION,
561 &copyBuffer)) ||
562 (CURLE_OK != curl_easy_setopt (c, CURLOPT_WRITEDATA, dcbp)) ||
563 (CURLE_OK != curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT,
564 (long) response_timeout_val)) ||
565 (CURLE_OK != curl_easy_setopt (c, CURLOPT_TIMEOUT,
566 (long) response_timeout_val)) ||
567 (CURLE_OK != curl_easy_setopt (c, CURLOPT_ERRORBUFFER,
568 libcurl_errbuf)) ||
569 (CURLE_OK != curl_easy_setopt (c, CURLOPT_HEADERFUNCTION,
570 lcurl_hdr_callback)) ||
571 (CURLE_OK != curl_easy_setopt (c, CURLOPT_HEADERDATA,
572 hdr_chk_result)) ||
573 (CURLE_OK != curl_easy_setopt (c, CURLOPT_FAILONERROR, 1L)) ||
574 (oneone) ?
575 (CURLE_OK != curl_easy_setopt (c, CURLOPT_HTTP_VERSION,
576 CURL_HTTP_VERSION_1_1)) :
577 (CURLE_OK != curl_easy_setopt (c, CURLOPT_HTTP_VERSION,
578 CURL_HTTP_VERSION_1_0)))
579 libcurlErrorExitDesc ("curl_easy_setopt() failed");
580
581 if (CURLE_OK != curl_easy_setopt (c, CURLOPT_CUSTOMREQUEST, method))
582 libcurlErrorExitDesc ("curl_easy_setopt() failed");
583
584 if (CURLE_OK != curl_easy_setopt (c, CURLOPT_HTTPHEADER, headers))
585 libcurlErrorExitDesc ("curl_easy_setopt() failed");
586
587 return c;
588}
589
590
591static CURLcode
592performQueryExternal (struct MHD_Daemon *d, CURL *c)
593{
594 CURLM *multi;
595 time_t start;
596 struct timeval tv;
597 CURLcode ret;
598
599 ret = CURLE_FAILED_INIT; /* will be replaced with real result */
600 multi = NULL;
601 multi = curl_multi_init ();
602 if (multi == NULL)
603 libcurlErrorExitDesc ("curl_multi_init() failed");
604 if (CURLM_OK != curl_multi_add_handle (multi, c))
605 libcurlErrorExitDesc ("curl_multi_add_handle() failed");
606
607 start = time (NULL);
608 while (time (NULL) - start <= TIMEOUTS_VAL)
609 {
610 fd_set rs;
611 fd_set ws;
612 fd_set es;
613 MHD_socket maxMhdSk;
614 int maxCurlSk;
615 int running;
616
617 maxMhdSk = MHD_INVALID_SOCKET;
618 maxCurlSk = -1;
619 FD_ZERO (&rs);
620 FD_ZERO (&ws);
621 FD_ZERO (&es);
622 if (NULL != multi)
623 {
624 curl_multi_perform (multi, &running);
625 if (0 == running)
626 {
627 struct CURLMsg *msg;
628 int msgLeft;
629 int totalMsgs = 0;
630 do
631 {
632 msg = curl_multi_info_read (multi, &msgLeft);
633 if (NULL == msg)
634 libcurlErrorExitDesc ("curl_multi_info_read() failed");
635 totalMsgs++;
636 if (CURLMSG_DONE == msg->msg)
637 ret = msg->data.result;
638 } while (msgLeft > 0);
639 if (1 != totalMsgs)
640 {
641 fprintf (stderr,
642 "curl_multi_info_read returned wrong "
643 "number of results (%d).\n",
644 totalMsgs);
645 externalErrorExit ();
646 }
647 curl_multi_remove_handle (multi, c);
648 curl_multi_cleanup (multi);
649 multi = NULL;
650 }
651 else
652 {
653 if (CURLM_OK != curl_multi_fdset (multi, &rs, &ws, &es, &maxCurlSk))
654 libcurlErrorExitDesc ("curl_multi_fdset() failed");
655 }
656 }
657 if (NULL == multi)
658 { /* libcurl has finished, check whether MHD still needs to perform cleanup */
659 unsigned long long to;
660 if ((MHD_YES != MHD_get_timeout (d, &to)) || (0 != to))
661 break; /* MHD finished as well */
662 }
663 if (MHD_YES != MHD_get_fdset (d, &rs, &ws, &es, &maxMhdSk))
664 mhdErrorExitDesc ("MHD_get_fdset() failed");
665 tv.tv_sec = 0;
666 tv.tv_usec = 1000;
667#ifdef MHD_POSIX_SOCKETS
668 if (maxMhdSk > maxCurlSk)
669 maxCurlSk = maxMhdSk;
670#endif /* MHD_POSIX_SOCKETS */
671 if (-1 == select (maxCurlSk + 1, &rs, &ws, &es, &tv))
672 {
673#ifdef MHD_POSIX_SOCKETS
674 if (EINTR != errno)
675 externalErrorExitDesc ("Unexpected select() error");
676#else
677 if ((WSAEINVAL != WSAGetLastError ()) ||
678 (0 != rs.fd_count) || (0 != ws.fd_count) || (0 != es.fd_count) )
679 externalErrorExitDesc ("Unexpected select() error");
680 Sleep (1);
681#endif
682 }
683 if (MHD_YES != MHD_run_from_select (d, &rs, &ws, &es))
684 mhdErrorExitDesc ("MHD_run_from_select() failed");
685 }
686
687 return ret;
688}
689
690
691struct curlQueryParams
692{
693 /* Destination path for CURL query */
694 const char *queryPath;
695
696 /* Custom query method, NULL for default */
697 const char *method;
698
699 /* Destination port for CURL query */
700 int queryPort;
701
702 /* List of additional request headers */
703 struct curl_slist *headers;
704
705 /* CURL query result error flag */
706 volatile int queryError;
707
708 /* Response HTTP code, zero if no response */
709 volatile int responseCode;
710};
711
712
713/* Returns zero for successful response and non-zero for failed response */
714static int
715doCurlQueryInThread (struct MHD_Daemon *d,
716 struct curlQueryParams *p,
717 struct headers_check_result *hdr_res,
718 const char *expected_data,
719 size_t expected_data_size)
720{
721 const union MHD_DaemonInfo *dinfo;
722 CURL *c;
723 struct lcurl_data_cb_param dcbp;
724 CURLcode errornum;
725 int use_external_poll;
726 long resp_code;
727
728 dinfo = MHD_get_daemon_info (d, MHD_DAEMON_INFO_FLAGS);
729 if (NULL == dinfo)
730 mhdErrorExitDesc ("MHD_get_daemon_info() failed");
731 use_external_poll = (0 == (dinfo->flags
732 & MHD_USE_INTERNAL_POLLING_THREAD));
733
734 if (NULL == p->queryPath)
735 abort ();
736
737 if (0 == p->queryPort)
738 abort ();
739
740 /* Test must not fail due to test's internal buffer shortage */
741 dcbp.size = TEST_FAIL_SIZE * 2 + 1;
742 dcbp.buf = malloc (dcbp.size);
743 if (NULL == dcbp.buf)
744 externalErrorExit ();
745 dcbp.pos = 0;
746
747 memset (hdr_res, 0, sizeof(*hdr_res));
748
749 c = curlEasyInitForTest (p->queryPath, p->method, p->queryPort,
750 &dcbp, hdr_res, p->headers);
751
752 if (! use_external_poll)
753 errornum = curl_easy_perform (c);
754 else
755 errornum = performQueryExternal (d, c);
756
757 if (CURLE_OK != curl_easy_getinfo (c, CURLINFO_RESPONSE_CODE, &resp_code))
758 libcurlErrorExitDesc ("curl_easy_getinfo() failed");
759
760 p->responseCode = (int) resp_code;
761 if ((CURLE_OK == errornum) && (200 != resp_code))
762 {
763 fprintf (stderr,
764 "Got reply with unexpected status code: %d\n",
765 p->responseCode);
766 mhdErrorExit ();
767 }
768
769 if (CURLE_OK != errornum)
770 {
771 if ((CURLE_GOT_NOTHING != errornum) && (CURLE_RECV_ERROR != errornum)
772 && (CURLE_HTTP_RETURNED_ERROR != errornum))
773 {
774 if (CURLE_OPERATION_TIMEDOUT == errornum)
775 mhdErrorExitDesc ("Request was aborted due to timeout");
776 fprintf (stderr, "libcurl returned expected error: %s\n",
777 curl_easy_strerror (errornum));
778 mhdErrorExitDesc ("Request failed due to unexpected error");
779 }
780 p->queryError = 1;
781 if ((0 != resp_code) &&
782 ((499 < resp_code) || (400 > resp_code))) /* TODO: add all expected error codes */
783 {
784 fprintf (stderr,
785 "Got reply with unexpected status code: %ld\n",
786 resp_code);
787 mhdErrorExit ();
788 }
789
790 return p->queryError;
791 }
792
793 if (dcbp.pos != expected_data_size)
794 mhdErrorExit ("libcurl reports wrong size of MHD reply body data");
795 else if (0 != memcmp (expected_data, dcbp.buf,
796 expected_data_size))
797 mhdErrorExit ("libcurl reports wrong MHD reply body data");
798 else
799 p->queryError = 0;
800
801 curl_easy_cleanup (c);
802 free (dcbp.buf);
803
804 return p->queryError;
805}
806
807
808/* Perform test queries, shut down MHD daemon, and free parameters */
809static int
810performTestQueries (struct MHD_Daemon *d, int d_port,
811 struct ahc_cls_type *ahc_param,
812 struct check_uri_cls *uri_cb_param)
813{
814 struct curlQueryParams qParam;
815 int ret = 0; /* Return value */
816 struct headers_check_result rp_headers_check;
817 char *buf;
818 size_t i;
819 size_t first_failed_at = 0;
820
821
822 buf = malloc (TEST_FAIL_SIZE + 1 + strlen (URL_SCHEME_HOST));
823 if (NULL == buf)
824 externalErrorExit ();
825
826 /* Common parameters, to be individually overridden by specific test cases */
827 qParam.queryPort = d_port;
828 qParam.method = NULL; /* Use libcurl default: GET */
829 qParam.queryPath = URL_SCHEME_HOST EXPECTED_URI_BASE_PATH;
830 qParam.headers = NULL; /* No additional headers */
831 uri_cb_param->uri = EXPECTED_URI_BASE_PATH;
832 ahc_param->rq_url = EXPECTED_URI_BASE_PATH;
833 ahc_param->rq_method = "GET"; /* Default expected method */
834
835 ahc_param->rp_data = "~";
836 ahc_param->rp_data_size = 1;
837 ahc_param->rp_large_hdr_name_size = 0;
838 ahc_param->rp_large_hdr_value_size = 0;
839 ahc_param->rp_num_n1_hdrs = 0;
840
841 if (large_req_method)
842 {
843 for (i = 0; i < TEST_START_SIZE; i++)
844 buf[i] = 'A' + i % ('Z' - 'A' + 1);
845 for (; i <= TEST_FAIL_SIZE; i++)
846 {
847 buf[i] = 0;
848
849 qParam.method = buf;
850 ahc_param->rq_method = buf;
851
852 memset (&ahc_param->header_check_param, 0,
853 sizeof (ahc_param->header_check_param));
854 if (0 != doCurlQueryInThread (d, &qParam, &rp_headers_check,
855 ahc_param->rp_data,
856 ahc_param->rp_data_size))
857 {
858 (void) qParam.responseCode; /* TODO: check for the right response code */
859 if (TEST_OK_SIZE >= i)
860 {
861 fprintf (stderr,
862 "Request failed when running with the valid value size.\n");
863 ret = 1; /* Failed too early */
864 }
865 if (0 == first_failed_at)
866 {
867 if (verbose)
868 fprintf (stderr, "First failed size is %u.\n", (unsigned int) i);
869 first_failed_at = i;
870 }
871 }
872 else
873 {
874 if (TEST_FAIL_SIZE == i)
875 {
876 fprintf (stderr, "Request succeed with the largest size.\n");
877 ret = 1; /* Succeed with largest value */
878 }
879 }
880 if (0 != ahc_param->header_check_param.num_n1_headers)
881 mhdErrorExitDesc ("Detected unexpected request headers");
882 if (0 != ahc_param->header_check_param.large_header_name_size)
883 mhdErrorExitDesc ("Detected unexpected large request header");
884 if (0 != rp_headers_check.num_n1_headers)
885 mhdErrorExitDesc ("Detected unexpected reply headers");
886 if (0 != rp_headers_check.large_header_name_size)
887 mhdErrorExitDesc ("Detected unexpected large reply header");
888
889 buf[i] = 'A' + i % ('Z' - 'A' + 1);
890 }
891 }
892 else if (large_req_url)
893 {
894 const size_t base_size = strlen (URL_SCHEME_HOST);
895 char *const url = buf + base_size;
896
897 memcpy (buf, URL_SCHEME_HOST, base_size);
898 url[0] = '/';
899 for (i = 1; i < TEST_START_SIZE; i++)
900 url[i] = 'a' + i % ('z' - 'a' + 1);
901 for (; i <= TEST_FAIL_SIZE; i++)
902 {
903 url[i] = 0;
904
905 qParam.queryPath = buf;
906 uri_cb_param->uri = url;
907 ahc_param->rq_url = url;
908
909 memset (&ahc_param->header_check_param, 0,
910 sizeof (ahc_param->header_check_param));
911 if (0 != doCurlQueryInThread (d, &qParam, &rp_headers_check,
912 ahc_param->rp_data,
913 ahc_param->rp_data_size))
914 {
915 (void) qParam.responseCode; /* TODO: check for the right response code */
916 if (TEST_OK_SIZE >= i)
917 {
918 fprintf (stderr,
919 "Request failed when running with the valid value size.\n");
920 ret = 1; /* Failed too early */
921 }
922 if (0 == first_failed_at)
923 {
924 if (verbose)
925 fprintf (stderr, "First failed size is %u.\n", (unsigned int) i);
926 first_failed_at = i;
927 }
928 }
929 else
930 {
931 if (TEST_FAIL_SIZE == i)
932 {
933 fprintf (stderr, "Request succeed with the largest size.\n");
934 ret = 1; /* Succeed with largest value */
935 }
936 }
937 if (0 != ahc_param->header_check_param.num_n1_headers)
938 mhdErrorExitDesc ("Detected unexpected request headers");
939 if (0 != ahc_param->header_check_param.large_header_name_size)
940 mhdErrorExitDesc ("Detected unexpected large request header");
941 if (0 != rp_headers_check.num_n1_headers)
942 mhdErrorExitDesc ("Detected unexpected reply headers");
943 if (0 != rp_headers_check.large_header_name_size)
944 mhdErrorExitDesc ("Detected unexpected large reply header");
945
946 url[i] = 'a' + i % ('z' - 'a' + 1);
947 }
948 }
949 else if (large_req_header_name)
950 {
951 buf[0] = '0'; /* Name starts with zero for unique identification */
952 for (i = 1; i < TEST_START_SIZE; i++)
953 buf[i] = 'a' + i % ('z' - 'a' + 1);
954 for (; i <= TEST_FAIL_SIZE; i++)
955 {
956 struct curl_slist *curl_headers;
957 curl_headers = NULL;
958
959 memcpy (buf + i, ": Z", 3); /* Note: strlen(": Z") is less than strlen(URL_SCHEME_HOST) */
960 buf[i + 3] = 0;
961
962 curl_headers = curl_slist_append (curl_headers, buf);
963 if (NULL == curl_headers)
964 externalErrorExit ();
965
966 qParam.headers = curl_headers;
967
968 memset (&ahc_param->header_check_param, 0,
969 sizeof (ahc_param->header_check_param));
970 if (0 != doCurlQueryInThread (d, &qParam, &rp_headers_check,
971 ahc_param->rp_data,
972 ahc_param->rp_data_size))
973 {
974 (void) qParam.responseCode; /* TODO: check for the right response code */
975 if (0 != ahc_param->header_check_param.large_header_name_size)
976 { /* If large header was processed, it must be valid */
977 if (i != ahc_param->header_check_param.large_header_name_size)
978 mhdErrorExitDesc ("Detected wrong large request header name size");
979 if (1 != ahc_param->header_check_param.large_header_value_size)
980 mhdErrorExitDesc ("Detected wrong large request header value size");
981 if (0 == ahc_param->header_check_param.large_header_valid)
982 mhdErrorExitDesc ("Detected wrong large request header");
983 }
984 if (TEST_OK_SIZE >= i)
985 {
986 fprintf (stderr,
987 "Request failed when running with the valid value size.\n");
988 ret = 1; /* Failed too early */
989 }
990 if (0 == first_failed_at)
991 {
992 if (verbose)
993 fprintf (stderr, "First failed size is %u.\n", (unsigned int) i);
994 first_failed_at = i;
995 }
996 }
997 else
998 {
999 if (i != ahc_param->header_check_param.large_header_name_size)
1000 mhdErrorExitDesc ("Detected wrong large request header name size");
1001 if (1 != ahc_param->header_check_param.large_header_value_size)
1002 mhdErrorExitDesc ("Detected wrong large request header value size");
1003 if (0 == ahc_param->header_check_param.large_header_valid)
1004 mhdErrorExitDesc ("Detected wrong large request header");
1005 if (TEST_FAIL_SIZE == i)
1006 {
1007 fprintf (stderr, "Request succeed with the largest size.\n");
1008 ret = 1; /* Succeed with largest value */
1009 }
1010 }
1011 if (0 != ahc_param->header_check_param.num_n1_headers)
1012 mhdErrorExitDesc ("Detected unexpected request headers");
1013 if (0 != rp_headers_check.num_n1_headers)
1014 mhdErrorExitDesc ("Detected unexpected reply headers");
1015 if (0 != rp_headers_check.large_header_name_size)
1016 mhdErrorExitDesc ("Detected unexpected large reply header");
1017
1018 curl_slist_free_all (curl_headers);
1019 buf[i] = 'a' + i % ('z' - 'a' + 1);
1020 }
1021 }
1022 else if (large_req_header_value)
1023 {
1024 char *const hdr_value = buf + 3;
1025 /* Name starts with zero for unique identification */
1026 memcpy (buf, "0: ", 3); /* Note: strlen(": Z") is less than strlen(URL_SCHEME_HOST) */
1027 for (i = 0; i < TEST_START_SIZE; i++)
1028 hdr_value[i] = 'Z' - i % ('Z' - 'A' + 1);
1029 for (; i <= TEST_FAIL_SIZE; i++)
1030 {
1031 struct curl_slist *curl_headers;
1032 curl_headers = NULL;
1033
1034 hdr_value[i] = 0;
1035
1036 curl_headers = curl_slist_append (curl_headers, buf);
1037 if (NULL == curl_headers)
1038 externalErrorExit ();
1039
1040 qParam.headers = curl_headers;
1041
1042 memset (&ahc_param->header_check_param, 0,
1043 sizeof (ahc_param->header_check_param));
1044 if (0 != doCurlQueryInThread (d, &qParam, &rp_headers_check,
1045 ahc_param->rp_data,
1046 ahc_param->rp_data_size))
1047 {
1048 (void) qParam.responseCode; /* TODO: check for the right response code */
1049 if (0 != ahc_param->header_check_param.large_header_name_size)
1050 { /* If large header was processed, it must be valid */
1051 if (1 != ahc_param->header_check_param.large_header_name_size)
1052 mhdErrorExitDesc ("Detected wrong large request header name size");
1053 if (i != ahc_param->header_check_param.large_header_value_size)
1054 mhdErrorExitDesc ("Detected wrong large request header value size");
1055 if (0 == ahc_param->header_check_param.large_header_valid)
1056 mhdErrorExitDesc ("Detected wrong large request header");
1057 }
1058 if (TEST_OK_SIZE >= i)
1059 {
1060 fprintf (stderr,
1061 "Request failed when running with the valid value size.\n");
1062 ret = 1; /* Failed too early */
1063 }
1064 if (0 == first_failed_at)
1065 {
1066 if (verbose)
1067 fprintf (stderr, "First failed size is %u.\n", (unsigned int) i);
1068 first_failed_at = i;
1069 }
1070 }
1071 else
1072 {
1073 if (1 != ahc_param->header_check_param.large_header_name_size)
1074 mhdErrorExitDesc ("Detected wrong large request header name size");
1075 if (i != ahc_param->header_check_param.large_header_value_size)
1076 mhdErrorExitDesc ("Detected wrong large request header value size");
1077 if (0 == ahc_param->header_check_param.large_header_valid)
1078 mhdErrorExitDesc ("Detected wrong large request header");
1079 if (TEST_FAIL_SIZE == i)
1080 {
1081 fprintf (stderr, "Request succeed with the largest size.\n");
1082 ret = 1; /* Succeed with largest value */
1083 }
1084 }
1085 if (0 != ahc_param->header_check_param.num_n1_headers)
1086 mhdErrorExitDesc ("Detected unexpected request headers");
1087 if (0 != rp_headers_check.num_n1_headers)
1088 mhdErrorExitDesc ("Detected unexpected reply headers");
1089 if (0 != rp_headers_check.large_header_name_size)
1090 mhdErrorExitDesc ("Detected unexpected large reply header");
1091
1092 curl_slist_free_all (curl_headers);
1093 hdr_value[i] = 'Z' - i % ('Z' - 'A' + 1);
1094 }
1095 }
1096 else if (large_req_headers)
1097 {
1098 unsigned int num_hdrs = 0;
1099 struct curl_slist *curl_headers;
1100 const size_t hdr_size = strlen (N1_HEADER_CRLF);
1101
1102 curl_headers = NULL;
1103
1104 for (i = 0; i < TEST_RQ_N1_START_SIZE; i += hdr_size)
1105 {
1106 curl_headers = curl_slist_append (curl_headers, N1_HEADER);
1107 if (NULL == curl_headers)
1108 externalErrorExit ();
1109 num_hdrs++;
1110 }
1111 for (; i <= TEST_FAIL_SIZE; i += hdr_size)
1112 {
1113 qParam.headers = curl_headers;
1114 ahc_param->header_check_param.num_n1_headers = num_hdrs;
1115
1116 memset (&ahc_param->header_check_param, 0,
1117 sizeof (ahc_param->header_check_param));
1118 if (0 != doCurlQueryInThread (d, &qParam, &rp_headers_check,
1119 ahc_param->rp_data,
1120 ahc_param->rp_data_size))
1121 {
1122 (void) qParam.responseCode; /* TODO: check for the right response code */
1123 if (0 != ahc_param->header_check_param.num_n1_headers)
1124 { /* If headers were processed, they must be valid */
1125 if (num_hdrs != ahc_param->header_check_param.num_n1_headers)
1126 mhdErrorExitDesc ("Detected wrong number of request headers");
1127 }
1128 if (TEST_RQ_N1_OK_SIZE >= i)
1129 {
1130 fprintf (stderr,
1131 "Request failed when running with the valid value size.\n");
1132 ret = 1; /* Failed too early */
1133 }
1134 if (0 == first_failed_at)
1135 {
1136 if (verbose)
1137 fprintf (stderr, "First failed size is %u.\n", (unsigned int) i);
1138 first_failed_at = i;
1139 }
1140 }
1141 else
1142 {
1143 if (num_hdrs != ahc_param->header_check_param.num_n1_headers)
1144 mhdErrorExitDesc ("Detected wrong number of request headers");
1145 if (TEST_FAIL_SIZE == i)
1146 {
1147 fprintf (stderr, "Request succeed with the largest size.\n");
1148 ret = 1; /* Succeed with largest value */
1149 }
1150 }
1151 if (0 != ahc_param->header_check_param.large_header_name_size)
1152 mhdErrorExitDesc ("Detected unexpected large request header");
1153 if (0 != rp_headers_check.num_n1_headers)
1154 mhdErrorExitDesc ("Detected unexpected reply headers");
1155 if (0 != rp_headers_check.large_header_name_size)
1156 mhdErrorExitDesc ("Detected unexpected large reply header");
1157
1158 curl_headers = curl_slist_append (curl_headers, N1_HEADER);
1159 if (NULL == curl_headers)
1160 externalErrorExit ();
1161 num_hdrs++;
1162 }
1163 curl_slist_free_all (curl_headers);
1164 }
1165 else if (large_rsp_header_name)
1166 {
1167 for (i = TEST_START_SIZE; i <= TEST_FAIL_SIZE; i++)
1168 {
1169 ahc_param->rp_large_hdr_name_size = i;
1170 ahc_param->rp_large_hdr_value_size = 1;
1171
1172 memset (&ahc_param->header_check_param, 0,
1173 sizeof (ahc_param->header_check_param));
1174 if (0 != doCurlQueryInThread (d, &qParam, &rp_headers_check,
1175 ahc_param->rp_data,
1176 ahc_param->rp_data_size))
1177 {
1178 (void) qParam.responseCode; /* TODO: check for the right response code */
1179 if (0 != rp_headers_check.large_header_name_size)
1180 mhdErrorExitDesc ("Detected unexpected large reply header");
1181 if (TEST_OK_SIZE >= i)
1182 {
1183 fprintf (stderr,
1184 "Request failed when running with the valid value size.\n");
1185 ret = 1; /* Failed too early */
1186 }
1187 if (0 == first_failed_at)
1188 {
1189 if (verbose)
1190 fprintf (stderr, "First failed size is %u.\n", (unsigned int) i);
1191 first_failed_at = i;
1192 }
1193 }
1194 else
1195 {
1196 if (i != rp_headers_check.large_header_name_size)
1197 mhdErrorExitDesc ("Detected wrong large reply header name size");
1198 if (1 != rp_headers_check.large_header_value_size)
1199 mhdErrorExitDesc ("Detected wrong large reply header value size");
1200 if (0 == rp_headers_check.large_header_valid)
1201 mhdErrorExitDesc ("Detected wrong large reply header");
1202 if (TEST_FAIL_SIZE == i)
1203 {
1204 fprintf (stderr, "Request succeed with the largest size.\n");
1205 ret = 1; /* Succeed with largest value */
1206 }
1207 }
1208 if (0 != ahc_param->header_check_param.num_n1_headers)
1209 mhdErrorExitDesc ("Detected unexpected request headers");
1210 if (0 != ahc_param->header_check_param.large_header_name_size)
1211 mhdErrorExitDesc ("Detected unexpected large request header");
1212 if (0 != rp_headers_check.num_n1_headers)
1213 mhdErrorExitDesc ("Detected unexpected reply headers");
1214 }
1215 }
1216 else if (large_rsp_header_value)
1217 {
1218 for (i = TEST_START_SIZE; i <= TEST_FAIL_SIZE; i++)
1219 {
1220 ahc_param->rp_large_hdr_name_size = 1;
1221 ahc_param->rp_large_hdr_value_size = i;
1222
1223 memset (&ahc_param->header_check_param, 0,
1224 sizeof (ahc_param->header_check_param));
1225 if (0 != doCurlQueryInThread (d, &qParam, &rp_headers_check,
1226 ahc_param->rp_data,
1227 ahc_param->rp_data_size))
1228 {
1229 (void) qParam.responseCode; /* TODO: check for the right response code */
1230 if (0 != rp_headers_check.large_header_name_size)
1231 mhdErrorExitDesc ("Detected unexpected large reply header");
1232 if (TEST_OK_SIZE >= i)
1233 {
1234 fprintf (stderr,
1235 "Request failed when running with the valid value size.\n");
1236 ret = 1; /* Failed too early */
1237 }
1238 if (0 == first_failed_at)
1239 {
1240 if (verbose)
1241 fprintf (stderr, "First failed size is %u.\n", (unsigned int) i);
1242 first_failed_at = i;
1243 }
1244 }
1245 else
1246 {
1247 if (1 != rp_headers_check.large_header_name_size)
1248 mhdErrorExitDesc ("Detected wrong large reply header name size");
1249 if (i != rp_headers_check.large_header_value_size)
1250 mhdErrorExitDesc ("Detected wrong large reply header value size");
1251 if (0 == rp_headers_check.large_header_valid)
1252 mhdErrorExitDesc ("Detected wrong large reply header");
1253 if (TEST_FAIL_SIZE == i)
1254 {
1255 fprintf (stderr, "Request succeed with the largest size.\n");
1256 ret = 1; /* Succeed with largest value */
1257 }
1258 }
1259 if (0 != ahc_param->header_check_param.num_n1_headers)
1260 mhdErrorExitDesc ("Detected unexpected request headers");
1261 if (0 != ahc_param->header_check_param.large_header_name_size)
1262 mhdErrorExitDesc ("Detected unexpected large request header");
1263 if (0 != rp_headers_check.num_n1_headers)
1264 mhdErrorExitDesc ("Detected unexpected reply headers");
1265 }
1266 }
1267 else if (large_rsp_headers)
1268 {
1269 unsigned int num_hrds;
1270 const size_t hdr_size = strlen (N1_HEADER_CRLF);
1271
1272 for (num_hrds = TEST_START_SIZE / hdr_size;
1273 num_hrds * hdr_size <= TEST_FAIL_SIZE; num_hrds++)
1274 {
1275 i = num_hrds * hdr_size;
1276 ahc_param->rp_num_n1_hdrs = num_hrds;
1277
1278 memset (&ahc_param->header_check_param, 0,
1279 sizeof (ahc_param->header_check_param));
1280 if (0 != doCurlQueryInThread (d, &qParam, &rp_headers_check,
1281 ahc_param->rp_data,
1282 ahc_param->rp_data_size))
1283 {
1284 (void) qParam.responseCode; /* TODO: check for the right response code */
1285 if (0 != rp_headers_check.num_n1_headers)
1286 mhdErrorExitDesc ("Detected unexpected reply headers");
1287 if (TEST_OK_SIZE >= i)
1288 {
1289 fprintf (stderr,
1290 "Request failed when running with the valid value size.\n");
1291 ret = 1; /* Failed too early */
1292 }
1293 if (0 == first_failed_at)
1294 {
1295 if (verbose)
1296 fprintf (stderr, "First failed size is %u.\n", (unsigned int) i);
1297 first_failed_at = i;
1298 }
1299 }
1300 else
1301 {
1302 if (num_hrds != rp_headers_check.num_n1_headers)
1303 mhdErrorExitDesc ("Detected wrong number of reply headers");
1304 if (TEST_FAIL_SIZE == i)
1305 {
1306 fprintf (stderr, "Request succeed with the largest size.\n");
1307 ret = 1; /* Succeed with largest value */
1308 }
1309 }
1310 if (0 != ahc_param->header_check_param.num_n1_headers)
1311 mhdErrorExitDesc ("Detected unexpected request headers");
1312 if (0 != ahc_param->header_check_param.large_header_name_size)
1313 mhdErrorExitDesc ("Detected unexpected large request header");
1314 if (0 != rp_headers_check.large_header_name_size)
1315 mhdErrorExitDesc ("Detected unexpected large reply header");
1316 }
1317 }
1318 else
1319 externalErrorExitDesc ("No valid test test was selected");
1320
1321 MHD_stop_daemon (d);
1322 free (buf);
1323 free (uri_cb_param);
1324 free (ahc_param);
1325
1326 return ret;
1327}
1328
1329
1330enum testMhdThreadsType
1331{
1332 testMhdThreadExternal = 0,
1333 testMhdThreadInternal = MHD_USE_INTERNAL_POLLING_THREAD,
1334 testMhdThreadInternalPerConnection = MHD_USE_THREAD_PER_CONNECTION
1335 | MHD_USE_INTERNAL_POLLING_THREAD,
1336 testMhdThreadInternalPool
1337};
1338
1339enum testMhdPollType
1340{
1341 testMhdPollBySelect = 0,
1342 testMhdPollByPoll = MHD_USE_POLL,
1343 testMhdPollByEpoll = MHD_USE_EPOLL,
1344 testMhdPollAuto = MHD_USE_AUTO
1345};
1346
1347/* Get number of threads for thread pool depending
1348 * on used poll function and test type. */
1349static unsigned int
1350testNumThreadsForPool (enum testMhdPollType pollType)
1351{
1352 int numThreads = MHD_CPU_COUNT;
1353 (void) pollType; /* Don't care about pollType for this test */
1354 return numThreads; /* No practical limit for non-cleanup test */
1355}
1356
1357
1358static struct MHD_Daemon *
1359startTestMhdDaemon (enum testMhdThreadsType thrType,
1360 enum testMhdPollType pollType, int *pport,
1361 struct ahc_cls_type **ahc_param,
1362 struct check_uri_cls **uri_cb_param)
1363{
1364 struct MHD_Daemon *d;
1365 const union MHD_DaemonInfo *dinfo;
1366
1367 if ((NULL == ahc_param) || (NULL == uri_cb_param))
1368 abort ();
1369
1370 *ahc_param = (struct ahc_cls_type *) malloc (sizeof(struct ahc_cls_type));
1371 if (NULL == *ahc_param)
1372 externalErrorExit ();
1373 *uri_cb_param =
1374 (struct check_uri_cls *) malloc (sizeof(struct check_uri_cls));
1375 if (NULL == *uri_cb_param)
1376 externalErrorExit ();
1377
1378 if ( (0 == *pport) &&
1379 (MHD_NO == MHD_is_feature_supported (MHD_FEATURE_AUTODETECT_BIND_PORT)) )
1380 {
1381 *pport = 4100;
1382 if (large_req_method)
1383 *pport += 1;
1384 if (large_req_url)
1385 *pport += 2;
1386 if (large_req_header_name)
1387 *pport += 3;
1388 if (large_req_header_value)
1389 *pport += 4;
1390 if (large_req_headers)
1391 *pport += 5;
1392 if (large_rsp_header_name)
1393 *pport += 6;
1394 if (large_rsp_header_value)
1395 *pport += 7;
1396 if (large_rsp_headers)
1397 *pport += 8;
1398 if (! oneone)
1399 *pport += 16;
1400 }
1401
1402 if (testMhdThreadInternalPool != thrType)
1403 d = MHD_start_daemon (((int) thrType) | ((int) pollType)
1404 | (verbose ? MHD_USE_ERROR_LOG : 0),
1405 *pport, NULL, NULL,
1406 &ahcCheck, *ahc_param,
1407 MHD_OPTION_URI_LOG_CALLBACK, &check_uri_cb,
1408 *uri_cb_param,
1409 MHD_OPTION_CONNECTION_MEMORY_LIMIT,
1410 (size_t) BUFFER_SIZE,
1411 MHD_OPTION_END);
1412 else
1413 d = MHD_start_daemon (MHD_USE_INTERNAL_POLLING_THREAD | ((int) pollType)
1414 | (verbose ? MHD_USE_ERROR_LOG : 0),
1415 *pport, NULL, NULL,
1416 &ahcCheck, *ahc_param,
1417 MHD_OPTION_THREAD_POOL_SIZE,
1418 testNumThreadsForPool (pollType),
1419 MHD_OPTION_URI_LOG_CALLBACK, &check_uri_cb,
1420 *uri_cb_param,
1421 MHD_OPTION_CONNECTION_MEMORY_LIMIT,
1422 (size_t) BUFFER_SIZE,
1423 MHD_OPTION_END);
1424
1425 if (NULL == d)
1426 {
1427 fprintf (stderr, "Failed to start MHD daemon, errno=%d.\n", errno);
1428 abort ();
1429 }
1430
1431 if (0 == *pport)
1432 {
1433 dinfo = MHD_get_daemon_info (d, MHD_DAEMON_INFO_BIND_PORT);
1434 if ((NULL == dinfo) || (0 == dinfo->port) )
1435 {
1436 fprintf (stderr, "MHD_get_daemon_info() failed.\n");
1437 abort ();
1438 }
1439 *pport = (int) dinfo->port;
1440 if (0 == global_port)
1441 global_port = *pport; /* Reuse the same port for all tests */
1442 }
1443
1444 return d;
1445}
1446
1447
1448/* Test runners */
1449
1450
1451static int
1452testExternalGet (void)
1453{
1454 struct MHD_Daemon *d;
1455 int d_port = global_port; /* Daemon's port */
1456 struct ahc_cls_type *ahc_param;
1457 struct check_uri_cls *uri_cb_param;
1458
1459 d = startTestMhdDaemon (testMhdThreadExternal, testMhdPollBySelect, &d_port,
1460 &ahc_param, &uri_cb_param);
1461
1462 return performTestQueries (d, d_port, ahc_param, uri_cb_param);
1463}
1464
1465
1466static int
1467testInternalGet (enum testMhdPollType pollType)
1468{
1469 struct MHD_Daemon *d;
1470 int d_port = global_port; /* Daemon's port */
1471 struct ahc_cls_type *ahc_param;
1472 struct check_uri_cls *uri_cb_param;
1473
1474 d = startTestMhdDaemon (testMhdThreadInternal, pollType, &d_port,
1475 &ahc_param, &uri_cb_param);
1476
1477 return performTestQueries (d, d_port, ahc_param, uri_cb_param);
1478}
1479
1480
1481static int
1482testMultithreadedGet (enum testMhdPollType pollType)
1483{
1484 struct MHD_Daemon *d;
1485 int d_port = global_port; /* Daemon's port */
1486 struct ahc_cls_type *ahc_param;
1487 struct check_uri_cls *uri_cb_param;
1488
1489 d = startTestMhdDaemon (testMhdThreadInternalPerConnection, pollType, &d_port,
1490 &ahc_param, &uri_cb_param);
1491 return performTestQueries (d, d_port, ahc_param, uri_cb_param);
1492}
1493
1494
1495static int
1496testMultithreadedPoolGet (enum testMhdPollType pollType)
1497{
1498 struct MHD_Daemon *d;
1499 int d_port = global_port; /* Daemon's port */
1500 struct ahc_cls_type *ahc_param;
1501 struct check_uri_cls *uri_cb_param;
1502
1503 d = startTestMhdDaemon (testMhdThreadInternalPool, pollType, &d_port,
1504 &ahc_param, &uri_cb_param);
1505 return performTestQueries (d, d_port, ahc_param, uri_cb_param);
1506}
1507
1508
1509int
1510main (int argc, char *const *argv)
1511{
1512 unsigned int errorCount = 0;
1513 unsigned int test_result = 0;
1514 verbose = 0;
1515
1516 if ((NULL == argv) || (0 == argv[0]))
1517 return 99;
1518 oneone = ! has_in_name (argv[0], "10");
1519 large_req_method = has_in_name (argv[0], "_method") ? 1 : 0;
1520 large_req_url = has_in_name (argv[0], "_url") ? 1 : 0;
1521 large_req_header_name = has_in_name (argv[0], "_request_header_name") ?
1522 1 : 0;
1523 large_req_header_value = has_in_name (argv[0], "_request_header_value") ?
1524 1 : 0;
1525 large_req_headers = has_in_name (argv[0], "_request_headers") ? 1 : 0;
1526 large_rsp_header_name = has_in_name (argv[0], "_reply_header_name") ?
1527 1 : 0;
1528 large_rsp_header_value = has_in_name (argv[0], "_reply_header_value") ?
1529 1 : 0;
1530 large_rsp_headers = has_in_name (argv[0], "_reply_headers") ? 1 : 0;
1531 if (large_req_method + large_req_url + large_req_header_name
1532 + large_req_header_value + large_req_headers + large_rsp_header_name
1533 + large_rsp_header_value + large_rsp_headers != 1)
1534 return 99;
1535 verbose = ! has_param (argc, argv, "-q") || has_param (argc, argv, "--quiet");
1536
1537 test_global_init ();
1538
1539 /* Could be set to non-zero value to enforce using specific port
1540 * in the test */
1541 global_port = 0;
1542 test_result = testExternalGet ();
1543 if (test_result)
1544 fprintf (stderr, "FAILED: testExternalGet () - %u.\n", test_result);
1545 else if (verbose)
1546 printf ("PASSED: testExternalGet ().\n");
1547 errorCount += test_result;
1548 if (MHD_YES == MHD_is_feature_supported (MHD_FEATURE_THREADS))
1549 {
1550 test_result = testInternalGet (testMhdPollAuto);
1551 if (test_result)
1552 fprintf (stderr, "FAILED: testInternalGet (testMhdPollAuto) - %u.\n",
1553 test_result);
1554 else if (verbose)
1555 printf ("PASSED: testInternalGet (testMhdPollBySelect).\n");
1556 errorCount += test_result;
1557#ifdef _MHD_HEAVY_TESTS
1558 /* Actually tests are not heavy, but took too long to complete while
1559 * not really provide any additional results. */
1560 test_result = testInternalGet (testMhdPollBySelect);
1561 if (test_result)
1562 fprintf (stderr, "FAILED: testInternalGet (testMhdPollBySelect) - %u.\n",
1563 test_result);
1564 else if (verbose)
1565 printf ("PASSED: testInternalGet (testMhdPollBySelect).\n");
1566 errorCount += test_result;
1567 test_result = testMultithreadedPoolGet (testMhdPollBySelect);
1568 if (test_result)
1569 fprintf (stderr,
1570 "FAILED: testMultithreadedPoolGet (testMhdPollBySelect) - %u.\n",
1571 test_result);
1572 else if (verbose)
1573 printf ("PASSED: testMultithreadedPoolGet (testMhdPollBySelect).\n");
1574 errorCount += test_result;
1575 test_result = testMultithreadedGet (testMhdPollBySelect);
1576 if (test_result)
1577 fprintf (stderr,
1578 "FAILED: testMultithreadedGet (testMhdPollBySelect) - %u.\n",
1579 test_result);
1580 else if (verbose)
1581 printf ("PASSED: testMultithreadedGet (testMhdPollBySelect).\n");
1582 errorCount += test_result;
1583 if (MHD_YES == MHD_is_feature_supported (MHD_FEATURE_POLL))
1584 {
1585 test_result = testInternalGet (testMhdPollByPoll);
1586 if (test_result)
1587 fprintf (stderr, "FAILED: testInternalGet (testMhdPollByPoll) - %u.\n",
1588 test_result);
1589 else if (verbose)
1590 printf ("PASSED: testInternalGet (testMhdPollByPoll).\n");
1591 errorCount += test_result;
1592 }
1593 if (MHD_YES == MHD_is_feature_supported (MHD_FEATURE_EPOLL))
1594 {
1595 test_result = testInternalGet (testMhdPollByEpoll);
1596 if (test_result)
1597 fprintf (stderr, "FAILED: testInternalGet (testMhdPollByEpoll) - %u.\n",
1598 test_result);
1599 else if (verbose)
1600 printf ("PASSED: testInternalGet (testMhdPollByEpoll).\n");
1601 errorCount += test_result;
1602 }
1603#else
1604 /* Mute compiler warnings */
1605 (void) testMultithreadedGet;
1606 (void) testMultithreadedPoolGet;
1607#endif /* _MHD_HEAVY_TESTS */
1608 }
1609 if (0 != errorCount)
1610 fprintf (stderr,
1611 "Error (code: %u)\n",
1612 errorCount);
1613 else if (verbose)
1614 printf ("All tests passed.\n");
1615
1616 test_global_cleanup ();
1617
1618 return (errorCount == 0) ? 0 : 1; /* 0 == pass */
1619}