aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorChristian Grothoff <christian@grothoff.org>2007-08-08 20:03:11 +0000
committerChristian Grothoff <christian@grothoff.org>2007-08-08 20:03:11 +0000
commit4093a912306203b5d99c377c5629e746521b5be8 (patch)
treed92adcd2757aee480d816228acf2eb5aedce92c0
parenta9366e931500a3ee1ac6ed30edbdcea708dcdcd8 (diff)
downloadlibmicrohttpd-4093a912306203b5d99c377c5629e746521b5be8.tar.gz
libmicrohttpd-4093a912306203b5d99c377c5629e746521b5be8.zip
handling of long headers (#1222)
-rw-r--r--README4
-rw-r--r--src/daemon/Makefile.am10
-rw-r--r--src/daemon/connection.c69
-rw-r--r--src/daemon/daemontest_long_header.c287
4 files changed, 346 insertions, 24 deletions
diff --git a/README b/README
index 628c75d1..2d80f840 100644
--- a/README
+++ b/README
@@ -14,9 +14,7 @@ feature.
14For http/1.1-compliance: 14For http/1.1-compliance:
15======================== 15========================
16connection.c: 16connection.c:
17- send proper error code back if headers are too long 17- support chunked requests from clients (#1260, ARCH, TEST)
18 (currently, we just close the connection) (#1222, ARCH, TEST)
19- support chunked requests from clients (#1260, TEST, TEST)
20- send proper error code back if client forgot the "Host" header (#1264, TRIV) 18- send proper error code back if client forgot the "Host" header (#1264, TRIV)
21- automatically add MHD_HTTP_HEADER_DATE if client "forgot" to add one (#1261, TRIV) 19- automatically add MHD_HTTP_HEADER_DATE if client "forgot" to add one (#1261, TRIV)
22- automatically drop body from responses to "HEAD" requests (#1262, TRIV) 20- automatically drop body from responses to "HEAD" requests (#1262, TRIV)
diff --git a/src/daemon/Makefile.am b/src/daemon/Makefile.am
index e02b2c7d..88292c88 100644
--- a/src/daemon/Makefile.am
+++ b/src/daemon/Makefile.am
@@ -35,7 +35,8 @@ check_PROGRAMS = \
35 daemontest_put \ 35 daemontest_put \
36 daemontest_get11 \ 36 daemontest_get11 \
37 daemontest_post11 \ 37 daemontest_post11 \
38 daemontest_put11 38 daemontest_put11 \
39 daemontest_long_header
39 40
40TESTS = $(check_PROGRAMS) 41TESTS = $(check_PROGRAMS)
41 42
@@ -74,11 +75,16 @@ daemontest_post11_LDADD = \
74 $(top_builddir)/src/daemon/libmicrohttpd.la \ 75 $(top_builddir)/src/daemon/libmicrohttpd.la \
75 @LIBCURL@ 76 @LIBCURL@
76 77
77
78daemontest_put11_SOURCES = \ 78daemontest_put11_SOURCES = \
79 daemontest_put.c 79 daemontest_put.c
80daemontest_put11_LDADD = \ 80daemontest_put11_LDADD = \
81 $(top_builddir)/src/daemon/libmicrohttpd.la \ 81 $(top_builddir)/src/daemon/libmicrohttpd.la \
82 @LIBCURL@ 82 @LIBCURL@
83 83
84daemontest_long_header_SOURCES = \
85 daemontest_long_header.c
86daemontest_long_header_LDADD = \
87 $(top_builddir)/src/daemon/libmicrohttpd.la \
88 @LIBCURL@
89
84endif \ No newline at end of file 90endif \ No newline at end of file
diff --git a/src/daemon/connection.c b/src/daemon/connection.c
index 272fb802..ec261963 100644
--- a/src/daemon/connection.c
+++ b/src/daemon/connection.c
@@ -41,6 +41,12 @@
41#define HTTP_100_CONTINUE "HTTP/1.1 100 Continue\r\n\r\n" 41#define HTTP_100_CONTINUE "HTTP/1.1 100 Continue\r\n\r\n"
42 42
43/** 43/**
44 * Response used when the request (http header) is too big to
45 * be processed.
46 */
47#define REQUEST_TOO_BIG ""
48
49/**
44 * Get all of the headers from the request. 50 * Get all of the headers from the request.
45 * 51 *
46 * @param iterator callback to call on each header; 52 * @param iterator callback to call on each header;
@@ -146,16 +152,14 @@ MHD_connection_get_fdset(struct MHD_Connection * connection,
146 fd = connection->socket_fd; 152 fd = connection->socket_fd;
147 if (fd == -1) 153 if (fd == -1)
148 return MHD_YES; 154 return MHD_YES;
149 if ( (connection->read_close == 0) && 155 if ( (connection->read_close == MHD_NO) &&
150 ( (connection->headersReceived == 0) || 156 ( (connection->headersReceived == 0) ||
151 (connection->readLoc < connection->read_buffer_size) ) ) { 157 (connection->readLoc < connection->read_buffer_size) ) ) {
152 FD_SET(fd, read_fd_set); 158 FD_SET(fd, read_fd_set);
153 if (fd > *max_fd) 159 if (fd > *max_fd)
154 *max_fd = fd; 160 *max_fd = fd;
155 } else { 161 } else {
156 162 if ( (connection->read_close == MHD_NO) &&
157
158 if ( (connection->read_close == 0) &&
159 ( (connection->headersReceived == 1) && 163 ( (connection->headersReceived == 1) &&
160 (connection->post_processed == MHD_NO) && 164 (connection->post_processed == MHD_NO) &&
161 (connection->readLoc == connection->read_buffer_size) ) ) { 165 (connection->readLoc == connection->read_buffer_size) ) ) {
@@ -188,17 +192,32 @@ MHD_connection_get_fdset(struct MHD_Connection * connection,
188 192
189/** 193/**
190 * We ran out of memory processing the 194 * We ran out of memory processing the
191 * header. Handle it properly. 195 * header. Handle it properly by stopping to read data
196 * and sending a HTTP 413 or HTTP 414 response.
197 *
198 * @param status_code the response code to send (413 or 414)
192 */ 199 */
193static void 200static void
194MHD_excessive_header_handler(struct MHD_Connection * connection) { 201MHD_excessive_data_handler(struct MHD_Connection * connection,
202 unsigned int status_code) {
203 struct MHD_Response * response;
204
195 /* die, header far too long to be reasonable; 205 /* die, header far too long to be reasonable;
196 FIXME: send proper response to client 206 FIXME: send proper response to client
197 (stop reading, queue proper response) */ 207 (stop reading, queue proper response) */
208 connection->read_close = MHD_YES;
209 connection->headersReceived = MHD_YES;
210 connection->bodyReceived = MHD_YES;
198 MHD_DLOG(connection->daemon, 211 MHD_DLOG(connection->daemon,
199 "Received excessively long header line, closing connection.\n"); 212 "Received excessively long header, closing connection.\n");
200 CLOSE(connection->socket_fd); 213 response = MHD_create_response_from_data(strlen(REQUEST_TOO_BIG),
201 connection->socket_fd = -1; 214 REQUEST_TOO_BIG,
215 MHD_NO,
216 MHD_NO);
217 MHD_queue_response(connection,
218 status_code,
219 response);
220 MHD_destroy_response(response);
202} 221}
203 222
204/** 223/**
@@ -230,7 +249,10 @@ MHD_get_next_header_line(struct MHD_Connection * connection) {
230 connection->read_buffer_size, 249 connection->read_buffer_size,
231 connection->read_buffer_size * 2 + MHD_BUF_INC_SIZE); 250 connection->read_buffer_size * 2 + MHD_BUF_INC_SIZE);
232 if (rbuf == NULL) { 251 if (rbuf == NULL) {
233 MHD_excessive_header_handler(connection); 252 MHD_excessive_data_handler(connection,
253 (connection->url != NULL)
254 ? MHD_HTTP_REQUEST_ENTITY_TOO_LARGE
255 : MHD_HTTP_REQUEST_URI_TOO_LONG);
234 } else { 256 } else {
235 connection->read_buffer_size = connection->read_buffer_size * 2 + MHD_BUF_INC_SIZE; 257 connection->read_buffer_size = connection->read_buffer_size * 2 + MHD_BUF_INC_SIZE;
236 connection->read_buffer = rbuf; 258 connection->read_buffer = rbuf;
@@ -265,7 +287,8 @@ MHD_connection_add_header(struct MHD_Connection * connection,
265 if (hdr == NULL) { 287 if (hdr == NULL) {
266 MHD_DLOG(connection->daemon, 288 MHD_DLOG(connection->daemon,
267 "Not enough memory to allocate header record!\n"); 289 "Not enough memory to allocate header record!\n");
268 MHD_excessive_header_handler(connection); 290 MHD_excessive_data_handler(connection,
291 MHD_HTTP_REQUEST_ENTITY_TOO_LARGE);
269 return MHD_NO; 292 return MHD_NO;
270 } 293 }
271 hdr->next = connection->headers_received; 294 hdr->next = connection->headers_received;
@@ -360,7 +383,8 @@ MHD_parse_cookie_header(struct MHD_Connection * connection) {
360 if (cpy == NULL) { 383 if (cpy == NULL) {
361 MHD_DLOG(connection->daemon, 384 MHD_DLOG(connection->daemon,
362 "Not enough memory to parse cookies!\n"); 385 "Not enough memory to parse cookies!\n");
363 MHD_excessive_header_handler(connection); 386 MHD_excessive_data_handler(connection,
387 MHD_HTTP_REQUEST_ENTITY_TOO_LARGE);
364 return MHD_NO; 388 return MHD_NO;
365 } 389 }
366 memcpy(cpy, 390 memcpy(cpy,
@@ -483,7 +507,8 @@ MHD_parse_connection_headers(struct MHD_Connection * connection) {
483 strlen(last)+1, 507 strlen(last)+1,
484 strlen(line) + strlen(last) + 1); 508 strlen(line) + strlen(last) + 1);
485 if (last == NULL) { 509 if (last == NULL) {
486 MHD_excessive_header_handler(connection); 510 MHD_excessive_data_handler(connection,
511 MHD_HTTP_REQUEST_ENTITY_TOO_LARGE);
487 break; 512 break;
488 } 513 }
489 tmp = line; 514 tmp = line;
@@ -620,8 +645,10 @@ MHD_test_post_data(struct MHD_Connection * connection) {
620 const char * encoding; 645 const char * encoding;
621 void * buf; 646 void * buf;
622 647
623 if (0 != strcasecmp(connection->method, 648 if ( (connection->method == NULL) ||
624 MHD_HTTP_METHOD_POST)) 649 (connection->response != NULL) ||
650 (0 != strcasecmp(connection->method,
651 MHD_HTTP_METHOD_POST)) )
625 return MHD_NO; 652 return MHD_NO;
626 encoding = MHD_lookup_connection_value(connection, 653 encoding = MHD_lookup_connection_value(connection,
627 MHD_HEADER_KIND, 654 MHD_HEADER_KIND,
@@ -711,6 +738,8 @@ MHD_call_connection_handler(struct MHD_Connection * connection) {
711 struct MHD_Access_Handler * ah; 738 struct MHD_Access_Handler * ah;
712 unsigned int processed; 739 unsigned int processed;
713 740
741 if (connection->response != NULL)
742 return; /* already queued a response */
714 if (connection->headersReceived == 0) 743 if (connection->headersReceived == 0)
715 abort(); /* bad timing... */ 744 abort(); /* bad timing... */
716 ah = MHD_find_access_handler(connection); 745 ah = MHD_find_access_handler(connection);
@@ -778,7 +807,8 @@ MHD_connection_handle_read(struct MHD_Connection * connection) {
778 if (tmp == NULL) { 807 if (tmp == NULL) {
779 MHD_DLOG(connection->daemon, 808 MHD_DLOG(connection->daemon,
780 "Not enough memory for reading headers!\n"); 809 "Not enough memory for reading headers!\n");
781 MHD_excessive_header_handler(connection); 810 MHD_excessive_data_handler(connection,
811 MHD_HTTP_REQUEST_ENTITY_TOO_LARGE);
782 return MHD_NO; 812 return MHD_NO;
783 } 813 }
784 connection->read_buffer = tmp; 814 connection->read_buffer = tmp;
@@ -824,8 +854,9 @@ MHD_connection_handle_read(struct MHD_Connection * connection) {
824 (connection->uploadSize == connection->readLoc) ) 854 (connection->uploadSize == connection->readLoc) )
825 if (MHD_NO == MHD_parse_post_data(connection)) 855 if (MHD_NO == MHD_parse_post_data(connection))
826 connection->post_processed = MHD_NO; 856 connection->post_processed = MHD_NO;
827 if ( (connection->post_processed == MHD_NO) || 857 if ( ( (connection->post_processed == MHD_NO) ||
828 (connection->read_buffer_size == connection->readLoc) ) 858 (connection->read_buffer_size == connection->readLoc) ) &&
859 (connection->method != NULL) )
829 MHD_call_connection_handler(connection); 860 MHD_call_connection_handler(connection);
830 } 861 }
831 return MHD_YES; 862 return MHD_YES;
@@ -1065,7 +1096,7 @@ MHD_connection_handle_write(struct MHD_Connection * connection) {
1065 connection->messagePos = 0; 1096 connection->messagePos = 0;
1066 connection->method = NULL; 1097 connection->method = NULL;
1067 connection->url = NULL; 1098 connection->url = NULL;
1068 if ( (connection->read_close != 0) || 1099 if ( (connection->read_close == MHD_YES) ||
1069 (0 != strcasecmp(MHD_HTTP_VERSION_1_1, 1100 (0 != strcasecmp(MHD_HTTP_VERSION_1_1,
1070 connection->version)) ) { 1101 connection->version)) ) {
1071 /* closed for reading => close for good! */ 1102 /* closed for reading => close for good! */
diff --git a/src/daemon/daemontest_long_header.c b/src/daemon/daemontest_long_header.c
new file mode 100644
index 00000000..48635704
--- /dev/null
+++ b/src/daemon/daemontest_long_header.c
@@ -0,0 +1,287 @@
1/*
2 This file is part of libmicrohttpd
3 (C) 2007 Christian Grothoff
4
5 libmicrohttpd is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published
7 by the Free Software Foundation; either version 2, or (at your
8 option) any later version.
9
10 libmicrohttpd is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with libmicrohttpd; see the file COPYING. If not, write to the
17 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA.
19*/
20
21/**
22 * @file daemontest_long_header.c
23 * @brief Testcase for libmicrohttpd handling of very long headers
24 * @author Christian Grothoff
25 */
26
27#include "config.h"
28#include <curl/curl.h>
29#include <microhttpd.h>
30#include <stdlib.h>
31#include <unistd.h>
32#include <string.h>
33#include <time.h>
34
35/**
36 * We will set the memory available per connection to
37 * half of this value, so the actual value does not have
38 * to be big at all...
39 */
40#define VERY_LONG (1024*10)
41
42static int oneone;
43
44static int apc_all(void * cls,
45 const struct sockaddr * addr,
46 socklen_t addrlen) {
47 return MHD_YES;
48}
49
50struct CBC {
51 char * buf;
52 size_t pos;
53 size_t size;
54};
55
56static size_t copyBuffer(void * ptr,
57 size_t size,
58 size_t nmemb,
59 void * ctx) {
60 return size * nmemb;
61}
62
63static int ahc_echo(void * cls,
64 struct MHD_Connection * connection,
65 const char * url,
66 const char * method,
67 const char * version,
68 const char * upload_data,
69 unsigned int * upload_data_size) {
70 const char * me = cls;
71 struct MHD_Response * response;
72 int ret;
73
74 if (0 != strcmp(me, method))
75 return MHD_NO; /* unexpected method */
76 response = MHD_create_response_from_data(strlen(url),
77 (void*) url,
78 MHD_NO,
79 MHD_YES);
80 ret = MHD_queue_response(connection,
81 MHD_HTTP_OK,
82 response);
83 MHD_destroy_response(response);
84 return ret;
85}
86
87
88static int testLongUrlGet() {
89 struct MHD_Daemon * d;
90 CURL * c;
91 char buf[2048];
92 struct CBC cbc;
93 char * url;
94 long code;
95
96 cbc.buf = buf;
97 cbc.size = 2048;
98 cbc.pos = 0;
99 d = MHD_start_daemon(MHD_USE_SELECT_INTERNALLY /* | MHD_USE_DEBUG */,
100 1080,
101 &apc_all,
102 NULL,
103 &ahc_echo,
104 "GET",
105 MHD_OPTION_CONNECTION_MEMORY_LIMIT,
106 VERY_LONG / 2,
107 MHD_OPTION_END);
108 if (d == NULL)
109 return 1;
110 c = curl_easy_init();
111 url = malloc(VERY_LONG);
112 memset(url,
113 'a',
114 VERY_LONG);
115 url[VERY_LONG-1] = '\0';
116 memcpy(url,
117 "http://localhost:1080/",
118 strlen("http://localhost:1080/"));
119 curl_easy_setopt(c,
120 CURLOPT_URL,
121 url);
122 curl_easy_setopt(c,
123 CURLOPT_WRITEFUNCTION,
124 &copyBuffer);
125 curl_easy_setopt(c,
126 CURLOPT_WRITEDATA,
127 &cbc);
128 curl_easy_setopt(c,
129 CURLOPT_FAILONERROR,
130 1);
131 curl_easy_setopt(c,
132 CURLOPT_TIMEOUT,
133 2L);
134 curl_easy_setopt(c,
135 CURLOPT_CONNECTTIMEOUT,
136 2L);
137 if (oneone)
138 curl_easy_setopt(c,
139 CURLOPT_HTTP_VERSION,
140 CURL_HTTP_VERSION_1_1);
141 else
142 curl_easy_setopt(c,
143 CURLOPT_HTTP_VERSION,
144 CURL_HTTP_VERSION_1_0);
145 // NOTE: use of CONNECTTIMEOUT without also
146 // setting NOSIGNAL results in really weird
147 // crashes on my system!
148 curl_easy_setopt(c,
149 CURLOPT_NOSIGNAL,
150 1);
151 if (CURLE_OK == curl_easy_perform(c)) {
152 curl_easy_cleanup(c);
153 MHD_stop_daemon(d);
154 free(url);
155 return 2;
156 }
157 if (CURLE_OK != curl_easy_getinfo(c,
158 CURLINFO_RESPONSE_CODE,
159 &code)) {
160 curl_easy_cleanup(c);
161 MHD_stop_daemon(d);
162 free(url);
163 return 4;
164 }
165 curl_easy_cleanup(c);
166 MHD_stop_daemon(d);
167 free(url);
168 if (code != MHD_HTTP_REQUEST_URI_TOO_LONG)
169 return 8;
170 return 0;
171}
172
173
174static int testLongHeaderGet() {
175 struct MHD_Daemon * d;
176 CURL * c;
177 char buf[2048];
178 struct CBC cbc;
179 char * url;
180 long code;
181 struct curl_slist * header = NULL;
182
183 cbc.buf = buf;
184 cbc.size = 2048;
185 cbc.pos = 0;
186 d = MHD_start_daemon(MHD_USE_SELECT_INTERNALLY /* | MHD_USE_DEBUG */,
187 1080,
188 &apc_all,
189 NULL,
190 &ahc_echo,
191 "GET",
192 MHD_OPTION_CONNECTION_MEMORY_LIMIT,
193 VERY_LONG / 2,
194 MHD_OPTION_END);
195 if (d == NULL)
196 return 16;
197 c = curl_easy_init();
198 url = malloc(VERY_LONG);
199 memset(url,
200 'a',
201 VERY_LONG);
202 url[VERY_LONG-1] = '\0';
203 url[VERY_LONG/2] = ':';
204 url[VERY_LONG/2+1] = ':';
205 header = curl_slist_append(header,
206 url);
207
208 curl_easy_setopt(c,
209 CURLOPT_HTTPHEADER,
210 header);
211 curl_easy_setopt(c,
212 CURLOPT_URL,
213 "http://localhost:1080/hello_world");
214 curl_easy_setopt(c,
215 CURLOPT_WRITEFUNCTION,
216 &copyBuffer);
217 curl_easy_setopt(c,
218 CURLOPT_WRITEDATA,
219 &cbc);
220 curl_easy_setopt(c,
221 CURLOPT_FAILONERROR,
222 1);
223 curl_easy_setopt(c,
224 CURLOPT_TIMEOUT,
225 2L);
226 curl_easy_setopt(c,
227 CURLOPT_CONNECTTIMEOUT,
228 2L);
229 if (oneone)
230 curl_easy_setopt(c,
231 CURLOPT_HTTP_VERSION,
232 CURL_HTTP_VERSION_1_1);
233 else
234 curl_easy_setopt(c,
235 CURLOPT_HTTP_VERSION,
236 CURL_HTTP_VERSION_1_0);
237 // NOTE: use of CONNECTTIMEOUT without also
238 // setting NOSIGNAL results in really weird
239 // crashes on my system!
240 curl_easy_setopt(c,
241 CURLOPT_NOSIGNAL,
242 1);
243 if (CURLE_OK == curl_easy_perform(c)) {
244 curl_easy_cleanup(c);
245 MHD_stop_daemon(d);
246 curl_slist_free_all(header);
247 free(url);
248 return 32;
249 }
250 if (CURLE_OK != curl_easy_getinfo(c,
251 CURLINFO_RESPONSE_CODE,
252 &code)) {
253 curl_slist_free_all(header);
254 curl_easy_cleanup(c);
255 MHD_stop_daemon(d);
256 free(url);
257 return 64;
258 }
259 curl_slist_free_all(header);
260 curl_easy_cleanup(c);
261 MHD_stop_daemon(d);
262 free(url);
263 if (code != MHD_HTTP_REQUEST_ENTITY_TOO_LARGE)
264 return 128;
265 return 0;
266}
267
268
269
270
271
272int main(int argc,
273 char * const * argv) {
274 unsigned int errorCount = 0;
275
276 oneone = NULL != strstr(argv[0], "11");
277 if (0 != curl_global_init(CURL_GLOBAL_WIN32))
278 return 2;
279 errorCount += testLongUrlGet();
280 errorCount += testLongHeaderGet();
281 if (errorCount != 0)
282 fprintf(stderr,
283 "Error (code: %u)\n",
284 errorCount);
285 curl_global_cleanup();
286 return errorCount != 0; /* 0 == pass */
287}