ソースを参照

Add documentation for recently added features

Update UsarManual and API reference
Add unit test for parse_http_headers
bel2125 4 年 前
コミット
3019ad17c7

+ 1 - 1
VisualStudio/unit_test/unit_test.vcxproj

@@ -73,7 +73,7 @@
       </PrecompiledHeader>
       <WarningLevel>Level3</WarningLevel>
       <Optimization>Disabled</Optimization>
-      <PreprocessorDefinitions>MAIN_PUBLIC_SERVER=main;OPENSSL_API_1_1;LOCAL_TEST;REPLACE_CHECK_FOR_LOCAL_DEBUGGING;LOCAL_TEST;USE_IPV6;USE_WEBSOCKET;MEMORY_DEBUGGING;WIN32;_DEBUG;_CONSOLE;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <PreprocessorDefinitions>MAIN_PRIVATE=main;OPENSSL_API_1_1;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>

+ 17 - 7
docs/APIReference.md

@@ -37,16 +37,18 @@ The content of both structures is not defined in the interface - they are only u
 
 ## Structures
 
+* [`struct mg_callbacks;`](api/mg_callbacks.md)
 * [`struct mg_client_cert;`](api/mg_client_cert.md)
 * [`struct mg_client_options;`](api/mg_client_options.md)
-* [`struct mg_callbacks;`](api/mg_callbacks.md)
+* [`struct mg_error_data;`](api/mg_error_data.md)
 * [`struct mg_form_data_handler;`](api/mg_form_data_handler.md)
 * [`struct mg_header;`](api/mg_header.md)
+* [`struct mg_init_data;`](api/mg_init_data.md)
 * [`struct mg_option;`](api/mg_option.md)
 * [`struct mg_request_info;`](api/mg_request_info.md)
 * [`struct mg_response_info;`](api/mg_response_info.md)
 * [`struct mg_server_port;`](api/mg_server_port.md)
-
+* [`struct mg_websocket_subprotocols;`](api/mg_websocket_subprotocols.md)
 
 ## Library API Functions
 
@@ -60,6 +62,9 @@ The content of both structures is not defined in the interface - they are only u
 ## Server API Functions
 
 * [`mg_start( callbacks, user_data, options );`](api/mg_start.md)
+* [`mg_start2( init, error );`](api/mg_start2.md)
+* [`mg_start_domain( ctx, configuration_options );`](api/mg_start_domain.md)
+* [`mg_start_domain2( ctx, configuration_options, error );`](api/mg_start_domain2.md)
 * [`mg_stop( ctx );`](api/mg_stop.md)
 
 * [`mg_get_builtin_mime_type( file_name );`](api/mg_get_builtin_mime_type.md)
@@ -69,6 +74,7 @@ The content of both structures is not defined in the interface - they are only u
 * [`mg_set_auth_handler( ctx, uri, handler, cbdata );`](api/mg_set_auth_handler.md)
 * [`mg_set_request_handler( ctx, uri, handler, cbdata );`](api/mg_set_request_handler.md)
 * [`mg_set_websocket_handler( ctx, uri, connect_handler, ready_handler, data_handler, close_handler, cbdata );`](api/mg_set_websocket_handler.md)
+* [`mg_set_websocket_handler_with_subprotocols( ctx, uri, subprotocols, connect_handler, ready_handler, data_handler, close_handler, cbdata );`](api/mg_set_websocket_handler_with_subprotocols.md)
 
 * [`mg_lock_context( ctx );`](api/mg_lock_context.md)
 * [`mg_unlock_context( ctx );`](api/mg_unlock_context.md)
@@ -92,6 +98,8 @@ The content of both structures is not defined in the interface - they are only u
 * [`mg_send_mime_file2( conn, path, mime_type, additional_headers );`](api/mg_send_mime_file2.md)
 * [`mg_websocket_write( conn, opcode, data, data_len );`](api/mg_websocket_write.md)
 
+* [`mg_response_header_*();`](api/mg_response_header_X.md)
+
 ## Client API Functions
 
 * [`mg_connect_client( host, port, use_ssl, error_buffer, error_buffer_size );`](api/mg_connect_client.md)
@@ -104,6 +112,10 @@ The content of both structures is not defined in the interface - they are only u
 * [`mg_get_response( conn, ebuf, ebuf_len, timeout );`](api/mg_get_response.md)
 * [`mg_get_response_info( conn );`](api/mg_get_response_info.md)
 
+* [`mg_connect_client2( host, protocol, port, path, init, error );`](api/mg_connect_client2.md)
+* [`mg_get_response2( conn, error, timeout );`](api/mg_get_response2.md)
+
+
 ## Common API Functions
 
 * [`mg_close_connection( conn );`](api/mg_close_connection.md)
@@ -121,8 +133,9 @@ The content of both structures is not defined in the interface - they are only u
 * [`mg_printf( conn, fmt, ... );`](api/mg_printf.md)
 * [`mg_read( conn, buf, len );`](api/mg_read.md)
 * [`mg_send_chunk( conn, buf, len );`](api/mg_send_chunk.md)
-* [`mg_send_file( conn, path );`](api/mg_send_file_body.md)
+* [`mg_send_file_body( conn, path );`](api/mg_send_file_body.md)
 * [`mg_set_user_connection_data( conn, data );`](api/mg_set_user_connection_data.md)
+* [`mg_split_form_urlencoded( data, form_fields, num_form_fields);`](api/mg_split_form_urlencoded.md)
 * [`mg_start_thread( f, p );`](api/mg_start_thread.md)
 * [`mg_store_body( conn, path );`](api/mg_store_body.md)
 * [`mg_strcasecmp( s1, s2 );`](api/mg_strcasecmp.md)
@@ -132,7 +145,6 @@ The content of both structures is not defined in the interface - they are only u
 * [`mg_url_encode( src, dst, dst_len );`](api/mg_url_encode.md)
 * [`mg_write( conn, buf, len );`](api/mg_write.md)
 
-
 ## Diagnosis Functions
 
 * [`mg_get_system_info( buffer, buf_len );`](api/mg_get_system_info.md)
@@ -140,10 +152,8 @@ The content of both structures is not defined in the interface - they are only u
 * [`mg_get_connection_info( ctx, idx, buffer, buf_len );`](api/mg_get_connection_info.md)
 
 
-## Deprecated:
+## Deprecated / removed:
 
 * [~~`mg_get_valid_option_names();`~~](api/mg_get_valid_option_names.md)
 * [~~`mg_upload( conn, destination_dir );`~~](api/mg_upload.md)
 * [~~`mg_get_ports( ctx, size, ports, ssl);`~~](api/mg_get_ports.md)
-
-

+ 7 - 4
docs/Building.md

@@ -49,8 +49,10 @@ Get a list of all supported make option
 
 ```
 make build
+make WITH_ALL=1
 ```
-compile the code
+Compile the code.
+Using the option "WITH_ALL=1" enables all optional features.
 
 ```
 make install
@@ -58,11 +60,10 @@ make install
 Install on the system, Linux only.
 
 ```
-make lib WITH_CPP=1 WITH_IPV6=1
-make clean slib WITH_CPP=1 WITH_LUA=1 WITH_WEBSOCKET=1
+make lib WITH_IPV6=1
+make clean slib WITH_LUA=1 WITH_WEBSOCKET=1
 ```
 Build the static and shared libraries.
-The *WITH_CPP* make option is to include the CivetServer class.
 The additional make options configure the library just as it would the application.
 
 The *slib* option should be done on a separate clean build as position
@@ -89,6 +90,7 @@ make build WITH_LUA=1
 | `WITH_DUKTAPE=1`            | build with server-side JavaScript support         |
 | `WITH_IPV6=1`               | with IPV6 support                                 |
 | `WITH_WEBSOCKET=1`          | build with web socket support                     |
+| `WITH_X_DOM_SOCKET=1`       | build with unix domain socket support             |
 | `WITH_SERVER_STATS=1`       | build with support for server statistics          |
 | `WITH_EXPERIMENTAL=1`       | include experimental features (version depending) |
 | `WITH_ALL=1`                | Include all of the above features                 |
@@ -167,6 +169,7 @@ make build COPT="-DNDEBUG -DNO_CGI"
 | `USE_SERVER_STATS`           | enable server statistics support                                    |
 | `USE_STACK_SIZE`             | define stack size instead of using system default                   |
 | `USE_WEBSOCKET`              | enable websocket support                                            |
+| `USE_X_DOM_SOCKET`           | enable unix domain socket support                                   |
 | `USE_ZLIB`                   | enable on-the-fly compression of files (using zlib)                 |
 |                              |                                                                     |
 | `MG_EXPERIMENTAL_INTERFACES` | include experimental interfaces                                     |

+ 21 - 4
docs/Embedding.md

@@ -39,7 +39,9 @@ but all functions required to run a HTTP server.
     - src/mod\_*.inl (modules to access third party components from civetweb)
 
 
-Note: The C++ wrapper uses the official C interface (civetweb.h) without adding any features to the server itself. Several features available in the C interface are missing in the C++ interface. While all features should be accessible using the C interface, this is not a design goal of the C++ interface.
+Note: The C++ wrapper uses the official C interface (civetweb.h) without adding any features to the server itself. 
+Several features available in the C interface are missing in the C++ interface. 
+While all features should be accessible using the C interface, this is not a design goal of the C++ interface.
 New code is advised to use the C interface, since this is unit tested and new API functions are often only added there.
 
 
@@ -179,7 +181,7 @@ Lua is a server side include functionality.  Files ending in .lua will be proces
   - src/third\_party/lfs.c
   - src/third\_party/lfs.h
 
-This build is valid for Lua version Lua 5.2. It is also possible to build with Lua 5.1 (including LuaJIT) or Lua 5.3.
+This build is valid for Lua version Lua 5.2. It is also possible to build with Lua 5.1 (including LuaJIT), Lua 5.3 or Lua 5.4.
 
 
 JavaScript Support
@@ -284,7 +286,23 @@ Initializing a HTTP server
 }
 ```
 
-A simple callback
+A simple callback (HTTP/1.x and HTTP/2):
+```C
+static int
+handler(struct mg_connection *conn, void *ignored)
+{
+	const char *msg = "Hello world";
+	unsigned long len = (unsigned long)strlen(msg);
+
+    mg_send_ok(conn, "text/plain", len);
+
+	mg_write(conn, msg, len);
+
+	return 200; /* HTTP state 200 = OK */
+}
+```
+
+A simple callback supporting HTTP/1.x only:
 ```C
 static int
 handler(struct mg_connection *conn, void *ignored)
@@ -304,4 +322,3 @@ handler(struct mg_connection *conn, void *ignored)
 	return 200;
 }
 ```
-

+ 24 - 5
docs/UserManual.md

@@ -436,7 +436,23 @@ environment - in some cases, you might need to resort to a fixed IP address.
 If you want to use an ephemeral port (i.e. let the operating system choose
 a port number), use `0` for the port number. This will make it necessary to
 communicate the port number to clients via other means, for example mDNS
-(or Zeroconf, Bonjour or Avahi).
+(Zeroconf, Bonjour, Avahi).
+
+In case the server has been built with the `USE_X_DOM_SOCKET` option set,
+it can listen to unix domain sockets as well. They are specified by a
+lower case `x` followed by the domain socket path, e.g. `x/tmp/sockname`.
+Domain sockets do not require a port number, always use HTTP (not HTTPS) 
+and never redirect. Thus `:` is not allowed, while `r` or `s` at the end 
+of the configuration is interpreted as part of the domain socket path.
+The domain sochet path must be a valid path to a non-existing file on a 
+Unix/Linux system. The CivetWeb process needs write/create access rights
+to create the domain socket in the Unix/Linux file system. 
+Use only alphanumerical characters, underscore and `/` in a domain socket
+path (in particular, `,;:` must be avoided).
+
+All socket/protocol types may be combined, separated by `,`.
+E.g.: `127.0.0.1:80,[::1]:80,x/tmp/sockname` will listen to localhost
+http connections using IPv4, IPv6 and the domain socket `/tmp/sockname`.
 
 ### lua\_background\_script
 Experimental feature, and subject to change.
@@ -446,9 +462,9 @@ It can be used to prepare the document root (e.g., update files, compress
 files, ...), check for external resources, remove old log files, etc.
 
 The Lua state remains open until the server is stopped.
-In the future, some callback functions will be available to notify the
-script on changes of the server state. See example lua script :
-[background.lua](https://github.com/civetweb/civetweb/blob/master/test/lua_backbround_script_timer.lua).
+
+For a detailed descriotion of available Lua callbacks see section 
+"Lua background script" below. 
 
 ### lua\_background\_script\_params
 Can add dynamic parameters to background script.
@@ -979,7 +995,7 @@ Lua websocket pages do support single shot (timeout) and interval timers.
 An example is shown in
 [websocket.lua](https://github.com/civetweb/civetweb/blob/master/test/websocket.lua).
 
-##Lua background script
+## Lua background script
 The Lua background script is loaded when the server is starting,
 before any client is able to connect. It can be used for preparation and
 maintenance tasks, e.g., for preparing the web contents, cleaning log files,
@@ -999,6 +1015,9 @@ A Lua background script may define the following functions:
     stop()       -- called when the server is stopped
 
 
+See example Lua script :
+[background.lua](https://github.com/civetweb/civetweb/blob/master/test/lua_backbround_script_timer.lua).
+
 # Using CGI
 
 Unlike some other web servers, CivetWeb does not require CGI scripts to be located

+ 23 - 0
docs/api/mg_error_data.md

@@ -0,0 +1,23 @@
+# Civetweb API Reference
+
+### `struct mg_error_data;`
+
+### Fields
+
+| Field | Type | Description |
+| :--- | :--- | :--- |
+|**`code`**|`unsigned *`| A pointer to an `unsigned` variable, to store the error code. |
+|**`text`**|`char *`| A text buffer to store the error text. |
+|**`text_buffer_size`**|`size_t`| Size of the text buffer. |
+
+### Description
+
+The structure `mg_error_data` is used in [`mg_start2()`](mg_start.md), [`mg_start_domain2();`](mg_start_domain2.md), [`mg_connect_client2();`](mg_connect_client2.md) and [`mg_get_response2();`](mg_get_response2.md) to return error information.
+
+### See Also
+
+* [`mg_start2();`](mg_start2.md)
+* [`mg_start_domain2();`](mg_start_domain2.md)
+* [`mg_connect_client2();`](mg_connect_client2.md)
+* [`mg_get_response2();`](mg_get_response2.md)
+* [`struct mg_init_data;`](mg_init_data.md)

+ 24 - 0
docs/api/mg_init_data.md

@@ -0,0 +1,24 @@
+# Civetweb API Reference
+
+### `struct mg_init_data;`
+
+### Fields
+
+| Field | Type | Description |
+| :--- | :--- | :--- |
+|**`callbacks`**|`const struct mg_callbacks *`| A structure with optional callback functions to process requests from the web server |
+|**`user_data`**|`void *`| A pointer to optional user data |
+|**`configuration_options`**|`const char **`| A list of options used to initialize the web server. The list consists of an NULL terminated list of option-value string pairs. |
+
+### Description
+
+The structure `mg_init_data` is used in [`mg_start2()`](mg_start.md) and [`mg_connect_client2();`](mg_connect_client2.md).
+It holds the same parameters supplied to [`mg_start();`](mg_start.md) as individual arguments in one structure.
+
+### See Also
+
+* [`struct mg_callbacks;`](mg_callbacks.md)
+* [`mg_start();`](mg_start.md)
+* [`mg_start2();`](mg_start2.md)
+* [`mg_connect_client2();`](mg_connect_client2.md)
+* [`struct mg_error_data;`](mg_error_data.md)

+ 3 - 1
docs/api/mg_set_websocket_handler.md

@@ -8,7 +8,7 @@
 | :--- | :--- | :--- |
 |**`ctx`**|`mg_context *`|The context in which to add the handlers|
 |**`uri`**|`const char *`|The URI for which the handlers should be activated|
-|**`connect_handler`**|`mg_websocket_connect_handler`|Handler called when a connect is signalled|
+|**`connect_handler`**|`mg_websocket_connect_handler`|Handler called when a connect is signaled|
 |**`ready_handler`**|`mg_websocket_ready_handler`|Handler called when the connection is ready|
 |**`data_handler`**|`mg_websocket_data_handler`|Handler called when data is received|
 |**`close_handler`**|`mg_websocket_close_handler`|Handler called when the connection closes|
@@ -28,3 +28,5 @@
 The function `mg_set_websocket_handler()` connects callback functions to a websocket URI. The callback functions are called when a state change is detected on the URI like an incoming connection or data received from a remote peer.
 
 ### See Also
+
+* [`mg_set_websocket_handler_with_subprotocols();`](mg_set_websocket_handler_with_subprotocols.md)

+ 36 - 0
docs/api/mg_set_websocket_handler_with_subprotocols.md

@@ -0,0 +1,36 @@
+# Civetweb API Reference
+
+### `mg_set_websocket_handler_with_subprotocols( ctx, uri, subprotocols, connect_handler, ready_handler, data_handler, close_handler, cbdata );`
+
+### Parameters
+
+| Parameter | Type | Description |
+| :--- | :--- | :--- |
+|**`ctx`**|`mg_context *`|The context in which to add the handlers|
+|**`uri`**|`const char *`|The URI for which the handlers should be activated|
+|**`subprotocols`**|`struct mg_websocket_subprotocols *`|A list of supported sub-protocols|
+|**`connect_handler`**|`mg_websocket_connect_handler`|Handler called when a connect is signaled|
+|**`ready_handler`**|`mg_websocket_ready_handler`|Handler called when the connection is ready|
+|**`data_handler`**|`mg_websocket_data_handler`|Handler called when data is received|
+|**`close_handler`**|`mg_websocket_close_handler`|Handler called when the connection closes|
+|**`cbdata`**|`void *`|User defined data|
+
+`int mg_websocket_connect_handler( const struct mg_connection *conn, void *cbdata );`
+`int mg_websocket_ready_handler( struct mg_connection *conn, void *cbdata );`
+`int mg_websocket_data_handler( struct mg_connection *conn, int opcode, char * buf, size_t buf_len, void *cbdata );`
+`int mg_websocket_close_handler( const struct mg_connection *conn,  void *cbdata );`
+
+### Return Value
+
+*none*
+
+### Description
+
+The function `mg_set_websocket_handler_with_subprotocols()` connects callback functions to a websocket URI, just like [`mg_set_websocket_handler();`](mg_set_websocket_handler.md). 
+In addition, it allows to specify websocket sub-protocols.
+The callback functions are called when a state change is detected on the URI like an incoming connection or data received from a remote peer.
+
+### See Also
+
+* [`struct mg_websocket_subprotocols;`](api/mg_websocket_subprotocols.md)
+* [`mg_set_websocket_handler();`](mg_set_websocket_handler.md)

+ 18 - 0
docs/api/mg_websocket_subprotocols.md

@@ -0,0 +1,18 @@
+# Civetweb API Reference
+
+### `struct mg_websocket_subprotocols;`
+
+### Fields
+
+| Field | Type | Description |
+| :--- | :--- | :--- |
+|**`nb_subprotocols`**|`int`| Number of websocket sub-protocol names following (>=0). |
+|**`subprotocols`**|`const char **`| Array of websocket sub-protocol names (nb_subprotocols elements). |
+
+### Description
+
+The structure `mg_websocket_subprotocols` is used to specify websocket sub-protocols supported by a websocket handler function.
+
+### See Also
+
+* [`mg_set_websocket_handler_with_subprotocols();`](mg_set_websocket_handler_with_subprotocols.md)

+ 1 - 1
include/civetweb.h

@@ -564,7 +564,7 @@ typedef void (*mg_websocket_close_handler)(const struct mg_connection *,
  */
 struct mg_websocket_subprotocols {
 	int nb_subprotocols;
-	char **subprotocols;
+	const char **subprotocols;
 };
 
 /* mg_set_websocket_handler

+ 6 - 0
src/civetweb.c

@@ -10844,6 +10844,12 @@ parse_http_headers(char **buf, struct mg_header hdr[MG_MAX_HEADERS])
 			/* End of headers reached. */
 			break;
 		}
+
+		/* Drop all spaces after header name before : */
+		while (*dp == ' ') {
+			*dp = 0;
+			dp++;
+		}
 		if (*dp != ':') {
 			/* This is not a valid field. */
 			return -1;

+ 1 - 0
unittest/CMakeLists.txt

@@ -168,6 +168,7 @@ civetweb_add_test(Private "Internal Parsing 3")
 civetweb_add_test(Private "Internal Parsing 4")
 civetweb_add_test(Private "Internal Parsing 5")
 civetweb_add_test(Private "Internal Parsing 6")
+civetweb_add_test(Private "Internal Parsing 7")
 civetweb_add_test(Private "Encode Decode")
 civetweb_add_test(Private "Mask Data")
 civetweb_add_test(Private "Date Parsing")

+ 266 - 0
unittest/private.c

@@ -707,6 +707,266 @@ START_TEST(test_parse_port_string)
 END_TEST
 
 
+START_TEST(test_parse_http_headers)
+{
+	char buf[2048];
+	char *ptr;
+	int ret;
+	struct mg_header hdr[MG_MAX_HEADERS];
+
+	memset(hdr, 0, sizeof(hdr));
+	memset(buf, 0, sizeof(buf));
+	ptr = &buf[0];
+	ret = parse_http_headers(&ptr, hdr);
+	ck_assert_int_eq(ret, 0);
+	ck_assert_ptr_eq(ptr, &buf[0]);
+	ck_assert_ptr_eq(hdr[0].name, NULL);
+	ck_assert_ptr_eq(hdr[0].value, NULL);
+	ck_assert_ptr_eq(hdr[1].name, NULL);
+	ck_assert_ptr_eq(hdr[1].value, NULL);
+	ck_assert_ptr_eq(hdr[2].name, NULL);
+	ck_assert_ptr_eq(hdr[2].value, NULL);
+
+	memset(hdr, 0, sizeof(hdr));
+	memset(buf, 0, sizeof(buf));
+	strcpy(buf, "\r\n");
+	ptr = &buf[0];
+	ret = parse_http_headers(&ptr, hdr);
+	ck_assert_int_eq(ret, 0);
+	ck_assert_ptr_eq(ptr, &buf[0]);
+	ck_assert_ptr_eq(hdr[0].name, NULL);
+	ck_assert_ptr_eq(hdr[0].value, NULL);
+	ck_assert_ptr_eq(hdr[1].name, NULL);
+	ck_assert_ptr_eq(hdr[1].value, NULL);
+	ck_assert_ptr_eq(hdr[2].name, NULL);
+	ck_assert_ptr_eq(hdr[2].value, NULL);
+
+	memset(hdr, 0, sizeof(hdr));
+	memset(buf, 0, sizeof(buf));
+	strcpy(buf, "a\r\n");
+	ptr = &buf[0];
+	ret = parse_http_headers(&ptr, hdr);
+	ck_assert_int_eq(ret, -1);
+	ck_assert_ptr_eq(ptr, &buf[0]);
+	ck_assert_ptr_eq(hdr[0].name, NULL);
+	ck_assert_ptr_eq(hdr[0].value, NULL);
+	ck_assert_ptr_eq(hdr[1].name, NULL);
+	ck_assert_ptr_eq(hdr[1].value, NULL);
+	ck_assert_ptr_eq(hdr[2].name, NULL);
+	ck_assert_ptr_eq(hdr[2].value, NULL);
+
+	memset(hdr, 0, sizeof(hdr));
+	memset(buf, 0, sizeof(buf));
+	strcpy(buf, "a:b");
+	ptr = &buf[0];
+	ret = parse_http_headers(&ptr, hdr);
+	ck_assert_int_eq(ret, 1);
+	ck_assert_ptr_eq(ptr, &buf[3]);
+	ck_assert_str_eq(hdr[0].name, "a");
+	ck_assert_str_eq(hdr[0].value, "b");
+	ck_assert_ptr_eq(hdr[0].name, &buf[0]);
+	ck_assert_ptr_eq(hdr[0].value, &buf[2]);
+	ck_assert_ptr_eq(hdr[1].name, NULL);
+	ck_assert_ptr_eq(hdr[1].value, NULL);
+	ck_assert_ptr_eq(hdr[2].name, NULL);
+	ck_assert_ptr_eq(hdr[2].value, NULL);
+
+	memset(hdr, 0, sizeof(hdr));
+	memset(buf, 0, sizeof(buf));
+	strcpy(buf, "a:b\r\n");
+	ptr = &buf[0];
+	ret = parse_http_headers(&ptr, hdr);
+	ck_assert_int_eq(ret, 1);
+	ck_assert_ptr_eq(ptr, &buf[5]);
+	ck_assert_str_eq(hdr[0].name, "a");
+	ck_assert_str_eq(hdr[0].value, "b");
+	ck_assert_ptr_eq(hdr[0].name, &buf[0]);
+	ck_assert_ptr_eq(hdr[0].value, &buf[2]);
+	ck_assert_ptr_eq(hdr[1].name, NULL);
+	ck_assert_ptr_eq(hdr[1].value, NULL);
+	ck_assert_ptr_eq(hdr[2].name, NULL);
+	ck_assert_ptr_eq(hdr[2].value, NULL);
+
+	memset(hdr, 0, sizeof(hdr));
+	memset(buf, 0, sizeof(buf));
+	strcpy(buf, "a:b\r\n\r\n");
+	ptr = &buf[0];
+	ret = parse_http_headers(&ptr, hdr);
+	ck_assert_int_eq(ret, 1);
+	ck_assert_ptr_eq(ptr, &buf[5]);
+	ck_assert_str_eq(hdr[0].name, "a");
+	ck_assert_str_eq(hdr[0].value, "b");
+	ck_assert_ptr_eq(hdr[0].name, &buf[0]);
+	ck_assert_ptr_eq(hdr[0].value, &buf[2]);
+	ck_assert_ptr_eq(hdr[1].name, NULL);
+	ck_assert_ptr_eq(hdr[1].value, NULL);
+	ck_assert_ptr_eq(hdr[2].name, NULL);
+	ck_assert_ptr_eq(hdr[2].value, NULL);
+
+	memset(hdr, 0, sizeof(hdr));
+	memset(buf, 0, sizeof(buf));
+	strcpy(buf, "a: b\r\n");
+	ptr = &buf[0];
+	ret = parse_http_headers(&ptr, hdr);
+	ck_assert_int_eq(ret, 1);
+	ck_assert_ptr_eq(ptr, &buf[6]);
+	ck_assert_str_eq(hdr[0].name, "a");
+	ck_assert_str_eq(hdr[0].value, "b");
+	ck_assert_ptr_eq(hdr[0].name, &buf[0]);
+	ck_assert_ptr_eq(hdr[0].value, &buf[3]);
+	ck_assert_ptr_eq(hdr[1].name, NULL);
+	ck_assert_ptr_eq(hdr[1].value, NULL);
+	ck_assert_ptr_eq(hdr[2].name, NULL);
+	ck_assert_ptr_eq(hdr[2].value, NULL);
+
+	memset(hdr, 0, sizeof(hdr));
+	memset(buf, 0, sizeof(buf));
+	strcpy(buf, "a :b\r\n");
+	ptr = &buf[0];
+	ret = parse_http_headers(&ptr, hdr);
+	ck_assert_int_eq(ret, 1);
+	ck_assert_ptr_eq(ptr, &buf[6]);
+	ck_assert_str_eq(hdr[0].name, "a");
+	ck_assert_str_eq(hdr[0].value, "b");
+	ck_assert_ptr_eq(hdr[0].name, &buf[0]);
+	ck_assert_ptr_eq(hdr[0].value, &buf[3]);
+	ck_assert_ptr_eq(hdr[1].name, NULL);
+	ck_assert_ptr_eq(hdr[1].value, NULL);
+	ck_assert_ptr_eq(hdr[2].name, NULL);
+	ck_assert_ptr_eq(hdr[2].value, NULL);
+
+	memset(hdr, 0, sizeof(hdr));
+	memset(buf, 0, sizeof(buf));
+	strcpy(buf, "a : b\r\n");
+	ptr = &buf[0];
+	ret = parse_http_headers(&ptr, hdr);
+	ck_assert_int_eq(ret, 1);
+	ck_assert_ptr_eq(ptr, &buf[7]);
+	ck_assert_str_eq(hdr[0].name, "a");
+	ck_assert_str_eq(hdr[0].value, "b");
+	ck_assert_ptr_eq(hdr[0].name, &buf[0]);
+	ck_assert_ptr_eq(hdr[0].value, &buf[4]);
+	ck_assert_ptr_eq(hdr[1].name, NULL);
+	ck_assert_ptr_eq(hdr[1].value, NULL);
+	ck_assert_ptr_eq(hdr[2].name, NULL);
+	ck_assert_ptr_eq(hdr[2].value, NULL);
+
+	memset(hdr, 0, sizeof(hdr));
+	memset(buf, 0, sizeof(buf));
+	strcpy(buf, "a: b\r\nc: d\r\n");
+	ptr = &buf[0];
+	ret = parse_http_headers(&ptr, hdr);
+	ck_assert_int_eq(ret, 2);
+	ck_assert_ptr_eq(ptr, &buf[12]);
+	ck_assert_str_eq(hdr[0].name, "a");
+	ck_assert_str_eq(hdr[0].value, "b");
+	ck_assert_str_eq(hdr[1].name, "c");
+	ck_assert_str_eq(hdr[1].value, "d");
+	ck_assert_ptr_eq(hdr[0].name, &buf[0]);
+	ck_assert_ptr_eq(hdr[0].value, &buf[3]);
+	ck_assert_ptr_eq(hdr[1].name, &buf[6]);
+	ck_assert_ptr_eq(hdr[1].value, &buf[9]);
+	ck_assert_ptr_eq(hdr[2].name, NULL);
+	ck_assert_ptr_eq(hdr[2].value, NULL);
+
+	memset(hdr, 0, sizeof(hdr));
+	memset(buf, 0, sizeof(buf));
+	strcpy(buf, "a: b\r\nc: d\r\n\r\n");
+	ptr = &buf[0];
+	ret = parse_http_headers(&ptr, hdr);
+	ck_assert_int_eq(ret, 2);
+	ck_assert_ptr_eq(ptr, &buf[12]);
+	ck_assert_str_eq(hdr[0].name, "a");
+	ck_assert_str_eq(hdr[0].value, "b");
+	ck_assert_str_eq(hdr[1].name, "c");
+	ck_assert_str_eq(hdr[1].value, "d");
+	ck_assert_ptr_eq(hdr[0].name, &buf[0]);
+	ck_assert_ptr_eq(hdr[0].value, &buf[3]);
+	ck_assert_ptr_eq(hdr[1].name, &buf[6]);
+	ck_assert_ptr_eq(hdr[1].value, &buf[9]);
+	ck_assert_ptr_eq(hdr[2].name, NULL);
+	ck_assert_ptr_eq(hdr[2].value, NULL);
+
+	memset(hdr, 0, sizeof(hdr));
+	memset(buf, 0, sizeof(buf));
+	strcpy(buf, "a: b\r\nc: d\r\n\r\ne: f\r\n");
+	ptr = &buf[0];
+	ret = parse_http_headers(&ptr, hdr);
+	ck_assert_int_eq(ret, 2);
+	ck_assert_ptr_eq(ptr, &buf[12]);
+	ck_assert_str_eq(hdr[0].name, "a");
+	ck_assert_str_eq(hdr[0].value, "b");
+	ck_assert_str_eq(hdr[1].name, "c");
+	ck_assert_str_eq(hdr[1].value, "d");
+	ck_assert_ptr_eq(hdr[0].name, &buf[0]);
+	ck_assert_ptr_eq(hdr[0].value, &buf[3]);
+	ck_assert_ptr_eq(hdr[1].name, &buf[6]);
+	ck_assert_ptr_eq(hdr[1].value, &buf[9]);
+	ck_assert_ptr_eq(hdr[2].name, NULL);
+	ck_assert_ptr_eq(hdr[2].value, NULL);
+
+	memset(hdr, 0, sizeof(hdr));
+	memset(buf, 0, sizeof(buf));
+	strcpy(buf, "a: b\r\nc: d");
+	ptr = &buf[0];
+	ret = parse_http_headers(&ptr, hdr);
+	ck_assert_int_eq(ret, 2);
+	ck_assert_ptr_eq(ptr, &buf[10]);
+	ck_assert_str_eq(hdr[0].name, "a");
+	ck_assert_str_eq(hdr[0].value, "b");
+	ck_assert_str_eq(hdr[1].name, "c");
+	ck_assert_str_eq(hdr[1].value, "d");
+	ck_assert_ptr_eq(hdr[0].name, &buf[0]);
+	ck_assert_ptr_eq(hdr[0].value, &buf[3]);
+	ck_assert_ptr_eq(hdr[1].name, &buf[6]);
+	ck_assert_ptr_eq(hdr[1].value, &buf[9]);
+	ck_assert_ptr_eq(hdr[2].name, NULL);
+	ck_assert_ptr_eq(hdr[2].value, NULL);
+
+	memset(hdr, 0, sizeof(hdr));
+	memset(buf, 0, sizeof(buf));
+	strcpy(buf, "a: b\r\nc: d\r\ne");
+	ptr = &buf[0];
+	ret = parse_http_headers(&ptr, hdr);
+	ck_assert_int_eq(ret, -1);
+	/* The following could be undefined, since ret == -1 */
+	ck_assert_ptr_eq(ptr, &buf[12]);
+	ck_assert_str_eq(hdr[0].name, "a");
+	ck_assert_str_eq(hdr[0].value, "b");
+	ck_assert_str_eq(hdr[1].name, "c");
+	ck_assert_str_eq(hdr[1].value, "d");
+	ck_assert_ptr_eq(hdr[0].name, &buf[0]);
+	ck_assert_ptr_eq(hdr[0].value, &buf[3]);
+	ck_assert_ptr_eq(hdr[1].name, &buf[6]);
+	ck_assert_ptr_eq(hdr[1].value, &buf[9]);
+	ck_assert_ptr_eq(hdr[2].name, NULL);
+	ck_assert_ptr_eq(hdr[2].value, NULL);
+
+	memset(hdr, 0, sizeof(hdr));
+	memset(buf, 0, sizeof(buf));
+	strcpy(buf, "a: b\r\nc: d\r\nefg:");
+	ptr = &buf[0];
+	ret = parse_http_headers(&ptr, hdr);
+	ck_assert_int_eq(ret, 3);
+	ck_assert_ptr_eq(ptr, &buf[16]);
+	ck_assert_str_eq(hdr[0].name, "a");
+	ck_assert_str_eq(hdr[0].value, "b");
+	ck_assert_str_eq(hdr[1].name, "c");
+	ck_assert_str_eq(hdr[1].value, "d");
+	ck_assert_str_eq(hdr[2].name, "efg");
+	ck_assert_str_eq(hdr[2].value, "");
+	ck_assert_ptr_eq(hdr[0].name, &buf[0]);
+	ck_assert_ptr_eq(hdr[0].value, &buf[3]);
+	ck_assert_ptr_eq(hdr[1].name, &buf[6]);
+	ck_assert_ptr_eq(hdr[1].value, &buf[9]);
+	ck_assert_ptr_eq(hdr[2].name, &buf[12]);
+	ck_assert_ptr_eq(hdr[2].value, &buf[16]);
+
+	int bp = 1;
+}
+END_TEST
+
+
 START_TEST(test_encode_decode)
 {
 	char buf[128];
@@ -1161,6 +1421,7 @@ make_private_suite(void)
 	TCase *const tcase_internal_parse_4 = tcase_create("Internal Parsing 4");
 	TCase *const tcase_internal_parse_5 = tcase_create("Internal Parsing 5");
 	TCase *const tcase_internal_parse_6 = tcase_create("Internal Parsing 6");
+	TCase *const tcase_internal_parse_6 = tcase_create("Internal Parsing 7");
 	TCase *const tcase_encode_decode = tcase_create("Encode Decode");
 	TCase *const tcase_mask_data = tcase_create("Mask Data");
 	TCase *const tcase_parse_date_string = tcase_create("Date Parsing");
@@ -1211,6 +1472,10 @@ make_private_suite(void)
 	tcase_set_timeout(tcase_internal_parse_6, civetweb_min_test_timeout);
 	suite_add_tcase(suite, tcase_internal_parse_6);
 
+	tcase_add_test(tcase_internal_parse_7, test_parse_http_headers);
+	tcase_set_timeout(tcase_internal_parse_7, civetweb_min_test_timeout);
+	suite_add_tcase(suite, tcase_internal_parse_7);
+
 	tcase_add_test(tcase_encode_decode, test_encode_decode);
 	tcase_set_timeout(tcase_encode_decode, civetweb_min_test_timeout);
 	suite_add_tcase(suite, tcase_encode_decode);
@@ -1254,6 +1519,7 @@ MAIN_PRIVATE(void)
 	test_parse_date_string(0);
 	test_parse_port_string(0);
 	test_parse_http_message(0);
+	test_parse_http_headers(0);
 	test_sha1(0);
 
 #if defined(_WIN32)