libmicrohttpd

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

commit ebe728629caf9d2f8dff78266f9e4bd436cda25f
parent 72c8b0db78ee801e0009ef4d13849b17e5bd94b2
Author: Christian Grothoff <christian@grothoff.org>
Date:   Mon, 14 Jul 2025 17:05:59 +0200

fix double-close() bug reported by MC on the mailinglist

Diffstat:
MChangeLog | 5+++++
MNEWS | 12++++++++++--
Mconfigure.ac | 7+++----
Mpo/libmicrohttpd.pot | 136++++++++++++++++++++++++++++++++++++++++----------------------------------------
Msrc/Makefile.am | 2+-
Dsrc/lib/Makefile.am | 182-------------------------------------------------------------------------------
Dsrc/lib/action_continue.c | 65-----------------------------------------------------------------
Dsrc/lib/action_from_response.c | 132-------------------------------------------------------------------------------
Dsrc/lib/action_parse_post.c | 61-------------------------------------------------------------
Dsrc/lib/action_process_upload.c | 92-------------------------------------------------------------------------------
Dsrc/lib/action_suspend.c | 138-------------------------------------------------------------------------------
Dsrc/lib/base64.c | 60------------------------------------------------------------
Dsrc/lib/base64.h | 17-----------------
Dsrc/lib/connection_add.c | 1140-------------------------------------------------------------------------------
Dsrc/lib/connection_add.h | 41-----------------------------------------
Dsrc/lib/connection_call_handlers.c | 3724-------------------------------------------------------------------------------
Dsrc/lib/connection_call_handlers.h | 64----------------------------------------------------------------
Dsrc/lib/connection_cleanup.c | 160-------------------------------------------------------------------------------
Dsrc/lib/connection_cleanup.h | 42------------------------------------------
Dsrc/lib/connection_close.c | 103-------------------------------------------------------------------------------
Dsrc/lib/connection_close.h | 56--------------------------------------------------------
Dsrc/lib/connection_finish_forward.c | 95-------------------------------------------------------------------------------
Dsrc/lib/connection_finish_forward.h | 44--------------------------------------------
Dsrc/lib/connection_info.c | 111-------------------------------------------------------------------------------
Dsrc/lib/connection_options.c | 118-------------------------------------------------------------------------------
Dsrc/lib/connection_update_last_activity.c | 65-----------------------------------------------------------------
Dsrc/lib/connection_update_last_activity.h | 40----------------------------------------
Dsrc/lib/daemon_close_all_connections.c | 237-------------------------------------------------------------------------------
Dsrc/lib/daemon_close_all_connections.h | 42------------------------------------------
Dsrc/lib/daemon_create.c | 138-------------------------------------------------------------------------------
Dsrc/lib/daemon_destroy.c | 203-------------------------------------------------------------------------------
Dsrc/lib/daemon_epoll.c | 517-------------------------------------------------------------------------------
Dsrc/lib/daemon_epoll.h | 46----------------------------------------------
Dsrc/lib/daemon_get_timeout.c | 127-------------------------------------------------------------------------------
Dsrc/lib/daemon_info.c | 106-------------------------------------------------------------------------------
Dsrc/lib/daemon_ip_limit.c | 303-------------------------------------------------------------------------------
Dsrc/lib/daemon_ip_limit.h | 60------------------------------------------------------------
Dsrc/lib/daemon_options.c | 780-------------------------------------------------------------------------------
Dsrc/lib/daemon_poll.c | 528-------------------------------------------------------------------------------
Dsrc/lib/daemon_poll.h | 87-------------------------------------------------------------------------------
Dsrc/lib/daemon_quiesce.c | 128-------------------------------------------------------------------------------
Dsrc/lib/daemon_run.c | 83-------------------------------------------------------------------------------
Dsrc/lib/daemon_run_from_select.c | 0
Dsrc/lib/daemon_select.c | 821-------------------------------------------------------------------------------
Dsrc/lib/daemon_select.h | 56--------------------------------------------------------
Dsrc/lib/daemon_start.c | 975-------------------------------------------------------------------------------
Dsrc/lib/init.c | 149-------------------------------------------------------------------------------
Dsrc/lib/init.h | 46----------------------------------------------
Dsrc/lib/internal.c | 288-------------------------------------------------------------------------------
Dsrc/lib/internal.h | 1892-------------------------------------------------------------------------------
Dsrc/lib/md5.c | 268-------------------------------------------------------------------------------
Dsrc/lib/md5.h | 66------------------------------------------------------------------
Dsrc/lib/memorypool.c | 340-------------------------------------------------------------------------------
Dsrc/lib/memorypool.h | 130-------------------------------------------------------------------------------
Dsrc/lib/mhd_assert.h | 49-------------------------------------------------
Dsrc/lib/mhd_byteorder.h | 167-------------------------------------------------------------------------------
Dsrc/lib/mhd_compat.c | 118-------------------------------------------------------------------------------
Dsrc/lib/mhd_compat.h | 91-------------------------------------------------------------------------------
Dsrc/lib/mhd_itc.c | 72------------------------------------------------------------------------
Dsrc/lib/mhd_itc.h | 369-------------------------------------------------------------------------------
Dsrc/lib/mhd_itc_types.h | 77-----------------------------------------------------------------------------
Dsrc/lib/mhd_limits.h | 154-------------------------------------------------------------------------------
Dsrc/lib/mhd_locks.h | 186-------------------------------------------------------------------------------
Dsrc/lib/mhd_mono_clock.c | 378-------------------------------------------------------------------------------
Dsrc/lib/mhd_mono_clock.h | 60------------------------------------------------------------
Dsrc/lib/mhd_sockets.c | 517-------------------------------------------------------------------------------
Dsrc/lib/mhd_sockets.h | 804-------------------------------------------------------------------------------
Dsrc/lib/mhd_str.c | 788-------------------------------------------------------------------------------
Dsrc/lib/mhd_str.h | 276-------------------------------------------------------------------------------
Dsrc/lib/mhd_threads.c | 368-------------------------------------------------------------------------------
Dsrc/lib/mhd_threads.h | 237-------------------------------------------------------------------------------
Dsrc/lib/panic.c | 61-------------------------------------------------------------
Dsrc/lib/reason_phrase.c | 182-------------------------------------------------------------------------------
Dsrc/lib/request.c | 160-------------------------------------------------------------------------------
Dsrc/lib/request_info.c | 86-------------------------------------------------------------------------------
Dsrc/lib/request_resume.c | 201-------------------------------------------------------------------------------
Dsrc/lib/request_resume.h | 43-------------------------------------------
Dsrc/lib/response.c | 260-------------------------------------------------------------------------------
Dsrc/lib/response_for_upgrade.c | 95-------------------------------------------------------------------------------
Dsrc/lib/response_from_buffer.c | 89-------------------------------------------------------------------------------
Dsrc/lib/response_from_callback.c | 80-------------------------------------------------------------------------------
Dsrc/lib/response_from_fd.c | 211-------------------------------------------------------------------------------
Dsrc/lib/response_options.c | 62--------------------------------------------------------------
Dsrc/lib/sysfdsetsize.c | 80-------------------------------------------------------------------------------
Dsrc/lib/sysfdsetsize.h | 36------------------------------------
Dsrc/lib/tsearch.c | 144-------------------------------------------------------------------------------
Dsrc/lib/tsearch.h | 38--------------------------------------
Dsrc/lib/upgrade_process.c | 395-------------------------------------------------------------------------------
Dsrc/lib/upgrade_process.h | 44--------------------------------------------
Dsrc/lib/version.c | 207-------------------------------------------------------------------------------
Msrc/microhttpd/daemon.c | 70+++++++++++++++++++++-------------------------------------------------
91 files changed, 108 insertions(+), 22275 deletions(-)

diff --git a/ChangeLog b/ChangeLog @@ -1,3 +1,8 @@ +Mon Jul 14 05:03:07 PM CEST 2025 + Fix double-close bugs on bind() errors reported by MC on the list. + Removed MHD2 draft code, now in libmicrohttpd2.git. + Releasing GNU libmicrohttpd 1.0.2 -CG + Fri 23 Feb 2024 21:00:00 UZT Releasing GNU libmicrohttpd 1.0.1 -EG diff --git a/NEWS b/NEWS @@ -1,3 +1,11 @@ +Mon Jul 14 2025 05:03:07 PM CEST +Released GNU libmicrohttpd 1.0.2. + + This is a bugfix release. + It primarily fixes a double-close() bug on bind() errors. + + -- Christian Grothoff + Fri 23 Feb 2024 21:00:00 UZT Released GNU libmicrohttpd 1.0.1. @@ -49,7 +57,7 @@ Released GNU libmicrohttpd 0.9.77. branch and back-porting them on top of version 0.9.76. The most notable changes are: some improvements for Digest and Basic authorizations, fixed efficiency for TLS upgraded connections, fixed - processing of folded headers in requests, fixed functionality with + processing of folded headers in requests, fixed functionality with blocking sockets, improved and fixed internal test-suite. The more detailed list of the important changes: @@ -279,7 +287,7 @@ Released GNU libmicrohttpd 0.9.74 # Fixed libcurl test with case-insensitive match for HTTP methods, method names must use case-sensitive match. # Fixed tests compatibility with old libcurl versions. - # Fixed build on W32 with llvm-dlltool (this tool is too + # Fixed build on W32 with llvm-dlltool (this tool is too oversimplified) -- Evgeny Grin (Karlson2k) diff --git a/configure.ac b/configure.ac @@ -23,7 +23,7 @@ # AC_PREREQ([2.64]) LT_PREREQ([2.4.0]) -AC_INIT([GNU libmicrohttpd],[1.0.1],[libmicrohttpd@gnu.org]) +AC_INIT([GNU libmicrohttpd],[1.0.2],[libmicrohttpd@gnu.org]) AC_CONFIG_AUX_DIR([build-aux]) MHD_AUX_DIR='build-aux' # Must be set to the same value as in the previous line AC_CONFIG_HEADERS([MHD_config.h]) @@ -31,7 +31,7 @@ AC_CONFIG_MACRO_DIR([m4]) m4_pattern_forbid([^_?MHD_[A-Z_]+_CC_])dnl LIB_VERSION_CURRENT=74 -LIB_VERSION_REVISION=1 +LIB_VERSION_REVISION=2 LIB_VERSION_AGE=62 AC_SUBST([LIB_VERSION_CURRENT]) AC_SUBST([LIB_VERSION_REVISION]) @@ -4512,7 +4512,7 @@ AS_VAR_IF([enable_tools],["yes"], i][f (1 != pstat_getdynamic(&psd_data, sizeof(psd_data), (size_t)1, 0)) return 2; i][f (0 >= psd_data.psd_proc_cnt) - return 3; + return 3; ]] ) MHD_CHECK_FUNC([vxCpuEnabledGet],[[#include <vxCpuLib.h>]], @@ -5928,7 +5928,6 @@ doc/examples/Makefile m4/Makefile src/Makefile src/include/Makefile -src/lib/Makefile src/microhttpd/Makefile src/microhttpd_ws/Makefile src/examples/Makefile diff --git a/po/libmicrohttpd.pot b/po/libmicrohttpd.pot @@ -6,9 +6,9 @@ #, fuzzy msgid "" msgstr "" -"Project-Id-Version: GNU libmicrohttpd 1.0.1\n" +"Project-Id-Version: GNU libmicrohttpd 1.0.2\n" "Report-Msgid-Bugs-To: libmicrohttpd@gnu.org\n" -"POT-Creation-Date: 2024-02-23 16:58+0100\n" +"POT-Creation-Date: 2025-07-14 17:05+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" @@ -63,7 +63,7 @@ msgstr "" #: src/microhttpd/connection.c:1316 src/microhttpd/connection.c:1335 #: src/microhttpd/daemon.c:3280 src/microhttpd/daemon.c:4108 -#: src/microhttpd/daemon.c:9007 +#: src/microhttpd/daemon.c:8982 msgid "Failed to remove FD from epoll set.\n" msgstr "" @@ -562,7 +562,7 @@ msgid "" "on the platform.\n" msgstr "" -#: src/microhttpd/daemon.c:1474 src/microhttpd/daemon.c:9146 +#: src/microhttpd/daemon.c:1474 src/microhttpd/daemon.c:9118 msgid "" "Initiated daemon shutdown while \"upgraded\" connection was not closed.\n" msgstr "" @@ -681,7 +681,7 @@ msgstr "" msgid "TLS connection on non-TLS daemon.\n" msgstr "" -#: src/microhttpd/daemon.c:2947 src/microhttpd/daemon.c:8768 +#: src/microhttpd/daemon.c:2947 src/microhttpd/daemon.c:8752 msgid "" "Failed to create a new thread because it would have exceeded the system " "limit on the number of threads or no system resources available.\n" @@ -815,9 +815,9 @@ msgstr "" msgid "Accepted connection on socket %d\n" msgstr "" -#: src/microhttpd/daemon.c:4064 src/microhttpd/daemon.c:9188 -#: src/microhttpd/daemon.c:9220 src/microhttpd/daemon.c:9253 -#: src/microhttpd/daemon.c:9367 +#: src/microhttpd/daemon.c:4064 src/microhttpd/daemon.c:9160 +#: src/microhttpd/daemon.c:9192 src/microhttpd/daemon.c:9225 +#: src/microhttpd/daemon.c:9339 msgid "Failed to join a thread.\n" msgstr "" @@ -1122,205 +1122,205 @@ msgid "" "MHD_USE_NO_LISTEN_SOCKET flag set.\n" msgstr "" -#: src/microhttpd/daemon.c:7896 +#: src/microhttpd/daemon.c:7895 msgid "Failed to initialise GnuTLS priorities.\n" msgstr "" -#: src/microhttpd/daemon.c:7908 +#: src/microhttpd/daemon.c:7907 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:7924 +#: src/microhttpd/daemon.c:7923 msgid "Using debug build of libmicrohttpd.\n" msgstr "" -#: src/microhttpd/daemon.c:7938 +#: src/microhttpd/daemon.c:7937 #, c-format msgid "Failed to create inter-thread communication channel: %s\n" msgstr "" -#: src/microhttpd/daemon.c:7953 +#: src/microhttpd/daemon.c:7952 msgid "" "file descriptor for inter-thread communication channel exceeds maximum " "value.\n" msgstr "" -#: src/microhttpd/daemon.c:7992 +#: src/microhttpd/daemon.c:7991 msgid "Specified value for NC_SIZE too large.\n" msgstr "" -#: src/microhttpd/daemon.c:8008 +#: src/microhttpd/daemon.c:8007 #, c-format msgid "Failed to allocate memory for nonce-nc map: %s\n" msgstr "" -#: src/microhttpd/daemon.c:8026 +#: src/microhttpd/daemon.c:8025 msgid "MHD failed to initialize nonce-nc mutex.\n" msgstr "" -#: src/microhttpd/daemon.c:8047 +#: src/microhttpd/daemon.c:8046 msgid "MHD thread polling only works with MHD_USE_INTERNAL_POLLING_THREAD.\n" msgstr "" -#: src/microhttpd/daemon.c:8077 +#: src/microhttpd/daemon.c:8076 msgid "" "MHD_USE_IPv6 is enabled, but 'struct sockaddr *' specified for " "MHD_OPTION_SOCK_ADDR_LEN or MHD_OPTION_SOCK_ADDR is not IPv6 address.\n" msgstr "" -#: src/microhttpd/daemon.c:8096 src/microhttpd/daemon.c:8141 +#: src/microhttpd/daemon.c:8095 src/microhttpd/daemon.c:8140 msgid "The size specified for MHD_OPTION_SOCK_ADDR_LEN option is wrong.\n" msgstr "" -#: src/microhttpd/daemon.c:8108 src/microhttpd/daemon.c:8153 +#: src/microhttpd/daemon.c:8107 src/microhttpd/daemon.c:8152 msgid "" "The value of 'struct sockaddr.sa_len' provided via MHD_OPTION_SOCK_ADDR_LEN " "option is not zero and does not match 'sa_family' value of the same " "structure.\n" msgstr "" -#: src/microhttpd/daemon.c:8191 +#: src/microhttpd/daemon.c:8190 msgid "" "The 'sa_family' of the 'struct sockaddr' provided via MHD_OPTION_SOCK_ADDR " "option is not supported.\n" msgstr "" -#: src/microhttpd/daemon.c:8268 +#: src/microhttpd/daemon.c:8267 #, c-format msgid "Failed to create socket for listening: %s\n" msgstr "" -#: src/microhttpd/daemon.c:8278 src/microhttpd/daemon.c:8476 +#: src/microhttpd/daemon.c:8278 src/microhttpd/daemon.c:8471 #, c-format msgid "" "Listen socket descriptor (%d) is not less than daemon FD_SETSIZE value " "(%d).\n" msgstr "" -#: src/microhttpd/daemon.c:8303 src/microhttpd/daemon.c:8322 -#: src/microhttpd/daemon.c:8345 src/microhttpd/daemon.c:8383 -#: src/microhttpd/daemon.c:8418 src/microhttpd/daemon.c:8450 +#: src/microhttpd/daemon.c:8301 src/microhttpd/daemon.c:8320 +#: src/microhttpd/daemon.c:8343 src/microhttpd/daemon.c:8381 +#: src/microhttpd/daemon.c:8414 src/microhttpd/daemon.c:8446 #, c-format msgid "setsockopt failed: %s\n" msgstr "" -#: src/microhttpd/daemon.c:8355 +#: src/microhttpd/daemon.c:8353 msgid "Cannot allow listening address reuse: SO_REUSEPORT not defined.\n" msgstr "" -#: src/microhttpd/daemon.c:8391 +#: src/microhttpd/daemon.c:8389 msgid "" "Cannot disallow listening address reuse: SO_EXCLUSIVEADDRUSE not defined.\n" msgstr "" -#: src/microhttpd/daemon.c:8429 +#: src/microhttpd/daemon.c:8427 #, c-format msgid "Failed to bind to port %u: %s\n" msgstr "" -#: src/microhttpd/daemon.c:8461 +#: src/microhttpd/daemon.c:8457 #, c-format msgid "Failed to listen for connections: %s\n" msgstr "" -#: src/microhttpd/daemon.c:8564 +#: src/microhttpd/daemon.c:8558 #, c-format msgid "Failed to get listen port number: %s\n" msgstr "" -#: src/microhttpd/daemon.c:8574 +#: src/microhttpd/daemon.c:8568 msgid "" "Failed to get listen port number (`struct sockaddr_storage` too small!?).\n" msgstr "" -#: src/microhttpd/daemon.c:8621 +#: src/microhttpd/daemon.c:8615 msgid "Listen socket has unknown address family!\n" msgstr "" -#: src/microhttpd/daemon.c:8638 +#: src/microhttpd/daemon.c:8632 #, c-format msgid "Failed to set nonblocking mode on listening socket: %s\n" msgstr "" -#: src/microhttpd/daemon.c:8676 +#: src/microhttpd/daemon.c:8668 msgid "" "Combining MHD_USE_THREAD_PER_CONNECTION and MHD_USE_EPOLL is not supported.\n" msgstr "" -#: src/microhttpd/daemon.c:8691 +#: src/microhttpd/daemon.c:8683 msgid "MHD failed to initialize IP connection limit mutex.\n" msgstr "" -#: src/microhttpd/daemon.c:8706 +#: src/microhttpd/daemon.c:8696 msgid "Failed to initialize TLS support.\n" msgstr "" -#: src/microhttpd/daemon.c:8738 src/microhttpd/daemon.c:8821 -#: src/microhttpd/daemon.c:8941 +#: src/microhttpd/daemon.c:8726 src/microhttpd/daemon.c:8803 +#: src/microhttpd/daemon.c:8923 msgid "Failed to initialise internal lists mutex.\n" msgstr "" -#: src/microhttpd/daemon.c:8749 src/microhttpd/daemon.c:8829 -#: src/microhttpd/daemon.c:8952 +#: src/microhttpd/daemon.c:8735 src/microhttpd/daemon.c:8811 +#: src/microhttpd/daemon.c:8932 msgid "Failed to initialise mutex.\n" msgstr "" -#: src/microhttpd/daemon.c:8774 +#: src/microhttpd/daemon.c:8758 #, c-format msgid "Failed to create listen thread: %s\n" msgstr "" -#: src/microhttpd/daemon.c:8840 +#: src/microhttpd/daemon.c:8822 #, c-format msgid "Failed to create worker inter-thread communication channel: %s\n" msgstr "" -#: src/microhttpd/daemon.c:8853 +#: src/microhttpd/daemon.c:8835 msgid "" "File descriptor for worker inter-thread communication channel exceeds " "maximum value.\n" msgstr "" -#: src/microhttpd/daemon.c:8914 +#: src/microhttpd/daemon.c:8896 msgid "" "Failed to create a new pool thread because it would have exceeded the system " "limit on the number of threads or no system resources available.\n" msgstr "" -#: src/microhttpd/daemon.c:8920 +#: src/microhttpd/daemon.c:8902 #, c-format msgid "Failed to create pool thread: %s\n" msgstr "" -#: src/microhttpd/daemon.c:9132 src/microhttpd/daemon.c:9165 +#: src/microhttpd/daemon.c:9104 src/microhttpd/daemon.c:9137 msgid "MHD_stop_daemon() called while we have suspended connections.\n" msgstr "" -#: src/microhttpd/daemon.c:9204 src/microhttpd/daemon.c:9310 -#: src/microhttpd/daemon.c:9348 +#: src/microhttpd/daemon.c:9176 src/microhttpd/daemon.c:9282 +#: src/microhttpd/daemon.c:9320 msgid "Failed to signal shutdown via inter-thread communication channel.\n" msgstr "" -#: src/microhttpd/daemon.c:9278 +#: src/microhttpd/daemon.c:9250 msgid "MHD_stop_daemon() was called twice." msgstr "" -#: src/microhttpd/daemon.c:9867 +#: src/microhttpd/daemon.c:9839 msgid "Failed to initialize winsock.\n" msgstr "" -#: src/microhttpd/daemon.c:9869 +#: src/microhttpd/daemon.c:9841 msgid "Winsock version 2.2 is not available.\n" msgstr "" -#: src/microhttpd/daemon.c:9878 src/microhttpd/daemon.c:9882 +#: src/microhttpd/daemon.c:9850 src/microhttpd/daemon.c:9854 msgid "Failed to initialise multithreading in libgcrypt.\n" msgstr "" -#: src/microhttpd/daemon.c:9888 +#: src/microhttpd/daemon.c:9860 msgid "libgcrypt is too old. MHD was compiled for libgcrypt 1.6.0 or newer.\n" msgstr "" @@ -1469,7 +1469,7 @@ msgstr "" msgid "The SHA-512/256 algorithm is not supported by this MHD build.\n" msgstr "" -#: src/microhttpd/digestauth.c:2628 src/microhttpd/digestauth.c:3571 +#: src/microhttpd/digestauth.c:2628 src/microhttpd/digestauth.c:3572 msgid "Wrong 'malgo3' value, API violation" msgstr "" @@ -1477,7 +1477,7 @@ msgstr "" msgid "The 'auth-int' QOP is not supported.\n" msgstr "" -#: src/microhttpd/digestauth.c:2647 src/microhttpd/digestauth.c:3601 +#: src/microhttpd/digestauth.c:2647 src/microhttpd/digestauth.c:3602 msgid "" "RFC2069 with SHA-256 or SHA-512/256 algorithm is non-standard extension.\n" msgstr "" @@ -1523,56 +1523,56 @@ msgid "Wrong 'userdigest_size' value, does not match 'malgo3', API violation" msgstr "" #: src/microhttpd/digestauth.c:3347 src/microhttpd/digestauth.c:3404 -#: src/microhttpd/digestauth.c:3590 +#: src/microhttpd/digestauth.c:3591 msgid "Wrong 'algo' value, API violation" msgstr "" -#: src/microhttpd/digestauth.c:3544 +#: src/microhttpd/digestauth.c:3545 msgid "Only non-'session' algorithms are supported.\n" msgstr "" -#: src/microhttpd/digestauth.c:3576 +#: src/microhttpd/digestauth.c:3577 msgid "No requested algorithm is supported by this MHD build.\n" msgstr "" -#: src/microhttpd/digestauth.c:3583 +#: src/microhttpd/digestauth.c:3584 msgid "Wrong 'mqop' value, API violation" msgstr "" -#: src/microhttpd/digestauth.c:3597 +#: src/microhttpd/digestauth.c:3598 msgid "" "The 'userhash' and 'charset' ('prefer_utf8') parameters are not compatible " "with RFC2069 and ignored.\n" msgstr "" -#: src/microhttpd/digestauth.c:3612 +#: src/microhttpd/digestauth.c:3613 msgid "The nonce array size is zero.\n" msgstr "" -#: src/microhttpd/digestauth.c:3628 +#: src/microhttpd/digestauth.c:3629 msgid "The 'realm' is too large.\n" msgstr "" -#: src/microhttpd/digestauth.c:3733 +#: src/microhttpd/digestauth.c:3734 msgid "The 'realm' is too large after 'quoting'.\n" msgstr "" -#: src/microhttpd/digestauth.c:3805 +#: src/microhttpd/digestauth.c:3806 msgid "" "TLS library reported hash calculation error, nonce could not be generated.\n" msgstr "" -#: src/microhttpd/digestauth.c:3813 +#: src/microhttpd/digestauth.c:3814 msgid "" "Could not register nonce. Client's requests with this nonce will be always " "'stale'. Probably clients' requests are too intensive.\n" msgstr "" -#: src/microhttpd/digestauth.c:3897 +#: src/microhttpd/digestauth.c:3898 msgid "Failed to add Digest auth header.\n" msgstr "" -#: src/microhttpd/digestauth.c:4038 +#: src/microhttpd/digestauth.c:4039 msgid "Wrong algo value.\n" msgstr "" diff --git a/src/Makefile.am b/src/Makefile.am @@ -11,7 +11,7 @@ endif # Finally (last!) also build experimental lib... if HAVE_EXPERIMENTAL -SUBDIRS += microhttpd_ws lib +SUBDIRS += microhttpd_ws endif if BUILD_EXAMPLES diff --git a/src/lib/Makefile.am b/src/lib/Makefile.am @@ -1,182 +0,0 @@ -# This Makefile.am is in the public domain -AM_CPPFLAGS = \ - -I$(top_srcdir)/src/include \ - -I$(top_srcdir)/src/lib - -AM_CFLAGS = $(HIDDEN_VISIBILITY_CFLAGS) - -# Call "libmicrohttpd2" for now, but only while under -# development. Once we have 'compat' working, this should be changed! -noinst_LTLIBRARIES = \ - libmicrohttpd2.la - -noinst_DATA = -MOSTLYCLEANFILES = - -if W32_SHARED_LIB_EXP -W32_MHD_LIB_LDFLAGS = -Wl,--output-def,$(lt_cv_objdir)/libmicrohttpd2.def -XCClinker -static-libgcc -noinst_DATA += $(lt_cv_objdir)/libmicrohttpd2.lib $(lt_cv_objdir)/libmicrohttpd2.def $(lt_cv_objdir)/libmicrohttpd2.exp -MOSTLYCLEANFILES += $(lt_cv_objdir)/libmicrohttpd2.lib $(lt_cv_objdir)/libmicrohttpd2.def $(lt_cv_objdir)/libmicrohttpd2.exp - -$(lt_cv_objdir)/libmicrohttpd2.def: libmicrohttpd2.la - -$(lt_cv_objdir)/libmicrohttpd2.exp: $(lt_cv_objdir)/libmicrohttpd2.lib - -$(lt_cv_objdir)/libmicrohttpd2.lib: $(lt_cv_objdir)/libmicrohttpd2.def libmicrohttpd2.la $(libmicrohttpd2_la_OBJECTS) -if USE_MS_LIB_TOOL - @echo Creating $@ and libmicrohttpd2.exp by $(MS_LIB_TOOL)... && \ - dll_name=`$(EGREP) -o dlname=\'.+\' libmicrohttpd2.la` && \ - dll_name=$${dll_name#*\'} && dll_name=$${dll_name%\'} && test -n "$$dll_name" && \ - echo Creating $$dll_name by $(MS_LIB_TOOL).. && cd "$(lt_cv_objdir)" && \ - $(MS_LIB_TOOL) -def:libmicrohttpd2.def -name:$$dll_name -out:libmicrohttpd2.lib $(libmicrohttpd2_la_OBJECTS:.lo=.o) && cd .. -else - @echo Creating $@ and libmicrohttpd2.exp by $(DLLTOOL)... && \ - dll_name=`$(EGREP) -o dlname=\'.+\' libmicrohttpd2.la` && \ - dll_name=$${dll_name#*\'} && dll_name=$${dll_name%\'} && test -n "$$dll_name" && \ - echo Creating $$dll_name by $(DLLTOOL).. && cd "$(lt_cv_objdir)" && \ - $(DLLTOOL) -d ./libmicrohttpd2.def -D $$dll_name -l libmicrohttpd2.lib $(libmicrohttpd2_la_OBJECTS:.lo=.o) -e ./libmicrohttpd2.exp && cd .. &&\ - echo Created libmicrohttpd2.exp and libmicrohttpd2.lib. -endif -else - W32_MHD_LIB_LDFLAGS = -endif - -if W32_STATIC_LIB -noinst_DATA += $(lt_cv_objdir)/libmicrohttpd2-static.lib -MOSTLYCLEANFILES += $(lt_cv_objdir)/libmicrohttpd2-static.lib - -$(lt_cv_objdir)/libmicrohttpd2-static.lib: libmicrohttpd2.la $(libmicrohttpd2_la_OBJECTS) -if USE_MS_LIB_TOOL - $(MS_LIB_TOOL) -out:$@ $(libmicrohttpd2_la_OBJECTS:.lo=.o) -else - cp $(lt_cv_objdir)/libmicrohttpd2.a $@ -endif -endif - - -libmicrohttpd2_la_SOURCES = \ - action_continue.c \ - action_from_response.c \ - action_parse_post.c \ - action_process_upload.c \ - action_suspend.c \ - connection_add.c connection_add.h \ - connection_call_handlers.c connection_call_handlers.h \ - connection_cleanup.c connection_cleanup.h \ - connection_close.c connection_close.h \ - connection_finish_forward.c connection_finish_forward.h \ - connection_info.c \ - connection_options.c \ - connection_update_last_activity.c connection_update_last_activity.h \ - daemon_close_all_connections.c daemon_close_all_connections.h \ - daemon_create.c \ - daemon_destroy.c \ - daemon_epoll.c daemon_epoll.h \ - daemon_get_timeout.c \ - daemon_info.c \ - daemon_ip_limit.c daemon_ip_limit.h \ - daemon_options.c \ - daemon_poll.c daemon_poll.h \ - daemon_run.c \ - daemon_select.c daemon_select.h \ - daemon_start.c \ - daemon_quiesce.c \ - init.c init.h \ - internal.c internal.h \ - memorypool.c memorypool.h \ - mhd_assert.h \ - mhd_byteorder.h \ - mhd_compat.c mhd_compat.h \ - mhd_itc.c mhd_itc.h mhd_itc_types.h \ - mhd_limits.h \ - mhd_locks.h \ - mhd_mono_clock.c mhd_mono_clock.h \ - mhd_str.c mhd_str.h \ - mhd_sockets.c mhd_sockets.h \ - mhd_threads.c mhd_threads.h \ - response.c \ - response_for_upgrade.c \ - response_from_buffer.c \ - response_from_callback.c \ - response_from_fd.c \ - response_options.c \ - reason_phrase.c \ - request.c \ - request_info.c \ - request_resume.c \ - request_resume.h \ - sysfdsetsize.c sysfdsetsize.h \ - upgrade_process.c upgrade_process.h \ - panic.c \ - version.c - -libmicrohttpd2_la_CPPFLAGS = \ - $(AM_CPPFLAGS) $(MHD_LIB_CPPFLAGS) $(MHD_TLS_LIB_CPPFLAGS) \ - -DBUILDING_MHD_LIB=1 -libmicrohttpd2_la_CFLAGS = \ - $(AM_CFLAGS) $(MHD_LIB_CFLAGS) $(MHD_TLS_LIB_CFLAGS) -libmicrohttpd2_la_LDFLAGS = \ - $(MHD_LIB_LDFLAGS) \ - $(W32_MHD_LIB_LDFLAGS) $(MHD_TLS_LIB_LDFLAGS) \ - -version-info 0:0:0 # FIXME: fix once closer to release... -if MHD_HAVE_TLS_PLUGIN -libmicrohttpd2_la_LDFLAGS += \ - -ldl -endif - -libmicrohttpd2_la_LIBADD = \ - $(MHD_LIBDEPS) $(MHD_TLS_LIBDEPS) - -if HAVE_W32 -MHD_DLL_RES_SRC = ../microhttpd/microhttpd_dll_res.rc -MHD_DLL_RES_LO = libmicrohttpd2_la-$(MHD_DLL_RES_SRC:.rc=.lo) - -EXTRA_libmicrohttpd2_la_DEPENDENCIES = $(MHD_DLL_RES_LO) -libmicrohttpd2_la_LIBADD += $(MHD_DLL_RES_LO) - -# General rule is not required, but keep it just in case -.rc.lo: - $(LIBTOOL) $(AM_V_lt) --tag=RC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(RC) $(RCFLAGS) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $< -o $@ - -# To add dll resource only to .dll file and exclude it form static -# lib, a little trick was used. Allow libtool to create file.lo, -# file.o and .libs/file.lo, .libs/file.o files, then overwrite file.o -# by empty object generated from empty c-file. Later libtool will -# use .libs/file.o for shared lib and empty file.o for static lib. -# This implementation is based on trick found in liblzma. -# Note: windres does not understand '-isystem' flag, so all -# possible '-isystem' flags are replaced by simple '-I' flags. -$(MHD_DLL_RES_LO): $(MHD_DLL_RES_SRC) - RC_CPP_FLAGS=" $(DEFAULT_INCLUDES) $(INCLUDES) $(libmicrohttpd2_la_CPPFLAGS) $(CPPFLAGS) " && \ - $(LIBTOOL) $(AM_V_lt) --tag=RC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(RC) $(RCFLAGS) $(DEFS) $${RC_CPP_FLAGS// -isystem / -I } $< -o $@ && \ - echo > $@-empty.c && $(CC) $(AM_CFLAGS) $(CFLAGS) -c $@-empty.c -o $(@:.lo=.o) && rm -f $@-empty.c -endif - -if USE_COVERAGE - AM_CFLAGS += --coverage -endif - -if !MHD_USE_SYS_TSEARCH -libmicrohttpd2_la_SOURCES += \ - tsearch.c tsearch.h -endif - -# TBD! -if HAVE_POSTPROCESSOR -#libmicrohttpd2_la_SOURCES += \ -# postprocessor.c -endif - -# TBD! -if ENABLE_DAUTH -#libmicrohttpd2_la_SOURCES += \ -# digestauth.c \ -# md5.c md5.h -endif - -# TBD! -if ENABLE_BAUTH -#libmicrohttpd2_la_SOURCES += \ -# basicauth.c \ -# base64.c base64.h -endif diff --git a/src/lib/action_continue.c b/src/lib/action_continue.c @@ -1,65 +0,0 @@ -/* - This file is part of libmicrohttpd - Copyright (C) 2007-2018 Daniel Pittman and Christian Grothoff - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. - - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with this library; if not, write to the Free Software - Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA -*/ - -/** - * @file lib/action_continue.c - * @brief implementation of MHD_action_continue() - * @author Christian Grothoff - */ -#include "internal.h" - - -/** - * The continue action is being run. Continue - * handling the upload. - * - * @param cls NULL - * @param request the request to apply the action to - * @return #MHD_SC_OK on success - */ -static enum MHD_StatusCode -cont_action (void *cls, - struct MHD_Request *request) -{ - (void) cls; - (void) request; - /* not sure yet, but this function body may - just legitimately stay empty... */ - return MHD_SC_OK; -} - - -/** - * Action telling MHD to continue processing the upload. - * - * @return action operation, never NULL - */ -const struct MHD_Action * -MHD_action_continue (void) -{ - static struct MHD_Action acont = { - .action = &cont_action, - .action_cls = NULL - }; - - return &acont; -} - - -/* end of action_continue.c */ diff --git a/src/lib/action_from_response.c b/src/lib/action_from_response.c @@ -1,132 +0,0 @@ -/* - This file is part of libmicrohttpd - Copyright (C) 2007-2018 Daniel Pittman and Christian Grothoff - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. - - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with this library; if not, write to the Free Software - Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA -*/ - -/** - * @file lib/action_from_response.c - * @brief implementation of #MHD_action_from_response() - * @author Christian Grothoff - */ -#include "internal.h" -#include "connection_call_handlers.h" - - -/** - * A response was given as the desired action for a @a request. - * Queue the response for the request. - * - * @param cls the `struct MHD_Response` - * @param request the request we are processing - * @return #MHD_SC_OK on success - */ -static enum MHD_StatusCode -response_action (void *cls, - struct MHD_Request *request) -{ - struct MHD_Response *response = cls; - struct MHD_Daemon *daemon = request->daemon; - - /* If daemon was shut down in parallel, - * response will be aborted now or on later stage. */ - if (daemon->shutdown) - return MHD_SC_DAEMON_ALREADY_SHUTDOWN; - -#ifdef UPGRADE_SUPPORT - if ( (NULL != response->upgrade_handler) && - daemon->disallow_upgrade) - { -#ifdef HAVE_MESSAGES - MHD_DLOG (daemon, - MHD_SC_UPGRADE_ON_DAEMON_WITH_UPGRADE_DISALLOWED, - _ ( - "Attempted 'upgrade' connection on daemon without MHD_ALLOW_UPGRADE option!\n")); -#endif - return MHD_SC_UPGRADE_ON_DAEMON_WITH_UPGRADE_DISALLOWED; - } -#endif /* UPGRADE_SUPPORT */ - request->response = response; -#if defined(_MHD_HAVE_SENDFILE) - if ( (-1 == response->fd) -#if HTTPS_SUPPORT - || (NULL != daemon->tls_api) -#endif - ) - request->resp_sender = MHD_resp_sender_std; - else - request->resp_sender = MHD_resp_sender_sendfile; -#endif /* _MHD_HAVE_SENDFILE */ - - if ( (MHD_METHOD_HEAD == request->method) || - (MHD_HTTP_OK > response->status_code) || - (MHD_HTTP_NO_CONTENT == response->status_code) || - (MHD_HTTP_NOT_MODIFIED == response->status_code) ) - { - /* if this is a "HEAD" request, or a status code for - which a body is not allowed, pretend that we - have already sent the full message body. */ - request->response_write_position = response->total_size; - } - if ( (MHD_REQUEST_HEADERS_PROCESSED == request->state) && - ( (MHD_METHOD_POST == request->method) || - (MHD_METHOD_PUT == request->method) ) ) - { - /* response was queued "early", refuse to read body / footers or - further requests! */ - request->connection->read_closed = true; - request->state = MHD_REQUEST_FOOTERS_RECEIVED; - } - if (! request->in_idle) - (void) MHD_request_handle_idle_ (request); - return MHD_SC_OK; -} - - -/** - * Converts a @a response to an action. If @a consume - * is set, the reference to the @a response is consumed - * by the conversion. If @a consume is #MHD_NO, then - * the response can be converted to actions in the future. - * However, the @a response is frozen by this step and - * must no longer be modified (i.e. by setting headers). - * - * @param response response to convert, not NULL - * @param destroy_after_use should the response object be consumed? - * @return corresponding action, never returns NULL - * - * Implementation note: internally, this is largely just - * a cast (and possibly an RC increment operation), - * as a response *is* an action. As no memory is - * allocated, this operation cannot fail. - */ -_MHD_EXTERN const struct MHD_Action * -MHD_action_from_response (struct MHD_Response *response, - enum MHD_Bool destroy_after_use) -{ - response->action.action = &response_action; - response->action.action_cls = response; - if (! destroy_after_use) - { - MHD_mutex_lock_chk_ (&response->mutex); - response->reference_count++; - MHD_mutex_unlock_chk_ (&response->mutex); - } - return &response->action; -} - - -/* end of action_from_response */ diff --git a/src/lib/action_parse_post.c b/src/lib/action_parse_post.c @@ -1,61 +0,0 @@ -/* - This file is part of libmicrohttpd - Copyright (C) 2007-2018 Daniel Pittman and Christian Grothoff - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. - - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with this library; if not, write to the Free Software - Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA -*/ - -/** - * @file lib/action_parse_post.c - * @brief implementation of MHD_action_parse_post() - * @author Christian Grothoff - */ -#include "internal.h" - - -/** - * Create an action that parses a POST request. - * - * This action can be used to (incrementally) parse the data portion - * of a POST request. Note that some buggy browsers fail to set the - * encoding type. If you want to support those, you may have to call - * #MHD_set_connection_value with the proper encoding type before - * returning this action (if no supported encoding type is detected, - * returning this action will cause a bad request to be returned to - * the client). - * - * @param buffer_size maximum number of bytes to use for - * internal buffering (used only for the parsing, - * specifically the parsing of the keys). A - * tiny value (256-1024) should be sufficient. - * Do NOT use a value smaller than 256. For good - * performance, use 32 or 64k (i.e. 65536). - * @param iter iterator to be called with the parsed data, - * Must NOT be NULL. - * @param iter_cls first argument to @a iter - * @return NULL on error (out of memory, unsupported encoding), - * otherwise a PP handle - * @ingroup request - */ -const struct MHD_Action * -MHD_action_parse_post (size_t buffer_size, - MHD_PostDataIterator iter, - void *iter_cls) -{ - return NULL; /* not yet implemented */ -} - - -/* end of action_parse_post.c */ diff --git a/src/lib/action_process_upload.c b/src/lib/action_process_upload.c @@ -1,92 +0,0 @@ -/* - This file is part of libmicrohttpd - Copyright (C) 2007-2018 Daniel Pittman and Christian Grothoff - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. - - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with this library; if not, write to the Free Software - Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA -*/ - -/** - * @file lib/action_process_upload.c - * @brief implementation of MHD_action_process_upload() - * @author Christian Grothoff - */ -#include "internal.h" - - -/** - * Internal details about an upload action. - */ -struct UploadAction -{ - /** - * An upload action is an action. This field - * must come first! - */ - struct MHD_Action action; - - MHD_UploadCallback uc; - - void *uc_cls; - -}; - - -/** - * The application wants to process uploaded data for - * the given request. Do it! - * - * @param cls the `struct UploadAction` with the - * function we are to call for upload data - * @param request the request for which we are to process - * upload data - * @return #MHD_SC_OK on success - */ -static enum MHD_StatusCode -upload_action (void *cls, - struct MHD_Request *request) -{ - struct UploadAction *ua = cls; - - (void) ua; - // FIXME: implement! - return -1; -} - - -/** - * Create an action that handles an upload. - * - * @param uc function to call with uploaded data - * @param uc_cls closure for @a uc - * @return NULL on error (out of memory) - * @ingroup action - */ -const struct MHD_Action * -MHD_action_process_upload (MHD_UploadCallback uc, - void *uc_cls) -{ - struct UploadAction *ua; - - if (NULL == (ua = malloc (sizeof (struct UploadAction)))) - return NULL; - ua->action.action = &upload_action; - ua->action.action_cls = ua; - ua->uc = uc; - ua->uc_cls = uc_cls; - return &ua->action; -} - - -/* end of action_process_upload.c */ diff --git a/src/lib/action_suspend.c b/src/lib/action_suspend.c @@ -1,138 +0,0 @@ -/* - This file is part of libmicrohttpd - Copyright (C) 2007-2018 Daniel Pittman and Christian Grothoff - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. - - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with this library; if not, write to the Free Software - Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA -*/ - -/** - * @file lib/action_suspend.c - * @brief implementation of MHD_action_suspend() - * @author Christian Grothoff - */ -#include "internal.h" - - -/** - * The suspend action is being run. Suspend handling - * of the given request. - * - * @param cls NULL - * @param request the request to apply the action to - * @return #MHD_SC_OK on success - */ -static enum MHD_StatusCode -suspend_action (void *cls, - struct MHD_Request *request) -{ - (void) cls; - struct MHD_Connection *connection = request->connection; - struct MHD_Daemon *daemon = connection->daemon; - - MHD_mutex_lock_chk_ (&daemon->cleanup_connection_mutex); - if (connection->resuming) - { - /* suspending again while we didn't even complete resuming yet */ - connection->resuming = false; - MHD_mutex_unlock_chk_ (&daemon->cleanup_connection_mutex); - return MHD_SC_OK; - } - if (daemon->threading_mode != MHD_TM_THREAD_PER_CONNECTION) - { - if (connection->connection_timeout == - daemon->connection_default_timeout) - XDLL_remove (daemon->normal_timeout_head, - daemon->normal_timeout_tail, - connection); - else - XDLL_remove (daemon->manual_timeout_head, - daemon->manual_timeout_tail, - connection); - } - DLL_remove (daemon->connections_head, - daemon->connections_tail, - connection); - mhd_assert (! connection->suspended); - DLL_insert (daemon->suspended_connections_head, - daemon->suspended_connections_tail, - connection); - connection->suspended = true; -#ifdef EPOLL_SUPPORT - if (MHD_ELS_EPOLL == daemon->event_loop_syscall) - { - if (0 != (connection->epoll_state & MHD_EPOLL_STATE_IN_EREADY_EDLL)) - { - EDLL_remove (daemon->eready_head, - daemon->eready_tail, - connection); - connection->epoll_state &= ~MHD_EPOLL_STATE_IN_EREADY_EDLL; - } - if (0 != (connection->epoll_state & MHD_EPOLL_STATE_IN_EPOLL_SET)) - { - if (0 != epoll_ctl (daemon->epoll_fd, - EPOLL_CTL_DEL, - connection->socket_fd, - NULL)) - MHD_PANIC (_ ("Failed to remove FD from epoll set.\n")); - connection->epoll_state &= ~MHD_EPOLL_STATE_IN_EPOLL_SET; - } - connection->epoll_state |= MHD_EPOLL_STATE_SUSPENDED; - } -#endif - MHD_mutex_unlock_chk_ (&daemon->cleanup_connection_mutex); - return MHD_SC_OK; -} - - -/** - * Suspend handling of network data for a given request. This can - * be used to dequeue a request from MHD's event loop for a while. - * - * If you use this API in conjunction with a internal select or a - * thread pool, you must set the option #MHD_USE_ITC to - * ensure that a resumed request is immediately processed by MHD. - * - * Suspended requests continue to count against the total number of - * requests allowed (per daemon, as well as per IP, if such limits - * are set). Suspended requests will NOT time out; timeouts will - * restart when the request handling is resumed. While a - * request is suspended, MHD will not detect disconnects by the - * client. - * - * The only safe time to suspend a request is from either a - * #MHD_RequestHeaderCallback, #MHD_UploadCallback, or a - * #MHD_RequestfetchResponseCallback. Suspending a request - * at any other time will cause an assertion failure. - * - * Finally, it is an API violation to call #MHD_daemon_stop() while - * having suspended requests (this will at least create memory and - * socket leaks or lead to undefined behavior). You must explicitly - * resume all requests before stopping the daemon. - * - * @return action to cause a request to be suspended. - */ -const struct MHD_Action * -MHD_action_suspend (void) -{ - static const struct MHD_Action suspend = { - .action = &suspend_action, - .action_cls = NULL - }; - - return &suspend; -} - - -/* end of action_suspend.c */ diff --git a/src/lib/base64.c b/src/lib/base64.c @@ -1,60 +0,0 @@ -/* - * This code implements the BASE64 algorithm. - * This code is in the public domain; do with it what you wish. - * - * @file base64.c - * @brief This code implements the BASE64 algorithm - * @author Matthieu Speder - */ -#include "base64.h" - -static const char base64_digits[] = -{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 62, 0, 0, 0, 63, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, - 0, 0, 0, -1, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, - 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 0, 0, 0, 0, 0, 0, 26, - 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, - 45, 46, 47, 48, 49, 50, 51, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; - - -char * -BASE64Decode (const char *src) -{ - size_t in_len = strlen (src); - char *dest; - char *result; - - if (in_len % 4) - { - /* Wrong base64 string length */ - return NULL; - } - result = dest = malloc (in_len / 4 * 3 + 1); - if (NULL == result) - return NULL; /* out of memory */ - while (*src) - { - char a = base64_digits[(unsigned char) *(src++)]; - char b = base64_digits[(unsigned char) *(src++)]; - char c = base64_digits[(unsigned char) *(src++)]; - char d = base64_digits[(unsigned char) *(src++)]; - *(dest++) = (a << 2) | ((b & 0x30) >> 4); - if (c == (char) -1) - break; - *(dest++) = ((b & 0x0f) << 4) | ((c & 0x3c) >> 2); - if (d == (char) -1) - break; - *(dest++) = ((c & 0x03) << 6) | d; - } - *dest = 0; - return result; -} - - -/* end of base64.c */ diff --git a/src/lib/base64.h b/src/lib/base64.h @@ -1,17 +0,0 @@ -/* - * This code implements the BASE64 algorithm. - * This code is in the public domain; do with it what you wish. - * - * @file base64.c - * @brief This code implements the BASE64 algorithm - * @author Matthieu Speder - */ -#ifndef BASE64_H -#define BASE64_H - -#include "platform.h" - -char * -BASE64Decode (const char *src); - -#endif /* !BASE64_H */ diff --git a/src/lib/connection_add.c b/src/lib/connection_add.c @@ -1,1140 +0,0 @@ -/* - This file is part of libmicrohttpd - Copyright (C) 2007-2018 Daniel Pittman and Christian Grothoff - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. - - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with this library; if not, write to the Free Software - Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA -*/ -/** - * @file lib/connection_add.c - * @brief functions to add connection to our active set - * @author Christian Grothoff - */ -#include "internal.h" -#include "connection_add.h" -#include "connection_call_handlers.h" -#include "connection_close.h" -#include "connection_finish_forward.h" -#include "connection_update_last_activity.h" -#include "daemon_ip_limit.h" -#include "daemon_select.h" -#include "daemon_poll.h" -#include "mhd_sockets.h" - - -#ifdef UPGRADE_SUPPORT -/** - * Main function of the thread that handles an individual connection - * after it was "upgraded" when #MHD_USE_THREAD_PER_CONNECTION is set. - * @remark To be called only from thread that process - * connection's recv(), send() and response. - * - * @param con the connection this thread will handle - */ -static void -thread_main_connection_upgrade (struct MHD_Connection *con) -{ -#ifdef HTTPS_SUPPORT - struct MHD_Daemon *daemon = con->daemon; - - /* Here, we need to bi-directionally forward - until the application tells us that it is done - with the socket; */ - if ( (NULL != daemon->tls_api) && - (MHD_ELS_POLL != daemon->event_loop_syscall) ) - { - MHD_daemon_upgrade_connection_with_select_ (con); - } -#ifdef HAVE_POLL - else if (NULL != daemon->tls_api) - { - MHD_daemon_upgrade_connection_with_poll_ (con); - } -#endif - /* end HTTPS */ -#endif /* HTTPS_SUPPORT */ - /* TLS forwarding was finished. Cleanup socketpair. */ - MHD_connection_finish_forward_ (con); - /* Do not set 'urh->clean_ready' yet as 'urh' will be used - * in connection thread for a little while. */ -} - - -#endif /* UPGRADE_SUPPORT */ - - -/** - * Main function of the thread that handles an individual - * connection when #MHD_USE_THREAD_PER_CONNECTION is set. - * - * @param data the `struct MHD_Connection` this thread will handle - * @return always 0 - */ -static MHD_THRD_RTRN_TYPE_ MHD_THRD_CALL_SPEC_ -thread_main_handle_connection (void *data) -{ - struct MHD_Connection *con = data; - struct MHD_Daemon *daemon = con->daemon; - int num_ready; - fd_set rs; - fd_set ws; - fd_set es; - MHD_socket maxsock; - struct timeval tv; - struct timeval *tvp; - time_t now; -#if WINDOWS -#ifdef HAVE_POLL - int extra_slot; -#endif /* HAVE_POLL */ -#define EXTRA_SLOTS 1 -#else /* !WINDOWS */ -#define EXTRA_SLOTS 0 -#endif /* !WINDOWS */ -#ifdef HAVE_POLL - struct pollfd p[1 + EXTRA_SLOTS]; -#endif -#undef EXTRA_SLOTS -#ifdef HAVE_POLL - const bool use_poll = (MHD_ELS_POLL == daemon->event_loop_syscall); -#else /* ! HAVE_POLL */ - const bool use_poll = false; -#endif /* ! HAVE_POLL */ - bool was_suspended = false; - - MHD_thread_init_ (&con->pid); - - while ( (! daemon->shutdown) && - (MHD_REQUEST_CLOSED != con->request.state) ) - { - const time_t timeout = daemon->connection_default_timeout; -#ifdef UPGRADE_SUPPORT - struct MHD_UpgradeResponseHandle *const urh = con->request.urh; -#else /* ! UPGRADE_SUPPORT */ - static const void *const urh = NULL; -#endif /* ! UPGRADE_SUPPORT */ - - if ( (con->suspended) && - (NULL == urh) ) - { - /* Connection was suspended, wait for resume. */ - was_suspended = true; - if (! use_poll) - { - FD_ZERO (&rs); - if (! MHD_add_to_fd_set_ (MHD_itc_r_fd_ (daemon->itc), - &rs, - NULL, - FD_SETSIZE)) - { - #ifdef HAVE_MESSAGES - MHD_DLOG (con->daemon, - MHD_SC_SOCKET_OUTSIDE_OF_FDSET_RANGE, - _ ("Failed to add FD to fd_set.\n")); - #endif - goto exit; - } - if (0 > MHD_SYS_select_ (MHD_itc_r_fd_ (daemon->itc) + 1, - &rs, - NULL, - NULL, - NULL)) - { - const int err = MHD_socket_get_error_ (); - - if (MHD_SCKT_ERR_IS_EINTR_ (err)) - continue; -#ifdef HAVE_MESSAGES - MHD_DLOG (con->daemon, - MHD_SC_UNEXPECTED_SELECT_ERROR, - _ ("Error during select (%d): `%s'\n"), - err, - MHD_socket_strerr_ (err)); -#endif - break; - } - } -#ifdef HAVE_POLL - else /* use_poll */ - { - p[0].events = POLLIN; - p[0].fd = MHD_itc_r_fd_ (daemon->itc); - p[0].revents = 0; - if (0 > MHD_sys_poll_ (p, - 1, - -1)) - { - if (MHD_SCKT_LAST_ERR_IS_ (MHD_SCKT_EINTR_)) - continue; -#ifdef HAVE_MESSAGES - MHD_DLOG (con->daemon, - MHD_SC_UNEXPECTED_POLL_ERROR, - _ ("Error during poll: `%s'\n"), - MHD_socket_last_strerr_ ()); -#endif - break; - } - } -#endif /* HAVE_POLL */ - MHD_itc_clear_ (daemon->itc); - continue; /* Check again for resume. */ - } /* End of "suspended" branch. */ - - if (was_suspended) - { - MHD_connection_update_last_activity_ (con); /* Reset timeout timer. */ - /* Process response queued during suspend and update states. */ - MHD_request_handle_idle_ (&con->request); - was_suspended = false; - } - - tvp = NULL; - - if ( (MHD_EVENT_LOOP_INFO_BLOCK == con->request.event_loop_info) -#ifdef HTTPS_SUPPORT - || ( (con->tls_read_ready) && - (MHD_EVENT_LOOP_INFO_READ == con->request.event_loop_info) ) -#endif /* HTTPS_SUPPORT */ - ) - { - /* do not block: more data may be inside of TLS buffers waiting or - * application must provide response data */ - tv.tv_sec = 0; - tv.tv_usec = 0; - tvp = &tv; - } - if ( (NULL == tvp) && - (timeout > 0) ) - { - now = MHD_monotonic_sec_counter (); - if (now - con->last_activity > timeout) - tv.tv_sec = 0; - else - { - const time_t seconds_left = timeout - (now - con->last_activity); -#if ! defined(_WIN32) || defined(__CYGWIN__) - tv.tv_sec = seconds_left; -#else /* _WIN32 && !__CYGWIN__ */ - if (seconds_left > TIMEVAL_TV_SEC_MAX) - tv.tv_sec = TIMEVAL_TV_SEC_MAX; - else - tv.tv_sec = (_MHD_TIMEVAL_TV_SEC_TYPE) seconds_left; -#endif /* _WIN32 && ! __CYGWIN__ */ - } - tv.tv_usec = 0; - tvp = &tv; - } - if (! use_poll) - { - /* use select */ - bool err_state = false; - - FD_ZERO (&rs); - FD_ZERO (&ws); - FD_ZERO (&es); - maxsock = MHD_INVALID_SOCKET; - switch (con->request.event_loop_info) - { - case MHD_EVENT_LOOP_INFO_READ: - if (! MHD_add_to_fd_set_ (con->socket_fd, - &rs, - &maxsock, - FD_SETSIZE)) - err_state = true; - break; - case MHD_EVENT_LOOP_INFO_WRITE: - if (! MHD_add_to_fd_set_ (con->socket_fd, - &ws, - &maxsock, - FD_SETSIZE)) - err_state = true; - break; - case MHD_EVENT_LOOP_INFO_BLOCK: - if (! MHD_add_to_fd_set_ (con->socket_fd, - &es, - &maxsock, - FD_SETSIZE)) - err_state = true; - break; - case MHD_EVENT_LOOP_INFO_CLEANUP: - /* how did we get here!? */ - goto exit; - } -#if WINDOWS - if (MHD_ITC_IS_VALID_ (daemon->itc) ) - { - if (! MHD_add_to_fd_set_ (MHD_itc_r_fd_ (daemon->itc), - &rs, - &maxsock, - FD_SETSIZE)) - err_state = 1; - } -#endif - if (err_state) - { -#ifdef HAVE_MESSAGES - MHD_DLOG (con->daemon, - MHD_SC_SOCKET_OUTSIDE_OF_FDSET_RANGE, - _ ("Failed to add FD to fd_set.\n")); -#endif - goto exit; - } - - num_ready = MHD_SYS_select_ (maxsock + 1, - &rs, - &ws, - &es, - tvp); - if (num_ready < 0) - { - const int err = MHD_socket_get_error_ (); - - if (MHD_SCKT_ERR_IS_EINTR_ (err)) - continue; -#ifdef HAVE_MESSAGES - MHD_DLOG (con->daemon, - MHD_SC_UNEXPECTED_SELECT_ERROR, - _ ("Error during select (%d): `%s'\n"), - err, - MHD_socket_strerr_ (err)); -#endif - break; - } -#if WINDOWS - /* Clear ITC before other processing so additional - * signals will trigger select() again */ - if ( (MHD_ITC_IS_VALID_ (daemon->itc)) && - (FD_ISSET (MHD_itc_r_fd_ (daemon->itc), - &rs)) ) - MHD_itc_clear_ (daemon->itc); -#endif - if (MHD_NO == - MHD_connection_call_handlers_ (con, - FD_ISSET (con->socket_fd, - &rs), - FD_ISSET (con->socket_fd, - &ws), - FD_ISSET (con->socket_fd, - &es)) ) - goto exit; - } -#ifdef HAVE_POLL - else - { - /* use poll */ - memset (&p, - 0, - sizeof (p)); - p[0].fd = con->socket_fd; - switch (con->request.event_loop_info) - { - case MHD_EVENT_LOOP_INFO_READ: - p[0].events |= POLLIN | MHD_POLL_EVENTS_ERR_DISC; - break; - case MHD_EVENT_LOOP_INFO_WRITE: - p[0].events |= POLLOUT | MHD_POLL_EVENTS_ERR_DISC; - break; - case MHD_EVENT_LOOP_INFO_BLOCK: - p[0].events |= MHD_POLL_EVENTS_ERR_DISC; - break; - case MHD_EVENT_LOOP_INFO_CLEANUP: - /* how did we get here!? */ - goto exit; - } -#if WINDOWS - extra_slot = 0; - if (MHD_ITC_IS_VALID_ (daemon->itc)) - { - p[1].events |= POLLIN; - p[1].fd = MHD_itc_r_fd_ (daemon->itc); - p[1].revents = 0; - extra_slot = 1; - } -#endif - if (MHD_sys_poll_ (p, -#if WINDOWS - 1 + extra_slot, -#else - 1, -#endif - (NULL == tvp) ? -1 : tv.tv_sec * 1000) < 0) - { - if (MHD_SCKT_LAST_ERR_IS_ (MHD_SCKT_EINTR_)) - continue; -#ifdef HAVE_MESSAGES - MHD_DLOG (con->daemon, - MHD_SC_UNEXPECTED_POLL_ERROR, - _ ("Error during poll: `%s'\n"), - MHD_socket_last_strerr_ ()); -#endif - break; - } -#if WINDOWS - /* Clear ITC before other processing so additional - * signals will trigger poll() again */ - if ( (MHD_ITC_IS_VALID_ (daemon->itc)) && - (0 != (p[1].revents & (POLLERR | POLLHUP | POLLIN))) ) - MHD_itc_clear_ (daemon->itc); -#endif - if (MHD_NO == - MHD_connection_call_handlers_ (con, - (0 != (p[0].revents & POLLIN)), - (0 != (p[0].revents & POLLOUT)), - (0 != (p[0].revents & (POLLERR - | - MHD_POLL_REVENTS_ERR_DISC))) )) - goto exit; - } -#endif -#ifdef UPGRADE_SUPPORT - if (MHD_REQUEST_UPGRADE == con->request.state) - { - /* Normal HTTP processing is finished, - * notify application. */ - if (NULL != con->request.response->termination_cb) - con->request.response->termination_cb - (con->request.response->termination_cb_cls, - MHD_REQUEST_TERMINATED_COMPLETED_OK, - con->request.client_context); - thread_main_connection_upgrade (con); - /* MHD_connection_finish_forward_() was called by thread_main_connection_upgrade(). */ - - /* "Upgraded" data will not be used in this thread from this point. */ - con->request.urh->clean_ready = true; - /* If 'urh->was_closed' set to true, connection will be - * moved immediately to cleanup list. Otherwise connection - * will stay in suspended list until 'urh' will be marked - * with 'was_closed' by application. */ - MHD_request_resume (&con->request); - - /* skip usual clean up */ - return (MHD_THRD_RTRN_TYPE_) 0; - } -#endif /* UPGRADE_SUPPORT */ - } -#if DEBUG_CLOSE -#ifdef HAVE_MESSAGES - MHD_DLOG (con->daemon, - MHD_SC_THREAD_TERMINATING, - _ ("Processing thread terminating. Closing connection.\n")); -#endif -#endif - if (MHD_REQUEST_CLOSED != con->request.state) - MHD_connection_close_ (con, - (daemon->shutdown) ? - MHD_REQUEST_TERMINATED_DAEMON_SHUTDOWN : - MHD_REQUEST_TERMINATED_WITH_ERROR); - MHD_request_handle_idle_ (&con->request); -exit: - if (NULL != con->request.response) - { - MHD_response_queue_for_destroy (con->request.response); - con->request.response = NULL; - } - - if (MHD_INVALID_SOCKET != con->socket_fd) - { - shutdown (con->socket_fd, - SHUT_WR); - /* 'socket_fd' can be used in other thread to signal shutdown. - * To avoid data races, do not close socket here. Daemon will - * use more connections only after cleanup anyway. */ - } - return (MHD_THRD_RTRN_TYPE_) 0; -} - - -/** - * Callback for receiving data from the socket. - * - * @param connection the MHD connection structure - * @param other where to write received data to - * @param i maximum size of other (in bytes) - * @return positive value for number of bytes actually received or - * negative value for error number MHD_ERR_xxx_ - */ -static ssize_t -recv_param_adapter (struct MHD_Connection *connection, - void *other, - size_t i) -{ - ssize_t ret; - - if ( (MHD_INVALID_SOCKET == connection->socket_fd) || - (MHD_REQUEST_CLOSED == connection->request.state) ) - { - return MHD_ERR_NOTCONN_; - } - if (i > MHD_SCKT_SEND_MAX_SIZE_) - i = MHD_SCKT_SEND_MAX_SIZE_; /* return value limit */ - - ret = MHD_recv_ (connection->socket_fd, - other, - i); - if (0 > ret) - { - const int err = MHD_socket_get_error_ (); - if (MHD_SCKT_ERR_IS_EAGAIN_ (err)) - { -#ifdef EPOLL_SUPPORT - /* Got EAGAIN --- no longer read-ready */ - connection->epoll_state &= ~MHD_EPOLL_STATE_READ_READY; -#endif /* EPOLL_SUPPORT */ - return MHD_ERR_AGAIN_; - } - if (MHD_SCKT_ERR_IS_EINTR_ (err)) - return MHD_ERR_AGAIN_; - if (MHD_SCKT_ERR_IS_ (err, MHD_SCKT_ECONNRESET_)) - return MHD_ERR_CONNRESET_; - /* Treat any other error as hard error. */ - return MHD_ERR_NOTCONN_; - } -#ifdef EPOLL_SUPPORT - else if (i > (size_t) ret) - connection->epoll_state &= ~MHD_EPOLL_STATE_READ_READY; -#endif /* EPOLL_SUPPORT */ - return ret; -} - - -/** - * Callback for writing data to the socket. - * - * @param connection the MHD connection structure - * @param other data to write - * @param i number of bytes to write - * @return positive value for number of bytes actually sent or - * negative value for error number MHD_ERR_xxx_ - */ -static ssize_t -send_param_adapter (struct MHD_Connection *connection, - const void *other, - size_t i) -{ - ssize_t ret; - - if ( (MHD_INVALID_SOCKET == connection->socket_fd) || - (MHD_REQUEST_CLOSED == connection->request.state) ) - { - return MHD_ERR_NOTCONN_; - } - if (i > MHD_SCKT_SEND_MAX_SIZE_) - i = MHD_SCKT_SEND_MAX_SIZE_; /* return value limit */ - - ret = MHD_send_ (connection->socket_fd, - other, - i); - if (0 > ret) - { - const int err = MHD_socket_get_error_ (); - - if (MHD_SCKT_ERR_IS_EAGAIN_ (err)) - { -#ifdef EPOLL_SUPPORT - /* EAGAIN --- no longer write-ready */ - connection->epoll_state &= ~MHD_EPOLL_STATE_WRITE_READY; -#endif /* EPOLL_SUPPORT */ - return MHD_ERR_AGAIN_; - } - if (MHD_SCKT_ERR_IS_EINTR_ (err)) - return MHD_ERR_AGAIN_; - if (MHD_SCKT_ERR_IS_ (err, MHD_SCKT_ECONNRESET_)) - return MHD_ERR_CONNRESET_; - /* Treat any other error as hard error. */ - return MHD_ERR_NOTCONN_; - } -#ifdef EPOLL_SUPPORT - else if (i > (size_t) ret) - connection->epoll_state &= ~MHD_EPOLL_STATE_WRITE_READY; -#endif /* EPOLL_SUPPORT */ - return ret; -} - - -/** - * Add another client connection to the set of connections - * managed by MHD. This API is usually not needed (since - * MHD will accept inbound connections on the server socket). - * Use this API in special cases, for example if your HTTP - * server is behind NAT and needs to connect out to the - * HTTP client. - * - * The given client socket will be managed (and closed!) by MHD after - * this call and must no longer be used directly by the application - * afterwards. - * - * @param daemon daemon that manages the connection - * @param client_socket socket to manage (MHD will expect - * to receive an HTTP request from this socket next). - * @param addr IP address of the client - * @param addrlen number of bytes in @a addr - * @param external_add perform additional operations needed due - * to the application calling us directly - * @param non_blck indicate that socket in non-blocking mode - * @return #MHD_SC_OK on success - */ -static enum MHD_StatusCode -internal_add_connection (struct MHD_Daemon *daemon, - MHD_socket client_socket, - const struct sockaddr *addr, - socklen_t addrlen, - bool external_add, - bool non_blck) -{ - enum MHD_StatusCode sc; - struct MHD_Connection *connection; - int eno = 0; - - /* Direct add to master daemon could happen only with "external" add mode. */ - mhd_assert ( (NULL == daemon->worker_pool) || - (external_add) ); - if ( (external_add) && - (NULL != daemon->worker_pool) ) - { - unsigned int i; - - /* have a pool, try to find a pool with capacity; we use the - 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 = - &daemon->worker_pool[(i + client_socket) % daemon->worker_pool_size]; - if (worker->connections < worker->global_connection_limit) - return internal_add_connection (worker, - client_socket, - addr, - addrlen, - true, - non_blck); - } - /* all pools are at their connection limit, must refuse */ - MHD_socket_close_chk_ (client_socket); -#if ENFILE - errno = ENFILE; -#endif - return MHD_SC_LIMIT_CONNECTIONS_REACHED; - } - - if ( (! MHD_SCKT_FD_FITS_FDSET_ (client_socket, - NULL)) && - (MHD_ELS_SELECT == daemon->event_loop_syscall) ) - { -#ifdef HAVE_MESSAGES - MHD_DLOG (daemon, - MHD_SC_SOCKET_OUTSIDE_OF_FDSET_RANGE, - _ ("Socket descriptor larger than FD_SETSIZE: %d > %d\n"), - (int) client_socket, - (int) FD_SETSIZE); -#endif - MHD_socket_close_chk_ (client_socket); -#if EINVAL - errno = EINVAL; -#endif - return MHD_SC_SOCKET_OUTSIDE_OF_FDSET_RANGE; - } - -#ifdef MHD_socket_nosignal_ - if (! MHD_socket_nosignal_ (client_socket)) - { -#ifdef HAVE_MESSAGES - MHD_DLOG (daemon, - MHD_SC_ACCEPT_CONFIGURE_NOSIGPIPE_FAILED, - _ ("Failed to set SO_NOSIGPIPE on accepted socket: %s\n"), - MHD_socket_last_strerr_ ()); -#endif -#ifndef MSG_NOSIGNAL - /* Cannot use socket as it can produce SIGPIPE. */ -#ifdef ENOTSOCK - errno = ENOTSOCK; -#endif /* ENOTSOCK */ - return MHD_SC_ACCEPT_CONFIGURE_NOSIGPIPE_FAILED; -#endif /* ! MSG_NOSIGNAL */ - } -#endif /* MHD_socket_nosignal_ */ - - -#ifdef HAVE_MESSAGES -#if DEBUG_CONNECT - MHD_DLOG (daemon, - MHD_SC_CONNECTION_ACCEPTED, - _ ("Accepted connection on socket %d.\n"), - client_socket); -#endif -#endif - if ( (daemon->connections == daemon->global_connection_limit) || - (MHD_NO == MHD_ip_limit_add (daemon, - addr, - addrlen)) ) - { - /* above connection limit - reject */ -#ifdef HAVE_MESSAGES - MHD_DLOG (daemon, - MHD_SC_LIMIT_CONNECTIONS_REACHED, - _ ( - "Server reached connection limit. Closing inbound connection.\n")); -#endif - MHD_socket_close_chk_ (client_socket); -#if ENFILE - errno = ENFILE; -#endif - return MHD_SC_LIMIT_CONNECTIONS_REACHED; - } - - /* apply connection acceptance policy if present */ - if ( (NULL != daemon->accept_policy_cb) && - (MHD_NO == - daemon->accept_policy_cb (daemon->accept_policy_cb_cls, - addr, - addrlen)) ) - { -#if DEBUG_CLOSE -#ifdef HAVE_MESSAGES - MHD_DLOG (daemon, - MHD_SC_ACCEPT_POLICY_REJECTED, - _ ("Connection rejected by application. Closing connection.\n")); -#endif -#endif - MHD_socket_close_chk_ (client_socket); - MHD_ip_limit_del (daemon, - addr, - addrlen); -#if EACCESS - errno = EACCESS; -#endif - return MHD_SC_ACCEPT_POLICY_REJECTED; - } - - if (NULL == - (connection = MHD_calloc_ (1, - sizeof (struct MHD_Connection)))) - { - eno = errno; -#ifdef HAVE_MESSAGES - MHD_DLOG (daemon, - MHD_SC_CONNECTION_MALLOC_FAILURE, - "Error allocating memory: %s\n", - MHD_strerror_ (errno)); -#endif - MHD_socket_close_chk_ (client_socket); - MHD_ip_limit_del (daemon, - addr, - addrlen); - errno = eno; - return MHD_SC_CONNECTION_MALLOC_FAILURE; - } - connection->pool - = MHD_pool_create (daemon->connection_memory_limit_b); - if (NULL == connection->pool) - { -#ifdef HAVE_MESSAGES - MHD_DLOG (daemon, - MHD_SC_POOL_MALLOC_FAILURE, - _ ("Error allocating memory: %s\n"), - MHD_strerror_ (errno)); -#endif - MHD_socket_close_chk_ (client_socket); - MHD_ip_limit_del (daemon, - addr, - addrlen); - free (connection); -#if ENOMEM - errno = ENOMEM; -#endif - return MHD_SC_POOL_MALLOC_FAILURE; - } - - connection->connection_timeout = daemon->connection_default_timeout; - memcpy (&connection->addr, - addr, - addrlen); - connection->addr_len = addrlen; - connection->socket_fd = client_socket; - connection->sk_nonblck = non_blck; - connection->daemon = daemon; - connection->last_activity = MHD_monotonic_sec_counter (); - -#ifdef HTTPS_SUPPORT - if (NULL != daemon->tls_api) - { - connection->tls_cs - = daemon->tls_api->setup_connection (daemon->tls_api->cls, - NULL /* FIXME */); - if (NULL == connection->tls_cs) - { - eno = EINVAL; - sc = -1; // FIXME! - goto cleanup; - } - } - else -#endif /* ! HTTPS_SUPPORT */ - { - /* set default connection handlers */ - connection->recv_cls = &recv_param_adapter; - connection->send_cls = &send_param_adapter; - } - MHD_mutex_lock_chk_ (&daemon->cleanup_connection_mutex); - /* Firm check under lock. */ - if (daemon->connections >= daemon->global_connection_limit) - { - MHD_mutex_unlock_chk_ (&daemon->cleanup_connection_mutex); - /* above connection limit - reject */ -#ifdef HAVE_MESSAGES - MHD_DLOG (daemon, - MHD_SC_LIMIT_CONNECTIONS_REACHED, - _ ( - "Server reached connection limit. Closing inbound connection.\n")); -#endif -#if ENFILE - eno = ENFILE; -#endif - sc = MHD_SC_LIMIT_CONNECTIONS_REACHED; - goto cleanup; - } - daemon->connections++; - if (MHD_TM_THREAD_PER_CONNECTION != daemon->threading_mode) - { - XDLL_insert (daemon->normal_timeout_head, - daemon->normal_timeout_tail, - connection); - } - DLL_insert (daemon->connections_head, - daemon->connections_tail, - connection); - MHD_mutex_unlock_chk_ (&daemon->cleanup_connection_mutex); - - if (NULL != daemon->notify_connection_cb) - daemon->notify_connection_cb (daemon->notify_connection_cb_cls, - connection, - MHD_CONNECTION_NOTIFY_STARTED); - - /* attempt to create handler thread */ - if (MHD_TM_THREAD_PER_CONNECTION == daemon->threading_mode) - { - if (! MHD_create_named_thread_ (&connection->pid, - "MHD-connection", - daemon->thread_stack_limit_b, - &thread_main_handle_connection, - connection)) - { - eno = errno; -#ifdef HAVE_MESSAGES - MHD_DLOG (daemon, - MHD_SC_THREAD_LAUNCH_FAILURE, - "Failed to create a thread: %s\n", - MHD_strerror_ (eno)); -#endif - sc = MHD_SC_THREAD_LAUNCH_FAILURE; - goto cleanup; - } - } - else - { - connection->pid = daemon->pid; - } -#ifdef EPOLL_SUPPORT - if (MHD_ELS_EPOLL == daemon->event_loop_syscall) - { - if ( (! daemon->enable_turbo) || - (external_add)) - { /* Do not manipulate EReady DL-list in 'external_add' mode. */ - struct epoll_event event; - - event.events = EPOLLIN | EPOLLOUT | EPOLLPRI | EPOLLET; - event.data.ptr = connection; - if (0 != epoll_ctl (daemon->epoll_fd, - EPOLL_CTL_ADD, - client_socket, - &event)) - { - eno = errno; -#ifdef HAVE_MESSAGES - MHD_DLOG (daemon, - MHD_SC_EPOLL_CTL_ADD_FAILED, - _ ("Call to epoll_ctl failed: %s\n"), - MHD_socket_last_strerr_ ()); -#endif - sc = MHD_SC_EPOLL_CTL_ADD_FAILED; - goto cleanup; - } - connection->epoll_state |= MHD_EPOLL_STATE_IN_EPOLL_SET; - } - else - { - 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); - } - } - else /* This 'else' is combined with next 'if'. */ -#endif - if ( (MHD_TM_THREAD_PER_CONNECTION != daemon->threading_mode) && - (external_add) && - (MHD_ITC_IS_VALID_ (daemon->itc)) && - (! MHD_itc_activate_ (daemon->itc, - "n")) ) - { -#ifdef HAVE_MESSAGES - MHD_DLOG (daemon, - MHD_SC_ITC_USE_FAILED, - _ ( - "Failed to signal new connection via inter-thread communication channel (not necessarily fatal, continuing anyway).\n")); -#endif - } - return MHD_SC_OK; - -cleanup: - if (NULL != daemon->notify_connection_cb) - daemon->notify_connection_cb (daemon->notify_connection_cb_cls, - connection, - MHD_CONNECTION_NOTIFY_CLOSED); -#ifdef HTTPS_SUPPORT - if ( (NULL != daemon->tls_api) && - (NULL != connection->tls_cs) ) - daemon->tls_api->teardown_connection (daemon->tls_api->cls, - connection->tls_cs); -#endif /* HTTPS_SUPPORT */ - MHD_socket_close_chk_ (client_socket); - MHD_ip_limit_del (daemon, - addr, - addrlen); - MHD_mutex_lock_chk_ (&daemon->cleanup_connection_mutex); - if (MHD_TM_THREAD_PER_CONNECTION != daemon->threading_mode) - { - XDLL_remove (daemon->normal_timeout_head, - daemon->normal_timeout_tail, - connection); - } - DLL_remove (daemon->connections_head, - daemon->connections_tail, - connection); - MHD_mutex_unlock_chk_ (&daemon->cleanup_connection_mutex); - MHD_pool_destroy (connection->pool); - free (connection); - if (0 != eno) - errno = eno; - else - errno = EINVAL; - return sc; -} - - -/** - * Add another client connection to the set of connections managed by - * MHD. This API is usually not needed (since MHD will accept inbound - * connections on the server socket). Use this API in special cases, - * for example if your HTTP server is behind NAT and needs to connect - * out to the HTTP client, or if you are building a proxy. - * - * If you use this API in conjunction with a internal select or a - * thread pool, you must set the option #MHD_USE_ITC to ensure that - * the freshly added connection is immediately processed by MHD. - * - * The given client socket will be managed (and closed!) by MHD after - * this call and must no longer be used directly by the application - * afterwards. - * - * @param daemon daemon that manages the connection - * @param client_socket socket to manage (MHD will expect - * to receive an HTTP request from this socket next). - * @param addr IP address of the client - * @param addrlen number of bytes in @a addr - * @return #MHD_YES on success, #MHD_NO if this daemon could - * not handle the connection (i.e. malloc() failed, etc). - * The socket will be closed in any case; `errno` is - * set to indicate further details about the error. - * @ingroup specialized - */ -enum MHD_StatusCode -MHD_daemon_add_connection (struct MHD_Daemon *daemon, - MHD_socket client_socket, - const struct sockaddr *addr, - socklen_t addrlen) -{ - bool sk_nonbl; - - if (! MHD_socket_nonblocking_ (client_socket)) - { -#ifdef HAVE_MESSAGES - MHD_DLOG (daemon, - MHD_SC_ACCEPT_CONFIGURE_NONBLOCKING_FAILED, - _ ("Failed to set nonblocking mode on new client socket: %s\n"), - MHD_socket_last_strerr_ ()); -#endif - sk_nonbl = false; - } - else - { - sk_nonbl = true; - } - - if ( (daemon->enable_turbo) && - (! MHD_socket_noninheritable_ (client_socket)) ) - { -#ifdef HAVE_MESSAGES - MHD_DLOG (daemon, - MHD_SC_ACCEPT_CONFIGURE_NOINHERIT_FAILED, - _ ("Failed to set noninheritable mode on new client socket.\n")); -#endif - } - return internal_add_connection (daemon, - client_socket, - addr, - addrlen, - true, - sk_nonbl); -} - - -/** - * Accept an incoming connection and create the MHD_Connection object - * for it. This function also enforces policy by way of checking with - * the accept policy callback. @remark To be called only from thread - * that process daemon's select()/poll()/etc. - * - * @param daemon handle with the listen socket - * @return #MHD_SC_OK on success - */ -enum MHD_StatusCode -MHD_accept_connection_ (struct MHD_Daemon *daemon) -{ - struct sockaddr_storage addrstorage; - struct sockaddr *addr = (struct sockaddr *) &addrstorage; - socklen_t addrlen; - MHD_socket s; - MHD_socket fd; - bool sk_nonbl; - - addrlen = sizeof (addrstorage); - memset (addr, - 0, - sizeof (addrstorage)); - if ( (MHD_INVALID_SOCKET == (fd = daemon->listen_socket)) || - (daemon->was_quiesced) ) - return MHD_SC_DAEMON_ALREADY_QUIESCED; -#ifdef USE_ACCEPT4 - s = accept4 (fd, - addr, - &addrlen, - MAYBE_SOCK_CLOEXEC | MAYBE_SOCK_NONBLOCK); - sk_nonbl = (0 != MAYBE_SOCK_NONBLOCK); -#else /* ! USE_ACCEPT4 */ - s = accept (fd, - addr, - &addrlen); - sk_nonbl = false; -#endif /* ! USE_ACCEPT4 */ - if ( (MHD_INVALID_SOCKET == s) || - (addrlen <= 0) ) - { - const int err = MHD_socket_get_error_ (); - - /* This could be a common occurrence with multiple worker threads */ - if (MHD_SCKT_ERR_IS_ (err, - MHD_SCKT_EINVAL_)) - return MHD_SC_DAEMON_ALREADY_SHUTDOWN; /* can happen during shutdown, let's hope this is the cause... */ - if (MHD_SCKT_ERR_IS_DISCNN_BEFORE_ACCEPT_ (err)) - return MHD_SC_ACCEPT_FAST_DISCONNECT; /* do not print error if client just disconnected early */ - if (MHD_SCKT_ERR_IS_EAGAIN_ (err) ) - return MHD_SC_ACCEPT_FAILED_EAGAIN; - if (MHD_INVALID_SOCKET != s) - { - MHD_socket_close_chk_ (s); - } - if (MHD_SCKT_ERR_IS_LOW_RESOURCES_ (err) ) - { - /* system/process out of resources */ - if (0 == daemon->connections) - { -#ifdef HAVE_MESSAGES - /* Not setting 'at_limit' flag, as there is no way it - would ever be cleared. Instead trying to produce - bit fat ugly warning. */ - MHD_DLOG (daemon, - MHD_SC_ACCEPT_SYSTEM_LIMIT_REACHED_INSTANTLY, - _ ( - "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")); -#endif - return MHD_SC_ACCEPT_SYSTEM_LIMIT_REACHED_INSTANTLY; - } - else - { - MHD_mutex_lock_chk_ (&daemon->cleanup_connection_mutex); - daemon->at_limit = true; - MHD_mutex_unlock_chk_ (&daemon->cleanup_connection_mutex); -#ifdef HAVE_MESSAGES - MHD_DLOG (daemon, - MHD_SC_ACCEPT_SYSTEM_LIMIT_REACHED, - _ ( - "Hit process or system resource limit at %u connections, temporarily suspending accept(). Consider setting a lower MHD_OPTION_CONNECTION_LIMIT.\n"), - (unsigned int) daemon->connections); -#endif - return MHD_SC_ACCEPT_SYSTEM_LIMIT_REACHED; - } - } -#ifdef HAVE_MESSAGES - MHD_DLOG (daemon, - MHD_SC_ACCEPT_FAILED_UNEXPECTEDLY, - _ ("Error accepting connection: %s\n"), - MHD_socket_strerr_ (err)); -#endif - return MHD_SC_ACCEPT_FAILED_UNEXPECTEDLY; - } -#if ! defined(USE_ACCEPT4) || ! defined(HAVE_SOCK_NONBLOCK) - if (! MHD_socket_nonblocking_ (s)) - { -#ifdef HAVE_MESSAGES - MHD_DLOG (daemon, - MHD_SC_ACCEPT_CONFIGURE_NONBLOCKING_FAILED, - _ ( - "Failed to set nonblocking mode on incoming connection socket: %s\n"), - MHD_socket_last_strerr_ ()); -#endif - } - else - sk_nonbl = true; -#endif /* !USE_ACCEPT4 || !HAVE_SOCK_NONBLOCK */ -#if ! defined(USE_ACCEPT4) || ! defined(SOCK_CLOEXEC) - if (! MHD_socket_noninheritable_ (s)) - { -#ifdef HAVE_MESSAGES - MHD_DLOG (daemon, - MHD_SC_ACCEPT_CONFIGURE_NOINHERIT_FAILED, - _ ( - "Failed to set noninheritable mode on incoming connection socket.\n")); -#endif - } -#endif /* !USE_ACCEPT4 || !SOCK_CLOEXEC */ -#ifdef HAVE_MESSAGES -#if DEBUG_CONNECT - MHD_DLOG (daemon, - MHD_SC_CONNECTION_ACCEPTED, - _ ("Accepted connection on socket %d.\n"), - s); -#endif -#endif - return internal_add_connection (daemon, - s, - addr, - addrlen, - false, - sk_nonbl); -} - - -/* end of connection_add.c */ diff --git a/src/lib/connection_add.h b/src/lib/connection_add.h @@ -1,41 +0,0 @@ -/* - This file is part of libmicrohttpd - Copyright (C) 2007-2018 Daniel Pittman and Christian Grothoff - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. - - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with this library; if not, write to the Free Software - Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA -*/ -/** - * @file lib/connection_add.h - * @brief functions to add connection to our active set - * @author Christian Grothoff - */ - -#ifndef CONNECTION_ADD_H -#define CONNECTION_ADD_H - -/** - * Accept an incoming connection and create the MHD_Connection object - * for it. This function also enforces policy by way of checking with - * the accept policy callback. @remark To be called only from thread - * that process daemon's select()/poll()/etc. - * - * @param daemon handle with the listen socket - * @return #MHD_SC_OK on success - */ -enum MHD_StatusCode -MHD_accept_connection_ (struct MHD_Daemon *daemon) -MHD_NONNULL (1); - -#endif diff --git a/src/lib/connection_call_handlers.c b/src/lib/connection_call_handlers.c @@ -1,3724 +0,0 @@ -/* - This file is part of libmicrohttpd - Copyright (C) 2007-2018 Daniel Pittman and Christian Grothoff - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. - - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with this library; if not, write to the Free Software - Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA -*/ -/** - * @file lib/connection_call_handlers.c - * @brief call the connection's handlers based on the event trigger - * @author Christian Grothoff - */ -#include "internal.h" -#include "connection_call_handlers.h" -#include "connection_update_last_activity.h" -#include "connection_close.h" - - -#ifdef MHD_LINUX_SOLARIS_SENDFILE -#include <sys/sendfile.h> -#endif /* MHD_LINUX_SOLARIS_SENDFILE */ -#if defined(HAVE_FREEBSD_SENDFILE) || defined(HAVE_DARWIN_SENDFILE) -#include <sys/types.h> -#include <sys/socket.h> -#include <sys/uio.h> -#endif /* HAVE_FREEBSD_SENDFILE || HAVE_DARWIN_SENDFILE */ - - -/** - * sendfile() chuck size - */ -#define MHD_SENFILE_CHUNK_ (0x20000) - -/** - * sendfile() chuck size for thread-per-connection - */ -#define MHD_SENFILE_CHUNK_THR_P_C_ (0x200000) - - -/** - * Response text used when the request (http header) is too big to - * be processed. - * - * Intentionally empty here to keep our memory footprint - * minimal. - */ -#ifdef HAVE_MESSAGES -#define REQUEST_TOO_BIG \ - "<html><head><title>Request too big</title></head><body>Your HTTP header was too big for the memory constraints of this webserver.</body></html>" -#else -#define REQUEST_TOO_BIG "" -#endif - -/** - * Response text used when the request (http header) does not - * contain a "Host:" header and still claims to be HTTP 1.1. - * - * Intentionally empty here to keep our memory footprint - * minimal. - */ -#ifdef HAVE_MESSAGES -#define REQUEST_LACKS_HOST \ - "<html><head><title>&quot;Host:&quot; header required</title></head><body>In HTTP 1.1, requests must include a &quot;Host:&quot; header, and your HTTP 1.1 request lacked such a header.</body></html>" -#else -#define REQUEST_LACKS_HOST "" -#endif - -/** - * Response text used when the request (http header) is - * malformed. - * - * Intentionally empty here to keep our memory footprint - * minimal. - */ -#ifdef HAVE_MESSAGES -#define REQUEST_MALFORMED \ - "<html><head><title>Request malformed</title></head><body>Your HTTP request was syntactically incorrect.</body></html>" -#else -#define REQUEST_MALFORMED "" -#endif - -/** - * Response text used when there is an internal server error. - * - * Intentionally empty here to keep our memory footprint - * minimal. - */ -#ifdef HAVE_MESSAGES -#define INTERNAL_ERROR \ - "<html><head><title>Internal server error</title></head><body>Please ask the developer of this Web server to carefully read the GNU libmicrohttpd documentation about connection management and blocking.</body></html>" -#else -#define INTERNAL_ERROR "" -#endif - - -#ifdef HAVE_FREEBSD_SENDFILE -#ifdef SF_FLAGS -/** - * FreeBSD sendfile() flags - */ -static int freebsd_sendfile_flags_; - -/** - * FreeBSD sendfile() flags for thread-per-connection - */ -static int freebsd_sendfile_flags_thd_p_c_; -#endif /* SF_FLAGS */ - - -/** - * Initialises static variables. - * - * FIXME: make sure its actually called! - */ -void -MHD_conn_init_static_ (void) -{ -/* FreeBSD 11 and later allow to specify read-ahead size - * and handles SF_NODISKIO differently. - * SF_FLAGS defined only on FreeBSD 11 and later. */ -#ifdef SF_FLAGS - long sys_page_size = sysconf (_SC_PAGESIZE); - if (0 > sys_page_size) - { /* Failed to get page size. */ - freebsd_sendfile_flags_ = SF_NODISKIO; - freebsd_sendfile_flags_thd_p_c_ = SF_NODISKIO; - } - else - { - freebsd_sendfile_flags_ = - SF_FLAGS ((uint16_t) (MHD_SENFILE_CHUNK_ / sys_page_size), SF_NODISKIO); - freebsd_sendfile_flags_thd_p_c_ = - SF_FLAGS ((uint16_t) (MHD_SENFILE_CHUNK_THR_P_C_ / sys_page_size), - SF_NODISKIO); - } -#endif /* SF_FLAGS */ -} - - -#endif /* HAVE_FREEBSD_SENDFILE */ - - -/** - * Message to transmit when http 1.1 request is received - */ -#define HTTP_100_CONTINUE "HTTP/1.1 100 Continue\r\n\r\n" - - -/** - * A serious error occurred, close the - * connection (and notify the application). - * - * @param connection connection to close with error - * @param sc the reason for closing the connection - * @param emsg error message (can be NULL) - */ -static void -connection_close_error (struct MHD_Connection *connection, - enum MHD_StatusCode sc, - const char *emsg) -{ -#ifdef HAVE_MESSAGES - if (NULL != emsg) - MHD_DLOG (connection->daemon, - sc, - emsg); -#else /* ! HAVE_MESSAGES */ - (void) emsg; /* Mute compiler warning. */ - (void) sc; -#endif /* ! HAVE_MESSAGES */ - MHD_connection_close_ (connection, - MHD_REQUEST_TERMINATED_WITH_ERROR); -} - - -/** - * Macro to only include error message in call to - * #connection_close_error() if we have HAVE_MESSAGES. - */ -#ifdef HAVE_MESSAGES -#define CONNECTION_CLOSE_ERROR(c, sc, emsg) connection_close_error (c, sc, emsg) -#else -#define CONNECTION_CLOSE_ERROR(c, sc, emsg) connection_close_error (c, sc, NULL) -#endif - - -/** - * Try growing the read buffer. We initially claim half the available - * buffer space for the read buffer (the other half being left for - * management data structures; the write buffer can in the end take - * virtually everything as the read buffer can be reduced to the - * minimum necessary at that point. - * - * @param request the request for which to grow the buffer - * @return true on success, false on failure - */ -static bool -try_grow_read_buffer (struct MHD_Request *request) -{ - struct MHD_Daemon *daemon = request->daemon; - void *buf; - size_t new_size; - - if (0 == request->read_buffer_size) - new_size = daemon->connection_memory_limit_b / 2; - else - new_size = request->read_buffer_size - + daemon->connection_memory_increment_b; - buf = MHD_pool_reallocate (request->connection->pool, - request->read_buffer, - request->read_buffer_size, - new_size); - if (NULL == buf) - return false; - /* we can actually grow the buffer, do it! */ - request->read_buffer = buf; - request->read_buffer_size = new_size; - return true; -} - - -/** - * This function handles a particular request when it has been - * determined that there is data to be read off a socket. - * - * @param request request to handle - */ -static void -MHD_request_handle_read_ (struct MHD_Request *request) -{ - struct MHD_Daemon *daemon = request->daemon; - struct MHD_Connection *connection = request->connection; - ssize_t bytes_read; - - if ( (MHD_REQUEST_CLOSED == request->state) || - (connection->suspended) ) - return; -#ifdef HTTPS_SUPPORT - { - struct MHD_TLS_Plugin *tls; - - if ( (NULL != (tls = daemon->tls_api)) && - (! tls->handshake (tls->cls, - connection->tls_cs)) ) - return; - } -#endif /* HTTPS_SUPPORT */ - - /* make sure "read" has a reasonable number of bytes - in buffer to use per system call (if possible) */ - if (request->read_buffer_offset - + daemon->connection_memory_increment_b > - request->read_buffer_size) - try_grow_read_buffer (request); - - if (request->read_buffer_size == request->read_buffer_offset) - return; /* No space for receiving data. */ - bytes_read = connection->recv_cls (connection, - &request->read_buffer - [request->read_buffer_offset], - request->read_buffer_size - - request->read_buffer_offset); - if (bytes_read < 0) - { - if (MHD_ERR_AGAIN_ == bytes_read) - return; /* No new data to process. */ - if (MHD_ERR_CONNRESET_ == bytes_read) - { - CONNECTION_CLOSE_ERROR (connection, - (MHD_REQUEST_INIT == request->state) - ? MHD_SC_CONNECTION_CLOSED - : MHD_SC_CONNECTION_RESET_CLOSED, - (MHD_REQUEST_INIT == request->state) - ? NULL - : _ ( - "Socket disconnected while reading request.\n")); - return; - } - CONNECTION_CLOSE_ERROR (connection, - (MHD_REQUEST_INIT == request->state) - ? MHD_SC_CONNECTION_CLOSED - : MHD_SC_CONNECTION_READ_FAIL_CLOSED, - (MHD_REQUEST_INIT == request->state) - ? NULL - : _ ( - "Connection socket is closed due to error when reading request.\n")); - return; - } - - if (0 == bytes_read) - { /* Remote side closed connection. */ - connection->read_closed = true; - MHD_connection_close_ (connection, - MHD_REQUEST_TERMINATED_CLIENT_ABORT); - return; - } - request->read_buffer_offset += bytes_read; - MHD_connection_update_last_activity_ (connection); -#if DEBUG_STATES - MHD_DLOG (daemon, - MHD_SC_STATE_MACHINE_STATUS_REPORT, - _ ("In function %s handling connection at state: %s\n"), - __FUNCTION__, - MHD_state_to_string (request->state)); -#endif - switch (request->state) - { - case MHD_REQUEST_INIT: - case MHD_REQUEST_URL_RECEIVED: - case MHD_REQUEST_HEADER_PART_RECEIVED: - case MHD_REQUEST_HEADERS_RECEIVED: - case MHD_REQUEST_HEADERS_PROCESSED: - case MHD_REQUEST_CONTINUE_SENDING: - case MHD_REQUEST_CONTINUE_SENT: - case MHD_REQUEST_BODY_RECEIVED: - case MHD_REQUEST_FOOTER_PART_RECEIVED: - /* nothing to do but default action */ - if (connection->read_closed) - { - MHD_connection_close_ (connection, - MHD_REQUEST_TERMINATED_READ_ERROR); - } - return; - case MHD_REQUEST_CLOSED: - return; -#ifdef UPGRADE_SUPPORT - case MHD_REQUEST_UPGRADE: - mhd_assert (0); - return; -#endif /* UPGRADE_SUPPORT */ - default: - /* shrink read buffer to how much is actually used */ - MHD_pool_reallocate (connection->pool, - request->read_buffer, - request->read_buffer_size + 1, - request->read_buffer_offset); - break; - } - return; -} - - -#if defined(_MHD_HAVE_SENDFILE) -/** - * Function for sending responses backed by file FD. - * - * @param connection the MHD connection structure - * @return actual number of bytes sent - */ -static ssize_t -sendfile_adapter (struct MHD_Connection *connection) -{ - struct MHD_Daemon *daemon = connection->daemon; - struct MHD_Request *request = &connection->request; - struct MHD_Response *response = request->response; - ssize_t ret; - const int file_fd = response->fd; - uint64_t left; - uint64_t offsetu64; -#ifndef HAVE_SENDFILE64 - const uint64_t max_off_t = (uint64_t) OFF_T_MAX; -#else /* HAVE_SENDFILE64 */ - const uint64_t max_off_t = (uint64_t) OFF64_T_MAX; -#endif /* HAVE_SENDFILE64 */ -#ifdef MHD_LINUX_SOLARIS_SENDFILE -#ifndef HAVE_SENDFILE64 - off_t offset; -#else /* HAVE_SENDFILE64 */ - off64_t offset; -#endif /* HAVE_SENDFILE64 */ -#endif /* MHD_LINUX_SOLARIS_SENDFILE */ -#ifdef HAVE_FREEBSD_SENDFILE - off_t sent_bytes; - int flags = 0; -#endif -#ifdef HAVE_DARWIN_SENDFILE - off_t len; -#endif /* HAVE_DARWIN_SENDFILE */ - const bool used_thr_p_c = (MHD_TM_THREAD_PER_CONNECTION == - daemon->threading_mode); - const size_t chunk_size = used_thr_p_c ? MHD_SENFILE_CHUNK_THR_P_C_ : - MHD_SENFILE_CHUNK_; - size_t send_size = 0; - - mhd_assert (MHD_resp_sender_sendfile == request->resp_sender); - offsetu64 = request->response_write_position + response->fd_off; - left = response->total_size - request->response_write_position; - /* Do not allow system to stick sending on single fast connection: - * use 128KiB chunks (2MiB for thread-per-connection). */ - send_size = (left > chunk_size) ? chunk_size : (size_t) left; - if (max_off_t < offsetu64) - { /* Retry to send with standard 'send()'. */ - request->resp_sender = MHD_resp_sender_std; - return MHD_ERR_AGAIN_; - } -#ifdef MHD_LINUX_SOLARIS_SENDFILE -#ifndef HAVE_SENDFILE64 - offset = (off_t) offsetu64; - ret = sendfile (connection->socket_fd, - file_fd, - &offset, - send_size); -#else /* HAVE_SENDFILE64 */ - offset = (off64_t) offsetu64; - ret = sendfile64 (connection->socket_fd, - file_fd, - &offset, - send_size); -#endif /* HAVE_SENDFILE64 */ - if (0 > ret) - { - const int err = MHD_socket_get_error_ (); - - if (MHD_SCKT_ERR_IS_EAGAIN_ (err)) - { -#ifdef EPOLL_SUPPORT - /* EAGAIN --- no longer write-ready */ - connection->epoll_state &= ~MHD_EPOLL_STATE_WRITE_READY; -#endif /* EPOLL_SUPPORT */ - return MHD_ERR_AGAIN_; - } - if (MHD_SCKT_ERR_IS_EINTR_ (err)) - return MHD_ERR_AGAIN_; -#ifdef HAVE_LINUX_SENDFILE - if (MHD_SCKT_ERR_IS_ (err, - MHD_SCKT_EBADF_)) - return MHD_ERR_BADF_; - /* sendfile() failed with EINVAL if mmap()-like operations are not - supported for FD or other 'unusual' errors occurred, so we should try - to fall back to 'SEND'; see also this thread for info on - odd libc/Linux behavior with sendfile: - http://lists.gnu.org/archive/html/libmicrohttpd/2011-02/msg00015.html */request->resp_sender = MHD_resp_sender_std; - return MHD_ERR_AGAIN_; -#else /* HAVE_SOLARIS_SENDFILE */ - if ( (EAFNOSUPPORT == err) || - (EINVAL == err) || - (EOPNOTSUPP == err) ) - { /* Retry with standard file reader. */ - request->resp_sender = MHD_resp_sender_std; - return MHD_ERR_AGAIN_; - } - if ( (ENOTCONN == err) || - (EPIPE == err) ) - { - return MHD_ERR_CONNRESET_; - } - return MHD_ERR_BADF_; /* Fail hard */ -#endif /* HAVE_SOLARIS_SENDFILE */ - } -#ifdef EPOLL_SUPPORT - else if (send_size > (size_t) ret) - connection->epoll_state &= ~MHD_EPOLL_STATE_WRITE_READY; -#endif /* EPOLL_SUPPORT */ -#elif defined(HAVE_FREEBSD_SENDFILE) -#ifdef SF_FLAGS - flags = used_thr_p_c ? - freebsd_sendfile_flags_thd_p_c_ : freebsd_sendfile_flags_; -#endif /* SF_FLAGS */ - if (0 != sendfile (file_fd, - connection->socket_fd, - (off_t) offsetu64, - send_size, - NULL, - &sent_bytes, - flags)) - { - const int err = MHD_socket_get_error_ (); - if (MHD_SCKT_ERR_IS_EAGAIN_ (err) || - MHD_SCKT_ERR_IS_EINTR_ (err) || - (EBUSY == err) ) - { - mhd_assert (SSIZE_MAX >= sent_bytes); - if (0 != sent_bytes) - return (ssize_t) sent_bytes; - - return MHD_ERR_AGAIN_; - } - /* Some unrecoverable error. Possibly file FD is not suitable - * for sendfile(). Retry with standard send(). */ - request->resp_sender = MHD_resp_sender_std; - return MHD_ERR_AGAIN_; - } - mhd_assert (0 < sent_bytes); - mhd_assert (SSIZE_MAX >= sent_bytes); - ret = (ssize_t) sent_bytes; -#elif defined(HAVE_DARWIN_SENDFILE) - len = (off_t) send_size; /* chunk always fit */ - if (0 != sendfile (file_fd, - connection->socket_fd, - (off_t) offsetu64, - &len, - NULL, - 0)) - { - const int err = MHD_socket_get_error_ (); - if (MHD_SCKT_ERR_IS_EAGAIN_ (err) || - MHD_SCKT_ERR_IS_EINTR_ (err)) - { - mhd_assert (0 <= len); - mhd_assert (SSIZE_MAX >= len); - mhd_assert (send_size >= (size_t) len); - if (0 != len) - return (ssize_t) len; - - return MHD_ERR_AGAIN_; - } - if ((ENOTCONN == err) || - (EPIPE == err) ) - return MHD_ERR_CONNRESET_; - if ((ENOTSUP == err) || - (EOPNOTSUPP == err) ) - { /* This file FD is not suitable for sendfile(). - * Retry with standard send(). */ - request->resp_sender = MHD_resp_sender_std; - return MHD_ERR_AGAIN_; - } - return MHD_ERR_BADF_; /* Return hard error. */ - } - mhd_assert (0 <= len); - mhd_assert (SSIZE_MAX >= len); - mhd_assert (send_size >= (size_t) len); - ret = (ssize_t) len; -#endif /* HAVE_FREEBSD_SENDFILE */ - return ret; -} - - -#endif /* _MHD_HAVE_SENDFILE */ - - -/** - * Check if we are done sending the write-buffer. If so, transition - * into "next_state". - * - * @param connection connection to check write status for - * @param next_state the next state to transition to - * @return false if we are not done, true if we are - */ -static bool -check_write_done (struct MHD_Request *request, - enum MHD_REQUEST_STATE next_state) -{ - if (request->write_buffer_append_offset != - request->write_buffer_send_offset) - return false; - request->write_buffer_append_offset = 0; - request->write_buffer_send_offset = 0; - request->state = next_state; - MHD_pool_reallocate (request->connection->pool, - request->write_buffer, - request->write_buffer_size, - 0); - request->write_buffer = NULL; - request->write_buffer_size = 0; - return true; -} - - -/** - * Prepare the response buffer of this request for sending. Assumes - * that the response mutex is already held. If the transmission is - * complete, this function may close the socket (and return false). - * - * @param request the request handle - * @return false if readying the response failed (the - * lock on the response will have been released already - * in this case). - */ -static bool -try_ready_normal_body (struct MHD_Request *request) -{ - struct MHD_Response *response = request->response; - struct MHD_Connection *connection = request->connection; - ssize_t ret; - - if (NULL == response->crc) - return true; - if ( (0 == response->total_size) || - (request->response_write_position == response->total_size) ) - return true; /* 0-byte response is always ready */ - if ( (response->data_start <= - request->response_write_position) && - (response->data_size + response->data_start > - request->response_write_position) ) - return true; /* response already ready */ -#if defined(_MHD_HAVE_SENDFILE) - if (MHD_resp_sender_sendfile == request->resp_sender) - { - /* will use sendfile, no need to bother response crc */ - return true; - } -#endif /* _MHD_HAVE_SENDFILE */ - - ret = response->crc (response->crc_cls, - request->response_write_position, - response->data, - (size_t) MHD_MIN ((uint64_t) response->data_buffer_size, - response->total_size - - request->response_write_position)); - if ( (((ssize_t) MHD_CONTENT_READER_END_OF_STREAM) == ret) || - (((ssize_t) MHD_CONTENT_READER_END_WITH_ERROR) == ret) ) - { - /* either error or http 1.0 transfer, close socket! */ - response->total_size = request->response_write_position; - MHD_mutex_unlock_chk_ (&response->mutex); - if ( ((ssize_t) MHD_CONTENT_READER_END_OF_STREAM) == ret) - MHD_connection_close_ (connection, - MHD_REQUEST_TERMINATED_COMPLETED_OK); - else - CONNECTION_CLOSE_ERROR (connection, - MHD_SC_APPLICATION_DATA_GENERATION_FAILURE_CLOSED, - _ ( - "Closing connection (application reported error generating data).\n")); - return false; - } - response->data_start = request->response_write_position; - response->data_size = ret; - if (0 == ret) - { - request->state = MHD_REQUEST_NORMAL_BODY_UNREADY; - MHD_mutex_unlock_chk_ (&response->mutex); - return false; - } - return true; -} - - -/** - * Prepare the response buffer of this request for sending. Assumes - * that the response mutex is already held. If the transmission is - * complete, this function may close the socket (and return false). - * - * @param connection the connection - * @return false if readying the response failed - */ -static bool -try_ready_chunked_body (struct MHD_Request *request) -{ - struct MHD_Connection *connection = request->connection; - struct MHD_Response *response = request->response; - struct MHD_Daemon *daemon = request->daemon; - ssize_t ret; - char *buf; - size_t size; - char cbuf[10]; /* 10: max strlen of "%x\r\n" */ - int cblen; - - if (NULL == response->crc) - return true; - if (0 == request->write_buffer_size) - { - size = MHD_MIN (daemon->connection_memory_limit_b, - 2 * (0xFFFFFF + sizeof(cbuf) + 2)); - do - { - size /= 2; - if (size < 128) - { - MHD_mutex_unlock_chk_ (&response->mutex); - /* not enough memory */ - CONNECTION_CLOSE_ERROR (connection, - MHD_SC_CONNECTION_POOL_MALLOC_FAILURE, - _ ("Closing connection (out of memory).\n")); - return false; - } - buf = MHD_pool_allocate (connection->pool, - size, - MHD_NO); - } - while (NULL == buf); - request->write_buffer_size = size; - request->write_buffer = buf; - } - - if (0 == response->total_size) - ret = 0; /* response must be empty, don't bother calling crc */ - else if ( (response->data_start <= - request->response_write_position) && - (response->data_start + response->data_size > - request->response_write_position) ) - { - /* difference between response_write_position and data_start is less - than data_size which is size_t type, no need to check for overflow */ - const size_t data_write_offset - = (size_t) (request->response_write_position - response->data_start); - /* buffer already ready, use what is there for the chunk */ - ret = response->data_size - data_write_offset; - if ( ((size_t) ret) > request->write_buffer_size - sizeof (cbuf) - 2) - ret = request->write_buffer_size - sizeof (cbuf) - 2; - memcpy (&request->write_buffer[sizeof (cbuf)], - &response->data[data_write_offset], - ret); - } - else - { - /* buffer not in range, try to fill it */ - ret = response->crc (response->crc_cls, - request->response_write_position, - &request->write_buffer[sizeof (cbuf)], - request->write_buffer_size - sizeof (cbuf) - 2); - } - if ( ((ssize_t) MHD_CONTENT_READER_END_WITH_ERROR) == ret) - { - /* error, close socket! */ - response->total_size = request->response_write_position; - MHD_mutex_unlock_chk_ (&response->mutex); - CONNECTION_CLOSE_ERROR (connection, - MHD_SC_APPLICATION_DATA_GENERATION_FAILURE_CLOSED, - _ ( - "Closing connection (application error generating response).\n")); - return false; - } - if ( (((ssize_t) MHD_CONTENT_READER_END_OF_STREAM) == ret) || - (0 == response->total_size) ) - { - /* end of message, signal other side! */ - memcpy (request->write_buffer, - "0\r\n", - 3); - request->write_buffer_append_offset = 3; - request->write_buffer_send_offset = 0; - response->total_size = request->response_write_position; - return true; - } - if (0 == ret) - { - request->state = MHD_REQUEST_CHUNKED_BODY_UNREADY; - MHD_mutex_unlock_chk_ (&response->mutex); - return false; - } - if (ret > 0xFFFFFF) - ret = 0xFFFFFF; - cblen = MHD_snprintf_ (cbuf, - sizeof (cbuf), - "%X\r\n", - (unsigned int) ret); - mhd_assert (cblen > 0); - mhd_assert ((size_t) cblen < sizeof(cbuf)); - memcpy (&request->write_buffer[sizeof (cbuf) - cblen], - cbuf, - cblen); - memcpy (&request->write_buffer[sizeof (cbuf) + ret], - "\r\n", - 2); - request->response_write_position += ret; - request->write_buffer_send_offset = sizeof (cbuf) - cblen; - request->write_buffer_append_offset = sizeof (cbuf) + ret + 2; - return true; -} - - -/** - * This function was created to handle writes to sockets when it has - * been determined that the socket can be written to. - * - * @param request the request to handle - */ -static void -MHD_request_handle_write_ (struct MHD_Request *request) -{ - struct MHD_Daemon *daemon = request->daemon; - struct MHD_Connection *connection = request->connection; - struct MHD_Response *response; - ssize_t ret; - - if (connection->suspended) - return; -#ifdef HTTPS_SUPPORT - { - struct MHD_TLS_Plugin *tls; - - if ( (NULL != (tls = daemon->tls_api)) && - (! tls->handshake (tls->cls, - connection->tls_cs)) ) - return; - } -#endif /* HTTPS_SUPPORT */ - -#if DEBUG_STATES - MHD_DLOG (daemon, - MHD_SC_STATE_MACHINE_STATUS_REPORT, - _ ("In function %s handling connection at state: %s\n"), - __FUNCTION__, - MHD_state_to_string (request->state)); -#endif - switch (request->state) - { - case MHD_REQUEST_INIT: - case MHD_REQUEST_URL_RECEIVED: - case MHD_REQUEST_HEADER_PART_RECEIVED: - case MHD_REQUEST_HEADERS_RECEIVED: - mhd_assert (0); - return; - case MHD_REQUEST_HEADERS_PROCESSED: - return; - case MHD_REQUEST_CONTINUE_SENDING: - ret = connection->send_cls (connection, - &HTTP_100_CONTINUE - [request->continue_message_write_offset], - MHD_STATICSTR_LEN_ (HTTP_100_CONTINUE) - - request->continue_message_write_offset); - if (ret < 0) - { - if (MHD_ERR_AGAIN_ == ret) - return; -#ifdef HAVE_MESSAGES - MHD_DLOG (daemon, - MHD_SC_CONNECTION_WRITE_FAIL_CLOSED, - _ ("Failed to send data in request for %s.\n"), - request->url); -#endif - CONNECTION_CLOSE_ERROR (connection, - MHD_SC_CONNECTION_WRITE_FAIL_CLOSED, - NULL); - return; - } - request->continue_message_write_offset += ret; - MHD_connection_update_last_activity_ (connection); - return; - case MHD_REQUEST_CONTINUE_SENT: - case MHD_REQUEST_BODY_RECEIVED: - case MHD_REQUEST_FOOTER_PART_RECEIVED: - case MHD_REQUEST_FOOTERS_RECEIVED: - mhd_assert (0); - return; - case MHD_REQUEST_HEADERS_SENDING: - ret = connection->send_cls (connection, - &request->write_buffer - [request->write_buffer_send_offset], - request->write_buffer_append_offset - - request->write_buffer_send_offset); - if (ret < 0) - { - if (MHD_ERR_AGAIN_ == ret) - return; - CONNECTION_CLOSE_ERROR (connection, - MHD_SC_CONNECTION_WRITE_FAIL_CLOSED, - _ ( - "Connection was closed while sending response headers.\n")); - return; - } - request->write_buffer_send_offset += ret; - MHD_connection_update_last_activity_ (connection); - if (MHD_REQUEST_HEADERS_SENDING != request->state) - return; - check_write_done (request, - MHD_REQUEST_HEADERS_SENT); - return; - case MHD_REQUEST_HEADERS_SENT: - return; - case MHD_REQUEST_NORMAL_BODY_READY: - response = request->response; - if (request->response_write_position < - request->response->total_size) - { - uint64_t data_write_offset; - - if (NULL != response->crc) - MHD_mutex_lock_chk_ (&response->mutex); - if (! try_ready_normal_body (request)) - { - /* mutex was already unlocked by try_ready_normal_body */ - return; - } -#if defined(_MHD_HAVE_SENDFILE) - if (MHD_resp_sender_sendfile == request->resp_sender) - { - ret = sendfile_adapter (connection); - } - else -#else /* ! _MHD_HAVE_SENDFILE */ - if (1) -#endif /* ! _MHD_HAVE_SENDFILE */ - { - data_write_offset = request->response_write_position - - response->data_start; - if (data_write_offset > (uint64_t) SIZE_MAX) - MHD_PANIC (_ ("Data offset exceeds limit.\n")); - ret = connection->send_cls (connection, - &response->data - [(size_t) data_write_offset], - response->data_size - - (size_t) data_write_offset); -#if DEBUG_SEND_DATA - if (ret > 0) - fprintf (stderr, - _ ("Sent %d-byte DATA response: `%.*s'\n"), - (int) ret, - (int) ret, - &response->data[request->response_write_position - - response->data_start]); -#endif - } - if (NULL != response->crc) - MHD_mutex_unlock_chk_ (&response->mutex); - if (ret < 0) - { - if (MHD_ERR_AGAIN_ == ret) - return; -#ifdef HAVE_MESSAGES - MHD_DLOG (daemon, - MHD_SC_CONNECTION_WRITE_FAIL_CLOSED, - _ ("Failed to send data in request for `%s'.\n"), - request->url); -#endif - CONNECTION_CLOSE_ERROR (connection, - MHD_SC_CONNECTION_WRITE_FAIL_CLOSED, - NULL); - return; - } - request->response_write_position += ret; - MHD_connection_update_last_activity_ (connection); - } - if (request->response_write_position == - request->response->total_size) - request->state = MHD_REQUEST_FOOTERS_SENT; /* have no footers */ - return; - case MHD_REQUEST_NORMAL_BODY_UNREADY: - mhd_assert (0); - return; - case MHD_REQUEST_CHUNKED_BODY_READY: - ret = connection->send_cls (connection, - &request->write_buffer - [request->write_buffer_send_offset], - request->write_buffer_append_offset - - request->write_buffer_send_offset); - if (ret < 0) - { - if (MHD_ERR_AGAIN_ == ret) - return; - CONNECTION_CLOSE_ERROR (connection, - MHD_SC_CONNECTION_WRITE_FAIL_CLOSED, - _ ( - "Connection was closed while sending response body.\n")); - return; - } - request->write_buffer_send_offset += ret; - MHD_connection_update_last_activity_ (connection); - if (MHD_REQUEST_CHUNKED_BODY_READY != request->state) - return; - check_write_done (request, - (request->response->total_size == - request->response_write_position) ? - MHD_REQUEST_BODY_SENT : - MHD_REQUEST_CHUNKED_BODY_UNREADY); - return; - case MHD_REQUEST_CHUNKED_BODY_UNREADY: - case MHD_REQUEST_BODY_SENT: - mhd_assert (0); - return; - case MHD_REQUEST_FOOTERS_SENDING: - ret = connection->send_cls (connection, - &request->write_buffer - [request->write_buffer_send_offset], - request->write_buffer_append_offset - - request->write_buffer_send_offset); - if (ret < 0) - { - if (MHD_ERR_AGAIN_ == ret) - return; - CONNECTION_CLOSE_ERROR (connection, - MHD_SC_CONNECTION_WRITE_FAIL_CLOSED, - _ ( - "Connection was closed while sending response body.\n")); - return; - } - request->write_buffer_send_offset += ret; - MHD_connection_update_last_activity_ (connection); - if (MHD_REQUEST_FOOTERS_SENDING != request->state) - return; - check_write_done (request, - MHD_REQUEST_FOOTERS_SENT); - return; - case MHD_REQUEST_FOOTERS_SENT: - mhd_assert (0); - return; - case MHD_REQUEST_CLOSED: - return; -#ifdef UPGRADE_SUPPORT - case MHD_REQUEST_UPGRADE: - mhd_assert (0); - return; -#endif /* UPGRADE_SUPPORT */ - default: - mhd_assert (0); - CONNECTION_CLOSE_ERROR (connection, - MHD_SC_STATEMACHINE_FAILURE_CONNECTION_CLOSED, - _ ("Internal error.\n")); - break; - } -} - - -/** - * Check whether request header contains particular token. - * - * Token could be surrounded by spaces and tabs and delimited by comma. - * Case-insensitive match used for header names and tokens. - * @param request the request to get values from - * @param header the header name - * @param token the token to find - * @param token_len the length of token, not including optional - * terminating null-character. - * @return true if token is found in specified header, - * false otherwise - */ -static bool -MHD_lookup_header_token_ci (const struct MHD_Request *request, - const char *header, - const char *token, - size_t token_len) -{ - struct MHD_HTTP_Header *pos; - - if ( (NULL == request) || /* FIXME: require non-null? */ - (NULL == header) || /* FIXME: require non-null? */ - (0 == header[0]) || - (NULL == token) || - (0 == token[0]) ) - return false; - for (pos = request->headers_received; NULL != pos; pos = pos->next) - { - if ( (0 != (pos->kind & MHD_HEADER_KIND)) && - ( (header == pos->header) || - (MHD_str_equal_caseless_ (header, - pos->header)) ) && - (MHD_str_has_token_caseless_ (pos->value, - token, - token_len)) ) - return true; - } - return false; -} - - -/** - * Check whether request header contains particular static @a tkn. - * - * Token could be surrounded by spaces and tabs and delimited by comma. - * Case-insensitive match used for header names and tokens. - * @param r the request to get values from - * @param h the header name - * @param tkn the static string of token to find - * @return true if token is found in specified header, - * false otherwise - */ -#define MHD_lookup_header_s_token_ci(r,h,tkn) \ - MHD_lookup_header_token_ci ((r),(h),(tkn),MHD_STATICSTR_LEN_ (tkn)) - - -/** - * Are we allowed to keep the given connection alive? We can use the - * TCP stream for a second request if the connection is HTTP 1.1 and - * the "Connection" header either does not exist or is not set to - * "close", or if the connection is HTTP 1.0 and the "Connection" - * header is explicitly set to "keep-alive". If no HTTP version is - * specified (or if it is not 1.0 or 1.1), we definitively close the - * connection. If the "Connection" header is not exactly "close" or - * "keep-alive", we proceed to use the default for the respective HTTP - * version (which is conservative for HTTP 1.0, but might be a bit - * optimistic for HTTP 1.1). - * - * @param request the request to check for keepalive - * @return #MHD_YES if (based on the request), a keepalive is - * legal - */ -static bool -keepalive_possible (struct MHD_Request *request) -{ - if (MHD_CONN_MUST_CLOSE == request->keepalive) - return false; - if (NULL == request->version_s) - return false; - if ( (NULL != request->response) && - (request->response->v10_only) ) - return false; - - if (MHD_str_equal_caseless_ (request->version_s, - MHD_HTTP_VERSION_1_1)) - { - if (MHD_lookup_header_s_token_ci (request, - MHD_HTTP_HEADER_CONNECTION, - "upgrade")) - return false; - if (MHD_lookup_header_s_token_ci (request, - MHD_HTTP_HEADER_CONNECTION, - "close")) - return false; - return true; - } - if (MHD_str_equal_caseless_ (request->version_s, - MHD_HTTP_VERSION_1_0)) - { - if (MHD_lookup_header_s_token_ci (request, - MHD_HTTP_HEADER_CONNECTION, - "Keep-Alive")) - return true; - return false; - } - return false; -} - - -/** - * Produce HTTP time stamp. - * - * @param date where to write the header, with - * at least 128 bytes available space. - * @param date_len number of bytes in @a date - */ -static void -get_date_string (char *date, - size_t date_len) -{ - static const char *const days[] = { - "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" - }; - static const char *const mons[] = { - "Jan", "Feb", "Mar", "Apr", "May", "Jun", - "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" - }; - struct tm now; - time_t t; -#if ! defined(HAVE_C11_GMTIME_S) && ! defined(HAVE_W32_GMTIME_S) && \ - ! defined(HAVE_GMTIME_R) - struct tm *pNow; -#endif - - date[0] = 0; - time (&t); -#if defined(HAVE_C11_GMTIME_S) - if (NULL == gmtime_s (&t, - &now)) - return; -#elif defined(HAVE_W32_GMTIME_S) - if (0 != gmtime_s (&now, - &t)) - return; -#elif defined(HAVE_GMTIME_R) - if (NULL == gmtime_r (&t, - &now)) - return; -#else - pNow = gmtime (&t); - if (NULL == pNow) - return; - now = *pNow; -#endif - MHD_snprintf_ (date, - date_len, - "Date: %3s, %02u %3s %04u %02u:%02u:%02u GMT\r\n", - days[now.tm_wday % 7], - (unsigned int) now.tm_mday, - mons[now.tm_mon % 12], - (unsigned int) (1900 + now.tm_year), - (unsigned int) now.tm_hour, - (unsigned int) now.tm_min, - (unsigned int) now.tm_sec); -} - - -/** - * Check whether response header contains particular @a token. - * - * Token could be surrounded by spaces and tabs and delimited by comma. - * Case-insensitive match used for header names and tokens. - * @param response the response to query - * @param key header name - * @param token the token to find - * @param token_len the length of token, not including optional - * terminating null-character. - * @return true if token is found in specified header, - * false otherwise - */ -static bool -check_response_header_token_ci (const struct MHD_Response *response, - const char *key, - const char *token, - size_t token_len) -{ - struct MHD_HTTP_Header *pos; - - if ( (NULL == key) || - ('\0' == key[0]) || - (NULL == token) || - ('\0' == token[0]) ) - return false; - - for (pos = response->first_header; - NULL != pos; - pos = pos->next) - { - if ( (pos->kind == MHD_HEADER_KIND) && - MHD_str_equal_caseless_ (pos->header, - key) && - MHD_str_has_token_caseless_ (pos->value, - token, - token_len) ) - return true; - } - return false; -} - - -/** - * Check whether response header contains particular static @a tkn. - * - * Token could be surrounded by spaces and tabs and delimited by comma. - * Case-insensitive match used for header names and tokens. - * @param r the response to query - * @param k header name - * @param tkn the static string of token to find - * @return true if token is found in specified header, - * false otherwise - */ -#define check_response_header_s_token_ci(r,k,tkn) \ - check_response_header_token_ci ((r),(k),(tkn),MHD_STATICSTR_LEN_ (tkn)) - - -/** - * Allocate the connection's write buffer and fill it with all of the - * headers (or footers, if we have already sent the body) from the - * HTTPd's response. If headers are missing in the response supplied - * by the application, additional headers may be added here. - * - * @param request the request for which to build the response header - * @return true on success, false on failure (out of memory) - */ -static bool -build_header_response (struct MHD_Request *request) -{ - struct MHD_Connection *connection = request->connection; - struct MHD_Daemon *daemon = request->daemon; - struct MHD_Response *response = request->response; - size_t size; - size_t off; - struct MHD_HTTP_Header *pos; - char code[256]; - char date[128]; - size_t datelen; - char content_length_buf[128]; - size_t content_length_len; - char *data; - enum MHD_ValueKind kind; - bool client_requested_close; - bool response_has_close; - bool response_has_keepalive; - const char *have_encoding; - const char *have_content_length; - bool must_add_close; - bool must_add_chunked_encoding; - bool must_add_keep_alive; - bool must_add_content_length; - - mhd_assert (NULL != request->version_s); - if (0 == request->version_s[0]) - { - data = MHD_pool_allocate (connection->pool, - 0, - MHD_YES); - request->write_buffer = data; - request->write_buffer_append_offset = 0; - request->write_buffer_send_offset = 0; - request->write_buffer_size = 0; - return true; - } - if (MHD_REQUEST_FOOTERS_RECEIVED == request->state) - { - const char *reason_phrase; - const char *version; - - reason_phrase - = MHD_get_reason_phrase_for (response->status_code); - version - = (response->icy) - ? "ICY" - : ( (MHD_str_equal_caseless_ (MHD_HTTP_VERSION_1_0, - request->version_s)) - ? MHD_HTTP_VERSION_1_0 - : MHD_HTTP_VERSION_1_1); - MHD_snprintf_ (code, - sizeof (code), - "%s %u %s\r\n", - version, - response->status_code, - reason_phrase); - off = strlen (code); - /* estimate size */ - size = off + 2; /* +2 for extra "\r\n" at the end */ - kind = MHD_HEADER_KIND; - if ( (! daemon->suppress_date) && - (NULL == MHD_response_get_header (response, - MHD_HTTP_HEADER_DATE)) ) - get_date_string (date, - sizeof (date)); - else - date[0] = '\0'; - datelen = strlen (date); - size += datelen; - } - else - { - /* 2 bytes for final CRLF of a Chunked-Body */ - size = 2; - kind = MHD_FOOTER_KIND; - off = 0; - datelen = 0; - } - - /* calculate extra headers we need to add, such as 'Connection: close', - first see what was explicitly requested by the application */ - must_add_close = false; - must_add_chunked_encoding = false; - must_add_keep_alive = false; - must_add_content_length = false; - response_has_close = false; - switch (request->state) - { - case MHD_REQUEST_FOOTERS_RECEIVED: - response_has_close - = check_response_header_s_token_ci (response, - MHD_HTTP_HEADER_CONNECTION, - "close"); - response_has_keepalive - = check_response_header_s_token_ci (response, - MHD_HTTP_HEADER_CONNECTION, - "Keep-Alive"); - client_requested_close - = MHD_lookup_header_s_token_ci (request, - MHD_HTTP_HEADER_CONNECTION, - "close"); - - if (response->v10_only) - request->keepalive = MHD_CONN_MUST_CLOSE; -#ifdef UPGRADE_SUPPORT - else if (NULL != response->upgrade_handler) - /* If this connection will not be "upgraded", it must be closed. */ - request->keepalive = MHD_CONN_MUST_CLOSE; -#endif /* UPGRADE_SUPPORT */ - - /* now analyze chunked encoding situation */ - request->have_chunked_upload = false; - - if ( (MHD_SIZE_UNKNOWN == response->total_size) && -#ifdef UPGRADE_SUPPORT - (NULL == response->upgrade_handler) && -#endif /* UPGRADE_SUPPORT */ - (! response_has_close) && - (! client_requested_close) ) - { - /* size is unknown, and close was not explicitly requested; - need to either to HTTP 1.1 chunked encoding or - close the connection */ - /* 'close' header doesn't exist yet, see if we need to add one; - if the client asked for a close, no need to start chunk'ing */ - if ( (keepalive_possible (request)) && - (MHD_str_equal_caseless_ (MHD_HTTP_VERSION_1_1, - request->version_s)) ) - { - have_encoding - = MHD_response_get_header (response, - MHD_HTTP_HEADER_TRANSFER_ENCODING); - if (NULL == have_encoding) - { - must_add_chunked_encoding = true; - request->have_chunked_upload = true; - } - else if (MHD_str_equal_caseless_ (have_encoding, - "identity")) - { - /* application forced identity encoding, can't do 'chunked' */ - must_add_close = true; - } - else - { - request->have_chunked_upload = true; - } - } - else - { - /* Keep alive or chunking not possible - => set close header if not present */ - if (! response_has_close) - must_add_close = true; - } - } - - /* check for other reasons to add 'close' header */ - if ( ( (client_requested_close) || - (connection->read_closed) || - (MHD_CONN_MUST_CLOSE == request->keepalive)) && - (! response_has_close) && -#ifdef UPGRADE_SUPPORT - (NULL == response->upgrade_handler) && -#endif /* UPGRADE_SUPPORT */ - (! response->v10_only) ) - must_add_close = true; - - /* check if we should add a 'content length' header */ - have_content_length - = MHD_response_get_header (response, - MHD_HTTP_HEADER_CONTENT_LENGTH); - - /* MHD_HTTP_NO_CONTENT, MHD_HTTP_NOT_MODIFIED and 1xx-status - codes SHOULD NOT have a Content-Length according to spec; - also chunked encoding / unknown length or CONNECT... */ - if ( (MHD_SIZE_UNKNOWN != response->total_size) && - (MHD_HTTP_NO_CONTENT != response->status_code) && - (MHD_HTTP_NOT_MODIFIED != response->status_code) && - (MHD_HTTP_OK <= response->status_code) && - (NULL == have_content_length) && - (request->method != MHD_METHOD_CONNECT) ) - { - /* - Here we add a content-length if one is missing; however, - for 'connect' methods, the responses MUST NOT include a - content-length header *if* the response code is 2xx (in - which case we expect there to be no body). Still, - as we don't know the response code here in some cases, we - simply only force adding a content-length header if this - is not a 'connect' or if the response is not empty - (which is kind of more sane, because if some crazy - application did return content with a 2xx status code, - then having a content-length might again be a good idea). - - Note that the change from 'SHOULD NOT' to 'MUST NOT' is - a recent development of the HTTP 1.1 specification. - */content_length_len - = MHD_snprintf_ (content_length_buf, - sizeof (content_length_buf), - MHD_HTTP_HEADER_CONTENT_LENGTH ": " - MHD_UNSIGNED_LONG_LONG_PRINTF "\r\n", - (MHD_UNSIGNED_LONG_LONG) response->total_size); - must_add_content_length = true; - } - - /* check for adding keep alive */ - if ( (! response_has_keepalive) && - (! response_has_close) && - (! must_add_close) && - (MHD_CONN_MUST_CLOSE != request->keepalive) && -#ifdef UPGRADE_SUPPORT - (NULL == response->upgrade_handler) && -#endif /* UPGRADE_SUPPORT */ - (keepalive_possible (request)) ) - must_add_keep_alive = true; - break; - case MHD_REQUEST_BODY_SENT: - response_has_keepalive = false; - break; - default: - mhd_assert (0); - return MHD_NO; - } - - if (MHD_CONN_MUST_CLOSE != request->keepalive) - { - if ( (must_add_close) || - (response_has_close) ) - request->keepalive = MHD_CONN_MUST_CLOSE; - else if ( (must_add_keep_alive) || - (response_has_keepalive) ) - request->keepalive = MHD_CONN_USE_KEEPALIVE; - } - - if (must_add_close) - size += MHD_STATICSTR_LEN_ ("Connection: close\r\n"); - if (must_add_keep_alive) - size += MHD_STATICSTR_LEN_ ("Connection: Keep-Alive\r\n"); - if (must_add_chunked_encoding) - size += MHD_STATICSTR_LEN_ ("Transfer-Encoding: chunked\r\n"); - if (must_add_content_length) - size += content_length_len; - mhd_assert (! (must_add_close && must_add_keep_alive) ); - mhd_assert (! (must_add_chunked_encoding && must_add_content_length) ); - - for (pos = response->first_header; NULL != pos; pos = pos->next) - { - /* TODO: add proper support for excluding "Keep-Alive" token. */ - if ( (pos->kind == kind) && - (! ( (must_add_close) && - (response_has_keepalive) && - (MHD_str_equal_caseless_ (pos->header, - MHD_HTTP_HEADER_CONNECTION)) && - (MHD_str_equal_caseless_ (pos->value, - "Keep-Alive")) ) ) ) - size += strlen (pos->header) + strlen (pos->value) + 4; /* colon, space, linefeeds */ - } - /* produce data */ - data = MHD_pool_allocate (connection->pool, - size + 1, - MHD_NO); - if (NULL == data) - { -#ifdef HAVE_MESSAGES - MHD_DLOG (daemon, - MHD_SC_CONNECTION_POOL_MALLOC_FAILURE, - "Not enough memory for write!\n"); -#endif - return false; - } - if (MHD_REQUEST_FOOTERS_RECEIVED == request->state) - { - memcpy (data, - code, - off); - } - if (must_add_close) - { - /* we must add the 'Connection: close' header */ - memcpy (&data[off], - "Connection: close\r\n", - MHD_STATICSTR_LEN_ ("Connection: close\r\n")); - off += MHD_STATICSTR_LEN_ ("Connection: close\r\n"); - } - if (must_add_keep_alive) - { - /* we must add the 'Connection: Keep-Alive' header */ - memcpy (&data[off], - "Connection: Keep-Alive\r\n", - MHD_STATICSTR_LEN_ ("Connection: Keep-Alive\r\n")); - off += MHD_STATICSTR_LEN_ ("Connection: Keep-Alive\r\n"); - } - if (must_add_chunked_encoding) - { - /* we must add the 'Transfer-Encoding: chunked' header */ - memcpy (&data[off], - "Transfer-Encoding: chunked\r\n", - MHD_STATICSTR_LEN_ ("Transfer-Encoding: chunked\r\n")); - off += MHD_STATICSTR_LEN_ ("Transfer-Encoding: chunked\r\n"); - } - if (must_add_content_length) - { - /* we must add the 'Content-Length' header */ - memcpy (&data[off], - content_length_buf, - content_length_len); - off += content_length_len; - } - for (pos = response->first_header; NULL != pos; pos = pos->next) - { - /* TODO: add proper support for excluding "Keep-Alive" token. */ - if ( (pos->kind == kind) && - (! ( (must_add_close) && - (response_has_keepalive) && - (MHD_str_equal_caseless_ (pos->header, - MHD_HTTP_HEADER_CONNECTION)) && - (MHD_str_equal_caseless_ (pos->value, - "Keep-Alive")) ) ) ) - off += MHD_snprintf_ (&data[off], - size - off, - "%s: %s\r\n", - pos->header, - pos->value); - } - if (MHD_REQUEST_FOOTERS_RECEIVED == request->state) - { - memcpy (&data[off], - date, - datelen); - off += datelen; - } - memcpy (&data[off], - "\r\n", - 2); - off += 2; - - if (off != size) - mhd_panic (mhd_panic_cls, - __FILE__, - __LINE__, - NULL); - request->write_buffer = data; - request->write_buffer_append_offset = size; - request->write_buffer_send_offset = 0; - request->write_buffer_size = size + 1; - return true; -} - - -/** - * We encountered an error processing the request. Handle it properly - * by stopping to read data and sending the indicated response code - * and message. - * - * @param request the request - * @param ec error code for MHD - * @param status_code the response code to send (400, 413 or 414) - * @param message the error message to send - */ -static void -transmit_error_response (struct MHD_Request *request, - enum MHD_StatusCode ec, - enum MHD_HTTP_StatusCode status_code, - const char *message) -{ - struct MHD_Response *response; - - if (NULL == request->version_s) - { - /* we were unable to process the full header line, so we don't - really know what version the client speaks; assume 1.0 */ - request->version_s = MHD_HTTP_VERSION_1_0; - } - request->state = MHD_REQUEST_FOOTERS_RECEIVED; - request->connection->read_closed = true; -#ifdef HAVE_MESSAGES - MHD_DLOG (request->daemon, - ec, - _ ( - "Error processing request (HTTP response code is %u (`%s')). Closing connection.\n"), - status_code, - message); -#endif - if (NULL != request->response) - { - MHD_response_queue_for_destroy (request->response); - request->response = NULL; - } - response = MHD_response_from_buffer (status_code, - strlen (message), - (void *) message, - MHD_RESPMEM_PERSISTENT); - request->response = response; - /* Do not reuse this connection. */ - request->keepalive = MHD_CONN_MUST_CLOSE; - if (! build_header_response (request)) - { - /* oops - close! */ - CONNECTION_CLOSE_ERROR (request->connection, - ec, - _ ( - "Closing connection (failed to create response header).\n")); - } - else - { - request->state = MHD_REQUEST_HEADERS_SENDING; - } -} - - -/** - * Convert @a method to the respective enum value. - * - * @param method the method string to look up enum value for - * @return resulting enum, or generic value for "unknown" - */ -static enum MHD_Method -method_string_to_enum (const char *method) -{ - static const struct - { - const char *key; - enum MHD_Method value; - } methods[] = { - { "OPTIONS", MHD_METHOD_OPTIONS }, - { "GET", MHD_METHOD_GET }, - { "HEAD", MHD_METHOD_HEAD }, - { "POST", MHD_METHOD_POST }, - { "PUT", MHD_METHOD_PUT }, - { "DELETE", MHD_METHOD_DELETE }, - { "TRACE", MHD_METHOD_TRACE }, - { "CONNECT", MHD_METHOD_CONNECT }, - { "ACL", MHD_METHOD_ACL }, - { "BASELINE_CONTROL", MHD_METHOD_BASELINE_CONTROL }, - { "BIND", MHD_METHOD_BIND }, - { "CHECKIN", MHD_METHOD_CHECKIN }, - { "CHECKOUT", MHD_METHOD_CHECKOUT }, - { "COPY", MHD_METHOD_COPY }, - { "LABEL", MHD_METHOD_LABEL }, - { "LINK", MHD_METHOD_LINK }, - { "LOCK", MHD_METHOD_LOCK }, - { "MERGE", MHD_METHOD_MERGE }, - { "MKACTIVITY", MHD_METHOD_MKACTIVITY }, - { "MKCOL", MHD_METHOD_MKCOL }, - { "MKREDIRECTREF", MHD_METHOD_MKREDIRECTREF }, - { "MKWORKSPACE", MHD_METHOD_MKWORKSPACE }, - { "MOVE", MHD_METHOD_MOVE }, - { "ORDERPATCH", MHD_METHOD_ORDERPATCH }, - { "PRI", MHD_METHOD_PRI }, - { "PROPFIND", MHD_METHOD_PROPFIND }, - { "PROPPATCH", MHD_METHOD_PROPPATCH }, - { "REBIND", MHD_METHOD_REBIND }, - { "REPORT", MHD_METHOD_REPORT }, - { "SEARCH", MHD_METHOD_SEARCH }, - { "UNBIND", MHD_METHOD_UNBIND }, - { "UNCHECKOUT", MHD_METHOD_UNCHECKOUT }, - { "UNLINK", MHD_METHOD_UNLINK }, - { "UNLOCK", MHD_METHOD_UNLOCK }, - { "UPDATE", MHD_METHOD_UPDATE }, - { "UPDATEDIRECTREF", MHD_METHOD_UPDATEDIRECTREF }, - { "VERSION-CONTROL", MHD_METHOD_VERSION_CONTROL }, - { NULL, MHD_METHOD_UNKNOWN } /* must be last! */ - }; - unsigned int i; - - for (i = 0; NULL != methods[i].key; i++) - if (0 == - MHD_str_equal_caseless_ (method, - methods[i].key)) - return methods[i].value; - return MHD_METHOD_UNKNOWN; -} - - -/** - * Add an entry to the HTTP headers of a request. If this fails, - * transmit an error response (request too big). - * - * @param request the request 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 false on failure (out of memory), true for success - */ -static bool -request_add_header (struct MHD_Request *request, - const char *key, - const char *value, - enum MHD_ValueKind kind) -{ - if (MHD_NO == - MHD_request_set_value (request, - kind, - key, - value)) - { -#ifdef HAVE_MESSAGES - MHD_DLOG (request->daemon, - MHD_SC_CONNECTION_POOL_MALLOC_FAILURE, - _ ("Not enough memory in pool to allocate header record!\n")); -#endif - transmit_error_response (request, - MHD_SC_CLIENT_HEADER_TOO_BIG, - MHD_HTTP_REQUEST_HEADER_FIELDS_TOO_LARGE, - REQUEST_TOO_BIG); - return false; - } - return true; -} - - -/** - * Parse the first line of the HTTP HEADER. - * - * @param connection the connection (updated) - * @param line the first line, not 0-terminated - * @param line_len length of the first @a line - * @return true if the line is ok, false if it is malformed - */ -static bool -parse_initial_message_line (struct MHD_Request *request, - char *line, - size_t line_len) -{ - struct MHD_Daemon *daemon = request->daemon; - const char *curi; - char *uri; - char *http_version; - char *args; - unsigned int unused_num_headers; - size_t url_end; - - if (NULL == (uri = memchr (line, - ' ', - line_len))) - return false; /* serious error */ - uri[0] = '\0'; - request->method_s = line; - request->method = method_string_to_enum (line); - uri++; - /* Skip any spaces. Not required by standard but allow - to be more tolerant. */ - while ( (' ' == uri[0]) && - ( (size_t) (uri - line) < line_len) ) - uri++; - if ((size_t) (uri - line) == line_len) - { - curi = ""; - uri = NULL; - request->version_s = ""; - args = NULL; - url_end = line_len - (line - uri); // EH, this is garbage. FIXME! - } - else - { - curi = uri; - /* Search from back to accept malformed URI with space */ - http_version = line + line_len - 1; - /* Skip any trailing spaces */ - while ( (' ' == http_version[0]) && - (http_version > uri) ) - http_version--; - /* Find first space in reverse direction */ - while ( (' ' != http_version[0]) && - (http_version > uri) ) - http_version--; - if (http_version > uri) - { - http_version[0] = '\0'; - request->version_s = http_version + 1; - args = memchr (uri, - '?', - http_version - uri); - } - else - { - request->version_s = ""; - args = memchr (uri, - '?', - line_len - (uri - line)); - } - url_end = http_version - uri; - } - if ( (MHD_PSL_STRICT == daemon->protocol_strict_level) && - (NULL != memchr (curi, - ' ', - url_end)) ) - { - /* space exists in URI and we are supposed to be strict, reject */ - return MHD_NO; - } - if (NULL != daemon->early_uri_logger_cb) - { - request->client_context - = daemon->early_uri_logger_cb (daemon->early_uri_logger_cb_cls, - curi, - request); - } - if (NULL != args) - { - args[0] = '\0'; - args++; - /* note that this call clobbers 'args' */ - MHD_parse_arguments_ (request, - MHD_GET_ARGUMENT_KIND, - args, - &request_add_header, - &unused_num_headers); - } - if (NULL != uri) - daemon->unescape_cb (daemon->unescape_cb_cls, - request, - uri); - request->url = curi; - return true; -} - - -/** - * We have received (possibly the beginning of) a line in the - * header (or footer). Validate (check for ":") and prepare - * to process. - * - * @param request the request we're processing - * @param line line from the header to process - * @return true on success, false on error (malformed @a line) - */ -static bool -process_header_line (struct MHD_Request *request, - char *line) -{ - struct MHD_Connection *connection = request->connection; - char *colon; - - /* line should be normal header line, find colon */ - colon = strchr (line, - ':'); - if (NULL == colon) - { - /* error in header line, die hard */ - CONNECTION_CLOSE_ERROR (connection, - MHD_SC_CONNECTION_PARSE_FAIL_CLOSED, - _ ( - "Received malformed line (no colon). Closing connection.\n")); - return false; - } - if (MHD_PSL_PERMISSIVE != request->daemon->protocol_strict_level) - { - /* check for whitespace before colon, which is not allowed - by RFC 7230 section 3.2.4; we count space ' ' and - tab '\t', but not '\r\n' as those would have ended the line. */ - const char *white; - - white = strchr (line, - (unsigned char) ' '); - if ( (NULL != white) && - (white < colon) ) - { - CONNECTION_CLOSE_ERROR (connection, - MHD_SC_CONNECTION_PARSE_FAIL_CLOSED, - _ ( - "Whitespace before colon forbidden by RFC 7230. Closing connection.\n")); - return false; - } - white = strchr (line, - (unsigned char) '\t'); - if ( (NULL != white) && - (white < colon) ) - { - CONNECTION_CLOSE_ERROR (connection, - MHD_SC_CONNECTION_PARSE_FAIL_CLOSED, - _ ( - "Tab before colon forbidden by RFC 7230. Closing connection.\n")); - return false; - } - } - /* zero-terminate header */ - colon[0] = '\0'; - colon++; /* advance to value */ - while ( ('\0' != colon[0]) && - ( (' ' == colon[0]) || - ('\t' == colon[0]) ) ) - colon++; - /* we do the actual adding of the connection - header at the beginning of the while - loop since we need to be able to inspect - the *next* header line (in case it starts - with a space...) */request->last = line; - request->colon = colon; - return true; -} - - -/** - * Process a header value that spans multiple lines. - * The previous line(s) are in connection->last. - * - * @param request the request we're processing - * @param line the current input line - * @param kind if the line is complete, add a header - * of the given kind - * @return true if the line was processed successfully - */ -static bool -process_broken_line (struct MHD_Request *request, - char *line, - enum MHD_ValueKind kind) -{ - struct MHD_Connection *connection = request->connection; - char *last; - char *tmp; - size_t last_len; - size_t tmp_len; - - last = request->last; - if ( (' ' == line[0]) || - ('\t' == line[0]) ) - { - /* value was continued on the next line, see - http://www.jmarshall.com/easy/http/ */ - last_len = strlen (last); - /* skip whitespace at start of 2nd line */ - tmp = line; - while ( (' ' == tmp[0]) || - ('\t' == tmp[0]) ) - tmp++; - tmp_len = strlen (tmp); - /* FIXME: we might be able to do this better (faster!), as most - likely 'last' and 'line' should already be adjacent in - memory; however, doing this right gets tricky if we have a - value continued over multiple lines (in which case we need to - record how often we have done this so we can check for - adjacency); also, in the case where these are not adjacent - (not sure how it can happen!), we would want to allocate from - the end of the pool, so as to not destroy the read-buffer's - ability to grow nicely. */last = MHD_pool_reallocate (connection->pool, - last, - last_len + 1, - last_len + tmp_len + 1); - if (NULL == last) - { - transmit_error_response (request, - MHD_SC_CLIENT_HEADER_TOO_BIG, - MHD_HTTP_REQUEST_HEADER_FIELDS_TOO_LARGE, - REQUEST_TOO_BIG); - return MHD_NO; - } - memcpy (&last[last_len], - tmp, - tmp_len + 1); - request->last = last; - return MHD_YES; /* possibly more than 2 lines... */ - } - mhd_assert ( (NULL != last) && - (NULL != request->colon) ); - if (! request_add_header (request, - last, - request->colon, - kind)) - { - transmit_error_response (request, - MHD_SC_CLIENT_HEADER_TOO_BIG, - MHD_HTTP_REQUEST_HEADER_FIELDS_TOO_LARGE, - REQUEST_TOO_BIG); - return false; - } - /* we still have the current line to deal with... */ - if ('\0' != line[0]) - { - if (! process_header_line (request, - line)) - { - transmit_error_response (request, - MHD_SC_CONNECTION_PARSE_FAIL_CLOSED, - MHD_HTTP_BAD_REQUEST, - REQUEST_MALFORMED); - return false; - } - } - return true; -} - - -/** - * Parse a single line of the HTTP header. Advance read_buffer (!) - * appropriately. If the current line does not fit, consider growing - * the buffer. If the line is far too long, close the connection. If - * no line is found (incomplete, buffer too small, line too long), - * return NULL. Otherwise return a pointer to the line. - * - * @param request request we're processing - * @param[out] line_len pointer to variable that receive - * length of line or NULL - * @return NULL if no full line is available; note that the returned - * string will not be 0-termianted - */ -static char * -get_next_header_line (struct MHD_Request *request, - size_t *line_len) -{ - char *rbuf; - size_t pos; - - if (0 == request->read_buffer_offset) - return NULL; - pos = 0; - rbuf = request->read_buffer; - while ( (pos < request->read_buffer_offset - 1) && - ('\r' != rbuf[pos]) && - ('\n' != rbuf[pos]) ) - pos++; - if ( (pos == request->read_buffer_offset - 1) && - ('\n' != rbuf[pos]) ) - { - /* not found, consider growing... */ - if ( (request->read_buffer_offset == request->read_buffer_size) && - (! try_grow_read_buffer (request)) ) - { - transmit_error_response (request, - MHD_SC_CLIENT_HEADER_TOO_BIG, - (NULL != request->url) - ? MHD_HTTP_REQUEST_HEADER_FIELDS_TOO_LARGE - : MHD_HTTP_URI_TOO_LONG, - REQUEST_TOO_BIG); - } - if (line_len) - *line_len = 0; - return NULL; - } - - if (line_len) - *line_len = pos; - /* found, check if we have proper LFCR */ - if ( ('\r' == rbuf[pos]) && - ('\n' == rbuf[pos + 1]) ) - rbuf[pos++] = '\0'; /* skip both r and n */ - rbuf[pos++] = '\0'; - request->read_buffer += pos; - request->read_buffer_size -= pos; - request->read_buffer_offset -= pos; - return rbuf; -} - - -/** - * Check whether is possible to force push socket buffer content as - * partial packet. - * MHD use different buffering logic depending on whether flushing of - * socket buffer is possible or not. - * If flushing IS possible than MHD activates extra buffering before - * sending data to prevent sending partial packets and flush pending - * data in socket buffer to push last partial packet to client after - * sending logical completed part of data (for example: after sending - * full response header or full response message). - * If flushing IS NOT possible than MHD activates no buffering (no - * delay sending) when it going to send formed fully completed logical - * part of data and activate normal buffering after sending. - * For idled keep-alive connection MHD always activate normal - * buffering. - * - * @param connection connection to check - * @return true if force push is possible, false otherwise - */ -static bool -socket_flush_possible (struct MHD_Connection *connection) -{ - (void) connection; /* Mute compiler warning. */ -#if defined(TCP_CORK) || defined(TCP_PUSH) - return true; -#else /* !TCP_CORK && !TCP_PUSH */ - return false; -#endif /* !TCP_CORK && !TCP_PUSH */ -} - - -/** - * Activate extra buffering mode on connection socket to prevent - * sending of partial packets. - * - * @param connection connection to be processed - * @return true on success, false otherwise - */ -static bool -socket_start_extra_buffering (struct MHD_Connection *connection) -{ - bool res = false; - (void) connection; /* Mute compiler warning. */ -#if defined(TCP_CORK) || defined(TCP_NOPUSH) - const MHD_SCKT_OPT_BOOL_ on_val = 1; -#if defined(TCP_NODELAY) - const MHD_SCKT_OPT_BOOL_ off_val = 0; -#endif /* TCP_NODELAY */ - mhd_assert (NULL != connection); -#if defined(TCP_NOPUSH) && ! defined(TCP_CORK) - /* Buffer data before sending */ - res = (0 == setsockopt (connection->socket_fd, - IPPROTO_TCP, - TCP_NOPUSH, - (const void *) &on_val, - sizeof (on_val))) - ? true : false; -#if defined(TCP_NODELAY) - /* Enable Nagle's algorithm */ - /* TCP_NODELAY may interfere with TCP_NOPUSH */ - res &= (0 == setsockopt (connection->socket_fd, - IPPROTO_TCP, - TCP_NODELAY, - (const void *) &off_val, - sizeof (off_val))) - ? true : false; -#endif /* TCP_NODELAY */ -#else /* TCP_CORK */ -#if defined(TCP_NODELAY) - /* Enable Nagle's algorithm */ - /* TCP_NODELAY may prevent enabling TCP_CORK. Resulting buffering mode depends - solely on TCP_CORK result, so ignoring return code here. */ - (void) setsockopt (connection->socket_fd, - IPPROTO_TCP, - TCP_NODELAY, - (const void *) &off_val, - sizeof (off_val)); -#endif /* TCP_NODELAY */ - /* Send only full packets */ - res = (0 == setsockopt (connection->socket_fd, - IPPROTO_TCP, - TCP_CORK, - (const void *) &on_val, - sizeof (on_val))) - ? true : false; -#endif /* TCP_CORK */ -#endif /* TCP_CORK || TCP_NOPUSH */ - return res; -} - - -/** - * Activate no buffering mode (no delay sending) on connection socket. - * - * @param connection connection to be processed - * @return true on success, false otherwise - */ -static bool -socket_start_no_buffering (struct MHD_Connection *connection) -{ -#if defined(TCP_NODELAY) - bool res = true; - const MHD_SCKT_OPT_BOOL_ on_val = 1; -#if defined(TCP_CORK) || defined(TCP_NOPUSH) - const MHD_SCKT_OPT_BOOL_ off_val = 0; -#endif /* TCP_CORK || TCP_NOPUSH */ - - (void) connection; /* Mute compiler warning. */ - mhd_assert (NULL != connection); -#if defined(TCP_CORK) - /* Allow partial packets */ - res &= (0 == setsockopt (connection->socket_fd, - IPPROTO_TCP, - TCP_CORK, - (const void *) &off_val, - sizeof (off_val))) - ? true : false; -#endif /* TCP_CORK */ -#if defined(TCP_NODELAY) - /* Disable Nagle's algorithm for sending packets without delay */ - res &= (0 == setsockopt (connection->socket_fd, - IPPROTO_TCP, - TCP_NODELAY, - (const void *) &on_val, - sizeof (on_val))) - ? true : false; -#endif /* TCP_NODELAY */ -#if defined(TCP_NOPUSH) && ! defined(TCP_CORK) - /* Disable extra buffering */ - res &= (0 == setsockopt (connection->socket_fd, - IPPROTO_TCP, - TCP_NOPUSH, - (const void *) &off_val, - sizeof (off_val))) - ? true : false; -#endif /* TCP_NOPUSH && !TCP_CORK */ - return res; -#else /* !TCP_NODELAY */ - return false; -#endif /* !TCP_NODELAY */ -} - - -/** - * Activate no buffering mode (no delay sending) on connection socket - * and push to client data pending in socket buffer. - * - * @param connection connection to be processed - * @return true on success, false otherwise - */ -static bool -socket_start_no_buffering_flush (struct MHD_Connection *connection) -{ - bool res = true; -#if defined(TCP_NOPUSH) && ! defined(TCP_CORK) - const int dummy = 0; -#endif /* !TCP_CORK */ - - if (NULL == connection) - return false; /* FIXME: use MHD_NONNULL? */ - res = socket_start_no_buffering (connection); -#if defined(TCP_NOPUSH) && ! defined(TCP_CORK) - /* Force flush data with zero send otherwise Darwin and some BSD systems - will add 5 seconds delay. Not required with TCP_CORK as switching off - TCP_CORK always flushes socket buffer. */ - res &= (0 <= MHD_send_ (connection->socket_fd, - &dummy, - 0)) - ? true : false; -#endif /* TCP_NOPUSH && !TCP_CORK*/ - return res; -} - - -/** - * Activate normal buffering mode on connection socket. - * - * @param connection connection to be processed - * @return true on success, false otherwise - */ -static bool -socket_start_normal_buffering (struct MHD_Connection *connection) -{ -#if defined(TCP_NODELAY) - bool res = true; - const MHD_SCKT_OPT_BOOL_ off_val = 0; -#if defined(TCP_CORK) - MHD_SCKT_OPT_BOOL_ cork_val = 0; - socklen_t param_size = sizeof (cork_val); -#endif /* TCP_CORK */ - - mhd_assert (NULL != connection); -#if defined(TCP_CORK) - /* Allow partial packets */ - /* Disabling TCP_CORK will flush partial packet even if TCP_CORK wasn't enabled before - so try to check current value of TCP_CORK to prevent unrequested flushing */ - if ( (0 != getsockopt (connection->socket_fd, - IPPROTO_TCP, - TCP_CORK, - (void *) &cork_val, - &param_size)) || - (0 != cork_val)) - res &= (0 == setsockopt (connection->socket_fd, - IPPROTO_TCP, - TCP_CORK, - (const void *) &off_val, - sizeof (off_val))) - ? true : false; -#elif defined(TCP_NOPUSH) - /* Disable extra buffering */ - /* No need to check current value as disabling TCP_NOPUSH will not flush partial - packet if TCP_NOPUSH wasn't enabled before */ - res &= (0 == setsockopt (connection->socket_fd, - IPPROTO_TCP, - TCP_NOPUSH, - (const void *) &off_val, - sizeof (off_val))) - ? true : false; -#endif /* TCP_NOPUSH && !TCP_CORK */ - /* Enable Nagle's algorithm for normal buffering */ - res &= (0 == setsockopt (connection->socket_fd, - IPPROTO_TCP, - TCP_NODELAY, - (const void *) &off_val, - sizeof (off_val))) - ? true : false; - return res; -#else /* !TCP_NODELAY */ - return false; -#endif /* !TCP_NODELAY */ -} - - -/** - * Do we (still) need to send a 100 continue - * message for this request? - * - * @param request the request to test - * @return false if we don't need 100 CONTINUE, true if we do - */ -static bool -need_100_continue (struct MHD_Request *request) -{ - const char *expect; - - return ( (NULL == request->response) && - (NULL != request->version_s) && - (MHD_str_equal_caseless_ (request->version_s, - MHD_HTTP_VERSION_1_1)) && - (NULL != (expect = MHD_request_lookup_value (request, - MHD_HEADER_KIND, - MHD_HTTP_HEADER_EXPECT))) - && - (MHD_str_equal_caseless_ (expect, - "100-continue")) && - (request->continue_message_write_offset < - MHD_STATICSTR_LEN_ (HTTP_100_CONTINUE)) ); -} - - -/** - * Parse the cookie header (see RFC 2109). - * - * @param request request to parse header of - * @return true for success, false for failure (malformed, out of memory) - */ -static int -parse_cookie_header (struct MHD_Request *request) -{ - const char *hdr; - char *cpy; - char *pos; - char *sce; - char *semicolon; - char *equals; - char *ekill; - char old; - int quotes; - - hdr = MHD_request_lookup_value (request, - MHD_HEADER_KIND, - MHD_HTTP_HEADER_COOKIE); - if (NULL == hdr) - return true; - cpy = MHD_pool_allocate (request->connection->pool, - strlen (hdr) + 1, - MHD_YES); - if (NULL == cpy) - { -#ifdef HAVE_MESSAGES - MHD_DLOG (request->daemon, - MHD_SC_COOKIE_POOL_ALLOCATION_FAILURE, - _ ("Not enough memory in pool to parse cookies!\n")); -#endif - transmit_error_response (request, - MHD_SC_COOKIE_POOL_ALLOCATION_FAILURE, - MHD_HTTP_REQUEST_HEADER_FIELDS_TOO_LARGE, - REQUEST_TOO_BIG); - return false; - } - memcpy (cpy, - hdr, - strlen (hdr) + 1); - pos = cpy; - while (NULL != pos) - { - while (' ' == *pos) - pos++; /* skip spaces */ - - sce = pos; - while ( ((*sce) != '\0') && - ((*sce) != ',') && - ((*sce) != ';') && - ((*sce) != '=') ) - sce++; - /* remove tailing whitespace (if any) from key */ - ekill = sce - 1; - while ( (*ekill == ' ') && - (ekill >= pos) ) - *(ekill--) = '\0'; - old = *sce; - *sce = '\0'; - if (old != '=') - { - /* value part omitted, use empty string... */ - if (! request_add_header (request, - pos, - "", - MHD_COOKIE_KIND)) - return false; - if (old == '\0') - break; - pos = sce + 1; - continue; - } - equals = sce + 1; - quotes = 0; - semicolon = equals; - while ( ('\0' != semicolon[0]) && - ( (0 != quotes) || - ( (';' != semicolon[0]) && - (',' != semicolon[0]) ) ) ) - { - if ('"' == semicolon[0]) - quotes = (quotes + 1) & 1; - semicolon++; - } - if ('\0' == semicolon[0]) - semicolon = NULL; - if (NULL != semicolon) - { - semicolon[0] = '\0'; - semicolon++; - } - /* remove quotes */ - if ( ('"' == equals[0]) && - ('"' == equals[strlen (equals) - 1]) ) - { - equals[strlen (equals) - 1] = '\0'; - equals++; - } - if (! request_add_header (request, - pos, - equals, - MHD_COOKIE_KIND)) - return false; - pos = semicolon; - } - return true; -} - - -/** - * Parse the various headers; figure out the size - * of the upload and make sure the headers follow - * the protocol. Advance to the appropriate state. - * - * @param request request we're processing - */ -static void -parse_request_headers (struct MHD_Request *request) -{ - struct MHD_Daemon *daemon = request->daemon; - struct MHD_Connection *connection = request->connection; - const char *clen; - struct MHD_Response *response; - const char *enc; - const char *end; - - parse_cookie_header (request); /* FIXME: return value ignored! */ - if ( (MHD_PSL_STRICT == daemon->protocol_strict_level) && - (NULL != request->version_s) && - (MHD_str_equal_caseless_ (MHD_HTTP_VERSION_1_1, - request->version_s)) && - (NULL == - MHD_request_lookup_value (request, - MHD_HEADER_KIND, - MHD_HTTP_HEADER_HOST)) ) - { - /* die, http 1.1 request without host and we are pedantic */ - request->state = MHD_REQUEST_FOOTERS_RECEIVED; - connection->read_closed = true; -#ifdef HAVE_MESSAGES - MHD_DLOG (daemon, - MHD_SC_HOST_HEADER_MISSING, - _ ("Received HTTP 1.1 request without `Host' header.\n")); -#endif - mhd_assert (NULL == request->response); - response = - MHD_response_from_buffer (MHD_HTTP_BAD_REQUEST, - MHD_STATICSTR_LEN_ (REQUEST_LACKS_HOST), - REQUEST_LACKS_HOST, - MHD_RESPMEM_PERSISTENT); - request->response = response; - // FIXME: state machine advance? - return; - } - - request->remaining_upload_size = 0; - enc = MHD_request_lookup_value (request, - MHD_HEADER_KIND, - MHD_HTTP_HEADER_TRANSFER_ENCODING); - if (NULL != enc) - { - request->remaining_upload_size = MHD_SIZE_UNKNOWN; - if (MHD_str_equal_caseless_ (enc, - "chunked")) - request->have_chunked_upload = true; - return; - } - clen = MHD_request_lookup_value (request, - MHD_HEADER_KIND, - MHD_HTTP_HEADER_CONTENT_LENGTH); - if (NULL == clen) - return; - end = clen + MHD_str_to_uint64_ (clen, - &request->remaining_upload_size); - if ( (clen == end) || - ('\0' != *end) ) - { - request->remaining_upload_size = 0; -#ifdef HAVE_MESSAGES - MHD_DLOG (request->daemon, - MHD_SC_CONTENT_LENGTH_MALFORMED, - "Failed to parse `Content-Length' header. Closing connection.\n"); -#endif - CONNECTION_CLOSE_ERROR (connection, - MHD_SC_CONTENT_LENGTH_MALFORMED, - NULL); - } -} - - -/** - * Call the handler of the application for this - * request. - * - * @param request request we're processing - */ -static void -call_request_handler (struct MHD_Request *request) -{ - struct MHD_Daemon *daemon = request->daemon; - struct MHD_Connection *connection = request->connection; - const struct MHD_Action *action; - - if (NULL != request->response) - return; /* already queued a response */ - if (NULL == (action = - daemon->rc (daemon->rc_cls, - request, - request->url, - request->method))) - { - /* serious internal error, close connection */ - CONNECTION_CLOSE_ERROR (connection, - MHD_SC_APPLICATION_CALLBACK_FAILURE_CLOSED, - _ ( - "Application reported internal error, closing connection.\n")); - return; - } - action->action (action->action_cls, - request); -} - - -/** - * Call the handler of the application for this request. Handles - * chunking of the upload as well as normal uploads. - * - * @param request request we're processing - */ -static void -process_request_body (struct MHD_Request *request) -{ - struct MHD_Daemon *daemon = request->daemon; - struct MHD_Connection *connection = request->connection; - size_t available; - bool instant_retry; - char *buffer_head; - - if (NULL != request->response) - return; /* already queued a response */ - - buffer_head = request->read_buffer; - available = request->read_buffer_offset; - do - { - size_t to_be_processed; - size_t left_unprocessed; - size_t processed_size; - - instant_retry = false; - if ( (request->have_chunked_upload) && - (MHD_SIZE_UNKNOWN == request->remaining_upload_size) ) - { - if ( (request->current_chunk_offset == request->current_chunk_size) && - (0LLU != request->current_chunk_offset) && - (available >= 2) ) - { - size_t i; - - /* skip new line at the *end* of a chunk */ - i = 0; - if ( ('\r' == buffer_head[i]) || - ('\n' == buffer_head[i]) ) - i++; /* skip 1st part of line feed */ - if ( ('\r' == buffer_head[i]) || - ('\n' == buffer_head[i]) ) - i++; /* skip 2nd part of line feed */ - if (0 == i) - { - /* malformed encoding */ - CONNECTION_CLOSE_ERROR (connection, - MHD_SC_CHUNKED_ENCODING_MALFORMED, - _ ( - "Received malformed HTTP request (bad chunked encoding). Closing connection.\n")); - return; - } - available -= i; - buffer_head += i; - request->current_chunk_offset = 0; - request->current_chunk_size = 0; - } - if (request->current_chunk_offset < - request->current_chunk_size) - { - uint64_t cur_chunk_left; - - /* we are in the middle of a chunk, give - as much as possible to the client (without - crossing chunk boundaries) */ - cur_chunk_left - = request->current_chunk_size - request->current_chunk_offset; - if (cur_chunk_left > available) - { - to_be_processed = available; - } - else - { /* cur_chunk_left <= (size_t)available */ - to_be_processed = (size_t) cur_chunk_left; - if (available > to_be_processed) - instant_retry = true; - } - } - else - { - size_t i; - size_t end_size; - bool malformed; - - /* we need to read chunk boundaries */ - i = 0; - while (i < available) - { - if ( ('\r' == buffer_head[i]) || - ('\n' == buffer_head[i]) || - (';' == buffer_head[i]) ) - break; - i++; - if (i >= 16) - break; - } - end_size = i; - /* find beginning of CRLF (skip over chunk extensions) */ - if (';' == buffer_head[i]) - { - while (i < available) - { - if ( ('\r' == buffer_head[i]) || - ('\n' == buffer_head[i]) ) - break; - i++; - } - } - /* take '\n' into account; if '\n' is the unavailable - character, we will need to wait until we have it - before going further */ - if ( (i + 1 >= available) && - ! ( (1 == i) && - (2 == available) && - ('0' == buffer_head[0]) ) ) - break; /* need more data... */ - i++; - malformed = (end_size >= 16); - if (! malformed) - { - size_t num_dig = MHD_strx_to_uint64_n_ (buffer_head, - end_size, - &request->current_chunk_size); - malformed = (end_size != num_dig); - } - if (malformed) - { - /* malformed encoding */ - CONNECTION_CLOSE_ERROR (connection, - MHD_SC_CHUNKED_ENCODING_MALFORMED, - _ ( - "Received malformed HTTP request (bad chunked encoding). Closing connection.\n")); - return; - } - /* skip 2nd part of line feed */ - if ( (i < available) && - ( ('\r' == buffer_head[i]) || - ('\n' == buffer_head[i]) ) ) - i++; - - buffer_head += i; - available -= i; - request->current_chunk_offset = 0; - - if (available > 0) - instant_retry = true; - if (0LLU == request->current_chunk_size) - { - request->remaining_upload_size = 0; - break; - } - continue; - } - } - else - { - /* no chunked encoding, give all to the client */ - if ( (0 != request->remaining_upload_size) && - (MHD_SIZE_UNKNOWN != request->remaining_upload_size) && - (request->remaining_upload_size < available) ) - { - to_be_processed = (size_t) request->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; -#if FIXME_OLD_STYLE - if (MHD_NO == - daemon->rc (daemon->rc_cls, - request, - request->url, - request->method, - request->version, - buffer_head, - &left_unprocessed, - &request->client_context)) - { - /* serious internal error, close connection */ - CONNECTION_CLOSE_ERROR (connection, - MHD_SC_APPLICATION_CALLBACK_FAILURE_CLOSED, - _ ( - "Application reported internal error, closing connection.\n")); - return; - } -#endif - if (left_unprocessed > to_be_processed) - mhd_panic (mhd_panic_cls, - __FILE__, - __LINE__ -#ifdef HAVE_MESSAGES - , _ ("libmicrohttpd API violation.\n") -#else - , NULL -#endif - ); - if (0 != left_unprocessed) - { - instant_retry = false; /* client did not process everything */ -#ifdef HAVE_MESSAGES - /* client did not process all upload data, complain if - the setup was incorrect, which may prevent us from - handling the rest of the request */ - if ( (MHD_TM_EXTERNAL_EVENT_LOOP == daemon->threading_mode) && - (! connection->suspended) ) - MHD_DLOG (daemon, - MHD_SC_APPLICATION_HUNG_CONNECTION, - _ ( - "WARNING: incomplete upload processing and connection not suspended may result in hung connection.\n")); -#endif - } - processed_size = to_be_processed - left_unprocessed; - if (request->have_chunked_upload) - request->current_chunk_offset += processed_size; - /* dh left "processed" bytes in buffer for next time... */ - buffer_head += processed_size; - available -= processed_size; - if (MHD_SIZE_UNKNOWN != request->remaining_upload_size) - request->remaining_upload_size -= processed_size; - } - while (instant_retry); - if (available > 0) - memmove (request->read_buffer, - buffer_head, - available); - request->read_buffer_offset = available; -} - - -/** - * Clean up the state of the given connection and move it into the - * clean up queue for final disposal. - * @remark To be called only from thread that process connection's - * recv(), send() and response. - * - * @param connection handle for the connection to clean up - */ -static void -cleanup_connection (struct MHD_Connection *connection) -{ - struct MHD_Daemon *daemon = connection->daemon; - - if (connection->request.in_cleanup) - return; /* Prevent double cleanup. */ - connection->request.in_cleanup = true; - if (NULL != connection->request.response) - { - MHD_response_queue_for_destroy (connection->request.response); - connection->request.response = NULL; - } - MHD_mutex_lock_chk_ (&daemon->cleanup_connection_mutex); - if (connection->suspended) - { - DLL_remove (daemon->suspended_connections_head, - daemon->suspended_connections_tail, - connection); - connection->suspended = false; - } - else - { - if (MHD_TM_THREAD_PER_CONNECTION != daemon->threading_mode) - { - if (connection->connection_timeout == - daemon->connection_default_timeout) - XDLL_remove (daemon->normal_timeout_head, - daemon->normal_timeout_tail, - connection); - else - XDLL_remove (daemon->manual_timeout_head, - daemon->manual_timeout_tail, - connection); - } - DLL_remove (daemon->connections_head, - daemon->connections_tail, - connection); - } - DLL_insert (daemon->cleanup_head, - daemon->cleanup_tail, - connection); - connection->resuming = false; - connection->request.in_idle = false; - MHD_mutex_unlock_chk_ (&daemon->cleanup_connection_mutex); - if (MHD_TM_THREAD_PER_CONNECTION == daemon->threading_mode) - { - /* if we were at the connection limit before and are in - thread-per-connection mode, signal the main thread - to resume accepting connections */ - if ( (MHD_ITC_IS_VALID_ (daemon->itc)) && - (! MHD_itc_activate_ (daemon->itc, - "c")) ) - { -#ifdef HAVE_MESSAGES - MHD_DLOG (daemon, - MHD_SC_ITC_USE_FAILED, - _ ( - "Failed to signal end of connection via inter-thread communication channel.\n")); -#endif - } - } -} - - -#ifdef EPOLL_SUPPORT -/** - * Perform epoll() processing, possibly moving the connection back into - * the epoll() set if needed. - * - * @param connection connection to process - * @return true if we should continue to process the - * connection (not dead yet), false if it died - */ -static bool -connection_epoll_update_ (struct MHD_Connection *connection) -{ - struct MHD_Daemon *daemon = connection->daemon; - - if ( (MHD_ELS_EPOLL == daemon->event_loop_syscall) && - (0 == (connection->epoll_state & MHD_EPOLL_STATE_IN_EPOLL_SET)) && - (0 == (connection->epoll_state & MHD_EPOLL_STATE_SUSPENDED)) && - ( ( (MHD_EVENT_LOOP_INFO_WRITE == connection->request.event_loop_info) && - (0 == (connection->epoll_state & MHD_EPOLL_STATE_WRITE_READY))) || - ( (MHD_EVENT_LOOP_INFO_READ == connection->request.event_loop_info) && - (0 == (connection->epoll_state & MHD_EPOLL_STATE_READ_READY)) ) ) ) - { - /* add to epoll set */ - struct epoll_event event; - - event.events = EPOLLIN | EPOLLOUT | EPOLLPRI | EPOLLET; - event.data.ptr = connection; - if (0 != epoll_ctl (daemon->epoll_fd, - EPOLL_CTL_ADD, - connection->socket_fd, - &event)) - { -#ifdef HAVE_MESSAGES - MHD_DLOG (daemon, - MHD_SC_EPOLL_CTL_ADD_FAILED, - _ ("Call to epoll_ctl failed: %s\n"), - MHD_socket_last_strerr_ ()); -#endif - connection->request.state = MHD_REQUEST_CLOSED; - cleanup_connection (connection); - return false; - } - connection->epoll_state |= MHD_EPOLL_STATE_IN_EPOLL_SET; - } - return true; -} - - -#endif - - -/** - * Update the 'event_loop_info' field of this connection based on the - * state that the connection is now in. May also close the connection - * or perform other updates to the connection if needed to prepare for - * the next round of the event loop. - * - * @param connection connection to get poll set for - */ -static void -connection_update_event_loop_info (struct MHD_Connection *connection) -{ - struct MHD_Daemon *daemon = connection->daemon; - struct MHD_Request *request = &connection->request; - - /* Do not update states of suspended connection */ - if (connection->suspended) - return; /* States will be updated after resume. */ -#ifdef HTTPS_SUPPORT - { - struct MHD_TLS_Plugin *tls; - - if ( (NULL != (tls = daemon->tls_api)) && - (tls->update_event_loop_info (tls->cls, - connection->tls_cs, - &request->event_loop_info)) ) - return; /* TLS has decided what to do */ - } -#endif /* HTTPS_SUPPORT */ - while (1) - { -#if DEBUG_STATES - MHD_DLOG (daemon, - MHD_SC_STATE_MACHINE_STATUS_REPORT, - _ ("In function %s handling connection at state: %s\n"), - __FUNCTION__, - MHD_state_to_string (request->state)); -#endif - switch (request->state) - { - case MHD_REQUEST_INIT: - case MHD_REQUEST_URL_RECEIVED: - case MHD_REQUEST_HEADER_PART_RECEIVED: - /* while reading headers, we always grow the - read buffer if needed, no size-check required */ - if ( (request->read_buffer_offset == request->read_buffer_size) && - (! try_grow_read_buffer (request)) ) - { - transmit_error_response (request, - MHD_SC_CLIENT_HEADER_TOO_BIG, - (NULL != request->url) - ? MHD_HTTP_REQUEST_HEADER_FIELDS_TOO_LARGE - : MHD_HTTP_URI_TOO_LONG, - REQUEST_TOO_BIG); - continue; - } - if (! connection->read_closed) - request->event_loop_info = MHD_EVENT_LOOP_INFO_READ; - else - request->event_loop_info = MHD_EVENT_LOOP_INFO_BLOCK; - break; - case MHD_REQUEST_HEADERS_RECEIVED: - mhd_assert (0); - break; - case MHD_REQUEST_HEADERS_PROCESSED: - mhd_assert (0); - break; - case MHD_REQUEST_CONTINUE_SENDING: - request->event_loop_info = MHD_EVENT_LOOP_INFO_WRITE; - break; - case MHD_REQUEST_CONTINUE_SENT: - if (request->read_buffer_offset == request->read_buffer_size) - { - if ( (! try_grow_read_buffer (request)) && - (MHD_TM_EXTERNAL_EVENT_LOOP != daemon->threading_mode) ) - { - /* failed to grow the read buffer, and the client - which is supposed to handle the received data in - a *blocking* fashion (in this mode) did not - handle the data as it was supposed to! - - => we would either have to do busy-waiting - (on the client, which would likely fail), - or if we do nothing, we would just timeout - on the connection (if a timeout is even set!). - - Solution: we kill the connection with an error */transmit_error_response (request, - MHD_SC_APPLICATION_HUNG_CONNECTION_CLOSED, - MHD_HTTP_INTERNAL_SERVER_ERROR, - INTERNAL_ERROR); - continue; - } - } - if ( (request->read_buffer_offset < request->read_buffer_size) && - (! connection->read_closed) ) - request->event_loop_info = MHD_EVENT_LOOP_INFO_READ; - else - request->event_loop_info = MHD_EVENT_LOOP_INFO_BLOCK; - break; - case MHD_REQUEST_BODY_RECEIVED: - case MHD_REQUEST_FOOTER_PART_RECEIVED: - /* while reading footers, we always grow the - read buffer if needed, no size-check required */ - if (connection->read_closed) - { - CONNECTION_CLOSE_ERROR (connection, - MHD_SC_CONNECTION_READ_FAIL_CLOSED, - NULL); - continue; - } - request->event_loop_info = MHD_EVENT_LOOP_INFO_READ; - /* transition to FOOTERS_RECEIVED - happens in read handler */ - break; - case MHD_REQUEST_FOOTERS_RECEIVED: - request->event_loop_info = MHD_EVENT_LOOP_INFO_BLOCK; - break; - case MHD_REQUEST_HEADERS_SENDING: - /* headers in buffer, keep writing */ - request->event_loop_info = MHD_EVENT_LOOP_INFO_WRITE; - break; - case MHD_REQUEST_HEADERS_SENT: - mhd_assert (0); - break; - case MHD_REQUEST_NORMAL_BODY_READY: - request->event_loop_info = MHD_EVENT_LOOP_INFO_WRITE; - break; - case MHD_REQUEST_NORMAL_BODY_UNREADY: - request->event_loop_info = MHD_EVENT_LOOP_INFO_BLOCK; - break; - case MHD_REQUEST_CHUNKED_BODY_READY: - request->event_loop_info = MHD_EVENT_LOOP_INFO_WRITE; - break; - case MHD_REQUEST_CHUNKED_BODY_UNREADY: - request->event_loop_info = MHD_EVENT_LOOP_INFO_BLOCK; - break; - case MHD_REQUEST_BODY_SENT: - mhd_assert (0); - break; - case MHD_REQUEST_FOOTERS_SENDING: - request->event_loop_info = MHD_EVENT_LOOP_INFO_WRITE; - break; - case MHD_REQUEST_FOOTERS_SENT: - mhd_assert (0); - break; - case MHD_REQUEST_CLOSED: - request->event_loop_info = MHD_EVENT_LOOP_INFO_CLEANUP; - return; /* do nothing, not even reading */ -#ifdef UPGRADE_SUPPORT - case MHD_REQUEST_UPGRADE: - mhd_assert (0); - break; -#endif /* UPGRADE_SUPPORT */ - default: - mhd_assert (0); - } - break; - } -} - - -/** - * This function was created to handle per-request processing that - * has to happen even if the socket cannot be read or written to. - * @remark To be called only from thread that process request's - * recv(), send() and response. - * - * @param request the request to handle - * @return true if we should continue to process the - * request (not dead yet), false if it died - */ -bool -MHD_request_handle_idle_ (struct MHD_Request *request) -{ - struct MHD_Daemon *daemon = request->daemon; - struct MHD_Connection *connection = request->connection; - char *line; - size_t line_len; - bool ret; - - request->in_idle = true; - while (! connection->suspended) - { -#ifdef HTTPS_SUPPORT - struct MHD_TLS_Plugin *tls; - - if ( (NULL != (tls = daemon->tls_api)) && - (! tls->idle_ready (tls->cls, - connection->tls_cs)) ) - break; -#endif /* HTTPS_SUPPORT */ -#if DEBUG_STATES - MHD_DLOG (daemon, - MHD_SC_STATE_MACHINE_STATUS_REPORT, - _ ("In function %s handling connection at state: %s\n"), - __FUNCTION__, - MHD_state_to_string (request->state)); -#endif - switch (request->state) - { - case MHD_REQUEST_INIT: - line = get_next_header_line (request, - &line_len); - /* Check for empty string, as we might want - to tolerate 'spurious' empty lines; also - NULL means we didn't get a full line yet; - line is not 0-terminated here. */ - if ( (NULL == line) || - (0 == line[0]) ) - { - if (MHD_REQUEST_INIT != request->state) - continue; - if (connection->read_closed) - { - CONNECTION_CLOSE_ERROR (connection, - MHD_SC_CONNECTION_READ_FAIL_CLOSED, - NULL); - continue; - } - break; - } - if (MHD_NO == - parse_initial_message_line (request, - line, - line_len)) - CONNECTION_CLOSE_ERROR (connection, - MHD_SC_CONNECTION_CLOSED, - NULL); - else - request->state = MHD_REQUEST_URL_RECEIVED; - continue; - case MHD_REQUEST_URL_RECEIVED: - line = get_next_header_line (request, - NULL); - if (NULL == line) - { - if (MHD_REQUEST_URL_RECEIVED != request->state) - continue; - if (connection->read_closed) - { - CONNECTION_CLOSE_ERROR (connection, - MHD_SC_CONNECTION_READ_FAIL_CLOSED, - NULL); - continue; - } - break; - } - if (0 == line[0]) - { - request->state = MHD_REQUEST_HEADERS_RECEIVED; - request->header_size = (size_t) (line - request->read_buffer); - continue; - } - if (! process_header_line (request, - line)) - { - transmit_error_response (request, - MHD_SC_CONNECTION_PARSE_FAIL_CLOSED, - MHD_HTTP_BAD_REQUEST, - REQUEST_MALFORMED); - break; - } - request->state = MHD_REQUEST_HEADER_PART_RECEIVED; - continue; - case MHD_REQUEST_HEADER_PART_RECEIVED: - line = get_next_header_line (request, - NULL); - if (NULL == line) - { - if (request->state != MHD_REQUEST_HEADER_PART_RECEIVED) - continue; - if (connection->read_closed) - { - CONNECTION_CLOSE_ERROR (connection, - MHD_SC_CONNECTION_READ_FAIL_CLOSED, - NULL); - continue; - } - break; - } - if (MHD_NO == - process_broken_line (request, - line, - MHD_HEADER_KIND)) - continue; - if (0 == line[0]) - { - request->state = MHD_REQUEST_HEADERS_RECEIVED; - request->header_size = (size_t) (line - request->read_buffer); - continue; - } - continue; - case MHD_REQUEST_HEADERS_RECEIVED: - parse_request_headers (request); - if (MHD_REQUEST_CLOSED == request->state) - continue; - request->state = MHD_REQUEST_HEADERS_PROCESSED; - if (connection->suspended) - break; - continue; - case MHD_REQUEST_HEADERS_PROCESSED: - call_request_handler (request); /* first call */ - if (MHD_REQUEST_CLOSED == request->state) - continue; - if (need_100_continue (request)) - { - request->state = MHD_REQUEST_CONTINUE_SENDING; - if (socket_flush_possible (connection)) - socket_start_extra_buffering (connection); - else - socket_start_no_buffering (connection); - break; - } - if ( (NULL != request->response) && - ( (MHD_METHOD_POST == request->method) || - (MHD_METHOD_PUT == request->method) ) ) - { - /* we refused (no upload allowed!) */ - request->remaining_upload_size = 0; - /* force close, in case client still tries to upload... */ - connection->read_closed = true; - } - request->state = (0 == request->remaining_upload_size) - ? MHD_REQUEST_FOOTERS_RECEIVED - : MHD_REQUEST_CONTINUE_SENT; - if (connection->suspended) - break; - continue; - case MHD_REQUEST_CONTINUE_SENDING: - if (request->continue_message_write_offset == - MHD_STATICSTR_LEN_ (HTTP_100_CONTINUE)) - { - request->state = MHD_REQUEST_CONTINUE_SENT; - if (! socket_flush_possible (connection)) - socket_start_no_buffering_flush (connection); - else - socket_start_normal_buffering (connection); - continue; - } - break; - case MHD_REQUEST_CONTINUE_SENT: - if (0 != request->read_buffer_offset) - { - process_request_body (request); /* loop call */ - if (MHD_REQUEST_CLOSED == request->state) - continue; - } - if ( (0 == request->remaining_upload_size) || - ( (MHD_SIZE_UNKNOWN == request->remaining_upload_size) && - (0 == request->read_buffer_offset) && - (connection->read_closed) ) ) - { - if ( (request->have_chunked_upload) && - (! connection->read_closed) ) - request->state = MHD_REQUEST_BODY_RECEIVED; - else - request->state = MHD_REQUEST_FOOTERS_RECEIVED; - if (connection->suspended) - break; - continue; - } - break; - case MHD_REQUEST_BODY_RECEIVED: - line = get_next_header_line (request, - NULL); - if (NULL == line) - { - if (request->state != MHD_REQUEST_BODY_RECEIVED) - continue; - if (connection->read_closed) - { - CONNECTION_CLOSE_ERROR (connection, - MHD_SC_CONNECTION_CLOSED, - NULL); - continue; - } - break; - } - if (0 == line[0]) - { - request->state = MHD_REQUEST_FOOTERS_RECEIVED; - if (connection->suspended) - break; - continue; - } - if (MHD_NO == process_header_line (request, - line)) - { - transmit_error_response (request, - MHD_SC_CONNECTION_PARSE_FAIL_CLOSED, - MHD_HTTP_BAD_REQUEST, - REQUEST_MALFORMED); - break; - } - request->state = MHD_REQUEST_FOOTER_PART_RECEIVED; - continue; - case MHD_REQUEST_FOOTER_PART_RECEIVED: - line = get_next_header_line (request, - NULL); - if (NULL == line) - { - if (request->state != MHD_REQUEST_FOOTER_PART_RECEIVED) - continue; - if (connection->read_closed) - { - CONNECTION_CLOSE_ERROR (connection, - MHD_SC_CONNECTION_CLOSED, - NULL); - continue; - } - break; - } - if (MHD_NO == - process_broken_line (request, - line, - MHD_FOOTER_KIND)) - continue; - if (0 == line[0]) - { - request->state = MHD_REQUEST_FOOTERS_RECEIVED; - if (connection->suspended) - break; - continue; - } - continue; - case MHD_REQUEST_FOOTERS_RECEIVED: - call_request_handler (request); /* "final" call */ - if (request->state == MHD_REQUEST_CLOSED) - continue; - if (NULL == request->response) - break; /* try again next time */ - if (! build_header_response (request)) - { - /* oops - close! */ - CONNECTION_CLOSE_ERROR (connection, - MHD_SC_FAILED_RESPONSE_HEADER_GENERATION, - _ ( - "Closing connection (failed to create response header).\n")); - continue; - } - request->state = MHD_REQUEST_HEADERS_SENDING; - if (MHD_NO != socket_flush_possible (connection)) - socket_start_extra_buffering (connection); - else - socket_start_no_buffering (connection); - - break; - case MHD_REQUEST_HEADERS_SENDING: - /* no default action */ - break; - case MHD_REQUEST_HEADERS_SENT: - /* Some clients may take some actions right after header receive */ - if (MHD_NO != socket_flush_possible (connection)) - socket_start_no_buffering_flush (connection); - -#ifdef UPGRADE_SUPPORT - if (NULL != request->response->upgrade_handler) - { - socket_start_normal_buffering (connection); - request->state = MHD_REQUEST_UPGRADE; -#if FIXME_LEGACY_STYLE - /* This request is "upgraded". Pass socket to application. */ - if (! MHD_response_execute_upgrade_ (request->response, - request)) - { - /* upgrade failed, fail hard */ - CONNECTION_CLOSE_ERROR (connection, - MHD_SC_CONNECTION_CLOSED, - NULL); - continue; - } -#endif - /* Response is not required anymore for this request. */ - { - struct MHD_Response *const resp = request->response; - - request->response = NULL; - MHD_response_queue_for_destroy (resp); - } - continue; - } -#endif /* UPGRADE_SUPPORT */ - if (MHD_NO != socket_flush_possible (connection)) - socket_start_extra_buffering (connection); - else - socket_start_normal_buffering (connection); - - if (request->have_chunked_upload) - request->state = MHD_REQUEST_CHUNKED_BODY_UNREADY; - else - request->state = MHD_REQUEST_NORMAL_BODY_UNREADY; - continue; - case MHD_REQUEST_NORMAL_BODY_READY: - /* nothing to do here */ - break; - case MHD_REQUEST_NORMAL_BODY_UNREADY: - if (NULL != request->response->crc) - MHD_mutex_lock_chk_ (&request->response->mutex); - if (0 == request->response->total_size) - { - if (NULL != request->response->crc) - MHD_mutex_unlock_chk_ (&request->response->mutex); - request->state = MHD_REQUEST_BODY_SENT; - continue; - } - if (try_ready_normal_body (request)) - { - if (NULL != request->response->crc) - MHD_mutex_unlock_chk_ (&request->response->mutex); - request->state = MHD_REQUEST_NORMAL_BODY_READY; - /* Buffering for flushable socket was already enabled*/ - if (MHD_NO == socket_flush_possible (connection)) - socket_start_no_buffering (connection); - break; - } - /* mutex was already unlocked by "try_ready_normal_body */ - /* not ready, no socket action */ - break; - case MHD_REQUEST_CHUNKED_BODY_READY: - /* nothing to do here */ - break; - case MHD_REQUEST_CHUNKED_BODY_UNREADY: - if (NULL != request->response->crc) - MHD_mutex_lock_chk_ (&request->response->mutex); - if ( (0 == request->response->total_size) || - (request->response_write_position == - request->response->total_size) ) - { - if (NULL != request->response->crc) - MHD_mutex_unlock_chk_ (&request->response->mutex); - request->state = MHD_REQUEST_BODY_SENT; - continue; - } - if (try_ready_chunked_body (request)) - { - if (NULL != request->response->crc) - MHD_mutex_unlock_chk_ (&request->response->mutex); - request->state = MHD_REQUEST_CHUNKED_BODY_READY; - /* Buffering for flushable socket was already enabled */ - if (MHD_NO == socket_flush_possible (connection)) - socket_start_no_buffering (connection); - continue; - } - /* mutex was already unlocked by try_ready_chunked_body */ - break; - case MHD_REQUEST_BODY_SENT: - if (! build_header_response (request)) - { - /* oops - close! */ - CONNECTION_CLOSE_ERROR (connection, - MHD_SC_FAILED_RESPONSE_HEADER_GENERATION, - _ ( - "Closing connection (failed to create response header).\n")); - continue; - } - if ( (! request->have_chunked_upload) || - (request->write_buffer_send_offset == - request->write_buffer_append_offset) ) - request->state = MHD_REQUEST_FOOTERS_SENT; - else - request->state = MHD_REQUEST_FOOTERS_SENDING; - continue; - case MHD_REQUEST_FOOTERS_SENDING: - /* no default action */ - break; - case MHD_REQUEST_FOOTERS_SENT: - { - struct MHD_Response *response = request->response; - - if (MHD_HTTP_PROCESSING == response->status_code) - { - /* After this type of response, we allow sending another! */ - request->state = MHD_REQUEST_HEADERS_PROCESSED; - MHD_response_queue_for_destroy (response); - request->response = NULL; - /* FIXME: maybe partially reset memory pool? */ - continue; - } - if (socket_flush_possible (connection)) - socket_start_no_buffering_flush (connection); - else - socket_start_normal_buffering (connection); - - if (NULL != response->termination_cb) - { - response->termination_cb (response->termination_cb_cls, - MHD_REQUEST_TERMINATED_COMPLETED_OK, - request->client_context); - } - MHD_response_queue_for_destroy (response); - request->response = NULL; - } - if ( (MHD_CONN_USE_KEEPALIVE != request->keepalive) || - (connection->read_closed) ) - { - /* have to close for some reason */ - MHD_connection_close_ (connection, - MHD_REQUEST_TERMINATED_COMPLETED_OK); - MHD_pool_destroy (connection->pool); - connection->pool = NULL; - request->read_buffer = NULL; - request->read_buffer_size = 0; - request->read_buffer_offset = 0; - } - else - { - /* can try to keep-alive */ - if (socket_flush_possible (connection)) - socket_start_normal_buffering (connection); - request->version_s = NULL; - request->state = MHD_REQUEST_INIT; - request->last = NULL; - request->colon = NULL; - request->header_size = 0; - request->keepalive = MHD_CONN_KEEPALIVE_UNKOWN; - /* Reset the read buffer to the starting size, - preserving the bytes we have already read. */ - request->read_buffer - = MHD_pool_reset (connection->pool, - request->read_buffer, - request->read_buffer_offset, - daemon->connection_memory_limit_b / 2); - request->read_buffer_size - = daemon->connection_memory_limit_b / 2; - } - // FIXME: this is too much, NULLs out some of the things - // initialized above... - memset (request, - 0, - sizeof (struct MHD_Request)); - request->daemon = daemon; - request->connection = connection; - continue; - case MHD_REQUEST_CLOSED: - cleanup_connection (connection); - request->in_idle = false; - return false; -#ifdef UPGRADE_SUPPORT - case MHD_REQUEST_UPGRADE: - request->in_idle = false; - return true; /* keep open */ -#endif /* UPGRADE_SUPPORT */ - default: - mhd_assert (0); - break; - } - break; - } - if (! connection->suspended) - { - time_t timeout; - timeout = connection->connection_timeout; - if ( (0 != timeout) && - (timeout <= (MHD_monotonic_sec_counter () - - connection->last_activity)) ) - { - MHD_connection_close_ (connection, - MHD_REQUEST_TERMINATED_TIMEOUT_REACHED); - request->in_idle = false; - return true; - } - } - connection_update_event_loop_info (connection); - ret = true; -#ifdef EPOLL_SUPPORT - if ( (! connection->suspended) && - (MHD_ELS_EPOLL == daemon->event_loop_syscall) ) - { - ret = connection_epoll_update_ (connection); - } -#endif /* EPOLL_SUPPORT */ - request->in_idle = false; - return ret; -} - - -/** - * Call the handlers for a connection in the appropriate order based - * on the readiness as detected by the event loop. - * - * @param con connection to handle - * @param read_ready set if the socket is ready for reading - * @param write_ready set if the socket is ready for writing - * @param force_close set if a hard error was detected on the socket; - * if this information is not available, simply pass #MHD_NO - * @return #MHD_YES to continue normally, - * #MHD_NO if a serious error was encountered and the - * connection is to be closed. - */ -// FIXME: rename connection->request? -int -MHD_connection_call_handlers_ (struct MHD_Connection *con, - bool read_ready, - bool write_ready, - bool force_close) -{ - struct MHD_Daemon *daemon = con->daemon; - int ret; - bool states_info_processed = false; - /* Fast track flag */ - bool on_fasttrack = (con->request.state == MHD_REQUEST_INIT); - -#ifdef HTTPS_SUPPORT - if (con->tls_read_ready) - read_ready = true; -#endif /* HTTPS_SUPPORT */ - if (! force_close) - { - if ( (MHD_EVENT_LOOP_INFO_READ == - con->request.event_loop_info) && - read_ready) - { - MHD_request_handle_read_ (&con->request); - ret = MHD_request_handle_idle_ (&con->request); - states_info_processed = true; - } - /* No need to check value of 'ret' here as closed connection - * cannot be in MHD_EVENT_LOOP_INFO_WRITE state. */ - if ( (MHD_EVENT_LOOP_INFO_WRITE == - con->request.event_loop_info) && - write_ready) - { - MHD_request_handle_write_ (&con->request); - ret = MHD_request_handle_idle_ (&con->request); - states_info_processed = true; - } - } - else - { - MHD_connection_close_ (con, - MHD_REQUEST_TERMINATED_WITH_ERROR); - return MHD_request_handle_idle_ (&con->request); - } - - if (! states_info_processed) - { /* Connection is not read or write ready, but external conditions - * may be changed and need to be processed. */ - ret = MHD_request_handle_idle_ (&con->request); - } - /* Fast track for fast connections. */ - /* If full request was read by single read_handler() invocation - and headers were completely prepared by single MHD_request_handle_idle_() - then try not to wait for next sockets polling and send response - immediately. - As writeability of socket was not checked and it may have - some data pending in system buffers, use this optimization - only for non-blocking sockets. *//* No need to check 'ret' as connection is always in - * MHD_CONNECTION_CLOSED state if 'ret' is equal 'MHD_NO'. */else if (on_fasttrack && - con->sk_nonblck) - { - if (MHD_REQUEST_HEADERS_SENDING == con->request.state) - { - MHD_request_handle_write_ (&con->request); - /* Always call 'MHD_request_handle_idle_()' after each read/write. */ - ret = MHD_request_handle_idle_ (&con->request); - } - /* If all headers were sent by single write_handler() and - * response body is prepared by single MHD_request_handle_idle_() - * call - continue. */ - if ((MHD_REQUEST_NORMAL_BODY_READY == con->request.state) || - (MHD_REQUEST_CHUNKED_BODY_READY == con->request.state)) - { - MHD_request_handle_write_ (&con->request); - ret = MHD_request_handle_idle_ (&con->request); - } - } - - /* All connection's data and states are processed for this turn. - * If connection already has more data to be processed - use - * zero timeout for next select()/poll(). */ - /* Thread-per-connection do not need global zero timeout as - * connections are processed individually. */ - /* Note: no need to check for read buffer availability for - * TLS read-ready connection in 'read info' state as connection - * without space in read buffer will be market as 'info block'. */ - if ( (! daemon->data_already_pending) && - (MHD_TM_THREAD_PER_CONNECTION != daemon->threading_mode) ) - { - if (MHD_EVENT_LOOP_INFO_BLOCK == - con->request.event_loop_info) - daemon->data_already_pending = true; -#ifdef HTTPS_SUPPORT - else if ( (con->tls_read_ready) && - (MHD_EVENT_LOOP_INFO_READ == - con->request.event_loop_info) ) - daemon->data_already_pending = true; -#endif /* HTTPS_SUPPORT */ - } - return ret; -} - - -/* end of connection_call_handlers.c */ diff --git a/src/lib/connection_call_handlers.h b/src/lib/connection_call_handlers.h @@ -1,64 +0,0 @@ -/* - This file is part of libmicrohttpd - Copyright (C) 2007-2018 Daniel Pittman and Christian Grothoff - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. - - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with this library; if not, write to the Free Software - Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA -*/ -/** - * @file lib/connection_call_handlers.h - * @brief function to call event handlers based on event mask - * @author Christian Grothoff - */ - -#ifndef CONNECTION_CALL_HANDLERS_H -#define CONNECTION_CALL_HANDLERS_H - -/** - * Call the handlers for a connection in the appropriate order based - * on the readiness as detected by the event loop. - * - * @param con connection to handle - * @param read_ready set if the socket is ready for reading - * @param write_ready set if the socket is ready for writing - * @param force_close set if a hard error was detected on the socket; - * if this information is not available, simply pass #MHD_NO - * @return #MHD_YES to continue normally, - * #MHD_NO if a serious error was encountered and the - * connection is to be closed. - */ -int -MHD_connection_call_handlers_ (struct MHD_Connection *con, - bool read_ready, - bool write_ready, - bool force_close) -MHD_NONNULL (1); - - -/** - * This function was created to handle per-request processing that - * has to happen even if the socket cannot be read or written to. - * @remark To be called only from thread that process request's - * recv(), send() and response. - * - * @param request the request to handle - * @return true if we should continue to process the - * request (not dead yet), false if it died - */ -bool -MHD_request_handle_idle_ (struct MHD_Request *request) -MHD_NONNULL (1); - - -#endif diff --git a/src/lib/connection_cleanup.c b/src/lib/connection_cleanup.c @@ -1,160 +0,0 @@ -/* - This file is part of libmicrohttpd - Copyright (C) 2007-2018 Daniel Pittman and Christian Grothoff - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. - - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with this library; if not, write to the Free Software - Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA -*/ -/** - * @file lib/connection_cleanup.c - * @brief function to clean up completed connections - * @author Christian Grothoff - */ -#include "internal.h" -#include "connection_cleanup.h" -#include "daemon_ip_limit.h" - - -#ifdef UPGRADE_SUPPORT -/** - * Finally cleanup upgrade-related resources. It should - * be called when TLS buffers have been drained and - * application signaled MHD by #MHD_UPGRADE_ACTION_CLOSE. - * - * @param connection handle to the upgraded connection to clean - */ -static void -connection_cleanup_upgraded (struct MHD_Connection *connection) -{ - struct MHD_UpgradeResponseHandle *urh = connection->request.urh; - - if (NULL == urh) - return; -#ifdef HTTPS_SUPPORT - /* Signal remote client the end of TLS connection by - * gracefully closing TLS session. */ - { - struct MHD_TLS_Plugin *tls; - - if (NULL != (tls = connection->daemon->tls_api)) - (void) tls->shutdown_connection (tls->cls, - connection->tls_cs); - } - if (MHD_INVALID_SOCKET != urh->mhd.socket) - MHD_socket_close_chk_ (urh->mhd.socket); - if (MHD_INVALID_SOCKET != urh->app.socket) - MHD_socket_close_chk_ (urh->app.socket); -#endif /* HTTPS_SUPPORT */ - connection->request.urh = NULL; - free (urh); -} - - -#endif /* UPGRADE_SUPPORT */ - - -/** - * Free resources associated with all closed connections. (destroy - * responses, free buffers, etc.). All closed connections are kept in - * the "cleanup" doubly-linked list. - * - * @remark To be called only from thread that process daemon's - * select()/poll()/etc. - * - * @param daemon daemon to clean up - */ -void -MHD_connection_cleanup_ (struct MHD_Daemon *daemon) -{ - struct MHD_Connection *pos; - - MHD_mutex_lock_chk_ (&daemon->cleanup_connection_mutex); - while (NULL != (pos = daemon->cleanup_tail)) - { - DLL_remove (daemon->cleanup_head, - daemon->cleanup_tail, - pos); - MHD_mutex_unlock_chk_ (&daemon->cleanup_connection_mutex); - - if ( (MHD_TM_THREAD_PER_CONNECTION == daemon->threading_mode) && - (! pos->thread_joined) && - (! MHD_join_thread_ (pos->pid.handle)) ) - MHD_PANIC (_ ("Failed to join a thread.\n")); -#ifdef UPGRADE_SUPPORT - connection_cleanup_upgraded (pos); -#endif /* UPGRADE_SUPPORT */ - MHD_pool_destroy (pos->pool); -#ifdef HTTPS_SUPPORT - { - struct MHD_TLS_Plugin *tls; - - if (NULL != (tls = daemon->tls_api)) - tls->teardown_connection (tls->cls, - pos->tls_cs); - } -#endif /* HTTPS_SUPPORT */ - - /* clean up the connection */ - if (NULL != daemon->notify_connection_cb) - daemon->notify_connection_cb (daemon->notify_connection_cb_cls, - pos, - MHD_CONNECTION_NOTIFY_CLOSED); - MHD_ip_limit_del (daemon, - (const struct sockaddr *) &pos->addr, - pos->addr_len); -#ifdef EPOLL_SUPPORT - if (MHD_ELS_EPOLL == daemon->event_loop_syscall) - { - if (0 != (pos->epoll_state & MHD_EPOLL_STATE_IN_EREADY_EDLL)) - { - EDLL_remove (daemon->eready_head, - daemon->eready_tail, - pos); - pos->epoll_state &= ~MHD_EPOLL_STATE_IN_EREADY_EDLL; - } - if ( (-1 != daemon->epoll_fd) && - (0 != (pos->epoll_state & MHD_EPOLL_STATE_IN_EPOLL_SET)) ) - { - /* epoll documentation suggests that closing a FD - automatically removes it from the epoll set; however, - this is not true as if we fail to do manually remove it, - we are still seeing an event for this fd in epoll, - causing grief (use-after-free...) --- at least on my - system. */if (0 != epoll_ctl (daemon->epoll_fd, - EPOLL_CTL_DEL, - pos->socket_fd, - NULL)) - MHD_PANIC (_ ("Failed to remove FD from epoll set.\n")); - pos->epoll_state &= ~MHD_EPOLL_STATE_IN_EPOLL_SET; - } - } -#endif - if (NULL != pos->request.response) - { - MHD_response_queue_for_destroy (pos->request.response); - pos->request.response = NULL; - } - if (MHD_INVALID_SOCKET != pos->socket_fd) - MHD_socket_close_chk_ (pos->socket_fd); - free (pos); - - MHD_mutex_lock_chk_ (&daemon->cleanup_connection_mutex); - daemon->connections--; - daemon->at_limit = false; - } - MHD_mutex_unlock_chk_ (&daemon->cleanup_connection_mutex); -} - - -/* end of connection_cleanup.c */ diff --git a/src/lib/connection_cleanup.h b/src/lib/connection_cleanup.h @@ -1,42 +0,0 @@ -/* - This file is part of libmicrohttpd - Copyright (C) 2007-2018 Daniel Pittman and Christian Grothoff - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. - - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with this library; if not, write to the Free Software - Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA -*/ -/** - * @file lib/connection_cleanup.h - * @brief functions to cleanup completed connection - * @author Christian Grothoff - */ -#ifndef CONNECTION_CLEANUP_H -#define CONNECTION_CLEANUP_H - - -/** - * Free resources associated with all closed connections. (destroy - * responses, free buffers, etc.). All closed connections are kept in - * the "cleanup" doubly-linked list. - * - * @remark To be called only from thread that process daemon's - * select()/poll()/etc. - * - * @param daemon daemon to clean up - */ -void -MHD_connection_cleanup_ (struct MHD_Daemon *daemon) -MHD_NONNULL (1); - -#endif diff --git a/src/lib/connection_close.c b/src/lib/connection_close.c @@ -1,103 +0,0 @@ -/* - This file is part of libmicrohttpd - Copyright (C) 2007-2018 Daniel Pittman and Christian Grothoff - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. - - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with this library; if not, write to the Free Software - Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA -*/ -/** - * @file lib/connection_close.c - * @brief functions to close a connection - * @author Christian Grothoff - */ -#include "internal.h" -#include "connection_close.h" - - -/** - * Mark connection as "closed". - * - * @remark To be called from any thread. - * - * @param connection connection to close - */ -void -MHD_connection_mark_closed_ (struct MHD_Connection *connection) -{ - const struct MHD_Daemon *daemon = connection->daemon; - - connection->request.state = MHD_REQUEST_CLOSED; - connection->request.event_loop_info = MHD_EVENT_LOOP_INFO_CLEANUP; - if (! daemon->enable_turbo) - { -#ifdef HTTPS_SUPPORT - struct MHD_TLS_Plugin *tls; - - /* For TLS connection use shutdown of TLS layer - * and do not shutdown TCP socket. This give more - * chances to send TLS closure data to remote side. - * Closure of TLS layer will be interpreted by - * remote side as end of transmission. */if (NULL != (tls = daemon->tls_api)) - { - if (MHD_YES != - tls->shutdown_connection (tls->cls, - connection->tls_cs)) - { - (void) shutdown (connection->socket_fd, - SHUT_WR); - /* FIXME: log errors */ - } - } - else /* Combined with next 'shutdown()'. */ -#endif /* HTTPS_SUPPORT */ - { - (void) shutdown (connection->socket_fd, - SHUT_WR); /* FIXME: log errors */ - } - } -} - - -/** - * Close the given connection and give the specified termination code - * to the user. - * - * @remark To be called only from thread that process - * connection's recv(), send() and response. - * - * @param connection connection to close - * @param rtc termination reason to give - */ -void -MHD_connection_close_ (struct MHD_Connection *connection, - enum MHD_RequestTerminationCode rtc) -{ - struct MHD_Daemon *daemon = connection->daemon; - struct MHD_Response *resp = connection->request.response; - - (void) rtc; // FIXME - MHD_connection_mark_closed_ (connection); - if (NULL != resp) - { - connection->request.response = NULL; - MHD_response_queue_for_destroy (resp); - } - if (NULL != daemon->notify_connection_cb) - daemon->notify_connection_cb (daemon->notify_connection_cb_cls, - connection, - MHD_CONNECTION_NOTIFY_CLOSED); -} - - -/* end of connection_close.c */ diff --git a/src/lib/connection_close.h b/src/lib/connection_close.h @@ -1,56 +0,0 @@ -/* - This file is part of libmicrohttpd - Copyright (C) 2007-2018 Daniel Pittman and Christian Grothoff - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. - - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with this library; if not, write to the Free Software - Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA -*/ -/** - * @file lib/connection_close.h - * @brief functions to close connection - * @author Christian Grothoff - */ -#ifndef CONNECTION_CLOSE_H -#define CONNECTION_CLOSE_H - -#include "microhttpd2.h" - -/** - * Mark connection as "closed". - * - * @remark To be called from any thread. - * - * @param connection connection to close - */ -void -MHD_connection_mark_closed_ (struct MHD_Connection *connection) -MHD_NONNULL (1); - - -/** - * Close the given connection and give the specified termination code - * to the user. - * - * @remark To be called only from thread that process - * connection's recv(), send() and response. - * - * @param connection connection to close - * @param rtc termination reason to give - */ -void -MHD_connection_close_ (struct MHD_Connection *connection, - enum MHD_RequestTerminationCode rtc) -MHD_NONNULL (1); - -#endif diff --git a/src/lib/connection_finish_forward.c b/src/lib/connection_finish_forward.c @@ -1,95 +0,0 @@ -/* - This file is part of libmicrohttpd - Copyright (C) 2007-2018 Daniel Pittman and Christian Grothoff - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. - - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with this library; if not, write to the Free Software - Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA -*/ -/** - * @file lib/connection_finish_forward.c - * @brief complete upgrade socket forwarding operation in TLS mode - * @author Christian Grothoff - */ -#include "internal.h" -#include "connection_finish_forward.h" - - -#if defined(HTTPS_SUPPORT) && defined(UPGRADE_SUPPORT) -/** - * Stop TLS forwarding on upgraded connection and - * reflect remote disconnect state to socketpair. - * @remark In thread-per-connection mode this function - * can be called from any thread, in other modes this - * function must be called only from thread that process - * daemon's select()/poll()/etc. - * - * @param connection the upgraded connection - */ -void -MHD_connection_finish_forward_ (struct MHD_Connection *connection) -{ - struct MHD_Daemon *daemon = connection->daemon; - struct MHD_UpgradeResponseHandle *urh = connection->request.urh; - - if (NULL == daemon->tls_api) - return; /* Nothing to do with non-TLS connection. */ - - if (MHD_TM_THREAD_PER_CONNECTION != daemon->threading_mode) - DLL_remove (daemon->urh_head, - daemon->urh_tail, - urh); -#if EPOLL_SUPPORT - if ( (MHD_ELS_EPOLL == daemon->event_loop_syscall) && - (0 != epoll_ctl (daemon->epoll_upgrade_fd, - EPOLL_CTL_DEL, - connection->socket_fd, - NULL)) ) - { - MHD_PANIC (_ ("Failed to remove FD from epoll set.\n")); - } - if (urh->in_eready_list) - { - EDLL_remove (daemon->eready_urh_head, - daemon->eready_urh_tail, - urh); - urh->in_eready_list = false; - } -#endif /* EPOLL_SUPPORT */ - if (MHD_INVALID_SOCKET != urh->mhd.socket) - { -#if EPOLL_SUPPORT - if ( (MHD_ELS_EPOLL == daemon->event_loop_syscall) && - (0 != epoll_ctl (daemon->epoll_upgrade_fd, - EPOLL_CTL_DEL, - urh->mhd.socket, - NULL)) ) - { - MHD_PANIC (_ ("Failed to remove FD from epoll set.\n")); - } -#endif /* EPOLL_SUPPORT */ - /* Reflect remote disconnect to application by breaking - * socketpair connection. */ - shutdown (urh->mhd.socket, - SHUT_RDWR); - } - /* Socketpair sockets will remain open as they will be - * used with MHD_UPGRADE_ACTION_CLOSE. They will be - * closed by MHD_cleanup_upgraded_connection_() during - * connection's final cleanup. - */} - - -#endif /* HTTPS_SUPPORT && UPGRADE_SUPPORT*/ - -/* end of connection_finish_forward.c */ diff --git a/src/lib/connection_finish_forward.h b/src/lib/connection_finish_forward.h @@ -1,44 +0,0 @@ -/* - This file is part of libmicrohttpd - Copyright (C) 2007-2018 Daniel Pittman and Christian Grothoff - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. - - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with this library; if not, write to the Free Software - Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA -*/ -/** - * @file lib/connection_finish_forward.h - * @brief complete upgrade socket forwarding operation in TLS mode - * @author Christian Grothoff - */ - -#ifndef CONNECTION_FINISH_FORWARD_H -#define CONNECTION_FINISH_FORWARD_H - - -/** - * Stop TLS forwarding on upgraded connection and - * reflect remote disconnect state to socketpair. - * - * @remark In thread-per-connection mode this function - * can be called from any thread, in other modes this - * function must be called only from thread that process - * daemon's select()/poll()/etc. - * - * @param connection the upgraded connection - */ -void -MHD_connection_finish_forward_ (struct MHD_Connection *connection) -MHD_NONNULL (1); - -#endif diff --git a/src/lib/connection_info.c b/src/lib/connection_info.c @@ -1,111 +0,0 @@ -/* - This file is part of libmicrohttpd - Copyright (C) 2007-2018 Daniel Pittman and Christian Grothoff - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. - - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with this library; if not, write to the Free Software - Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA -*/ - -/** - * @file lib/connection_info.c - * @brief implementation of MHD_connection_get_information_sz() - * @author Christian Grothoff - */ -#include "internal.h" - - -/** - * Obtain information about the given connection. - * Use wrapper macro #MHD_connection_get_information() instead of direct use - * of this function. - * - * @param connection what connection to get information about - * @param info_type what information is desired? - * @param[out] return_value pointer to union where requested information will - * be stored - * @param return_value_size size of union MHD_ConnectionInformation at compile - * time - * @return #MHD_YES on success, #MHD_NO on error - * (@a info_type is unknown, NULL pointer etc.) - * @ingroup specialized - */ -enum MHD_Bool -MHD_connection_get_information_sz (struct MHD_Connection *connection, - enum MHD_ConnectionInformationType info_type, - union MHD_ConnectionInformation *return_value, - size_t return_value_size) -{ -#define CHECK_SIZE(type) if (sizeof(type) < return_value_size) \ - return MHD_NO - - switch (info_type) - { -#ifdef HTTPS_SUPPORT - case MHD_CONNECTION_INFORMATION_CIPHER_ALGO: - CHECK_SIZE (int); - if (NULL == connection->tls_cs) - return MHD_NO; - // return_value->cipher_algorithm - // = gnutls_cipher_get (connection->tls_session); - return MHD_NO; // FIXME: to be implemented - case MHD_CONNECTION_INFORMATION_PROTOCOL: - CHECK_SIZE (int); - if (NULL == connection->tls_cs) - return MHD_NO; - // return_value->protocol - // = gnutls_protocol_get_version (connection->tls_session); - return MHD_NO; // FIXME: to be implemented - case MHD_CONNECTION_INFORMATION_GNUTLS_SESSION: - CHECK_SIZE (void *); - if (NULL == connection->tls_cs) - return MHD_NO; - // return_value->tls_session = connection->tls_session; - return MHD_NO; // FIXME: to be implemented -#endif /* HTTPS_SUPPORT */ - case MHD_CONNECTION_INFORMATION_CLIENT_ADDRESS: - CHECK_SIZE (struct sockaddr *); - return_value->client_addr - = (const struct sockaddr *) &connection->addr; - return MHD_YES; - case MHD_CONNECTION_INFORMATION_DAEMON: - CHECK_SIZE (struct MHD_Daemon *); - return_value->daemon = connection->daemon; - return MHD_YES; - case MHD_CONNECTION_INFORMATION_CONNECTION_FD: - CHECK_SIZE (MHD_socket); - return_value->connect_fd = connection->socket_fd; - return MHD_YES; - case MHD_CONNECTION_INFORMATION_SOCKET_CONTEXT: - CHECK_SIZE (void **); - return_value->socket_context = &connection->socket_context; - return MHD_YES; - case MHD_CONNECTION_INFORMATION_CONNECTION_SUSPENDED: - CHECK_SIZE (enum MHD_Bool); - return_value->suspended - = connection->suspended ? MHD_YES : MHD_NO; - return MHD_YES; - case MHD_CONNECTION_INFORMATION_CONNECTION_TIMEOUT: - CHECK_SIZE (unsigned int); - return_value->connection_timeout - = (unsigned int) connection->connection_timeout; - return MHD_YES; - default: - return MHD_NO; - } - -#undef CHECK_SIZE -} - - -/* end of connection_info.c */ diff --git a/src/lib/connection_options.c b/src/lib/connection_options.c @@ -1,118 +0,0 @@ -/* - This file is part of libmicrohttpd - Copyright (C) 2007-2018 Daniel Pittman and Christian Grothoff - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. - - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with this library; if not, write to the Free Software - Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA -*/ - -/** - * @file lib/connection_options.c - * @brief functions to set per-connection options - * @author Christian Grothoff - */ -#include "internal.h" - - -/** - * Set custom timeout for the given connection. Specified as the - * number of seconds. Use zero for no timeout. Calling this function - * will reset timeout timer. - * - * @param connection connection to configure timeout for - * @param timeout_s new timeout in seconds - */ -void -MHD_connection_set_timeout (struct MHD_Connection *connection, - unsigned int timeout_s) -{ - struct MHD_Daemon *daemon = connection->daemon; - - connection->last_activity = MHD_monotonic_sec_counter (); - if (MHD_TM_THREAD_PER_CONNECTION == daemon->threading_mode) - { - /* Simple case, no need to lock to update DLLs */ - connection->connection_timeout = (time_t) timeout_s; - return; - } - - MHD_mutex_lock_chk_ (&daemon->cleanup_connection_mutex); - if (! connection->suspended) - { - if (connection->connection_timeout == - daemon->connection_default_timeout) - XDLL_remove (daemon->normal_timeout_head, - daemon->normal_timeout_tail, - connection); - else - XDLL_remove (daemon->manual_timeout_head, - daemon->manual_timeout_tail, - connection); - } - connection->connection_timeout = (time_t) timeout_s; - if (! connection->suspended) - { - if (connection->connection_timeout == - daemon->connection_default_timeout) - XDLL_insert (daemon->normal_timeout_head, - daemon->normal_timeout_tail, - connection); - else - XDLL_insert (daemon->manual_timeout_head, - daemon->manual_timeout_tail, - connection); - } - MHD_mutex_unlock_chk_ (&daemon->cleanup_connection_mutex); -} - - -/** - * Update the 'last_activity' field of the connection to the current - * time and move the connection to the head of the 'normal_timeout' - * list if the timeout for the connection uses the default value. - * - * @param connection the connection that saw some activity - */ -void -MHD_update_last_activity_ (struct MHD_Connection *connection) -{ - struct MHD_Daemon *daemon = connection->daemon; - - if (0 == connection->connection_timeout) - return; /* Skip update of activity for connections - without timeout timer. */ - if (connection->suspended) - return; /* no activity on suspended connections */ - - connection->last_activity = MHD_monotonic_sec_counter (); - if (MHD_TM_THREAD_PER_CONNECTION == daemon->threading_mode) - return; /* each connection has personal timeout */ - - if (connection->connection_timeout != - daemon->connection_default_timeout) - return; /* custom timeout, no need to move it in "normal" DLL */ - - MHD_mutex_lock_chk_ (&daemon->cleanup_connection_mutex); - /* move connection to head of timeout list (by remove + add operation) */ - XDLL_remove (daemon->normal_timeout_head, - daemon->normal_timeout_tail, - connection); - XDLL_insert (daemon->normal_timeout_head, - daemon->normal_timeout_tail, - connection); - MHD_mutex_unlock_chk_ (&daemon->cleanup_connection_mutex); -} - - -/* end of connection_options.c */ diff --git a/src/lib/connection_update_last_activity.c b/src/lib/connection_update_last_activity.c @@ -1,65 +0,0 @@ -/* - This file is part of libmicrohttpd - Copyright (C) 2007-2018 Daniel Pittman and Christian Grothoff - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. - - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with this library; if not, write to the Free Software - Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA -*/ -/** - * @file lib/connection_update_last_activity.c - * @brief functions to add connection to our active set - * @author Christian Grothoff - */ -#include "internal.h" -#include "connection_update_last_activity.h" - - -/** - * Update the 'last_activity' field of the connection to the current time - * and move the connection to the head of the 'normal_timeout' list if - * the timeout for the connection uses the default value. - * - * @param connection the connection that saw some activity - */ -void -MHD_connection_update_last_activity_ (struct MHD_Connection *connection) -{ - struct MHD_Daemon *daemon = connection->daemon; - - if (0 == connection->connection_timeout) - return; /* Skip update of activity for connections - without timeout timer. */ - if (connection->suspended) - return; /* no activity on suspended connections */ - - connection->last_activity = MHD_monotonic_sec_counter (); - if (MHD_TM_THREAD_PER_CONNECTION == daemon->threading_mode) - return; /* each connection has personal timeout */ - - if (connection->connection_timeout != daemon->connection_default_timeout) - return; /* custom timeout, no need to move it in "normal" DLL */ - - MHD_mutex_lock_chk_ (&daemon->cleanup_connection_mutex); - /* move connection to head of timeout list (by remove + add operation) */ - XDLL_remove (daemon->normal_timeout_head, - daemon->normal_timeout_tail, - connection); - XDLL_insert (daemon->normal_timeout_head, - daemon->normal_timeout_tail, - connection); - MHD_mutex_unlock_chk_ (&daemon->cleanup_connection_mutex); -} - - -/* end of connection_update_last_activity.c */ diff --git a/src/lib/connection_update_last_activity.h b/src/lib/connection_update_last_activity.h @@ -1,40 +0,0 @@ -/* - This file is part of libmicrohttpd - Copyright (C) 2007-2018 Daniel Pittman and Christian Grothoff - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. - - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with this library; if not, write to the Free Software - Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA -*/ -/** - * @file lib/connection_update_last_activity.h - * @brief function to update last activity of a connection - * @author Christian Grothoff - */ - -#ifndef CONNECTION_UPDATE_LAST_ACTIVITY_H -#define CONNECTION_UPDATE_LAST_ACTIVITY_H - - -/** - * Update the 'last_activity' field of the connection to the current time - * and move the connection to the head of the 'normal_timeout' list if - * the timeout for the connection uses the default value. - * - * @param connection the connection that saw some activity - */ -void -MHD_connection_update_last_activity_ (struct MHD_Connection *connection) -MHD_NONNULL (1); - -#endif diff --git a/src/lib/daemon_close_all_connections.c b/src/lib/daemon_close_all_connections.c @@ -1,237 +0,0 @@ -/* - This file is part of libmicrohttpd - Copyright (C) 2007-2018 Daniel Pittman and Christian Grothoff - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. - - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with this library; if not, write to the Free Software - Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA -*/ - -/** - * @file lib/daemon_close_all_connections.c - * @brief function to close all connections open at a daemon - * @author Christian Grothoff - */ -#include "internal.h" -#include "connection_cleanup.h" -#include "connection_close.h" -#include "connection_finish_forward.h" -#include "daemon_close_all_connections.h" -#include "request_resume.h" -#include "upgrade_process.h" - - -/** - * Close the given connection, remove it from all of its - * DLLs and move it into the cleanup queue. - * @remark To be called only from thread that - * process daemon's select()/poll()/etc. - * - * @param pos connection to move to cleanup - */ -static void -close_connection (struct MHD_Connection *pos) -{ - struct MHD_Daemon *daemon = pos->daemon; - - if (MHD_TM_THREAD_PER_CONNECTION == daemon->threading_mode) - { - MHD_connection_mark_closed_ (pos); - return; /* must let thread to do the rest */ - } - MHD_connection_close_ (pos, - MHD_REQUEST_TERMINATED_DAEMON_SHUTDOWN); - - MHD_mutex_lock_chk_ (&daemon->cleanup_connection_mutex); - - mhd_assert (! pos->suspended); - mhd_assert (! pos->resuming); - if (pos->connection_timeout == - pos->daemon->connection_default_timeout) - XDLL_remove (daemon->normal_timeout_head, - daemon->normal_timeout_tail, - pos); - else - XDLL_remove (daemon->manual_timeout_head, - daemon->manual_timeout_tail, - pos); - DLL_remove (daemon->connections_head, - daemon->connections_tail, - pos); - DLL_insert (daemon->cleanup_head, - daemon->cleanup_tail, - pos); - - MHD_mutex_unlock_chk_ (&daemon->cleanup_connection_mutex); -} - - -/** - * Close all connections for the daemon. Must only be called when - * MHD_Daemon::shutdown was set to true. - * - * @remark To be called only from thread that process daemon's - * select()/poll()/etc. - * - * @param daemon daemon to close down - */ -void -MHD_daemon_close_all_connections_ (struct MHD_Daemon *daemon) -{ - struct MHD_Connection *pos; - const bool used_thr_p_c = (MHD_TM_THREAD_PER_CONNECTION == - daemon->threading_mode); -#ifdef UPGRADE_SUPPORT - const bool upg_allowed = (! daemon->disallow_upgrade); -#endif /* UPGRADE_SUPPORT */ -#if defined(HTTPS_SUPPORT) && defined(UPGRADE_SUPPORT) - struct MHD_UpgradeResponseHandle *urh; - struct MHD_UpgradeResponseHandle *urhn; - const bool used_tls = (NULL != daemon->tls_api); - - mhd_assert (NULL == daemon->worker_pool); - mhd_assert (daemon->shutdown); - /* give upgraded HTTPS connections a chance to finish */ - /* 'daemon->urh_head' is not used in thread-per-connection mode. */ - for (urh = daemon->urh_tail; NULL != urh; urh = urhn) - { - urhn = urh->prev; - /* call generic forwarding function for passing data - with chance to detect that application is done. */ - MHD_upgrade_response_handle_process_ (urh); - MHD_connection_finish_forward_ (urh->connection); - urh->clean_ready = true; - /* Resuming will move connection to cleanup list. */ - MHD_request_resume (&urh->connection->request); - } -#endif /* HTTPS_SUPPORT && UPGRADE_SUPPORT */ - - /* Give suspended connections a chance to resume to avoid - running into the check for there not being any suspended - connections left in case of a tight race with a recently - resumed connection. */ - if (! daemon->disallow_suspend_resume) - { - daemon->resuming = true; /* Force check for pending resume. */ - MHD_resume_suspended_connections_ (daemon); - } - /* first, make sure all threads are aware of shutdown; need to - traverse DLLs in peace... */ - MHD_mutex_lock_chk_ (&daemon->cleanup_connection_mutex); -#ifdef UPGRADE_SUPPORT - if (upg_allowed) - { - struct MHD_Connection *susp; - - susp = daemon->suspended_connections_tail; - while (NULL != susp) - { - if (NULL == susp->request.urh) /* "Upgraded" connection? */ - MHD_PANIC (_ ( - "MHD_stop_daemon() called while we have suspended connections.\n")); -#ifdef HTTPS_SUPPORT - else if (used_tls && - used_thr_p_c && - (! susp->request.urh->clean_ready) ) - shutdown (susp->request.urh->app.socket, - SHUT_RDWR); /* Wake thread by shutdown of app socket. */ -#endif /* HTTPS_SUPPORT */ - else - { -#ifdef HAVE_MESSAGES - if (! susp->request.urh->was_closed) - MHD_DLOG (daemon, - MHD_SC_SHUTDOWN_WITH_OPEN_UPGRADED_CONNECTION, - _ ( - "Initiated daemon shutdown while \"upgraded\" connection was not closed.\n")); -#endif - susp->request.urh->was_closed = true; - /* If thread-per-connection is used, connection's thread - * may still processing "upgrade" (exiting). */ - if (! used_thr_p_c) - MHD_connection_finish_forward_ (susp); - /* Do not use MHD_resume_connection() as mutex is - * already locked. */ - susp->resuming = true; - daemon->resuming = true; - } - susp = susp->prev; - } - } - else /* This 'else' is combined with next 'if' */ -#endif /* UPGRADE_SUPPORT */ - if (NULL != daemon->suspended_connections_head) - MHD_PANIC (_ ( - "MHD_stop_daemon() called while we have suspended connections.\n")); - for (pos = daemon->connections_tail; NULL != pos; pos = pos->prev) - { - shutdown (pos->socket_fd, - SHUT_RDWR); -#if MHD_WINSOCK_SOCKETS - if ( (used_thr_p_c) && - (MHD_ITC_IS_VALID_ (daemon->itc)) && - (! MHD_itc_activate_ (daemon->itc, - "e")) ) - MHD_PANIC (_ ( - "Failed to signal shutdown via inter-thread communication channel.\n")); -#endif - } - - /* now, collect per-connection threads */ - if (used_thr_p_c) - { - pos = daemon->connections_tail; - while (NULL != pos) - { - if (! pos->thread_joined) - { - MHD_mutex_unlock_chk_ (&daemon->cleanup_connection_mutex); - if (! MHD_join_thread_ (pos->pid.handle)) - MHD_PANIC (_ ("Failed to join a thread.\n")); - MHD_mutex_lock_chk_ (&daemon->cleanup_connection_mutex); - pos->thread_joined = true; - /* The thread may have concurrently modified the DLL, - need to restart from the beginning */ - pos = daemon->connections_tail; - continue; - } - pos = pos->prev; - } - } - MHD_mutex_unlock_chk_ (&daemon->cleanup_connection_mutex); - -#ifdef UPGRADE_SUPPORT - /* Finished threads with "upgraded" connections need to be moved - * to cleanup list by resume_suspended_connections(). */ - /* "Upgraded" connections that were not closed explicitly by - * application should be moved to cleanup list too. */ - if (upg_allowed) - { - daemon->resuming = true; /* Force check for pending resume. */ - MHD_resume_suspended_connections_ (daemon); - } -#endif /* UPGRADE_SUPPORT */ - - /* now that we're alone, move everyone to cleanup */ - while (NULL != (pos = daemon->connections_tail)) - { - if ( (used_thr_p_c) && - (! pos->thread_joined) ) - MHD_PANIC (_ ("Failed to join a thread.\n")); - close_connection (pos); - } - MHD_connection_cleanup_ (daemon); -} - - -/* end of daemon_close_all_connections.c */ diff --git a/src/lib/daemon_close_all_connections.h b/src/lib/daemon_close_all_connections.h @@ -1,42 +0,0 @@ -/* - This file is part of libmicrohttpd - Copyright (C) 2007-2018 Daniel Pittman and Christian Grothoff - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. - - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with this library; if not, write to the Free Software - Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA -*/ - -/** - * @file lib/daemon_close_all_connections.h - * @brief function to close all connections open at a daemon - * @author Christian Grothoff - */ -#ifndef DAEMON_CLOSE_ALL_CONNECTIONS_H -#define DAEMON_CLOSE_ALL_CONNECTIONS_H - - -/** - * Close all connections for the daemon. Must only be called when - * MHD_Daemon::shutdown was set to true. - * - * @remark To be called only from thread that process daemon's - * select()/poll()/etc. - * - * @param daemon daemon to close down - */ -void -MHD_daemon_close_all_connections_ (struct MHD_Daemon *daemon) -MHD_NONNULL (1); - -#endif diff --git a/src/lib/daemon_create.c b/src/lib/daemon_create.c @@ -1,138 +0,0 @@ -/* - This file is part of libmicrohttpd - Copyright (C) 2007-2018 Daniel Pittman and Christian Grothoff - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. - - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with this library; if not, write to the Free Software - Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA -*/ - -/** - * @file lib/daemon_create.c - * @brief main functions to create a daemon - * @author Christian Grothoff - */ -#include "internal.h" -#include "init.h" - - -/** - * Logging implementation that logs to a file given - * as the @a cls. - * - * @param cls a `FILE *` to log to - * @param sc status code of the event (ignored) - * @param fm format string (`printf()`-style) - * @param ap arguments to @a fm - * @ingroup logging - */ -static void -file_logger (void *cls, - enum MHD_StatusCode sc, - const char *fm, - va_list ap) -{ - FILE *f = cls; - - (void) sc; - (void) vfprintf (f, - fm, - ap); -} - - -/** - * Process escape sequences ('%HH') Updates val in place; the - * result should be UTF-8 encoded and cannot be larger than the input. - * The result must also still be 0-terminated. - * - * @param cls closure (use NULL) - * @param req handle to request, not used - * @param val value to unescape (modified in the process) - * @return length of the resulting val (strlen(val) maybe - * shorter afterwards due to elimination of escape sequences) - */ -static size_t -unescape_wrapper (void *cls, - struct MHD_Request *req, - char *val) -{ - (void) cls; /* Mute compiler warning. */ - (void) req; /* Mute compiler warning. */ - return MHD_http_unescape (val); -} - - -/** - * Create (but do not yet start) an MHD daemon. - * Usually, you will want to set various options before - * starting the daemon with #MHD_daemon_start(). - * - * @param cb function to be called for incoming requests - * @param cb_cls closure for @a cb - * @return NULL on error - */ -struct MHD_Daemon * -MHD_daemon_create (MHD_RequestCallback cb, - void *cb_cls) -{ - struct MHD_Daemon *daemon; - - MHD_check_global_init_ (); - if (NULL == (daemon = malloc (sizeof (struct MHD_Daemon)))) - return NULL; - memset (daemon, - 0, - sizeof (struct MHD_Daemon)); -#ifdef EPOLL_SUPPORT - daemon->epoll_itc_marker = "itc_marker"; -#endif - daemon->rc = cb; - daemon->rc_cls = cb_cls; - daemon->logger = &file_logger; - daemon->logger_cls = stderr; - daemon->unescape_cb = &unescape_wrapper; - daemon->connection_memory_limit_b = POOL_SIZE_DEFAULT; - daemon->connection_memory_increment_b = BUF_INC_SIZE_DEFAULT; -#if ENABLE_DAUTH - daemon->digest_nc_length = DIGEST_NC_LENGTH_DEFAULT; -#endif - daemon->listen_backlog = LISTEN_BACKLOG_DEFAULT; - daemon->fo_queue_length = FO_QUEUE_LENGTH_DEFAULT; - daemon->listen_socket = MHD_INVALID_SOCKET; - - if (! MHD_mutex_init_ (&daemon->cleanup_connection_mutex)) - { - free (daemon); - return NULL; - } - if (! MHD_mutex_init_ (&daemon->per_ip_connection_mutex)) - { - (void) MHD_mutex_destroy_ (&daemon->cleanup_connection_mutex); - free (daemon); - return NULL; - } -#ifdef DAUTH_SUPPORT - if (! MHD_mutex_init_ (&daemon->nnc_lock)) - { - (void) MHD_mutex_destroy_ (&daemon->cleanup_connection_mutex); - (void) MHD_mutex_destroy_ (&daemon->per_ip_connection_mutex); - free (daemon); - return NULL; - } -#endif - return daemon; -} - - -/* end of daemon_create.c */ diff --git a/src/lib/daemon_destroy.c b/src/lib/daemon_destroy.c @@ -1,203 +0,0 @@ -/* - This file is part of libmicrohttpd - Copyright (C) 2007-2018 Daniel Pittman and Christian Grothoff - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. - - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with this library; if not, write to the Free Software - Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA -*/ - -/** - * @file lib/daemon_destroy.c - * @brief main functions to destroy a daemon - * @author Christian Grothoff - */ -#include "internal.h" -#include "request_resume.h" -#include "daemon_close_all_connections.h" - - -/** - * Stop all worker threads from the worker pool. - * - * @param daemon master daemon controlling the workers - */ -static void -stop_workers (struct MHD_Daemon *daemon) -{ - MHD_socket fd; - unsigned int i; - - mhd_assert (1 < daemon->worker_pool_size); - mhd_assert (1 < daemon->threading_mode); - if (daemon->was_quiesced) - fd = MHD_INVALID_SOCKET; /* Do not use FD if daemon was quiesced */ - else - fd = daemon->listen_socket; - /* Let workers shutdown in parallel. */ - for (i = 0; i < daemon->worker_pool_size; i++) - { - daemon->worker_pool[i].shutdown = true; - if (MHD_ITC_IS_VALID_ (daemon->worker_pool[i].itc)) - { - if (! MHD_itc_activate_ (daemon->worker_pool[i].itc, - "e")) - MHD_PANIC (_ ( - "Failed to signal shutdown via inter-thread communication channel.\n")); - } - else - { - /* Better hope shutdown() works... */ - mhd_assert (MHD_INVALID_SOCKET != fd); - } - } -#ifdef HAVE_LISTEN_SHUTDOWN - if (MHD_INVALID_SOCKET != fd) - { - (void) shutdown (fd, - SHUT_RDWR); - } -#endif /* HAVE_LISTEN_SHUTDOWN */ - for (i = 0; i < daemon->worker_pool_size; ++i) - { - MHD_daemon_destroy (&daemon->worker_pool[i]); - } - free (daemon->worker_pool); - daemon->worker_pool = NULL; - /* FIXME: does this still hold? */ - mhd_assert (MHD_ITC_IS_INVALID_ (daemon->itc)); -#ifdef EPOLL_SUPPORT - mhd_assert (-1 == daemon->epoll_fd); -#if defined(HTTPS_SUPPORT) && defined(UPGRADE_SUPPORT) - mhd_assert (-1 == daemon->epoll_upgrade_fd); -#endif /* HTTPS_SUPPORT && UPGRADE_SUPPORT */ -#endif /* EPOLL_SUPPORT */ -} - - -/** - * Shutdown and destroy an HTTP daemon. - * - * @param daemon daemon to stop - * @ingroup event - */ -void -MHD_daemon_destroy (struct MHD_Daemon *daemon) -{ - MHD_socket fd; - - daemon->shutdown = true; - if (daemon->was_quiesced) - fd = MHD_INVALID_SOCKET; /* Do not use FD if daemon was quiesced */ - else - fd = daemon->listen_socket; - - /* FIXME: convert from here to microhttpd2-style API! */ - - if (NULL != daemon->worker_pool) - { /* Master daemon with worker pool. */ - stop_workers (daemon); - } - else - { - mhd_assert (0 == daemon->worker_pool_size); - /* Worker daemon or single-thread daemon. */ - if (MHD_TM_EXTERNAL_EVENT_LOOP != daemon->threading_mode) - { - /* Worker daemon or single daemon with internal thread(s). */ - /* Separate thread(s) is used for polling sockets. */ - if (MHD_ITC_IS_VALID_ (daemon->itc)) - { - if (! MHD_itc_activate_ (daemon->itc, - "e")) - MHD_PANIC (_ ( - "Failed to signal shutdown via inter-thread communication channel.\n")); - } - else - { -#ifdef HAVE_LISTEN_SHUTDOWN - if (MHD_INVALID_SOCKET != fd) - { - if (NULL == daemon->master) - (void) shutdown (fd, - SHUT_RDWR); - } - else -#endif /* HAVE_LISTEN_SHUTDOWN */ - mhd_assert (false); /* Should never happen */ - } - - if (! MHD_join_thread_ (daemon->pid.handle)) - { - MHD_PANIC (_ ("Failed to join a thread.\n")); - } - /* close_all_connections() was called in daemon thread. */ - } - else - { - /* No internal threads are used for polling sockets - (external event loop) */ - MHD_daemon_close_all_connections_ (daemon); - } - if (MHD_ITC_IS_VALID_ (daemon->itc)) - MHD_itc_destroy_chk_ (daemon->itc); - -#ifdef EPOLL_SUPPORT - if ( (MHD_ELS_EPOLL == daemon->event_loop_syscall) && - (-1 != daemon->epoll_fd) ) - MHD_socket_close_chk_ (daemon->epoll_fd); -#if defined(HTTPS_SUPPORT) && defined(UPGRADE_SUPPORT) - if ( (MHD_ELS_EPOLL == daemon->event_loop_syscall) && - (-1 != daemon->epoll_upgrade_fd) ) - MHD_socket_close_chk_ (daemon->epoll_upgrade_fd); -#endif /* HTTPS_SUPPORT && UPGRADE_SUPPORT */ -#endif /* EPOLL_SUPPORT */ - - MHD_mutex_destroy_chk_ (&daemon->cleanup_connection_mutex); - } - - if (NULL != daemon->master) - return; - /* Cleanup that should be done only one time in master/single daemon. - * Do not perform this cleanup in worker daemons. */ - - if (MHD_INVALID_SOCKET != fd) - MHD_socket_close_chk_ (fd); - - /* TLS clean up */ -#ifdef HTTPS_SUPPORT - if (NULL != daemon->tls_api) - { -#if FIXME_TLS_API - if (daemon->have_dhparams) - { - gnutls_dh_params_deinit (daemon->https_mem_dhparams); - daemon->have_dhparams = false; - } - gnutls_priority_deinit (daemon->priority_cache); - if (daemon->x509_cred) - gnutls_certificate_free_credentials (daemon->x509_cred); -#endif - } -#endif /* HTTPS_SUPPORT */ - -#ifdef DAUTH_SUPPORT - free (daemon->nnc); - MHD_mutex_destroy_chk_ (&daemon->nnc_lock); -#endif - MHD_mutex_destroy_chk_ (&daemon->per_ip_connection_mutex); - free (daemon); -} - - -/* end of daemon_destroy.c */ diff --git a/src/lib/daemon_epoll.c b/src/lib/daemon_epoll.c @@ -1,517 +0,0 @@ -/* - This file is part of libmicrohttpd - Copyright (C) 2007-2018 Daniel Pittman and Christian Grothoff - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. - - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with this library; if not, write to the Free Software - Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA -*/ - -/** - * @file lib/daemon_epoll.c - * @brief functions to run epoll()-based event loop - * @author Christian Grothoff - */ -#include "internal.h" -#include "connection_add.h" -#include "connection_call_handlers.h" -#include "connection_finish_forward.h" -#include "daemon_epoll.h" -#include "upgrade_process.h" -#include "request_resume.h" - -#ifdef EPOLL_SUPPORT - -/** - * How many events to we process at most per epoll() call? Trade-off - * between required stack-size and number of system calls we have to - * make; 128 should be way enough to avoid more than one system call - * for most scenarios, and still be moderate in stack size - * consumption. Embedded systems might want to choose a smaller value - * --- but why use epoll() on such a system in the first place? - */ -#define MAX_EVENTS 128 - - -#if defined(HTTPS_SUPPORT) && defined(UPGRADE_SUPPORT) - -/** - * Checks whether @a urh has some data to process. - * - * @param urh upgrade handler to analyse - * @return 'true' if @a urh has some data to process, - * 'false' otherwise - */ -static bool -is_urh_ready (struct MHD_UpgradeResponseHandle *const urh) -{ - const struct MHD_Connection *const connection = urh->connection; - - if ( (0 == urh->in_buffer_size) && - (0 == urh->out_buffer_size) && - (0 == urh->in_buffer_used) && - (0 == urh->out_buffer_used) ) - return false; - - if (connection->daemon->shutdown) - return true; - - if ( ( (0 != (MHD_EPOLL_STATE_READ_READY & urh->app.celi)) || - (connection->tls_read_ready) ) && - (urh->in_buffer_used < urh->in_buffer_size) ) - return true; - - if ( (0 != (MHD_EPOLL_STATE_READ_READY & urh->mhd.celi)) && - (urh->out_buffer_used < urh->out_buffer_size) ) - return true; - - if ( (0 != (MHD_EPOLL_STATE_WRITE_READY & urh->app.celi)) && - (urh->out_buffer_used > 0) ) - return true; - - if ( (0 != (MHD_EPOLL_STATE_WRITE_READY & urh->mhd.celi)) && - (urh->in_buffer_used > 0) ) - return true; - - return false; -} - - -/** - * Do epoll()-based processing for TLS connections that have been - * upgraded. This requires a separate epoll() invocation as we - * cannot use the `struct MHD_Connection` data structures for - * the `union epoll_data` in this case. - * @remark To be called only from thread that process - * daemon's select()/poll()/etc. - * - * @param daemon the daemmon for which we process connections - * @return #MHD_SC_OK on success - */ -static enum MHD_StatusCode -run_epoll_for_upgrade (struct MHD_Daemon *daemon) -{ - struct epoll_event events[MAX_EVENTS]; - int num_events; - struct MHD_UpgradeResponseHandle *pos; - struct MHD_UpgradeResponseHandle *prev; - - num_events = MAX_EVENTS; - while (MAX_EVENTS == num_events) - { - unsigned int i; - - /* update event masks */ - num_events = epoll_wait (daemon->epoll_upgrade_fd, - events, - MAX_EVENTS, - 0); - if (-1 == num_events) - { - const int err = MHD_socket_get_error_ (); - if (MHD_SCKT_ERR_IS_EINTR_ (err)) - return MHD_SC_OK; -#ifdef HAVE_MESSAGES - MHD_DLOG (daemon, - MHD_SC_UNEXPECTED_EPOLL_WAIT_ERROR, - _ ("Call to epoll_wait failed: %s\n"), - MHD_socket_strerr_ (err)); -#endif - return MHD_SC_UNEXPECTED_EPOLL_WAIT_ERROR; - } - 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; - - if (urh->clean_ready) - continue; - - /* Update ueh state based on what is ready according to epoll() */ - if (0 != (events[i].events & EPOLLIN)) - ueh->celi |= MHD_EPOLL_STATE_READ_READY; - if (0 != (events[i].events & EPOLLOUT)) - ueh->celi |= MHD_EPOLL_STATE_WRITE_READY; - if (0 != (events[i].events & EPOLLHUP)) - ueh->celi |= MHD_EPOLL_STATE_READ_READY | MHD_EPOLL_STATE_WRITE_READY; - - 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; - } - } - } - } - prev = daemon->eready_urh_tail; - while (NULL != (pos = prev)) - { - prev = pos->prevE; - MHD_upgrade_response_handle_process_ (pos); - if (! is_urh_ready (pos)) - { - EDLL_remove (daemon->eready_urh_head, - daemon->eready_urh_tail, - pos); - pos->in_eready_list = false; - } - /* Finished forwarding? */ - if ( (0 == pos->in_buffer_size) && - (0 == pos->out_buffer_size) && - (0 == pos->in_buffer_used) && - (0 == pos->out_buffer_used) ) - { - MHD_connection_finish_forward_ (pos->connection); - pos->clean_ready = true; - /* If 'pos->was_closed' already was set to true, connection - * 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_request_resume (&pos->connection->request); - } - } - - return MHD_SC_OK; -} - - -#endif /* HTTPS_SUPPORT && UPGRADE_SUPPORT */ - - -/** - * Do epoll()-based processing (this function is allowed to - * block if @a may_block is set to #MHD_YES). - * - * @param daemon daemon to run poll loop for - * @param may_block true if blocking, false if non-blocking - * @return #MHD_SC_OK on success - */ -enum MHD_StatusCode -MHD_daemon_epoll_ (struct MHD_Daemon *daemon, - bool may_block) -{ -#if defined(HTTPS_SUPPORT) && defined(UPGRADE_SUPPORT) - static const char *const upgrade_marker = "upgrade_ptr"; -#endif /* HTTPS_SUPPORT && UPGRADE_SUPPORT */ - struct MHD_Connection *pos; - struct MHD_Connection *prev; - struct epoll_event events[MAX_EVENTS]; - struct epoll_event event; - int timeout_ms; - MHD_UNSIGNED_LONG_LONG timeout_ll; - int num_events; - unsigned int i; - MHD_socket ls; -#if defined(HTTPS_SUPPORT) && defined(UPGRADE_SUPPORT) - bool run_upgraded = false; -#endif /* HTTPS_SUPPORT && UPGRADE_SUPPORT */ - - if (-1 == daemon->epoll_fd) - return MHD_SC_EPOLL_FD_INVALID; /* we're down! */ - if (daemon->shutdown) - return MHD_SC_DAEMON_ALREADY_SHUTDOWN; - if ( (MHD_INVALID_SOCKET != (ls = daemon->listen_socket)) && - (! daemon->was_quiesced) && - (daemon->connections < daemon->global_connection_limit) && - (! daemon->listen_socket_in_epoll) && - (! daemon->at_limit) ) - { - event.events = EPOLLIN; - event.data.ptr = daemon; - if (0 != epoll_ctl (daemon->epoll_fd, - EPOLL_CTL_ADD, - ls, - &event)) - { -#ifdef HAVE_MESSAGES - MHD_DLOG (daemon, - MHD_SC_EPOLL_CTL_ADD_FAILED, - _ ("Call to epoll_ctl failed: %s\n"), - MHD_socket_last_strerr_ ()); -#endif - return MHD_SC_EPOLL_CTL_ADD_FAILED; - } - 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)) - 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) ) - { - 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)) - { -#ifdef HAVE_MESSAGES - MHD_DLOG (daemon, - MHD_SC_EPOLL_CTL_ADD_FAILED, - _ ("Call to epoll_ctl failed: %s\n"), - MHD_socket_last_strerr_ ()); -#endif - return MHD_SC_EPOLL_CTL_ADD_FAILED; - } - daemon->upgrade_fd_in_epoll = true; - } -#endif /* HTTPS_SUPPORT && UPGRADE_SUPPORT */ - if ( (daemon->listen_socket_in_epoll) && - ( (daemon->connections == daemon->global_connection_limit) || - (daemon->at_limit) || - (daemon->was_quiesced) ) ) - { - /* 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")); - daemon->listen_socket_in_epoll = false; - } - - if ( (! daemon->disallow_suspend_resume) && - (MHD_resume_suspended_connections_ (daemon)) ) - may_block = false; - - if (may_block) - { - if (MHD_SC_OK == /* FIXME: distinguish between NO_TIMEOUT and errors */ - MHD_daemon_get_timeout (daemon, - &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; - } - else - timeout_ms = 0; - - /* Reset. New value will be set when connections are processed. */ - /* Note: Used mostly for uniformity here as same situation is - * signaled in epoll mode by non-empty eready DLL. */ - daemon->data_already_pending = false; - - /* drain 'epoll' event queue; need to iterate as we get at most - MAX_EVENTS in one system call here; in practice this should - pretty much mean only one round, but better an extra loop here - than unfair behavior... */ - num_events = MAX_EVENTS; - while (MAX_EVENTS == num_events) - { - /* update event masks */ - num_events = epoll_wait (daemon->epoll_fd, - 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_SC_OK; -#ifdef HAVE_MESSAGES - MHD_DLOG (daemon, - MHD_SC_UNEXPECTED_EPOLL_WAIT_ERROR, - _ ("Call to epoll_wait failed: %s\n"), - MHD_socket_strerr_ (err)); -#endif - return MHD_SC_UNEXPECTED_EPOLL_WAIT_ERROR; - } - 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 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 = true; - continue; - } -#endif /* HTTPS_SUPPORT && UPGRADE_SUPPORT */ - if (daemon->epoll_itc_marker == events[i].data.ptr) - { - /* It's OK to clear ITC here as all external - conditions will be processed later. */ - MHD_itc_clear_ (daemon->itc); - continue; - } - 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))) - { - unsigned int series_length = 0; - /* Run 'accept' until it fails or daemon at limit of connections. - * Do not accept more then 10 connections at once. The rest will - * be accepted on next turn (level trigger is used for listen - * socket). */ - while ( (MHD_SC_OK == - MHD_accept_connection_ (daemon)) && - (series_length < 10) && - (daemon->connections < daemon->global_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'. */ - pos = events[i].data.ptr; - /* normal processing: update read/write data */ - if (0 != (events[i].events & (EPOLLPRI | EPOLLERR | EPOLLHUP))) - { - pos->epoll_state |= MHD_EPOLL_STATE_ERROR; - if (0 == (pos->epoll_state & MHD_EPOLL_STATE_IN_EREADY_EDLL)) - { - EDLL_insert (daemon->eready_head, - daemon->eready_tail, - pos); - pos->epoll_state |= MHD_EPOLL_STATE_IN_EREADY_EDLL; - } - } - else - { - if (0 != (events[i].events & EPOLLIN)) - { - pos->epoll_state |= MHD_EPOLL_STATE_READ_READY; - if ( ( (MHD_EVENT_LOOP_INFO_READ == pos->request.event_loop_info) || - (pos->request.read_buffer_size > - pos->request.read_buffer_offset) ) && - (0 == (pos->epoll_state & MHD_EPOLL_STATE_IN_EREADY_EDLL) ) ) - { - EDLL_insert (daemon->eready_head, - daemon->eready_tail, - pos); - pos->epoll_state |= MHD_EPOLL_STATE_IN_EREADY_EDLL; - } - } - if (0 != (events[i].events & EPOLLOUT)) - { - pos->epoll_state |= MHD_EPOLL_STATE_WRITE_READY; - if ( (MHD_EVENT_LOOP_INFO_WRITE == pos->request.event_loop_info) && - (0 == (pos->epoll_state & MHD_EPOLL_STATE_IN_EREADY_EDLL) ) ) - { - EDLL_insert (daemon->eready_head, - daemon->eready_tail, - pos); - pos->epoll_state |= MHD_EPOLL_STATE_IN_EREADY_EDLL; - } - } - } - } - } - -#if defined(HTTPS_SUPPORT) && defined(UPGRADE_SUPPORT) - if (run_upgraded) - run_epoll_for_upgrade (daemon); /* FIXME: return value? */ -#endif /* HTTPS_SUPPORT && UPGRADE_SUPPORT */ - - /* process events for connections */ - prev = daemon->eready_tail; - while (NULL != (pos = prev)) - { - prev = pos->prevE; - MHD_connection_call_handlers_ (pos, - 0 != (pos->epoll_state - & MHD_EPOLL_STATE_READ_READY), - 0 != (pos->epoll_state - & MHD_EPOLL_STATE_WRITE_READY), - 0 != (pos->epoll_state - & MHD_EPOLL_STATE_ERROR)); - if (MHD_EPOLL_STATE_IN_EREADY_EDLL == - (pos->epoll_state & (MHD_EPOLL_STATE_SUSPENDED - | MHD_EPOLL_STATE_IN_EREADY_EDLL))) - { - if ( ((MHD_EVENT_LOOP_INFO_READ == pos->request.event_loop_info) && - (0 == (pos->epoll_state & MHD_EPOLL_STATE_READ_READY)) ) || - ((MHD_EVENT_LOOP_INFO_WRITE == pos->request.event_loop_info) && - (0 == (pos->epoll_state & MHD_EPOLL_STATE_WRITE_READY)) ) || - (MHD_EVENT_LOOP_INFO_CLEANUP == pos->request.event_loop_info) ) - { - EDLL_remove (daemon->eready_head, - daemon->eready_tail, - pos); - pos->epoll_state &= ~MHD_EPOLL_STATE_IN_EREADY_EDLL; - } - } - } - - /* Finally, handle timed-out connections; we need to do this here - as the epoll mechanism won't call the 'MHD_request_handle_idle_()' on everything, - as the other event loops do. As timeouts do not get an explicit - event, we need to find those connections that might have timed out - here. - - Connections with custom timeouts must all be looked at, as we - do not bother to sort that (presumably very short) list. */prev = daemon->manual_timeout_tail; - while (NULL != (pos = prev)) - { - prev = pos->prevX; - MHD_request_handle_idle_ (&pos->request); - } - /* Connections with the default timeout are sorted by prepending - them to the head of the list whenever we touch the connection; - thus it suffices to iterate from the tail until the first - connection is NOT timed out */ - prev = daemon->normal_timeout_tail; - while (NULL != (pos = prev)) - { - prev = pos->prevX; - MHD_request_handle_idle_ (&pos->request); - if (MHD_REQUEST_CLOSED != pos->request.state) - break; /* sorted by timeout, no need to visit the rest! */ - } - return MHD_SC_OK; -} - - -#endif - -/* end of daemon_epoll.c */ diff --git a/src/lib/daemon_epoll.h b/src/lib/daemon_epoll.h @@ -1,46 +0,0 @@ -/* - This file is part of libmicrohttpd - Copyright (C) 2007-2018 Daniel Pittman and Christian Grothoff - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. - - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with this library; if not, write to the Free Software - Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA -*/ - -/** - * @file lib/daemon_epoll.h - * @brief non-public functions provided by daemon_epoll.c - * @author Christian Grothoff - */ - -#ifndef DAEMON_EPOLL_H -#define DAEMON_EPOLL_H - -#ifdef EPOLL_SUPPORT - -/** - * Do epoll()-based processing (this function is allowed to - * block if @a may_block is set to #MHD_YES). - * - * @param daemon daemon to run poll loop for - * @param may_block true if blocking, false if non-blocking - * @return #MHD_SC_OK on success - */ -enum MHD_StatusCode -MHD_daemon_epoll_ (struct MHD_Daemon *daemon, - bool may_block) -MHD_NONNULL (1); - -#endif - -#endif diff --git a/src/lib/daemon_get_timeout.c b/src/lib/daemon_get_timeout.c @@ -1,127 +0,0 @@ -/* - This file is part of libmicrohttpd - Copyright (C) 2007-2018 Daniel Pittman and Christian Grothoff - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. - - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with this library; if not, write to the Free Software - Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA -*/ - -/** - * @file lib/daemon_get_timeout.c - * @brief function to obtain timeout for event loop - * @author Christian Grothoff - */ -#include "internal.h" - - -/** - * Obtain timeout value for polling function for this daemon. - * This function set value to amount of milliseconds for which polling - * function (`select()` or `poll()`) should at most block, not the - * timeout value set for connections. - * It is important to always use this function, even if connection - * timeout is not set, as in some cases MHD may already have more - * data to process on next turn (data pending in TLS buffers, - * connections are already ready with epoll etc.) and returned timeout - * will be zero. - * - * @param daemon daemon to query for timeout - * @param timeout set to the timeout (in milliseconds) - * @return #MHD_SC_OK on success, #MHD_SC_NO_TIMEOUT if timeouts are - * not used (or no connections exist that would - * necessitate the use of a timeout right now), otherwise - * an error code - * @ingroup event - */ -enum MHD_StatusCode -MHD_daemon_get_timeout (struct MHD_Daemon *daemon, - MHD_UNSIGNED_LONG_LONG *timeout) -{ - time_t earliest_deadline; - time_t now; - struct MHD_Connection *pos; - bool have_timeout; - - if (MHD_TM_EXTERNAL_EVENT_LOOP != daemon->threading_mode) - { -#ifdef HAVE_MESSAGES - MHD_DLOG (daemon, - MHD_SC_CONFIGURATION_MISMATCH_FOR_GET_TIMEOUT, - _ ("Illegal call to MHD_get_timeout.\n")); -#endif - return MHD_SC_CONFIGURATION_MISMATCH_FOR_GET_TIMEOUT; - } - - if (daemon->data_already_pending) - { - /* Some data already waiting to be processed. */ - *timeout = 0; - return MHD_SC_OK; - } - -#ifdef EPOLL_SUPPORT - if ( (MHD_ELS_EPOLL == daemon->event_loop_syscall) && - ((NULL != daemon->eready_head) -#if defined(UPGRADE_SUPPORT) && defined(HTTPS_SUPPORT) - || (NULL != daemon->eready_urh_head) -#endif /* UPGRADE_SUPPORT && HTTPS_SUPPORT */ - ) ) - { - /* Some connection(s) already have some data pending. */ - *timeout = 0; - return MHD_SC_OK; - } -#endif /* EPOLL_SUPPORT */ - - have_timeout = false; - earliest_deadline = 0; /* avoid compiler warnings */ - for (pos = daemon->manual_timeout_tail; NULL != pos; pos = pos->prevX) - { - if (0 != pos->connection_timeout) - { - if ( (! have_timeout) || - (earliest_deadline - pos->last_activity > pos->connection_timeout) ) - earliest_deadline = pos->last_activity + pos->connection_timeout; - have_timeout = true; - } - } - /* normal timeouts are sorted, so we only need to look at the 'tail' (oldest) */ - pos = daemon->normal_timeout_tail; - if ( (NULL != pos) && - (0 != pos->connection_timeout) ) - { - if ( (! have_timeout) || - (earliest_deadline - pos->connection_timeout > pos->last_activity) ) - earliest_deadline = pos->last_activity + pos->connection_timeout; - have_timeout = true; - } - - if (! have_timeout) - return MHD_SC_NO_TIMEOUT; - now = MHD_monotonic_sec_counter (); - if (earliest_deadline < now) - *timeout = 0; - else - { - const time_t second_left = earliest_deadline - now; - if (second_left > ULLONG_MAX / 1000) /* Ignore compiler warning: 'second_left' is always positive. */ - *timeout = ULLONG_MAX; - else - *timeout = 1000LL * second_left; - } - return MHD_SC_OK; -} - - -/* end of daemon_get_timeout.c */ diff --git a/src/lib/daemon_info.c b/src/lib/daemon_info.c @@ -1,106 +0,0 @@ -/* - This file is part of libmicrohttpd - Copyright (C) 2007-2018 Daniel Pittman and Christian Grothoff - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. - - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with this library; if not, write to the Free Software - Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA -*/ - -/** - * @file lib/daemon_info.c - * @brief implementation of MHD_daemon_get_information_sz() - * @author Christian Grothoff - */ -#include "internal.h" -#include "connection_cleanup.h" - - -/** - * Obtain information about the given daemon. - * Use wrapper macro #MHD_daemon_get_information() instead of direct use - * of this function. - * - * @param daemon what daemon to get information about - * @param info_type what information is desired? - * @param[out] return_value pointer to union where requested information will - * be stored - * @param return_value_size size of union MHD_DaemonInformation at compile - * time - * @return #MHD_YES on success, #MHD_NO on error - * (@a info_type is unknown, NULL pointer etc.) - * @ingroup specialized - */ -enum MHD_Bool -MHD_daemon_get_information_sz (struct MHD_Daemon *daemon, - enum MHD_DaemonInformationType info_type, - union MHD_DaemonInformation *return_value, - size_t return_value_size) -{ -#define CHECK_SIZE(type) if (sizeof(type) < return_value_size) \ - return MHD_NO - - switch (info_type) - { - case MHD_DAEMON_INFORMATION_LISTEN_SOCKET: - CHECK_SIZE (MHD_socket); - return_value->listen_socket - = daemon->listen_socket; - return MHD_YES; -#ifdef EPOLL_SUPPORT - case MHD_DAEMON_INFORMATION_EPOLL_FD: - CHECK_SIZE (int); - // FIXME: maybe return MHD_NO if we are not using EPOLL? - return_value->epoll_fd = daemon->epoll_fd; - return MHD_YES; -#endif - case MHD_DAEMON_INFORMATION_CURRENT_CONNECTIONS: - CHECK_SIZE (unsigned int); - if (MHD_TM_EXTERNAL_EVENT_LOOP == daemon->threading_mode) - { - /* Assumes that MHD_run() in not called in other thread - (of the application) at the same time. */ - MHD_connection_cleanup_ (daemon); - return_value->num_connections - = daemon->connections; - } - else if (daemon->worker_pool) - { - unsigned int i; - /* Collect the connection information stored in the workers. */ - return_value->num_connections = 0; - for (i = 0; i < daemon->worker_pool_size; i++) - { - /* FIXME: next line is thread-safe only if read is atomic. */ - return_value->num_connections - += daemon->worker_pool[i].connections; - } - } - else - return_value->num_connections - = daemon->connections; - return MHD_YES; - case MHD_DAEMON_INFORMATION_BIND_PORT: - CHECK_SIZE (uint16_t); - // FIXME: return MHD_NO if port is not known/UNIX? - return_value->port = daemon->listen_port; - return MHD_YES; - default: - return MHD_NO; - } - -#undef CHECK_SIZE -} - - -/* end of daemon_info.c */ diff --git a/src/lib/daemon_ip_limit.c b/src/lib/daemon_ip_limit.c @@ -1,303 +0,0 @@ -/* - This file is part of libmicrohttpd - Copyright (C) 2007-2018 Daniel Pittman and Christian Grothoff - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. - - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with this library; if not, write to the Free Software - Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA -*/ - -/** - * @file lib/daemon_ip_limit.c - * @brief counting of connections per IP - * @author Christian Grothoff - */ -#include "internal.h" -#include "daemon_ip_limit.h" -#ifdef HAVE_SEARCH_H -#include <search.h> -#else -#include "tsearch.h" -#endif - - -/** - * Maintain connection count for single address. - */ -struct MHD_IPCount -{ - /** - * Address family. AF_INET or AF_INET6 for now. - */ - int family; - - /** - * Actual address. - */ - union - { - /** - * IPv4 address. - */ - struct in_addr ipv4; -#ifdef HAVE_INET6 - /** - * IPv6 address. - */ - struct in6_addr ipv6; -#endif - } addr; - - /** - * Counter. - */ - unsigned int count; -}; - - -/** - * Trace up to and return master daemon. If the supplied daemon - * is a master, then return the daemon itself. - * - * @param daemon handle to a daemon - * @return master daemon handle - */ -static struct MHD_Daemon * -get_master (struct MHD_Daemon *daemon) -{ - while (NULL != daemon->master) - daemon = daemon->master; - return daemon; -} - - -/** - * Lock shared structure for IP connection counts and connection DLLs. - * - * @param daemon handle to daemon where lock is - */ -static void -MHD_ip_count_lock (struct MHD_Daemon *daemon) -{ - MHD_mutex_lock_chk_ (&daemon->per_ip_connection_mutex); -} - - -/** - * Unlock shared structure for IP connection counts and connection DLLs. - * - * @param daemon handle to daemon where lock is - */ -static void -MHD_ip_count_unlock (struct MHD_Daemon *daemon) -{ - MHD_mutex_unlock_chk_ (&daemon->per_ip_connection_mutex); -} - - -/** - * Tree comparison function for IP addresses (supplied to tsearch() family). - * We compare everything in the struct up through the beginning of the - * 'count' field. - * - * @param a1 first address to compare - * @param a2 second address to compare - * @return -1, 0 or 1 depending on result of compare - */ -static int -MHD_ip_addr_compare (const void *a1, - const void *a2) -{ - return memcmp (a1, - a2, - offsetof (struct MHD_IPCount, - count)); -} - - -/** - * Parse address and initialize @a key using the address. - * - * @param addr address to parse - * @param addrlen number of bytes in @a addr - * @param key where to store the parsed address - * @return #MHD_YES on success and #MHD_NO otherwise (e.g., invalid address type) - */ -static int -MHD_ip_addr_to_key (const struct sockaddr *addr, - socklen_t addrlen, - struct MHD_IPCount *key) -{ - memset (key, - 0, - sizeof(*key)); - - /* IPv4 addresses */ - if (sizeof (struct sockaddr_in) == addrlen) - { - const struct sockaddr_in *addr4 = (const struct sockaddr_in *) addr; - - key->family = AF_INET; - memcpy (&key->addr.ipv4, - &addr4->sin_addr, - sizeof(addr4->sin_addr)); - return MHD_YES; - } - -#ifdef HAVE_INET6 - /* IPv6 addresses */ - if (sizeof (struct sockaddr_in6) == addrlen) - { - const struct sockaddr_in6 *addr6 = (const struct sockaddr_in6 *) addr; - - key->family = AF_INET6; - memcpy (&key->addr.ipv6, - &addr6->sin6_addr, - sizeof(addr6->sin6_addr)); - return MHD_YES; - } -#endif - - /* Some other address */ - return MHD_NO; -} - - -/** - * Check if IP address is over its limit in terms of the number - * of allowed concurrent connections. If the IP is still allowed, - * increments the connection counter. - * - * @param daemon handle to daemon where connection counts are tracked - * @param addr address to add (or increment counter) - * @param addrlen number of bytes in @a addr - * @return Return #MHD_YES if IP below limit, #MHD_NO if IP has surpassed limit. - * Also returns #MHD_NO if fails to allocate memory. - */ -int -MHD_ip_limit_add (struct MHD_Daemon *daemon, - const struct sockaddr *addr, - socklen_t addrlen) -{ - struct MHD_IPCount *key; - void **nodep; - void *node; - int result; - - daemon = get_master (daemon); - /* Ignore if no connection limit assigned */ - if (0 == daemon->ip_connection_limit) - return MHD_YES; - - if (NULL == (key = malloc (sizeof(*key)))) - return MHD_NO; - - /* Initialize key */ - if (MHD_NO == MHD_ip_addr_to_key (addr, - addrlen, - key)) - { - /* Allow unhandled address types through */ - free (key); - return MHD_YES; - } - MHD_ip_count_lock (daemon); - - /* Search for the IP address */ - if (NULL == (nodep = tsearch (key, - &daemon->per_ip_connection_count, - &MHD_ip_addr_compare))) - { -#ifdef HAVE_MESSAGES - MHD_DLOG (daemon, - MHD_SC_IP_COUNTER_FAILURE, - _ ("Failed to add IP connection count node.\n")); -#endif - MHD_ip_count_unlock (daemon); - free (key); - return MHD_NO; - } - node = *nodep; - /* If we got an existing node back, free the one we created */ - if (node != key) - free (key); - key = (struct MHD_IPCount *) node; - /* Test if there is room for another connection; if so, - * increment count */ - result = (key->count < daemon->ip_connection_limit) ? MHD_YES : MHD_NO; - if (MHD_YES == result) - ++key->count; - - MHD_ip_count_unlock (daemon); - return result; -} - - -/** - * Decrement connection count for IP address, removing from table - * count reaches 0. - * - * @param daemon handle to daemon where connection counts are tracked - * @param addr address to remove (or decrement counter) - * @param addrlen number of bytes in @a addr - */ -void -MHD_ip_limit_del (struct MHD_Daemon *daemon, - const struct sockaddr *addr, - socklen_t addrlen) -{ - struct MHD_IPCount search_key; - struct MHD_IPCount *found_key; - void **nodep; - - daemon = get_master (daemon); - /* Ignore if no connection limit assigned */ - if (0 == daemon->ip_connection_limit) - return; - /* Initialize search key */ - if (MHD_NO == MHD_ip_addr_to_key (addr, - addrlen, - &search_key)) - return; - - MHD_ip_count_lock (daemon); - - /* Search for the IP address */ - if (NULL == (nodep = tfind (&search_key, - &daemon->per_ip_connection_count, - &MHD_ip_addr_compare))) - { - /* Something's wrong if we couldn't find an IP address - * that was previously added */ - MHD_PANIC (_ ("Failed to find previously-added IP address.\n")); - } - found_key = (struct MHD_IPCount *) *nodep; - /* Validate existing count for IP address */ - if (0 == found_key->count) - { - MHD_PANIC (_ ("Previously-added IP address had counter of zero.\n")); - } - /* Remove the node entirely if count reduces to 0 */ - if (0 == --found_key->count) - { - tdelete (found_key, - &daemon->per_ip_connection_count, - &MHD_ip_addr_compare); - free (found_key); - } - - MHD_ip_count_unlock (daemon); -} - - -/* end of daemon_ip_limit.c */ diff --git a/src/lib/daemon_ip_limit.h b/src/lib/daemon_ip_limit.h @@ -1,60 +0,0 @@ -/* - This file is part of libmicrohttpd - Copyright (C) 2007-2018 Daniel Pittman and Christian Grothoff - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. - - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with this library; if not, write to the Free Software - Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA -*/ -/** - * @file lib/daemon_ip_limit.h - * @brief counting of connections per IP - * @author Christian Grothoff - */ - -#ifndef DAEMON_IP_LIMIT_H -#define DAEMON_IP_LIMIT_H - -/** - * Check if IP address is over its limit in terms of the number - * of allowed concurrent connections. If the IP is still allowed, - * increments the connection counter. - * - * @param daemon handle to daemon where connection counts are tracked - * @param addr address to add (or increment counter) - * @param addrlen number of bytes in @a addr - * @return Return #MHD_YES if IP below limit, #MHD_NO if IP has surpassed limit. - * Also returns #MHD_NO if fails to allocate memory. - */ -int -MHD_ip_limit_add (struct MHD_Daemon *daemon, - const struct sockaddr *addr, - socklen_t addrlen) -MHD_NONNULL (1,2); - - -/** - * Decrement connection count for IP address, removing from table - * count reaches 0. - * - * @param daemon handle to daemon where connection counts are tracked - * @param addr address to remove (or decrement counter) - * @param addrlen number of bytes in @a addr - */ -void -MHD_ip_limit_del (struct MHD_Daemon *daemon, - const struct sockaddr *addr, - socklen_t addrlen) -MHD_NONNULL (1,2); - -#endif diff --git a/src/lib/daemon_options.c b/src/lib/daemon_options.c @@ -1,780 +0,0 @@ -/* - This file is part of libmicrohttpd - Copyright (C) 2007-2018 Daniel Pittman and Christian Grothoff - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. - - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with this library; if not, write to the Free Software - Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA -*/ - -/** - * @file lib/daemon_options.c - * @brief boring functions to manipulate daemon options - * @author Christian Grothoff - */ -#include "internal.h" -#ifdef HAVE_DLFCN_H -#include <dlfcn.h> -#endif - -/** - * Set logging method. Specify NULL to disable logging entirely. By - * default (if this option is not given), we log error messages to - * stderr. - * - * @param daemon which instance to setup logging for - * @param logger function to invoke - * @param logger_cls closure for @a logger - */ -void -MHD_daemon_set_logger (struct MHD_Daemon *daemon, - MHD_LoggingCallback logger, - void *logger_cls) -{ - daemon->logger = logger; - daemon->logger_cls = logger_cls; -} - - -/** - * Suppress use of "Date" header as this system has no RTC. - * - * @param daemon which instance to disable clock for. - */ -void -MHD_daemon_suppress_date_no_clock (struct MHD_Daemon *daemon) -{ - daemon->suppress_date = true; -} - - -/** - * Disable use of inter-thread communication channel. - * #MHD_daemon_disable_itc() can be used with - * #MHD_daemon_thread_internal() to perform some additional - * optimizations (in particular, not creating a pipe for IPC - * signalling). If it is used, certain functions like - * #MHD_daemon_quiesce() or #MHD_connection_add() or - * #MHD_action_suspend() cannot be used anymore. - * #MHD_daemon_disable_itc() is not beneficial on platforms where - * select()/poll()/other signal shutdown() of a listen socket. - * - * You should only use this function if you are sure you do - * satisfy all of its requirements and need a generally minor - * boost in performance. - * - * @param daemon which instance to disable itc for - */ -void -MHD_daemon_disable_itc (struct MHD_Daemon *daemon) -{ - daemon->disable_itc = true; -} - - -/** - * Enable `turbo`. Disables certain calls to `shutdown()`, - * enables aggressive non-blocking optimistic reads and - * other potentially unsafe optimizations. - * Most effects only happen with #MHD_ELS_EPOLL. - * - * @param daemon which instance to enable turbo for - */ -void -MHD_daemon_enable_turbo (struct MHD_Daemon *daemon) -{ - daemon->enable_turbo = true; -} - - -/** - * Disable #MHD_action_suspend() functionality. - * - * You should only use this function if you are sure you do - * satisfy all of its requirements and need a generally minor - * boost in performance. - * - * @param daemon which instance to disable suspend for - */ -void -MHD_daemon_disallow_suspend_resume (struct MHD_Daemon *daemon) -{ - daemon->disallow_suspend_resume = true; -} - - -/** - * You need to set this option if you want to disable use of HTTP "Upgrade". - * "Upgrade" may require usage of additional internal resources, - * which we can avoid providing if they will not be used. - * - * You should only use this function if you are sure you do - * satisfy all of its requirements and need a generally minor - * boost in performance. - * - * @param daemon which instance to enable suspend/resume for - */ -void -MHD_daemon_disallow_upgrade (struct MHD_Daemon *daemon) -{ - daemon->disallow_upgrade = true; -} - - -/** - * Configure TCP_FASTOPEN option, including setting a - * custom @a queue_length. - * - * Note that having a larger queue size can cause resource exhaustion - * attack as the TCP stack has to now allocate resources for the SYN - * packet along with its DATA. - * - * @param daemon which instance to configure TCP_FASTOPEN for - * @param fom under which conditions should we use TCP_FASTOPEN? - * @param queue_length queue length to use, default is 50 if this - * option is never given. - * @return #MHD_YES upon success, #MHD_NO if #MHD_FOM_REQUIRE was - * given, but TCP_FASTOPEN is not available on the platform - */ -enum MHD_Bool -MHD_daemon_tcp_fastopen (struct MHD_Daemon *daemon, - enum MHD_FastOpenMethod fom, - unsigned int queue_length) -{ - daemon->fast_open_method = fom; - daemon->fo_queue_length = queue_length; - switch (fom) - { - case MHD_FOM_DISABLE: - return MHD_YES; - case MHD_FOM_AUTO: - return MHD_YES; - case MHD_FOM_REQUIRE: -#ifdef TCP_FASTOPEN - return MHD_YES; -#else - return MHD_NO; -#endif - } - return MHD_NO; -} - - -/** - * Bind to the given TCP port and address family. - * - * Ineffective in conjunction with #MHD_daemon_listen_socket(). - * Ineffective in conjunction with #MHD_daemon_bind_sa(). - * - * If neither this option nor the other two mentioned above - * is specified, MHD will simply not listen on any socket! - * - * @param daemon which instance to configure the TCP port for - * @param af address family to use - * @param port port to use, 0 to bind to a random (free) port - */ -void -MHD_daemon_bind_port (struct MHD_Daemon *daemon, - enum MHD_AddressFamily af, - uint16_t port) -{ - daemon->listen_af = af; - daemon->listen_port = port; -} - - -/** - * Bind to the given socket address. - * Ineffective in conjunction with #MHD_daemon_listen_socket(). - * - * @param daemon which instance to configure the binding address for - * @param sa address to bind to; can be IPv4 (AF_INET), IPv6 (AF_INET6) - * or even a UNIX domain socket (AF_UNIX) - * @param sa_len number of bytes in @a sa - */ -void -MHD_daemon_bind_socket_address (struct MHD_Daemon *daemon, - const struct sockaddr *sa, - size_t sa_len) -{ - memcpy (&daemon->listen_sa, - sa, - sa_len); - daemon->listen_sa_len = sa_len; -} - - -/** - * Use the given backlog for the listen() call. - * Ineffective in conjunction with #MHD_daemon_listen_socket(). - * - * @param daemon which instance to configure the backlog for - * @param listen_backlog backlog to use - */ -void -MHD_daemon_listen_backlog (struct MHD_Daemon *daemon, - int listen_backlog) -{ - daemon->listen_backlog = listen_backlog; -} - - -/** - * If present true, allow reusing address:port socket (by using - * SO_REUSEPORT on most platform, or platform-specific ways). If - * present and set to false, disallow reusing address:port socket - * (does nothing on most platform, but uses SO_EXCLUSIVEADDRUSE on - * Windows). - * Ineffective in conjunction with #MHD_daemon_listen_socket(). - * - * @param daemon daemon to configure address reuse for - */ -void -MHD_daemon_listen_allow_address_reuse (struct MHD_Daemon *daemon) -{ - daemon->allow_address_reuse = true; -} - - -/** - * Use SHOUTcast. This will cause the response to begin - * with the SHOUTcast "ICY" line instead of "HTTP". - * - * @param daemon daemon to set SHOUTcast option for - */ -_MHD_EXTERN void -MHD_daemon_enable_shoutcast (struct MHD_Daemon *daemon) -{ - daemon->enable_shoutcast = true; -} - - -/** - * Accept connections from the given socket. Socket - * must be a TCP or UNIX domain (stream) socket. - * - * Unless -1 is given, this disables other listen options, including - * #MHD_daemon_bind_sa(), #MHD_daemon_bind_port(), - * #MHD_daemon_listen_queue() and - * #MHD_daemon_listen_allow_address_reuse(). - * - * @param daemon daemon to set listen socket for - * @param listen_socket listen socket to use, - * MHD_INVALID_SOCKET value will cause this call to be - * ignored (other binding options may still be effective) - */ -void -MHD_daemon_listen_socket (struct MHD_Daemon *daemon, - MHD_socket listen_socket) -{ - daemon->listen_socket = listen_socket; -} - - -/** - * Force use of a particular event loop system call. - * - * @param daemon daemon to set event loop style for - * @param els event loop syscall to use - * @return #MHD_NO on failure, #MHD_YES on success - */ -enum MHD_Bool -MHD_daemon_event_loop (struct MHD_Daemon *daemon, - enum MHD_EventLoopSyscall els) -{ - switch (els) - { - case MHD_ELS_AUTO: - break; /* should always be OK */ - case MHD_ELS_SELECT: - break; /* should always be OK */ - case MHD_ELS_POLL: -#ifdef HAVE_POLL - break; -#else - return MHD_NO; /* not supported */ -#endif - case MHD_ELS_EPOLL: -#ifdef EPOLL_SUPPORT - break; -#else - return MHD_NO; /* not supported */ -#endif - default: - return MHD_NO; /* not supported (presumably future ABI extension) */ - } - daemon->event_loop_syscall = els; - return MHD_YES; -} - - -/** - * Set how strictly MHD will enforce the HTTP protocol. - * - * @param daemon daemon to configure strictness for - * @param sl how strict should we be - */ -void -MHD_daemon_protocol_strict_level (struct MHD_Daemon *daemon, - enum MHD_ProtocolStrictLevel sl) -{ - daemon->protocol_strict_level = sl; -} - - -/** - * Enable and configure TLS. - * - * @param daemon which instance should be configured - * @param tls_backend which TLS backend should be used, - * currently only "gnutls" is supported. You can - * also specify NULL for best-available (which is the default). - * @param ciphers which ciphers should be used by TLS, default is - * "NORMAL" - * @return status code, #MHD_SC_OK upon success - * #MHD_TLS_BACKEND_UNSUPPORTED if the @a backend is unknown - * #MHD_TLS_DISABLED if this build of MHD does not support TLS - * #MHD_TLS_CIPHERS_INVALID if the given @a ciphers are not supported - * by this backend - */ -enum MHD_StatusCode -MHD_daemon_set_tls_backend (struct MHD_Daemon *daemon, - const char *tls_backend, - const char *ciphers) -{ -#if ! (defined(HTTPS_SUPPORT) && defined (HAVE_DLFCN_H)) - return MHD_SC_TLS_DISABLED; -#else - char filename[1024]; - int res; - MHD_TLS_PluginInit init; - - /* todo: .dll on W32? */ - res = MHD_snprintf_ (filename, - sizeof (filename), - "%s/libmicrohttpd_tls_%s.so", - MHD_PLUGIN_INSTALL_PREFIX, - tls_backend); - if (0 >= res) - return MHD_SC_TLS_BACKEND_UNSUPPORTED; /* string too long? */ - if (NULL == - (daemon->tls_backend_lib = dlopen (filename, - RTLD_NOW | RTLD_LOCAL))) - return MHD_SC_TLS_BACKEND_UNSUPPORTED; /* plugin not found */ - if (NULL == (init = dlsym (daemon->tls_backend_lib, - "MHD_TLS_init_" MHD_TLS_ABI_VERSION_STR))) - - { - dlclose (daemon->tls_backend_lib); - daemon->tls_backend_lib = NULL; - return MHD_SC_TLS_BACKEND_UNSUPPORTED; /* possibly wrong version installed */ - } - if (NULL == (daemon->tls_api = init (ciphers))) - { - dlclose (daemon->tls_backend_lib); - daemon->tls_backend_lib = NULL; - return MHD_SC_TLS_CIPHERS_INVALID; /* possibly wrong version installed */ - } - return MHD_SC_OK; -#endif -} - - -/** - * Provide TLS key and certificate data in-memory. - * - * @param daemon which instance should be configured - * @param mem_key private key (key.pem) to be used by the - * HTTPS daemon. Must be the actual data in-memory, not a filename. - * @param mem_cert certificate (cert.pem) to be used by the - * HTTPS daemon. Must be the actual data in-memory, not a filename. - * @param pass passphrase phrase to decrypt 'key.pem', NULL - * if @param mem_key is in cleartext already - * @return #MHD_SC_OK upon success; MHD_BACKEND_UNINITIALIZED - * if the TLS backend is not yet setup. - */ -enum MHD_StatusCode -MHD_daemon_tls_key_and_cert_from_memory (struct MHD_Daemon *daemon, - const char *mem_key, - const char *mem_cert, - const char *pass) -{ -#ifndef HTTPS_SUPPORT - return MHD_SC_TLS_DISABLED; -#else - struct MHD_TLS_Plugin *plugin; - - if (NULL == (plugin = daemon->tls_api)) - return MHD_SC_TLS_BACKEND_UNINITIALIZED; - return plugin->init_kcp (plugin->cls, - mem_key, - mem_cert, - pass); -#endif -} - - -/** - * Configure DH parameters (dh.pem) to use for the TLS key - * exchange. - * - * @param daemon daemon to configure tls for - * @param dh parameters to use - * @return #MHD_SC_OK upon success; MHD_BACKEND_UNINITIALIZED - * if the TLS backend is not yet setup. - */ -enum MHD_StatusCode -MHD_daemon_tls_mem_dhparams (struct MHD_Daemon *daemon, - const char *dh) -{ -#ifndef HTTPS_SUPPORT - return MHD_SC_TLS_DISABLED; -#else - struct MHD_TLS_Plugin *plugin; - - if (NULL == (plugin = daemon->tls_api)) - return MHD_SC_TLS_BACKEND_UNINITIALIZED; - return plugin->init_dhparams (plugin->cls, - dh); -#endif -} - - -/** - * Memory pointer for the certificate (ca.pem) to be used by the - * HTTPS daemon for client authentication. - * - * @param daemon daemon to configure tls for - * @param mem_trust memory pointer to the certificate - * @return #MHD_SC_OK upon success; MHD_BACKEND_UNINITIALIZED - * if the TLS backend is not yet setup. - */ -enum MHD_StatusCode -MHD_daemon_tls_mem_trust (struct MHD_Daemon *daemon, - const char *mem_trust) -{ -#ifndef HTTPS_SUPPORT - return MHD_SC_TLS_DISABLED; -#else - struct MHD_TLS_Plugin *plugin; - - if (NULL == (plugin = daemon->tls_api)) - return MHD_SC_TLS_BACKEND_UNINITIALIZED; - return plugin->init_mem_trust (plugin->cls, - mem_trust); -#endif -} - - -/** - * Configure daemon credentials type for GnuTLS. - * - * @param gnutls_credentials must be a value of - * type `gnutls_credentials_type_t` - * @return #MHD_SC_OK upon success; TODO: define failure modes - */ -enum MHD_StatusCode -MHD_daemon_gnutls_credentials (struct MHD_Daemon *daemon, - int gnutls_credentials) -{ -#ifndef HTTPS_SUPPORT - return MHD_SC_TLS_DISABLED; -#else - struct MHD_TLS_Plugin *plugin; - - if (NULL == (plugin = daemon->tls_api)) - return MHD_SC_TLS_BACKEND_UNINITIALIZED; - return MHD_SC_TLS_BACKEND_OPERATION_UNSUPPORTED; -#endif -} - - -/** - * Provide TLS key and certificate data via callback. - * - * Use a callback to determine which X.509 certificate should be used - * for a given HTTPS connection. This option provides an alternative - * to #MHD_daemon_tls_key_and_cert_from_memory(). You must use this - * version if multiple domains are to be hosted at the same IP address - * using TLS's Server Name Indication (SNI) extension. In this case, - * the callback is expected to select the correct certificate based on - * the SNI information provided. The callback is expected to access - * the SNI data using `gnutls_server_name_get()`. Using this option - * requires GnuTLS 3.0 or higher. - * - * @param daemon daemon to configure callback for - * @param cb must be of type `gnutls_certificate_retrieve_function2 *`. - * @return #MHD_SC_OK on success - */ -enum MHD_StatusCode -MHD_daemon_gnutls_key_and_cert_from_callback (struct MHD_Daemon *daemon, - void *cb) -{ -#ifndef HTTPS_SUPPORT - return MHD_SC_TLS_DISABLED; -#else - struct MHD_TLS_Plugin *plugin; - - if (NULL == (plugin = daemon->tls_api)) - return MHD_SC_TLS_BACKEND_UNINITIALIZED; - return MHD_SC_TLS_BACKEND_OPERATION_UNSUPPORTED; -#endif -} - - -/** - * Specify threading mode to use. - * - * @param daemon daemon to configure - * @param tm mode to use (positive values indicate the - * number of worker threads to be used) - */ -void -MHD_daemon_threading_mode (struct MHD_Daemon *daemon, - enum MHD_ThreadingMode tm) -{ - daemon->threading_mode = tm; -} - - -/** - * Set a policy callback that accepts/rejects connections - * based on the client's IP address. This function will be called - * before a connection object is created. - * - * @param daemon daemon to set policy for - * @param apc function to call to check the policy - * @param apc_cls closure for @a apc - */ -void -MHD_daemon_accept_policy (struct MHD_Daemon *daemon, - MHD_AcceptPolicyCallback apc, - void *apc_cls) -{ - daemon->accept_policy_cb = apc; - daemon->accept_policy_cb_cls = apc_cls; -} - - -/** - * Register a callback to be called first for every request - * (before any parsing of the header). Makes it easy to - * log the full URL. - * - * @param daemon daemon for which to set the logger - * @param cb function to call - * @param cb_cls closure for @a cb - */ -void -MHD_daemon_set_early_uri_logger (struct MHD_Daemon *daemon, - MHD_EarlyUriLogCallback cb, - void *cb_cls) -{ - daemon->early_uri_logger_cb = cb; - daemon->early_uri_logger_cb_cls = cb_cls; -} - - -/** - * Register a function that should be called whenever a connection is - * started or closed. - * - * @param daemon daemon to set callback for - * @param ncc function to call to check the policy - * @param ncc_cls closure for @a apc - */ -void -MHD_daemon_set_notify_connection (struct MHD_Daemon *daemon, - MHD_NotifyConnectionCallback ncc, - void *ncc_cls) -{ - daemon->notify_connection_cb = ncc; - daemon->notify_connection_cb_cls = ncc_cls; -} - - -/** - * Maximum memory size per connection. - * Default is 32 kb (#MHD_POOL_SIZE_DEFAULT). - * Values above 128k are unlikely to result in much benefit, as half - * of the memory will be typically used for IO, and TCP buffers are - * unlikely to support window sizes above 64k on most systems. - * - * @param daemon daemon to configure - * @param memory_limit_b connection memory limit to use in bytes - * @param memory_increment_b increment to use when growing the read buffer, must be smaller than @a memory_limit_b - */ -void -MHD_daemon_connection_memory_limit (struct MHD_Daemon *daemon, - size_t memory_limit_b, - size_t memory_increment_b) -{ - if (memory_increment_b >= memory_limit_b) - MHD_PANIC ("Sane memory increment must be below memory limit.\n"); - daemon->connection_memory_limit_b = memory_limit_b; - daemon->connection_memory_increment_b = memory_increment_b; -} - - -/** - * Desired size of the stack for threads created by MHD. Use 0 for - * system default. Only useful if the selected threading mode - * is not #MHD_TM_EXTERNAL_EVENT_LOOP. - * - * @param daemon daemon to configure - * @param stack_limit_b stack size to use in bytes - */ -void -MHD_daemon_thread_stack_size (struct MHD_Daemon *daemon, - size_t stack_limit_b) -{ - daemon->thread_stack_limit_b = stack_limit_b; -} - - -/** - * Set maximum number of concurrent connections to accept. If not - * given, MHD will not enforce any limits (modulo running into - * OS limits). Values of 0 mean no limit. - * - * @param daemon daemon to configure - * @param global_connection_limit maximum number of (concurrent) - connections - * @param ip_connection_limit limit on the number of (concurrent) - * connections made to the server from the same IP address. - * Can be used to prevent one IP from taking over all of - * the allowed connections. If the same IP tries to - * establish more than the specified number of - * connections, they will be immediately rejected. - */ -void -MHD_daemon_connection_limits (struct MHD_Daemon *daemon, - unsigned int global_connection_limit, - unsigned int ip_connection_limit) -{ - daemon->global_connection_limit = global_connection_limit; - daemon->ip_connection_limit = ip_connection_limit; -} - - -/** - * After how many seconds of inactivity should a - * connection automatically be timed out? - * Use zero for no timeout, which is also the (unsafe!) default. - * - * @param daemon daemon to configure - * @param timeout_s number of seconds of timeout to use - */ -void -MHD_daemon_connection_default_timeout (struct MHD_Daemon *daemon, - unsigned int timeout_s) -{ - daemon->connection_default_timeout = (time_t) timeout_s; -} - - -/** - * Specify a function that should be called for unescaping escape - * sequences in URIs and URI arguments. Note that this function - * will NOT be used by the `struct MHD_PostProcessor`. If this - * option is not specified, the default method will be used which - * decodes escape sequences of the form "%HH". - * - * @param daemon daemon to configure - * @param unescape_cb function to use, NULL for default - * @param unescape_cb_cls closure for @a unescape_cb - */ -void -MHD_daemon_unescape_cb (struct MHD_Daemon *daemon, - MHD_UnescapeCallback unescape_cb, - void *unescape_cb_cls) -{ - daemon->unescape_cb = unescape_cb; - daemon->unescape_cb_cls = unescape_cb_cls; -} - - -/** - * Set random values to be used by the Digest Auth module. Note that - * the application must ensure that @a buf remains allocated and - * unmodified while the daemon is running. - * - * @param daemon daemon to configure - * @param buf_size number of bytes in @a buf - * @param buf entropy buffer - */ -void -MHD_daemon_digest_auth_random (struct MHD_Daemon *daemon, - size_t buf_size, - const void *buf) -{ -#if ENABLE_DAUTH - daemon->digest_auth_random_buf = buf; - daemon->digest_auth_random_buf_size = buf_size; -#else - (void) daemon; - (void) buf_size; - (void) buf; - MHD_PANIC ("Digest authentication not supported by this build.\n"); -#endif -} - - -/** - * Length of the internal array holding the map of the nonce and - * the nonce counter. - * - * @param daemon daemon to configure - * @param nc_length desired array length - */ -enum MHD_StatusCode -MHD_daemon_digest_auth_nc_length (struct MHD_Daemon *daemon, - size_t nc_length) -{ -#if ENABLE_DAUTH - if ( ( (size_t) (nc_length * sizeof (struct MHD_NonceNc))) - / sizeof (struct MHD_NonceNc) != nc_length) - { -#ifdef HAVE_MESSAGES - MHD_DLOG (daemon, - _ ("Specified value for NC_SIZE too large.\n")); -#endif - return MHD_SC_DIGEST_AUTH_NC_LENGTH_TOO_BIG; - } - if (0 < nc_length) - { - if (NULL != daemon->nnc) - free (daemon->nnc); - daemon->nnc = malloc (daemon->nonce_nc_size - * sizeof (struct MHD_NonceNc)); - if (NULL == daemon->nnc) - { -#ifdef HAVE_MESSAGES - MHD_DLOG (daemon, - _ ("Failed to allocate memory for nonce-nc map: %s\n"), - MHD_strerror_ (errno)); -#endif - return MHD_SC_DIGEST_AUTH_NC_ALLOCATION_FAILURE; - } - } - daemon->digest_nc_length = nc_length; - return MHD_SC_OK; -#else - (void) daemon; - (void) nc_length; - return MHD_SC_DIGEST_AUTH_NOT_SUPPORTED_BY_BUILD; -#endif -} - - -/* end of daemon_options.c */ diff --git a/src/lib/daemon_poll.c b/src/lib/daemon_poll.c @@ -1,528 +0,0 @@ -/* - This file is part of libmicrohttpd - Copyright (C) 2007-2018 Daniel Pittman and Christian Grothoff - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. - - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with this library; if not, write to the Free Software - Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA -*/ -/** - * @file lib/daemon_poll.c - * @brief functions to run poll-based event loop - * @author Christian Grothoff - */ -#include "internal.h" -#include "connection_add.h" -#include "connection_call_handlers.h" -#include "connection_finish_forward.h" -#include "daemon_poll.h" -#include "upgrade_process.h" -#include "request_resume.h" - - -#ifdef HAVE_POLL - - -#if defined(HTTPS_SUPPORT) && defined(UPGRADE_SUPPORT) - -/** - * Set required 'event' members in 'pollfd' elements, - * assuming that @a p[0].fd is MHD side of socketpair - * and @a p[1].fd is TLS connected socket. - * - * @param urh upgrade handle to watch for - * @param p pollfd array to update - */ -static void -urh_update_pollfd (struct MHD_UpgradeResponseHandle *urh, - struct pollfd p[2]) -{ - p[0].events = 0; - p[1].events = 0; - - if (urh->in_buffer_used < urh->in_buffer_size) - p[0].events |= POLLIN; - if (0 != urh->out_buffer_used) - p[0].events |= POLLOUT; - - /* Do not monitor again for errors if error was detected before as - * error state is remembered. */ - if ((0 == (urh->app.celi & MHD_EPOLL_STATE_ERROR)) && - ((0 != urh->in_buffer_size) || - (0 != urh->out_buffer_size) || - (0 != urh->out_buffer_used))) - p[0].events |= MHD_POLL_EVENTS_ERR_DISC; - - if (urh->out_buffer_used < urh->out_buffer_size) - p[1].events |= POLLIN; - if (0 != urh->in_buffer_used) - p[1].events |= POLLOUT; - - /* Do not monitor again for errors if error was detected before as - * error state is remembered. */ - if ((0 == (urh->mhd.celi & MHD_EPOLL_STATE_ERROR)) && - ((0 != urh->out_buffer_size) || - (0 != urh->in_buffer_size) || - (0 != urh->in_buffer_used))) - p[1].events |= MHD_POLL_EVENTS_ERR_DISC; -} - - -/** - * Set @a p to watch for @a urh. - * - * @param urh upgrade handle to watch for - * @param p pollfd array to set - */ -static void -urh_to_pollfd (struct MHD_UpgradeResponseHandle *urh, - struct pollfd p[2]) -{ - p[0].fd = urh->connection->socket_fd; - p[1].fd = urh->mhd.socket; - urh_update_pollfd (urh, - p); -} - - -/** - * Update ready state in @a urh based on pollfd. - * @param urh upgrade handle to update - * @param p 'poll()' processed pollfd. - */ -static void -urh_from_pollfd (struct MHD_UpgradeResponseHandle *urh, - struct pollfd p[2]) -{ - /* Reset read/write ready, preserve error state. */ - urh->app.celi &= (~MHD_EPOLL_STATE_READ_READY & ~MHD_EPOLL_STATE_WRITE_READY); - urh->mhd.celi &= (~MHD_EPOLL_STATE_READ_READY & ~MHD_EPOLL_STATE_WRITE_READY); - - if (0 != (p[0].revents & POLLIN)) - urh->app.celi |= MHD_EPOLL_STATE_READ_READY; - if (0 != (p[0].revents & POLLOUT)) - urh->app.celi |= MHD_EPOLL_STATE_WRITE_READY; - if (0 != (p[0].revents & POLLHUP)) - urh->app.celi |= MHD_EPOLL_STATE_READ_READY | MHD_EPOLL_STATE_WRITE_READY; - if (0 != (p[0].revents & MHD_POLL_REVENTS_ERRROR)) - urh->app.celi |= MHD_EPOLL_STATE_ERROR; - if (0 != (p[1].revents & POLLIN)) - urh->mhd.celi |= MHD_EPOLL_STATE_READ_READY; - if (0 != (p[1].revents & POLLOUT)) - urh->mhd.celi |= MHD_EPOLL_STATE_WRITE_READY; - if (0 != (p[1].revents & POLLHUP)) - urh->mhd.celi |= MHD_EPOLL_STATE_ERROR; - if (0 != (p[1].revents & MHD_POLL_REVENTS_ERRROR)) - urh->mhd.celi |= MHD_EPOLL_STATE_READ_READY | MHD_EPOLL_STATE_WRITE_READY; -} - - -#endif /* HTTPS_SUPPORT && UPGRADE_SUPPORT */ - - -/** - * Process all of our connections and possibly the server - * socket using poll(). - * - * @param daemon daemon to run poll loop for - * @param may_block #MHD_YES if blocking, #MHD_NO if non-blocking - * @return #MHD_SC_OK on success - */ -enum MHD_StatusCode -MHD_daemon_poll_all_ (struct MHD_Daemon *daemon, - bool may_block) -{ - unsigned int num_connections; - struct MHD_Connection *pos; - struct MHD_Connection *prev; -#if defined(HTTPS_SUPPORT) && defined(UPGRADE_SUPPORT) - struct MHD_UpgradeResponseHandle *urh; - struct MHD_UpgradeResponseHandle *urhn; -#endif /* HTTPS_SUPPORT && UPGRADE_SUPPORT */ - - if ( (! daemon->disallow_suspend_resume) && - (MHD_resume_suspended_connections_ (daemon)) ) - may_block = false; - - /* count number of connections and thus determine poll set size */ - num_connections = 0; - for (pos = daemon->connections_head; NULL != pos; pos = pos->next) - num_connections++; -#if defined(HTTPS_SUPPORT) && defined(UPGRADE_SUPPORT) - for (urh = daemon->urh_head; NULL != urh; urh = urh->next) - num_connections += 2; -#endif /* HTTPS_SUPPORT && UPGRADE_SUPPORT */ - { - MHD_UNSIGNED_LONG_LONG ltimeout; - unsigned int i; - int timeout; - unsigned int poll_server; - int poll_listen; - int poll_itc_idx; - struct pollfd *p; - MHD_socket ls; - - p = MHD_calloc_ ((2 + num_connections), - sizeof (struct pollfd)); - if (NULL == p) - { -#ifdef HAVE_MESSAGES - MHD_DLOG (daemon, - MHD_SC_POLL_MALLOC_FAILURE, - _ ("Error allocating memory: %s\n"), - MHD_strerror_ (errno)); -#endif - return MHD_SC_POLL_MALLOC_FAILURE; - } - poll_server = 0; - poll_listen = -1; - if ( (MHD_INVALID_SOCKET != (ls = daemon->listen_socket)) && - (! daemon->was_quiesced) && - (daemon->connections < daemon->global_connection_limit) && - (! daemon->at_limit) ) - { - /* only listen if we are not at the connection limit */ - p[poll_server].fd = ls; - p[poll_server].events = POLLIN; - p[poll_server].revents = 0; - poll_listen = (int) poll_server; - poll_server++; - } - poll_itc_idx = -1; - if (MHD_ITC_IS_VALID_ (daemon->itc)) - { - p[poll_server].fd = MHD_itc_r_fd_ (daemon->itc); - p[poll_server].events = POLLIN; - p[poll_server].revents = 0; - poll_itc_idx = (int) poll_server; - poll_server++; - } - if (! may_block) - timeout = 0; - else if ( (MHD_TM_THREAD_PER_CONNECTION == daemon->threading_mode) || - (MHD_SC_OK != /* FIXME: distinguish between NO_TIMEOUT and errors! */ - MHD_daemon_get_timeout (daemon, - &ltimeout)) ) - timeout = -1; - else - timeout = (ltimeout > INT_MAX) ? INT_MAX : (int) ltimeout; - - i = 0; - for (pos = daemon->connections_tail; NULL != pos; pos = pos->prev) - { - p[poll_server + i].fd = pos->socket_fd; - switch (pos->request.event_loop_info) - { - case MHD_EVENT_LOOP_INFO_READ: - p[poll_server + i].events |= POLLIN | MHD_POLL_EVENTS_ERR_DISC; - break; - case MHD_EVENT_LOOP_INFO_WRITE: - p[poll_server + i].events |= POLLOUT | MHD_POLL_EVENTS_ERR_DISC; - break; - case MHD_EVENT_LOOP_INFO_BLOCK: - p[poll_server + i].events |= MHD_POLL_EVENTS_ERR_DISC; - break; - case MHD_EVENT_LOOP_INFO_CLEANUP: - timeout = 0; /* clean up "pos" immediately */ - break; - } - i++; - } -#if defined(HTTPS_SUPPORT) && defined(UPGRADE_SUPPORT) - for (urh = daemon->urh_tail; NULL != urh; urh = urh->prev) - { - urh_to_pollfd (urh, - &(p[poll_server + i])); - i += 2; - } -#endif /* HTTPS_SUPPORT && UPGRADE_SUPPORT */ - if (0 == poll_server + num_connections) - { - free (p); - return MHD_SC_OK; - } - if (MHD_sys_poll_ (p, - poll_server + num_connections, - timeout) < 0) - { - const int err = MHD_socket_get_error_ (); - if (MHD_SCKT_ERR_IS_EINTR_ (err)) - { - free (p); - return MHD_SC_OK; - } -#ifdef HAVE_MESSAGES - MHD_DLOG (daemon, - MHD_SC_UNEXPECTED_POLL_ERROR, - _ ("poll failed: %s\n"), - MHD_socket_strerr_ (err)); -#endif - free (p); - return MHD_SC_UNEXPECTED_POLL_ERROR; - } - - /* Reset. New value will be set when connections are processed. */ - daemon->data_already_pending = false; - - /* handle ITC FD */ - /* do it before any other processing so - new signals will be processed in next loop */ - if ( (-1 != poll_itc_idx) && - (0 != (p[poll_itc_idx].revents & POLLIN)) ) - MHD_itc_clear_ (daemon->itc); - - /* handle shutdown */ - if (daemon->shutdown) - { - free (p); - return MHD_SC_DAEMON_ALREADY_SHUTDOWN; - } - i = 0; - prev = daemon->connections_tail; - while (NULL != (pos = prev)) - { - prev = pos->prev; - /* first, sanity checks */ - if (i >= num_connections) - break; /* connection list changed somehow, retry later ... */ - if (p[poll_server + i].fd != pos->socket_fd) - continue; /* fd mismatch, something else happened, retry later ... */ - MHD_connection_call_handlers_ (pos, - 0 != (p[poll_server + i].revents & POLLIN), - 0 != (p[poll_server + i].revents - & POLLOUT), - 0 != (p[poll_server + i].revents - & MHD_POLL_REVENTS_ERR_DISC)); - i++; - } -#if defined(HTTPS_SUPPORT) && defined(UPGRADE_SUPPORT) - for (urh = daemon->urh_tail; NULL != urh; urh = urhn) - { - if (i >= num_connections) - break; /* connection list changed somehow, retry later ... */ - - /* Get next connection here as connection can be removed - * from 'daemon->urh_head' list. */ - urhn = urh->prev; - /* Check for fd mismatch. FIXME: required for safety? */ - if ((p[poll_server + i].fd != urh->connection->socket_fd) || - (p[poll_server + i + 1].fd != urh->mhd.socket)) - break; - urh_from_pollfd (urh, - &p[poll_server + i]); - i += 2; - MHD_upgrade_response_handle_process_ (urh); - /* Finished forwarding? */ - if ( (0 == urh->in_buffer_size) && - (0 == urh->out_buffer_size) && - (0 == urh->in_buffer_used) && - (0 == urh->out_buffer_used) ) - { - /* MHD_connection_finish_forward_() will remove connection from - * 'daemon->urh_head' list. */ - MHD_connection_finish_forward_ (urh->connection); - urh->clean_ready = true; - /* If 'urh->was_closed' already was set to true, connection will be - * moved immediately to cleanup list. Otherwise connection - * will stay in suspended list until 'urh' will be marked - * with 'was_closed' by application. */ - MHD_request_resume (&urh->connection->request); - } - } -#endif /* HTTPS_SUPPORT && UPGRADE_SUPPORT */ - /* handle 'listen' FD */ - if ( (-1 != poll_listen) && - (0 != (p[poll_listen].revents & POLLIN)) ) - (void) MHD_accept_connection_ (daemon); - - free (p); - } - return MHD_SC_OK; -} - - -/** - * Process only the listen socket using poll(). - * - * @param daemon daemon to run poll loop for - * @param may_block true if blocking, false if non-blocking - * @return #MHD_SC_OK on success - */ -enum MHD_StatusCode -MHD_daemon_poll_listen_socket_ (struct MHD_Daemon *daemon, - bool may_block) -{ - struct pollfd p[2]; - int timeout; - unsigned int poll_count; - int poll_listen; - int poll_itc_idx; - MHD_socket ls; - - memset (&p, - 0, - sizeof (p)); - poll_count = 0; - poll_listen = -1; - poll_itc_idx = -1; - if ( (MHD_INVALID_SOCKET != (ls = daemon->listen_socket)) && - (! daemon->was_quiesced) ) - - { - p[poll_count].fd = ls; - p[poll_count].events = POLLIN; - p[poll_count].revents = 0; - poll_listen = poll_count; - poll_count++; - } - if (MHD_ITC_IS_VALID_ (daemon->itc)) - { - p[poll_count].fd = MHD_itc_r_fd_ (daemon->itc); - p[poll_count].events = POLLIN; - p[poll_count].revents = 0; - poll_itc_idx = poll_count; - poll_count++; - } - - if (! daemon->disallow_suspend_resume) - (void) MHD_resume_suspended_connections_ (daemon); - - if (! may_block) - timeout = 0; - else - timeout = -1; - if (0 == poll_count) - return MHD_SC_OK; - if (MHD_sys_poll_ (p, - poll_count, - timeout) < 0) - { - const int err = MHD_socket_get_error_ (); - - if (MHD_SCKT_ERR_IS_EINTR_ (err)) - return MHD_SC_OK; -#ifdef HAVE_MESSAGES - MHD_DLOG (daemon, - MHD_SC_UNEXPECTED_POLL_ERROR, - _ ("poll failed: %s\n"), - MHD_socket_strerr_ (err)); -#endif - return MHD_SC_UNEXPECTED_POLL_ERROR; - } - if ( (-1 != poll_itc_idx) && - (0 != (p[poll_itc_idx].revents & POLLIN)) ) - MHD_itc_clear_ (daemon->itc); - - /* handle shutdown */ - if (daemon->shutdown) - return MHD_SC_DAEMON_ALREADY_SHUTDOWN; - if ( (-1 != poll_listen) && - (0 != (p[poll_listen].revents & POLLIN)) ) - (void) MHD_accept_connection_ (daemon); - return MHD_SC_OK; -} - - -#endif - - -/** - * Do poll()-based processing. - * - * @param daemon daemon to run poll()-loop for - * @param may_block true if blocking, false if non-blocking - * @return #MHD_SC_OK on success - */ -enum MHD_StatusCode -MHD_daemon_poll_ (struct MHD_Daemon *daemon, - bool may_block) -{ -#ifdef HAVE_POLL - if (daemon->shutdown) - return MHD_SC_DAEMON_ALREADY_SHUTDOWN; - if (MHD_TM_THREAD_PER_CONNECTION != daemon->threading_mode) - return MHD_daemon_poll_all_ (daemon, - may_block); - return MHD_daemon_poll_listen_socket_ (daemon, - may_block); -#else - /* This code should be dead, as we should have checked - this earlier... */ - return MHD_SC_POLL_NOT_SUPPORTED; -#endif -} - - -#ifdef HAVE_POLL -#ifdef HTTPS_SUPPORT -/** - * Process upgraded connection with a poll() loop. - * We are in our own thread, only processing @a con - * - * @param con connection to process - */ -void -MHD_daemon_upgrade_connection_with_poll_ (struct MHD_Connection *con) -{ - struct MHD_UpgradeResponseHandle *urh = con->request.urh; - struct pollfd p[2]; - - memset (p, - 0, - sizeof (p)); - p[0].fd = urh->connection->socket_fd; - p[1].fd = urh->mhd.socket; - - while ( (0 != urh->in_buffer_size) || - (0 != urh->out_buffer_size) || - (0 != urh->in_buffer_used) || - (0 != urh->out_buffer_used) ) - { - int timeout; - - urh_update_pollfd (urh, - p); - - if ( (con->tls_read_ready) && - (urh->in_buffer_used < urh->in_buffer_size)) - timeout = 0; /* No need to wait if incoming data is already pending in TLS buffers. */ - else - timeout = -1; - - if (MHD_sys_poll_ (p, - 2, - timeout) < 0) - { - const int err = MHD_socket_get_error_ (); - - if (MHD_SCKT_ERR_IS_EINTR_ (err)) - continue; -#ifdef HAVE_MESSAGES - MHD_DLOG (con->daemon, - MHD_SC_UNEXPECTED_POLL_ERROR, - _ ("Error during poll: `%s'\n"), - MHD_socket_strerr_ (err)); -#endif - break; - } - urh_from_pollfd (urh, - p); - MHD_upgrade_response_handle_process_ (urh); - } -} - - -#endif -#endif - -/* end of daemon_poll.c */ diff --git a/src/lib/daemon_poll.h b/src/lib/daemon_poll.h @@ -1,87 +0,0 @@ -/* - This file is part of libmicrohttpd - Copyright (C) 2007-2018 Daniel Pittman and Christian Grothoff - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. - - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with this library; if not, write to the Free Software - Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA -*/ - -/** - * @file lib/daemon_poll.h - * @brief non-public functions provided by daemon_poll.c - * @author Christian Grothoff - */ -#ifndef DAEMON_POLL_H -#define DAEMON_POLL_H - - -#ifdef HAVE_POLL -/** - * Process all of our connections and possibly the server - * socket using poll(). - * - * @param daemon daemon to run poll loop for - * @param may_block #MHD_YES if blocking, #MHD_NO if non-blocking - * @return #MHD_SC_OK on success - */ -enum MHD_StatusCode -MHD_daemon_poll_all_ (struct MHD_Daemon *daemon, - bool may_block) -MHD_NONNULL (1); - - -/** - * Process only the listen socket using poll(). - * - * @param daemon daemon to run poll loop for - * @param may_block true if blocking, false if non-blocking - * @return #MHD_SC_OK on success - */ -enum MHD_StatusCode -MHD_daemon_poll_listen_socket_ (struct MHD_Daemon *daemon, - bool may_block) -MHD_NONNULL (1); - - -/** - * Do poll()-based processing. - * - * @param daemon daemon to run poll()-loop for - * @param may_block #MHD_YES if blocking, #MHD_NO if non-blocking - * @return #MHD_SC_OK on success - */ -enum MHD_StatusCode -MHD_daemon_poll_ (struct MHD_Daemon *daemon, - bool may_block) -MHD_NONNULL (1); - -#endif - - -#ifdef HTTPS_SUPPORT -#ifdef HAVE_POLL -/** - * Process upgraded connection with a poll() loop. - * We are in our own thread, only processing @a con - * - * @param con connection to process - */ -void -MHD_daemon_upgrade_connection_with_poll_ (struct MHD_Connection *con) -MHD_NONNULL (1); - -#endif -#endif - -#endif diff --git a/src/lib/daemon_quiesce.c b/src/lib/daemon_quiesce.c @@ -1,128 +0,0 @@ -/* - This file is part of libmicrohttpd - Copyright (C) 2007-2018 Daniel Pittman and Christian Grothoff - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. - - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with this library; if not, write to the Free Software - Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA -*/ - -/** - * @file lib/daemon_quiesce.c - * @brief main functions to quiesce a daemon - * @author Christian Grothoff - */ -#include "internal.h" - - -/** - * Stop accepting connections from the listening socket. Allows - * clients to continue processing, but stops accepting new - * connections. Note that the caller is responsible for closing the - * returned socket; however, if MHD is run using threads (anything but - * external select mode), it must not be closed until AFTER - * #MHD_stop_daemon has been called (as it is theoretically possible - * that an existing thread is still using it). - * - * Note that some thread modes require the caller to have passed - * #MHD_USE_ITC when using this API. If this daemon is - * in one of those modes and this option was not given to - * #MHD_start_daemon, this function will return #MHD_INVALID_SOCKET. - * - * @param daemon daemon to stop accepting new connections for - * @return old listen socket on success, #MHD_INVALID_SOCKET if - * the daemon was already not listening anymore, or - * was never started - * @ingroup specialized - */ -MHD_socket -MHD_daemon_quiesce (struct MHD_Daemon *daemon) -{ - MHD_socket listen_socket; - - if (MHD_INVALID_SOCKET == (listen_socket = daemon->listen_socket)) - return MHD_INVALID_SOCKET; - if ( (daemon->disable_itc) && - (MHD_TM_EXTERNAL_EVENT_LOOP != daemon->threading_mode) ) - { -#ifdef HAVE_MESSAGES - MHD_DLOG (daemon, - MHD_SC_SYSCALL_QUIESCE_REQUIRES_ITC, - "Using MHD_quiesce_daemon in this mode requires ITC.\n"); -#endif - return MHD_INVALID_SOCKET; - } - - if (NULL != daemon->worker_pool) - { - unsigned int i; - - for (i = 0; i < daemon->worker_pool_size; i++) - { - struct MHD_Daemon *worker = &daemon->worker_pool[i]; - - worker->was_quiesced = true; -#ifdef EPOLL_SUPPORT - if ( (MHD_ELS_EPOLL == daemon->event_loop_syscall) && - (-1 != worker->epoll_fd) && - (worker->listen_socket_in_epoll) ) - { - if (0 != epoll_ctl (worker->epoll_fd, - EPOLL_CTL_DEL, - listen_socket, - NULL)) - MHD_PANIC (_ ("Failed to remove listen FD from epoll set.\n")); - worker->listen_socket_in_epoll = false; - } - else -#endif - if (MHD_ITC_IS_VALID_ (worker->itc)) - { - if (! MHD_itc_activate_ (worker->itc, - "q")) - MHD_PANIC (_ ( - "Failed to signal quiesce via inter-thread communication channel.\n")); - } - } - daemon->was_quiesced = true; -#ifdef EPOLL_SUPPORT - if ( (MHD_ELS_EPOLL == daemon->event_loop_syscall) && - (-1 != daemon->epoll_fd) && - (daemon->listen_socket_in_epoll) ) - { - if (0 != epoll_ctl (daemon->epoll_fd, - EPOLL_CTL_DEL, - listen_socket, - NULL)) - MHD_PANIC ("Failed to remove listen FD from epoll set.\n"); - daemon->listen_socket_in_epoll = false; - } -#endif - } - - if ( (MHD_ITC_IS_VALID_ (daemon->itc)) && - (! MHD_itc_activate_ (daemon->itc, - "q")) ) - MHD_PANIC (_ ( - "Failed to signal quiesce via inter-thread communication channel.\n")); - - /* FIXME: we might want some bi-directional communication here - (in both the thread-pool and single-thread case!) - to be sure that the threads have stopped using the listen - socket, otherwise there is still the possibility of a race - between a thread accept()ing and the caller closing and - re-binding the socket. */return listen_socket; -} - - -/* end of daemon_quiesce.c */ diff --git a/src/lib/daemon_run.c b/src/lib/daemon_run.c @@ -1,83 +0,0 @@ -/* - This file is part of libmicrohttpd - Copyright (C) 2007-2018 Daniel Pittman and Christian Grothoff - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. - - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with this library; if not, write to the Free Software - Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA -*/ - -/** - * @file lib/daemon_run.c - * @brief generic function to run event loop of a daemon - * @author Christian Grothoff - */ -#include "internal.h" -#include "connection_cleanup.h" -#include "daemon_epoll.h" -#include "daemon_poll.h" -#include "daemon_select.h" - - -/** - * Run webserver operations (without blocking unless in client - * callbacks). This method should be called by clients in combination - * with #MHD_get_fdset if the client-controlled select method is used - * and #MHD_get_timeout(). - * - * This function is a convenience method, which is useful if the - * fd_sets from #MHD_get_fdset were not directly passed to `select()`; - * with this function, MHD will internally do the appropriate `select()` - * call itself again. While it is always safe to call #MHD_run (if - * #MHD_USE_INTERNAL_POLLING_THREAD is not set), you should call - * #MHD_run_from_select if performance is important (as it saves an - * expensive call to `select()`). - * - * @param daemon daemon to run - * @return #MHD_SC_OK on success - * @ingroup event - */ -enum MHD_StatusCode -MHD_daemon_run (struct MHD_Daemon *daemon) -{ - enum MHD_StatusCode sc; - - if (daemon->shutdown) - return MHD_SC_DAEMON_ALREADY_SHUTDOWN; - if (MHD_TM_EXTERNAL_EVENT_LOOP != daemon->threading_mode) - return MHD_SC_CONFIGURATION_MISMATCH_FOR_RUN_EXTERNAL; - switch (daemon->event_loop_syscall) - { - case MHD_ELS_POLL: - sc = MHD_daemon_poll_ (daemon, - MHD_NO); - MHD_connection_cleanup_ (daemon); - return sc; -#ifdef EPOLL_SUPPORT - case MHD_ELS_EPOLL: - sc = MHD_daemon_epoll_ (daemon, - MHD_NO); - MHD_connection_cleanup_ (daemon); - return sc; -#endif - case MHD_ELS_SELECT: - return MHD_daemon_select_ (daemon, - MHD_NO); - /* MHD_select does MHD_connection_cleanup_ already */ - default: - return MHD_SC_CONFIGURATION_UNEXPECTED_ELS; - } -} - - -/* end of daemon_run.c */ diff --git a/src/lib/daemon_run_from_select.c b/src/lib/daemon_run_from_select.c diff --git a/src/lib/daemon_select.c b/src/lib/daemon_select.c @@ -1,821 +0,0 @@ -/* - This file is part of libmicrohttpd - Copyright (C) 2007-2018 Daniel Pittman and Christian Grothoff - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. - - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with this library; if not, write to the Free Software - Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA -*/ - -/** - * @file lib/daemon_select.c - * @brief function to run select()-based event loop of a daemon - * @author Christian Grothoff - */ -#include "internal.h" -#include "connection_add.h" -#include "connection_call_handlers.h" -#include "connection_cleanup.h" -#include "connection_finish_forward.h" -#include "daemon_select.h" -#include "daemon_epoll.h" -#include "request_resume.h" -#include "upgrade_process.h" - - -/** - * We defined a macro with the same name as a function we - * are implementing here. Need to undef the macro to avoid - * causing a conflict. - */ -#undef MHD_daemon_get_fdset - -/** - * Obtain the `select()` sets for this daemon. Daemon's FDs will be - * added to fd_sets. To get only daemon FDs in fd_sets, call FD_ZERO - * for each fd_set before calling this function. FD_SETSIZE is assumed - * to be platform's default. - * - * This function should only be called in when MHD is configured to - * use external select with 'select()' or with 'epoll'. In the latter - * case, it will only add the single 'epoll()' file descriptor used by - * MHD to the sets. It's necessary to use #MHD_daemon_get_timeout() in - * combination with this function. - * - * This function must be called only for daemon started without - * #MHD_USE_INTERNAL_POLLING_THREAD flag. - * - * @param daemon daemon to get sets from - * @param read_fd_set read set - * @param write_fd_set write set - * @param except_fd_set except set - * @param max_fd increased to largest FD added (if larger - * than existing value); can be NULL - * @return #MHD_SC_OK on success, otherwise error code - * @ingroup event - */ -enum MHD_StatusCode -MHD_daemon_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) -{ - return MHD_daemon_get_fdset2 (daemon, - read_fd_set, - write_fd_set, - except_fd_set, - max_fd, - _MHD_SYS_DEFAULT_FD_SETSIZE); -} - - -#if defined(HTTPS_SUPPORT) && defined(UPGRADE_SUPPORT) -/** - * Obtain the select() file descriptor sets for the - * given @a urh. - * - * @param urh upgrade handle to wait for - * @param[out] rs read set to initialize - * @param[out] ws write set to initialize - * @param[out] es except set to initialize - * @param[out] max_fd maximum FD to update - * @param fd_setsize value of FD_SETSIZE - * @return true on success, false on error - */ -static bool -urh_to_fdset (struct MHD_UpgradeResponseHandle *urh, - fd_set *rs, - fd_set *ws, - fd_set *es, - MHD_socket *max_fd, - unsigned int fd_setsize) -{ - const MHD_socket conn_sckt = urh->connection->socket_fd; - const MHD_socket mhd_sckt = urh->mhd.socket; - bool res = true; - - /* Do not add to 'es' only if socket is closed - * or not used anymore. */ - if (MHD_INVALID_SOCKET != conn_sckt) - { - if ( (urh->in_buffer_used < urh->in_buffer_size) && - (! MHD_add_to_fd_set_ (conn_sckt, - rs, - max_fd, - fd_setsize)) ) - res = false; - if ( (0 != urh->out_buffer_used) && - (! MHD_add_to_fd_set_ (conn_sckt, - ws, - max_fd, - fd_setsize)) ) - res = false; - /* Do not monitor again for errors if error was detected before as - * error state is remembered. */ - if ((0 == (urh->app.celi & MHD_EPOLL_STATE_ERROR)) && - ((0 != urh->in_buffer_size) || - (0 != urh->out_buffer_size) || - (0 != urh->out_buffer_used))) - MHD_add_to_fd_set_ (conn_sckt, - es, - max_fd, - fd_setsize); - } - if (MHD_INVALID_SOCKET != mhd_sckt) - { - if ( (urh->out_buffer_used < urh->out_buffer_size) && - (! MHD_add_to_fd_set_ (mhd_sckt, - rs, - max_fd, - fd_setsize)) ) - res = false; - if ( (0 != urh->in_buffer_used) && - (! MHD_add_to_fd_set_ (mhd_sckt, - ws, - max_fd, - fd_setsize)) ) - res = false; - /* Do not monitor again for errors if error was detected before as - * error state is remembered. */ - if ((0 == (urh->mhd.celi & MHD_EPOLL_STATE_ERROR)) && - ((0 != urh->out_buffer_size) || - (0 != urh->in_buffer_size) || - (0 != urh->in_buffer_used))) - MHD_add_to_fd_set_ (mhd_sckt, - es, - max_fd, - fd_setsize); - } - - return res; -} - - -#endif - - -/** - * Internal version of #MHD_daemon_get_fdset2(). - * - * @param daemon daemon to get sets from - * @param read_fd_set read set - * @param write_fd_set write set - * @param except_fd_set except set - * @param max_fd increased to largest FD added (if larger - * than existing value); can be NULL - * @param fd_setsize value of FD_SETSIZE - * @return #MHD_SC_OK on success - * @ingroup event - */ -static enum MHD_StatusCode -internal_get_fdset2 (struct MHD_Daemon *daemon, - fd_set *read_fd_set, - fd_set *write_fd_set, - fd_set *except_fd_set, - MHD_socket *max_fd, - unsigned int fd_setsize) - -{ - struct MHD_Connection *pos; - struct MHD_Connection *posn; - enum MHD_StatusCode result = MHD_SC_OK; - MHD_socket ls; - - if (daemon->shutdown) - return MHD_SC_DAEMON_ALREADY_SHUTDOWN; - - ls = daemon->listen_socket; - if ( (MHD_INVALID_SOCKET != ls) && - (! daemon->was_quiesced) && - (! MHD_add_to_fd_set_ (ls, - read_fd_set, - max_fd, - fd_setsize)) ) - result = MHD_SC_SOCKET_OUTSIDE_OF_FDSET_RANGE; - - /* Add all sockets to 'except_fd_set' as well to watch for - * out-of-band data. However, ignore errors if INFO_READ - * or INFO_WRITE sockets will not fit 'except_fd_set'. */ - /* Start from oldest connections. Make sense for W32 FDSETs. */ - for (pos = daemon->connections_tail; NULL != pos; pos = posn) - { - posn = pos->prev; - - switch (pos->request.event_loop_info) - { - case MHD_EVENT_LOOP_INFO_READ: - if (! MHD_add_to_fd_set_ (pos->socket_fd, - read_fd_set, - max_fd, - fd_setsize)) - result = MHD_SC_SOCKET_OUTSIDE_OF_FDSET_RANGE; -#ifdef MHD_POSIX_SOCKETS - MHD_add_to_fd_set_ (pos->socket_fd, - except_fd_set, - max_fd, - fd_setsize); -#endif /* MHD_POSIX_SOCKETS */ - break; - case MHD_EVENT_LOOP_INFO_WRITE: - if (! MHD_add_to_fd_set_ (pos->socket_fd, - write_fd_set, - max_fd, - fd_setsize)) - result = MHD_SC_SOCKET_OUTSIDE_OF_FDSET_RANGE; -#ifdef MHD_POSIX_SOCKETS - MHD_add_to_fd_set_ (pos->socket_fd, - except_fd_set, - max_fd, - fd_setsize); -#endif /* MHD_POSIX_SOCKETS */ - break; - case MHD_EVENT_LOOP_INFO_BLOCK: - if ( (NULL == except_fd_set) || - ! MHD_add_to_fd_set_ (pos->socket_fd, - except_fd_set, - max_fd, - fd_setsize)) - result = MHD_SC_SOCKET_OUTSIDE_OF_FDSET_RANGE; - break; - case MHD_EVENT_LOOP_INFO_CLEANUP: - /* this should never happen */ - break; - } - } -#ifdef MHD_WINSOCK_SOCKETS - /* W32 use limited array for fd_set so add INFO_READ/INFO_WRITE sockets - * only after INFO_BLOCK sockets to ensure that INFO_BLOCK sockets will - * not be pushed out. */ - for (pos = daemon->connections_tail; NULL != pos; pos = posn) - { - posn = pos->prev; - MHD_add_to_fd_set_ (pos->socket_fd, - except_fd_set, - max_fd, - fd_setsize); - } -#endif /* MHD_WINSOCK_SOCKETS */ -#if defined(HTTPS_SUPPORT) && defined(UPGRADE_SUPPORT) - { - struct MHD_UpgradeResponseHandle *urh; - - for (urh = daemon->urh_tail; NULL != urh; urh = urh->prev) - { - if (! urh_to_fdset (urh, - read_fd_set, - write_fd_set, - except_fd_set, - max_fd, - fd_setsize)) - result = MHD_SC_SOCKET_OUTSIDE_OF_FDSET_RANGE; - } - } -#endif - return result; -} - - -/** - * Obtain the `select()` sets for this daemon. Daemon's FDs will be - * added to fd_sets. To get only daemon FDs in fd_sets, call FD_ZERO - * for each fd_set before calling this function. - * - * Passing custom FD_SETSIZE as @a fd_setsize allow usage of - * larger/smaller than platform's default fd_sets. - * - * This function should only be called in when MHD is configured to - * use external select with 'select()' or with 'epoll'. In the latter - * case, it will only add the single 'epoll' file descriptor used by - * MHD to the sets. It's necessary to use #MHD_get_timeout() in - * combination with this function. - * - * This function must be called only for daemon started - * without #MHD_USE_INTERNAL_POLLING_THREAD flag. - * - * @param daemon daemon to get sets from - * @param read_fd_set read set - * @param write_fd_set write set - * @param except_fd_set except set - * @param max_fd increased to largest FD added (if larger - * than existing value); can be NULL - * @param fd_setsize value of FD_SETSIZE - * @return #MHD_SC_OK on success, otherwise error code - * @ingroup event - */ -enum MHD_StatusCode -MHD_daemon_get_fdset2 (struct MHD_Daemon *daemon, - fd_set *read_fd_set, - fd_set *write_fd_set, - fd_set *except_fd_set, - MHD_socket *max_fd, - unsigned int fd_setsize) -{ - if ( (MHD_TM_EXTERNAL_EVENT_LOOP != daemon->threading_mode) || - (MHD_ELS_POLL == daemon->event_loop_syscall) ) - return MHD_SC_CONFIGURATION_MISMATCH_FOR_GET_FDSET; - -#ifdef EPOLL_SUPPORT - if (MHD_ELS_EPOLL == daemon->event_loop_syscall) - { - if (daemon->shutdown) - return MHD_SC_DAEMON_ALREADY_SHUTDOWN; - - /* we're in epoll mode, use the epoll FD as a stand-in for - the entire event set */ - - return MHD_add_to_fd_set_ (daemon->epoll_fd, - read_fd_set, - max_fd, - fd_setsize) - ? MHD_SC_OK - : MHD_SC_SOCKET_OUTSIDE_OF_FDSET_RANGE; - } -#endif - - return internal_get_fdset2 (daemon, - read_fd_set, - write_fd_set, - except_fd_set, - max_fd, - fd_setsize); -} - - -#if defined(HTTPS_SUPPORT) && defined(UPGRADE_SUPPORT) -/** - * Update the @a urh based on the ready FDs in - * the @a rs, @a ws, and @a es. - * - * @param urh upgrade handle to update - * @param rs read result from select() - * @param ws write result from select() - * @param es except result from select() - */ -static void -urh_from_fdset (struct MHD_UpgradeResponseHandle *urh, - const fd_set *rs, - const fd_set *ws, - const fd_set *es) -{ - const MHD_socket conn_sckt = urh->connection->socket_fd; - const MHD_socket mhd_sckt = urh->mhd.socket; - - /* Reset read/write ready, preserve error state. */ - urh->app.celi &= (~MHD_EPOLL_STATE_READ_READY & ~MHD_EPOLL_STATE_WRITE_READY); - urh->mhd.celi &= (~MHD_EPOLL_STATE_READ_READY & ~MHD_EPOLL_STATE_WRITE_READY); - - if (MHD_INVALID_SOCKET != conn_sckt) - { - if (FD_ISSET (conn_sckt, rs)) - urh->app.celi |= MHD_EPOLL_STATE_READ_READY; - if (FD_ISSET (conn_sckt, ws)) - urh->app.celi |= MHD_EPOLL_STATE_WRITE_READY; - if (FD_ISSET (conn_sckt, es)) - urh->app.celi |= MHD_EPOLL_STATE_ERROR; - } - if ((MHD_INVALID_SOCKET != mhd_sckt)) - { - if (FD_ISSET (mhd_sckt, rs)) - urh->mhd.celi |= MHD_EPOLL_STATE_READ_READY; - if (FD_ISSET (mhd_sckt, ws)) - urh->mhd.celi |= MHD_EPOLL_STATE_WRITE_READY; - if (FD_ISSET (mhd_sckt, es)) - urh->mhd.celi |= MHD_EPOLL_STATE_ERROR; - } -} - - -#endif - - -/** - * Internal version of #MHD_run_from_select(). - * - * @param daemon daemon to run select loop for - * @param read_fd_set read set - * @param write_fd_set write set - * @param except_fd_set except set - * @return #MHD_SC_OK on success - * @ingroup event - */ -static enum MHD_StatusCode -internal_run_from_select (struct MHD_Daemon *daemon, - const fd_set *read_fd_set, - const fd_set *write_fd_set, - const fd_set *except_fd_set) -{ - MHD_socket ds; - struct MHD_Connection *pos; - struct MHD_Connection *prev; -#if defined(HTTPS_SUPPORT) && defined(UPGRADE_SUPPORT) - struct MHD_UpgradeResponseHandle *urh; - struct MHD_UpgradeResponseHandle *urhn; -#endif /* HTTPS_SUPPORT && UPGRADE_SUPPORT */ - /* Reset. New value will be set when connections are processed. */ - /* Note: no-op for thread-per-connection as it is always false in that mode. */ - daemon->data_already_pending = false; - - /* Clear ITC to avoid spinning select */ - /* Do it before any other processing so new signals - will trigger select again and will be processed */ - if ( (MHD_ITC_IS_VALID_ (daemon->itc)) && - (FD_ISSET (MHD_itc_r_fd_ (daemon->itc), - read_fd_set)) ) - MHD_itc_clear_ (daemon->itc); - - /* select connection thread handling type */ - if ( (MHD_INVALID_SOCKET != (ds = daemon->listen_socket)) && - (! daemon->was_quiesced) && - (FD_ISSET (ds, - read_fd_set)) ) - (void) MHD_accept_connection_ (daemon); - - if (MHD_TM_THREAD_PER_CONNECTION != daemon->threading_mode) - { - /* do not have a thread per connection, process all connections now */ - prev = daemon->connections_tail; - while (NULL != (pos = prev)) - { - prev = pos->prev; - ds = pos->socket_fd; - if (MHD_INVALID_SOCKET == ds) - continue; - MHD_connection_call_handlers_ (pos, - FD_ISSET (ds, - read_fd_set), - FD_ISSET (ds, - write_fd_set), - FD_ISSET (ds, - except_fd_set)); - } - } - -#if defined(HTTPS_SUPPORT) && defined(UPGRADE_SUPPORT) - /* handle upgraded HTTPS connections */ - for (urh = daemon->urh_tail; NULL != urh; urh = urhn) - { - urhn = urh->prev; - /* update urh state based on select() output */ - urh_from_fdset (urh, - read_fd_set, - write_fd_set, - except_fd_set); - /* call generic forwarding function for passing data */ - MHD_upgrade_response_handle_process_ (urh); - /* Finished forwarding? */ - if ( (0 == urh->in_buffer_size) && - (0 == urh->out_buffer_size) && - (0 == urh->in_buffer_used) && - (0 == urh->out_buffer_used) ) - { - MHD_connection_finish_forward_ (urh->connection); - urh->clean_ready = true; - /* Resuming will move connection to cleanup list. */ - MHD_request_resume (&urh->connection->request); - } - } -#endif /* HTTPS_SUPPORT && UPGRADE_SUPPORT */ - MHD_connection_cleanup_ (daemon); - return MHD_SC_OK; -} - - -#if defined(HTTPS_SUPPORT) && defined(UPGRADE_SUPPORT) -/** - * Process upgraded connection with a select loop. - * We are in our own thread, only processing @a con - * - * @param con connection to process - */ -void -MHD_daemon_upgrade_connection_with_select_ (struct MHD_Connection *con) -{ - struct MHD_UpgradeResponseHandle *urh = con->request.urh; - - while ( (0 != urh->in_buffer_size) || - (0 != urh->out_buffer_size) || - (0 != urh->in_buffer_used) || - (0 != urh->out_buffer_used) ) - { - /* use select */ - fd_set rs; - fd_set ws; - fd_set es; - MHD_socket max_fd; - int num_ready; - bool result; - - FD_ZERO (&rs); - FD_ZERO (&ws); - FD_ZERO (&es); - max_fd = MHD_INVALID_SOCKET; - result = urh_to_fdset (urh, - &rs, - &ws, - &es, - &max_fd, - FD_SETSIZE); - if (! result) - { -#ifdef HAVE_MESSAGES - MHD_DLOG (con->daemon, - MHD_SC_SOCKET_OUTSIDE_OF_FDSET_RANGE, - _ ("Error preparing select.\n")); -#endif - break; - } - /* FIXME: does this check really needed? */ - if (MHD_INVALID_SOCKET != max_fd) - { - struct timeval *tvp; - struct timeval tv; - if ( (con->tls_read_ready) && - (urh->in_buffer_used < urh->in_buffer_size)) - { /* No need to wait if incoming data is already pending in TLS buffers. */ - tv.tv_sec = 0; - tv.tv_usec = 0; - tvp = &tv; - } - else - tvp = NULL; - num_ready = MHD_SYS_select_ (max_fd + 1, - &rs, - &ws, - &es, - tvp); - } - else - num_ready = 0; - if (num_ready < 0) - { - const int err = MHD_socket_get_error_ (); - - if (MHD_SCKT_ERR_IS_EINTR_ (err)) - continue; -#ifdef HAVE_MESSAGES - MHD_DLOG (con->daemon, - MHD_SC_UNEXPECTED_SELECT_ERROR, - _ ("Error during select (%d): `%s'\n"), - err, - MHD_socket_strerr_ (err)); -#endif - break; - } - urh_from_fdset (urh, - &rs, - &ws, - &es); - MHD_upgrade_response_handle_process_ (urh); - } -} - - -#endif - - -/** - * Run webserver operations. This method should be called by clients - * in combination with #MHD_get_fdset and #MHD_get_timeout() if the - * client-controlled select method is used. - * - * You can use this function instead of #MHD_run if you called - * `select()` on the result from #MHD_get_fdset. File descriptors in - * the sets that are not controlled by MHD will be ignored. Calling - * this function instead of #MHD_run is more efficient as MHD will not - * have to call `select()` again to determine which operations are - * ready. - * - * This function cannot be used with daemon started with - * #MHD_USE_INTERNAL_POLLING_THREAD flag. - * - * @param daemon daemon to run select loop for - * @param read_fd_set read set - * @param write_fd_set write set - * @param except_fd_set except set - * @return #MHD_SC_OK on success - * @ingroup event - */ -enum MHD_StatusCode -MHD_daemon_run_from_select (struct MHD_Daemon *daemon, - const fd_set *read_fd_set, - - - const fd_set *write_fd_set, - const fd_set *except_fd_set) -{ - if ( (MHD_TM_EXTERNAL_EVENT_LOOP != daemon->threading_mode) || - (MHD_ELS_POLL == daemon->event_loop_syscall) ) - return MHD_SC_CONFIGURATION_MISMATCH_FOR_RUN_SELECT; - if (MHD_ELS_EPOLL == daemon->event_loop_syscall) - { -#ifdef EPOLL_SUPPORT - enum MHD_StatusCode sc; - - sc = MHD_daemon_epoll_ (daemon, - MHD_NO); - MHD_connection_cleanup_ (daemon); - return sc; -#else /* ! EPOLL_SUPPORT */ - return MHD_NO; -#endif /* ! EPOLL_SUPPORT */ - } - - /* Resuming external connections when using an extern mainloop */ - if (! daemon->disallow_suspend_resume) - (void) MHD_resume_suspended_connections_ (daemon); - - return internal_run_from_select (daemon, - read_fd_set, - write_fd_set, - except_fd_set); -} - - -/** - * Main internal select() call. Will compute select sets, call - * select() and then #internal_run_from_select() with the result. - * - * @param daemon daemon to run select() loop for - * @param may_block #MHD_YES if blocking, #MHD_NO if non-blocking - * @return #MHD_SC_OK on success - */ -enum MHD_StatusCode -MHD_daemon_select_ (struct MHD_Daemon *daemon, - int may_block) -{ - int num_ready; - fd_set rs; - fd_set ws; - fd_set es; - MHD_socket maxsock; - struct timeval timeout; - struct timeval *tv; - MHD_UNSIGNED_LONG_LONG ltimeout; - MHD_socket ls; - enum MHD_StatusCode sc; - enum MHD_StatusCode sc2; - - timeout.tv_sec = 0; - timeout.tv_usec = 0; - if (daemon->shutdown) - return MHD_SC_DAEMON_ALREADY_SHUTDOWN; - FD_ZERO (&rs); - FD_ZERO (&ws); - FD_ZERO (&es); - maxsock = MHD_INVALID_SOCKET; - sc = MHD_SC_OK; - if ( (! daemon->disallow_suspend_resume) && - (MHD_resume_suspended_connections_ (daemon)) && - (MHD_TM_THREAD_PER_CONNECTION != daemon->threading_mode) ) - may_block = MHD_NO; - - if (MHD_TM_THREAD_PER_CONNECTION != daemon->threading_mode) - { - - /* single-threaded, go over everything */ - if (MHD_SC_OK != - (sc = internal_get_fdset2 (daemon, - &rs, - &ws, - &es, - &maxsock, - FD_SETSIZE))) - { -#ifdef HAVE_MESSAGES - MHD_DLOG (daemon, - sc, - _ ("Could not obtain daemon fdsets.\n")); -#endif - } - } - else - { - /* accept only, have one thread per connection */ - if ( (MHD_INVALID_SOCKET != (ls = daemon->listen_socket)) && - (! daemon->was_quiesced) && - (! MHD_add_to_fd_set_ (ls, - &rs, - &maxsock, - FD_SETSIZE)) ) - { -#ifdef HAVE_MESSAGES - MHD_DLOG (daemon, - MHD_SC_SOCKET_OUTSIDE_OF_FDSET_RANGE, - _ ("Could not add listen socket to fdset.\n")); -#endif - return MHD_SC_SOCKET_OUTSIDE_OF_FDSET_RANGE; - } - } - if ( (MHD_ITC_IS_VALID_ (daemon->itc)) && - (! MHD_add_to_fd_set_ (MHD_itc_r_fd_ (daemon->itc), - &rs, - &maxsock, - FD_SETSIZE)) ) - { -#if defined(MHD_WINSOCK_SOCKETS) - /* fdset limit reached, new connections - cannot be handled. Remove listen socket FD - from fdset and retry to add ITC FD. */ - if ( (MHD_INVALID_SOCKET != (ls = daemon->listen_socket)) && - (! daemon->was_quiesced) ) - { - FD_CLR (ls, - &rs); - if (! MHD_add_to_fd_set_ (MHD_itc_r_fd_ (daemon->itc), - &rs, - &maxsock, - FD_SETSIZE)) - { -#endif /* MHD_WINSOCK_SOCKETS */ - sc = MHD_SC_SOCKET_OUTSIDE_OF_FDSET_RANGE; -#ifdef HAVE_MESSAGES - MHD_DLOG (daemon, - sc, - _ ( - "Could not add control inter-thread communication channel FD to fdset.\n")); -#endif -#if defined(MHD_WINSOCK_SOCKETS) - } -} - - -#endif /* MHD_WINSOCK_SOCKETS */ - } - /* Stop listening if we are at the configured connection limit */ - /* If we're at the connection limit, no point in really - accepting new connections; however, make sure we do not miss - the shutdown OR the termination of an existing connection; so - only do this optimization if we have a signaling ITC in - place. */ - if ( (MHD_INVALID_SOCKET != (ls = daemon->listen_socket)) && - (MHD_ITC_IS_VALID_ (daemon->itc)) && - ( (daemon->connections == daemon->global_connection_limit) || - (daemon->at_limit) ) ) - { - FD_CLR (ls, - &rs); - } - tv = NULL; - if (MHD_SC_OK != sc) - may_block = MHD_NO; - if (MHD_NO == may_block) - { - timeout.tv_usec = 0; - timeout.tv_sec = 0; - tv = &timeout; - } - else if ( (MHD_TM_THREAD_PER_CONNECTION != daemon->threading_mode) && - (MHD_SC_OK == - MHD_daemon_get_timeout (daemon, - &ltimeout)) ) - { - /* ltimeout is in ms */ - timeout.tv_usec = (ltimeout % 1000) * 1000; - if (ltimeout / 1000 > TIMEVAL_TV_SEC_MAX) - timeout.tv_sec = TIMEVAL_TV_SEC_MAX; - else - timeout.tv_sec = (_MHD_TIMEVAL_TV_SEC_TYPE) (ltimeout / 1000); - tv = &timeout; - } - num_ready = MHD_SYS_select_ (maxsock + 1, - &rs, - &ws, - &es, - tv); - if (daemon->shutdown) - return MHD_SC_DAEMON_ALREADY_SHUTDOWN; - if (num_ready < 0) - { - const int err = MHD_socket_get_error_ (); - - if (MHD_SCKT_ERR_IS_EINTR_ (err)) - return sc; -#ifdef HAVE_MESSAGES - MHD_DLOG (daemon, - MHD_SC_UNEXPECTED_SELECT_ERROR, - _ ("select failed: %s\n"), - MHD_socket_strerr_ (err)); -#endif - return MHD_SC_UNEXPECTED_SELECT_ERROR; - } - if (MHD_SC_OK != - (sc2 = internal_run_from_select (daemon, - &rs, - &ws, - &es))) - return sc2; - return sc; -} - -/* end of daemon_select.c */ diff --git a/src/lib/daemon_select.h b/src/lib/daemon_select.h @@ -1,56 +0,0 @@ -/* - This file is part of libmicrohttpd - Copyright (C) 2007-2018 Daniel Pittman and Christian Grothoff - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. - - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with this library; if not, write to the Free Software - Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA -*/ - -/** - * @file lib/daemon_select.h - * @brief non-public functions provided by daemon_select.c - * @author Christian Grothoff - */ - -#ifndef DAEMON_SELECT_H -#define DAEMON_SELECT_H - -/** - * Main internal select() call. Will compute select sets, call - * select() and then #internal_run_from_select() with the result. - * - * @param daemon daemon to run select() loop for - * @param may_block #MHD_YES if blocking, #MHD_NO if non-blocking - * @return #MHD_SC_OK on success - */ -enum MHD_StatusCode -MHD_daemon_select_ (struct MHD_Daemon *daemon, - int may_block) -MHD_NONNULL (1); - - -#if defined(HTTPS_SUPPORT) && defined(UPGRADE_SUPPORT) -/** - * Process upgraded connection with a select loop. - * We are in our own thread, only processing @a con - * - * @param con connection to process - */ -void -MHD_daemon_upgrade_connection_with_select_ (struct MHD_Connection *con) -MHD_NONNULL (1); - -#endif - -#endif diff --git a/src/lib/daemon_start.c b/src/lib/daemon_start.c @@ -1,975 +0,0 @@ -/* - This file is part of libmicrohttpd - Copyright (C) 2007-2018 Daniel Pittman and Christian Grothoff - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. - - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with this library; if not, write to the Free Software - Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA -*/ - -/** - * @file lib/daemon_start.c - * @brief functions to start a daemon - * @author Christian Grothoff - */ -#include "internal.h" -#include "connection_cleanup.h" -#include "daemon_close_all_connections.h" -#include "daemon_select.h" -#include "daemon_poll.h" -#include "daemon_epoll.h" -#include "request_resume.h" - - -/** - * Set listen socket options to allow port rebinding (or not) - * depending on how MHD was configured. - * - * @param[in,out] daemon the daemon with the listen socket to configure - * @return #MHD_SC_OK on success (or non-fatal errors) - */ -static enum MHD_StatusCode -configure_listen_reuse (struct MHD_Daemon *daemon) -{ - const MHD_SCKT_OPT_BOOL_ on = 1; - - /* Apply the socket options according to - listening_address_reuse. */ - if (daemon->allow_address_reuse) - { - /* User requested to allow reusing listening address:port. */ -#ifndef MHD_WINSOCK_SOCKETS - /* Use SO_REUSEADDR on non-W32 platforms, and do not fail if - * it doesn't work. */ - if (0 > setsockopt (daemon->listen_socket, - SOL_SOCKET, - SO_REUSEADDR, - (void *) &on, - sizeof (on))) - { -#ifdef HAVE_MESSAGES - MHD_DLOG (daemon, - MHD_SC_LISTEN_ADDRESS_REUSE_ENABLE_FAILED, - _ ("setsockopt failed: %s\n"), - MHD_socket_last_strerr_ ()); -#endif - return MHD_SC_LISTEN_ADDRESS_REUSE_ENABLE_FAILED; - } - return MHD_SC_OK; -#endif /* ! MHD_WINSOCK_SOCKETS */ - /* Use SO_REUSEADDR on Windows and SO_REUSEPORT on most platforms. - * Fail if SO_REUSEPORT is not defined or setsockopt fails. - */ - /* SO_REUSEADDR on W32 has the same semantics - as SO_REUSEPORT on BSD/Linux */ -#if defined(MHD_WINSOCK_SOCKETS) || defined(SO_REUSEPORT) - if (0 > setsockopt (daemon->listen_socket, - SOL_SOCKET, -#ifndef MHD_WINSOCK_SOCKETS - SO_REUSEPORT, -#else /* MHD_WINSOCK_SOCKETS */ - SO_REUSEADDR, -#endif /* MHD_WINSOCK_SOCKETS */ - (void *) &on, - sizeof (on))) - { -#ifdef HAVE_MESSAGES - MHD_DLOG (daemon, - MHD_SC_LISTEN_ADDRESS_REUSE_ENABLE_FAILED, - _ ("setsockopt failed: %s\n"), - MHD_socket_last_strerr_ ()); -#endif - return MHD_SC_LISTEN_ADDRESS_REUSE_ENABLE_FAILED; - } - return MHD_SC_OK; -#else /* !MHD_WINSOCK_SOCKETS && !SO_REUSEPORT */ - /* we're supposed to allow address:port re-use, but - on this platform we cannot; fail hard */ -#ifdef HAVE_MESSAGES - MHD_DLOG (daemon, - MHD_SC_LISTEN_ADDRESS_REUSE_ENABLE_NOT_SUPPORTED, - _ ( - "Cannot allow listening address reuse: SO_REUSEPORT not defined.\n")); -#endif - return MHD_SC_LISTEN_ADDRESS_REUSE_ENABLE_NOT_SUPPORTED; -#endif /* !MHD_WINSOCK_SOCKETS && !SO_REUSEPORT */ - } - - /* if (! daemon->allow_address_reuse) */ - /* User requested to disallow reusing listening address:port. - * Do nothing except for Windows where SO_EXCLUSIVEADDRUSE - * is used and Solaris with SO_EXCLBIND. - * Fail if MHD was compiled for W32 without SO_EXCLUSIVEADDRUSE - * or setsockopt fails. - */ -#if (defined(MHD_WINSOCK_SOCKETS) && defined(SO_EXCLUSIVEADDRUSE)) || \ - (defined(__sun) && defined(SO_EXCLBIND)) - if (0 > setsockopt (daemon->listen_socket, - SOL_SOCKET, -#ifdef SO_EXCLUSIVEADDRUSE - SO_EXCLUSIVEADDRUSE, -#else /* SO_EXCLBIND */ - SO_EXCLBIND, -#endif /* SO_EXCLBIND */ - (void *) &on, - sizeof (on))) - { -#ifdef HAVE_MESSAGES - MHD_DLOG (daemon, - MHD_SC_LISTEN_ADDRESS_REUSE_DISABLE_FAILED, - _ ("setsockopt failed: %s\n"), - MHD_socket_last_strerr_ ()); -#endif - return MHD_SC_LISTEN_ADDRESS_REUSE_DISABLE_FAILED; - } - return MHD_SC_OK; -#elif defined(MHD_WINSOCK_SOCKETS) /* SO_EXCLUSIVEADDRUSE not defined on W32? */ -#ifdef HAVE_MESSAGES - MHD_DLOG (daemon, - MHD_SC_LISTEN_ADDRESS_REUSE_DISABLE_NOT_SUPPORTED, - _ ( - "Cannot disallow listening address reuse: SO_EXCLUSIVEADDRUSE not defined.\n")); -#endif - return MHD_SC_LISTEN_ADDRESS_REUSE_DISABLE_NOT_SUPPORTED; -#endif /* MHD_WINSOCK_SOCKETS */ - /* Not on WINSOCK, simply doing nothing will do */ - return MHD_SC_OK; -} - - -/** - * Open, configure and bind the listen socket (if required). - * - * @param[in,out] daemon daemon to open the socket for - * @return #MHD_SC_OK on success - */ -static enum MHD_StatusCode -open_listen_socket (struct MHD_Daemon *daemon) -{ - enum MHD_StatusCode sc; - socklen_t addrlen; - struct sockaddr_storage ss; - const struct sockaddr *sa; - int pf; - bool use_v6; - - if (MHD_INVALID_SOCKET != daemon->listen_socket) - return MHD_SC_OK; /* application opened it for us! */ - pf = -1; - /* Determine address family */ - switch (daemon->listen_af) - { - case MHD_AF_NONE: - if (0 == daemon->listen_sa_len) - { - /* no listening desired, that's OK */ - return MHD_SC_OK; - } - /* we have a listen address, get AF from there! */ - switch (daemon->listen_sa.ss_family) - { - case AF_INET: - pf = PF_INET; - use_v6 = false; - break; -#ifdef AF_INET6 - case AF_INET6: - pf = PF_INET6; - use_v6 = true; - break; -#endif -#ifdef AF_UNIX - case AF_UNIX: - pf = PF_UNIX; - use_v6 = false; - break; -#endif - default: - return MHD_SC_AF_NOT_SUPPORTED_BY_BUILD; - } /* switch on ss_family */ - break; /* MHD_AF_NONE */ - case MHD_AF_AUTO: -#ifdef HAVE_INET6 - pf = PF_INET6; - use_v6 = true; -#else - pf = PF_INET; - use_v6 = false; -#endif - break; - case MHD_AF_INET4: - use_v6 = false; - pf = PF_INET; - break; - case MHD_AF_INET6: - case MHD_AF_DUAL: -#ifdef HAVE_INET6 - pf = PF_INET6; - use_v6 = true; - break; -#else -#ifdef HAVE_MESSAGES - MHD_DLOG (daemon, - MHD_SC_IPV6_NOT_SUPPORTED_BY_BUILD, - _ ("IPv6 not supported by this build.\n")); -#endif - return MHD_SC_IPV6_NOT_SUPPORTED_BY_BUILD; -#endif - } - mhd_assert (-1 != pf); - /* try to open listen socket */ -try_open_listen_socket: - daemon->listen_socket = MHD_socket_create_listen_ (pf); - if ( (MHD_INVALID_SOCKET == daemon->listen_socket) && - (MHD_AF_AUTO == daemon->listen_af) && - (use_v6) ) - { - use_v6 = false; - pf = PF_INET; - goto try_open_listen_socket; - } - if (MHD_INVALID_SOCKET == daemon->listen_socket) - { -#ifdef HAVE_MESSAGES - MHD_DLOG (daemon, - MHD_SC_FAILED_TO_OPEN_LISTEN_SOCKET, - _ ("Failed to create socket for listening: %s\n"), - MHD_socket_last_strerr_ ()); -#endif - return MHD_SC_FAILED_TO_OPEN_LISTEN_SOCKET; - } - - if (MHD_SC_OK != - (sc = configure_listen_reuse (daemon))) - return sc; - - /* configure for dual stack (or not) */ - if (use_v6) - { -#if defined IPPROTO_IPV6 && defined IPV6_V6ONLY - /* Note: "IPV6_V6ONLY" is declared by Windows Vista ff., see "IPPROTO_IPV6 Socket Options" - (http://msdn.microsoft.com/en-us/library/ms738574%28v=VS.85%29.aspx); - and may also be missing on older POSIX systems; good luck if you have any of those, - your IPv6 socket may then also bind against IPv4 anyway... */ - const MHD_SCKT_OPT_BOOL_ v6_only = - (MHD_AF_INET6 == daemon->listen_af); - if (0 > setsockopt (daemon->listen_socket, - IPPROTO_IPV6, - IPV6_V6ONLY, - (const void *) &v6_only, - sizeof (v6_only))) - { -#ifdef HAVE_MESSAGES - MHD_DLOG (daemon, - MHD_SC_LISTEN_DUAL_STACK_CONFIGURATION_FAILED, - _ ("setsockopt failed: %s\n"), - MHD_socket_last_strerr_ ()); -#endif - } -#else -#ifdef HAVE_MESSAGES - MHD_DLOG (daemon, - MHD_SC_LISTEN_DUAL_STACK_CONFIGURATION_NOT_SUPPORTED, - _ ( - "Cannot explicitly setup dual stack behavior on this platform.\n")); -#endif -#endif - } - - /* Determine address to bind to */ - if (0 != daemon->listen_sa_len) - { - /* Bind address explicitly given */ - sa = (const struct sockaddr *) &daemon->listen_sa; - addrlen = daemon->listen_sa_len; - } - else - { - /* Compute bind address based on port and AF */ -#ifdef HAVE_INET6 - if (use_v6) - { -#ifdef IN6ADDR_ANY_INIT - static const struct in6_addr static_in6any = IN6ADDR_ANY_INIT; -#endif - struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *) &ss; - - addrlen = sizeof (struct sockaddr_in6); - memset (sin6, - 0, - sizeof (struct sockaddr_in6)); - sin6->sin6_family = AF_INET6; - sin6->sin6_port = htons (daemon->listen_port); -#ifdef IN6ADDR_ANY_INIT - sin6->sin6_addr = static_in6any; -#endif -#if HAVE_STRUCT_SOCKADDR_IN6_SIN6_LEN - sin6->sin6_len = sizeof (struct sockaddr_in6); -#endif - } - else -#endif - { - struct sockaddr_in *sin4 = (struct sockaddr_in *) &ss; - - addrlen = sizeof (struct sockaddr_in); - memset (sin4, - 0, - sizeof (struct sockaddr_in)); - sin4->sin_family = AF_INET; - sin4->sin_port = htons (daemon->listen_port); - if (0 != INADDR_ANY) - sin4->sin_addr.s_addr = htonl (INADDR_ANY); -#if HAVE_STRUCT_SOCKADDR_IN_SIN_LEN - sin4->sin_len = sizeof (struct sockaddr_in); -#endif - } - sa = (const struct sockaddr *) &ss; - } - - /* actually do the bind() */ - if (-1 == bind (daemon->listen_socket, - sa, - addrlen)) - { -#ifdef HAVE_MESSAGES - unsigned int port = 0; - - switch (sa->sa_family) - { - case AF_INET: - if (addrlen == sizeof (struct sockaddr_in)) - port = ntohs (((const struct sockaddr_in *) sa)->sin_port); - else - port = UINT16_MAX + 1; /* indicate size error */ - break; - case AF_INET6: - if (addrlen == sizeof (struct sockaddr_in6)) - port = ntohs (((const struct sockaddr_in6 *) sa)->sin6_port); - else - port = UINT16_MAX + 1; /* indicate size error */ - break; - default: - port = UINT_MAX; /* AF_UNIX? */ - break; - } - MHD_DLOG (daemon, - MHD_SC_LISTEN_SOCKET_BIND_FAILED, - _ ("Failed to bind to port %u: %s\n"), - port, - MHD_socket_last_strerr_ ()); -#endif - return MHD_SC_LISTEN_SOCKET_BIND_FAILED; - } - - /* setup TCP_FASTOPEN */ -#ifdef TCP_FASTOPEN - if (MHD_FOM_DISABLE != daemon->fast_open_method) - { - if (0 != setsockopt (daemon->listen_socket, - IPPROTO_TCP, - TCP_FASTOPEN, - &daemon->fo_queue_length, - sizeof (daemon->fo_queue_length))) - { -#ifdef HAVE_MESSAGES - MHD_DLOG (daemon, - MHD_SC_FAST_OPEN_FAILURE, - _ ("setsockopt failed: %s\n"), - MHD_socket_last_strerr_ ()); -#endif - if (MHD_FOM_REQUIRE == daemon->fast_open_method) - return MHD_SC_FAST_OPEN_FAILURE; - } - } -#endif - - /* setup listening */ - if (0 > listen (daemon->listen_socket, - daemon->listen_backlog)) - { -#ifdef HAVE_MESSAGES - MHD_DLOG (daemon, - MHD_SC_LISTEN_FAILURE, - _ ("Failed to listen for connections: %s\n"), - MHD_socket_last_strerr_ ()); -#endif - return MHD_SC_LISTEN_FAILURE; - } - return MHD_SC_OK; -} - - -/** - * Obtain the listen port number from the socket (if it - * was not explicitly set by us, i.e. if we were given - * a listen socket or if the port was 0 and the OS picked - * a free one). - * - * @param[in,out] daemon daemon to obtain the port number for - */ -static void -get_listen_port_number (struct MHD_Daemon *daemon) -{ - struct sockaddr_storage servaddr; - socklen_t addrlen; - - if ( (0 != daemon->listen_port) || - (MHD_INVALID_SOCKET == daemon->listen_socket) ) - return; /* nothing to be done */ - - memset (&servaddr, - 0, - sizeof (struct sockaddr_storage)); - addrlen = sizeof (servaddr); - if (0 != getsockname (daemon->listen_socket, - (struct sockaddr *) &servaddr, - &addrlen)) - { -#ifdef HAVE_MESSAGES - MHD_DLOG (daemon, - MHD_SC_LISTEN_PORT_INTROSPECTION_FAILURE, - _ ("Failed to get listen port number: %s\n"), - MHD_socket_last_strerr_ ()); -#endif /* HAVE_MESSAGES */ - return; - } -#ifdef MHD_POSIX_SOCKETS - if (sizeof (servaddr) < addrlen) - { - /* should be impossible with `struct sockaddr_storage` */ -#ifdef HAVE_MESSAGES - MHD_DLOG (daemon, - MHD_SC_LISTEN_PORT_INTROSPECTION_FAILURE, - _ ( - "Failed to get listen port number (`struct sockaddr_storage` too small!?).\n")); -#endif /* HAVE_MESSAGES */ - return; - } -#endif /* MHD_POSIX_SOCKETS */ - switch (servaddr.ss_family) - { - case AF_INET: - { - struct sockaddr_in *s4 = (struct sockaddr_in *) &servaddr; - - daemon->listen_port = ntohs (s4->sin_port); - break; - } -#ifdef HAVE_INET6 - case AF_INET6: - { - struct sockaddr_in6 *s6 = (struct sockaddr_in6 *) &servaddr; - - daemon->listen_port = ntohs (s6->sin6_port); - break; - } -#endif /* HAVE_INET6 */ -#ifdef AF_UNIX - case AF_UNIX: - daemon->listen_port = 0; /* special value for UNIX domain sockets */ - break; -#endif - default: -#ifdef HAVE_MESSAGES - MHD_DLOG (daemon, - MHD_SC_LISTEN_PORT_INTROSPECTION_UNKNOWN_AF, - _ ("Unknown address family!\n")); -#endif - daemon->listen_port = 0; /* ugh */ - break; - } -} - - -#ifdef EPOLL_SUPPORT -/** - * Setup file descriptor to be used for epoll() control. - * - * @param daemon the daemon to setup epoll FD for - * @return the epoll() fd to use - */ -static int -setup_epoll_fd (struct MHD_Daemon *daemon) -{ - int fd; - -#ifndef HAVE_MESSAGES - (void) daemon; /* Mute compiler warning. */ -#endif /* ! HAVE_MESSAGES */ - -#ifdef USE_EPOLL_CREATE1 - fd = epoll_create1 (EPOLL_CLOEXEC); -#else /* ! USE_EPOLL_CREATE1 */ - fd = epoll_create (MAX_EVENTS); -#endif /* ! USE_EPOLL_CREATE1 */ - if (MHD_INVALID_SOCKET == fd) - { -#ifdef HAVE_MESSAGES - MHD_DLOG (daemon, - MHD_SC_EPOLL_CTL_CREATE_FAILED, - _ ("Call to epoll_create1 failed: %s\n"), - MHD_socket_last_strerr_ ()); -#endif - return MHD_INVALID_SOCKET; - } -#if ! defined(USE_EPOLL_CREATE1) - if (! MHD_socket_noninheritable_ (fd)) - { -#ifdef HAVE_MESSAGES - MHD_DLOG (daemon, - MHD_SC_EPOLL_CTL_CONFIGURE_NOINHERIT_FAILED, - _ ("Failed to set noninheritable mode on epoll FD.\n")); -#endif - } -#endif /* ! USE_EPOLL_CREATE1 */ - return fd; -} - - -/** - * Setup epoll() FD for the daemon and initialize it to listen - * on the listen FD. - * @remark To be called only from thread that process - * daemon's select()/poll()/etc. - * - * @param daemon daemon to initialize for epoll() - * @return #MHD_SC_OK on success - */ -static enum MHD_StatusCode -setup_epoll_to_listen (struct MHD_Daemon *daemon) -{ - struct epoll_event event; - MHD_socket ls; - - /* FIXME: update function! */ - daemon->epoll_fd = setup_epoll_fd (daemon); - if (-1 == daemon->epoll_fd) - return MHD_SC_EPOLL_CTL_CREATE_FAILED; -#if defined(HTTPS_SUPPORT) && defined(UPGRADE_SUPPORT) - if (! daemon->disallow_upgrade) - { - daemon->epoll_upgrade_fd = setup_epoll_fd (daemon); - if (MHD_INVALID_SOCKET == daemon->epoll_upgrade_fd) - return MHD_SC_EPOLL_CTL_CREATE_FAILED; - } -#endif /* HTTPS_SUPPORT && UPGRADE_SUPPORT */ - if ( (MHD_INVALID_SOCKET == (ls = daemon->listen_socket)) || - (daemon->was_quiesced) ) - return MHD_SC_OK; /* non-listening daemon */ - event.events = EPOLLIN; - event.data.ptr = daemon; - if (0 != epoll_ctl (daemon->epoll_fd, - EPOLL_CTL_ADD, - ls, - &event)) - { -#ifdef HAVE_MESSAGES - MHD_DLOG (daemon, - MHD_SC_EPOLL_CTL_ADD_FAILED, - _ ("Call to epoll_ctl failed: %s\n"), - MHD_socket_last_strerr_ ()); -#endif - return MHD_SC_EPOLL_CTL_ADD_FAILED; - } - daemon->listen_socket_in_epoll = true; - if (MHD_ITC_IS_VALID_ (daemon->itc)) - { - event.events = EPOLLIN; - event.data.ptr = (void *) daemon->epoll_itc_marker; - if (0 != epoll_ctl (daemon->epoll_fd, - EPOLL_CTL_ADD, - MHD_itc_r_fd_ (daemon->itc), - &event)) - { -#ifdef HAVE_MESSAGES - MHD_DLOG (daemon, - MHD_SC_EPOLL_CTL_ADD_FAILED, - _ ("Call to epoll_ctl failed: %s\n"), - MHD_socket_last_strerr_ ()); -#endif - return MHD_SC_EPOLL_CTL_ADD_FAILED; - } - } - return MHD_SC_OK; -} - - -#endif - - -/** - * Thread that runs the polling loop until the daemon - * is explicitly shut down. - * - * @param cls `struct MHD_Deamon` to run select loop in a thread for - * @return always 0 (on shutdown) - */ -static MHD_THRD_RTRN_TYPE_ MHD_THRD_CALL_SPEC_ -MHD_polling_thread (void *cls) -{ - struct MHD_Daemon *daemon = cls; - - MHD_thread_init_ (&daemon->pid); - while (! daemon->shutdown) - { - switch (daemon->event_loop_syscall) - { - case MHD_ELS_AUTO: - MHD_PANIC ("MHD_ELS_AUTO should have been mapped to preferred style.\n"); - break; - case MHD_ELS_SELECT: - MHD_daemon_select_ (daemon, - MHD_YES); - break; - case MHD_ELS_POLL: -#ifdef HAVE_POLL - MHD_daemon_poll_ (daemon, - MHD_YES); -#else - MHD_PANIC ("MHD_ELS_POLL not supported, should have failed earlier.\n"); -#endif - break; - case MHD_ELS_EPOLL: -#ifdef EPOLL_SUPPORT - MHD_daemon_epoll_ (daemon, - MHD_YES); -#else - MHD_PANIC ("MHD_ELS_EPOLL not supported, should have failed earlier.\n"); -#endif - break; - } - MHD_connection_cleanup_ (daemon); - } - /* Resume any pending for resume connections, join - * all connection's threads (if any) and finally cleanup - * everything. */ - if (! daemon->disallow_suspend_resume) - MHD_resume_suspended_connections_ (daemon); - MHD_daemon_close_all_connections_ (daemon); - - return (MHD_THRD_RTRN_TYPE_) 0; -} - - -/** - * Setup the thread pool (if needed). - * - * @param[in,out] daemon daemon to setup thread pool for - * @return #MHD_SC_OK on success - */ -static enum MHD_StatusCode -setup_thread_pool (struct MHD_Daemon *daemon) -{ - /* Coarse-grained count of connections per thread (note error - * due to integer division). Also keep track of how many - * connections are leftover after an equal split. */ - unsigned int conns_per_thread = daemon->global_connection_limit - / daemon->threading_mode; - unsigned int leftover_conns = daemon->global_connection_limit - % daemon->threading_mode; - int i; - enum MHD_StatusCode sc; - - /* Allocate memory for pooled objects */ - daemon->worker_pool = MHD_calloc_ (daemon->threading_mode, - sizeof (struct MHD_Daemon)); - if (NULL == daemon->worker_pool) - return MHD_SC_THREAD_POOL_MALLOC_FAILURE; - - /* Start the workers in the pool */ - for (i = 0; i < daemon->threading_mode; i++) - { - /* Create copy of the Daemon object for each worker */ - struct MHD_Daemon *d = &daemon->worker_pool[i]; - - memcpy (d, - daemon, - sizeof (struct MHD_Daemon)); - /* Adjust pooling params for worker daemons; note that memcpy() - has already copied MHD_USE_INTERNAL_POLLING_THREAD thread mode into - the worker threads. */ - d->master = daemon; - d->worker_pool_size = 0; - d->worker_pool = NULL; - /* Divide available connections evenly amongst the threads. - * Thread indexes in [0, leftover_conns) each get one of the - * leftover connections. */ - d->global_connection_limit = conns_per_thread; - if (((unsigned int) i) < leftover_conns) - ++d->global_connection_limit; - - if (! daemon->disable_itc) - { - if (! MHD_itc_init_ (d->itc)) - { -#ifdef HAVE_MESSAGES - MHD_DLOG (daemon, - MHD_SC_ITC_INITIALIZATION_FAILED, - _ ( - "Failed to create worker inter-thread communication channel: %s\n"), - MHD_itc_last_strerror_ () ); -#endif - sc = MHD_SC_ITC_INITIALIZATION_FAILED; - goto thread_failed; - } - if ( (MHD_ELS_SELECT == daemon->event_loop_syscall) && - (! MHD_SCKT_FD_FITS_FDSET_ (MHD_itc_r_fd_ (d->itc), - NULL)) ) - { -#ifdef HAVE_MESSAGES - MHD_DLOG (daemon, - MHD_SC_ITC_DESCRIPTOR_TOO_LARGE, - _ ( - "File descriptor for inter-thread communication channel exceeds maximum value.\n")); -#endif - MHD_itc_destroy_chk_ (d->itc); - sc = MHD_SC_ITC_DESCRIPTOR_TOO_LARGE; - goto thread_failed; - } - } - else - { - MHD_itc_set_invalid_ (d->itc); - } - -#ifdef EPOLL_SUPPORT - if ( (MHD_ELS_EPOLL == daemon->event_loop_syscall) && - (MHD_SC_OK != (sc = setup_epoll_to_listen (d))) ) - goto thread_failed; -#endif - - /* Must init cleanup connection mutex for each worker */ - if (! MHD_mutex_init_ (&d->cleanup_connection_mutex)) - { -#ifdef HAVE_MESSAGES - MHD_DLOG (daemon, - MHD_SC_THREAD_POOL_CREATE_MUTEX_FAILURE, - _ ("MHD failed to initialize cleanup connection mutex.\n")); -#endif - if (! daemon->disable_itc) - MHD_itc_destroy_chk_ (d->itc); - sc = MHD_SC_THREAD_POOL_CREATE_MUTEX_FAILURE; - goto thread_failed; - } - - /* Spawn the worker thread */ - if (! MHD_create_named_thread_ (&d->pid, - "MHD-worker", - daemon->thread_stack_limit_b, - &MHD_polling_thread, - d)) - { -#ifdef HAVE_MESSAGES - MHD_DLOG (daemon, - MHD_SC_THREAD_POOL_LAUNCH_FAILURE, - _ ("Failed to create pool thread: %s\n"), - MHD_strerror_ (errno)); -#endif - /* Free memory for this worker; cleanup below handles - * all previously-created workers. */ - if (! daemon->disable_itc) - MHD_itc_destroy_chk_ (d->itc); - MHD_mutex_destroy_chk_ (&d->cleanup_connection_mutex); - sc = MHD_SC_THREAD_POOL_LAUNCH_FAILURE; - goto thread_failed; - } - } /* end for() */ - return MHD_SC_OK; - -thread_failed: - /* If no worker threads created, then shut down normally. Calling - MHD_stop_daemon (as we do below) doesn't work here since it - assumes a 0-sized thread pool means we had been in the default - MHD_USE_INTERNAL_POLLING_THREAD mode. */ - if (0 == i) - { - if (NULL != daemon->worker_pool) - { - free (daemon->worker_pool); - daemon->worker_pool = NULL; - } - return MHD_SC_THREAD_LAUNCH_FAILURE; - } - /* Shutdown worker threads we've already created. Pretend - as though we had fully initialized our daemon, but - with a smaller number of threads than had been - requested. */ - daemon->worker_pool_size = i; - daemon->listen_socket = MHD_daemon_quiesce (daemon); - return sc; -} - - -/** - * Start a webserver. - * - * @param daemon daemon to start; you can no longer set - * options on this daemon after this call! - * @return #MHD_SC_OK on success - * @ingroup event - */ -enum MHD_StatusCode -MHD_daemon_start (struct MHD_Daemon *daemon) -{ - enum MHD_StatusCode sc; - - if (MHD_ELS_AUTO == daemon->event_loop_syscall) - { -#if EPOLL_SUPPORT - /* We do not support thread-per-connection in combination - with epoll, so use poll in this case, otherwise prefer - epoll. */ - if (MHD_TM_THREAD_PER_CONNECTION == daemon->threading_mode) - daemon->event_loop_syscall = MHD_ELS_POLL; - else - daemon->event_loop_syscall = MHD_ELS_EPOLL; -#elif defined(HAVE_POLL) - daemon->event_loop_syscall = MHD_ELS_POLL; -#else - daemon->event_loop_syscall = MHD_ELS_SELECT; -#endif - } - -#ifdef EPOLL_SUPPORT - if ( (MHD_ELS_EPOLL == daemon->event_loop_syscall) && - (0 == daemon->worker_pool_size) && - (MHD_INVALID_SOCKET != daemon->listen_socket) && - (MHD_TM_THREAD_PER_CONNECTION == daemon->threading_mode) ) - { -#ifdef HAVE_MESSAGES - MHD_DLOG (daemon, - MHD_SC_SYSCALL_THREAD_COMBINATION_INVALID, - _ ( - "Combining MHD_USE_THREAD_PER_CONNECTION and MHD_USE_EPOLL is not supported.\n")); -#endif - return MHD_SC_SYSCALL_THREAD_COMBINATION_INVALID; - } -#endif - - /* Setup ITC */ - if ( (! daemon->disable_itc) && - (0 == daemon->worker_pool_size) ) - { - if (! MHD_itc_init_ (daemon->itc)) - { -#ifdef HAVE_MESSAGES - MHD_DLOG (daemon, - MHD_SC_ITC_INITIALIZATION_FAILED, - _ ("Failed to create inter-thread communication channel: %s\n"), - MHD_itc_last_strerror_ ()); -#endif - return MHD_SC_ITC_INITIALIZATION_FAILED; - } - if ( (MHD_ELS_SELECT == daemon->event_loop_syscall) && - (! MHD_SCKT_FD_FITS_FDSET_ (MHD_itc_r_fd_ (daemon->itc), - NULL)) ) - { -#ifdef HAVE_MESSAGES - MHD_DLOG (daemon, - MHD_SC_ITC_DESCRIPTOR_TOO_LARGE, - _ ( - "File descriptor for inter-thread communication channel exceeds maximum value.\n")); -#endif - return MHD_SC_ITC_DESCRIPTOR_TOO_LARGE; - } - } - - if (MHD_SC_OK != (sc = open_listen_socket (daemon))) - return sc; - - /* Check listen socket is in range (if we are limited) */ - if ( (MHD_INVALID_SOCKET != daemon->listen_socket) && - (MHD_ELS_SELECT == daemon->event_loop_syscall) && - (! MHD_SCKT_FD_FITS_FDSET_ (daemon->listen_socket, - NULL)) ) - { -#ifdef HAVE_MESSAGES - MHD_DLOG (daemon, - MHD_SC_LISTEN_SOCKET_TOO_LARGE, - _ ("Socket descriptor larger than FD_SETSIZE: %d > %d\n"), - daemon->listen_socket, - FD_SETSIZE); -#endif - return MHD_SC_LISTEN_SOCKET_TOO_LARGE; - } - - /* set listen socket to non-blocking */ - if ( (MHD_INVALID_SOCKET != daemon->listen_socket) && - (! MHD_socket_nonblocking_ (daemon->listen_socket)) ) - { -#ifdef HAVE_MESSAGES - MHD_DLOG (daemon, - MHD_SC_LISTEN_SOCKET_NONBLOCKING_FAILURE, - _ ("Failed to set nonblocking mode on listening socket: %s\n"), - MHD_socket_last_strerr_ ()); -#endif - if ( (MHD_ELS_EPOLL == daemon->event_loop_syscall) || - (daemon->worker_pool_size > 0) ) - { - /* Accept must be non-blocking. Multiple children may wake - * up to handle a new connection, but only one will win the - * race. The others must immediately return. As this is - * not possible, we must fail hard here. */ - return MHD_SC_LISTEN_SOCKET_NONBLOCKING_FAILURE; - } - } - -#ifdef EPOLL_SUPPORT - /* Setup epoll */ - if ( (MHD_ELS_EPOLL == daemon->event_loop_syscall) && - (0 == daemon->worker_pool_size) && - (MHD_INVALID_SOCKET != daemon->listen_socket) && - (MHD_SC_OK != (sc = setup_epoll_to_listen (daemon))) ) - return sc; -#endif - - /* Setup main listen thread (only if we have no thread pool or - external event loop and do have a listen socket) */ - /* FIXME: why no worker thread if we have no listen socket? */ - if ( ( (MHD_TM_THREAD_PER_CONNECTION == daemon->threading_mode) || - (1 == daemon->threading_mode) ) && - (MHD_INVALID_SOCKET != daemon->listen_socket) && - (! MHD_create_named_thread_ (&daemon->pid, - (MHD_TM_THREAD_PER_CONNECTION == - daemon->threading_mode) - ? "MHD-listen" - : "MHD-single", - daemon->thread_stack_limit_b, - &MHD_polling_thread, - daemon) ) ) - { -#ifdef HAVE_MESSAGES - MHD_DLOG (daemon, - MHD_SC_THREAD_MAIN_LAUNCH_FAILURE, - _ ("Failed to create listen thread: %s\n"), - MHD_strerror_ (errno)); -#endif - return MHD_SC_THREAD_MAIN_LAUNCH_FAILURE; - } - - /* Setup worker threads */ - /* FIXME: why no thread pool if we have no listen socket? */ - if ( (1 < daemon->threading_mode) && - (MHD_INVALID_SOCKET != daemon->listen_socket) && - (MHD_SC_OK != (sc = setup_thread_pool (daemon))) ) - return sc; - - /* make sure we know our listen port (if any) */ - get_listen_port_number (daemon); - - return MHD_SC_OK; -} - - -/* end of daemon_start.c */ diff --git a/src/lib/init.c b/src/lib/init.c @@ -1,149 +0,0 @@ -/* - This file is part of libmicrohttpd - Copyright (C) 2007-2018 Daniel Pittman and Christian Grothoff - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. - - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with this library; if not, write to the Free Software - Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA -*/ - -/** - * @file lib/init.c - * @brief initialization routines - * @author Christian Grothoff - */ -#include "internal.h" -#include "init.h" - - -#ifndef _AUTOINIT_FUNCS_ARE_SUPPORTED -/** - * Track global initialisation - */ -volatile unsigned int global_init_count = 0; - -#ifdef MHD_MUTEX_STATIC_DEFN_INIT_ -/** - * Global initialisation mutex - */ -MHD_MUTEX_STATIC_DEFN_INIT_ (global_init_mutex_); -#endif /* MHD_MUTEX_STATIC_DEFN_INIT_ */ - -#endif - -#if defined(_WIN32) && ! defined(__CYGWIN__) -/** - * Track initialization of winsock - */ -static int mhd_winsock_inited_ = 0; -#endif - -/** - * Default implementation of the panic function, - * prints an error message and aborts. - * - * @param cls unused - * @param file name of the file with the problem - * @param line line number with the problem - * @param reason error message with details - */ -static void -mhd_panic_std (void *cls, - const char *file, - unsigned int line, - const char *reason) -{ - (void) cls; /* Mute compiler warning. */ -#ifdef HAVE_MESSAGES - fprintf (stderr, - _ ("Fatal error in GNU libmicrohttpd %s:%u: %s\n"), - file, - line, - reason); -#else /* ! HAVE_MESSAGES */ - (void) file; /* Mute compiler warning. */ - (void) line; /* Mute compiler warning. */ - (void) reason; /* Mute compiler warning. */ -#endif - abort (); -} - - -/** - * Globally initialize library. - */ -void -MHD_init (void) -{ -#if defined(_WIN32) && ! defined(__CYGWIN__) - WSADATA wsd; -#endif /* _WIN32 && ! __CYGWIN__ */ - - if (NULL == mhd_panic) - mhd_panic = &mhd_panic_std; - -#if defined(_WIN32) && ! defined(__CYGWIN__) - if (0 != WSAStartup (MAKEWORD (2, 2), - &wsd)) - MHD_PANIC (_ ("Failed to initialize winsock.\n")); - mhd_winsock_inited_ = 1; - if ( (2 != LOBYTE (wsd.wVersion)) && - (2 != HIBYTE (wsd.wVersion)) ) - MHD_PANIC (_ ("Winsock version 2.2 is not available.\n")); -#endif - MHD_monotonic_sec_counter_init (); -#ifdef HAVE_FREEBSD_SENDFILE - MHD_conn_init_static_ (); -#endif /* HAVE_FREEBSD_SENDFILE */ -} - - -/** - * Global teardown work. - */ -void -MHD_fini (void) -{ -#if defined(_WIN32) && ! defined(__CYGWIN__) - if (mhd_winsock_inited_) - WSACleanup (); -#endif - MHD_monotonic_sec_counter_finish (); -} - - -#ifdef _AUTOINIT_FUNCS_ARE_SUPPORTED - -_SET_INIT_AND_DEINIT_FUNCS (MHD_init, MHD_fini); - -#else - -/** - * Check whether global initialisation was performed - * and call initialiser if necessary. - */ -void -MHD_check_global_init_ (void) -{ -#ifdef MHD_MUTEX_STATIC_DEFN_INIT_ - MHD_mutex_lock_chk_ (&global_init_mutex_); -#endif /* MHD_MUTEX_STATIC_DEFN_INIT_ */ - if (0 == global_init_count++) - MHD_init (); -#ifdef MHD_MUTEX_STATIC_DEFN_INIT_ - MHD_mutex_unlock_chk_ (&global_init_mutex_); -#endif /* MHD_MUTEX_STATIC_DEFN_INIT_ */ -} - - -#endif diff --git a/src/lib/init.h b/src/lib/init.h @@ -1,46 +0,0 @@ -/* - This file is part of libmicrohttpd - Copyright (C) 2007-2018 Daniel Pittman and Christian Grothoff - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. - - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with this library; if not, write to the Free Software - Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA -*/ -/** - * @file lib/init.h - * @brief functions to initialize library - * @author Christian Grothoff - */ -#ifndef INIT_H -#define INIT_H - -#include "internal.h" - -#ifdef _AUTOINIT_FUNCS_ARE_SUPPORTED -/** - * Do nothing - global initialisation is - * performed by library constructor. - */ -#define MHD_check_global_init_() (void) 0 -#else /* ! _AUTOINIT_FUNCS_ARE_SUPPORTED */ -/** - * Check whether global initialisation was performed - * and call initialiser if necessary. - */ -void -MHD_check_global_init_ (void); - -#endif /* ! _AUTOINIT_FUNCS_ARE_SUPPORTED */ - - -#endif /* INIT_H */ diff --git a/src/lib/internal.c b/src/lib/internal.c @@ -1,288 +0,0 @@ -/* - This file is part of libmicrohttpd - Copyright (C) 2007-2018 Daniel Pittman and Christian Grothoff - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. - - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with this library; if not, write to the Free Software - Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA -*/ - -/** - * @file microhttpd/internal.c - * @brief internal shared structures - * @author Daniel Pittman - * @author Christian Grothoff - */ -#include "internal.h" -#include "mhd_str.h" - -#ifdef HAVE_MESSAGES -#if DEBUG_STATES -/** - * State to string dictionary. - */ -const char * -MHD_state_to_string (enum MHD_CONNECTION_STATE state) -{ - switch (state) - { - case MHD_CONNECTION_INIT: - return "connection init"; - case MHD_CONNECTION_REQ_LINE_RECEIVED: - return "connection url received"; - case MHD_CONNECTION_REQ_HEADERS_RECEIVING: - return "header receiving"; - case MHD_CONNECTION_HEADERS_RECEIVED: - return "headers received"; - case MHD_CONNECTION_HEADERS_PROCESSED: - return "headers processed"; - case MHD_CONNECTION_CONTINUE_SENDING: - return "continue sending"; - case MHD_CONNECTION_CONTINUE_SENT: - return "continue sent"; - case MHD_CONNECTION_BODY_RECEIVED: - return "body received"; - case MHD_CONNECTION_FOOTERS_RECEIVING: - return "footers receiving"; - case MHD_CONNECTION_FOOTERS_RECEIVED: - return "footers received"; - case MHD_CONNECTION_HEADERS_SENDING: - return "headers sending"; - case MHD_CONNECTION_HEADERS_SENT: - return "headers sent"; - case MHD_CONNECTION_NORMAL_BODY_READY: - return "normal body ready"; - case MHD_CONNECTION_NORMAL_BODY_UNREADY: - return "normal body unready"; - case MHD_CONNECTION_CHUNKED_BODY_READY: - return "chunked body ready"; - case MHD_CONNECTION_CHUNKED_BODY_UNREADY: - return "chunked body unready"; - case MHD_CONNECTION_BODY_SENT: - return "body sent"; - case MHD_CONNECTION_FOOTERS_SENDING: - return "footers sending"; - case MHD_CONNECTION_FOOTERS_SENT: - return "footers sent"; - case MHD_CONNECTION_CLOSED: - return "closed"; - default: - return "unrecognized connection state"; - } -} - - -#endif -#endif - - -#ifdef HAVE_MESSAGES -/** - * fprintf-like helper function for logging debug - * messages. - */ -void -MHD_DLOG (const struct MHD_Daemon *daemon, - enum MHD_StatusCode sc, - const char *format, - ...) -{ - va_list va; - - if (NULL == daemon->logger) - return; - va_start (va, - format); - daemon->logger (daemon->logger_cls, - sc, - format, - va); - va_end (va); -} - - -#endif - - -/** - * Convert all occurrences of '+' to ' '. - * - * @param arg string that is modified (in place), must be 0-terminated - */ -void -MHD_unescape_plus (char *arg) -{ - char *p; - - for (p = strchr (arg, '+'); NULL != p; p = strchr (p + 1, '+')) - *p = ' '; -} - - -/** - * Process escape sequences ('%HH') Updates val in place; the - * result should be UTF-8 encoded and cannot be larger than the input. - * The result must also still be 0-terminated. - * - * @param val value to unescape (modified in the process) - * @return length of the resulting val (strlen(val) maybe - * shorter afterwards due to elimination of escape sequences) - */ -size_t -MHD_http_unescape (char *val) -{ - char *rpos = val; - char *wpos = val; - - while ('\0' != *rpos) - { - uint32_t num; - switch (*rpos) - { - case '%': - if (2 == MHD_strx_to_uint32_n_ (rpos + 1, - 2, - &num)) - { - *wpos = (char) ((unsigned char) num); - wpos++; - rpos += 3; - break; - } - /* TODO: add bad sequence handling */ - /* intentional fall through! */ - default: - *wpos = *rpos; - wpos++; - rpos++; - } - } - *wpos = '\0'; /* add 0-terminator */ - return wpos - val; /* = strlen(val) */ -} - - -/** - * Parse and unescape the arguments given by the client - * as part of the HTTP request URI. - * - * @param request request to add headers to - * @param kind header kind to pass to @a cb - * @param[in,out] args argument URI string (after "?" in URI), - * clobbered in the process! - * @param cb function to call on each key-value pair found - * @param[out] num_headers set to the number of headers found - * @return false on failure (@a cb returned false), - * true for success (parsing succeeded, @a cb always - * returned true) - */ -bool -MHD_parse_arguments_ (struct MHD_Request *request, - enum MHD_ValueKind kind, - char *args, - MHD_ArgumentIterator_ cb, - unsigned int *num_headers) -{ - struct MHD_Daemon *daemon = request->daemon; - char *equals; - char *amper; - - *num_headers = 0; - while ( (NULL != args) && - ('\0' != args[0]) ) - { - equals = strchr (args, '='); - amper = strchr (args, '&'); - if (NULL == amper) - { - /* last argument */ - if (NULL == equals) - { - /* last argument, without '=' */ - MHD_unescape_plus (args); - daemon->unescape_cb (daemon->unescape_cb_cls, - request, - args); - if (! cb (request, - args, - NULL, - kind)) - return false; - (*num_headers)++; - break; - } - /* got 'foo=bar' */ - equals[0] = '\0'; - equals++; - MHD_unescape_plus (args); - daemon->unescape_cb (daemon->unescape_cb_cls, - request, - args); - MHD_unescape_plus (equals); - daemon->unescape_cb (daemon->unescape_cb_cls, - request, - equals); - if (! cb (request, - args, - equals, - kind)) - return false; - (*num_headers)++; - break; - } - /* amper is non-NULL here */ - amper[0] = '\0'; - amper++; - if ( (NULL == equals) || - (equals >= amper) ) - { - /* got 'foo&bar' or 'foo&bar=val', add key 'foo' with NULL for value */ - MHD_unescape_plus (args); - daemon->unescape_cb (daemon->unescape_cb_cls, - request, - args); - if (! cb (request, - args, - NULL, - kind)) - return false; - /* continue with 'bar' */ - (*num_headers)++; - args = amper; - continue; - } - /* equals and amper are non-NULL here, and equals < amper, - so we got regular 'foo=value&bar...'-kind of argument */ - equals[0] = '\0'; - equals++; - MHD_unescape_plus (args); - daemon->unescape_cb (daemon->unescape_cb_cls, - request, - args); - MHD_unescape_plus (equals); - daemon->unescape_cb (daemon->unescape_cb_cls, - request, - equals); - if (! cb (request, - args, - equals, - kind)) - return false; - (*num_headers)++; - args = amper; - } - return true; -} - - -/* end of internal.c */ diff --git a/src/lib/internal.h b/src/lib/internal.h @@ -1,1892 +0,0 @@ -/* - This file is part of libmicrohttpd - Copyright (C) 2007-2017 Daniel Pittman and Christian Grothoff - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. - - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with this library; if not, write to the Free Software - Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA -*/ - -/** - * @file lib/internal.h - * @brief internal shared structures - * @author Daniel Pittman - * @author Christian Grothoff - */ - -#ifndef INTERNAL_H -#define INTERNAL_H - -#include "mhd_options.h" -#include "platform.h" -#include "microhttpd2.h" -#include "microhttpd_tls.h" -#include "mhd_assert.h" -#include "mhd_compat.h" -#include "mhd_itc.h" -#include "mhd_mono_clock.h" -#include "memorypool.h" - -#ifdef HTTPS_SUPPORT -#include <gnutls/gnutls.h> -#if GNUTLS_VERSION_MAJOR >= 3 -#include <gnutls/abstract.h> -#endif -#endif /* HTTPS_SUPPORT */ - -#ifdef HAVE_STDBOOL_H -#include <stdbool.h> -#endif -#ifdef MHD_PANIC -/* Override any defined MHD_PANIC macro with proper one */ -#undef MHD_PANIC -#endif /* MHD_PANIC */ - -#ifdef HAVE_MESSAGES -/** - * Trigger 'panic' action based on fatal errors. - * - * @param msg error message (const char *) - */ -#define MHD_PANIC(msg) do { mhd_panic (mhd_panic_cls, __FILE__, __LINE__, msg); \ - BUILTIN_NOT_REACHED; } while (0) -#else -/** - * Trigger 'panic' action based on fatal errors. - * - * @param msg error message (const char *) - */ -#define MHD_PANIC(msg) do { mhd_panic (mhd_panic_cls, __FILE__, __LINE__, NULL); \ - BUILTIN_NOT_REACHED; } while (0) -#endif - -#include "mhd_threads.h" -#include "mhd_locks.h" -#include "mhd_sockets.h" -#include "mhd_str.h" -#include "mhd_itc_types.h" - - -#ifdef HAVE_MESSAGES -/** - * fprintf()-like helper function for logging debug - * messages. - */ -void -MHD_DLOG (const struct MHD_Daemon *daemon, - enum MHD_StatusCode sc, - const char *format, - ...); - -#endif - - -/** - * Close FD and abort execution if error is detected. - * @param fd the FD to close - */ -#define MHD_fd_close_chk_(fd) do { \ - if ( (0 != close ((fd)) && (EBADF == errno)) ) \ - MHD_PANIC (_ ("Failed to close FD.\n")); \ -} while (0) - -/** - * Should we perform additional sanity checks at runtime (on our internal - * invariants)? This may lead to aborts, but can be useful for debugging. - */ -#define EXTRA_CHECKS MHD_NO - -#define MHD_MAX(a,b) (((a)<(b)) ? (b) : (a)) -#define MHD_MIN(a,b) (((a)<(b)) ? (a) : (b)) - - -/** - * Minimum size by which MHD tries to increment read/write buffers. - * We usually begin with half the available pool space for the - * IO-buffer, but if absolutely needed we additively grow by the - * number of bytes given here (up to -- theoretically -- the full pool - * space). - */ -#define MHD_BUF_INC_SIZE 1024 - - -/** - * Handler for fatal errors. - */ -extern MHD_PanicCallback mhd_panic; - -/** - * Closure argument for "mhd_panic". - */ -extern void *mhd_panic_cls; - -/* If we have Clang or gcc >= 4.5, use __buildin_unreachable() */ -#if defined(__clang__) || (__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= \ - 5) -#define BUILTIN_NOT_REACHED __builtin_unreachable () -#elif defined(_MSC_FULL_VER) -#define BUILTIN_NOT_REACHED __assume (0) -#else -#define BUILTIN_NOT_REACHED -#endif - -#ifndef MHD_STATICSTR_LEN_ -/** - * Determine length of static string / macro strings at compile time. - */ -#define MHD_STATICSTR_LEN_(macro) (sizeof(macro) / sizeof(char) - 1) -#endif /* ! MHD_STATICSTR_LEN_ */ - - -/** - * Ability to use same connection for next request - */ -enum MHD_ConnKeepAlive -{ - /** - * Connection must be closed after sending response. - */ - MHD_CONN_MUST_CLOSE = -1, - - /** - * KeelAlive state is not yet determined - */ - MHD_CONN_KEEPALIVE_UNKOWN = 0, - - /** - * Connection can be used for serving next request - */ - MHD_CONN_USE_KEEPALIVE = 1 -}; - - -/** - * Function to receive plaintext data. - * - * @param conn the connection struct - * @param write_to where to write received data - * @param max_bytes maximum number of bytes to receive - * @return number of bytes written to @a write_to - */ -typedef ssize_t -(*ReceiveCallback) (struct MHD_Connection *conn, - void *write_to, - size_t max_bytes); - - -/** - * Function to transmit plaintext data. - * - * @param conn the connection struct - * @param read_from where to read data to transmit - * @param max_bytes maximum number of bytes to transmit - * @return number of bytes transmitted - */ -typedef ssize_t -(*TransmitCallback) (struct MHD_Connection *conn, - const void *read_from, - size_t max_bytes); - - -/** - * States in a state machine for a request. - * - * The main transitions are any-state to #MHD_REQUEST_CLOSED, any - * state to state+1, #MHD_REQUEST_FOOTERS_SENT to - * #MHD_REQUEST_INIT. #MHD_REQUEST_CLOSED is the terminal state - * and #MHD_REQUEST_INIT the initial state. - * - * Note that transitions for *reading* happen only after the input has - * been processed; transitions for *writing* happen after the - * respective data has been put into the write buffer (the write does - * not have to be completed yet). A transition to - * #MHD_REQUEST_CLOSED or #MHD_REQUEST_INIT requires the write - * to be complete. - */ -enum MHD_REQUEST_STATE // FIXME: fix capitalization! -{ - /** - * Request just started (no headers received). - * Waiting for the line with the request type, URL and version. - */ - MHD_REQUEST_INIT = 0, - - /** - * 1: We got the URL (and request type and version). Wait for a header line. - */ - MHD_REQUEST_URL_RECEIVED = MHD_REQUEST_INIT + 1, - - /** - * 2: We got part of a multi-line request header. Wait for the rest. - */ - MHD_REQUEST_HEADER_PART_RECEIVED = MHD_REQUEST_URL_RECEIVED + 1, - - /** - * 3: We got the request headers. Process them. - */ - MHD_REQUEST_HEADERS_RECEIVED = MHD_REQUEST_HEADER_PART_RECEIVED + 1, - - /** - * 4: We have processed the request headers. Send 100 continue. - */ - MHD_REQUEST_HEADERS_PROCESSED = MHD_REQUEST_HEADERS_RECEIVED + 1, - - /** - * 5: We have processed the headers and need to send 100 CONTINUE. - */ - MHD_REQUEST_CONTINUE_SENDING = MHD_REQUEST_HEADERS_PROCESSED + 1, - - /** - * 6: We have sent 100 CONTINUE (or do not need to). Read the message body. - */ - MHD_REQUEST_CONTINUE_SENT = MHD_REQUEST_CONTINUE_SENDING + 1, - - /** - * 7: We got the request body. Wait for a line of the footer. - */ - MHD_REQUEST_BODY_RECEIVED = MHD_REQUEST_CONTINUE_SENT + 1, - - /** - * 8: We got part of a line of the footer. Wait for the - * rest. - */ - MHD_REQUEST_FOOTER_PART_RECEIVED = MHD_REQUEST_BODY_RECEIVED + 1, - - /** - * 9: We received the entire footer. Wait for a response to be queued - * and prepare the response headers. - */ - MHD_REQUEST_FOOTERS_RECEIVED = MHD_REQUEST_FOOTER_PART_RECEIVED + 1, - - /** - * 10: We have prepared the response headers in the writ buffer. - * Send the response headers. - */ - MHD_REQUEST_HEADERS_SENDING = MHD_REQUEST_FOOTERS_RECEIVED + 1, - - /** - * 11: We have sent the response headers. Get ready to send the body. - */ - MHD_REQUEST_HEADERS_SENT = MHD_REQUEST_HEADERS_SENDING + 1, - - /** - * 12: We are ready to send a part of a non-chunked body. Send it. - */ - MHD_REQUEST_NORMAL_BODY_READY = MHD_REQUEST_HEADERS_SENT + 1, - - /** - * 13: We are waiting for the client to provide more - * data of a non-chunked body. - */ - MHD_REQUEST_NORMAL_BODY_UNREADY = MHD_REQUEST_NORMAL_BODY_READY + 1, - - /** - * 14: We are ready to send a chunk. - */ - MHD_REQUEST_CHUNKED_BODY_READY = MHD_REQUEST_NORMAL_BODY_UNREADY + 1, - - /** - * 15: We are waiting for the client to provide a chunk of the body. - */ - MHD_REQUEST_CHUNKED_BODY_UNREADY = MHD_REQUEST_CHUNKED_BODY_READY + 1, - - /** - * 16: We have sent the response body. Prepare the footers. - */ - MHD_REQUEST_BODY_SENT = MHD_REQUEST_CHUNKED_BODY_UNREADY + 1, - - /** - * 17: We have prepared the response footer. Send it. - */ - MHD_REQUEST_FOOTERS_SENDING = MHD_REQUEST_BODY_SENT + 1, - - /** - * 18: We have sent the response footer. Shutdown or restart. - */ - MHD_REQUEST_FOOTERS_SENT = MHD_REQUEST_FOOTERS_SENDING + 1, - - /** - * 19: This request is to be closed. - */ - MHD_REQUEST_CLOSED = MHD_REQUEST_FOOTERS_SENT + 1, - -#ifdef UPGRADE_SUPPORT - /** - * Request was "upgraded" and socket is now under the - * control of the application. - */ - MHD_REQUEST_UPGRADE -#endif /* UPGRADE_SUPPORT */ - -}; - - -/** - * Header or cookie in HTTP request or response. - */ -struct MHD_HTTP_Header -{ - /** - * Headers are kept in a linked list. - */ - struct MHD_HTTP_Header *next; - - /** - * The name of the header (key), without the colon. - */ - char *header; - - /** - * The value of the header. - */ - char *value; - - /** - * Type of the header (where in the HTTP protocol is this header - * from). - */ - enum MHD_ValueKind kind; - -}; - - -/** - * State kept for each HTTP request. - */ -struct MHD_Request -{ - - /** - * Reference to the `struct MHD_Daemon`. - */ - struct MHD_Daemon *daemon; - - /** - * Connection this request is associated with. - */ - struct MHD_Connection *connection; - - /** - * Response to return for this request, set once - * it is available. - */ - struct MHD_Response *response; - - /** - * Linked list of parsed headers. - */ - struct MHD_HTTP_Header *headers_received; - - /** - * Tail of linked list of parsed headers. - */ - struct MHD_HTTP_Header *headers_received_tail; - - /** - * We allow the main application to associate some pointer with the - * HTTP request, which is passed to each #MHD_AccessHandlerCallback - * and some other API calls. Here is where we store it. (MHD does - * not know or care what it is). - */ - void *client_context; - - /** - * Request method as string. Should be GET/POST/etc. Allocated in - * pool. - */ - char *method_s; - - /** - * Requested URL (everything after "GET" only). Allocated - * in pool. - */ - const char *url; - - /** - * HTTP version string (i.e. http/1.1). Allocated - * in pool. - */ - char *version_s; - - /** - * Close connection after sending response? - * Functions may change value from "Unknown" or "KeepAlive" to "Must close", - * but no functions reset value "Must Close" to any other value. - */ - enum MHD_ConnKeepAlive keepalive; - - /** - * Buffer for reading requests. Allocated in pool. Actually one - * byte larger than @e read_buffer_size (if non-NULL) to allow for - * 0-termination. - */ - char *read_buffer; - - /** - * Buffer for writing response (headers only). Allocated - * in pool. - */ - char *write_buffer; - - /** - * Last incomplete header line during parsing of headers. - * Allocated in pool. Only valid if state is - * either #MHD_REQUEST_HEADER_PART_RECEIVED or - * #MHD_REQUEST_FOOTER_PART_RECEIVED. - */ - char *last; - - /** - * Position after the colon on the last incomplete header - * line during parsing of headers. - * Allocated in pool. Only valid if state is - * either #MHD_REQUEST_HEADER_PART_RECEIVED or - * #MHD_REQUEST_FOOTER_PART_RECEIVED. - */ - char *colon; - -#ifdef UPGRADE_SUPPORT - /** - * If this connection was upgraded, this points to - * the upgrade response details such that the - * #thread_main_connection_upgrade()-logic can perform the - * bi-directional forwarding. - */ - struct MHD_UpgradeResponseHandle *urh; -#endif /* UPGRADE_SUPPORT */ - - /** - * Size of @e read_buffer (in bytes). This value indicates - * how many bytes we're willing to read into the buffer; - * the real buffer is one byte longer to allow for - * adding zero-termination (when needed). - */ - size_t read_buffer_size; - - /** - * Position where we currently append data in - * @e read_buffer (last valid position). - */ - size_t read_buffer_offset; - - /** - * Size of @e write_buffer (in bytes). - */ - size_t write_buffer_size; - - /** - * Offset where we are with sending from @e write_buffer. - */ - size_t write_buffer_send_offset; - - /** - * Last valid location in write_buffer (where do we - * append and up to where is it safe to send?) - */ - size_t write_buffer_append_offset; - - /** - * Number of bytes we had in the HTTP header, set once we - * pass #MHD_REQUEST_HEADERS_RECEIVED. - */ - size_t header_size; - - /** - * How many more bytes of the body do we expect - * to read? #MHD_SIZE_UNKNOWN for unknown. - */ - uint64_t remaining_upload_size; - - /** - * If we are receiving with chunked encoding, where are we right - * now? Set to 0 if we are waiting to receive the chunk size; - * otherwise, this is the size of the current chunk. A value of - * zero is also used when we're at the end of the chunks. - */ - uint64_t current_chunk_size; - - /** - * If we are receiving with chunked encoding, where are we currently - * with respect to the current chunk (at what offset / position)? - */ - uint64_t current_chunk_offset; - - /** - * Current write position in the actual response - * (excluding headers, content only; should be 0 - * while sending headers). - */ - uint64_t response_write_position; - - #if defined(_MHD_HAVE_SENDFILE) - // FIXME: document, fix capitalization! - enum MHD_resp_sender_ - { - MHD_resp_sender_std = 0, - MHD_resp_sender_sendfile - } resp_sender; -#endif /* _MHD_HAVE_SENDFILE */ - - /** - * Position in the 100 CONTINUE message that - * we need to send when receiving http 1.1 requests. - */ - size_t continue_message_write_offset; - - /** - * State in the FSM for this request. - */ - enum MHD_REQUEST_STATE state; - - /** - * HTTP method, as an enum. - */ - enum MHD_Method method; - - /** - * What is this request waiting for? - */ - enum MHD_RequestEventLoopInfo event_loop_info; - - /** - * Are we currently inside the "idle" handler (to avoid recursively - * invoking it). - */ - bool in_idle; - - /** - * Are we currently inside the "idle" handler (to avoid recursively - * invoking it). - */ - bool in_cleanup; - - /** - * Are we receiving with chunked encoding? This will be set to - * #MHD_YES after we parse the headers and are processing the body - * with chunks. After we are done with the body and we are - * processing the footers; once the footers are also done, this will - * be set to #MHD_NO again (before the final call to the handler). - */ - bool have_chunked_upload; -}; - - -/** - * State of the socket with respect to epoll (bitmask). - */ -enum MHD_EpollState -{ - - /** - * The socket is not involved with a defined state in epoll() right - * now. - */ - MHD_EPOLL_STATE_UNREADY = 0, - - /** - * epoll() told us that data was ready for reading, and we did - * not consume all of it yet. - */ - MHD_EPOLL_STATE_READ_READY = 1, - - /** - * epoll() told us that space was available for writing, and we did - * not consume all of it yet. - */ - MHD_EPOLL_STATE_WRITE_READY = 2, - - /** - * Is this connection currently in the 'eready' EDLL? - */ - MHD_EPOLL_STATE_IN_EREADY_EDLL = 4, - - /** - * Is this connection currently in the epoll() set? - */ - MHD_EPOLL_STATE_IN_EPOLL_SET = 8, - - /** - * Is this connection currently suspended? - */ - MHD_EPOLL_STATE_SUSPENDED = 16, - - /** - * Is this connection in some error state? - */ - MHD_EPOLL_STATE_ERROR = 128 -}; - - -/** - * State kept per HTTP connection. - */ -struct MHD_Connection -{ - -#ifdef EPOLL_SUPPORT - /** - * Next pointer for the EDLL listing connections that are epoll-ready. - */ - struct MHD_Connection *nextE; - - /** - * Previous pointer for the EDLL listing connections that are epoll-ready. - */ - struct MHD_Connection *prevE; -#endif - - /** - * Next pointer for the DLL describing our IO state. - */ - struct MHD_Connection *next; - - /** - * Previous pointer for the DLL describing our IO state. - */ - struct MHD_Connection *prev; - - /** - * Next pointer for the XDLL organizing connections by timeout. - * This DLL can be either the - * 'manual_timeout_head/manual_timeout_tail' or the - * 'normal_timeout_head/normal_timeout_tail', depending on whether a - * custom timeout is set for the connection. - */ - struct MHD_Connection *nextX; - - /** - * Previous pointer for the XDLL organizing connections by timeout. - */ - struct MHD_Connection *prevX; - - /** - * Reference to the MHD_Daemon struct. - */ - struct MHD_Daemon *daemon; - - /** - * The memory pool is created whenever we first read from the TCP - * stream and destroyed at the end of each request (and re-created - * for the next request). In the meantime, this pointer is NULL. - * The pool is used for all request-related data except for the - * response (which maybe shared between requests) and the IP - * address (which persists across individual requests). - */ - struct MemoryPool *pool; - - /** - * We allow the main application to associate some pointer with the - * TCP connection (which may span multiple HTTP requests). Here is - * where we store it. (MHD does not know or care what it is). - * The location is given to the #MHD_NotifyConnectionCallback and - * also accessible via #MHD_CONNECTION_INFO_SOCKET_CONTEXT. - */ - void *socket_context; - -#ifdef HTTPS_SUPPORT - /** - * State kept per TLS connection. Plugin-specific. - */ - struct MHD_TLS_ConnectionState *tls_cs; -#endif - - /** - * Function used for reading HTTP request stream. - */ - ReceiveCallback recv_cls; - - /** - * Function used for writing HTTP response stream. - */ - TransmitCallback send_cls; - - /** - * Information about the current request we are processing - * on this connection. - */ - struct MHD_Request request; - - /** - * Thread handle for this connection (if we are using - * one thread per connection). - */ - MHD_thread_handle_ID_ pid; - - /** - * Foreign address (of length @e addr_len). - */ - struct sockaddr_storage addr; - - /** - * Length of the foreign address. - */ - socklen_t addr_len; - - /** - * Last time this connection had any activity - * (reading or writing). - */ - time_t last_activity; - - /** - * After how many seconds of inactivity should - * this connection time out? Zero for no timeout. - */ - time_t connection_timeout; - - /** - * Socket for this connection. Set to #MHD_INVALID_SOCKET if - * this connection has died (daemon should clean - * up in that case). - */ - MHD_socket socket_fd; - -#ifdef EPOLL_SUPPORT - /** - * What is the state of this socket in relation to epoll? - */ - enum MHD_EpollState epoll_state; -#endif - - /** - * Is the connection suspended? - */ - bool suspended; - - /** - * Are we ready to read from TLS for this connection? - */ - bool tls_read_ready; - - /** - * Is the connection wanting to resume? - */ - bool resuming; - - /** - * Set to `true` if the thread has been joined. - */ - bool thread_joined; - - /** - * true if #socket_fd is non-blocking, false otherwise. - */ - bool sk_nonblck; - - /** - * Has this socket been closed for reading (i.e. other side closed - * the connection)? If so, we must completely close the connection - * once we are done sending our response (and stop trying to read - * from this socket). - */ - bool read_closed; - -}; - - -#ifdef UPGRADE_SUPPORT -/** - * Buffer we use for upgrade response handling in the unlikely - * case where the memory pool was so small it had no buffer - * capacity left. Note that we don't expect to _ever_ use this - * buffer, so it's mostly wasted memory (except that it allows - * us to handle a tricky error condition nicely). So no need to - * make this one big. Applications that want to perform well - * should just pick an adequate size for the memory pools. - */ -#define RESERVE_EBUF_SIZE 8 - -/** - * Context we pass to epoll() for each of the two sockets - * of a `struct MHD_UpgradeResponseHandle`. We need to do - * this so we can distinguish the two sockets when epoll() - * gives us event notifications. - */ -struct UpgradeEpollHandle -{ - /** - * Reference to the overall response handle this struct is - * included within. - */ - struct MHD_UpgradeResponseHandle *urh; - - /** - * The socket this event is kind-of about. Note that this is NOT - * necessarily the socket we are polling on, as for when we read - * from TLS, we epoll() on the connection's socket - * (`urh->connection->socket_fd`), while this then the application's - * socket (where the application will read from). Nevertheless, for - * the application to read, we need to first read from TLS, hence - * the two are related. - * - * Similarly, for writing to TLS, this epoll() will be on the - * connection's `socket_fd`, and this will merely be the FD which - * the application would write to. Hence this struct must always be - * interpreted based on which field in `struct - * MHD_UpgradeResponseHandle` it is (`app` or `mhd`). - */ - MHD_socket socket; - - /** - * IO-state of the @e socket (or the connection's `socket_fd`). - */ - enum MHD_EpollState celi; - -}; - - -/** - * Handle given to the application to manage special - * actions relating to MHD responses that "upgrade" - * the HTTP protocol (i.e. to WebSockets). - */ -struct MHD_UpgradeResponseHandle -{ - /** - * The connection for which this is an upgrade handle. Note that - * because a response may be shared over many connections, this may - * not be the only upgrade handle for the response of this connection. - */ - struct MHD_Connection *connection; - -#ifdef HTTPS_SUPPORT - /** - * Kept in a DLL per daemon. - */ - struct MHD_UpgradeResponseHandle *next; - - /** - * Kept in a DLL per daemon. - */ - struct MHD_UpgradeResponseHandle *prev; - -#ifdef EPOLL_SUPPORT - /** - * Next pointer for the EDLL listing urhs that are epoll-ready. - */ - struct MHD_UpgradeResponseHandle *nextE; - - /** - * Previous pointer for the EDLL listing urhs that are epoll-ready. - */ - struct MHD_UpgradeResponseHandle *prevE; - - /** - * Specifies whether urh already in EDLL list of ready connections. - */ - bool in_eready_list; -#endif - - /** - * The buffer for receiving data from TLS to - * be passed to the application. Contains @e in_buffer_size - * bytes (unless @e in_buffer_size is zero). Do not free! - */ - char *in_buffer; - - /** - * The buffer for receiving data from the application to - * be passed to TLS. Contains @e out_buffer_size - * bytes (unless @e out_buffer_size is zero). Do not free! - */ - char *out_buffer; - - /** - * Size of the @e in_buffer. - * Set to 0 if the TLS connection went down for reading or socketpair - * went down for writing. - */ - size_t in_buffer_size; - - /** - * Size of the @e out_buffer. - * Set to 0 if the TLS connection went down for writing or socketpair - * went down for reading. - */ - size_t out_buffer_size; - - /** - * Number of bytes actually in use in the @e in_buffer. Can be larger - * than @e in_buffer_size if and only if @a in_buffer_size is zero and - * we still have bytes that can be forwarded. - * Reset to zero if all data was forwarded to socketpair or - * if socketpair went down for writing. - */ - size_t in_buffer_used; - - /** - * Number of bytes actually in use in the @e out_buffer. Can be larger - * than @e out_buffer_size if and only if @a out_buffer_size is zero and - * we still have bytes that can be forwarded. - * Reset to zero if all data was forwarded to TLS connection or - * if TLS connection went down for writing. - */ - size_t out_buffer_used; - - /** - * The socket we gave to the application (r/w). - */ - struct UpgradeEpollHandle app; - - /** - * If @a app_sock was a socketpair, our end of it, otherwise - * #MHD_INVALID_SOCKET; (r/w). - */ - struct UpgradeEpollHandle mhd; - - /** - * Emergency IO buffer we use in case the memory pool has literally - * nothing left. - */ - char e_buf[RESERVE_EBUF_SIZE]; - -#endif /* HTTPS_SUPPORT */ - - /** - * Set to true after the application finished with the socket - * by #MHD_UPGRADE_ACTION_CLOSE. - * - * When BOTH @e was_closed (changed by command from application) - * AND @e clean_ready (changed internally by MHD) are set to - * #MHD_YES, function #MHD_resume_connection() will move this - * connection to cleanup list. - * @remark This flag could be changed from any thread. - */ - volatile bool was_closed; - - /** - * Set to true if connection is ready for cleanup. - * - * In TLS mode functions #MHD_connection_finish_forward_() must - * be called before setting this flag to true. - * - * In thread-per-connection mode, true in this flag means - * that connection's thread exited or about to exit and will - * not use MHD_Connection::urh data anymore. - * - * In any mode true in this flag also means that - * MHD_Connection::urh data will not be used for socketpair - * forwarding and forwarding itself is finished. - * - * When BOTH @e was_closed (changed by command from application) - * AND @e clean_ready (changed internally by MHD) are set to - * true, function #MHD_resume_connection() will move this - * connection to cleanup list. - * @remark This flag could be changed from thread that process - * connection's recv(), send() and response. - */ - bool clean_ready; -}; -#endif /* UPGRADE_SUPPORT */ - - -/** - * State kept for each MHD daemon. All connections are kept in two - * doubly-linked lists. The first one reflects the state of the - * connection in terms of what operations we are waiting for (read, - * write, locally blocked, cleanup) whereas the second is about its - * timeout state (default or custom). - */ -struct MHD_Daemon -{ - /** - * Function to call to handle incoming requests. - */ - MHD_RequestCallback rc; - - /** - * Closure for @e rc. - */ - void *rc_cls; - - /** - * Function to call for logging. - */ - MHD_LoggingCallback logger; - - /** - * Closure for @e logger. - */ - void *logger_cls; - - /** - * Function to call to accept/reject connections based on - * the client's IP address. - */ - MHD_AcceptPolicyCallback accept_policy_cb; - - /** - * Closure for @e accept_policy_cb. - */ - void *accept_policy_cb_cls; - - /** - * Function to call on the full URL early for logging. - */ - MHD_EarlyUriLogCallback early_uri_logger_cb; - - /** - * Closure for @e early_uri_logger_cb. - */ - void *early_uri_logger_cb_cls; - - /** - * Function to call whenever a connection is started or - * closed. - */ - MHD_NotifyConnectionCallback notify_connection_cb; - - /** - * Closure for @e notify_connection_cb. - */ - void *notify_connection_cb_cls; - - /** - * Function to call to unescape sequences in URIs and URI arguments. - * See #MHD_daemon_unescape_cb(). - */ - MHD_UnescapeCallback unescape_cb; - - /** - * Closure for @e unescape_cb. - */ - void *unescape_cb_cls; - - /** - * Pointer to master daemon (NULL if this is the master) - */ - struct MHD_Daemon *master; - - /** - * Worker daemons (one per thread) - */ - struct MHD_Daemon *worker_pool; - - -#if HTTPS_SUPPORT -#ifdef UPGRADE_SUPPORT - /** - * Head of DLL of upgrade response handles we are processing. - * Used for upgraded TLS connections when thread-per-connection - * is not used. - */ - struct MHD_UpgradeResponseHandle *urh_head; - - /** - * Tail of DLL of upgrade response handles we are processing. - * Used for upgraded TLS connections when thread-per-connection - * is not used. - */ - struct MHD_UpgradeResponseHandle *urh_tail; -#endif /* UPGRADE_SUPPORT */ - - /** - * Which TLS backend should be used. NULL for no TLS. - * This is merely the handle to the dlsym() object, not - * the API. - */ - void *tls_backend_lib; - - /** - * Callback functions to use for TLS operations. - */ - struct MHD_TLS_Plugin *tls_api; -#endif -#if ENABLE_DAUTH - - /** - * Random values to be used by digest authentication module. - * Size given in @e digest_auth_random_buf_size. - */ - const void *digest_auth_random_buf; -#endif - - /** - * Head of the XDLL of ALL connections with a default ('normal') - * timeout, sorted by timeout (earliest at the tail, most recently - * used connection at the head). MHD can just look at the tail of - * this list to determine the timeout for all of its elements; - * whenever there is an event of a connection, the connection is - * moved back to the tail of the list. - * - * All connections by default start in this list; if a custom - * timeout that does not match @e connection_timeout is set, they - * are moved to the @e manual_timeout_head-XDLL. - * Not used in MHD_USE_THREAD_PER_CONNECTION mode as each thread - * needs only one connection-specific timeout. - */ - struct MHD_Connection *normal_timeout_head; - - /** - * Tail of the XDLL of ALL connections with a default timeout, - * sorted by timeout (earliest timeout at the tail). - * Not used in MHD_USE_THREAD_PER_CONNECTION mode. - */ - struct MHD_Connection *normal_timeout_tail; - - /** - * Head of the XDLL of ALL connections with a non-default/custom - * timeout, unsorted. MHD will do a O(n) scan over this list to - * determine the current timeout. - * Not used in MHD_USE_THREAD_PER_CONNECTION mode. - */ - struct MHD_Connection *manual_timeout_head; - - /** - * Tail of the XDLL of ALL connections with a non-default/custom - * timeout, unsorted. - * Not used in MHD_USE_THREAD_PER_CONNECTION mode. - */ - struct MHD_Connection *manual_timeout_tail; - - /** - * Head of doubly-linked list of our current, active connections. - */ - struct MHD_Connection *connections_head; - - /** - * Tail of doubly-linked list of our current, active connections. - */ - struct MHD_Connection *connections_tail; - - /** - * Head of doubly-linked list of our current but suspended - * connections. - */ - struct MHD_Connection *suspended_connections_head; - - /** - * Tail of doubly-linked list of our current but suspended - * connections. - */ - struct MHD_Connection *suspended_connections_tail; - - /** - * Head of doubly-linked list of connections to clean up. - */ - struct MHD_Connection *cleanup_head; - - /** - * Tail of doubly-linked list of connections to clean up. - */ - struct MHD_Connection *cleanup_tail; - - /** - * Table storing number of connections per IP - */ - void *per_ip_connection_count; - -#ifdef EPOLL_SUPPORT - /** - * Head of EDLL of connections ready for processing (in epoll mode). - */ - struct MHD_Connection *eready_head; - - /** - * Tail of EDLL of connections ready for processing (in epoll mode) - */ - struct MHD_Connection *eready_tail; - - /** - * Pointer to marker used to indicate ITC slot in epoll sets. - */ - const char *epoll_itc_marker; -#ifdef UPGRADE_SUPPORT - /** - * Head of EDLL of upgraded connections ready for processing (in epoll mode). - */ - struct MHD_UpgradeResponseHandle *eready_urh_head; - - /** - * Tail of EDLL of upgraded connections ready for processing (in epoll mode) - */ - struct MHD_UpgradeResponseHandle *eready_urh_tail; -#endif /* UPGRADE_SUPPORT */ -#endif /* EPOLL_SUPPORT */ - -#ifdef DAUTH_SUPPORT - - /** - * Character array of random values. - */ - const char *digest_auth_random; - - /** - * An array that contains the map nonce-nc. - */ - struct MHD_NonceNc *nnc; - - /** - * A rw-lock for synchronizing access to @e nnc. - */ - MHD_mutex_ nnc_lock; - - /** - * Size of `digest_auth_random. - */ - size_t digest_auth_rand_size; - - /** - * Size of the nonce-nc array. - */ - unsigned int nonce_nc_size; - -#endif - - /** - * The select thread handle (if we have internal select) - */ - MHD_thread_handle_ID_ pid; - - /** - * Socket address to bind to for the listen socket. - */ - struct sockaddr_storage listen_sa; - - /** - * Mutex for per-IP connection counts. - */ - MHD_mutex_ per_ip_connection_mutex; - - /** - * Mutex for (modifying) access to the "cleanup", "normal_timeout" and - * "manual_timeout" DLLs. - */ - MHD_mutex_ cleanup_connection_mutex; - - /** - * Number of (valid) bytes in @e listen_sa. Zero - * if @e listen_sa is not initialized. - */ - size_t listen_sa_len; - -/** - * Default size of the per-connection memory pool. - */ -#define POOL_SIZE_DEFAULT (32 * 1024) - /** - * Buffer size to use for each connection. Default - * is #POOL_SIZE_DEFAULT. - */ - size_t connection_memory_limit_b; - -/** - * Default minimum size by which MHD tries to increment read/write - * buffers. We usually begin with half the available pool space for - * the IO-buffer, but if absolutely needed we additively grow by the - * number of bytes given here (up to -- theoretically -- the full pool - * space). - */ -#define BUF_INC_SIZE_DEFAULT 1024 - - /** - * Increment to use when growing the read buffer. Smaller - * than @e connection_memory_limit_b. - */ - size_t connection_memory_increment_b; - - /** - * Desired size of the stack for threads created by MHD, - * 0 for system default. - */ - size_t thread_stack_limit_b; - -#if ENABLE_DAUTH - - /** - * Size of @e digest_auth_random_buf. - */ - size_t digest_auth_random_buf_size; - - /** - * Default value for @e digest_nc_length. - */ -#define DIGEST_NC_LENGTH_DEFAULT 4 - - /** - * Desired length of the internal array with the nonce and - * nonce counters for digest authentication. - */ - size_t digest_nc_length; -#endif - - /** - * Default value we use for the listen backlog. - */ -#ifdef SOMAXCONN -#define LISTEN_BACKLOG_DEFAULT SOMAXCONN -#else /* !SOMAXCONN */ -#define LISTEN_BACKLOG_DEFAULT 511 -#endif - - /** - * Backlog argument to use for listen. See - * #MHD_daemon_listen_backlog(). - */ - int listen_backlog; - - /** - * Default queue length to use with fast open. - */ -#define FO_QUEUE_LENGTH_DEFAULT 50 - - /** - * Queue length to use with fast open. - */ - unsigned int fo_queue_length; - - /** - * Maximum number of connections MHD accepts. 0 for unlimited. - */ - unsigned int global_connection_limit; - - /** - * Maximum number of connections we accept per IP, 0 for unlimited. - */ - unsigned int ip_connection_limit; - - /** - * Number of active parallel connections. - */ - unsigned int connections; - - /** - * Number of worker daemons - */ - unsigned int worker_pool_size; - - /** - * Default timeout in seconds for idle connections. - */ - time_t connection_default_timeout; - - /** - * Listen socket we should use, MHD_INVALID_SOCKET means - * we are to initialize the socket from the other options given. - */ - MHD_socket listen_socket; - -#ifdef EPOLL_SUPPORT - /** - * File descriptor associated with our epoll loop. - */ - int epoll_fd; - - /** - * true if the listen socket is in the 'epoll' set, - * false if not. - */ - bool listen_socket_in_epoll; - -#if defined(HTTPS_SUPPORT) && defined(UPGRADE_SUPPORT) - /** - * File descriptor associated with the #run_epoll_for_upgrade() loop. - * Only available if #MHD_USE_HTTPS_EPOLL_UPGRADE is set. - */ - int epoll_upgrade_fd; - - /** - * true if @e epoll_upgrade_fd is in the 'epoll' set, - * false if not. - */ - bool upgrade_fd_in_epoll; -#endif /* HTTPS_SUPPORT && UPGRADE_SUPPORT */ - -#endif - - /** - * Inter-thread communication channel. - */ - struct MHD_itc_ itc; - - /** - * Which threading mode do we use? Positive - * numbers indicate the number of worker threads to be used. - * Values larger than 1 imply a thread pool. - */ - enum MHD_ThreadingMode threading_mode; - - /** - * When should we use TCP_FASTOPEN? - * See #MHD_daemon_tcp_fastopen(). - */ - enum MHD_FastOpenMethod fast_open_method; - - /** - * Address family to use when listening. - * Default is #MHD_AF_NONE (do not listen). - */ - enum MHD_AddressFamily listen_af; - - /** - * Sets active/desired style of the event loop. - * (Auto only possible during initialization, later set to - * the actual style we use.) - */ - enum MHD_EventLoopSyscall event_loop_syscall; - - /** - * How strictly do we enforce the HTTP protocol? - * See #MHD_daemon_protocol_strict_level(). - */ - enum MHD_ProtocolStrictLevel protocol_strict_level; - - /** - * On which port should we listen on? Only effective if we were not - * given a listen socket or a full address via - * #MHD_daemon_bind_sa(). 0 means to bind to random free port. - */ - uint16_t listen_port; - - /** - * Suppress generating the "Date:" header, this system - * lacks an RTC (or developer is hyper-optimizing). See - * #MHD_daemon_suppress_date_no_clock(). - */ - bool suppress_date; - - /** - * The use of the inter-thread communication channel is disabled. - * See #MHD_daemon_disable_itc(). - */ - bool disable_itc; - - /** - * Disable #MHD_action_suspend() functionality. See - * #MHD_daemon_disallow_suspend_resume(). - */ - bool disallow_suspend_resume; - - /** - * Disable #MHD_action_upgrade() functionality. See - * #MHD_daemon_disallow_upgrade(). - */ - bool disallow_upgrade; - - /** - * Did we hit some system or process-wide resource limit while - * trying to accept() the last time? If so, we don't accept new - * connections until we close an existing one. This effectively - * temporarily lowers the "connection_limit" to the current - * number of connections. - */ - bool at_limit; - - /** - * Disables optional calls to `shutdown()` and enables aggressive - * non-blocking optimistic reads and other potentially unsafe - * optimizations. See #MHD_daemon_enable_turbo(). - */ - bool enable_turbo; - - /** - * 'True' if some data is already waiting to be processed. If set - * to 'true' - zero timeout for select()/poll*() is used. Should be - * reset each time before processing connections and raised by any - * connection which require additional immediately processing - * (application does not provide data for response, data waiting in - * TLS buffers etc.) - */ - bool data_already_pending; - - /** - * MHD_daemon_quiesce() was run against this daemon. - */ - bool was_quiesced; - - /** - * Is some connection wanting to resume? - */ - bool resuming; - - /** - * Allow reusing the address:port combination when binding. - * See #MHD_daemon_listen_allow_address_reuse(). - */ - bool allow_address_reuse; - - /** - * MHD should speak SHOUTcast instead of HTTP. - */ - bool enable_shoutcast; - - /** - * Are we shutting down? - */ - volatile bool shutdown; - -}; - - -/** - * Action function implementing some action to be - * performed on a request. - * - * @param cls action-specfic closure - * @param request the request on which the action is to be performed - * @return #MHD_SC_OK on success, otherwise an error code - */ -typedef enum MHD_StatusCode -(*ActionCallback)(void *cls, - struct MHD_Request *request); - - -/** - * Actions are returned by the application to drive the request - * handling of MHD. - */ -struct MHD_Action -{ - - /** - * Function to call for the action. - */ - ActionCallback action; - - /** - * Closure for @a action - */ - void *action_cls; - -}; - - -/** - * Representation of an HTTP response. - */ -struct MHD_Response -{ - - /** - * A response *is* an action. See also - * #MHD_action_from_response(). Hence this field - * must be the first field in a response! - */ - struct MHD_Action action; - - /** - * Headers to send for the response. Initially - * the linked list is created in inverse order; - * the order should be inverted before sending! - */ - struct MHD_HTTP_Header *first_header; - - /** - * Buffer pointing to data that we are supposed - * to send as a response. - */ - char *data; - - /** - * Closure to give to the content reader @e crc - * and content reader free callback @e crfc. - */ - void *crc_cls; - - /** - * How do we get more data? NULL if we are - * given all of the data up front. - */ - MHD_ContentReaderCallback crc; - - /** - * NULL if data must not be freed, otherwise - * either user-specified callback or "&free". - */ - MHD_ContentReaderFreeCallback crfc; - - /** - * Function to call once MHD is finished with - * the request, may be NULL. - */ - MHD_RequestTerminationCallback termination_cb; - - /** - * Closure for @e termination_cb. - */ - void *termination_cb_cls; - -#ifdef UPGRADE_SUPPORT - /** - * Application function to call once we are done sending the headers - * of the response; NULL unless this is a response created with - * #MHD_create_response_for_upgrade(). - */ - MHD_UpgradeHandler upgrade_handler; - - /** - * Closure for @e uh. - */ - void *upgrade_handler_cls; -#endif /* UPGRADE_SUPPORT */ - - /** - * Mutex to synchronize access to @e data, @e size and - * @e reference_count. - */ - MHD_mutex_ mutex; - - /** - * Set to #MHD_SIZE_UNKNOWN if size is not known. - */ - uint64_t total_size; - - /** - * At what offset in the stream is the - * beginning of @e data located? - */ - uint64_t data_start; - - /** - * Offset to start reading from when using @e fd. - */ - uint64_t fd_off; - - /** - * Number of bytes ready in @e data (buffer may be larger - * than what is filled with payload). - */ - size_t data_size; - - /** - * Size of the data buffer @e data. - */ - size_t data_buffer_size; - - /** - * HTTP status code of the response. - */ - enum MHD_HTTP_StatusCode status_code; - - /** - * Reference count for this response. Free once the counter hits - * zero. - */ - unsigned int reference_count; - - /** - * File-descriptor if this response is FD-backed. - */ - int fd; - - /** - * Only respond in HTTP 1.0 mode. - */ - bool v10_only; - - /** - * Use ShoutCAST format. - */ - bool icy; - -}; - - -/** - * Callback invoked when iterating over @a key / @a value - * argument pairs during parsing. - * - * @param request context of the iteration - * @param key 0-terminated key string, never NULL - * @param value 0-terminated value string, may be NULL - * @param kind origin of the key-value pair - * @return true on success (continue to iterate) - * false to signal failure (and abort iteration) - */ -typedef bool -(*MHD_ArgumentIterator_)(struct MHD_Request *request, - const char *key, - const char *value, - enum MHD_ValueKind kind); - - -/** - * Parse and unescape the arguments given by the client - * as part of the HTTP request URI. - * - * @param request request to add headers to - * @param kind header kind to pass to @a cb - * @param[in,out] args argument URI string (after "?" in URI), - * clobbered in the process! - * @param cb function to call on each key-value pair found - * @param[out] num_headers set to the number of headers found - * @return false on failure (@a cb returned false), - * true for success (parsing succeeded, @a cb always - * returned true) - */ -bool -MHD_parse_arguments_ (struct MHD_Request *request, - enum MHD_ValueKind kind, - char *args, - MHD_ArgumentIterator_ cb, - unsigned int *num_headers); - - -/** - * Insert an element at the head of a DLL. Assumes that head, tail and - * element are structs with prev and next fields. - * - * @param head pointer to the head of the DLL - * @param tail pointer to the tail of the DLL - * @param element element to insert - */ -#define DLL_insert(head,tail,element) do { \ - mhd_assert (NULL == (element)->next); \ - mhd_assert (NULL == (element)->prev); \ - (element)->next = (head); \ - (element)->prev = NULL; \ - if ((tail) == NULL) \ - (tail) = element; \ - else \ - (head)->prev = element; \ - (head) = (element); } while (0) - - -/** - * Remove an element from a DLL. Assumes that head, tail and element - * are structs with prev and next fields. - * - * @param head pointer to the head of the DLL - * @param tail pointer to the tail of the DLL - * @param element element to remove - */ -#define DLL_remove(head,tail,element) do { \ - mhd_assert ( (NULL != (element)->next) || ((element) == (tail))); \ - mhd_assert ( (NULL != (element)->prev) || ((element) == (head))); \ - if ((element)->prev == NULL) \ - (head) = (element)->next; \ - else \ - (element)->prev->next = (element)->next; \ - if ((element)->next == NULL) \ - (tail) = (element)->prev; \ - else \ - (element)->next->prev = (element)->prev; \ - (element)->next = NULL; \ - (element)->prev = NULL; } while (0) - - -/** - * Insert an element at the head of a XDLL. Assumes that head, tail and - * element are structs with prevX and nextX fields. - * - * @param head pointer to the head of the XDLL - * @param tail pointer to the tail of the XDLL - * @param element element to insert - */ -#define XDLL_insert(head,tail,element) do { \ - mhd_assert (NULL == (element)->nextX); \ - mhd_assert (NULL == (element)->prevX); \ - (element)->nextX = (head); \ - (element)->prevX = NULL; \ - if (NULL == (tail)) \ - (tail) = element; \ - else \ - (head)->prevX = element; \ - (head) = (element); } while (0) - - -/** - * Remove an element from a XDLL. Assumes that head, tail and element - * are structs with prevX and nextX fields. - * - * @param head pointer to the head of the XDLL - * @param tail pointer to the tail of the XDLL - * @param element element to remove - */ -#define XDLL_remove(head,tail,element) do { \ - mhd_assert ( (NULL != (element)->nextX) || ((element) == (tail))); \ - mhd_assert ( (NULL != (element)->prevX) || ((element) == (head))); \ - if (NULL == (element)->prevX) \ - (head) = (element)->nextX; \ - else \ - (element)->prevX->nextX = (element)->nextX; \ - if (NULL == (element)->nextX) \ - (tail) = (element)->prevX; \ - else \ - (element)->nextX->prevX = (element)->prevX; \ - (element)->nextX = NULL; \ - (element)->prevX = NULL; } while (0) - - -/** - * Insert an element at the head of a EDLL. Assumes that head, tail and - * element are structs with prevE and nextE fields. - * - * @param head pointer to the head of the EDLL - * @param tail pointer to the tail of the EDLL - * @param element element to insert - */ -#define EDLL_insert(head,tail,element) do { \ - (element)->nextE = (head); \ - (element)->prevE = NULL; \ - if ((tail) == NULL) \ - (tail) = element; \ - else \ - (head)->prevE = element; \ - (head) = (element); } while (0) - - -/** - * Remove an element from a EDLL. Assumes that head, tail and element - * are structs with prevE and nextE fields. - * - * @param head pointer to the head of the EDLL - * @param tail pointer to the tail of the EDLL - * @param element element to remove - */ -#define EDLL_remove(head,tail,element) do { \ - if ((element)->prevE == NULL) \ - (head) = (element)->nextE; \ - else \ - (element)->prevE->nextE = (element)->nextE; \ - if ((element)->nextE == NULL) \ - (tail) = (element)->prevE; \ - else \ - (element)->nextE->prevE = (element)->prevE; \ - (element)->nextE = NULL; \ - (element)->prevE = NULL; } while (0) - - -/** - * Error code similar to EGAIN or EINTR - */ -#define MHD_ERR_AGAIN_ (-3073) - -/** - * Connection was hard-closed by remote peer. - */ -#define MHD_ERR_CONNRESET_ (-3074) - -/** - * Connection is not connected anymore due to - * network error or any other reason. - */ -#define MHD_ERR_NOTCONN_ (-3075) - -/** - * "Not enough memory" error code - */ -#define MHD_ERR_NOMEM_ (-3076) - -/** - * "Bad FD" error code - */ -#define MHD_ERR_BADF_ (-3077) - -/** - * Error code similar to EINVAL - */ -#define MHD_ERR_INVAL_ (-3078) - - -#endif diff --git a/src/lib/md5.c b/src/lib/md5.c @@ -1,268 +0,0 @@ -/* - * This code implements the MD5 message-digest algorithm. - * The algorithm is due to Ron Rivest. This code was - * written by Colin Plumb in 1993, no copyright is claimed. - * This code is in the public domain; do with it what you wish. - * - * Equivalent code is available from RSA Data Security, Inc. - * This code has been tested against that, and is equivalent, - * except that you don't need to include two pages of legalese - * with every copy. - * - * To compute the message digest of a chunk of bytes, declare an - * MD5Context structure, pass it to MHD_MD5Init, call MHD_MD5Update as - * needed on buffers full of bytes, and then call MHD_MD5Final, which - * will fill a supplied 16-byte array with the digest. - */ - -/* Based on OpenBSD modifications */ - -#include "md5.h" -#include "mhd_byteorder.h" - -#define PUT_64BIT_LE(cp, value) do { \ - (cp)[7] = (uint8_t) ((value) >> 56); \ - (cp)[6] = (uint8_t) ((value) >> 48); \ - (cp)[5] = (uint8_t) ((value) >> 40); \ - (cp)[4] = (uint8_t) ((value) >> 32); \ - (cp)[3] = (uint8_t) ((value) >> 24); \ - (cp)[2] = (uint8_t) ((value) >> 16); \ - (cp)[1] = (uint8_t) ((value) >> 8); \ - (cp)[0] = (uint8_t) ((value)); } while (0) - -#define PUT_32BIT_LE(cp, value) do { \ - (cp)[3] = (uint8_t) ((value) >> 24); \ - (cp)[2] = (uint8_t) ((value) >> 16); \ - (cp)[1] = (uint8_t) ((value) >> 8); \ - (cp)[0] = (uint8_t) ((value)); } while (0) - -static uint8_t PADDING[MD5_BLOCK_SIZE] = { - 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 -}; - -/* - * Start MD5 accumulation. Set bit count to 0 and buffer to mysterious - * initialization constants. - */ -void -MHD_MD5Init (struct MD5Context *ctx) -{ - if (! ctx) - return; - - ctx->count = 0; - ctx->state[0] = 0x67452301; - ctx->state[1] = 0xefcdab89; - ctx->state[2] = 0x98badcfe; - ctx->state[3] = 0x10325476; -} - - -/* - * Update context to reflect the concatenation of another buffer full - * of bytes. - */ -void -MHD_MD5Update (struct MD5Context *ctx, const unsigned char *input, size_t len) -{ - size_t have, need; - - if (! ctx || ! input) - return; - - /* Check how many bytes we already have and how many more we need. */ - have = (size_t) ((ctx->count >> 3) & (MD5_BLOCK_SIZE - 1)); - need = MD5_BLOCK_SIZE - have; - - /* Update bitcount */ - ctx->count += (uint64_t) len << 3; - - if (len >= need) - { - if (have != 0) - { - memcpy (ctx->buffer + have, input, need); - MD5Transform (ctx->state, ctx->buffer); - input += need; - len -= need; - have = 0; - } - - /* Process data in MD5_BLOCK_SIZE-byte chunks. */ - while (len >= MD5_BLOCK_SIZE) - { - MD5Transform (ctx->state, input); - input += MD5_BLOCK_SIZE; - len -= MD5_BLOCK_SIZE; - } - } - - /* Handle any remaining bytes of data. */ - if (len != 0) - memcpy (ctx->buffer + have, input, len); -} - - -/* - * Pad pad to 64-byte boundary with the bit pattern - * 1 0* (64-bit count of bits processed, MSB-first) - */ -void -MD5Pad (struct MD5Context *ctx) -{ - uint8_t count[8]; - size_t padlen; - - if (! ctx) - return; - - /* Convert count to 8 bytes in little endian order. */ - PUT_64BIT_LE (count, ctx->count); - - /* Pad out to 56 mod 64. */ - padlen = MD5_BLOCK_SIZE - - ((ctx->count >> 3) & (MD5_BLOCK_SIZE - 1)); - if (padlen < 1 + 8) - padlen += MD5_BLOCK_SIZE; - MHD_MD5Update (ctx, PADDING, padlen - 8); /* padlen - 8 <= 64 */ - MHD_MD5Update (ctx, count, 8); -} - - -/* - * Final wrapup--call MD5Pad, fill in digest and zero out ctx. - */ -void -MHD_MD5Final (unsigned char digest[MD5_DIGEST_SIZE], struct MD5Context *ctx) -{ - int i; - - if (! ctx || ! digest) - return; - - MD5Pad (ctx); - for (i = 0; i < 4; i++) - PUT_32BIT_LE (digest + i * 4, ctx->state[i]); - - memset (ctx, 0, sizeof(*ctx)); -} - - -/* The four core functions - F1 is optimized somewhat */ - -/* #define F1(x, y, z) (x & y | ~x & z) */ -#define F1(x, y, z) (z ^ (x & (y ^ z))) -#define F2(x, y, z) F1 (z, x, y) -#define F3(x, y, z) (x ^ y ^ z) -#define F4(x, y, z) (y ^ (x | ~z)) - -/* This is the central step in the MD5 algorithm. */ -#define MD5STEP(f, w, x, y, z, data, s) \ - (w += f (x, y, z) + data, w = w << s | w >> (32 - s), w += x) - -/* - * The core of the MD5 algorithm, this alters an existing MD5 hash to - * reflect the addition of 16 longwords of new data. MHD_MD5Update blocks - * the data and converts bytes into longwords for this routine. - */ -void -MD5Transform (uint32_t state[4], const uint8_t block[MD5_BLOCK_SIZE]) -{ - uint32_t a, b, c, d, in[MD5_BLOCK_SIZE / 4]; - -#if _MHD_BYTE_ORDER == _MHD_LITTLE_ENDIAN - memcpy (in, block, sizeof(in)); -#else - for (a = 0; a < MD5_BLOCK_SIZE / 4; a++) - { - in[a] = (uint32_t) ( - (uint32_t) (block[a * 4 + 0]) - | (uint32_t) (block[a * 4 + 1]) << 8 - | (uint32_t) (block[a * 4 + 2]) << 16 - | (uint32_t) (block[a * 4 + 3]) << 24); - } -#endif - - a = state[0]; - b = state[1]; - c = state[2]; - d = state[3]; - - MD5STEP (F1, a, b, c, d, in[0] + 0xd76aa478, 7); - MD5STEP (F1, d, a, b, c, in[1] + 0xe8c7b756, 12); - MD5STEP (F1, c, d, a, b, in[2] + 0x242070db, 17); - MD5STEP (F1, b, c, d, a, in[3] + 0xc1bdceee, 22); - MD5STEP (F1, a, b, c, d, in[4] + 0xf57c0faf, 7); - MD5STEP (F1, d, a, b, c, in[5] + 0x4787c62a, 12); - MD5STEP (F1, c, d, a, b, in[6] + 0xa8304613, 17); - MD5STEP (F1, b, c, d, a, in[7] + 0xfd469501, 22); - MD5STEP (F1, a, b, c, d, in[8] + 0x698098d8, 7); - MD5STEP (F1, d, a, b, c, in[9] + 0x8b44f7af, 12); - MD5STEP (F1, c, d, a, b, in[10] + 0xffff5bb1, 17); - MD5STEP (F1, b, c, d, a, in[11] + 0x895cd7be, 22); - MD5STEP (F1, a, b, c, d, in[12] + 0x6b901122, 7); - MD5STEP (F1, d, a, b, c, in[13] + 0xfd987193, 12); - MD5STEP (F1, c, d, a, b, in[14] + 0xa679438e, 17); - MD5STEP (F1, b, c, d, a, in[15] + 0x49b40821, 22); - - MD5STEP (F2, a, b, c, d, in[1] + 0xf61e2562, 5); - MD5STEP (F2, d, a, b, c, in[6] + 0xc040b340, 9); - MD5STEP (F2, c, d, a, b, in[11] + 0x265e5a51, 14); - MD5STEP (F2, b, c, d, a, in[0] + 0xe9b6c7aa, 20); - MD5STEP (F2, a, b, c, d, in[5] + 0xd62f105d, 5); - MD5STEP (F2, d, a, b, c, in[10] + 0x02441453, 9); - MD5STEP (F2, c, d, a, b, in[15] + 0xd8a1e681, 14); - MD5STEP (F2, b, c, d, a, in[4] + 0xe7d3fbc8, 20); - MD5STEP (F2, a, b, c, d, in[9] + 0x21e1cde6, 5); - MD5STEP (F2, d, a, b, c, in[14] + 0xc33707d6, 9); - MD5STEP (F2, c, d, a, b, in[3] + 0xf4d50d87, 14); - MD5STEP (F2, b, c, d, a, in[8] + 0x455a14ed, 20); - MD5STEP (F2, a, b, c, d, in[13] + 0xa9e3e905, 5); - MD5STEP (F2, d, a, b, c, in[2] + 0xfcefa3f8, 9); - MD5STEP (F2, c, d, a, b, in[7] + 0x676f02d9, 14); - MD5STEP (F2, b, c, d, a, in[12] + 0x8d2a4c8a, 20); - - MD5STEP (F3, a, b, c, d, in[5] + 0xfffa3942, 4); - MD5STEP (F3, d, a, b, c, in[8] + 0x8771f681, 11); - MD5STEP (F3, c, d, a, b, in[11] + 0x6d9d6122, 16); - MD5STEP (F3, b, c, d, a, in[14] + 0xfde5380c, 23); - MD5STEP (F3, a, b, c, d, in[1] + 0xa4beea44, 4); - MD5STEP (F3, d, a, b, c, in[4] + 0x4bdecfa9, 11); - MD5STEP (F3, c, d, a, b, in[7] + 0xf6bb4b60, 16); - MD5STEP (F3, b, c, d, a, in[10] + 0xbebfbc70, 23); - MD5STEP (F3, a, b, c, d, in[13] + 0x289b7ec6, 4); - MD5STEP (F3, d, a, b, c, in[0] + 0xeaa127fa, 11); - MD5STEP (F3, c, d, a, b, in[3] + 0xd4ef3085, 16); - MD5STEP (F3, b, c, d, a, in[6] + 0x04881d05, 23); - MD5STEP (F3, a, b, c, d, in[9] + 0xd9d4d039, 4); - MD5STEP (F3, d, a, b, c, in[12] + 0xe6db99e5, 11); - MD5STEP (F3, c, d, a, b, in[15] + 0x1fa27cf8, 16); - MD5STEP (F3, b, c, d, a, in[2] + 0xc4ac5665, 23); - - MD5STEP (F4, a, b, c, d, in[0] + 0xf4292244, 6); - MD5STEP (F4, d, a, b, c, in[7] + 0x432aff97, 10); - MD5STEP (F4, c, d, a, b, in[14] + 0xab9423a7, 15); - MD5STEP (F4, b, c, d, a, in[5] + 0xfc93a039, 21); - MD5STEP (F4, a, b, c, d, in[12] + 0x655b59c3, 6); - MD5STEP (F4, d, a, b, c, in[3] + 0x8f0ccc92, 10); - MD5STEP (F4, c, d, a, b, in[10] + 0xffeff47d, 15); - MD5STEP (F4, b, c, d, a, in[1] + 0x85845dd1, 21); - MD5STEP (F4, a, b, c, d, in[8] + 0x6fa87e4f, 6); - MD5STEP (F4, d, a, b, c, in[15] + 0xfe2ce6e0, 10); - MD5STEP (F4, c, d, a, b, in[6] + 0xa3014314, 15); - MD5STEP (F4, b, c, d, a, in[13] + 0x4e0811a1, 21); - MD5STEP (F4, a, b, c, d, in[4] + 0xf7537e82, 6); - MD5STEP (F4, d, a, b, c, in[11] + 0xbd3af235, 10); - MD5STEP (F4, c, d, a, b, in[2] + 0x2ad7d2bb, 15); - MD5STEP (F4, b, c, d, a, in[9] + 0xeb86d391, 21); - - state[0] += a; - state[1] += b; - state[2] += c; - state[3] += d; -} - - -/* end of md5.c */ diff --git a/src/lib/md5.h b/src/lib/md5.h @@ -1,66 +0,0 @@ -/* - * This code implements the MD5 message-digest algorithm. - * The algorithm is due to Ron Rivest. This code was - * written by Colin Plumb in 1993, no copyright is claimed. - * This code is in the public domain; do with it what you wish. - * - * Equivalent code is available from RSA Data Security, Inc. - * This code has been tested against that, and is equivalent, - * except that you don't need to include two pages of legalese - * with every copy. - * - * To compute the message digest of a chunk of bytes, declare an - * MD5Context structure, pass it to MHD_MD5Init, call MHD_MD5Update as - * needed on buffers full of bytes, and then call MHD_MD5Final, which - * will fill a supplied 16-byte array with the digest. - */ - -#ifndef MHD_MD5_H -#define MHD_MD5_H - -#include "platform.h" - -#define MD5_BLOCK_SIZE 64 -#define MD5_DIGEST_SIZE 16 -#define MD5_DIGEST_STRING_LENGTH (MD5_DIGEST_SIZE * 2 + 1) - -struct MD5Context -{ - uint32_t state[4]; /* state */ - uint64_t count; /* number of bits, mod 2^64 */ - uint8_t buffer[MD5_BLOCK_SIZE]; /* input buffer */ -}; - -/* - * Start MD5 accumulation. Set bit count to 0 and buffer to mysterious - * initialization constants. - */ -void MHD_MD5Init (struct MD5Context *ctx); - -/* - * Update context to reflect the concatenation of another buffer full - * of bytes. - */ -void MHD_MD5Update (struct MD5Context *ctx, const unsigned char *input, size_t - len); - -/* - * Pad pad to 64-byte boundary with the bit pattern - * 1 0* (64-bit count of bits processed, MSB-first) - */ -void MD5Pad (struct MD5Context *ctx); - -/* - * Final wrapup--call MD5Pad, fill in digest and zero out ctx. - */ -void MHD_MD5Final (unsigned char digest[MD5_DIGEST_SIZE], struct - MD5Context *ctx); - -/* - * The core of the MD5 algorithm, this alters an existing MD5 hash to - * reflect the addition of 16 longwords of new data. MHD_MD5Update blocks - * the data and converts bytes into longwords for this routine. - */ -void MD5Transform (uint32_t state[4], const uint8_t block[MD5_BLOCK_SIZE]); - -#endif /* !MHD_MD5_H */ diff --git a/src/lib/memorypool.c b/src/lib/memorypool.c @@ -1,340 +0,0 @@ -/* - This file is part of libmicrohttpd - Copyright (C) 2007, 2009, 2010, 2018 Christian Grothoff - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. - - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with this library; if not, write to the Free Software - Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA -*/ - -/** - * @file memorypool.c - * @brief memory pool - * @author Christian Grothoff - */ -#include "memorypool.h" - -/* define MAP_ANONYMOUS for Mac OS X */ -#if defined(MAP_ANON) && ! defined(MAP_ANONYMOUS) -#define MAP_ANONYMOUS MAP_ANON -#endif -#ifndef MAP_FAILED -#define MAP_FAILED ((void*) -1) -#endif - -/** - * Align to 2x word size (as GNU libc does). - */ -#define ALIGN_SIZE (2 * sizeof(void*)) - -/** - * Round up 'n' to a multiple of ALIGN_SIZE. - */ -#define ROUND_TO_ALIGN(n) ((n + (ALIGN_SIZE - 1)) & (~(ALIGN_SIZE - 1))) - - -/** - * Handle for a memory pool. Pools are not reentrant and must not be - * used by multiple threads. - */ -struct MemoryPool -{ - - /** - * Pointer to the pool's memory - */ - char *memory; - - /** - * Size of the pool. - */ - size_t size; - - /** - * Offset of the first unallocated byte. - */ - size_t pos; - - /** - * Offset of the last unallocated byte. - */ - size_t end; - - /** - * false if pool was malloc'ed, true if mmapped (VirtualAlloc'ed for W32). - */ - bool is_mmap; -}; - - -/** - * Free the memory given by @a ptr. Calls "free(ptr)". This function - * should be used to free the username returned by - * #MHD_digest_auth_get_username(). - * @note Since v0.9.56 - * - * @param ptr pointer to free. - */ -_MHD_EXTERN void -MHD_free (void *ptr) -{ - free (ptr); -} - - -/** - * Create a memory pool. - * - * @param max maximum size of the pool - * @return NULL on error - */ -struct MemoryPool * -MHD_pool_create (size_t max) -{ - struct MemoryPool *pool; - - pool = malloc (sizeof (struct MemoryPool)); - if (NULL == pool) - return NULL; -#if defined(MAP_ANONYMOUS) || defined(_WIN32) - if (max <= 32 * 1024) - pool->memory = MAP_FAILED; - else -#if defined(MAP_ANONYMOUS) && ! defined(_WIN32) - pool->memory = mmap (NULL, - max, - PROT_READ | PROT_WRITE, - MAP_PRIVATE | MAP_ANONYMOUS, - -1, - 0); -#elif defined(_WIN32) - pool->memory = VirtualAlloc (NULL, - max, - MEM_COMMIT | MEM_RESERVE, - PAGE_READWRITE); -#endif -#else - pool->memory = MAP_FAILED; -#endif - if ( (MAP_FAILED == pool->memory) || - (NULL == pool->memory)) - { - pool->memory = malloc (max); - if (NULL == pool->memory) - { - free (pool); - return NULL; - } - pool->is_mmap = false; - } - else - { - pool->is_mmap = true; - } - pool->pos = 0; - pool->end = max; - pool->size = max; - return pool; -} - - -/** - * Destroy a memory pool. - * - * @param pool memory pool to destroy - */ -void -MHD_pool_destroy (struct MemoryPool *pool) -{ - if (NULL == pool) - return; - if (! pool->is_mmap) - free (pool->memory); - else -#if defined(MAP_ANONYMOUS) && ! defined(_WIN32) - munmap (pool->memory, - pool->size); -#elif defined(_WIN32) - VirtualFree (pool->memory, - 0, - MEM_RELEASE); -#else - abort (); -#endif - free (pool); -} - - -/** - * Check how much memory is left in the @a pool - * - * @param pool pool to check - * @return number of bytes still available in @a pool - */ -size_t -MHD_pool_get_free (struct MemoryPool *pool) -{ - return (pool->end - pool->pos); -} - - -/** - * Allocate size bytes from the pool. - * - * @param pool memory pool to use for the operation - * @param size number of bytes to allocate - * @param from_end allocate from end of pool (set to #MHD_YES); - * use this for small, persistent allocations that - * will never be reallocated - * @return NULL if the pool cannot support size more - * bytes - */ -void * -MHD_pool_allocate (struct MemoryPool *pool, - size_t size, - int from_end) -{ - void *ret; - size_t asize; - - asize = ROUND_TO_ALIGN (size); - if ( (0 == asize) && (0 != size) ) - return NULL; /* size too close to SIZE_MAX */ - if ( (pool->pos + asize > pool->end) || - (pool->pos + asize < pool->pos)) - return NULL; - if (from_end == MHD_YES) - { - ret = &pool->memory[pool->end - asize]; - pool->end -= asize; - } - else - { - ret = &pool->memory[pool->pos]; - pool->pos += asize; - } - return ret; -} - - -/** - * Reallocate a block of memory obtained from the pool. - * This is particularly efficient when growing or - * shrinking the block that was last (re)allocated. - * If the given block is not the most recently - * (re)allocated block, the memory of the previous - * allocation may be leaked until the pool is - * destroyed (and copying the data maybe required). - * - * @param pool memory pool to use for the operation - * @param old the existing block - * @param old_size the size of the existing block - * @param new_size the new size of the block - * @return new address of the block, or - * NULL if the pool cannot support @a new_size - * bytes (old continues to be valid for @a old_size) - */ -void * -MHD_pool_reallocate (struct MemoryPool *pool, - void *old, - size_t old_size, - size_t new_size) -{ - void *ret; - size_t asize; - - asize = ROUND_TO_ALIGN (new_size); - if ( (0 == asize) && - (0 != new_size) ) - return NULL; /* new_size too close to SIZE_MAX */ - if ( (pool->end < old_size) || - (pool->end < asize) ) - return NULL; /* unsatisfiable or bogus request */ - - if ( (pool->pos >= old_size) && - (&pool->memory[pool->pos - old_size] == old) ) - { - /* was the previous allocation - optimize! */ - if (pool->pos + asize - old_size <= pool->end) - { - /* fits */ - pool->pos += asize - old_size; - if (asize < old_size) /* shrinking - zero again! */ - memset (&pool->memory[pool->pos], - 0, - old_size - asize); - return old; - } - /* does not fit */ - return NULL; - } - if (asize <= old_size) - return old; /* cannot shrink, no need to move */ - if ((pool->pos + asize >= pool->pos) && - (pool->pos + asize <= pool->end)) - { - /* fits */ - ret = &pool->memory[pool->pos]; - if (0 != old_size) - memmove (ret, - old, - old_size); - pool->pos += asize; - return ret; - } - /* does not fit */ - return NULL; -} - - -/** - * Clear all entries from the memory pool except - * for @a keep of the given @a size. The pointer - * returned should be a buffer of @a new_size where - * the first @a copy_bytes are from @a keep. - * - * @param pool memory pool to use for the operation - * @param keep pointer to the entry to keep (maybe NULL) - * @param copy_bytes how many bytes need to be kept at this address - * @param new_size how many bytes should the allocation we return have? - * (should be larger or equal to @a copy_bytes) - * @return addr new address of @a keep (if it had to change) - */ -void * -MHD_pool_reset (struct MemoryPool *pool, - void *keep, - size_t copy_bytes, - size_t new_size) -{ - if ( (NULL != keep) && - (keep != pool->memory) ) - { - if (0 != copy_bytes) - memmove (pool->memory, - keep, - copy_bytes); - keep = pool->memory; - } - pool->end = pool->size; - /* technically not needed, but safer to zero out */ - if (pool->size > copy_bytes) - memset (&pool->memory[copy_bytes], - 0, - pool->size - copy_bytes); - if (NULL != keep) - pool->pos = ROUND_TO_ALIGN (new_size); - return keep; -} - - -/* end of memorypool.c */ diff --git a/src/lib/memorypool.h b/src/lib/memorypool.h @@ -1,130 +0,0 @@ -/* - This file is part of libmicrohttpd - Copyright (C) 2007, 2009 Daniel Pittman and Christian Grothoff - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. - - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with this library; if not, write to the Free Software - Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA -*/ - -/** - * @file memorypool.h - * @brief memory pool; mostly used for efficient (de)allocation - * for each connection and bounding memory use for each - * request - * @author Christian Grothoff - */ - -#ifndef MEMORYPOOL_H -#define MEMORYPOOL_H - -#include "internal.h" - -/** - * Opaque handle for a memory pool. - * Pools are not reentrant and must not be used - * by multiple threads. - */ -struct MemoryPool; - - -/** - * Create a memory pool. - * - * @param max maximum size of the pool - * @return NULL on error - */ -struct MemoryPool * -MHD_pool_create (size_t max); - - -/** - * Destroy a memory pool. - * - * @param pool memory pool to destroy - */ -void -MHD_pool_destroy (struct MemoryPool *pool); - - -/** - * Allocate size bytes from the pool. - * - * @param pool memory pool to use for the operation - * @param size number of bytes to allocate - * @param from_end allocate from end of pool (set to #MHD_YES); - * use this for small, persistent allocations that - * will never be reallocated - * @return NULL if the pool cannot support size more - * bytes - */ -void * -MHD_pool_allocate (struct MemoryPool *pool, - size_t size, - int from_end); - - -/** - * Reallocate a block of memory obtained from the pool. - * This is particularly efficient when growing or - * shrinking the block that was last (re)allocated. - * If the given block is not the most recently - * (re)allocated block, the memory of the previous - * allocation may be leaked until the pool is - * destroyed (and copying the data maybe required). - * - * @param pool memory pool to use for the operation - * @param old the existing block - * @param old_size the size of the existing block - * @param new_size the new size of the block - * @return new address of the block, or - * NULL if the pool cannot support new_size - * bytes (old continues to be valid for old_size) - */ -void * -MHD_pool_reallocate (struct MemoryPool *pool, - void *old, - size_t old_size, - size_t new_size); - - -/** - * Check how much memory is left in the @a pool - * - * @param pool pool to check - * @return number of bytes still available in @a pool - */ -size_t -MHD_pool_get_free (struct MemoryPool *pool); - - -/** - * Clear all entries from the memory pool except - * for @a keep of the given @a copy_bytes. The pointer - * returned should be a buffer of @a new_size where - * the first @a copy_bytes are from @a keep. - * - * @param pool memory pool to use for the operation - * @param keep pointer to the entry to keep (maybe NULL) - * @param copy_bytes how many bytes need to be kept at this address - * @param new_size how many bytes should the allocation we return have? - * (should be larger or equal to @a copy_bytes) - * @return addr new address of @a keep (if it had to change) - */ -void * -MHD_pool_reset (struct MemoryPool *pool, - void *keep, - size_t copy_bytes, - size_t new_size); - -#endif diff --git a/src/lib/mhd_assert.h b/src/lib/mhd_assert.h @@ -1,49 +0,0 @@ -/* - This file is part of libmicrohttpd - Copyright (C) 2017 Karlson2k (Evgeny Grin) - - 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, see <http://www.gnu.org/licenses/>. -*/ - -/** - * @file microhttpd/mhd_assert.h - * @brief macros for mhd_assert() - * @author Karlson2k (Evgeny Grin) - */ - -#ifndef MHD_ASSERT_H -#define MHD_ASSERT_H 1 - -#include "mhd_options.h" -#ifdef NDEBUG -# define mhd_assert(ignore) ((void) 0) -#else /* _DEBUG */ -# ifdef HAVE_ASSERT -# include <assert.h> -# define mhd_assert(CHK) assert (CHK) -# else /* ! HAVE_ASSERT */ -# include <stdio.h> -# include <stdlib.h> -# define mhd_assert(CHK) \ - do { \ - if (! (CHK)) { \ - fprintf (stderr, "%s:%u Assertion failed: %s\nProgram aborted.\n", \ - __FILE__, (unsigned) __LINE__, #CHK); \ - fflush (stderr); abort (); } \ - } while (0) -# endif /* ! HAVE_ASSERT */ -#endif /* _DEBUG */ - -#endif /* ! MHD_ASSERT_H */ diff --git a/src/lib/mhd_byteorder.h b/src/lib/mhd_byteorder.h @@ -1,167 +0,0 @@ -/* - This file is part of libmicrohttpd - Copyright (C) 2015 Karlson2k (Evgeny Grin) - - 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, see <http://www.gnu.org/licenses/>. -*/ - -/** - * @file microhttpd/mhd_byteorder.h - * @brief macro definitions for host byte order - * @author Karlson2k (Evgeny Grin) - */ - -#ifndef MHD_BYTEORDER_H -#define MHD_BYTEORDER_H - -#include "platform.h" - -#ifdef HAVE_ENDIAN_H -#include <endian.h> -#endif - -#ifdef HAVE_SYS_PARAM_H -#include <sys/param.h> -#endif - -#ifdef HAVE_MACHINE_ENDIAN_H -#include <machine/endian.h> -#endif - -#ifdef HAVE_SYS_ENDIAN_H -#include <sys/endian.h> -#endif - -#ifdef HAVE_SYS_TYPES_H -#include <sys/types.h> -#endif - -#ifdef HAVE_SYS_BYTEORDER_H -#include <sys/byteorder.h> -#endif - -#ifdef HAVE_SYS_MACHINE_H -#include <sys/machine.h> -#endif - -#ifdef HAVE_MACHINE_PARAM_H -#include <machine/param.h> -#endif - -#ifdef HAVE_SYS_ISA_DEFS_H -#include <sys/isa_defs.h> -#endif - -#define _MHD_BIG_ENDIAN 1234 -#define _MHD_LITTLE_ENDIAN 4321 -#define _MHD_PDP_ENDIAN 2143 - -#if defined(__BYTE_ORDER__) -#if defined(__ORDER_BIG_ENDIAN__) && __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ -#define _MHD_BYTE_ORDER _MHD_BIG_ENDIAN -#elif defined(__ORDER_LITTLE_ENDIAN__) && __BYTE_ORDER__ == \ - __ORDER_LITTLE_ENDIAN__ -#define _MHD_BYTE_ORDER _MHD_LITTLE_ENDIAN -#elif defined(__ORDER_PDP_ENDIAN__) && __BYTE_ORDER__ == __ORDER_PDP_ENDIAN__ -#define _MHD_BYTE_ORDER _MHD_PDP_ENDIAN -#endif /* __BYTE_ORDER__ == __ORDER_PDP_ENDIAN__ */ -#elif defined(__BYTE_ORDER) -#if defined(__BIG_ENDIAN) && __BYTE_ORDER == __BIG_ENDIAN -#define _MHD_BYTE_ORDER _MHD_BIG_ENDIAN -#elif defined(__LITTLE_ENDIAN) && __BYTE_ORDER == __LITTLE_ENDIAN -#define _MHD_BYTE_ORDER _MHD_LITTLE_ENDIAN -#elif defined(__PDP_ENDIAN) && __BYTE_ORDER == __PDP_ENDIAN -#define _MHD_BYTE_ORDER _MHD_PDP_ENDIAN -#endif /* __BYTE_ORDER == __PDP_ENDIAN */ -#elif defined (BYTE_ORDER) -#if defined(BIG_ENDIAN) && BYTE_ORDER == BIG_ENDIAN -#define _MHD_BYTE_ORDER _MHD_BIG_ENDIAN -#elif defined(LITTLE_ENDIAN) && BYTE_ORDER == LITTLE_ENDIAN -#define _MHD_BYTE_ORDER _MHD_LITTLE_ENDIAN -#elif defined(PDP_ENDIAN) && BYTE_ORDER == PDP_ENDIAN -#define _MHD_BYTE_ORDER _MHD_PDP_ENDIAN -#endif /* __BYTE_ORDER == _PDP_ENDIAN */ -#elif defined (_BYTE_ORDER) -#if defined(_BIG_ENDIAN) && _BYTE_ORDER == _BIG_ENDIAN -#define _MHD_BYTE_ORDER _MHD_BIG_ENDIAN -#elif defined(_LITTLE_ENDIAN) && _BYTE_ORDER == _LITTLE_ENDIAN -#define _MHD_BYTE_ORDER _MHD_LITTLE_ENDIAN -#elif defined(_PDP_ENDIAN) && _BYTE_ORDER == _PDP_ENDIAN -#define _MHD_BYTE_ORDER _MHD_PDP_ENDIAN -#endif /* _BYTE_ORDER == _PDP_ENDIAN */ -#endif /* _BYTE_ORDER */ - -#ifndef _MHD_BYTE_ORDER -/* Byte order specification didn't detected in system headers */ -/* Try some guessing */ - -#if (defined(__BIG_ENDIAN__) && ! defined(__LITTLE_ENDIAN__)) || \ - (defined(_BIG_ENDIAN) && ! defined(_LITTLE_ENDIAN)) -/* Seems that we are on big endian platform */ -#define _MHD_BYTE_ORDER _MHD_BIG_ENDIAN -#elif (defined(__LITTLE_ENDIAN__) && ! defined(__BIG_ENDIAN__)) || \ - (defined(_LITTLE_ENDIAN) && ! defined(_BIG_ENDIAN)) -/* Seems that we are on little endian platform */ -#define _MHD_BYTE_ORDER _MHD_LITTLE_ENDIAN -#elif defined(__amd64__) || defined(__amd64) || defined(__x86_64__) || \ - defined(__x86_64) || \ - defined(_M_X64) || defined(_M_AMD64) || defined(i386) || defined(__i386) || \ - defined(__i386__) || defined(__i486__) || defined(__i586__) || \ - defined(__i686__) || \ - defined(_M_IX86) || defined(_X86_) || defined (__THW_INTEL__) -/* x86 family is little endian */ -#define _MHD_BYTE_ORDER _MHD_LITTLE_ENDIAN -#elif defined(__ARMEB__) || defined(__THUMBEB__) || defined(__AARCH64EB__) || \ - defined(_MIPSEB) || defined(__MIPSEB) || defined(__MIPSEB__) -/* Looks like we are on ARM/MIPS in big endian mode */ -#define _MHD_BYTE_ORDER _MHD_BIG_ENDIAN -#elif defined(__ARMEL__) || defined(__THUMBEL__) || defined(__AARCH64EL__) || \ - defined(_MIPSEL) || defined(__MIPSEL) || defined(__MIPSEL__) -/* Looks like we are on ARM/MIPS in little endian mode */ -#define _MHD_BYTE_ORDER _MHD_LITTLE_ENDIAN -#elif defined(__m68k__) || defined(M68000) || defined(__hppa__) || \ - defined(__hppa) || \ - defined(__HPPA__) || defined(__370__) || defined(__THW_370__) || \ - defined(__s390__) || defined(__s390x__) || defined(__SYSC_ZARCH__) -/* Looks like we are on big endian platform */ -#define _MHD_BYTE_ORDER _MHD_BIG_ENDIAN -#elif defined(__ia64__) || defined(_IA64) || defined(__IA64__) || \ - defined(__ia64) || \ - defined(_M_IA64) || defined(__itanium__) || defined(__bfin__) || \ - defined(__BFIN__) || defined(bfin) || defined(BFIN) -/* Looks like we are on little endian platform */ -#define _MHD_BYTE_ORDER _MHD_LITTLE_ENDIAN -#elif defined(_WIN32) -/* W32 is always little endian on all platforms */ -#define _MHD_BYTE_ORDER _MHD_LITTLE_ENDIAN -#elif defined(WORDS_BIGENDIAN) -/* Use byte order detected by configure */ -#define _MHD_BYTE_ORDER _MHD_BIG_ENDIAN -#endif /* _WIN32 */ - -#endif /* !_MHD_BYTE_ORDER */ - -#ifdef _MHD_BYTE_ORDER -/* Some safety checks */ -#if defined(WORDS_BIGENDIAN) && _MHD_BYTE_ORDER != _MHD_BIG_ENDIAN -#error \ - Configure detected big endian byte order but headers specify different byte order -#elif ! defined(WORDS_BIGENDIAN) && _MHD_BYTE_ORDER == _MHD_BIG_ENDIAN -#error \ - Configure did not detect big endian byte order but headers specify big endian byte order -#endif /* !WORDS_BIGENDIAN && _MHD_BYTE_ORDER == _MHD_BIG_ENDIAN */ -#endif /* _MHD_BYTE_ORDER */ - -#endif /* !MHD_BYTEORDER_H */ diff --git a/src/lib/mhd_compat.c b/src/lib/mhd_compat.c @@ -1,118 +0,0 @@ -/* - This file is part of libmicrohttpd - Copyright (C) 2014-2016 Karlson2k (Evgeny Grin) - - 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 microhttpd/mhd_compat.c - * @brief Implementation of platform missing functions. - * @author Karlson2k (Evgeny Grin) - */ - -#include "mhd_compat.h" -#if defined(_WIN32) && ! defined(__CYGWIN__) -#include <stdint.h> -#include <time.h> -#ifndef HAVE_SNPRINTF -#include <stdio.h> -#include <stdarg.h> -#endif /* HAVE_SNPRINTF */ -#endif /* _WIN32 && !__CYGWIN__ */ - -#ifndef HAVE_CALLOC -#include <string.h> /* for memset() */ -#endif /* ! HAVE_CALLOC */ - -#if defined(_WIN32) && ! defined(__CYGWIN__) - -#ifndef HAVE_SNPRINTF -/* Emulate snprintf function on W32 */ -int -W32_snprintf (char *__restrict s, - size_t n, - const char *__restrict format, - ...) -{ - int ret; - va_list args; - - if ( (0 != n) && - (NULL != s) ) - { - va_start (args, - format); - ret = _vsnprintf (s, - n, - format, - args); - va_end (args); - if ((int) n == ret) - s[n - 1] = 0; - if (ret >= 0) - return ret; - } - va_start (args, - format); - ret = _vscprintf (format, - args); - va_end (args); - if ( (0 <= ret) && - (0 != n) && - (NULL == s) ) - return -1; - - return ret; -} - - -#endif /* HAVE_SNPRINTF */ -#endif /* _WIN32 && !__CYGWIN__ */ - -#ifndef HAVE_CALLOC - -#ifdef __has_builtin -# if __has_builtin (__builtin_mul_overflow) -# define MHD_HAVE_NUL_OVERFLOW 1 -# endif -#elif __GNUC__ + 0 >= 5 -# define MHD_HAVE_NUL_OVERFLOW 1 -#endif /* __GNUC__ >= 5 */ - - -void * -MHD_calloc_ (size_t nelem, size_t elsize) -{ - size_t alloc_size; - void *ptr; -#ifdef MHD_HAVE_NUL_OVERFLOW - if (__builtin_mul_overflow (nelem, elsize, &alloc_size) || (0 == alloc_size)) - return NULL; -#else /* ! MHD_HAVE_NUL_OVERFLOW */ - alloc_size = nelem * elsize; - if ((0 == alloc_size) || (elsize != alloc_size / nelem)) - return NULL; -#endif /* ! MHD_HAVE_NUL_OVERFLOW */ - ptr = malloc (alloc_size); - if (NULL == ptr) - return NULL; - memset (ptr, 0, alloc_size); - return ptr; -} - - -#endif /* ! HAVE_CALLOC */ diff --git a/src/lib/mhd_compat.h b/src/lib/mhd_compat.h @@ -1,91 +0,0 @@ -/* - This file is part of libmicrohttpd - Copyright (C) 2014-2016 Karlson2k (Evgeny Grin) - - 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 microhttpd/mhd_compat.h - * @brief Header for platform missing functions. - * @author Karlson2k (Evgeny Grin) - * - * Provides compatibility for platforms with some missing - * functionality. - * Any functions can be implemented as macro on some platforms - * unless explicitly marked otherwise. - * Any function argument can be skipped in macro, so avoid - * variable modification in function parameters. - */ - -#ifndef MHD_COMPAT_H -#define MHD_COMPAT_H 1 - -#include "mhd_options.h" -#include <stdlib.h> -#ifdef HAVE_STRING_H /* for strerror() */ -#include <string.h> -#endif /* HAVE_STRING_H */ - -/* MHD_strerror_ is strerror */ -#define MHD_strerror_(errnum) strerror ((errnum)) - -/* Platform-independent snprintf name */ -#if defined(HAVE_SNPRINTF) -#define MHD_snprintf_ snprintf -#else /* ! HAVE_SNPRINTF */ -#if defined(_WIN32) && ! defined(__CYGWIN__) -/* Emulate snprintf function on W32 */ -int W32_snprintf (char *__restrict s, size_t n, const char *__restrict format, - ...); - -#define MHD_snprintf_ W32_snprintf -#else /* ! _WIN32 || __CYGWIN__ */ -#error \ - Your platform does not support snprintf() and MHD does not know how to emulate it on your platform. -#endif /* ! _WIN32 || __CYGWIN__ */ -#endif /* ! HAVE_SNPRINTF */ - -#ifdef HAVE_RANDOM -/** - * Generate pseudo random number at least 30-bit wide. - * @return pseudo random number at least 30-bit wide. - */ -#define MHD_random_() random () -#else /* HAVE_RANDOM */ -#ifdef HAVE_RAND -/** - * Generate pseudo random number at least 30-bit wide. - * @return pseudo random number at least 30-bit wide. - */ -#define MHD_random_() ( (((long) rand ()) << 15) + (long) rand () ) -#endif /* HAVE_RAND */ -#endif /* HAVE_RANDOM */ - -#ifdef HAVE_CALLOC -/** - * MHD_calloc_ is platform-independent calloc() - */ -#define MHD_calloc_(n,s) calloc ((n),(s)) -#else /* ! HAVE_CALLOC */ -/** - * MHD_calloc_ is platform-independent calloc() - */ -void *MHD_calloc_ (size_t nelem, size_t elsize); - -#endif /* ! HAVE_CALLOC */ - -#endif /* MHD_COMPAT_H */ diff --git a/src/lib/mhd_itc.c b/src/lib/mhd_itc.c @@ -1,72 +0,0 @@ -/* - This file is part of libmicrohttpd - Copyright (C) 2016 Karlson2k (Evgeny Grin) - - 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 microhttpd/mhd_itc.c - * @brief Implementation of inter-thread communication functions - * @author Karlson2k (Evgeny Grin) - * @author Christian Grothoff - */ - -#include "mhd_itc.h" -#ifdef HAVE_UNISTD_H -#include <unistd.h> -#endif /* HAVE_UNISTD_H */ -#include <fcntl.h> -#include "internal.h" - - -#if defined(_MHD_ITC_PIPE) -#if ! defined(_WIN32) || defined(__CYGWIN__) - -#ifndef HAVE_PIPE2_FUNC -/** - * Change itc FD options to be non-blocking. - * - * @param itc the inter-thread communication primitive to manipulate - * @return non-zero if succeeded, zero otherwise - */ -int -MHD_itc_nonblocking_ (struct MHD_itc_ itc) -{ - unsigned int i; - - for (i = 0; i < 2; i++) - { - int flags; - - flags = fcntl (itc.fd[i], - F_GETFL); - if (-1 == flags) - return 0; - - if ( ((flags | O_NONBLOCK) != flags) && - (0 != fcntl (itc.fd[i], - F_SETFL, - flags | O_NONBLOCK)) ) - return 0; - } - return ! 0; -} - - -#endif /* ! HAVE_PIPE2_FUNC */ -#endif /* !_WIN32 || __CYGWIN__ */ -#endif /* _MHD_ITC_EVENTFD || _MHD_ITC_PIPE */ diff --git a/src/lib/mhd_itc.h b/src/lib/mhd_itc.h @@ -1,369 +0,0 @@ -/* - This file is part of libmicrohttpd - Copyright (C) 2016 Karlson2k (Evgeny Grin) - - 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 microhttpd/mhd_itc.h - * @brief Header for platform-independent inter-thread communication - * @author Karlson2k (Evgeny Grin) - * @author Christian Grothoff - * - * Provides basic abstraction for inter-thread communication. - * Any functions can be implemented as macro on some platforms - * unless explicitly marked otherwise. - * Any function argument can be skipped in macro, so avoid - * variable modification in function parameters. - */ -#ifndef MHD_ITC_H -#define MHD_ITC_H 1 -#include "mhd_itc_types.h" - -#include <fcntl.h> - -#ifndef MHD_PANIC -# include <stdio.h> -# include <stdlib.h> -/* Simple implementation of MHD_PANIC, to be used outside lib */ -# define MHD_PANIC(msg) do { fprintf (stderr, \ - "Abnormal termination at %d line in file %s: %s\n", \ - (int) __LINE__, __FILE__, msg); abort (); \ -} while (0) -#endif /* ! MHD_PANIC */ - -#if defined(_MHD_ITC_EVENTFD) - -/* **************** Optimized GNU/Linux ITC implementation by eventfd ********** */ -#include <sys/eventfd.h> -#include <stdint.h> /* for uint64_t */ -#ifdef HAVE_UNISTD_H -#include <unistd.h> /* for read(), write(), errno */ -#endif /* HAVE_UNISTD_H */ -#ifdef HAVE_STRING_H -#include <string.h> /* for strerror() */ -#endif - - -/** - * Initialise ITC by generating eventFD - * @param itc the itc to initialise - * @return non-zero if succeeded, zero otherwise - */ -#define MHD_itc_init_(itc) (-1 != ((itc).fd = eventfd (0, EFD_CLOEXEC \ - | EFD_NONBLOCK))) - -/** - * Get description string of last errno for itc operations. - */ -#define MHD_itc_last_strerror_() strerror (errno) - -/** - * Internal static const helper for MHD_itc_activate_() - */ -static const uint64_t _MHD_itc_wr_data = 1; - -/** - * Activate signal on @a itc - * @param itc the itc to use - * @param str ignored - * @return non-zero if succeeded, zero otherwise - */ -#define MHD_itc_activate_(itc, str) \ - ((write ((itc).fd, (const void*) &_MHD_itc_wr_data, 8) > 0) || (EAGAIN == \ - errno)) - -/** - * Return read FD of @a itc which can be used for poll(), select() etc. - * @param itc the itc to get FD - * @return FD of read side - */ -#define MHD_itc_r_fd_(itc) ((itc).fd) - -/** - * Return write FD of @a itc - * @param itc the itc to get FD - * @return FD of write side - */ -#define MHD_itc_w_fd_(itc) ((itc).fd) - -/** - * Clear signaled state on @a itc - * @param itc the itc to clear - */ -#define MHD_itc_clear_(itc) \ - do { uint64_t __b; int __r; \ - __r = read ((itc).fd, &__b, sizeof(__b)); \ - (void) __r; } while (0) - -/** - * Destroy previously initialised ITC. Note that close() - * on some platforms returns odd errors, so we ONLY fail - * if the errno is EBADF. - * @param itc the itc to destroy - * @return non-zero if succeeded, zero otherwise - */ -#define MHD_itc_destroy_(itc) ((0 == close ((itc).fd)) || (EBADF != errno)) - -/** - * Check whether ITC has valid value. - * - * Macro check whether @a itc value is valid (allowed), - * macro does not check whether @a itc was really initialised. - * @param itc the itc to check - * @return boolean true if @a itc has valid value, - * boolean false otherwise. - */ -#define MHD_ITC_IS_VALID_(itc) (-1 != ((itc).fd)) - -/** - * Set @a itc to invalid value. - * @param itc the itc to set - */ -#define MHD_itc_set_invalid_(itc) ((itc).fd = -1) - - -#elif defined(_MHD_ITC_PIPE) - -/* **************** Standard UNIX ITC implementation by pipe ********** */ - -#if defined(HAVE_PIPE2_FUNC) && defined(HAVE_FCNTL_H) -# include <fcntl.h> /* for O_CLOEXEC, O_NONBLOCK */ -#endif /* HAVE_PIPE2_FUNC && HAVE_FCNTL_H */ -#ifdef HAVE_UNISTD_H -#include <unistd.h> /* for read(), write(), errno */ -#endif /* HAVE_UNISTD_H */ -#ifdef HAVE_STRING_H -#include <string.h> /* for strerror() */ -#endif - - -/** - * Initialise ITC by generating pipe - * @param itc the itc to initialise - * @return non-zero if succeeded, zero otherwise - */ -#ifdef HAVE_PIPE2_FUNC -# define MHD_itc_init_(itc) (! pipe2 ((itc).fd, O_CLOEXEC | O_NONBLOCK)) -#else /* ! HAVE_PIPE2_FUNC */ -# define MHD_itc_init_(itc) \ - ( (! pipe ((itc).fd)) ? \ - (MHD_itc_nonblocking_ ((itc)) ? \ - (! 0) : \ - (MHD_itc_destroy_ ((itc)), 0) ) \ - : (0) ) -#endif /* ! HAVE_PIPE2_FUNC */ - -/** - * Get description string of last errno for itc operations. - */ -#define MHD_itc_last_strerror_() strerror (errno) - -/** - * Activate signal on @a itc - * @param itc the itc to use - * @param str one-symbol string, useful only for strace debug - * @return non-zero if succeeded, zero otherwise - */ -#define MHD_itc_activate_(itc, str) \ - ((write ((itc).fd[1], (const void*) (str), 1) > 0) || (EAGAIN == errno)) - - -/** - * Return read FD of @a itc which can be used for poll(), select() etc. - * @param itc the itc to get FD - * @return FD of read side - */ -#define MHD_itc_r_fd_(itc) ((itc).fd[0]) - -/** - * Return write FD of @a itc - * @param itc the itc to get FD - * @return FD of write side - */ -#define MHD_itc_w_fd_(itc) ((itc).fd[1]) - -/** - * Clear signaled state on @a itc - * @param itc the itc to clear - */ -#define MHD_itc_clear_(itc) do \ - { long __b; \ - while (0 < read ((itc).fd[0], &__b, sizeof(__b))) \ - {} } while (0) - -/** - * Destroy previously initialised ITC - * @param itc the itc to destroy - * @return non-zero if succeeded, zero otherwise - */ -#define MHD_itc_destroy_(itc) \ - ( (0 == close ((itc).fd[0])) ? \ - (0 == close ((itc).fd[1])) : \ - ((close ((itc).fd[1])), 0) ) - -/** - * Check whether ITC has valid value. - * - * Macro check whether @a itc value is valid (allowed), - * macro does not check whether @a itc was really initialised. - * @param itc the itc to check - * @return boolean true if @a itc has valid value, - * boolean false otherwise. - */ -#define MHD_ITC_IS_VALID_(itc) (-1 != (itc).fd[0]) - -/** - * Set @a itc to invalid value. - * @param itc the itc to set - */ -#define MHD_itc_set_invalid_(itc) ((itc).fd[0] = (itc).fd[1] = -1) - -#ifndef HAVE_PIPE2_FUNC -/** - * Change itc FD options to be non-blocking. - * - * @param fd the FD to manipulate - * @return non-zero if succeeded, zero otherwise - */ -int -MHD_itc_nonblocking_ (struct MHD_itc_ itc); - -#endif /* ! HAVE_PIPE2_FUNC */ - - -#elif defined(_MHD_ITC_SOCKETPAIR) - -/* **************** ITC implementation by socket pair ********** */ - -#include "mhd_sockets.h" - - -/** - * Initialise ITC by generating socketpair - * @param itc the itc to initialise - * @return non-zero if succeeded, zero otherwise - */ -#ifdef MHD_socket_pair_nblk_ -# define MHD_itc_init_(itc) MHD_socket_pair_nblk_ ((itc).sk) -#else /* ! MHD_socket_pair_nblk_ */ -# define MHD_itc_init_(itc) \ - (MHD_socket_pair_ ((itc).sk) ? \ - (MHD_itc_nonblocking_ ((itc)) ? \ - (! 0) : \ - (MHD_itc_destroy_ ((itc)), 0) ) \ - : (0)) -#endif /* ! MHD_socket_pair_nblk_ */ - -/** - * Get description string of last error for itc operations. - */ -#define MHD_itc_last_strerror_() MHD_socket_last_strerr_ () - -/** - * Activate signal on @a itc - * @param itc the itc to use - * @param str one-symbol string, useful only for strace debug - * @return non-zero if succeeded, zero otherwise - */ -#define MHD_itc_activate_(itc, str) \ - ((MHD_send_ ((itc).sk[1], (str), 1) > 0) || \ - (MHD_SCKT_ERR_IS_EAGAIN_ (MHD_socket_get_error_ ()))) - -/** - * Return read FD of @a itc which can be used for poll(), select() etc. - * @param itc the itc to get FD - * @return FD of read side - */ -#define MHD_itc_r_fd_(itc) ((itc).sk[0]) - -/** - * Return write FD of @a itc - * @param itc the itc to get FD - * @return FD of write side - */ -#define MHD_itc_w_fd_(itc) ((itc).sk[1]) - -/** - * Clear signaled state on @a itc - * @param itc the itc to clear - */ -#define MHD_itc_clear_(itc) do \ - { long __b; \ - while (0 < recv ((itc).sk[0], \ - (char*) &__b, \ - sizeof(__b), 0)) \ - {} } while (0) - -/** - * Destroy previously initialised ITC - * @param itc the itc to destroy - * @return non-zero if succeeded, zero otherwise - */ -#define MHD_itc_destroy_(itc) \ - (MHD_socket_close_ ((itc).sk[0]) ? \ - MHD_socket_close_ ((itc).sk[1]) : \ - ((void) MHD_socket_close_ ((itc).sk[1]), 0) ) - - -/** - * Check whether ITC has valid value. - * - * Macro check whether @a itc value is valid (allowed), - * macro does not check whether @a itc was really initialised. - * @param itc the itc to check - * @return boolean true if @a itc has valid value, - * boolean false otherwise. - */ -#define MHD_ITC_IS_VALID_(itc) (MHD_INVALID_SOCKET != (itc).sk[0]) - -/** - * Set @a itc to invalid value. - * @param itc the itc to set - */ -#define MHD_itc_set_invalid_(itc) ((itc).sk[0] = (itc).sk[1] = \ - MHD_INVALID_SOCKET) - -#ifndef MHD_socket_pair_nblk_ -# define MHD_itc_nonblocking_(pip) (MHD_socket_nonblocking_ ((pip).sk[0]) && \ - MHD_socket_nonblocking_ ((pip).sk[1])) -#endif /* ! MHD_socket_pair_nblk_ */ - -#endif /* _MHD_ITC_SOCKETPAIR */ - -/** - * Destroy previously initialised ITC and abort execution - * if error is detected. - * @param itc the itc to destroy - */ -#define MHD_itc_destroy_chk_(itc) do { \ - if (! MHD_itc_destroy_ (itc)) \ - MHD_PANIC (_ ("Failed to destroy ITC.\n")); \ -} while (0) - -/** - * Check whether ITC has invalid value. - * - * Macro check whether @a itc value is invalid, - * macro does not check whether @a itc was destroyed. - * @param itc the itc to check - * @return boolean true if @a itc has invalid value, - * boolean false otherwise. - */ -#define MHD_ITC_IS_INVALID_(itc) (! MHD_ITC_IS_VALID_ (itc)) - -#endif /* MHD_ITC_H */ diff --git a/src/lib/mhd_itc_types.h b/src/lib/mhd_itc_types.h @@ -1,77 +0,0 @@ -/* - This file is part of libmicrohttpd - Copyright (C) 2016 Karlson2k (Evgeny Grin), Christian Grothoff - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. - - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with this library; if not, write to the Free Software - Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - -*/ - -/** - * @file microhttpd/mhd_itc_types.h - * @brief Types for platform-independent inter-thread communication - * @author Karlson2k (Evgeny Grin) - * @author Christian Grothoff - * - * Provides basic types for inter-thread communication. - * Designed to be included by other headers. - */ -#ifndef MHD_ITC_TYPES_H -#define MHD_ITC_TYPES_H 1 -#include "mhd_options.h" - -/* Force socketpair on native W32 */ -#if defined(_WIN32) && ! defined(__CYGWIN__) && ! defined(_MHD_ITC_SOCKETPAIR) -#error _MHD_ITC_SOCKETPAIR is not defined on naitive W32 platform -#endif /* _WIN32 && !__CYGWIN__ && !_MHD_ITC_SOCKETPAIR */ - -#if defined(_MHD_ITC_EVENTFD) -/* **************** Optimized GNU/Linux ITC implementation by eventfd ********** */ - -/** - * Data type for a MHD ITC. - */ -struct MHD_itc_ -{ - int fd; -}; - -#elif defined(_MHD_ITC_PIPE) -/* **************** Standard UNIX ITC implementation by pipe ********** */ - -/** - * Data type for a MHD ITC. - */ -struct MHD_itc_ -{ - int fd[2]; -}; - - -#elif defined(_MHD_ITC_SOCKETPAIR) -/* **************** ITC implementation by socket pair ********** */ - -#include "mhd_sockets.h" - -/** - * Data type for a MHD ITC. - */ -struct MHD_itc_ -{ - MHD_socket sk[2]; -}; - -#endif /* _MHD_ITC_SOCKETPAIR */ - -#endif /* ! MHD_ITC_TYPES_H */ diff --git a/src/lib/mhd_limits.h b/src/lib/mhd_limits.h @@ -1,154 +0,0 @@ -/* - This file is part of libmicrohttpd - Copyright (C) 2015 Karlson2k (Evgeny Grin) - - 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 microhttpd/mhd_limits.h - * @brief limits values definitions - * @author Karlson2k (Evgeny Grin) - */ - -#ifndef MHD_LIMITS_H -#define MHD_LIMITS_H - -#include "platform.h" - -#ifdef HAVE_LIMITS_H -#include <limits.h> -#endif /* HAVE_LIMITS_H */ - -#define MHD_UNSIGNED_TYPE_MAX_(type) ((type) - 1) -/* Assume 8 bits per byte, no padding bits. */ -#define MHD_SIGNED_TYPE_MAX_(type) \ - ( (type) ((( ((type) 1) << (sizeof(type) * 8 - 2)) - 1) * 2 + 1) ) -#define MHD_TYPE_IS_SIGNED_(type) (((type) 0)>((type) - 1)) - -#ifndef UINT_MAX -#ifdef __UINT_MAX__ -#define UINT_MAX __UINT_MAX__ -#else /* ! __UINT_MAX__ */ -#define UINT_MAX MHD_UNSIGNED_TYPE_MAX_ (unsigned int) -#endif /* ! __UINT_MAX__ */ -#endif /* !UINT_MAX */ - -#ifndef LONG_MAX -#ifdef __LONG_MAX__ -#define LONG_MAX __LONG_MAX__ -#else /* ! __LONG_MAX__ */ -#define LONG_MAX MHD_SIGNED_TYPE_MAX (long) -#endif /* ! __LONG_MAX__ */ -#endif /* !OFF_T_MAX */ - -#ifndef ULLONG_MAX -#define ULLONG_MAX MHD_UNSIGNED_TYPE_MAX_ (MHD_UNSIGNED_LONG_LONG) -#endif /* !ULLONG_MAX */ - -#ifndef INT32_MAX -#ifdef __INT32_MAX__ -#define INT32_MAX __INT32_MAX__ -#else /* ! __INT32_MAX__ */ -#define INT32_MAX ((int32_t) 0x7FFFFFFF) -#endif /* ! __INT32_MAX__ */ -#endif /* !INT32_MAX */ - -#ifndef UINT32_MAX -#ifdef __UINT32_MAX__ -#define UINT32_MAX __UINT32_MAX__ -#else /* ! __UINT32_MAX__ */ -#define UINT32_MAX ((int32_t) 0xFFFFFFFF) -#endif /* ! __UINT32_MAX__ */ -#endif /* !UINT32_MAX */ - -#ifndef UINT64_MAX -#ifdef __UINT64_MAX__ -#define UINT64_MAX __UINT64_MAX__ -#else /* ! __UINT64_MAX__ */ -#define UINT64_MAX ((uint64_t) 0xFFFFFFFFFFFFFFFF) -#endif /* ! __UINT64_MAX__ */ -#endif /* !UINT64_MAX */ - -#ifndef INT64_MAX -#ifdef __INT64_MAX__ -#define INT64_MAX __INT64_MAX__ -#else /* ! __INT64_MAX__ */ -#define INT64_MAX ((int64_t) 0x7FFFFFFFFFFFFFFF) -#endif /* ! __UINT64_MAX__ */ -#endif /* !INT64_MAX */ - -#ifndef SIZE_MAX -#ifdef __SIZE_MAX__ -#define SIZE_MAX __SIZE_MAX__ -#elif defined(UINTPTR_MAX) -#define SIZE_MAX UINTPTR_MAX -#else /* ! __SIZE_MAX__ */ -#define SIZE_MAX MHD_UNSIGNED_TYPE_MAX_ (size_t) -#endif /* ! __SIZE_MAX__ */ -#endif /* !SIZE_MAX */ - -#ifndef SSIZE_MAX -#ifdef __SSIZE_MAX__ -#define SSIZE_MAX __SSIZE_MAX__ -#elif defined(PTRDIFF_MAX) -#define SSIZE_MAX PTRDIFF_MAX -#elif defined(INTPTR_MAX) -#define SSIZE_MAX INTPTR_MAX -#else -#define SSIZE_MAN MHD_SIGNED_TYPE_MAX_ (ssize_t) -#endif -#endif /* ! SSIZE_MAX */ - -#ifndef OFF_T_MAX -#ifdef OFF_MAX -#define OFF_T_MAX OFF_MAX -#elif defined(OFFT_MAX) -#define OFF_T_MAX OFFT_MAX -#elif defined(__APPLE__) && defined(__MACH__) -#define OFF_T_MAX INT64_MAX -#else -#define OFF_T_MAX MHD_SIGNED_TYPE_MAX_ (off_t) -#endif -#endif /* !OFF_T_MAX */ - -#if defined(_LARGEFILE64_SOURCE) && ! defined(OFF64_T_MAX) -#define OFF64_T_MAX MHD_SIGNED_TYPE_MAX_ (uint64_t) -#endif /* _LARGEFILE64_SOURCE && !OFF64_T_MAX */ - -#ifndef TIME_T_MAX -#define TIME_T_MAX ((time_t) \ - (MHD_TYPE_IS_SIGNED_ (time_t) ? \ - MHD_SIGNED_TYPE_MAX_ (time_t) : \ - MHD_UNSIGNED_TYPE_MAX_ (time_t))) -#endif /* !TIME_T_MAX */ - -#ifndef TIMEVAL_TV_SEC_MAX -#ifndef _WIN32 -#define TIMEVAL_TV_SEC_MAX TIME_T_MAX -#else /* _WIN32 */ -#define TIMEVAL_TV_SEC_MAX LONG_MAX -#endif /* _WIN32 */ -#endif /* !TIMEVAL_TV_SEC_MAX */ - -#ifndef MHD_FD_BLOCK_SIZE -#ifdef _WIN32 -#define MHD_FD_BLOCK_SIZE 16384 /* 16k */ -#else /* _WIN32 */ -#define MHD_FD_BLOCK_SIZE 4096 /* 4k */ -#endif /* _WIN32 */ -#endif /* !MHD_FD_BLOCK_SIZE */ - -#endif /* MHD_LIMITS_H */ diff --git a/src/lib/mhd_locks.h b/src/lib/mhd_locks.h @@ -1,186 +0,0 @@ -/* - This file is part of libmicrohttpd - Copyright (C) 2016 Karlson2k (Evgeny Grin) - - 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 microhttpd/mhd_locks.h - * @brief Header for platform-independent locks abstraction - * @author Karlson2k (Evgeny Grin) - * @author Christian Grothoff - * - * Provides basic abstraction for locks/mutex. - * Any functions can be implemented as macro on some platforms - * unless explicitly marked otherwise. - * Any function argument can be skipped in macro, so avoid - * variable modification in function parameters. - * - * @warning Unlike pthread functions, most of functions return - * nonzero on success. - */ - -#ifndef MHD_LOCKS_H -#define MHD_LOCKS_H 1 - -#include "mhd_options.h" - -#if defined(MHD_USE_W32_THREADS) -# define MHD_W32_MUTEX_ 1 -# ifndef WIN32_LEAN_AND_MEAN -# define WIN32_LEAN_AND_MEAN 1 -# endif /* !WIN32_LEAN_AND_MEAN */ -# include <windows.h> -#elif defined(HAVE_PTHREAD_H) && defined(MHD_USE_POSIX_THREADS) -# define MHD_PTHREAD_MUTEX_ 1 -# undef HAVE_CONFIG_H -# include <pthread.h> -# define HAVE_CONFIG_H 1 -#else -# error No base mutex API is available. -#endif - -#ifndef MHD_PANIC -# include <stdio.h> -# include <stdlib.h> -/* Simple implementation of MHD_PANIC, to be used outside lib */ -# define MHD_PANIC(msg) do { fprintf (stderr, \ - "Abnormal termination at %d line in file %s: %s\n", \ - (int) __LINE__, __FILE__, msg); abort (); \ -} while (0) -#endif /* ! MHD_PANIC */ - -#if defined(MHD_PTHREAD_MUTEX_) -typedef pthread_mutex_t MHD_mutex_; -#elif defined(MHD_W32_MUTEX_) -typedef CRITICAL_SECTION MHD_mutex_; -#endif - -#if defined(MHD_PTHREAD_MUTEX_) -/** - * Initialise new mutex. - * @param pmutex pointer to the mutex - * @return nonzero on success, zero otherwise - */ -#define MHD_mutex_init_(pmutex) (! (pthread_mutex_init ((pmutex), NULL))) -#elif defined(MHD_W32_MUTEX_) -/** - * Initialise new mutex. - * @param pmutex pointer to mutex - * @return nonzero on success, zero otherwise - */ -#define MHD_mutex_init_(pmutex) (InitializeCriticalSectionAndSpinCount ( \ - (pmutex),16)) -#endif - -#if defined(MHD_PTHREAD_MUTEX_) -# if defined(PTHREAD_MUTEX_INITIALIZER) -/** - * Define static mutex and statically initialise it. - */ -# define MHD_MUTEX_STATIC_DEFN_INIT_(m) static MHD_mutex_ m = \ - PTHREAD_MUTEX_INITIALIZER -# endif /* PTHREAD_MUTEX_INITIALIZER */ -#endif - -#if defined(MHD_PTHREAD_MUTEX_) -/** - * Destroy previously initialised mutex. - * @param pmutex pointer to mutex - * @return nonzero on success, zero otherwise - */ -#define MHD_mutex_destroy_(pmutex) (! (pthread_mutex_destroy ((pmutex)))) -#elif defined(MHD_W32_MUTEX_) -/** - * Destroy previously initialised mutex. - * @param pmutex pointer to mutex - * @return Always nonzero - */ -#define MHD_mutex_destroy_(pmutex) (DeleteCriticalSection ((pmutex)), ! 0) -#endif - -/** - * Destroy previously initialised mutex and abort execution - * if error is detected. - * @param pmutex pointer to mutex - */ -#define MHD_mutex_destroy_chk_(pmutex) do { \ - if (! MHD_mutex_destroy_ (pmutex)) \ - MHD_PANIC (_ ("Failed to destroy mutex.\n")); \ -} while (0) - - -#if defined(MHD_PTHREAD_MUTEX_) -/** - * Acquire lock on previously initialised mutex. - * If mutex was already locked by other thread, function - * blocks until mutex becomes available. - * @param pmutex pointer to mutex - * @return nonzero on success, zero otherwise - */ -#define MHD_mutex_lock_(pmutex) (! (pthread_mutex_lock ((pmutex)))) -#elif defined(MHD_W32_MUTEX_) -/** - * Acquire lock on previously initialised mutex. - * If mutex was already locked by other thread, function - * blocks until mutex becomes available. - * @param pmutex pointer to mutex - * @return Always nonzero - */ -#define MHD_mutex_lock_(pmutex) (EnterCriticalSection ((pmutex)), ! 0) -#endif - -/** - * Acquire lock on previously initialised mutex. - * If mutex was already locked by other thread, function - * blocks until mutex becomes available. - * If error is detected, execution will be aborted. - * @param pmutex pointer to mutex - */ -#define MHD_mutex_lock_chk_(pmutex) do { \ - if (! MHD_mutex_lock_ (pmutex)) \ - MHD_PANIC (_ ("Failed to lock mutex.\n")); \ -} while (0) - -#if defined(MHD_PTHREAD_MUTEX_) -/** - * Unlock previously initialised and locked mutex. - * @param pmutex pointer to mutex - * @return nonzero on success, zero otherwise - */ -#define MHD_mutex_unlock_(pmutex) (! (pthread_mutex_unlock ((pmutex)))) -#elif defined(MHD_W32_MUTEX_) -/** - * Unlock previously initialised and locked mutex. - * @param pmutex pointer to mutex - * @return Always nonzero - */ -#define MHD_mutex_unlock_(pmutex) (LeaveCriticalSection ((pmutex)), ! 0) -#endif - -/** - * Unlock previously initialised and locked mutex. - * If error is detected, execution will be aborted. - * @param pmutex pointer to mutex - */ -#define MHD_mutex_unlock_chk_(pmutex) do { \ - if (! MHD_mutex_unlock_ (pmutex)) \ - MHD_PANIC (_ ("Failed to unlock mutex.\n")); \ -} while (0) - - -#endif /* ! MHD_LOCKS_H */ diff --git a/src/lib/mhd_mono_clock.c b/src/lib/mhd_mono_clock.c @@ -1,378 +0,0 @@ -/* - This file is part of libmicrohttpd - Copyright (C) 2015 Karlson2k (Evgeny Grin) - - 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 microhttpd/mhd_mono_clock.h - * @brief internal monotonic clock functions implementations - * @author Karlson2k (Evgeny Grin) - */ - -#include "mhd_mono_clock.h" - -#if defined(_WIN32) && ! defined(__CYGWIN__) && defined(HAVE_CLOCK_GETTIME) -/* Prefer native clock source over wrappers */ -#undef HAVE_CLOCK_GETTIME -#endif /* _WIN32 && ! __CYGWIN__ && HAVE_CLOCK_GETTIME */ - -#ifdef HAVE_CLOCK_GETTIME -#include <time.h> -#endif /* HAVE_CLOCK_GETTIME */ - -#ifdef HAVE_GETHRTIME -#ifdef HAVE_SYS_TIME_H -/* Solaris defines gethrtime() in sys/time.h */ -#include <sys/time.h> -#endif /* HAVE_SYS_TIME_H */ -#ifdef HAVE_TIME_H -/* HP-UX defines gethrtime() in time.h */ -#include <time.h> -#endif /* HAVE_TIME_H */ -#endif /* HAVE_GETHRTIME */ - -#ifdef HAVE_CLOCK_GET_TIME -#include <mach/mach.h> -/* for host_get_clock_service(), mach_host_self(), mach_task_self() */ -#include <mach/clock.h> -/* for clock_get_time() */ - -#define _MHD_INVALID_CLOCK_SERV ((clock_serv_t) -2) - -static clock_serv_t mono_clock_service = _MHD_INVALID_CLOCK_SERV; -#endif /* HAVE_CLOCK_GET_TIME */ - -#ifdef _WIN32 -#ifndef WIN32_LEAN_AND_MEAN -/* Do not include unneeded parts of W32 headers. */ -#define WIN32_LEAN_AND_MEAN 1 -#endif /* !WIN32_LEAN_AND_MEAN */ -#include <windows.h> -#include <stdint.h> -#endif /* _WIN32 */ - -#ifdef HAVE_CLOCK_GETTIME -#ifdef CLOCK_REALTIME -#define _MHD_UNWANTED_CLOCK CLOCK_REALTIME -#else /* !CLOCK_REALTIME */ -#define _MHD_UNWANTED_CLOCK ((clockid_t) -2) -#endif /* !CLOCK_REALTIME */ - -static clockid_t mono_clock_id = _MHD_UNWANTED_CLOCK; -#endif /* HAVE_CLOCK_GETTIME */ - -/* sync clocks; reduce chance of value wrap */ -#if defined(HAVE_CLOCK_GETTIME) || defined(HAVE_CLOCK_GET_TIME) || \ - defined(HAVE_GETHRTIME) -static time_t mono_clock_start; -#endif /* HAVE_CLOCK_GETTIME || HAVE_CLOCK_GET_TIME || HAVE_GETHRTIME */ -static time_t sys_clock_start; -#ifdef HAVE_GETHRTIME -static hrtime_t hrtime_start; -#endif /* HAVE_GETHRTIME */ -#ifdef _WIN32 -#if _WIN32_WINNT >= 0x0600 -static uint64_t tick_start; -#else /* _WIN32_WINNT < 0x0600 */ -static int64_t perf_freq; -static int64_t perf_start; -#endif /* _WIN32_WINNT < 0x0600 */ -#endif /* _WIN32 */ - - -/** - * Type of monotonic clock source - */ -enum _MHD_mono_clock_source -{ - /** - * No monotonic clock - */ - _MHD_CLOCK_NO_SOURCE = 0, - - /** - * clock_gettime() with specific clock - */ - _MHD_CLOCK_GETTIME, - - /** - * clock_get_time() with specific clock service - */ - _MHD_CLOCK_GET_TIME, - - /** - * gethrtime() / 1000000000 - */ - _MHD_CLOCK_GETHRTIME, - - /** - * GetTickCount64() / 1000 - */ - _MHD_CLOCK_GETTICKCOUNT64, - - /** - * QueryPerformanceCounter() / QueryPerformanceFrequency() - */ - _MHD_CLOCK_PERFCOUNTER -}; - - -/** - * Initialise monotonic seconds counter. - */ -void -MHD_monotonic_sec_counter_init (void) -{ -#ifdef HAVE_CLOCK_GET_TIME - mach_timespec_t cur_time; -#endif /* HAVE_CLOCK_GET_TIME */ - enum _MHD_mono_clock_source mono_clock_source = _MHD_CLOCK_NO_SOURCE; -#ifdef HAVE_CLOCK_GETTIME - struct timespec ts; - - mono_clock_id = _MHD_UNWANTED_CLOCK; -#endif /* HAVE_CLOCK_GETTIME */ -#ifdef HAVE_CLOCK_GET_TIME - mono_clock_service = _MHD_INVALID_CLOCK_SERV; -#endif /* HAVE_CLOCK_GET_TIME */ - - /* just a little syntactic trick to get the - various following ifdef's to work out nicely */ - if (0) - { - } - else -#ifdef HAVE_CLOCK_GETTIME -#ifdef CLOCK_MONOTONIC_COARSE - /* Linux-specific fast value-getting clock */ - /* Can be affected by frequency adjustment and don't count time in suspend, */ - /* but preferred since it's fast */ - if (0 == clock_gettime (CLOCK_MONOTONIC_COARSE, - &ts)) - { - mono_clock_id = CLOCK_MONOTONIC_COARSE; - mono_clock_start = ts.tv_sec; - mono_clock_source = _MHD_CLOCK_GETTIME; - } - else -#endif /* CLOCK_MONOTONIC_COARSE */ -#ifdef CLOCK_MONOTONIC_FAST - /* FreeBSD/DragonFly fast value-getting clock */ - /* Can be affected by frequency adjustment, but preferred since it's fast */ - if (0 == clock_gettime (CLOCK_MONOTONIC_FAST, - &ts)) - { - mono_clock_id = CLOCK_MONOTONIC_FAST; - mono_clock_start = ts.tv_sec; - mono_clock_source = _MHD_CLOCK_GETTIME; - } - else -#endif /* CLOCK_MONOTONIC_COARSE */ -#ifdef CLOCK_MONOTONIC_RAW - /* Linux-specific clock */ - /* Not affected by frequency adjustment, but don't count time in suspend */ - if (0 == clock_gettime (CLOCK_MONOTONIC_RAW, - &ts)) - { - mono_clock_id = CLOCK_MONOTONIC_RAW; - mono_clock_start = ts.tv_sec; - mono_clock_source = _MHD_CLOCK_GETTIME; - } - else -#endif /* CLOCK_MONOTONIC_RAW */ -#ifdef CLOCK_BOOTTIME - /* Linux-specific clock */ - /* Count time in suspend so it's real monotonic on Linux, */ - /* but can be slower value-getting than other clocks */ - if (0 == clock_gettime (CLOCK_BOOTTIME, - &ts)) - { - mono_clock_id = CLOCK_BOOTTIME; - mono_clock_start = ts.tv_sec; - mono_clock_source = _MHD_CLOCK_GETTIME; - } - else -#endif /* CLOCK_BOOTTIME */ -#ifdef CLOCK_MONOTONIC - /* Monotonic clock */ - /* Widely supported, may be affected by frequency adjustment */ - /* On Linux it's not truly monotonic as it doesn't count time in suspend */ - if (0 == clock_gettime (CLOCK_MONOTONIC, - &ts)) - { - mono_clock_id = CLOCK_MONOTONIC; - mono_clock_start = ts.tv_sec; - mono_clock_source = _MHD_CLOCK_GETTIME; - } - else -#endif /* CLOCK_BOOTTIME */ -#endif /* HAVE_CLOCK_GETTIME */ -#ifdef HAVE_CLOCK_GET_TIME - /* Darwin-specific monotonic clock */ - /* Should be monotonic as clock_set_time function always unconditionally */ - /* failed on latest kernels */ - if ( (KERN_SUCCESS == host_get_clock_service (mach_host_self (), - SYSTEM_CLOCK, - &mono_clock_service)) && - (KERN_SUCCESS == clock_get_time (mono_clock_service, - &cur_time)) ) - { - mono_clock_start = cur_time.tv_sec; - mono_clock_source = _MHD_CLOCK_GET_TIME; - } - else -#endif /* HAVE_CLOCK_GET_TIME */ -#ifdef _WIN32 -#if _WIN32_WINNT >= 0x0600 - /* W32 Vista or later specific monotonic clock */ - /* Available since Vista, ~15ms accuracy */ - if (1) - { - tick_start = GetTickCount64 (); - mono_clock_source = _MHD_CLOCK_GETTICKCOUNT64; - } - else -#else /* _WIN32_WINNT < 0x0600 */ - /* W32 specific monotonic clock */ - /* Available on Windows 2000 and later */ - if (1) - { - LARGE_INTEGER freq; - LARGE_INTEGER perf_counter; - - QueryPerformanceFrequency (&freq); /* never fail on XP and later */ - QueryPerformanceCounter (&perf_counter); /* never fail on XP and later */ - perf_freq = freq.QuadPart; - perf_start = perf_counter.QuadPart; - mono_clock_source = _MHD_CLOCK_PERFCOUNTER; - } - else -#endif /* _WIN32_WINNT < 0x0600 */ -#endif /* _WIN32 */ -#ifdef HAVE_CLOCK_GETTIME -#ifdef CLOCK_HIGHRES - /* Solaris-specific monotonic high-resolution clock */ - /* Not preferred due to be potentially resource-hungry */ - if (0 == clock_gettime (CLOCK_HIGHRES, - &ts)) - { - mono_clock_id = CLOCK_HIGHRES; - mono_clock_start = ts.tv_sec; - mono_clock_source = _MHD_CLOCK_GETTIME; - } - else -#endif /* CLOCK_HIGHRES */ -#endif /* HAVE_CLOCK_GETTIME */ -#ifdef HAVE_GETHRTIME - /* HP-UX and Solaris monotonic clock */ - /* Not preferred due to be potentially resource-hungry */ - if (1) - { - hrtime_start = gethrtime (); - mono_clock_source = _MHD_CLOCK_GETHRTIME; - } - else -#endif /* HAVE_GETHRTIME */ - { - /* no suitable clock source was found */ - mono_clock_source = _MHD_CLOCK_NO_SOURCE; - } - -#ifdef HAVE_CLOCK_GET_TIME - if ( (_MHD_CLOCK_GET_TIME != mono_clock_source) && - (_MHD_INVALID_CLOCK_SERV != mono_clock_service) ) - { - /* clock service was initialised but clock_get_time failed */ - mach_port_deallocate (mach_task_self (), - mono_clock_service); - mono_clock_service = _MHD_INVALID_CLOCK_SERV; - } -#else - (void) mono_clock_source; /* avoid compiler warning */ -#endif /* HAVE_CLOCK_GET_TIME */ - - sys_clock_start = time (NULL); -} - - -/** - * Deinitialise monotonic seconds counter by freeing any allocated resources - */ -void -MHD_monotonic_sec_counter_finish (void) -{ -#ifdef HAVE_CLOCK_GET_TIME - if (_MHD_INVALID_CLOCK_SERV != mono_clock_service) - { - mach_port_deallocate (mach_task_self (), - mono_clock_service); - mono_clock_service = _MHD_INVALID_CLOCK_SERV; - } -#endif /* HAVE_CLOCK_GET_TIME */ -} - - -/** - * Monotonic seconds counter, useful for timeout calculation. - * Tries to be not affected by manually setting the system real time - * clock or adjustments by NTP synchronization. - * - * @return number of seconds from some fixed moment - */ -time_t -MHD_monotonic_sec_counter (void) -{ -#ifdef HAVE_CLOCK_GETTIME - struct timespec ts; - - if ( (_MHD_UNWANTED_CLOCK != mono_clock_id) && - (0 == clock_gettime (mono_clock_id, - &ts)) ) - return ts.tv_sec - mono_clock_start; -#endif /* HAVE_CLOCK_GETTIME */ -#ifdef HAVE_CLOCK_GET_TIME - if (_MHD_INVALID_CLOCK_SERV != mono_clock_service) - { - mach_timespec_t cur_time; - - if (KERN_SUCCESS == clock_get_time (mono_clock_service, - &cur_time)) - return cur_time.tv_sec - mono_clock_start; - } -#endif /* HAVE_CLOCK_GET_TIME */ -#if defined(_WIN32) -#if _WIN32_WINNT >= 0x0600 - if (1) - return (time_t) (((uint64_t) (GetTickCount64 () - tick_start)) / 1000); -#else /* _WIN32_WINNT < 0x0600 */ - if (0 != perf_freq) - { - LARGE_INTEGER perf_counter; - - QueryPerformanceCounter (&perf_counter); /* never fail on XP and later */ - return (time_t) (((uint64_t) (perf_counter.QuadPart - perf_start)) - / perf_freq); - } -#endif /* _WIN32_WINNT < 0x0600 */ -#endif /* _WIN32 */ -#ifdef HAVE_GETHRTIME - if (1) - return (time_t) (((uint64_t) (gethrtime () - hrtime_start)) / 1000000000); -#endif /* HAVE_GETHRTIME */ - - return time (NULL) - sys_clock_start; -} diff --git a/src/lib/mhd_mono_clock.h b/src/lib/mhd_mono_clock.h @@ -1,60 +0,0 @@ -/* - This file is part of libmicrohttpd - Copyright (C) 2015 Karlson2k (Evgeny Grin) - - 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 microhttpd/mhd_mono_clock.h - * @brief internal monotonic clock functions declarations - * @author Karlson2k (Evgeny Grin) - */ - -#ifndef MHD_MONO_CLOCK_H -#define MHD_MONO_CLOCK_H 1 -#include "mhd_options.h" - -#if defined(HAVE_TIME_H) -#include <time.h> -#elif defined(HAVE_SYS_TYPES_H) -#include <sys/types.h> -#endif - -/** - * Initialise monotonic seconds counter. - */ -void -MHD_monotonic_sec_counter_init (void); - - -/** - * Deinitialise monotonic seconds counter by freeing any allocated resources - */ -void -MHD_monotonic_sec_counter_finish (void); - - -/** - * Monotonic seconds counter, useful for timeout calculation. - * Tries to be not affected by manually setting the system real time - * clock or adjustments by NTP synchronization. - * - * @return number of seconds from some fixed moment - */ -time_t -MHD_monotonic_sec_counter (void); - -#endif /* MHD_MONO_CLOCK_H */ diff --git a/src/lib/mhd_sockets.c b/src/lib/mhd_sockets.c @@ -1,517 +0,0 @@ -/* - This file is part of libmicrohttpd - Copyright (C) 2014-2016 Karlson2k (Evgeny Grin) - - 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 microhttpd/mhd_sockets.c - * @brief Implementation for sockets functions - * @author Karlson2k (Evgeny Grin) - */ - -#include "mhd_sockets.h" -#ifdef HAVE_UNISTD_H -#include <unistd.h> -#endif /* HAVE_UNISTD_H */ -#include <fcntl.h> - -#ifdef MHD_WINSOCK_SOCKETS - -/** - * Return pointer to string description of specified WinSock error - * @param err the WinSock error code. - * @return pointer to string description of specified WinSock error. - */ -const char * -MHD_W32_strerror_winsock_ (int err) -{ - switch (err) - { - case 0: - return "No error"; - case WSA_INVALID_HANDLE: - return "Specified event object handle is invalid"; - case WSA_NOT_ENOUGH_MEMORY: - return "Insufficient memory available"; - case WSA_INVALID_PARAMETER: - return "One or more parameters are invalid"; - case WSA_OPERATION_ABORTED: - return "Overlapped operation aborted"; - case WSA_IO_INCOMPLETE: - return "Overlapped I/O event object not in signaled state"; - case WSA_IO_PENDING: - return "Overlapped operations will complete later"; - case WSAEINTR: - return "Interrupted function call"; - case WSAEBADF: - return "File handle is not valid"; - case WSAEACCES: - return "Permission denied"; - case WSAEFAULT: - return "Bad address"; - case WSAEINVAL: - return "Invalid argument"; - case WSAEMFILE: - return "Too many open files"; - case WSAEWOULDBLOCK: - return "Resource temporarily unavailable"; - case WSAEINPROGRESS: - return "Operation now in progress"; - case WSAEALREADY: - return "Operation already in progress"; - case WSAENOTSOCK: - return "Socket operation on nonsocket"; - case WSAEDESTADDRREQ: - return "Destination address required"; - case WSAEMSGSIZE: - return "Message too long"; - case WSAEPROTOTYPE: - return "Protocol wrong type for socket"; - case WSAENOPROTOOPT: - return "Bad protocol option"; - case WSAEPROTONOSUPPORT: - return "Protocol not supported"; - case WSAESOCKTNOSUPPORT: - return "Socket type not supported"; - case WSAEOPNOTSUPP: - return "Operation not supported"; - case WSAEPFNOSUPPORT: - return "Protocol family not supported"; - case WSAEAFNOSUPPORT: - return "Address family not supported by protocol family"; - case WSAEADDRINUSE: - return "Address already in use"; - case WSAEADDRNOTAVAIL: - return "Cannot assign requested address"; - case WSAENETDOWN: - return "Network is down"; - case WSAENETUNREACH: - return "Network is unreachable"; - case WSAENETRESET: - return "Network dropped connection on reset"; - case WSAECONNABORTED: - return "Software caused connection abort"; - case WSAECONNRESET: - return "Connection reset by peer"; - case WSAENOBUFS: - return "No buffer space available"; - case WSAEISCONN: - return "Socket is already connected"; - case WSAENOTCONN: - return "Socket is not connected"; - case WSAESHUTDOWN: - return "Cannot send after socket shutdown"; - case WSAETOOMANYREFS: - return "Too many references"; - case WSAETIMEDOUT: - return "Connection timed out"; - case WSAECONNREFUSED: - return "Connection refused"; - case WSAELOOP: - return "Cannot translate name"; - case WSAENAMETOOLONG: - return "Name too long"; - case WSAEHOSTDOWN: - return "Host is down"; - case WSAEHOSTUNREACH: - return "No route to host"; - case WSAENOTEMPTY: - return "Directory not empty"; - case WSAEPROCLIM: - return "Too many processes"; - case WSAEUSERS: - return "User quota exceeded"; - case WSAEDQUOT: - return "Disk quota exceeded"; - case WSAESTALE: - return "Stale file handle reference"; - case WSAEREMOTE: - return "Item is remote"; - case WSASYSNOTREADY: - return "Network subsystem is unavailable"; - case WSAVERNOTSUPPORTED: - return "Winsock.dll version out of range"; - case WSANOTINITIALISED: - return "Successful WSAStartup not yet performed"; - case WSAEDISCON: - return "Graceful shutdown in progress"; - case WSAENOMORE: - return "No more results"; - case WSAECANCELLED: - return "Call has been canceled"; - case WSAEINVALIDPROCTABLE: - return "Procedure call table is invalid"; - case WSAEINVALIDPROVIDER: - return "Service provider is invalid"; - case WSAEPROVIDERFAILEDINIT: - return "Service provider failed to initialize"; - case WSASYSCALLFAILURE: - return "System call failure"; - case WSASERVICE_NOT_FOUND: - return "Service not found"; - case WSATYPE_NOT_FOUND: - return "Class type not found"; - case WSA_E_NO_MORE: - return "No more results"; - case WSA_E_CANCELLED: - return "Call was canceled"; - case WSAEREFUSED: - return "Database query was refused"; - case WSAHOST_NOT_FOUND: - return "Host not found"; - case WSATRY_AGAIN: - return "Nonauthoritative host not found"; - case WSANO_RECOVERY: - return "This is a nonrecoverable error"; - case WSANO_DATA: - return "Valid name, no data record of requested type"; - case WSA_QOS_RECEIVERS: - return "QoS receivers"; - case WSA_QOS_SENDERS: - return "QoS senders"; - case WSA_QOS_NO_SENDERS: - return "No QoS senders"; - case WSA_QOS_NO_RECEIVERS: - return "QoS no receivers"; - case WSA_QOS_REQUEST_CONFIRMED: - return "QoS request confirmed"; - case WSA_QOS_ADMISSION_FAILURE: - return "QoS admission error"; - case WSA_QOS_POLICY_FAILURE: - return "QoS policy failure"; - case WSA_QOS_BAD_STYLE: - return "QoS bad style"; - case WSA_QOS_BAD_OBJECT: - return "QoS bad object"; - case WSA_QOS_TRAFFIC_CTRL_ERROR: - return "QoS traffic control error"; - case WSA_QOS_GENERIC_ERROR: - return "QoS generic error"; - case WSA_QOS_ESERVICETYPE: - return "QoS service type error"; - case WSA_QOS_EFLOWSPEC: - return "QoS flowspec error"; - case WSA_QOS_EPROVSPECBUF: - return "Invalid QoS provider buffer"; - case WSA_QOS_EFILTERSTYLE: - return "Invalid QoS filter style"; - case WSA_QOS_EFILTERTYPE: - return "Invalid QoS filter type"; - case WSA_QOS_EFILTERCOUNT: - return "Incorrect QoS filter count"; - case WSA_QOS_EOBJLENGTH: - return "Invalid QoS object length"; - case WSA_QOS_EFLOWCOUNT: - return "Incorrect QoS flow count"; - case WSA_QOS_EUNKOWNPSOBJ: - return "Unrecognized QoS object"; - case WSA_QOS_EPOLICYOBJ: - return "Invalid QoS policy object"; - case WSA_QOS_EFLOWDESC: - return "Invalid QoS flow descriptor"; - case WSA_QOS_EPSFLOWSPEC: - return "Invalid QoS provider-specific flowspec"; - case WSA_QOS_EPSFILTERSPEC: - return "Invalid QoS provider-specific filterspec"; - case WSA_QOS_ESDMODEOBJ: - return "Invalid QoS shape discard mode object"; - case WSA_QOS_ESHAPERATEOBJ: - return "Invalid QoS shaping rate object"; - case WSA_QOS_RESERVED_PETYPE: - return "Reserved policy QoS element type"; - } - return "Unknown winsock error"; -} - - -/** - * Create pair of mutually connected TCP/IP sockets on loopback address - * @param sockets_pair array to receive resulted sockets - * @param non_blk if set to non-zero value, sockets created in non-blocking mode - * otherwise sockets will be in blocking mode - * @return non-zero if succeeded, zero otherwise - */ -int -MHD_W32_socket_pair_ (SOCKET sockets_pair[2], int non_blk) -{ - int i; - - if (! sockets_pair) - { - WSASetLastError (WSAEFAULT); - return 0; - } - -#define PAIRMAXTRYIES 800 - for (i = 0; i < PAIRMAXTRYIES; i++) - { - struct sockaddr_in listen_addr; - SOCKET listen_s; - static const int c_addinlen = sizeof(struct sockaddr_in); /* help compiler to optimize */ - int addr_len = c_addinlen; - unsigned long on_val = 1; - unsigned long off_val = 0; - - listen_s = socket (AF_INET, - SOCK_STREAM, - IPPROTO_TCP); - if (INVALID_SOCKET == listen_s) - break; /* can't create even single socket */ - - listen_addr.sin_family = AF_INET; - listen_addr.sin_port = 0; /* same as htons(0) */ - listen_addr.sin_addr.s_addr = htonl (INADDR_LOOPBACK); - if ( ((0 == bind (listen_s, - (struct sockaddr *) &listen_addr, - c_addinlen)) && - (0 == listen (listen_s, - 1) ) && - (0 == getsockname (listen_s, - (struct sockaddr *) &listen_addr, - &addr_len))) ) - { - SOCKET client_s = socket (AF_INET, - SOCK_STREAM, - IPPROTO_TCP); - struct sockaddr_in accepted_from_addr; - struct sockaddr_in client_addr; - SOCKET server_s; - - if (INVALID_SOCKET == client_s) - { - /* try again */ - closesocket (listen_s); - continue; - } - - if ( (0 != ioctlsocket (client_s, - FIONBIO, - &on_val)) || - ( (0 != connect (client_s, - (struct sockaddr *) &listen_addr, - c_addinlen)) && - (WSAGetLastError () != WSAEWOULDBLOCK)) ) - { - /* try again */ - closesocket (listen_s); - closesocket (client_s); - continue; - } - - addr_len = c_addinlen; - server_s = accept (listen_s, - (struct sockaddr *) &accepted_from_addr, - &addr_len); - if (INVALID_SOCKET == server_s) - { - /* try again */ - closesocket (listen_s); - closesocket (client_s); - continue; - } - - addr_len = c_addinlen; - if ( (0 == getsockname (client_s, - (struct sockaddr *) &client_addr, - &addr_len)) && - (accepted_from_addr.sin_family == client_addr.sin_family) && - (accepted_from_addr.sin_port == client_addr.sin_port) && - (accepted_from_addr.sin_addr.s_addr == - client_addr.sin_addr.s_addr) && - ( (0 != non_blk) ? - (0 == ioctlsocket (server_s, - FIONBIO, - &on_val)) : - (0 == ioctlsocket (client_s, - FIONBIO, - &off_val)) ) ) - { - closesocket (listen_s); - sockets_pair[0] = server_s; - sockets_pair[1] = client_s; - return ! 0; - } - closesocket (server_s); - closesocket (client_s); - } - closesocket (listen_s); - } - - sockets_pair[0] = INVALID_SOCKET; - sockets_pair[1] = INVALID_SOCKET; - WSASetLastError (WSAECONNREFUSED); - - return 0; -} - - -#endif /* MHD_WINSOCK_SOCKETS */ - - -/** - * Add @a fd to the @a set. If @a fd is - * greater than @a max_fd, set @a max_fd to @a fd. - * - * @param fd file descriptor to add to the @a set - * @param set set to modify - * @param max_fd maximum value to potentially update - * @param fd_setsize value of FD_SETSIZE - * @return non-zero if succeeded, zero otherwise - */ -int -MHD_add_to_fd_set_ (MHD_socket fd, - fd_set *set, - MHD_socket *max_fd, - unsigned int fd_setsize) -{ - if ( (NULL == set) || - (MHD_INVALID_SOCKET == fd) ) - return 0; - if (! MHD_SCKT_FD_FITS_FDSET_SETSIZE_ (fd, - set, - fd_setsize)) - return 0; - MHD_SCKT_ADD_FD_TO_FDSET_SETSIZE_ (fd, - set, - fd_setsize); - if ( (NULL != max_fd) && - ( (fd > *max_fd) || - (MHD_INVALID_SOCKET == *max_fd) ) ) - *max_fd = fd; - return ! 0; -} - - -/** - * Change socket options to be non-blocking. - * - * @param sock socket to manipulate - * @return non-zero if succeeded, zero otherwise - */ -int -MHD_socket_nonblocking_ (MHD_socket sock) -{ -#if defined(MHD_POSIX_SOCKETS) - int flags; - - flags = fcntl (sock, - F_GETFL); - if (-1 == flags) - return 0; - - if ( ((flags | O_NONBLOCK) != flags) && - (0 != fcntl (sock, - F_SETFL, - flags | O_NONBLOCK)) ) - return 0; -#elif defined(MHD_WINSOCK_SOCKETS) - unsigned long flags = 1; - - if (0 != ioctlsocket (sock, - FIONBIO, - &flags)) - return 0; -#endif /* MHD_WINSOCK_SOCKETS */ - return ! 0; -} - - -/** - * Change socket options to be non-inheritable. - * - * @param sock socket to manipulate - * @return non-zero if succeeded, zero otherwise - * @warning Does not set socket error on W32. - */ -int -MHD_socket_noninheritable_ (MHD_socket sock) -{ -#if defined(MHD_POSIX_SOCKETS) - int flags; - - flags = fcntl (sock, - F_GETFD); - if (-1 == flags) - return 0; - - if ( ((flags | FD_CLOEXEC) != flags) && - (0 != fcntl (sock, - F_SETFD, - flags | FD_CLOEXEC)) ) - return 0; -#elif defined(MHD_WINSOCK_SOCKETS) - if (! SetHandleInformation ((HANDLE) sock, - HANDLE_FLAG_INHERIT, - 0)) - return 0; -#endif /* MHD_WINSOCK_SOCKETS */ - return ! 0; -} - - -/** - * Create a listen socket, with noninheritable flag if possible. - * - * @param pf protocol family to use - * @return created socket or MHD_INVALID_SOCKET in case of errors - */ -MHD_socket -MHD_socket_create_listen_ (int pf) -{ - MHD_socket fd; - int cloexec_set; - -#if defined(MHD_POSIX_SOCKETS) && defined(SOCK_CLOEXEC) - fd = socket (pf, - SOCK_STREAM | SOCK_CLOEXEC, - 0); - cloexec_set = ! 0; -#elif defined(MHD_WINSOCK_SOCKETS) && defined (WSA_FLAG_NO_HANDLE_INHERIT) - fd = WSASocketW (pf, - SOCK_STREAM, - 0, - NULL, - 0, - WSA_FLAG_NO_HANDLE_INHERIT); - cloexec_set = ! 0; -#else /* !SOCK_CLOEXEC */ - fd = MHD_INVALID_SOCKET; -#endif /* !SOCK_CLOEXEC */ - if (MHD_INVALID_SOCKET == fd) - { - fd = socket (pf, - SOCK_STREAM, - 0); - cloexec_set = 0; - } - if (MHD_INVALID_SOCKET == fd) - return MHD_INVALID_SOCKET; -#ifdef MHD_socket_nosignal_ - if (! MHD_socket_nosignal_ (fd)) - { - const int err = MHD_socket_get_error_ (); - (void) MHD_socket_close_ (fd); - MHD_socket_fset_error_ (err); - return MHD_INVALID_SOCKET; - } -#endif /* MHD_socket_nosignal_ */ - if (! cloexec_set) - (void) MHD_socket_noninheritable_ (fd); - - return fd; -} diff --git a/src/lib/mhd_sockets.h b/src/lib/mhd_sockets.h @@ -1,804 +0,0 @@ -/* - This file is part of libmicrohttpd - Copyright (C) 2014-2016 Karlson2k (Evgeny Grin) - - 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 microhttpd/mhd_sockets.c - * @brief Header for platform-independent sockets abstraction - * @author Karlson2k (Evgeny Grin) - * - * Provides basic abstraction for sockets. - * Any functions can be implemented as macro on some platforms - * unless explicitly marked otherwise. - * Any function argument can be skipped in macro, so avoid - * variable modification in function parameters. - */ - -#ifndef MHD_SOCKETS_H -#define MHD_SOCKETS_H 1 -#include "mhd_options.h" - -#include <errno.h> - -#if ! defined(MHD_POSIX_SOCKETS) && ! defined(MHD_WINSOCK_SOCKETS) -# if ! defined(_WIN32) || defined(__CYGWIN__) -# define MHD_POSIX_SOCKETS 1 -# else /* defined(_WIN32) && !defined(__CYGWIN__) */ -# define MHD_WINSOCK_SOCKETS 1 -# endif /* defined(_WIN32) && !defined(__CYGWIN__) */ -#endif /* !MHD_POSIX_SOCKETS && !MHD_WINSOCK_SOCKETS */ - -/* - * MHD require headers that define socket type, socket basic functions - * (socket(), accept(), listen(), bind(), send(), recv(), select()), socket - * parameters like SOCK_CLOEXEC, SOCK_NONBLOCK, additional socket functions - * (poll(), epoll(), accept4()), struct timeval and other types, required - * for socket function. - */ -#if defined(MHD_POSIX_SOCKETS) -# ifdef HAVE_SYS_TYPES_H -# include <sys/types.h> /* required on old platforms */ -# endif -# ifdef HAVE_SYS_SOCKET_H -# include <sys/socket.h> -# endif -# if defined(__VXWORKS__) || defined(__vxworks) || defined(OS_VXWORKS) -# ifdef HAVE_SOCKLIB_H -# include <sockLib.h> -# endif /* HAVE_SOCKLIB_H */ -# ifdef HAVE_INETLIB_H -# include <inetLib.h> -# endif /* HAVE_INETLIB_H */ -# include <strings.h> /* required for FD_SET (bzero() function) */ -# endif /* __VXWORKS__ || __vxworks || OS_VXWORKS */ -# ifdef HAVE_NETINET_IN_H -# include <netinet/in.h> -# endif /* HAVE_NETINET_IN_H */ -# ifdef HAVE_ARPA_INET_H -# include <arpa/inet.h> -# endif -# ifdef HAVE_NET_IF_H -# include <net/if.h> -# endif -# ifdef HAVE_SYS_TIME_H -# include <sys/time.h> -# endif -# ifdef HAVE_TIME_H -# include <time.h> -# endif -# ifdef HAVE_NETDB_H -# include <netdb.h> -# endif -# ifdef HAVE_SYS_SELECT_H -# include <sys/select.h> -# endif -# ifdef EPOLL_SUPPORT -# include <sys/epoll.h> -# endif -# ifdef HAVE_NETINET_TCP_H -/* for TCP_FASTOPEN and TCP_CORK */ -# include <netinet/tcp.h> -# endif -# ifdef HAVE_STRING_H -# include <string.h> /* for strerror() */ -# endif -#elif defined(MHD_WINSOCK_SOCKETS) -# ifndef WIN32_LEAN_AND_MEAN -# define WIN32_LEAN_AND_MEAN 1 -# endif /* !WIN32_LEAN_AND_MEAN */ -# include <winsock2.h> -# include <ws2tcpip.h> -#endif /* MHD_WINSOCK_SOCKETS */ - -#if defined(HAVE_POLL_H) && defined(HAVE_POLL) -# include <poll.h> -#endif - -#include <stddef.h> -#if defined(_MSC_FULL_VER) && ! defined (_SSIZE_T_DEFINED) -# include <stdint.h> -# define _SSIZE_T_DEFINED -typedef intptr_t ssize_t; -#endif /* !_SSIZE_T_DEFINED */ - -#include "mhd_limits.h" - -#ifdef _MHD_FD_SETSIZE_IS_DEFAULT -# define _MHD_SYS_DEFAULT_FD_SETSIZE FD_SETSIZE -#else /* ! _MHD_FD_SETSIZE_IS_DEFAULT */ -# include "sysfdsetsize.h" -# define _MHD_SYS_DEFAULT_FD_SETSIZE get_system_fdsetsize_value () -#endif /* ! _MHD_FD_SETSIZE_IS_DEFAULT */ - -#ifndef MHD_PANIC -# include <stdio.h> -# include <stdlib.h> -/* Simple implementation of MHD_PANIC, to be used outside lib */ -# define MHD_PANIC(msg) do { fprintf (stderr, \ - "Abnormal termination at %d line in file %s: %s\n", \ - (int) __LINE__, __FILE__, msg); abort (); \ -} while (0) -#endif /* ! MHD_PANIC */ - -#ifndef MHD_SOCKET_DEFINED -/** - * MHD_socket is type for socket FDs - */ -# if defined(MHD_POSIX_SOCKETS) -typedef int MHD_socket; -# define MHD_INVALID_SOCKET (-1) -# elif defined(MHD_WINSOCK_SOCKETS) -typedef SOCKET MHD_socket; -# define MHD_INVALID_SOCKET (INVALID_SOCKET) -# endif /* MHD_WINSOCK_SOCKETS */ - -# define MHD_SOCKET_DEFINED 1 -#endif /* ! MHD_SOCKET_DEFINED */ - -#ifdef SOCK_CLOEXEC -# define MAYBE_SOCK_CLOEXEC SOCK_CLOEXEC -#else /* ! SOCK_CLOEXEC */ -# define MAYBE_SOCK_CLOEXEC 0 -#endif /* ! SOCK_CLOEXEC */ - -#ifdef HAVE_SOCK_NONBLOCK -# define MAYBE_SOCK_NONBLOCK SOCK_NONBLOCK -#else /* ! HAVE_SOCK_NONBLOCK */ -# define MAYBE_SOCK_NONBLOCK 0 -#endif /* ! HAVE_SOCK_NONBLOCK */ - -#ifdef MSG_NOSIGNAL -# define MAYBE_MSG_NOSIGNAL MSG_NOSIGNAL -#else /* ! MSG_NOSIGNAL */ -# define MAYBE_MSG_NOSIGNAL 0 -#endif /* ! MSG_NOSIGNAL */ - -#if ! defined(SHUT_WR) && defined(SD_SEND) -# define SHUT_WR SD_SEND -#endif -#if ! defined(SHUT_RD) && defined(SD_RECEIVE) -# define SHUT_RD SD_RECEIVE -#endif -#if ! defined(SHUT_RDWR) && defined(SD_BOTH) -# define SHUT_RDWR SD_BOTH -#endif - -#if HAVE_ACCEPT4 + 0 != 0 && (defined(HAVE_SOCK_NONBLOCK) || \ - defined(SOCK_CLOEXEC)) -# define USE_ACCEPT4 1 -#endif - -#if defined(HAVE_EPOLL_CREATE1) && defined(EPOLL_CLOEXEC) -# define USE_EPOLL_CREATE1 1 -#endif /* HAVE_EPOLL_CREATE1 && EPOLL_CLOEXEC */ - -#ifdef TCP_FASTOPEN -/** - * Default TCP fastopen queue size. - */ -#define MHD_TCP_FASTOPEN_QUEUE_SIZE_DEFAULT 10 -#endif - - -/** - * MHD_SCKT_OPT_BOOL_ is type for bool parameters for setsockopt()/getsockopt() - */ -#ifdef MHD_POSIX_SOCKETS -typedef int MHD_SCKT_OPT_BOOL_; -#else /* MHD_WINSOCK_SOCKETS */ -typedef BOOL MHD_SCKT_OPT_BOOL_; -#endif /* MHD_WINSOCK_SOCKETS */ - -/** - * MHD_SCKT_SEND_SIZE_ is type used to specify size for send and recv - * functions - */ -#if ! defined(MHD_WINSOCK_SOCKETS) -typedef size_t MHD_SCKT_SEND_SIZE_; -#else -typedef int MHD_SCKT_SEND_SIZE_; -#endif - -/** - * MHD_SCKT_SEND_MAX_SIZE_ is maximum send()/recv() size value. - */ -#if ! defined(MHD_WINSOCK_SOCKETS) -# define MHD_SCKT_SEND_MAX_SIZE_ SSIZE_MAX -#else -# define MHD_SCKT_SEND_MAX_SIZE_ INT_MAX -#endif - -/** - * MHD_socket_close_(fd) close any FDs (non-W32) / close only socket - * FDs (W32). Note that on HP-UNIX, this function may leak the FD if - * errno is set to EINTR. Do not use HP-UNIX. - * - * @param fd descriptor to close - * @return boolean true on success (error codes like EINTR and EIO are - * counted as success, only EBADF counts as an error!), - * boolean false otherwise. - */ -#if ! defined(MHD_WINSOCK_SOCKETS) -# define MHD_socket_close_(fd) ((0 == close ((fd))) || (EBADF != errno)) -#else -# define MHD_socket_close_(fd) (0 == closesocket ((fd))) -#endif - -/** - * MHD_socket_close_chk_(fd) close socket and abort execution - * if error is detected. - * @param fd socket to close - */ -#define MHD_socket_close_chk_(fd) do { \ - if (! MHD_socket_close_ (fd)) \ - MHD_PANIC (_ ("Close socket failed.\n")); \ -} while (0) - - -/** - * MHD_send_ is wrapper for system's send() - * @param s the socket to use - * @param b the buffer with data to send - * @param l the length of data in @a b - * @return ssize_t type value - */ -#define MHD_send_(s,b,l) \ - ((ssize_t) send ((s),(const void*) (b),((MHD_SCKT_SEND_SIZE_) l), \ - MAYBE_MSG_NOSIGNAL)) - - -/** - * MHD_recv_ is wrapper for system's recv() - * @param s the socket to use - * @param b the buffer for data to receive - * @param l the length of @a b - * @return ssize_t type value - */ -#define MHD_recv_(s,b,l) \ - ((ssize_t) recv ((s),(void*) (b),((MHD_SCKT_SEND_SIZE_) l), 0)) - - -/** - * Check whether FD can be added to fd_set with specified FD_SETSIZE. - * @param fd the fd to check - * @param pset the pointer to fd_set to check or NULL to check - * whether FD can be used with fd_sets. - * @param setsize the value of FD_SETSIZE. - * @return boolean true if FD can be added to fd_set, - * boolean false otherwise. - */ -#if defined(MHD_POSIX_SOCKETS) -# define MHD_SCKT_FD_FITS_FDSET_SETSIZE_(fd,pset,setsize) ((fd) < \ - ((MHD_socket) \ - setsize)) -#elif defined(MHD_WINSOCK_SOCKETS) -# define MHD_SCKT_FD_FITS_FDSET_SETSIZE_(fd,pset,setsize) ( ((void*) (pset)== \ - (void*) 0) || \ - (((fd_set*) (pset)) \ - ->fd_count < \ - ((unsigned) \ - setsize)) || \ - (FD_ISSET ((fd), \ - (pset))) ) -#endif - -/** - * Check whether FD can be added to fd_set with current FD_SETSIZE. - * @param fd the fd to check - * @param pset the pointer to fd_set to check or NULL to check - * whether FD can be used with fd_sets. - * @return boolean true if FD can be added to fd_set, - * boolean false otherwise. - */ -#define MHD_SCKT_FD_FITS_FDSET_(fd,pset) MHD_SCKT_FD_FITS_FDSET_SETSIZE_ ((fd), \ - (pset), \ - FD_SETSIZE) - -/** - * Add FD to fd_set with specified FD_SETSIZE. - * @param fd the fd to add - * @param pset the valid pointer to fd_set. - * @param setsize the value of FD_SETSIZE. - * @note To work on W32 with value of FD_SETSIZE different from currently defined value, - * system definition of FD_SET() is not used. - */ -#if defined(MHD_POSIX_SOCKETS) -# define MHD_SCKT_ADD_FD_TO_FDSET_SETSIZE_(fd,pset,setsize) FD_SET ((fd), \ - (pset)) -#elif defined(MHD_WINSOCK_SOCKETS) -# define MHD_SCKT_ADD_FD_TO_FDSET_SETSIZE_(fd,pset,setsize) \ - do { \ - u_int _i_ = 0; \ - fd_set*const _s_ = (fd_set*) (pset); \ - while ((_i_ < _s_->fd_count) && ((fd) != _s_->fd_array [_i_])) {++_i_;} \ - if ((_i_ == _s_->fd_count)) {_s_->fd_array [_s_->fd_count ++] = (fd);} \ - } while (0) -#endif - -/* MHD_SYS_select_ is wrapper macro for system select() function */ -#if ! defined(MHD_WINSOCK_SOCKETS) -# define MHD_SYS_select_(n,r,w,e,t) select ((n),(r),(w),(e),(t)) -#else -# define MHD_SYS_select_(n,r,w,e,t) \ - ( ( (((void*) (r) == (void*) 0) || ((fd_set*) (r))->fd_count == 0) && \ - (((void*) (w) == (void*) 0) || ((fd_set*) (w))->fd_count == 0) && \ - (((void*) (e) == (void*) 0) || ((fd_set*) (e))->fd_count == 0) ) ? \ - ( ((void*) (t) == (void*) 0) ? 0 : \ - (Sleep (((struct timeval*) (t))->tv_sec * 1000 \ - + ((struct timeval*) (t))->tv_usec / 1000), 0) ) : \ - (select ((int) 0,(r),(w),(e),(t))) ) -#endif - -#if defined(HAVE_POLL) -/* MHD_sys_poll_ is wrapper macro for system poll() function */ -# if ! defined(MHD_WINSOCK_SOCKETS) -# define MHD_sys_poll_ poll -# else /* MHD_WINSOCK_SOCKETS */ -# define MHD_sys_poll_ WSAPoll -# endif /* MHD_WINSOCK_SOCKETS */ - -# ifdef POLLPRI -# define MHD_POLLPRI_OR_ZERO POLLPRI -# else /* ! POLLPRI */ -# define MHD_POLLPRI_OR_ZERO 0 -# endif /* ! POLLPRI */ -# ifdef POLLRDBAND -# define MHD_POLLRDBAND_OR_ZERO POLLRDBAND -# else /* ! POLLRDBAND */ -# define MHD_POLLRDBAND_OR_ZERO 0 -# endif /* ! POLLRDBAND */ -# ifdef POLLNVAL -# define MHD_POLLNVAL_OR_ZERO POLLNVAL -# else /* ! POLLNVAL */ -# define MHD_POLLNVAL_OR_ZERO 0 -# endif /* ! POLLNVAL */ - -/* MHD_POLL_EVENTS_ERR_DISC is 'events' mask for errors and disconnect. - * Note: Out-of-band data is treated as error. */ -# if defined(_WIN32) && ! defined(__CYGWIN__) -# define MHD_POLL_EVENTS_ERR_DISC POLLRDBAND -# elif defined(__linux__) -# define MHD_POLL_EVENTS_ERR_DISC POLLPRI -# else /* ! __linux__ */ -# define MHD_POLL_EVENTS_ERR_DISC (MHD_POLLPRI_OR_ZERO \ - | MHD_POLLRDBAND_OR_ZERO) -# endif /* ! __linux__ */ -/* MHD_POLL_REVENTS_ERR_DISC is 'revents' mask for errors and disconnect. - * Note: Out-of-band data is treated as error. */ -# define MHD_POLL_REVENTS_ERR_DISC \ - (MHD_POLLPRI_OR_ZERO | MHD_POLLRDBAND_OR_ZERO | MHD_POLLNVAL_OR_ZERO \ - | POLLERR | POLLHUP) -/* MHD_POLL_REVENTS_ERRROR is 'revents' mask for errors. - * Note: Out-of-band data is treated as error. */ -# define MHD_POLL_REVENTS_ERRROR \ - (MHD_POLLPRI_OR_ZERO | MHD_POLLRDBAND_OR_ZERO | MHD_POLLNVAL_OR_ZERO \ - | POLLERR) -#endif /* HAVE_POLL */ - -#define MHD_SCKT_MISSING_ERR_CODE_ 31450 - -#if defined(MHD_POSIX_SOCKETS) -# if defined(EAGAIN) -# define MHD_SCKT_EAGAIN_ EAGAIN -# elif defined(EWOULDBLOCK) -# define MHD_SCKT_EAGAIN_ EWOULDBLOCK -# else /* !EAGAIN && !EWOULDBLOCK */ -# define MHD_SCKT_EAGAIN_ MHD_SCKT_MISSING_ERR_CODE_ -# endif /* !EAGAIN && !EWOULDBLOCK */ -# if defined(EWOULDBLOCK) -# define MHD_SCKT_EWOULDBLOCK_ EWOULDBLOCK -# elif defined(EAGAIN) -# define MHD_SCKT_EWOULDBLOCK_ EAGAIN -# else /* !EWOULDBLOCK && !EAGAIN */ -# define MHD_SCKT_EWOULDBLOCK_ MHD_SCKT_MISSING_ERR_CODE_ -# endif /* !EWOULDBLOCK && !EAGAIN */ -# ifdef EINTR -# define MHD_SCKT_EINTR_ EINTR -# else /* ! EINTR */ -# define MHD_SCKT_EINTR_ MHD_SCKT_MISSING_ERR_CODE_ -# endif /* ! EINTR */ -# ifdef ECONNRESET -# define MHD_SCKT_ECONNRESET_ ECONNRESET -# else /* ! ECONNRESET */ -# define MHD_SCKT_ECONNRESET_ MHD_SCKT_MISSING_ERR_CODE_ -# endif /* ! ECONNRESET */ -# ifdef ECONNABORTED -# define MHD_SCKT_ECONNABORTED_ ECONNABORTED -# else /* ! ECONNABORTED */ -# define MHD_SCKT_ECONNABORTED_ MHD_SCKT_MISSING_ERR_CODE_ -# endif /* ! ECONNABORTED */ -# ifdef ENOTCONN -# define MHD_SCKT_ENOTCONN_ ENOTCONN -# else /* ! ENOTCONN */ -# define MHD_SCKT_ENOTCONN_ MHD_SCKT_MISSING_ERR_CODE_ -# endif /* ! ENOTCONN */ -# ifdef EMFILE -# define MHD_SCKT_EMFILE_ EMFILE -# else /* ! EMFILE */ -# define MHD_SCKT_EMFILE_ MHD_SCKT_MISSING_ERR_CODE_ -# endif /* ! EMFILE */ -# ifdef ENFILE -# define MHD_SCKT_ENFILE_ ENFILE -# else /* ! ENFILE */ -# define MHD_SCKT_ENFILE_ MHD_SCKT_MISSING_ERR_CODE_ -# endif /* ! ENFILE */ -# ifdef ENOMEM -# define MHD_SCKT_ENOMEM_ ENOMEM -# else /* ! ENOMEM */ -# define MHD_SCKT_ENOMEM_ MHD_SCKT_MISSING_ERR_CODE_ -# endif /* ! ENOMEM */ -# ifdef ENOBUFS -# define MHD_SCKT_ENOBUFS_ ENOBUFS -# else /* ! ENOBUFS */ -# define MHD_SCKT_ENOBUFS_ MHD_SCKT_MISSING_ERR_CODE_ -# endif /* ! ENOBUFS */ -# ifdef EBADF -# define MHD_SCKT_EBADF_ EBADF -# else /* ! EBADF */ -# define MHD_SCKT_EBADF_ MHD_SCKT_MISSING_ERR_CODE_ -# endif /* ! EBADF */ -# ifdef ENOTSOCK -# define MHD_SCKT_ENOTSOCK_ ENOTSOCK -# else /* ! ENOTSOCK */ -# define MHD_SCKT_ENOTSOCK_ MHD_SCKT_MISSING_ERR_CODE_ -# endif /* ! ENOTSOCK */ -# ifdef EINVAL -# define MHD_SCKT_EINVAL_ EINVAL -# else /* ! EINVAL */ -# define MHD_SCKT_EINVAL_ MHD_SCKT_MISSING_ERR_CODE_ -# endif /* ! EINVAL */ -# ifdef EFAULT -# define MHD_SCKT_EFAUL_ EFAULT -# else /* ! EFAULT */ -# define MHD_SCKT_EFAUL_ MHD_SCKT_MISSING_ERR_CODE_ -# endif /* ! EFAULT */ -# ifdef ENOSYS -# define MHD_SCKT_ENOSYS_ ENOSYS -# else /* ! ENOSYS */ -# define MHD_SCKT_ENOSYS_ MHD_SCKT_MISSING_ERR_CODE_ -# endif /* ! ENOSYS */ -# ifdef ENOTSUP -# define MHD_SCKT_ENOTSUP_ ENOTSUP -# else /* ! ENOTSUP */ -# define MHD_SCKT_ENOTSUP_ MHD_SCKT_MISSING_ERR_CODE_ -# endif /* ! ENOTSUP */ -# ifdef EOPNOTSUPP -# define MHD_SCKT_EOPNOTSUPP_ EOPNOTSUPP -# else /* ! EOPNOTSUPP */ -# define MHD_SCKT_EOPNOTSUPP_ MHD_SCKT_MISSING_ERR_CODE_ -# endif /* ! EOPNOTSUPP */ -# ifdef EACCES -# define MHD_SCKT_EACCESS_ EACCES -# else /* ! EACCES */ -# define MHD_SCKT_EACCESS_ MHD_SCKT_MISSING_ERR_CODE_ -# endif /* ! EACCES */ -# ifdef ENETDOWN -# define MHD_SCKT_ENETDOWN_ ENETDOWN -# else /* ! ENETDOWN */ -# define MHD_SCKT_ENETDOWN_ MHD_SCKT_MISSING_ERR_CODE_ -# endif /* ! ENETDOWN */ -#elif defined(MHD_WINSOCK_SOCKETS) -# define MHD_SCKT_EAGAIN_ WSAEWOULDBLOCK -# define MHD_SCKT_EWOULDBLOCK_ WSAEWOULDBLOCK -# define MHD_SCKT_EINTR_ WSAEINTR -# define MHD_SCKT_ECONNRESET_ WSAECONNRESET -# define MHD_SCKT_ECONNABORTED_ WSAECONNABORTED -# define MHD_SCKT_ENOTCONN_ WSAENOTCONN -# define MHD_SCKT_EMFILE_ WSAEMFILE -# define MHD_SCKT_ENFILE_ MHD_SCKT_MISSING_ERR_CODE_ -# define MHD_SCKT_ENOMEM_ MHD_SCKT_MISSING_ERR_CODE_ -# define MHD_SCKT_ENOBUFS_ WSAENOBUFS -# define MHD_SCKT_EBADF_ WSAEBADF -# define MHD_SCKT_ENOTSOCK_ WSAENOTSOCK -# define MHD_SCKT_EINVAL_ WSAEINVAL -# define MHD_SCKT_EFAUL_ WSAEFAULT -# define MHD_SCKT_ENOSYS_ MHD_SCKT_MISSING_ERR_CODE_ -# define MHD_SCKT_ENOTSUP_ MHD_SCKT_MISSING_ERR_CODE_ -# define MHD_SCKT_EOPNOTSUPP_ WSAEOPNOTSUPP -# define MHD_SCKT_EACCESS_ WSAEACCES -# define MHD_SCKT_ENETDOWN_ WSAENETDOWN -#endif - -/** - * MHD_socket_error_ return system native error code for last socket error. - * @return system error code for last socket error. - */ -#if defined(MHD_POSIX_SOCKETS) -# define MHD_socket_get_error_() (errno) -#elif defined(MHD_WINSOCK_SOCKETS) -# define MHD_socket_get_error_() WSAGetLastError () -#endif - -#ifdef MHD_WINSOCK_SOCKETS -/* POSIX-W32 sockets compatibility functions */ - -/** - * Return pointer to string description of specified WinSock error - * @param err the WinSock error code. - * @return pointer to string description of specified WinSock error. - */ -const char *MHD_W32_strerror_winsock_ (int err); - -#endif /* MHD_WINSOCK_SOCKETS */ - -/* MHD_socket_last_strerr_ is description string of specified socket error code */ -#if defined(MHD_POSIX_SOCKETS) -# define MHD_socket_strerr_(err) strerror ((err)) -#elif defined(MHD_WINSOCK_SOCKETS) -# define MHD_socket_strerr_(err) MHD_W32_strerror_winsock_ ((err)) -#endif - -/* MHD_socket_last_strerr_ is description string of last errno (non-W32) / - * description string of last socket error (W32) */ -#define MHD_socket_last_strerr_() MHD_socket_strerr_ (MHD_socket_get_error_ ()) - -/** - * MHD_socket_fset_error_() set socket system native error code. - */ -#if defined(MHD_POSIX_SOCKETS) -# define MHD_socket_fset_error_(err) (errno = (err)) -#elif defined(MHD_WINSOCK_SOCKETS) -# define MHD_socket_fset_error_(err) (WSASetLastError ((err))) -#endif - -/** - * MHD_socket_try_set_error_() set socket system native error code if - * specified code is defined on system. - * @return non-zero if specified @a err code is defined on system - * and error was set; - * zero if specified @a err code is not defined on system - * and error was not set. - */ -#define MHD_socket_try_set_error_(err) ( (MHD_SCKT_MISSING_ERR_CODE_ != (err)) ? \ - (MHD_socket_fset_error_ ((err)), ! 0) : \ - 0) - -/** - * MHD_socket_set_error_() set socket system native error code to - * specified code or replacement code if specified code is not - * defined on system. - */ -#if defined(MHD_POSIX_SOCKETS) -# if defined(ENOSYS) -# define MHD_socket_set_error_(err) ( (MHD_SCKT_MISSING_ERR_CODE_ == (err)) ? \ - (errno = ENOSYS) : (errno = (err)) ) -# elif defined(EOPNOTSUPP) -# define MHD_socket_set_error_(err) ( (MHD_SCKT_MISSING_ERR_CODE_ == (err)) ? \ - (errno = EOPNOTSUPP) : (errno = \ - (err)) ) -# elif defined (EFAULT) -# define MHD_socket_set_error_(err) ( (MHD_SCKT_MISSING_ERR_CODE_ == (err)) ? \ - (errno = EFAULT) : (errno = (err)) ) -# elif defined (EINVAL) -# define MHD_socket_set_error_(err) ( (MHD_SCKT_MISSING_ERR_CODE_ == (err)) ? \ - (errno = EINVAL) : (errno = (err)) ) -# else /* !EOPNOTSUPP && !EFAULT && !EINVAL */ -# warning \ - No suitable replacement for missing socket error code is found. Edit this file and add replacement code which is defined on system. -# define MHD_socket_set_error_(err) (errno = (err)) -# endif /* !EOPNOTSUPP && !EFAULT && !EINVAL*/ -#elif defined(MHD_WINSOCK_SOCKETS) -# define MHD_socket_set_error_(err) ( (MHD_SCKT_MISSING_ERR_CODE_ == (err)) ? \ - (WSASetLastError ((WSAEOPNOTSUPP))) : \ - (WSASetLastError ((err))) ) -#endif - -/** - * Check whether given socket error is equal to specified system - * native MHD_SCKT_E*_ code. - * If platform don't have specific error code, result is - * always boolean false. - * @return boolean true if @a code is real error code and - * @a err equals to MHD_SCKT_E*_ @a code; - * boolean false otherwise - */ -#define MHD_SCKT_ERR_IS_(err,code) ( (MHD_SCKT_MISSING_ERR_CODE_ != (code)) && \ - ((code) == (err)) ) - -/** - * Check whether last socket error is equal to specified system - * native MHD_SCKT_E*_ code. - * If platform don't have specific error code, result is - * always boolean false. - * @return boolean true if @a code is real error code and - * last socket error equals to MHD_SCKT_E*_ @a code; - * boolean false otherwise - */ -#define MHD_SCKT_LAST_ERR_IS_(code) MHD_SCKT_ERR_IS_ (MHD_socket_get_error_ (), \ - (code)) - -/* Specific error code checks */ - -/** - * Check whether given socket error is equal to system's - * socket error codes for EINTR. - * @return boolean true if @a err is equal to sockets' EINTR code; - * boolean false otherwise. - */ -#define MHD_SCKT_ERR_IS_EINTR_(err) MHD_SCKT_ERR_IS_ ((err),MHD_SCKT_EINTR_) - -/** - * Check whether given socket error is equal to system's - * socket error codes for EAGAIN or EWOULDBLOCK. - * @return boolean true if @a err is equal to sockets' EAGAIN or EWOULDBLOCK codes; - * boolean false otherwise. - */ -#if MHD_SCKT_EAGAIN_ == MHD_SCKT_EWOULDBLOCK_ -# define MHD_SCKT_ERR_IS_EAGAIN_(err) MHD_SCKT_ERR_IS_ ((err),MHD_SCKT_EAGAIN_) -#else /* MHD_SCKT_EAGAIN_ != MHD_SCKT_EWOULDBLOCK_ */ -# define MHD_SCKT_ERR_IS_EAGAIN_(err) (MHD_SCKT_ERR_IS_ ((err), \ - MHD_SCKT_EAGAIN_) || \ - MHD_SCKT_ERR_IS_ ((err), \ - MHD_SCKT_EWOULDBLOCK_) ) -#endif /* MHD_SCKT_EAGAIN_ != MHD_SCKT_EWOULDBLOCK_ */ - -/** - * Check whether given socket error is any kind of "low resource" error. - * @return boolean true if @a err is any kind of "low resource" error, - * boolean false otherwise. - */ -#define MHD_SCKT_ERR_IS_LOW_RESOURCES_(err) (MHD_SCKT_ERR_IS_ ((err), \ - MHD_SCKT_EMFILE_) \ - || \ - MHD_SCKT_ERR_IS_ ((err), \ - MHD_SCKT_ENFILE_) \ - || \ - MHD_SCKT_ERR_IS_ ((err), \ - MHD_SCKT_ENOMEM_) \ - || \ - MHD_SCKT_ERR_IS_ ((err), \ - MHD_SCKT_ENOBUFS_) ) - -/** - * Check whether is given socket error is type of "incoming connection - * was disconnected before 'accept()' is called". - * @return boolean true is @a err match described socket error code, - * boolean false otherwise. - */ -#if defined(MHD_POSIX_SOCKETS) -# define MHD_SCKT_ERR_IS_DISCNN_BEFORE_ACCEPT_(err) MHD_SCKT_ERR_IS_ ((err), \ - MHD_SCKT_ECONNABORTED_) -#elif defined(MHD_WINSOCK_SOCKETS) -# define MHD_SCKT_ERR_IS_DISCNN_BEFORE_ACCEPT_(err) MHD_SCKT_ERR_IS_ ((err), \ - MHD_SCKT_ECONNRESET_) -#endif - -/** - * Check whether is given socket error is type of "connection was terminated - * by remote side". - * @return boolean true is @a err match described socket error code, - * boolean false otherwise. - */ -#define MHD_SCKT_ERR_IS_REMOTE_DISCNN_(err) (MHD_SCKT_ERR_IS_ ((err), \ - MHD_SCKT_ECONNRESET_) \ - || \ - MHD_SCKT_ERR_IS_ ((err), \ - MHD_SCKT_ECONNABORTED_)) - -/* Specific error code set */ - -/** - * Set socket's error code to ENOMEM or equivalent if ENOMEM is not - * available on platform. - */ -#if MHD_SCKT_MISSING_ERR_CODE_ != MHD_SCKT_ENOMEM_ -# define MHD_socket_set_error_to_ENOMEM() MHD_socket_set_error_ ( \ - MHD_SCKT_ENOMEM_) -#elif MHD_SCKT_MISSING_ERR_CODE_ != MHD_SCKT_ENOBUFS_ -# define MHD_socket_set_error_to_ENOMEM() MHD_socket_set_error_ ( \ - MHD_SCKT_ENOBUFS_) -#else -# warning \ - No suitable replacement for ENOMEM error codes is found. Edit this file and add replacement code which is defined on system. -# define MHD_socket_set_error_to_ENOMEM() MHD_socket_set_error_ ( \ - MHD_SCKT_ENOMEM_) -#endif - -/* Socket functions */ - -#if defined(AF_LOCAL) -# define MHD_SCKT_LOCAL AF_LOCAL -#elif defined(AF_UNIX) -# define MHD_SCKT_LOCAL AF_UNIX -#endif /* AF_UNIX */ - -#if defined(MHD_POSIX_SOCKETS) && defined(MHD_SCKT_LOCAL) -# define MHD_socket_pair_(fdarr) (! socketpair (MHD_SCKT_LOCAL, SOCK_STREAM, 0, \ - (fdarr))) -# if defined(HAVE_SOCK_NONBLOCK) -# define MHD_socket_pair_nblk_(fdarr) (! socketpair (MHD_SCKT_LOCAL, \ - SOCK_STREAM \ - | SOCK_NONBLOCK, 0, \ - (fdarr))) -# endif /* HAVE_SOCK_NONBLOCK*/ -#elif defined(MHD_WINSOCK_SOCKETS) -/** - * Create pair of mutually connected TCP/IP sockets on loopback address - * @param sockets_pair array to receive resulted sockets - * @param non_blk if set to non-zero value, sockets created in non-blocking mode - * otherwise sockets will be in blocking mode - * @return non-zero if succeeded, zero otherwise - */ -int MHD_W32_socket_pair_ (SOCKET sockets_pair[2], int non_blk); - -# define MHD_socket_pair_(fdarr) MHD_W32_socket_pair_ ((fdarr), 0) -# define MHD_socket_pair_nblk_(fdarr) MHD_W32_socket_pair_ ((fdarr), 1) -#endif - -/** - * Add @a fd to the @a set. If @a fd is - * greater than @a max_fd, set @a max_fd to @a fd. - * - * @param fd file descriptor to add to the @a set - * @param set set to modify - * @param max_fd maximum value to potentially update - * @param fd_setsize value of FD_SETSIZE - * @return non-zero if succeeded, zero otherwise - */ -int -MHD_add_to_fd_set_ (MHD_socket fd, - fd_set *set, - MHD_socket *max_fd, - unsigned int fd_setsize); - - -/** - * Change socket options to be non-blocking. - * - * @param sock socket to manipulate - * @return non-zero if succeeded, zero otherwise - */ -int -MHD_socket_nonblocking_ (MHD_socket sock); - - -/** - * Change socket options to be non-inheritable. - * - * @param sock socket to manipulate - * @return non-zero if succeeded, zero otherwise - * @warning Does not set socket error on W32. - */ -int -MHD_socket_noninheritable_ (MHD_socket sock); - - -#if defined(SOL_SOCKET) && defined(SO_NOSIGPIPE) -static const int _MHD_socket_int_one = 1; -/** - * Change socket options to no signal on remote disconnect. - * - * @param sock socket to manipulate - * @return non-zero if succeeded, zero otherwise - */ -# define MHD_socket_nosignal_(sock) \ - (! setsockopt ((sock),SOL_SOCKET,SO_NOSIGPIPE,&_MHD_socket_int_one, \ - sizeof(_MHD_socket_int_one))) -#endif /* SOL_SOCKET && SO_NOSIGPIPE */ - -/** - * Create a listen socket, with noninheritable flag if possible. - * - * @param pf protocol family to use - * @return created socket or MHD_INVALID_SOCKET in case of errors - */ -MHD_socket -MHD_socket_create_listen_ (int pf); - -#endif /* ! MHD_SOCKETS_H */ diff --git a/src/lib/mhd_str.c b/src/lib/mhd_str.c @@ -1,788 +0,0 @@ -/* - This file is part of libmicrohttpd - Copyright (C) 2015, 2016 Karlson2k (Evgeny Grin) - - 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 microhttpd/mhd_str.c - * @brief Functions implementations for string manipulating - * @author Karlson2k (Evgeny Grin) - */ - -#include "mhd_str.h" - -#ifdef HAVE_STDBOOL_H -#include <stdbool.h> -#endif - -#include "mhd_limits.h" - -#ifdef MHD_FAVOR_SMALL_CODE -#ifdef _MHD_static_inline -#undef _MHD_static_inline -#endif /* _MHD_static_inline */ -/* Do not force inlining and do not use macro functions, use normal static - functions instead. - This may give more flexibility for size optimizations. */ -#define _MHD_static_inline static -#ifndef INLINE_FUNC -#define INLINE_FUNC 1 -#endif /* !INLINE_FUNC */ -#endif /* MHD_FAVOR_SMALL_CODE */ - -/* - * Block of functions/macros that use US-ASCII charset as required by HTTP - * standards. Not affected by current locale settings. - */ - -#ifdef INLINE_FUNC - -#if 0 /* Disable unused functions. */ -/** - * Check whether character is lower case letter in US-ASCII - * - * @param c character to check - * @return non-zero if character is lower case letter, zero otherwise - */ -_MHD_static_inline bool -isasciilower (char c) -{ - return (c >= 'a') && (c <= 'z'); -} - - -#endif /* Disable unused functions. */ - - -/** - * Check whether character is upper case letter in US-ASCII - * - * @param c character to check - * @return non-zero if character is upper case letter, zero otherwise - */ -_MHD_static_inline bool -isasciiupper (char c) -{ - return (c >= 'A') && (c <= 'Z'); -} - - -#if 0 /* Disable unused functions. */ -/** - * Check whether character is letter in US-ASCII - * - * @param c character to check - * @return non-zero if character is letter in US-ASCII, zero otherwise - */ -_MHD_static_inline bool -isasciialpha (char c) -{ - return isasciilower (c) || isasciiupper (c); -} - - -#endif /* Disable unused functions. */ - - -/** - * Check whether character is decimal digit in US-ASCII - * - * @param c character to check - * @return non-zero if character is decimal digit, zero otherwise - */ -_MHD_static_inline bool -isasciidigit (char c) -{ - return (c >= '0') && (c <= '9'); -} - - -#if 0 /* Disable unused functions. */ -/** - * Check whether character is hexadecimal digit in US-ASCII - * - * @param c character to check - * @return non-zero if character is decimal digit, zero otherwise - */ -_MHD_static_inline bool -isasciixdigit (char c) -{ - return isasciidigit (c) || - ( (c >= 'A') && (c <= 'F') ) || - ( (c >= 'a') && (c <= 'f') ); -} - - -/** - * Check whether character is decimal digit or letter in US-ASCII - * - * @param c character to check - * @return non-zero if character is decimal digit or letter, zero otherwise - */ -_MHD_static_inline bool -isasciialnum (char c) -{ - return isasciialpha (c) || isasciidigit (c); -} - - -#endif /* Disable unused functions. */ - - -/** - * Convert US-ASCII character to lower case. - * If character is upper case letter in US-ASCII than it's converted to lower - * case analog. If character is NOT upper case letter than it's returned - * unmodified. - * - * @param c character to convert - * @return converted to lower case character - */ -_MHD_static_inline char -toasciilower (char c) -{ - return isasciiupper (c) ? (c - 'A' + 'a') : c; -} - - -#if 0 /* Disable unused functions. */ -/** - * Convert US-ASCII character to upper case. - * If character is lower case letter in US-ASCII than it's converted to upper - * case analog. If character is NOT lower case letter than it's returned - * unmodified. - * - * @param c character to convert - * @return converted to upper case character - */ -_MHD_static_inline char -toasciiupper (char c) -{ - return isasciilower (c) ? (c - 'a' + 'A') : c; -} - - -#endif /* Disable unused functions. */ - - -#if defined(MHD_FAVOR_SMALL_CODE) /* Used only in MHD_str_to_uvalue_n_() */ -/** - * Convert US-ASCII decimal digit to its value. - * - * @param c character to convert - * @return value of decimal digit or -1 if @ c is not decimal digit - */ -_MHD_static_inline int -todigitvalue (char c) -{ - if (isasciidigit (c)) - return (unsigned char) (c - '0'); - - return -1; -} - - -#endif /* MHD_FAVOR_SMALL_CODE */ - - -/** - * Convert US-ASCII hexadecimal digit to its value. - * - * @param c character to convert - * @return value of hexadecimal digit or -1 if @ c is not hexadecimal digit - */ -_MHD_static_inline int -toxdigitvalue (char c) -{ - if (isasciidigit (c)) - return (unsigned char) (c - '0'); - if ( (c >= 'A') && (c <= 'F') ) - return (unsigned char) (c - 'A' + 10); - if ( (c >= 'a') && (c <= 'f') ) - return (unsigned char) (c - 'a' + 10); - - return -1; -} - - -#else /* !INLINE_FUNC */ - - -/** - * Checks whether character is lower case letter in US-ASCII - * - * @param c character to check - * @return boolean true if character is lower case letter, - * boolean false otherwise - */ -#define isasciilower(c) (((char) (c)) >= 'a' && ((char) (c)) <= 'z') - - -/** - * Checks whether character is upper case letter in US-ASCII - * - * @param c character to check - * @return boolean true if character is upper case letter, - * boolean false otherwise - */ -#define isasciiupper(c) (((char) (c)) >= 'A' && ((char) (c)) <= 'Z') - - -/** - * Checks whether character is letter in US-ASCII - * - * @param c character to check - * @return boolean true if character is letter, boolean false - * otherwise - */ -#define isasciialpha(c) (isasciilower (c) || isasciiupper (c)) - - -/** - * Check whether character is decimal digit in US-ASCII - * - * @param c character to check - * @return boolean true if character is decimal digit, boolean false - * otherwise - */ -#define isasciidigit(c) (((char) (c)) >= '0' && ((char) (c)) <= '9') - - -/** - * Check whether character is hexadecimal digit in US-ASCII - * - * @param c character to check - * @return boolean true if character is hexadecimal digit, - * boolean false otherwise - */ -#define isasciixdigit(c) (isasciidigit ((c)) || \ - (((char) (c)) >= 'A' && ((char) (c)) <= 'F') || \ - (((char) (c)) >= 'a' && ((char) (c)) <= 'f') ) - - -/** - * Check whether character is decimal digit or letter in US-ASCII - * - * @param c character to check - * @return boolean true if character is decimal digit or letter, - * boolean false otherwise - */ -#define isasciialnum(c) (isasciialpha (c) || isasciidigit (c)) - - -/** - * Convert US-ASCII character to lower case. - * If character is upper case letter in US-ASCII than it's converted to lower - * case analog. If character is NOT upper case letter than it's returned - * unmodified. - * - * @param c character to convert - * @return converted to lower case character - */ -#define toasciilower(c) ((isasciiupper (c)) ? (((char) (c)) - 'A' + 'a') : \ - ((char) (c))) - - -/** - * Convert US-ASCII character to upper case. - * If character is lower case letter in US-ASCII than it's converted to upper - * case analog. If character is NOT lower case letter than it's returned - * unmodified. - * - * @param c character to convert - * @return converted to upper case character - */ -#define toasciiupper(c) ((isasciilower (c)) ? (((char) (c)) - 'a' + 'A') : \ - ((char) (c))) - - -/** - * Convert US-ASCII decimal digit to its value. - * - * @param c character to convert - * @return value of hexadecimal digit or -1 if @ c is not hexadecimal digit - */ -#define todigitvalue(c) (isasciidigit (c) ? (int) (((char) (c)) - '0') : \ - (int) (-1)) - - -/** - * Convert US-ASCII hexadecimal digit to its value. - * @param c character to convert - * @return value of hexadecimal digit or -1 if @ c is not hexadecimal digit - */ -#define toxdigitvalue(c) (isasciidigit (c) ? (int) (((char) (c)) - '0') : \ - ( (((char) (c)) >= 'A' && ((char) (c)) <= 'F') ? \ - (int) (((unsigned char) (c)) - 'A' + 10) : \ - ( (((char) (c)) >= 'a' && ((char) (c)) <= 'f') ? \ - (int) (((unsigned char) (c)) - 'a' + 10) : \ - (int) (-1) ))) -#endif /* !INLINE_FUNC */ - - -#ifndef MHD_FAVOR_SMALL_CODE -/** - * Check two string for equality, ignoring case of US-ASCII letters. - * - * @param str1 first string to compare - * @param str2 second string to compare - * @return non-zero if two strings are equal, zero otherwise. - */ -int -MHD_str_equal_caseless_ (const char *str1, - const char *str2) -{ - while (0 != (*str1)) - { - const char c1 = *str1; - const char c2 = *str2; - if ( (c1 != c2) && - (toasciilower (c1) != toasciilower (c2)) ) - return 0; - str1++; - str2++; - } - return 0 == (*str2); -} - - -#endif /* ! MHD_FAVOR_SMALL_CODE */ - - -/** - * Check two string for equality, ignoring case of US-ASCII letters and - * checking not more than @a maxlen characters. - * Compares up to first terminating null character, but not more than - * first @a maxlen characters. - * - * @param str1 first string to compare - * @param str2 second string to compare - * @param maxlen maximum number of characters to compare - * @return non-zero if two strings are equal, zero otherwise. - */ -int -MHD_str_equal_caseless_n_ (const char *const str1, - const char *const str2, - size_t maxlen) -{ - size_t i; - - for (i = 0; i < maxlen; ++i) - { - const char c1 = str1[i]; - const char c2 = str2[i]; - if (0 == c2) - return 0 == c1; - if ( (c1 != c2) && - (toasciilower (c1) != toasciilower (c2)) ) - return 0; - } - return ! 0; -} - - -/** - * Check whether @a str has case-insensitive @a token. - * Token could be surrounded by spaces and tabs and delimited by comma. - * Match succeed if substring between start, end (of string) or comma - * contains only case-insensitive token and optional spaces and tabs. - * @warning token must not contain null-charters except optional - * terminating null-character. - * @param str the string to check - * @param token the token to find - * @param token_len length of token, not including optional terminating - * null-character. - * @return non-zero if two strings are equal, zero otherwise. - */ -bool -MHD_str_has_token_caseless_ (const char *str, - const char *const token, - size_t token_len) -{ - if (0 == token_len) - return false; - - while (0 != *str) - { - size_t i; - /* Skip all whitespaces and empty tokens. */ - while (' ' == *str || '\t' == *str || ',' == *str) - str++; - - /* Check for token match. */ - i = 0; - while (1) - { - const char sc = *(str++); - const char tc = token[i++]; - - if (0 == sc) - return false; - if ( (sc != tc) && - (toasciilower (sc) != toasciilower (tc)) ) - break; - if (i >= token_len) - { - /* Check whether substring match token fully or - * has additional unmatched chars at tail. */ - while (' ' == *str || '\t' == *str) - str++; - /* End of (sub)string? */ - if ((0 == *str) || (',' == *str) ) - return true; - /* Unmatched chars at end of substring. */ - break; - } - } - /* Find next substring. */ - while (0 != *str && ',' != *str) - str++; - } - return false; -} - - -#ifndef MHD_FAVOR_SMALL_CODE -/* Use individual function for each case */ - -/** - * Convert decimal US-ASCII digits in string to number in uint64_t. - * Conversion stopped at first non-digit character. - * - * @param str string to convert - * @param[out] out_val pointer to uint64_t to store result of conversion - * @return non-zero number of characters processed on succeed, - * zero if no digit is found, resulting value is larger - * then possible to store in uint64_t or @a out_val is NULL - */ -size_t -MHD_str_to_uint64_ (const char *str, - uint64_t *out_val) -{ - const char *const start = str; - uint64_t res; - - if (! str || ! out_val || ! isasciidigit (str[0])) - return 0; - - res = 0; - do - { - const int digit = (unsigned char) (*str) - '0'; - if ( (res > (UINT64_MAX / 10)) || - ( (res == (UINT64_MAX / 10)) && - ((uint64_t) digit > (UINT64_MAX % 10)) ) ) - return 0; - - res *= 10; - res += digit; - str++; - } while (isasciidigit (*str)); - - *out_val = res; - return str - start; -} - - -/** - * Convert not more then @a maxlen decimal US-ASCII digits in string to - * number in uint64_t. - * Conversion stopped at first non-digit character or after @a maxlen - * digits. - * - * @param str string to convert - * @param maxlen maximum number of characters to process - * @param[out] out_val pointer to uint64_t to store result of conversion - * @return non-zero number of characters processed on succeed, - * zero if no digit is found, resulting value is larger - * then possible to store in uint64_t or @a out_val is NULL - */ -size_t -MHD_str_to_uint64_n_ (const char *str, - size_t maxlen, - uint64_t *out_val) -{ - uint64_t res; - size_t i; - - if (! str || ! maxlen || ! out_val || ! isasciidigit (str[0])) - return 0; - - res = 0; - i = 0; - do - { - const int digit = (unsigned char) str[i] - '0'; - - if ( (res > (UINT64_MAX / 10)) || - ( (res == (UINT64_MAX / 10)) && - ((uint64_t) digit > (UINT64_MAX % 10)) ) ) - return 0; - - res *= 10; - res += digit; - i++; - } while ( (i < maxlen) && - isasciidigit (str[i]) ); - - *out_val = res; - return i; -} - - -/** - * Convert hexadecimal US-ASCII digits in string to number in uint32_t. - * Conversion stopped at first non-digit character. - * - * @param str string to convert - * @param[out] out_val pointer to uint32_t to store result of conversion - * @return non-zero number of characters processed on succeed, - * zero if no digit is found, resulting value is larger - * then possible to store in uint32_t or @a out_val is NULL - */ -size_t -MHD_strx_to_uint32_ (const char *str, - uint32_t *out_val) -{ - const char *const start = str; - uint32_t res; - int digit; - - if (! str || ! out_val) - return 0; - - res = 0; - digit = toxdigitvalue (*str); - while (digit >= 0) - { - if ( (res < (UINT32_MAX / 16)) || - ((res == (UINT32_MAX / 16)) && ( (uint32_t) digit <= (UINT32_MAX - % 16)) ) ) - { - res *= 16; - res += digit; - } - else - return 0; - str++; - digit = toxdigitvalue (*str); - } - - if (str - start > 0) - *out_val = res; - return str - start; -} - - -/** - * Convert not more then @a maxlen hexadecimal US-ASCII digits in string - * to number in uint32_t. - * Conversion stopped at first non-digit character or after @a maxlen - * digits. - * - * @param str string to convert - * @param maxlen maximum number of characters to process - * @param[out] out_val pointer to uint32_t to store result of conversion - * @return non-zero number of characters processed on succeed, - * zero if no digit is found, resulting value is larger - * then possible to store in uint32_t or @a out_val is NULL - */ -size_t -MHD_strx_to_uint32_n_ (const char *str, - size_t maxlen, - uint32_t *out_val) -{ - size_t i; - uint32_t res; - int digit; - if (! str || ! out_val) - return 0; - - res = 0; - i = 0; - while (i < maxlen && (digit = toxdigitvalue (str[i])) >= 0) - { - if ( (res > (UINT32_MAX / 16)) || - ((res == (UINT32_MAX / 16)) && ( (uint32_t) digit > (UINT32_MAX - % 16)) ) ) - return 0; - - res *= 16; - res += digit; - i++; - } - - if (i) - *out_val = res; - return i; -} - - -/** - * Convert hexadecimal US-ASCII digits in string to number in uint64_t. - * Conversion stopped at first non-digit character. - * - * @param str string to convert - * @param[out] out_val pointer to uint64_t to store result of conversion - * @return non-zero number of characters processed on succeed, - * zero if no digit is found, resulting value is larger - * then possible to store in uint64_t or @a out_val is NULL - */ -size_t -MHD_strx_to_uint64_ (const char *str, - uint64_t *out_val) -{ - const char *const start = str; - uint64_t res; - int digit; - if (! str || ! out_val) - return 0; - - res = 0; - digit = toxdigitvalue (*str); - while (digit >= 0) - { - if ( (res < (UINT64_MAX / 16)) || - ((res == (UINT64_MAX / 16)) && ( (uint64_t) digit <= (UINT64_MAX - % 16)) ) ) - { - res *= 16; - res += digit; - } - else - return 0; - str++; - digit = toxdigitvalue (*str); - } - - if (str - start > 0) - *out_val = res; - return str - start; -} - - -/** - * Convert not more then @a maxlen hexadecimal US-ASCII digits in string - * to number in uint64_t. - * Conversion stopped at first non-digit character or after @a maxlen - * digits. - * - * @param str string to convert - * @param maxlen maximum number of characters to process - * @param[out] out_val pointer to uint64_t to store result of conversion - * @return non-zero number of characters processed on succeed, - * zero if no digit is found, resulting value is larger - * then possible to store in uint64_t or @a out_val is NULL - */ -size_t -MHD_strx_to_uint64_n_ (const char *str, - size_t maxlen, - uint64_t *out_val) -{ - size_t i; - uint64_t res; - int digit; - if (! str || ! out_val) - return 0; - - res = 0; - i = 0; - while (i < maxlen && (digit = toxdigitvalue (str[i])) >= 0) - { - if ( (res > (UINT64_MAX / 16)) || - ((res == (UINT64_MAX / 16)) && ( (uint64_t) digit > (UINT64_MAX - % 16)) ) ) - return 0; - - res *= 16; - res += digit; - i++; - } - - if (i) - *out_val = res; - return i; -} - - -#else /* MHD_FAVOR_SMALL_CODE */ - -/** - * Generic function for converting not more then @a maxlen - * hexadecimal or decimal US-ASCII digits in string to number. - * Conversion stopped at first non-digit character or after @a maxlen - * digits. - * To be used only within macro. - * - * @param str the string to convert - * @param maxlen the maximum number of characters to process - * @param out_val the pointer to variable to store result of conversion - * @param val_size the size of variable pointed by @a out_val, in bytes, 4 or 8 - * @param max_val the maximum decoded number - * @param base the numeric base, 10 or 16 - * @return non-zero number of characters processed on succeed, - * zero if no digit is found, resulting value is larger - * then @max_val, @val_size is not 16/32 or @a out_val is NULL - */ -size_t -MHD_str_to_uvalue_n_ (const char *str, - size_t maxlen, - void *out_val, - size_t val_size, - uint64_t max_val, - int base) -{ - size_t i; - uint64_t res; - int digit; - const uint64_t max_v_div_b = max_val / base; - const uint64_t max_v_mod_b = max_val % base; - /* 'digit->value' must be function, not macro */ - int (*const dfunc)(char) = (base == 16) ? - toxdigitvalue : todigitvalue; - - if (! str || ! out_val || - ((base != 16) && (base != 10)) ) - return 0; - - res = 0; - i = 0; - while (maxlen > i && 0 <= (digit = dfunc (str[i]))) - { - if ( ((max_v_div_b) < res) || - (( (max_v_div_b) == res) && ( (max_v_mod_b) < (uint64_t) digit) ) ) - return 0; - - res *= base; - res += digit; - i++; - } - - if (i) - { - if (8 == val_size) - *(uint64_t *) out_val = res; - else if (4 == val_size) - *(uint32_t *) out_val = (uint32_t) res; - else - return 0; - } - return i; -} - - -#endif /* MHD_FAVOR_SMALL_CODE */ diff --git a/src/lib/mhd_str.h b/src/lib/mhd_str.h @@ -1,276 +0,0 @@ -/* - This file is part of libmicrohttpd - Copyright (C) 2015, 2016 Karlson2k (Evgeny Grin) - - 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 microhttpd/mhd_str.h - * @brief Header for string manipulating helpers - * @author Karlson2k (Evgeny Grin) - */ - -#ifndef MHD_STR_H -#define MHD_STR_H 1 - -#include "mhd_options.h" - -#include <stdint.h> -#include <stdlib.h> -#ifdef HAVE_STDBOOL_H -#include <stdbool.h> -#endif /* HAVE_STDBOOL_H */ - -#ifdef MHD_FAVOR_SMALL_CODE -#include "mhd_limits.h" -#endif /* MHD_FAVOR_SMALL_CODE */ - -#ifndef MHD_STATICSTR_LEN_ -/** - * Determine length of static string / macro strings at compile time. - */ -#define MHD_STATICSTR_LEN_(macro) (sizeof(macro) / sizeof(char) - 1) -#endif /* ! MHD_STATICSTR_LEN_ */ - -/* - * Block of functions/macros that use US-ASCII charset as required by HTTP - * standards. Not affected by current locale settings. - */ - -#ifndef MHD_FAVOR_SMALL_CODE -/** - * Check two string for equality, ignoring case of US-ASCII letters. - * @param str1 first string to compare - * @param str2 second string to compare - * @return non-zero if two strings are equal, zero otherwise. - */ -int -MHD_str_equal_caseless_ (const char *str1, - const char *str2); - -#else /* MHD_FAVOR_SMALL_CODE */ -/* Reuse MHD_str_equal_caseless_n_() to reduce size */ -#define MHD_str_equal_caseless_(s1,s2) MHD_str_equal_caseless_n_ ((s1),(s2), \ - SIZE_MAX) -#endif /* MHD_FAVOR_SMALL_CODE */ - - -/** - * Check two string for equality, ignoring case of US-ASCII letters and - * checking not more than @a maxlen characters. - * Compares up to first terminating null character, but not more than - * first @a maxlen characters. - * @param str1 first string to compare - * @param str2 second string to compare - * @param maxlen maximum number of characters to compare - * @return non-zero if two strings are equal, zero otherwise. - */ -int -MHD_str_equal_caseless_n_ (const char *const str1, - const char *const str2, - size_t maxlen); - - -/** - * Check whether @a str has case-insensitive @a token. - * Token could be surrounded by spaces and tabs and delimited by comma. - * Match succeed if substring between start, end of string or comma - * contains only case-insensitive token and optional spaces and tabs. - * @warning token must not contain null-charters except optional - * terminating null-character. - * @param str the string to check - * @param token the token to find - * @param token_len length of token, not including optional terminating - * null-character. - * @return non-zero if two strings are equal, zero otherwise. - */ -bool -MHD_str_has_token_caseless_ (const char *str, - const char *const token, - size_t token_len); - -/** - * Check whether @a str has case-insensitive static @a tkn. - * Token could be surrounded by spaces and tabs and delimited by comma. - * Match succeed if substring between start, end of string or comma - * contains only case-insensitive token and optional spaces and tabs. - * @warning tkn must be static string - * @param str the string to check - * @param tkn the static string of token to find - * @return non-zero if two strings are equal, zero otherwise. - */ -#define MHD_str_has_s_token_caseless_(str,tkn) \ - MHD_str_has_token_caseless_ ((str),(tkn),MHD_STATICSTR_LEN_ (tkn)) - -#ifndef MHD_FAVOR_SMALL_CODE -/* Use individual function for each case to improve speed */ - -/** - * Convert decimal US-ASCII digits in string to number in uint64_t. - * Conversion stopped at first non-digit character. - * @param str string to convert - * @param out_val pointer to uint64_t to store result of conversion - * @return non-zero number of characters processed on succeed, - * zero if no digit is found, resulting value is larger - * then possible to store in uint64_t or @a out_val is NULL - */ -size_t -MHD_str_to_uint64_ (const char *str, - uint64_t *out_val); - -/** - * Convert not more then @a maxlen decimal US-ASCII digits in string to - * number in uint64_t. - * Conversion stopped at first non-digit character or after @a maxlen - * digits. - * @param str string to convert - * @param maxlen maximum number of characters to process - * @param out_val pointer to uint64_t to store result of conversion - * @return non-zero number of characters processed on succeed, - * zero if no digit is found, resulting value is larger - * then possible to store in uint64_t or @a out_val is NULL - */ -size_t -MHD_str_to_uint64_n_ (const char *str, - size_t maxlen, - uint64_t *out_val); - - -/** - * Convert hexadecimal US-ASCII digits in string to number in uint32_t. - * Conversion stopped at first non-digit character. - * @param str string to convert - * @param out_val pointer to uint32_t to store result of conversion - * @return non-zero number of characters processed on succeed, - * zero if no digit is found, resulting value is larger - * then possible to store in uint32_t or @a out_val is NULL - */ -size_t -MHD_strx_to_uint32_ (const char *str, - uint32_t *out_val); - - -/** - * Convert not more then @a maxlen hexadecimal US-ASCII digits in string - * to number in uint32_t. - * Conversion stopped at first non-digit character or after @a maxlen - * digits. - * @param str string to convert - * @param maxlen maximum number of characters to process - * @param out_val pointer to uint32_t to store result of conversion - * @return non-zero number of characters processed on succeed, - * zero if no digit is found, resulting value is larger - * then possible to store in uint32_t or @a out_val is NULL - */ -size_t -MHD_strx_to_uint32_n_ (const char *str, - size_t maxlen, - uint32_t *out_val); - - -/** - * Convert hexadecimal US-ASCII digits in string to number in uint64_t. - * Conversion stopped at first non-digit character. - * @param str string to convert - * @param out_val pointer to uint64_t to store result of conversion - * @return non-zero number of characters processed on succeed, - * zero if no digit is found, resulting value is larger - * then possible to store in uint64_t or @a out_val is NULL - */ -size_t -MHD_strx_to_uint64_ (const char *str, - uint64_t *out_val); - - -/** - * Convert not more then @a maxlen hexadecimal US-ASCII digits in string - * to number in uint64_t. - * Conversion stopped at first non-digit character or after @a maxlen - * digits. - * @param str string to convert - * @param maxlen maximum number of characters to process - * @param out_val pointer to uint64_t to store result of conversion - * @return non-zero number of characters processed on succeed, - * zero if no digit is found, resulting value is larger - * then possible to store in uint64_t or @a out_val is NULL - */ -size_t -MHD_strx_to_uint64_n_ (const char *str, - size_t maxlen, - uint64_t *out_val); - -#else /* MHD_FAVOR_SMALL_CODE */ -/* Use one universal function and macros to reduce size */ - -/** - * Generic function for converting not more then @a maxlen - * hexadecimal or decimal US-ASCII digits in string to number. - * Conversion stopped at first non-digit character or after @a maxlen - * digits. - * To be used only within macro. - * @param str the string to convert - * @param maxlen the maximum number of characters to process - * @param out_val the pointer to uint64_t to store result of conversion - * @param val_size the size of variable pointed by @a out_val - * @param max_val the maximum decoded number - * @param base the numeric base, 10 or 16 - * @return non-zero number of characters processed on succeed, - * zero if no digit is found, resulting value is larger - * then @ max_val or @a out_val is NULL - */ -size_t -MHD_str_to_uvalue_n_ (const char *str, - size_t maxlen, - void *out_val, - size_t val_size, - uint64_t max_val, - int base); - -#define MHD_str_to_uint64_(s,ov) MHD_str_to_uvalue_n_ ((s),SIZE_MAX,(ov), \ - sizeof(uint64_t), \ - UINT64_MAX,10) - -#define MHD_str_to_uint64_n_(s,ml,ov) MHD_str_to_uvalue_n_ ((s),(ml),(ov), \ - sizeof(uint64_t), \ - UINT64_MAX,10) - -#define MHD_strx_to_sizet_(s,ov) MHD_str_to_uvalue_n_ ((s),SIZE_MAX,(ov), \ - sizeof(size_t),SIZE_MAX, \ - 16) - -#define MHD_strx_to_sizet_n_(s,ml,ov) MHD_str_to_uvalue_n_ ((s),(ml),(ov), \ - sizeof(size_t), \ - SIZE_MAX,16) - -#define MHD_strx_to_uint32_(s,ov) MHD_str_to_uvalue_n_ ((s),SIZE_MAX,(ov), \ - sizeof(uint32_t), \ - UINT32_MAX,16) - -#define MHD_strx_to_uint32_n_(s,ml,ov) MHD_str_to_uvalue_n_ ((s),(ml),(ov), \ - sizeof(uint32_t), \ - UINT32_MAX,16) - -#define MHD_strx_to_uint64_(s,ov) MHD_str_to_uvalue_n_ ((s),SIZE_MAX,(ov), \ - sizeof(uint64_t), \ - UINT64_MAX,16) - -#define MHD_strx_to_uint64_n_(s,ml,ov) MHD_str_to_uvalue_n_ ((s),(ml),(ov), \ - sizeof(uint64_t), \ - UINT64_MAX,16) - -#endif /* MHD_FAVOR_SMALL_CODE */ - -#endif /* MHD_STR_H */ diff --git a/src/lib/mhd_threads.c b/src/lib/mhd_threads.c @@ -1,368 +0,0 @@ -/* - This file is part of libmicrohttpd - Copyright (C) 2016 Karlson2k (Evgeny Grin) - - 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 microhttpd/mhd_threads.c - * @brief Implementation for thread functions - * @author Karlson2k (Evgeny Grin) - */ - -#include "mhd_threads.h" -#ifdef MHD_USE_W32_THREADS -#include "mhd_limits.h" -#include <process.h> -#endif -#ifdef MHD_USE_THREAD_NAME_ -#include <stdlib.h> -#ifdef HAVE_PTHREAD_NP_H -#include <pthread_np.h> -#endif /* HAVE_PTHREAD_NP_H */ -#endif /* MHD_USE_THREAD_NAME_ */ -#include <errno.h> - - -#ifndef MHD_USE_THREAD_NAME_ - -#define MHD_set_thread_name_(t, n) (void) -#define MHD_set_cur_thread_name_(n) (void) - -#else /* MHD_USE_THREAD_NAME_ */ - -#if defined(MHD_USE_POSIX_THREADS) -#if defined(HAVE_PTHREAD_ATTR_SETNAME_NP_NETBSD) || \ - defined(HAVE_PTHREAD_ATTR_SETNAME_NP_IBMI) -# define MHD_USE_THREAD_ATTR_SETNAME 1 -#endif \ - /* HAVE_PTHREAD_ATTR_SETNAME_NP_NETBSD || HAVE_PTHREAD_ATTR_SETNAME_NP_IBMI */ - -#if defined(HAVE_PTHREAD_SETNAME_NP_GNU) || \ - defined(HAVE_PTHREAD_SET_NAME_NP_FREEBSD) \ - || defined(HAVE_PTHREAD_SETNAME_NP_NETBSD) - -/** - * Set thread name - * - * @param thread_id ID of thread - * @param thread_name name to set - * @return non-zero on success, zero otherwise - */ -static int -MHD_set_thread_name_ (const MHD_thread_ID_ thread_id, - const char *thread_name) -{ - if (NULL == thread_name) - return 0; - -#if defined(HAVE_PTHREAD_SETNAME_NP_GNU) - return ! pthread_setname_np (thread_id, thread_name); -#elif defined(HAVE_PTHREAD_SET_NAME_NP_FREEBSD) - /* FreeBSD and OpenBSD use different name and void return type */ - pthread_set_name_np (thread_id, thread_name); - return ! 0; -#elif defined(HAVE_PTHREAD_SETNAME_NP_NETBSD) - /* NetBSD use 3 arguments: second argument is string in printf-like format, - * third argument is single argument for printf; - * OSF1 use 3 arguments too, but last one always must be zero (NULL). - * MHD doesn't use '%' in thread names, so both form are used in same way. - */return ! pthread_setname_np (thread_id, thread_name, 0); -#endif /* HAVE_PTHREAD_SETNAME_NP_NETBSD */ -} - - -#ifndef __QNXNTO__ -/** - * Set current thread name - * @param n name to set - * @return non-zero on success, zero otherwise - */ -#define MHD_set_cur_thread_name_(n) MHD_set_thread_name_ (pthread_self (),(n)) -#else /* __QNXNTO__ */ -/* Special case for QNX Neutrino - using zero for thread ID sets name faster. */ -#define MHD_set_cur_thread_name_(n) MHD_set_thread_name_ (0,(n)) -#endif /* __QNXNTO__ */ -#elif defined(HAVE_PTHREAD_SETNAME_NP_DARWIN) - -/** - * Set current thread name - * @param n name to set - * @return non-zero on success, zero otherwise - */ -#define MHD_set_cur_thread_name_(n) (! (pthread_setname_np ((n)))) -#endif /* HAVE_PTHREAD_SETNAME_NP_DARWIN */ - -#elif defined(MHD_USE_W32_THREADS) -#ifndef _MSC_FULL_VER -/* Thread name available only for VC-compiler */ -#else /* _MSC_FULL_VER */ -/** - * Set thread name - * - * @param thread_id ID of thread, -1 for current thread - * @param thread_name name to set - * @return non-zero on success, zero otherwise - */ -static int -MHD_set_thread_name_ (const MHD_thread_ID_ thread_id, - const char *thread_name) -{ - static const DWORD VC_SETNAME_EXC = 0x406D1388; -#pragma pack(push,8) - struct thread_info_struct - { - DWORD type; /* Must be 0x1000. */ - LPCSTR name; /* Pointer to name (in user address space). */ - DWORD ID; /* Thread ID (-1 = caller thread). */ - DWORD flags; /* Reserved for future use, must be zero. */ - } thread_info; -#pragma pack(pop) - - if (NULL == thread_name) - return 0; - - thread_info.type = 0x1000; - thread_info.name = thread_name; - thread_info.ID = thread_id; - thread_info.flags = 0; - - __try - { /* This exception is intercepted by debugger */ - RaiseException (VC_SETNAME_EXC, - 0, - sizeof (thread_info) / sizeof(ULONG_PTR), - (ULONG_PTR *) &thread_info); - } - __except (EXCEPTION_EXECUTE_HANDLER) - {} - - return ! 0; -} - - -/** - * Set current thread name - * @param n name to set - * @return non-zero on success, zero otherwise - */ -#define MHD_set_cur_thread_name_(n) MHD_set_thread_name_ (-1,(n)) -#endif /* _MSC_FULL_VER */ -#endif /* MHD_USE_W32_THREADS */ - -#endif /* MHD_USE_THREAD_NAME_ */ - - -/** - * Create a thread and set the attributes according to our options. - * - * @param thread handle to initialize - * @param stack_size size of stack for new thread, 0 for default - * @param start_routine main function of thread - * @param arg argument for start_routine - * @return non-zero on success; zero otherwise (with errno set) - */ -int -MHD_create_thread_ (MHD_thread_handle_ID_ *thread, - size_t stack_size, - MHD_THREAD_START_ROUTINE_ start_routine, - void *arg) -{ -#if defined(MHD_USE_POSIX_THREADS) - int res; - - if (0 != stack_size) - { - pthread_attr_t attr; - res = pthread_attr_init (&attr); - if (0 == res) - { - res = pthread_attr_setstacksize (&attr, - stack_size); - if (0 == res) - res = pthread_create (&(thread->handle), - &attr, - start_routine, - arg); - pthread_attr_destroy (&attr); - } - } - else - res = pthread_create (&(thread->handle), - NULL, - start_routine, - arg); - - if (0 != res) - errno = res; - - return ! res; -#elif defined(MHD_USE_W32_THREADS) -#if SIZE_MAX != UINT_MAX - if (stack_size > UINT_MAX) - { - errno = EINVAL; - return 0; - } -#endif /* SIZE_MAX != UINT_MAX */ - - thread->handle = (MHD_thread_handle_) - _beginthreadex (NULL, - (unsigned int) stack_size, - start_routine, - arg, - 0, - NULL); - - if ((MHD_thread_handle_) - 1 == thread->handle) - return 0; - - return ! 0; -#endif -} - - -#ifdef MHD_USE_THREAD_NAME_ - -#ifndef MHD_USE_THREAD_ATTR_SETNAME -struct MHD_named_helper_param_ -{ - /** - * Real thread start routine - */ - MHD_THREAD_START_ROUTINE_ start_routine; - - /** - * Argument for thread start routine - */ - void *arg; - - /** - * Name for thread - */ - const char *name; -}; - - -static MHD_THRD_RTRN_TYPE_ MHD_THRD_CALL_SPEC_ -named_thread_starter (void *data) -{ - struct MHD_named_helper_param_ *const param = - (struct MHD_named_helper_param_ *) data; - void *arg; - MHD_THREAD_START_ROUTINE_ thr_func; - - if (NULL == data) - return (MHD_THRD_RTRN_TYPE_) 0; - - MHD_set_cur_thread_name_ (param->name); - - arg = param->arg; - thr_func = param->start_routine; - free (data); - - return thr_func (arg); -} - - -#endif /* ! MHD_USE_THREAD_ATTR_SETNAME */ - - -/** - * Create a named thread and set the attributes according to our options. - * - * @param thread handle to initialize - * @param thread_name name for new thread - * @param stack_size size of stack for new thread, 0 for default - * @param start_routine main function of thread - * @param arg argument for start_routine - * @return non-zero on success; zero otherwise (with errno set) - */ -int -MHD_create_named_thread_ (MHD_thread_handle_ID_ *thread, - const char *thread_name, - size_t stack_size, - MHD_THREAD_START_ROUTINE_ start_routine, - void *arg) -{ -#if defined(MHD_USE_THREAD_ATTR_SETNAME) - int res; - pthread_attr_t attr; - - res = pthread_attr_init (&attr); - if (0 == res) - { -#if defined(HAVE_PTHREAD_ATTR_SETNAME_NP_NETBSD) - /* NetBSD use 3 arguments: second argument is string in printf-like format, - * third argument is single argument for printf; - * OSF1 use 3 arguments too, but last one always must be zero (NULL). - * MHD doesn't use '%' in thread names, so both form are used in same way. - */res = pthread_attr_setname_np (&attr, thread_name, 0); -#elif defined(HAVE_PTHREAD_ATTR_SETNAME_NP_IBMI) - res = pthread_attr_setname_np (&attr, thread_name); -#else -#error No pthread_attr_setname_np() function. -#endif - if ((res == 0) && (0 != stack_size) ) - res = pthread_attr_setstacksize (&attr, - stack_size); - if (0 == res) - res = pthread_create (&(thread->handle), - &attr, - start_routine, - arg); - pthread_attr_destroy (&attr); - } - if (0 != res) - errno = res; - - return ! res; -#else /* ! MHD_USE_THREAD_ATTR_SETNAME */ - struct MHD_named_helper_param_ *param; - - if (NULL == thread_name) - { - errno = EINVAL; - return 0; - } - - param = malloc (sizeof (struct MHD_named_helper_param_)); - if (NULL == param) - return 0; - - param->start_routine = start_routine; - param->arg = arg; - param->name = thread_name; - - /* Set thread name in thread itself to avoid problems with - * threads which terminated before name is set in other thread. - */ - if (! MHD_create_thread_ (thread, - stack_size, - &named_thread_starter, - (void *) param)) - { - free (param); - return 0; - } - - return ! 0; -#endif /* ! MHD_USE_THREAD_ATTR_SETNAME */ -} - - -#endif /* MHD_USE_THREAD_NAME_ */ diff --git a/src/lib/mhd_threads.h b/src/lib/mhd_threads.h @@ -1,237 +0,0 @@ -/* - This file is part of libmicrohttpd - Copyright (C) 2016 Karlson2k (Evgeny Grin) - - 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 microhttpd/mhd_threads.h - * @brief Header for platform-independent threads abstraction - * @author Karlson2k (Evgeny Grin) - * - * Provides basic abstraction for threads. - * Any functions can be implemented as macro on some platforms - * unless explicitly marked otherwise. - * Any function argument can be skipped in macro, so avoid - * variable modification in function parameters. - * - * @warning Unlike pthread functions, most of functions return - * nonzero on success. - */ - -#ifndef MHD_THREADS_H -#define MHD_THREADS_H 1 - -#include "mhd_options.h" -#ifdef HAVE_STDDEF_H -# include <stddef.h> /* for size_t */ -#else /* ! HAVE_STDDEF_H */ -# include <stdlib.h> /* for size_t */ -#endif /* ! HAVE_STDDEF_H */ - -#if defined(MHD_USE_POSIX_THREADS) -# undef HAVE_CONFIG_H -# include <pthread.h> -# define HAVE_CONFIG_H 1 -#elif defined(MHD_USE_W32_THREADS) -# ifndef WIN32_LEAN_AND_MEAN -# define WIN32_LEAN_AND_MEAN 1 -# endif /* !WIN32_LEAN_AND_MEAN */ -# include <windows.h> -#else -# error No threading API is available. -#endif - -#ifndef MHD_NO_THREAD_NAMES -# if defined(MHD_USE_POSIX_THREADS) -# if defined(HAVE_PTHREAD_SETNAME_NP_GNU) || \ - defined(HAVE_PTHREAD_SET_NAME_NP_FREEBSD) || \ - defined(HAVE_PTHREAD_SETNAME_NP_DARWIN) || \ - defined(HAVE_PTHREAD_SETNAME_NP_NETBSD) || \ - defined(HAVE_PTHREAD_ATTR_SETNAME_NP_NETBSD) || \ - defined(HAVE_PTHREAD_ATTR_SETNAME_NP_IBMI) -# define MHD_USE_THREAD_NAME_ -# endif /* HAVE_PTHREAD_SETNAME_NP */ -# elif defined(MHD_USE_W32_THREADS) -# ifdef _MSC_FULL_VER -/* Thread names only available with VC compiler */ -# define MHD_USE_THREAD_NAME_ -# endif /* _MSC_FULL_VER */ -# endif -#endif - -#if defined(MHD_USE_POSIX_THREADS) -typedef pthread_t MHD_thread_handle_; -#elif defined(MHD_USE_W32_THREADS) -typedef HANDLE MHD_thread_handle_; -#endif - -#if defined(MHD_USE_POSIX_THREADS) -# define MHD_THRD_RTRN_TYPE_ void* -# define MHD_THRD_CALL_SPEC_ -#elif defined(MHD_USE_W32_THREADS) -# define MHD_THRD_RTRN_TYPE_ unsigned -# define MHD_THRD_CALL_SPEC_ __stdcall -#endif - -#if defined(MHD_USE_POSIX_THREADS) -typedef pthread_t MHD_thread_ID_; -#elif defined(MHD_USE_W32_THREADS) -typedef DWORD MHD_thread_ID_; -#endif - -/* Depending on implementation, pthread_create() MAY set thread ID into - * provided pointer and after it start thread OR start thread and after - * if set thread ID. In latter case, to avoid data races, additional - * pthread_self() call is required in thread routine. Is some platform - * is known for setting thread ID BEFORE starting thread macro - * MHD_PTHREAD_CREATE__SET_ID_BEFORE_START_THREAD could be defined - * to save some resources. */ -#if defined(MHD_USE_POSIX_THREADS) -# ifdef MHD_PTHREAD_CREATE__SET_ID_BEFORE_START_THREAD -union _MHD_thread_handle_ID_ -{ - MHD_thread_handle_ handle; /**< To be used in other threads */ - MHD_thread_ID_ ID; /**< To be used in thread itself */ -}; -typedef union _MHD_thread_handle_ID_ MHD_thread_handle_ID_; -# else /* ! MHD_PTHREAD_CREATE__SET_ID_BEFORE_START_THREAD */ -struct _MHD_thread_handle_ID_ -{ - MHD_thread_handle_ handle; /**< To be used in other threads */ - MHD_thread_ID_ ID; /**< To be used in thread itself */ -}; -typedef struct _MHD_thread_handle_ID_ MHD_thread_handle_ID_; -# endif /* ! MHD_PTHREAD_CREATE__SET_ID_BEFORE_START_THREAD */ -#elif defined(MHD_USE_W32_THREADS) -struct _MHD_thread_handle_ID_ -{ - MHD_thread_handle_ handle; /**< To be used in other threads */ - MHD_thread_ID_ ID; /**< To be used in thread itself */ -}; -typedef struct _MHD_thread_handle_ID_ MHD_thread_handle_ID_; -#endif - -#if defined(MHD_USE_POSIX_THREADS) -/** - * Wait until specified thread is ended and free thread handle on success. - * @param thread handle to watch - * @return nonzero on success, zero otherwise - */ -#define MHD_join_thread_(thread) (! pthread_join ((thread), NULL)) -#elif defined(MHD_USE_W32_THREADS) -/** - * Wait until specified thread is ended and free thread handle on success. - * @param thread handle to watch - * @return nonzero on success, zero otherwise - */ -#define MHD_join_thread_(thread) (WAIT_OBJECT_0 == WaitForSingleObject ( \ - (thread), INFINITE) ? (CloseHandle ( \ - (thread)), ! 0) : \ - 0) -#endif - -#if defined(MHD_USE_POSIX_THREADS) -/** - * Check whether provided thread ID match current thread. - * @param ID thread ID to match - * @return nonzero on match, zero otherwise - */ -#define MHD_thread_ID_match_current_(ID) (pthread_equal ((ID), pthread_self ())) -#elif defined(MHD_USE_W32_THREADS) -/** - * Check whether provided thread ID match current thread. - * @param ID thread ID to match - * @return nonzero on match, zero otherwise - */ -#define MHD_thread_ID_match_current_(ID) (GetCurrentThreadId () == (ID)) -#endif - -#if defined(MHD_USE_POSIX_THREADS) -# ifdef MHD_PTHREAD_CREATE__SET_ID_BEFORE_START_THREAD -/** - * Initialise thread ID. - * @param thread_handle_ID_ptr pointer to thread handle-ID - */ -#define MHD_thread_init_(thread_handle_ID_ptr) (void) 0 -# else /* ! MHD_PTHREAD_CREATE__SET_ID_BEFORE_START_THREAD */ -/** - * Initialise thread ID. - * @param thread_handle_ID_ptr pointer to thread handle-ID - */ -#define MHD_thread_init_(thread_handle_ID_ptr) ((thread_handle_ID_ptr)->ID = \ - pthread_self ()) -# endif /* ! MHD_PTHREAD_CREATE__SET_ID_BEFORE_START_THREAD */ -#elif defined(MHD_USE_W32_THREADS) -/** - * Initialise thread ID. - * @param thread_handle_ID_ptr pointer to thread handle-ID - */ -#define MHD_thread_init_(thread_handle_ID_ptr) ((thread_handle_ID_ptr)->ID = \ - GetCurrentThreadId ()) -#endif - -/** - * Signature of main function for a thread. - * - * @param cls closure argument for the function - * @return termination code from the thread - */ -typedef MHD_THRD_RTRN_TYPE_ -(MHD_THRD_CALL_SPEC_ *MHD_THREAD_START_ROUTINE_)(void *cls); - - -/** - * Create a thread and set the attributes according to our options. - * - * If thread is created, thread handle must be freed by MHD_join_thread_(). - * - * @param thread handle to initialize - * @param stack_size size of stack for new thread, 0 for default - * @param start_routine main function of thread - * @param arg argument for start_routine - * @return non-zero on success; zero otherwise - */ -int -MHD_create_thread_ (MHD_thread_handle_ID_ *thread, - size_t stack_size, - MHD_THREAD_START_ROUTINE_ start_routine, - void *arg); - -#ifndef MHD_USE_THREAD_NAME_ -#define MHD_create_named_thread_(t,n,s,r,a) MHD_create_thread_ ((t),(s),(r),(a)) -#else /* MHD_USE_THREAD_NAME_ */ -/** - * Create a named thread and set the attributes according to our options. - * - * @param thread handle to initialize - * @param thread_name name for new thread - * @param stack_size size of stack for new thread, 0 for default - * @param start_routine main function of thread - * @param arg argument for start_routine - * @return non-zero on success; zero otherwise - */ -int -MHD_create_named_thread_ (MHD_thread_handle_ID_ *thread, - const char *thread_name, - size_t stack_size, - MHD_THREAD_START_ROUTINE_ start_routine, - void *arg); - -#endif /* MHD_USE_THREAD_NAME_ */ - -#endif /* ! MHD_THREADS_H */ diff --git a/src/lib/panic.c b/src/lib/panic.c @@ -1,61 +0,0 @@ -/* - This file is part of libmicrohttpd - Copyright (C) 2007-2018 Daniel Pittman and Christian Grothoff - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. - - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with this library; if not, write to the Free Software - Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA -*/ - -/** - * @file lib/panic.c - * @brief functions to panic (abort) - * @author Christian Grothoff - */ -#include "internal.h" - - -/** - * Handler for fatal errors. - */ -MHD_PanicCallback mhd_panic = NULL; - -/** - * Closure argument for #mhd_panic. - */ -void *mhd_panic_cls = NULL; - - -/** - * 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 - * case of serious memory corruption or similar problems with the - * architecture. While @a cb is allowed to return and MHD will then - * try to continue, this is never safe. - * - * The default implementation that is used if no panic function is set - * simply prints an error message and calls `abort()`. Alternative - * implementations might call `exit()` or other similar functions. - * - * @param cb new error handler - * @param cls passed to @a cb - * @ingroup logging - */ -void -MHD_set_panic_func (MHD_PanicCallback cb, - void *cls) -{ - mhd_panic = cb; - mhd_panic_cls = cls; -} diff --git a/src/lib/reason_phrase.c b/src/lib/reason_phrase.c @@ -1,182 +0,0 @@ -/* - This file is part of libmicrohttpd - Copyright (C) 2007, 2011, 2017 Christian Grothoff, Karlson2k (Evgeny Grin) - - 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 reason_phrase.c - * @brief Tables of the string response phrases - * @author Elliot Glaysher - * @author Christian Grothoff (minor code clean up) - * @author Karlson2k (Evgeny Grin) - */ -#include "internal.h" - -#ifndef NULL -#define NULL ((void*) 0) -#endif - -static const char *const invalid_hundred[] = { - NULL -}; - -static const char *const one_hundred[] = { - "Continue", - "Switching Protocols", - "Processing" -}; - -static const char *const two_hundred[] = { - "OK", - "Created", - "Accepted", - "Non-Authoritative Information", - "No Content", - "Reset Content", - "Partial Content", - "Multi-Status", - "Already Reported", - "Unknown", - "Unknown", /* 210 */ - "Unknown", - "Unknown", - "Unknown", - "Unknown", - "Unknown", /* 215 */ - "Unknown", - "Unknown", - "Unknown", - "Unknown", - "Unknown", /* 220 */ - "Unknown", - "Unknown", - "Unknown", - "Unknown", - "Unknown", /* 225 */ - "IM Used" -}; - -static const char *const three_hundred[] = { - "Multiple Choices", - "Moved Permanently", - "Found", - "See Other", - "Not Modified", - "Use Proxy", - "Switch Proxy", - "Temporary Redirect", - "Permanent Redirect" -}; - -static const char *const four_hundred[] = { - "Bad Request", - "Unauthorized", - "Payment Required", - "Forbidden", - "Not Found", - "Method Not Allowed", - "Not Acceptable", - "Proxy Authentication Required", - "Request Timeout", - "Conflict", - "Gone", - "Length Required", - "Precondition Failed", - "Payload Too Large", - "URI Too Long", - "Unsupported Media Type", - "Range Not Satisfiable", - "Expectation Failed", - "Unknown", - "Unknown", - "Unknown", /* 420 */ - "Misdirected Request", - "Unprocessable Entity", - "Locked", - "Failed Dependency", - "Unordered Collection", - "Upgrade Required", - "Unknown", - "Precondition Required", - "Too Many Requests", - "Unknown", /* 430 */ - "Request Header Fields Too Large", - "Unknown", - "Unknown", - "Unknown", - "Unknown", /* 435 */ - "Unknown", - "Unknown", - "Unknown", - "Unknown", - "Unknown", /* 440 */ - "Unknown", - "Unknown", - "Unknown", - "No Response", - "Unknown", /* 445 */ - "Unknown", - "Unknown", - "Unknown", - "Retry With", - "Blocked by Windows Parental Controls", /* 450 */ - "Unavailable For Legal Reasons" -}; - -static const char *const five_hundred[] = { - "Internal Server Error", - "Not Implemented", - "Bad Gateway", - "Service Unavailable", - "Gateway Timeout", - "HTTP Version Not Supported", - "Variant Also Negotiates", - "Insufficient Storage", - "Loop Detected", - "Bandwidth Limit Exceeded", - "Not Extended", - "Network Authentication Required" -}; - - -struct MHD_Reason_Block -{ - size_t max; - const char *const *data; -}; - -#define BLOCK(m) { (sizeof(m) / sizeof(char*)), m } - -static const struct MHD_Reason_Block reasons[] = { - BLOCK (invalid_hundred), - BLOCK (one_hundred), - BLOCK (two_hundred), - BLOCK (three_hundred), - BLOCK (four_hundred), - BLOCK (five_hundred), -}; - - -const char * -MHD_get_reason_phrase_for (enum MHD_HTTP_StatusCode code) -{ - if ( (code >= 100) && - (code < 600) && - (reasons[code / 100].max > (code % 100)) ) - return reasons[code / 100].data[code % 100]; - return "Unknown"; -} diff --git a/src/lib/request.c b/src/lib/request.c @@ -1,160 +0,0 @@ -/* - This file is part of libmicrohttpd - Copyright (C) 2007-2018 Daniel Pittman and Christian Grothoff - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. - - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with this library; if not, write to the Free Software - Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - -*/ -/** - * @file requests.c - * @brief Methods for managing HTTP requests - * @author Daniel Pittman - * @author Christian Grothoff - * @author Karlson2k (Evgeny Grin) - */ -#include "internal.h" - - -/** - * Get all of the headers from the request. - * - * @param request request to get values from - * @param kind types of values to iterate over, can be a bitmask - * @param iterator callback to call on each header; - * maybe NULL (then just count headers) - * @param iterator_cls extra argument to @a iterator - * @return number of entries iterated over - * @ingroup request - */ -unsigned int -MHD_request_get_values (struct MHD_Request *request, - enum MHD_ValueKind kind, - MHD_KeyValueIterator iterator, - void *iterator_cls) -{ - int ret; - struct MHD_HTTP_Header *pos; - - ret = 0; - for (pos = request->headers_received; - NULL != pos; - pos = pos->next) - { - if (0 != (pos->kind & kind)) - { - ret++; - if ( (NULL != iterator) && - (MHD_YES != iterator (iterator_cls, - pos->kind, - pos->header, - pos->value)) ) - return ret; - } - } - return ret; -} - - -/** - * This function can be used to add an entry to the HTTP headers of a - * request (so that the #MHD_request_get_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 - * request callbacks (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 request the request 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 - */ -enum MHD_Bool -MHD_request_set_value (struct MHD_Request *request, - enum MHD_ValueKind kind, - const char *key, - const char *value) -{ - struct MHD_HTTP_Header *pos; - - pos = MHD_pool_allocate (request->connection->pool, - sizeof (struct MHD_HTTP_Header), - MHD_YES); - if (NULL == pos) - return MHD_NO; - pos->header = (char *) key; - pos->value = (char *) value; - pos->kind = kind; - pos->next = NULL; - /* append 'pos' to the linked list of headers */ - if (NULL == request->headers_received_tail) - { - request->headers_received = pos; - request->headers_received_tail = pos; - } - else - { - request->headers_received_tail->next = pos; - request->headers_received_tail = pos; - } - return MHD_YES; -} - - -/** - * Get a particular header value. If multiple - * values match the kind, return any one of them. - * - * @param request request to get values from - * @param kind what kind of value are we looking for - * @param key the header to look for, NULL to lookup 'trailing' value without a key - * @return NULL if no such item was found - * @ingroup request - */ -const char * -MHD_request_lookup_value (struct MHD_Request *request, - enum MHD_ValueKind kind, - const char *key) -{ - struct MHD_HTTP_Header *pos; - - for (pos = request->headers_received; - NULL != pos; - pos = pos->next) - { - if ((0 != (pos->kind & kind)) && - ( (key == pos->header) || - ( (NULL != pos->header) && - (NULL != key) && - (MHD_str_equal_caseless_ (key, - pos->header))))) - return pos->value; - } - return NULL; -} - - -/* end of request.c */ diff --git a/src/lib/request_info.c b/src/lib/request_info.c @@ -1,86 +0,0 @@ -/* - This file is part of libmicrohttpd - Copyright (C) 2007-2018 Daniel Pittman and Christian Grothoff - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. - - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with this library; if not, write to the Free Software - Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA -*/ - -/** - * @file lib/request_info.c - * @brief implementation of MHD_request_get_information_sz() - * @author Christian Grothoff - */ -#include "internal.h" - - -/** - * Obtain information about the given request. - * Use wrapper macro #MHD_request_get_information() instead of direct use - * of this function. - * - * @param request what request to get information about - * @param info_type what information is desired? - * @param[out] return_value pointer to union where requested information will - * be stored - * @param return_value_size size of union MHD_RequestInformation at compile - * time - * @return #MHD_YES on success, #MHD_NO on error - * (@a info_type is unknown, NULL pointer etc.) - * @ingroup specialized - */ -enum MHD_Bool -MHD_request_get_information_sz (struct MHD_Request *request, - enum MHD_RequestInformationType info_type, - union MHD_RequestInformation *return_value, - size_t return_value_size) -{ -#define CHECK_SIZE(type) if (sizeof(type) < return_value_size) \ - return MHD_NO - - switch (info_type) - { - case MHD_REQUEST_INFORMATION_CONNECTION: - CHECK_SIZE (struct MHD_Connection *); - return_value->connection = request->connection; - return MHD_YES; - case MHD_REQUEST_INFORMATION_CLIENT_CONTEXT: - CHECK_SIZE (void **); - return_value->request_context = &request->client_context; - return MHD_YES; - case MHD_REQUEST_INFORMATION_HTTP_VERSION: - CHECK_SIZE (const char *); - return_value->http_version = request->version_s; - return MHD_YES; - case MHD_REQUEST_INFORMATION_HTTP_METHOD: - CHECK_SIZE (const char *); - return_value->http_method = request->method_s; - return MHD_YES; - case MHD_REQUEST_INFORMATION_HEADER_SIZE: - CHECK_SIZE (size_t); - if ( (MHD_REQUEST_HEADERS_RECEIVED > request->state) || - (MHD_REQUEST_CLOSED == request->state) ) - return MHD_NO; /* invalid, too early! */ - return_value->header_size = request->header_size; - return MHD_YES; - - default: - return MHD_NO; - } - -#undef CHECK_SIZE -} - - -/* end of request_info.c */ diff --git a/src/lib/request_resume.c b/src/lib/request_resume.c @@ -1,201 +0,0 @@ -/* - This file is part of libmicrohttpd - Copyright (C) 2007-2018 Daniel Pittman and Christian Grothoff - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. - - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with this library; if not, write to the Free Software - Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA -*/ - -/** - * @file lib/request_resume.c - * @brief implementation of MHD_request_resume() - * @author Christian Grothoff - */ -#include "internal.h" -#include "connection_close.h" - -/** - * Resume handling of network data for suspended request. It is - * safe to resume a suspended request at any time. Calling this - * function on a request that was not previously suspended will - * result in undefined behavior. - * - * If you are using this function in ``external'' select mode, you must - * make sure to run #MHD_run() afterwards (before again calling - * #MHD_get_fdset(), as otherwise the change may not be reflected in - * the set returned by #MHD_get_fdset() and you may end up with a - * request that is stuck until the next network activity. - * - * @param request the request to resume - */ -void -MHD_request_resume (struct MHD_Request *request) -{ - struct MHD_Daemon *daemon = request->daemon; - - if (daemon->disallow_suspend_resume) - MHD_PANIC (_ ( - "Cannot resume connections without enabling MHD_ALLOW_SUSPEND_RESUME!\n")); - MHD_mutex_lock_chk_ (&daemon->cleanup_connection_mutex); - request->connection->resuming = true; - daemon->resuming = true; - MHD_mutex_unlock_chk_ (&daemon->cleanup_connection_mutex); - if ( (MHD_ITC_IS_VALID_ (daemon->itc)) && - (! MHD_itc_activate_ (daemon->itc, - "r")) ) - { -#ifdef HAVE_MESSAGES - MHD_DLOG (daemon, - MHD_SC_ITC_USE_FAILED, - _ ( - "Failed to signal resume via inter-thread communication channel.\n")); -#endif - } -} - - -/** - * Run through the suspended connections and move any that are no - * longer suspended back to the active state. - * - * @remark To be called only from thread that process - * daemon's select()/poll()/etc. - * - * @param daemon daemon context - * @return true if a connection was actually resumed - */ -bool -MHD_resume_suspended_connections_ (struct MHD_Daemon *daemon) -/* FIXME: rename connections -> requests? */ -{ - struct MHD_Connection *pos; - struct MHD_Connection *prev = NULL; - bool ret; - const bool used_thr_p_c = (MHD_TM_THREAD_PER_CONNECTION == - daemon->threading_mode); - - mhd_assert (NULL == daemon->worker_pool); - ret = false; - MHD_mutex_lock_chk_ (&daemon->cleanup_connection_mutex); - - if (daemon->resuming) - { - prev = daemon->suspended_connections_tail; - /* During shutdown check for resuming is forced. */ - mhd_assert ((NULL != prev) || (daemon->shutdown)); - } - - daemon->resuming = false; - - while (NULL != (pos = prev)) - { -#ifdef UPGRADE_SUPPORT - struct MHD_UpgradeResponseHandle *const urh = pos->request.urh; -#else /* ! UPGRADE_SUPPORT */ - static const void *const urh = NULL; -#endif /* ! UPGRADE_SUPPORT */ - prev = pos->prev; - if ( (! pos->resuming) -#ifdef UPGRADE_SUPPORT - || ( (NULL != urh) && - ( (! urh->was_closed) || - (! urh->clean_ready) ) ) -#endif /* UPGRADE_SUPPORT */ - ) - continue; - ret = true; - mhd_assert (pos->suspended); - DLL_remove (daemon->suspended_connections_head, - daemon->suspended_connections_tail, - pos); - pos->suspended = false; - if (NULL == urh) - { - DLL_insert (daemon->connections_head, - daemon->connections_tail, - pos); - if (! used_thr_p_c) - { - /* Reset timeout timer on resume. */ - if (0 != pos->connection_timeout) - pos->last_activity = MHD_monotonic_sec_counter (); - - if (pos->connection_timeout == daemon->connection_default_timeout) - XDLL_insert (daemon->normal_timeout_head, - daemon->normal_timeout_tail, - pos); - else - XDLL_insert (daemon->manual_timeout_head, - daemon->manual_timeout_tail, - pos); - } -#ifdef EPOLL_SUPPORT - if (MHD_ELS_EPOLL == daemon->event_loop_syscall) - { - if (0 != (pos->epoll_state & MHD_EPOLL_STATE_IN_EREADY_EDLL)) - MHD_PANIC ("Resumed connection was already in EREADY set.\n"); - /* we always mark resumed connections as ready, as we - might have missed the edge poll event during suspension */ - EDLL_insert (daemon->eready_head, - daemon->eready_tail, - pos); - pos->epoll_state |= MHD_EPOLL_STATE_IN_EREADY_EDLL \ - | MHD_EPOLL_STATE_READ_READY - | MHD_EPOLL_STATE_WRITE_READY; - pos->epoll_state &= ~MHD_EPOLL_STATE_SUSPENDED; - } -#endif - } -#ifdef UPGRADE_SUPPORT - else - { - struct MHD_Response *response = pos->request.response; - - /* Data forwarding was finished (for TLS connections) AND - * application was closed upgraded connection. - * Insert connection into cleanup list. */ - if ( (NULL != response) && - (MHD_TM_THREAD_PER_CONNECTION != daemon->threading_mode) && - (NULL != response->termination_cb) ) - response->termination_cb (response->termination_cb_cls, - MHD_REQUEST_TERMINATED_COMPLETED_OK, - &pos->request.client_context); - DLL_insert (daemon->cleanup_head, - daemon->cleanup_tail, - pos); - - } -#endif /* UPGRADE_SUPPORT */ - pos->resuming = false; - } - MHD_mutex_unlock_chk_ (&daemon->cleanup_connection_mutex); - if ( (used_thr_p_c) && - (ret) ) - { /* Wake up suspended connections. */ - if (! MHD_itc_activate_ (daemon->itc, - "w")) - { -#ifdef HAVE_MESSAGES - MHD_DLOG (daemon, - MHD_SC_ITC_USE_FAILED, - _ ( - "Failed to signal resume of connection via inter-thread communication channel.\n")); -#endif - } - } - return ret; -} - - -/* end of request_resume.c */ diff --git a/src/lib/request_resume.h b/src/lib/request_resume.h @@ -1,43 +0,0 @@ -/* - This file is part of libmicrohttpd - Copyright (C) 2007-2018 Daniel Pittman and Christian Grothoff - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. - - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with this library; if not, write to the Free Software - Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA -*/ - -/** - * @file lib/request_resume.h - * @brief implementation of MHD_request_resume() - * @author Christian Grothoff - */ - - -#ifndef REQUEST_RESUME_H -#define REQUEST_RESUME_H - -/** - * Run through the suspended connections and move any that are no - * longer suspended back to the active state. - * @remark To be called only from thread that process - * daemon's select()/poll()/etc. - * - * @param daemon daemon context - * @return true if a connection was actually resumed - */ -bool -MHD_resume_suspended_connections_ (struct MHD_Daemon *daemon) -MHD_NONNULL (1); - -#endif diff --git a/src/lib/response.c b/src/lib/response.c @@ -1,260 +0,0 @@ -/* - This file is part of libmicrohttpd - Copyright (C) 2007-2018 Daniel Pittman and Christian Grothoff - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. - - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with this library; if not, write to the Free Software - Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA -*/ - -/** - * @file lib/response.c - * @brief implementation of general response functions - * @author Daniel Pittman - * @author Christian Grothoff - * @author Karlson2k (Evgeny Grin) - */ -#include "internal.h" - - -/** - * Add a header or footer line to the response. - * - * @param response response to add a header to - * @param kind header or footer - * @param header the header to add - * @param content value to add - * @return false on error (i.e. invalid header or content format). - */ -static bool -add_response_entry (struct MHD_Response *response, - enum MHD_ValueKind kind, - const char *header, - const char *content) -{ - struct MHD_HTTP_Header *hdr; - - if ( (NULL == header) || - (NULL == content) || - (0 == header[0]) || - (0 == content[0]) || - (NULL != strchr (header, '\t')) || - (NULL != strchr (header, '\r')) || - (NULL != strchr (header, '\n')) || - (NULL != strchr (content, '\t')) || - (NULL != strchr (content, '\r')) || - (NULL != strchr (content, '\n')) ) - return false; - if (NULL == (hdr = malloc (sizeof (struct MHD_HTTP_Header)))) - return false; - if (NULL == (hdr->header = strdup (header))) - { - free (hdr); - return false; - } - if (NULL == (hdr->value = strdup (content))) - { - free (hdr->header); - free (hdr); - return false; - } - hdr->kind = kind; - hdr->next = response->first_header; - response->first_header = hdr; - return true; -} - - -/** - * Explicitly decrease reference counter of a response object. If the - * counter hits zero, destroys a response object and associated - * resources. Usually, this is implicitly done by converting a - * response to an action and returning the action to MHD. - * - * @param response response to decrement RC of - * @ingroup response - */ -void -MHD_response_queue_for_destroy (struct MHD_Response *response) -{ - struct MHD_HTTP_Header *pos; - - MHD_mutex_lock_chk_ (&response->mutex); - if (0 != --(response->reference_count)) - { - MHD_mutex_unlock_chk_ (&response->mutex); - return; - } - MHD_mutex_unlock_chk_ (&response->mutex); - MHD_mutex_destroy_chk_ (&response->mutex); - if (NULL != response->crfc) - response->crfc (response->crc_cls); - while (NULL != response->first_header) - { - pos = response->first_header; - response->first_header = pos->next; - free (pos->header); - free (pos->value); - free (pos); - } - free (response); -} - - -/** - * Add a header line to the response. - * - * @param response response to add a header to - * @param header the header to add - * @param content value to add - * @return #MHD_NO on error (i.e. invalid header or content format), - * or out of memory - * @ingroup response - */ -enum MHD_Bool -MHD_response_add_header (struct MHD_Response *response, - const char *header, - const char *content) -{ - return add_response_entry (response, - MHD_HEADER_KIND, - header, - content) ? MHD_YES : MHD_NO; -} - - -/** - * Add a tailer line to the response. - * - * @param response response to add a footer to - * @param footer the footer to add - * @param content value to add - * @return #MHD_NO on error (i.e. invalid footer or content format), - * or out of memory - * @ingroup response - */ -enum MHD_Bool -MHD_response_add_trailer (struct MHD_Response *response, - const char *footer, - const char *content) -{ - return add_response_entry (response, - MHD_FOOTER_KIND, - footer, - content) ? MHD_YES : MHD_NO; -} - - -/** - * Delete a header (or footer) line from the response. - * - * @param response response to remove a header from - * @param header the header to delete - * @param content value to delete - * @return #MHD_NO on error (no such header known) - * @ingroup response - */ -enum MHD_Bool -MHD_response_del_header (struct MHD_Response *response, - const char *header, - const char *content) -{ - struct MHD_HTTP_Header *pos; - struct MHD_HTTP_Header *prev; - - prev = NULL; - pos = response->first_header; - while (NULL != pos) - { - if ((0 == strcmp (header, - pos->header)) && - (0 == strcmp (content, - pos->value))) - { - free (pos->header); - free (pos->value); - if (NULL == prev) - response->first_header = pos->next; - else - prev->next = pos->next; - free (pos); - return MHD_YES; - } - prev = pos; - pos = pos->next; - } - return MHD_NO; -} - - -/** - * Get all of the headers (and footers) added to a response. - * - * @param response response to query - * @param iterator callback to call on each header; - * maybe NULL (then just count headers) - * @param iterator_cls extra argument to @a iterator - * @return number of entries iterated over - * @ingroup response - */ -unsigned int -MHD_response_get_headers (struct MHD_Response *response, - MHD_KeyValueIterator iterator, - void *iterator_cls) -{ - unsigned int numHeaders = 0; - struct MHD_HTTP_Header *pos; - - for (pos = response->first_header; - NULL != pos; - pos = pos->next) - { - numHeaders++; - if ( (NULL != iterator) && - (MHD_YES != iterator (iterator_cls, - pos->kind, - pos->header, - pos->value)) ) - break; - } - return numHeaders; -} - - -/** - * Get a particular header (or footer) from the response. - * - * @param response response to query - * @param key which header to get - * @return NULL if header does not exist - * @ingroup response - */ -const char * -MHD_response_get_header (struct MHD_Response *response, - const char *key) -{ - struct MHD_HTTP_Header *pos; - - for (pos = response->first_header; - NULL != pos; - pos = pos->next) - { - if (MHD_str_equal_caseless_ (pos->header, - key)) - return pos->value; - } - return NULL; -} - - -/* end of response.c */ diff --git a/src/lib/response_for_upgrade.c b/src/lib/response_for_upgrade.c @@ -1,95 +0,0 @@ -/* - This file is part of libmicrohttpd - Copyright (C) 2007-2018 Daniel Pittman and Christian Grothoff - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. - - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with this library; if not, write to the Free Software - Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA -*/ - -/** - * @file lib/response_for_upgrade.c - * @brief implementation of MHD_response_for_upgrade() - * @author Christian Grothoff - */ -#include "internal.h" - - -/** - * Create a response object that can be used for 101 UPGRADE - * responses, for example to implement WebSockets. After sending the - * response, control over the data stream is given to the callback (which - * can then, for example, start some bi-directional communication). - * If the response is queued for multiple connections, the callback - * will be called for each connection. The callback - * will ONLY be called after the response header was successfully passed - * to the OS; if there are communication errors before, the usual MHD - * connection error handling code will be performed. - * - * MHD will automatically set the correct HTTP status - * code (#MHD_HTTP_SWITCHING_PROTOCOLS). - * Setting correct HTTP headers for the upgrade must be done - * manually (this way, it is possible to implement most existing - * WebSocket versions using this API; in fact, this API might be useful - * for any protocol switch, not just WebSockets). Note that - * draft-ietf-hybi-thewebsocketprotocol-00 cannot be implemented this - * way as the header "HTTP/1.1 101 WebSocket Protocol Handshake" - * cannot be generated; instead, MHD will always produce "HTTP/1.1 101 - * Switching Protocols" (if the response code 101 is used). - * - * As usual, the response object can be extended with header - * information and then be used any number of times (as long as the - * header information is not connection-specific). - * - * @param upgrade_handler function to call with the "upgraded" socket - * @param upgrade_handler_cls closure for @a upgrade_handler - * @return NULL on error (i.e. invalid arguments, out of memory) - */ -struct MHD_Response * -MHD_response_for_upgrade (MHD_UpgradeHandler upgrade_handler, - void *upgrade_handler_cls) -{ -#ifdef UPGRADE_SUPPORT - struct MHD_Response *response; - - mhd_assert (NULL != upgrade_handler); - response = MHD_calloc_ (1, - sizeof (struct MHD_Response)); - if (NULL == response) - return NULL; - if (! MHD_mutex_init_ (&response->mutex)) - { - free (response); - return NULL; - } - response->upgrade_handler = upgrade_handler; - response->upgrade_handler_cls = upgrade_handler_cls; - response->status_code = MHD_HTTP_SWITCHING_PROTOCOLS; - response->total_size = MHD_SIZE_UNKNOWN; - response->reference_count = 1; - if (MHD_NO == - MHD_response_add_header (response, - MHD_HTTP_HEADER_CONNECTION, - "Upgrade")) - { - MHD_response_queue_for_destroy (response); - return NULL; - } - return response; -#else - return NULL; -#endif -} - - -/* end of response_for_upgrade.c */ diff --git a/src/lib/response_from_buffer.c b/src/lib/response_from_buffer.c @@ -1,89 +0,0 @@ -/* - This file is part of libmicrohttpd - Copyright (C) 2007-2018 Daniel Pittman and Christian Grothoff - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. - - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with this library; if not, write to the Free Software - Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA -*/ - -/** - * @file lib/response_from_buffer.c - * @brief implementation of MHD_response_from_buffer() - * @author Christian Grothoff - */ -#include "internal.h" - - -/** - * Create a response object. The response object can be extended with - * header information and then be used any number of times. - * - * @param sc status code to use for the response; - * #MHD_HTTP_NO_CONTENT is only valid if @a size is 0; - * @param size size of the data portion of the response - * @param buffer size bytes containing the response's data portion - * @param mode flags for buffer management - * @return NULL on error (i.e. invalid arguments, out of memory) - * @ingroup response - */ -struct MHD_Response * -MHD_response_from_buffer (enum MHD_HTTP_StatusCode sc, - size_t size, - void *buffer, - enum MHD_ResponseMemoryMode mode) -{ - struct MHD_Response *response; - void *tmp; - - mhd_assert ( (NULL != buffer) || - (0 == size) ); - if (NULL == - (response = MHD_calloc_ (1, - sizeof (struct MHD_Response)))) - return NULL; - response->fd = -1; - if (! MHD_mutex_init_ (&response->mutex)) - { - free (response); - return NULL; - } - if ( (MHD_RESPMEM_MUST_COPY == mode) && - (size > 0) ) - { - if (NULL == (tmp = malloc (size))) - { - MHD_mutex_destroy_chk_ (&response->mutex); - free (response); - return NULL; - } - memcpy (tmp, - buffer, - size); - buffer = tmp; - } - if (MHD_RESPMEM_PERSISTENT != mode) - { - response->crfc = &free; - response->crc_cls = buffer; - } - response->status_code = sc; - response->reference_count = 1; - response->total_size = size; - response->data = buffer; - response->data_size = size; - return response; -} - - -/* end of response_from_buffer.c */ diff --git a/src/lib/response_from_callback.c b/src/lib/response_from_callback.c @@ -1,80 +0,0 @@ -/* - This file is part of libmicrohttpd - Copyright (C) 2007-2018 Daniel Pittman and Christian Grothoff - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. - - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with this library; if not, write to the Free Software - Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA -*/ - -/** - * @file lib/response_from_callback.c - * @brief implementation of MHD_response_from_callback() - * @author Christian Grothoff - */ -#include "internal.h" - - -/** - * Create a response action. The response object can be extended with - * header information and then be used any number of times. - * - * @param sc status code to return - * @param size size of the data portion of the response, #MHD_SIZE_UNKNOWN for unknown - * @param block_size preferred block size for querying crc (advisory only, - * MHD may still call @a crc using smaller chunks); this - * is essentially the buffer size used for IO, clients - * should pick a value that is appropriate for IO and - * memory performance requirements - * @param crc callback to use to obtain response data - * @param crc_cls extra argument to @a crc - * @param crfc callback to call to free @a crc_cls resources - * @return NULL on error (i.e. invalid arguments, out of memory) - * @ingroup response - */ -struct MHD_Response * -MHD_response_from_callback (enum MHD_HTTP_StatusCode sc, - uint64_t size, - size_t block_size, - MHD_ContentReaderCallback crc, - void *crc_cls, - MHD_ContentReaderFreeCallback crfc) -{ - struct MHD_Response *response; - - mhd_assert (NULL != crc); - mhd_assert (0 != block_size); - if (NULL == - (response = MHD_calloc_ (1, - sizeof (struct MHD_Response) - + block_size))) - return NULL; - response->fd = -1; - response->status_code = sc; - response->data = (void *) &response[1]; - response->data_buffer_size = block_size; - if (! MHD_mutex_init_ (&response->mutex)) - { - free (response); - return NULL; - } - response->crc = crc; - response->crfc = crfc; - response->crc_cls = crc_cls; - response->reference_count = 1; - response->total_size = size; - return response; -} - - -/* end of response_from_callback.c */ diff --git a/src/lib/response_from_fd.c b/src/lib/response_from_fd.c @@ -1,211 +0,0 @@ -/* - This file is part of libmicrohttpd - Copyright (C) 2007-2019 Daniel Pittman, Christian Grothoff and - Karlson2k (Evgeny Grin) - - 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 lib/response_from_fd.c - * @brief implementation of MHD_response_from_fd() - * @author Daniel Pittman - * @author Christian Grothoff - * @author Karlson2k (Evgeny Grin) - */ -#include "internal.h" - - -/** - * Size of single file read operation for - * file-backed responses. - */ -#ifndef MHD_FILE_READ_BLOCK_SIZE -#ifdef _WIN32 -#define MHD_FILE_READ_BLOCK_SIZE 16384 /* 16k */ -#else /* _WIN32 */ -#define MHD_FILE_READ_BLOCK_SIZE 4096 /* 4k */ -#endif /* _WIN32 */ -#endif /* !MHD_FD_BLOCK_SIZE */ - -/** - * Given a file descriptor, read data from the file - * to generate the response. - * - * @param cls pointer to the response - * @param pos offset in the file to access - * @param buf where to write the data - * @param max number of bytes to write at most - * @return number of bytes written - */ -static ssize_t -file_reader (void *cls, - uint64_t pos, - char *buf, - size_t max) -{ - struct MHD_Response *response = cls; -#if ! defined(_WIN32) || defined(__CYGWIN__) - ssize_t n; -#else /* _WIN32 && !__CYGWIN__ */ - const HANDLE fh = (HANDLE) _get_osfhandle (response->fd); -#endif /* _WIN32 && !__CYGWIN__ */ - const int64_t offset64 = (int64_t) (pos + response->fd_off); - - if (offset64 < 0) - return MHD_CONTENT_READER_END_WITH_ERROR; /* seek to required position is not possible */ - -#if ! defined(_WIN32) || defined(__CYGWIN__) - if (max > SSIZE_MAX) - max = SSIZE_MAX; /* Clamp to maximum return value. */ - -#if defined(HAVE_PREAD64) - n = pread64 (response->fd, - buf, - max, - offset64); -#elif defined(HAVE_PREAD) - if ( (sizeof(off_t) < sizeof (uint64_t)) && - (offset64 > (uint64_t) INT32_MAX) ) - return MHD_CONTENT_READER_END_WITH_ERROR; /* Read at required position is not possible. */ - - n = pread (response->fd, - buf, - max, - (off_t) offset64); -#else /* ! HAVE_PREAD */ -#if defined(HAVE_LSEEK64) - if (lseek64 (response->fd, - offset64, - SEEK_SET) != offset64) - return MHD_CONTENT_READER_END_WITH_ERROR; /* can't seek to required position */ -#else /* ! HAVE_LSEEK64 */ - if ( (sizeof(off_t) < sizeof (uint64_t)) && - (offset64 > (uint64_t) INT32_MAX) ) - return MHD_CONTENT_READER_END_WITH_ERROR; /* seek to required position is not possible */ - - if (lseek (response->fd, - (off_t) offset64, - SEEK_SET) != (off_t) offset64) - return MHD_CONTENT_READER_END_WITH_ERROR; /* can't seek to required position */ -#endif /* ! HAVE_LSEEK64 */ - n = read (response->fd, - buf, - max); - -#endif /* ! HAVE_PREAD */ - if (0 == n) - return MHD_CONTENT_READER_END_OF_STREAM; - if (n < 0) - return MHD_CONTENT_READER_END_WITH_ERROR; - return n; -#else /* _WIN32 && !__CYGWIN__ */ - if (INVALID_HANDLE_VALUE == fh) - return MHD_CONTENT_READER_END_WITH_ERROR; /* Value of 'response->fd' is not valid. */ - else - { - OVERLAPPED f_ol = {0, 0, {{0, 0}}, 0}; /* Initialize to zero. */ - ULARGE_INTEGER pos_uli; - DWORD toRead = (max > INT32_MAX) ? INT32_MAX : (DWORD) max; - DWORD resRead; - - pos_uli.QuadPart = (uint64_t) offset64; /* Simple transformation 64bit -> 2x32bit. */ - f_ol.Offset = pos_uli.LowPart; - f_ol.OffsetHigh = pos_uli.HighPart; - if (! ReadFile (fh, - (void *) buf, - toRead, - &resRead, - &f_ol)) - return MHD_CONTENT_READER_END_WITH_ERROR; /* Read error. */ - if (0 == resRead) - return MHD_CONTENT_READER_END_OF_STREAM; - return (ssize_t) resRead; - } -#endif /* _WIN32 && !__CYGWIN__ */ -} - - -/** - * Destroy file reader context. Closes the file - * descriptor. - * - * @param cls pointer to file descriptor - */ -static void -free_callback (void *cls) -{ - struct MHD_Response *response = cls; - - (void) close (response->fd); - response->fd = -1; -} - - -/** - * Create a response object based on an @a fd from which - * data is read. The response object can be extended with - * header information and then be used any number of times. - * - * @param sc status code to return - * @param fd file descriptor referring to a file on disk with the - * data; will be closed when response is destroyed; - * fd should be in 'blocking' mode - * @param offset offset to start reading from in the file; - * reading file beyond 2 GiB may be not supported by OS or - * MHD build; see ::MHD_FEATURE_LARGE_FILE - * @param size size of the data portion of the response; - * sizes larger than 2 GiB may be not supported by OS or - * MHD build; see ::MHD_FEATURE_LARGE_FILE - * @return NULL on error (i.e. invalid arguments, out of memory) - * @ingroup response - */ -struct MHD_Response * -MHD_response_from_fd (enum MHD_HTTP_StatusCode sc, - int fd, - uint64_t offset, - uint64_t size) -{ - struct MHD_Response *response; - - mhd_assert (-1 != fd); -#if ! defined(HAVE___LSEEKI64) && ! defined(HAVE_LSEEK64) - if ( (sizeof (uint64_t) > sizeof (off_t)) && - ( (size > (uint64_t) INT32_MAX) || - (offset > (uint64_t) INT32_MAX) || - ((size + offset) >= (uint64_t) INT32_MAX) ) ) - return NULL; -#endif - if ( ((int64_t) size < 0) || - ((int64_t) offset < 0) || - ((int64_t) (size + offset) < 0) ) - return NULL; - - response = MHD_response_from_callback (sc, - size, - MHD_FILE_READ_BLOCK_SIZE, - &file_reader, - NULL, - &free_callback); - if (NULL == response) - return NULL; - response->fd = fd; - response->fd_off = offset; - response->crc_cls = response; - return response; -} - - -/* end of response_from_fd.c */ diff --git a/src/lib/response_options.c b/src/lib/response_options.c @@ -1,62 +0,0 @@ -/* - This file is part of libmicrohttpd - Copyright (C) 2007-2018 Daniel Pittman and Christian Grothoff - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. - - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with this library; if not, write to the Free Software - Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA -*/ - -/** - * @file lib/response_option.c - * @brief implementation of response options - * @author Christian Grothoff - */ -#include "internal.h" - - -/** - * Only respond in conservative HTTP 1.0-mode. In - * particular, do not (automatically) sent "Connection" headers and - * always close the connection after generating the response. - * - * @param request the request for which we force HTTP 1.0 to be used - */ -void -MHD_response_option_v10_only (struct MHD_Response *response) -{ - response->v10_only = true; -} - - -/** - * Set a function to be called once MHD is finished with the - * request. - * - * @param response which response to set the callback for - * @param termination_cb function to call - * @param termination_cb_cls closure for @e termination_cb - */ -void -MHD_response_option_termination_callback (struct MHD_Response *response, - MHD_RequestTerminationCallback - termination_cb, - void *termination_cb_cls) -{ - /* Q: should we assert termination_cb non-NULL? */ - response->termination_cb = termination_cb; - response->termination_cb_cls = termination_cb_cls; -} - - -/* end of response_option.c */ diff --git a/src/lib/sysfdsetsize.c b/src/lib/sysfdsetsize.c @@ -1,80 +0,0 @@ -/* - This file is part of libmicrohttpd - Copyright (C) 2015 Karlson2k (Evgeny Grin) - - 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 microhttpd/sysfdsetsize.c - * @brief Helper for obtaining FD_SETSIZE system default value - * @author Karlson2k (Evgeny Grin) - */ - - -#include "MHD_config.h" - -#ifdef FD_SETSIZE -/* FD_SETSIZE was defined before system headers. */ -/* To get system value of FD_SETSIZE, undefine FD_SETSIZE - here. */ -#undef FD_SETSIZE -#endif /* FD_SETSIZE */ - -#include <stdlib.h> -#if defined(__VXWORKS__) || defined(__vxworks) || defined(OS_VXWORKS) -#include <sockLib.h> -#endif /* OS_VXWORKS */ -#ifdef HAVE_SYS_SELECT_H -#include <sys/select.h> -#endif /* HAVE_SYS_SELECT_H */ -#ifdef HAVE_SYS_TYPES_H -#include <sys/types.h> -#endif /* HAVE_SYS_TYPES_H */ -#ifdef HAVE_SYS_TIME_H -#include <sys/time.h> -#endif /* HAVE_SYS_TIME_H */ -#ifdef HAVE_TIME_H -#include <time.h> -#endif /* HAVE_TIME_H */ -#ifdef HAVE_UNISTD_H -#include <unistd.h> -#endif /* HAVE_UNISTD_H */ -#ifdef HAVE_SYS_SOCKET_H -#include <sys/socket.h> -#endif /* HAVE_SYS_SOCKET_H */ - -#if defined(_WIN32) && ! defined(__CYGWIN__) -#ifndef WIN32_LEAN_AND_MEAN -#define WIN32_LEAN_AND_MEAN 1 -#endif /* !WIN32_LEAN_AND_MEAN */ -#include <winsock2.h> -#endif /* _WIN32 && !__CYGWIN__ */ - -#ifndef FD_SETSIZE -#error FD_SETSIZE must be defined in system headers -#endif /* !FD_SETSIZE */ - -#include "sysfdsetsize.h" - -/** - * Get system default value of FD_SETSIZE - * @return system default value of FD_SETSIZE - */ -int -get_system_fdsetsize_value (void) -{ - return FD_SETSIZE; -} diff --git a/src/lib/sysfdsetsize.h b/src/lib/sysfdsetsize.h @@ -1,36 +0,0 @@ -/* - This file is part of libmicrohttpd - Copyright (C) 2015 Karlson2k (Evgeny Grin) - - 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 microhttpd/sysfdsetsize.h - * @brief Helper for obtaining FD_SETSIZE system default value - * @author Karlson2k (Evgeny Grin) - */ - -#ifndef SYSFDSETSIZE_H -#define SYSFDSETSIZE_H 1 - -/** - * Get system default value of FD_SETSIZE - * @return system default value of FD_SETSIZE - */ -int -get_system_fdsetsize_value (void); - -#endif /* !SYSFDSETSIZE_H */ diff --git a/src/lib/tsearch.c b/src/lib/tsearch.c @@ -1,144 +0,0 @@ -/* - * Tree search generalized from Knuth (6.2.2) Algorithm T just like - * the AT&T man page says. - * - * The node_t structure is for internal use only, lint doesn't grok it. - * - * Written by reading the System V Interface Definition, not the code. - * - * Totally public domain. - */ - -#include "tsearch.h" -#include <stdlib.h> - - -typedef struct node -{ - const void *key; - struct node *llink; - struct node *rlink; -} node_t; - - -/* $NetBSD: tsearch.c,v 1.5 2005/11/29 03:12:00 christos Exp $ */ -/* find or insert datum into search tree */ -void * -tsearch (const void *vkey, /* key to be located */ - void **vrootp, /* address of tree root */ - int (*compar)(const void *, const void *)) -{ - node_t *q; - node_t **rootp = (node_t **) vrootp; - - if (NULL == rootp) - return NULL; - - while (*rootp != NULL) - { /* Knuth's T1: */ - int r; - - if ((r = (*compar)(vkey, (*rootp)->key)) == 0) /* T2: */ - return *rootp; /* we found it! */ - - rootp = (r < 0) ? - &(*rootp)->llink : /* T3: follow left branch */ - &(*rootp)->rlink; /* T4: follow right branch */ - } - - q = malloc (sizeof(node_t)); /* T5: key not found */ - if (q) - { /* make new node */ - *rootp = q; /* link new node to old */ - q->key = vkey; /* initialize new node */ - q->llink = q->rlink = NULL; - } - return q; -} - - -/* $NetBSD: tfind.c,v 1.5 2005/03/23 08:16:53 kleink Exp $ */ -/* find a node, or return NULL */ -void * -tfind (const void *vkey, /* key to be found */ - void *const *vrootp, /* address of the tree root */ - int (*compar)(const void *, const void *)) -{ - node_t *const *rootp = (node_t *const *) vrootp; - - if (NULL == rootp) - return NULL; - - while (*rootp != NULL) - { /* T1: */ - int r; - - if ((r = (*compar)(vkey, (*rootp)->key)) == 0) /* T2: */ - return *rootp; /* key found */ - rootp = (r < 0) ? - &(*rootp)->llink : /* T3: follow left branch */ - &(*rootp)->rlink; /* T4: follow right branch */ - } - return NULL; -} - - -/* $NetBSD: tdelete.c,v 1.2 1999/09/16 11:45:37 lukem Exp $ */ -/* - * delete node with given key - * - * vkey: key to be deleted - * vrootp: address of the root of the tree - * compar: function to carry out node comparisons - */ -void * -tdelete (const void *__restrict vkey, - void **__restrict vrootp, - int (*compar)(const void *, const void *)) -{ - node_t **rootp = (node_t **) vrootp; - node_t *p; - node_t *q; - node_t *r; - int cmp; - - if ((rootp == NULL) || ((p = *rootp) == NULL)) - return NULL; - - while ((cmp = (*compar)(vkey, (*rootp)->key)) != 0) - { - p = *rootp; - rootp = (cmp < 0) ? - &(*rootp)->llink : /* follow llink branch */ - &(*rootp)->rlink; /* follow rlink branch */ - if (*rootp == NULL) - return NULL; /* key not found */ - } - r = (*rootp)->rlink; /* D1: */ - if ((q = (*rootp)->llink) == NULL) /* Left NULL? */ - { - q = r; - } - else if (r != NULL) - { /* Right link is NULL? */ - if (r->llink == NULL) - { /* D2: Find successor */ - r->llink = q; - q = r; - } - else - { /* D3: Find NULL link */ - for (q = r->llink; q->llink != NULL; q = r->llink) - r = q; - r->llink = q->rlink; - q->llink = (*rootp)->llink; - q->rlink = (*rootp)->rlink; - } - } - free (*rootp); /* D4: Free node */ - *rootp = q; /* link parent to new node */ - return p; -} - - -/* end of tsearch.c */ diff --git a/src/lib/tsearch.h b/src/lib/tsearch.h @@ -1,38 +0,0 @@ -/*- - * Written by J.T. Conklin <jtc@netbsd.org> - * Public domain. - * - * $NetBSD: search.h,v 1.12 1999/02/22 10:34:28 christos Exp $ - * $FreeBSD: release/9.0.0/include/search.h 105250 2002-10-16 14:29:23Z robert $ - */ - -#ifndef _TSEARCH_H_ -#define _TSEARCH_H_ - -#if defined(__cplusplus) -extern "C" { -#endif /* __cplusplus */ - - -void * -tdelete (const void *__restrict, - void **__restrict, - int (*)(const void *, const void *)); - - -void * -tfind (const void *, - void *const *, - int (*)(const void *, const void *)); - - -void * -tsearch (const void *, - void **, - int (*)(const void *, const void *)); - -#if defined(__cplusplus) -}; -#endif /* __cplusplus */ - -#endif /* !_TSEARCH_H_ */ diff --git a/src/lib/upgrade_process.c b/src/lib/upgrade_process.c @@ -1,395 +0,0 @@ -/* - This file is part of libmicrohttpd - Copyright (C) 2007-2018 Daniel Pittman and Christian Grothoff - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. - - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with this library; if not, write to the Free Software - Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA -*/ -/** - * @file lib/upgrade_process.c - * @brief function to process upgrade activity (over TLS) - * @author Christian Grothoff - */ -#include "internal.h" -#include "upgrade_process.h" - - -#if defined(HTTPS_SUPPORT) && defined(UPGRADE_SUPPORT) -/** - * Performs bi-directional forwarding on upgraded HTTPS connections - * based on the readiness state stored in the @a urh handle. - * @remark To be called only from thread that process - * connection's recv(), send() and response. - * - * @param urh handle to process - */ -void -MHD_upgrade_response_handle_process_ (struct MHD_UpgradeResponseHandle *urh) -{ - /* Help compiler to optimize: - * pointers to 'connection' and 'daemon' are not changed - * during this processing, so no need to chain dereference - * each time. */ - struct MHD_Connection *const connection = urh->connection; - struct MHD_Daemon *const daemon = connection->daemon; - /* Prevent data races: use same value of 'was_closed' throughout - * this function. If 'was_closed' changed externally in the middle - * of processing - it will be processed on next iteration. */ - bool was_closed; - struct MHD_TLS_Plugin *tls = daemon->tls_api; - - if (daemon->shutdown) - { - /* Daemon shutting down, application will not receive any more data. */ -#ifdef HAVE_MESSAGES - if (! urh->was_closed) - { - MHD_DLOG (daemon, - MHD_SC_DAEMON_ALREADY_SHUTDOWN, - _ ( - "Initiated daemon shutdown while \"upgraded\" connection was not closed.\n")); - } -#endif - urh->was_closed = true; - } - was_closed = urh->was_closed; - if (was_closed) - { - /* Application was closed connections: no more data - * can be forwarded to application socket. */ - if (0 < urh->in_buffer_used) - { -#ifdef HAVE_MESSAGES - MHD_DLOG (daemon, - MHD_SC_UPGRADE_FORWARD_INCOMPLETE, - _ ( - "Failed to forward to application " - MHD_UNSIGNED_LONG_LONG_PRINTF \ - " bytes of data received from remote side: application shut down socket.\n"), - (MHD_UNSIGNED_LONG_LONG) urh->in_buffer_used); -#endif - - } - /* If application signaled MHD about socket closure then - * check for any pending data even if socket is not marked - * as 'ready' (signal may arrive after poll()/select()). - * Socketpair for forwarding is always in non-blocking mode - * so no risk that recv() will block the thread. */if (0 != urh->out_buffer_size) - urh->mhd.celi |= MHD_EPOLL_STATE_READ_READY; - /* Discard any data received form remote. */ - urh->in_buffer_used = 0; - /* Do not try to push data to application. */ - urh->mhd.celi &= ~MHD_EPOLL_STATE_WRITE_READY; - /* Reading from remote client is not required anymore. */ - urh->in_buffer_size = 0; - urh->app.celi &= ~MHD_EPOLL_STATE_READ_READY; - connection->tls_read_ready = false; - } - - /* On some platforms (W32, possibly Darwin) failed send() (send() will always - * fail after remote disconnect was detected) may discard data in system - * buffers received by system but not yet read by recv(). - * So, before trying send() on any socket, recv() must be performed at first - * otherwise last part of incoming data may be lost. *//* If disconnect or error was detected - try to read from socket - * to dry data possibly pending is system buffers. */if (0 != (MHD_EPOLL_STATE_ERROR & urh->app.celi)) - urh->app.celi |= MHD_EPOLL_STATE_READ_READY; - if (0 != (MHD_EPOLL_STATE_ERROR & urh->mhd.celi)) - urh->mhd.celi |= MHD_EPOLL_STATE_READ_READY; - - /* - * handle reading from remote TLS client - */ - if ( ( (0 != (MHD_EPOLL_STATE_READ_READY & urh->app.celi)) || - (connection->tls_read_ready) ) && - (urh->in_buffer_used < urh->in_buffer_size) ) - { - ssize_t res; - size_t buf_size; - - buf_size = urh->in_buffer_size - urh->in_buffer_used; - if (buf_size > SSIZE_MAX) - buf_size = SSIZE_MAX; - - connection->tls_read_ready = false; - res = tls->recv (tls->cls, - connection->tls_cs, - &urh->in_buffer[urh->in_buffer_used], - buf_size); - if (0 >= res) - { - // FIXME: define GNUTLS-independent error codes! - if (GNUTLS_E_INTERRUPTED != res) - { - urh->app.celi &= ~MHD_EPOLL_STATE_READ_READY; - if (GNUTLS_E_AGAIN != res) - { - /* Unrecoverable error on socket was detected or - * socket was disconnected/shut down. */ - /* Stop trying to read from this TLS socket. */ - urh->in_buffer_size = 0; - } - } - } - else /* 0 < res */ - { - urh->in_buffer_used += res; - if (buf_size > (size_t) res) - urh->app.celi &= ~MHD_EPOLL_STATE_READ_READY; - else if (0 < tls->check_record_pending (tls->cls, - connection->tls_cs)) - connection->tls_read_ready = true; - } - if (MHD_EPOLL_STATE_ERROR == - ((MHD_EPOLL_STATE_ERROR | MHD_EPOLL_STATE_READ_READY) & urh->app.celi)) - { - /* Unrecoverable error on socket was detected and all - * pending data was read from system buffers. */ - /* Stop trying to read from this TLS socket. */ - urh->in_buffer_size = 0; - } - } - - /* - * handle reading from application - */ - if ( (0 != (MHD_EPOLL_STATE_READ_READY & urh->mhd.celi)) && - (urh->out_buffer_used < urh->out_buffer_size) ) - { - ssize_t res; - size_t buf_size; - - buf_size = urh->out_buffer_size - urh->out_buffer_used; - if (buf_size > MHD_SCKT_SEND_MAX_SIZE_) - buf_size = MHD_SCKT_SEND_MAX_SIZE_; - - res = MHD_recv_ (urh->mhd.socket, - &urh->out_buffer[urh->out_buffer_used], - buf_size); - if (0 >= res) - { - const int err = MHD_socket_get_error_ (); - if ((0 == res) || - ((! MHD_SCKT_ERR_IS_EINTR_ (err)) && - (! MHD_SCKT_ERR_IS_LOW_RESOURCES_ (err)))) - { - urh->mhd.celi &= ~MHD_EPOLL_STATE_READ_READY; - if ((0 == res) || - (was_closed) || - (0 != (MHD_EPOLL_STATE_ERROR & urh->mhd.celi)) || - (! MHD_SCKT_ERR_IS_EAGAIN_ (err))) - { - /* Socket disconnect/shutdown was detected; - * Application signaled about closure of 'upgraded' socket; - * or persistent / unrecoverable error. */ - /* Do not try to pull more data from application. */ - urh->out_buffer_size = 0; - } - } - } - else /* 0 < res */ - { - urh->out_buffer_used += res; - if (buf_size > (size_t) res) - urh->mhd.celi &= ~MHD_EPOLL_STATE_READ_READY; - } - if ( (0 == (MHD_EPOLL_STATE_READ_READY & urh->mhd.celi)) && - ( (0 != (MHD_EPOLL_STATE_ERROR & urh->mhd.celi)) || - (was_closed) ) ) - { - /* Unrecoverable error on socket was detected and all - * pending data was read from system buffers. */ - /* Do not try to pull more data from application. */ - urh->out_buffer_size = 0; - } - } - - /* - * handle writing to remote HTTPS client - */ - if ( (0 != (MHD_EPOLL_STATE_WRITE_READY & urh->app.celi)) && - (urh->out_buffer_used > 0) ) - { - ssize_t res; - size_t data_size; - - data_size = urh->out_buffer_used; - if (data_size > SSIZE_MAX) - data_size = SSIZE_MAX; - - res = tls->send (tls->cls, - connection->tls_cs, - urh->out_buffer, - data_size); - if (0 >= res) - { - // FIXME: define GNUTLS-independent error codes! - if (GNUTLS_E_INTERRUPTED != res) - { - urh->app.celi &= ~MHD_EPOLL_STATE_WRITE_READY; - if (GNUTLS_E_INTERRUPTED != res) - { - /* TLS connection shut down or - * persistent / unrecoverable error. */ -#ifdef HAVE_MESSAGES - MHD_DLOG (daemon, - MHD_SC_UPGRADE_FORWARD_INCOMPLETE, - _ ( - "Failed to forward to remote client " - MHD_UNSIGNED_LONG_LONG_PRINTF \ - " bytes of data received from application: %s\n"), - (MHD_UNSIGNED_LONG_LONG) urh->out_buffer_used, - tls->strerror (tls->cls, - res)); -#endif - /* Discard any data unsent to remote. */ - urh->out_buffer_used = 0; - /* Do not try to pull more data from application. */ - urh->out_buffer_size = 0; - urh->mhd.celi &= ~MHD_EPOLL_STATE_READ_READY; - } - } - } - else /* 0 < res */ - { - const size_t next_out_buffer_used = urh->out_buffer_used - res; - if (0 != next_out_buffer_used) - { - memmove (urh->out_buffer, - &urh->out_buffer[res], - next_out_buffer_used); - if (data_size > (size_t) res) - urh->app.celi &= ~MHD_EPOLL_STATE_WRITE_READY; - } - urh->out_buffer_used = next_out_buffer_used; - } - if ( (0 == urh->out_buffer_used) && - (0 != (MHD_EPOLL_STATE_ERROR & urh->app.celi)) ) - { - /* Unrecoverable error on socket was detected and all - * pending data was sent to remote. */ - /* Do not try to send to remote anymore. */ - urh->app.celi &= ~MHD_EPOLL_STATE_WRITE_READY; - /* Do not try to pull more data from application. */ - urh->out_buffer_size = 0; - urh->mhd.celi &= ~MHD_EPOLL_STATE_READ_READY; - } - } - - /* - * handle writing to application - */ - if ( (0 != (MHD_EPOLL_STATE_WRITE_READY & urh->mhd.celi)) && - (urh->in_buffer_used > 0) ) - { - ssize_t res; - size_t data_size; - - data_size = urh->in_buffer_used; - if (data_size > MHD_SCKT_SEND_MAX_SIZE_) - data_size = MHD_SCKT_SEND_MAX_SIZE_; - - res = MHD_send_ (urh->mhd.socket, - urh->in_buffer, - data_size); - if (0 >= res) - { - const int err = MHD_socket_get_error_ (); - if ( (! MHD_SCKT_ERR_IS_EINTR_ (err)) && - (! MHD_SCKT_ERR_IS_LOW_RESOURCES_ (err)) ) - { - urh->mhd.celi &= ~MHD_EPOLL_STATE_WRITE_READY; - if (! MHD_SCKT_ERR_IS_EAGAIN_ (err)) - { - /* Socketpair connection shut down or - * persistent / unrecoverable error. */ -#ifdef HAVE_MESSAGES - MHD_DLOG (daemon, - MHD_SC_UPGRADE_FORWARD_INCOMPLETE, - _ ( - "Failed to forward to application " - MHD_UNSIGNED_LONG_LONG_PRINTF \ - " bytes of data received from remote side: %s\n"), - (MHD_UNSIGNED_LONG_LONG) urh->in_buffer_used, - MHD_socket_strerr_ (err)); -#endif - /* Discard any data received form remote. */ - urh->in_buffer_used = 0; - /* Reading from remote client is not required anymore. */ - urh->in_buffer_size = 0; - urh->app.celi &= ~MHD_EPOLL_STATE_READ_READY; - connection->tls_read_ready = false; - } - } - } - else /* 0 < res */ - { - const size_t next_in_buffer_used = urh->in_buffer_used - res; - if (0 != next_in_buffer_used) - { - memmove (urh->in_buffer, - &urh->in_buffer[res], - next_in_buffer_used); - if (data_size > (size_t) res) - urh->mhd.celi &= ~MHD_EPOLL_STATE_WRITE_READY; - } - urh->in_buffer_used = next_in_buffer_used; - } - if ( (0 == urh->in_buffer_used) && - (0 != (MHD_EPOLL_STATE_ERROR & urh->mhd.celi)) ) - { - /* Do not try to push data to application. */ - urh->mhd.celi &= ~MHD_EPOLL_STATE_WRITE_READY; - /* Reading from remote client is not required anymore. */ - urh->in_buffer_size = 0; - urh->app.celi &= ~MHD_EPOLL_STATE_READ_READY; - connection->tls_read_ready = false; - } - } - - /* Check whether data is present in TLS buffers - * and incoming forward buffer have some space. */ - if ( (connection->tls_read_ready) && - (urh->in_buffer_used < urh->in_buffer_size) && - (MHD_TM_THREAD_PER_CONNECTION != daemon->threading_mode) ) - daemon->data_already_pending = true; - - if ( (daemon->shutdown) && - ( (0 != urh->out_buffer_size) || - (0 != urh->out_buffer_used) ) ) - { - /* Daemon shutting down, discard any remaining forward data. */ -#ifdef HAVE_MESSAGES - if (0 < urh->out_buffer_used) - MHD_DLOG (daemon, - MHD_SC_UPGRADE_FORWARD_INCOMPLETE, - _ ( - "Failed to forward to remote client " - MHD_UNSIGNED_LONG_LONG_PRINTF \ - " bytes of data received from application: daemon shut down.\n"), - (MHD_UNSIGNED_LONG_LONG) urh->out_buffer_used); -#endif - /* Discard any data unsent to remote. */ - urh->out_buffer_used = 0; - /* Do not try to sent to remote anymore. */ - urh->app.celi &= ~MHD_EPOLL_STATE_WRITE_READY; - /* Do not try to pull more data from application. */ - urh->out_buffer_size = 0; - urh->mhd.celi &= ~MHD_EPOLL_STATE_READ_READY; - } -} - - -#endif /* HTTPS_SUPPORT && UPGRADE_SUPPORT */ - -/* end of upgrade_process.c */ diff --git a/src/lib/upgrade_process.h b/src/lib/upgrade_process.h @@ -1,44 +0,0 @@ -/* - This file is part of libmicrohttpd - Copyright (C) 2007-2018 Daniel Pittman and Christian Grothoff - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. - - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with this library; if not, write to the Free Software - Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA -*/ -/** - * @file lib/upgrade_process.h - * @brief function to process upgrade activity (over TLS) - * @author Christian Grothoff - */ -#ifndef UPGRADE_PROCESS_H -#define UPGRADE_PROCESS_H - - -#if defined(HTTPS_SUPPORT) && defined(UPGRADE_SUPPORT) -/** - * Performs bi-directional forwarding on upgraded HTTPS connections - * based on the readiness state stored in the @a urh handle. - * @remark To be called only from thread that process - * connection's recv(), send() and response. - * - * @param urh handle to process - */ -void -MHD_upgrade_response_handle_process_ (struct MHD_UpgradeResponseHandle *urh) -MHD_NONNULL (1); - - -#endif - -#endif diff --git a/src/lib/version.c b/src/lib/version.c @@ -1,207 +0,0 @@ -/* - This file is part of libmicrohttpd - Copyright (C) 2007-2018 Daniel Pittman and Christian Grothoff - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. - - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with this library; if not, write to the Free Software - Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA -*/ - -/** - * @file lib/version.c - * @brief versioning and optional feature tests - * @author Christian Grothoff - */ -#include "internal.h" - - -/** - * Obtain the version of this library - * - * @return static version string, e.g. "0.9.9" - * @ingroup specialized - */ -const char * -MHD_get_version (void) -{ -#ifdef PACKAGE_VERSION - return PACKAGE_VERSION; -#else /* !PACKAGE_VERSION */ - static char ver[12] = "\0\0\0\0\0\0\0\0\0\0\0"; - if (0 == ver[0]) - { - int res = MHD_snprintf_ (ver, - sizeof(ver), - "%x.%x.%x", - (((int) MHD_VERSION >> 24) & 0xFF), - (((int) MHD_VERSION >> 16) & 0xFF), - (((int) MHD_VERSION >> 8) & 0xFF)); - if ((0 >= res) || (sizeof(ver) <= res)) - return "0.0.0"; /* Can't return real version*/ - } - return ver; -#endif /* !PACKAGE_VERSION */ -} - - -/** - * Get information about supported MHD features. - * Indicate that MHD was compiled with or without support for - * particular feature. Some features require additional support - * by kernel. Kernel support is not checked by this function. - * - * @param feature type of requested information - * @return #MHD_YES if feature is supported by MHD, #MHD_NO if - * feature is not supported or feature is unknown. - * @ingroup specialized - */ -_MHD_EXTERN enum MHD_Bool -MHD_is_feature_supported (enum MHD_Feature feature) -{ - switch (feature) - { - case MHD_FEATURE_MESSAGES: -#ifdef HAVE_MESSAGES - return MHD_YES; -#else - return MHD_NO; -#endif - case MHD_FEATURE_TLS: -#ifdef HTTPS_SUPPORT - return MHD_YES; -#else /* ! HTTPS_SUPPORT */ - return MHD_NO; -#endif /* ! HTTPS_SUPPORT */ - case MHD_FEATURE_HTTPS_CERT_CALLBACK: -#if defined(HTTPS_SUPPORT) && GNUTLS_VERSION_MAJOR >= 3 - return MHD_YES; -#else /* !HTTPS_SUPPORT || GNUTLS_VERSION_MAJOR < 3 */ - return MHD_NO; -#endif /* !HTTPS_SUPPORT || GNUTLS_VERSION_MAJOR < 3 */ - case MHD_FEATURE_IPv6: -#ifdef HAVE_INET6 - return MHD_YES; -#else - return MHD_NO; -#endif - case MHD_FEATURE_IPv6_ONLY: -#if defined(IPPROTO_IPV6) && defined(IPV6_V6ONLY) - return MHD_YES; -#else - return MHD_NO; -#endif - case MHD_FEATURE_POLL: -#ifdef HAVE_POLL - return MHD_YES; -#else - return MHD_NO; -#endif - case MHD_FEATURE_EPOLL: -#ifdef EPOLL_SUPPORT - return MHD_YES; -#else - return MHD_NO; -#endif - case MHD_FEATURE_SHUTDOWN_LISTEN_SOCKET: -#ifdef HAVE_LISTEN_SHUTDOWN - return MHD_YES; -#else - return MHD_NO; -#endif - case MHD_FEATURE_SOCKETPAIR: -#ifdef _MHD_ITC_SOCKETPAIR - return MHD_YES; -#else - return MHD_NO; -#endif - case MHD_FEATURE_TCP_FASTOPEN: -#ifdef TCP_FASTOPEN - return MHD_YES; -#else - return MHD_NO; -#endif - case MHD_FEATURE_BASIC_AUTH: -#ifdef BAUTH_SUPPORT - return MHD_YES; -#else - return MHD_NO; -#endif - case MHD_FEATURE_DIGEST_AUTH: -#ifdef DAUTH_SUPPORT - return MHD_YES; -#else - return MHD_NO; -#endif - case MHD_FEATURE_POSTPROCESSOR: -#ifdef HAVE_POSTPROCESSOR - return MHD_YES; -#else - return MHD_NO; -#endif - case MHD_FEATURE_HTTPS_KEY_PASSWORD: -#if defined(HTTPS_SUPPORT) && GNUTLS_VERSION_NUMBER >= 0x030111 - return MHD_YES; -#else /* !HTTPS_SUPPORT || GNUTLS_VERSION_NUMBER < 0x030111 */ - return MHD_NO; -#endif /* !HTTPS_SUPPORT || GNUTLS_VERSION_NUMBER < 0x030111 */ - case MHD_FEATURE_LARGE_FILE: -#if defined(HAVE_PREAD64) || defined(_WIN32) - return MHD_YES; -#elif defined(HAVE_PREAD) - return (sizeof(uint64_t) > sizeof(off_t)) ? MHD_NO : MHD_YES; -#elif defined(HAVE_LSEEK64) - return MHD_YES; -#else - return (sizeof(uint64_t) > sizeof(off_t)) ? MHD_NO : MHD_YES; -#endif - case MHD_FEATURE_THREAD_NAMES: -#if defined(MHD_USE_THREAD_NAME_) - return MHD_YES; -#else - return MHD_NO; -#endif - case MHD_FEATURE_UPGRADE: -#if defined(UPGRADE_SUPPORT) - return MHD_YES; -#else - return MHD_NO; -#endif - case MHD_FEATURE_RESPONSES_SHARED_FD: -#if defined(HAVE_PREAD64) || defined(HAVE_PREAD) || defined(_WIN32) - return MHD_YES; -#else - return MHD_NO; -#endif - case MHD_FEATURE_AUTODETECT_BIND_PORT: -#ifdef MHD_USE_GETSOCKNAME - return MHD_YES; -#else - return MHD_NO; -#endif - case MHD_FEATURE_AUTOSUPPRESS_SIGPIPE: -#if defined(MHD_WINSOCK_SOCKETS) || defined(MHD_socket_nosignal_) || \ - defined (MSG_NOSIGNAL) - return MHD_YES; -#else - return MHD_NO; -#endif - case MHD_FEATURE_SENDFILE: -#ifdef _MHD_HAVE_SENDFILE - return MHD_YES; -#else - return MHD_NO; -#endif - - } - return MHD_NO; -} diff --git a/src/microhttpd/daemon.c b/src/microhttpd/daemon.c @@ -7672,7 +7672,6 @@ MHD_start_daemon_va (unsigned int flags, { const MHD_SCKT_OPT_BOOL_ on = 1; struct MHD_Daemon *daemon; - MHD_socket listen_fd = MHD_INVALID_SOCKET; const struct sockaddr *pservaddr = NULL; socklen_t addrlen; #if defined(MHD_USE_POSIX_THREADS) || defined(MHD_USE_W32_THREADS) @@ -8260,8 +8259,8 @@ MHD_start_daemon_va (unsigned int flags, #endif /* HAVE_INET6 */ } - listen_fd = MHD_socket_create_listen_ (domain); - if (MHD_INVALID_SOCKET == listen_fd) + daemon->listen_fd = MHD_socket_create_listen_ (domain); + if (MHD_INVALID_SOCKET == daemon->listen_fd) { #ifdef HAVE_MESSAGES MHD_DLOG (daemon, @@ -8271,17 +8270,16 @@ MHD_start_daemon_va (unsigned int flags, goto free_and_fail; } if (MHD_D_IS_USING_SELECT_ (daemon) && - (! MHD_D_DOES_SCKT_FIT_FDSET_ (listen_fd, daemon)) ) + (! MHD_D_DOES_SCKT_FIT_FDSET_ (daemon->listen_fd, + daemon)) ) { #ifdef HAVE_MESSAGES MHD_DLOG (daemon, _ ("Listen socket descriptor (%d) is not " \ "less than daemon FD_SETSIZE value (%d).\n"), - (int) listen_fd, + (int) daemon->listen_fd, (int) MHD_D_GET_FD_SETSIZE_ (daemon)); #endif - MHD_socket_close_chk_ (listen_fd); - listen_fd = MHD_INVALID_SOCKET; goto free_and_fail; } @@ -8293,7 +8291,7 @@ MHD_start_daemon_va (unsigned int flags, * on non-W32 platforms, and do not fail if it doesn't work. * Don't use it on W32, because on W32 it will allow multiple * bind to the same address:port, like SO_REUSEPORT on others. */ - if (0 > setsockopt (listen_fd, + if (0 > setsockopt (daemon->listen_fd, SOL_SOCKET, SO_REUSEADDR, (const void *) &on, sizeof (on))) @@ -8312,7 +8310,7 @@ MHD_start_daemon_va (unsigned int flags, #ifndef MHD_WINSOCK_SOCKETS /* Use SO_REUSEADDR on non-W32 platforms, and do not fail if * it doesn't work. */ - if (0 > setsockopt (listen_fd, + if (0 > setsockopt (daemon->listen_fd, SOL_SOCKET, SO_REUSEADDR, (const void *) &on, sizeof (on))) @@ -8330,7 +8328,7 @@ MHD_start_daemon_va (unsigned int flags, /* SO_REUSEADDR on W32 has the same semantics as SO_REUSEPORT on BSD/Linux */ #if defined(MHD_WINSOCK_SOCKETS) || defined(SO_REUSEPORT) - if (0 > setsockopt (listen_fd, + if (0 > setsockopt (daemon->listen_fd, SOL_SOCKET, #ifndef MHD_WINSOCK_SOCKETS SO_REUSEPORT, @@ -8368,7 +8366,7 @@ MHD_start_daemon_va (unsigned int flags, */ #if (defined(MHD_WINSOCK_SOCKETS) && defined(SO_EXCLUSIVEADDRUSE)) || \ (defined(__sun) && defined(SO_EXCLBIND)) - if (0 > setsockopt (listen_fd, + if (0 > setsockopt (daemon->listen_fd, SOL_SOCKET, #ifdef SO_EXCLUSIVEADDRUSE SO_EXCLUSIVEADDRUSE, @@ -8396,8 +8394,6 @@ MHD_start_daemon_va (unsigned int flags, } /* check for user supplied sockaddr */ - daemon->listen_fd = listen_fd; - if (0 != (*pflags & MHD_USE_IPv6)) { #ifdef IPPROTO_IPV6 @@ -8408,7 +8404,7 @@ MHD_start_daemon_va (unsigned int flags, your IPv6 socket may then also bind against IPv4 anyway... */ const MHD_SCKT_OPT_BOOL_ v6_only = (MHD_USE_DUAL_STACK != (*pflags & MHD_USE_DUAL_STACK)); - if (0 > setsockopt (listen_fd, + if (0 > setsockopt (daemon->listen_fd, IPPROTO_IPV6, IPV6_V6ONLY, (const void *) &v6_only, sizeof (v6_only))) @@ -8422,7 +8418,9 @@ MHD_start_daemon_va (unsigned int flags, #endif #endif } - if (0 != bind (listen_fd, pservaddr, addrlen)) + if (0 != bind (daemon->listen_fd, + pservaddr, + addrlen)) { #ifdef HAVE_MESSAGES MHD_DLOG (daemon, @@ -8430,8 +8428,6 @@ MHD_start_daemon_va (unsigned int flags, (unsigned int) port, MHD_socket_last_strerr_ ()); #endif - MHD_socket_close_chk_ (listen_fd); - listen_fd = MHD_INVALID_SOCKET; goto free_and_fail; } #ifdef TCP_FASTOPEN @@ -8439,7 +8435,7 @@ MHD_start_daemon_va (unsigned int flags, { if (0 == daemon->fastopen_queue_size) daemon->fastopen_queue_size = MHD_TCP_FASTOPEN_QUEUE_SIZE_DEFAULT; - if (0 != setsockopt (listen_fd, + if (0 != setsockopt (daemon->listen_fd, IPPROTO_TCP, TCP_FASTOPEN, (const void *) &daemon->fastopen_queue_size, @@ -8453,7 +8449,7 @@ MHD_start_daemon_va (unsigned int flags, } } #endif - if (0 != listen (listen_fd, + if (0 != listen (daemon->listen_fd, (int) daemon->listen_backlog_size)) { #ifdef HAVE_MESSAGES @@ -8461,15 +8457,14 @@ MHD_start_daemon_va (unsigned int flags, _ ("Failed to listen for connections: %s\n"), MHD_socket_last_strerr_ ()); #endif - MHD_socket_close_chk_ (listen_fd); - listen_fd = MHD_INVALID_SOCKET; goto free_and_fail; } } else { if (MHD_D_IS_USING_SELECT_ (daemon) && - (! MHD_D_DOES_SCKT_FIT_FDSET_ (daemon->listen_fd, daemon)) ) + (! MHD_D_DOES_SCKT_FIT_FDSET_ (daemon->listen_fd, + daemon)) ) { #ifdef HAVE_MESSAGES MHD_DLOG (daemon, @@ -8535,7 +8530,6 @@ MHD_start_daemon_va (unsigned int flags, daemon->listen_is_unix = _MHD_UNKNOWN; } - listen_fd = daemon->listen_fd; #ifdef MHD_USE_GETSOCKNAME daemon->port = 0; /* Force use of autodetection */ #endif /* MHD_USE_GETSOCKNAME */ @@ -8555,7 +8549,7 @@ MHD_start_daemon_va (unsigned int flags, #ifdef HAVE_STRUCT_SOCKADDR_STORAGE_SS_LEN bindaddr.ss_len = (socklen_t) addrlen; #endif - if (0 != getsockname (listen_fd, + if (0 != getsockname (daemon->listen_fd, (struct sockaddr *) &bindaddr, &addrlen)) { @@ -8628,10 +8622,10 @@ MHD_start_daemon_va (unsigned int flags, } #endif /* MHD_USE_GETSOCKNAME */ - if (MHD_INVALID_SOCKET != listen_fd) + if (MHD_INVALID_SOCKET != daemon->listen_fd) { mhd_assert (0 == (*pflags & MHD_USE_NO_LISTEN_SOCKET)); - if (! MHD_socket_nonblocking_ (listen_fd)) + if (! MHD_socket_nonblocking_ (daemon->listen_fd)) { #ifdef HAVE_MESSAGES MHD_DLOG (daemon, @@ -8647,8 +8641,6 @@ MHD_start_daemon_va (unsigned int flags, /* Accept must be non-blocking. Multiple children may wake up * to handle a new connection, but only one will win the race. * The others must immediately return. */ - MHD_socket_close_chk_ (listen_fd); - listen_fd = MHD_INVALID_SOCKET; goto free_and_fail; } daemon->listen_nonblk = false; @@ -8690,8 +8682,6 @@ MHD_start_daemon_va (unsigned int flags, MHD_DLOG (daemon, _ ("MHD failed to initialize IP connection limit mutex.\n")); #endif - if (MHD_INVALID_SOCKET != listen_fd) - MHD_socket_close_chk_ (listen_fd); goto free_and_fail; } #endif @@ -8705,8 +8695,6 @@ MHD_start_daemon_va (unsigned int flags, MHD_DLOG (daemon, _ ("Failed to initialize TLS support.\n")); #endif - if (MHD_INVALID_SOCKET != listen_fd) - MHD_socket_close_chk_ (listen_fd); #if defined(MHD_USE_POSIX_THREADS) || defined(MHD_USE_W32_THREADS) MHD_mutex_destroy_chk_ (&daemon->per_ip_connection_mutex); #endif @@ -8738,8 +8726,6 @@ MHD_start_daemon_va (unsigned int flags, _ ("Failed to initialise internal lists mutex.\n")); #endif MHD_mutex_destroy_chk_ (&daemon->per_ip_connection_mutex); - if (MHD_INVALID_SOCKET != listen_fd) - MHD_socket_close_chk_ (listen_fd); goto free_and_fail; } if (! MHD_mutex_init_ (&daemon->new_connections_mutex)) @@ -8750,8 +8736,6 @@ MHD_start_daemon_va (unsigned int flags, #endif MHD_mutex_destroy_chk_ (&daemon->per_ip_connection_mutex); MHD_mutex_destroy_chk_ (&daemon->cleanup_connection_mutex); - if (MHD_INVALID_SOCKET != listen_fd) - MHD_socket_close_chk_ (listen_fd); goto free_and_fail; } if (! MHD_create_named_thread_ (&daemon->tid, @@ -8777,8 +8761,6 @@ MHD_start_daemon_va (unsigned int flags, MHD_mutex_destroy_chk_ (&daemon->new_connections_mutex); MHD_mutex_destroy_chk_ (&daemon->per_ip_connection_mutex); MHD_mutex_destroy_chk_ (&daemon->cleanup_connection_mutex); - if (MHD_INVALID_SOCKET != listen_fd) - MHD_socket_close_chk_ (listen_fd); goto free_and_fail; } } @@ -8941,8 +8923,6 @@ MHD_start_daemon_va (unsigned int flags, _ ("Failed to initialise internal lists mutex.\n")); #endif MHD_mutex_destroy_chk_ (&daemon->per_ip_connection_mutex); - if (MHD_INVALID_SOCKET != listen_fd) - MHD_socket_close_chk_ (listen_fd); goto free_and_fail; } if (! MHD_mutex_init_ (&daemon->new_connections_mutex)) @@ -8953,8 +8933,6 @@ MHD_start_daemon_va (unsigned int flags, #endif MHD_mutex_destroy_chk_ (&daemon->cleanup_connection_mutex); MHD_mutex_destroy_chk_ (&daemon->per_ip_connection_mutex); - if (MHD_INVALID_SOCKET != listen_fd) - MHD_socket_close_chk_ (listen_fd); goto free_and_fail; } } @@ -8975,9 +8953,6 @@ thread_failed: MHD_USE_INTERNAL_POLLING_THREAD mode. */ if (0 == i) { - if (MHD_INVALID_SOCKET != listen_fd) - MHD_socket_close_chk_ (listen_fd); - listen_fd = MHD_INVALID_SOCKET; MHD_mutex_destroy_chk_ (&daemon->per_ip_connection_mutex); if (NULL != daemon->worker_pool) free (daemon->worker_pool); @@ -9034,10 +9009,7 @@ free_and_fail: #endif /* HTTPS_SUPPORT */ if (MHD_ITC_IS_VALID_ (daemon->itc)) MHD_itc_destroy_chk_ (daemon->itc); - if (MHD_INVALID_SOCKET != listen_fd) - (void) MHD_socket_close_ (listen_fd); - if ((MHD_INVALID_SOCKET != daemon->listen_fd) && - (listen_fd != daemon->listen_fd)) + if (MHD_INVALID_SOCKET != daemon->listen_fd) (void) MHD_socket_close_ (daemon->listen_fd); free (daemon); return NULL;