Browse Source

Merge branch 'master' into wip-resetconlen-in-cte

bel2125 7 years ago
parent
commit
59e22af08c
9 changed files with 353 additions and 145 deletions
  1. 10 1
      .travis.yml
  2. 22 8
      CMakeLists.txt
  3. 2 2
      README.md
  4. 115 41
      appveyor.yml
  5. 3 1
      docs/README.md
  6. 146 61
      src/civetweb.c
  7. 1 1
      test/CMakeLists.txt
  8. 1 1
      test/main.c
  9. 53 29
      test/public_server.c

+ 10 - 1
.travis.yml

@@ -1,6 +1,8 @@
 language: c
 
-sudo: false
+#sudo: false
+sudo: required
+dist: precise
 
 cache:
   directories:
@@ -16,8 +18,11 @@ addons:
       - cmake
       - openssl
       - libssl-dev
+#      - gcc-5
+#      - clang-3.8
     sources:
       - kubuntu-backports
+      - ubuntu-toolchain-r-test
 
 before_install:
   - if [ "${TRAVIS_OS_NAME}" == "linux" ]; then
@@ -41,6 +46,10 @@ before_script:
   # Check some settings of the build server
   - uname -a
   - pwd
+  - if [ "${TRAVIS_OS_NAME}" == "linux" ]; then
+      apt-cache search gcc | grep "GNU C compiler";
+      apt-cache search clang | grep compiler;
+    fi
   - if [[ "${BUILD_TYPE}" == "OSX_OPENSSL_1_1" ]]; then brew install openssl@1.1 ;fi
   # Generate the build scripts with CMake
   - mkdir output

+ 22 - 8
CMakeLists.txt

@@ -105,10 +105,16 @@ message(STATUS "Executable installation - ${CIVETWEB_INSTALL_EXECUTABLE}")
 
 # Allow builds to complete with warnings (do not set -Werror)
 if (LINUX)
-# CivetWeb Linux support is stable: Builds must be free from warnings.
-  option(CIVETWEB_ALLOW_WARNINGS "Do not stop build if there are warnings" OFF)
+# CivetWeb Linux support is stable:
+# Builds for GCC 4.6 and clang 3.4 are free from warnings.
+# However, GCC introduced a couple of new, partially idiotic warnings,
+# that can not be disabled using a #pragma directive.
+# It seems unreasonable to have all GCC versions warning free, but only
+# some selected ones.
+# For the moment, allow warnings.
+  option(CIVETWEB_ALLOW_WARNINGS "Do not stop build if there are warnings" ON)
 else()
-# CivetWeb Linux support for other systems is in a setup phase.
+# CivetWeb support for OSX works, but is not free of warnings.
   option(CIVETWEB_ALLOW_WARNINGS "Do not stop build if there are warnings" ON)
 endif()
 message(STATUS "Build if there are warnings - ${CIVETWEB_ALLOW_WARNINGS}")
@@ -269,18 +275,26 @@ if ("${CIVETWEB_C_STANDARD}" STREQUAL "auto")
 else()
   add_c_compiler_flag(-std=${CIVETWEB_C_STANDARD})
 endif()
+
+#Warnings: enable everything
 add_c_compiler_flag(-Wall)
 add_c_compiler_flag(-Wextra)
 add_c_compiler_flag(-Wshadow)
 add_c_compiler_flag(-Wconversion)
 add_c_compiler_flag(-Wmissing-prototypes)
 add_c_compiler_flag(-Weverything)
-add_c_compiler_flag(/W4)
-add_c_compiler_flag(-Wno-padded)
-add_c_compiler_flag(/Wd4820) # padding
-add_c_compiler_flag(-Wno-unused-macros)
-add_c_compiler_flag(-Wno-format-nonliteral)
 add_c_compiler_flag(-Wparentheses)
+add_c_compiler_flag(/W4) # VisualStudio highest warning level
+
+#Warnings: Disable some warnings
+add_c_compiler_flag(-Wno-padded) # padding in structures by compiler
+add_c_compiler_flag(-Wno-unused-macros) # so what?
+add_c_compiler_flag(-Wno-reserved-id-macros) # for system headers
+add_c_compiler_flag(-Wno-format-nonliteral) # printf(myFormatStringVar, ...)
+add_c_compiler_flag(-Wno-date-time) # using __DATE__ once
+add_c_compiler_flag(-Wno-cast-qual) # const cast
+add_c_compiler_flag(/Wd4820) # padding
+
 if (MINGW)
   add_c_compiler_flag(-Wno-format)
 endif()

+ 2 - 2
README.md

@@ -52,8 +52,8 @@ Developers can contribute to CivetWeb via GitHub
 Trouble tickets should be filed on GitHub
 [https://github.com/civetweb/civetweb/issues](https://github.com/civetweb/civetweb/issues)
 
-Discussion/support group and announcements are at Google Groups
-[https://groups.google.com/d/forum/civetweb](https://groups.google.com/d/forum/civetweb)
+Announcements are at Google Groups
+[https://groups.google.com/d/forum/civetweb](https://groups.google.com/d/forum/civetweb). Some older support and discussion threads are there as well. However, recently support questions and discussions are usually [GitHub issues](https://github.com/civetweb/civetweb/issues).
 
 Source releases can be found on GitHub
 [https://github.com/civetweb/civetweb/releases](https://github.com/civetweb/civetweb/releases)

+ 115 - 41
appveyor.yml

@@ -5,9 +5,13 @@ build:
 # no automatic build in script mode
 
 
-platform:
-  - x86
-  - x64
+skip_commits:
+  # Builds just testing something on Travis CI don't need to be 
+  # done on AppVeyor
+  message: /\[Travis\]/
+  # Dont build, if only documentation was changed
+  files:
+  - '**/*.md'
 
 
 environment:
@@ -18,47 +22,100 @@ environment:
   c_standard: auto
   cxx_standard: auto
   matrix:
-    - id: 1
-      compiler: msvc-18-seh
+    # Use default values
+    - id: Default-x86
+      compiler: msvc-19-seh
       build_shared: NO
       no_files: NO
       enable_ipv6: NO
       enable_ssl: YES
-      enable_websockets: YES
+      enable_websockets: NO
       no_cgi: NO
       no_caching: NO
       configuration: Release
-    - id: 2
-      compiler: msvc-18-seh
-      build_shared: YES
+      platform: x86
+    - id: Default-x64
+      compiler: msvc-19-seh
+      build_shared: NO
       no_files: NO
       enable_ipv6: NO
       enable_ssl: YES
+      enable_websockets: NO
+      no_cgi: NO
+      no_caching: NO
+      configuration: Release
+      platform: x64
+    # Use default values
+    - id: Full-x86
+      compiler: msvc-19-seh
+      build_shared: NO
+      no_files: NO
+      enable_ipv6: YES
+      enable_ssl: YES
       enable_websockets: YES
       no_cgi: NO
       no_caching: NO
       configuration: Release
-    - id: 3
-      compiler: msvc-18-seh
-      build_shared: YES
-      no_files: YES
-      enable_ipv6: NO
+      platform: x86
+    - id: Full-x64
+      compiler: msvc-19-seh
+      build_shared: NO
+      no_files: NO
+      enable_ipv6: YES
       enable_ssl: YES
       enable_websockets: YES
       no_cgi: NO
       no_caching: NO
       configuration: Release
-    - id: 4
-      compiler: gcc-5.1.0-posix
+      platform: x64
+    # Minimum settings
+    - id: Minimal-x86
+      compiler: msvc-19-seh
+      build_shared: NO
+      no_files: YES
+      enable_ipv6: NO
+      enable_ssl: NO
+      enable_websockets: NO
+      no_cgi: YES
+      no_caching: YeS
+      configuration: Release
+      platform: x86
+    - id: Minimal-x64
+      compiler: msvc-19-seh
       build_shared: NO
       no_files: YES
       enable_ipv6: NO
       enable_ssl: NO
       enable_websockets: NO
       no_cgi: YES
-      no_caching: YES
+      no_caching: YeS
       configuration: Release
-    - id: 5
+      platform: x64
+    # Test shared and debug build
+    - id: Shared-default-debug-x86
+      compiler: msvc-19-seh
+      build_shared: YES
+      no_files: NO
+      enable_ipv6: NO
+      enable_ssl: YES
+      enable_websockets: NO
+      no_cgi: NO
+      no_caching: NO
+      configuration: Debug
+      platform: x86
+    - id: Shared-default-debug-x64
+      compiler: msvc-19-seh
+      build_shared: YES
+      no_files: NO
+      enable_ipv6: NO
+      enable_ssl: YES
+      enable_websockets: NO
+      no_cgi: NO
+      no_caching: NO
+      configuration: Debug
+      platform: x64
+    # MinGW
+    - id: Full-GCC-x86
       compiler: gcc-5.1.0-posix
       build_shared: NO
       no_files: NO
@@ -66,39 +123,46 @@ environment:
       enable_ssl: YES
       enable_websockets: YES
       no_cgi: NO
-      no_caching: YES
+      no_caching: NO
       configuration: Release
-    - id: 6
+      platform: x86
+    - id: Full-GCC-x64
       compiler: gcc-5.1.0-posix
       build_shared: NO
       no_files: NO
-      enable_ipv6: NO
+      enable_ipv6: YES
       enable_ssl: YES
       enable_websockets: YES
       no_cgi: NO
-      no_caching: YES
+      no_caching: NO
       configuration: Release
-    - id: 7
-      compiler: gcc-5.1.0-posix
-      build_shared: YES
+      platform: x64
+    # Visual Studio 2010
+    - id: Full-VS2010-x86
+      compiler: msvc-16-seh
+      build_shared: NO
       no_files: NO
-      enable_ipv6: NO
+      enable_ipv6: YES
       enable_ssl: YES
       enable_websockets: YES
       no_cgi: NO
-      no_caching: YES
+      no_caching: NO
       configuration: Release
-    - id: 8
-      compiler: gcc-5.1.0-posix
-      build_shared: YES
-      no_files: YES
-      enable_ipv6: NO
+      platform: x86
+    # Visual Studio 2012
+    - id: Full-VS2012-x86
+      compiler: msvc-17-seh
+      build_shared: NO
+      no_files: NO
+      enable_ipv6: YES
       enable_ssl: YES
       enable_websockets: YES
       no_cgi: NO
-      no_caching: YES
+      no_caching: NO
       configuration: Release
-    - id: 9
+      platform: x86
+    # Visual Studio 2013
+    - id: Full-VS2013-x86
       compiler: msvc-18-seh
       build_shared: NO
       no_files: NO
@@ -107,9 +171,10 @@ environment:
       enable_websockets: YES
       no_cgi: NO
       no_caching: NO
-      configuration: Debug
-    - id: 10
-      compiler: msvc-16-seh
+      configuration: Release
+      platform: x86
+    - id: Full-VS2013-x64
+      compiler: msvc-18-seh
       build_shared: NO
       no_files: NO
       enable_ipv6: YES
@@ -118,8 +183,11 @@ environment:
       no_cgi: NO
       no_caching: NO
       configuration: Release
-    - id: 11
-      compiler: msvc-17-seh
+      platform: x64
+    # Visual Studio 2015 is default
+    # Visual Studio 2017 is not yet default
+    - id: Full-VS2017-x86
+      compiler: msvc-20-seh
       build_shared: NO
       no_files: NO
       enable_ipv6: YES
@@ -128,8 +196,10 @@ environment:
       no_cgi: NO
       no_caching: NO
       configuration: Release
-    - id: 12
-      compiler: msvc-19-seh
+      platform: x86
+      APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2017
+    - id: Full-VS2017-x64
+      compiler: msvc-20-seh
       build_shared: NO
       no_files: NO
       enable_ipv6: YES
@@ -138,6 +208,9 @@ environment:
       no_cgi: NO
       no_caching: NO
       configuration: Release
+      platform: x64
+      APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2017
+
 
 install:
   # Derive some extra information
@@ -196,6 +269,7 @@ before_build:
   - if "%compiler_version%"=="17" (set "vs_version=11" & set "vs_year=2012")
   - if "%compiler_version%"=="18" (set "vs_version=12" & set "vs_year=2013")
   - if "%compiler_version%"=="19" (set "vs_version=14" & set "vs_year=2015")
+  - if "%compiler_version%"=="20" (set "vs_version=15" & set "vs_year=2017")
   - if "%compiler_name%"=="msvc" (set "generator=Visual Studio %vs_version% %vs_year%")
   - if "%compiler_name%"=="msvc" (
       if "%platform%"=="x64" (

+ 3 - 1
docs/README.md

@@ -20,9 +20,11 @@ Developers can contribute to CivetWeb via GitHub
 Trouble tickets should be filed on GitHub
 [https://github.com/civetweb/civetweb/issues](https://github.com/civetweb/civetweb/issues)
 
-Discussion/support group and announcements are at Google Groups
+Announcements are at Google Groups
 [https://groups.google.com/d/forum/civetweb](https://groups.google.com/d/forum/civetweb)
 
+While older support question and discussion threads have been at [Google groups](https://groups.google.com/d/forum/civetweb), most newer ones are [GitHub issues](https://github.com/civetweb/civetweb/issues).
+
 Source releases can be found on GitHub
 [https://github.com/civetweb/civetweb/releases](https://github.com/civetweb/civetweb/releases)
 

+ 146 - 61
src/civetweb.c

@@ -180,6 +180,20 @@ mg_static_assert(sizeof(void *) >= sizeof(int), "data type size check");
 #pragma clang diagnostic ignored "-Wdisabled-macro-expansion"
 #endif
 
+#if defined(__GNUC__) || defined(__MINGW32__)
+/* Who on earth came to the conclusion, using __DATE__ should rise
+ * an "expansion of date or time macro is not reproducible"
+ * warning. That's exactly what was intended by using this macro.
+ * Just disable this nonsense warning. */
+
+/* And disabling them does not work either:
+ * #pragma clang diagnostic ignored "-Wno-error=date-time"
+ * #pragma clang diagnostic ignored "-Wdate-time"
+ * So we just have to disable ALL warnings for some lines
+ * of code.
+ */
+#endif
+
 
 #ifdef __MACH__ /* Apple OSX section */
 
@@ -2368,8 +2382,12 @@ struct mg_connection {
 	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 */
+	int is_chunked;           /* Transfer-Encoding is chunked:
+	                           * 0 = not chunked,
+	                           * 1 = chunked, do data read yet,
+	                           * 2 = chunked, some data read,
+	                           * 3 = chunked, 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 */
@@ -5858,9 +5876,9 @@ discard_unread_request_data(struct mg_connection *conn)
 	to_read = sizeof(buf);
 
 	if (conn->is_chunked) {
-		/* Chunked encoding: 1=chunk not read completely, 2=chunk read
+		/* Chunked encoding: 3=chunk read completely
 		 * completely */
-		while (conn->is_chunked == 1) {
+		while (conn->is_chunked != 3) {
 			nread = mg_read(conn, buf, to_read);
 			if (nread <= 0) {
 				break;
@@ -5898,11 +5916,19 @@ mg_read_inner(struct mg_connection *conn, void *buf, size_t len)
 		return 0;
 	}
 
-	/* If Content-Length is not set for a PUT or POST request, read until
-	 * socket is closed */
-	if ((conn->consumed_content) == 0 && (conn->content_len == -1)) {
-		conn->content_len = INT64_MAX;
-		conn->must_close = 1;
+	/* If Content-Length is not set for a request with body data
+	 * (e.g., a PUT or POST request), we do not know in advance
+	 * how much data should be read. */
+	if (conn->consumed_content == 0) {
+		if (conn->is_chunked == 1) {
+			conn->content_len = len64;
+			conn->is_chunked = 2;
+		} else if (conn->content_len == -1) {
+			/* The body data is completed when the connection
+			 * is closed. */
+			conn->content_len = INT64_MAX;
+			conn->must_close = 1;
+		}
 	}
 
 	nread = 0;
@@ -5951,7 +5977,6 @@ mg_getc(struct mg_connection *conn)
 	if (conn == NULL) {
 		return 0;
 	}
-	conn->content_len++;
 	if (mg_read_inner(conn, &c, 1) <= 0) {
 		return (char)0;
 	}
@@ -5974,8 +5999,7 @@ mg_read(struct mg_connection *conn, void *buf, size_t len)
 		size_t all_read = 0;
 
 		while (len > 0) {
-
-			if (conn->is_chunked == 2) {
+			if (conn->is_chunked == 3) {
 				/* No more data left to read */
 				return 0;
 			}
@@ -6003,8 +6027,10 @@ mg_read(struct mg_connection *conn, void *buf, size_t len)
 				if (conn->chunk_remainder == 0) {
 					/* Add data bytes in the current chunk have been read,
 					 * so we are expecting \r\n now. */
-					char x1 = mg_getc(conn);
-					char x2 = mg_getc(conn);
+					char x1, x2;
+					conn->content_len += 2;
+					x1 = mg_getc(conn);
+					x2 = mg_getc(conn);
 					if ((x1 != '\r') || (x2 != '\n')) {
 						/* Protocol violation */
 						return -1;
@@ -6019,6 +6045,7 @@ mg_read(struct mg_connection *conn, void *buf, size_t len)
 				unsigned long chunkSize = 0;
 
 				for (i = 0; i < ((int)sizeof(lenbuf) - 1); i++) {
+					conn->content_len++;
 					lenbuf[i] = mg_getc(conn);
 					if ((i > 0) && (lenbuf[i] == '\r')
 					    && (lenbuf[i - 1] != '\r')) {
@@ -6030,7 +6057,7 @@ mg_read(struct mg_connection *conn, void *buf, size_t len)
 						chunkSize = strtoul(lenbuf, &end, 16);
 						if (chunkSize == 0) {
 							/* regular end of content */
-							conn->is_chunked = 2;
+							conn->is_chunked = 3;
 						}
 						break;
 					}
@@ -9176,47 +9203,96 @@ parse_http_headers(char **buf, struct mg_header hdr[MG_MAX_HEADERS])
 }
 
 
-static int
-is_valid_http_method(const char *method)
+struct mg_http_method_info {
+	const char *name;
+	int request_has_body;
+	int response_has_body;
+	int is_safe;
+	int is_idempotent;
+	int is_cacheable;
+};
+
+
+/* https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods */
+static struct mg_http_method_info http_methods[] = {
+    /* HTTP (RFC 2616) */
+    {"GET", 0, 1, 1, 1, 1},
+    {"POST", 1, 1, 0, 0, 0},
+    {"PUT", 1, 0, 0, 1, 0},
+    {"DELETE", 0, 0, 0, 1, 0},
+    {"HEAD", 0, 0, 1, 1, 1},
+    {"OPTIONS", 0, 0, 1, 1, 0},
+    {"CONNECT", 1, 1, 0, 0, 0},
+    /* TRACE method (RFC 2616) is not supported for security reasons */
+
+    /* PATCH method (RFC 5789) */
+    {"PATCH", 1, 0, 0, 0, 0},
+    /* PATCH method only allowed for CGI/Lua/LSP and callbacks. */
+
+    /* WEBDAV (RFC 2518) */
+    {"PROPFIND", 0, 1, 1, 1, 0},
+    /* http://www.webdav.org/specs/rfc4918.html, 9.1:
+     * Some PROPFIND results MAY be cached, with care,
+     * as there is no cache validation mechanism for
+     * most properties. This method is both safe and
+     * idempotent (see Section 9.1 of [RFC2616]). */
+    {"MKCOL", 0, 0, 0, 1, 0},
+    /* http://www.webdav.org/specs/rfc4918.html, 9.1:
+     * When MKCOL is invoked without a request body,
+     * the newly created collection SHOULD have no
+     * members. A MKCOL request message may contain
+     * a message body. The precise behavior of a MKCOL
+     * request when the body is present is undefined,
+     * ... ==> We do not support MKCOL with body data.
+     * This method is idempotent, but not safe (see
+     * Section 9.1 of [RFC2616]). Responses to this
+     * method MUST NOT be cached. */
+
+    /* Unsupported WEBDAV Methods: */
+    /* PROPPATCH, COPY, MOVE, LOCK, UNLOCK (RFC 2518) */
+    /* + 11 methods from RFC 3253 */
+    /* ORDERPATCH (RFC 3648) */
+    /* ACL (RFC 3744) */
+    /* SEARCH (RFC 5323) */
+    /* + MicroSoft extensions
+     * https://msdn.microsoft.com/en-us/library/aa142917.aspx */
+
+    /* REPORT method (RFC 3253) */
+    {"REPORT", 1, 1, 1, 1, 1},
+    /* REPORT method only allowed for CGI/Lua/LSP and callbacks. */
+    /* It was defined for WEBDAV in RFC 3253, Sec. 3.6
+     * (https://tools.ietf.org/html/rfc3253#section-3.6), but seems
+     * to be useful for REST in case a "GET request with body" is
+     * required. */
+
+    {NULL, 0, 0, 0, 0, 0}
+    /* end of list */
+};
+
+
+static const struct mg_http_method_info *
+get_http_method_info(const char *method)
 {
 	/* Check if the method is known to the server. The list of all known
 	 * HTTP methods can be found here at
 	 * http://www.iana.org/assignments/http-methods/http-methods.xhtml
 	 */
+	const struct mg_http_method_info *m = http_methods;
+
+	while (m->name) {
+		if (!strcmp(m->name, method)) {
+			return m;
+		}
+		m++;
+	}
+	return NULL;
+}
 
-	return !strcmp(method, "GET")        /* HTTP (RFC 2616) */
-	       || !strcmp(method, "POST")    /* HTTP (RFC 2616) */
-	       || !strcmp(method, "HEAD")    /* HTTP (RFC 2616) */
-	       || !strcmp(method, "PUT")     /* HTTP (RFC 2616) */
-	       || !strcmp(method, "DELETE")  /* HTTP (RFC 2616) */
-	       || !strcmp(method, "OPTIONS") /* HTTP (RFC 2616) */
-	       /* TRACE method (RFC 2616) is not supported for security reasons
-	          */
-	       || !strcmp(method, "CONNECT") /* HTTP (RFC 2616) */
-
-	       || !strcmp(method, "PROPFIND") /* WEBDAV (RFC 2518) */
-	       || !strcmp(method, "MKCOL")    /* WEBDAV (RFC 2518) */
-
-	       /* Unsupported WEBDAV Methods: */
-	       /* PROPPATCH, COPY, MOVE, LOCK, UNLOCK (RFC 2518) */
-	       /* + 11 methods from RFC 3253 */
-	       /* ORDERPATCH (RFC 3648) */
-	       /* ACL (RFC 3744) */
-	       /* SEARCH (RFC 5323) */
-	       /* + MicroSoft extensions
-	        * https://msdn.microsoft.com/en-us/library/aa142917.aspx */
-
-	       /* PATCH method only allowed for CGI/Lua/LSP and callbacks. */
-	       || !strcmp(method, "PATCH") /* PATCH method (RFC 5789) */
-
-	       /* REPORT method only allowed for CGI/Lua/LSP and callbacks. */
-	       /* It was defined for WEBDAV in RFC 3253, Sec. 3.6
-	        * (https://tools.ietf.org/html/rfc3253#section-3.6), but seems
-	        * to be useful for REST in case a "GET request with body" is
-	        * required. */
-	       || !strcmp(method, "REPORT") /* REPORT method (RFC 3253) */
 
-	    ;
+static int
+is_valid_http_method(const char *method)
+{
+	return (get_http_method_info(method) != NULL);
 }
 
 
@@ -9549,7 +9625,7 @@ forward_body_data(struct mg_connection *conn, FILE *fp, SOCKET sock, SSL *ssl)
 		return 0;
 	}
 
-	if ((conn->content_len == -1) && !conn->is_chunked) {
+	if ((conn->content_len == -1) && (!conn->is_chunked)) {
 		/* Content length is not specified by the client. */
 		mg_send_http_error(conn,
 		                   411,
@@ -10059,7 +10135,7 @@ handle_cgi_request(struct mg_connection *conn, const char *prog)
 	setbuf(err, NULL);
 	fout.access.fp = out;
 
-	if ((conn->request_info.content_length > 0) || conn->is_chunked) {
+	if ((conn->request_info.content_length != 0) || (conn->is_chunked)) {
 		/* This is a POST/PUT request, or another request with body data. */
 		if (!forward_body_data(conn, in, INVALID_SOCKET, NULL)) {
 			/* Error sending the body data */
@@ -14343,6 +14419,7 @@ reset_per_request_attributes(struct mg_connection *conn)
 
 	conn->path_info = NULL;
 	conn->status_code = -1;
+	conn->content_len = -1;
 	conn->is_chunked = 0;
 	conn->must_close = 0;
 	conn->request_len = 0;
@@ -15200,17 +15277,14 @@ get_request(struct mg_connection *conn, char *ebuf, size_t ebuf_len, int *err)
 	                            "Transfer-Encoding")) != NULL
 	           && !mg_strcasecmp(cl, "chunked")) {
 		conn->is_chunked = 1;
-		conn->content_len = 0;
-	} else if (!mg_strcasecmp(conn->request_info.request_method, "POST")
-	           || !mg_strcasecmp(conn->request_info.request_method, "PUT")) {
+		conn->content_len = -1; /* unknown content length */
+	} else if (get_http_method_info(conn->request_info.request_method)
+	               ->request_has_body) {
 		/* POST or PUT request without content length set */
-		conn->content_len = -1;
-	} else if (!mg_strncasecmp(conn->request_info.request_method, "HTTP/", 5)) {
-		/* Response without content length set */
-		conn->content_len = -1;
+		conn->content_len = -1; /* unknown content length */
 	} else {
 		/* Other request */
-		conn->content_len = 0;
+		conn->content_len = 0; /* No content */
 	}
 
 	conn->connection_type = 1; /* Valid request */
@@ -15267,11 +15341,11 @@ get_response(struct mg_connection *conn, char *ebuf, size_t ebuf_len, int *err)
 	                            "Transfer-Encoding")) != NULL
 	           && !mg_strcasecmp(cl, "chunked")) {
 		conn->is_chunked = 1;
+		conn->content_len = -1; /* unknown content length */
 	} else {
-		conn->content_len = -1;
+		conn->content_len = -1; /* unknown content length */
 	}
 
-
 	conn->connection_type = 2; /* Valid response */
 	return 1;
 }
@@ -17042,6 +17116,12 @@ mg_get_system_info_impl(char *buffer, int buflen)
 
 	/* Build date */
 	{
+#if defined(__GNUC__)
+#pragma GCC diagnostic push
+/* Disable bogus compiler warning -Wdate-time */
+#pragma GCC diagnostic ignored "-Wall"
+#pragma GCC diagnostic ignored "-Werror"
+#endif
 		mg_snprintf(NULL,
 		            NULL,
 		            block,
@@ -17049,6 +17129,11 @@ mg_get_system_info_impl(char *buffer, int buflen)
 		            "\"build\" : \"%s\",%s",
 		            __DATE__,
 		            eol);
+
+#if defined(__GNUC__)
+#pragma GCC diagnostic pop
+#endif
+
 		system_info_length += (int)strlen(block);
 		if (system_info_length < buflen) {
 			strcat0(buffer, block);

+ 1 - 1
test/CMakeLists.txt

@@ -27,7 +27,7 @@ ExternalProject_Add(check-unit-test-framework
 ## Use a civetweb specific patched version
 URL "https://github.com/civetweb/check/archive/master.zip"
 DOWNLOAD_NAME "master.zip"
-## <Edit this file to flush AppVeyor build cache and force reloading check>
+# <Edit this file to flush AppVeyor build cache and force reloading check>
 
   PREFIX "${CIVETWEB_THIRD_PARTY_DIR}"
   BUILD_IN_SOURCE 1

+ 1 - 1
test/main.c

@@ -49,7 +49,7 @@ main(const int argc, char *argv[])
 	const char *const test_dir_arg = "--test-dir=";
 	const size_t test_dir_arg_size = strlen(test_dir_arg);
 
-	SRunner *const srunner;
+	SRunner *srunner;
 	int number_run = 0;
 	int number_failed = 0;
 

+ 53 - 29
test/public_server.c

@@ -133,19 +133,13 @@ wait_not_null(void *volatile *data)
 #if defined(__MINGW32__) || defined(__GNUC__)
 #pragma GCC diagnostic push
 #pragma GCC diagnostic ignored "-Wunreachable-code"
-#endif
-#ifdef __clang__
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wunreachable-code"
+#pragma GCC diagnostic ignored "-Wunreachable-code-return"
 #endif
 
 	ck_abort_msg("wait_not_null failed (%i sec)", i);
 
 	return 0;
 
-#ifdef __clang__
-#pragma clang diagnostic pop
-#endif
 #if defined(__MINGW32__) || defined(__GNUC__)
 #pragma GCC diagnostic pop
 #endif
@@ -327,6 +321,13 @@ test_mg_start(const struct mg_callbacks *callbacks,
 static void
 test_mg_stop(struct mg_context *ctx)
 {
+#ifdef __MACH__
+	/* For unknown reasons, there are sporadic hands
+	 * for OSX if mark_point is called here */
+	test_sleep(SLEEP_BEFORE_MG_STOP);
+	mg_stop(ctx);
+	test_sleep(SLEEP_AFTER_MG_STOP);
+#else
 	mark_point();
 	test_sleep(SLEEP_BEFORE_MG_STOP);
 	mark_point();
@@ -334,6 +335,7 @@ test_mg_stop(struct mg_context *ctx)
 	mark_point();
 	test_sleep(SLEEP_AFTER_MG_STOP);
 	mark_point();
+#endif
 }
 
 
@@ -830,6 +832,7 @@ request_test_handler(struct mg_connection *conn, void *cbdata)
 	const struct mg_request_info *ri;
 	struct mg_context *ctx;
 	void *ud, *cud;
+	void *dummy = malloc(1);
 
 	ctx = mg_get_context(conn);
 	ud = mg_get_user_data(ctx);
@@ -839,11 +842,19 @@ request_test_handler(struct mg_connection *conn, void *cbdata)
 	ck_assert(ctx == g_ctx);
 	ck_assert(ud == &g_ctx);
 
-	mg_set_user_connection_data(conn, (void *)6543);
+	ck_assert(dummy != NULL);
+
+	mg_set_user_connection_data(conn, (void *)&dummy);
 	cud = mg_get_user_connection_data(conn);
-	ck_assert_ptr_eq((void *)cud, (void *)6543);
+	ck_assert_ptr_eq((void *)cud, (void *)&dummy);
 
-	ck_assert_ptr_eq((void *)cbdata, (void *)7);
+	mg_set_user_connection_data(conn, (void *)NULL);
+	cud = mg_get_user_connection_data(conn);
+	ck_assert_ptr_eq((void *)cud, (void *)NULL);
+
+	free(dummy);
+
+	ck_assert_ptr_eq((void *)cbdata, (void *)(ptrdiff_t)7);
 	strcpy(chunk_data, "123456789A123456789B123456789C");
 
 	mg_printf(conn,
@@ -863,6 +874,7 @@ request_test_handler(struct mg_connection *conn, void *cbdata)
 	return 1;
 }
 
+
 #ifdef USE_WEBSOCKET
 /****************************************************************************/
 /* WEBSOCKET SERVER                                                         */
@@ -894,7 +906,7 @@ websock_server_connect(const struct mg_connection *conn, void *udata)
 {
 	(void)conn;
 
-	ck_assert_ptr_eq((void *)udata, (void *)7531);
+	ck_assert_ptr_eq((void *)udata, (void *)(ptrdiff_t)7531);
 	WS_TEST_TRACE("Server: Websocket connected\n");
 	mark_point();
 
@@ -905,7 +917,7 @@ websock_server_connect(const struct mg_connection *conn, void *udata)
 static void
 websock_server_ready(struct mg_connection *conn, void *udata)
 {
-	ck_assert_ptr_eq((void *)udata, (void *)7531);
+	ck_assert_ptr_eq((void *)udata, (void *)(ptrdiff_t)7531);
 	ck_assert_ptr_ne((void *)conn, (void *)NULL);
 	WS_TEST_TRACE("Server: Websocket ready\n");
 
@@ -936,7 +948,7 @@ websock_server_data(struct mg_connection *conn,
 {
 	(void)bits;
 
-	ck_assert_ptr_eq((void *)udata, (void *)7531);
+	ck_assert_ptr_eq((void *)udata, (void *)(ptrdiff_t)7531);
 	WS_TEST_TRACE("Server: Got %u bytes from the client\n", (unsigned)data_len);
 
 	if (data_len == 3 && !memcmp(data, "bye", 3)) {
@@ -1007,16 +1019,18 @@ websock_server_data(struct mg_connection *conn,
 static void
 websock_server_close(const struct mg_connection *conn, void *udata)
 {
-	(void)conn;
-	(void)udata;
-
-	ck_assert_ptr_eq((void *)udata, (void *)7531);
+#ifndef __MACH__
+	ck_assert_ptr_eq((void *)udata, (void *)(ptrdiff_t)7531);
 	WS_TEST_TRACE("Server: Close connection\n");
 
 	/* Can not send a websocket goodbye message here -
 	 * the connection is already closed */
 
 	mark_point();
+#endif
+
+	(void)conn;
+	(void)udata;
 }
 
 
@@ -1082,17 +1096,24 @@ websocket_client_close_handler(const struct mg_connection *conn,
 	struct tclient_data *pclient_data =
 	    (struct tclient_data *)mg_get_user_data(ctx);
 
+#ifndef __MACH__
 	ck_assert_ptr_eq(user_data, (void *)pclient_data);
-	(void)user_data;
 
 	ck_assert(pclient_data != NULL);
 
 	WS_TEST_TRACE("Client %i: Close handler\n", pclient_data->clientId);
-	pclient_data->closed++;
 
 	mark_point();
+#else
+
+	(void)user_data;
+
+	pclient_data->closed++;
+
+#endif /* __MACH__ */
 }
-#endif
+
+#endif /* USE_WEBSOCKET */
 
 
 START_TEST(test_request_handlers)
@@ -1227,15 +1248,15 @@ START_TEST(test_request_handlers)
 	}
 	for (i = 500; i < 800; i++) {
 		sprintf(uri, "/U%u", i);
-		mg_set_request_handler(ctx, uri, NULL, (void *)1);
+		mg_set_request_handler(ctx, uri, NULL, (void *)(ptrdiff_t)1);
 	}
 	for (i = 600; i >= 0; i--) {
 		sprintf(uri, "/U%u", i);
-		mg_set_request_handler(ctx, uri, NULL, (void *)2);
+		mg_set_request_handler(ctx, uri, NULL, (void *)(ptrdiff_t)2);
 	}
 	for (i = 750; i <= 1000; i++) {
 		sprintf(uri, "/U%u", i);
-		mg_set_request_handler(ctx, uri, NULL, (void *)3);
+		mg_set_request_handler(ctx, uri, NULL, (void *)(ptrdiff_t)3);
 	}
 	for (i = 5; i < 9; i++) {
 		sprintf(uri, "/U%u", i);
@@ -1252,7 +1273,7 @@ START_TEST(test_request_handlers)
 	                         websock_server_ready,
 	                         websock_server_data,
 	                         websock_server_close,
-	                         (void *)7531);
+	                         (void *)(ptrdiff_t)7531);
 #endif
 
 	/* Try to load non existing file */
@@ -1869,11 +1890,14 @@ START_TEST(test_request_handlers)
 	ws_client1_data.data = NULL;
 	ws_client1_data.len = 0;
 
+	ck_assert(ws_client1_data.closed == 0); /* Not closed */
+
 	mg_close_connection(ws_client1_conn);
 
 	test_sleep(3); /* Won't get any message */
 
-	ck_assert(ws_client1_data.closed == 1);
+	ck_assert(ws_client1_data.closed == 1); /* Closed */
+
 	ck_assert(ws_client2_data.closed == 0);
 	ck_assert(ws_client1_data.data == NULL);
 	ck_assert(ws_client1_data.len == 0);
@@ -2431,9 +2455,9 @@ START_TEST(test_handle_form)
 	opt = mg_get_option(ctx, "listening_ports");
 	ck_assert_str_eq(opt, "8884");
 
-	mg_set_request_handler(ctx, "/handle_form", FormGet, (void *)0);
-	mg_set_request_handler(ctx, "/handle_form_store", FormStore1, (void *)0);
-	mg_set_request_handler(ctx, "/handle_form_store2", FormStore2, (void *)0);
+	mg_set_request_handler(ctx, "/handle_form", FormGet, NULL);
+	mg_set_request_handler(ctx, "/handle_form_store", FormStore1, NULL);
+	mg_set_request_handler(ctx, "/handle_form_store2", FormStore2, NULL);
 
 	test_sleep(1);
 
@@ -4044,7 +4068,7 @@ START_TEST(test_large_file)
 	OPTIONS[opt_cnt++] = "ssl_protocol_version";
 	OPTIONS[opt_cnt++] = "2";
 #else
-	                 /* The Linux builds on Travis CI work fine with TLS1.2 */
+	/* The Linux builds on Travis CI work fine with TLS1.2 */
 	OPTIONS[opt_cnt++] = "ssl_protocol_version";
 	OPTIONS[opt_cnt++] = "4";
 #endif