libmicrohttpd

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

test_process_headers.c (16429B)


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