diff options
-rw-r--r-- | src/testcurl/Makefile.am | 10 | ||||
-rw-r--r-- | src/testcurl/test_put_header_fold.c | 1231 |
2 files changed, 1241 insertions, 0 deletions
diff --git a/src/testcurl/Makefile.am b/src/testcurl/Makefile.am index 834c0775..930bedd3 100644 --- a/src/testcurl/Makefile.am +++ b/src/testcurl/Makefile.am | |||
@@ -137,6 +137,9 @@ check_PROGRAMS = \ | |||
137 | test_get_chunked_close_empty_forced \ | 137 | test_get_chunked_close_empty_forced \ |
138 | test_put_chunked \ | 138 | test_put_chunked \ |
139 | test_callback \ | 139 | test_callback \ |
140 | test_get_header_fold \ | ||
141 | test_put_header_fold \ | ||
142 | test_put_large_header_fold \ | ||
140 | $(EMPTY_ITEM) | 143 | $(EMPTY_ITEM) |
141 | 144 | ||
142 | if ENABLE_COOKIE | 145 | if ENABLE_COOKIE |
@@ -629,3 +632,10 @@ test_put_broken_len_SOURCES = \ | |||
629 | test_put_broken_len.c mhd_has_in_name.h mhd_has_param.h | 632 | test_put_broken_len.c mhd_has_in_name.h mhd_has_param.h |
630 | 633 | ||
631 | test_put_broken_len10_SOURCES = $(test_put_broken_len_SOURCES) | 634 | test_put_broken_len10_SOURCES = $(test_put_broken_len_SOURCES) |
635 | |||
636 | test_put_header_fold_SOURCES = \ | ||
637 | test_put_header_fold.c mhd_has_in_name.h mhd_has_param.h | ||
638 | |||
639 | test_put_large_header_fold_SOURCES = $(test_put_header_fold_SOURCES) | ||
640 | |||
641 | test_get_header_fold_SOURCES = $(test_put_header_fold_SOURCES) | ||
diff --git a/src/testcurl/test_put_header_fold.c b/src/testcurl/test_put_header_fold.c new file mode 100644 index 00000000..f01b6762 --- /dev/null +++ b/src/testcurl/test_put_header_fold.c | |||
@@ -0,0 +1,1231 @@ | |||
1 | /* | ||
2 | This file is part of GNU libmicrohttpd | ||
3 | Copyright (C) 2010 Christian Grothoff | ||
4 | Copyright (C) 2016-2022 Evgeny Grin (Karlson2k) | ||
5 | |||
6 | GNU 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 | GNU 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 | /** | ||
23 | * @file testcurl/test_put_header_fold.c | ||
24 | * @brief Testcase for requests with header fold | ||
25 | * @author Karlson2k (Evgeny Grin) | ||
26 | */ | ||
27 | |||
28 | #include "mhd_options.h" | ||
29 | #include "platform.h" | ||
30 | #include <curl/curl.h> | ||
31 | #include <microhttpd.h> | ||
32 | #include <stdlib.h> | ||
33 | #include <string.h> | ||
34 | #include <time.h> | ||
35 | |||
36 | #ifndef _WIN32 | ||
37 | #include <sys/socket.h> | ||
38 | #include <unistd.h> | ||
39 | #endif | ||
40 | |||
41 | #include "internal.h" | ||
42 | #include "mhd_has_param.h" | ||
43 | #include "mhd_has_in_name.h" | ||
44 | |||
45 | /* The next macros are borrowed from memorypool.c | ||
46 | Keep them in sync! */ | ||
47 | |||
48 | /** | ||
49 | * Align to 2x word size (as GNU libc does). | ||
50 | */ | ||
51 | #define ALIGN_SIZE (2 * sizeof(void*)) | ||
52 | /** | ||
53 | * Round up 'n' to a multiple of ALIGN_SIZE. | ||
54 | */ | ||
55 | #define ROUND_TO_ALIGN(n) (((n) + (ALIGN_SIZE - 1)) \ | ||
56 | / (ALIGN_SIZE) *(ALIGN_SIZE)) | ||
57 | #ifndef MHD_ASAN_POISON_ACTIVE | ||
58 | #define _MHD_RED_ZONE_SIZE (0) | ||
59 | #else /* MHD_ASAN_POISON_ACTIVE */ | ||
60 | #define _MHD_RED_ZONE_SIZE (ALIGN_SIZE) | ||
61 | #endif /* MHD_ASAN_POISON_ACTIVE */ | ||
62 | |||
63 | #define ROUND_TO_ALIGN_PLUS_RED_ZONE(n) (ROUND_TO_ALIGN(n) + _MHD_RED_ZONE_SIZE) | ||
64 | |||
65 | /* The previous macros are borrowed from memorypool.c | ||
66 | Keep them in sync! */ | ||
67 | |||
68 | #ifndef MHD_STATICSTR_LEN_ | ||
69 | /** | ||
70 | * Determine length of static string / macro strings at compile time. | ||
71 | */ | ||
72 | #define MHD_STATICSTR_LEN_(macro) (sizeof(macro) / sizeof(char) - 1) | ||
73 | #endif /* ! MHD_STATICSTR_LEN_ */ | ||
74 | |||
75 | #ifndef CURL_VERSION_BITS | ||
76 | #define CURL_VERSION_BITS(x,y,z) ((x) << 16 | (y) << 8 | (z)) | ||
77 | #endif /* ! CURL_VERSION_BITS */ | ||
78 | #ifndef CURL_AT_LEAST_VERSION | ||
79 | #define CURL_AT_LEAST_VERSION(x,y,z) \ | ||
80 | (LIBCURL_VERSION_NUM >= CURL_VERSION_BITS (x, y, z)) | ||
81 | #endif /* ! CURL_AT_LEAST_VERSION */ | ||
82 | |||
83 | #ifndef _MHD_INSTRMACRO | ||
84 | /* Quoted macro parameter */ | ||
85 | #define _MHD_INSTRMACRO(a) #a | ||
86 | #endif /* ! _MHD_INSTRMACRO */ | ||
87 | #ifndef _MHD_STRMACRO | ||
88 | /* Quoted expanded macro parameter */ | ||
89 | #define _MHD_STRMACRO(a) _MHD_INSTRMACRO (a) | ||
90 | #endif /* ! _MHD_STRMACRO */ | ||
91 | |||
92 | #if defined(HAVE___FUNC__) | ||
93 | #define externalErrorExit(ignore) \ | ||
94 | _externalErrorExit_func (NULL, __func__, __LINE__) | ||
95 | #define externalErrorExitDesc(errDesc) \ | ||
96 | _externalErrorExit_func (errDesc, __func__, __LINE__) | ||
97 | #define libcurlErrorExit(ignore) \ | ||
98 | _libcurlErrorExit_func (NULL, __func__, __LINE__) | ||
99 | #define libcurlErrorExitDesc(errDesc) \ | ||
100 | _libcurlErrorExit_func (errDesc, __func__, __LINE__) | ||
101 | #define mhdErrorExit(ignore) \ | ||
102 | _mhdErrorExit_func (NULL, __func__, __LINE__) | ||
103 | #define mhdErrorExitDesc(errDesc) \ | ||
104 | _mhdErrorExit_func (errDesc, __func__, __LINE__) | ||
105 | #define checkCURLE_OK(libcurlcall) \ | ||
106 | _checkCURLE_OK_func ((libcurlcall), _MHD_STRMACRO (libcurlcall), \ | ||
107 | __func__, __LINE__) | ||
108 | #elif defined(HAVE___FUNCTION__) | ||
109 | #define externalErrorExit(ignore) \ | ||
110 | _externalErrorExit_func (NULL, __FUNCTION__, __LINE__) | ||
111 | #define externalErrorExitDesc(errDesc) \ | ||
112 | _externalErrorExit_func (errDesc, __FUNCTION__, __LINE__) | ||
113 | #define libcurlErrorExit(ignore) \ | ||
114 | _libcurlErrorExit_func (NULL, __FUNCTION__, __LINE__) | ||
115 | #define libcurlErrorExitDesc(errDesc) \ | ||
116 | _libcurlErrorExit_func (errDesc, __FUNCTION__, __LINE__) | ||
117 | #define mhdErrorExit(ignore) \ | ||
118 | _mhdErrorExit_func (NULL, __FUNCTION__, __LINE__) | ||
119 | #define mhdErrorExitDesc(errDesc) \ | ||
120 | _mhdErrorExit_func (errDesc, __FUNCTION__, __LINE__) | ||
121 | #define checkCURLE_OK(libcurlcall) \ | ||
122 | _checkCURLE_OK_func ((libcurlcall), _MHD_STRMACRO (libcurlcall), \ | ||
123 | __FUNCTION__, __LINE__) | ||
124 | #else | ||
125 | #define externalErrorExit(ignore) _externalErrorExit_func (NULL, NULL, __LINE__) | ||
126 | #define externalErrorExitDesc(errDesc) \ | ||
127 | _externalErrorExit_func (errDesc, NULL, __LINE__) | ||
128 | #define libcurlErrorExit(ignore) _libcurlErrorExit_func (NULL, NULL, __LINE__) | ||
129 | #define libcurlErrorExitDesc(errDesc) \ | ||
130 | _libcurlErrorExit_func (errDesc, NULL, __LINE__) | ||
131 | #define mhdErrorExit(ignore) _mhdErrorExit_func (NULL, NULL, __LINE__) | ||
132 | #define mhdErrorExitDesc(errDesc) _mhdErrorExit_func (errDesc, NULL, __LINE__) | ||
133 | #define checkCURLE_OK(libcurlcall) \ | ||
134 | _checkCURLE_OK_func ((libcurlcall), _MHD_STRMACRO (libcurlcall), NULL, \ | ||
135 | __LINE__) | ||
136 | #endif | ||
137 | |||
138 | |||
139 | _MHD_NORETURN static void | ||
140 | _externalErrorExit_func (const char *errDesc, const char *funcName, int lineNum) | ||
141 | { | ||
142 | fflush (stdout); | ||
143 | if ((NULL != errDesc) && (0 != errDesc[0])) | ||
144 | fprintf (stderr, "%s", errDesc); | ||
145 | else | ||
146 | fprintf (stderr, "System or external 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 | #ifdef MHD_WINSOCK_SOCKETS | ||
155 | fprintf (stderr, "WSAGetLastError() value: %d\n", (int) WSAGetLastError ()); | ||
156 | #endif /* MHD_WINSOCK_SOCKETS */ | ||
157 | fflush (stderr); | ||
158 | exit (99); | ||
159 | } | ||
160 | |||
161 | |||
162 | static char libcurl_errbuf[CURL_ERROR_SIZE] = ""; | ||
163 | |||
164 | _MHD_NORETURN static void | ||
165 | _libcurlErrorExit_func (const char *errDesc, const char *funcName, int lineNum) | ||
166 | { | ||
167 | fflush (stdout); | ||
168 | if ((NULL != errDesc) && (0 != errDesc[0])) | ||
169 | fprintf (stderr, "%s", errDesc); | ||
170 | else | ||
171 | fprintf (stderr, "CURL library call failed"); | ||
172 | if ((NULL != funcName) && (0 != funcName[0])) | ||
173 | fprintf (stderr, " in %s", funcName); | ||
174 | if (0 < lineNum) | ||
175 | fprintf (stderr, " at line %d", lineNum); | ||
176 | |||
177 | fprintf (stderr, ".\nLast errno value: %d (%s)\n", (int) errno, | ||
178 | strerror (errno)); | ||
179 | #ifdef MHD_WINSOCK_SOCKETS | ||
180 | fprintf (stderr, "WSAGetLastError() value: %d\n", (int) WSAGetLastError ()); | ||
181 | #endif /* MHD_WINSOCK_SOCKETS */ | ||
182 | if (0 != libcurl_errbuf[0]) | ||
183 | fprintf (stderr, "Last libcurl error description: %s\n", libcurl_errbuf); | ||
184 | |||
185 | fflush (stderr); | ||
186 | exit (99); | ||
187 | } | ||
188 | |||
189 | |||
190 | _MHD_NORETURN static void | ||
191 | _mhdErrorExit_func (const char *errDesc, const char *funcName, int lineNum) | ||
192 | { | ||
193 | fflush (stdout); | ||
194 | if ((NULL != errDesc) && (0 != errDesc[0])) | ||
195 | fprintf (stderr, "%s", errDesc); | ||
196 | else | ||
197 | fprintf (stderr, "MHD unexpected error"); | ||
198 | if ((NULL != funcName) && (0 != funcName[0])) | ||
199 | fprintf (stderr, " in %s", funcName); | ||
200 | if (0 < lineNum) | ||
201 | fprintf (stderr, " at line %d", lineNum); | ||
202 | |||
203 | fprintf (stderr, ".\nLast errno value: %d (%s)\n", (int) errno, | ||
204 | strerror (errno)); | ||
205 | #ifdef MHD_WINSOCK_SOCKETS | ||
206 | fprintf (stderr, "WSAGetLastError() value: %d\n", (int) WSAGetLastError ()); | ||
207 | #endif /* MHD_WINSOCK_SOCKETS */ | ||
208 | |||
209 | fflush (stderr); | ||
210 | exit (8); | ||
211 | } | ||
212 | |||
213 | |||
214 | /* Could be increased to facilitate debugging */ | ||
215 | #define TIMEOUTS_VAL 5 | ||
216 | |||
217 | #define TEST_UPLOAD_DATA_SIZE 2048U | ||
218 | |||
219 | #define EXPECTED_URI_BASE_PATH "/" | ||
220 | |||
221 | #define URL_SCHEME "http:/" "/" | ||
222 | |||
223 | #define URL_HOST "127.0.0.1" | ||
224 | |||
225 | #define URL_SCHEME_HOST_PATH URL_SCHEME URL_HOST EXPECTED_URI_BASE_PATH | ||
226 | |||
227 | #define RP_HEADER1_NAME "First" | ||
228 | #define RP_HEADER1_VALUE "1st" | ||
229 | #define RP_HEADER1 RP_HEADER1_NAME ": " RP_HEADER1_VALUE | ||
230 | #define RP_HEADER1_CRLF RP_HEADER1 "\r\n" | ||
231 | #define RP_HEADER2_NAME "Normal" | ||
232 | #define RP_HEADER2_VALUE "it's fine" | ||
233 | #define RP_HEADER2 RP_HEADER2_NAME ": " RP_HEADER2_VALUE | ||
234 | #define RP_HEADER2_CRLF RP_HEADER2 "\r\n" | ||
235 | |||
236 | #define RQ_HEADER1_NAME RP_HEADER1_NAME | ||
237 | #define RQ_HEADER1_VALUE RP_HEADER1_VALUE | ||
238 | #define RQ_HEADER1 RQ_HEADER1_NAME ": " RQ_HEADER1_VALUE | ||
239 | #define RQ_HEADER2_NAME "Folded" | ||
240 | #define RQ_HEADER2_VALUE_S "start" | ||
241 | #define RQ_HEADER2_VALUE_E "end" | ||
242 | #define RQ_HEADER2_VALUE RQ_HEADER2_VALUE_S "\r\n \r\n " RQ_HEADER2_VALUE_E | ||
243 | #define RQ_HEADER2 RQ_HEADER2_NAME ": " RQ_HEADER2_VALUE | ||
244 | #define RQ_HEADER3_NAME RP_HEADER2_NAME | ||
245 | #define RQ_HEADER3_VALUE RP_HEADER2_VALUE | ||
246 | #define RQ_HEADER3 RQ_HEADER3_NAME ": " RQ_HEADER3_VALUE | ||
247 | |||
248 | /** | ||
249 | * The number of request headers: 3 custom headers + 2 automatic headers | ||
250 | */ | ||
251 | #define RQ_NUM_HEADERS (3 + 2) | ||
252 | /** | ||
253 | * The extra size in the memory pool for pointers to the headers | ||
254 | */ | ||
255 | #define HEADERS_POINTERS_SIZE \ | ||
256 | RQ_NUM_HEADERS * \ | ||
257 | ROUND_TO_ALIGN_PLUS_RED_ZONE(sizeof(struct MHD_HTTP_Req_Header)) | ||
258 | |||
259 | #define PAGE \ | ||
260 | "<html><head><title>libmicrohttpd demo page</title></head>" \ | ||
261 | "<body>Success!</body></html>" | ||
262 | |||
263 | /* Global parameters */ | ||
264 | static int verbose; | ||
265 | static int oneone; /**< If false use HTTP/1.0 for requests*/ | ||
266 | static int use_get; | ||
267 | static int use_put; | ||
268 | static int use_put_large; | ||
269 | |||
270 | /* Static data */ | ||
271 | static struct curl_slist *libcurl_headers = NULL; | ||
272 | |||
273 | static char *put_data = NULL; | ||
274 | |||
275 | /** | ||
276 | * Initialise headers for libcurl | ||
277 | * | ||
278 | * @return non-zero if succeed, | ||
279 | * zero if failed | ||
280 | */ | ||
281 | static void | ||
282 | libcurl_headers_init (void) | ||
283 | { | ||
284 | libcurl_headers = curl_slist_append (NULL, RQ_HEADER1); | ||
285 | if (NULL == libcurl_headers) | ||
286 | libcurlErrorExitDesc ("curl_slist_append() failed"); | ||
287 | |||
288 | libcurl_headers = curl_slist_append (libcurl_headers, RQ_HEADER2); | ||
289 | if (NULL == libcurl_headers) | ||
290 | libcurlErrorExitDesc ("curl_slist_append() failed"); | ||
291 | |||
292 | libcurl_headers = curl_slist_append (libcurl_headers, RQ_HEADER3); | ||
293 | if (NULL == libcurl_headers) | ||
294 | libcurlErrorExitDesc ("curl_slist_append() failed"); | ||
295 | } | ||
296 | |||
297 | |||
298 | static void | ||
299 | init_put_data (void) | ||
300 | { | ||
301 | size_t i; | ||
302 | put_data = malloc (TEST_UPLOAD_DATA_SIZE + 1); | ||
303 | if (NULL == put_data) | ||
304 | externalErrorExit (); | ||
305 | |||
306 | for (i = 0; i < (TEST_UPLOAD_DATA_SIZE - 1); ++i) | ||
307 | { | ||
308 | if (0 == (i % 7)) | ||
309 | put_data[i] = ' '; | ||
310 | else if (0 == (i % 47)) | ||
311 | put_data[i] = '\n'; | ||
312 | else if (0 == (i % 11)) | ||
313 | put_data[i] = (char) ('A' + i % ('Z' - 'A' + 1)); | ||
314 | else | ||
315 | put_data[i] = (char) ('a' + i % ('z' - 'a' + 1)); | ||
316 | } | ||
317 | put_data[TEST_UPLOAD_DATA_SIZE - 1] = '\n'; | ||
318 | put_data[TEST_UPLOAD_DATA_SIZE] = 0; | ||
319 | } | ||
320 | |||
321 | |||
322 | static void | ||
323 | test_global_init (void) | ||
324 | { | ||
325 | libcurl_errbuf[0] = 0; | ||
326 | |||
327 | if (0 != curl_global_init (CURL_GLOBAL_WIN32)) | ||
328 | externalErrorExit (); | ||
329 | |||
330 | init_put_data (); | ||
331 | libcurl_headers_init (); | ||
332 | } | ||
333 | |||
334 | |||
335 | static void | ||
336 | test_global_cleanup (void) | ||
337 | { | ||
338 | curl_slist_free_all (libcurl_headers); | ||
339 | curl_global_cleanup (); | ||
340 | if (NULL != put_data) | ||
341 | free (put_data); | ||
342 | put_data = NULL; | ||
343 | } | ||
344 | |||
345 | |||
346 | struct headers_check_result | ||
347 | { | ||
348 | unsigned int expected_size; | ||
349 | int header1_found; | ||
350 | int header2_found; | ||
351 | unsigned int size_found; | ||
352 | unsigned int size_broken_found; | ||
353 | }; | ||
354 | |||
355 | static size_t | ||
356 | lcurl_hdr_callback (char *buffer, size_t size, size_t nitems, | ||
357 | void *userdata) | ||
358 | { | ||
359 | const size_t data_size = size * nitems; | ||
360 | struct headers_check_result *check_res = | ||
361 | (struct headers_check_result *) userdata; | ||
362 | |||
363 | if ((MHD_STATICSTR_LEN_ (RP_HEADER1_CRLF) == data_size) && | ||
364 | (0 == memcmp (RP_HEADER1_CRLF, buffer, data_size))) | ||
365 | check_res->header1_found++; | ||
366 | else if ((MHD_STATICSTR_LEN_ (RP_HEADER2_CRLF) == data_size) && | ||
367 | (0 == memcmp (RP_HEADER2_CRLF, buffer, data_size))) | ||
368 | check_res->header2_found++; | ||
369 | else if ((MHD_STATICSTR_LEN_ (MHD_HTTP_HEADER_CONTENT_LENGTH ": ") | ||
370 | < data_size) && | ||
371 | (0 == | ||
372 | memcmp (MHD_HTTP_HEADER_CONTENT_LENGTH ": ", buffer, | ||
373 | MHD_STATICSTR_LEN_ (MHD_HTTP_HEADER_CONTENT_LENGTH ": ")))) | ||
374 | { | ||
375 | char cmpbuf[256]; | ||
376 | int res; | ||
377 | const unsigned int numbers_pos = | ||
378 | MHD_STATICSTR_LEN_ (MHD_HTTP_HEADER_CONTENT_LENGTH ": "); | ||
379 | res = snprintf (cmpbuf, sizeof(cmpbuf), "%u", check_res->expected_size); | ||
380 | if ((res <= 0) || (res > ((int) (sizeof(cmpbuf) - 1)))) | ||
381 | externalErrorExit (); | ||
382 | if (data_size - numbers_pos <= 2) | ||
383 | { | ||
384 | fprintf (stderr, "Broken Content-Length.\n"); | ||
385 | check_res->size_broken_found++; | ||
386 | } | ||
387 | else if ((((size_t) res + 2) != data_size - numbers_pos) || | ||
388 | (0 != memcmp (buffer + numbers_pos, cmpbuf, (size_t) res))) | ||
389 | { | ||
390 | fprintf (stderr, "Wrong Content-Length. " | ||
391 | "Expected: %u. " | ||
392 | "Received: %.*s.\n", | ||
393 | check_res->expected_size, | ||
394 | (int) (data_size - numbers_pos - 2), | ||
395 | buffer + numbers_pos); | ||
396 | check_res->size_broken_found++; | ||
397 | } | ||
398 | else if (0 != memcmp ("\r\n", buffer + data_size - 2, 2)) | ||
399 | { | ||
400 | fprintf (stderr, "The Content-Length header is not " | ||
401 | "terminated by CRLF.\n"); | ||
402 | check_res->size_broken_found++; | ||
403 | } | ||
404 | else | ||
405 | check_res->size_found++; | ||
406 | } | ||
407 | |||
408 | return data_size; | ||
409 | } | ||
410 | |||
411 | |||
412 | struct CBC | ||
413 | { | ||
414 | /* Upload members */ | ||
415 | size_t up_pos; | ||
416 | size_t up_size; | ||
417 | /* Download members */ | ||
418 | char *dn_buf; | ||
419 | size_t dn_pos; | ||
420 | size_t dn_buf_size; | ||
421 | }; | ||
422 | |||
423 | |||
424 | static size_t | ||
425 | copyBuffer (void *ptr, | ||
426 | size_t size, | ||
427 | size_t nmemb, | ||
428 | void *ctx) | ||
429 | { | ||
430 | struct CBC *cbc = ctx; | ||
431 | |||
432 | if (cbc->dn_pos + size * nmemb > cbc->dn_buf_size) | ||
433 | return 0; /* overflow */ | ||
434 | memcpy (&cbc->dn_buf[cbc->dn_pos], ptr, size * nmemb); | ||
435 | cbc->dn_pos += size * nmemb; | ||
436 | return size * nmemb; | ||
437 | } | ||
438 | |||
439 | |||
440 | static size_t | ||
441 | libcurlUploadDataCB (void *stream, size_t item_size, size_t nitems, void *ctx) | ||
442 | { | ||
443 | size_t to_fill; | ||
444 | struct CBC *cbc = ctx; | ||
445 | |||
446 | to_fill = cbc->up_size - cbc->up_pos; | ||
447 | if (to_fill > item_size * nitems) | ||
448 | to_fill = item_size * nitems; | ||
449 | |||
450 | /* Avoid libcurl magic numbers */ | ||
451 | #ifdef CURL_READFUNC_PAUSE | ||
452 | if (CURL_READFUNC_ABORT == to_fill) | ||
453 | to_fill -= 2; | ||
454 | #endif /* CURL_READFUNC_PAUSE */ | ||
455 | #ifdef CURL_READFUNC_ABORT | ||
456 | if (CURL_READFUNC_ABORT == to_fill) | ||
457 | --to_fill; | ||
458 | #endif /* CURL_READFUNC_ABORT */ | ||
459 | |||
460 | memcpy (stream, put_data + cbc->up_pos, to_fill); | ||
461 | cbc->up_pos += to_fill; | ||
462 | return to_fill; | ||
463 | } | ||
464 | |||
465 | |||
466 | static int | ||
467 | libcurl_debug_cb (CURL *handle, | ||
468 | curl_infotype type, | ||
469 | char *data, | ||
470 | size_t size, | ||
471 | void *userptr) | ||
472 | { | ||
473 | static const char excess_mark[] = "Excess found"; | ||
474 | static const size_t excess_mark_len = MHD_STATICSTR_LEN_ (excess_mark); | ||
475 | |||
476 | (void) handle; | ||
477 | (void) userptr; | ||
478 | |||
479 | #ifdef _DEBUG | ||
480 | switch (type) | ||
481 | { | ||
482 | case CURLINFO_TEXT: | ||
483 | fprintf (stderr, "* %.*s", (int) size, data); | ||
484 | break; | ||
485 | case CURLINFO_HEADER_IN: | ||
486 | fprintf (stderr, "< %.*s", (int) size, data); | ||
487 | break; | ||
488 | case CURLINFO_HEADER_OUT: | ||
489 | fprintf (stderr, "> %.*s", (int) size, data); | ||
490 | break; | ||
491 | case CURLINFO_DATA_IN: | ||
492 | #if 0 | ||
493 | fprintf (stderr, "<| %.*s\n", (int) size, data); | ||
494 | #endif | ||
495 | break; | ||
496 | case CURLINFO_DATA_OUT: | ||
497 | case CURLINFO_SSL_DATA_IN: | ||
498 | case CURLINFO_SSL_DATA_OUT: | ||
499 | case CURLINFO_END: | ||
500 | default: | ||
501 | break; | ||
502 | } | ||
503 | #endif /* _DEBUG */ | ||
504 | if (CURLINFO_TEXT == type) | ||
505 | { | ||
506 | if ((size >= excess_mark_len) && | ||
507 | (0 == memcmp (data, excess_mark, excess_mark_len))) | ||
508 | mhdErrorExitDesc ("Extra data has been detected in MHD reply"); | ||
509 | } | ||
510 | return 0; | ||
511 | } | ||
512 | |||
513 | |||
514 | static CURL * | ||
515 | setupCURL (void *cbc, uint16_t port, | ||
516 | struct headers_check_result *hdr_chk_result) | ||
517 | { | ||
518 | CURL *c; | ||
519 | |||
520 | c = curl_easy_init (); | ||
521 | if (NULL == c) | ||
522 | libcurlErrorExitDesc ("curl_easy_init() failed"); | ||
523 | |||
524 | if ((CURLE_OK != curl_easy_setopt (c, CURLOPT_NOSIGNAL, 1L)) || | ||
525 | (CURLE_OK != curl_easy_setopt (c, CURLOPT_WRITEFUNCTION, | ||
526 | ©Buffer)) || | ||
527 | (CURLE_OK != curl_easy_setopt (c, CURLOPT_WRITEDATA, cbc)) || | ||
528 | (CURLE_OK != curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT, | ||
529 | ((long) TIMEOUTS_VAL))) || | ||
530 | (CURLE_OK != curl_easy_setopt (c, CURLOPT_HTTP_VERSION, | ||
531 | (oneone) ? | ||
532 | CURL_HTTP_VERSION_1_1 : | ||
533 | CURL_HTTP_VERSION_1_0)) || | ||
534 | (CURLE_OK != curl_easy_setopt (c, CURLOPT_TIMEOUT, | ||
535 | ((long) TIMEOUTS_VAL))) || | ||
536 | (CURLE_OK != curl_easy_setopt (c, CURLOPT_HEADERFUNCTION, | ||
537 | lcurl_hdr_callback)) || | ||
538 | (CURLE_OK != curl_easy_setopt (c, CURLOPT_HEADERDATA, | ||
539 | hdr_chk_result)) || | ||
540 | (CURLE_OK != curl_easy_setopt (c, CURLOPT_ERRORBUFFER, | ||
541 | libcurl_errbuf)) || | ||
542 | (CURLE_OK != curl_easy_setopt (c, CURLOPT_FAILONERROR, 0L)) || | ||
543 | #ifdef _DEBUG | ||
544 | (CURLE_OK != curl_easy_setopt (c, CURLOPT_VERBOSE, 1L)) || | ||
545 | #endif /* _DEBUG */ | ||
546 | (CURLE_OK != curl_easy_setopt (c, CURLOPT_DEBUGFUNCTION, | ||
547 | &libcurl_debug_cb)) || | ||
548 | #if CURL_AT_LEAST_VERSION (7, 19, 4) | ||
549 | (CURLE_OK != curl_easy_setopt (c, CURLOPT_PROTOCOLS, CURLPROTO_HTTP)) || | ||
550 | #endif /* CURL_AT_LEAST_VERSION (7, 19, 4) */ | ||
551 | #if CURL_AT_LEAST_VERSION (7, 45, 0) | ||
552 | (CURLE_OK != curl_easy_setopt (c, CURLOPT_DEFAULT_PROTOCOL, "http")) || | ||
553 | #endif /* CURL_AT_LEAST_VERSION (7, 45, 0) */ | ||
554 | (CURLE_OK != curl_easy_setopt (c, CURLOPT_URL, | ||
555 | URL_SCHEME_HOST_PATH)) || | ||
556 | (CURLE_OK != curl_easy_setopt (c, CURLOPT_PORT, ((long) port)))) | ||
557 | libcurlErrorExitDesc ("curl_easy_setopt() failed"); | ||
558 | |||
559 | if (CURLE_OK != curl_easy_setopt (c, CURLOPT_HTTPHEADER, libcurl_headers)) | ||
560 | libcurlErrorExitDesc ("Failed to set request headers"); | ||
561 | |||
562 | if (use_put) | ||
563 | { | ||
564 | if ((CURLE_OK != curl_easy_setopt (c, CURLOPT_UPLOAD, 1L)) || | ||
565 | (CURLE_OK != curl_easy_setopt (c, CURLOPT_READFUNCTION, | ||
566 | libcurlUploadDataCB)) || | ||
567 | (CURLE_OK != curl_easy_setopt (c, CURLOPT_READDATA, | ||
568 | cbc))) | ||
569 | libcurlErrorExitDesc ("Failed to configure the PUT upload"); | ||
570 | } | ||
571 | |||
572 | return c; | ||
573 | } | ||
574 | |||
575 | |||
576 | struct ahc_cls_type | ||
577 | { | ||
578 | const char *rq_method; | ||
579 | const char *rq_url; | ||
580 | |||
581 | unsigned int num_req; | ||
582 | /* Position in the upload data, not compatible with parallel requests */ | ||
583 | size_t up_pos; | ||
584 | size_t expected_upload_size; | ||
585 | unsigned int req_check_error; | ||
586 | }; | ||
587 | |||
588 | |||
589 | static enum MHD_Result | ||
590 | ahcCheck (void *cls, | ||
591 | struct MHD_Connection *connection, | ||
592 | const char *url, | ||
593 | const char *method, | ||
594 | const char *version, | ||
595 | const char *upload_data, size_t *upload_data_size, | ||
596 | void **req_cls) | ||
597 | { | ||
598 | static int marker; | ||
599 | struct MHD_Response *response; | ||
600 | enum MHD_Result ret; | ||
601 | struct ahc_cls_type *const param = (struct ahc_cls_type *) cls; | ||
602 | |||
603 | if (NULL == param) | ||
604 | mhdErrorExitDesc ("cls parameter is NULL"); | ||
605 | |||
606 | if (oneone) | ||
607 | { | ||
608 | if (0 != strcmp (version, MHD_HTTP_VERSION_1_1)) | ||
609 | mhdErrorExitDesc ("Unexpected HTTP version"); | ||
610 | } | ||
611 | else | ||
612 | { | ||
613 | if (0 != strcmp (version, MHD_HTTP_VERSION_1_0)) | ||
614 | mhdErrorExitDesc ("Unexpected HTTP version"); | ||
615 | } | ||
616 | |||
617 | if (0 != strcmp (url, param->rq_url)) | ||
618 | mhdErrorExitDesc ("Unexpected URI"); | ||
619 | |||
620 | if (0 != strcmp (param->rq_method, method)) | ||
621 | mhdErrorExitDesc ("Unexpected request method"); | ||
622 | |||
623 | if (NULL == upload_data_size) | ||
624 | mhdErrorExitDesc ("'upload_data_size' pointer is NULL"); | ||
625 | |||
626 | if (NULL != upload_data) | ||
627 | { | ||
628 | size_t report_processed_size; | ||
629 | if (0 == *upload_data_size) | ||
630 | mhdErrorExitDesc ("'*upload_data_size' value is zero"); | ||
631 | report_processed_size = *upload_data_size; | ||
632 | /* The next checks are not compatible with parallel requests */ | ||
633 | if (*upload_data_size > param->expected_upload_size - param->up_pos) | ||
634 | { | ||
635 | fprintf (stderr, "Unexpected *upload_data_size value: %lu. " | ||
636 | "Already processed data size: %lu. " | ||
637 | "Total expected upload size: %lu. " | ||
638 | "Expected unprocessed upload size: %lu. " | ||
639 | "The upload data cannot be checked.\n", | ||
640 | (unsigned long) *upload_data_size, | ||
641 | (unsigned long) param->up_pos, | ||
642 | (unsigned long) param->expected_upload_size, | ||
643 | (unsigned long) (param->expected_upload_size - param->up_pos)); | ||
644 | param->req_check_error++; | ||
645 | } | ||
646 | else | ||
647 | { | ||
648 | if (0 != memcmp (upload_data, put_data + param->up_pos, | ||
649 | *upload_data_size)) | ||
650 | { | ||
651 | fprintf (stderr, "Wrong upload data.\n" | ||
652 | "Expected: '%.*s'\n" | ||
653 | "Received: '%.*s'.\n", | ||
654 | (int) *upload_data_size, upload_data, | ||
655 | (int) *upload_data_size, put_data + param->up_pos); | ||
656 | param->req_check_error++; | ||
657 | } | ||
658 | if (use_put_large && | ||
659 | (report_processed_size > param->expected_upload_size / 10)) | ||
660 | report_processed_size = param->expected_upload_size / 10; | ||
661 | |||
662 | param->up_pos += report_processed_size; | ||
663 | } | ||
664 | *upload_data_size -= report_processed_size; | ||
665 | return MHD_YES; | ||
666 | } | ||
667 | else | ||
668 | { | ||
669 | if (0 != *upload_data_size) | ||
670 | mhdErrorExitDesc ("'*upload_data_size' value is not zero"); | ||
671 | } | ||
672 | |||
673 | if (1) | ||
674 | { | ||
675 | /* Check headers */ | ||
676 | const char *value; | ||
677 | size_t value_len; | ||
678 | unsigned int header_check_error; | ||
679 | |||
680 | header_check_error = 0; | ||
681 | |||
682 | value = MHD_lookup_connection_value (connection, MHD_HEADER_KIND, | ||
683 | RQ_HEADER1_NAME); | ||
684 | if (NULL == value) | ||
685 | { | ||
686 | fprintf (stderr, "Request header '" RQ_HEADER1_NAME "' not found.\n"); | ||
687 | header_check_error++; | ||
688 | } | ||
689 | else | ||
690 | { | ||
691 | if (0 != strcmp (value, RQ_HEADER1_VALUE)) | ||
692 | { | ||
693 | fprintf (stderr, "Wrong header '" RQ_HEADER1_NAME "'value. " | ||
694 | "Expected: '%s'. Received: '%s'.\n", | ||
695 | RQ_HEADER1_VALUE, value); | ||
696 | header_check_error++; | ||
697 | } | ||
698 | } | ||
699 | |||
700 | if (MHD_YES != | ||
701 | MHD_lookup_connection_value_n (connection, MHD_HEADER_KIND, | ||
702 | RQ_HEADER2_NAME, | ||
703 | MHD_STATICSTR_LEN_ (RQ_HEADER2_NAME), | ||
704 | &value, &value_len)) | ||
705 | { | ||
706 | fprintf (stderr, "Request header '" RQ_HEADER2_NAME "' not found.\n"); | ||
707 | header_check_error++; | ||
708 | } | ||
709 | else | ||
710 | { | ||
711 | if (NULL == value) | ||
712 | mhdErrorExitDesc ("The 'value' pointer is NULL"); | ||
713 | if (strlen (value) != value_len) | ||
714 | mhdErrorExitDesc ("The 'value' length does not match strlen(value)"); | ||
715 | |||
716 | if (value_len < MHD_STATICSTR_LEN_ (RQ_HEADER2_VALUE_S) | ||
717 | + MHD_STATICSTR_LEN_ (RQ_HEADER2_VALUE_E)) | ||
718 | { | ||
719 | fprintf (stderr, "The value_len is too short. The value: '%s'.\n", | ||
720 | value); | ||
721 | header_check_error++; | ||
722 | } | ||
723 | if (0 != memcmp (value, RQ_HEADER2_VALUE_S, | ||
724 | MHD_STATICSTR_LEN_ (RQ_HEADER2_VALUE_S))) | ||
725 | { | ||
726 | fprintf (stderr, "The 'value' does not start with '" | ||
727 | RQ_HEADER2_VALUE_S "'. The 'value' is '%s'. ", | ||
728 | value); | ||
729 | header_check_error++; | ||
730 | } | ||
731 | if (0 != memcmp (value | ||
732 | + value_len - MHD_STATICSTR_LEN_ (RQ_HEADER2_VALUE_E), | ||
733 | RQ_HEADER2_VALUE_E, | ||
734 | MHD_STATICSTR_LEN_ (RQ_HEADER2_VALUE_E))) | ||
735 | { | ||
736 | fprintf (stderr, "The 'value' does not end with '" | ||
737 | RQ_HEADER2_VALUE_E "'. The 'value' is '%s'. ", | ||
738 | value); | ||
739 | header_check_error++; | ||
740 | } | ||
741 | } | ||
742 | |||
743 | value = MHD_lookup_connection_value (connection, MHD_HEADER_KIND, | ||
744 | RQ_HEADER3_NAME); | ||
745 | if (NULL == value) | ||
746 | { | ||
747 | fprintf (stderr, "Request header '" RQ_HEADER3_NAME "' not found.\n"); | ||
748 | header_check_error++; | ||
749 | } | ||
750 | else | ||
751 | { | ||
752 | if (0 != strcmp (value, RQ_HEADER3_VALUE)) | ||
753 | { | ||
754 | fprintf (stderr, "Wrong header '" RQ_HEADER3_NAME "'value. " | ||
755 | "Expected: '%s'. Received: '%s'.\n", | ||
756 | RQ_HEADER3_VALUE, value); | ||
757 | header_check_error++; | ||
758 | } | ||
759 | } | ||
760 | param->req_check_error += header_check_error; | ||
761 | } | ||
762 | |||
763 | if (&marker != *req_cls) | ||
764 | { | ||
765 | *req_cls = ▮ | ||
766 | if (param->num_req) | ||
767 | mhdErrorExitDesc ("Got unexpected second request"); | ||
768 | param->num_req++; | ||
769 | return MHD_YES; | ||
770 | } | ||
771 | *req_cls = NULL; | ||
772 | |||
773 | if (0 != strcmp (url, EXPECTED_URI_BASE_PATH)) | ||
774 | { | ||
775 | fprintf (stderr, "Unexpected URI: '%s'. ", url); | ||
776 | mhdErrorExitDesc ("Unexpected URI found"); | ||
777 | } | ||
778 | |||
779 | response = | ||
780 | MHD_create_response_from_buffer_static (MHD_STATICSTR_LEN_ (PAGE), | ||
781 | PAGE); | ||
782 | if (NULL == response) | ||
783 | mhdErrorExitDesc ("Failed to create response"); | ||
784 | |||
785 | if (MHD_YES != MHD_add_response_header (response, | ||
786 | RP_HEADER1_NAME, | ||
787 | RP_HEADER1_VALUE)) | ||
788 | mhdErrorExitDesc ("Cannot add header1"); | ||
789 | if (MHD_YES != MHD_add_response_header (response, | ||
790 | RP_HEADER2_NAME, | ||
791 | RP_HEADER2_VALUE)) | ||
792 | mhdErrorExitDesc ("Cannot add header2"); | ||
793 | |||
794 | ret = MHD_queue_response (connection, | ||
795 | MHD_HTTP_OK, | ||
796 | response); | ||
797 | MHD_destroy_response (response); | ||
798 | if (MHD_YES != ret) | ||
799 | mhdErrorExitDesc ("Failed to queue response"); | ||
800 | |||
801 | return ret; | ||
802 | } | ||
803 | |||
804 | |||
805 | static CURLcode | ||
806 | performQueryExternal (struct MHD_Daemon *d, CURL *c, CURLM **multi_reuse) | ||
807 | { | ||
808 | CURLM *multi; | ||
809 | time_t start; | ||
810 | struct timeval tv; | ||
811 | CURLcode ret; | ||
812 | int libcurl_finished; | ||
813 | |||
814 | ret = CURLE_FAILED_INIT; /* will be replaced with real result */ | ||
815 | if (NULL != *multi_reuse) | ||
816 | multi = *multi_reuse; | ||
817 | else | ||
818 | { | ||
819 | multi = curl_multi_init (); | ||
820 | if (multi == NULL) | ||
821 | libcurlErrorExitDesc ("curl_multi_init() failed"); | ||
822 | *multi_reuse = multi; | ||
823 | } | ||
824 | if (CURLM_OK != curl_multi_add_handle (multi, c)) | ||
825 | libcurlErrorExitDesc ("curl_multi_add_handle() failed"); | ||
826 | libcurl_finished = 0; | ||
827 | |||
828 | start = time (NULL); | ||
829 | while (time (NULL) - start <= TIMEOUTS_VAL) | ||
830 | { | ||
831 | fd_set rs; | ||
832 | fd_set ws; | ||
833 | fd_set es; | ||
834 | MHD_socket maxMhdSk; | ||
835 | int maxCurlSk; | ||
836 | |||
837 | maxMhdSk = MHD_INVALID_SOCKET; | ||
838 | maxCurlSk = -1; | ||
839 | FD_ZERO (&rs); | ||
840 | FD_ZERO (&ws); | ||
841 | FD_ZERO (&es); | ||
842 | if (! libcurl_finished) | ||
843 | { | ||
844 | int running; | ||
845 | curl_multi_perform (multi, &running); | ||
846 | if (0 == running) | ||
847 | { | ||
848 | struct CURLMsg *msg; | ||
849 | int msgLeft; | ||
850 | int totalMsgs = 0; | ||
851 | do | ||
852 | { | ||
853 | msg = curl_multi_info_read (multi, &msgLeft); | ||
854 | if (NULL == msg) | ||
855 | libcurlErrorExitDesc ("curl_multi_info_read() failed"); | ||
856 | totalMsgs++; | ||
857 | if (CURLMSG_DONE == msg->msg) | ||
858 | ret = msg->data.result; | ||
859 | } while (msgLeft > 0); | ||
860 | if (1 != totalMsgs) | ||
861 | { | ||
862 | fprintf (stderr, | ||
863 | "curl_multi_info_read returned wrong " | ||
864 | "number of results (%d).\n", | ||
865 | totalMsgs); | ||
866 | externalErrorExit (); | ||
867 | } | ||
868 | curl_multi_remove_handle (multi, c); | ||
869 | libcurl_finished = ! 0; | ||
870 | } | ||
871 | else | ||
872 | { | ||
873 | if (CURLM_OK != curl_multi_fdset (multi, &rs, &ws, &es, &maxCurlSk)) | ||
874 | libcurlErrorExitDesc ("curl_multi_fdset() failed"); | ||
875 | } | ||
876 | } | ||
877 | if (libcurl_finished) | ||
878 | { /* libcurl has finished, check whether MHD still needs to perform cleanup */ | ||
879 | if (0 != MHD_get_timeout64s (d)) | ||
880 | break; /* MHD finished as well */ | ||
881 | } | ||
882 | if (MHD_YES != MHD_get_fdset (d, &rs, &ws, &es, &maxMhdSk)) | ||
883 | mhdErrorExitDesc ("MHD_get_fdset() failed"); | ||
884 | tv.tv_sec = 0; | ||
885 | tv.tv_usec = 200000; | ||
886 | if (0 == MHD_get_timeout64s (d)) | ||
887 | tv.tv_usec = 0; | ||
888 | else | ||
889 | { | ||
890 | long curl_to = -1; | ||
891 | curl_multi_timeout (multi, &curl_to); | ||
892 | if (0 == curl_to) | ||
893 | tv.tv_usec = 0; | ||
894 | } | ||
895 | #ifdef MHD_POSIX_SOCKETS | ||
896 | if (maxMhdSk > maxCurlSk) | ||
897 | maxCurlSk = maxMhdSk; | ||
898 | #endif /* MHD_POSIX_SOCKETS */ | ||
899 | if (-1 == select (maxCurlSk + 1, &rs, &ws, &es, &tv)) | ||
900 | { | ||
901 | #ifdef MHD_POSIX_SOCKETS | ||
902 | if (EINTR != errno) | ||
903 | externalErrorExitDesc ("Unexpected select() error"); | ||
904 | #else | ||
905 | if ((WSAEINVAL != WSAGetLastError ()) || | ||
906 | (0 != rs.fd_count) || (0 != ws.fd_count) || (0 != es.fd_count) ) | ||
907 | externalErrorExitDesc ("Unexpected select() error"); | ||
908 | Sleep ((unsigned long) tv.tv_usec / 1000); | ||
909 | #endif | ||
910 | } | ||
911 | if (MHD_YES != MHD_run_from_select (d, &rs, &ws, &es)) | ||
912 | mhdErrorExitDesc ("MHD_run_from_select() failed"); | ||
913 | } | ||
914 | |||
915 | return ret; | ||
916 | } | ||
917 | |||
918 | |||
919 | /** | ||
920 | * Check request result | ||
921 | * @param curl_code the CURL easy return code | ||
922 | * @param pcbc the pointer struct CBC | ||
923 | * @return non-zero if success, zero if failed | ||
924 | */ | ||
925 | static unsigned int | ||
926 | check_result (CURLcode curl_code, CURL *c, long expected_code, | ||
927 | struct CBC *pcbc, struct headers_check_result *hdr_res, | ||
928 | struct ahc_cls_type *ahc_cls) | ||
929 | { | ||
930 | long code; | ||
931 | unsigned int ret; | ||
932 | |||
933 | fflush (stderr); | ||
934 | fflush (stdout); | ||
935 | if (CURLE_OK != curl_code) | ||
936 | { | ||
937 | fflush (stdout); | ||
938 | if (0 != libcurl_errbuf[0]) | ||
939 | fprintf (stderr, "Request failed. " | ||
940 | "libcurl error: '%s'.\n" | ||
941 | "libcurl error description: '%s'.\n", | ||
942 | curl_easy_strerror (curl_code), | ||
943 | libcurl_errbuf); | ||
944 | else | ||
945 | fprintf (stderr, "Request failed. " | ||
946 | "libcurl error: '%s'.\n", | ||
947 | curl_easy_strerror (curl_code)); | ||
948 | return 0; | ||
949 | } | ||
950 | |||
951 | if (CURLE_OK != curl_easy_getinfo (c, CURLINFO_RESPONSE_CODE, &code)) | ||
952 | libcurlErrorExit (); | ||
953 | |||
954 | if (expected_code != code) | ||
955 | { | ||
956 | fprintf (stderr, "The response has wrong HTTP code: %ld\tExpected: %ld.\n", | ||
957 | code, expected_code); | ||
958 | return 0; | ||
959 | } | ||
960 | else if (verbose) | ||
961 | printf ("The response has expected HTTP code: %ld\n", expected_code); | ||
962 | |||
963 | ret = 1; | ||
964 | |||
965 | if (ahc_cls->req_check_error) | ||
966 | { | ||
967 | fprintf (stderr, "One or more errors have been detected by access " | ||
968 | "handler callback.\n"); | ||
969 | ret = 0; | ||
970 | } | ||
971 | if (ahc_cls->expected_upload_size != ahc_cls->up_pos) | ||
972 | { | ||
973 | fprintf (stderr, "Upload size does not match expected. " | ||
974 | "Expected: %lu. " | ||
975 | "Received: %lu.\n", | ||
976 | (unsigned long) ahc_cls->expected_upload_size, | ||
977 | (unsigned long) ahc_cls->up_pos); | ||
978 | ret = 0; | ||
979 | } | ||
980 | |||
981 | if (1 != hdr_res->header1_found) | ||
982 | { | ||
983 | if (0 == hdr_res->header1_found) | ||
984 | fprintf (stderr, "Response header1 was not found.\n"); | ||
985 | else | ||
986 | fprintf (stderr, "Response header1 was found %d times " | ||
987 | "instead of one time only.\n", hdr_res->header1_found); | ||
988 | ret = 0; | ||
989 | } | ||
990 | else if (verbose) | ||
991 | printf ("Header1 is present in the response.\n"); | ||
992 | if (1 != hdr_res->header2_found) | ||
993 | { | ||
994 | if (0 == hdr_res->header2_found) | ||
995 | fprintf (stderr, "Response header2 was not found.\n"); | ||
996 | else | ||
997 | fprintf (stderr, "Response header2 was found %d times " | ||
998 | "instead of one time only.\n", hdr_res->header2_found); | ||
999 | ret = 0; | ||
1000 | } | ||
1001 | else if (verbose) | ||
1002 | printf ("Header2 is present in the response.\n"); | ||
1003 | if (1 != hdr_res->size_found) | ||
1004 | { | ||
1005 | if (0 == hdr_res->size_found) | ||
1006 | fprintf (stderr, "Correct response 'Content-Length' header " | ||
1007 | "was not found.\n"); | ||
1008 | else | ||
1009 | fprintf (stderr, "Correct response 'Content-Length' header " | ||
1010 | "was found %u times instead of one time only.\n", | ||
1011 | hdr_res->size_found); | ||
1012 | ret = 0; | ||
1013 | } | ||
1014 | else if (verbose) | ||
1015 | printf ("'Content-Length' header with correct value " | ||
1016 | "is present in the response.\n"); | ||
1017 | if (0 != hdr_res->size_broken_found) | ||
1018 | { | ||
1019 | fprintf (stderr, "Wrong response 'Content-Length' header was found " | ||
1020 | "%u times.\n", hdr_res->size_broken_found); | ||
1021 | ret = 0; | ||
1022 | } | ||
1023 | |||
1024 | if (pcbc->dn_pos != MHD_STATICSTR_LEN_ (PAGE)) | ||
1025 | { | ||
1026 | fprintf (stderr, "Got %u bytes ('%.*s'), expected %u bytes. ", | ||
1027 | (unsigned) pcbc->dn_pos, (int) pcbc->dn_pos, pcbc->dn_buf, | ||
1028 | (unsigned) MHD_STATICSTR_LEN_ (PAGE)); | ||
1029 | mhdErrorExitDesc ("Wrong returned data length"); | ||
1030 | } | ||
1031 | if (0 != memcmp (PAGE, pcbc->dn_buf, pcbc->dn_pos)) | ||
1032 | { | ||
1033 | fprintf (stderr, "Got invalid response '%.*s'. ", | ||
1034 | (int) pcbc->dn_pos, pcbc->dn_buf); | ||
1035 | mhdErrorExitDesc ("Wrong returned data"); | ||
1036 | } | ||
1037 | fflush (stderr); | ||
1038 | fflush (stdout); | ||
1039 | return ret; | ||
1040 | } | ||
1041 | |||
1042 | |||
1043 | static unsigned int | ||
1044 | performCheck (void) | ||
1045 | { | ||
1046 | struct MHD_Daemon *d; | ||
1047 | uint16_t port; | ||
1048 | struct CBC cbc; | ||
1049 | struct ahc_cls_type ahc_param; | ||
1050 | struct headers_check_result rp_headers_check; | ||
1051 | char buf[2048]; | ||
1052 | CURL *c; | ||
1053 | CURLM *multi_reuse; | ||
1054 | int failed = 0; | ||
1055 | |||
1056 | if (MHD_NO != MHD_is_feature_supported (MHD_FEATURE_AUTODETECT_BIND_PORT)) | ||
1057 | port = 0; | ||
1058 | else | ||
1059 | { | ||
1060 | port = UINT16_C (4220); | ||
1061 | if (! oneone) | ||
1062 | port += UINT16_C (1); | ||
1063 | if (use_put) | ||
1064 | port += UINT16_C (2); | ||
1065 | if (use_put_large) | ||
1066 | port += UINT16_C (4); | ||
1067 | port = (uint16_t) 4220; | ||
1068 | } | ||
1069 | |||
1070 | if (1) | ||
1071 | { | ||
1072 | size_t mem_limit; | ||
1073 | if (use_put_large) | ||
1074 | mem_limit = (size_t) (TEST_UPLOAD_DATA_SIZE / 2 + HEADERS_POINTERS_SIZE); | ||
1075 | else | ||
1076 | mem_limit = (size_t) ((TEST_UPLOAD_DATA_SIZE * 4) / 3 + 2 + | ||
1077 | HEADERS_POINTERS_SIZE); | ||
1078 | |||
1079 | d = MHD_start_daemon (MHD_USE_ERROR_LOG, | ||
1080 | port, NULL, NULL, | ||
1081 | &ahcCheck, &ahc_param, | ||
1082 | MHD_OPTION_CONNECTION_MEMORY_LIMIT, mem_limit, | ||
1083 | MHD_OPTION_END); | ||
1084 | } | ||
1085 | if (d == NULL) | ||
1086 | return 1; | ||
1087 | if (0 == port) | ||
1088 | { | ||
1089 | const union MHD_DaemonInfo *dinfo; | ||
1090 | |||
1091 | dinfo = MHD_get_daemon_info (d, | ||
1092 | MHD_DAEMON_INFO_BIND_PORT); | ||
1093 | if ( (NULL == dinfo) || | ||
1094 | (0 == dinfo->port) ) | ||
1095 | mhdErrorExitDesc ("MHD_get_daemon_info() failed"); | ||
1096 | port = dinfo->port; | ||
1097 | } | ||
1098 | |||
1099 | /* First request */ | ||
1100 | ahc_param.rq_method = use_put ? MHD_HTTP_METHOD_PUT : MHD_HTTP_METHOD_GET; | ||
1101 | ahc_param.rq_url = EXPECTED_URI_BASE_PATH; | ||
1102 | ahc_param.expected_upload_size = use_put ? TEST_UPLOAD_DATA_SIZE : 0; | ||
1103 | ahc_param.req_check_error = 0; | ||
1104 | ahc_param.num_req = 0; | ||
1105 | ahc_param.up_pos = 0; | ||
1106 | rp_headers_check.expected_size = MHD_STATICSTR_LEN_ (PAGE); | ||
1107 | rp_headers_check.header1_found = 0; | ||
1108 | rp_headers_check.header2_found = 0; | ||
1109 | rp_headers_check.size_found = 0; | ||
1110 | rp_headers_check.size_broken_found = 0; | ||
1111 | cbc.dn_buf = buf; | ||
1112 | cbc.dn_buf_size = sizeof (buf); | ||
1113 | cbc.dn_pos = 0; | ||
1114 | memset (cbc.dn_buf, 0, cbc.dn_buf_size); | ||
1115 | cbc.up_size = TEST_UPLOAD_DATA_SIZE; | ||
1116 | cbc.up_pos = 0; | ||
1117 | c = setupCURL (&cbc, port, &rp_headers_check); | ||
1118 | multi_reuse = NULL; | ||
1119 | /* First request */ | ||
1120 | if (check_result (performQueryExternal (d, c, &multi_reuse), c, | ||
1121 | MHD_HTTP_OK, &cbc, &rp_headers_check, &ahc_param)) | ||
1122 | { | ||
1123 | fflush (stderr); | ||
1124 | if (verbose) | ||
1125 | printf ("Got first expected response.\n"); | ||
1126 | fflush (stdout); | ||
1127 | } | ||
1128 | else | ||
1129 | { | ||
1130 | fprintf (stderr, "First request FAILED.\n"); | ||
1131 | fflush (stderr); | ||
1132 | failed = 1; | ||
1133 | } | ||
1134 | /* Second request */ | ||
1135 | ahc_param.req_check_error = 0; | ||
1136 | ahc_param.num_req = 0; | ||
1137 | ahc_param.up_pos = 0; | ||
1138 | rp_headers_check.header1_found = 0; | ||
1139 | rp_headers_check.header2_found = 0; | ||
1140 | rp_headers_check.size_found = 0; | ||
1141 | rp_headers_check.size_broken_found = 0; | ||
1142 | /* Reset buffer position */ | ||
1143 | cbc.dn_pos = 0; | ||
1144 | memset (cbc.dn_buf, 0, cbc.dn_buf_size); | ||
1145 | cbc.up_pos = 0; | ||
1146 | if (check_result (performQueryExternal (d, c, &multi_reuse), c, | ||
1147 | MHD_HTTP_OK, &cbc, &rp_headers_check, &ahc_param)) | ||
1148 | { | ||
1149 | fflush (stderr); | ||
1150 | if (verbose) | ||
1151 | printf ("Got second expected response.\n"); | ||
1152 | fflush (stdout); | ||
1153 | } | ||
1154 | else | ||
1155 | { | ||
1156 | fprintf (stderr, "Second request FAILED.\n"); | ||
1157 | fflush (stderr); | ||
1158 | failed = 1; | ||
1159 | } | ||
1160 | /* Third request */ | ||
1161 | ahc_param.req_check_error = 0; | ||
1162 | ahc_param.num_req = 0; | ||
1163 | ahc_param.up_pos = 0; | ||
1164 | rp_headers_check.header1_found = 0; | ||
1165 | rp_headers_check.header2_found = 0; | ||
1166 | rp_headers_check.size_found = 0; | ||
1167 | rp_headers_check.size_broken_found = 0; | ||
1168 | /* Reset buffer position */ | ||
1169 | cbc.dn_pos = 0; | ||
1170 | memset (cbc.dn_buf, 0, cbc.dn_buf_size); | ||
1171 | cbc.up_pos = 0; | ||
1172 | if (NULL != multi_reuse) | ||
1173 | curl_multi_cleanup (multi_reuse); | ||
1174 | multi_reuse = NULL; /* Force new connection */ | ||
1175 | if (check_result (performQueryExternal (d, c, &multi_reuse), c, | ||
1176 | MHD_HTTP_OK, &cbc, &rp_headers_check, &ahc_param)) | ||
1177 | { | ||
1178 | fflush (stderr); | ||
1179 | if (verbose) | ||
1180 | printf ("Got third expected response.\n"); | ||
1181 | fflush (stdout); | ||
1182 | } | ||
1183 | else | ||
1184 | { | ||
1185 | fprintf (stderr, "Third request FAILED.\n"); | ||
1186 | fflush (stderr); | ||
1187 | failed = 1; | ||
1188 | } | ||
1189 | |||
1190 | curl_easy_cleanup (c); | ||
1191 | if (NULL != multi_reuse) | ||
1192 | curl_multi_cleanup (multi_reuse); | ||
1193 | |||
1194 | MHD_stop_daemon (d); | ||
1195 | return failed ? 1 : 0; | ||
1196 | } | ||
1197 | |||
1198 | |||
1199 | int | ||
1200 | main (int argc, char *const *argv) | ||
1201 | { | ||
1202 | unsigned int errorCount = 0; | ||
1203 | |||
1204 | /* Test type and test parameters */ | ||
1205 | verbose = ! (has_param (argc, argv, "-q") || | ||
1206 | has_param (argc, argv, "--quiet") || | ||
1207 | has_param (argc, argv, "-s") || | ||
1208 | has_param (argc, argv, "--silent")); | ||
1209 | oneone = ! has_in_name (argv[0], "10"); | ||
1210 | |||
1211 | use_get = has_in_name (argv[0], "_get"); | ||
1212 | use_put = has_in_name (argv[0], "_put"); | ||
1213 | |||
1214 | use_put_large = has_in_name (argv[0], "_put_large"); | ||
1215 | |||
1216 | if (1 != | ||
1217 | ((use_get ? 1 : 0) + (use_put ? 1 : 0))) | ||
1218 | { | ||
1219 | fprintf (stderr, "Wrong test name '%s': no or multiple indications " | ||
1220 | "for the test type.\n", argv[0] ? argv[0] : "(NULL)"); | ||
1221 | return 99; | ||
1222 | } | ||
1223 | |||
1224 | test_global_init (); | ||
1225 | |||
1226 | errorCount += performCheck (); | ||
1227 | if (errorCount != 0) | ||
1228 | fprintf (stderr, "Error (code: %u)\n", errorCount); | ||
1229 | test_global_cleanup (); | ||
1230 | return (0 == errorCount) ? 0 : 1; /* 0 == pass */ | ||
1231 | } | ||