libmicrohttpd2

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

stream_process_request.c (144919B)


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