libmicrohttpd

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

test_patch.c (13464B)


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