[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[GNUnet-SVN] r27025 - in libmicrohttpd: . src src/microhttpd
From: |
gnunet |
Subject: |
[GNUnet-SVN] r27025 - in libmicrohttpd: . src src/microhttpd |
Date: |
Sun, 5 May 2013 20:07:33 +0200 |
Author: grothoff
Date: 2013-05-05 20:07:33 +0200 (Sun, 05 May 2013)
New Revision: 27025
Added:
libmicrohttpd/src/microhttpd/
libmicrohttpd/src/microhttpd/basicauth.c
libmicrohttpd/src/microhttpd/connection.c
libmicrohttpd/src/microhttpd/daemon.c
libmicrohttpd/src/microhttpd/digestauth.c
libmicrohttpd/src/microhttpd/internal.c
libmicrohttpd/src/microhttpd/internal.h
libmicrohttpd/src/microhttpd/memorypool.c
libmicrohttpd/src/microhttpd/memorypool.h
libmicrohttpd/src/microhttpd/postprocessor.c
libmicrohttpd/src/microhttpd/response.c
libmicrohttpd/src/microhttpd/test_daemon.c
libmicrohttpd/src/microhttpd/test_postprocessor.c
libmicrohttpd/src/microhttpd/test_postprocessor_large.c
Removed:
libmicrohttpd/src/daemon/
libmicrohttpd/src/microhttpd/basicauth.c
libmicrohttpd/src/microhttpd/connection.c
libmicrohttpd/src/microhttpd/daemon.c
libmicrohttpd/src/microhttpd/digestauth.c
libmicrohttpd/src/microhttpd/internal.c
libmicrohttpd/src/microhttpd/internal.h
libmicrohttpd/src/microhttpd/memorypool.c
libmicrohttpd/src/microhttpd/memorypool.h
libmicrohttpd/src/microhttpd/postprocessor.c
libmicrohttpd/src/microhttpd/response.c
libmicrohttpd/src/microhttpd/test_daemon.c
libmicrohttpd/src/microhttpd/test_postprocessor.c
libmicrohttpd/src/microhttpd/test_postprocessor_large.c
Modified:
libmicrohttpd/configure.ac
libmicrohttpd/src/Makefile.am
Log:
-changing directory name
Modified: libmicrohttpd/configure.ac
===================================================================
--- libmicrohttpd/configure.ac 2013-05-05 18:01:09 UTC (rev 27024)
+++ libmicrohttpd/configure.ac 2013-05-05 18:07:33 UTC (rev 27025)
@@ -449,7 +449,7 @@
src/Makefile
src/include/Makefile
src/include/plibc/Makefile
-src/daemon/Makefile
+src/microhttpd/Makefile
src/examples/Makefile
src/testcurl/Makefile
src/testcurl/https/Makefile
Modified: libmicrohttpd/src/Makefile.am
===================================================================
--- libmicrohttpd/src/Makefile.am 2013-05-05 18:01:09 UTC (rev 27024)
+++ libmicrohttpd/src/Makefile.am 2013-05-05 18:07:33 UTC (rev 27025)
@@ -6,4 +6,4 @@
endif
endif
endif
-SUBDIRS = include daemon examples $(curltests) $(zzuftests) .
+SUBDIRS = include microhttpd examples $(curltests) $(zzuftests) .
Deleted: libmicrohttpd/src/microhttpd/basicauth.c
===================================================================
--- libmicrohttpd/src/daemon/basicauth.c 2013-05-05 12:01:06 UTC (rev
27023)
+++ libmicrohttpd/src/microhttpd/basicauth.c 2013-05-05 18:07:33 UTC (rev
27025)
@@ -1,132 +0,0 @@
-/*
- This file is part of libmicrohttpd
- (C) 2010, 2011, 2012 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
- version 2.1 of the License, or (at your option) any later version.
-
- This library is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- Lesser General Public License for more details.
-
- You should have received a copy of the GNU Lesser General Public
- License along with this library; if not, write to the Free Software
- Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
USA
-*/
-/**
- * @file basicauth.c
- * @brief Implements HTTP basic authentication methods
- * @author Amr Ali
- * @author Matthieu Speder
- */
-#include "platform.h"
-#include <limits.h>
-#include "internal.h"
-#include "base64.h"
-
-/**
- * Beginning string for any valid Basic authentication header.
- */
-#define _BASIC_BASE "Basic "
-
-
-/**
- * Get the username and password from the basic authorization header sent by
the client
- *
- * @param connection The MHD connection structure
- * @param password a pointer for the password
- * @return NULL if no username could be found, a pointer
- * to the username if found
- */
-char *
-MHD_basic_auth_get_username_password(struct MHD_Connection *connection,
- char** password)
-{
- const char *header;
- char *decode;
- const char *separator;
- char *user;
-
- if ( (NULL == (header = MHD_lookup_connection_value (connection,
- MHD_HEADER_KIND,
-
MHD_HTTP_HEADER_AUTHORIZATION))) ||
- (0 != strncmp (header, _BASIC_BASE, strlen(_BASIC_BASE))) )
- return NULL;
- header += strlen (_BASIC_BASE);
- if (NULL == (decode = BASE64Decode (header)))
- {
-#if HAVE_MESSAGES
- MHD_DLOG (connection->daemon,
- "Error decoding basic authentication\n");
-#endif
- return NULL;
- }
- /* Find user:password pattern */
- if (NULL == (separator = strchr (decode, ':')))
- {
-#if HAVE_MESSAGES
- MHD_DLOG(connection->daemon,
- "Basic authentication doesn't contain ':' separator\n");
-#endif
- free (decode);
- return NULL;
- }
- if (NULL == (user = strdup (decode)))
- {
- free (decode);
- return NULL;
- }
- user[separator - decode] = '\0'; /* cut off at ':' */
- if (NULL != password)
- {
- *password = strdup (separator + 1);
- if (NULL == *password)
- {
-#if HAVE_MESSAGES
- MHD_DLOG(connection->daemon,
- "Failed to allocate memory for password\n");
-#endif
- free (decode);
- free (user);
- return NULL;
- }
- }
- free (decode);
- return user;
-}
-
-
-/**
- * Queues a response to request basic authentication from the client
- *
- * @param connection The MHD connection structure
- * @param realm the realm presented to the client
- * @return MHD_YES on success, MHD_NO otherwise
- */
-int
-MHD_queue_basic_auth_fail_response (struct MHD_Connection *connection,
- const char *realm,
- struct MHD_Response *response)
-{
- int ret;
- size_t hlen = strlen(realm) + strlen("Basic realm=\"\"") + 1;
- char header[hlen];
-
- snprintf (header,
- sizeof (header),
- "Basic realm=\"%s\"",
- realm);
- ret = MHD_add_response_header (response,
- MHD_HTTP_HEADER_WWW_AUTHENTICATE,
- header);
- if (MHD_YES == ret)
- ret = MHD_queue_response (connection,
- MHD_HTTP_UNAUTHORIZED,
- response);
- return ret;
-}
-
-/* end of basicauth.c */
Copied: libmicrohttpd/src/microhttpd/basicauth.c (from rev 27024,
libmicrohttpd/src/daemon/basicauth.c)
===================================================================
--- libmicrohttpd/src/microhttpd/basicauth.c (rev 0)
+++ libmicrohttpd/src/microhttpd/basicauth.c 2013-05-05 18:07:33 UTC (rev
27025)
@@ -0,0 +1,136 @@
+/*
+ This file is part of libmicrohttpd
+ (C) 2010, 2011, 2012 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
+ version 2.1 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
USA
+*/
+/**
+ * @file basicauth.c
+ * @brief Implements HTTP basic authentication methods
+ * @author Amr Ali
+ * @author Matthieu Speder
+ */
+#include "platform.h"
+#include <limits.h>
+#include "internal.h"
+#include "base64.h"
+
+/**
+ * Beginning string for any valid Basic authentication header.
+ */
+#define _BASIC_BASE "Basic "
+
+
+/**
+ * Get the username and password from the basic authorization header sent by
the client
+ *
+ * @param connection The MHD connection structure
+ * @param password a pointer for the password
+ * @return NULL if no username could be found, a pointer
+ * to the username if found
+ */
+char *
+MHD_basic_auth_get_username_password(struct MHD_Connection *connection,
+ char** password)
+{
+ const char *header;
+ char *decode;
+ const char *separator;
+ char *user;
+
+ if ( (NULL == (header = MHD_lookup_connection_value (connection,
+ MHD_HEADER_KIND,
+
MHD_HTTP_HEADER_AUTHORIZATION))) ||
+ (0 != strncmp (header, _BASIC_BASE, strlen(_BASIC_BASE))) )
+ return NULL;
+ header += strlen (_BASIC_BASE);
+ if (NULL == (decode = BASE64Decode (header)))
+ {
+#if HAVE_MESSAGES
+ MHD_DLOG (connection->daemon,
+ "Error decoding basic authentication\n");
+#endif
+ return NULL;
+ }
+ /* Find user:password pattern */
+ if (NULL == (separator = strchr (decode, ':')))
+ {
+#if HAVE_MESSAGES
+ MHD_DLOG(connection->daemon,
+ "Basic authentication doesn't contain ':' separator\n");
+#endif
+ free (decode);
+ return NULL;
+ }
+ if (NULL == (user = strdup (decode)))
+ {
+ free (decode);
+ return NULL;
+ }
+ user[separator - decode] = '\0'; /* cut off at ':' */
+ if (NULL != password)
+ {
+ *password = strdup (separator + 1);
+ if (NULL == *password)
+ {
+#if HAVE_MESSAGES
+ MHD_DLOG(connection->daemon,
+ "Failed to allocate memory for password\n");
+#endif
+ free (decode);
+ free (user);
+ return NULL;
+ }
+ }
+ free (decode);
+ return user;
+}
+
+
+/**
+ * Queues a response to request basic authentication from the client.
+ * The given response object is expected to include the payload for
+ * the response; the "WWW-Authenticate" header will be added and the
+ * response queued with the 'UNAUTHORIZED' status code.
+ *
+ * @param connection The MHD connection structure
+ * @param realm the realm presented to the client
+ * @param response response object to modify and queue
+ * @return MHD_YES on success, MHD_NO otherwise
+ */
+int
+MHD_queue_basic_auth_fail_response (struct MHD_Connection *connection,
+ const char *realm,
+ struct MHD_Response *response)
+{
+ int ret;
+ size_t hlen = strlen(realm) + strlen("Basic realm=\"\"") + 1;
+ char header[hlen];
+
+ snprintf (header,
+ sizeof (header),
+ "Basic realm=\"%s\"",
+ realm);
+ ret = MHD_add_response_header (response,
+ MHD_HTTP_HEADER_WWW_AUTHENTICATE,
+ header);
+ if (MHD_YES == ret)
+ ret = MHD_queue_response (connection,
+ MHD_HTTP_UNAUTHORIZED,
+ response);
+ return ret;
+}
+
+/* end of basicauth.c */
Deleted: libmicrohttpd/src/microhttpd/connection.c
===================================================================
--- libmicrohttpd/src/daemon/connection.c 2013-05-05 12:01:06 UTC (rev
27023)
+++ libmicrohttpd/src/microhttpd/connection.c 2013-05-05 18:07:33 UTC (rev
27025)
@@ -1,2578 +0,0 @@
-/*
- This file is part of libmicrohttpd
- (C) 2007, 2008, 2009, 2010, 2011, 2012 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
- version 2.1 of the License, or (at your option) any later version.
-
- This library is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- Lesser General Public License for more details.
-
- You should have received a copy of the GNU Lesser General Public
- License along with this library; if not, write to the Free Software
- Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
USA
-
-*/
-
-/**
- * @file connection.c
- * @brief Methods for managing connections
- * @author Daniel Pittman
- * @author Christian Grothoff
- */
-
-#include "internal.h"
-#include <limits.h>
-#include "connection.h"
-#include "memorypool.h"
-#include "response.h"
-#include "reason_phrase.h"
-
-#if HAVE_NETINET_TCP_H
-/* for TCP_CORK */
-#include <netinet/tcp.h>
-#endif
-
-/**
- * Minimum size by which MHD tries to increment read/write buffers.
- * We usually begin with half the available pool space for the
- * IO-buffer, but if absolutely needed we additively grow by the
- * number of bytes given here (up to -- theoretically -- the full pool
- * space).
- */
-#define MHD_BUF_INC_SIZE 1024
-
-/**
- * Message to transmit when http 1.1 request is received
- */
-#define HTTP_100_CONTINUE "HTTP/1.1 100 Continue\r\n\r\n"
-
-/**
- * Response text used when the request (http header) is too big to
- * be processed.
- *
- * Intentionally empty here to keep our memory footprint
- * minimal.
- */
-#if HAVE_MESSAGES
-#define REQUEST_TOO_BIG "<html><head><title>Request too
big</title></head><body>Your HTTP header was too big for the memory constraints
of this webserver.</body></html>"
-#else
-#define REQUEST_TOO_BIG ""
-#endif
-
-/**
- * Response text used when the request (http header) does not
- * contain a "Host:" header and still claims to be HTTP 1.1.
- *
- * Intentionally empty here to keep our memory footprint
- * minimal.
- */
-#if HAVE_MESSAGES
-#define REQUEST_LACKS_HOST "<html><head><title>"Host:" header
required</title></head><body>In HTTP 1.1, requests must include a
"Host:" header, and your HTTP 1.1 request lacked such a
header.</body></html>"
-#else
-#define REQUEST_LACKS_HOST ""
-#endif
-
-/**
- * Response text used when the request (http header) is
- * malformed.
- *
- * Intentionally empty here to keep our memory footprint
- * minimal.
- */
-#if HAVE_MESSAGES
-#define REQUEST_MALFORMED "<html><head><title>Request
malformed</title></head><body>Your HTTP request was syntactically
incorrect.</body></html>"
-#else
-#define REQUEST_MALFORMED ""
-#endif
-
-/**
- * Response text used when there is an internal server error.
- *
- * Intentionally empty here to keep our memory footprint
- * minimal.
- */
-#if HAVE_MESSAGES
-#define INTERNAL_ERROR "<html><head><title>Internal server
error</title></head><body>Some programmer needs to study the manual more
carefully.</body></html>"
-#else
-#define INTERNAL_ERROR ""
-#endif
-
-/**
- * Add extra debug messages with reasons for closing connections
- * (non-error reasons).
- */
-#define DEBUG_CLOSE MHD_NO
-
-/**
- * Should all data send be printed to stderr?
- */
-#define DEBUG_SEND_DATA MHD_NO
-
-
-/**
- * Get all of the headers from the request.
- *
- * @param connection connection to get values from
- * @param kind types of values to iterate over
- * @param iterator callback to call on each header;
- * maybe NULL (then just count headers)
- * @param iterator_cls extra argument to iterator
- * @return number of entries iterated over
- */
-int
-MHD_get_connection_values (struct MHD_Connection *connection,
- enum MHD_ValueKind kind,
- MHD_KeyValueIterator iterator, void *iterator_cls)
-{
- int ret;
- struct MHD_HTTP_Header *pos;
-
- if (NULL == connection)
- return -1;
- ret = 0;
- for (pos = connection->headers_received; NULL != pos; pos = pos->next)
- if (0 != (pos->kind & kind))
- {
- ret++;
- if ((NULL != iterator) &&
- (MHD_YES != iterator (iterator_cls,
- kind, pos->header, pos->value)))
- return ret;
- }
- return ret;
-}
-
-
-/**
- * This function can be used to append an entry to
- * the list of HTTP headers of a connection (so that the
- * MHD_get_connection_values function will return
- * them -- and the MHD PostProcessor will also
- * see them). This maybe required in certain
- * situations (see Mantis #1399) where (broken)
- * HTTP implementations fail to supply values needed
- * by the post processor (or other parts of the
- * application).
- * <p>
- * This function MUST only be called from within
- * the MHD_AccessHandlerCallback (otherwise, access
- * maybe improperly synchronized). Furthermore,
- * the client must guarantee that the key and
- * value arguments are 0-terminated strings that
- * are NOT freed until the connection is closed.
- * (The easiest way to do this is by passing only
- * arguments to permanently allocated strings.).
- *
- * @param connection the connection for which a
- * value should be set
- * @param kind kind of the value
- * @param key key for the value
- * @param value the value itself
- * @return MHD_NO if the operation could not be
- * performed due to insufficient memory;
- * MHD_YES on success
- */
-int
-MHD_set_connection_value (struct MHD_Connection *connection,
- enum MHD_ValueKind kind,
- const char *key, const char *value)
-{
- struct MHD_HTTP_Header *pos;
-
- pos = MHD_pool_allocate (connection->pool,
- sizeof (struct MHD_HTTP_Header), MHD_NO);
- if (NULL == pos)
- return MHD_NO;
- pos->header = (char *) key;
- pos->value = (char *) value;
- pos->kind = kind;
- pos->next = NULL;
- /* append 'pos' to the linked list of headers */
- if (NULL == connection->headers_received_tail)
- {
- connection->headers_received = pos;
- connection->headers_received_tail = pos;
- }
- else
- {
- connection->headers_received_tail->next = pos;
- connection->headers_received_tail = pos;
- }
- return MHD_YES;
-}
-
-
-/**
- * Get a particular header value. If multiple
- * values match the kind, return any one of them.
- *
- * @param connection connection to get values from
- * @param kind what kind of value are we looking for
- * @param key the header to look for, NULL to lookup 'trailing' value without
a key
- * @return NULL if no such item was found
- */
-const char *
-MHD_lookup_connection_value (struct MHD_Connection *connection,
- enum MHD_ValueKind kind, const char *key)
-{
- struct MHD_HTTP_Header *pos;
-
- if (NULL == connection)
- return NULL;
- for (pos = connection->headers_received; NULL != pos; pos = pos->next)
- if ((0 != (pos->kind & kind)) &&
- ( (key == pos->header) ||
- ( (NULL != pos->header) &&
- (NULL != key) &&
- (0 == strcasecmp (key, pos->header))) ))
- return pos->value;
- return NULL;
-}
-
-
-/**
- * Queue a response to be transmitted to the client (as soon as
- * possible but after MHD_AccessHandlerCallback returns).
- *
- * @param connection the connection identifying the client
- * @param status_code HTTP status code (i.e. 200 for OK)
- * @param response response to transmit
- * @return MHD_NO on error (i.e. reply already sent),
- * MHD_YES on success or if message has been queued
- */
-int
-MHD_queue_response (struct MHD_Connection *connection,
- unsigned int status_code, struct MHD_Response *response)
-{
- if ( (NULL == connection) ||
- (NULL == response) ||
- (NULL != connection->response) ||
- ( (MHD_CONNECTION_HEADERS_PROCESSED != connection->state) &&
- (MHD_CONNECTION_FOOTERS_RECEIVED != connection->state) ) )
- return MHD_NO;
- MHD_increment_response_rc (response);
- connection->response = response;
- connection->responseCode = status_code;
- if ( (NULL != connection->method) &&
- (0 == strcasecmp (connection->method, MHD_HTTP_METHOD_HEAD)) )
- {
- /* if this is a "HEAD" request, pretend that we
- have already sent the full message body */
- connection->response_write_position = response->total_size;
- }
- if (MHD_CONNECTION_HEADERS_PROCESSED == connection->state)
- {
- /* response was queued "early",
- refuse to read body / footers or further
- requests! */
- (void) SHUTDOWN (connection->socket_fd, SHUT_RD);
- connection->read_closed = MHD_YES;
- connection->state = MHD_CONNECTION_FOOTERS_RECEIVED;
- }
- return MHD_YES;
-}
-
-
-/**
- * Do we (still) need to send a 100 continue
- * message for this connection?
- *
- * @param connection connection to test
- * @return 0 if we don't need 100 CONTINUE, 1 if we do
- */
-static int
-need_100_continue (struct MHD_Connection *connection)
-{
- const char *expect;
-
- return ( (NULL == connection->response) &&
- (NULL != connection->version) &&
- (0 == strcasecmp (connection->version,
- MHD_HTTP_VERSION_1_1)) &&
- (NULL != (expect = MHD_lookup_connection_value (connection,
- MHD_HEADER_KIND,
-
MHD_HTTP_HEADER_EXPECT))) &&
- (0 == strcasecmp (expect, "100-continue")) &&
- (connection->continue_message_write_offset <
- strlen (HTTP_100_CONTINUE)) );
-}
-
-
-/**
- * Close the given connection and give the
- * specified termination code to the user.
- *
- * @param connection connection to close
- * @param termination_code termination reason to give
- */
-void
-MHD_connection_close (struct MHD_Connection *connection,
- enum MHD_RequestTerminationCode termination_code)
-{
- struct MHD_Daemon *daemon;
-
- daemon = connection->daemon;
- SHUTDOWN (connection->socket_fd,
- (connection->read_closed == MHD_YES) ? SHUT_WR : SHUT_RDWR);
- connection->state = MHD_CONNECTION_CLOSED;
- if ( (NULL != daemon->notify_completed) &&
- (MHD_YES == connection->client_aware) )
- daemon->notify_completed (daemon->notify_completed_cls,
- connection,
- &connection->client_context,
- termination_code);
- connection->client_aware = MHD_NO;
-}
-
-
-/**
- * A serious error occured, close the
- * connection (and notify the application).
- *
- * @param connection connection to close with error
- * @param emsg error message (can be NULL)
- */
-static void
-connection_close_error (struct MHD_Connection *connection,
- const char *emsg)
-{
-#if HAVE_MESSAGES
- if (NULL != emsg)
- MHD_DLOG (connection->daemon, emsg);
-#endif
- MHD_connection_close (connection, MHD_REQUEST_TERMINATED_WITH_ERROR);
-}
-
-
-/**
- * Macro to only include error message in call to
- * "connection_close_error" if we have HAVE_MESSAGES.
- */
-#if HAVE_MESSAGES
-#define CONNECTION_CLOSE_ERROR(c, emsg) connection_close_error (c, emsg)
-#else
-#define CONNECTION_CLOSE_ERROR(c, emsg) connection_close_error (c, NULL)
-#endif
-
-
-/**
- * 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).
- *
- * @param connection the connection
- * @return MHD_NO if readying the response failed
- */
-static int
-try_ready_normal_body (struct MHD_Connection *connection)
-{
- ssize_t ret;
- struct MHD_Response *response;
-
- response = connection->response;
- if (NULL == response->crc)
- return MHD_YES;
- if (0 == response->total_size)
- return MHD_YES; /* 0-byte response is always ready */
- if ( (response->data_start <=
- connection->response_write_position) &&
- (response->data_size + response->data_start >
- connection->response_write_position) )
- return MHD_YES; /* response already ready */
-#if LINUX
- if ( (-1 != response->fd) &&
- (0 == (connection->daemon->options & MHD_USE_SSL)) )
- {
- /* will use sendfile, no need to bother response crc */
- return MHD_YES;
- }
-#endif
-
- ret = response->crc (response->crc_cls,
- connection->response_write_position,
- response->data,
- MHD_MIN (response->data_buffer_size,
- response->total_size -
- connection->response_write_position));
- if ((0 == ret) &&
- (0 != (connection->daemon->options & MHD_USE_SELECT_INTERNALLY)))
- mhd_panic (mhd_panic_cls, __FILE__, __LINE__
-#if HAVE_MESSAGES
- , "API violation"
-#else
- , NULL
-#endif
- );
- if ( (MHD_CONTENT_READER_END_OF_STREAM == ret) ||
- (MHD_CONTENT_READER_END_WITH_ERROR == ret) )
- {
- /* either error or http 1.0 transfer, close socket! */
- response->total_size = connection->response_write_position;
- CONNECTION_CLOSE_ERROR (connection,
- (ret == MHD_CONTENT_READER_END_OF_STREAM)
- ? "Closing connection (end of response)\n"
- : "Closing connection (stream error)\n");
- return MHD_NO;
- }
- response->data_start = connection->response_write_position;
- response->data_size = ret;
- if (0 == ret)
- {
- connection->state = MHD_CONNECTION_NORMAL_BODY_UNREADY;
- 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).
- *
- * @param connection the connection
- * @return MHD_NO if readying the response failed
- */
-static int
-try_ready_chunked_body (struct MHD_Connection *connection)
-{
- ssize_t ret;
- char *buf;
- struct MHD_Response *response;
- size_t size;
- char cbuf[10]; /* 10: max strlen of "%x\r\n" */
- int cblen;
-
- response = connection->response;
- if (0 == connection->write_buffer_size)
- {
- size = connection->daemon->pool_size;
- do
- {
- size /= 2;
- if (size < 128)
- {
- /* not enough memory */
- CONNECTION_CLOSE_ERROR (connection,
- "Closing connection (out of memory)\n");
- return MHD_NO;
- }
- buf = MHD_pool_allocate (connection->pool, size, MHD_NO);
- }
- while (NULL == buf);
- connection->write_buffer_size = size;
- connection->write_buffer = buf;
- }
-
- if ( (response->data_start <=
- connection->response_write_position) &&
- (response->data_size + response->data_start >
- connection->response_write_position) )
- {
- /* buffer already ready, use what is there for the chunk */
- ret = response->data_size + response->data_start -
connection->response_write_position;
- if (ret > connection->write_buffer_size - sizeof (cbuf) - 2)
- ret = connection->write_buffer_size - sizeof (cbuf) - 2;
- memcpy (&connection->write_buffer[sizeof (cbuf)],
- &response->data[connection->response_write_position -
response->data_start],
- ret);
- }
- else
- {
- /* buffer not in range, try to fill it */
- if (0 == response->total_size)
- ret = 0; /* response must be empty, don't bother calling crc */
- else
- ret = response->crc (response->crc_cls,
- connection->response_write_position,
- &connection->write_buffer[sizeof (cbuf)],
- connection->write_buffer_size - sizeof (cbuf) - 2);
- }
- if (MHD_CONTENT_READER_END_WITH_ERROR == ret)
- {
- /* error, close socket! */
- response->total_size = connection->response_write_position;
- CONNECTION_CLOSE_ERROR (connection,
- "Closing connection (error generating
response)\n");
- return MHD_NO;
- }
- if ( (MHD_CONTENT_READER_END_OF_STREAM == ret) ||
- (0 == response->total_size) )
- {
- /* 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 (0 == ret)
- {
- connection->state = MHD_CONNECTION_CHUNKED_BODY_UNREADY;
- return MHD_NO;
- }
- if (ret > 0xFFFFFF)
- ret = 0xFFFFFF;
- snprintf (cbuf,
- sizeof (cbuf),
- "%X\r\n", (unsigned int) ret);
- cblen = strlen (cbuf);
- EXTRA_CHECK (cblen <= sizeof (cbuf));
- memcpy (&connection->write_buffer[sizeof (cbuf) - cblen], cbuf, cblen);
- memcpy (&connection->write_buffer[sizeof (cbuf) + ret], "\r\n", 2);
- connection->response_write_position += ret;
- connection->write_buffer_send_offset = sizeof (cbuf) - cblen;
- connection->write_buffer_append_offset = sizeof (cbuf) + ret + 2;
- return MHD_YES;
-}
-
-
-/**
- * Check if we need to set some additional headers
- * for http-compiliance.
- *
- * @param connection connection to check (and possibly modify)
- */
-static void
-add_extra_headers (struct MHD_Connection *connection)
-{
- const char *have_close;
- const char *client_close;
- const char *have_encoding;
- char buf[128];
- int add_close;
-
- client_close = MHD_lookup_connection_value (connection,
- MHD_HEADER_KIND,
- MHD_HTTP_HEADER_CONNECTION);
- /* we only care about 'close', everything else is ignored */
- if ( (NULL != client_close) && (0 != strcasecmp (client_close, "close")) )
- client_close = NULL;
- have_close = MHD_get_response_header (connection->response,
- MHD_HTTP_HEADER_CONNECTION);
- if ( (NULL != have_close) && (0 != strcasecmp (have_close, "close")) )
- have_close = NULL;
- connection->have_chunked_upload = MHD_NO;
- add_close = MHD_NO;
- if (MHD_SIZE_UNKNOWN == connection->response->total_size)
- {
- /* size is unknown, need to either to HTTP 1.1 chunked encoding or
- close the connection */
- if (NULL == have_close)
- {
- /* 'close' header doesn't exist yet, see if we need to add one;
- if the client asked for a close, no need to start chunk'ing */
- if ((NULL == client_close) &&
- (NULL != connection->version) &&
- (0 == strcasecmp (connection->version, MHD_HTTP_VERSION_1_1)))
- {
- connection->have_chunked_upload = MHD_YES;
- have_encoding = MHD_get_response_header (connection->response,
-
MHD_HTTP_HEADER_TRANSFER_ENCODING);
- if (NULL == have_encoding)
- MHD_add_response_header (connection->response,
- MHD_HTTP_HEADER_TRANSFER_ENCODING,
- "chunked");
- else if (0 != strcasecmp (have_encoding, "chunked"))
- add_close = MHD_YES; /* application already set some strange
encoding, can't do 'chunked' */
- }
- else
- {
- /* HTTP not 1.1 or client asked for close => set close header */
- add_close = MHD_YES;
- }
- }
- }
- else
- {
- /* check if we should add a 'close' anyway */
- if ( (NULL != client_close) &&
- (NULL == have_close) )
- add_close = MHD_YES; /* client asked for it, so add it */
-
- /* if not present, add content length */
- if (NULL == MHD_get_response_header (connection->response,
- MHD_HTTP_HEADER_CONTENT_LENGTH))
- {
- SPRINTF (buf,
- MHD_UNSIGNED_LONG_LONG_PRINTF,
- (MHD_UNSIGNED_LONG_LONG) connection->response->total_size);
- MHD_add_response_header (connection->response,
- MHD_HTTP_HEADER_CONTENT_LENGTH, buf);
- }
- }
- if (MHD_YES == add_close)
- MHD_add_response_header (connection->response,
- MHD_HTTP_HEADER_CONNECTION, "close");
-}
-
-
-/**
- * Produce HTTP "Date:" header.
- *
- * @param date where to write the header, with
- * at least 128 bytes available space.
- */
-static void
-get_date_string (char *date)
-{
- static const char *const days[] =
- { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" };
- static const char *const 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);
- SPRINTF (date,
- "Date: %3s, %02u %3s %04u %02u:%02u:%02u GMT\r\n",
- days[now.tm_wday % 7],
- (unsigned int) now.tm_mday,
- mons[now.tm_mon % 12],
- (unsigned int) (1900 + now.tm_year),
- (unsigned int) now.tm_hour,
- (unsigned int) now.tm_min,
- (unsigned int) now.tm_sec);
-}
-
-
-/**
- * Try growing the read buffer. We initially claim half the
- * available buffer space for the read buffer (the other half
- * being left for management data structures; the write
- * buffer can in the end take virtually everything as the
- * read buffer can be reduced to the minimum necessary at that
- * point.
- *
- * @param connection the connection
- * @return MHD_YES on success, MHD_NO on failure
- */
-static int
-try_grow_read_buffer (struct MHD_Connection *connection)
-{
- void *buf;
- size_t new_size;
-
- if (0 == connection->read_buffer_size)
- new_size = connection->daemon->pool_size / 2;
- else
- new_size = connection->read_buffer_size + MHD_BUF_INC_SIZE;
- buf = MHD_pool_reallocate (connection->pool,
- connection->read_buffer,
- connection->read_buffer_size,
- new_size);
- if (NULL == buf)
- return MHD_NO;
- /* we can actually grow the buffer, do it! */
- connection->read_buffer = buf;
- connection->read_buffer_size = new_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.
- *
- * @param connection the connection
- */
-static int
-build_header_response (struct MHD_Connection *connection)
-{
- size_t size;
- size_t off;
- struct MHD_HTTP_Header *pos;
- char code[256];
- char date[128];
- char *data;
- enum MHD_ValueKind kind;
- const char *reason_phrase;
- uint32_t rc;
- int must_add_close;
-
- EXTRA_CHECK (NULL != connection->version);
- if (0 == strlen(connection->version))
- {
- data = MHD_pool_allocate (connection->pool, 0, MHD_YES);
- connection->write_buffer = data;
- connection->write_buffer_append_offset = 0;
- connection->write_buffer_send_offset = 0;
- connection->write_buffer_size = 0;
- return MHD_YES;
- }
- if (MHD_CONNECTION_FOOTERS_RECEIVED == connection->state)
- {
- add_extra_headers (connection);
- rc = connection->responseCode & (~MHD_ICY_FLAG);
- reason_phrase = MHD_get_reason_phrase_for (rc);
- SPRINTF (code,
- "%s %u %s\r\n",
- (0 != (connection->responseCode & MHD_ICY_FLAG))
- ? "ICY"
- : ( (0 == strcasecmp (MHD_HTTP_VERSION_1_0,
- connection->version))
- ? MHD_HTTP_VERSION_1_0
- : MHD_HTTP_VERSION_1_1),
- rc,
- reason_phrase);
- off = strlen (code);
- /* estimate size */
- size = off + 2; /* extra \r\n at the end */
- kind = MHD_HEADER_KIND;
- if ( (0 == (connection->daemon->options & MHD_SUPPRESS_DATE_NO_CLOCK))
&&
- (NULL == MHD_get_response_header (connection->response,
- MHD_HTTP_HEADER_DATE)) )
- get_date_string (date);
- else
- date[0] = '\0';
- size += strlen (date);
- }
- else
- {
- size = 2;
- kind = MHD_FOOTER_KIND;
- off = 0;
- }
- must_add_close = ( (connection->state == MHD_CONNECTION_FOOTERS_RECEIVED) &&
- (connection->read_closed == MHD_YES) &&
- (0 == strcasecmp (connection->version,
- MHD_HTTP_VERSION_1_1)) &&
- (NULL == MHD_get_response_header (connection->response,
-
MHD_HTTP_HEADER_CONNECTION)) );
- if (must_add_close)
- size += strlen ("Connection: close\r\n");
- for (pos = connection->response->first_header; NULL != pos; pos = pos->next)
- if (pos->kind == kind)
- size += strlen (pos->header) + strlen (pos->value) + 4; /* colon, space,
linefeeds */
- /* produce data */
- data = MHD_pool_allocate (connection->pool, size + 1, MHD_YES);
- if (NULL == data)
- {
-#if HAVE_MESSAGES
- MHD_DLOG (connection->daemon, "Not enough memory for write!\n");
-#endif
- return MHD_NO;
- }
- if (MHD_CONNECTION_FOOTERS_RECEIVED == connection->state)
- {
- memcpy (data, code, off);
- }
- if (must_add_close)
- {
- /* we must add the 'close' header because circumstances forced us to
- stop reading from the socket; however, we are not adding the header
- to the response as the response may be used in a different context
- as well */
- memcpy (&data[off], "Connection: close\r\n",
- strlen ("Connection: close\r\n"));
- off += strlen ("Connection: close\r\n");
- }
- for (pos = connection->response->first_header; NULL != pos; pos = pos->next)
- if (pos->kind == kind)
- off += SPRINTF (&data[off],
- "%s: %s\r\n",
- pos->header,
- pos->value);
- if (connection->state == MHD_CONNECTION_FOOTERS_RECEIVED)
- {
- strcpy (&data[off], date);
- off += strlen (date);
- }
- memcpy (&data[off], "\r\n", 2);
- off += 2;
-
- if (off != size)
- mhd_panic (mhd_panic_cls, __FILE__, __LINE__, NULL);
- 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;
-}
-
-
-/**
- * We encountered an error processing the request.
- * Handle it properly by stopping to read data
- * and sending the indicated response code and message.
- *
- * @param connection the connection
- * @param status_code the response code to send (400, 413 or 414)
- * @param message the error message to send
- */
-static void
-transmit_error_response (struct MHD_Connection *connection,
- unsigned int status_code,
- const char *message)
-{
- struct MHD_Response *response;
-
- if (NULL == connection->version)
- {
- /* we were unable to process the full header line, so we don't
- really know what version the client speaks; assume 1.0 */
- connection->version = MHD_HTTP_VERSION_1_0;
- }
- connection->state = MHD_CONNECTION_FOOTERS_RECEIVED;
- connection->read_closed = MHD_YES;
-#if HAVE_MESSAGES
- MHD_DLOG (connection->daemon,
- "Error %u (`%s') processing request, closing connection.\n",
- status_code, message);
-#endif
- EXTRA_CHECK (connection->response == NULL);
- response = MHD_create_response_from_buffer (strlen (message),
- (void *) message,
- MHD_RESPMEM_PERSISTENT);
- 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! */
- CONNECTION_CLOSE_ERROR (connection,
- "Closing connection (failed to create response
header)\n");
- }
- else
- {
- connection->state = MHD_CONNECTION_HEADERS_SENDING;
- }
-}
-
-
-/**
- * Add "fd" to the "fd_set". If "fd" is
- * greater than "*max", set "*max" to fd.
- *
- * @param fd file descriptor to add to the set
- * @param set set to modify
- * @param max_fd maximum value to potentially update
- */
-static void
-add_to_fd_set (int fd,
- fd_set *set,
- int *max_fd)
-{
- FD_SET (fd, set);
- if ( (NULL != max_fd) &&
- (fd > *max_fd) )
- *max_fd = fd;
-}
-
-
-/**
- * Obtain the select sets for this connection. The given
- * sets (and the maximum) are updated and must have
- * already been initialized.
- *
- * @param connection connetion to get select sets for
- * @param read_fd_set read set to initialize
- * @param write_fd_set write set to initialize
- * @param except_fd_set except set to initialize (never changed)
- * @param max_fd where to store largest FD put into any set
- * @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 ret;
- struct MHD_Pollfd p;
-
- /* we use the 'poll fd' as a convenient way to re-use code
- when determining the select sets */
- memset (&p, 0, sizeof(struct MHD_Pollfd));
- ret = MHD_connection_get_pollfd (connection, &p);
- if ( (MHD_YES == ret) && (p.fd >= 0) ) {
- if (0 != (p.events & MHD_POLL_ACTION_IN))
- add_to_fd_set(p.fd, read_fd_set, max_fd);
- if (0 != (p.events & MHD_POLL_ACTION_OUT))
- add_to_fd_set(p.fd, write_fd_set, max_fd);
- }
- return ret;
-}
-
-
-/**
- * Obtain the pollfd for this connection
- *
- * @param connection connetion to get poll set for
- * @param p where to store the polling information
- * @return MHD_YES on success. If return MHD_YES and p->fd < 0, this
- * connection is not waiting for any read or write events
- */
-int
-MHD_connection_get_pollfd (struct MHD_Connection *connection,
- struct MHD_Pollfd *p)
-{
- int fd;
-
- if (NULL == connection->pool)
- connection->pool = MHD_pool_create (connection->daemon->pool_size);
- if (NULL == connection->pool)
- {
- CONNECTION_CLOSE_ERROR (connection,
- "Failed to create memory pool!\n");
- return MHD_YES;
- }
- fd = connection->socket_fd;
- p->fd = fd;
- if (-1 == fd)
- return MHD_YES;
- while (1)
- {
-#if DEBUG_STATES
- MHD_DLOG (connection->daemon, "%s: state: %s\n",
- __FUNCTION__, MHD_state_to_string (connection->state));
-#endif
- switch (connection->state)
- {
-#if HTTPS_SUPPORT
- case MHD_TLS_CONNECTION_INIT:
- if (0 == gnutls_record_get_direction (connection->tls_session))
- p->events |= MHD_POLL_ACTION_IN;
- else
- p->events |= MHD_POLL_ACTION_OUT;
- break;
-#endif
- 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) &&
- (0 == connection->read_buffer_offset))
- {
- CONNECTION_CLOSE_ERROR (connection,
- "Connection buffer to small for
request\n");
- continue;
- }
- if ((connection->read_buffer_offset == connection->read_buffer_size)
- && (MHD_NO == try_grow_read_buffer (connection)))
- {
- transmit_error_response (connection,
- (connection->url != NULL)
- ? MHD_HTTP_REQUEST_ENTITY_TOO_LARGE
- : MHD_HTTP_REQUEST_URI_TOO_LONG,
- REQUEST_TOO_BIG);
- continue;
- }
- if (MHD_NO == connection->read_closed)
- p->events |= MHD_POLL_ACTION_IN;
- 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:
- p->events |= MHD_POLL_ACTION_OUT;
- break;
- case MHD_CONNECTION_CONTINUE_SENT:
- if (connection->read_buffer_offset == connection->read_buffer_size)
- {
- if ((MHD_YES != try_grow_read_buffer (connection)) &&
- (0 != (connection->daemon->options &
- (MHD_USE_SELECT_INTERNALLY |
- MHD_USE_THREAD_PER_CONNECTION))))
- {
- /* failed to grow the read buffer, and the
- client which is supposed to handle the
- received data in a *blocking* fashion
- (in this mode) did not handle the data as
- it was supposed to!
- => we would either have to do busy-waiting
- (on the client, which would likely fail),
- or if we do nothing, we would just timeout
- on the connection (if a timeout is even
- set!).
- Solution: we kill the connection with an error */
- transmit_error_response (connection,
- MHD_HTTP_INTERNAL_SERVER_ERROR,
- INTERNAL_ERROR);
- continue;
- }
- }
- if ((connection->read_buffer_offset < connection->read_buffer_size)
- && (MHD_NO == connection->read_closed))
- p->events |= MHD_POLL_ACTION_IN;
- 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_CLOSE_ERROR (connection,
- NULL);
- continue;
- }
- p->events |= MHD_POLL_ACTION_IN;
- /* 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 */
- p->events |= MHD_POLL_ACTION_OUT;
- break;
- case MHD_CONNECTION_HEADERS_SENT:
- EXTRA_CHECK (0);
- break;
- case MHD_CONNECTION_NORMAL_BODY_READY:
- p->events |= MHD_POLL_ACTION_OUT;
- break;
- case MHD_CONNECTION_NORMAL_BODY_UNREADY:
- /* not ready, no socket action */
- break;
- case MHD_CONNECTION_CHUNKED_BODY_READY:
- p->events |= MHD_POLL_ACTION_OUT;
- 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:
- p->events |= MHD_POLL_ACTION_OUT;
- break;
- case MHD_CONNECTION_FOOTERS_SENT:
- EXTRA_CHECK (0);
- break;
- case MHD_CONNECTION_CLOSED:
- 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
- * far too long, close the connection. If no line is
- * found (incomplete, buffer too small, line too long),
- * return NULL. Otherwise return a pointer to the line.
- */
-static char *
-get_next_header_line (struct MHD_Connection *connection)
-{
- char *rbuf;
- size_t pos;
-
- if (0 == connection->read_buffer_offset)
- return NULL;
- pos = 0;
- rbuf = connection->read_buffer;
- while ((pos < connection->read_buffer_offset - 1) &&
- ('\r' != rbuf[pos]) && ('\n' != rbuf[pos]))
- pos++;
- if (pos == connection->read_buffer_offset - 1)
- {
- /* not found, consider growing... */
- if ( (connection->read_buffer_offset == connection->read_buffer_size) &&
- (MHD_NO ==
- try_grow_read_buffer (connection)) )
- {
- transmit_error_response (connection,
- (NULL != connection->url)
- ? MHD_HTTP_REQUEST_ENTITY_TOO_LARGE
- : MHD_HTTP_REQUEST_URI_TOO_LONG,
- REQUEST_TOO_BIG);
- }
- return NULL;
- }
- /* found, check if we have proper CRLF */
- if (('\r' == rbuf[pos]) && ('\n' == rbuf[pos + 1]))
- rbuf[pos++] = '\0'; /* skip both r and n */
- rbuf[pos++] = '\0';
- connection->read_buffer += pos;
- connection->read_buffer_size -= pos;
- connection->read_buffer_offset -= pos;
- return rbuf;
-}
-
-
-/**
- * Add an entry to the HTTP headers of a connection. If this fails,
- * transmit an error response (request too big).
- *
- * @param connection the connection for which a
- * value should be set
- * @param kind kind of the value
- * @param key key for the value
- * @param value the value itself
- * @return MHD_NO on failure (out of memory), MHD_YES for success
- */
-static int
-connection_add_header (struct MHD_Connection *connection,
- char *key, char *value, enum MHD_ValueKind kind)
-{
- if (MHD_NO == MHD_set_connection_value (connection,
- kind,
- key, value))
- {
-#if HAVE_MESSAGES
- MHD_DLOG (connection->daemon,
- "Not enough memory to allocate header record!\n");
-#endif
- transmit_error_response (connection, MHD_HTTP_REQUEST_ENTITY_TOO_LARGE,
- REQUEST_TOO_BIG);
- return MHD_NO;
- }
- return MHD_YES;
-}
-
-
-/**
- * Parse and unescape the arguments given by the client as part
- * of the HTTP request URI.
- *
- * @param kind header kind to use for adding to the connection
- * @param connection connection to add headers to
- * @param args argument URI string (after "?" in URI)
- * @return MHD_NO on failure (out of memory), MHD_YES for success
- */
-static int
-parse_arguments (enum MHD_ValueKind kind,
- struct MHD_Connection *connection,
- char *args)
-{
- char *equals;
- char *amper;
-
- while (NULL != args)
- {
- equals = strchr (args, '=');
- amper = strchr (args, '&');
- if (NULL == amper)
- {
- /* last argument */
- if (NULL == equals)
- {
- /* got 'foo', add key 'foo' with NULL for value */
- connection->daemon->unescape_callback
(connection->daemon->unescape_callback_cls,
- connection,
- args);
- return connection_add_header (connection,
- args,
- NULL,
- kind);
- }
- /* got 'foo=bar' */
- equals[0] = '\0';
- equals++;
- connection->daemon->unescape_callback
(connection->daemon->unescape_callback_cls,
- connection,
- args);
- connection->daemon->unescape_callback
(connection->daemon->unescape_callback_cls,
- connection,
- equals);
- return connection_add_header (connection, args, equals, kind);
- }
- /* amper is non-NULL here */
- amper[0] = '\0';
- amper++;
- if ( (NULL == equals) ||
- (equals >= amper) )
- {
- /* got 'foo&bar' or 'foo&bar=val', add key 'foo' with NULL for value
*/
- connection->daemon->unescape_callback
(connection->daemon->unescape_callback_cls,
- connection,
- args);
- if (MHD_NO ==
- connection_add_header (connection,
- args,
- NULL,
- kind))
- return MHD_NO;
- /* continue with 'bar' */
- args = amper;
- continue;
-
- }
- /* equals and amper are non-NULL here, and equals < amper,
- so we got regular 'foo=value&bar...'-kind of argument */
- equals[0] = '\0';
- equals++;
- connection->daemon->unescape_callback
(connection->daemon->unescape_callback_cls,
- connection,
- args);
- connection->daemon->unescape_callback
(connection->daemon->unescape_callback_cls,
- connection,
- equals);
- if (MHD_NO == connection_add_header (connection, args, equals, kind))
- return MHD_NO;
- args = amper;
- }
- return MHD_YES;
-}
-
-
-/**
- * Parse the cookie header (see RFC 2109).
- *
- * @return MHD_YES for success, MHD_NO for failure (malformed, out of memory)
- */
-static int
-parse_cookie_header (struct MHD_Connection *connection)
-{
- const char *hdr;
- char *cpy;
- char *pos;
- char *sce;
- char *semicolon;
- char *equals;
- char *ekill;
- char old;
- int quotes;
-
- hdr = MHD_lookup_connection_value (connection,
- MHD_HEADER_KIND,
- MHD_HTTP_HEADER_COOKIE);
- if (hdr == NULL)
- return MHD_YES;
- cpy = MHD_pool_allocate (connection->pool, strlen (hdr) + 1, MHD_YES);
- if (cpy == NULL)
- {
-#if HAVE_MESSAGES
- MHD_DLOG (connection->daemon, "Not enough memory to parse cookies!\n");
-#endif
- transmit_error_response (connection, MHD_HTTP_REQUEST_ENTITY_TOO_LARGE,
- REQUEST_TOO_BIG);
- return MHD_NO;
- }
- memcpy (cpy, hdr, strlen (hdr) + 1);
- pos = cpy;
- while (pos != NULL)
- {
- while (*pos == ' ')
- pos++; /* skip spaces */
-
- sce = pos;
- while (((*sce) != '\0') &&
- ((*sce) != ',') && ((*sce) != ';') && ((*sce) != '='))
- sce++;
- /* remove tailing whitespace (if any) from key */
- ekill = sce - 1;
- while ((*ekill == ' ') && (ekill >= pos))
- *(ekill--) = '\0';
- old = *sce;
- *sce = '\0';
- if (old != '=')
- {
- /* value part omitted, use empty string... */
- if (MHD_NO ==
- connection_add_header (connection, pos, "", MHD_COOKIE_KIND))
- return MHD_NO;
- if (old == '\0')
- break;
- pos = sce + 1;
- continue;
- }
- equals = sce + 1;
- quotes = 0;
- semicolon = equals;
- while ((semicolon[0] != '\0') &&
- ((quotes != 0) ||
- ((semicolon[0] != ';') && (semicolon[0] != ','))))
- {
- if (semicolon[0] == '"')
- quotes = (quotes + 1) & 1;
- semicolon++;
- }
- if (semicolon[0] == '\0')
- semicolon = NULL;
- if (semicolon != NULL)
- {
- semicolon[0] = '\0';
- semicolon++;
- }
- /* remove quotes */
- if ((equals[0] == '"') && (equals[strlen (equals) - 1] == '"'))
- {
- equals[strlen (equals) - 1] = '\0';
- equals++;
- }
- if (MHD_NO == connection_add_header (connection,
- pos, equals, MHD_COOKIE_KIND))
- return MHD_NO;
- pos = semicolon;
- }
- return MHD_YES;
-}
-
-
-/**
- * Parse the first line of the HTTP HEADER.
- *
- * @param connection the connection (updated)
- * @param line the first line
- * @return MHD_YES if the line is ok, MHD_NO if it is malformed
- */
-static int
-parse_initial_message_line (struct MHD_Connection *connection, char *line)
-{
- char *uri;
- char *httpVersion;
- char *args;
-
- if (NULL == (uri = strchr (line, ' ')))
- return MHD_NO; /* serious error */
- uri[0] = '\0';
- connection->method = line;
- uri++;
- while (uri[0] == ' ')
- uri++;
- httpVersion = strchr (uri, ' ');
- if (httpVersion != NULL)
- {
- httpVersion[0] = '\0';
- httpVersion++;
- }
- if (connection->daemon->uri_log_callback != NULL)
- connection->client_context
- =
- connection->daemon->uri_log_callback (connection->daemon->
- uri_log_callback_cls, uri);
- args = strchr (uri, '?');
- if (NULL != args)
- {
- args[0] = '\0';
- args++;
- parse_arguments (MHD_GET_ARGUMENT_KIND, connection, args);
- }
- connection->daemon->unescape_callback
(connection->daemon->unescape_callback_cls,
- connection,
- uri);
- connection->url = uri;
- if (NULL == httpVersion)
- connection->version = "";
- else
- connection->version = httpVersion;
- return MHD_YES;
-}
-
-
-/**
- * Call the handler of the application for this
- * connection. Handles chunking of the upload
- * as well as normal uploads.
- */
-static void
-call_connection_handler (struct MHD_Connection *connection)
-{
- size_t processed;
-
- if (NULL != connection->response)
- return; /* already queued a response */
- processed = 0;
- connection->client_aware = MHD_YES;
- if (MHD_NO ==
- connection->daemon->default_handler (connection->daemon->
- default_handler_cls,
- connection, connection->url,
- connection->method,
- connection->version,
- NULL, &processed,
- &connection->client_context))
- {
- /* serious internal error, close connection */
- CONNECTION_CLOSE_ERROR (connection,
- "Internal application error, closing
connection.\n");
- return;
- }
-}
-
-
-
-/**
- * Call the handler of the application for this
- * connection. Handles chunking of the upload
- * as well as normal uploads.
- */
-static void
-process_request_body (struct MHD_Connection *connection)
-{
- size_t processed;
- size_t available;
- size_t used;
- size_t i;
- int instant_retry;
- int malformed;
- char *buffer_head;
- char *end;
-
- if (NULL != connection->response)
- return; /* already queued a response */
-
- buffer_head = connection->read_buffer;
- available = connection->read_buffer_offset;
- do
- {
- instant_retry = MHD_NO;
- if ((connection->have_chunked_upload == MHD_YES) &&
- (connection->remaining_upload_size == MHD_SIZE_UNKNOWN))
- {
- if ((connection->current_chunk_offset ==
- connection->current_chunk_size)
- && (connection->current_chunk_offset != 0) && (available >= 2))
- {
- /* skip new line at the *end* of a chunk */
- i = 0;
- if ((buffer_head[i] == '\r') || (buffer_head[i] == '\n'))
- i++; /* skip 1st part of line feed */
- if ((buffer_head[i] == '\r') || (buffer_head[i] == '\n'))
- i++; /* skip 2nd part of line feed */
- if (i == 0)
- {
- /* malformed encoding */
- CONNECTION_CLOSE_ERROR (connection,
- "Received malformed HTTP request (bad
chunked encoding), closing connection.\n");
- return;
- }
- available -= i;
- buffer_head += i;
- connection->current_chunk_offset = 0;
- connection->current_chunk_size = 0;
- }
- if (connection->current_chunk_offset <
- connection->current_chunk_size)
- {
- /* we are in the middle of a chunk, give
- as much as possible to the client (without
- crossing chunk boundaries) */
- processed =
- connection->current_chunk_size -
- connection->current_chunk_offset;
- if (processed > available)
- processed = available;
- if (available > processed)
- instant_retry = MHD_YES;
- }
- else
- {
- /* we need to read chunk boundaries */
- i = 0;
- while (i < available)
- {
- if ((buffer_head[i] == '\r') || (buffer_head[i] == '\n'))
- break;
- i++;
- if (i >= 6)
- break;
- }
- /* take '\n' into account; if '\n'
- is the unavailable character, we
- will need to wait until we have it
- before going further */
- if ((i + 1 >= available) &&
- !((i == 1) && (available == 2) && (buffer_head[0] == '0')))
- break; /* need more data... */
- malformed = (i >= 6);
- if (!malformed)
- {
- buffer_head[i] = '\0';
- connection->current_chunk_size = strtoul (buffer_head, &end,
16);
- malformed = ('\0' != *end);
- }
- if (malformed)
- {
- /* malformed encoding */
- CONNECTION_CLOSE_ERROR (connection,
- "Received malformed HTTP request (bad
chunked encoding), closing connection.\n");
- return;
- }
- i++;
- if ((i < available) &&
- ((buffer_head[i] == '\r') || (buffer_head[i] == '\n')))
- i++; /* skip 2nd part of line feed */
-
- buffer_head += i;
- available -= i;
- connection->current_chunk_offset = 0;
-
- if (available > 0)
- instant_retry = MHD_YES;
- if (connection->current_chunk_size == 0)
- {
- connection->remaining_upload_size = 0;
- break;
- }
- continue;
- }
- }
- else
- {
- /* no chunked encoding, give all to the client */
- if ( (0 != connection->remaining_upload_size) &&
- (MHD_SIZE_UNKNOWN != connection->remaining_upload_size) &&
- (connection->remaining_upload_size < available) )
- {
- processed = connection->remaining_upload_size;
- }
- else
- {
- /**
- * 1. no chunked encoding, give all to the client
- * 2. client may send large chunked data, but only a smaller
part is available at one time.
- */
- processed = available;
- }
- }
- used = processed;
- connection->client_aware = MHD_YES;
- if (MHD_NO ==
- connection->daemon->default_handler (connection->daemon->
- default_handler_cls,
- connection, connection->url,
- connection->method,
- connection->version,
- buffer_head, &processed,
- &connection->client_context))
- {
- /* serious internal error, close connection */
- CONNECTION_CLOSE_ERROR (connection,
- "Internal application error, closing
connection.\n");
- return;
- }
- if (processed > used)
- mhd_panic (mhd_panic_cls, __FILE__, __LINE__
-#if HAVE_MESSAGES
- , "API violation"
-#else
- , NULL
-#endif
- );
- if (processed != 0)
- instant_retry = MHD_NO; /* client did not process everything */
- used -= processed;
- if (connection->have_chunked_upload == MHD_YES)
- connection->current_chunk_offset += used;
- /* dh left "processed" bytes in buffer for next time... */
- buffer_head += used;
- available -= used;
- if (connection->remaining_upload_size != MHD_SIZE_UNKNOWN)
- connection->remaining_upload_size -= used;
- }
- while (MHD_YES == instant_retry);
- if (available > 0)
- memmove (connection->read_buffer, buffer_head, available);
- connection->read_buffer_offset = available;
-}
-
-
-/**
- * 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
- */
-static int
-do_read (struct MHD_Connection *connection)
-{
- int bytes_read;
-
- if (connection->read_buffer_size == connection->read_buffer_offset)
- return MHD_NO;
-
- bytes_read = connection->recv_cls (connection,
- &connection->read_buffer
- [connection->read_buffer_offset],
- connection->read_buffer_size -
- connection->read_buffer_offset);
- if (bytes_read < 0)
- {
- if ((EINTR == errno) || (EAGAIN == errno))
- return MHD_NO;
-#if HAVE_MESSAGES
-#if HTTPS_SUPPORT
- if (0 != (connection->daemon->options & MHD_USE_SSL))
- MHD_DLOG (connection->daemon,
- "Failed to receive data: %s\n",
- gnutls_strerror (bytes_read));
- else
-#endif
- MHD_DLOG (connection->daemon,
- "Failed to receive data: %s\n", STRERROR (errno));
-#endif
- CONNECTION_CLOSE_ERROR (connection, NULL);
- return MHD_YES;
- }
- if (0 == bytes_read)
- {
- /* other side closed connection */
- connection->read_closed = MHD_YES;
- SHUTDOWN (connection->socket_fd, SHUT_RD);
- return MHD_YES;
- }
- connection->read_buffer_offset += bytes_read;
- 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
-do_write (struct MHD_Connection *connection)
-{
- int ret;
-
- ret = connection->send_cls (connection,
- &connection->write_buffer
- [connection->write_buffer_send_offset],
- connection->write_buffer_append_offset
- - connection->write_buffer_send_offset);
-
- if (ret < 0)
- {
- if ((EINTR == errno) || (EAGAIN == errno))
- return MHD_NO;
-#if HAVE_MESSAGES
-#if HTTPS_SUPPORT
- if (0 != (connection->daemon->options & MHD_USE_SSL))
- MHD_DLOG (connection->daemon,
- "Failed to send data: %s\n",
- gnutls_strerror (ret));
- else
-#endif
- MHD_DLOG (connection->daemon,
- "Failed to send data: %s\n", STRERROR (errno));
-#endif
- CONNECTION_CLOSE_ERROR (connection, NULL);
- return MHD_YES;
- }
-#if DEBUG_SEND_DATA
- FPRINTF (stderr,
- "Sent 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".
- *
- * @param connection connection to check write status for
- * @param next_state the next state to transition to
- * @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->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;
-}
-
-
-/**
- * We have received (possibly the beginning of) a line in the
- * header (or footer). Validate (check for ":") and prepare
- * to process.
- */
-static int
-process_header_line (struct MHD_Connection *connection, char *line)
-{
- char *colon;
-
- /* line should be normal header line, find colon */
- colon = strchr (line, ':');
- if (colon == NULL)
- {
- /* error in header line, die hard */
- CONNECTION_CLOSE_ERROR (connection,
- "Received malformed line (no colon), closing
connection.\n");
- return MHD_NO;
- }
- /* 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;
- return MHD_YES;
-}
-
-
-/**
- * Process a header value that spans multiple lines.
- * The previous line(s) are in connection->last.
- *
- * @param connection connection we're processing
- * @param line the current input line
- * @param kind if the line is complete, add a header
- * of the given kind
- * @return MHD_YES if the line was processed successfully
- */
-static int
-process_broken_line (struct MHD_Connection *connection,
- char *line, enum MHD_ValueKind kind)
-{
- char *last;
- char *tmp;
- size_t last_len;
- size_t tmp_len;
-
- last = connection->last;
- if ((line[0] == ' ') || (line[0] == '\t'))
- {
- /* value was continued on the next line, see
- http://www.jmarshall.com/easy/http/ */
- last_len = strlen (last);
- /* skip whitespace at start of 2nd line */
- tmp = line;
- while ((tmp[0] == ' ') || (tmp[0] == '\t'))
- tmp++;
- tmp_len = strlen (tmp);
- last = MHD_pool_reallocate (connection->pool,
- last,
- last_len + 1,
- last_len + tmp_len + 1);
- if (last == NULL)
- {
- transmit_error_response (connection,
- MHD_HTTP_REQUEST_ENTITY_TOO_LARGE,
- REQUEST_TOO_BIG);
- return MHD_NO;
- }
- memcpy (&last[last_len], tmp, tmp_len + 1);
- connection->last = last;
- return MHD_YES; /* possibly more than 2 lines... */
- }
- EXTRA_CHECK ((last != NULL) && (connection->colon != NULL));
- if ((MHD_NO == connection_add_header (connection,
- last, connection->colon, kind)))
- {
- transmit_error_response (connection, MHD_HTTP_REQUEST_ENTITY_TOO_LARGE,
- REQUEST_TOO_BIG);
- return MHD_NO;
- }
- /* we still have the current line to deal with... */
- if (strlen (line) != 0)
- {
- if (MHD_NO == process_header_line (connection, line))
- {
- transmit_error_response (connection,
- MHD_HTTP_BAD_REQUEST, REQUEST_MALFORMED);
- return MHD_NO;
- }
- }
- return MHD_YES;
-}
-
-
-/**
- * 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
-parse_connection_headers (struct MHD_Connection *connection)
-{
- const char *clen;
- MHD_UNSIGNED_LONG_LONG cval;
- struct MHD_Response *response;
- const char *enc;
- char *end;
-
- 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
- EXTRA_CHECK (connection->response == NULL);
- response =
- MHD_create_response_from_buffer (strlen (REQUEST_LACKS_HOST),
- REQUEST_LACKS_HOST,
- MHD_RESPMEM_PERSISTENT);
- MHD_queue_response (connection, MHD_HTTP_BAD_REQUEST, response);
- MHD_destroy_response (response);
- return;
- }
-
- connection->remaining_upload_size = 0;
- enc = MHD_lookup_connection_value (connection,
- MHD_HEADER_KIND,
- MHD_HTTP_HEADER_TRANSFER_ENCODING);
- if (enc != NULL)
- {
- connection->remaining_upload_size = MHD_SIZE_UNKNOWN;
- if (0 == strcasecmp (enc, "chunked"))
- connection->have_chunked_upload = MHD_YES;
- }
- else
- {
- clen = MHD_lookup_connection_value (connection,
- MHD_HEADER_KIND,
- MHD_HTTP_HEADER_CONTENT_LENGTH);
- if (clen != NULL)
- {
- cval = strtoul (clen, &end, 10);
- if ( ('\0' != *end) ||
- ( (LONG_MAX == cval) && (errno == ERANGE) ) )
- {
-#if HAVE_MESSAGES
- MHD_DLOG (connection->daemon,
- "Failed to parse `%s' header `%s', closing connection.\n",
- MHD_HTTP_HEADER_CONTENT_LENGTH, clen);
-#endif
- CONNECTION_CLOSE_ERROR (connection, NULL);
- return;
- }
- connection->remaining_upload_size = cval;
- }
- }
-}
-
-
-/**
- * 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.
- *
- * @param connection connection to handle
- * @return always MHD_YES (we should continue to process the
- * connection)
- */
-int
-MHD_connection_handle_read (struct MHD_Connection *connection)
-{
- connection->last_activity = MHD_monotonic_time();
- if (connection->state == MHD_CONNECTION_CLOSED)
- return MHD_YES;
- /* make sure "read" has a reasonable number of bytes
- in buffer to use per system call (if possible) */
- if (connection->read_buffer_offset + MHD_BUF_INC_SIZE >
- connection->read_buffer_size)
- try_grow_read_buffer (connection);
- if (MHD_NO == do_read (connection))
- return MHD_YES;
- while (1)
- {
-#if DEBUG_STATES
- MHD_DLOG (connection->daemon, "%s: state: %s\n",
- __FUNCTION__, MHD_state_to_string (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)
- {
- MHD_connection_close (connection,
- MHD_REQUEST_TERMINATED_READ_ERROR);
- continue;
- }
- break;
- case MHD_CONNECTION_CLOSED:
- return MHD_YES;
- 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;
-}
-
-
-/**
- * This function was created to handle writes to sockets when it has
- * been determined that the socket can be written to. All
- * implementations (multithreaded, external select, internal select)
- * call this function
- *
- * @param connection connection to handle
- * @return always MHD_YES (we should continue to process the
- * connection)
- */
-int
-MHD_connection_handle_write (struct MHD_Connection *connection)
-{
- struct MHD_Response *response;
- int ret;
- connection->last_activity = MHD_monotonic_time();
- while (1)
- {
-#if DEBUG_STATES
- MHD_DLOG (connection->daemon, "%s: state: %s\n",
- __FUNCTION__, MHD_state_to_string (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 = connection->send_cls (connection,
- &HTTP_100_CONTINUE
-
[connection->continue_message_write_offset],
- strlen (HTTP_100_CONTINUE) -
-
connection->continue_message_write_offset);
- if (ret < 0)
- {
- if ((errno == EINTR) || (errno == EAGAIN))
- break;
-#if HAVE_MESSAGES
- MHD_DLOG (connection->daemon,
- "Failed to send data: %s\n", STRERROR (errno));
-#endif
- CONNECTION_CLOSE_ERROR (connection, NULL);
- return MHD_YES;
- }
-#if DEBUG_SEND_DATA
- FPRINTF (stderr,
- "Sent 100 continue response: `%.*s'\n",
- ret,
- &HTTP_100_CONTINUE
- [connection->continue_message_write_offset]);
-#endif
- 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);
- if (connection->state != MHD_CONNECTION_HEADERS_SENDING)
- break;
- 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);
- break;
- }
- ret = connection->send_cls (connection,
- &response->data
- [connection->response_write_position
- - response->data_start],
- response->data_size -
- (connection->response_write_position
- - response->data_start));
-#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) || (errno == EAGAIN))
- return MHD_YES;
-#if HAVE_MESSAGES
- MHD_DLOG (connection->daemon,
- "Failed to send data: %s\n", STRERROR (errno));
-#endif
- CONNECTION_CLOSE_ERROR (connection, NULL);
- return MHD_YES;
- }
- 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);
- if (connection->state != MHD_CONNECTION_CHUNKED_BODY_READY)
- break;
- 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);
- if (connection->state != MHD_CONNECTION_FOOTERS_SENDING)
- break;
- check_write_done (connection, MHD_CONNECTION_FOOTERS_SENT);
- break;
- case MHD_CONNECTION_FOOTERS_SENT:
- EXTRA_CHECK (0);
- break;
- case MHD_CONNECTION_CLOSED:
- return MHD_YES;
- case MHD_TLS_CONNECTION_INIT:
- EXTRA_CHECK (0);
- break;
- default:
- EXTRA_CHECK (0);
- CONNECTION_CLOSE_ERROR (connection, "Internal error\n");
- return MHD_YES;
- }
- 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.
- *
- * @param connection connection to handle
- * @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)
-{
- struct MHD_Daemon *daemon;
- unsigned int timeout;
- const char *end;
- int rend;
- char *line;
-
- while (1)
- {
-#if DEBUG_STATES
- MHD_DLOG (connection->daemon, "%s: state: %s\n",
- __FUNCTION__, MHD_state_to_string (connection->state));
-#endif
- switch (connection->state)
- {
- case MHD_CONNECTION_INIT:
- line = get_next_header_line (connection);
- if (line == NULL)
- {
- if (connection->state != MHD_CONNECTION_INIT)
- continue;
- if (connection->read_closed)
- {
- CONNECTION_CLOSE_ERROR (connection,
- NULL);
- continue;
- }
- break;
- }
- if (MHD_NO == parse_initial_message_line (connection, line))
- CONNECTION_CLOSE_ERROR (connection, NULL);
- else
- connection->state = MHD_CONNECTION_URL_RECEIVED;
- continue;
- case MHD_CONNECTION_URL_RECEIVED:
- line = get_next_header_line (connection);
- if (line == NULL)
- {
- if (connection->state != MHD_CONNECTION_URL_RECEIVED)
- continue;
- if (connection->read_closed)
- {
- CONNECTION_CLOSE_ERROR (connection,
- NULL);
- continue;
- }
- break;
- }
- if (strlen (line) == 0)
- {
- connection->state = MHD_CONNECTION_HEADERS_RECEIVED;
- continue;
- }
- if (MHD_NO == process_header_line (connection, line))
- {
- transmit_error_response (connection,
- MHD_HTTP_BAD_REQUEST,
- REQUEST_MALFORMED);
- break;
- }
- connection->state = MHD_CONNECTION_HEADER_PART_RECEIVED;
- continue;
- case MHD_CONNECTION_HEADER_PART_RECEIVED:
- line = get_next_header_line (connection);
- if (line == NULL)
- {
- if (connection->state != MHD_CONNECTION_HEADER_PART_RECEIVED)
- continue;
- if (connection->read_closed)
- {
- CONNECTION_CLOSE_ERROR (connection,
- NULL);
- continue;
- }
- break;
- }
- if (MHD_NO ==
- process_broken_line (connection, line, MHD_HEADER_KIND))
- continue;
- 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 (connection->state == MHD_CONNECTION_CLOSED)
- continue;
- if (need_100_continue (connection))
- {
- connection->state = MHD_CONNECTION_CONTINUE_SENDING;
- break;
- }
- if (connection->response != NULL)
- {
- /* we refused (no upload allowed!) */
- connection->remaining_upload_size = 0;
- /* force close, in case client still tries to upload... */
- connection->read_closed = MHD_YES;
- }
- 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)
- {
- process_request_body (connection); /* loop call */
- if (connection->state == MHD_CONNECTION_CLOSED)
- continue;
- }
- if ((connection->remaining_upload_size == 0) ||
- ((connection->remaining_upload_size == MHD_SIZE_UNKNOWN) &&
- (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)
- {
- if (connection->state != MHD_CONNECTION_BODY_RECEIVED)
- continue;
- if (connection->read_closed)
- {
- CONNECTION_CLOSE_ERROR (connection,
- NULL);
- continue;
- }
- break;
- }
- if (strlen (line) == 0)
- {
- connection->state = MHD_CONNECTION_FOOTERS_RECEIVED;
- continue;
- }
- if (MHD_NO == process_header_line (connection, line))
- {
- transmit_error_response (connection,
- MHD_HTTP_BAD_REQUEST,
- REQUEST_MALFORMED);
- break;
- }
- connection->state = MHD_CONNECTION_FOOTER_PART_RECEIVED;
- continue;
- case MHD_CONNECTION_FOOTER_PART_RECEIVED:
- line = get_next_header_line (connection);
- if (line == NULL)
- {
- if (connection->state != MHD_CONNECTION_FOOTER_PART_RECEIVED)
- continue;
- if (connection->read_closed)
- {
- CONNECTION_CLOSE_ERROR (connection,
- NULL);
- continue;
- }
- break;
- }
- if (MHD_NO ==
- process_broken_line (connection, line, MHD_FOOTER_KIND))
- continue;
- 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! */
- CONNECTION_CLOSE_ERROR (connection,
- "Closing connection (failed to create
response header)\n");
- continue;
- }
- connection->state = MHD_CONNECTION_HEADERS_SENDING;
-
-#if HAVE_DECL_TCP_CORK
- /* starting header send, set TCP cork */
- {
- const int val = 1;
- setsockopt (connection->socket_fd, IPPROTO_TCP, TCP_CORK, &val,
- sizeof (val));
- }
-#endif
- 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:
-#if HAVE_DECL_TCP_CORK
- /* done sending, uncork */
- {
- const int val = 0;
- setsockopt (connection->socket_fd, IPPROTO_TCP, TCP_CORK, &val,
- sizeof (val));
- }
-#endif
- end =
- MHD_get_response_header (connection->response,
- MHD_HTTP_HEADER_CONNECTION);
- rend = ( (end != NULL) && (0 == strcasecmp (end, "close")) );
- MHD_destroy_response (connection->response);
- connection->response = NULL;
- 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_aware = MHD_NO;
- 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->headers_received = NULL;
- connection->headers_received_tail = 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 ( (rend) || ((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)))
- {
- /* http 1.0, version-less requests cannot be pipelined */
- MHD_connection_close (connection,
MHD_REQUEST_TERMINATED_COMPLETED_OK);
- 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->response != NULL)
- {
- MHD_destroy_response (connection->response);
- connection->response = NULL;
- }
- daemon = connection->daemon;
- if (0 != pthread_mutex_lock(&daemon->cleanup_connection_mutex))
- {
- MHD_PANIC ("Failed to acquire cleanup mutex\n");
- }
- DLL_remove (daemon->connections_head,
- daemon->connections_tail,
- connection);
- DLL_insert (daemon->cleanup_head,
- daemon->cleanup_tail,
- connection);
- if (0 != pthread_mutex_unlock(&daemon->cleanup_connection_mutex))
- {
- MHD_PANIC ("Failed to release cleanup mutex\n");
- }
- return MHD_NO;
- default:
- EXTRA_CHECK (0);
- break;
- }
- break;
- }
- timeout = connection->connection_timeout;
- if ( (timeout != 0) &&
- (timeout <= (MHD_monotonic_time() - connection->last_activity)) )
- {
- MHD_connection_close (connection,
MHD_REQUEST_TERMINATED_TIMEOUT_REACHED);
- return MHD_YES;
- }
- return MHD_YES;
-}
-
-
-/**
- * Set callbacks for this connection to those for HTTP.
- *
- * @param connection connection to initialize
- */
-void
-MHD_set_http_callbacks_ (struct MHD_Connection *connection)
-{
- connection->read_handler = &MHD_connection_handle_read;
- connection->write_handler = &MHD_connection_handle_write;
- connection->idle_handler = &MHD_connection_handle_idle;
-}
-
-
-/**
- * Obtain information about the given connection.
- *
- * @param connection what connection to get information about
- * @param infoType what information is desired?
- * @param ... depends on infoType
- * @return NULL if this information is not available
- * (or if the infoType is unknown)
- */
-const union MHD_ConnectionInfo *
-MHD_get_connection_info (struct MHD_Connection *connection,
- enum MHD_ConnectionInfoType infoType, ...)
-{
- switch (infoType)
- {
-#if HTTPS_SUPPORT
- case MHD_CONNECTION_INFO_CIPHER_ALGO:
- if (connection->tls_session == NULL)
- return NULL;
- connection->cipher = gnutls_cipher_get (connection->tls_session);
- return (const union MHD_ConnectionInfo *) &connection->cipher;
- case MHD_CONNECTION_INFO_PROTOCOL:
- if (connection->tls_session == NULL)
- return NULL;
- connection->protocol = gnutls_protocol_get_version
(connection->tls_session);
- return (const union MHD_ConnectionInfo *) &connection->protocol;
- case MHD_CONNECTION_INFO_GNUTLS_SESSION:
- if (connection->tls_session == NULL)
- return NULL;
- return (const union MHD_ConnectionInfo *) &connection->tls_session;
-#endif
- case MHD_CONNECTION_INFO_CLIENT_ADDRESS:
- return (const union MHD_ConnectionInfo *) &connection->addr;
- case MHD_CONNECTION_INFO_DAEMON:
- return (const union MHD_ConnectionInfo *) &connection->daemon;
- default:
- return NULL;
- };
-}
-
-
-/**
- * Set a custom option for the given connection, overriding defaults.
- *
- * @param connection connection to modify
- * @param option option to set
- * @param ... arguments to the option, depending on the option type
- * @return MHD_YES on success, MHD_NO if setting the option failed
- */
-int
-MHD_set_connection_option (struct MHD_Connection *connection,
- enum MHD_CONNECTION_OPTION option,
- ...)
-{
- va_list ap;
-
- switch (option)
- {
- case MHD_CONNECTION_OPTION_TIMEOUT:
- va_start (ap, option);
- connection->connection_timeout = va_arg (ap, unsigned int);
- va_end (ap);
- return MHD_YES;
- default:
- return MHD_NO;
- }
-}
-
-
-/* end of connection.c */
Copied: libmicrohttpd/src/microhttpd/connection.c (from rev 27024,
libmicrohttpd/src/daemon/connection.c)
===================================================================
--- libmicrohttpd/src/microhttpd/connection.c (rev 0)
+++ libmicrohttpd/src/microhttpd/connection.c 2013-05-05 18:07:33 UTC (rev
27025)
@@ -0,0 +1,2578 @@
+/*
+ This file is part of libmicrohttpd
+ (C) 2007, 2008, 2009, 2010, 2011, 2012 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
+ version 2.1 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
USA
+
+*/
+
+/**
+ * @file connection.c
+ * @brief Methods for managing connections
+ * @author Daniel Pittman
+ * @author Christian Grothoff
+ */
+
+#include "internal.h"
+#include <limits.h>
+#include "connection.h"
+#include "memorypool.h"
+#include "response.h"
+#include "reason_phrase.h"
+
+#if HAVE_NETINET_TCP_H
+/* for TCP_CORK */
+#include <netinet/tcp.h>
+#endif
+
+/**
+ * Minimum size by which MHD tries to increment read/write buffers.
+ * We usually begin with half the available pool space for the
+ * IO-buffer, but if absolutely needed we additively grow by the
+ * number of bytes given here (up to -- theoretically -- the full pool
+ * space).
+ */
+#define MHD_BUF_INC_SIZE 1024
+
+/**
+ * Message to transmit when http 1.1 request is received
+ */
+#define HTTP_100_CONTINUE "HTTP/1.1 100 Continue\r\n\r\n"
+
+/**
+ * Response text used when the request (http header) is too big to
+ * be processed.
+ *
+ * Intentionally empty here to keep our memory footprint
+ * minimal.
+ */
+#if HAVE_MESSAGES
+#define REQUEST_TOO_BIG "<html><head><title>Request too
big</title></head><body>Your HTTP header was too big for the memory constraints
of this webserver.</body></html>"
+#else
+#define REQUEST_TOO_BIG ""
+#endif
+
+/**
+ * Response text used when the request (http header) does not
+ * contain a "Host:" header and still claims to be HTTP 1.1.
+ *
+ * Intentionally empty here to keep our memory footprint
+ * minimal.
+ */
+#if HAVE_MESSAGES
+#define REQUEST_LACKS_HOST "<html><head><title>"Host:" header
required</title></head><body>In HTTP 1.1, requests must include a
"Host:" header, and your HTTP 1.1 request lacked such a
header.</body></html>"
+#else
+#define REQUEST_LACKS_HOST ""
+#endif
+
+/**
+ * Response text used when the request (http header) is
+ * malformed.
+ *
+ * Intentionally empty here to keep our memory footprint
+ * minimal.
+ */
+#if HAVE_MESSAGES
+#define REQUEST_MALFORMED "<html><head><title>Request
malformed</title></head><body>Your HTTP request was syntactically
incorrect.</body></html>"
+#else
+#define REQUEST_MALFORMED ""
+#endif
+
+/**
+ * Response text used when there is an internal server error.
+ *
+ * Intentionally empty here to keep our memory footprint
+ * minimal.
+ */
+#if HAVE_MESSAGES
+#define INTERNAL_ERROR "<html><head><title>Internal server
error</title></head><body>Some programmer needs to study the manual more
carefully.</body></html>"
+#else
+#define INTERNAL_ERROR ""
+#endif
+
+/**
+ * Add extra debug messages with reasons for closing connections
+ * (non-error reasons).
+ */
+#define DEBUG_CLOSE MHD_NO
+
+/**
+ * Should all data send be printed to stderr?
+ */
+#define DEBUG_SEND_DATA MHD_NO
+
+
+/**
+ * Get all of the headers from the request.
+ *
+ * @param connection connection to get values from
+ * @param kind types of values to iterate over
+ * @param iterator callback to call on each header;
+ * maybe NULL (then just count headers)
+ * @param iterator_cls extra argument to iterator
+ * @return number of entries iterated over
+ */
+int
+MHD_get_connection_values (struct MHD_Connection *connection,
+ enum MHD_ValueKind kind,
+ MHD_KeyValueIterator iterator, void *iterator_cls)
+{
+ int ret;
+ struct MHD_HTTP_Header *pos;
+
+ if (NULL == connection)
+ return -1;
+ ret = 0;
+ for (pos = connection->headers_received; NULL != pos; pos = pos->next)
+ if (0 != (pos->kind & kind))
+ {
+ ret++;
+ if ((NULL != iterator) &&
+ (MHD_YES != iterator (iterator_cls,
+ kind, pos->header, pos->value)))
+ return ret;
+ }
+ return ret;
+}
+
+
+/**
+ * This function can be used to append an entry to
+ * the list of HTTP headers of a connection (so that the
+ * MHD_get_connection_values function will return
+ * them -- and the MHD PostProcessor will also
+ * see them). This maybe required in certain
+ * situations (see Mantis #1399) where (broken)
+ * HTTP implementations fail to supply values needed
+ * by the post processor (or other parts of the
+ * application).
+ * <p>
+ * This function MUST only be called from within
+ * the MHD_AccessHandlerCallback (otherwise, access
+ * maybe improperly synchronized). Furthermore,
+ * the client must guarantee that the key and
+ * value arguments are 0-terminated strings that
+ * are NOT freed until the connection is closed.
+ * (The easiest way to do this is by passing only
+ * arguments to permanently allocated strings.).
+ *
+ * @param connection the connection for which a
+ * value should be set
+ * @param kind kind of the value
+ * @param key key for the value
+ * @param value the value itself
+ * @return MHD_NO if the operation could not be
+ * performed due to insufficient memory;
+ * MHD_YES on success
+ */
+int
+MHD_set_connection_value (struct MHD_Connection *connection,
+ enum MHD_ValueKind kind,
+ const char *key, const char *value)
+{
+ struct MHD_HTTP_Header *pos;
+
+ pos = MHD_pool_allocate (connection->pool,
+ sizeof (struct MHD_HTTP_Header), MHD_NO);
+ if (NULL == pos)
+ return MHD_NO;
+ pos->header = (char *) key;
+ pos->value = (char *) value;
+ pos->kind = kind;
+ pos->next = NULL;
+ /* append 'pos' to the linked list of headers */
+ if (NULL == connection->headers_received_tail)
+ {
+ connection->headers_received = pos;
+ connection->headers_received_tail = pos;
+ }
+ else
+ {
+ connection->headers_received_tail->next = pos;
+ connection->headers_received_tail = pos;
+ }
+ return MHD_YES;
+}
+
+
+/**
+ * Get a particular header value. If multiple
+ * values match the kind, return any one of them.
+ *
+ * @param connection connection to get values from
+ * @param kind what kind of value are we looking for
+ * @param key the header to look for, NULL to lookup 'trailing' value without
a key
+ * @return NULL if no such item was found
+ */
+const char *
+MHD_lookup_connection_value (struct MHD_Connection *connection,
+ enum MHD_ValueKind kind, const char *key)
+{
+ struct MHD_HTTP_Header *pos;
+
+ if (NULL == connection)
+ return NULL;
+ for (pos = connection->headers_received; NULL != pos; pos = pos->next)
+ if ((0 != (pos->kind & kind)) &&
+ ( (key == pos->header) ||
+ ( (NULL != pos->header) &&
+ (NULL != key) &&
+ (0 == strcasecmp (key, pos->header))) ))
+ return pos->value;
+ return NULL;
+}
+
+
+/**
+ * Queue a response to be transmitted to the client (as soon as
+ * possible but after MHD_AccessHandlerCallback returns).
+ *
+ * @param connection the connection identifying the client
+ * @param status_code HTTP status code (i.e. 200 for OK)
+ * @param response response to transmit
+ * @return MHD_NO on error (i.e. reply already sent),
+ * MHD_YES on success or if message has been queued
+ */
+int
+MHD_queue_response (struct MHD_Connection *connection,
+ unsigned int status_code, struct MHD_Response *response)
+{
+ if ( (NULL == connection) ||
+ (NULL == response) ||
+ (NULL != connection->response) ||
+ ( (MHD_CONNECTION_HEADERS_PROCESSED != connection->state) &&
+ (MHD_CONNECTION_FOOTERS_RECEIVED != connection->state) ) )
+ return MHD_NO;
+ MHD_increment_response_rc (response);
+ connection->response = response;
+ connection->responseCode = status_code;
+ if ( (NULL != connection->method) &&
+ (0 == strcasecmp (connection->method, MHD_HTTP_METHOD_HEAD)) )
+ {
+ /* if this is a "HEAD" request, pretend that we
+ have already sent the full message body */
+ connection->response_write_position = response->total_size;
+ }
+ if (MHD_CONNECTION_HEADERS_PROCESSED == connection->state)
+ {
+ /* response was queued "early",
+ refuse to read body / footers or further
+ requests! */
+ (void) SHUTDOWN (connection->socket_fd, SHUT_RD);
+ connection->read_closed = MHD_YES;
+ connection->state = MHD_CONNECTION_FOOTERS_RECEIVED;
+ }
+ return MHD_YES;
+}
+
+
+/**
+ * Do we (still) need to send a 100 continue
+ * message for this connection?
+ *
+ * @param connection connection to test
+ * @return 0 if we don't need 100 CONTINUE, 1 if we do
+ */
+static int
+need_100_continue (struct MHD_Connection *connection)
+{
+ const char *expect;
+
+ return ( (NULL == connection->response) &&
+ (NULL != connection->version) &&
+ (0 == strcasecmp (connection->version,
+ MHD_HTTP_VERSION_1_1)) &&
+ (NULL != (expect = MHD_lookup_connection_value (connection,
+ MHD_HEADER_KIND,
+
MHD_HTTP_HEADER_EXPECT))) &&
+ (0 == strcasecmp (expect, "100-continue")) &&
+ (connection->continue_message_write_offset <
+ strlen (HTTP_100_CONTINUE)) );
+}
+
+
+/**
+ * Close the given connection and give the
+ * specified termination code to the user.
+ *
+ * @param connection connection to close
+ * @param termination_code termination reason to give
+ */
+void
+MHD_connection_close (struct MHD_Connection *connection,
+ enum MHD_RequestTerminationCode termination_code)
+{
+ struct MHD_Daemon *daemon;
+
+ daemon = connection->daemon;
+ SHUTDOWN (connection->socket_fd,
+ (connection->read_closed == MHD_YES) ? SHUT_WR : SHUT_RDWR);
+ connection->state = MHD_CONNECTION_CLOSED;
+ if ( (NULL != daemon->notify_completed) &&
+ (MHD_YES == connection->client_aware) )
+ daemon->notify_completed (daemon->notify_completed_cls,
+ connection,
+ &connection->client_context,
+ termination_code);
+ connection->client_aware = MHD_NO;
+}
+
+
+/**
+ * A serious error occured, close the
+ * connection (and notify the application).
+ *
+ * @param connection connection to close with error
+ * @param emsg error message (can be NULL)
+ */
+static void
+connection_close_error (struct MHD_Connection *connection,
+ const char *emsg)
+{
+#if HAVE_MESSAGES
+ if (NULL != emsg)
+ MHD_DLOG (connection->daemon, emsg);
+#endif
+ MHD_connection_close (connection, MHD_REQUEST_TERMINATED_WITH_ERROR);
+}
+
+
+/**
+ * Macro to only include error message in call to
+ * "connection_close_error" if we have HAVE_MESSAGES.
+ */
+#if HAVE_MESSAGES
+#define CONNECTION_CLOSE_ERROR(c, emsg) connection_close_error (c, emsg)
+#else
+#define CONNECTION_CLOSE_ERROR(c, emsg) connection_close_error (c, NULL)
+#endif
+
+
+/**
+ * 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).
+ *
+ * @param connection the connection
+ * @return MHD_NO if readying the response failed
+ */
+static int
+try_ready_normal_body (struct MHD_Connection *connection)
+{
+ ssize_t ret;
+ struct MHD_Response *response;
+
+ response = connection->response;
+ if (NULL == response->crc)
+ return MHD_YES;
+ if (0 == response->total_size)
+ return MHD_YES; /* 0-byte response is always ready */
+ if ( (response->data_start <=
+ connection->response_write_position) &&
+ (response->data_size + response->data_start >
+ connection->response_write_position) )
+ return MHD_YES; /* response already ready */
+#if LINUX
+ if ( (-1 != response->fd) &&
+ (0 == (connection->daemon->options & MHD_USE_SSL)) )
+ {
+ /* will use sendfile, no need to bother response crc */
+ return MHD_YES;
+ }
+#endif
+
+ ret = response->crc (response->crc_cls,
+ connection->response_write_position,
+ response->data,
+ MHD_MIN (response->data_buffer_size,
+ response->total_size -
+ connection->response_write_position));
+ if ((0 == ret) &&
+ (0 != (connection->daemon->options & MHD_USE_SELECT_INTERNALLY)))
+ mhd_panic (mhd_panic_cls, __FILE__, __LINE__
+#if HAVE_MESSAGES
+ , "API violation"
+#else
+ , NULL
+#endif
+ );
+ if ( (MHD_CONTENT_READER_END_OF_STREAM == ret) ||
+ (MHD_CONTENT_READER_END_WITH_ERROR == ret) )
+ {
+ /* either error or http 1.0 transfer, close socket! */
+ response->total_size = connection->response_write_position;
+ CONNECTION_CLOSE_ERROR (connection,
+ (ret == MHD_CONTENT_READER_END_OF_STREAM)
+ ? "Closing connection (end of response)\n"
+ : "Closing connection (stream error)\n");
+ return MHD_NO;
+ }
+ response->data_start = connection->response_write_position;
+ response->data_size = ret;
+ if (0 == ret)
+ {
+ connection->state = MHD_CONNECTION_NORMAL_BODY_UNREADY;
+ 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).
+ *
+ * @param connection the connection
+ * @return MHD_NO if readying the response failed
+ */
+static int
+try_ready_chunked_body (struct MHD_Connection *connection)
+{
+ ssize_t ret;
+ char *buf;
+ struct MHD_Response *response;
+ size_t size;
+ char cbuf[10]; /* 10: max strlen of "%x\r\n" */
+ int cblen;
+
+ response = connection->response;
+ if (0 == connection->write_buffer_size)
+ {
+ size = connection->daemon->pool_size;
+ do
+ {
+ size /= 2;
+ if (size < 128)
+ {
+ /* not enough memory */
+ CONNECTION_CLOSE_ERROR (connection,
+ "Closing connection (out of memory)\n");
+ return MHD_NO;
+ }
+ buf = MHD_pool_allocate (connection->pool, size, MHD_NO);
+ }
+ while (NULL == buf);
+ connection->write_buffer_size = size;
+ connection->write_buffer = buf;
+ }
+
+ if ( (response->data_start <=
+ connection->response_write_position) &&
+ (response->data_size + response->data_start >
+ connection->response_write_position) )
+ {
+ /* buffer already ready, use what is there for the chunk */
+ ret = response->data_size + response->data_start -
connection->response_write_position;
+ if (ret > connection->write_buffer_size - sizeof (cbuf) - 2)
+ ret = connection->write_buffer_size - sizeof (cbuf) - 2;
+ memcpy (&connection->write_buffer[sizeof (cbuf)],
+ &response->data[connection->response_write_position -
response->data_start],
+ ret);
+ }
+ else
+ {
+ /* buffer not in range, try to fill it */
+ if (0 == response->total_size)
+ ret = 0; /* response must be empty, don't bother calling crc */
+ else
+ ret = response->crc (response->crc_cls,
+ connection->response_write_position,
+ &connection->write_buffer[sizeof (cbuf)],
+ connection->write_buffer_size - sizeof (cbuf) - 2);
+ }
+ if (MHD_CONTENT_READER_END_WITH_ERROR == ret)
+ {
+ /* error, close socket! */
+ response->total_size = connection->response_write_position;
+ CONNECTION_CLOSE_ERROR (connection,
+ "Closing connection (error generating
response)\n");
+ return MHD_NO;
+ }
+ if ( (MHD_CONTENT_READER_END_OF_STREAM == ret) ||
+ (0 == response->total_size) )
+ {
+ /* 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 (0 == ret)
+ {
+ connection->state = MHD_CONNECTION_CHUNKED_BODY_UNREADY;
+ return MHD_NO;
+ }
+ if (ret > 0xFFFFFF)
+ ret = 0xFFFFFF;
+ snprintf (cbuf,
+ sizeof (cbuf),
+ "%X\r\n", (unsigned int) ret);
+ cblen = strlen (cbuf);
+ EXTRA_CHECK (cblen <= sizeof (cbuf));
+ memcpy (&connection->write_buffer[sizeof (cbuf) - cblen], cbuf, cblen);
+ memcpy (&connection->write_buffer[sizeof (cbuf) + ret], "\r\n", 2);
+ connection->response_write_position += ret;
+ connection->write_buffer_send_offset = sizeof (cbuf) - cblen;
+ connection->write_buffer_append_offset = sizeof (cbuf) + ret + 2;
+ return MHD_YES;
+}
+
+
+/**
+ * Check if we need to set some additional headers
+ * for http-compiliance.
+ *
+ * @param connection connection to check (and possibly modify)
+ */
+static void
+add_extra_headers (struct MHD_Connection *connection)
+{
+ const char *have_close;
+ const char *client_close;
+ const char *have_encoding;
+ char buf[128];
+ int add_close;
+
+ client_close = MHD_lookup_connection_value (connection,
+ MHD_HEADER_KIND,
+ MHD_HTTP_HEADER_CONNECTION);
+ /* we only care about 'close', everything else is ignored */
+ if ( (NULL != client_close) && (0 != strcasecmp (client_close, "close")) )
+ client_close = NULL;
+ have_close = MHD_get_response_header (connection->response,
+ MHD_HTTP_HEADER_CONNECTION);
+ if ( (NULL != have_close) && (0 != strcasecmp (have_close, "close")) )
+ have_close = NULL;
+ connection->have_chunked_upload = MHD_NO;
+ add_close = MHD_NO;
+ if (MHD_SIZE_UNKNOWN == connection->response->total_size)
+ {
+ /* size is unknown, need to either to HTTP 1.1 chunked encoding or
+ close the connection */
+ if (NULL == have_close)
+ {
+ /* 'close' header doesn't exist yet, see if we need to add one;
+ if the client asked for a close, no need to start chunk'ing */
+ if ((NULL == client_close) &&
+ (NULL != connection->version) &&
+ (0 == strcasecmp (connection->version, MHD_HTTP_VERSION_1_1)))
+ {
+ connection->have_chunked_upload = MHD_YES;
+ have_encoding = MHD_get_response_header (connection->response,
+
MHD_HTTP_HEADER_TRANSFER_ENCODING);
+ if (NULL == have_encoding)
+ MHD_add_response_header (connection->response,
+ MHD_HTTP_HEADER_TRANSFER_ENCODING,
+ "chunked");
+ else if (0 != strcasecmp (have_encoding, "chunked"))
+ add_close = MHD_YES; /* application already set some strange
encoding, can't do 'chunked' */
+ }
+ else
+ {
+ /* HTTP not 1.1 or client asked for close => set close header */
+ add_close = MHD_YES;
+ }
+ }
+ }
+ else
+ {
+ /* check if we should add a 'close' anyway */
+ if ( (NULL != client_close) &&
+ (NULL == have_close) )
+ add_close = MHD_YES; /* client asked for it, so add it */
+
+ /* if not present, add content length */
+ if (NULL == MHD_get_response_header (connection->response,
+ MHD_HTTP_HEADER_CONTENT_LENGTH))
+ {
+ SPRINTF (buf,
+ MHD_UNSIGNED_LONG_LONG_PRINTF,
+ (MHD_UNSIGNED_LONG_LONG) connection->response->total_size);
+ MHD_add_response_header (connection->response,
+ MHD_HTTP_HEADER_CONTENT_LENGTH, buf);
+ }
+ }
+ if (MHD_YES == add_close)
+ MHD_add_response_header (connection->response,
+ MHD_HTTP_HEADER_CONNECTION, "close");
+}
+
+
+/**
+ * Produce HTTP "Date:" header.
+ *
+ * @param date where to write the header, with
+ * at least 128 bytes available space.
+ */
+static void
+get_date_string (char *date)
+{
+ static const char *const days[] =
+ { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" };
+ static const char *const 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);
+ SPRINTF (date,
+ "Date: %3s, %02u %3s %04u %02u:%02u:%02u GMT\r\n",
+ days[now.tm_wday % 7],
+ (unsigned int) now.tm_mday,
+ mons[now.tm_mon % 12],
+ (unsigned int) (1900 + now.tm_year),
+ (unsigned int) now.tm_hour,
+ (unsigned int) now.tm_min,
+ (unsigned int) now.tm_sec);
+}
+
+
+/**
+ * Try growing the read buffer. We initially claim half the
+ * available buffer space for the read buffer (the other half
+ * being left for management data structures; the write
+ * buffer can in the end take virtually everything as the
+ * read buffer can be reduced to the minimum necessary at that
+ * point.
+ *
+ * @param connection the connection
+ * @return MHD_YES on success, MHD_NO on failure
+ */
+static int
+try_grow_read_buffer (struct MHD_Connection *connection)
+{
+ void *buf;
+ size_t new_size;
+
+ if (0 == connection->read_buffer_size)
+ new_size = connection->daemon->pool_size / 2;
+ else
+ new_size = connection->read_buffer_size + MHD_BUF_INC_SIZE;
+ buf = MHD_pool_reallocate (connection->pool,
+ connection->read_buffer,
+ connection->read_buffer_size,
+ new_size);
+ if (NULL == buf)
+ return MHD_NO;
+ /* we can actually grow the buffer, do it! */
+ connection->read_buffer = buf;
+ connection->read_buffer_size = new_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.
+ *
+ * @param connection the connection
+ */
+static int
+build_header_response (struct MHD_Connection *connection)
+{
+ size_t size;
+ size_t off;
+ struct MHD_HTTP_Header *pos;
+ char code[256];
+ char date[128];
+ char *data;
+ enum MHD_ValueKind kind;
+ const char *reason_phrase;
+ uint32_t rc;
+ int must_add_close;
+
+ EXTRA_CHECK (NULL != connection->version);
+ if (0 == strlen(connection->version))
+ {
+ data = MHD_pool_allocate (connection->pool, 0, MHD_YES);
+ connection->write_buffer = data;
+ connection->write_buffer_append_offset = 0;
+ connection->write_buffer_send_offset = 0;
+ connection->write_buffer_size = 0;
+ return MHD_YES;
+ }
+ if (MHD_CONNECTION_FOOTERS_RECEIVED == connection->state)
+ {
+ add_extra_headers (connection);
+ rc = connection->responseCode & (~MHD_ICY_FLAG);
+ reason_phrase = MHD_get_reason_phrase_for (rc);
+ SPRINTF (code,
+ "%s %u %s\r\n",
+ (0 != (connection->responseCode & MHD_ICY_FLAG))
+ ? "ICY"
+ : ( (0 == strcasecmp (MHD_HTTP_VERSION_1_0,
+ connection->version))
+ ? MHD_HTTP_VERSION_1_0
+ : MHD_HTTP_VERSION_1_1),
+ rc,
+ reason_phrase);
+ off = strlen (code);
+ /* estimate size */
+ size = off + 2; /* extra \r\n at the end */
+ kind = MHD_HEADER_KIND;
+ if ( (0 == (connection->daemon->options & MHD_SUPPRESS_DATE_NO_CLOCK))
&&
+ (NULL == MHD_get_response_header (connection->response,
+ MHD_HTTP_HEADER_DATE)) )
+ get_date_string (date);
+ else
+ date[0] = '\0';
+ size += strlen (date);
+ }
+ else
+ {
+ size = 2;
+ kind = MHD_FOOTER_KIND;
+ off = 0;
+ }
+ must_add_close = ( (connection->state == MHD_CONNECTION_FOOTERS_RECEIVED) &&
+ (connection->read_closed == MHD_YES) &&
+ (0 == strcasecmp (connection->version,
+ MHD_HTTP_VERSION_1_1)) &&
+ (NULL == MHD_get_response_header (connection->response,
+
MHD_HTTP_HEADER_CONNECTION)) );
+ if (must_add_close)
+ size += strlen ("Connection: close\r\n");
+ for (pos = connection->response->first_header; NULL != pos; pos = pos->next)
+ if (pos->kind == kind)
+ size += strlen (pos->header) + strlen (pos->value) + 4; /* colon, space,
linefeeds */
+ /* produce data */
+ data = MHD_pool_allocate (connection->pool, size + 1, MHD_YES);
+ if (NULL == data)
+ {
+#if HAVE_MESSAGES
+ MHD_DLOG (connection->daemon, "Not enough memory for write!\n");
+#endif
+ return MHD_NO;
+ }
+ if (MHD_CONNECTION_FOOTERS_RECEIVED == connection->state)
+ {
+ memcpy (data, code, off);
+ }
+ if (must_add_close)
+ {
+ /* we must add the 'close' header because circumstances forced us to
+ stop reading from the socket; however, we are not adding the header
+ to the response as the response may be used in a different context
+ as well */
+ memcpy (&data[off], "Connection: close\r\n",
+ strlen ("Connection: close\r\n"));
+ off += strlen ("Connection: close\r\n");
+ }
+ for (pos = connection->response->first_header; NULL != pos; pos = pos->next)
+ if (pos->kind == kind)
+ off += SPRINTF (&data[off],
+ "%s: %s\r\n",
+ pos->header,
+ pos->value);
+ if (connection->state == MHD_CONNECTION_FOOTERS_RECEIVED)
+ {
+ strcpy (&data[off], date);
+ off += strlen (date);
+ }
+ memcpy (&data[off], "\r\n", 2);
+ off += 2;
+
+ if (off != size)
+ mhd_panic (mhd_panic_cls, __FILE__, __LINE__, NULL);
+ 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;
+}
+
+
+/**
+ * We encountered an error processing the request.
+ * Handle it properly by stopping to read data
+ * and sending the indicated response code and message.
+ *
+ * @param connection the connection
+ * @param status_code the response code to send (400, 413 or 414)
+ * @param message the error message to send
+ */
+static void
+transmit_error_response (struct MHD_Connection *connection,
+ unsigned int status_code,
+ const char *message)
+{
+ struct MHD_Response *response;
+
+ if (NULL == connection->version)
+ {
+ /* we were unable to process the full header line, so we don't
+ really know what version the client speaks; assume 1.0 */
+ connection->version = MHD_HTTP_VERSION_1_0;
+ }
+ connection->state = MHD_CONNECTION_FOOTERS_RECEIVED;
+ connection->read_closed = MHD_YES;
+#if HAVE_MESSAGES
+ MHD_DLOG (connection->daemon,
+ "Error %u (`%s') processing request, closing connection.\n",
+ status_code, message);
+#endif
+ EXTRA_CHECK (connection->response == NULL);
+ response = MHD_create_response_from_buffer (strlen (message),
+ (void *) message,
+ MHD_RESPMEM_PERSISTENT);
+ 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! */
+ CONNECTION_CLOSE_ERROR (connection,
+ "Closing connection (failed to create response
header)\n");
+ }
+ else
+ {
+ connection->state = MHD_CONNECTION_HEADERS_SENDING;
+ }
+}
+
+
+/**
+ * Add "fd" to the "fd_set". If "fd" is
+ * greater than "*max", set "*max" to fd.
+ *
+ * @param fd file descriptor to add to the set
+ * @param set set to modify
+ * @param max_fd maximum value to potentially update
+ */
+static void
+add_to_fd_set (int fd,
+ fd_set *set,
+ int *max_fd)
+{
+ FD_SET (fd, set);
+ if ( (NULL != max_fd) &&
+ (fd > *max_fd) )
+ *max_fd = fd;
+}
+
+
+/**
+ * Obtain the select sets for this connection. The given
+ * sets (and the maximum) are updated and must have
+ * already been initialized.
+ *
+ * @param connection connetion to get select sets for
+ * @param read_fd_set read set to initialize
+ * @param write_fd_set write set to initialize
+ * @param except_fd_set except set to initialize (never changed)
+ * @param max_fd where to store largest FD put into any set
+ * @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 ret;
+ struct MHD_Pollfd p;
+
+ /* we use the 'poll fd' as a convenient way to re-use code
+ when determining the select sets */
+ memset (&p, 0, sizeof(struct MHD_Pollfd));
+ ret = MHD_connection_get_pollfd (connection, &p);
+ if ( (MHD_YES == ret) && (p.fd >= 0) ) {
+ if (0 != (p.events & MHD_POLL_ACTION_IN))
+ add_to_fd_set(p.fd, read_fd_set, max_fd);
+ if (0 != (p.events & MHD_POLL_ACTION_OUT))
+ add_to_fd_set(p.fd, write_fd_set, max_fd);
+ }
+ return ret;
+}
+
+
+/**
+ * Obtain the pollfd for this connection
+ *
+ * @param connection connetion to get poll set for
+ * @param p where to store the polling information
+ * @return MHD_YES on success. If return MHD_YES and p->fd < 0, this
+ * connection is not waiting for any read or write events
+ */
+int
+MHD_connection_get_pollfd (struct MHD_Connection *connection,
+ struct MHD_Pollfd *p)
+{
+ int fd;
+
+ if (NULL == connection->pool)
+ connection->pool = MHD_pool_create (connection->daemon->pool_size);
+ if (NULL == connection->pool)
+ {
+ CONNECTION_CLOSE_ERROR (connection,
+ "Failed to create memory pool!\n");
+ return MHD_YES;
+ }
+ fd = connection->socket_fd;
+ p->fd = fd;
+ if (-1 == fd)
+ return MHD_YES;
+ while (1)
+ {
+#if DEBUG_STATES
+ MHD_DLOG (connection->daemon, "%s: state: %s\n",
+ __FUNCTION__, MHD_state_to_string (connection->state));
+#endif
+ switch (connection->state)
+ {
+#if HTTPS_SUPPORT
+ case MHD_TLS_CONNECTION_INIT:
+ if (0 == gnutls_record_get_direction (connection->tls_session))
+ p->events |= MHD_POLL_ACTION_IN;
+ else
+ p->events |= MHD_POLL_ACTION_OUT;
+ break;
+#endif
+ 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) &&
+ (0 == connection->read_buffer_offset))
+ {
+ CONNECTION_CLOSE_ERROR (connection,
+ "Connection buffer to small for
request\n");
+ continue;
+ }
+ if ((connection->read_buffer_offset == connection->read_buffer_size)
+ && (MHD_NO == try_grow_read_buffer (connection)))
+ {
+ transmit_error_response (connection,
+ (connection->url != NULL)
+ ? MHD_HTTP_REQUEST_ENTITY_TOO_LARGE
+ : MHD_HTTP_REQUEST_URI_TOO_LONG,
+ REQUEST_TOO_BIG);
+ continue;
+ }
+ if (MHD_NO == connection->read_closed)
+ p->events |= MHD_POLL_ACTION_IN;
+ 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:
+ p->events |= MHD_POLL_ACTION_OUT;
+ break;
+ case MHD_CONNECTION_CONTINUE_SENT:
+ if (connection->read_buffer_offset == connection->read_buffer_size)
+ {
+ if ((MHD_YES != try_grow_read_buffer (connection)) &&
+ (0 != (connection->daemon->options &
+ (MHD_USE_SELECT_INTERNALLY |
+ MHD_USE_THREAD_PER_CONNECTION))))
+ {
+ /* failed to grow the read buffer, and the
+ client which is supposed to handle the
+ received data in a *blocking* fashion
+ (in this mode) did not handle the data as
+ it was supposed to!
+ => we would either have to do busy-waiting
+ (on the client, which would likely fail),
+ or if we do nothing, we would just timeout
+ on the connection (if a timeout is even
+ set!).
+ Solution: we kill the connection with an error */
+ transmit_error_response (connection,
+ MHD_HTTP_INTERNAL_SERVER_ERROR,
+ INTERNAL_ERROR);
+ continue;
+ }
+ }
+ if ((connection->read_buffer_offset < connection->read_buffer_size)
+ && (MHD_NO == connection->read_closed))
+ p->events |= MHD_POLL_ACTION_IN;
+ 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_CLOSE_ERROR (connection,
+ NULL);
+ continue;
+ }
+ p->events |= MHD_POLL_ACTION_IN;
+ /* 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 */
+ p->events |= MHD_POLL_ACTION_OUT;
+ break;
+ case MHD_CONNECTION_HEADERS_SENT:
+ EXTRA_CHECK (0);
+ break;
+ case MHD_CONNECTION_NORMAL_BODY_READY:
+ p->events |= MHD_POLL_ACTION_OUT;
+ break;
+ case MHD_CONNECTION_NORMAL_BODY_UNREADY:
+ /* not ready, no socket action */
+ break;
+ case MHD_CONNECTION_CHUNKED_BODY_READY:
+ p->events |= MHD_POLL_ACTION_OUT;
+ 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:
+ p->events |= MHD_POLL_ACTION_OUT;
+ break;
+ case MHD_CONNECTION_FOOTERS_SENT:
+ EXTRA_CHECK (0);
+ break;
+ case MHD_CONNECTION_CLOSED:
+ 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
+ * far too long, close the connection. If no line is
+ * found (incomplete, buffer too small, line too long),
+ * return NULL. Otherwise return a pointer to the line.
+ */
+static char *
+get_next_header_line (struct MHD_Connection *connection)
+{
+ char *rbuf;
+ size_t pos;
+
+ if (0 == connection->read_buffer_offset)
+ return NULL;
+ pos = 0;
+ rbuf = connection->read_buffer;
+ while ((pos < connection->read_buffer_offset - 1) &&
+ ('\r' != rbuf[pos]) && ('\n' != rbuf[pos]))
+ pos++;
+ if (pos == connection->read_buffer_offset - 1)
+ {
+ /* not found, consider growing... */
+ if ( (connection->read_buffer_offset == connection->read_buffer_size) &&
+ (MHD_NO ==
+ try_grow_read_buffer (connection)) )
+ {
+ transmit_error_response (connection,
+ (NULL != connection->url)
+ ? MHD_HTTP_REQUEST_ENTITY_TOO_LARGE
+ : MHD_HTTP_REQUEST_URI_TOO_LONG,
+ REQUEST_TOO_BIG);
+ }
+ return NULL;
+ }
+ /* found, check if we have proper LFCR */
+ if (('\r' == rbuf[pos]) && ('\n' == rbuf[pos + 1]))
+ rbuf[pos++] = '\0'; /* skip both r and n */
+ rbuf[pos++] = '\0';
+ connection->read_buffer += pos;
+ connection->read_buffer_size -= pos;
+ connection->read_buffer_offset -= pos;
+ return rbuf;
+}
+
+
+/**
+ * Add an entry to the HTTP headers of a connection. If this fails,
+ * transmit an error response (request too big).
+ *
+ * @param connection the connection for which a
+ * value should be set
+ * @param kind kind of the value
+ * @param key key for the value
+ * @param value the value itself
+ * @return MHD_NO on failure (out of memory), MHD_YES for success
+ */
+static int
+connection_add_header (struct MHD_Connection *connection,
+ char *key, char *value, enum MHD_ValueKind kind)
+{
+ if (MHD_NO == MHD_set_connection_value (connection,
+ kind,
+ key, value))
+ {
+#if HAVE_MESSAGES
+ MHD_DLOG (connection->daemon,
+ "Not enough memory to allocate header record!\n");
+#endif
+ transmit_error_response (connection, MHD_HTTP_REQUEST_ENTITY_TOO_LARGE,
+ REQUEST_TOO_BIG);
+ return MHD_NO;
+ }
+ return MHD_YES;
+}
+
+
+/**
+ * Parse and unescape the arguments given by the client as part
+ * of the HTTP request URI.
+ *
+ * @param kind header kind to use for adding to the connection
+ * @param connection connection to add headers to
+ * @param args argument URI string (after "?" in URI)
+ * @return MHD_NO on failure (out of memory), MHD_YES for success
+ */
+static int
+parse_arguments (enum MHD_ValueKind kind,
+ struct MHD_Connection *connection,
+ char *args)
+{
+ char *equals;
+ char *amper;
+
+ while (NULL != args)
+ {
+ equals = strchr (args, '=');
+ amper = strchr (args, '&');
+ if (NULL == amper)
+ {
+ /* last argument */
+ if (NULL == equals)
+ {
+ /* got 'foo', add key 'foo' with NULL for value */
+ connection->daemon->unescape_callback
(connection->daemon->unescape_callback_cls,
+ connection,
+ args);
+ return connection_add_header (connection,
+ args,
+ NULL,
+ kind);
+ }
+ /* got 'foo=bar' */
+ equals[0] = '\0';
+ equals++;
+ connection->daemon->unescape_callback
(connection->daemon->unescape_callback_cls,
+ connection,
+ args);
+ connection->daemon->unescape_callback
(connection->daemon->unescape_callback_cls,
+ connection,
+ equals);
+ return connection_add_header (connection, args, equals, kind);
+ }
+ /* amper is non-NULL here */
+ amper[0] = '\0';
+ amper++;
+ if ( (NULL == equals) ||
+ (equals >= amper) )
+ {
+ /* got 'foo&bar' or 'foo&bar=val', add key 'foo' with NULL for value
*/
+ connection->daemon->unescape_callback
(connection->daemon->unescape_callback_cls,
+ connection,
+ args);
+ if (MHD_NO ==
+ connection_add_header (connection,
+ args,
+ NULL,
+ kind))
+ return MHD_NO;
+ /* continue with 'bar' */
+ args = amper;
+ continue;
+
+ }
+ /* equals and amper are non-NULL here, and equals < amper,
+ so we got regular 'foo=value&bar...'-kind of argument */
+ equals[0] = '\0';
+ equals++;
+ connection->daemon->unescape_callback
(connection->daemon->unescape_callback_cls,
+ connection,
+ args);
+ connection->daemon->unescape_callback
(connection->daemon->unescape_callback_cls,
+ connection,
+ equals);
+ if (MHD_NO == connection_add_header (connection, args, equals, kind))
+ return MHD_NO;
+ args = amper;
+ }
+ return MHD_YES;
+}
+
+
+/**
+ * Parse the cookie header (see RFC 2109).
+ *
+ * @return MHD_YES for success, MHD_NO for failure (malformed, out of memory)
+ */
+static int
+parse_cookie_header (struct MHD_Connection *connection)
+{
+ const char *hdr;
+ char *cpy;
+ char *pos;
+ char *sce;
+ char *semicolon;
+ char *equals;
+ char *ekill;
+ char old;
+ int quotes;
+
+ hdr = MHD_lookup_connection_value (connection,
+ MHD_HEADER_KIND,
+ MHD_HTTP_HEADER_COOKIE);
+ if (hdr == NULL)
+ return MHD_YES;
+ cpy = MHD_pool_allocate (connection->pool, strlen (hdr) + 1, MHD_YES);
+ if (cpy == NULL)
+ {
+#if HAVE_MESSAGES
+ MHD_DLOG (connection->daemon, "Not enough memory to parse cookies!\n");
+#endif
+ transmit_error_response (connection, MHD_HTTP_REQUEST_ENTITY_TOO_LARGE,
+ REQUEST_TOO_BIG);
+ return MHD_NO;
+ }
+ memcpy (cpy, hdr, strlen (hdr) + 1);
+ pos = cpy;
+ while (pos != NULL)
+ {
+ while (*pos == ' ')
+ pos++; /* skip spaces */
+
+ sce = pos;
+ while (((*sce) != '\0') &&
+ ((*sce) != ',') && ((*sce) != ';') && ((*sce) != '='))
+ sce++;
+ /* remove tailing whitespace (if any) from key */
+ ekill = sce - 1;
+ while ((*ekill == ' ') && (ekill >= pos))
+ *(ekill--) = '\0';
+ old = *sce;
+ *sce = '\0';
+ if (old != '=')
+ {
+ /* value part omitted, use empty string... */
+ if (MHD_NO ==
+ connection_add_header (connection, pos, "", MHD_COOKIE_KIND))
+ return MHD_NO;
+ if (old == '\0')
+ break;
+ pos = sce + 1;
+ continue;
+ }
+ equals = sce + 1;
+ quotes = 0;
+ semicolon = equals;
+ while ((semicolon[0] != '\0') &&
+ ((quotes != 0) ||
+ ((semicolon[0] != ';') && (semicolon[0] != ','))))
+ {
+ if (semicolon[0] == '"')
+ quotes = (quotes + 1) & 1;
+ semicolon++;
+ }
+ if (semicolon[0] == '\0')
+ semicolon = NULL;
+ if (semicolon != NULL)
+ {
+ semicolon[0] = '\0';
+ semicolon++;
+ }
+ /* remove quotes */
+ if ((equals[0] == '"') && (equals[strlen (equals) - 1] == '"'))
+ {
+ equals[strlen (equals) - 1] = '\0';
+ equals++;
+ }
+ if (MHD_NO == connection_add_header (connection,
+ pos, equals, MHD_COOKIE_KIND))
+ return MHD_NO;
+ pos = semicolon;
+ }
+ return MHD_YES;
+}
+
+
+/**
+ * Parse the first line of the HTTP HEADER.
+ *
+ * @param connection the connection (updated)
+ * @param line the first line
+ * @return MHD_YES if the line is ok, MHD_NO if it is malformed
+ */
+static int
+parse_initial_message_line (struct MHD_Connection *connection, char *line)
+{
+ char *uri;
+ char *httpVersion;
+ char *args;
+
+ if (NULL == (uri = strchr (line, ' ')))
+ return MHD_NO; /* serious error */
+ uri[0] = '\0';
+ connection->method = line;
+ uri++;
+ while (uri[0] == ' ')
+ uri++;
+ httpVersion = strchr (uri, ' ');
+ if (httpVersion != NULL)
+ {
+ httpVersion[0] = '\0';
+ httpVersion++;
+ }
+ if (connection->daemon->uri_log_callback != NULL)
+ connection->client_context
+ =
+ connection->daemon->uri_log_callback (connection->daemon->
+ uri_log_callback_cls, uri);
+ args = strchr (uri, '?');
+ if (NULL != args)
+ {
+ args[0] = '\0';
+ args++;
+ parse_arguments (MHD_GET_ARGUMENT_KIND, connection, args);
+ }
+ connection->daemon->unescape_callback
(connection->daemon->unescape_callback_cls,
+ connection,
+ uri);
+ connection->url = uri;
+ if (NULL == httpVersion)
+ connection->version = "";
+ else
+ connection->version = httpVersion;
+ return MHD_YES;
+}
+
+
+/**
+ * Call the handler of the application for this
+ * connection. Handles chunking of the upload
+ * as well as normal uploads.
+ */
+static void
+call_connection_handler (struct MHD_Connection *connection)
+{
+ size_t processed;
+
+ if (NULL != connection->response)
+ return; /* already queued a response */
+ processed = 0;
+ connection->client_aware = MHD_YES;
+ if (MHD_NO ==
+ connection->daemon->default_handler (connection->daemon->
+ default_handler_cls,
+ connection, connection->url,
+ connection->method,
+ connection->version,
+ NULL, &processed,
+ &connection->client_context))
+ {
+ /* serious internal error, close connection */
+ CONNECTION_CLOSE_ERROR (connection,
+ "Internal application error, closing
connection.\n");
+ return;
+ }
+}
+
+
+
+/**
+ * Call the handler of the application for this
+ * connection. Handles chunking of the upload
+ * as well as normal uploads.
+ */
+static void
+process_request_body (struct MHD_Connection *connection)
+{
+ size_t processed;
+ size_t available;
+ size_t used;
+ size_t i;
+ int instant_retry;
+ int malformed;
+ char *buffer_head;
+ char *end;
+
+ if (NULL != connection->response)
+ return; /* already queued a response */
+
+ buffer_head = connection->read_buffer;
+ available = connection->read_buffer_offset;
+ do
+ {
+ instant_retry = MHD_NO;
+ if ((connection->have_chunked_upload == MHD_YES) &&
+ (connection->remaining_upload_size == MHD_SIZE_UNKNOWN))
+ {
+ if ((connection->current_chunk_offset ==
+ connection->current_chunk_size)
+ && (connection->current_chunk_offset != 0) && (available >= 2))
+ {
+ /* skip new line at the *end* of a chunk */
+ i = 0;
+ if ((buffer_head[i] == '\r') || (buffer_head[i] == '\n'))
+ i++; /* skip 1st part of line feed */
+ if ((buffer_head[i] == '\r') || (buffer_head[i] == '\n'))
+ i++; /* skip 2nd part of line feed */
+ if (i == 0)
+ {
+ /* malformed encoding */
+ CONNECTION_CLOSE_ERROR (connection,
+ "Received malformed HTTP request (bad
chunked encoding), closing connection.\n");
+ return;
+ }
+ available -= i;
+ buffer_head += i;
+ connection->current_chunk_offset = 0;
+ connection->current_chunk_size = 0;
+ }
+ if (connection->current_chunk_offset <
+ connection->current_chunk_size)
+ {
+ /* we are in the middle of a chunk, give
+ as much as possible to the client (without
+ crossing chunk boundaries) */
+ processed =
+ connection->current_chunk_size -
+ connection->current_chunk_offset;
+ if (processed > available)
+ processed = available;
+ if (available > processed)
+ instant_retry = MHD_YES;
+ }
+ else
+ {
+ /* we need to read chunk boundaries */
+ i = 0;
+ while (i < available)
+ {
+ if ((buffer_head[i] == '\r') || (buffer_head[i] == '\n'))
+ break;
+ i++;
+ if (i >= 6)
+ break;
+ }
+ /* take '\n' into account; if '\n'
+ is the unavailable character, we
+ will need to wait until we have it
+ before going further */
+ if ((i + 1 >= available) &&
+ !((i == 1) && (available == 2) && (buffer_head[0] == '0')))
+ break; /* need more data... */
+ malformed = (i >= 6);
+ if (!malformed)
+ {
+ buffer_head[i] = '\0';
+ connection->current_chunk_size = strtoul (buffer_head, &end,
16);
+ malformed = ('\0' != *end);
+ }
+ if (malformed)
+ {
+ /* malformed encoding */
+ CONNECTION_CLOSE_ERROR (connection,
+ "Received malformed HTTP request (bad
chunked encoding), closing connection.\n");
+ return;
+ }
+ i++;
+ if ((i < available) &&
+ ((buffer_head[i] == '\r') || (buffer_head[i] == '\n')))
+ i++; /* skip 2nd part of line feed */
+
+ buffer_head += i;
+ available -= i;
+ connection->current_chunk_offset = 0;
+
+ if (available > 0)
+ instant_retry = MHD_YES;
+ if (connection->current_chunk_size == 0)
+ {
+ connection->remaining_upload_size = 0;
+ break;
+ }
+ continue;
+ }
+ }
+ else
+ {
+ /* no chunked encoding, give all to the client */
+ if ( (0 != connection->remaining_upload_size) &&
+ (MHD_SIZE_UNKNOWN != connection->remaining_upload_size) &&
+ (connection->remaining_upload_size < available) )
+ {
+ processed = connection->remaining_upload_size;
+ }
+ else
+ {
+ /**
+ * 1. no chunked encoding, give all to the client
+ * 2. client may send large chunked data, but only a smaller
part is available at one time.
+ */
+ processed = available;
+ }
+ }
+ used = processed;
+ connection->client_aware = MHD_YES;
+ if (MHD_NO ==
+ connection->daemon->default_handler (connection->daemon->
+ default_handler_cls,
+ connection, connection->url,
+ connection->method,
+ connection->version,
+ buffer_head, &processed,
+ &connection->client_context))
+ {
+ /* serious internal error, close connection */
+ CONNECTION_CLOSE_ERROR (connection,
+ "Internal application error, closing
connection.\n");
+ return;
+ }
+ if (processed > used)
+ mhd_panic (mhd_panic_cls, __FILE__, __LINE__
+#if HAVE_MESSAGES
+ , "API violation"
+#else
+ , NULL
+#endif
+ );
+ if (processed != 0)
+ instant_retry = MHD_NO; /* client did not process everything */
+ used -= processed;
+ if (connection->have_chunked_upload == MHD_YES)
+ connection->current_chunk_offset += used;
+ /* dh left "processed" bytes in buffer for next time... */
+ buffer_head += used;
+ available -= used;
+ if (connection->remaining_upload_size != MHD_SIZE_UNKNOWN)
+ connection->remaining_upload_size -= used;
+ }
+ while (MHD_YES == instant_retry);
+ if (available > 0)
+ memmove (connection->read_buffer, buffer_head, available);
+ connection->read_buffer_offset = available;
+}
+
+
+/**
+ * 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
+ */
+static int
+do_read (struct MHD_Connection *connection)
+{
+ int bytes_read;
+
+ if (connection->read_buffer_size == connection->read_buffer_offset)
+ return MHD_NO;
+
+ bytes_read = connection->recv_cls (connection,
+ &connection->read_buffer
+ [connection->read_buffer_offset],
+ connection->read_buffer_size -
+ connection->read_buffer_offset);
+ if (bytes_read < 0)
+ {
+ if ((EINTR == errno) || (EAGAIN == errno))
+ return MHD_NO;
+#if HAVE_MESSAGES
+#if HTTPS_SUPPORT
+ if (0 != (connection->daemon->options & MHD_USE_SSL))
+ MHD_DLOG (connection->daemon,
+ "Failed to receive data: %s\n",
+ gnutls_strerror (bytes_read));
+ else
+#endif
+ MHD_DLOG (connection->daemon,
+ "Failed to receive data: %s\n", STRERROR (errno));
+#endif
+ CONNECTION_CLOSE_ERROR (connection, NULL);
+ return MHD_YES;
+ }
+ if (0 == bytes_read)
+ {
+ /* other side closed connection */
+ connection->read_closed = MHD_YES;
+ SHUTDOWN (connection->socket_fd, SHUT_RD);
+ return MHD_YES;
+ }
+ connection->read_buffer_offset += bytes_read;
+ 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
+do_write (struct MHD_Connection *connection)
+{
+ int ret;
+
+ ret = connection->send_cls (connection,
+ &connection->write_buffer
+ [connection->write_buffer_send_offset],
+ connection->write_buffer_append_offset
+ - connection->write_buffer_send_offset);
+
+ if (ret < 0)
+ {
+ if ((EINTR == errno) || (EAGAIN == errno))
+ return MHD_NO;
+#if HAVE_MESSAGES
+#if HTTPS_SUPPORT
+ if (0 != (connection->daemon->options & MHD_USE_SSL))
+ MHD_DLOG (connection->daemon,
+ "Failed to send data: %s\n",
+ gnutls_strerror (ret));
+ else
+#endif
+ MHD_DLOG (connection->daemon,
+ "Failed to send data: %s\n", STRERROR (errno));
+#endif
+ CONNECTION_CLOSE_ERROR (connection, NULL);
+ return MHD_YES;
+ }
+#if DEBUG_SEND_DATA
+ FPRINTF (stderr,
+ "Sent 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".
+ *
+ * @param connection connection to check write status for
+ * @param next_state the next state to transition to
+ * @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->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;
+}
+
+
+/**
+ * We have received (possibly the beginning of) a line in the
+ * header (or footer). Validate (check for ":") and prepare
+ * to process.
+ */
+static int
+process_header_line (struct MHD_Connection *connection, char *line)
+{
+ char *colon;
+
+ /* line should be normal header line, find colon */
+ colon = strchr (line, ':');
+ if (colon == NULL)
+ {
+ /* error in header line, die hard */
+ CONNECTION_CLOSE_ERROR (connection,
+ "Received malformed line (no colon), closing
connection.\n");
+ return MHD_NO;
+ }
+ /* 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;
+ return MHD_YES;
+}
+
+
+/**
+ * Process a header value that spans multiple lines.
+ * The previous line(s) are in connection->last.
+ *
+ * @param connection connection we're processing
+ * @param line the current input line
+ * @param kind if the line is complete, add a header
+ * of the given kind
+ * @return MHD_YES if the line was processed successfully
+ */
+static int
+process_broken_line (struct MHD_Connection *connection,
+ char *line, enum MHD_ValueKind kind)
+{
+ char *last;
+ char *tmp;
+ size_t last_len;
+ size_t tmp_len;
+
+ last = connection->last;
+ if ((line[0] == ' ') || (line[0] == '\t'))
+ {
+ /* value was continued on the next line, see
+ http://www.jmarshall.com/easy/http/ */
+ last_len = strlen (last);
+ /* skip whitespace at start of 2nd line */
+ tmp = line;
+ while ((tmp[0] == ' ') || (tmp[0] == '\t'))
+ tmp++;
+ tmp_len = strlen (tmp);
+ last = MHD_pool_reallocate (connection->pool,
+ last,
+ last_len + 1,
+ last_len + tmp_len + 1);
+ if (last == NULL)
+ {
+ transmit_error_response (connection,
+ MHD_HTTP_REQUEST_ENTITY_TOO_LARGE,
+ REQUEST_TOO_BIG);
+ return MHD_NO;
+ }
+ memcpy (&last[last_len], tmp, tmp_len + 1);
+ connection->last = last;
+ return MHD_YES; /* possibly more than 2 lines... */
+ }
+ EXTRA_CHECK ((last != NULL) && (connection->colon != NULL));
+ if ((MHD_NO == connection_add_header (connection,
+ last, connection->colon, kind)))
+ {
+ transmit_error_response (connection, MHD_HTTP_REQUEST_ENTITY_TOO_LARGE,
+ REQUEST_TOO_BIG);
+ return MHD_NO;
+ }
+ /* we still have the current line to deal with... */
+ if (strlen (line) != 0)
+ {
+ if (MHD_NO == process_header_line (connection, line))
+ {
+ transmit_error_response (connection,
+ MHD_HTTP_BAD_REQUEST, REQUEST_MALFORMED);
+ return MHD_NO;
+ }
+ }
+ return MHD_YES;
+}
+
+
+/**
+ * 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
+parse_connection_headers (struct MHD_Connection *connection)
+{
+ const char *clen;
+ MHD_UNSIGNED_LONG_LONG cval;
+ struct MHD_Response *response;
+ const char *enc;
+ char *end;
+
+ 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
+ EXTRA_CHECK (connection->response == NULL);
+ response =
+ MHD_create_response_from_buffer (strlen (REQUEST_LACKS_HOST),
+ REQUEST_LACKS_HOST,
+ MHD_RESPMEM_PERSISTENT);
+ MHD_queue_response (connection, MHD_HTTP_BAD_REQUEST, response);
+ MHD_destroy_response (response);
+ return;
+ }
+
+ connection->remaining_upload_size = 0;
+ enc = MHD_lookup_connection_value (connection,
+ MHD_HEADER_KIND,
+ MHD_HTTP_HEADER_TRANSFER_ENCODING);
+ if (enc != NULL)
+ {
+ connection->remaining_upload_size = MHD_SIZE_UNKNOWN;
+ if (0 == strcasecmp (enc, "chunked"))
+ connection->have_chunked_upload = MHD_YES;
+ }
+ else
+ {
+ clen = MHD_lookup_connection_value (connection,
+ MHD_HEADER_KIND,
+ MHD_HTTP_HEADER_CONTENT_LENGTH);
+ if (clen != NULL)
+ {
+ cval = strtoul (clen, &end, 10);
+ if ( ('\0' != *end) ||
+ ( (LONG_MAX == cval) && (errno == ERANGE) ) )
+ {
+#if HAVE_MESSAGES
+ MHD_DLOG (connection->daemon,
+ "Failed to parse `%s' header `%s', closing connection.\n",
+ MHD_HTTP_HEADER_CONTENT_LENGTH, clen);
+#endif
+ CONNECTION_CLOSE_ERROR (connection, NULL);
+ return;
+ }
+ connection->remaining_upload_size = cval;
+ }
+ }
+}
+
+
+/**
+ * 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.
+ *
+ * @param connection connection to handle
+ * @return always MHD_YES (we should continue to process the
+ * connection)
+ */
+int
+MHD_connection_handle_read (struct MHD_Connection *connection)
+{
+ connection->last_activity = MHD_monotonic_time();
+ if (connection->state == MHD_CONNECTION_CLOSED)
+ return MHD_YES;
+ /* make sure "read" has a reasonable number of bytes
+ in buffer to use per system call (if possible) */
+ if (connection->read_buffer_offset + MHD_BUF_INC_SIZE >
+ connection->read_buffer_size)
+ try_grow_read_buffer (connection);
+ if (MHD_NO == do_read (connection))
+ return MHD_YES;
+ while (1)
+ {
+#if DEBUG_STATES
+ MHD_DLOG (connection->daemon, "%s: state: %s\n",
+ __FUNCTION__, MHD_state_to_string (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)
+ {
+ MHD_connection_close (connection,
+ MHD_REQUEST_TERMINATED_READ_ERROR);
+ continue;
+ }
+ break;
+ case MHD_CONNECTION_CLOSED:
+ return MHD_YES;
+ 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;
+}
+
+
+/**
+ * This function was created to handle writes to sockets when it has
+ * been determined that the socket can be written to. All
+ * implementations (multithreaded, external select, internal select)
+ * call this function
+ *
+ * @param connection connection to handle
+ * @return always MHD_YES (we should continue to process the
+ * connection)
+ */
+int
+MHD_connection_handle_write (struct MHD_Connection *connection)
+{
+ struct MHD_Response *response;
+ int ret;
+ connection->last_activity = MHD_monotonic_time();
+ while (1)
+ {
+#if DEBUG_STATES
+ MHD_DLOG (connection->daemon, "%s: state: %s\n",
+ __FUNCTION__, MHD_state_to_string (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 = connection->send_cls (connection,
+ &HTTP_100_CONTINUE
+
[connection->continue_message_write_offset],
+ strlen (HTTP_100_CONTINUE) -
+
connection->continue_message_write_offset);
+ if (ret < 0)
+ {
+ if ((errno == EINTR) || (errno == EAGAIN))
+ break;
+#if HAVE_MESSAGES
+ MHD_DLOG (connection->daemon,
+ "Failed to send data: %s\n", STRERROR (errno));
+#endif
+ CONNECTION_CLOSE_ERROR (connection, NULL);
+ return MHD_YES;
+ }
+#if DEBUG_SEND_DATA
+ FPRINTF (stderr,
+ "Sent 100 continue response: `%.*s'\n",
+ ret,
+ &HTTP_100_CONTINUE
+ [connection->continue_message_write_offset]);
+#endif
+ 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);
+ if (connection->state != MHD_CONNECTION_HEADERS_SENDING)
+ break;
+ 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);
+ break;
+ }
+ ret = connection->send_cls (connection,
+ &response->data
+ [connection->response_write_position
+ - response->data_start],
+ response->data_size -
+ (connection->response_write_position
+ - response->data_start));
+#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) || (errno == EAGAIN))
+ return MHD_YES;
+#if HAVE_MESSAGES
+ MHD_DLOG (connection->daemon,
+ "Failed to send data: %s\n", STRERROR (errno));
+#endif
+ CONNECTION_CLOSE_ERROR (connection, NULL);
+ return MHD_YES;
+ }
+ 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);
+ if (connection->state != MHD_CONNECTION_CHUNKED_BODY_READY)
+ break;
+ 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);
+ if (connection->state != MHD_CONNECTION_FOOTERS_SENDING)
+ break;
+ check_write_done (connection, MHD_CONNECTION_FOOTERS_SENT);
+ break;
+ case MHD_CONNECTION_FOOTERS_SENT:
+ EXTRA_CHECK (0);
+ break;
+ case MHD_CONNECTION_CLOSED:
+ return MHD_YES;
+ case MHD_TLS_CONNECTION_INIT:
+ EXTRA_CHECK (0);
+ break;
+ default:
+ EXTRA_CHECK (0);
+ CONNECTION_CLOSE_ERROR (connection, "Internal error\n");
+ return MHD_YES;
+ }
+ 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.
+ *
+ * @param connection connection to handle
+ * @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)
+{
+ struct MHD_Daemon *daemon;
+ unsigned int timeout;
+ const char *end;
+ int rend;
+ char *line;
+
+ while (1)
+ {
+#if DEBUG_STATES
+ MHD_DLOG (connection->daemon, "%s: state: %s\n",
+ __FUNCTION__, MHD_state_to_string (connection->state));
+#endif
+ switch (connection->state)
+ {
+ case MHD_CONNECTION_INIT:
+ line = get_next_header_line (connection);
+ if (line == NULL)
+ {
+ if (connection->state != MHD_CONNECTION_INIT)
+ continue;
+ if (connection->read_closed)
+ {
+ CONNECTION_CLOSE_ERROR (connection,
+ NULL);
+ continue;
+ }
+ break;
+ }
+ if (MHD_NO == parse_initial_message_line (connection, line))
+ CONNECTION_CLOSE_ERROR (connection, NULL);
+ else
+ connection->state = MHD_CONNECTION_URL_RECEIVED;
+ continue;
+ case MHD_CONNECTION_URL_RECEIVED:
+ line = get_next_header_line (connection);
+ if (line == NULL)
+ {
+ if (connection->state != MHD_CONNECTION_URL_RECEIVED)
+ continue;
+ if (connection->read_closed)
+ {
+ CONNECTION_CLOSE_ERROR (connection,
+ NULL);
+ continue;
+ }
+ break;
+ }
+ if (strlen (line) == 0)
+ {
+ connection->state = MHD_CONNECTION_HEADERS_RECEIVED;
+ continue;
+ }
+ if (MHD_NO == process_header_line (connection, line))
+ {
+ transmit_error_response (connection,
+ MHD_HTTP_BAD_REQUEST,
+ REQUEST_MALFORMED);
+ break;
+ }
+ connection->state = MHD_CONNECTION_HEADER_PART_RECEIVED;
+ continue;
+ case MHD_CONNECTION_HEADER_PART_RECEIVED:
+ line = get_next_header_line (connection);
+ if (line == NULL)
+ {
+ if (connection->state != MHD_CONNECTION_HEADER_PART_RECEIVED)
+ continue;
+ if (connection->read_closed)
+ {
+ CONNECTION_CLOSE_ERROR (connection,
+ NULL);
+ continue;
+ }
+ break;
+ }
+ if (MHD_NO ==
+ process_broken_line (connection, line, MHD_HEADER_KIND))
+ continue;
+ 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 (connection->state == MHD_CONNECTION_CLOSED)
+ continue;
+ if (need_100_continue (connection))
+ {
+ connection->state = MHD_CONNECTION_CONTINUE_SENDING;
+ break;
+ }
+ if (connection->response != NULL)
+ {
+ /* we refused (no upload allowed!) */
+ connection->remaining_upload_size = 0;
+ /* force close, in case client still tries to upload... */
+ connection->read_closed = MHD_YES;
+ }
+ 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)
+ {
+ process_request_body (connection); /* loop call */
+ if (connection->state == MHD_CONNECTION_CLOSED)
+ continue;
+ }
+ if ((connection->remaining_upload_size == 0) ||
+ ((connection->remaining_upload_size == MHD_SIZE_UNKNOWN) &&
+ (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)
+ {
+ if (connection->state != MHD_CONNECTION_BODY_RECEIVED)
+ continue;
+ if (connection->read_closed)
+ {
+ CONNECTION_CLOSE_ERROR (connection,
+ NULL);
+ continue;
+ }
+ break;
+ }
+ if (strlen (line) == 0)
+ {
+ connection->state = MHD_CONNECTION_FOOTERS_RECEIVED;
+ continue;
+ }
+ if (MHD_NO == process_header_line (connection, line))
+ {
+ transmit_error_response (connection,
+ MHD_HTTP_BAD_REQUEST,
+ REQUEST_MALFORMED);
+ break;
+ }
+ connection->state = MHD_CONNECTION_FOOTER_PART_RECEIVED;
+ continue;
+ case MHD_CONNECTION_FOOTER_PART_RECEIVED:
+ line = get_next_header_line (connection);
+ if (line == NULL)
+ {
+ if (connection->state != MHD_CONNECTION_FOOTER_PART_RECEIVED)
+ continue;
+ if (connection->read_closed)
+ {
+ CONNECTION_CLOSE_ERROR (connection,
+ NULL);
+ continue;
+ }
+ break;
+ }
+ if (MHD_NO ==
+ process_broken_line (connection, line, MHD_FOOTER_KIND))
+ continue;
+ 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! */
+ CONNECTION_CLOSE_ERROR (connection,
+ "Closing connection (failed to create
response header)\n");
+ continue;
+ }
+ connection->state = MHD_CONNECTION_HEADERS_SENDING;
+
+#if HAVE_DECL_TCP_CORK
+ /* starting header send, set TCP cork */
+ {
+ const int val = 1;
+ setsockopt (connection->socket_fd, IPPROTO_TCP, TCP_CORK, &val,
+ sizeof (val));
+ }
+#endif
+ 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:
+#if HAVE_DECL_TCP_CORK
+ /* done sending, uncork */
+ {
+ const int val = 0;
+ setsockopt (connection->socket_fd, IPPROTO_TCP, TCP_CORK, &val,
+ sizeof (val));
+ }
+#endif
+ end =
+ MHD_get_response_header (connection->response,
+ MHD_HTTP_HEADER_CONNECTION);
+ rend = ( (end != NULL) && (0 == strcasecmp (end, "close")) );
+ MHD_destroy_response (connection->response);
+ connection->response = NULL;
+ 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_aware = MHD_NO;
+ 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->headers_received = NULL;
+ connection->headers_received_tail = 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 ( (rend) || ((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)))
+ {
+ /* http 1.0, version-less requests cannot be pipelined */
+ MHD_connection_close (connection,
MHD_REQUEST_TERMINATED_COMPLETED_OK);
+ 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->response != NULL)
+ {
+ MHD_destroy_response (connection->response);
+ connection->response = NULL;
+ }
+ daemon = connection->daemon;
+ if (0 != pthread_mutex_lock(&daemon->cleanup_connection_mutex))
+ {
+ MHD_PANIC ("Failed to acquire cleanup mutex\n");
+ }
+ DLL_remove (daemon->connections_head,
+ daemon->connections_tail,
+ connection);
+ DLL_insert (daemon->cleanup_head,
+ daemon->cleanup_tail,
+ connection);
+ if (0 != pthread_mutex_unlock(&daemon->cleanup_connection_mutex))
+ {
+ MHD_PANIC ("Failed to release cleanup mutex\n");
+ }
+ return MHD_NO;
+ default:
+ EXTRA_CHECK (0);
+ break;
+ }
+ break;
+ }
+ timeout = connection->connection_timeout;
+ if ( (timeout != 0) &&
+ (timeout <= (MHD_monotonic_time() - connection->last_activity)) )
+ {
+ MHD_connection_close (connection,
MHD_REQUEST_TERMINATED_TIMEOUT_REACHED);
+ return MHD_YES;
+ }
+ return MHD_YES;
+}
+
+
+/**
+ * Set callbacks for this connection to those for HTTP.
+ *
+ * @param connection connection to initialize
+ */
+void
+MHD_set_http_callbacks_ (struct MHD_Connection *connection)
+{
+ connection->read_handler = &MHD_connection_handle_read;
+ connection->write_handler = &MHD_connection_handle_write;
+ connection->idle_handler = &MHD_connection_handle_idle;
+}
+
+
+/**
+ * Obtain information about the given connection.
+ *
+ * @param connection what connection to get information about
+ * @param infoType what information is desired?
+ * @param ... depends on infoType
+ * @return NULL if this information is not available
+ * (or if the infoType is unknown)
+ */
+const union MHD_ConnectionInfo *
+MHD_get_connection_info (struct MHD_Connection *connection,
+ enum MHD_ConnectionInfoType infoType, ...)
+{
+ switch (infoType)
+ {
+#if HTTPS_SUPPORT
+ case MHD_CONNECTION_INFO_CIPHER_ALGO:
+ if (connection->tls_session == NULL)
+ return NULL;
+ connection->cipher = gnutls_cipher_get (connection->tls_session);
+ return (const union MHD_ConnectionInfo *) &connection->cipher;
+ case MHD_CONNECTION_INFO_PROTOCOL:
+ if (connection->tls_session == NULL)
+ return NULL;
+ connection->protocol = gnutls_protocol_get_version
(connection->tls_session);
+ return (const union MHD_ConnectionInfo *) &connection->protocol;
+ case MHD_CONNECTION_INFO_GNUTLS_SESSION:
+ if (connection->tls_session == NULL)
+ return NULL;
+ return (const union MHD_ConnectionInfo *) &connection->tls_session;
+#endif
+ case MHD_CONNECTION_INFO_CLIENT_ADDRESS:
+ return (const union MHD_ConnectionInfo *) &connection->addr;
+ case MHD_CONNECTION_INFO_DAEMON:
+ return (const union MHD_ConnectionInfo *) &connection->daemon;
+ default:
+ return NULL;
+ };
+}
+
+
+/**
+ * Set a custom option for the given connection, overriding defaults.
+ *
+ * @param connection connection to modify
+ * @param option option to set
+ * @param ... arguments to the option, depending on the option type
+ * @return MHD_YES on success, MHD_NO if setting the option failed
+ */
+int
+MHD_set_connection_option (struct MHD_Connection *connection,
+ enum MHD_CONNECTION_OPTION option,
+ ...)
+{
+ va_list ap;
+
+ switch (option)
+ {
+ case MHD_CONNECTION_OPTION_TIMEOUT:
+ va_start (ap, option);
+ connection->connection_timeout = va_arg (ap, unsigned int);
+ va_end (ap);
+ return MHD_YES;
+ default:
+ return MHD_NO;
+ }
+}
+
+
+/* end of connection.c */
Deleted: libmicrohttpd/src/microhttpd/daemon.c
===================================================================
--- libmicrohttpd/src/daemon/daemon.c 2013-05-05 12:01:06 UTC (rev 27023)
+++ libmicrohttpd/src/microhttpd/daemon.c 2013-05-05 18:07:33 UTC (rev
27025)
@@ -1,2947 +0,0 @@
-/*
- This file is part of libmicrohttpd
- (C) 2007, 2008, 2009, 2010, 2011, 2012 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
- version 2.1 of the License, or (at your option) any later version.
-
- This library is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- Lesser General Public License for more details.
-
- You should have received a copy of the GNU Lesser General Public
- License along with this library; if not, write to the Free Software
- Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
USA
-
-*/
-
-/**
- * @file daemon.c
- * @brief A minimal-HTTP server library
- * @author Daniel Pittman
- * @author Christian Grothoff
- */
-#include "platform.h"
-#include "internal.h"
-#include "response.h"
-#include "connection.h"
-#include "memorypool.h"
-#include <limits.h>
-
-#if HAVE_SEARCH_H
-#include <search.h>
-#else
-#include "tsearch.h"
-#endif
-
-#if HTTPS_SUPPORT
-#include "connection_https.h"
-#include <gnutls/gnutls.h>
-#include <gcrypt.h>
-#endif
-
-#ifdef HAVE_POLL_H
-#include <poll.h>
-#endif
-
-#ifdef LINUX
-#include <sys/sendfile.h>
-#endif
-
-/**
- * Default connection limit.
- */
-#ifndef WINDOWS
-#define MHD_MAX_CONNECTIONS_DEFAULT FD_SETSIZE - 4
-#else
-#define MHD_MAX_CONNECTIONS_DEFAULT FD_SETSIZE
-#endif
-
-/**
- * Default memory allowed per connection.
- */
-#define MHD_POOL_SIZE_DEFAULT (32 * 1024)
-
-/**
- * Print extra messages with reasons for closing
- * sockets? (only adds non-error messages).
- */
-#define DEBUG_CLOSE MHD_NO
-
-/**
- * Print extra messages when establishing
- * connections? (only adds non-error messages).
- */
-#define DEBUG_CONNECT MHD_NO
-
-#ifndef LINUX
-#ifndef MSG_NOSIGNAL
-#define MSG_NOSIGNAL 0
-#endif
-#endif
-
-#ifndef SOCK_CLOEXEC
-#define SOCK_CLOEXEC 0
-#endif
-
-
-/**
- * Default implementation of the panic function,
- * prints an error message and aborts.
- *
- * @param cls unused
- * @param file name of the file with the problem
- * @param line line number with the problem
- * @param msg error message with details
- */
-static void
-mhd_panic_std (void *cls,
- const char *file,
- unsigned int line,
- const char *reason)
-{
-#if HAVE_MESSAGES
- fprintf (stderr, "Fatal error in GNU libmicrohttpd %s:%u: %s\n",
- file, line, reason);
-#endif
- abort ();
-}
-
-
-/**
- * Handler for fatal errors.
- */
-MHD_PanicCallback mhd_panic;
-
-/**
- * Closure argument for "mhd_panic".
- */
-void *mhd_panic_cls;
-
-
-/**
- * Trace up to and return master daemon. If the supplied daemon
- * is a master, then return the daemon itself.
- *
- * @param daemon handle to a daemon
- * @return master daemon handle
- */
-static struct MHD_Daemon*
-MHD_get_master (struct MHD_Daemon *daemon)
-{
- while (NULL != daemon->master)
- daemon = daemon->master;
- return daemon;
-}
-
-
-/**
- * Maintain connection count for single address.
- */
-struct MHD_IPCount
-{
- /**
- * Address family. AF_INET or AF_INET6 for now.
- */
- int family;
-
- /**
- * Actual address.
- */
- union
- {
- /**
- * IPv4 address.
- */
- struct in_addr ipv4;
-#if HAVE_IPV6
- /**
- * IPv6 address.
- */
- struct in6_addr ipv6;
-#endif
- } addr;
-
- /**
- * Counter.
- */
- unsigned int count;
-};
-
-
-/**
- * Lock shared structure for IP connection counts and connection DLLs.
- *
- * @param daemon handle to daemon where lock is
- */
-static void
-MHD_ip_count_lock(struct MHD_Daemon *daemon)
-{
- if (0 != pthread_mutex_lock(&daemon->per_ip_connection_mutex))
- {
- MHD_PANIC ("Failed to acquire IP connection limit mutex\n");
- }
-}
-
-
-/**
- * Unlock shared structure for IP connection counts and connection DLLs.
- *
- * @param daemon handle to daemon where lock is
- */
-static void
-MHD_ip_count_unlock(struct MHD_Daemon *daemon)
-{
- if (0 != pthread_mutex_unlock(&daemon->per_ip_connection_mutex))
- {
- MHD_PANIC ("Failed to release IP connection limit mutex\n");
- }
-}
-
-
-/**
- * Tree comparison function for IP addresses (supplied to tsearch() family).
- * We compare everything in the struct up through the beginning of the
- * 'count' field.
- *
- * @param a1 first address to compare
- * @param a2 second address to compare
- * @return -1, 0 or 1 depending on result of compare
- */
-static int
-MHD_ip_addr_compare(const void *a1, const void *a2)
-{
- return memcmp (a1, a2, offsetof (struct MHD_IPCount, count));
-}
-
-
-/**
- * Parse address and initialize 'key' using the address.
- *
- * @param addr address to parse
- * @param addrlen number of bytes in addr
- * @param key where to store the parsed address
- * @return MHD_YES on success and MHD_NO otherwise (e.g., invalid address type)
- */
-static int
-MHD_ip_addr_to_key(const struct sockaddr *addr,
- socklen_t addrlen,
- struct MHD_IPCount *key)
-{
- memset(key, 0, sizeof(*key));
-
- /* IPv4 addresses */
- if (sizeof (struct sockaddr_in) == addrlen)
- {
- const struct sockaddr_in *addr4 = (const struct sockaddr_in*) addr;
- key->family = AF_INET;
- memcpy (&key->addr.ipv4, &addr4->sin_addr, sizeof(addr4->sin_addr));
- return MHD_YES;
- }
-
-#if HAVE_IPV6
- /* IPv6 addresses */
- if (sizeof (struct sockaddr_in6) == addrlen)
- {
- const struct sockaddr_in6 *addr6 = (const struct sockaddr_in6*) addr;
- key->family = AF_INET6;
- memcpy (&key->addr.ipv6, &addr6->sin6_addr, sizeof(addr6->sin6_addr));
- return MHD_YES;
- }
-#endif
-
- /* Some other address */
- return MHD_NO;
-}
-
-
-/**
- * Check if IP address is over its limit.
- *
- * @param daemon handle to daemon where connection counts are tracked
- * @param addr address to add (or increment counter)
- * @param addrlen number of bytes in addr
- * @return Return MHD_YES if IP below limit, MHD_NO if IP has surpassed limit.
- * Also returns MHD_NO if fails to allocate memory.
- */
-static int
-MHD_ip_limit_add(struct MHD_Daemon *daemon,
- const struct sockaddr *addr,
- socklen_t addrlen)
-{
- struct MHD_IPCount *key;
- void **nodep;
- void *node;
- int result;
-
- daemon = MHD_get_master (daemon);
- /* Ignore if no connection limit assigned */
- if (0 == daemon->per_ip_connection_limit)
- return MHD_YES;
-
- if (NULL == (key = malloc (sizeof(*key))))
- return MHD_NO;
-
- /* Initialize key */
- if (MHD_NO == MHD_ip_addr_to_key (addr, addrlen, key))
- {
- /* Allow unhandled address types through */
- free (key);
- return MHD_YES;
- }
- MHD_ip_count_lock (daemon);
-
- /* Search for the IP address */
- if (NULL == (nodep = TSEARCH (key,
- &daemon->per_ip_connection_count,
- &MHD_ip_addr_compare)))
- {
-#if HAVE_MESSAGES
- MHD_DLOG (daemon,
- "Failed to add IP connection count node\n");
-#endif
- MHD_ip_count_unlock (daemon);
- free (key);
- return MHD_NO;
- }
- node = *nodep;
- /* If we got an existing node back, free the one we created */
- if (node != key)
- free(key);
- key = (struct MHD_IPCount *) node;
- /* Test if there is room for another connection; if so,
- * increment count */
- result = (key->count < daemon->per_ip_connection_limit);
- if (MHD_YES == result)
- ++key->count;
-
- MHD_ip_count_unlock (daemon);
- return result;
-}
-
-
-/**
- * Decrement connection count for IP address, removing from table
- * count reaches 0
- *
- * @param daemon handle to daemon where connection counts are tracked
- * @param addr address to remove (or decrement counter)
- * @param addrlen number of bytes in addr
- */
-static void
-MHD_ip_limit_del(struct MHD_Daemon *daemon,
- const struct sockaddr *addr,
- socklen_t addrlen)
-{
- struct MHD_IPCount search_key;
- struct MHD_IPCount *found_key;
- void **nodep;
-
- daemon = MHD_get_master (daemon);
- /* Ignore if no connection limit assigned */
- if (0 == daemon->per_ip_connection_limit)
- return;
- /* Initialize search key */
- if (MHD_NO == MHD_ip_addr_to_key (addr, addrlen, &search_key))
- return;
-
- MHD_ip_count_lock (daemon);
-
- /* Search for the IP address */
- if (NULL == (nodep = TFIND (&search_key,
- &daemon->per_ip_connection_count,
- &MHD_ip_addr_compare)))
- {
- /* Something's wrong if we couldn't find an IP address
- * that was previously added */
- MHD_PANIC ("Failed to find previously-added IP address\n");
- }
- found_key = (struct MHD_IPCount *) *nodep;
- /* Validate existing count for IP address */
- if (0 == found_key->count)
- {
- MHD_PANIC ("Previously-added IP address had 0 count\n");
- }
- /* Remove the node entirely if count reduces to 0 */
- if (0 == --found_key->count)
- {
- TDELETE (found_key,
- &daemon->per_ip_connection_count,
- &MHD_ip_addr_compare);
- free (found_key);
- }
-
- MHD_ip_count_unlock (daemon);
-}
-
-
-#if HTTPS_SUPPORT
-/**
- * Callback for receiving data from the socket.
- *
- * @param connection the MHD connection structure
- * @param other where to write received data to
- * @param i maximum size of other (in bytes)
- * @return number of bytes actually received
- */
-static ssize_t
-recv_tls_adapter (struct MHD_Connection *connection, void *other, size_t i)
-{
- int res;
-
- connection->tls_read_ready = MHD_NO;
- res = gnutls_record_recv (connection->tls_session, other, i);
- if ( (GNUTLS_E_AGAIN == res) ||
- (GNUTLS_E_INTERRUPTED == res) )
- {
- errno = EINTR;
- return -1;
- }
- if (res < 0)
- {
- /* Likely 'GNUTLS_E_INVALID_SESSION' (client communication
- disrupted); set errno to something caller will interpret
- correctly as a hard error*/
- errno = EPIPE;
- return res;
- }
- if (res == i)
- connection->tls_read_ready = MHD_YES;
- return res;
-}
-
-
-/**
- * Callback for writing data to the socket.
- *
- * @param connection the MHD connection structure
- * @param other data to write
- * @param i number of bytes to write
- * @return actual number of bytes written
- */
-static ssize_t
-send_tls_adapter (struct MHD_Connection *connection,
- const void *other, size_t i)
-{
- int res;
-
- res = gnutls_record_send (connection->tls_session, other, i);
- if ( (GNUTLS_E_AGAIN == res) ||
- (GNUTLS_E_INTERRUPTED == res) )
- {
- errno = EINTR;
- return -1;
- }
- return res;
-}
-
-
-/**
- * Read and setup our certificate and key.
- *
- * @param daemon handle to daemon to initialize
- * @return 0 on success
- */
-static int
-MHD_init_daemon_certificate (struct MHD_Daemon *daemon)
-{
- gnutls_datum_t key;
- gnutls_datum_t cert;
-
- if (NULL != daemon->https_mem_trust)
- {
- cert.data = (unsigned char *) daemon->https_mem_trust;
- cert.size = strlen (daemon->https_mem_trust);
- if (gnutls_certificate_set_x509_trust_mem (daemon->x509_cred, &cert,
- GNUTLS_X509_FMT_PEM) < 0)
- {
-#if HAVE_MESSAGES
- MHD_DLOG(daemon,
- "Bad trust certificate format\n");
-#endif
- return -1;
- }
- }
-
- /* certificate & key loaded from memory */
- if ( (NULL != daemon->https_mem_cert) &&
- (NULL != daemon->https_mem_key) )
- {
- key.data = (unsigned char *) daemon->https_mem_key;
- key.size = strlen (daemon->https_mem_key);
- cert.data = (unsigned char *) daemon->https_mem_cert;
- cert.size = strlen (daemon->https_mem_cert);
-
- return gnutls_certificate_set_x509_key_mem (daemon->x509_cred,
- &cert, &key,
- GNUTLS_X509_FMT_PEM);
- }
-#if HAVE_MESSAGES
- MHD_DLOG (daemon, "You need to specify a certificate and key location\n");
-#endif
- return -1;
-}
-
-
-/**
- * Initialize security aspects of the HTTPS daemon
- *
- * @param daemon handle to daemon to initialize
- * @return 0 on success
- */
-static int
-MHD_TLS_init (struct MHD_Daemon *daemon)
-{
- switch (daemon->cred_type)
- {
- case GNUTLS_CRD_CERTIFICATE:
- if (0 !=
- gnutls_certificate_allocate_credentials (&daemon->x509_cred))
- return GNUTLS_E_MEMORY_ERROR;
- return MHD_init_daemon_certificate (daemon);
- default:
-#if HAVE_MESSAGES
- MHD_DLOG (daemon,
- "Error: invalid credentials type %d specified.\n",
- daemon->cred_type);
-#endif
- return -1;
- }
-}
-#endif
-
-
-/**
- * Obtain the select sets for this daemon.
- *
- * @param daemon daemon to get sets from
- * @param read_fd_set read set
- * @param write_fd_set write set
- * @param except_fd_set except set
- * @param max_fd increased to largest FD added (if larger
- * than existing value); can be NULL
- * @return MHD_YES on success, MHD_NO if this
- * daemon was not started with the right
- * options for this call.
- */
-int
-MHD_get_fdset (struct MHD_Daemon *daemon,
- fd_set *read_fd_set,
- fd_set *write_fd_set,
- fd_set *except_fd_set,
- int *max_fd)
-{
- struct MHD_Connection *pos;
- int fd;
-
- if ( (NULL == daemon)
- || (NULL == read_fd_set)
- || (NULL == write_fd_set)
- || (NULL == except_fd_set)
- || (NULL == max_fd)
- || (MHD_YES == daemon->shutdown)
- || (0 != (daemon->options & MHD_USE_THREAD_PER_CONNECTION))
- || (0 != (daemon->options & MHD_USE_POLL)))
- return MHD_NO;
- fd = daemon->socket_fd;
- if (-1 != fd)
- {
- FD_SET (fd, read_fd_set);
- /* update max file descriptor */
- if ((*max_fd) < fd)
- *max_fd = fd;
- }
- for (pos = daemon->connections_head; NULL != pos; pos = pos->next)
- if (MHD_YES != MHD_connection_get_fdset (pos,
- read_fd_set,
- write_fd_set,
- except_fd_set, max_fd))
- return MHD_NO;
-#if DEBUG_CONNECT
- MHD_DLOG (daemon, "Maximum socket in select set: %d\n", *max_fd);
-#endif
- return MHD_YES;
-}
-
-
-/**
- * Main function of the thread that handles an individual
- * connection when MHD_USE_THREAD_PER_CONNECTION is set.
- *
- * @param data the 'struct MHD_Connection' this thread will handle
- * @return always NULL
- */
-static void *
-MHD_handle_connection (void *data)
-{
- struct MHD_Connection *con = data;
- int num_ready;
- fd_set rs;
- fd_set ws;
- fd_set es;
- int max;
- struct timeval tv;
- struct timeval *tvp;
- unsigned int timeout;
- time_t now;
-#ifdef HAVE_POLL_H
- struct MHD_Pollfd mp;
- struct pollfd p[1];
-#endif
-
- timeout = con->daemon->connection_timeout;
- while ( (MHD_YES != con->daemon->shutdown) &&
- (MHD_CONNECTION_CLOSED != con->state) )
- {
- tvp = NULL;
- if (timeout > 0)
- {
- now = MHD_monotonic_time();
- if (now - con->last_activity > timeout)
- tv.tv_sec = 0;
- else
- tv.tv_sec = timeout - (now - con->last_activity);
- tv.tv_usec = 0;
- tvp = &tv;
- }
- if ( (MHD_CONNECTION_NORMAL_BODY_UNREADY == con->state) ||
- (MHD_CONNECTION_CHUNKED_BODY_UNREADY == con->state) )
- {
- /* do not block (we're waiting for our callback to succeed) */
- tv.tv_sec = 0;
- tv.tv_usec = 0;
- tvp = &tv;
- }
-#if HTTPS_SUPPORT
- if (MHD_YES == con->tls_read_ready)
- {
- /* do not block (more data may be inside of TLS buffers waiting for
us) */
- tv.tv_sec = 0;
- tv.tv_usec = 0;
- tvp = &tv;
- }
-#endif
- if (0 == (con->daemon->options & MHD_USE_POLL))
- {
- /* use select */
- FD_ZERO (&rs);
- FD_ZERO (&ws);
- FD_ZERO (&es);
- max = 0;
- MHD_connection_get_fdset (con, &rs, &ws, &es, &max);
- num_ready = SELECT (max + 1, &rs, &ws, &es, tvp);
- if (num_ready < 0)
- {
- if (EINTR == errno)
- continue;
-#if HAVE_MESSAGES
- MHD_DLOG (con->daemon,
- "Error during select (%d): `%s'\n",
- max,
- STRERROR (errno));
-#endif
- break;
- }
- /* call appropriate connection handler if necessary */
- if ( (FD_ISSET (con->socket_fd, &rs))
-#if HTTPS_SUPPORT
- || (MHD_YES == con->tls_read_ready)
-#endif
- )
- con->read_handler (con);
- if (FD_ISSET (con->socket_fd, &ws))
- con->write_handler (con);
- if (MHD_NO == con->idle_handler (con))
- goto exit;
- }
-#ifdef HAVE_POLL_H
- else
- {
- /* use poll */
- memset(&mp, 0, sizeof (struct MHD_Pollfd));
- MHD_connection_get_pollfd(con, &mp);
- memset(&p, 0, sizeof (p));
- p[0].fd = mp.fd;
- if (mp.events & MHD_POLL_ACTION_IN)
- p[0].events |= POLLIN;
- if (mp.events & MHD_POLL_ACTION_OUT)
- p[0].events |= POLLOUT;
- if (poll (p,
- 1,
- (NULL == tvp) ? -1 : tv.tv_sec * 1000) < 0)
- {
- if (EINTR == errno)
- continue;
-#if HAVE_MESSAGES
- MHD_DLOG (con->daemon, "Error during poll: `%s'\n",
- STRERROR (errno));
-#endif
- break;
- }
- if ( (0 != (p[0].revents & POLLIN))
-#if HTTPS_SUPPORT
- || (MHD_YES == con->tls_read_ready)
-#endif
- )
- con->read_handler (con);
- if (0 != (p[0].revents & POLLOUT))
- con->write_handler (con);
- if (0 != (p[0].revents & (POLLERR | POLLHUP)))
- MHD_connection_close (con, MHD_REQUEST_TERMINATED_WITH_ERROR);
- if (MHD_NO == con->idle_handler (con))
- goto exit;
- }
-#endif
- }
- if (MHD_CONNECTION_IN_CLEANUP != con->state)
- {
-#if DEBUG_CLOSE
-#if HAVE_MESSAGES
- MHD_DLOG (con->daemon,
- "Processing thread terminating, closing connection\n");
-#endif
-#endif
- if (MHD_CONNECTION_CLOSED != con->state)
- MHD_connection_close (con,
- MHD_REQUEST_TERMINATED_DAEMON_SHUTDOWN);
- con->idle_handler (con);
- }
-exit:
- if (NULL != con->response)
- {
- MHD_destroy_response (con->response);
- con->response = NULL;
- }
- return NULL;
-}
-
-
-/**
- * Callback for receiving data from the socket.
- *
- * @param conn the MHD connection structure
- * @param other where to write received data to
- * @param i maximum size of other (in bytes)
- * @return number of bytes actually received
- */
-static ssize_t
-recv_param_adapter (struct MHD_Connection *connection,
- void *other,
- size_t i)
-{
- if ( (-1 == connection->socket_fd) ||
- (MHD_CONNECTION_CLOSED == connection->state) )
- {
- errno = ENOTCONN;
- return -1;
- }
- if (0 != (connection->daemon->options & MHD_USE_SSL))
- return RECV (connection->socket_fd, other, i, MSG_NOSIGNAL);
- return RECV (connection->socket_fd, other, i, MSG_NOSIGNAL);
-}
-
-
-/**
- * Callback for writing data to the socket.
- *
- * @param conn the MHD connection structure
- * @param other data to write
- * @param i number of bytes to write
- * @return actual number of bytes written
- */
-static ssize_t
-send_param_adapter (struct MHD_Connection *connection,
- const void *other,
- size_t i)
-{
-#if LINUX
- int fd;
- off_t offset;
- off_t left;
- ssize_t ret;
-#endif
- if ( (-1 == connection->socket_fd) ||
- (MHD_CONNECTION_CLOSED == connection->state) )
- {
- errno = ENOTCONN;
- return -1;
- }
- if (0 != (connection->daemon->options & MHD_USE_SSL))
- return SEND (connection->socket_fd, other, i, MSG_NOSIGNAL);
-#if LINUX
- if ( (connection->write_buffer_append_offset ==
- connection->write_buffer_send_offset) &&
- (NULL != connection->response) &&
- (-1 != (fd = connection->response->fd)) )
- {
- /* can use sendfile */
- offset = (off_t) connection->response_write_position +
connection->response->fd_off;
- left = connection->response->total_size -
connection->response_write_position;
- if (left > SSIZE_MAX)
- left = SSIZE_MAX; /* cap at return value limit */
- if (-1 != (ret = sendfile (connection->socket_fd,
- fd,
- &offset,
- (size_t) left)))
- return ret;
- if ( (EINTR == errno) || (EAGAIN == errno) )
- return 0;
- if ( (EINVAL == errno) || (EBADF == errno) )
- return -1;
- /* None of the 'usual' sendfile errors occurred, so we should try
- to fall back to 'SEND'; see also this thread for info on
- odd libc/Linux behavior with sendfile:
- http://lists.gnu.org/archive/html/libmicrohttpd/2011-02/msg00015.html
*/
- }
-#endif
- return SEND (connection->socket_fd, other, i, MSG_NOSIGNAL);
-}
-
-
-/**
- * Signature of main function for a thread.
- */
-typedef void *(*ThreadStartRoutine)(void *cls);
-
-
-/**
- * Create a thread and set the attributes according to our options.
- *
- * @param thread handle to initialize
- * @param daemon daemon with options
- * @param start_routine main function of thread
- * @param arg argument for start_routine
- * @return 0 on success
- */
-static int
-create_thread (pthread_t * thread,
- const struct MHD_Daemon *daemon,
- ThreadStartRoutine start_routine,
- void *arg)
-{
- pthread_attr_t attr;
- pthread_attr_t *pattr;
- int ret;
-
- if (0 != daemon->thread_stack_size)
- {
- if (0 != (ret = pthread_attr_init (&attr)))
- goto ERR;
- if (0 != (ret = pthread_attr_setstacksize (&attr,
daemon->thread_stack_size)))
- {
- pthread_attr_destroy (&attr);
- goto ERR;
- }
- pattr = &attr;
- }
- else
- {
- pattr = NULL;
- }
- ret = pthread_create (thread, pattr,
- start_routine, arg);
- if (0 != daemon->thread_stack_size)
- pthread_attr_destroy (&attr);
- return ret;
- ERR:
-#if HAVE_MESSAGES
- MHD_DLOG (daemon,
- "Failed to set thread stack size\n");
-#endif
- errno = EINVAL;
- return ret;
-}
-
-
-/**
- * Add another client connection to the set of connections
- * managed by MHD. This API is usually not needed (since
- * MHD will accept inbound connections on the server socket).
- * Use this API in special cases, for example if your HTTP
- * server is behind NAT and needs to connect out to the
- * HTTP client.
- *
- * The given client socket will be managed (and closed!) by MHD after
- * this call and must no longer be used directly by the application
- * afterwards.
- *
- * Per-IP connection limits are ignored when using this API.
- *
- * @param daemon daemon that manages the connection
- * @param client_socket socket to manage (MHD will expect
- * to receive an HTTP request from this socket next).
- * @param addr IP address of the client
- * @param addrlen number of bytes in addr
- * @return MHD_YES on success, MHD_NO if this daemon could
- * not handle the connection (i.e. malloc failed, etc).
- * The socket will be closed in any case.
- */
-int
-MHD_add_connection (struct MHD_Daemon *daemon,
- int client_socket,
- const struct sockaddr *addr,
- socklen_t addrlen)
-{
- struct MHD_Connection *connection;
- int res_thread_create;
-#if OSX
- static int on = 1;
-#endif
-
-#ifndef WINDOWS
- if ( (client_socket >= FD_SETSIZE) &&
- (0 == (daemon->options & MHD_USE_POLL)) )
- {
-#if HAVE_MESSAGES
- MHD_DLOG (daemon,
- "Socket descriptor larger than FD_SETSIZE: %d > %d\n",
- client_socket,
- FD_SETSIZE);
-#endif
- SHUTDOWN (client_socket, SHUT_RDWR);
- CLOSE (client_socket);
- return MHD_NO;
- }
-#endif
-
-
-#if HAVE_MESSAGES
-#if DEBUG_CONNECT
- MHD_DLOG (daemon, "Accepted connection on socket %d\n", s);
-#endif
-#endif
- if ( (0 == daemon->max_connections) ||
- (MHD_NO == MHD_ip_limit_add (daemon, addr, addrlen)) )
- {
- /* above connection limit - reject */
-#if HAVE_MESSAGES
- MHD_DLOG (daemon,
- "Server reached connection limit (closing inbound
connection)\n");
-#endif
- SHUTDOWN (client_socket, SHUT_RDWR);
- CLOSE (client_socket);
- return MHD_NO;
- }
-
- /* apply connection acceptance policy if present */
- if ( (NULL != daemon->apc) &&
- (MHD_NO == daemon->apc (daemon->apc_cls,
- addr, addrlen)) )
- {
-#if DEBUG_CLOSE
-#if HAVE_MESSAGES
- MHD_DLOG (daemon, "Connection rejected, closing connection\n");
-#endif
-#endif
- SHUTDOWN (client_socket, SHUT_RDWR);
- CLOSE (client_socket);
- MHD_ip_limit_del (daemon, addr, addrlen);
- return MHD_YES;
- }
-
-#if OSX
-#ifdef SOL_SOCKET
-#ifdef SO_NOSIGPIPE
- setsockopt (client_socket,
- SOL_SOCKET, SO_NOSIGPIPE,
- &on, sizeof (on));
-#endif
-#endif
-#endif
-
- if (NULL == (connection = malloc (sizeof (struct MHD_Connection))))
- {
-#if HAVE_MESSAGES
- MHD_DLOG (daemon,
- "Error allocating memory: %s\n",
- STRERROR (errno));
-#endif
- SHUTDOWN (client_socket, SHUT_RDWR);
- CLOSE (client_socket);
- MHD_ip_limit_del (daemon, addr, addrlen);
- return MHD_NO;
- }
- memset (connection, 0, sizeof (struct MHD_Connection));
- connection->connection_timeout = daemon->connection_timeout;
- connection->pool = NULL;
- if (NULL == (connection->addr = malloc (addrlen)))
- {
-#if HAVE_MESSAGES
- MHD_DLOG (daemon,
- "Error allocating memory: %s\n",
- STRERROR (errno));
-#endif
- SHUTDOWN (client_socket, SHUT_RDWR);
- CLOSE (client_socket);
- MHD_ip_limit_del (daemon, addr, addrlen);
- free (connection);
- return MHD_NO;
- }
- memcpy (connection->addr, addr, addrlen);
- connection->addr_len = addrlen;
- connection->socket_fd = client_socket;
- connection->daemon = daemon;
- connection->last_activity = MHD_monotonic_time();
-
- /* set default connection handlers */
- MHD_set_http_callbacks_ (connection);
- connection->recv_cls = &recv_param_adapter;
- connection->send_cls = &send_param_adapter;
- /* non-blocking sockets are required on most systems and for GNUtls;
- however, they somehow cause serious problems on CYGWIN (#1824) */
-#ifdef CYGWIN
- if (0 != (daemon->options & MHD_USE_SSL))
-#endif
- {
- /* make socket non-blocking */
-#ifndef MINGW
- int flags = fcntl (connection->socket_fd, F_GETFL);
- if ( (-1 == flags) ||
- (0 != fcntl (connection->socket_fd, F_SETFL, flags | O_NONBLOCK)) )
- {
-#if HAVE_MESSAGES
- MHD_DLOG (daemon,
- "Failed to make socket %d non-blocking: %s\n",
- connection->socket_fd,
- STRERROR (errno));
-#endif
- }
-#else
- unsigned long flags = 1;
- if (0 != ioctlsocket (connection->socket_fd, FIONBIO, &flags))
- {
-#if HAVE_MESSAGES
- MHD_DLOG (daemon,
- "Failed to make socket non-blocking: %s\n",
- STRERROR (errno));
-#endif
- }
-#endif
- }
-
-#if HTTPS_SUPPORT
- if (0 != (daemon->options & MHD_USE_SSL))
- {
- connection->recv_cls = &recv_tls_adapter;
- connection->send_cls = &send_tls_adapter;
- connection->state = MHD_TLS_CONNECTION_INIT;
- MHD_set_https_callbacks (connection);
- gnutls_init (&connection->tls_session, GNUTLS_SERVER);
- gnutls_priority_set (connection->tls_session,
- daemon->priority_cache);
- switch (daemon->cred_type)
- {
- /* set needed credentials for certificate authentication. */
- case GNUTLS_CRD_CERTIFICATE:
- gnutls_credentials_set (connection->tls_session,
- GNUTLS_CRD_CERTIFICATE,
- daemon->x509_cred);
- break;
- default:
-#if HAVE_MESSAGES
- MHD_DLOG (connection->daemon,
- "Failed to setup TLS credentials: unknown credential type
%d\n",
- daemon->cred_type);
-#endif
- SHUTDOWN (client_socket, SHUT_RDWR);
- CLOSE (client_socket);
- MHD_ip_limit_del (daemon, addr, addrlen);
- free (connection->addr);
- free (connection);
- MHD_PANIC ("Unknown credential type");
- return MHD_NO;
- }
- gnutls_transport_set_ptr (connection->tls_session,
- (gnutls_transport_ptr_t) connection);
- gnutls_transport_set_pull_function (connection->tls_session,
- (gnutls_pull_func) &
- recv_param_adapter);
- gnutls_transport_set_push_function (connection->tls_session,
- (gnutls_push_func) &
- send_param_adapter);
-
- if (daemon->https_mem_trust){
- gnutls_certificate_server_set_request(connection->tls_session,
GNUTLS_CERT_REQUEST);
- }
- }
-#endif
-
- if (0 != pthread_mutex_lock(&daemon->cleanup_connection_mutex))
- {
- MHD_PANIC ("Failed to acquire cleanup mutex\n");
- }
- DLL_insert (daemon->connections_head,
- daemon->connections_tail,
- connection);
- if (0 != pthread_mutex_unlock(&daemon->cleanup_connection_mutex))
- {
- MHD_PANIC ("Failed to release cleanup mutex\n");
- }
-
- /* attempt to create handler thread */
- if (0 != (daemon->options & MHD_USE_THREAD_PER_CONNECTION))
- {
- res_thread_create = create_thread (&connection->pid, daemon,
- &MHD_handle_connection, connection);
- if (0 != res_thread_create)
- {
-#if HAVE_MESSAGES
- MHD_DLOG (daemon, "Failed to create a thread: %s\n",
- STRERROR (res_thread_create));
-#endif
- SHUTDOWN (client_socket, SHUT_RDWR);
- CLOSE (client_socket);
- MHD_ip_limit_del (daemon, addr, addrlen);
- if (0 != pthread_mutex_lock(&daemon->cleanup_connection_mutex))
- {
- MHD_PANIC ("Failed to acquire cleanup mutex\n");
- }
- DLL_remove (daemon->connections_head,
- daemon->connections_tail,
- connection);
- if (0 != pthread_mutex_unlock(&daemon->cleanup_connection_mutex))
- {
- MHD_PANIC ("Failed to release cleanup mutex\n");
- }
- free (connection->addr);
- free (connection);
- return MHD_NO;
- }
- }
- daemon->max_connections--;
- return MHD_YES;
-}
-
-
-/**
- * Accept an incoming connection and create the MHD_Connection object for
- * it. This function also enforces policy by way of checking with the
- * accept policy callback.
- *
- * @param daemon handle with the listen socket
- * @return MHD_YES on success
- */
-static int
-MHD_accept_connection (struct MHD_Daemon *daemon)
-{
-#if HAVE_INET6
- struct sockaddr_in6 addrstorage;
-#else
- struct sockaddr_in addrstorage;
-#endif
- struct sockaddr *addr = (struct sockaddr *) &addrstorage;
- socklen_t addrlen;
- int s;
- int flags;
- int need_fcntl;
- int fd;
-
- addrlen = sizeof (addrstorage);
- memset (addr, 0, sizeof (addrstorage));
- if (-1 == (fd = daemon->socket_fd))
- return MHD_NO;
-#if HAVE_ACCEPT4
- s = accept4 (fd, addr, &addrlen, SOCK_CLOEXEC);
- need_fcntl = MHD_NO;
-#else
- s = -1;
- need_fcntl = MHD_YES;
-#endif
- if (-1 == s)
- {
- s = ACCEPT (fd, addr, &addrlen);
- need_fcntl = MHD_YES;
- }
- if ((-1 == s) || (addrlen <= 0))
- {
-#if HAVE_MESSAGES
- /* This could be a common occurance with multiple worker threads */
- if ((EAGAIN != errno) && (EWOULDBLOCK != errno))
- MHD_DLOG (daemon,
- "Error accepting connection: %s\n",
- STRERROR (errno));
-#endif
- if (-1 != s)
- {
- SHUTDOWN (s, SHUT_RDWR);
- CLOSE (s);
- /* just in case */
- }
- return MHD_NO;
- }
- if (MHD_YES == need_fcntl)
- {
- /* make socket non-inheritable */
-#ifdef WINDOWS
- DWORD dwFlags;
- if (!GetHandleInformation ((HANDLE) s, &dwFlags) ||
- ((dwFlags != dwFlags & ~HANDLE_FLAG_INHERIT) &&
- !SetHandleInformation ((HANDLE) s, HANDLE_FLAG_INHERIT, 0)))
- {
-#if HAVE_MESSAGES
- SetErrnoFromWinError (GetLastError ());
- MHD_DLOG (daemon,
- "Failed to make socket non-inheritable: %s\n",
- STRERROR (errno));
-#endif
- }
-#else
- flags = fcntl (s, F_GETFD);
- if ( ( (-1 == flags) ||
- ( (flags != (flags | FD_CLOEXEC)) &&
- (0 != fcntl (s, F_SETFD, flags | FD_CLOEXEC)) ) ) )
- {
-#if HAVE_MESSAGES
- MHD_DLOG (daemon,
- "Failed to make socket non-inheritable: %s\n",
- STRERROR (errno));
-#endif
- }
-#endif
- }
-#if HAVE_MESSAGES
-#if DEBUG_CONNECT
- MHD_DLOG (daemon, "Accepted connection on socket %d\n", s);
-#endif
-#endif
- return MHD_add_connection (daemon, s,
- addr, addrlen);
-}
-
-
-/**
- * Free resources associated with all closed connections.
- * (destroy responses, free buffers, etc.). All closed
- * connections are kept in the "cleanup" doubly-linked list.
- *
- * @param daemon daemon to clean up
- */
-static void
-MHD_cleanup_connections (struct MHD_Daemon *daemon)
-{
- struct MHD_Connection *pos;
- void *unused;
- int rc;
-
- if (0 != pthread_mutex_lock(&daemon->cleanup_connection_mutex))
- {
- MHD_PANIC ("Failed to acquire cleanup mutex\n");
- }
- while (NULL != (pos = daemon->cleanup_head))
- {
- DLL_remove (daemon->cleanup_head,
- daemon->cleanup_tail,
- pos);
- if ( (0 != (daemon->options & MHD_USE_THREAD_PER_CONNECTION)) &&
- (MHD_NO == pos->thread_joined) )
- {
- if (0 != (rc = pthread_join (pos->pid, &unused)))
- {
- MHD_PANIC ("Failed to join a thread\n");
- }
- }
- MHD_pool_destroy (pos->pool);
-#if HTTPS_SUPPORT
- if (pos->tls_session != NULL)
- gnutls_deinit (pos->tls_session);
-#endif
- MHD_ip_limit_del (daemon, (struct sockaddr*)pos->addr, pos->addr_len);
- if (NULL != pos->response)
- {
- MHD_destroy_response (pos->response);
- pos->response = NULL;
- }
- if (-1 != pos->socket_fd)
- CLOSE (pos->socket_fd);
- if (NULL != pos->addr)
- free (pos->addr);
- free (pos);
- daemon->max_connections++;
- }
- if (0 != pthread_mutex_unlock(&daemon->cleanup_connection_mutex))
- {
- MHD_PANIC ("Failed to release cleanup mutex\n");
- }
-}
-
-
-/**
- * Obtain timeout value for select for this daemon
- * (only needed if connection timeout is used). The
- * returned value is how long select should at most
- * block, not the timeout value set for connections.
- *
- * @param daemon daemon to query for timeout
- * @param timeout set to the timeout (in milliseconds)
- * @return MHD_YES on success, MHD_NO if timeouts are
- * not used (or no connections exist that would
- * necessiate the use of a timeout right now).
- */
-int
-MHD_get_timeout (struct MHD_Daemon *daemon,
- MHD_UNSIGNED_LONG_LONG *timeout)
-{
- time_t earliest_deadline;
- time_t now;
- struct MHD_Connection *pos;
- int have_timeout;
-
- if (0 != (daemon->options & MHD_USE_THREAD_PER_CONNECTION))
- {
-#if HAVE_MESSAGES
- MHD_DLOG (daemon, "Illegal call to MHD_get_timeout\n");
-#endif
- return MHD_NO;
- }
- have_timeout = MHD_NO;
- for (pos = daemon->connections_head; NULL != pos; pos = pos->next)
- {
-#if HTTPS_SUPPORT
- if (MHD_YES == pos->tls_read_ready)
- {
- earliest_deadline = 0;
- have_timeout = MHD_YES;
- break;
- }
-#endif
- if (0 != pos->connection_timeout)
- {
- if ( (! have_timeout) ||
- (earliest_deadline > pos->last_activity +
pos->connection_timeout) )
- earliest_deadline = pos->last_activity + pos->connection_timeout;
-#if HTTPS_SUPPORT
- if ( (0 != (daemon->options & MHD_USE_SSL)) &&
- (0 != gnutls_record_check_pending (pos->tls_session)) )
- earliest_deadline = 0;
-#endif
- have_timeout = MHD_YES;
- }
- }
- if (MHD_NO == have_timeout)
- return MHD_NO;
- now = MHD_monotonic_time();
- if (earliest_deadline < now)
- *timeout = 0;
- else
- *timeout = 1000 * (1 + earliest_deadline - now);
- return MHD_YES;
-}
-
-
-/**
- * Run webserver operations. This method should be called by clients
- * in combination with MHD_get_fdset if the client-controlled select
- * method is used.
- *
- * You can use this function instead of "MHD_run" if you called
- * 'select' on the result from "MHD_get_fdset". File descriptors in
- * the sets that are not controlled by MHD will be ignored. Calling
- * this function instead of "MHD_run" is more efficient as MHD will
- * not have to call 'select' again to determine which operations are
- * ready.
- *
- * @param daemon daemon to run select loop for
- * @param read_fd_set read set
- * @param write_fd_set write set
- * @param except_fd_set except set (not used, can be NULL)
- * @return MHD_NO on serious errors, MHD_YES on success
- */
-int
-MHD_run_from_select (struct MHD_Daemon *daemon,
- const fd_set *read_fd_set,
- const fd_set *write_fd_set,
- const fd_set *except_fd_set)
-{
- int ds;
- int tmp;
- struct MHD_Connection *pos;
- struct MHD_Connection *next;
-
- /* select connection thread handling type */
- if ( (-1 != (ds = daemon->socket_fd)) &&
- (FD_ISSET (ds, read_fd_set)) )
- MHD_accept_connection (daemon);
- /* drain signaling pipe to avoid spinning select */
- if ( (-1 != daemon->wpipe[0]) &&
- (FD_ISSET (daemon->wpipe[0], read_fd_set)) )
- (void) read (daemon->wpipe[0], &tmp, sizeof (tmp));
-
- if (0 == (daemon->options & MHD_USE_THREAD_PER_CONNECTION))
- {
- /* do not have a thread per connection, process all connections now */
- next = daemon->connections_head;
- while (NULL != (pos = next))
- {
- next = pos->next;
- ds = pos->socket_fd;
- if (ds != -1)
- {
- if ( (FD_ISSET (ds, read_fd_set))
-#if HTTPS_SUPPORT
- || (MHD_YES == pos->tls_read_ready)
-#endif
- )
- pos->read_handler (pos);
- if (FD_ISSET (ds, write_fd_set))
- pos->write_handler (pos);
- pos->idle_handler (pos);
- }
- }
- }
- return MHD_YES;
-}
-
-
-/**
- * Main internal select call. Will compute select sets, call 'select'
- * and then MHD_run_from_select with the result.
- *
- * @param daemon daemon to run select loop for
- * @param may_block YES if blocking, NO if non-blocking
- * @return MHD_NO on serious errors, MHD_YES on success
- */
-static int
-MHD_select (struct MHD_Daemon *daemon,
- int may_block)
-{
- int num_ready;
- fd_set rs;
- fd_set ws;
- fd_set es;
- int max;
- struct timeval timeout;
- struct timeval *tv;
- MHD_UNSIGNED_LONG_LONG ltimeout;
-
- timeout.tv_sec = 0;
- timeout.tv_usec = 0;
- if (MHD_YES == daemon->shutdown)
- return MHD_NO;
- FD_ZERO (&rs);
- FD_ZERO (&ws);
- FD_ZERO (&es);
- max = -1;
- 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;
-
- /* If we're at the connection limit, no need to
- accept new connections. */
- if ( (0 == daemon->max_connections) &&
- (-1 != daemon->socket_fd) )
- FD_CLR (daemon->socket_fd, &rs);
- }
- else
- {
- /* accept only, have one thread per connection */
- if (-1 != daemon->socket_fd)
- {
- max = daemon->socket_fd;
- FD_SET (daemon->socket_fd, &rs);
- }
- }
- if (-1 != daemon->wpipe[0])
- {
- FD_SET (daemon->wpipe[0], &rs);
- /* update max file descriptor */
- if (max < daemon->wpipe[0])
- max = daemon->wpipe[0];
- }
-
- tv = NULL;
- if (MHD_NO == may_block)
- {
- timeout.tv_usec = 0;
- timeout.tv_sec = 0;
- tv = &timeout;
- }
- else if ( (0 == (daemon->options & MHD_USE_THREAD_PER_CONNECTION)) &&
- (MHD_YES == MHD_get_timeout (daemon, <imeout)) )
- {
- /* ltimeout is in ms */
- timeout.tv_usec = (ltimeout % 1000) * 1000;
- timeout.tv_sec = ltimeout / 1000;
- tv = &timeout;
- }
- if (-1 == max)
- return MHD_YES;
- num_ready = SELECT (max + 1, &rs, &ws, &es, tv);
- if (MHD_YES == daemon->shutdown)
- return MHD_NO;
- if (num_ready < 0)
- {
- if (EINTR == errno)
- return MHD_YES;
-#if HAVE_MESSAGES
- MHD_DLOG (daemon, "select failed: %s\n", STRERROR (errno));
-#endif
- return MHD_NO;
- }
- return MHD_run_from_select (daemon, &rs, &ws, &es);
-}
-
-
-#ifdef HAVE_POLL_H
-/**
- * Process all of our connections and possibly the server
- * socket using 'poll'.
- *
- * @param daemon daemon to run poll loop for
- * @param may_block YES if blocking, NO if non-blocking
- * @return MHD_NO on serious errors, MHD_YES on success
- */
-static int
-MHD_poll_all (struct MHD_Daemon *daemon,
- int may_block)
-{
- unsigned int num_connections;
- struct MHD_Connection *pos;
- struct MHD_Connection *next;
-
- /* count number of connections and thus determine poll set size */
- num_connections = 0;
- for (pos = daemon->connections_head; NULL != pos; pos = pos->next)
- num_connections++;
-
- {
- struct pollfd p[2 + num_connections];
- struct MHD_Pollfd mp;
- MHD_UNSIGNED_LONG_LONG ltimeout;
- unsigned int i;
- int timeout;
- unsigned int poll_server;
- int poll_listen;
-
- memset (p, 0, sizeof (p));
- poll_server = 0;
- poll_listen = -1;
- if ( (-1 != daemon->socket_fd) &&
- (0 != daemon->max_connections) )
- {
- /* only listen if we are not at the connection limit */
- p[poll_server].fd = daemon->socket_fd;
- p[poll_server].events = POLLIN;
- p[poll_server].revents = 0;
- poll_listen = (int) poll_server;
- poll_server++;
- }
- if (-1 != daemon->wpipe[0])
- {
- p[poll_server].fd = daemon->wpipe[0];
- p[poll_server].events = POLLIN;
- p[poll_server].revents = 0;
- poll_server++;
- }
- if (may_block == MHD_NO)
- timeout = 0;
- else if ( (0 != (daemon->options & MHD_USE_THREAD_PER_CONNECTION)) ||
- (MHD_YES != MHD_get_timeout (daemon, <imeout)) )
- timeout = -1;
- else
- timeout = (ltimeout > INT_MAX) ? INT_MAX : (int) ltimeout;
-
- i = 0;
- for (pos = daemon->connections_head; NULL != pos; pos = pos->next)
- {
- memset(&mp, 0, sizeof (struct MHD_Pollfd));
- MHD_connection_get_pollfd (pos, &mp);
- p[poll_server+i].fd = mp.fd;
- if (mp.events & MHD_POLL_ACTION_IN)
- p[poll_server+i].events |= POLLIN;
- if (mp.events & MHD_POLL_ACTION_OUT)
- p[poll_server+i].events |= POLLOUT;
- i++;
- }
- if (0 == poll_server + num_connections)
- return MHD_YES;
- if (poll (p, poll_server + num_connections, timeout) < 0)
- {
- if (EINTR == errno)
- return MHD_YES;
-#if HAVE_MESSAGES
- MHD_DLOG (daemon,
- "poll failed: %s\n",
- STRERROR (errno));
-#endif
- return MHD_NO;
- }
- /* handle shutdown */
- if (MHD_YES == daemon->shutdown)
- return MHD_NO;
- i = 0;
- next = daemon->connections_head;
- while (NULL != (pos = next))
- {
- next = pos->next;
- /* first, sanity checks */
- if (i >= num_connections)
- break; /* connection list changed somehow, retry later ... */
- MHD_connection_get_pollfd (pos, &mp);
- if (p[poll_server+i].fd != mp.fd)
- break; /* fd mismatch, something else happened, retry later ... */
-
- /* normal handling */
- if (0 != (p[poll_server+i].revents & POLLIN))
- pos->read_handler (pos);
- if (0 != (p[poll_server+i].revents & POLLOUT))
- pos->write_handler (pos);
- pos->idle_handler (pos);
- i++;
- }
- if ( (-1 != poll_listen) &&
- (0 != (p[poll_listen].revents & POLLIN)) )
- MHD_accept_connection (daemon);
- }
- return MHD_YES;
-}
-
-
-/**
- * Process only the listen socket using 'poll'.
- *
- * @param daemon daemon to run poll loop for
- * @param may_block YES if blocking, NO if non-blocking
- * @return MHD_NO on serious errors, MHD_YES on success
- */
-static int
-MHD_poll_listen_socket (struct MHD_Daemon *daemon,
- int may_block)
-{
- struct pollfd p[2];
- int timeout;
- unsigned int poll_count;
- int poll_listen;
-
- memset (&p, 0, sizeof (p));
- poll_count = 0;
- poll_listen = -1;
- if (-1 != daemon->socket_fd)
- {
- p[poll_count].fd = daemon->socket_fd;
- p[poll_count].events = POLLIN;
- p[poll_count].revents = 0;
- poll_listen = poll_count;
- poll_count++;
- }
- if (-1 != daemon->wpipe[0])
- {
- p[poll_count].fd = daemon->wpipe[0];
- p[poll_count].events = POLLIN;
- p[poll_count].revents = 0;
- poll_count++;
- }
- if (MHD_NO == may_block)
- timeout = 0;
- else
- timeout = -1;
- if (0 == poll_count)
- return MHD_YES;
- if (poll (p, poll_count, timeout) < 0)
- {
- if (EINTR == errno)
- return MHD_YES;
-#if HAVE_MESSAGES
- MHD_DLOG (daemon, "poll failed: %s\n", STRERROR (errno));
-#endif
- return MHD_NO;
- }
- /* handle shutdown */
- if (MHD_YES == daemon->shutdown)
- return MHD_NO;
- if ( (-1 != poll_listen) &&
- (0 != (p[poll_listen].revents & POLLIN)) )
- MHD_accept_connection (daemon);
- return MHD_YES;
-}
-#endif
-
-
-/**
- * Do 'poll'-based processing.
- *
- * @param daemon daemon to run poll loop for
- * @param may_block YES if blocking, NO if non-blocking
- * @return MHD_NO on serious errors, MHD_YES on success
- */
-static int
-MHD_poll (struct MHD_Daemon *daemon,
- int may_block)
-{
-#ifdef HAVE_POLL_H
- if (MHD_YES == daemon->shutdown)
- return MHD_NO;
- if (0 == (daemon->options & MHD_USE_THREAD_PER_CONNECTION))
- return MHD_poll_all (daemon, may_block);
- else
- return MHD_poll_listen_socket (daemon, may_block);
-#else
- return MHD_NO;
-#endif
-}
-
-
-/**
- * Run webserver operations (without blocking unless
- * in client callbacks). This method should be called
- * by clients in combination with MHD_get_fdset
- * if the client-controlled select method is used.
- *
- * This function will work for external 'poll' and 'select' mode.
- * However, if using external 'select' mode, you may want to
- * instead use 'MHD_run_from_select', as it is more efficient.
- *
- * @return MHD_YES on success, MHD_NO if this
- * daemon was not started with the right
- * options for this call.
- */
-int
-MHD_run (struct MHD_Daemon *daemon)
-{
- if ( (MHD_YES == daemon->shutdown) ||
- (0 != (daemon->options & MHD_USE_THREAD_PER_CONNECTION)) ||
- (0 != (daemon->options & MHD_USE_SELECT_INTERNALLY)) )
- return MHD_NO;
- if (0 == (daemon->options & MHD_USE_POLL))
- MHD_select (daemon, MHD_NO);
- else
- MHD_poll (daemon, MHD_NO);
- MHD_cleanup_connections (daemon);
- return MHD_YES;
-}
-
-
-/**
- * Thread that runs the select loop until the daemon
- * is explicitly shut down.
- *
- * @param cls 'struct MHD_Deamon' to run select loop in a thread for
- * @return always NULL (on shutdown)
- */
-static void *
-MHD_select_thread (void *cls)
-{
- struct MHD_Daemon *daemon = cls;
-
- while (MHD_YES != daemon->shutdown)
- {
- if (0 == (daemon->options & MHD_USE_POLL))
- MHD_select (daemon, MHD_YES);
- else
- MHD_poll (daemon, MHD_YES);
- MHD_cleanup_connections (daemon);
- }
- return NULL;
-}
-
-
-/**
- * Start a webserver on the given port.
- *
- * @param port port to bind to
- * @param apc callback to call to check which clients
- * will be allowed to connect
- * @param apc_cls extra argument to apc
- * @param dh default handler for all URIs
- * @param dh_cls extra argument to dh
- * @return NULL on error, handle to daemon on success
- */
-struct MHD_Daemon *
-MHD_start_daemon (unsigned int options,
- uint16_t port,
- MHD_AcceptPolicyCallback apc,
- void *apc_cls,
- MHD_AccessHandlerCallback dh, void *dh_cls, ...)
-{
- struct MHD_Daemon *daemon;
- va_list ap;
-
- va_start (ap, dh_cls);
- daemon = MHD_start_daemon_va (options, port, apc, apc_cls, dh, dh_cls, ap);
- va_end (ap);
- return daemon;
-}
-
-
-/**
- * Stop accepting connections from the listening socket. Allows
- * clients to continue processing, but stops accepting new
- * connections. Note that the caller is responsible for closing the
- * returned socket; however, if MHD is run using threads (anything but
- * external select mode), it must not be closed until AFTER
- * "MHD_stop_daemon" has been called (as it is theoretically possible
- * that an existing thread is still using it).
- *
- * @param daemon daemon to stop accepting new connections for
- * @return old listen socket on success, -1 if the daemon was
- * already not listening anymore
- */
-int
-MHD_quiesce_daemon (struct MHD_Daemon *daemon)
-{
- unsigned int i;
- int ret;
-
- ret = daemon->socket_fd;
- if (NULL != daemon->worker_pool)
- for (i = 0; i < daemon->worker_pool_size; i++)
- daemon->worker_pool[i].socket_fd = -1;
- daemon->socket_fd = -1;
- return ret;
-}
-
-
-/**
- * Signature of the MHD custom logger function.
- *
- * @param cls closure
- * @param format format string
- * @param va arguments to the format string (fprintf-style)
- */
-typedef void (*VfprintfFunctionPointerType)(void *cls,
- const char *format,
- va_list va);
-
-
-/**
- * Parse a list of options given as varargs.
- *
- * @param daemon the daemon to initialize
- * @param servaddr where to store the server's listen address
- * @param ap the options
- * @return MHD_YES on success, MHD_NO on error
- */
-static int
-parse_options_va (struct MHD_Daemon *daemon,
- const struct sockaddr **servaddr,
- va_list ap);
-
-
-/**
- * Parse a list of options given as varargs.
- *
- * @param daemon the daemon to initialize
- * @param servaddr where to store the server's listen address
- * @param ... the options
- * @return MHD_YES on success, MHD_NO on error
- */
-static int
-parse_options (struct MHD_Daemon *daemon,
- const struct sockaddr **servaddr,
- ...)
-{
- va_list ap;
- int ret;
-
- va_start (ap, servaddr);
- ret = parse_options_va (daemon, servaddr, ap);
- va_end (ap);
- return ret;
-}
-
-
-/**
- * Parse a list of options given as varargs.
- *
- * @param daemon the daemon to initialize
- * @param servaddr where to store the server's listen address
- * @param ap the options
- * @return MHD_YES on success, MHD_NO on error
- */
-static int
-parse_options_va (struct MHD_Daemon *daemon,
- const struct sockaddr **servaddr,
- va_list ap)
-{
- enum MHD_OPTION opt;
- struct MHD_OptionItem *oa;
- unsigned int i;
-#if HTTPS_SUPPORT
- int ret;
- const char *pstr;
-#endif
-
- while (MHD_OPTION_END != (opt = (enum MHD_OPTION) va_arg (ap, int)))
- {
- switch (opt)
- {
- case MHD_OPTION_CONNECTION_MEMORY_LIMIT:
- daemon->pool_size = va_arg (ap, size_t);
- break;
- case MHD_OPTION_CONNECTION_LIMIT:
- daemon->max_connections = va_arg (ap, unsigned int);
- break;
- case MHD_OPTION_CONNECTION_TIMEOUT:
- daemon->connection_timeout = va_arg (ap, unsigned int);
- break;
- case MHD_OPTION_NOTIFY_COMPLETED:
- daemon->notify_completed =
- va_arg (ap, MHD_RequestCompletedCallback);
- daemon->notify_completed_cls = va_arg (ap, void *);
- break;
- case MHD_OPTION_PER_IP_CONNECTION_LIMIT:
- daemon->per_ip_connection_limit = va_arg (ap, unsigned int);
- break;
- case MHD_OPTION_SOCK_ADDR:
- *servaddr = va_arg (ap, const struct sockaddr *);
- break;
- case MHD_OPTION_URI_LOG_CALLBACK:
- daemon->uri_log_callback =
- va_arg (ap, LogCallback);
- daemon->uri_log_callback_cls = va_arg (ap, void *);
- break;
- case MHD_OPTION_THREAD_POOL_SIZE:
- daemon->worker_pool_size = va_arg (ap, unsigned int);
- if (daemon->worker_pool_size >= (SIZE_MAX / sizeof (struct
MHD_Daemon)))
- {
-#if HAVE_MESSAGES
- MHD_DLOG (daemon,
- "Specified thread pool size (%u) too big\n",
- daemon->worker_pool_size);
-#endif
- return MHD_NO;
- }
- break;
-#if HTTPS_SUPPORT
- case MHD_OPTION_HTTPS_MEM_KEY:
- if (0 != (daemon->options & MHD_USE_SSL))
- daemon->https_mem_key = va_arg (ap, const char *);
-#if HAVE_MESSAGES
- else
- MHD_DLOG (daemon,
- "MHD HTTPS option %d passed to MHD but MHD_USE_SSL not
set\n",
- opt);
-#endif
- break;
- case MHD_OPTION_HTTPS_MEM_CERT:
- if (0 != (daemon->options & MHD_USE_SSL))
- daemon->https_mem_cert = va_arg (ap, const char *);
-#if HAVE_MESSAGES
- else
- MHD_DLOG (daemon,
- "MHD HTTPS option %d passed to MHD but MHD_USE_SSL not
set\n",
- opt);
-#endif
- break;
- case MHD_OPTION_HTTPS_MEM_TRUST:
- if (0 != (daemon->options & MHD_USE_SSL))
- daemon->https_mem_trust = va_arg (ap, const char *);
-#if HAVE_MESSAGES
- else
- MHD_DLOG (daemon,
- "MHD HTTPS option %d passed to MHD but MHD_USE_SSL not
set\n",
- opt);
-#endif
- break;
- case MHD_OPTION_HTTPS_CRED_TYPE:
- daemon->cred_type = (gnutls_credentials_type_t) va_arg (ap, int);
- break;
- case MHD_OPTION_HTTPS_PRIORITIES:
- if (0 != (daemon->options & MHD_USE_SSL))
- {
- gnutls_priority_deinit (daemon->priority_cache);
- ret = gnutls_priority_init (&daemon->priority_cache,
- pstr = va_arg (ap, const char*),
- NULL);
- if (ret != GNUTLS_E_SUCCESS)
- {
-#if HAVE_MESSAGES
- MHD_DLOG (daemon,
- "Setting priorities to `%s' failed: %s\n",
- pstr,
- gnutls_strerror (ret));
-#endif
- daemon->priority_cache = NULL;
- return MHD_NO;
- }
- }
- break;
-#endif
-#ifdef DAUTH_SUPPORT
- case MHD_OPTION_DIGEST_AUTH_RANDOM:
- daemon->digest_auth_rand_size = va_arg (ap, size_t);
- daemon->digest_auth_random = va_arg (ap, const char *);
- break;
- case MHD_OPTION_NONCE_NC_SIZE:
- daemon->nonce_nc_size = va_arg (ap, unsigned int);
- break;
-#endif
- case MHD_OPTION_LISTEN_SOCKET:
- daemon->socket_fd = va_arg (ap, int);
- break;
- case MHD_OPTION_EXTERNAL_LOGGER:
-#if HAVE_MESSAGES
- daemon->custom_error_log =
- va_arg (ap, VfprintfFunctionPointerType);
- daemon->custom_error_log_cls = va_arg (ap, void *);
-#else
- va_arg (ap, VfprintfFunctionPointerType);
- va_arg (ap, void *);
-#endif
- break;
- case MHD_OPTION_THREAD_STACK_SIZE:
- daemon->thread_stack_size = va_arg (ap, size_t);
- break;
- case MHD_OPTION_ARRAY:
- oa = va_arg (ap, struct MHD_OptionItem*);
- i = 0;
- while (MHD_OPTION_END != (opt = oa[i].option))
- {
- switch (opt)
- {
- /* all options taking 'size_t' */
- case MHD_OPTION_CONNECTION_MEMORY_LIMIT:
- case MHD_OPTION_THREAD_STACK_SIZE:
- if (MHD_YES != parse_options (daemon,
- servaddr,
- opt,
- (size_t) oa[i].value,
- MHD_OPTION_END))
- return MHD_NO;
- break;
- /* all options taking 'unsigned int' */
- case MHD_OPTION_NONCE_NC_SIZE:
- case MHD_OPTION_CONNECTION_LIMIT:
- case MHD_OPTION_CONNECTION_TIMEOUT:
- case MHD_OPTION_PER_IP_CONNECTION_LIMIT:
- case MHD_OPTION_THREAD_POOL_SIZE:
- if (MHD_YES != parse_options (daemon,
- servaddr,
- opt,
- (unsigned int) oa[i].value,
- MHD_OPTION_END))
- return MHD_NO;
- break;
- /* all options taking 'int' or 'enum' */
- case MHD_OPTION_HTTPS_CRED_TYPE:
- case MHD_OPTION_LISTEN_SOCKET:
- if (MHD_YES != parse_options (daemon,
- servaddr,
- opt,
- (int) oa[i].value,
- MHD_OPTION_END))
- return MHD_NO;
- break;
- /* all options taking one pointer */
- case MHD_OPTION_SOCK_ADDR:
- case MHD_OPTION_HTTPS_MEM_KEY:
- case MHD_OPTION_HTTPS_MEM_CERT:
- case MHD_OPTION_HTTPS_MEM_TRUST:
- case MHD_OPTION_HTTPS_PRIORITIES:
- case MHD_OPTION_ARRAY:
- if (MHD_YES != parse_options (daemon,
- servaddr,
- opt,
- oa[i].ptr_value,
- MHD_OPTION_END))
- return MHD_NO;
- break;
- /* all options taking two pointers */
- case MHD_OPTION_NOTIFY_COMPLETED:
- case MHD_OPTION_URI_LOG_CALLBACK:
- case MHD_OPTION_EXTERNAL_LOGGER:
- case MHD_OPTION_UNESCAPE_CALLBACK:
- if (MHD_YES != parse_options (daemon,
- servaddr,
- opt,
- (void *) oa[i].value,
- oa[i].ptr_value,
- MHD_OPTION_END))
- return MHD_NO;
- break;
- /* options taking size_t-number followed by pointer */
- case MHD_OPTION_DIGEST_AUTH_RANDOM:
- if (MHD_YES != parse_options (daemon,
- servaddr,
- opt,
- (size_t) oa[i].value,
- oa[i].ptr_value,
- MHD_OPTION_END))
- return MHD_NO;
- break;
- default:
- return MHD_NO;
- }
- i++;
- }
- break;
- case MHD_OPTION_UNESCAPE_CALLBACK:
- daemon->unescape_callback =
- va_arg (ap, UnescapeCallback);
- daemon->unescape_callback_cls = va_arg (ap, void *);
- break;
- default:
-#if HAVE_MESSAGES
- if (((opt >= MHD_OPTION_HTTPS_MEM_KEY) &&
- (opt <= MHD_OPTION_HTTPS_PRIORITIES)) || (opt ==
MHD_OPTION_HTTPS_MEM_TRUST))
- {
- MHD_DLOG (daemon,
- "MHD HTTPS option %d passed to MHD compiled without
HTTPS support\n",
- opt);
- }
- else
- {
- MHD_DLOG (daemon,
- "Invalid option %d! (Did you terminate the list with
MHD_OPTION_END?)\n",
- opt);
- }
-#endif
- return MHD_NO;
- }
- }
- return MHD_YES;
-}
-
-
-/**
- * Create a listen socket, if possible with CLOEXEC flag set.
- *
- * @param domain socket domain (i.e. PF_INET)
- * @param type socket type (usually SOCK_STREAM)
- * @param protocol desired protocol, 0 for default
- */
-static int
-create_socket (int domain, int type, int protocol)
-{
- static int sock_cloexec = SOCK_CLOEXEC;
- int ctype = SOCK_STREAM | sock_cloexec;
- int fd;
- int flags;
-#ifdef WINDOWS
- DWORD dwFlags;
-#endif
-
- /* use SOCK_STREAM rather than ai_socktype: some getaddrinfo
- * implementations do not set ai_socktype, e.g. RHL6.2. */
- fd = SOCKET (domain, ctype, protocol);
- if ( (-1 == fd) && (EINVAL == errno) && (0 != sock_cloexec) )
- {
- sock_cloexec = 0;
- fd = SOCKET(domain, type, protocol);
- }
- if (-1 == fd)
- return -1;
- if (0 != sock_cloexec)
- return fd; /* this is it */
- /* flag was not set during 'socket' call, let's try setting it manually */
-#ifndef WINDOWS
- flags = fcntl (fd, F_GETFD);
- if (flags < 0)
-#else
- if (!GetHandleInformation ((HANDLE) fd, &dwFlags))
-#endif
- {
-#ifdef WINDOWS
- SetErrnoFromWinError (GetLastError ());
-#endif
- return fd; /* good luck */
- }
-#ifndef WINDOWS
- if (flags == (flags | FD_CLOEXEC))
- return fd; /* already set */
- flags |= FD_CLOEXEC;
- if (0 != fcntl (fd, F_SETFD, flags))
-#else
- if (dwFlags != (dwFlags | HANDLE_FLAG_INHERIT))
- return fd; /* already unset */
- if (!SetHandleInformation ((HANDLE) fd, HANDLE_FLAG_INHERIT, 0))
-#endif
- {
-#ifdef WINDOWS
- SetErrnoFromWinError (GetLastError ());
-#endif
- return fd; /* good luck */
- }
- return fd;
-}
-
-
-/**
- * Start a webserver on the given port.
- *
- * @param port port to bind to
- * @param apc callback to call to check which clients
- * will be allowed to connect
- * @param apc_cls extra argument to apc
- * @param dh default handler for all URIs
- * @param dh_cls extra argument to dh
- * @return NULL on error, handle to daemon on success
- */
-struct MHD_Daemon *
-MHD_start_daemon_va (unsigned int options,
- uint16_t port,
- MHD_AcceptPolicyCallback apc,
- void *apc_cls,
- MHD_AccessHandlerCallback dh, void *dh_cls,
- va_list ap)
-{
- const int on = 1;
- struct MHD_Daemon *daemon;
- int socket_fd;
- struct sockaddr_in servaddr4;
-#if HAVE_INET6
- struct sockaddr_in6 servaddr6;
-#endif
- const struct sockaddr *servaddr = NULL;
- socklen_t addrlen;
- unsigned int i;
- int res_thread_create;
- int use_pipe;
-
-#ifndef HAVE_INET6
- if (0 != (options & MHD_USE_IPv6))
- return NULL;
-#endif
-#ifndef HAVE_POLL_H
- if (0 != (options & MHD_USE_POLL))
- return NULL;
-#endif
-#if ! HTTPS_SUPPORT
- if (0 != (options & MHD_USE_SSL))
- return NULL;
-#endif
- if (NULL == dh)
- return NULL;
- if (NULL == (daemon = malloc (sizeof (struct MHD_Daemon))))
- return NULL;
- memset (daemon, 0, sizeof (struct MHD_Daemon));
- /* try to open listen socket */
-#if HTTPS_SUPPORT
- if (0 != (options & MHD_USE_SSL))
- {
- gnutls_priority_init (&daemon->priority_cache,
- "NORMAL",
- NULL);
- }
-#endif
- daemon->socket_fd = -1;
- daemon->options = (enum MHD_OPTION) options;
- daemon->port = port;
- daemon->apc = apc;
- daemon->apc_cls = apc_cls;
- daemon->default_handler = dh;
- daemon->default_handler_cls = dh_cls;
- daemon->max_connections = MHD_MAX_CONNECTIONS_DEFAULT;
- daemon->pool_size = MHD_POOL_SIZE_DEFAULT;
- daemon->unescape_callback = &MHD_http_unescape;
- daemon->connection_timeout = 0; /* no timeout */
- daemon->wpipe[0] = -1;
- daemon->wpipe[1] = -1;
-#if HAVE_MESSAGES
- daemon->custom_error_log = (MHD_LogCallback) &vfprintf;
- daemon->custom_error_log_cls = stderr;
-#endif
-#ifdef HAVE_LISTEN_SHUTDOWN
- use_pipe = (0 != (daemon->options & MHD_USE_NO_LISTEN_SOCKET));
-#else
- use_pipe = 1; /* yes, must use pipe to signal shutdown */
-#endif
- if ( (use_pipe) &&
- (0 != PIPE (daemon->wpipe)) )
- {
-#if HAVE_MESSAGES
- MHD_DLOG (daemon,
- "Failed to create control pipe: %s\n",
- STRERROR (errno));
-#endif
- free (daemon);
- return NULL;
- }
-#ifndef WINDOWS
- if ( (0 == (options & MHD_USE_POLL)) &&
- (daemon->wpipe[0] >= FD_SETSIZE) )
- {
-#if HAVE_MESSAGES
- MHD_DLOG (daemon,
- "file descriptor for control pipe exceeds maximum value\n");
-#endif
- CLOSE (daemon->wpipe[0]);
- CLOSE (daemon->wpipe[1]);
- free (daemon);
- return NULL;
- }
-#endif
-#ifdef DAUTH_SUPPORT
- daemon->digest_auth_rand_size = 0;
- daemon->digest_auth_random = NULL;
- daemon->nonce_nc_size = 4; /* tiny */
-#endif
-#if HTTPS_SUPPORT
- if (0 != (options & MHD_USE_SSL))
- {
- daemon->cred_type = GNUTLS_CRD_CERTIFICATE;
- }
-#endif
-
-
- if (MHD_YES != parse_options_va (daemon, &servaddr, ap))
- {
-#if HTTPS_SUPPORT
- if ( (0 != (options & MHD_USE_SSL)) &&
- (NULL != daemon->priority_cache) )
- gnutls_priority_deinit (daemon->priority_cache);
-#endif
- free (daemon);
- return NULL;
- }
-#ifdef DAUTH_SUPPORT
- if (daemon->nonce_nc_size > 0)
- {
- if ( ( (size_t) (daemon->nonce_nc_size * sizeof(struct MHD_NonceNc))) /
- sizeof(struct MHD_NonceNc) != daemon->nonce_nc_size)
- {
-#if HAVE_MESSAGES
- MHD_DLOG (daemon,
- "Specified value for NC_SIZE too large\n");
-#endif
-#if HTTPS_SUPPORT
- if (0 != (options & MHD_USE_SSL))
- gnutls_priority_deinit (daemon->priority_cache);
-#endif
- free (daemon);
- return NULL;
- }
- daemon->nnc = malloc (daemon->nonce_nc_size * sizeof(struct
MHD_NonceNc));
- if (NULL == daemon->nnc)
- {
-#if HAVE_MESSAGES
- MHD_DLOG (daemon,
- "Failed to allocate memory for nonce-nc map: %s\n",
- STRERROR (errno));
-#endif
-#if HTTPS_SUPPORT
- if (0 != (options & MHD_USE_SSL))
- gnutls_priority_deinit (daemon->priority_cache);
-#endif
- free (daemon);
- return NULL;
- }
- }
-
- if (0 != pthread_mutex_init (&daemon->nnc_lock, NULL))
- {
-#if HAVE_MESSAGES
- MHD_DLOG (daemon,
- "MHD failed to initialize nonce-nc mutex\n");
-#endif
-#if HTTPS_SUPPORT
- if (0 != (options & MHD_USE_SSL))
- gnutls_priority_deinit (daemon->priority_cache);
-#endif
- free (daemon->nnc);
- free (daemon);
- return NULL;
- }
-#endif
-
- /* Thread pooling currently works only with internal select thread model */
- if ( (0 == (options & MHD_USE_SELECT_INTERNALLY)) &&
- (daemon->worker_pool_size > 0) )
- {
-#if HAVE_MESSAGES
- MHD_DLOG (daemon,
- "MHD thread pooling only works with
MHD_USE_SELECT_INTERNALLY\n");
-#endif
- goto free_and_fail;
- }
-
-#ifdef __SYMBIAN32__
- if (0 != (options & (MHD_USE_SELECT_INTERNALLY |
MHD_USE_THREAD_PER_CONNECTION)))
- {
-#if HAVE_MESSAGES
- MHD_DLOG (daemon,
- "Threaded operations are not supported on Symbian.\n");
-#endif
- goto free_and_fail;
- }
-#endif
- if ( (-1 == daemon->socket_fd) &&
- (0 == (daemon->options & MHD_USE_NO_LISTEN_SOCKET)) )
- {
- /* try to open listen socket */
- if ((options & MHD_USE_IPv6) != 0)
- socket_fd = create_socket (PF_INET6, SOCK_STREAM, 0);
- else
- socket_fd = create_socket (PF_INET, SOCK_STREAM, 0);
- if (-1 == socket_fd)
- {
-#if HAVE_MESSAGES
- if (0 != (options & MHD_USE_DEBUG))
- MHD_DLOG (daemon,
- "Call to socket failed: %s\n",
- STRERROR (errno));
-#endif
- goto free_and_fail;
- }
- if ((SETSOCKOPT (socket_fd,
- SOL_SOCKET,
- SO_REUSEADDR,
- &on, sizeof (on)) < 0) && ((options & MHD_USE_DEBUG) !=
0))
- {
-#if HAVE_MESSAGES
- MHD_DLOG (daemon,
- "setsockopt failed: %s\n",
- STRERROR (errno));
-#endif
- }
-
- /* check for user supplied sockaddr */
-#if HAVE_INET6
- if (0 != (options & MHD_USE_IPv6))
- addrlen = sizeof (struct sockaddr_in6);
- else
-#endif
- addrlen = sizeof (struct sockaddr_in);
- if (NULL == servaddr)
- {
-#if HAVE_INET6
- if (0 != (options & MHD_USE_IPv6))
- {
- memset (&servaddr6, 0, sizeof (struct sockaddr_in6));
- servaddr6.sin6_family = AF_INET6;
- servaddr6.sin6_port = htons (port);
-#if HAVE_SOCKADDR_IN_SIN_LEN
- servaddr6.sin6_len = sizeof (struct sockaddr_in6);
-#endif
- servaddr = (struct sockaddr *) &servaddr6;
- }
- else
-#endif
- {
- memset (&servaddr4, 0, sizeof (struct sockaddr_in));
- servaddr4.sin_family = AF_INET;
- servaddr4.sin_port = htons (port);
-#if HAVE_SOCKADDR_IN_SIN_LEN
- servaddr4.sin_len = sizeof (struct sockaddr_in);
-#endif
- servaddr = (struct sockaddr *) &servaddr4;
- }
- }
- daemon->socket_fd = socket_fd;
-
- if (0 != (options & MHD_USE_IPv6))
- {
-#ifdef IPPROTO_IPV6
-#ifdef IPV6_V6ONLY
- /* Note: "IPV6_V6ONLY" is declared by Windows Vista ff., see
"IPPROTO_IPV6 Socket Options"
-
(http://msdn.microsoft.com/en-us/library/ms738574%28v=VS.85%29.aspx);
- and may also be missing on older POSIX systems; good luck if you
have any of those,
- your IPv6 socket may then also bind against IPv4... */
-#ifndef WINDOWS
- const int on = 1;
- setsockopt (socket_fd,
- IPPROTO_IPV6, IPV6_V6ONLY,
- &on, sizeof (on));
-#else
- const char on = 1;
- setsockopt (socket_fd,
- IPPROTO_IPV6, IPV6_V6ONLY,
- &on, sizeof (on));
-#endif
-#endif
-#endif
- }
- if (-1 == BIND (socket_fd, servaddr, addrlen))
- {
-#if HAVE_MESSAGES
- if (0 != (options & MHD_USE_DEBUG))
- MHD_DLOG (daemon,
- "Failed to bind to port %u: %s\n",
- (unsigned int) port,
- STRERROR (errno));
-#endif
- CLOSE (socket_fd);
- goto free_and_fail;
- }
-
- if (LISTEN (socket_fd, 20) < 0)
- {
-#if HAVE_MESSAGES
- if (0 != (options & MHD_USE_DEBUG))
- MHD_DLOG (daemon,
- "Failed to listen for connections: %s\n",
- STRERROR (errno));
-#endif
- CLOSE (socket_fd);
- goto free_and_fail;
- }
- }
- else
- {
- socket_fd = daemon->socket_fd;
- }
-#ifndef WINDOWS
- if ( (socket_fd >= FD_SETSIZE) &&
- (0 == (options & MHD_USE_POLL)) )
- {
-#if HAVE_MESSAGES
- if ((options & MHD_USE_DEBUG) != 0)
- MHD_DLOG (daemon,
- "Socket descriptor larger than FD_SETSIZE: %d > %d\n",
- socket_fd,
- FD_SETSIZE);
-#endif
- CLOSE (socket_fd);
- goto free_and_fail;
- }
-#endif
-
- if (0 != pthread_mutex_init (&daemon->per_ip_connection_mutex, NULL))
- {
-#if HAVE_MESSAGES
- MHD_DLOG (daemon,
- "MHD failed to initialize IP connection limit mutex\n");
-#endif
- if (-1 != socket_fd)
- CLOSE (socket_fd);
- goto free_and_fail;
- }
- if (0 != pthread_mutex_init (&daemon->cleanup_connection_mutex, NULL))
- {
-#if HAVE_MESSAGES
- MHD_DLOG (daemon,
- "MHD failed to initialize IP connection limit mutex\n");
-#endif
- pthread_mutex_destroy (&daemon->cleanup_connection_mutex);
- if (-1 != socket_fd)
- CLOSE (socket_fd);
- goto free_and_fail;
- }
-
-#if HTTPS_SUPPORT
- /* initialize HTTPS daemon certificate aspects & send / recv functions */
- if ((0 != (options & MHD_USE_SSL)) && (0 != MHD_TLS_init (daemon)))
- {
-#if HAVE_MESSAGES
- MHD_DLOG (daemon,
- "Failed to initialize TLS support\n");
-#endif
- if (-1 != socket_fd)
- CLOSE (socket_fd);
- pthread_mutex_destroy (&daemon->cleanup_connection_mutex);
- pthread_mutex_destroy (&daemon->per_ip_connection_mutex);
- goto free_and_fail;
- }
-#endif
- if ( ( (0 != (options & MHD_USE_THREAD_PER_CONNECTION)) ||
- ( (0 != (options & MHD_USE_SELECT_INTERNALLY)) &&
- (0 == daemon->worker_pool_size)) ) &&
- (0 == (daemon->options & MHD_USE_NO_LISTEN_SOCKET)) &&
- (0 != (res_thread_create =
- create_thread (&daemon->pid, daemon, &MHD_select_thread,
daemon))))
- {
-#if HAVE_MESSAGES
- MHD_DLOG (daemon,
- "Failed to create listen thread: %s\n",
- STRERROR (res_thread_create));
-#endif
- pthread_mutex_destroy (&daemon->cleanup_connection_mutex);
- pthread_mutex_destroy (&daemon->per_ip_connection_mutex);
- if (-1 != socket_fd)
- CLOSE (socket_fd);
- goto free_and_fail;
- }
- if ( (daemon->worker_pool_size > 0) &&
- (0 == (daemon->options & MHD_USE_NO_LISTEN_SOCKET)) )
- {
-#ifndef MINGW
- int sk_flags;
-#else
- unsigned long sk_flags;
-#endif
-
- /* Coarse-grained count of connections per thread (note error
- * due to integer division). Also keep track of how many
- * connections are leftover after an equal split. */
- unsigned int conns_per_thread = daemon->max_connections
- / daemon->worker_pool_size;
- unsigned int leftover_conns = daemon->max_connections
- % daemon->worker_pool_size;
-
- i = 0; /* we need this in case fcntl or malloc fails */
-
- /* Accept must be non-blocking. Multiple children may wake up
- * to handle a new connection, but only one will win the race.
- * The others must immediately return. */
-#ifndef MINGW
- sk_flags = fcntl (socket_fd, F_GETFL);
- if (sk_flags < 0)
- goto thread_failed;
- if (0 != fcntl (socket_fd, F_SETFL, sk_flags | O_NONBLOCK))
- goto thread_failed;
-#else
- sk_flags = 1;
-#if HAVE_PLIBC_FD
- if (SOCKET_ERROR ==
- ioctlsocket (plibc_fd_get_handle (socket_fd), FIONBIO, &sk_flags))
- goto thread_failed;
-#else
- if (ioctlsocket (socket_fd, FIONBIO, &sk_flags) == SOCKET_ERROR)
- goto thread_failed;
-#endif // PLIBC_FD
-#endif // MINGW
-
- /* Allocate memory for pooled objects */
- daemon->worker_pool = malloc (sizeof (struct MHD_Daemon)
- * daemon->worker_pool_size);
- if (NULL == daemon->worker_pool)
- goto thread_failed;
-
- /* Start the workers in the pool */
- for (i = 0; i < daemon->worker_pool_size; ++i)
- {
- /* Create copy of the Daemon object for each worker */
- struct MHD_Daemon *d = &daemon->worker_pool[i];
- memcpy (d, daemon, sizeof (struct MHD_Daemon));
-
- /* Adjust pooling params for worker daemons; note that memcpy()
- has already copied MHD_USE_SELECT_INTERNALLY thread model into
- the worker threads. */
- d->master = daemon;
- d->worker_pool_size = 0;
- d->worker_pool = NULL;
-
- /* Divide available connections evenly amongst the threads.
- * Thread indexes in [0, leftover_conns) each get one of the
- * leftover connections. */
- d->max_connections = conns_per_thread;
- if (i < leftover_conns)
- ++d->max_connections;
-
- /* Must init cleanup connection mutex for each worker */
- if (0 != pthread_mutex_init (&d->cleanup_connection_mutex, NULL))
- {
-#if HAVE_MESSAGES
- MHD_DLOG (daemon,
- "MHD failed to initialize cleanup connection mutex for
thread worker %d\n", i);
-#endif
- goto thread_failed;
- }
-
- /* Spawn the worker thread */
- if (0 != (res_thread_create = create_thread (&d->pid, daemon,
&MHD_select_thread, d)))
- {
-#if HAVE_MESSAGES
- MHD_DLOG (daemon,
- "Failed to create pool thread: %s\n",
- STRERROR (res_thread_create));
-#endif
- /* Free memory for this worker; cleanup below handles
- * all previously-created workers. */
- pthread_mutex_destroy (&d->cleanup_connection_mutex);
- goto thread_failed;
- }
- }
- }
- return daemon;
-
-thread_failed:
- /* If no worker threads created, then shut down normally. Calling
- MHD_stop_daemon (as we do below) doesn't work here since it
- assumes a 0-sized thread pool means we had been in the default
- MHD_USE_SELECT_INTERNALLY mode. */
- if (0 == i)
- {
- if (-1 != socket_fd)
- CLOSE (socket_fd);
- pthread_mutex_destroy (&daemon->cleanup_connection_mutex);
- pthread_mutex_destroy (&daemon->per_ip_connection_mutex);
- if (NULL != daemon->worker_pool)
- free (daemon->worker_pool);
- goto free_and_fail;
- }
-
- /* Shutdown worker threads we've already created. Pretend
- as though we had fully initialized our daemon, but
- with a smaller number of threads than had been
- requested. */
- daemon->worker_pool_size = i - 1;
- MHD_stop_daemon (daemon);
- return NULL;
-
- free_and_fail:
- /* clean up basic memory state in 'daemon' and return NULL to
- indicate failure */
-#ifdef DAUTH_SUPPORT
- free (daemon->nnc);
- pthread_mutex_destroy (&daemon->nnc_lock);
-#endif
-#if HTTPS_SUPPORT
- if (0 != (options & MHD_USE_SSL))
- gnutls_priority_deinit (daemon->priority_cache);
-#endif
- free (daemon);
- return NULL;
-}
-
-
-/**
- * Close all connections for the daemon; must only be called after
- * all of the threads have been joined and there is no more concurrent
- * activity on the connection lists.
- *
- * @param daemon daemon to close down
- */
-static void
-close_all_connections (struct MHD_Daemon *daemon)
-{
- struct MHD_Connection *pos;
- void *unused;
- int rc;
-
- /* first, make sure all threads are aware of shutdown; need to
- traverse DLLs in peace... */
- if (0 != pthread_mutex_lock(&daemon->cleanup_connection_mutex))
- {
- MHD_PANIC ("Failed to acquire cleanup mutex\n");
- }
- for (pos = daemon->connections_head; NULL != pos; pos = pos->next)
- SHUTDOWN (pos->socket_fd,
- (pos->read_closed == MHD_YES) ? SHUT_WR : SHUT_RDWR);
- if (0 != pthread_mutex_unlock (&daemon->cleanup_connection_mutex))
- {
- MHD_PANIC ("Failed to release cleanup mutex\n");
- }
-
- /* now, collect threads */
- if (0 != (daemon->options & MHD_USE_THREAD_PER_CONNECTION))
- {
- while (NULL != (pos = daemon->connections_head))
- {
- if (0 != (rc = pthread_join (pos->pid, &unused)))
- {
- MHD_PANIC ("Failed to join a thread\n");
- }
- pos->thread_joined = MHD_YES;
- }
- }
-
- /* now that we're alone, move everyone to cleanup */
- while (NULL != (pos = daemon->connections_head))
- {
- MHD_connection_close (pos,
- MHD_REQUEST_TERMINATED_DAEMON_SHUTDOWN);
- DLL_remove (daemon->connections_head,
- daemon->connections_tail,
- pos);
- DLL_insert (daemon->cleanup_head,
- daemon->cleanup_tail,
- pos);
- }
- MHD_cleanup_connections (daemon);
-}
-
-
-/**
- * Shutdown an http daemon
- *
- * @param daemon daemon to stop
- */
-void
-MHD_stop_daemon (struct MHD_Daemon *daemon)
-{
- void *unused;
- int fd;
- unsigned int i;
- int rc;
-
- if (NULL == daemon)
- return;
- daemon->shutdown = MHD_YES;
- fd = daemon->socket_fd;
- daemon->socket_fd = -1;
- /* Prepare workers for shutdown */
- if (NULL != daemon->worker_pool)
- {
- /* MHD_USE_NO_LISTEN_SOCKET disables thread pools, hence we need to
check */
- for (i = 0; i < daemon->worker_pool_size; ++i)
- {
- daemon->worker_pool[i].shutdown = MHD_YES;
- daemon->worker_pool[i].socket_fd = -1;
- }
- }
- if (-1 != daemon->wpipe[1])
- {
- if (1 != WRITE (daemon->wpipe[1], "e", 1))
- MHD_PANIC ("failed to signal shutdownn via pipe");
- }
-#ifdef HAVE_LISTEN_SHUTDOWN
- else
- {
- /* fd might be -1 here due to 'MHD_quiesce_daemon' */
- if (-1 != fd)
- (void) SHUTDOWN (fd, SHUT_RDWR);
- }
-#endif
-#if DEBUG_CLOSE
-#if HAVE_MESSAGES
- MHD_DLOG (daemon, "MHD listen socket shutdown\n");
-#endif
-#endif
-
-
- /* Signal workers to stop and clean them up */
- if (NULL != daemon->worker_pool)
- {
- /* MHD_USE_NO_LISTEN_SOCKET disables thread pools, hence we need to
check */
- for (i = 0; i < daemon->worker_pool_size; ++i)
- {
- if (0 != (rc = pthread_join (daemon->worker_pool[i].pid, &unused)))
- {
- MHD_PANIC ("Failed to join a thread\n");
- }
- close_all_connections (&daemon->worker_pool[i]);
- pthread_mutex_destroy
(&daemon->worker_pool[i].cleanup_connection_mutex);
- }
- free (daemon->worker_pool);
- }
- else
- {
- /* clean up master threads */
- if ((0 != (daemon->options & MHD_USE_THREAD_PER_CONNECTION)) ||
- ((0 != (daemon->options & MHD_USE_SELECT_INTERNALLY))
- && (0 == daemon->worker_pool_size)))
- {
- if (0 != (rc = pthread_join (daemon->pid, &unused)))
- {
- MHD_PANIC ("Failed to join a thread\n");
- }
- }
- }
- close_all_connections (daemon);
- if (-1 != fd)
- (void) CLOSE (fd);
-
- /* TLS clean up */
-#if HTTPS_SUPPORT
- if (0 != (daemon->options & MHD_USE_SSL))
- {
- gnutls_priority_deinit (daemon->priority_cache);
- if (daemon->x509_cred)
- gnutls_certificate_free_credentials (daemon->x509_cred);
- }
-#endif
-
-#ifdef DAUTH_SUPPORT
- free (daemon->nnc);
- pthread_mutex_destroy (&daemon->nnc_lock);
-#endif
- pthread_mutex_destroy (&daemon->per_ip_connection_mutex);
- pthread_mutex_destroy (&daemon->cleanup_connection_mutex);
-
- if (-1 != daemon->wpipe[1])
- {
- (void) CLOSE (daemon->wpipe[0]);
- (void) CLOSE (daemon->wpipe[1]);
- }
- free (daemon);
-}
-
-
-/**
- * Obtain information about the given daemon
- * (not fully implemented!).
- *
- * @param daemon what daemon to get information about
- * @param infoType what information is desired?
- * @param ... depends on infoType
- * @return NULL if this information is not available
- * (or if the infoType is unknown)
- */
-const union MHD_DaemonInfo *
-MHD_get_daemon_info (struct MHD_Daemon *daemon,
- enum MHD_DaemonInfoType infoType, ...)
-{
- switch (infoType)
- {
- case MHD_DAEMON_INFO_LISTEN_FD:
- return (const union MHD_DaemonInfo *) &daemon->socket_fd;
- default:
- return NULL;
- };
-}
-
-
-/**
- * Sets the global error handler to a different implementation. "cb"
- * will only be called in the case of typically fatal, serious
- * internal consistency issues. These issues should only arise in the
- * case of serious memory corruption or similar problems with the
- * architecture. While "cb" is allowed to return and MHD will then
- * try to continue, this is never safe.
- *
- * The default implementation that is used if no panic function is set
- * simply prints an error message and calls "abort". Alternative
- * implementations might call "exit" or other similar functions.
- *
- * @param cb new error handler
- * @param cls passed to error handler
- */
-void
-MHD_set_panic_func (MHD_PanicCallback cb, void *cls)
-{
- mhd_panic = cb;
- mhd_panic_cls = cls;
-}
-
-
-/**
- * Obtain the version of this library
- *
- * @return static version string, e.g. "0.4.1"
- */
-const char *
-MHD_get_version (void)
-{
- return PACKAGE_VERSION;
-}
-
-
-#ifdef __GNUC__
-#define ATTRIBUTE_CONSTRUCTOR __attribute__ ((constructor))
-#define ATTRIBUTE_DESTRUCTOR __attribute__ ((destructor))
-#else // !__GNUC__
-#define ATTRIBUTE_CONSTRUCTOR
-#define ATTRIBUTE_DESTRUCTOR
-#endif // __GNUC__
-
-#if HTTPS_SUPPORT
-GCRY_THREAD_OPTION_PTHREAD_IMPL;
-#endif
-
-
-/**
- * Initialize do setup work.
- */
-void ATTRIBUTE_CONSTRUCTOR
-MHD_init ()
-{
- mhd_panic = &mhd_panic_std;
- mhd_panic_cls = NULL;
-
-#ifdef WINDOWS
- plibc_init ("GNU", "libmicrohttpd");
-#endif
-#if HTTPS_SUPPORT
- gcry_control (GCRYCTL_SET_THREAD_CBS, &gcry_threads_pthread);
- gnutls_global_init ();
-#endif
-}
-
-
-void ATTRIBUTE_DESTRUCTOR
-MHD_fini ()
-{
-#if HTTPS_SUPPORT
- gnutls_global_deinit ();
-#endif
-#ifdef WINDOWS
- plibc_shutdown ();
-#endif
-}
-
-/* end of daemon.c */
Copied: libmicrohttpd/src/microhttpd/daemon.c (from rev 27024,
libmicrohttpd/src/daemon/daemon.c)
===================================================================
--- libmicrohttpd/src/microhttpd/daemon.c (rev 0)
+++ libmicrohttpd/src/microhttpd/daemon.c 2013-05-05 18:07:33 UTC (rev
27025)
@@ -0,0 +1,2947 @@
+/*
+ This file is part of libmicrohttpd
+ (C) 2007, 2008, 2009, 2010, 2011, 2012 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
+ version 2.1 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
USA
+
+*/
+
+/**
+ * @file daemon.c
+ * @brief A minimal-HTTP server library
+ * @author Daniel Pittman
+ * @author Christian Grothoff
+ */
+#include "platform.h"
+#include "internal.h"
+#include "response.h"
+#include "connection.h"
+#include "memorypool.h"
+#include <limits.h>
+
+#if HAVE_SEARCH_H
+#include <search.h>
+#else
+#include "tsearch.h"
+#endif
+
+#if HTTPS_SUPPORT
+#include "connection_https.h"
+#include <gnutls/gnutls.h>
+#include <gcrypt.h>
+#endif
+
+#ifdef HAVE_POLL_H
+#include <poll.h>
+#endif
+
+#ifdef LINUX
+#include <sys/sendfile.h>
+#endif
+
+/**
+ * Default connection limit.
+ */
+#ifndef WINDOWS
+#define MHD_MAX_CONNECTIONS_DEFAULT FD_SETSIZE - 4
+#else
+#define MHD_MAX_CONNECTIONS_DEFAULT FD_SETSIZE
+#endif
+
+/**
+ * Default memory allowed per connection.
+ */
+#define MHD_POOL_SIZE_DEFAULT (32 * 1024)
+
+/**
+ * Print extra messages with reasons for closing
+ * sockets? (only adds non-error messages).
+ */
+#define DEBUG_CLOSE MHD_NO
+
+/**
+ * Print extra messages when establishing
+ * connections? (only adds non-error messages).
+ */
+#define DEBUG_CONNECT MHD_NO
+
+#ifndef LINUX
+#ifndef MSG_NOSIGNAL
+#define MSG_NOSIGNAL 0
+#endif
+#endif
+
+#ifndef SOCK_CLOEXEC
+#define SOCK_CLOEXEC 0
+#endif
+
+
+/**
+ * Default implementation of the panic function,
+ * prints an error message and aborts.
+ *
+ * @param cls unused
+ * @param file name of the file with the problem
+ * @param line line number with the problem
+ * @param reason error message with details
+ */
+static void
+mhd_panic_std (void *cls,
+ const char *file,
+ unsigned int line,
+ const char *reason)
+{
+#if HAVE_MESSAGES
+ fprintf (stderr, "Fatal error in GNU libmicrohttpd %s:%u: %s\n",
+ file, line, reason);
+#endif
+ abort ();
+}
+
+
+/**
+ * Handler for fatal errors.
+ */
+MHD_PanicCallback mhd_panic;
+
+/**
+ * Closure argument for "mhd_panic".
+ */
+void *mhd_panic_cls;
+
+
+/**
+ * Trace up to and return master daemon. If the supplied daemon
+ * is a master, then return the daemon itself.
+ *
+ * @param daemon handle to a daemon
+ * @return master daemon handle
+ */
+static struct MHD_Daemon*
+MHD_get_master (struct MHD_Daemon *daemon)
+{
+ while (NULL != daemon->master)
+ daemon = daemon->master;
+ return daemon;
+}
+
+
+/**
+ * Maintain connection count for single address.
+ */
+struct MHD_IPCount
+{
+ /**
+ * Address family. AF_INET or AF_INET6 for now.
+ */
+ int family;
+
+ /**
+ * Actual address.
+ */
+ union
+ {
+ /**
+ * IPv4 address.
+ */
+ struct in_addr ipv4;
+#if HAVE_IPV6
+ /**
+ * IPv6 address.
+ */
+ struct in6_addr ipv6;
+#endif
+ } addr;
+
+ /**
+ * Counter.
+ */
+ unsigned int count;
+};
+
+
+/**
+ * Lock shared structure for IP connection counts and connection DLLs.
+ *
+ * @param daemon handle to daemon where lock is
+ */
+static void
+MHD_ip_count_lock(struct MHD_Daemon *daemon)
+{
+ if (0 != pthread_mutex_lock(&daemon->per_ip_connection_mutex))
+ {
+ MHD_PANIC ("Failed to acquire IP connection limit mutex\n");
+ }
+}
+
+
+/**
+ * Unlock shared structure for IP connection counts and connection DLLs.
+ *
+ * @param daemon handle to daemon where lock is
+ */
+static void
+MHD_ip_count_unlock(struct MHD_Daemon *daemon)
+{
+ if (0 != pthread_mutex_unlock(&daemon->per_ip_connection_mutex))
+ {
+ MHD_PANIC ("Failed to release IP connection limit mutex\n");
+ }
+}
+
+
+/**
+ * Tree comparison function for IP addresses (supplied to tsearch() family).
+ * We compare everything in the struct up through the beginning of the
+ * 'count' field.
+ *
+ * @param a1 first address to compare
+ * @param a2 second address to compare
+ * @return -1, 0 or 1 depending on result of compare
+ */
+static int
+MHD_ip_addr_compare(const void *a1, const void *a2)
+{
+ return memcmp (a1, a2, offsetof (struct MHD_IPCount, count));
+}
+
+
+/**
+ * Parse address and initialize 'key' using the address.
+ *
+ * @param addr address to parse
+ * @param addrlen number of bytes in addr
+ * @param key where to store the parsed address
+ * @return MHD_YES on success and MHD_NO otherwise (e.g., invalid address type)
+ */
+static int
+MHD_ip_addr_to_key(const struct sockaddr *addr,
+ socklen_t addrlen,
+ struct MHD_IPCount *key)
+{
+ memset(key, 0, sizeof(*key));
+
+ /* IPv4 addresses */
+ if (sizeof (struct sockaddr_in) == addrlen)
+ {
+ const struct sockaddr_in *addr4 = (const struct sockaddr_in*) addr;
+ key->family = AF_INET;
+ memcpy (&key->addr.ipv4, &addr4->sin_addr, sizeof(addr4->sin_addr));
+ return MHD_YES;
+ }
+
+#if HAVE_IPV6
+ /* IPv6 addresses */
+ if (sizeof (struct sockaddr_in6) == addrlen)
+ {
+ const struct sockaddr_in6 *addr6 = (const struct sockaddr_in6*) addr;
+ key->family = AF_INET6;
+ memcpy (&key->addr.ipv6, &addr6->sin6_addr, sizeof(addr6->sin6_addr));
+ return MHD_YES;
+ }
+#endif
+
+ /* Some other address */
+ return MHD_NO;
+}
+
+
+/**
+ * Check if IP address is over its limit.
+ *
+ * @param daemon handle to daemon where connection counts are tracked
+ * @param addr address to add (or increment counter)
+ * @param addrlen number of bytes in addr
+ * @return Return MHD_YES if IP below limit, MHD_NO if IP has surpassed limit.
+ * Also returns MHD_NO if fails to allocate memory.
+ */
+static int
+MHD_ip_limit_add(struct MHD_Daemon *daemon,
+ const struct sockaddr *addr,
+ socklen_t addrlen)
+{
+ struct MHD_IPCount *key;
+ void **nodep;
+ void *node;
+ int result;
+
+ daemon = MHD_get_master (daemon);
+ /* Ignore if no connection limit assigned */
+ if (0 == daemon->per_ip_connection_limit)
+ return MHD_YES;
+
+ if (NULL == (key = malloc (sizeof(*key))))
+ return MHD_NO;
+
+ /* Initialize key */
+ if (MHD_NO == MHD_ip_addr_to_key (addr, addrlen, key))
+ {
+ /* Allow unhandled address types through */
+ free (key);
+ return MHD_YES;
+ }
+ MHD_ip_count_lock (daemon);
+
+ /* Search for the IP address */
+ if (NULL == (nodep = TSEARCH (key,
+ &daemon->per_ip_connection_count,
+ &MHD_ip_addr_compare)))
+ {
+#if HAVE_MESSAGES
+ MHD_DLOG (daemon,
+ "Failed to add IP connection count node\n");
+#endif
+ MHD_ip_count_unlock (daemon);
+ free (key);
+ return MHD_NO;
+ }
+ node = *nodep;
+ /* If we got an existing node back, free the one we created */
+ if (node != key)
+ free(key);
+ key = (struct MHD_IPCount *) node;
+ /* Test if there is room for another connection; if so,
+ * increment count */
+ result = (key->count < daemon->per_ip_connection_limit);
+ if (MHD_YES == result)
+ ++key->count;
+
+ MHD_ip_count_unlock (daemon);
+ return result;
+}
+
+
+/**
+ * Decrement connection count for IP address, removing from table
+ * count reaches 0
+ *
+ * @param daemon handle to daemon where connection counts are tracked
+ * @param addr address to remove (or decrement counter)
+ * @param addrlen number of bytes in addr
+ */
+static void
+MHD_ip_limit_del(struct MHD_Daemon *daemon,
+ const struct sockaddr *addr,
+ socklen_t addrlen)
+{
+ struct MHD_IPCount search_key;
+ struct MHD_IPCount *found_key;
+ void **nodep;
+
+ daemon = MHD_get_master (daemon);
+ /* Ignore if no connection limit assigned */
+ if (0 == daemon->per_ip_connection_limit)
+ return;
+ /* Initialize search key */
+ if (MHD_NO == MHD_ip_addr_to_key (addr, addrlen, &search_key))
+ return;
+
+ MHD_ip_count_lock (daemon);
+
+ /* Search for the IP address */
+ if (NULL == (nodep = TFIND (&search_key,
+ &daemon->per_ip_connection_count,
+ &MHD_ip_addr_compare)))
+ {
+ /* Something's wrong if we couldn't find an IP address
+ * that was previously added */
+ MHD_PANIC ("Failed to find previously-added IP address\n");
+ }
+ found_key = (struct MHD_IPCount *) *nodep;
+ /* Validate existing count for IP address */
+ if (0 == found_key->count)
+ {
+ MHD_PANIC ("Previously-added IP address had 0 count\n");
+ }
+ /* Remove the node entirely if count reduces to 0 */
+ if (0 == --found_key->count)
+ {
+ TDELETE (found_key,
+ &daemon->per_ip_connection_count,
+ &MHD_ip_addr_compare);
+ free (found_key);
+ }
+
+ MHD_ip_count_unlock (daemon);
+}
+
+
+#if HTTPS_SUPPORT
+/**
+ * Callback for receiving data from the socket.
+ *
+ * @param connection the MHD connection structure
+ * @param other where to write received data to
+ * @param i maximum size of other (in bytes)
+ * @return number of bytes actually received
+ */
+static ssize_t
+recv_tls_adapter (struct MHD_Connection *connection, void *other, size_t i)
+{
+ int res;
+
+ connection->tls_read_ready = MHD_NO;
+ res = gnutls_record_recv (connection->tls_session, other, i);
+ if ( (GNUTLS_E_AGAIN == res) ||
+ (GNUTLS_E_INTERRUPTED == res) )
+ {
+ errno = EINTR;
+ return -1;
+ }
+ if (res < 0)
+ {
+ /* Likely 'GNUTLS_E_INVALID_SESSION' (client communication
+ disrupted); set errno to something caller will interpret
+ correctly as a hard error*/
+ errno = EPIPE;
+ return res;
+ }
+ if (res == i)
+ connection->tls_read_ready = MHD_YES;
+ return res;
+}
+
+
+/**
+ * Callback for writing data to the socket.
+ *
+ * @param connection the MHD connection structure
+ * @param other data to write
+ * @param i number of bytes to write
+ * @return actual number of bytes written
+ */
+static ssize_t
+send_tls_adapter (struct MHD_Connection *connection,
+ const void *other, size_t i)
+{
+ int res;
+
+ res = gnutls_record_send (connection->tls_session, other, i);
+ if ( (GNUTLS_E_AGAIN == res) ||
+ (GNUTLS_E_INTERRUPTED == res) )
+ {
+ errno = EINTR;
+ return -1;
+ }
+ return res;
+}
+
+
+/**
+ * Read and setup our certificate and key.
+ *
+ * @param daemon handle to daemon to initialize
+ * @return 0 on success
+ */
+static int
+MHD_init_daemon_certificate (struct MHD_Daemon *daemon)
+{
+ gnutls_datum_t key;
+ gnutls_datum_t cert;
+
+ if (NULL != daemon->https_mem_trust)
+ {
+ cert.data = (unsigned char *) daemon->https_mem_trust;
+ cert.size = strlen (daemon->https_mem_trust);
+ if (gnutls_certificate_set_x509_trust_mem (daemon->x509_cred, &cert,
+ GNUTLS_X509_FMT_PEM) < 0)
+ {
+#if HAVE_MESSAGES
+ MHD_DLOG(daemon,
+ "Bad trust certificate format\n");
+#endif
+ return -1;
+ }
+ }
+
+ /* certificate & key loaded from memory */
+ if ( (NULL != daemon->https_mem_cert) &&
+ (NULL != daemon->https_mem_key) )
+ {
+ key.data = (unsigned char *) daemon->https_mem_key;
+ key.size = strlen (daemon->https_mem_key);
+ cert.data = (unsigned char *) daemon->https_mem_cert;
+ cert.size = strlen (daemon->https_mem_cert);
+
+ return gnutls_certificate_set_x509_key_mem (daemon->x509_cred,
+ &cert, &key,
+ GNUTLS_X509_FMT_PEM);
+ }
+#if HAVE_MESSAGES
+ MHD_DLOG (daemon, "You need to specify a certificate and key location\n");
+#endif
+ return -1;
+}
+
+
+/**
+ * Initialize security aspects of the HTTPS daemon
+ *
+ * @param daemon handle to daemon to initialize
+ * @return 0 on success
+ */
+static int
+MHD_TLS_init (struct MHD_Daemon *daemon)
+{
+ switch (daemon->cred_type)
+ {
+ case GNUTLS_CRD_CERTIFICATE:
+ if (0 !=
+ gnutls_certificate_allocate_credentials (&daemon->x509_cred))
+ return GNUTLS_E_MEMORY_ERROR;
+ return MHD_init_daemon_certificate (daemon);
+ default:
+#if HAVE_MESSAGES
+ MHD_DLOG (daemon,
+ "Error: invalid credentials type %d specified.\n",
+ daemon->cred_type);
+#endif
+ return -1;
+ }
+}
+#endif
+
+
+/**
+ * Obtain the select sets for this daemon.
+ *
+ * @param daemon daemon to get sets from
+ * @param read_fd_set read set
+ * @param write_fd_set write set
+ * @param except_fd_set except set
+ * @param max_fd increased to largest FD added (if larger
+ * than existing value); can be NULL
+ * @return MHD_YES on success, MHD_NO if this
+ * daemon was not started with the right
+ * options for this call.
+ */
+int
+MHD_get_fdset (struct MHD_Daemon *daemon,
+ fd_set *read_fd_set,
+ fd_set *write_fd_set,
+ fd_set *except_fd_set,
+ int *max_fd)
+{
+ struct MHD_Connection *pos;
+ int fd;
+
+ if ( (NULL == daemon)
+ || (NULL == read_fd_set)
+ || (NULL == write_fd_set)
+ || (NULL == except_fd_set)
+ || (NULL == max_fd)
+ || (MHD_YES == daemon->shutdown)
+ || (0 != (daemon->options & MHD_USE_THREAD_PER_CONNECTION))
+ || (0 != (daemon->options & MHD_USE_POLL)))
+ return MHD_NO;
+ fd = daemon->socket_fd;
+ if (-1 != fd)
+ {
+ FD_SET (fd, read_fd_set);
+ /* update max file descriptor */
+ if ((*max_fd) < fd)
+ *max_fd = fd;
+ }
+ for (pos = daemon->connections_head; NULL != pos; pos = pos->next)
+ if (MHD_YES != MHD_connection_get_fdset (pos,
+ read_fd_set,
+ write_fd_set,
+ except_fd_set, max_fd))
+ return MHD_NO;
+#if DEBUG_CONNECT
+ MHD_DLOG (daemon, "Maximum socket in select set: %d\n", *max_fd);
+#endif
+ return MHD_YES;
+}
+
+
+/**
+ * Main function of the thread that handles an individual
+ * connection when MHD_USE_THREAD_PER_CONNECTION is set.
+ *
+ * @param data the 'struct MHD_Connection' this thread will handle
+ * @return always NULL
+ */
+static void *
+MHD_handle_connection (void *data)
+{
+ struct MHD_Connection *con = data;
+ int num_ready;
+ fd_set rs;
+ fd_set ws;
+ fd_set es;
+ int max;
+ struct timeval tv;
+ struct timeval *tvp;
+ unsigned int timeout;
+ time_t now;
+#ifdef HAVE_POLL_H
+ struct MHD_Pollfd mp;
+ struct pollfd p[1];
+#endif
+
+ timeout = con->daemon->connection_timeout;
+ while ( (MHD_YES != con->daemon->shutdown) &&
+ (MHD_CONNECTION_CLOSED != con->state) )
+ {
+ tvp = NULL;
+ if (timeout > 0)
+ {
+ now = MHD_monotonic_time();
+ if (now - con->last_activity > timeout)
+ tv.tv_sec = 0;
+ else
+ tv.tv_sec = timeout - (now - con->last_activity);
+ tv.tv_usec = 0;
+ tvp = &tv;
+ }
+ if ( (MHD_CONNECTION_NORMAL_BODY_UNREADY == con->state) ||
+ (MHD_CONNECTION_CHUNKED_BODY_UNREADY == con->state) )
+ {
+ /* do not block (we're waiting for our callback to succeed) */
+ tv.tv_sec = 0;
+ tv.tv_usec = 0;
+ tvp = &tv;
+ }
+#if HTTPS_SUPPORT
+ if (MHD_YES == con->tls_read_ready)
+ {
+ /* do not block (more data may be inside of TLS buffers waiting for
us) */
+ tv.tv_sec = 0;
+ tv.tv_usec = 0;
+ tvp = &tv;
+ }
+#endif
+ if (0 == (con->daemon->options & MHD_USE_POLL))
+ {
+ /* use select */
+ FD_ZERO (&rs);
+ FD_ZERO (&ws);
+ FD_ZERO (&es);
+ max = 0;
+ MHD_connection_get_fdset (con, &rs, &ws, &es, &max);
+ num_ready = SELECT (max + 1, &rs, &ws, &es, tvp);
+ if (num_ready < 0)
+ {
+ if (EINTR == errno)
+ continue;
+#if HAVE_MESSAGES
+ MHD_DLOG (con->daemon,
+ "Error during select (%d): `%s'\n",
+ max,
+ STRERROR (errno));
+#endif
+ break;
+ }
+ /* call appropriate connection handler if necessary */
+ if ( (FD_ISSET (con->socket_fd, &rs))
+#if HTTPS_SUPPORT
+ || (MHD_YES == con->tls_read_ready)
+#endif
+ )
+ con->read_handler (con);
+ if (FD_ISSET (con->socket_fd, &ws))
+ con->write_handler (con);
+ if (MHD_NO == con->idle_handler (con))
+ goto exit;
+ }
+#ifdef HAVE_POLL_H
+ else
+ {
+ /* use poll */
+ memset(&mp, 0, sizeof (struct MHD_Pollfd));
+ MHD_connection_get_pollfd(con, &mp);
+ memset(&p, 0, sizeof (p));
+ p[0].fd = mp.fd;
+ if (mp.events & MHD_POLL_ACTION_IN)
+ p[0].events |= POLLIN;
+ if (mp.events & MHD_POLL_ACTION_OUT)
+ p[0].events |= POLLOUT;
+ if (poll (p,
+ 1,
+ (NULL == tvp) ? -1 : tv.tv_sec * 1000) < 0)
+ {
+ if (EINTR == errno)
+ continue;
+#if HAVE_MESSAGES
+ MHD_DLOG (con->daemon, "Error during poll: `%s'\n",
+ STRERROR (errno));
+#endif
+ break;
+ }
+ if ( (0 != (p[0].revents & POLLIN))
+#if HTTPS_SUPPORT
+ || (MHD_YES == con->tls_read_ready)
+#endif
+ )
+ con->read_handler (con);
+ if (0 != (p[0].revents & POLLOUT))
+ con->write_handler (con);
+ if (0 != (p[0].revents & (POLLERR | POLLHUP)))
+ MHD_connection_close (con, MHD_REQUEST_TERMINATED_WITH_ERROR);
+ if (MHD_NO == con->idle_handler (con))
+ goto exit;
+ }
+#endif
+ }
+ if (MHD_CONNECTION_IN_CLEANUP != con->state)
+ {
+#if DEBUG_CLOSE
+#if HAVE_MESSAGES
+ MHD_DLOG (con->daemon,
+ "Processing thread terminating, closing connection\n");
+#endif
+#endif
+ if (MHD_CONNECTION_CLOSED != con->state)
+ MHD_connection_close (con,
+ MHD_REQUEST_TERMINATED_DAEMON_SHUTDOWN);
+ con->idle_handler (con);
+ }
+exit:
+ if (NULL != con->response)
+ {
+ MHD_destroy_response (con->response);
+ con->response = NULL;
+ }
+ return NULL;
+}
+
+
+/**
+ * Callback for receiving data from the socket.
+ *
+ * @param connection the MHD connection structure
+ * @param other where to write received data to
+ * @param i maximum size of other (in bytes)
+ * @return number of bytes actually received
+ */
+static ssize_t
+recv_param_adapter (struct MHD_Connection *connection,
+ void *other,
+ size_t i)
+{
+ if ( (-1 == connection->socket_fd) ||
+ (MHD_CONNECTION_CLOSED == connection->state) )
+ {
+ errno = ENOTCONN;
+ return -1;
+ }
+ if (0 != (connection->daemon->options & MHD_USE_SSL))
+ return RECV (connection->socket_fd, other, i, MSG_NOSIGNAL);
+ return RECV (connection->socket_fd, other, i, MSG_NOSIGNAL);
+}
+
+
+/**
+ * Callback for writing data to the socket.
+ *
+ * @param connection the MHD connection structure
+ * @param other data to write
+ * @param i number of bytes to write
+ * @return actual number of bytes written
+ */
+static ssize_t
+send_param_adapter (struct MHD_Connection *connection,
+ const void *other,
+ size_t i)
+{
+#if LINUX
+ int fd;
+ off_t offset;
+ off_t left;
+ ssize_t ret;
+#endif
+ if ( (-1 == connection->socket_fd) ||
+ (MHD_CONNECTION_CLOSED == connection->state) )
+ {
+ errno = ENOTCONN;
+ return -1;
+ }
+ if (0 != (connection->daemon->options & MHD_USE_SSL))
+ return SEND (connection->socket_fd, other, i, MSG_NOSIGNAL);
+#if LINUX
+ if ( (connection->write_buffer_append_offset ==
+ connection->write_buffer_send_offset) &&
+ (NULL != connection->response) &&
+ (-1 != (fd = connection->response->fd)) )
+ {
+ /* can use sendfile */
+ offset = (off_t) connection->response_write_position +
connection->response->fd_off;
+ left = connection->response->total_size -
connection->response_write_position;
+ if (left > SSIZE_MAX)
+ left = SSIZE_MAX; /* cap at return value limit */
+ if (-1 != (ret = sendfile (connection->socket_fd,
+ fd,
+ &offset,
+ (size_t) left)))
+ return ret;
+ if ( (EINTR == errno) || (EAGAIN == errno) )
+ return 0;
+ if ( (EINVAL == errno) || (EBADF == errno) )
+ return -1;
+ /* None of the 'usual' sendfile errors occurred, so we should try
+ to fall back to 'SEND'; see also this thread for info on
+ odd libc/Linux behavior with sendfile:
+ http://lists.gnu.org/archive/html/libmicrohttpd/2011-02/msg00015.html
*/
+ }
+#endif
+ return SEND (connection->socket_fd, other, i, MSG_NOSIGNAL);
+}
+
+
+/**
+ * Signature of main function for a thread.
+ *
+ * @param cls closure argument for the function
+ * @return termination code from the thread
+ */
+typedef void *(*ThreadStartRoutine)(void *cls);
+
+
+/**
+ * Create a thread and set the attributes according to our options.
+ *
+ * @param thread handle to initialize
+ * @param daemon daemon with options
+ * @param start_routine main function of thread
+ * @param arg argument for start_routine
+ * @return 0 on success
+ */
+static int
+create_thread (pthread_t *thread,
+ const struct MHD_Daemon *daemon,
+ ThreadStartRoutine start_routine,
+ void *arg)
+{
+ pthread_attr_t attr;
+ pthread_attr_t *pattr;
+ int ret;
+
+ if (0 != daemon->thread_stack_size)
+ {
+ if (0 != (ret = pthread_attr_init (&attr)))
+ goto ERR;
+ if (0 != (ret = pthread_attr_setstacksize (&attr,
daemon->thread_stack_size)))
+ {
+ pthread_attr_destroy (&attr);
+ goto ERR;
+ }
+ pattr = &attr;
+ }
+ else
+ {
+ pattr = NULL;
+ }
+ ret = pthread_create (thread, pattr,
+ start_routine, arg);
+ if (0 != daemon->thread_stack_size)
+ pthread_attr_destroy (&attr);
+ return ret;
+ ERR:
+#if HAVE_MESSAGES
+ MHD_DLOG (daemon,
+ "Failed to set thread stack size\n");
+#endif
+ errno = EINVAL;
+ return ret;
+}
+
+
+/**
+ * Add another client connection to the set of connections
+ * managed by MHD. This API is usually not needed (since
+ * MHD will accept inbound connections on the server socket).
+ * Use this API in special cases, for example if your HTTP
+ * server is behind NAT and needs to connect out to the
+ * HTTP client.
+ *
+ * The given client socket will be managed (and closed!) by MHD after
+ * this call and must no longer be used directly by the application
+ * afterwards.
+ *
+ * Per-IP connection limits are ignored when using this API.
+ *
+ * @param daemon daemon that manages the connection
+ * @param client_socket socket to manage (MHD will expect
+ * to receive an HTTP request from this socket next).
+ * @param addr IP address of the client
+ * @param addrlen number of bytes in addr
+ * @return MHD_YES on success, MHD_NO if this daemon could
+ * not handle the connection (i.e. malloc failed, etc).
+ * The socket will be closed in any case.
+ */
+int
+MHD_add_connection (struct MHD_Daemon *daemon,
+ int client_socket,
+ const struct sockaddr *addr,
+ socklen_t addrlen)
+{
+ struct MHD_Connection *connection;
+ int res_thread_create;
+#if OSX
+ static int on = 1;
+#endif
+
+#ifndef WINDOWS
+ if ( (client_socket >= FD_SETSIZE) &&
+ (0 == (daemon->options & MHD_USE_POLL)) )
+ {
+#if HAVE_MESSAGES
+ MHD_DLOG (daemon,
+ "Socket descriptor larger than FD_SETSIZE: %d > %d\n",
+ client_socket,
+ FD_SETSIZE);
+#endif
+ SHUTDOWN (client_socket, SHUT_RDWR);
+ CLOSE (client_socket);
+ return MHD_NO;
+ }
+#endif
+
+
+#if HAVE_MESSAGES
+#if DEBUG_CONNECT
+ MHD_DLOG (daemon, "Accepted connection on socket %d\n", s);
+#endif
+#endif
+ if ( (0 == daemon->max_connections) ||
+ (MHD_NO == MHD_ip_limit_add (daemon, addr, addrlen)) )
+ {
+ /* above connection limit - reject */
+#if HAVE_MESSAGES
+ MHD_DLOG (daemon,
+ "Server reached connection limit (closing inbound
connection)\n");
+#endif
+ SHUTDOWN (client_socket, SHUT_RDWR);
+ CLOSE (client_socket);
+ return MHD_NO;
+ }
+
+ /* apply connection acceptance policy if present */
+ if ( (NULL != daemon->apc) &&
+ (MHD_NO == daemon->apc (daemon->apc_cls,
+ addr, addrlen)) )
+ {
+#if DEBUG_CLOSE
+#if HAVE_MESSAGES
+ MHD_DLOG (daemon, "Connection rejected, closing connection\n");
+#endif
+#endif
+ SHUTDOWN (client_socket, SHUT_RDWR);
+ CLOSE (client_socket);
+ MHD_ip_limit_del (daemon, addr, addrlen);
+ return MHD_YES;
+ }
+
+#if OSX
+#ifdef SOL_SOCKET
+#ifdef SO_NOSIGPIPE
+ setsockopt (client_socket,
+ SOL_SOCKET, SO_NOSIGPIPE,
+ &on, sizeof (on));
+#endif
+#endif
+#endif
+
+ if (NULL == (connection = malloc (sizeof (struct MHD_Connection))))
+ {
+#if HAVE_MESSAGES
+ MHD_DLOG (daemon,
+ "Error allocating memory: %s\n",
+ STRERROR (errno));
+#endif
+ SHUTDOWN (client_socket, SHUT_RDWR);
+ CLOSE (client_socket);
+ MHD_ip_limit_del (daemon, addr, addrlen);
+ return MHD_NO;
+ }
+ memset (connection, 0, sizeof (struct MHD_Connection));
+ connection->connection_timeout = daemon->connection_timeout;
+ connection->pool = NULL;
+ if (NULL == (connection->addr = malloc (addrlen)))
+ {
+#if HAVE_MESSAGES
+ MHD_DLOG (daemon,
+ "Error allocating memory: %s\n",
+ STRERROR (errno));
+#endif
+ SHUTDOWN (client_socket, SHUT_RDWR);
+ CLOSE (client_socket);
+ MHD_ip_limit_del (daemon, addr, addrlen);
+ free (connection);
+ return MHD_NO;
+ }
+ memcpy (connection->addr, addr, addrlen);
+ connection->addr_len = addrlen;
+ connection->socket_fd = client_socket;
+ connection->daemon = daemon;
+ connection->last_activity = MHD_monotonic_time();
+
+ /* set default connection handlers */
+ MHD_set_http_callbacks_ (connection);
+ connection->recv_cls = &recv_param_adapter;
+ connection->send_cls = &send_param_adapter;
+ /* non-blocking sockets are required on most systems and for GNUtls;
+ however, they somehow cause serious problems on CYGWIN (#1824) */
+#ifdef CYGWIN
+ if (0 != (daemon->options & MHD_USE_SSL))
+#endif
+ {
+ /* make socket non-blocking */
+#ifndef MINGW
+ int flags = fcntl (connection->socket_fd, F_GETFL);
+ if ( (-1 == flags) ||
+ (0 != fcntl (connection->socket_fd, F_SETFL, flags | O_NONBLOCK)) )
+ {
+#if HAVE_MESSAGES
+ MHD_DLOG (daemon,
+ "Failed to make socket %d non-blocking: %s\n",
+ connection->socket_fd,
+ STRERROR (errno));
+#endif
+ }
+#else
+ unsigned long flags = 1;
+ if (0 != ioctlsocket (connection->socket_fd, FIONBIO, &flags))
+ {
+#if HAVE_MESSAGES
+ MHD_DLOG (daemon,
+ "Failed to make socket non-blocking: %s\n",
+ STRERROR (errno));
+#endif
+ }
+#endif
+ }
+
+#if HTTPS_SUPPORT
+ if (0 != (daemon->options & MHD_USE_SSL))
+ {
+ connection->recv_cls = &recv_tls_adapter;
+ connection->send_cls = &send_tls_adapter;
+ connection->state = MHD_TLS_CONNECTION_INIT;
+ MHD_set_https_callbacks (connection);
+ gnutls_init (&connection->tls_session, GNUTLS_SERVER);
+ gnutls_priority_set (connection->tls_session,
+ daemon->priority_cache);
+ switch (daemon->cred_type)
+ {
+ /* set needed credentials for certificate authentication. */
+ case GNUTLS_CRD_CERTIFICATE:
+ gnutls_credentials_set (connection->tls_session,
+ GNUTLS_CRD_CERTIFICATE,
+ daemon->x509_cred);
+ break;
+ default:
+#if HAVE_MESSAGES
+ MHD_DLOG (connection->daemon,
+ "Failed to setup TLS credentials: unknown credential type
%d\n",
+ daemon->cred_type);
+#endif
+ SHUTDOWN (client_socket, SHUT_RDWR);
+ CLOSE (client_socket);
+ MHD_ip_limit_del (daemon, addr, addrlen);
+ free (connection->addr);
+ free (connection);
+ MHD_PANIC ("Unknown credential type");
+ return MHD_NO;
+ }
+ gnutls_transport_set_ptr (connection->tls_session,
+ (gnutls_transport_ptr_t) connection);
+ gnutls_transport_set_pull_function (connection->tls_session,
+ (gnutls_pull_func)
&recv_param_adapter);
+ gnutls_transport_set_push_function (connection->tls_session,
+ (gnutls_push_func)
&send_param_adapter);
+
+ if (daemon->https_mem_trust)
+ gnutls_certificate_server_set_request(connection->tls_session,
GNUTLS_CERT_REQUEST);
+ }
+#endif
+
+ if (0 != pthread_mutex_lock(&daemon->cleanup_connection_mutex))
+ MHD_PANIC ("Failed to acquire cleanup mutex\n");
+ DLL_insert (daemon->connections_head,
+ daemon->connections_tail,
+ connection);
+ if (0 != pthread_mutex_unlock(&daemon->cleanup_connection_mutex))
+ MHD_PANIC ("Failed to release cleanup mutex\n");
+
+ /* attempt to create handler thread */
+ if (0 != (daemon->options & MHD_USE_THREAD_PER_CONNECTION))
+ {
+ res_thread_create = create_thread (&connection->pid, daemon,
+ &MHD_handle_connection, connection);
+ if (0 != res_thread_create)
+ {
+#if HAVE_MESSAGES
+ MHD_DLOG (daemon, "Failed to create a thread: %s\n",
+ STRERROR (res_thread_create));
+#endif
+ SHUTDOWN (client_socket, SHUT_RDWR);
+ CLOSE (client_socket);
+ MHD_ip_limit_del (daemon, addr, addrlen);
+ if (0 != pthread_mutex_lock(&daemon->cleanup_connection_mutex))
+ {
+ MHD_PANIC ("Failed to acquire cleanup mutex\n");
+ }
+ DLL_remove (daemon->connections_head,
+ daemon->connections_tail,
+ connection);
+ if (0 != pthread_mutex_unlock(&daemon->cleanup_connection_mutex))
+ {
+ MHD_PANIC ("Failed to release cleanup mutex\n");
+ }
+ free (connection->addr);
+ free (connection);
+ return MHD_NO;
+ }
+ }
+ daemon->max_connections--;
+ return MHD_YES;
+}
+
+
+/**
+ * Accept an incoming connection and create the MHD_Connection object for
+ * it. This function also enforces policy by way of checking with the
+ * accept policy callback.
+ *
+ * @param daemon handle with the listen socket
+ * @return MHD_YES on success
+ */
+static int
+MHD_accept_connection (struct MHD_Daemon *daemon)
+{
+#if HAVE_INET6
+ struct sockaddr_in6 addrstorage;
+#else
+ struct sockaddr_in addrstorage;
+#endif
+ struct sockaddr *addr = (struct sockaddr *) &addrstorage;
+ socklen_t addrlen;
+ int s;
+ int flags;
+ int need_fcntl;
+ int fd;
+
+ addrlen = sizeof (addrstorage);
+ memset (addr, 0, sizeof (addrstorage));
+ if (-1 == (fd = daemon->socket_fd))
+ return MHD_NO;
+#if HAVE_ACCEPT4
+ s = accept4 (fd, addr, &addrlen, SOCK_CLOEXEC);
+ need_fcntl = MHD_NO;
+#else
+ s = -1;
+ need_fcntl = MHD_YES;
+#endif
+ if (-1 == s)
+ {
+ s = ACCEPT (fd, addr, &addrlen);
+ need_fcntl = MHD_YES;
+ }
+ if ((-1 == s) || (addrlen <= 0))
+ {
+#if HAVE_MESSAGES
+ /* This could be a common occurance with multiple worker threads */
+ if ((EAGAIN != errno) && (EWOULDBLOCK != errno))
+ MHD_DLOG (daemon,
+ "Error accepting connection: %s\n",
+ STRERROR (errno));
+#endif
+ if (-1 != s)
+ {
+ SHUTDOWN (s, SHUT_RDWR);
+ CLOSE (s);
+ /* just in case */
+ }
+ return MHD_NO;
+ }
+ if (MHD_YES == need_fcntl)
+ {
+ /* make socket non-inheritable */
+#ifdef WINDOWS
+ DWORD dwFlags;
+ if (!GetHandleInformation ((HANDLE) s, &dwFlags) ||
+ ((dwFlags != dwFlags & ~HANDLE_FLAG_INHERIT) &&
+ !SetHandleInformation ((HANDLE) s, HANDLE_FLAG_INHERIT, 0)))
+ {
+#if HAVE_MESSAGES
+ SetErrnoFromWinError (GetLastError ());
+ MHD_DLOG (daemon,
+ "Failed to make socket non-inheritable: %s\n",
+ STRERROR (errno));
+#endif
+ }
+#else
+ flags = fcntl (s, F_GETFD);
+ if ( ( (-1 == flags) ||
+ ( (flags != (flags | FD_CLOEXEC)) &&
+ (0 != fcntl (s, F_SETFD, flags | FD_CLOEXEC)) ) ) )
+ {
+#if HAVE_MESSAGES
+ MHD_DLOG (daemon,
+ "Failed to make socket non-inheritable: %s\n",
+ STRERROR (errno));
+#endif
+ }
+#endif
+ }
+#if HAVE_MESSAGES
+#if DEBUG_CONNECT
+ MHD_DLOG (daemon, "Accepted connection on socket %d\n", s);
+#endif
+#endif
+ return MHD_add_connection (daemon, s,
+ addr, addrlen);
+}
+
+
+/**
+ * Free resources associated with all closed connections.
+ * (destroy responses, free buffers, etc.). All closed
+ * connections are kept in the "cleanup" doubly-linked list.
+ *
+ * @param daemon daemon to clean up
+ */
+static void
+MHD_cleanup_connections (struct MHD_Daemon *daemon)
+{
+ struct MHD_Connection *pos;
+ void *unused;
+ int rc;
+
+ if (0 != pthread_mutex_lock(&daemon->cleanup_connection_mutex))
+ {
+ MHD_PANIC ("Failed to acquire cleanup mutex\n");
+ }
+ while (NULL != (pos = daemon->cleanup_head))
+ {
+ DLL_remove (daemon->cleanup_head,
+ daemon->cleanup_tail,
+ pos);
+ if ( (0 != (daemon->options & MHD_USE_THREAD_PER_CONNECTION)) &&
+ (MHD_NO == pos->thread_joined) )
+ {
+ if (0 != (rc = pthread_join (pos->pid, &unused)))
+ {
+ MHD_PANIC ("Failed to join a thread\n");
+ }
+ }
+ MHD_pool_destroy (pos->pool);
+#if HTTPS_SUPPORT
+ if (pos->tls_session != NULL)
+ gnutls_deinit (pos->tls_session);
+#endif
+ MHD_ip_limit_del (daemon, (struct sockaddr*)pos->addr, pos->addr_len);
+ if (NULL != pos->response)
+ {
+ MHD_destroy_response (pos->response);
+ pos->response = NULL;
+ }
+ if (-1 != pos->socket_fd)
+ CLOSE (pos->socket_fd);
+ if (NULL != pos->addr)
+ free (pos->addr);
+ free (pos);
+ daemon->max_connections++;
+ }
+ if (0 != pthread_mutex_unlock(&daemon->cleanup_connection_mutex))
+ {
+ MHD_PANIC ("Failed to release cleanup mutex\n");
+ }
+}
+
+
+/**
+ * Obtain timeout value for select for this daemon
+ * (only needed if connection timeout is used). The
+ * returned value is how long select should at most
+ * block, not the timeout value set for connections.
+ *
+ * @param daemon daemon to query for timeout
+ * @param timeout set to the timeout (in milliseconds)
+ * @return MHD_YES on success, MHD_NO if timeouts are
+ * not used (or no connections exist that would
+ * necessiate the use of a timeout right now).
+ */
+int
+MHD_get_timeout (struct MHD_Daemon *daemon,
+ MHD_UNSIGNED_LONG_LONG *timeout)
+{
+ time_t earliest_deadline;
+ time_t now;
+ struct MHD_Connection *pos;
+ int have_timeout;
+
+ if (0 != (daemon->options & MHD_USE_THREAD_PER_CONNECTION))
+ {
+#if HAVE_MESSAGES
+ MHD_DLOG (daemon, "Illegal call to MHD_get_timeout\n");
+#endif
+ return MHD_NO;
+ }
+ have_timeout = MHD_NO;
+ for (pos = daemon->connections_head; NULL != pos; pos = pos->next)
+ {
+#if HTTPS_SUPPORT
+ if (MHD_YES == pos->tls_read_ready)
+ {
+ earliest_deadline = 0;
+ have_timeout = MHD_YES;
+ break;
+ }
+#endif
+ if (0 != pos->connection_timeout)
+ {
+ if ( (! have_timeout) ||
+ (earliest_deadline > pos->last_activity +
pos->connection_timeout) )
+ earliest_deadline = pos->last_activity + pos->connection_timeout;
+#if HTTPS_SUPPORT
+ if ( (0 != (daemon->options & MHD_USE_SSL)) &&
+ (0 != gnutls_record_check_pending (pos->tls_session)) )
+ earliest_deadline = 0;
+#endif
+ have_timeout = MHD_YES;
+ }
+ }
+ if (MHD_NO == have_timeout)
+ return MHD_NO;
+ now = MHD_monotonic_time();
+ if (earliest_deadline < now)
+ *timeout = 0;
+ else
+ *timeout = 1000 * (1 + earliest_deadline - now);
+ return MHD_YES;
+}
+
+
+/**
+ * Run webserver operations. This method should be called by clients
+ * in combination with MHD_get_fdset if the client-controlled select
+ * method is used.
+ *
+ * You can use this function instead of "MHD_run" if you called
+ * 'select' on the result from "MHD_get_fdset". File descriptors in
+ * the sets that are not controlled by MHD will be ignored. Calling
+ * this function instead of "MHD_run" is more efficient as MHD will
+ * not have to call 'select' again to determine which operations are
+ * ready.
+ *
+ * @param daemon daemon to run select loop for
+ * @param read_fd_set read set
+ * @param write_fd_set write set
+ * @param except_fd_set except set (not used, can be NULL)
+ * @return MHD_NO on serious errors, MHD_YES on success
+ */
+int
+MHD_run_from_select (struct MHD_Daemon *daemon,
+ const fd_set *read_fd_set,
+ const fd_set *write_fd_set,
+ const fd_set *except_fd_set)
+{
+ int ds;
+ int tmp;
+ struct MHD_Connection *pos;
+ struct MHD_Connection *next;
+
+ /* select connection thread handling type */
+ if ( (-1 != (ds = daemon->socket_fd)) &&
+ (FD_ISSET (ds, read_fd_set)) )
+ MHD_accept_connection (daemon);
+ /* drain signaling pipe to avoid spinning select */
+ if ( (-1 != daemon->wpipe[0]) &&
+ (FD_ISSET (daemon->wpipe[0], read_fd_set)) )
+ (void) read (daemon->wpipe[0], &tmp, sizeof (tmp));
+
+ if (0 == (daemon->options & MHD_USE_THREAD_PER_CONNECTION))
+ {
+ /* do not have a thread per connection, process all connections now */
+ next = daemon->connections_head;
+ while (NULL != (pos = next))
+ {
+ next = pos->next;
+ ds = pos->socket_fd;
+ if (ds != -1)
+ {
+ if ( (FD_ISSET (ds, read_fd_set))
+#if HTTPS_SUPPORT
+ || (MHD_YES == pos->tls_read_ready)
+#endif
+ )
+ pos->read_handler (pos);
+ if (FD_ISSET (ds, write_fd_set))
+ pos->write_handler (pos);
+ pos->idle_handler (pos);
+ }
+ }
+ }
+ return MHD_YES;
+}
+
+
+/**
+ * Main internal select call. Will compute select sets, call 'select'
+ * and then MHD_run_from_select with the result.
+ *
+ * @param daemon daemon to run select loop for
+ * @param may_block YES if blocking, NO if non-blocking
+ * @return MHD_NO on serious errors, MHD_YES on success
+ */
+static int
+MHD_select (struct MHD_Daemon *daemon,
+ int may_block)
+{
+ int num_ready;
+ fd_set rs;
+ fd_set ws;
+ fd_set es;
+ int max;
+ struct timeval timeout;
+ struct timeval *tv;
+ MHD_UNSIGNED_LONG_LONG ltimeout;
+
+ timeout.tv_sec = 0;
+ timeout.tv_usec = 0;
+ if (MHD_YES == daemon->shutdown)
+ return MHD_NO;
+ FD_ZERO (&rs);
+ FD_ZERO (&ws);
+ FD_ZERO (&es);
+ max = -1;
+ 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;
+
+ /* If we're at the connection limit, no need to
+ accept new connections. */
+ if ( (0 == daemon->max_connections) &&
+ (-1 != daemon->socket_fd) )
+ FD_CLR (daemon->socket_fd, &rs);
+ }
+ else
+ {
+ /* accept only, have one thread per connection */
+ if (-1 != daemon->socket_fd)
+ {
+ max = daemon->socket_fd;
+ FD_SET (daemon->socket_fd, &rs);
+ }
+ }
+ if (-1 != daemon->wpipe[0])
+ {
+ FD_SET (daemon->wpipe[0], &rs);
+ /* update max file descriptor */
+ if (max < daemon->wpipe[0])
+ max = daemon->wpipe[0];
+ }
+
+ tv = NULL;
+ if (MHD_NO == may_block)
+ {
+ timeout.tv_usec = 0;
+ timeout.tv_sec = 0;
+ tv = &timeout;
+ }
+ else if ( (0 == (daemon->options & MHD_USE_THREAD_PER_CONNECTION)) &&
+ (MHD_YES == MHD_get_timeout (daemon, <imeout)) )
+ {
+ /* ltimeout is in ms */
+ timeout.tv_usec = (ltimeout % 1000) * 1000;
+ timeout.tv_sec = ltimeout / 1000;
+ tv = &timeout;
+ }
+ if (-1 == max)
+ return MHD_YES;
+ num_ready = SELECT (max + 1, &rs, &ws, &es, tv);
+ if (MHD_YES == daemon->shutdown)
+ return MHD_NO;
+ if (num_ready < 0)
+ {
+ if (EINTR == errno)
+ return MHD_YES;
+#if HAVE_MESSAGES
+ MHD_DLOG (daemon, "select failed: %s\n", STRERROR (errno));
+#endif
+ return MHD_NO;
+ }
+ return MHD_run_from_select (daemon, &rs, &ws, &es);
+}
+
+
+#ifdef HAVE_POLL_H
+/**
+ * Process all of our connections and possibly the server
+ * socket using 'poll'.
+ *
+ * @param daemon daemon to run poll loop for
+ * @param may_block YES if blocking, NO if non-blocking
+ * @return MHD_NO on serious errors, MHD_YES on success
+ */
+static int
+MHD_poll_all (struct MHD_Daemon *daemon,
+ int may_block)
+{
+ unsigned int num_connections;
+ struct MHD_Connection *pos;
+ struct MHD_Connection *next;
+
+ /* count number of connections and thus determine poll set size */
+ num_connections = 0;
+ for (pos = daemon->connections_head; NULL != pos; pos = pos->next)
+ num_connections++;
+
+ {
+ struct pollfd p[2 + num_connections];
+ struct MHD_Pollfd mp;
+ MHD_UNSIGNED_LONG_LONG ltimeout;
+ unsigned int i;
+ int timeout;
+ unsigned int poll_server;
+ int poll_listen;
+
+ memset (p, 0, sizeof (p));
+ poll_server = 0;
+ poll_listen = -1;
+ if ( (-1 != daemon->socket_fd) &&
+ (0 != daemon->max_connections) )
+ {
+ /* only listen if we are not at the connection limit */
+ p[poll_server].fd = daemon->socket_fd;
+ p[poll_server].events = POLLIN;
+ p[poll_server].revents = 0;
+ poll_listen = (int) poll_server;
+ poll_server++;
+ }
+ if (-1 != daemon->wpipe[0])
+ {
+ p[poll_server].fd = daemon->wpipe[0];
+ p[poll_server].events = POLLIN;
+ p[poll_server].revents = 0;
+ poll_server++;
+ }
+ if (may_block == MHD_NO)
+ timeout = 0;
+ else if ( (0 != (daemon->options & MHD_USE_THREAD_PER_CONNECTION)) ||
+ (MHD_YES != MHD_get_timeout (daemon, <imeout)) )
+ timeout = -1;
+ else
+ timeout = (ltimeout > INT_MAX) ? INT_MAX : (int) ltimeout;
+
+ i = 0;
+ for (pos = daemon->connections_head; NULL != pos; pos = pos->next)
+ {
+ memset(&mp, 0, sizeof (struct MHD_Pollfd));
+ MHD_connection_get_pollfd (pos, &mp);
+ p[poll_server+i].fd = mp.fd;
+ if (mp.events & MHD_POLL_ACTION_IN)
+ p[poll_server+i].events |= POLLIN;
+ if (mp.events & MHD_POLL_ACTION_OUT)
+ p[poll_server+i].events |= POLLOUT;
+ i++;
+ }
+ if (0 == poll_server + num_connections)
+ return MHD_YES;
+ if (poll (p, poll_server + num_connections, timeout) < 0)
+ {
+ if (EINTR == errno)
+ return MHD_YES;
+#if HAVE_MESSAGES
+ MHD_DLOG (daemon,
+ "poll failed: %s\n",
+ STRERROR (errno));
+#endif
+ return MHD_NO;
+ }
+ /* handle shutdown */
+ if (MHD_YES == daemon->shutdown)
+ return MHD_NO;
+ i = 0;
+ next = daemon->connections_head;
+ while (NULL != (pos = next))
+ {
+ next = pos->next;
+ /* first, sanity checks */
+ if (i >= num_connections)
+ break; /* connection list changed somehow, retry later ... */
+ MHD_connection_get_pollfd (pos, &mp);
+ if (p[poll_server+i].fd != mp.fd)
+ break; /* fd mismatch, something else happened, retry later ... */
+
+ /* normal handling */
+ if (0 != (p[poll_server+i].revents & POLLIN))
+ pos->read_handler (pos);
+ if (0 != (p[poll_server+i].revents & POLLOUT))
+ pos->write_handler (pos);
+ pos->idle_handler (pos);
+ i++;
+ }
+ if ( (-1 != poll_listen) &&
+ (0 != (p[poll_listen].revents & POLLIN)) )
+ MHD_accept_connection (daemon);
+ }
+ return MHD_YES;
+}
+
+
+/**
+ * Process only the listen socket using 'poll'.
+ *
+ * @param daemon daemon to run poll loop for
+ * @param may_block YES if blocking, NO if non-blocking
+ * @return MHD_NO on serious errors, MHD_YES on success
+ */
+static int
+MHD_poll_listen_socket (struct MHD_Daemon *daemon,
+ int may_block)
+{
+ struct pollfd p[2];
+ int timeout;
+ unsigned int poll_count;
+ int poll_listen;
+
+ memset (&p, 0, sizeof (p));
+ poll_count = 0;
+ poll_listen = -1;
+ if (-1 != daemon->socket_fd)
+ {
+ p[poll_count].fd = daemon->socket_fd;
+ p[poll_count].events = POLLIN;
+ p[poll_count].revents = 0;
+ poll_listen = poll_count;
+ poll_count++;
+ }
+ if (-1 != daemon->wpipe[0])
+ {
+ p[poll_count].fd = daemon->wpipe[0];
+ p[poll_count].events = POLLIN;
+ p[poll_count].revents = 0;
+ poll_count++;
+ }
+ if (MHD_NO == may_block)
+ timeout = 0;
+ else
+ timeout = -1;
+ if (0 == poll_count)
+ return MHD_YES;
+ if (poll (p, poll_count, timeout) < 0)
+ {
+ if (EINTR == errno)
+ return MHD_YES;
+#if HAVE_MESSAGES
+ MHD_DLOG (daemon, "poll failed: %s\n", STRERROR (errno));
+#endif
+ return MHD_NO;
+ }
+ /* handle shutdown */
+ if (MHD_YES == daemon->shutdown)
+ return MHD_NO;
+ if ( (-1 != poll_listen) &&
+ (0 != (p[poll_listen].revents & POLLIN)) )
+ MHD_accept_connection (daemon);
+ return MHD_YES;
+}
+#endif
+
+
+/**
+ * Do 'poll'-based processing.
+ *
+ * @param daemon daemon to run poll loop for
+ * @param may_block YES if blocking, NO if non-blocking
+ * @return MHD_NO on serious errors, MHD_YES on success
+ */
+static int
+MHD_poll (struct MHD_Daemon *daemon,
+ int may_block)
+{
+#ifdef HAVE_POLL_H
+ if (MHD_YES == daemon->shutdown)
+ return MHD_NO;
+ if (0 == (daemon->options & MHD_USE_THREAD_PER_CONNECTION))
+ return MHD_poll_all (daemon, may_block);
+ else
+ return MHD_poll_listen_socket (daemon, may_block);
+#else
+ return MHD_NO;
+#endif
+}
+
+
+/**
+ * Run webserver operations (without blocking unless
+ * in client callbacks). This method should be called
+ * by clients in combination with MHD_get_fdset
+ * if the client-controlled select method is used.
+ *
+ * This function will work for external 'poll' and 'select' mode.
+ * However, if using external 'select' mode, you may want to
+ * instead use 'MHD_run_from_select', as it is more efficient.
+ *
+ * @return MHD_YES on success, MHD_NO if this
+ * daemon was not started with the right
+ * options for this call.
+ */
+int
+MHD_run (struct MHD_Daemon *daemon)
+{
+ if ( (MHD_YES == daemon->shutdown) ||
+ (0 != (daemon->options & MHD_USE_THREAD_PER_CONNECTION)) ||
+ (0 != (daemon->options & MHD_USE_SELECT_INTERNALLY)) )
+ return MHD_NO;
+ if (0 == (daemon->options & MHD_USE_POLL))
+ MHD_select (daemon, MHD_NO);
+ else
+ MHD_poll (daemon, MHD_NO);
+ MHD_cleanup_connections (daemon);
+ return MHD_YES;
+}
+
+
+/**
+ * Thread that runs the select loop until the daemon
+ * is explicitly shut down.
+ *
+ * @param cls 'struct MHD_Deamon' to run select loop in a thread for
+ * @return always NULL (on shutdown)
+ */
+static void *
+MHD_select_thread (void *cls)
+{
+ struct MHD_Daemon *daemon = cls;
+
+ while (MHD_YES != daemon->shutdown)
+ {
+ if (0 == (daemon->options & MHD_USE_POLL))
+ MHD_select (daemon, MHD_YES);
+ else
+ MHD_poll (daemon, MHD_YES);
+ MHD_cleanup_connections (daemon);
+ }
+ return NULL;
+}
+
+
+/**
+ * Start a webserver on the given port.
+ *
+ * @param flags combination of MHD_FLAG values
+ * @param port port to bind to
+ * @param apc callback to call to check which clients
+ * will be allowed to connect
+ * @param apc_cls extra argument to apc
+ * @param dh default handler for all URIs
+ * @param dh_cls extra argument to dh
+ * @return NULL on error, handle to daemon on success
+ */
+struct MHD_Daemon *
+MHD_start_daemon (unsigned int flags,
+ uint16_t port,
+ MHD_AcceptPolicyCallback apc,
+ void *apc_cls,
+ MHD_AccessHandlerCallback dh, void *dh_cls, ...)
+{
+ struct MHD_Daemon *daemon;
+ va_list ap;
+
+ va_start (ap, dh_cls);
+ daemon = MHD_start_daemon_va (flags, port, apc, apc_cls, dh, dh_cls, ap);
+ va_end (ap);
+ return daemon;
+}
+
+
+/**
+ * Stop accepting connections from the listening socket. Allows
+ * clients to continue processing, but stops accepting new
+ * connections. Note that the caller is responsible for closing the
+ * returned socket; however, if MHD is run using threads (anything but
+ * external select mode), it must not be closed until AFTER
+ * "MHD_stop_daemon" has been called (as it is theoretically possible
+ * that an existing thread is still using it).
+ *
+ * @param daemon daemon to stop accepting new connections for
+ * @return old listen socket on success, -1 if the daemon was
+ * already not listening anymore
+ */
+int
+MHD_quiesce_daemon (struct MHD_Daemon *daemon)
+{
+ unsigned int i;
+ int ret;
+
+ ret = daemon->socket_fd;
+ if (NULL != daemon->worker_pool)
+ for (i = 0; i < daemon->worker_pool_size; i++)
+ daemon->worker_pool[i].socket_fd = -1;
+ daemon->socket_fd = -1;
+ return ret;
+}
+
+
+/**
+ * Signature of the MHD custom logger function.
+ *
+ * @param cls closure
+ * @param format format string
+ * @param va arguments to the format string (fprintf-style)
+ */
+typedef void (*VfprintfFunctionPointerType)(void *cls,
+ const char *format,
+ va_list va);
+
+
+/**
+ * Parse a list of options given as varargs.
+ *
+ * @param daemon the daemon to initialize
+ * @param servaddr where to store the server's listen address
+ * @param ap the options
+ * @return MHD_YES on success, MHD_NO on error
+ */
+static int
+parse_options_va (struct MHD_Daemon *daemon,
+ const struct sockaddr **servaddr,
+ va_list ap);
+
+
+/**
+ * Parse a list of options given as varargs.
+ *
+ * @param daemon the daemon to initialize
+ * @param servaddr where to store the server's listen address
+ * @param ... the options
+ * @return MHD_YES on success, MHD_NO on error
+ */
+static int
+parse_options (struct MHD_Daemon *daemon,
+ const struct sockaddr **servaddr,
+ ...)
+{
+ va_list ap;
+ int ret;
+
+ va_start (ap, servaddr);
+ ret = parse_options_va (daemon, servaddr, ap);
+ va_end (ap);
+ return ret;
+}
+
+
+/**
+ * Parse a list of options given as varargs.
+ *
+ * @param daemon the daemon to initialize
+ * @param servaddr where to store the server's listen address
+ * @param ap the options
+ * @return MHD_YES on success, MHD_NO on error
+ */
+static int
+parse_options_va (struct MHD_Daemon *daemon,
+ const struct sockaddr **servaddr,
+ va_list ap)
+{
+ enum MHD_OPTION opt;
+ struct MHD_OptionItem *oa;
+ unsigned int i;
+#if HTTPS_SUPPORT
+ int ret;
+ const char *pstr;
+#endif
+
+ while (MHD_OPTION_END != (opt = (enum MHD_OPTION) va_arg (ap, int)))
+ {
+ switch (opt)
+ {
+ case MHD_OPTION_CONNECTION_MEMORY_LIMIT:
+ daemon->pool_size = va_arg (ap, size_t);
+ break;
+ case MHD_OPTION_CONNECTION_LIMIT:
+ daemon->max_connections = va_arg (ap, unsigned int);
+ break;
+ case MHD_OPTION_CONNECTION_TIMEOUT:
+ daemon->connection_timeout = va_arg (ap, unsigned int);
+ break;
+ case MHD_OPTION_NOTIFY_COMPLETED:
+ daemon->notify_completed =
+ va_arg (ap, MHD_RequestCompletedCallback);
+ daemon->notify_completed_cls = va_arg (ap, void *);
+ break;
+ case MHD_OPTION_PER_IP_CONNECTION_LIMIT:
+ daemon->per_ip_connection_limit = va_arg (ap, unsigned int);
+ break;
+ case MHD_OPTION_SOCK_ADDR:
+ *servaddr = va_arg (ap, const struct sockaddr *);
+ break;
+ case MHD_OPTION_URI_LOG_CALLBACK:
+ daemon->uri_log_callback =
+ va_arg (ap, LogCallback);
+ daemon->uri_log_callback_cls = va_arg (ap, void *);
+ break;
+ case MHD_OPTION_THREAD_POOL_SIZE:
+ daemon->worker_pool_size = va_arg (ap, unsigned int);
+ if (daemon->worker_pool_size >= (SIZE_MAX / sizeof (struct
MHD_Daemon)))
+ {
+#if HAVE_MESSAGES
+ MHD_DLOG (daemon,
+ "Specified thread pool size (%u) too big\n",
+ daemon->worker_pool_size);
+#endif
+ return MHD_NO;
+ }
+ break;
+#if HTTPS_SUPPORT
+ case MHD_OPTION_HTTPS_MEM_KEY:
+ if (0 != (daemon->options & MHD_USE_SSL))
+ daemon->https_mem_key = va_arg (ap, const char *);
+#if HAVE_MESSAGES
+ else
+ MHD_DLOG (daemon,
+ "MHD HTTPS option %d passed to MHD but MHD_USE_SSL not
set\n",
+ opt);
+#endif
+ break;
+ case MHD_OPTION_HTTPS_MEM_CERT:
+ if (0 != (daemon->options & MHD_USE_SSL))
+ daemon->https_mem_cert = va_arg (ap, const char *);
+#if HAVE_MESSAGES
+ else
+ MHD_DLOG (daemon,
+ "MHD HTTPS option %d passed to MHD but MHD_USE_SSL not
set\n",
+ opt);
+#endif
+ break;
+ case MHD_OPTION_HTTPS_MEM_TRUST:
+ if (0 != (daemon->options & MHD_USE_SSL))
+ daemon->https_mem_trust = va_arg (ap, const char *);
+#if HAVE_MESSAGES
+ else
+ MHD_DLOG (daemon,
+ "MHD HTTPS option %d passed to MHD but MHD_USE_SSL not
set\n",
+ opt);
+#endif
+ break;
+ case MHD_OPTION_HTTPS_CRED_TYPE:
+ daemon->cred_type = (gnutls_credentials_type_t) va_arg (ap, int);
+ break;
+ case MHD_OPTION_HTTPS_PRIORITIES:
+ if (0 != (daemon->options & MHD_USE_SSL))
+ {
+ gnutls_priority_deinit (daemon->priority_cache);
+ ret = gnutls_priority_init (&daemon->priority_cache,
+ pstr = va_arg (ap, const char*),
+ NULL);
+ if (ret != GNUTLS_E_SUCCESS)
+ {
+#if HAVE_MESSAGES
+ MHD_DLOG (daemon,
+ "Setting priorities to `%s' failed: %s\n",
+ pstr,
+ gnutls_strerror (ret));
+#endif
+ daemon->priority_cache = NULL;
+ return MHD_NO;
+ }
+ }
+ break;
+#endif
+#ifdef DAUTH_SUPPORT
+ case MHD_OPTION_DIGEST_AUTH_RANDOM:
+ daemon->digest_auth_rand_size = va_arg (ap, size_t);
+ daemon->digest_auth_random = va_arg (ap, const char *);
+ break;
+ case MHD_OPTION_NONCE_NC_SIZE:
+ daemon->nonce_nc_size = va_arg (ap, unsigned int);
+ break;
+#endif
+ case MHD_OPTION_LISTEN_SOCKET:
+ daemon->socket_fd = va_arg (ap, int);
+ break;
+ case MHD_OPTION_EXTERNAL_LOGGER:
+#if HAVE_MESSAGES
+ daemon->custom_error_log =
+ va_arg (ap, VfprintfFunctionPointerType);
+ daemon->custom_error_log_cls = va_arg (ap, void *);
+#else
+ va_arg (ap, VfprintfFunctionPointerType);
+ va_arg (ap, void *);
+#endif
+ break;
+ case MHD_OPTION_THREAD_STACK_SIZE:
+ daemon->thread_stack_size = va_arg (ap, size_t);
+ break;
+ case MHD_OPTION_ARRAY:
+ oa = va_arg (ap, struct MHD_OptionItem*);
+ i = 0;
+ while (MHD_OPTION_END != (opt = oa[i].option))
+ {
+ switch (opt)
+ {
+ /* all options taking 'size_t' */
+ case MHD_OPTION_CONNECTION_MEMORY_LIMIT:
+ case MHD_OPTION_THREAD_STACK_SIZE:
+ if (MHD_YES != parse_options (daemon,
+ servaddr,
+ opt,
+ (size_t) oa[i].value,
+ MHD_OPTION_END))
+ return MHD_NO;
+ break;
+ /* all options taking 'unsigned int' */
+ case MHD_OPTION_NONCE_NC_SIZE:
+ case MHD_OPTION_CONNECTION_LIMIT:
+ case MHD_OPTION_CONNECTION_TIMEOUT:
+ case MHD_OPTION_PER_IP_CONNECTION_LIMIT:
+ case MHD_OPTION_THREAD_POOL_SIZE:
+ if (MHD_YES != parse_options (daemon,
+ servaddr,
+ opt,
+ (unsigned int) oa[i].value,
+ MHD_OPTION_END))
+ return MHD_NO;
+ break;
+ /* all options taking 'int' or 'enum' */
+ case MHD_OPTION_HTTPS_CRED_TYPE:
+ case MHD_OPTION_LISTEN_SOCKET:
+ if (MHD_YES != parse_options (daemon,
+ servaddr,
+ opt,
+ (int) oa[i].value,
+ MHD_OPTION_END))
+ return MHD_NO;
+ break;
+ /* all options taking one pointer */
+ case MHD_OPTION_SOCK_ADDR:
+ case MHD_OPTION_HTTPS_MEM_KEY:
+ case MHD_OPTION_HTTPS_MEM_CERT:
+ case MHD_OPTION_HTTPS_MEM_TRUST:
+ case MHD_OPTION_HTTPS_PRIORITIES:
+ case MHD_OPTION_ARRAY:
+ if (MHD_YES != parse_options (daemon,
+ servaddr,
+ opt,
+ oa[i].ptr_value,
+ MHD_OPTION_END))
+ return MHD_NO;
+ break;
+ /* all options taking two pointers */
+ case MHD_OPTION_NOTIFY_COMPLETED:
+ case MHD_OPTION_URI_LOG_CALLBACK:
+ case MHD_OPTION_EXTERNAL_LOGGER:
+ case MHD_OPTION_UNESCAPE_CALLBACK:
+ if (MHD_YES != parse_options (daemon,
+ servaddr,
+ opt,
+ (void *) oa[i].value,
+ oa[i].ptr_value,
+ MHD_OPTION_END))
+ return MHD_NO;
+ break;
+ /* options taking size_t-number followed by pointer */
+ case MHD_OPTION_DIGEST_AUTH_RANDOM:
+ if (MHD_YES != parse_options (daemon,
+ servaddr,
+ opt,
+ (size_t) oa[i].value,
+ oa[i].ptr_value,
+ MHD_OPTION_END))
+ return MHD_NO;
+ break;
+ default:
+ return MHD_NO;
+ }
+ i++;
+ }
+ break;
+ case MHD_OPTION_UNESCAPE_CALLBACK:
+ daemon->unescape_callback =
+ va_arg (ap, UnescapeCallback);
+ daemon->unescape_callback_cls = va_arg (ap, void *);
+ break;
+ default:
+#if HAVE_MESSAGES
+ if (((opt >= MHD_OPTION_HTTPS_MEM_KEY) &&
+ (opt <= MHD_OPTION_HTTPS_PRIORITIES)) || (opt ==
MHD_OPTION_HTTPS_MEM_TRUST))
+ {
+ MHD_DLOG (daemon,
+ "MHD HTTPS option %d passed to MHD compiled without
HTTPS support\n",
+ opt);
+ }
+ else
+ {
+ MHD_DLOG (daemon,
+ "Invalid option %d! (Did you terminate the list with
MHD_OPTION_END?)\n",
+ opt);
+ }
+#endif
+ return MHD_NO;
+ }
+ }
+ return MHD_YES;
+}
+
+
+/**
+ * Create a listen socket, if possible with CLOEXEC flag set.
+ *
+ * @param domain socket domain (i.e. PF_INET)
+ * @param type socket type (usually SOCK_STREAM)
+ * @param protocol desired protocol, 0 for default
+ */
+static int
+create_socket (int domain, int type, int protocol)
+{
+ static int sock_cloexec = SOCK_CLOEXEC;
+ int ctype = SOCK_STREAM | sock_cloexec;
+ int fd;
+ int flags;
+#ifdef WINDOWS
+ DWORD dwFlags;
+#endif
+
+ /* use SOCK_STREAM rather than ai_socktype: some getaddrinfo
+ * implementations do not set ai_socktype, e.g. RHL6.2. */
+ fd = SOCKET (domain, ctype, protocol);
+ if ( (-1 == fd) && (EINVAL == errno) && (0 != sock_cloexec) )
+ {
+ sock_cloexec = 0;
+ fd = SOCKET(domain, type, protocol);
+ }
+ if (-1 == fd)
+ return -1;
+ if (0 != sock_cloexec)
+ return fd; /* this is it */
+ /* flag was not set during 'socket' call, let's try setting it manually */
+#ifndef WINDOWS
+ flags = fcntl (fd, F_GETFD);
+ if (flags < 0)
+#else
+ if (!GetHandleInformation ((HANDLE) fd, &dwFlags))
+#endif
+ {
+#ifdef WINDOWS
+ SetErrnoFromWinError (GetLastError ());
+#endif
+ return fd; /* good luck */
+ }
+#ifndef WINDOWS
+ if (flags == (flags | FD_CLOEXEC))
+ return fd; /* already set */
+ flags |= FD_CLOEXEC;
+ if (0 != fcntl (fd, F_SETFD, flags))
+#else
+ if (dwFlags != (dwFlags | HANDLE_FLAG_INHERIT))
+ return fd; /* already unset */
+ if (!SetHandleInformation ((HANDLE) fd, HANDLE_FLAG_INHERIT, 0))
+#endif
+ {
+#ifdef WINDOWS
+ SetErrnoFromWinError (GetLastError ());
+#endif
+ return fd; /* good luck */
+ }
+ return fd;
+}
+
+
+/**
+ * Start a webserver on the given port.
+ *
+ * @param flags combination of MHD_FLAG values
+ * @param port port to bind to
+ * @param apc callback to call to check which clients
+ * will be allowed to connect
+ * @param apc_cls extra argument to apc
+ * @param dh default handler for all URIs
+ * @param dh_cls extra argument to dh
+ * @param ap list of options (type-value pairs,
+ * terminated with MHD_OPTION_END).
+ * @return NULL on error, handle to daemon on success
+ */
+struct MHD_Daemon *
+MHD_start_daemon_va (unsigned int flags,
+ uint16_t port,
+ MHD_AcceptPolicyCallback apc,
+ void *apc_cls,
+ MHD_AccessHandlerCallback dh, void *dh_cls,
+ va_list ap)
+{
+ const int on = 1;
+ struct MHD_Daemon *daemon;
+ int socket_fd;
+ struct sockaddr_in servaddr4;
+#if HAVE_INET6
+ struct sockaddr_in6 servaddr6;
+#endif
+ const struct sockaddr *servaddr = NULL;
+ socklen_t addrlen;
+ unsigned int i;
+ int res_thread_create;
+ int use_pipe;
+
+#ifndef HAVE_INET6
+ if (0 != (flags & MHD_USE_IPv6))
+ return NULL;
+#endif
+#ifndef HAVE_POLL_H
+ if (0 != (flags & MHD_USE_POLL))
+ return NULL;
+#endif
+#if ! HTTPS_SUPPORT
+ if (0 != (flags & MHD_USE_SSL))
+ return NULL;
+#endif
+ if (NULL == dh)
+ return NULL;
+ if (NULL == (daemon = malloc (sizeof (struct MHD_Daemon))))
+ return NULL;
+ memset (daemon, 0, sizeof (struct MHD_Daemon));
+ /* try to open listen socket */
+#if HTTPS_SUPPORT
+ if (0 != (flags & MHD_USE_SSL))
+ {
+ gnutls_priority_init (&daemon->priority_cache,
+ "NORMAL",
+ NULL);
+ }
+#endif
+ daemon->socket_fd = -1;
+ daemon->options = (enum MHD_OPTION) flags;
+ daemon->port = port;
+ daemon->apc = apc;
+ daemon->apc_cls = apc_cls;
+ daemon->default_handler = dh;
+ daemon->default_handler_cls = dh_cls;
+ daemon->max_connections = MHD_MAX_CONNECTIONS_DEFAULT;
+ daemon->pool_size = MHD_POOL_SIZE_DEFAULT;
+ daemon->unescape_callback = &MHD_http_unescape;
+ daemon->connection_timeout = 0; /* no timeout */
+ daemon->wpipe[0] = -1;
+ daemon->wpipe[1] = -1;
+#if HAVE_MESSAGES
+ daemon->custom_error_log = (MHD_LogCallback) &vfprintf;
+ daemon->custom_error_log_cls = stderr;
+#endif
+#ifdef HAVE_LISTEN_SHUTDOWN
+ use_pipe = (0 != (daemon->options & MHD_USE_NO_LISTEN_SOCKET));
+#else
+ use_pipe = 1; /* yes, must use pipe to signal shutdown */
+#endif
+ if ( (use_pipe) &&
+ (0 != PIPE (daemon->wpipe)) )
+ {
+#if HAVE_MESSAGES
+ MHD_DLOG (daemon,
+ "Failed to create control pipe: %s\n",
+ STRERROR (errno));
+#endif
+ free (daemon);
+ return NULL;
+ }
+#ifndef WINDOWS
+ if ( (0 == (flags & MHD_USE_POLL)) &&
+ (daemon->wpipe[0] >= FD_SETSIZE) )
+ {
+#if HAVE_MESSAGES
+ MHD_DLOG (daemon,
+ "file descriptor for control pipe exceeds maximum value\n");
+#endif
+ CLOSE (daemon->wpipe[0]);
+ CLOSE (daemon->wpipe[1]);
+ free (daemon);
+ return NULL;
+ }
+#endif
+#ifdef DAUTH_SUPPORT
+ daemon->digest_auth_rand_size = 0;
+ daemon->digest_auth_random = NULL;
+ daemon->nonce_nc_size = 4; /* tiny */
+#endif
+#if HTTPS_SUPPORT
+ if (0 != (flags & MHD_USE_SSL))
+ {
+ daemon->cred_type = GNUTLS_CRD_CERTIFICATE;
+ }
+#endif
+
+
+ if (MHD_YES != parse_options_va (daemon, &servaddr, ap))
+ {
+#if HTTPS_SUPPORT
+ if ( (0 != (flags & MHD_USE_SSL)) &&
+ (NULL != daemon->priority_cache) )
+ gnutls_priority_deinit (daemon->priority_cache);
+#endif
+ free (daemon);
+ return NULL;
+ }
+#ifdef DAUTH_SUPPORT
+ if (daemon->nonce_nc_size > 0)
+ {
+ if ( ( (size_t) (daemon->nonce_nc_size * sizeof(struct MHD_NonceNc))) /
+ sizeof(struct MHD_NonceNc) != daemon->nonce_nc_size)
+ {
+#if HAVE_MESSAGES
+ MHD_DLOG (daemon,
+ "Specified value for NC_SIZE too large\n");
+#endif
+#if HTTPS_SUPPORT
+ if (0 != (flags & MHD_USE_SSL))
+ gnutls_priority_deinit (daemon->priority_cache);
+#endif
+ free (daemon);
+ return NULL;
+ }
+ daemon->nnc = malloc (daemon->nonce_nc_size * sizeof(struct
MHD_NonceNc));
+ if (NULL == daemon->nnc)
+ {
+#if HAVE_MESSAGES
+ MHD_DLOG (daemon,
+ "Failed to allocate memory for nonce-nc map: %s\n",
+ STRERROR (errno));
+#endif
+#if HTTPS_SUPPORT
+ if (0 != (flags & MHD_USE_SSL))
+ gnutls_priority_deinit (daemon->priority_cache);
+#endif
+ free (daemon);
+ return NULL;
+ }
+ }
+
+ if (0 != pthread_mutex_init (&daemon->nnc_lock, NULL))
+ {
+#if HAVE_MESSAGES
+ MHD_DLOG (daemon,
+ "MHD failed to initialize nonce-nc mutex\n");
+#endif
+#if HTTPS_SUPPORT
+ if (0 != (flags & MHD_USE_SSL))
+ gnutls_priority_deinit (daemon->priority_cache);
+#endif
+ free (daemon->nnc);
+ free (daemon);
+ return NULL;
+ }
+#endif
+
+ /* Thread pooling currently works only with internal select thread model */
+ if ( (0 == (flags & MHD_USE_SELECT_INTERNALLY)) &&
+ (daemon->worker_pool_size > 0) )
+ {
+#if HAVE_MESSAGES
+ MHD_DLOG (daemon,
+ "MHD thread pooling only works with
MHD_USE_SELECT_INTERNALLY\n");
+#endif
+ goto free_and_fail;
+ }
+
+#ifdef __SYMBIAN32__
+ if (0 != (flags & (MHD_USE_SELECT_INTERNALLY |
MHD_USE_THREAD_PER_CONNECTION)))
+ {
+#if HAVE_MESSAGES
+ MHD_DLOG (daemon,
+ "Threaded operations are not supported on Symbian.\n");
+#endif
+ goto free_and_fail;
+ }
+#endif
+ if ( (-1 == daemon->socket_fd) &&
+ (0 == (daemon->options & MHD_USE_NO_LISTEN_SOCKET)) )
+ {
+ /* try to open listen socket */
+ if ((flags & MHD_USE_IPv6) != 0)
+ socket_fd = create_socket (PF_INET6, SOCK_STREAM, 0);
+ else
+ socket_fd = create_socket (PF_INET, SOCK_STREAM, 0);
+ if (-1 == socket_fd)
+ {
+#if HAVE_MESSAGES
+ if (0 != (flags & MHD_USE_DEBUG))
+ MHD_DLOG (daemon,
+ "Call to socket failed: %s\n",
+ STRERROR (errno));
+#endif
+ goto free_and_fail;
+ }
+ if ((SETSOCKOPT (socket_fd,
+ SOL_SOCKET,
+ SO_REUSEADDR,
+ &on, sizeof (on)) < 0) && ((flags & MHD_USE_DEBUG) != 0))
+ {
+#if HAVE_MESSAGES
+ MHD_DLOG (daemon,
+ "setsockopt failed: %s\n",
+ STRERROR (errno));
+#endif
+ }
+
+ /* check for user supplied sockaddr */
+#if HAVE_INET6
+ if (0 != (flags & MHD_USE_IPv6))
+ addrlen = sizeof (struct sockaddr_in6);
+ else
+#endif
+ addrlen = sizeof (struct sockaddr_in);
+ if (NULL == servaddr)
+ {
+#if HAVE_INET6
+ if (0 != (flags & MHD_USE_IPv6))
+ {
+ memset (&servaddr6, 0, sizeof (struct sockaddr_in6));
+ servaddr6.sin6_family = AF_INET6;
+ servaddr6.sin6_port = htons (port);
+#if HAVE_SOCKADDR_IN_SIN_LEN
+ servaddr6.sin6_len = sizeof (struct sockaddr_in6);
+#endif
+ servaddr = (struct sockaddr *) &servaddr6;
+ }
+ else
+#endif
+ {
+ memset (&servaddr4, 0, sizeof (struct sockaddr_in));
+ servaddr4.sin_family = AF_INET;
+ servaddr4.sin_port = htons (port);
+#if HAVE_SOCKADDR_IN_SIN_LEN
+ servaddr4.sin_len = sizeof (struct sockaddr_in);
+#endif
+ servaddr = (struct sockaddr *) &servaddr4;
+ }
+ }
+ daemon->socket_fd = socket_fd;
+
+ if (0 != (flags & MHD_USE_IPv6))
+ {
+#ifdef IPPROTO_IPV6
+#ifdef IPV6_V6ONLY
+ /* Note: "IPV6_V6ONLY" is declared by Windows Vista ff., see
"IPPROTO_IPV6 Socket Options"
+
(http://msdn.microsoft.com/en-us/library/ms738574%28v=VS.85%29.aspx);
+ and may also be missing on older POSIX systems; good luck if you
have any of those,
+ your IPv6 socket may then also bind against IPv4... */
+#ifndef WINDOWS
+ const int on = 1;
+ setsockopt (socket_fd,
+ IPPROTO_IPV6, IPV6_V6ONLY,
+ &on, sizeof (on));
+#else
+ const char on = 1;
+ setsockopt (socket_fd,
+ IPPROTO_IPV6, IPV6_V6ONLY,
+ &on, sizeof (on));
+#endif
+#endif
+#endif
+ }
+ if (-1 == BIND (socket_fd, servaddr, addrlen))
+ {
+#if HAVE_MESSAGES
+ if (0 != (flags & MHD_USE_DEBUG))
+ MHD_DLOG (daemon,
+ "Failed to bind to port %u: %s\n",
+ (unsigned int) port,
+ STRERROR (errno));
+#endif
+ CLOSE (socket_fd);
+ goto free_and_fail;
+ }
+
+ if (LISTEN (socket_fd, 20) < 0)
+ {
+#if HAVE_MESSAGES
+ if (0 != (flags & MHD_USE_DEBUG))
+ MHD_DLOG (daemon,
+ "Failed to listen for connections: %s\n",
+ STRERROR (errno));
+#endif
+ CLOSE (socket_fd);
+ goto free_and_fail;
+ }
+ }
+ else
+ {
+ socket_fd = daemon->socket_fd;
+ }
+#ifndef WINDOWS
+ if ( (socket_fd >= FD_SETSIZE) &&
+ (0 == (flags & MHD_USE_POLL)) )
+ {
+#if HAVE_MESSAGES
+ if ((flags & MHD_USE_DEBUG) != 0)
+ MHD_DLOG (daemon,
+ "Socket descriptor larger than FD_SETSIZE: %d > %d\n",
+ socket_fd,
+ FD_SETSIZE);
+#endif
+ CLOSE (socket_fd);
+ goto free_and_fail;
+ }
+#endif
+
+ if (0 != pthread_mutex_init (&daemon->per_ip_connection_mutex, NULL))
+ {
+#if HAVE_MESSAGES
+ MHD_DLOG (daemon,
+ "MHD failed to initialize IP connection limit mutex\n");
+#endif
+ if (-1 != socket_fd)
+ CLOSE (socket_fd);
+ goto free_and_fail;
+ }
+ if (0 != pthread_mutex_init (&daemon->cleanup_connection_mutex, NULL))
+ {
+#if HAVE_MESSAGES
+ MHD_DLOG (daemon,
+ "MHD failed to initialize IP connection limit mutex\n");
+#endif
+ pthread_mutex_destroy (&daemon->cleanup_connection_mutex);
+ if (-1 != socket_fd)
+ CLOSE (socket_fd);
+ goto free_and_fail;
+ }
+
+#if HTTPS_SUPPORT
+ /* initialize HTTPS daemon certificate aspects & send / recv functions */
+ if ((0 != (flags & MHD_USE_SSL)) && (0 != MHD_TLS_init (daemon)))
+ {
+#if HAVE_MESSAGES
+ MHD_DLOG (daemon,
+ "Failed to initialize TLS support\n");
+#endif
+ if (-1 != socket_fd)
+ CLOSE (socket_fd);
+ pthread_mutex_destroy (&daemon->cleanup_connection_mutex);
+ pthread_mutex_destroy (&daemon->per_ip_connection_mutex);
+ goto free_and_fail;
+ }
+#endif
+ if ( ( (0 != (flags & MHD_USE_THREAD_PER_CONNECTION)) ||
+ ( (0 != (flags & MHD_USE_SELECT_INTERNALLY)) &&
+ (0 == daemon->worker_pool_size)) ) &&
+ (0 == (daemon->options & MHD_USE_NO_LISTEN_SOCKET)) &&
+ (0 != (res_thread_create =
+ create_thread (&daemon->pid, daemon, &MHD_select_thread,
daemon))))
+ {
+#if HAVE_MESSAGES
+ MHD_DLOG (daemon,
+ "Failed to create listen thread: %s\n",
+ STRERROR (res_thread_create));
+#endif
+ pthread_mutex_destroy (&daemon->cleanup_connection_mutex);
+ pthread_mutex_destroy (&daemon->per_ip_connection_mutex);
+ if (-1 != socket_fd)
+ CLOSE (socket_fd);
+ goto free_and_fail;
+ }
+ if ( (daemon->worker_pool_size > 0) &&
+ (0 == (daemon->options & MHD_USE_NO_LISTEN_SOCKET)) )
+ {
+#ifndef MINGW
+ int sk_flags;
+#else
+ unsigned long sk_flags;
+#endif
+
+ /* Coarse-grained count of connections per thread (note error
+ * due to integer division). Also keep track of how many
+ * connections are leftover after an equal split. */
+ unsigned int conns_per_thread = daemon->max_connections
+ / daemon->worker_pool_size;
+ unsigned int leftover_conns = daemon->max_connections
+ % daemon->worker_pool_size;
+
+ i = 0; /* we need this in case fcntl or malloc fails */
+
+ /* Accept must be non-blocking. Multiple children may wake up
+ * to handle a new connection, but only one will win the race.
+ * The others must immediately return. */
+#ifndef MINGW
+ sk_flags = fcntl (socket_fd, F_GETFL);
+ if (sk_flags < 0)
+ goto thread_failed;
+ if (0 != fcntl (socket_fd, F_SETFL, sk_flags | O_NONBLOCK))
+ goto thread_failed;
+#else
+ sk_flags = 1;
+#if HAVE_PLIBC_FD
+ if (SOCKET_ERROR ==
+ ioctlsocket (plibc_fd_get_handle (socket_fd), FIONBIO, &sk_flags))
+ goto thread_failed;
+#else
+ if (ioctlsocket (socket_fd, FIONBIO, &sk_flags) == SOCKET_ERROR)
+ goto thread_failed;
+#endif // PLIBC_FD
+#endif // MINGW
+
+ /* Allocate memory for pooled objects */
+ daemon->worker_pool = malloc (sizeof (struct MHD_Daemon)
+ * daemon->worker_pool_size);
+ if (NULL == daemon->worker_pool)
+ goto thread_failed;
+
+ /* Start the workers in the pool */
+ for (i = 0; i < daemon->worker_pool_size; ++i)
+ {
+ /* Create copy of the Daemon object for each worker */
+ struct MHD_Daemon *d = &daemon->worker_pool[i];
+ memcpy (d, daemon, sizeof (struct MHD_Daemon));
+
+ /* Adjust pooling params for worker daemons; note that memcpy()
+ has already copied MHD_USE_SELECT_INTERNALLY thread model into
+ the worker threads. */
+ d->master = daemon;
+ d->worker_pool_size = 0;
+ d->worker_pool = NULL;
+
+ /* Divide available connections evenly amongst the threads.
+ * Thread indexes in [0, leftover_conns) each get one of the
+ * leftover connections. */
+ d->max_connections = conns_per_thread;
+ if (i < leftover_conns)
+ ++d->max_connections;
+
+ /* Must init cleanup connection mutex for each worker */
+ if (0 != pthread_mutex_init (&d->cleanup_connection_mutex, NULL))
+ {
+#if HAVE_MESSAGES
+ MHD_DLOG (daemon,
+ "MHD failed to initialize cleanup connection mutex for
thread worker %d\n", i);
+#endif
+ goto thread_failed;
+ }
+
+ /* Spawn the worker thread */
+ if (0 != (res_thread_create = create_thread (&d->pid, daemon,
&MHD_select_thread, d)))
+ {
+#if HAVE_MESSAGES
+ MHD_DLOG (daemon,
+ "Failed to create pool thread: %s\n",
+ STRERROR (res_thread_create));
+#endif
+ /* Free memory for this worker; cleanup below handles
+ * all previously-created workers. */
+ pthread_mutex_destroy (&d->cleanup_connection_mutex);
+ goto thread_failed;
+ }
+ }
+ }
+ return daemon;
+
+thread_failed:
+ /* If no worker threads created, then shut down normally. Calling
+ MHD_stop_daemon (as we do below) doesn't work here since it
+ assumes a 0-sized thread pool means we had been in the default
+ MHD_USE_SELECT_INTERNALLY mode. */
+ if (0 == i)
+ {
+ if (-1 != socket_fd)
+ CLOSE (socket_fd);
+ pthread_mutex_destroy (&daemon->cleanup_connection_mutex);
+ pthread_mutex_destroy (&daemon->per_ip_connection_mutex);
+ if (NULL != daemon->worker_pool)
+ free (daemon->worker_pool);
+ goto free_and_fail;
+ }
+
+ /* Shutdown worker threads we've already created. Pretend
+ as though we had fully initialized our daemon, but
+ with a smaller number of threads than had been
+ requested. */
+ daemon->worker_pool_size = i - 1;
+ MHD_stop_daemon (daemon);
+ return NULL;
+
+ free_and_fail:
+ /* clean up basic memory state in 'daemon' and return NULL to
+ indicate failure */
+#ifdef DAUTH_SUPPORT
+ free (daemon->nnc);
+ pthread_mutex_destroy (&daemon->nnc_lock);
+#endif
+#if HTTPS_SUPPORT
+ if (0 != (flags & MHD_USE_SSL))
+ gnutls_priority_deinit (daemon->priority_cache);
+#endif
+ free (daemon);
+ return NULL;
+}
+
+
+/**
+ * Close all connections for the daemon; must only be called after
+ * all of the threads have been joined and there is no more concurrent
+ * activity on the connection lists.
+ *
+ * @param daemon daemon to close down
+ */
+static void
+close_all_connections (struct MHD_Daemon *daemon)
+{
+ struct MHD_Connection *pos;
+ void *unused;
+ int rc;
+
+ /* first, make sure all threads are aware of shutdown; need to
+ traverse DLLs in peace... */
+ if (0 != pthread_mutex_lock(&daemon->cleanup_connection_mutex))
+ {
+ MHD_PANIC ("Failed to acquire cleanup mutex\n");
+ }
+ for (pos = daemon->connections_head; NULL != pos; pos = pos->next)
+ SHUTDOWN (pos->socket_fd,
+ (pos->read_closed == MHD_YES) ? SHUT_WR : SHUT_RDWR);
+ if (0 != pthread_mutex_unlock (&daemon->cleanup_connection_mutex))
+ {
+ MHD_PANIC ("Failed to release cleanup mutex\n");
+ }
+
+ /* now, collect threads */
+ if (0 != (daemon->options & MHD_USE_THREAD_PER_CONNECTION))
+ {
+ while (NULL != (pos = daemon->connections_head))
+ {
+ if (0 != (rc = pthread_join (pos->pid, &unused)))
+ {
+ MHD_PANIC ("Failed to join a thread\n");
+ }
+ pos->thread_joined = MHD_YES;
+ }
+ }
+
+ /* now that we're alone, move everyone to cleanup */
+ while (NULL != (pos = daemon->connections_head))
+ {
+ MHD_connection_close (pos,
+ MHD_REQUEST_TERMINATED_DAEMON_SHUTDOWN);
+ DLL_remove (daemon->connections_head,
+ daemon->connections_tail,
+ pos);
+ DLL_insert (daemon->cleanup_head,
+ daemon->cleanup_tail,
+ pos);
+ }
+ MHD_cleanup_connections (daemon);
+}
+
+
+/**
+ * Shutdown an http daemon
+ *
+ * @param daemon daemon to stop
+ */
+void
+MHD_stop_daemon (struct MHD_Daemon *daemon)
+{
+ void *unused;
+ int fd;
+ unsigned int i;
+ int rc;
+
+ if (NULL == daemon)
+ return;
+ daemon->shutdown = MHD_YES;
+ fd = daemon->socket_fd;
+ daemon->socket_fd = -1;
+ /* Prepare workers for shutdown */
+ if (NULL != daemon->worker_pool)
+ {
+ /* MHD_USE_NO_LISTEN_SOCKET disables thread pools, hence we need to
check */
+ for (i = 0; i < daemon->worker_pool_size; ++i)
+ {
+ daemon->worker_pool[i].shutdown = MHD_YES;
+ daemon->worker_pool[i].socket_fd = -1;
+ }
+ }
+ if (-1 != daemon->wpipe[1])
+ {
+ if (1 != WRITE (daemon->wpipe[1], "e", 1))
+ MHD_PANIC ("failed to signal shutdownn via pipe");
+ }
+#ifdef HAVE_LISTEN_SHUTDOWN
+ else
+ {
+ /* fd might be -1 here due to 'MHD_quiesce_daemon' */
+ if (-1 != fd)
+ (void) SHUTDOWN (fd, SHUT_RDWR);
+ }
+#endif
+#if DEBUG_CLOSE
+#if HAVE_MESSAGES
+ MHD_DLOG (daemon, "MHD listen socket shutdown\n");
+#endif
+#endif
+
+
+ /* Signal workers to stop and clean them up */
+ if (NULL != daemon->worker_pool)
+ {
+ /* MHD_USE_NO_LISTEN_SOCKET disables thread pools, hence we need to
check */
+ for (i = 0; i < daemon->worker_pool_size; ++i)
+ {
+ if (0 != (rc = pthread_join (daemon->worker_pool[i].pid, &unused)))
+ {
+ MHD_PANIC ("Failed to join a thread\n");
+ }
+ close_all_connections (&daemon->worker_pool[i]);
+ pthread_mutex_destroy
(&daemon->worker_pool[i].cleanup_connection_mutex);
+ }
+ free (daemon->worker_pool);
+ }
+ else
+ {
+ /* clean up master threads */
+ if ((0 != (daemon->options & MHD_USE_THREAD_PER_CONNECTION)) ||
+ ((0 != (daemon->options & MHD_USE_SELECT_INTERNALLY))
+ && (0 == daemon->worker_pool_size)))
+ {
+ if (0 != (rc = pthread_join (daemon->pid, &unused)))
+ {
+ MHD_PANIC ("Failed to join a thread\n");
+ }
+ }
+ }
+ close_all_connections (daemon);
+ if (-1 != fd)
+ (void) CLOSE (fd);
+
+ /* TLS clean up */
+#if HTTPS_SUPPORT
+ if (0 != (daemon->options & MHD_USE_SSL))
+ {
+ gnutls_priority_deinit (daemon->priority_cache);
+ if (daemon->x509_cred)
+ gnutls_certificate_free_credentials (daemon->x509_cred);
+ }
+#endif
+
+#ifdef DAUTH_SUPPORT
+ free (daemon->nnc);
+ pthread_mutex_destroy (&daemon->nnc_lock);
+#endif
+ pthread_mutex_destroy (&daemon->per_ip_connection_mutex);
+ pthread_mutex_destroy (&daemon->cleanup_connection_mutex);
+
+ if (-1 != daemon->wpipe[1])
+ {
+ (void) CLOSE (daemon->wpipe[0]);
+ (void) CLOSE (daemon->wpipe[1]);
+ }
+ free (daemon);
+}
+
+
+/**
+ * Obtain information about the given daemon
+ * (not fully implemented!).
+ *
+ * @param daemon what daemon to get information about
+ * @param infoType what information is desired?
+ * @param ... depends on infoType
+ * @return NULL if this information is not available
+ * (or if the infoType is unknown)
+ */
+const union MHD_DaemonInfo *
+MHD_get_daemon_info (struct MHD_Daemon *daemon,
+ enum MHD_DaemonInfoType infoType, ...)
+{
+ switch (infoType)
+ {
+ case MHD_DAEMON_INFO_LISTEN_FD:
+ return (const union MHD_DaemonInfo *) &daemon->socket_fd;
+ default:
+ return NULL;
+ };
+}
+
+
+/**
+ * Sets the global error handler to a different implementation. "cb"
+ * will only be called in the case of typically fatal, serious
+ * internal consistency issues. These issues should only arise in the
+ * case of serious memory corruption or similar problems with the
+ * architecture. While "cb" is allowed to return and MHD will then
+ * try to continue, this is never safe.
+ *
+ * The default implementation that is used if no panic function is set
+ * simply prints an error message and calls "abort". Alternative
+ * implementations might call "exit" or other similar functions.
+ *
+ * @param cb new error handler
+ * @param cls passed to error handler
+ */
+void
+MHD_set_panic_func (MHD_PanicCallback cb, void *cls)
+{
+ mhd_panic = cb;
+ mhd_panic_cls = cls;
+}
+
+
+/**
+ * Obtain the version of this library
+ *
+ * @return static version string, e.g. "0.4.1"
+ */
+const char *
+MHD_get_version (void)
+{
+ return PACKAGE_VERSION;
+}
+
+
+#ifdef __GNUC__
+#define ATTRIBUTE_CONSTRUCTOR __attribute__ ((constructor))
+#define ATTRIBUTE_DESTRUCTOR __attribute__ ((destructor))
+#else // !__GNUC__
+#define ATTRIBUTE_CONSTRUCTOR
+#define ATTRIBUTE_DESTRUCTOR
+#endif // __GNUC__
+
+#if HTTPS_SUPPORT
+GCRY_THREAD_OPTION_PTHREAD_IMPL;
+#endif
+
+
+/**
+ * Initialize do setup work.
+ */
+void ATTRIBUTE_CONSTRUCTOR
+MHD_init ()
+{
+ mhd_panic = &mhd_panic_std;
+ mhd_panic_cls = NULL;
+
+#ifdef WINDOWS
+ plibc_init ("GNU", "libmicrohttpd");
+#endif
+#if HTTPS_SUPPORT
+ gcry_control (GCRYCTL_SET_THREAD_CBS, &gcry_threads_pthread);
+ gnutls_global_init ();
+#endif
+}
+
+
+void ATTRIBUTE_DESTRUCTOR
+MHD_fini ()
+{
+#if HTTPS_SUPPORT
+ gnutls_global_deinit ();
+#endif
+#ifdef WINDOWS
+ plibc_shutdown ();
+#endif
+}
+
+/* end of daemon.c */
Deleted: libmicrohttpd/src/microhttpd/digestauth.c
===================================================================
--- libmicrohttpd/src/daemon/digestauth.c 2013-05-05 12:01:06 UTC (rev
27023)
+++ libmicrohttpd/src/microhttpd/digestauth.c 2013-05-05 18:07:33 UTC (rev
27025)
@@ -1,804 +0,0 @@
-/*
- This file is part of libmicrohttpd
- (C) 2010, 2011, 2012 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
- version 2.1 of the License, or (at your option) any later version.
-
- This library is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- Lesser General Public License for more details.
-
- You should have received a copy of the GNU Lesser General Public
- License along with this library; if not, write to the Free Software
- Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
USA
-*/
-/**
- * @file digestauth.c
- * @brief Implements HTTP digest authentication
- * @author Amr Ali
- * @author Matthieu Speder
- */
-#include "platform.h"
-#include <limits.h>
-#include "internal.h"
-#include "md5.h"
-
-#define HASH_MD5_HEX_LEN (2 * MD5_DIGEST_SIZE)
-
-/**
- * Beginning string for any valid Digest authentication header.
- */
-#define _BASE "Digest "
-
-/**
- * Maximum length of a username for digest authentication.
- */
-#define MAX_USERNAME_LENGTH 128
-
-/**
- * Maximum length of a realm for digest authentication.
- */
-#define MAX_REALM_LENGTH 256
-
-/**
- * Maximum length of the response in digest authentication.
- */
-#define MAX_AUTH_RESPONSE_LENGTH 128
-
-
-/**
- * convert bin to hex
- *
- * @param bin binary data
- * @param len number of bytes in bin
- * @param hex pointer to len*2+1 bytes
- */
-static void
-cvthex (const unsigned char *bin,
- size_t len,
- char *hex)
-{
- size_t i;
- unsigned int j;
-
- for (i = 0; i < len; ++i)
- {
- j = (bin[i] >> 4) & 0x0f;
- hex[i * 2] = j <= 9 ? (j + '0') : (j + 'a' - 10);
- j = bin[i] & 0x0f;
- hex[i * 2 + 1] = j <= 9 ? (j + '0') : (j + 'a' - 10);
- }
- hex[len * 2] = '\0';
-}
-
-
-/**
- * calculate H(A1) as per RFC2617 spec and store the
- * result in 'sessionkey'.
- *
- * @param alg The hash algorithm used, can be "md5" or "md5-sess"
- * @param username A `char *' pointer to the username value
- * @param realm A `char *' pointer to the realm value
- * @param password A `char *' pointer to the password value
- * @param nonce A `char *' pointer to the nonce value
- * @param cnonce A `char *' pointer to the cnonce value
- * @param sessionkey pointer to buffer of HASH_MD5_HEX_LEN+1 bytes
- */
-static void
-digest_calc_ha1 (const char *alg,
- const char *username,
- const char *realm,
- const char *password,
- const char *nonce,
- const char *cnonce,
- char *sessionkey)
-{
- struct MD5Context md5;
- unsigned char ha1[MD5_DIGEST_SIZE];
-
- MD5Init (&md5);
- MD5Update (&md5, username, strlen (username));
- MD5Update (&md5, ":", 1);
- MD5Update (&md5, realm, strlen (realm));
- MD5Update (&md5, ":", 1);
- MD5Update (&md5, password, strlen (password));
- MD5Final (ha1, &md5);
- if (0 == strcasecmp (alg, "md5-sess"))
- {
- MD5Init (&md5);
- MD5Update (&md5, ha1, sizeof (ha1));
- MD5Update (&md5, ":", 1);
- MD5Update (&md5, nonce, strlen (nonce));
- MD5Update (&md5, ":", 1);
- MD5Update (&md5, cnonce, strlen (cnonce));
- MD5Final (ha1, &md5);
- }
- cvthex (ha1, sizeof (ha1), sessionkey);
-}
-
-
-/**
- * Calculate request-digest/response-digest as per RFC2617 spec
- *
- * @param ha1 H(A1)
- * @param nonce nonce from server
- * @param noncecount 8 hex digits
- * @param cnonce client nonce
- * @param qop qop-value: "", "auth" or "auth-int"
- * @param method method from request
- * @param uri requested URL
- * @param hentity H(entity body) if qop="auth-int"
- * @param response request-digest or response-digest
- */
-static void
-digest_calc_response (const char *ha1,
- const char *nonce,
- const char *noncecount,
- const char *cnonce,
- const char *qop,
- const char *method,
- const char *uri,
- const char *hentity,
- char *response)
-{
- struct MD5Context md5;
- unsigned char ha2[MD5_DIGEST_SIZE];
- unsigned char resphash[MD5_DIGEST_SIZE];
- char ha2hex[HASH_MD5_HEX_LEN + 1];
-
- MD5Init (&md5);
- MD5Update (&md5, method, strlen(method));
- MD5Update (&md5, ":", 1);
- MD5Update (&md5, uri, strlen(uri));
-#if 0
- if (0 == strcasecmp(qop, "auth-int"))
- {
- /* This is dead code since the rest of this module does
- not support auth-int. */
- MD5Update (&md5, ":", 1);
- if (NULL != hentity)
- MD5Update (&md5, hentity, strlen(hentity));
- }
-#endif
- MD5Final (ha2, &md5);
- cvthex (ha2, MD5_DIGEST_SIZE, ha2hex);
- MD5Init (&md5);
- /* calculate response */
- MD5Update (&md5, ha1, HASH_MD5_HEX_LEN);
- MD5Update (&md5, ":", 1);
- MD5Update (&md5, nonce, strlen(nonce));
- MD5Update (&md5, ":", 1);
- if ('\0' != *qop)
- {
- MD5Update (&md5, noncecount, strlen(noncecount));
- MD5Update (&md5, ":", 1);
- MD5Update (&md5, cnonce, strlen(cnonce));
- MD5Update (&md5, ":", 1);
- MD5Update (&md5, qop, strlen(qop));
- MD5Update (&md5, ":", 1);
- }
- MD5Update (&md5, ha2hex, HASH_MD5_HEX_LEN);
- MD5Final (resphash, &md5);
- cvthex (resphash, sizeof (resphash), response);
-}
-
-
-/**
- * Lookup subvalue off of the HTTP Authorization header.
- *
- * A description of the input format for 'data' is at
- * http://en.wikipedia.org/wiki/Digest_access_authentication
- *
- *
- * @param dest where to store the result (possibly truncated if
- * the buffer is not big enough).
- * @param size size of dest
- * @param data pointer to the Authorization header
- * @param key key to look up in data
- * @return size of the located value, 0 if otherwise
- */
-static int
-lookup_sub_value (char *dest,
- size_t size,
- const char *data,
- const char *key)
-{
- size_t keylen;
- size_t len;
- const char *ptr;
- const char *eq;
- const char *q1;
- const char *q2;
- const char *qn;
-
- if (0 == size)
- return 0;
- keylen = strlen (key);
- ptr = data;
- while ('\0' != *ptr)
- {
- if (NULL == (eq = strchr (ptr, '=')))
- return 0;
- q1 = eq + 1;
- while (' ' == *q1)
- q1++;
- if ('\"' != *q1)
- {
- q2 = strchr (q1, ',');
- qn = q2;
- }
- else
- {
- q1++;
- q2 = strchr (q1, '\"');
- if (NULL == q2)
- return 0; /* end quote not found */
- qn = q2 + 1;
- }
- if ( (0 == strncasecmp (ptr,
- key,
- keylen)) &&
- (eq == &ptr[keylen]) )
- {
- if (NULL == q2)
- {
- len = strlen (q1) + 1;
- if (size > len)
- size = len;
- size--;
- strncpy (dest,
- q1,
- size);
- dest[size] = '\0';
- return size;
- }
- else
- {
- if (size > (q2 - q1) + 1)
- size = (q2 - q1) + 1;
- size--;
- memcpy (dest,
- q1,
- size);
- dest[size] = '\0';
- return size;
- }
- }
- if (NULL == qn)
- return 0;
- ptr = strchr (qn, ',');
- if (NULL == ptr)
- return 0;
- ptr++;
- while (' ' == *ptr)
- ptr++;
- }
- return 0;
-}
-
-
-/**
- * Check nonce-nc map array with either new nonce counter
- * or a whole new nonce.
- *
- * @param connection The MHD connection structure
- * @param nonce A pointer that referenced a zero-terminated array of nonce
- * @param nc The nonce counter, zero to add the nonce to the array
- * @return MHD_YES if successful, MHD_NO if invalid (or we have no NC array)
- */
-static int
-check_nonce_nc (struct MHD_Connection *connection,
- const char *nonce,
- unsigned long int nc)
-{
- uint32_t off;
- uint32_t mod;
- const char *np;
-
- mod = connection->daemon->nonce_nc_size;
- if (0 == mod)
- return MHD_NO; /* no array! */
- /* super-fast xor-based "hash" function for HT lookup in nonce array */
- off = 0;
- np = nonce;
- while ('\0' != *np)
- {
- off = (off << 8) | (*np ^ (off >> 24));
- np++;
- }
- off = off % mod;
- /*
- * Look for the nonce, if it does exist and its corresponding
- * nonce counter is less than the current nonce counter by 1,
- * then only increase the nonce counter by one.
- */
-
- pthread_mutex_lock (&connection->daemon->nnc_lock);
- if (0 == nc)
- {
- strcpy(connection->daemon->nnc[off].nonce,
- nonce);
- connection->daemon->nnc[off].nc = 0;
- pthread_mutex_unlock (&connection->daemon->nnc_lock);
- return MHD_YES;
- }
- if ( (nc <= connection->daemon->nnc[off].nc) ||
- (0 != strcmp(connection->daemon->nnc[off].nonce, nonce)) )
- {
- pthread_mutex_unlock (&connection->daemon->nnc_lock);
-#if HAVE_MESSAGES
- MHD_DLOG (connection->daemon,
- "Stale nonce received. If this happens a lot, you should
probably increase the size of the nonce array.\n");
-#endif
- return MHD_NO;
- }
- connection->daemon->nnc[off].nc = nc;
- pthread_mutex_unlock (&connection->daemon->nnc_lock);
- return MHD_YES;
-}
-
-
-/**
- * Get the username from the authorization header sent by the client
- *
- * @param connection The MHD connection structure
- * @return NULL if no username could be found, a pointer
- * to the username if found
- */
-char *
-MHD_digest_auth_get_username(struct MHD_Connection *connection)
-{
- size_t len;
- char user[MAX_USERNAME_LENGTH];
- const char *header;
-
- if (NULL == (header = MHD_lookup_connection_value (connection,
- MHD_HEADER_KIND,
-
MHD_HTTP_HEADER_AUTHORIZATION)))
- return NULL;
- if (0 != strncmp (header, _BASE, strlen (_BASE)))
- return NULL;
- header += strlen (_BASE);
- if (0 == (len = lookup_sub_value (user,
- sizeof (user),
- header,
- "username")))
- return NULL;
- return strdup (user);
-}
-
-
-/**
- * Calculate the server nonce so that it mitigates replay attacks
- * The current format of the nonce is ...
- * H(timestamp ":" method ":" random ":" uri ":" realm) + Hex(timestamp)
- *
- * @param nonce_time The amount of time in seconds for a nonce to be invalid
- * @param method HTTP method
- * @param rnd A pointer to a character array for the random seed
- * @param rnd_size The size of the random seed array
- * @param uri HTTP URI (in MHD, without the arguments ("?k=v")
- * @param realm A string of characters that describes the realm of auth.
- * @param nonce A pointer to a character array for the nonce to put in
- */
-static void
-calculate_nonce (uint32_t nonce_time,
- const char *method,
- const char *rnd,
- unsigned int rnd_size,
- const char *uri,
- const char *realm,
- char *nonce)
-{
- struct MD5Context md5;
- unsigned char timestamp[4];
- unsigned char tmpnonce[MD5_DIGEST_SIZE];
- char timestamphex[sizeof(timestamp) * 2 + 1];
-
- MD5Init (&md5);
- timestamp[0] = (nonce_time & 0xff000000) >> 0x18;
- timestamp[1] = (nonce_time & 0x00ff0000) >> 0x10;
- timestamp[2] = (nonce_time & 0x0000ff00) >> 0x08;
- timestamp[3] = (nonce_time & 0x000000ff);
- MD5Update (&md5, timestamp, 4);
- MD5Update (&md5, ":", 1);
- MD5Update (&md5, method, strlen(method));
- MD5Update (&md5, ":", 1);
- if (rnd_size > 0)
- MD5Update (&md5, rnd, rnd_size);
- MD5Update (&md5, ":", 1);
- MD5Update (&md5, uri, strlen(uri));
- MD5Update (&md5, ":", 1);
- MD5Update (&md5, realm, strlen(realm));
- MD5Final (tmpnonce, &md5);
- cvthex (tmpnonce, sizeof (tmpnonce), nonce);
- cvthex (timestamp, 4, timestamphex);
- strncat (nonce, timestamphex, 8);
-}
-
-
-/**
- * Test if the given key-value pair is in the headers for the
- * given connection.
- *
- * @param connection the connection
- * @param key the key
- * @param value the value, can be NULL
- * @return MHD_YES if the key-value pair is in the headers,
- * MHD_NO if not
- */
-static int
-test_header (struct MHD_Connection *connection,
- const char *key,
- const char *value)
-{
- struct MHD_HTTP_Header *pos;
-
- for (pos = connection->headers_received; NULL != pos; pos = pos->next)
- {
- if (MHD_GET_ARGUMENT_KIND != pos->kind)
- continue;
- if (0 != strcmp (key, pos->header))
- continue;
- if ( (NULL == value) &&
- (NULL == pos->value) )
- return MHD_YES;
- if ( (NULL == value) ||
- (NULL == pos->value) ||
- (0 != strcmp (value, pos->value)) )
- continue;
- return MHD_YES;
- }
- return MHD_NO;
-}
-
-
-/**
- * Check that the arguments given by the client as part
- * of the authentication header match the arguments we
- * got as part of the HTTP request URI.
- *
- * @param connection connections with headers to compare against
- * @param args argument URI string (after "?" in URI)
- * @return MHD_YES if the arguments match,
- * MHD_NO if not
- */
-static int
-check_argument_match (struct MHD_Connection *connection,
- const char *args)
-{
- struct MHD_HTTP_Header *pos;
- size_t slen = strlen (args) + 1;
- char argb[slen];
- char *argp;
- char *equals;
- char *amper;
- unsigned int num_headers;
-
- num_headers = 0;
- memcpy (argb, args, slen);
- argp = argb;
- while ( (NULL != argp) &&
- ('\0' != argp[0]) )
- {
- equals = strchr (argp, '=');
- if (NULL == equals)
- {
- /* add with 'value' NULL */
- connection->daemon->unescape_callback
(connection->daemon->unescape_callback_cls,
- connection,
- argp);
- if (MHD_YES != test_header (connection, argp, NULL))
- return MHD_NO;
- num_headers++;
- break;
- }
- equals[0] = '\0';
- equals++;
- amper = strchr (equals, '&');
- if (NULL != amper)
- {
- amper[0] = '\0';
- amper++;
- }
- connection->daemon->unescape_callback
(connection->daemon->unescape_callback_cls,
- connection,
- argp);
- connection->daemon->unescape_callback
(connection->daemon->unescape_callback_cls,
- connection,
- equals);
- if (! test_header (connection, argp, equals))
- return MHD_NO;
- num_headers++;
- argp = amper;
- }
-
- /* also check that the number of headers matches */
- for (pos = connection->headers_received; NULL != pos; pos = pos->next)
- {
- if (MHD_GET_ARGUMENT_KIND != pos->kind)
- continue;
- num_headers--;
- }
- if (0 != num_headers)
- return MHD_NO;
- return MHD_YES;
-}
-
-
-/**
- * Authenticates the authorization header sent by the client
- *
- * @param connection The MHD connection structure
- * @param realm The realm presented to the client
- * @param username The username needs to be authenticated
- * @param password The password used in the authentication
- * @param nonce_timeout The amount of time for a nonce to be
- * invalid in seconds
- * @return MHD_YES if authenticated, MHD_NO if not,
- * MHD_INVALID_NONCE if nonce is invalid
- */
-int
-MHD_digest_auth_check (struct MHD_Connection *connection,
- const char *realm,
- const char *username,
- const char *password,
- unsigned int nonce_timeout)
-{
- size_t len;
- const char *header;
- char *end;
- char nonce[MAX_NONCE_LENGTH];
- char cnonce[MAX_NONCE_LENGTH];
- char qop[15]; /* auth,auth-int */
- char nc[20];
- char response[MAX_AUTH_RESPONSE_LENGTH];
- const char *hentity = NULL; /* "auth-int" is not supported */
- char ha1[HASH_MD5_HEX_LEN + 1];
- char respexp[HASH_MD5_HEX_LEN + 1];
- char noncehashexp[HASH_MD5_HEX_LEN + 9];
- uint32_t nonce_time;
- uint32_t t;
- size_t left; /* number of characters left in 'header' for 'uri' */
- unsigned long int nci;
-
- header = MHD_lookup_connection_value (connection,
- MHD_HEADER_KIND,
- MHD_HTTP_HEADER_AUTHORIZATION);
- if (NULL == header)
- return MHD_NO;
- if (0 != strncmp(header, _BASE, strlen(_BASE)))
- return MHD_NO;
- header += strlen (_BASE);
- left = strlen (header);
-
- {
- char un[MAX_USERNAME_LENGTH];
-
- len = lookup_sub_value (un,
- sizeof (un),
- header, "username");
- if ( (0 == len) ||
- (0 != strcmp(username, un)) )
- return MHD_NO;
- left -= strlen ("username") + len;
- }
-
- {
- char r[MAX_REALM_LENGTH];
-
- len = lookup_sub_value(r,
- sizeof (r),
- header, "realm");
- if ( (0 == len) ||
- (0 != strcmp(realm, r)) )
- return MHD_NO;
- left -= strlen ("realm") + len;
- }
-
- if (0 == (len = lookup_sub_value (nonce,
- sizeof (nonce),
- header, "nonce")))
- return MHD_NO;
- left -= strlen ("nonce") + len;
-
- {
- char uri[left];
-
- if (0 == lookup_sub_value(uri,
- sizeof (uri),
- header, "uri"))
- return MHD_NO;
-
- /* 8 = 4 hexadecimal numbers for the timestamp */
- nonce_time = strtoul(nonce + len - 8, (char **)NULL, 16);
- t = (uint32_t) MHD_monotonic_time();
- /*
- * First level vetting for the nonce validity if the timestamp
- * attached to the nonce exceeds `nonce_timeout' then the nonce is
- * invalid.
- */
- if ( (t > nonce_time + nonce_timeout) ||
- (nonce_time + nonce_timeout < nonce_time) )
- return MHD_INVALID_NONCE;
- if (0 != strncmp (uri,
- connection->url,
- strlen (connection->url)))
- {
-#if HAVE_MESSAGES
- MHD_DLOG (connection->daemon,
- "Authentication failed, URI does not match.\n");
-#endif
- return MHD_NO;
- }
- {
- const char *args = strchr (uri, '?');
-
- if (NULL == args)
- args = "";
- else
- args++;
- if (MHD_YES !=
- check_argument_match (connection,
- args) )
- {
-#if HAVE_MESSAGES
- MHD_DLOG (connection->daemon,
- "Authentication failed, arguments do not match.\n");
-#endif
- return MHD_NO;
- }
- }
- calculate_nonce (nonce_time,
- connection->method,
- connection->daemon->digest_auth_random,
- connection->daemon->digest_auth_rand_size,
- connection->url,
- realm,
- noncehashexp);
- /*
- * Second level vetting for the nonce validity
- * if the timestamp attached to the nonce is valid
- * and possibly fabricated (in case of an attack)
- * the attacker must also know the random seed to be
- * able to generate a "sane" nonce, which if he does
- * not, the nonce fabrication process going to be
- * very hard to achieve.
- */
-
- if (0 != strcmp (nonce, noncehashexp))
- return MHD_INVALID_NONCE;
- if ( (0 == lookup_sub_value (cnonce,
- sizeof (cnonce),
- header, "cnonce")) ||
- (0 == lookup_sub_value (qop, sizeof (qop), header, "qop")) ||
- ( (0 != strcmp (qop, "auth")) &&
- (0 != strcmp (qop, "")) ) ||
- (0 == lookup_sub_value (nc, sizeof (nc), header, "nc")) ||
- (0 == lookup_sub_value (response, sizeof (response), header,
"response")) )
- {
-#if HAVE_MESSAGES
- MHD_DLOG (connection->daemon,
- "Authentication failed, invalid format.\n");
-#endif
- return MHD_NO;
- }
- nci = strtoul (nc, &end, 16);
- if ( ('\0' != *end) ||
- ( (LONG_MAX == nci) &&
- (ERANGE == errno) ) )
- {
-#if HAVE_MESSAGES
- MHD_DLOG (connection->daemon,
- "Authentication failed, invalid format.\n");
-#endif
- return MHD_NO; /* invalid nonce format */
- }
- /*
- * Checking if that combination of nonce and nc is sound
- * and not a replay attack attempt. Also adds the nonce
- * to the nonce-nc map if it does not exist there.
- */
-
- if (MHD_YES != check_nonce_nc (connection, nonce, nci))
- return MHD_NO;
-
- digest_calc_ha1("md5",
- username,
- realm,
- password,
- nonce,
- cnonce,
- ha1);
- digest_calc_response (ha1,
- nonce,
- nc,
- cnonce,
- qop,
- connection->method,
- uri,
- hentity,
- respexp);
- return (0 == strcmp(response, respexp))
- ? MHD_YES
- : MHD_NO;
- }
-}
-
-
-/**
- * Queues a response to request authentication from the client
- *
- * @param connection The MHD connection structure
- * @param realm the realm presented to the client
- * @param opaque string to user for opaque value
- * @param signal_stale MHD_YES if the nonce is invalid to add
- * 'stale=true' to the authentication header
- * @return MHD_YES on success, MHD_NO otherwise
- */
-int
-MHD_queue_auth_fail_response (struct MHD_Connection *connection,
- const char *realm,
- const char *opaque,
- struct MHD_Response *response,
- int signal_stale)
-{
- int ret;
- size_t hlen;
- char nonce[HASH_MD5_HEX_LEN + 9];
-
- /* Generating the server nonce */
- calculate_nonce ((uint32_t) MHD_monotonic_time(),
- connection->method,
- connection->daemon->digest_auth_random,
- connection->daemon->digest_auth_rand_size,
- connection->url,
- realm,
- nonce);
- if (MHD_YES != check_nonce_nc (connection, nonce, 0))
- {
-#if HAVE_MESSAGES
- MHD_DLOG (connection->daemon,
- "Could not register nonce (is the nonce array size zero?).\n");
-#endif
- return MHD_NO;
- }
- /* Building the authentication header */
- hlen = snprintf (NULL,
- 0,
- "Digest
realm=\"%s\",qop=\"auth\",nonce=\"%s\",opaque=\"%s\"%s",
- realm,
- nonce,
- opaque,
- signal_stale
- ? ",stale=\"true\""
- : "");
- {
- char header[hlen + 1];
-
- snprintf (header,
- sizeof(header),
- "Digest realm=\"%s\",qop=\"auth\",nonce=\"%s\",opaque=\"%s\"%s",
- realm,
- nonce,
- opaque,
- signal_stale
- ? ",stale=\"true\""
- : "");
- ret = MHD_add_response_header(response,
- MHD_HTTP_HEADER_WWW_AUTHENTICATE,
- header);
- }
- if (MHD_YES == ret)
- ret = MHD_queue_response(connection,
- MHD_HTTP_UNAUTHORIZED,
- response);
- return ret;
-}
-
-
-/* end of digestauth.c */
Copied: libmicrohttpd/src/microhttpd/digestauth.c (from rev 27024,
libmicrohttpd/src/daemon/digestauth.c)
===================================================================
--- libmicrohttpd/src/microhttpd/digestauth.c (rev 0)
+++ libmicrohttpd/src/microhttpd/digestauth.c 2013-05-05 18:07:33 UTC (rev
27025)
@@ -0,0 +1,807 @@
+/*
+ This file is part of libmicrohttpd
+ (C) 2010, 2011, 2012 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
+ version 2.1 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
USA
+*/
+/**
+ * @file digestauth.c
+ * @brief Implements HTTP digest authentication
+ * @author Amr Ali
+ * @author Matthieu Speder
+ */
+#include "platform.h"
+#include <limits.h>
+#include "internal.h"
+#include "md5.h"
+
+#define HASH_MD5_HEX_LEN (2 * MD5_DIGEST_SIZE)
+
+/**
+ * Beginning string for any valid Digest authentication header.
+ */
+#define _BASE "Digest "
+
+/**
+ * Maximum length of a username for digest authentication.
+ */
+#define MAX_USERNAME_LENGTH 128
+
+/**
+ * Maximum length of a realm for digest authentication.
+ */
+#define MAX_REALM_LENGTH 256
+
+/**
+ * Maximum length of the response in digest authentication.
+ */
+#define MAX_AUTH_RESPONSE_LENGTH 128
+
+
+/**
+ * convert bin to hex
+ *
+ * @param bin binary data
+ * @param len number of bytes in bin
+ * @param hex pointer to len*2+1 bytes
+ */
+static void
+cvthex (const unsigned char *bin,
+ size_t len,
+ char *hex)
+{
+ size_t i;
+ unsigned int j;
+
+ for (i = 0; i < len; ++i)
+ {
+ j = (bin[i] >> 4) & 0x0f;
+ hex[i * 2] = j <= 9 ? (j + '0') : (j + 'a' - 10);
+ j = bin[i] & 0x0f;
+ hex[i * 2 + 1] = j <= 9 ? (j + '0') : (j + 'a' - 10);
+ }
+ hex[len * 2] = '\0';
+}
+
+
+/**
+ * calculate H(A1) as per RFC2617 spec and store the
+ * result in 'sessionkey'.
+ *
+ * @param alg The hash algorithm used, can be "md5" or "md5-sess"
+ * @param username A `char *' pointer to the username value
+ * @param realm A `char *' pointer to the realm value
+ * @param password A `char *' pointer to the password value
+ * @param nonce A `char *' pointer to the nonce value
+ * @param cnonce A `char *' pointer to the cnonce value
+ * @param sessionkey pointer to buffer of HASH_MD5_HEX_LEN+1 bytes
+ */
+static void
+digest_calc_ha1 (const char *alg,
+ const char *username,
+ const char *realm,
+ const char *password,
+ const char *nonce,
+ const char *cnonce,
+ char *sessionkey)
+{
+ struct MD5Context md5;
+ unsigned char ha1[MD5_DIGEST_SIZE];
+
+ MD5Init (&md5);
+ MD5Update (&md5, username, strlen (username));
+ MD5Update (&md5, ":", 1);
+ MD5Update (&md5, realm, strlen (realm));
+ MD5Update (&md5, ":", 1);
+ MD5Update (&md5, password, strlen (password));
+ MD5Final (ha1, &md5);
+ if (0 == strcasecmp (alg, "md5-sess"))
+ {
+ MD5Init (&md5);
+ MD5Update (&md5, ha1, sizeof (ha1));
+ MD5Update (&md5, ":", 1);
+ MD5Update (&md5, nonce, strlen (nonce));
+ MD5Update (&md5, ":", 1);
+ MD5Update (&md5, cnonce, strlen (cnonce));
+ MD5Final (ha1, &md5);
+ }
+ cvthex (ha1, sizeof (ha1), sessionkey);
+}
+
+
+/**
+ * Calculate request-digest/response-digest as per RFC2617 spec
+ *
+ * @param ha1 H(A1)
+ * @param nonce nonce from server
+ * @param noncecount 8 hex digits
+ * @param cnonce client nonce
+ * @param qop qop-value: "", "auth" or "auth-int"
+ * @param method method from request
+ * @param uri requested URL
+ * @param hentity H(entity body) if qop="auth-int"
+ * @param response request-digest or response-digest
+ */
+static void
+digest_calc_response (const char *ha1,
+ const char *nonce,
+ const char *noncecount,
+ const char *cnonce,
+ const char *qop,
+ const char *method,
+ const char *uri,
+ const char *hentity,
+ char *response)
+{
+ struct MD5Context md5;
+ unsigned char ha2[MD5_DIGEST_SIZE];
+ unsigned char resphash[MD5_DIGEST_SIZE];
+ char ha2hex[HASH_MD5_HEX_LEN + 1];
+
+ MD5Init (&md5);
+ MD5Update (&md5, method, strlen(method));
+ MD5Update (&md5, ":", 1);
+ MD5Update (&md5, uri, strlen(uri));
+#if 0
+ if (0 == strcasecmp(qop, "auth-int"))
+ {
+ /* This is dead code since the rest of this module does
+ not support auth-int. */
+ MD5Update (&md5, ":", 1);
+ if (NULL != hentity)
+ MD5Update (&md5, hentity, strlen(hentity));
+ }
+#endif
+ MD5Final (ha2, &md5);
+ cvthex (ha2, MD5_DIGEST_SIZE, ha2hex);
+ MD5Init (&md5);
+ /* calculate response */
+ MD5Update (&md5, ha1, HASH_MD5_HEX_LEN);
+ MD5Update (&md5, ":", 1);
+ MD5Update (&md5, nonce, strlen(nonce));
+ MD5Update (&md5, ":", 1);
+ if ('\0' != *qop)
+ {
+ MD5Update (&md5, noncecount, strlen(noncecount));
+ MD5Update (&md5, ":", 1);
+ MD5Update (&md5, cnonce, strlen(cnonce));
+ MD5Update (&md5, ":", 1);
+ MD5Update (&md5, qop, strlen(qop));
+ MD5Update (&md5, ":", 1);
+ }
+ MD5Update (&md5, ha2hex, HASH_MD5_HEX_LEN);
+ MD5Final (resphash, &md5);
+ cvthex (resphash, sizeof (resphash), response);
+}
+
+
+/**
+ * Lookup subvalue off of the HTTP Authorization header.
+ *
+ * A description of the input format for 'data' is at
+ * http://en.wikipedia.org/wiki/Digest_access_authentication
+ *
+ *
+ * @param dest where to store the result (possibly truncated if
+ * the buffer is not big enough).
+ * @param size size of dest
+ * @param data pointer to the Authorization header
+ * @param key key to look up in data
+ * @return size of the located value, 0 if otherwise
+ */
+static int
+lookup_sub_value (char *dest,
+ size_t size,
+ const char *data,
+ const char *key)
+{
+ size_t keylen;
+ size_t len;
+ const char *ptr;
+ const char *eq;
+ const char *q1;
+ const char *q2;
+ const char *qn;
+
+ if (0 == size)
+ return 0;
+ keylen = strlen (key);
+ ptr = data;
+ while ('\0' != *ptr)
+ {
+ if (NULL == (eq = strchr (ptr, '=')))
+ return 0;
+ q1 = eq + 1;
+ while (' ' == *q1)
+ q1++;
+ if ('\"' != *q1)
+ {
+ q2 = strchr (q1, ',');
+ qn = q2;
+ }
+ else
+ {
+ q1++;
+ q2 = strchr (q1, '\"');
+ if (NULL == q2)
+ return 0; /* end quote not found */
+ qn = q2 + 1;
+ }
+ if ( (0 == strncasecmp (ptr,
+ key,
+ keylen)) &&
+ (eq == &ptr[keylen]) )
+ {
+ if (NULL == q2)
+ {
+ len = strlen (q1) + 1;
+ if (size > len)
+ size = len;
+ size--;
+ strncpy (dest,
+ q1,
+ size);
+ dest[size] = '\0';
+ return size;
+ }
+ else
+ {
+ if (size > (q2 - q1) + 1)
+ size = (q2 - q1) + 1;
+ size--;
+ memcpy (dest,
+ q1,
+ size);
+ dest[size] = '\0';
+ return size;
+ }
+ }
+ if (NULL == qn)
+ return 0;
+ ptr = strchr (qn, ',');
+ if (NULL == ptr)
+ return 0;
+ ptr++;
+ while (' ' == *ptr)
+ ptr++;
+ }
+ return 0;
+}
+
+
+/**
+ * Check nonce-nc map array with either new nonce counter
+ * or a whole new nonce.
+ *
+ * @param connection The MHD connection structure
+ * @param nonce A pointer that referenced a zero-terminated array of nonce
+ * @param nc The nonce counter, zero to add the nonce to the array
+ * @return MHD_YES if successful, MHD_NO if invalid (or we have no NC array)
+ */
+static int
+check_nonce_nc (struct MHD_Connection *connection,
+ const char *nonce,
+ unsigned long int nc)
+{
+ uint32_t off;
+ uint32_t mod;
+ const char *np;
+
+ mod = connection->daemon->nonce_nc_size;
+ if (0 == mod)
+ return MHD_NO; /* no array! */
+ /* super-fast xor-based "hash" function for HT lookup in nonce array */
+ off = 0;
+ np = nonce;
+ while ('\0' != *np)
+ {
+ off = (off << 8) | (*np ^ (off >> 24));
+ np++;
+ }
+ off = off % mod;
+ /*
+ * Look for the nonce, if it does exist and its corresponding
+ * nonce counter is less than the current nonce counter by 1,
+ * then only increase the nonce counter by one.
+ */
+
+ pthread_mutex_lock (&connection->daemon->nnc_lock);
+ if (0 == nc)
+ {
+ strcpy(connection->daemon->nnc[off].nonce,
+ nonce);
+ connection->daemon->nnc[off].nc = 0;
+ pthread_mutex_unlock (&connection->daemon->nnc_lock);
+ return MHD_YES;
+ }
+ if ( (nc <= connection->daemon->nnc[off].nc) ||
+ (0 != strcmp(connection->daemon->nnc[off].nonce, nonce)) )
+ {
+ pthread_mutex_unlock (&connection->daemon->nnc_lock);
+#if HAVE_MESSAGES
+ MHD_DLOG (connection->daemon,
+ "Stale nonce received. If this happens a lot, you should
probably increase the size of the nonce array.\n");
+#endif
+ return MHD_NO;
+ }
+ connection->daemon->nnc[off].nc = nc;
+ pthread_mutex_unlock (&connection->daemon->nnc_lock);
+ return MHD_YES;
+}
+
+
+/**
+ * Get the username from the authorization header sent by the client
+ *
+ * @param connection The MHD connection structure
+ * @return NULL if no username could be found, a pointer
+ * to the username if found
+ */
+char *
+MHD_digest_auth_get_username(struct MHD_Connection *connection)
+{
+ size_t len;
+ char user[MAX_USERNAME_LENGTH];
+ const char *header;
+
+ if (NULL == (header = MHD_lookup_connection_value (connection,
+ MHD_HEADER_KIND,
+
MHD_HTTP_HEADER_AUTHORIZATION)))
+ return NULL;
+ if (0 != strncmp (header, _BASE, strlen (_BASE)))
+ return NULL;
+ header += strlen (_BASE);
+ if (0 == (len = lookup_sub_value (user,
+ sizeof (user),
+ header,
+ "username")))
+ return NULL;
+ return strdup (user);
+}
+
+
+/**
+ * Calculate the server nonce so that it mitigates replay attacks
+ * The current format of the nonce is ...
+ * H(timestamp ":" method ":" random ":" uri ":" realm) + Hex(timestamp)
+ *
+ * @param nonce_time The amount of time in seconds for a nonce to be invalid
+ * @param method HTTP method
+ * @param rnd A pointer to a character array for the random seed
+ * @param rnd_size The size of the random seed array
+ * @param uri HTTP URI (in MHD, without the arguments ("?k=v")
+ * @param realm A string of characters that describes the realm of auth.
+ * @param nonce A pointer to a character array for the nonce to put in
+ */
+static void
+calculate_nonce (uint32_t nonce_time,
+ const char *method,
+ const char *rnd,
+ unsigned int rnd_size,
+ const char *uri,
+ const char *realm,
+ char *nonce)
+{
+ struct MD5Context md5;
+ unsigned char timestamp[4];
+ unsigned char tmpnonce[MD5_DIGEST_SIZE];
+ char timestamphex[sizeof(timestamp) * 2 + 1];
+
+ MD5Init (&md5);
+ timestamp[0] = (nonce_time & 0xff000000) >> 0x18;
+ timestamp[1] = (nonce_time & 0x00ff0000) >> 0x10;
+ timestamp[2] = (nonce_time & 0x0000ff00) >> 0x08;
+ timestamp[3] = (nonce_time & 0x000000ff);
+ MD5Update (&md5, timestamp, 4);
+ MD5Update (&md5, ":", 1);
+ MD5Update (&md5, method, strlen(method));
+ MD5Update (&md5, ":", 1);
+ if (rnd_size > 0)
+ MD5Update (&md5, rnd, rnd_size);
+ MD5Update (&md5, ":", 1);
+ MD5Update (&md5, uri, strlen(uri));
+ MD5Update (&md5, ":", 1);
+ MD5Update (&md5, realm, strlen(realm));
+ MD5Final (tmpnonce, &md5);
+ cvthex (tmpnonce, sizeof (tmpnonce), nonce);
+ cvthex (timestamp, 4, timestamphex);
+ strncat (nonce, timestamphex, 8);
+}
+
+
+/**
+ * Test if the given key-value pair is in the headers for the
+ * given connection.
+ *
+ * @param connection the connection
+ * @param key the key
+ * @param value the value, can be NULL
+ * @return MHD_YES if the key-value pair is in the headers,
+ * MHD_NO if not
+ */
+static int
+test_header (struct MHD_Connection *connection,
+ const char *key,
+ const char *value)
+{
+ struct MHD_HTTP_Header *pos;
+
+ for (pos = connection->headers_received; NULL != pos; pos = pos->next)
+ {
+ if (MHD_GET_ARGUMENT_KIND != pos->kind)
+ continue;
+ if (0 != strcmp (key, pos->header))
+ continue;
+ if ( (NULL == value) &&
+ (NULL == pos->value) )
+ return MHD_YES;
+ if ( (NULL == value) ||
+ (NULL == pos->value) ||
+ (0 != strcmp (value, pos->value)) )
+ continue;
+ return MHD_YES;
+ }
+ return MHD_NO;
+}
+
+
+/**
+ * Check that the arguments given by the client as part
+ * of the authentication header match the arguments we
+ * got as part of the HTTP request URI.
+ *
+ * @param connection connections with headers to compare against
+ * @param args argument URI string (after "?" in URI)
+ * @return MHD_YES if the arguments match,
+ * MHD_NO if not
+ */
+static int
+check_argument_match (struct MHD_Connection *connection,
+ const char *args)
+{
+ struct MHD_HTTP_Header *pos;
+ size_t slen = strlen (args) + 1;
+ char argb[slen];
+ char *argp;
+ char *equals;
+ char *amper;
+ unsigned int num_headers;
+
+ num_headers = 0;
+ memcpy (argb, args, slen);
+ argp = argb;
+ while ( (NULL != argp) &&
+ ('\0' != argp[0]) )
+ {
+ equals = strchr (argp, '=');
+ if (NULL == equals)
+ {
+ /* add with 'value' NULL */
+ connection->daemon->unescape_callback
(connection->daemon->unescape_callback_cls,
+ connection,
+ argp);
+ if (MHD_YES != test_header (connection, argp, NULL))
+ return MHD_NO;
+ num_headers++;
+ break;
+ }
+ equals[0] = '\0';
+ equals++;
+ amper = strchr (equals, '&');
+ if (NULL != amper)
+ {
+ amper[0] = '\0';
+ amper++;
+ }
+ connection->daemon->unescape_callback
(connection->daemon->unescape_callback_cls,
+ connection,
+ argp);
+ connection->daemon->unescape_callback
(connection->daemon->unescape_callback_cls,
+ connection,
+ equals);
+ if (! test_header (connection, argp, equals))
+ return MHD_NO;
+ num_headers++;
+ argp = amper;
+ }
+
+ /* also check that the number of headers matches */
+ for (pos = connection->headers_received; NULL != pos; pos = pos->next)
+ {
+ if (MHD_GET_ARGUMENT_KIND != pos->kind)
+ continue;
+ num_headers--;
+ }
+ if (0 != num_headers)
+ return MHD_NO;
+ return MHD_YES;
+}
+
+
+/**
+ * Authenticates the authorization header sent by the client
+ *
+ * @param connection The MHD connection structure
+ * @param realm The realm presented to the client
+ * @param username The username needs to be authenticated
+ * @param password The password used in the authentication
+ * @param nonce_timeout The amount of time for a nonce to be
+ * invalid in seconds
+ * @return MHD_YES if authenticated, MHD_NO if not,
+ * MHD_INVALID_NONCE if nonce is invalid
+ */
+int
+MHD_digest_auth_check (struct MHD_Connection *connection,
+ const char *realm,
+ const char *username,
+ const char *password,
+ unsigned int nonce_timeout)
+{
+ size_t len;
+ const char *header;
+ char *end;
+ char nonce[MAX_NONCE_LENGTH];
+ char cnonce[MAX_NONCE_LENGTH];
+ char qop[15]; /* auth,auth-int */
+ char nc[20];
+ char response[MAX_AUTH_RESPONSE_LENGTH];
+ const char *hentity = NULL; /* "auth-int" is not supported */
+ char ha1[HASH_MD5_HEX_LEN + 1];
+ char respexp[HASH_MD5_HEX_LEN + 1];
+ char noncehashexp[HASH_MD5_HEX_LEN + 9];
+ uint32_t nonce_time;
+ uint32_t t;
+ size_t left; /* number of characters left in 'header' for 'uri' */
+ unsigned long int nci;
+
+ header = MHD_lookup_connection_value (connection,
+ MHD_HEADER_KIND,
+ MHD_HTTP_HEADER_AUTHORIZATION);
+ if (NULL == header)
+ return MHD_NO;
+ if (0 != strncmp(header, _BASE, strlen(_BASE)))
+ return MHD_NO;
+ header += strlen (_BASE);
+ left = strlen (header);
+
+ {
+ char un[MAX_USERNAME_LENGTH];
+
+ len = lookup_sub_value (un,
+ sizeof (un),
+ header, "username");
+ if ( (0 == len) ||
+ (0 != strcmp(username, un)) )
+ return MHD_NO;
+ left -= strlen ("username") + len;
+ }
+
+ {
+ char r[MAX_REALM_LENGTH];
+
+ len = lookup_sub_value(r,
+ sizeof (r),
+ header, "realm");
+ if ( (0 == len) ||
+ (0 != strcmp(realm, r)) )
+ return MHD_NO;
+ left -= strlen ("realm") + len;
+ }
+
+ if (0 == (len = lookup_sub_value (nonce,
+ sizeof (nonce),
+ header, "nonce")))
+ return MHD_NO;
+ left -= strlen ("nonce") + len;
+
+ {
+ char uri[left];
+
+ if (0 == lookup_sub_value(uri,
+ sizeof (uri),
+ header, "uri"))
+ return MHD_NO;
+
+ /* 8 = 4 hexadecimal numbers for the timestamp */
+ nonce_time = strtoul(nonce + len - 8, (char **)NULL, 16);
+ t = (uint32_t) MHD_monotonic_time();
+ /*
+ * First level vetting for the nonce validity if the timestamp
+ * attached to the nonce exceeds `nonce_timeout' then the nonce is
+ * invalid.
+ */
+ if ( (t > nonce_time + nonce_timeout) ||
+ (nonce_time + nonce_timeout < nonce_time) )
+ return MHD_INVALID_NONCE;
+ if (0 != strncmp (uri,
+ connection->url,
+ strlen (connection->url)))
+ {
+#if HAVE_MESSAGES
+ MHD_DLOG (connection->daemon,
+ "Authentication failed, URI does not match.\n");
+#endif
+ return MHD_NO;
+ }
+ {
+ const char *args = strchr (uri, '?');
+
+ if (NULL == args)
+ args = "";
+ else
+ args++;
+ if (MHD_YES !=
+ check_argument_match (connection,
+ args) )
+ {
+#if HAVE_MESSAGES
+ MHD_DLOG (connection->daemon,
+ "Authentication failed, arguments do not match.\n");
+#endif
+ return MHD_NO;
+ }
+ }
+ calculate_nonce (nonce_time,
+ connection->method,
+ connection->daemon->digest_auth_random,
+ connection->daemon->digest_auth_rand_size,
+ connection->url,
+ realm,
+ noncehashexp);
+ /*
+ * Second level vetting for the nonce validity
+ * if the timestamp attached to the nonce is valid
+ * and possibly fabricated (in case of an attack)
+ * the attacker must also know the random seed to be
+ * able to generate a "sane" nonce, which if he does
+ * not, the nonce fabrication process going to be
+ * very hard to achieve.
+ */
+
+ if (0 != strcmp (nonce, noncehashexp))
+ return MHD_INVALID_NONCE;
+ if ( (0 == lookup_sub_value (cnonce,
+ sizeof (cnonce),
+ header, "cnonce")) ||
+ (0 == lookup_sub_value (qop, sizeof (qop), header, "qop")) ||
+ ( (0 != strcmp (qop, "auth")) &&
+ (0 != strcmp (qop, "")) ) ||
+ (0 == lookup_sub_value (nc, sizeof (nc), header, "nc")) ||
+ (0 == lookup_sub_value (response, sizeof (response), header,
"response")) )
+ {
+#if HAVE_MESSAGES
+ MHD_DLOG (connection->daemon,
+ "Authentication failed, invalid format.\n");
+#endif
+ return MHD_NO;
+ }
+ nci = strtoul (nc, &end, 16);
+ if ( ('\0' != *end) ||
+ ( (LONG_MAX == nci) &&
+ (ERANGE == errno) ) )
+ {
+#if HAVE_MESSAGES
+ MHD_DLOG (connection->daemon,
+ "Authentication failed, invalid format.\n");
+#endif
+ return MHD_NO; /* invalid nonce format */
+ }
+ /*
+ * Checking if that combination of nonce and nc is sound
+ * and not a replay attack attempt. Also adds the nonce
+ * to the nonce-nc map if it does not exist there.
+ */
+
+ if (MHD_YES != check_nonce_nc (connection, nonce, nci))
+ return MHD_NO;
+
+ digest_calc_ha1("md5",
+ username,
+ realm,
+ password,
+ nonce,
+ cnonce,
+ ha1);
+ digest_calc_response (ha1,
+ nonce,
+ nc,
+ cnonce,
+ qop,
+ connection->method,
+ uri,
+ hentity,
+ respexp);
+ return (0 == strcmp(response, respexp))
+ ? MHD_YES
+ : MHD_NO;
+ }
+}
+
+
+/**
+ * Queues a response to request authentication from the client
+ *
+ * @param connection The MHD connection structure
+ * @param realm the realm presented to the client
+ * @param opaque string to user for opaque value
+ * @param response reply to send; should contain the "access denied"
+ * body; note that this function will set the "WWW Authenticate"
+ * header and that the caller should not do this
+ * @param signal_stale MHD_YES if the nonce is invalid to add
+ * 'stale=true' to the authentication header
+ * @return MHD_YES on success, MHD_NO otherwise
+ */
+int
+MHD_queue_auth_fail_response (struct MHD_Connection *connection,
+ const char *realm,
+ const char *opaque,
+ struct MHD_Response *response,
+ int signal_stale)
+{
+ int ret;
+ size_t hlen;
+ char nonce[HASH_MD5_HEX_LEN + 9];
+
+ /* Generating the server nonce */
+ calculate_nonce ((uint32_t) MHD_monotonic_time(),
+ connection->method,
+ connection->daemon->digest_auth_random,
+ connection->daemon->digest_auth_rand_size,
+ connection->url,
+ realm,
+ nonce);
+ if (MHD_YES != check_nonce_nc (connection, nonce, 0))
+ {
+#if HAVE_MESSAGES
+ MHD_DLOG (connection->daemon,
+ "Could not register nonce (is the nonce array size zero?).\n");
+#endif
+ return MHD_NO;
+ }
+ /* Building the authentication header */
+ hlen = snprintf (NULL,
+ 0,
+ "Digest
realm=\"%s\",qop=\"auth\",nonce=\"%s\",opaque=\"%s\"%s",
+ realm,
+ nonce,
+ opaque,
+ signal_stale
+ ? ",stale=\"true\""
+ : "");
+ {
+ char header[hlen + 1];
+
+ snprintf (header,
+ sizeof(header),
+ "Digest realm=\"%s\",qop=\"auth\",nonce=\"%s\",opaque=\"%s\"%s",
+ realm,
+ nonce,
+ opaque,
+ signal_stale
+ ? ",stale=\"true\""
+ : "");
+ ret = MHD_add_response_header(response,
+ MHD_HTTP_HEADER_WWW_AUTHENTICATE,
+ header);
+ }
+ if (MHD_YES == ret)
+ ret = MHD_queue_response(connection,
+ MHD_HTTP_UNAUTHORIZED,
+ response);
+ return ret;
+}
+
+
+/* end of digestauth.c */
Deleted: libmicrohttpd/src/microhttpd/internal.c
===================================================================
--- libmicrohttpd/src/daemon/internal.c 2013-05-05 12:01:06 UTC (rev 27023)
+++ libmicrohttpd/src/microhttpd/internal.c 2013-05-05 18:07:33 UTC (rev
27025)
@@ -1,170 +0,0 @@
-/*
- 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
- version 2.1 of the License, or (at your option) any later version.
-
- This library is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- Lesser General Public License for more details.
-
- You should have received a copy of the GNU Lesser General Public
- License along with this library; if not, write to the Free Software
- Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
USA
-*/
-
-/**
- * @file internal.h
- * @brief internal shared structures
- * @author Daniel Pittman
- * @author Christian Grothoff
- */
-
-#include "internal.h"
-
-#if HAVE_MESSAGES
-#if DEBUG_STATES
-/**
- * State to string dictionary.
- */
-const char *
-MHD_state_to_string (enum MHD_CONNECTION_STATE state)
-{
- switch (state)
- {
- case MHD_CONNECTION_INIT:
- return "connection init";
- case MHD_CONNECTION_URL_RECEIVED:
- return "connection url received";
- case MHD_CONNECTION_HEADER_PART_RECEIVED:
- return "header partially received";
- case MHD_CONNECTION_HEADERS_RECEIVED:
- return "headers received";
- case MHD_CONNECTION_HEADERS_PROCESSED:
- return "headers processed";
- case MHD_CONNECTION_CONTINUE_SENDING:
- return "continue sending";
- case MHD_CONNECTION_CONTINUE_SENT:
- return "continue sent";
- case MHD_CONNECTION_BODY_RECEIVED:
- return "body received";
- case MHD_CONNECTION_FOOTER_PART_RECEIVED:
- return "footer partially received";
- case MHD_CONNECTION_FOOTERS_RECEIVED:
- return "footers received";
- case MHD_CONNECTION_HEADERS_SENDING:
- return "headers sending";
- case MHD_CONNECTION_HEADERS_SENT:
- return "headers sent";
- case MHD_CONNECTION_NORMAL_BODY_READY:
- return "normal body ready";
- case MHD_CONNECTION_NORMAL_BODY_UNREADY:
- return "normal body unready";
- case MHD_CONNECTION_CHUNKED_BODY_READY:
- return "chunked body ready";
- case MHD_CONNECTION_CHUNKED_BODY_UNREADY:
- return "chunked body unready";
- case MHD_CONNECTION_BODY_SENT:
- return "body sent";
- case MHD_CONNECTION_FOOTERS_SENDING:
- return "footers sending";
- case MHD_CONNECTION_FOOTERS_SENT:
- return "footers sent";
- case MHD_CONNECTION_CLOSED:
- return "closed";
- case MHD_TLS_CONNECTION_INIT:
- return "secure connection init";
- default:
- return "unrecognized connection state";
- }
-}
-#endif
-#endif
-
-#if HAVE_MESSAGES
-/**
- * fprintf-like helper function for logging debug
- * messages.
- */
-void
-MHD_DLOG (const struct MHD_Daemon *daemon, const char *format, ...)
-{
- va_list va;
-
- if ((daemon->options & MHD_USE_DEBUG) == 0)
- return;
- va_start (va, format);
- daemon->custom_error_log (daemon->custom_error_log_cls, format, va);
- va_end (va);
-}
-#endif
-
-
-/**
- * Process escape sequences ('+'=space, %HH) Updates val in place; the
- * result should be UTF-8 encoded and cannot be larger than the input.
- * The result must also still be 0-terminated.
- *
- * @param cls closure (use NULL)
- * @param connection handle to connection, not used
- * @return length of the resulting val (strlen(val) maybe
- * shorter afterwards due to elimination of escape sequences)
- */
-size_t
-MHD_http_unescape (void *cls,
- struct MHD_Connection *connection,
- char *val)
-{
- char *rpos = val;
- char *wpos = val;
- char *end;
- unsigned int num;
- char buf3[3];
-
- while ('\0' != *rpos)
- {
- switch (*rpos)
- {
- case '+':
- *wpos = ' ';
- wpos++;
- rpos++;
- break;
- case '%':
- buf3[0] = rpos[1];
- buf3[1] = rpos[2];
- buf3[2] = '\0';
- num = strtoul (buf3, &end, 16);
- if ('\0' == *end)
- {
- *wpos = (unsigned char) num;
- wpos++;
- rpos += 3;
- break;
- }
- /* intentional fall through! */
- default:
- *wpos = *rpos;
- wpos++;
- rpos++;
- }
- }
- *wpos = '\0'; /* add 0-terminator */
- return wpos - val; /* = strlen(val) */
-}
-
-time_t MHD_monotonic_time(void)
-{
-#ifdef HAVE_CLOCK_GETTIME
- struct timespec ts;
- if (clock_gettime(CLOCK_MONOTONIC, &ts) == 0)
- return ts.tv_sec;
-#endif
- return time(NULL);
-}
-
-/* end of internal.c */
Copied: libmicrohttpd/src/microhttpd/internal.c (from rev 27024,
libmicrohttpd/src/daemon/internal.c)
===================================================================
--- libmicrohttpd/src/microhttpd/internal.c (rev 0)
+++ libmicrohttpd/src/microhttpd/internal.c 2013-05-05 18:07:33 UTC (rev
27025)
@@ -0,0 +1,171 @@
+/*
+ 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
+ version 2.1 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
USA
+*/
+
+/**
+ * @file internal.h
+ * @brief internal shared structures
+ * @author Daniel Pittman
+ * @author Christian Grothoff
+ */
+
+#include "internal.h"
+
+#if HAVE_MESSAGES
+#if DEBUG_STATES
+/**
+ * State to string dictionary.
+ */
+const char *
+MHD_state_to_string (enum MHD_CONNECTION_STATE state)
+{
+ switch (state)
+ {
+ case MHD_CONNECTION_INIT:
+ return "connection init";
+ case MHD_CONNECTION_URL_RECEIVED:
+ return "connection url received";
+ case MHD_CONNECTION_HEADER_PART_RECEIVED:
+ return "header partially received";
+ case MHD_CONNECTION_HEADERS_RECEIVED:
+ return "headers received";
+ case MHD_CONNECTION_HEADERS_PROCESSED:
+ return "headers processed";
+ case MHD_CONNECTION_CONTINUE_SENDING:
+ return "continue sending";
+ case MHD_CONNECTION_CONTINUE_SENT:
+ return "continue sent";
+ case MHD_CONNECTION_BODY_RECEIVED:
+ return "body received";
+ case MHD_CONNECTION_FOOTER_PART_RECEIVED:
+ return "footer partially received";
+ case MHD_CONNECTION_FOOTERS_RECEIVED:
+ return "footers received";
+ case MHD_CONNECTION_HEADERS_SENDING:
+ return "headers sending";
+ case MHD_CONNECTION_HEADERS_SENT:
+ return "headers sent";
+ case MHD_CONNECTION_NORMAL_BODY_READY:
+ return "normal body ready";
+ case MHD_CONNECTION_NORMAL_BODY_UNREADY:
+ return "normal body unready";
+ case MHD_CONNECTION_CHUNKED_BODY_READY:
+ return "chunked body ready";
+ case MHD_CONNECTION_CHUNKED_BODY_UNREADY:
+ return "chunked body unready";
+ case MHD_CONNECTION_BODY_SENT:
+ return "body sent";
+ case MHD_CONNECTION_FOOTERS_SENDING:
+ return "footers sending";
+ case MHD_CONNECTION_FOOTERS_SENT:
+ return "footers sent";
+ case MHD_CONNECTION_CLOSED:
+ return "closed";
+ case MHD_TLS_CONNECTION_INIT:
+ return "secure connection init";
+ default:
+ return "unrecognized connection state";
+ }
+}
+#endif
+#endif
+
+#if HAVE_MESSAGES
+/**
+ * fprintf-like helper function for logging debug
+ * messages.
+ */
+void
+MHD_DLOG (const struct MHD_Daemon *daemon, const char *format, ...)
+{
+ va_list va;
+
+ if ((daemon->options & MHD_USE_DEBUG) == 0)
+ return;
+ va_start (va, format);
+ daemon->custom_error_log (daemon->custom_error_log_cls, format, va);
+ va_end (va);
+}
+#endif
+
+
+/**
+ * Process escape sequences ('+'=space, %HH) Updates val in place; the
+ * result should be UTF-8 encoded and cannot be larger than the input.
+ * The result must also still be 0-terminated.
+ *
+ * @param cls closure (use NULL)
+ * @param connection handle to connection, not used
+ * @param val value to unescape (modified in the process)
+ * @return length of the resulting val (strlen(val) maybe
+ * shorter afterwards due to elimination of escape sequences)
+ */
+size_t
+MHD_http_unescape (void *cls,
+ struct MHD_Connection *connection,
+ char *val)
+{
+ char *rpos = val;
+ char *wpos = val;
+ char *end;
+ unsigned int num;
+ char buf3[3];
+
+ while ('\0' != *rpos)
+ {
+ switch (*rpos)
+ {
+ case '+':
+ *wpos = ' ';
+ wpos++;
+ rpos++;
+ break;
+ case '%':
+ buf3[0] = rpos[1];
+ buf3[1] = rpos[2];
+ buf3[2] = '\0';
+ num = strtoul (buf3, &end, 16);
+ if ('\0' == *end)
+ {
+ *wpos = (unsigned char) num;
+ wpos++;
+ rpos += 3;
+ break;
+ }
+ /* intentional fall through! */
+ default:
+ *wpos = *rpos;
+ wpos++;
+ rpos++;
+ }
+ }
+ *wpos = '\0'; /* add 0-terminator */
+ return wpos - val; /* = strlen(val) */
+}
+
+time_t MHD_monotonic_time(void)
+{
+#ifdef HAVE_CLOCK_GETTIME
+ struct timespec ts;
+ if (clock_gettime(CLOCK_MONOTONIC, &ts) == 0)
+ return ts.tv_sec;
+#endif
+ return time(NULL);
+}
+
+/* end of internal.c */
Deleted: libmicrohttpd/src/microhttpd/internal.h
===================================================================
--- libmicrohttpd/src/daemon/internal.h 2013-05-05 12:01:06 UTC (rev 27023)
+++ libmicrohttpd/src/microhttpd/internal.h 2013-05-05 18:07:33 UTC (rev
27025)
@@ -1,1092 +0,0 @@
-/*
- This file is part of libmicrohttpd
- (C) 2007, 2008, 2009, 2010, 2011, 2012 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
- version 2.1 of the License, or (at your option) any later version.
-
- This library is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- Lesser General Public License for more details.
-
- You should have received a copy of the GNU Lesser General Public
- License along with this library; if not, write to the Free Software
- Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
USA
-*/
-
-/**
- * @file internal.h
- * @brief internal shared structures
- * @author Daniel Pittman
- * @author Christian Grothoff
- */
-
-#ifndef INTERNAL_H
-#define INTERNAL_H
-
-#include "platform.h"
-#include "microhttpd.h"
-#if HTTPS_SUPPORT
-#include <gnutls/gnutls.h>
-#endif
-
-/**
- * Should we perform additional sanity checks at runtime (on our internal
- * invariants)? This may lead to aborts, but can be useful for debugging.
- */
-#define EXTRA_CHECKS MHD_NO
-
-#define MHD_MAX(a,b) ((a)<(b)) ? (b) : (a)
-#define MHD_MIN(a,b) ((a)<(b)) ? (a) : (b)
-
-
-/**
- * Handler for fatal errors.
- */
-extern MHD_PanicCallback mhd_panic;
-
-/**
- * Closure argument for "mhd_panic".
- */
-extern void *mhd_panic_cls;
-
-#if HAVE_MESSAGES
-/**
- * Trigger 'panic' action based on fatal errors.
- *
- * @param error message (const char *)
- */
-#define MHD_PANIC(msg) mhd_panic (mhd_panic_cls, __FILE__, __LINE__, msg)
-#else
-/**
- * Trigger 'panic' action based on fatal errors.
- *
- * @param error message (const char *)
- */
-#define MHD_PANIC(msg) mhd_panic (mhd_panic_cls, __FILE__, __LINE__, NULL)
-#endif
-
-/**
- * Events we care about with respect to poll/select
- * for file descriptors.
- */
-enum MHD_PollActions
- {
- /**
- * No event interests us.
- */
- MHD_POLL_ACTION_NOTHING = 0,
-
- /**
- * We would like to read.
- */
- MHD_POLL_ACTION_IN = 1,
-
- /**
- * We would like to write.
- */
- MHD_POLL_ACTION_OUT = 2
- };
-
-
-/**
- * Socket descriptor and events we care about.
- */
-struct MHD_Pollfd
-{
- /**
- * Socket descriptor.
- */
- int fd;
-
- /**
- * Which events do we care about for this socket?
- */
- enum MHD_PollActions events;
-};
-
-
-/**
- * Maximum length of a nonce in digest authentication. 32(MD5 Hex) +
- * 8(Timestamp Hex) + 1(NULL); hence 41 should suffice, but Opera
- * (already) takes more (see Mantis #1633), so we've increased the
- * value to support something longer...
- */
-#define MAX_NONCE_LENGTH 129
-
-
-/**
- * A structure representing the internal holder of the
- * nonce-nc map.
- */
-struct MHD_NonceNc
-{
-
- /**
- * Nonce counter, a value that increases for each subsequent
- * request for the same nonce.
- */
- unsigned long int nc;
-
- /**
- * Nonce value:
- */
- char nonce[MAX_NONCE_LENGTH];
-
-};
-
-#if HAVE_MESSAGES
-/**
- * fprintf-like helper function for logging debug
- * messages.
- */
-void
-MHD_DLOG (const struct MHD_Daemon *daemon,
- const char *format, ...);
-
-#endif
-
-/**
- * Process escape sequences ('+'=space, %HH) Updates val in place; the
- * result should be UTF-8 encoded and cannot be larger than the input.
- * The result must also still be 0-terminated.
- *
- * @param cls closure (use NULL)
- * @param connection handle to connection, not used
- * @return length of the resulting val (strlen(val) maybe
- * shorter afterwards due to elimination of escape sequences)
- */
-size_t
-MHD_http_unescape (void *cls,
- struct MHD_Connection *connection,
- char *val);
-
-
-/**
- * Header or cookie in HTTP request or response.
- */
-struct MHD_HTTP_Header
-{
- /**
- * Headers are kept in a linked list.
- */
- struct MHD_HTTP_Header *next;
-
- /**
- * The name of the header (key), without
- * the colon.
- */
- char *header;
-
- /**
- * The value of the header.
- */
- char *value;
-
- /**
- * Type of the header (where in the HTTP
- * protocol is this header from).
- */
- enum MHD_ValueKind kind;
-
-};
-
-
-/**
- * Representation of a response.
- */
-struct MHD_Response
-{
-
- /**
- * Headers to send for the response. Initially
- * the linked list is created in inverse order;
- * the order should be inverted before sending!
- */
- struct MHD_HTTP_Header *first_header;
-
- /**
- * Buffer pointing to data that we are supposed
- * to send as a response.
- */
- char *data;
-
- /**
- * Closure to give to the content reader
- * free callback.
- */
- void *crc_cls;
-
- /**
- * How do we get more data? NULL if we are
- * given all of the data up front.
- */
- MHD_ContentReaderCallback crc;
-
- /**
- * NULL if data must not be freed, otherwise
- * either user-specified callback or "&free".
- */
- MHD_ContentReaderFreeCallback crfc;
-
- /**
- * Mutex to synchronize access to data/size and
- * reference counts.
- */
- pthread_mutex_t mutex;
-
- /**
- * Set to MHD_SIZE_UNKNOWN if size is not known.
- */
- uint64_t total_size;
-
- /**
- * At what offset in the stream is the
- * beginning of data located?
- */
- uint64_t data_start;
-
- /**
- * Offset to start reading from when using 'fd'.
- */
- off_t fd_off;
-
- /**
- * Size of data.
- */
- size_t data_size;
-
- /**
- * Size of the data buffer.
- */
- size_t data_buffer_size;
-
- /**
- * Reference count for this response. Free
- * once the counter hits zero.
- */
- unsigned int reference_count;
-
- /**
- * File-descriptor if this response is FD-backed.
- */
- int fd;
-
-};
-
-
-/**
- * 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 to be closed.
- */
- MHD_CONNECTION_CLOSED = MHD_CONNECTION_FOOTERS_SENT + 1,
-
- /**
- * 20: This connection is finished (only to be freed)
- */
- MHD_CONNECTION_IN_CLEANUP = MHD_CONNECTION_CLOSED + 1,
-
- /*
- * SSL/TLS connection states
- */
-
- /**
- * The initial connection state for all secure connectoins
- * Handshake messages will be processed in this state & while
- * in the 'MHD_TLS_HELLO_REQUEST' state
- */
- MHD_TLS_CONNECTION_INIT = MHD_CONNECTION_CLOSED + 1
-
-};
-
-/**
- * Should all state transitions be printed to stderr?
- */
-#define DEBUG_STATES MHD_NO
-
-
-#if HAVE_MESSAGES
-#if DEBUG_STATES
-const char *
-MHD_state_to_string (enum MHD_CONNECTION_STATE state);
-#endif
-#endif
-
-/**
- * Function to receive plaintext data.
- *
- * @param conn the connection struct
- * @param write_to where to write received data
- * @param max_bytes maximum number of bytes to receive
- * @return number of bytes written to write_to
- */
-typedef ssize_t (*ReceiveCallback) (struct MHD_Connection * conn,
- void *write_to, size_t max_bytes);
-
-
-/**
- * Function to transmit plaintext data.
- *
- * @param conn the connection struct
- * @param read_from where to read data to transmit
- * @param max_bytes maximum number of bytes to transmit
- * @return number of bytes transmitted
- */
-typedef ssize_t (*TransmitCallback) (struct MHD_Connection * conn,
- const void *write_to, size_t max_bytes);
-
-
-/**
- * State kept for each HTTP request.
- */
-struct MHD_Connection
-{
-
- /**
- * This is a doubly-linked list.
- */
- struct MHD_Connection *next;
-
- /**
- * This is a doubly-linked list.
- */
- struct MHD_Connection *prev;
-
- /**
- * Reference to the MHD_Daemon struct.
- */
- struct MHD_Daemon *daemon;
-
- /**
- * Linked list of parsed headers.
- */
- struct MHD_HTTP_Header *headers_received;
-
- /**
- * Tail of linked list of parsed headers.
- */
- struct MHD_HTTP_Header *headers_received_tail;
-
- /**
- * Response to transmit (initially NULL).
- */
- struct MHD_Response *response;
-
- /**
- * The memory pool is created whenever we first read
- * from the TCP stream and destroyed at the end of
- * each request (and re-created for the next request).
- * In the meantime, this pointer is NULL. The
- * pool is used for all connection-related data
- * except for the response (which maybe shared between
- * connections) and the IP address (which persists
- * across individual requests).
- */
- struct MemoryPool *pool;
-
- /**
- * We allow the main application to associate some
- * pointer with the connection. Here is where we
- * store it. (MHD does not know or care what it
- * is).
- */
- void *client_context;
-
- /**
- * Request method. Should be GET/POST/etc. Allocated
- * in pool.
- */
- char *method;
-
- /**
- * Requested URL (everything after "GET" only). Allocated
- * in pool.
- */
- char *url;
-
- /**
- * HTTP version string (i.e. http/1.1). Allocated
- * in pool.
- */
- char *version;
-
- /**
- * Buffer for reading requests. Allocated
- * in pool. Actually one byte larger than
- * read_buffer_size (if non-NULL) to allow for
- * 0-termination.
- */
- char *read_buffer;
-
- /**
- * Buffer for writing response (headers only). Allocated
- * in pool.
- */
- 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!).
- */
- struct sockaddr *addr;
-
- /**
- * Thread for this connection (if we are using
- * one thread per connection).
- */
- pthread_t pid;
-
- /**
- * Size of read_buffer (in bytes). This value indicates
- * how many bytes we're willing to read into the buffer;
- * the real buffer is one byte longer to allow for
- * adding zero-termination (when needed).
- */
- size_t read_buffer_size;
-
- /**
- * Position where we currently append data in
- * read_buffer (last valid position).
- */
- size_t read_buffer_offset;
-
- /**
- * Size of write_buffer (in bytes).
- */
- size_t write_buffer_size;
-
- /**
- * Offset where we are with sending from write_buffer.
- */
- size_t write_buffer_send_offset;
-
- /**
- * Last valid location in write_buffer (where do we
- * append and up to where is it safe to send?)
- */
- size_t write_buffer_append_offset;
-
- /**
- * How many more bytes of the body do we expect
- * to read? "-1" for unknown.
- */
- uint64_t remaining_upload_size;
-
- /**
- * Current write position in the actual response
- * (excluding headers, content only; should be 0
- * while sending headers).
- */
- uint64_t response_write_position;
-
- /**
- * Position in the 100 CONTINUE message that
- * we need to send when receiving http 1.1 requests.
- */
- size_t continue_message_write_offset;
-
- /**
- * Length of the foreign address.
- */
- socklen_t addr_len;
-
- /**
- * Last time this connection had any activity
- * (reading or writing).
- */
- time_t last_activity;
-
- /**
- * After how many seconds of inactivity should
- * this connection time out? Zero for no timeout.
- */
- unsigned int connection_timeout;
-
- /**
- * Did we ever call the "default_handler" on this connection?
- * (this flag will determine if we call the 'notify_completed'
- * handler when the connection closes down).
- */
- int client_aware;
-
- /**
- * Socket for this connection. Set to -1 if
- * this connection has died (daemon should clean
- * up in that case).
- */
- int socket_fd;
-
- /**
- * 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 (and stop
- * trying to read from this socket).
- */
- int read_closed;
-
- /**
- * Set to MHD_YES if the thread has been joined.
- */
- int thread_joined;
-
- /**
- * State in the FSM for this connection.
- */
- enum MHD_CONNECTION_STATE state;
-
- /**
- * HTTP response code. Only valid if response object
- * is already set.
- */
- unsigned int responseCode;
-
- /**
- * Set to MHD_YES if the response's content reader
- * callback failed to provide data the last time
- * we tried to read from it. In that case, the
- * write socket should be marked as unready until
- * the CRC call succeeds.
- */
- int response_unready;
-
- /**
- * 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
- * processing the footers; once the footers are also done, this will
- * be set to MHD_NO again (before the final call to the handler).
- */
- int have_chunked_upload;
-
- /**
- * If we are receiving with chunked encoding, where are we right
- * now? Set to 0 if we are waiting to receive the chunk size;
- * otherwise, this is the size of the current chunk. A value of
- * zero is also used when we're at the end of the chunks.
- */
- unsigned int current_chunk_size;
-
- /**
- * If we are receiving with chunked encoding, where are we currently
- * with respect to the current chunk (at what offset / position)?
- */
- unsigned int current_chunk_offset;
-
- /**
- * Handler used for processing read connection operations
- */
- int (*read_handler) (struct MHD_Connection * connection);
-
- /**
- * Handler used for processing write connection operations
- */
- int (*write_handler) (struct MHD_Connection * connection);
-
- /**
- * Handler used for processing idle connection operations
- */
- int (*idle_handler) (struct MHD_Connection * connection);
-
- /**
- * Function used for reading HTTP request stream.
- */
- ReceiveCallback recv_cls;
-
- /**
- * Function used for writing HTTP response stream.
- */
- TransmitCallback send_cls;
-
-#if HTTPS_SUPPORT
- /**
- * State required for HTTPS/SSL/TLS support.
- */
- gnutls_session_t tls_session;
-
- /**
- * Memory location to return for protocol session info.
- */
- int protocol;
-
- /**
- * Memory location to return for protocol session info.
- */
- int cipher;
-
- /**
- * Could it be that we are ready to read due to TLS buffers
- * even though the socket is not?
- */
- int tls_read_ready;
-
-#endif
-};
-
-/**
- * Signature of function called to log URI accesses.
- *
- * @param cls closure
- * @param uri uri being accessed
- * @return new closure
- */
-typedef void * (*LogCallback)(void * cls, const char * uri);
-
-/**
- * Signature of function called to unescape URIs. See also
- * MHD_http_unescape.
- *
- * @param cls closure
- * @param conn connection handle
- * @param uri 0-terminated string to unescape (should be updated)
- * @return length of the resulting string
- */
-typedef size_t (*UnescapeCallback)(void *cls,
- struct MHD_Connection *conn,
- char *uri);
-
-
-/**
- * State kept for each MHD daemon.
- */
-struct MHD_Daemon
-{
-
- /**
- * Callback function for all requests.
- */
- MHD_AccessHandlerCallback default_handler;
-
- /**
- * Closure argument to default_handler.
- */
- void *default_handler_cls;
-
- /**
- * Tail of doubly-linked list of our current, active connections.
- */
- struct MHD_Connection *connections_head;
-
- /**
- * Tail of doubly-linked list of our current, active connections.
- */
- struct MHD_Connection *connections_tail;
-
- /**
- * Tail of doubly-linked list of connections to clean up.
- */
- struct MHD_Connection *cleanup_head;
-
- /**
- * Tail of doubly-linked list of connections to clean up.
- */
- struct MHD_Connection *cleanup_tail;
-
- /**
- * Function to call to check if we should
- * accept or reject an incoming request.
- * May be NULL.
- */
- MHD_AcceptPolicyCallback apc;
-
- /**
- * Closure argument to apc.
- */
- void *apc_cls;
-
- /**
- * Function to call when we are done processing
- * a particular request. May be NULL.
- */
- MHD_RequestCompletedCallback notify_completed;
-
- /**
- * Closure argument to notify_completed.
- */
- void *notify_completed_cls;
-
- /**
- * Function to call with the full URI at the
- * beginning of request processing. May be NULL.
- * <p>
- * Returns the initial pointer to internal state
- * kept by the client for the request.
- */
- LogCallback uri_log_callback;
-
- /**
- * Closure argument to uri_log_callback.
- */
- void *uri_log_callback_cls;
-
- /**
- * Function to call when we unescape escape sequences.
- */
- UnescapeCallback unescape_callback;
-
- /**
- * Closure for unescape callback.
- */
- void *unescape_callback_cls;
-
-#if HAVE_MESSAGES
- /**
- * Function for logging error messages (if we
- * support error reporting).
- */
- void (*custom_error_log) (void *cls, const char *fmt, va_list va);
-
- /**
- * Closure argument to custom_error_log.
- */
- void *custom_error_log_cls;
-#endif
-
- /**
- * Pointer to master daemon (NULL if this is the master)
- */
- struct MHD_Daemon *master;
-
- /**
- * Worker daemons (one per thread)
- */
- struct MHD_Daemon *worker_pool;
-
- /**
- * Table storing number of connections per IP
- */
- void *per_ip_connection_count;
-
- /**
- * Size of the per-connection memory pools.
- */
- size_t pool_size;
-
- /**
- * Size of threads created by MHD.
- */
- size_t thread_stack_size;
-
- /**
- * Number of worker daemons
- */
- unsigned int worker_pool_size;
-
- /**
- * PID of the select thread (if we have internal select)
- */
- pthread_t pid;
-
- /**
- * Mutex for per-IP connection counts.
- */
- pthread_mutex_t per_ip_connection_mutex;
-
- /**
- * Mutex for (modifying) access to the "cleanup" connection DLL.
- */
- pthread_mutex_t cleanup_connection_mutex;
-
- /**
- * Listen socket.
- */
- int socket_fd;
-
- /**
- * Pipe we use to signal shutdown, unless
- * 'HAVE_LISTEN_SHUTDOWN' is defined AND we have a listen
- * socket (which we can then 'shutdown' to stop listening).
- */
- int wpipe[2];
-
- /**
- * Are we shutting down?
- */
- int shutdown;
-
- /**
- * Limit on the number of parallel connections.
- */
- unsigned int max_connections;
-
- /**
- * After how many seconds of inactivity should
- * connections time out? Zero for no timeout.
- */
- unsigned int connection_timeout;
-
- /**
- * Maximum number of connections per IP, or 0 for
- * unlimited.
- */
- unsigned int per_ip_connection_limit;
-
- /**
- * Daemon's options.
- */
- enum MHD_OPTION options;
-
- /**
- * Listen port.
- */
- uint16_t port;
-
-#if HTTPS_SUPPORT
- /**
- * Desired cipher algorithms.
- */
- gnutls_priority_t priority_cache;
-
- /**
- * What kind of credentials are we offering
- * for SSL/TLS?
- */
- gnutls_credentials_type_t cred_type;
-
- /**
- * Server x509 credentials
- */
- gnutls_certificate_credentials_t x509_cred;
-
- /**
- * Diffie-Hellman parameters
- */
- gnutls_dh_params_t dh_params;
-
- /**
- * Pointer to our SSL/TLS key (in ASCII) in memory.
- */
- const char *https_mem_key;
-
- /**
- * Pointer to our SSL/TLS certificate (in ASCII) in memory.
- */
- const char *https_mem_cert;
-
- /**
- * Pointer to our SSL/TLS certificate authority (in ASCII) in memory.
- */
- const char *https_mem_trust;
-
-#endif
-
-#ifdef DAUTH_SUPPORT
-
- /**
- * Character array of random values.
- */
- const char *digest_auth_random;
-
- /**
- * An array that contains the map nonce-nc.
- */
- struct MHD_NonceNc *nnc;
-
- /**
- * A rw-lock for synchronizing access to `nnc'.
- */
- pthread_mutex_t nnc_lock;
-
- /**
- * Size of `digest_auth_random.
- */
- unsigned int digest_auth_rand_size;
-
- /**
- * Size of the nonce-nc array.
- */
- unsigned int nonce_nc_size;
-
-#endif
-
-};
-
-
-#if EXTRA_CHECKS
-#define EXTRA_CHECK(a) if (!(a)) abort();
-#else
-#define EXTRA_CHECK(a)
-#endif
-
-
-/**
- * Insert an element at the head of a DLL. Assumes that head, tail and
- * element are structs with prev and next fields.
- *
- * @param head pointer to the head of the DLL
- * @param tail pointer to the tail of the DLL
- * @param element element to insert
- */
-#define DLL_insert(head,tail,element) do { \
- (element)->next = (head); \
- (element)->prev = NULL; \
- if ((tail) == NULL) \
- (tail) = element; \
- else \
- (head)->prev = element; \
- (head) = (element); } while (0)
-
-
-/**
- * Remove an element from a DLL. Assumes
- * that head, tail and element are structs
- * with prev and next fields.
- *
- * @param head pointer to the head of the DLL
- * @param tail pointer to the tail of the DLL
- * @param element element to remove
- */
-#define DLL_remove(head,tail,element) do { \
- if ((element)->prev == NULL) \
- (head) = (element)->next; \
- else \
- (element)->prev->next = (element)->next; \
- if ((element)->next == NULL) \
- (tail) = (element)->prev; \
- else \
- (element)->next->prev = (element)->prev; \
- (element)->next = NULL; \
- (element)->prev = NULL; } while (0)
-
-
-/**
- * Equivalent to time(NULL) but tries to use some sort of monotonic
- * clock that isn't affected by someone setting the system real time
- * clock.
- */
-time_t MHD_monotonic_time(void);
-
-#endif
Copied: libmicrohttpd/src/microhttpd/internal.h (from rev 27024,
libmicrohttpd/src/daemon/internal.h)
===================================================================
--- libmicrohttpd/src/microhttpd/internal.h (rev 0)
+++ libmicrohttpd/src/microhttpd/internal.h 2013-05-05 18:07:33 UTC (rev
27025)
@@ -0,0 +1,1093 @@
+/*
+ This file is part of libmicrohttpd
+ (C) 2007, 2008, 2009, 2010, 2011, 2012 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
+ version 2.1 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
USA
+*/
+
+/**
+ * @file internal.h
+ * @brief internal shared structures
+ * @author Daniel Pittman
+ * @author Christian Grothoff
+ */
+
+#ifndef INTERNAL_H
+#define INTERNAL_H
+
+#include "platform.h"
+#include "microhttpd.h"
+#if HTTPS_SUPPORT
+#include <gnutls/gnutls.h>
+#endif
+
+/**
+ * Should we perform additional sanity checks at runtime (on our internal
+ * invariants)? This may lead to aborts, but can be useful for debugging.
+ */
+#define EXTRA_CHECKS MHD_NO
+
+#define MHD_MAX(a,b) ((a)<(b)) ? (b) : (a)
+#define MHD_MIN(a,b) ((a)<(b)) ? (a) : (b)
+
+
+/**
+ * Handler for fatal errors.
+ */
+extern MHD_PanicCallback mhd_panic;
+
+/**
+ * Closure argument for "mhd_panic".
+ */
+extern void *mhd_panic_cls;
+
+#if HAVE_MESSAGES
+/**
+ * Trigger 'panic' action based on fatal errors.
+ *
+ * @param msg error message (const char *)
+ */
+#define MHD_PANIC(msg) mhd_panic (mhd_panic_cls, __FILE__, __LINE__, msg)
+#else
+/**
+ * Trigger 'panic' action based on fatal errors.
+ *
+ * @param msg error message (const char *)
+ */
+#define MHD_PANIC(msg) mhd_panic (mhd_panic_cls, __FILE__, __LINE__, NULL)
+#endif
+
+/**
+ * Events we care about with respect to poll/select
+ * for file descriptors.
+ */
+enum MHD_PollActions
+ {
+ /**
+ * No event interests us.
+ */
+ MHD_POLL_ACTION_NOTHING = 0,
+
+ /**
+ * We would like to read.
+ */
+ MHD_POLL_ACTION_IN = 1,
+
+ /**
+ * We would like to write.
+ */
+ MHD_POLL_ACTION_OUT = 2
+ };
+
+
+/**
+ * Socket descriptor and events we care about.
+ */
+struct MHD_Pollfd
+{
+ /**
+ * Socket descriptor.
+ */
+ int fd;
+
+ /**
+ * Which events do we care about for this socket?
+ */
+ enum MHD_PollActions events;
+};
+
+
+/**
+ * Maximum length of a nonce in digest authentication. 32(MD5 Hex) +
+ * 8(Timestamp Hex) + 1(NULL); hence 41 should suffice, but Opera
+ * (already) takes more (see Mantis #1633), so we've increased the
+ * value to support something longer...
+ */
+#define MAX_NONCE_LENGTH 129
+
+
+/**
+ * A structure representing the internal holder of the
+ * nonce-nc map.
+ */
+struct MHD_NonceNc
+{
+
+ /**
+ * Nonce counter, a value that increases for each subsequent
+ * request for the same nonce.
+ */
+ unsigned long int nc;
+
+ /**
+ * Nonce value:
+ */
+ char nonce[MAX_NONCE_LENGTH];
+
+};
+
+#if HAVE_MESSAGES
+/**
+ * fprintf-like helper function for logging debug
+ * messages.
+ */
+void
+MHD_DLOG (const struct MHD_Daemon *daemon,
+ const char *format, ...);
+
+#endif
+
+/**
+ * Process escape sequences ('+'=space, %HH) Updates val in place; the
+ * result should be UTF-8 encoded and cannot be larger than the input.
+ * The result must also still be 0-terminated.
+ *
+ * @param cls closure (use NULL)
+ * @param connection handle to connection, not used
+ * @param val value to unescape (modified in the process)
+ * @return length of the resulting val (strlen(val) maybe
+ * shorter afterwards due to elimination of escape sequences)
+ */
+size_t
+MHD_http_unescape (void *cls,
+ struct MHD_Connection *connection,
+ char *val);
+
+
+/**
+ * Header or cookie in HTTP request or response.
+ */
+struct MHD_HTTP_Header
+{
+ /**
+ * Headers are kept in a linked list.
+ */
+ struct MHD_HTTP_Header *next;
+
+ /**
+ * The name of the header (key), without
+ * the colon.
+ */
+ char *header;
+
+ /**
+ * The value of the header.
+ */
+ char *value;
+
+ /**
+ * Type of the header (where in the HTTP
+ * protocol is this header from).
+ */
+ enum MHD_ValueKind kind;
+
+};
+
+
+/**
+ * Representation of a response.
+ */
+struct MHD_Response
+{
+
+ /**
+ * Headers to send for the response. Initially
+ * the linked list is created in inverse order;
+ * the order should be inverted before sending!
+ */
+ struct MHD_HTTP_Header *first_header;
+
+ /**
+ * Buffer pointing to data that we are supposed
+ * to send as a response.
+ */
+ char *data;
+
+ /**
+ * Closure to give to the content reader
+ * free callback.
+ */
+ void *crc_cls;
+
+ /**
+ * How do we get more data? NULL if we are
+ * given all of the data up front.
+ */
+ MHD_ContentReaderCallback crc;
+
+ /**
+ * NULL if data must not be freed, otherwise
+ * either user-specified callback or "&free".
+ */
+ MHD_ContentReaderFreeCallback crfc;
+
+ /**
+ * Mutex to synchronize access to data/size and
+ * reference counts.
+ */
+ pthread_mutex_t mutex;
+
+ /**
+ * Set to MHD_SIZE_UNKNOWN if size is not known.
+ */
+ uint64_t total_size;
+
+ /**
+ * At what offset in the stream is the
+ * beginning of data located?
+ */
+ uint64_t data_start;
+
+ /**
+ * Offset to start reading from when using 'fd'.
+ */
+ off_t fd_off;
+
+ /**
+ * Size of data.
+ */
+ size_t data_size;
+
+ /**
+ * Size of the data buffer.
+ */
+ size_t data_buffer_size;
+
+ /**
+ * Reference count for this response. Free
+ * once the counter hits zero.
+ */
+ unsigned int reference_count;
+
+ /**
+ * File-descriptor if this response is FD-backed.
+ */
+ int fd;
+
+};
+
+
+/**
+ * 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 to be closed.
+ */
+ MHD_CONNECTION_CLOSED = MHD_CONNECTION_FOOTERS_SENT + 1,
+
+ /**
+ * 20: This connection is finished (only to be freed)
+ */
+ MHD_CONNECTION_IN_CLEANUP = MHD_CONNECTION_CLOSED + 1,
+
+ /*
+ * SSL/TLS connection states
+ */
+
+ /**
+ * The initial connection state for all secure connectoins
+ * Handshake messages will be processed in this state & while
+ * in the 'MHD_TLS_HELLO_REQUEST' state
+ */
+ MHD_TLS_CONNECTION_INIT = MHD_CONNECTION_CLOSED + 1
+
+};
+
+/**
+ * Should all state transitions be printed to stderr?
+ */
+#define DEBUG_STATES MHD_NO
+
+
+#if HAVE_MESSAGES
+#if DEBUG_STATES
+const char *
+MHD_state_to_string (enum MHD_CONNECTION_STATE state);
+#endif
+#endif
+
+/**
+ * Function to receive plaintext data.
+ *
+ * @param conn the connection struct
+ * @param write_to where to write received data
+ * @param max_bytes maximum number of bytes to receive
+ * @return number of bytes written to write_to
+ */
+typedef ssize_t (*ReceiveCallback) (struct MHD_Connection * conn,
+ void *write_to, size_t max_bytes);
+
+
+/**
+ * Function to transmit plaintext data.
+ *
+ * @param conn the connection struct
+ * @param read_from where to read data to transmit
+ * @param max_bytes maximum number of bytes to transmit
+ * @return number of bytes transmitted
+ */
+typedef ssize_t (*TransmitCallback) (struct MHD_Connection * conn,
+ const void *write_to, size_t max_bytes);
+
+
+/**
+ * State kept for each HTTP request.
+ */
+struct MHD_Connection
+{
+
+ /**
+ * This is a doubly-linked list.
+ */
+ struct MHD_Connection *next;
+
+ /**
+ * This is a doubly-linked list.
+ */
+ struct MHD_Connection *prev;
+
+ /**
+ * Reference to the MHD_Daemon struct.
+ */
+ struct MHD_Daemon *daemon;
+
+ /**
+ * Linked list of parsed headers.
+ */
+ struct MHD_HTTP_Header *headers_received;
+
+ /**
+ * Tail of linked list of parsed headers.
+ */
+ struct MHD_HTTP_Header *headers_received_tail;
+
+ /**
+ * Response to transmit (initially NULL).
+ */
+ struct MHD_Response *response;
+
+ /**
+ * The memory pool is created whenever we first read
+ * from the TCP stream and destroyed at the end of
+ * each request (and re-created for the next request).
+ * In the meantime, this pointer is NULL. The
+ * pool is used for all connection-related data
+ * except for the response (which maybe shared between
+ * connections) and the IP address (which persists
+ * across individual requests).
+ */
+ struct MemoryPool *pool;
+
+ /**
+ * We allow the main application to associate some
+ * pointer with the connection. Here is where we
+ * store it. (MHD does not know or care what it
+ * is).
+ */
+ void *client_context;
+
+ /**
+ * Request method. Should be GET/POST/etc. Allocated
+ * in pool.
+ */
+ char *method;
+
+ /**
+ * Requested URL (everything after "GET" only). Allocated
+ * in pool.
+ */
+ char *url;
+
+ /**
+ * HTTP version string (i.e. http/1.1). Allocated
+ * in pool.
+ */
+ char *version;
+
+ /**
+ * Buffer for reading requests. Allocated
+ * in pool. Actually one byte larger than
+ * read_buffer_size (if non-NULL) to allow for
+ * 0-termination.
+ */
+ char *read_buffer;
+
+ /**
+ * Buffer for writing response (headers only). Allocated
+ * in pool.
+ */
+ 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!).
+ */
+ struct sockaddr *addr;
+
+ /**
+ * Thread for this connection (if we are using
+ * one thread per connection).
+ */
+ pthread_t pid;
+
+ /**
+ * Size of read_buffer (in bytes). This value indicates
+ * how many bytes we're willing to read into the buffer;
+ * the real buffer is one byte longer to allow for
+ * adding zero-termination (when needed).
+ */
+ size_t read_buffer_size;
+
+ /**
+ * Position where we currently append data in
+ * read_buffer (last valid position).
+ */
+ size_t read_buffer_offset;
+
+ /**
+ * Size of write_buffer (in bytes).
+ */
+ size_t write_buffer_size;
+
+ /**
+ * Offset where we are with sending from write_buffer.
+ */
+ size_t write_buffer_send_offset;
+
+ /**
+ * Last valid location in write_buffer (where do we
+ * append and up to where is it safe to send?)
+ */
+ size_t write_buffer_append_offset;
+
+ /**
+ * How many more bytes of the body do we expect
+ * to read? "-1" for unknown.
+ */
+ uint64_t remaining_upload_size;
+
+ /**
+ * Current write position in the actual response
+ * (excluding headers, content only; should be 0
+ * while sending headers).
+ */
+ uint64_t response_write_position;
+
+ /**
+ * Position in the 100 CONTINUE message that
+ * we need to send when receiving http 1.1 requests.
+ */
+ size_t continue_message_write_offset;
+
+ /**
+ * Length of the foreign address.
+ */
+ socklen_t addr_len;
+
+ /**
+ * Last time this connection had any activity
+ * (reading or writing).
+ */
+ time_t last_activity;
+
+ /**
+ * After how many seconds of inactivity should
+ * this connection time out? Zero for no timeout.
+ */
+ unsigned int connection_timeout;
+
+ /**
+ * Did we ever call the "default_handler" on this connection?
+ * (this flag will determine if we call the 'notify_completed'
+ * handler when the connection closes down).
+ */
+ int client_aware;
+
+ /**
+ * Socket for this connection. Set to -1 if
+ * this connection has died (daemon should clean
+ * up in that case).
+ */
+ int socket_fd;
+
+ /**
+ * 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 (and stop
+ * trying to read from this socket).
+ */
+ int read_closed;
+
+ /**
+ * Set to MHD_YES if the thread has been joined.
+ */
+ int thread_joined;
+
+ /**
+ * State in the FSM for this connection.
+ */
+ enum MHD_CONNECTION_STATE state;
+
+ /**
+ * HTTP response code. Only valid if response object
+ * is already set.
+ */
+ unsigned int responseCode;
+
+ /**
+ * Set to MHD_YES if the response's content reader
+ * callback failed to provide data the last time
+ * we tried to read from it. In that case, the
+ * write socket should be marked as unready until
+ * the CRC call succeeds.
+ */
+ int response_unready;
+
+ /**
+ * 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
+ * processing the footers; once the footers are also done, this will
+ * be set to MHD_NO again (before the final call to the handler).
+ */
+ int have_chunked_upload;
+
+ /**
+ * If we are receiving with chunked encoding, where are we right
+ * now? Set to 0 if we are waiting to receive the chunk size;
+ * otherwise, this is the size of the current chunk. A value of
+ * zero is also used when we're at the end of the chunks.
+ */
+ unsigned int current_chunk_size;
+
+ /**
+ * If we are receiving with chunked encoding, where are we currently
+ * with respect to the current chunk (at what offset / position)?
+ */
+ unsigned int current_chunk_offset;
+
+ /**
+ * Handler used for processing read connection operations
+ */
+ int (*read_handler) (struct MHD_Connection * connection);
+
+ /**
+ * Handler used for processing write connection operations
+ */
+ int (*write_handler) (struct MHD_Connection * connection);
+
+ /**
+ * Handler used for processing idle connection operations
+ */
+ int (*idle_handler) (struct MHD_Connection * connection);
+
+ /**
+ * Function used for reading HTTP request stream.
+ */
+ ReceiveCallback recv_cls;
+
+ /**
+ * Function used for writing HTTP response stream.
+ */
+ TransmitCallback send_cls;
+
+#if HTTPS_SUPPORT
+ /**
+ * State required for HTTPS/SSL/TLS support.
+ */
+ gnutls_session_t tls_session;
+
+ /**
+ * Memory location to return for protocol session info.
+ */
+ int protocol;
+
+ /**
+ * Memory location to return for protocol session info.
+ */
+ int cipher;
+
+ /**
+ * Could it be that we are ready to read due to TLS buffers
+ * even though the socket is not?
+ */
+ int tls_read_ready;
+
+#endif
+};
+
+/**
+ * Signature of function called to log URI accesses.
+ *
+ * @param cls closure
+ * @param uri uri being accessed
+ * @return new closure
+ */
+typedef void * (*LogCallback)(void * cls, const char * uri);
+
+/**
+ * Signature of function called to unescape URIs. See also
+ * MHD_http_unescape.
+ *
+ * @param cls closure
+ * @param conn connection handle
+ * @param uri 0-terminated string to unescape (should be updated)
+ * @return length of the resulting string
+ */
+typedef size_t (*UnescapeCallback)(void *cls,
+ struct MHD_Connection *conn,
+ char *uri);
+
+
+/**
+ * State kept for each MHD daemon.
+ */
+struct MHD_Daemon
+{
+
+ /**
+ * Callback function for all requests.
+ */
+ MHD_AccessHandlerCallback default_handler;
+
+ /**
+ * Closure argument to default_handler.
+ */
+ void *default_handler_cls;
+
+ /**
+ * Tail of doubly-linked list of our current, active connections.
+ */
+ struct MHD_Connection *connections_head;
+
+ /**
+ * Tail of doubly-linked list of our current, active connections.
+ */
+ struct MHD_Connection *connections_tail;
+
+ /**
+ * Tail of doubly-linked list of connections to clean up.
+ */
+ struct MHD_Connection *cleanup_head;
+
+ /**
+ * Tail of doubly-linked list of connections to clean up.
+ */
+ struct MHD_Connection *cleanup_tail;
+
+ /**
+ * Function to call to check if we should
+ * accept or reject an incoming request.
+ * May be NULL.
+ */
+ MHD_AcceptPolicyCallback apc;
+
+ /**
+ * Closure argument to apc.
+ */
+ void *apc_cls;
+
+ /**
+ * Function to call when we are done processing
+ * a particular request. May be NULL.
+ */
+ MHD_RequestCompletedCallback notify_completed;
+
+ /**
+ * Closure argument to notify_completed.
+ */
+ void *notify_completed_cls;
+
+ /**
+ * Function to call with the full URI at the
+ * beginning of request processing. May be NULL.
+ * <p>
+ * Returns the initial pointer to internal state
+ * kept by the client for the request.
+ */
+ LogCallback uri_log_callback;
+
+ /**
+ * Closure argument to uri_log_callback.
+ */
+ void *uri_log_callback_cls;
+
+ /**
+ * Function to call when we unescape escape sequences.
+ */
+ UnescapeCallback unescape_callback;
+
+ /**
+ * Closure for unescape callback.
+ */
+ void *unescape_callback_cls;
+
+#if HAVE_MESSAGES
+ /**
+ * Function for logging error messages (if we
+ * support error reporting).
+ */
+ void (*custom_error_log) (void *cls, const char *fmt, va_list va);
+
+ /**
+ * Closure argument to custom_error_log.
+ */
+ void *custom_error_log_cls;
+#endif
+
+ /**
+ * Pointer to master daemon (NULL if this is the master)
+ */
+ struct MHD_Daemon *master;
+
+ /**
+ * Worker daemons (one per thread)
+ */
+ struct MHD_Daemon *worker_pool;
+
+ /**
+ * Table storing number of connections per IP
+ */
+ void *per_ip_connection_count;
+
+ /**
+ * Size of the per-connection memory pools.
+ */
+ size_t pool_size;
+
+ /**
+ * Size of threads created by MHD.
+ */
+ size_t thread_stack_size;
+
+ /**
+ * Number of worker daemons
+ */
+ unsigned int worker_pool_size;
+
+ /**
+ * PID of the select thread (if we have internal select)
+ */
+ pthread_t pid;
+
+ /**
+ * Mutex for per-IP connection counts.
+ */
+ pthread_mutex_t per_ip_connection_mutex;
+
+ /**
+ * Mutex for (modifying) access to the "cleanup" connection DLL.
+ */
+ pthread_mutex_t cleanup_connection_mutex;
+
+ /**
+ * Listen socket.
+ */
+ int socket_fd;
+
+ /**
+ * Pipe we use to signal shutdown, unless
+ * 'HAVE_LISTEN_SHUTDOWN' is defined AND we have a listen
+ * socket (which we can then 'shutdown' to stop listening).
+ */
+ int wpipe[2];
+
+ /**
+ * Are we shutting down?
+ */
+ int shutdown;
+
+ /**
+ * Limit on the number of parallel connections.
+ */
+ unsigned int max_connections;
+
+ /**
+ * After how many seconds of inactivity should
+ * connections time out? Zero for no timeout.
+ */
+ unsigned int connection_timeout;
+
+ /**
+ * Maximum number of connections per IP, or 0 for
+ * unlimited.
+ */
+ unsigned int per_ip_connection_limit;
+
+ /**
+ * Daemon's options.
+ */
+ enum MHD_OPTION options;
+
+ /**
+ * Listen port.
+ */
+ uint16_t port;
+
+#if HTTPS_SUPPORT
+ /**
+ * Desired cipher algorithms.
+ */
+ gnutls_priority_t priority_cache;
+
+ /**
+ * What kind of credentials are we offering
+ * for SSL/TLS?
+ */
+ gnutls_credentials_type_t cred_type;
+
+ /**
+ * Server x509 credentials
+ */
+ gnutls_certificate_credentials_t x509_cred;
+
+ /**
+ * Diffie-Hellman parameters
+ */
+ gnutls_dh_params_t dh_params;
+
+ /**
+ * Pointer to our SSL/TLS key (in ASCII) in memory.
+ */
+ const char *https_mem_key;
+
+ /**
+ * Pointer to our SSL/TLS certificate (in ASCII) in memory.
+ */
+ const char *https_mem_cert;
+
+ /**
+ * Pointer to our SSL/TLS certificate authority (in ASCII) in memory.
+ */
+ const char *https_mem_trust;
+
+#endif
+
+#ifdef DAUTH_SUPPORT
+
+ /**
+ * Character array of random values.
+ */
+ const char *digest_auth_random;
+
+ /**
+ * An array that contains the map nonce-nc.
+ */
+ struct MHD_NonceNc *nnc;
+
+ /**
+ * A rw-lock for synchronizing access to `nnc'.
+ */
+ pthread_mutex_t nnc_lock;
+
+ /**
+ * Size of `digest_auth_random.
+ */
+ unsigned int digest_auth_rand_size;
+
+ /**
+ * Size of the nonce-nc array.
+ */
+ unsigned int nonce_nc_size;
+
+#endif
+
+};
+
+
+#if EXTRA_CHECKS
+#define EXTRA_CHECK(a) if (!(a)) abort();
+#else
+#define EXTRA_CHECK(a)
+#endif
+
+
+/**
+ * Insert an element at the head of a DLL. Assumes that head, tail and
+ * element are structs with prev and next fields.
+ *
+ * @param head pointer to the head of the DLL
+ * @param tail pointer to the tail of the DLL
+ * @param element element to insert
+ */
+#define DLL_insert(head,tail,element) do { \
+ (element)->next = (head); \
+ (element)->prev = NULL; \
+ if ((tail) == NULL) \
+ (tail) = element; \
+ else \
+ (head)->prev = element; \
+ (head) = (element); } while (0)
+
+
+/**
+ * Remove an element from a DLL. Assumes
+ * that head, tail and element are structs
+ * with prev and next fields.
+ *
+ * @param head pointer to the head of the DLL
+ * @param tail pointer to the tail of the DLL
+ * @param element element to remove
+ */
+#define DLL_remove(head,tail,element) do { \
+ if ((element)->prev == NULL) \
+ (head) = (element)->next; \
+ else \
+ (element)->prev->next = (element)->next; \
+ if ((element)->next == NULL) \
+ (tail) = (element)->prev; \
+ else \
+ (element)->next->prev = (element)->prev; \
+ (element)->next = NULL; \
+ (element)->prev = NULL; } while (0)
+
+
+/**
+ * Equivalent to time(NULL) but tries to use some sort of monotonic
+ * clock that isn't affected by someone setting the system real time
+ * clock.
+ */
+time_t MHD_monotonic_time(void);
+
+#endif
Deleted: libmicrohttpd/src/microhttpd/memorypool.c
===================================================================
--- libmicrohttpd/src/daemon/memorypool.c 2013-05-05 12:01:06 UTC (rev
27023)
+++ libmicrohttpd/src/microhttpd/memorypool.c 2013-05-05 18:07:33 UTC (rev
27025)
@@ -1,250 +0,0 @@
-/*
- This file is part of libmicrohttpd
- (C) 2007, 2009, 2010 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
- version 2.1 of the License, or (at your option) any later version.
-
- This library is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- Lesser General Public License for more details.
-
- You should have received a copy of the GNU Lesser General Public
- License along with this library; if not, write to the Free Software
- Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
USA
-*/
-
-/**
- * @file memorypool.c
- * @brief memory pool
- * @author Christian Grothoff
- */
-#include "memorypool.h"
-
-/* define MAP_ANONYMOUS for Mac OS X */
-#if defined(MAP_ANON) && !defined(MAP_ANONYMOUS)
-#define MAP_ANONYMOUS MAP_ANON
-#endif
-#ifndef MAP_FAILED
-#define MAP_FAILED ((void*)-1)
-#endif
-
-/**
- * Align to 2x word size (as GNU libc does).
- */
-#define ALIGN_SIZE (2 * sizeof(void*))
-
-/**
- * Round up 'n' to a multiple of ALIGN_SIZE.
- */
-#define ROUND_TO_ALIGN(n) ((n+(ALIGN_SIZE-1)) & (~(ALIGN_SIZE-1)))
-
-
-/**
- * Handle for a memory pool. Pools are not reentrant and must not be
- * used by multiple threads.
- */
-struct MemoryPool
-{
-
- /**
- * Pointer to the pool's memory
- */
- char *memory;
-
- /**
- * Size of the pool.
- */
- size_t size;
-
- /**
- * Offset of the first unallocated byte.
- */
- size_t pos;
-
- /**
- * Offset of the last unallocated byte.
- */
- size_t end;
-
- /**
- * MHD_NO if pool was malloc'ed, MHD_YES if mmapped.
- */
- int is_mmap;
-};
-
-
-/**
- * Create a memory pool.
- *
- * @param max maximum size of the pool
- */
-struct MemoryPool *
-MHD_pool_create (size_t max)
-{
- struct MemoryPool *pool;
-
- pool = malloc (sizeof (struct MemoryPool));
- if (pool == NULL)
- return NULL;
-#ifdef MAP_ANONYMOUS
- pool->memory = MMAP (NULL, max, PROT_READ | PROT_WRITE,
- MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
-#else
- pool->memory = MAP_FAILED;
-#endif
- if ((pool->memory == MAP_FAILED) || (pool->memory == NULL))
- {
- pool->memory = malloc (max);
- if (pool->memory == NULL)
- {
- free (pool);
- return NULL;
- }
- pool->is_mmap = MHD_NO;
- }
- else
- {
- pool->is_mmap = MHD_YES;
- }
- pool->pos = 0;
- pool->end = max;
- pool->size = max;
- return pool;
-}
-
-
-/**
- * Destroy a memory pool.
- */
-void
-MHD_pool_destroy (struct MemoryPool *pool)
-{
- if (pool == NULL)
- return;
- if (pool->is_mmap == MHD_NO)
- free (pool->memory);
- else
- MUNMAP (pool->memory, pool->size);
- free (pool);
-}
-
-
-/**
- * Allocate size bytes from the pool.
- * @return NULL if the pool cannot support size more
- * bytes
- */
-void *
-MHD_pool_allocate (struct MemoryPool *pool,
- size_t size, int from_end)
-{
- void *ret;
-
- size = ROUND_TO_ALIGN (size);
- if ((pool->pos + size > pool->end) || (pool->pos + size < pool->pos))
- return NULL;
- if (from_end == MHD_YES)
- {
- ret = &pool->memory[pool->end - size];
- pool->end -= size;
- }
- else
- {
- ret = &pool->memory[pool->pos];
- pool->pos += size;
- }
- return ret;
-}
-
-
-/**
- * Reallocate a block of memory obtained from the pool.
- * This is particularly efficient when growing or
- * shrinking the block that was last (re)allocated.
- * If the given block is not the most recenlty
- * (re)allocated block, the memory of the previous
- * allocation may be leaked until the pool is
- * destroyed (and copying the data maybe required).
- *
- * @param old the existing block
- * @param old_size the size of the existing block
- * @param new_size the new size of the block
- * @return new address of the block, or
- * NULL if the pool cannot support new_size
- * bytes (old continues to be valid for old_size)
- */
-void *
-MHD_pool_reallocate (struct MemoryPool *pool,
- void *old,
- size_t old_size,
- size_t new_size)
-{
- void *ret;
-
- new_size = ROUND_TO_ALIGN (new_size);
- if ((pool->end < old_size) || (pool->end < new_size))
- return NULL; /* unsatisfiable or bogus request */
-
- if ((pool->pos >= old_size) && (&pool->memory[pool->pos - old_size] == old))
- {
- /* was the previous allocation - optimize! */
- if (pool->pos + new_size - old_size <= pool->end)
- {
- /* fits */
- pool->pos += new_size - old_size;
- if (new_size < old_size) /* shrinking - zero again! */
- memset (&pool->memory[pool->pos], 0, old_size - new_size);
- return old;
- }
- /* does not fit */
- return NULL;
- }
- if (new_size <= old_size)
- return old; /* cannot shrink, no need to move */
- if ((pool->pos + new_size >= pool->pos) &&
- (pool->pos + new_size <= pool->end))
- {
- /* fits */
- ret = &pool->memory[pool->pos];
- memcpy (ret, old, old_size);
- pool->pos += new_size;
- return ret;
- }
- /* does not fit */
- return NULL;
-}
-
-
-/**
- * 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,
- size_t size)
-{
- size = ROUND_TO_ALIGN (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 */
Copied: libmicrohttpd/src/microhttpd/memorypool.c (from rev 27024,
libmicrohttpd/src/daemon/memorypool.c)
===================================================================
--- libmicrohttpd/src/microhttpd/memorypool.c (rev 0)
+++ libmicrohttpd/src/microhttpd/memorypool.c 2013-05-05 18:07:33 UTC (rev
27025)
@@ -0,0 +1,261 @@
+/*
+ This file is part of libmicrohttpd
+ (C) 2007, 2009, 2010 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
+ version 2.1 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
USA
+*/
+
+/**
+ * @file memorypool.c
+ * @brief memory pool
+ * @author Christian Grothoff
+ */
+#include "memorypool.h"
+
+/* define MAP_ANONYMOUS for Mac OS X */
+#if defined(MAP_ANON) && !defined(MAP_ANONYMOUS)
+#define MAP_ANONYMOUS MAP_ANON
+#endif
+#ifndef MAP_FAILED
+#define MAP_FAILED ((void*)-1)
+#endif
+
+/**
+ * Align to 2x word size (as GNU libc does).
+ */
+#define ALIGN_SIZE (2 * sizeof(void*))
+
+/**
+ * Round up 'n' to a multiple of ALIGN_SIZE.
+ */
+#define ROUND_TO_ALIGN(n) ((n+(ALIGN_SIZE-1)) & (~(ALIGN_SIZE-1)))
+
+
+/**
+ * Handle for a memory pool. Pools are not reentrant and must not be
+ * used by multiple threads.
+ */
+struct MemoryPool
+{
+
+ /**
+ * Pointer to the pool's memory
+ */
+ char *memory;
+
+ /**
+ * Size of the pool.
+ */
+ size_t size;
+
+ /**
+ * Offset of the first unallocated byte.
+ */
+ size_t pos;
+
+ /**
+ * Offset of the last unallocated byte.
+ */
+ size_t end;
+
+ /**
+ * MHD_NO if pool was malloc'ed, MHD_YES if mmapped.
+ */
+ int is_mmap;
+};
+
+
+/**
+ * Create a memory pool.
+ *
+ * @param max maximum size of the pool
+ * @return NULL on error
+ */
+struct MemoryPool *
+MHD_pool_create (size_t max)
+{
+ struct MemoryPool *pool;
+
+ pool = malloc (sizeof (struct MemoryPool));
+ if (pool == NULL)
+ return NULL;
+#ifdef MAP_ANONYMOUS
+ pool->memory = MMAP (NULL, max, PROT_READ | PROT_WRITE,
+ MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
+#else
+ pool->memory = MAP_FAILED;
+#endif
+ if ((pool->memory == MAP_FAILED) || (pool->memory == NULL))
+ {
+ pool->memory = malloc (max);
+ if (pool->memory == NULL)
+ {
+ free (pool);
+ return NULL;
+ }
+ pool->is_mmap = MHD_NO;
+ }
+ else
+ {
+ pool->is_mmap = MHD_YES;
+ }
+ pool->pos = 0;
+ pool->end = max;
+ pool->size = max;
+ return pool;
+}
+
+
+/**
+ * Destroy a memory pool.
+ *
+ * @param pool memory pool to destroy
+ */
+void
+MHD_pool_destroy (struct MemoryPool *pool)
+{
+ if (pool == NULL)
+ return;
+ if (pool->is_mmap == MHD_NO)
+ free (pool->memory);
+ else
+ MUNMAP (pool->memory, pool->size);
+ free (pool);
+}
+
+
+/**
+ * Allocate size bytes from the pool.
+ *
+ * @param pool memory pool to use for the operation
+ * @param size number of bytes to allocate
+ * @param from_end allocate from end of pool (set to MHD_YES);
+ * use this for small, persistent allocations that
+ * will never be reallocated
+ * @return NULL if the pool cannot support size more
+ * bytes
+ */
+void *
+MHD_pool_allocate (struct MemoryPool *pool,
+ size_t size, int from_end)
+{
+ void *ret;
+
+ size = ROUND_TO_ALIGN (size);
+ if ((pool->pos + size > pool->end) || (pool->pos + size < pool->pos))
+ return NULL;
+ if (from_end == MHD_YES)
+ {
+ ret = &pool->memory[pool->end - size];
+ pool->end -= size;
+ }
+ else
+ {
+ ret = &pool->memory[pool->pos];
+ pool->pos += size;
+ }
+ return ret;
+}
+
+
+/**
+ * Reallocate a block of memory obtained from the pool.
+ * This is particularly efficient when growing or
+ * shrinking the block that was last (re)allocated.
+ * If the given block is not the most recenlty
+ * (re)allocated block, the memory of the previous
+ * allocation may be leaked until the pool is
+ * destroyed (and copying the data maybe required).
+ *
+ * @param pool memory pool to use for the operation
+ * @param old the existing block
+ * @param old_size the size of the existing block
+ * @param new_size the new size of the block
+ * @return new address of the block, or
+ * NULL if the pool cannot support new_size
+ * bytes (old continues to be valid for old_size)
+ */
+void *
+MHD_pool_reallocate (struct MemoryPool *pool,
+ void *old,
+ size_t old_size,
+ size_t new_size)
+{
+ void *ret;
+
+ new_size = ROUND_TO_ALIGN (new_size);
+ if ((pool->end < old_size) || (pool->end < new_size))
+ return NULL; /* unsatisfiable or bogus request */
+
+ if ((pool->pos >= old_size) && (&pool->memory[pool->pos - old_size] == old))
+ {
+ /* was the previous allocation - optimize! */
+ if (pool->pos + new_size - old_size <= pool->end)
+ {
+ /* fits */
+ pool->pos += new_size - old_size;
+ if (new_size < old_size) /* shrinking - zero again! */
+ memset (&pool->memory[pool->pos], 0, old_size - new_size);
+ return old;
+ }
+ /* does not fit */
+ return NULL;
+ }
+ if (new_size <= old_size)
+ return old; /* cannot shrink, no need to move */
+ if ((pool->pos + new_size >= pool->pos) &&
+ (pool->pos + new_size <= pool->end))
+ {
+ /* fits */
+ ret = &pool->memory[pool->pos];
+ memcpy (ret, old, old_size);
+ pool->pos += new_size;
+ return ret;
+ }
+ /* does not fit */
+ return NULL;
+}
+
+
+/**
+ * Clear all entries from the memory pool except
+ * for "keep" of the given "size".
+ *
+ * @param pool memory pool to use for the operation
+ * @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,
+ size_t size)
+{
+ size = ROUND_TO_ALIGN (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 */
Deleted: libmicrohttpd/src/microhttpd/memorypool.h
===================================================================
--- libmicrohttpd/src/daemon/memorypool.h 2013-05-05 12:01:06 UTC (rev
27023)
+++ libmicrohttpd/src/microhttpd/memorypool.h 2013-05-05 18:07:33 UTC (rev
27025)
@@ -1,107 +0,0 @@
-/*
- This file is part of libmicrohttpd
- (C) 2007, 2009 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
- version 2.1 of the License, or (at your option) any later version.
-
- This library is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- Lesser General Public License for more details.
-
- You should have received a copy of the GNU Lesser General Public
- License along with this library; if not, write to the Free Software
- Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
USA
-*/
-
-/**
- * @file memorypool.h
- * @brief memory pool; mostly used for efficient (de)allocation
- * for each connection and bounding memory use for each
- * request
- * @author Christian Grothoff
- */
-
-#ifndef MEMORYPOOL_H
-#define MEMORYPOOL_H
-
-#include "internal.h"
-
-/**
- * Opaque handle for a memory pool.
- * Pools are not reentrant and must not be used
- * by multiple threads.
- */
-struct MemoryPool;
-
-
-/**
- * Create a memory pool.
- *
- * @param max maximum size of the pool
- */
-struct MemoryPool *
-MHD_pool_create (size_t max);
-
-
-/**
- * Destroy a memory pool.
- */
-void
-MHD_pool_destroy (struct MemoryPool *pool);
-
-
-/**
- * Allocate size bytes from the pool.
- *
- * @param from_end allocate from end of pool (set to MHD_YES);
- * use this for small, persistent allocations that
- * will never be reallocated
- * @return NULL if the pool cannot support size more
- * bytes
- */
-void *
-MHD_pool_allocate (struct MemoryPool *pool,
- size_t size, int from_end);
-
-
-/**
- * Reallocate a block of memory obtained from the pool.
- * This is particularly efficient when growing or
- * shrinking the block that was last (re)allocated.
- * If the given block is not the most recenlty
- * (re)allocated block, the memory of the previous
- * allocation may be leaked until the pool is
- * destroyed (and copying the data maybe required).
- *
- * @param old the existing block
- * @param old_size the size of the existing block
- * @param new_size the new size of the block
- * @return new address of the block, or
- * NULL if the pool cannot support new_size
- * bytes (old continues to be valid for old_size)
- */
-void *
-MHD_pool_reallocate (struct MemoryPool *pool,
- void *old,
- size_t old_size,
- size_t 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,
- size_t size);
-
-#endif
Copied: libmicrohttpd/src/microhttpd/memorypool.h (from rev 27024,
libmicrohttpd/src/daemon/memorypool.h)
===================================================================
--- libmicrohttpd/src/microhttpd/memorypool.h (rev 0)
+++ libmicrohttpd/src/microhttpd/memorypool.h 2013-05-05 18:07:33 UTC (rev
27025)
@@ -0,0 +1,114 @@
+/*
+ This file is part of libmicrohttpd
+ (C) 2007, 2009 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
+ version 2.1 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
USA
+*/
+
+/**
+ * @file memorypool.h
+ * @brief memory pool; mostly used for efficient (de)allocation
+ * for each connection and bounding memory use for each
+ * request
+ * @author Christian Grothoff
+ */
+
+#ifndef MEMORYPOOL_H
+#define MEMORYPOOL_H
+
+#include "internal.h"
+
+/**
+ * Opaque handle for a memory pool.
+ * Pools are not reentrant and must not be used
+ * by multiple threads.
+ */
+struct MemoryPool;
+
+
+/**
+ * Create a memory pool.
+ *
+ * @param max maximum size of the pool
+ * @return NULL on error
+ */
+struct MemoryPool *
+MHD_pool_create (size_t max);
+
+
+/**
+ * Destroy a memory pool.
+ *
+ * @param pool memory pool to destroy
+ */
+void
+MHD_pool_destroy (struct MemoryPool *pool);
+
+
+/**
+ * Allocate size bytes from the pool.
+ *
+ * @param pool memory pool to use for the operation
+ * @param size number of bytes to allocate
+ * @param from_end allocate from end of pool (set to MHD_YES);
+ * use this for small, persistent allocations that
+ * will never be reallocated
+ * @return NULL if the pool cannot support size more
+ * bytes
+ */
+void *
+MHD_pool_allocate (struct MemoryPool *pool,
+ size_t size, int from_end);
+
+
+/**
+ * Reallocate a block of memory obtained from the pool.
+ * This is particularly efficient when growing or
+ * shrinking the block that was last (re)allocated.
+ * If the given block is not the most recenlty
+ * (re)allocated block, the memory of the previous
+ * allocation may be leaked until the pool is
+ * destroyed (and copying the data maybe required).
+ *
+ * @param pool memory pool to use for the operation
+ * @param old the existing block
+ * @param old_size the size of the existing block
+ * @param new_size the new size of the block
+ * @return new address of the block, or
+ * NULL if the pool cannot support new_size
+ * bytes (old continues to be valid for old_size)
+ */
+void *
+MHD_pool_reallocate (struct MemoryPool *pool,
+ void *old,
+ size_t old_size,
+ size_t new_size);
+
+
+/**
+ * Clear all entries from the memory pool except
+ * for "keep" of the given "size".
+ *
+ * @param pool memory pool to use for the operation
+ * @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,
+ size_t size);
+
+#endif
Deleted: libmicrohttpd/src/microhttpd/postprocessor.c
===================================================================
--- libmicrohttpd/src/daemon/postprocessor.c 2013-05-05 12:01:06 UTC (rev
27023)
+++ libmicrohttpd/src/microhttpd/postprocessor.c 2013-05-05 18:07:33 UTC
(rev 27025)
@@ -1,1143 +0,0 @@
-/*
- This file is part of libmicrohttpd
- (C) 2007, 2009, 2010, 2011, 2012 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
- version 2.1 of the License, or (at your option) any later version.
-
- This library is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- Lesser General Public License for more details.
-
- You should have received a copy of the GNU Lesser General Public
- License along with this library; if not, write to the Free Software
- Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
USA
-*/
-
-/**
- * @file postprocessor.c
- * @brief Methods for parsing POST data
- * @author Christian Grothoff
- */
-
-#include "internal.h"
-
-/**
- * Size of on-stack buffer that we use for un-escaping of the value.
- * We use a pretty small value to be nice to the stack on embedded
- * systems.
- */
-#define XBUF_SIZE 512
-
-/**
- * States in the PP parser's state machine.
- */
-enum PP_State
-{
- /* general states */
- PP_Error,
- PP_Done,
- PP_Init,
- PP_NextBoundary,
-
- /* url encoding-states */
- PP_ProcessValue,
- PP_ExpectNewLine,
-
- /* post encoding-states */
- PP_ProcessEntryHeaders,
- PP_PerformCheckMultipart,
- PP_ProcessValueToBoundary,
- PP_PerformCleanup,
-
- /* nested post-encoding states */
- PP_Nested_Init,
- PP_Nested_PerformMarking,
- PP_Nested_ProcessEntryHeaders,
- PP_Nested_ProcessValueToBoundary,
- PP_Nested_PerformCleanup
-
-};
-
-
-enum RN_State
-{
- /**
- * No RN-preprocessing in this state.
- */
- RN_Inactive = 0,
-
- /**
- * If the next character is '\n', skip it. Otherwise,
- * just go inactive.
- */
- RN_OptN = 1,
-
- /**
- * Expect '\r\n' (and only '\r\n'). As always, we also
- * expect only '\r' or only '\n'.
- */
- RN_Full = 2,
-
- /**
- * Expect either '\r\n' or '--\r\n'. If '--\r\n', transition into dash-state
- * for the main state machine
- */
- RN_Dash = 3,
-
- /**
- * Got a single dash, expect second dash.
- */
- RN_Dash2 = 4
-};
-
-
-/**
- * Bits for the globally known fields that
- * should not be deleted when we exit the
- * nested state.
- */
-enum NE_State
-{
- NE_none = 0,
- NE_content_name = 1,
- NE_content_type = 2,
- NE_content_filename = 4,
- NE_content_transfer_encoding = 8
-};
-
-
-/**
- * Internal state of the post-processor. Note that the fields
- * are sorted by type to enable optimal packing by the compiler.
- */
-struct MHD_PostProcessor
-{
-
- /**
- * The connection for which we are doing
- * POST processing.
- */
- struct MHD_Connection *connection;
-
- /**
- * Function to call with POST data.
- */
- MHD_PostDataIterator ikvi;
-
- /**
- * Extra argument to ikvi.
- */
- void *cls;
-
- /**
- * Encoding as given by the headers of the
- * connection.
- */
- const char *encoding;
-
- /**
- * Primary boundary (points into encoding string)
- */
- const char *boundary;
-
- /**
- * Nested boundary (if we have multipart/mixed encoding).
- */
- char *nested_boundary;
-
- /**
- * Pointer to the name given in disposition.
- */
- char *content_name;
-
- /**
- * Pointer to the (current) content type.
- */
- char *content_type;
-
- /**
- * Pointer to the (current) filename.
- */
- char *content_filename;
-
- /**
- * Pointer to the (current) encoding.
- */
- char *content_transfer_encoding;
-
- /**
- * Unprocessed value bytes due to escape
- * sequences (URL-encoding only).
- */
- char xbuf[8];
-
- /**
- * Size of our buffer for the key.
- */
- size_t buffer_size;
-
- /**
- * Current position in the key buffer.
- */
- size_t buffer_pos;
-
- /**
- * Current position in xbuf.
- */
- size_t xbuf_pos;
-
- /**
- * Current offset in the value being processed.
- */
- uint64_t value_offset;
-
- /**
- * strlen(boundary) -- if boundary != NULL.
- */
- size_t blen;
-
- /**
- * strlen(nested_boundary) -- if nested_boundary != NULL.
- */
- size_t nlen;
-
- /**
- * Do we have to call the 'ikvi' callback when processing the
- * multipart post body even if the size of the payload is zero?
- * Set to MHD_YES whenever we parse a new multiparty entry header,
- * and to MHD_NO the first time we call the 'ikvi' callback.
- * Used to ensure that we do always call 'ikvi' even if the
- * payload is empty (but not more than once).
- */
- int must_ikvi;
-
- /**
- * State of the parser.
- */
- enum PP_State state;
-
- /**
- * Side-state-machine: skip '\r\n' (or just '\n').
- * Set to 0 if we are not in skip mode. Set to 2
- * if a '\r\n' is expected, set to 1 if a '\n' should
- * be skipped if it is the next character.
- */
- enum RN_State skip_rn;
-
- /**
- * If we are in skip_rn with "dash" mode and
- * do find 2 dashes, what state do we go into?
- */
- enum PP_State dash_state;
-
- /**
- * Which headers are global? (used to tell which
- * headers were only valid for the nested multipart).
- */
- enum NE_State have;
-
-};
-
-
-/**
- * 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
- * @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,
- size_t buffer_size,
- MHD_PostDataIterator ikvi, void *cls)
-{
- struct MHD_PostProcessor *ret;
- const char *encoding;
- const char *boundary;
- size_t blen;
-
- if ((buffer_size < 256) || (connection == NULL) || (ikvi == NULL))
- mhd_panic (mhd_panic_cls, __FILE__, __LINE__, NULL);
- encoding = MHD_lookup_connection_value (connection,
- MHD_HEADER_KIND,
- MHD_HTTP_HEADER_CONTENT_TYPE);
- if (encoding == NULL)
- return NULL;
- boundary = NULL;
- if (0 != strncasecmp (MHD_HTTP_POST_ENCODING_FORM_URLENCODED, encoding,
- strlen (MHD_HTTP_POST_ENCODING_FORM_URLENCODED)))
- {
- if (0 !=
- strncasecmp (MHD_HTTP_POST_ENCODING_MULTIPART_FORMDATA, encoding,
- strlen (MHD_HTTP_POST_ENCODING_MULTIPART_FORMDATA)))
- return NULL;
- boundary =
- &encoding[strlen (MHD_HTTP_POST_ENCODING_MULTIPART_FORMDATA)];
- /* Q: should this be "strcasestr"? */
- boundary = strstr (boundary, "boundary=");
- if (NULL == boundary)
- return NULL; /* failed to determine boundary */
- boundary += strlen ("boundary=");
- blen = strlen (boundary);
- if ((blen == 0) || (blen * 2 + 2 > buffer_size))
- return NULL; /* (will be) out of memory or invalid boundary
*/
- if ( (boundary[0] == '"') && (boundary[blen - 1] == '"') )
- {
- /* remove enclosing quotes */
- ++boundary;
- blen -= 2;
- }
- }
- else
- blen = 0;
- buffer_size += 4; /* round up to get nice block sizes despite boundary
search */
-
- /* add +1 to ensure we ALWAYS have a zero-termination at the end */
- if (NULL == (ret = malloc (sizeof (struct MHD_PostProcessor) + buffer_size +
1)))
- return NULL;
- memset (ret, 0, sizeof (struct MHD_PostProcessor) + buffer_size + 1);
- ret->connection = connection;
- ret->ikvi = ikvi;
- ret->cls = cls;
- ret->encoding = encoding;
- ret->buffer_size = buffer_size;
- ret->state = PP_Init;
- ret->blen = blen;
- ret->boundary = boundary;
- ret->skip_rn = RN_Inactive;
- return ret;
-}
-
-
-/**
- * Process url-encoded POST data.
- *
- * @param pp post processor context
- * @param post_data upload data
- * @param post_data_len number of bytes in upload_data
- * @return MHD_YES on success, MHD_NO if there was an error processing the data
- */
-static int
-post_process_urlencoded (struct MHD_PostProcessor *pp,
- const char *post_data,
- size_t post_data_len)
-{
- size_t equals;
- size_t amper;
- size_t poff;
- size_t xoff;
- size_t delta;
- int end_of_value_found;
- char *buf;
- char xbuf[XBUF_SIZE + 1];
-
- buf = (char *) &pp[1];
- poff = 0;
- while (poff < post_data_len)
- {
- switch (pp->state)
- {
- case PP_Error:
- return MHD_NO;
- case PP_Done:
- /* did not expect to receive more data */
- pp->state = PP_Error;
- return MHD_NO;
- case PP_Init:
- equals = 0;
- while ((equals + poff < post_data_len) &&
- (post_data[equals + poff] != '='))
- equals++;
- if (equals + pp->buffer_pos > pp->buffer_size)
- {
- pp->state = PP_Error; /* out of memory */
- return MHD_NO;
- }
- memcpy (&buf[pp->buffer_pos], &post_data[poff], equals);
- pp->buffer_pos += equals;
- if (equals + poff == post_data_len)
- return MHD_YES; /* no '=' yet */
- buf[pp->buffer_pos] = '\0'; /* 0-terminate key */
- pp->buffer_pos = 0; /* reset for next key */
- MHD_http_unescape (NULL, NULL, buf);
- poff += equals + 1;
- pp->state = PP_ProcessValue;
- pp->value_offset = 0;
- break;
- case PP_ProcessValue:
- /* obtain rest of value from previous iteration */
- memcpy (xbuf, pp->xbuf, pp->xbuf_pos);
- xoff = pp->xbuf_pos;
- pp->xbuf_pos = 0;
-
- /* find last position in input buffer that is part of the value */
- amper = 0;
- while ((amper + poff < post_data_len) &&
- (amper < XBUF_SIZE) &&
- (post_data[amper + poff] != '&') &&
- (post_data[amper + poff] != '\n') &&
- (post_data[amper + poff] != '\r'))
- amper++;
- end_of_value_found = ((amper + poff < post_data_len) &&
- ((post_data[amper + poff] == '&') ||
- (post_data[amper + poff] == '\n') ||
- (post_data[amper + poff] == '\r')));
- /* compute delta, the maximum number of bytes that we will be able to
- process right now (either amper-limited of xbuf-size limited) */
- delta = amper;
- if (delta > XBUF_SIZE - xoff)
- delta = XBUF_SIZE - xoff;
-
- /* move input into processing buffer */
- memcpy (&xbuf[xoff], &post_data[poff], delta);
- xoff += delta;
- poff += delta;
-
- /* find if escape sequence is at the end of the processing buffer;
- if so, exclude those from processing (reduce delta to point at
- end of processed region) */
- delta = xoff;
- if ((delta > 0) && (xbuf[delta - 1] == '%'))
- delta--;
- else if ((delta > 1) && (xbuf[delta - 2] == '%'))
- delta -= 2;
-
- /* if we have an incomplete escape sequence, save it to
- pp->xbuf for later */
- if (delta < xoff)
- {
- memcpy (pp->xbuf, &xbuf[delta], xoff - delta);
- pp->xbuf_pos = xoff - delta;
- xoff = delta;
- }
-
- /* If we have nothing to do (delta == 0) and
- not just because the value is empty (are
- waiting for more data), go for next iteration */
- if ((xoff == 0) && (poff == post_data_len))
- continue;
-
- /* unescape */
- xbuf[xoff] = '\0'; /* 0-terminate in preparation */
- xoff = MHD_http_unescape (NULL, NULL, xbuf);
- /* finally: call application! */
- pp->must_ikvi = MHD_NO;
- if (MHD_NO == pp->ikvi (pp->cls, MHD_POSTDATA_KIND, (const char *)
&pp[1], /* key */
- NULL, NULL, NULL, xbuf, pp->value_offset,
- xoff))
- {
- pp->state = PP_Error;
- return MHD_NO;
- }
- pp->value_offset += xoff;
-
- /* are we done with the value? */
- if (end_of_value_found)
- {
- /* we found the end of the value! */
- if ((post_data[poff] == '\n') || (post_data[poff] == '\r'))
- {
- pp->state = PP_ExpectNewLine;
- }
- else if (post_data[poff] == '&')
- {
- poff++; /* skip '&' */
- pp->state = PP_Init;
- }
- }
- break;
- case PP_ExpectNewLine:
- if ((post_data[poff] == '\n') || (post_data[poff] == '\r'))
- {
- poff++;
- /* we are done, report error if we receive any more... */
- pp->state = PP_Done;
- return MHD_YES;
- }
- return MHD_NO;
- default:
- mhd_panic (mhd_panic_cls, __FILE__, __LINE__, NULL); /*
should never happen! */
- }
- }
- return MHD_YES;
-}
-
-
-/**
- * If the given line matches the prefix, strdup the
- * rest of the line into the suffix ptr.
- *
- * @param prefix prefix to match
- * @param line line to match prefix in
- * @param suffix set to a copy of the rest of the line, starting at the end of
the match
- * @return MHD_YES if there was a match, MHD_NO if not
- */
-static int
-try_match_header (const char *prefix, char *line, char **suffix)
-{
- if (NULL != *suffix)
- return MHD_NO;
- while (*line != 0)
- {
- if (0 == strncasecmp (prefix, line, strlen (prefix)))
- {
- *suffix = strdup (&line[strlen (prefix)]);
- return MHD_YES;
- }
- ++line;
- }
- return MHD_NO;
-}
-
-
-/**
- *
- * @param pp post processor context
- */
-static int
-find_boundary (struct MHD_PostProcessor *pp,
- const char *boundary,
- size_t blen,
- size_t *ioffptr,
- enum PP_State next_state, enum PP_State next_dash_state)
-{
- char *buf = (char *) &pp[1];
-
- if (pp->buffer_pos < 2 + blen)
- {
- if (pp->buffer_pos == pp->buffer_size)
- pp->state = PP_Error; /* out of memory */
- ++(*ioffptr);
- return MHD_NO; /* not enough data */
- }
- if ((0 != memcmp ("--", buf, 2)) || (0 != memcmp (&buf[2], boundary, blen)))
- {
- if (pp->state != PP_Init)
- pp->state = PP_Error;
- return MHD_NO; /* expected boundary */
- }
- /* remove boundary from buffer */
- (*ioffptr) += 2 + blen;
- /* next: start with headers */
- pp->skip_rn = RN_Dash;
- pp->state = next_state;
- pp->dash_state = next_dash_state;
- return MHD_YES;
-}
-
-
-/**
- * In buf, there maybe an expression '$key="$value"'. If that is the
- * case, copy a copy of $value to destination.
- *
- * If destination is already non-NULL,
- * do nothing.
- */
-static void
-try_get_value (const char *buf,
- const char *key,
- char **destination)
-{
- const char *spos;
- const char *bpos;
- const char *endv;
- size_t klen;
- size_t vlen;
-
- if (NULL != *destination)
- return;
- bpos = buf;
- klen = strlen (key);
- while (NULL != (spos = strstr (bpos, key)))
- {
- if ((spos[klen] != '=') || ((spos != buf) && (spos[-1] != ' ')))
- {
- /* no match */
- bpos = spos + 1;
- continue;
- }
- if (spos[klen + 1] != '"')
- return; /* not quoted */
- if (NULL == (endv = strchr (&spos[klen + 2], '\"')))
- return; /* no end-quote */
- vlen = endv - spos - klen - 1;
- *destination = malloc (vlen);
- if (NULL == *destination)
- return; /* out of memory */
- (*destination)[vlen - 1] = '\0';
- memcpy (*destination, &spos[klen + 2], vlen - 1);
- return; /* success */
- }
-}
-
-
-/**
- * Go over the headers of the part and update
- * the fields in "pp" according to what we find.
- * If we are at the end of the headers (as indicated
- * by an empty line), transition into next_state.
- *
- * @param pp post processor context
- * @param ioffptr set to how many bytes have been
- * processed
- * @return MHD_YES if we can continue processing,
- * MHD_NO on error or if we do not have
- * enough data yet
- */
-static int
-process_multipart_headers (struct MHD_PostProcessor *pp,
- size_t *ioffptr, enum PP_State next_state)
-{
- char *buf = (char *) &pp[1];
- size_t newline;
-
- newline = 0;
- while ((newline < pp->buffer_pos) &&
- (buf[newline] != '\r') && (buf[newline] != '\n'))
- newline++;
- if (newline == pp->buffer_size)
- {
- pp->state = PP_Error;
- return MHD_NO; /* out of memory */
- }
- if (newline == pp->buffer_pos)
- return MHD_NO; /* will need more data */
- if (newline == 0)
- {
- /* empty line - end of headers */
- pp->skip_rn = RN_Full;
- pp->state = next_state;
- return MHD_YES;
- }
- /* got an actual header */
- if (buf[newline] == '\r')
- pp->skip_rn = RN_OptN;
- buf[newline] = '\0';
- if (0 == strncasecmp ("Content-disposition: ",
- buf, strlen ("Content-disposition: ")))
- {
- try_get_value (&buf[strlen ("Content-disposition: ")],
- "name", &pp->content_name);
- try_get_value (&buf[strlen ("Content-disposition: ")],
- "filename", &pp->content_filename);
- }
- else
- {
- try_match_header ("Content-type: ", buf, &pp->content_type);
- try_match_header ("Content-Transfer-Encoding: ",
- buf, &pp->content_transfer_encoding);
- }
- (*ioffptr) += newline + 1;
- return MHD_YES;
-}
-
-
-/**
- * We have the value until we hit the given boundary;
- * process accordingly.
- *
- * @param pp post processor context
- * @param boundary the boundary to look for
- * @param blen strlen(boundary)
- * @param next_state what state to go into after the
- * boundary was found
- * @param next_dash_state state to go into if the next
- * boundary ends with "--"
- * @return MHD_YES if we can continue processing,
- * MHD_NO on error or if we do not have
- * enough data yet
- */
-static int
-process_value_to_boundary (struct MHD_PostProcessor *pp,
- size_t *ioffptr,
- const char *boundary,
- size_t blen,
- enum PP_State next_state,
- enum PP_State next_dash_state)
-{
- char *buf = (char *) &pp[1];
- size_t newline;
-
- /* all data in buf until the boundary
- (\r\n--+boundary) is part of the value */
- newline = 0;
- while (1)
- {
- while ((newline + 4 < pp->buffer_pos) &&
- (0 != memcmp ("\r\n--", &buf[newline], 4)))
- newline++;
- if (newline + pp->blen + 4 <= pp->buffer_pos)
- {
- /* can check boundary */
- if (0 != memcmp (&buf[newline + 4], boundary, pp->blen))
- {
- /* no boundary, "\r\n--" is part of content, skip */
- newline += 4;
- continue;
- }
- else
- {
- /* boundary found, process until newline then
- skip boundary and go back to init */
- pp->skip_rn = RN_Dash;
- pp->state = next_state;
- pp->dash_state = next_dash_state;
- (*ioffptr) += pp->blen + 4; /* skip boundary as well */
- buf[newline] = '\0';
- break;
- }
- }
- else
- {
- /* cannot check for boundary, process content that
- we have and check again later; except, if we have
- no content, abort (out of memory) */
- if ((newline == 0) && (pp->buffer_pos == pp->buffer_size))
- {
- pp->state = PP_Error;
- return MHD_NO;
- }
- break;
- }
- }
- /* newline is either at beginning of boundary or
- at least at the last character that we are sure
- is not part of the boundary */
- if ( ( (MHD_YES == pp->must_ikvi) ||
- (0 != newline) ) &&
- (MHD_NO == pp->ikvi (pp->cls,
- MHD_POSTDATA_KIND,
- pp->content_name,
- pp->content_filename,
- pp->content_type,
- pp->content_transfer_encoding,
- buf, pp->value_offset, newline)) )
- {
- pp->state = PP_Error;
- return MHD_NO;
- }
- pp->must_ikvi = MHD_NO;
- pp->value_offset += newline;
- (*ioffptr) += newline;
- return MHD_YES;
-}
-
-
-/**
- *
- * @param pp post processor context
- */
-static void
-free_unmarked (struct MHD_PostProcessor *pp)
-{
- if ((pp->content_name != NULL) && (0 == (pp->have & NE_content_name)))
- {
- free (pp->content_name);
- pp->content_name = NULL;
- }
- if ((pp->content_type != NULL) && (0 == (pp->have & NE_content_type)))
- {
- free (pp->content_type);
- pp->content_type = NULL;
- }
- if ((pp->content_filename != NULL) &&
- (0 == (pp->have & NE_content_filename)))
- {
- free (pp->content_filename);
- pp->content_filename = NULL;
- }
- if ((pp->content_transfer_encoding != NULL) &&
- (0 == (pp->have & NE_content_transfer_encoding)))
- {
- free (pp->content_transfer_encoding);
- pp->content_transfer_encoding = NULL;
- }
-}
-
-
-/**
- * Decode multipart POST data.
- *
- * @param pp post processor context
- */
-static int
-post_process_multipart (struct MHD_PostProcessor *pp,
- const char *post_data,
- size_t post_data_len)
-{
- char *buf;
- size_t max;
- size_t ioff;
- size_t poff;
- int state_changed;
-
- buf = (char *) &pp[1];
- ioff = 0;
- poff = 0;
- state_changed = 1;
- while ((poff < post_data_len) ||
- ((pp->buffer_pos > 0) && (state_changed != 0)))
- {
- /* first, move as much input data
- as possible to our internal buffer */
- max = pp->buffer_size - pp->buffer_pos;
- if (max > post_data_len - poff)
- max = post_data_len - poff;
- memcpy (&buf[pp->buffer_pos], &post_data[poff], max);
- poff += max;
- pp->buffer_pos += max;
- if ((max == 0) && (state_changed == 0) && (poff < post_data_len))
- {
- pp->state = PP_Error;
- return MHD_NO; /* out of memory */
- }
- state_changed = 0;
-
- /* first state machine for '\r'-'\n' and '--' handling */
- switch (pp->skip_rn)
- {
- case RN_Inactive:
- break;
- case RN_OptN:
- if (buf[0] == '\n')
- {
- ioff++;
- pp->skip_rn = RN_Inactive;
- goto AGAIN;
- }
- /* fall-through! */
- case RN_Dash:
- if (buf[0] == '-')
- {
- ioff++;
- pp->skip_rn = RN_Dash2;
- goto AGAIN;
- }
- pp->skip_rn = RN_Full;
- /* fall-through! */
- case RN_Full:
- if (buf[0] == '\r')
- {
- if ((pp->buffer_pos > 1) && (buf[1] == '\n'))
- {
- pp->skip_rn = RN_Inactive;
- ioff += 2;
- }
- else
- {
- pp->skip_rn = RN_OptN;
- ioff++;
- }
- goto AGAIN;
- }
- if (buf[0] == '\n')
- {
- ioff++;
- pp->skip_rn = RN_Inactive;
- goto AGAIN;
- }
- pp->skip_rn = RN_Inactive;
- pp->state = PP_Error;
- return MHD_NO; /* no '\r\n' */
- case RN_Dash2:
- if (buf[0] == '-')
- {
- ioff++;
- pp->skip_rn = RN_Full;
- pp->state = pp->dash_state;
- goto AGAIN;
- }
- pp->state = PP_Error;
- break;
- }
-
- /* main state engine */
- switch (pp->state)
- {
- case PP_Error:
- return MHD_NO;
- case PP_Done:
- /* did not expect to receive more data */
- pp->state = PP_Error;
- return MHD_NO;
- case PP_Init:
- /**
- * Per RFC2046 5.1.1 NOTE TO IMPLEMENTORS, consume anything
- * prior to the first multipart boundary:
- *
- * > There appears to be room for additional information prior
- * > to the first boundary delimiter line and following the
- * > final boundary delimiter line. These areas should
- * > generally be left blank, and implementations must ignore
- * > anything that appears before the first boundary delimiter
- * > line or after the last one.
- */
- (void) find_boundary (pp,
- pp->boundary,
- pp->blen,
- &ioff,
- PP_ProcessEntryHeaders, PP_Done);
- break;
- case PP_NextBoundary:
- if (MHD_NO == find_boundary (pp,
- pp->boundary,
- pp->blen,
- &ioff,
- PP_ProcessEntryHeaders, PP_Done))
- {
- if (pp->state == PP_Error)
- return MHD_NO;
- goto END;
- }
- break;
- case PP_ProcessEntryHeaders:
- pp->must_ikvi = MHD_YES;
- if (MHD_NO ==
- process_multipart_headers (pp, &ioff, PP_PerformCheckMultipart))
- {
- if (pp->state == PP_Error)
- return MHD_NO;
- else
- goto END;
- }
- state_changed = 1;
- break;
- case PP_PerformCheckMultipart:
- if ((pp->content_type != NULL) &&
- (0 == strncasecmp (pp->content_type,
- "multipart/mixed",
- strlen ("multipart/mixed"))))
- {
- pp->nested_boundary = strstr (pp->content_type, "boundary=");
- if (pp->nested_boundary == NULL)
- {
- pp->state = PP_Error;
- return MHD_NO;
- }
- pp->nested_boundary =
- strdup (&pp->nested_boundary[strlen ("boundary=")]);
- if (pp->nested_boundary == NULL)
- {
- /* out of memory */
- pp->state = PP_Error;
- return MHD_NO;
- }
- /* free old content type, we will need that field
- for the content type of the nested elements */
- free (pp->content_type);
- pp->content_type = NULL;
- pp->nlen = strlen (pp->nested_boundary);
- pp->state = PP_Nested_Init;
- state_changed = 1;
- break;
- }
- pp->state = PP_ProcessValueToBoundary;
- pp->value_offset = 0;
- state_changed = 1;
- break;
- case PP_ProcessValueToBoundary:
- if (MHD_NO == process_value_to_boundary (pp,
- &ioff,
- pp->boundary,
- pp->blen,
- PP_PerformCleanup,
- PP_Done))
- {
- if (pp->state == PP_Error)
- return MHD_NO;
- break;
- }
- break;
- case PP_PerformCleanup:
- /* clean up state of one multipart form-data element! */
- pp->have = NE_none;
- free_unmarked (pp);
- if (pp->nested_boundary != NULL)
- {
- free (pp->nested_boundary);
- pp->nested_boundary = NULL;
- }
- pp->state = PP_ProcessEntryHeaders;
- state_changed = 1;
- break;
- case PP_Nested_Init:
- if (pp->nested_boundary == NULL)
- {
- pp->state = PP_Error;
- return MHD_NO;
- }
- if (MHD_NO == find_boundary (pp,
- pp->nested_boundary,
- pp->nlen,
- &ioff,
- PP_Nested_PerformMarking,
- PP_NextBoundary /* or PP_Error? */ ))
- {
- if (pp->state == PP_Error)
- return MHD_NO;
- goto END;
- }
- break;
- case PP_Nested_PerformMarking:
- /* remember what headers were given
- globally */
- pp->have = NE_none;
- if (pp->content_name != NULL)
- pp->have |= NE_content_name;
- if (pp->content_type != NULL)
- pp->have |= NE_content_type;
- if (pp->content_filename != NULL)
- pp->have |= NE_content_filename;
- if (pp->content_transfer_encoding != NULL)
- pp->have |= NE_content_transfer_encoding;
- pp->state = PP_Nested_ProcessEntryHeaders;
- state_changed = 1;
- break;
- case PP_Nested_ProcessEntryHeaders:
- pp->value_offset = 0;
- if (MHD_NO ==
- process_multipart_headers (pp, &ioff,
- PP_Nested_ProcessValueToBoundary))
- {
- if (pp->state == PP_Error)
- return MHD_NO;
- else
- goto END;
- }
- state_changed = 1;
- break;
- case PP_Nested_ProcessValueToBoundary:
- if (MHD_NO == process_value_to_boundary (pp,
- &ioff,
- pp->nested_boundary,
- pp->nlen,
- PP_Nested_PerformCleanup,
- PP_NextBoundary))
- {
- if (pp->state == PP_Error)
- return MHD_NO;
- break;
- }
- break;
- case PP_Nested_PerformCleanup:
- free_unmarked (pp);
- pp->state = PP_Nested_ProcessEntryHeaders;
- state_changed = 1;
- break;
- default:
- mhd_panic (mhd_panic_cls, __FILE__, __LINE__, NULL); /*
should never happen! */
- }
- AGAIN:
- if (ioff > 0)
- {
- memmove (buf, &buf[ioff], pp->buffer_pos - ioff);
- pp->buffer_pos -= ioff;
- ioff = 0;
- state_changed = 1;
- }
- }
-END:
- if (ioff != 0)
- {
- memmove (buf, &buf[ioff], pp->buffer_pos - ioff);
- pp->buffer_pos -= ioff;
- }
- if (poff < post_data_len)
- {
- pp->state = PP_Error;
- return MHD_NO; /* serious error */
- }
- return MHD_YES;
-}
-
-
-/**
- * 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, size_t post_data_len)
-{
- if (post_data_len == 0)
- return MHD_YES;
- if (pp == NULL)
- return MHD_NO;
- if (0 == strncasecmp (MHD_HTTP_POST_ENCODING_FORM_URLENCODED, pp->encoding,
- strlen(MHD_HTTP_POST_ENCODING_FORM_URLENCODED)))
- return post_process_urlencoded (pp, post_data, post_data_len);
- if (0 ==
- strncasecmp (MHD_HTTP_POST_ENCODING_MULTIPART_FORMDATA, pp->encoding,
- strlen (MHD_HTTP_POST_ENCODING_MULTIPART_FORMDATA)))
- return post_process_multipart (pp, post_data, post_data_len);
- /* this should never be reached */
- return MHD_NO;
-}
-
-
-/**
- * Release PostProcessor resources.
- *
- * @param pp post processor context to destroy
- * @return MHD_YES if processing completed nicely,
- * MHD_NO if there were spurious characters / formatting
- * problems; it is common to ignore the return
- * value of this function
- */
-int
-MHD_destroy_post_processor (struct MHD_PostProcessor *pp)
-{
- int ret;
-
- if (NULL == pp)
- return MHD_YES;
- if (PP_ProcessValue == pp->state)
- {
- /* key without terminated value left at the end of the
- buffer; fake receiving a termination character to
- ensure it is also processed */
- post_process_urlencoded (pp, "\n", 1);
- }
- /* These internal strings need cleaning up since
- the post-processing may have been interrupted
- at any stage */
- if ((pp->xbuf_pos > 0) ||
- ( (pp->state != PP_Done) &&
- (pp->state != PP_ExpectNewLine)))
- ret = MHD_NO;
- else
- ret = MHD_YES;
- pp->have = NE_none;
- free_unmarked (pp);
- if (pp->nested_boundary != NULL)
- free (pp->nested_boundary);
- free (pp);
- return ret;
-}
-
-/* end of postprocessor.c */
Copied: libmicrohttpd/src/microhttpd/postprocessor.c (from rev 27024,
libmicrohttpd/src/daemon/postprocessor.c)
===================================================================
--- libmicrohttpd/src/microhttpd/postprocessor.c
(rev 0)
+++ libmicrohttpd/src/microhttpd/postprocessor.c 2013-05-05 18:07:33 UTC
(rev 27025)
@@ -0,0 +1,1158 @@
+/*
+ This file is part of libmicrohttpd
+ (C) 2007, 2009, 2010, 2011, 2012 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
+ version 2.1 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
USA
+*/
+
+/**
+ * @file postprocessor.c
+ * @brief Methods for parsing POST data
+ * @author Christian Grothoff
+ */
+
+#include "internal.h"
+
+/**
+ * Size of on-stack buffer that we use for un-escaping of the value.
+ * We use a pretty small value to be nice to the stack on embedded
+ * systems.
+ */
+#define XBUF_SIZE 512
+
+/**
+ * States in the PP parser's state machine.
+ */
+enum PP_State
+{
+ /* general states */
+ PP_Error,
+ PP_Done,
+ PP_Init,
+ PP_NextBoundary,
+
+ /* url encoding-states */
+ PP_ProcessValue,
+ PP_ExpectNewLine,
+
+ /* post encoding-states */
+ PP_ProcessEntryHeaders,
+ PP_PerformCheckMultipart,
+ PP_ProcessValueToBoundary,
+ PP_PerformCleanup,
+
+ /* nested post-encoding states */
+ PP_Nested_Init,
+ PP_Nested_PerformMarking,
+ PP_Nested_ProcessEntryHeaders,
+ PP_Nested_ProcessValueToBoundary,
+ PP_Nested_PerformCleanup
+
+};
+
+
+enum RN_State
+{
+ /**
+ * No RN-preprocessing in this state.
+ */
+ RN_Inactive = 0,
+
+ /**
+ * If the next character is CR, skip it. Otherwise,
+ * just go inactive.
+ */
+ RN_OptN = 1,
+
+ /**
+ * Expect LFCR (and only LFCR). As always, we also
+ * expect only LF or only CR.
+ */
+ RN_Full = 2,
+
+ /**
+ * Expect either LFCR or '--'LFCR. If '--'LFCR, transition into dash-state
+ * for the main state machine
+ */
+ RN_Dash = 3,
+
+ /**
+ * Got a single dash, expect second dash.
+ */
+ RN_Dash2 = 4
+};
+
+
+/**
+ * Bits for the globally known fields that
+ * should not be deleted when we exit the
+ * nested state.
+ */
+enum NE_State
+{
+ NE_none = 0,
+ NE_content_name = 1,
+ NE_content_type = 2,
+ NE_content_filename = 4,
+ NE_content_transfer_encoding = 8
+};
+
+
+/**
+ * Internal state of the post-processor. Note that the fields
+ * are sorted by type to enable optimal packing by the compiler.
+ */
+struct MHD_PostProcessor
+{
+
+ /**
+ * The connection for which we are doing
+ * POST processing.
+ */
+ struct MHD_Connection *connection;
+
+ /**
+ * Function to call with POST data.
+ */
+ MHD_PostDataIterator ikvi;
+
+ /**
+ * Extra argument to ikvi.
+ */
+ void *cls;
+
+ /**
+ * Encoding as given by the headers of the
+ * connection.
+ */
+ const char *encoding;
+
+ /**
+ * Primary boundary (points into encoding string)
+ */
+ const char *boundary;
+
+ /**
+ * Nested boundary (if we have multipart/mixed encoding).
+ */
+ char *nested_boundary;
+
+ /**
+ * Pointer to the name given in disposition.
+ */
+ char *content_name;
+
+ /**
+ * Pointer to the (current) content type.
+ */
+ char *content_type;
+
+ /**
+ * Pointer to the (current) filename.
+ */
+ char *content_filename;
+
+ /**
+ * Pointer to the (current) encoding.
+ */
+ char *content_transfer_encoding;
+
+ /**
+ * Unprocessed value bytes due to escape
+ * sequences (URL-encoding only).
+ */
+ char xbuf[8];
+
+ /**
+ * Size of our buffer for the key.
+ */
+ size_t buffer_size;
+
+ /**
+ * Current position in the key buffer.
+ */
+ size_t buffer_pos;
+
+ /**
+ * Current position in xbuf.
+ */
+ size_t xbuf_pos;
+
+ /**
+ * Current offset in the value being processed.
+ */
+ uint64_t value_offset;
+
+ /**
+ * strlen(boundary) -- if boundary != NULL.
+ */
+ size_t blen;
+
+ /**
+ * strlen(nested_boundary) -- if nested_boundary != NULL.
+ */
+ size_t nlen;
+
+ /**
+ * Do we have to call the 'ikvi' callback when processing the
+ * multipart post body even if the size of the payload is zero?
+ * Set to MHD_YES whenever we parse a new multiparty entry header,
+ * and to MHD_NO the first time we call the 'ikvi' callback.
+ * Used to ensure that we do always call 'ikvi' even if the
+ * payload is empty (but not more than once).
+ */
+ int must_ikvi;
+
+ /**
+ * State of the parser.
+ */
+ enum PP_State state;
+
+ /**
+ * Side-state-machine: skip LRCR (or just LF).
+ * Set to 0 if we are not in skip mode. Set to 2
+ * if a LFCR is expected, set to 1 if a CR should
+ * be skipped if it is the next character.
+ */
+ enum RN_State skip_rn;
+
+ /**
+ * If we are in skip_rn with "dash" mode and
+ * do find 2 dashes, what state do we go into?
+ */
+ enum PP_State dash_state;
+
+ /**
+ * Which headers are global? (used to tell which
+ * headers were only valid for the nested multipart).
+ */
+ enum NE_State have;
+
+};
+
+
+/**
+ * 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 iter iterator to be called with the parsed data
+ * @param iter_cls first argument to iter
+ * @return NULL on error (out of memory, unsupported encoding),
+ * otherwise a PP handle
+ */
+struct MHD_PostProcessor *
+MHD_create_post_processor (struct MHD_Connection *connection,
+ size_t buffer_size,
+ MHD_PostDataIterator iter, void *iter_cls)
+{
+ struct MHD_PostProcessor *ret;
+ const char *encoding;
+ const char *boundary;
+ size_t blen;
+
+ if ((buffer_size < 256) || (connection == NULL) || (iter == NULL))
+ mhd_panic (mhd_panic_cls, __FILE__, __LINE__, NULL);
+ encoding = MHD_lookup_connection_value (connection,
+ MHD_HEADER_KIND,
+ MHD_HTTP_HEADER_CONTENT_TYPE);
+ if (encoding == NULL)
+ return NULL;
+ boundary = NULL;
+ if (0 != strncasecmp (MHD_HTTP_POST_ENCODING_FORM_URLENCODED, encoding,
+ strlen (MHD_HTTP_POST_ENCODING_FORM_URLENCODED)))
+ {
+ if (0 !=
+ strncasecmp (MHD_HTTP_POST_ENCODING_MULTIPART_FORMDATA, encoding,
+ strlen (MHD_HTTP_POST_ENCODING_MULTIPART_FORMDATA)))
+ return NULL;
+ boundary =
+ &encoding[strlen (MHD_HTTP_POST_ENCODING_MULTIPART_FORMDATA)];
+ /* Q: should this be "strcasestr"? */
+ boundary = strstr (boundary, "boundary=");
+ if (NULL == boundary)
+ return NULL; /* failed to determine boundary */
+ boundary += strlen ("boundary=");
+ blen = strlen (boundary);
+ if ((blen == 0) || (blen * 2 + 2 > buffer_size))
+ return NULL; /* (will be) out of memory or invalid boundary
*/
+ if ( (boundary[0] == '"') && (boundary[blen - 1] == '"') )
+ {
+ /* remove enclosing quotes */
+ ++boundary;
+ blen -= 2;
+ }
+ }
+ else
+ blen = 0;
+ buffer_size += 4; /* round up to get nice block sizes despite boundary
search */
+
+ /* add +1 to ensure we ALWAYS have a zero-termination at the end */
+ if (NULL == (ret = malloc (sizeof (struct MHD_PostProcessor) + buffer_size +
1)))
+ return NULL;
+ memset (ret, 0, sizeof (struct MHD_PostProcessor) + buffer_size + 1);
+ ret->connection = connection;
+ ret->ikvi = iter;
+ ret->cls = iter_cls;
+ ret->encoding = encoding;
+ ret->buffer_size = buffer_size;
+ ret->state = PP_Init;
+ ret->blen = blen;
+ ret->boundary = boundary;
+ ret->skip_rn = RN_Inactive;
+ return ret;
+}
+
+
+/**
+ * Process url-encoded POST data.
+ *
+ * @param pp post processor context
+ * @param post_data upload data
+ * @param post_data_len number of bytes in upload_data
+ * @return MHD_YES on success, MHD_NO if there was an error processing the data
+ */
+static int
+post_process_urlencoded (struct MHD_PostProcessor *pp,
+ const char *post_data,
+ size_t post_data_len)
+{
+ size_t equals;
+ size_t amper;
+ size_t poff;
+ size_t xoff;
+ size_t delta;
+ int end_of_value_found;
+ char *buf;
+ char xbuf[XBUF_SIZE + 1];
+
+ buf = (char *) &pp[1];
+ poff = 0;
+ while (poff < post_data_len)
+ {
+ switch (pp->state)
+ {
+ case PP_Error:
+ return MHD_NO;
+ case PP_Done:
+ /* did not expect to receive more data */
+ pp->state = PP_Error;
+ return MHD_NO;
+ case PP_Init:
+ equals = 0;
+ while ((equals + poff < post_data_len) &&
+ (post_data[equals + poff] != '='))
+ equals++;
+ if (equals + pp->buffer_pos > pp->buffer_size)
+ {
+ pp->state = PP_Error; /* out of memory */
+ return MHD_NO;
+ }
+ memcpy (&buf[pp->buffer_pos], &post_data[poff], equals);
+ pp->buffer_pos += equals;
+ if (equals + poff == post_data_len)
+ return MHD_YES; /* no '=' yet */
+ buf[pp->buffer_pos] = '\0'; /* 0-terminate key */
+ pp->buffer_pos = 0; /* reset for next key */
+ MHD_http_unescape (NULL, NULL, buf);
+ poff += equals + 1;
+ pp->state = PP_ProcessValue;
+ pp->value_offset = 0;
+ break;
+ case PP_ProcessValue:
+ /* obtain rest of value from previous iteration */
+ memcpy (xbuf, pp->xbuf, pp->xbuf_pos);
+ xoff = pp->xbuf_pos;
+ pp->xbuf_pos = 0;
+
+ /* find last position in input buffer that is part of the value */
+ amper = 0;
+ while ((amper + poff < post_data_len) &&
+ (amper < XBUF_SIZE) &&
+ (post_data[amper + poff] != '&') &&
+ (post_data[amper + poff] != '\n') &&
+ (post_data[amper + poff] != '\r'))
+ amper++;
+ end_of_value_found = ((amper + poff < post_data_len) &&
+ ((post_data[amper + poff] == '&') ||
+ (post_data[amper + poff] == '\n') ||
+ (post_data[amper + poff] == '\r')));
+ /* compute delta, the maximum number of bytes that we will be able to
+ process right now (either amper-limited of xbuf-size limited) */
+ delta = amper;
+ if (delta > XBUF_SIZE - xoff)
+ delta = XBUF_SIZE - xoff;
+
+ /* move input into processing buffer */
+ memcpy (&xbuf[xoff], &post_data[poff], delta);
+ xoff += delta;
+ poff += delta;
+
+ /* find if escape sequence is at the end of the processing buffer;
+ if so, exclude those from processing (reduce delta to point at
+ end of processed region) */
+ delta = xoff;
+ if ((delta > 0) && (xbuf[delta - 1] == '%'))
+ delta--;
+ else if ((delta > 1) && (xbuf[delta - 2] == '%'))
+ delta -= 2;
+
+ /* if we have an incomplete escape sequence, save it to
+ pp->xbuf for later */
+ if (delta < xoff)
+ {
+ memcpy (pp->xbuf, &xbuf[delta], xoff - delta);
+ pp->xbuf_pos = xoff - delta;
+ xoff = delta;
+ }
+
+ /* If we have nothing to do (delta == 0) and
+ not just because the value is empty (are
+ waiting for more data), go for next iteration */
+ if ((xoff == 0) && (poff == post_data_len))
+ continue;
+
+ /* unescape */
+ xbuf[xoff] = '\0'; /* 0-terminate in preparation */
+ xoff = MHD_http_unescape (NULL, NULL, xbuf);
+ /* finally: call application! */
+ pp->must_ikvi = MHD_NO;
+ if (MHD_NO == pp->ikvi (pp->cls, MHD_POSTDATA_KIND, (const char *)
&pp[1], /* key */
+ NULL, NULL, NULL, xbuf, pp->value_offset,
+ xoff))
+ {
+ pp->state = PP_Error;
+ return MHD_NO;
+ }
+ pp->value_offset += xoff;
+
+ /* are we done with the value? */
+ if (end_of_value_found)
+ {
+ /* we found the end of the value! */
+ if ((post_data[poff] == '\n') || (post_data[poff] == '\r'))
+ {
+ pp->state = PP_ExpectNewLine;
+ }
+ else if (post_data[poff] == '&')
+ {
+ poff++; /* skip '&' */
+ pp->state = PP_Init;
+ }
+ }
+ break;
+ case PP_ExpectNewLine:
+ if ((post_data[poff] == '\n') || (post_data[poff] == '\r'))
+ {
+ poff++;
+ /* we are done, report error if we receive any more... */
+ pp->state = PP_Done;
+ return MHD_YES;
+ }
+ return MHD_NO;
+ default:
+ mhd_panic (mhd_panic_cls, __FILE__, __LINE__, NULL); /*
should never happen! */
+ }
+ }
+ return MHD_YES;
+}
+
+
+/**
+ * If the given line matches the prefix, strdup the
+ * rest of the line into the suffix ptr.
+ *
+ * @param prefix prefix to match
+ * @param line line to match prefix in
+ * @param suffix set to a copy of the rest of the line, starting at the end of
the match
+ * @return MHD_YES if there was a match, MHD_NO if not
+ */
+static int
+try_match_header (const char *prefix, char *line, char **suffix)
+{
+ if (NULL != *suffix)
+ return MHD_NO;
+ while (*line != 0)
+ {
+ if (0 == strncasecmp (prefix, line, strlen (prefix)))
+ {
+ *suffix = strdup (&line[strlen (prefix)]);
+ return MHD_YES;
+ }
+ ++line;
+ }
+ return MHD_NO;
+}
+
+
+/**
+ *
+ * @param pp post processor context
+ * @param boundary boundary to look for
+ * @param blen number of bytes in boundary
+ * @param ioffptr set to the end of the boundary if found,
+ * otherwise incremented by one (FIXME: quirky API!)
+ * @param next_state state to which we should advance the post processor
+ * if the boundary is found
+ * @param next_dash_state dash_state to which we should advance the
+ * post processor if the boundary is found
+ * @return MHD_NO if the boundary is not found, MHD_YES if we did find it
+ */
+static int
+find_boundary (struct MHD_PostProcessor *pp,
+ const char *boundary,
+ size_t blen,
+ size_t *ioffptr,
+ enum PP_State next_state, enum PP_State next_dash_state)
+{
+ char *buf = (char *) &pp[1];
+
+ if (pp->buffer_pos < 2 + blen)
+ {
+ if (pp->buffer_pos == pp->buffer_size)
+ pp->state = PP_Error; /* out of memory */
+ ++(*ioffptr);
+ return MHD_NO; /* not enough data */
+ }
+ if ((0 != memcmp ("--", buf, 2)) || (0 != memcmp (&buf[2], boundary, blen)))
+ {
+ if (pp->state != PP_Init)
+ pp->state = PP_Error;
+ return MHD_NO; /* expected boundary */
+ }
+ /* remove boundary from buffer */
+ (*ioffptr) += 2 + blen;
+ /* next: start with headers */
+ pp->skip_rn = RN_Dash;
+ pp->state = next_state;
+ pp->dash_state = next_dash_state;
+ return MHD_YES;
+}
+
+
+/**
+ * In buf, there maybe an expression '$key="$value"'. If that is the
+ * case, copy a copy of $value to destination.
+ *
+ * If destination is already non-NULL,
+ * do nothing.
+ */
+static void
+try_get_value (const char *buf,
+ const char *key,
+ char **destination)
+{
+ const char *spos;
+ const char *bpos;
+ const char *endv;
+ size_t klen;
+ size_t vlen;
+
+ if (NULL != *destination)
+ return;
+ bpos = buf;
+ klen = strlen (key);
+ while (NULL != (spos = strstr (bpos, key)))
+ {
+ if ((spos[klen] != '=') || ((spos != buf) && (spos[-1] != ' ')))
+ {
+ /* no match */
+ bpos = spos + 1;
+ continue;
+ }
+ if (spos[klen + 1] != '"')
+ return; /* not quoted */
+ if (NULL == (endv = strchr (&spos[klen + 2], '\"')))
+ return; /* no end-quote */
+ vlen = endv - spos - klen - 1;
+ *destination = malloc (vlen);
+ if (NULL == *destination)
+ return; /* out of memory */
+ (*destination)[vlen - 1] = '\0';
+ memcpy (*destination, &spos[klen + 2], vlen - 1);
+ return; /* success */
+ }
+}
+
+
+/**
+ * Go over the headers of the part and update
+ * the fields in "pp" according to what we find.
+ * If we are at the end of the headers (as indicated
+ * by an empty line), transition into next_state.
+ *
+ * @param pp post processor context
+ * @param ioffptr set to how many bytes have been
+ * processed
+ * @param next_state state to which the post processor should
+ * be advanced if we find the end of the headers
+ * @return MHD_YES if we can continue processing,
+ * MHD_NO on error or if we do not have
+ * enough data yet
+ */
+static int
+process_multipart_headers (struct MHD_PostProcessor *pp,
+ size_t *ioffptr, enum PP_State next_state)
+{
+ char *buf = (char *) &pp[1];
+ size_t newline;
+
+ newline = 0;
+ while ((newline < pp->buffer_pos) &&
+ (buf[newline] != '\r') && (buf[newline] != '\n'))
+ newline++;
+ if (newline == pp->buffer_size)
+ {
+ pp->state = PP_Error;
+ return MHD_NO; /* out of memory */
+ }
+ if (newline == pp->buffer_pos)
+ return MHD_NO; /* will need more data */
+ if (0 == newline)
+ {
+ /* empty line - end of headers */
+ pp->skip_rn = RN_Full;
+ pp->state = next_state;
+ return MHD_YES;
+ }
+ /* got an actual header */
+ if (buf[newline] == '\r')
+ pp->skip_rn = RN_OptN;
+ buf[newline] = '\0';
+ if (0 == strncasecmp ("Content-disposition: ",
+ buf, strlen ("Content-disposition: ")))
+ {
+ try_get_value (&buf[strlen ("Content-disposition: ")],
+ "name", &pp->content_name);
+ try_get_value (&buf[strlen ("Content-disposition: ")],
+ "filename", &pp->content_filename);
+ }
+ else
+ {
+ try_match_header ("Content-type: ", buf, &pp->content_type);
+ try_match_header ("Content-Transfer-Encoding: ",
+ buf, &pp->content_transfer_encoding);
+ }
+ (*ioffptr) += newline + 1;
+ return MHD_YES;
+}
+
+
+/**
+ * We have the value until we hit the given boundary;
+ * process accordingly.
+ *
+ * @param pp post processor context
+ * @param ioffptr incremented based on the number of bytes processed
+ * @param boundary the boundary to look for
+ * @param blen strlen(boundary)
+ * @param next_state what state to go into after the
+ * boundary was found
+ * @param next_dash_state state to go into if the next
+ * boundary ends with "--"
+ * @return MHD_YES if we can continue processing,
+ * MHD_NO on error or if we do not have
+ * enough data yet
+ */
+static int
+process_value_to_boundary (struct MHD_PostProcessor *pp,
+ size_t *ioffptr,
+ const char *boundary,
+ size_t blen,
+ enum PP_State next_state,
+ enum PP_State next_dash_state)
+{
+ char *buf = (char *) &pp[1];
+ size_t newline;
+
+ /* all data in buf until the boundary
+ (\r\n--+boundary) is part of the value */
+ newline = 0;
+ while (1)
+ {
+ while ((newline + 4 < pp->buffer_pos) &&
+ (0 != memcmp ("\r\n--", &buf[newline], 4)))
+ newline++;
+ if (newline + pp->blen + 4 <= pp->buffer_pos)
+ {
+ /* can check boundary */
+ if (0 != memcmp (&buf[newline + 4], boundary, pp->blen))
+ {
+ /* no boundary, "\r\n--" is part of content, skip */
+ newline += 4;
+ continue;
+ }
+ else
+ {
+ /* boundary found, process until newline then
+ skip boundary and go back to init */
+ pp->skip_rn = RN_Dash;
+ pp->state = next_state;
+ pp->dash_state = next_dash_state;
+ (*ioffptr) += pp->blen + 4; /* skip boundary as well */
+ buf[newline] = '\0';
+ break;
+ }
+ }
+ else
+ {
+ /* cannot check for boundary, process content that
+ we have and check again later; except, if we have
+ no content, abort (out of memory) */
+ if ((0 == newline) && (pp->buffer_pos == pp->buffer_size))
+ {
+ pp->state = PP_Error;
+ return MHD_NO;
+ }
+ break;
+ }
+ }
+ /* newline is either at beginning of boundary or
+ at least at the last character that we are sure
+ is not part of the boundary */
+ if ( ( (MHD_YES == pp->must_ikvi) ||
+ (0 != newline) ) &&
+ (MHD_NO == pp->ikvi (pp->cls,
+ MHD_POSTDATA_KIND,
+ pp->content_name,
+ pp->content_filename,
+ pp->content_type,
+ pp->content_transfer_encoding,
+ buf, pp->value_offset, newline)) )
+ {
+ pp->state = PP_Error;
+ return MHD_NO;
+ }
+ pp->must_ikvi = MHD_NO;
+ pp->value_offset += newline;
+ (*ioffptr) += newline;
+ return MHD_YES;
+}
+
+
+/**
+ *
+ * @param pp post processor context
+ */
+static void
+free_unmarked (struct MHD_PostProcessor *pp)
+{
+ if ((NULL != pp->content_name) && (0 == (pp->have & NE_content_name)))
+ {
+ free (pp->content_name);
+ pp->content_name = NULL;
+ }
+ if ((NULL != pp->content_type) && (0 == (pp->have & NE_content_type)))
+ {
+ free (pp->content_type);
+ pp->content_type = NULL;
+ }
+ if ((NULL != pp->content_filename) &&
+ (0 == (pp->have & NE_content_filename)))
+ {
+ free (pp->content_filename);
+ pp->content_filename = NULL;
+ }
+ if ((NULL != pp->content_transfer_encoding) &&
+ (0 == (pp->have & NE_content_transfer_encoding)))
+ {
+ free (pp->content_transfer_encoding);
+ pp->content_transfer_encoding = NULL;
+ }
+}
+
+
+/**
+ * Decode multipart POST data.
+ *
+ * @param pp post processor context
+ * @param post_data data to decode
+ * @param post_data_len number of bytes in 'post_data'
+ * @return MHD_NO on error,
+ */
+static int
+post_process_multipart (struct MHD_PostProcessor *pp,
+ const char *post_data,
+ size_t post_data_len)
+{
+ char *buf;
+ size_t max;
+ size_t ioff;
+ size_t poff;
+ int state_changed;
+
+ buf = (char *) &pp[1];
+ ioff = 0;
+ poff = 0;
+ state_changed = 1;
+ while ((poff < post_data_len) ||
+ ((pp->buffer_pos > 0) && (state_changed != 0)))
+ {
+ /* first, move as much input data
+ as possible to our internal buffer */
+ max = pp->buffer_size - pp->buffer_pos;
+ if (max > post_data_len - poff)
+ max = post_data_len - poff;
+ memcpy (&buf[pp->buffer_pos], &post_data[poff], max);
+ poff += max;
+ pp->buffer_pos += max;
+ if ((max == 0) && (state_changed == 0) && (poff < post_data_len))
+ {
+ pp->state = PP_Error;
+ return MHD_NO; /* out of memory */
+ }
+ state_changed = 0;
+
+ /* first state machine for '\r'-'\n' and '--' handling */
+ switch (pp->skip_rn)
+ {
+ case RN_Inactive:
+ break;
+ case RN_OptN:
+ if (buf[0] == '\n')
+ {
+ ioff++;
+ pp->skip_rn = RN_Inactive;
+ goto AGAIN;
+ }
+ /* fall-through! */
+ case RN_Dash:
+ if (buf[0] == '-')
+ {
+ ioff++;
+ pp->skip_rn = RN_Dash2;
+ goto AGAIN;
+ }
+ pp->skip_rn = RN_Full;
+ /* fall-through! */
+ case RN_Full:
+ if (buf[0] == '\r')
+ {
+ if ((pp->buffer_pos > 1) && (buf[1] == '\n'))
+ {
+ pp->skip_rn = RN_Inactive;
+ ioff += 2;
+ }
+ else
+ {
+ pp->skip_rn = RN_OptN;
+ ioff++;
+ }
+ goto AGAIN;
+ }
+ if (buf[0] == '\n')
+ {
+ ioff++;
+ pp->skip_rn = RN_Inactive;
+ goto AGAIN;
+ }
+ pp->skip_rn = RN_Inactive;
+ pp->state = PP_Error;
+ return MHD_NO; /* no '\r\n' */
+ case RN_Dash2:
+ if (buf[0] == '-')
+ {
+ ioff++;
+ pp->skip_rn = RN_Full;
+ pp->state = pp->dash_state;
+ goto AGAIN;
+ }
+ pp->state = PP_Error;
+ break;
+ }
+
+ /* main state engine */
+ switch (pp->state)
+ {
+ case PP_Error:
+ return MHD_NO;
+ case PP_Done:
+ /* did not expect to receive more data */
+ pp->state = PP_Error;
+ return MHD_NO;
+ case PP_Init:
+ /**
+ * Per RFC2046 5.1.1 NOTE TO IMPLEMENTORS, consume anything
+ * prior to the first multipart boundary:
+ *
+ * > There appears to be room for additional information prior
+ * > to the first boundary delimiter line and following the
+ * > final boundary delimiter line. These areas should
+ * > generally be left blank, and implementations must ignore
+ * > anything that appears before the first boundary delimiter
+ * > line or after the last one.
+ */
+ (void) find_boundary (pp,
+ pp->boundary,
+ pp->blen,
+ &ioff,
+ PP_ProcessEntryHeaders, PP_Done);
+ break;
+ case PP_NextBoundary:
+ if (MHD_NO == find_boundary (pp,
+ pp->boundary,
+ pp->blen,
+ &ioff,
+ PP_ProcessEntryHeaders, PP_Done))
+ {
+ if (pp->state == PP_Error)
+ return MHD_NO;
+ goto END;
+ }
+ break;
+ case PP_ProcessEntryHeaders:
+ pp->must_ikvi = MHD_YES;
+ if (MHD_NO ==
+ process_multipart_headers (pp, &ioff, PP_PerformCheckMultipart))
+ {
+ if (pp->state == PP_Error)
+ return MHD_NO;
+ else
+ goto END;
+ }
+ state_changed = 1;
+ break;
+ case PP_PerformCheckMultipart:
+ if ((pp->content_type != NULL) &&
+ (0 == strncasecmp (pp->content_type,
+ "multipart/mixed",
+ strlen ("multipart/mixed"))))
+ {
+ pp->nested_boundary = strstr (pp->content_type, "boundary=");
+ if (pp->nested_boundary == NULL)
+ {
+ pp->state = PP_Error;
+ return MHD_NO;
+ }
+ pp->nested_boundary =
+ strdup (&pp->nested_boundary[strlen ("boundary=")]);
+ if (pp->nested_boundary == NULL)
+ {
+ /* out of memory */
+ pp->state = PP_Error;
+ return MHD_NO;
+ }
+ /* free old content type, we will need that field
+ for the content type of the nested elements */
+ free (pp->content_type);
+ pp->content_type = NULL;
+ pp->nlen = strlen (pp->nested_boundary);
+ pp->state = PP_Nested_Init;
+ state_changed = 1;
+ break;
+ }
+ pp->state = PP_ProcessValueToBoundary;
+ pp->value_offset = 0;
+ state_changed = 1;
+ break;
+ case PP_ProcessValueToBoundary:
+ if (MHD_NO == process_value_to_boundary (pp,
+ &ioff,
+ pp->boundary,
+ pp->blen,
+ PP_PerformCleanup,
+ PP_Done))
+ {
+ if (pp->state == PP_Error)
+ return MHD_NO;
+ break;
+ }
+ break;
+ case PP_PerformCleanup:
+ /* clean up state of one multipart form-data element! */
+ pp->have = NE_none;
+ free_unmarked (pp);
+ if (pp->nested_boundary != NULL)
+ {
+ free (pp->nested_boundary);
+ pp->nested_boundary = NULL;
+ }
+ pp->state = PP_ProcessEntryHeaders;
+ state_changed = 1;
+ break;
+ case PP_Nested_Init:
+ if (pp->nested_boundary == NULL)
+ {
+ pp->state = PP_Error;
+ return MHD_NO;
+ }
+ if (MHD_NO == find_boundary (pp,
+ pp->nested_boundary,
+ pp->nlen,
+ &ioff,
+ PP_Nested_PerformMarking,
+ PP_NextBoundary /* or PP_Error? */ ))
+ {
+ if (pp->state == PP_Error)
+ return MHD_NO;
+ goto END;
+ }
+ break;
+ case PP_Nested_PerformMarking:
+ /* remember what headers were given
+ globally */
+ pp->have = NE_none;
+ if (pp->content_name != NULL)
+ pp->have |= NE_content_name;
+ if (pp->content_type != NULL)
+ pp->have |= NE_content_type;
+ if (pp->content_filename != NULL)
+ pp->have |= NE_content_filename;
+ if (pp->content_transfer_encoding != NULL)
+ pp->have |= NE_content_transfer_encoding;
+ pp->state = PP_Nested_ProcessEntryHeaders;
+ state_changed = 1;
+ break;
+ case PP_Nested_ProcessEntryHeaders:
+ pp->value_offset = 0;
+ if (MHD_NO ==
+ process_multipart_headers (pp, &ioff,
+ PP_Nested_ProcessValueToBoundary))
+ {
+ if (pp->state == PP_Error)
+ return MHD_NO;
+ else
+ goto END;
+ }
+ state_changed = 1;
+ break;
+ case PP_Nested_ProcessValueToBoundary:
+ if (MHD_NO == process_value_to_boundary (pp,
+ &ioff,
+ pp->nested_boundary,
+ pp->nlen,
+ PP_Nested_PerformCleanup,
+ PP_NextBoundary))
+ {
+ if (pp->state == PP_Error)
+ return MHD_NO;
+ break;
+ }
+ break;
+ case PP_Nested_PerformCleanup:
+ free_unmarked (pp);
+ pp->state = PP_Nested_ProcessEntryHeaders;
+ state_changed = 1;
+ break;
+ default:
+ mhd_panic (mhd_panic_cls, __FILE__, __LINE__, NULL); /*
should never happen! */
+ }
+ AGAIN:
+ if (ioff > 0)
+ {
+ memmove (buf, &buf[ioff], pp->buffer_pos - ioff);
+ pp->buffer_pos -= ioff;
+ ioff = 0;
+ state_changed = 1;
+ }
+ }
+END:
+ if (ioff != 0)
+ {
+ memmove (buf, &buf[ioff], pp->buffer_pos - ioff);
+ pp->buffer_pos -= ioff;
+ }
+ if (poff < post_data_len)
+ {
+ pp->state = PP_Error;
+ return MHD_NO; /* serious error */
+ }
+ return MHD_YES;
+}
+
+
+/**
+ * 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, size_t post_data_len)
+{
+ if (post_data_len == 0)
+ return MHD_YES;
+ if (pp == NULL)
+ return MHD_NO;
+ if (0 == strncasecmp (MHD_HTTP_POST_ENCODING_FORM_URLENCODED, pp->encoding,
+ strlen(MHD_HTTP_POST_ENCODING_FORM_URLENCODED)))
+ return post_process_urlencoded (pp, post_data, post_data_len);
+ if (0 ==
+ strncasecmp (MHD_HTTP_POST_ENCODING_MULTIPART_FORMDATA, pp->encoding,
+ strlen (MHD_HTTP_POST_ENCODING_MULTIPART_FORMDATA)))
+ return post_process_multipart (pp, post_data, post_data_len);
+ /* this should never be reached */
+ return MHD_NO;
+}
+
+
+/**
+ * Release PostProcessor resources.
+ *
+ * @param pp post processor context to destroy
+ * @return MHD_YES if processing completed nicely,
+ * MHD_NO if there were spurious characters / formatting
+ * problems; it is common to ignore the return
+ * value of this function
+ */
+int
+MHD_destroy_post_processor (struct MHD_PostProcessor *pp)
+{
+ int ret;
+
+ if (NULL == pp)
+ return MHD_YES;
+ if (PP_ProcessValue == pp->state)
+ {
+ /* key without terminated value left at the end of the
+ buffer; fake receiving a termination character to
+ ensure it is also processed */
+ post_process_urlencoded (pp, "\n", 1);
+ }
+ /* These internal strings need cleaning up since
+ the post-processing may have been interrupted
+ at any stage */
+ if ((pp->xbuf_pos > 0) ||
+ ( (pp->state != PP_Done) &&
+ (pp->state != PP_ExpectNewLine)))
+ ret = MHD_NO;
+ else
+ ret = MHD_YES;
+ pp->have = NE_none;
+ free_unmarked (pp);
+ if (pp->nested_boundary != NULL)
+ free (pp->nested_boundary);
+ free (pp);
+ return ret;
+}
+
+/* end of postprocessor.c */
Deleted: libmicrohttpd/src/microhttpd/response.c
===================================================================
--- libmicrohttpd/src/daemon/response.c 2013-05-05 12:01:06 UTC (rev 27023)
+++ libmicrohttpd/src/microhttpd/response.c 2013-05-05 18:07:33 UTC (rev
27025)
@@ -1,456 +0,0 @@
-/*
- This file is part of libmicrohttpd
- (C) 2007, 2009, 2010 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
- version 2.1 of the License, or (at your option) any later version.
-
- This library is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- Lesser General Public License for more details.
-
- You should have received a copy of the GNU Lesser General Public
- License along with this library; if not, write to the Free Software
- Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
USA
-*/
-
-/**
- * @file response.c
- * @brief Methods for managing response objects
- * @author Daniel Pittman
- * @author Christian Grothoff
- */
-
-#include "internal.h"
-#include "response.h"
-
-
-/**
- * Add a header or footer line to the response.
- *
- * @param response response to add a header to
- * @param kind header or footer
- * @param header the header to add
- * @param content value to add
- * @return MHD_NO on error (i.e. invalid header or content format).
- */
-static int
-add_response_entry (struct MHD_Response *response,
- enum MHD_ValueKind kind,
- const char *header,
- const char *content)
-{
- struct MHD_HTTP_Header *hdr;
-
- if ( (NULL == response) ||
- (NULL == header) ||
- (NULL == content) ||
- (0 == strlen (header)) ||
- (0 == strlen (content)) ||
- (NULL != strchr (header, '\t')) ||
- (NULL != strchr (header, '\r')) ||
- (NULL != strchr (header, '\n')) ||
- (NULL != strchr (content, '\t')) ||
- (NULL != strchr (content, '\r')) ||
- (NULL != strchr (content, '\n')) )
- return MHD_NO;
- if (NULL == (hdr = malloc (sizeof (struct MHD_HTTP_Header))))
- return MHD_NO;
- if (NULL == (hdr->header = strdup (header)))
- {
- free (hdr);
- return MHD_NO;
- }
- if (NULL == (hdr->value = strdup (content)))
- {
- free (hdr->header);
- free (hdr);
- return MHD_NO;
- }
- hdr->kind = kind;
- hdr->next = response->first_header;
- response->first_header = hdr;
- return MHD_YES;
-}
-
-
-/**
- * Add a header line to the response.
- *
- * @param response response to add a header to
- * @param header the header to add
- * @param content value to add
- * @return MHD_NO on error (i.e. invalid header or content format).
- */
-int
-MHD_add_response_header (struct MHD_Response *response,
- const char *header, const char *content)
-{
- return add_response_entry (response,
- MHD_HEADER_KIND,
- header,
- content);
-}
-
-
-/**
- * Add a footer line to the response.
- *
- * @param response response to remove a header from
- * @param footer the footer to delete
- * @param content value to delete
- * @return MHD_NO on error (i.e. invalid footer or content format).
- */
-int
-MHD_add_response_footer (struct MHD_Response *response,
- const char *footer, const char *content)
-{
- return add_response_entry (response,
- MHD_FOOTER_KIND,
- footer,
- content);
-}
-
-
-/**
- * Delete a header line from the response.
- *
- * @param response response to remove a header from
- * @param header the header to delete
- * @param content value to delete
- * @return MHD_NO on error (no such header known)
- */
-int
-MHD_del_response_header (struct MHD_Response *response,
- const char *header, const char *content)
-{
- struct MHD_HTTP_Header *pos;
- struct MHD_HTTP_Header *prev;
-
- if ( (NULL == header) || (NULL == content) )
- return MHD_NO;
- prev = NULL;
- pos = response->first_header;
- while (pos != NULL)
- {
- if ((0 == strcmp (header, pos->header)) &&
- (0 == strcmp (content, pos->value)))
- {
- free (pos->header);
- free (pos->value);
- if (NULL == prev)
- response->first_header = pos->next;
- else
- prev->next = pos->next;
- free (pos);
- return MHD_YES;
- }
- prev = pos;
- pos = pos->next;
- }
- return MHD_NO;
-}
-
-
-/**
- * Get all of the headers added to a response.
- *
- * @param iterator callback to call on each header;
- * maybe NULL (then just count headers)
- * @param iterator_cls extra argument to iterator
- * @return number of entries iterated over
- */
-int
-MHD_get_response_headers (struct MHD_Response *response,
- MHD_KeyValueIterator iterator, void *iterator_cls)
-{
- struct MHD_HTTP_Header *pos;
- int numHeaders = 0;
-
- for (pos = response->first_header; NULL != pos; pos = pos->next)
- {
- numHeaders++;
- if ((NULL != iterator) &&
- (MHD_YES != iterator (iterator_cls,
- pos->kind, pos->header, pos->value)))
- break;
- }
- return numHeaders;
-}
-
-
-/**
- * Get a particular header from the response.
- *
- * @param key which header to get
- * @return NULL if header does not exist
- */
-const char *
-MHD_get_response_header (struct MHD_Response *response, const char *key)
-{
- struct MHD_HTTP_Header *pos;
-
- if (NULL == key)
- return NULL;
- for (pos = response->first_header; NULL != pos; pos = pos->next)
- if (0 == strcmp (key, pos->header))
- return pos->value;
- return NULL;
-}
-
-
-/**
- * Create a response object. The response object can be extended with
- * header information and then be used any number of times.
- *
- * @param size size of the data portion of the response, MHD_SIZE_UNKNOWN for
unknown
- * @param block_size preferred block size for querying crc (advisory only,
- * MHD may still call crc using smaller chunks); this
- * is essentially the buffer size used for IO, clients
- * should pick a value that is appropriate for IO and
- * memory performance requirements
- * @param crc callback to use to obtain response data
- * @param crc_cls extra argument to crc
- * @param crfc callback to call to free crc_cls resources
- * @return NULL on error (i.e. invalid arguments, out of memory)
- */
-struct MHD_Response *
-MHD_create_response_from_callback (uint64_t size,
- size_t block_size,
- MHD_ContentReaderCallback crc,
- void *crc_cls,
- MHD_ContentReaderFreeCallback crfc)
-{
- struct MHD_Response *response;
-
- if ((NULL == crc) || (0 == block_size))
- return NULL;
- if (NULL == (response = malloc (sizeof (struct MHD_Response) + block_size)))
- return NULL;
- memset (response, 0, sizeof (struct MHD_Response));
- response->fd = -1;
- response->data = (void *) &response[1];
- response->data_buffer_size = block_size;
- if (0 != pthread_mutex_init (&response->mutex, NULL))
- {
- free (response);
- return NULL;
- }
- response->crc = crc;
- response->crfc = crfc;
- response->crc_cls = crc_cls;
- response->reference_count = 1;
- response->total_size = size;
- return response;
-}
-
-
-/**
- * Given a file descriptor, read data from the file
- * to generate the response.
- *
- * @param cls pointer to the response
- * @param pos offset in the file to access
- * @param buf where to write the data
- * @param max number of bytes to write at most
- * @return number of bytes written
- */
-static ssize_t
-file_reader (void *cls, uint64_t pos, char *buf, size_t max)
-{
- struct MHD_Response *response = cls;
- ssize_t n;
-
- (void) lseek (response->fd, pos + response->fd_off, SEEK_SET);
- n = read (response->fd, buf, max);
- if (0 == n)
- return MHD_CONTENT_READER_END_OF_STREAM;
- if (n < 0)
- return MHD_CONTENT_READER_END_WITH_ERROR;
- return n;
-}
-
-
-/**
- * Destroy file reader context. Closes the file
- * descriptor.
- *
- * @param cls pointer to file descriptor
- */
-static void
-free_callback (void *cls)
-{
- struct MHD_Response *response = cls;
-
- (void) close (response->fd);
- response->fd = -1;
-}
-
-
-/**
- * Create a response object. The response object can be extended with
- * header information and then be used any number of times.
- *
- * @param size size of the data portion of the response
- * @param fd file descriptor referring to a file on disk with the data
- * @param off offset to start reading from in the file
- * @return NULL on error (i.e. invalid arguments, out of memory)
- */
-struct MHD_Response *MHD_create_response_from_fd_at_offset (size_t size,
- int fd,
- off_t offset)
-{
- struct MHD_Response *response;
-
- response = MHD_create_response_from_callback (size,
- 4 * 1024,
- &file_reader,
- NULL,
- &free_callback);
- if (NULL == response)
- return NULL;
- response->fd = fd;
- response->fd_off = offset;
- response->crc_cls = response;
- return response;
-}
-
-
-/**
- * Create a response object. The response object can be extended with
- * header information and then be used any number of times.
- *
- * @param size size of the data portion of the response
- * @param fd file descriptor referring to a file on disk with the data
- * @return NULL on error (i.e. invalid arguments, out of memory)
- */
-struct MHD_Response *MHD_create_response_from_fd (size_t size,
- int fd)
-{
- return MHD_create_response_from_fd_at_offset (size, fd, 0);
-}
-
-
-/**
- * Create a response object. The response object can be extended with
- * header information and then be used any number of times.
- *
- * @param size size of the data portion of the response
- * @param data the data itself
- * @param must_free libmicrohttpd should free data when done
- * @param must_copy libmicrohttpd must make a copy of data
- * right away, the data maybe released anytime after
- * this call returns
- * @return NULL on error (i.e. invalid arguments, out of memory)
- * @deprecated use MHD_create_response_from_buffer instead
- */
-struct MHD_Response *
-MHD_create_response_from_data (size_t size,
- void *data, int must_free, int must_copy)
-{
- struct MHD_Response *response;
- void *tmp;
-
- if ((NULL == data) && (size > 0))
- return NULL;
- if (NULL == (response = malloc (sizeof (struct MHD_Response))))
- return NULL;
- memset (response, 0, sizeof (struct MHD_Response));
- response->fd = -1;
- if (0 != pthread_mutex_init (&response->mutex, NULL))
- {
- free (response);
- return NULL;
- }
- if ((must_copy) && (size > 0))
- {
- if (NULL == (tmp = malloc (size)))
- {
- pthread_mutex_destroy (&response->mutex);
- free (response);
- return NULL;
- }
- memcpy (tmp, data, size);
- must_free = MHD_YES;
- data = tmp;
- }
- response->crc = NULL;
- response->crfc = must_free ? &free : NULL;
- response->crc_cls = must_free ? data : NULL;
- response->reference_count = 1;
- response->total_size = size;
- response->data = data;
- response->data_size = size;
- return response;
-}
-
-
-/**
- * Create a response object. The response object can be extended with
- * header information and then be used any number of times.
- *
- * @param size size of the data portion of the response
- * @param buffer size bytes containing the response's data portion
- * @param mode flags for buffer management
- * @return NULL on error (i.e. invalid arguments, out of memory)
- */
-struct MHD_Response *
-MHD_create_response_from_buffer (size_t size,
- void *buffer,
- enum MHD_ResponseMemoryMode mode)
-{
- return MHD_create_response_from_data (size,
- buffer,
- mode == MHD_RESPMEM_MUST_FREE,
- mode == MHD_RESPMEM_MUST_COPY);
-}
-
-
-/**
- * Destroy a response object and associated resources. Note that
- * libmicrohttpd may keep some of the resources around if the response
- * is still in the queue for some clients, so the memory may not
- * necessarily be freed immediatley.
- */
-void
-MHD_destroy_response (struct MHD_Response *response)
-{
- struct MHD_HTTP_Header *pos;
-
- if (NULL == response)
- return;
- pthread_mutex_lock (&response->mutex);
- if (0 != --(response->reference_count))
- {
- pthread_mutex_unlock (&response->mutex);
- return;
- }
- pthread_mutex_unlock (&response->mutex);
- pthread_mutex_destroy (&response->mutex);
- if (response->crfc != NULL)
- response->crfc (response->crc_cls);
- while (NULL != response->first_header)
- {
- pos = response->first_header;
- response->first_header = pos->next;
- free (pos->header);
- free (pos->value);
- free (pos);
- }
- free (response);
-}
-
-
-void
-MHD_increment_response_rc (struct MHD_Response *response)
-{
- pthread_mutex_lock (&response->mutex);
- (response->reference_count)++;
- pthread_mutex_unlock (&response->mutex);
-}
-
-
-/* end of response.c */
Copied: libmicrohttpd/src/microhttpd/response.c (from rev 27024,
libmicrohttpd/src/daemon/response.c)
===================================================================
--- libmicrohttpd/src/microhttpd/response.c (rev 0)
+++ libmicrohttpd/src/microhttpd/response.c 2013-05-05 18:07:33 UTC (rev
27025)
@@ -0,0 +1,459 @@
+/*
+ This file is part of libmicrohttpd
+ (C) 2007, 2009, 2010 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
+ version 2.1 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
USA
+*/
+
+/**
+ * @file response.c
+ * @brief Methods for managing response objects
+ * @author Daniel Pittman
+ * @author Christian Grothoff
+ */
+
+#include "internal.h"
+#include "response.h"
+
+
+/**
+ * Add a header or footer line to the response.
+ *
+ * @param response response to add a header to
+ * @param kind header or footer
+ * @param header the header to add
+ * @param content value to add
+ * @return MHD_NO on error (i.e. invalid header or content format).
+ */
+static int
+add_response_entry (struct MHD_Response *response,
+ enum MHD_ValueKind kind,
+ const char *header,
+ const char *content)
+{
+ struct MHD_HTTP_Header *hdr;
+
+ if ( (NULL == response) ||
+ (NULL == header) ||
+ (NULL == content) ||
+ (0 == strlen (header)) ||
+ (0 == strlen (content)) ||
+ (NULL != strchr (header, '\t')) ||
+ (NULL != strchr (header, '\r')) ||
+ (NULL != strchr (header, '\n')) ||
+ (NULL != strchr (content, '\t')) ||
+ (NULL != strchr (content, '\r')) ||
+ (NULL != strchr (content, '\n')) )
+ return MHD_NO;
+ if (NULL == (hdr = malloc (sizeof (struct MHD_HTTP_Header))))
+ return MHD_NO;
+ if (NULL == (hdr->header = strdup (header)))
+ {
+ free (hdr);
+ return MHD_NO;
+ }
+ if (NULL == (hdr->value = strdup (content)))
+ {
+ free (hdr->header);
+ free (hdr);
+ return MHD_NO;
+ }
+ hdr->kind = kind;
+ hdr->next = response->first_header;
+ response->first_header = hdr;
+ return MHD_YES;
+}
+
+
+/**
+ * Add a header line to the response.
+ *
+ * @param response response to add a header to
+ * @param header the header to add
+ * @param content value to add
+ * @return MHD_NO on error (i.e. invalid header or content format).
+ */
+int
+MHD_add_response_header (struct MHD_Response *response,
+ const char *header, const char *content)
+{
+ return add_response_entry (response,
+ MHD_HEADER_KIND,
+ header,
+ content);
+}
+
+
+/**
+ * Add a footer line to the response.
+ *
+ * @param response response to remove a header from
+ * @param footer the footer to delete
+ * @param content value to delete
+ * @return MHD_NO on error (i.e. invalid footer or content format).
+ */
+int
+MHD_add_response_footer (struct MHD_Response *response,
+ const char *footer, const char *content)
+{
+ return add_response_entry (response,
+ MHD_FOOTER_KIND,
+ footer,
+ content);
+}
+
+
+/**
+ * Delete a header line from the response.
+ *
+ * @param response response to remove a header from
+ * @param header the header to delete
+ * @param content value to delete
+ * @return MHD_NO on error (no such header known)
+ */
+int
+MHD_del_response_header (struct MHD_Response *response,
+ const char *header, const char *content)
+{
+ struct MHD_HTTP_Header *pos;
+ struct MHD_HTTP_Header *prev;
+
+ if ( (NULL == header) || (NULL == content) )
+ return MHD_NO;
+ prev = NULL;
+ pos = response->first_header;
+ while (pos != NULL)
+ {
+ if ((0 == strcmp (header, pos->header)) &&
+ (0 == strcmp (content, pos->value)))
+ {
+ free (pos->header);
+ free (pos->value);
+ if (NULL == prev)
+ response->first_header = pos->next;
+ else
+ prev->next = pos->next;
+ free (pos);
+ return MHD_YES;
+ }
+ prev = pos;
+ pos = pos->next;
+ }
+ return MHD_NO;
+}
+
+
+/**
+ * Get all of the headers added to a response.
+ *
+ * @param response response to query
+ * @param iterator callback to call on each header;
+ * maybe NULL (then just count headers)
+ * @param iterator_cls extra argument to iterator
+ * @return number of entries iterated over
+ */
+int
+MHD_get_response_headers (struct MHD_Response *response,
+ MHD_KeyValueIterator iterator, void *iterator_cls)
+{
+ struct MHD_HTTP_Header *pos;
+ int numHeaders = 0;
+
+ for (pos = response->first_header; NULL != pos; pos = pos->next)
+ {
+ numHeaders++;
+ if ((NULL != iterator) &&
+ (MHD_YES != iterator (iterator_cls,
+ pos->kind, pos->header, pos->value)))
+ break;
+ }
+ return numHeaders;
+}
+
+
+/**
+ * Get a particular header from the response.
+ *
+ * @param response response to query
+ * @param key which header to get
+ * @return NULL if header does not exist
+ */
+const char *
+MHD_get_response_header (struct MHD_Response *response,
+ const char *key)
+{
+ struct MHD_HTTP_Header *pos;
+
+ if (NULL == key)
+ return NULL;
+ for (pos = response->first_header; NULL != pos; pos = pos->next)
+ if (0 == strcmp (key, pos->header))
+ return pos->value;
+ return NULL;
+}
+
+
+/**
+ * Create a response object. The response object can be extended with
+ * header information and then be used any number of times.
+ *
+ * @param size size of the data portion of the response, MHD_SIZE_UNKNOWN for
unknown
+ * @param block_size preferred block size for querying crc (advisory only,
+ * MHD may still call crc using smaller chunks); this
+ * is essentially the buffer size used for IO, clients
+ * should pick a value that is appropriate for IO and
+ * memory performance requirements
+ * @param crc callback to use to obtain response data
+ * @param crc_cls extra argument to crc
+ * @param crfc callback to call to free crc_cls resources
+ * @return NULL on error (i.e. invalid arguments, out of memory)
+ */
+struct MHD_Response *
+MHD_create_response_from_callback (uint64_t size,
+ size_t block_size,
+ MHD_ContentReaderCallback crc,
+ void *crc_cls,
+ MHD_ContentReaderFreeCallback crfc)
+{
+ struct MHD_Response *response;
+
+ if ((NULL == crc) || (0 == block_size))
+ return NULL;
+ if (NULL == (response = malloc (sizeof (struct MHD_Response) + block_size)))
+ return NULL;
+ memset (response, 0, sizeof (struct MHD_Response));
+ response->fd = -1;
+ response->data = (void *) &response[1];
+ response->data_buffer_size = block_size;
+ if (0 != pthread_mutex_init (&response->mutex, NULL))
+ {
+ free (response);
+ return NULL;
+ }
+ response->crc = crc;
+ response->crfc = crfc;
+ response->crc_cls = crc_cls;
+ response->reference_count = 1;
+ response->total_size = size;
+ return response;
+}
+
+
+/**
+ * Given a file descriptor, read data from the file
+ * to generate the response.
+ *
+ * @param cls pointer to the response
+ * @param pos offset in the file to access
+ * @param buf where to write the data
+ * @param max number of bytes to write at most
+ * @return number of bytes written
+ */
+static ssize_t
+file_reader (void *cls, uint64_t pos, char *buf, size_t max)
+{
+ struct MHD_Response *response = cls;
+ ssize_t n;
+
+ (void) lseek (response->fd, pos + response->fd_off, SEEK_SET);
+ n = read (response->fd, buf, max);
+ if (0 == n)
+ return MHD_CONTENT_READER_END_OF_STREAM;
+ if (n < 0)
+ return MHD_CONTENT_READER_END_WITH_ERROR;
+ return n;
+}
+
+
+/**
+ * Destroy file reader context. Closes the file
+ * descriptor.
+ *
+ * @param cls pointer to file descriptor
+ */
+static void
+free_callback (void *cls)
+{
+ struct MHD_Response *response = cls;
+
+ (void) close (response->fd);
+ response->fd = -1;
+}
+
+
+/**
+ * Create a response object. The response object can be extended with
+ * header information and then be used any number of times.
+ *
+ * @param size size of the data portion of the response
+ * @param fd file descriptor referring to a file on disk with the data
+ * @param offset offset to start reading from in the file
+ * @return NULL on error (i.e. invalid arguments, out of memory)
+ */
+struct MHD_Response *MHD_create_response_from_fd_at_offset (size_t size,
+ int fd,
+ off_t offset)
+{
+ struct MHD_Response *response;
+
+ response = MHD_create_response_from_callback (size,
+ 4 * 1024,
+ &file_reader,
+ NULL,
+ &free_callback);
+ if (NULL == response)
+ return NULL;
+ response->fd = fd;
+ response->fd_off = offset;
+ response->crc_cls = response;
+ return response;
+}
+
+
+/**
+ * Create a response object. The response object can be extended with
+ * header information and then be used any number of times.
+ *
+ * @param size size of the data portion of the response
+ * @param fd file descriptor referring to a file on disk with the data
+ * @return NULL on error (i.e. invalid arguments, out of memory)
+ */
+struct MHD_Response *MHD_create_response_from_fd (size_t size,
+ int fd)
+{
+ return MHD_create_response_from_fd_at_offset (size, fd, 0);
+}
+
+
+/**
+ * Create a response object. The response object can be extended with
+ * header information and then be used any number of times.
+ *
+ * @param size size of the data portion of the response
+ * @param data the data itself
+ * @param must_free libmicrohttpd should free data when done
+ * @param must_copy libmicrohttpd must make a copy of data
+ * right away, the data maybe released anytime after
+ * this call returns
+ * @return NULL on error (i.e. invalid arguments, out of memory)
+ * @deprecated use MHD_create_response_from_buffer instead
+ */
+struct MHD_Response *
+MHD_create_response_from_data (size_t size,
+ void *data, int must_free, int must_copy)
+{
+ struct MHD_Response *response;
+ void *tmp;
+
+ if ((NULL == data) && (size > 0))
+ return NULL;
+ if (NULL == (response = malloc (sizeof (struct MHD_Response))))
+ return NULL;
+ memset (response, 0, sizeof (struct MHD_Response));
+ response->fd = -1;
+ if (0 != pthread_mutex_init (&response->mutex, NULL))
+ {
+ free (response);
+ return NULL;
+ }
+ if ((must_copy) && (size > 0))
+ {
+ if (NULL == (tmp = malloc (size)))
+ {
+ pthread_mutex_destroy (&response->mutex);
+ free (response);
+ return NULL;
+ }
+ memcpy (tmp, data, size);
+ must_free = MHD_YES;
+ data = tmp;
+ }
+ response->crc = NULL;
+ response->crfc = must_free ? &free : NULL;
+ response->crc_cls = must_free ? data : NULL;
+ response->reference_count = 1;
+ response->total_size = size;
+ response->data = data;
+ response->data_size = size;
+ return response;
+}
+
+
+/**
+ * Create a response object. The response object can be extended with
+ * header information and then be used any number of times.
+ *
+ * @param size size of the data portion of the response
+ * @param buffer size bytes containing the response's data portion
+ * @param mode flags for buffer management
+ * @return NULL on error (i.e. invalid arguments, out of memory)
+ */
+struct MHD_Response *
+MHD_create_response_from_buffer (size_t size,
+ void *buffer,
+ enum MHD_ResponseMemoryMode mode)
+{
+ return MHD_create_response_from_data (size,
+ buffer,
+ mode == MHD_RESPMEM_MUST_FREE,
+ mode == MHD_RESPMEM_MUST_COPY);
+}
+
+
+/**
+ * Destroy a response object and associated resources. Note that
+ * libmicrohttpd may keep some of the resources around if the response
+ * is still in the queue for some clients, so the memory may not
+ * necessarily be freed immediatley.
+ */
+void
+MHD_destroy_response (struct MHD_Response *response)
+{
+ struct MHD_HTTP_Header *pos;
+
+ if (NULL == response)
+ return;
+ pthread_mutex_lock (&response->mutex);
+ if (0 != --(response->reference_count))
+ {
+ pthread_mutex_unlock (&response->mutex);
+ return;
+ }
+ pthread_mutex_unlock (&response->mutex);
+ pthread_mutex_destroy (&response->mutex);
+ if (response->crfc != NULL)
+ response->crfc (response->crc_cls);
+ while (NULL != response->first_header)
+ {
+ pos = response->first_header;
+ response->first_header = pos->next;
+ free (pos->header);
+ free (pos->value);
+ free (pos);
+ }
+ free (response);
+}
+
+
+void
+MHD_increment_response_rc (struct MHD_Response *response)
+{
+ pthread_mutex_lock (&response->mutex);
+ (response->reference_count)++;
+ pthread_mutex_unlock (&response->mutex);
+}
+
+
+/* end of response.c */
Deleted: libmicrohttpd/src/microhttpd/test_daemon.c
===================================================================
--- libmicrohttpd/src/daemon/test_daemon.c 2013-05-05 12:01:06 UTC (rev
27023)
+++ libmicrohttpd/src/microhttpd/test_daemon.c 2013-05-05 18:07:33 UTC (rev
27025)
@@ -1,168 +0,0 @@
-/*
- This file is part of libmicrohttpd
- (C) 2007 Christian Grothoff
-
- libmicrohttpd is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published
- by the Free Software Foundation; either version 2, or (at your
- option) any later version.
-
- libmicrohttpd is distributed in the hope that it will be useful, but
- WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with libmicrohttpd; see the file COPYING. If not, write to the
- Free Software Foundation, Inc., 59 Temple Place - Suite 330,
- Boston, MA 02111-1307, USA.
-*/
-
-/**
- * @file daemon_test.c
- * @brief Testcase for libmicrohttpd starts and stops
- * @author Christian Grothoff
- */
-
-#include "platform.h"
-#include "platform.h"
-#include "microhttpd.h"
-#include <stdlib.h>
-#include <string.h>
-#include <stdio.h>
-
-#ifndef WINDOWS
-#include <unistd.h>
-#endif
-
-
-static int
-testStartError ()
-{
- struct MHD_Daemon *d;
-
- d = MHD_start_daemon (MHD_USE_DEBUG, 0, NULL, NULL, NULL, NULL);
- if (d != NULL)
- return 1;
- return 0;
-}
-
-static int
-apc_nothing (void *cls, const struct sockaddr *addr, socklen_t addrlen)
-{
- return MHD_NO;
-}
-
-static int
-apc_all (void *cls, const struct sockaddr *addr, socklen_t addrlen)
-{
- return MHD_YES;
-}
-
-static int
-ahc_nothing (void *cls,
- struct MHD_Connection *connection,
- const char *url,
- const char *method,
- const char *version,
- const char *upload_data, size_t *upload_data_size,
- void **unused)
-{
- return MHD_NO;
-}
-
-static int
-testStartStop ()
-{
- struct MHD_Daemon *d;
-
- d = MHD_start_daemon (MHD_USE_SELECT_INTERNALLY | MHD_USE_DEBUG,
- 1080,
- &apc_nothing,
- NULL, &ahc_nothing, NULL, MHD_OPTION_END);
- if (d == NULL)
- return 2;
- MHD_stop_daemon (d);
- return 0;
-}
-
-static int
-testExternalRun ()
-{
- struct MHD_Daemon *d;
- fd_set rs;
- int maxfd;
- int i;
-
- d = MHD_start_daemon (MHD_USE_DEBUG,
- 1081,
- &apc_all, NULL, &ahc_nothing, NULL, MHD_OPTION_END);
-
- if (d == NULL)
- return 4;
- i = 0;
- while (i < 15)
- {
- maxfd = 0;
- FD_ZERO (&rs);
- if (MHD_YES != MHD_get_fdset (d, &rs, &rs, &rs, &maxfd))
- {
- MHD_stop_daemon (d);
- return 256;
- }
- if (MHD_run (d) == MHD_NO)
- {
- MHD_stop_daemon (d);
- return 8;
- }
- i++;
- }
- MHD_stop_daemon (d);
- return 0;
-}
-
-static int
-testThread ()
-{
- struct MHD_Daemon *d;
- d = MHD_start_daemon (MHD_USE_DEBUG | MHD_USE_SELECT_INTERNALLY,
- 1082,
- &apc_all, NULL, &ahc_nothing, NULL, MHD_OPTION_END);
-
- if (d == NULL)
- return 16;
- if (MHD_run (d) != MHD_NO)
- return 32;
- MHD_stop_daemon (d);
- return 0;
-}
-
-static int
-testMultithread ()
-{
- struct MHD_Daemon *d;
- d = MHD_start_daemon (MHD_USE_DEBUG | MHD_USE_THREAD_PER_CONNECTION,
- 1083,
- &apc_all, NULL, &ahc_nothing, NULL, MHD_OPTION_END);
-
- if (d == NULL)
- return 64;
- if (MHD_run (d) != MHD_NO)
- return 128;
- MHD_stop_daemon (d);
- return 0;
-}
-
-int
-main (int argc, char *const *argv)
-{
- int errorCount = 0;
- errorCount += testStartError ();
- errorCount += testStartStop ();
- errorCount += testExternalRun ();
- errorCount += testThread ();
- errorCount += testMultithread ();
- if (errorCount != 0)
- fprintf (stderr, "Error (code: %u)\n", errorCount);
- return errorCount != 0; /* 0 == pass */
-}
Copied: libmicrohttpd/src/microhttpd/test_daemon.c (from rev 27024,
libmicrohttpd/src/daemon/test_daemon.c)
===================================================================
--- libmicrohttpd/src/microhttpd/test_daemon.c (rev 0)
+++ libmicrohttpd/src/microhttpd/test_daemon.c 2013-05-05 18:07:33 UTC (rev
27025)
@@ -0,0 +1,168 @@
+/*
+ This file is part of libmicrohttpd
+ (C) 2007 Christian Grothoff
+
+ libmicrohttpd is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published
+ by the Free Software Foundation; either version 2, or (at your
+ option) any later version.
+
+ libmicrohttpd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with libmicrohttpd; see the file COPYING. If not, write to the
+ Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+*/
+
+/**
+ * @file test_daemon.c
+ * @brief Testcase for libmicrohttpd starts and stops
+ * @author Christian Grothoff
+ */
+
+#include "platform.h"
+#include "platform.h"
+#include "microhttpd.h"
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+
+#ifndef WINDOWS
+#include <unistd.h>
+#endif
+
+
+static int
+testStartError ()
+{
+ struct MHD_Daemon *d;
+
+ d = MHD_start_daemon (MHD_USE_DEBUG, 0, NULL, NULL, NULL, NULL);
+ if (d != NULL)
+ return 1;
+ return 0;
+}
+
+static int
+apc_nothing (void *cls, const struct sockaddr *addr, socklen_t addrlen)
+{
+ return MHD_NO;
+}
+
+static int
+apc_all (void *cls, const struct sockaddr *addr, socklen_t addrlen)
+{
+ return MHD_YES;
+}
+
+static int
+ahc_nothing (void *cls,
+ struct MHD_Connection *connection,
+ const char *url,
+ const char *method,
+ const char *version,
+ const char *upload_data, size_t *upload_data_size,
+ void **unused)
+{
+ return MHD_NO;
+}
+
+static int
+testStartStop ()
+{
+ struct MHD_Daemon *d;
+
+ d = MHD_start_daemon (MHD_USE_SELECT_INTERNALLY | MHD_USE_DEBUG,
+ 1080,
+ &apc_nothing,
+ NULL, &ahc_nothing, NULL, MHD_OPTION_END);
+ if (d == NULL)
+ return 2;
+ MHD_stop_daemon (d);
+ return 0;
+}
+
+static int
+testExternalRun ()
+{
+ struct MHD_Daemon *d;
+ fd_set rs;
+ int maxfd;
+ int i;
+
+ d = MHD_start_daemon (MHD_USE_DEBUG,
+ 1081,
+ &apc_all, NULL, &ahc_nothing, NULL, MHD_OPTION_END);
+
+ if (d == NULL)
+ return 4;
+ i = 0;
+ while (i < 15)
+ {
+ maxfd = 0;
+ FD_ZERO (&rs);
+ if (MHD_YES != MHD_get_fdset (d, &rs, &rs, &rs, &maxfd))
+ {
+ MHD_stop_daemon (d);
+ return 256;
+ }
+ if (MHD_run (d) == MHD_NO)
+ {
+ MHD_stop_daemon (d);
+ return 8;
+ }
+ i++;
+ }
+ MHD_stop_daemon (d);
+ return 0;
+}
+
+static int
+testThread ()
+{
+ struct MHD_Daemon *d;
+ d = MHD_start_daemon (MHD_USE_DEBUG | MHD_USE_SELECT_INTERNALLY,
+ 1082,
+ &apc_all, NULL, &ahc_nothing, NULL, MHD_OPTION_END);
+
+ if (d == NULL)
+ return 16;
+ if (MHD_run (d) != MHD_NO)
+ return 32;
+ MHD_stop_daemon (d);
+ return 0;
+}
+
+static int
+testMultithread ()
+{
+ struct MHD_Daemon *d;
+ d = MHD_start_daemon (MHD_USE_DEBUG | MHD_USE_THREAD_PER_CONNECTION,
+ 1083,
+ &apc_all, NULL, &ahc_nothing, NULL, MHD_OPTION_END);
+
+ if (d == NULL)
+ return 64;
+ if (MHD_run (d) != MHD_NO)
+ return 128;
+ MHD_stop_daemon (d);
+ return 0;
+}
+
+int
+main (int argc, char *const *argv)
+{
+ int errorCount = 0;
+ errorCount += testStartError ();
+ errorCount += testStartStop ();
+ errorCount += testExternalRun ();
+ errorCount += testThread ();
+ errorCount += testMultithread ();
+ if (errorCount != 0)
+ fprintf (stderr, "Error (code: %u)\n", errorCount);
+ return errorCount != 0; /* 0 == pass */
+}
Deleted: libmicrohttpd/src/microhttpd/test_postprocessor.c
===================================================================
--- libmicrohttpd/src/daemon/test_postprocessor.c 2013-05-05 12:01:06 UTC
(rev 27023)
+++ libmicrohttpd/src/microhttpd/test_postprocessor.c 2013-05-05 18:07:33 UTC
(rev 27025)
@@ -1,271 +0,0 @@
-/*
- This file is part of libmicrohttpd
- (C) 2007 Christian Grothoff
-
- libmicrohttpd is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published
- by the Free Software Foundation; either version 2, or (at your
- option) any later version.
-
- libmicrohttpd is distributed in the hope that it will be useful, but
- WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with libmicrohttpd; see the file COPYING. If not, write to the
- Free Software Foundation, Inc., 59 Temple Place - Suite 330,
- Boston, MA 02111-1307, USA.
-*/
-
-/**
- * @file postprocessor_test.c
- * @brief Testcase for postprocessor
- * @author Christian Grothoff
- */
-
-#include "platform.h"
-#include "microhttpd.h"
-#include "internal.h"
-#include <stdlib.h>
-#include <string.h>
-#include <stdio.h>
-
-#ifndef WINDOWS
-#include <unistd.h>
-#endif
-
-/**
- * Array of values that the value checker "wants".
- * Each series of checks should be terminated by
- * five NULL-entries.
- */
-const char *want[] = {
-#define URL_DATA "abc=def&x=5"
-#define URL_START 0
- "abc", NULL, NULL, NULL, "def",
- "x", NULL, NULL, NULL, "5",
-#define URL_END (URL_START + 10)
- NULL, NULL, NULL, NULL, NULL,
-#define FORM_DATA "--AaB03x\r\ncontent-disposition: form-data;
name=\"field1\"\r\n\r\nJoe Blow\r\n--AaB03x\r\ncontent-disposition: form-data;
name=\"pics\"; filename=\"file1.txt\"\r\nContent-Type:
text/plain\r\nContent-Transfer-Encoding:
binary\r\n\r\nfiledata\r\n--AaB03x--\r\n"
-#define FORM_START (URL_END + 5)
- "field1", NULL, NULL, NULL, "Joe Blow",
- "pics", "file1.txt", "text/plain", "binary", "filedata",
-#define FORM_END (FORM_START + 10)
- NULL, NULL, NULL, NULL, NULL,
-#define FORM_NESTED_DATA "--AaB03x\r\ncontent-disposition: form-data;
name=\"field1\"\r\n\r\nJane Blow\r\n--AaB03x\r\ncontent-disposition: form-data;
name=\"pics\"\r\nContent-type: multipart/mixed,
boundary=BbC04y\r\n\r\n--BbC04y\r\nContent-disposition: attachment;
filename=\"file1.txt\"\r\nContent-Type:
text/plain\r\n\r\nfiledata1\r\n--BbC04y\r\nContent-disposition: attachment;
filename=\"file2.gif\"\r\nContent-type: image/gif\r\nContent-Transfer-Encoding:
binary\r\n\r\nfiledata2\r\n--BbC04y--\r\n--AaB03x--"
-#define FORM_NESTED_START (FORM_END + 5)
- "field1", NULL, NULL, NULL, "Jane Blow",
- "pics", "file1.txt", "text/plain", NULL, "filedata1",
- "pics", "file2.gif", "image/gif", "binary", "filedata2",
-#define FORM_NESTED_END (FORM_NESTED_START + 15)
- NULL, NULL, NULL, NULL, NULL,
-#define URL_EMPTY_VALUE_DATA "key1=value1&key2=&key3="
-#define URL_EMPTY_VALUE_START (FORM_NESTED_END + 5)
- "key1", NULL, NULL, NULL, "value1",
- "key2", NULL, NULL, NULL, "",
- "key3", NULL, NULL, NULL, "",
-#define URL_EMPTY_VALUE_END (URL_EMPTY_VALUE_START + 15)
- NULL, NULL, NULL, NULL, NULL
-};
-
-static int
-mismatch (const char *a, const char *b)
-{
- if (a == b)
- return 0;
- if ((a == NULL) || (b == NULL))
- return 1;
- return 0 != strcmp (a, b);
-}
-
-static int
-value_checker (void *cls,
- enum MHD_ValueKind kind,
- const char *key,
- const char *filename,
- const char *content_type,
- const char *transfer_encoding,
- const char *data, uint64_t off, size_t size)
-{
- int *want_off = cls;
- int idx = *want_off;
-
-#if 0
- fprintf (stderr,
- "VC: `%s' `%s' `%s' `%s' `%.*s'\n",
- key, filename, content_type, transfer_encoding, size, data);
-#endif
- if ( (0 != off) && (0 == size) )
- return MHD_YES;
- if ((idx < 0) ||
- (want[idx] == NULL) ||
- (0 != strcmp (key, want[idx])) ||
- (mismatch (filename, want[idx + 1])) ||
- (mismatch (content_type, want[idx + 2])) ||
- (mismatch (transfer_encoding, want[idx + 3])) ||
- (0 != memcmp (data, &want[idx + 4][off], size)))
- {
- *want_off = -1;
- return MHD_NO;
- }
- if (off + size == strlen (want[idx + 4]))
- *want_off = idx + 5;
- return MHD_YES;
-
-}
-
-
-static int
-test_urlencoding ()
-{
- struct MHD_Connection connection;
- struct MHD_HTTP_Header header;
- struct MHD_PostProcessor *pp;
- unsigned int want_off = URL_START;
- int i;
- int delta;
- size_t size;
-
- memset (&connection, 0, sizeof (struct MHD_Connection));
- memset (&header, 0, sizeof (struct MHD_HTTP_Header));
- connection.headers_received = &header;
- header.header = MHD_HTTP_HEADER_CONTENT_TYPE;
- header.value = MHD_HTTP_POST_ENCODING_FORM_URLENCODED;
- header.kind = MHD_HEADER_KIND;
- pp = MHD_create_post_processor (&connection,
- 1024, &value_checker, &want_off);
- i = 0;
- size = strlen (URL_DATA);
- while (i < size)
- {
- delta = 1 + RANDOM () % (size - i);
- MHD_post_process (pp, &URL_DATA[i], delta);
- i += delta;
- }
- MHD_destroy_post_processor (pp);
- if (want_off != URL_END)
- return 1;
- return 0;
-}
-
-
-static int
-test_multipart ()
-{
- struct MHD_Connection connection;
- struct MHD_HTTP_Header header;
- struct MHD_PostProcessor *pp;
- unsigned int want_off = FORM_START;
- int i;
- int delta;
- size_t size;
-
- memset (&connection, 0, sizeof (struct MHD_Connection));
- memset (&header, 0, sizeof (struct MHD_HTTP_Header));
- connection.headers_received = &header;
- header.header = MHD_HTTP_HEADER_CONTENT_TYPE;
- header.value =
- MHD_HTTP_POST_ENCODING_MULTIPART_FORMDATA ", boundary=AaB03x";
- header.kind = MHD_HEADER_KIND;
- pp = MHD_create_post_processor (&connection,
- 1024, &value_checker, &want_off);
- i = 0;
- size = strlen (FORM_DATA);
- while (i < size)
- {
- delta = 1 + RANDOM () % (size - i);
- MHD_post_process (pp, &FORM_DATA[i], delta);
- i += delta;
- }
- MHD_destroy_post_processor (pp);
- if (want_off != FORM_END)
- return 2;
- return 0;
-}
-
-
-static int
-test_nested_multipart ()
-{
- struct MHD_Connection connection;
- struct MHD_HTTP_Header header;
- struct MHD_PostProcessor *pp;
- unsigned int want_off = FORM_NESTED_START;
- int i;
- int delta;
- size_t size;
-
- memset (&connection, 0, sizeof (struct MHD_Connection));
- memset (&header, 0, sizeof (struct MHD_HTTP_Header));
- connection.headers_received = &header;
- header.header = MHD_HTTP_HEADER_CONTENT_TYPE;
- header.value =
- MHD_HTTP_POST_ENCODING_MULTIPART_FORMDATA ", boundary=AaB03x";
- header.kind = MHD_HEADER_KIND;
- pp = MHD_create_post_processor (&connection,
- 1024, &value_checker, &want_off);
- i = 0;
- size = strlen (FORM_NESTED_DATA);
- while (i < size)
- {
- delta = 1 + RANDOM () % (size - i);
- MHD_post_process (pp, &FORM_NESTED_DATA[i], delta);
- i += delta;
- }
- MHD_destroy_post_processor (pp);
- if (want_off != FORM_NESTED_END)
- return 4;
- return 0;
-}
-
-
-static int
-test_empty_value ()
-{
- struct MHD_Connection connection;
- struct MHD_HTTP_Header header;
- struct MHD_PostProcessor *pp;
- unsigned int want_off = URL_EMPTY_VALUE_START;
- int i;
- int delta;
- size_t size;
-
- memset (&connection, 0, sizeof (struct MHD_Connection));
- memset (&header, 0, sizeof (struct MHD_HTTP_Header));
- connection.headers_received = &header;
- header.header = MHD_HTTP_HEADER_CONTENT_TYPE;
- header.value = MHD_HTTP_POST_ENCODING_FORM_URLENCODED;
- header.kind = MHD_HEADER_KIND;
- pp = MHD_create_post_processor (&connection,
- 1024, &value_checker, &want_off);
- i = 0;
- size = strlen (URL_EMPTY_VALUE_DATA);
- while (i < size)
- {
- delta = 1 + RANDOM () % (size - i);
- MHD_post_process (pp, &URL_EMPTY_VALUE_DATA[i], delta);
- i += delta;
- }
- MHD_destroy_post_processor (pp);
- if (want_off != URL_EMPTY_VALUE_END)
- return 8;
- return 0;
-}
-
-
-
-
-int
-main (int argc, char *const *argv)
-{
- unsigned int errorCount = 0;
-
- errorCount += test_urlencoding ();
- errorCount += test_multipart ();
- errorCount += test_nested_multipart ();
- errorCount += test_empty_value ();
- if (errorCount != 0)
- fprintf (stderr, "Error (code: %u)\n", errorCount);
- return errorCount != 0; /* 0 == pass */
-}
Copied: libmicrohttpd/src/microhttpd/test_postprocessor.c (from rev 27024,
libmicrohttpd/src/daemon/test_postprocessor.c)
===================================================================
--- libmicrohttpd/src/microhttpd/test_postprocessor.c
(rev 0)
+++ libmicrohttpd/src/microhttpd/test_postprocessor.c 2013-05-05 18:07:33 UTC
(rev 27025)
@@ -0,0 +1,271 @@
+/*
+ This file is part of libmicrohttpd
+ (C) 2007 Christian Grothoff
+
+ libmicrohttpd is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published
+ by the Free Software Foundation; either version 2, or (at your
+ option) any later version.
+
+ libmicrohttpd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with libmicrohttpd; see the file COPYING. If not, write to the
+ Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+*/
+
+/**
+ * @file test_postprocessor.c
+ * @brief Testcase for postprocessor
+ * @author Christian Grothoff
+ */
+
+#include "platform.h"
+#include "microhttpd.h"
+#include "internal.h"
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+
+#ifndef WINDOWS
+#include <unistd.h>
+#endif
+
+/**
+ * Array of values that the value checker "wants".
+ * Each series of checks should be terminated by
+ * five NULL-entries.
+ */
+const char *want[] = {
+#define URL_DATA "abc=def&x=5"
+#define URL_START 0
+ "abc", NULL, NULL, NULL, "def",
+ "x", NULL, NULL, NULL, "5",
+#define URL_END (URL_START + 10)
+ NULL, NULL, NULL, NULL, NULL,
+#define FORM_DATA "--AaB03x\r\ncontent-disposition: form-data;
name=\"field1\"\r\n\r\nJoe Blow\r\n--AaB03x\r\ncontent-disposition: form-data;
name=\"pics\"; filename=\"file1.txt\"\r\nContent-Type:
text/plain\r\nContent-Transfer-Encoding:
binary\r\n\r\nfiledata\r\n--AaB03x--\r\n"
+#define FORM_START (URL_END + 5)
+ "field1", NULL, NULL, NULL, "Joe Blow",
+ "pics", "file1.txt", "text/plain", "binary", "filedata",
+#define FORM_END (FORM_START + 10)
+ NULL, NULL, NULL, NULL, NULL,
+#define FORM_NESTED_DATA "--AaB03x\r\ncontent-disposition: form-data;
name=\"field1\"\r\n\r\nJane Blow\r\n--AaB03x\r\ncontent-disposition: form-data;
name=\"pics\"\r\nContent-type: multipart/mixed,
boundary=BbC04y\r\n\r\n--BbC04y\r\nContent-disposition: attachment;
filename=\"file1.txt\"\r\nContent-Type:
text/plain\r\n\r\nfiledata1\r\n--BbC04y\r\nContent-disposition: attachment;
filename=\"file2.gif\"\r\nContent-type: image/gif\r\nContent-Transfer-Encoding:
binary\r\n\r\nfiledata2\r\n--BbC04y--\r\n--AaB03x--"
+#define FORM_NESTED_START (FORM_END + 5)
+ "field1", NULL, NULL, NULL, "Jane Blow",
+ "pics", "file1.txt", "text/plain", NULL, "filedata1",
+ "pics", "file2.gif", "image/gif", "binary", "filedata2",
+#define FORM_NESTED_END (FORM_NESTED_START + 15)
+ NULL, NULL, NULL, NULL, NULL,
+#define URL_EMPTY_VALUE_DATA "key1=value1&key2=&key3="
+#define URL_EMPTY_VALUE_START (FORM_NESTED_END + 5)
+ "key1", NULL, NULL, NULL, "value1",
+ "key2", NULL, NULL, NULL, "",
+ "key3", NULL, NULL, NULL, "",
+#define URL_EMPTY_VALUE_END (URL_EMPTY_VALUE_START + 15)
+ NULL, NULL, NULL, NULL, NULL
+};
+
+static int
+mismatch (const char *a, const char *b)
+{
+ if (a == b)
+ return 0;
+ if ((a == NULL) || (b == NULL))
+ return 1;
+ return 0 != strcmp (a, b);
+}
+
+static int
+value_checker (void *cls,
+ enum MHD_ValueKind kind,
+ const char *key,
+ const char *filename,
+ const char *content_type,
+ const char *transfer_encoding,
+ const char *data, uint64_t off, size_t size)
+{
+ int *want_off = cls;
+ int idx = *want_off;
+
+#if 0
+ fprintf (stderr,
+ "VC: `%s' `%s' `%s' `%s' `%.*s'\n",
+ key, filename, content_type, transfer_encoding, size, data);
+#endif
+ if ( (0 != off) && (0 == size) )
+ return MHD_YES;
+ if ((idx < 0) ||
+ (want[idx] == NULL) ||
+ (0 != strcmp (key, want[idx])) ||
+ (mismatch (filename, want[idx + 1])) ||
+ (mismatch (content_type, want[idx + 2])) ||
+ (mismatch (transfer_encoding, want[idx + 3])) ||
+ (0 != memcmp (data, &want[idx + 4][off], size)))
+ {
+ *want_off = -1;
+ return MHD_NO;
+ }
+ if (off + size == strlen (want[idx + 4]))
+ *want_off = idx + 5;
+ return MHD_YES;
+
+}
+
+
+static int
+test_urlencoding ()
+{
+ struct MHD_Connection connection;
+ struct MHD_HTTP_Header header;
+ struct MHD_PostProcessor *pp;
+ unsigned int want_off = URL_START;
+ int i;
+ int delta;
+ size_t size;
+
+ memset (&connection, 0, sizeof (struct MHD_Connection));
+ memset (&header, 0, sizeof (struct MHD_HTTP_Header));
+ connection.headers_received = &header;
+ header.header = MHD_HTTP_HEADER_CONTENT_TYPE;
+ header.value = MHD_HTTP_POST_ENCODING_FORM_URLENCODED;
+ header.kind = MHD_HEADER_KIND;
+ pp = MHD_create_post_processor (&connection,
+ 1024, &value_checker, &want_off);
+ i = 0;
+ size = strlen (URL_DATA);
+ while (i < size)
+ {
+ delta = 1 + RANDOM () % (size - i);
+ MHD_post_process (pp, &URL_DATA[i], delta);
+ i += delta;
+ }
+ MHD_destroy_post_processor (pp);
+ if (want_off != URL_END)
+ return 1;
+ return 0;
+}
+
+
+static int
+test_multipart ()
+{
+ struct MHD_Connection connection;
+ struct MHD_HTTP_Header header;
+ struct MHD_PostProcessor *pp;
+ unsigned int want_off = FORM_START;
+ int i;
+ int delta;
+ size_t size;
+
+ memset (&connection, 0, sizeof (struct MHD_Connection));
+ memset (&header, 0, sizeof (struct MHD_HTTP_Header));
+ connection.headers_received = &header;
+ header.header = MHD_HTTP_HEADER_CONTENT_TYPE;
+ header.value =
+ MHD_HTTP_POST_ENCODING_MULTIPART_FORMDATA ", boundary=AaB03x";
+ header.kind = MHD_HEADER_KIND;
+ pp = MHD_create_post_processor (&connection,
+ 1024, &value_checker, &want_off);
+ i = 0;
+ size = strlen (FORM_DATA);
+ while (i < size)
+ {
+ delta = 1 + RANDOM () % (size - i);
+ MHD_post_process (pp, &FORM_DATA[i], delta);
+ i += delta;
+ }
+ MHD_destroy_post_processor (pp);
+ if (want_off != FORM_END)
+ return 2;
+ return 0;
+}
+
+
+static int
+test_nested_multipart ()
+{
+ struct MHD_Connection connection;
+ struct MHD_HTTP_Header header;
+ struct MHD_PostProcessor *pp;
+ unsigned int want_off = FORM_NESTED_START;
+ int i;
+ int delta;
+ size_t size;
+
+ memset (&connection, 0, sizeof (struct MHD_Connection));
+ memset (&header, 0, sizeof (struct MHD_HTTP_Header));
+ connection.headers_received = &header;
+ header.header = MHD_HTTP_HEADER_CONTENT_TYPE;
+ header.value =
+ MHD_HTTP_POST_ENCODING_MULTIPART_FORMDATA ", boundary=AaB03x";
+ header.kind = MHD_HEADER_KIND;
+ pp = MHD_create_post_processor (&connection,
+ 1024, &value_checker, &want_off);
+ i = 0;
+ size = strlen (FORM_NESTED_DATA);
+ while (i < size)
+ {
+ delta = 1 + RANDOM () % (size - i);
+ MHD_post_process (pp, &FORM_NESTED_DATA[i], delta);
+ i += delta;
+ }
+ MHD_destroy_post_processor (pp);
+ if (want_off != FORM_NESTED_END)
+ return 4;
+ return 0;
+}
+
+
+static int
+test_empty_value ()
+{
+ struct MHD_Connection connection;
+ struct MHD_HTTP_Header header;
+ struct MHD_PostProcessor *pp;
+ unsigned int want_off = URL_EMPTY_VALUE_START;
+ int i;
+ int delta;
+ size_t size;
+
+ memset (&connection, 0, sizeof (struct MHD_Connection));
+ memset (&header, 0, sizeof (struct MHD_HTTP_Header));
+ connection.headers_received = &header;
+ header.header = MHD_HTTP_HEADER_CONTENT_TYPE;
+ header.value = MHD_HTTP_POST_ENCODING_FORM_URLENCODED;
+ header.kind = MHD_HEADER_KIND;
+ pp = MHD_create_post_processor (&connection,
+ 1024, &value_checker, &want_off);
+ i = 0;
+ size = strlen (URL_EMPTY_VALUE_DATA);
+ while (i < size)
+ {
+ delta = 1 + RANDOM () % (size - i);
+ MHD_post_process (pp, &URL_EMPTY_VALUE_DATA[i], delta);
+ i += delta;
+ }
+ MHD_destroy_post_processor (pp);
+ if (want_off != URL_EMPTY_VALUE_END)
+ return 8;
+ return 0;
+}
+
+
+
+
+int
+main (int argc, char *const *argv)
+{
+ unsigned int errorCount = 0;
+
+ errorCount += test_urlencoding ();
+ errorCount += test_multipart ();
+ errorCount += test_nested_multipart ();
+ errorCount += test_empty_value ();
+ if (errorCount != 0)
+ fprintf (stderr, "Error (code: %u)\n", errorCount);
+ return errorCount != 0; /* 0 == pass */
+}
Deleted: libmicrohttpd/src/microhttpd/test_postprocessor_large.c
===================================================================
--- libmicrohttpd/src/daemon/test_postprocessor_large.c 2013-05-05 12:01:06 UTC
(rev 27023)
+++ libmicrohttpd/src/microhttpd/test_postprocessor_large.c 2013-05-05
18:07:33 UTC (rev 27025)
@@ -1,105 +0,0 @@
-/*
- This file is part of libmicrohttpd
- (C) 2008 Christian Grothoff
-
- libmicrohttpd is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published
- by the Free Software Foundation; either version 2, or (at your
- option) any later version.
-
- libmicrohttpd is distributed in the hope that it will be useful, but
- WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with libmicrohttpd; see the file COPYING. If not, write to the
- Free Software Foundation, Inc., 59 Temple Place - Suite 330,
- Boston, MA 02111-1307, USA.
-*/
-
-/**
- * @file postprocessor_large_test.c
- * @brief Testcase with very large input for postprocessor
- * @author Christian Grothoff
- */
-
-#include "platform.h"
-#include "microhttpd.h"
-#include "internal.h"
-
-#ifndef WINDOWS
-#include <unistd.h>
-#endif
-
-static int
-value_checker (void *cls,
- enum MHD_ValueKind kind,
- const char *key,
- const char *filename,
- const char *content_type,
- const char *transfer_encoding,
- const char *data, uint64_t off, size_t size)
-{
- unsigned int *pos = cls;
-#if 0
- fprintf (stderr,
- "VC: %llu %u `%s' `%s' `%s' `%s' `%.*s'\n",
- off, size,
- key, filename, content_type, transfer_encoding, size, data);
-#endif
- if (size == 0)
- return MHD_YES;
- *pos += size;
- return MHD_YES;
-
-}
-
-
-static int
-test_simple_large ()
-{
- struct MHD_Connection connection;
- struct MHD_HTTP_Header header;
- struct MHD_PostProcessor *pp;
- int i;
- int delta;
- size_t size;
- char data[102400];
- unsigned int pos;
-
- pos = 0;
- memset (data, 'A', sizeof (data));
- memcpy (data, "key=", 4);
- data[sizeof (data) - 1] = '\0';
- memset (&connection, 0, sizeof (struct MHD_Connection));
- memset (&header, 0, sizeof (struct MHD_HTTP_Header));
- connection.headers_received = &header;
- header.header = MHD_HTTP_HEADER_CONTENT_TYPE;
- header.value = MHD_HTTP_POST_ENCODING_FORM_URLENCODED;
- header.kind = MHD_HEADER_KIND;
- pp = MHD_create_post_processor (&connection, 1024, &value_checker, &pos);
- i = 0;
- size = strlen (data);
- while (i < size)
- {
- delta = 1 + RANDOM () % (size - i);
- MHD_post_process (pp, &data[i], delta);
- i += delta;
- }
- MHD_destroy_post_processor (pp);
- if (pos != sizeof (data) - 5) /* minus 0-termination and 'key=' */
- return 1;
- return 0;
-}
-
-int
-main (int argc, char *const *argv)
-{
- unsigned int errorCount = 0;
-
- errorCount += test_simple_large ();
- if (errorCount != 0)
- fprintf (stderr, "Error (code: %u)\n", errorCount);
- return errorCount != 0; /* 0 == pass */
-}
Copied: libmicrohttpd/src/microhttpd/test_postprocessor_large.c (from rev
27024, libmicrohttpd/src/daemon/test_postprocessor_large.c)
===================================================================
--- libmicrohttpd/src/microhttpd/test_postprocessor_large.c
(rev 0)
+++ libmicrohttpd/src/microhttpd/test_postprocessor_large.c 2013-05-05
18:07:33 UTC (rev 27025)
@@ -0,0 +1,105 @@
+/*
+ This file is part of libmicrohttpd
+ (C) 2008 Christian Grothoff
+
+ libmicrohttpd is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published
+ by the Free Software Foundation; either version 2, or (at your
+ option) any later version.
+
+ libmicrohttpd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with libmicrohttpd; see the file COPYING. If not, write to the
+ Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+*/
+
+/**
+ * @file test_postprocessor_large.c
+ * @brief Testcase with very large input for postprocessor
+ * @author Christian Grothoff
+ */
+
+#include "platform.h"
+#include "microhttpd.h"
+#include "internal.h"
+
+#ifndef WINDOWS
+#include <unistd.h>
+#endif
+
+static int
+value_checker (void *cls,
+ enum MHD_ValueKind kind,
+ const char *key,
+ const char *filename,
+ const char *content_type,
+ const char *transfer_encoding,
+ const char *data, uint64_t off, size_t size)
+{
+ unsigned int *pos = cls;
+#if 0
+ fprintf (stderr,
+ "VC: %llu %u `%s' `%s' `%s' `%s' `%.*s'\n",
+ off, size,
+ key, filename, content_type, transfer_encoding, size, data);
+#endif
+ if (size == 0)
+ return MHD_YES;
+ *pos += size;
+ return MHD_YES;
+
+}
+
+
+static int
+test_simple_large ()
+{
+ struct MHD_Connection connection;
+ struct MHD_HTTP_Header header;
+ struct MHD_PostProcessor *pp;
+ int i;
+ int delta;
+ size_t size;
+ char data[102400];
+ unsigned int pos;
+
+ pos = 0;
+ memset (data, 'A', sizeof (data));
+ memcpy (data, "key=", 4);
+ data[sizeof (data) - 1] = '\0';
+ memset (&connection, 0, sizeof (struct MHD_Connection));
+ memset (&header, 0, sizeof (struct MHD_HTTP_Header));
+ connection.headers_received = &header;
+ header.header = MHD_HTTP_HEADER_CONTENT_TYPE;
+ header.value = MHD_HTTP_POST_ENCODING_FORM_URLENCODED;
+ header.kind = MHD_HEADER_KIND;
+ pp = MHD_create_post_processor (&connection, 1024, &value_checker, &pos);
+ i = 0;
+ size = strlen (data);
+ while (i < size)
+ {
+ delta = 1 + RANDOM () % (size - i);
+ MHD_post_process (pp, &data[i], delta);
+ i += delta;
+ }
+ MHD_destroy_post_processor (pp);
+ if (pos != sizeof (data) - 5) /* minus 0-termination and 'key=' */
+ return 1;
+ return 0;
+}
+
+int
+main (int argc, char *const *argv)
+{
+ unsigned int errorCount = 0;
+
+ errorCount += test_simple_large ();
+ if (errorCount != 0)
+ fprintf (stderr, "Error (code: %u)\n", errorCount);
+ return errorCount != 0; /* 0 == pass */
+}
[Prev in Thread] |
Current Thread |
[Next in Thread] |
- [GNUnet-SVN] r27025 - in libmicrohttpd: . src src/microhttpd,
gnunet <=