mirror of
https://git.gnunet.org/libmicrohttpd.git
synced 2026-09-25 04:09:31 +03:00
updates
This commit is contained in:
@@ -1,2 +1,6 @@
|
||||
Wed Aug 8 01:46:06 MDT 2007
|
||||
Added pool allocation and connection limitations (total
|
||||
number and memory size).
|
||||
|
||||
Tue Jan 9 20:52:48 MST 2007
|
||||
Created project build files and updated API. - CG
|
||||
|
||||
@@ -1,32 +1,25 @@
|
||||
Run "autoreconf -f -i" to create configure.
|
||||
Run "autoreconf -fi" to create configure.
|
||||
|
||||
This is still pre-alpha software. The following
|
||||
things need to be implemented (in list of importance)
|
||||
before certain features can be used at all:
|
||||
This is still pre-alpha software. The following things should be
|
||||
implemented (in order of importance) before we can claim to be
|
||||
reasonably complete:
|
||||
|
||||
|
||||
For http/1.1-compliance:
|
||||
========================
|
||||
connection.c:
|
||||
- support responding immediately with "100 CONTINUE" (http 1.1)!
|
||||
- send proper error code back if headers are too long
|
||||
(currently, we just close the connection)
|
||||
- support chunked requests from clients
|
||||
- send proper error code back if client forgot the "Host" header (?)
|
||||
- automatically add MHD_HTTP_HEADER_DATE if client "forgot" to add one (?)
|
||||
- automatically drop body from responses to "HEAD" requests?
|
||||
|
||||
For POST:
|
||||
=========
|
||||
- Decoding of POST data, testing thereof
|
||||
- POST testcase currently fails (blocks!)
|
||||
|
||||
For http-compliance:
|
||||
====================
|
||||
session.c:
|
||||
- send proper error code back if headers are too long
|
||||
(investigate what we should do with those headers,
|
||||
read? give user control?)
|
||||
ALSO: should this limit be per-line or for the
|
||||
entire header? (currently, we enforce per-line,
|
||||
but the entire header might make more sense!)
|
||||
- http 1.0 compatibility (if 1.0, force connection
|
||||
close at the end!)
|
||||
|
||||
For IPv6:
|
||||
=========
|
||||
daemon.c:
|
||||
- fix start daemon and accept handlers
|
||||
|
||||
- find better way to handle POST data that does not fit into memory
|
||||
- add support to decode multipart/form-data
|
||||
|
||||
For SSL:
|
||||
========
|
||||
@@ -34,13 +27,8 @@ microhttpd.h:
|
||||
- define appropriate MHD_OPTIONs
|
||||
- actual implementation
|
||||
|
||||
|
||||
Other:
|
||||
======
|
||||
- allow client to control size of input/output
|
||||
buffers (add MHD_OPTION)
|
||||
- allow client to limit total number of connections
|
||||
(add MHD_OPTION)
|
||||
|
||||
|
||||
|
||||
Missing Testcases:
|
||||
==================
|
||||
- add testcases for http/1.1 pipelining (need
|
||||
to figure out how to ensure curl pipelines)
|
||||
- add testcases for resource limit enforcement
|
||||
|
||||
+17
-3
@@ -11,6 +11,7 @@ libmicrohttpd_la_SOURCES = \
|
||||
connection.c connection.h \
|
||||
daemon.c \
|
||||
internal.c internal.h \
|
||||
memorypool.c memorypool.h \
|
||||
plibc.h \
|
||||
response.c response.h
|
||||
|
||||
@@ -31,7 +32,9 @@ check_PROGRAMS = \
|
||||
daemontest \
|
||||
daemontest_get \
|
||||
daemontest_post \
|
||||
daemontest_put
|
||||
daemontest_put \
|
||||
daemontest_post11 \
|
||||
daemontest_put11
|
||||
|
||||
TESTS = $(check_PROGRAMS)
|
||||
|
||||
@@ -46,18 +49,29 @@ daemontest_get_LDADD = \
|
||||
$(top_builddir)/src/daemon/libmicrohttpd.la \
|
||||
@LIBCURL@
|
||||
|
||||
|
||||
daemontest_post_SOURCES = \
|
||||
daemontest_post.c
|
||||
daemontest_post_LDADD = \
|
||||
$(top_builddir)/src/daemon/libmicrohttpd.la \
|
||||
@LIBCURL@
|
||||
|
||||
|
||||
daemontest_put_SOURCES = \
|
||||
daemontest_put.c
|
||||
daemontest_put_LDADD = \
|
||||
$(top_builddir)/src/daemon/libmicrohttpd.la \
|
||||
@LIBCURL@
|
||||
|
||||
daemontest_post11_SOURCES = \
|
||||
daemontest_post.c
|
||||
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@
|
||||
|
||||
endif
|
||||
+341
-142
@@ -27,8 +27,14 @@
|
||||
|
||||
#include "internal.h"
|
||||
#include "connection.h"
|
||||
#include "memorypool.h"
|
||||
#include "response.h"
|
||||
|
||||
/**
|
||||
* Size by which MHD usually tries to increment read/write buffers.
|
||||
*/
|
||||
#define MHD_BUF_INC_SIZE 2048
|
||||
|
||||
|
||||
/**
|
||||
* Get all of the headers from the request.
|
||||
@@ -40,9 +46,9 @@
|
||||
*/
|
||||
int
|
||||
MHD_get_connection_values(struct MHD_Connection * connection,
|
||||
enum MHD_ValueKind kind,
|
||||
MHD_KeyValueIterator iterator,
|
||||
void * iterator_cls) {
|
||||
enum MHD_ValueKind kind,
|
||||
MHD_KeyValueIterator iterator,
|
||||
void * iterator_cls) {
|
||||
int ret;
|
||||
struct MHD_HTTP_Header * pos;
|
||||
|
||||
@@ -131,41 +137,77 @@ MHD_connection_get_fdset(struct MHD_Connection * connection,
|
||||
fd_set * except_fd_set,
|
||||
int * max_fd) {
|
||||
int fd;
|
||||
void * buf;
|
||||
|
||||
fd = connection->socket_fd;
|
||||
if (fd == -1)
|
||||
return MHD_YES;
|
||||
if ( (connection->read_close == 0) &&
|
||||
( (connection->headersReceived == 0) ||
|
||||
(connection->readLoc < connection->read_buffer_size) ) )
|
||||
(connection->readLoc < connection->read_buffer_size) ) ) {
|
||||
FD_SET(fd, read_fd_set);
|
||||
if (connection->response != NULL)
|
||||
if (fd > *max_fd)
|
||||
*max_fd = fd;
|
||||
} else {
|
||||
|
||||
|
||||
if ( (connection->read_close == 0) &&
|
||||
( (connection->headersReceived == 1) &&
|
||||
(connection->post_processed == MHD_NO) &&
|
||||
(connection->readLoc == connection->read_buffer_size) ) ) {
|
||||
/* try growing the read buffer, just in case */
|
||||
buf = MHD_pool_reallocate(connection->pool,
|
||||
connection->read_buffer,
|
||||
connection->read_buffer_size,
|
||||
connection->read_buffer_size * 2 + MHD_BUF_INC_SIZE);
|
||||
if (buf != NULL) {
|
||||
/* we can actually grow the buffer, do it! */
|
||||
connection->read_buffer = buf;
|
||||
connection->read_buffer_size = connection->read_buffer_size * 2 + MHD_BUF_INC_SIZE;
|
||||
FD_SET(fd, read_fd_set);
|
||||
if (fd > *max_fd)
|
||||
*max_fd = fd;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (connection->response != NULL) {
|
||||
FD_SET(fd, write_fd_set);
|
||||
if ( (fd > *max_fd) &&
|
||||
( (connection->headersReceived == 0) ||
|
||||
(connection->readLoc < connection->read_buffer_size) ||
|
||||
(connection->response != NULL) ) )
|
||||
*max_fd = fd;
|
||||
if (fd > *max_fd)
|
||||
*max_fd = fd;
|
||||
}
|
||||
return MHD_YES;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse a single line of the HTTP header. Remove it
|
||||
* from the read buffer. If the current line does not
|
||||
* We ran out of memory processing the
|
||||
* header. Handle it properly.
|
||||
*/
|
||||
static void
|
||||
MHD_excessive_header_handler(struct MHD_Connection * connection) {
|
||||
/* die, header far too long to be reasonable;
|
||||
FIXME: send proper response to client
|
||||
(stop reading, queue proper response) */
|
||||
MHD_DLOG(connection->daemon,
|
||||
"Received excessively long header line, closing connection.\n");
|
||||
CLOSE(connection->socket_fd);
|
||||
connection->socket_fd = -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse a single line of the HTTP header. Advance
|
||||
* read_buffer (!) appropriately. If the current line does not
|
||||
* fit, consider growing the buffer. If the line is
|
||||
* far too long, close the connection. If no line is
|
||||
* found (incomplete, buffer too small, line too long),
|
||||
* return NULL. Otherwise return a copy of the line.
|
||||
* return NULL. Otherwise return a pointer to the line.
|
||||
*/
|
||||
static char *
|
||||
MHD_get_next_header_line(struct MHD_Connection * connection) {
|
||||
char * rbuf;
|
||||
size_t pos;
|
||||
size_t start;
|
||||
|
||||
if (connection->readLoc == 0)
|
||||
return NULL;
|
||||
start = 0;
|
||||
pos = 0;
|
||||
rbuf = connection->read_buffer;
|
||||
while ( (pos < connection->readLoc - 1) &&
|
||||
@@ -175,56 +217,55 @@ MHD_get_next_header_line(struct MHD_Connection * connection) {
|
||||
if (pos == connection->readLoc - 1) {
|
||||
/* not found, consider growing... */
|
||||
if (connection->readLoc == connection->read_buffer_size) {
|
||||
/* grow buffer to read larger header or die... */
|
||||
if (connection->read_buffer_size < 4 * MHD_MAX_BUF_SIZE) {
|
||||
rbuf = malloc(connection->read_buffer_size * 2);
|
||||
memcpy(rbuf,
|
||||
connection->read_buffer,
|
||||
connection->readLoc);
|
||||
free(connection->read_buffer);
|
||||
connection->read_buffer = rbuf;
|
||||
connection->read_buffer_size *= 2;
|
||||
rbuf = MHD_pool_reallocate(connection->pool,
|
||||
connection->read_buffer,
|
||||
connection->read_buffer_size,
|
||||
connection->read_buffer_size * 2 + MHD_BUF_INC_SIZE);
|
||||
if (rbuf == NULL) {
|
||||
MHD_excessive_header_handler(connection);
|
||||
} else {
|
||||
/* die, header far too long to be reasonable */
|
||||
MHD_DLOG(connection->daemon,
|
||||
"Received excessively long header line (>%u), closing connection.\n",
|
||||
4 * MHD_MAX_BUF_SIZE);
|
||||
CLOSE(connection->socket_fd);
|
||||
connection->socket_fd = -1;
|
||||
connection->read_buffer_size = connection->read_buffer_size * 2 + MHD_BUF_INC_SIZE;
|
||||
connection->read_buffer = rbuf;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
/* found, check if we have proper CRLF */
|
||||
rbuf = malloc(pos + 1);
|
||||
memcpy(rbuf,
|
||||
connection->read_buffer,
|
||||
pos);
|
||||
rbuf[pos] = '\0';
|
||||
if ( (connection->read_buffer[pos] == '\r') &&
|
||||
(connection->read_buffer[pos+1] == '\n') )
|
||||
pos++; /* skip both r and n */
|
||||
pos++;
|
||||
memmove(connection->read_buffer,
|
||||
&connection->read_buffer[pos],
|
||||
connection->readLoc - pos);
|
||||
if ( (rbuf[pos] == '\r') &&
|
||||
(rbuf[pos+1] == '\n') )
|
||||
rbuf[pos++] = '\0'; /* skip both r and n */
|
||||
rbuf[pos++] = '\0';
|
||||
connection->read_buffer += pos;
|
||||
connection->read_buffer_size -= pos;
|
||||
connection->readLoc -= pos;
|
||||
return rbuf;
|
||||
}
|
||||
|
||||
static void
|
||||
/**
|
||||
* @return MHD_NO on failure (out of memory), MHD_YES for success
|
||||
*/
|
||||
static int
|
||||
MHD_connection_add_header(struct MHD_Connection * connection,
|
||||
const char * key,
|
||||
const char * value,
|
||||
enum MHD_ValueKind kind) {
|
||||
char * key,
|
||||
char * value,
|
||||
enum MHD_ValueKind kind) {
|
||||
struct MHD_HTTP_Header * hdr;
|
||||
|
||||
hdr = malloc(sizeof(struct MHD_HTTP_Header));
|
||||
hdr = MHD_pool_allocate(connection->pool,
|
||||
sizeof(struct MHD_HTTP_Header),
|
||||
MHD_YES);
|
||||
if (hdr == NULL) {
|
||||
MHD_DLOG(connection->daemon,
|
||||
"Not enough memory to allocate header record!\n");
|
||||
MHD_excessive_header_handler(connection);
|
||||
return MHD_NO;
|
||||
}
|
||||
hdr->next = connection->headers_received;
|
||||
hdr->header = strdup(key);
|
||||
hdr->value = strdup(value);
|
||||
hdr->header = key;
|
||||
hdr->value = value;
|
||||
hdr->kind = kind;
|
||||
connection->headers_received = hdr;
|
||||
return MHD_YES;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -253,7 +294,10 @@ MHD_http_unescape(char * val) {
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
/**
|
||||
* @return MHD_NO on failure (out of memory), MHD_YES for success
|
||||
*/
|
||||
static int
|
||||
parse_arguments(enum MHD_ValueKind kind,
|
||||
struct MHD_Connection * connection,
|
||||
char * args) {
|
||||
@@ -263,7 +307,7 @@ parse_arguments(enum MHD_ValueKind kind,
|
||||
while (args != NULL) {
|
||||
equals = strstr(args, "=");
|
||||
if (equals == NULL)
|
||||
return; /* invalid, ignore */
|
||||
return MHD_NO; /* invalid, ignore */
|
||||
equals[0] = '\0';
|
||||
equals++;
|
||||
amper = strstr(equals, "&");
|
||||
@@ -273,18 +317,22 @@ parse_arguments(enum MHD_ValueKind kind,
|
||||
}
|
||||
MHD_http_unescape(args);
|
||||
MHD_http_unescape(equals);
|
||||
MHD_connection_add_header(connection,
|
||||
args,
|
||||
equals,
|
||||
kind);
|
||||
if (MHD_NO == MHD_connection_add_header(connection,
|
||||
args,
|
||||
equals,
|
||||
kind))
|
||||
return MHD_NO;
|
||||
args = amper;
|
||||
}
|
||||
return MHD_YES;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse the cookie header (see RFC 2109).
|
||||
*
|
||||
* @return MHD_YES for success, MHD_NO for failure (malformed, out of memory)
|
||||
*/
|
||||
static void
|
||||
static int
|
||||
MHD_parse_cookie_header(struct MHD_Connection * connection) {
|
||||
const char * hdr;
|
||||
char * cpy;
|
||||
@@ -294,11 +342,22 @@ MHD_parse_cookie_header(struct MHD_Connection * connection) {
|
||||
int quotes;
|
||||
|
||||
hdr = MHD_lookup_connection_value(connection,
|
||||
MHD_HEADER_KIND,
|
||||
"Cookie");
|
||||
MHD_HEADER_KIND,
|
||||
"Cookie");
|
||||
if (hdr == NULL)
|
||||
return;
|
||||
cpy = strdup(hdr);
|
||||
return MHD_YES;
|
||||
cpy = MHD_pool_allocate(connection->pool,
|
||||
strlen(hdr)+1,
|
||||
MHD_YES);
|
||||
if (cpy == NULL) {
|
||||
MHD_DLOG(connection->daemon,
|
||||
"Not enough memory to parse cookies!\n");
|
||||
MHD_excessive_header_handler(connection);
|
||||
return MHD_NO;
|
||||
}
|
||||
memcpy(cpy,
|
||||
hdr,
|
||||
strlen(hdr)+1);
|
||||
pos = cpy;
|
||||
while (pos != NULL) {
|
||||
equals = strstr(pos, "=");
|
||||
@@ -328,13 +387,14 @@ MHD_parse_cookie_header(struct MHD_Connection * connection) {
|
||||
equals[strlen(equals)-1] = '\0';
|
||||
equals++;
|
||||
}
|
||||
MHD_connection_add_header(connection,
|
||||
pos,
|
||||
equals,
|
||||
MHD_COOKIE_KIND);
|
||||
if (MHD_NO == MHD_connection_add_header(connection,
|
||||
pos,
|
||||
equals,
|
||||
MHD_COOKIE_KIND))
|
||||
return MHD_NO;
|
||||
pos = semicolon;
|
||||
}
|
||||
free(cpy);
|
||||
return MHD_YES;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -355,7 +415,7 @@ parse_initial_message_line(struct MHD_Connection * connection,
|
||||
if (uri == NULL)
|
||||
return MHD_NO; /* serious error */
|
||||
uri[0] = '\0';
|
||||
connection->method = strdup(line);
|
||||
connection->method = line;
|
||||
uri++;
|
||||
while (uri[0] == ' ')
|
||||
uri++;
|
||||
@@ -372,11 +432,11 @@ parse_initial_message_line(struct MHD_Connection * connection,
|
||||
connection,
|
||||
args);
|
||||
}
|
||||
connection->url = strdup(uri);
|
||||
connection->url = uri;
|
||||
if (httpVersion == NULL)
|
||||
connection->version = strdup("");
|
||||
connection->version = "";
|
||||
else
|
||||
connection->version = strdup(httpVersion);
|
||||
connection->version = httpVersion;
|
||||
return MHD_YES;
|
||||
}
|
||||
|
||||
@@ -398,6 +458,7 @@ MHD_parse_connection_headers(struct MHD_Connection * connection) {
|
||||
char * colon;
|
||||
char * tmp;
|
||||
const char * clen;
|
||||
const char * end;
|
||||
unsigned long long cval;
|
||||
|
||||
if (connection->bodyReceived == 1)
|
||||
@@ -409,51 +470,38 @@ MHD_parse_connection_headers(struct MHD_Connection * connection) {
|
||||
(line[0] == '\t') ) {
|
||||
/* value was continued on the next line, see
|
||||
http://www.jmarshall.com/easy/http/ */
|
||||
if ( (strlen(line) + strlen(last) >
|
||||
4 * MHD_MAX_BUF_SIZE) ) {
|
||||
free(line);
|
||||
free(last);
|
||||
last = NULL;
|
||||
MHD_DLOG(connection->daemon,
|
||||
"Received excessively long header line (>%u), closing connection.\n",
|
||||
4 * MHD_MAX_BUF_SIZE);
|
||||
CLOSE(connection->socket_fd);
|
||||
connection->socket_fd = -1;
|
||||
last = MHD_pool_reallocate(connection->pool,
|
||||
last,
|
||||
strlen(last)+1,
|
||||
strlen(line) + strlen(last) + 1);
|
||||
if (last == NULL) {
|
||||
MHD_excessive_header_handler(connection);
|
||||
break;
|
||||
}
|
||||
tmp = malloc(strlen(line) + strlen(last) + 1);
|
||||
strcpy(tmp, last);
|
||||
free(last);
|
||||
last = tmp;
|
||||
tmp = line;
|
||||
while ( (tmp[0] == ' ') ||
|
||||
(tmp[0] == '\t') )
|
||||
tmp++; /* skip whitespace at start of 2nd line */
|
||||
strcat(last, tmp);
|
||||
free(line);
|
||||
strcat(last, tmp);
|
||||
continue; /* possibly more than 2 lines... */
|
||||
} else {
|
||||
MHD_connection_add_header(connection,
|
||||
last,
|
||||
colon,
|
||||
MHD_HEADER_KIND);
|
||||
free(last);
|
||||
if (MHD_NO == MHD_connection_add_header(connection,
|
||||
last,
|
||||
colon,
|
||||
MHD_HEADER_KIND))
|
||||
return;
|
||||
last = NULL;
|
||||
}
|
||||
}
|
||||
if (connection->url == NULL) {
|
||||
/* line must be request line (first line of header) */
|
||||
if (MHD_NO == parse_initial_message_line(connection,
|
||||
line)) {
|
||||
free(line);
|
||||
goto DIE;
|
||||
}
|
||||
free(line);
|
||||
line))
|
||||
goto DIE;
|
||||
continue;
|
||||
}
|
||||
/* check if this is the end of the header */
|
||||
if (strlen(line) == 0) {
|
||||
free(line);
|
||||
/* end of header */
|
||||
connection->headersReceived = 1;
|
||||
clen = MHD_lookup_connection_value(connection,
|
||||
@@ -483,6 +531,17 @@ MHD_parse_connection_headers(struct MHD_Connection * connection) {
|
||||
connection->bodyReceived = 0;
|
||||
}
|
||||
}
|
||||
end = MHD_lookup_connection_value(connection,
|
||||
MHD_HEADER_KIND,
|
||||
MHD_HTTP_HEADER_CONNECTION);
|
||||
if ( (end != NULL) &&
|
||||
(0 == strcasecmp(end,
|
||||
"close")) ) {
|
||||
/* other side explicitly requested
|
||||
that we close the connection after
|
||||
this request */
|
||||
connection->read_close = MHD_YES;
|
||||
}
|
||||
break;
|
||||
}
|
||||
/* line should be normal header line, find colon */
|
||||
@@ -507,13 +566,12 @@ MHD_parse_connection_headers(struct MHD_Connection * connection) {
|
||||
with a space...) */
|
||||
last = line;
|
||||
}
|
||||
if (last != NULL) {
|
||||
MHD_connection_add_header(connection,
|
||||
last,
|
||||
colon,
|
||||
MHD_HEADER_KIND);
|
||||
free(last);
|
||||
}
|
||||
if ( (last != NULL) &&
|
||||
(MHD_NO == MHD_connection_add_header(connection,
|
||||
last,
|
||||
colon,
|
||||
MHD_HEADER_KIND)) )
|
||||
return; /* error */
|
||||
MHD_parse_cookie_header(connection);
|
||||
return;
|
||||
DIE:
|
||||
@@ -539,6 +597,103 @@ MHD_find_access_handler(struct MHD_Connection * connection) {
|
||||
return &connection->daemon->default_handler;
|
||||
}
|
||||
|
||||
/**
|
||||
* Test if we are able to process the POST data.
|
||||
* This depends on available memory (enough to load
|
||||
* all of the POST data into the pool) and the
|
||||
* content encoding of the POST data. And of course,
|
||||
* this requires that the request is actually a
|
||||
* POST request.
|
||||
*
|
||||
* @return MHD_YES if so
|
||||
*/
|
||||
static int
|
||||
MHD_test_post_data(struct MHD_Connection * connection) {
|
||||
const char * encoding;
|
||||
void * buf;
|
||||
|
||||
if (0 != strcasecmp(connection->method,
|
||||
MHD_HTTP_METHOD_POST))
|
||||
return MHD_NO;
|
||||
encoding = MHD_lookup_connection_value(connection,
|
||||
MHD_HEADER_KIND,
|
||||
MHD_HTTP_HEADER_CONTENT_TYPE);
|
||||
if (encoding == NULL)
|
||||
return MHD_NO;
|
||||
if ( (0 == strcasecmp(MHD_HTTP_POST_ENCODING_FORM_URLENCODED,
|
||||
encoding)) &&
|
||||
(connection->uploadSize != -1) ) {
|
||||
buf = MHD_pool_reallocate(connection->pool,
|
||||
connection->read_buffer,
|
||||
connection->read_buffer_size,
|
||||
connection->uploadSize + 1);
|
||||
if (buf == NULL)
|
||||
return MHD_NO;
|
||||
connection->read_buffer_size = connection->uploadSize + 1;
|
||||
connection->read_buffer = buf;
|
||||
return MHD_YES;
|
||||
}
|
||||
return MHD_NO;
|
||||
}
|
||||
|
||||
/**
|
||||
* Process the POST data here (adding to headers).
|
||||
*
|
||||
* Needs to first check POST encoding and then do
|
||||
* the right thing (TM). The POST data is in the
|
||||
* connection's post_data buffer between the postPos
|
||||
* and postLoc offsets. The POST message maybe
|
||||
* incomplete. The existing buffer (allocated from
|
||||
* the pool) can be used and modified but must then
|
||||
* be properly removed from the struct.
|
||||
*
|
||||
* @return MHD_YES on success, MHD_NO on error (i.e. out of
|
||||
* memory).
|
||||
*/
|
||||
static int
|
||||
MHD_parse_post_data(struct MHD_Connection * connection) {
|
||||
const char * encoding;
|
||||
int ret;
|
||||
|
||||
encoding = MHD_lookup_connection_value(connection,
|
||||
MHD_HEADER_KIND,
|
||||
MHD_HTTP_HEADER_CONTENT_TYPE);
|
||||
if (encoding == NULL)
|
||||
return MHD_NO;
|
||||
if (0 == strcasecmp(MHD_HTTP_POST_ENCODING_FORM_URLENCODED,
|
||||
encoding)) {
|
||||
ret = parse_arguments(MHD_POSTDATA_KIND,
|
||||
connection,
|
||||
connection->read_buffer);
|
||||
/* invalidate read buffer for other uses --
|
||||
in particular, do not give it to the
|
||||
client; if this were to be needed, we would
|
||||
have to make a copy, which would double memory
|
||||
requirements */
|
||||
connection->read_buffer_size = 0;
|
||||
connection->readLoc = 0;
|
||||
connection->uploadSize = 0;
|
||||
connection->read_buffer = NULL;
|
||||
return ret;
|
||||
}
|
||||
if (0 == strcasecmp(MHD_HTTP_POST_ENCODING_MULTIPART_FORMDATA,
|
||||
encoding)) {
|
||||
/* this code should never been reached right now,
|
||||
since the test_post_data function would already
|
||||
return MHD_NO; code is here only for future
|
||||
extensions... */
|
||||
/* see http://www.w3.org/TR/html4/interact/forms.html#h-17.13.4 */
|
||||
MHD_DLOG(connection->daemon,
|
||||
"Unsupported multipart encoding of POST data specified, not processing POST data.\n");
|
||||
return MHD_NO;
|
||||
}
|
||||
/* this should never be reached, just here for
|
||||
error checking */
|
||||
MHD_DLOG(connection->daemon,
|
||||
"Unknown encoding of POST data specified, not processing POST data.\n");
|
||||
return MHD_NO;
|
||||
}
|
||||
|
||||
/**
|
||||
* Call the handler of the application for this
|
||||
* connection.
|
||||
@@ -552,9 +707,6 @@ MHD_call_connection_handler(struct MHD_Connection * connection) {
|
||||
abort(); /* bad timing... */
|
||||
ah = MHD_find_access_handler(connection);
|
||||
processed = connection->readLoc;
|
||||
/* FIXME: in case of POST, we need to
|
||||
process the POST data here as well
|
||||
(adding to the header list! */
|
||||
if (MHD_NO == ah->dh(ah->dh_cls,
|
||||
connection,
|
||||
connection->url,
|
||||
@@ -564,7 +716,7 @@ MHD_call_connection_handler(struct MHD_Connection * connection) {
|
||||
&processed)) {
|
||||
/* serios internal error, close connection */
|
||||
MHD_DLOG(connection->daemon,
|
||||
"Internal application error, closing connection.");
|
||||
"Internal application error, closing connection.\n");
|
||||
CLOSE(connection->socket_fd);
|
||||
connection->socket_fd = -1;
|
||||
return;
|
||||
@@ -583,7 +735,6 @@ MHD_call_connection_handler(struct MHD_Connection * connection) {
|
||||
connection->bodyReceived = 1;
|
||||
connection->readLoc = 0;
|
||||
connection->read_buffer_size = 0;
|
||||
free(connection->read_buffer);
|
||||
connection->read_buffer = NULL;
|
||||
}
|
||||
}
|
||||
@@ -600,17 +751,30 @@ MHD_connection_handle_read(struct MHD_Connection * connection) {
|
||||
int bytes_read;
|
||||
void * tmp;
|
||||
|
||||
if (connection->pool == NULL)
|
||||
connection->pool = MHD_pool_create(connection->daemon->pool_size);
|
||||
if (connection->pool == NULL) {
|
||||
MHD_DLOG(connection->daemon,
|
||||
"Failed to create memory pool!\n");
|
||||
CLOSE(connection->socket_fd);
|
||||
connection->socket_fd = -1;
|
||||
return MHD_NO;
|
||||
}
|
||||
if ( (connection->readLoc >= connection->read_buffer_size) &&
|
||||
(connection->headersReceived == 0) ) {
|
||||
/* need to grow read buffer */
|
||||
tmp = malloc(connection->read_buffer_size * 2 + MHD_MAX_BUF_SIZE);
|
||||
memcpy(tmp,
|
||||
connection->read_buffer,
|
||||
connection->read_buffer_size);
|
||||
connection->read_buffer_size = connection->read_buffer_size * 2 + MHD_MAX_BUF_SIZE;
|
||||
if (connection->read_buffer != NULL)
|
||||
free(connection->read_buffer);
|
||||
tmp = MHD_pool_reallocate(connection->pool,
|
||||
connection->read_buffer,
|
||||
connection->read_buffer_size,
|
||||
connection->read_buffer_size * 2 + MHD_BUF_INC_SIZE);
|
||||
if (tmp == NULL) {
|
||||
MHD_DLOG(connection->daemon,
|
||||
"Not enough memory for reading headers!\n");
|
||||
MHD_excessive_header_handler(connection);
|
||||
return MHD_NO;
|
||||
}
|
||||
connection->read_buffer = tmp;
|
||||
connection->read_buffer_size = connection->read_buffer_size * 2 + MHD_BUF_INC_SIZE;
|
||||
}
|
||||
if (connection->readLoc >= connection->read_buffer_size) {
|
||||
MHD_DLOG(connection->daemon,
|
||||
@@ -634,16 +798,28 @@ MHD_connection_handle_read(struct MHD_Connection * connection) {
|
||||
}
|
||||
if (bytes_read == 0) {
|
||||
/* other side closed connection */
|
||||
connection->read_close = MHD_YES;
|
||||
if (connection->readLoc > 0)
|
||||
MHD_call_connection_handler(connection);
|
||||
shutdown(connection->socket_fd, SHUT_RD);
|
||||
return MHD_YES;
|
||||
}
|
||||
connection->readLoc += bytes_read;
|
||||
if (connection->headersReceived == 0)
|
||||
if (connection->headersReceived == 0) {
|
||||
MHD_parse_connection_headers(connection);
|
||||
if (connection->headersReceived == 1)
|
||||
MHD_call_connection_handler(connection);
|
||||
if (connection->headersReceived == 1) {
|
||||
connection->post_processed = MHD_test_post_data(connection);
|
||||
}
|
||||
}
|
||||
if (connection->headersReceived == 1) {
|
||||
if ( (connection->post_processed == MHD_YES) &&
|
||||
(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) )
|
||||
MHD_call_connection_handler(connection);
|
||||
}
|
||||
return MHD_YES;
|
||||
}
|
||||
|
||||
@@ -666,9 +842,9 @@ MHD_add_extra_headers(struct MHD_Connection * connection) {
|
||||
} else if (NULL == MHD_get_response_header(connection->response,
|
||||
MHD_HTTP_HEADER_CONTENT_LENGTH)) {
|
||||
_REAL_SNPRINTF(buf,
|
||||
128,
|
||||
"%llu",
|
||||
(unsigned long long) connection->response->total_size);
|
||||
128,
|
||||
"%llu",
|
||||
(unsigned long long) connection->response->total_size);
|
||||
MHD_add_response_header(connection->response,
|
||||
MHD_HTTP_HEADER_CONTENT_LENGTH,
|
||||
buf);
|
||||
@@ -680,7 +856,7 @@ MHD_add_extra_headers(struct MHD_Connection * connection) {
|
||||
* fill it with all of the headers from the
|
||||
* HTTPd's response.
|
||||
*/
|
||||
static void
|
||||
static int
|
||||
MHD_build_header_response(struct MHD_Connection * connection) {
|
||||
size_t size;
|
||||
size_t off;
|
||||
@@ -702,7 +878,14 @@ MHD_build_header_response(struct MHD_Connection * connection) {
|
||||
pos = pos->next;
|
||||
}
|
||||
/* produce data */
|
||||
data = malloc(size + 1);
|
||||
data = MHD_pool_allocate(connection->pool,
|
||||
size + 1,
|
||||
MHD_YES);
|
||||
if (data == NULL) {
|
||||
MHD_DLOG(connection->daemon,
|
||||
"Not enough memory for write!\n");
|
||||
return MHD_NO;
|
||||
}
|
||||
memcpy(data,
|
||||
code,
|
||||
off);
|
||||
@@ -721,7 +904,10 @@ MHD_build_header_response(struct MHD_Connection * connection) {
|
||||
if (off != size)
|
||||
abort();
|
||||
connection->write_buffer = data;
|
||||
connection->write_buffer_size = size;
|
||||
connection->writeLoc = size;
|
||||
connection->writePos = 0;
|
||||
connection->write_buffer_size = size + 1;
|
||||
return MHD_YES;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -743,11 +929,16 @@ MHD_connection_handle_write(struct MHD_Connection * connection) {
|
||||
return MHD_NO;
|
||||
}
|
||||
if (! connection->headersSent) {
|
||||
if (connection->write_buffer == NULL)
|
||||
MHD_build_header_response(connection);
|
||||
if ( (connection->write_buffer == NULL) &&
|
||||
(MHD_NO == MHD_build_header_response(connection)) ) {
|
||||
/* oops - close! */
|
||||
CLOSE(connection->socket_fd);
|
||||
connection->socket_fd = -1;
|
||||
return MHD_NO;
|
||||
}
|
||||
ret = SEND(connection->socket_fd,
|
||||
&connection->write_buffer[connection->writeLoc],
|
||||
connection->write_buffer_size - connection->writeLoc,
|
||||
&connection->write_buffer[connection->writePos],
|
||||
connection->writeLoc - connection->writePos,
|
||||
0);
|
||||
if (ret < 0) {
|
||||
if (errno == EINTR)
|
||||
@@ -759,13 +950,17 @@ MHD_connection_handle_write(struct MHD_Connection * connection) {
|
||||
connection->socket_fd = -1;
|
||||
return MHD_YES;
|
||||
}
|
||||
connection->writeLoc += ret;
|
||||
if (connection->writeLoc == connection->write_buffer_size) {
|
||||
connection->writePos += ret;
|
||||
if (connection->writeLoc == connection->writePos) {
|
||||
connection->writeLoc = 0;
|
||||
free(connection->write_buffer);
|
||||
connection->writePos = 0;
|
||||
connection->headersSent = 1;
|
||||
MHD_pool_reallocate(connection->pool,
|
||||
connection->write_buffer,
|
||||
connection->write_buffer_size,
|
||||
0);
|
||||
connection->write_buffer = NULL;
|
||||
connection->write_buffer_size = 0;
|
||||
connection->headersSent = 1;
|
||||
}
|
||||
return MHD_YES;
|
||||
}
|
||||
@@ -781,13 +976,13 @@ MHD_connection_handle_write(struct MHD_Connection * connection) {
|
||||
if (response->data_size == 0) {
|
||||
if (response->data != NULL)
|
||||
free(response->data);
|
||||
response->data = malloc(MHD_MAX_BUF_SIZE);
|
||||
response->data_size = MHD_MAX_BUF_SIZE;
|
||||
response->data = malloc(MHD_BUF_INC_SIZE);
|
||||
response->data_size = MHD_BUF_INC_SIZE;
|
||||
}
|
||||
ret = response->crc(response->crc_cls,
|
||||
connection->messagePos,
|
||||
response->data,
|
||||
MAX(MHD_MAX_BUF_SIZE,
|
||||
MAX(MHD_BUF_INC_SIZE,
|
||||
response->data_size - connection->messagePos));
|
||||
if (ret == -1) {
|
||||
/* end of message, signal other side by closing! */
|
||||
@@ -838,22 +1033,26 @@ MHD_connection_handle_write(struct MHD_Connection * connection) {
|
||||
connection->headersSent = 0;
|
||||
connection->bodyReceived = 0;
|
||||
connection->messagePos = 0;
|
||||
free(connection->method);
|
||||
connection->method = NULL;
|
||||
free(connection->url);
|
||||
connection->url = NULL;
|
||||
free(connection->write_buffer);
|
||||
connection->write_buffer = NULL;
|
||||
connection->write_buffer_size = 0;
|
||||
if ( (connection->read_close != 0) ||
|
||||
(0 != strcasecmp(MHD_HTTP_VERSION_1_1,
|
||||
connection->version)) ) {
|
||||
/* closed for reading => close for good! */
|
||||
CLOSE(connection->socket_fd);
|
||||
if (connection->socket_fd != -1)
|
||||
CLOSE(connection->socket_fd);
|
||||
connection->socket_fd = -1;
|
||||
}
|
||||
free(connection->version);
|
||||
connection->version = NULL;
|
||||
connection->read_buffer = NULL;
|
||||
connection->write_buffer = NULL;
|
||||
connection->read_buffer_size = 0;
|
||||
connection->readLoc = 0;
|
||||
connection->write_buffer_size = 0;
|
||||
connection->writePos = 0;
|
||||
connection->writeLoc = 0;
|
||||
MHD_pool_destroy(connection->pool);
|
||||
connection->pool = NULL;
|
||||
}
|
||||
return MHD_YES;
|
||||
}
|
||||
|
||||
+47
-18
@@ -28,9 +28,17 @@
|
||||
#include "internal.h"
|
||||
#include "response.h"
|
||||
#include "connection.h"
|
||||
#include "memorypool.h"
|
||||
|
||||
#define MHD_MAX_CONNECTIONS FD_SETSIZE -4
|
||||
/**
|
||||
* Default connection limit.
|
||||
*/
|
||||
#define MHD_MAX_CONNECTIONS_DEFAULT FD_SETSIZE -4
|
||||
|
||||
/**
|
||||
* Default memory allowed per connection.
|
||||
*/
|
||||
#define MHD_POOL_SIZE_DEFAULT (1024 * 1024)
|
||||
|
||||
/**
|
||||
* Register an access handler for all URIs beginning with uri_prefix.
|
||||
@@ -229,6 +237,13 @@ MHD_accept_connection(struct MHD_Daemon * daemon) {
|
||||
MHD_DLOG(daemon,
|
||||
"Error accepting connection: %s\n",
|
||||
STRERROR(errno));
|
||||
if (s != -1)
|
||||
CLOSE(s); /* just in case */
|
||||
return MHD_NO;
|
||||
}
|
||||
if (daemon->max_connections == 0) {
|
||||
/* above connection limit - reject */
|
||||
CLOSE(s);
|
||||
return MHD_NO;
|
||||
}
|
||||
if (MHD_NO == daemon->apc(daemon->apc_cls,
|
||||
@@ -241,7 +256,13 @@ MHD_accept_connection(struct MHD_Daemon * daemon) {
|
||||
memset(connection,
|
||||
0,
|
||||
sizeof(struct MHD_Connection));
|
||||
connection->pool = NULL;
|
||||
connection->addr = malloc(addrlen);
|
||||
if (connection->addr == NULL) {
|
||||
CLOSE(s);
|
||||
free(connection);
|
||||
return MHD_NO;
|
||||
}
|
||||
memcpy(connection->addr,
|
||||
addr,
|
||||
addrlen);
|
||||
@@ -258,11 +279,13 @@ MHD_accept_connection(struct MHD_Daemon * daemon) {
|
||||
STRERROR(errno));
|
||||
free(connection->addr);
|
||||
CLOSE(s);
|
||||
free(connection->addr);
|
||||
free(connection);
|
||||
return MHD_NO;
|
||||
}
|
||||
connection->next = daemon->connections;
|
||||
daemon->connections = connection;
|
||||
daemon->max_connections--;
|
||||
return MHD_YES;
|
||||
}
|
||||
|
||||
@@ -281,7 +304,6 @@ static void
|
||||
MHD_cleanup_connections(struct MHD_Daemon * daemon) {
|
||||
struct MHD_Connection * pos;
|
||||
struct MHD_Connection * prev;
|
||||
struct MHD_HTTP_Header * hpos;
|
||||
void * unused;
|
||||
|
||||
pos = daemon->connections;
|
||||
@@ -296,25 +318,12 @@ MHD_cleanup_connections(struct MHD_Daemon * daemon) {
|
||||
pthread_kill(pos->pid, SIGALRM);
|
||||
pthread_join(pos->pid, &unused);
|
||||
}
|
||||
free(pos->addr);
|
||||
if (pos->url != NULL)
|
||||
free(pos->url);
|
||||
if (pos->method != NULL)
|
||||
free(pos->method);
|
||||
if (pos->write_buffer != NULL)
|
||||
free(pos->write_buffer);
|
||||
if (pos->read_buffer != NULL)
|
||||
free(pos->read_buffer);
|
||||
while (pos->headers_received != NULL) {
|
||||
hpos = pos->headers_received;
|
||||
pos->headers_received = hpos->next;
|
||||
free(hpos->header);
|
||||
free(hpos->value);
|
||||
free(hpos);
|
||||
}
|
||||
if (pos->response != NULL)
|
||||
MHD_destroy_response(pos->response);
|
||||
MHD_pool_destroy(pos->pool);
|
||||
free(pos->addr);
|
||||
free(pos);
|
||||
daemon->max_connections++;
|
||||
if (prev == NULL)
|
||||
pos = daemon->connections;
|
||||
else
|
||||
@@ -474,6 +483,8 @@ MHD_start_daemon(unsigned int options,
|
||||
struct sockaddr_in6 servaddr6;
|
||||
const struct sockaddr * servaddr;
|
||||
socklen_t addrlen;
|
||||
va_list ap;
|
||||
enum MHD_OPTION opt;
|
||||
|
||||
if ((options & MHD_USE_SSL) != 0)
|
||||
return NULL;
|
||||
@@ -549,6 +560,24 @@ MHD_start_daemon(unsigned int options,
|
||||
retVal->default_handler.dh_cls = dh_cls;
|
||||
retVal->default_handler.uri_prefix = "";
|
||||
retVal->default_handler.next = NULL;
|
||||
retVal->max_connections = MHD_MAX_CONNECTIONS_DEFAULT;
|
||||
retVal->pool_size = MHD_POOL_SIZE_DEFAULT;
|
||||
va_start(ap, dh_cls);
|
||||
while (MHD_OPTION_END != (opt = va_arg(ap, enum MHD_OPTION))) {
|
||||
switch (opt) {
|
||||
case MHD_OPTION_CONNECTION_MEMORY_LIMIT:
|
||||
retVal->pool_size = va_arg(ap, unsigned int);
|
||||
break;
|
||||
case MHD_OPTION_CONNECTION_LIMIT:
|
||||
retVal->max_connections = va_arg(ap, unsigned int);
|
||||
break;
|
||||
default:
|
||||
fprintf(stderr,
|
||||
"Invalid MHD_OPTION argument! (Did you terminate the list with MHD_OPTION_END?)\n");
|
||||
abort();
|
||||
}
|
||||
}
|
||||
va_end(ap);
|
||||
if ( ( (0 != (options & MHD_USE_THREAD_PER_CONNECTION)) ||
|
||||
(0 != (options & MHD_USE_SELECT_INTERNALLY)) ) &&
|
||||
(0 != pthread_create(&retVal->pid,
|
||||
|
||||
@@ -39,6 +39,7 @@
|
||||
|
||||
#define POST_DATA "name=daniel&project=curl"
|
||||
|
||||
static int oneone;
|
||||
|
||||
static int apc_all(void * cls,
|
||||
const struct sockaddr * addr,
|
||||
@@ -76,40 +77,36 @@ static int ahc_echo(void * cls,
|
||||
unsigned int * upload_data_size) {
|
||||
struct MHD_Response * response;
|
||||
int ret;
|
||||
const char * r1;
|
||||
const char * r2;
|
||||
|
||||
if (0 != strcmp("POST", method)) {
|
||||
printf("METHOD: %s\n", method);
|
||||
return MHD_NO; /* unexpected method */
|
||||
}
|
||||
if ( (*upload_data_size < 24) &&
|
||||
(*upload_data_size > 0) )
|
||||
return MHD_YES; /* continue */
|
||||
if (*upload_data_size == 24) {
|
||||
*upload_data_size = 0;
|
||||
if ( (0 != strcmp("daniel",
|
||||
MHD_lookup_connection_value(connection,
|
||||
MHD_POSTDATA_KIND,
|
||||
"name"))) ||
|
||||
(0 != strcmp("curl",
|
||||
MHD_lookup_connection_value(connection,
|
||||
MHD_POSTDATA_KIND,
|
||||
"project"))) ) {
|
||||
printf("POST DATA not processed correctly!\n");
|
||||
return MHD_NO;
|
||||
}
|
||||
|
||||
return MHD_YES; /* continue */
|
||||
r1 = MHD_lookup_connection_value(connection,
|
||||
MHD_POSTDATA_KIND,
|
||||
"name");
|
||||
r2 = MHD_lookup_connection_value(connection,
|
||||
MHD_POSTDATA_KIND,
|
||||
"project");
|
||||
if ( (r1 != NULL) &&
|
||||
(r2 != NULL) &&
|
||||
(0 == strcmp("daniel",
|
||||
r1)) &&
|
||||
(0 == strcmp("curl",
|
||||
r2)) ) {
|
||||
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 MHD_YES; /* done */
|
||||
}
|
||||
/* FIXME: check connection headers... */
|
||||
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;
|
||||
return MHD_YES;
|
||||
}
|
||||
|
||||
|
||||
@@ -118,7 +115,7 @@ static int testInternalPost() {
|
||||
CURL * c;
|
||||
char buf[2048];
|
||||
struct CBC cbc;
|
||||
|
||||
|
||||
cbc.buf = buf;
|
||||
cbc.size = 2048;
|
||||
cbc.pos = 0;
|
||||
@@ -156,9 +153,14 @@ static int testInternalPost() {
|
||||
curl_easy_setopt(c,
|
||||
CURLOPT_TIMEOUT,
|
||||
2L);
|
||||
curl_easy_setopt(c,
|
||||
CURLOPT_HTTP_VERSION,
|
||||
CURL_HTTP_VERSION_1_0);
|
||||
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);
|
||||
curl_easy_setopt(c,
|
||||
CURLOPT_CONNECTTIMEOUT,
|
||||
2L);
|
||||
@@ -233,9 +235,14 @@ static int testMultithreadedPost() {
|
||||
curl_easy_setopt(c,
|
||||
CURLOPT_TIMEOUT,
|
||||
2L);
|
||||
curl_easy_setopt(c,
|
||||
CURLOPT_HTTP_VERSION,
|
||||
CURL_HTTP_VERSION_1_0);
|
||||
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);
|
||||
curl_easy_setopt(c,
|
||||
CURLOPT_CONNECTTIMEOUT,
|
||||
2L);
|
||||
@@ -321,9 +328,14 @@ static int testExternalPost() {
|
||||
curl_easy_setopt(c,
|
||||
CURLOPT_TIMEOUT,
|
||||
5L);
|
||||
curl_easy_setopt(c,
|
||||
CURLOPT_HTTP_VERSION,
|
||||
CURL_HTTP_VERSION_1_0);
|
||||
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);
|
||||
curl_easy_setopt(c,
|
||||
CURLOPT_CONNECTTIMEOUT,
|
||||
5L);
|
||||
@@ -429,11 +441,12 @@ 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 += testInternalPost();
|
||||
errorCount += testMultithreadedPost();
|
||||
errorCount += testExternalPost();
|
||||
errorCount += testExternalPost();
|
||||
if (errorCount != 0)
|
||||
fprintf(stderr,
|
||||
"Error (code: %u)\n",
|
||||
|
||||
@@ -32,6 +32,8 @@
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
|
||||
static int oneone;
|
||||
|
||||
static int apc_all(void * cls,
|
||||
const struct sockaddr * addr,
|
||||
socklen_t addrlen) {
|
||||
@@ -164,9 +166,14 @@ static int testInternalPut() {
|
||||
curl_easy_setopt(c,
|
||||
CURLOPT_TIMEOUT,
|
||||
15L);
|
||||
curl_easy_setopt(c,
|
||||
CURLOPT_HTTP_VERSION,
|
||||
CURL_HTTP_VERSION_1_0);
|
||||
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);
|
||||
curl_easy_setopt(c,
|
||||
CURLOPT_CONNECTTIMEOUT,
|
||||
15L);
|
||||
@@ -246,9 +253,14 @@ static int testMultithreadedPut() {
|
||||
curl_easy_setopt(c,
|
||||
CURLOPT_TIMEOUT,
|
||||
15L);
|
||||
curl_easy_setopt(c,
|
||||
CURLOPT_HTTP_VERSION,
|
||||
CURL_HTTP_VERSION_1_0);
|
||||
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);
|
||||
curl_easy_setopt(c,
|
||||
CURLOPT_CONNECTTIMEOUT,
|
||||
15L);
|
||||
@@ -339,9 +351,14 @@ static int testExternalPut() {
|
||||
curl_easy_setopt(c,
|
||||
CURLOPT_TIMEOUT,
|
||||
15L);
|
||||
curl_easy_setopt(c,
|
||||
CURLOPT_HTTP_VERSION,
|
||||
CURL_HTTP_VERSION_1_0);
|
||||
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);
|
||||
curl_easy_setopt(c,
|
||||
CURLOPT_CONNECTTIMEOUT,
|
||||
15L);
|
||||
@@ -447,6 +464,7 @@ 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 += testInternalPut();
|
||||
|
||||
+78
-9
@@ -37,6 +37,7 @@
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <signal.h>
|
||||
#include <sys/mman.h>
|
||||
|
||||
#include "config.h"
|
||||
#include "plibc.h"
|
||||
@@ -49,8 +50,6 @@
|
||||
|
||||
#include <pthread.h>
|
||||
|
||||
#define MHD_MAX_BUF_SIZE 2048
|
||||
|
||||
#define MAX(a,b) ((a)<(b)) ? (b) : (a)
|
||||
|
||||
|
||||
@@ -157,41 +156,72 @@ struct MHD_Response {
|
||||
|
||||
|
||||
struct MHD_Connection {
|
||||
|
||||
/**
|
||||
* This is a linked list.
|
||||
*/
|
||||
struct MHD_Connection * next;
|
||||
|
||||
/**
|
||||
* Reference to the MHD_Daemon struct.
|
||||
*/
|
||||
struct MHD_Daemon * daemon;
|
||||
|
||||
/**
|
||||
* Linked list of parsed headers.
|
||||
*/
|
||||
struct MHD_HTTP_Header * headers_received;
|
||||
|
||||
/**
|
||||
* Response to transmit (initially NULL).
|
||||
*/
|
||||
struct MHD_Response * response;
|
||||
|
||||
/**
|
||||
* Request method. Should be GET/POST/etc.
|
||||
* The memory pool is created whenever we first read
|
||||
* from the TCP stream and destroyed at the end of
|
||||
* each request (and re-created for the next request).
|
||||
* In the meantime, this pointer is NULL. The
|
||||
* pool is used for all connection-related data
|
||||
* except for the response (which maybe shared between
|
||||
* connections) and the IP address (which persists
|
||||
* across individual requests).
|
||||
*/
|
||||
struct MemoryPool * pool;
|
||||
|
||||
/**
|
||||
* Request method. Should be GET/POST/etc. Allocated
|
||||
* in pool.
|
||||
*/
|
||||
char * method;
|
||||
|
||||
/**
|
||||
* Requested URL (everything after "GET" only).
|
||||
* Requested URL (everything after "GET" only). Allocated
|
||||
* in pool.
|
||||
*/
|
||||
char * url;
|
||||
|
||||
/**
|
||||
* HTTP version string (i.e. http/1.1)
|
||||
* HTTP version string (i.e. http/1.1). Allocated
|
||||
* in pool.
|
||||
*/
|
||||
char * version;
|
||||
|
||||
/**
|
||||
* Buffer for reading requests.
|
||||
* Buffer for reading requests. Allocated
|
||||
* in pool.
|
||||
*/
|
||||
char * read_buffer;
|
||||
|
||||
/**
|
||||
* Buffer for writing response.
|
||||
* Buffer for writing response (headers only). Allocated
|
||||
* in pool.
|
||||
*/
|
||||
char * write_buffer;
|
||||
|
||||
/**
|
||||
* Foreign address (of length addr_len).
|
||||
* Foreign address (of length addr_len). MALLOCED (not
|
||||
* in pool!).
|
||||
*/
|
||||
struct sockaddr_in * addr;
|
||||
|
||||
@@ -201,12 +231,30 @@ struct MHD_Connection {
|
||||
*/
|
||||
pthread_t pid;
|
||||
|
||||
/**
|
||||
* Size of read_buffer (in bytes).
|
||||
*/
|
||||
size_t read_buffer_size;
|
||||
|
||||
/**
|
||||
* Position where we currently append data in
|
||||
* read_buffer (last valid position).
|
||||
*/
|
||||
size_t readLoc;
|
||||
|
||||
/**
|
||||
* Size of write_buffer (in bytes).
|
||||
*/
|
||||
size_t write_buffer_size;
|
||||
|
||||
/**
|
||||
* Offset where we are with sending from write_buffer.
|
||||
*/
|
||||
size_t writePos;
|
||||
|
||||
/**
|
||||
* Last valid location in write_buffer.
|
||||
*/
|
||||
size_t writeLoc;
|
||||
|
||||
/**
|
||||
@@ -263,6 +311,11 @@ struct MHD_Connection {
|
||||
*/
|
||||
int headersSent;
|
||||
|
||||
/**
|
||||
* Are we processing the POST data?
|
||||
*/
|
||||
int post_processed;
|
||||
|
||||
/**
|
||||
* HTTP response code. Only valid if response object
|
||||
* is already set.
|
||||
@@ -279,6 +332,9 @@ struct MHD_Daemon {
|
||||
|
||||
struct MHD_Access_Handler default_handler;
|
||||
|
||||
/**
|
||||
* Linked list of our current connections.
|
||||
*/
|
||||
struct MHD_Connection * connections;
|
||||
|
||||
MHD_AcceptPolicyCallback apc;
|
||||
@@ -300,12 +356,25 @@ struct MHD_Daemon {
|
||||
*/
|
||||
int shutdown;
|
||||
|
||||
/**
|
||||
* Size of the per-connection memory pools.
|
||||
*/
|
||||
unsigned int pool_size;
|
||||
|
||||
/**
|
||||
* Limit on the number of parallel connections.
|
||||
*/
|
||||
unsigned int max_connections;
|
||||
|
||||
/**
|
||||
* Daemon's options.
|
||||
*/
|
||||
enum MHD_OPTION options;
|
||||
|
||||
unsigned short port;
|
||||
/**
|
||||
* Listen port.
|
||||
*/
|
||||
unsigned short port;
|
||||
|
||||
};
|
||||
|
||||
|
||||
@@ -0,0 +1,180 @@
|
||||
/*
|
||||
This file is part of libmicrohttpd
|
||||
(C) 2007 Daniel Pittman
|
||||
|
||||
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 memorypool.c
|
||||
* @brief memory pool
|
||||
* @author Christian Grothoff
|
||||
*/
|
||||
|
||||
#include "memorypool.h"
|
||||
|
||||
struct MemoryPool {
|
||||
|
||||
/**
|
||||
* Pointer to the pool's memory
|
||||
*/
|
||||
char * memory;
|
||||
|
||||
/**
|
||||
* Size of the pool.
|
||||
*/
|
||||
unsigned int size;
|
||||
|
||||
/**
|
||||
* Offset of the first unallocated byte.
|
||||
*/
|
||||
unsigned int pos;
|
||||
|
||||
/**
|
||||
* Offset of the last unallocated byte.
|
||||
*/
|
||||
unsigned int end;
|
||||
|
||||
/**
|
||||
* 0 if pool was malloc'ed, 1 if mmapped.
|
||||
*/
|
||||
int is_mmap;
|
||||
};
|
||||
|
||||
/**
|
||||
* Create a memory pool.
|
||||
*
|
||||
* @param max maximum size of the pool
|
||||
*/
|
||||
struct MemoryPool * MHD_pool_create(unsigned int max) {
|
||||
struct MemoryPool * pool;
|
||||
|
||||
pool = malloc(sizeof(struct MemoryPool));
|
||||
if (pool == NULL)
|
||||
return NULL;
|
||||
pool->memory = MMAP(NULL, max, PROT_READ | PROT_WRITE,
|
||||
MAP_ANONYMOUS, -1, 0);
|
||||
if ( (pool->memory == MAP_FAILED) ||
|
||||
(pool->memory == NULL) ) {
|
||||
pool->memory = malloc(max);
|
||||
if (pool->memory == NULL) {
|
||||
free(pool);
|
||||
return NULL;
|
||||
}
|
||||
pool->is_mmap = 0;
|
||||
} else {
|
||||
pool->is_mmap = 1;
|
||||
}
|
||||
pool->pos = 0;
|
||||
pool->end = max;
|
||||
pool->size = max;
|
||||
return pool;
|
||||
}
|
||||
|
||||
/**
|
||||
* Destroy a memory pool.
|
||||
*/
|
||||
void MHD_pool_destroy(struct MemoryPool * pool) {
|
||||
if (pool == NULL)
|
||||
return;
|
||||
if (pool->is_mmap == 0)
|
||||
free(pool->memory);
|
||||
else
|
||||
MUNMAP(pool->memory, pool->size);
|
||||
free(pool);
|
||||
}
|
||||
|
||||
/**
|
||||
* Allocate size bytes from the pool.
|
||||
* @return NULL if the pool cannot support size more
|
||||
* bytes
|
||||
*/
|
||||
void * MHD_pool_allocate(struct MemoryPool * pool,
|
||||
unsigned int size,
|
||||
int from_end) {
|
||||
void * ret;
|
||||
|
||||
if ( (pool->pos + size > pool->end) ||
|
||||
(pool->pos + size < pool->pos) )
|
||||
return NULL;
|
||||
if (from_end == MHD_YES) {
|
||||
ret = &pool->memory[pool->end - size];
|
||||
pool->end -= size;
|
||||
} else {
|
||||
ret = &pool->memory[pool->pos];
|
||||
pool->pos += size;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reallocate a block of memory obtained from the pool.
|
||||
* This is particularly efficient when growing or
|
||||
* shrinking the block that was last (re)allocated.
|
||||
* If the given block is not the most recenlty
|
||||
* (re)allocated block, the memory of the previous
|
||||
* allocation may be leaked until the pool is
|
||||
* destroyed (and copying the data maybe required).
|
||||
*
|
||||
* @param old the existing block
|
||||
* @param old_size the size of the existing block
|
||||
* @param new_size the new size of the block
|
||||
* @return new address of the block, or
|
||||
* NULL if the pool cannot support new_size
|
||||
* bytes (old continues to be valid for old_size)
|
||||
*/
|
||||
void * MHD_pool_reallocate(struct MemoryPool * pool,
|
||||
void * old,
|
||||
unsigned int old_size,
|
||||
unsigned int new_size) {
|
||||
void * ret;
|
||||
|
||||
if ( (pool->end < old_size) ||
|
||||
(pool->end < new_size) )
|
||||
return NULL; /* unsatisfiable or bogus request */
|
||||
|
||||
if ( (pool->pos >= old_size) &&
|
||||
(&pool->memory[pool->pos - old_size] == old) ) {
|
||||
/* was the previous allocation - optimize! */
|
||||
if (pool->pos + new_size - old_size <= pool->end) {
|
||||
/* fits */
|
||||
pool->pos += new_size - old_size;
|
||||
if (new_size < old_size) /* shrinking - zero again! */
|
||||
memset(&pool->memory[pool->pos],
|
||||
0,
|
||||
old_size - new_size);
|
||||
return old;
|
||||
}
|
||||
/* does not fit */
|
||||
return NULL;
|
||||
}
|
||||
if (new_size <= old_size)
|
||||
return old; /* cannot shrink, no need to move */
|
||||
if ( (pool->pos + new_size >= pool->pos) &&
|
||||
(pool->pos + new_size <= pool->end) ) {
|
||||
/* fits */
|
||||
ret = &pool->memory[pool->pos];
|
||||
memcpy(ret,
|
||||
old,
|
||||
old_size);
|
||||
pool->pos += new_size;
|
||||
return ret;
|
||||
}
|
||||
/* does not fit */
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* end of memorypool.c */
|
||||
@@ -0,0 +1,87 @@
|
||||
/*
|
||||
This file is part of libmicrohttpd
|
||||
(C) 2007 Daniel Pittman
|
||||
|
||||
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 memorypool.h
|
||||
* @brief memory pool; mostly used for efficient (de)allocation
|
||||
* for each connection and bounding memory use for each
|
||||
* request
|
||||
* @author Christian Grothoff
|
||||
*/
|
||||
|
||||
#ifndef MEMORYPOOL_H
|
||||
#define MEMORYPOOL_H
|
||||
|
||||
#include "internal.h"
|
||||
|
||||
/**
|
||||
* Opaque handle for a memory pool.
|
||||
* Pools are not reentrant and must not be used
|
||||
* by multiple threads.
|
||||
*/
|
||||
struct MemoryPool;
|
||||
|
||||
/**
|
||||
* Create a memory pool.
|
||||
*
|
||||
* @param max maximum size of the pool
|
||||
*/
|
||||
struct MemoryPool * MHD_pool_create(unsigned int max);
|
||||
|
||||
/**
|
||||
* Destroy a memory pool.
|
||||
*/
|
||||
void MHD_pool_destroy(struct MemoryPool * pool);
|
||||
|
||||
/**
|
||||
* Allocate size bytes from the pool.
|
||||
*
|
||||
* @param from_end allocate from end of pool (set to MHD_YES);
|
||||
* use this for small, persistent allocations that
|
||||
* will never be reallocated
|
||||
* @return NULL if the pool cannot support size more
|
||||
* bytes
|
||||
*/
|
||||
void * MHD_pool_allocate(struct MemoryPool * pool,
|
||||
unsigned int size,
|
||||
int from_end);
|
||||
|
||||
/**
|
||||
* Reallocate a block of memory obtained from the pool.
|
||||
* This is particularly efficient when growing or
|
||||
* shrinking the block that was last (re)allocated.
|
||||
* If the given block is not the most recenlty
|
||||
* (re)allocated block, the memory of the previous
|
||||
* allocation may be leaked until the pool is
|
||||
* destroyed (and copying the data maybe required).
|
||||
*
|
||||
* @param old the existing block
|
||||
* @param old_size the size of the existing block
|
||||
* @param new_size the new size of the block
|
||||
* @return new address of the block, or
|
||||
* NULL if the pool cannot support new_size
|
||||
* bytes (old continues to be valid for old_size)
|
||||
*/
|
||||
void * MHD_pool_reallocate(struct MemoryPool * pool,
|
||||
void * old,
|
||||
unsigned int old_size,
|
||||
unsigned int new_size);
|
||||
|
||||
#endif
|
||||
@@ -187,7 +187,24 @@ extern "C" {
|
||||
#define MHD_HTTP_VERSION_1_0 "HTTP/1.0"
|
||||
#define MHD_HTTP_VERSION_1_1 "HTTP/1.1"
|
||||
|
||||
/**
|
||||
* HTTP methods
|
||||
*/
|
||||
#define MHD_HTTP_METHOD_CONNECT "CONNECT"
|
||||
#define MHD_HTTP_METHOD_DELETE "DELETE"
|
||||
#define MHD_HTTP_METHOD_GET "GET"
|
||||
#define MHD_HTTP_METHOD_HEAD "HEAD"
|
||||
#define MHD_HTTP_METHOD_OPTIONS "OPTIONS"
|
||||
#define MHD_HTTP_METHOD_POST "POST"
|
||||
#define MHD_HTTP_METHOD_PUT "PUT"
|
||||
#define MHD_HTTP_METHOD_TRACE "TRACE"
|
||||
|
||||
/**
|
||||
* HTTP POST encodings, see also
|
||||
* http://www.w3.org/TR/html4/interact/forms.html#h-17.13.4
|
||||
*/
|
||||
#define MHD_HTTP_POST_ENCODING_FORM_URLENCODED "application/x-www-form-urlencoded"
|
||||
#define MHD_HTTP_POST_ENCODING_MULTIPART_FORMDATA "multipart/form-data"
|
||||
|
||||
/**
|
||||
* Options for the MHD daemon. Note that if neither
|
||||
@@ -248,8 +265,16 @@ enum MHD_OPTION {
|
||||
MHD_OPTION_END = 0,
|
||||
|
||||
/**
|
||||
* FIXME: add options for buffer sizes here...
|
||||
* Maximum memory size per connection (followed by an
|
||||
* unsigned int).
|
||||
*/
|
||||
MHD_OPTION_CONNECTION_MEMORY_LIMIT = 1,
|
||||
|
||||
/**
|
||||
* Maximum number of concurrenct connections to
|
||||
* accept (followed by an unsigned int).
|
||||
*/
|
||||
MHD_OPTION_CONNECTION_LIMIT = 2,
|
||||
|
||||
};
|
||||
|
||||
@@ -265,22 +290,28 @@ enum MHD_ValueKind {
|
||||
MHD_RESPONSE_HEADER_KIND = 0,
|
||||
|
||||
/**
|
||||
* HTTP header
|
||||
* HTTP header.
|
||||
*/
|
||||
MHD_HEADER_KIND = 1,
|
||||
|
||||
/**
|
||||
* Cookies
|
||||
* Cookies. Note that the original HTTP header containing
|
||||
* the cookie(s) will still be available and intact.
|
||||
*/
|
||||
MHD_COOKIE_KIND = 2,
|
||||
|
||||
/**
|
||||
* POST data
|
||||
* POST data. This is available only if a content encoding
|
||||
* supported by MHD is used (currently only URL encoding),
|
||||
* and only if the posted content fits within the available
|
||||
* memory pool. Note that in that case, the upload data
|
||||
* given to the MHD_AccessHandlerCallback will be
|
||||
* empty (since it has already been processed).
|
||||
*/
|
||||
MHD_POSTDATA_KIND = 4,
|
||||
|
||||
/**
|
||||
* GET (URI) arguments
|
||||
* GET (URI) arguments.
|
||||
*/
|
||||
MHD_GET_ARGUMENT_KIND = 8,
|
||||
|
||||
@@ -326,9 +357,16 @@ typedef int
|
||||
* @param url the requested url
|
||||
* @param method the HTTP method used ("GET", "PUT", etc.)
|
||||
* @param version the HTTP version string (i.e. "HTTP/1.1")
|
||||
* @param upload_data the data being uploaded (excluding HEADERS,
|
||||
* for a POST that fits into memory and that is encoded
|
||||
* with a supported encoding, the POST data will NOT be
|
||||
* given in upload_data and is instead available as
|
||||
* part of MHD_get_connection_values; very large POST
|
||||
* data *will* be made available incrementally in
|
||||
* upload_data)
|
||||
* @param upload_data_size set initially to the size of the
|
||||
* upload_data provided; the method must update this
|
||||
* value to the number of bytes NOT processed
|
||||
* value to the number of bytes NOT processed;
|
||||
* @return MHS_YES if the connection was handled successfully,
|
||||
* MHS_NO if the socket must be closed due to a serios
|
||||
* error while handling the request
|
||||
|
||||
Reference in New Issue
Block a user