libmicrohttpd

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

test_put_chunked.c (16149B)


      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 daemontest_put_chunked.c
     24  * @brief Testcase for libmicrohttpd PUT operations with chunked encoding
     25  *        for the upload data
     26  * @author Christian Grothoff
     27  * @author Karlson2k (Evgeny Grin)
     28  */
     29 
     30 #include "MHD_config.h"
     31 #include "platform.h"
     32 #include <curl/curl.h>
     33 #include <microhttpd.h>
     34 #include <stdlib.h>
     35 #include <string.h>
     36 #include <time.h>
     37 #include <errno.h>
     38 
     39 /* Turn any MHD_PANIC() or failing mhd_assert() reached from this
     40    test into a marked, classifiable test error (TESTING.md, P5). */
     41 #include "mhd_panic_tripwire.h"
     42 
     43 #ifndef WINDOWS
     44 #include <unistd.h>
     45 #endif
     46 
     47 #if defined(MHD_CPU_COUNT) && (MHD_CPU_COUNT + 0) < 2
     48 #undef MHD_CPU_COUNT
     49 #endif
     50 #if ! defined(MHD_CPU_COUNT)
     51 #define MHD_CPU_COUNT 2
     52 #endif
     53 
     54 struct CBC
     55 {
     56   char *buf;
     57   size_t pos;
     58   size_t size;
     59 };
     60 
     61 static size_t
     62 putBuffer (void *stream, size_t size, size_t nmemb, void *ptr)
     63 {
     64   size_t *pos = ptr;
     65   size_t wrt;
     66 
     67   wrt = size * nmemb;
     68   if (wrt > 8 - (*pos))
     69     wrt = 8 - (*pos);
     70   if (wrt > 4)
     71     wrt = 4;                    /* only send half at first => force multiple chunks! */
     72   memcpy (stream, &("Hello123"[*pos]), wrt);
     73   (*pos) += wrt;
     74   return wrt;
     75 }
     76 
     77 
     78 static size_t
     79 copyBuffer (void *ptr, size_t size, size_t nmemb, void *ctx)
     80 {
     81   struct CBC *cbc = ctx;
     82 
     83   if (cbc->pos + size * nmemb > cbc->size)
     84     return 0;                   /* overflow */
     85   memcpy (&cbc->buf[cbc->pos], ptr, size * nmemb);
     86   cbc->pos += size * nmemb;
     87   return size * nmemb;
     88 }
     89 
     90 
     91 static enum MHD_Result
     92 ahc_echo (void *cls,
     93           struct MHD_Connection *connection,
     94           const char *url,
     95           const char *method,
     96           const char *version,
     97           const char *upload_data, size_t *upload_data_size,
     98           void **req_cls)
     99 {
    100   size_t *done = cls;
    101   struct MHD_Response *response;
    102   enum MHD_Result ret;
    103   size_t have;
    104   (void) version; (void) req_cls;   /* Unused. Silent compiler warning. */
    105 
    106   if (0 != strcmp ("PUT", method))
    107     return MHD_NO;              /* unexpected method */
    108   if ((*done) < 8)
    109   {
    110     have = *upload_data_size;
    111     if (have + *done > 8)
    112     {
    113       printf ("Invalid upload data `%8s'!\n", upload_data);
    114       return MHD_NO;
    115     }
    116     if (0 == have)
    117       return MHD_YES;
    118     if (0 == memcmp (upload_data, &"Hello123"[*done], have))
    119     {
    120       *done += have;
    121       *upload_data_size = 0;
    122     }
    123     else
    124     {
    125       printf ("Invalid upload data `%8s'!\n", upload_data);
    126       return MHD_NO;
    127     }
    128 #if 0
    129     fprintf (stderr, "Not ready for response: %u/%u\n", *done, 8);
    130 #endif
    131     return MHD_YES;
    132   }
    133   response = MHD_create_response_from_buffer_copy (strlen (url),
    134                                                    (const void *) url);
    135   ret = MHD_queue_response (connection, MHD_HTTP_OK, response);
    136   MHD_destroy_response (response);
    137   return ret;
    138 }
    139 
    140 
    141 static unsigned int
    142 testInternalPut (void)
    143 {
    144   struct MHD_Daemon *d;
    145   CURL *c;
    146   char buf[2048];
    147   struct CBC cbc;
    148   size_t pos = 0;
    149   size_t done_flag = 0;
    150   CURLcode errornum;
    151   uint16_t port;
    152 
    153   if (MHD_NO != MHD_is_feature_supported (MHD_FEATURE_AUTODETECT_BIND_PORT))
    154     port = 0;
    155   else
    156     port = 1440;
    157 
    158   cbc.buf = buf;
    159   cbc.size = 2048;
    160   cbc.pos = 0;
    161   d = MHD_start_daemon (MHD_USE_INTERNAL_POLLING_THREAD | MHD_USE_ERROR_LOG,
    162                         port,
    163                         NULL, NULL, &ahc_echo, &done_flag, MHD_OPTION_END);
    164   if (d == NULL)
    165     return 1;
    166   if (0 == port)
    167   {
    168     const union MHD_DaemonInfo *dinfo;
    169     dinfo = MHD_get_daemon_info (d, MHD_DAEMON_INFO_BIND_PORT);
    170     if ((NULL == dinfo) || (0 == dinfo->port) )
    171     {
    172       MHD_stop_daemon (d); return 32;
    173     }
    174     port = dinfo->port;
    175   }
    176   c = curl_easy_init ();
    177   curl_easy_setopt (c, CURLOPT_URL, "http://127.0.0.1/hello_world");
    178   curl_easy_setopt (c, CURLOPT_PORT, (long) port);
    179   curl_easy_setopt (c, CURLOPT_WRITEFUNCTION, &copyBuffer);
    180   curl_easy_setopt (c, CURLOPT_WRITEDATA, &cbc);
    181   curl_easy_setopt (c, CURLOPT_READFUNCTION, &putBuffer);
    182   curl_easy_setopt (c, CURLOPT_READDATA, &pos);
    183   curl_easy_setopt (c, CURLOPT_UPLOAD, 1L);
    184   /* by not giving the file size, we force chunking! */
    185   /*
    186      curl_easy_setopt (c, CURLOPT_INFILESIZE_LARGE, (curl_off_t) 8L);
    187    */
    188   curl_easy_setopt (c, CURLOPT_FAILONERROR, 1L);
    189   curl_easy_setopt (c, CURLOPT_TIMEOUT, 150L);
    190   curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
    191   curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT, 150L);
    192   /* NOTE: use of CONNECTTIMEOUT without also
    193    *   setting NOSIGNAL results in really weird
    194    *   crashes on my system! */
    195   curl_easy_setopt (c, CURLOPT_NOSIGNAL, 1L);
    196   if (CURLE_OK != (errornum = curl_easy_perform (c)))
    197   {
    198     fprintf (stderr,
    199              "curl_easy_perform failed: `%s'\n",
    200              curl_easy_strerror (errornum));
    201     curl_easy_cleanup (c);
    202     MHD_stop_daemon (d);
    203     return 2;
    204   }
    205   curl_easy_cleanup (c);
    206   MHD_stop_daemon (d);
    207   if (cbc.pos != strlen ("/hello_world"))
    208     return 4;
    209   if (0 != strncmp ("/hello_world", cbc.buf, strlen ("/hello_world")))
    210     return 8;
    211   return 0;
    212 }
    213 
    214 
    215 static unsigned int
    216 testMultithreadedPut (void)
    217 {
    218   struct MHD_Daemon *d;
    219   CURL *c;
    220   char buf[2048];
    221   struct CBC cbc;
    222   size_t pos = 0;
    223   size_t done_flag = 0;
    224   CURLcode errornum;
    225   uint16_t port;
    226 
    227   if (MHD_NO != MHD_is_feature_supported (MHD_FEATURE_AUTODETECT_BIND_PORT))
    228     port = 0;
    229   else
    230     port = 1441;
    231 
    232   cbc.buf = buf;
    233   cbc.size = 2048;
    234   cbc.pos = 0;
    235   d = MHD_start_daemon (MHD_USE_THREAD_PER_CONNECTION
    236                         | MHD_USE_INTERNAL_POLLING_THREAD | MHD_USE_ERROR_LOG,
    237                         port,
    238                         NULL, NULL, &ahc_echo, &done_flag, MHD_OPTION_END);
    239   if (d == NULL)
    240     return 16;
    241   if (0 == port)
    242   {
    243     const union MHD_DaemonInfo *dinfo;
    244     dinfo = MHD_get_daemon_info (d, MHD_DAEMON_INFO_BIND_PORT);
    245     if ((NULL == dinfo) || (0 == dinfo->port) )
    246     {
    247       MHD_stop_daemon (d); return 32;
    248     }
    249     port = dinfo->port;
    250   }
    251   c = curl_easy_init ();
    252   curl_easy_setopt (c, CURLOPT_URL, "http://127.0.0.1/hello_world");
    253   curl_easy_setopt (c, CURLOPT_PORT, (long) port);
    254   curl_easy_setopt (c, CURLOPT_WRITEFUNCTION, &copyBuffer);
    255   curl_easy_setopt (c, CURLOPT_WRITEDATA, &cbc);
    256   curl_easy_setopt (c, CURLOPT_READFUNCTION, &putBuffer);
    257   curl_easy_setopt (c, CURLOPT_READDATA, &pos);
    258   curl_easy_setopt (c, CURLOPT_UPLOAD, 1L);
    259   /* by not giving the file size, we force chunking! */
    260   /*
    261      curl_easy_setopt (c, CURLOPT_INFILESIZE_LARGE, (curl_off_t) 8L);
    262    */
    263   curl_easy_setopt (c, CURLOPT_FAILONERROR, 1L);
    264   curl_easy_setopt (c, CURLOPT_TIMEOUT, 150L);
    265   curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
    266   curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT, 150L);
    267   /* NOTE: use of CONNECTTIMEOUT without also
    268    *   setting NOSIGNAL results in really weird
    269    *   crashes on my system! */
    270   curl_easy_setopt (c, CURLOPT_NOSIGNAL, 1L);
    271   if (CURLE_OK != (errornum = curl_easy_perform (c)))
    272   {
    273     fprintf (stderr,
    274              "curl_easy_perform failed: `%s'\n",
    275              curl_easy_strerror (errornum));
    276     curl_easy_cleanup (c);
    277     MHD_stop_daemon (d);
    278     return 32;
    279   }
    280   curl_easy_cleanup (c);
    281   MHD_stop_daemon (d);
    282   if (cbc.pos != strlen ("/hello_world"))
    283     return 64;
    284   if (0 != strncmp ("/hello_world", cbc.buf, strlen ("/hello_world")))
    285     return 128;
    286 
    287   return 0;
    288 }
    289 
    290 
    291 static unsigned int
    292 testMultithreadedPoolPut (void)
    293 {
    294   struct MHD_Daemon *d;
    295   CURL *c;
    296   char buf[2048];
    297   struct CBC cbc;
    298   size_t pos = 0;
    299   size_t done_flag = 0;
    300   CURLcode errornum;
    301   uint16_t port;
    302 
    303   if (MHD_NO != MHD_is_feature_supported (MHD_FEATURE_AUTODETECT_BIND_PORT))
    304     port = 0;
    305   else
    306     port = 1442;
    307 
    308   cbc.buf = buf;
    309   cbc.size = 2048;
    310   cbc.pos = 0;
    311   d = MHD_start_daemon (MHD_USE_INTERNAL_POLLING_THREAD | MHD_USE_ERROR_LOG,
    312                         port,
    313                         NULL, NULL, &ahc_echo, &done_flag,
    314                         MHD_OPTION_THREAD_POOL_SIZE, MHD_CPU_COUNT,
    315                         MHD_OPTION_END);
    316   if (d == NULL)
    317     return 16;
    318   if (0 == port)
    319   {
    320     const union MHD_DaemonInfo *dinfo;
    321     dinfo = MHD_get_daemon_info (d, MHD_DAEMON_INFO_BIND_PORT);
    322     if ((NULL == dinfo) || (0 == dinfo->port) )
    323     {
    324       MHD_stop_daemon (d); return 32;
    325     }
    326     port = dinfo->port;
    327   }
    328   c = curl_easy_init ();
    329   curl_easy_setopt (c, CURLOPT_URL, "http://127.0.0.1/hello_world");
    330   curl_easy_setopt (c, CURLOPT_PORT, (long) port);
    331   curl_easy_setopt (c, CURLOPT_WRITEFUNCTION, &copyBuffer);
    332   curl_easy_setopt (c, CURLOPT_WRITEDATA, &cbc);
    333   curl_easy_setopt (c, CURLOPT_READFUNCTION, &putBuffer);
    334   curl_easy_setopt (c, CURLOPT_READDATA, &pos);
    335   curl_easy_setopt (c, CURLOPT_UPLOAD, 1L);
    336   /* by not giving the file size, we force chunking! */
    337   /*
    338      curl_easy_setopt (c, CURLOPT_INFILESIZE_LARGE, (curl_off_t) 8L);
    339    */
    340   curl_easy_setopt (c, CURLOPT_FAILONERROR, 1L);
    341   curl_easy_setopt (c, CURLOPT_TIMEOUT, 150L);
    342   curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
    343   curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT, 150L);
    344   /* NOTE: use of CONNECTTIMEOUT without also
    345    *   setting NOSIGNAL results in really weird
    346    *   crashes on my system! */
    347   curl_easy_setopt (c, CURLOPT_NOSIGNAL, 1L);
    348   if (CURLE_OK != (errornum = curl_easy_perform (c)))
    349   {
    350     fprintf (stderr,
    351              "curl_easy_perform failed: `%s'\n",
    352              curl_easy_strerror (errornum));
    353     curl_easy_cleanup (c);
    354     MHD_stop_daemon (d);
    355     return 32;
    356   }
    357   curl_easy_cleanup (c);
    358   MHD_stop_daemon (d);
    359   if (cbc.pos != strlen ("/hello_world"))
    360     return 64;
    361   if (0 != strncmp ("/hello_world", cbc.buf, strlen ("/hello_world")))
    362     return 128;
    363 
    364   return 0;
    365 }
    366 
    367 
    368 static unsigned int
    369 testExternalPut (void)
    370 {
    371   struct MHD_Daemon *d;
    372   CURL *c;
    373   char buf[2048];
    374   struct CBC cbc;
    375   CURLM *multi;
    376   CURLMcode mret;
    377   fd_set rs;
    378   fd_set ws;
    379   fd_set es;
    380   MHD_socket maxsock;
    381 #ifdef MHD_WINSOCK_SOCKETS
    382   int maxposixs; /* Max socket number unused on W32 */
    383 #else  /* MHD_POSIX_SOCKETS */
    384 #define maxposixs maxsock
    385 #endif /* MHD_POSIX_SOCKETS */
    386   int running;
    387   struct CURLMsg *msg;
    388   time_t start;
    389   struct timeval tv;
    390   size_t pos = 0;
    391   size_t done_flag = 0;
    392   uint16_t port;
    393 
    394   if (MHD_NO != MHD_is_feature_supported (MHD_FEATURE_AUTODETECT_BIND_PORT))
    395     port = 0;
    396   else
    397     port = 1443;
    398 
    399   multi = NULL;
    400   cbc.buf = buf;
    401   cbc.size = 2048;
    402   cbc.pos = 0;
    403   d = MHD_start_daemon (MHD_USE_ERROR_LOG,
    404                         port,
    405                         NULL, NULL, &ahc_echo, &done_flag,
    406                         MHD_OPTION_APP_FD_SETSIZE, (int) FD_SETSIZE,
    407                         MHD_OPTION_END);
    408   if (d == NULL)
    409     return 256;
    410   if (0 == port)
    411   {
    412     const union MHD_DaemonInfo *dinfo;
    413     dinfo = MHD_get_daemon_info (d, MHD_DAEMON_INFO_BIND_PORT);
    414     if ((NULL == dinfo) || (0 == dinfo->port) )
    415     {
    416       MHD_stop_daemon (d); return 32;
    417     }
    418     port = dinfo->port;
    419   }
    420   c = curl_easy_init ();
    421   curl_easy_setopt (c, CURLOPT_URL, "http://127.0.0.1/hello_world");
    422   curl_easy_setopt (c, CURLOPT_PORT, (long) port);
    423   curl_easy_setopt (c, CURLOPT_WRITEFUNCTION, &copyBuffer);
    424   curl_easy_setopt (c, CURLOPT_WRITEDATA, &cbc);
    425   curl_easy_setopt (c, CURLOPT_READFUNCTION, &putBuffer);
    426   curl_easy_setopt (c, CURLOPT_READDATA, &pos);
    427   curl_easy_setopt (c, CURLOPT_UPLOAD, 1L);
    428   /* by not giving the file size, we force chunking! */
    429   /*
    430      curl_easy_setopt (c, CURLOPT_INFILESIZE_LARGE, (curl_off_t) 8L);
    431    */
    432   curl_easy_setopt (c, CURLOPT_FAILONERROR, 1L);
    433   curl_easy_setopt (c, CURLOPT_TIMEOUT, 150L);
    434   curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
    435   curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT, 150L);
    436   /* NOTE: use of CONNECTTIMEOUT without also
    437    *   setting NOSIGNAL results in really weird
    438    *   crashes on my system! */
    439   curl_easy_setopt (c, CURLOPT_NOSIGNAL, 1L);
    440 
    441 
    442   multi = curl_multi_init ();
    443   if (multi == NULL)
    444   {
    445     curl_easy_cleanup (c);
    446     MHD_stop_daemon (d);
    447     return 512;
    448   }
    449   mret = curl_multi_add_handle (multi, c);
    450   if (mret != CURLM_OK)
    451   {
    452     curl_multi_cleanup (multi);
    453     curl_easy_cleanup (c);
    454     MHD_stop_daemon (d);
    455     return 1024;
    456   }
    457   start = time (NULL);
    458   while ((time (NULL) - start < 5) && (multi != NULL))
    459   {
    460     maxsock = MHD_INVALID_SOCKET;
    461     maxposixs = -1;
    462     FD_ZERO (&rs);
    463     FD_ZERO (&ws);
    464     FD_ZERO (&es);
    465     curl_multi_perform (multi, &running);
    466     mret = curl_multi_fdset (multi, &rs, &ws, &es, &maxposixs);
    467     if (mret != CURLM_OK)
    468     {
    469       curl_multi_remove_handle (multi, c);
    470       curl_multi_cleanup (multi);
    471       curl_easy_cleanup (c);
    472       MHD_stop_daemon (d);
    473       return 2048;
    474     }
    475     if (MHD_YES != MHD_get_fdset (d, &rs, &ws, &es, &maxsock))
    476     {
    477       curl_multi_remove_handle (multi, c);
    478       curl_multi_cleanup (multi);
    479       curl_easy_cleanup (c);
    480       MHD_stop_daemon (d);
    481       return 4096;
    482     }
    483     tv.tv_sec = 0;
    484     tv.tv_usec = 1000;
    485     if (-1 == select (maxposixs + 1, &rs, &ws, &es, &tv))
    486     {
    487 #ifdef MHD_POSIX_SOCKETS
    488       if (EINTR != errno)
    489       {
    490         fprintf (stderr, "Unexpected select() error: %d. Line: %d\n",
    491                  (int) errno, __LINE__);
    492         fflush (stderr);
    493         exit (99);
    494       }
    495 #else
    496       if ((WSAEINVAL != WSAGetLastError ()) ||
    497           (0 != rs.fd_count) || (0 != ws.fd_count) || (0 != es.fd_count) )
    498       {
    499         fprintf (stderr, "Unexpected select() error: %d. Line: %d\n",
    500                  (int) WSAGetLastError (), __LINE__);
    501         fflush (stderr);
    502         exit (99);
    503       }
    504       Sleep (1);
    505 #endif
    506     }
    507     curl_multi_perform (multi, &running);
    508     if (0 == running)
    509     {
    510       int pending;
    511       int curl_fine = 0;
    512       while (NULL != (msg = curl_multi_info_read (multi, &pending)))
    513       {
    514         if (msg->msg == CURLMSG_DONE)
    515         {
    516           if (msg->data.result == CURLE_OK)
    517             curl_fine = 1;
    518           else
    519           {
    520             fprintf (stderr,
    521                      "%s failed at %s:%d: `%s'\n",
    522                      "curl_multi_perform",
    523                      __FILE__,
    524                      __LINE__, curl_easy_strerror (msg->data.result));
    525             abort ();
    526           }
    527         }
    528       }
    529       if (! curl_fine)
    530       {
    531         fprintf (stderr, "libcurl haven't returned OK code\n");
    532         abort ();
    533       }
    534       curl_multi_remove_handle (multi, c);
    535       curl_multi_cleanup (multi);
    536       curl_easy_cleanup (c);
    537       c = NULL;
    538       multi = NULL;
    539     }
    540     MHD_run (d);
    541   }
    542   if (multi != NULL)
    543   {
    544     curl_multi_remove_handle (multi, c);
    545     curl_easy_cleanup (c);
    546     curl_multi_cleanup (multi);
    547   }
    548   MHD_stop_daemon (d);
    549   if (cbc.pos != strlen ("/hello_world"))
    550     return 8192;
    551   if (0 != strncmp ("/hello_world", cbc.buf, strlen ("/hello_world")))
    552     return 16384;
    553   return 0;
    554 }
    555 
    556 
    557 int
    558 main (int argc, char *const *argv)
    559 {
    560   unsigned int errorCount = 0;
    561   (void) argc; (void) argv; /* Unused. Silent compiler warning. */
    562 
    563   if (0 != curl_global_init (CURL_GLOBAL_WIN32))
    564     return 2;
    565   if (MHD_YES == MHD_is_feature_supported (MHD_FEATURE_THREADS))
    566   {
    567     errorCount += testInternalPut ();
    568     errorCount += testMultithreadedPut ();
    569     errorCount += testMultithreadedPoolPut ();
    570   }
    571   errorCount += testExternalPut ();
    572   if (errorCount != 0)
    573     fprintf (stderr, "Error (code: %u)\n", errorCount);
    574   curl_global_cleanup ();
    575   return (0 == errorCount) ? 0 : 1;       /* 0 == pass */
    576 }