1 /*
2 This file is part of libmicrohttpd
3 Copyright (C) 2007 Christian Grothoff
4 Copyright (C) 2014-2021 Evgeny Grin (Karlson2k)
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 /**
23 * @file test_get_chunked.c
24 * @brief Testcase for libmicrohttpd GET operations with chunked content encoding
25 * @author Christian Grothoff
26 * @author Karlson2k (Evgeny Grin)
27 */
28
29 #include "MHD_config.h"
30 #include "platform.h"
31 #include <curl/curl.h>
32 #include <microhttpd.h>
33 #include <stdlib.h>
34 #include <string.h>
35 #include <time.h>
36
37 #ifndef WINDOWS
38 #include <unistd.h>
39 #endif
40
41 #include "mhd_has_in_name.h"
42
43 #if defined(MHD_CPU_COUNT) && (MHD_CPU_COUNT + 0) < 2
44 #undef MHD_CPU_COUNT
45 #endif
46 #if ! defined(MHD_CPU_COUNT)
47 #define MHD_CPU_COUNT 2
48 #endif
49
50 #define HDR_CHUNKED_ENCODING MHD_HTTP_HEADER_TRANSFER_ENCODING ": chunked"
51 #define RESP_FOOTER_NAME "Footer"
52 #define RESP_FOOTER_VALUE "working"
53 #define RESP_FOOTER RESP_FOOTER_NAME ": " RESP_FOOTER_VALUE
54
55 #define RESP_BLOCK_SIZE 128
56 #define RESP_BLOCK_QUANTIY 10
57 #define RESP_SIZE (RESP_BLOCK_SIZE * RESP_BLOCK_QUANTIY)
58
59 /**
60 * Use "Connection: close" header?
61 */
62 int conn_close;
63
64 /**
65 * Use static string response instead of callback-generated?
66 */
67 int resp_string;
68
69 /**
70 * Use response with known size?
71 */
72 int resp_sized;
73
74 /**
75 * Use empty (zero-sized) response?
76 */
77 int resp_empty;
78
79 /**
80 * Force chunked response by response header?
81 */
82 int chunked_forced;
83
84 /**
85 * MHD port used for testing
86 */
87 int port_global;
88
89
90 struct headers_check_result
91 {
92 int found_chunked;
93 int found_footer;
94 };
95
96 size_t
lcurl_hdr_callback(char * buffer,size_t size,size_t nitems,void * userdata)97 lcurl_hdr_callback (char *buffer, size_t size, size_t nitems,
98 void *userdata)
99 {
100 const size_t data_size = size * nitems;
101 struct headers_check_result *check_res =
102 (struct headers_check_result *) userdata;
103
104 if ((data_size == strlen (HDR_CHUNKED_ENCODING) + 2) &&
105 (0 == memcmp (buffer, HDR_CHUNKED_ENCODING "\r\n", data_size)))
106 check_res->found_chunked = 1;
107 if ((data_size == strlen (RESP_FOOTER) + 2) &&
108 (0 == memcmp (buffer, RESP_FOOTER "\r\n", data_size)))
109 check_res->found_footer = 1;
110
111 return data_size;
112 }
113
114
115 struct CBC
116 {
117 char *buf;
118 size_t pos;
119 size_t size;
120 };
121
122
123 static size_t
copyBuffer(void * ptr,size_t size,size_t nmemb,void * ctx)124 copyBuffer (void *ptr,
125 size_t size,
126 size_t nmemb,
127 void *ctx)
128 {
129 struct CBC *cbc = ctx;
130
131 if (cbc->pos + size * nmemb > cbc->size)
132 return 0; /* overflow */
133 memcpy (&cbc->buf[cbc->pos], ptr, size * nmemb);
134 cbc->pos += size * nmemb;
135 return size * nmemb;
136 }
137
138
139 /**
140 * MHD content reader callback that returns data in chunks.
141 */
142 static ssize_t
crc(void * cls,uint64_t pos,char * buf,size_t max)143 crc (void *cls,
144 uint64_t pos,
145 char *buf,
146 size_t max)
147 {
148 struct MHD_Response **responseptr = cls;
149
150 if (resp_empty || (pos == RESP_SIZE - RESP_BLOCK_SIZE))
151 { /* Add footer with the last block */
152 if (MHD_YES != MHD_add_response_footer (*responseptr,
153 RESP_FOOTER_NAME,
154 RESP_FOOTER_VALUE))
155 abort ();
156 }
157 if (resp_empty || (pos == RESP_SIZE))
158 return MHD_CONTENT_READER_END_OF_STREAM;
159
160 if (max < RESP_BLOCK_SIZE)
161 abort (); /* should not happen in this testcase... */
162 memset (buf, 'A' + (pos / RESP_BLOCK_SIZE), RESP_BLOCK_SIZE);
163 return RESP_BLOCK_SIZE;
164 }
165
166
167 /**
168 * Dummy function that frees the "responseptr".
169 */
170 static void
crcf(void * ptr)171 crcf (void *ptr)
172 {
173 free (ptr);
174 }
175
176
177 static enum MHD_Result
ahc_echo(void * cls,struct MHD_Connection * connection,const char * url,const char * method,const char * version,const char * upload_data,size_t * upload_data_size,void ** ptr)178 ahc_echo (void *cls,
179 struct MHD_Connection *connection,
180 const char *url,
181 const char *method,
182 const char *version,
183 const char *upload_data, size_t *upload_data_size, void **ptr)
184 {
185 static int aptr;
186 const char *me = cls;
187 struct MHD_Response *response;
188 enum MHD_Result ret;
189
190 (void) url;
191 (void) version; /* Unused. Silent compiler warning. */
192 (void) upload_data;
193 (void) upload_data_size; /* Unused. Silent compiler warning. */
194
195 if (0 != strcmp (me, method))
196 return MHD_NO; /* unexpected method */
197 if (&aptr != *ptr)
198 {
199 /* do never respond on first call */
200 *ptr = &aptr;
201 return MHD_YES;
202 }
203 if (! resp_string)
204 {
205 struct MHD_Response **responseptr;
206 responseptr = malloc (sizeof (struct MHD_Response *));
207 if (NULL == responseptr)
208 _exit (99);
209
210 response = MHD_create_response_from_callback (resp_sized ?
211 RESP_SIZE : MHD_SIZE_UNKNOWN,
212 1024,
213 &crc,
214 responseptr,
215 &crcf);
216 *responseptr = response;
217 }
218 else
219 {
220 if (! resp_empty)
221 {
222 size_t pos;
223 static const size_t resp_size = RESP_SIZE;
224 char *buf = malloc (resp_size);
225 if (NULL == buf)
226 _exit (99);
227 for (pos = 0; pos < resp_size; pos += RESP_BLOCK_SIZE)
228 memset (buf + pos, 'A' + (pos / RESP_BLOCK_SIZE), RESP_BLOCK_SIZE);
229
230 response = MHD_create_response_from_buffer (resp_size, buf,
231 MHD_RESPMEM_MUST_COPY);
232 free (buf);
233 }
234 else
235 response = MHD_create_response_from_buffer (0, NULL,
236 MHD_RESPMEM_PERSISTENT);
237 }
238 if (NULL == response)
239 abort ();
240 if (chunked_forced)
241 {
242 if (MHD_NO == MHD_add_response_header (response,
243 MHD_HTTP_HEADER_TRANSFER_ENCODING,
244 "chunked"))
245 abort ();
246 }
247 if (MHD_NO == MHD_add_response_header (response,
248 MHD_HTTP_HEADER_TRAILER,
249 RESP_FOOTER_NAME))
250 abort ();
251
252 if (resp_string || (resp_sized && resp_empty))
253 {
254 /* There is no chance to add footer later */
255 if (MHD_YES != MHD_add_response_footer (response,
256 RESP_FOOTER_NAME,
257 RESP_FOOTER_VALUE))
258 abort ();
259 }
260
261 ret = MHD_queue_response (connection,
262 MHD_HTTP_OK,
263 response);
264 MHD_destroy_response (response);
265 return ret;
266 }
267
268
269 static int
validate(struct CBC cbc,int ebase)270 validate (struct CBC cbc, int ebase)
271 {
272 int i;
273 char buf[RESP_BLOCK_SIZE];
274
275 if (resp_empty)
276 {
277 if (0 != cbc.pos)
278 {
279 fprintf (stderr,
280 "Got %u bytes instead of zero!\n",
281 (unsigned int) cbc.pos);
282 return 1;
283 }
284 return 0;
285 }
286
287 if (cbc.pos != RESP_SIZE)
288 {
289 fprintf (stderr,
290 "Got %u bytes instead of 1280!\n",
291 (unsigned int) cbc.pos);
292 return ebase;
293 }
294
295 for (i = 0; i < RESP_BLOCK_QUANTIY; i++)
296 {
297 memset (buf, 'A' + i, RESP_BLOCK_SIZE);
298 if (0 != memcmp (buf, &cbc.buf[i * RESP_BLOCK_SIZE], RESP_BLOCK_SIZE))
299 {
300 fprintf (stderr,
301 "Got `%.*s'\nWant `%.*s'\n",
302 RESP_BLOCK_SIZE, &cbc.buf[i * RESP_BLOCK_SIZE],
303 RESP_BLOCK_SIZE, buf);
304 return ebase * 2;
305 }
306 }
307 return 0;
308 }
309
310
311 static int
testInternalGet()312 testInternalGet ()
313 {
314 struct MHD_Daemon *d;
315 CURL *c;
316 char buf[2048];
317 struct CBC cbc;
318 CURLcode errornum;
319 int port;
320 struct curl_slist *h_list = NULL;
321 struct headers_check_result hdr_check;
322
323 port = port_global;
324 cbc.buf = buf;
325 cbc.size = 2048;
326 cbc.pos = 0;
327 d = MHD_start_daemon (MHD_USE_INTERNAL_POLLING_THREAD | MHD_USE_ERROR_LOG,
328 port, NULL, NULL, &ahc_echo, "GET", MHD_OPTION_END);
329 if (d == NULL)
330 return 1;
331 if (0 == port)
332 {
333 const union MHD_DaemonInfo *dinfo;
334 dinfo = MHD_get_daemon_info (d, MHD_DAEMON_INFO_BIND_PORT);
335 if ((NULL == dinfo) || (0 == dinfo->port) )
336 {
337 MHD_stop_daemon (d); return 32;
338 }
339 port = (int) dinfo->port;
340 if (0 == port_global)
341 port_global = port; /* Re-use the same port for all checks */
342 }
343 hdr_check.found_chunked = 0;
344 hdr_check.found_footer = 0;
345 c = curl_easy_init ();
346 curl_easy_setopt (c, CURLOPT_URL, "http://127.0.0.1/hello_world");
347 curl_easy_setopt (c, CURLOPT_PORT, (long) port);
348 curl_easy_setopt (c, CURLOPT_WRITEFUNCTION, ©Buffer);
349 curl_easy_setopt (c, CURLOPT_WRITEDATA, &cbc);
350 curl_easy_setopt (c, CURLOPT_FAILONERROR, 1L);
351 curl_easy_setopt (c, CURLOPT_TIMEOUT, 150L);
352 curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT, 150L);
353 curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
354 curl_easy_setopt (c, CURLOPT_HEADERFUNCTION, lcurl_hdr_callback);
355 curl_easy_setopt (c, CURLOPT_HEADERDATA, &hdr_check);
356 curl_easy_setopt (c, CURLOPT_NOSIGNAL, 1L);
357 if (conn_close)
358 {
359 h_list = curl_slist_append (h_list, "Connection: close");
360 if (NULL == h_list)
361 abort ();
362 curl_easy_setopt (c, CURLOPT_HTTPHEADER, h_list);
363 }
364 if (CURLE_OK != (errornum = curl_easy_perform (c)))
365 {
366 fprintf (stderr,
367 "curl_easy_perform failed: `%s'\n",
368 curl_easy_strerror (errornum));
369 curl_easy_cleanup (c);
370 curl_slist_free_all (h_list);
371 MHD_stop_daemon (d);
372 return 2;
373 }
374 curl_easy_cleanup (c);
375 curl_slist_free_all (h_list);
376 MHD_stop_daemon (d);
377 if (1 != hdr_check.found_chunked)
378 {
379 fprintf (stderr,
380 "Chunked encoding header was not found in the response\n");
381 return 8;
382 }
383 if (1 != hdr_check.found_footer)
384 {
385 fprintf (stderr,
386 "The specified footer was not found in the response\n");
387 return 16;
388 }
389 return validate (cbc, 4);
390 }
391
392
393 static int
testMultithreadedGet()394 testMultithreadedGet ()
395 {
396 struct MHD_Daemon *d;
397 CURL *c;
398 char buf[2048];
399 struct CBC cbc;
400 CURLcode errornum;
401 int port;
402 struct curl_slist *h_list = NULL;
403 struct headers_check_result hdr_check;
404
405 port = port_global;
406 cbc.buf = buf;
407 cbc.size = 2048;
408 cbc.pos = 0;
409 d = MHD_start_daemon (MHD_USE_THREAD_PER_CONNECTION
410 | MHD_USE_INTERNAL_POLLING_THREAD | MHD_USE_ERROR_LOG,
411 port, NULL, NULL, &ahc_echo, "GET", MHD_OPTION_END);
412 if (d == NULL)
413 return 16;
414 if (0 == port)
415 {
416 const union MHD_DaemonInfo *dinfo;
417 dinfo = MHD_get_daemon_info (d, MHD_DAEMON_INFO_BIND_PORT);
418 if ((NULL == dinfo) || (0 == dinfo->port) )
419 {
420 MHD_stop_daemon (d); return 32;
421 }
422 port = (int) dinfo->port;
423 if (0 == port_global)
424 port_global = port; /* Re-use the same port for all checks */
425 }
426 c = curl_easy_init ();
427 curl_easy_setopt (c, CURLOPT_URL, "http://127.0.0.1/hello_world");
428 curl_easy_setopt (c, CURLOPT_PORT, (long) port);
429 curl_easy_setopt (c, CURLOPT_WRITEFUNCTION, ©Buffer);
430 curl_easy_setopt (c, CURLOPT_WRITEDATA, &cbc);
431 curl_easy_setopt (c, CURLOPT_FAILONERROR, 1L);
432 curl_easy_setopt (c, CURLOPT_TIMEOUT, 150L);
433 curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
434 curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT, 150L);
435 curl_easy_setopt (c, CURLOPT_NOSIGNAL, 1L);
436 hdr_check.found_chunked = 0;
437 hdr_check.found_footer = 0;
438 curl_easy_setopt (c, CURLOPT_HEADERFUNCTION, lcurl_hdr_callback);
439 curl_easy_setopt (c, CURLOPT_HEADERDATA, &hdr_check);
440 if (conn_close)
441 {
442 h_list = curl_slist_append (h_list, "Connection: close");
443 if (NULL == h_list)
444 abort ();
445 curl_easy_setopt (c, CURLOPT_HTTPHEADER, h_list);
446 }
447 if (CURLE_OK != (errornum = curl_easy_perform (c)))
448 {
449 fprintf (stderr,
450 "curl_easy_perform failed: `%s'\n",
451 curl_easy_strerror (errornum));
452 curl_easy_cleanup (c);
453 curl_slist_free_all (h_list);
454 MHD_stop_daemon (d);
455 return 32;
456 }
457 curl_easy_cleanup (c);
458 curl_slist_free_all (h_list);
459 MHD_stop_daemon (d);
460 if (1 != hdr_check.found_chunked)
461 {
462 fprintf (stderr,
463 "Chunked encoding header was not found in the response\n");
464 return 8;
465 }
466 if (1 != hdr_check.found_footer)
467 {
468 fprintf (stderr,
469 "The specified footer was not found in the response\n");
470 return 16;
471 }
472 return validate (cbc, 64);
473 }
474
475
476 static int
testMultithreadedPoolGet()477 testMultithreadedPoolGet ()
478 {
479 struct MHD_Daemon *d;
480 CURL *c;
481 char buf[2048];
482 struct CBC cbc;
483 CURLcode errornum;
484 int port;
485 struct curl_slist *h_list = NULL;
486 struct headers_check_result hdr_check;
487
488 port = port_global;
489 cbc.buf = buf;
490 cbc.size = 2048;
491 cbc.pos = 0;
492 d = MHD_start_daemon (MHD_USE_INTERNAL_POLLING_THREAD | MHD_USE_ERROR_LOG,
493 port, NULL, NULL, &ahc_echo, "GET",
494 MHD_OPTION_THREAD_POOL_SIZE, MHD_CPU_COUNT,
495 MHD_OPTION_END);
496 if (d == NULL)
497 return 16;
498 if (0 == port)
499 {
500 const union MHD_DaemonInfo *dinfo;
501 dinfo = MHD_get_daemon_info (d, MHD_DAEMON_INFO_BIND_PORT);
502 if ((NULL == dinfo) || (0 == dinfo->port) )
503 {
504 MHD_stop_daemon (d); return 32;
505 }
506 port = (int) dinfo->port;
507 if (0 == port_global)
508 port_global = port; /* Re-use the same port for all checks */
509 }
510 c = curl_easy_init ();
511 curl_easy_setopt (c, CURLOPT_URL, "http://127.0.0.1/hello_world");
512 curl_easy_setopt (c, CURLOPT_PORT, (long) port);
513 curl_easy_setopt (c, CURLOPT_WRITEFUNCTION, ©Buffer);
514 curl_easy_setopt (c, CURLOPT_WRITEDATA, &cbc);
515 curl_easy_setopt (c, CURLOPT_FAILONERROR, 1L);
516 curl_easy_setopt (c, CURLOPT_TIMEOUT, 150L);
517 curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
518 curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT, 150L);
519 curl_easy_setopt (c, CURLOPT_NOSIGNAL, 1L);
520 hdr_check.found_chunked = 0;
521 hdr_check.found_footer = 0;
522 curl_easy_setopt (c, CURLOPT_HEADERFUNCTION, lcurl_hdr_callback);
523 curl_easy_setopt (c, CURLOPT_HEADERDATA, &hdr_check);
524 if (conn_close)
525 {
526 h_list = curl_slist_append (h_list, "Connection: close");
527 if (NULL == h_list)
528 abort ();
529 curl_easy_setopt (c, CURLOPT_HTTPHEADER, h_list);
530 }
531 if (CURLE_OK != (errornum = curl_easy_perform (c)))
532 {
533 fprintf (stderr,
534 "curl_easy_perform failed: `%s'\n",
535 curl_easy_strerror (errornum));
536 curl_easy_cleanup (c);
537 curl_slist_free_all (h_list);
538 MHD_stop_daemon (d);
539 return 32;
540 }
541 curl_easy_cleanup (c);
542 curl_slist_free_all (h_list);
543 MHD_stop_daemon (d);
544 if (1 != hdr_check.found_chunked)
545 {
546 fprintf (stderr,
547 "Chunked encoding header was not found in the response\n");
548 return 8;
549 }
550 if (1 != hdr_check.found_footer)
551 {
552 fprintf (stderr,
553 "The specified footer was not found in the response\n");
554 return 16;
555 }
556 return validate (cbc, 64);
557 }
558
559
560 static int
testExternalGet()561 testExternalGet ()
562 {
563 struct MHD_Daemon *d;
564 CURL *c;
565 char buf[2048];
566 struct CBC cbc;
567 CURLM *multi;
568 CURLMcode mret;
569 fd_set rs;
570 fd_set ws;
571 fd_set es;
572 MHD_socket maxsock;
573 #ifdef MHD_WINSOCK_SOCKETS
574 int maxposixs; /* Max socket number unused on W32 */
575 #else /* MHD_POSIX_SOCKETS */
576 #define maxposixs maxsock
577 #endif /* MHD_POSIX_SOCKETS */
578 int running;
579 struct CURLMsg *msg;
580 time_t start;
581 struct timeval tv;
582 int port;
583 struct curl_slist *h_list = NULL;
584 struct headers_check_result hdr_check;
585
586 port = port_global;
587 multi = NULL;
588 cbc.buf = buf;
589 cbc.size = 2048;
590 cbc.pos = 0;
591 d = MHD_start_daemon (MHD_USE_ERROR_LOG,
592 port, NULL, NULL, &ahc_echo, "GET", MHD_OPTION_END);
593 if (d == NULL)
594 return 256;
595 if (0 == port)
596 {
597 const union MHD_DaemonInfo *dinfo;
598 dinfo = MHD_get_daemon_info (d, MHD_DAEMON_INFO_BIND_PORT);
599 if ((NULL == dinfo) || (0 == dinfo->port) )
600 {
601 MHD_stop_daemon (d); return 32;
602 }
603 port = (int) dinfo->port;
604 if (0 == port_global)
605 port_global = port; /* Re-use the same port for all checks */
606 }
607 c = curl_easy_init ();
608 curl_easy_setopt (c, CURLOPT_URL, "http://127.0.0.1/hello_world");
609 curl_easy_setopt (c, CURLOPT_PORT, (long) port);
610 curl_easy_setopt (c, CURLOPT_WRITEFUNCTION, ©Buffer);
611 curl_easy_setopt (c, CURLOPT_WRITEDATA, &cbc);
612 curl_easy_setopt (c, CURLOPT_FAILONERROR, 1L);
613 curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
614 curl_easy_setopt (c, CURLOPT_TIMEOUT, 150L);
615 curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT, 5L);
616 curl_easy_setopt (c, CURLOPT_NOSIGNAL, 1L);
617 hdr_check.found_chunked = 0;
618 hdr_check.found_footer = 0;
619 curl_easy_setopt (c, CURLOPT_HEADERFUNCTION, lcurl_hdr_callback);
620 curl_easy_setopt (c, CURLOPT_HEADERDATA, &hdr_check);
621 if (conn_close)
622 {
623 h_list = curl_slist_append (h_list, "Connection: close");
624 if (NULL == h_list)
625 abort ();
626 curl_easy_setopt (c, CURLOPT_HTTPHEADER, h_list);
627 }
628
629 multi = curl_multi_init ();
630 if (multi == NULL)
631 {
632 curl_easy_cleanup (c);
633 curl_slist_free_all (h_list);
634 MHD_stop_daemon (d);
635 return 512;
636 }
637 mret = curl_multi_add_handle (multi, c);
638 if (mret != CURLM_OK)
639 {
640 curl_multi_cleanup (multi);
641 curl_easy_cleanup (c);
642 curl_slist_free_all (h_list);
643 MHD_stop_daemon (d);
644 return 1024;
645 }
646 start = time (NULL);
647 while ((time (NULL) - start < 5) && (multi != NULL))
648 {
649 maxsock = MHD_INVALID_SOCKET;
650 maxposixs = -1;
651 FD_ZERO (&rs);
652 FD_ZERO (&ws);
653 FD_ZERO (&es);
654 curl_multi_perform (multi, &running);
655 mret = curl_multi_fdset (multi, &rs, &ws, &es, &maxposixs);
656 if (mret != CURLM_OK)
657 {
658 curl_multi_remove_handle (multi, c);
659 curl_multi_cleanup (multi);
660 curl_easy_cleanup (c);
661 curl_slist_free_all (h_list);
662 MHD_stop_daemon (d);
663 return 2048;
664 }
665 if (MHD_YES != MHD_get_fdset (d, &rs, &ws, &es, &maxsock))
666 {
667 curl_multi_remove_handle (multi, c);
668 curl_multi_cleanup (multi);
669 curl_easy_cleanup (c);
670 curl_slist_free_all (h_list);
671 MHD_stop_daemon (d);
672 return 4096;
673 }
674 tv.tv_sec = 0;
675 tv.tv_usec = 1000;
676 if (-1 == select (maxposixs + 1, &rs, &ws, &es, &tv))
677 {
678 #ifdef MHD_POSIX_SOCKETS
679 if (EINTR != errno)
680 {
681 fprintf (stderr, "Unexpected select() error: %d. Line: %d\n",
682 (int) errno, __LINE__);
683 fflush (stderr);
684 exit (99);
685 }
686 #else
687 if ((WSAEINVAL != WSAGetLastError ()) ||
688 (0 != rs.fd_count) || (0 != ws.fd_count) || (0 != es.fd_count) )
689 {
690 fprintf (stderr, "Unexpected select() error: %d. Line: %d\n",
691 (int) WSAGetLastError (), __LINE__);
692 fflush (stderr);
693 exit (99);
694 }
695 Sleep (1);
696 #endif
697 }
698 curl_multi_perform (multi, &running);
699 if (0 == running)
700 {
701 int pending;
702 int curl_fine = 0;
703 while (NULL != (msg = curl_multi_info_read (multi, &pending)))
704 {
705 if (msg->msg == CURLMSG_DONE)
706 {
707 if (msg->data.result == CURLE_OK)
708 curl_fine = 1;
709 else
710 {
711 fprintf (stderr,
712 "%s failed at %s:%d: `%s'\n",
713 "curl_multi_perform",
714 __FILE__,
715 __LINE__, curl_easy_strerror (msg->data.result));
716 abort ();
717 }
718 }
719 }
720 if (! curl_fine)
721 {
722 fprintf (stderr, "libcurl haven't returned OK code\n");
723 abort ();
724 }
725 curl_multi_remove_handle (multi, c);
726 curl_multi_cleanup (multi);
727 curl_easy_cleanup (c);
728 curl_slist_free_all (h_list);
729 h_list = NULL;
730 c = NULL;
731 multi = NULL;
732 }
733 MHD_run (d);
734 }
735 MHD_stop_daemon (d);
736 if (multi != NULL)
737 {
738 curl_multi_remove_handle (multi, c);
739 curl_easy_cleanup (c);
740 curl_multi_cleanup (multi);
741 }
742 curl_slist_free_all (h_list);
743 if (1 != hdr_check.found_chunked)
744 {
745 fprintf (stderr,
746 "Chunked encoding header was not found in the response\n");
747 return 8;
748 }
749 if (1 != hdr_check.found_footer)
750 {
751 fprintf (stderr,
752 "The specified footer was not found in the response\n");
753 return 16;
754 }
755 return validate (cbc, 8192);
756 }
757
758
759 int
main(int argc,char * const * argv)760 main (int argc, char *const *argv)
761 {
762 unsigned int errorCount = 0;
763 (void) argc; (void) argv; /* Unused. Silent compiler warning. */
764
765 if (0 != curl_global_init (CURL_GLOBAL_WIN32))
766 return 2;
767 conn_close = has_in_name (argv[0], "_close");
768 resp_string = has_in_name (argv[0], "_string");
769 resp_sized = has_in_name (argv[0], "_sized");
770 resp_empty = has_in_name (argv[0], "_empty");
771 chunked_forced = has_in_name (argv[0], "_forced");
772 if (resp_string)
773 resp_sized = ! 0;
774 if (resp_sized)
775 chunked_forced = ! 0;
776
777 if (MHD_NO != MHD_is_feature_supported (MHD_FEATURE_AUTODETECT_BIND_PORT))
778 port_global = 0;
779 else
780 {
781 port_global = 4100;
782 if (conn_close)
783 port_global += 1 << 0;
784 if (resp_string)
785 port_global += 1 << 1;
786 if (resp_sized)
787 port_global += 1 << 2;
788 if (resp_empty)
789 port_global += 1 << 3;
790 if (chunked_forced)
791 port_global += 1 << 4;
792 }
793
794 if (MHD_YES == MHD_is_feature_supported (MHD_FEATURE_THREADS))
795 {
796 errorCount += testInternalGet ();
797 errorCount += testMultithreadedGet ();
798 errorCount += testMultithreadedPoolGet ();
799 }
800 errorCount += testExternalGet ();
801 if (errorCount != 0)
802 fprintf (stderr, "Error (code: %u)\n", errorCount);
803 curl_global_cleanup ();
804 return (0 == errorCount) ? 0 : 1; /* 0 == pass */
805 }
806