Explorar el Código

Merge branch 'master' into fix/mg_server_ports

bel2125 hace 6 años
padre
commit
79fcb55855

+ 0 - 4
VisualStudio/civetweb_lua/civetweb_lua.vcxproj

@@ -175,9 +175,6 @@
     </Link>
   </ItemDefinitionGroup>
   <ItemGroup>
-    <Text Include="ReadMe.txt" />
-  </ItemGroup>
-  <ItemGroup>
     <ClInclude Include="..\..\include\civetweb.h" />
     <ClInclude Include="..\..\src\third_party\civetweb_lua.h" />
     <ClInclude Include="..\..\src\third_party\lua-5.2.4\src\lauxlib.h" />
@@ -208,7 +205,6 @@
     <None Include="..\..\src\mod_duktape.inl" />
     <None Include="..\..\src\mod_lua.inl" />
     <None Include="..\..\src\timer.inl" />
-    <None Include="..\..\src\file_ops.inl" />
   </ItemGroup>
   <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
   <ImportGroup Label="ExtensionTargets">

+ 0 - 6
VisualStudio/civetweb_lua/civetweb_lua.vcxproj.filters

@@ -18,9 +18,6 @@
     </Filter>
   </ItemGroup>
   <ItemGroup>
-    <Text Include="ReadMe.txt" />
-  </ItemGroup>
-  <ItemGroup>
     <ClInclude Include="..\..\include\civetweb.h">
       <Filter>Header Files</Filter>
     </ClInclude>
@@ -65,9 +62,6 @@
     <None Include="..\..\src\timer.inl">
       <Filter>inl files</Filter>
     </None>
-    <None Include="..\..\src\file_ops.inl">
-      <Filter>inl files</Filter>
-    </None>
     <None Include="..\..\src\mod_duktape.inl">
       <Filter>inl files</Filter>
     </None>

+ 2 - 2
VisualStudio/unit_test/unit_test.vcxproj

@@ -73,7 +73,7 @@
       </PrecompiledHeader>
       <WarningLevel>Level3</WarningLevel>
       <Optimization>Disabled</Optimization>
-      <PreprocessorDefinitions>REPLACE_CHECK_FOR_LOCAL_DEBUGGING;LOCAL_TEST;USE_IPV6;USE_WEBSOCKET;MEMORY_DEBUGGING;WIN32;_DEBUG;_CONSOLE;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <PreprocessorDefinitions>MAIN_PUBLIC_SERVER=main;LOCAL_TEST;REPLACE_CHECK_FOR_LOCAL_DEBUGGING;LOCAL_TEST;USE_IPV6;USE_WEBSOCKET;MEMORY_DEBUGGING;WIN32;_DEBUG;_CONSOLE;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
       <AdditionalIncludeDirectories>$(ProjectDir)..\..\src;$(ProjectDir)..\..\include;$(ProjectDir)..\..\src\third_party\lua-5.2.4\src;$(ProjectDir)..\..\..\check-0.10.0\;$(ProjectDir)..\..\..\check-0.10.0\src\;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
     </ClCompile>
     <Link>
@@ -89,7 +89,7 @@
       <Optimization>MaxSpeed</Optimization>
       <FunctionLevelLinking>true</FunctionLevelLinking>
       <IntrinsicFunctions>true</IntrinsicFunctions>
-      <PreprocessorDefinitions>REPLACE_CHECK_FOR_LOCAL_DEBUGGING;WIN32;NDEBUG;_CONSOLE;_CRT_SECURE_NO_DEPRECATE;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <PreprocessorDefinitions>MAIN_PUBLIC_SERVER=main;LOCAL_TEST;REPLACE_CHECK_FOR_LOCAL_DEBUGGING;WIN32;NDEBUG;_CONSOLE;_CRT_SECURE_NO_DEPRECATE;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
       <AdditionalIncludeDirectories>$(ProjectDir)..\..\src;$(ProjectDir)..\..\include;$(ProjectDir)..\..\src\third_party\lua-5.2.4\src;$(ProjectDir)..\..\..\check-0.10.0\;$(ProjectDir)..\..\..\check-0.10.0\src\;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
     </ClCompile>
     <Link>

+ 55 - 12
examples/embedded_c/embedded_c.c

@@ -4,6 +4,10 @@
  * License http://opensource.org/licenses/mit-license.php MIT License
  */
 
+#ifdef NO_SSL
+#define TEST_WITHOUT_SSL
+#endif
+
 /* Simple example program on how to use CivetWeb embedded into a C program. */
 #ifdef _WIN32
 #include <windows.h>
@@ -19,7 +23,7 @@
 
 
 #define DOCUMENT_ROOT "."
-#ifdef NO_SSL
+#ifndef TEST_WITHOUT_SSL
 #ifdef USE_IPV6
 #define PORT "[::]:8888,8884"
 #else
@@ -219,6 +223,24 @@ FileHandler(struct mg_connection *conn, void *cbdata)
 }
 
 
+#define MD5_STATIC static
+#include "../src/md5.inl"
+
+/* Stringify binary data. Output buffer must be twice as big as input,
+ * because each byte takes 2 bytes in string representation */
+static void
+bin2str(char *to, const unsigned char *p, size_t len)
+{
+	static const char *hex = "0123456789abcdef";
+
+	for (; len--; p++) {
+		*to++ = hex[p[0] >> 4];
+		*to++ = hex[p[0] & 0x0f];
+	}
+	*to = '\0';
+}
+
+
 int
 field_found(const char *key,
             const char *filename,
@@ -236,7 +258,7 @@ field_found(const char *key,
 #else
 		snprintf(path, pathlen, "/tmp/%s", filename);
 #endif
-		return MG_FORM_FIELD_STORAGE_STORE;
+		return MG_FORM_FIELD_STORAGE_GET;
 	}
 	return MG_FORM_FIELD_STORAGE_GET;
 }
@@ -247,10 +269,33 @@ field_get(const char *key, const char *value, size_t valuelen, void *user_data)
 {
 	struct mg_connection *conn = (struct mg_connection *)user_data;
 
-	if (key[0]) {
-		mg_printf(conn, "%s = ", key);
+	if ((key != NULL) && (key[0] == '\0')) {
+		/* Incorrect form data detected */
+		return MG_FORM_FIELD_HANDLE_ABORT;
+	}
+	if ((valuelen > 0) && (value == NULL)) {
+		/* Unreachable, since this call will not be generated by civetweb. */
+		return MG_FORM_FIELD_HANDLE_ABORT;
+	}
+
+	if (key) {
+		mg_printf(conn, "key = %s\n", key);
+	}
+	mg_printf(conn, "valuelen = %u\n", valuelen);
+
+	if (valuelen > 0) {
+		/* mg_write(conn, value, valuelen); */
+
+		md5_byte_t hash[16];
+		md5_state_t ctx;
+		char outputbuf[33];
+
+		md5_init(&ctx);
+		md5_append(&ctx, (const md5_byte_t *)value, valuelen);
+		md5_finish(&ctx, hash);
+		bin2str(outputbuf, hash, sizeof(hash));
+		mg_printf(conn, "value md5 hash = %s\n", outputbuf);
 	}
-	mg_write(conn, value, valuelen);
 
 	return 0;
 }
@@ -318,8 +363,6 @@ FileUploadForm(struct mg_connection *conn, void *cbdata)
 	return 1;
 }
 
-#define MD5_STATIC static
-#include "../src/md5.inl"
 
 struct tfile_checksum {
 	char name[128];
@@ -846,7 +889,7 @@ get_dh2236()
 #endif
 
 
-#ifndef NO_SSL
+#ifndef TEST_WITHOUT_SSL
 int
 init_ssl(void *ssl_context, void *user_data)
 {
@@ -900,7 +943,7 @@ main(int argc, char *argv[])
 	    "websocket_timeout_ms",
 	    "3600000",
 #endif
-#ifndef NO_SSL
+#ifndef TEST_WITHOUT_SSL
 	    "ssl_certificate",
 	    "../../resources/cert/server.pem",
 	    "ssl_protocol_version",
@@ -921,7 +964,7 @@ main(int argc, char *argv[])
 	int port_cnt, n;
 	int err = 0;
 
-/* Check if libcivetweb has been built with all required features. */
+	/* Check if libcivetweb has been built with all required features. */
 #ifdef USE_IPV6
 	if (!mg_check_feature(8)) {
 		fprintf(stderr,
@@ -938,7 +981,7 @@ main(int argc, char *argv[])
 		err = 1;
 	}
 #endif
-#ifndef NO_SSL
+#ifndef TEST_WITHOUT_SSL
 	if (!mg_check_feature(2)) {
 		fprintf(stderr,
 		        "Error: Embedded example built with SSL support, "
@@ -953,7 +996,7 @@ main(int argc, char *argv[])
 
 	/* Start CivetWeb web server */
 	memset(&callbacks, 0, sizeof(callbacks));
-#ifndef NO_SSL
+#ifndef TEST_WITHOUT_SSL
 	callbacks.init_ssl = init_ssl;
 #endif
 	callbacks.log_message = log_message;

+ 10 - 10
include/CivetServer.h

@@ -366,16 +366,16 @@ class CIVETWEB_CXX_API CivetServer
 	 * @return A vector of ports
 	 */
 
-	std::vector<int> getListeningPorts();
-
-	/**
-	 * getListeningPorts()
-	 *
-	 * Variant of getListeningPorts() returning the full port information
-	 * (protocol, SSL, ...)
-	 *
-	 * @return A vector of ports
-	 */
+	std::vector<int> getListeningPorts();
+
+	/**
+	 * getListeningPorts()
+	 *
+	 * Variant of getListeningPorts() returning the full port information
+	 * (protocol, SSL, ...)
+	 *
+	 * @return A vector of ports
+	 */
 	std::vector<struct mg_server_port> getListeningPortsFull();
 
 	/**

+ 33 - 34
src/CivetServer.cpp

@@ -445,10 +445,10 @@ CivetServer::urlDecode(const char *src,
 	dst.clear();
 	for (i = j = 0; i < (int)src_len; i++, j++) {
 		if (i < (int)src_len - 2 && src[i] == '%'
-		    && isxdigit(*(const unsigned char *)(src + i + 1))
-		    && isxdigit(*(const unsigned char *)(src + i + 2))) {
-			a = tolower(*(const unsigned char *)(src + i + 1));
-			b = tolower(*(const unsigned char *)(src + i + 2));
+		    && isxdigit((unsigned char)src[i + 1])
+		    && isxdigit((unsigned char)src[i + 2])) {
+			a = tolower((unsigned char)src[i + 1]);
+			b = tolower((unsigned char)src[i + 2]);
 			dst.push_back((char)((HEXTOI(a) << 4) | HEXTOI(b)));
 			i += 2;
 		} else if (is_form_url_encoded && src[i] == '+') {
@@ -608,43 +608,42 @@ CivetServer::urlEncode(const char *src,
 		dst.clear();
 
 	for (; src_len > 0; src++, src_len--) {
-		if (isalnum(*(const unsigned char *)src)
-		    || strchr(dont_escape, *(const unsigned char *)src) != NULL) {
+		if (isalnum((unsigned char)*src) || strchr(dont_escape, *src) != NULL) {
 			dst.push_back(*src);
 		} else {
 			dst.push_back('%');
-			dst.push_back(hex[(*(const unsigned char *)src) >> 4]);
-			dst.push_back(hex[(*(const unsigned char *)src) & 0xf]);
+			dst.push_back(hex[(unsigned char)*src >> 4]);
+			dst.push_back(hex[(unsigned char)*src & 0xf]);
 		}
 	}
 }
 
-std::vector<int>
-CivetServer::getListeningPorts()
-{
-	std::vector<struct mg_server_port> server_ports = getListeningPortsFull();
-
-	std::vector<int> ports(server_ports.size());
-	for (size_t i = 0; i < server_ports.size(); i++) {
-		ports[i] = server_ports[i].port;
-	}
-
-	return ports;
-}
-
-std::vector<struct mg_server_port>
-CivetServer::getListeningPortsFull()
-{
-	std::vector<struct mg_server_port> server_ports(50);
-	int size = mg_get_server_ports(context,
-	                               (int)server_ports.size(),
-	                               &server_ports[0]);
-	if (size <= 0) {
-		server_ports.resize(0);
-		return server_ports;
-	}
-	server_ports.resize(size);
-	return server_ports;
+std::vector<int>
+CivetServer::getListeningPorts()
+{
+	std::vector<struct mg_server_port> server_ports = getListeningPortsFull();
+
+	std::vector<int> ports(server_ports.size());
+	for (size_t i = 0; i < server_ports.size(); i++) {
+		ports[i] = server_ports[i].port;
+	}
+
+	return ports;
+}
+
+std::vector<struct mg_server_port>
+CivetServer::getListeningPortsFull()
+{
+	std::vector<struct mg_server_port> server_ports(50);
+	int size = mg_get_server_ports(context,
+	                               (int)server_ports.size(),
+	                               &server_ports[0]);
+	if (size <= 0) {
+		server_ports.resize(0);
+		return server_ports;
+	}
+	server_ports.resize(size);
+	return server_ports;
 }
 
 CivetServer::CivetConnection::CivetConnection()

+ 230 - 215
src/civetweb.c

@@ -2201,18 +2201,18 @@ static struct ssl_func crypto_sw[] = {{"CRYPTO_num_locks", NULL},
 
 
 #if !defined(NO_CACHING)
-static const char *month_names[] = {"Jan",
-                                    "Feb",
-                                    "Mar",
-                                    "Apr",
-                                    "May",
-                                    "Jun",
-                                    "Jul",
-                                    "Aug",
-                                    "Sep",
-                                    "Oct",
-                                    "Nov",
-                                    "Dec"};
+static const char month_names[][4] = {"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
@@ -2753,14 +2753,12 @@ struct mg_connection {
 	                       * throttle */
 
 	time_t last_throttle_time;   /* Last time throttled data was sent */
-	int64_t last_throttle_bytes; /* Bytes sent this second */
+	int 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 */
 };
 
 
@@ -3273,7 +3271,7 @@ mg_strlcpy(register char *dst, register const char *src, size_t n)
 static int
 lowercase(const char *s)
 {
-	return tolower(*(const unsigned char *)s);
+	return tolower((unsigned char)*s);
 }
 
 
@@ -5110,10 +5108,11 @@ mg_wcscasecmp(const wchar_t *s1, const wchar_t *s2)
 	int diff;
 
 	do {
-		diff = tolower(*s1) - tolower(*s2);
+		diff = ((*s1 >= L'A') && (*s1 <= L'Z') ? (*s1 - L'A' + L'a') : *s1)
+		       - ((*s2 >= L'A') && (*s2 <= L'Z') ? (*s2 - L'A' + L'a') : *s2);
 		s1++;
 		s2++;
-	} while ((diff == 0) && (s1[-1] != '\0'));
+	} while ((diff == 0) && (s1[-1] != L'\0'));
 
 	return diff;
 }
@@ -5191,19 +5190,6 @@ path_to_unicode(const struct mg_connection *conn,
 }
 
 
-/* Windows happily opens files with some garbage at the end of file name.
- * For example, fopen("a.cgi    ", "r") on Windows successfully opens
- * "a.cgi", despite one would expect an error back.
- * This function returns non-0 if path ends with some garbage. */
-static int
-path_cannot_disclose_cgi(const char *path)
-{
-	static const char *allowed_last_characters = "_-";
-	int last = path[strlen(path) - 1];
-	return isalnum(last) || strchr(allowed_last_characters, last) != NULL;
-}
-
-
 static int
 mg_stat(const struct mg_connection *conn,
         const char *path,
@@ -5212,6 +5198,7 @@ mg_stat(const struct mg_connection *conn,
 	wchar_t wbuf[W_PATH_MAX];
 	WIN32_FILE_ATTRIBUTE_DATA info;
 	time_t creation_time;
+	size_t len;
 
 	if (!filep) {
 		return 0;
@@ -5248,7 +5235,12 @@ mg_stat(const struct mg_connection *conn,
 	}
 
 	path_to_unicode(conn, path, wbuf, ARRAY_SIZE(wbuf));
-	if (GetFileAttributesExW(wbuf, GetFileExInfoStandard, &info) != 0) {
+	/* Windows happily opens files with some garbage at the end of file name.
+	 * For example, fopen("a.cgi    ", "r") on Windows successfully opens
+	 * "a.cgi", despite one would expect an error back. */
+	len = strlen(path);
+	if ((len > 0) && (path[len - 1] != ' ') && (path[len - 1] != '.')
+	    && (GetFileAttributesExW(wbuf, GetFileExInfoStandard, &info) != 0)) {
 		filep->size = MAKEUQUAD(info.nFileSizeLow, info.nFileSizeHigh);
 		filep->last_modified =
 		    SYS2UNIX_TIME(info.ftLastWriteTime.dwLowDateTime,
@@ -5265,15 +5257,6 @@ mg_stat(const struct mg_connection *conn,
 		}
 
 		filep->is_directory = info.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY;
-		/* If file name is fishy, reset the file structure and return
-		 * error.
-		 * Note it is important to reset, not just return the error, cause
-		 * functions like is_file_opened() check the struct. */
-		if (!filep->is_directory && !path_cannot_disclose_cgi(path)) {
-			memset(filep, 0, sizeof(*filep));
-			return 0;
-		}
-
 		return 1;
 	}
 
@@ -5421,9 +5404,10 @@ poll(struct mg_pollfd *pfd, unsigned int n, int milliseconds)
 
 	for (i = 0; i < n; i++) {
 		if (pfd[i].events & POLLIN) {
-			FD_SET((SOCKET)pfd[i].fd, &rset);
-		} else if (pfd[i].events & POLLOUT) {
-			FD_SET((SOCKET)pfd[i].fd, &wset);
+			FD_SET(pfd[i].fd, &rset);
+		}
+		if (pfd[i].events & POLLOUT) {
+			FD_SET(pfd[i].fd, &wset);
 		}
 		pfd[i].revents = 0;
 
@@ -5503,9 +5487,9 @@ mg_start_thread_with_id(unsigned(__stdcall *f)(void *),
 	HANDLE threadhandle;
 	int result = -1;
 
-	uip = _beginthreadex(NULL, 0, (unsigned(__stdcall *)(void *))f, p, 0, NULL);
+	uip = _beginthreadex(NULL, 0, f, p, 0, NULL);
 	threadhandle = (HANDLE)uip;
-	if ((uip != (uintptr_t)(-1L)) && (threadidptr != NULL)) {
+	if ((uip != 0) && (threadidptr != NULL)) {
 		*threadidptr = threadhandle;
 		result = 0;
 	}
@@ -5625,9 +5609,9 @@ waitpid(pid_t pid, int *status, int flags)
 static void
 trim_trailing_whitespaces(char *s)
 {
-	char *e = s + strlen(s) - 1;
-	while ((e > s) && isspace(*(unsigned char *)e)) {
-		*e-- = '\0';
+	char *e = s + strlen(s);
+	while ((e > s) && isspace((unsigned char)e[-1])) {
+		*(--e) = '\0';
 	}
 }
 
@@ -5965,6 +5949,8 @@ spawn_process(struct mg_connection *conn,
 			                fderr[1],
 			                strerror(ERRNO));
 		} else {
+			struct sigaction sa;
+
 			/* Keep stderr and stdout in two different pipes.
 			 * Stdout will be sent back to the client,
 			 * stderr should go into a server error log. */
@@ -5982,7 +5968,9 @@ spawn_process(struct mg_connection *conn,
 			 * POSIX.1-2001 and Linux's implementation, SIGCHLD's handler
 			 * will leave unchanged after exec if it was set to be ignored.
 			 * Restore it to default action. */
-			signal(SIGCHLD, SIG_DFL);
+			memset(&sa, 0, sizeof(sa));
+			sa.sa_handler = SIG_DFL;
+			sigaction(SIGCHLD, &sa, NULL);
 
 			interp = conn->dom_ctx->config[CGI_INTERPRETER];
 			if (interp == NULL) {
@@ -6267,16 +6255,16 @@ push_inner(struct mg_context *ctx,
 }
 
 
-static int64_t
+static int
 push_all(struct mg_context *ctx,
          FILE *fp,
          SOCKET sock,
          SSL *ssl,
          const char *buf,
-         int64_t len)
+         int len)
 {
 	double timeout = -1.0;
-	int64_t n, nwritten = 0;
+	int n, nwritten = 0;
 
 	if (ctx == NULL) {
 		return -1;
@@ -6287,10 +6275,10 @@ push_all(struct mg_context *ctx,
 	}
 
 	while ((len > 0) && (ctx->stop_flag == 0)) {
-		n = push_inner(ctx, fp, sock, ssl, buf + nwritten, (int)len, timeout);
+		n = push_inner(ctx, fp, sock, ssl, buf + nwritten, len, timeout);
 		if (n < 0) {
 			if (nwritten == 0) {
-				nwritten = n; /* Propagate the error */
+				nwritten = -1; /* Propagate the error */
 			}
 			break;
 		} else if (n == 0) {
@@ -6745,7 +6733,7 @@ mg_read(struct mg_connection *conn, void *buf, size_t len)
 						}
 						break;
 					}
-					if (!isxdigit(lenbuf[i])) {
+					if (!isxdigit((unsigned char)lenbuf[i])) {
 						/* illegal character for chunk length */
 						return -1;
 					}
@@ -6772,11 +6760,14 @@ int
 mg_write(struct mg_connection *conn, const void *buf, size_t len)
 {
 	time_t now;
-	int64_t n, total, allowed;
+	int n, total, allowed;
 
 	if (conn == NULL) {
 		return 0;
 	}
+	if (len > INT_MAX) {
+		return -1;
+	}
 
 	if (conn->throttle > 0) {
 		if ((now = time(NULL)) != conn->last_throttle_time) {
@@ -6784,28 +6775,28 @@ mg_write(struct mg_connection *conn, const void *buf, size_t len)
 			conn->last_throttle_bytes = 0;
 		}
 		allowed = conn->throttle - conn->last_throttle_bytes;
-		if (allowed > (int64_t)len) {
-			allowed = (int64_t)len;
+		if (allowed > (int)len) {
+			allowed = (int)len;
 		}
 		if ((total = push_all(conn->phys_ctx,
 		                      NULL,
 		                      conn->client.sock,
 		                      conn->ssl,
 		                      (const char *)buf,
-		                      (int64_t)allowed))
+		                      allowed))
 		    == allowed) {
 			buf = (const char *)buf + total;
 			conn->last_throttle_bytes += total;
-			while ((total < (int64_t)len) && (conn->phys_ctx->stop_flag == 0)) {
-				allowed = (conn->throttle > ((int64_t)len - total))
-				              ? (int64_t)len - total
+			while ((total < (int)len) && (conn->phys_ctx->stop_flag == 0)) {
+				allowed = (conn->throttle > ((int)len - total))
+				              ? (int)len - total
 				              : conn->throttle;
 				if ((n = push_all(conn->phys_ctx,
 				                  NULL,
 				                  conn->client.sock,
 				                  conn->ssl,
 				                  (const char *)buf,
-				                  (int64_t)allowed))
+				                  allowed))
 				    != allowed) {
 					break;
 				}
@@ -6822,12 +6813,12 @@ mg_write(struct mg_connection *conn, const void *buf, size_t len)
 		                 conn->client.sock,
 		                 conn->ssl,
 		                 (const char *)buf,
-		                 (int64_t)len);
+		                 (int)len);
 	}
 	if (total > 0) {
 		conn->num_bytes_sent += total;
 	}
-	return (int)total;
+	return total;
 }
 
 
@@ -7018,10 +7009,10 @@ mg_url_decode(const char *src,
 
 	for (i = j = 0; (i < src_len) && (j < (dst_len - 1)); i++, j++) {
 		if ((i < src_len - 2) && (src[i] == '%')
-		    && isxdigit(*(const unsigned char *)(src + i + 1))
-		    && isxdigit(*(const unsigned char *)(src + i + 2))) {
-			a = tolower(*(const unsigned char *)(src + i + 1));
-			b = tolower(*(const unsigned char *)(src + i + 2));
+		    && isxdigit((unsigned char)src[i + 1])
+		    && isxdigit((unsigned char)src[i + 2])) {
+			a = tolower((unsigned char)src[i + 1]);
+			b = tolower((unsigned char)src[i + 2]);
 			dst[j] = (char)((HEXTOI(a) << 4) | HEXTOI(b));
 			i += 2;
 		} else if (is_form_url_encoded && (src[i] == '+')) {
@@ -7665,7 +7656,7 @@ get_http_header_len(const char *buf, int buflen)
 	int i;
 	for (i = 0; i < buflen; i++) {
 		/* Do an unsigned comparison in some conditions below */
-		const unsigned char c = ((const unsigned char *)buf)[i];
+		const unsigned char c = (unsigned char)buf[i];
 
 		if ((c < 128) && ((char)c != '\r') && ((char)c != '\n')
 		    && !isprint(c)) {
@@ -8154,7 +8145,7 @@ parse_auth_header(struct mg_connection *conn,
 	/* Parse authorization header */
 	for (;;) {
 		/* Gobble initial spaces */
-		while (isspace(*(unsigned char *)s)) {
+		while (isspace((unsigned char)*s)) {
 			s++;
 		}
 		name = skip_quoted(&s, "=", " ", 0);
@@ -8323,8 +8314,8 @@ read_auth_file(struct mg_file *filep,
 	while (mg_fgets(workdata->buf, sizeof(workdata->buf), filep, &p) != NULL) {
 		l = strlen(workdata->buf);
 		while (l > 0) {
-			if (isspace(workdata->buf[l - 1])
-			    || iscntrl(workdata->buf[l - 1])) {
+			if (isspace((unsigned char)workdata->buf[l - 1])
+			    || iscntrl((unsigned char)workdata->buf[l - 1])) {
 				l--;
 				workdata->buf[l] = 0;
 			} else
@@ -8629,7 +8620,7 @@ mg_modify_passwords_file(const char *fname,
 	/* Do not allow control characters like newline in user name and domain.
 	 * Do not allow excessively long names either. */
 	for (i = 0; ((i < 255) && (user[i] != 0)); i++) {
-		if (iscntrl(user[i])) {
+		if (iscntrl((unsigned char)user[i])) {
 			return 0;
 		}
 	}
@@ -8637,7 +8628,7 @@ mg_modify_passwords_file(const char *fname,
 		return 0;
 	}
 	for (i = 0; ((i < 255) && (domain[i] != 0)); i++) {
-		if (iscntrl(domain[i])) {
+		if (iscntrl((unsigned char)domain[i])) {
 			return 0;
 		}
 	}
@@ -8989,13 +8980,13 @@ mg_url_encode(const char *src, char *dst, size_t dst_len)
 	const char *end = dst + dst_len - 1;
 
 	for (; ((*src != '\0') && (pos < end)); src++, pos++) {
-		if (isalnum(*(const unsigned char *)src)
-		    || (strchr(dont_escape, *(const unsigned char *)src) != NULL)) {
+		if (isalnum((unsigned char)*src)
+		    || (strchr(dont_escape, *src) != NULL)) {
 			*pos = *src;
 		} else if (pos + 2 < end) {
 			pos[0] = '%';
-			pos[1] = hex[(*(const unsigned char *)src) >> 4];
-			pos[2] = hex[(*(const unsigned char *)src) & 0xf];
+			pos[1] = hex[(unsigned char)*src >> 4];
+			pos[2] = hex[(unsigned char)*src & 0xf];
 			pos += 2;
 		} else {
 			break;
@@ -9011,8 +9002,8 @@ mg_url_encode(const char *src, char *dst, size_t dst_len)
 static int
 print_dir_entry(struct de *de)
 {
-	size_t hrefsize;
-	char *href;
+	size_t namesize, escsize, i;
+	char *href, *esc, *p;
 	char size[64], mod[64];
 #if defined(REENTRANT_TIME)
 	struct tm _tm;
@@ -9021,11 +9012,30 @@ print_dir_entry(struct de *de)
 	struct tm *tm;
 #endif
 
-	hrefsize = PATH_MAX * 3; /* worst case */
-	href = (char *)mg_malloc(hrefsize);
+	/* Estimate worst case size for encoding and escaping */
+	namesize = strlen(de->file_name) + 1;
+	escsize = de->file_name[strcspn(de->file_name, "&<>")] ? namesize * 5 : 0;
+	href = (char *)mg_malloc(namesize * 3 + escsize);
 	if (href == NULL) {
 		return -1;
 	}
+	mg_url_encode(de->file_name, href, namesize * 3);
+	esc = NULL;
+	if (escsize > 0) {
+		/* HTML escaping needed */
+		esc = href + namesize * 3;
+		for (i = 0, p = esc; de->file_name[i]; i++, p += strlen(p)) {
+			mg_strlcpy(p, de->file_name + i, 2);
+			if (*p == '&') {
+				strcpy(p, "&amp;");
+			} else if (*p == '<') {
+				strcpy(p, "&lt;");
+			} else if (*p == '>') {
+				strcpy(p, "&gt;");
+			}
+		}
+	}
+
 	if (de->file.is_directory) {
 		mg_snprintf(de->conn,
 		            NULL, /* Buffer is big enough */
@@ -9081,16 +9091,15 @@ print_dir_entry(struct de *de)
 		mg_strlcpy(mod, "01-Jan-1970 00:00", sizeof(mod));
 		mod[sizeof(mod) - 1] = '\0';
 	}
-	mg_url_encode(de->file_name, href, hrefsize);
 	mg_printf(de->conn,
-              "<tr><td><a href=\"%s%s\">%s%s</a></td>"
-              "<td>&nbsp;%s</td><td>&nbsp;&nbsp;%s</td></tr>\n",
-              href,
-              de->file.is_directory ? "/" : "",
-              de->file_name,
-              de->file.is_directory ? "/" : "",
-              mod,
-              size);
+	          "<tr><td><a href=\"%s%s\">%s%s</a></td>"
+	          "<td>&nbsp;%s</td><td>&nbsp;&nbsp;%s</td></tr>\n",
+	          href,
+	          de->file.is_directory ? "/" : "",
+	          esc ? esc : de->file_name,
+	          de->file.is_directory ? "/" : "",
+	          mod,
+	          size);
 	mg_free(href);
 	return 0;
 }
@@ -9108,8 +9117,8 @@ compare_dir_entries(const void *p1, const void *p2)
 		const char *query_string = a->conn->request_info.query_string;
 		int cmp_result = 0;
 
-		if (query_string == NULL) {
-			query_string = "na";
+		if ((query_string == NULL) || (query_string[0] == '\0')) {
+			query_string = "n";
 		}
 
 		if (a->file.is_directory && !b->file.is_directory) {
@@ -9324,7 +9333,8 @@ handle_directory_request(struct mg_connection *conn, const char *dir)
 	unsigned int i;
 	int sort_direction;
 	struct dir_scan_data data = {NULL, 0, 128};
-	char date[64];
+	char date[64], *esc, *p;
+	const char *title;
 	time_t curtime = time(NULL);
 
 	if (!scan_directory(conn, dir, &data, dir_scan_callback)) {
@@ -9342,7 +9352,29 @@ handle_directory_request(struct mg_connection *conn, const char *dir)
 		return;
 	}
 
+	esc = NULL;
+	title = conn->request_info.local_uri;
+	if (title[strcspn(title, "&<>")]) {
+		/* HTML escaping needed */
+		esc = (char *)mg_malloc(strlen(title) * 5 + 1);
+		if (esc) {
+			for (i = 0, p = esc; title[i]; i++, p += strlen(p)) {
+				mg_strlcpy(p, title + i, 2);
+				if (*p == '&') {
+					strcpy(p, "&amp;");
+				} else if (*p == '<') {
+					strcpy(p, "&lt;");
+				} else if (*p == '>') {
+					strcpy(p, "&gt;");
+				}
+			}
+		} else {
+			title = "";
+		}
+	}
+
 	sort_direction = ((conn->request_info.query_string != NULL)
+	                  && (conn->request_info.query_string[0] != '\0')
 	                  && (conn->request_info.query_string[1] == 'd'))
 	                     ? 'a'
 	                     : 'd';
@@ -9364,20 +9396,21 @@ handle_directory_request(struct mg_connection *conn, const char *dir)
 	          "<th><a href=\"?d%c\">Modified</a></th>"
 	          "<th><a href=\"?s%c\">Size</a></th></tr>"
 	          "<tr><td colspan=\"3\"><hr></td></tr>",
-	          conn->request_info.local_uri,
-	          conn->request_info.local_uri,
+	          esc ? esc : title,
+	          esc ? esc : title,
 	          sort_direction,
 	          sort_direction,
 	          sort_direction);
+	mg_free(esc);
 
 	/* Print first entry - link to a parent directory */
 	mg_printf(conn,
-              "<tr><td><a href=\"%s\">%s</a></td>"
-              "<td>&nbsp;%s</td><td>&nbsp;&nbsp;%s</td></tr>\n",
-              "..",
-              "Parent directory",
-              "-",
-              "-");
+	          "<tr><td><a href=\"%s\">%s</a></td>"
+	          "<td>&nbsp;%s</td><td>&nbsp;&nbsp;%s</td></tr>\n",
+	          "..",
+	          "Parent directory",
+	          "-",
+	          "-");
 
 	/* Sort and print directory entries */
 	if (data.entries != NULL) {
@@ -9392,7 +9425,7 @@ handle_directory_request(struct mg_connection *conn, const char *dir)
 		mg_free(data.entries);
 	}
 
-	mg_printf(conn, "%s", "</table></body></html>");
+	mg_printf(conn, "%s", "</table></pre></body></html>");
 	conn->status_code = 200;
 }
 
@@ -10028,7 +10061,7 @@ skip_to_end_of_word_and_terminate(char **ppw, int eol)
 {
 	/* Forward until a space is found - use isgraph here */
 	/* See http://www.cplusplus.com/reference/cctype/ */
-	while (isgraph(**ppw)) {
+	while (isgraph((unsigned char)**ppw)) {
 		(*ppw)++;
 	}
 
@@ -10049,12 +10082,12 @@ skip_to_end_of_word_and_terminate(char **ppw, int eol)
 	do {
 		**ppw = 0;
 		(*ppw)++;
-	} while ((**ppw) && isspace(**ppw));
+	} while (isspace((unsigned char)**ppw));
 
 	/* Check after term */
 	if (!eol) {
 		/* if it's not the end of line, there must be a next word */
-		if (!isgraph(**ppw)) {
+		if (!isgraph((unsigned char)**ppw)) {
 			return -1;
 		}
 	}
@@ -10239,7 +10272,7 @@ parse_http_request(char *buf, int len, struct mg_request_info *ri)
 	/* RFC says that all initial whitespaces should be ingored */
 	/* This included all leading \r and \n (isspace) */
 	/* See table: http://www.cplusplus.com/reference/cctype/ */
-	while ((len > 0) && isspace(*(unsigned char *)buf)) {
+	while ((len > 0) && isspace((unsigned char)*buf)) {
 		buf++;
 		len--;
 		init_skip++;
@@ -10251,7 +10284,7 @@ parse_http_request(char *buf, int len, struct mg_request_info *ri)
 	}
 
 	/* Control characters are not allowed, including zero */
-	if (iscntrl(*(unsigned char *)buf)) {
+	if (iscntrl((unsigned char)*buf)) {
 		return -1;
 	}
 
@@ -10326,7 +10359,7 @@ parse_http_response(char *buf, int len, struct mg_response_info *ri)
 	/* RFC says that all initial whitespaces should be ingored */
 	/* This included all leading \r and \n (isspace) */
 	/* See table: http://www.cplusplus.com/reference/cctype/ */
-	while ((len > 0) && isspace(*(unsigned char *)buf)) {
+	while ((len > 0) && isspace((unsigned char)*buf)) {
 		buf++;
 		len--;
 		init_skip++;
@@ -10338,7 +10371,7 @@ parse_http_response(char *buf, int len, struct mg_response_info *ri)
 	}
 
 	/* Control characters are not allowed, including zero */
-	if (iscntrl(*(unsigned char *)buf)) {
+	if (iscntrl((unsigned char)*buf)) {
 		return -1;
 	}
 
@@ -10360,7 +10393,7 @@ parse_http_response(char *buf, int len, struct mg_response_info *ri)
 		return -1;
 	}
 	buf += 5;
-	if (!isgraph(buf[0])) {
+	if (!isgraph((unsigned char)buf[0])) {
 		/* Invalid request */
 		return -1;
 	}
@@ -10389,7 +10422,7 @@ parse_http_response(char *buf, int len, struct mg_response_info *ri)
 
 	/* Find end of status text */
 	/* isgraph or isspace = isprint */
-	while (isprint(*buf)) {
+	while (isprint((unsigned char)*buf)) {
 		buf++;
 	}
 	if ((*buf != '\r') && (*buf != '\n')) {
@@ -10399,7 +10432,7 @@ parse_http_response(char *buf, int len, struct mg_response_info *ri)
 	do {
 		*buf = 0;
 		buf++;
-	} while ((*buf) && isspace(*buf));
+	} while (isspace((unsigned char)*buf));
 
 
 	/* Parse all HTTP headers */
@@ -10499,7 +10532,7 @@ forward_body_data(struct mg_connection *conn, FILE *fp, SOCKET sock, SSL *ssl)
 	const char *expect, *body;
 	char buf[MG_BUF_LEN];
 	int to_read, nread, success = 0;
-	int64_t buffered_len;
+	int buffered_len;
 	double timeout = -1.0;
 
 	if (!conn) {
@@ -10538,8 +10571,7 @@ forward_body_data(struct mg_connection *conn, FILE *fp, SOCKET sock, SSL *ssl)
 			conn->status_code = 200;
 		}
 
-		buffered_len = (int64_t)(conn->data_len) - (int64_t)conn->request_len
-		               - conn->consumed_content;
+		buffered_len = conn->data_len - conn->request_len;
 
 		DEBUG_ASSERT(buffered_len >= 0);
 		DEBUG_ASSERT(conn->consumed_content == 0);
@@ -10553,9 +10585,8 @@ forward_body_data(struct mg_connection *conn, FILE *fp, SOCKET sock, SSL *ssl)
 			if ((int64_t)buffered_len > conn->content_len) {
 				buffered_len = (int)conn->content_len;
 			}
-			body = conn->buf + conn->request_len + conn->consumed_content;
-			push_all(
-			    conn->phys_ctx, fp, sock, ssl, body, (int64_t)buffered_len);
+			body = conn->buf + conn->request_len;
+			push_all(conn->phys_ctx, fp, sock, ssl, body, buffered_len);
 			conn->consumed_content += buffered_len;
 		}
 
@@ -10575,8 +10606,8 @@ forward_body_data(struct mg_connection *conn, FILE *fp, SOCKET sock, SSL *ssl)
 				    != nread) {
 					break;
 				}
+				conn->consumed_content += nread;
 			}
-			conn->consumed_content += nread;
 		}
 
 		if (conn->consumed_content == conn->content_len) {
@@ -10880,7 +10911,7 @@ prepare_cgi_environment(struct mg_connection *conn,
 			if (*p == '-') {
 				*p = '_';
 			}
-			*p = (char)toupper(*(unsigned char *)p);
+			*p = (char)toupper((unsigned char)*p);
 		}
 
 		addenv(env,
@@ -11215,8 +11246,7 @@ handle_cgi_request(struct mg_connection *conn, const char *prog)
 	    != NULL) {
 		conn->status_code = atoi(status);
 		status_text = status;
-		while (isdigit(*(const unsigned char *)status_text)
-		       || *status_text == ' ') {
+		while (isdigit((unsigned char)*status_text) || *status_text == ' ') {
 			status_text++;
 		}
 	} else if (get_header(ri.http_headers, ri.num_headers, "Location")
@@ -11914,16 +11944,40 @@ send_options(struct mg_connection *conn)
 
 
 /* Writes PROPFIND properties for a collection element */
-static void
+static int
 print_props(struct mg_connection *conn,
             const char *uri,
+            const char *name,
             struct mg_file_stat *filep)
 {
-	char mtime[64];
+	size_t href_size, i, j;
+	int len;
+	char *href, mtime[64];
 
-	if ((conn == NULL) || (uri == NULL) || (filep == NULL)) {
-		return;
+	if ((conn == NULL) || (uri == NULL) || (name == NULL) || (filep == NULL)) {
+		return 0;
 	}
+	/* Estimate worst case size for encoding */
+	href_size = (strlen(uri) + strlen(name)) * 3 + 1;
+	href = (char *)mg_malloc(href_size);
+	if (href == NULL) {
+		return 0;
+	}
+	len = mg_url_encode(uri, href, href_size);
+	if (len >= 0) {
+		/* Append an extra string */
+		mg_url_encode(name, href + len, href_size - (size_t)len);
+	}
+	/* Directory separator should be preserved. */
+	for (i = j = 0; href[i]; j++) {
+		if (!strncmp(href + i, "%2f", 3)) {
+			href[j] = '/';
+			i += 3;
+		} else {
+			href[j] = href[i++];
+		}
+	}
+	href[j] = '\0';
 
 	gmt_time_string(mtime, sizeof(mtime), &filep->last_modified);
 	mg_printf(conn,
@@ -11938,45 +11992,23 @@ print_props(struct mg_connection *conn,
 	          "<d:status>HTTP/1.1 200 OK</d:status>"
 	          "</d:propstat>"
 	          "</d:response>\n",
-	          uri,
+	          href,
 	          filep->is_directory ? "<d:collection/>" : "",
 	          filep->size,
 	          mtime);
+	mg_free(href);
+	return 1;
 }
 
 
 static int
 print_dav_dir_entry(struct de *de, void *data)
 {
-	char href[PATH_MAX];
-	int truncated;
-
 	struct mg_connection *conn = (struct mg_connection *)data;
-	if (!de || !conn) {
+	if (!de || !conn || !print_props(conn, conn->request_info.local_uri,
+	                                 de->file_name, &de->file)) {
 		return -1;
 	}
-	mg_snprintf(conn,
-	            &truncated,
-	            href,
-	            sizeof(href),
-	            "%s%s",
-	            conn->request_info.local_uri,
-	            de->file_name);
-
-	if (!truncated) {
-		size_t href_encoded_size;
-		char *href_encoded;
-
-		href_encoded_size = PATH_MAX * 3; /* worst case */
-		href_encoded = (char *)mg_malloc(href_encoded_size);
-		if (href_encoded == NULL) {
-			return -1;
-		}
-		mg_url_encode(href, href_encoded, href_encoded_size);
-		print_props(conn, href_encoded, &de->file);
-		mg_free(href_encoded);
-	}
-
 	return 0;
 }
 
@@ -12014,7 +12046,7 @@ handle_propfind(struct mg_connection *conn,
 	          "<d:multistatus xmlns:d='DAV:'>\n");
 
 	/* Print properties for the requested resource itself */
-	print_props(conn, conn->request_info.local_uri, filep);
+	print_props(conn, conn->request_info.local_uri, "", filep);
 
 	/* If it is a directory, print directory entries too if Depth is not 0
 	 */
@@ -12668,7 +12700,7 @@ handle_websocket_request(struct mg_connection *conn,
 					curSubProtocol = protocol;
 					len = sep ? (unsigned long)(sep - protocol)
 					          : (unsigned long)strlen(protocol);
-					while (sep && isspace(*++sep))
+					while (sep && isspace((unsigned char)*++sep))
 						; // ignore leading whitespaces
 					protocol = sep;
 
@@ -12711,7 +12743,7 @@ handle_websocket_request(struct mg_connection *conn,
 				 * has
 				 * offered.
 				 */
-				while (isspace(*++sep)) {
+				while (isspace((unsigned char)*++sep)) {
 					; /* ignore leading whitespaces */
 				}
 				conn->request_info.acceptedWebSocketSubprotocol = sep;
@@ -13045,7 +13077,7 @@ alloc_get_host(struct mg_connection *conn)
 		mg_strlcpy(buf, host_header, buflen);
 		buf[buflen - 1] = '\0';
 		host = buf;
-		while (isspace(*host)) {
+		while (isspace((unsigned char)*host)) {
 			host++;
 		}
 
@@ -15107,7 +15139,7 @@ sslize(struct mg_connection *conn,
 	/* SSL functions may fail and require to be called again:
 	 * see https://www.openssl.org/docs/manmaster/ssl/SSL_get_error.html
 	 * Here "func" could be SSL_connect or SSL_accept. */
-	for (i = 16; i <= timeout; i *= 2) {
+	for (i = 0; i <= timeout; i += 50) {
 		ret = func(conn->ssl);
 		if (ret != 1) {
 			err = SSL_get_error(conn->ssl, ret);
@@ -15115,14 +15147,24 @@ sslize(struct mg_connection *conn,
 			    || (err == SSL_ERROR_WANT_ACCEPT)
 			    || (err == SSL_ERROR_WANT_READ) || (err == SSL_ERROR_WANT_WRITE)
 			    || (err == SSL_ERROR_WANT_X509_LOOKUP)) {
-				/* Need to retry the function call "later".
-				 * See https://linux.die.net/man/3/ssl_get_error
-				 * This is typical for non-blocking sockets. */
 				if (*stop_server) {
 					/* Don't wait if the server is going to be stopped. */
 					break;
 				}
-				mg_sleep(i);
+				if (err == SSL_ERROR_WANT_X509_LOOKUP) {
+					/* Simply retry the function call. */
+					mg_sleep(50);
+				} else {
+					/* Need to retry the function call "later".
+					 * See https://linux.die.net/man/3/ssl_get_error
+					 * This is typical for non-blocking sockets. */
+					struct mg_pollfd pfd;
+					pfd.fd = conn->client.sock;
+					pfd.events = ((err == SSL_ERROR_WANT_CONNECT)
+					              || (err == SSL_ERROR_WANT_WRITE)) ? POLLOUT
+					                                                : POLLIN;
+					mg_poll(&pfd, 1, 50, stop_server);
+				}
 
 			} else if (err == SSL_ERROR_SYSCALL) {
 				/* This is an IO error. Look at errno. */
@@ -17775,17 +17817,11 @@ produce_socket(struct mg_context *ctx, const struct socket *sp)
 #endif /* ALTERNATIVE_QUEUE */
 
 
-struct worker_thread_args {
-	struct mg_context *ctx;
-	int index;
-};
-
-
-static void *
-worker_thread_run(struct worker_thread_args *thread_args)
+static void
+worker_thread_run(struct mg_connection *conn)
 {
-	struct mg_context *ctx = thread_args->ctx;
-	struct mg_connection *conn;
+	struct mg_context *ctx = conn->phys_ctx;
+	int thread_index;
 	struct mg_workerTLS tls;
 #if defined(MG_LEGACY_INTERFACE)
 	uint32_t addr;
@@ -17808,15 +17844,15 @@ worker_thread_run(struct worker_thread_args *thread_args)
 	}
 
 	/* Connection structure has been pre-allocated */
-	if (((int)thread_args->index < 0)
-	    || ((unsigned)thread_args->index
+	thread_index = (int)(conn - ctx->worker_connections);
+	if ((thread_index < 0)
+	    || ((unsigned)thread_index
 	        >= (unsigned)ctx->cfg_worker_threads)) {
 		mg_cry_internal(fc(ctx),
 		                "Internal error: Invalid worker index %i",
-		                (int)thread_args->index);
-		return NULL;
+		                thread_index);
+		return;
 	}
-	conn = ctx->worker_connections + thread_args->index;
 
 	/* Request buffers are not pre-allocated. They are private to the
 	 * request and do not contain any state information that might be
@@ -17825,16 +17861,14 @@ worker_thread_run(struct worker_thread_args *thread_args)
 	if (conn->buf == NULL) {
 		mg_cry_internal(fc(ctx),
 		                "Out of memory: Cannot allocate buffer for worker %i",
-		                (int)thread_args->index);
-		return NULL;
+		                thread_index);
+		return;
 	}
 	conn->buf_size = (int)ctx->max_request_size;
 
-	conn->phys_ctx = ctx;
 	conn->dom_ctx = &(ctx->dd); /* Use default domain and default host */
 	conn->host = NULL;          /* until we have more information. */
 
-	conn->thread_index = thread_args->index;
 	conn->request_info.user_data = ctx->user_data;
 	/* Allocate a mutex for this connection to allow communication both
 	 * within the request handler and from elsewhere in the application
@@ -17842,7 +17876,7 @@ worker_thread_run(struct worker_thread_args *thread_args)
 	if (0 != pthread_mutex_init(&conn->mutex, &pthread_mutex_attr)) {
 		mg_free(conn->buf);
 		mg_cry_internal(fc(ctx), "%s", "Cannot create mutex");
-		return NULL;
+		return;
 	}
 
 #if defined(USE_SERVER_STATS)
@@ -17852,7 +17886,7 @@ worker_thread_run(struct worker_thread_args *thread_args)
 	/* Call consume_socket() even when ctx->stop_flag > 0, to let it
 	 * signal sq_empty condvar to wake up the master waiting in
 	 * produce_socket() */
-	while (consume_socket(ctx, &conn->client, conn->thread_index)) {
+	while (consume_socket(ctx, &conn->client, thread_index)) {
 
 		conn->conn_birth_time = time(NULL);
 
@@ -17943,7 +17977,6 @@ worker_thread_run(struct worker_thread_args *thread_args)
 #endif
 
 	DEBUG_TRACE("%s", "exiting");
-	return NULL;
 }
 
 
@@ -17951,18 +17984,13 @@ worker_thread_run(struct worker_thread_args *thread_args)
 #if defined(_WIN32)
 static unsigned __stdcall worker_thread(void *thread_func_param)
 {
-	struct worker_thread_args *pwta =
-	    (struct worker_thread_args *)thread_func_param;
-	worker_thread_run(pwta);
-	mg_free(thread_func_param);
+	worker_thread_run((struct mg_connection *)thread_func_param);
 	return 0;
 }
 #else
 static void *
 worker_thread(void *thread_func_param)
 {
-	struct worker_thread_args *pwta =
-	    (struct worker_thread_args *)thread_func_param;
 	struct sigaction sa;
 
 	/* Ignore SIGPIPE */
@@ -17970,8 +17998,7 @@ worker_thread(void *thread_func_param)
 	sa.sa_handler = SIG_IGN;
 	sigaction(SIGPIPE, &sa, NULL);
 
-	worker_thread_run(pwta);
-	mg_free(thread_func_param);
+	worker_thread_run((struct mg_connection *)thread_func_param);
 	return NULL;
 }
 #endif /* _WIN32 */
@@ -18063,9 +18090,8 @@ accept_new_connection(const struct socket *listener, struct mg_context *ctx)
 
 
 static void
-master_thread_run(void *thread_func_param)
+master_thread_run(struct mg_context *ctx)
 {
-	struct mg_context *ctx = (struct mg_context *)thread_func_param;
 	struct mg_workerTLS tls;
 	struct mg_pollfd *pfd;
 	unsigned int i;
@@ -18187,7 +18213,7 @@ master_thread_run(void *thread_func_param)
 #if defined(_WIN32)
 static unsigned __stdcall master_thread(void *thread_func_param)
 {
-	master_thread_run(thread_func_param);
+	master_thread_run((struct mg_context *)thread_func_param);
 	return 0;
 }
 #else
@@ -18201,7 +18227,7 @@ master_thread(void *thread_func_param)
 	sa.sa_handler = SIG_IGN;
 	sigaction(SIGPIPE, &sa, NULL);
 
-	master_thread_run(thread_func_param);
+	master_thread_run((struct mg_context *)thread_func_param);
 	return NULL;
 }
 #endif /* _WIN32 */
@@ -18695,24 +18721,13 @@ mg_start(const struct mg_callbacks *callbacks,
 
 	/* Start worker threads */
 	for (i = 0; i < ctx->cfg_worker_threads; i++) {
-		struct worker_thread_args *wta = (struct worker_thread_args *)
-		    mg_malloc_ctx(sizeof(struct worker_thread_args), ctx);
-		if (wta) {
-			wta->ctx = ctx;
-			wta->index = (int)i;
-		}
-
-		if ((wta == NULL)
-		    || (mg_start_thread_with_id(worker_thread,
-		                                wta,
-		                                &ctx->worker_threadids[i])
-		        != 0)) {
+		/* worker_thread sets up the other fields */
+		ctx->worker_connections[i].phys_ctx = ctx;
+		if (mg_start_thread_with_id(worker_thread,
+		                            &ctx->worker_connections[i],
+		                            &ctx->worker_threadids[i]) != 0) {
 
 			/* thread was not created */
-			if (wta != NULL) {
-				mg_free(wta);
-			}
-
 			if (i > 0) {
 				mg_cry_internal(fc(ctx),
 				                "Cannot start worker thread %i: error %ld",

+ 13 - 0
src/handle_form.inl

@@ -873,6 +873,14 @@ mg_handle_form_request(struct mg_connection *conn,
 				/* Set "towrite" to the number of bytes available
 				 * in the buffer */
 				towrite = (size_t)(buf - hend + buf_fill);
+
+				if (towrite < bl + 4) {
+					/* Not enough data stored. */
+					/* Incomplete request. */
+					mg_free(boundary);
+					return -1;
+				}
+
 				/* Subtract the boundary length, to deal with
 				 * cases the boundary is only partially stored
 				 * in the buffer. */
@@ -941,6 +949,11 @@ mg_handle_form_request(struct mg_connection *conn,
 
 				/* Find boundary */
 				next = search_boundary(buf, (size_t)buf_fill, boundary, bl);
+
+				if (!next && (r == 0)) {
+					/* incomplete request */
+					all_data_read = 1;
+				}
 			}
 
 			towrite = (size_t)(next - hend);

+ 12 - 11
src/main.c

@@ -732,34 +732,35 @@ read_config_file(const char *config_file, char **options)
 		line_no++;
 
 		/* Ignore empty lines and comments */
-		for (i = 0; isspace(*(unsigned char *)&line[i]);)
+		for (i = 0; isspace((unsigned char)p[i]);)
 			i++;
 		if (p[i] == '#' || p[i] == '\0') {
 			continue;
 		}
 
 		/* Skip spaces, \r and \n at the end of the line */
-		for (j = strlen(line) - 1; isspace(*(unsigned char *)&line[j])
-		                           || iscntrl(*(unsigned char *)&line[j]);)
-			line[j--] = 0;
+		for (j = strlen(p); (j > 0)
+		                    && (isspace((unsigned char)p[j - 1])
+		                        || iscntrl((unsigned char)p[j - 1]));)
+			p[--j] = 0;
 
 		/* Find the space character between option name and value */
-		for (j = i; !isspace(*(unsigned char *)&line[j]) && (line[j] != 0);)
+		for (j = i; !isspace((unsigned char)p[j]) && (p[j] != 0);)
 			j++;
 
-		/* Terminate the string - then the string at (line+i) contains the
+		/* Terminate the string - then the string at (p+i) contains the
 		 * option name */
-		line[j] = 0;
+		p[j] = 0;
 		j++;
 
 		/* Trim additional spaces between option name and value - then
-		 * (line+j) contains the option value */
-		while (isspace(line[j])) {
+		 * (p+j) contains the option value */
+		while (isspace((unsigned char)p[j])) {
 			j++;
 		}
 
 		/* Set option */
-		if (!set_option(options, line + i, line + j)) {
+		if (!set_option(options, p + i, p + j)) {
 			fprintf(stderr,
 			        "%s: line %d is invalid, ignoring it:\n %s",
 			        config_file,
@@ -922,7 +923,7 @@ is_path_absolute(const char *path)
 	return path != NULL
 	       && ((path[0] == '\\' && path[1] == '\\') || /* UNC path, e.g.
 	                                                      \\server\dir */
-	           (isalpha(path[0]) && path[1] == ':'
+	           (isalpha((unsigned char)path[0]) && path[1] == ':'
 	            && path[2] == '\\')); /* E.g. X:\dir */
 #else
 	return path != NULL && path[0] == '/';

+ 4 - 4
src/mod_duktape.inl

@@ -13,10 +13,10 @@
 
 /* Note: This is only experimental support, so the API may still change. */
 
-static const char *civetweb_conn_id = "\xFF"
-                                      "civetweb_conn";
-static const char *civetweb_ctx_id = "\xFF"
-                                     "civetweb_ctx";
+static const char *const civetweb_conn_id = "\xFF"
+                                            "civetweb_conn";
+static const char *const civetweb_ctx_id = "\xFF"
+                                           "civetweb_ctx";
 
 
 static void *

+ 36 - 19
src/mod_lua.inl

@@ -2,6 +2,9 @@
  * See https://github.com/civetweb/civetweb/
  */
 
+#if !defined(_WIN32)
+#include <dlfcn.h>
+#endif
 #include "civetweb_lua.h"
 #include "civetweb_private_lua.h"
 
@@ -45,11 +48,11 @@ munmap(void *addr, int64_t length)
 #include <sys/mman.h>
 #endif
 
-static const char *LUASOCKET = "luasocket";
+static const char *const LUASOCKET = "luasocket";
 static const char lua_regkey_ctx = 1;
 static const char lua_regkey_connlist = 2;
 static const char lua_regkey_lsp_include_history = 3;
-static const char *LUABACKGROUNDPARAMS = "mg";
+static const char *const LUABACKGROUNDPARAMS = "mg";
 
 /* Limit nesting depth of mg.include.
  * This takes a lot of stack (~10 kB per recursion),
@@ -562,7 +565,8 @@ run_lsp_kepler(struct mg_connection *conn,
                const char *path,
                const char *p,
                int64_t len,
-               lua_State *L)
+               lua_State *L,
+               int depth)
 {
 
 	int lua_ok;
@@ -572,15 +576,23 @@ run_lsp_kepler(struct mg_connection *conn,
 
 	gmt_time_string(date, sizeof(date), &curtime);
 
-	conn->must_close = 1;
-	mg_printf(conn, "HTTP/1.1 200 OK\r\n");
-	send_no_cache_header(conn);
-	send_additional_header(conn);
-	mg_printf(conn,
-	          "Date: %s\r\n"
-	          "Connection: close\r\n"
-	          "Content-Type: text/html; charset=utf-8\r\n\r\n",
-	          date);
+	if (depth == 1) {
+		/* Top level page assumes keep_alive is disabled.
+		 * Do not overwrite this setting for included pages. */
+		conn->must_close = 1;
+
+		/* Only send a HTML header, if this is the top level page.
+		 * If this page is included by some mg.include calls, do not add a
+		 * header. */
+		mg_printf(conn, "HTTP/1.1 200 OK\r\n");
+		send_no_cache_header(conn);
+		send_additional_header(conn);
+		mg_printf(conn,
+		          "Date: %s\r\n"
+		          "Connection: close\r\n"
+		          "Content-Type: text/html; charset=utf-8\r\n\r\n",
+		          date);
+	}
 
 	data.begin = p;
 	data.len = len;
@@ -608,7 +620,8 @@ run_lsp_civetweb(struct mg_connection *conn,
                  const char *path,
                  const char *p,
                  int64_t len,
-                 lua_State *L)
+                 lua_State *L,
+                 int depth)
 {
 	int i, j, s, pos = 0, lines = 1, lualines = 0, is_var, lua_ok;
 	char chunkname[MG_BUF_LEN];
@@ -616,6 +629,12 @@ run_lsp_civetweb(struct mg_connection *conn,
 	const char lsp_mark1 = '?'; /* Use <? code ?> */
 	const char lsp_mark2 = '%'; /* Use <% code %> */
 
+	if (depth == 1) {
+		/* Assume the script does not support keep_alive. The script may change
+		 * this by calling mg.keep_alive(true). */
+		conn->must_close = 1;
+	}
+
 	for (i = 0; i < len; i++) {
 		if (p[i] == '\n') {
 			lines++;
@@ -2362,13 +2381,10 @@ handle_lsp_request(struct mg_connection *conn,
 	               const char *,
 	               const char *,
 	               int64_t,
-	               lua_State *);
+	               lua_State *,
+	               int);
 	const char *addr;
 
-	/* Assume the script does not support keep_alive. The script may change this
-	 * by calling mg.keep_alive(true). */
-	conn->must_close = 1;
-
 	/* mg_fopen opens the file and sets the size accordingly */
 	if (!mg_fopen(conn, path, MG_FOPEN_MODE_READ, filep)) {
 
@@ -2507,7 +2523,8 @@ handle_lsp_request(struct mg_connection *conn,
 	}
 
 	/* We're not sending HTTP headers here, Lua page must do it. */
-	error = run_lsp(conn, path, addr, filep->stat.size, L);
+	error =
+	    run_lsp(conn, path, addr, filep->stat.size, L, include_history->depth);
 
 cleanup_handle_lsp_request:
 

+ 4 - 17
unittest/public_server.c

@@ -183,23 +183,8 @@ START_TEST(test_the_test_environment)
 	if (f) {
 		fclose(f);
 	} else {
-		fprintf(stderr, "%s not found", buf);
-	}
-
-/* Check the test dir */
-#ifdef _WIN32
-	strcpy(buf, wd);
-	strcat(buf, "\\test");
-#else
-	strcpy(buf, wd);
-	strcat(buf, "/test");
-#endif
-
-	memset(&st, 0, sizeof(st));
-	ret = stat(buf, &st);
-
-	if (ret) {
-		fprintf(stderr, "%s not found", buf);
+		fprintf(stderr, "Certificate %s not found\n", buf);
+		exit(1); /* some path is not correct --> test will not work */
 	}
 
 
@@ -5086,6 +5071,8 @@ MAIN_PUBLIC_SERVER(void)
 	unsigned f_ret = mg_init_library(f_avail);
 	ck_assert_uint_eq(f_ret, f_avail);
 
+	test_handle_form(0);
+
 	test_the_test_environment(0);
 	test_threading(0);