libmicrohttpd

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

test_post.c (23746B)


      1 /*
      2      This file is part of libmicrohttpd
      3      Copyright (C) 2007 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_post.c
     24  * @brief  Testcase for libmicrohttpd POST operations using URL-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 #include <errno.h>
     37 
     38 #ifndef WINDOWS
     39 #include <unistd.h>
     40 #endif
     41 
     42 #ifdef _WIN32
     43 #ifndef WIN32_LEAN_AND_MEAN
     44 #define WIN32_LEAN_AND_MEAN 1
     45 #endif /* !WIN32_LEAN_AND_MEAN */
     46 #include <windows.h>
     47 #endif
     48 
     49 #include "mhd_has_in_name.h"
     50 
     51 /* Turn any MHD_PANIC() or failing mhd_assert() reached from this
     52    test into a marked, classifiable test error (TESTING.md, P5). */
     53 #include "mhd_panic_tripwire.h"
     54 
     55 #if defined(MHD_CPU_COUNT) && (MHD_CPU_COUNT + 0) < 2
     56 #undef MHD_CPU_COUNT
     57 #endif
     58 #if ! defined(MHD_CPU_COUNT)
     59 #define MHD_CPU_COUNT 2
     60 #endif
     61 
     62 #define POST_DATA "name=daniel&project=curl"
     63 
     64 static int oneone;
     65 
     66 struct CBC
     67 {
     68   char *buf;
     69   size_t pos;
     70   size_t size;
     71 };
     72 
     73 
     74 static void
     75 completed_cb (void *cls,
     76               struct MHD_Connection *connection,
     77               void **req_cls,
     78               enum MHD_RequestTerminationCode toe)
     79 {
     80   struct MHD_PostProcessor *pp = *req_cls;
     81   (void) cls; (void) connection; (void) toe; /* Unused. Silent compiler warning. */
     82 
     83   if (NULL != pp)
     84     MHD_destroy_post_processor (pp);
     85   *req_cls = NULL;
     86 }
     87 
     88 
     89 static size_t
     90 copyBuffer (void *ptr, size_t size, size_t nmemb, void *ctx)
     91 {
     92   struct CBC *cbc = ctx;
     93 
     94   if (cbc->pos + size * nmemb > cbc->size)
     95     return 0;                   /* overflow */
     96   memcpy (&cbc->buf[cbc->pos], ptr, size * nmemb);
     97   cbc->pos += size * nmemb;
     98   return size * nmemb;
     99 }
    100 
    101 
    102 /**
    103  * Note that this post_iterator is not perfect
    104  * in that it fails to support incremental processing.
    105  * (to be fixed in the future)
    106  */
    107 static enum MHD_Result
    108 post_iterator (void *cls,
    109                enum MHD_ValueKind kind,
    110                const char *key,
    111                const char *filename,
    112                const char *content_type,
    113                const char *transfer_encoding,
    114                const char *value, uint64_t off, size_t size)
    115 {
    116   int *eok = cls;
    117   (void) kind; (void) filename; (void) content_type; /* Unused. Silent compiler warning. */
    118   (void) transfer_encoding; (void) off;            /* Unused. Silent compiler warning. */
    119 
    120   if ((0 == strcmp (key, "name")) &&
    121       (size == strlen ("daniel")) && (0 == strncmp (value, "daniel", size)))
    122     (*eok) |= 1;
    123   if ((0 == strcmp (key, "project")) &&
    124       (size == strlen ("curl")) && (0 == strncmp (value, "curl", size)))
    125     (*eok) |= 2;
    126   return MHD_YES;
    127 }
    128 
    129 
    130 static enum MHD_Result
    131 ahc_echo (void *cls,
    132           struct MHD_Connection *connection,
    133           const char *url,
    134           const char *method,
    135           const char *version,
    136           const char *upload_data, size_t *upload_data_size,
    137           void **req_cls)
    138 {
    139   static int eok;
    140   struct MHD_Response *response;
    141   struct MHD_PostProcessor *pp;
    142   enum MHD_Result ret;
    143   (void) cls; (void) version;      /* Unused. Silent compiler warning. */
    144 
    145   if (0 != strcmp (MHD_HTTP_METHOD_POST, method))
    146   {
    147     fprintf (stderr, "METHOD: %s\n", method);
    148     return MHD_NO;              /* unexpected method */
    149   }
    150   pp = *req_cls;
    151   if (pp == NULL)
    152   {
    153     eok = 0;
    154     pp = MHD_create_post_processor (connection, 1024, &post_iterator, &eok);
    155     *req_cls = pp;
    156   }
    157   MHD_post_process (pp, upload_data, *upload_data_size);
    158   if ((eok == 3) && (0 == *upload_data_size))
    159   {
    160     response = MHD_create_response_from_buffer_copy (strlen (url),
    161                                                      (const void *) url);
    162     ret = MHD_queue_response (connection, MHD_HTTP_OK, response);
    163     MHD_destroy_response (response);
    164     MHD_destroy_post_processor (pp);
    165     *req_cls = NULL;
    166     return ret;
    167   }
    168   *upload_data_size = 0;
    169   return MHD_YES;
    170 }
    171 
    172 
    173 static unsigned int
    174 testInternalPost (void)
    175 {
    176   struct MHD_Daemon *d;
    177   CURL *c;
    178   char buf[2048];
    179   struct CBC cbc;
    180   CURLcode errornum;
    181   uint16_t port;
    182 
    183   if (MHD_NO != MHD_is_feature_supported (MHD_FEATURE_AUTODETECT_BIND_PORT))
    184     port = 0;
    185   else
    186   {
    187     port = 1370;
    188     if (oneone)
    189       port += 10;
    190   }
    191 
    192   cbc.buf = buf;
    193   cbc.size = 2048;
    194   cbc.pos = 0;
    195   d = MHD_start_daemon (MHD_USE_INTERNAL_POLLING_THREAD | MHD_USE_ERROR_LOG,
    196                         port, NULL, NULL, &ahc_echo, NULL,
    197                         MHD_OPTION_NOTIFY_COMPLETED, &completed_cb, NULL,
    198                         MHD_OPTION_END);
    199   if (d == NULL)
    200     return 1;
    201   if (0 == port)
    202   {
    203     const union MHD_DaemonInfo *dinfo;
    204     dinfo = MHD_get_daemon_info (d, MHD_DAEMON_INFO_BIND_PORT);
    205     if ((NULL == dinfo) || (0 == dinfo->port) )
    206     {
    207       MHD_stop_daemon (d); return 32;
    208     }
    209     port = dinfo->port;
    210   }
    211   c = curl_easy_init ();
    212   curl_easy_setopt (c, CURLOPT_URL, "http://127.0.0.1/hello_world");
    213   curl_easy_setopt (c, CURLOPT_PORT, (long) port);
    214   curl_easy_setopt (c, CURLOPT_WRITEFUNCTION, &copyBuffer);
    215   curl_easy_setopt (c, CURLOPT_WRITEDATA, &cbc);
    216   curl_easy_setopt (c, CURLOPT_POSTFIELDS, POST_DATA);
    217   curl_easy_setopt (c, CURLOPT_POSTFIELDSIZE, strlen (POST_DATA));
    218   curl_easy_setopt (c, CURLOPT_POST, 1L);
    219   curl_easy_setopt (c, CURLOPT_FAILONERROR, 1L);
    220   curl_easy_setopt (c, CURLOPT_TIMEOUT, 150L);
    221   if (oneone)
    222     curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
    223   else
    224     curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0);
    225   curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT, 150L);
    226   /* NOTE: use of CONNECTTIMEOUT without also
    227    *   setting NOSIGNAL results in really weird
    228    *   crashes on my system! */
    229   curl_easy_setopt (c, CURLOPT_NOSIGNAL, 1L);
    230   if (CURLE_OK != (errornum = curl_easy_perform (c)))
    231   {
    232     fprintf (stderr,
    233              "curl_easy_perform failed: `%s'\n",
    234              curl_easy_strerror (errornum));
    235     curl_easy_cleanup (c);
    236     MHD_stop_daemon (d);
    237     return 2;
    238   }
    239   curl_easy_cleanup (c);
    240   MHD_stop_daemon (d);
    241   if (cbc.pos != strlen ("/hello_world"))
    242     return 4;
    243   if (0 != strncmp ("/hello_world", cbc.buf, strlen ("/hello_world")))
    244     return 8;
    245   return 0;
    246 }
    247 
    248 
    249 static unsigned int
    250 testMultithreadedPost (void)
    251 {
    252   struct MHD_Daemon *d;
    253   CURL *c;
    254   char buf[2048];
    255   struct CBC cbc;
    256   CURLcode errornum;
    257   uint16_t port;
    258 
    259   if (MHD_NO != MHD_is_feature_supported (MHD_FEATURE_AUTODETECT_BIND_PORT))
    260     port = 0;
    261   else
    262   {
    263     port = 1371;
    264     if (oneone)
    265       port += 10;
    266   }
    267 
    268   cbc.buf = buf;
    269   cbc.size = 2048;
    270   cbc.pos = 0;
    271   d = MHD_start_daemon (MHD_USE_THREAD_PER_CONNECTION
    272                         | MHD_USE_INTERNAL_POLLING_THREAD | MHD_USE_ERROR_LOG,
    273                         port, NULL, NULL, &ahc_echo, NULL,
    274                         MHD_OPTION_NOTIFY_COMPLETED, &completed_cb, NULL,
    275                         MHD_OPTION_END);
    276   if (d == NULL)
    277     return 16;
    278   if (0 == port)
    279   {
    280     const union MHD_DaemonInfo *dinfo;
    281     dinfo = MHD_get_daemon_info (d, MHD_DAEMON_INFO_BIND_PORT);
    282     if ((NULL == dinfo) || (0 == dinfo->port) )
    283     {
    284       MHD_stop_daemon (d); return 32;
    285     }
    286     port = dinfo->port;
    287   }
    288   c = curl_easy_init ();
    289   curl_easy_setopt (c, CURLOPT_URL, "http://127.0.0.1/hello_world");
    290   curl_easy_setopt (c, CURLOPT_PORT, (long) port);
    291   curl_easy_setopt (c, CURLOPT_WRITEFUNCTION, &copyBuffer);
    292   curl_easy_setopt (c, CURLOPT_WRITEDATA, &cbc);
    293   curl_easy_setopt (c, CURLOPT_POSTFIELDS, POST_DATA);
    294   curl_easy_setopt (c, CURLOPT_POSTFIELDSIZE, strlen (POST_DATA));
    295   curl_easy_setopt (c, CURLOPT_POST, 1L);
    296   curl_easy_setopt (c, CURLOPT_FAILONERROR, 1L);
    297   curl_easy_setopt (c, CURLOPT_TIMEOUT, 150L);
    298   if (oneone)
    299     curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
    300   else
    301     curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0);
    302   curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT, 150L);
    303   /* NOTE: use of CONNECTTIMEOUT without also
    304    *   setting NOSIGNAL results in really weird
    305    *   crashes on my system! */
    306   curl_easy_setopt (c, CURLOPT_NOSIGNAL, 1L);
    307   if (CURLE_OK != (errornum = curl_easy_perform (c)))
    308   {
    309     fprintf (stderr,
    310              "curl_easy_perform failed: `%s'\n",
    311              curl_easy_strerror (errornum));
    312     curl_easy_cleanup (c);
    313     MHD_stop_daemon (d);
    314     return 32;
    315   }
    316   curl_easy_cleanup (c);
    317   MHD_stop_daemon (d);
    318   if (cbc.pos != strlen ("/hello_world"))
    319     return 64;
    320   if (0 != strncmp ("/hello_world", cbc.buf, strlen ("/hello_world")))
    321     return 128;
    322   return 0;
    323 }
    324 
    325 
    326 static unsigned int
    327 testMultithreadedPoolPost (void)
    328 {
    329   struct MHD_Daemon *d;
    330   CURL *c;
    331   char buf[2048];
    332   struct CBC cbc;
    333   CURLcode errornum;
    334   uint16_t port;
    335 
    336   if (MHD_NO != MHD_is_feature_supported (MHD_FEATURE_AUTODETECT_BIND_PORT))
    337     port = 0;
    338   else
    339   {
    340     port = 1372;
    341     if (oneone)
    342       port += 10;
    343   }
    344 
    345   cbc.buf = buf;
    346   cbc.size = 2048;
    347   cbc.pos = 0;
    348   d = MHD_start_daemon (MHD_USE_INTERNAL_POLLING_THREAD | MHD_USE_ERROR_LOG,
    349                         port, NULL, NULL, &ahc_echo, NULL,
    350                         MHD_OPTION_THREAD_POOL_SIZE, MHD_CPU_COUNT,
    351                         MHD_OPTION_NOTIFY_COMPLETED, &completed_cb, NULL,
    352                         MHD_OPTION_END);
    353   if (d == NULL)
    354     return 16;
    355   if (0 == port)
    356   {
    357     const union MHD_DaemonInfo *dinfo;
    358     dinfo = MHD_get_daemon_info (d, MHD_DAEMON_INFO_BIND_PORT);
    359     if ((NULL == dinfo) || (0 == dinfo->port) )
    360     {
    361       MHD_stop_daemon (d); return 32;
    362     }
    363     port = dinfo->port;
    364   }
    365   c = curl_easy_init ();
    366   curl_easy_setopt (c, CURLOPT_URL, "http://127.0.0.1/hello_world");
    367   curl_easy_setopt (c, CURLOPT_PORT, (long) port);
    368   curl_easy_setopt (c, CURLOPT_WRITEFUNCTION, &copyBuffer);
    369   curl_easy_setopt (c, CURLOPT_WRITEDATA, &cbc);
    370   curl_easy_setopt (c, CURLOPT_POSTFIELDS, POST_DATA);
    371   curl_easy_setopt (c, CURLOPT_POSTFIELDSIZE, strlen (POST_DATA));
    372   curl_easy_setopt (c, CURLOPT_POST, 1L);
    373   curl_easy_setopt (c, CURLOPT_FAILONERROR, 1L);
    374   curl_easy_setopt (c, CURLOPT_TIMEOUT, 150L);
    375   if (oneone)
    376     curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
    377   else
    378     curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0);
    379   curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT, 150L);
    380   /* NOTE: use of CONNECTTIMEOUT without also
    381    *   setting NOSIGNAL results in really weird
    382    *   crashes on my system! */
    383   curl_easy_setopt (c, CURLOPT_NOSIGNAL, 1L);
    384   if (CURLE_OK != (errornum = curl_easy_perform (c)))
    385   {
    386     fprintf (stderr,
    387              "curl_easy_perform failed: `%s'\n",
    388              curl_easy_strerror (errornum));
    389     curl_easy_cleanup (c);
    390     MHD_stop_daemon (d);
    391     return 32;
    392   }
    393   curl_easy_cleanup (c);
    394   MHD_stop_daemon (d);
    395   if (cbc.pos != strlen ("/hello_world"))
    396     return 64;
    397   if (0 != strncmp ("/hello_world", cbc.buf, strlen ("/hello_world")))
    398     return 128;
    399   return 0;
    400 }
    401 
    402 
    403 static unsigned int
    404 testExternalPost (void)
    405 {
    406   struct MHD_Daemon *d;
    407   CURL *c;
    408   char buf[2048];
    409   struct CBC cbc;
    410   CURLM *multi;
    411   CURLMcode mret;
    412   fd_set rs;
    413   fd_set ws;
    414   fd_set es;
    415   MHD_socket maxsock;
    416 #ifdef MHD_WINSOCK_SOCKETS
    417   int maxposixs; /* Max socket number unused on W32 */
    418 #else  /* MHD_POSIX_SOCKETS */
    419 #define maxposixs maxsock
    420 #endif /* MHD_POSIX_SOCKETS */
    421   int running;
    422   struct CURLMsg *msg;
    423   time_t start;
    424   struct timeval tv;
    425   uint16_t port;
    426 
    427   if (MHD_NO != MHD_is_feature_supported (MHD_FEATURE_AUTODETECT_BIND_PORT))
    428     port = 0;
    429   else
    430   {
    431     port = 1373;
    432     if (oneone)
    433       port += 10;
    434   }
    435 
    436   multi = NULL;
    437   cbc.buf = buf;
    438   cbc.size = 2048;
    439   cbc.pos = 0;
    440   d = MHD_start_daemon (MHD_USE_ERROR_LOG | MHD_USE_NO_THREAD_SAFETY,
    441                         port, NULL, NULL, &ahc_echo, NULL,
    442                         MHD_OPTION_NOTIFY_COMPLETED, &completed_cb, NULL,
    443                         MHD_OPTION_APP_FD_SETSIZE, (int) FD_SETSIZE,
    444                         MHD_OPTION_END);
    445   if (d == NULL)
    446     return 256;
    447   if (0 == port)
    448   {
    449     const union MHD_DaemonInfo *dinfo;
    450     dinfo = MHD_get_daemon_info (d, MHD_DAEMON_INFO_BIND_PORT);
    451     if ((NULL == dinfo) || (0 == dinfo->port) )
    452     {
    453       MHD_stop_daemon (d); return 32;
    454     }
    455     port = dinfo->port;
    456   }
    457   c = curl_easy_init ();
    458   curl_easy_setopt (c, CURLOPT_URL, "http://127.0.0.1/hello_world");
    459   curl_easy_setopt (c, CURLOPT_PORT, (long) port);
    460   curl_easy_setopt (c, CURLOPT_WRITEFUNCTION, &copyBuffer);
    461   curl_easy_setopt (c, CURLOPT_WRITEDATA, &cbc);
    462   curl_easy_setopt (c, CURLOPT_POSTFIELDS, POST_DATA);
    463   curl_easy_setopt (c, CURLOPT_POSTFIELDSIZE, strlen (POST_DATA));
    464   curl_easy_setopt (c, CURLOPT_POST, 1L);
    465   curl_easy_setopt (c, CURLOPT_FAILONERROR, 1L);
    466   curl_easy_setopt (c, CURLOPT_TIMEOUT, 150L);
    467   if (oneone)
    468     curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
    469   else
    470     curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0);
    471   curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT, 150L);
    472   /* NOTE: use of CONNECTTIMEOUT without also
    473    *   setting NOSIGNAL results in really weird
    474    *   crashes on my system! */
    475   curl_easy_setopt (c, CURLOPT_NOSIGNAL, 1L);
    476 
    477 
    478   multi = curl_multi_init ();
    479   if (multi == NULL)
    480   {
    481     curl_easy_cleanup (c);
    482     MHD_stop_daemon (d);
    483     return 512;
    484   }
    485   mret = curl_multi_add_handle (multi, c);
    486   if (mret != CURLM_OK)
    487   {
    488     curl_multi_cleanup (multi);
    489     curl_easy_cleanup (c);
    490     MHD_stop_daemon (d);
    491     return 1024;
    492   }
    493   start = time (NULL);
    494   while ((time (NULL) - start < 5) && (multi != NULL))
    495   {
    496     maxsock = MHD_INVALID_SOCKET;
    497     maxposixs = -1;
    498     FD_ZERO (&rs);
    499     FD_ZERO (&ws);
    500     FD_ZERO (&es);
    501     curl_multi_perform (multi, &running);
    502     mret = curl_multi_fdset (multi, &rs, &ws, &es, &maxposixs);
    503     if (mret != CURLM_OK)
    504     {
    505       curl_multi_remove_handle (multi, c);
    506       curl_multi_cleanup (multi);
    507       curl_easy_cleanup (c);
    508       MHD_stop_daemon (d);
    509       return 2048;
    510     }
    511     if (MHD_YES != MHD_get_fdset (d, &rs, &ws, &es, &maxsock))
    512     {
    513       curl_multi_remove_handle (multi, c);
    514       curl_multi_cleanup (multi);
    515       curl_easy_cleanup (c);
    516       MHD_stop_daemon (d);
    517       return 4096;
    518     }
    519     tv.tv_sec = 0;
    520     tv.tv_usec = 1000;
    521     if (-1 == select (maxposixs + 1, &rs, &ws, &es, &tv))
    522     {
    523 #ifdef MHD_POSIX_SOCKETS
    524       if (EINTR != errno)
    525       {
    526         fprintf (stderr, "Unexpected select() error: %d. Line: %d\n",
    527                  (int) errno, __LINE__);
    528         fflush (stderr);
    529         exit (99);
    530       }
    531 #else
    532       if ((WSAEINVAL != WSAGetLastError ()) ||
    533           (0 != rs.fd_count) || (0 != ws.fd_count) || (0 != es.fd_count) )
    534       {
    535         fprintf (stderr, "Unexpected select() error: %d. Line: %d\n",
    536                  (int) WSAGetLastError (), __LINE__);
    537         fflush (stderr);
    538         exit (99);
    539       }
    540       Sleep (1);
    541 #endif
    542     }
    543     curl_multi_perform (multi, &running);
    544     if (0 == running)
    545     {
    546       int pending;
    547       int curl_fine = 0;
    548       while (NULL != (msg = curl_multi_info_read (multi, &pending)))
    549       {
    550         if (msg->msg == CURLMSG_DONE)
    551         {
    552           if (msg->data.result == CURLE_OK)
    553             curl_fine = 1;
    554           else
    555           {
    556             fprintf (stderr,
    557                      "%s failed at %s:%d: `%s'\n",
    558                      "curl_multi_perform",
    559                      __FILE__,
    560                      __LINE__, curl_easy_strerror (msg->data.result));
    561             abort ();
    562           }
    563         }
    564       }
    565       if (! curl_fine)
    566       {
    567         fprintf (stderr, "libcurl haven't returned OK code\n");
    568         abort ();
    569       }
    570       curl_multi_remove_handle (multi, c);
    571       curl_multi_cleanup (multi);
    572       curl_easy_cleanup (c);
    573       c = NULL;
    574       multi = NULL;
    575     }
    576     MHD_run (d);
    577   }
    578   if (multi != NULL)
    579   {
    580     curl_multi_remove_handle (multi, c);
    581     curl_easy_cleanup (c);
    582     curl_multi_cleanup (multi);
    583   }
    584   MHD_stop_daemon (d);
    585   if (cbc.pos != strlen ("/hello_world"))
    586     return 8192;
    587   if (0 != strncmp ("/hello_world", cbc.buf, strlen ("/hello_world")))
    588     return 16384;
    589   return 0;
    590 }
    591 
    592 
    593 static enum MHD_Result
    594 ahc_cancel (void *cls,
    595             struct MHD_Connection *connection,
    596             const char *url,
    597             const char *method,
    598             const char *version,
    599             const char *upload_data, size_t *upload_data_size,
    600             void **req_cls)
    601 {
    602   struct MHD_Response *response;
    603   enum MHD_Result ret;
    604   (void) cls; (void) url; (void) version;          /* Unused. Silent compiler warning. */
    605   (void) upload_data; (void) upload_data_size;     /* Unused. Silent compiler warning. */
    606 
    607   if (0 != strcmp ("POST", method))
    608   {
    609     fprintf (stderr,
    610              "Unexpected method `%s'\n", method);
    611     return MHD_NO;
    612   }
    613 
    614   if (*req_cls == NULL)
    615   {
    616     static int marker = 1;
    617     *req_cls = &marker;
    618     /* We don't want the body. Send a 500. */
    619     response = MHD_create_response_empty (MHD_RF_NONE);
    620     ret = MHD_queue_response (connection, 500, response);
    621     if (ret != MHD_YES)
    622       fprintf (stderr, "Failed to queue response\n");
    623     MHD_destroy_response (response);
    624     return ret;
    625   }
    626   else
    627   {
    628     fprintf (stderr,
    629              "In ahc_cancel again. This should not happen.\n");
    630     return MHD_NO;
    631   }
    632 }
    633 
    634 
    635 struct CRBC
    636 {
    637   const char *buffer;
    638   size_t size;
    639   size_t pos;
    640 };
    641 
    642 
    643 static size_t
    644 readBuffer (void *p, size_t size, size_t nmemb, void *opaque)
    645 {
    646   struct CRBC *data = opaque;
    647   size_t required = size * nmemb;
    648   size_t left = data->size - data->pos;
    649 
    650   if (required > left)
    651     required = left;
    652 
    653   memcpy (p, data->buffer + data->pos, required);
    654   data->pos += required;
    655 
    656   return required / size;
    657 }
    658 
    659 
    660 static size_t
    661 slowReadBuffer (void *p, size_t size, size_t nmemb, void *opaque)
    662 {
    663   (void) sleep (1);
    664   return readBuffer (p, size, nmemb, opaque);
    665 }
    666 
    667 
    668 #define FLAG_EXPECT_CONTINUE 1
    669 #define FLAG_CHUNKED 2
    670 #define FLAG_FORM_DATA 4
    671 #define FLAG_SLOW_READ 8
    672 #define FLAG_COUNT 16
    673 
    674 
    675 static unsigned int
    676 testMultithreadedPostCancelPart (int flags)
    677 {
    678   struct MHD_Daemon *d;
    679   CURL *c;
    680   char buf[2048];
    681   struct CBC cbc;
    682   CURLcode errornum;
    683   struct curl_slist *headers = NULL;
    684   long response_code;
    685   CURLcode cc;
    686   unsigned int result = 0;
    687   struct CRBC crbc;
    688   uint16_t port;
    689 
    690   if (MHD_NO != MHD_is_feature_supported (MHD_FEATURE_AUTODETECT_BIND_PORT))
    691     port = 0;
    692   else
    693   {
    694     port = 1374;
    695     if (oneone)
    696       port += 10;
    697   }
    698 
    699   /* Don't test features that aren't available with HTTP/1.0 in
    700    * HTTP/1.0 mode. */
    701   if (! oneone && (flags & (FLAG_EXPECT_CONTINUE | FLAG_CHUNKED)))
    702     return 0;
    703 
    704   cbc.buf = buf;
    705   cbc.size = 2048;
    706   cbc.pos = 0;
    707   d = MHD_start_daemon (MHD_USE_THREAD_PER_CONNECTION
    708                         | MHD_USE_INTERNAL_POLLING_THREAD | MHD_USE_ERROR_LOG,
    709                         port, NULL, NULL, &ahc_cancel, NULL,
    710                         MHD_OPTION_END);
    711   if (d == NULL)
    712     return 32768;
    713   if (0 == port)
    714   {
    715     const union MHD_DaemonInfo *dinfo;
    716     dinfo = MHD_get_daemon_info (d, MHD_DAEMON_INFO_BIND_PORT);
    717     if ((NULL == dinfo) || (0 == dinfo->port) )
    718     {
    719       MHD_stop_daemon (d); return 32;
    720     }
    721     port = dinfo->port;
    722   }
    723 
    724   crbc.buffer = "Test content";
    725   crbc.size = strlen (crbc.buffer);
    726   crbc.pos = 0;
    727 
    728   c = curl_easy_init ();
    729   curl_easy_setopt (c, CURLOPT_URL, "http://127.0.0.1/hello_world");
    730   curl_easy_setopt (c, CURLOPT_PORT, (long) port);
    731   curl_easy_setopt (c, CURLOPT_WRITEFUNCTION, &copyBuffer);
    732   curl_easy_setopt (c, CURLOPT_WRITEDATA, &cbc);
    733   curl_easy_setopt (c, CURLOPT_READFUNCTION, (flags & FLAG_SLOW_READ) ?
    734                     &slowReadBuffer : &readBuffer);
    735   curl_easy_setopt (c, CURLOPT_READDATA, &crbc);
    736   curl_easy_setopt (c, CURLOPT_POSTFIELDS, NULL);
    737   curl_easy_setopt (c, CURLOPT_POSTFIELDSIZE, crbc.size);
    738   curl_easy_setopt (c, CURLOPT_POST, 1L);
    739   curl_easy_setopt (c, CURLOPT_FAILONERROR, 1L);
    740   curl_easy_setopt (c, CURLOPT_TIMEOUT, 150L);
    741   if (oneone)
    742     curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
    743   else
    744     curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0);
    745   curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT, 150L);
    746   /* NOTE: use of CONNECTTIMEOUT without also
    747    *   setting NOSIGNAL results in really weird
    748    *   crashes on my system! */
    749   curl_easy_setopt (c, CURLOPT_NOSIGNAL, 1L);
    750 
    751   if (flags & FLAG_CHUNKED)
    752     headers = curl_slist_append (headers, "Transfer-Encoding: chunked");
    753   if (! (flags & FLAG_FORM_DATA))
    754     headers = curl_slist_append (headers,
    755                                  "Content-Type: application/octet-stream");
    756   if (flags & FLAG_EXPECT_CONTINUE)
    757     headers = curl_slist_append (headers, "Expect: 100-Continue");
    758   curl_easy_setopt (c, CURLOPT_HTTPHEADER, headers);
    759 
    760   if (CURLE_HTTP_RETURNED_ERROR != (errornum = curl_easy_perform (c)))
    761   {
    762 #ifdef _WIN32
    763     curl_version_info_data *curlverd = curl_version_info (CURLVERSION_NOW);
    764     if ((0 != (flags & FLAG_SLOW_READ)) && (CURLE_RECV_ERROR == errornum) &&
    765         ((curlverd == NULL) || (curlverd->ares_num < 0x073100) ) )
    766     {     /* libcurl up to version 7.49.0 didn't have workaround for WinSock bug */
    767       fprintf (stderr,
    768                "Ignored curl_easy_perform expected failure on W32 with \"slow read\".\n");
    769       result = 0;
    770     }
    771     else
    772 #else  /* ! _WIN32 */
    773     if (1)
    774 #endif /* ! _WIN32 */
    775     {
    776       fprintf (stderr,
    777                "flibbet curl_easy_perform didn't fail as expected: `%s' %u\n",
    778                curl_easy_strerror (errornum), (unsigned int) errornum);
    779       result = 65536;
    780     }
    781     curl_easy_cleanup (c);
    782     MHD_stop_daemon (d);
    783     curl_slist_free_all (headers);
    784     return result;
    785   }
    786 
    787   if (CURLE_OK != (cc = curl_easy_getinfo (c, CURLINFO_RESPONSE_CODE,
    788                                            &response_code)))
    789   {
    790     fprintf (stderr, "curl_easy_getinfo failed: '%s'\n",
    791              curl_easy_strerror (cc));
    792     result = 65536;
    793   }
    794 
    795   if (! result && (response_code != 500))
    796   {
    797     fprintf (stderr, "Unexpected response code: %ld\n", response_code);
    798     result = 131072;
    799   }
    800 
    801   if (! result && (cbc.pos != 0))
    802     result = 262144;
    803 
    804   curl_easy_cleanup (c);
    805   MHD_stop_daemon (d);
    806   curl_slist_free_all (headers);
    807   return result;
    808 }
    809 
    810 
    811 static unsigned int
    812 testMultithreadedPostCancel (void)
    813 {
    814   unsigned int result = 0;
    815   int flags;
    816   for (flags = 0; flags < FLAG_COUNT; ++flags)
    817     result |= testMultithreadedPostCancelPart (flags);
    818   return result;
    819 }
    820 
    821 
    822 int
    823 main (int argc, char *const *argv)
    824 {
    825   unsigned int errorCount = 0;
    826   (void) argc;   /* Unused. Silent compiler warning. */
    827 
    828   if ((NULL == argv) || (0 == argv[0]))
    829     return 99;
    830   oneone = has_in_name (argv[0], "11");
    831   if (0 != curl_global_init (CURL_GLOBAL_WIN32))
    832     return 2;
    833   if (MHD_YES == MHD_is_feature_supported (MHD_FEATURE_THREADS))
    834   {
    835     errorCount += testMultithreadedPostCancel ();
    836     errorCount += testInternalPost ();
    837     errorCount += testMultithreadedPost ();
    838     errorCount += testMultithreadedPoolPost ();
    839   }
    840   errorCount += testExternalPost ();
    841   if (errorCount != 0)
    842     fprintf (stderr, "Error (code: %u)\n", errorCount);
    843   curl_global_cleanup ();
    844   return (0 == errorCount) ? 0 : 1;       /* 0 == pass */
    845 }