Просмотр исходного кода

Merge branch 'master' into patch-2

bel2125 5 лет назад
Родитель
Сommit
6f414dfa2e

+ 9 - 1
.gitignore

@@ -267,5 +267,13 @@ requests.db
 ci/lua
 
 
-# Conan test cache
+##########################
+## Conan test cache
+##########################
 conan/test_package/build
+
+
+##########################
+## Visual Studio Code
+##########################
+.vscode

+ 16 - 8
CREDITS.md

@@ -11,6 +11,7 @@
 * beaver
 * bel2125
 * Ben M. Ward
+* Bernhard Lehner
 * BigJoe
 * Bjoern Petri
 * Braedy Kuzma
@@ -20,9 +21,9 @@
 * Brian Spratke
 * cdbishop
 * celeron55
+* Charles Olivi
 * Chris Jones
 * Chris Rehn
-* Charles Olivi
 * Christian Mauderer
 * Christopher Galas
 * cjh
@@ -38,6 +39,7 @@
 * David Loffredo
 * Dialga
 * Domenico Di Iorio
+* Drew Wells
 * duong2179
 * ehlertjd
 * Eric Tsau
@@ -45,11 +47,11 @@
 * Erik Beran
 * Erik Partridge
 * extergnoto
+* F-Secure Corporation
 * Fabrice Fontaine
 * feneuilflo
 * Fernando G. Aranda
 * Frank Hilliger
-* F-Secure Corporation
 * Grahack
 * Gregor Jasny
 * grenclave
@@ -61,20 +63,21 @@
 * Herumb Shandilya
 * Herve Codina
 * Iain Morton
-* Ivan Dlugos
 * ImgBotApp
+* Ivan Dlugos
 * Jack
 * Jacob Repp
 * Jacob Skillin
 * Jan Kowalewski
 * Jan Willem Janssen
 * Jeremy Lin
+* Jesse Williamson
 * Jim Evans
 * jmc-
 * Joakim L. Gilje
 * Jochen Scheib
-* Joel Gallant
 * Joe Mucchiello
+* Joel Gallant
 * Johan De Taeye
 * John Faith
 * Jordan
@@ -94,9 +97,10 @@
 * Kimmo Mustonen
 * Krzysztof Kozlowski
 * Lammert Bies
+* Lars Immisch
 * Lawrence
-* Lianghui
 * Li Peng
+* Lianghui
 * Luka Rahne
 * Maarten Fremouw
 * makrsmark
@@ -120,19 +124,22 @@
 * palortoff
 * Patrick Drechsler
 * Patrick Trinkle
-* Paulo Brizolara
 * Paul Sokolovsky
+* Paulo Brizolara
 * pavel.pimenov
 * PavelVozenilek
 * Perttu Ahola
 * Peter Foerster
 * Philipp Friedenberger
 * Philipp Hasper
+* Piotr Zierhoffer
 * pkvamme
 * Radoslaw Zarzynski
 * Red54
 * Retallack Mark mark.retallack
 * Richard Screene
+* Rimas Misevi-ìius
+* Rinat Dobrokhotov
 * ryankopf
 * Sage Weil
 * Sangwhan Moon
@@ -147,17 +154,18 @@
 * SpaceLord
 * sunfch
 * suzukibitman
+* Símal Rasmussen
 * Tamotsu Kanoh
 * thewaterymoon
 * Thiago Macedo
 * THILMANT, Bernard
 * Thomas Davis
 * Thomas Klausner
-* Tomasz Gorochowik
 * Thorsten Horstmann
 * tnoho
-* Tomas Andrle
 * Tom Deblauwe
+* Tomas Andrle
+* Tomasz Gorochowik
 * Toni Wilk
 * Torben Jonas
 * Uilian Ries

+ 10 - 0
RELEASE_NOTES.md

@@ -5,6 +5,16 @@ Release Notes v1.12
 Changes
 -------
 
+- Format configuration dialogs for Windows
+- Add option "hide_tray" to start without Windows systray icon
+- URI checking according to "remove_dot_segments" algorithm from RFC
+- Experimental support for a new server and client start API
+- Additional callbacks to initialize external SSL context
+- More cache control options for static files
+- Trace function for Lua server pages
+- Access to client certificate data for Lua pages
+- Allow to configure SOMAXCONN (max. number of waiting connections)
+- Include some build options for Zephyr
 - Support for flawed CGI interpreters returning only <LF> instead of <CR><LF>
 - Add NO_FILESYSTEM flag for (embedded) system without any file system
 - Several fixes for server side Lua scripts

+ 15 - 1
docs/UserManual.md

@@ -617,6 +617,20 @@ than the depth set here connection is refused.
 ### ssl\_verify\_peer `no`
 Enable client's certificate verification by the server.
 
+### static\_file\_cache\_control
+Set the `Cache-Control` header of static files responses.
+The string value will be used directly.
+
+E.g. this config:
+
+`static_file_cache_control no-cache, max-age=31536000`
+
+Will result in this header being added:
+
+`Cache-Control: no-cache, max-age=31536000`
+
+This will take precedence over the static\_file\_max\_age option.
+
 ### static\_file\_max\_age `3600`
 Set the maximum time (in seconds) a cache may store a static files.
 
@@ -626,7 +640,7 @@ must send cache control headers by themselves.
 
 A value >0 corresponds to a maximum allowed caching time in seconds.
 This value should not exceed one year (RFC 2616, Section 14.21).
-A value of 0 will send "do not cache" headers for all static files.
+A value of 0 will send "do not cache at all" headers for all static files.
 For values <0 and values >31622400, the behaviour is undefined.
 
 ### strict\_transport\_security\_max\_age

+ 8 - 4
docs/api/mg_callbacks.md

@@ -20,10 +20,14 @@
 | |The callback function `init_context()` is called after the CivetWeb server has been started and initialized, but before any requests are served. This allowes the application to perform some initialization activities before the first requests are handled.|
 |**`init_lua`**|**`void (*init_lua)( const struct mg_connection *conn, void *lua_context );`**|
 | |The callback function `init_lua()` is called just before a Lua server page is to be served. Lua page serving must have been enabled at compile time for this callback function to be called. The parameter `lua_context` is a `lua_State *` pointer.|
-|**`external_ssl_ctx`**|**`int (*external_ssl_ctx)(void **ssl_ctx, void *user_data);`**|
-| |The callback function `external_ssl_ctx()` is called when civetweb is about to create (`*ssl_ctx` is `NULL`) or free (`*ssl_ctx` is not `NULL`) a SSL context. The parameter `user_data` contains a pointer to the data which was provided to `mg_start()` when the server was started. The callback function can return 0 to signal that CivetWeb should setup the SSL context. With a return value of 1 the callback function signals CivetWeb that the SSL context has already been setup and no further processing is necessary. Also with a return value of 1 another callback function `init_ssl()` is not called. The value -1 should be returned when the SSL context initialization fails.|
-|**`init_ssl`**|**`int (*init_ssl)( void *ssl_context, void *user_data );`**|
-| |The callback function `init_ssl()` is called when CivetWeb initializes the SSL library. The parameter `user_data` contains a pointer to the data which was provided to `mg_start()` when the server was started. The callback function can return 0 to signal that CivetWeb should setup the SSL certificate. With a return value of 1 the callback function signals CivetWeb that the certificate has already been setup and no further processing is necessary. The value -1 should be returned when the SSL initialization fails.|
+|**`external_ssl_ctx`**|**`int (*external_ssl_ctx)( void **ssl_ctx, void *user_data );`**|
+| |The callback function `external_ssl_ctx()` is called when civetweb is about to create (`*ssl_ctx` is `NULL`) or free (`*ssl_ctx` is not `NULL`) a SSL context. The parameter `user_data` contains a pointer to the data which was provided to `mg_start()` when the server was started. The callback function can return 0 to signal that CivetWeb should setup the SSL context. With a return value of 1 the callback function signals CivetWeb that the SSL context has already been setup and no further processing is necessary. Also with a return value of 1 other callback functions `init_ssl()` and `init_ssl_domain()` are not called. The value -1 should be returned when the SSL context initialization fails.|
+|**`external_ssl_ctx_domain`**|**`int (*external_ssl_ctx_domain)( const char *server_domain, void **ssl_ctx, void *user_data );`**|
+| |The callback function `external_ssl_ctx_domain()` is called when civetweb is about to create (`*ssl_ctx` is `NULL`) or free (`*ssl_ctx` is not `NULL`) a SSL context. The parameter `server_domain` is a pointer to the `authentication_domain` config parameter of the domain being created or freed. The parameter `user_data` contains a pointer to the data which was provided to `mg_start()` when the server was started. The callback function can return 0 to signal that CivetWeb should setup the SSL context. With a return value of 1 the callback function signals CivetWeb that the SSL context has already been setup and no further processing is necessary. Also with a return value of 1 other callback functions `init_ssl()` and `init_ssl_domain()` are not called. The value -1 should be returned when the SSL context initialization fails.|
+|**`init_ssl`**|**`int (*init_ssl)( void *ssl_ctx, void *user_data );`**|
+| |The callback function `init_ssl()` is called when CivetWeb initializes the SSL library. The `ssl_ctx` parameter is a pointer to the SSL context being configure. The parameter `user_data` contains a pointer to the data which was provided to `mg_start()` when the server was started. The callback function can return 0 to signal that CivetWeb should setup the SSL certificate. With a return value of 1 the callback function signals CivetWeb that the certificate has already been setup and no further processing is necessary. The value -1 should be returned when the SSL initialization fails.|
+|**`init_ssl_domain`**|**`int (*init_ssl_domain)( const char *server_domain, void *ssl_ctx, void *user_data );`**|
+| |The callback function `init_ssl_domain()` is called when CivetWeb initializes the SSL library. The parameter `server_domain` is a pointer to the `authentication_domain` config parameter of the domain being configured. The `ssl_ctx` parameter is a pointer to the SSL context being configure. The parameter `user_data` contains a pointer to the data which was provided to `mg_start()` when the server was started. The callback function can return 0 to signal that CivetWeb should setup the SSL certificate. With a return value of 1 the callback function signals CivetWeb that the certificate has already been setup and no further processing is necessary. The value -1 should be returned when the SSL initialization fails.|
 |**`init_thread`**|**`void * (*init_thread)( const struct mg_context *ctx, int thread_type );`**|
 | |The callback function `init_thread()` is called when a new thread is created by CivetWeb. The `thread_type` parameter indicates which type of thread has been created. following thread types are recognized:|
 | |**0** - The master thread is created |

+ 1 - 1
examples/embedded_c/Makefile

@@ -18,7 +18,7 @@ LIBS = -lpthread
 include $(TOP)/resources/Makefile.in-os
 
 ifeq ($(TARGET_OS),LINUX) 
-	LIBS += -ldl
+	LIBS += -ldl -lrt
 endif
 
 all: $(PROG)

+ 2 - 2
examples/embedded_c/embedded_c.c

@@ -916,10 +916,10 @@ get_dh2236()
 
 #ifndef TEST_WITHOUT_SSL
 int
-init_ssl(void *ssl_context, void *user_data)
+init_ssl(void *ssl_ctx, void *user_data)
 {
 	/* Add application specific SSL initialization */
-	struct ssl_ctx_st *ctx = (struct ssl_ctx_st *)ssl_context;
+	struct ssl_ctx_st *ctx = (struct ssl_ctx_st *)ssl_ctx;
 
 #ifdef USE_SSL_DH
 	/* example from https://github.com/civetweb/civetweb/issues/347 */

+ 1 - 1
examples/embedded_cpp/Makefile

@@ -18,7 +18,7 @@ LIBS = -lpthread
 include $(TOP)/resources/Makefile.in-os
 
 ifeq ($(TARGET_OS),LINUX) 
-	LIBS += -ldl
+	LIBS += -ldl -lrt
 endif
 
 all: $(PROG)

+ 98 - 18
include/civetweb.h

@@ -1,4 +1,4 @@
-/* Copyright (c) 2013-2018 the Civetweb developers
+/* Copyright (c) 2013-2019 the Civetweb developers
  * Copyright (c) 2004-2013 Sergey Lyubka
  *
  * Permission is hereby granted, free of charge, to any person obtaining a copy
@@ -247,28 +247,54 @@ struct mg_callbacks {
 
 	/* Called when civetweb initializes SSL library.
 	   Parameters:
+	     ssl_ctx: SSL_CTX pointer.
 	     user_data: parameter user_data passed when starting the server.
 	   Return value:
 	     0: civetweb will set up the SSL certificate.
 	     1: civetweb assumes the callback already set up the certificate.
 	    -1: initializing ssl fails. */
-	int (*init_ssl)(void *ssl_context, void *user_data);
+	int (*init_ssl)(void *ssl_ctx, void *user_data);
 
-	/* Called when civetweb is about to create or free a SSL_CTX.
-	Parameters:
-	   ssl_ctx: SSL_CTX pointer. NULL at creation time, Not NULL when mg_context
-	            will be freed
+	/* Called when civetweb initializes SSL library for a domain.
+	   Parameters:
+	     server_domain: authentication_domain from the domain config.
+	     ssl_ctx: SSL_CTX pointer.
 	     user_data: parameter user_data passed when starting the server.
 	   Return value:
-	     0: civetweb will continue to create the context, just as if the
-	        callback would not be present.
-	        The value in *ssl_ctx when the function returns is ignored.
-	     1: civetweb will copy the value from *ssl_ctx to the civetweb context
-	        and doesn't create its own.
-	    -1: initializing ssl fails.*/
+	     0: civetweb will set up the SSL certificate.
+	     1: civetweb assumes the callback already set up the certificate.
+	    -1: initializing ssl fails. */
+	int (*init_ssl_domain)(const char *server_domain,
+	                       void *ssl_ctx,
+	                       void *user_data);
+
+	/* Called when civetweb is about to create or free a SSL_CTX.
+	Parameters:
+	     ssl_ctx: SSL_CTX pointer. NULL at creation time, Not NULL when
+	mg_context will be freed user_data: parameter user_data passed when starting
+	the server. Return value: 0: civetweb will continue to create the context,
+	just as if the callback would not be present. The value in *ssl_ctx when the
+	function returns is ignored. 1: civetweb will copy the value from *ssl_ctx
+	to the civetweb context and doesn't create its own. -1: initializing ssl
+	fails.*/
 	int (*external_ssl_ctx)(void **ssl_ctx, void *user_data);
 
-#if defined(MG_LEGACY_INTERFACE) /* 2015-08-19 */
+	/* Called when civetweb is about to create or free a SSL_CTX for a domain.
+	Parameters:
+	     server_domain: authentication_domain from the domain config.
+	     ssl_ctx: SSL_CTX pointer. NULL at creation time, Not NULL when
+	mg_context will be freed user_data: parameter user_data passed when starting
+	the server. Return value: 0: civetweb will continue to create the context,
+	just as if the callback would not be present. The value in *ssl_ctx when the
+	function returns is ignored. 1: civetweb will copy the value from *ssl_ctx
+	to the civetweb context and doesn't create its own. -1: initializing ssl
+	fails.*/
+	int (*external_ssl_ctx_domain)(const char *server_domain,
+	                               void **ssl_ctx,
+	                               void *user_data);
+
+#if defined(MG_LEGACY_INTERFACE)           /* 2015-08-19 */                    \
+    || defined(MG_EXPERIMENTAL_INTERFACES) /* 2019-11-03 */
 	/* Called when websocket request is received, before websocket handshake.
 	   Return value:
 	     0: civetweb proceeds with websocket handshake.
@@ -381,7 +407,6 @@ struct mg_callbacks {
 	                    int thread_type,
 	                    void *thread_pointer);
 
-
 	/* Called when initializing a new connection object.
 	 * Can be used to initialize the connection specific user data
 	 * (mg_request_info->conn_data, mg_get_user_connection_data).
@@ -618,6 +643,10 @@ mg_get_context(const struct mg_connection *conn);
 CIVETWEB_API void *mg_get_user_data(const struct mg_context *ctx);
 
 
+/* Get user data passed to mg_start from connection. */
+CIVETWEB_API void *mg_get_user_context_data(const struct mg_connection *conn);
+
+
 /* Get user defined thread pointer for server threads (see init_thread). */
 CIVETWEB_API void *mg_get_thread_pointer(const struct mg_connection *conn);
 
@@ -1149,6 +1178,10 @@ CIVETWEB_API int mg_get_cookie(const char *cookie,
      struct mg_connection *conn;
      conn = mg_download("google.com", 80, 0, ebuf, sizeof(ebuf),
                         "%s", "GET / HTTP/1.0\r\nHost: google.com\r\n\r\n");
+
+   mg_download is equivalent to calling mg_connect_client followed by
+   mg_printf and mg_get_response. Using these three functions directly may
+   allow more control as compared to using mg_download.
  */
 CIVETWEB_API struct mg_connection *
 mg_download(const char *host,
@@ -1425,7 +1458,9 @@ mg_connect_client_secure(const struct mg_client_options *client_options,
                          size_t error_buffer_size);
 
 
+#if defined(MG_LEGACY_INTERFACE) /* 2019-11-02 */
 enum { TIMEOUT_INFINITE = -1 };
+#endif
 enum { MG_TIMEOUT_INFINITE = -1 };
 
 /* Wait for a response from the server
@@ -1464,13 +1499,17 @@ CIVETWEB_API int mg_get_response(struct mg_connection *conn,
         64  support server side JavaScript (USE_DUKTAPE is set)
        128  support caching (NO_CACHING not set)
        256  support server statistics (USE_SERVER_STATS is set)
+       512  support for on the fly compression (USE_ZLIB is set)
+
+       These values are defined as MG_FEATURES_*
+
        The result is undefined, if bits are set that do not represent a
-       defined feature (currently: feature >= 512).
+       defined feature (currently: feature >= 1024).
        The result is undefined, if no bit is set (feature == 0).
 
    Return:
-     If feature is available, the corresponding bit is set
-     If feature is not available, the bit is 0
+     If a feature is available, the corresponding bit is set
+     If a feature is not available, the bit is 0
 */
 CIVETWEB_API unsigned mg_check_feature(unsigned feature);
 
@@ -1510,7 +1549,7 @@ CIVETWEB_API int
 mg_get_context_info(const struct mg_context *ctx, char *buffer, int buflen);
 
 
-#ifdef MG_EXPERIMENTAL_INTERFACES
+#if defined(MG_EXPERIMENTAL_INTERFACES)
 /* Get connection information. Useful for server diagnosis.
    Parameters:
      ctx: Context handle
@@ -1534,6 +1573,47 @@ CIVETWEB_API int mg_get_connection_info(const struct mg_context *ctx,
 #endif
 
 
+/* New APIs for enhanced option and error handling.
+   These mg_*2 API functions have the same purpose as their original versions,
+   but provide additional options and/or provide improved error diagnostics.
+
+   Note: Experimental interfaces may change
+*/
+struct mg_error_data {
+	unsigned *code;          /* error code (number) */
+	char *text;              /* buffer for error text */
+	size_t text_buffer_size; /* size of buffer of "text" */
+};
+
+struct mg_init_data {
+	const struct mg_callbacks *callbacks; /* callback function pointer */
+	void *user_data;                      /* data */
+	const char **configuration_options;
+};
+
+
+#if defined(MG_EXPERIMENTAL_INTERFACES)
+
+CIVETWEB_API struct mg_connection *
+mg_connect_client2(const char *host,
+                   const char *protocol,
+                   int port,
+                   const char *path,
+                   struct mg_init_data *init,
+                   struct mg_error_data *error);
+
+CIVETWEB_API int mg_get_response2(struct mg_connection *conn,
+                                  struct mg_error_data *error,
+                                  int timeout);
+
+CIVETWEB_API struct mg_context *mg_start2(struct mg_init_data *init,
+                                          struct mg_error_data *error);
+
+CIVETWEB_API int mg_start_domain2(struct mg_context *ctx,
+                                  const char **configuration_options,
+                                  struct mg_error_data *error);
+#endif
+
 #ifdef __cplusplus
 }
 #endif /* __cplusplus */

Разница между файлами не показана из-за своего большого размера
+ 594 - 100
src/civetweb.c


+ 130 - 93
src/main.c

@@ -1,4 +1,4 @@
-/* Copyright (c) 2013-2018 the Civetweb developers
+/* Copyright (c) 2013-2020 the Civetweb developers
  * Copyright (c) 2004-2013 Sergey Lyubka
  *
  * Permission is hereby granted, free of charge, to any person obtaining a copy
@@ -184,7 +184,8 @@ static int g_num_add_domains;     /* Default from init_server_name,
                                    * updated later from the server config */
 static const char **g_add_domain; /* Default from init_server_name,
                                    * updated later from the server config */
-
+static int g_hide_tray = 0;       /* Default = do not hide (0),
+                                   * updated later from the server config */
 
 static char *g_system_info; /* Set by init_system_info() */
 static char g_config_file_name[PATH_MAX] =
@@ -213,6 +214,7 @@ enum {
 	OPTION_ICON,
 	OPTION_WEBPAGE,
 	OPTION_ADD_DOMAIN,
+	OPTION_HIDE_TRAY,
 	NUM_MAIN_OPTIONS
 };
 
@@ -221,6 +223,7 @@ static struct mg_option main_config_options[] = {
     {"icon", MG_CONFIG_TYPE_STRING, NULL},
     {"website", MG_CONFIG_TYPE_STRING, NULL},
     {"add_domain", MG_CONFIG_TYPE_STRING_LIST, NULL},
+    {"hide_tray", MG_CONFIG_TYPE_BOOLEAN, NULL},
     {NULL, MG_CONFIG_TYPE_UNKNOWN, NULL}};
 
 
@@ -584,6 +587,14 @@ set_option(char **options, const char *name, const char *value)
 			g_website = sdup(value);
 			return 1;
 		}
+		if (!strcmp(name, main_config_options[OPTION_HIDE_TRAY].name)) {
+			if (!strcmp(value, "yes")) {
+				g_hide_tray = 1;
+			} else if (!strcmp(value, "no")) {
+				g_hide_tray = 0;
+			}
+			return 1;
+		}
 		if (!strcmp(name, main_config_options[OPTION_ADD_DOMAIN].name)) {
 			if (g_num_add_domains > 0) {
 				g_add_domain = (const char **)realloc(
@@ -1562,13 +1573,16 @@ struct dlg_proc_param {
 };
 
 struct dlg_header_param {
+	/* https://docs.microsoft.com/en-us/windows/win32/api/winuser/ns-winuser-dlgtemplate
+	 */
 	DLGTEMPLATE dlg_template; /* 18 bytes */
 	WORD menu, dlg_class;
 	wchar_t caption[1];
-	WORD fontsiz;
+	WORD fontsize;
 	wchar_t fontface[7];
 };
 
+
 static struct dlg_header_param
 GetDlgHeader(const short width)
 {
@@ -1577,26 +1591,29 @@ GetDlgHeader(const short width)
 #pragma warning(push)
 #pragma warning(disable : 4204)
 #endif /* if defined(_MSC_VER) */
-	struct dlg_header_param dialog_header = {{WS_CAPTION | WS_POPUP | WS_SYSMENU
-	                                              | WS_VISIBLE | DS_SETFONT
-	                                              | WS_DLGFRAME,
-	                                          WS_EX_TOOLWINDOW,
-	                                          0,
-	                                          200,
-	                                          200,
-	                                          width,
-	                                          0},
-	                                         0,
-	                                         0,
-	                                         L"",
-	                                         8,
-	                                         L"Tahoma"};
+
+	struct dlg_header_param dialog_header = {
+	    /* DLGTEMPLATE */
+	    {/* style */ WS_CAPTION | WS_POPUP | WS_SYSMENU | WS_VISIBLE
+	         | DS_SETFONT | DS_CENTER | WS_DLGFRAME,
+	     /* extstyle */ WS_EX_TOOLWINDOW,
+	     /* cdit */ 0,
+	     /* x ignored by DS_CENTER */ 0,
+	     /* y ignored by DS_CENTER */ 0,
+	     width,
+	     /* height - to be calculated */ 0},
+	    /* menu */ 0,
+	    /* dlg_class */ 0,
+	    /* caption */ L"",
+	    /* fontsize */ 8,
+	    /* font */ L"Tahoma"};
 #if defined(_MSC_VER)
 #pragma warning(pop)
 #endif /* if defined(_MSC_VER) */
 	return dialog_header;
 }
 
+
 /* Dialog proc for settings dialog */
 static INT_PTR CALLBACK
 SettingsDlgProc(HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam)
@@ -1888,10 +1905,12 @@ get_password(const char *user,
              char *passwd,
              unsigned passwd_len)
 {
-#define HEIGHT (15)
-#define WIDTH (280)
-#define LABEL_WIDTH (90)
+	/* Parameter for size/format tuning of the dialog */
+	short HEIGHT = 15;
+	short WIDTH = 280;
+	short LABEL_WIDTH = 90;
 
+	/* Other variables */
 	unsigned char mem[4096], *p;
 	DLGTEMPLATE *dia = (DLGTEMPLATE *)mem;
 	int ok;
@@ -2030,16 +2049,12 @@ get_password(const char *user,
 
 	ok = (IDOK
 	      == DialogBoxIndirectParam(
-	             NULL, dia, NULL, InputDlgProc, (LPARAM)&s_dlg_proc_param));
+	          NULL, dia, NULL, InputDlgProc, (LPARAM)&s_dlg_proc_param));
 
 	s_dlg_proc_param.hWnd = NULL;
 	s_dlg_proc_param.guard = 0;
 
 	return ok;
-
-#undef HEIGHT
-#undef WIDTH
-#undef LABEL_WIDTH
 }
 
 
@@ -2163,19 +2178,28 @@ add_control(unsigned char **mem,
 static void
 show_settings_dialog()
 {
-#define HEIGHT (15)
-#define WIDTH (460)
-#define LABEL_WIDTH (90)
-
+	/* Parameter for size/format tuning of the dialog */
+	short HEIGHT = 15;
+	short BORDER_WIDTH = 10;
+	short CELL_WIDTH = 125;
+	short LABEL_WIDTH = 115;
+	short FILE_DIALOG_BUTTON_WIDTH = 15;
+	short NO_OF_COLUMNS = 3;
+
+	/* Calculates size */
+	short COLUMN_WIDTH = LABEL_WIDTH + CELL_WIDTH + BORDER_WIDTH;
+	short DIALOG_WIDTH = BORDER_WIDTH + NO_OF_COLUMNS * COLUMN_WIDTH;
+
+	/* All other variables */
 	unsigned char mem[16 * 1024], *p;
 	const struct mg_option *options;
 	DWORD style;
 	DLGTEMPLATE *dia = (DLGTEMPLATE *)mem;
 	WORD i, cl, nelems = 0;
-	short width, x, y;
+	short x, y, next_cell_width;
 	static struct dlg_proc_param s_dlg_proc_param;
 
-	const struct dlg_header_param dialog_header = GetDlgHeader(WIDTH);
+	const struct dlg_header_param dialog_header = GetDlgHeader(DIALOG_WIDTH);
 
 	if (s_dlg_proc_param.guard == 0) {
 		memset(&s_dlg_proc_param, 0, sizeof(s_dlg_proc_param));
@@ -2193,9 +2217,11 @@ show_settings_dialog()
 	options = mg_get_valid_options();
 	for (i = 0; options[i].name != NULL; i++) {
 		style = WS_CHILD | WS_VISIBLE | WS_TABSTOP;
-		x = 10 + (WIDTH / 2) * (nelems % 2);
-		y = (nelems / 2 + 1) * HEIGHT + 5;
-		width = WIDTH / 2 - 20 - LABEL_WIDTH;
+
+		x = BORDER_WIDTH + COLUMN_WIDTH * (nelems % NO_OF_COLUMNS);
+		y = BORDER_WIDTH / 2 + HEIGHT + HEIGHT * (nelems / NO_OF_COLUMNS);
+		next_cell_width = CELL_WIDTH;
+
 		if (options[i].type == MG_CONFIG_TYPE_NUMBER) {
 			style |= ES_NUMBER;
 			cl = 0x81;
@@ -2206,19 +2232,24 @@ show_settings_dialog()
 		} else if ((options[i].type == MG_CONFIG_TYPE_FILE)
 		           || (options[i].type == MG_CONFIG_TYPE_DIRECTORY)) {
 			style |= WS_BORDER | ES_AUTOHSCROLL;
-			width -= 20;
 			cl = 0x81;
+
+			/* Additional button for file dialog */
 			add_control(&p,
 			            dia,
 			            0x80,
 			            ID_CONTROLS + i + ID_FILE_BUTTONS_DELTA,
 			            WS_VISIBLE | WS_CHILD | BS_PUSHBUTTON,
-			            x + width + LABEL_WIDTH + 5,
+			            x + LABEL_WIDTH + CELL_WIDTH - FILE_DIALOG_BUTTON_WIDTH,
 			            y,
-			            15,
-			            12,
+			            FILE_DIALOG_BUTTON_WIDTH,
+			            HEIGHT - 3,
 			            "...");
+
+			next_cell_width -= FILE_DIALOG_BUTTON_WIDTH + BORDER_WIDTH / 2;
+
 		} else if (options[i].type == MG_CONFIG_TYPE_STRING_MULTILINE) {
+
 			/* TODO: This is not really uer friendly */
 			cl = 0x81;
 			style |= WS_BORDER | ES_AUTOHSCROLL | ES_MULTILINE | ES_WANTRETURN
@@ -2227,6 +2258,8 @@ show_settings_dialog()
 			cl = 0x81;
 			style |= WS_BORDER | ES_AUTOHSCROLL;
 		}
+
+		/* Add label (static text) */
 		add_control(&p,
 		            dia,
 		            0x82,
@@ -2244,32 +2277,35 @@ show_settings_dialog()
 		            style,
 		            x + LABEL_WIDTH,
 		            y,
-		            width,
-		            12,
+		            next_cell_width,
+		            HEIGHT - 3,
 		            "");
 		nelems++;
 
 		DEBUG_ASSERT(((intptr_t)p - (intptr_t)mem) < (intptr_t)sizeof(mem));
 	}
 
-	y = (((nelems + 1) / 2 + 1) * HEIGHT + 5);
+	/* "Settings" frame around all options */
+	y = ((nelems + NO_OF_COLUMNS - 1) / NO_OF_COLUMNS + 1) * HEIGHT;
 	add_control(&p,
 	            dia,
 	            0x80,
 	            ID_GROUP,
 	            WS_CHILD | WS_VISIBLE | BS_GROUPBOX,
-	            5,
-	            5,
-	            WIDTH - 10,
+	            BORDER_WIDTH / 2,
+	            BORDER_WIDTH / 2,
+	            DIALOG_WIDTH - BORDER_WIDTH,
 	            y,
 	            " Settings ");
-	y += 10;
+
+	/* Buttons below "Settings" frame */
+	y += HEIGHT;
 	add_control(&p,
 	            dia,
 	            0x80,
 	            ID_SAVE,
 	            WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON | WS_TABSTOP,
-	            WIDTH - 70,
+	            DIALOG_WIDTH - 70,
 	            y,
 	            65,
 	            12,
@@ -2279,7 +2315,7 @@ show_settings_dialog()
 	            0x80,
 	            ID_RESET_DEFAULTS,
 	            WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON | WS_TABSTOP,
-	            WIDTH - 140,
+	            DIALOG_WIDTH - 140,
 	            y,
 	            65,
 	            12,
@@ -2289,7 +2325,7 @@ show_settings_dialog()
 	            0x80,
 	            ID_RESET_FILE,
 	            WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON | WS_TABSTOP,
-	            WIDTH - 210,
+	            DIALOG_WIDTH - 210,
 	            y,
 	            65,
 	            12,
@@ -2299,7 +2335,7 @@ show_settings_dialog()
 	            0x80,
 	            ID_RESET_ACTIVE,
 	            WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON | WS_TABSTOP,
-	            WIDTH - 280,
+	            DIALOG_WIDTH - 280,
 	            y,
 	            65,
 	            12,
@@ -2315,9 +2351,11 @@ show_settings_dialog()
 	            12,
 	            g_server_base_name);
 
+	/* Check befor every release */
 	DEBUG_ASSERT(((intptr_t)p - (intptr_t)mem) < (intptr_t)sizeof(mem));
 
-	dia->cy = ((nelems + 1) / 2 + 1) * HEIGHT + 30;
+	/* Calculate total height of the dialog */
+	dia->cy = y + HEIGHT;
 
 	s_dlg_proc_param.fRetry = NULL;
 
@@ -2326,20 +2364,18 @@ show_settings_dialog()
 
 	s_dlg_proc_param.hWnd = NULL;
 	s_dlg_proc_param.guard = 0;
-
-#undef HEIGHT
-#undef WIDTH
-#undef LABEL_WIDTH
 }
 
 
 static void
 change_password_file()
 {
-#define HEIGHT (15)
-#define WIDTH (320)
-#define LABEL_WIDTH (90)
+	/* Parameter for size/format tuning of the dialog */
+	short HEIGHT = 15;
+	short WIDTH = 320;
+	short LABEL_WIDTH = 90;
 
+	/* Other variables */
 	OPENFILENAME of;
 	char path[PATH_MAX] = PASSWORDS_FILE_NAME;
 	char strbuf[256], u[256], d[256];
@@ -2519,18 +2555,13 @@ change_password_file()
 		s_dlg_proc_param.name = path;
 		s_dlg_proc_param.fRetry = NULL;
 
-	} while (
-	    (IDOK
-	     == DialogBoxIndirectParam(
-	            NULL, dia, NULL, PasswordDlgProc, (LPARAM)&s_dlg_proc_param))
-	    && (!g_exit_flag));
+	} while ((IDOK
+	          == DialogBoxIndirectParam(
+	              NULL, dia, NULL, PasswordDlgProc, (LPARAM)&s_dlg_proc_param))
+	         && (!g_exit_flag));
 
 	s_dlg_proc_param.hWnd = NULL;
 	s_dlg_proc_param.guard = 0;
-
-#undef HEIGHT
-#undef WIDTH
-#undef LABEL_WIDTH
 }
 
 
@@ -2564,10 +2595,12 @@ sysinfo_reload(struct dlg_proc_param *prm)
 int
 show_system_info()
 {
-#define HEIGHT (15)
-#define WIDTH (320)
-#define LABEL_WIDTH (50)
+	/* Parameter for size/format tuning of the dialog */
+	short HEIGHT = 15;
+	short WIDTH = 320;
+	short LABEL_WIDTH = 50;
 
+	/* Other parameters */
 	unsigned char mem[4096], *p;
 	DLGTEMPLATE *dia = (DLGTEMPLATE *)mem;
 	int ok;
@@ -2648,16 +2681,12 @@ show_system_info()
 
 	ok = (IDOK
 	      == DialogBoxIndirectParam(
-	             NULL, dia, NULL, InputDlgProc, (LPARAM)&s_dlg_proc_param));
+	          NULL, dia, NULL, InputDlgProc, (LPARAM)&s_dlg_proc_param));
 
 	s_dlg_proc_param.hWnd = NULL;
 	s_dlg_proc_param.guard = 0;
 
 	return ok;
-
-#undef HEIGHT
-#undef WIDTH
-#undef LABEL_WIDTH
 }
 
 
@@ -2731,7 +2760,6 @@ WindowProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
 	char buf[200];
 	POINT pt;
 	HMENU hMenu;
-	static UINT s_uTaskbarRestart; /* for taskbar creation */
 
 	switch (msg) {
 
@@ -2754,7 +2782,6 @@ WindowProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
 			exit(EXIT_SUCCESS);
 		} else {
 			start_civetweb(__argc, __argv);
-			s_uTaskbarRestart = RegisterWindowMessage(TEXT("TaskbarCreated"));
 		}
 		break;
 
@@ -2762,7 +2789,9 @@ WindowProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
 		switch (LOWORD(wParam)) {
 		case ID_QUIT:
 			stop_civetweb();
-			Shell_NotifyIcon(NIM_DELETE, &TrayIcon);
+			if (TrayIcon.cbSize) {
+				Shell_NotifyIcon(NIM_DELETE, &TrayIcon);
+			}
 			g_exit_flag = 1;
 			PostQuitMessage(0);
 			return 0;
@@ -2840,14 +2869,12 @@ WindowProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
 
 	case WM_CLOSE:
 		stop_civetweb();
-		Shell_NotifyIcon(NIM_DELETE, &TrayIcon);
+		if (TrayIcon.cbSize) {
+			Shell_NotifyIcon(NIM_DELETE, &TrayIcon);
+		}
 		g_exit_flag = 1;
 		PostQuitMessage(0);
 		return 0; /* We've just sent our own quit message, with proper hwnd. */
-
-	default:
-		if (msg == s_uTaskbarRestart)
-			Shell_NotifyIcon(NIM_ADD, &TrayIcon);
 	}
 
 	return DefWindowProc(hWnd, msg, wParam, lParam);
@@ -2933,6 +2960,7 @@ WinMain(HINSTANCE hInst, HINSTANCE hPrev, LPSTR cmdline, int show)
 	                    NULL);
 	ShowWindow(hWnd, SW_HIDE);
 
+	/* Load icon for systray and other dialogs */
 	if (g_icon_name) {
 		hIcon = (HICON)
 		    LoadImage(NULL, g_icon_name, IMAGE_ICON, 16, 16, LR_LOADFROMFILE);
@@ -2945,15 +2973,21 @@ WinMain(HINSTANCE hInst, HINSTANCE hPrev, LPSTR cmdline, int show)
 		                         0);
 	}
 
-	TrayIcon.cbSize = sizeof(TrayIcon);
-	TrayIcon.uID = ID_ICON;
-	TrayIcon.uFlags = NIF_ICON | NIF_MESSAGE | NIF_TIP;
-	TrayIcon.hIcon = hIcon;
-	TrayIcon.hWnd = hWnd;
-	snprintf(TrayIcon.szTip, sizeof(TrayIcon.szTip), "%s", g_server_name);
-	TrayIcon.uCallbackMessage = WM_USER;
-	Shell_NotifyIcon(NIM_ADD, &TrayIcon);
+	/* add icon to systray; tray icon is entry point to the menu */
+	if (!g_hide_tray) {
+		TrayIcon.cbSize = sizeof(TrayIcon);
+		TrayIcon.uID = ID_ICON;
+		TrayIcon.uFlags = NIF_ICON | NIF_MESSAGE | NIF_TIP;
+		TrayIcon.hIcon = hIcon;
+		TrayIcon.hWnd = hWnd;
+		snprintf(TrayIcon.szTip, sizeof(TrayIcon.szTip), "%s", g_server_name);
+		TrayIcon.uCallbackMessage = WM_USER;
+		Shell_NotifyIcon(NIM_ADD, &TrayIcon);
+	} else {
+		TrayIcon.cbSize = 0;
+	}
 
+	/* Message loop */
 	while (GetMessage(&msg, hWnd, 0, 0) > 0) {
 		TranslateMessage(&msg);
 		DispatchMessage(&msg);
@@ -2985,13 +3019,15 @@ main(int argc, char *argv[])
 @end
 
 @implementation Civetweb
-- (void)openBrowser {
+- (void)openBrowser
+{
 	[[NSWorkspace sharedWorkspace]
 	    openURL:[NSURL URLWithString:[NSString stringWithUTF8String:
 	                                               get_url_to_first_open_port(
 	                                                   g_ctx)]]];
 }
-- (void)editConfig {
+- (void)editConfig
+{
 	create_config_file(g_ctx, g_config_file_name);
 	NSString *path = [NSString stringWithUTF8String:g_config_file_name];
 	if (![[NSWorkspace sharedWorkspace] openFile:path
@@ -3004,7 +3040,8 @@ main(int argc, char *argv[])
 		(void)[alert runModal];
 	}
 }
-- (void)shutDown {
+- (void)shutDown
+{
 	[NSApp terminate:nil];
 }
 @end

+ 68 - 0
src/mod_lua.inl

@@ -1588,6 +1588,67 @@ lsp_get_option(lua_State *L)
 	return luaL_error(L, "invalid get_option() call");
 }
 
+static int s_lua_traceLevel = 1;
+static FILE *s_lua_traceFile = NULL;
+static pthread_mutex_t s_lua_traceMutex;
+
+
+/* mg.trace */
+static int
+lsp_trace(lua_State *L)
+{
+	int num_args = lua_gettop(L);
+	int arg_type[8];
+	int trace_level = 0;
+	int firstarg = 1;
+	int i;
+
+	for (i = 0; i < 8; i++) {
+		if (num_args >= (i + 1)) {
+			arg_type[i] = lua_type(L, (i + 1));
+		} else {
+			arg_type[i] = LUA_TNIL;
+		}
+	}
+
+	if (arg_type[0] == LUA_TNUMBER) {
+		trace_level = lua_tointeger(L, 1);
+		if (num_args == 1) {
+			/* Set a new trace level, return the current one. */
+			lua_pushinteger(L, s_lua_traceLevel);
+			s_lua_traceLevel = trace_level;
+			if (s_lua_traceFile) {
+				pthread_mutex_lock(&s_lua_traceMutex);
+				fflush(s_lua_traceFile);
+				pthread_mutex_unlock(&s_lua_traceMutex);
+			}
+			return 1;
+		}
+		firstarg = 2;
+	}
+
+	if (trace_level > s_lua_traceLevel) {
+		/* If this trace request has a higher trace level than the global trace
+		 * level, do not trace. */
+		lua_pushboolean(L, 0);
+		return 1;
+	}
+
+	/* Print to file */
+	if (s_lua_traceFile) {
+		pthread_mutex_lock(&s_lua_traceMutex);
+		for (i = firstarg; i <= num_args; i++) {
+			if (arg_type[i - 1] == LUA_TSTRING) {
+				const char *arg = lua_tostring(L, i);
+				fprintf(s_lua_traceFile, "%s\n", arg);
+			}
+		}
+		pthread_mutex_unlock(&s_lua_traceMutex);
+	}
+	lua_pushboolean(L, 1);
+	return 1;
+}
+
 
 /* UUID library and function pointer */
 union {
@@ -2232,6 +2293,7 @@ prepare_lua_environment(struct mg_context *ctx,
 	reg_function(L, "get_response_code_text", lsp_get_response_code_text);
 	reg_function(L, "random", lsp_random);
 	reg_function(L, "get_info", lsp_get_info);
+	reg_function(L, "trace", lsp_trace);
 
 	if (pf_uuid_generate.f) {
 		reg_function(L, "uuid", lsp_uuid);
@@ -2875,6 +2937,9 @@ static void *lib_handle_uuid = NULL;
 static void
 lua_init_optional_libraries(void)
 {
+	/* Create logging mutex */
+	pthread_mutex_init(&s_lua_traceMutex, &pthread_mutex_attr);
+
 	/* shared Lua state */
 	lua_shared_init();
 
@@ -2903,6 +2968,9 @@ lua_exit_optional_libraries(void)
 
 	/* shared Lua state */
 	lua_shared_exit();
+
+	/* Delete logging mutex */
+	pthread_mutex_destroy(&s_lua_traceMutex);
 }
 
 

+ 2 - 0
test/nonlatin/𞤀𞤣𞤤𞤢𞤥 𞤆𞤵𞤤𞤢𞤪.txt

@@ -0,0 +1,2 @@
+𞤀𞤣𞤤𞤢𞤥 𞤆𞤵𞤤𞤢𞤪
+https://en.wikipedia.org/wiki/Fula_language#Adlam_script

+ 70 - 37
unittest/private.c

@@ -1,4 +1,4 @@
-/* Copyright (c) 2015-2018 the Civetweb developers
+/* Copyright (c) 2015-2019 the Civetweb developers
  *
  * Permission is hereby granted, free of charge, to any person obtaining a copy
  * of this software and associated documentation files (the "Software"), to deal
@@ -19,23 +19,25 @@
  * THE SOFTWARE.
  */
 
-/**
- * We include the source file so that we have access to the internal private
- * static functions
+/**********************************************************************************/
+/*
+ * We include the civetweb.c source file so that we have access to the internal
+ * private static functions
  */
+
 #ifdef _MSC_VER
-#ifndef _CRT_SECURE_NO_WARNINGS
-#define _CRT_SECURE_NO_WARNINGS
-#endif
+/* Since the C file is included, declare all API functions as static,
+ * to avoid linker errors in the test (seems to be required for MS
+ * toolchain only).
+ */
 #define CIVETWEB_API static
 #endif
 
-#ifdef REPLACE_CHECK_FOR_LOCAL_DEBUGGING
-#undef MEMORY_DEBUGGING
-#endif
-
 #include "../src/civetweb.c"
+/**********************************************************************************/
+
 
+/* Standard includes for the test code below */
 #include <stdlib.h>
 #include <time.h>
 
@@ -93,7 +95,6 @@ START_TEST(test_parse_http_message)
 	char req12[] =
 	    "POST /a/b/c.d?e=f&g HTTP/1.1\r\nKey1: val1\r\nKey2: val2\r\n\r\nBODY";
 
-
 	int lenreq1 = (int)strlen(req1);
 	int lenreq2 = (int)strlen(req2);
 	int lenreq3 = (int)strlen(req3);
@@ -323,33 +324,66 @@ START_TEST(test_match_prefix)
 END_TEST
 
 
-START_TEST(test_remove_double_dots_and_double_slashes)
+START_TEST(test_remove_dot_segments)
 {
-	/* Adapted from unit_test.c */
-	/* Copyright (c) 2013-2015 the Civetweb developers */
-	/* Copyright (c) 2004-2013 Sergey Lyubka */
+	int i;
+
 	struct {
-		char before[20], after[20];
-	} data[] = {
-	    {"////a", "/a"},
-	    {"/.....", "/."},
-	    {"/......", "/"},
-	    {"..", "."},
-	    {"...", "."},
-	    {"/...///", "/./"},
-	    {"/a...///", "/a.../"},
-	    {"/.x", "/.x"},
-	    {"/\\", "/"},
-	    {"/a\\", "/a\\"},
-	    {"/a\\\\...", "/a\\."},
-	};
-	size_t i;
+		const char *input;
+		const char *expected_output;
+	} tests[] = {{"/path/to/file.ext", "/path/to/file.ext"},
+	             {"/file.ext", "/file.ext"},
+	             {"/path/../file.ext", "/file.ext"},
+	             {"/../to/file.ext", "/to/file.ext"},
+	             {"/../../file.ext", "/file.ext"},
+	             {"/./../file.ext", "/file.ext"},
+	             {"/.././file.ext", "/file.ext"},
+	             {"/././file.ext", "/file.ext"},
+	             {"/././file.ext", "/file.ext"},
+	             {"/path/.to/..file.ext", "/path/.to/..file.ext"},
+	             {"/file", "/file"},
+	             {"/path/", "/path/"},
+
+	             {"file.ext", "file.ext"},
+	             {"./file.ext", "file.ext"},
+	             {"../file.ext", "file.ext"},
+	             {".file.ext", ".file.ext"},
+	             {"..file.ext", "..file.ext"},
+	             {"file", "file"},
+	             {"/x/../", "/"},
+	             {"/x/../../", "/"},
+	             {"/x/.././", "/"},
+	             {"/./x/.././", "/"},
+
+	             /* Windows specific */
+	             {"\\file.ext", "/file.ext"},
+	             {"\\..\\file.ext", "/file.ext"},
+	             {"/file.", "/file"},
+	             {"/path\\to.\\.\\file.", "/path/to/file"},
+
+	             /* Multiple dots and slashes */
+	             {"\\//\\\\x", "/x"},
+	             {"//", "/"},
+	             {"/./", "/"},
+	             {"/../", "/"},
+	             {"/.../", "/"},
+	             {"/..../", "/"},
+	             {"/...../", "/"},
+	             {"/...../", "/"},
+	             {"/...//", "/"},
+	             {"/..././", "/"},
+	             {"/.../../", "/"},
+	             {"/.../.../", "/"},
+
+	             {NULL, NULL}};
 
 	mark_point();
 
-	for (i = 0; i < ARRAY_SIZE(data); i++) {
-		remove_double_dots_and_double_slashes(data[i].before);
-		ck_assert_str_eq(data[i].before, data[i].after);
+	for (i = 0; (tests[i].input != NULL); i++) {
+		char inout[256];
+		strcpy(inout, tests[i].input);
+		remove_dot_segments(inout);
+		ck_assert_str_eq(inout, tests[i].expected_output);
 	}
 }
 END_TEST
@@ -1106,8 +1140,7 @@ make_private_suite(void)
 	tcase_set_timeout(tcase_url_parsing_1, civetweb_min_test_timeout);
 	suite_add_tcase(suite, tcase_url_parsing_1);
 
-	tcase_add_test(tcase_url_parsing_2,
-	               test_remove_double_dots_and_double_slashes);
+	tcase_add_test(tcase_url_parsing_2, test_remove_dot_segments);
 	tcase_set_timeout(tcase_url_parsing_2, civetweb_min_test_timeout);
 	suite_add_tcase(suite, tcase_url_parsing_2);
 
@@ -1178,7 +1211,7 @@ MAIN_PRIVATE(void)
 
 	test_alloc_vprintf(0);
 	test_mg_vsnprintf(0);
-	test_remove_double_dots_and_double_slashes(0);
+	test_remove_dot_segments(0);
 	test_parse_date_string(0);
 	test_parse_port_string(0);
 	test_parse_http_message(0);

+ 17 - 9
unittest/public_server.c

@@ -151,8 +151,6 @@ START_TEST(test_the_test_environment)
 	char wd[300];
 	char buf[500];
 	FILE *f;
-	struct stat st;
-	int ret;
 	const char *ssl_cert = locate_ssl_cert();
 
 	memset(wd, 0, sizeof(wd));
@@ -832,16 +830,18 @@ request_test_handler(struct mg_connection *conn, void *cbdata)
 	char chunk_data[32];
 	const struct mg_request_info *ri;
 	struct mg_context *ctx;
-	void *ud, *cud;
+	void *ud, *cud, *ud2;
 	void *dummy = malloc(1);
 
 	ctx = mg_get_context(conn);
 	ud = mg_get_user_data(ctx);
+	ud2 = mg_get_user_context_data(conn);
 	ri = mg_get_request_info(conn);
 
 	ck_assert(ri != NULL);
 	ck_assert(ctx == g_ctx);
 	ck_assert(ud == &g_ctx);
+	ck_assert(ud == ud2);
 
 	ck_assert(dummy != NULL);
 
@@ -884,15 +884,17 @@ request_test_handler2(struct mg_connection *conn, void *cbdata)
 	const char *chunk_data = "123456789A123456789B123456789C";
 	const struct mg_request_info *ri;
 	struct mg_context *ctx;
-	void *ud;
+	void *ud, *ud2;
 
 	ctx = mg_get_context(conn);
 	ud = mg_get_user_data(ctx);
+	ud2 = mg_get_user_context_data(conn);
 	ri = mg_get_request_info(conn);
 
 	ck_assert(ri != NULL);
 	ck_assert(ctx == g_ctx);
 	ck_assert(ud == &g_ctx);
+	ck_assert(ud == ud2);
 
 	mg_send_http_ok(conn, "text/plain", -1);
 
@@ -4652,7 +4654,7 @@ minimal_http_https_client_impl(const char *server,
 		             client_err_buf);
 	}
 
-	mg_printf(client, "GET /%s HTTP/1.0\r\n\r\n", uri);
+	mg_printf(client, "GET %s HTTP/1.0\r\n\r\n", uri);
 
 	r = mg_get_response(client, client_err_buf, sizeof(client_err_buf), 10000);
 
@@ -4669,8 +4671,14 @@ minimal_http_https_client_impl(const char *server,
 	ck_assert(client_ri != NULL);
 
 	/* Check for status code 200 OK or 30? moved */
-	if ((client_ri->status_code < 300) || (client_ri->status_code > 308)) {
-		ck_assert_int_eq(client_ri->status_code, 200);
+	if ((client_ri->status_code != 200)
+	    && (client_ri->status_code / 10 != 30)) {
+		ck_abort_msg("Request to %s://%s:%u/%s: Status %u",
+		             use_ssl ? "HTTPS" : "HTTP",
+		             server,
+		             port,
+		             uri,
+		             client_ri->status_code);
 	}
 
 	data_read = 0;
@@ -4721,7 +4729,7 @@ START_TEST(test_minimal_client)
 	/* Call a test client */
 	minimal_http_client_impl("192.30.253.113" /* www.github.com */,
 	                         80,
-	                         "civetweb/civetweb/");
+	                         "/civetweb/civetweb/");
 
 	mark_point();
 
@@ -4750,7 +4758,7 @@ START_TEST(test_minimal_tls_client)
 	/* Call a test client */
 	minimal_https_client_impl("192.30.253.113" /* www.github.com */,
 	                          443,
-	                          "civetweb/civetweb/");
+	                          "/civetweb/civetweb/");
 
 	mark_point();
 

Некоторые файлы не были показаны из-за большого количества измененных файлов