handling of long headers (#1222)

This commit is contained in:
Christian Grothoff
2007-08-08 20:03:11 +00:00
parent a9366e9315
commit 4093a91230
4 changed files with 346 additions and 24 deletions
+1 -3
View File
@@ -14,9 +14,7 @@ feature.
For http/1.1-compliance:
========================
connection.c:
- send proper error code back if headers are too long
(currently, we just close the connection) (#1222, ARCH, TEST)
- support chunked requests from clients (#1260, TEST, TEST)
- support chunked requests from clients (#1260, ARCH, TEST)
- send proper error code back if client forgot the "Host" header (#1264, TRIV)
- automatically add MHD_HTTP_HEADER_DATE if client "forgot" to add one (#1261, TRIV)
- automatically drop body from responses to "HEAD" requests (#1262, TRIV)
+8 -2
View File
@@ -35,7 +35,8 @@ check_PROGRAMS = \
daemontest_put \
daemontest_get11 \
daemontest_post11 \
daemontest_put11
daemontest_put11 \
daemontest_long_header
TESTS = $(check_PROGRAMS)
@@ -74,11 +75,16 @@ daemontest_post11_LDADD = \
$(top_builddir)/src/daemon/libmicrohttpd.la \
@LIBCURL@
daemontest_put11_SOURCES = \
daemontest_put.c
daemontest_put11_LDADD = \
$(top_builddir)/src/daemon/libmicrohttpd.la \
@LIBCURL@
daemontest_long_header_SOURCES = \
daemontest_long_header.c
daemontest_long_header_LDADD = \
$(top_builddir)/src/daemon/libmicrohttpd.la \
@LIBCURL@
endif
+50 -19
View File
@@ -40,6 +40,12 @@
*/
#define HTTP_100_CONTINUE "HTTP/1.1 100 Continue\r\n\r\n"
/**
* Response used when the request (http header) is too big to
* be processed.
*/
#define REQUEST_TOO_BIG ""
/**
* Get all of the headers from the request.
*
@@ -146,16 +152,14 @@ MHD_connection_get_fdset(struct MHD_Connection * connection,
fd = connection->socket_fd;
if (fd == -1)
return MHD_YES;
if ( (connection->read_close == 0) &&
if ( (connection->read_close == MHD_NO) &&
( (connection->headersReceived == 0) ||
(connection->readLoc < connection->read_buffer_size) ) ) {
FD_SET(fd, read_fd_set);
if (fd > *max_fd)
*max_fd = fd;
} else {
if ( (connection->read_close == 0) &&
if ( (connection->read_close == MHD_NO) &&
( (connection->headersReceived == 1) &&
(connection->post_processed == MHD_NO) &&
(connection->readLoc == connection->read_buffer_size) ) ) {
@@ -188,17 +192,32 @@ MHD_connection_get_fdset(struct MHD_Connection * connection,
/**
* We ran out of memory processing the
* header. Handle it properly.
* header. Handle it properly by stopping to read data
* and sending a HTTP 413 or HTTP 414 response.
*
* @param status_code the response code to send (413 or 414)
*/
static void
MHD_excessive_header_handler(struct MHD_Connection * connection) {
MHD_excessive_data_handler(struct MHD_Connection * connection,
unsigned int status_code) {
struct MHD_Response * response;
/* die, header far too long to be reasonable;
FIXME: send proper response to client
(stop reading, queue proper response) */
connection->read_close = MHD_YES;
connection->headersReceived = MHD_YES;
connection->bodyReceived = MHD_YES;
MHD_DLOG(connection->daemon,
"Received excessively long header line, closing connection.\n");
CLOSE(connection->socket_fd);
connection->socket_fd = -1;
"Received excessively long header, closing connection.\n");
response = MHD_create_response_from_data(strlen(REQUEST_TOO_BIG),
REQUEST_TOO_BIG,
MHD_NO,
MHD_NO);
MHD_queue_response(connection,
status_code,
response);
MHD_destroy_response(response);
}
/**
@@ -230,7 +249,10 @@ MHD_get_next_header_line(struct MHD_Connection * connection) {
connection->read_buffer_size,
connection->read_buffer_size * 2 + MHD_BUF_INC_SIZE);
if (rbuf == NULL) {
MHD_excessive_header_handler(connection);
MHD_excessive_data_handler(connection,
(connection->url != NULL)
? MHD_HTTP_REQUEST_ENTITY_TOO_LARGE
: MHD_HTTP_REQUEST_URI_TOO_LONG);
} else {
connection->read_buffer_size = connection->read_buffer_size * 2 + MHD_BUF_INC_SIZE;
connection->read_buffer = rbuf;
@@ -265,7 +287,8 @@ MHD_connection_add_header(struct MHD_Connection * connection,
if (hdr == NULL) {
MHD_DLOG(connection->daemon,
"Not enough memory to allocate header record!\n");
MHD_excessive_header_handler(connection);
MHD_excessive_data_handler(connection,
MHD_HTTP_REQUEST_ENTITY_TOO_LARGE);
return MHD_NO;
}
hdr->next = connection->headers_received;
@@ -360,7 +383,8 @@ MHD_parse_cookie_header(struct MHD_Connection * connection) {
if (cpy == NULL) {
MHD_DLOG(connection->daemon,
"Not enough memory to parse cookies!\n");
MHD_excessive_header_handler(connection);
MHD_excessive_data_handler(connection,
MHD_HTTP_REQUEST_ENTITY_TOO_LARGE);
return MHD_NO;
}
memcpy(cpy,
@@ -483,7 +507,8 @@ MHD_parse_connection_headers(struct MHD_Connection * connection) {
strlen(last)+1,
strlen(line) + strlen(last) + 1);
if (last == NULL) {
MHD_excessive_header_handler(connection);
MHD_excessive_data_handler(connection,
MHD_HTTP_REQUEST_ENTITY_TOO_LARGE);
break;
}
tmp = line;
@@ -620,8 +645,10 @@ MHD_test_post_data(struct MHD_Connection * connection) {
const char * encoding;
void * buf;
if (0 != strcasecmp(connection->method,
MHD_HTTP_METHOD_POST))
if ( (connection->method == NULL) ||
(connection->response != NULL) ||
(0 != strcasecmp(connection->method,
MHD_HTTP_METHOD_POST)) )
return MHD_NO;
encoding = MHD_lookup_connection_value(connection,
MHD_HEADER_KIND,
@@ -711,6 +738,8 @@ MHD_call_connection_handler(struct MHD_Connection * connection) {
struct MHD_Access_Handler * ah;
unsigned int processed;
if (connection->response != NULL)
return; /* already queued a response */
if (connection->headersReceived == 0)
abort(); /* bad timing... */
ah = MHD_find_access_handler(connection);
@@ -778,7 +807,8 @@ MHD_connection_handle_read(struct MHD_Connection * connection) {
if (tmp == NULL) {
MHD_DLOG(connection->daemon,
"Not enough memory for reading headers!\n");
MHD_excessive_header_handler(connection);
MHD_excessive_data_handler(connection,
MHD_HTTP_REQUEST_ENTITY_TOO_LARGE);
return MHD_NO;
}
connection->read_buffer = tmp;
@@ -824,8 +854,9 @@ MHD_connection_handle_read(struct MHD_Connection * connection) {
(connection->uploadSize == connection->readLoc) )
if (MHD_NO == MHD_parse_post_data(connection))
connection->post_processed = MHD_NO;
if ( (connection->post_processed == MHD_NO) ||
(connection->read_buffer_size == connection->readLoc) )
if ( ( (connection->post_processed == MHD_NO) ||
(connection->read_buffer_size == connection->readLoc) ) &&
(connection->method != NULL) )
MHD_call_connection_handler(connection);
}
return MHD_YES;
@@ -1065,7 +1096,7 @@ MHD_connection_handle_write(struct MHD_Connection * connection) {
connection->messagePos = 0;
connection->method = NULL;
connection->url = NULL;
if ( (connection->read_close != 0) ||
if ( (connection->read_close == MHD_YES) ||
(0 != strcasecmp(MHD_HTTP_VERSION_1_1,
connection->version)) ) {
/* closed for reading => close for good! */
+287
View File
@@ -0,0 +1,287 @@
/*
This file is part of libmicrohttpd
(C) 2007 Christian Grothoff
libmicrohttpd is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published
by the Free Software Foundation; either version 2, or (at your
option) any later version.
libmicrohttpd is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU General Public License
along with libmicrohttpd; see the file COPYING. If not, write to the
Free Software Foundation, Inc., 59 Temple Place - Suite 330,
Boston, MA 02111-1307, USA.
*/
/**
* @file daemontest_long_header.c
* @brief Testcase for libmicrohttpd handling of very long headers
* @author Christian Grothoff
*/
#include "config.h"
#include <curl/curl.h>
#include <microhttpd.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <time.h>
/**
* We will set the memory available per connection to
* half of this value, so the actual value does not have
* to be big at all...
*/
#define VERY_LONG (1024*10)
static int oneone;
static int apc_all(void * cls,
const struct sockaddr * addr,
socklen_t addrlen) {
return MHD_YES;
}
struct CBC {
char * buf;
size_t pos;
size_t size;
};
static size_t copyBuffer(void * ptr,
size_t size,
size_t nmemb,
void * ctx) {
return size * nmemb;
}
static int ahc_echo(void * cls,
struct MHD_Connection * connection,
const char * url,
const char * method,
const char * version,
const char * upload_data,
unsigned int * upload_data_size) {
const char * me = cls;
struct MHD_Response * response;
int ret;
if (0 != strcmp(me, method))
return MHD_NO; /* unexpected method */
response = MHD_create_response_from_data(strlen(url),
(void*) url,
MHD_NO,
MHD_YES);
ret = MHD_queue_response(connection,
MHD_HTTP_OK,
response);
MHD_destroy_response(response);
return ret;
}
static int testLongUrlGet() {
struct MHD_Daemon * d;
CURL * c;
char buf[2048];
struct CBC cbc;
char * url;
long code;
cbc.buf = buf;
cbc.size = 2048;
cbc.pos = 0;
d = MHD_start_daemon(MHD_USE_SELECT_INTERNALLY /* | MHD_USE_DEBUG */,
1080,
&apc_all,
NULL,
&ahc_echo,
"GET",
MHD_OPTION_CONNECTION_MEMORY_LIMIT,
VERY_LONG / 2,
MHD_OPTION_END);
if (d == NULL)
return 1;
c = curl_easy_init();
url = malloc(VERY_LONG);
memset(url,
'a',
VERY_LONG);
url[VERY_LONG-1] = '\0';
memcpy(url,
"http://localhost:1080/",
strlen("http://localhost:1080/"));
curl_easy_setopt(c,
CURLOPT_URL,
url);
curl_easy_setopt(c,
CURLOPT_WRITEFUNCTION,
&copyBuffer);
curl_easy_setopt(c,
CURLOPT_WRITEDATA,
&cbc);
curl_easy_setopt(c,
CURLOPT_FAILONERROR,
1);
curl_easy_setopt(c,
CURLOPT_TIMEOUT,
2L);
curl_easy_setopt(c,
CURLOPT_CONNECTTIMEOUT,
2L);
if (oneone)
curl_easy_setopt(c,
CURLOPT_HTTP_VERSION,
CURL_HTTP_VERSION_1_1);
else
curl_easy_setopt(c,
CURLOPT_HTTP_VERSION,
CURL_HTTP_VERSION_1_0);
// NOTE: use of CONNECTTIMEOUT without also
// setting NOSIGNAL results in really weird
// crashes on my system!
curl_easy_setopt(c,
CURLOPT_NOSIGNAL,
1);
if (CURLE_OK == curl_easy_perform(c)) {
curl_easy_cleanup(c);
MHD_stop_daemon(d);
free(url);
return 2;
}
if (CURLE_OK != curl_easy_getinfo(c,
CURLINFO_RESPONSE_CODE,
&code)) {
curl_easy_cleanup(c);
MHD_stop_daemon(d);
free(url);
return 4;
}
curl_easy_cleanup(c);
MHD_stop_daemon(d);
free(url);
if (code != MHD_HTTP_REQUEST_URI_TOO_LONG)
return 8;
return 0;
}
static int testLongHeaderGet() {
struct MHD_Daemon * d;
CURL * c;
char buf[2048];
struct CBC cbc;
char * url;
long code;
struct curl_slist * header = NULL;
cbc.buf = buf;
cbc.size = 2048;
cbc.pos = 0;
d = MHD_start_daemon(MHD_USE_SELECT_INTERNALLY /* | MHD_USE_DEBUG */,
1080,
&apc_all,
NULL,
&ahc_echo,
"GET",
MHD_OPTION_CONNECTION_MEMORY_LIMIT,
VERY_LONG / 2,
MHD_OPTION_END);
if (d == NULL)
return 16;
c = curl_easy_init();
url = malloc(VERY_LONG);
memset(url,
'a',
VERY_LONG);
url[VERY_LONG-1] = '\0';
url[VERY_LONG/2] = ':';
url[VERY_LONG/2+1] = ':';
header = curl_slist_append(header,
url);
curl_easy_setopt(c,
CURLOPT_HTTPHEADER,
header);
curl_easy_setopt(c,
CURLOPT_URL,
"http://localhost:1080/hello_world");
curl_easy_setopt(c,
CURLOPT_WRITEFUNCTION,
&copyBuffer);
curl_easy_setopt(c,
CURLOPT_WRITEDATA,
&cbc);
curl_easy_setopt(c,
CURLOPT_FAILONERROR,
1);
curl_easy_setopt(c,
CURLOPT_TIMEOUT,
2L);
curl_easy_setopt(c,
CURLOPT_CONNECTTIMEOUT,
2L);
if (oneone)
curl_easy_setopt(c,
CURLOPT_HTTP_VERSION,
CURL_HTTP_VERSION_1_1);
else
curl_easy_setopt(c,
CURLOPT_HTTP_VERSION,
CURL_HTTP_VERSION_1_0);
// NOTE: use of CONNECTTIMEOUT without also
// setting NOSIGNAL results in really weird
// crashes on my system!
curl_easy_setopt(c,
CURLOPT_NOSIGNAL,
1);
if (CURLE_OK == curl_easy_perform(c)) {
curl_easy_cleanup(c);
MHD_stop_daemon(d);
curl_slist_free_all(header);
free(url);
return 32;
}
if (CURLE_OK != curl_easy_getinfo(c,
CURLINFO_RESPONSE_CODE,
&code)) {
curl_slist_free_all(header);
curl_easy_cleanup(c);
MHD_stop_daemon(d);
free(url);
return 64;
}
curl_slist_free_all(header);
curl_easy_cleanup(c);
MHD_stop_daemon(d);
free(url);
if (code != MHD_HTTP_REQUEST_ENTITY_TOO_LARGE)
return 128;
return 0;
}
int main(int argc,
char * const * argv) {
unsigned int errorCount = 0;
oneone = NULL != strstr(argv[0], "11");
if (0 != curl_global_init(CURL_GLOBAL_WIN32))
return 2;
errorCount += testLongUrlGet();
errorCount += testLongHeaderGet();
if (errorCount != 0)
fprintf(stderr,
"Error (code: %u)\n",
errorCount);
curl_global_cleanup();
return errorCount != 0; /* 0 == pass */
}