diff options
-rw-r--r-- | src/microhttpd/connection.c | 818 | ||||
-rw-r--r-- | src/testcurl/test_toolarge.c | 58 |
2 files changed, 778 insertions, 98 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>"Cookie:"</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 | */ | ||
2964 | enum 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 | */ | ||
3059 | static unsigned int | ||
3060 | get_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 | */ | ||
3272 | static void | ||
3273 | handle_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 | */ | ||
3295 | static void | ||
3296 | handle_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 | */ | ||
3323 | static void | ||
3324 | handle_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 | */ | ||
3363 | static void | ||
3364 | handle_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 | */ | ||
3388 | static void | ||
3389 | handle_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 | } |
diff --git a/src/testcurl/test_toolarge.c b/src/testcurl/test_toolarge.c index 36649a76..87be49dc 100644 --- a/src/testcurl/test_toolarge.c +++ b/src/testcurl/test_toolarge.c | |||
@@ -182,7 +182,7 @@ _mhdErrorExit_func (const char *errDesc, const char *funcName, int lineNum) | |||
182 | 182 | ||
183 | 183 | ||
184 | /* Could be increased to facilitate debugging */ | 184 | /* Could be increased to facilitate debugging */ |
185 | #define TIMEOUTS_VAL 5 | 185 | #define TIMEOUTS_VAL 50000 |
186 | 186 | ||
187 | #define EXPECTED_URI_BASE_PATH "/a" | 187 | #define EXPECTED_URI_BASE_PATH "/a" |
188 | 188 | ||
@@ -788,9 +788,16 @@ doCurlQueryInThread (struct MHD_Daemon *d, | |||
788 | mhdErrorExitDesc ("Request failed due to unexpected error"); | 788 | mhdErrorExitDesc ("Request failed due to unexpected error"); |
789 | } | 789 | } |
790 | p->queryError = 1; | 790 | p->queryError = 1; |
791 | if ((0 != resp_code) && | 791 | switch (resp_code) |
792 | ((499 < resp_code) || (400 > resp_code))) /* TODO: add all expected error codes */ | ||
793 | { | 792 | { |
793 | case 0: /* No parsed response */ | ||
794 | case 413: /* "Content Too Large" */ | ||
795 | case 414: /* "URI Too Long" */ | ||
796 | case 431: /* "Request Header Fields Too Large" */ | ||
797 | case 501: /* "Not Implemented" */ | ||
798 | /* Expected error codes */ | ||
799 | break; | ||
800 | default: | ||
794 | fprintf (stderr, | 801 | fprintf (stderr, |
795 | "Got reply with unexpected status code: %ld\n", | 802 | "Got reply with unexpected status code: %ld\n", |
796 | resp_code); | 803 | resp_code); |
@@ -865,7 +872,14 @@ performTestQueries (struct MHD_Daemon *d, uint16_t d_port, | |||
865 | ahc_param->rp_data, | 872 | ahc_param->rp_data, |
866 | ahc_param->rp_data_size)) | 873 | ahc_param->rp_data_size)) |
867 | { | 874 | { |
868 | (void) qParam.responseCode; /* TODO: check for the right response code */ | 875 | if ((0 != qParam.responseCode) && (501 != qParam.responseCode)) |
876 | { | ||
877 | fprintf (stderr, | ||
878 | "Got reply with status code %d, " | ||
879 | "while code 501 (\"Not Implemented\") is expected.\n", | ||
880 | qParam.responseCode); | ||
881 | mhdErrorExit (); | ||
882 | } | ||
869 | if (TEST_OK_SIZE >= i) | 883 | if (TEST_OK_SIZE >= i) |
870 | { | 884 | { |
871 | fprintf (stderr, | 885 | fprintf (stderr, |
@@ -922,7 +936,14 @@ performTestQueries (struct MHD_Daemon *d, uint16_t d_port, | |||
922 | ahc_param->rp_data, | 936 | ahc_param->rp_data, |
923 | ahc_param->rp_data_size)) | 937 | ahc_param->rp_data_size)) |
924 | { | 938 | { |
925 | (void) qParam.responseCode; /* TODO: check for the right response code */ | 939 | if ((0 != qParam.responseCode) && (414 != qParam.responseCode)) |
940 | { | ||
941 | fprintf (stderr, | ||
942 | "Got reply with status code %d, " | ||
943 | "while code 414 (\"URI Too Long\") is expected.\n", | ||
944 | qParam.responseCode); | ||
945 | mhdErrorExit (); | ||
946 | } | ||
926 | if (TEST_OK_SIZE >= i) | 947 | if (TEST_OK_SIZE >= i) |
927 | { | 948 | { |
928 | fprintf (stderr, | 949 | fprintf (stderr, |
@@ -981,7 +1002,14 @@ performTestQueries (struct MHD_Daemon *d, uint16_t d_port, | |||
981 | ahc_param->rp_data, | 1002 | ahc_param->rp_data, |
982 | ahc_param->rp_data_size)) | 1003 | ahc_param->rp_data_size)) |
983 | { | 1004 | { |
984 | (void) qParam.responseCode; /* TODO: check for the right response code */ | 1005 | if ((0 != qParam.responseCode) && (431 != qParam.responseCode)) |
1006 | { | ||
1007 | fprintf (stderr, | ||
1008 | "Got reply with status code %d, while code 431 " | ||
1009 | "(\"Request Header Fields Too Large\") is expected.\n", | ||
1010 | qParam.responseCode); | ||
1011 | mhdErrorExit (); | ||
1012 | } | ||
985 | if (0 != ahc_param->header_check_param.large_header_name_size) | 1013 | if (0 != ahc_param->header_check_param.large_header_name_size) |
986 | { /* If large header was processed, it must be valid */ | 1014 | { /* If large header was processed, it must be valid */ |
987 | if (i != ahc_param->header_check_param.large_header_name_size) | 1015 | if (i != ahc_param->header_check_param.large_header_name_size) |
@@ -1055,7 +1083,14 @@ performTestQueries (struct MHD_Daemon *d, uint16_t d_port, | |||
1055 | ahc_param->rp_data, | 1083 | ahc_param->rp_data, |
1056 | ahc_param->rp_data_size)) | 1084 | ahc_param->rp_data_size)) |
1057 | { | 1085 | { |
1058 | (void) qParam.responseCode; /* TODO: check for the right response code */ | 1086 | if ((0 != qParam.responseCode) && (431 != qParam.responseCode)) |
1087 | { | ||
1088 | fprintf (stderr, | ||
1089 | "Got reply with status code %d, while code 431 " | ||
1090 | "(\"Request Header Fields Too Large\") is expected.\n", | ||
1091 | qParam.responseCode); | ||
1092 | mhdErrorExit (); | ||
1093 | } | ||
1059 | if (0 != ahc_param->header_check_param.large_header_name_size) | 1094 | if (0 != ahc_param->header_check_param.large_header_name_size) |
1060 | { /* If large header was processed, it must be valid */ | 1095 | { /* If large header was processed, it must be valid */ |
1061 | if (1 != ahc_param->header_check_param.large_header_name_size) | 1096 | if (1 != ahc_param->header_check_param.large_header_name_size) |
@@ -1129,7 +1164,14 @@ performTestQueries (struct MHD_Daemon *d, uint16_t d_port, | |||
1129 | ahc_param->rp_data, | 1164 | ahc_param->rp_data, |
1130 | ahc_param->rp_data_size)) | 1165 | ahc_param->rp_data_size)) |
1131 | { | 1166 | { |
1132 | (void) qParam.responseCode; /* TODO: check for the right response code */ | 1167 | if ((0 != qParam.responseCode) && (431 != qParam.responseCode)) |
1168 | { | ||
1169 | fprintf (stderr, | ||
1170 | "Got reply with status code %d, while code 431 " | ||
1171 | "(\"Request Header Fields Too Large\") is expected.\n", | ||
1172 | qParam.responseCode); | ||
1173 | mhdErrorExit (); | ||
1174 | } | ||
1133 | if (0 != ahc_param->header_check_param.num_n1_headers) | 1175 | if (0 != ahc_param->header_check_param.num_n1_headers) |
1134 | { /* If headers were processed, they must be valid */ | 1176 | { /* If headers were processed, they must be valid */ |
1135 | if (num_hdrs != ahc_param->header_check_param.num_n1_headers) | 1177 | if (num_hdrs != ahc_param->header_check_param.num_n1_headers) |