[Top][All Lists]
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[GNUnet-SVN] r5980 - in libmicrohttpd: . src/daemon src/include
From: |
gnunet |
Subject: |
[GNUnet-SVN] r5980 - in libmicrohttpd: . src/daemon src/include |
Date: |
Wed, 19 Dec 2007 21:23:00 -0700 (MST) |
Author: grothoff
Date: 2007-12-19 21:23:00 -0700 (Wed, 19 Dec 2007)
New Revision: 5980
Modified:
libmicrohttpd/ChangeLog
libmicrohttpd/README
libmicrohttpd/configure.ac
libmicrohttpd/src/daemon/Makefile.am
libmicrohttpd/src/daemon/connection.c
libmicrohttpd/src/daemon/connection.h
libmicrohttpd/src/daemon/daemon.c
libmicrohttpd/src/daemon/daemontest_get.c
libmicrohttpd/src/daemon/daemontest_large_put.c
libmicrohttpd/src/daemon/daemontest_postform.c
libmicrohttpd/src/daemon/daemontest_put_chunked.c
libmicrohttpd/src/daemon/fileserver_example.c
libmicrohttpd/src/daemon/internal.h
libmicrohttpd/src/daemon/memorypool.c
libmicrohttpd/src/daemon/memorypool.h
libmicrohttpd/src/daemon/minimal_example.c
libmicrohttpd/src/include/microhttpd.h
Log:
new MHD with support for chunked encoding
Modified: libmicrohttpd/ChangeLog
===================================================================
--- libmicrohttpd/ChangeLog 2007-12-20 00:40:49 UTC (rev 5979)
+++ libmicrohttpd/ChangeLog 2007-12-20 04:23:00 UTC (rev 5980)
@@ -1,3 +1,12 @@
+Wed Dec 19 21:12:04 MST 2007
+ Implemented chunked (HTTP 1.1) downloads (including
+ sending of HTTP footers). Also allowed queuing of
+ a response early to suppress the otherwise automatic
+ "100 CONTINUE" response. Removed the mostly useless
+ "(un)register handler" methods from the API. Changed
+ the internal implementation to use a finite state
+ machine (cleaner code, slightly less memory consumption). - CG
+
Sun Dec 16 03:24:13 MST 2007
Implemented handling of chunked (HTTP 1.1) uploads.
Note that the upload callback must be able to
Modified: libmicrohttpd/README
===================================================================
--- libmicrohttpd/README 2007-12-20 00:40:49 UTC (rev 5979)
+++ libmicrohttpd/README 2007-12-20 04:23:00 UTC (rev 5980)
@@ -24,7 +24,7 @@
default to reduce the size of the library (error messages take
space!). If you are concerned about space, you should set "CFLAGS" to
"-Os --fomit-frame-pointer" to have gcc generate tight code. The
-resulting binary should be less than 25k (on x86).
+resulting binary should be about 25k (on x86).
Portability
@@ -50,11 +50,6 @@
feature.
-For http/1.1-compliance:
-========================
-connection.c:
-- support sending of chunked responses (#1302, TEST, ARCH)
-
For POST:
=========
- add support to decode multipart/form-data with
@@ -71,6 +66,10 @@
- add testcases for http/1.1 pipelining (need
to figure out how to ensure curl pipelines)
- add testcases for resource limit enforcement
+- add testcases for client queuing early response,
+ suppressing 100 CONTINUE
+- extend testcase for chunked encoding to validate
+ handling of footers
Documentation:
==============
Modified: libmicrohttpd/configure.ac
===================================================================
--- libmicrohttpd/configure.ac 2007-12-20 00:40:49 UTC (rev 5979)
+++ libmicrohttpd/configure.ac 2007-12-20 04:23:00 UTC (rev 5980)
@@ -21,8 +21,8 @@
#
#
AC_PREREQ(2.57)
-AC_INIT([libmicrohttpd], [0.1.2],address@hidden)
-AM_INIT_AUTOMAKE([libmicrohttpd], [0.1.2])
+AC_INIT([libmicrohttpd], [0.2.0],address@hidden)
+AM_INIT_AUTOMAKE([libmicrohttpd], [0.2.0])
AM_CONFIG_HEADER([config.h])
AH_TOP([#define _GNU_SOURCE 1])
Modified: libmicrohttpd/src/daemon/Makefile.am
===================================================================
--- libmicrohttpd/src/daemon/Makefile.am 2007-12-20 00:40:49 UTC (rev
5979)
+++ libmicrohttpd/src/daemon/Makefile.am 2007-12-20 04:23:00 UTC (rev
5980)
@@ -12,7 +12,7 @@
libmicrohttpd.la
libmicrohttpd_la_LDFLAGS = \
- -export-dynamic -version-info 2:1:1 $(retaincommand)
+ -export-dynamic -version-info 3:0:0 $(retaincommand)
libmicrohttpd_la_SOURCES = \
connection.c connection.h \
reason_phrase.c reason_phrase.h \
@@ -48,7 +48,6 @@
daemontest_postform \
daemontest_post_loop \
daemontest_put \
- daemontest_put_chunked \
daemontest_large_put \
daemontest_get11 \
daemontest_post11 \
@@ -56,7 +55,9 @@
daemontest_post_loop11 \
daemontest_put11 \
daemontest_large_put11 \
- daemontest_long_header
+ daemontest_long_header \
+ daemontest_get_chunked \
+ daemontest_put_chunked
TESTS = $(check_PROGRAMS)
@@ -71,6 +72,12 @@
$(top_builddir)/src/daemon/libmicrohttpd.la \
@LIBCURL@
+daemontest_get_chunked_SOURCES = \
+ daemontest_get_chunked.c
+daemontest_get_chunked_LDADD = \
+ $(top_builddir)/src/daemon/libmicrohttpd.la \
+ @LIBCURL@
+
daemontest_post_SOURCES = \
daemontest_post.c
daemontest_post_LDADD = \
Modified: libmicrohttpd/src/daemon/connection.c
===================================================================
--- libmicrohttpd/src/daemon/connection.c 2007-12-20 00:40:49 UTC (rev
5979)
+++ libmicrohttpd/src/daemon/connection.c 2007-12-20 04:23:00 UTC (rev
5980)
@@ -2,7 +2,6 @@
This file is part of libmicrohttpd
(C) 2007 Daniel Pittman and Christian Grothoff
-
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
@@ -62,6 +61,14 @@
*/
#define REQUEST_LACKS_HOST ""
+#define EXTRA_CHECKS MHD_YES
+
+#if EXTRA_CHECKS
+#define EXTRA_CHECK(a) if (!(a)) abort();
+#else
+#define EXTRA_CHECK(a)
+#endif
+
/**
* Add extra debug messages with reasons for closing connections
* (non-error reasons).
@@ -73,6 +80,10 @@
*/
#define DEBUG_SEND_DATA MHD_NO
+/**
+ * Should all state transitions be printed to stderr?
+ */
+#define DEBUG_STATES MHD_NO
/**
* Get all of the headers from the request.
@@ -109,7 +120,6 @@
return ret;
}
-
/**
* Get a particular header value. If multiple
* values match the kind, return any one of them.
@@ -149,11 +159,11 @@
MHD_queue_response (struct MHD_Connection *connection,
unsigned int status_code, struct MHD_Response *response)
{
- if ((connection == NULL) ||
- (response == NULL) ||
- (connection->response != NULL) ||
- (connection->have_received_body == MHD_NO)
- || (connection->have_received_headers == MHD_NO))
+ if ( (connection == NULL) ||
+ (response == NULL) ||
+ (connection->response != NULL) ||
+ ( (connection->state != MHD_CONNECTION_HEADERS_PROCESSED) &&
+ (connection->state != MHD_CONNECTION_FOOTERS_RECEIVED) ) )
return MHD_NO;
MHD_increment_response_rc (response);
connection->response = response;
@@ -165,6 +175,21 @@
have already sent the full message body */
connection->response_write_position = response->total_size;
}
+ if ( (response->total_size == -1) &&
+ (0 == strcasecmp(connection->version,
+ MHD_HTTP_VERSION_1_1)) )
+ connection->have_chunked_response = MHD_YES;
+ else
+ connection->have_chunked_response = MHD_NO;
+ if (connection->state == MHD_CONNECTION_HEADERS_PROCESSED)
+ {
+ /* response was queued "early",
+ refuse to read body / footers or further
+ requests! */
+ SHUTDOWN (connection->socket_fd, SHUT_RD);
+ connection->read_closed = MHD_YES;
+ connection->state = MHD_CONNECTION_FOOTERS_RECEIVED;
+ }
return MHD_YES;
}
@@ -173,14 +198,14 @@
* message for this connection?
*/
static int
-MHD_need_100_continue (struct MHD_Connection *connection)
+need_100_continue (struct MHD_Connection *connection)
{
const char *expect;
- return ((connection->version != NULL) &&
+ return ((connection->response == NULL) &&
+ (connection->version != NULL) &&
(0 == strcasecmp (connection->version,
MHD_HTTP_VERSION_1_1)) &&
- (connection->have_received_headers == MHD_YES) &&
(NULL != (expect = MHD_lookup_connection_value (connection,
MHD_HEADER_KIND,
MHD_HTTP_HEADER_EXPECT)))
@@ -199,6 +224,7 @@
SHUTDOWN (connection->socket_fd, SHUT_RDWR);
CLOSE (connection->socket_fd);
connection->socket_fd = -1;
+ connection->state = MHD_CONNECTION_CLOSED;
if (connection->daemon->notify_completed != NULL)
connection->daemon->notify_completed (connection->daemon->
notify_completed_cls, connection,
@@ -216,12 +242,14 @@
* @return MHD_NO if readying the response failed
*/
static int
-ready_response (struct MHD_Connection *connection)
+try_ready_normal_body (struct MHD_Connection *connection)
{
int ret;
struct MHD_Response *response;
response = connection->response;
+ if (response->crc == NULL)
+ return MHD_YES;
ret = response->crc (response->crc_cls,
connection->response_write_position,
response->data,
@@ -230,7 +258,8 @@
connection->response_write_position));
if (ret == -1)
{
- /* end of message, signal other side by closing! */
+ /* either error or http 1.0 transfer, close
+ socket! */
#if DEBUG_CLOSE
#if HAVE_MESSAGES
MHD_DLOG (connection->daemon, "Closing connection (end of response)\n");
@@ -239,94 +268,281 @@
response->total_size = connection->response_write_position;
connection_close_error (connection);
return MHD_NO;
- }
+ }
response->data_start = connection->response_write_position;
response->data_size = ret;
if (ret == 0)
+ return MHD_NO;
+ return MHD_YES;
+}
+
+/**
+ * Prepare the response buffer of this connection for
+ * sending. Assumes that the response mutex is
+ * already held. If the transmission is complete,
+ * this function may close the socket (and return
+ * MHD_NO).
+ *
+ * @return MHD_NO if readying the response failed
+ */
+static int
+try_ready_chunked_body (struct MHD_Connection *connection)
+{
+ int ret;
+ char * buf;
+ struct MHD_Response *response;
+ unsigned int size;
+ char cbuf[9];
+
+ response = connection->response;
+ if (connection->write_buffer_size == 0)
{
- /* avoid busy-waiting when using external select
- (we assume that the main application will
- wake up the external select once more data
- is ready). With internal selects, we
- have no choice; if the app uses a thread
- per connection, ret==0 is likely a bug --
- the application should block until data
- is ready! */
- if ((0 ==
- (connection->daemon->
- options & (MHD_USE_SELECT_INTERNALLY |
- MHD_USE_THREAD_PER_CONNECTION))))
- connection->response_unready = MHD_YES;
+ size = connection->daemon->pool_size;
+ do
+ {
+ size /= 2;
+ if (size < 128)
+ {
+ /* not enough memory */
+#if DEBUG_CLOSE
+#if HAVE_MESSAGES
+ MHD_DLOG (connection->daemon, "Closing connection (out of
memory)\n");
+#endif
+#endif
+ connection_close_error (connection);
+ return MHD_NO;
+ }
+ buf = MHD_pool_allocate (connection->pool,
+ size,
+ MHD_NO);
+ }
+ while (buf == NULL);
+ connection->write_buffer_size = size;
+ connection->write_buffer = buf;
+ }
+
+ ret = response->crc (response->crc_cls,
+ connection->response_write_position,
+ &connection->write_buffer[8],
+ connection->write_buffer_size - 8 - 2);
+ if (ret == -1)
+ {
+ /* end of message, signal other side! */
+ strcpy(connection->write_buffer,
+ "0\r\n");
+ connection->write_buffer_append_offset = 3;
+ connection->write_buffer_send_offset = 0;
+ response->total_size = connection->response_write_position;
+ return MHD_YES;
+ }
+ if (ret == 0)
+ {
+ connection->state = MHD_CONNECTION_CHUNKED_BODY_UNREADY;
return MHD_NO;
}
- connection->response_unready = MHD_NO;
+ if (ret > 0xFFFFFF)
+ ret = 0xFFFFFF;
+ snprintf(cbuf,
+ 8,
+ "%X\r\n",
+ ret);
+ memcpy(&connection->write_buffer[8 - strlen(cbuf)],
+ cbuf,
+ strlen(cbuf));
+ memcpy(&connection->write_buffer[8 + ret],
+ "\r\n",
+ 2);
+ connection->response_write_position += ret;
+ connection->write_buffer_send_offset = 8 - strlen(cbuf);
+ connection->write_buffer_append_offset = 8 + ret + 2;
return MHD_YES;
}
/**
- * Obtain the select sets for this connection
- *
- * @return MHD_YES on success
+ * Check if we need to set some additional headers
+ * for http-compiliance.
*/
-int
-MHD_connection_get_fdset (struct MHD_Connection *connection,
- fd_set * read_fd_set,
- fd_set * write_fd_set,
- fd_set * except_fd_set, int *max_fd)
+static void
+add_extra_headers (struct MHD_Connection *connection)
{
- int fd;
- void *buf;
+ const char *have;
+ char buf[128];
- fd = connection->socket_fd;
- if (fd == -1)
- return MHD_YES;
- if ((connection->read_close == MHD_NO) &&
- ((connection->have_received_headers == MHD_NO) ||
- (connection->read_buffer_offset < connection->read_buffer_size)))
+ connection->have_chunked_upload = MHD_NO;
+ if (connection->response->total_size == -1)
+ {
+ have = MHD_get_response_header (connection->response,
+ MHD_HTTP_HEADER_CONNECTION);
+ if ( (have == NULL) ||
+ (0 != strcasecmp(have, "close")) )
+ {
+ if ( (connection->version != NULL) &&
+ (0 == strcasecmp(connection->version,
+ MHD_HTTP_VERSION_1_1)) )
+ {
+ connection->have_chunked_upload = MHD_YES;
+ have = MHD_get_response_header (connection->response,
+
MHD_HTTP_HEADER_TRANSFER_ENCODING);
+ if (have == NULL)
+ MHD_add_response_header (connection->response,
+ MHD_HTTP_HEADER_TRANSFER_ENCODING,
+ "chunked");
+ }
+ else
+ {
+ MHD_add_response_header (connection->response,
+ MHD_HTTP_HEADER_CONNECTION, "close");
+ }
+ }
+ }
+ else if (NULL == MHD_get_response_header (connection->response,
+ MHD_HTTP_HEADER_CONTENT_LENGTH))
{
- FD_SET (fd, read_fd_set);
- if (fd > *max_fd)
- *max_fd = fd;
+ _REAL_SNPRINTF (buf,
+ 128,
+ "%llu",
+ (unsigned long long) connection->response->total_size);
+ MHD_add_response_header (connection->response,
+ MHD_HTTP_HEADER_CONTENT_LENGTH, buf);
}
+}
+
+/**
+ * Produce HTTP "Date:" header.
+ * @param date where to write the header
+ * @param max maximum number of characters to write
+ */
+static void
+get_date_string (char *date, unsigned int max)
+{
+ static const char *days[] =
+ { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" };
+ static const char *mons[] =
+ { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct",
+ "Nov", "Dec"
+ };
+ struct tm now;
+ time_t t;
+
+ time (&t);
+ gmtime_r (&t, &now);
+ snprintf (date,
+ max - 1,
+ "Date: %3s, %02u %3s %04u %02u:%02u:%02u GMT\r\n",
+ days[now.tm_wday % 7],
+ now.tm_mday,
+ mons[now.tm_mon % 12],
+ now.tm_year, now.tm_hour, now.tm_min, now.tm_sec);
+}
+
+/**
+ * try growing the read buffer
+ * @return MHD_YES on success, MHD_NO on failure
+ */
+static int
+try_grow_read_buffer(struct MHD_Connection * connection)
+{
+ void * buf;
+
+ buf = MHD_pool_reallocate (connection->pool,
+ connection->read_buffer,
+ connection->read_buffer_size,
+ connection->read_buffer_size * 2 +
+ MHD_BUF_INC_SIZE + 1);
+ if (buf == NULL)
+ return MHD_NO;
+ /* 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;
+ return MHD_YES;
+}
+
+/**
+ * Allocate the connection's write buffer and
+ * fill it with all of the headers (or footers,
+ * if we have already sent the body) from the
+ * HTTPd's response.
+ */
+static int
+build_header_response (struct MHD_Connection *connection)
+{
+ size_t size;
+ size_t off;
+ struct MHD_HTTP_Header *pos;
+ char code[128];
+ char date[128];
+ char *data;
+ enum MHD_ValueKind kind;
+ const char *reason_phrase;
+
+ if (connection->state == MHD_CONNECTION_FOOTERS_RECEIVED)
+ {
+ add_extra_headers (connection);
+ reason_phrase =
+ MHD_get_reason_phrase_for (connection->responseCode);
+ _REAL_SNPRINTF (code, 128, "%s %u %s\r\n", MHD_HTTP_VERSION_1_1,
+ connection->responseCode, reason_phrase);
+ off = strlen (code);
+ /* estimate size */
+ size = off + 2; /* extra \r\n at the end */
+ kind = MHD_HEADER_KIND;
+ if (NULL == MHD_get_response_header (connection->response,
+ MHD_HTTP_HEADER_DATE))
+ get_date_string (date, sizeof (date));
+ else
+ date[0] = '\0';
+ size += strlen (date);
+ }
else
{
- if ((connection->read_close == MHD_NO) &&
- ((connection->have_received_headers == MHD_YES) &&
- (connection->read_buffer_offset == 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;
- }
- }
+ size = 2;
+ kind = MHD_FOOTER_KIND;
+ off = 0;
}
- if ((connection->response != NULL) &&
- (connection->response_unready == MHD_YES))
+ pos = connection->response->first_header;
+ while (pos != NULL)
{
- pthread_mutex_lock (&connection->response->mutex);
- ready_response (connection);
- pthread_mutex_unlock (&connection->response->mutex);
+ if (pos->kind == kind)
+ size += strlen (pos->header) + strlen (pos->value) + 4; /* colon,
space, linefeeds */
+ pos = pos->next;
}
- if (((connection->response != NULL) &&
- (connection->response_unready == MHD_NO)) ||
- MHD_need_100_continue (connection))
+ /* produce data */
+ data = MHD_pool_allocate (connection->pool, size + 1, MHD_YES);
+ if (data == NULL)
{
- FD_SET (fd, write_fd_set);
- if (fd > *max_fd)
- *max_fd = fd;
+#if HAVE_MESSAGES
+ MHD_DLOG (connection->daemon, "Not enough memory for write!\n");
+#endif
+ return MHD_NO;
}
+ if (connection->state == MHD_CONNECTION_FOOTERS_RECEIVED)
+ {
+ memcpy (data, code, off);
+ }
+ pos = connection->response->first_header;
+ while (pos != NULL)
+ {
+ if (pos->kind == kind)
+ {
+ SPRINTF (&data[off], "%s: %s\r\n", pos->header, pos->value);
+ off += strlen (pos->header) + strlen (pos->value) + 4;
+ }
+ pos = pos->next;
+ }
+ if (connection->state == MHD_CONNECTION_FOOTERS_RECEIVED)
+ {
+ strcpy (&data[off], date);
+ off += strlen (date);
+ }
+ sprintf (&data[off], "\r\n");
+ off += 2;
+ if (off != size)
+ abort ();
+ connection->write_buffer = data;
+ connection->write_buffer_append_offset = size;
+ connection->write_buffer_send_offset = 0;
+ connection->write_buffer_size = size + 1;
return MHD_YES;
}
@@ -338,15 +554,14 @@
* @param status_code the response code to send (413 or 414)
*/
static void
-MHD_excessive_data_handler (struct MHD_Connection *connection,
- unsigned int status_code)
+excessive_data_handler (struct MHD_Connection *connection,
+ unsigned int status_code)
{
struct MHD_Response *response;
/* die, header far too long to be reasonable */
- connection->read_close = MHD_YES;
- connection->have_received_headers = MHD_YES;
- connection->have_received_body = MHD_YES;
+ connection->state = MHD_CONNECTION_FOOTERS_RECEIVED;
+ connection->read_closed = MHD_YES;
#if HAVE_MESSAGES
MHD_DLOG (connection->daemon,
"Received excessively long header, closing connection.\n");
@@ -354,10 +569,169 @@
response = MHD_create_response_from_data (strlen (REQUEST_TOO_BIG),
REQUEST_TOO_BIG, MHD_NO, MHD_NO);
MHD_queue_response (connection, status_code, response);
+ EXTRA_CHECK(connection->response != NULL);
MHD_destroy_response (response);
+ if (MHD_NO == build_header_response (connection))
+ {
+ /* oops - close! */
+#if HAVE_MESSAGES
+ MHD_DLOG (connection->daemon,
+ "Closing connection (failed to create response header)\n");
+#endif
+ connection->state = MHD_CONNECTION_CLOSED;
+ }
+ else
+ {
+ connection->state = MHD_CONNECTION_HEADERS_SENDING;
+ }
}
/**
+ * Add "fd" to the "fd_set". If "fd" is
+ * greater than "*max", set "*max" to fd.
+ */
+static void
+do_fd_set(int fd,
+ fd_set * set,
+ int * max_fd)
+{
+ FD_SET (fd, set);
+ if (fd > *max_fd)
+ *max_fd = fd;
+}
+
+/**
+ * Obtain the select sets for this connection
+ *
+ * @return MHD_YES on success
+ */
+int
+MHD_connection_get_fdset (struct MHD_Connection *connection,
+ fd_set * read_fd_set,
+ fd_set * write_fd_set,
+ fd_set * except_fd_set, int *max_fd)
+{
+ int fd;
+
+ if (connection->pool == NULL)
+ connection->pool = MHD_pool_create (connection->daemon->pool_size);
+ if (connection->pool == NULL)
+ {
+#if HAVE_MESSAGES
+ MHD_DLOG (connection->daemon, "Failed to create memory pool!\n");
+#endif
+ connection_close_error (connection);
+ return MHD_NO;
+ }
+ fd = connection->socket_fd;
+ if (fd == -1)
+ return MHD_YES;
+ while (1) {
+#if DEBUG_STATES
+ fprintf(stderr,
+ "`%s' in state %u\n",
+ __FUNCTION__,
+ connection->state);
+#endif
+ switch (connection->state)
+ {
+ case MHD_CONNECTION_INIT:
+ case MHD_CONNECTION_URL_RECEIVED:
+ case MHD_CONNECTION_HEADER_PART_RECEIVED:
+ /* while reading headers, we always grow the
+ read buffer if needed, no size-check required */
+ if ( (connection->read_closed) &&
+ (connection->read_buffer_offset == 0) )
+ {
+ connection->state = MHD_CONNECTION_CLOSED;
+ continue;
+ }
+ if ( (connection->read_buffer_offset == connection->read_buffer_size) &&
+ (MHD_NO == try_grow_read_buffer(connection)) )
+ {
+ excessive_data_handler (connection,
+ (connection->url != NULL)
+ ? MHD_HTTP_REQUEST_ENTITY_TOO_LARGE
+ : MHD_HTTP_REQUEST_URI_TOO_LONG);
+ continue;
+ }
+ if (MHD_NO == connection->read_closed)
+ do_fd_set (fd, read_fd_set, max_fd);
+ break;
+ case MHD_CONNECTION_HEADERS_RECEIVED:
+ /* we should never get here */
+ EXTRA_CHECK(0);
+ break;
+ case MHD_CONNECTION_HEADERS_PROCESSED:
+ EXTRA_CHECK(0);
+ break;
+ case MHD_CONNECTION_CONTINUE_SENDING:
+ do_fd_set (fd, write_fd_set, max_fd);
+ break;
+ case MHD_CONNECTION_CONTINUE_SENT:
+ if (connection->read_buffer_offset == connection->read_buffer_size)
+ try_grow_read_buffer(connection);
+ if (connection->read_buffer_offset < connection->read_buffer_size)
+ do_fd_set (fd, read_fd_set, max_fd);
+ break;
+ case MHD_CONNECTION_BODY_RECEIVED:
+ case MHD_CONNECTION_FOOTER_PART_RECEIVED:
+ /* while reading footers, we always grow the
+ read buffer if needed, no size-check required */
+ if (MHD_YES == connection->read_closed)
+ {
+ connection->state = MHD_CONNECTION_CLOSED;
+ continue;
+ }
+ do_fd_set (fd, read_fd_set, max_fd);
+ /* transition to FOOTERS_RECEIVED
+ happens in read handler */
+ break;
+ case MHD_CONNECTION_FOOTERS_RECEIVED:
+ /* no socket action, wait for client
+ to provide response */
+ break;
+ case MHD_CONNECTION_HEADERS_SENDING:
+ /* headers in buffer, keep writing */
+ do_fd_set(fd, write_fd_set, max_fd);
+ break;
+ case MHD_CONNECTION_HEADERS_SENT:
+ EXTRA_CHECK(0);
+ break;
+ case MHD_CONNECTION_NORMAL_BODY_READY:
+ do_fd_set (fd, write_fd_set, max_fd);
+ break;
+ case MHD_CONNECTION_NORMAL_BODY_UNREADY:
+ /* not ready, no socket action */
+ break;
+ case MHD_CONNECTION_CHUNKED_BODY_READY:
+ do_fd_set (fd, write_fd_set, max_fd);
+ break;
+ case MHD_CONNECTION_CHUNKED_BODY_UNREADY:
+ /* not ready, no socket action */
+ break;
+ case MHD_CONNECTION_BODY_SENT:
+ EXTRA_CHECK(0);
+ break;
+ case MHD_CONNECTION_FOOTERS_SENDING:
+ do_fd_set (fd, write_fd_set, max_fd);
+ break;
+ case MHD_CONNECTION_FOOTERS_SENT:
+ EXTRA_CHECK(0);
+ break;
+ case MHD_CONNECTION_CLOSED:
+ if (connection->socket_fd != -1)
+ connection_close_error(connection);
+ return MHD_YES; /* do nothing, not even reading */
+ default:
+ EXTRA_CHECK(0);
+ }
+ break;
+ }
+ return MHD_YES;
+}
+
+/**
* 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
@@ -366,7 +740,7 @@
* return NULL. Otherwise return a pointer to the line.
*/
static char *
-MHD_get_next_header_line (struct MHD_Connection *connection)
+get_next_header_line (struct MHD_Connection *connection)
{
char *rbuf;
size_t pos;
@@ -390,10 +764,10 @@
MHD_BUF_INC_SIZE);
if (rbuf == NULL)
{
- MHD_excessive_data_handler (connection,
- (connection->url != NULL)
- ? MHD_HTTP_REQUEST_ENTITY_TOO_LARGE
- : MHD_HTTP_REQUEST_URI_TOO_LONG);
+ excessive_data_handler (connection,
+ (connection->url != NULL)
+ ? MHD_HTTP_REQUEST_ENTITY_TOO_LARGE
+ : MHD_HTTP_REQUEST_URI_TOO_LONG);
}
else
{
@@ -418,8 +792,8 @@
* @return MHD_NO on failure (out of memory), MHD_YES for success
*/
static int
-MHD_connection_add_header (struct MHD_Connection *connection,
- char *key, char *value, enum MHD_ValueKind kind)
+connection_add_header (struct MHD_Connection *connection,
+ char *key, char *value, enum MHD_ValueKind kind)
{
struct MHD_HTTP_Header *hdr;
@@ -431,8 +805,8 @@
MHD_DLOG (connection->daemon,
"Not enough memory to allocate header record!\n");
#endif
- MHD_excessive_data_handler (connection,
- MHD_HTTP_REQUEST_ENTITY_TOO_LARGE);
+ excessive_data_handler (connection,
+ MHD_HTTP_REQUEST_ENTITY_TOO_LARGE);
return MHD_NO;
}
hdr->next = connection->headers_received;
@@ -468,8 +842,8 @@
}
MHD_http_unescape (args);
MHD_http_unescape (equals);
- if (MHD_NO == MHD_connection_add_header (connection,
- args, equals, kind))
+ if (MHD_NO == connection_add_header (connection,
+ args, equals, kind))
return MHD_NO;
args = amper;
}
@@ -482,7 +856,7 @@
* @return MHD_YES for success, MHD_NO for failure (malformed, out of memory)
*/
static int
-MHD_parse_cookie_header (struct MHD_Connection *connection)
+parse_cookie_header (struct MHD_Connection *connection)
{
const char *hdr;
char *cpy;
@@ -500,8 +874,8 @@
#if HAVE_MESSAGES
MHD_DLOG (connection->daemon, "Not enough memory to parse cookies!\n");
#endif
- MHD_excessive_data_handler (connection,
- MHD_HTTP_REQUEST_ENTITY_TOO_LARGE);
+ excessive_data_handler (connection,
+ MHD_HTTP_REQUEST_ENTITY_TOO_LARGE);
return MHD_NO;
}
memcpy (cpy, hdr, strlen (hdr) + 1);
@@ -536,8 +910,8 @@
equals[strlen (equals) - 1] = '\0';
equals++;
}
- if (MHD_NO == MHD_connection_add_header (connection,
- pos, equals, MHD_COOKIE_KIND))
+ if (MHD_NO == connection_add_header (connection,
+ pos, equals, MHD_COOKIE_KIND))
return MHD_NO;
pos = semicolon;
}
@@ -587,235 +961,29 @@
return MHD_YES;
}
-
/**
- * This function is designed to parse the input buffer of a given
- * connection for HTTP headers -- and in the case of chunked encoding,
- * also for HTTP "footers".
- *
- * Once the header is complete, it should have set the
- * headers_received, url and method values and set
- * have_received_headers to MHD_YES. If no body is expected, it
- * should also set "have_received_body" to MHD_YES. Otherwise, it
- * should set "remaining_upload_size" to the expected size of the
- * body. If the size of the body is unknown, it should be set to -1.
+ * Call the handler of the application for this
+ * connection. Handles chunking of the upload
+ * as well as normal uploads.
*/
static void
-MHD_parse_connection_headers (struct MHD_Connection *connection)
+call_connection_handler (struct MHD_Connection *connection)
{
- char *last;
- char *line;
- char *colon;
- char *tmp;
- const char *clen;
- unsigned long long cval;
- struct MHD_Response *response;
-
- if (((connection->have_received_body == MHD_YES) &&
- (connection->have_chunked_upload == MHD_NO)) ||
- (connection->have_received_headers == MHD_YES))
- abort ();
- colon = NULL; /* make gcc happy */
- last = NULL;
- while (NULL != (line = MHD_get_next_header_line (connection)))
- {
- if (last != NULL)
- {
- if ((line[0] == ' ') || (line[0] == '\t'))
- {
- /* value was continued on the next line, see
- http://www.jmarshall.com/easy/http/ */
- last = MHD_pool_reallocate (connection->pool,
- last,
- strlen (last) + 1,
- strlen (line) + strlen (last) + 1);
- if (last == NULL)
- {
- MHD_excessive_data_handler (connection,
-
MHD_HTTP_REQUEST_ENTITY_TOO_LARGE);
- break;
- }
- tmp = line;
- while ((tmp[0] == ' ') || (tmp[0] == '\t'))
- tmp++; /* skip whitespace at start of 2nd line */
- strcat (last, tmp);
- continue; /* possibly more than 2 lines... */
- }
- else
- {
- 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))
- goto DIE;
- continue;
- }
- /* check if this is the end of the header */
- if (strlen (line) == 0)
- {
- /* end of header */
- connection->have_received_headers = MHD_YES;
- clen = MHD_lookup_connection_value (connection,
- MHD_HEADER_KIND,
- MHD_HTTP_HEADER_CONTENT_LENGTH);
- if (clen != NULL)
- {
- if (1 != sscanf (clen, "%llu", &cval))
- {
-#if HAVE_MESSAGES
- MHD_DLOG (connection->daemon,
- "Failed to parse `%s' header `%s', closing
connection.\n",
- MHD_HTTP_HEADER_CONTENT_LENGTH, clen);
-#endif
- goto DIE;
- }
- connection->remaining_upload_size = cval;
- connection->have_received_body = cval == 0 ? MHD_YES : MHD_NO;
- }
- else
- {
- if (NULL == MHD_lookup_connection_value (connection,
- MHD_HEADER_KIND,
-
MHD_HTTP_HEADER_TRANSFER_ENCODING))
- {
- /* this request does not have a body */
- connection->remaining_upload_size = 0;
- connection->have_received_body = MHD_YES;
- }
- else
- {
- if (connection->have_chunked_upload == MHD_NO)
- {
- connection->remaining_upload_size = -1; /* unknown
size */
- if (0 ==
- strcasecmp (MHD_lookup_connection_value
- (connection, MHD_HEADER_KIND,
- MHD_HTTP_HEADER_TRANSFER_ENCODING),
- "chunked"))
- connection->have_chunked_upload = MHD_YES;
- }
- else
- {
- /* we were actually processing the footers at the
- END of a chunked encoding; give connection
- handler an extra chance... */
- connection->have_chunked_upload = MHD_NO; /* no more! */
- MHD_call_connection_handler (connection);
- }
- }
- }
- if ((0 != (MHD_USE_PEDANTIC_CHECKS & connection->daemon->options))
- && (NULL != connection->version)
- && (0 == strcasecmp (MHD_HTTP_VERSION_1_1, connection->version))
- && (NULL ==
- MHD_lookup_connection_value (connection, MHD_HEADER_KIND,
- MHD_HTTP_HEADER_HOST)))
- {
- /* die, http 1.1 request without host and we are pedantic */
- connection->have_received_body = MHD_YES;
- connection->read_close = MHD_YES;
-#if HAVE_MESSAGES
- MHD_DLOG (connection->daemon,
- "Received `%s' request without `%s' header.\n",
- MHD_HTTP_VERSION_1_1, MHD_HTTP_HEADER_HOST);
-#endif
- response =
- MHD_create_response_from_data (strlen (REQUEST_LACKS_HOST),
- REQUEST_LACKS_HOST, MHD_NO,
- MHD_NO);
- MHD_queue_response (connection, MHD_HTTP_BAD_REQUEST, response);
- MHD_destroy_response (response);
- }
- break;
- }
- /* line should be normal header line, find colon */
- colon = strstr (line, ":");
- if (colon == NULL)
- {
- /* error in header line, die hard */
-#if HAVE_MESSAGES
- MHD_DLOG (connection->daemon,
- "Received malformed line (no colon), closing
connection.\n");
-#endif
- goto DIE;
- }
- /* zero-terminate header */
- colon[0] = '\0';
- colon++; /* advance to value */
- while ((colon[0] != '\0') && ((colon[0] == ' ') || (colon[0] == '\t')))
- colon++;
- /* we do the actual adding of the connection
- header at the beginning of the while
- loop since we need to be able to inspect
- the *next* header line (in case it starts
- with a space...) */
- last = line;
- }
- if ((last != NULL) &&
- (MHD_NO == MHD_connection_add_header (connection,
- last, colon, MHD_HEADER_KIND)))
- return; /* error */
- MHD_parse_cookie_header (connection);
- return;
-DIE:
-#if HAVE_MESSAGES
- MHD_DLOG (connection->daemon,
- "Closing connection (problem parsing headers)\n");
-#endif
- connection_close_error (connection);
-}
-
-
-/**
- * Find the handler responsible for this request.
- */
-static struct MHD_Access_Handler *
-MHD_find_access_handler (struct MHD_Connection *connection)
-{
- struct MHD_Access_Handler *pos;
-
- pos = connection->daemon->handlers;
- while (pos != NULL)
- {
- if (0 == strcmp (connection->url, pos->uri_prefix))
- return pos;
- pos = pos->next;
- }
- return &connection->daemon->default_handler;
-}
-
-
-/**
- * Call the handler of the application for this
- * connection.
- */
-void
-MHD_call_connection_handler (struct MHD_Connection *connection)
-{
- struct MHD_Access_Handler *ah;
unsigned int processed;
unsigned int available;
unsigned int used;
int instant_retry;
unsigned int i;
+ int malformed;
if (connection->response != NULL)
- return; /* already queued a response */
- if (connection->have_received_headers == MHD_NO)
- abort (); /* bad timing... */
+ return; /* already queued a response */
do
{
instant_retry = MHD_NO;
available = connection->read_buffer_offset;
- if (connection->have_chunked_upload == MHD_YES)
+ if ( (connection->have_chunked_upload == MHD_YES) &&
+ (connection->remaining_upload_size == -1) )
{
if ((connection->current_chunk_offset ==
connection->current_chunk_size)
@@ -876,18 +1044,17 @@
}
if (i >= available)
return; /* need more data... */
- /* The following if-statement is a bit crazy -- we
- use the second clause only for the side-effect,
- 0-terminating the buffer for the following sscanf
- attempts; yes, there should be only a single
- "="-sign (assignment!) in the read_buffer[i]-line. */
- if ((i >= 6) ||
- ((connection->read_buffer[i] = '\0')) ||
- ((1 != sscanf (connection->read_buffer,
- "%X",
- &connection->current_chunk_size)) &&
- (1 != sscanf (connection->read_buffer,
- "%x", &connection->current_chunk_size))))
+ malformed = (i >= 6);
+ if (! malformed)
+ {
+ connection->read_buffer[i] = '\0';
+ malformed = (1 != sscanf (connection->read_buffer,
+ "%X",
+ &connection->current_chunk_size)) &&
+ (1 != sscanf (connection->read_buffer,
+ "%x", &connection->current_chunk_size));
+ }
+ if (malformed)
{
/* malformed encoding */
#if HAVE_MESSAGES
@@ -908,11 +1075,7 @@
instant_retry = MHD_YES;
if (connection->current_chunk_size == 0)
{
- /* we're back to reading HEADERS (footers!) */
- connection->have_received_body = MHD_YES;
connection->remaining_upload_size = 0;
- connection->have_received_headers = MHD_NO;
- MHD_parse_connection_headers (connection);
return;
}
continue;
@@ -925,8 +1088,7 @@
available = 0;
}
used = processed;
- ah = MHD_find_access_handler (connection);
- if (MHD_NO == ah->dh (ah->dh_cls,
+ if (MHD_NO == connection->daemon->default_handler
(connection->daemon->default_handler_cls,
connection,
connection->url,
connection->method,
@@ -942,6 +1104,8 @@
connection_close_error (connection);
return;
}
+ if (processed > used)
+ abort(); /* fatal client API violation! */
if (processed != 0)
instant_retry = MHD_NO; /* client did not process everything */
used -= processed;
@@ -953,79 +1117,26 @@
&connection->read_buffer[used], processed + available);
if (connection->remaining_upload_size != -1)
connection->remaining_upload_size -= used;
- connection->read_buffer_offset = processed + available;
- if ((connection->remaining_upload_size == 0) ||
- ((connection->read_buffer_offset == 0) &&
- (connection->remaining_upload_size == -1)
- && (connection->socket_fd == -1)))
- {
- connection->have_received_body = MHD_YES;
- if (connection->read_buffer != NULL)
- MHD_pool_reallocate (connection->pool,
- connection->read_buffer,
- (connection->read_buffer ==
- NULL) ? 0 : connection->read_buffer_size +
- 1, 0);
- connection->read_buffer_offset = 0;
- connection->read_buffer_size = 0;
- connection->read_buffer = NULL;
- }
+ connection->read_buffer_offset = processed + available;
}
while (instant_retry == MHD_YES);
}
/**
- * This function handles a particular connection when it has been
- * determined that there is data to be read off a socket. All implementations
- * (multithreaded, external select, internal select) call this function
- * to handle reads.
+ * Try reading data from the socket into the
+ * read buffer of the connection.
+ *
+ * @return MHD_YES if something changed,
+ * MHD_NO if we were interrupted or if
+ * no space was available
*/
-int
-MHD_connection_handle_read (struct MHD_Connection *connection)
+static int
+do_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)
- {
-#if HAVE_MESSAGES
- MHD_DLOG (connection->daemon, "Failed to create memory pool!\n");
-#endif
- connection_close_error (connection);
- return MHD_NO;
- }
- if ((connection->read_buffer_offset >= connection->read_buffer_size) &&
- (connection->have_received_headers == MHD_NO))
- {
- /* need to grow read buffer */
- tmp = MHD_pool_reallocate (connection->pool,
- connection->read_buffer,
- connection->read_buffer_size,
- connection->read_buffer_size * 2 +
- MHD_BUF_INC_SIZE + 1);
- if (tmp == NULL)
- {
-#if HAVE_MESSAGES
- MHD_DLOG (connection->daemon,
- "Not enough memory for reading headers!\n");
-#endif
- MHD_excessive_data_handler (connection,
- MHD_HTTP_REQUEST_ENTITY_TOO_LARGE);
- return MHD_NO;
- }
- connection->read_buffer = tmp;
- connection->read_buffer_size =
- connection->read_buffer_size * 2 + MHD_BUF_INC_SIZE;
- }
- if (connection->read_buffer_offset >= connection->read_buffer_size)
- {
-#if HAVE_MESSAGES
- MHD_DLOG (connection->daemon, "Unexpected call to %s.\n", __FUNCTION__);
-#endif
- return MHD_NO;
- }
+
+ if (connection->read_buffer_size == connection->read_buffer_offset)
+ return MHD_NO;
bytes_read = RECV (connection->socket_fd,
&connection->read_buffer[connection->read_buffer_offset],
connection->read_buffer_size -
@@ -1044,150 +1155,297 @@
if (bytes_read == 0)
{
/* other side closed connection */
- connection->read_close = MHD_YES;
- if ((connection->have_received_headers == MHD_YES)
- && (connection->read_buffer_offset > 0))
- MHD_call_connection_handler (connection);
-#if DEBUG_CLOSE
+ connection->read_closed = MHD_YES;
+ return MHD_NO;
+ }
+ connection->read_buffer_offset += bytes_read;
+ return MHD_YES;
+}
+
+/**
+ * We have received (possibly the beginning of) a line in the
+ * header (or footer). Validate (check for ":") and prepare
+ * to process.
+ */
+static void
+process_header_line(struct MHD_Connection * connection,
+ char * line)
+{
+ char *colon;
+
+ /* line should be normal header line, find colon */
+ colon = strstr (line, ":");
+ if (colon == NULL)
+ {
+ /* error in header line, die hard */
#if HAVE_MESSAGES
MHD_DLOG (connection->daemon,
- "Shutting down connection for reading (other side closed
connection)\n");
+ "Received malformed line (no colon), closing connection.\n");
#endif
-#endif
- shutdown (connection->socket_fd, SHUT_RD);
- if ((connection->have_received_headers == MHD_NO) ||
- (connection->have_received_body == MHD_NO))
- {
- /* no request => no response! */
- CLOSE (connection->socket_fd);
- connection->socket_fd = -1;
- }
- return MHD_YES;
+ connection->state = MHD_CONNECTION_CLOSED;
+ return;
}
- connection->read_buffer_offset += bytes_read;
- if (connection->have_received_headers == MHD_NO)
- MHD_parse_connection_headers (connection);
- if ((connection->have_received_headers == MHD_YES)
- && (connection->method != NULL))
- MHD_call_connection_handler (connection);
- return MHD_YES;
+ /* zero-terminate header */
+ colon[0] = '\0';
+ colon++; /* advance to value */
+ while ((colon[0] != '\0') && ((colon[0] == ' ') || (colon[0] == '\t')))
+ colon++;
+ /* we do the actual adding of the connection
+ header at the beginning of the while
+ loop since we need to be able to inspect
+ the *next* header line (in case it starts
+ with a space...) */
+ connection->last = line;
+ connection->colon = colon;
}
/**
- * Check if we need to set some additional headers
- * for http-compiliance.
+ * Process a header value that spans multiple lines.
+ * The previous line(s) are in connection->last.
+ *
+ * @param line the current input line
+ * @param kind if the line is complete, add a header
+ * of the given kind
*/
static void
-MHD_add_extra_headers (struct MHD_Connection *connection)
+process_broken_line(struct MHD_Connection * connection,
+ char * line,
+ enum MHD_ValueKind kind)
{
- const char *have;
- char buf[128];
+ char * last;
+ char * tmp;
- if (connection->response->total_size == -1)
+ last = connection->last;
+ if ((line[0] == ' ') || (line[0] == '\t'))
{
- have = MHD_get_response_header (connection->response,
- MHD_HTTP_HEADER_CONNECTION);
- if (have == NULL)
- MHD_add_response_header (connection->response,
- MHD_HTTP_HEADER_CONNECTION, "close");
+ /* value was continued on the next line, see
+ http://www.jmarshall.com/easy/http/ */
+ last = MHD_pool_reallocate (connection->pool,
+ last,
+ strlen (last) + 1,
+ strlen (line) + strlen (last) + 1);
+ if (last == NULL)
+ {
+ excessive_data_handler (connection,
+ MHD_HTTP_REQUEST_ENTITY_TOO_LARGE);
+ return;
+ }
+ tmp = line;
+ while ((tmp[0] == ' ') || (tmp[0] == '\t'))
+ tmp++; /* skip whitespace at start of 2nd line */
+ strcat (last, tmp);
+ connection->last = last;
+ return; /* possibly more than 2 lines... */
}
- else if (NULL == MHD_get_response_header (connection->response,
- MHD_HTTP_HEADER_CONTENT_LENGTH))
+ if (MHD_NO == connection_add_header (connection,
+ last,
+ connection->colon,
+ kind))
{
- _REAL_SNPRINTF (buf,
- 128,
- "%llu",
- (unsigned long long) connection->response->total_size);
- MHD_add_response_header (connection->response,
- MHD_HTTP_HEADER_CONTENT_LENGTH, buf);
+ excessive_data_handler (connection,
+ MHD_HTTP_REQUEST_ENTITY_TOO_LARGE);
+ return;
}
+ /* we still have the current line to deal with... */
+ if (strlen(line) != 0)
+ process_header_line(connection,
+ line);
}
+/**
+ * Parse the various headers; figure out the size
+ * of the upload and make sure the headers follow
+ * the protocol. Advance to the appropriate state.
+ */
static void
-get_date_string (char *date, unsigned int max)
+parse_connection_headers(struct MHD_Connection * connection)
{
- static const char *days[] =
- { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" };
- static const char *mons[] =
- { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct",
- "Nov", "Dec"
- };
- struct tm now;
- time_t t;
+ const char *clen;
+ unsigned long long cval;
+ struct MHD_Response *response;
- time (&t);
- gmtime_r (&t, &now);
- snprintf (date,
- max - 1,
- "Date: %3s, %02u %3s %04u %02u:%02u:%02u GMT\r\n",
- days[now.tm_wday % 7],
- now.tm_mday,
- mons[now.tm_mon % 12],
- now.tm_year, now.tm_hour, now.tm_min, now.tm_sec);
+ parse_cookie_header (connection);
+ if ((0 != (MHD_USE_PEDANTIC_CHECKS & connection->daemon->options))
+ && (NULL != connection->version)
+ && (0 == strcasecmp (MHD_HTTP_VERSION_1_1, connection->version))
+ && (NULL ==
+ MHD_lookup_connection_value (connection, MHD_HEADER_KIND,
+ MHD_HTTP_HEADER_HOST)))
+ {
+ /* die, http 1.1 request without host and we are pedantic */
+ connection->state = MHD_CONNECTION_FOOTERS_RECEIVED;
+ connection->read_closed = MHD_YES;
+#if HAVE_MESSAGES
+ MHD_DLOG (connection->daemon,
+ "Received `%s' request without `%s' header.\n",
+ MHD_HTTP_VERSION_1_1, MHD_HTTP_HEADER_HOST);
+#endif
+ response =
+ MHD_create_response_from_data (strlen (REQUEST_LACKS_HOST),
+ REQUEST_LACKS_HOST, MHD_NO,
+ MHD_NO);
+ MHD_queue_response (connection, MHD_HTTP_BAD_REQUEST, response);
+ MHD_destroy_response (response);
+ return;
+ }
+
+ clen = MHD_lookup_connection_value (connection,
+ MHD_HEADER_KIND,
+ MHD_HTTP_HEADER_CONTENT_LENGTH);
+ if (clen != NULL)
+ {
+ if (1 != sscanf (clen, "%llu", &cval))
+ {
+#if HAVE_MESSAGES
+ MHD_DLOG (connection->daemon,
+ "Failed to parse `%s' header `%s', closing connection.\n",
+ MHD_HTTP_HEADER_CONTENT_LENGTH, clen);
+#endif
+ connection->state = MHD_CONNECTION_CLOSED;
+ return;
+ }
+ connection->remaining_upload_size = cval;
+ }
+ else
+ {
+ if (NULL == MHD_lookup_connection_value (connection,
+ MHD_HEADER_KIND,
+
MHD_HTTP_HEADER_TRANSFER_ENCODING))
+ {
+ /* this request does not have a body */
+ connection->remaining_upload_size = 0;
+ }
+ else
+ {
+ connection->remaining_upload_size = -1; /* unknown size */
+ if (0 ==
+ strcasecmp (MHD_lookup_connection_value
+ (connection, MHD_HEADER_KIND,
+ MHD_HTTP_HEADER_TRANSFER_ENCODING),
+ "chunked"))
+ connection->have_chunked_upload = MHD_YES;
+ }
+ }
}
/**
- * Allocate the connection's write buffer and
- * fill it with all of the headers from the
- * HTTPd's response.
+ * This function handles a particular connection when it has been
+ * determined that there is data to be read off a socket. All
+ * implementations (multithreaded, external select, internal select)
+ * call this function to handle reads.
+ *
+ * @return MHD_YES if we should continue to process the
+ * connection (not dead yet), MHD_NO if it died
*/
+int
+MHD_connection_handle_read (struct MHD_Connection *connection)
+{
+ connection->last_activity = time(NULL);
+ if (connection->state == MHD_CONNECTION_CLOSED)
+ return MHD_NO;
+ if (MHD_NO == do_read(connection))
+ return MHD_YES;
+ while (1) {
+#if DEBUG_STATES
+ fprintf(stderr,
+ "`%s' in state %u\n",
+ __FUNCTION__,
+ connection->state);
+#endif
+ switch (connection->state)
+ {
+ case MHD_CONNECTION_INIT:
+ case MHD_CONNECTION_URL_RECEIVED:
+ case MHD_CONNECTION_HEADER_PART_RECEIVED:
+ case MHD_CONNECTION_HEADERS_RECEIVED:
+ case MHD_CONNECTION_HEADERS_PROCESSED:
+ case MHD_CONNECTION_CONTINUE_SENDING:
+ case MHD_CONNECTION_CONTINUE_SENT:
+ case MHD_CONNECTION_BODY_RECEIVED:
+ case MHD_CONNECTION_FOOTER_PART_RECEIVED:
+ /* nothing to do but default action */
+ if (MHD_YES == connection->read_closed)
+ {
+ connection->state = MHD_CONNECTION_CLOSED;
+ continue;
+ }
+ break;
+ case MHD_CONNECTION_CLOSED:
+ if (connection->socket_fd != -1)
+ connection_close_error (connection);
+ return MHD_NO;
+ default:
+ /* shrink read buffer to how much is actually used */
+ MHD_pool_reallocate (connection->pool,
+ connection->read_buffer,
+ connection->read_buffer_size + 1,
+ connection->read_buffer_offset);
+ break;
+ }
+ break;
+ }
+ return MHD_YES;
+}
+
+/**
+ * Try writing data to the socket from the
+ * write buffer of the connection.
+ *
+ * @return MHD_YES if something changed,
+ * MHD_NO if we were interrupted
+ */
static int
-MHD_build_header_response (struct MHD_Connection *connection)
+do_write(struct MHD_Connection * connection)
{
- size_t size;
- size_t off;
- struct MHD_HTTP_Header *pos;
- char code[128];
- char date[128];
- char *data;
+ int ret;
- MHD_add_extra_headers (connection);
- const char *reason_phrase =
- MHD_get_reason_phrase_for (connection->responseCode);
- _REAL_SNPRINTF (code, 128, "%s %u %s\r\n", MHD_HTTP_VERSION_1_1,
- connection->responseCode, reason_phrase);
- off = strlen (code);
- /* estimate size */
- size = off + 2; /* extra \r\n at the end */
- pos = connection->response->first_header;
- while (pos != NULL)
+ ret = SEND (connection->socket_fd,
+ &connection->write_buffer[connection->
+ write_buffer_send_offset],
+ connection->write_buffer_append_offset -
+ connection->write_buffer_send_offset, MSG_NOSIGNAL);
+ if (ret < 0)
{
- size += strlen (pos->header) + strlen (pos->value) + 4; /* colon,
space, linefeeds */
- pos = pos->next;
- }
- if (NULL == MHD_get_response_header (connection->response,
- MHD_HTTP_HEADER_DATE))
- get_date_string (date, sizeof (date));
- else
- date[0] = '\0';
- size += strlen (date);
- /* produce data */
- data = MHD_pool_allocate (connection->pool, size + 1, MHD_YES);
- if (data == NULL)
- {
+ if (errno == EINTR)
+ return MHD_NO;
#if HAVE_MESSAGES
- MHD_DLOG (connection->daemon, "Not enough memory for write!\n");
+ MHD_DLOG (connection->daemon,
+ "Failed to send data: %s\n", STRERROR (errno));
#endif
- return MHD_NO;
+ connection_close_error (connection);
+ return MHD_YES;
}
- memcpy (data, code, off);
- pos = connection->response->first_header;
- while (pos != NULL)
- {
- SPRINTF (&data[off], "%s: %s\r\n", pos->header, pos->value);
- off += strlen (pos->header) + strlen (pos->value) + 4;
- pos = pos->next;
- }
- strcpy (&data[off], date);
- off += strlen (date);
- sprintf (&data[off], "\r\n");
- off += 2;
- if (off != size)
- abort ();
- connection->write_buffer = data;
- connection->write_buffer_append_offset = size;
+#if DEBUG_SEND_DATA
+ fprintf (stderr,
+ "Sent HEADER response: `%.*s'\n",
+ ret,
+ &connection->write_buffer[connection->
+ write_buffer_send_offset]);
+#endif
+ connection->write_buffer_send_offset += ret;
+ return MHD_YES;
+}
+
+/**
+ * Check if we are done sending the write-buffer.
+ * If so, transition into "next_state".
+ * @return MHY_NO if we are not done, MHD_YES if we are
+ */
+static int
+check_write_done(struct MHD_Connection * connection,
+ enum MHD_CONNECTION_STATE next_state)
+{
+ if (connection->write_buffer_append_offset !=
+ connection->write_buffer_send_offset)
+ return MHD_NO;
+ connection->write_buffer_append_offset = 0;
connection->write_buffer_send_offset = 0;
- connection->write_buffer_size = size + 1;
+ connection->state = next_state;
+ MHD_pool_reallocate (connection->pool,
+ connection->write_buffer,
+ connection->write_buffer_size, 0);
+ connection->write_buffer = NULL;
+ connection->write_buffer_size = 0;
return MHD_YES;
}
@@ -1196,207 +1454,414 @@
* been determined that the socket can be written to. All
* implementations (multithreaded, external select, internal select)
* call this function
+ *
+ * @return MHD_YES if we should continue to process the
+ * connection (not dead yet), MHD_NO if it died
*/
int
MHD_connection_handle_write (struct MHD_Connection *connection)
{
struct MHD_Response *response;
int ret;
- const char *end;
- if (MHD_need_100_continue (connection))
- {
- ret = SEND (connection->socket_fd,
- &HTTP_100_CONTINUE[connection->
- continue_message_write_offset],
- strlen (HTTP_100_CONTINUE) -
- connection->continue_message_write_offset, MSG_NOSIGNAL);
- if (ret < 0)
- {
- if (errno == EINTR)
- return MHD_YES;
+ connection->last_activity = time(NULL);
+ while (1) {
+#if DEBUG_STATES
+ fprintf(stderr,
+ "`%s' in state %u\n",
+ __FUNCTION__,
+ connection->state);
+#endif
+ switch (connection->state)
+ {
+ case MHD_CONNECTION_INIT:
+ case MHD_CONNECTION_URL_RECEIVED:
+ case MHD_CONNECTION_HEADER_PART_RECEIVED:
+ case MHD_CONNECTION_HEADERS_RECEIVED:
+ EXTRA_CHECK(0);
+ break;
+ case MHD_CONNECTION_HEADERS_PROCESSED:
+ break;
+ case MHD_CONNECTION_CONTINUE_SENDING:
+ ret = SEND (connection->socket_fd,
+ &HTTP_100_CONTINUE[connection->
+ continue_message_write_offset],
+ strlen (HTTP_100_CONTINUE) -
+ connection->continue_message_write_offset, MSG_NOSIGNAL);
+ if (ret < 0)
+ {
+ if (errno == EINTR)
+ break;
#if HAVE_MESSAGES
- MHD_DLOG (connection->daemon,
- "Failed to send data: %s\n", STRERROR (errno));
+ MHD_DLOG (connection->daemon,
+ "Failed to send data: %s\n", STRERROR (errno));
#endif
- connection_close_error (connection);
- return MHD_YES;
- }
+ connection_close_error (connection);
+ return MHD_NO;
+ }
#if DEBUG_SEND_DATA
- fprintf (stderr,
- "Sent 100 continue response: `%.*s'\n",
- ret,
- &HTTP_100_CONTINUE[connection->continue_message_write_offset]);
+ fprintf (stderr,
+ "Sent 100 continue response: `%.*s'\n",
+ ret,
+ &HTTP_100_CONTINUE[connection->continue_message_write_offset]);
#endif
- connection->continue_message_write_offset += ret;
- return MHD_YES;
- }
- response = connection->response;
- if (response == NULL)
- {
+ connection->continue_message_write_offset += ret;
+ break;
+ case MHD_CONNECTION_CONTINUE_SENT:
+ case MHD_CONNECTION_BODY_RECEIVED:
+ case MHD_CONNECTION_FOOTER_PART_RECEIVED:
+ case MHD_CONNECTION_FOOTERS_RECEIVED:
+ EXTRA_CHECK(0);
+ break;
+ case MHD_CONNECTION_HEADERS_SENDING:
+ do_write(connection);
+ check_write_done(connection,
+ MHD_CONNECTION_HEADERS_SENT);
+ break;
+ case MHD_CONNECTION_HEADERS_SENT:
+ EXTRA_CHECK(0);
+ break;
+ case MHD_CONNECTION_NORMAL_BODY_READY:
+ response = connection->response;
+ if (response->crc != NULL)
+ pthread_mutex_lock (&response->mutex);
+ if (MHD_YES != try_ready_normal_body(connection))
+ {
+ if (response->crc != NULL)
+ pthread_mutex_unlock (&response->mutex);
+ connection->state = MHD_CONNECTION_NORMAL_BODY_UNREADY;
+ break;
+ }
+ ret = SEND (connection->socket_fd,
+ &response->data[connection->response_write_position -
+ response->data_start],
+ response->data_size - (connection->response_write_position -
+ response->data_start), MSG_NOSIGNAL);
+#if DEBUG_SEND_DATA
+ if (ret > 0)
+ fprintf (stderr,
+ "Sent DATA response: `%.*s'\n",
+ ret,
+ &response->data[connection->response_write_position -
+ response->data_start]);
+#endif
+ if (response->crc != NULL)
+ pthread_mutex_unlock (&response->mutex);
+ if (ret < 0)
+ {
+ if (errno == EINTR)
+ return MHD_YES;
#if HAVE_MESSAGES
- MHD_DLOG (connection->daemon, "Unexpected call to %s.\n", __FUNCTION__);
+ MHD_DLOG (connection->daemon,
+ "Failed to send data: %s\n", STRERROR (errno));
#endif
- return MHD_NO;
- }
- if (MHD_NO == connection->have_sent_headers)
- {
- if ((connection->write_buffer == NULL) &&
- (MHD_NO == MHD_build_header_response (connection)))
- {
- /* oops - close! */
+ connection_close_error (connection);
+ return MHD_NO;
+ }
+ connection->response_write_position += ret;
+ if (connection->response_write_position ==
connection->response->total_size)
+ connection->state = MHD_CONNECTION_FOOTERS_SENT; /* have no
footers... */
+ break;
+ case MHD_CONNECTION_NORMAL_BODY_UNREADY:
+ EXTRA_CHECK(0);
+ break;
+ case MHD_CONNECTION_CHUNKED_BODY_READY:
+ do_write(connection);
+ check_write_done(connection,
+ (connection->response->total_size ==
connection->response_write_position)
+ ? MHD_CONNECTION_BODY_SENT
+ : MHD_CONNECTION_CHUNKED_BODY_UNREADY);
+ break;
+ case MHD_CONNECTION_CHUNKED_BODY_UNREADY:
+ case MHD_CONNECTION_BODY_SENT:
+ EXTRA_CHECK(0);
+ break;
+ case MHD_CONNECTION_FOOTERS_SENDING:
+ do_write(connection);
+ check_write_done(connection,
+ MHD_CONNECTION_FOOTERS_SENT);
+ break;
+ case MHD_CONNECTION_FOOTERS_SENT:
+ EXTRA_CHECK(0);
+ break;
+ case MHD_CONNECTION_CLOSED:
+ if (connection->socket_fd != -1)
+ connection_close_error(connection);
+ return MHD_NO;
+ }
+ break;
+ }
+ return MHD_YES;
+}
+
+/**
+ * This function was created to handle per-connection processing that
+ * has to happen even if the socket cannot be read or written to. All
+ * implementations (multithreaded, external select, internal select)
+ * call this function.
+ *
+ * @return MHD_YES if we should continue to process the
+ * connection (not dead yet), MHD_NO if it died
+ */
+int
+MHD_connection_handle_idle (struct MHD_Connection *connection)
+{
+ unsigned int timeout;
+ const char * end;
+ char * line;
+
+ while (1) {
+#if DEBUG_STATES
+ fprintf(stderr,
+ "`%s' in state %u\n",
+ __FUNCTION__,
+ connection->state);
+#endif
+ switch (connection->state)
+ {
+ case MHD_CONNECTION_INIT:
+ line = get_next_header_line(connection);
+ if (line == NULL)
+ break;
+ if (MHD_NO == parse_initial_message_line (connection, line))
+ connection->state = MHD_CONNECTION_CLOSED;
+ else
+ connection->state = MHD_CONNECTION_URL_RECEIVED;
+ continue;
+ case MHD_CONNECTION_URL_RECEIVED:
+ line = get_next_header_line(connection);
+ if (line == NULL)
+ break;
+ if (strlen(line) == 0)
+ {
+ connection->state = MHD_CONNECTION_HEADERS_RECEIVED;
+ continue;
+ }
+ process_header_line(connection, line);
+ connection->state = MHD_CONNECTION_HEADER_PART_RECEIVED;
+ continue;
+ case MHD_CONNECTION_HEADER_PART_RECEIVED:
+ line = get_next_header_line(connection);
+ if (line == NULL)
+ break;
+ process_broken_line(connection,
+ line,
+ MHD_HEADER_KIND);
+ if (strlen(line) == 0)
+ {
+ connection->state = MHD_CONNECTION_HEADERS_RECEIVED;
+ continue;
+ }
+ continue;
+ case MHD_CONNECTION_HEADERS_RECEIVED:
+ parse_connection_headers (connection);
+ if (connection->state == MHD_CONNECTION_CLOSED)
+ continue;
+ connection->state = MHD_CONNECTION_HEADERS_PROCESSED;
+ continue;
+ case MHD_CONNECTION_HEADERS_PROCESSED:
+ call_connection_handler (connection); /* first call */
+ if (need_100_continue(connection))
+ {
+ connection->state = MHD_CONNECTION_CONTINUE_SENDING;
+ break;
+ }
+ connection->state = (connection->remaining_upload_size == 0)
+ ? MHD_CONNECTION_FOOTERS_RECEIVED
+ : MHD_CONNECTION_CONTINUE_SENT;
+ continue;
+ case MHD_CONNECTION_CONTINUE_SENDING:
+ if (connection->continue_message_write_offset ==
+ strlen (HTTP_100_CONTINUE))
+ {
+ connection->state = MHD_CONNECTION_CONTINUE_SENT;
+ continue;
+ }
+ break;
+ case MHD_CONNECTION_CONTINUE_SENT:
+ if (connection->read_buffer_offset != 0) {
+ call_connection_handler(connection); /* loop call */
+ if (connection->state == MHD_CONNECTION_CLOSED)
+ continue;
+ }
+ if ( (connection->remaining_upload_size == 0) ||
+ ( (connection->remaining_upload_size == -1) &&
+ (connection->read_buffer_offset == 0) &&
+ (MHD_YES == connection->read_closed) ) )
+ {
+ if ( (MHD_YES == connection->have_chunked_upload) &&
+ (MHD_NO == connection->read_closed) )
+ connection->state = MHD_CONNECTION_BODY_RECEIVED;
+ else
+ connection->state = MHD_CONNECTION_FOOTERS_RECEIVED;
+ continue;
+ }
+ break;
+ case MHD_CONNECTION_BODY_RECEIVED:
+ line = get_next_header_line(connection);
+ if (line == NULL)
+ break;
+ if (strlen(line) == 0)
+ {
+ connection->state = MHD_CONNECTION_FOOTERS_RECEIVED;
+ continue;
+ }
+ process_header_line(connection, line);
+ connection->state = MHD_CONNECTION_FOOTER_PART_RECEIVED;
+ continue;
+ case MHD_CONNECTION_FOOTER_PART_RECEIVED:
+ line = get_next_header_line(connection);
+ if (line == NULL)
+ break;
+ process_broken_line(connection,
+ line,
+ MHD_FOOTER_KIND);
+ if (strlen(line) == 0)
+ {
+ connection->state = MHD_CONNECTION_FOOTERS_RECEIVED;
+ continue;
+ }
+ continue;
+ case MHD_CONNECTION_FOOTERS_RECEIVED:
+ call_connection_handler (connection); /* "final" call */
+ if (connection->state == MHD_CONNECTION_CLOSED)
+ continue;
+ if (connection->response == NULL)
+ break; /* try again next time */
+ if (MHD_NO == build_header_response (connection))
+ {
+ /* oops - close! */
#if HAVE_MESSAGES
- MHD_DLOG (connection->daemon,
- "Closing connection (failed to create response header)\n");
+ MHD_DLOG (connection->daemon,
+ "Closing connection (failed to create response
header)\n");
#endif
- connection_close_error (connection);
- return MHD_NO;
- }
- ret = SEND (connection->socket_fd,
- &connection->write_buffer[connection->
- write_buffer_send_offset],
- connection->write_buffer_append_offset -
- connection->write_buffer_send_offset, MSG_NOSIGNAL);
- if (ret < 0)
- {
- if (errno == EINTR)
- return MHD_YES;
-#if HAVE_MESSAGES
- MHD_DLOG (connection->daemon,
- "Failed to send data: %s\n", STRERROR (errno));
-#endif
- connection_close_error (connection);
- return MHD_YES;
- }
-#if DEBUG_SEND_DATA
- fprintf (stderr,
- "Sent HEADER response: `%.*s'\n",
- ret,
- &connection->write_buffer[connection->
- write_buffer_send_offset]);
-#endif
- connection->write_buffer_send_offset += ret;
- if (connection->write_buffer_append_offset ==
- connection->write_buffer_send_offset)
- {
- connection->write_buffer_append_offset = 0;
- connection->write_buffer_send_offset = 0;
- connection->have_sent_headers = MHD_YES;
- MHD_pool_reallocate (connection->pool,
- connection->write_buffer,
- connection->write_buffer_size, 0);
- connection->write_buffer = NULL;
- connection->write_buffer_size = 0;
- }
- return MHD_YES;
- }
- if (response->total_size < connection->response_write_position)
- abort (); /* internal error */
- if (response->crc != NULL)
- pthread_mutex_lock (&response->mutex);
-
- /* prepare send buffer */
- if ((response->crc != NULL) &&
- ((response->data_start > connection->response_write_position) ||
- (response->data_start + response->data_size <=
- connection->response_write_position))
- && (MHD_YES != ready_response (connection)))
+ connection->state = MHD_CONNECTION_CLOSED;
+ continue;
+ }
+ connection->state = MHD_CONNECTION_HEADERS_SENDING;
+ break;
+ case MHD_CONNECTION_HEADERS_SENDING:
+ /* no default action */
+ break;
+ case MHD_CONNECTION_HEADERS_SENT:
+ if (connection->have_chunked_upload)
+ connection->state = MHD_CONNECTION_CHUNKED_BODY_UNREADY;
+ else
+ connection->state = MHD_CONNECTION_NORMAL_BODY_UNREADY;
+ continue;
+ case MHD_CONNECTION_NORMAL_BODY_READY:
+ /* nothing to do here */
+ break;
+ case MHD_CONNECTION_NORMAL_BODY_UNREADY:
+ if (connection->response->crc != NULL)
+ pthread_mutex_lock (&connection->response->mutex);
+ if (MHD_YES == try_ready_normal_body(connection))
+ {
+ if (connection->response->crc != NULL)
+ pthread_mutex_unlock (&connection->response->mutex);
+ connection->state = MHD_CONNECTION_NORMAL_BODY_READY;
+ break;
+ }
+ if (connection->response->crc != NULL)
+ pthread_mutex_unlock (&connection->response->mutex);
+ /* not ready, no socket action */
+ break;
+ case MHD_CONNECTION_CHUNKED_BODY_READY:
+ /* nothing to do here */
+ break;
+ case MHD_CONNECTION_CHUNKED_BODY_UNREADY:
+ if (connection->response->crc != NULL)
+ pthread_mutex_lock (&connection->response->mutex);
+ if (MHD_YES == try_ready_chunked_body(connection))
+ {
+ if (connection->response->crc != NULL)
+ pthread_mutex_unlock (&connection->response->mutex);
+ connection->state = MHD_CONNECTION_CHUNKED_BODY_READY;
+ continue;
+ }
+ if (connection->response->crc != NULL)
+ pthread_mutex_unlock (&connection->response->mutex);
+ break;
+ case MHD_CONNECTION_BODY_SENT:
+ build_header_response(connection);
+ if (connection->write_buffer_send_offset ==
connection->write_buffer_append_offset)
+ connection->state = MHD_CONNECTION_FOOTERS_SENT;
+ else
+ connection->state = MHD_CONNECTION_FOOTERS_SENDING;
+ continue;
+ case MHD_CONNECTION_FOOTERS_SENDING:
+ /* no default action */
+ break;
+ case MHD_CONNECTION_FOOTERS_SENT:
+ MHD_destroy_response (connection->response);
+ if (connection->daemon->notify_completed != NULL)
+ connection->daemon->notify_completed (connection->daemon->
+ notify_completed_cls,
+ connection,
+ &connection->client_context,
+
MHD_REQUEST_TERMINATED_COMPLETED_OK);
+ end = MHD_lookup_connection_value (connection,
+ MHD_HEADER_KIND,
+ MHD_HTTP_HEADER_CONNECTION);
+ connection->client_context = NULL;
+ connection->continue_message_write_offset = 0;
+ connection->responseCode = 0;
+ connection->response = NULL;
+ connection->headers_received = NULL;
+ connection->response_write_position = 0;
+ connection->have_chunked_upload = MHD_NO;
+ connection->method = NULL;
+ connection->url = NULL;
+ connection->write_buffer = NULL;
+ connection->write_buffer_size = 0;
+ connection->write_buffer_send_offset = 0;
+ connection->write_buffer_append_offset = 0;
+ if ((end != NULL) && (0 == strcasecmp (end, "close")))
+ {
+ connection->read_closed = MHD_YES;
+ connection->read_buffer_offset = 0;
+ }
+ if ( ( (MHD_YES == connection->read_closed) &&
+ (0 == connection->read_buffer_offset) ) ||
+ (connection->version == NULL) ||
+ (0 != strcasecmp (MHD_HTTP_VERSION_1_1, connection->version)) )
+ {
+ connection->state = MHD_CONNECTION_CLOSED;
+ MHD_pool_destroy (connection->pool);
+ connection->pool = NULL;
+ connection->read_buffer = NULL;
+ connection->read_buffer_size = 0;
+ connection->read_buffer_offset = 0;
+ }
+ else
+ {
+ connection->version = NULL;
+ connection->state = MHD_CONNECTION_INIT;
+ connection->read_buffer
+ = MHD_pool_reset(connection->pool,
+ connection->read_buffer,
+ connection->read_buffer_size);
+ }
+ continue;
+ case MHD_CONNECTION_CLOSED:
+ if (connection->socket_fd != -1)
+ connection_close_error (connection);
+ break;
+ default:
+ EXTRA_CHECK(0);
+ break;
+ }
+ break;
+ }
+ timeout = connection->daemon->connection_timeout;
+ if ( (timeout != 0) &&
+ (time(NULL) - timeout > connection->last_activity) )
{
- pthread_mutex_unlock (&response->mutex);
- return MHD_YES;
- }
- /* transmit */
- ret = SEND (connection->socket_fd,
- &response->data[connection->response_write_position -
- response->data_start],
- response->data_size - (connection->response_write_position -
- response->data_start), MSG_NOSIGNAL);
- if (response->crc != NULL)
- pthread_mutex_unlock (&response->mutex);
- if (ret < 0)
- {
- if (errno == EINTR)
- return MHD_YES;
-#if HAVE_MESSAGES
- MHD_DLOG (connection->daemon,
- "Failed to send data: %s\n", STRERROR (errno));
-#endif
connection_close_error (connection);
- return MHD_YES;
+ return MHD_NO;
}
-#if DEBUG_SEND_DATA
- fprintf (stderr,
- "Sent DATA response: `%.*s'\n",
- ret,
- &response->data[connection->response_write_position -
- response->data_start]);
-#endif
- connection->response_write_position += ret;
- if (connection->response_write_position > response->total_size)
- abort (); /* internal error */
- if (connection->response_write_position == response->total_size)
- {
- if ((connection->have_received_body == MHD_NO) ||
- (connection->have_received_headers == MHD_NO))
- abort (); /* internal error */
- MHD_destroy_response (response);
- if (connection->daemon->notify_completed != NULL)
- connection->daemon->notify_completed (connection->daemon->
- notify_completed_cls,
- connection,
- &connection->client_context,
-
MHD_REQUEST_TERMINATED_COMPLETED_OK);
- end = MHD_lookup_connection_value (connection,
- MHD_HEADER_KIND,
- MHD_HTTP_HEADER_CONNECTION);
- connection->client_context = NULL;
- connection->continue_message_write_offset = 0;
- connection->responseCode = 0;
- connection->response = NULL;
- connection->headers_received = NULL;
- connection->have_received_headers = MHD_NO;
- connection->have_sent_headers = MHD_NO;
- connection->have_received_body = MHD_NO;
- connection->response_write_position = 0;
- connection->have_chunked_upload = MHD_NO;
- connection->method = NULL;
- connection->url = NULL;
- if ((end != NULL) && (0 == strcasecmp (end, "close")))
- {
- /* other side explicitly requested
- that we close the connection after
- this request */
- connection->read_close = MHD_YES;
- }
- if ((connection->read_close == MHD_YES) ||
- (0 != strcasecmp (MHD_HTTP_VERSION_1_1, connection->version)))
- {
- /* closed for reading => close for good! */
- if (connection->socket_fd != -1)
- {
-#if DEBUG_CLOSE
-#if HAVE_MESSAGES
- MHD_DLOG (connection->daemon,
- "Closing connection (http 1.0 or end-of-stream for
unknown content length)\n");
-#endif
-#endif
- SHUTDOWN (connection->socket_fd, SHUT_RDWR);
- CLOSE (connection->socket_fd);
- }
- connection->socket_fd = -1;
- }
- connection->version = NULL;
- connection->read_buffer = NULL;
- connection->write_buffer = NULL;
- connection->read_buffer_size = 0;
- connection->read_buffer_offset = 0;
- connection->write_buffer_size = 0;
- connection->write_buffer_send_offset = 0;
- connection->write_buffer_append_offset = 0;
- MHD_pool_destroy (connection->pool);
- connection->pool = NULL;
- }
return MHD_YES;
+
}
/* end of connection.c */
Modified: libmicrohttpd/src/daemon/connection.h
===================================================================
--- libmicrohttpd/src/daemon/connection.h 2007-12-20 00:40:49 UTC (rev
5979)
+++ libmicrohttpd/src/daemon/connection.h 2007-12-20 04:23:00 UTC (rev
5980)
@@ -39,18 +39,14 @@
fd_set * write_fd_set,
fd_set * except_fd_set, int *max_fd);
-
/**
- * Call the handler of the application for this
- * connection.
- */
-void MHD_call_connection_handler (struct MHD_Connection *connection);
-
-/**
* This function handles a particular connection when it has been
* determined that there is data to be read off a socket. All implementations
* (multithreaded, external select, internal select) call this function
* to handle reads.
+ *
+ * @return MHD_YES if we should continue to process the
+ * connection (not dead yet), MHD_NO if it died
*/
int MHD_connection_handle_read (struct MHD_Connection *connection);
@@ -60,8 +56,23 @@
* determined that the socket can be written to. If there is no data
* to be written, however, the function call does nothing. All implementations
* (multithreaded, external select, internal select) call this function
+ *
+ * @return MHD_YES if we should continue to process the
+ * connection (not dead yet), MHD_NO if it died
*/
int MHD_connection_handle_write (struct MHD_Connection *connection);
+/**
+ * This function was created to handle per-connection processing that
+ * has to happen even if the socket cannot be read or written to. All
+ * implementations (multithreaded, external select, internal select)
+ * call this function.
+ *
+ * @return MHD_YES if we should continue to process the
+ * connection (not dead yet), MHD_NO if it died
+ */
+int
+MHD_connection_handle_idle (struct MHD_Connection *connection);
+
#endif
Modified: libmicrohttpd/src/daemon/daemon.c
===================================================================
--- libmicrohttpd/src/daemon/daemon.c 2007-12-20 00:40:49 UTC (rev 5979)
+++ libmicrohttpd/src/daemon/daemon.c 2007-12-20 04:23:00 UTC (rev 5980)
@@ -52,87 +52,8 @@
*/
#define DEBUG_CONNECT MHD_NO
-/**
- * Register an access handler for all URIs beginning with uri_prefix.
- *
- * @param uri_prefix
- * @return MRI_NO if a handler for this exact prefix
- * already exists
- */
-int
-MHD_register_handler (struct MHD_Daemon *daemon,
- const char *uri_prefix,
- MHD_AccessHandlerCallback dh, void *dh_cls)
-{
- struct MHD_Access_Handler *ah;
- if ((daemon == NULL) || (uri_prefix == NULL) || (dh == NULL))
- return MHD_NO;
- ah = daemon->handlers;
- while (ah != NULL)
- {
- if (0 == strcmp (uri_prefix, ah->uri_prefix))
- return MHD_NO;
- ah = ah->next;
- }
- ah = malloc (sizeof (struct MHD_Access_Handler));
- if (ah == NULL)
- {
-#if HAVE_MESSAGES
- MHD_DLOG (daemon, "Error allocating memory: %s\n", STRERROR (errno));
-#endif
- return MHD_NO;
- }
-
- ah->next = daemon->handlers;
- ah->uri_prefix = strdup (uri_prefix);
- ah->dh = dh;
- ah->dh_cls = dh_cls;
- daemon->handlers = ah;
- return MHD_YES;
-}
-
-
/**
- * Unregister an access handler for the URIs beginning with
- * uri_prefix.
- *
- * @param uri_prefix
- * @return MHD_NO if a handler for this exact prefix
- * is not known for this daemon
- */
-int
-MHD_unregister_handler (struct MHD_Daemon *daemon,
- const char *uri_prefix,
- MHD_AccessHandlerCallback dh, void *dh_cls)
-{
- struct MHD_Access_Handler *prev;
- struct MHD_Access_Handler *pos;
-
- if ((daemon == NULL) || (uri_prefix == NULL) || (dh == NULL))
- return MHD_NO;
- pos = daemon->handlers;
- prev = NULL;
- while (pos != NULL)
- {
- if ((dh == pos->dh) &&
- (dh_cls == pos->dh_cls) &&
- (0 == strcmp (uri_prefix, pos->uri_prefix)))
- {
- if (prev == NULL)
- daemon->handlers = pos->next;
- else
- prev->next = pos->next;
- free (pos);
- return MHD_YES;
- }
- prev = pos;
- pos = pos->next;
- }
- return MHD_NO;
-}
-
-/**
* Obtain the select sets for this daemon.
*
* @return MHD_YES on success, MHD_NO if this
@@ -175,7 +96,6 @@
return MHD_YES;
}
-
/**
* Main function of the thread that handles an individual
* connection.
@@ -191,26 +111,27 @@
int max;
struct timeval tv;
unsigned int timeout;
- time_t now;
+ unsigned int now;
if (con == NULL)
abort ();
timeout = con->daemon->connection_timeout;
- now = time (NULL);
- while ((!con->daemon->shutdown) &&
- (con->socket_fd != -1) &&
- ((timeout == 0) || (now - timeout > con->last_activity)))
+ while ( (!con->daemon->shutdown) &&
+ (con->socket_fd != -1) )
{
FD_ZERO (&rs);
FD_ZERO (&ws);
FD_ZERO (&es);
max = 0;
MHD_connection_get_fdset (con, &rs, &ws, &es, &max);
+ now = time(NULL);
tv.tv_usec = 0;
- tv.tv_sec = timeout - (now - con->last_activity);
+ if ( timeout > (now - con->last_activity) )
+ tv.tv_sec = timeout - (now - con->last_activity);
+ else
+ tv.tv_sec = 0;
num_ready = SELECT (max + 1,
&rs, &ws, &es, (timeout != 0) ? &tv : NULL);
- now = time (NULL);
if (num_ready < 0)
{
if (errno == EINTR)
@@ -221,18 +142,13 @@
#endif
break;
}
- if (((FD_ISSET (con->socket_fd, &rs)) &&
- (MHD_YES != MHD_connection_handle_read (con))) ||
- ((con->socket_fd != -1) &&
- (FD_ISSET (con->socket_fd, &ws)) &&
- (MHD_YES != MHD_connection_handle_write (con))))
- break;
- if ((con->have_received_headers == MHD_YES) && (con->response == NULL))
- MHD_call_connection_handler (con);
+ if (FD_ISSET (con->socket_fd, &rs))
+ MHD_connection_handle_read (con);
if ((con->socket_fd != -1) &&
- ((FD_ISSET (con->socket_fd, &rs)) ||
- (FD_ISSET (con->socket_fd, &ws))))
- con->last_activity = now;
+ (FD_ISSET (con->socket_fd, &ws)) )
+ MHD_connection_handle_write (con);
+ if (con->socket_fd != -1)
+ MHD_connection_handle_idle (con);
}
if (con->socket_fd != -1)
{
@@ -370,11 +286,6 @@
* Free resources associated with all closed connections.
* (destroy responses, free buffers, etc.). A connection
* is known to be closed if the socket_fd is -1.
- *
- * Also performs connection actions that need to be run
- * even if the connection is not selectable (such as
- * calling the application again with upload data when
- * the upload data buffer is full).
*/
static void
MHD_cleanup_connections (struct MHD_Daemon *daemon)
@@ -382,33 +293,11 @@
struct MHD_Connection *pos;
struct MHD_Connection *prev;
void *unused;
- time_t timeout;
- timeout = time (NULL);
- if (daemon->connection_timeout != 0)
- timeout -= daemon->connection_timeout;
- else
- timeout = 0;
pos = daemon->connections;
prev = NULL;
while (pos != NULL)
{
- if ((pos->last_activity < timeout) && (pos->socket_fd != -1))
- {
-#if DEBUG_CLOSE
-#if HAVE_MESSAGES
- MHD_DLOG (daemon, "Connection timed out, closing connection\n");
-#endif
-#endif
- SHUTDOWN (pos->socket_fd, SHUT_RDWR);
- CLOSE (pos->socket_fd);
- pos->socket_fd = -1;
- if (pos->daemon->notify_completed != NULL)
- pos->daemon->notify_completed (pos->daemon->notify_completed_cls,
- pos,
- &pos->client_context,
-
MHD_REQUEST_TERMINATED_TIMEOUT_REACHED);
- }
if (pos->socket_fd == -1)
{
if (prev == NULL)
@@ -420,8 +309,7 @@
pthread_kill (pos->pid, SIGALRM);
pthread_join (pos->pid, &unused);
}
- if (pos->response != NULL)
- MHD_destroy_response (pos->response);
+ MHD_destroy_response (pos->response);
MHD_pool_destroy (pos->pool);
free (pos->addr);
free (pos);
@@ -432,12 +320,6 @@
pos = prev->next;
continue;
}
-
- if ((0 == (daemon->options & MHD_USE_THREAD_PER_CONNECTION)) &&
- ((pos->have_received_headers == MHD_YES)
- && (pos->response == NULL)))
- MHD_call_connection_handler (pos);
-
prev = pos;
pos = pos->next;
}
@@ -573,15 +455,12 @@
if (ds != -1)
{
if (FD_ISSET (ds, &rs))
- {
- pos->last_activity = now;
- MHD_connection_handle_read (pos);
- }
- if (FD_ISSET (ds, &ws))
- {
- pos->last_activity = now;
- MHD_connection_handle_write (pos);
- }
+ MHD_connection_handle_read (pos);
+ if ( (pos->socket_fd != -1) &&
+ (FD_ISSET (ds, &ws)) )
+ MHD_connection_handle_write (pos);
+ if (pos->socket_fd != -1)
+ MHD_connection_handle_idle(pos);
}
pos = pos->next;
}
@@ -726,10 +605,8 @@
retVal->apc = apc;
retVal->apc_cls = apc_cls;
retVal->socket_fd = socket_fd;
- retVal->default_handler.dh = dh;
- retVal->default_handler.dh_cls = dh_cls;
- retVal->default_handler.uri_prefix = "";
- retVal->default_handler.next = NULL;
+ retVal->default_handler = dh;
+ retVal->default_handler_cls = dh_cls;
retVal->max_connections = MHD_MAX_CONNECTIONS_DEFAULT;
retVal->pool_size = MHD_POOL_SIZE_DEFAULT;
retVal->connection_timeout = 0; /* no timeout */
Modified: libmicrohttpd/src/daemon/daemontest_get.c
===================================================================
--- libmicrohttpd/src/daemon/daemontest_get.c 2007-12-20 00:40:49 UTC (rev
5979)
+++ libmicrohttpd/src/daemon/daemontest_get.c 2007-12-20 04:23:00 UTC (rev
5980)
@@ -66,16 +66,24 @@
const char *upload_data, unsigned int *upload_data_size,
void **unused)
{
+ static int ptr;
const char *me = cls;
struct MHD_Response *response;
int ret;
if (0 != strcmp (me, method))
return MHD_NO; /* unexpected method */
+ if (&ptr != *unused) {
+ *unused = &ptr;
+ return MHD_YES;
+ }
+ *unused = NULL;
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);
+ if (ret == MHD_NO)
+ abort();
return ret;
}
@@ -93,11 +101,11 @@
cbc.size = 2048;
cbc.pos = 0;
d = MHD_start_daemon (MHD_USE_SELECT_INTERNALLY | MHD_USE_DEBUG,
- 1080, NULL, NULL, &ahc_echo, "GET", MHD_OPTION_END);
+ 11080, NULL, NULL, &ahc_echo, "GET", MHD_OPTION_END);
if (d == NULL)
return 1;
c = curl_easy_init ();
- curl_easy_setopt (c, CURLOPT_URL, "http://localhost:1080/hello_world");
+ curl_easy_setopt (c, CURLOPT_URL, "http://localhost:11080/hello_world");
curl_easy_setopt (c, CURLOPT_WRITEFUNCTION, ©Buffer);
curl_easy_setopt (c, CURLOPT_WRITEDATA, &cbc);
curl_easy_setopt (c, CURLOPT_FAILONERROR, 1);
@@ -150,12 +158,12 @@
curl_easy_setopt (c, CURLOPT_WRITEFUNCTION, ©Buffer);
curl_easy_setopt (c, CURLOPT_WRITEDATA, &cbc);
curl_easy_setopt (c, CURLOPT_FAILONERROR, 1);
- curl_easy_setopt (c, CURLOPT_TIMEOUT, 2L);
+ curl_easy_setopt (c, CURLOPT_TIMEOUT, 150L);
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);
+ curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT, 15L);
// NOTE: use of CONNECTTIMEOUT without also
// setting NOSIGNAL results in really weird
// crashes on my system!
@@ -214,8 +222,8 @@
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_TIMEOUT, 5L);
- curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT, 5L);
+ curl_easy_setopt (c, CURLOPT_TIMEOUT, 150L);
+ curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT, 15L);
// NOTE: use of CONNECTTIMEOUT without also
// setting NOSIGNAL results in really weird
// crashes on my system!
Modified: libmicrohttpd/src/daemon/daemontest_large_put.c
===================================================================
--- libmicrohttpd/src/daemon/daemontest_large_put.c 2007-12-20 00:40:49 UTC
(rev 5979)
+++ libmicrohttpd/src/daemon/daemontest_large_put.c 2007-12-20 04:23:00 UTC
(rev 5980)
@@ -96,8 +96,16 @@
return MHD_NO; /* unexpected method */
if ((*done) == 0)
{
- if (*upload_data_size != PUT_SIZE)
- return MHD_YES; /* not yet ready */
+ if (*upload_data_size != PUT_SIZE)
+ {
+#if 0
+ fprintf(stderr,
+ "Waiting for more data (%u/%u)...\n",
+ *upload_data_size,
+ PUT_SIZE);
+#endif
+ return MHD_YES; /* not yet ready */
+ }
if (0 == memcmp (upload_data, put_buffer, PUT_SIZE))
{
*upload_data_size = 0;
@@ -227,7 +235,13 @@
curl_easy_cleanup (c);
MHD_stop_daemon (d);
if (cbc.pos != strlen ("/hello_world"))
- return 64;
+ {
+ fprintf(stderr,
+ "Got invalid response `%.*s'\n",
+ cbc.pos,
+ cbc.buf);
+ return 64;
+ }
if (0 != strncmp ("/hello_world", cbc.buf, strlen ("/hello_world")))
return 128;
return 0;
@@ -260,7 +274,10 @@
multi = NULL;
d = MHD_start_daemon (MHD_USE_DEBUG,
1082,
- NULL, NULL, &ahc_echo, &done_flag, MHD_OPTION_END);
+ NULL, NULL, &ahc_echo, &done_flag,
+ MHD_OPTION_CONNECTION_MEMORY_LIMIT,
+ PUT_SIZE * 4,
+ MHD_OPTION_END);
if (d == NULL)
return 256;
c = curl_easy_init ();
@@ -357,9 +374,15 @@
}
MHD_stop_daemon (d);
if (cbc.pos != strlen ("/hello_world"))
- return 64;
+ {
+ fprintf(stderr,
+ "Got invalid response `%.*s'\n",
+ cbc.pos,
+ cbc.buf);
+ return 8192;
+ }
if (0 != strncmp ("/hello_world", cbc.buf, strlen ("/hello_world")))
- return 128;
+ return 16384;
return 0;
}
@@ -380,7 +403,7 @@
errorCount += testInternalPut ();
errorCount += testMultithreadedPut ();
}
- errorCount += testExternalPut ();
+ errorCount += testExternalPut ();
free (put_buffer);
if (errorCount != 0)
fprintf (stderr, "Error (code: %u)\n", errorCount);
Modified: libmicrohttpd/src/daemon/daemontest_postform.c
===================================================================
--- libmicrohttpd/src/daemon/daemontest_postform.c 2007-12-20 00:40:49 UTC
(rev 5979)
+++ libmicrohttpd/src/daemon/daemontest_postform.c 2007-12-20 04:23:00 UTC
(rev 5980)
@@ -72,6 +72,13 @@
{
int *eok = cls;
+#if 0
+ fprintf(stderr,
+ "PI sees %s-%.*s\n",
+ key,
+ size,
+ value);
+#endif
if ((0 == strcmp (key, "name")) &&
(size == strlen ("daniel")) && (0 == strncmp (value, "daniel", size)))
(*eok) |= 1;
@@ -95,7 +102,7 @@
struct MHD_PostProcessor *pp;
int ret;
- if (0 != strcmp ("POST", method))
+ if (0 != strcmp ("POST", method))
{
printf ("METHOD: %s\n", method);
return MHD_NO; /* unexpected method */
Modified: libmicrohttpd/src/daemon/daemontest_put_chunked.c
===================================================================
--- libmicrohttpd/src/daemon/daemontest_put_chunked.c 2007-12-20 00:40:49 UTC
(rev 5979)
+++ libmicrohttpd/src/daemon/daemontest_put_chunked.c 2007-12-20 04:23:00 UTC
(rev 5980)
@@ -105,6 +105,11 @@
printf ("Invalid upload data `%8s'!\n", upload_data);
return MHD_NO;
}
+#if 0
+ fprintf(stderr,
+ "Not ready for response: %u/%u\n",
+ *done, 8);
+#endif
return MHD_YES;
}
response = MHD_create_response_from_data (strlen (url),
Modified: libmicrohttpd/src/daemon/fileserver_example.c
===================================================================
--- libmicrohttpd/src/daemon/fileserver_example.c 2007-12-20 00:40:49 UTC
(rev 5979)
+++ libmicrohttpd/src/daemon/fileserver_example.c 2007-12-20 04:23:00 UTC
(rev 5980)
@@ -52,8 +52,9 @@
const char *url,
const char *method,
const char *upload_data,
- const char *version, unsigned int *upload_data_size, void **unused)
+ const char *version, unsigned int *upload_data_size, void **ptr)
{
+ static int aptr;
struct MHD_Response *response;
int ret;
FILE *file;
@@ -61,6 +62,13 @@
if (0 != strcmp (method, "GET"))
return MHD_NO; /* unexpected method */
+ if (&aptr != *ptr)
+ {
+ /* do never respond on first call */
+ *ptr = &aptr;
+ return MHD_YES;
+ }
+ *ptr = NULL; /* reset when done */
file = fopen (&url[1], "r");
if (file == NULL)
{
Modified: libmicrohttpd/src/daemon/internal.h
===================================================================
--- libmicrohttpd/src/daemon/internal.h 2007-12-20 00:40:49 UTC (rev 5979)
+++ libmicrohttpd/src/daemon/internal.h 2007-12-20 04:23:00 UTC (rev 5980)
@@ -85,21 +85,9 @@
char *value;
enum MHD_ValueKind kind;
-};
-
-struct MHD_Access_Handler
-{
- struct MHD_Access_Handler *next;
-
- char *uri_prefix;
-
- MHD_AccessHandlerCallback dh;
-
- void *dh_cls;
};
-
/**
* Representation of a response.
*/
@@ -172,8 +160,130 @@
};
+/**
+ * States in a state machine for a connection.
+ *
+ * Transitions are any-state to CLOSED, any state to state+1,
+ * FOOTERS_SENT to INIT. CLOSED is the terminal state and
+ * INIT the initial state.
+ *
+ * Note that transitions for *reading* happen only after
+ * the input has been processed; transitions for
+ * *writing* happen after the respective data has been
+ * put into the write buffer (the write does not have
+ * to be completed yet). A transition to CLOSED or INIT
+ * requires the write to be complete.
+ */
+enum MHD_CONNECTION_STATE
+{
+ /**
+ * Connection just started (no headers received).
+ * Waiting for the line with the request type, URL and version.
+ */
+ MHD_CONNECTION_INIT = 0,
+ /**
+ * 1: We got the URL (and request type and version). Wait for a header line.
+ */
+ MHD_CONNECTION_URL_RECEIVED = MHD_CONNECTION_INIT + 1,
+ /**
+ * 2: We got part of a multi-line request header. Wait for the rest.
+ */
+ MHD_CONNECTION_HEADER_PART_RECEIVED = MHD_CONNECTION_URL_RECEIVED + 1,
+
+ /**
+ * 3: We got the request headers. Process them.
+ */
+ MHD_CONNECTION_HEADERS_RECEIVED = MHD_CONNECTION_HEADER_PART_RECEIVED + 1,
+
+ /**
+ * 4: We have processed the request headers. Send 100 continue.
+ */
+ MHD_CONNECTION_HEADERS_PROCESSED = MHD_CONNECTION_HEADERS_RECEIVED + 1,
+
+ /**
+ * 5: We have processed the headers and need to send 100 CONTINUE.
+ */
+ MHD_CONNECTION_CONTINUE_SENDING = MHD_CONNECTION_HEADERS_PROCESSED + 1,
+
+ /**
+ * 6: We have sent 100 CONTINUE (or do not need to). Read the message body.
+ */
+ MHD_CONNECTION_CONTINUE_SENT = MHD_CONNECTION_CONTINUE_SENDING + 1,
+
+ /**
+ * 7: We got the request body. Wait for a line of the footer.
+ */
+ MHD_CONNECTION_BODY_RECEIVED = MHD_CONNECTION_CONTINUE_SENT + 1,
+
+ /**
+ * 8: We got part of a line of the footer. Wait for the
+ * rest.
+ */
+ MHD_CONNECTION_FOOTER_PART_RECEIVED = MHD_CONNECTION_BODY_RECEIVED + 1,
+
+ /**
+ * 9: We received the entire footer. Wait for a response to be queued
+ * and prepare the response headers.
+ */
+ MHD_CONNECTION_FOOTERS_RECEIVED = MHD_CONNECTION_FOOTER_PART_RECEIVED + 1,
+
+ /**
+ * 10: We have prepared the response headers in the writ buffer.
+ * Send the response headers.
+ */
+ MHD_CONNECTION_HEADERS_SENDING = MHD_CONNECTION_FOOTERS_RECEIVED + 1,
+
+ /**
+ * 11: We have sent the response headers. Get ready to send the body.
+ */
+ MHD_CONNECTION_HEADERS_SENT = MHD_CONNECTION_HEADERS_SENDING + 1,
+
+ /**
+ * 12: We are ready to send a part of a non-chunked body. Send it.
+ */
+ MHD_CONNECTION_NORMAL_BODY_READY = MHD_CONNECTION_HEADERS_SENT + 1,
+
+ /**
+ * 13: We are waiting for the client to provide more
+ * data of a non-chunked body.
+ */
+ MHD_CONNECTION_NORMAL_BODY_UNREADY = MHD_CONNECTION_NORMAL_BODY_READY + 1,
+
+ /**
+ * 14: We are ready to send a chunk.
+ */
+ MHD_CONNECTION_CHUNKED_BODY_READY = MHD_CONNECTION_NORMAL_BODY_UNREADY + 1,
+
+ /**
+ * 15: We are waiting for the client to provide a chunk of the body.
+ */
+ MHD_CONNECTION_CHUNKED_BODY_UNREADY = MHD_CONNECTION_CHUNKED_BODY_READY + 1,
+
+ /**
+ * 16: We have sent the response body. Prepare the footers.
+ */
+ MHD_CONNECTION_BODY_SENT = MHD_CONNECTION_CHUNKED_BODY_UNREADY + 1,
+
+ /**
+ * 17: We have prepared the response footer. Send it.
+ */
+ MHD_CONNECTION_FOOTERS_SENDING = MHD_CONNECTION_BODY_SENT + 1,
+
+ /**
+ * 18: We have sent the response footer. Shutdown or restart.
+ */
+ MHD_CONNECTION_FOOTERS_SENT = MHD_CONNECTION_FOOTERS_SENDING + 1,
+
+ /**
+ * 19: This connection is closed (no more activity
+ * allowed).
+ */
+ MHD_CONNECTION_CLOSED = MHD_CONNECTION_FOOTERS_SENT + 1,
+
+};
+
struct MHD_Connection
{
@@ -250,6 +360,21 @@
char *write_buffer;
/**
+ * Last incomplete header line during parsing of headers.
+ * Allocated in pool. Only valid if state is
+ * either HEADER_PART_RECEIVED or FOOTER_PART_RECEIVED.
+ */
+ char *last;
+
+ /**
+ * Position after the colon on the last incomplete header
+ * line during parsing of headers.
+ * Allocated in pool. Only valid if state is
+ * either HEADER_PART_RECEIVED or FOOTER_PART_RECEIVED.
+ */
+ char *colon;
+
+ /**
* Foreign address (of length addr_len). MALLOCED (not
* in pool!).
*/
@@ -292,6 +417,12 @@
size_t write_buffer_append_offset;
/**
+ * How many more bytes of the body do we expect
+ * to read? "-1" for unknown.
+ */
+ size_t remaining_upload_size;
+
+ /**
* Current write position in the actual response
* (excluding headers, content only; should be 0
* while sending headers).
@@ -299,13 +430,6 @@
size_t response_write_position;
/**
- * Remaining (!) number of bytes in the upload.
- * Set to -1 for unknown (connection will close
- * to indicate end of upload).
- */
- size_t remaining_upload_size;
-
- /**
* Position in the 100 CONTINUE message that
* we need to send when receiving http 1.1 requests.
*/
@@ -333,31 +457,17 @@
* Has this socket been closed for reading (i.e.
* other side closed the connection)? If so,
* we must completely close the connection once
- * we are done sending our response.
+ * we are done sending our response (and stop
+ * trying to read from this socket).
*/
- int read_close;
+ int read_closed;
/**
- * Have we finished receiving all of the headers yet?
- * Set to 1 once we are done processing all of the
- * headers. Note that due to pipelining, it is
- * possible that the NEXT request is already
- * (partially) waiting in the read buffer.
+ * State in the FSM for this connection.
*/
- int have_received_headers;
+ enum MHD_CONNECTION_STATE state;
/**
- * Have we finished receiving the data from a
- * potential file-upload?
- */
- int have_received_body;
-
- /**
- * Have we finished sending all of the headers yet?
- */
- int have_sent_headers;
-
- /**
* HTTP response code. Only valid if response object
* is already set.
*/
@@ -373,6 +483,11 @@
int response_unready;
/**
+ * Are we sending with chunked encoding?
+ */
+ int have_chunked_response;
+
+ /**
* Are we receiving with chunked encoding? This will be set to
* MHD_YES after we parse the headers and are processing the body
* with chunks. After we are done with the body and we are
@@ -402,9 +517,15 @@
struct MHD_Daemon
{
- struct MHD_Access_Handler *handlers;
+ /**
+ * Callback function for all requests.
+ */
+ MHD_AccessHandlerCallback default_handler;
- struct MHD_Access_Handler default_handler;
+ /**
+ * Closure argument to default_handler.
+ */
+ void * default_handler_cls;
/**
* Linked list of our current connections.
Modified: libmicrohttpd/src/daemon/memorypool.c
===================================================================
--- libmicrohttpd/src/daemon/memorypool.c 2007-12-20 00:40:49 UTC (rev
5979)
+++ libmicrohttpd/src/daemon/memorypool.c 2007-12-20 04:23:00 UTC (rev
5980)
@@ -188,4 +188,33 @@
return NULL;
}
+/**
+ * Clear all entries from the memory pool except
+ * for "keep" of the given "size".
+ *
+ * @param keep pointer to the entry to keep (maybe NULL)
+ * @param size how many bytes need to be kept at this address
+ * @return addr new address of "keep" (if it had to change)
+ */
+void *MHD_pool_reset(struct MemoryPool * pool,
+ void * keep,
+ unsigned int size)
+{
+ if (keep != NULL)
+ {
+ if (keep != pool->memory)
+ {
+ memmove(pool->memory,
+ keep,
+ size);
+ keep = pool->memory;
+ }
+ pool->pos = size;
+ }
+ pool->end = pool->size;
+ return keep;
+}
+
+
+
/* end of memorypool.c */
Modified: libmicrohttpd/src/daemon/memorypool.h
===================================================================
--- libmicrohttpd/src/daemon/memorypool.h 2007-12-20 00:40:49 UTC (rev
5979)
+++ libmicrohttpd/src/daemon/memorypool.h 2007-12-20 04:23:00 UTC (rev
5980)
@@ -81,4 +81,16 @@
void *old,
unsigned int old_size, unsigned int new_size);
+/**
+ * Clear all entries from the memory pool except
+ * for "keep" of the given "size".
+ *
+ * @param keep pointer to the entry to keep (maybe NULL)
+ * @param size how many bytes need to be kept at this address
+ * @return addr new address of "keep" (if it had to change)
+ */
+void *MHD_pool_reset(struct MemoryPool * pool,
+ void * keep,
+ unsigned int size);
+
#endif
Modified: libmicrohttpd/src/daemon/minimal_example.c
===================================================================
--- libmicrohttpd/src/daemon/minimal_example.c 2007-12-20 00:40:49 UTC (rev
5979)
+++ libmicrohttpd/src/daemon/minimal_example.c 2007-12-20 04:23:00 UTC (rev
5980)
@@ -41,14 +41,22 @@
const char *url,
const char *method,
const char *upload_data,
- const char *version, unsigned int *upload_data_size, void **unused)
+ const char *version, unsigned int *upload_data_size, void **ptr)
{
+ static int aptr;
const char *me = cls;
struct MHD_Response *response;
int ret;
if (0 != strcmp (method, "GET"))
return MHD_NO; /* unexpected method */
+ if (&aptr != *ptr)
+ {
+ /* do never respond on first call */
+ *ptr = &aptr;
+ return MHD_YES;
+ }
+ *ptr = NULL; /* reset when done */
response = MHD_create_response_from_data (strlen (me),
(void *) me, MHD_NO, MHD_NO);
ret = MHD_queue_response (connection, MHD_HTTP_OK, response);
Modified: libmicrohttpd/src/include/microhttpd.h
===================================================================
--- libmicrohttpd/src/include/microhttpd.h 2007-12-20 00:40:49 UTC (rev
5979)
+++ libmicrohttpd/src/include/microhttpd.h 2007-12-20 04:23:00 UTC (rev
5980)
@@ -84,7 +84,7 @@
/**
* Current version of the library.
*/
-#define MHD_VERSION 0x00000100
+#define MHD_VERSION 0x00000200
/**
* MHD-internal return codes.
@@ -373,6 +373,11 @@
*/
MHD_GET_ARGUMENT_KIND = 8,
+ /**
+ * HTTP footer (only for http 1.1 chunked encodings).
+ */
+ MHD_FOOTER_KIND = 16,
+
};
/**
@@ -544,9 +549,15 @@
* libmicrohttpd guarantees that "pos" will be
* the sum of all non-negative return values
* obtained from the content reader so far.
- * @return -1 on error (libmicrohttpd will no longer
- * try to read content and instead close the connection
- * with the client).
+ * @return -1 for the end of transmission (or on error);
+ * if a content transfer size was pre-set and the callback
+ * has provided fewer than that amount of data,
+ * MHD will close the connection with the client;
+ * if no content size was specified and this is an
+ * http 1.1 connection using chunked encoding, MHD will
+ * interpret "-1" as the normal end of the transfer
+ * (possibly allowing the client to perform additional
+ * requests using the same TCP connection).
*/
typedef int
(*MHD_ContentReaderCallback) (void *cls, size_t pos, char *buf, int max);
@@ -596,7 +607,7 @@
* in which case connections from any IP will be
* accepted
* @param apc_cls extra argument to apc
- * @param dh default handler for all URIs
+ * @param dh handler called for all requests (repeatedly)
* @param dh_cls extra argument to dh
* @param ... list of options (type-value pairs,
* terminated with MHD_OPTION_END).
@@ -655,33 +666,7 @@
*/
int MHD_run (struct MHD_Daemon *daemon);
-
/**
- * Register an access handler for all URIs beginning with uri_prefix.
- *
- * @param uri_prefix
- * @return MRI_NO if a handler for this exact prefix
- * already exists
- */
-int
-MHD_register_handler (struct MHD_Daemon *daemon,
- const char *uri_prefix,
- MHD_AccessHandlerCallback dh, void *dh_cls);
-
-/**
- * Unregister an access handler for the URIs beginning with
- * uri_prefix.
- *
- * @param uri_prefix
- * @return MHD_NO if a handler for this exact prefix
- * is not known for this daemon
- */
-int
-MHD_unregister_handler (struct MHD_Daemon *daemon,
- const char *uri_prefix,
- MHD_AccessHandlerCallback dh, void *dh_cls);
-
-/**
* Get all of the headers from the request.
*
* @param iterator callback to call on each header;
[Prev in Thread] |
Current Thread |
[Next in Thread] |
- [GNUnet-SVN] r5980 - in libmicrohttpd: . src/daemon src/include,
gnunet <=