nifi-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From ald...@apache.org
Subject [38/51] [partial] nifi-minifi-cpp git commit: MINIFI-183 Added civet 1.9.1 sources Added onScheduled hook Added initial two-way TLS-enabled ListenHTTP implementation
Date Wed, 22 Feb 2017 20:52:33 GMT
http://git-wip-us.apache.org/repos/asf/nifi-minifi-cpp/blob/a9485aeb/thirdparty/civetweb-1.9.1/src/civetweb.c
----------------------------------------------------------------------
diff --git a/thirdparty/civetweb-1.9.1/src/civetweb.c b/thirdparty/civetweb-1.9.1/src/civetweb.c
new file mode 100644
index 0000000..da491b6
--- /dev/null
+++ b/thirdparty/civetweb-1.9.1/src/civetweb.c
@@ -0,0 +1,14894 @@
+/* Copyright (c) 2013-2017 the Civetweb developers
+ * Copyright (c) 2004-2013 Sergey Lyubka
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#if defined(_WIN32)
+#if !defined(_CRT_SECURE_NO_WARNINGS)
+#define _CRT_SECURE_NO_WARNINGS /* Disable deprecation warning in VS2005 */
+#endif
+#ifndef _WIN32_WINNT /* defined for tdm-gcc so we can use getnameinfo */
+#define _WIN32_WINNT 0x0501
+#endif
+#else
+#if defined(__GNUC__) && !defined(_GNU_SOURCE)
+#define _GNU_SOURCE /* for setgroups() */
+#endif
+#if defined(__linux__) && !defined(_XOPEN_SOURCE)
+#define _XOPEN_SOURCE 600 /* For flockfile() on Linux */
+#endif
+#ifndef _LARGEFILE_SOURCE
+#define _LARGEFILE_SOURCE /* For fseeko(), ftello() */
+#endif
+#ifndef _FILE_OFFSET_BITS
+#define _FILE_OFFSET_BITS 64 /* Use 64-bit file offsets by default */
+#endif
+#ifndef __STDC_FORMAT_MACROS
+#define __STDC_FORMAT_MACROS /* <inttypes.h> wants this for C++ */
+#endif
+#ifndef __STDC_LIMIT_MACROS
+#define __STDC_LIMIT_MACROS /* C++ wants that for INT64_MAX */
+#endif
+#ifdef __sun
+#define __EXTENSIONS__  /* to expose flockfile and friends in stdio.h */
+#define __inline inline /* not recognized on older compiler versions */
+#endif
+#endif
+
+#if defined(USE_LUA)
+#define USE_TIMERS
+#endif
+
+#if defined(_MSC_VER)
+/* 'type cast' : conversion from 'int' to 'HANDLE' of greater size */
+#pragma warning(disable : 4306)
+/* conditional expression is constant: introduced by FD_SET(..) */
+#pragma warning(disable : 4127)
+/* non-constant aggregate initializer: issued due to missing C99 support */
+#pragma warning(disable : 4204)
+/* padding added after data member */
+#pragma warning(disable : 4820)
+/* not defined as a preprocessor macro, replacing with '0' for '#if/#elif' */
+#pragma warning(disable : 4668)
+/* no function prototype given: converting '()' to '(void)' */
+#pragma warning(disable : 4255)
+/* function has been selected for automatic inline expansion */
+#pragma warning(disable : 4711)
+#endif
+
+
+/* This code uses static_assert to check some conditions.
+ * Unfortunately some compilers still do not support it, so we have a
+ * replacement function here. */
+#if defined(_MSC_VER) && (_MSC_VER >= 1600)
+#define mg_static_assert static_assert
+#elif defined(__cplusplus) && (__cplusplus >= 201103L)
+#define mg_static_assert static_assert
+#elif defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 201112L)
+#define mg_static_assert _Static_assert
+#else
+char static_assert_replacement[1];
+#define mg_static_assert(cond, txt)                                            \
+	extern char static_assert_replacement[(cond) ? 1 : -1]
+#endif
+
+mg_static_assert(sizeof(int) == 4 || sizeof(int) == 8,
+                 "int data type size check");
+mg_static_assert(sizeof(void *) == 4 || sizeof(void *) == 8,
+                 "pointer data type size check");
+mg_static_assert(sizeof(void *) >= sizeof(int), "data type size check");
+
+
+/* DTL -- including winsock2.h works better if lean and mean */
+#ifndef WIN32_LEAN_AND_MEAN
+#define WIN32_LEAN_AND_MEAN
+#endif
+
+#if defined(__SYMBIAN32__)
+#define NO_SSL /* SSL is not supported */
+#define NO_CGI /* CGI is not supported */
+#define PATH_MAX FILENAME_MAX
+#endif /* __SYMBIAN32__ */
+
+
+/* Include the header file here, so the CivetWeb interface is defined for the
+ * entire implementation, including the following forward definitions. */
+#include "civetweb.h"
+
+
+#ifndef IGNORE_UNUSED_RESULT
+#define IGNORE_UNUSED_RESULT(a) ((void)((a) && 1))
+#endif
+
+#ifndef _WIN32_WCE /* Some ANSI #includes are not available on Windows CE */
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <errno.h>
+#include <signal.h>
+#include <fcntl.h>
+#endif /* !_WIN32_WCE */
+
+
+#ifdef __clang__
+/* When using -Weverything, clang does not accept it's own headers
+ * in a release build configuration. Disable what is too much in
+ * -Weverything. */
+#pragma clang diagnostic ignored "-Wdisabled-macro-expansion"
+#endif
+
+
+#ifdef __MACH__ /* Apple OSX section */
+
+#ifdef __clang__
+/* Avoid warnings for Xopen 7.00 and higher */
+#pragma clang diagnostic ignored "-Wno-reserved-id-macro"
+#pragma clang diagnostic ignored "-Wno-keyword-macro"
+#endif
+
+#define CLOCK_MONOTONIC (1)
+#define CLOCK_REALTIME (2)
+
+#include <sys/errno.h>
+#include <sys/time.h>
+#include <mach/clock.h>
+#include <mach/mach.h>
+#include <mach/mach_time.h>
+#include <assert.h>
+
+/* clock_gettime is not implemented on OSX prior to 10.12 */
+static int
+_civet_clock_gettime(int clk_id, struct timespec *t)
+{
+	memset(t, 0, sizeof(*t));
+	if (clk_id == CLOCK_REALTIME) {
+		struct timeval now;
+		int rv = gettimeofday(&now, NULL);
+		if (rv) {
+			return rv;
+		}
+		t->tv_sec = now.tv_sec;
+		t->tv_nsec = now.tv_usec * 1000;
+		return 0;
+
+	} else if (clk_id == CLOCK_MONOTONIC) {
+		static uint64_t clock_start_time = 0;
+		static mach_timebase_info_data_t timebase_ifo = {0, 0};
+
+		uint64_t now = mach_absolute_time();
+
+		if (clock_start_time == 0) {
+			kern_return_t mach_status = mach_timebase_info(&timebase_ifo);
+#if defined(DEBUG)
+			assert(mach_status == KERN_SUCCESS);
+#else
+			/* appease "unused variable" warning for release builds */
+			(void)mach_status;
+#endif
+			clock_start_time = now;
+		}
+
+		now = (uint64_t)((double)(now - clock_start_time)
+		                 * (double)timebase_ifo.numer
+		                 / (double)timebase_ifo.denom);
+
+		t->tv_sec = now / 1000000000;
+		t->tv_nsec = now % 1000000000;
+		return 0;
+	}
+	return -1; /* EINVAL - Clock ID is unknown */
+}
+
+/* if clock_gettime is declared, then __CLOCK_AVAILABILITY will be defined */
+#ifdef __CLOCK_AVAILABILITY
+/* If we compiled with Mac OSX 10.12 or later, then clock_gettime will be
+ * declared but it may be NULL at runtime. So we need to check before using
+ * it. */
+static int
+_civet_safe_clock_gettime(int clk_id, struct timespec *t)
+{
+	if (clock_gettime) {
+		return clock_gettime(clk_id, t);
+	}
+	return _civet_clock_gettime(clk_id, t);
+}
+#define clock_gettime _civet_safe_clock_gettime
+#else
+#define clock_gettime _civet_clock_gettime
+#endif
+
+#endif
+
+
+#include <time.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <assert.h>
+#include <string.h>
+#include <ctype.h>
+#include <limits.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <stdint.h>
+
+#ifndef INT64_MAX
+#define INT64_MAX (9223372036854775807)
+#endif
+
+
+#ifndef MAX_WORKER_THREADS
+#define MAX_WORKER_THREADS (1024 * 64)
+#endif
+
+#ifndef SOCKET_TIMEOUT_QUANTUM /* in ms */
+#define SOCKET_TIMEOUT_QUANTUM (2000)
+#endif
+
+#define SHUTDOWN_RD (0)
+#define SHUTDOWN_WR (1)
+#define SHUTDOWN_BOTH (2)
+
+mg_static_assert(MAX_WORKER_THREADS >= 1,
+                 "worker threads must be a positive number");
+
+mg_static_assert(sizeof(size_t) == 4 || sizeof(size_t) == 8,
+                 "size_t data type size check");
+
+#if defined(_WIN32)                                                            \
+    && !defined(__SYMBIAN32__) /* WINDOWS / UNIX include block */
+#include <windows.h>
+#include <winsock2.h> /* DTL add for SO_EXCLUSIVE */
+#include <ws2tcpip.h>
+
+typedef const char *SOCK_OPT_TYPE;
+
+#if !defined(PATH_MAX)
+#define PATH_MAX (MAX_PATH)
+#endif
+
+#if !defined(PATH_MAX)
+#define PATH_MAX (4096)
+#endif
+
+mg_static_assert(PATH_MAX >= 1, "path length must be a positive number");
+
+#ifndef _IN_PORT_T
+#ifndef in_port_t
+#define in_port_t u_short
+#endif
+#endif
+
+#ifndef _WIN32_WCE
+#include <process.h>
+#include <direct.h>
+#include <io.h>
+#else            /* _WIN32_WCE */
+#define NO_CGI   /* WinCE has no pipes */
+#define NO_POPEN /* WinCE has no popen */
+
+typedef long off_t;
+
+#define errno ((int)(GetLastError()))
+#define strerror(x) (_ultoa(x, (char *)_alloca(sizeof(x) * 3), 10))
+#endif /* _WIN32_WCE */
+
+#define MAKEUQUAD(lo, hi)                                                      \
+	((uint64_t)(((uint32_t)(lo)) | ((uint64_t)((uint32_t)(hi))) << 32))
+#define RATE_DIFF (10000000) /* 100 nsecs */
+#define EPOCH_DIFF (MAKEUQUAD(0xd53e8000, 0x019db1de))
+#define SYS2UNIX_TIME(lo, hi)                                                  \
+	((time_t)((MAKEUQUAD((lo), (hi)) - EPOCH_DIFF) / RATE_DIFF))
+
+/* Visual Studio 6 does not know __func__ or __FUNCTION__
+ * The rest of MS compilers use __FUNCTION__, not C99 __func__
+ * Also use _strtoui64 on modern M$ compilers */
+#if defined(_MSC_VER)
+#if (_MSC_VER < 1300)
+#define STRX(x) #x
+#define STR(x) STRX(x)
+#define __func__ __FILE__ ":" STR(__LINE__)
+#define strtoull(x, y, z) ((unsigned __int64)_atoi64(x))
+#define strtoll(x, y, z) (_atoi64(x))
+#else
+#define __func__ __FUNCTION__
+#define strtoull(x, y, z) (_strtoui64(x, y, z))
+#define strtoll(x, y, z) (_strtoi64(x, y, z))
+#endif
+#endif /* _MSC_VER */
+
+#define ERRNO ((int)(GetLastError()))
+#define NO_SOCKLEN_T
+
+#if defined(_WIN64) || defined(__MINGW64__)
+#define SSL_LIB "ssleay64.dll"
+#define CRYPTO_LIB "libeay64.dll"
+#else
+#define SSL_LIB "ssleay32.dll"
+#define CRYPTO_LIB "libeay32.dll"
+#endif
+
+#define O_NONBLOCK (0)
+#ifndef W_OK
+#define W_OK (2) /* http://msdn.microsoft.com/en-us/library/1w06ktdy.aspx */
+#endif
+#if !defined(EWOULDBLOCK)
+#define EWOULDBLOCK WSAEWOULDBLOCK
+#endif /* !EWOULDBLOCK */
+#define _POSIX_
+#define INT64_FMT "I64d"
+#define UINT64_FMT "I64u"
+
+#define WINCDECL __cdecl
+#define vsnprintf_impl _vsnprintf
+#define access _access
+#define mg_sleep(x) (Sleep(x))
+
+#define pipe(x) _pipe(x, MG_BUF_LEN, _O_BINARY)
+#ifndef popen
+#define popen(x, y) (_popen(x, y))
+#endif
+#ifndef pclose
+#define pclose(x) (_pclose(x))
+#endif
+#define close(x) (_close(x))
+#define dlsym(x, y) (GetProcAddress((HINSTANCE)(x), (y)))
+#define RTLD_LAZY (0)
+#define fseeko(x, y, z) ((_lseeki64(_fileno(x), (y), (z)) == -1) ? -1 : 0)
+#define fdopen(x, y) (_fdopen((x), (y)))
+#define write(x, y, z) (_write((x), (y), (unsigned)z))
+#define read(x, y, z) (_read((x), (y), (unsigned)z))
+#define flockfile(x) (EnterCriticalSection(&global_log_file_lock))
+#define funlockfile(x) (LeaveCriticalSection(&global_log_file_lock))
+#define sleep(x) (Sleep((x)*1000))
+#define rmdir(x) (_rmdir(x))
+#define timegm(x) (_mkgmtime(x))
+
+#if !defined(fileno)
+#define fileno(x) (_fileno(x))
+#endif /* !fileno MINGW #defines fileno */
+
+typedef HANDLE pthread_mutex_t;
+typedef DWORD pthread_key_t;
+typedef HANDLE pthread_t;
+typedef struct {
+	CRITICAL_SECTION threadIdSec;
+	struct mg_workerTLS *waiting_thread; /* The chain of threads */
+} pthread_cond_t;
+
+#ifndef __clockid_t_defined
+typedef DWORD clockid_t;
+#endif
+#ifndef CLOCK_MONOTONIC
+#define CLOCK_MONOTONIC (1)
+#endif
+#ifndef CLOCK_REALTIME
+#define CLOCK_REALTIME (2)
+#endif
+
+#if defined(_MSC_VER) && (_MSC_VER >= 1900)
+#define _TIMESPEC_DEFINED
+#endif
+#ifndef _TIMESPEC_DEFINED
+struct timespec {
+	time_t tv_sec; /* seconds */
+	long tv_nsec;  /* nanoseconds */
+};
+#endif
+
+#if !defined(WIN_PTHREADS_TIME_H)
+#define MUST_IMPLEMENT_CLOCK_GETTIME
+#endif
+
+#ifdef MUST_IMPLEMENT_CLOCK_GETTIME
+#define clock_gettime mg_clock_gettime
+static int
+clock_gettime(clockid_t clk_id, struct timespec *tp)
+{
+	FILETIME ft;
+	ULARGE_INTEGER li;
+	BOOL ok = FALSE;
+	double d;
+	static double perfcnt_per_sec = 0.0;
+
+	if (tp) {
+		memset(tp, 0, sizeof(*tp));
+		if (clk_id == CLOCK_REALTIME) {
+			GetSystemTimeAsFileTime(&ft);
+			li.LowPart = ft.dwLowDateTime;
+			li.HighPart = ft.dwHighDateTime;
+			li.QuadPart -= 116444736000000000; /* 1.1.1970 in filedate */
+			tp->tv_sec = (time_t)(li.QuadPart / 10000000);
+			tp->tv_nsec = (long)(li.QuadPart % 10000000) * 100;
+			ok = TRUE;
+		} else if (clk_id == CLOCK_MONOTONIC) {
+			if (perfcnt_per_sec == 0.0) {
+				QueryPerformanceFrequency((LARGE_INTEGER *)&li);
+				perfcnt_per_sec = 1.0 / li.QuadPart;
+			}
+			if (perfcnt_per_sec != 0.0) {
+				QueryPerformanceCounter((LARGE_INTEGER *)&li);
+				d = li.QuadPart * perfcnt_per_sec;
+				tp->tv_sec = (time_t)d;
+				d -= tp->tv_sec;
+				tp->tv_nsec = (long)(d * 1.0E9);
+				ok = TRUE;
+			}
+		}
+	}
+
+	return ok ? 0 : -1;
+}
+#endif
+
+
+#define pid_t HANDLE /* MINGW typedefs pid_t to int. Using #define here. */
+
+static int pthread_mutex_lock(pthread_mutex_t *);
+static int pthread_mutex_unlock(pthread_mutex_t *);
+static void path_to_unicode(const struct mg_connection *conn,
+                            const char *path,
+                            wchar_t *wbuf,
+                            size_t wbuf_len);
+
+/* All file operations need to be rewritten to solve #246. */
+
+#include "file_ops.inl"
+
+struct mg_file;
+
+static const char *
+mg_fgets(char *buf, size_t size, struct mg_file *filep, char **p);
+
+
+/* POSIX dirent interface */
+struct dirent {
+	char d_name[PATH_MAX];
+};
+
+typedef struct DIR {
+	HANDLE handle;
+	WIN32_FIND_DATAW info;
+	struct dirent result;
+} DIR;
+
+#if defined(_WIN32) && !defined(POLLIN)
+#ifndef HAVE_POLL
+struct pollfd {
+	SOCKET fd;
+	short events;
+	short revents;
+};
+#define POLLIN (0x0300)
+#endif
+#endif
+
+/* Mark required libraries */
+#if defined(_MSC_VER)
+#pragma comment(lib, "Ws2_32.lib")
+#endif
+
+#else /* defined(_WIN32) && !defined(__SYMBIAN32__) -                          \
+         WINDOWS / UNIX include block */
+
+#include <sys/wait.h>
+#include <sys/socket.h>
+#include <sys/poll.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <sys/time.h>
+#include <sys/utsname.h>
+#include <stdint.h>
+#include <inttypes.h>
+#include <netdb.h>
+#include <netinet/tcp.h>
+typedef const void *SOCK_OPT_TYPE;
+
+#if defined(ANDROID)
+typedef unsigned short int in_port_t;
+#endif
+
+#include <pwd.h>
+#include <unistd.h>
+#include <grp.h>
+#include <dirent.h>
+#define vsnprintf_impl vsnprintf
+
+#if !defined(NO_SSL_DL) && !defined(NO_SSL)
+#include <dlfcn.h>
+#endif
+#include <pthread.h>
+#if defined(__MACH__)
+#define SSL_LIB "libssl.dylib"
+#define CRYPTO_LIB "libcrypto.dylib"
+#else
+#if !defined(SSL_LIB)
+#define SSL_LIB "libssl.so"
+#endif
+#if !defined(CRYPTO_LIB)
+#define CRYPTO_LIB "libcrypto.so"
+#endif
+#endif
+#ifndef O_BINARY
+#define O_BINARY (0)
+#endif /* O_BINARY */
+#define closesocket(a) (close(a))
+#define mg_mkdir(conn, path, mode) (mkdir(path, mode))
+#define mg_remove(conn, x) (remove(x))
+#define mg_sleep(x) (usleep((x)*1000))
+#define mg_opendir(conn, x) (opendir(x))
+#define mg_closedir(x) (closedir(x))
+#define mg_readdir(x) (readdir(x))
+#define ERRNO (errno)
+#define INVALID_SOCKET (-1)
+#define INT64_FMT PRId64
+#define UINT64_FMT PRIu64
+typedef int SOCKET;
+#define WINCDECL
+
+#if defined(__hpux)
+/* HPUX 11 does not have monotonic, fall back to realtime */
+#ifndef CLOCK_MONOTONIC
+#define CLOCK_MONOTONIC CLOCK_REALTIME
+#endif
+
+/* HPUX defines socklen_t incorrectly as size_t which is 64bit on
+ * Itanium.  Without defining _XOPEN_SOURCE or _XOPEN_SOURCE_EXTENDED
+ * the prototypes use int* rather than socklen_t* which matches the
+ * actual library expectation.  When called with the wrong size arg
+ * accept() returns a zero client inet addr and check_acl() always
+ * fails.  Since socklen_t is widely used below, just force replace
+ * their typedef with int. - DTL
+ */
+#define socklen_t int
+#endif /* hpux */
+
+#endif /* defined(_WIN32) && !defined(__SYMBIAN32__) -                         \
+          WINDOWS / UNIX include block */
+
+/* va_copy should always be a macro, C99 and C++11 - DTL */
+#ifndef va_copy
+#define va_copy(x, y) ((x) = (y))
+#endif
+
+#ifdef _WIN32
+/* Create substitutes for POSIX functions in Win32. */
+
+#if defined(__MINGW32__)
+/* Show no warning in case system functions are not used. */
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wunused-function"
+#endif
+
+
+static CRITICAL_SECTION global_log_file_lock;
+static DWORD
+pthread_self(void)
+{
+	return GetCurrentThreadId();
+}
+
+
+static int
+pthread_key_create(
+    pthread_key_t *key,
+    void (*_ignored)(void *) /* destructor not supported for Windows */
+    )
+{
+	(void)_ignored;
+
+	if ((key != 0)) {
+		*key = TlsAlloc();
+		return (*key != TLS_OUT_OF_INDEXES) ? 0 : -1;
+	}
+	return -2;
+}
+
+
+static int
+pthread_key_delete(pthread_key_t key)
+{
+	return TlsFree(key) ? 0 : 1;
+}
+
+
+static int
+pthread_setspecific(pthread_key_t key, void *value)
+{
+	return TlsSetValue(key, value) ? 0 : 1;
+}
+
+
+static void *
+pthread_getspecific(pthread_key_t key)
+{
+	return TlsGetValue(key);
+}
+
+#if defined(__MINGW32__)
+/* Enable unused function warning again */
+#pragma GCC diagnostic pop
+#endif
+
+static struct pthread_mutex_undefined_struct *pthread_mutex_attr = NULL;
+#else
+static pthread_mutexattr_t pthread_mutex_attr;
+#endif /* _WIN32 */
+
+
+#define PASSWORDS_FILE_NAME ".htpasswd"
+#define CGI_ENVIRONMENT_SIZE (4096)
+#define MAX_CGI_ENVIR_VARS (256)
+#define MG_BUF_LEN (8192)
+
+#ifndef MAX_REQUEST_SIZE
+#define MAX_REQUEST_SIZE (16384)
+#endif
+
+mg_static_assert(MAX_REQUEST_SIZE >= 256,
+                 "request size length must be a positive number");
+
+#define ARRAY_SIZE(array) (sizeof(array) / sizeof(array[0]))
+
+
+#if defined(_WIN32_WCE)
+/* Create substitutes for POSIX functions in Win32. */
+
+#if defined(__MINGW32__)
+/* Show no warning in case system functions are not used. */
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wunused-function"
+#endif
+
+
+static time_t
+time(time_t *ptime)
+{
+	time_t t;
+	SYSTEMTIME st;
+	FILETIME ft;
+
+	GetSystemTime(&st);
+	SystemTimeToFileTime(&st, &ft);
+	t = SYS2UNIX_TIME(ft.dwLowDateTime, ft.dwHighDateTime);
+
+	if (ptime != NULL) {
+		*ptime = t;
+	}
+
+	return t;
+}
+
+
+static struct tm *
+localtime_s(const time_t *ptime, struct tm *ptm)
+{
+	int64_t t = ((int64_t)*ptime) * RATE_DIFF + EPOCH_DIFF;
+	FILETIME ft, lft;
+	SYSTEMTIME st;
+	TIME_ZONE_INFORMATION tzinfo;
+
+	if (ptm == NULL) {
+		return NULL;
+	}
+
+	*(int64_t *)&ft = t;
+	FileTimeToLocalFileTime(&ft, &lft);
+	FileTimeToSystemTime(&lft, &st);
+	ptm->tm_year = st.wYear - 1900;
+	ptm->tm_mon = st.wMonth - 1;
+	ptm->tm_wday = st.wDayOfWeek;
+	ptm->tm_mday = st.wDay;
+	ptm->tm_hour = st.wHour;
+	ptm->tm_min = st.wMinute;
+	ptm->tm_sec = st.wSecond;
+	ptm->tm_yday = 0; /* hope nobody uses this */
+	ptm->tm_isdst =
+	    (GetTimeZoneInformation(&tzinfo) == TIME_ZONE_ID_DAYLIGHT) ? 1 : 0;
+
+	return ptm;
+}
+
+
+static struct tm *
+gmtime_s(const time_t *ptime, struct tm *ptm)
+{
+	/* FIXME(lsm): fix this. */
+	return localtime_s(ptime, ptm);
+}
+
+
+static int mg_atomic_inc(volatile int *addr);
+static struct tm tm_array[MAX_WORKER_THREADS];
+static int tm_index = 0;
+
+
+static struct tm *
+localtime(const time_t *ptime)
+{
+	int i = mg_atomic_inc(&tm_index) % (sizeof(tm_array) / sizeof(tm_array[0]));
+	return localtime_s(ptime, tm_array + i);
+}
+
+
+static struct tm *
+gmtime(const time_t *ptime)
+{
+	int i = mg_atomic_inc(&tm_index) % ARRAY_SIZE(tm_array);
+	return gmtime_s(ptime, tm_array + i);
+}
+
+
+static size_t
+strftime(char *dst, size_t dst_size, const char *fmt, const struct tm *tm)
+{
+	/* TODO: (void)mg_snprintf(NULL, dst, dst_size, "implement strftime()
+	 * for WinCE"); */
+	return 0;
+}
+
+#define _beginthreadex(psec, stack, func, prm, flags, ptid)                    \
+	(uintptr_t) CreateThread(psec, stack, func, prm, flags, ptid)
+
+#define remove(f) mg_remove(NULL, f)
+
+static int
+rename(const char *a, const char *b)
+{
+	wchar_t wa[PATH_MAX];
+	wchar_t wb[PATH_MAX];
+	path_to_unicode(NULL, a, wa, ARRAY_SIZE(wa));
+	path_to_unicode(NULL, b, wb, ARRAY_SIZE(wb));
+
+	return MoveFileW(wa, wb) ? 0 : -1;
+}
+
+struct stat {
+	int64_t st_size;
+	time_t st_mtime;
+};
+
+static int
+stat(const char *name, struct stat *st)
+{
+	wchar_t wbuf[PATH_MAX];
+	WIN32_FILE_ATTRIBUTE_DATA attr;
+	time_t creation_time, write_time;
+
+	path_to_unicode(NULL, name, wbuf, ARRAY_SIZE(wbuf));
+	memset(&attr, 0, sizeof(attr));
+
+	GetFileAttributesExW(wbuf, GetFileExInfoStandard, &attr);
+	st->st_size =
+	    (((int64_t)attr.nFileSizeHigh) << 32) + (int64_t)attr.nFileSizeLow;
+
+	write_time = SYS2UNIX_TIME(attr.ftLastWriteTime.dwLowDateTime,
+	                           attr.ftLastWriteTime.dwHighDateTime);
+	creation_time = SYS2UNIX_TIME(attr.ftCreationTime.dwLowDateTime,
+	                              attr.ftCreationTime.dwHighDateTime);
+
+	if (creation_time > write_time) {
+		st->st_mtime = creation_time;
+	} else {
+		st->st_mtime = write_time;
+	}
+	return 0;
+}
+
+#define access(x, a) 1 /* not required anyway */
+
+/* WinCE-TODO: define stat, remove, rename, _rmdir, _lseeki64 */
+/* Values from errno.h in Windows SDK (Visual Studio). */
+#define EEXIST 17
+#define EACCES 13
+#define ENOENT 2
+
+#if defined(__MINGW32__)
+/* Enable unused function warning again */
+#pragma GCC diagnostic pop
+#endif
+
+#endif /* defined(_WIN32_WCE) */
+
+
+static int
+mg_atomic_inc(volatile int *addr)
+{
+	int ret;
+#if defined(_WIN32) && !defined(__SYMBIAN32__)
+	/* Depending on the SDK, this function uses either
+	 * (volatile unsigned int *) or (volatile LONG *),
+	 * so whatever you use, the other SDK is likely to raise a warning. */
+	ret = InterlockedIncrement((volatile long *)addr);
+#elif defined(__GNUC__)                                                        \
+    && ((__GNUC__ > 4) || ((__GNUC__ == 4) && (__GNUC_MINOR__ > 0)))
+	ret = __sync_add_and_fetch(addr, 1);
+#else
+	ret = (++(*addr));
+#endif
+	return ret;
+}
+
+
+static int
+mg_atomic_dec(volatile int *addr)
+{
+	int ret;
+#if defined(_WIN32) && !defined(__SYMBIAN32__)
+	/* Depending on the SDK, this function uses either
+	 * (volatile unsigned int *) or (volatile LONG *),
+	 * so whatever you use, the other SDK is likely to raise a warning. */
+	ret = InterlockedDecrement((volatile long *)addr);
+#elif defined(__GNUC__)                                                        \
+    && ((__GNUC__ > 4) || ((__GNUC__ == 4) && (__GNUC_MINOR__ > 0)))
+	ret = __sync_sub_and_fetch(addr, 1);
+#else
+	ret = (--(*addr));
+#endif
+	return ret;
+}
+
+
+#if defined(MEMORY_DEBUGGING)
+static unsigned long mg_memory_debug_blockCount = 0;
+static unsigned long mg_memory_debug_totalMemUsed = 0;
+
+
+static void *
+mg_malloc_ex(size_t size, const char *file, unsigned line)
+{
+	void *data = malloc(size + sizeof(size_t));
+	void *memory = 0;
+	char mallocStr[256];
+
+	if (data) {
+		*(size_t *)data = size;
+		mg_memory_debug_totalMemUsed += size;
+		mg_memory_debug_blockCount++;
+		memory = (void *)(((char *)data) + sizeof(size_t));
+	}
+
+	sprintf(mallocStr,
+	        "MEM: %p %5lu alloc   %7lu %4lu --- %s:%u\n",
+	        memory,
+	        (unsigned long)size,
+	        mg_memory_debug_totalMemUsed,
+	        mg_memory_debug_blockCount,
+	        file,
+	        line);
+#if defined(_WIN32)
+	OutputDebugStringA(mallocStr);
+#else
+	DEBUG_TRACE("%s", mallocStr);
+#endif
+
+	return memory;
+}
+
+
+static void *
+mg_calloc_ex(size_t count, size_t size, const char *file, unsigned line)
+{
+	void *data = mg_malloc_ex(size * count, file, line);
+	if (data) {
+		memset(data, 0, size * count);
+	}
+	return data;
+}
+
+
+static void
+mg_free_ex(void *memory, const char *file, unsigned line)
+{
+	char mallocStr[256];
+	void *data = (void *)(((char *)memory) - sizeof(size_t));
+	size_t size;
+
+	if (memory) {
+		size = *(size_t *)data;
+		mg_memory_debug_totalMemUsed -= size;
+		mg_memory_debug_blockCount--;
+		sprintf(mallocStr,
+		        "MEM: %p %5lu free    %7lu %4lu --- %s:%u\n",
+		        memory,
+		        (unsigned long)size,
+		        mg_memory_debug_totalMemUsed,
+		        mg_memory_debug_blockCount,
+		        file,
+		        line);
+#if defined(_WIN32)
+		OutputDebugStringA(mallocStr);
+#else
+		DEBUG_TRACE("%s", mallocStr);
+#endif
+
+		free(data);
+	}
+}
+
+
+static void *
+mg_realloc_ex(void *memory, size_t newsize, const char *file, unsigned line)
+{
+	char mallocStr[256];
+	void *data;
+	void *_realloc;
+	size_t oldsize;
+
+	if (newsize) {
+		if (memory) {
+			data = (void *)(((char *)memory) - sizeof(size_t));
+			oldsize = *(size_t *)data;
+			_realloc = realloc(data, newsize + sizeof(size_t));
+			if (_realloc) {
+				data = _realloc;
+				mg_memory_debug_totalMemUsed -= oldsize;
+				sprintf(mallocStr,
+				        "MEM: %p %5lu r-free  %7lu %4lu --- %s:%u\n",
+				        memory,
+				        (unsigned long)oldsize,
+				        mg_memory_debug_totalMemUsed,
+				        mg_memory_debug_blockCount,
+				        file,
+				        line);
+#if defined(_WIN32)
+				OutputDebugStringA(mallocStr);
+#else
+				DEBUG_TRACE("%s", mallocStr);
+#endif
+				mg_memory_debug_totalMemUsed += newsize;
+				sprintf(mallocStr,
+				        "MEM: %p %5lu r-alloc %7lu %4lu --- %s:%u\n",
+				        memory,
+				        (unsigned long)newsize,
+				        mg_memory_debug_totalMemUsed,
+				        mg_memory_debug_blockCount,
+				        file,
+				        line);
+#if defined(_WIN32)
+				OutputDebugStringA(mallocStr);
+#else
+				DEBUG_TRACE("%s", mallocStr);
+#endif
+				*(size_t *)data = newsize;
+				data = (void *)(((char *)data) + sizeof(size_t));
+			} else {
+#if defined(_WIN32)
+				OutputDebugStringA("MEM: realloc failed\n");
+#else
+				DEBUG_TRACE("%s", "MEM: realloc failed\n");
+#endif
+				return _realloc;
+			}
+		} else {
+			data = mg_malloc_ex(newsize, file, line);
+		}
+	} else {
+		data = 0;
+		mg_free_ex(memory, file, line);
+	}
+
+	return data;
+}
+
+#define mg_malloc(a) mg_malloc_ex(a, __FILE__, __LINE__)
+#define mg_calloc(a, b) mg_calloc_ex(a, b, __FILE__, __LINE__)
+#define mg_realloc(a, b) mg_realloc_ex(a, b, __FILE__, __LINE__)
+#define mg_free(a) mg_free_ex(a, __FILE__, __LINE__)
+
+#else
+
+static __inline void *
+mg_malloc(size_t a)
+{
+	return malloc(a);
+}
+
+static __inline void *
+mg_calloc(size_t a, size_t b)
+{
+	return calloc(a, b);
+}
+
+static __inline void *
+mg_realloc(void *a, size_t b)
+{
+	return realloc(a, b);
+}
+
+static __inline void
+mg_free(void *a)
+{
+	free(a);
+}
+
+#endif
+
+
+static void mg_vsnprintf(const struct mg_connection *conn,
+                         int *truncated,
+                         char *buf,
+                         size_t buflen,
+                         const char *fmt,
+                         va_list ap);
+
+static void mg_snprintf(const struct mg_connection *conn,
+                        int *truncated,
+                        char *buf,
+                        size_t buflen,
+                        PRINTF_FORMAT_STRING(const char *fmt),
+                        ...) PRINTF_ARGS(5, 6);
+
+/* This following lines are just meant as a reminder to use the mg-functions
+ * for memory management */
+#ifdef malloc
+#undef malloc
+#endif
+#ifdef calloc
+#undef calloc
+#endif
+#ifdef realloc
+#undef realloc
+#endif
+#ifdef free
+#undef free
+#endif
+#ifdef snprintf
+#undef snprintf
+#endif
+#ifdef vsnprintf
+#undef vsnprintf
+#endif
+#define malloc DO_NOT_USE_THIS_FUNCTION__USE_mg_malloc
+#define calloc DO_NOT_USE_THIS_FUNCTION__USE_mg_calloc
+#define realloc DO_NOT_USE_THIS_FUNCTION__USE_mg_realloc
+#define free DO_NOT_USE_THIS_FUNCTION__USE_mg_free
+#define snprintf DO_NOT_USE_THIS_FUNCTION__USE_mg_snprintf
+#ifdef _WIN32 /* vsnprintf must not be used in any system, * \ \ \             \
+               * but this define only works well for Windows. */
+#define vsnprintf DO_NOT_USE_THIS_FUNCTION__USE_mg_vsnprintf
+#endif
+
+
+static pthread_key_t sTlsKey; /* Thread local storage index */
+static int sTlsInit = 0;
+static int thread_idx_max = 0;
+
+
+struct mg_workerTLS {
+	int is_master;
+	unsigned long thread_idx;
+#if defined(_WIN32) && !defined(__SYMBIAN32__)
+	HANDLE pthread_cond_helper_mutex;
+	struct mg_workerTLS *next_waiting_thread;
+#endif
+};
+
+
+#if defined(__GNUC__) || defined(__MINGW32__)
+/* Show no warning in case system functions are not used. */
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wunused-function"
+#endif
+#if defined(__clang__)
+/* Show no warning in case system functions are not used. */
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wunused-function"
+#endif
+
+
+/* Get a unique thread ID as unsigned long, independent from the data type
+ * of thread IDs defined by the operating system API.
+ * If two calls to mg_current_thread_id  return the same value, they calls
+ * are done from the same thread. If they return different values, they are
+ * done from different threads. (Provided this function is used in the same
+ * process context and threads are not repeatedly created and deleted, but
+ * CivetWeb does not do that).
+ * This function must match the signature required for SSL id callbacks:
+ * CRYPTO_set_id_callback
+ */
+static unsigned long
+mg_current_thread_id(void)
+{
+#ifdef _WIN32
+	return GetCurrentThreadId();
+#else
+
+#ifdef __clang__
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wunreachable-code"
+/* For every compiler, either "sizeof(pthread_t) > sizeof(unsigned long)"
+ * or not, so one of the two conditions will be unreachable by construction.
+ * Unfortunately the C standard does not define a way to check this at
+ * compile time, since the #if preprocessor conditions can not use the sizeof
+ * operator as an argument. */
+#endif
+
+	if (sizeof(pthread_t) > sizeof(unsigned long)) {
+		/* This is the problematic case for CRYPTO_set_id_callback:
+		 * The OS pthread_t can not be cast to unsigned long. */
+		struct mg_workerTLS *tls =
+		    (struct mg_workerTLS *)pthread_getspecific(sTlsKey);
+		if (tls == NULL) {
+			/* SSL called from an unknown thread: Create some thread index.
+			 */
+			tls = (struct mg_workerTLS *)mg_malloc(sizeof(struct mg_workerTLS));
+			tls->is_master = -2; /* -2 means "3rd party thread" */
+			tls->thread_idx = (unsigned)mg_atomic_inc(&thread_idx_max);
+			pthread_setspecific(sTlsKey, tls);
+		}
+		return tls->thread_idx;
+	} else {
+		/* pthread_t may be any data type, so a simple cast to unsigned long
+		 * can rise a warning/error, depending on the platform.
+		 * Here memcpy is used as an anything-to-anything cast. */
+		unsigned long ret = 0;
+		pthread_t t = pthread_self();
+		memcpy(&ret, &t, sizeof(pthread_t));
+		return ret;
+	}
+
+#ifdef __clang__
+#pragma clang diagnostic pop
+#endif
+
+#endif
+}
+
+
+#if defined(__GNUC__)
+/* Show no warning in case system functions are not used. */
+#pragma GCC diagnostic pop
+#endif
+#if defined(__clang__)
+/* Show no warning in case system functions are not used. */
+#pragma clang diagnostic pop
+#endif
+
+
+#if !defined(DEBUG_TRACE)
+#if defined(DEBUG)
+static void DEBUG_TRACE_FUNC(const char *func,
+                             unsigned line,
+                             PRINTF_FORMAT_STRING(const char *fmt),
+                             ...) PRINTF_ARGS(3, 4);
+
+static void
+DEBUG_TRACE_FUNC(const char *func, unsigned line, const char *fmt, ...)
+{
+	va_list args;
+	struct timespec tsnow;
+	uint64_t nsnow;
+	static uint64_t nslast;
+
+	/* Get some operating system independent thread id */
+	unsigned long thread_id = mg_current_thread_id();
+
+	clock_gettime(CLOCK_REALTIME, &tsnow);
+	nsnow = (((uint64_t)tsnow.tv_sec) * 1000000000) + (uint64_t)tsnow.tv_nsec;
+
+	flockfile(stdout);
+	printf("*** %lu.%09lu %12" INT64_FMT " %lu %s:%u: ",
+	       (unsigned long)tsnow.tv_sec,
+	       (unsigned long)tsnow.tv_nsec,
+	       nsnow - nslast,
+	       thread_id,
+	       func,
+	       line);
+	va_start(args, fmt);
+	vprintf(fmt, args);
+	va_end(args);
+	putchar('\n');
+	fflush(stdout);
+	funlockfile(stdout);
+	nslast = nsnow;
+}
+
+#define DEBUG_TRACE(fmt, ...)                                                  \
+	DEBUG_TRACE_FUNC(__func__, __LINE__, fmt, __VA_ARGS__)
+
+#else
+#define DEBUG_TRACE(fmt, ...)                                                  \
+	do {                                                                       \
+	} while (0)
+#endif /* DEBUG */
+#endif /* DEBUG_TRACE */
+
+
+#define MD5_STATIC static
+#include "md5.inl"
+
+/* Darwin prior to 7.0 and Win32 do not have socklen_t */
+#ifdef NO_SOCKLEN_T
+typedef int socklen_t;
+#endif /* NO_SOCKLEN_T */
+#define _DARWIN_UNLIMITED_SELECT
+
+#define IP_ADDR_STR_LEN (50) /* IPv6 hex string is 46 chars */
+
+#if !defined(MSG_NOSIGNAL)
+#define MSG_NOSIGNAL (0)
+#endif
+
+#if !defined(SOMAXCONN)
+#define SOMAXCONN (100)
+#endif
+
+/* Size of the accepted socket queue */
+#if !defined(MGSQLEN)
+#define MGSQLEN (20)
+#endif
+
+
+#if defined(NO_SSL)
+typedef struct SSL SSL; /* dummy for SSL argument to push/pull */
+typedef struct SSL_CTX SSL_CTX;
+#else
+#if defined(NO_SSL_DL)
+#include <openssl/ssl.h>
+#include <openssl/err.h>
+#include <openssl/crypto.h>
+#include <openssl/x509.h>
+#include <openssl/pem.h>
+#include <openssl/engine.h>
+#include <openssl/conf.h>
+#include <openssl/dh.h>
+#else
+/* SSL loaded dynamically from DLL.
+ * I put the prototypes here to be independent from OpenSSL source
+ * installation. */
+
+typedef struct ssl_st SSL;
+typedef struct ssl_method_st SSL_METHOD;
+typedef struct ssl_ctx_st SSL_CTX;
+typedef struct x509_store_ctx_st X509_STORE_CTX;
+typedef struct x509_name X509_NAME;
+typedef struct asn1_integer ASN1_INTEGER;
+typedef struct evp_md EVP_MD;
+typedef struct x509 X509;
+
+
+#define SSL_CTRL_OPTIONS (32)
+#define SSL_CTRL_CLEAR_OPTIONS (77)
+#define SSL_CTRL_SET_ECDH_AUTO (94)
+
+#define SSL_VERIFY_NONE (0)
+#define SSL_VERIFY_PEER (1)
+#define SSL_VERIFY_FAIL_IF_NO_PEER_CERT (2)
+#define SSL_VERIFY_CLIENT_ONCE (4)
+#define SSL_OP_ALL ((long)(0x80000BFFUL))
+#define SSL_OP_NO_SSLv2 (0x01000000L)
+#define SSL_OP_NO_SSLv3 (0x02000000L)
+#define SSL_OP_NO_TLSv1 (0x04000000L)
+#define SSL_OP_NO_TLSv1_2 (0x08000000L)
+#define SSL_OP_NO_TLSv1_1 (0x10000000L)
+#define SSL_OP_SINGLE_DH_USE (0x00100000L)
+#define SSL_OP_CIPHER_SERVER_PREFERENCE (0x00400000L)
+#define SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION (0x00010000L)
+
+#define SSL_ERROR_NONE (0)
+#define SSL_ERROR_SSL (1)
+#define SSL_ERROR_WANT_READ (2)
+#define SSL_ERROR_WANT_WRITE (3)
+#define SSL_ERROR_WANT_X509_LOOKUP (4)
+#define SSL_ERROR_SYSCALL (5) /* see errno */
+#define SSL_ERROR_ZERO_RETURN (6)
+#define SSL_ERROR_WANT_CONNECT (7)
+#define SSL_ERROR_WANT_ACCEPT (8)
+
+
+struct ssl_func {
+	const char *name;  /* SSL function name */
+	void (*ptr)(void); /* Function pointer */
+};
+
+#define SSL_free (*(void (*)(SSL *))ssl_sw[0].ptr)
+#define SSL_accept (*(int (*)(SSL *))ssl_sw[1].ptr)
+#define SSL_connect (*(int (*)(SSL *))ssl_sw[2].ptr)
+#define SSL_read (*(int (*)(SSL *, void *, int))ssl_sw[3].ptr)
+#define SSL_write (*(int (*)(SSL *, const void *, int))ssl_sw[4].ptr)
+#define SSL_get_error (*(int (*)(SSL *, int))ssl_sw[5].ptr)
+#define SSL_set_fd (*(int (*)(SSL *, SOCKET))ssl_sw[6].ptr)
+#define SSL_new (*(SSL * (*)(SSL_CTX *))ssl_sw[7].ptr)
+#define SSL_CTX_new (*(SSL_CTX * (*)(SSL_METHOD *))ssl_sw[8].ptr)
+#define SSLv23_server_method (*(SSL_METHOD * (*)(void))ssl_sw[9].ptr)
+#define SSL_library_init (*(int (*)(void))ssl_sw[10].ptr)
+#define SSL_CTX_use_PrivateKey_file                                            \
+	(*(int (*)(SSL_CTX *, const char *, int))ssl_sw[11].ptr)
+#define SSL_CTX_use_certificate_file                                           \
+	(*(int (*)(SSL_CTX *, const char *, int))ssl_sw[12].ptr)
+#define SSL_CTX_set_default_passwd_cb                                          \
+	(*(void (*)(SSL_CTX *, mg_callback_t))ssl_sw[13].ptr)
+#define SSL_CTX_free (*(void (*)(SSL_CTX *))ssl_sw[14].ptr)
+#define SSL_load_error_strings (*(void (*)(void))ssl_sw[15].ptr)
+#define SSL_CTX_use_certificate_chain_file                                     \
+	(*(int (*)(SSL_CTX *, const char *))ssl_sw[16].ptr)
+#define SSLv23_client_method (*(SSL_METHOD * (*)(void))ssl_sw[17].ptr)
+#define SSL_pending (*(int (*)(SSL *))ssl_sw[18].ptr)
+#define SSL_CTX_set_verify                                                     \
+	(*(void (*)(SSL_CTX *,                                                     \
+	            int,                                                           \
+	            int (*verify_callback)(int, X509_STORE_CTX *)))ssl_sw[19].ptr)
+#define SSL_shutdown (*(int (*)(SSL *))ssl_sw[20].ptr)
+#define SSL_CTX_load_verify_locations                                          \
+	(*(int (*)(SSL_CTX *, const char *, const char *))ssl_sw[21].ptr)
+#define SSL_CTX_set_default_verify_paths (*(int (*)(SSL_CTX *))ssl_sw[22].ptr)
+#define SSL_CTX_set_verify_depth (*(void (*)(SSL_CTX *, int))ssl_sw[23].ptr)
+#define SSL_get_peer_certificate (*(X509 * (*)(SSL *))ssl_sw[24].ptr)
+#define SSL_get_version (*(const char *(*)(SSL *))ssl_sw[25].ptr)
+#define SSL_get_current_cipher (*(SSL_CIPHER * (*)(SSL *))ssl_sw[26].ptr)
+#define SSL_CIPHER_get_name                                                    \
+	(*(const char *(*)(const SSL_CIPHER *))ssl_sw[27].ptr)
+#define SSL_CTX_check_private_key (*(int (*)(SSL_CTX *))ssl_sw[28].ptr)
+#define SSL_CTX_set_session_id_context                                         \
+	(*(int (*)(SSL_CTX *, const unsigned char *, unsigned int))ssl_sw[29].ptr)
+#define SSL_CTX_ctrl (*(long (*)(SSL_CTX *, int, long, void *))ssl_sw[30].ptr)
+
+
+#define SSL_CTX_set_cipher_list                                                \
+	(*(int (*)(SSL_CTX *, const char *))ssl_sw[31].ptr)
+#define SSL_CTX_set_options(ctx, op)                                           \
+	SSL_CTX_ctrl((ctx), SSL_CTRL_OPTIONS, (op), NULL)
+#define SSL_CTX_clear_options(ctx, op)                                         \
+	SSL_CTX_ctrl((ctx), SSL_CTRL_CLEAR_OPTIONS, (op), NULL)
+#define SSL_CTX_set_ecdh_auto(ctx, onoff)                                      \
+	SSL_CTX_ctrl(ctx, SSL_CTRL_SET_ECDH_AUTO, onoff, NULL)
+
+#define X509_get_notBefore(x) ((x)->cert_info->validity->notBefore)
+#define X509_get_notAfter(x) ((x)->cert_info->validity->notAfter)
+
+
+#define CRYPTO_num_locks (*(int (*)(void))crypto_sw[0].ptr)
+#define CRYPTO_set_locking_callback                                            \
+	(*(void (*)(void (*)(int, int, const char *, int)))crypto_sw[1].ptr)
+#define CRYPTO_set_id_callback                                                 \
+	(*(void (*)(unsigned long (*)(void)))crypto_sw[2].ptr)
+#define ERR_get_error (*(unsigned long (*)(void))crypto_sw[3].ptr)
+#define ERR_error_string (*(char *(*)(unsigned long, char *))crypto_sw[4].ptr)
+#define ERR_remove_state (*(void (*)(unsigned long))crypto_sw[5].ptr)
+#define ERR_free_strings (*(void (*)(void))crypto_sw[6].ptr)
+#define ENGINE_cleanup (*(void (*)(void))crypto_sw[7].ptr)
+#define CONF_modules_unload (*(void (*)(int))crypto_sw[8].ptr)
+#define CRYPTO_cleanup_all_ex_data (*(void (*)(void))crypto_sw[9].ptr)
+#define EVP_cleanup (*(void (*)(void))crypto_sw[10].ptr)
+#define X509_free (*(void (*)(X509 *))crypto_sw[11].ptr)
+#define X509_get_subject_name (*(X509_NAME * (*)(X509 *))crypto_sw[12].ptr)
+#define X509_get_issuer_name (*(X509_NAME * (*)(X509 *))crypto_sw[13].ptr)
+#define X509_NAME_oneline                                                      \
+	(*(char *(*)(X509_NAME *, char *, int))crypto_sw[14].ptr)
+#define X509_get_serialNumber (*(ASN1_INTEGER * (*)(X509 *))crypto_sw[15].ptr)
+#define i2c_ASN1_INTEGER                                                       \
+	(*(int (*)(ASN1_INTEGER *, unsigned char **))crypto_sw[16].ptr)
+#define EVP_get_digestbyname                                                   \
+	(*(const EVP_MD *(*)(const char *))crypto_sw[17].ptr)
+#define ASN1_digest                                                            \
+	(*(int (*)(int (*)(),                                                      \
+	           const EVP_MD *,                                                 \
+	           char *,                                                         \
+	           unsigned char *,                                                \
+	           unsigned int *))crypto_sw[18].ptr)
+#define i2d_X509 (*(int (*)(X509 *, unsigned char **))crypto_sw[19].ptr)
+
+
+/* set_ssl_option() function updates this array.
+ * It loads SSL library dynamically and changes NULLs to the actual addresses
+ * of respective functions. The macros above (like SSL_connect()) are really
+ * just calling these functions indirectly via the pointer. */
+static struct ssl_func ssl_sw[] = {{"SSL_free", NULL},
+                                   {"SSL_accept", NULL},
+                                   {"SSL_connect", NULL},
+                                   {"SSL_read", NULL},
+                                   {"SSL_write", NULL},
+                                   {"SSL_get_error", NULL},
+                                   {"SSL_set_fd", NULL},
+                                   {"SSL_new", NULL},
+                                   {"SSL_CTX_new", NULL},
+                                   {"SSLv23_server_method", NULL},
+                                   {"SSL_library_init", NULL},
+                                   {"SSL_CTX_use_PrivateKey_file", NULL},
+                                   {"SSL_CTX_use_certificate_file", NULL},
+                                   {"SSL_CTX_set_default_passwd_cb", NULL},
+                                   {"SSL_CTX_free", NULL},
+                                   {"SSL_load_error_strings", NULL},
+                                   {"SSL_CTX_use_certificate_chain_file", NULL},
+                                   {"SSLv23_client_method", NULL},
+                                   {"SSL_pending", NULL},
+                                   {"SSL_CTX_set_verify", NULL},
+                                   {"SSL_shutdown", NULL},
+                                   {"SSL_CTX_load_verify_locations", NULL},
+                                   {"SSL_CTX_set_default_verify_paths", NULL},
+                                   {"SSL_CTX_set_verify_depth", NULL},
+                                   {"SSL_get_peer_certificate", NULL},
+                                   {"SSL_get_version", NULL},
+                                   {"SSL_get_current_cipher", NULL},
+                                   {"SSL_CIPHER_get_name", NULL},
+                                   {"SSL_CTX_check_private_key", NULL},
+                                   {"SSL_CTX_set_session_id_context", NULL},
+                                   {"SSL_CTX_ctrl", NULL},
+                                   {"SSL_CTX_set_cipher_list", NULL},
+                                   {NULL, NULL}};
+
+
+/* Similar array as ssl_sw. These functions could be located in different
+ * lib. */
+static struct ssl_func crypto_sw[] = {{"CRYPTO_num_locks", NULL},
+                                      {"CRYPTO_set_locking_callback", NULL},
+                                      {"CRYPTO_set_id_callback", NULL},
+                                      {"ERR_get_error", NULL},
+                                      {"ERR_error_string", NULL},
+                                      {"ERR_remove_state", NULL},
+                                      {"ERR_free_strings", NULL},
+                                      {"ENGINE_cleanup", NULL},
+                                      {"CONF_modules_unload", NULL},
+                                      {"CRYPTO_cleanup_all_ex_data", NULL},
+                                      {"EVP_cleanup", NULL},
+                                      {"X509_free", NULL},
+                                      {"X509_get_subject_name", NULL},
+                                      {"X509_get_issuer_name", NULL},
+                                      {"X509_NAME_oneline", NULL},
+                                      {"X509_get_serialNumber", NULL},
+                                      {"i2c_ASN1_INTEGER", NULL},
+                                      {"EVP_get_digestbyname", NULL},
+                                      {"ASN1_digest", NULL},
+                                      {"i2d_X509", NULL},
+                                      {NULL, NULL}};
+#endif /* NO_SSL_DL */
+#endif /* NO_SSL */
+
+
+#if !defined(NO_CACHING)
+static const char *month_names[] = {"Jan",
+                                    "Feb",
+                                    "Mar",
+                                    "Apr",
+                                    "May",
+                                    "Jun",
+                                    "Jul",
+                                    "Aug",
+                                    "Sep",
+                                    "Oct",
+                                    "Nov",
+                                    "Dec"};
+#endif /* !NO_CACHING */
+
+/* Unified socket address. For IPv6 support, add IPv6 address structure in the
+ * union u. */
+union usa {
+	struct sockaddr sa;
+	struct sockaddr_in sin;
+#if defined(USE_IPV6)
+	struct sockaddr_in6 sin6;
+#endif
+};
+
+/* Describes a string (chunk of memory). */
+struct vec {
+	const char *ptr;
+	size_t len;
+};
+
+struct mg_file_stat {
+	/* File properties filled by mg_stat: */
+	uint64_t size;
+	time_t last_modified;
+	int is_directory; /* Set to 1 if mg_stat is called for a directory */
+	int is_gzipped;   /* Set to 1 if the content is gzipped, in which
+	                   * case we need a "Content-Eencoding: gzip" header */
+	int location;     /* 0 = nowhere, 1 = on disk, 2 = in memory */
+};
+
+struct mg_file_in_memory {
+	char *p;
+	uint32_t pos;
+	char mode;
+};
+
+struct mg_file_access {
+	/* File properties filled by mg_fopen: */
+	FILE *fp;
+	/* TODO (low): Replace "membuf" implementation by a "file in memory"
+	 * support library. Use some struct mg_file_in_memory *mf; instead of
+	 * membuf char pointer. */
+	const char *membuf;
+};
+
+struct mg_file {
+	struct mg_file_stat stat;
+	struct mg_file_access access;
+};
+
+#define STRUCT_FILE_INITIALIZER                                                \
+	{                                                                          \
+		{                                                                      \
+			(uint64_t)0, (time_t)0, 0, 0, 0                                    \
+		}                                                                      \
+		,                                                                      \
+		{                                                                      \
+			(FILE *) NULL, (const char *)NULL                                  \
+		}                                                                      \
+	}
+
+/* Describes listening socket, or socket which was accept()-ed by the master
+ * thread and queued for future handling by the worker thread. */
+struct socket {
+	SOCKET sock;             /* Listening socket */
+	union usa lsa;           /* Local socket address */
+	union usa rsa;           /* Remote socket address */
+	unsigned char is_ssl;    /* Is port SSL-ed */
+	unsigned char ssl_redir; /* Is port supposed to redirect everything to SSL
+	                          * port */
+	unsigned char in_use;    /* Is valid */
+};
+
+/* NOTE(lsm): this enum shoulds be in sync with the config_options below. */
+enum {
+	CGI_EXTENSIONS,
+	CGI_ENVIRONMENT,
+	PUT_DELETE_PASSWORDS_FILE,
+	CGI_INTERPRETER,
+	PROTECT_URI,
+	AUTHENTICATION_DOMAIN,
+	SSI_EXTENSIONS,
+	THROTTLE,
+	ACCESS_LOG_FILE,
+	ENABLE_DIRECTORY_LISTING,
+	ERROR_LOG_FILE,
+	GLOBAL_PASSWORDS_FILE,
+	INDEX_FILES,
+	ENABLE_KEEP_ALIVE,
+	ACCESS_CONTROL_LIST,
+	EXTRA_MIME_TYPES,
+	LISTENING_PORTS,
+	DOCUMENT_ROOT,
+	SSL_CERTIFICATE,
+	NUM_THREADS,
+	RUN_AS_USER,
+	REWRITE,
+	HIDE_FILES,
+	REQUEST_TIMEOUT,
+	KEEP_ALIVE_TIMEOUT,
+	LINGER_TIMEOUT,
+	SSL_DO_VERIFY_PEER,
+	SSL_CA_PATH,
+	SSL_CA_FILE,
+	SSL_VERIFY_DEPTH,
+	SSL_DEFAULT_VERIFY_PATHS,
+	SSL_CIPHER_LIST,
+	SSL_PROTOCOL_VERSION,
+	SSL_SHORT_TRUST,
+
+#if defined(USE_WEBSOCKET)
+	WEBSOCKET_TIMEOUT,
+#endif
+
+	DECODE_URL,
+
+#if defined(USE_LUA)
+	LUA_PRELOAD_FILE,
+	LUA_SCRIPT_EXTENSIONS,
+	LUA_SERVER_PAGE_EXTENSIONS,
+#endif
+#if defined(USE_DUKTAPE)
+	DUKTAPE_SCRIPT_EXTENSIONS,
+#endif
+
+#if defined(USE_WEBSOCKET)
+	WEBSOCKET_ROOT,
+#endif
+#if defined(USE_LUA) && defined(USE_WEBSOCKET)
+	LUA_WEBSOCKET_EXTENSIONS,
+#endif
+
+	ACCESS_CONTROL_ALLOW_ORIGIN,
+	ERROR_PAGES,
+	CONFIG_TCP_NODELAY, /* Prepended CONFIG_ to avoid conflict with the
+                         * socket option typedef TCP_NODELAY. */
+#if !defined(NO_CACHING)
+	STATIC_FILE_MAX_AGE,
+#endif
+#if defined(__linux__)
+	ALLOW_SENDFILE_CALL,
+#endif
+#if defined(_WIN32)
+	CASE_SENSITIVE_FILES,
+#endif
+#if defined(USE_LUA)
+	LUA_BACKGROUND_SCRIPT,
+#endif
+
+	NUM_OPTIONS
+};
+
+
+/* Config option name, config types, default value */
+static struct mg_option config_options[] = {
+    {"cgi_pattern", CONFIG_TYPE_EXT_PATTERN, "**.cgi$|**.pl$|**.php$"},
+    {"cgi_environment", CONFIG_TYPE_STRING, NULL},
+    {"put_delete_auth_file", CONFIG_TYPE_FILE, NULL},
+    {"cgi_interpreter", CONFIG_TYPE_FILE, NULL},
+    {"protect_uri", CONFIG_TYPE_STRING, NULL},
+    {"authentication_domain", CONFIG_TYPE_STRING, "mydomain.com"},
+    {"ssi_pattern", CONFIG_TYPE_EXT_PATTERN, "**.shtml$|**.shtm$"},
+    {"throttle", CONFIG_TYPE_STRING, NULL},
+    {"access_log_file", CONFIG_TYPE_FILE, NULL},
+    {"enable_directory_listing", CONFIG_TYPE_BOOLEAN, "yes"},
+    {"error_log_file", CONFIG_TYPE_FILE, NULL},
+    {"global_auth_file", CONFIG_TYPE_FILE, NULL},
+    {"index_files",
+     CONFIG_TYPE_STRING,
+#ifdef USE_LUA
+     "index.xhtml,index.html,index.htm,index.lp,index.lsp,index.lua,index.cgi,"
+     "index.shtml,index.php"},
+#else
+     "index.xhtml,index.html,index.htm,index.cgi,index.shtml,index.php"},
+#endif
+    {"enable_keep_alive", CONFIG_TYPE_BOOLEAN, "no"},
+    {"access_control_list", CONFIG_TYPE_STRING, NULL},
+    {"extra_mime_types", CONFIG_TYPE_STRING, NULL},
+    {"listening_ports", CONFIG_TYPE_STRING, "8080"},
+    {"document_root", CONFIG_TYPE_DIRECTORY, NULL},
+    {"ssl_certificate", CONFIG_TYPE_FILE, NULL},
+    {"num_threads", CONFIG_TYPE_NUMBER, "50"},
+    {"run_as_user", CONFIG_TYPE_STRING, NULL},
+    {"url_rewrite_patterns", CONFIG_TYPE_STRING, NULL},
+    {"hide_files_patterns", CONFIG_TYPE_EXT_PATTERN, NULL},
+    {"request_timeout_ms", CONFIG_TYPE_NUMBER, "30000"},
+    {"keep_alive_timeout_ms", CONFIG_TYPE_NUMBER, "500"},
+    {"linger_timeout_ms", CONFIG_TYPE_NUMBER, NULL},
+    {"ssl_verify_peer", CONFIG_TYPE_BOOLEAN, "no"},
+    {"ssl_ca_path", CONFIG_TYPE_DIRECTORY, NULL},
+    {"ssl_ca_file", CONFIG_TYPE_FILE, NULL},
+    {"ssl_verify_depth", CONFIG_TYPE_NUMBER, "9"},
+    {"ssl_default_verify_paths", CONFIG_TYPE_BOOLEAN, "yes"},
+    {"ssl_cipher_list", CONFIG_TYPE_STRING, NULL},
+    {"ssl_protocol_version", CONFIG_TYPE_NUMBER, "0"},
+    {"ssl_short_trust", CONFIG_TYPE_BOOLEAN, "no"},
+#if defined(USE_WEBSOCKET)
+    {"websocket_timeout_ms", CONFIG_TYPE_NUMBER, "30000"},
+#endif
+    {"decode_url", CONFIG_TYPE_BOOLEAN, "yes"},
+
+#if defined(USE_LUA)
+    {"lua_preload_file", CONFIG_TYPE_FILE, NULL},
+    {"lua_script_pattern", CONFIG_TYPE_EXT_PATTERN, "**.lua$"},
+    {"lua_server_page_pattern", CONFIG_TYPE_EXT_PATTERN, "**.lp$|**.lsp$"},
+#endif
+#if defined(USE_DUKTAPE)
+    /* The support for duktape is still in alpha version state.
+     * The name of this config option might change. */
+    {"duktape_script_pattern", CONFIG_TYPE_EXT_PATTERN, "**.ssjs$"},
+#endif
+
+#if defined(USE_WEBSOCKET)
+    {"websocket_root", CONFIG_TYPE_DIRECTORY, NULL},
+#endif
+#if defined(USE_LUA) && defined(USE_WEBSOCKET)
+    {"lua_websocket_pattern", CONFIG_TYPE_EXT_PATTERN, "**.lua$"},
+#endif
+    {"access_control_allow_origin", CONFIG_TYPE_STRING, "*"},
+    {"error_pages", CONFIG_TYPE_DIRECTORY, NULL},
+    {"tcp_nodelay", CONFIG_TYPE_NUMBER, "0"},
+#if !defined(NO_CACHING)
+    {"static_file_max_age", CONFIG_TYPE_NUMBER, "3600"},
+#endif
+#if defined(__linux__)
+    {"allow_sendfile_call", CONFIG_TYPE_BOOLEAN, "yes"},
+#endif
+#if defined(_WIN32)
+    {"case_sensitive", CONFIG_TYPE_BOOLEAN, "no"},
+#endif
+#if defined(USE_LUA)
+    {"lua_background_script", CONFIG_TYPE_FILE, NULL},
+#endif
+
+    {NULL, CONFIG_TYPE_UNKNOWN, NULL}};
+
+/* Check if the config_options and the corresponding enum have compatible
+ * sizes. */
+mg_static_assert((sizeof(config_options) / sizeof(config_options[0]))
+                     == (NUM_OPTIONS + 1),
+                 "config_options and enum not sync");
+
+enum { REQUEST_HANDLER, WEBSOCKET_HANDLER, AUTH_HANDLER };
+
+struct mg_handler_info {
+	/* Name/Pattern of the URI. */
+	char *uri;
+	size_t uri_len;
+
+	/* handler type */
+	int handler_type;
+
+	/* Handler for http/https or authorization requests. */
+	mg_request_handler handler;
+
+	/* Handler for ws/wss (websocket) requests. */
+	mg_websocket_connect_handler connect_handler;
+	mg_websocket_ready_handler ready_handler;
+	mg_websocket_data_handler data_handler;
+	mg_websocket_close_handler close_handler;
+
+	/* accepted subprotocols for ws/wss requests. */
+	struct mg_websocket_subprotocols *subprotocols;
+
+	/* Handler for authorization requests */
+	mg_authorization_handler auth_handler;
+
+	/* User supplied argument for the handler function. */
+	void *cbdata;
+
+	/* next handler in a linked list */
+	struct mg_handler_info *next;
+};
+
+struct mg_context {
+	volatile int stop_flag;        /* Should we stop event loop */
+	SSL_CTX *ssl_ctx;              /* SSL context */
+	char *config[NUM_OPTIONS];     /* Civetweb configuration parameters */
+	struct mg_callbacks callbacks; /* User-defined callback function */
+	void *user_data;               /* User-defined data */
+	int context_type;              /* 1 = server context,
+	                                * 2 = ws/wss client context,
+	                                */
+
+	struct socket *listening_sockets;
+	struct pollfd *listening_socket_fds;
+	unsigned int num_listening_sockets;
+
+	pthread_mutex_t thread_mutex; /* Protects (max|num)_threads */
+
+#ifdef ALTERNATIVE_QUEUE
+	struct socket *client_socks;
+	void **client_wait_events;
+#else
+	struct socket queue[MGSQLEN]; /* Accepted sockets */
+	volatile int sq_head;         /* Head of the socket queue */
+	volatile int sq_tail;         /* Tail of the socket queue */
+	pthread_cond_t sq_full;       /* Signaled when socket is produced */
+	pthread_cond_t sq_empty;      /* Signaled when socket is consumed */
+#endif
+
+	pthread_t masterthreadid; /* The master thread ID */
+	unsigned int
+	    cfg_worker_threads;      /* The number of configured worker threads. */
+	pthread_t *worker_threadids; /* The worker thread IDs */
+	struct mg_connection *worker_connections; /* The connection struct, pre-
+	                                           * allocated for each worker */
+
+	time_t start_time;        /* Server start time, used for authentication */
+	uint64_t auth_nonce_mask; /* Mask for all nonce values */
+	pthread_mutex_t nonce_mutex; /* Protects nonce_count */
+	unsigned long nonce_count;   /* Used nonces, used for authentication */
+
+	char *systemName; /* What operating system is running */
+
+	/* linked list of uri handlers */
+	struct mg_handler_info *handlers;
+
+#if defined(USE_LUA) && defined(USE_WEBSOCKET)
+	/* linked list of shared lua websockets */
+	struct mg_shared_lua_websocket_list *shared_lua_websockets;
+#endif
+
+#if defined(USE_TIMERS)
+	struct ttimers *timers;
+#endif
+
+#if defined(USE_LUA)
+	void *lua_background_state;
+#endif
+};
+
+
+struct mg_connection {
+	struct mg_request_info request_info;
+	struct mg_context *ctx;
+	SSL *ssl;                 /* SSL descriptor */
+	SSL_CTX *client_ssl_ctx;  /* SSL context for client connections */
+	struct socket client;     /* Connected client */
+	time_t conn_birth_time;   /* Time (wall clock) when connection was
+	                           * established */
+	struct timespec req_time; /* Time (since system start) when the request
+	                           * was received */
+	int64_t num_bytes_sent;   /* Total bytes sent to client */
+	int64_t content_len;      /* Content-Length header value */
+	int64_t consumed_content; /* How many bytes of content have been read */
+	int is_chunked;           /* Transfer-Encoding is chunked: 0=no, 1=yes:
+	                           * data available, 2: all data read */
+	size_t chunk_remainder;   /* Unread data from the last chunk */
+	char *buf;                /* Buffer for received data */
+	char *path_info;          /* PATH_INFO part of the URL */
+
+	int must_close;       /* 1 if connection must be closed */
+	int in_error_handler; /* 1 if in handler for user defined error
+	                       * pages */
+	int handled_requests; /* Number of requests handled by this connection */
+	int buf_size;         /* Buffer size */
+	int request_len;      /* Size of the request + headers in a buffer */
+	int data_len;         /* Total size of data in a buffer */
+	int status_code;      /* HTTP reply status code, e.g. 200 */
+	int throttle;         /* Throttling, bytes/sec. <= 0 means no
+	                       * throttle */
+	time_t last_throttle_time;   /* Last time throttled data was sent */
+	int64_t last_throttle_bytes; /* Bytes sent this second */
+	pthread_mutex_t mutex;       /* Used by mg_(un)lock_connection to ensure
+	                              * atomic transmissions for websockets */
+#if defined(USE_LUA) && defined(USE_WEBSOCKET)
+	void *lua_websocket_state; /* Lua_State for a websocket connection */
+#endif
+
+	int thread_index; /* Thread index within ctx */
+};
+
+
+/* Directory entry */
+struct de {
+	struct mg_connection *conn;
+	char *file_name;
+	struct mg_file_stat file;
+};
+
+
+#if defined(USE_WEBSOCKET)
+static int is_websocket_protocol(const struct mg_connection *conn);
+#else
+#define is_websocket_protocol(conn) (0)
+#endif
+
+
+#if !defined(NO_THREAD_NAME)
+#if defined(_WIN32) && defined(_MSC_VER)
+/* Set the thread name for debugging purposes in Visual Studio
+ * http://msdn.microsoft.com/en-us/library/xcb2z8hs.aspx
+ */
+#pragma pack(push, 8)
+typedef struct tagTHREADNAME_INFO {
+	DWORD dwType;     /* Must be 0x1000. */
+	LPCSTR szName;    /* Pointer to name (in user addr space). */
+	DWORD dwThreadID; /* Thread ID (-1=caller thread). */
+	DWORD dwFlags;    /* Reserved for future use, must be zero. */
+} THREADNAME_INFO;
+#pragma pack(pop)
+
+#elif defined(__linux__)
+
+#include <sys/prctl.h>
+#include <sys/sendfile.h>
+#include <sys/eventfd.h>
+
+
+#if defined(ALTERNATIVE_QUEUE)
+
+static void *
+event_create(void)
+{
+	int ret = eventfd(0, EFD_CLOEXEC);
+	if (ret == -1) {
+		/* Linux uses -1 on error, Windows NULL. */
+		/* However, Linux does not return 0 on success either. */
+		return 0;
+	}
+	return (void *)ret;
+}
+
+
+static int
+event_wait(void *eventhdl)
+{
+	uint64_t u;
+	int s = (int)read((int)eventhdl, &u, sizeof(u));
+	if (s != sizeof(uint64_t)) {
+		/* error */
+		return 0;
+	}
+	(void)u; /* the value is not required */
+	return 1;
+}
+
+
+static int
+event_signal(void *eventhdl)
+{
+	uint64_t u = 1;
+	int s = (int)write((int)eventhdl, &u, sizeof(u));
+	if (s != sizeof(uint64_t)) {
+		/* error */
+		return 0;
+	}
+	return 1;
+}
+
+
+static void
+event_destroy(void *eventhdl)
+{
+	close((int)eventhdl);
+}
+#endif
+
+#endif
+
+
+#if !defined(__linux__) && !defined(_WIN32) && defined(ALTERNATIVE_QUEUE)
+
+struct posix_event {
+	pthread_mutex_t mutex;
+	pthread_cond_t cond;
+};
+
+
+static void *
+event_create(void)
+{
+	struct posix_event *ret = mg_malloc(sizeof(struct posix_event));
+	if (ret == 0) {
+		/* out of memory */
+		return 0;
+	}
+	if (0 != pthread_mutex_init(&(ret->mutex), NULL)) {
+		/* pthread mutex not available */
+		mg_free(ret);
+		return 0;
+	}
+	if (0 != pthread_cond_init(&(ret->cond), NULL)) {
+		/* pthread cond not available */
+		pthread_mutex_destroy(&(ret->mutex));
+		mg_free(ret);
+		return 0;
+	}
+	return (void *)ret;
+}
+
+
+static int
+event_wait(void *eventhdl)
+{
+	struct posix_event *ev = (struct posix_event *)eventhdl;
+	pthread_mutex_lock(&(ev->mutex));
+	pthread_cond_wait(&(ev->cond), &(ev->mutex));
+	pthread_mutex_unlock(&(ev->mutex));
+	return 1;
+}
+
+
+static int
+event_signal(void *eventhdl)
+{
+	struct posix_event *ev = (struct posix_event *)eventhdl;
+	pthread_mutex_lock(&(ev->mutex));
+	pthread_cond_signal(&(ev->cond));
+	pthread_mutex_unlock(&(ev->mutex));
+	return 1;
+}
+
+
+static void
+event_destroy(void *eventhdl)
+{
+	struct posix_event *ev = (struct posix_event *)eventhdl;
+	pthread_cond_destroy(&(ev->cond));
+	pthread_mutex_destroy(&(ev->mutex));
+	mg_free(ev);
+}
+#endif
+
+
+static void
+mg_set_thread_name(const char *name)
+{
+	char threadName[16 + 1]; /* 16 = Max. thread length in Linux/OSX/.. */
+
+	mg_snprintf(
+	    NULL, NULL, threadName, sizeof(threadName), "civetweb-%s", name);
+
+#if defined(_WIN32)
+#if defined(_MSC_VER)
+	/* Windows and Visual Studio Compiler */
+	__try
+	{
+		THREADNAME_INFO info;
+		info.dwType = 0x1000;
+		info.szName = threadName;
+		info.dwThreadID = ~0U;
+		info.dwFlags = 0;
+
+		RaiseException(0x406D1388,
+		               0,
+		               sizeof(info) / sizeof(ULONG_PTR),
+		               (ULONG_PTR *)&info);
+	}
+	__except(EXCEPTION_EXECUTE_HANDLER)
+	{
+	}
+#elif defined(__MINGW32__)
+/* No option known to set thread name for MinGW */
+#endif
+#elif defined(__GLIBC__)                                                       \
+    && ((__GLIBC__ > 2) || ((__GLIBC__ == 2) && (__GLIBC_MINOR__ >= 12)))
+	/* pthread_setname_np first appeared in glibc in version 2.12*/
+	(void)pthread_setname_np(pthread_self(), threadName);
+#elif defined(__linux__)
+	/* on linux we can use the old prctl function */
+	(void)prctl(PR_SET_NAME, threadName, 0, 0, 0);
+#endif
+}
+#else /* !defined(NO_THREAD_NAME) */
+void
+mg_set_thread_name(const char *threadName)
+{
+}
+#endif
+
+
+#if defined(MG_LEGACY_INTERFACE)
+const char **
+mg_get_valid_option_names(void)
+{
+	/* This function is deprecated. Use mg_get_valid_options instead. */
+	static const char *
+	    data[2 * sizeof(config_options) / sizeof(config_options[0])] = {0};
+	int i;
+
+	for (i = 0; config_options[i].name != NULL; i++) {
+		data[i * 2] = config_options[i].name;
+		data[i * 2 + 1] = config_options[i].default_value;
+	}
+
+	return data;
+}
+#endif
+
+
+const struct mg_option *
+mg_get_valid_options(void)
+{
+	return config_options;
+}
+
+
+/* Do not open file (used in is_file_in_memory) */
+#define MG_FOPEN_MODE_NONE (0)
+
+/* Open file for read only access */
+#define MG_FOPEN_MODE_READ (1)
+
+/* Open file for writing, create and overwrite */
+#define MG_FOPEN_MODE_WRITE (2)
+
+/* Open file for writing, create and append */
+#define MG_FOPEN_MODE_APPEND (4)
+
+
+/* If a file is in memory, set all "stat" members and the membuf pointer of
+ * output filep and return 1, otherwise return 0 and don't modify anything. */
+static int
+open_file_in_memory(const struct mg_connection *conn,
+                    const char *path,
+                    struct mg_file *filep,
+                    int mode)
+{
+	size_t size = 0;
+	const char *buf = NULL;
+	if (!conn) {
+		return 0;
+	}
+
+	if ((mode != MG_FOPEN_MODE_NONE) && (mode != MG_FOPEN_MODE_READ)) {
+		return 0;
+	}
+
+	if (conn->ctx->callbacks.open_file) {
+		buf = conn->ctx->callbacks.open_file(conn, path, &size);
+		if (buf != NULL) {
+			if (filep == NULL) {
+				/* This is a file in memory, but we cannot store the properties
+				 * now.
+				 * Called from "is_file_in_memory" function. */
+				return 1;
+			}
+
+			/* NOTE: override filep->size only on success. Otherwise, it might
+			 * break constructs like if (!mg_stat() || !mg_fopen()) ... */
+			filep->access.membuf = buf;
+			filep->access.fp = NULL;
+
+			/* Size was set by the callback */
+			filep->stat.size = size;
+
+			/* Assume the data may change during runtime by setting
+			 * last_modified = now */
+			filep->stat.last_modified = time(NULL);
+
+			filep->stat.is_directory = 0;
+			filep->stat.is_gzipped = 0;
+		}
+	}
+
+	return (buf != NULL);
+}
+
+
+static int
+is_file_in_memory(const struct mg_connection *conn, const char *path)
+{
+	return open_file_in_memory(conn, path, NULL, MG_FOPEN_MODE_NONE);
+}
+
+
+static int
+is_file_opened(const struct mg_file_access *fileacc)
+{
+	if (!fileacc) {
+		return 0;
+	}
+	return (fileacc->membuf != NULL) || (fileacc->fp != NULL);
+}
+
+
+static int mg_stat(const struct mg_connection *conn,
+                   const char *path,
+                   struct mg_file_stat *filep);
+
+
+/* mg_fopen will open a file either in memory or on the disk.
+ * The input parameter path is a string in UTF-8 encoding.
+ * The input parameter mode is MG_FOPEN_MODE_*
+ * On success, either fp or membuf will be set in the output
+ * struct file. All status members will also be set.
+ * The function returns 1 on success, 0 on error. */
+static int
+mg_fopen(const struct mg_connection *conn,
+         const char *path,
+         int mode,
+         struct mg_file *filep)
+{
+	int found;
+
+	if (!filep) {
+		return 0;
+	}
+	filep->access.fp = NULL;
+	filep->access.membuf = NULL;
+
+	if (!is_file_in_memory(conn, path)) {
+
+		/* filep is initialized in mg_stat: all fields with memset to,
+		* some fields like size and modification date with values */
+		found = mg_stat(conn, path, &(filep->stat));
+
+		if ((mode == MG_FOPEN_MODE_READ) && (!found)) {
+			/* file does not exist and will not be created */
+			return 0;
+		}
+
+#ifdef _WIN32
+		{
+			wchar_t wbuf[PATH_MAX];
+			path_to_unicode(conn, path, wbuf, ARRAY_SIZE(wbuf));
+			switch (mode) {
+			case MG_FOPEN_MODE_READ:
+				filep->access.fp = _wfopen(wbuf, L"rb");
+				break;
+			case MG_FOPEN_MODE_WRITE:
+				filep->access.fp = _wfopen(wbuf, L"wb");
+				break;
+			case MG_FOPEN_MODE_APPEND:
+				filep->access.fp = _wfopen(wbuf, L"ab");
+				break;
+			}
+		}
+#else
+		/* Linux et al already use unicode. No need to convert. */
+		switch (mode) {
+		case MG_FOPEN_MODE_READ:
+			filep->access.fp = fopen(path, "r");
+			break;
+		case MG_FOPEN_MODE_WRITE:
+			filep->access.fp = fopen(path, "w");
+			break;
+		case MG_FOPEN_MODE_APPEND:
+			filep->access.fp = fopen(path, "a");
+			break;
+		}
+
+#endif
+		if (!found) {
+			/* File did not exist before fopen was called.
+			 * Maybe it has been created now. Get stat info
+			 * like creation time now. */
+			found = mg_stat(conn, path, &(filep->stat));
+			(void)found;
+		}
+
+		/* file is on disk */
+		return (filep->access.fp != NULL);
+
+	} else {
+		/* is_file_in_memory returned true */
+		if (open_file_in_memory(conn, path, filep, mode)) {
+			/* file is in memory */
+			return (filep->access.membuf != NULL);
+		}
+	}
+
+	/* Open failed */
+	return 0;
+}
+
+
+/* return 0 on success, just like fclose */
+static int
+mg_fclose(struct mg_file_access *fileacc)
+{
+	int ret = -1;
+	if (fileacc != NULL) {
+		if (fileacc->fp != NULL) {
+			ret = fclose(fileacc->fp);
+		} else if (fileacc->membuf != NULL) {
+			ret = 0;
+		}
+		/* reset all members of fileacc */
+		memset(fileacc, 0, sizeof(*fileacc));
+	}
+	return ret;
+}
+
+
+static void
+mg_strlcpy(register char *dst, register const char *src, size_t n)
+{
+	for (; *src != '\0' && n > 1; n--) {
+		*dst++ = *src++;
+	}
+	*dst = '\0';
+}
+
+
+static int
+lowercase(const char *s)
+{
+	return tolower(*(const unsigned char *)s);
+}
+
+
+int
+mg_strncasecmp(const char *s1, const char *s2, size_t len)
+{
+	int diff = 0;
+
+	if (len > 0) {
+		do {
+			diff = lowercase(s1++) - lowercase(s2++);
+		} while (diff == 0 && s1[-1] != '\0' && --len > 0);
+	}
+
+	return diff;
+}
+
+
+int
+mg_strcasecmp(const char *s1, const char *s2)
+{
+	int diff;
+
+	do {
+		diff = lowercase(s1++) - lowercase(s2++);
+	} while (diff == 0 && s1[-1] != '\0');
+
+	return diff;
+}
+
+
+static char *
+mg_strndup(const char *ptr, size_t len)
+{
+	char *p;
+
+	if ((p = (char *)mg_malloc(len + 1)) != NULL) {
+		mg_strlcpy(p, ptr, len + 1);
+	}
+
+	return p;
+}
+
+
+static char *
+mg_strdup(const char *str)
+{
+	return mg_strndup(str, strlen(str));
+}
+
+
+static const char *
+mg_strcasestr(const char *big_str, const char *small_str)
+{
+	size_t i, big_len = strlen(big_str), small_len = strlen(small_str);
+
+	if (big_len >= small_len) {
+		for (i = 0; i <= (big_len - small_len); i++) {
+			if (mg_strncasecmp(big_str + i, small_str, small_len) == 0) {
+				return big_str + i;
+			}
+		}
+	}
+
+	return NULL;
+}
+
+
+/* Return null terminated string of given maximum length.
+ * Report errors if length is exceeded. */
+static void
+mg_vsnprintf(const struct mg_connection *conn,
+             int *truncated,
+             char *buf,
+             size_t buflen,
+             const char *fmt,
+             va_list ap)
+{
+	int n, ok;
+
+	if (buflen == 0) {
+		return;
+	}
+
+#ifdef __clang__
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wformat-nonliteral"
+/* Using fmt as a non-literal is intended here, since it is mostly called
+ * indirectly by mg_snprintf */
+#endif
+
+	n = (int)vsnprintf_impl(buf, buflen, fmt, ap);
+	ok = (n >= 0) && ((size_t)n < buflen);
+
+#ifdef __clang__
+#pragma clang diagnostic pop
+#endif
+
+	if (ok) {
+		if (truncated) {
+			*truncated = 0;
+		}
+	} else {
+		if (truncated) {
+			*truncated = 1;
+		}
+		mg_cry(conn,
+		       "truncating vsnprintf buffer: [%.*s]",
+		       (int)((buflen > 200) ? 200 : (buflen - 1)),
+		       buf);
+		n = (int)buflen - 1;
+	}
+	buf[n] = '\0';
+}
+
+
+static void
+mg_snprintf(const struct mg_connection *conn,
+            int *truncated,
+            char *buf,
+            size_t buflen,
+            const char *fmt,
+            ...)
+{
+	va_list ap;
+
+	va_start(ap, fmt);
+	mg_vsnprintf(conn, truncated, buf, buflen, fmt, ap);
+	va_end(ap);
+}
+
+
+static int
+get_option_index(const char *name)
+{
+	int i;
+
+	for (i = 0; config_options[i].name != NULL; i++) {
+		if (strcmp(config_options[i].name, name) == 0) {
+			return i;
+		}
+	}
+	return -1;
+}
+
+
+const char *
+mg_get_option(const struct mg_context *ctx, const char *name)
+{
+	int i;
+	if ((i = get_option_index(name)) == -1) {
+		return NULL;
+	} else if (!ctx || ctx->config[i] == NULL) {
+		return "";
+	} else {
+		return ctx->config[i];
+	}
+}
+
+
+struct mg_context *
+mg_get_context(const struct mg_connection *conn)
+{
+	return (conn == NULL) ? (struct mg_context *)NULL : (conn->ctx);
+}
+
+
+void *
+mg_get_user_data(const struct mg_context *ctx)
+{
+	return (ctx == NULL) ? NULL : ctx->user_data;
+}
+
+
+void
+mg_set_user_connection_data(struct mg_connection *conn, void *data)
+{
+	if (conn != NULL) {
+		conn->request_info.conn_data = data;
+	}
+}
+
+
+void *
+mg_get_user_connection_data(const struct mg_connection *conn)
+{
+	if (conn != NULL) {
+		return conn->request_info.conn_data;
+	}
+	return NULL;
+}
+
+
+size_t
+mg_get_ports(const struct mg_context *ctx, size_t size, int *ports, int *ssl)
+{
+	size_t i;
+	if (!ctx) {
+		return 0;
+	}
+	for (i = 0; i < size && i < ctx->num_listening_sockets; i++) {
+		ssl[i] = ctx->listening_sockets[i].is_ssl;
+		ports[i] =
+#if defined(USE_IPV6)
+		    (ctx->listening_sockets[i].lsa.sa.sa_family == AF_INET6)
+		        ? ntohs(ctx->listening_sockets[i].lsa.sin6.sin6_port)
+		        :
+#endif
+		        ntohs(ctx->listening_sockets[i].lsa.sin.sin_port);
+	}
+	return i;
+}
+
+
+int
+mg_get_server_ports(const struct mg_context *ctx,
+                    int size,
+                    struct mg_server_ports *ports)
+{
+	int i, cnt = 0;
+
+	if (size <= 0) {
+		return -1;
+	}
+	memset(ports, 0, sizeof(*ports) * (size_t)size);
+	if (!ctx) {
+		return -1;
+	}
+	if (!ctx->listening_sockets) {
+		return -1;
+	}
+
+	for (i = 0; (i < size) && (i < (int)ctx->num_listening_sockets); i++) {
+
+		ports[cnt].port =
+#if defined(USE_IPV6)
+		    (ctx->listening_sockets[i].lsa.sa.sa_family == AF_INET6)
+		        ? ntohs(ctx->listening_sockets[i].lsa.sin6.sin6_port)
+		        :
+#endif
+		        ntohs(ctx->listening_sockets[i].lsa.sin.sin_port);
+		ports[cnt].is_ssl = ctx->listening_sockets[i].is_ssl;
+		ports[cnt].is_redirect = ctx->listening_sockets[i].ssl_redir;
+
+		if (ctx->listening_sockets[i].lsa.sa.sa_family == AF_INET) {
+			/* IPv4 */
+			ports[cnt].protocol = 1;
+			cnt++;
+		} else if (ctx->listening_sockets[i].lsa.sa.sa_family == AF_INET6) {
+			/* IPv6 */
+			ports[cnt].protocol = 3;
+			cnt++;
+		}
+	}
+
+	return cnt;
+}
+
+
+static void
+sockaddr_to_string(char *buf, size_t len, const union usa *usa)
+{
+	buf[0] = '\0';
+
+	if (!usa) {
+		return;
+	}
+
+	if (usa->sa.sa_family == AF_INET) {
+		getnameinfo(&usa->sa,
+		            sizeof(usa->sin),
+		            buf,
+		            (unsigned)len,
+		            NULL,
+		            0,
+		            NI_NUMERICHOST);
+	}
+#if defined(USE_IPV6)
+	else if (usa->sa.sa_family == AF_INET6) {
+		getnameinfo(&usa->sa,
+		            sizeof(usa->sin6),
+		            buf,
+		            (unsigned)len,
+		            NULL,
+		            0,
+		            NI_NUMERICHOST);
+	}
+#endif
+}
+
+
+/* Convert time_t to a string. According to RFC2616, Sec 14.18, this must be
+ * included in all responses other than 100, 101, 5xx. */
+static void
+gmt_time_string(char *buf, size_t buf_len, time_t *t)
+{
+	struct tm *tm;
+
+	tm = ((t != NULL) ? gmtime(t) : NULL);
+	if (tm != NULL) {
+		strftime(buf, buf_len, "%a, %d %b %Y %H:%M:%S GMT", tm);
+	} else {
+		mg_strlcpy(buf, "Thu, 01 Jan 1970 00:00:00 GMT", buf_len);
+		buf[buf_len - 1] = '\0';
+	}
+}
+
+
+/* difftime for struct timespec. Return value is in seconds. */
+static double
+mg_difftimespec(const struct timespec *ts_now, const struct timespec *ts_before)
+{
+	return (double)(ts_now->tv_nsec - ts_before->tv_nsec) * 1.0E-9
+	       + (double)(ts_now->tv_sec - ts_before->tv_sec);
+}
+
+
+/* Print error message to the opened error log stream. */
+void
+mg_cry(const struct mg_connection *conn, const char *fmt, ...)
+{
+	char buf[MG_BUF_LEN], src_addr[IP_ADDR_STR_LEN];
+	va_list ap;
+	struct mg_file fi;
+	time_t timestamp;
+
+	va_start(ap, fmt);
+	IGNORE_UNUSED_RESULT(vsnprintf_impl(buf, sizeof(buf), fmt, ap));
+	va_end(ap);
+	buf[sizeof(buf) - 1] = 0;
+
+	if (!conn) {
+		puts(buf);
+		return;
+	}
+
+	/* Do not lock when getting the callback value, here and below.
+	 * I suppose this is fine, since function cannot disappear in the
+	 * same way string option can. */
+	if ((conn->ctx->callbacks.log_message == NULL)
+	    || (conn->ctx->callbacks.log_message(conn, buf) == 0)) {
+
+		if (conn->ctx->config[ERROR_LOG_FILE] != NULL) {
+			if (mg_fopen(conn,
+			             conn->ctx->config[ERROR_LOG_FILE],
+			             MG_FOPEN_MODE_APPEND,
+			             &fi) == 0) {
+				fi.access.fp = NULL;
+			}
+		} else {
+			fi.access.fp = NULL;
+		}
+
+		if (fi.access.fp != NULL) {
+			flockfile(fi.access.fp);
+			timestamp = time(NULL);
+
+			sockaddr_to_string(src_addr, sizeof(src_addr), &conn->client.rsa);
+			fprintf(fi.access.fp,
+			        "[%010lu] [error] [client %s] ",
+			        (unsigned long)timestamp,
+			        src_addr);
+
+			if (conn->request_info.request_method != NULL) {
+				fprintf(fi.access.fp,
+				        "%s %s: ",
+				        conn->request_info.request_method,
+				        conn->request_info.request_uri);
+			}
+
+			fprintf(fi.access.fp, "%s", buf);
+			fputc('\n', fi.access.fp);
+			fflush(fi.access.fp);
+			funlockfile(fi.access.fp);
+			(void)mg_fclose(&fi.access); /* Ignore errors. We can't call
+			                              * mg_cry here anyway ;-) */
+		}
+	}
+}
+
+
+/* Return fake connection structure. Used for logging, if connection
+ * is not applicable at the moment of logging. */
+static struct mg_connection *
+fc(struct mg_context *ctx)
+{
+	static struct mg_connection fake_connection;
+	fake_connection.ctx = ctx;
+	return &fake_connection;
+}
+
+
+const char *
+mg_version(void)
+{
+	return CIVETWEB_VERSION;
+}
+
+
+const struct mg_request_info *
+mg_get_request_info(const struct mg_connection *conn)
+{
+	if (!conn) {
+		return NULL;
+	}
+	return &conn->request_info;
+}
+
+
+/* Skip the characters until one of the delimiters characters found.
+ * 0-terminate resulting word. Skip the delimiter and following whitespaces.
+ * Advance pointer to buffer to the next word. Return found 0-terminated word.
+ * Delimiters can be quoted with quotechar. */
+static char *
+skip_quoted(char **buf,
+            const char *delimiters,
+            const char *whitespace,
+            char quotechar)
+{
+	char *p, *begin_word, *end_word, *end_whitespace;
+
+	begin_word = *buf;
+	end_word = begin_word + strcspn(begin_word, delimiters);
+
+	/* Check for quotechar */
+	if (end_word > begin_word) {
+		p = end_word - 1;
+		while (*p == quotechar) {
+			/* While the delimiter is quoted, look for the next delimiter. */
+			/* This happens, e.g., in calls from parse_auth_header,
+			 * if the user name contains a " character. */
+
+			/* If there is anything beyond end_word, copy it. */
+			if (*end_word != '\0') {
+				size_t end_off = strcspn(end_word + 1, delimiters);
+				memmove(p, end_word, end_off + 1);
+				p += end_off; /* p must correspond to end_word - 1 */
+				end_word += end_off + 1;
+			} else {
+				*p = '\0';
+				break;
+			}
+		}
+		for (p++; p < end_word; p++) {
+			*p = '\0';
+		}
+	}
+
+	if (*end_word == '\0') {
+		*buf = end_word;
+	} else {
+
+#if defined(__GNUC__) || defined(__MINGW32__)
+/* Disable spurious conversion warning for GCC */
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wsign-conversion"
+#endif
+
+		end_whitespace = end_word + strspn(&end_word[1], whitespace) + 1;
+
+#if defined(__GNUC__) || defined(__MINGW32__)
+#pragma GCC diagnostic pop
+#endif
+
+		for (p = end_word; p < end_whitespace; p++) {
+			*p = '\0';
+		}
+
+		*buf = end_whitespace;
+	}
+
+	return begin_word;
+}
+
+
+/* Simplified version of skip_quoted without quote char
+ * and whitespace == delimiters */
+static char *
+skip(char **buf, const char *delimiters)
+{
+	return skip_quoted(buf, delimiters, delimiters, 0);
+}
+
+
+/* Return HTTP header value, or NULL if not found. */
+static const char *
+get_header(const struct mg_request_info *ri, const char *name)
+{
+	int i;
+	if (ri) {
+		for (i = 0; i < ri->num_headers; i++) {
+			if (!mg_strcasecmp(name, ri->http_headers[i].name)) {
+				return ri->http_headers[i].value;
+			}
+		}
+	}
+
+	return NULL;
+}
+
+
+const char *
+mg_get_header(const struct mg_connection *conn, const char *name)
+{
+	if (!conn) {
+		return NULL;
+	}
+
+	return get_header(&conn->request_info, name);
+}
+
+
+/* A helper function for traversing a comma separated list of values.
+ * It returns a list pointer shifted to the next value, or NULL if the end
+ * of the list found.
+ * Value is stored in val vector. If value has form "x=y", then eq_val
+ * vector is initialized to point to the "y" part, and val vector length
+ * is adjusted to point only to "x". */
+static const char *
+next_option(const char *list, struct vec *val, struct vec *eq_val)
+{
+	int end;
+
+reparse:
+	if (val == NULL || list == NULL || *list == '\0') {
+		/* End of the list */
+		list = NULL;
+	} else {
+		/* Skip over leading LWS */
+		while (*list == ' ' || *list == '\t')
+			list++;
+
+		val->ptr = list;
+		if ((list = strchr(val->ptr, ',')) != NULL) {
+			/* Comma found. Store length and shift the list ptr */
+			val->len = ((size_t)(list - val->ptr));
+			list++;
+		} else {
+			/* This value is the last one */
+			list = val->ptr + strlen(val->ptr);
+			val->len = ((size_t)(list - val->ptr));
+		}
+
+		/* Adjust length for trailing LWS */
+		end = (int)val->len - 1;
+		while (end >= 0 && (val->ptr[end] == ' ' || val->ptr[end] == '\t'))
+			end--;
+		val->len = (size_t)(end + 1);
+
+		if (val->len == 0) {
+			/* Ignore any empty entries. */
+			goto reparse;
+		}
+
+		if (eq_val != NULL) {
+			/* Value has form "x=y", adjust pointers and lengths
+			 * so that val points to "x", and eq_val points to "y". */
+			eq_val->len = 0;
+			eq_val->ptr = (const char *)memchr(val->ptr, '=', val->len);
+			if (eq_val->ptr != NULL) {
+				eq_val->ptr++; /* Skip over '=' character */
+				eq_val->len = ((size_t)(val->ptr - eq_val->ptr)) + val->len;
+				val->len = ((size_t)(eq_val->ptr - val->ptr)) - 1;
+			}
+		}
+	}
+
+	return list;
+}
+
+/* A helper function for checking if a comma separated list of values contains
+ * the given option (case insensitvely).
+ * 'header' can be NULL, in which case false is returned. */
+static int
+header_has_option(const char *header, const char *option)
+{
+	struct vec opt_vec;
+	struct vec eq_vec;
+
+	assert(option != NULL);
+	assert(option[0] != '\0');
+
+	while ((header = next_option(header, &opt_vec, &eq_vec)) != NULL) {
+		if (mg_strncasecmp(option, opt_vec.ptr, opt_vec.len) == 0)
+			return 1;
+	}
+
+	return 0;
+}
+
+/* Perform case-insensitive match of string against pattern */
+static int
+match_prefix(const char *pattern, size_t pattern_len, const char *str)
+{
+	const char *or_str;
+	size_t i;
+	int j, len, res;
+
+	if ((or_str = (const char *)memchr(pattern, '|', pattern_len)) != NULL) {
+		res = match_prefix(pattern, (size_t)(or_str - pattern), str);
+		return (res > 0) ? res : match_prefix(or_str + 1,
+		                                      (size_t)((pattern + pattern_len)
+		                                               - (or_str + 1)),
+		                                      str);
+	}
+
+	for (i = 0, j = 0; i < pattern_len; i++, j++) {
+		if (pattern[i] == '?' && str[j] != '\0') {
+			continue;
+		} else if (pattern[i] == '$') {
+			return (str[j] == '\0') ? j : -1;
+		} else if (pattern[i] == '*') {
+			i++;
+			if (pattern[i] == '*') {
+				i++;
+				len = (int)strlen(str + j);
+			} else {
+				len = (int)strcspn(str + j, "/");
+			}
+			if (i == pattern_len) {
+				return j + len;
+			}
+			do {
+				res = match_prefix(pattern + i, pattern_len - i, str + j + len);
+			} while (res == -1 && len-- > 0);
+			return (res == -1) ? -1 : j + res + len;
+		} else if (lowercase(&pattern[i]) != lowercase(&str[j])) {
+			return -1;
+		}
+	}
+	return j;
+}
+
+
+/* HTTP 1.1 assumes keep alive if "Connection:" header is not set
+ * This function must tolerate situations when connection info is not
+ * set up, for example if request parsing failed. */
+static int
+should_keep_alive(const struct mg_connection *conn)
+{
+	if (conn != NULL) {
+		const char *http_version = conn->request_info.http_version;
+		const char *header = mg_get_header(conn, "Connection");
+		if (conn->must_close || conn->status_code == 401
+		    || mg_strcasecmp(conn->ctx->config[ENABLE_KEEP_ALIVE], "yes") != 0
+		    || (header != NULL && !header_has_option(header, "keep-alive"))
+		    || (header == NULL && http_version
+		        && 0 != strcmp(http_version, "1.1"))) {
+			return 0;
+		}
+		return 1;
+	}
+	return 0;
+}
+
+
+static int
+should_decode_url(const struct mg_connection *conn)
+{
+	if (!conn || !conn->ctx) {
+		return 0;
+	}
+
+	return (mg_strcasecmp(conn->ctx->config[DECODE_URL], "yes") == 0);
+}
+
+
+static const char *
+suggest_connection_header(const struct mg_connection *conn)
+{
+	return should_keep_alive(conn) ? "keep-alive" : "close";
+}
+
+
+static int
+send_no_cache_header(struct mg_connection *conn)
+{
+	/* Send all current and obsolete cache opt-out directives. */
+	return mg_printf(conn,
+	                 "Cache-Control: no-cache, no-store, "
+	                 "must-revalidate, private, max-age=0\r\n"
+	                 "Pragma: no-cache\r\n"
+	                 "Expires: 0\r\n");
+}
+
+
+static int
+send_static_cache_header(struct mg_connection *conn)
+{
+#if !defined(NO_CACHING)
+	/* Read the server config to check how long a file may be cached.
+	 * The configuration is in seconds. */
+	int max_age = atoi(conn->ctx->config[STATIC_FILE_MAX_AGE]);
+	if (max_age <= 0) {
+		/* 0 means "do not cache". All values <0 are reserved
+		 * and may be used differently in the future. */
+		/* If a file should not be cached, do not only send
+		 * max-age=0, but also pragmas and Expires headers. */
+		return send_no_cache_header(conn);
+	}
+
+	/* Use "Cache-Control: max-age" instead of "Expires" header.
+	 * Reason: see https://www.mnot.net/blog/2007/05/15/expires_max-age */
+	/* See also https://www.mnot.net/cache_docs/ */
+	/* According to RFC 2616, Section 14.21, caching times should not exceed
+	 * one year. A year with 365 days corresponds to 31536000 seconds, a leap
+	 * year to 31622400 seconds. For the moment, we just send whatever has
+	 * been configured, still the behavior for >1 year should be considered
+	 * as undefined. */
+	return mg_printf(conn, "Cache-Control: max-age=%u\r\n", (unsigned)max_age);
+#else  /* NO_CACHING */
+	return send_no_cache_header(conn);
+#endif /* !NO_CACHING */
+}
+
+
+static void handle_file_based_request(struct mg_connection *conn,
+                                      const char *path,
+                                      struct mg_file *filep);
+
+
+const char *
+mg_get_response_code_text(struct mg_connection *conn, int response_code)
+{
+	/* See IANA HTTP status code assignment:
+	 * http://www.iana.org/assignments/http-status-codes/http-status-codes.xhtml
+	 */
+
+	switch (response_code) {
+	/* RFC2616 Section 10.1 - Informational 1xx */
+	case 100:
+		return "Continue"; /* RFC2616 Section 10.1.1 */
+	case 101:
+		return "Switching Protocols"; /* RFC2616 Section 10.1.2 */
+	case 102:
+		return "Processing"; /* RFC2518 Section 10.1 */
+
+	/* RFC2616 Section 10.2 - Successful 2xx */
+	case 200:
+		return "OK"; /* RFC2616 Section 10.2.1 */
+	case 201:
+		return "Created"; /* RFC2616 Section 10.2.2 */
+	case 202:
+		return "Accepted"; /* RFC2616 Section 10.2.3 */
+	case 203:
+		return "Non-Authoritative Information"; /* RFC2616 Section 10.2.4 */
+	case 204:
+		return "No Content"; /* RFC2616 Section 10.2.5 */
+	case 205:
+		return "Reset Content"; /* RFC2616 Section 10.2.6 */
+	case 206:
+		return "Partial Content"; /* RFC2616 Section 10.2.7 */
+	case 207:
+		return "Multi-Status"; /* RFC2518 Section 10.2, RFC4918 Section 11.1 */
+	case 208:
+		return "Already Reported"; /* RFC5842 Section 7.1 */
+
+	case 226:
+		return "IM used"; /* RFC3229 Section 10.4.1 */
+
+	/* RFC2616 Section 10.3 - Redirection 3xx */
+	case 300:
+		return "Multiple Choices"; /* RFC2616 Section 10.3.1 */
+	case 301:
+		return "Moved Permanently"; /* RFC2616 Section 10.3.2 */
+	case 302:
+		return "Found"; /* RFC2616 Section 10.3.3 */
+	case 303:
+		return "See Other"; /* RFC2616 Section 10.3.4 */
+	case 304:
+		return "Not Modified"; /* RFC2616 Section 10.3.5 */
+	case 305:
+		return "Use Proxy"; /* RFC2616 Section 10.3.6 */
+	case 307:
+		return "Temporary Redirect"; /* RFC2616 Section 10.3.8 */
+	case 308:
+		return "Permanent Redirect"; /* RFC7238 Section 3 */
+
+	/* RFC2616 Section 10.4 - Client Error 4xx */
+	case 400:
+		return "Bad Request"; /* RFC2616 Section 10.4.1 */
+	case 401:
+		return "Unauthorized"; /* RFC2616 Section 10.4.2 */
+	case 402:
+		return "Payment Required"; /* RFC2616 Section 10.4.3 */
+	case 403:
+		return "Forbidden"; /* RFC2616 Section 10.4.4 */
+	case 404:
+		return "Not Found"; /* RFC2616 Section 10.4.5 */
+	case 405:
+		return "Method Not Allowed"; /* RFC2616 Section 10.4.6 */
+	case 406:
+		return "Not Acceptable"; /* RFC2616 Section 10.4.7 */
+	case 407:
+		return "Proxy Authentication Required"; /* RFC2616 Section 10.4.8 */
+	case 408:
+		return "Request Time-out"; /* RFC2616 Section 10.4.9 */
+	case 409:
+		return "Conflict"; /* RFC2616 Section 10.4.10 */
+	case 410:
+		return "Gone"; /* RFC2616 Section 10.4.11 */
+	case 411:
+		return "Length Required"; /* RFC2616 Section 10.4.12 */
+	case 412:
+		return "Precondition Failed"; /* RFC2616 Section 10.4.13 */
+	case 413:
+		return "Request Entity Too Large"; /* RFC2616 Section 10.4.14 */
+	case 414:
+		return "Request-URI Too Large"; /* RFC2616 Section 10.4.15 */
+	case 415:
+		return "Unsupported Media Type"; /* RFC2616 Section 10.4.16 */
+	case 416:
+		return "Requested range not satisfiable"; /* RFC2616 Section 10.4.17 */
+	case 417:
+		return "Expectation Failed"; /* RFC2616 Section 10.4.18 */
+
+	case 421:
+		return "Misdirected Request"; /* RFC7540 Section 9.1.2 */
+	case 422:
+		return "Unproccessable entity"; /* RFC2518 Section 10.3, RFC4918
+		                                 * Sect

<TRUNCATED>

Mime
View raw message