libmicrohttpd2

HTTP server C library (MHD 2.x, alpha)
Log | Files | Refs | README | LICENSE

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>&quot;Host:&quot; header required</title></head>" \
    367         "<body>HTTP/1.1 request without <b>&quot;Host:&quot;</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 &quot;Host:&quot; headers used</title></head>" \
    377         "<body>" \
    378         "Request with more than one <b>&quot;Host:&quot;</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 &quot;Host:&quot; header</title></head>" \
    388         "<body>" \
    389         "Malformed <b>&quot;Host:&quot;</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 }