libmicrohttpd

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

test_put.c (16009B)


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