libmicrohttpd

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

test_post.c (23579B)


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