libmicrohttpd

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

test_https_get_iovec.c (11436B)


      1 /*
      2   This file is part of libmicrohttpd
      3   Copyright (C) 2007-2021 Christian Grothoff
      4   Copyright (C) 2016-2022 Evgeny Grin
      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 3, 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_https_get_iovec.c
     24  * @brief  Testcase for libmicrohttpd HTTPS GET operations using an iovec
     25  * @author Sagie Amir
     26  * @author Karlson2k (Evgeny Grin)
     27  * @author Lawrence Sebald
     28  */
     29 
     30 /*
     31  * This testcase is derived from the test_https_get.c testcase. This version
     32  * adds the usage of a scatter/gather array for storing the response data.
     33  */
     34 
     35 #include "platform.h"
     36 #include "microhttpd.h"
     37 #include <limits.h>
     38 #include <sys/stat.h>
     39 #include <curl/curl.h>
     40 #ifdef MHD_HTTPS_REQUIRE_GCRYPT
     41 #include <gcrypt.h>
     42 #endif /* MHD_HTTPS_REQUIRE_GCRYPT */
     43 #include "tls_test_common.h"
     44 #include "tls_test_keys.h"
     45 
     46 
     47 static uint16_t global_port;
     48 
     49 /* Use large enough pieces (>16KB) to test partially consumed
     50  * data as TLS doesn't take more than 16KB by a single call. */
     51 #define TESTSTR_IOVLEN 20480
     52 #define TESTSTR_IOVCNT 30
     53 #define TESTSTR_SIZE   (TESTSTR_IOVCNT * TESTSTR_IOVLEN)
     54 
     55 
     56 static void
     57 iov_free_callback (void *cls)
     58 {
     59   free (cls);
     60 }
     61 
     62 
     63 static int
     64 check_read_data (const void *ptr, size_t len)
     65 {
     66   const int *buf;
     67   size_t i;
     68 
     69   if (len % sizeof(int))
     70     return -1;
     71 
     72   buf = (const int *) ptr;
     73 
     74   for (i = 0; i < len / sizeof(int); ++i)
     75   {
     76     if (buf[i] != (int) i)
     77       return -1;
     78   }
     79 
     80   return 0;
     81 }
     82 
     83 
     84 static enum MHD_Result
     85 iovec_ahc (void *cls,
     86            struct MHD_Connection *connection,
     87            const char *url,
     88            const char *method,
     89            const char *version,
     90            const char *upload_data,
     91            size_t *upload_data_size,
     92            void **req_cls)
     93 {
     94   static int aptr;
     95   struct MHD_Response *response;
     96   enum MHD_Result ret;
     97   int *data;
     98   struct MHD_IoVec iov[TESTSTR_IOVCNT];
     99   int i;
    100   int j;
    101   (void) cls; (void) url; (void) version;          /* Unused. Silent compiler warning. */
    102   (void) upload_data; (void) upload_data_size;     /* Unused. Silent compiler warning. */
    103 
    104   if (0 != strcmp (method, MHD_HTTP_METHOD_GET))
    105     return MHD_NO;              /* unexpected method */
    106   if (&aptr != *req_cls)
    107   {
    108     /* do never respond on first call */
    109     *req_cls = &aptr;
    110     return MHD_YES;
    111   }
    112   *req_cls = NULL;                  /* reset when done */
    113 
    114   /* Create some test data. */
    115   if (NULL == (data = malloc (TESTSTR_SIZE)))
    116     return MHD_NO;
    117 
    118   for (j = 0; j < TESTSTR_IOVCNT; ++j)
    119   {
    120     int *chunk;
    121     /* Assign chunks of memory area in the reverse order
    122      * to make non-continous set of data therefore
    123      * possible buffer overruns could be detected */
    124     chunk = data + (((TESTSTR_IOVCNT - 1) - (unsigned int) j)
    125                     * (TESTSTR_SIZE / TESTSTR_IOVCNT / sizeof(int)));
    126     iov[j].iov_base = chunk;
    127     iov[j].iov_len = TESTSTR_SIZE / TESTSTR_IOVCNT;
    128 
    129     for (i = 0; i < (int) (TESTSTR_IOVLEN / sizeof(int)); ++i)
    130       chunk[i] = i + (j * (int) (TESTSTR_IOVLEN / sizeof(int)));
    131   }
    132 
    133   response = MHD_create_response_from_iovec (iov,
    134                                              TESTSTR_IOVCNT,
    135                                              &iov_free_callback,
    136                                              data);
    137   ret = MHD_queue_response (connection, MHD_HTTP_OK, response);
    138   MHD_destroy_response (response);
    139   return ret;
    140 }
    141 
    142 
    143 static unsigned int
    144 test_iovec_transfer (void *cls,
    145                      uint16_t port,
    146                      const char *cipher_suite,
    147                      int proto_version)
    148 {
    149   size_t len;
    150   unsigned int ret = 0;
    151   struct CBC cbc;
    152   char url[255];
    153   (void) cls;    /* Unused. Silent compiler warning. */
    154 
    155   len = TESTSTR_SIZE;
    156   if (NULL == (cbc.buf = malloc (sizeof (char) * len)))
    157   {
    158     fprintf (stderr, MHD_E_MEM);
    159     return 1;
    160   }
    161   cbc.size = len;
    162   cbc.pos = 0;
    163 
    164   if (gen_test_uri (url,
    165                     sizeof (url),
    166                     port))
    167   {
    168     ret = 1;
    169     goto cleanup;
    170   }
    171 
    172   if (CURLE_OK !=
    173       send_curl_req (url, &cbc, cipher_suite, proto_version))
    174   {
    175     ret = 1;
    176     goto cleanup;
    177   }
    178 
    179   if ((cbc.pos != TESTSTR_SIZE) ||
    180       (0 != check_read_data (cbc.buf, cbc.pos)))
    181   {
    182     fprintf (stderr, "Error: local file & received file differ.\n");
    183     ret = 1;
    184   }
    185 cleanup:
    186   free (cbc.buf);
    187   return ret;
    188 }
    189 
    190 
    191 /* perform a HTTP GET request via SSL/TLS */
    192 static unsigned int
    193 test_secure_get (FILE *test_fd,
    194                  const char *cipher_suite,
    195                  int proto_version)
    196 {
    197   unsigned int ret;
    198   struct MHD_Daemon *d;
    199   uint16_t port;
    200 
    201   if (MHD_NO != MHD_is_feature_supported (MHD_FEATURE_AUTODETECT_BIND_PORT))
    202     port = 0;
    203   else
    204     port = 3045;
    205 
    206   d = MHD_start_daemon (MHD_USE_THREAD_PER_CONNECTION
    207                         | MHD_USE_INTERNAL_POLLING_THREAD | MHD_USE_TLS
    208                         | MHD_USE_ERROR_LOG, port,
    209                         NULL, NULL,
    210                         &iovec_ahc, NULL,
    211                         MHD_OPTION_HTTPS_MEM_KEY, srv_signed_key_pem,
    212                         MHD_OPTION_HTTPS_MEM_CERT, srv_signed_cert_pem,
    213                         MHD_OPTION_END);
    214 
    215   if (d == NULL)
    216   {
    217     fprintf (stderr, MHD_E_SERVER_INIT);
    218     return 1;
    219   }
    220   if (0 == port)
    221   {
    222     const union MHD_DaemonInfo *dinfo;
    223     dinfo = MHD_get_daemon_info (d, MHD_DAEMON_INFO_BIND_PORT);
    224     if ((NULL == dinfo) || (0 == dinfo->port) )
    225     {
    226       MHD_stop_daemon (d);
    227       return 1;
    228     }
    229     port = dinfo->port;
    230   }
    231 
    232   ret = test_iovec_transfer (test_fd,
    233                              port,
    234                              cipher_suite,
    235                              proto_version);
    236 
    237   MHD_stop_daemon (d);
    238   return ret;
    239 }
    240 
    241 
    242 static enum MHD_Result
    243 ahc_empty (void *cls,
    244            struct MHD_Connection *connection,
    245            const char *url,
    246            const char *method,
    247            const char *version,
    248            const char *upload_data,
    249            size_t *upload_data_size,
    250            void **req_cls)
    251 {
    252   static int ptr;
    253   struct MHD_Response *response;
    254   enum MHD_Result ret;
    255   struct MHD_IoVec iov;
    256   (void) cls;
    257   (void) url;
    258   (void) url;
    259   (void) version;          /* Unused. Silent compiler warning. */
    260   (void) upload_data;
    261   (void) upload_data_size; /* Unused. Silent compiler warning. */
    262 
    263   if (0 != strcmp (MHD_HTTP_METHOD_GET,
    264                    method))
    265     return MHD_NO;              /* unexpected method */
    266   if (&ptr != *req_cls)
    267   {
    268     *req_cls = &ptr;
    269     return MHD_YES;
    270   }
    271   *req_cls = NULL;
    272 
    273   iov.iov_base = NULL;
    274   iov.iov_len = 0;
    275 
    276   response = MHD_create_response_from_iovec (&iov,
    277                                              1,
    278                                              NULL,
    279                                              NULL);
    280   ret = MHD_queue_response (connection,
    281                             MHD_HTTP_OK,
    282                             response);
    283   MHD_destroy_response (response);
    284   if (ret == MHD_NO)
    285   {
    286     fprintf (stderr, "Failed to queue response.\n");
    287     _exit (20);
    288   }
    289   return ret;
    290 }
    291 
    292 
    293 static int
    294 curlExcessFound (CURL *c,
    295                  curl_infotype type,
    296                  char *data,
    297                  size_t size,
    298                  void *cls)
    299 {
    300   static const char *excess_found = "Excess found";
    301   const size_t str_size = strlen (excess_found);
    302   (void) c;      /* Unused. Silence compiler warning. */
    303 
    304 #ifdef _DEBUG
    305   if ((CURLINFO_TEXT == type) ||
    306       (CURLINFO_HEADER_IN == type) ||
    307       (CURLINFO_HEADER_OUT == type))
    308     fprintf (stderr, "%.*s", (int) size, data);
    309 #endif /* _DEBUG */
    310   if ((CURLINFO_TEXT == type)
    311       && (size >= str_size)
    312       && (0 == strncmp (excess_found, data, str_size)))
    313     *(int *) cls = 1;
    314   return 0;
    315 }
    316 
    317 
    318 static unsigned int
    319 testEmptyGet (unsigned int poll_flag)
    320 {
    321   struct MHD_Daemon *d;
    322   CURL *c;
    323   char buf[2048];
    324   struct CBC cbc;
    325   CURLcode errornum;
    326   int excess_found = 0;
    327 
    328 
    329   if ( (0 == global_port) &&
    330        (MHD_NO == MHD_is_feature_supported (MHD_FEATURE_AUTODETECT_BIND_PORT)) )
    331   {
    332     global_port = 1225;
    333 
    334   }
    335 
    336   cbc.buf = buf;
    337   cbc.size = 2048;
    338   cbc.pos = 0;
    339   d = MHD_start_daemon (MHD_USE_INTERNAL_POLLING_THREAD | MHD_USE_ERROR_LOG
    340                         | poll_flag | MHD_USE_TLS,
    341                         global_port, NULL, NULL,
    342                         &ahc_empty, NULL,
    343                         MHD_OPTION_HTTPS_MEM_KEY, srv_signed_key_pem,
    344                         MHD_OPTION_HTTPS_MEM_CERT, srv_signed_cert_pem,
    345                         MHD_OPTION_END);
    346   if (d == NULL)
    347     return 4194304;
    348   if (0 == global_port)
    349   {
    350     const union MHD_DaemonInfo *dinfo;
    351     dinfo = MHD_get_daemon_info (d, MHD_DAEMON_INFO_BIND_PORT);
    352     if ((NULL == dinfo) || (0 == dinfo->port) )
    353     {
    354       MHD_stop_daemon (d); return 32;
    355     }
    356     global_port = dinfo->port;
    357   }
    358   c = curl_easy_init ();
    359 #ifdef _DEBUG
    360   curl_easy_setopt (c, CURLOPT_VERBOSE, 1L);
    361 #endif
    362   curl_easy_setopt (c, CURLOPT_URL, "https://127.0.0.1/");
    363   curl_easy_setopt (c, CURLOPT_PORT, (long) global_port);
    364   curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
    365   curl_easy_setopt (c, CURLOPT_WRITEFUNCTION, &copyBuffer);
    366   curl_easy_setopt (c, CURLOPT_WRITEDATA, &cbc);
    367   curl_easy_setopt (c, CURLOPT_DEBUGFUNCTION, &curlExcessFound);
    368   curl_easy_setopt (c, CURLOPT_DEBUGDATA, &excess_found);
    369   curl_easy_setopt (c, CURLOPT_VERBOSE, 1L);
    370   curl_easy_setopt (c, CURLOPT_FAILONERROR, 1L);
    371   curl_easy_setopt (c, CURLOPT_TIMEOUT, 150L);
    372   curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT, 150L);
    373   curl_easy_setopt (c, CURLOPT_SSL_VERIFYPEER, 0L);
    374   curl_easy_setopt (c, CURLOPT_SSL_VERIFYHOST, 0L);
    375   /* NOTE: use of CONNECTTIMEOUT without also
    376      setting NOSIGNAL results in really weird
    377      crashes on my system!*/
    378   curl_easy_setopt (c, CURLOPT_NOSIGNAL, 1L);
    379   if (CURLE_OK != (errornum = curl_easy_perform (c)))
    380   {
    381     fprintf (stderr,
    382              "curl_easy_perform failed: `%s'\n",
    383              curl_easy_strerror (errornum));
    384     curl_easy_cleanup (c);
    385     MHD_stop_daemon (d);
    386     return 8388608;
    387   }
    388   curl_easy_cleanup (c);
    389   MHD_stop_daemon (d);
    390   if (cbc.pos != 0)
    391     return 16777216;
    392   if (excess_found)
    393     return 33554432;
    394   return 0;
    395 }
    396 
    397 
    398 int
    399 main (int argc, char *const *argv)
    400 {
    401   unsigned int errorCount = 0;
    402   (void) argc; (void) argv;   /* Unused. Silent compiler warning. */
    403 
    404 #ifdef MHD_HTTPS_REQUIRE_GCRYPT
    405   gcry_control (GCRYCTL_ENABLE_QUICK_RANDOM, 0);
    406 #ifdef GCRYCTL_INITIALIZATION_FINISHED
    407   gcry_control (GCRYCTL_INITIALIZATION_FINISHED, 0);
    408 #endif
    409 #endif /* MHD_HTTPS_REQUIRE_GCRYPT */
    410   if (! testsuite_curl_global_init ())
    411     return 99;
    412   if (NULL == curl_version_info (CURLVERSION_NOW)->ssl_version)
    413   {
    414     fprintf (stderr, "Curl does not support SSL.  Cannot run the test.\n");
    415     curl_global_cleanup ();
    416     return 77;
    417   }
    418 
    419   errorCount +=
    420     test_secure_get (NULL, NULL, CURL_SSLVERSION_DEFAULT);
    421   errorCount += testEmptyGet (0);
    422   curl_global_cleanup ();
    423 
    424   return errorCount != 0 ? 1 : 0;
    425 }