libmicrohttpd

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

test_long_header.c (14909B)


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