libmicrohttpd

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

test_put_chunked.c (15982B)


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