libmicrohttpd

HTTP/1.x server C library (MHD 1.x, stable)
Log | Files | Refs | Submodules | README | LICENSE

commit 312713c3634aa22d538c09d0146b5ac1d62c206b
parent 83a86be948420f7bd902ceee9d661fc208eec1cd
Author: Christian Grothoff <christian@grothoff.org>
Date:   Mon,  8 Apr 2019 15:12:45 +0200

merge

Diffstat:
MChangeLog | 19+++++++++++++++++++
Mconfigure.ac | 6+++---
Mdoc/examples/logging.c | 17++++++++++++-----
Mdoc/libmicrohttpd.texi | 13++++++++++++-
Mpo/libmicrohttpd.pot | 266++++++++++++++++++++++++++++++++++++++++---------------------------------------
Msrc/examples/Makefile.am | 8+++++++-
Asrc/examples/http_chunked_compression.c | 199+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/include/microhttpd.h | 56++++++++++++++++++++++++++++++++++++++++++++++++--------
Msrc/microhttpd/connection.c | 108+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++------------------
Msrc/microhttpd/digestauth.c | 23+++++++++++++++--------
Msrc/microhttpd/internal.c | 19++++++++++++-------
Msrc/microhttpd/internal.h | 9++++++++-
Msrc/microhttpd/response.c | 3++-
Msrc/testcurl/test_get.c | 94+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--------------
Msrc/testcurl/test_process_headers.c | 8+++++++-
Msrc/testcurl/test_urlparse.c | 9+++++++--
16 files changed, 648 insertions(+), 209 deletions(-)

diff --git a/ChangeLog b/ChangeLog @@ -2,6 +2,25 @@ Mon 08 Apr 2019 03:06:05 PM CEST Fix close() checks as suggested by MK on the mailinglist (#3926). -MK/CG +Wed 20 Mar 2019 10:20:24 AM CET + Adding additional "value_length" argument to MHD_KeyValueIterator + callback to support binary zeros in values. This is done in a + backwards-compatible way, but may require adding a cast to existing + code to avoid a compiler warning. -CG + +Sun Feb 10 21:00:37 BRT 2019 + Added example for how to compress a chunked HTTP response. -SC + +Sun 10 Feb 2019 05:03:44 PM CET + Releasing libmicrohttpd 0.9.63. -CG + +Sat 09 Feb 2019 01:51:02 PM CET + Extended test_get to test URI logging and query string parsing + to avoid regression fixed in previous patch in the future. -CG + +Thu Feb 7 16:16:12 CET 2019 + Preliminary patch for the raw query string issue, to be tested. -CG + Tue Jan 8 02:57:21 BRT 2019 Added minimal example for how to compress HTTP response. -SC diff --git a/configure.ac b/configure.ac @@ -22,15 +22,15 @@ # AC_PREREQ([2.64]) LT_PREREQ([2.4.0]) -AC_INIT([GNU Libmicrohttpd],[0.9.62],[libmicrohttpd@gnu.org]) +AC_INIT([GNU Libmicrohttpd],[0.9.63],[libmicrohttpd@gnu.org]) AC_CONFIG_AUX_DIR([build-aux]) AM_INIT_AUTOMAKE([silent-rules] [subdir-objects]) AC_CONFIG_HEADERS([MHD_config.h]) AC_CONFIG_MACRO_DIR([m4]) -LIB_VERSION_CURRENT=61 +LIB_VERSION_CURRENT=62 LIB_VERSION_REVISION=0 -LIB_VERSION_AGE=49 +LIB_VERSION_AGE=50 AC_SUBST(LIB_VERSION_CURRENT) AC_SUBST(LIB_VERSION_REVISION) AC_SUBST(LIB_VERSION_AGE) diff --git a/doc/examples/logging.c b/doc/examples/logging.c @@ -15,11 +15,16 @@ static int -print_out_key (void *cls, enum MHD_ValueKind kind, const char *key, - const char *value) +print_out_key (void *cls, + enum MHD_ValueKind kind, + const char *key, + const char *value, + size_t value_size) { - (void)cls; /* Unused. Silent compiler warning. */ - (void)kind; /* Unused. Silent compiler warning. */ + (void) cls; /* Unused. Silent compiler warning. */ + (void) kind; /* Unused. Silent compiler warning. */ + (void) value_size; + printf ("%s: %s\n", key, value); return MHD_YES; } @@ -38,7 +43,9 @@ answer_to_connection (void *cls, struct MHD_Connection *connection, (void)con_cls; /* Unused. Silent compiler warning. */ printf ("New %s request for %s using version %s\n", method, url, version); - MHD_get_connection_values (connection, MHD_HEADER_KIND, print_out_key, + MHD_get_connection_values (connection, + MHD_HEADER_KIND, + &print_out_key, NULL); return MHD_NO; diff --git a/doc/libmicrohttpd.texi b/doc/libmicrohttpd.texi @@ -1356,7 +1356,7 @@ reason for request termination see @code{MHD_OPTION_NOTIFY_COMPLETED}. @end deftypefn -@deftypefn {Function Pointer} int {*MHD_KeyValueIterator} (void *cls, enum MHD_ValueKind kind, const char *key, const char *value) +@deftypefn {Function Pointer} int {*MHD_KeyValueIterator} (void *cls, enum MHD_ValueKind kind, const char *key, const char *value, size_t value_size) Iterator over key-value pairs. This iterator can be used to iterate over all of the cookies, headers, or @code{POST}-data fields of a request, and also to iterate over the headers that have been added to a @@ -1375,6 +1375,17 @@ key for the value, can be an empty string @item value value corresponding value, can be NULL +@item value_size +number of bytes in @code{value}. This argument was introduced in +@code{MHD_VERSION} 0x00096301 to allow applications to use binary +zeros in values. Applications using this argument must ensure that +they are using a sufficiently recent version of MHD, i.e. by testing +@code{MHD_get_version()} for values above or equal to 0.9.64. +Applications that do not need zeros in values and that want to compile +without warnings against newer versions of MHD should not declare this +argument and cast the function pointer argument to +@code{MHD_KeyValueIterator}. + @end table Return @code{MHD_YES} to continue iterating, @code{MHD_NO} to abort the diff --git a/po/libmicrohttpd.pot b/po/libmicrohttpd.pot @@ -6,9 +6,9 @@ #, fuzzy msgid "" msgstr "" -"Project-Id-Version: GNU libmicrohttpd 0.9.62\n" +"Project-Id-Version: GNU libmicrohttpd 0.9.63\n" "Report-Msgid-Bugs-To: libmicrohttpd@gnu.org\n" -"POT-Creation-Date: 2018-12-08 23:11+0100\n" +"POT-Creation-Date: 2019-02-10 17:12+0100\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" "Language-Team: LANGUAGE <LL@li.org>\n" @@ -37,53 +37,53 @@ msgstr "" msgid "Failed to close FD.\n" msgstr "" -#: src/microhttpd/digestauth.c:563 +#: src/microhttpd/digestauth.c:599 msgid "" "Stale nonce received. If this happens a lot, you should probably increase " "the size of the nonce array.\n" msgstr "" -#: src/microhttpd/digestauth.c:755 +#: src/microhttpd/digestauth.c:792 msgid "Failed to allocate memory for copy of URI arguments\n" msgstr "" -#: src/microhttpd/digestauth.c:893 +#: src/microhttpd/digestauth.c:932 msgid "Authentication failed, invalid timestamp format.\n" msgstr "" -#: src/microhttpd/digestauth.c:956 +#: src/microhttpd/digestauth.c:995 msgid "Authentication failed, invalid format.\n" msgstr "" -#: src/microhttpd/digestauth.c:966 +#: src/microhttpd/digestauth.c:1005 msgid "Authentication failed, invalid nc format.\n" msgstr "" -#: src/microhttpd/digestauth.c:992 +#: src/microhttpd/digestauth.c:1031 msgid "Failed to allocate memory for auth header processing\n" msgstr "" -#: src/microhttpd/digestauth.c:1049 +#: src/microhttpd/digestauth.c:1090 msgid "Authentication failed, URI does not match.\n" msgstr "" -#: src/microhttpd/digestauth.c:1069 +#: src/microhttpd/digestauth.c:1109 msgid "Authentication failed, arguments do not match.\n" msgstr "" -#: src/microhttpd/digestauth.c:1224 +#: src/microhttpd/digestauth.c:1264 msgid "digest size missmatch" msgstr "" -#: src/microhttpd/digestauth.c:1314 +#: src/microhttpd/digestauth.c:1356 msgid "Could not register nonce (is the nonce array size zero?).\n" msgstr "" -#: src/microhttpd/digestauth.c:1339 +#: src/microhttpd/digestauth.c:1381 msgid "Failed to allocate memory for auth response header\n" msgstr "" -#: src/microhttpd/digestauth.c:1375 +#: src/microhttpd/digestauth.c:1417 msgid "Failed to add Digest auth header\n" msgstr "" @@ -126,7 +126,7 @@ msgid "" "unsupported.\n" msgstr "" -#: src/microhttpd/daemon.c:1279 src/microhttpd/daemon.c:6454 +#: src/microhttpd/daemon.c:1279 src/microhttpd/daemon.c:6469 msgid "" "Initiated daemon shutdown while \"upgraded\" connection was not closed.\n" msgstr "" @@ -180,7 +180,7 @@ msgstr "" msgid "PSK authentication failed: gnutls_malloc failed to allocate memory\n" msgstr "" -#: src/microhttpd/daemon.c:2318 src/microhttpd/daemon.c:6104 +#: src/microhttpd/daemon.c:2318 src/microhttpd/daemon.c:6113 #, c-format msgid "Socket descriptor larger than FD_SETSIZE: %d > %d\n" msgstr "" @@ -190,7 +190,7 @@ msgstr "" msgid "Failed to set SO_NOSIGPIPE on accepted socket: %s\n" msgstr "" -#: src/microhttpd/daemon.c:2351 src/microhttpd/daemon.c:3155 +#: src/microhttpd/daemon.c:2351 src/microhttpd/daemon.c:3164 #, c-format msgid "Accepted connection on socket %d\n" msgstr "" @@ -204,7 +204,7 @@ msgid "Connection rejected by application. Closing connection.\n" msgstr "" #: src/microhttpd/daemon.c:2414 src/microhttpd/daemon.c:2434 -#: src/microhttpd/daemon.c:3741 +#: src/microhttpd/daemon.c:3750 #, c-format msgid "Error allocating memory: %s\n" msgstr "" @@ -218,9 +218,9 @@ msgstr "" msgid "Unknown credential type" msgstr "" -#: src/microhttpd/daemon.c:2607 src/microhttpd/daemon.c:4231 -#: src/microhttpd/daemon.c:4264 src/microhttpd/daemon.c:5409 -#: src/microhttpd/daemon.c:5426 src/microhttpd/connection.c:3867 +#: src/microhttpd/daemon.c:2607 src/microhttpd/daemon.c:4240 +#: src/microhttpd/daemon.c:4273 src/microhttpd/daemon.c:5418 +#: src/microhttpd/daemon.c:5435 src/microhttpd/connection.c:3857 #: src/microhttpd/response.c:968 src/microhttpd/response.c:994 #, c-format msgid "Call to epoll_ctl failed: %s\n" @@ -230,9 +230,9 @@ msgstr "" msgid "Failed to signal new connection via inter-thread communication channel." msgstr "" -#: src/microhttpd/daemon.c:2737 src/microhttpd/daemon.c:3239 -#: src/microhttpd/daemon.c:6350 src/microhttpd/connection.c:992 -#: src/microhttpd/connection.c:1011 +#: src/microhttpd/daemon.c:2737 src/microhttpd/daemon.c:3248 +#: src/microhttpd/daemon.c:6359 src/microhttpd/connection.c:979 +#: src/microhttpd/connection.c:998 msgid "Failed to remove FD from epoll set\n" msgstr "" @@ -266,474 +266,478 @@ msgstr "" msgid "Failed to set noninheritable mode on new client socket.\n" msgstr "" -#: src/microhttpd/daemon.c:3093 +#: src/microhttpd/daemon.c:3029 +msgid "Failed to reset buffering mode on new client socket.\n" +msgstr "" + +#: src/microhttpd/daemon.c:3102 #, c-format msgid "Error accepting connection: %s\n" msgstr "" -#: src/microhttpd/daemon.c:3110 +#: src/microhttpd/daemon.c:3119 msgid "" "Hit process or system resource limit at FIRST connection. This is really bad " "as there is no sane way to proceed. Will try busy waiting for system " "resources to become magically available.\n" msgstr "" -#: src/microhttpd/daemon.c:3124 +#: src/microhttpd/daemon.c:3133 #, c-format msgid "" "Hit process or system resource limit at %u connections, temporarily " "suspending accept(). Consider setting a lower MHD_OPTION_CONNECTION_LIMIT.\n" msgstr "" -#: src/microhttpd/daemon.c:3136 +#: src/microhttpd/daemon.c:3145 #, c-format msgid "Failed to set nonblocking mode on incoming connection socket: %s\n" msgstr "" -#: src/microhttpd/daemon.c:3148 +#: src/microhttpd/daemon.c:3157 msgid "Failed to set noninheritable mode on incoming connection socket.\n" msgstr "" -#: src/microhttpd/daemon.c:3196 src/microhttpd/daemon.c:6496 -#: src/microhttpd/daemon.c:6528 src/microhttpd/daemon.c:6628 +#: src/microhttpd/daemon.c:3205 src/microhttpd/daemon.c:6511 +#: src/microhttpd/daemon.c:6543 src/microhttpd/daemon.c:6643 msgid "Failed to join a thread\n" msgstr "" -#: src/microhttpd/daemon.c:3300 +#: src/microhttpd/daemon.c:3309 msgid "Illegal call to MHD_get_timeout\n" msgstr "" -#: src/microhttpd/daemon.c:3497 +#: src/microhttpd/daemon.c:3506 msgid "" "MHD_run_from_select() called with except_fd_set set to NULL. Such behavior " "is deprecated.\n" msgstr "" -#: src/microhttpd/daemon.c:3577 +#: src/microhttpd/daemon.c:3586 msgid "Could not obtain daemon fdsets" msgstr "" -#: src/microhttpd/daemon.c:3594 +#: src/microhttpd/daemon.c:3603 msgid "Could not add listen socket to fdset" msgstr "" -#: src/microhttpd/daemon.c:3622 +#: src/microhttpd/daemon.c:3631 msgid "Could not add control inter-thread communication channel FD to fdset" msgstr "" -#: src/microhttpd/daemon.c:3678 +#: src/microhttpd/daemon.c:3687 #, c-format msgid "select failed: %s\n" msgstr "" -#: src/microhttpd/daemon.c:3823 src/microhttpd/daemon.c:3970 +#: src/microhttpd/daemon.c:3832 src/microhttpd/daemon.c:3979 #, c-format msgid "poll failed: %s\n" msgstr "" -#: src/microhttpd/daemon.c:4100 src/microhttpd/daemon.c:4331 +#: src/microhttpd/daemon.c:4109 src/microhttpd/daemon.c:4340 #, c-format msgid "Call to epoll_wait failed: %s\n" msgstr "" -#: src/microhttpd/daemon.c:4283 src/microhttpd/daemon.c:4740 +#: src/microhttpd/daemon.c:4292 src/microhttpd/daemon.c:4749 msgid "Failed to remove listen FD from epoll set\n" msgstr "" -#: src/microhttpd/daemon.c:4748 +#: src/microhttpd/daemon.c:4757 msgid "Failed to signal quiesce via inter-thread communication channel" msgstr "" -#: src/microhttpd/daemon.c:4770 +#: src/microhttpd/daemon.c:4779 msgid "failed to signal quiesce via inter-thread communication channel" msgstr "" -#: src/microhttpd/daemon.c:4878 +#: src/microhttpd/daemon.c:4887 msgid "Warning: Too large timeout value, ignored.\n" msgstr "" -#: src/microhttpd/daemon.c:4919 +#: src/microhttpd/daemon.c:4928 msgid "" "Warning: Zero size, specified for thread pool size, is ignored. Thread pool " "is not used.\n" msgstr "" -#: src/microhttpd/daemon.c:4927 +#: src/microhttpd/daemon.c:4936 msgid "" "Warning: \"1\", specified for thread pool size, is ignored. Thread pool is " "not used.\n" msgstr "" -#: src/microhttpd/daemon.c:4939 +#: src/microhttpd/daemon.c:4948 #, c-format msgid "Specified thread pool size (%u) too big\n" msgstr "" -#: src/microhttpd/daemon.c:4950 +#: src/microhttpd/daemon.c:4959 msgid "" "MHD_OPTION_THREAD_POOL_SIZE option is specified but " "MHD_USE_INTERNAL_POLLING_THREAD flag is not specified.\n" msgstr "" -#: src/microhttpd/daemon.c:4959 +#: src/microhttpd/daemon.c:4968 msgid "" "Both MHD_OPTION_THREAD_POOL_SIZE option and MHD_USE_THREAD_PER_CONNECTION " "flag are specified.\n" msgstr "" -#: src/microhttpd/daemon.c:4976 src/microhttpd/daemon.c:4988 -#: src/microhttpd/daemon.c:5000 src/microhttpd/daemon.c:5012 -#: src/microhttpd/daemon.c:5053 src/microhttpd/daemon.c:5081 -#: src/microhttpd/daemon.c:5100 +#: src/microhttpd/daemon.c:4985 src/microhttpd/daemon.c:4997 +#: src/microhttpd/daemon.c:5009 src/microhttpd/daemon.c:5021 +#: src/microhttpd/daemon.c:5062 src/microhttpd/daemon.c:5090 +#: src/microhttpd/daemon.c:5109 #, c-format msgid "MHD HTTPS option %d passed to MHD but MHD_USE_TLS not set\n" msgstr "" -#: src/microhttpd/daemon.c:5031 +#: src/microhttpd/daemon.c:5040 msgid "Error initializing DH parameters\n" msgstr "" -#: src/microhttpd/daemon.c:5043 +#: src/microhttpd/daemon.c:5052 msgid "Bad Diffie-Hellman parameters format\n" msgstr "" -#: src/microhttpd/daemon.c:5070 +#: src/microhttpd/daemon.c:5079 #, c-format msgid "Setting priorities to `%s' failed: %s\n" msgstr "" -#: src/microhttpd/daemon.c:5089 +#: src/microhttpd/daemon.c:5098 msgid "" "MHD_OPTION_HTTPS_CERT_CALLBACK requires building MHD with GnuTLS >= 3.0\n" msgstr "" -#: src/microhttpd/daemon.c:5123 +#: src/microhttpd/daemon.c:5132 msgid "" "MHD_OPTION_LISTEN_SOCKET specified for daemon with MHD_USE_NO_LISTEN_SOCKET " "flag set.\n" msgstr "" -#: src/microhttpd/daemon.c:5172 +#: src/microhttpd/daemon.c:5181 msgid "" "Flag MHD_USE_PEDANTIC_CHECKS is ignored because another behavior is " "specified by MHD_OPTION_STRICT_CLIENT.\n" msgstr "" -#: src/microhttpd/daemon.c:5304 +#: src/microhttpd/daemon.c:5313 #, c-format msgid "MHD HTTPS option %d passed to MHD compiled without GNUtls >= 3\n" msgstr "" -#: src/microhttpd/daemon.c:5317 +#: src/microhttpd/daemon.c:5326 #, c-format msgid "MHD HTTPS option %d passed to MHD compiled without HTTPS support\n" msgstr "" -#: src/microhttpd/daemon.c:5323 +#: src/microhttpd/daemon.c:5332 #, c-format msgid "Invalid option %d! (Did you terminate the list with MHD_OPTION_END?)\n" msgstr "" -#: src/microhttpd/daemon.c:5353 +#: src/microhttpd/daemon.c:5362 #, c-format msgid "Call to epoll_create1 failed: %s\n" msgstr "" -#: src/microhttpd/daemon.c:5363 +#: src/microhttpd/daemon.c:5372 msgid "Failed to set noninheritable mode on epoll FD.\n" msgstr "" -#: src/microhttpd/daemon.c:5606 +#: src/microhttpd/daemon.c:5615 msgid "" "Warning: MHD_USE_THREAD_PER_CONNECTION must be used only with " "MHD_USE_INTERNAL_POLLING_THREAD. Flag MHD_USE_INTERNAL_POLLING_THREAD was " "added. Consider setting MHD_USE_INTERNAL_POLLING_THREAD explicitly.\n" msgstr "" -#: src/microhttpd/daemon.c:5654 +#: src/microhttpd/daemon.c:5663 msgid "Using debug build of libmicrohttpd.\n" msgstr "" -#: src/microhttpd/daemon.c:5668 +#: src/microhttpd/daemon.c:5677 #, c-format msgid "Failed to create inter-thread communication channel: %s\n" msgstr "" -#: src/microhttpd/daemon.c:5684 +#: src/microhttpd/daemon.c:5693 msgid "" "file descriptor for inter-thread communication channel exceeds maximum " "value\n" msgstr "" -#: src/microhttpd/daemon.c:5704 +#: src/microhttpd/daemon.c:5713 msgid "Specified value for NC_SIZE too large\n" msgstr "" -#: src/microhttpd/daemon.c:5718 +#: src/microhttpd/daemon.c:5727 #, c-format msgid "Failed to allocate memory for nonce-nc map: %s\n" msgstr "" -#: src/microhttpd/daemon.c:5735 +#: src/microhttpd/daemon.c:5744 msgid "MHD failed to initialize nonce-nc mutex\n" msgstr "" -#: src/microhttpd/daemon.c:5755 +#: src/microhttpd/daemon.c:5764 msgid "MHD thread pooling only works with MHD_USE_INTERNAL_POLLING_THREAD\n" msgstr "" -#: src/microhttpd/daemon.c:5779 +#: src/microhttpd/daemon.c:5788 #, c-format msgid "Failed to create socket for listening: %s\n" msgstr "" -#: src/microhttpd/daemon.c:5800 src/microhttpd/daemon.c:5819 -#: src/microhttpd/daemon.c:5842 src/microhttpd/daemon.c:5879 -#: src/microhttpd/daemon.c:5956 src/microhttpd/daemon.c:5987 +#: src/microhttpd/daemon.c:5809 src/microhttpd/daemon.c:5828 +#: src/microhttpd/daemon.c:5851 src/microhttpd/daemon.c:5888 +#: src/microhttpd/daemon.c:5965 src/microhttpd/daemon.c:5996 #, c-format msgid "setsockopt failed: %s\n" msgstr "" -#: src/microhttpd/daemon.c:5852 +#: src/microhttpd/daemon.c:5861 msgid "Cannot allow listening address reuse: SO_REUSEPORT not defined\n" msgstr "" -#: src/microhttpd/daemon.c:5887 +#: src/microhttpd/daemon.c:5896 msgid "" "Cannot disallow listening address reuse: SO_EXCLUSIVEADDRUSE not defined\n" msgstr "" -#: src/microhttpd/daemon.c:5967 +#: src/microhttpd/daemon.c:5976 #, c-format msgid "Failed to bind to port %u: %s\n" msgstr "" -#: src/microhttpd/daemon.c:5998 +#: src/microhttpd/daemon.c:6007 #, c-format msgid "Failed to listen for connections: %s\n" msgstr "" -#: src/microhttpd/daemon.c:6025 +#: src/microhttpd/daemon.c:6034 #, c-format msgid "Failed to get listen port number: %s\n" msgstr "" -#: src/microhttpd/daemon.c:6035 +#: src/microhttpd/daemon.c:6044 msgid "" "Failed to get listen port number (`struct sockaddr_storage` too small!?)\n" msgstr "" -#: src/microhttpd/daemon.c:6068 +#: src/microhttpd/daemon.c:6077 msgid "Unknown address family!\n" msgstr "" -#: src/microhttpd/daemon.c:6081 +#: src/microhttpd/daemon.c:6090 #, c-format msgid "Failed to set nonblocking mode on listening socket: %s\n" msgstr "" -#: src/microhttpd/daemon.c:6123 +#: src/microhttpd/daemon.c:6132 msgid "" "Combining MHD_USE_THREAD_PER_CONNECTION and MHD_USE_EPOLL is not supported.\n" msgstr "" -#: src/microhttpd/daemon.c:6137 src/microhttpd/daemon.c:6150 +#: src/microhttpd/daemon.c:6146 src/microhttpd/daemon.c:6159 msgid "MHD failed to initialize IP connection limit mutex\n" msgstr "" -#: src/microhttpd/daemon.c:6169 +#: src/microhttpd/daemon.c:6178 msgid "Failed to initialize TLS support\n" msgstr "" -#: src/microhttpd/daemon.c:6196 +#: src/microhttpd/daemon.c:6205 #, c-format msgid "Failed to create listen thread: %s\n" msgstr "" -#: src/microhttpd/daemon.c:6244 +#: src/microhttpd/daemon.c:6253 #, c-format msgid "Failed to create worker inter-thread communication channel: %s\n" msgstr "" -#: src/microhttpd/daemon.c:6255 +#: src/microhttpd/daemon.c:6264 msgid "" "File descriptor for worker inter-thread communication channel exceeds " "maximum value\n" msgstr "" -#: src/microhttpd/daemon.c:6280 +#: src/microhttpd/daemon.c:6289 msgid "MHD failed to initialize cleanup connection mutex\n" msgstr "" -#: src/microhttpd/daemon.c:6294 +#: src/microhttpd/daemon.c:6303 #, c-format msgid "Failed to create pool thread: %s\n" msgstr "" -#: src/microhttpd/daemon.c:6441 src/microhttpd/daemon.c:6472 +#: src/microhttpd/daemon.c:6456 src/microhttpd/daemon.c:6487 msgid "MHD_stop_daemon() called while we have suspended connections.\n" msgstr "" -#: src/microhttpd/daemon.c:6481 src/microhttpd/daemon.c:6610 +#: src/microhttpd/daemon.c:6496 src/microhttpd/daemon.c:6625 msgid "Failed to signal shutdown via inter-thread communication channel" msgstr "" -#: src/microhttpd/daemon.c:6573 +#: src/microhttpd/daemon.c:6588 msgid "Failed to signal shutdown via inter-thread communication channel." msgstr "" -#: src/microhttpd/daemon.c:7040 +#: src/microhttpd/daemon.c:7055 msgid "Failed to initialize winsock\n" msgstr "" -#: src/microhttpd/daemon.c:7043 +#: src/microhttpd/daemon.c:7058 msgid "Winsock version 2.2 is not available\n" msgstr "" -#: src/microhttpd/daemon.c:7051 src/microhttpd/daemon.c:7055 +#: src/microhttpd/daemon.c:7066 src/microhttpd/daemon.c:7070 msgid "Failed to initialise multithreading in libgcrypt\n" msgstr "" -#: src/microhttpd/daemon.c:7060 +#: src/microhttpd/daemon.c:7075 msgid "libgcrypt is too old. MHD was compiled for libgcrypt 1.6.0 or newer\n" msgstr "" -#: src/microhttpd/mhd_sockets.h:248 +#: src/microhttpd/mhd_sockets.h:261 msgid "Close socket failed.\n" msgstr "" -#: src/microhttpd/connection.c:1117 +#: src/microhttpd/connection.c:1104 msgid "Closing connection (application reported error generating data)\n" msgstr "" -#: src/microhttpd/connection.c:1170 +#: src/microhttpd/connection.c:1157 msgid "Closing connection (out of memory)\n" msgstr "" -#: src/microhttpd/connection.c:1217 +#: src/microhttpd/connection.c:1204 msgid "Closing connection (application error generating response)\n" msgstr "" -#: src/microhttpd/connection.c:1799 +#: src/microhttpd/connection.c:1786 #, c-format msgid "" "Error processing request (HTTP response code is %u (`%s')). Closing " "connection.\n" msgstr "" -#: src/microhttpd/connection.c:1825 src/microhttpd/connection.c:2810 +#: src/microhttpd/connection.c:1812 src/microhttpd/connection.c:2800 msgid "Closing connection (failed to queue response)\n" msgstr "" -#: src/microhttpd/connection.c:1835 src/microhttpd/connection.c:3579 -#: src/microhttpd/connection.c:3702 +#: src/microhttpd/connection.c:1822 src/microhttpd/connection.c:3569 +#: src/microhttpd/connection.c:3692 msgid "Closing connection (failed to create response header)\n" msgstr "" -#: src/microhttpd/connection.c:1881 src/microhttpd/connection.c:2960 -#: src/microhttpd/connection.c:3028 src/microhttpd/connection.c:3344 +#: src/microhttpd/connection.c:1868 src/microhttpd/connection.c:2950 +#: src/microhttpd/connection.c:3018 src/microhttpd/connection.c:3334 #, c-format msgid "In function %s handling connection at state: %s\n" msgstr "" -#: src/microhttpd/connection.c:2093 +#: src/microhttpd/connection.c:2080 msgid "Not enough memory in pool to allocate header record!\n" msgstr "" -#: src/microhttpd/connection.c:2135 +#: src/microhttpd/connection.c:2122 msgid "Not enough memory in pool to parse cookies!\n" msgstr "" -#: src/microhttpd/connection.c:2356 src/microhttpd/connection.c:2541 +#: src/microhttpd/connection.c:2346 src/microhttpd/connection.c:2531 msgid "Application reported internal error, closing connection.\n" msgstr "" -#: src/microhttpd/connection.c:2409 src/microhttpd/connection.c:2486 +#: src/microhttpd/connection.c:2399 src/microhttpd/connection.c:2476 msgid "" "Received malformed HTTP request (bad chunked encoding). Closing connection.\n" msgstr "" -#: src/microhttpd/connection.c:2549 +#: src/microhttpd/connection.c:2539 msgid "libmicrohttpd API violation" msgstr "" -#: src/microhttpd/connection.c:2564 +#: src/microhttpd/connection.c:2554 msgid "" "WARNING: incomplete upload processing and connection not suspended may " "result in hung connection.\n" msgstr "" -#: src/microhttpd/connection.c:2634 +#: src/microhttpd/connection.c:2624 msgid "Received malformed line (no colon). Closing connection.\n" msgstr "" -#: src/microhttpd/connection.c:2788 +#: src/microhttpd/connection.c:2778 msgid "Received HTTP 1.1 request without `Host' header.\n" msgstr "" -#: src/microhttpd/connection.c:2799 +#: src/microhttpd/connection.c:2789 msgid "Closing connection (failed to create response)\n" msgstr "" -#: src/microhttpd/connection.c:2939 +#: src/microhttpd/connection.c:2929 msgid "Socket disconnected while reading request.\n" msgstr "" -#: src/microhttpd/connection.c:2945 +#: src/microhttpd/connection.c:2935 msgid "Connection socket is closed due to error when reading request.\n" msgstr "" -#: src/microhttpd/connection.c:3054 +#: src/microhttpd/connection.c:3044 #, c-format msgid "Failed to send data in request for %s.\n" msgstr "" -#: src/microhttpd/connection.c:3063 +#: src/microhttpd/connection.c:3053 #, c-format msgid "Sent 100 continue response: `%.*s'\n" msgstr "" -#: src/microhttpd/connection.c:3087 +#: src/microhttpd/connection.c:3077 msgid "Connection was closed while sending response headers.\n" msgstr "" -#: src/microhttpd/connection.c:3128 +#: src/microhttpd/connection.c:3118 msgid "Data offset exceeds limit" msgstr "" -#: src/microhttpd/connection.c:3137 +#: src/microhttpd/connection.c:3127 #, c-format msgid "Sent %d-byte DATA response: `%.*s'\n" msgstr "" -#: src/microhttpd/connection.c:3154 +#: src/microhttpd/connection.c:3144 #, c-format msgid "Failed to send data in request for `%s'.\n" msgstr "" -#: src/microhttpd/connection.c:3182 src/microhttpd/connection.c:3210 +#: src/microhttpd/connection.c:3172 src/microhttpd/connection.c:3200 msgid "Connection was closed while sending response body.\n" msgstr "" -#: src/microhttpd/connection.c:3233 +#: src/microhttpd/connection.c:3223 msgid "Internal error\n" msgstr "" -#: src/microhttpd/connection.c:3306 +#: src/microhttpd/connection.c:3296 msgid "" "Failed to signal end of connection via inter-thread communication channel" msgstr "" -#: src/microhttpd/connection.c:4053 +#: src/microhttpd/connection.c:4043 msgid "Attempted to queue response on wrong thread!\n" msgstr "" -#: src/microhttpd/connection.c:4064 +#: src/microhttpd/connection.c:4054 msgid "" "Attempted 'upgrade' connection on daemon without MHD_ALLOW_UPGRADE option!\n" msgstr "" -#: src/microhttpd/connection.c:4073 +#: src/microhttpd/connection.c:4063 msgid "Application used invalid status code for 'upgrade' response!\n" msgstr "" diff --git a/src/examples/Makefile.am b/src/examples/Makefile.am @@ -70,7 +70,8 @@ endif if HAVE_ZLIB noinst_PROGRAMS += \ - http_compression + http_compression \ + http_chunked_compression endif if HAVE_W32 @@ -206,8 +207,13 @@ https_fileserver_example_LDADD = \ http_compression_SOURCES = \ http_compression.c +http_chunked_compression_SOURCES = \ + http_chunked_compression.c http_compression_LDADD = \ $(top_builddir)/src/microhttpd/libmicrohttpd.la +http_chunked_compression_LDADD = \ + $(top_builddir)/src/microhttpd/libmicrohttpd.la if HAVE_ZLIB http_compression_LDADD += -lz + http_chunked_compression_LDADD += -lz endif \ No newline at end of file diff --git a/src/examples/http_chunked_compression.c b/src/examples/http_chunked_compression.c @@ -0,0 +1,199 @@ +/* + This file is part of libmicrohttpd + Copyright (C) 2019 Christian Grothoff (and other contributing authors) + + 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 http_chunked_compression.c + * @brief example for how to compress a chunked HTTP response + * @author Silvio Clecio (silvioprog) + */ + +#include "platform.h" +#include <zlib.h> +#include <microhttpd.h> + +#define CHUNK 16384 + +struct Holder { + FILE *file; + z_stream stream; + void *buf; +}; + +static int +compress_buf (z_stream *strm, const void *src, size_t src_size, size_t *offset, void **dest, size_t *dest_size, + void *tmp) +{ + unsigned int have; + int ret; + int flush; + *dest = NULL; + *dest_size = 0; + do + { + if (src_size > CHUNK) + { + strm->avail_in = CHUNK; + src_size -= CHUNK; + flush = Z_NO_FLUSH; + } + else + { + strm->avail_in = (uInt) src_size; + flush = Z_SYNC_FLUSH; + } + *offset += strm->avail_in; + strm->next_in = (Bytef *) src; + do + { + strm->avail_out = CHUNK; + strm->next_out = tmp; + ret = deflate (strm, flush); + have = CHUNK - strm->avail_out; + *dest_size += have; + *dest = realloc (*dest, *dest_size); + if (NULL == *dest) + return MHD_NO; + memcpy ((*dest) + ((*dest_size) - have), tmp, have); + } + while (0 == strm->avail_out); + } + while (flush != Z_SYNC_FLUSH); + return (Z_OK == ret) ? MHD_YES : MHD_NO; +} + +static ssize_t +read_cb (void *cls, uint64_t pos, char *mem, size_t size) +{ + struct Holder *holder = cls; + void *src; + void *buf; + src = malloc (size); + if (NULL == src) + return MHD_CONTENT_READER_END_WITH_ERROR; + size = fread (src, 1, size, holder->file); + if (size < 0) + { + size = MHD_CONTENT_READER_END_WITH_ERROR; + goto done; + } + if (0 == size) + { + size = MHD_CONTENT_READER_END_OF_STREAM; + goto done; + } + if (MHD_YES != compress_buf (&holder->stream, src, size, &pos, &buf, &size, holder->buf)) + size = MHD_CONTENT_READER_END_WITH_ERROR; + else + { + memcpy (mem, buf, size); + free (buf); + } +done: + free (src); + return size; +} + +static void +free_cb (void *cls) +{ + struct Holder *holder = cls; + fclose (holder->file); + deflateEnd (&holder->stream); + free (holder->buf); + free (holder); +} + +static int +ahc_echo (void *cls, struct MHD_Connection *con, const char *url, const char *method, const char *version, + const char *upload_data, size_t *upload_size, void **ptr) +{ + struct Holder *holder; + struct MHD_Response *res; + int ret; + (void) cls; + (void) url; + (void) method; + (void) version; + (void) upload_data; + (void) upload_size; + if (NULL == *ptr) + { + *ptr = (void *) 1; + return MHD_YES; + } + *ptr = NULL; + holder = calloc (1, sizeof (struct Holder)); + if (!holder) + return MHD_NO; + holder->file = fopen (__FILE__, "rb"); + if (NULL == holder->file) + goto file_error; + ret = deflateInit(&holder->stream, Z_BEST_COMPRESSION); + if (ret != Z_OK) + goto stream_error; + holder->buf = malloc (CHUNK); + if (NULL == holder->buf) + goto buf_error; + res = MHD_create_response_from_callback (MHD_SIZE_UNKNOWN, 1024, &read_cb, holder, &free_cb); + if (NULL == res) + goto error; + ret = MHD_add_response_header (res, MHD_HTTP_HEADER_CONTENT_ENCODING, "deflate"); + if (MHD_YES != ret) + goto res_error; + ret = MHD_add_response_header (res, MHD_HTTP_HEADER_CONTENT_TYPE, "text/x-c"); + if (MHD_YES != ret) + goto res_error; + ret = MHD_queue_response (con, MHD_HTTP_OK, res); +res_error: + MHD_destroy_response (res); + return ret; +error: + free (holder->buf); +buf_error: + deflateEnd (&holder->stream); +stream_error: + fclose (holder->file); +file_error: + free (holder); + return MHD_NO; +} + +int +main (int argc, char *const *argv) +{ + struct MHD_Daemon *d; + unsigned int port; + if ((argc != 2) || + (1 != sscanf (argv[1], "%u", &port)) || + (UINT16_MAX < port)) + { + fprintf (stderr, "%s PORT\n", argv[0]); + return 1; + } + d = MHD_start_daemon (MHD_USE_AUTO | MHD_USE_INTERNAL_POLLING_THREAD, (uint16_t) port, NULL, NULL, + &ahc_echo, NULL, + MHD_OPTION_END); + if (NULL == d) + return 1; + if (0 == port) + MHD_get_daemon_info (d, MHD_DAEMON_INFO_BIND_PORT, &port); + fprintf (stdout, "HTTP server running at http://localhost:%u\n\nPress ENTER to stop the server ...\n", port); + (void) getc (stdin); + MHD_stop_daemon (d); + return 0; +} diff --git a/src/include/microhttpd.h b/src/include/microhttpd.h @@ -1,6 +1,6 @@ /* This file is part of libmicrohttpd - Copyright (C) 2006-2018 Christian Grothoff (and other contributing authors) + Copyright (C) 2006--2019 Christian Grothoff (and other contributing authors) This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public @@ -126,7 +126,7 @@ typedef intptr_t ssize_t; * Current version of the library. * 0x01093001 = 1.9.30-1. */ -#define MHD_VERSION 0x00096203 +#define MHD_VERSION 0x00096301 /** * MHD-internal return code for "YES". @@ -1380,10 +1380,12 @@ enum MHD_OPTION * struct MHD_Connection *c, * char *s) * - * where the return value must be "strlen(s)" and "s" should be - * updated. Note that the unescape function must not lengthen "s" - * (the result must be shorter than the input and still be - * 0-terminated). "cls" will be set to the second argument + * where the return value must be the length of the value left in + * "s" (without the 0-terminator) and "s" should be updated. Note + * that the unescape function must not lengthen "s" (the result must + * be shorter than the input and must still be 0-terminated). + * However, it may also include binary zeros before the + * 0-termination. "cls" will be set to the second argument * following #MHD_OPTION_UNESCAPE_CALLBACK. */ MHD_OPTION_UNESCAPE_CALLBACK = 16, @@ -2025,6 +2027,8 @@ typedef void * @param kind kind of the header we are looking at * @param key key for the value, can be an empty string * @param value corresponding value, can be NULL + * @param value_size number of bytes in @a value, NEW since #MHD_VERSION 0x00096301; + * for C-strings, the length excludes the 0-terminator * @return #MHD_YES to continue iterating, * #MHD_NO to abort the iteration * @ingroup request @@ -2033,7 +2037,8 @@ typedef int (*MHD_KeyValueIterator) (void *cls, enum MHD_ValueKind kind, const char *key, - const char *value); + const char *value, + size_t value_size); /** @@ -2494,6 +2499,40 @@ MHD_set_connection_value (struct MHD_Connection *connection, /** + * This function can be used to add an entry to the HTTP headers of a + * connection (so that the #MHD_get_connection_values function will + * return them -- and the `struct 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). + * + * 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 + * @param value_size number of bytes in @a value (excluding 0-terminator for C-strings) + * @return #MHD_NO if the operation could not be + * performed due to insufficient memory; + * #MHD_YES on success + * @ingroup request + */ +int +MHD_set_connection_value2 (struct MHD_Connection *connection, + enum MHD_ValueKind kind, + const char *key, + const char *value, + size_t value_size); + + +/** * Sets the global error handler to a different implementation. @a cb * will only be called in the case of typically fatal, serious * internal consistency issues. These issues should only arise in the @@ -3075,7 +3114,8 @@ MHD_del_response_header (struct MHD_Response *response, */ _MHD_EXTERN int MHD_get_response_headers (struct MHD_Response *response, - MHD_KeyValueIterator iterator, void *iterator_cls); + MHD_KeyValueIterator iterator, + void *iterator_cls); /** diff --git a/src/microhttpd/connection.c b/src/microhttpd/connection.c @@ -1,6 +1,6 @@ /* This file is part of libmicrohttpd - Copyright (C) 2007-2017 Daniel Pittman and Christian Grothoff + Copyright (C) 2007-2019 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 @@ -706,7 +706,8 @@ MHD_get_connection_values (struct MHD_Connection *connection, (MHD_YES != iterator (iterator_cls, pos->kind, pos->header, - pos->value)) ) + pos->value, + pos->value_size)) ) return ret; } return ret; @@ -733,16 +734,18 @@ MHD_get_connection_values (struct MHD_Connection *connection, * @param kind kind of the value * @param key key for the value * @param value the value itself + * @param value_size number of bytes in @a value * @return #MHD_NO if the operation could not be * performed due to insufficient memory; * #MHD_YES on success * @ingroup request */ int -MHD_set_connection_value (struct MHD_Connection *connection, - enum MHD_ValueKind kind, - const char *key, - const char *value) +MHD_set_connection_value2 (struct MHD_Connection *connection, + enum MHD_ValueKind kind, + const char *key, + const char *value, + size_t value_size) { struct MHD_HTTP_Header *pos; @@ -753,6 +756,7 @@ MHD_set_connection_value (struct MHD_Connection *connection, return MHD_NO; pos->header = (char *) key; pos->value = (char *) value; + pos->value_size = value_size; pos->kind = kind; pos->next = NULL; /* append 'pos' to the linked list of headers */ @@ -771,6 +775,47 @@ MHD_set_connection_value (struct MHD_Connection *connection, /** + * This function can be used to add an entry to the HTTP headers of a + * connection (so that the #MHD_get_connection_values function will + * return them -- and the `struct 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). + * + * 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 + * @ingroup request + */ +int +MHD_set_connection_value (struct MHD_Connection *connection, + enum MHD_ValueKind kind, + const char *key, + const char *value) +{ + return MHD_set_connection_value2 (connection, + kind, + key, + value, + NULL != value + ? strlen (value) + : 0); +} + + +/** * Get a particular header value. If multiple * values match the kind, return any one of them. * @@ -2061,19 +2106,22 @@ get_next_header_line (struct MHD_Connection *connection, * @param kind kind of the value * @param key key for the value * @param value the value itself + * @param value_size number of bytes in @a value * @return #MHD_NO on failure (out of memory), #MHD_YES for success */ static int connection_add_header (struct MHD_Connection *connection, const char *key, const char *value, + size_t value_size, enum MHD_ValueKind kind) { if (MHD_NO == - MHD_set_connection_value (connection, - kind, - key, - value)) + MHD_set_connection_value2 (connection, + kind, + key, + value, + value_size)) { #ifdef HAVE_MESSAGES MHD_DLOG (connection->daemon, @@ -2104,6 +2152,7 @@ parse_cookie_header (struct MHD_Connection *connection) char *semicolon; char *equals; char *ekill; + char *end; char old; int quotes; @@ -2155,6 +2204,7 @@ parse_cookie_header (struct MHD_Connection *connection) connection_add_header (connection, pos, "", + 0, MHD_COOKIE_KIND)) return MHD_NO; if (old == '\0') @@ -2174,6 +2224,7 @@ parse_cookie_header (struct MHD_Connection *connection) quotes = (quotes + 1) & 1; semicolon++; } + end = semicolon; if ('\0' == semicolon[0]) semicolon = NULL; if (NULL != semicolon) @@ -2183,15 +2234,17 @@ parse_cookie_header (struct MHD_Connection *connection) } /* remove quotes */ if ( ('"' == equals[0]) && - ('"' == equals[strlen (equals) - 1]) ) + ('"' == end[-1]) ) { - equals[strlen (equals) - 1] = '\0'; equals++; + end--; + *end = '\0'; } if (MHD_NO == connection_add_header (connection, pos, equals, + end - equals, MHD_COOKIE_KIND)) return MHD_NO; pos = semicolon; @@ -2257,7 +2310,7 @@ parse_initial_message_line (struct MHD_Connection *connection, http_version--; if (http_version > uri) { - /* http_version points to string before HTTP version string */ + /* http_version points to character before HTTP version string */ http_version[0] = '\0'; connection->version = http_version + 1; uri_len = http_version - uri; @@ -2277,24 +2330,21 @@ parse_initial_message_line (struct MHD_Connection *connection, return MHD_NO; } - /* unescape URI before searching for arguments */ - daemon->unescape_callback (daemon->unescape_callback_cls, - connection, - uri); - uri_len = strlen (uri); /* recalculate: may have changed! */ args = memchr (uri, '?', uri_len); } + /* log callback before we modify URI *or* args */ if (NULL != daemon->uri_log_callback) { connection->client_aware = true; connection->client_context = daemon->uri_log_callback (daemon->uri_log_callback_cls, - curi, + uri, connection); } + if (NULL != args) { args[0] = '\0'; @@ -2306,6 +2356,12 @@ parse_initial_message_line (struct MHD_Connection *connection, &connection_add_header, &unused_num_headers); } + + /* unescape URI *after* searching for arguments and log callback */ + if (NULL != uri) + daemon->unescape_callback (daemon->unescape_callback_cls, + connection, + uri); connection->url = curi; return MHD_YES; } @@ -2708,16 +2764,20 @@ process_broken_line (struct MHD_Connection *connection, REQUEST_TOO_BIG); return MHD_NO; } - memcpy (&last[last_len], tmp, tmp_len + 1); + memcpy (&last[last_len], + tmp, + tmp_len + 1); connection->last = last; return MHD_YES; /* possibly more than 2 lines... */ } mhd_assert ( (NULL != last) && (NULL != connection->colon) ); - if ((MHD_NO == connection_add_header (connection, - last, - connection->colon, - kind))) + if (MHD_NO == + connection_add_header (connection, + last, + connection->colon, + strlen (connection->colon), + kind)) { transmit_error_response (connection, MHD_HTTP_REQUEST_HEADER_FIELDS_TOO_LARGE, diff --git a/src/microhttpd/digestauth.c b/src/microhttpd/digestauth.c @@ -732,6 +732,7 @@ calculate_nonce (uint32_t nonce_time, * @param connection the connection * @param key the key * @param value the value, can be NULL + * @param value_size number of bytes in @a value * @param kind type of the header * @return #MHD_YES if the key-value pair is in the headers, * #MHD_NO if not @@ -740,6 +741,7 @@ static int test_header (struct MHD_Connection *connection, const char *key, const char *value, + size_t value_size, enum MHD_ValueKind kind) { struct MHD_HTTP_Header *pos; @@ -748,6 +750,8 @@ test_header (struct MHD_Connection *connection, { if (kind != pos->kind) continue; + if (value_size != pos->value_size) + continue; if (0 != strcmp (key, pos->header)) continue; @@ -756,8 +760,9 @@ test_header (struct MHD_Connection *connection, return MHD_YES; if ( (NULL == value) || (NULL == pos->value) || - (0 != strcmp (value, - pos->value)) ) + (0 != memcmp (value, + pos->value, + value_size)) ) continue; return MHD_YES; } @@ -862,6 +867,7 @@ digest_auth_check_all (struct MHD_Connection *connection, uint32_t t; size_t left; /* number of characters left in 'header' for 'uri' */ uint64_t nci; + char *qmark; VLA_CHECK_LEN_DIGEST(da->digest_size); header = MHD_lookup_connection_value (connection, @@ -1072,15 +1078,17 @@ digest_auth_check_all (struct MHD_Connection *connection, uri, hentity, da); - + qmark = strchr (uri, + '?'); + if (NULL != qmark) + *qmark = '\0'; /* Need to unescape URI before comparing with connection->url */ daemon->unescape_callback (daemon->unescape_callback_cls, connection, uri); - if (0 != strncmp (uri, - connection->url, - strlen (connection->url))) + if (0 != strcmp (uri, + connection->url)) { #ifdef HAVE_MESSAGES MHD_DLOG (daemon, @@ -1091,8 +1099,7 @@ digest_auth_check_all (struct MHD_Connection *connection, } { - const char *args = strchr (uri, - '?'); + const char *args = qmark; if (NULL == args) args = ""; diff --git a/src/microhttpd/internal.c b/src/microhttpd/internal.c @@ -162,7 +162,7 @@ MHD_http_unescape (char *val) } } *wpos = '\0'; /* add 0-terminator */ - return wpos - val; /* = strlen(val) */ + return wpos - val; } @@ -190,6 +190,7 @@ MHD_parse_arguments_ (struct MHD_Connection *connection, struct MHD_Daemon *daemon = connection->daemon; char *equals; char *amper; + size_t len; *num_headers = 0; while ( (NULL != args) && @@ -210,6 +211,7 @@ MHD_parse_arguments_ (struct MHD_Connection *connection, if (MHD_YES != cb (connection, args, NULL, + 0, kind)) return MHD_NO; (*num_headers)++; @@ -223,12 +225,13 @@ MHD_parse_arguments_ (struct MHD_Connection *connection, connection, args); MHD_unescape_plus (equals); - daemon->unescape_callback (daemon->unescape_callback_cls, - connection, - equals); + len = daemon->unescape_callback (daemon->unescape_callback_cls, + connection, + equals); if (MHD_YES != cb (connection, args, equals, + len, kind)) return MHD_NO; (*num_headers)++; @@ -248,6 +251,7 @@ MHD_parse_arguments_ (struct MHD_Connection *connection, if (MHD_YES != cb (connection, args, NULL, + 0, kind)) return MHD_NO; /* continue with 'bar' */ @@ -264,12 +268,13 @@ MHD_parse_arguments_ (struct MHD_Connection *connection, connection, args); MHD_unescape_plus (equals); - daemon->unescape_callback (daemon->unescape_callback_cls, - connection, - equals); + len = daemon->unescape_callback (daemon->unescape_callback_cls, + connection, + equals); if (MHD_YES != cb (connection, args, equals, + len, kind)) return MHD_NO; (*num_headers)++; diff --git a/src/microhttpd/internal.h b/src/microhttpd/internal.h @@ -276,6 +276,11 @@ struct MHD_HTTP_Header char *value; /** + * Number of bytes in @a value. + */ + size_t value_size; + + /** * Type of the header (where in the HTTP protocol is this header * from). */ @@ -1881,7 +1886,8 @@ MHD_unescape_plus (char *arg); * * @param connection context of the iteration * @param key 0-terminated key string, never NULL - * @param value 0-terminated value string, may be NULL + * @param value 0-terminated binary data, may include binary zeros, may be NULL + * @param value_size number of bytes in value * @param kind origin of the key-value pair * @return #MHD_YES on success (continue to iterate) * #MHD_NO to signal failure (and abort iteration) @@ -1890,6 +1896,7 @@ typedef int (*MHD_ArgumentIterator_)(struct MHD_Connection *connection, const char *key, const char *value, + size_t value_size, enum MHD_ValueKind kind); diff --git a/src/microhttpd/response.c b/src/microhttpd/response.c @@ -240,7 +240,8 @@ MHD_get_response_headers (struct MHD_Response *response, (MHD_YES != iterator (iterator_cls, pos->kind, pos->header, - pos->value))) + pos->value, + pos->value_size))) break; } return numHeaders; diff --git a/src/testcurl/test_get.c b/src/testcurl/test_get.c @@ -35,6 +35,8 @@ #include "mhd_sockets.h" /* only macros used */ +#define EXPECTED_URI_PATH "/hello_world?a=%26&b=c" + #ifdef _WIN32 #ifndef WIN32_LEAN_AND_MEAN #define WIN32_LEAN_AND_MEAN 1 @@ -64,6 +66,7 @@ struct CBC size_t size; }; + static size_t copyBuffer (void *ptr, size_t size, size_t nmemb, void *ctx) { @@ -76,6 +79,24 @@ copyBuffer (void *ptr, size_t size, size_t nmemb, void *ctx) return size * nmemb; } + +static void * +log_cb (void *cls, + const char *uri, + struct MHD_Connection *con) +{ + if (0 != strcmp (uri, + EXPECTED_URI_PATH)) + { + fprintf (stderr, + "Wrong URI: `%s'\n", + uri); + abort (); + } + return NULL; +} + + static int ahc_echo (void *cls, struct MHD_Connection *connection, @@ -89,7 +110,10 @@ ahc_echo (void *cls, const char *me = cls; struct MHD_Response *response; int ret; - (void)version;(void)upload_data;(void)upload_data_size; /* Unused. Silent compiler warning. */ + const char *v; + (void) version; + (void) upload_data; + (void) upload_data_size; /* Unused. Silence compiler warning. */ if (0 != strcasecmp (me, method)) return MHD_NO; /* unexpected method */ @@ -99,10 +123,26 @@ ahc_echo (void *cls, return MHD_YES; } *unused = NULL; + v = MHD_lookup_connection_value (connection, + MHD_GET_ARGUMENT_KIND, + "a"); + if ( (NULL == v) || + (0 != strcmp ("&", + v)) ) + abort (); + v = MHD_lookup_connection_value (connection, + MHD_GET_ARGUMENT_KIND, + "b"); + if ( (NULL == v) || + (0 != strcmp ("c", + v)) ) + abort (); response = MHD_create_response_from_buffer (strlen (url), (void *) url, MHD_RESPMEM_MUST_COPY); - ret = MHD_queue_response (connection, MHD_HTTP_OK, response); + ret = MHD_queue_response (connection, + MHD_HTTP_OK, + response); MHD_destroy_response (response); if (ret == MHD_NO) abort (); @@ -131,7 +171,10 @@ testInternalGet (int poll_flag) cbc.size = 2048; cbc.pos = 0; d = MHD_start_daemon (MHD_USE_INTERNAL_POLLING_THREAD | MHD_USE_ERROR_LOG | poll_flag, - global_port, NULL, NULL, &ahc_echo, "GET", MHD_OPTION_END); + global_port, NULL, NULL, + &ahc_echo, "GET", + MHD_OPTION_URI_LOG_CALLBACK, &log_cb, NULL, + MHD_OPTION_END); if (d == NULL) return 1; if (0 == global_port) @@ -143,7 +186,7 @@ testInternalGet (int poll_flag) global_port = (int)dinfo->port; } c = curl_easy_init (); - curl_easy_setopt (c, CURLOPT_URL, "http://127.0.0.1/hello_world"); + curl_easy_setopt (c, CURLOPT_URL, "http://127.0.0.1" EXPECTED_URI_PATH); curl_easy_setopt (c, CURLOPT_PORT, (long)global_port); curl_easy_setopt (c, CURLOPT_WRITEFUNCTION, &copyBuffer); curl_easy_setopt (c, CURLOPT_WRITEDATA, &cbc); @@ -198,7 +241,10 @@ testMultithreadedGet (int poll_flag) cbc.size = 2048; cbc.pos = 0; d = MHD_start_daemon (MHD_USE_THREAD_PER_CONNECTION | MHD_USE_INTERNAL_POLLING_THREAD | MHD_USE_ERROR_LOG | poll_flag, - global_port, NULL, NULL, &ahc_echo, "GET", MHD_OPTION_END); + global_port, NULL, NULL, + &ahc_echo, "GET", + MHD_OPTION_URI_LOG_CALLBACK, &log_cb, NULL, + MHD_OPTION_END); if (d == NULL) return 16; if (0 == global_port) @@ -210,7 +256,7 @@ testMultithreadedGet (int poll_flag) global_port = (int)dinfo->port; } c = curl_easy_init (); - curl_easy_setopt (c, CURLOPT_URL, "http://127.0.0.1/hello_world"); + curl_easy_setopt (c, CURLOPT_URL, "http://127.0.0.1" EXPECTED_URI_PATH); curl_easy_setopt (c, CURLOPT_PORT, (long)global_port); curl_easy_setopt (c, CURLOPT_WRITEFUNCTION, &copyBuffer); curl_easy_setopt (c, CURLOPT_WRITEDATA, &cbc); @@ -265,8 +311,11 @@ testMultithreadedPoolGet (int poll_flag) cbc.size = 2048; cbc.pos = 0; d = MHD_start_daemon (MHD_USE_INTERNAL_POLLING_THREAD | MHD_USE_ERROR_LOG | poll_flag, - global_port, NULL, NULL, &ahc_echo, "GET", - MHD_OPTION_THREAD_POOL_SIZE, CPU_COUNT, MHD_OPTION_END); + global_port, NULL, NULL, + &ahc_echo, "GET", + MHD_OPTION_THREAD_POOL_SIZE, CPU_COUNT, + MHD_OPTION_URI_LOG_CALLBACK, &log_cb, NULL, + MHD_OPTION_END); if (d == NULL) return 16; if (0 == global_port) @@ -278,7 +327,7 @@ testMultithreadedPoolGet (int poll_flag) global_port = (int)dinfo->port; } c = curl_easy_init (); - curl_easy_setopt (c, CURLOPT_URL, "http://127.0.0.1/hello_world"); + curl_easy_setopt (c, CURLOPT_URL, "http://127.0.0.1" EXPECTED_URI_PATH); curl_easy_setopt (c, CURLOPT_PORT, (long)global_port); curl_easy_setopt (c, CURLOPT_WRITEFUNCTION, &copyBuffer); curl_easy_setopt (c, CURLOPT_WRITEDATA, &cbc); @@ -348,7 +397,10 @@ testExternalGet () cbc.size = 2048; cbc.pos = 0; d = MHD_start_daemon (MHD_USE_ERROR_LOG, - global_port, NULL, NULL, &ahc_echo, "GET", MHD_OPTION_END); + global_port, NULL, NULL, + &ahc_echo, "GET", + MHD_OPTION_URI_LOG_CALLBACK, &log_cb, NULL, + MHD_OPTION_END); if (d == NULL) return 256; if (0 == global_port) @@ -360,7 +412,7 @@ testExternalGet () global_port = (int)dinfo->port; } c = curl_easy_init (); - curl_easy_setopt (c, CURLOPT_URL, "http://127.0.0.1/hello_world"); + curl_easy_setopt (c, CURLOPT_URL, "http://127.0.0.1" EXPECTED_URI_PATH); curl_easy_setopt (c, CURLOPT_PORT, (long)global_port); curl_easy_setopt (c, CURLOPT_WRITEFUNCTION, &copyBuffer); curl_easy_setopt (c, CURLOPT_WRITEDATA, &cbc); @@ -494,6 +546,7 @@ testUnknownPortGet (int poll_flag) d = MHD_start_daemon (MHD_USE_INTERNAL_POLLING_THREAD | MHD_USE_ERROR_LOG | poll_flag, 0, NULL, NULL, &ahc_echo, "GET", MHD_OPTION_SOCK_ADDR, &addr, + MHD_OPTION_URI_LOG_CALLBACK, &log_cb, NULL, MHD_OPTION_END); if (MHD_NO == MHD_is_feature_supported (MHD_FEATURE_AUTODETECT_BIND_PORT)) { @@ -517,8 +570,11 @@ testUnknownPortGet (int poll_flag) port = (int)dinfo->port; } - snprintf(buf, sizeof(buf), "http://127.0.0.1:%d/hello_world", - port); + snprintf(buf, + sizeof(buf), + "http://127.0.0.1:%d%s", + port, + EXPECTED_URI_PATH); c = curl_easy_init (); curl_easy_setopt (c, CURLOPT_URL, buf); @@ -570,7 +626,10 @@ testStopRace (int poll_flag) } d = MHD_start_daemon (MHD_USE_THREAD_PER_CONNECTION | MHD_USE_INTERNAL_POLLING_THREAD | MHD_USE_ERROR_LOG | poll_flag, - global_port, NULL, NULL, &ahc_echo, "GET", MHD_OPTION_END); + global_port, NULL, NULL, + &ahc_echo, "GET", + MHD_OPTION_URI_LOG_CALLBACK, &log_cb, NULL, + MHD_OPTION_END); if (d == NULL) return 16; if (0 == global_port) @@ -686,7 +745,10 @@ testEmptyGet (int poll_flag) cbc.size = 2048; cbc.pos = 0; d = MHD_start_daemon (MHD_USE_INTERNAL_POLLING_THREAD | MHD_USE_ERROR_LOG | poll_flag, - global_port, NULL, NULL, &ahc_empty, NULL, MHD_OPTION_END); + global_port, NULL, NULL, + &ahc_empty, NULL, + MHD_OPTION_URI_LOG_CALLBACK, &log_cb, NULL, + MHD_OPTION_END); if (d == NULL) return 4194304; if (0 == global_port) @@ -698,7 +760,7 @@ testEmptyGet (int poll_flag) global_port = (int)dinfo->port; } c = curl_easy_init (); - curl_easy_setopt (c, CURLOPT_URL, "http://127.0.0.1/hello_world"); + curl_easy_setopt (c, CURLOPT_URL, "http://127.0.0.1" EXPECTED_URI_PATH); curl_easy_setopt (c, CURLOPT_PORT, (long)global_port); curl_easy_setopt (c, CURLOPT_WRITEFUNCTION, &copyBuffer); curl_easy_setopt (c, CURLOPT_WRITEDATA, &cbc); diff --git a/src/testcurl/test_process_headers.c b/src/testcurl/test_process_headers.c @@ -65,8 +65,13 @@ copyBuffer (void *ptr, size_t size, size_t nmemb, void *ctx) return size * nmemb; } + static int -kv_cb (void *cls, enum MHD_ValueKind kind, const char *key, const char *value) +kv_cb (void *cls, + enum MHD_ValueKind kind, + const char *key, + const char *value, + size_t value_size) { if ((0 == strcmp (key, MHD_HTTP_HEADER_HOST)) && (0 == strncmp (value, "127.0.0.1", strlen("127.0.0.1"))) && (kind == MHD_HEADER_KIND)) @@ -77,6 +82,7 @@ kv_cb (void *cls, enum MHD_ValueKind kind, const char *key, const char *value) return MHD_YES; } + static int ahc_echo (void *cls, struct MHD_Connection *connection, diff --git a/src/testcurl/test_urlparse.c b/src/testcurl/test_urlparse.c @@ -67,13 +67,18 @@ copyBuffer (void *ptr, size_t size, size_t nmemb, void *ctx) return size * nmemb; } + static int test_values (void *cls, enum MHD_ValueKind kind, const char *key, - const char *value) + const char *value, + size_t value_size) { - (void)cls;(void)kind; /* Unused. Silent compiler warning. */ + (void) cls; + (void) kind; + (void) value_size; + if ( (0 == strcmp (key, "a")) && (0 == strcmp (value, "b")) ) matches += 1;