libmicrohttpd2

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

stream_process_request.c (147733B)


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