stream_process_request.c (144919B)
1 /* SPDX-License-Identifier: LGPL-2.1-or-later OR (GPL-2.0-or-later WITH eCos-exception-2.0) */ 2 /* 3 This file is part of GNU libmicrohttpd. 4 Copyright (C) 2014-2024 Evgeny Grin (Karlson2k) 5 Copyright (C) 2007-2020 Daniel Pittman and Christian Grothoff 6 7 GNU libmicrohttpd is free software; you can redistribute it and/or 8 modify it under the terms of the GNU Lesser General Public 9 License as published by the Free Software Foundation; either 10 version 2.1 of the License, or (at your option) any later version. 11 12 GNU libmicrohttpd is distributed in the hope that it will be useful, 13 but WITHOUT ANY WARRANTY; without even the implied warranty of 14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 15 Lesser General Public License for more details. 16 17 Alternatively, you can redistribute GNU libmicrohttpd and/or 18 modify it under the terms of the GNU General Public License as 19 published by the Free Software Foundation; either version 2 of 20 the License, or (at your option) any later version, together 21 with the eCos exception, as follows: 22 23 As a special exception, if other files instantiate templates or 24 use macros or inline functions from this file, or you compile this 25 file and link it with other works to produce a work based on this 26 file, this file does not by itself cause the resulting work to be 27 covered by the GNU General Public License. However the source code 28 for this file must still be made available in accordance with 29 section (3) of the GNU General Public License v2. 30 31 This exception does not invalidate any other reasons why a work 32 based on this file might be covered by the GNU General Public 33 License. 34 35 You should have received copies of the GNU Lesser General Public 36 License and the GNU General Public License along with this library; 37 if not, see <https://www.gnu.org/licenses/>. 38 */ 39 40 /** 41 * @file src/mhd2/stream_process_request.c 42 * @brief The implementation of internal functions for requests parsing 43 * and processing 44 * @author Karlson2k (Evgeny Grin) 45 * 46 * Based on the MHD v0.x code by Daniel Pittman, Christian Grothoff and other 47 * contributors. 48 */ 49 50 #include "mhd_sys_options.h" 51 #include "stream_process_request.h" 52 53 #include "sys_bool_type.h" 54 #include "sys_base_types.h" 55 56 #include "mhd_assert.h" 57 #include "mhd_unreachable.h" 58 59 #include "sys_malloc.h" 60 61 #ifdef mhd_DEBUG_SUSPEND_RESUME 62 # include <stdio.h> 63 #endif /* mhd_DEBUG_SUSPEND_RESUME */ 64 65 #include "mhd_str_types.h" 66 #include "mhd_str_macros.h" 67 #include "mhd_str.h" 68 69 #include <string.h> 70 71 #include "mhd_daemon.h" 72 #include "mhd_connection.h" 73 74 #include "daemon_logger.h" 75 #include "mhd_panic.h" 76 77 #include "mempool_funcs.h" 78 79 #include "response_destroy.h" 80 #include "request_funcs.h" 81 #include "request_get_value.h" 82 #include "respond_with_error.h" 83 #include "stream_funcs.h" 84 #include "daemon_funcs.h" 85 86 #ifdef MHD_SUPPORT_POST_PARSER 87 # include "post_parser_funcs.h" 88 #endif /* MHD_SUPPORT_POST_PARSER */ 89 90 #include "mhd_public_api.h" 91 92 93 /** 94 * Response text used when the request (http header) is 95 * malformed. 96 */ 97 #define ERR_RSP_REQUEST_MALFORMED \ 98 "<html><head><title>Request malformed</title></head>" \ 99 "<body>HTTP request is syntactically incorrect.</body></html>" 100 101 /** 102 * Response text used when the request HTTP version is too old. 103 */ 104 #define ERR_RSP_REQ_HTTP_VER_IS_TOO_OLD \ 105 "<html>" \ 106 "<head><title>Requested HTTP version is not supported</title></head>" \ 107 "<body>Requested HTTP version is too old and not " \ 108 "supported.</body></html>" 109 /** 110 * Response text used when the request HTTP version is not supported. 111 */ 112 #define ERR_RSP_REQ_HTTP_VER_IS_NOT_SUPPORTED \ 113 "<html>" \ 114 "<head><title>Requested HTTP version is not supported</title></head>" \ 115 "<body>Requested HTTP version is not supported.</body></html>" 116 117 /** 118 * Response text used when the request HTTP header has bare CR character 119 * without LF character (and CR is not allowed to be treated as whitespace). 120 */ 121 #define ERR_RSP_BARE_CR_IN_HEADER \ 122 "<html>" \ 123 "<head><title>Request broken</title></head>" \ 124 "<body>Request HTTP header has bare CR character without " \ 125 "following LF character.</body>" \ 126 "</html>" 127 128 /** 129 * Response text used when the request HTTP footer has bare CR character 130 * without LF character (and CR is not allowed to be treated as whitespace). 131 */ 132 #define ERR_RSP_BARE_CR_IN_FOOTER \ 133 "<html>" \ 134 "<head><title>Request broken</title></head>" \ 135 "<body>Request HTTP footer has bare CR character without " \ 136 "following LF character.</body>" \ 137 "</html>" 138 139 /** 140 * Response text used when the request HTTP header has bare LF character 141 * without CR character. 142 */ 143 #define ERR_RSP_BARE_LF_IN_HEADER \ 144 "<html>" \ 145 "<head><title>Request broken</title></head>" \ 146 "<body>Request HTTP header has bare LF character without " \ 147 "preceding CR character.</body>" \ 148 "</html>" 149 /** 150 * Response text used when the request HTTP footer has bare LF character 151 * without CR character. 152 */ 153 #define ERR_RSP_BARE_LF_IN_FOOTER \ 154 "<html>" \ 155 "<head><title>Request broken</title></head>" \ 156 "<body>Request HTTP footer has bare LF character without " \ 157 "preceding CR character.</body>" \ 158 "</html>" 159 160 /** 161 * Response text used when the request line has more then two whitespaces. 162 */ 163 #define ERR_RSP_RQ_LINE_TOO_MANY_WSP \ 164 "<html>" \ 165 "<head><title>Request broken</title></head>" \ 166 "<body>The request line has more then two whitespaces.</body>" \ 167 "</html>" 168 169 /** 170 * Response text used when the request line has invalid characters in URI. 171 */ 172 #define ERR_RSP_RQ_TARGET_INVALID_CHAR \ 173 "<html>" \ 174 "<head><title>Request broken</title></head>" \ 175 "<body>HTTP request has invalid characters in " \ 176 "the request-target.</body>" \ 177 "</html>" 178 179 /** 180 * Response text used when line folding is used in request headers. 181 */ 182 #define ERR_RSP_OBS_FOLD \ 183 "<html>" \ 184 "<head><title>Request broken</title></head>" \ 185 "<body>Obsolete line folding is used in HTTP request header.</body>" \ 186 "</html>" 187 188 /** 189 * Response text used when line folding is used in request footers. 190 */ 191 #define ERR_RSP_OBS_FOLD_FOOTER \ 192 "<html>" \ 193 "<head><title>Request broken</title></head>" \ 194 "<body>Obsolete line folding is used in HTTP request footer.</body>" \ 195 "</html>" 196 197 /** 198 * Response text used when request header has no colon character. 199 */ 200 #define ERR_RSP_HEADER_WITHOUT_COLON \ 201 "<html>" \ 202 "<head><title>Request broken</title></head>" \ 203 "<body>HTTP request header line has no colon character.</body>" \ 204 "</html>" 205 206 /** 207 * Response text used when request footer has no colon character. 208 */ 209 #define ERR_RSP_FOOTER_WITHOUT_COLON \ 210 "<html>" \ 211 "<head><title>Request broken</title></head>" \ 212 "<body>HTTP request footer line has no colon character.</body>" \ 213 "</html>" 214 /** 215 * Response text used when the request has whitespace at the start 216 * of the first header line. 217 */ 218 #define ERR_RSP_WSP_BEFORE_HEADER \ 219 "<html>" \ 220 "<head><title>Request broken</title></head>" \ 221 "<body>HTTP request has whitespace between the request line and " \ 222 "the first header.</body>" \ 223 "</html>" 224 225 /** 226 * Response text used when the request has whitespace at the start 227 * of the first footer line. 228 */ 229 #define ERR_RSP_WSP_BEFORE_FOOTER \ 230 "<html>" \ 231 "<head><title>Request broken</title></head>" \ 232 "<body>First HTTP footer line has whitespace at the first " \ 233 "position.</body>" \ 234 "</html>" 235 236 /** 237 * Response text used when the whitespace found before colon (inside header 238 * name or between header name and colon). 239 */ 240 #define ERR_RSP_WSP_IN_HEADER_NAME \ 241 "<html>" \ 242 "<head><title>Request broken</title></head>" \ 243 "<body>HTTP request has whitespace before the first colon " \ 244 "in header line.</body>" \ 245 "</html>" 246 247 /** 248 * Response text used when the whitespace found before colon (inside header 249 * name or between header name and colon). 250 */ 251 #define ERR_RSP_WSP_IN_FOOTER_NAME \ 252 "<html>" \ 253 "<head><title>Request broken</title></head>" \ 254 "<body>HTTP request has whitespace before the first colon " \ 255 "in footer line.</body>" \ 256 "</html>" 257 /** 258 * Response text used when request header has invalid character. 259 */ 260 #define ERR_RSP_INVALID_CHR_IN_HEADER \ 261 "<html>" \ 262 "<head><title>Request broken</title></head>" \ 263 "<body>HTTP request has invalid character in header.</body>" \ 264 "</html>" 265 266 /** 267 * Response text used when request header has invalid character. 268 */ 269 #define ERR_RSP_INVALID_CHR_IN_FOOTER \ 270 "<html>" \ 271 "<head><title>Request broken</title></head>" \ 272 "<body>HTTP request has invalid character in footer.</body>" \ 273 "</html>" 274 275 /** 276 * Response text used when request header has zero-length header (filed) name. 277 */ 278 #define ERR_RSP_EMPTY_HEADER_NAME \ 279 "<html>" \ 280 "<head><title>Request broken</title></head>" \ 281 "<body>HTTP request header has empty header name.</body>" \ 282 "</html>" 283 284 /** 285 * Response text used when request header has zero-length header (filed) name. 286 */ 287 #define ERR_RSP_EMPTY_FOOTER_NAME \ 288 "<html>" \ 289 "<head><title>Request broken</title></head>" \ 290 "<body>HTTP request footer has empty footer name.</body>" \ 291 "</html>" 292 293 /** 294 * Response text used when the request header is too big to be processed. 295 */ 296 #define ERR_RSP_REQUEST_HEADER_TOO_BIG \ 297 "<html>" \ 298 "<head><title>Request too big</title></head>" \ 299 "<body><p>The total size of the request headers, which includes the " \ 300 "request target and the request field lines, exceeds the memory " \ 301 "constraints of this web server.</p>" \ 302 "<p>The request could be re-tried with shorter field lines, a shorter " \ 303 "request target or a shorter request method token.</p></body>" \ 304 "</html>" 305 306 /** 307 * Response text used when the request header is too big to be processed. 308 */ 309 #define ERR_RSP_REQUEST_FOOTER_TOO_BIG \ 310 "<html>" \ 311 "<head><title>Request too big</title></head>" \ 312 "<body><p>The total size of the request headers, which includes the " \ 313 "request target, the request field lines and the chunked trailer " \ 314 "section exceeds the memory constraints of this web server.</p>" \ 315 "<p>The request could be re-tried with a shorter chunked trailer " \ 316 "section, shorter field lines, a shorter request target or " \ 317 "a shorter request method token.</p></body>" \ 318 "</html>" 319 320 /** 321 * Response text used when the request (http header) is too big to 322 * be processed. 323 */ 324 #define ERR_RSP_MSG_REQUEST_TOO_BIG \ 325 "<html>" \ 326 "<head><title>Request too big</title></head>" \ 327 "<body>Request HTTP header is too big for the memory constraints " \ 328 "of this webserver.</body>" \ 329 "</html>" 330 /** 331 * Response text used when the request chunk size line with chunk extension 332 * cannot fit the buffer. 333 */ 334 #define ERR_RSP_REQUEST_CHUNK_LINE_EXT_TOO_BIG \ 335 "<html>" \ 336 "<head><title>Request too big</title></head>" \ 337 "<body><p>The total size of the request target, the request field lines " \ 338 "and the chunk size line exceeds the memory constraints of this web " \ 339 "server.</p>" \ 340 "<p>The request could be re-tried without chunk extensions, with a smaller " \ 341 "chunk size, shorter field lines, a shorter request target or a shorter " \ 342 "request method token.</p></body>" \ 343 "</html>" 344 345 /** 346 * Response text used when the request chunk size line without chunk extension 347 * cannot fit the buffer. 348 */ 349 #define ERR_RSP_REQUEST_CHUNK_LINE_TOO_BIG \ 350 "<html>" \ 351 "<head><title>Request too big</title></head>" \ 352 "<body><p>The total size of the request target, the request field lines " \ 353 "and the chunk size line exceeds the memory constraints of this web " \ 354 "server.</p>" \ 355 "<p>The request could be re-tried with a smaller " \ 356 "chunk size, shorter field lines, a shorter request target or a shorter " \ 357 "request method token.</p></body>" \ 358 "</html>" 359 360 /** 361 * Response text used when the request (http header) does not 362 * contain a "Host:" header and still claims to be HTTP 1.1. 363 */ 364 #define ERR_RSP_REQUEST_LACKS_HOST \ 365 "<html>" \ 366 "<head><title>"Host:" header required</title></head>" \ 367 "<body>HTTP/1.1 request without <b>"Host:"</b>.</body>" \ 368 "</html>" 369 370 /** 371 * Response text used when the request has more than one "Host:" header. 372 */ 373 #define ERR_RSP_REQUEST_HAS_SEVERAL_HOSTS \ 374 "<html>" \ 375 "<head>" \ 376 "<title>Several "Host:" headers used</title></head>" \ 377 "<body>" \ 378 "Request with more than one <b>"Host:"</b> header.</body>" \ 379 "</html>" 380 381 /** 382 * Response text used when the request has unsupported "Transfer-Encoding:". 383 */ 384 #define ERR_RSP_UNSUPPORTED_TR_ENCODING \ 385 "<html>" \ 386 "<head><title>Unsupported Transfer-Encoding</title></head>" \ 387 "<body>The Transfer-Encoding used in request is not supported.</body>" \ 388 "</html>" 389 390 /** 391 * Response text used when the request has unsupported "Expect:" value. 392 */ 393 #define ERR_RSP_UNSUPPORTED_EXPECT_HDR_VALUE \ 394 "<html>" \ 395 "<head><title>Unsupported 'Expect:'</title></head>" \ 396 "<body>The value of 'Expect:' header used in the request is " \ 397 "not supported.</body>" \ 398 "</html>" 399 400 /** 401 * Response text used when the request has unsupported both headers: 402 * "Transfer-Encoding:" and "Content-Length:" 403 */ 404 #define ERR_RSP_REQUEST_CNTNLENGTH_WITH_TR_ENCODING \ 405 "<html>" \ 406 "<head><title>Malformed request</title></head>" \ 407 "<body>Wrong combination of the request headers: both Transfer-Encoding " \ 408 "and Content-Length headers are used at the same time.</body>" \ 409 "</html>" 410 411 /** 412 * Response text used when the request HTTP content is too large. 413 */ 414 #define ERR_RSP_REQUEST_CONTENTLENGTH_TOOLARGE \ 415 "<html><head><title>Request content too large</title></head>" \ 416 "<body>HTTP request has too large value for " \ 417 "<b>Content-Length</b> header.</body></html>" 418 419 /** 420 * Response text used when the request HTTP chunked encoding is 421 * malformed. 422 */ 423 #define ERR_RSP_REQUEST_CONTENTLENGTH_MALFORMED \ 424 "<html><head><title>Request malformed</title></head>" \ 425 "<body>HTTP request has wrong value for " \ 426 "<b>Content-Length</b> header.</body></html>" 427 428 /** 429 * Response text used when the request has more than one "Content-Length:" 430 * header. 431 */ 432 #define ERR_RSP_REQUEST_CONTENTLENGTH_SEVERAL \ 433 "<html><head><title>Request malformed</title></head>" \ 434 "<body>HTTP request has several " \ 435 "<b>Content-Length</b> headers.</body></html>" 436 437 /** 438 * Response text used when the request HTTP chunked encoding is 439 * malformed. 440 */ 441 #define ERR_RSP_REQUEST_CHUNKED_MALFORMED \ 442 "<html><head><title>Request malformed</title></head>" \ 443 "<body>HTTP chunked encoding is syntactically incorrect.</body></html>" 444 445 /** 446 * Response text used when the request HTTP chunk is too large. 447 */ 448 #define ERR_RSP_REQUEST_CHUNK_TOO_LARGE \ 449 "<html><head><title>Request content too large</title></head>" \ 450 "<body>The chunk size used in HTTP chunked encoded " \ 451 "request is too large.</body></html>" 452 453 454 /** 455 * The reasonable length of the upload chunk "header" (the size specifier 456 * with optional chunk extension). 457 * MHD tries to keep the space in the read buffer large enough to read 458 * the chunk "header" in one step. 459 * The real "header" could be much larger, it will be handled correctly 460 * anyway, however it may require several rounds of buffer grow. 461 */ 462 #define MHD_CHUNK_HEADER_REASONABLE_LEN 24 463 464 /** 465 * The valid length of any HTTP version string 466 */ 467 #define HTTP_VER_LEN (mhd_SSTR_LEN (MHD_HTTP_VERSION_1_1_STR)) 468 469 470 /** 471 * Parse HTTP method string. 472 * @param len the length of the @a mtd string 473 * @param mtd the method string, does not need to be zero-terminated 474 * @return enum mhd_HTTP_Method value 475 */ 476 MHD_INTERNAL MHD_FN_PAR_NONNULL_ALL_ 477 MHD_FN_PAR_IN_SIZE_ (2,1) 478 MHD_FN_PURE_ enum mhd_HTTP_Method 479 mhd_parse_http_method (size_t len, 480 const char mtd[MHD_FN_PAR_DYN_ARR_SIZE_ (len)]) 481 { 482 switch (len) 483 { 484 case 3: /* mhd_SSTR_LEN (MHD_HTTP_METHOD_STR_GET) */ 485 /* mhd_SSTR_LEN (MHD_HTTP_METHOD_STR_PUT) */ 486 mhd_assert (len == mhd_SSTR_LEN (MHD_HTTP_METHOD_STR_GET)); 487 mhd_assert (len == mhd_SSTR_LEN (MHD_HTTP_METHOD_STR_PUT)); 488 if (0 == memcmp (mtd, 489 MHD_HTTP_METHOD_STR_GET, 490 mhd_SSTR_LEN (MHD_HTTP_METHOD_STR_GET))) 491 return mhd_HTTP_METHOD_GET; 492 else if (0 == memcmp (mtd, 493 MHD_HTTP_METHOD_STR_PUT, 494 mhd_SSTR_LEN (MHD_HTTP_METHOD_STR_PUT))) 495 return mhd_HTTP_METHOD_PUT; 496 break; 497 case 4: /* mhd_SSTR_LEN (MHD_HTTP_METHOD_STR_HEAD) */ 498 /* mhd_SSTR_LEN (MHD_HTTP_METHOD_STR_POST) */ 499 mhd_assert (len == mhd_SSTR_LEN (MHD_HTTP_METHOD_STR_HEAD)); 500 mhd_assert (len == mhd_SSTR_LEN (MHD_HTTP_METHOD_STR_POST)); 501 if (0 == memcmp (mtd, 502 MHD_HTTP_METHOD_STR_HEAD, 503 mhd_SSTR_LEN (MHD_HTTP_METHOD_STR_HEAD))) 504 return mhd_HTTP_METHOD_HEAD; 505 else if (0 == memcmp (mtd, 506 MHD_HTTP_METHOD_STR_POST, 507 mhd_SSTR_LEN (MHD_HTTP_METHOD_STR_POST))) 508 return mhd_HTTP_METHOD_POST; 509 break; 510 case 6: /* mhd_SSTR_LEN (MHD_HTTP_METHOD_STR_DELETE) */ 511 mhd_assert (len == mhd_SSTR_LEN (MHD_HTTP_METHOD_STR_DELETE)); 512 if (0 == memcmp (mtd, 513 MHD_HTTP_METHOD_STR_DELETE, 514 mhd_SSTR_LEN (MHD_HTTP_METHOD_STR_DELETE))) 515 return mhd_HTTP_METHOD_DELETE; 516 break; 517 case 7: /* mhd_SSTR_LEN (MHD_HTTP_METHOD_STR_CONNECT) */ 518 /* mhd_SSTR_LEN (MHD_HTTP_METHOD_STR_OPTIONS) */ 519 mhd_assert (len == mhd_SSTR_LEN (MHD_HTTP_METHOD_STR_CONNECT)); 520 mhd_assert (len == mhd_SSTR_LEN (MHD_HTTP_METHOD_STR_OPTIONS)); 521 if (0 == memcmp (mtd, 522 MHD_HTTP_METHOD_STR_CONNECT, 523 mhd_SSTR_LEN (MHD_HTTP_METHOD_STR_CONNECT))) 524 return mhd_HTTP_METHOD_CONNECT; 525 else if (0 == memcmp (mtd, 526 MHD_HTTP_METHOD_STR_OPTIONS, 527 mhd_SSTR_LEN (MHD_HTTP_METHOD_STR_OPTIONS))) 528 return mhd_HTTP_METHOD_OPTIONS; 529 break; 530 case 5: /* mhd_SSTR_LEN (MHD_HTTP_METHOD_STR_TRACE) */ 531 mhd_assert (len == mhd_SSTR_LEN (MHD_HTTP_METHOD_STR_TRACE)); 532 if (0 == memcmp (mtd, 533 MHD_HTTP_METHOD_STR_TRACE, 534 mhd_SSTR_LEN (MHD_HTTP_METHOD_STR_TRACE))) 535 return mhd_HTTP_METHOD_TRACE; 536 break; 537 case 1: /* mhd_SSTR_LEN (MHD_HTTP_METHOD_STR_ASTERISK) */ 538 mhd_assert (len == mhd_SSTR_LEN (MHD_HTTP_METHOD_STR_ASTERISK)); 539 if ('*' == mtd[0]) 540 return mhd_HTTP_METHOD_ASTERISK; 541 break; 542 default: 543 break; /* Handled after the "switch()" body */ 544 } 545 return mhd_HTTP_METHOD_OTHER; 546 } 547 548 549 /** 550 * Detect standard HTTP request method 551 * 552 * @param connection the connection to process 553 */ 554 static MHD_FN_PAR_NONNULL_ALL_ void 555 parse_http_std_method (struct MHD_Connection *restrict connection) 556 { 557 const char *const restrict m = connection->rq.method.cstr; /**< short alias */ 558 const size_t len = connection->rq.method.len; /**< short alias */ 559 mhd_assert (NULL != m); 560 mhd_assert (0 != len); 561 562 connection->rq.http_mthd = mhd_parse_http_method (len, 563 m); 564 } 565 566 567 /** 568 * Detect HTTP version, send error response if version is not supported 569 * 570 * @param connection the connection 571 * @param http_string the pointer to HTTP version string 572 * @param len the length of @a http_string in bytes 573 * @return true if HTTP version is correct and supported, 574 * false if HTTP version is not correct or unsupported. 575 */ 576 static bool 577 parse_http_version (struct MHD_Connection *restrict connection, 578 const char *restrict http_string, 579 size_t len) 580 { 581 const char *const h = http_string; /**< short alias */ 582 mhd_assert (NULL != http_string); 583 584 /* String must start with 'HTTP/d.d', case-sensetive match. 585 * See https://www.rfc-editor.org/rfc/rfc9112#name-http-version */ 586 if ((HTTP_VER_LEN != len) || 587 ('H' != h[0]) || ('T' != h[1]) || ('T' != h[2]) || ('P' != h[3]) || 588 ('/' != h[4]) 589 || ('.' != h[6])) 590 { 591 connection->rq.http_ver = MHD_HTTP_VERSION_INVALID; 592 mhd_RESPOND_WITH_ERROR_STATIC (connection, 593 MHD_HTTP_STATUS_BAD_REQUEST, 594 ERR_RSP_REQUEST_MALFORMED); 595 return false; 596 } 597 if (1 == h[5] - '0') 598 { 599 /* HTTP/1.x */ 600 if (1 == h[7] - '0') 601 { 602 connection->rq.http_ver = MHD_HTTP_VERSION_1_1; 603 return true; 604 } 605 else if (0 == h[7] - '0') 606 { 607 connection->rq.http_ver = MHD_HTTP_VERSION_1_0; 608 return true; 609 } 610 else 611 connection->rq.http_ver = MHD_HTTP_VERSION_INVALID; 612 613 } 614 else if (0 == h[5] - '0') 615 { 616 /* Too old major version */ 617 connection->rq.http_ver = MHD_HTTP_VERSION_INVALID; 618 mhd_RESPOND_WITH_ERROR_STATIC (connection, 619 MHD_HTTP_STATUS_HTTP_VERSION_NOT_SUPPORTED, 620 ERR_RSP_REQ_HTTP_VER_IS_TOO_OLD); 621 return false; 622 } 623 else if ((2 == h[5] - '0') && ('0' == h[7] - '0')) 624 connection->rq.http_ver = MHD_HTTP_VERSION_2; 625 else 626 connection->rq.http_ver = MHD_HTTP_VERSION_INVALID; 627 628 mhd_RESPOND_WITH_ERROR_STATIC (connection, 629 MHD_HTTP_STATUS_HTTP_VERSION_NOT_SUPPORTED, 630 ERR_RSP_REQ_HTTP_VER_IS_NOT_SUPPORTED); 631 return false; 632 } 633 634 635 #ifndef MHD_MAX_EMPTY_LINES_SKIP 636 /** 637 * The maximum number of ignored empty line before the request line 638 * at default "strictness" level. 639 */ 640 # define MHD_MAX_EMPTY_LINES_SKIP 1024 641 #endif /* ! MHD_MAX_EMPTY_LINES_SKIP */ 642 643 644 /** 645 * Find and parse the request line. 646 * @param c the connection to process 647 * @return true if request line completely processed (or unrecoverable error 648 * found) and state is changed, 649 * false if not enough data yet in the receive buffer 650 */ 651 static MHD_FN_PAR_NONNULL_ALL_ bool 652 get_request_line_inner (struct MHD_Connection *restrict c) 653 { 654 size_t p; /**< The current processing position */ 655 const int discp_lvl = c->daemon->req_cfg.strictness; 656 /* Allow to skip one or more empty lines before the request line. 657 RFC 9112, section 2.2 */ 658 const bool skip_empty_lines = (1 >= discp_lvl); 659 /* Allow to skip more then one empty line before the request line. 660 RFC 9112, section 2.2 */ 661 const bool skip_several_empty_lines = (skip_empty_lines && (0 >= discp_lvl)); 662 /* Allow to skip unlimited number of empty lines before the request line. 663 RFC 9112, section 2.2 */ 664 const bool skip_unlimited_empty_lines = 665 (skip_empty_lines && (-3 >= discp_lvl)); 666 /* Treat bare LF as the end of the line. 667 RFC 9112, section 2.2 */ 668 const bool bare_lf_as_crlf = mhd_ALLOW_BARE_LF_AS_CRLF (discp_lvl); 669 /* Treat tab as whitespace delimiter. 670 RFC 9112, section 3 */ 671 const bool tab_as_wsp = (0 >= discp_lvl); 672 /* Treat VT (vertical tab) and FF (form feed) as whitespace delimiters. 673 RFC 9112, section 3 */ 674 const bool other_wsp_as_wsp = (-1 >= discp_lvl); 675 /* Treat continuous whitespace block as a single space. 676 RFC 9112, section 3 */ 677 const bool wsp_blocks = (-1 >= discp_lvl); 678 /* Parse whitespace in URI, special parsing of the request line. 679 RFC 9112, section 3.2 */ 680 const bool wsp_in_uri = (0 >= discp_lvl); 681 /* Keep whitespace in URI, give app URI with whitespace instead of 682 automatic redirect to fixed URI. 683 Violates RFC 9112, section 3.2 */ 684 const bool wsp_in_uri_keep = (-2 >= discp_lvl); 685 /* Keep bare CR character as is. 686 Violates RFC 9112, section 2.2 */ 687 const bool bare_cr_keep = (wsp_in_uri_keep && (-3 >= discp_lvl)); 688 /* Treat bare CR as space; replace it with space before processing. 689 RFC 9112, section 2.2 */ 690 const bool bare_cr_as_sp = ((! bare_cr_keep) && (-1 >= discp_lvl)); 691 692 mhd_assert (mhd_HTTP_STAGE_INIT == c->stage || \ 693 mhd_HTTP_STAGE_REQ_LINE_RECEIVING == c->stage); 694 mhd_assert (NULL == c->rq.method.cstr || \ 695 mhd_HTTP_STAGE_REQ_LINE_RECEIVING == c->stage); 696 mhd_assert (mhd_HTTP_METHOD_NO_METHOD == c->rq.http_mthd || \ 697 mhd_HTTP_STAGE_REQ_LINE_RECEIVING == c->stage); 698 mhd_assert (mhd_HTTP_METHOD_NO_METHOD == c->rq.http_mthd || \ 699 0 != c->rq.hdrs.rq_line.proc_pos); 700 701 if (0 == c->read_buffer_offset) 702 { 703 mhd_assert (mhd_HTTP_STAGE_INIT == c->stage); 704 return false; /* No data to process */ 705 } 706 p = c->rq.hdrs.rq_line.proc_pos; 707 mhd_assert (p <= c->read_buffer_offset); 708 709 /* Skip empty lines, if any (and if allowed) */ 710 /* See RFC 9112, section 2.2 */ 711 if ((0 == p) 712 && (skip_empty_lines)) 713 { 714 /* Skip empty lines before the request line. 715 See RFC 9112, section 2.2 */ 716 bool is_empty_line; 717 mhd_assert (mhd_HTTP_STAGE_INIT == c->stage); 718 mhd_assert (0 == c->rq.method.len); 719 mhd_assert (NULL == c->rq.method.cstr); 720 mhd_assert (NULL == c->rq.url); 721 mhd_assert (0 == c->rq.url_len); 722 mhd_assert (NULL == c->rq.hdrs.rq_line.rq_tgt); 723 mhd_assert (0 == c->rq.req_target_len); 724 mhd_assert (NULL == c->rq.version); 725 do 726 { 727 is_empty_line = false; 728 if ('\r' == c->read_buffer[0]) 729 { 730 if (1 == c->read_buffer_offset) 731 return false; /* Not enough data yet */ 732 if ('\n' == c->read_buffer[1]) 733 { 734 is_empty_line = true; 735 c->read_buffer += 2; 736 c->read_buffer_size -= 2; 737 c->read_buffer_offset -= 2; 738 c->rq.hdrs.rq_line.skipped_empty_lines++; 739 } 740 } 741 else if (('\n' == c->read_buffer[0]) && 742 (bare_lf_as_crlf)) 743 { 744 is_empty_line = true; 745 c->read_buffer += 1; 746 c->read_buffer_size -= 1; 747 c->read_buffer_offset -= 1; 748 c->rq.hdrs.rq_line.skipped_empty_lines++; 749 } 750 if (is_empty_line) 751 { 752 if ((! skip_unlimited_empty_lines) && 753 (((unsigned int) ((skip_several_empty_lines) ? 754 MHD_MAX_EMPTY_LINES_SKIP : 1)) < 755 c->rq.hdrs.rq_line.skipped_empty_lines)) 756 { 757 mhd_STREAM_ABORT (c, mhd_CONN_CLOSE_CLIENT_HTTP_ERR_ABORT_CONN, 758 "Too many meaningless extra empty lines " \ 759 "received before the request."); 760 return true; /* Process connection closure */ 761 } 762 if (0 == c->read_buffer_offset) 763 return false; /* No more data to process */ 764 } 765 } while (is_empty_line); 766 } 767 /* All empty lines are skipped */ 768 769 c->stage = mhd_HTTP_STAGE_REQ_LINE_RECEIVING; 770 /* Read and parse the request line */ 771 mhd_assert (1 <= c->read_buffer_offset); 772 773 while (p < c->read_buffer_offset) 774 { 775 char *const restrict read_buffer = c->read_buffer; 776 const char chr = read_buffer[p]; 777 bool end_of_line; 778 /* 779 The processing logic is different depending on the configured strictness: 780 781 When whitespace BLOCKS are NOT ALLOWED, the end of the whitespace is 782 processed BEFORE processing of the current character. 783 When whitespace BLOCKS are ALLOWED, the end of the whitespace is 784 processed AFTER processing of the current character. 785 786 When space char in the URI is ALLOWED, the delimiter between the URI and 787 the HTTP version string is processed only at the END of the line. 788 When space in the URI is NOT ALLOWED, the delimiter between the URI and 789 the HTTP version string is processed as soon as the FIRST whitespace is 790 found after URI start. 791 */ 792 793 end_of_line = false; 794 795 mhd_assert ((0 == c->rq.hdrs.rq_line.last_ws_end) || \ 796 (c->rq.hdrs.rq_line.last_ws_end > \ 797 c->rq.hdrs.rq_line.last_ws_start)); 798 mhd_assert ((0 == c->rq.hdrs.rq_line.last_ws_start) || \ 799 (0 != c->rq.hdrs.rq_line.last_ws_end)); 800 801 /* Check for the end of the line */ 802 if ('\r' == chr) 803 { 804 if (p + 1 == c->read_buffer_offset) 805 { 806 c->rq.hdrs.rq_line.proc_pos = p; 807 return false; /* Not enough data yet */ 808 } 809 else if ('\n' == read_buffer[p + 1]) 810 end_of_line = true; 811 else 812 { 813 /* Bare CR alone */ 814 /* Must be rejected or replaced with space char. 815 See RFC 9112, section 2.2 */ 816 if (bare_cr_as_sp) 817 { 818 read_buffer[p] = ' '; 819 c->rq.num_cr_sp_replaced++; 820 continue; /* Re-start processing of the current character */ 821 } 822 else if (! bare_cr_keep) 823 { 824 /* A quick simple check whether this line looks like an HTTP request */ 825 if ((mhd_HTTP_METHOD_GET <= c->rq.http_mthd) && 826 (mhd_HTTP_METHOD_DELETE >= c->rq.http_mthd)) 827 { 828 mhd_RESPOND_WITH_ERROR_STATIC (c, 829 MHD_HTTP_STATUS_BAD_REQUEST, 830 ERR_RSP_BARE_CR_IN_HEADER); 831 } 832 else 833 mhd_STREAM_ABORT (c, mhd_CONN_CLOSE_CLIENT_HTTP_ERR_ABORT_CONN, 834 "Bare CR characters are not allowed " \ 835 "in the request line."); 836 837 return true; /* Error in the request */ 838 } 839 } 840 } 841 else if ('\n' == chr) 842 { 843 /* Bare LF may be recognised as a line delimiter. 844 See RFC 9112, section 2.2 */ 845 if (bare_lf_as_crlf) 846 end_of_line = true; 847 else 848 { 849 /* While RFC does not enforce error for bare LF character, 850 if this char is not treated as a line delimiter, it should be 851 rejected to avoid any security weakness due to request smuggling. */ 852 /* A quick simple check whether this line looks like an HTTP request */ 853 if ((mhd_HTTP_METHOD_GET <= c->rq.http_mthd) && 854 (mhd_HTTP_METHOD_DELETE >= c->rq.http_mthd)) 855 { 856 mhd_RESPOND_WITH_ERROR_STATIC (c, 857 MHD_HTTP_STATUS_BAD_REQUEST, 858 ERR_RSP_BARE_LF_IN_HEADER); 859 } 860 else 861 mhd_STREAM_ABORT (c, mhd_CONN_CLOSE_CLIENT_HTTP_ERR_ABORT_CONN, 862 "Bare LF characters are not allowed " \ 863 "in the request line."); 864 return true; /* Error in the request */ 865 } 866 } 867 868 if (end_of_line) 869 { 870 /* Handle the end of the request line */ 871 872 if (NULL != c->rq.method.cstr) 873 { 874 if (wsp_in_uri) 875 { 876 /* The end of the URI and the start of the HTTP version string 877 should be determined now. */ 878 mhd_assert (NULL == c->rq.version); 879 mhd_assert (0 == c->rq.req_target_len); 880 if (0 != c->rq.hdrs.rq_line.last_ws_end) 881 { 882 /* Determine the end and the length of the URI */ 883 if (NULL != c->rq.hdrs.rq_line.rq_tgt) 884 { 885 read_buffer [c->rq.hdrs.rq_line.last_ws_start] = 0; /* Zero terminate the URI */ 886 c->rq.req_target_len = 887 c->rq.hdrs.rq_line.last_ws_start 888 - (size_t) (c->rq.hdrs.rq_line.rq_tgt - read_buffer); 889 } 890 else if ((c->rq.hdrs.rq_line.last_ws_start + 1 < 891 c->rq.hdrs.rq_line.last_ws_end) && 892 (HTTP_VER_LEN == (p - c->rq.hdrs.rq_line.last_ws_end))) 893 { 894 /* Found only HTTP method and HTTP version and more than one 895 whitespace between them. Assume zero-length URI. */ 896 mhd_assert (wsp_blocks); 897 c->rq.hdrs.rq_line.last_ws_start++; 898 read_buffer[c->rq.hdrs.rq_line.last_ws_start] = 0; /* Zero terminate the URI */ 899 c->rq.hdrs.rq_line.rq_tgt = 900 read_buffer + c->rq.hdrs.rq_line.last_ws_start; 901 c->rq.req_target_len = 0; 902 c->rq.hdrs.rq_line.num_ws_in_uri = 0; 903 c->rq.hdrs.rq_line.rq_tgt_qmark = NULL; 904 } 905 /* Determine the start of the HTTP version string */ 906 if (NULL != c->rq.hdrs.rq_line.rq_tgt) 907 { 908 c->rq.version = read_buffer + c->rq.hdrs.rq_line.last_ws_end; 909 } 910 } 911 } 912 else 913 { 914 /* The end of the URI and the start of the HTTP version string 915 should be already known. */ 916 if ((NULL == c->rq.version) 917 && (NULL != c->rq.hdrs.rq_line.rq_tgt) 918 && (HTTP_VER_LEN == p - (size_t) (c->rq.hdrs.rq_line.rq_tgt 919 - read_buffer)) 920 && (0 != read_buffer[(size_t) 921 (c->rq.hdrs.rq_line.rq_tgt 922 - read_buffer) - 1])) 923 { 924 /* Found only HTTP method and HTTP version and more than one 925 whitespace between them. Assume zero-length URI. */ 926 size_t uri_pos; 927 mhd_assert (wsp_blocks); 928 mhd_assert (0 == c->rq.req_target_len); 929 uri_pos = (size_t) (c->rq.hdrs.rq_line.rq_tgt - read_buffer) - 1; 930 mhd_assert (uri_pos < p); 931 c->rq.version = c->rq.hdrs.rq_line.rq_tgt; 932 read_buffer[uri_pos] = 0; /* Zero terminate the URI */ 933 c->rq.hdrs.rq_line.rq_tgt = read_buffer + uri_pos; 934 c->rq.req_target_len = 0; 935 c->rq.hdrs.rq_line.num_ws_in_uri = 0; 936 c->rq.hdrs.rq_line.rq_tgt_qmark = NULL; 937 } 938 } 939 940 if (NULL != c->rq.version) 941 { 942 mhd_assert (NULL != c->rq.hdrs.rq_line.rq_tgt); 943 if (! parse_http_version (c, c->rq.version, 944 p 945 - (size_t) (c->rq.version 946 - read_buffer))) 947 { 948 mhd_assert (mhd_HTTP_STAGE_REQ_LINE_RECEIVING < c->stage); 949 return true; /* Unsupported / broken HTTP version */ 950 } 951 read_buffer[p] = 0; /* Zero terminate the HTTP version strings */ 952 if ('\r' == chr) 953 { 954 p++; /* Consume CR */ 955 mhd_assert (p < c->read_buffer_offset); /* The next character has been already checked */ 956 } 957 p++; /* Consume LF */ 958 c->read_buffer += p; 959 c->read_buffer_size -= p; 960 c->read_buffer_offset -= p; 961 mhd_assert (c->rq.hdrs.rq_line.num_ws_in_uri <= \ 962 c->rq.req_target_len); 963 mhd_assert ((NULL == c->rq.hdrs.rq_line.rq_tgt_qmark) || \ 964 (0 != c->rq.req_target_len)); 965 mhd_assert ((NULL == c->rq.hdrs.rq_line.rq_tgt_qmark) || \ 966 ((size_t) (c->rq.hdrs.rq_line.rq_tgt_qmark \ 967 - c->rq.hdrs.rq_line.rq_tgt) < \ 968 c->rq.req_target_len)); 969 mhd_assert ((NULL == c->rq.hdrs.rq_line.rq_tgt_qmark) || \ 970 (c->rq.hdrs.rq_line.rq_tgt_qmark >= \ 971 c->rq.hdrs.rq_line.rq_tgt)); 972 return true; /* The request line is successfully parsed */ 973 } 974 } 975 /* Error in the request line */ 976 977 /* A quick simple check whether this line looks like an HTTP request */ 978 if ((mhd_HTTP_METHOD_GET <= c->rq.http_mthd) && 979 (mhd_HTTP_METHOD_DELETE >= c->rq.http_mthd)) 980 { 981 mhd_RESPOND_WITH_ERROR_STATIC (c, 982 MHD_HTTP_STATUS_BAD_REQUEST, 983 ERR_RSP_REQUEST_MALFORMED); 984 } 985 else 986 mhd_STREAM_ABORT (c, mhd_CONN_CLOSE_CLIENT_HTTP_ERR_ABORT_CONN, 987 "The request line is malformed."); 988 989 return true; 990 } 991 992 /* Process possible end of the previously found whitespace delimiter */ 993 if ((! wsp_blocks) && 994 (p == c->rq.hdrs.rq_line.last_ws_end) && 995 (0 != c->rq.hdrs.rq_line.last_ws_end)) 996 { 997 /* Previous character was a whitespace char and whitespace blocks 998 are not allowed. */ 999 /* The current position is the next character after 1000 a whitespace delimiter */ 1001 if (NULL == c->rq.hdrs.rq_line.rq_tgt) 1002 { 1003 /* The current position is the start of the URI */ 1004 mhd_assert (0 == c->rq.req_target_len); 1005 mhd_assert (NULL == c->rq.version); 1006 c->rq.hdrs.rq_line.rq_tgt = read_buffer + p; 1007 /* Reset the whitespace marker */ 1008 c->rq.hdrs.rq_line.last_ws_start = 0; 1009 c->rq.hdrs.rq_line.last_ws_end = 0; 1010 } 1011 else 1012 { 1013 /* It was a whitespace after the start of the URI */ 1014 if (! wsp_in_uri) 1015 { 1016 mhd_assert ((0 != c->rq.req_target_len) || \ 1017 (c->rq.hdrs.rq_line.rq_tgt + 1 == read_buffer + p)); 1018 mhd_assert (NULL == c->rq.version); /* Too many whitespaces? This error is handled at whitespace start */ 1019 c->rq.version = read_buffer + p; 1020 /* Reset the whitespace marker */ 1021 c->rq.hdrs.rq_line.last_ws_start = 0; 1022 c->rq.hdrs.rq_line.last_ws_end = 0; 1023 } 1024 } 1025 } 1026 1027 /* Process the current character. 1028 Is it not the end of the line. */ 1029 if ((' ' == chr) 1030 || (('\t' == chr) && (tab_as_wsp)) 1031 || ((other_wsp_as_wsp) && ((0xb == chr) || (0xc == chr)))) 1032 { 1033 /* A whitespace character */ 1034 if ((0 == c->rq.hdrs.rq_line.last_ws_end) || 1035 (p != c->rq.hdrs.rq_line.last_ws_end) || 1036 (! wsp_blocks)) 1037 { 1038 /* Found first whitespace char of the new whitespace block */ 1039 if (NULL == c->rq.method.cstr) 1040 { 1041 /* Found the end of the HTTP method string */ 1042 mhd_assert (0 == c->rq.hdrs.rq_line.last_ws_start); 1043 mhd_assert (0 == c->rq.hdrs.rq_line.last_ws_end); 1044 mhd_assert (NULL == c->rq.hdrs.rq_line.rq_tgt); 1045 mhd_assert (0 == c->rq.req_target_len); 1046 mhd_assert (NULL == c->rq.version); 1047 if (0 == p) 1048 { 1049 mhd_STREAM_ABORT (c, mhd_CONN_CLOSE_CLIENT_HTTP_ERR_ABORT_CONN, 1050 "The request line starts with a whitespace."); 1051 return true; /* Error in the request */ 1052 } 1053 read_buffer[p] = 0; /* Zero-terminate the request method string */ 1054 c->rq.method.cstr = read_buffer; 1055 c->rq.method.len = p; 1056 parse_http_std_method (c); 1057 } 1058 else 1059 { 1060 /* A whitespace after the start of the URI */ 1061 if (! wsp_in_uri) 1062 { 1063 /* Whitespace in URI is not allowed to be parsed */ 1064 if (NULL == c->rq.version) 1065 { 1066 mhd_assert (NULL != c->rq.hdrs.rq_line.rq_tgt); 1067 /* This is a delimiter between URI and HTTP version string */ 1068 read_buffer[p] = 0; /* Zero-terminate request URI string */ 1069 mhd_assert (((size_t) (c->rq.hdrs.rq_line.rq_tgt \ 1070 - read_buffer)) <= p); 1071 c->rq.req_target_len = 1072 p - (size_t) (c->rq.hdrs.rq_line.rq_tgt - read_buffer); 1073 } 1074 else 1075 { 1076 /* This is a delimiter AFTER version string */ 1077 1078 /* A quick simple check whether this line looks like an HTTP request */ 1079 if ((mhd_HTTP_METHOD_GET <= c->rq.http_mthd) && 1080 (mhd_HTTP_METHOD_DELETE >= c->rq.http_mthd)) 1081 { 1082 mhd_RESPOND_WITH_ERROR_STATIC (c, 1083 MHD_HTTP_STATUS_BAD_REQUEST, 1084 ERR_RSP_RQ_LINE_TOO_MANY_WSP); 1085 } 1086 else 1087 mhd_STREAM_ABORT (c, mhd_CONN_CLOSE_CLIENT_HTTP_ERR_ABORT_CONN, 1088 "The request line has more than " 1089 "two whitespaces."); 1090 return true; /* Error in the request */ 1091 } 1092 } 1093 else 1094 { 1095 /* Whitespace in URI is allowed to be parsed */ 1096 if (0 != c->rq.hdrs.rq_line.last_ws_end) 1097 { 1098 /* The whitespace after the start of the URI has been found already */ 1099 c->rq.hdrs.rq_line.num_ws_in_uri += 1100 c->rq.hdrs.rq_line.last_ws_end 1101 - c->rq.hdrs.rq_line.last_ws_start; 1102 } 1103 } 1104 } 1105 c->rq.hdrs.rq_line.last_ws_start = p; 1106 c->rq.hdrs.rq_line.last_ws_end = p + 1; /* Will be updated on the next char parsing */ 1107 } 1108 else 1109 { 1110 /* Continuation of the whitespace block */ 1111 mhd_assert (0 != c->rq.hdrs.rq_line.last_ws_end); 1112 mhd_assert (0 != p); 1113 c->rq.hdrs.rq_line.last_ws_end = p + 1; 1114 } 1115 } 1116 else 1117 { 1118 /* Non-whitespace char, not the end of the line */ 1119 mhd_assert ((0 == c->rq.hdrs.rq_line.last_ws_end) || \ 1120 (c->rq.hdrs.rq_line.last_ws_end == p) || \ 1121 wsp_in_uri); 1122 1123 if ((p == c->rq.hdrs.rq_line.last_ws_end) && 1124 (0 != c->rq.hdrs.rq_line.last_ws_end) && 1125 (wsp_blocks)) 1126 { 1127 /* The end of the whitespace block */ 1128 if (NULL == c->rq.hdrs.rq_line.rq_tgt) 1129 { 1130 /* This is the first character of the URI */ 1131 mhd_assert (0 == c->rq.req_target_len); 1132 mhd_assert (NULL == c->rq.version); 1133 c->rq.hdrs.rq_line.rq_tgt = read_buffer + p; 1134 /* Reset the whitespace marker */ 1135 c->rq.hdrs.rq_line.last_ws_start = 0; 1136 c->rq.hdrs.rq_line.last_ws_end = 0; 1137 } 1138 else 1139 { 1140 if (! wsp_in_uri) 1141 { 1142 /* This is the first character of the HTTP version */ 1143 mhd_assert (NULL != c->rq.hdrs.rq_line.rq_tgt); 1144 mhd_assert ((0 != c->rq.req_target_len) || \ 1145 (c->rq.hdrs.rq_line.rq_tgt + 1 == read_buffer + p)); 1146 mhd_assert (NULL == c->rq.version); /* Handled at whitespace start */ 1147 c->rq.version = read_buffer + p; 1148 /* Reset the whitespace marker */ 1149 c->rq.hdrs.rq_line.last_ws_start = 0; 1150 c->rq.hdrs.rq_line.last_ws_end = 0; 1151 } 1152 } 1153 } 1154 1155 /* Handle other special characters */ 1156 if ('?' == chr) 1157 { 1158 if ((NULL == c->rq.hdrs.rq_line.rq_tgt_qmark) && 1159 (NULL != c->rq.hdrs.rq_line.rq_tgt)) 1160 { 1161 c->rq.hdrs.rq_line.rq_tgt_qmark = read_buffer + p; 1162 } 1163 } 1164 else if ((0xb == chr) || (0xc == chr)) 1165 { 1166 /* VT or LF characters */ 1167 mhd_assert (! other_wsp_as_wsp); 1168 if ((NULL != c->rq.hdrs.rq_line.rq_tgt) && 1169 (NULL == c->rq.version) && 1170 (wsp_in_uri)) 1171 { 1172 c->rq.hdrs.rq_line.num_ws_in_uri++; 1173 } 1174 else 1175 { 1176 mhd_STREAM_ABORT (c, mhd_CONN_CLOSE_CLIENT_HTTP_ERR_ABORT_CONN, 1177 "Invalid character is in the request line."); 1178 return true; /* Error in the request */ 1179 } 1180 } 1181 else if (0 == chr) 1182 { 1183 /* NUL character */ 1184 mhd_STREAM_ABORT (c, mhd_CONN_CLOSE_CLIENT_HTTP_ERR_ABORT_CONN, 1185 "The NUL character is in the request line."); 1186 return true; /* Error in the request */ 1187 } 1188 } 1189 1190 p++; 1191 } 1192 1193 c->rq.hdrs.rq_line.proc_pos = p; 1194 return false; /* Not enough data yet */ 1195 } 1196 1197 1198 /** 1199 * Callback for iterating over GET parameters 1200 * @param cls the iterator metadata 1201 * @param name the name of the parameter 1202 * @param value the value of the parameter 1203 * @return bool to continue iterations, 1204 * false to stop the iteration 1205 */ 1206 static MHD_FN_PAR_NONNULL_ (2) MHD_FN_PAR_NONNULL_ (3) bool 1207 request_add_get_arg (void *restrict cls, 1208 const struct MHD_String *restrict name, 1209 const struct MHD_StringNullable *restrict value) 1210 { 1211 struct MHD_Stream *s = (struct MHD_Stream *) cls; 1212 1213 return mhd_stream_add_field_nullable (s, MHD_VK_URI_QUERY_PARAM, name, value); 1214 } 1215 1216 1217 MHD_INTERNAL 1218 MHD_FN_PAR_NONNULL_ (2) MHD_FN_PAR_CSTR_ (2) 1219 MHD_FN_PAR_INOUT_SIZE_ (2, 1) bool 1220 // TODO: detect and report errors 1221 mhd_parse_uri_args (size_t args_len, 1222 char *restrict args, 1223 mhd_GetArgumentInter cb, 1224 void *restrict cls) 1225 { 1226 size_t i; 1227 1228 mhd_assert (args_len < (size_t) (args_len + 1)); /* Does not work when args_len == SIZE_MAX */ 1229 1230 for (i = 0; i < args_len; ++i) /* Looking for names of the parameters */ 1231 { 1232 size_t name_start; 1233 size_t name_len; 1234 size_t value_start; 1235 size_t value_len; 1236 struct MHD_String name; 1237 struct MHD_StringNullable value; 1238 1239 /* Found start of the name */ 1240 1241 value_start = 0; 1242 for (name_start = i; i < args_len; ++i) /* Processing parameter */ 1243 { 1244 if ('+' == args[i]) 1245 args[i] = ' '; 1246 else if ('=' == args[i]) 1247 { 1248 /* Found start of the value */ 1249 for (value_start = ++i; i < args_len; ++i) /* Processing parameter value */ 1250 { 1251 if ('+' == args[i]) 1252 args[i] = ' '; 1253 else if ('&' == args[i]) /* delimiter for the next parameter */ 1254 break; /* Next parameter */ 1255 } 1256 break; /* End of the current parameter */ 1257 } 1258 else if ('&' == args[i]) 1259 break; /* End of the name of the parameter without a value */ 1260 } 1261 1262 /* PCT-decode, zero-terminate and store the found parameter */ 1263 1264 if (0 != value_start) /* Value cannot start at zero position */ 1265 { /* Name with value */ 1266 mhd_assert (name_start + 1 <= value_start); 1267 name_len = value_start - name_start - 1; 1268 1269 value_len = 1270 mhd_str_pct_decode_lenient_n (args + value_start, i - value_start, 1271 args + value_start, i - value_start, 1272 NULL); // TODO: add support for broken encoding detection 1273 if (value_start + value_len < args_len) 1274 args[value_start + value_len] = 0; 1275 value.cstr = args + value_start; 1276 value.len = value_len; 1277 } 1278 else 1279 { /* Name without value */ 1280 name_len = i - name_start; 1281 1282 value.cstr = NULL; 1283 value.len = 0; 1284 } 1285 name_len = mhd_str_pct_decode_lenient_n (args + name_start, name_len, 1286 args + name_start, name_len, 1287 NULL); // TODO: add support for broken encoding detection 1288 if (name_start + name_len < args_len) 1289 args[name_start + name_len] = 0; 1290 name.cstr = args + name_start; 1291 name.len = name_len; 1292 if (! cb (cls, &name, &value)) 1293 return false; 1294 } 1295 return true; 1296 } 1297 1298 1299 /** 1300 * Process request-target string, form URI and URI parameters 1301 * @param c the connection to process 1302 * @return true if request-target successfully processed, 1303 * false if error encountered 1304 */ 1305 static MHD_FN_PAR_NONNULL_ALL_ bool 1306 process_request_target (struct MHD_Connection *c) 1307 { 1308 size_t params_len; 1309 1310 mhd_assert (mhd_HTTP_STAGE_REQ_LINE_RECEIVING == c->stage); 1311 mhd_assert (NULL == c->rq.url); 1312 mhd_assert (0 == c->rq.url_len); 1313 mhd_assert (NULL != c->rq.hdrs.rq_line.rq_tgt); 1314 mhd_assert ((NULL == c->rq.hdrs.rq_line.rq_tgt_qmark) || \ 1315 (c->rq.hdrs.rq_line.rq_tgt <= c->rq.hdrs.rq_line.rq_tgt_qmark)); 1316 mhd_assert ((NULL == c->rq.hdrs.rq_line.rq_tgt_qmark) || \ 1317 (c->rq.req_target_len > \ 1318 (size_t) (c->rq.hdrs.rq_line.rq_tgt_qmark \ 1319 - c->rq.hdrs.rq_line.rq_tgt))); 1320 1321 /* Log callback before the request-target is modified/decoded */ 1322 if (NULL != c->daemon->req_cfg.uri_cb.cb) 1323 { 1324 struct MHD_EarlyUriCbData req_data; 1325 req_data.request = &(c->rq); 1326 req_data.full_uri.cstr = c->rq.hdrs.rq_line.rq_tgt; 1327 req_data.full_uri.len = c->rq.req_target_len; 1328 c->rq.app_aware = true; 1329 c->daemon->req_cfg.uri_cb.cb (c->daemon->req_cfg.uri_cb.cls, 1330 &req_data, 1331 &(c->rq.app_context)); 1332 } 1333 1334 if (NULL != c->rq.hdrs.rq_line.rq_tgt_qmark) 1335 { 1336 params_len = 1337 c->rq.req_target_len 1338 - (size_t) (c->rq.hdrs.rq_line.rq_tgt_qmark - c->rq.hdrs.rq_line.rq_tgt); 1339 1340 mhd_assert (1 <= params_len); 1341 1342 c->rq.hdrs.rq_line.rq_tgt_qmark[0] = 0; /* Replace '?' with zero termination */ 1343 1344 // TODO: support detection of decoding errors 1345 if (! mhd_parse_uri_args (params_len - 1, 1346 c->rq.hdrs.rq_line.rq_tgt_qmark + 1, 1347 &request_add_get_arg, 1348 &(c->h1_stream))) 1349 { 1350 mhd_LOG_MSG (c->daemon, MHD_SC_CONNECTION_POOL_NO_MEM_GET_PARAM, 1351 "Not enough memory in the pool to store GET parameter"); 1352 1353 mhd_RESPOND_WITH_ERROR_STATIC ( 1354 c, 1355 mhd_stream_get_no_space_err_status_code (c, 1356 MHD_PROC_RECV_URI, 1357 0, 1358 NULL), 1359 ERR_RSP_MSG_REQUEST_TOO_BIG); 1360 mhd_assert (mhd_HTTP_STAGE_REQ_LINE_RECEIVING != c->stage); 1361 return false; 1362 1363 } 1364 } 1365 else 1366 params_len = 0; 1367 1368 mhd_assert (strlen (c->rq.hdrs.rq_line.rq_tgt) == \ 1369 c->rq.req_target_len - params_len); 1370 1371 /* Finally unescape URI itself */ 1372 // TODO: support detection of decoding errors 1373 c->rq.url_len = 1374 mhd_str_pct_decode_lenient_n (c->rq.hdrs.rq_line.rq_tgt, 1375 c->rq.req_target_len - params_len, 1376 c->rq.hdrs.rq_line.rq_tgt, 1377 c->rq.req_target_len - params_len, 1378 NULL); 1379 c->rq.url = c->rq.hdrs.rq_line.rq_tgt; 1380 1381 return true; 1382 } 1383 1384 1385 #ifndef MHD_MAX_FIXED_URI_LEN 1386 /** 1387 * The maximum size of the fixed URI for automatic redirection 1388 */ 1389 #define MHD_MAX_FIXED_URI_LEN (64 * 1024) 1390 #endif /* ! MHD_MAX_FIXED_URI_LEN */ 1391 1392 /** 1393 * Send the automatic redirection to fixed URI when received URI with 1394 * whitespaces. 1395 * If URI is too large, close connection with error. 1396 * 1397 * @param c the connection to process 1398 */ 1399 static void 1400 send_redirect_fixed_rq_target (struct MHD_Connection *restrict c) 1401 { 1402 static const char hdr_prefix[] = MHD_HTTP_HEADER_LOCATION ": "; 1403 static const size_t hdr_prefix_len = 1404 mhd_SSTR_LEN (MHD_HTTP_HEADER_LOCATION ": "); 1405 char *hdr_line; 1406 char *b; 1407 size_t fixed_uri_len; 1408 size_t i; 1409 size_t o; 1410 1411 mhd_assert (mhd_HTTP_STAGE_REQ_LINE_RECEIVING == c->stage); 1412 mhd_assert (0 != c->rq.hdrs.rq_line.num_ws_in_uri); 1413 mhd_assert (c->rq.hdrs.rq_line.num_ws_in_uri <= \ 1414 c->rq.req_target_len); 1415 fixed_uri_len = c->rq.req_target_len 1416 + 2 * c->rq.hdrs.rq_line.num_ws_in_uri; 1417 if ( (fixed_uri_len + 200 > c->daemon->conns.cfg.mem_pool_size) || 1418 (fixed_uri_len > MHD_MAX_FIXED_URI_LEN) || 1419 (NULL == 1420 (hdr_line = (char *) malloc (fixed_uri_len + 1 + hdr_prefix_len))) ) 1421 { 1422 mhd_STREAM_ABORT (c, mhd_CONN_CLOSE_CLIENT_HTTP_ERR_ABORT_CONN, \ 1423 "The request has whitespace character is " \ 1424 "in the URI and the URI is too large to " \ 1425 "send automatic redirect to fixed URI."); 1426 return; 1427 } 1428 memcpy (hdr_line, hdr_prefix, hdr_prefix_len); 1429 b = hdr_line + hdr_prefix_len; 1430 i = 0; 1431 o = 0; 1432 1433 do 1434 { 1435 const char chr = c->rq.hdrs.rq_line.rq_tgt[i++]; 1436 1437 mhd_assert ('\r' != chr); /* Replaced during request line parsing */ 1438 mhd_assert ('\n' != chr); /* Rejected during request line parsing */ 1439 mhd_assert (0 != chr); /* Rejected during request line parsing */ 1440 switch (chr) 1441 { 1442 case ' ': 1443 b[o++] = '%'; 1444 b[o++] = '2'; 1445 b[o++] = '0'; 1446 break; 1447 case '\t': 1448 b[o++] = '%'; 1449 b[o++] = '0'; 1450 b[o++] = '9'; 1451 break; 1452 case 0x0B: /* VT (vertical tab) */ 1453 b[o++] = '%'; 1454 b[o++] = '0'; 1455 b[o++] = 'B'; 1456 break; 1457 case 0x0C: /* FF (form feed) */ 1458 b[o++] = '%'; 1459 b[o++] = '0'; 1460 b[o++] = 'C'; 1461 break; 1462 default: 1463 b[o++] = chr; 1464 break; 1465 } 1466 } while (i < c->rq.req_target_len); 1467 mhd_assert (fixed_uri_len == o); 1468 b[o] = 0; /* Zero-terminate the result */ 1469 1470 mhd_RESPOND_WITH_ERROR_HEADER (c, 1471 MHD_HTTP_STATUS_MOVED_PERMANENTLY, 1472 ERR_RSP_RQ_TARGET_INVALID_CHAR, 1473 o + hdr_prefix_len, 1474 hdr_line); 1475 1476 return; 1477 } 1478 1479 1480 MHD_INTERNAL MHD_FN_PAR_NONNULL_ALL_ bool 1481 mhd_stream_get_request_line (struct MHD_Connection *restrict c) 1482 { 1483 const int discp_lvl = c->daemon->req_cfg.strictness; 1484 /* Parse whitespace in URI, special parsing of the request line */ 1485 const bool wsp_in_uri = (0 >= discp_lvl); 1486 /* Keep whitespace in URI, give app URI with whitespace instead of 1487 automatic redirect to fixed URI */ 1488 const bool wsp_in_uri_keep = (-2 >= discp_lvl); 1489 1490 if (! get_request_line_inner (c)) 1491 { 1492 /* End of the request line has not been found yet */ 1493 mhd_assert ((! wsp_in_uri) || NULL == c->rq.version); 1494 if ((NULL != c->rq.version) && 1495 (HTTP_VER_LEN < 1496 (c->rq.hdrs.rq_line.proc_pos 1497 - (size_t) (c->rq.version - c->read_buffer)))) 1498 { 1499 c->rq.http_ver = MHD_HTTP_VERSION_INVALID; 1500 mhd_RESPOND_WITH_ERROR_STATIC (c, 1501 MHD_HTTP_STATUS_BAD_REQUEST, 1502 ERR_RSP_REQUEST_MALFORMED); 1503 return true; /* Error in the request */ 1504 } 1505 return false; 1506 } 1507 if (mhd_HTTP_STAGE_REQ_LINE_RECEIVING < c->stage) 1508 return true; /* Error in the request */ 1509 1510 mhd_assert (mhd_HTTP_STAGE_REQ_LINE_RECEIVING == c->stage); 1511 mhd_assert (NULL == c->rq.url); 1512 mhd_assert (0 == c->rq.url_len); 1513 mhd_assert (NULL != c->rq.hdrs.rq_line.rq_tgt); 1514 if (0 != c->rq.hdrs.rq_line.num_ws_in_uri) 1515 { 1516 if (! wsp_in_uri) 1517 { 1518 mhd_RESPOND_WITH_ERROR_STATIC (c, 1519 MHD_HTTP_STATUS_BAD_REQUEST, 1520 ERR_RSP_RQ_TARGET_INVALID_CHAR); 1521 return true; /* Error in the request */ 1522 } 1523 if (! wsp_in_uri_keep) 1524 { 1525 send_redirect_fixed_rq_target (c); 1526 return true; /* Error in the request */ 1527 } 1528 } 1529 if (! process_request_target (c)) 1530 return true; /* Error in processing */ 1531 1532 c->stage = mhd_HTTP_STAGE_REQ_LINE_RECEIVED; 1533 return true; 1534 } 1535 1536 1537 MHD_INTERNAL MHD_FN_PAR_NONNULL_ALL_ void 1538 mhd_stream_switch_to_rq_headers_proc (struct MHD_Connection *restrict c) 1539 { 1540 c->rq.field_lines.start = c->read_buffer; 1541 mhd_stream_reset_rq_hdr_proc_state (c); 1542 c->stage = mhd_HTTP_STAGE_REQ_HEADERS_RECEIVING; 1543 } 1544 1545 1546 /** 1547 * Send error reply when receive buffer space exhausted while receiving or 1548 * storing the request headers 1549 * @param c the connection to handle 1550 * @param add_header the optional pointer to the current header string being 1551 * processed or the header failed to be added. 1552 * Could be not zero-terminated and can contain binary zeros. 1553 * Can be NULL. 1554 * @param add_header_size the size of the @a add_header 1555 */ 1556 mhd_static_inline 1557 MHD_FN_PAR_NONNULL_ (1) void 1558 handle_req_headers_no_space (struct MHD_Connection *restrict c, 1559 const char *restrict add_header, 1560 size_t add_header_size) 1561 { 1562 unsigned int err_code; 1563 1564 err_code = mhd_stream_get_no_space_err_status_code (c, 1565 MHD_PROC_RECV_HEADERS, 1566 add_header_size, 1567 add_header); 1568 mhd_RESPOND_WITH_ERROR_STATIC (c, 1569 err_code, 1570 ERR_RSP_REQUEST_HEADER_TOO_BIG); 1571 } 1572 1573 1574 /** 1575 * Send error reply when receive buffer space exhausted while receiving or 1576 * storing the request footers (for chunked requests). 1577 * @param c the connection to handle 1578 * @param add_footer the optional pointer to the current footer string being 1579 * processed or the footer failed to be added. 1580 * Could be not zero-terminated and can contain binary zeros. 1581 * Can be NULL. 1582 * @param add_footer_size the size of the @a add_footer 1583 */ 1584 mhd_static_inline 1585 MHD_FN_PAR_NONNULL_ (1) void 1586 handle_req_footers_no_space (struct MHD_Connection *restrict c, 1587 const char *restrict add_footer, 1588 size_t add_footer_size) 1589 { 1590 (void) add_footer; (void) add_footer_size; /* Unused */ 1591 mhd_assert (c->rq.have_chunked_upload); 1592 1593 /* Footers should be optional */ 1594 mhd_RESPOND_WITH_ERROR_STATIC ( 1595 c, 1596 MHD_HTTP_STATUS_REQUEST_HEADER_FIELDS_TOO_LARGE, 1597 ERR_RSP_REQUEST_FOOTER_TOO_BIG); 1598 } 1599 1600 1601 /** 1602 * Results of header line reading 1603 */ 1604 enum MHD_FIXED_ENUM_ mhd_HdrLineReadRes 1605 { 1606 /** 1607 * Not enough data yet 1608 */ 1609 MHD_HDR_LINE_READING_NEED_MORE_DATA = 0, 1610 /** 1611 * New header line has been read 1612 */ 1613 MHD_HDR_LINE_READING_GOT_HEADER, 1614 /** 1615 * Error in header data, error response has been queued 1616 */ 1617 MHD_HDR_LINE_READING_DATA_ERROR, 1618 /** 1619 * Found the end of the request header (end of field lines) 1620 */ 1621 MHD_HDR_LINE_READING_GOT_END_OF_HEADER 1622 }; 1623 1624 1625 /** 1626 * Find the end of the request header line and make basic header parsing. 1627 * Handle errors and header folding. 1628 * @param c the connection to process 1629 * @param process_footers if true then footers are processed, 1630 * if false then headers are processed 1631 * @param[out] hdr_name the name of the parsed header (field) 1632 * @param[out] hdr_value the value of the parsed header (field) 1633 * @return mhd_HdrLineReadRes value 1634 */ 1635 static enum mhd_HdrLineReadRes 1636 get_req_header (struct MHD_Connection *restrict c, 1637 bool process_footers, 1638 struct MHD_String *restrict hdr_name, 1639 struct MHD_String *restrict hdr_value) 1640 { 1641 const int discp_lvl = c->daemon->req_cfg.strictness; 1642 /* Treat bare LF as the end of the line. 1643 RFC 9112, section 2.2-3 1644 Note: MHD never replaces bare LF with space (RFC 9110, section 5.5-5). 1645 Bare LF is processed as end of the line or rejected as broken request. */ 1646 const bool bare_lf_as_crlf = mhd_ALLOW_BARE_LF_AS_CRLF (discp_lvl); 1647 /* Keep bare CR character as is. 1648 Violates RFC 9112, section 2.2-4 */ 1649 const bool bare_cr_keep = (-3 >= discp_lvl); 1650 /* Treat bare CR as space; replace it with space before processing. 1651 RFC 9112, section 2.2-4 */ 1652 const bool bare_cr_as_sp = ((! bare_cr_keep) && (-1 >= discp_lvl)); 1653 /* Treat NUL as space; replace it with space before processing. 1654 RFC 9110, section 5.5-5 */ 1655 const bool nul_as_sp = (-1 >= discp_lvl); 1656 /* Allow folded header lines. 1657 RFC 9112, section 5.2-4 */ 1658 const bool allow_folded = (0 >= discp_lvl); 1659 /* Do not reject headers with the whitespace at the start of the first line. 1660 When allowed, the first line with whitespace character at the first 1661 position is ignored (as well as all possible line foldings of the first 1662 line). 1663 RFC 9112, section 2.2-8 */ 1664 const bool allow_wsp_at_start = allow_folded && (-1 >= discp_lvl); 1665 /* Allow whitespace in header (field) name. 1666 Violates RFC 9110, section 5.1-2 */ 1667 const bool allow_wsp_in_name = (-2 >= discp_lvl); 1668 /* Allow zero-length header (field) name. 1669 Violates RFC 9110, section 5.1-2 */ 1670 const bool allow_empty_name = (-2 >= discp_lvl); 1671 /* Allow whitespace before colon. 1672 Violates RFC 9112, section 5.1-2 */ 1673 const bool allow_wsp_before_colon = (-3 >= discp_lvl); 1674 /* Do not abort the request when header line has no colon, just skip such 1675 bad lines. 1676 RFC 9112, section 5-1 */ 1677 const bool allow_line_without_colon = (-2 >= discp_lvl); 1678 1679 size_t p; /**< The position of the currently processed character */ 1680 1681 (void) process_footers; /* Unused parameter in non-debug and no messages */ 1682 1683 mhd_assert ((process_footers ? mhd_HTTP_STAGE_FOOTERS_RECEIVING : \ 1684 mhd_HTTP_STAGE_REQ_HEADERS_RECEIVING) == \ 1685 c->stage); 1686 1687 p = c->rq.hdrs.hdr.proc_pos; 1688 1689 mhd_assert (p <= c->read_buffer_offset); 1690 while (p < c->read_buffer_offset) 1691 { 1692 char *const restrict read_buffer = c->read_buffer; 1693 const char chr = read_buffer[p]; 1694 bool end_of_line; 1695 1696 mhd_assert ((0 == c->rq.hdrs.hdr.name_len) || \ 1697 (c->rq.hdrs.hdr.name_len < p)); 1698 mhd_assert ((0 == c->rq.hdrs.hdr.name_len) || (0 != p)); 1699 mhd_assert ((0 == c->rq.hdrs.hdr.name_len) || \ 1700 (c->rq.hdrs.hdr.name_end_found)); 1701 mhd_assert ((0 == c->rq.hdrs.hdr.value_start) || \ 1702 (c->rq.hdrs.hdr.name_len < c->rq.hdrs.hdr.value_start)); 1703 mhd_assert ((0 == c->rq.hdrs.hdr.value_start) || \ 1704 (0 != c->rq.hdrs.hdr.name_len)); 1705 mhd_assert ((0 == c->rq.hdrs.hdr.ws_start) || \ 1706 (0 == c->rq.hdrs.hdr.name_len) || \ 1707 (c->rq.hdrs.hdr.ws_start > c->rq.hdrs.hdr.name_len)); 1708 mhd_assert ((0 == c->rq.hdrs.hdr.ws_start) || \ 1709 (0 == c->rq.hdrs.hdr.value_start) || \ 1710 (c->rq.hdrs.hdr.ws_start > c->rq.hdrs.hdr.value_start)); 1711 1712 /* Check for the end of the line */ 1713 if ('\r' == chr) 1714 { 1715 if (0 != p) 1716 { 1717 /* Line is not empty, need to check for possible line folding */ 1718 if (p + 2 >= c->read_buffer_offset) 1719 break; /* Not enough data yet to check for folded line */ 1720 } 1721 else 1722 { 1723 /* Line is empty, no need to check for possible line folding */ 1724 if (p + 2 > c->read_buffer_offset) 1725 break; /* Not enough data yet to check for the end of the line */ 1726 } 1727 if ('\n' == read_buffer[p + 1]) 1728 end_of_line = true; 1729 else 1730 { 1731 /* Bare CR alone */ 1732 /* Must be rejected or replaced with space char. 1733 See RFC 9112, section 2.2-4 */ 1734 if (bare_cr_as_sp) 1735 { 1736 read_buffer[p] = ' '; 1737 c->rq.num_cr_sp_replaced++; 1738 continue; /* Re-start processing of the current character */ 1739 } 1740 else if (! bare_cr_keep) 1741 { 1742 if (! process_footers) 1743 mhd_RESPOND_WITH_ERROR_STATIC (c, 1744 MHD_HTTP_STATUS_BAD_REQUEST, 1745 ERR_RSP_BARE_CR_IN_HEADER); 1746 else 1747 mhd_RESPOND_WITH_ERROR_STATIC (c, 1748 MHD_HTTP_STATUS_BAD_REQUEST, 1749 ERR_RSP_BARE_CR_IN_FOOTER); 1750 return MHD_HDR_LINE_READING_DATA_ERROR; /* Error in the request */ 1751 } 1752 end_of_line = false; 1753 } 1754 } 1755 else if ('\n' == chr) 1756 { 1757 /* Bare LF may be recognised as a line delimiter. 1758 See RFC 9112, section 2.2-3 */ 1759 if (bare_lf_as_crlf) 1760 { 1761 if (0 != p) 1762 { 1763 /* Line is not empty, need to check for possible line folding */ 1764 if (p + 1 >= c->read_buffer_offset) 1765 break; /* Not enough data yet to check for folded line */ 1766 } 1767 end_of_line = true; 1768 } 1769 else 1770 { 1771 if (! process_footers) 1772 mhd_RESPOND_WITH_ERROR_STATIC (c, 1773 MHD_HTTP_STATUS_BAD_REQUEST, 1774 ERR_RSP_BARE_LF_IN_HEADER); 1775 else 1776 mhd_RESPOND_WITH_ERROR_STATIC (c, 1777 MHD_HTTP_STATUS_BAD_REQUEST, 1778 ERR_RSP_BARE_LF_IN_FOOTER); 1779 return MHD_HDR_LINE_READING_DATA_ERROR; /* Error in the request */ 1780 } 1781 } 1782 else 1783 end_of_line = false; 1784 1785 if (end_of_line) 1786 { 1787 /* Handle the end of the line */ 1788 /** 1789 * The full length of the line, including CRLF (or bare LF). 1790 */ 1791 const size_t line_len = p + (('\r' == chr) ? 2 : 1); 1792 char next_line_char; 1793 mhd_assert (line_len <= c->read_buffer_offset); 1794 1795 if (0 == p) 1796 { 1797 /* Zero-length header line. This is the end of the request header 1798 section. 1799 RFC 9112, Section 2.1-1 */ 1800 mhd_assert (! c->rq.hdrs.hdr.starts_with_ws); 1801 mhd_assert (! c->rq.hdrs.hdr.name_end_found); 1802 mhd_assert (0 == c->rq.hdrs.hdr.name_len); 1803 mhd_assert (0 == c->rq.hdrs.hdr.ws_start); 1804 mhd_assert (0 == c->rq.hdrs.hdr.value_start); 1805 /* Consume the line with CRLF (or bare LF) */ 1806 c->read_buffer += line_len; 1807 c->read_buffer_offset -= line_len; 1808 c->read_buffer_size -= line_len; 1809 return MHD_HDR_LINE_READING_GOT_END_OF_HEADER; 1810 } 1811 1812 mhd_assert (line_len < c->read_buffer_offset); 1813 mhd_assert (0 != line_len); 1814 mhd_assert ('\n' == read_buffer[line_len - 1]); 1815 next_line_char = read_buffer[line_len]; 1816 if ((' ' == next_line_char) || 1817 ('\t' == next_line_char)) 1818 { 1819 /* Folded line */ 1820 if (! allow_folded) 1821 { 1822 if (! process_footers) 1823 mhd_RESPOND_WITH_ERROR_STATIC (c, 1824 MHD_HTTP_STATUS_BAD_REQUEST, 1825 ERR_RSP_OBS_FOLD); 1826 else 1827 mhd_RESPOND_WITH_ERROR_STATIC (c, 1828 MHD_HTTP_STATUS_BAD_REQUEST, 1829 ERR_RSP_OBS_FOLD_FOOTER); 1830 1831 return MHD_HDR_LINE_READING_DATA_ERROR; /* Error in the request */ 1832 } 1833 /* Replace CRLF (or bare LF) character(s) with space characters. 1834 See RFC 9112, Section 5.2-4 */ 1835 read_buffer[p] = ' '; 1836 if ('\r' == chr) 1837 read_buffer[p + 1] = ' '; 1838 continue; /* Re-start processing of the current character */ 1839 } 1840 else 1841 { 1842 /* It is not a folded line, it's the real end of the non-empty line */ 1843 bool skip_line = false; 1844 mhd_assert (0 != p); 1845 if (c->rq.hdrs.hdr.starts_with_ws) 1846 { 1847 /* This is the first line and it starts with whitespace. This line 1848 must be discarded completely. 1849 See RFC 9112, Section 2.2-8 */ 1850 mhd_assert (allow_wsp_at_start); 1851 1852 mhd_LOG_MSG (c->daemon, MHD_SC_REQ_FIRST_HEADER_LINE_SPACE_PREFIXED, 1853 "Whitespace-prefixed first header line " \ 1854 "has been skipped."); 1855 skip_line = true; 1856 } 1857 else if (! c->rq.hdrs.hdr.name_end_found) 1858 { 1859 if (! allow_line_without_colon) 1860 { 1861 if (! process_footers) 1862 mhd_RESPOND_WITH_ERROR_STATIC (c, 1863 MHD_HTTP_STATUS_BAD_REQUEST, 1864 ERR_RSP_HEADER_WITHOUT_COLON); 1865 else 1866 mhd_RESPOND_WITH_ERROR_STATIC (c, 1867 MHD_HTTP_STATUS_BAD_REQUEST, 1868 ERR_RSP_FOOTER_WITHOUT_COLON); 1869 1870 return MHD_HDR_LINE_READING_DATA_ERROR; /* Error in the request */ 1871 } 1872 /* Skip broken line completely */ 1873 c->rq.skipped_broken_lines++; 1874 skip_line = true; 1875 } 1876 if (skip_line) 1877 { 1878 /* Skip the entire line */ 1879 c->read_buffer += line_len; 1880 c->read_buffer_offset -= line_len; 1881 c->read_buffer_size -= line_len; 1882 p = 0; 1883 /* Reset processing state */ 1884 memset (&c->rq.hdrs.hdr, 0, sizeof(c->rq.hdrs.hdr)); 1885 /* Start processing of the next line */ 1886 continue; 1887 } 1888 else 1889 { 1890 /* This line should be valid header line */ 1891 size_t value_len; 1892 mhd_assert ((0 != c->rq.hdrs.hdr.name_len) || allow_empty_name); 1893 1894 hdr_name->cstr = read_buffer + 0; /* The name always starts at the first character */ 1895 hdr_name->len = c->rq.hdrs.hdr.name_len; 1896 mhd_assert (0 == hdr_name->cstr[hdr_name->len]); 1897 1898 if (0 == c->rq.hdrs.hdr.value_start) 1899 { 1900 c->rq.hdrs.hdr.value_start = p; 1901 read_buffer[p] = 0; 1902 value_len = 0; 1903 } 1904 else if (0 != c->rq.hdrs.hdr.ws_start) 1905 { 1906 mhd_assert (p > c->rq.hdrs.hdr.ws_start); 1907 mhd_assert (c->rq.hdrs.hdr.ws_start > c->rq.hdrs.hdr.value_start); 1908 read_buffer[c->rq.hdrs.hdr.ws_start] = 0; 1909 value_len = c->rq.hdrs.hdr.ws_start - c->rq.hdrs.hdr.value_start; 1910 } 1911 else 1912 { 1913 mhd_assert (p > c->rq.hdrs.hdr.ws_start); 1914 read_buffer[p] = 0; 1915 value_len = p - c->rq.hdrs.hdr.value_start; 1916 } 1917 hdr_value->cstr = read_buffer + c->rq.hdrs.hdr.value_start; 1918 hdr_value->len = value_len; 1919 mhd_assert (0 == hdr_value->cstr[hdr_value->len]); 1920 /* Consume the entire line */ 1921 c->read_buffer += line_len; 1922 c->read_buffer_offset -= line_len; 1923 c->read_buffer_size -= line_len; 1924 return MHD_HDR_LINE_READING_GOT_HEADER; 1925 } 1926 } 1927 } 1928 else if ((' ' == chr) || ('\t' == chr)) 1929 { 1930 if (0 == p) 1931 { 1932 if (! allow_wsp_at_start) 1933 { 1934 if (! process_footers) 1935 mhd_RESPOND_WITH_ERROR_STATIC (c, 1936 MHD_HTTP_STATUS_BAD_REQUEST, 1937 ERR_RSP_WSP_BEFORE_HEADER); 1938 else 1939 mhd_RESPOND_WITH_ERROR_STATIC (c, 1940 MHD_HTTP_STATUS_BAD_REQUEST, 1941 ERR_RSP_WSP_BEFORE_FOOTER); 1942 return MHD_HDR_LINE_READING_DATA_ERROR; /* Error in the request */ 1943 } 1944 c->rq.hdrs.hdr.starts_with_ws = true; 1945 } 1946 else if ((! c->rq.hdrs.hdr.name_end_found) && 1947 (! c->rq.hdrs.hdr.starts_with_ws)) 1948 { 1949 /* Whitespace in header name / between header name and colon */ 1950 if (allow_wsp_in_name || allow_wsp_before_colon) 1951 { 1952 if (0 == c->rq.hdrs.hdr.ws_start) 1953 c->rq.hdrs.hdr.ws_start = p; 1954 } 1955 else 1956 { 1957 if (! process_footers) 1958 mhd_RESPOND_WITH_ERROR_STATIC (c, 1959 MHD_HTTP_STATUS_BAD_REQUEST, 1960 ERR_RSP_WSP_IN_HEADER_NAME); 1961 else 1962 mhd_RESPOND_WITH_ERROR_STATIC (c, 1963 MHD_HTTP_STATUS_BAD_REQUEST, 1964 ERR_RSP_WSP_IN_FOOTER_NAME); 1965 1966 return MHD_HDR_LINE_READING_DATA_ERROR; /* Error in the request */ 1967 } 1968 } 1969 else 1970 { 1971 /* Whitespace before/inside/after header (field) value */ 1972 if (0 == c->rq.hdrs.hdr.ws_start) 1973 c->rq.hdrs.hdr.ws_start = p; 1974 } 1975 } 1976 else if (0 == chr) 1977 { 1978 if (! nul_as_sp) 1979 { 1980 if (! process_footers) 1981 mhd_RESPOND_WITH_ERROR_STATIC (c, 1982 MHD_HTTP_STATUS_BAD_REQUEST, 1983 ERR_RSP_INVALID_CHR_IN_HEADER); 1984 else 1985 mhd_RESPOND_WITH_ERROR_STATIC (c, 1986 MHD_HTTP_STATUS_BAD_REQUEST, 1987 ERR_RSP_INVALID_CHR_IN_FOOTER); 1988 1989 return MHD_HDR_LINE_READING_DATA_ERROR; /* Error in the request */ 1990 } 1991 read_buffer[p] = ' '; 1992 continue; /* Re-start processing of the current character */ 1993 } 1994 else 1995 { 1996 /* Not a whitespace, not the end of the header line */ 1997 mhd_assert ('\r' != chr); 1998 mhd_assert ('\n' != chr); 1999 mhd_assert ('\0' != chr); 2000 if ((! c->rq.hdrs.hdr.name_end_found) && 2001 (! c->rq.hdrs.hdr.starts_with_ws)) 2002 { 2003 /* Processing the header (field) name */ 2004 if (':' == chr) 2005 { 2006 if (0 == c->rq.hdrs.hdr.ws_start) 2007 c->rq.hdrs.hdr.name_len = p; 2008 else 2009 { 2010 mhd_assert (allow_wsp_in_name || allow_wsp_before_colon); 2011 if (! allow_wsp_before_colon) 2012 { 2013 if (! process_footers) 2014 mhd_RESPOND_WITH_ERROR_STATIC (c, 2015 MHD_HTTP_STATUS_BAD_REQUEST, 2016 ERR_RSP_WSP_IN_HEADER_NAME); 2017 else 2018 mhd_RESPOND_WITH_ERROR_STATIC (c, 2019 MHD_HTTP_STATUS_BAD_REQUEST, 2020 ERR_RSP_WSP_IN_FOOTER_NAME); 2021 return MHD_HDR_LINE_READING_DATA_ERROR; /* Error in the request */ 2022 } 2023 c->rq.hdrs.hdr.name_len = c->rq.hdrs.hdr.ws_start; 2024 #ifndef MHD_FAVOR_SMALL_CODE 2025 c->rq.hdrs.hdr.ws_start = 0; /* Not on whitespace anymore */ 2026 #endif /* ! MHD_FAVOR_SMALL_CODE */ 2027 } 2028 if ((0 == c->rq.hdrs.hdr.name_len) && ! allow_empty_name) 2029 { 2030 if (! process_footers) 2031 mhd_RESPOND_WITH_ERROR_STATIC (c, 2032 MHD_HTTP_STATUS_BAD_REQUEST, 2033 ERR_RSP_EMPTY_HEADER_NAME); 2034 else 2035 mhd_RESPOND_WITH_ERROR_STATIC (c, 2036 MHD_HTTP_STATUS_BAD_REQUEST, 2037 ERR_RSP_EMPTY_FOOTER_NAME); 2038 return MHD_HDR_LINE_READING_DATA_ERROR; /* Error in the request */ 2039 } 2040 c->rq.hdrs.hdr.name_end_found = true; 2041 read_buffer[c->rq.hdrs.hdr.name_len] = 0; /* Zero-terminate the name */ 2042 } 2043 else 2044 { 2045 if (0 != c->rq.hdrs.hdr.ws_start) 2046 { 2047 /* End of the whitespace in header (field) name */ 2048 mhd_assert (allow_wsp_in_name || allow_wsp_before_colon); 2049 if (! allow_wsp_in_name) 2050 { 2051 if (! process_footers) 2052 mhd_RESPOND_WITH_ERROR_STATIC (c, 2053 MHD_HTTP_STATUS_BAD_REQUEST, 2054 ERR_RSP_WSP_IN_HEADER_NAME); 2055 else 2056 mhd_RESPOND_WITH_ERROR_STATIC (c, 2057 MHD_HTTP_STATUS_BAD_REQUEST, 2058 ERR_RSP_WSP_IN_FOOTER_NAME); 2059 2060 return MHD_HDR_LINE_READING_DATA_ERROR; /* Error in the request */ 2061 } 2062 #ifndef MHD_FAVOR_SMALL_CODE 2063 c->rq.hdrs.hdr.ws_start = 0; /* Not on whitespace anymore */ 2064 #endif /* ! MHD_FAVOR_SMALL_CODE */ 2065 } 2066 } 2067 } 2068 else 2069 { 2070 /* Processing the header (field) value */ 2071 if (0 == c->rq.hdrs.hdr.value_start) 2072 c->rq.hdrs.hdr.value_start = p; 2073 #ifndef MHD_FAVOR_SMALL_CODE 2074 c->rq.hdrs.hdr.ws_start = 0; /* Not on whitespace anymore */ 2075 #endif /* ! MHD_FAVOR_SMALL_CODE */ 2076 } 2077 #ifdef MHD_FAVOR_SMALL_CODE 2078 c->rq.hdrs.hdr.ws_start = 0; /* Not on whitespace anymore */ 2079 #endif /* MHD_FAVOR_SMALL_CODE */ 2080 } 2081 p++; 2082 } 2083 c->rq.hdrs.hdr.proc_pos = p; 2084 return MHD_HDR_LINE_READING_NEED_MORE_DATA; /* Not enough data yet */ 2085 } 2086 2087 2088 /** 2089 * Reset request header processing state. 2090 * 2091 * This function resets the processing state before processing the next header 2092 * (or footer) line. 2093 * @param c the connection to process 2094 */ 2095 MHD_INTERNAL MHD_FN_PAR_NONNULL_ALL_ void 2096 mhd_stream_reset_rq_hdr_proc_state (struct MHD_Connection *c) 2097 { 2098 memset (&c->rq.hdrs.hdr, 0, sizeof(c->rq.hdrs.hdr)); 2099 } 2100 2101 2102 MHD_INTERNAL MHD_FN_PAR_NONNULL_ALL_ bool 2103 mhd_stream_get_request_headers (struct MHD_Connection *restrict c, 2104 bool process_footers) 2105 { 2106 do 2107 { 2108 struct MHD_String hdr_name; 2109 struct MHD_String hdr_value; 2110 enum mhd_HdrLineReadRes res; 2111 2112 mhd_assert ((process_footers ? mhd_HTTP_STAGE_FOOTERS_RECEIVING : \ 2113 mhd_HTTP_STAGE_REQ_HEADERS_RECEIVING) == \ 2114 c->stage); 2115 2116 #ifndef NDEBUG 2117 hdr_name.cstr = NULL; 2118 hdr_value.cstr = NULL; 2119 #endif /* ! NDEBUG */ 2120 res = get_req_header (c, process_footers, &hdr_name, &hdr_value); 2121 if (MHD_HDR_LINE_READING_GOT_HEADER == res) 2122 { 2123 mhd_assert ((process_footers ? mhd_HTTP_STAGE_FOOTERS_RECEIVING : \ 2124 mhd_HTTP_STAGE_REQ_HEADERS_RECEIVING) == \ 2125 c->stage); 2126 mhd_assert (NULL != hdr_name.cstr); 2127 mhd_assert (NULL != hdr_value.cstr); 2128 /* Values must be zero-terminated and must not have binary zeros */ 2129 mhd_assert (strlen (hdr_name.cstr) == hdr_name.len); 2130 mhd_assert (strlen (hdr_value.cstr) == hdr_value.len); 2131 /* Values must not have whitespaces at the start or at the end */ 2132 mhd_assert ((hdr_name.len == 0) || (hdr_name.cstr[0] != ' ')); 2133 mhd_assert ((hdr_name.len == 0) || (hdr_name.cstr[0] != '\t')); 2134 mhd_assert ((hdr_name.len == 0) || \ 2135 (hdr_name.cstr[hdr_name.len - 1] != ' ')); 2136 mhd_assert ((hdr_name.len == 0) || \ 2137 (hdr_name.cstr[hdr_name.len - 1] != '\t')); 2138 mhd_assert ((hdr_value.len == 0) || (hdr_value.cstr[0] != ' ')); 2139 mhd_assert ((hdr_value.len == 0) || (hdr_value.cstr[0] != '\t')); 2140 mhd_assert ((hdr_value.len == 0) || \ 2141 (hdr_value.cstr[hdr_value.len - 1] != ' ')); 2142 mhd_assert ((hdr_value.len == 0) || \ 2143 (hdr_value.cstr[hdr_value.len - 1] != '\t')); 2144 2145 if (! mhd_stream_add_field (&(c->h1_stream), 2146 process_footers ? 2147 MHD_VK_TRAILER : MHD_VK_HEADER, 2148 &hdr_name, 2149 &hdr_value)) 2150 { 2151 size_t add_element_size; 2152 2153 mhd_assert (hdr_name.cstr < hdr_value.cstr); 2154 2155 if (! process_footers) 2156 mhd_LOG_MSG (c->daemon, MHD_SC_CONNECTION_POOL_NO_MEM_REQ, \ 2157 "Failed to allocate memory in the connection memory " \ 2158 "pool to store header."); 2159 else 2160 mhd_LOG_MSG (c->daemon, MHD_SC_CONNECTION_POOL_NO_MEM_REQ, \ 2161 "Failed to allocate memory in the connection memory " \ 2162 "pool to store footer."); 2163 2164 add_element_size = hdr_value.len 2165 + (size_t) (hdr_value.cstr - hdr_name.cstr); 2166 2167 if (! process_footers) 2168 handle_req_headers_no_space (c, hdr_name.cstr, add_element_size); 2169 else 2170 handle_req_footers_no_space (c, hdr_name.cstr, add_element_size); 2171 2172 mhd_assert (mhd_HTTP_STAGE_FULL_REQ_RECEIVED < c->stage); 2173 return true; 2174 } 2175 /* Reset processing state */ 2176 mhd_stream_reset_rq_hdr_proc_state (c); 2177 mhd_assert ((process_footers ? mhd_HTTP_STAGE_FOOTERS_RECEIVING : \ 2178 mhd_HTTP_STAGE_REQ_HEADERS_RECEIVING) == \ 2179 c->stage); 2180 /* Read the next header (field) line */ 2181 continue; 2182 } 2183 else if (MHD_HDR_LINE_READING_NEED_MORE_DATA == res) 2184 { 2185 mhd_assert ((process_footers ? mhd_HTTP_STAGE_FOOTERS_RECEIVING : \ 2186 mhd_HTTP_STAGE_REQ_HEADERS_RECEIVING) == \ 2187 c->stage); 2188 return false; 2189 } 2190 else if (MHD_HDR_LINE_READING_DATA_ERROR == res) 2191 { 2192 mhd_assert ((process_footers ? \ 2193 mhd_HTTP_STAGE_FOOTERS_RECEIVING : \ 2194 mhd_HTTP_STAGE_REQ_HEADERS_RECEIVING) < c->stage); 2195 mhd_assert (c->stop_with_error); 2196 mhd_assert (c->discard_request); 2197 return true; 2198 } 2199 mhd_assert (MHD_HDR_LINE_READING_GOT_END_OF_HEADER == res); 2200 break; 2201 } while (1); 2202 2203 if (1 == c->rq.num_cr_sp_replaced) 2204 { 2205 if (! process_footers) 2206 mhd_LOG_MSG (c->daemon, MHD_SC_REQ_HEADER_CR_REPLACED, \ 2207 "One bare CR character has been replaced with space " \ 2208 "in the request line or in the request headers."); 2209 else 2210 mhd_LOG_MSG (c->daemon, MHD_SC_REQ_FOOTER_CR_REPLACED, \ 2211 "One bare CR character has been replaced with space " \ 2212 "in the request footers."); 2213 } 2214 else if (0 != c->rq.num_cr_sp_replaced) 2215 { 2216 if (! process_footers) 2217 mhd_LOG_PRINT (c->daemon, MHD_SC_REQ_HEADER_CR_REPLACED, \ 2218 mhd_LOG_FMT ("%" PRIuFAST64 " bare CR characters have " \ 2219 "been replaced with spaces in the request " \ 2220 "line and/or in the request headers."), \ 2221 (uint_fast64_t) c->rq.num_cr_sp_replaced); 2222 else 2223 mhd_LOG_PRINT (c->daemon, MHD_SC_REQ_HEADER_CR_REPLACED, \ 2224 mhd_LOG_FMT ("%" PRIuFAST64 " bare CR characters have " \ 2225 "been replaced with spaces in the request " \ 2226 "footers."), \ 2227 (uint_fast64_t) c->rq.num_cr_sp_replaced); 2228 2229 2230 } 2231 if (1 == c->rq.skipped_broken_lines) 2232 { 2233 if (! process_footers) 2234 mhd_LOG_MSG (c->daemon, MHD_SC_REQ_HEADER_LINE_NO_COLON, \ 2235 "One header line without colon has been skipped."); 2236 else 2237 mhd_LOG_MSG (c->daemon, MHD_SC_REQ_FOOTER_LINE_NO_COLON, \ 2238 "One footer line without colon has been skipped."); 2239 } 2240 else if (0 != c->rq.skipped_broken_lines) 2241 { 2242 if (! process_footers) 2243 mhd_LOG_PRINT (c->daemon, MHD_SC_REQ_HEADER_CR_REPLACED, \ 2244 mhd_LOG_FMT ("%" PRIu64 " header lines without colons " 2245 "have been skipped."), 2246 (uint_fast64_t) c->rq.skipped_broken_lines); 2247 else 2248 mhd_LOG_PRINT (c->daemon, MHD_SC_REQ_HEADER_CR_REPLACED, \ 2249 mhd_LOG_FMT ("%" PRIu64 " footer lines without colons " 2250 "have been skipped."), 2251 (uint_fast64_t) c->rq.skipped_broken_lines); 2252 } 2253 2254 mhd_assert (c->rq.method.cstr < c->read_buffer); 2255 if (! process_footers) 2256 { 2257 c->rq.header_size = (size_t) (c->read_buffer - c->rq.method.cstr); 2258 mhd_assert (NULL != c->rq.field_lines.start); 2259 c->rq.field_lines.size = 2260 (size_t) ((c->read_buffer - c->rq.field_lines.start) - 1); 2261 if ('\r' == *(c->read_buffer - 2)) 2262 c->rq.field_lines.size--; 2263 c->stage = mhd_HTTP_STAGE_HEADERS_RECEIVED; 2264 2265 if (mhd_BUF_INC_SIZE > c->read_buffer_size) 2266 { 2267 /* Try to re-use some of the last bytes of the request header */ 2268 /* Do this only if space in the read buffer is limited AND 2269 amount of read ahead data is small. */ 2270 /** 2271 * The position of the terminating NUL after the last character of 2272 * the last header element. 2273 */ 2274 const char *last_elmnt_end; 2275 size_t shift_back_size; 2276 struct mhd_RequestField *header; 2277 header = mhd_DLINKEDL_GET_LAST (&(c->rq), fields); 2278 if (NULL != header) 2279 last_elmnt_end = 2280 header->field.nv.value.cstr + header->field.nv.value.len; 2281 else 2282 last_elmnt_end = c->rq.version + HTTP_VER_LEN; 2283 mhd_assert ((last_elmnt_end + 1) < c->read_buffer); 2284 shift_back_size = (size_t) (c->read_buffer - (last_elmnt_end + 1)); 2285 if (0 != c->read_buffer_offset) 2286 memmove (c->read_buffer - shift_back_size, 2287 c->read_buffer, 2288 c->read_buffer_offset); 2289 c->read_buffer -= shift_back_size; 2290 c->read_buffer_size += shift_back_size; 2291 } 2292 } 2293 else 2294 c->stage = mhd_HTTP_STAGE_FOOTERS_RECEIVED; 2295 2296 return true; 2297 } 2298 2299 2300 #ifdef MHD_SUPPORT_COOKIES 2301 2302 /** 2303 * Cookie parsing result 2304 */ 2305 enum mhd_ParseCookie 2306 { 2307 MHD_PARSE_COOKIE_OK_LAX = 2 /**< Cookies parsed, but workarounds used */ 2308 , 2309 MHD_PARSE_COOKIE_OK = 1 /**< Success or no cookies in headers */ 2310 , 2311 MHD_PARSE_COOKIE_NO_MEMORY = 0 /**< Not enough memory in the pool */ 2312 , 2313 MHD_PARSE_COOKIE_MALFORMED = -1 /**< Invalid cookie header */ 2314 }; 2315 2316 2317 /** 2318 * Parse the cookies string (see RFC 6265). 2319 * 2320 * Try to parse the cookies string even if it is not strictly formed 2321 * as specified by RFC 6265. 2322 * 2323 * @param str_len the size of the @a str, not including mandatory 2324 * zero-termination 2325 * @param str the string to parse, without leading whitespaces 2326 * @param strictness the protocol strictness 2327 * @param s the stream to process 2328 * @return #MHD_PARSE_COOKIE_OK for success, error code otherwise 2329 */ 2330 static MHD_FN_PAR_NONNULL_ALL_ 2331 MHD_FN_PAR_CSTR_ (2) 2332 MHD_FN_PAR_INOUT_SIZE_ (2,1) enum mhd_ParseCookie 2333 parse_cookies_string (const size_t str_len, 2334 char *restrict str, 2335 enum MHD_ProtocolStrictLevel strictness, 2336 struct MHD_Stream *restrict s) 2337 { 2338 size_t i; 2339 bool non_strict; 2340 /* Skip extra whitespaces and empty cookies */ 2341 const bool allow_wsp_empty = (0 >= strictness); 2342 /* Allow whitespaces around '=' character */ 2343 const bool wsp_around_eq = (-3 >= strictness); 2344 /* Allow whitespaces in quoted cookie value */ 2345 const bool wsp_in_quoted = (-2 >= strictness); 2346 /* Allow tab as space after semicolon between cookies */ 2347 const bool tab_as_sp = (0 >= strictness); 2348 /* Allow no space after semicolon between cookies */ 2349 const bool allow_no_space = (0 >= strictness); 2350 2351 non_strict = false; 2352 i = 0; 2353 while (i < str_len) 2354 { 2355 size_t name_start; 2356 size_t name_len; 2357 size_t value_start; 2358 size_t value_len; 2359 bool val_quoted; 2360 /* Skip any whitespaces and empty cookies */ 2361 while (' ' == str[i] || '\t' == str[i] || ';' == str[i]) 2362 { 2363 if (! allow_wsp_empty) 2364 return MHD_PARSE_COOKIE_MALFORMED; 2365 non_strict = true; 2366 i++; 2367 if (i == str_len) 2368 return non_strict? MHD_PARSE_COOKIE_OK_LAX : MHD_PARSE_COOKIE_OK; 2369 } 2370 /* 'i' must point to the first char of cookie-name */ 2371 name_start = i; 2372 /* Find the end of the cookie-name */ 2373 do 2374 { 2375 const char l = str[i]; 2376 if (('=' == l) || (' ' == l) || ('\t' == l) || ('"' == l) || (',' == l) || 2377 (';' == l) || (0 == l)) 2378 break; 2379 } while (str_len > ++i); 2380 name_len = i - name_start; 2381 /* Skip any whitespaces */ 2382 while (str_len > i && (' ' == str[i] || '\t' == str[i])) 2383 { 2384 if (! wsp_around_eq) 2385 return MHD_PARSE_COOKIE_MALFORMED; 2386 non_strict = true; 2387 i++; 2388 } 2389 if ((str_len == i) || ('=' != str[i]) || (0 == name_len)) 2390 return MHD_PARSE_COOKIE_MALFORMED; /* Incomplete cookie name */ 2391 /* 'i' must point to the '=' char */ 2392 mhd_assert ('=' == str[i]); 2393 i++; 2394 /* Skip any whitespaces */ 2395 while (str_len > i && (' ' == str[i] || '\t' == str[i])) 2396 { 2397 if (! wsp_around_eq) 2398 return MHD_PARSE_COOKIE_MALFORMED; 2399 non_strict = true; 2400 i++; 2401 } 2402 /* 'i' must point to the first char of cookie-value */ 2403 if (str_len == i) 2404 { 2405 value_start = 0; 2406 value_len = 0; 2407 #ifndef NDEBUG 2408 val_quoted = false; /* This assignment used in assert */ 2409 #endif 2410 } 2411 else 2412 { 2413 bool valid_cookie; 2414 val_quoted = ('"' == str[i]); 2415 if (val_quoted) 2416 i++; 2417 value_start = i; 2418 /* Find the end of the cookie-value */ 2419 while (str_len > i) 2420 { 2421 const char l = str[i]; 2422 if ((';' == l) || ('"' == l) || (',' == l) || (';' == l) || 2423 ('\\' == l) || (0 == l)) 2424 break; 2425 if ((' ' == l) || ('\t' == l)) 2426 { 2427 if (! val_quoted) 2428 break; 2429 if (! wsp_in_quoted) 2430 return MHD_PARSE_COOKIE_MALFORMED; 2431 non_strict = true; 2432 } 2433 i++; 2434 } 2435 value_len = i - value_start; 2436 if (val_quoted) 2437 { 2438 if ((str_len == i) || ('"' != str[i])) 2439 return MHD_PARSE_COOKIE_MALFORMED; /* Incomplete cookie value, no closing quote */ 2440 i++; 2441 } 2442 /* Skip any whitespaces */ 2443 if ((str_len > i) && ((' ' == str[i]) || ('\t' == str[i]))) 2444 { 2445 do 2446 { 2447 i++; 2448 } while (str_len > i && (' ' == str[i] || '\t' == str[i])); 2449 /* Whitespace at the end? */ 2450 if (str_len > i) 2451 { 2452 if (! allow_wsp_empty) 2453 return MHD_PARSE_COOKIE_MALFORMED; 2454 non_strict = true; 2455 } 2456 } 2457 if (str_len == i) 2458 valid_cookie = true; 2459 else if (';' == str[i]) 2460 valid_cookie = true; 2461 else 2462 valid_cookie = false; 2463 2464 if (! valid_cookie) 2465 return MHD_PARSE_COOKIE_MALFORMED; /* Garbage at the end of the cookie value */ 2466 } 2467 mhd_assert (0 != name_len); 2468 str[name_start + name_len] = 0; /* Zero-terminate the name */ 2469 if (0 != value_len) 2470 { 2471 struct MHD_String name; 2472 struct MHD_String value; 2473 mhd_assert (value_start + value_len <= str_len); 2474 name.cstr = str + name_start; 2475 name.len = name_len; 2476 str[value_start + value_len] = 0; /* Zero-terminate the value */ 2477 value.cstr = str + value_start; 2478 value.len = value_len; 2479 if (! mhd_stream_add_field (s, 2480 MHD_VK_COOKIE, 2481 &name, 2482 &value)) 2483 return MHD_PARSE_COOKIE_NO_MEMORY; 2484 } 2485 else 2486 { 2487 struct MHD_String name; 2488 struct MHD_String value; 2489 name.cstr = str + name_start; 2490 name.len = name_len; 2491 value.cstr = ""; 2492 value.len = 0; 2493 if (! mhd_stream_add_field (s, 2494 MHD_VK_COOKIE, 2495 &name, 2496 &value)) 2497 return MHD_PARSE_COOKIE_NO_MEMORY; 2498 } 2499 if (str_len > i) 2500 { 2501 mhd_assert (0 == str[i] || ';' == str[i]); 2502 mhd_assert (! val_quoted || ';' == str[i]); 2503 mhd_assert (';' != str[i] || val_quoted || non_strict || 0 == value_len); 2504 i++; 2505 if (str_len == i) 2506 { /* No next cookie after semicolon */ 2507 if (! allow_wsp_empty) 2508 return MHD_PARSE_COOKIE_MALFORMED; 2509 non_strict = true; 2510 } 2511 else if (' ' != str[i]) 2512 {/* No space after semicolon */ 2513 if (('\t' == str[i]) && tab_as_sp) 2514 i++; 2515 else if (! allow_no_space) 2516 return MHD_PARSE_COOKIE_MALFORMED; 2517 non_strict = true; 2518 } 2519 else 2520 { 2521 i++; 2522 if (str_len == i) 2523 { 2524 if (! allow_wsp_empty) 2525 return MHD_PARSE_COOKIE_MALFORMED; 2526 non_strict = true; 2527 } 2528 } 2529 } 2530 } 2531 return non_strict? MHD_PARSE_COOKIE_OK_LAX : MHD_PARSE_COOKIE_OK; 2532 } 2533 2534 2535 /** 2536 * Parse the cookie header (see RFC 6265). 2537 * 2538 * @param connection connection to parse header of 2539 * @param cookie_val the value of the "Cookie:" header 2540 * @return #MHD_PARSE_COOKIE_OK for success, error code otherwise 2541 */ 2542 static enum mhd_ParseCookie 2543 parse_cookie_header (struct MHD_Connection *restrict connection, 2544 struct MHD_StringNullable *restrict cookie_val) 2545 { 2546 char *cpy; 2547 size_t i; 2548 enum mhd_ParseCookie parse_res; 2549 struct mhd_RequestField *const saved_tail = 2550 connection->rq.fields.last; // FIXME: a better way? 2551 const bool allow_partially_correct_cookie = 2552 (1 >= connection->daemon->req_cfg.strictness); 2553 2554 if (NULL == cookie_val) 2555 return MHD_PARSE_COOKIE_OK; 2556 if (0 == cookie_val->len) 2557 return MHD_PARSE_COOKIE_OK; 2558 2559 cpy = (char *) mhd_stream_alloc_memory (connection, 2560 cookie_val->len + 1); 2561 if (NULL == cpy) 2562 parse_res = MHD_PARSE_COOKIE_NO_MEMORY; 2563 else 2564 { 2565 memcpy (cpy, 2566 cookie_val->cstr, 2567 cookie_val->len + 1); 2568 mhd_assert (0 == cpy[cookie_val->len]); 2569 2570 /* Must not have initial whitespaces */ 2571 mhd_assert (' ' != cpy[0]); 2572 mhd_assert ('\t' != cpy[0]); 2573 2574 i = 0; 2575 parse_res = parse_cookies_string (cookie_val->len - i, 2576 cpy + i, 2577 connection->daemon->req_cfg.strictness, 2578 &(connection->h1_stream)); 2579 } 2580 2581 switch (parse_res) 2582 { 2583 case MHD_PARSE_COOKIE_OK: 2584 break; 2585 case MHD_PARSE_COOKIE_OK_LAX: 2586 if (saved_tail != connection->rq.fields.last) 2587 mhd_LOG_MSG (connection->daemon, MHD_SC_REQ_COOKIE_PARSED_NOT_COMPLIANT, \ 2588 "The Cookie header has been parsed, but it is not " 2589 "fully compliant with specifications."); 2590 break; 2591 case MHD_PARSE_COOKIE_MALFORMED: 2592 if (saved_tail != connection->rq.fields.last) // FIXME: a better way? 2593 { 2594 if (! allow_partially_correct_cookie) 2595 { 2596 /* Remove extracted values from partially broken cookie */ 2597 /* Memory remains allocated until the end of the request processing */ 2598 connection->rq.fields.last = saved_tail; // FIXME: a better way? 2599 saved_tail->fields.next = NULL; // FIXME: a better way? 2600 mhd_LOG_MSG ( \ 2601 connection->daemon, MHD_SC_REQ_COOKIE_IGNORED_NOT_COMPLIANT, \ 2602 "The Cookie header is ignored as it contains malformed data."); 2603 } 2604 else 2605 mhd_LOG_MSG (connection->daemon, MHD_SC_REQ_COOKIE_PARSED_PARTIALLY, \ 2606 "The Cookie header has been only partially parsed " \ 2607 "as it contains malformed data."); 2608 } 2609 else 2610 mhd_LOG_MSG (connection->daemon, MHD_SC_REQ_COOKIE_INVALID, 2611 "The Cookie header has malformed data."); 2612 break; 2613 case MHD_PARSE_COOKIE_NO_MEMORY: 2614 mhd_LOG_MSG (connection->daemon, MHD_SC_CONNECTION_POOL_NO_MEM_COOKIE, 2615 "Not enough memory in the connection pool to " 2616 "parse client cookies!\n"); 2617 break; 2618 default: 2619 mhd_UNREACHABLE (); 2620 break; 2621 } 2622 2623 return parse_res; 2624 } 2625 2626 2627 /** 2628 * Send error reply when receive buffer space exhausted while receiving or 2629 * storing the request headers 2630 * @param c the connection to handle 2631 */ 2632 mhd_static_inline void 2633 handle_req_cookie_no_space (struct MHD_Connection *restrict c) 2634 { 2635 unsigned int err_code; 2636 2637 err_code = mhd_stream_get_no_space_err_status_code (c, 2638 MHD_PROC_RECV_COOKIE, 2639 0, 2640 NULL); 2641 mhd_RESPOND_WITH_ERROR_STATIC (c, 2642 err_code, 2643 ERR_RSP_REQUEST_HEADER_TOO_BIG); 2644 } 2645 2646 2647 #endif /* MHD_SUPPORT_COOKIES */ 2648 2649 2650 MHD_INTERNAL MHD_FN_PAR_NONNULL_ALL_ void 2651 mhd_stream_parse_request_headers (struct MHD_Connection *restrict c) 2652 { 2653 bool has_host; 2654 bool has_trenc; 2655 bool has_cntnlen; 2656 bool has_keepalive; 2657 struct mhd_RequestField *f; 2658 2659 /* The presence of the request body is indicated by "Content-Length:" or 2660 "Transfer-Encoding:" request headers. 2661 Unless one of these two headers is used, the request has no request body. 2662 See RFC9112, Section 6, paragraph 4. */ 2663 c->rq.have_chunked_upload = false; 2664 c->rq.cntn.cntn_size = 0; 2665 2666 has_host = false; 2667 has_trenc = false; 2668 has_cntnlen = false; 2669 has_keepalive = true; 2670 2671 for (f = mhd_DLINKEDL_GET_FIRST (&(c->rq), fields); 2672 NULL != f; 2673 f = mhd_DLINKEDL_GET_NEXT (f, fields)) 2674 { 2675 if (MHD_VK_HEADER != f->field.kind) 2676 continue; 2677 2678 /* "Host:" */ 2679 if (mhd_str_equal_caseless_n_st (MHD_HTTP_HEADER_HOST, 2680 f->field.nv.name.cstr, 2681 f->field.nv.name.len)) 2682 { 2683 if ((has_host) 2684 && (-3 < c->daemon->req_cfg.strictness)) 2685 { 2686 mhd_LOG_MSG (c->daemon, MHD_SC_HOST_HEADER_SEVERAL, \ 2687 "Received request with more than one 'Host' header."); 2688 mhd_RESPOND_WITH_ERROR_STATIC (c, 2689 MHD_HTTP_STATUS_BAD_REQUEST, 2690 ERR_RSP_REQUEST_HAS_SEVERAL_HOSTS); 2691 return; 2692 } 2693 has_host = true; 2694 continue; 2695 } 2696 2697 /* "Content-Length:" */ 2698 if (mhd_str_equal_caseless_n_st (MHD_HTTP_HEADER_CONTENT_LENGTH, 2699 f->field.nv.name.cstr, 2700 f->field.nv.name.len)) 2701 { 2702 size_t num_digits; 2703 uint_fast64_t cntn_size; 2704 2705 num_digits = mhd_str_to_uint64_n (f->field.nv.value.cstr, 2706 f->field.nv.value.len, 2707 &cntn_size); 2708 if (((0 == num_digits) && 2709 (0 != f->field.nv.value.len) && 2710 ('9' >= f->field.nv.value.cstr[0]) 2711 && ('0' <= f->field.nv.value.cstr[0])) 2712 || (MHD_SIZE_UNKNOWN == c->rq.cntn.cntn_size)) 2713 { 2714 mhd_LOG_MSG (c->daemon, MHD_SC_CONTENT_LENGTH_TOO_LARGE, \ 2715 "Too large value of 'Content-Length' header. " \ 2716 "Closing connection."); 2717 mhd_RESPOND_WITH_ERROR_STATIC (c, \ 2718 MHD_HTTP_STATUS_CONTENT_TOO_LARGE, \ 2719 ERR_RSP_REQUEST_CONTENTLENGTH_TOOLARGE); 2720 return; 2721 } 2722 else if ((f->field.nv.value.len != num_digits) || 2723 (0 == num_digits)) 2724 { 2725 mhd_LOG_MSG (c->daemon, MHD_SC_CONTENT_LENGTH_MALFORMED, \ 2726 "Failed to parse 'Content-Length' header. " \ 2727 "Closing connection."); 2728 mhd_RESPOND_WITH_ERROR_STATIC (c, \ 2729 MHD_HTTP_STATUS_BAD_REQUEST, \ 2730 ERR_RSP_REQUEST_CONTENTLENGTH_MALFORMED); 2731 return; 2732 } 2733 2734 if (has_cntnlen) 2735 { 2736 bool send_err; 2737 send_err = false; 2738 if (c->rq.cntn.cntn_size == cntn_size) 2739 { 2740 if (0 < c->daemon->req_cfg.strictness) 2741 { 2742 mhd_LOG_MSG (c->daemon, MHD_SC_CONTENT_LENGTH_SEVERAL_SAME, \ 2743 "Received request with more than one " \ 2744 "'Content-Length' header with the same value."); 2745 send_err = true; 2746 } 2747 } 2748 else 2749 { 2750 mhd_LOG_MSG (c->daemon, MHD_SC_CONTENT_LENGTH_SEVERAL_DIFFERENT, \ 2751 "Received request with more than one " \ 2752 "'Content-Length' header with conflicting values."); 2753 send_err = true; 2754 } 2755 2756 if (send_err) 2757 { 2758 mhd_RESPOND_WITH_ERROR_STATIC ( \ 2759 c, \ 2760 MHD_HTTP_STATUS_BAD_REQUEST, \ 2761 ERR_RSP_REQUEST_CONTENTLENGTH_SEVERAL); 2762 return; 2763 } 2764 } 2765 mhd_assert ((0 == c->rq.cntn.cntn_size) || \ 2766 (c->rq.cntn.cntn_size == cntn_size)); 2767 c->rq.cntn.cntn_size = cntn_size; 2768 has_cntnlen = true; 2769 continue; 2770 } 2771 2772 /* "Connection:" */ 2773 if (mhd_str_equal_caseless_n_st (MHD_HTTP_HEADER_CONNECTION, 2774 f->field.nv.name.cstr, 2775 f->field.nv.name.len)) 2776 { 2777 if (mhd_str_has_token_caseless (f->field.nv.value.cstr, // TODO: compare as size string 2778 "close", 2779 mhd_SSTR_LEN ("close"))) 2780 { 2781 mhd_assert (mhd_CONN_MUST_UPGRADE != c->conn_reuse); 2782 c->conn_reuse = mhd_CONN_MUST_CLOSE; 2783 } 2784 else if ((MHD_HTTP_VERSION_1_0 == c->rq.http_ver) 2785 && (mhd_CONN_MUST_CLOSE != c->conn_reuse)) 2786 { 2787 if (mhd_str_has_token_caseless (f->field.nv.value.cstr, // TODO: compare as size string 2788 "keep-alive", 2789 mhd_SSTR_LEN ("keep-alive"))) 2790 has_keepalive = true; 2791 } 2792 2793 continue; 2794 } 2795 2796 /* "Transfer-Encoding:" */ 2797 if (mhd_str_equal_caseless_n_st (MHD_HTTP_HEADER_TRANSFER_ENCODING, 2798 f->field.nv.name.cstr, 2799 f->field.nv.name.len)) 2800 { 2801 if (mhd_str_equal_caseless_n_st ("chunked", 2802 f->field.nv.value.cstr, 2803 f->field.nv.value.len)) 2804 { 2805 c->rq.have_chunked_upload = true; 2806 c->rq.cntn.cntn_size = MHD_SIZE_UNKNOWN; 2807 } 2808 else 2809 { 2810 mhd_LOG_MSG (c->daemon, MHD_SC_TRANSFER_ENCODING_UNSUPPORTED, \ 2811 "The 'Transfer-Encoding' used in request is " \ 2812 "unsupported or invalid."); 2813 mhd_RESPOND_WITH_ERROR_STATIC (c, 2814 MHD_HTTP_STATUS_BAD_REQUEST, 2815 ERR_RSP_UNSUPPORTED_TR_ENCODING); 2816 return; 2817 } 2818 has_trenc = true; 2819 continue; 2820 } 2821 2822 #ifdef MHD_SUPPORT_COOKIES 2823 /* "Cookie:" */ 2824 if ((! c->daemon->req_cfg.disable_cookies) && 2825 mhd_str_equal_caseless_n_st (MHD_HTTP_HEADER_COOKIE, 2826 f->field.nv.name.cstr, 2827 f->field.nv.name.len)) 2828 { 2829 if (MHD_PARSE_COOKIE_NO_MEMORY == 2830 parse_cookie_header (c, 2831 &(f->field.nv.value))) 2832 { 2833 handle_req_cookie_no_space (c); 2834 return; 2835 } 2836 continue; 2837 } 2838 #endif /* MHD_SUPPORT_COOKIES */ 2839 2840 /* "Expect: 100-continue" */ 2841 if (mhd_str_equal_caseless_n_st (MHD_HTTP_HEADER_EXPECT, 2842 f->field.nv.name.cstr, 2843 f->field.nv.name.len)) 2844 { 2845 if (mhd_str_equal_caseless_n_st ("100-continue", 2846 f->field.nv.value.cstr, 2847 f->field.nv.value.len)) 2848 c->rq.have_expect_100 = true; 2849 else 2850 { 2851 if (0 < c->daemon->req_cfg.strictness) 2852 { 2853 mhd_LOG_MSG (c->daemon, MHD_SC_EXPECT_HEADER_VALUE_UNSUPPORTED, \ 2854 "The 'Expect' header value used in request is " \ 2855 "unsupported or invalid."); 2856 mhd_RESPOND_WITH_ERROR_STATIC (c, 2857 MHD_HTTP_STATUS_EXPECTATION_FAILED, 2858 ERR_RSP_UNSUPPORTED_EXPECT_HDR_VALUE); 2859 return; 2860 } 2861 } 2862 continue; 2863 } 2864 } 2865 2866 c->rq.cntn.cntn_present = (has_trenc || has_cntnlen); 2867 if (has_trenc && has_cntnlen) 2868 { 2869 if (0 < c->daemon->req_cfg.strictness) 2870 { 2871 mhd_RESPOND_WITH_ERROR_STATIC ( \ 2872 c, \ 2873 MHD_HTTP_STATUS_BAD_REQUEST, \ 2874 ERR_RSP_REQUEST_CNTNLENGTH_WITH_TR_ENCODING); 2875 return; 2876 } 2877 /* Must close connection after reply to prevent potential attack */ 2878 c->conn_reuse = mhd_CONN_MUST_CLOSE; 2879 c->rq.cntn.cntn_size = MHD_SIZE_UNKNOWN; 2880 mhd_assert (c->rq.have_chunked_upload); 2881 mhd_LOG_MSG (c->daemon, MHD_SC_CONTENT_LENGTH_AND_TR_ENC, \ 2882 "The 'Content-Length' request header is ignored " \ 2883 "as chunked 'Transfer-Encoding' is used " \ 2884 "for this request."); 2885 } 2886 2887 if (MHD_HTTP_VERSION_1_1 <= c->rq.http_ver) 2888 { 2889 if ((! has_host) && 2890 (-3 < c->daemon->req_cfg.strictness)) 2891 { 2892 mhd_LOG_MSG (c->daemon, MHD_SC_HOST_HEADER_MISSING, \ 2893 "Received HTTP/1.1 request without 'Host' header."); 2894 mhd_RESPOND_WITH_ERROR_STATIC (c, 2895 MHD_HTTP_STATUS_BAD_REQUEST, 2896 ERR_RSP_REQUEST_LACKS_HOST); 2897 return; 2898 } 2899 } 2900 else 2901 { 2902 if (! has_keepalive) 2903 c->conn_reuse = mhd_CONN_MUST_CLOSE; /* Do not re-use HTTP/1.0 connection by default */ 2904 if (has_trenc) 2905 c->conn_reuse = mhd_CONN_MUST_CLOSE; /* Framing could be incorrect */ 2906 } 2907 2908 c->stage = mhd_HTTP_STAGE_HEADERS_PROCESSED; 2909 return; 2910 } 2911 2912 2913 /** 2914 * Is "100 Continue" needed to be sent for current request? 2915 * 2916 * @param c the connection to check 2917 * @return false 100 CONTINUE is not needed, 2918 * true otherwise 2919 */ 2920 static MHD_FN_PAR_NONNULL_ALL_ bool 2921 need_100_continue (struct MHD_Connection *restrict c) 2922 { 2923 mhd_assert (MHD_HTTP_VERSION_IS_SUPPORTED (c->rq.http_ver)); 2924 mhd_assert (mhd_HTTP_STAGE_HEADERS_PROCESSED <= c->stage); 2925 mhd_assert (mhd_HTTP_STAGE_BODY_RECEIVING > c->stage); 2926 2927 if (! c->rq.have_expect_100) 2928 return false; /* "100 Continue" has not been requested by the client */ 2929 2930 if (0 != c->read_buffer_offset) 2931 return false; /* Part of the content has been received already */ 2932 2933 if (0 == c->rq.cntn.cntn_size) 2934 return false; /* There is no content or zero-sized content for this request */ 2935 2936 if (MHD_HTTP_VERSION_1_0 == c->rq.http_ver) 2937 return false; /* '100 Continue' is not allowed for HTTP/1.0 */ 2938 2939 return true; 2940 } 2941 2942 2943 /** 2944 * Check whether special buffer is required to handle the upload content and 2945 * try to allocate if necessary. 2946 * Respond with error to the client if buffer cannot be allocated 2947 * @param c the connection to 2948 * @return true if succeed, 2949 * false if error response is set 2950 */ 2951 static MHD_FN_PAR_NONNULL_ALL_ bool 2952 check_and_alloc_buf_for_upload_processing (struct MHD_Connection *restrict c) 2953 { 2954 mhd_assert ((mhd_ACTION_UPLOAD == c->rq.app_act.head_act.act) || \ 2955 (mhd_ACTION_POST_PARSE == c->rq.app_act.head_act.act)); 2956 2957 if (c->rq.have_chunked_upload) 2958 return true; /* The size is unknown, buffers will be dynamically allocated 2959 and re-allocated */ 2960 mhd_assert (c->read_buffer_size > c->read_buffer_offset); 2961 #if 0 // TODO: support processing full response in the connection buffer 2962 if ((c->read_buffer_size - c->read_buffer_offset) >= 2963 c->rq.cntn.cntn_size) 2964 return true; /* No additional buffer needed */ 2965 #endif 2966 2967 if ((mhd_ACTION_UPLOAD == c->rq.app_act.head_act.act) && 2968 (NULL == c->rq.app_act.head_act.data.upload.full.cb)) 2969 return true; /* data will be processed only incrementally */ 2970 2971 if (mhd_ACTION_UPLOAD != c->rq.app_act.head_act.act) 2972 { 2973 // TODO: add check for intermental-only POST processing */ 2974 mhd_assert (0 && "Not implemented yet"); 2975 return false; 2976 } 2977 2978 if ((c->rq.cntn.cntn_size > 2979 c->rq.app_act.head_act.data.upload.large_buffer_size) || 2980 ! mhd_daemon_get_lbuf (c->daemon, 2981 (size_t) c->rq.cntn.cntn_size, 2982 &(c->rq.cntn.lbuf))) 2983 { 2984 if (NULL != c->rq.app_act.head_act.data.upload.inc.cb) 2985 { 2986 c->rq.app_act.head_act.data.upload.full.cb = NULL; 2987 return true; /* Data can be processed incrementally */ 2988 } 2989 2990 mhd_RESPOND_WITH_ERROR_STATIC (c, 2991 MHD_HTTP_STATUS_CONTENT_TOO_LARGE, 2992 ERR_RSP_REQUEST_CONTENTLENGTH_TOOLARGE); 2993 return false; 2994 } 2995 2996 return true; 2997 } 2998 2999 3000 MHD_INTERNAL MHD_FN_PAR_NONNULL_ALL_ bool 3001 mhd_stream_call_app_request_cb (struct MHD_Connection *restrict c) 3002 { 3003 struct MHD_Daemon *restrict d = c->daemon; 3004 struct MHD_String path; 3005 const struct MHD_Action *a; 3006 3007 mhd_assert (mhd_HTTP_METHOD_NO_METHOD != c->rq.http_mthd); 3008 mhd_assert (NULL == c->rp.response); 3009 3010 if (mhd_ACTION_NO_ACTION != c->rq.app_act.head_act.act) 3011 MHD_PANIC ("MHD_Action has been set already"); 3012 3013 path.cstr = c->rq.url; 3014 path.len = c->rq.url_len; 3015 3016 c->rq.app_aware = true; 3017 a = d->req_cfg.cb (d->req_cfg.cb_cls, 3018 &(c->rq), 3019 &path, 3020 (enum MHD_HTTP_Method) c->rq.http_mthd, 3021 c->rq.cntn.cntn_size); 3022 3023 if ((NULL != a) 3024 && (((&(c->rq.app_act.head_act) != a)) 3025 || ! mhd_ACTION_IS_VALID (c->rq.app_act.head_act.act))) 3026 { 3027 mhd_LOG_MSG (d, MHD_SC_ACTION_INVALID, \ 3028 "Provided action is not a correct action generated " \ 3029 "for the current request."); 3030 /* Perform cleanup of the created but now unused action */ 3031 switch (c->rq.app_act.head_act.act) 3032 { 3033 case mhd_ACTION_RESPONSE: 3034 mhd_assert (NULL != c->rq.app_act.head_act.data.response); 3035 mhd_response_dec_use_count (c->rq.app_act.head_act.data.response); 3036 break; 3037 case mhd_ACTION_UPLOAD: 3038 case mhd_ACTION_SUSPEND: 3039 /* No cleanup needed */ 3040 break; 3041 #ifdef MHD_SUPPORT_POST_PARSER 3042 case mhd_ACTION_POST_PARSE: 3043 /* No cleanup needed */ 3044 break; 3045 #endif /* MHD_SUPPORT_POST_PARSER */ 3046 #ifdef MHD_SUPPORT_UPGRADE 3047 case mhd_ACTION_UPGRADE: 3048 /* No cleanup needed */ 3049 break; 3050 #endif /* MHD_SUPPORT_UPGRADE */ 3051 case mhd_ACTION_ABORT: 3052 mhd_UNREACHABLE (); 3053 break; 3054 case mhd_ACTION_NO_ACTION: 3055 default: 3056 break; 3057 } 3058 a = NULL; 3059 } 3060 if (NULL == a) 3061 c->rq.app_act.head_act.act = mhd_ACTION_ABORT; 3062 3063 switch (c->rq.app_act.head_act.act) 3064 { 3065 case mhd_ACTION_RESPONSE: 3066 c->rp.response = c->rq.app_act.head_act.data.response; 3067 c->stage = mhd_HTTP_STAGE_REQ_RECV_FINISHED; 3068 return true; 3069 case mhd_ACTION_UPLOAD: 3070 if (0 != c->rq.cntn.cntn_size) 3071 { 3072 if (! check_and_alloc_buf_for_upload_processing (c)) 3073 return true; 3074 if (need_100_continue (c)) 3075 { 3076 c->stage = mhd_HTTP_STAGE_CONTINUE_SENDING; 3077 return true; 3078 } 3079 c->stage = mhd_HTTP_STAGE_BODY_RECEIVING; 3080 return (0 != c->read_buffer_offset); 3081 } 3082 c->stage = mhd_HTTP_STAGE_FULL_REQ_RECEIVED; 3083 return true; 3084 #ifdef MHD_SUPPORT_POST_PARSER 3085 case mhd_ACTION_POST_PARSE: 3086 if (0 == c->rq.cntn.cntn_size) 3087 { 3088 c->rq.u_proc.post.parse_result = MHD_POST_PARSE_RES_REQUEST_EMPTY; 3089 c->stage = mhd_HTTP_STAGE_FULL_REQ_RECEIVED; 3090 return true; 3091 } 3092 if (! mhd_stream_prepare_for_post_parse (c)) 3093 { 3094 mhd_assert (mhd_HTTP_STAGE_FOOTERS_RECEIVED < c->stage); 3095 return true; 3096 } 3097 if (need_100_continue (c)) 3098 { 3099 c->stage = mhd_HTTP_STAGE_CONTINUE_SENDING; 3100 return true; 3101 } 3102 c->stage = mhd_HTTP_STAGE_BODY_RECEIVING; 3103 return true; 3104 #endif /* MHD_SUPPORT_POST_PARSER */ 3105 case mhd_ACTION_SUSPEND: 3106 c->suspended = true; 3107 #ifdef mhd_DEBUG_SUSPEND_RESUME 3108 fprintf (stderr, 3109 "%%%%%% Suspending connection, FD: %2llu\n", 3110 (unsigned long long) c->sk.fd); 3111 #endif /* mhd_DEBUG_SUSPEND_RESUME */ 3112 c->rq.app_act.head_act.act = mhd_ACTION_NO_ACTION; 3113 return false; 3114 #ifdef MHD_SUPPORT_UPGRADE 3115 case mhd_ACTION_UPGRADE: 3116 mhd_assert (0 == c->rq.cntn.cntn_size); 3117 c->stage = mhd_HTTP_STAGE_UPGRADE_HEADERS_SENDING; 3118 return false; 3119 #endif /* MHD_SUPPORT_UPGRADE */ 3120 case mhd_ACTION_ABORT: 3121 mhd_conn_start_closing_app_abort (c); 3122 return true; 3123 case mhd_ACTION_NO_ACTION: 3124 default: 3125 mhd_assert (0 && "Impossible value"); 3126 break; 3127 } 3128 mhd_UNREACHABLE (); 3129 return false; 3130 } 3131 3132 3133 /** 3134 * React on provided action for upload 3135 * @param c the stream to use 3136 * @param act the action provided by application 3137 * @param final set to 'true' if this is final upload callback 3138 * @return true if connection state has been changed, 3139 * false otherwise 3140 */ 3141 MHD_INTERNAL 3142 MHD_FN_PAR_NONNULL_ (1) bool 3143 mhd_stream_process_upload_action (struct MHD_Connection *restrict c, 3144 const struct MHD_UploadAction *act, 3145 bool final) 3146 { 3147 if (NULL != act) 3148 { 3149 if ((&(c->rq.app_act.upl_act) != act) || 3150 ! mhd_UPLOAD_ACTION_IS_VALID (c->rq.app_act.upl_act.act) || 3151 (final && 3152 (mhd_UPLOAD_ACTION_CONTINUE == c->rq.app_act.upl_act.act))) 3153 { 3154 /* Perform cleanup of the created but now unused action */ 3155 switch (c->rq.app_act.upl_act.act) 3156 { 3157 case mhd_UPLOAD_ACTION_RESPONSE: 3158 mhd_assert (NULL != c->rq.app_act.upl_act.data.response); 3159 mhd_response_dec_use_count (c->rq.app_act.upl_act.data.response); 3160 break; 3161 case mhd_UPLOAD_ACTION_CONTINUE: 3162 case mhd_UPLOAD_ACTION_SUSPEND: 3163 /* No cleanup needed */ 3164 break; 3165 #ifdef MHD_SUPPORT_UPGRADE 3166 case mhd_UPLOAD_ACTION_UPGRADE: 3167 /* No cleanup needed */ 3168 break; 3169 #endif /* MHD_SUPPORT_UPGRADE */ 3170 case mhd_UPLOAD_ACTION_ABORT: 3171 mhd_UNREACHABLE (); 3172 break; 3173 case mhd_UPLOAD_ACTION_NO_ACTION: 3174 default: 3175 break; 3176 } 3177 mhd_LOG_MSG (c->daemon, MHD_SC_UPLOAD_ACTION_INVALID, \ 3178 "Provided action is not a correct action generated " \ 3179 "for the current request."); 3180 act = NULL; 3181 } 3182 } 3183 if (NULL == act) 3184 c->rq.app_act.upl_act.act = mhd_UPLOAD_ACTION_ABORT; 3185 3186 switch (c->rq.app_act.upl_act.act) 3187 { 3188 case mhd_UPLOAD_ACTION_RESPONSE: 3189 c->rp.response = c->rq.app_act.upl_act.data.response; 3190 c->stage = mhd_HTTP_STAGE_REQ_RECV_FINISHED; 3191 return true; 3192 case mhd_UPLOAD_ACTION_CONTINUE: 3193 memset (&(c->rq.app_act.upl_act), 0, sizeof(c->rq.app_act.upl_act)); 3194 return false; 3195 case mhd_UPLOAD_ACTION_SUSPEND: 3196 c->suspended = true; 3197 #ifdef mhd_DEBUG_SUSPEND_RESUME 3198 fprintf (stderr, 3199 "%%%%%% Suspending connection, FD: %2llu\n", 3200 (unsigned long long) c->sk.fd); 3201 #endif /* mhd_DEBUG_SUSPEND_RESUME */ 3202 memset (&(c->rq.app_act.upl_act), 0, sizeof(c->rq.app_act.upl_act)); 3203 return false; 3204 #ifdef MHD_SUPPORT_UPGRADE 3205 case mhd_UPLOAD_ACTION_UPGRADE: 3206 mhd_assert (c->rq.cntn.recv_size == c->rq.cntn.cntn_size); 3207 mhd_assert (! c->rq.have_chunked_upload || \ 3208 mhd_HTTP_STAGE_FULL_REQ_RECEIVED == c->stage); 3209 c->stage = mhd_HTTP_STAGE_UPGRADE_HEADERS_SENDING; 3210 return false; 3211 #endif /* MHD_SUPPORT_UPGRADE */ 3212 case mhd_UPLOAD_ACTION_ABORT: 3213 mhd_conn_start_closing_app_abort (c); 3214 return true; 3215 case mhd_UPLOAD_ACTION_NO_ACTION: 3216 default: 3217 mhd_assert (0 && "Impossible value"); 3218 break; 3219 } 3220 mhd_UNREACHABLE (); 3221 return false; 3222 } 3223 3224 3225 static MHD_FN_PAR_NONNULL_ALL_ bool 3226 process_request_chunked_body (struct MHD_Connection *restrict c) 3227 { 3228 struct MHD_Daemon *restrict d = c->daemon; 3229 size_t available; 3230 bool has_more_data; 3231 char *restrict buffer_head; 3232 const int discp_lvl = d->req_cfg.strictness; 3233 /* Treat bare LF as the end of the line. 3234 RFC 9112, section 2.2-3 3235 Note: MHD never replaces bare LF with space (RFC 9110, section 5.5-5). 3236 Bare LF is processed as end of the line or rejected as broken request. */ 3237 const bool bare_lf_as_crlf = mhd_ALLOW_BARE_LF_AS_CRLF (discp_lvl); 3238 /* Allow "Bad WhiteSpace" in chunk extension. 3239 RFC 9112, Section 7.1.1, Paragraph 2 */ 3240 const bool allow_bws = (2 < discp_lvl); 3241 bool state_updated; 3242 3243 mhd_assert (NULL == c->rp.response); 3244 mhd_assert (c->rq.have_chunked_upload); 3245 mhd_assert (MHD_SIZE_UNKNOWN == c->rq.cntn.cntn_size); 3246 3247 buffer_head = c->read_buffer; 3248 available = c->read_buffer_offset; 3249 state_updated = false; 3250 do 3251 { 3252 size_t cntn_data_ready; 3253 bool need_inc_proc; 3254 3255 has_more_data = false; 3256 3257 if ( (c->rq.current_chunk_offset == 3258 c->rq.current_chunk_size) && 3259 (0 != c->rq.current_chunk_size) ) 3260 { 3261 size_t i; 3262 mhd_assert (0 != available); 3263 /* skip new line at the *end* of a chunk */ 3264 i = 0; 3265 if ( (2 <= available) && 3266 ('\r' == buffer_head[0]) && 3267 ('\n' == buffer_head[1]) ) 3268 i += 2; /* skip CRLF */ 3269 else if (bare_lf_as_crlf && ('\n' == buffer_head[0])) 3270 i++; /* skip bare LF */ 3271 else if (2 > available) 3272 break; /* need more upload data */ 3273 if (0 == i) 3274 { 3275 /* malformed encoding */ 3276 mhd_RESPOND_WITH_ERROR_STATIC (c, 3277 MHD_HTTP_STATUS_BAD_REQUEST, 3278 ERR_RSP_REQUEST_CHUNKED_MALFORMED); 3279 return true; 3280 } 3281 available -= i; 3282 buffer_head += i; 3283 c->rq.current_chunk_offset = 0; 3284 c->rq.current_chunk_size = 0; 3285 if (0 == available) 3286 break; 3287 } 3288 if (0 != c->rq.current_chunk_size) 3289 { 3290 uint_fast64_t cur_chunk_left; 3291 mhd_assert (c->rq.current_chunk_offset < \ 3292 c->rq.current_chunk_size); 3293 /* we are in the middle of a chunk, give 3294 as much as possible to the client (without 3295 crossing chunk boundaries) */ 3296 cur_chunk_left 3297 = c->rq.current_chunk_size 3298 - c->rq.current_chunk_offset; 3299 if (cur_chunk_left > available) 3300 cntn_data_ready = available; 3301 else 3302 { /* cur_chunk_left <= (size_t)available */ 3303 cntn_data_ready = (size_t) cur_chunk_left; 3304 if (available > cntn_data_ready) 3305 has_more_data = true; 3306 } 3307 } 3308 else 3309 { /* Need the parse the chunk size line */ 3310 /** The number of found digits in the chunk size number */ 3311 size_t num_dig; 3312 uint_fast64_t chunk_size; 3313 bool broken; 3314 bool overflow; 3315 3316 mhd_assert (0 != available); 3317 3318 overflow = false; 3319 chunk_size = 0; /* Mute possible compiler warning. 3320 The real value will be set later. */ 3321 3322 num_dig = mhd_strx_to_uint64_n (buffer_head, 3323 available, 3324 &chunk_size); 3325 mhd_assert (num_dig <= available); 3326 if (num_dig == available) 3327 continue; /* Need line delimiter */ 3328 3329 broken = (0 == num_dig); 3330 if (broken) 3331 /* Check whether result is invalid due to uint64_t overflow */ 3332 overflow = ((('0' <= buffer_head[0]) && ('9' >= buffer_head[0])) || 3333 (('A' <= buffer_head[0]) && ('F' >= buffer_head[0])) || 3334 (('a' <= buffer_head[0]) && ('f' >= buffer_head[0]))); 3335 else 3336 { 3337 /** 3338 * The length of the string with the number of the chunk size, 3339 * including chunk extension 3340 */ 3341 size_t chunk_size_line_len; 3342 3343 chunk_size_line_len = 0; 3344 if ((';' == buffer_head[num_dig]) || 3345 (allow_bws && 3346 ((' ' == buffer_head[num_dig]) || 3347 ('\t' == buffer_head[num_dig])))) 3348 { /* Chunk extension */ 3349 size_t i; 3350 3351 /* Skip bad whitespaces (if any) */ 3352 for (i = num_dig; i < available; ++i) 3353 { 3354 if ((' ' != buffer_head[i]) && ('\t' != buffer_head[i])) 3355 break; 3356 } 3357 if (i == available) 3358 break; /* need more data */ 3359 if (';' == buffer_head[i]) 3360 { 3361 for (++i; i < available; ++i) 3362 { 3363 if ('\n' == buffer_head[i]) 3364 break; 3365 } 3366 if (i == available) 3367 break; /* need more data */ 3368 mhd_assert (i > num_dig); 3369 mhd_assert (1 <= i); 3370 /* Found LF position */ 3371 if (bare_lf_as_crlf) 3372 chunk_size_line_len = i; /* Don't care about CR before LF */ 3373 else if ('\r' == buffer_head[i - 1]) 3374 chunk_size_line_len = i; 3375 } 3376 else 3377 { /* No ';' after "bad whitespace" */ 3378 mhd_assert (allow_bws); 3379 mhd_assert (0 == chunk_size_line_len); 3380 } 3381 } 3382 else 3383 { 3384 mhd_assert (available >= num_dig); 3385 if ((2 <= (available - num_dig)) && 3386 ('\r' == buffer_head[num_dig]) && 3387 ('\n' == buffer_head[num_dig + 1])) 3388 chunk_size_line_len = num_dig + 2; 3389 else if (bare_lf_as_crlf && 3390 ('\n' == buffer_head[num_dig])) 3391 chunk_size_line_len = num_dig + 1; 3392 else if (2 > (available - num_dig)) 3393 break; /* need more data */ 3394 } 3395 3396 if (0 != chunk_size_line_len) 3397 { /* Valid termination of the chunk size line */ 3398 mhd_assert (chunk_size_line_len <= available); 3399 /* Start reading payload data of the chunk */ 3400 c->rq.current_chunk_offset = 0; 3401 c->rq.current_chunk_size = chunk_size; 3402 3403 available -= chunk_size_line_len; 3404 buffer_head += chunk_size_line_len; 3405 3406 if (0 == chunk_size) 3407 { /* The final (termination) chunk */ 3408 c->rq.cntn.cntn_size = c->rq.cntn.recv_size; 3409 c->stage = mhd_HTTP_STAGE_BODY_RECEIVED; 3410 state_updated = true; 3411 break; 3412 } 3413 if (available > 0) 3414 has_more_data = true; 3415 continue; 3416 } 3417 /* Invalid chunk size line */ 3418 } 3419 3420 if (! overflow) 3421 mhd_RESPOND_WITH_ERROR_STATIC (c, 3422 MHD_HTTP_STATUS_BAD_REQUEST, 3423 ERR_RSP_REQUEST_CHUNKED_MALFORMED); 3424 else 3425 mhd_RESPOND_WITH_ERROR_STATIC (c, 3426 MHD_HTTP_STATUS_CONTENT_TOO_LARGE, 3427 ERR_RSP_REQUEST_CHUNK_TOO_LARGE); 3428 return true; 3429 } 3430 mhd_assert (c->rq.app_aware); 3431 3432 #ifdef MHD_SUPPORT_POST_PARSER 3433 if (mhd_ACTION_POST_PARSE == c->rq.app_act.head_act.act) 3434 { 3435 size_t size_provided; 3436 3437 c->rq.cntn.recv_size += cntn_data_ready; 3438 size_provided = cntn_data_ready; 3439 3440 state_updated = mhd_stream_post_parse (c, 3441 &cntn_data_ready, 3442 buffer_head); 3443 // TODO: support one chunk in-place processing? 3444 mhd_assert ((0 == cntn_data_ready) || \ 3445 (MHD_POST_PARSE_RES_OK != c->rq.u_proc.post.parse_result) || \ 3446 (mhd_HTTP_STAGE_BODY_RECEIVING != c->stage)); 3447 if (mhd_HTTP_STAGE_BODY_RECEIVING != c->stage) 3448 c->discard_request = true; 3449 c->rq.cntn.recv_size += size_provided; 3450 } 3451 else 3452 #endif /* MHD_SUPPORT_POST_PARSER */ 3453 if (1) 3454 { 3455 mhd_assert (mhd_ACTION_UPLOAD == c->rq.app_act.head_act.act); 3456 if (NULL != c->rq.app_act.head_act.data.upload.full.cb) 3457 { 3458 need_inc_proc = false; 3459 3460 mhd_assert (0 == c->rq.cntn.proc_size); 3461 if ((uint_fast64_t) c->rq.cntn.lbuf.size < 3462 c->rq.cntn.recv_size + cntn_data_ready) 3463 { 3464 size_t grow_size; 3465 3466 grow_size = (size_t) (c->rq.cntn.recv_size + cntn_data_ready 3467 - c->rq.cntn.lbuf.size); 3468 if (((size_t) (c->rq.cntn.recv_size + cntn_data_ready) < 3469 cntn_data_ready) || 3470 (! mhd_daemon_grow_lbuf (d, 3471 grow_size, 3472 &(c->rq.cntn.lbuf)))) 3473 { 3474 /* Failed to grow the buffer, no space to put the new data */ 3475 const struct MHD_UploadAction *act; 3476 if (NULL != c->rq.app_act.head_act.data.upload.inc.cb) 3477 { 3478 mhd_RESPOND_WITH_ERROR_STATIC ( 3479 c, 3480 MHD_HTTP_STATUS_CONTENT_TOO_LARGE, 3481 ERR_RSP_MSG_REQUEST_TOO_BIG); 3482 return true; 3483 } 3484 c->rq.app_act.head_act.data.upload.full.cb = NULL; /* Cannot process "full" content */ 3485 /* Process previously buffered data */ 3486 mhd_assert (c->rq.cntn.recv_size <= c->rq.cntn.lbuf.size); 3487 act = c->rq.app_act.head_act.data.upload.inc.cb ( 3488 c->rq.app_act.head_act.data.upload.inc.cls, 3489 &(c->rq), 3490 (size_t) c->rq.cntn.recv_size, 3491 c->rq.cntn.lbuf.data); 3492 c->rq.cntn.proc_size = c->rq.cntn.recv_size; 3493 mhd_daemon_free_lbuf (d, &(c->rq.cntn.lbuf)); 3494 if (mhd_stream_process_upload_action (c, act, false)) 3495 return true; 3496 need_inc_proc = true; 3497 } 3498 } 3499 if (! need_inc_proc) 3500 { 3501 memcpy (c->rq.cntn.lbuf.data + c->rq.cntn.recv_size, 3502 buffer_head, cntn_data_ready); 3503 c->rq.cntn.recv_size += cntn_data_ready; 3504 } 3505 } 3506 else 3507 need_inc_proc = true; 3508 3509 if (need_inc_proc) 3510 { 3511 const struct MHD_UploadAction *act; 3512 mhd_assert (NULL != c->rq.app_act.head_act.data.upload.inc.cb); 3513 3514 c->rq.cntn.recv_size += cntn_data_ready; 3515 act = c->rq.app_act.head_act.data.upload.inc.cb ( 3516 c->rq.app_act.head_act.data.upload.inc.cls, 3517 &(c->rq), 3518 cntn_data_ready, 3519 buffer_head); 3520 c->rq.cntn.proc_size += cntn_data_ready; 3521 state_updated = mhd_stream_process_upload_action (c, act, false); 3522 } 3523 } 3524 /* dh left "processed" bytes in buffer for next time... */ 3525 buffer_head += cntn_data_ready; 3526 available -= cntn_data_ready; 3527 mhd_assert (MHD_SIZE_UNKNOWN == c->rq.cntn.cntn_size); 3528 c->rq.current_chunk_offset += cntn_data_ready; 3529 } while (has_more_data && ! state_updated); 3530 /* TODO: optionally? zero out reused memory region */ 3531 if ( (available > 0) && 3532 (buffer_head != c->read_buffer) ) 3533 memmove (c->read_buffer, 3534 buffer_head, 3535 available); 3536 else 3537 mhd_assert ((0 == available) || \ 3538 (c->read_buffer_offset == available)); 3539 c->read_buffer_offset = available; 3540 3541 return state_updated; 3542 } 3543 3544 3545 static MHD_FN_PAR_NONNULL_ALL_ bool 3546 process_request_nonchunked_body (struct MHD_Connection *restrict c) 3547 { 3548 size_t cntn_data_ready; 3549 bool read_buf_reuse; 3550 bool state_updated; 3551 3552 mhd_assert (NULL == c->rp.response); 3553 mhd_assert (! c->rq.have_chunked_upload); 3554 mhd_assert (MHD_SIZE_UNKNOWN != c->rq.cntn.cntn_size); 3555 mhd_assert (c->rq.cntn.recv_size < c->rq.cntn.cntn_size); 3556 mhd_assert (c->rq.app_aware); 3557 3558 if ((c->rq.cntn.cntn_size - c->rq.cntn.recv_size) < c->read_buffer_offset) 3559 cntn_data_ready = (size_t) (c->rq.cntn.cntn_size - c->rq.cntn.recv_size); 3560 else 3561 cntn_data_ready = c->read_buffer_offset; 3562 3563 read_buf_reuse = false; 3564 state_updated = false; 3565 mhd_assert (! read_buf_reuse); /* Mute analyser warning */ 3566 3567 #ifdef MHD_SUPPORT_POST_PARSER 3568 if (mhd_ACTION_POST_PARSE == c->rq.app_act.head_act.act) 3569 { 3570 size_t size_provided; 3571 // TODO: rework to correctly support partial processing 3572 // TODO: rework to support receiving directly into "large buffer" 3573 c->rq.cntn.recv_size += cntn_data_ready; 3574 size_provided = cntn_data_ready; 3575 3576 state_updated = mhd_stream_post_parse (c, 3577 &size_provided, 3578 c->read_buffer); 3579 mhd_assert ((0 == size_provided) || \ 3580 (MHD_POST_PARSE_RES_OK != c->rq.u_proc.post.parse_result) || \ 3581 (mhd_HTTP_STAGE_BODY_RECEIVING != c->stage)); 3582 if (mhd_HTTP_STAGE_BODY_RECEIVING != c->stage) 3583 c->discard_request = true; 3584 3585 read_buf_reuse = true; 3586 c->rq.cntn.proc_size += cntn_data_ready; 3587 if (c->rq.cntn.recv_size == c->rq.cntn.cntn_size) 3588 { 3589 c->stage = mhd_HTTP_STAGE_FULL_REQ_RECEIVED; 3590 state_updated = true; 3591 } 3592 } 3593 else 3594 #endif /* MHD_SUPPORT_POST_PARSER */ 3595 if (1) 3596 { 3597 mhd_assert (mhd_ACTION_UPLOAD == c->rq.app_act.head_act.act); 3598 if (NULL != c->rq.app_act.head_act.data.upload.full.cb) 3599 { 3600 // TODO: implement processing in pool memory if buffer is large enough 3601 mhd_assert ((c->rq.cntn.recv_size + cntn_data_ready) <= 3602 (uint_fast64_t) c->rq.cntn.lbuf.size); 3603 memcpy (c->rq.cntn.lbuf.data + c->rq.cntn.recv_size, 3604 c->read_buffer, cntn_data_ready); 3605 c->rq.cntn.recv_size += cntn_data_ready; 3606 read_buf_reuse = true; 3607 if (c->rq.cntn.recv_size == c->rq.cntn.cntn_size) 3608 { 3609 c->stage = mhd_HTTP_STAGE_FULL_REQ_RECEIVED; 3610 state_updated = true; 3611 } 3612 } 3613 else 3614 { 3615 const struct MHD_UploadAction *act; 3616 mhd_assert (NULL != c->rq.app_act.head_act.data.upload.inc.cb); 3617 3618 c->rq.cntn.recv_size += cntn_data_ready; 3619 act = c->rq.app_act.head_act.data.upload.inc.cb ( 3620 c->rq.app_act.head_act.data.upload.inc.cls, 3621 &(c->rq), 3622 cntn_data_ready, 3623 c->read_buffer); 3624 c->rq.cntn.proc_size += cntn_data_ready; 3625 read_buf_reuse = true; 3626 state_updated = mhd_stream_process_upload_action (c, act, false); 3627 } 3628 } 3629 3630 if (read_buf_reuse) 3631 { 3632 size_t data_left_size; 3633 mhd_assert (c->read_buffer_offset >= cntn_data_ready); 3634 data_left_size = c->read_buffer_offset - cntn_data_ready; 3635 if (0 != data_left_size) 3636 memmove (c->read_buffer, 3637 c->read_buffer + cntn_data_ready, 3638 data_left_size); 3639 c->read_buffer_offset = data_left_size; 3640 } 3641 3642 return state_updated; 3643 } 3644 3645 3646 MHD_INTERNAL MHD_FN_PAR_NONNULL_ALL_ bool 3647 mhd_stream_process_request_body (struct MHD_Connection *restrict c) 3648 { 3649 if (c->rq.have_chunked_upload) 3650 return process_request_chunked_body (c); 3651 3652 return process_request_nonchunked_body (c); 3653 } 3654 3655 3656 MHD_INTERNAL MHD_FN_PAR_NONNULL_ALL_ bool 3657 mhd_stream_call_app_final_upload_cb (struct MHD_Connection *restrict c) 3658 { 3659 const struct MHD_UploadAction *act; 3660 bool state_changed; 3661 mhd_assert (mhd_ACTION_POST_PARSE == c->rq.app_act.head_act.act || \ 3662 mhd_ACTION_UPLOAD == c->rq.app_act.head_act.act); 3663 3664 #ifdef MHD_SUPPORT_POST_PARSER 3665 if (mhd_ACTION_POST_PARSE == c->rq.app_act.head_act.act) 3666 return mhd_stream_process_post_finish (c); 3667 #endif /* MHD_SUPPORT_POST_PARSER */ 3668 3669 mhd_assert (mhd_ACTION_UPLOAD == c->rq.app_act.head_act.act); 3670 3671 if (NULL != c->rq.app_act.head_act.data.upload.full.cb) 3672 { 3673 mhd_assert (c->rq.cntn.recv_size == c->rq.cntn.cntn_size); 3674 mhd_assert (0 == c->rq.cntn.proc_size); 3675 mhd_assert (NULL != c->rq.cntn.lbuf.data); 3676 mhd_assert (c->rq.cntn.recv_size <= c->rq.cntn.lbuf.size); 3677 // TODO: implement processing in pool memory if it is large enough 3678 act = c->rq.app_act.head_act.data.upload.full.cb ( 3679 c->rq.app_act.head_act.data.upload.full.cls, 3680 &(c->rq), 3681 (size_t) c->rq.cntn.recv_size, 3682 c->rq.cntn.lbuf.data); 3683 c->rq.cntn.proc_size = c->rq.cntn.recv_size; 3684 } 3685 else 3686 { 3687 mhd_assert (NULL != c->rq.app_act.head_act.data.upload.inc.cb); 3688 mhd_assert (c->rq.cntn.cntn_size == c->rq.cntn.proc_size); 3689 act = c->rq.app_act.head_act.data.upload.inc.cb ( 3690 c->rq.app_act.head_act.data.upload.inc.cls, 3691 &(c->rq), 3692 0, 3693 NULL); 3694 } 3695 3696 state_changed = mhd_stream_process_upload_action (c, act, true); 3697 if (! c->suspended) 3698 mhd_daemon_free_lbuf (c->daemon, &(c->rq.cntn.lbuf)); 3699 3700 return state_changed; 3701 } 3702 3703 3704 MHD_INTERNAL MHD_FN_PAR_NONNULL_ALL_ bool 3705 mhd_stream_process_req_recv_finished (struct MHD_Connection *restrict c) 3706 { 3707 if (NULL != c->rq.cntn.lbuf.data) 3708 mhd_daemon_free_lbuf (c->daemon, &(c->rq.cntn.lbuf)); 3709 c->rq.cntn.lbuf.data = NULL; 3710 if (c->rq.cntn.cntn_size != c->rq.cntn.proc_size) 3711 c->discard_request = true; 3712 mhd_assert (NULL != c->rp.response); 3713 c->stage = mhd_HTTP_STAGE_START_REPLY; 3714 return true; 3715 } 3716 3717 3718 /** 3719 * Send error reply when receive buffer space exhausted while receiving 3720 * the chunk size line. 3721 * @param c the connection to handle 3722 * @param chunk_size_line the optional pointer to the partially received 3723 * the current chunk size line. 3724 * Could be not zero-terminated and can contain binary 3725 * zeros. 3726 * Can be NULL. 3727 * @param chunk_size_line_size the size of the @a chunk_size_line 3728 */ 3729 static void 3730 handle_req_chunk_size_line_no_space (struct MHD_Connection *c, 3731 const char *chunk_size_line, 3732 size_t chunk_size_line_size) 3733 { 3734 unsigned int err_code; 3735 3736 if (NULL != chunk_size_line) 3737 { 3738 const char *semicol; 3739 /* Check for chunk extension */ 3740 semicol = (const char *) 3741 memchr (chunk_size_line, 3742 ';', 3743 chunk_size_line_size); 3744 if (NULL != semicol) 3745 { /* Chunk extension present. It could be removed without any loss of the 3746 details of the request. */ 3747 mhd_RESPOND_WITH_ERROR_STATIC (c, 3748 MHD_HTTP_STATUS_CONTENT_TOO_LARGE, 3749 ERR_RSP_REQUEST_CHUNK_LINE_EXT_TOO_BIG); 3750 } 3751 } 3752 err_code = mhd_stream_get_no_space_err_status_code (c, 3753 MHD_PROC_RECV_BODY_CHUNKED, 3754 chunk_size_line_size, 3755 chunk_size_line); 3756 mhd_RESPOND_WITH_ERROR_STATIC (c, 3757 err_code, 3758 ERR_RSP_REQUEST_CHUNK_LINE_TOO_BIG); 3759 } 3760 3761 3762 /** 3763 * Handle situation with read buffer exhaustion. 3764 * Must be called when no more space left in the read buffer, no more 3765 * space left in the memory pool to grow the read buffer, but more data 3766 * need to be received from the client. 3767 * Could be called when the result of received data processing cannot be 3768 * stored in the memory pool (like some header). 3769 * @param c the connection to process 3770 * @param stage the receive stage where the exhaustion happens. 3771 */ 3772 static MHD_FN_PAR_NONNULL_ALL_ void 3773 handle_recv_no_space (struct MHD_Connection *c, 3774 enum MHD_ProcRecvDataStage stage) 3775 { 3776 mhd_assert (MHD_PROC_RECV_INIT <= stage); 3777 mhd_assert (MHD_PROC_RECV_FOOTERS >= stage); 3778 mhd_assert (mhd_HTTP_STAGE_FULL_REQ_RECEIVED > c->stage); 3779 mhd_assert ((MHD_PROC_RECV_INIT != stage) || \ 3780 (mhd_HTTP_STAGE_INIT == c->stage)); 3781 mhd_assert ((MHD_PROC_RECV_METHOD != stage) || \ 3782 (mhd_HTTP_STAGE_REQ_LINE_RECEIVING == c->stage)); 3783 mhd_assert ((MHD_PROC_RECV_URI != stage) || \ 3784 (mhd_HTTP_STAGE_REQ_LINE_RECEIVING == c->stage)); 3785 mhd_assert ((MHD_PROC_RECV_HTTPVER != stage) || \ 3786 (mhd_HTTP_STAGE_REQ_LINE_RECEIVING == c->stage)); 3787 mhd_assert ((MHD_PROC_RECV_HEADERS != stage) || \ 3788 (mhd_HTTP_STAGE_REQ_HEADERS_RECEIVING == c->stage)); 3789 mhd_assert (MHD_PROC_RECV_COOKIE != stage); /* handle_req_cookie_no_space() must be called directly */ 3790 mhd_assert ((MHD_PROC_RECV_BODY_NORMAL != stage) || \ 3791 (mhd_HTTP_STAGE_BODY_RECEIVING == c->stage)); 3792 mhd_assert ((MHD_PROC_RECV_BODY_CHUNKED != stage) || \ 3793 (mhd_HTTP_STAGE_BODY_RECEIVING == c->stage)); 3794 mhd_assert ((MHD_PROC_RECV_FOOTERS != stage) || \ 3795 (mhd_HTTP_STAGE_FOOTERS_RECEIVING == c->stage)); 3796 mhd_assert ((MHD_PROC_RECV_BODY_NORMAL != stage) || \ 3797 (! c->rq.have_chunked_upload)); 3798 mhd_assert ((MHD_PROC_RECV_BODY_CHUNKED != stage) || \ 3799 (c->rq.have_chunked_upload)); 3800 switch (stage) 3801 { 3802 case MHD_PROC_RECV_INIT: 3803 case MHD_PROC_RECV_METHOD: 3804 /* Some data has been received, but it is not clear yet whether 3805 * the received data is an valid HTTP request */ 3806 mhd_STREAM_ABORT (c, mhd_CONN_CLOSE_NO_POOL_MEM_FOR_REQUEST, \ 3807 "No space left in the read buffer when " \ 3808 "receiving the initial part of " \ 3809 "the request line."); 3810 return; 3811 case MHD_PROC_RECV_URI: 3812 case MHD_PROC_RECV_HTTPVER: 3813 /* Some data has been received, but the request line is incomplete */ 3814 mhd_assert (mhd_HTTP_METHOD_NO_METHOD != c->rq.http_mthd); 3815 mhd_assert (MHD_HTTP_VERSION_INVALID == c->rq.http_ver); 3816 /* A quick simple check whether the incomplete line looks 3817 * like an HTTP request */ 3818 if ((mhd_HTTP_METHOD_GET <= c->rq.http_mthd) && 3819 (mhd_HTTP_METHOD_DELETE >= c->rq.http_mthd)) 3820 { 3821 mhd_RESPOND_WITH_ERROR_STATIC (c, 3822 MHD_HTTP_STATUS_URI_TOO_LONG, 3823 ERR_RSP_MSG_REQUEST_TOO_BIG); 3824 return; 3825 } 3826 mhd_STREAM_ABORT (c, mhd_CONN_CLOSE_NO_POOL_MEM_FOR_REQUEST, \ 3827 "No space left in the read buffer when " \ 3828 "receiving the URI in " \ 3829 "the request line. " \ 3830 "The request uses non-standard HTTP request " \ 3831 "method token."); 3832 return; 3833 case MHD_PROC_RECV_HEADERS: 3834 handle_req_headers_no_space (c, c->read_buffer, c->read_buffer_offset); 3835 return; 3836 case MHD_PROC_RECV_BODY_NORMAL: 3837 /* A header probably has been added to a suspended connection and 3838 it took precisely all the space in the buffer. 3839 Very low probability. */ 3840 mhd_assert (! c->rq.have_chunked_upload); 3841 handle_req_headers_no_space (c, NULL, 0); // FIXME: check 3842 return; 3843 case MHD_PROC_RECV_BODY_CHUNKED: 3844 mhd_assert (c->rq.have_chunked_upload); 3845 if (c->rq.current_chunk_offset != c->rq.current_chunk_size) 3846 { /* Receiving content of the chunk */ 3847 /* A header probably has been added to a suspended connection and 3848 it took precisely all the space in the buffer. 3849 Very low probability. */ 3850 handle_req_headers_no_space (c, NULL, 0); // FIXME: check 3851 } 3852 else 3853 { 3854 if (0 != c->rq.current_chunk_size) 3855 { /* Waiting for chunk-closing CRLF */ 3856 /* Not really possible as some payload should be 3857 processed and the space used by payload should be available. */ 3858 handle_req_headers_no_space (c, NULL, 0); // FIXME: check 3859 } 3860 else 3861 { /* Reading the line with the chunk size */ 3862 handle_req_chunk_size_line_no_space (c, 3863 c->read_buffer, 3864 c->read_buffer_offset); 3865 } 3866 } 3867 return; 3868 case MHD_PROC_RECV_FOOTERS: 3869 handle_req_footers_no_space (c, c->read_buffer, c->read_buffer_offset); 3870 return; 3871 /* The next cases should not be possible */ 3872 case MHD_PROC_RECV_COOKIE: 3873 default: 3874 break; 3875 } 3876 mhd_UNREACHABLE (); 3877 } 3878 3879 3880 /** 3881 * Try growing the read buffer. We initially claim half the available 3882 * buffer space for the read buffer (the other half being left for 3883 * management data structures; the write buffer can in the end take 3884 * virtually everything as the read buffer can be reduced to the 3885 * minimum necessary at that point. 3886 * 3887 * @param connection the connection 3888 * @param required set to 'true' if grow is required, i.e. connection 3889 * will fail if no additional space is granted 3890 * @return 'true' on success, 'false' on failure 3891 */ 3892 static MHD_FN_PAR_NONNULL_ALL_ bool 3893 try_grow_read_buffer (struct MHD_Connection *restrict connection, 3894 bool required) 3895 { 3896 size_t new_size; 3897 size_t avail_size; 3898 const size_t def_grow_size = 1536; // TODO: remove hardcoded increment 3899 char *rb; 3900 3901 avail_size = mhd_pool_get_free (connection->pool); 3902 if (0 == avail_size) 3903 return false; /* No more space available */ 3904 if (0 == connection->read_buffer_size) 3905 new_size = avail_size / 2; /* Use half of available buffer for reading */ 3906 else 3907 { 3908 size_t grow_size; 3909 3910 grow_size = avail_size / 8; 3911 if (def_grow_size > grow_size) 3912 { /* Shortage of space */ 3913 const size_t left_free = 3914 connection->read_buffer_size - connection->read_buffer_offset; 3915 mhd_assert (connection->read_buffer_size >= \ 3916 connection->read_buffer_offset); 3917 if ((def_grow_size <= grow_size + left_free) 3918 && (left_free < def_grow_size)) 3919 grow_size = def_grow_size - left_free; /* Use precise 'def_grow_size' for new free space */ 3920 else if (! required) 3921 return false; /* Grow is not mandatory, leave some space in pool */ 3922 else 3923 { 3924 /* Shortage of space, but grow is mandatory */ 3925 const size_t small_inc = 3926 ((mhd_BUF_INC_SIZE > def_grow_size) ? 3927 def_grow_size : mhd_BUF_INC_SIZE) / 8; 3928 if (small_inc < avail_size) 3929 grow_size = small_inc; 3930 else 3931 grow_size = avail_size; 3932 } 3933 } 3934 new_size = connection->read_buffer_size + grow_size; 3935 } 3936 /* Make sure that read buffer will not be moved */ 3937 if ((NULL != connection->read_buffer) && 3938 ! mhd_pool_is_resizable_inplace (connection->pool, 3939 connection->read_buffer, 3940 connection->read_buffer_size)) 3941 { 3942 mhd_assert (0); 3943 return false; 3944 } 3945 /* we can actually grow the buffer, do it! */ 3946 rb = (char *) mhd_pool_reallocate (connection->pool, 3947 connection->read_buffer, 3948 connection->read_buffer_size, 3949 new_size); 3950 if (NULL == rb) 3951 { 3952 /* This should NOT be possible: we just computed 'new_size' so that 3953 it should fit. If it happens, somehow our read buffer is not in 3954 the right position in the pool, say because someone called 3955 mhd_pool_allocate() without 'from_end' set to 'true'? Anyway, 3956 should be investigated! (Ideally provide all data from 3957 *pool and connection->read_buffer and new_size for debugging). */ 3958 mhd_assert (0); 3959 return false; 3960 } 3961 mhd_assert (connection->read_buffer == rb); 3962 connection->read_buffer = rb; 3963 mhd_assert (NULL != connection->read_buffer); 3964 connection->read_buffer_size = new_size; 3965 return true; 3966 } 3967 3968 3969 MHD_INTERNAL MHD_FN_PAR_NONNULL_ALL_ bool 3970 mhd_stream_check_and_grow_read_buffer_space (struct MHD_Connection *restrict c) 3971 { 3972 /** 3973 * The increase of read buffer size is desirable. 3974 */ 3975 bool rbuff_grow_desired; 3976 /** 3977 * The increase of read buffer size is a hard requirement. 3978 */ 3979 bool rbuff_grow_required; 3980 3981 mhd_assert (0 != (MHD_EVENT_LOOP_INFO_RECV & c->event_loop_info)); 3982 mhd_assert (! c->discard_request); 3983 3984 rbuff_grow_required = (c->read_buffer_offset == c->read_buffer_size); 3985 if (rbuff_grow_required) 3986 rbuff_grow_desired = true; 3987 else 3988 { 3989 rbuff_grow_desired = (c->read_buffer_offset + 1536 > // TODO: remove handcoded buffer grow size 3990 c->read_buffer_size); 3991 3992 if ((rbuff_grow_desired) && 3993 (mhd_HTTP_STAGE_BODY_RECEIVING == c->stage)) 3994 { 3995 if (! c->rq.have_chunked_upload) 3996 { 3997 mhd_assert (MHD_SIZE_UNKNOWN != c->rq.cntn.cntn_size); 3998 /* Do not grow read buffer more than necessary to process the current 3999 request. */ 4000 rbuff_grow_desired = 4001 (c->rq.cntn.cntn_size - c->rq.cntn.recv_size > c->read_buffer_size); // FIXME 4002 } 4003 else 4004 { 4005 mhd_assert (MHD_SIZE_UNKNOWN == c->rq.cntn.cntn_size); 4006 if (0 == c->rq.current_chunk_size) 4007 rbuff_grow_desired = /* Reading value of the next chunk size */ 4008 (MHD_CHUNK_HEADER_REASONABLE_LEN > 4009 c->read_buffer_size); 4010 else 4011 { 4012 const uint_fast64_t cur_chunk_left = 4013 c->rq.current_chunk_size - c->rq.current_chunk_offset; 4014 /* Do not grow read buffer more than necessary to process the current 4015 chunk with terminating CRLF. */ 4016 mhd_assert (c->rq.current_chunk_offset <= c->rq.current_chunk_size); 4017 rbuff_grow_desired = 4018 ((cur_chunk_left + 2) > (uint_fast64_t) (c->read_buffer_size)); 4019 } 4020 } 4021 } 4022 } 4023 4024 if (! rbuff_grow_desired) 4025 return true; /* No need to increase the buffer */ 4026 4027 if (try_grow_read_buffer (c, rbuff_grow_required)) 4028 return true; /* Buffer increase succeed */ 4029 4030 if (! rbuff_grow_required) 4031 return true; /* Can continue without buffer increase */ 4032 4033 /* Failed to increase the read buffer size, but need to read the data 4034 from the network. 4035 No more space left in the buffer, no more space to increase the buffer. */ 4036 4037 if (1) 4038 { 4039 enum MHD_ProcRecvDataStage stage; 4040 4041 switch (c->stage) 4042 { 4043 case mhd_HTTP_STAGE_INIT: 4044 stage = MHD_PROC_RECV_INIT; 4045 break; 4046 case mhd_HTTP_STAGE_REQ_LINE_RECEIVING: 4047 if (mhd_HTTP_METHOD_NO_METHOD == c->rq.http_mthd) 4048 stage = MHD_PROC_RECV_METHOD; 4049 else if (0 == c->rq.req_target_len) 4050 stage = MHD_PROC_RECV_URI; 4051 else 4052 stage = MHD_PROC_RECV_HTTPVER; 4053 break; 4054 case mhd_HTTP_STAGE_REQ_HEADERS_RECEIVING: 4055 stage = MHD_PROC_RECV_HEADERS; 4056 break; 4057 case mhd_HTTP_STAGE_BODY_RECEIVING: 4058 stage = c->rq.have_chunked_upload ? 4059 MHD_PROC_RECV_BODY_CHUNKED : MHD_PROC_RECV_BODY_NORMAL; 4060 break; 4061 case mhd_HTTP_STAGE_FOOTERS_RECEIVING: 4062 stage = MHD_PROC_RECV_FOOTERS; 4063 break; 4064 case mhd_HTTP_STAGE_REQ_LINE_RECEIVED: 4065 case mhd_HTTP_STAGE_HEADERS_RECEIVED: 4066 case mhd_HTTP_STAGE_HEADERS_PROCESSED: 4067 case mhd_HTTP_STAGE_CONTINUE_SENDING: 4068 case mhd_HTTP_STAGE_BODY_RECEIVED: 4069 case mhd_HTTP_STAGE_FOOTERS_RECEIVED: 4070 case mhd_HTTP_STAGE_FULL_REQ_RECEIVED: 4071 case mhd_HTTP_STAGE_REQ_RECV_FINISHED: 4072 case mhd_HTTP_STAGE_START_REPLY: 4073 case mhd_HTTP_STAGE_HEADERS_SENDING: 4074 case mhd_HTTP_STAGE_HEADERS_SENT: 4075 case mhd_HTTP_STAGE_UNCHUNKED_BODY_UNREADY: 4076 case mhd_HTTP_STAGE_UNCHUNKED_BODY_READY: 4077 case mhd_HTTP_STAGE_CHUNKED_BODY_UNREADY: 4078 case mhd_HTTP_STAGE_CHUNKED_BODY_READY: 4079 case mhd_HTTP_STAGE_CHUNKED_BODY_SENT: 4080 case mhd_HTTP_STAGE_FOOTERS_SENDING: 4081 case mhd_HTTP_STAGE_FULL_REPLY_SENT: 4082 case mhd_HTTP_STAGE_PRE_CLOSING: 4083 case mhd_HTTP_STAGE_CLOSED: 4084 #ifdef MHD_SUPPORT_UPGRADE 4085 case mhd_HTTP_STAGE_UPGRADE_HEADERS_SENDING: 4086 case mhd_HTTP_STAGE_UPGRADING: 4087 case mhd_HTTP_STAGE_UPGRADED: 4088 case mhd_HTTP_STAGE_UPGRADED_CLEANING: 4089 #endif /* MHD_SUPPORT_UPGRADE */ 4090 default: 4091 mhd_UNREACHABLE (); 4092 stage = MHD_PROC_RECV_BODY_NORMAL; 4093 break; 4094 } 4095 4096 handle_recv_no_space (c, stage); 4097 } 4098 return false; 4099 }