libmicrohttpd

HTTP/1.x server C library (MHD 1.x, stable)
Log | Files | Refs | Submodules | README | LICENSE

test_large_put.c (28069B)


      1 /*
      2      This file is part of libmicrohttpd
      3      Copyright (C) 2007, 2008 Christian Grothoff
      4      Copyright (C) 2014-2022 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_large_put.c
     24  * @brief  Testcase for libmicrohttpd PUT operations
     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 #include <errno.h>
     37 
     38 #ifndef WINDOWS
     39 #include <unistd.h>
     40 #endif
     41 
     42 #include "mhd_has_in_name.h"
     43 #include "mhd_has_param.h"
     44 
     45 #if defined(MHD_CPU_COUNT) && (MHD_CPU_COUNT + 0) < 2
     46 #undef MHD_CPU_COUNT
     47 #endif
     48 #if ! defined(MHD_CPU_COUNT)
     49 #define MHD_CPU_COUNT 2
     50 #endif
     51 
     52 
     53 #if defined(HAVE___FUNC__)
     54 #define externalErrorExit(ignore) \
     55     _externalErrorExit_func(NULL, __func__, __LINE__)
     56 #define externalErrorExitDesc(errDesc) \
     57     _externalErrorExit_func(errDesc, __func__, __LINE__)
     58 #define libcurlErrorExit(ignore) \
     59     _libcurlErrorExit_func(NULL, __func__, __LINE__)
     60 #define libcurlErrorExitDesc(errDesc) \
     61     _libcurlErrorExit_func(errDesc, __func__, __LINE__)
     62 #define mhdErrorExit(ignore) \
     63     _mhdErrorExit_func(NULL, __func__, __LINE__)
     64 #define mhdErrorExitDesc(errDesc) \
     65     _mhdErrorExit_func(errDesc, __func__, __LINE__)
     66 #elif defined(HAVE___FUNCTION__)
     67 #define externalErrorExit(ignore) \
     68     _externalErrorExit_func(NULL, __FUNCTION__, __LINE__)
     69 #define externalErrorExitDesc(errDesc) \
     70     _externalErrorExit_func(errDesc, __FUNCTION__, __LINE__)
     71 #define libcurlErrorExit(ignore) \
     72     _libcurlErrorExit_func(NULL, __FUNCTION__, __LINE__)
     73 #define libcurlErrorExitDesc(errDesc) \
     74     _libcurlErrorExit_func(errDesc, __FUNCTION__, __LINE__)
     75 #define mhdErrorExit(ignore) \
     76     _mhdErrorExit_func(NULL, __FUNCTION__, __LINE__)
     77 #define mhdErrorExitDesc(errDesc) \
     78     _mhdErrorExit_func(errDesc, __FUNCTION__, __LINE__)
     79 #else
     80 #define externalErrorExit(ignore) _externalErrorExit_func(NULL, NULL, __LINE__)
     81 #define externalErrorExitDesc(errDesc) \
     82   _externalErrorExit_func(errDesc, NULL, __LINE__)
     83 #define libcurlErrorExit(ignore) _libcurlErrorExit_func(NULL, NULL, __LINE__)
     84 #define libcurlErrorExitDesc(errDesc) \
     85   _libcurlErrorExit_func(errDesc, NULL, __LINE__)
     86 #define mhdErrorExit(ignore) _mhdErrorExit_func(NULL, NULL, __LINE__)
     87 #define mhdErrorExitDesc(errDesc) _mhdErrorExit_func(errDesc, NULL, __LINE__)
     88 #endif
     89 
     90 
     91 _MHD_NORETURN static void
     92 _externalErrorExit_func (const char *errDesc, const char *funcName, int lineNum)
     93 {
     94   if ((NULL != errDesc) && (0 != errDesc[0]))
     95     fprintf (stderr, "%s", errDesc);
     96   else
     97     fprintf (stderr, "System or external library call failed");
     98   if ((NULL != funcName) && (0 != funcName[0]))
     99     fprintf (stderr, " in %s", funcName);
    100   if (0 < lineNum)
    101     fprintf (stderr, " at line %d", lineNum);
    102 
    103   fprintf (stderr, ".\nLast errno value: %d (%s)\n", (int) errno,
    104            strerror (errno));
    105 #ifdef MHD_WINSOCK_SOCKETS
    106   fprintf (stderr, "WSAGetLastError() value: %d\n", (int) WSAGetLastError ());
    107 #endif /* MHD_WINSOCK_SOCKETS */
    108   fflush (stderr);
    109   exit (99);
    110 }
    111 
    112 
    113 static char libcurl_errbuf[CURL_ERROR_SIZE] = "";
    114 
    115 _MHD_NORETURN static void
    116 _libcurlErrorExit_func (const char *errDesc, const char *funcName, int lineNum)
    117 {
    118   if ((NULL != errDesc) && (0 != errDesc[0]))
    119     fprintf (stderr, "%s", errDesc);
    120   else
    121     fprintf (stderr, "CURL library call failed");
    122   if ((NULL != funcName) && (0 != funcName[0]))
    123     fprintf (stderr, " in %s", funcName);
    124   if (0 < lineNum)
    125     fprintf (stderr, " at line %d", lineNum);
    126 
    127   fprintf (stderr, ".\nLast errno value: %d (%s)\n", (int) errno,
    128            strerror (errno));
    129   if (0 != libcurl_errbuf[0])
    130     fprintf (stderr, "Last libcurl error details: %s\n", libcurl_errbuf);
    131 
    132   fflush (stderr);
    133   exit (99);
    134 }
    135 
    136 
    137 _MHD_NORETURN static void
    138 _mhdErrorExit_func (const char *errDesc, const char *funcName, int lineNum)
    139 {
    140   if ((NULL != errDesc) && (0 != errDesc[0]))
    141     fprintf (stderr, "%s", errDesc);
    142   else
    143     fprintf (stderr, "MHD unexpected error");
    144   if ((NULL != funcName) && (0 != funcName[0]))
    145     fprintf (stderr, " in %s", funcName);
    146   if (0 < lineNum)
    147     fprintf (stderr, " at line %d", lineNum);
    148 
    149   fprintf (stderr, ".\nLast errno value: %d (%s)\n", (int) errno,
    150            strerror (errno));
    151 
    152   fflush (stderr);
    153   exit (8);
    154 }
    155 
    156 
    157 static int oneone;
    158 static int incr_read; /* Use incremental read */
    159 static int verbose; /* Be verbose */
    160 
    161 #define PUT_SIZE (256 * 1024)
    162 
    163 static char *put_buffer;
    164 
    165 struct CBC
    166 {
    167   char *buf;
    168   size_t pos;
    169   size_t size;
    170 };
    171 
    172 static char *
    173 alloc_init (size_t buf_size)
    174 {
    175   static const char template[] =
    176     "ABCDEFGHIJKLMNOPQRSTUVWXYZ[]^_`abcdefghijklmnopqrstuvwxyz";
    177   static const size_t templ_size = sizeof(template) / sizeof(char) - 1;
    178   char *buf;
    179   char *fill_ptr;
    180   size_t to_fill;
    181 
    182   buf = malloc (buf_size);
    183   if (NULL == buf)
    184     externalErrorExit ();
    185 
    186   fill_ptr = buf;
    187   to_fill = buf_size;
    188   while (to_fill > 0)
    189   {
    190     const size_t to_copy = to_fill > templ_size ? templ_size : to_fill;
    191     memcpy (fill_ptr, template, to_copy);
    192     fill_ptr += to_copy;
    193     to_fill -= to_copy;
    194   }
    195   return buf;
    196 }
    197 
    198 
    199 static size_t
    200 putBuffer (void *stream, size_t size, size_t nmemb, void *ptr)
    201 {
    202   size_t *pos = (size_t *) ptr;
    203   size_t wrt;
    204 
    205   wrt = size * nmemb;
    206   /* Check for overflow. */
    207   if (wrt / size != nmemb)
    208     libcurlErrorExitDesc ("Too large buffer size");
    209   if (wrt > PUT_SIZE - (*pos))
    210     wrt = PUT_SIZE - (*pos);
    211   memcpy (stream, &put_buffer[*pos], wrt);
    212   (*pos) += wrt;
    213   return wrt;
    214 }
    215 
    216 
    217 static size_t
    218 copyBuffer (void *ptr, size_t size, size_t nmemb, void *ctx)
    219 {
    220   struct CBC *cbc = ctx;
    221 
    222   if (cbc->pos + size * nmemb > cbc->size)
    223     libcurlErrorExitDesc ("Too large buffer size");
    224   memcpy (&cbc->buf[cbc->pos], ptr, size * nmemb);
    225   cbc->pos += size * nmemb;
    226   return size * nmemb;
    227 }
    228 
    229 
    230 static enum MHD_Result
    231 ahc_echo (void *cls,
    232           struct MHD_Connection *connection,
    233           const char *url,
    234           const char *method,
    235           const char *version,
    236           const char *upload_data, size_t *upload_data_size,
    237           void **req_cls)
    238 {
    239   int *done = cls;
    240   struct MHD_Response *response;
    241   enum MHD_Result ret;
    242   static size_t processed;
    243 
    244   if (NULL == cls)
    245     mhdErrorExitDesc ("cls parameter is NULL");
    246 
    247   if (0 != strcmp (version, oneone ?
    248                    MHD_HTTP_VERSION_1_1 : MHD_HTTP_VERSION_1_0))
    249     mhdErrorExitDesc ("Unexpected HTTP version");
    250 
    251   if (NULL == url)
    252     mhdErrorExitDesc ("url parameter is NULL");
    253 
    254   if (NULL == upload_data_size)
    255     mhdErrorExitDesc ("'upload_data_size' pointer is NULL");
    256 
    257   if (0 != strcmp ("PUT", method))
    258     mhdErrorExitDesc ("Unexpected request method");   /* unexpected method */
    259 
    260   if ((*done) == 0)
    261   {
    262     size_t *pproc;
    263     if (NULL == *req_cls)
    264     {
    265       processed = 0;
    266       /* Safe as long as only one parallel request served. */
    267       *req_cls = &processed;
    268     }
    269     pproc = (size_t *) *req_cls;
    270 
    271     if (0 == *upload_data_size)
    272       return MHD_YES;   /* No data to process. */
    273 
    274     if (*pproc + *upload_data_size > PUT_SIZE)
    275       mhdErrorExitDesc ("Incoming data larger than expected");
    276 
    277     if ( (! incr_read) && (*upload_data_size != PUT_SIZE) )
    278       return MHD_YES;   /* Wait until whole request is received. */
    279 
    280     if (0 != memcmp (upload_data, put_buffer + (*pproc), *upload_data_size))
    281       mhdErrorExitDesc ("Incoming data does not match sent data");
    282 
    283     *pproc += *upload_data_size;
    284     *upload_data_size = 0;   /* Current block of data is fully processed. */
    285 
    286     if (PUT_SIZE == *pproc)
    287       *done = 1;   /* Whole request is processed. */
    288     return MHD_YES;
    289   }
    290   response =
    291     MHD_create_response_from_buffer_copy (strlen (url),
    292                                           (const void *) url);
    293   if (NULL == response)
    294     mhdErrorExitDesc ("Failed to create response");
    295   ret = MHD_queue_response (connection, MHD_HTTP_OK, response);
    296   MHD_destroy_response (response);
    297   return ret;
    298 }
    299 
    300 
    301 static unsigned int
    302 testPutInternalThread (unsigned int add_flag)
    303 {
    304   struct MHD_Daemon *d;
    305   CURL *c;
    306   struct CBC cbc;
    307   size_t pos = 0;
    308   int done_flag = 0;
    309   CURLcode errornum;
    310   char buf[2048];
    311   uint16_t port;
    312 
    313   if (MHD_NO != MHD_is_feature_supported (MHD_FEATURE_AUTODETECT_BIND_PORT))
    314     port = 0;
    315   else
    316   {
    317     port = 1270;
    318     if (oneone)
    319       port += 10;
    320     if (incr_read)
    321       port += 20;
    322   }
    323 
    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                         | add_flag,
    329                         port,
    330                         NULL, NULL, &ahc_echo, &done_flag,
    331                         MHD_OPTION_CONNECTION_MEMORY_LIMIT,
    332                         (size_t) (incr_read ? 1024 : (PUT_SIZE * 4 / 3)),
    333                         MHD_OPTION_END);
    334   if (d == NULL)
    335     mhdErrorExit ();
    336   if (0 == port)
    337   {
    338     const union MHD_DaemonInfo *dinfo;
    339     dinfo = MHD_get_daemon_info (d, MHD_DAEMON_INFO_BIND_PORT);
    340     if ((NULL == dinfo) || (0 == dinfo->port) )
    341       mhdErrorExit ();
    342     port = dinfo->port;
    343   }
    344 
    345   c = curl_easy_init ();
    346   if (NULL == c)
    347   {
    348     fprintf (stderr, "curl_easy_init() failed.\n");
    349     externalErrorExit ();
    350   }
    351   if ((CURLE_OK != curl_easy_setopt (c, CURLOPT_NOSIGNAL, 1L)) ||
    352       (CURLE_OK != curl_easy_setopt (c, CURLOPT_URL,
    353                                      "http://127.0.0.1/hello_world")) ||
    354       (CURLE_OK != curl_easy_setopt (c, CURLOPT_PORT, (long) port)) ||
    355       (CURLE_OK != curl_easy_setopt (c, CURLOPT_WRITEFUNCTION,
    356                                      &copyBuffer)) ||
    357       (CURLE_OK != curl_easy_setopt (c, CURLOPT_WRITEDATA, &cbc)) ||
    358       (CURLE_OK != curl_easy_setopt (c, CURLOPT_READFUNCTION,
    359                                      &putBuffer)) ||
    360       (CURLE_OK != curl_easy_setopt (c, CURLOPT_READDATA, &pos)) ||
    361       (CURLE_OK != curl_easy_setopt (c, CURLOPT_UPLOAD, 1L)) ||
    362       (CURLE_OK != curl_easy_setopt (c, CURLOPT_INFILESIZE, (long) PUT_SIZE)) ||
    363       (CURLE_OK != curl_easy_setopt (c, CURLOPT_FAILONERROR, 1L)) ||
    364       (CURLE_OK != curl_easy_setopt (c, CURLOPT_ERRORBUFFER, libcurl_errbuf)) ||
    365       (CURLE_OK != curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT,
    366                                      (long) 150)) ||
    367       (CURLE_OK != curl_easy_setopt (c, CURLOPT_TIMEOUT,
    368                                      (long) 150)) ||
    369       (CURLE_OK != curl_easy_setopt (c, CURLOPT_HTTP_VERSION,
    370                                      (oneone) ?
    371                                      CURL_HTTP_VERSION_1_1 :
    372                                      CURL_HTTP_VERSION_1_0)))
    373 
    374   {
    375     fprintf (stderr, "curl_easy_setopt() failed.\n");
    376     externalErrorExit ();
    377   }
    378 
    379   if (CURLE_OK != (errornum = curl_easy_perform (c)))
    380   {
    381     fprintf (stderr,
    382              "curl_easy_perform failed: `%s'\n",
    383              curl_easy_strerror (errornum));
    384     curl_easy_cleanup (c);
    385     MHD_stop_daemon (d);
    386     return 2;
    387   }
    388   curl_easy_cleanup (c);
    389   MHD_stop_daemon (d);
    390   if (cbc.pos != strlen ("/hello_world"))
    391   {
    392     fprintf (stderr, "Got %u bytes ('%.*s'), expected %u bytes. ",
    393              (unsigned) cbc.pos, (int) cbc.pos, cbc.buf,
    394              (unsigned) strlen ("/hello_world"));
    395     mhdErrorExitDesc ("Wrong returned data length");
    396   }
    397   if (0 != strncmp ("/hello_world", cbc.buf, strlen ("/hello_world")))
    398   {
    399     fprintf (stderr, "Got invalid response '%.*s'. ", (int) cbc.pos, cbc.buf);
    400     mhdErrorExitDesc ("Wrong returned data length");
    401   }
    402   return 0;
    403 }
    404 
    405 
    406 static unsigned int
    407 testPutThreadPerConn (unsigned int add_flag)
    408 {
    409   struct MHD_Daemon *d;
    410   CURL *c;
    411   struct CBC cbc;
    412   size_t pos = 0;
    413   int done_flag = 0;
    414   CURLcode errornum;
    415   char buf[2048];
    416   uint16_t port;
    417 
    418   if (MHD_NO != MHD_is_feature_supported (MHD_FEATURE_AUTODETECT_BIND_PORT))
    419     port = 0;
    420   else
    421   {
    422     port = 1271;
    423     if (oneone)
    424       port += 10;
    425     if (incr_read)
    426       port += 20;
    427   }
    428 
    429   cbc.buf = buf;
    430   cbc.size = 2048;
    431   cbc.pos = 0;
    432   d = MHD_start_daemon (MHD_USE_THREAD_PER_CONNECTION
    433                         | MHD_USE_INTERNAL_POLLING_THREAD
    434                         | MHD_USE_ERROR_LOG | add_flag,
    435                         port,
    436                         NULL, NULL, &ahc_echo, &done_flag,
    437                         MHD_OPTION_CONNECTION_MEMORY_LIMIT,
    438                         (size_t) (incr_read ? 1024 : (PUT_SIZE * 4)),
    439                         MHD_OPTION_END);
    440   if (d == NULL)
    441     mhdErrorExit ();
    442   if (0 == port)
    443   {
    444     const union MHD_DaemonInfo *dinfo;
    445     dinfo = MHD_get_daemon_info (d, MHD_DAEMON_INFO_BIND_PORT);
    446     if ((NULL == dinfo) || (0 == dinfo->port) )
    447       mhdErrorExit ();
    448     port = dinfo->port;
    449   }
    450 
    451   c = curl_easy_init ();
    452   if (NULL == c)
    453   {
    454     fprintf (stderr, "curl_easy_init() failed.\n");
    455     externalErrorExit ();
    456   }
    457   if ((CURLE_OK != curl_easy_setopt (c, CURLOPT_NOSIGNAL, 1L)) ||
    458       (CURLE_OK != curl_easy_setopt (c, CURLOPT_URL,
    459                                      "http://127.0.0.1/hello_world")) ||
    460       (CURLE_OK != curl_easy_setopt (c, CURLOPT_PORT, (long) port)) ||
    461       (CURLE_OK != curl_easy_setopt (c, CURLOPT_WRITEFUNCTION,
    462                                      &copyBuffer)) ||
    463       (CURLE_OK != curl_easy_setopt (c, CURLOPT_WRITEDATA, &cbc)) ||
    464       (CURLE_OK != curl_easy_setopt (c, CURLOPT_READFUNCTION,
    465                                      &putBuffer)) ||
    466       (CURLE_OK != curl_easy_setopt (c, CURLOPT_READDATA, &pos)) ||
    467       (CURLE_OK != curl_easy_setopt (c, CURLOPT_UPLOAD, 1L)) ||
    468       (CURLE_OK != curl_easy_setopt (c, CURLOPT_INFILESIZE, (long) PUT_SIZE)) ||
    469       (CURLE_OK != curl_easy_setopt (c, CURLOPT_FAILONERROR, 1L)) ||
    470       (CURLE_OK != curl_easy_setopt (c, CURLOPT_ERRORBUFFER, libcurl_errbuf)) ||
    471       (CURLE_OK != curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT,
    472                                      (long) 150)) ||
    473       (CURLE_OK != curl_easy_setopt (c, CURLOPT_TIMEOUT,
    474                                      (long) 150)) ||
    475       (CURLE_OK != curl_easy_setopt (c, CURLOPT_HTTP_VERSION,
    476                                      (oneone) ?
    477                                      CURL_HTTP_VERSION_1_1 :
    478                                      CURL_HTTP_VERSION_1_0)))
    479   {
    480     fprintf (stderr, "curl_easy_setopt() failed.\n");
    481     externalErrorExit ();
    482   }
    483 
    484   if (CURLE_OK != (errornum = curl_easy_perform (c)))
    485   {
    486     fprintf (stderr,
    487              "curl_easy_perform failed: `%s'\n",
    488              curl_easy_strerror (errornum));
    489     curl_easy_cleanup (c);
    490     MHD_stop_daemon (d);
    491     return 32;
    492   }
    493   curl_easy_cleanup (c);
    494   MHD_stop_daemon (d);
    495   if (cbc.pos != strlen ("/hello_world"))
    496   {
    497     fprintf (stderr, "Got %u bytes ('%.*s'), expected %u bytes. ",
    498              (unsigned) cbc.pos, (int) cbc.pos, cbc.buf,
    499              (unsigned) strlen ("/hello_world"));
    500     mhdErrorExitDesc ("Wrong returned data length");
    501   }
    502   if (0 != strncmp ("/hello_world", cbc.buf, strlen ("/hello_world")))
    503   {
    504     fprintf (stderr, "Got invalid response '%.*s'. ", (int) cbc.pos, cbc.buf);
    505     mhdErrorExitDesc ("Wrong returned data length");
    506   }
    507   return 0;
    508 }
    509 
    510 
    511 static unsigned int
    512 testPutThreadPool (unsigned int add_flag)
    513 {
    514   struct MHD_Daemon *d;
    515   CURL *c;
    516   struct CBC cbc;
    517   size_t pos = 0;
    518   int done_flag = 0;
    519   CURLcode errornum;
    520   char buf[2048];
    521   uint16_t port;
    522 
    523   if (MHD_NO != MHD_is_feature_supported (MHD_FEATURE_AUTODETECT_BIND_PORT))
    524     port = 0;
    525   else
    526   {
    527     port = 1272;
    528     if (oneone)
    529       port += 10;
    530     if (incr_read)
    531       port += 20;
    532   }
    533 
    534   cbc.buf = buf;
    535   cbc.size = 2048;
    536   cbc.pos = 0;
    537   d = MHD_start_daemon (MHD_USE_INTERNAL_POLLING_THREAD | MHD_USE_ERROR_LOG
    538                         | add_flag,
    539                         port,
    540                         NULL, NULL, &ahc_echo, &done_flag,
    541                         MHD_OPTION_THREAD_POOL_SIZE, MHD_CPU_COUNT,
    542                         MHD_OPTION_CONNECTION_MEMORY_LIMIT,
    543                         (size_t) (incr_read ? 1024 : (PUT_SIZE * 4)),
    544                         MHD_OPTION_END);
    545   if (d == NULL)
    546     mhdErrorExit ();
    547   if (0 == port)
    548   {
    549     const union MHD_DaemonInfo *dinfo;
    550     dinfo = MHD_get_daemon_info (d, MHD_DAEMON_INFO_BIND_PORT);
    551     if ((NULL == dinfo) || (0 == dinfo->port) )
    552       mhdErrorExit ();
    553     port = dinfo->port;
    554   }
    555 
    556   c = curl_easy_init ();
    557   if (NULL == c)
    558   {
    559     fprintf (stderr, "curl_easy_init() failed.\n");
    560     externalErrorExit ();
    561   }
    562   if ((CURLE_OK != curl_easy_setopt (c, CURLOPT_NOSIGNAL, 1L)) ||
    563       (CURLE_OK != curl_easy_setopt (c, CURLOPT_URL,
    564                                      "http://127.0.0.1/hello_world")) ||
    565       (CURLE_OK != curl_easy_setopt (c, CURLOPT_PORT, (long) port)) ||
    566       (CURLE_OK != curl_easy_setopt (c, CURLOPT_WRITEFUNCTION,
    567                                      &copyBuffer)) ||
    568       (CURLE_OK != curl_easy_setopt (c, CURLOPT_WRITEDATA, &cbc)) ||
    569       (CURLE_OK != curl_easy_setopt (c, CURLOPT_READFUNCTION,
    570                                      &putBuffer)) ||
    571       (CURLE_OK != curl_easy_setopt (c, CURLOPT_READDATA, &pos)) ||
    572       (CURLE_OK != curl_easy_setopt (c, CURLOPT_UPLOAD, 1L)) ||
    573       (CURLE_OK != curl_easy_setopt (c, CURLOPT_INFILESIZE, (long) PUT_SIZE)) ||
    574       (CURLE_OK != curl_easy_setopt (c, CURLOPT_FAILONERROR, 1L)) ||
    575       (CURLE_OK != curl_easy_setopt (c, CURLOPT_ERRORBUFFER, libcurl_errbuf)) ||
    576       (CURLE_OK != curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT,
    577                                      (long) 150)) ||
    578       (CURLE_OK != curl_easy_setopt (c, CURLOPT_TIMEOUT,
    579                                      (long) 150)) ||
    580       (CURLE_OK != curl_easy_setopt (c, CURLOPT_HTTP_VERSION,
    581                                      (oneone) ?
    582                                      CURL_HTTP_VERSION_1_1 :
    583                                      CURL_HTTP_VERSION_1_0)))
    584   {
    585     fprintf (stderr, "curl_easy_setopt() failed.\n");
    586     externalErrorExit ();
    587   }
    588   if (CURLE_OK != (errornum = curl_easy_perform (c)))
    589   {
    590     fprintf (stderr,
    591              "curl_easy_perform failed: `%s'\n",
    592              curl_easy_strerror (errornum));
    593     curl_easy_cleanup (c);
    594     MHD_stop_daemon (d);
    595     return 32;
    596   }
    597   curl_easy_cleanup (c);
    598   MHD_stop_daemon (d);
    599   if (cbc.pos != strlen ("/hello_world"))
    600   {
    601     fprintf (stderr, "Got %u bytes ('%.*s'), expected %u bytes. ",
    602              (unsigned) cbc.pos, (int) cbc.pos, cbc.buf,
    603              (unsigned) strlen ("/hello_world"));
    604     mhdErrorExitDesc ("Wrong returned data length");
    605   }
    606   if (0 != strncmp ("/hello_world", cbc.buf, strlen ("/hello_world")))
    607   {
    608     fprintf (stderr, "Got invalid response '%.*s'. ", (int) cbc.pos, cbc.buf);
    609     mhdErrorExitDesc ("Wrong returned data length");
    610   }
    611   return 0;
    612 }
    613 
    614 
    615 static unsigned int
    616 testPutExternal (void)
    617 {
    618   struct MHD_Daemon *d;
    619   CURL *c;
    620   struct CBC cbc;
    621   CURLM *multi;
    622   CURLMcode mret;
    623   fd_set rs;
    624   fd_set ws;
    625   fd_set es;
    626   int running;
    627   struct CURLMsg *msg;
    628   time_t start;
    629   struct timeval tv;
    630   size_t pos = 0;
    631   int done_flag = 0;
    632   char buf[2048];
    633   uint16_t port;
    634 
    635   if (MHD_NO != MHD_is_feature_supported (MHD_FEATURE_AUTODETECT_BIND_PORT))
    636     port = 0;
    637   else
    638   {
    639     port = 1273;
    640     if (oneone)
    641       port += 10;
    642     if (incr_read)
    643       port += 20;
    644   }
    645 
    646   cbc.buf = buf;
    647   cbc.size = 2048;
    648   cbc.pos = 0;
    649   multi = NULL;
    650   d = MHD_start_daemon (MHD_USE_ERROR_LOG | MHD_USE_NO_THREAD_SAFETY,
    651                         port,
    652                         NULL, NULL, &ahc_echo, &done_flag,
    653                         MHD_OPTION_CONNECTION_MEMORY_LIMIT,
    654                         (size_t) (incr_read ? 1024 : (PUT_SIZE * 4)),
    655                         MHD_OPTION_APP_FD_SETSIZE, (int) FD_SETSIZE,
    656                         MHD_OPTION_END);
    657   if (d == NULL)
    658     mhdErrorExit ();
    659   if (0 == port)
    660   {
    661     const union MHD_DaemonInfo *dinfo;
    662     dinfo = MHD_get_daemon_info (d, MHD_DAEMON_INFO_BIND_PORT);
    663     if ((NULL == dinfo) || (0 == dinfo->port) )
    664       mhdErrorExit ();
    665     port = dinfo->port;
    666   }
    667 
    668   c = curl_easy_init ();
    669   if (NULL == c)
    670   {
    671     fprintf (stderr, "curl_easy_init() failed.\n");
    672     externalErrorExit ();
    673   }
    674   if ((CURLE_OK != curl_easy_setopt (c, CURLOPT_NOSIGNAL, 1L)) ||
    675       (CURLE_OK != curl_easy_setopt (c, CURLOPT_URL,
    676                                      "http://127.0.0.1/hello_world")) ||
    677       (CURLE_OK != curl_easy_setopt (c, CURLOPT_PORT, (long) port)) ||
    678       (CURLE_OK != curl_easy_setopt (c, CURLOPT_WRITEFUNCTION,
    679                                      &copyBuffer)) ||
    680       (CURLE_OK != curl_easy_setopt (c, CURLOPT_WRITEDATA, &cbc)) ||
    681       (CURLE_OK != curl_easy_setopt (c, CURLOPT_READFUNCTION,
    682                                      &putBuffer)) ||
    683       (CURLE_OK != curl_easy_setopt (c, CURLOPT_READDATA, &pos)) ||
    684       (CURLE_OK != curl_easy_setopt (c, CURLOPT_UPLOAD, 1L)) ||
    685       (CURLE_OK != curl_easy_setopt (c, CURLOPT_INFILESIZE, (long) PUT_SIZE)) ||
    686       (CURLE_OK != curl_easy_setopt (c, CURLOPT_FAILONERROR, 1L)) ||
    687       (CURLE_OK != curl_easy_setopt (c, CURLOPT_ERRORBUFFER, libcurl_errbuf)) ||
    688       (CURLE_OK != curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT,
    689                                      (long) 150)) ||
    690       (CURLE_OK != curl_easy_setopt (c, CURLOPT_TIMEOUT,
    691                                      (long) 150)) ||
    692       (CURLE_OK != curl_easy_setopt (c, CURLOPT_HTTP_VERSION,
    693                                      (oneone) ?
    694                                      CURL_HTTP_VERSION_1_1 :
    695                                      CURL_HTTP_VERSION_1_0)))
    696   {
    697     fprintf (stderr, "curl_easy_setopt() failed.\n");
    698     externalErrorExit ();
    699   }
    700 
    701   multi = curl_multi_init ();
    702   if (multi == NULL)
    703     libcurlErrorExit ();
    704   mret = curl_multi_add_handle (multi, c);
    705   if (mret != CURLM_OK)
    706     libcurlErrorExit ();
    707 
    708   start = time (NULL);
    709   while ((time (NULL) - start < 45) && (multi != NULL))
    710   {
    711     MHD_socket maxMHDsock;
    712     int maxcurlsock;
    713     maxMHDsock = MHD_INVALID_SOCKET;
    714     maxcurlsock = -1;
    715     FD_ZERO (&rs);
    716     FD_ZERO (&ws);
    717     FD_ZERO (&es);
    718     mret = curl_multi_perform (multi, &running);
    719     if ((CURLM_OK != mret) && (CURLM_CALL_MULTI_PERFORM != mret))
    720     {
    721       fprintf (stderr, "curl_multi_perform() failed. Error: '%s'. ",
    722                curl_multi_strerror (mret));
    723       libcurlErrorExit ();
    724     }
    725     if (CURLM_OK != curl_multi_fdset (multi, &rs, &ws, &es, &maxcurlsock))
    726       libcurlErrorExitDesc ("curl_multi_fdset() failed");
    727     if (MHD_YES != MHD_get_fdset (d, &rs, &ws, &es, &maxMHDsock))
    728       mhdErrorExit ();
    729 
    730     tv.tv_sec = 0;
    731     tv.tv_usec = 1000;
    732 #ifndef MHD_WINSOCK_SOCKETS
    733     if (maxMHDsock > maxcurlsock)
    734       maxcurlsock = maxMHDsock;
    735 #endif /* MHD_WINSOCK_SOCKETS */
    736     if (-1 == select (maxcurlsock + 1, &rs, &ws, &es, &tv))
    737     {
    738 #ifdef MHD_POSIX_SOCKETS
    739       if (EINTR != errno)
    740         externalErrorExitDesc ("Unexpected select() error");
    741 #else
    742       if ((WSAEINVAL != WSAGetLastError ()) ||
    743           (0 != rs.fd_count) || (0 != ws.fd_count) || (0 != es.fd_count) )
    744         externalErrorExitDesc ("Unexpected select() error");
    745       Sleep ((DWORD) (tv.tv_sec * 1000 + tv.tv_usec / 1000));
    746 #endif
    747     }
    748 
    749     mret = curl_multi_perform (multi, &running);
    750     if ((CURLM_OK != mret) && (CURLM_CALL_MULTI_PERFORM != mret))
    751     {
    752       fprintf (stderr, "curl_multi_perform() failed. Error: '%s'. ",
    753                curl_multi_strerror (mret));
    754       libcurlErrorExit ();
    755     }
    756     if (0 == running)
    757     {
    758       int pending;
    759       int curl_fine = 0;
    760       while (NULL != (msg = curl_multi_info_read (multi, &pending)))
    761       {
    762         if (msg->msg == CURLMSG_DONE)
    763         {
    764           if (msg->data.result == CURLE_OK)
    765             curl_fine = 1;
    766           else
    767           {
    768             fprintf (stderr,
    769                      "curl_multi_perform() failed: '%s' ",
    770                      curl_easy_strerror (msg->data.result));
    771             libcurlErrorExit ();
    772           }
    773         }
    774       }
    775       if (! curl_fine)
    776       {
    777         fprintf (stderr, "libcurl haven't returned OK code ");
    778         mhdErrorExit ();
    779       }
    780       curl_multi_remove_handle (multi, c);
    781       curl_multi_cleanup (multi);
    782       curl_easy_cleanup (c);
    783       c = NULL;
    784       multi = NULL;
    785     }
    786     MHD_run (d);
    787   }
    788   if (multi != NULL)
    789     mhdErrorExitDesc ("Request has been aborted by timeout");
    790 
    791   MHD_stop_daemon (d);
    792   if (cbc.pos != strlen ("/hello_world"))
    793   {
    794     fprintf (stderr, "Got %u bytes ('%.*s'), expected %u bytes. ",
    795              (unsigned) cbc.pos, (int) cbc.pos, cbc.buf,
    796              (unsigned) strlen ("/hello_world"));
    797     mhdErrorExitDesc ("Wrong returned data length");
    798   }
    799   if (0 != strncmp ("/hello_world", cbc.buf, strlen ("/hello_world")))
    800   {
    801     fprintf (stderr, "Got invalid response '%.*s'. ", (int) cbc.pos, cbc.buf);
    802     mhdErrorExitDesc ("Wrong returned data length");
    803   }
    804   return 0;
    805 }
    806 
    807 
    808 int
    809 main (int argc, char *const *argv)
    810 {
    811   unsigned int errorCount = 0;
    812   unsigned int lastErr;
    813 
    814   oneone = has_in_name (argv[0], "11");
    815   incr_read = has_in_name (argv[0], "_inc");
    816   verbose = has_param (argc, argv, "-v");
    817   if (0 != curl_global_init (CURL_GLOBAL_WIN32))
    818     return 99;
    819   put_buffer = alloc_init (PUT_SIZE);
    820   if (NULL == put_buffer)
    821     return 99;
    822   lastErr = testPutExternal ();
    823   if (verbose && (0 != lastErr))
    824     fprintf (stderr, "Error during testing with external select().\n");
    825   errorCount += lastErr;
    826   if (MHD_YES == MHD_is_feature_supported (MHD_FEATURE_THREADS))
    827   {
    828     lastErr = testPutInternalThread (0);
    829     if (verbose && (0 != lastErr) )
    830       fprintf (stderr,
    831                "Error during testing with internal thread with select().\n");
    832     errorCount += lastErr;
    833     lastErr = testPutThreadPerConn (0);
    834     if (verbose && (0 != lastErr) )
    835       fprintf (stderr,
    836                "Error during testing with internal thread per connection with select().\n");
    837     errorCount += lastErr;
    838     lastErr = testPutThreadPool (0);
    839     if (verbose && (0 != lastErr) )
    840       fprintf (stderr,
    841                "Error during testing with thread pool per connection with select().\n");
    842     errorCount += lastErr;
    843     if (MHD_is_feature_supported (MHD_FEATURE_POLL))
    844     {
    845       lastErr = testPutInternalThread (MHD_USE_POLL);
    846       if (verbose && (0 != lastErr) )
    847         fprintf (stderr,
    848                  "Error during testing with internal thread with poll().\n");
    849       errorCount += lastErr;
    850       lastErr = testPutThreadPerConn (MHD_USE_POLL);
    851       if (verbose && (0 != lastErr) )
    852         fprintf (stderr,
    853                  "Error during testing with internal thread per connection with poll().\n");
    854       errorCount += lastErr;
    855       lastErr = testPutThreadPool (MHD_USE_POLL);
    856       if (verbose && (0 != lastErr) )
    857         fprintf (stderr,
    858                  "Error during testing with thread pool per connection with poll().\n");
    859       errorCount += lastErr;
    860     }
    861     if (MHD_is_feature_supported (MHD_FEATURE_EPOLL))
    862     {
    863       lastErr = testPutInternalThread (MHD_USE_EPOLL);
    864       if (verbose && (0 != lastErr) )
    865         fprintf (stderr,
    866                  "Error during testing with internal thread with epoll.\n");
    867       errorCount += lastErr;
    868       lastErr = testPutThreadPool (MHD_USE_EPOLL);
    869       if (verbose && (0 != lastErr) )
    870         fprintf (stderr,
    871                  "Error during testing with thread pool per connection with epoll.\n");
    872       errorCount += lastErr;
    873     }
    874   }
    875   free (put_buffer);
    876   if (errorCount != 0)
    877     fprintf (stderr, "Error (code: %u)\n", errorCount);
    878   else if (verbose)
    879     printf ("All checks passed successfully.\n");
    880   curl_global_cleanup ();
    881   return (errorCount == 0) ? 0 : 1;
    882 }