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