libmicrohttpd

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

test_patch.c (13631B)


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