summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorChristian Grothoff <christian@grothoff.org>2007-09-09 03:56:10 +0000
committerChristian Grothoff <christian@grothoff.org>2007-09-09 03:56:10 +0000
commit21acb929f578eb8f876c235669b78d6b213ec315 (patch)
tree45f89714c45e2a4f0999f2601fb8df7076f5b4fd
parent5e828bda99ed11ae9f183965eece0b5931ec0270 (diff)
incremental post processing API and implementation
-rw-r--r--ChangeLog8
-rw-r--r--README9
-rw-r--r--configure.ac4
-rw-r--r--src/daemon/Makefile.am3
-rw-r--r--src/daemon/connection.c299
-rw-r--r--src/daemon/daemon.c115
-rw-r--r--src/daemon/daemontest.c2
-rw-r--r--src/daemon/daemontest_get.c2
-rw-r--r--src/daemon/daemontest_large_put.c2
-rw-r--r--src/daemon/daemontest_long_header.c2
-rw-r--r--src/daemon/daemontest_post.c46
-rw-r--r--src/daemon/daemontest_put.c11
-rw-r--r--src/daemon/fileserver_example.c3
-rw-r--r--src/daemon/internal.c23
-rw-r--r--src/daemon/internal.h14
-rw-r--r--src/daemon/minimal_example.c3
-rw-r--r--src/include/microhttpd.h81
17 files changed, 306 insertions, 321 deletions
diff --git a/ChangeLog b/ChangeLog
index a7c7ec25..d95139e2 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,11 @@
+Sat Sep 8 21:54:04 MDT 2007
+ Extended API to allow for incremental POST
+ processing. The new API is binary-compatible
+ as long as the app does not handle POSTs, but
+ since that maybe the case, we're strictly speaking
+ breaking backwards compatibility (since url-encoded
+ POST data is no longer obtained the same way). - CG
+
Thu Aug 30 00:59:24 MDT 2007
Improving API to allow clients to associate state
with a connection and to be notified about request
diff --git a/README b/README
index a1216b26..1cf31703 100644
--- a/README
+++ b/README
@@ -18,17 +18,8 @@ connection.c:
For POST:
=========
-- find better way to handle POST data that does not fit into memory (#1221, API, TEST)
- add support to decode multipart/form-data (#1221, TEST)
-API Improvements:
-=================
-- allow clients to associate a void * with each connection (for easier client state
- management)
-- allow clients to register callback to be invoked when connection is
- timed out or otherwise completed / aborted / closed (for easier client
- state clean up)
-
For SSL:
========
microhttpd.h:
diff --git a/configure.ac b/configure.ac
index 44afee9e..1db29b83 100644
--- a/configure.ac
+++ b/configure.ac
@@ -21,8 +21,8 @@
#
#
AC_PREREQ(2.57)
-AC_INIT([libmicrohttpd], [0.0.3],[libmicrohttpd@gnunet.org])
-AM_INIT_AUTOMAKE([libmicrohttpd], [0.0.3])
+AC_INIT([libmicrohttpd], [0.1.0],[libmicrohttpd@gnunet.org])
+AM_INIT_AUTOMAKE([libmicrohttpd], [0.1.0])
AM_CONFIG_HEADER([config.h])
AH_TOP([#define _GNU_SOURCE 1])
diff --git a/src/daemon/Makefile.am b/src/daemon/Makefile.am
index e70583d3..3fa68c75 100644
--- a/src/daemon/Makefile.am
+++ b/src/daemon/Makefile.am
@@ -6,13 +6,14 @@ lib_LTLIBRARIES = \
libmicrohttpd.la
libmicrohttpd_la_LDFLAGS = \
- -export-dynamic -version-info 1:0:0
+ -export-dynamic -version-info 2:0:0
libmicrohttpd_la_SOURCES = \
connection.c connection.h \
daemon.c \
internal.c internal.h \
memorypool.c memorypool.h \
plibc.h \
+ postprocessor.c \
response.c response.h
# example programs
diff --git a/src/daemon/connection.c b/src/daemon/connection.c
index eef9e9e8..476d1c89 100644
--- a/src/daemon/connection.c
+++ b/src/daemon/connection.c
@@ -57,7 +57,7 @@
/**
* Add extra debug messages with reasons for closing connections
* (non-error reasons).
- */
+ */
#define DEBUG_CLOSE 0
@@ -184,17 +184,17 @@ MHD_need_100_continue (struct MHD_Connection *connection)
* A serious error occured, close the
* connection (and notify the application).
*/
-static void
-connection_close_error(struct MHD_Connection * connection)
+static void
+connection_close_error (struct MHD_Connection *connection)
{
SHUTDOWN (connection->socket_fd, SHUT_RDWR);
CLOSE (connection->socket_fd);
connection->socket_fd = -1;
- if (connection->daemon->notify_completed != NULL)
- connection->daemon->notify_completed(connection->daemon->notify_completed_cls,
- connection,
- &connection->client_context,
- MHD_REQUEST_TERMINATED_WITH_ERROR);
+ if (connection->daemon->notify_completed != NULL)
+ connection->daemon->notify_completed (connection->daemon->
+ notify_completed_cls, connection,
+ &connection->client_context,
+ MHD_REQUEST_TERMINATED_WITH_ERROR);
}
/**
@@ -222,11 +222,10 @@ ready_response (struct MHD_Connection *connection)
{
/* end of message, signal other side by closing! */
#if DEBUG_CLOSE
- MHD_DLOG (connection->daemon,
- "Closing connection (end of response)\n");
+ MHD_DLOG (connection->daemon, "Closing connection (end of response)\n");
#endif
response->total_size = connection->messagePos;
- connection_close_error(connection);
+ connection_close_error (connection);
return MHD_NO;
}
response->data_start = connection->messagePos;
@@ -281,7 +280,6 @@ MHD_connection_get_fdset (struct MHD_Connection *connection,
{
if ((connection->read_close == MHD_NO) &&
((connection->headersReceived == 1) &&
- (connection->post_processed == MHD_NO) &&
(connection->readLoc == connection->read_buffer_size)))
{
/* try growing the read buffer, just in case */
@@ -297,7 +295,7 @@ MHD_connection_get_fdset (struct MHD_Connection *connection,
connection->read_buffer_size =
connection->read_buffer_size * 2 + MHD_BUF_INC_SIZE;
FD_SET (fd, read_fd_set);
- if (fd > *max_fd)
+ if (fd > *max_fd)
*max_fd = fd;
}
}
@@ -430,29 +428,6 @@ MHD_connection_add_header (struct MHD_Connection *connection,
}
/**
- * Process escape sequences ('+'=space, %HH)
- */
-static void
-MHD_http_unescape (char *val)
-{
- char *esc;
- unsigned int num;
-
- while (NULL != (esc = strstr (val, "+")))
- *esc = ' ';
- while (NULL != (esc = strstr (val, "%")))
- {
- if ((1 == sscanf (&esc[1],
- "%2x", &num)) || (1 == sscanf (&esc[1], "%2X", &num)))
- {
- esc[0] = (unsigned char) num;
- memmove (&esc[1], &esc[3], strlen (&esc[3]));
- }
- val = esc + 1;
- }
-}
-
-/**
* @return MHD_NO on failure (out of memory), MHD_YES for success
*/
static int
@@ -615,10 +590,11 @@ MHD_parse_connection_headers (struct MHD_Connection *connection)
const char *clen;
const char *end;
unsigned long long cval;
- struct MHD_Response * response;
+ struct MHD_Response *response;
if (connection->bodyReceived == 1)
abort ();
+ colon = NULL; /* make gcc happy */
last = NULL;
while (NULL != (line = MHD_get_next_header_line (connection)))
{
@@ -707,29 +683,28 @@ MHD_parse_connection_headers (struct MHD_Connection *connection)
this request */
connection->read_close = MHD_YES;
}
-
- 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->bodyReceived = MHD_YES;
- connection->read_close = MHD_YES;
- MHD_DLOG (connection->daemon,
- "Received `%s' request without `%s' header.\n",
- MHD_HTTP_VERSION_1_1,
- MHD_HTTP_HEADER_HOST);
- 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);
- }
-
+
+ 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->bodyReceived = MHD_YES;
+ connection->read_close = MHD_YES;
+ MHD_DLOG (connection->daemon,
+ "Received `%s' request without `%s' header.\n",
+ MHD_HTTP_VERSION_1_1, MHD_HTTP_HEADER_HOST);
+ 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 */
@@ -761,8 +736,8 @@ MHD_parse_connection_headers (struct MHD_Connection *connection)
return;
DIE:
MHD_DLOG (connection->daemon,
- "Closing connection (problem parsing headers)\n");
- connection_close_error(connection);
+ "Closing connection (problem parsing headers)\n");
+ connection_close_error (connection);
}
@@ -784,107 +759,6 @@ MHD_find_access_handler (struct MHD_Connection *connection)
return &connection->daemon->default_handler;
}
-/**
- * Test if we are able to process the POST data.
- * This depends on available memory (enough to load
- * all of the POST data into the pool) and the
- * content encoding of the POST data. And of course,
- * this requires that the request is actually a
- * POST request.
- *
- * @return MHD_YES if so
- */
-static int
-MHD_test_post_data (struct MHD_Connection *connection)
-{
- const char *encoding;
- void *buf;
-
- if ((connection->method == NULL) ||
- (connection->response != NULL) ||
- (0 != strcasecmp (connection->method, MHD_HTTP_METHOD_POST)))
- return MHD_NO;
- encoding = MHD_lookup_connection_value (connection,
- MHD_HEADER_KIND,
- MHD_HTTP_HEADER_CONTENT_TYPE);
- if (encoding == NULL)
- return MHD_NO;
- if ((0 == strcasecmp (MHD_HTTP_POST_ENCODING_FORM_URLENCODED,
- encoding)) && (connection->uploadSize != -1))
- {
- buf = MHD_pool_reallocate (connection->pool,
- connection->read_buffer,
- (connection->read_buffer == NULL) ? 0 : connection->read_buffer_size + 1,
- connection->uploadSize + 1);
- if (buf == NULL)
- return MHD_NO;
- connection->read_buffer_size = connection->uploadSize;
- connection->read_buffer = buf;
- return MHD_YES;
- }
- return MHD_NO;
-}
-
-/**
- * Process the POST data here (adding to headers).
- *
- * Needs to first check POST encoding and then do
- * the right thing (TM). The POST data is in the
- * connection's post_data buffer between the postPos
- * and postLoc offsets. The POST message maybe
- * incomplete. The existing buffer (allocated from
- * the pool) can be used and modified but must then
- * be properly removed from the struct.
- *
- * @return MHD_YES on success, MHD_NO on error (i.e. out of
- * memory).
- */
-static int
-MHD_parse_post_data (struct MHD_Connection *connection)
-{
- const char *encoding;
- int ret;
-
- encoding = MHD_lookup_connection_value (connection,
- MHD_HEADER_KIND,
- MHD_HTTP_HEADER_CONTENT_TYPE);
- if (encoding == NULL)
- return MHD_NO;
- if (0 == strcasecmp (MHD_HTTP_POST_ENCODING_FORM_URLENCODED, encoding))
- {
- /* add 0-termination, that's why the actual buffer size
- is always 1 more than what is actually required for the data! */
- connection->read_buffer[connection->readLoc] = '\0';
- ret = parse_arguments (MHD_POSTDATA_KIND,
- connection, connection->read_buffer);
- /* invalidate read buffer for other uses --
- in particular, do not give it to the
- client; if this were to be needed, we would
- have to make a copy, which would double memory
- requirements */
- connection->read_buffer_size = 0;
- connection->readLoc = 0;
- connection->uploadSize = 0;
- connection->read_buffer = NULL;
- return ret;
- }
- if (0 == strcasecmp (MHD_HTTP_POST_ENCODING_MULTIPART_FORMDATA, encoding))
- {
- /* this code should never been reached right now,
- since the test_post_data function would already
- return MHD_NO; code is here only for future
- extensions... */
- /* see http://www.w3.org/TR/html4/interact/forms.html#h-17.13.4 */
- MHD_DLOG (connection->daemon,
- "Unsupported multipart encoding of POST data specified, not processing POST data.\n");
- return MHD_NO;
- }
- /* this should never be reached, just here for
- error checking */
- MHD_DLOG (connection->daemon,
- "Unknown encoding of POST data specified, not processing POST data.\n");
- return MHD_NO;
-}
/**
* Call the handler of the application for this
@@ -908,12 +782,12 @@ MHD_call_connection_handler (struct MHD_Connection *connection)
connection->method,
connection->version,
connection->read_buffer, &processed,
- &connection->client_context))
+ &connection->client_context))
{
/* serios internal error, close connection */
MHD_DLOG (connection->daemon,
"Internal application error, closing connection.\n");
- connection_close_error(connection);
+ connection_close_error (connection);
return;
}
/* dh left "processed" bytes in buffer for next time... */
@@ -929,10 +803,11 @@ MHD_call_connection_handler (struct MHD_Connection *connection)
{
connection->bodyReceived = 1;
if (connection->read_buffer != NULL)
- MHD_pool_reallocate(connection->pool,
- connection->read_buffer,
- (connection->read_buffer == NULL) ? 0 : connection->read_buffer_size + 1,
- 0);
+ MHD_pool_reallocate (connection->pool,
+ connection->read_buffer,
+ (connection->read_buffer ==
+ NULL) ? 0 : connection->read_buffer_size + 1,
+ 0);
connection->readLoc = 0;
connection->read_buffer_size = 0;
connection->read_buffer = NULL;
@@ -957,7 +832,7 @@ MHD_connection_handle_read (struct MHD_Connection *connection)
if (connection->pool == NULL)
{
MHD_DLOG (connection->daemon, "Failed to create memory pool!\n");
- connection_close_error(connection);
+ connection_close_error (connection);
return MHD_NO;
}
if ((connection->readLoc >= connection->read_buffer_size) &&
@@ -995,15 +870,14 @@ MHD_connection_handle_read (struct MHD_Connection *connection)
return MHD_NO;
MHD_DLOG (connection->daemon,
"Failed to receive data: %s\n", STRERROR (errno));
- connection_close_error(connection);
+ connection_close_error (connection);
return MHD_YES;
}
if (bytes_read == 0)
{
/* other side closed connection */
connection->read_close = MHD_YES;
- if ( (connection->headersReceived == 1) &&
- (connection->readLoc > 0) )
+ if ((connection->headersReceived == 1) && (connection->readLoc > 0))
MHD_call_connection_handler (connection);
#if DEBUG_CLOSE
MHD_DLOG (connection->daemon,
@@ -1014,24 +888,9 @@ MHD_connection_handle_read (struct MHD_Connection *connection)
}
connection->readLoc += bytes_read;
if (connection->headersReceived == 0)
- {
- MHD_parse_connection_headers (connection);
- if (connection->headersReceived == 1)
- {
- connection->post_processed = MHD_test_post_data (connection);
- }
- }
- if (connection->headersReceived == 1)
- {
- if ((connection->post_processed == MHD_YES) &&
- (connection->uploadSize == connection->readLoc))
- if (MHD_NO == MHD_parse_post_data (connection))
- connection->post_processed = MHD_NO;
- if (((connection->post_processed == MHD_NO) ||
- (connection->read_buffer_size == connection->readLoc)) &&
- (connection->method != NULL))
- MHD_call_connection_handler (connection);
- }
+ MHD_parse_connection_headers (connection);
+ if ((connection->headersReceived == 1) && (connection->method != NULL))
+ MHD_call_connection_handler (connection);
return MHD_YES;
}
@@ -1171,14 +1030,13 @@ MHD_connection_handle_write (struct MHD_Connection *connection)
return MHD_YES;
MHD_DLOG (connection->daemon,
"Failed to send data: %s\n", STRERROR (errno));
- connection_close_error(connection);
+ connection_close_error (connection);
return MHD_YES;
}
#if DEBUG_SEND_DATA
- fprintf(stderr,
- "Sent 100 continue response: `%.*s'\n",
- ret,
- &HTTP_100_CONTINUE[connection->continuePos]);
+ fprintf (stderr,
+ "Sent 100 continue response: `%.*s'\n",
+ ret, &HTTP_100_CONTINUE[connection->continuePos]);
#endif
connection->continuePos += ret;
return MHD_YES;
@@ -1195,9 +1053,9 @@ MHD_connection_handle_write (struct MHD_Connection *connection)
(MHD_NO == MHD_build_header_response (connection)))
{
/* oops - close! */
- MHD_DLOG (connection->daemon,
- "Closing connection (failed to create response header)\n");
- connection_close_error(connection);
+ MHD_DLOG (connection->daemon,
+ "Closing connection (failed to create response header)\n");
+ connection_close_error (connection);
return MHD_NO;
}
ret = SEND (connection->socket_fd,
@@ -1209,14 +1067,13 @@ MHD_connection_handle_write (struct MHD_Connection *connection)
return MHD_YES;
MHD_DLOG (connection->daemon,
"Failed to send data: %s\n", STRERROR (errno));
- connection_close_error(connection);
+ connection_close_error (connection);
return MHD_YES;
}
#if DEBUG_SEND_DATA
- fprintf(stderr,
- "Sent HEADER response: `%.*s'\n",
- ret,
- &connection->write_buffer[connection->writePos]);
+ fprintf (stderr,
+ "Sent HEADER response: `%.*s'\n",
+ ret, &connection->write_buffer[connection->writePos]);
#endif
connection->writePos += ret;
if (connection->writeLoc == connection->writePos)
@@ -1259,14 +1116,14 @@ MHD_connection_handle_write (struct MHD_Connection *connection)
return MHD_YES;
MHD_DLOG (connection->daemon,
"Failed to send data: %s\n", STRERROR (errno));
- connection_close_error(connection);
+ connection_close_error (connection);
return MHD_YES;
}
#if DEBUG_SEND_DATA
- fprintf(stderr,
- "Sent DATA response: `%.*s'\n",
- ret,
- &response->data[connection->messagePos - response->data_start]);
+ fprintf (stderr,
+ "Sent DATA response: `%.*s'\n",
+ ret,
+ &response->data[connection->messagePos - response->data_start]);
#endif
connection->messagePos += ret;
if (connection->messagePos > response->total_size)
@@ -1277,11 +1134,12 @@ MHD_connection_handle_write (struct MHD_Connection *connection)
(connection->headersReceived == 0))
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);
+ if (connection->daemon->notify_completed != NULL)
+ connection->daemon->notify_completed (connection->daemon->
+ notify_completed_cls,
+ connection,
+ &connection->client_context,
+ MHD_REQUEST_TERMINATED_COMPLETED_OK);
connection->client_context = NULL;
connection->continuePos = 0;
connection->responseCode = 0;
@@ -1297,14 +1155,15 @@ MHD_connection_handle_write (struct MHD_Connection *connection)
(0 != strcasecmp (MHD_HTTP_VERSION_1_1, connection->version)))
{
/* closed for reading => close for good! */
- if (connection->socket_fd != -1) {
+ if (connection->socket_fd != -1)
+ {
#if DEBUG_CLOSE
- MHD_DLOG (connection->daemon,
- "Closing connection (http 1.0 or end-of-stream for unknown content length)\n");
+ MHD_DLOG (connection->daemon,
+ "Closing connection (http 1.0 or end-of-stream for unknown content length)\n");
#endif
- SHUTDOWN (connection->socket_fd, SHUT_RDWR);
- CLOSE (connection->socket_fd);
- }
+ SHUTDOWN (connection->socket_fd, SHUT_RDWR);
+ CLOSE (connection->socket_fd);
+ }
connection->socket_fd = -1;
}
connection->version = NULL;
diff --git a/src/daemon/daemon.c b/src/daemon/daemon.c
index 3b8b1111..346abedf 100644
--- a/src/daemon/daemon.c
+++ b/src/daemon/daemon.c
@@ -225,8 +225,8 @@ MHD_handle_connection (void *data)
if (con->socket_fd != -1)
{
#if DEBUG_CLOSE
- MHD_DLOG (con->daemon,
- "Processing thread terminating, closing connection\n");
+ MHD_DLOG (con->daemon,
+ "Processing thread terminating, closing connection\n");
#endif
SHUTDOWN (con->socket_fd, SHUT_RDWR);
CLOSE (con->socket_fd);
@@ -259,10 +259,11 @@ MHD_accept_connection (struct MHD_Daemon *daemon)
if ((s < 0) || (addrlen <= 0))
{
MHD_DLOG (daemon, "Error accepting connection: %s\n", STRERROR (errno));
- if (s != -1) {
- SHUTDOWN (s, SHUT_RDWR);
- CLOSE (s); /* just in case */
- }
+ if (s != -1)
+ {
+ SHUTDOWN (s, SHUT_RDWR);
+ CLOSE (s); /* just in case */
+ }
return MHD_NO;
}
if (daemon->max_connections == 0)
@@ -278,8 +279,7 @@ MHD_accept_connection (struct MHD_Daemon *daemon)
(MHD_NO == daemon->apc (daemon->apc_cls, addr, addrlen)))
{
#if DEBUG_CLOSE
- MHD_DLOG (daemon,
- "Connection rejected, closing connection\n");
+ MHD_DLOG (daemon, "Connection rejected, closing connection\n");
#endif
SHUTDOWN (s, SHUT_RDWR);
CLOSE (s);
@@ -357,17 +357,16 @@ MHD_cleanup_connections (struct MHD_Daemon *daemon)
if ((pos->last_activity < timeout) && (pos->socket_fd != -1))
{
#if DEBUG_CLOSE
- MHD_DLOG (daemon,
- "Connection timed out, closing connection\n");
+ MHD_DLOG (daemon, "Connection timed out, closing connection\n");
#endif
- SHUTDOWN (pos->socket_fd, SHUT_RDWR);
+ 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->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)
{
@@ -472,19 +471,19 @@ MHD_select (struct MHD_Daemon *daemon, int may_block)
FD_ZERO (&ws);
FD_ZERO (&es);
max = 0;
-
+
if (0 == (daemon->options & MHD_USE_THREAD_PER_CONNECTION))
{
/* single-threaded, go over everything */
if (MHD_NO == MHD_get_fdset (daemon, &rs, &ws, &es, &max))
- return MHD_NO;
+ return MHD_NO;
}
else
{
/* accept only, have one thread per connection */
max = daemon->socket_fd;
if (max == -1)
- return MHD_NO;
+ return MHD_NO;
FD_SET (max, &rs);
}
if (may_block == MHD_NO)
@@ -496,21 +495,20 @@ MHD_select (struct MHD_Daemon *daemon, int may_block)
{
/* ltimeout is in ms */
if (MHD_YES == MHD_get_timeout (daemon, &ltimeout))
- {
- timeout.tv_usec = (ltimeout % 1000) * 1000;
- timeout.tv_sec = ltimeout / 1000;
- may_block = MHD_NO;
- }
+ {
+ timeout.tv_usec = (ltimeout % 1000) * 1000;
+ timeout.tv_sec = ltimeout / 1000;
+ may_block = MHD_NO;
+ }
}
num_ready = SELECT (max + 1,
- &rs, &ws, &es,
- may_block == MHD_NO ? &timeout : NULL);
+ &rs, &ws, &es, may_block == MHD_NO ? &timeout : NULL);
if (daemon->shutdown == MHD_YES)
return MHD_NO;
if (num_ready < 0)
{
if (errno == EINTR)
- return MHD_YES;
+ return MHD_YES;
MHD_DLOG (daemon, "Select failed: %s\n", STRERROR (errno));
return MHD_NO;
}
@@ -525,23 +523,23 @@ MHD_select (struct MHD_Daemon *daemon, int may_block)
now = time (NULL);
pos = daemon->connections;
while (pos != NULL)
- {
- ds = pos->socket_fd;
- 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);
- }
- }
- pos = pos->next;
- }
+ {
+ ds = pos->socket_fd;
+ 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);
+ }
+ }
+ pos = pos->next;
+ }
}
return MHD_YES;
}
@@ -694,10 +692,11 @@ MHD_start_daemon (unsigned int options,
case MHD_OPTION_CONNECTION_TIMEOUT:
retVal->connection_timeout = va_arg (ap, unsigned int);
break;
- case MHD_OPTION_NOTIFY_COMPLETED:
- retVal->notify_completed = va_arg(ap, MHD_RequestCompletedCallback);
- retVal->notify_completed_cls = va_arg(ap, void *);
- break;
+ case MHD_OPTION_NOTIFY_COMPLETED:
+ retVal->notify_completed =
+ va_arg (ap, MHD_RequestCompletedCallback);
+ retVal->notify_completed_cls = va_arg (ap, void *);
+ break;
default:
fprintf (stderr,
"Invalid MHD_OPTION argument! (Did you terminate the list with MHD_OPTION_END?)\n");
@@ -733,8 +732,7 @@ MHD_stop_daemon (struct MHD_Daemon *daemon)
fd = daemon->socket_fd;
daemon->socket_fd = -1;
#if DEBUG_CLOSE
- MHD_DLOG (daemon,
- "MHD shutdown, closing listen socket\n");
+ MHD_DLOG (daemon, "MHD shutdown, closing listen socket\n");
#endif
CLOSE (fd);
if ((0 != (daemon->options & MHD_USE_THREAD_PER_CONNECTION)) ||
@@ -748,16 +746,15 @@ MHD_stop_daemon (struct MHD_Daemon *daemon)
if (-1 != daemon->connections->socket_fd)
{
#if DEBUG_CLOSE
- MHD_DLOG (daemon,
- "MHD shutdown, closing active connections\n");
+ MHD_DLOG (daemon, "MHD shutdown, closing active connections\n");
#endif
- if (daemon->notify_completed != NULL)
- daemon->notify_completed(daemon->notify_completed_cls,
- daemon->connections,
- &daemon->connections->client_context,
- MHD_REQUEST_TERMINATED_DAEMON_SHUTDOWN);
- SHUTDOWN (daemon->connections->socket_fd, SHUT_RDWR);
- CLOSE (daemon->connections->socket_fd);
+ if (daemon->notify_completed != NULL)
+ daemon->notify_completed (daemon->notify_completed_cls,
+ daemon->connections,
+ &daemon->connections->client_context,
+ MHD_REQUEST_TERMINATED_DAEMON_SHUTDOWN);
+ SHUTDOWN (daemon->connections->socket_fd, SHUT_RDWR);
+ CLOSE (daemon->connections->socket_fd);
daemon->connections->socket_fd = -1;
}
MHD_cleanup_connections (daemon);
diff --git a/src/daemon/daemontest.c b/src/daemon/daemontest.c
index 2ecb87f2..8048b438 100644
--- a/src/daemon/daemontest.c
+++ b/src/daemon/daemontest.c
@@ -65,7 +65,7 @@ ahc_nothing (void *cls,
const char *method,
const char *version,
const char *upload_data, unsigned int *upload_data_size,
- void ** unused)
+ void **unused)
{
return MHD_NO;
}
diff --git a/src/daemon/daemontest_get.c b/src/daemon/daemontest_get.c
index 79726a11..3f0b36ba 100644
--- a/src/daemon/daemontest_get.c
+++ b/src/daemon/daemontest_get.c
@@ -64,7 +64,7 @@ ahc_echo (void *cls,
const char *method,
const char *version,
const char *upload_data, unsigned int *upload_data_size,
- void ** unused)
+ void **unused)
{
const char *me = cls;
struct MHD_Response *response;
diff --git a/src/daemon/daemontest_large_put.c b/src/daemon/daemontest_large_put.c
index f4ae5583..2a9ad4fa 100644
--- a/src/daemon/daemontest_large_put.c
+++ b/src/daemon/daemontest_large_put.c
@@ -86,7 +86,7 @@ ahc_echo (void *cls,
const char *method,
const char *version,
const char *upload_data, unsigned int *upload_data_size,
- void ** unused)
+ void **unused)
{
int *done = cls;
struct MHD_Response *response;
diff --git a/src/daemon/daemontest_long_header.c b/src/daemon/daemontest_long_header.c
index 6a451136..22368e9c 100644
--- a/src/daemon/daemontest_long_header.c
+++ b/src/daemon/daemontest_long_header.c
@@ -70,7 +70,7 @@ ahc_echo (void *cls,
const char *method,
const char *version,
const char *upload_data, unsigned int *upload_data_size,
- void ** unused)
+ void **unused)
{
const char *me = cls;
struct MHD_Response *response;
diff --git a/src/daemon/daemontest_post.c b/src/daemon/daemontest_post.c
index 10f6ea97..00bec910 100644
--- a/src/daemon/daemontest_post.c
+++ b/src/daemon/daemontest_post.c
@@ -63,6 +63,27 @@ copyBuffer (void *ptr, size_t size, size_t nmemb, void *ctx)
return size * nmemb;
}
+/**
+ * Note that this post_iterator is not perfect
+ * in that it fails to support incremental processing.
+ * (to be fixed in the future)
+ */
+static int
+post_iterator (void *cls,
+ enum MHD_ValueKind kind,
+ const char *key, const char *value, size_t off, size_t size)
+{
+ int *eok = cls;
+
+ if ((0 == strcmp (key, "name")) &&
+ (size == strlen ("daniel")) && (0 == strncmp (value, "daniel", size)))
+ (*eok) |= 1;
+ if ((0 == strcmp (key, "project")) &&
+ (size == strlen ("curl")) && (0 == strncmp (value, "curl", size)))
+ (*eok) |= 2;
+ return MHD_YES;
+}
+
static int
ahc_echo (void *cls,
struct MHD_Connection *connection,
@@ -70,31 +91,38 @@ ahc_echo (void *cls,
const char *method,
const char *version,
const char *upload_data, unsigned int *upload_data_size,
- void ** unused)
+ void **unused)
{
+ static int eok;
struct MHD_Response *response;
+ struct MHD_PostProcessor *pp;
int ret;
- const char *r1;
- const char *r2;
if (0 != strcmp ("POST", method))
{
printf ("METHOD: %s\n", method);
return MHD_NO; /* unexpected method */
}
- r1 = MHD_lookup_connection_value (connection, MHD_POSTDATA_KIND, "name");
- r2 = MHD_lookup_connection_value (connection, MHD_POSTDATA_KIND, "project");
- if ((r1 != NULL) &&
- (r2 != NULL) &&
- (0 == strcmp ("daniel", r1)) && (0 == strcmp ("curl", r2)))
+ pp = *unused;
+ if (pp == NULL)
+ {
+ eok = 0;
+ pp = MHD_create_post_processor (connection, 1024, &post_iterator, &eok);
+ *unused = pp;
+ }
+ MHD_post_process (pp, upload_data, *upload_data_size);
+ if ((eok == 3) && (0 == *upload_data_size))
{
response = MHD_create_response_from_data (strlen (url),
(void *) url,
MHD_NO, MHD_YES);
ret = MHD_queue_response (connection, MHD_HTTP_OK, response);
MHD_destroy_response (response);
- return MHD_YES; /* done */
+ MHD_destroy_post_processor (pp);
+ *unused = NULL;
+ return ret;
}
+ *upload_data_size = 0;
return MHD_YES;
}
diff --git a/src/daemon/daemontest_put.c b/src/daemon/daemontest_put.c
index ad761025..2c3acfdb 100644
--- a/src/daemon/daemontest_put.c
+++ b/src/daemon/daemontest_put.c
@@ -77,7 +77,7 @@ ahc_echo (void *cls,
const char *method,
const char *version,
const char *upload_data, unsigned int *upload_data_size,
- void ** unused)
+ void **unused)
{
int *done = cls;
struct MHD_Response *response;
@@ -363,10 +363,11 @@ main (int argc, char *const *argv)
if (0 != curl_global_init (CURL_GLOBAL_WIN32))
return 2;
errorCount += testInternalPut ();
- if (0) {
- errorCount += testMultithreadedPut ();
- errorCount += testExternalPut ();
- }
+ if (0)
+ {
+ errorCount += testMultithreadedPut ();
+ errorCount += testExternalPut ();
+ }
if (errorCount != 0)
fprintf (stderr, "Error (code: %u)\n", errorCount);
curl_global_cleanup ();
diff --git a/src/daemon/fileserver_example.c b/src/daemon/fileserver_example.c
index 15b6aa24..c5bf762b 100644
--- a/src/daemon/fileserver_example.c
+++ b/src/daemon/fileserver_example.c
@@ -52,8 +52,7 @@ ahc_echo (void *cls,
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 **unused)
{
struct MHD_Response *response;
int ret;
diff --git a/src/daemon/internal.c b/src/daemon/internal.c
index 0407cfa9..85c17b16 100644
--- a/src/daemon/internal.c
+++ b/src/daemon/internal.c
@@ -42,3 +42,26 @@ MHD_DLOG (const struct MHD_Daemon *daemon, const char *format, ...)
VFPRINTF (stderr, format, va);
va_end (va);
}
+
+/**
+ * Process escape sequences ('+'=space, %HH)
+ */
+void
+MHD_http_unescape (char *val)
+{
+ char *esc;
+ unsigned int num;
+
+ while (NULL != (esc = strstr (val, "+")))
+ *esc = ' ';
+ while (NULL != (esc = strstr (val, "%")))
+ {
+ if ((1 == sscanf (&esc[1],
+ "%2x", &num)) || (1 == sscanf (&esc[1], "%2X", &num)))
+ {
+ esc[0] = (unsigned char) num;
+ memmove (&esc[1], &esc[3], strlen (&esc[3]));
+ }
+ val = esc + 1;
+ }
+}
diff --git a/src/daemon/internal.h b/src/daemon/internal.h
index 125755d2..3f3f91b0 100644
--- a/src/daemon/internal.h
+++ b/src/daemon/internal.h
@@ -65,6 +65,11 @@
*/
void MHD_DLOG (const struct MHD_Daemon *daemon, const char *format, ...);
+/**
+ * Process escape sequences ('+'=space, %HH).
+ * Updates val in place.
+ */
+void MHD_http_unescape (char *val);
/**
* Header or cookie in HTTP request or response.
@@ -208,7 +213,7 @@ struct MHD_Connection
* store it. (MHD does not know or care what it
* is).
*/
- void * client_context;
+ void *client_context;
/**
* Request method. Should be GET/POST/etc. Allocated
@@ -350,11 +355,6 @@ struct MHD_Connection
int headersSent;
/**
- * Are we processing the POST data?
- */
- int post_processed;
-
- /**
* HTTP response code. Only valid if response object
* is already set.
*/
@@ -391,7 +391,7 @@ struct MHD_Daemon
MHD_RequestCompletedCallback notify_completed;
- void * notify_completed_cls;
+ void *notify_completed_cls;
/**
* PID of the select thread (if we have internal select)
diff --git a/src/daemon/minimal_example.c b/src/daemon/minimal_example.c
index 80442072..62ee5ae5 100644
--- a/src/daemon/minimal_example.c
+++ b/src/daemon/minimal_example.c
@@ -41,8 +41,7 @@ ahc_echo (void *cls,
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 **unused)
{
const char *me = cls;
struct MHD_Response *response;
diff --git a/src/include/microhttpd.h b/src/include/microhttpd.h
index 3a46c085..c77ea6b9 100644
--- a/src/include/microhttpd.h
+++ b/src/include/microhttpd.h
@@ -84,7 +84,7 @@ extern "C"
/**
* Current version of the library.
*/
-#define MHD_VERSION 0x00000004
+#define MHD_VERSION 0x00000005
/**
* MHD-internal return codes.
@@ -428,6 +428,11 @@ struct MHD_Connection;
struct MHD_Response;
/**
+ * Handle for POST processing.
+ */
+struct MHD_PostProcessor;
+
+/**
* Allow or deny a client to connect.
*
*
@@ -555,6 +560,29 @@ typedef int
typedef void (*MHD_ContentReaderFreeCallback) (void *cls);
/**
+ * Iterator over key-value pairs where the value
+ * maybe made available in increments and/or may
+ * not be zero-terminated.
+ *
+ * @param cls user-specified closure
+ * @param kind type of the value
+ * @param 0-terminated key for the value
+ * @param value pointer to size bytes of data at the
+ * specified offset
+ * @param off offset of value in the overall data
+ * @param size number of bytes in value available
+ * @return MHD_YES to continue iterating,
+ * MHD_NO to abort the iteration
+ */
+typedef int
+ (*MHD_IncrementalKeyValueIterator) (void *cls,
+ enum MHD_ValueKind kind,
+ const char *key,
+ const char *value,
+ size_t off,
+ size_t size);
+
+/**
* Start a webserver on the given port.
* @param flags combination of MHD_FLAG values
* @param port port to bind to
@@ -776,6 +804,57 @@ const char *MHD_get_response_header (struct MHD_Response *response,
const char *key);
+/**
+ * Create a PostProcessor.
+ *
+ * A PostProcessor can be used to (incrementally)
+ * parse the data portion of a POST request.
+ *
+ * @param connection the connection on which the POST is
+ * happening (used to determine the POST format)
+ * @param buffer_size maximum number of bytes to use for
+ * internal buffering (used only for the parsing,
+ * specifically the parsing of the keys). A
+ * tiny value (256-1024) should be sufficient.
+ * Do NOT use 0.
+ * @param ikvi iterator to be called with the parsed data,
+ * Must NOT be NULL.
+ * @param cls first argument to ikvi
+ * @return NULL on error (out of memory, unsupported encoding),
+ otherwise a PP handle
+ */
+struct MHD_PostProcessor *
+MHD_create_post_processor(struct MHD_Connection * connection,
+ unsigned int buffer_size,
+ MHD_IncrementalKeyValueIterator ikvi,
+ void * cls);
+
+/**
+ * Parse and process POST data.
+ * Call this function when POST data is available
+ * (usually during an MHD_AccessHandlerCallback)
+ * with the upload_data and upload_data_size.
+ * Whenever possible, this will then cause calls
+ * to the MHD_IncrementalKeyValueIterator.
+ *
+ * @param pp the post processor
+ * @param post_data post_data_len bytes of POST data
+ * @param post_data_len length of post_data
+ * @return MHD_YES on success, MHD_NO on error
+ * (out-of-memory, iterator aborted, parse error)
+ */
+int
+MHD_post_process(struct MHD_PostProcessor * pp,
+ const char * post_data,
+ unsigned int post_data_len);
+
+/**
+ * Release PostProcessor resources.
+ */
+void
+MHD_destroy_post_processor(struct MHD_PostProcessor * pp);
+
+
#if 0 /* keep Emacsens' auto-indent happy */
{
#endif