aboutsummaryrefslogtreecommitdiff
path: root/src/microhttpd/connection.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/microhttpd/connection.c')
-rw-r--r--src/microhttpd/connection.c818
1 files changed, 728 insertions, 90 deletions
diff --git a/src/microhttpd/connection.c b/src/microhttpd/connection.c
index 0adc15a7..83615eb4 100644
--- a/src/microhttpd/connection.c
+++ b/src/microhttpd/connection.c
@@ -1,7 +1,7 @@
1/* 1/*
2 This file is part of libmicrohttpd 2 This file is part of libmicrohttpd
3 Copyright (C) 2007-2020 Daniel Pittman and Christian Grothoff 3 Copyright (C) 2007-2020 Daniel Pittman and Christian Grothoff
4 Copyright (C) 2015-2022 Evgeny Grin (Karlson2k) 4 Copyright (C) 2015-2023 Evgeny Grin (Karlson2k)
5 5
6 This library is free software; you can redistribute it and/or 6 This library is free software; you can redistribute it and/or
7 modify it under the terms of the GNU Lesser General Public 7 modify it under the terms of the GNU Lesser General Public
@@ -76,14 +76,105 @@
76 * be processed. 76 * be processed.
77 */ 77 */
78#ifdef HAVE_MESSAGES 78#ifdef HAVE_MESSAGES
79#define REQUEST_TOO_BIG \ 79#define ERR_MSG_REQUEST_TOO_BIG \
80 "<html>" \ 80 "<html>" \
81 "<head><title>Request too big</title></head>" \ 81 "<head><title>Request too big</title></head>" \
82 "<body>Request HTTP header is too big for the memory constraints " \ 82 "<body>Request HTTP header is too big for the memory constraints " \
83 "of this webserver.</body>" \ 83 "of this webserver.</body>" \
84 "</html>" 84 "</html>"
85#else 85#else
86#define REQUEST_TOO_BIG "" 86#define ERR_MSG_REQUEST_TOO_BIG ""
87#endif
88
89/**
90 * Response text used when the request header is too big to be processed.
91 */
92#ifdef HAVE_MESSAGES
93#define ERR_MSG_REQUEST_HEADER_TOO_BIG \
94 "<html>" \
95 "<head><title>Request too big</title></head>" \
96 "<body><p>The total size of the request headers, which includes the " \
97 "request target and the request field lines, exceeds the memory " \
98 "constraints of this web server.</p>" \
99 "<p>The request could be re-tried with shorter field lines, a shorter "\
100 "request target or a shorter request method token.</p></body>" \
101 "</html>"
102#else
103#define ERR_MSG_REQUEST_HEADER_TOO_BIG ""
104#endif
105
106/**
107 * Response text used when the request cookie header is too big to be processed.
108 */
109#ifdef HAVE_MESSAGES
110#define ERR_MSG_REQUEST_HEADER_WITH_COOKIES_TOO_BIG \
111 "<html>" \
112 "<head><title>Request too big</title></head>" \
113 "<body><p>The total size of the request headers, which includes the " \
114 "request target and the request field lines, exceeds the memory " \
115 "constraints of this web server.</p> "\
116 "<p>The request could be re-tried with smaller " \
117 "<b>&quot;Cookie:&quot;</b> field value, shorter other field lines, " \
118 "a shorter request target or a shorter request method token.</p></body> " \
119 "</html>"
120#else
121#define ERR_MSG_REQUEST_HEADER_WITH_COOKIES_TOO_BIG ""
122#endif
123
124/**
125 * Response text used when the request chunk size line with chunk extension
126 * cannot fit the buffer.
127 */
128#ifdef HAVE_MESSAGES
129#define ERR_MSG_REQUEST_CHUNK_LINE_EXT_TOO_BIG \
130 "<html>" \
131 "<head><title>Request too big</title></head>" \
132 "<body><p>The total size of the request target, the request field lines " \
133 "and the chunk size line exceeds the memory constraints of this web " \
134 "server.</p>" \
135 "<p>The request could be re-tried without chunk extensions, with a smaller " \
136 "chunk size, shorter field lines, a shorter request target or a shorter " \
137 "request method token.</p></body>" \
138 "</html>"
139#else
140#define ERR_MSG_REQUEST_CHUNK_LINE_EXT_TOO_BIG ""
141#endif
142
143/**
144 * Response text used when the request chunk size line without chunk extension
145 * cannot fit the buffer.
146 */
147#ifdef HAVE_MESSAGES
148#define ERR_MSG_REQUEST_CHUNK_LINE_TOO_BIG \
149 "<html>" \
150 "<head><title>Request too big</title></head>" \
151 "<body><p>The total size of the request target, the request field lines " \
152 "and the chunk size line exceeds the memory constraints of this web " \
153 "server.</p>" \
154 "<p>The request could be re-tried with a smaller " \
155 "chunk size, shorter field lines, a shorter request target or a shorter " \
156 "request method token.</p></body>" \
157 "</html>"
158#else
159#define ERR_MSG_REQUEST_CHUNK_LINE_TOO_BIG ""
160#endif
161
162/**
163 * Response text used when the request header is too big to be processed.
164 */
165#ifdef HAVE_MESSAGES
166#define ERR_MSG_REQUEST_FOOTER_TOO_BIG \
167 "<html>" \
168 "<head><title>Request too big</title></head>" \
169 "<body><p>The total size of the request headers, which includes the " \
170 "request target, the request field lines and the chunked trailer " \
171 "section exceeds the memory constraints of this web server.</p>" \
172 "<p>The request could be re-tried with a shorter chunked trailer " \
173 "section, shorter field lines, a shorter request target or " \
174 "a shorter request method token.</p></body>" \
175 "</html>"
176#else
177#define ERR_MSG_REQUEST_FOOTER_TOO_BIG ""
87#endif 178#endif
88 179
89/** 180/**
@@ -2860,13 +2951,579 @@ has_unprocessed_upload_body_data_in_buffer (struct MHD_Connection *c)
2860 { 2951 {
2861 /* 0 == c->rq.current_chunk_size: Waiting the chunk size (chunk header). 2952 /* 0 == c->rq.current_chunk_size: Waiting the chunk size (chunk header).
2862 0 != c->rq.current_chunk_size: Waiting for chunk-closing CRLF. */ 2953 0 != c->rq.current_chunk_size: Waiting for chunk-closing CRLF. */
2863 return false; /* */ 2954 return false;
2864 } 2955 }
2865 return 0 != c->read_buffer_offset; /* Chunk payload data in the read buffer */ 2956 return 0 != c->read_buffer_offset; /* Chunk payload data in the read buffer */
2866} 2957}
2867 2958
2868 2959
2869/** 2960/**
2961 * The stage of input data processing.
2962 * Used for out-of-memory (in the pool) handling.
2963 */
2964enum MHD_ProcRecvDataStage
2965{
2966 MHD_PROC_RECV_INIT, /**< No data HTTP request data have been processed yet */
2967 MHD_PROC_RECV_METHOD, /**< Processing/receiving the request HTTP method */
2968 MHD_PROC_RECV_URI, /**< Processing/receiving the request URI */
2969 MHD_PROC_RECV_HTTPVER, /**< Processing/receiving the request HTTP version string */
2970 MHD_PROC_RECV_HEADERS, /**< Processing/receiving the request HTTP headers */
2971 MHD_PROC_RECV_COOKIE, /**< Processing the received request cookie header */
2972 MHD_PROC_RECV_BODY_NORMAL, /**< Processing/receiving the request non-chunked body */
2973 MHD_PROC_RECV_BODY_CHUNKED,/**< Processing/receiving the request chunked body */
2974 MHD_PROC_RECV_FOOTERS /**< Processing/receiving the request footers */
2975};
2976
2977
2978#ifndef MHD_MAX_REASONABLE_HEADERS_SIZE_
2979/**
2980 * A reasonable headers size (excluding request line) that should be sufficient
2981 * for most requests.
2982 * If incoming data buffer free space is not enough to process the complete
2983 * header (the request line and all headers) and the headers size is larger than
2984 * this size then the status code 431 "Request Header Fields Too Large" is
2985 * returned to the client.
2986 * The larger headers are processed by MHD if enough space is available.
2987 */
2988# define MHD_MAX_REASONABLE_HEADERS_SIZE_ (6 * 1024)
2989#endif /* ! MHD_MAX_REASONABLE_HEADERS_SIZE_ */
2990
2991#ifndef MHD_MAX_REASONABLE_REQ_TARGET_SIZE_
2992/**
2993 * A reasonable request target (the request URI) size that should be sufficient
2994 * for most requests.
2995 * If incoming data buffer free space is not enough to process the complete
2996 * header (the request line and all headers) and the request target size is
2997 * larger than this size then the status code 414 "URI Too Long" is
2998 * returned to the client.
2999 * The larger request targets are processed by MHD if enough space is available.
3000 * The value chosen according to RFC 9112 Section 3, paragraph 5
3001 */
3002# define MHD_MAX_REASONABLE_REQ_TARGET_SIZE_ 8000
3003#endif /* ! MHD_MAX_REASONABLE_REQ_TARGET_SIZE_ */
3004
3005#ifndef MHD_MIN_REASONABLE_HEADERS_SIZE_
3006/**
3007 * A reasonable headers size (excluding request line) that should be sufficient
3008 * for basic simple requests.
3009 * When no space left in the receiving buffer try to avoid replying with
3010 * the status code 431 "Request Header Fields Too Large" if headers size
3011 * is smaller then this value.
3012 */
3013# define MHD_MIN_REASONABLE_HEADERS_SIZE_ 26
3014#endif /* ! MHD_MIN_REASONABLE_HEADERS_SIZE_ */
3015
3016#ifndef MHD_MIN_REASONABLE_REQ_TARGET_SIZE_
3017/**
3018 * A reasonable request target (the request URI) size that should be sufficient
3019 * for basic simple requests.
3020 * When no space left in the receiving buffer try to avoid replying with
3021 * the status code 414 "URI Too Long" if the request target size is smaller then
3022 * this value.
3023 */
3024# define MHD_MIN_REASONABLE_REQ_TARGET_SIZE_ 40
3025#endif /* ! MHD_MIN_REASONABLE_REQ_TARGET_SIZE_ */
3026
3027#ifndef MHD_MIN_REASONABLE_REQ_METHOD_SIZE_
3028/**
3029 * A reasonable request method string size that should be sufficient
3030 * for basic simple requests.
3031 * When no space left in the receiving buffer try to avoid replying with
3032 * the status code 501 "Not Implemented" if the request method size is
3033 * smaller then this value.
3034 */
3035# define MHD_MIN_REASONABLE_REQ_METHOD_SIZE_ 16
3036#endif /* ! MHD_MIN_REASONABLE_REQ_METHOD_SIZE_ */
3037
3038#ifndef MHD_MIN_REASONABLE_REQ_CHUNK_LINE_LENGTH_
3039/**
3040 * A reasonable minimal chunk line length.
3041 * When no space left in the receiving buffer reply with 413 "Content Too Large"
3042 * if the chunk line length is larger than this value.
3043 */
3044# define MHD_MIN_REASONABLE_REQ_CHUNK_LINE_LENGTH_ 4
3045#endif /* ! MHD_MIN_REASONABLE_REQ_CHUNK_LINE_LENGTH_ */
3046
3047
3048/**
3049 * Select the HTTP error status code for "out of receive buffer space" error.
3050 * @param c the connection to process
3051 * @param stage the current stage of request receiving
3052 * @param add_element the optional pointer to the element failed to be processed
3053 * or added, the meaning of the element depends on
3054 * the @a stage. Could be not zero-terminated and can
3055 * contain binary zeros. Can be NULL.
3056 * @param add_element_size the size of the @a add_element
3057 * @return the HTTP error code to use in the error reply
3058 */
3059static unsigned int
3060get_no_space_err_status_code (struct MHD_Connection *c,
3061 enum MHD_ProcRecvDataStage stage,
3062 const char *add_element,
3063 size_t add_element_size)
3064{
3065 size_t method_size;
3066 size_t uri_size;
3067 size_t opt_headers_size;
3068 size_t host_field_line_size;
3069
3070 mhd_assert (MHD_CONNECTION_REQ_LINE_RECEIVED < c->state);
3071 mhd_assert (MHD_PROC_RECV_HEADERS <= stage);
3072 mhd_assert ((0 == add_element_size) || (NULL != add_element));
3073
3074 if (MHD_CONNECTION_HEADERS_RECEIVED > c->state)
3075 {
3076 mhd_assert (NULL != c->rq.field_lines.start);
3077 opt_headers_size =
3078 (size_t) ((c->read_buffer + c->read_buffer_offset)
3079 - c->rq.field_lines.start);
3080 }
3081 else
3082 opt_headers_size = c->rq.field_lines.size;
3083
3084 /* The read buffer is fully used by the request line, the field lines
3085 (headers) and internal information.
3086 The return status code works as a suggestion for the client to reduce
3087 one of the request elements. */
3088
3089 if ((MHD_PROC_RECV_BODY_CHUNKED == stage) &&
3090 (MHD_MIN_REASONABLE_REQ_CHUNK_LINE_LENGTH_ < add_element_size))
3091 {
3092 /* Request could be re-tried easily with smaller chunk sizes */
3093 return MHD_HTTP_CONTENT_TOO_LARGE;
3094 }
3095
3096 host_field_line_size = 0;
3097 /* The "Host:" field line is mandatory.
3098 The total size of the field lines (headers) cannot be smaller than
3099 the size of the "Host:" field line. */
3100 if ((MHD_PROC_RECV_HEADERS == stage)
3101 && (0 != add_element_size))
3102 {
3103 static const size_t header_host_key_len =
3104 MHD_STATICSTR_LEN_ (MHD_HTTP_HEADER_HOST);
3105 const bool is_host_header =
3106 (header_host_key_len + 1 <= add_element_size)
3107 && ( (0 == add_element[header_host_key_len])
3108 || (':' == add_element[header_host_key_len]) )
3109 && MHD_str_equal_caseless_bin_n_ (MHD_HTTP_HEADER_HOST,
3110 add_element,
3111 header_host_key_len);
3112 if (is_host_header)
3113 {
3114 const bool is_parsed = ! (
3115 (MHD_CONNECTION_HEADERS_RECEIVED > c->state) &&
3116 (add_element_size == c->read_buffer_offset) &&
3117 (c->read_buffer == add_element) );
3118 size_t actual_element_size;
3119
3120 mhd_assert (! is_parsed || (0 == add_element[header_host_key_len]));
3121 /* The actual size should be larger due to CRLF or LF chars,
3122 however the exact termination sequence is not known here and
3123 as perfect precision is not required, to simplify the code
3124 assume the minimal length. */
3125 if (is_parsed)
3126 actual_element_size = add_element_size + 1; /* "1" for LF */
3127 else
3128 actual_element_size = add_element_size;
3129
3130 host_field_line_size = actual_element_size;
3131 mhd_assert (opt_headers_size >= actual_element_size);
3132 opt_headers_size -= actual_element_size;
3133 }
3134 }
3135 if (0 == host_field_line_size)
3136 {
3137 static const size_t host_field_name_len =
3138 MHD_STATICSTR_LEN_ (MHD_HTTP_HEADER_HOST);
3139 size_t host_field_name_value_len;
3140 if (MHD_NO != MHD_lookup_connection_value_n (c,
3141 MHD_HEADER_KIND,
3142 MHD_HTTP_HEADER_HOST,
3143 host_field_name_len,
3144 NULL,
3145 &host_field_name_value_len))
3146 {
3147 /* Calculate the minimal size of the field line: no space between
3148 colon and the field value, line terminated by LR */
3149 host_field_line_size =
3150 host_field_name_len + host_field_name_value_len + 2; /* "2" for ':' and LF */
3151
3152 /* The "Host:" field could be added by application */
3153 if (opt_headers_size >= host_field_line_size)
3154 {
3155 opt_headers_size -= host_field_line_size;
3156 /* Take into account typical space after colon and CR at the end of the line */
3157 if (opt_headers_size >= 2)
3158 opt_headers_size -= 2;
3159 }
3160 else
3161 host_field_line_size = 0; /* No "Host:" field line set by the client */
3162 }
3163 }
3164
3165 uri_size = c->rq.req_target_len;
3166 if (MHD_HTTP_MTHD_OTHER != c->rq.http_mthd)
3167 method_size = 0; /* Do not recommend shorter request method */
3168 else
3169 {
3170 mhd_assert (NULL != c->rq.method);
3171 method_size = strlen (c->rq.method);
3172 }
3173
3174 if ((size_t) MHD_MAX_REASONABLE_HEADERS_SIZE_ < opt_headers_size)
3175 {
3176 /* Typically the easiest way to reduce request header size is
3177 a removal of some optional headers. */
3178 if (opt_headers_size > (uri_size / 8))
3179 {
3180 if ((opt_headers_size / 2) > method_size)
3181 return MHD_HTTP_REQUEST_HEADER_FIELDS_TOO_LARGE;
3182 else
3183 return MHD_HTTP_NOT_IMPLEMENTED; /* The length of the HTTP request method is unreasonably large */
3184 }
3185 else
3186 { /* Request target is MUCH larger than headers */
3187 if ((uri_size / 16) > method_size)
3188 return MHD_HTTP_URI_TOO_LONG;
3189 else
3190 return MHD_HTTP_NOT_IMPLEMENTED; /* The length of the HTTP request method is unreasonably large */
3191 }
3192 }
3193 if ((size_t) MHD_MAX_REASONABLE_REQ_TARGET_SIZE_ < uri_size)
3194 {
3195 /* If request target size if larger than maximum reasonable size
3196 recommend client to reduce the request target size (length). */
3197 if ((uri_size / 16) > method_size)
3198 return MHD_HTTP_URI_TOO_LONG; /* Request target is MUCH larger than headers */
3199 else
3200 return MHD_HTTP_NOT_IMPLEMENTED; /* The length of the HTTP request method is unreasonably large */
3201 }
3202
3203 /* The read buffer is too small to handle reasonably large requests */
3204
3205 if ((size_t) MHD_MIN_REASONABLE_HEADERS_SIZE_ < opt_headers_size)
3206 {
3207 /* Recommend application to retry with minimal headers */
3208 if ((opt_headers_size * 4) > uri_size)
3209 {
3210 if (opt_headers_size > method_size)
3211 return MHD_HTTP_REQUEST_HEADER_FIELDS_TOO_LARGE;
3212 else
3213 return MHD_HTTP_NOT_IMPLEMENTED; /* The length of the HTTP request method is unreasonably large */
3214 }
3215 else
3216 { /* Request target is significantly larger than headers */
3217 if (uri_size > method_size * 4)
3218 return MHD_HTTP_URI_TOO_LONG;
3219 else
3220 return MHD_HTTP_NOT_IMPLEMENTED; /* The length of the HTTP request method is unreasonably large */
3221 }
3222 }
3223 if ((size_t) MHD_MIN_REASONABLE_REQ_TARGET_SIZE_ < uri_size)
3224 {
3225 /* Recommend application to retry with a shorter request target */
3226 if (uri_size > method_size * 4)
3227 return MHD_HTTP_URI_TOO_LONG;
3228 else
3229 return MHD_HTTP_NOT_IMPLEMENTED; /* The length of the HTTP request method is unreasonably large */
3230 }
3231
3232 if ((size_t) MHD_MIN_REASONABLE_REQ_METHOD_SIZE_ < method_size)
3233 {
3234 /* The request target (URI) and headers are (reasonably) very small.
3235 Some non-standard long request method is used. */
3236 /* The last resort response as it means "the method is not supported
3237 by the server for any URI". */
3238 return MHD_HTTP_NOT_IMPLEMENTED;
3239 }
3240
3241 /* The almost impossible situation: all elements are small, but cannot
3242 fit the buffer. The application set the buffer size to
3243 critically low value? */
3244
3245 if ((1 < opt_headers_size) || (1 < uri_size))
3246 {
3247 if (opt_headers_size >= uri_size)
3248 return MHD_HTTP_REQUEST_HEADER_FIELDS_TOO_LARGE;
3249 else
3250 return MHD_HTTP_URI_TOO_LONG;
3251 }
3252
3253 /* Nothing to reduce in the request.
3254 Reply with some status. */
3255 if (0 != host_field_line_size)
3256 return MHD_HTTP_REQUEST_HEADER_FIELDS_TOO_LARGE;
3257
3258 return MHD_HTTP_URI_TOO_LONG;
3259}
3260
3261
3262/**
3263 * Send error reply when receive buffer space exhausted while receiving or
3264 * storing the request headers
3265 * @param c the connection to handle
3266 * @param add_header the optional pointer to the current header string being
3267 * processed or the header failed to be added.
3268 * Could be not zero-terminated and can contain binary zeros.
3269 * Can be NULL.
3270 * @param add_header_size the size of the @a add_header
3271 */
3272static void
3273handle_req_headers_no_space (struct MHD_Connection *c,
3274 const char *add_header,
3275 size_t add_header_size)
3276{
3277 unsigned int err_code;
3278
3279 err_code = get_no_space_err_status_code (c,
3280 MHD_PROC_RECV_HEADERS,
3281 add_header,
3282 add_header_size);
3283 transmit_error_response_static (c,
3284 err_code,
3285 ERR_MSG_REQUEST_HEADER_TOO_BIG);
3286}
3287
3288
3289#ifdef COOKIE_SUPPORT
3290/**
3291 * Send error reply when the pool has no space to store 'cookie' header
3292 * parsing results.
3293 * @param c the connection to handle
3294 */
3295static void
3296handle_req_cookie_no_space (struct MHD_Connection *c)
3297{
3298 unsigned int err_code;
3299
3300 err_code = get_no_space_err_status_code (c,
3301 MHD_PROC_RECV_COOKIE,
3302 NULL,
3303 0);
3304 transmit_error_response_static (c,
3305 err_code,
3306 ERR_MSG_REQUEST_HEADER_WITH_COOKIES_TOO_BIG);
3307}
3308
3309
3310#endif /* COOKIE_SUPPORT */
3311
3312
3313/**
3314 * Send error reply when receive buffer space exhausted while receiving
3315 * the chunk size line.
3316 * @param c the connection to handle
3317 * @param add_header the optional pointer to the partially received
3318 * the current chunk size line.
3319 * Could be not zero-terminated and can contain binary zeros.
3320 * Can be NULL.
3321 * @param add_header_size the size of the @a add_header
3322 */
3323static void
3324handle_req_chunk_size_line_no_space (struct MHD_Connection *c,
3325 const char *chunk_size_line,
3326 size_t chunk_size_line_size)
3327{
3328 unsigned int err_code;
3329
3330 if (NULL != chunk_size_line)
3331 {
3332 const char *semicol;
3333 /* Check for chunk extension */
3334 semicol = memchr (chunk_size_line, ';', chunk_size_line_size);
3335 if (NULL != semicol)
3336 { /* Chunk extension present. It could be removed without any loss of the
3337 details of the request. */
3338 transmit_error_response_static (c,
3339 MHD_HTTP_CONTENT_TOO_LARGE,
3340 ERR_MSG_REQUEST_CHUNK_LINE_EXT_TOO_BIG);
3341 }
3342 }
3343 err_code = get_no_space_err_status_code (c,
3344 MHD_PROC_RECV_BODY_CHUNKED,
3345 chunk_size_line,
3346 chunk_size_line_size);
3347 transmit_error_response_static (c,
3348 err_code,
3349 ERR_MSG_REQUEST_CHUNK_LINE_TOO_BIG);
3350}
3351
3352
3353/**
3354 * Send error reply when receive buffer space exhausted while receiving or
3355 * storing the request footers (for chunked requests).
3356 * @param c the connection to handle
3357 * @param add_footer the optional pointer to the current footer string being
3358 * processed or the footer failed to be added.
3359 * Could be not zero-terminated and can contain binary zeros.
3360 * Can be NULL.
3361 * @param add_footer_size the size of the @a add_footer
3362 */
3363static void
3364handle_req_footers_no_space (struct MHD_Connection *c,
3365 const char *add_footer,
3366 size_t add_footer_size)
3367{
3368 (void) add_footer; (void) add_footer_size; /* Unused */
3369 mhd_assert (c->rq.have_chunked_upload);
3370
3371 /* Footers should be optional */
3372 transmit_error_response_static (c,
3373 MHD_HTTP_REQUEST_HEADER_FIELDS_TOO_LARGE,
3374 ERR_MSG_REQUEST_FOOTER_TOO_BIG);
3375}
3376
3377
3378/**
3379 * Handle situation with read buffer exhaustion.
3380 * Must be called when no more space left in the read buffer, no more
3381 * space left in the memory pool to grow the read buffer, but more data
3382 * need to be received from the client.
3383 * Could be called when the result of received data processing cannot be
3384 * stored in the memory pool (like some header).
3385 * @param c the connection to process
3386 * @param stage the receive stage where the exhaustion happens.
3387 */
3388static void
3389handle_recv_no_space (struct MHD_Connection *c,
3390 enum MHD_ProcRecvDataStage stage)
3391{
3392 mhd_assert (MHD_PROC_RECV_INIT <= stage);
3393 mhd_assert (MHD_PROC_RECV_FOOTERS >= stage);
3394 mhd_assert (MHD_CONNECTION_FULL_REQ_RECEIVED > c->state);
3395 mhd_assert ((MHD_PROC_RECV_INIT != stage) || \
3396 (MHD_CONNECTION_INIT == c->state));
3397 mhd_assert ((MHD_PROC_RECV_METHOD != stage) || \
3398 (MHD_CONNECTION_REQ_LINE_RECEIVING == c->state));
3399 mhd_assert ((MHD_PROC_RECV_URI != stage) || \
3400 (MHD_CONNECTION_REQ_LINE_RECEIVING == c->state));
3401 mhd_assert ((MHD_PROC_RECV_HTTPVER != stage) || \
3402 (MHD_CONNECTION_REQ_LINE_RECEIVING == c->state));
3403 mhd_assert ((MHD_PROC_RECV_HEADERS != stage) || \
3404 (MHD_CONNECTION_REQ_HEADERS_RECEIVING == c->state));
3405 mhd_assert (MHD_PROC_RECV_COOKIE != stage); /* handle_req_cookie_no_space() must be called directly */
3406 mhd_assert ((MHD_PROC_RECV_BODY_NORMAL != stage) || \
3407 (MHD_CONNECTION_BODY_RECEIVING == c->state));
3408 mhd_assert ((MHD_PROC_RECV_BODY_CHUNKED != stage) || \
3409 (MHD_CONNECTION_BODY_RECEIVING == c->state));
3410 mhd_assert ((MHD_PROC_RECV_FOOTERS != stage) || \
3411 (MHD_CONNECTION_FOOTERS_RECEIVING == c->state));
3412 mhd_assert ((MHD_PROC_RECV_BODY_NORMAL != stage) || \
3413 (! c->rq.have_chunked_upload));
3414 mhd_assert ((MHD_PROC_RECV_BODY_CHUNKED != stage) || \
3415 (c->rq.have_chunked_upload));
3416 switch (stage)
3417 {
3418 case MHD_PROC_RECV_INIT:
3419 case MHD_PROC_RECV_METHOD:
3420 /* Some data has been received, but it is not clear yet whether
3421 * the received data is an valid HTTP request */
3422 connection_close_error (c,
3423 _ ("No space left in the read buffer when " \
3424 "receiving the initial part of " \
3425 "the request line."));
3426 return;
3427 case MHD_PROC_RECV_URI:
3428 case MHD_PROC_RECV_HTTPVER:
3429 /* Some data has been received, but the request line is incomplete */
3430 mhd_assert (MHD_HTTP_MTHD_NO_METHOD != c->rq.http_mthd);
3431 mhd_assert (MHD_HTTP_VER_UNKNOWN == c->rq.http_ver);
3432 /* A quick simple check whether the incomplete line looks
3433 * like an HTTP request */
3434 if ((MHD_HTTP_MTHD_GET <= c->rq.http_mthd) &&
3435 (MHD_HTTP_MTHD_DELETE >= c->rq.http_mthd))
3436 {
3437 transmit_error_response_static (c,
3438 MHD_HTTP_URI_TOO_LONG,
3439 ERR_MSG_REQUEST_TOO_BIG);
3440 return;
3441 }
3442 connection_close_error (c,
3443 _ ("No space left in the read buffer when " \
3444 "receiving the URI in " \
3445 "the request line. " \
3446 "The request uses non-standard HTTP request " \
3447 "method token."));
3448 return;
3449 case MHD_PROC_RECV_HEADERS:
3450 handle_req_headers_no_space (c, c->read_buffer, c->read_buffer_offset);
3451 return;
3452 case MHD_PROC_RECV_BODY_NORMAL:
3453 case MHD_PROC_RECV_BODY_CHUNKED:
3454 mhd_assert ((MHD_PROC_RECV_BODY_CHUNKED != stage) || \
3455 ! c->rq.some_payload_processed);
3456 if (has_unprocessed_upload_body_data_in_buffer (c))
3457 {
3458 /* The connection must not be in MHD_EVENT_LOOP_INFO_READ state
3459 when external polling is used and some data left unprocessed. */
3460 mhd_assert (0 != (c->daemon->options & MHD_USE_INTERNAL_POLLING_THREAD));
3461 /* failed to grow the read buffer, and the
3462 client which is supposed to handle the
3463 received data in a *blocking* fashion
3464 (in this mode) did not handle the data as
3465 it was supposed to!
3466 => we would either have to do busy-waiting
3467 (on the client, which would likely fail),
3468 or if we do nothing, we would just timeout
3469 on the connection (if a timeout is even
3470 set!).
3471 Solution: we kill the connection with an error */
3472 transmit_error_response_static (c,
3473 MHD_HTTP_INTERNAL_SERVER_ERROR,
3474 ERROR_MSG_DATA_NOT_HANDLED_BY_APP);
3475 }
3476 else
3477 {
3478 if (MHD_PROC_RECV_BODY_NORMAL == stage)
3479 {
3480 /* A header probably has been added to a suspended connection and
3481 it took precisely all the space in the buffer.
3482 Very low probability. */
3483 mhd_assert (! c->rq.have_chunked_upload);
3484 handle_req_headers_no_space (c, NULL, 0);
3485 }
3486 else
3487 {
3488 mhd_assert (c->rq.have_chunked_upload);
3489 if (c->rq.current_chunk_offset != c->rq.current_chunk_size)
3490 { /* Receiving content of the chunk */
3491 /* A header probably has been added to a suspended connection and
3492 it took precisely all the space in the buffer.
3493 Very low probability. */
3494 handle_req_headers_no_space (c, NULL, 0);
3495 }
3496 else
3497 {
3498 if (0 != c->rq.current_chunk_size)
3499 { /* Waiting for chunk-closing CRLF */
3500 /* Not really possible as some payload should be
3501 processed and the space used by payload should be available. */
3502 handle_req_headers_no_space (c, NULL, 0);
3503 }
3504 else
3505 { /* Reading the line with the chunk size */
3506 handle_req_chunk_size_line_no_space (c,
3507 c->read_buffer,
3508 c->read_buffer_offset);
3509 }
3510 }
3511 }
3512 }
3513 return;
3514 case MHD_PROC_RECV_FOOTERS:
3515 handle_req_footers_no_space (c, c->read_buffer, c->read_buffer_offset);
3516 return;
3517 /* The next cases should not be possible */
3518 case MHD_PROC_RECV_COOKIE:
3519 default:
3520 break;
3521 }
3522 mhd_assert (0);
3523}
3524
3525
3526/**
2870 * Check whether enough space is available in the read buffer for the next 3527 * Check whether enough space is available in the read buffer for the next
2871 * operation. 3528 * operation.
2872 * Handles grow of the buffer if required and error conditions (when buffer 3529 * Handles grow of the buffer if required and error conditions (when buffer
@@ -2944,70 +3601,75 @@ check_and_grow_read_buffer_space (struct MHD_Connection *c)
2944 3601
2945 /* Failed to increase the read buffer size, but need to read the data 3602 /* Failed to increase the read buffer size, but need to read the data
2946 from the network. 3603 from the network.
2947 No more space in the buffer, no more space to increase the buffer. */ 3604 No more space left in the buffer, no more space to increase the buffer. */
2948 3605
2949 /* 'PROCESS_READ' event state flag must be set only if the last application 3606 /* 'PROCESS_READ' event state flag must be set only if the last application
2950 callback has processed some data. If any data is processed then some 3607 callback has processed some data. If any data is processed then some
2951 space in the read buffer must be available. */ 3608 space in the read buffer must be available. */
2952 mhd_assert (0 == (MHD_EVENT_LOOP_INFO_PROCESS & c->event_loop_info)); 3609 mhd_assert (0 == (MHD_EVENT_LOOP_INFO_PROCESS & c->event_loop_info));
2953 3610
2954 if (MHD_CONNECTION_BODY_RECEIVING != c->state) 3611 if ((0 == (c->daemon->options & MHD_USE_INTERNAL_POLLING_THREAD))
2955 { 3612 && (MHD_CONNECTION_BODY_RECEIVING == c->state)
2956 /* Receiving request line, request headers or request footers */ 3613 && has_unprocessed_upload_body_data_in_buffer (c))
2957 /* TODO: Improve detection of the conditions */
2958 if (c->rq.url != NULL)
2959 transmit_error_response_static (c,
2960 MHD_HTTP_REQUEST_HEADER_FIELDS_TOO_LARGE,
2961 REQUEST_TOO_BIG);
2962 else
2963 transmit_error_response_static (c,
2964 MHD_HTTP_URI_TOO_LONG,
2965 REQUEST_TOO_BIG);
2966 return false;
2967 }
2968
2969 /* Receiving the request body and no space left in the buffer */
2970
2971 if (! has_unprocessed_upload_body_data_in_buffer (c))
2972 {
2973 /* Full header is received and no space left for reading
2974 the request body.
2975 For chunked upload encoding: chunk-extension is too long or
2976 chunk size is encoded with excessive number of leading zeros. */
2977 /* TODO: report proper cause for the error */
2978 transmit_error_response_static (c,
2979 MHD_HTTP_REQUEST_HEADER_FIELDS_TOO_LARGE,
2980 REQUEST_TOO_BIG);
2981 return false;
2982 }
2983
2984 /* No space left in the buffer but some upload body data can be processed
2985 and some space could be freed. */
2986 mhd_assert (! c->rq.some_payload_processed);
2987 if (0 == (c->daemon->options & MHD_USE_INTERNAL_POLLING_THREAD))
2988 { 3614 {
2989 /* The application is handling processing cycles. 3615 /* The application is handling processing cycles.
2990 The data may be processed later. */ 3616 The data could be processed later. */
2991 c->event_loop_info = MHD_EVENT_LOOP_INFO_PROCESS; 3617 c->event_loop_info = MHD_EVENT_LOOP_INFO_PROCESS;
2992 return true; 3618 return true;
2993 } 3619 }
3620 else
3621 {
3622 enum MHD_ProcRecvDataStage stage;
2994 3623
2995 /* Using internal thread for sockets polling */ 3624 switch (c->state)
3625 {
3626 case MHD_CONNECTION_INIT:
3627 stage = MHD_PROC_RECV_INIT;
3628 break;
3629 case MHD_CONNECTION_REQ_LINE_RECEIVING:
3630 if (MHD_HTTP_MTHD_NO_METHOD == c->rq.http_mthd)
3631 stage = MHD_PROC_RECV_METHOD;
3632 else if (0 == c->rq.req_target_len)
3633 stage = MHD_PROC_RECV_URI;
3634 else
3635 stage = MHD_PROC_RECV_HTTPVER;
3636 break;
3637 case MHD_CONNECTION_REQ_HEADERS_RECEIVING:
3638 stage = MHD_PROC_RECV_HEADERS;
3639 break;
3640 case MHD_CONNECTION_BODY_RECEIVING:
3641 stage = c->rq.have_chunked_upload ?
3642 MHD_PROC_RECV_BODY_CHUNKED : MHD_PROC_RECV_BODY_NORMAL;
3643 break;
3644 case MHD_CONNECTION_FOOTERS_RECEIVING:
3645 stage = MHD_PROC_RECV_FOOTERS;
3646 break;
3647 case MHD_CONNECTION_REQ_LINE_RECEIVED:
3648 case MHD_CONNECTION_HEADERS_RECEIVED:
3649 case MHD_CONNECTION_HEADERS_PROCESSED:
3650 case MHD_CONNECTION_CONTINUE_SENDING:
3651 case MHD_CONNECTION_BODY_RECEIVED:
3652 case MHD_CONNECTION_FOOTERS_RECEIVED:
3653 case MHD_CONNECTION_FULL_REQ_RECEIVED:
3654 case MHD_CONNECTION_START_REPLY:
3655 case MHD_CONNECTION_HEADERS_SENDING:
3656 case MHD_CONNECTION_HEADERS_SENT:
3657 case MHD_CONNECTION_NORMAL_BODY_UNREADY:
3658 case MHD_CONNECTION_NORMAL_BODY_READY:
3659 case MHD_CONNECTION_CHUNKED_BODY_UNREADY:
3660 case MHD_CONNECTION_CHUNKED_BODY_READY:
3661 case MHD_CONNECTION_CHUNKED_BODY_SENT:
3662 case MHD_CONNECTION_FOOTERS_SENDING:
3663 case MHD_CONNECTION_FULL_REPLY_SENT:
3664 case MHD_CONNECTION_CLOSED:
3665 case MHD_CONNECTION_UPGRADE:
3666 default:
3667 stage = MHD_PROC_RECV_BODY_NORMAL;
3668 mhd_assert (0);
3669 }
2996 3670
2997 /* failed to grow the read buffer, and the 3671 handle_recv_no_space (c, stage);
2998 client which is supposed to handle the 3672 }
2999 received data in a *blocking* fashion
3000 (in this mode) did not handle the data as
3001 it was supposed to!
3002 => we would either have to do busy-waiting
3003 (on the client, which would likely fail),
3004 or if we do nothing, we would just timeout
3005 on the connection (if a timeout is even
3006 set!).
3007 Solution: we kill the connection with an error */
3008 transmit_error_response_static (c,
3009 MHD_HTTP_INTERNAL_SERVER_ERROR,
3010 ERROR_MSG_DATA_NOT_HANDLED_BY_APP);
3011 return false; 3673 return false;
3012} 3674}
3013 3675
@@ -3214,7 +3876,7 @@ connection_add_header (void *cls,
3214#endif 3876#endif
3215 transmit_error_response_static (connection, 3877 transmit_error_response_static (connection,
3216 MHD_HTTP_REQUEST_HEADER_FIELDS_TOO_LARGE, 3878 MHD_HTTP_REQUEST_HEADER_FIELDS_TOO_LARGE,
3217 REQUEST_TOO_BIG); 3879 ERR_MSG_REQUEST_TOO_BIG);
3218 return MHD_NO; 3880 return MHD_NO;
3219 } 3881 }
3220 return MHD_YES; 3882 return MHD_YES;
@@ -4021,9 +4683,7 @@ parse_connection_headers (struct MHD_Connection *connection)
4021#ifdef COOKIE_SUPPORT 4683#ifdef COOKIE_SUPPORT
4022 if (MHD_PARSE_COOKIE_NO_MEMORY == parse_cookie_header (connection)) 4684 if (MHD_PARSE_COOKIE_NO_MEMORY == parse_cookie_header (connection))
4023 { 4685 {
4024 transmit_error_response_static (connection, 4686 handle_req_cookie_no_space (connection);
4025 MHD_HTTP_REQUEST_HEADER_FIELDS_TOO_LARGE,
4026 REQUEST_TOO_BIG);
4027 return; 4687 return;
4028 } 4688 }
4029#endif /* COOKIE_SUPPORT */ 4689#endif /* COOKIE_SUPPORT */
@@ -5518,11 +6178,10 @@ get_req_headers (struct MHD_Connection *c, bool process_footers)
5518 hdr_name.str, hdr_name.len, 6178 hdr_name.str, hdr_name.len,
5519 hdr_value.str, hdr_value.len)) 6179 hdr_value.str, hdr_value.len))
5520 { 6180 {
5521 /** 6181 size_t add_element_size;
5522 * If 'true' then "headers too large" is used for the error response, 6182
5523 * if 'false' then "URI too large is used for the error response. 6183 mhd_assert (hdr_name.str < hdr_value.str);
5524 */ 6184
5525 bool headers_large_err;
5526#ifdef HAVE_MESSAGES 6185#ifdef HAVE_MESSAGES
5527 MHD_DLOG (c->daemon, 6186 MHD_DLOG (c->daemon,
5528 _ ("Failed to allocate memory in the connection memory " \ 6187 _ ("Failed to allocate memory in the connection memory " \
@@ -5530,35 +6189,14 @@ get_req_headers (struct MHD_Connection *c, bool process_footers)
5530 (! process_footers) ? _ ("header") : _ ("footer")); 6189 (! process_footers) ? _ ("header") : _ ("footer"));
5531#endif /* HAVE_MESSAGES */ 6190#endif /* HAVE_MESSAGES */
5532 6191
5533 if (! process_footers) 6192 add_element_size = hdr_value.len
5534 { 6193 + (size_t) (hdr_value.str - hdr_name.str);
5535 size_t http_headers_size;
5536 size_t url_size;
5537 const struct MHD_HTTP_Req_Header *hdr;
5538 6194
5539 http_headers_size = hdr_name.len + hdr_value.len; 6195 if (! process_footers)
5540 url_size = c->rq.url_len; 6196 handle_req_headers_no_space (c, hdr_name.str, add_element_size);
5541 for (hdr = c->rq.headers_received; NULL != hdr; hdr = hdr->next)
5542 {
5543 if (MHD_HEADER_KIND == hdr->kind)
5544 http_headers_size += hdr->header_size + hdr->value_size + 2;
5545 else if (MHD_GET_ARGUMENT_KIND == hdr->kind)
5546 url_size += hdr->header_size + hdr->value_size + 2;
5547 }
5548 /* The comparison is not precise as linefeeds (for headers) and
5549 unescaping (for GET parameters) are not taken into account,
5550 but precision is not required here. */
5551 headers_large_err =
5552 (http_headers_size >= url_size);
5553 }
5554 else 6197 else
5555 headers_large_err = true; 6198 handle_req_footers_no_space (c, hdr_name.str, add_element_size);
5556 6199
5557 transmit_error_response_static (c,
5558 headers_large_err ?
5559 MHD_HTTP_REQUEST_HEADER_FIELDS_TOO_LARGE
5560 : MHD_HTTP_URI_TOO_LONG,
5561 REQUEST_TOO_BIG);
5562 mhd_assert (MHD_CONNECTION_FULL_REQ_RECEIVED < c->state); 6200 mhd_assert (MHD_CONNECTION_FULL_REQ_RECEIVED < c->state);
5563 return true; 6201 return true;
5564 } 6202 }