This commit is contained in:
Christian Grothoff
2007-08-08 08:07:49 +00:00
parent 5b7eec5d2d
commit 0cacf72380
11 changed files with 899 additions and 260 deletions
+4
View File
@@ -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
+22 -34
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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,
+52 -39
View File
@@ -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",
+27 -9
View File
@@ -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
View File
@@ -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;
};
+180
View File
@@ -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 */
+87
View File
@@ -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
+44 -6
View File
@@ -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