libmicrohttpd

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

test_process_headers.c (16262B)


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