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 ©Buffer)) 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 = ▮ 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 }