summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorng0 <ng0@n0.is>2019-08-01 14:28:34 +0000
committerng0 <ng0@n0.is>2019-08-01 14:28:34 +0000
commit8ad5fab2e8173476b78c3fda9a416540b925cd0f (patch)
tree511af471e5c12c749792228281fcecb9e510e78f
parent9edfc8f8f7a548e0dad8f3d18f671b822601a384 (diff)
parent91826f50011e76014dc307e370e6b1f45b2df151 (diff)
Merge remote-tracking branch 'origin/master' into dev/ng0/gsoc2019
-rw-r--r--ChangeLog23
-rw-r--r--configure.ac6
-rw-r--r--doc/libmicrohttpd.texi2
-rw-r--r--po/libmicrohttpd.pot297
-rw-r--r--src/include/microhttpd.h2
-rw-r--r--src/microhttpd/.gitignore2
-rw-r--r--src/microhttpd/Makefile.am34
-rw-r--r--src/microhttpd/connection.c30
-rw-r--r--src/microhttpd/daemon.c310
-rw-r--r--src/microhttpd/response.c80
-rw-r--r--src/microhttpd/test_upgrade_large.c1386
11 files changed, 1811 insertions, 361 deletions
diff --git a/ChangeLog b/ChangeLog
index 6a057b73..92161c6f 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,6 +1,23 @@
+Thu 01 Aug 2019 01:23:36 PM CEST
+ Releasing libmicrohttpd 0.9.66. -CG
+
+Thu 01 Aug 2019 12:53:49 AM CEST
+ Fix issue with discarding unhandled upload data discovered
+ by Florian Dold. -CG
+
+Mon 29 Jul 2019 08:01:50 PM CEST
+ Fix hanging situation with large transmission over upgraded
+ (i.e. Web socket) connection with epoll() and HTTPS enabled
+ (as reported by Viet on the mailinglist). -CG
+
+Thu 25 Jul 2019 02:40:12 PM CEST
+ Fixing regression introduced in cc5032b85 (bit mask matching
+ of the header kinds in MHD_lookup_connection_value()), as
+ reported by Jose Bollo on the mailinglist. -CG/JB
+
Tue Jul 16 19:56:14 CEST 2019
- Add MHD_OPTION_HTTPS_CERT_CALLBACK2 to allow OCSP stapling
- and MHD_FEATURE_HTTPS_CERT_CALLBACK2 to check for. -TR
+ Add MHD_OPTION_HTTPS_CERT_CALLBACK2 to allow OCSP stapling
+ and MHD_FEATURE_HTTPS_CERT_CALLBACK2 to check for. -TR
Fri Jul 05 2019 22:30:40 MSK
Releasing libmicrohttpd 0.9.65. -EG
@@ -102,7 +119,7 @@ Sun Apr 21 16:40:00 MSK 2019
Fri Apr 19 23:00:00 MSK 2019
Rewritten SHA-256 calculations from scratch to avoid changing LGPL version;
- Added usage of GCC/Clang built-ins for bytes swap to significantly improve
+ Added usage of GCC/Clang built-ins for bytes swap to significantly improve
speed of MD5 and SHA-256 calculation on platforms with known endianness.
Added test for SHA-256 calculations. -EG
diff --git a/configure.ac b/configure.ac
index 8e4f4a9b..1a0870b7 100644
--- 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.65],[libmicrohttpd@gnu.org])
+AC_INIT([GNU Libmicrohttpd],[0.9.66],[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=64
+LIB_VERSION_CURRENT=65
LIB_VERSION_REVISION=0
-LIB_VERSION_AGE=52
+LIB_VERSION_AGE=53
AC_SUBST(LIB_VERSION_CURRENT)
AC_SUBST(LIB_VERSION_REVISION)
AC_SUBST(LIB_VERSION_AGE)
diff --git a/doc/libmicrohttpd.texi b/doc/libmicrohttpd.texi
index 6f34d799..4424337f 100644
--- a/doc/libmicrohttpd.texi
+++ b/doc/libmicrohttpd.texi
@@ -12,7 +12,7 @@ This manual is for GNU libmicrohttpd
(version @value{VERSION}, @value{UPDATED}), a library for embedding
an HTTP(S) server into C applications.
-Copyright @copyright{} 2007--2017 Christian Grothoff
+Copyright @copyright{} 2007--2019 Christian Grothoff
@quotation
Permission is granted to copy, distribute and/or modify this document
diff --git a/po/libmicrohttpd.pot b/po/libmicrohttpd.pot
index feb037c6..0f089638 100644
--- a/po/libmicrohttpd.pot
+++ b/po/libmicrohttpd.pot
@@ -6,9 +6,9 @@
#, fuzzy
msgid ""
msgstr ""
-"Project-Id-Version: GNU libmicrohttpd 0.9.65\n"
+"Project-Id-Version: GNU libmicrohttpd 0.9.66\n"
"Report-Msgid-Bugs-To: libmicrohttpd@gnu.org\n"
-"POT-Creation-Date: 2019-07-05 22:27+0300\n"
+"POT-Creation-Date: 2019-08-01 13:26+0200\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"
@@ -104,513 +104,518 @@ msgstr ""
msgid "Previously-added IP address had counter of zero\n"
msgstr ""
-#: src/microhttpd/daemon.c:614
+#: src/microhttpd/daemon.c:622
msgid ""
"Failed to setup x509 certificate/key: pre 3.X.X version of GnuTLS does not "
"support setting key password"
msgstr ""
-#: src/microhttpd/daemon.c:668
+#: src/microhttpd/daemon.c:680
#, c-format
msgid "Error: invalid credentials type %d specified.\n"
msgstr ""
-#: src/microhttpd/daemon.c:1067
+#: src/microhttpd/daemon.c:1079
#, c-format
msgid "Maximum socket in select set: %d\n"
msgstr ""
-#: src/microhttpd/daemon.c:1128
+#: src/microhttpd/daemon.c:1140
msgid ""
"MHD_get_fdset2() called with except_fd_set set to NULL. Such behavior is "
"unsupported.\n"
msgstr ""
-#: src/microhttpd/daemon.c:1334 src/microhttpd/daemon.c:6573
+#: src/microhttpd/daemon.c:1346 src/microhttpd/daemon.c:6611
msgid ""
"Initiated daemon shutdown while \"upgraded\" connection was not closed.\n"
msgstr ""
-#: src/microhttpd/daemon.c:1348 src/microhttpd/daemon.c:1583
+#: src/microhttpd/daemon.c:1360 src/microhttpd/daemon.c:1595
msgid "Failed to forward to application "
msgstr ""
-#: src/microhttpd/daemon.c:1515 src/microhttpd/daemon.c:1637
+#: src/microhttpd/daemon.c:1527 src/microhttpd/daemon.c:1649
msgid "Failed to forward to remote client "
msgstr ""
-#: src/microhttpd/daemon.c:1702
+#: src/microhttpd/daemon.c:1714
msgid "Error preparing select\n"
msgstr ""
-#: src/microhttpd/daemon.c:1736 src/microhttpd/daemon.c:1888
-#: src/microhttpd/daemon.c:2032
+#: src/microhttpd/daemon.c:1748 src/microhttpd/daemon.c:1900
+#: src/microhttpd/daemon.c:2044
#, c-format
msgid "Error during select (%d): `%s'\n"
msgstr ""
-#: src/microhttpd/daemon.c:1785 src/microhttpd/daemon.c:1909
-#: src/microhttpd/daemon.c:2101
+#: src/microhttpd/daemon.c:1797 src/microhttpd/daemon.c:1921
+#: src/microhttpd/daemon.c:2113
#, c-format
msgid "Error during poll: `%s'\n"
msgstr ""
-#: src/microhttpd/daemon.c:1872 src/microhttpd/daemon.c:2014
+#: src/microhttpd/daemon.c:1884 src/microhttpd/daemon.c:2026
msgid "Failed to add FD to fd_set\n"
msgstr ""
-#: src/microhttpd/daemon.c:2153
+#: src/microhttpd/daemon.c:2165
msgid "Processing thread terminating. Closing connection\n"
msgstr ""
-#: src/microhttpd/daemon.c:2182
+#: src/microhttpd/daemon.c:2194
msgid ""
"Failed to signal thread termination via inter-thread communication channel."
msgstr ""
-#: src/microhttpd/daemon.c:2253
+#: src/microhttpd/daemon.c:2265
msgid "Internal server error. This should be impossible.\n"
msgstr ""
-#: src/microhttpd/daemon.c:2263 src/microhttpd/daemon.c:2300
+#: src/microhttpd/daemon.c:2275 src/microhttpd/daemon.c:2312
msgid "PSK not supported by this server.\n"
msgstr ""
-#: src/microhttpd/daemon.c:2277
+#: src/microhttpd/daemon.c:2289
msgid "PSK authentication failed: gnutls_malloc failed to allocate memory\n"
msgstr ""
-#: src/microhttpd/daemon.c:2286
+#: src/microhttpd/daemon.c:2298
msgid "PSK authentication failed: PSK too long\n"
msgstr ""
-#: src/microhttpd/daemon.c:2382 src/microhttpd/daemon.c:6217
+#: src/microhttpd/daemon.c:2394 src/microhttpd/daemon.c:6255
#, c-format
msgid "Socket descriptor larger than FD_SETSIZE: %d > %d\n"
msgstr ""
-#: src/microhttpd/daemon.c:2398
+#: src/microhttpd/daemon.c:2410
#, c-format
msgid "Failed to set SO_NOSIGPIPE on accepted socket: %s\n"
msgstr ""
-#: src/microhttpd/daemon.c:2415 src/microhttpd/daemon.c:3238
+#: src/microhttpd/daemon.c:2427 src/microhttpd/daemon.c:3251
#, c-format
msgid "Accepted connection on socket %d\n"
msgstr ""
-#: src/microhttpd/daemon.c:2427 src/microhttpd/daemon.c:2617
+#: src/microhttpd/daemon.c:2439 src/microhttpd/daemon.c:2629
msgid "Server reached connection limit. Closing inbound connection.\n"
msgstr ""
-#: src/microhttpd/daemon.c:2445
+#: src/microhttpd/daemon.c:2457
msgid "Connection rejected by application. Closing connection.\n"
msgstr ""
-#: src/microhttpd/daemon.c:2478 src/microhttpd/daemon.c:2498
-#: src/microhttpd/daemon.c:3824
+#: src/microhttpd/daemon.c:2490 src/microhttpd/daemon.c:2510
+#: src/microhttpd/daemon.c:3837
#, c-format
msgid "Error allocating memory: %s\n"
msgstr ""
-#: src/microhttpd/daemon.c:2570
+#: src/microhttpd/daemon.c:2582
#, c-format
msgid "Failed to setup TLS credentials: unknown credential type %d\n"
msgstr ""
-#: src/microhttpd/daemon.c:2579
+#: src/microhttpd/daemon.c:2591
msgid "Unknown credential type"
msgstr ""
-#: src/microhttpd/daemon.c:2681 src/microhttpd/daemon.c:4314
-#: src/microhttpd/daemon.c:4347 src/microhttpd/daemon.c:5509
-#: src/microhttpd/daemon.c:5526 src/microhttpd/connection.c:4130
+#: src/microhttpd/daemon.c:2693 src/microhttpd/daemon.c:4328
+#: src/microhttpd/daemon.c:4361 src/microhttpd/daemon.c:5547
+#: src/microhttpd/daemon.c:5564 src/microhttpd/connection.c:4142
#: src/microhttpd/response.c:1003 src/microhttpd/response.c:1029
#, c-format
msgid "Call to epoll_ctl failed: %s\n"
msgstr ""
-#: src/microhttpd/daemon.c:2706
+#: src/microhttpd/daemon.c:2718
msgid "Failed to signal new connection via inter-thread communication channel."
msgstr ""
-#: src/microhttpd/daemon.c:2811 src/microhttpd/daemon.c:3322
-#: src/microhttpd/daemon.c:6463 src/microhttpd/connection.c:1190
+#: src/microhttpd/daemon.c:2823 src/microhttpd/daemon.c:3335
+#: src/microhttpd/daemon.c:6501 src/microhttpd/connection.c:1190
#: src/microhttpd/connection.c:1209
msgid "Failed to remove FD from epoll set\n"
msgstr ""
-#: src/microhttpd/daemon.c:2860
+#: src/microhttpd/daemon.c:2872
msgid "Cannot suspend connections without enabling MHD_ALLOW_SUSPEND_RESUME!\n"
msgstr ""
-#: src/microhttpd/daemon.c:2866
+#: src/microhttpd/daemon.c:2878
msgid "Error: connection scheduled for \"upgrade\" cannot be suspended"
msgstr ""
-#: src/microhttpd/daemon.c:2889
+#: src/microhttpd/daemon.c:2901
msgid "Cannot resume connections without enabling MHD_ALLOW_SUSPEND_RESUME!\n"
msgstr ""
-#: src/microhttpd/daemon.c:2903
+#: src/microhttpd/daemon.c:2915
msgid "Failed to signal resume via inter-thread communication channel."
msgstr ""
-#: src/microhttpd/daemon.c:3035
+#: src/microhttpd/daemon.c:3047
msgid ""
"Failed to signal resume of connection via inter-thread communication channel."
msgstr ""
-#: src/microhttpd/daemon.c:3081
+#: src/microhttpd/daemon.c:3094
#, c-format
msgid "Failed to set nonblocking mode on new client socket: %s\n"
msgstr ""
-#: src/microhttpd/daemon.c:3094
+#: src/microhttpd/daemon.c:3107
msgid "Failed to set noninheritable mode on new client socket.\n"
msgstr ""
-#: src/microhttpd/daemon.c:3103
+#: src/microhttpd/daemon.c:3116
msgid "Failed to reset buffering mode on new client socket.\n"
msgstr ""
-#: src/microhttpd/daemon.c:3176
+#: src/microhttpd/daemon.c:3189
#, c-format
msgid "Error accepting connection: %s\n"
msgstr ""
-#: src/microhttpd/daemon.c:3193
+#: src/microhttpd/daemon.c:3206
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:3207
+#: src/microhttpd/daemon.c:3220
#, 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:3219
+#: src/microhttpd/daemon.c:3232
#, c-format
msgid "Failed to set nonblocking mode on incoming connection socket: %s\n"
msgstr ""
-#: src/microhttpd/daemon.c:3231
+#: src/microhttpd/daemon.c:3244
msgid "Failed to set noninheritable mode on incoming connection socket.\n"
msgstr ""
-#: src/microhttpd/daemon.c:3279 src/microhttpd/daemon.c:6615
-#: src/microhttpd/daemon.c:6647 src/microhttpd/daemon.c:6747
+#: src/microhttpd/daemon.c:3292 src/microhttpd/daemon.c:6653
+#: src/microhttpd/daemon.c:6685 src/microhttpd/daemon.c:6785
msgid "Failed to join a thread\n"
msgstr ""
-#: src/microhttpd/daemon.c:3383
+#: src/microhttpd/daemon.c:3396
msgid "Illegal call to MHD_get_timeout\n"
msgstr ""
-#: src/microhttpd/daemon.c:3580
+#: src/microhttpd/daemon.c:3593
msgid ""
"MHD_run_from_select() called with except_fd_set set to NULL. Such behavior "
"is deprecated.\n"
msgstr ""
-#: src/microhttpd/daemon.c:3660
+#: src/microhttpd/daemon.c:3673
msgid "Could not obtain daemon fdsets"
msgstr ""
-#: src/microhttpd/daemon.c:3677
+#: src/microhttpd/daemon.c:3690
msgid "Could not add listen socket to fdset"
msgstr ""
-#: src/microhttpd/daemon.c:3705
+#: src/microhttpd/daemon.c:3718
msgid "Could not add control inter-thread communication channel FD to fdset"
msgstr ""
-#: src/microhttpd/daemon.c:3761
+#: src/microhttpd/daemon.c:3774
#, c-format
msgid "select failed: %s\n"
msgstr ""
-#: src/microhttpd/daemon.c:3906 src/microhttpd/daemon.c:4053
+#: src/microhttpd/daemon.c:3919 src/microhttpd/daemon.c:4066
#, c-format
msgid "poll failed: %s\n"
msgstr ""
-#: src/microhttpd/daemon.c:4183 src/microhttpd/daemon.c:4414
+#: src/microhttpd/daemon.c:4197 src/microhttpd/daemon.c:4428
#, c-format
msgid "Call to epoll_wait failed: %s\n"
msgstr ""
-#: src/microhttpd/daemon.c:4366 src/microhttpd/daemon.c:4823
+#: src/microhttpd/daemon.c:4380 src/microhttpd/daemon.c:4837
msgid "Failed to remove listen FD from epoll set\n"
msgstr ""
-#: src/microhttpd/daemon.c:4831
+#: src/microhttpd/daemon.c:4845
msgid "Failed to signal quiesce via inter-thread communication channel"
msgstr ""
-#: src/microhttpd/daemon.c:4853
+#: src/microhttpd/daemon.c:4867
msgid "failed to signal quiesce via inter-thread communication channel"
msgstr ""
-#: src/microhttpd/daemon.c:4964
+#: src/microhttpd/daemon.c:4981
msgid "Warning: Too large timeout value, ignored.\n"
msgstr ""
-#: src/microhttpd/daemon.c:5003
+#: src/microhttpd/daemon.c:5020
msgid ""
"Warning: Zero size, specified for thread pool size, is ignored. Thread pool "
"is not used.\n"
msgstr ""
-#: src/microhttpd/daemon.c:5011
+#: src/microhttpd/daemon.c:5028
msgid ""
"Warning: \"1\", specified for thread pool size, is ignored. Thread pool is "
"not used.\n"
msgstr ""
-#: src/microhttpd/daemon.c:5023
+#: src/microhttpd/daemon.c:5040
#, c-format
msgid "Specified thread pool size (%u) too big\n"
msgstr ""
-#: src/microhttpd/daemon.c:5034
+#: src/microhttpd/daemon.c:5051
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:5043
+#: src/microhttpd/daemon.c:5060
msgid ""
"Both MHD_OPTION_THREAD_POOL_SIZE option and MHD_USE_THREAD_PER_CONNECTION "
"flag are specified.\n"
msgstr ""
-#: src/microhttpd/daemon.c:5060 src/microhttpd/daemon.c:5072
-#: src/microhttpd/daemon.c:5084 src/microhttpd/daemon.c:5096
-#: src/microhttpd/daemon.c:5147 src/microhttpd/daemon.c:5175
-#: src/microhttpd/daemon.c:5194
+#: src/microhttpd/daemon.c:5077 src/microhttpd/daemon.c:5089
+#: src/microhttpd/daemon.c:5101 src/microhttpd/daemon.c:5113
+#: src/microhttpd/daemon.c:5164 src/microhttpd/daemon.c:5192
+#: src/microhttpd/daemon.c:5211 src/microhttpd/daemon.c:5231
#, c-format
msgid "MHD HTTPS option %d passed to MHD but MHD_USE_TLS not set\n"
msgstr ""
-#: src/microhttpd/daemon.c:5116
+#: src/microhttpd/daemon.c:5133
msgid "Error initializing DH parameters\n"
msgstr ""
-#: src/microhttpd/daemon.c:5126
+#: src/microhttpd/daemon.c:5143
msgid "Diffie-Hellman parameters string too long\n"
msgstr ""
-#: src/microhttpd/daemon.c:5137
+#: src/microhttpd/daemon.c:5154
msgid "Bad Diffie-Hellman parameters format\n"
msgstr ""
-#: src/microhttpd/daemon.c:5164
+#: src/microhttpd/daemon.c:5181
#, c-format
msgid "Setting priorities to `%s' failed: %s\n"
msgstr ""
-#: src/microhttpd/daemon.c:5183
+#: src/microhttpd/daemon.c:5200
msgid ""
"MHD_OPTION_HTTPS_CERT_CALLBACK requires building MHD with GnuTLS >= 3.0\n"
msgstr ""
-#: src/microhttpd/daemon.c:5217
+#: src/microhttpd/daemon.c:5220
+msgid ""
+"MHD_OPTION_HTTPS_CERT_CALLBACK2 requires building MHD with GnuTLS >= 3.6.3\n"
+msgstr ""
+
+#: src/microhttpd/daemon.c:5254
msgid ""
"MHD_OPTION_LISTEN_SOCKET specified for daemon with MHD_USE_NO_LISTEN_SOCKET "
"flag set.\n"
msgstr ""
-#: src/microhttpd/daemon.c:5253
+#: src/microhttpd/daemon.c:5290
msgid "TCP fastopen is not supported on this platform\n"
msgstr ""
-#: src/microhttpd/daemon.c:5272
+#: src/microhttpd/daemon.c:5309
msgid ""
"Flag MHD_USE_PEDANTIC_CHECKS is ignored because another behavior is "
"specified by MHD_OPTION_STRICT_CLIENT.\n"
msgstr ""
-#: src/microhttpd/daemon.c:5404
+#: src/microhttpd/daemon.c:5442
#, c-format
msgid "MHD HTTPS option %d passed to MHD compiled without GNUtls >= 3\n"
msgstr ""
-#: src/microhttpd/daemon.c:5417
+#: src/microhttpd/daemon.c:5455
#, c-format
msgid "MHD HTTPS option %d passed to MHD compiled without HTTPS support\n"
msgstr ""
-#: src/microhttpd/daemon.c:5423
+#: src/microhttpd/daemon.c:5461
#, c-format
msgid "Invalid option %d! (Did you terminate the list with MHD_OPTION_END?)\n"
msgstr ""
-#: src/microhttpd/daemon.c:5453
+#: src/microhttpd/daemon.c:5491
#, c-format
msgid "Call to epoll_create1 failed: %s\n"
msgstr ""
-#: src/microhttpd/daemon.c:5463
+#: src/microhttpd/daemon.c:5501
msgid "Failed to set noninheritable mode on epoll FD.\n"
msgstr ""
-#: src/microhttpd/daemon.c:5706
+#: src/microhttpd/daemon.c:5744
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:5754
+#: src/microhttpd/daemon.c:5792
msgid "Using debug build of libmicrohttpd.\n"
msgstr ""
-#: src/microhttpd/daemon.c:5768
+#: src/microhttpd/daemon.c:5806
#, c-format
msgid "Failed to create inter-thread communication channel: %s\n"
msgstr ""
-#: src/microhttpd/daemon.c:5784
+#: src/microhttpd/daemon.c:5822
msgid ""
"file descriptor for inter-thread communication channel exceeds maximum "
"value\n"
msgstr ""
-#: src/microhttpd/daemon.c:5804
+#: src/microhttpd/daemon.c:5842
msgid "Specified value for NC_SIZE too large\n"
msgstr ""
-#: src/microhttpd/daemon.c:5818
+#: src/microhttpd/daemon.c:5856
#, c-format
msgid "Failed to allocate memory for nonce-nc map: %s\n"
msgstr ""
-#: src/microhttpd/daemon.c:5835
+#: src/microhttpd/daemon.c:5873
msgid "MHD failed to initialize nonce-nc mutex\n"
msgstr ""
-#: src/microhttpd/daemon.c:5855
+#: src/microhttpd/daemon.c:5893
msgid "MHD thread pooling only works with MHD_USE_INTERNAL_POLLING_THREAD\n"
msgstr ""
-#: src/microhttpd/daemon.c:5879
+#: src/microhttpd/daemon.c:5917
#, c-format
msgid "Failed to create socket for listening: %s\n"
msgstr ""
-#: src/microhttpd/daemon.c:5900 src/microhttpd/daemon.c:5919
-#: src/microhttpd/daemon.c:5942 src/microhttpd/daemon.c:5979
-#: src/microhttpd/daemon.c:6056 src/microhttpd/daemon.c:6087
+#: src/microhttpd/daemon.c:5938 src/microhttpd/daemon.c:5957
+#: src/microhttpd/daemon.c:5980 src/microhttpd/daemon.c:6017
+#: src/microhttpd/daemon.c:6094 src/microhttpd/daemon.c:6125
#, c-format
msgid "setsockopt failed: %s\n"
msgstr ""
-#: src/microhttpd/daemon.c:5952
+#: src/microhttpd/daemon.c:5990
msgid "Cannot allow listening address reuse: SO_REUSEPORT not defined\n"
msgstr ""
-#: src/microhttpd/daemon.c:5987
+#: src/microhttpd/daemon.c:6025
msgid ""
"Cannot disallow listening address reuse: SO_EXCLUSIVEADDRUSE not defined\n"
msgstr ""
-#: src/microhttpd/daemon.c:6067
+#: src/microhttpd/daemon.c:6105
#, c-format
msgid "Failed to bind to port %u: %s\n"
msgstr ""
-#: src/microhttpd/daemon.c:6098
+#: src/microhttpd/daemon.c:6136
#, c-format
msgid "Failed to listen for connections: %s\n"
msgstr ""
-#: src/microhttpd/daemon.c:6129
+#: src/microhttpd/daemon.c:6167
#, c-format
msgid "Failed to get listen port number: %s\n"
msgstr ""
-#: src/microhttpd/daemon.c:6139
+#: src/microhttpd/daemon.c:6177
msgid ""
"Failed to get listen port number (`struct sockaddr_storage` too small!?)\n"
msgstr ""
-#: src/microhttpd/daemon.c:6180
+#: src/microhttpd/daemon.c:6218
msgid "Unknown address family!\n"
msgstr ""
-#: src/microhttpd/daemon.c:6194
+#: src/microhttpd/daemon.c:6232
#, c-format
msgid "Failed to set nonblocking mode on listening socket: %s\n"
msgstr ""
-#: src/microhttpd/daemon.c:6236
+#: src/microhttpd/daemon.c:6274
msgid ""
"Combining MHD_USE_THREAD_PER_CONNECTION and MHD_USE_EPOLL is not supported.\n"
msgstr ""
-#: src/microhttpd/daemon.c:6250 src/microhttpd/daemon.c:6263
+#: src/microhttpd/daemon.c:6288 src/microhttpd/daemon.c:6301
msgid "MHD failed to initialize IP connection limit mutex\n"
msgstr ""
-#: src/microhttpd/daemon.c:6282
+#: src/microhttpd/daemon.c:6320
msgid "Failed to initialize TLS support\n"
msgstr ""
-#: src/microhttpd/daemon.c:6309
+#: src/microhttpd/daemon.c:6347
#, c-format
msgid "Failed to create listen thread: %s\n"
msgstr ""
-#: src/microhttpd/daemon.c:6357
+#: src/microhttpd/daemon.c:6395
#, c-format
msgid "Failed to create worker inter-thread communication channel: %s\n"
msgstr ""
-#: src/microhttpd/daemon.c:6368
+#: src/microhttpd/daemon.c:6406
msgid ""
"File descriptor for worker inter-thread communication channel exceeds "
"maximum value\n"
msgstr ""
-#: src/microhttpd/daemon.c:6393
+#: src/microhttpd/daemon.c:6431
msgid "MHD failed to initialize cleanup connection mutex\n"
msgstr ""
-#: src/microhttpd/daemon.c:6407
+#: src/microhttpd/daemon.c:6445
#, c-format
msgid "Failed to create pool thread: %s\n"
msgstr ""
-#: src/microhttpd/daemon.c:6560 src/microhttpd/daemon.c:6591
+#: src/microhttpd/daemon.c:6598 src/microhttpd/daemon.c:6629
msgid "MHD_stop_daemon() called while we have suspended connections.\n"
msgstr ""
-#: src/microhttpd/daemon.c:6600 src/microhttpd/daemon.c:6729
+#: src/microhttpd/daemon.c:6638 src/microhttpd/daemon.c:6767
msgid "Failed to signal shutdown via inter-thread communication channel"
msgstr ""
-#: src/microhttpd/daemon.c:6692
+#: src/microhttpd/daemon.c:6730
msgid "Failed to signal shutdown via inter-thread communication channel."
msgstr ""
-#: src/microhttpd/daemon.c:7159
+#: src/microhttpd/daemon.c:7203
msgid "Failed to initialize winsock\n"
msgstr ""
-#: src/microhttpd/daemon.c:7162
+#: src/microhttpd/daemon.c:7206
msgid "Winsock version 2.2 is not available\n"
msgstr ""
-#: src/microhttpd/daemon.c:7170 src/microhttpd/daemon.c:7174
+#: src/microhttpd/daemon.c:7214 src/microhttpd/daemon.c:7218
msgid "Failed to initialise multithreading in libgcrypt\n"
msgstr ""
-#: src/microhttpd/daemon.c:7179
+#: src/microhttpd/daemon.c:7223
msgid "libgcrypt is too old. MHD was compiled for libgcrypt 1.6.0 or newer\n"
msgstr ""
@@ -637,17 +642,17 @@ msgid ""
"connection.\n"
msgstr ""
-#: src/microhttpd/connection.c:2055 src/microhttpd/connection.c:3068
+#: src/microhttpd/connection.c:2055 src/microhttpd/connection.c:3080
msgid "Closing connection (failed to queue response)\n"
msgstr ""
-#: src/microhttpd/connection.c:2065 src/microhttpd/connection.c:3842
-#: src/microhttpd/connection.c:3965
+#: src/microhttpd/connection.c:2065 src/microhttpd/connection.c:3854
+#: src/microhttpd/connection.c:3977
msgid "Closing connection (failed to create response header)\n"
msgstr ""
-#: src/microhttpd/connection.c:2111 src/microhttpd/connection.c:3223
-#: src/microhttpd/connection.c:3291 src/microhttpd/connection.c:3607
+#: src/microhttpd/connection.c:2111 src/microhttpd/connection.c:3235
+#: src/microhttpd/connection.c:3303 src/microhttpd/connection.c:3619
#, c-format
msgid "In function %s handling connection at state: %s\n"
msgstr ""
@@ -660,96 +665,96 @@ msgstr ""
msgid "Not enough memory in pool to parse cookies!\n"
msgstr ""
-#: src/microhttpd/connection.c:2605 src/microhttpd/connection.c:2790
+#: src/microhttpd/connection.c:2605 src/microhttpd/connection.c:2802
msgid "Application reported internal error, closing connection.\n"
msgstr ""
-#: src/microhttpd/connection.c:2658 src/microhttpd/connection.c:2735
+#: src/microhttpd/connection.c:2670 src/microhttpd/connection.c:2747
msgid ""
"Received malformed HTTP request (bad chunked encoding). Closing connection.\n"
msgstr ""
-#: src/microhttpd/connection.c:2798
+#: src/microhttpd/connection.c:2810
msgid "libmicrohttpd API violation"
msgstr ""
-#: src/microhttpd/connection.c:2813
+#: src/microhttpd/connection.c:2825
msgid ""
"WARNING: incomplete upload processing and connection not suspended may "
"result in hung connection.\n"
msgstr ""
-#: src/microhttpd/connection.c:2884
+#: src/microhttpd/connection.c:2896
msgid "Received malformed line (no colon). Closing connection.\n"
msgstr ""
-#: src/microhttpd/connection.c:3046
+#: src/microhttpd/connection.c:3058
msgid "Received HTTP 1.1 request without `Host' header.\n"
msgstr ""
-#: src/microhttpd/connection.c:3057
+#: src/microhttpd/connection.c:3069
msgid "Closing connection (failed to create response)\n"
msgstr ""
-#: src/microhttpd/connection.c:3202
+#: src/microhttpd/connection.c:3214
msgid "Socket disconnected while reading request.\n"
msgstr ""
-#: src/microhttpd/connection.c:3208
+#: src/microhttpd/connection.c:3220
msgid "Connection socket is closed due to error when reading request.\n"
msgstr ""
-#: src/microhttpd/connection.c:3317
+#: src/microhttpd/connection.c:3329
#, c-format
msgid "Failed to send data in request for %s.\n"
msgstr ""
-#: src/microhttpd/connection.c:3326
+#: src/microhttpd/connection.c:3338
#, c-format
msgid "Sent 100 continue response: `%.*s'\n"
msgstr ""
-#: src/microhttpd/connection.c:3350
+#: src/microhttpd/connection.c:3362
msgid "Connection was closed while sending response headers.\n"
msgstr ""
-#: src/microhttpd/connection.c:3391
+#: src/microhttpd/connection.c:3403
msgid "Data offset exceeds limit"
msgstr ""
-#: src/microhttpd/connection.c:3400
+#: src/microhttpd/connection.c:3412
#, c-format
msgid "Sent %d-byte DATA response: `%.*s'\n"
msgstr ""
-#: src/microhttpd/connection.c:3417
+#: src/microhttpd/connection.c:3429
#, c-format
msgid "Failed to send data in request for `%s'.\n"
msgstr ""
-#: src/microhttpd/connection.c:3445 src/microhttpd/connection.c:3473
+#: src/microhttpd/connection.c:3457 src/microhttpd/connection.c:3485
msgid "Connection was closed while sending response body.\n"
msgstr ""
-#: src/microhttpd/connection.c:3496
+#: src/microhttpd/connection.c:3508
msgid "Internal error\n"
msgstr ""
-#: src/microhttpd/connection.c:3569
+#: src/microhttpd/connection.c:3581
msgid ""
"Failed to signal end of connection via inter-thread communication channel"
msgstr ""
-#: src/microhttpd/connection.c:4316
+#: src/microhttpd/connection.c:4328
msgid "Attempted to queue response on wrong thread!\n"
msgstr ""
-#: src/microhttpd/connection.c:4327
+#: src/microhttpd/connection.c:4339
msgid ""
"Attempted 'upgrade' connection on daemon without MHD_ALLOW_UPGRADE option!\n"
msgstr ""
-#: src/microhttpd/connection.c:4336
+#: src/microhttpd/connection.c:4348
msgid "Application used invalid status code for 'upgrade' response!\n"
msgstr ""
diff --git a/src/include/microhttpd.h b/src/include/microhttpd.h
index 9d28cdb1..c2fc90a4 100644
--- a/src/include/microhttpd.h
+++ b/src/include/microhttpd.h
@@ -132,7 +132,7 @@ typedef intptr_t ssize_t;
* Current version of the library.
* 0x01093001 = 1.9.30-1.
*/
-#define MHD_VERSION 0x00096502
+#define MHD_VERSION 0x00096600
/**
* MHD-internal return code for "YES".
diff --git a/src/microhttpd/.gitignore b/src/microhttpd/.gitignore
index ec2e027d..611f88ec 100644
--- a/src/microhttpd/.gitignore
+++ b/src/microhttpd/.gitignore
@@ -57,3 +57,5 @@ test_shutdown_poll
test_shutdown_select
test_md5
test_sha256
+test_upgrade_large
+test_upgrade_large_tls
diff --git a/src/microhttpd/Makefile.am b/src/microhttpd/Makefile.am
index 7a484371..597a2d56 100644
--- a/src/microhttpd/Makefile.am
+++ b/src/microhttpd/Makefile.am
@@ -166,17 +166,17 @@ check_PROGRAMS = \
if HAVE_POSIX_THREADS
if ENABLE_UPGRADE
if USE_POSIX_THREADS
- check_PROGRAMS += test_upgrade
+ check_PROGRAMS += test_upgrade test_upgrade_large
endif
if USE_W32_THREADS
- check_PROGRAMS += test_upgrade
+ check_PROGRAMS += test_upgrade test_upgrade_large
endif
if ENABLE_HTTPS
if USE_POSIX_THREADS
-check_PROGRAMS += test_upgrade_tls
+check_PROGRAMS += test_upgrade_tls test_upgrade_large_tls
endif
if USE_W32_THREADS
-check_PROGRAMS += test_upgrade_tls
+check_PROGRAMS += test_upgrade_tls test_upgrade_large_tls
endif
endif
endif
@@ -231,6 +231,19 @@ test_upgrade_LDADD = \
$(MHD_TLS_LIB_LDFLAGS) $(MHD_TLS_LIBDEPS) \
$(PTHREAD_LIBS)
+test_upgrade_large_SOURCES = \
+ test_upgrade_large.c test_helpers.h mhd_sockets.h
+test_upgrade_large_CPPFLAGS = \
+ $(AM_CPPFLAGS) $(MHD_TLS_LIB_CPPFLAGS)
+test_upgrade_large_CFLAGS = \
+ $(AM_CFLAGS) $(PTHREAD_CFLAGS) $(MHD_TLS_LIB_CFLAGS)
+test_upgrade_large_LDFLAGS = \
+ $(MHD_TLS_LIB_LDFLAGS)
+test_upgrade_large_LDADD = \
+ $(top_builddir)/src/microhttpd/libmicrohttpd.la \
+ $(MHD_TLS_LIB_LDFLAGS) $(MHD_TLS_LIBDEPS) \
+ $(PTHREAD_LIBS)
+
test_upgrade_tls_SOURCES = \
test_upgrade.c test_helpers.h mhd_sockets.h
test_upgrade_tls_CPPFLAGS = \
@@ -244,6 +257,19 @@ test_upgrade_tls_LDADD = \
$(MHD_TLS_LIB_LDFLAGS) $(MHD_TLS_LIBDEPS) \
$(PTHREAD_LIBS)
+test_upgrade_large_tls_SOURCES = \
+ test_upgrade_large.c test_helpers.h mhd_sockets.h
+test_upgrade_large_tls_CPPFLAGS = \
+ $(AM_CPPFLAGS) $(MHD_TLS_LIB_CPPFLAGS)
+test_upgrade_large_tls_CFLAGS = \
+ $(AM_CFLAGS) $(PTHREAD_CFLAGS) $(MHD_TLS_LIB_CFLAGS)
+test_upgrade_large_tls_LDFLAGS = \
+ $(MHD_TLS_LIB_LDFLAGS)
+test_upgrade_large_tls_LDADD = \
+ $(top_builddir)/src/microhttpd/libmicrohttpd.la \
+ $(MHD_TLS_LIB_LDFLAGS) $(MHD_TLS_LIBDEPS) \
+ $(PTHREAD_LIBS)
+
test_postprocessor_SOURCES = \
test_postprocessor.c
test_postprocessor_CPPFLAGS = \
diff --git a/src/microhttpd/connection.c b/src/microhttpd/connection.c
index 643ef52a..dab28878 100644
--- a/src/microhttpd/connection.c
+++ b/src/microhttpd/connection.c
@@ -800,7 +800,7 @@ MHD_lookup_connection_value_n (struct MHD_Connection *connection,
{
for (pos = connection->headers_received; NULL != pos; pos = pos->next)
{
- if ( (kind == pos->kind) &&
+ if ( (0 != (kind & pos->kind)) &&
(NULL == pos->header) )
break;
}
@@ -809,7 +809,7 @@ MHD_lookup_connection_value_n (struct MHD_Connection *connection,
{
for (pos = connection->headers_received; NULL != pos; pos = pos->next)
{
- if ( (kind == pos->kind) &&
+ if ( (0 != (kind & pos->kind)) &&
(key_size == pos->header_size) &&
( (key == pos->header) ||
(MHD_str_equal_caseless_bin_n_ (key,
@@ -2454,7 +2454,19 @@ process_request_body (struct MHD_Connection *connection)
char *buffer_head;
if (NULL != connection->response)
- return; /* already queued a response */
+ {
+ /* already queued a response, discard remaining upload
+ (but not more, there might be another request after it) */
+ uint64_t purge = MHD_MIN (connection->remaining_upload_size,
+ connection->read_buffer_offset);
+ connection->remaining_upload_size -= purge;
+ if (connection->read_buffer_offset > purge)
+ memmove (connection->read_buffer,
+ &connection->read_buffer[purge],
+ connection->read_buffer_offset - purge);
+ connection->read_buffer_offset -= purge;
+ return;
+ }
buffer_head = connection->read_buffer;
available = connection->read_buffer_offset;
@@ -2589,19 +2601,19 @@ process_request_body (struct MHD_Connection *connection)
{
/* 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) )
- {
+ (MHD_SIZE_UNKNOWN != connection->remaining_upload_size) &&
+ (connection->remaining_upload_size < available) )
+ {
to_be_processed = (size_t)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.
*/
to_be_processed = available;
- }
+ }
}
left_unprocessed = to_be_processed;
connection->client_aware = true;
diff --git a/src/microhttpd/daemon.c b/src/microhttpd/daemon.c
index d3595fe0..bf01ba9b 100644
--- a/src/microhttpd/daemon.c
+++ b/src/microhttpd/daemon.c
@@ -721,8 +721,8 @@ int
MHD_get_fdset (struct MHD_Daemon *daemon,
fd_set *read_fd_set,
fd_set *write_fd_set,
- fd_set *except_fd_set,
- MHD_socket *max_fd)
+ fd_set *except_fd_set,
+ MHD_socket *max_fd)
{
return MHD_get_fdset2 (daemon,
read_fd_set,
@@ -2344,11 +2344,11 @@ psk_gnutls_adapter (gnutls_session_t session,
*/
static int
internal_add_connection (struct MHD_Daemon *daemon,
- MHD_socket client_socket,
- const struct sockaddr *addr,
- socklen_t addrlen,
- bool external_add,
- bool non_blck)
+ MHD_socket client_socket,
+ const struct sockaddr *addr,
+ socklen_t addrlen,
+ bool external_add,
+ bool non_blck)
{
struct MHD_Connection *connection;
#if defined(MHD_USE_POSIX_THREADS) || defined(MHD_USE_W32_THREADS)
@@ -2362,8 +2362,8 @@ internal_add_connection (struct MHD_Daemon *daemon,
if ((external_add) && (NULL != daemon->worker_pool))
{
/* have a pool, try to find a pool with capacity; we use the
- socket as the initial offset into the pool for load
- balancing */
+ socket as the initial offset into the pool for load
+ balancing */
for (i = 0; i < daemon->worker_pool_size; ++i)
{
struct MHD_Daemon * const worker =
@@ -2448,7 +2448,7 @@ internal_add_connection (struct MHD_Daemon *daemon,
/* apply connection acceptance policy if present */
if ( (NULL != daemon->apc) &&
(MHD_NO == daemon->apc (daemon->apc_cls,
- addr,
+ addr,
addrlen)) )
{
#if DEBUG_CLOSE
@@ -2472,8 +2472,8 @@ internal_add_connection (struct MHD_Daemon *daemon,
eno = errno;
#ifdef HAVE_MESSAGES
MHD_DLOG (daemon,
- "Error allocating memory: %s\n",
- MHD_strerror_ (errno));
+ "Error allocating memory: %s\n",
+ MHD_strerror_ (errno));
#endif
MHD_socket_close_chk_ (client_socket);
MHD_ip_limit_del (daemon,
@@ -2487,8 +2487,8 @@ internal_add_connection (struct MHD_Daemon *daemon,
{
#ifdef HAVE_MESSAGES
MHD_DLOG (daemon,
- _("Error allocating memory: %s\n"),
- MHD_strerror_ (errno));
+ _("Error allocating memory: %s\n"),
+ MHD_strerror_ (errno));
#endif
MHD_socket_close_chk_ (client_socket);
MHD_ip_limit_del (daemon,
@@ -2507,8 +2507,8 @@ internal_add_connection (struct MHD_Daemon *daemon,
eno = errno;
#ifdef HAVE_MESSAGES
MHD_DLOG (daemon,
- _("Error allocating memory: %s\n"),
- MHD_strerror_ (errno));
+ _("Error allocating memory: %s\n"),
+ MHD_strerror_ (errno));
#endif
MHD_socket_close_chk_ (client_socket);
MHD_ip_limit_del (daemon,
@@ -2566,8 +2566,8 @@ internal_add_connection (struct MHD_Daemon *daemon,
/* set needed credentials for certificate authentication. */
case GNUTLS_CRD_CERTIFICATE:
gnutls_credentials_set (connection->tls_session,
- GNUTLS_CRD_CERTIFICATE,
- daemon->x509_cred);
+ GNUTLS_CRD_CERTIFICATE,
+ daemon->x509_cred);
break;
case GNUTLS_CRD_PSK:
gnutls_credentials_set (connection->tls_session,
@@ -2596,18 +2596,18 @@ internal_add_connection (struct MHD_Daemon *daemon,
}
#if (GNUTLS_VERSION_NUMBER+0 >= 0x030109) && !defined(_WIN64)
gnutls_transport_set_int (connection->tls_session,
- (int)(client_socket));
+ (int)(client_socket));
#else /* GnuTLS before 3.1.9 or Win x64 */
gnutls_transport_set_ptr (connection->tls_session,
- (gnutls_transport_ptr_t)(intptr_t)(client_socket));
+ (gnutls_transport_ptr_t)(intptr_t)(client_socket));
#endif /* GnuTLS before 3.1.9 */
#ifdef MHD_TLSLIB_NEED_PUSH_FUNC
gnutls_transport_set_push_function (connection->tls_session,
- MHD_tls_push_func_);
+ MHD_tls_push_func_);
#endif /* MHD_TLSLIB_NEED_PUSH_FUNC */
if (daemon->https_mem_trust)
gnutls_certificate_server_set_request (connection->tls_session,
- GNUTLS_CERT_REQUEST);
+ GNUTLS_CERT_REQUEST);
#else /* ! HTTPS_SUPPORT */
eno = EINVAL;
goto cleanup;
@@ -2641,8 +2641,8 @@ internal_add_connection (struct MHD_Daemon *daemon,
connection);
}
DLL_insert (daemon->connections_head,
- daemon->connections_tail,
- connection);
+ daemon->connections_tail,
+ connection);
#if defined(MHD_USE_POSIX_THREADS) || defined(MHD_USE_W32_THREADS)
MHD_mutex_unlock_chk_ (&daemon->cleanup_connection_mutex);
#endif
@@ -2661,13 +2661,13 @@ internal_add_connection (struct MHD_Daemon *daemon,
&thread_main_handle_connection,
connection))
{
- eno = errno;
+ eno = errno;
#ifdef HAVE_MESSAGES
MHD_DLOG (daemon,
"Failed to create a thread: %s\n",
MHD_strerror_ (eno));
#endif
- goto cleanup;
+ goto cleanup;
}
}
else
@@ -2683,9 +2683,9 @@ internal_add_connection (struct MHD_Daemon *daemon,
event.events = EPOLLIN | EPOLLOUT | EPOLLPRI | EPOLLET;
event.data.ptr = connection;
if (0 != epoll_ctl (daemon->epoll_fd,
- EPOLL_CTL_ADD,
- client_socket,
- &event))
+ EPOLL_CTL_ADD,
+ client_socket,
+ &event))
{
eno = errno;
#ifdef HAVE_MESSAGES
@@ -2702,8 +2702,8 @@ internal_add_connection (struct MHD_Daemon *daemon,
connection->epoll_state |= MHD_EPOLL_STATE_READ_READY | MHD_EPOLL_STATE_WRITE_READY
| MHD_EPOLL_STATE_IN_EREADY_EDLL;
EDLL_insert (daemon->eready_head,
- daemon->eready_tail,
- connection);
+ daemon->eready_tail,
+ connection);
}
}
else /* This 'else' is combined with next 'if'. */
@@ -3041,12 +3041,12 @@ resume_suspended_connections (struct MHD_Daemon *daemon)
{ /* Wake up suspended connections. */
if (! MHD_itc_activate_(daemon->itc,
"w"))
- {
+ {
#ifdef HAVE_MESSAGES
- MHD_DLOG (daemon,
- _("Failed to signal resume of connection via inter-thread communication channel."));
+ MHD_DLOG (daemon,
+ _("Failed to signal resume of connection via inter-thread communication channel."));
#endif
- }
+ }
}
return ret;
}
@@ -3081,11 +3081,12 @@ resume_suspended_connections (struct MHD_Daemon *daemon)
*/
int
MHD_add_connection (struct MHD_Daemon *daemon,
- MHD_socket client_socket,
- const struct sockaddr *addr,
- socklen_t addrlen)
+ MHD_socket client_socket,
+ const struct sockaddr *addr,
+ socklen_t addrlen)
{
bool sk_nonbl;
+
if (! MHD_socket_nonblocking_ (client_socket))
{
#ifdef HAVE_MESSAGES
@@ -3116,11 +3117,11 @@ MHD_add_connection (struct MHD_Daemon *daemon,
#endif
}
return internal_add_connection (daemon,
- client_socket,
- addr,
+ client_socket,
+ addr,
addrlen,
- true,
- sk_nonbl);
+ true,
+ sk_nonbl);
}
@@ -3253,9 +3254,9 @@ MHD_accept_connection (struct MHD_Daemon *daemon)
#endif
(void) internal_add_connection (daemon,
s,
- addr,
+ addr,
addrlen,
- false,
+ false,
sk_nonbl);
return MHD_YES;
}
@@ -3381,7 +3382,7 @@ MHD_cleanup_connections (struct MHD_Daemon *daemon)
*/
int
MHD_get_timeout (struct MHD_Daemon *daemon,
- MHD_UNSIGNED_LONG_LONG *timeout)
+ MHD_UNSIGNED_LONG_LONG *timeout)
{
time_t earliest_deadline;
time_t now;
@@ -4177,28 +4178,29 @@ run_epoll_for_upgrade (struct MHD_Daemon *daemon)
struct MHD_UpgradeResponseHandle * prev;
num_events = MAX_EVENTS;
- while (MAX_EVENTS == num_events)
+ while (0 != num_events)
{
unsigned int i;
/* update event masks */
num_events = epoll_wait (daemon->epoll_upgrade_fd,
- events,
+ events,
MAX_EVENTS,
0);
if (-1 == num_events)
- {
+ {
const int err = MHD_socket_get_error_ ();
+
if (MHD_SCKT_ERR_IS_EINTR_ (err))
- return MHD_YES;
+ return MHD_YES;
#ifdef HAVE_MESSAGES
MHD_DLOG (daemon,
_("Call to epoll_wait failed: %s\n"),
MHD_socket_strerr_ (err));
#endif
- return MHD_NO;
- }
+ return MHD_NO;
+ }
for (i = 0; i < (unsigned int) num_events; i++)
- {
+ {
struct UpgradeEpollHandle * const ueh = events[i].data.ptr;
struct MHD_UpgradeResponseHandle * const urh = ueh->urh;
bool new_err_state = false;
@@ -4216,24 +4218,24 @@ run_epoll_for_upgrade (struct MHD_Daemon *daemon)
if ( (0 == (ueh->celi & MHD_EPOLL_STATE_ERROR)) &&
(0 != (events[i].events & (EPOLLERR | EPOLLPRI))) )
- {
+ {
/* Process new error state only one time
* and avoid continuously marking this connection
* as 'ready'. */
ueh->celi |= MHD_EPOLL_STATE_ERROR;
new_err_state = true;
- }
+ }
if (! urh->in_eready_list)
{
if (new_err_state ||
- is_urh_ready(urh))
- {
- EDLL_insert (daemon->eready_urh_head,
- daemon->eready_urh_tail,
- urh);
- urh->in_eready_list = true;
- }
+ is_urh_ready(urh))
+ {
+ EDLL_insert (daemon->eready_urh_head,
+ daemon->eready_urh_tail,
+ urh);
+ urh->in_eready_list = true;
+ }
}
}
}
@@ -4245,8 +4247,8 @@ run_epoll_for_upgrade (struct MHD_Daemon *daemon)
if (! is_urh_ready(pos))
{
EDLL_remove (daemon->eready_urh_head,
- daemon->eready_urh_tail,
- pos);
+ daemon->eready_urh_tail,
+ pos);
pos->in_eready_list = false;
}
/* Finished forwarding? */
@@ -4261,7 +4263,7 @@ run_epoll_for_upgrade (struct MHD_Daemon *daemon)
* will be moved immediately to cleanup list. Otherwise
* connection will stay in suspended list until 'pos' will
* be marked with 'was_closed' by application. */
- MHD_resume_connection(pos->connection);
+ MHD_resume_connection (pos->connection);
}
}
@@ -4286,7 +4288,7 @@ static const char * const epoll_itc_marker = "itc_marker";
*/
static int
MHD_epoll (struct MHD_Daemon *daemon,
- int may_block)
+ int may_block)
{
#if defined(HTTPS_SUPPORT) && defined(UPGRADE_SUPPORT)
static const char * const upgrade_marker = "upgrade_ptr";
@@ -4301,7 +4303,7 @@ MHD_epoll (struct MHD_Daemon *daemon,
unsigned int i;
MHD_socket ls;
#if defined(HTTPS_SUPPORT) && defined(UPGRADE_SUPPORT)
- int run_upgraded = MHD_NO;
+ bool run_upgraded = false;
#endif /* HTTPS_SUPPORT && UPGRADE_SUPPORT */
if (-1 == daemon->epoll_fd)
@@ -4317,50 +4319,50 @@ MHD_epoll (struct MHD_Daemon *daemon,
event.events = EPOLLIN;
event.data.ptr = daemon;
if (0 != epoll_ctl (daemon->epoll_fd,
- EPOLL_CTL_ADD,
- ls,
- &event))
- {
+ EPOLL_CTL_ADD,
+ ls,
+ &event))
+ {
#ifdef HAVE_MESSAGES
MHD_DLOG (daemon,
_("Call to epoll_ctl failed: %s\n"),
MHD_socket_last_strerr_ ());
#endif
- return MHD_NO;
- }
+ return MHD_NO;
+ }
daemon->listen_socket_in_epoll = true;
}
if ( (daemon->was_quiesced) &&
(daemon->listen_socket_in_epoll) )
- {
- if ( (0 != epoll_ctl (daemon->epoll_fd,
- EPOLL_CTL_DEL,
- ls,
- NULL)) &&
- (ENOENT != errno) ) /* ENOENT can happen due to race with
- #MHD_quiesce_daemon() */
- MHD_PANIC ("Failed to remove listen FD from epoll set\n");
- daemon->listen_socket_in_epoll = false;
- }
+ {
+ if ( (0 != epoll_ctl (daemon->epoll_fd,
+ EPOLL_CTL_DEL,
+ ls,
+ NULL)) &&
+ (ENOENT != errno) ) /* ENOENT can happen due to race with
+ #MHD_quiesce_daemon() */
+ MHD_PANIC ("Failed to remove listen FD from epoll set\n");
+ daemon->listen_socket_in_epoll = false;
+ }
#if defined(HTTPS_SUPPORT) && defined(UPGRADE_SUPPORT)
- if ( (! daemon->upgrade_fd_in_epoll) &&
- (-1 != daemon->epoll_upgrade_fd) )
+ if ( ( (! daemon->upgrade_fd_in_epoll) &&
+ (-1 != daemon->epoll_upgrade_fd) ) )
{
event.events = EPOLLIN | EPOLLOUT;
event.data.ptr = (void *) upgrade_marker;
if (0 != epoll_ctl (daemon->epoll_fd,
- EPOLL_CTL_ADD,
- daemon->epoll_upgrade_fd,
- &event))
- {
+ EPOLL_CTL_ADD,
+ daemon->epoll_upgrade_fd,
+ &event))
+ {
#ifdef HAVE_MESSAGES
MHD_DLOG (daemon,
_("Call to epoll_ctl failed: %s\n"),
MHD_socket_last_strerr_ ());
#endif
- return MHD_NO;
- }
+ return MHD_NO;
+ }
daemon->upgrade_fd_in_epoll = true;
}
#endif /* HTTPS_SUPPORT && UPGRADE_SUPPORT */
@@ -4372,10 +4374,10 @@ MHD_epoll (struct MHD_Daemon *daemon,
/* we're at the connection limit, disable listen socket
for event loop for now */
if (0 != epoll_ctl (daemon->epoll_fd,
- EPOLL_CTL_DEL,
- ls,
- NULL))
- MHD_PANIC (_("Failed to remove listen FD from epoll set\n"));
+ EPOLL_CTL_DEL,
+ ls,
+ NULL))
+ MHD_PANIC (_("Failed to remove listen FD from epoll set\n"));
daemon->listen_socket_in_epoll = false;
}
@@ -4386,15 +4388,15 @@ MHD_epoll (struct MHD_Daemon *daemon,
if (MHD_YES == may_block)
{
if (MHD_YES == MHD_get_timeout (daemon,
- &timeout_ll))
- {
- if (timeout_ll >= (MHD_UNSIGNED_LONG_LONG) INT_MAX)
- timeout_ms = INT_MAX;
- else
- timeout_ms = (int) timeout_ll;
- }
+ &timeout_ll))
+ {
+ if (timeout_ll >= (MHD_UNSIGNED_LONG_LONG) INT_MAX)
+ timeout_ms = INT_MAX;
+ else
+ timeout_ms = (int) timeout_ll;
+ }
else
- timeout_ms = -1;
+ timeout_ms = -1;
}
else
timeout_ms = 0;
@@ -4413,33 +4415,33 @@ MHD_epoll (struct MHD_Daemon *daemon,
{
/* update event masks */
num_events = epoll_wait (daemon->epoll_fd,
- events,
+ events,
MAX_EVENTS,
timeout_ms);
if (-1 == num_events)
- {
+ {
const int err = MHD_socket_get_error_ ();
if (MHD_SCKT_ERR_IS_EINTR_ (err))
- return MHD_YES;
+ return MHD_YES;
#ifdef HAVE_MESSAGES
MHD_DLOG (daemon,
_("Call to epoll_wait failed: %s\n"),
MHD_socket_strerr_ (err));
#endif
- return MHD_NO;
- }
+ return MHD_NO;
+ }
for (i=0;i<(unsigned int) num_events;i++)
- {
+ {
/* First, check for the values of `ptr` that would indicate
that this event is not about a normal connection. */
- if (NULL == events[i].data.ptr)
- continue; /* shutdown signal! */
+ if (NULL == events[i].data.ptr)
+ continue; /* shutdown signal! */
#if defined(HTTPS_SUPPORT) && defined(UPGRADE_SUPPORT)
if (upgrade_marker == events[i].data.ptr)
{
/* activity on an upgraded connection, we process
those in a separate epoll() */
- run_upgraded = MHD_YES;
+ run_upgraded = true;
continue;
}
#endif /* HTTPS_SUPPORT && UPGRADE_SUPPORT */
@@ -4450,8 +4452,8 @@ MHD_epoll (struct MHD_Daemon *daemon,
MHD_itc_clear_ (daemon->itc);
continue;
}
- if (daemon == events[i].data.ptr)
- {
+ if (daemon == events[i].data.ptr)
+ {
/* Check for error conditions on listen socket. */
/* FIXME: Initiate MHD_quiesce_daemon() to prevent busy waiting? */
if (0 == (events[i].events & (EPOLLERR | EPOLLHUP)))
@@ -4466,9 +4468,9 @@ MHD_epoll (struct MHD_Daemon *daemon,
(daemon->connections < daemon->connection_limit) &&
(! daemon->at_limit) )
series_length++;
- }
+ }
continue;
- }
+ }
/* this is an event relating to a 'normal' connection,
remember the event and if appropriate mark the
connection as 'eready'. */
@@ -4517,7 +4519,7 @@ MHD_epoll (struct MHD_Daemon *daemon,
}
#if defined(HTTPS_SUPPORT) && defined(UPGRADE_SUPPORT)
- if (MHD_YES == run_upgraded)
+ if (run_upgraded || (NULL != daemon->eready_urh_head))
run_epoll_for_upgrade (daemon);
#endif /* HTTPS_SUPPORT && UPGRADE_SUPPORT */
@@ -4605,22 +4607,22 @@ MHD_run (struct MHD_Daemon *daemon)
(0 != (daemon->options & MHD_USE_INTERNAL_POLLING_THREAD)) )
return MHD_NO;
if (0 != (daemon->options & MHD_USE_POLL))
- {
- MHD_poll (daemon, MHD_NO);
- MHD_cleanup_connections (daemon);
- }
+ {
+ MHD_poll (daemon, MHD_NO);
+ MHD_cleanup_connections (daemon);
+ }
#ifdef EPOLL_SUPPORT
else if (0 != (daemon->options & MHD_USE_EPOLL))
- {
- MHD_epoll (daemon, MHD_NO);
- MHD_cleanup_connections (daemon);
- }
+ {
+ MHD_epoll (daemon, MHD_NO);
+ MHD_cleanup_connections (daemon);
+ }
#endif
else
- {
- MHD_select (daemon, MHD_NO);
- /* MHD_select does MHD_cleanup_connections already */
- }
+ {
+ MHD_select (daemon, MHD_NO);
+ /* MHD_select does MHD_cleanup_connections already */
+ }
return MHD_YES;
}
@@ -4822,26 +4824,26 @@ MHD_quiesce_daemon (struct MHD_Daemon *daemon)
if (NULL != daemon->worker_pool)
for (i = 0; i < daemon->worker_pool_size; i++)
{
- daemon->worker_pool[i].was_quiesced = true;
+ daemon->worker_pool[i].was_quiesced = true;
#ifdef EPOLL_SUPPORT
- if ( (0 != (daemon->options & MHD_USE_EPOLL)) &&
- (-1 != daemon->worker_pool[i].epoll_fd) &&
- (daemon->worker_pool[i].listen_socket_in_epoll) )
- {
- if (0 != epoll_ctl (daemon->worker_pool[i].epoll_fd,
- EPOLL_CTL_DEL,
- ret,
- NULL))
- MHD_PANIC (_("Failed to remove listen FD from epoll set\n"));
- daemon->worker_pool[i].listen_socket_in_epoll = false;
- }
- else
-#endif
- if (MHD_ITC_IS_VALID_(daemon->worker_pool[i].itc))
+ if ( (0 != (daemon->options & MHD_USE_EPOLL)) &&
+ (-1 != daemon->worker_pool[i].epoll_fd) &&
+ (daemon->worker_pool[i].listen_socket_in_epoll) )
{
- if (! MHD_itc_activate_ (daemon->worker_pool[i].itc, "q"))
- MHD_PANIC (_("Failed to signal quiesce via inter-thread communication channel"));
+ if (0 != epoll_ctl (daemon->worker_pool[i].epoll_fd,
+ EPOLL_CTL_DEL,
+ ret,
+ NULL))
+ MHD_PANIC (_("Failed to remove listen FD from epoll set\n"));
+ daemon->worker_pool[i].listen_socket_in_epoll = false;
}
+ else
+#endif
+ if (MHD_ITC_IS_VALID_(daemon->worker_pool[i].itc))
+ {
+ if (! MHD_itc_activate_ (daemon->worker_pool[i].itc, "q"))
+ MHD_PANIC (_("Failed to signal quiesce via inter-thread communication channel"));
+ }
}
#endif
daemon->was_quiesced = true;
@@ -4856,7 +4858,7 @@ MHD_quiesce_daemon (struct MHD_Daemon *daemon)
NULL)) &&
(ENOENT != errno) ) /* ENOENT can happen due to race with
#MHD_epoll() */
- MHD_PANIC ("Failed to remove listen FD from epoll set\n");
+ MHD_PANIC ("Failed to remove listen FD from epoll set\n");
daemon->listen_socket_in_epoll = false;
}
#endif
@@ -4890,8 +4892,8 @@ typedef void
*/
static int
parse_options_va (struct MHD_Daemon *daemon,
- const struct sockaddr **servaddr,
- va_list ap);
+ const struct sockaddr **servaddr,
+ va_list ap);
/**
@@ -4929,8 +4931,8 @@ parse_options (struct MHD_Daemon *daemon,
*/
static int
parse_options_va (struct MHD_Daemon *daemon,
- const struct sockaddr **servaddr,
- va_list ap)
+ const struct sockaddr **servaddr,
+ va_list ap)
{
enum MHD_OPTION opt;
struct MHD_OptionItem *oa;
@@ -6493,10 +6495,10 @@ thread_failed:
if (daemon->upgrade_fd_in_epoll)
{
if (0 != epoll_ctl (daemon->epoll_fd,
- EPOLL_CTL_DEL,
- daemon->epoll_upgrade_fd,
- NULL))
- MHD_PANIC (_("Failed to remove FD from epoll set\n"));
+ EPOLL_CTL_DEL,
+ daemon->epoll_upgrade_fd,
+ NULL))
+ MHD_PANIC (_("Failed to remove FD from epoll set\n"));
daemon->upgrade_fd_in_epoll = false;
}
#endif /* HTTPS_SUPPORT && UPGRADE_SUPPORT */
diff --git a/src/microhttpd/response.c b/src/microhttpd/response.c
index 6e569d77..7b98a45c 100644
--- a/src/microhttpd/response.c
+++ b/src/microhttpd/response.c
@@ -80,9 +80,9 @@
*/
static int
add_response_entry (struct MHD_Response *response,
- enum MHD_ValueKind kind,
- const char *header,
- const char *content)
+ enum MHD_ValueKind kind,
+ const char *header,
+ const char *content)
{
struct MHD_HTTP_Header *hdr;
@@ -280,7 +280,7 @@ MHD_get_response_headers (struct MHD_Response *response,
*/
const char *
MHD_get_response_header (struct MHD_Response *response,
- const char *key)
+ const char *key)
{
struct MHD_HTTP_Header *pos;
size_t key_size;
@@ -732,8 +732,8 @@ MHD_create_response_from_data (size_t size,
*/
struct MHD_Response *
MHD_create_response_from_buffer (size_t size,
- void *buffer,
- enum MHD_ResponseMemoryMode mode)
+ void *buffer,
+ enum MHD_ResponseMemoryMode mode)
{
return MHD_create_response_from_data (size,
buffer,
@@ -754,15 +754,15 @@ MHD_create_response_from_buffer (size_t size,
*/
_MHD_EXTERN struct MHD_Response *
MHD_create_response_from_buffer_with_free_callback (size_t size,
- void *buffer,
- MHD_ContentReaderFreeCallback crfc)
+ void *buffer,
+ MHD_ContentReaderFreeCallback crfc)
{
struct MHD_Response *r;
r = MHD_create_response_from_data (size,
- buffer,
- MHD_YES,
- MHD_NO);
+ buffer,
+ MHD_YES,
+ MHD_NO);
if (NULL == r)
return r;
r->crfc = crfc;
@@ -997,17 +997,17 @@ MHD_response_execute_upgrade_ (struct MHD_Response *response,
EPOLL_CTL_ADD,
connection->socket_fd,
&event))
- {
+ {
#ifdef HAVE_MESSAGES
- MHD_DLOG (daemon,
- _("Call to epoll_ctl failed: %s\n"),
- MHD_socket_last_strerr_ ());
+ MHD_DLOG (daemon,
+ _("Call to epoll_ctl failed: %s\n"),
+ MHD_socket_last_strerr_ ());
#endif
- MHD_socket_close_chk_ (sv[0]);
- MHD_socket_close_chk_ (sv[1]);
- free (urh);
- return MHD_NO;
- }
+ MHD_socket_close_chk_ (sv[0]);
+ MHD_socket_close_chk_ (sv[1]);
+ free (urh);
+ return MHD_NO;
+ }
/* Second, add our end of the UNIX socketpair() */
event.events = EPOLLIN | EPOLLOUT | EPOLLPRI | EPOLLET;
@@ -1016,28 +1016,28 @@ MHD_response_execute_upgrade_ (struct MHD_Response *response,
EPOLL_CTL_ADD,
urh->mhd.socket,
&event))
- {
- event.events = EPOLLIN | EPOLLOUT | EPOLLPRI;
- event.data.ptr = &urh->app;
- if (0 != epoll_ctl (daemon->epoll_upgrade_fd,
- EPOLL_CTL_DEL,
- connection->socket_fd,
- &event))
- MHD_PANIC (_("Error cleaning up while handling epoll error"));
+ {
+ event.events = EPOLLIN | EPOLLOUT | EPOLLPRI;
+ event.data.ptr = &urh->app;
+ if (0 != epoll_ctl (daemon->epoll_upgrade_fd,
+ EPOLL_CTL_DEL,
+ connection->socket_fd,
+ &event))
+ MHD_PANIC (_("Error cleaning up while handling epoll error"));
#ifdef HAVE_MESSAGES
- MHD_DLOG (daemon,
- _("Call to epoll_ctl failed: %s\n"),
- MHD_socket_last_strerr_ ());
+ MHD_DLOG (daemon,
+ _("Call to epoll_ctl failed: %s\n"),
+ MHD_socket_last_strerr_ ());
#endif
- MHD_socket_close_chk_ (sv[0]);
- MHD_socket_close_chk_ (sv[1]);
- free (urh);
- return MHD_NO;
- }
- EDLL_insert (daemon->eready_urh_head,
- daemon->eready_urh_tail,
- urh);
- urh->in_eready_list = true;
+ MHD_socket_close_chk_ (sv[0]);
+ MHD_socket_close_chk_ (sv[1]);
+ free (urh);
+ return MHD_NO;
+ }
+ EDLL_insert (daemon->eready_urh_head,
+ daemon->eready_urh_tail,
+ urh);
+ urh->in_eready_list = true;
}
#endif /* EPOLL_SUPPORT */
if (0 == (daemon->options & MHD_USE_THREAD_PER_CONNECTION) )
diff --git a/src/microhttpd/test_upgrade_large.c b/src/microhttpd/test_upgrade_large.c
new file mode 100644
index 00000000..6f2a1b94
--- /dev/null
+++ b/src/microhttpd/test_upgrade_large.c
@@ -0,0 +1,1386 @@
+/*
+ This file is part of libmicrohttpd
+ Copyright (C) 2016, 2019 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 3, 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., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+
+/**
+ * @file test_upgrade_large.c
+ * @brief Testcase for libmicrohttpd upgrading a connection,
+ * modified to test the "large" corner case reported
+ * by Viet on the mailinglist in 6'2019
+ * @author Christian Grothoff
+ * @author Karlson2k (Evgeny Grin)
+ */
+
+#include "mhd_options.h"
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include <pthread.h>
+#include <stdlib.h>
+#include <stddef.h>
+#ifndef WINDOWS
+#include <unistd.h>
+#endif
+#ifdef HAVE_STDBOOL_H
+#include <stdbool.h>
+#endif /* HAVE_STDBOOL_H */
+
+#include "mhd_sockets.h"
+#ifdef HAVE_NETINET_IP_H
+#include <netinet/ip.h>
+#endif /* HAVE_NETINET_IP_H */
+
+#include "platform.h"
+#include "microhttpd.h"
+
+#include "test_helpers.h"
+
+#define LARGE_STRING "HelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHello"
+
+#define LARGE_REPLY_STRING "WorldWorldWorldWorldWorldWorldWorldWorldWorldWorldWorldWorldWorldWorldWorldWorldWorldWorldWorldWorldWorldWorldWorldWorldWorldWorldWorldWorldWorldWorldWorldWorldWorldWorldWorldWorldWorldWorldWorldWorld"
+
+#ifdef HTTPS_SUPPORT
+#include <gnutls/gnutls.h>
+#include "../testcurl/https/tls_test_keys.h"
+
+#if defined(HAVE_FORK) && defined(HAVE_WAITPID)
+#include <sys/types.h>
+#include <sys/wait.h>
+#endif /* HAVE_FORK && HAVE_WAITPID */
+#endif /* HTTPS_SUPPORT */
+
+static int verbose = 0;
+
+static int kicker[2] = {-1, -1} ;
+
+enum tls_tool
+{
+ TLS_CLI_NO_TOOL = 0,
+ TLS_CLI_GNUTLS,
+ TLS_CLI_OPENSSL,
+ TLS_LIB_GNUTLS
+};
+
+enum tls_tool use_tls_tool;
+
+#if defined(HTTPS_SUPPORT) && defined(HAVE_FORK) && defined(HAVE_WAITPID)
+/**
+ * Fork child that connects via GnuTLS-CLI to our @a port. Allows us to
+ * talk to our port over a socket in @a sp without having to worry
+ * about TLS.
+ *
+ * @param location where the socket is returned
+ * @return -1 on error, otherwise PID of TLS child process
+ */
+static pid_t
+gnutlscli_connect (int *sock,
+ uint16_t port)
+{
+ pid_t chld;
+ int sp[2];
+ char destination[30];
+
+ if (0 != socketpair (AF_UNIX,
+ SOCK_STREAM,
+ 0,
+ sp))
+ return -1;
+ chld = fork ();
+ if (0 != chld)
+ {
+ *sock = sp[1];
+ MHD_socket_close_chk_ (sp[0]);
+ return chld;
+ }
+ MHD_socket_close_chk_ (sp[1]);
+ (void) close (0);
+ (void) close (1);
+ if (-1 == dup2 (sp[0], 0))
+ abort ();
+ if (-1 == dup2 (sp[0], 1))
+ abort ();
+ MHD_socket_close_chk_ (sp[0]);
+ if (TLS_CLI_GNUTLS == use_tls_tool)
+ {
+ snprintf (destination,
+ sizeof(destination),
+ "%u",
+ (unsigned int) port);
+ execlp ("gnutls-cli",
+ "gnutls-cli",
+ "--insecure",
+ "-p",
+ destination,
+ "127.0.0.1",
+ (char *) NULL);
+ }
+ else if (TLS_CLI_OPENSSL == use_tls_tool)
+ {
+ snprintf (destination,
+ sizeof(destination),
+ "127.0.0.1:%u",
+ (unsigned int) port);
+ execlp ("openssl",
+ "openssl",
+ "s_client",
+ "-connect",
+ destination,
+ "-verify",
+ "1",
+ (char *) NULL);
+ }
+ _exit (1);
+}
+#endif /* HTTPS_SUPPORT && HAVE_FORK && HAVE_WAITPID */
+
+
+/**
+ * Wrapper structure for plain&TLS sockets
+ */
+struct wr_socket
+{
+ /**
+ * Real network socket
+ */
+ MHD_socket fd;
+
+ /**
+ * Type of this socket
+ */
+ enum wr_type
+ {
+ wr_invalid = 0,
+ wr_plain = 1,
+ wr_tls = 2
+ } t;
+#ifdef HTTPS_SUPPORT
+ /**
+ * TLS credentials
+ */
+ gnutls_certificate_credentials_t tls_crd;
+
+ /**
+ * TLS session.
+ */
+ gnutls_session_t tls_s;
+
+ /**
+ * TLS handshake already succeed?
+ */
+ bool tls_connected;
+#endif
+};
+
+
+/**
+ * Get underlying real socket.
+ * @return FD of real socket
+ */
+#define wr_fd(s) ((s)->fd)
+
+
+/**
+ * Create wr_socket with plain TCP underlying socket
+ * @return created socket on success, NULL otherwise
+ */
+static struct wr_socket *
+wr_create_plain_sckt(void)
+{
+ struct wr_socket *s = malloc(sizeof(struct wr_socket));
+ if (NULL == s)
+ return NULL;
+ s->t = wr_plain;
+ s->fd = socket (AF_INET, SOCK_STREAM, IPPROTO_TCP);
+ if (MHD_INVALID_SOCKET != s->fd)
+ return s;
+ free(s);
+ return NULL;
+}
+
+
+/**
+ * Create wr_socket with TLS TCP underlying socket
+ * @return created socket on success, NULL otherwise
+ */
+static struct wr_socket *
+wr_create_tls_sckt(void)
+{
+#ifdef HTTPS_SUPPORT
+ struct wr_socket *s = malloc(sizeof(struct wr_socket));
+ if (NULL == s)
+ return NULL;
+ s->t = wr_tls;
+ s->tls_connected = 0;
+ s->fd = socket (AF_INET, SOCK_STREAM, IPPROTO_TCP);
+ if (MHD_INVALID_SOCKET != s->fd)
+ {
+ if (GNUTLS_E_SUCCESS == gnutls_init (&(s->tls_s), GNUTLS_CLIENT))
+ {
+ if (GNUTLS_E_SUCCESS == gnutls_set_default_priority (s->tls_s))
+ {
+ if (GNUTLS_E_SUCCESS == gnutls_certificate_allocate_credentials (&(s->tls_crd)))
+ {
+ if (GNUTLS_E_SUCCESS == gnutls_credentials_set (s->tls_s, GNUTLS_CRD_CERTIFICATE, s->tls_crd))
+ {
+#if GNUTLS_VERSION_NUMBER+0 >= 0x030109
+ gnutls_transport_set_int (s->tls_s, (int)(s->fd));
+#else /* GnuTLS before 3.1.9 */
+ gnutls_transport_set_ptr (s->tls_s, (gnutls_transport_ptr_t)(intptr_t)(s->fd));
+#endif /* GnuTLS before 3.1.9 */
+ return s;
+ }
+ gnutls_certificate_free_credentials (s->tls_crd);
+ }
+ }
+ gnutls_deinit (s->tls_s);
+ }
+ (void)MHD_socket_close_ (s->fd);
+ }
+ free(s);
+#endif /* HTTPS_SUPPORT */
+ return NULL;
+}
+
+
+/**
+ * Create wr_socket with plain TCP underlying socket
+ * from already created TCP socket.
+ * @param plain_sk real TCP socket
+ * @return created socket on success, NULL otherwise
+ */
+static struct wr_socket *
+wr_create_from_plain_sckt(MHD_socket plain_sk)
+{
+ struct wr_socket *s = malloc(sizeof(struct wr_socket));
+
+ if (NULL == s)
+ return NULL;
+ s->t = wr_plain;
+ s->fd = plain_sk;
+ return s;
+}
+
+
+/**
+ * Connect socket to specified address.
+ * @param s socket to use
+ * @param addr address to connect
+ * @param length of sturcture pointed by @a addr
+ * @return zero on success, -1 otherwise.
+ */
+static int
+wr_connect(struct wr_socket *s,
+ const struct sockaddr *addr,
+ int length)
+{
+ if (0 != connect (s->fd, addr, length))
+ return -1;
+ if (wr_plain == s->t)
+ return 0;
+#ifdef HTTPS_SUPPORT
+ if (wr_tls == s->t)
+ {
+ /* Do not try handshake here as
+ * it require processing on MHD side and
+ * when testing with "external" polling,
+ * test will call MHD processing only
+ * after return from wr_connect(). */
+ s->tls_connected = 0;
+ return 0;
+ }
+#endif /* HTTPS_SUPPORT */
+ return -1;
+}
+
+#ifdef HTTPS_SUPPORT
+/* Only to be called from wr_send() and wr_recv() ! */
+static bool
+wr_handshake(struct wr_socket *s)
+{
+ int res = gnutls_handshake (s->tls_s);
+ if (GNUTLS_E_SUCCESS == res)
+ s->tls_connected = true;
+ else if (GNUTLS_E_AGAIN == res)
+ MHD_socket_set_error_ (MHD_SCKT_EAGAIN_);
+ else
+ MHD_socket_set_error_ (MHD_SCKT_ECONNABORTED_); /* hard error */
+ return s->tls_connected;
+}
+#endif /* HTTPS_SUPPORT */
+
+
+/**
+ * Send data to remote by socket.
+ * @param s the socket to use
+ * @param buf the buffer with data to send
+ * @param len the length of data in @a buf
+ * @return number of bytes were sent if succeed,
+ * -1 if failed. Use #MHD_socket_get_error_()
+ * to get socket error.
+ */
+static ssize_t
+wr_send (struct wr_socket *s,
+ const void *buf,
+ size_t len)
+{
+ if (wr_plain == s->t)
+ return MHD_send_(s->fd, buf, len);
+#ifdef HTTPS_SUPPORT
+ if (wr_tls == s->t)
+ {
+ ssize_t ret;
+ if (!s->tls_connected && !wr_handshake (s))
+ return -1;
+
+ ret = gnutls_record_send (s->tls_s, buf, len);
+ if (ret > 0)
+ return ret;
+ if (GNUTLS_E_AGAIN == ret)
+ MHD_socket_set_error_ (MHD_SCKT_EAGAIN_);
+ else
+ MHD_socket_set_error_ (MHD_SCKT_ECONNABORTED_); /* hard error */
+ }
+#endif /* HTTPS_SUPPORT */
+ return -1;
+}
+
+
+/**
+ * Receive data from remote by socket.
+ * @param s the socket to use
+ * @param buf the buffer to store received data
+ * @param len the length of @a buf
+ * @return number of bytes were received if succeed,
+ * -1 if failed. Use #MHD_socket_get_error_()
+ * to get socket error.
+ */
+static ssize_t
+wr_recv (struct wr_socket *s,
+ void *buf,
+ size_t len)
+{
+ if (wr_plain == s->t)
+ return MHD_recv_ (s->fd, buf, len);
+#ifdef HTTPS_SUPPORT
+ if (wr_tls == s->t)
+ {
+ ssize_t ret;
+ if (!s->tls_connected && !wr_handshake (s))
+ return -1;
+
+ ret = gnutls_record_recv (s->tls_s, buf, len);
+ if (ret > 0)
+ return ret;
+ if (GNUTLS_E_AGAIN == ret)
+ MHD_socket_set_error_ (MHD_SCKT_EAGAIN_);
+ else
+ MHD_socket_set_error_ (MHD_SCKT_ECONNABORTED_); /* hard error */
+ }
+#endif /* HTTPS_SUPPORT */
+ return -1;
+}
+
+
+/**
+ * Close socket and release allocated resourced
+ * @param s the socket to close
+ * @return zero on succeed, -1 otherwise
+ */
+static int
+wr_close (struct wr_socket *s)
+{
+ int ret = (MHD_socket_close_(s->fd)) ? 0 : -1;
+#ifdef HTTPS_SUPPORT
+ if (wr_tls == s->t)
+ {
+ gnutls_deinit (s->tls_s);
+ gnutls_certificate_free_credentials (s->tls_crd);
+ }
+#endif /* HTTPS_SUPPORT */
+ free (s);
+ return ret;
+}
+
+
+/**
+ * Thread we use to run the interaction with the upgraded socket.
+ */
+static pthread_t pt;
+
+/**
+ * Will be set to the upgraded socket.
+ */
+static struct wr_socket *usock;
+
+/**
+ * Thread we use to run the interaction with the upgraded socket.
+ */
+static pthread_t pt_client;
+
+/**
+ * Flag set to 1 once the test is finished.
+ */
+static volatile bool done;
+
+
+/**
+ * Callback used by MHD to notify the application about completed
+ * requests. Frees memory.
+ *
+ * @param cls client-defined closure
+ * @param connection connection handle
+ * @param con_cls value as set by the last call to
+ * the #MHD_AccessHandlerCallback
+ * @param toe reason for request termination
+ */
+static void
+notify_completed_cb (void *cls,
+ struct MHD_Connection *connection,
+ void **con_cls,
+ enum MHD_RequestTerminationCode toe)
+{
+ pthread_t* ppth = *con_cls;
+
+ (void) cls;
+ (void) connection; /* Unused. Silent compiler warning. */
+ if ( (toe != MHD_REQUEST_TERMINATED_COMPLETED_OK) &&
+ (toe != MHD_REQUEST_TERMINATED_CLIENT_ABORT) &&
+ (toe != MHD_REQUEST_TERMINATED_DAEMON_SHUTDOWN) )
+ abort ();
+ if (! pthread_equal (**((pthread_t**)con_cls),
+ pthread_self ()))
+ abort ();
+ if (NULL != ppth)
+ free (*con_cls);
+ *con_cls = NULL;
+}
+
+
+/**
+ * Logging callback.
+ *
+ * @param cls logging closure (NULL)
+ * @param uri access URI
+ * @param connection connection handle
+ * @return #TEST_PTR
+ */
+static void *
+log_cb (void *cls,
+ const char *uri,
+ struct MHD_Connection *connection)
+{
+ pthread_t *ppth;
+
+ (void) cls;
+ (void) connection; /* Unused. Silent compiler warning. */
+ if (0 != strcmp (uri,
+ "/"))
+ abort ();
+ ppth = malloc (sizeof (pthread_t));
+ if (NULL == ppth)
+ abort();
+ *ppth = pthread_self ();
+ return (void *) ppth;
+}
+
+
+/**
+ * Function to check that MHD properly notifies about starting
+ * and stopping.
+ *
+ * @param cls client-defined closure
+ * @param connection connection handle
+ * @param socket_context socket-specific pointer where the
+ * client can associate some state specific
+ * to the TCP connection; note that this is
+ * different from the "con_cls" which is per
+ * HTTP request. The client can initialize
+ * during #MHD_CONNECTION_NOTIFY_STARTED and
+ * cleanup during #MHD_CONNECTION_NOTIFY_CLOSED
+ * and access in the meantime using
+ * #MHD_CONNECTION_INFO_SOCKET_CONTEXT.
+ * @param toe reason for connection notification
+ * @see #MHD_OPTION_NOTIFY_CONNECTION
+ * @ingroup request
+ */
+static void
+notify_connection_cb (void *cls,
+ struct MHD_Connection *connection,
+ void **socket_context,
+ enum MHD_ConnectionNotificationCode toe)
+{
+ static int started;
+
+ (void) cls;
+ (void) connection; /* Unused. Silent compiler warning. */
+ switch (toe)
+ {
+ case MHD_CONNECTION_NOTIFY_STARTED:
+ if (MHD_NO != started)
+ abort ();
+ started = MHD_YES;
+ *socket_context = &started;
+ break;
+ case MHD_CONNECTION_NOTIFY_CLOSED:
+ if (MHD_YES != started)
+ abort ();
+ if (&started != *socket_context)
+ abort ();
+ *socket_context = NULL;
+ started = MHD_NO;
+ break;
+ }
+}
+
+
+/**
+ * Change socket to blocking.
+ *
+ * @param fd the socket to manipulate
+ * @return non-zero if succeeded, zero otherwise
+ */
+static void
+make_blocking (MHD_socket fd)
+{
+#if defined(MHD_POSIX_SOCKETS)
+ int flags;
+
+ flags = fcntl (fd, F_GETFL);
+ if (-1 == flags)
+ return;
+ if ((flags & ~O_NONBLOCK) != flags)
+ if (-1 == fcntl (fd, F_SETFL, flags & ~O_NONBLOCK))
+ abort ();
+#elif defined(MHD_WINSOCK_SOCKETS)
+ unsigned long flags = 1;
+
+ ioctlsocket (fd, FIONBIO, &flags);
+#endif /* MHD_WINSOCK_SOCKETS */
+}
+
+
+static void
+kick_select ()
+{
+ if (-1 != kicker[1])
+ {
+ write (kicker[1], "K", 1);
+ fprintf (stderr, "KICKING\n");
+ }
+}
+
+
+static void
+send_all (struct wr_socket *sock,
+ const char *text)
+{
+ size_t len = strlen (text);
+ ssize_t ret;
+ size_t off;
+
+ make_blocking (wr_fd (sock));
+ for (off = 0; off < len; off += ret)
+ {
+ ret = wr_send (sock,
+ &text[off],
+ len - off);
+ kick_select ();
+ if (0 > ret)
+ {
+ if (MHD_SCKT_ERR_IS_EAGAIN_ (MHD_socket_get_error_ ()))
+ {
+ ret = 0;
+ continue;
+ }
+ abort ();
+ }
+ }
+}
+
+
+/**
+ * Read character-by-character until we
+ * get '\r\n\r\n'.
+ */
+static void
+recv_hdr (struct wr_socket *sock)
+{
+ unsigned int i;
+ char next;
+ char c;
+ ssize_t ret;
+
+ make_blocking (wr_fd (sock));
+ next = '\r';
+ i = 0;
+ while (i < 4)
+ {
+ ret = wr_recv (sock,
+ &c,
+ 1);
+ kick_select ();
+ if (0 > ret)
+ {
+ if (MHD_SCKT_ERR_IS_EAGAIN_ (MHD_socket_get_error_ ()))
+ continue;
+ abort ();
+ }
+ if (0 == ret)
+ continue;
+ if (c == next)
+ {
+ i++;
+ if (next == '\r')
+ next = '\n';
+ else
+ next = '\r';
+ continue;
+ }
+ if (c == '\r')
+ {
+ i = 1;
+ next = '\n';
+ continue;
+ }
+ i = 0;
+ next = '\r';
+ }
+}
+
+
+static void
+recv_all (struct wr_socket *sock,
+ const char *text)
+{
+ size_t len = strlen (text);
+ char buf[len];
+ ssize_t ret;
+ size_t off;
+
+ make_blocking (wr_fd (sock));
+ for (off = 0; off < len; off += ret)
+ {
+ ret = wr_recv (sock,
+ &buf[off],
+ len - off);
+ if (0 > ret)
+ {
+ if (MHD_SCKT_ERR_IS_EAGAIN_ (MHD_socket_get_error_ ()))
+ {
+ ret = 0;
+ continue;
+ }
+ abort ();
+ }
+ }
+ if (0 != strncmp (text, buf, len))
+ abort();
+}
+
+
+/**
+ * Main function for the thread that runs the interaction with
+ * the upgraded socket.
+ *
+ * @param cls the handle for the upgrade
+ */
+static void *
+run_usock (void *cls)
+{
+ struct MHD_UpgradeResponseHandle *urh = cls;
+
+ send_all (usock,
+ LARGE_STRING);
+ recv_all (usock,
+ LARGE_REPLY_STRING);
+ send_all (usock,
+ "Finished");
+ MHD_upgrade_action (urh,
+ MHD_UPGRADE_ACTION_CLOSE);
+ free (usock);
+ usock = NULL;
+ return NULL;
+}
+
+
+/**
+ * Main function for the thread that runs the client-side of the
+ * interaction with the upgraded socket.
+ *
+ * @param cls the client socket
+ */
+static void *
+run_usock_client (void *cls)
+{
+ struct wr_socket *sock = cls;
+
+ send_all (sock,
+ "GET / HTTP/1.1\r\nConnection: Upgrade\r\n\r\n");
+ recv_hdr (sock);
+ recv_all (sock,
+ LARGE_STRING);
+ send_all (sock,
+ LARGE_REPLY_STRING);
+ recv_all (sock,
+ "Finished");
+ wr_close (sock);
+ done = true;
+ return NULL;
+}
+
+
+/**
+ * Function called after a protocol "upgrade" response was sent
+ * successfully and the socket should now be controlled by some
+ * protocol other than HTTP.
+ *
+ * Any data already received on the socket will be made available in
+ * @e extra_in. This can happen if the application sent extra data
+ * before MHD send the upgrade response. The application should
+ * treat data from @a extra_in as if it had read it from the socket.
+ *
+ * Note that the application must not close() @a sock directly,
+ * but instead use #MHD_upgrade_action() for special operations
+ * on @a sock.
+ *
+ * Except when in 'thread-per-connection' mode, implementations
+ * of this function should never block (as it will still be called
+ * from within the main event loop).
+ *
+ * @param cls closure, whatever was given to #MHD_create_response_for_upgrade().
+ * @param connection original HTTP connection handle,
+ * giving the function a last chance
+ * to inspect the original HTTP request
+ * @param con_cls last value left in `con_cls` of the `MHD_AccessHandlerCallback`
+ * @param extra_in if we happened to have read bytes after the
+ * HTTP header already (because the client sent
+ * more than the HTTP header of the request before
+ * we sent the upgrade response),
+ * these are the extra bytes already read from @a sock
+ * by MHD. The application should treat these as if
+ * it had read them from @a sock.
+ * @param extra_in_size number of bytes in @a extra_in
+ * @param sock socket to use for bi-directional communication
+ * with the client. For HTTPS, this may not be a socket
+ * that is directly connected to the client and thus certain
+ * operations (TCP-specific setsockopt(), getsockopt(), etc.)
+ * may not work as expected (as the socket could be from a
+ * socketpair() or a TCP-loopback). The application is expected
+ * to perform read()/recv() and write()/send() calls on the socket.
+ * The application may also call shutdown(), but must not call
+ * close() directly.
+ * @param urh argument for #MHD_upgrade_action()s on this @a connection.
+ * Applications must eventually use this callback to (indirectly)
+ * perform the close() action on the @a sock.
+ */
+static void
+upgrade_cb (void *cls,
+ struct MHD_Connection *connection,
+ void *con_cls,
+ const char *extra_in,
+ size_t extra_in_size,
+ MHD_socket sock,
+ struct MHD_UpgradeResponseHandle *urh)
+{
+ (void) cls;
+ (void) connection;
+ (void) con_cls;
+ (void) extra_in; /* Unused. Silent compiler warning. */
+
+ usock = wr_create_from_plain_sckt (sock);
+ if (0 != extra_in_size)
+ abort ();
+ if (0 != pthread_create (&pt,
+ NULL,
+ &run_usock,
+ urh))
+ abort ();
+}
+
+
+/**
+ * A client has requested the given url using the given method
+ * (#MHD_HTTP_METHOD_GET, #MHD_HTTP_METHOD_PUT,
+ * #MHD_HTTP_METHOD_DELETE, #MHD_HTTP_METHOD_POST, etc). The callback
+ * must call MHD callbacks to provide content to give back to the
+ * client and return an HTTP status code (i.e. #MHD_HTTP_OK,
+ * #MHD_HTTP_NOT_FOUND, etc.).
+ *
+ * @param cls argument given together with the function
+ * pointer when the handler was registered with MHD
+ * @param url the requested url
+ * @param method the HTTP method used (#MHD_HTTP_METHOD_GET,
+ * #MHD_HTTP_METHOD_PUT, etc.)
+ * @param version the HTTP version string (i.e.
+ * #MHD_HTTP_VERSION_1_1)
+ * @param upload_data the data being uploaded (excluding HEADERS,
+ * for a POST that fits into memory and that is encoded
+ * with a supported encoding, the POST data will NOT be
+ * given in upload_data and is instead available as
+ * part of #MHD_get_connection_values; very large POST
+ * data *will* be made available incrementally in
+ * @a upload_data)
+ * @param upload_data_size set initially to the size of the
+ * @a upload_data provided; the method must update this
+ * value to the number of bytes NOT processed;
+ * @param con_cls pointer that the callback can set to some
+ * address and that will be preserved by MHD for future
+ * calls for this request; since the access handler may
+ * be called many times (i.e., for a PUT/POST operation
+ * with plenty of upload data) this allows the application
+ * to easily associate some request-specific state.
+ * If necessary, this state can be cleaned up in the
+ * global #MHD_RequestCompletedCallback (which
+ * can be set with the #MHD_OPTION_NOTIFY_COMPLETED).
+ * Initially, `*con_cls` will be NULL.
+ * @return #MHD_YES if the connection was handled successfully,
+ * #MHD_NO if the socket must be closed due to a serios
+ * error while handling the request
+ */
+static int
+ahc_upgrade (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 **con_cls)
+{
+ struct MHD_Response *resp;
+ int ret;
+ (void) cls;
+ (void) url;
+ (void) method; /* Unused. Silent compiler warning. */
+ (void) version;
+ (void) upload_data;
+ (void) upload_data_size; /* Unused. Silent compiler warning. */
+
+ if (NULL == *con_cls)
+ abort ();
+ if (! pthread_equal (**((pthread_t**)con_cls), pthread_self ()))
+ abort ();
+ resp = MHD_create_response_for_upgrade (&upgrade_cb,
+ NULL);
+ MHD_add_response_header (resp,
+ MHD_HTTP_HEADER_UPGRADE,
+ "Hello World Protocol");
+ ret = MHD_queue_response (connection,
+ MHD_HTTP_SWITCHING_PROTOCOLS,
+ resp);
+ MHD_destroy_response (resp);
+ return ret;
+}
+
+
+/**
+ * Run the MHD external event loop using select.
+ *
+ * @param daemon daemon to run it for
+ */
+static void
+run_mhd_select_loop (struct MHD_Daemon *daemon)
+{
+ fd_set rs;
+ fd_set ws;
+ fd_set es;
+ MHD_socket max_fd;
+ MHD_UNSIGNED_LONG_LONG to;
+ struct timeval tv;
+ char drain[128];
+
+ if (0 != pipe (kicker))
+ abort ();
+ while (! done)
+ {
+ FD_ZERO (&rs);
+ FD_ZERO (&ws);
+ FD_ZERO (&es);
+ max_fd = -1;
+ to = 1000;
+
+ FD_SET (kicker[0], &rs);
+ if (MHD_YES !=
+ MHD_get_fdset (daemon,
+ &rs,
+ &ws,
+ &es,
+ &max_fd))
+ abort ();
+ (void) MHD_get_timeout (daemon,
+ &to);
+ if (1000 < to)
+ to = 1000;
+ tv.tv_sec = to / 1000;
+ tv.tv_usec = 1000 * (to % 1000);
+ if (0 > MHD_SYS_select_ (max_fd + 1,
+ &rs,
+ &ws,
+ &es,
+ &tv))
+ abort ();
+ if (FD_ISSET (kicker[0], &rs))
+ (void) read (kicker[0], drain, sizeof (drain));
+ MHD_run_from_select (daemon,
+ &rs,
+ &ws,
+ &es);
+ }
+ close (kicker[0]);
+ close (kicker[1]);
+ kicker[0] = -1;
+ kicker[1] = -1;
+}
+
+#ifdef HAVE_POLL
+
+/**
+ * Run the MHD external event loop using select.
+ *
+ * @param daemon daemon to run it for
+ */
+static void
+run_mhd_poll_loop (struct MHD_Daemon *daemon)
+{
+ (void)daemon; /* Unused. Silent compiler warning. */
+ abort (); /* currently not implementable with existing MHD API */
+}
+#endif /* HAVE_POLL */
+
+
+#ifdef EPOLL_SUPPORT
+/**
+ * Run the MHD external event loop using select.
+ *
+ * @param daemon daemon to run it for
+ */
+static void
+run_mhd_epoll_loop (struct MHD_Daemon *daemon)
+{
+ const union MHD_DaemonInfo *di;
+ MHD_socket ep;
+ fd_set rs;
+ MHD_UNSIGNED_LONG_LONG to;
+ struct timeval tv;
+ int ret;
+ char drain[128];
+
+ di = MHD_get_daemon_info (daemon,
+ MHD_DAEMON_INFO_EPOLL_FD);
+ ep = di->listen_fd;
+ if (0 != pipe (kicker))
+ abort ();
+ while (! done)
+ {
+ FD_ZERO (&rs);
+ to = 1000;
+ FD_SET (kicker[0], &rs);
+ FD_SET (ep, &rs);
+ (void) MHD_get_timeout (daemon,
+ &to);
+ if (1000 < to)
+ to = 1000;
+ tv.tv_sec = to / 1000;
+ tv.tv_usec = 1000 * (to % 1000);
+ ret = select (ep + 1,
+ &rs,
+ NULL,
+ NULL,
+ &tv);
+ if ( (-1 == ret) &&
+ (EAGAIN != errno) &&
+ (EINTR != errno) )
+ abort ();
+ if (FD_ISSET (kicker[0], &rs))
+ (void) read (kicker[0], drain, sizeof (drain));
+ MHD_run (daemon);
+ }
+ close (kicker[0]);
+ close (kicker[1]);
+ kicker[0] = -1;
+ kicker[1] = -1;
+}
+#endif /* EPOLL_SUPPORT */
+
+/**
+ * Run the MHD external event loop using select.
+ *
+ * @param daemon daemon to run it for
+ */
+static void
+run_mhd_loop (struct MHD_Daemon *daemon,
+ int flags)
+{
+ if (0 == (flags & (MHD_USE_POLL | MHD_USE_EPOLL)))
+ run_mhd_select_loop (daemon);
+#ifdef HAVE_POLL
+ else if (0 != (flags & MHD_USE_POLL))
+ run_mhd_poll_loop (daemon);
+#endif /* HAVE_POLL */
+#if EPOLL_SUPPORT
+ else if (0 != (flags & MHD_USE_EPOLL))
+ run_mhd_epoll_loop (daemon);
+#endif
+ else
+ abort ();
+}
+
+
+static bool test_tls;
+
+/**
+ * Test upgrading a connection.
+ *
+ * @param flags which event loop style should be tested
+ * @param pool size of the thread pool, 0 to disable
+ */
+static int
+test_upgrade (int flags,
+ unsigned int pool)
+{
+ struct MHD_Daemon *d = NULL;
+ struct wr_socket *sock;
+ struct sockaddr_in sa;
+ const union MHD_DaemonInfo *real_flags;
+ const union MHD_DaemonInfo *dinfo;
+#if defined(HTTPS_SUPPORT) && defined(HAVE_FORK) && defined(HAVE_WAITPID)
+ pid_t pid = -1;
+#endif /* HTTPS_SUPPORT && HAVE_FORK && HAVE_WAITPID */
+
+ done = false;
+
+ if (! test_tls)
+ d = MHD_start_daemon (flags | MHD_USE_ERROR_LOG | MHD_ALLOW_UPGRADE,
+ MHD_is_feature_supported(MHD_FEATURE_AUTODETECT_BIND_PORT) ?
+ 0 : 1090,
+ NULL, NULL,
+ &ahc_upgrade, NULL,
+ MHD_OPTION_CONNECTION_MEMORY_LIMIT, 512,
+ MHD_OPTION_URI_LOG_CALLBACK, &log_cb, NULL,
+ MHD_OPTION_NOTIFY_COMPLETED, &notify_completed_cb, NULL,
+ MHD_OPTION_NOTIFY_CONNECTION, &notify_connection_cb, NULL,
+ MHD_OPTION_THREAD_POOL_SIZE, pool,
+ MHD_OPTION_END);
+#ifdef HTTPS_SUPPORT
+ else
+ d = MHD_start_daemon (flags | MHD_USE_ERROR_LOG | MHD_ALLOW_UPGRADE | MHD_USE_TLS,
+ MHD_is_feature_supported(MHD_FEATURE_AUTODETECT_BIND_PORT) ?
+ 0 : 1090,
+ NULL, NULL,
+ &ahc_upgrade, NULL,
+ MHD_OPTION_CONNECTION_MEMORY_LIMIT, 512,
+ MHD_OPTION_URI_LOG_CALLBACK, &log_cb, NULL,
+ MHD_OPTION_NOTIFY_COMPLETED, &notify_completed_cb, NULL,
+ MHD_OPTION_NOTIFY_CONNECTION, &notify_connection_cb, NULL,
+ MHD_OPTION_HTTPS_MEM_KEY, srv_signed_key_pem,
+ MHD_OPTION_HTTPS_MEM_CERT, srv_signed_cert_pem,
+ MHD_OPTION_THREAD_POOL_SIZE, pool,
+ MHD_OPTION_END);
+#endif /* HTTPS_SUPPORT */
+ if (NULL == d)
+ return 2;
+ real_flags = MHD_get_daemon_info (d,
+ MHD_DAEMON_INFO_FLAGS);
+ if (NULL == real_flags)
+ abort ();
+ dinfo = MHD_get_daemon_info (d,
+ MHD_DAEMON_INFO_BIND_PORT);
+ if ( (NULL == dinfo) ||
+ (0 == dinfo->port) )
+ abort ();
+ if (!test_tls || TLS_LIB_GNUTLS == use_tls_tool)
+ {
+ sock = test_tls ? wr_create_tls_sckt () : wr_create_plain_sckt ();
+ if (NULL == sock)
+ abort ();
+ sa.sin_family = AF_INET;
+ sa.sin_port = htons (dinfo->port);
+ sa.sin_addr.s_addr = htonl (INADDR_LOOPBACK);
+ if (0 != wr_connect (sock,
+ (struct sockaddr *) &sa,
+ sizeof (sa)))
+ abort ();
+ }
+ else
+ {
+#if defined(HTTPS_SUPPORT) && defined(HAVE_FORK) && defined(HAVE_WAITPID)
+ MHD_socket tls_fork_sock;
+ uint16_t port;
+
+ /* make address sanitizer happy */
+ memcpy (&port,
+ dinfo /* ->port */,
+ sizeof (port));
+ if (-1 == (pid = gnutlscli_connect (&tls_fork_sock,
+ port)))
+ {
+ MHD_stop_daemon (d);
+ return 4;
+ }
+
+ sock = wr_create_from_plain_sckt (tls_fork_sock);
+ if (NULL == sock)
+ abort ();
+#else /* !HTTPS_SUPPORT || !HAVE_FORK || !HAVE_WAITPID */
+ abort ();
+#endif /* !HTTPS_SUPPORT || !HAVE_FORK || !HAVE_WAITPID */
+ }
+
+ if (0 != pthread_create (&pt_client,
+ NULL,
+ &run_usock_client,
+ sock))
+ abort ();
+ if (0 == (flags & MHD_USE_INTERNAL_POLLING_THREAD) )
+ {
+ enum MHD_FLAG flags;
+
+ /* make address sanitizer happy */
+ memcpy (&flags,
+ real_flags /* ->flags */,
+ sizeof (flags));
+ run_mhd_loop (d, flags);
+ }
+ pthread_join (pt_client,
+ NULL);
+ pthread_join (pt,
+ NULL);
+#if defined(HTTPS_SUPPORT) && defined(HAVE_FORK) && defined(HAVE_WAITPID)
+ if (test_tls && TLS_LIB_GNUTLS != use_tls_tool)
+ waitpid (pid, NULL, 0);
+#endif /* HTTPS_SUPPORT && HAVE_FORK && HAVE_WAITPID */
+ MHD_stop_daemon (d);
+ return 0;
+}
+
+
+int
+main (int argc,
+ char *const *argv)
+{
+ int error_count = 0;
+ int res;
+
+ use_tls_tool = TLS_CLI_NO_TOOL;
+ test_tls = has_in_name(argv[0], "_tls");
+
+ verbose = 1;
+ if (has_param(argc, argv, "-q") ||
+ has_param(argc, argv, "--quiet"))
+ verbose = 0;
+
+ if (test_tls)
+ {
+#ifdef HTTPS_SUPPORT
+ if (has_param(argc, argv, "--use-gnutls-cli"))
+ use_tls_tool = TLS_CLI_GNUTLS;
+ else if (has_param(argc, argv, "--use-openssl"))
+ use_tls_tool = TLS_CLI_OPENSSL;
+ else if (has_param(argc, argv, "--use-gnutls-lib"))
+ use_tls_tool = TLS_LIB_GNUTLS;
+#if defined(HAVE_FORK) && defined(HAVE_WAITPID)
+ else if (0 == system ("gnutls-cli --version 1> /dev/null 2> /dev/null"))
+ use_tls_tool = TLS_CLI_GNUTLS;
+ else if (0 == system ("openssl version 1> /dev/null 2> /dev/null"))
+ use_tls_tool = TLS_CLI_OPENSSL;
+#endif /* HAVE_FORK && HAVE_WAITPID */
+ else
+ use_tls_tool = TLS_LIB_GNUTLS; /* Should be available as MHD use it. */
+ if (verbose)
+ {
+ switch (use_tls_tool)
+ {
+ case TLS_CLI_GNUTLS:
+ printf ("GnuTLS-CLI will be used for testing.\n");
+ break;
+ case TLS_CLI_OPENSSL:
+ printf ("Command line version of OpenSSL will be used for testing.\n");
+ break;
+ case TLS_LIB_GNUTLS:
+ printf ("GnuTLS library will be used for testing.\n");
+ break;
+ default:
+ abort ();
+ }
+ }
+ if ( (TLS_LIB_GNUTLS == use_tls_tool) &&
+ (GNUTLS_E_SUCCESS != gnutls_global_init()) )
+ abort ();
+
+#else /* ! HTTPS_SUPPORT */
+ fprintf (stderr, "HTTPS support was disabled by configure.\n");
+ return 77;
+#endif /* ! HTTPS_SUPPORT */
+ }
+
+ /* run tests */
+ if (verbose)
+ printf ("Starting HTTP \"Upgrade\" tests with %s connections.\n",
+ test_tls ? "TLS" : "plain");
+ /* try external select */
+ res = test_upgrade (0,
+ 0);
+ error_count += res;
+ if (res)
+ fprintf (stderr,
+ "FAILED: Upgrade with external select, return code %d.\n",
+ res);
+ else if (verbose)
+ printf ("PASSED: Upgrade with external select.\n");
+
+ /* Try external auto */
+ res = test_upgrade (MHD_USE_AUTO,
+ 0);
+ error_count += res;
+ if (res)
+ fprintf (stderr,
+ "FAILED: Upgrade with external 'auto', return code %d.\n",
+ res);
+ else if (verbose)
+ printf ("PASSED: Upgrade with external 'auto'.\n");
+
+#ifdef EPOLL_SUPPORT
+ res = test_upgrade (MHD_USE_EPOLL,
+ 0);
+ error_count += res;
+ if (res)
+ fprintf (stderr,
+ "FAILED: Upgrade with external select with EPOLL, return code %d.\n",
+ res);
+ else if (verbose)
+ printf ("PASSED: Upgrade with external select with EPOLL.\n");
+#endif
+
+ /* Test thread-per-connection */
+ res = test_upgrade (MHD_USE_INTERNAL_POLLING_THREAD | MHD_USE_THREAD_PER_CONNECTION,
+ 0);
+ error_count += res;
+ if (res)
+ fprintf (stderr,
+ "FAILED: Upgrade with thread per connection, return code %d.\n",
+ res);
+ else if (verbose)
+ printf ("PASSED: Upgrade with thread per connection.\n");
+
+ res = test_upgrade (MHD_USE_AUTO | MHD_USE_INTERNAL_POLLING_THREAD | MHD_USE_THREAD_PER_CONNECTION,
+ 0);
+ error_count += res;
+ if (res)
+ fprintf (stderr,
+ "FAILED: Upgrade with thread per connection and 'auto', return code %d.\n",
+ res);
+ else if (verbose)
+ printf ("PASSED: Upgrade with thread per connection and 'auto'.\n");
+#ifdef HAVE_POLL
+ res = test_upgrade (MHD_USE_INTERNAL_POLLING_THREAD | MHD_USE_THREAD_PER_CONNECTION | MHD_USE_POLL,
+ 0);
+ error_count += res;
+ if (res)
+ fprintf (stderr,
+ "FAILED: Upgrade with thread per connection and poll, return code %d.\n",
+ res);
+ else if (verbose)
+ printf ("PASSED: Upgrade with thread per connection and poll.\n");
+#endif /* HAVE_POLL */
+
+ /* Test different event loops, with and without thread pool */
+ res = test_upgrade (MHD_USE_INTERNAL_POLLING_THREAD,
+ 0);
+ error_count += res;
+ if (res)
+ fprintf (stderr,
+ "FAILED: Upgrade with internal select, return code %d.\n",
+ res);
+ else if (verbose)
+ printf ("PASSED: Upgrade with internal select.\n");
+ res = test_upgrade (MHD_USE_INTERNAL_POLLING_THREAD,
+ 2);
+ error_count += res;
+ if (res)
+ fprintf (stderr,
+ "FAILED: Upgrade with internal select with thread pool, return code %d.\n",
+ res);
+ else if (verbose)
+ printf ("PASSED: Upgrade with internal select with thread pool.\n");
+ res = test_upgrade (MHD_USE_AUTO | MHD_USE_INTERNAL_POLLING_THREAD,
+ 0);
+ error_count += res;
+ if (res)
+ fprintf (stderr,
+ "FAILED: Upgrade with internal 'auto' return code %d.\n",
+ res);
+ else if (verbose)
+ printf ("PASSED: Upgrade with internal 'auto'.\n");
+ res = test_upgrade (MHD_USE_AUTO | MHD_USE_INTERNAL_POLLING_THREAD,
+ 2);
+ error_count += res;
+ if (res)
+ fprintf (stderr,
+ "FAILED: Upgrade with internal 'auto' with thread pool, return code %d.\n",
+ res);
+ else if (verbose)
+ printf ("PASSED: Upgrade with internal 'auto' with thread pool.\n");
+#ifdef HAVE_POLL
+ res = test_upgrade (MHD_USE_POLL_INTERNAL_THREAD,
+ 0);
+ error_count += res;
+ if (res)
+ fprintf (stderr,
+ "FAILED: Upgrade with internal poll, return code %d.\n",
+ res);
+ else if (verbose)
+ printf ("PASSED: Upgrade with internal poll.\n");
+ res = test_upgrade (MHD_USE_POLL_INTERNAL_THREAD,
+ 2);
+ if (res)
+ fprintf (stderr,
+ "FAILED: Upgrade with internal poll with thread pool, return code %d.\n",
+ res);
+ else if (verbose)
+ printf ("PASSED: Upgrade with internal poll with thread pool.\n");
+#endif
+#ifdef EPOLL_SUPPORT
+ res = test_upgrade (MHD_USE_EPOLL_INTERNAL_THREAD,
+ 0);
+ if (res)
+ fprintf (stderr,
+ "FAILED: Upgrade with internal epoll, return code %d.\n",
+ res);
+ else if (verbose)
+ printf ("PASSED: Upgrade with internal epoll.\n");
+ res = test_upgrade (MHD_USE_EPOLL_INTERNAL_THREAD,
+ 2);
+ if (res)
+ fprintf (stderr,
+ "FAILED: Upgrade with internal epoll, return code %d.\n",
+ res);
+ else if (verbose)
+ printf ("PASSED: Upgrade with internal epoll.\n");
+#endif
+ /* report result */
+ if (0 != error_count)
+ fprintf (stderr,
+ "Error (code: %u)\n",
+ error_count);
+#ifdef HTTPS_SUPPORT
+ if (test_tls && (TLS_LIB_GNUTLS == use_tls_tool))
+ gnutls_global_deinit();
+#endif /* HTTPS_SUPPORT */
+ return error_count != 0; /* 0 == pass */
+}