libmicrohttpd

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

test_put.c (16176B)


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