libmicrohttpd

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

test_long_header.c (14742B)


      1 /*
      2      This file is part of libmicrohttpd
      3      Copyright (C) 2007 Christian Grothoff
      4      Copyright (C) 2016-2023 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_long_header.c
     24  * @brief  Testcase for libmicrohttpd handling of very long headers
     25  * @author Christian Grothoff
     26  * @author Karlson2k (Evgeny Grin)
     27  */
     28 
     29 #include "mhd_options.h"
     30 #include "platform.h"
     31 #include <curl/curl.h>
     32 #include <microhttpd.h>
     33 #include <stdlib.h>
     34 #include <stdio.h>
     35 #include <string.h>
     36 #include <time.h>
     37 #include "mhd_has_in_name.h"
     38 
     39 #ifndef WINDOWS
     40 #include <unistd.h>
     41 #endif
     42 
     43 #ifndef MHD_STATICSTR_LEN_
     44 /**
     45  * Determine length of static string / macro strings at compile time.
     46  */
     47 #define MHD_STATICSTR_LEN_(macro) (sizeof(macro) / sizeof(char) - 1)
     48 #endif /* ! MHD_STATICSTR_LEN_ */
     49 
     50 #ifndef CURL_VERSION_BITS
     51 #define CURL_VERSION_BITS(x,y,z) ((x) << 16 | (y) << 8 | (z))
     52 #endif /* ! CURL_VERSION_BITS */
     53 #ifndef CURL_AT_LEAST_VERSION
     54 #define CURL_AT_LEAST_VERSION(x,y,z) \
     55   (LIBCURL_VERSION_NUM >= CURL_VERSION_BITS (x, y, z))
     56 #endif /* ! CURL_AT_LEAST_VERSION */
     57 
     58 /**
     59  * We will set the memory available per connection to
     60  * half of this value, so the actual value does not have
     61  * to be big at all...
     62  */
     63 #define VERY_LONG (1024 * 8)
     64 
     65 /* Could be increased to facilitate debugging */
     66 #define TIMEOUTS_VAL 5
     67 
     68 #define EXPECTED_URI_BASE_PATH  "/"
     69 
     70 #define URL_SCHEME "http:/" "/"
     71 
     72 #define URL_HOST "127.0.0.1"
     73 
     74 #define URL_SCHEME_HOST_PATH URL_SCHEME URL_HOST EXPECTED_URI_BASE_PATH
     75 
     76 
     77 static int oneone;
     78 
     79 static uint16_t daemon_port;
     80 
     81 
     82 static char libcurl_err_buf[CURL_ERROR_SIZE];
     83 
     84 struct CBC
     85 {
     86   char *buf;
     87   size_t pos;
     88   size_t size;
     89 };
     90 
     91 static size_t
     92 copyBuffer (void *ptr, size_t size, size_t nmemb, void *ctx)
     93 {
     94   (void) ptr; (void) ctx;  /* Unused. Silent compiler warning. */
     95   return size * nmemb;
     96 }
     97 
     98 
     99 /* Return non-zero on success, zero on failure */
    100 static int
    101 setup_easy_handler_params (CURL *c, struct CBC *pcbc, const char *url,
    102                            struct curl_slist *header)
    103 {
    104   libcurl_err_buf[0] = 0; /* Reset error message */
    105 
    106   if (CURLE_OK != curl_easy_setopt (c, CURLOPT_ERRORBUFFER, libcurl_err_buf))
    107   {
    108     fprintf (stderr, "Failed to set CURLOPT_ERRORBUFFER option.\n");
    109     return 0;
    110   }
    111   if (CURLE_OK != curl_easy_setopt (c, CURLOPT_NOSIGNAL, 1L))
    112   {
    113     fprintf (stderr, "Failed to set CURLOPT_NOSIGNAL option.\n");
    114     return 0;
    115   }
    116   if (CURLE_OK != curl_easy_setopt (c, CURLOPT_WRITEFUNCTION,
    117                                     &copyBuffer))
    118   {
    119     fprintf (stderr, "Failed to set CURLOPT_WRITEFUNCTION option.\n");
    120     return 0;
    121   }
    122   if (CURLE_OK != curl_easy_setopt (c, CURLOPT_WRITEDATA, pcbc))
    123   {
    124     fprintf (stderr, "Failed to set CURLOPT_WRITEDATA option.\n");
    125     return 0;
    126   }
    127   if (CURLE_OK != curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT,
    128                                     ((long) TIMEOUTS_VAL)))
    129   {
    130     fprintf (stderr, "Failed to set CURLOPT_CONNECTTIMEOUT option.\n");
    131     return 0;
    132   }
    133   if (CURLE_OK != curl_easy_setopt (c, CURLOPT_TIMEOUT,
    134                                     ((long) TIMEOUTS_VAL)))
    135   {
    136     fprintf (stderr, "Failed to set CURLOPT_TIMEOUT option.\n");
    137     return 0;
    138   }
    139   if (CURLE_OK != curl_easy_setopt (c, CURLOPT_HTTP_VERSION,
    140                                     (oneone) ?
    141                                     CURL_HTTP_VERSION_1_1 :
    142                                     CURL_HTTP_VERSION_1_0))
    143   {
    144     fprintf (stderr, "Failed to set CURLOPT_HTTP_VERSION option.\n");
    145     return 0;
    146   }
    147   if (CURLE_OK != curl_easy_setopt (c, CURLOPT_FAILONERROR, 0L))
    148   {
    149     fprintf (stderr, "Failed to set CURLOPT_FAILONERROR option.\n");
    150     return 0;
    151   }
    152 #ifdef _DEBUG
    153   if (CURLE_OK != curl_easy_setopt (c, CURLOPT_VERBOSE, 1L))
    154   {
    155     fprintf (stderr, "Failed to set CURLOPT_VERBOSE option.\n");
    156     return 0;
    157   }
    158 #endif /* _DEBUG */
    159 #if CURL_AT_LEAST_VERSION (7, 45, 0)
    160   if (CURLE_OK != curl_easy_setopt (c, CURLOPT_DEFAULT_PROTOCOL, "http"))
    161   {
    162     fprintf (stderr, "Failed to set CURLOPT_DEFAULT_PROTOCOL option.\n");
    163     return 0;
    164   }
    165 #endif /* CURL_AT_LEAST_VERSION (7, 45, 0) */
    166 #if CURL_AT_LEAST_VERSION (7, 85, 0)
    167   if (CURLE_OK != curl_easy_setopt (c, CURLOPT_PROTOCOLS_STR, "http"))
    168   {
    169     fprintf (stderr, "Failed to set CURLOPT_PROTOCOLS_STR option.\n");
    170     return 0;
    171   }
    172 #elif CURL_AT_LEAST_VERSION (7, 19, 4)
    173   if (CURLE_OK != curl_easy_setopt (c, CURLOPT_PROTOCOLS, CURLPROTO_HTTP))
    174   {
    175     fprintf (stderr, "Failed to set CURLOPT_PROTOCOLS option.\n");
    176     return 0;
    177   }
    178 #endif /* CURL_AT_LEAST_VERSION (7, 19, 4) */
    179   if (CURLE_OK != curl_easy_setopt (c, CURLOPT_URL,
    180                                     (NULL != url) ?
    181                                     url : URL_SCHEME_HOST_PATH))
    182   {
    183     fprintf (stderr, "Failed to set CURLOPT_PROTOCOLS option.\n");
    184     return 0;
    185   }
    186   if (CURLE_OK != curl_easy_setopt (c, CURLOPT_PORT, ((long) daemon_port)))
    187   {
    188     fprintf (stderr, "Failed to set CURLOPT_PROTOCOLS option.\n");
    189     return 0;
    190   }
    191 
    192   if (NULL != header)
    193   {
    194     if (CURLE_OK != curl_easy_setopt (c, CURLOPT_HTTPHEADER, header))
    195     {
    196       fprintf (stderr, "Failed to set CURLOPT_HTTPHEADER option.\n");
    197       return 0;
    198     }
    199   }
    200   return ! 0;
    201 }
    202 
    203 
    204 static CURL *
    205 setup_easy_handler (struct CBC *pcbc, const char *url, struct
    206                     curl_slist *header)
    207 {
    208   CURL *c;
    209 
    210   c = curl_easy_init ();
    211   if (NULL == c)
    212   {
    213     fprintf (stderr, "curl_easy_init() error.\n");
    214     return NULL;
    215   }
    216   if (setup_easy_handler_params (c, pcbc, url, header))
    217   {
    218     return c; /* Success exit point */
    219   }
    220   curl_easy_cleanup (c);
    221   return NULL;
    222 }
    223 
    224 
    225 static enum MHD_Result
    226 ahc_echo (void *cls,
    227           struct MHD_Connection *connection,
    228           const char *url,
    229           const char *method,
    230           const char *version,
    231           const char *upload_data, size_t *upload_data_size,
    232           void **req_cls)
    233 {
    234   struct MHD_Response *response;
    235   enum MHD_Result ret;
    236   static int marker;
    237 
    238   (void) cls;
    239   (void) version; (void) upload_data;      /* Unused. Silent compiler warning. */
    240   (void) upload_data_size; (void) req_cls; /* Unused. Silent compiler warning. */
    241 
    242   if (&marker != *req_cls)
    243   {
    244     *req_cls = &marker;
    245     return MHD_YES;
    246   }
    247   if (0 != strcmp (MHD_HTTP_METHOD_GET, method))
    248     return MHD_NO;              /* unexpected method */
    249   response = MHD_create_response_from_buffer_copy (strlen (url),
    250                                                    (const void *) url);
    251   ret = MHD_queue_response (connection, MHD_HTTP_OK, response);
    252   MHD_destroy_response (response);
    253   return ret;
    254 }
    255 
    256 
    257 static unsigned int
    258 testLongUrlGet (size_t buff_size)
    259 {
    260   struct MHD_Daemon *d;
    261   unsigned int ret;
    262 
    263   ret = 1; /* Error value, shall be reset to zero if succeed */
    264   d =
    265     MHD_start_daemon (MHD_USE_INTERNAL_POLLING_THREAD /* | MHD_USE_ERROR_LOG */,
    266                       daemon_port,
    267                       NULL,
    268                       NULL,
    269                       &ahc_echo, NULL,
    270                       MHD_OPTION_CONNECTION_MEMORY_LIMIT,
    271                       (size_t) buff_size, MHD_OPTION_END);
    272   if (d == NULL)
    273   {
    274     fprintf (stderr, "MHD_start_daemon() failed.\n");
    275     return 16;
    276   }
    277   if (0 == daemon_port)
    278   {
    279     const union MHD_DaemonInfo *dinfo;
    280     dinfo = MHD_get_daemon_info (d, MHD_DAEMON_INFO_BIND_PORT);
    281     if ((NULL == dinfo) || (0 == dinfo->port) )
    282       fprintf (stderr, "MHD_get_daemon_info(d, MHD_DAEMON_INFO_BIND_PORT) " \
    283                "failed.\n");
    284     else
    285       daemon_port = dinfo->port;
    286   }
    287   if (0 != daemon_port)
    288   {
    289     char *url_str;
    290     url_str = malloc (VERY_LONG);
    291     if (NULL == url_str)
    292       fprintf (stderr, "malloc (VERY_LONG) failed.\n");
    293     else
    294     {
    295       CURL *c;
    296       char buf[2048];
    297       struct CBC cbc;
    298 
    299       memset (url_str, 'a', VERY_LONG);
    300       url_str[VERY_LONG - 1] = '\0';
    301       memcpy (url_str, URL_SCHEME_HOST_PATH,
    302               MHD_STATICSTR_LEN_ (URL_SCHEME_HOST_PATH));
    303 
    304       cbc.buf = buf;
    305       cbc.size = sizeof (buf);
    306       cbc.pos = 0;
    307 
    308       c = setup_easy_handler (&cbc, url_str, NULL);
    309       if (NULL != c)
    310       {
    311         CURLcode r;
    312         r = curl_easy_perform (c);
    313         if (CURLE_OK != r)
    314         {
    315           fprintf (stderr, "curl_easy_perform() failed. Error message: %s\n",
    316                    curl_easy_strerror (r));
    317           if (0 != libcurl_err_buf[0])
    318             fprintf (stderr, "Detailed error message: %s\n", libcurl_err_buf);
    319         }
    320         else
    321         {
    322           long code;
    323 
    324           r = curl_easy_getinfo (c, CURLINFO_RESPONSE_CODE, &code);
    325           if (CURLE_OK != r)
    326           {
    327             fprintf (stderr, "curl_easy_getinfo() failed. "
    328                      "Error message: %s\n",
    329                      curl_easy_strerror (r));
    330             if (0 != libcurl_err_buf[0])
    331               fprintf (stderr, "Detailed error message: %s\n",
    332                        libcurl_err_buf);
    333           }
    334           else
    335           {
    336             if (code != MHD_HTTP_URI_TOO_LONG)
    337             {
    338               fprintf (stderr, "testLongHeaderGet(%lu) failed. HTTP "
    339                        "response code is %ld, while it should be %ld.\n",
    340                        (unsigned long) buff_size,
    341                        code,
    342                        (long) MHD_HTTP_URI_TOO_LONG);
    343             }
    344             else
    345             {
    346               printf ("testLongHeaderGet(%lu) succeed. HTTP "
    347                       "response code is %ld, as expected.\n",
    348                       (unsigned long) buff_size,
    349                       (long) MHD_HTTP_URI_TOO_LONG);
    350               ret = 0; /* Success */
    351             }
    352           }
    353         }
    354         curl_easy_cleanup (c);
    355       }
    356       free (url_str);
    357     }
    358   }
    359   MHD_stop_daemon (d);
    360   return ret;
    361 }
    362 
    363 
    364 static unsigned int
    365 testLongHeaderGet (size_t buff_size)
    366 {
    367   struct MHD_Daemon *d;
    368   unsigned int ret;
    369 
    370   ret = 1; /* Error value, shall be reset to zero if succeed */
    371   d =
    372     MHD_start_daemon (MHD_USE_INTERNAL_POLLING_THREAD /* | MHD_USE_ERROR_LOG */,
    373                       daemon_port,
    374                       NULL,
    375                       NULL,
    376                       &ahc_echo, NULL,
    377                       MHD_OPTION_CONNECTION_MEMORY_LIMIT,
    378                       (size_t) buff_size, MHD_OPTION_END);
    379   if (d == NULL)
    380   {
    381     fprintf (stderr, "MHD_start_daemon() failed.\n");
    382     return 16;
    383   }
    384   if (0 == daemon_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       fprintf (stderr, "MHD_get_daemon_info(d, MHD_DAEMON_INFO_BIND_PORT) " \
    390                "failed.\n");
    391     else
    392       daemon_port = dinfo->port;
    393   }
    394   if (0 != daemon_port)
    395   {
    396     char *header_str;
    397     header_str = malloc (VERY_LONG);
    398     if (NULL == header_str)
    399       fprintf (stderr, "malloc (VERY_LONG) failed.\n");
    400     else
    401     {
    402       struct curl_slist *header = NULL;
    403 
    404       memset (header_str, 'a', VERY_LONG);
    405       header_str[VERY_LONG - 1] = '\0';
    406       header_str[VERY_LONG / 2] = ':';
    407       header_str[VERY_LONG / 2 + 1] = ' ';
    408 
    409       header = curl_slist_append (header, header_str);
    410       if (NULL == header)
    411         fprintf (stderr, "curl_slist_append () failed.\n");
    412       else
    413       {
    414         CURL *c;
    415         char buf[2048];
    416         struct CBC cbc;
    417 
    418         cbc.buf = buf;
    419         cbc.size = sizeof (buf);
    420         cbc.pos = 0;
    421 
    422         c = setup_easy_handler (&cbc, NULL, header);
    423         if (NULL != c)
    424         {
    425           CURLcode r;
    426           r = curl_easy_perform (c);
    427           if (CURLE_OK != r)
    428           {
    429             fprintf (stderr, "curl_easy_perform() failed. Error message: %s\n",
    430                      curl_easy_strerror (r));
    431             if (0 != libcurl_err_buf[0])
    432               fprintf (stderr, "Detailed error message: %s\n", libcurl_err_buf);
    433           }
    434           else
    435           {
    436             long code;
    437 
    438             r = curl_easy_getinfo (c, CURLINFO_RESPONSE_CODE, &code);
    439             if (CURLE_OK != r)
    440             {
    441               fprintf (stderr, "curl_easy_getinfo() failed. "
    442                        "Error message: %s\n",
    443                        curl_easy_strerror (r));
    444               if (0 != libcurl_err_buf[0])
    445                 fprintf (stderr, "Detailed error message: %s\n",
    446                          libcurl_err_buf);
    447             }
    448             else
    449             {
    450               if (code != MHD_HTTP_REQUEST_HEADER_FIELDS_TOO_LARGE)
    451               {
    452                 fprintf (stderr, "testLongHeaderGet(%lu) failed. HTTP "
    453                          "response code is %ld, while it should be %ld.\n",
    454                          (unsigned long) buff_size,
    455                          code,
    456                          (long) MHD_HTTP_REQUEST_HEADER_FIELDS_TOO_LARGE);
    457               }
    458               else
    459               {
    460                 printf ("testLongHeaderGet(%lu) succeed. HTTP "
    461                         "response code is %ld, as expected.\n",
    462                         (unsigned long) buff_size,
    463                         (long) MHD_HTTP_REQUEST_HEADER_FIELDS_TOO_LARGE);
    464                 ret = 0; /* Success */
    465               }
    466             }
    467           }
    468           curl_easy_cleanup (c);
    469         }
    470         curl_slist_free_all (header);
    471       }
    472       free (header_str);
    473     }
    474   }
    475   MHD_stop_daemon (d);
    476   return ret;
    477 }
    478 
    479 
    480 int
    481 main (int argc, char *const *argv)
    482 {
    483   unsigned int errorCount = 0;
    484   (void) argc;   /* Unused. Silent compiler warning. */
    485 
    486   if ((NULL == argv) || (0 == argv[0]))
    487     return 99;
    488   oneone = has_in_name (argv[0], "11");
    489   if (0 != curl_global_init (CURL_GLOBAL_WIN32))
    490     return 2;
    491   if (MHD_NO == MHD_is_feature_supported (MHD_FEATURE_AUTODETECT_BIND_PORT))
    492     daemon_port = 0;
    493   else
    494     daemon_port = oneone ? 1336 : 1331;
    495 
    496   errorCount += testLongUrlGet (VERY_LONG / 2);
    497   errorCount += testLongUrlGet (VERY_LONG / 2 + 978);
    498   errorCount += testLongHeaderGet (VERY_LONG / 2);
    499   errorCount += testLongHeaderGet (VERY_LONG / 2 + 1893);
    500   if (errorCount != 0)
    501     fprintf (stderr, "Error (code: %u)\n", errorCount);
    502   else
    503     printf ("Test succeed.\n");
    504   curl_global_cleanup ();
    505   return (0 == errorCount) ? 0 : 1;       /* 0 == pass */
    506 }