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