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, &copyBuffer);
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, &copyBuffer);
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, &copyBuffer);
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, &copyBuffer);
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