libmicrohttpd2

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

stream_process_request.c (147943B)


      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)
    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 <= c->rq.hdrs.rq_line.rq_tgt_qmark));
   1389   mhd_assert ((NULL == c->rq.hdrs.rq_line.rq_tgt_qmark) || \
   1390               (c->rq.req_target_len > \
   1391                (size_t) (c->rq.hdrs.rq_line.rq_tgt_qmark \
   1392                          - c->rq.hdrs.rq_line.rq_tgt)));
   1393 
   1394   /* Log callback before the request-target is modified/decoded */
   1395   if (NULL != c->daemon->req_cfg.uri_cb.cb)
   1396   {
   1397     struct MHD_EarlyUriCbData req_data;
   1398     req_data.request = &(c->rq);
   1399     req_data.full_uri.cstr = c->rq.hdrs.rq_line.rq_tgt;
   1400     req_data.full_uri.len = c->rq.req_target_len;
   1401     c->rq.app_aware = true;
   1402     c->daemon->req_cfg.uri_cb.cb (c->daemon->req_cfg.uri_cb.cls,
   1403                                   &req_data,
   1404                                   &(c->rq.app_context));
   1405   }
   1406 
   1407   if (NULL != c->rq.hdrs.rq_line.rq_tgt_qmark)
   1408   {
   1409     params_len =
   1410       c->rq.req_target_len
   1411       - (size_t) (c->rq.hdrs.rq_line.rq_tgt_qmark - c->rq.hdrs.rq_line.rq_tgt);
   1412 
   1413     mhd_assert (1 <= params_len);
   1414 
   1415     c->rq.hdrs.rq_line.rq_tgt_qmark[0] = 0; /* Replace '?' with zero termination */
   1416 
   1417     // TODO: support detection of decoding errors
   1418     if (! mhd_parse_uri_args (params_len - 1,
   1419                               c->rq.hdrs.rq_line.rq_tgt_qmark + 1,
   1420                               &request_add_get_arg,
   1421                               &(c->h1_stream)))
   1422     {
   1423       mhd_LOG_MSG (c->daemon, MHD_SC_CONNECTION_POOL_NO_MEM_GET_PARAM,
   1424                    "Not enough memory in the pool to store GET parameter");
   1425 
   1426       mhd_RESPOND_WITH_ERROR_STATIC (
   1427         c,
   1428         mhd_stream_get_no_space_err_status_code (c,
   1429                                                  MHD_PROC_RECV_URI,
   1430                                                  0,
   1431                                                  NULL),
   1432         ERR_RSP_MSG_REQUEST_TOO_BIG);
   1433       mhd_assert (mhd_HTTP_STAGE_REQ_LINE_RECEIVING != c->stage);
   1434       return false;
   1435 
   1436     }
   1437   }
   1438   else
   1439     params_len = 0;
   1440 
   1441   mhd_assert (strlen (c->rq.hdrs.rq_line.rq_tgt) == \
   1442               c->rq.req_target_len - params_len);
   1443 
   1444   /* Finally unescape URI itself */
   1445   // TODO: support detection of decoding errors
   1446   c->rq.url_len =
   1447     mhd_str_pct_decode_lenient_n (c->rq.hdrs.rq_line.rq_tgt,
   1448                                   c->rq.req_target_len - params_len,
   1449                                   c->rq.hdrs.rq_line.rq_tgt,
   1450                                   c->rq.req_target_len - params_len,
   1451                                   NULL);
   1452   c->rq.url = c->rq.hdrs.rq_line.rq_tgt;
   1453 
   1454   return true;
   1455 }
   1456 
   1457 
   1458 #ifndef MHD_MAX_FIXED_URI_LEN
   1459 /**
   1460  * The maximum size of the fixed URI for automatic redirection
   1461  */
   1462 #define MHD_MAX_FIXED_URI_LEN (64 * 1024)
   1463 #endif /* ! MHD_MAX_FIXED_URI_LEN */
   1464 
   1465 /**
   1466  * Send the automatic redirection to fixed URI when received URI with
   1467  * whitespaces.
   1468  * If URI is too large, close connection with error.
   1469  *
   1470  * @param c the connection to process
   1471  */
   1472 static void
   1473 send_redirect_fixed_rq_target (struct MHD_Connection *restrict c)
   1474 {
   1475   static const char hdr_prefix[] = MHD_HTTP_HEADER_LOCATION ": ";
   1476   static const size_t hdr_prefix_len =
   1477     mhd_SSTR_LEN (MHD_HTTP_HEADER_LOCATION ": ");
   1478   char *hdr_line;
   1479   char *b;
   1480   size_t fixed_uri_len;
   1481   size_t i;
   1482   size_t o;
   1483 
   1484   mhd_assert (mhd_HTTP_STAGE_REQ_LINE_RECEIVING == c->stage);
   1485   mhd_assert (0 != c->rq.hdrs.rq_line.num_ws_in_uri);
   1486   mhd_assert (c->rq.hdrs.rq_line.num_ws_in_uri <= \
   1487               c->rq.req_target_len);
   1488   fixed_uri_len = c->rq.req_target_len
   1489                   + 2 * c->rq.hdrs.rq_line.num_ws_in_uri;
   1490   if ( (fixed_uri_len + 200 > c->daemon->conns.cfg.mem_pool_size) ||
   1491        (fixed_uri_len > MHD_MAX_FIXED_URI_LEN) ||
   1492        (NULL ==
   1493         (hdr_line = (char *) malloc (fixed_uri_len + 1 + hdr_prefix_len))) )
   1494   {
   1495     mhd_STREAM_ABORT (c, mhd_CONN_CLOSE_CLIENT_HTTP_ERR_ABORT_CONN, \
   1496                       "The request has whitespace character is " \
   1497                       "in the URI and the URI is too large to " \
   1498                       "send automatic redirect to fixed URI.");
   1499     return;
   1500   }
   1501   memcpy (hdr_line, hdr_prefix, hdr_prefix_len);
   1502   b = hdr_line + hdr_prefix_len;
   1503   i = 0;
   1504   o = 0;
   1505 
   1506   do
   1507   {
   1508     const char chr = c->rq.hdrs.rq_line.rq_tgt[i++];
   1509 
   1510     mhd_assert ('\r' != chr); /* Replaced during request line parsing */
   1511     mhd_assert ('\n' != chr); /* Rejected during request line parsing */
   1512     mhd_assert (0 != chr); /* Rejected during request line parsing */
   1513     switch (chr)
   1514     {
   1515     case ' ':
   1516       b[o++] = '%';
   1517       b[o++] = '2';
   1518       b[o++] = '0';
   1519       break;
   1520     case '\t':
   1521       b[o++] = '%';
   1522       b[o++] = '0';
   1523       b[o++] = '9';
   1524       break;
   1525     case 0x0B:   /* VT (vertical tab) */
   1526       b[o++] = '%';
   1527       b[o++] = '0';
   1528       b[o++] = 'B';
   1529       break;
   1530     case 0x0C:   /* FF (form feed) */
   1531       b[o++] = '%';
   1532       b[o++] = '0';
   1533       b[o++] = 'C';
   1534       break;
   1535     default:
   1536       b[o++] = chr;
   1537       break;
   1538     }
   1539   } while (i < c->rq.req_target_len);
   1540   mhd_assert (fixed_uri_len == o);
   1541   b[o] = 0; /* Zero-terminate the result */
   1542 
   1543   mhd_RESPOND_WITH_ERROR_HEADER (c,
   1544                                  MHD_HTTP_STATUS_MOVED_PERMANENTLY,
   1545                                  ERR_RSP_RQ_TARGET_INVALID_CHAR,
   1546                                  o + hdr_prefix_len,
   1547                                  hdr_line);
   1548 
   1549   return;
   1550 }
   1551 
   1552 
   1553 MHD_INTERNAL MHD_FN_PAR_NONNULL_ALL_ bool
   1554 mhd_stream_get_request_line (struct MHD_Connection *restrict c)
   1555 {
   1556   const int discp_lvl = c->daemon->req_cfg.strictness;
   1557   /* Parse whitespace in URI, special parsing of the request line */
   1558   const bool wsp_in_uri = (0 >= discp_lvl);
   1559   /* Keep whitespace in URI, give app URI with whitespace instead of
   1560      automatic redirect to fixed URI */
   1561   const bool wsp_in_uri_keep = (-2 >= discp_lvl);
   1562 
   1563   if (! get_request_line_inner (c))
   1564   {
   1565     /* End of the request line has not been found yet */
   1566     mhd_assert ((! wsp_in_uri) || NULL == c->rq.version);
   1567     if ((NULL != c->rq.version) &&
   1568         (HTTP_VER_LEN <
   1569          (c->rq.hdrs.rq_line.proc_pos
   1570           - (size_t) (c->rq.version - c->read_buffer))))
   1571     {
   1572       c->rq.http_ver = MHD_HTTP_VERSION_INVALID;
   1573       mhd_RESPOND_WITH_ERROR_STATIC (c,
   1574                                      MHD_HTTP_STATUS_BAD_REQUEST,
   1575                                      ERR_RSP_REQUEST_MALFORMED);
   1576       return true; /* Error in the request */
   1577     }
   1578     return false;
   1579   }
   1580   if (mhd_HTTP_STAGE_REQ_LINE_RECEIVING < c->stage)
   1581     return true; /* Error in the request */
   1582 
   1583   mhd_assert (mhd_HTTP_STAGE_REQ_LINE_RECEIVING == c->stage);
   1584   mhd_assert (NULL == c->rq.url);
   1585   mhd_assert (0 == c->rq.url_len);
   1586   mhd_assert (NULL != c->rq.hdrs.rq_line.rq_tgt);
   1587   if (0 != c->rq.hdrs.rq_line.num_ws_in_uri)
   1588   {
   1589     if (! wsp_in_uri)
   1590     {
   1591       mhd_RESPOND_WITH_ERROR_STATIC (c,
   1592                                      MHD_HTTP_STATUS_BAD_REQUEST,
   1593                                      ERR_RSP_RQ_TARGET_INVALID_CHAR);
   1594       return true; /* Error in the request */
   1595     }
   1596     if (! wsp_in_uri_keep)
   1597     {
   1598       send_redirect_fixed_rq_target (c);
   1599       return true; /* Error in the request */
   1600     }
   1601   }
   1602   if (! process_request_target (c))
   1603     return true; /* Error in processing */
   1604 
   1605   c->stage = mhd_HTTP_STAGE_REQ_LINE_RECEIVED;
   1606   return true;
   1607 }
   1608 
   1609 
   1610 MHD_INTERNAL MHD_FN_PAR_NONNULL_ALL_ void
   1611 mhd_stream_switch_to_rq_headers_proc (struct MHD_Connection *restrict c)
   1612 {
   1613   c->rq.field_lines.start = c->read_buffer;
   1614   mhd_stream_reset_rq_hdr_proc_state (c);
   1615   c->stage = mhd_HTTP_STAGE_REQ_HEADERS_RECEIVING;
   1616 }
   1617 
   1618 
   1619 /**
   1620  * Send error reply when receive buffer space exhausted while receiving or
   1621  * storing the request headers
   1622  * @param c the connection to handle
   1623  * @param add_header the optional pointer to the current header string being
   1624  *                   processed or the header failed to be added.
   1625  *                   Could be not zero-terminated and can contain binary zeros.
   1626  *                   Can be NULL.
   1627  * @param add_header_size the size of the @a add_header
   1628  */
   1629 mhd_static_inline
   1630 MHD_FN_PAR_NONNULL_ (1) void
   1631 handle_req_headers_no_space (struct MHD_Connection *restrict c,
   1632                              const char *restrict add_header,
   1633                              size_t add_header_size)
   1634 {
   1635   unsigned int err_code;
   1636 
   1637   err_code = mhd_stream_get_no_space_err_status_code (c,
   1638                                                       MHD_PROC_RECV_HEADERS,
   1639                                                       add_header_size,
   1640                                                       add_header);
   1641   mhd_RESPOND_WITH_ERROR_STATIC (c,
   1642                                  err_code,
   1643                                  ERR_RSP_REQUEST_HEADER_TOO_BIG);
   1644 }
   1645 
   1646 
   1647 /**
   1648  * Send error reply when receive buffer space exhausted while receiving or
   1649  * storing the request footers (for chunked requests).
   1650  * @param c the connection to handle
   1651  * @param add_footer the optional pointer to the current footer string being
   1652  *                   processed or the footer failed to be added.
   1653  *                   Could be not zero-terminated and can contain binary zeros.
   1654  *                   Can be NULL.
   1655  * @param add_footer_size the size of the @a add_footer
   1656  */
   1657 mhd_static_inline
   1658 MHD_FN_PAR_NONNULL_ (1) void
   1659 handle_req_footers_no_space (struct MHD_Connection *restrict c,
   1660                              const char *restrict add_footer,
   1661                              size_t add_footer_size)
   1662 {
   1663   (void) add_footer; (void) add_footer_size; /* Unused */
   1664   mhd_assert (c->rq.have_chunked_upload);
   1665 
   1666   /* Footers should be optional */
   1667   mhd_RESPOND_WITH_ERROR_STATIC (
   1668     c,
   1669     MHD_HTTP_STATUS_REQUEST_HEADER_FIELDS_TOO_LARGE,
   1670     ERR_RSP_REQUEST_FOOTER_TOO_BIG);
   1671 }
   1672 
   1673 
   1674 /**
   1675  * Results of header line reading
   1676  */
   1677 enum MHD_FIXED_ENUM_ mhd_HdrLineReadRes
   1678 {
   1679   /**
   1680    * Not enough data yet
   1681    */
   1682   MHD_HDR_LINE_READING_NEED_MORE_DATA = 0,
   1683   /**
   1684    * New header line has been read
   1685    */
   1686   MHD_HDR_LINE_READING_GOT_HEADER,
   1687   /**
   1688    * Error in header data, error response has been queued
   1689    */
   1690   MHD_HDR_LINE_READING_DATA_ERROR,
   1691   /**
   1692    * Found the end of the request header (end of field lines)
   1693    */
   1694   MHD_HDR_LINE_READING_GOT_END_OF_HEADER
   1695 };
   1696 
   1697 
   1698 /**
   1699  * Find the end of the request header line and make basic header parsing.
   1700  * Handle errors and header folding.
   1701  * @param c the connection to process
   1702  * @param process_footers if true then footers are processed,
   1703  *                        if false then headers are processed
   1704  * @param[out] hdr_name the name of the parsed header (field)
   1705  * @param[out] hdr_value the value of the parsed header (field)
   1706  * @return mhd_HdrLineReadRes value
   1707  */
   1708 static enum mhd_HdrLineReadRes
   1709 get_req_header (struct MHD_Connection *restrict c,
   1710                 bool process_footers,
   1711                 struct MHD_String *restrict hdr_name,
   1712                 struct MHD_String *restrict hdr_value)
   1713 {
   1714   const int discp_lvl = c->daemon->req_cfg.strictness;
   1715   /* Treat bare LF as the end of the line.
   1716      RFC 9112, section 2.2-3
   1717      Note: MHD never replaces bare LF with space (RFC 9110, section 5.5-5).
   1718      Bare LF is processed as end of the line or rejected as broken request. */
   1719   const bool bare_lf_as_crlf = mhd_ALLOW_BARE_LF_AS_CRLF (discp_lvl);
   1720   /* Keep bare CR character as is.
   1721      Violates RFC 9112, section 2.2-4 */
   1722   const bool bare_cr_keep = (-3 >= discp_lvl);
   1723   /* Treat bare CR as space; replace it with space before processing.
   1724      RFC 9112, section 2.2-4 */
   1725   const bool bare_cr_as_sp = ((! bare_cr_keep) && (-1 >= discp_lvl));
   1726   /* Treat NUL as space; replace it with space before processing.
   1727      RFC 9110, section 5.5-5 */
   1728   const bool nul_as_sp = (-1 >= discp_lvl);
   1729   /* Allow folded header lines.
   1730      RFC 9112, section 5.2-4 */
   1731   const bool allow_folded = (0 >= discp_lvl);
   1732   /* Do not reject headers with the whitespace at the start of the first line.
   1733      When allowed, the first line with whitespace character at the first
   1734      position is ignored (as well as all possible line foldings of the first
   1735      line).
   1736      RFC 9112, section 2.2-8 */
   1737   const bool allow_wsp_at_start = allow_folded && (-1 >= discp_lvl);
   1738   /* Allow whitespace in header (field) name.
   1739      Violates RFC 9110, section 5.1-2 */
   1740   const bool allow_wsp_in_name = (-2 >= discp_lvl);
   1741   /* Allow zero-length header (field) name.
   1742      Violates RFC 9110, section 5.1-2 */
   1743   const bool allow_empty_name = (-2 >= discp_lvl);
   1744   /* Allow whitespace before colon.
   1745      Violates RFC 9112, section 5.1-2 */
   1746   const bool allow_wsp_before_colon = (-3 >= discp_lvl);
   1747   /* Do not abort the request when header line has no colon, just skip such
   1748      bad lines.
   1749      RFC 9112, section 5-1 */
   1750   const bool allow_line_without_colon = (-2 >= discp_lvl);
   1751 
   1752   size_t p; /**< The position of the currently processed character */
   1753 
   1754   (void) process_footers; /* Unused parameter in non-debug and no messages */
   1755 
   1756   mhd_assert ((process_footers ? mhd_HTTP_STAGE_FOOTERS_RECEIVING : \
   1757                mhd_HTTP_STAGE_REQ_HEADERS_RECEIVING) == \
   1758               c->stage);
   1759 
   1760   p = c->rq.hdrs.hdr.proc_pos;
   1761 
   1762   mhd_assert (p <= c->read_buffer_offset);
   1763   while (p < c->read_buffer_offset)
   1764   {
   1765     char *const restrict read_buffer = c->read_buffer;
   1766     const char chr = read_buffer[p];
   1767     bool end_of_line;
   1768 
   1769     mhd_assert ((0 == c->rq.hdrs.hdr.name_len) || \
   1770                 (c->rq.hdrs.hdr.name_len < p));
   1771     mhd_assert ((0 == c->rq.hdrs.hdr.name_len) || (0 != p));
   1772     mhd_assert ((0 == c->rq.hdrs.hdr.name_len) || \
   1773                 (c->rq.hdrs.hdr.name_end_found));
   1774     mhd_assert ((0 == c->rq.hdrs.hdr.value_start) || \
   1775                 (c->rq.hdrs.hdr.name_len < c->rq.hdrs.hdr.value_start));
   1776     mhd_assert ((0 == c->rq.hdrs.hdr.value_start) || \
   1777                 (0 != c->rq.hdrs.hdr.name_len));
   1778     mhd_assert ((0 == c->rq.hdrs.hdr.ws_start) || \
   1779                 (0 == c->rq.hdrs.hdr.name_len) || \
   1780                 (c->rq.hdrs.hdr.ws_start > c->rq.hdrs.hdr.name_len));
   1781     mhd_assert ((0 == c->rq.hdrs.hdr.ws_start) || \
   1782                 (0 == c->rq.hdrs.hdr.value_start) || \
   1783                 (c->rq.hdrs.hdr.ws_start > c->rq.hdrs.hdr.value_start));
   1784 
   1785     /* Check for the end of the line */
   1786     if ('\r' == chr)
   1787     {
   1788       if (0 != p)
   1789       {
   1790         /* Line is not empty, need to check for possible line folding */
   1791         if (p + 2 >= c->read_buffer_offset)
   1792           break; /* Not enough data yet to check for folded line */
   1793       }
   1794       else
   1795       {
   1796         /* Line is empty, no need to check for possible line folding */
   1797         if (p + 2 > c->read_buffer_offset)
   1798           break; /* Not enough data yet to check for the end of the line */
   1799       }
   1800       if ('\n' == read_buffer[p + 1])
   1801         end_of_line = true;
   1802       else
   1803       {
   1804         /* Bare CR alone */
   1805         /* Must be rejected or replaced with space char.
   1806            See RFC 9112, section 2.2-4 */
   1807         if (bare_cr_as_sp)
   1808         {
   1809           read_buffer[p] = ' ';
   1810           c->rq.num_cr_sp_replaced++;
   1811           continue; /* Re-start processing of the current character */
   1812         }
   1813         else if (! bare_cr_keep)
   1814         {
   1815           if (! process_footers)
   1816             mhd_RESPOND_WITH_ERROR_STATIC (c,
   1817                                            MHD_HTTP_STATUS_BAD_REQUEST,
   1818                                            ERR_RSP_BARE_CR_IN_HEADER);
   1819           else
   1820             mhd_RESPOND_WITH_ERROR_STATIC (c,
   1821                                            MHD_HTTP_STATUS_BAD_REQUEST,
   1822                                            ERR_RSP_BARE_CR_IN_FOOTER);
   1823           return MHD_HDR_LINE_READING_DATA_ERROR; /* Error in the request */
   1824         }
   1825         end_of_line = false;
   1826       }
   1827     }
   1828     else if ('\n' == chr)
   1829     {
   1830       /* Bare LF may be recognised as a line delimiter.
   1831          See RFC 9112, section 2.2-3 */
   1832       if (bare_lf_as_crlf)
   1833       {
   1834         if (0 != p)
   1835         {
   1836           /* Line is not empty, need to check for possible line folding */
   1837           if (p + 1 >= c->read_buffer_offset)
   1838             break; /* Not enough data yet to check for folded line */
   1839         }
   1840         end_of_line = true;
   1841       }
   1842       else
   1843       {
   1844         if (! process_footers)
   1845           mhd_RESPOND_WITH_ERROR_STATIC (c,
   1846                                          MHD_HTTP_STATUS_BAD_REQUEST,
   1847                                          ERR_RSP_BARE_LF_IN_HEADER);
   1848         else
   1849           mhd_RESPOND_WITH_ERROR_STATIC (c,
   1850                                          MHD_HTTP_STATUS_BAD_REQUEST,
   1851                                          ERR_RSP_BARE_LF_IN_FOOTER);
   1852         return MHD_HDR_LINE_READING_DATA_ERROR; /* Error in the request */
   1853       }
   1854     }
   1855     else
   1856       end_of_line = false;
   1857 
   1858     if (end_of_line)
   1859     {
   1860       /* Handle the end of the line */
   1861       /**
   1862        *  The full length of the line, including CRLF (or bare LF).
   1863        */
   1864       const size_t line_len = p + (('\r' == chr) ? 2 : 1);
   1865       char next_line_char;
   1866       mhd_assert (line_len <= c->read_buffer_offset);
   1867 
   1868       if (0 == p)
   1869       {
   1870         /* Zero-length header line. This is the end of the request header
   1871            section.
   1872            RFC 9112, Section 2.1-1 */
   1873         mhd_assert (! c->rq.hdrs.hdr.starts_with_ws);
   1874         mhd_assert (! c->rq.hdrs.hdr.name_end_found);
   1875         mhd_assert (0 == c->rq.hdrs.hdr.name_len);
   1876         mhd_assert (0 == c->rq.hdrs.hdr.ws_start);
   1877         mhd_assert (0 == c->rq.hdrs.hdr.value_start);
   1878         /* Consume the line with CRLF (or bare LF) */
   1879         c->read_buffer += line_len;
   1880         c->read_buffer_offset -= line_len;
   1881         c->read_buffer_size -= line_len;
   1882         return MHD_HDR_LINE_READING_GOT_END_OF_HEADER;
   1883       }
   1884 
   1885       mhd_assert (line_len < c->read_buffer_offset);
   1886       mhd_assert (0 != line_len);
   1887       mhd_assert ('\n' == read_buffer[line_len - 1]);
   1888       next_line_char = read_buffer[line_len];
   1889       if ((' ' == next_line_char) ||
   1890           ('\t' == next_line_char))
   1891       {
   1892         /* Folded line */
   1893         if (! allow_folded)
   1894         {
   1895           if (! process_footers)
   1896             mhd_RESPOND_WITH_ERROR_STATIC (c,
   1897                                            MHD_HTTP_STATUS_BAD_REQUEST,
   1898                                            ERR_RSP_OBS_FOLD);
   1899           else
   1900             mhd_RESPOND_WITH_ERROR_STATIC (c,
   1901                                            MHD_HTTP_STATUS_BAD_REQUEST,
   1902                                            ERR_RSP_OBS_FOLD_FOOTER);
   1903 
   1904           return MHD_HDR_LINE_READING_DATA_ERROR; /* Error in the request */
   1905         }
   1906         /* Replace CRLF (or bare LF) character(s) with space characters.
   1907            See RFC 9112, Section 5.2-4 */
   1908         read_buffer[p] = ' ';
   1909         if ('\r' == chr)
   1910           read_buffer[p + 1] = ' ';
   1911         continue; /* Re-start processing of the current character */
   1912       }
   1913       else
   1914       {
   1915         /* It is not a folded line, it's the real end of the non-empty line */
   1916         bool skip_line = false;
   1917         mhd_assert (0 != p);
   1918         if (c->rq.hdrs.hdr.starts_with_ws)
   1919         {
   1920           /* This is the first line and it starts with whitespace. This line
   1921              must be discarded completely.
   1922              See RFC 9112, Section 2.2-8 */
   1923           mhd_assert (allow_wsp_at_start);
   1924 
   1925           mhd_LOG_MSG (c->daemon, MHD_SC_REQ_FIRST_HEADER_LINE_SPACE_PREFIXED,
   1926                        "Whitespace-prefixed first header line " \
   1927                        "has been skipped.");
   1928           skip_line = true;
   1929         }
   1930         else if (! c->rq.hdrs.hdr.name_end_found)
   1931         {
   1932           if (! allow_line_without_colon)
   1933           {
   1934             if (! process_footers)
   1935               mhd_RESPOND_WITH_ERROR_STATIC (c,
   1936                                              MHD_HTTP_STATUS_BAD_REQUEST,
   1937                                              ERR_RSP_HEADER_WITHOUT_COLON);
   1938             else
   1939               mhd_RESPOND_WITH_ERROR_STATIC (c,
   1940                                              MHD_HTTP_STATUS_BAD_REQUEST,
   1941                                              ERR_RSP_FOOTER_WITHOUT_COLON);
   1942 
   1943             return MHD_HDR_LINE_READING_DATA_ERROR; /* Error in the request */
   1944           }
   1945           /* Skip broken line completely */
   1946           c->rq.skipped_broken_lines++;
   1947           skip_line = true;
   1948         }
   1949         if (skip_line)
   1950         {
   1951           /* Skip the entire line */
   1952           c->read_buffer += line_len;
   1953           c->read_buffer_offset -= line_len;
   1954           c->read_buffer_size -= line_len;
   1955           p = 0;
   1956           /* Reset processing state */
   1957           memset (&c->rq.hdrs.hdr, 0, sizeof(c->rq.hdrs.hdr));
   1958           /* Start processing of the next line */
   1959           continue;
   1960         }
   1961         else
   1962         {
   1963           /* This line should be valid header line */
   1964           size_t value_len;
   1965           mhd_assert ((0 != c->rq.hdrs.hdr.name_len) || allow_empty_name);
   1966 
   1967           hdr_name->cstr = read_buffer + 0; /* The name always starts at the first character */
   1968           hdr_name->len = c->rq.hdrs.hdr.name_len;
   1969           mhd_assert (0 == hdr_name->cstr[hdr_name->len]);
   1970 
   1971           if (0 == c->rq.hdrs.hdr.value_start)
   1972           {
   1973             c->rq.hdrs.hdr.value_start = p;
   1974             read_buffer[p] = 0;
   1975             value_len = 0;
   1976           }
   1977           else if (0 != c->rq.hdrs.hdr.ws_start)
   1978           {
   1979             mhd_assert (p > c->rq.hdrs.hdr.ws_start);
   1980             mhd_assert (c->rq.hdrs.hdr.ws_start > c->rq.hdrs.hdr.value_start);
   1981             read_buffer[c->rq.hdrs.hdr.ws_start] = 0;
   1982             value_len = c->rq.hdrs.hdr.ws_start - c->rq.hdrs.hdr.value_start;
   1983           }
   1984           else
   1985           {
   1986             mhd_assert (p > c->rq.hdrs.hdr.ws_start);
   1987             read_buffer[p] = 0;
   1988             value_len = p - c->rq.hdrs.hdr.value_start;
   1989           }
   1990           hdr_value->cstr = read_buffer + c->rq.hdrs.hdr.value_start;
   1991           hdr_value->len = value_len;
   1992           mhd_assert (0 == hdr_value->cstr[hdr_value->len]);
   1993           /* Consume the entire line */
   1994           c->read_buffer += line_len;
   1995           c->read_buffer_offset -= line_len;
   1996           c->read_buffer_size -= line_len;
   1997           return MHD_HDR_LINE_READING_GOT_HEADER;
   1998         }
   1999       }
   2000     }
   2001     else if ((' ' == chr) || ('\t' == chr))
   2002     {
   2003       if (0 == p)
   2004       {
   2005         if (! allow_wsp_at_start)
   2006         {
   2007           if (! process_footers)
   2008             mhd_RESPOND_WITH_ERROR_STATIC (c,
   2009                                            MHD_HTTP_STATUS_BAD_REQUEST,
   2010                                            ERR_RSP_WSP_BEFORE_HEADER);
   2011           else
   2012             mhd_RESPOND_WITH_ERROR_STATIC (c,
   2013                                            MHD_HTTP_STATUS_BAD_REQUEST,
   2014                                            ERR_RSP_WSP_BEFORE_FOOTER);
   2015           return MHD_HDR_LINE_READING_DATA_ERROR; /* Error in the request */
   2016         }
   2017         c->rq.hdrs.hdr.starts_with_ws = true;
   2018       }
   2019       else if ((! c->rq.hdrs.hdr.name_end_found) &&
   2020                (! c->rq.hdrs.hdr.starts_with_ws))
   2021       {
   2022         /* Whitespace in header name / between header name and colon */
   2023         if (allow_wsp_in_name || allow_wsp_before_colon)
   2024         {
   2025           if (0 == c->rq.hdrs.hdr.ws_start)
   2026             c->rq.hdrs.hdr.ws_start = p;
   2027         }
   2028         else
   2029         {
   2030           if (! process_footers)
   2031             mhd_RESPOND_WITH_ERROR_STATIC (c,
   2032                                            MHD_HTTP_STATUS_BAD_REQUEST,
   2033                                            ERR_RSP_WSP_IN_HEADER_NAME);
   2034           else
   2035             mhd_RESPOND_WITH_ERROR_STATIC (c,
   2036                                            MHD_HTTP_STATUS_BAD_REQUEST,
   2037                                            ERR_RSP_WSP_IN_FOOTER_NAME);
   2038 
   2039           return MHD_HDR_LINE_READING_DATA_ERROR; /* Error in the request */
   2040         }
   2041       }
   2042       else
   2043       {
   2044         /* Whitespace before/inside/after header (field) value */
   2045         if (0 == c->rq.hdrs.hdr.ws_start)
   2046           c->rq.hdrs.hdr.ws_start = p;
   2047       }
   2048     }
   2049     else if (0 == chr)
   2050     {
   2051       if (! nul_as_sp)
   2052       {
   2053         if (! process_footers)
   2054           mhd_RESPOND_WITH_ERROR_STATIC (c,
   2055                                          MHD_HTTP_STATUS_BAD_REQUEST,
   2056                                          ERR_RSP_INVALID_CHR_IN_HEADER);
   2057         else
   2058           mhd_RESPOND_WITH_ERROR_STATIC (c,
   2059                                          MHD_HTTP_STATUS_BAD_REQUEST,
   2060                                          ERR_RSP_INVALID_CHR_IN_FOOTER);
   2061 
   2062         return MHD_HDR_LINE_READING_DATA_ERROR; /* Error in the request */
   2063       }
   2064       read_buffer[p] = ' ';
   2065       continue; /* Re-start processing of the current character */
   2066     }
   2067     else
   2068     {
   2069       /* Not a whitespace, not the end of the header line */
   2070       mhd_assert ('\r' != chr);
   2071       mhd_assert ('\n' != chr);
   2072       mhd_assert ('\0' != chr);
   2073       if ((! c->rq.hdrs.hdr.name_end_found) &&
   2074           (! c->rq.hdrs.hdr.starts_with_ws))
   2075       {
   2076         /* Processing the header (field) name */
   2077         if (':' == chr)
   2078         {
   2079           if (0 == c->rq.hdrs.hdr.ws_start)
   2080             c->rq.hdrs.hdr.name_len = p;
   2081           else
   2082           {
   2083             mhd_assert (allow_wsp_in_name || allow_wsp_before_colon);
   2084             if (! allow_wsp_before_colon)
   2085             {
   2086               if (! process_footers)
   2087                 mhd_RESPOND_WITH_ERROR_STATIC (c,
   2088                                                MHD_HTTP_STATUS_BAD_REQUEST,
   2089                                                ERR_RSP_WSP_IN_HEADER_NAME);
   2090               else
   2091                 mhd_RESPOND_WITH_ERROR_STATIC (c,
   2092                                                MHD_HTTP_STATUS_BAD_REQUEST,
   2093                                                ERR_RSP_WSP_IN_FOOTER_NAME);
   2094               return MHD_HDR_LINE_READING_DATA_ERROR; /* Error in the request */
   2095             }
   2096             c->rq.hdrs.hdr.name_len = c->rq.hdrs.hdr.ws_start;
   2097 #ifndef MHD_FAVOR_SMALL_CODE
   2098             c->rq.hdrs.hdr.ws_start = 0; /* Not on whitespace anymore */
   2099 #endif /* ! MHD_FAVOR_SMALL_CODE */
   2100           }
   2101           if ((0 == c->rq.hdrs.hdr.name_len) && ! allow_empty_name)
   2102           {
   2103             if (! process_footers)
   2104               mhd_RESPOND_WITH_ERROR_STATIC (c,
   2105                                              MHD_HTTP_STATUS_BAD_REQUEST,
   2106                                              ERR_RSP_EMPTY_HEADER_NAME);
   2107             else
   2108               mhd_RESPOND_WITH_ERROR_STATIC (c,
   2109                                              MHD_HTTP_STATUS_BAD_REQUEST,
   2110                                              ERR_RSP_EMPTY_FOOTER_NAME);
   2111             return MHD_HDR_LINE_READING_DATA_ERROR; /* Error in the request */
   2112           }
   2113           c->rq.hdrs.hdr.name_end_found = true;
   2114           read_buffer[c->rq.hdrs.hdr.name_len] = 0; /* Zero-terminate the name */
   2115         }
   2116         else
   2117         {
   2118           if (0 != c->rq.hdrs.hdr.ws_start)
   2119           {
   2120             /* End of the whitespace in header (field) name */
   2121             mhd_assert (allow_wsp_in_name || allow_wsp_before_colon);
   2122             if (! allow_wsp_in_name)
   2123             {
   2124               if (! process_footers)
   2125                 mhd_RESPOND_WITH_ERROR_STATIC (c,
   2126                                                MHD_HTTP_STATUS_BAD_REQUEST,
   2127                                                ERR_RSP_WSP_IN_HEADER_NAME);
   2128               else
   2129                 mhd_RESPOND_WITH_ERROR_STATIC (c,
   2130                                                MHD_HTTP_STATUS_BAD_REQUEST,
   2131                                                ERR_RSP_WSP_IN_FOOTER_NAME);
   2132 
   2133               return MHD_HDR_LINE_READING_DATA_ERROR; /* Error in the request */
   2134             }
   2135 #ifndef MHD_FAVOR_SMALL_CODE
   2136             c->rq.hdrs.hdr.ws_start = 0; /* Not on whitespace anymore */
   2137 #endif /* ! MHD_FAVOR_SMALL_CODE */
   2138           }
   2139         }
   2140       }
   2141       else
   2142       {
   2143         /* Processing the header (field) value */
   2144         if (0 == c->rq.hdrs.hdr.value_start)
   2145           c->rq.hdrs.hdr.value_start = p;
   2146 #ifndef MHD_FAVOR_SMALL_CODE
   2147         c->rq.hdrs.hdr.ws_start = 0; /* Not on whitespace anymore */
   2148 #endif /* ! MHD_FAVOR_SMALL_CODE */
   2149       }
   2150 #ifdef MHD_FAVOR_SMALL_CODE
   2151       c->rq.hdrs.hdr.ws_start = 0; /* Not on whitespace anymore */
   2152 #endif /* MHD_FAVOR_SMALL_CODE */
   2153     }
   2154     p++;
   2155   }
   2156   c->rq.hdrs.hdr.proc_pos = p;
   2157   return MHD_HDR_LINE_READING_NEED_MORE_DATA; /* Not enough data yet */
   2158 }
   2159 
   2160 
   2161 /**
   2162  * Reset request header processing state.
   2163  *
   2164  * This function resets the processing state before processing the next header
   2165  * (or footer) line.
   2166  * @param c the connection to process
   2167  */
   2168 MHD_INTERNAL MHD_FN_PAR_NONNULL_ALL_ void
   2169 mhd_stream_reset_rq_hdr_proc_state (struct MHD_Connection *c)
   2170 {
   2171   memset (&c->rq.hdrs.hdr, 0, sizeof(c->rq.hdrs.hdr));
   2172 }
   2173 
   2174 
   2175 MHD_INTERNAL MHD_FN_PAR_NONNULL_ALL_ bool
   2176 mhd_stream_get_request_headers (struct MHD_Connection *restrict c,
   2177                                 bool process_footers)
   2178 {
   2179   do
   2180   {
   2181     struct MHD_String hdr_name;
   2182     struct MHD_String hdr_value;
   2183     enum mhd_HdrLineReadRes res;
   2184 
   2185     mhd_assert ((process_footers ? mhd_HTTP_STAGE_FOOTERS_RECEIVING : \
   2186                  mhd_HTTP_STAGE_REQ_HEADERS_RECEIVING) == \
   2187                 c->stage);
   2188 
   2189 #ifndef NDEBUG
   2190     hdr_name.cstr = NULL;
   2191     hdr_value.cstr = NULL;
   2192 #endif /* ! NDEBUG */
   2193     res = get_req_header (c, process_footers, &hdr_name, &hdr_value);
   2194     if (MHD_HDR_LINE_READING_GOT_HEADER == res)
   2195     {
   2196       mhd_assert ((process_footers ? mhd_HTTP_STAGE_FOOTERS_RECEIVING : \
   2197                    mhd_HTTP_STAGE_REQ_HEADERS_RECEIVING) == \
   2198                   c->stage);
   2199       mhd_assert (NULL != hdr_name.cstr);
   2200       mhd_assert (NULL != hdr_value.cstr);
   2201       /* Values must be zero-terminated and must not have binary zeros */
   2202       mhd_assert (strlen (hdr_name.cstr) == hdr_name.len);
   2203       mhd_assert (strlen (hdr_value.cstr) == hdr_value.len);
   2204       /* Values must not have whitespaces at the start or at the end */
   2205       mhd_assert ((hdr_name.len == 0) || (hdr_name.cstr[0] != ' '));
   2206       mhd_assert ((hdr_name.len == 0) || (hdr_name.cstr[0] != '\t'));
   2207       mhd_assert ((hdr_name.len == 0) || \
   2208                   (hdr_name.cstr[hdr_name.len - 1] != ' '));
   2209       mhd_assert ((hdr_name.len == 0) || \
   2210                   (hdr_name.cstr[hdr_name.len - 1] != '\t'));
   2211       mhd_assert ((hdr_value.len == 0) || (hdr_value.cstr[0] != ' '));
   2212       mhd_assert ((hdr_value.len == 0) || (hdr_value.cstr[0] != '\t'));
   2213       mhd_assert ((hdr_value.len == 0) || \
   2214                   (hdr_value.cstr[hdr_value.len - 1] != ' '));
   2215       mhd_assert ((hdr_value.len == 0) || \
   2216                   (hdr_value.cstr[hdr_value.len - 1] != '\t'));
   2217 
   2218       if (! mhd_stream_add_field (&(c->h1_stream),
   2219                                   process_footers ?
   2220                                   MHD_VK_TRAILER : MHD_VK_HEADER,
   2221                                   &hdr_name,
   2222                                   &hdr_value))
   2223       {
   2224         size_t add_element_size;
   2225 
   2226         mhd_assert (hdr_name.cstr < hdr_value.cstr);
   2227 
   2228         if (! process_footers)
   2229           mhd_LOG_MSG (c->daemon, MHD_SC_CONNECTION_POOL_NO_MEM_REQ, \
   2230                        "Failed to allocate memory in the connection memory " \
   2231                        "pool to store header.");
   2232         else
   2233           mhd_LOG_MSG (c->daemon, MHD_SC_CONNECTION_POOL_NO_MEM_REQ, \
   2234                        "Failed to allocate memory in the connection memory " \
   2235                        "pool to store footer.");
   2236 
   2237         add_element_size = hdr_value.len
   2238                            + (size_t) (hdr_value.cstr - hdr_name.cstr);
   2239 
   2240         if (! process_footers)
   2241           handle_req_headers_no_space (c, hdr_name.cstr, add_element_size);
   2242         else
   2243           handle_req_footers_no_space (c, hdr_name.cstr, add_element_size);
   2244 
   2245         mhd_assert (mhd_HTTP_STAGE_FULL_REQ_RECEIVED < c->stage);
   2246         return true;
   2247       }
   2248       /* Reset processing state */
   2249       mhd_stream_reset_rq_hdr_proc_state (c);
   2250       mhd_assert ((process_footers ? mhd_HTTP_STAGE_FOOTERS_RECEIVING : \
   2251                    mhd_HTTP_STAGE_REQ_HEADERS_RECEIVING) == \
   2252                   c->stage);
   2253       /* Read the next header (field) line */
   2254       continue;
   2255     }
   2256     else if (MHD_HDR_LINE_READING_NEED_MORE_DATA == res)
   2257     {
   2258       mhd_assert ((process_footers ? mhd_HTTP_STAGE_FOOTERS_RECEIVING : \
   2259                    mhd_HTTP_STAGE_REQ_HEADERS_RECEIVING) == \
   2260                   c->stage);
   2261       return false;
   2262     }
   2263     else if (MHD_HDR_LINE_READING_DATA_ERROR == res)
   2264     {
   2265       mhd_assert ((process_footers ? \
   2266                    mhd_HTTP_STAGE_FOOTERS_RECEIVING : \
   2267                    mhd_HTTP_STAGE_REQ_HEADERS_RECEIVING) < c->stage);
   2268       mhd_assert (c->stop_with_error);
   2269       mhd_assert (c->discard_request);
   2270       return true;
   2271     }
   2272     mhd_assert (MHD_HDR_LINE_READING_GOT_END_OF_HEADER == res);
   2273     break;
   2274   } while (1);
   2275 
   2276   if (1 == c->rq.num_cr_sp_replaced)
   2277   {
   2278     if (! process_footers)
   2279       mhd_LOG_MSG (c->daemon, MHD_SC_REQ_HEADER_CR_REPLACED, \
   2280                    "One bare CR character has been replaced with space " \
   2281                    "in the request line or in the request headers.");
   2282     else
   2283       mhd_LOG_MSG (c->daemon, MHD_SC_REQ_FOOTER_CR_REPLACED, \
   2284                    "One bare CR character has been replaced with space " \
   2285                    "in the request footers.");
   2286   }
   2287   else if (0 != c->rq.num_cr_sp_replaced)
   2288   {
   2289     if (! process_footers)
   2290       mhd_LOG_PRINT (c->daemon, MHD_SC_REQ_HEADER_CR_REPLACED, \
   2291                      mhd_LOG_FMT ("%" PRIuFAST64 " bare CR characters have " \
   2292                                   "been replaced with spaces in the request " \
   2293                                   "line and/or in the request headers."), \
   2294                      (uint_fast64_t) c->rq.num_cr_sp_replaced);
   2295     else
   2296       mhd_LOG_PRINT (c->daemon, MHD_SC_REQ_HEADER_CR_REPLACED, \
   2297                      mhd_LOG_FMT ("%" PRIuFAST64 " bare CR characters have " \
   2298                                   "been replaced with spaces in the request " \
   2299                                   "footers."), \
   2300                      (uint_fast64_t) c->rq.num_cr_sp_replaced);
   2301 
   2302 
   2303   }
   2304   if (1 == c->rq.skipped_broken_lines)
   2305   {
   2306     if (! process_footers)
   2307       mhd_LOG_MSG (c->daemon, MHD_SC_REQ_HEADER_LINE_NO_COLON, \
   2308                    "One header line without colon has been skipped.");
   2309     else
   2310       mhd_LOG_MSG (c->daemon, MHD_SC_REQ_FOOTER_LINE_NO_COLON, \
   2311                    "One footer line without colon has been skipped.");
   2312   }
   2313   else if (0 != c->rq.skipped_broken_lines)
   2314   {
   2315     if (! process_footers)
   2316       mhd_LOG_PRINT (c->daemon, MHD_SC_REQ_HEADER_CR_REPLACED, \
   2317                      mhd_LOG_FMT ("%" PRIu64 " header lines without colons "
   2318                                   "have been skipped."),
   2319                      (uint_fast64_t) c->rq.skipped_broken_lines);
   2320     else
   2321       mhd_LOG_PRINT (c->daemon, MHD_SC_REQ_HEADER_CR_REPLACED, \
   2322                      mhd_LOG_FMT ("%" PRIu64 " footer lines without colons "
   2323                                   "have been skipped."),
   2324                      (uint_fast64_t) c->rq.skipped_broken_lines);
   2325   }
   2326 
   2327   mhd_assert (c->rq.method.cstr < c->read_buffer);
   2328   if (! process_footers)
   2329   {
   2330     c->rq.header_size = (size_t) (c->read_buffer - c->rq.method.cstr);
   2331     mhd_assert (NULL != c->rq.field_lines.start);
   2332     c->rq.field_lines.size =
   2333       (size_t) ((c->read_buffer - c->rq.field_lines.start) - 1);
   2334     if ('\r' == *(c->read_buffer - 2))
   2335       c->rq.field_lines.size--;
   2336     c->stage = mhd_HTTP_STAGE_HEADERS_RECEIVED;
   2337 
   2338     if (mhd_BUF_INC_SIZE > c->read_buffer_size)
   2339     {
   2340       /* Try to re-use some of the last bytes of the request header */
   2341       /* Do this only if space in the read buffer is limited AND
   2342          amount of read ahead data is small. */
   2343       /**
   2344        *  The position of the terminating NUL after the last character of
   2345        *  the last header element.
   2346        */
   2347       const char *last_elmnt_end;
   2348       size_t shift_back_size;
   2349       struct mhd_RequestField *header;
   2350       header = mhd_DLINKEDL_GET_LAST (&(c->rq), fields);
   2351       if (NULL != header)
   2352         last_elmnt_end =
   2353           header->field.nv.value.cstr + header->field.nv.value.len;
   2354       else
   2355         last_elmnt_end = c->rq.version + HTTP_VER_LEN;
   2356       mhd_assert ((last_elmnt_end + 1) < c->read_buffer);
   2357       shift_back_size = (size_t) (c->read_buffer - (last_elmnt_end + 1));
   2358       if (0 != c->read_buffer_offset)
   2359         memmove (c->read_buffer - shift_back_size,
   2360                  c->read_buffer,
   2361                  c->read_buffer_offset);
   2362       c->read_buffer -= shift_back_size;
   2363       c->read_buffer_size += shift_back_size;
   2364     }
   2365   }
   2366   else
   2367     c->stage = mhd_HTTP_STAGE_FOOTERS_RECEIVED;
   2368 
   2369   return true;
   2370 }
   2371 
   2372 
   2373 #ifdef MHD_SUPPORT_COOKIES
   2374 
   2375 /**
   2376  * Cookie parsing result
   2377  */
   2378 enum mhd_ParseCookie
   2379 {
   2380   MHD_PARSE_COOKIE_OK_LAX = 2        /**< Cookies parsed, but workarounds used */
   2381   ,
   2382   MHD_PARSE_COOKIE_OK = 1            /**< Success or no cookies in headers */
   2383   ,
   2384   MHD_PARSE_COOKIE_NO_MEMORY = 0     /**< Not enough memory in the pool */
   2385   ,
   2386   MHD_PARSE_COOKIE_MALFORMED = -1    /**< Invalid cookie header */
   2387 };
   2388 
   2389 
   2390 /**
   2391  * Parse the cookies string (see RFC 6265).
   2392  *
   2393  * Try to parse the cookies string even if it is not strictly formed
   2394  * as specified by RFC 6265.
   2395  *
   2396  * @param str_len the size of the @a str, not including mandatory
   2397  *                zero-termination
   2398  * @param str the string to parse, without leading whitespaces
   2399  * @param strictness the protocol strictness
   2400  * @param s the stream to process
   2401  * @return #MHD_PARSE_COOKIE_OK for success, error code otherwise
   2402  */
   2403 static MHD_FN_PAR_NONNULL_ALL_
   2404 MHD_FN_PAR_CSTR_ (2)
   2405 MHD_FN_PAR_INOUT_SIZE_ (2,1) enum mhd_ParseCookie
   2406 parse_cookies_string (const size_t str_len,
   2407                       char *restrict str,
   2408                       enum MHD_ProtocolStrictLevel strictness,
   2409                       struct MHD_Stream *restrict s)
   2410 {
   2411   size_t i;
   2412   bool non_strict;
   2413   /* Skip extra whitespaces and empty cookies */
   2414   const bool allow_wsp_empty = (MHD_PSL_DEFAULT >= strictness);
   2415   /* Allow whitespaces around '=' character */
   2416   const bool wsp_around_eq = (MHD_PSL_EXTRA_PERMISSIVE >= strictness);
   2417   /* Allow whitespaces in quoted cookie value */
   2418   const bool wsp_in_quoted = (MHD_PSL_VERY_PERMISSIVE >= strictness);
   2419   /* Allow tab as space after semicolon between cookies */
   2420   const bool tab_as_sp = (MHD_PSL_DEFAULT >= strictness);
   2421   /* Allow no space after semicolon between cookies */
   2422   const bool allow_no_space = (MHD_PSL_DEFAULT >= strictness);
   2423 
   2424   non_strict = false;
   2425   i = 0;
   2426   while (i < str_len)
   2427   {
   2428     size_t name_start;
   2429     size_t name_len;
   2430     size_t value_start;
   2431     size_t value_len;
   2432     bool val_quoted;
   2433     /* Skip any whitespaces and empty cookies */
   2434     while (' ' == str[i] || '\t' == str[i] || ';' == str[i])
   2435     {
   2436       if (! allow_wsp_empty)
   2437         return MHD_PARSE_COOKIE_MALFORMED;
   2438       non_strict = true;
   2439       i++;
   2440       if (i == str_len)
   2441         return non_strict? MHD_PARSE_COOKIE_OK_LAX : MHD_PARSE_COOKIE_OK;
   2442     }
   2443     /* 'i' must point to the first char of cookie-name */
   2444     name_start = i;
   2445     /* Find the end of the cookie-name */
   2446     do
   2447     {
   2448       const char l = str[i];
   2449       if (('=' == l) || (' ' == l) || ('\t' == l) || ('"' == l) || (',' == l) ||
   2450           (';' == l) || (0 == l))
   2451         break;
   2452     } while (str_len > ++i);
   2453     name_len = i - name_start;
   2454     /* Skip any whitespaces */
   2455     while (str_len > i && (' ' == str[i] || '\t' == str[i]))
   2456     {
   2457       if (! wsp_around_eq)
   2458         return MHD_PARSE_COOKIE_MALFORMED;
   2459       non_strict = true;
   2460       i++;
   2461     }
   2462     if ((str_len == i) || ('=' != str[i]) || (0 == name_len))
   2463       return MHD_PARSE_COOKIE_MALFORMED; /* Incomplete cookie name */
   2464     /* 'i' must point to the '=' char */
   2465     mhd_assert ('=' == str[i]);
   2466     i++;
   2467     /* Skip any whitespaces */
   2468     while (str_len > i && (' ' == str[i] || '\t' == str[i]))
   2469     {
   2470       if (! wsp_around_eq)
   2471         return MHD_PARSE_COOKIE_MALFORMED;
   2472       non_strict = true;
   2473       i++;
   2474     }
   2475     /* 'i' must point to the first char of cookie-value */
   2476     if (str_len == i)
   2477     {
   2478       value_start = 0;
   2479       value_len = 0;
   2480 #ifndef NDEBUG
   2481       val_quoted = false; /* This assignment used in assert */
   2482 #endif
   2483     }
   2484     else
   2485     {
   2486       bool valid_cookie;
   2487       val_quoted = ('"' == str[i]);
   2488       if (val_quoted)
   2489         i++;
   2490       value_start = i;
   2491       /* Find the end of the cookie-value */
   2492       while (str_len > i)
   2493       {
   2494         const char l = str[i];
   2495         if ((';' == l) || ('"' == l) || (',' == l) || ('\\' == l) || (0 == l))
   2496           break;
   2497         if ((' ' == l) || ('\t' == l))
   2498         {
   2499           if (! val_quoted)
   2500             break;
   2501           if (! wsp_in_quoted)
   2502             return MHD_PARSE_COOKIE_MALFORMED;
   2503           non_strict = true;
   2504         }
   2505         i++;
   2506       }
   2507       value_len = i - value_start;
   2508       if (val_quoted)
   2509       {
   2510         if ((str_len == i) || ('"' != str[i]))
   2511           return MHD_PARSE_COOKIE_MALFORMED; /* Incomplete cookie value, no closing quote */
   2512         i++;
   2513       }
   2514       /* Skip any whitespaces */
   2515       if ((str_len > i) && ((' ' == str[i]) || ('\t' == str[i])))
   2516       {
   2517         do
   2518         {
   2519           i++;
   2520         } while (str_len > i && (' ' == str[i] || '\t' == str[i]));
   2521         /* Whitespace at the end? */
   2522         if (str_len > i)
   2523         {
   2524           if (! allow_wsp_empty)
   2525             return MHD_PARSE_COOKIE_MALFORMED;
   2526           non_strict = true;
   2527         }
   2528       }
   2529       if (str_len == i)
   2530         valid_cookie = true;
   2531       else if (';' == str[i])
   2532         valid_cookie = true;
   2533       else
   2534         valid_cookie = false;
   2535 
   2536       if (! valid_cookie)
   2537         return MHD_PARSE_COOKIE_MALFORMED; /* Garbage at the end of the cookie value */
   2538     }
   2539 
   2540     mhd_ASSUME (0u != name_len);
   2541     mhd_ASSUME (str_len > name_start + name_len);
   2542     str[name_start + name_len] = '\0';
   2543 
   2544     if (0 != value_len)
   2545     {
   2546       struct MHD_String name;
   2547       struct MHD_String value;
   2548       mhd_ASSUME (str_len >= value_start + value_len);
   2549       name.cstr = str + name_start;
   2550       name.len = name_len;
   2551       if (value_start + value_len < str_len) /* Do not write outside allowed area */
   2552         str[value_start + value_len] = '\0'; /* Zero-terminate the value */
   2553       value.cstr = str + value_start;
   2554       value.len = value_len;
   2555       if (! mhd_stream_add_field (s,
   2556                                   MHD_VK_COOKIE,
   2557                                   &name,
   2558                                   &value))
   2559         return MHD_PARSE_COOKIE_NO_MEMORY;
   2560     }
   2561     else
   2562     {
   2563       struct MHD_String name;
   2564       struct MHD_String value;
   2565       name.cstr = str + name_start;
   2566       name.len = name_len;
   2567       value.cstr = "";
   2568       value.len = 0;
   2569       if (! mhd_stream_add_field (s,
   2570                                   MHD_VK_COOKIE,
   2571                                   &name,
   2572                                   &value))
   2573         return MHD_PARSE_COOKIE_NO_MEMORY;
   2574     }
   2575     if (str_len > i)
   2576     {
   2577       mhd_assert (0 == str[i] || ';' == str[i]);
   2578       mhd_assert (! val_quoted || ';' == str[i]);
   2579       mhd_assert (';' != str[i] || val_quoted || non_strict || 0 == value_len);
   2580       i++;
   2581       if (str_len == i)
   2582       { /* No next cookie after semicolon */
   2583         if (! allow_wsp_empty)
   2584           return MHD_PARSE_COOKIE_MALFORMED;
   2585         non_strict = true;
   2586       }
   2587       else if (' ' != str[i])
   2588       {/* No space after semicolon */
   2589         if (('\t' == str[i]) && tab_as_sp)
   2590           i++;
   2591         else if (! allow_no_space)
   2592           return MHD_PARSE_COOKIE_MALFORMED;
   2593         non_strict = true;
   2594       }
   2595       else
   2596       {
   2597         i++;
   2598         if (str_len == i)
   2599         {
   2600           if (! allow_wsp_empty)
   2601             return MHD_PARSE_COOKIE_MALFORMED;
   2602           non_strict = true;
   2603         }
   2604       }
   2605     }
   2606   }
   2607   return non_strict? MHD_PARSE_COOKIE_OK_LAX : MHD_PARSE_COOKIE_OK;
   2608 }
   2609 
   2610 
   2611 /**
   2612  * Parse the cookie header (see RFC 6265).
   2613  *
   2614  * @param connection connection to parse header of
   2615  * @param cookie_val the value of the "Cookie:" header
   2616  * @return #MHD_PARSE_COOKIE_OK for success, error code otherwise
   2617  */
   2618 static enum mhd_ParseCookie
   2619 parse_cookie_header (struct MHD_Connection *restrict connection,
   2620                      struct MHD_StringNullable *restrict cookie_val)
   2621 {
   2622   char *cpy;
   2623   size_t i;
   2624   enum mhd_ParseCookie parse_res;
   2625   struct mhd_RequestField *const saved_tail =
   2626     connection->rq.fields.last;  // FIXME: a better way?
   2627   const bool allow_partially_correct_cookie =
   2628     (1 >= connection->daemon->req_cfg.strictness);
   2629 
   2630   if (NULL == cookie_val)
   2631     return MHD_PARSE_COOKIE_OK;
   2632   if (0 == cookie_val->len)
   2633     return MHD_PARSE_COOKIE_OK;
   2634 
   2635   cpy = (char *) mhd_stream_alloc_memory (connection,
   2636                                           cookie_val->len + 1);
   2637   if (NULL == cpy)
   2638     parse_res = MHD_PARSE_COOKIE_NO_MEMORY;
   2639   else
   2640   {
   2641     memcpy (cpy,
   2642             cookie_val->cstr,
   2643             cookie_val->len + 1);
   2644     mhd_assert (0 == cpy[cookie_val->len]);
   2645 
   2646     /* Must not have initial whitespaces */
   2647     mhd_assert (' ' != cpy[0]);
   2648     mhd_assert ('\t' != cpy[0]);
   2649 
   2650     i = 0;
   2651     parse_res = parse_cookies_string (cookie_val->len - i,
   2652                                       cpy + i,
   2653                                       connection->daemon->req_cfg.strictness,
   2654                                       &(connection->h1_stream));
   2655   }
   2656 
   2657   switch (parse_res)
   2658   {
   2659   case MHD_PARSE_COOKIE_OK:
   2660     break;
   2661   case MHD_PARSE_COOKIE_OK_LAX:
   2662     if (saved_tail != connection->rq.fields.last)
   2663       mhd_LOG_MSG (connection->daemon, MHD_SC_REQ_COOKIE_PARSED_NOT_COMPLIANT, \
   2664                    "The Cookie header has been parsed, but it is not "
   2665                    "fully compliant with specifications.");
   2666     break;
   2667   case MHD_PARSE_COOKIE_MALFORMED:
   2668     if (saved_tail != connection->rq.fields.last) // FIXME: a better way?
   2669     {
   2670       if (! allow_partially_correct_cookie)
   2671       {
   2672         /* Remove extracted values from partially broken cookie */
   2673         /* Memory remains allocated until the end of the request processing */
   2674         connection->rq.fields.last = saved_tail;  // FIXME: a better way?
   2675         saved_tail->fields.next = NULL;  // FIXME: a better way?
   2676         mhd_LOG_MSG ( \
   2677           connection->daemon, MHD_SC_REQ_COOKIE_IGNORED_NOT_COMPLIANT, \
   2678           "The Cookie header is ignored as it contains malformed data.");
   2679       }
   2680       else
   2681         mhd_LOG_MSG (connection->daemon, MHD_SC_REQ_COOKIE_PARSED_PARTIALLY, \
   2682                      "The Cookie header has been only partially parsed " \
   2683                      "as it contains malformed data.");
   2684     }
   2685     else
   2686       mhd_LOG_MSG (connection->daemon, MHD_SC_REQ_COOKIE_INVALID,
   2687                    "The Cookie header has malformed data.");
   2688     break;
   2689   case MHD_PARSE_COOKIE_NO_MEMORY:
   2690     mhd_LOG_MSG (connection->daemon, MHD_SC_CONNECTION_POOL_NO_MEM_COOKIE,
   2691                  "Not enough memory in the connection pool to "
   2692                  "parse client cookies!\n");
   2693     break;
   2694   default:
   2695     mhd_UNREACHABLE ();
   2696     break;
   2697   }
   2698 
   2699   return parse_res;
   2700 }
   2701 
   2702 
   2703 /**
   2704  * Send error reply when receive buffer space exhausted while receiving or
   2705  * storing the request headers
   2706  * @param c the connection to handle
   2707  */
   2708 mhd_static_inline void
   2709 handle_req_cookie_no_space (struct MHD_Connection *restrict c)
   2710 {
   2711   unsigned int err_code;
   2712 
   2713   err_code = mhd_stream_get_no_space_err_status_code (c,
   2714                                                       MHD_PROC_RECV_COOKIE,
   2715                                                       0,
   2716                                                       NULL);
   2717   mhd_RESPOND_WITH_ERROR_STATIC (c,
   2718                                  err_code,
   2719                                  ERR_RSP_REQUEST_HEADER_TOO_BIG);
   2720 }
   2721 
   2722 
   2723 #endif /* MHD_SUPPORT_COOKIES */
   2724 
   2725 
   2726 MHD_INTERNAL MHD_FN_PAR_NONNULL_ALL_ void
   2727 mhd_stream_parse_request_headers (struct MHD_Connection *restrict c)
   2728 {
   2729   bool has_host;
   2730   bool has_trenc;
   2731   bool has_cntnlen;
   2732   bool has_keepalive;
   2733   struct mhd_RequestField *f;
   2734 
   2735   /* The presence of the request body is indicated by "Content-Length:" or
   2736      "Transfer-Encoding:" request headers.
   2737      Unless one of these two headers is used, the request has no request body.
   2738      See RFC9112, Section 6, paragraph 4. */
   2739   c->rq.have_chunked_upload = false;
   2740   c->rq.cntn.cntn_size = 0;
   2741 
   2742   has_host = false;
   2743   has_trenc = false;
   2744   has_cntnlen = false;
   2745   has_keepalive = true;
   2746 
   2747   for (f = mhd_DLINKEDL_GET_FIRST (&(c->rq), fields);
   2748        NULL != f;
   2749        f = mhd_DLINKEDL_GET_NEXT (f, fields))
   2750   {
   2751     if (MHD_VK_HEADER != f->field.kind)
   2752       continue;
   2753 
   2754     /* "Host:" */
   2755     if (mhd_str_equal_caseless_n_st (MHD_HTTP_HEADER_HOST,
   2756                                      f->field.nv.name.cstr,
   2757                                      f->field.nv.name.len))
   2758     {
   2759       if ((has_host)
   2760           && (-3 < c->daemon->req_cfg.strictness))
   2761       {
   2762         mhd_LOG_MSG (c->daemon, MHD_SC_HOST_HEADER_SEVERAL, \
   2763                      "Received request with more than one 'Host' header.");
   2764         mhd_RESPOND_WITH_ERROR_STATIC (c,
   2765                                        MHD_HTTP_STATUS_BAD_REQUEST,
   2766                                        ERR_RSP_REQUEST_HAS_SEVERAL_HOSTS);
   2767         return;
   2768       }
   2769       if ((0u == f->field.nv.value.len)
   2770           && (-3 < c->daemon->req_cfg.strictness))
   2771       {
   2772         mhd_LOG_MSG (c->daemon, MHD_SC_HOST_HEADER_MALFORMED, \
   2773                      "Received request with empty 'Host' header.");
   2774         mhd_RESPOND_WITH_ERROR_STATIC (c,
   2775                                        MHD_HTTP_STATUS_BAD_REQUEST,
   2776                                        ERR_RSP_REQUEST_HAS_MALFORMED_HOST);
   2777         return;
   2778       }
   2779       has_host = true;
   2780       continue;
   2781     }
   2782 
   2783     /* "Content-Length:" */
   2784     if (mhd_str_equal_caseless_n_st (MHD_HTTP_HEADER_CONTENT_LENGTH,
   2785                                      f->field.nv.name.cstr,
   2786                                      f->field.nv.name.len))
   2787     {
   2788       size_t num_digits;
   2789       uint_fast64_t cntn_size;
   2790 
   2791       num_digits = mhd_str_to_uint64_n (f->field.nv.value.cstr,
   2792                                         f->field.nv.value.len,
   2793                                         &cntn_size);
   2794       if (((0 == num_digits) &&
   2795            (0 != f->field.nv.value.len) &&
   2796            ('9' >= f->field.nv.value.cstr[0])
   2797            && ('0' <= f->field.nv.value.cstr[0]))
   2798           || (MHD_SIZE_UNKNOWN == c->rq.cntn.cntn_size))
   2799       {
   2800         mhd_LOG_MSG (c->daemon, MHD_SC_CONTENT_LENGTH_TOO_LARGE, \
   2801                      "Too large value of 'Content-Length' header. " \
   2802                      "Closing connection.");
   2803         mhd_RESPOND_WITH_ERROR_STATIC (c, \
   2804                                        MHD_HTTP_STATUS_CONTENT_TOO_LARGE, \
   2805                                        ERR_RSP_REQUEST_CONTENTLENGTH_TOOLARGE);
   2806         return;
   2807       }
   2808       else if ((f->field.nv.value.len != num_digits) ||
   2809                (0 == num_digits))
   2810       {
   2811         mhd_LOG_MSG (c->daemon, MHD_SC_CONTENT_LENGTH_MALFORMED, \
   2812                      "Failed to parse 'Content-Length' header. " \
   2813                      "Closing connection.");
   2814         mhd_RESPOND_WITH_ERROR_STATIC (c, \
   2815                                        MHD_HTTP_STATUS_BAD_REQUEST, \
   2816                                        ERR_RSP_REQUEST_CONTENTLENGTH_MALFORMED);
   2817         return;
   2818       }
   2819 
   2820       if (has_cntnlen)
   2821       {
   2822         bool send_err;
   2823         send_err = false;
   2824         if (c->rq.cntn.cntn_size == cntn_size)
   2825         {
   2826           if (0 < c->daemon->req_cfg.strictness)
   2827           {
   2828             mhd_LOG_MSG (c->daemon, MHD_SC_CONTENT_LENGTH_SEVERAL_SAME, \
   2829                          "Received request with more than one " \
   2830                          "'Content-Length' header with the same value.");
   2831             send_err = true;
   2832           }
   2833         }
   2834         else
   2835         {
   2836           mhd_LOG_MSG (c->daemon, MHD_SC_CONTENT_LENGTH_SEVERAL_DIFFERENT, \
   2837                        "Received request with more than one " \
   2838                        "'Content-Length' header with conflicting values.");
   2839           send_err = true;
   2840         }
   2841 
   2842         if (send_err)
   2843         {
   2844           mhd_RESPOND_WITH_ERROR_STATIC ( \
   2845             c, \
   2846             MHD_HTTP_STATUS_BAD_REQUEST, \
   2847             ERR_RSP_REQUEST_CONTENTLENGTH_SEVERAL);
   2848           return;
   2849         }
   2850       }
   2851       mhd_assert ((0 == c->rq.cntn.cntn_size) || \
   2852                   (c->rq.cntn.cntn_size == cntn_size));
   2853       c->rq.cntn.cntn_size = cntn_size;
   2854       has_cntnlen = true;
   2855       continue;
   2856     }
   2857 
   2858     /* "Connection:" */
   2859     if (mhd_str_equal_caseless_n_st (MHD_HTTP_HEADER_CONNECTION,
   2860                                      f->field.nv.name.cstr,
   2861                                      f->field.nv.name.len))
   2862     {
   2863       if (mhd_str_has_token_caseless (f->field.nv.value.cstr, // TODO: compare as size string
   2864                                       "close",
   2865                                       mhd_SSTR_LEN ("close")))
   2866       {
   2867         mhd_assert (mhd_CONN_MUST_UPGRADE != c->conn_reuse);
   2868         c->conn_reuse = mhd_CONN_MUST_CLOSE;
   2869       }
   2870       else if ((MHD_HTTP_VERSION_1_0 == c->rq.http_ver)
   2871                && (mhd_CONN_MUST_CLOSE != c->conn_reuse))
   2872       {
   2873         if (mhd_str_has_token_caseless (f->field.nv.value.cstr,  // TODO: compare as size string
   2874                                         "keep-alive",
   2875                                         mhd_SSTR_LEN ("keep-alive")))
   2876           has_keepalive = true;
   2877       }
   2878 
   2879       continue;
   2880     }
   2881 
   2882     /* "Transfer-Encoding:" */
   2883     if (mhd_str_equal_caseless_n_st (MHD_HTTP_HEADER_TRANSFER_ENCODING,
   2884                                      f->field.nv.name.cstr,
   2885                                      f->field.nv.name.len))
   2886     {
   2887       if (mhd_str_equal_caseless_n_st ("chunked",
   2888                                        f->field.nv.value.cstr,
   2889                                        f->field.nv.value.len))
   2890       {
   2891         c->rq.have_chunked_upload = true;
   2892         c->rq.cntn.cntn_size = MHD_SIZE_UNKNOWN;
   2893       }
   2894       else
   2895       {
   2896         mhd_LOG_MSG (c->daemon, MHD_SC_TRANSFER_ENCODING_UNSUPPORTED, \
   2897                      "The 'Transfer-Encoding' used in request is " \
   2898                      "unsupported or invalid.");
   2899         mhd_RESPOND_WITH_ERROR_STATIC (c,
   2900                                        MHD_HTTP_STATUS_BAD_REQUEST,
   2901                                        ERR_RSP_UNSUPPORTED_TR_ENCODING);
   2902         return;
   2903       }
   2904       has_trenc = true;
   2905       continue;
   2906     }
   2907 
   2908 #ifdef MHD_SUPPORT_COOKIES
   2909     /* "Cookie:" */
   2910     if ((! c->daemon->req_cfg.disable_cookies) &&
   2911         mhd_str_equal_caseless_n_st (MHD_HTTP_HEADER_COOKIE,
   2912                                      f->field.nv.name.cstr,
   2913                                      f->field.nv.name.len))
   2914     {
   2915       if (MHD_PARSE_COOKIE_NO_MEMORY ==
   2916           parse_cookie_header (c,
   2917                                &(f->field.nv.value)))
   2918       {
   2919         handle_req_cookie_no_space (c);
   2920         return;
   2921       }
   2922       continue;
   2923     }
   2924 #endif /* MHD_SUPPORT_COOKIES */
   2925 
   2926     /* "Expect: 100-continue" */
   2927     if (mhd_str_equal_caseless_n_st (MHD_HTTP_HEADER_EXPECT,
   2928                                      f->field.nv.name.cstr,
   2929                                      f->field.nv.name.len))
   2930     {
   2931       if (mhd_str_equal_caseless_n_st ("100-continue",
   2932                                        f->field.nv.value.cstr,
   2933                                        f->field.nv.value.len))
   2934         c->rq.have_expect_100 = true;
   2935       else
   2936       {
   2937         if (0 < c->daemon->req_cfg.strictness)
   2938         {
   2939           mhd_LOG_MSG (c->daemon, MHD_SC_EXPECT_HEADER_VALUE_UNSUPPORTED, \
   2940                        "The 'Expect' header value used in request is " \
   2941                        "unsupported or invalid.");
   2942           mhd_RESPOND_WITH_ERROR_STATIC (c,
   2943                                          MHD_HTTP_STATUS_EXPECTATION_FAILED,
   2944                                          ERR_RSP_UNSUPPORTED_EXPECT_HDR_VALUE);
   2945           return;
   2946         }
   2947       }
   2948       continue;
   2949     }
   2950   }
   2951 
   2952   c->rq.cntn.cntn_present = (has_trenc || has_cntnlen);
   2953   if (has_trenc && has_cntnlen)
   2954   {
   2955     if (0 < c->daemon->req_cfg.strictness)
   2956     {
   2957       mhd_RESPOND_WITH_ERROR_STATIC ( \
   2958         c, \
   2959         MHD_HTTP_STATUS_BAD_REQUEST, \
   2960         ERR_RSP_REQUEST_CNTNLENGTH_WITH_TR_ENCODING);
   2961       return;
   2962     }
   2963     /* Must close connection after reply to prevent potential attack */
   2964     c->conn_reuse = mhd_CONN_MUST_CLOSE;
   2965     c->rq.cntn.cntn_size = MHD_SIZE_UNKNOWN;
   2966     mhd_assert (c->rq.have_chunked_upload);
   2967     mhd_LOG_MSG (c->daemon, MHD_SC_CONTENT_LENGTH_AND_TR_ENC, \
   2968                  "The 'Content-Length' request header is ignored " \
   2969                  "as chunked 'Transfer-Encoding' is used " \
   2970                  "for this request.");
   2971   }
   2972 
   2973   if (MHD_HTTP_VERSION_IS_LIKE_11 (c->rq.http_ver))
   2974   {
   2975     if ((! has_host) &&
   2976         (-3 < c->daemon->req_cfg.strictness))
   2977     {
   2978       mhd_LOG_MSG (c->daemon, MHD_SC_HOST_HEADER_MISSING, \
   2979                    "Received HTTP/1.1 request without 'Host' header.");
   2980       mhd_RESPOND_WITH_ERROR_STATIC (c,
   2981                                      MHD_HTTP_STATUS_BAD_REQUEST,
   2982                                      ERR_RSP_REQUEST_LACKS_HOST);
   2983       return;
   2984     }
   2985   }
   2986   else
   2987   {
   2988     if (! has_keepalive)
   2989       c->conn_reuse = mhd_CONN_MUST_CLOSE; /* Do not re-use HTTP/1.0 connection by default */
   2990     if (has_trenc)
   2991       c->conn_reuse = mhd_CONN_MUST_CLOSE; /* Framing could be incorrect */
   2992   }
   2993 
   2994   c->stage = mhd_HTTP_STAGE_HEADERS_PROCESSED;
   2995   return;
   2996 }
   2997 
   2998 
   2999 /**
   3000  * Is "100 Continue" needed to be sent for current request?
   3001  *
   3002  * @param c the connection to check
   3003  * @return false 100 CONTINUE is not needed,
   3004  *         true otherwise
   3005  */
   3006 static MHD_FN_PAR_NONNULL_ALL_ bool
   3007 need_100_continue (struct MHD_Connection *restrict c)
   3008 {
   3009   mhd_assert (MHD_HTTP_VERSION_IS_1X (c->rq.http_ver));
   3010   mhd_assert (mhd_HTTP_STAGE_HEADERS_PROCESSED <= c->stage);
   3011   mhd_assert (mhd_HTTP_STAGE_BODY_RECEIVING > c->stage);
   3012 
   3013   if (! c->rq.have_expect_100)
   3014     return false; /* "100 Continue" has not been requested by the client */
   3015 
   3016   if (0 != c->read_buffer_offset)
   3017     return false; /* Part of the content has been received already */
   3018 
   3019   if (0 == c->rq.cntn.cntn_size)
   3020     return false; /* There is no content or zero-sized content for this request */
   3021 
   3022   if (MHD_HTTP_VERSION_1_0 == c->rq.http_ver)
   3023     return false; /* '100 Continue' is not allowed for HTTP/1.0 */
   3024 
   3025   return true;
   3026 }
   3027 
   3028 
   3029 /**
   3030  * Check whether special buffer is required to handle the upload content and
   3031  * try to allocate if necessary.
   3032  * Respond with error to the client if buffer cannot be allocated
   3033  * @param c the connection to
   3034  * @return true if succeed,
   3035  *         false if error response is set
   3036  */
   3037 static MHD_FN_PAR_NONNULL_ALL_ bool
   3038 check_and_alloc_buf_for_upload_processing (struct MHD_Connection *restrict c)
   3039 {
   3040   mhd_assert ((mhd_ACTION_UPLOAD == c->rq.app_act.head_act.act) || \
   3041               (mhd_ACTION_POST_PARSE == c->rq.app_act.head_act.act));
   3042 
   3043   if (c->rq.have_chunked_upload)
   3044     return true; /* The size is unknown, buffers will be dynamically allocated
   3045                     and re-allocated */
   3046   mhd_assert (c->read_buffer_size > c->read_buffer_offset);
   3047 #if 0 // TODO: support processing full response in the connection buffer
   3048   if ((c->read_buffer_size - c->read_buffer_offset) >=
   3049       c->rq.cntn.cntn_size)
   3050     return true; /* No additional buffer needed */
   3051 #endif
   3052 
   3053   if ((mhd_ACTION_UPLOAD == c->rq.app_act.head_act.act) &&
   3054       (NULL == c->rq.app_act.head_act.data.upload.full.cb))
   3055     return true; /* data will be processed only incrementally */
   3056 
   3057   if (mhd_ACTION_UPLOAD != c->rq.app_act.head_act.act)
   3058   {
   3059     // TODO: add check for intermental-only POST processing */
   3060     mhd_assert (0 && "Not implemented yet");
   3061     return false;
   3062   }
   3063 
   3064   if ((c->rq.cntn.cntn_size >
   3065        c->rq.app_act.head_act.data.upload.large_buffer_size) ||
   3066       ! mhd_daemon_get_lbuf (c->daemon,
   3067                              (size_t) c->rq.cntn.cntn_size,
   3068                              &(c->rq.cntn.lbuf)))
   3069   {
   3070     if (NULL != c->rq.app_act.head_act.data.upload.inc.cb)
   3071     {
   3072       c->rq.app_act.head_act.data.upload.full.cb = NULL;
   3073       return true; /* Data can be processed incrementally */
   3074     }
   3075 
   3076     mhd_RESPOND_WITH_ERROR_STATIC (c,
   3077                                    MHD_HTTP_STATUS_CONTENT_TOO_LARGE,
   3078                                    ERR_RSP_REQUEST_CONTENTLENGTH_TOOLARGE);
   3079     return false;
   3080   }
   3081 
   3082   return true;
   3083 }
   3084 
   3085 
   3086 MHD_INTERNAL MHD_FN_PAR_NONNULL_ALL_ bool
   3087 mhd_stream_call_app_request_cb (struct MHD_Connection *restrict c)
   3088 {
   3089   struct MHD_Daemon *restrict d = c->daemon;
   3090   struct MHD_String path;
   3091   const struct MHD_Action *a;
   3092 
   3093   mhd_assert (mhd_HTTP_METHOD_NO_METHOD != c->rq.http_mthd);
   3094   mhd_assert (NULL == c->rp.response);
   3095 
   3096   if (mhd_ACTION_NO_ACTION != c->rq.app_act.head_act.act)
   3097     MHD_PANIC ("MHD_Action has been set already");
   3098 
   3099   path.cstr = c->rq.url;
   3100   path.len = c->rq.url_len;
   3101 
   3102   c->rq.app_aware = true;
   3103   a = d->req_cfg.cb (d->req_cfg.cb_cls,
   3104                      &(c->rq),
   3105                      &path,
   3106                      (enum MHD_HTTP_Method) c->rq.http_mthd,
   3107                      c->rq.cntn.cntn_size);
   3108 
   3109   if ((NULL != a)
   3110       && (((&(c->rq.app_act.head_act) != a))
   3111           || ! mhd_ACTION_IS_VALID (c->rq.app_act.head_act.act)))
   3112   {
   3113     mhd_LOG_MSG (d, MHD_SC_ACTION_INVALID, \
   3114                  "Provided action is not a correct action generated " \
   3115                  "for the current request.");
   3116     /* Perform cleanup of the created but now unused action */
   3117     switch (c->rq.app_act.head_act.act)
   3118     {
   3119     case mhd_ACTION_RESPONSE:
   3120       mhd_assert (NULL != c->rq.app_act.head_act.data.response);
   3121       mhd_response_dec_use_count (c->rq.app_act.head_act.data.response);
   3122       break;
   3123     case mhd_ACTION_UPLOAD:
   3124     case mhd_ACTION_SUSPEND:
   3125       /* No cleanup needed */
   3126       break;
   3127 #ifdef MHD_SUPPORT_POST_PARSER
   3128     case mhd_ACTION_POST_PARSE:
   3129       /* No cleanup needed */
   3130       break;
   3131 #endif /* MHD_SUPPORT_POST_PARSER */
   3132 #ifdef MHD_SUPPORT_UPGRADE
   3133     case mhd_ACTION_UPGRADE:
   3134       /* No cleanup needed */
   3135       break;
   3136 #endif /* MHD_SUPPORT_UPGRADE */
   3137     case mhd_ACTION_ABORT:
   3138       mhd_UNREACHABLE ();
   3139       break;
   3140     case mhd_ACTION_NO_ACTION:
   3141     default:
   3142       break;
   3143     }
   3144     a = NULL;
   3145   }
   3146   if (NULL == a)
   3147     c->rq.app_act.head_act.act = mhd_ACTION_ABORT;
   3148 
   3149   switch (c->rq.app_act.head_act.act)
   3150   {
   3151   case mhd_ACTION_RESPONSE:
   3152     c->rp.response = c->rq.app_act.head_act.data.response;
   3153     c->stage = mhd_HTTP_STAGE_REQ_RECV_FINISHED;
   3154     return true;
   3155   case mhd_ACTION_UPLOAD:
   3156     if (0 != c->rq.cntn.cntn_size)
   3157     {
   3158       if (! check_and_alloc_buf_for_upload_processing (c))
   3159         return true;
   3160       if (need_100_continue (c))
   3161       {
   3162         c->stage = mhd_HTTP_STAGE_CONTINUE_SENDING;
   3163         return true;
   3164       }
   3165       c->stage = mhd_HTTP_STAGE_BODY_RECEIVING;
   3166       return (0 != c->read_buffer_offset);
   3167     }
   3168     c->stage = mhd_HTTP_STAGE_FULL_REQ_RECEIVED;
   3169     return true;
   3170 #ifdef MHD_SUPPORT_POST_PARSER
   3171   case mhd_ACTION_POST_PARSE:
   3172     if (0 == c->rq.cntn.cntn_size)
   3173     {
   3174       c->rq.u_proc.post.parse_result = MHD_POST_PARSE_RES_REQUEST_EMPTY;
   3175       c->stage = mhd_HTTP_STAGE_FULL_REQ_RECEIVED;
   3176       return true;
   3177     }
   3178     if (! mhd_stream_prepare_for_post_parse (c))
   3179     {
   3180       mhd_assert (mhd_HTTP_STAGE_FOOTERS_RECEIVED < c->stage);
   3181       return true;
   3182     }
   3183     if (need_100_continue (c))
   3184     {
   3185       c->stage = mhd_HTTP_STAGE_CONTINUE_SENDING;
   3186       return true;
   3187     }
   3188     c->stage = mhd_HTTP_STAGE_BODY_RECEIVING;
   3189     return true;
   3190 #endif /* MHD_SUPPORT_POST_PARSER */
   3191   case mhd_ACTION_SUSPEND:
   3192     c->suspended = true;
   3193 #ifdef MHD_USE_TRACE_SUSPEND_RESUME
   3194     fprintf (stderr,
   3195              "%%%%%% Suspending connection, FD: %2llu\n",
   3196              (unsigned long long) c->sk.fd);
   3197 #endif /* MHD_USE_TRACE_SUSPEND_RESUME */
   3198     c->rq.app_act.head_act.act = mhd_ACTION_NO_ACTION;
   3199     return false;
   3200 #ifdef MHD_SUPPORT_UPGRADE
   3201   case mhd_ACTION_UPGRADE:
   3202     mhd_assert (0 == c->rq.cntn.cntn_size);
   3203     c->stage = mhd_HTTP_STAGE_UPGRADE_HEADERS_SENDING;
   3204     return false;
   3205 #endif /* MHD_SUPPORT_UPGRADE */
   3206   case mhd_ACTION_ABORT:
   3207     mhd_conn_start_closing_app_abort (c);
   3208     return true;
   3209   case mhd_ACTION_NO_ACTION:
   3210   default:
   3211     mhd_assert (0 && "Impossible value");
   3212     break;
   3213   }
   3214   mhd_UNREACHABLE ();
   3215   return false;
   3216 }
   3217 
   3218 
   3219 /**
   3220  * React on provided action for upload
   3221  * @param c the stream to use
   3222  * @param act the action provided by application
   3223  * @param final set to 'true' if this is final upload callback
   3224  * @return true if connection state has been changed,
   3225  *         false otherwise
   3226  */
   3227 MHD_INTERNAL
   3228 MHD_FN_PAR_NONNULL_ (1) bool
   3229 mhd_stream_process_upload_action (struct MHD_Connection *restrict c,
   3230                                   const struct MHD_UploadAction *act,
   3231                                   bool final)
   3232 {
   3233   if (NULL != act)
   3234   {
   3235     if ((&(c->rq.app_act.upl_act) != act) ||
   3236         ! mhd_UPLOAD_ACTION_IS_VALID (c->rq.app_act.upl_act.act) ||
   3237         (final &&
   3238          (mhd_UPLOAD_ACTION_CONTINUE == c->rq.app_act.upl_act.act)))
   3239     {
   3240       /* Perform cleanup of the created but now unused action */
   3241       switch (c->rq.app_act.upl_act.act)
   3242       {
   3243       case mhd_UPLOAD_ACTION_RESPONSE:
   3244         mhd_assert (NULL != c->rq.app_act.upl_act.data.response);
   3245         mhd_response_dec_use_count (c->rq.app_act.upl_act.data.response);
   3246         break;
   3247       case mhd_UPLOAD_ACTION_CONTINUE:
   3248       case mhd_UPLOAD_ACTION_SUSPEND:
   3249         /* No cleanup needed */
   3250         break;
   3251   #ifdef MHD_SUPPORT_UPGRADE
   3252       case mhd_UPLOAD_ACTION_UPGRADE:
   3253         /* No cleanup needed */
   3254         break;
   3255   #endif /* MHD_SUPPORT_UPGRADE */
   3256       case mhd_UPLOAD_ACTION_ABORT:
   3257         mhd_UNREACHABLE ();
   3258         break;
   3259       case mhd_UPLOAD_ACTION_NO_ACTION:
   3260       default:
   3261         break;
   3262       }
   3263       mhd_LOG_MSG (c->daemon, MHD_SC_UPLOAD_ACTION_INVALID, \
   3264                    "Provided action is not a correct action generated " \
   3265                    "for the current request.");
   3266       act = NULL;
   3267     }
   3268   }
   3269   if (NULL == act)
   3270     c->rq.app_act.upl_act.act = mhd_UPLOAD_ACTION_ABORT;
   3271 
   3272   switch (c->rq.app_act.upl_act.act)
   3273   {
   3274   case mhd_UPLOAD_ACTION_RESPONSE:
   3275     c->rp.response = c->rq.app_act.upl_act.data.response;
   3276     c->stage = mhd_HTTP_STAGE_REQ_RECV_FINISHED;
   3277     return true;
   3278   case mhd_UPLOAD_ACTION_CONTINUE:
   3279     memset (&(c->rq.app_act.upl_act), 0, sizeof(c->rq.app_act.upl_act));
   3280     return false;
   3281   case mhd_UPLOAD_ACTION_SUSPEND:
   3282     c->suspended = true;
   3283 #ifdef MHD_USE_TRACE_SUSPEND_RESUME
   3284     fprintf (stderr,
   3285              "%%%%%% Suspending connection, FD: %2llu\n",
   3286              (unsigned long long) c->sk.fd);
   3287 #endif /* MHD_USE_TRACE_SUSPEND_RESUME */
   3288     memset (&(c->rq.app_act.upl_act), 0, sizeof(c->rq.app_act.upl_act));
   3289     return false;
   3290 #ifdef MHD_SUPPORT_UPGRADE
   3291   case mhd_UPLOAD_ACTION_UPGRADE:
   3292     mhd_assert (c->rq.cntn.recv_size == c->rq.cntn.cntn_size);
   3293     mhd_assert (! c->rq.have_chunked_upload || \
   3294                 mhd_HTTP_STAGE_FULL_REQ_RECEIVED == c->stage);
   3295     c->stage = mhd_HTTP_STAGE_UPGRADE_HEADERS_SENDING;
   3296     return false;
   3297 #endif /* MHD_SUPPORT_UPGRADE */
   3298   case mhd_UPLOAD_ACTION_ABORT:
   3299     mhd_conn_start_closing_app_abort (c);
   3300     return true;
   3301   case mhd_UPLOAD_ACTION_NO_ACTION:
   3302   default:
   3303     mhd_assert (0 && "Impossible value");
   3304     break;
   3305   }
   3306   mhd_UNREACHABLE ();
   3307   return false;
   3308 }
   3309 
   3310 
   3311 static MHD_FN_PAR_NONNULL_ALL_ bool
   3312 process_request_chunked_body (struct MHD_Connection *restrict c)
   3313 {
   3314   struct MHD_Daemon *restrict d = c->daemon;
   3315   size_t available;
   3316   bool has_more_data;
   3317   char *restrict buffer_head;
   3318   const int discp_lvl = d->req_cfg.strictness;
   3319   /* Treat bare LF as the end of the line.
   3320      RFC 9112, section 2.2-3
   3321      Note: MHD never replaces bare LF with space (RFC 9110, section 5.5-5).
   3322      Bare LF is processed as end of the line or rejected as broken request. */
   3323   const bool bare_lf_as_crlf = mhd_ALLOW_BARE_LF_AS_CRLF (discp_lvl);
   3324   /* Allow "Bad WhiteSpace" in chunk extension.
   3325      RFC 9112, Section 7.1.1, Paragraph 2 */
   3326   const bool allow_bws = (MHD_PSL_VERY_STRICT > discp_lvl);
   3327   bool state_updated;
   3328 
   3329   mhd_assert (NULL == c->rp.response);
   3330   mhd_assert (c->rq.have_chunked_upload);
   3331   mhd_assert (MHD_SIZE_UNKNOWN == c->rq.cntn.cntn_size);
   3332 
   3333   buffer_head = c->read_buffer;
   3334   available = c->read_buffer_offset;
   3335   state_updated = false;
   3336   do
   3337   {
   3338     size_t cntn_data_ready;
   3339     bool need_inc_proc;
   3340 
   3341     has_more_data = false;
   3342 
   3343     if ( (c->rq.current_chunk_offset ==
   3344           c->rq.current_chunk_size) &&
   3345          (0 != c->rq.current_chunk_size) )
   3346     {
   3347       size_t i;
   3348       mhd_assert (0 != available);
   3349       /* skip new line at the *end* of a chunk */
   3350       i = 0;
   3351       if ( (2 <= available) &&
   3352            ('\r' == buffer_head[0]) &&
   3353            ('\n' == buffer_head[1]) )
   3354         i += 2;                        /* skip CRLF */
   3355       else if (bare_lf_as_crlf && ('\n' == buffer_head[0]))
   3356         i++;                           /* skip bare LF */
   3357       else if (2 > available)
   3358         break;                         /* need more upload data */
   3359       if (0 == i)
   3360       {
   3361         /* malformed encoding */
   3362         mhd_RESPOND_WITH_ERROR_STATIC (c,
   3363                                        MHD_HTTP_STATUS_BAD_REQUEST,
   3364                                        ERR_RSP_REQUEST_CHUNKED_MALFORMED);
   3365         return true;
   3366       }
   3367       available -= i;
   3368       buffer_head += i;
   3369       c->rq.current_chunk_offset = 0;
   3370       c->rq.current_chunk_size = 0;
   3371       if (0 == available)
   3372         break;
   3373     }
   3374     if (0 != c->rq.current_chunk_size)
   3375     {
   3376       uint_fast64_t cur_chunk_left;
   3377       mhd_assert (c->rq.current_chunk_offset < \
   3378                   c->rq.current_chunk_size);
   3379       /* we are in the middle of a chunk, give
   3380          as much as possible to the client (without
   3381          crossing chunk boundaries) */
   3382       cur_chunk_left
   3383         = c->rq.current_chunk_size
   3384           - c->rq.current_chunk_offset;
   3385       if (cur_chunk_left > available)
   3386         cntn_data_ready = available;
   3387       else
   3388       {         /* cur_chunk_left <= (size_t)available */
   3389         cntn_data_ready = (size_t) cur_chunk_left;
   3390         if (available > cntn_data_ready)
   3391           has_more_data = true;
   3392       }
   3393     }
   3394     else
   3395     { /* Need the parse the chunk size line */
   3396       /** The number of found digits in the chunk size number */
   3397       size_t num_dig;
   3398       uint_fast64_t chunk_size;
   3399       bool broken;
   3400       bool overflow;
   3401 
   3402       mhd_assert (0 != available);
   3403 
   3404       overflow = false;
   3405       chunk_size = 0; /* Mute possible compiler warning.
   3406                          The real value will be set later. */
   3407 
   3408       num_dig = mhd_strx_to_uint64_n (buffer_head,
   3409                                       available,
   3410                                       &chunk_size);
   3411       mhd_assert (num_dig <= available);
   3412       if (num_dig == available)
   3413         continue; /* Need line delimiter */
   3414 
   3415       broken = (0 == num_dig);
   3416       if (broken)
   3417         /* Check whether result is invalid due to uint64_t overflow */
   3418         overflow = ((('0' <= buffer_head[0]) && ('9' >= buffer_head[0])) ||
   3419                     (('A' <= buffer_head[0]) && ('F' >= buffer_head[0])) ||
   3420                     (('a' <= buffer_head[0]) && ('f' >= buffer_head[0])));
   3421       else
   3422       {
   3423         /**
   3424          * The length of the string with the number of the chunk size,
   3425          * including chunk extension
   3426          */
   3427         size_t chunk_size_line_len;
   3428 
   3429         chunk_size_line_len = 0;
   3430         if ((';' == buffer_head[num_dig]) ||
   3431             (allow_bws &&
   3432              ((' ' == buffer_head[num_dig]) ||
   3433               ('\t' == buffer_head[num_dig]))))
   3434         { /* Chunk extension */
   3435           size_t i;
   3436 
   3437           /* Skip bad whitespaces (if any) */
   3438           for (i = num_dig; i < available; ++i)
   3439           {
   3440             if ((' ' != buffer_head[i]) && ('\t' != buffer_head[i]))
   3441               break;
   3442           }
   3443           if (i == available)
   3444             break; /* need more data */
   3445           if (';' == buffer_head[i])
   3446           {
   3447             for (++i; i < available; ++i)
   3448             {
   3449               if ('\n' == buffer_head[i])
   3450                 break;
   3451             }
   3452             if (i == available)
   3453               break; /* need more data */
   3454             mhd_assert (i > num_dig);
   3455             mhd_assert (1 <= i);
   3456             /* Found LF position at 'i' (buffer_head[i] = '\n'), chunk ends at i+1 */
   3457             if (bare_lf_as_crlf)
   3458               chunk_size_line_len = i + 1; /* Don't care about CR before LF */
   3459             else if ('\r' == buffer_head[i - 1])
   3460               chunk_size_line_len = i + 1; /* Have CR LF, all good */
   3461             /* else: invalid termination, leave chunk_size_line_len at 0 */
   3462           }
   3463           else
   3464           { /* No ';' after "bad whitespace" */
   3465             mhd_assert (allow_bws);
   3466             mhd_assert (0 == chunk_size_line_len);
   3467           }
   3468         }
   3469         else
   3470         {
   3471           mhd_assert (available >= num_dig);
   3472           if ((2 <= (available - num_dig)) &&
   3473               ('\r' == buffer_head[num_dig]) &&
   3474               ('\n' == buffer_head[num_dig + 1]))
   3475             chunk_size_line_len = num_dig + 2;
   3476           else if (bare_lf_as_crlf &&
   3477                    ('\n' == buffer_head[num_dig]))
   3478             chunk_size_line_len = num_dig + 1;
   3479           else if (2 > (available - num_dig))
   3480             break; /* need more data */
   3481         }
   3482 
   3483         if (0 != chunk_size_line_len)
   3484         { /* Valid termination of the chunk size line */
   3485           mhd_assert (chunk_size_line_len <= available);
   3486           /* Start reading payload data of the chunk */
   3487           c->rq.current_chunk_offset = 0;
   3488           c->rq.current_chunk_size = chunk_size;
   3489 
   3490           available -= chunk_size_line_len;
   3491           buffer_head += chunk_size_line_len;
   3492 
   3493           if (0 == chunk_size)
   3494           { /* The final (termination) chunk */
   3495             c->rq.cntn.cntn_size = c->rq.cntn.recv_size;
   3496             c->stage = mhd_HTTP_STAGE_BODY_RECEIVED;
   3497             state_updated = true;
   3498             break;
   3499           }
   3500           if (available > 0)
   3501             has_more_data = true;
   3502           continue;
   3503         }
   3504         /* Invalid chunk size line */
   3505       }
   3506 
   3507       if (! overflow)
   3508         mhd_RESPOND_WITH_ERROR_STATIC (c,
   3509                                        MHD_HTTP_STATUS_BAD_REQUEST,
   3510                                        ERR_RSP_REQUEST_CHUNKED_MALFORMED);
   3511       else
   3512         mhd_RESPOND_WITH_ERROR_STATIC (c,
   3513                                        MHD_HTTP_STATUS_CONTENT_TOO_LARGE,
   3514                                        ERR_RSP_REQUEST_CHUNK_TOO_LARGE);
   3515       return true;
   3516     }
   3517     mhd_assert (c->rq.app_aware);
   3518 
   3519 #ifdef MHD_SUPPORT_POST_PARSER
   3520     if (mhd_ACTION_POST_PARSE == c->rq.app_act.head_act.act)
   3521     {
   3522       size_t size_provided;
   3523 
   3524       c->rq.cntn.recv_size += cntn_data_ready;
   3525       size_provided = cntn_data_ready;
   3526 
   3527       state_updated = mhd_stream_post_parse (c,
   3528                                              &size_provided,
   3529                                              buffer_head);
   3530       // TODO: support one chunk in-place processing?
   3531       mhd_assert ((0 == size_provided) || \
   3532                   (MHD_POST_PARSE_RES_OK != c->rq.u_proc.post.parse_result) || \
   3533                   (mhd_HTTP_STAGE_BODY_RECEIVING != c->stage));
   3534       if (mhd_HTTP_STAGE_BODY_RECEIVING != c->stage)
   3535         c->discard_request = true;
   3536     }
   3537     else
   3538 #endif /* MHD_SUPPORT_POST_PARSER */
   3539     if (1)
   3540     {
   3541       mhd_assert (mhd_ACTION_UPLOAD == c->rq.app_act.head_act.act);
   3542       if (NULL != c->rq.app_act.head_act.data.upload.full.cb)
   3543       {
   3544         need_inc_proc = false;
   3545 
   3546         mhd_assert (0 == c->rq.cntn.proc_size);
   3547         if ((uint_fast64_t) c->rq.cntn.lbuf.size <
   3548             c->rq.cntn.recv_size + cntn_data_ready)
   3549         {
   3550           size_t grow_size;
   3551 
   3552           grow_size = (size_t) (c->rq.cntn.recv_size + cntn_data_ready
   3553                                 - c->rq.cntn.lbuf.size);
   3554           if (((size_t) (c->rq.cntn.recv_size + cntn_data_ready) <
   3555                cntn_data_ready) ||
   3556               (! mhd_daemon_grow_lbuf (d,
   3557                                        grow_size,
   3558                                        &(c->rq.cntn.lbuf))))
   3559           {
   3560             /* Failed to grow the buffer, no space to put the new data */
   3561             const struct MHD_UploadAction *act;
   3562             if (NULL != c->rq.app_act.head_act.data.upload.inc.cb)
   3563             {
   3564               mhd_RESPOND_WITH_ERROR_STATIC (
   3565                 c,
   3566                 MHD_HTTP_STATUS_CONTENT_TOO_LARGE,
   3567                 ERR_RSP_MSG_REQUEST_TOO_BIG);
   3568               return true;
   3569             }
   3570             c->rq.app_act.head_act.data.upload.full.cb = NULL; /* Cannot process "full" content */
   3571             /* Process previously buffered data */
   3572             mhd_assert (c->rq.cntn.recv_size <= c->rq.cntn.lbuf.size);
   3573             act = c->rq.app_act.head_act.data.upload.inc.cb (
   3574               c->rq.app_act.head_act.data.upload.inc.cls,
   3575               &(c->rq),
   3576               (size_t) c->rq.cntn.recv_size,
   3577               c->rq.cntn.lbuf.data);
   3578             c->rq.cntn.proc_size = c->rq.cntn.recv_size;
   3579             mhd_daemon_free_lbuf (d, &(c->rq.cntn.lbuf));
   3580             if (mhd_stream_process_upload_action (c, act, false))
   3581               return true;
   3582             need_inc_proc = true;
   3583           }
   3584         }
   3585         if (! need_inc_proc)
   3586         {
   3587           memcpy (c->rq.cntn.lbuf.data + c->rq.cntn.recv_size,
   3588                   buffer_head, cntn_data_ready);
   3589           c->rq.cntn.recv_size += cntn_data_ready;
   3590         }
   3591       }
   3592       else
   3593         need_inc_proc = true;
   3594 
   3595       if (need_inc_proc)
   3596       {
   3597         const struct MHD_UploadAction *act;
   3598         mhd_assert (NULL != c->rq.app_act.head_act.data.upload.inc.cb);
   3599 
   3600         c->rq.cntn.recv_size += cntn_data_ready;
   3601         act = c->rq.app_act.head_act.data.upload.inc.cb (
   3602           c->rq.app_act.head_act.data.upload.inc.cls,
   3603           &(c->rq),
   3604           cntn_data_ready,
   3605           buffer_head);
   3606         c->rq.cntn.proc_size += cntn_data_ready;
   3607         state_updated = mhd_stream_process_upload_action (c, act, false);
   3608       }
   3609     }
   3610     /* dh left "processed" bytes in buffer for next time... */
   3611     mhd_ASSUME (available >= cntn_data_ready);
   3612     buffer_head += cntn_data_ready;
   3613     available -= cntn_data_ready;
   3614     mhd_assert (MHD_SIZE_UNKNOWN == c->rq.cntn.cntn_size);
   3615     mhd_ASSUME (c->rq.current_chunk_offset + cntn_data_ready >=
   3616                 c->rq.current_chunk_offset);
   3617     c->rq.current_chunk_offset += cntn_data_ready;
   3618   } while (has_more_data && ! state_updated);
   3619   /* TODO: optionally? zero out reused memory region */
   3620   if ( (available > 0) &&
   3621        (buffer_head != c->read_buffer) )
   3622     memmove (c->read_buffer,
   3623              buffer_head,
   3624              available);
   3625   else
   3626     mhd_assert ((0 == available) || \
   3627                 (c->read_buffer_offset == available));
   3628   c->read_buffer_offset = available;
   3629 
   3630   return state_updated;
   3631 }
   3632 
   3633 
   3634 static MHD_FN_PAR_NONNULL_ALL_ bool
   3635 process_request_nonchunked_body (struct MHD_Connection *restrict c)
   3636 {
   3637   size_t cntn_data_ready;
   3638   bool read_buf_reuse;
   3639   bool state_updated;
   3640 
   3641   mhd_assert (NULL == c->rp.response);
   3642   mhd_assert (! c->rq.have_chunked_upload);
   3643   mhd_assert (MHD_SIZE_UNKNOWN != c->rq.cntn.cntn_size);
   3644   mhd_assert (c->rq.cntn.recv_size < c->rq.cntn.cntn_size);
   3645   mhd_assert (c->rq.app_aware);
   3646 
   3647   if ((c->rq.cntn.cntn_size - c->rq.cntn.recv_size) < c->read_buffer_offset)
   3648     cntn_data_ready = (size_t) (c->rq.cntn.cntn_size - c->rq.cntn.recv_size);
   3649   else
   3650     cntn_data_ready = c->read_buffer_offset;
   3651 
   3652   read_buf_reuse = false;
   3653   state_updated = false;
   3654   mhd_assert (! read_buf_reuse); /* Mute analyser warning */
   3655 
   3656 #ifdef MHD_SUPPORT_POST_PARSER
   3657   if (mhd_ACTION_POST_PARSE == c->rq.app_act.head_act.act)
   3658   {
   3659     size_t size_provided;
   3660     // TODO: rework to correctly support partial processing
   3661     // TODO: rework to support receiving directly into "large buffer"
   3662     c->rq.cntn.recv_size += cntn_data_ready;
   3663     size_provided = cntn_data_ready;
   3664 
   3665     state_updated = mhd_stream_post_parse (c,
   3666                                            &size_provided,
   3667                                            c->read_buffer);
   3668     mhd_assert ((0 == size_provided) || \
   3669                 (MHD_POST_PARSE_RES_OK != c->rq.u_proc.post.parse_result) || \
   3670                 (mhd_HTTP_STAGE_BODY_RECEIVING != c->stage));
   3671     if (mhd_HTTP_STAGE_BODY_RECEIVING != c->stage)
   3672       c->discard_request = true;
   3673 
   3674     read_buf_reuse = true;
   3675     c->rq.cntn.proc_size += cntn_data_ready;
   3676     if (c->rq.cntn.recv_size == c->rq.cntn.cntn_size)
   3677     {
   3678       c->stage = mhd_HTTP_STAGE_FULL_REQ_RECEIVED;
   3679       state_updated = true;
   3680     }
   3681   }
   3682   else
   3683 #endif /* MHD_SUPPORT_POST_PARSER */
   3684   if (1)
   3685   {
   3686     mhd_assert (mhd_ACTION_UPLOAD == c->rq.app_act.head_act.act);
   3687     if (NULL != c->rq.app_act.head_act.data.upload.full.cb)
   3688     {
   3689       // TODO: implement processing in pool memory if buffer is large enough
   3690       mhd_assert ((c->rq.cntn.recv_size + cntn_data_ready) <=
   3691                   (uint_fast64_t) c->rq.cntn.lbuf.size);
   3692       memcpy (c->rq.cntn.lbuf.data + c->rq.cntn.recv_size,
   3693               c->read_buffer, cntn_data_ready);
   3694       c->rq.cntn.recv_size += cntn_data_ready;
   3695       read_buf_reuse = true;
   3696       if (c->rq.cntn.recv_size == c->rq.cntn.cntn_size)
   3697       {
   3698         c->stage = mhd_HTTP_STAGE_FULL_REQ_RECEIVED;
   3699         state_updated = true;
   3700       }
   3701     }
   3702     else
   3703     {
   3704       const struct MHD_UploadAction *act;
   3705       mhd_assert (NULL != c->rq.app_act.head_act.data.upload.inc.cb);
   3706 
   3707       c->rq.cntn.recv_size += cntn_data_ready;
   3708       act = c->rq.app_act.head_act.data.upload.inc.cb (
   3709         c->rq.app_act.head_act.data.upload.inc.cls,
   3710         &(c->rq),
   3711         cntn_data_ready,
   3712         c->read_buffer);
   3713       c->rq.cntn.proc_size += cntn_data_ready;
   3714       read_buf_reuse = true;
   3715       state_updated = mhd_stream_process_upload_action (c, act, false);
   3716     }
   3717   }
   3718 
   3719   if (read_buf_reuse)
   3720   {
   3721     size_t data_left_size;
   3722     mhd_assert (c->read_buffer_offset >= cntn_data_ready);
   3723     data_left_size = c->read_buffer_offset - cntn_data_ready;
   3724     if (0 != data_left_size)
   3725       memmove (c->read_buffer,
   3726                c->read_buffer + cntn_data_ready,
   3727                data_left_size);
   3728     c->read_buffer_offset = data_left_size;
   3729   }
   3730 
   3731   return state_updated;
   3732 }
   3733 
   3734 
   3735 MHD_INTERNAL MHD_FN_PAR_NONNULL_ALL_ bool
   3736 mhd_stream_process_request_body (struct MHD_Connection *restrict c)
   3737 {
   3738   if (c->rq.have_chunked_upload)
   3739     return process_request_chunked_body (c);
   3740 
   3741   return process_request_nonchunked_body (c);
   3742 }
   3743 
   3744 
   3745 MHD_INTERNAL MHD_FN_PAR_NONNULL_ALL_ bool
   3746 mhd_stream_call_app_final_upload_cb (struct MHD_Connection *restrict c)
   3747 {
   3748   const struct MHD_UploadAction *act;
   3749   bool state_changed;
   3750   mhd_assert (mhd_ACTION_POST_PARSE == c->rq.app_act.head_act.act || \
   3751               mhd_ACTION_UPLOAD == c->rq.app_act.head_act.act);
   3752 
   3753 #ifdef MHD_SUPPORT_POST_PARSER
   3754   if (mhd_ACTION_POST_PARSE == c->rq.app_act.head_act.act)
   3755     return mhd_stream_process_post_finish (c);
   3756 #endif /* MHD_SUPPORT_POST_PARSER */
   3757 
   3758   mhd_assert (mhd_ACTION_UPLOAD == c->rq.app_act.head_act.act);
   3759 
   3760   if (NULL != c->rq.app_act.head_act.data.upload.full.cb)
   3761   {
   3762     mhd_assert (c->rq.cntn.recv_size == c->rq.cntn.cntn_size);
   3763     mhd_assert (0 == c->rq.cntn.proc_size);
   3764     mhd_assert (NULL != c->rq.cntn.lbuf.data);
   3765     mhd_assert (c->rq.cntn.recv_size <= c->rq.cntn.lbuf.size);
   3766     // TODO: implement processing in pool memory if it is large enough
   3767     act = c->rq.app_act.head_act.data.upload.full.cb (
   3768       c->rq.app_act.head_act.data.upload.full.cls,
   3769       &(c->rq),
   3770       (size_t) c->rq.cntn.recv_size,
   3771       c->rq.cntn.lbuf.data);
   3772     c->rq.cntn.proc_size = c->rq.cntn.recv_size;
   3773   }
   3774   else
   3775   {
   3776     mhd_assert (NULL != c->rq.app_act.head_act.data.upload.inc.cb);
   3777     mhd_assert (c->rq.cntn.cntn_size == c->rq.cntn.proc_size);
   3778     act = c->rq.app_act.head_act.data.upload.inc.cb (
   3779       c->rq.app_act.head_act.data.upload.inc.cls,
   3780       &(c->rq),
   3781       0,
   3782       NULL);
   3783   }
   3784 
   3785   state_changed = mhd_stream_process_upload_action (c, act, true);
   3786   if (! c->suspended)
   3787     mhd_daemon_free_lbuf (c->daemon, &(c->rq.cntn.lbuf));
   3788 
   3789   return state_changed;
   3790 }
   3791 
   3792 
   3793 MHD_INTERNAL MHD_FN_PAR_NONNULL_ALL_ bool
   3794 mhd_stream_process_req_recv_finished (struct MHD_Connection *restrict c)
   3795 {
   3796   if (NULL != c->rq.cntn.lbuf.data)
   3797     mhd_daemon_free_lbuf (c->daemon, &(c->rq.cntn.lbuf));
   3798   c->rq.cntn.lbuf.data = NULL;
   3799   if (c->rq.cntn.cntn_size != c->rq.cntn.proc_size)
   3800     c->discard_request = true;
   3801   mhd_assert (NULL != c->rp.response);
   3802   c->stage = mhd_HTTP_STAGE_START_REPLY;
   3803   return true;
   3804 }
   3805 
   3806 
   3807 /**
   3808  * Send error reply when receive buffer space exhausted while receiving
   3809  * the chunk size line.
   3810  * @param c the connection to handle
   3811  * @param chunk_size_line the optional pointer to the partially received
   3812  *                        the current chunk size line.
   3813  *                        Could be not zero-terminated and can contain binary
   3814  *                        zeros.
   3815  *                        Can be NULL.
   3816  * @param chunk_size_line_size the size of the @a chunk_size_line
   3817  */
   3818 static void
   3819 handle_req_chunk_size_line_no_space (struct MHD_Connection *c,
   3820                                      const char *chunk_size_line,
   3821                                      size_t chunk_size_line_size)
   3822 {
   3823   unsigned int err_code;
   3824 
   3825   if (NULL != chunk_size_line)
   3826   {
   3827     const char *semicol;
   3828     /* Check for chunk extension */
   3829     semicol = (const char *)
   3830               memchr (chunk_size_line,
   3831                       ';',
   3832                       chunk_size_line_size);
   3833     if (NULL != semicol)
   3834     { /* Chunk extension present. It could be removed without any loss of the
   3835          details of the request. */
   3836       mhd_RESPOND_WITH_ERROR_STATIC (c,
   3837                                      MHD_HTTP_STATUS_CONTENT_TOO_LARGE,
   3838                                      ERR_RSP_REQUEST_CHUNK_LINE_EXT_TOO_BIG);
   3839       return;
   3840     }
   3841   }
   3842   err_code = mhd_stream_get_no_space_err_status_code (c,
   3843                                                       MHD_PROC_RECV_BODY_CHUNKED,
   3844                                                       chunk_size_line_size,
   3845                                                       chunk_size_line);
   3846   mhd_RESPOND_WITH_ERROR_STATIC (c,
   3847                                  err_code,
   3848                                  ERR_RSP_REQUEST_CHUNK_LINE_TOO_BIG);
   3849 }
   3850 
   3851 
   3852 /**
   3853  * Handle situation with read buffer exhaustion.
   3854  * Must be called when no more space left in the read buffer, no more
   3855  * space left in the memory pool to grow the read buffer, but more data
   3856  * need to be received from the client.
   3857  * Could be called when the result of received data processing cannot be
   3858  * stored in the memory pool (like some header).
   3859  * @param c the connection to process
   3860  * @param stage the receive stage where the exhaustion happens.
   3861  * @return 'true' if connection should NOT be closed,
   3862  *         'false' if connection is closing
   3863  */
   3864 static MHD_FN_PAR_NONNULL_ALL_ bool
   3865 handle_recv_no_space (struct MHD_Connection *c,
   3866                       enum MHD_ProcRecvDataStage stage)
   3867 {
   3868   mhd_assert (MHD_PROC_RECV_INIT <= stage);
   3869   mhd_assert (MHD_PROC_RECV_FOOTERS >= stage);
   3870   mhd_assert (mhd_HTTP_STAGE_FULL_REQ_RECEIVED > c->stage);
   3871   mhd_assert ((MHD_PROC_RECV_INIT != stage) || \
   3872               (mhd_HTTP_STAGE_INIT == c->stage));
   3873   mhd_assert ((MHD_PROC_RECV_METHOD != stage) || \
   3874               (mhd_HTTP_STAGE_REQ_LINE_RECEIVING == c->stage));
   3875   mhd_assert ((MHD_PROC_RECV_URI != stage) || \
   3876               (mhd_HTTP_STAGE_REQ_LINE_RECEIVING == c->stage));
   3877   mhd_assert ((MHD_PROC_RECV_HTTPVER != stage) || \
   3878               (mhd_HTTP_STAGE_REQ_LINE_RECEIVING == c->stage));
   3879   mhd_assert ((MHD_PROC_RECV_HEADERS != stage) || \
   3880               (mhd_HTTP_STAGE_REQ_HEADERS_RECEIVING == c->stage));
   3881   mhd_assert (MHD_PROC_RECV_COOKIE != stage); /* handle_req_cookie_no_space() must be called directly */
   3882   mhd_assert ((MHD_PROC_RECV_BODY_NORMAL != stage) || \
   3883               (mhd_HTTP_STAGE_BODY_RECEIVING == c->stage));
   3884   mhd_assert ((MHD_PROC_RECV_BODY_CHUNKED != stage) || \
   3885               (mhd_HTTP_STAGE_BODY_RECEIVING == c->stage));
   3886   mhd_assert ((MHD_PROC_RECV_FOOTERS != stage) || \
   3887               (mhd_HTTP_STAGE_FOOTERS_RECEIVING == c->stage));
   3888   mhd_assert ((MHD_PROC_RECV_BODY_NORMAL != stage) || \
   3889               (! c->rq.have_chunked_upload));
   3890   mhd_assert ((MHD_PROC_RECV_BODY_CHUNKED != stage) || \
   3891               (c->rq.have_chunked_upload));
   3892   switch (stage)
   3893   {
   3894   case MHD_PROC_RECV_INIT:
   3895   case MHD_PROC_RECV_METHOD:
   3896     /* Some data has been received, but it is not clear yet whether
   3897      * the received data is an valid HTTP request */
   3898     mhd_STREAM_ABORT (c, mhd_CONN_CLOSE_NO_POOL_MEM_FOR_REQUEST, \
   3899                       "No space left in the read buffer when " \
   3900                       "receiving the initial part of " \
   3901                       "the request line.");
   3902     return false;
   3903   case MHD_PROC_RECV_URI:
   3904   case MHD_PROC_RECV_HTTPVER:
   3905     /* Some data has been received, but the request line is incomplete */
   3906     mhd_assert (mhd_HTTP_METHOD_NO_METHOD != c->rq.http_mthd);
   3907     mhd_assert (MHD_HTTP_VERSION_INVALID == c->rq.http_ver);
   3908     /* A quick simple check whether the incomplete line looks
   3909      * like an HTTP request */
   3910     if ((mhd_HTTP_METHOD_GET <= c->rq.http_mthd) &&
   3911         (mhd_HTTP_METHOD_DELETE >= c->rq.http_mthd))
   3912     {
   3913       mhd_RESPOND_WITH_ERROR_STATIC (c,
   3914                                      MHD_HTTP_STATUS_URI_TOO_LONG,
   3915                                      ERR_RSP_MSG_REQUEST_TOO_BIG);
   3916       return true;
   3917     }
   3918     mhd_STREAM_ABORT (c, mhd_CONN_CLOSE_NO_POOL_MEM_FOR_REQUEST, \
   3919                       "No space left in the read buffer when " \
   3920                       "receiving the URI in " \
   3921                       "the request line. " \
   3922                       "The request uses non-standard HTTP request " \
   3923                       "method token.");
   3924     return false;
   3925   case MHD_PROC_RECV_HEADERS:
   3926     handle_req_headers_no_space (c, c->read_buffer, c->read_buffer_offset);
   3927     return true;
   3928   case MHD_PROC_RECV_BODY_NORMAL:
   3929     /* A header probably has been added to a suspended connection and
   3930        it took precisely all the space in the buffer.
   3931        Very low probability. */
   3932     mhd_assert (! c->rq.have_chunked_upload);
   3933     handle_req_headers_no_space (c, NULL, 0); // FIXME: check
   3934     return true;
   3935   case MHD_PROC_RECV_BODY_CHUNKED:
   3936     mhd_assert (c->rq.have_chunked_upload);
   3937     if (c->rq.current_chunk_offset != c->rq.current_chunk_size)
   3938     { /* Receiving content of the chunk */
   3939       /* A header probably has been added to a suspended connection and
   3940          it took precisely all the space in the buffer.
   3941          Very low probability. */
   3942       handle_req_headers_no_space (c, NULL, 0);  // FIXME: check
   3943     }
   3944     else
   3945     {
   3946       if (0 != c->rq.current_chunk_size)
   3947       { /* Waiting for chunk-closing CRLF */
   3948         /* Not really possible as some payload should be
   3949            processed and the space used by payload should be available. */
   3950         handle_req_headers_no_space (c, NULL, 0);  // FIXME: check
   3951       }
   3952       else
   3953       { /* Reading the line with the chunk size */
   3954         handle_req_chunk_size_line_no_space (c,
   3955                                              c->read_buffer,
   3956                                              c->read_buffer_offset);
   3957       }
   3958     }
   3959     return true;
   3960   case MHD_PROC_RECV_FOOTERS:
   3961     handle_req_footers_no_space (c, c->read_buffer, c->read_buffer_offset);
   3962     return true;
   3963   /* The next cases should not be possible */
   3964   case MHD_PROC_RECV_COOKIE:
   3965   default:
   3966     break;
   3967   }
   3968   mhd_UNREACHABLE ();
   3969   return false;
   3970 }
   3971 
   3972 
   3973 /**
   3974  * Try growing the read buffer.  We initially claim half the available
   3975  * buffer space for the read buffer (the other half being left for
   3976  * management data structures; the write buffer can in the end take
   3977  * virtually everything as the read buffer can be reduced to the
   3978  * minimum necessary at that point.
   3979  *
   3980  * @param connection the connection
   3981  * @param required set to 'true' if grow is required, i.e. connection
   3982  *                 will fail if no additional space is granted
   3983  * @return 'true' on success, 'false' on failure
   3984  */
   3985 static MHD_FN_PAR_NONNULL_ALL_ bool
   3986 try_grow_read_buffer (struct MHD_Connection *restrict connection,
   3987                       bool required)
   3988 {
   3989   size_t new_size;
   3990   size_t avail_size;
   3991   const size_t def_grow_size = 1536; // TODO: remove hardcoded increment
   3992   char *rb;
   3993 
   3994   avail_size = mhd_pool_get_free (connection->pool);
   3995   if (0 == avail_size)
   3996     return false;               /* No more space available */
   3997   if (0 == connection->read_buffer_size)
   3998     new_size = avail_size / 2;  /* Use half of available buffer for reading */
   3999   else
   4000   {
   4001     size_t grow_size;
   4002 
   4003     grow_size = avail_size / 8;
   4004     if (def_grow_size > grow_size)
   4005     {                  /* Shortage of space */
   4006       const size_t left_free =
   4007         connection->read_buffer_size - connection->read_buffer_offset;
   4008       mhd_assert (connection->read_buffer_size >= \
   4009                   connection->read_buffer_offset);
   4010       if ((def_grow_size <= grow_size + left_free)
   4011           && (left_free < def_grow_size))
   4012         grow_size = def_grow_size - left_free;  /* Use precise 'def_grow_size' for new free space */
   4013       else if (! required)
   4014         return false;                           /* Grow is not mandatory, leave some space in pool */
   4015       else
   4016       {
   4017         /* Shortage of space, but grow is mandatory */
   4018         const size_t small_inc =
   4019           ((mhd_BUF_INC_SIZE > def_grow_size) ?
   4020            def_grow_size : mhd_BUF_INC_SIZE) / 8;
   4021         if (small_inc < avail_size)
   4022           grow_size = small_inc;
   4023         else
   4024           grow_size = avail_size;
   4025       }
   4026     }
   4027     new_size = connection->read_buffer_size + grow_size;
   4028   }
   4029   /* Make sure that read buffer will not be moved */
   4030   if ((NULL != connection->read_buffer) &&
   4031       ! mhd_pool_is_resizable_inplace (connection->pool,
   4032                                        connection->read_buffer,
   4033                                        connection->read_buffer_size))
   4034   {
   4035     mhd_assert (0);
   4036     return false;
   4037   }
   4038   /* we can actually grow the buffer, do it! */
   4039   rb = (char *) mhd_pool_reallocate (connection->pool,
   4040                                      connection->read_buffer,
   4041                                      connection->read_buffer_size,
   4042                                      new_size);
   4043   if (NULL == rb)
   4044   {
   4045     /* This should NOT be possible: we just computed 'new_size' so that
   4046        it should fit. If it happens, somehow our read buffer is not in
   4047        the right position in the pool, say because someone called
   4048        mhd_pool_allocate() without 'from_end' set to 'true'? Anyway,
   4049        should be investigated! (Ideally provide all data from
   4050        *pool and connection->read_buffer and new_size for debugging). */
   4051     mhd_assert (0);
   4052     return false;
   4053   }
   4054   mhd_assert (connection->read_buffer == rb);
   4055   connection->read_buffer = rb;
   4056   mhd_assert (NULL != connection->read_buffer);
   4057   connection->read_buffer_size = new_size;
   4058   return true;
   4059 }
   4060 
   4061 
   4062 MHD_INTERNAL MHD_FN_PAR_NONNULL_ALL_ enum mhd_ConnectionBufferGrowResult
   4063 mhd_stream_check_and_grow_read_buffer_space (struct MHD_Connection *restrict c)
   4064 {
   4065   enum MHD_ProcRecvDataStage stage;
   4066   bool res;
   4067   /**
   4068    * The increase of read buffer size is desirable.
   4069    */
   4070   bool rbuff_grow_desired;
   4071   /**
   4072    * The increase of read buffer size is a hard requirement.
   4073    */
   4074   bool rbuff_grow_required;
   4075 
   4076   mhd_assert (0 != (MHD_EVENT_LOOP_INFO_RECV & c->event_loop_info));
   4077   mhd_assert (! c->discard_request);
   4078 
   4079   rbuff_grow_required = (c->read_buffer_offset == c->read_buffer_size);
   4080   if (rbuff_grow_required)
   4081     rbuff_grow_desired = true;
   4082   else
   4083   {
   4084     rbuff_grow_desired = (c->read_buffer_offset + 1536 > // TODO: remove handcoded buffer grow size
   4085                           c->read_buffer_size);
   4086 
   4087     if ((rbuff_grow_desired) &&
   4088         (mhd_HTTP_STAGE_BODY_RECEIVING == c->stage))
   4089     {
   4090       if (! c->rq.have_chunked_upload)
   4091       {
   4092         mhd_assert (MHD_SIZE_UNKNOWN != c->rq.cntn.cntn_size);
   4093         /* Do not grow read buffer more than necessary to process the current
   4094            request. */
   4095         rbuff_grow_desired =
   4096           (c->rq.cntn.cntn_size - c->rq.cntn.recv_size > c->read_buffer_size); // FIXME
   4097       }
   4098       else
   4099       {
   4100         mhd_assert (MHD_SIZE_UNKNOWN == c->rq.cntn.cntn_size);
   4101         if (0 == c->rq.current_chunk_size)
   4102           rbuff_grow_desired =  /* Reading value of the next chunk size */
   4103                                (MHD_CHUNK_HEADER_REASONABLE_LEN >
   4104                                 c->read_buffer_size);
   4105         else
   4106         {
   4107           const uint_fast64_t cur_chunk_left =
   4108             c->rq.current_chunk_size - c->rq.current_chunk_offset;
   4109           /* Do not grow read buffer more than necessary to process the current
   4110              chunk with terminating CRLF. */
   4111           mhd_assert (c->rq.current_chunk_offset <= c->rq.current_chunk_size);
   4112           rbuff_grow_desired =
   4113             ((cur_chunk_left + 2) > (uint_fast64_t) (c->read_buffer_size));
   4114         }
   4115       }
   4116     }
   4117   }
   4118 
   4119   if (! rbuff_grow_desired)
   4120     return mhd_CONN_BUFF_GROW_OK; /* No need to increase the buffer */
   4121 
   4122   if (try_grow_read_buffer (c, rbuff_grow_required))
   4123     return mhd_CONN_BUFF_GROW_OK; /* Buffer increase succeed */
   4124 
   4125   if (! rbuff_grow_required)
   4126     return mhd_CONN_BUFF_GROW_OK; /* Can continue without buffer increase */
   4127 
   4128   /* Failed to increase the read buffer size, but need to read the data
   4129      from the network.
   4130      No more space left in the buffer, no more space to increase the buffer. */
   4131 
   4132   switch (c->stage)
   4133   {
   4134   case mhd_HTTP_STAGE_INIT:
   4135     stage = MHD_PROC_RECV_INIT;
   4136     break;
   4137   case mhd_HTTP_STAGE_REQ_LINE_RECEIVING:
   4138     if (mhd_HTTP_METHOD_NO_METHOD == c->rq.http_mthd)
   4139       stage = MHD_PROC_RECV_METHOD;
   4140     else if (0 == c->rq.req_target_len)
   4141       stage = MHD_PROC_RECV_URI;
   4142     else
   4143       stage = MHD_PROC_RECV_HTTPVER;
   4144     break;
   4145   case mhd_HTTP_STAGE_REQ_HEADERS_RECEIVING:
   4146     stage = MHD_PROC_RECV_HEADERS;
   4147     break;
   4148   case mhd_HTTP_STAGE_BODY_RECEIVING:
   4149     stage = c->rq.have_chunked_upload ?
   4150             MHD_PROC_RECV_BODY_CHUNKED : MHD_PROC_RECV_BODY_NORMAL;
   4151     break;
   4152   case mhd_HTTP_STAGE_FOOTERS_RECEIVING:
   4153     stage = MHD_PROC_RECV_FOOTERS;
   4154     break;
   4155   case mhd_HTTP_STAGE_REQ_LINE_RECEIVED:
   4156   case mhd_HTTP_STAGE_HEADERS_RECEIVED:
   4157   case mhd_HTTP_STAGE_HEADERS_PROCESSED:
   4158   case mhd_HTTP_STAGE_CONTINUE_SENDING:
   4159   case mhd_HTTP_STAGE_BODY_RECEIVED:
   4160   case mhd_HTTP_STAGE_FOOTERS_RECEIVED:
   4161   case mhd_HTTP_STAGE_FULL_REQ_RECEIVED:
   4162   case mhd_HTTP_STAGE_REQ_RECV_FINISHED:
   4163   case mhd_HTTP_STAGE_START_REPLY:
   4164   case mhd_HTTP_STAGE_HEADERS_SENDING:
   4165   case mhd_HTTP_STAGE_HEADERS_SENT:
   4166   case mhd_HTTP_STAGE_UNCHUNKED_BODY_UNREADY:
   4167   case mhd_HTTP_STAGE_UNCHUNKED_BODY_READY:
   4168   case mhd_HTTP_STAGE_CHUNKED_BODY_UNREADY:
   4169   case mhd_HTTP_STAGE_CHUNKED_BODY_READY:
   4170   case mhd_HTTP_STAGE_CHUNKED_BODY_SENT:
   4171   case mhd_HTTP_STAGE_FOOTERS_SENDING:
   4172   case mhd_HTTP_STAGE_FULL_REPLY_SENT:
   4173   case mhd_HTTP_STAGE_PRE_CLOSING:
   4174   case mhd_HTTP_STAGE_CLOSED:
   4175 #ifdef MHD_SUPPORT_UPGRADE
   4176   case mhd_HTTP_STAGE_UPGRADE_HEADERS_SENDING:
   4177   case mhd_HTTP_STAGE_UPGRADING:
   4178   case mhd_HTTP_STAGE_UPGRADED:
   4179   case mhd_HTTP_STAGE_UPGRADED_CLEANING:
   4180 #endif /* MHD_SUPPORT_UPGRADE */
   4181   default:
   4182     mhd_UNREACHABLE ();
   4183     stage = MHD_PROC_RECV_BODY_NORMAL;
   4184     break;
   4185   }
   4186 
   4187   res = handle_recv_no_space (c, stage);
   4188 
   4189   mhd_assert (! res || ! c->dbg.closing_started);
   4190   mhd_assert (res || c->dbg.closing_started);
   4191 
   4192   return
   4193     res ? mhd_CONN_BUFF_GROW_ERR_REPLY : mhd_CONN_BUFF_GROW_ERR_CONN_CLOSE;
   4194 }