Przeglądaj źródła

Merge branch 'master' into http2

bel2125 4 lat temu
rodzic
commit
7dc4261acb

+ 36 - 6
.travis.yml

@@ -50,7 +50,7 @@ before_script:
   - uname -a
   - if [ "${TRAVIS_OS_NAME}" == "linux" ]; then
       lsb_release -a;
-      cat /etc/network/interfaces;
+      cat /etc/network/interfaces || true;
     fi
   - ifconfig
   - pwd
@@ -63,7 +63,7 @@ before_script:
   # Generate the build scripts with CMake
   - mkdir output
   - openssl version
-  - gcc --version 
+  - gcc --version
   - cmake --version
   - gcc unittest/cgi_test.c -o output/cgi_test.cgi
   - cd output
@@ -77,6 +77,7 @@ before_script:
     -DCIVETWEB_DISABLE_CGI=${NO_CGI}
     -DCIVETWEB_SERVE_NO_FILES=${NO_FILES}
     -DCIVETWEB_ENABLE_SSL_DYNAMIC_LOADING=${ENABLE_SSL_DYNAMIC_LOADING}
+    -DCIVETWEB_SSL_OPENSSL_API_1_0=${OPENSSL_1_0}
     -DCIVETWEB_SSL_OPENSSL_API_1_1=${OPENSSL_1_1}
     -DCIVETWEB_ENABLE_WEBSOCKETS=${ENABLE_WEBSOCKETS}
     -DCIVETWEB_ENABLE_CXX=${ENABLE_CXX}
@@ -182,6 +183,7 @@ matrix:
       MATRIX_EVAL="CC=clang-3.8 && CXX=clang++-3.8"
       BUILD_TYPE=Debug
       ENABLE_SSL_DYNAMIC_LOADING=YES
+      OPENSSL_1_0=NO
       OPENSSL_1_1=NO
       ENABLE_CXX=NO
       ENABLE_LUA_SHARED=NO
@@ -216,6 +218,7 @@ matrix:
       MATRIX_EVAL="CC=clang-3.8 && CXX=clang++-3.8"
       BUILD_TYPE=Release
       ENABLE_SSL_DYNAMIC_LOADING=YES
+      OPENSSL_1_0=YES
       OPENSSL_1_1=NO
       ENABLE_CXX=NO
       ENABLE_LUA_SHARED=NO
@@ -251,6 +254,7 @@ matrix:
       MATRIX_EVAL="CC=clang-3.8 && CXX=clang++-3.8"
       BUILD_TYPE=Release
       ENABLE_SSL_DYNAMIC_LOADING=YES
+      OPENSSL_1_0=YES
       OPENSSL_1_1=NO
       ENABLE_CXX=NO
       ENABLE_LUA_SHARED=NO
@@ -285,6 +289,7 @@ matrix:
       MATRIX_EVAL="CC=clang-3.8 && CXX=clang++-3.8"
       BUILD_TYPE=Release
       ENABLE_SSL_DYNAMIC_LOADING=YES
+      OPENSSL_1_0=YES
       OPENSSL_1_1=NO
       ENABLE_CXX=NO
       ENABLE_LUA_SHARED=NO
@@ -319,6 +324,7 @@ matrix:
       MATRIX_EVAL="CC=gcc-5 && CXX=g++-5"
       BUILD_TYPE=Release
       ENABLE_SSL_DYNAMIC_LOADING=YES
+      OPENSSL_1_0=YES
       OPENSSL_1_1=NO
       ENABLE_CXX=NO
       ENABLE_LUA_SHARED=NO
@@ -344,6 +350,7 @@ matrix:
       N=GCCAnyVersion-Linux-Coverage
       BUILD_TYPE=Coverage
       ENABLE_SSL_DYNAMIC_LOADING=YES
+      OPENSSL_1_0=YES
       OPENSSL_1_1=NO
       ENABLE_CXX=NO
       ENABLE_LUA_SHARED=NO
@@ -369,6 +376,7 @@ matrix:
       N=Clang-OSX-Complete-NoLua-Release
       BUILD_TYPE=Release
       ENABLE_SSL_DYNAMIC_LOADING=YES
+      OPENSSL_1_0=YES
       OPENSSL_1_1=NO
       ENABLE_CXX=NO
       ENABLE_LUA_SHARED=NO
@@ -395,6 +403,7 @@ matrix:
       N=Clang-OSX-Complete-NoLua-Release-OpenSSL_1_1_NoDynLoad
       BUILD_TYPE=OSX_OPENSSL_1_1
       ENABLE_SSL_DYNAMIC_LOADING=NO
+      OPENSSL_1_0=NO
       OPENSSL_1_1=YES
       ENABLE_CXX=NO
       ENABLE_LUA_SHARED=NO
@@ -434,6 +443,7 @@ matrix:
       N=Clang50-Linux-Default-Shared
       BUILD_TYPE=Debug
       ENABLE_SSL_DYNAMIC_LOADING=YES
+      OPENSSL_1_0=YES
       OPENSSL_1_1=NO
       ENABLE_CXX=NO
       ENABLE_LUA_SHARED=NO
@@ -462,6 +472,7 @@ matrix:
       N=Precise-Clang-Linux-Default
       BUILD_TYPE=Debug
       ENABLE_SSL_DYNAMIC_LOADING=YES
+      OPENSSL_1_0=YES
       OPENSSL_1_1=NO
       ENABLE_CXX=NO
       ENABLE_LUA_SHARED=NO
@@ -488,6 +499,7 @@ matrix:
       N=OSX-Package
       BUILD_TYPE=Release
       ENABLE_SSL_DYNAMIC_LOADING=YES
+      OPENSSL_1_0=YES
       OPENSSL_1_1=NO
       ENABLE_CXX=NO
       ENABLE_LUA_SHARED=NO
@@ -523,6 +535,7 @@ matrix:
       ARCH=x86
       BUILD_TYPE=Release
       ENABLE_SSL_DYNAMIC_LOADING=YES
+      OPENSSL_1_0=YES
       OPENSSL_1_1=NO
       ENABLE_CXX=NO
       ENABLE_LUA_SHARED=NO
@@ -555,6 +568,7 @@ matrix:
       N=NoSslDynamicLoading
       BUILD_TYPE=Release
       ENABLE_SSL_DYNAMIC_LOADING=NO
+      OPENSSL_1_0=YES
       OPENSSL_1_1=NO
       ENABLE_CXX=NO
       C_STANDARD=auto
@@ -580,6 +594,7 @@ matrix:
       N=GCCLinuxDefault_Debug
       BUILD_TYPE=Debug
       ENABLE_SSL_DYNAMIC_LOADING=YES
+      OPENSSL_1_0=YES
       OPENSSL_1_1=NO
       ENABLE_CXX=NO
       ENABLE_LUA_SHARED=NO
@@ -604,6 +619,7 @@ matrix:
       N=GCCLinuxDefault_RelWithDebInfo
       BUILD_TYPE=RelWithDebInfo
       ENABLE_SSL_DYNAMIC_LOADING=YES
+      OPENSSL_1_0=YES
       OPENSSL_1_1=NO
       ENABLE_CXX=NO
       ENABLE_LUA_SHARED=NO
@@ -628,6 +644,7 @@ matrix:
       N=GCCLinuxDefault_MinSizeRel
       BUILD_TYPE=MinSizeRel
       ENABLE_SSL_DYNAMIC_LOADING=YES
+      OPENSSL_1_0=YES
       OPENSSL_1_1=NO
       ENABLE_CXX=NO
       ENABLE_LUA_SHARED=NO
@@ -652,6 +669,7 @@ matrix:
       N=GCCLinuxDefault_None
       BUILD_TYPE=None
       ENABLE_SSL_DYNAMIC_LOADING=YES
+      OPENSSL_1_0=YES
       OPENSSL_1_1=NO
       ENABLE_CXX=NO
       ENABLE_LUA_SHARED=NO
@@ -677,6 +695,7 @@ matrix:
       MATRIX_EVAL="CC=clang-3.8 && CXX=clang++-3.8"
       BUILD_TYPE=Debug
       ENABLE_SSL_DYNAMIC_LOADING=YES
+      OPENSSL_1_0=YES
       OPENSSL_1_1=NO
       ENABLE_CXX=NO
       ENABLE_LUA_SHARED=YES
@@ -711,6 +730,7 @@ matrix:
       MATRIX_EVAL="CC=clang-5.0 && CXX=clang++-5.0"
       BUILD_TYPE=Release
       ENABLE_SSL_DYNAMIC_LOADING=YES
+      OPENSSL_1_0=YES
       OPENSSL_1_1=NO
       ENABLE_CXX=YES
       ENABLE_LUA_SHARED=YES
@@ -737,6 +757,7 @@ matrix:
       N=GCCLinuxDefault_xenial
       BUILD_TYPE=Release
       ENABLE_SSL_DYNAMIC_LOADING=YES
+      OPENSSL_1_0=YES
       OPENSSL_1_1=NO
       ENABLE_CXX=NO
       ENABLE_LUA_SHARED=NO
@@ -762,7 +783,8 @@ matrix:
       N=GCCLinuxDefault_bionic
       BUILD_TYPE=Release
       ENABLE_SSL_DYNAMIC_LOADING=YES
-      OPENSSL_1_1=NO
+      OPENSSL_1_0=NO
+      OPENSSL_1_1=YES
       ENABLE_CXX=NO
       ENABLE_LUA_SHARED=NO
       C_STANDARD=auto
@@ -787,7 +809,8 @@ matrix:
       N=GCCLinuxDefault_eoan
       BUILD_TYPE=Release
       ENABLE_SSL_DYNAMIC_LOADING=YES
-      OPENSSL_1_1=NO
+      OPENSSL_1_0=NO
+      OPENSSL_1_1=YES
       ENABLE_CXX=NO
       ENABLE_LUA_SHARED=NO
       C_STANDARD=auto
@@ -807,12 +830,17 @@ matrix:
     os: linux
     compiler: gcc
     dist: focal
+    addons:
+      apt:
+        packages:
+          - lsb-core
     env:
       idx=23
       N=GCCLinuxDefault_focal
       BUILD_TYPE=Release
       ENABLE_SSL_DYNAMIC_LOADING=YES
-      OPENSSL_1_1=NO
+      OPENSSL_1_0=NO
+      OPENSSL_1_1=YES
       ENABLE_CXX=NO
       ENABLE_LUA_SHARED=NO
       C_STANDARD=auto
@@ -879,7 +907,8 @@ matrix:
 #      MATRIX_EVAL="CC=clang-3.8 && CXX=clang++-3.8"
 #      BUILD_TYPE=Debug
 #      ENABLE_SSL_DYNAMIC_LOADING=YES
-#      OPENSSL_1_1=NO
+#      OPENSSL_1_0=NO
+#      OPENSSL_1_1=YES
 #      ENABLE_CXX=NO
 #      ENABLE_LUA_SHARED=YES
 #      C_STANDARD=auto
@@ -892,6 +921,7 @@ matrix:
 #      ENABLE_WEBSOCKETS=YES
 #      ENABLE_SERVER_STATS=YES
 #      ENABLE_LUA=YES
+#      ENABLE_LUA_SHARED=YES
 #      ENABLE_DUKTAPE=NO
 #      NO_CACHING=YES
 #      ALLOW_WARNINGS=YES

+ 13 - 1
CMakeLists.txt

@@ -219,8 +219,12 @@ message(STATUS "Duktape CGI support - ${CIVETWEB_ENABLE_DUKTAPE}")
 option(CIVETWEB_ENABLE_SSL "Enables the secure socket layer" ON)
 message(STATUS "SSL support - ${CIVETWEB_ENABLE_SSL}")
 
+# OpenSSL 1.0 API
+option(CIVETWEB_SSL_OPENSSL_API_1_0 "Use the OpenSSL 1.0 API" OFF)
+message(STATUS "Compile for OpenSSL 1.0 API - ${CIVETWEB_SSL_OPENSSL_API_1_0}")
+
 # OpenSSL 1.1 API
-option(CIVETWEB_SSL_OPENSSL_API_1_1 "Use the OpenSSL 1.1 API" OFF)
+option(CIVETWEB_SSL_OPENSSL_API_1_1 "Use the OpenSSL 1.1 API" ON)
 message(STATUS "Compile for OpenSSL 1.1 API - ${CIVETWEB_SSL_OPENSSL_API_1_1}")
 
 # Dynamically load or link the SSL libraries
@@ -519,9 +523,17 @@ else()
     add_definitions(-DCRYPTO_LIB="${CIVETWEB_SSL_CRYPTO_LIB}")
   endif()
 endif()
+
+if(CIVETWEB_SSL_OPENSSL_API_1_0)
+  add_definitions(-DOPENSSL_API_1_0)
+endif()
 if(CIVETWEB_SSL_OPENSSL_API_1_1)
   add_definitions(-DOPENSSL_API_1_1)
 endif()
+if(CIVETWEB_SSL_OPENSSL_API_1_0 AND CIVETWEB_SSL_OPENSSL_API_1_1)
+  message(FATAL_ERROR "Multiple SSL API versions defined")
+endif()
+
 add_definitions(-DUSE_STACK_SIZE=${CIVETWEB_THREAD_STACK_SIZE})
 
 # Set 32 or 64 bit environment

+ 11 - 4
Makefile

@@ -67,6 +67,10 @@ endif
 # only set main compile options if none were chosen
 CFLAGS += -Wall -Wextra -Wshadow -Wformat-security -Winit-self -Wmissing-prototypes -D$(TARGET_OS) -Iinclude $(COPT) -DUSE_STACK_SIZE=$(USE_STACK_SIZE)
 
+ifdef WITH_CFLAGS
+  CFLAGS += $(WITH_CFLAGS)
+endif
+
 LIBS = -lpthread -lm
 
 ifdef WITH_DEBUG
@@ -81,7 +85,7 @@ else ifdef TEST_FUZZ
   CXX = clang++
   BUILD_DIRS += $(BUILD_DIR)/fuzztest
   APP_SOURCES = fuzztest/fuzzmain.c
-  OBJECTS = $(LIB_SOURCES:.c=.o) $(APP_SOURCES:.c=.o) 
+  OBJECTS = $(LIB_SOURCES:.c=.o) $(APP_SOURCES:.c=.o)
   CFLAGS += -DTEST_FUZZ$(TEST_FUZZ)
 else
   CFLAGS += -O2 -DNDEBUG
@@ -89,6 +93,9 @@ endif
 
 ifdef NO_SSL
   CFLAGS += -DNO_SSL
+else
+  #Use OpenSSL 1.1 API version as default
+  CFLAGS += -DOPENSSL_API_1_1
 endif
 ifdef NO_CGI
   CFLAGS += -DNO_CGI
@@ -325,9 +332,9 @@ install-lib: lib$(CPROG).a
 install-slib: lib$(CPROG).so
 	$(eval version=$(shell grep -w "define CIVETWEB_VERSION" include/civetweb.h | sed 's|.*VERSION "\(.*\)"|\1|g'))
 	$(eval major=$(shell echo $(version) | cut -d'.' -f1))
-	install -m 644 $< "$(LIBDIR)"
-	install -m 777 $<.$(major) "$(LIBDIR)"
-	install -m 777 $<.$(version).0 "$(LIBDIR)"
+	install -m 755 $<.$(version).0 "$(LIBDIR)"
+	cd "$(LIBDIR)" && ln -sfv $<.$(version).0 $<.$(major)
+	cd "$(LIBDIR)" && ln -sfv $<.$(version).0 $<
 
 # Install target we do not want to overwrite
 # as it may be an upgrade

+ 15 - 0
SECURITY.md

@@ -0,0 +1,15 @@
+# Security Policy
+
+## Supported Versions
+
+The current "head" version undergoes some automatic tests, but may have defects and vulnerabilities of any kind.
+Also development branches may be in an intermediate, untested state.
+
+For released versions, manual tests are performed in addition to automatic tests and static source code analysis.
+
+Defects will be fixed in the current head version. 
+Selected, critical defects are fixed in the latest release as well.
+
+## Reporting a Vulnerability
+
+Please send vulnerability reports by email to bel2125 at gmail com.

+ 4 - 3
VisualStudio/civetweb_lua/civetweb_lua.vcxproj

@@ -102,7 +102,7 @@
       </PrecompiledHeader>
       <WarningLevel>Level3</WarningLevel>
       <Optimization>Disabled</Optimization>
-      <PreprocessorDefinitions>USE_HTTP2;MG_EXPERIMENTAL_INTERFACES;USE_SERVER_STATS;USE_DUKTAPE;USE_IPV6;LUA_COMPAT_ALL;USE_LUA;USE_LUA_SQLITE3;USE_LUA_FILE_SYSTEM;USE_LUA_LUAXML;USE_WEBSOCKET;WIN32;_DEBUG;_WINDOWS;_CRT_SECURE_NO_DEPRECATE;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <PreprocessorDefinitions>STOP_FLAG_NEEDS_LOCK;OPENSSL_API_1_0;MG_EXPERIMENTAL_INTERFACES;USE_SERVER_STATS;USE_DUKTAPE;USE_IPV6;LUA_COMPAT_ALL;USE_LUA;USE_LUA_SQLITE3;USE_LUA_FILE_SYSTEM;USE_LUA_LUAXML;USE_WEBSOCKET;WIN32;_DEBUG;_WINDOWS;_CRT_SECURE_NO_DEPRECATE;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
       <AdditionalIncludeDirectories>$(ProjectDir)..\..\include;$(ProjectDir)..\..\src\third_party;$(ProjectDir)..\..\src\third_party\lua-5.2.4\src;$(ProjectDir)..\..\src\third_party\duktape-1.5.2\src;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
     </ClCompile>
     <Link>
@@ -149,7 +149,7 @@
       <Optimization>MaxSpeed</Optimization>
       <FunctionLevelLinking>true</FunctionLevelLinking>
       <IntrinsicFunctions>true</IntrinsicFunctions>
-      <PreprocessorDefinitions>MG_EXPERIMENTAL_INTERFACES;USE_SERVER_STATS;USE_DUKTAPE;USE_IPV6;LUA_COMPAT_ALL;USE_LUA;USE_LUA_SQLITE3;USE_LUA_FILE_SYSTEM;USE_LUA_LUAXML;USE_WEBSOCKET;WIN32;_WINDOWS;_CRT_SECURE_NO_DEPRECATE;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <PreprocessorDefinitions>OPENSSL_API_1_0;MG_EXPERIMENTAL_INTERFACES;USE_SERVER_STATS;USE_DUKTAPE;USE_IPV6;LUA_COMPAT_ALL;USE_LUA;USE_LUA_SQLITE3;USE_LUA_FILE_SYSTEM;USE_LUA_LUAXML;USE_WEBSOCKET;WIN32;_WINDOWS;_CRT_SECURE_NO_DEPRECATE;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
       <AdditionalIncludeDirectories>$(ProjectDir)..\..\include;$(ProjectDir)..\..\src\third_party;$(ProjectDir)..\..\src\third_party\lua-5.2.4\src;$(ProjectDir)..\..\src\third_party\duktape-1.5.2\src;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
     </ClCompile>
     <Link>
@@ -210,6 +210,7 @@
     <None Include="..\..\src\mod_http2.inl" />
     <None Include="..\..\src\mod_lua_shared.inl" />
     <None Include="..\..\src\response.inl" />
+    <None Include="..\..\src\mod_zlib.inl" />
     <None Include="..\..\src\sha1.inl" />
     <None Include="..\..\src\mod_duktape.inl" />
     <None Include="..\..\src\mod_lua.inl" />
@@ -218,4 +219,4 @@
   <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
   <ImportGroup Label="ExtensionTargets">
   </ImportGroup>
-</Project>
+</Project>

+ 2 - 1
VisualStudio/civetweb_lua/civetweb_lua.vcxproj.filters

@@ -75,7 +75,8 @@
       <Filter>inl files</Filter>
     </None>
     <None Include="..\..\src\response.inl">
+    <None Include="..\..\src\mod_zlib.inl">
       <Filter>inl files</Filter>
     </None>
   </ItemGroup>
-</Project>
+</Project>

+ 3 - 3
VisualStudio/unit_test/unit_test.vcxproj

@@ -26,7 +26,7 @@
     <ClCompile Include="..\..\unittest\private_exe.c" />
     <ClCompile Include="..\..\unittest\public_func.c" />
     <ClCompile Include="..\..\unittest\public_server.c" />
-    <ClInclude Include="..\..\unittest\timertest.c" />
+    <ClCompile Include="..\..\unittest\timertest.c" />
     <ClCompile Include="..\..\unittest\shared.c" />
   </ItemGroup>
   <PropertyGroup Label="Globals">
@@ -73,7 +73,7 @@
       </PrecompiledHeader>
       <WarningLevel>Level3</WarningLevel>
       <Optimization>Disabled</Optimization>
-      <PreprocessorDefinitions>MAIN_PRIVATE=main;LOCAL_TEST;REPLACE_CHECK_FOR_LOCAL_DEBUGGING;LOCAL_TEST;USE_IPV6;USE_WEBSOCKET;MEMORY_DEBUGGING;WIN32;_DEBUG;_CONSOLE;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <PreprocessorDefinitions>MAIN_TIMER_PRIVATE=main;LOCAL_TEST;REPLACE_CHECK_FOR_LOCAL_DEBUGGING;LOCAL_TEST;USE_IPV6;USE_WEBSOCKET;MEMORY_DEBUGGING;WIN32;_DEBUG;_CONSOLE;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
       <AdditionalIncludeDirectories>$(ProjectDir)..\..\src;$(ProjectDir)..\..\include;$(ProjectDir)..\..\src\third_party\lua-5.2.4\src;$(ProjectDir)..\..\..\check-0.10.0\;$(ProjectDir)..\..\..\check-0.10.0\src\;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
     </ClCompile>
     <Link>
@@ -102,4 +102,4 @@
   <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
   <ImportGroup Label="ExtensionTargets">
   </ImportGroup>
-</Project>
+</Project>

+ 3 - 3
VisualStudio/unit_test/unit_test.vcxproj.filters

@@ -39,9 +39,6 @@
     <ClInclude Include="..\..\unittest\civetweb_check.h">
       <Filter>Headerdateien</Filter>
     </ClInclude>
-    <ClInclude Include="..\..\unittest\timertest.c">
-      <Filter>Quelldateien</Filter>
-    </ClInclude>
   </ItemGroup>
   <ItemGroup>
     <ClCompile Include="..\..\unittest\public_server.c">
@@ -62,5 +59,8 @@
     <ClCompile Include="..\..\src\civetweb.c">
       <Filter>Quelldateien</Filter>
     </ClCompile>
+    <ClCompile Include="..\..\unittest\timertest.c">
+      <Filter>Quelldateien</Filter>
+    </ClCompile>
   </ItemGroup>
 </Project>

+ 2 - 0
appveyor.yml

@@ -438,6 +438,8 @@ before_build:
     -DCIVETWEB_DISABLE_CACHING=%no_caching%
     -DCIVETWEB_C_STANDARD=%c_standard%
     -DCIVETWEB_CXX_STANDARD=%cxx_standard%
+    -DCIVETWEB_SSL_OPENSSL_API_1_0=NO
+    -DCIVETWEB_SSL_OPENSSL_API_1_1=YES
     "%source_path%"
   - powershell Push-AppveyorArtifact CMakeCache.txt
   - cd "%source_path%"

+ 4 - 2
docs/api/mg_callbacks.md

@@ -18,8 +18,10 @@
 | |The callback function `http_error()` is called by CivetWeb just before an HTTP error is to be sent to the client. The function allows the application to send a custom error page. The status code of the error is provided as a parameter. If the application sends their own error page, it must return 0 to signal CivetWeb that no further processing is needed. If the returned value is not 0, CivetWeb will send an error page to the client.|
 |**`init_context`**|**`void (*init_context)( const struct mg_context *ctx );`**|
 | |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.|
+|**`init_lua`**|**`void (*init_lua)( const struct mg_connection *conn, void *lua_context, unsigned context_flags );`**|
+| |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. The parameter `context_flags` indicate the type of Lua environment. |
+|**`exit_lua`**|**`void (*init_lua)( const struct mg_connection *conn, void *lua_context, unsigned context_flags );`**|
+| |The callback function `exit_lua()` is called when a Lua state is about to be closed. Lua page serving must have been enabled at compile time for this callback function to be called. The parameters are identical to `lua_init()`.|
 |**`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 );`**|

+ 3 - 1
examples/embedded_c/Makefile

@@ -23,7 +23,9 @@ include $(TOP)/resources/Makefile.in-os
 ifeq ($(TARGET_OS),LINUX)
 	LIBS += -ldl -lrt
 endif
-
+ifdef WITH_ZLIB
+	LIBS += -lz
+endif
 
 all: $(PROG)
 

+ 3 - 0
examples/embedded_cpp/Makefile

@@ -20,6 +20,9 @@ include $(TOP)/resources/Makefile.in-os
 ifeq ($(TARGET_OS),LINUX) 
 	LIBS += -ldl -lrt
 endif
+ifdef WITH_ZLIB
+	LIBS += -lz
+endif
 
 all: $(PROG)
 

+ 13 - 4
include/civetweb.h

@@ -339,12 +339,21 @@ struct mg_callbacks {
 	*/
 	void (*connection_close)(const struct mg_connection *);
 
-	/* Called when civetweb is about to serve Lua server page, if
-	   Lua support is enabled.
+	/* init_lua is called when civetweb is about to serve Lua server page.
+	   exit_lua is called when the Lua processing is complete.
+	   Both will work only if Lua support is enabled.
 	   Parameters:
 	     conn: current connection.
-	     lua_context: "lua_State *" pointer. */
-	void (*init_lua)(const struct mg_connection *conn, void *lua_context);
+	     lua_context: "lua_State *" pointer.
+	     context_flags: context type information as bitmask:
+	       context_flags & 0x0F: (0-15) Lua environment type
+	*/
+	void (*init_lua)(const struct mg_connection *conn,
+	                 void *lua_context,
+	                 unsigned context_flags);
+	void (*exit_lua)(const struct mg_connection *conn,
+	                 void *lua_context,
+	                 unsigned context_flags);
 
 #if defined(MG_LEGACY_INTERFACE) /* 2016-05-14 */
 	/* Called when civetweb has uploaded a file to a temporary directory as a

Plik diff jest za duży
+ 222 - 357
src/civetweb.c


+ 319 - 78
src/mod_lua.inl

@@ -4,10 +4,13 @@
 
 #if !defined(_WIN32)
 #include <dlfcn.h>
+#include <sys/mman.h>
 #endif
+
 #include "civetweb_lua.h"
 #include "civetweb_private_lua.h"
 
+
 #if defined(_WIN32)
 static void *
 mmap(void *addr, int64_t len, int prot, int flags, int fd, int offset)
@@ -32,6 +35,7 @@ mmap(void *addr, int64_t len, int prot, int flags, int fd, int offset)
 	return p;
 }
 
+
 static void
 munmap(void *addr, int64_t length)
 {
@@ -41,19 +45,21 @@ munmap(void *addr, int64_t length)
 	UnmapViewOfFile(addr);
 }
 
-#define MAP_FAILED (NULL)
+
 #define MAP_PRIVATE (0)
 #define PROT_READ (0)
-#else
-#include <sys/mman.h>
 #endif
 
+
 static const char *const LUASOCKET = "luasocket";
 static const char lua_regkey_ctx = 1;
 static const char lua_regkey_connlist = 2;
 static const char lua_regkey_lsp_include_history = 3;
+static const char lua_regkey_environment_type = 4;
+static const char lua_regkey_dtor = 5;
 static const char *const LUABACKGROUNDPARAMS = "mg";
 
+
 /* Limit nesting depth of mg.include.
  * This takes a lot of stack (~10 kB per recursion),
  * so do not use a too high limit. */
@@ -82,6 +88,7 @@ reg_lstring(struct lua_State *L,
 	}
 }
 
+
 static void
 reg_llstring(struct lua_State *L,
              const void *buffer1,
@@ -96,9 +103,11 @@ reg_llstring(struct lua_State *L,
 	}
 }
 
+
 #define reg_string(L, name, val)                                               \
 	reg_lstring(L, name, val, val ? strlen(val) : 0)
 
+
 static void
 reg_int(struct lua_State *L, const char *name, int val)
 {
@@ -109,6 +118,7 @@ reg_int(struct lua_State *L, const char *name, int val)
 	}
 }
 
+
 static void
 reg_boolean(struct lua_State *L, const char *name, int val)
 {
@@ -119,6 +129,7 @@ reg_boolean(struct lua_State *L, const char *name, int val)
 	}
 }
 
+
 static void
 reg_conn_function(struct lua_State *L,
                   const char *name,
@@ -133,6 +144,7 @@ reg_conn_function(struct lua_State *L,
 	}
 }
 
+
 static void
 reg_function(struct lua_State *L, const char *name, lua_CFunction func)
 {
@@ -143,6 +155,7 @@ reg_function(struct lua_State *L, const char *name, lua_CFunction func)
 	}
 }
 
+
 static void
 lua_cry(struct mg_connection *conn,
         int err,
@@ -200,6 +213,7 @@ lua_cry(struct mg_connection *conn,
 	}
 }
 
+
 static int
 lsp_sock_close(lua_State *L)
 {
@@ -221,6 +235,7 @@ lsp_sock_close(lua_State *L)
 	return 0;
 }
 
+
 static int
 lsp_sock_recv(lua_State *L)
 {
@@ -248,6 +263,7 @@ lsp_sock_recv(lua_State *L)
 	return 1;
 }
 
+
 static int
 lsp_sock_send(lua_State *L)
 {
@@ -279,6 +295,7 @@ lsp_sock_send(lua_State *L)
 	return 1;
 }
 
+
 static int
 lsp_sock_gc(lua_State *L)
 {
@@ -301,6 +318,7 @@ lsp_sock_gc(lua_State *L)
 	return 0;
 }
 
+
 /* Methods and meta-methods supported by the object returned by connect.
  * For meta-methods, see http://lua-users.org/wiki/MetatableEvents */
 static const struct luaL_Reg luasocket_methods[] = {{"close", lsp_sock_close},
@@ -309,6 +327,7 @@ static const struct luaL_Reg luasocket_methods[] = {{"close", lsp_sock_close},
                                                     {"__gc", lsp_sock_gc},
                                                     {NULL, NULL}};
 
+
 static int
 lsp_connect(lua_State *L)
 {
@@ -345,6 +364,7 @@ lsp_connect(lua_State *L)
 	return 1;
 }
 
+
 static int
 lsp_error(lua_State *L)
 {
@@ -356,6 +376,7 @@ lsp_error(lua_State *L)
 	return 0;
 }
 
+
 /* Silently stop processing chunks. */
 static void
 lsp_abort(lua_State *L)
@@ -1732,6 +1753,7 @@ 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;
@@ -1964,70 +1986,111 @@ lwebsock_write(lua_State *L)
 }
 
 
-struct laction_arg {
-	lua_State *state;
+struct laction_string_arg {
+	lua_State *L;
 	const char *script;
 	pthread_mutex_t *pmutex;
 	char txt[1];
 };
 
+struct laction_funcref_arg {
+	lua_State *L;
+	const char *script;
+	pthread_mutex_t *pmutex;
+	int funcref;
+};
+
 
 static int
-lua_action(struct laction_arg *arg)
+lua_action_string(struct laction_string_arg *arg)
 {
 	int err, ok;
 	struct mg_context *ctx;
 
 	(void)pthread_mutex_lock(arg->pmutex);
 
-	lua_pushlightuserdata(arg->state, (void *)&lua_regkey_ctx);
-	lua_gettable(arg->state, LUA_REGISTRYINDEX);
-	ctx = (struct mg_context *)lua_touserdata(arg->state, -1);
-	lua_pop(arg->state, 1);
+	lua_pushlightuserdata(arg->L, (void *)&lua_regkey_ctx);
+	lua_gettable(arg->L, LUA_REGISTRYINDEX);
+	ctx = (struct mg_context *)lua_touserdata(arg->L, -1);
+	lua_pop(arg->L, 1);
 
-	err = luaL_loadstring(arg->state, arg->txt);
+	err = luaL_loadstring(arg->L, arg->txt);
 	if (err != 0) {
 		struct mg_connection fc;
-		lua_cry(
-		    fake_connection(&fc, ctx), err, arg->state, arg->script, "timer");
+		lua_cry(fake_connection(&fc, ctx), err, arg->L, arg->script, "timer");
 		(void)pthread_mutex_unlock(arg->pmutex);
-		mg_free(arg);
 		return 0;
 	}
-	err = lua_pcall(arg->state, 0, 1, 0);
+	err = lua_pcall(arg->L, 0, 1, 0);
 	if (err != 0) {
 		struct mg_connection fc;
-		lua_cry(
-		    fake_connection(&fc, ctx), err, arg->state, arg->script, "timer");
+		lua_cry(fake_connection(&fc, ctx), err, arg->L, arg->script, "timer");
 		(void)pthread_mutex_unlock(arg->pmutex);
-		mg_free(arg);
 		return 0;
 	}
 
-	ok = lua_type(arg->state, -1);
-	if (lua_isboolean(arg->state, -1)) {
-		ok = lua_toboolean(arg->state, -1);
+	ok = lua_type(arg->L, -1);
+	if (lua_isboolean(arg->L, -1)) {
+		ok = lua_toboolean(arg->L, -1);
 	} else {
 		ok = 0;
 	}
-	lua_pop(arg->state, 1);
+	lua_pop(arg->L, 1);
 
 	(void)pthread_mutex_unlock(arg->pmutex);
 
-	if (!ok) {
-		mg_free(arg);
-	}
 	return ok;
 }
 
 
 static int
-lua_action_free(struct laction_arg *arg)
+lua_action_funcref(struct laction_funcref_arg *arg)
 {
-	if (lua_action(arg)) {
-		mg_free(arg);
+	int err, ok;
+	struct mg_context *ctx;
+
+	(void)pthread_mutex_lock(arg->pmutex);
+
+	lua_pushlightuserdata(arg->L, (void *)&lua_regkey_ctx);
+	lua_gettable(arg->L, LUA_REGISTRYINDEX);
+	ctx = (struct mg_context *)lua_touserdata(arg->L, -1);
+	lua_pop(arg->L, 1);
+
+	lua_rawgeti(arg->L, LUA_REGISTRYINDEX, arg->funcref);
+	err = lua_pcall(arg->L, 0, 1, 0);
+	if (err != 0) {
+		struct mg_connection fc;
+		lua_cry(fake_connection(&fc, ctx), err, arg->L, arg->script, "timer");
+		(void)pthread_mutex_unlock(arg->pmutex);
+		return 0;
 	}
-	return 0;
+
+	ok = lua_type(arg->L, -1);
+	if (lua_isboolean(arg->L, -1)) {
+		ok = lua_toboolean(arg->L, -1);
+	} else {
+		ok = 0;
+	}
+	lua_pop(arg->L, 1);
+
+	(void)pthread_mutex_unlock(arg->pmutex);
+
+	return ok;
+}
+
+
+static void
+lua_action_string_cancel(struct laction_string_arg *arg)
+{
+	mg_free(arg);
+}
+
+
+static void
+lua_action_funcref_cancel(struct laction_funcref_arg *arg)
+{
+	luaL_unref(arg->L, LUA_REGISTRYINDEX, arg->funcref);
+	mg_free(arg);
 }
 
 
@@ -2037,12 +2100,9 @@ lwebsocket_set_timer(lua_State *L, int is_periodic)
 #if defined(USE_TIMERS) && defined(USE_WEBSOCKET)
 	int num_args = lua_gettop(L);
 	struct lua_websock_data *ws;
-	int type1, type2, ok = 0;
-	double timediff;
+	int type1, type2, type3, ok = 0;
+	double delay, interval;
 	struct mg_context *ctx;
-	struct laction_arg *arg;
-	const char *txt;
-	size_t txt_len;
 
 	lua_pushlightuserdata(L, (void *)&lua_regkey_ctx);
 	lua_gettable(L, LUA_REGISTRYINDEX);
@@ -2059,36 +2119,108 @@ lwebsocket_set_timer(lua_State *L, int is_periodic)
 
 	type1 = lua_type(L, 1);
 	type2 = lua_type(L, 2);
+	type3 = lua_type(L, 3);
+
+	/* Must have at least two arguments, ant the first one has to be some text
+	 */
+	if ((num_args < 2) || (num_args > 3)) {
+		return luaL_error(L, "invalid arguments for set_timer/interval() call");
+	}
+
+	/* Second argument is the delay (and interval) */
+	if (type2 != LUA_TNUMBER) {
+		return luaL_error(L, "invalid arguments for set_timer/interval() call");
+	}
+	delay = (double)lua_tonumber(L, 2);
+	interval = (is_periodic ? delay : 0.0);
+
+	/* Third argument (optional) could be an interval */
+	if (num_args > 2) {
+		if (is_periodic || (type3 != LUA_TNUMBER)) {
+			return luaL_error(
+			    L, "invalid arguments for set_timer/interval() call");
+		}
+		interval = (double)lua_tonumber(L, 3);
+	}
+
+	/* Check numbers */
+	if ((delay < 0.0) || (interval < 0.0)) {
+		return luaL_error(L, "invalid arguments for set_timer/interval() call");
+	}
+
+	/* First value specifies the action */
+	if (type1 == LUA_TSTRING) {
+
+		/* Action could be passed as a string value */
+		struct laction_string_arg *arg;
+		const char *action_txt;
+		size_t action_txt_len;
+
+		action_txt = lua_tostring(L, 1);
+		if ((action_txt == NULL) || (action_txt[0] == 0)) {
+			return luaL_error(
+			    L, "invalid arguments for set_timer/interval() call");
+		}
+		action_txt_len = strlen(action_txt);
 
-	if (type1 == LUA_TSTRING && type2 == LUA_TNUMBER && num_args == 2) {
-		timediff = (double)lua_tonumber(L, 2);
-		txt = lua_tostring(L, 1);
-		txt_len = strlen(txt);
-		arg = (struct laction_arg *)mg_malloc_ctx(sizeof(struct laction_arg)
-		                                              + txt_len + 10,
-		                                          ctx);
+		/* Create timer data structure and schedule timer */
+		arg = (struct laction_string_arg *)mg_malloc_ctx(
+		    sizeof(struct laction_string_arg) + action_txt_len + 10, ctx);
 		if (!arg) {
 			return luaL_error(L, "out of memory");
 		}
 
-		arg->state = L;
+		/* Argument for timer */
+		arg->L = L;
 		arg->script = ws->script;
 		arg->pmutex = &(ws->ws_mutex);
 		memcpy(arg->txt, "return(", 7);
-		memcpy(arg->txt + 7, txt, txt_len);
-		arg->txt[txt_len + 7] = ')';
-		arg->txt[txt_len + 8] = 0;
-		ok =
-		    (0
-		     == timer_add(ctx,
-		                  timediff,
-		                  is_periodic,
-		                  1,
-		                  (taction)(is_periodic ? lua_action : lua_action_free),
-		                  (void *)arg));
-	} else if (type1 == LUA_TFUNCTION && type2 == LUA_TNUMBER) {
-		/* TODO (mid): not implemented yet */
-		return luaL_error(L, "invalid arguments for set_timer/interval() call");
+		memcpy(arg->txt + 7, action_txt, action_txt_len);
+		arg->txt[action_txt_len + 7] = ')';
+		arg->txt[action_txt_len + 8] = 0;
+		if (0
+		    == timer_add(ctx,
+		                 delay,
+		                 interval,
+		                 1,
+		                 (taction)lua_action_string,
+		                 (void *)arg,
+		                 (tcancelaction)lua_action_string_cancel)) {
+			/* Timer added successfully */
+			ok = 1;
+		}
+	} else if (type1 == LUA_TFUNCTION) {
+
+		/* Action could be passed as a function */
+		int funcref;
+		struct laction_funcref_arg *arg;
+
+		lua_pushvalue(L, 1);
+		funcref = luaL_ref(L, LUA_REGISTRYINDEX);
+
+		/* Create timer data structure and schedule timer */
+		arg = (struct laction_funcref_arg *)
+		    mg_malloc_ctx(sizeof(struct laction_funcref_arg), ctx);
+		if (!arg) {
+			return luaL_error(L, "out of memory");
+		}
+
+		/* Argument for timer */
+		arg->L = L;
+		arg->script = ws->script;
+		arg->pmutex = &(ws->ws_mutex);
+		arg->funcref = funcref;
+		if (0
+		    == timer_add(ctx,
+		                 delay,
+		                 interval,
+		                 1,
+		                 (taction)lua_action_funcref,
+		                 (void *)arg,
+		                 (tcancelaction)lua_action_funcref_cancel)) {
+			/* Timer added successfully */
+			ok = 1;
+		}
 	} else {
 		return luaL_error(L, "invalid arguments for set_timer/interval() call");
 	}
@@ -2330,6 +2462,65 @@ civetweb_open_lua_libs(lua_State *L)
 }
 
 
+/* garbage collect "mg"
+ * this indicates when a Lua state is closed
+ */
+static int
+lsp_mg_gc(lua_State *L)
+{
+	unsigned context_flags;
+	struct mg_context *ctx;
+	struct mg_connection *conn =
+	    (struct mg_connection *)lua_touserdata(L, lua_upvalueindex(1));
+
+	lua_pushlightuserdata(L, (void *)&lua_regkey_ctx);
+	lua_gettable(L, LUA_REGISTRYINDEX);
+	ctx = (struct mg_context *)lua_touserdata(L, -1);
+
+	lua_pushlightuserdata(L, (void *)&lua_regkey_environment_type);
+	lua_gettable(L, LUA_REGISTRYINDEX);
+	context_flags = lua_tounsigned(L, -1);
+
+	if (ctx != NULL) {
+		if (ctx->callbacks.exit_lua != NULL) {
+			ctx->callbacks.exit_lua(conn, L, context_flags);
+		}
+	}
+
+	return 0;
+}
+
+
+static void
+reg_gc(lua_State *L, void *conn)
+{
+	/* Key element */
+	lua_pushlightuserdata(L, (void *)&lua_regkey_dtor);
+
+	/* Value element */
+	lua_newuserdata(L, 0);
+
+	/* Prepare metatable for value element */
+	lua_newtable(L);
+
+	/* Add garbage collector key to metatable */
+	lua_pushliteral(L, "__gc");
+
+	/* Add garbage collector function with one upvalue (conn) */
+	lua_pushlightuserdata(L, conn);
+	lua_pushcclosure(L, lsp_mg_gc, 1);
+
+	/* Set __gc = function lsp_mg_gc in metatable */
+	lua_rawset(L, -3);
+
+	/* Set metatable for "value element" */
+	lua_setmetatable(L, -2);
+
+	/* Add key (lightuserdata) = value (userdata with metatable) */
+	lua_settable(L, LUA_REGISTRYINDEX);
+}
+
+
 static void
 prepare_lua_environment(struct mg_context *ctx,
                         struct mg_connection *conn,
@@ -2341,6 +2532,8 @@ prepare_lua_environment(struct mg_context *ctx,
 	const char *preload_file_name = NULL;
 	const char *debug_params = NULL;
 
+	unsigned lua_context_flags = (unsigned)lua_env_type;
+
 	civetweb_open_lua_libs(L);
 
 #if defined(MG_EXPERIMENTAL_INTERFACES)
@@ -2375,6 +2568,12 @@ prepare_lua_environment(struct mg_context *ctx,
 		lua_pushlightuserdata(L, (void *)ws_conn_list);
 		lua_settable(L, LUA_REGISTRYINDEX);
 	}
+	lua_pushlightuserdata(L, (void *)&lua_regkey_environment_type);
+	lua_pushunsigned(L, (unsigned)lua_context_flags);
+	lua_settable(L, LUA_REGISTRYINDEX);
+
+	/* State close function */
+	reg_gc(L, conn);
 
 	/* Lua server pages store the depth of mg.include, in order
 	 * to detect recursions and prevent stack overflows. */
@@ -2504,7 +2703,7 @@ prepare_lua_environment(struct mg_context *ctx,
 	/* Call user init function */
 	if (ctx != NULL) {
 		if (ctx->callbacks.init_lua != NULL) {
-			ctx->callbacks.init_lua(conn, L);
+			ctx->callbacks.init_lua(conn, L, lua_context_flags);
 		}
 	}
 
@@ -2608,7 +2807,6 @@ handle_lsp_request(struct mg_connection *conn,
 	lua_State *L = NULL;
 	struct lsp_include_history *include_history;
 	int error = 1;
-	void *file_in_memory; /* TODO(low): remove when removing "file in memory" */
 	int (*run_lsp)(struct mg_connection *,
 	               const char *,
 	               const char *,
@@ -2633,23 +2831,14 @@ handle_lsp_request(struct mg_connection *conn,
 		goto cleanup_handle_lsp_request;
 	}
 
-#if defined(MG_USE_OPEN_FILE)
-	/* The "file in memory" feature is going to be removed. For details see
-	 * https://groups.google.com/forum/#!topic/civetweb/h9HT4CmeYqI */
-	file_in_memory = filep->access.membuf;
-#else
-	file_in_memory = NULL;
-#endif
-
 	/* Map file in memory (size is known). */
-	if (file_in_memory == NULL
-	    && (p = mmap(NULL,
-	                 (size_t)filep->stat.size,
-	                 PROT_READ,
-	                 MAP_PRIVATE,
-	                 fileno(filep->access.fp),
-	                 0))
-	           == MAP_FAILED) {
+	if ((p = mmap(NULL,
+	              (size_t)filep->stat.size,
+	              PROT_READ,
+	              MAP_PRIVATE,
+	              fileno(filep->access.fp),
+	              0))
+	    == NULL) {
 
 		/* File was not already in memory, and mmap failed now.
 		 * Since wi have no data, show an error. */
@@ -2673,9 +2862,8 @@ handle_lsp_request(struct mg_connection *conn,
 		goto cleanup_handle_lsp_request;
 	}
 
-	/* File content is now memory mapped. Get mapping address */
-	addr = (file_in_memory == NULL) ? (const char *)p
-	                                : (const char *)file_in_memory;
+	/* File content is now memory mapped. Get mapping address. */
+	addr = (const char *)p;
 
 	/* Get a Lua state */
 	if (ls != NULL) {
@@ -2966,6 +3154,7 @@ lua_websocket_close(struct mg_connection *conn, void *ws_arg)
 	    &(conn->dom_ctx->shared_lua_websockets);
 	int err = 0;
 	unsigned i;
+	int delete_state = 0;
 
 	DEBUG_ASSERT(ws != NULL);
 	DEBUG_ASSERT(ws->state != NULL);
@@ -2985,13 +3174,33 @@ lua_websocket_close(struct mg_connection *conn, void *ws_arg)
 	for (i = 0; i < ws->references; i++) {
 		if (ws->conn[i] == conn) {
 			ws->references--;
+			if (ws->references == 0) {
+				delete_state = 1;
+				break;
+			}
 			ws->conn[i] = ws->conn[ws->references];
 		}
 	}
 	/* TODO: Delete lua_websock_data and remove it from the websocket list.
 	   This must only be done, when all connections are closed, and all
 	   asynchronous operations and timers are completed/expired. */
-	(void)shared_websock_list; /* shared_websock_list unused (see open TODO) */
+	if (delete_state) {
+		/* lock list (mg_context global) */
+		mg_lock_context(conn->phys_ctx);
+		while (*shared_websock_list) {
+			/* find ws in list */
+			if (0 == strcmp(ws->script, (*shared_websock_list)->ws.script)) {
+				break;
+			}
+			shared_websock_list = &((*shared_websock_list)->next);
+		}
+		if (*shared_websock_list != NULL) {
+			/* TODO: If we close the state here, timers must be stopped first */
+			/* lua_close(ws->state); <-- other thread will crash, if there are
+			 * still active timers */
+		}
+		mg_unlock_context(conn->phys_ctx);
+	}
 
 	(void)pthread_mutex_unlock(&(ws->ws_mutex));
 }
@@ -3062,6 +3271,38 @@ mg_prepare_lua_context_script(const char *file_name,
 }
 
 
+static void
+lua_ctx_init(struct mg_context *ctx)
+{
+#if defined(USE_WEBSOCKET)
+	ctx->dd.shared_lua_websockets = NULL;
+#endif
+}
+
+
+static void
+lua_ctx_exit(struct mg_context *ctx)
+{
+#if defined(USE_WEBSOCKET)
+	struct mg_shared_lua_websocket_list **shared_websock_list =
+	    &(ctx->dd.shared_lua_websockets);
+	struct mg_shared_lua_websocket_list *next;
+
+	mg_lock_context(ctx);
+	while (*shared_websock_list) {
+		lua_close((*shared_websock_list)->ws.state);
+		mg_free((*shared_websock_list)->ws.script);
+
+		/* Save "next" pointer before freeing list element */
+		next = ((*shared_websock_list)->next);
+		mg_free(*shared_websock_list);
+		shared_websock_list = &next;
+	}
+	mg_unlock_context(ctx);
+#endif
+}
+
+
 int
 run_lua(const char *file_name)
 {

+ 1 - 45
src/mod_lua_shared.inl

@@ -63,6 +63,7 @@ lua_shared_exit(void)
 	pthread_mutex_destroy(&lua_shared_lock);
 }
 
+
 #if defined(MG_EXPERIMENTAL_INTERFACES)
 static double
 shared_locked_add(const char *name, size_t namlen, double value, int op)
@@ -159,45 +160,6 @@ lua_shared_exchange(struct lua_State *L)
 	lua_pushnumber(L, ret);
 	return 1;
 }
-
-/*
-static int
-lua_shared_push(struct lua_State *L)
-{
-    int val_type = lua_type(L, 1);
-
-    if ((val_type != LUA_TNUMBER) && (val_type != LUA_TSTRING)
-        && (val_type != LUA_TBOOLEAN)) {
-        return luaL_error(L, "shared value must be string, number or boolean");
-    }
-
-    pthread_mutex_lock(&lua_shared_lock);
-
-    lua_getglobal(L_shared, "shared");
-    lua_pushnumber(L_shared, num);
-
-    if (val_type == LUA_TNUMBER) {
-        double num = lua_tonumber(L, 3);
-        lua_pushnumber(L_shared, num);
-
-    } else if (val_type == LUA_TBOOLEAN) {
-        int i = lua_toboolean(L, 3);
-        lua_pushboolean(L_shared, i);
-
-    } else {
-        size_t len = 0;
-        const char *str = lua_tolstring(L, 3, &len);
-        lua_pushlstring(L_shared, str, len);
-    }
-
-    lua_rawset(L_shared, -3);
-    lua_pop(L_shared, 1);
-
-    pthread_mutex_unlock(&lua_shared_lock);
-
-    return 0;
-}
-*/
 #endif
 
 
@@ -242,12 +204,6 @@ lua_shared_index(struct lua_State *L)
 				lua_pushcclosure(L, lua_shared_dec, 0);
 			} else if (0 == strcmp(str, "__exchange")) {
 				lua_pushcclosure(L, lua_shared_exchange, 0);
-				/*
-			} else if (0 == strcmp(str, "__push")) {
-				lua_pushcclosure(L, lua_shared_push, 0);
-			} else if (0 == strcmp(str, "__pop")) {
-				lua_pushcclosure(L, lua_shared_pop, 0);
-				*/
 			} else
 #endif
 			{

+ 6 - 0
src/third_party/civetweb_lua.h

@@ -27,6 +27,9 @@
 #define CIVETWEB_LUA_H
 
 #define LUA_LIB
+#define LUA_COMPAT_LOG10
+#define LUA_COMPAT_APIINTCASTS
+
 #include "lauxlib.h"
 #include "lua.h"
 #include "lualib.h"
@@ -65,6 +68,9 @@
 /* Lua 5.4 detected */
 #define mg_lua_load lua_load
 
+#else
+#error "Lua version not supported (yet?)"
+
 #endif
 
 #ifdef LUA_VERSION_MAKEFILE

+ 2 - 0
src/third_party/lsqlite3.c

@@ -36,7 +36,9 @@
 /*
 ** Lua 5.2
 */
+#ifndef lua_strlen
 #define lua_strlen lua_rawlen
+#endif
 /* luaL_typerror always used with arg at ndx == NULL */
 #define luaL_typerror(L,ndx,str) luaL_error(L,"bad argument %d (%s expected, got nil)",ndx,str)
 /* luaL_register used once, so below expansion is OK for this case */

Plik diff jest za duży
+ 291 - 145
src/third_party/sqlite3.c


Plik diff jest za duży
+ 437 - 131
src/third_party/sqlite3.h


+ 50 - 16
src/timer.inl

@@ -1,19 +1,25 @@
 /* This file is part of the CivetWeb web server.
  * See https://github.com/civetweb/civetweb/
- * (C) 2014-2018 by the CivetWeb authors, MIT license.
+ * (C) 2014-2020 by the CivetWeb authors, MIT license.
  */
 
 #if !defined(MAX_TIMERS)
 #define MAX_TIMERS MAX_WORKER_THREADS
 #endif
+#if !defined(TIMER_RESOLUTION)
+/* Timer resolution in ms */
+#define TIMER_RESOLUTION (10)
+#endif
 
 typedef int (*taction)(void *arg);
+typedef void (*tcancelaction)(void *arg);
 
 struct ttimer {
 	double time;
 	double period;
 	taction action;
 	void *arg;
+	tcancelaction cancel;
 };
 
 struct ttimers {
@@ -66,7 +72,8 @@ timer_add(struct mg_context *ctx,
           double period,
           int is_relative,
           taction action,
-          void *arg)
+          void *arg,
+          tcancelaction cancel)
 {
 	int error = 0;
 	double now;
@@ -126,6 +133,7 @@ timer_add(struct mg_context *ctx,
 		ctx->timers->timers[u].period = period;
 		ctx->timers->timers[u].action = action;
 		ctx->timers->timers[u].arg = arg;
+		ctx->timers->timers[u].cancel = cancel;
 		ctx->timers->timer_count++;
 	}
 	pthread_mutex_unlock(&ctx->timers->mutex);
@@ -139,7 +147,7 @@ timer_thread_run(void *thread_func_param)
 	struct mg_context *ctx = (struct mg_context *)thread_func_param;
 	double d;
 	unsigned u;
-	int re_schedule;
+	int action_res;
 	struct ttimer t;
 
 	mg_set_thread_name("timer");
@@ -149,39 +157,65 @@ timer_thread_run(void *thread_func_param)
 		ctx->callbacks.init_thread(ctx, 2);
 	}
 
+	/* Timer main loop */
 	d = timer_getcurrenttime(ctx);
-
 	while (STOP_FLAG_IS_ZERO(&ctx->stop_flag)) {
 		pthread_mutex_lock(&ctx->timers->mutex);
 		if ((ctx->timers->timer_count > 0)
 		    && (d >= ctx->timers->timers[0].time)) {
+			/* Timer list is sorted. First action should run now. */
+			/* Store active timer in "t" */
 			t = ctx->timers->timers[0];
+
+			/* Shift all other timers */
 			for (u = 1; u < ctx->timers->timer_count; u++) {
 				ctx->timers->timers[u - 1] = ctx->timers->timers[u];
 			}
 			ctx->timers->timer_count--;
+
 			pthread_mutex_unlock(&ctx->timers->mutex);
-			re_schedule = t.action(t.arg);
-			if (re_schedule && (t.period > 0)) {
-				timer_add(ctx, t.time + t.period, t.period, 0, t.action, t.arg);
+
+			/* Call timer action */
+			action_res = t.action(t.arg);
+
+			/* action_res == 1: reschedule */
+			/* action_res == 0: do not reschedule, free(arg) */
+			if ((action_res > 0) && (t.period > 0)) {
+				/* Should schedule timer again */
+				timer_add(ctx,
+				          t.time + t.period,
+				          t.period,
+				          0,
+				          t.action,
+				          t.arg,
+				          t.cancel);
+			} else {
+				/* Allow user to free timer argument */
+				if (t.cancel != NULL) {
+					t.cancel(t.arg);
+				}
 			}
 			continue;
 		} else {
 			pthread_mutex_unlock(&ctx->timers->mutex);
 		}
 
-/* 10 ms seems reasonable.
- * A faster loop (smaller sleep value) increases CPU load,
- * a slower loop (higher sleep value) decreases timer accuracy.
- */
-#if defined(_WIN32)
-		Sleep(10);
-#else
-		usleep(10000);
-#endif
+		/* TIMER_RESOLUTION = 10 ms seems reasonable.
+		 * A faster loop (smaller sleep value) increases CPU load,
+		 * a slower loop (higher sleep value) decreases timer accuracy.
+		 */
+		mg_sleep(TIMER_RESOLUTION);
 
 		d = timer_getcurrenttime(ctx);
 	}
+
+	/* Remove remaining timers */
+	for (u = 0; u < ctx->timers->timer_count; u++) {
+		t = ctx->timers->timers[u];
+		if (t.cancel != NULL) {
+			t.cancel(t.arg);
+		}
+	}
 }
 
 

+ 20 - 0
test/scriptdir/index.lua

@@ -43,6 +43,26 @@ mg.write("      </tr>\r\n");
 end
 mg.write("    </table>\r\n");
 
+
+mg.write("  <h2>Info</h2>\r\n")
+
+info = {}
+info["path_info"] = mg.request_info.path_info
+info["query_string"] = mg.request_info.query_string
+
+mg.write("    <table>\r\n");
+mg.write("      <tr>\r\n");
+mg.write("        <th>Info</th>\r\n")
+mg.write("        <th>Value</th>\r\n")
+mg.write("      </tr>\r\n");
+for k,v in pairs(info) do
+  mg.write("      <tr>\r\n");
+  mg.write("        <td>" .. k .. "</td>\r\n")
+  mg.write("        <td>" .. v .. "</td>\r\n")
+end
+mg.write("      </tr>\r\n");
+mg.write("    </table>\r\n");
+
 mg.write("  <h2>Body</h2>\r\n")
 mg.write("<pre>\r\n");
 

+ 105 - 0
test/websocket2.lua

@@ -0,0 +1,105 @@
+
+function trace(text)
+    local f = io.open("websocket2.trace", "a")
+    f:write(os.date() .. " - " .. text .. "\n")
+    f:close()
+end
+
+function iswebsocket()
+  return mg.lua_type == "websocket"
+end
+
+trace("called with Lua type " .. tostring(mg.lua_type))
+
+if not iswebsocket() then
+  trace("no websocket")
+  mg.write("HTTP/1.0 403 Forbidden\r\n")
+  mg.write("Connection: close\r\n")
+  mg.write("\r\n")
+  mg.write("forbidden")
+  return
+end
+
+
+-- Serialize table to string
+function ser(val)
+  local t
+  if type(val) == "table" then
+    for k,v in pairs(val) do
+      if t then
+        t = t .. ", " .. ser(k) .. "=" .. ser(v)
+      else
+        t = "{" .. ser(k) .. "=" .. ser(v)
+      end
+    end
+    t = t .. "}"
+  else
+    t = tostring(val)
+  end
+  return t
+end
+
+-- table of all active connection
+allConnections = {}
+
+-- function to get a client identification string
+function who(tab)
+  local ri = allConnections[tab.client].request_info
+  return ri.remote_addr .. ":" .. ri.remote_port
+end
+
+-- Callback to accept or reject a connection
+function open(tab)
+  allConnections[tab.client] = tab
+  trace("open[" .. who(tab) .. "]: " .. ser(tab))
+  return true -- return true to accept the connection
+end
+
+-- Callback for "Websocket ready"
+function ready(tab)
+  trace("ready[" .. who(tab) .. "]: " .. ser(tab))
+  mg.write(tab.client, "text", "Websocket ready")
+  mg.write(tab.client, 1, "-->h 180");
+  mg.write(tab.client, "-->m 180");
+  senddata()
+  mg.set_interval(timer, 1)
+  return true -- return true to keep the connection open
+end
+
+-- Callback for "Websocket received data"
+function data(tab)
+    trace("data[" .. who(tab) .. "]: " .. ser(tab))
+    senddata()
+    return true -- return true to keep the connection open
+end
+
+-- Callback for "Websocket is closing"
+function close(tab)
+    trace("close[" .. who(tab) .. "]: " .. ser(tab))
+    mg.write("text", "end")
+    allConnections[tab.client] = nil
+end
+
+function senddata()
+    local date = os.date('*t');
+    local hand = (date.hour%12)*60+date.min;
+
+    mg.write("text", string.format("%u:%02u:%02u", date.hour, date.min, date.sec));
+
+    if (hand ~= lasthand) then
+        mg.write(1, string.format("-->h %f", hand*360/(12*60)));
+        mg.write(   string.format("-->m %f", date.min*360/60));
+        lasthand = hand;
+    end
+
+    if bits and content then
+        data(bits, content)
+    end
+end
+
+function timer()
+    trace("timer")
+    senddata()
+    return true -- return true to keep an interval timer running
+end
+

+ 117 - 0
test/websocket2.xhtml

@@ -0,0 +1,117 @@
+<!DOCTYPE HTML>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+  <meta charset="UTF-8"></meta>
+  <title>Websocket test</title>
+  <style type="text/css" media="screen">
+    body { background:#eee; margin:0 }
+    .main {
+      display:block; border:1px solid #ccc; position:absolute;
+      top:5%; left:5%; width:90%; height:90%; background:#fff;
+    }
+  </style>
+</head>
+<body>
+  <script type="text/javascript"><![CDATA[
+
+    var connection;
+    var websock_text_field;
+    var hand_hour;
+    var hand_min;
+
+    function queryStringElem(name, idx) {
+      if (typeof(queryStringElem_Table) != "object") {
+        queryStringElem_Table = {};
+        window.location.search.slice(1).split('&').forEach(
+          function(keyValuePair) {
+            keyValuePair = keyValuePair.split('=');
+            if (typeof(queryStringElem_Table[keyValuePair[0]]) != "object") {
+              queryStringElem_Table[keyValuePair[0]] = [];
+            }
+            var idx = queryStringElem_Table[keyValuePair[0]].length+1;
+            queryStringElem_Table[keyValuePair[0]][idx] = keyValuePair[1] || '';
+          }
+        );
+      }
+      idx = idx || 1;
+      if (queryStringElem_Table[name]) {
+        return queryStringElem_Table[name][idx];
+      }
+      return null;
+    }
+
+    function webSockKeepAlive() {
+      if (keepAlive) {
+        connection.send('client still alive');
+        console.log('send keep alive')
+        setTimeout("webSockKeepAlive()", 10000);
+      }
+    }
+
+    function load() {
+      var wsproto = (location.protocol === 'https:') ? "wss:" : "ws:";
+      connection = new WebSocket(wsproto + "//" + window.location.host + "/websocket2.lua");
+      websock_text_field = document.getElementById('websock_text_field');
+      hand_min = document.getElementById('hand_min');
+      hand_hour = document.getElementById('hand_hour');
+
+      var ka = queryStringElem("keepAlive");
+      if (ka) {
+        ka = ka.toLowerCase();
+        use_keepAlive = (ka!="false") && (ka!="f") && (ka!="no") && (ka!="n") && (ka!=0);
+      } else {
+        use_keepAlive = true;
+      }
+
+      connection.onopen = function () {
+        keepAlive = use_keepAlive;
+        webSockKeepAlive();
+      };
+
+      // Log errors
+      connection.onerror = function (error) {
+        keepAlive = false;
+        alert("WebSocket error");
+        connection.close();
+      };
+
+      // Log messages from the server
+      connection.onmessage = function (e) {
+        var lCmd = e.data.substring(0,3);
+        if (lCmd == "-->") {
+          console.log(e.data);
+          var lDirection = Number(e.data.substring(5));
+          if (e.data[3] == 'h') {
+            hand_hour.setAttribute("transform", "rotate(" + lDirection + " 800 600)");
+          }
+          if (e.data[3] == 'm') {
+            hand_min.setAttribute("transform", "rotate(" + lDirection + " 800 600)");
+          }
+        } else {
+          websock_text_field.textContent = e.data;
+        }
+      };
+
+      console.log("load");
+    }
+
+  ]]></script>
+
+<svg class="main"
+  xmlns="http://www.w3.org/2000/svg"
+  xmlns:svg="http://www.w3.org/2000/svg"
+  version="1.1"
+  xmlns:xlink="http://www.w3.org/1999/xlink"
+  viewBox="0 0 1600 1200" preserveAspectRatio="xMinYMin meet"
+  onload="load()"
+  >
+
+  <circle id="line_a" cx="800" cy="600" r="500" style="stroke:rgb(255,0,0); stroke-width:5; fill:rgb(200,200,200)"/>
+  <polygon points="800,200 900,300 850,300 850,600 750,600 750,300 700,300" style="fill:rgb(100,0,0)" transform="rotate(0,800,600)" id="hand_hour"/>
+  <polygon points="800,100 840,200 820,200 820,600 780,600 780,200 760,200" style="fill:rgb(0,100,0)" transform="rotate(0,800,600)" id="hand_min"/>
+  <text id="websock_text_field" x="800" y="600" text-anchor="middle" font-size="50px" fill="red">No websocket connection yet</text>
+
+</svg>
+
+</body>
+</html>

+ 117 - 0
test/websocket_wrong_url.xhtml

@@ -0,0 +1,117 @@
+<!DOCTYPE HTML>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+  <meta charset="UTF-8"></meta>
+  <title>Websocket test</title>
+  <style type="text/css" media="screen">
+    body { background:#eee; margin:0 }
+    .main {
+      display:block; border:1px solid #ccc; position:absolute;
+      top:5%; left:5%; width:90%; height:90%; background:#fff;
+    }
+  </style>
+</head>
+<body>
+  <script type="text/javascript"><![CDATA[
+
+    var connection;
+    var websock_text_field;
+    var hand_hour;
+    var hand_min;
+
+    function queryStringElem(name, idx) {
+      if (typeof(queryStringElem_Table) != "object") {
+        queryStringElem_Table = {};
+        window.location.search.slice(1).split('&').forEach(
+          function(keyValuePair) {
+            keyValuePair = keyValuePair.split('=');
+            if (typeof(queryStringElem_Table[keyValuePair[0]]) != "object") {
+              queryStringElem_Table[keyValuePair[0]] = [];
+            }
+            var idx = queryStringElem_Table[keyValuePair[0]].length+1;
+            queryStringElem_Table[keyValuePair[0]][idx] = keyValuePair[1] || '';
+          }
+        );
+      }
+      idx = idx || 1;
+      if (queryStringElem_Table[name]) {
+        return queryStringElem_Table[name][idx];
+      }
+      return null;
+    }
+
+    function webSockKeepAlive() {
+      if (keepAlive) {
+        connection.send('client still alive');
+        console.log('send keep alive')
+        setTimeout("webSockKeepAlive()", 10000);
+      }
+    }
+
+    function load() {
+      var wsproto = (location.protocol === 'https:') ? "wss:" : "ws:";
+      connection = new WebSocket(wsproto + "//" + window.location.host + "/websocket_not_found.xyz");
+      websock_text_field = document.getElementById('websock_text_field');
+      hand_min = document.getElementById('hand_min');
+      hand_hour = document.getElementById('hand_hour');
+
+      var ka = queryStringElem("keepAlive");
+      if (ka) {
+        ka = ka.toLowerCase();
+        use_keepAlive = (ka!="false") && (ka!="f") && (ka!="no") && (ka!="n") && (ka!=0);
+      } else {
+        use_keepAlive = true;
+      }
+
+      connection.onopen = function () {
+        keepAlive = use_keepAlive;
+        webSockKeepAlive();
+      };
+
+      // Log errors
+      connection.onerror = function (error) {
+        keepAlive = false;
+        alert("WebSocket error");
+        connection.close();
+      };
+
+      // Log messages from the server
+      connection.onmessage = function (e) {
+        var lCmd = e.data.substring(0,3);
+        if (lCmd == "-->") {
+          console.log(e.data);
+          var lDirection = Number(e.data.substring(5));
+          if (e.data[3] == 'h') {
+            hand_hour.setAttribute("transform", "rotate(" + lDirection + " 800 600)");
+          }
+          if (e.data[3] == 'm') {
+            hand_min.setAttribute("transform", "rotate(" + lDirection + " 800 600)");
+          }
+        } else {
+          websock_text_field.textContent = e.data;
+        }
+      };
+
+      console.log("load");
+    }
+
+  ]]></script>
+
+<svg class="main"
+  xmlns="http://www.w3.org/2000/svg"
+  xmlns:svg="http://www.w3.org/2000/svg"
+  version="1.1"
+  xmlns:xlink="http://www.w3.org/1999/xlink"
+  viewBox="0 0 1600 1200" preserveAspectRatio="xMinYMin meet"
+  onload="load()"
+  >
+
+  <circle id="line_a" cx="800" cy="600" r="500" style="stroke:rgb(255,0,0); stroke-width:5; fill:rgb(200,200,200)"/>
+  <polygon points="800,200 900,300 850,300 850,600 750,600 750,300 700,300" style="fill:rgb(100,0,0)" transform="rotate(0,800,600)" id="hand_hour"/>
+  <polygon points="800,100 840,200 820,200 820,600 780,600 780,200 760,200" style="fill:rgb(0,100,0)" transform="rotate(0,800,600)" id="hand_min"/>
+  <text id="websock_text_field" x="800" y="600" text-anchor="middle" font-size="50px" fill="red">No websocket connection yet</text>
+
+</svg>
+
+</body>
+</html>

+ 8 - 3
unittest/public_func.c

@@ -175,10 +175,15 @@ START_TEST(test_mg_get_valid_options)
 		ck_assert_int_ge(len, 8);
 		ck_assert_int_lt(len, 40);
 
-		/* check valid characters (lower case or underscore) */
-		for (j = 0; j < len; j++) {
+		/* check valid characters: */
+		/* Every option name must start with lower case letter */
+		c = default_options[i].name[0];
+		ck_assert((c >= 'a') && (c <= 'z'));
+		for (j = 1; j < len; j++) {
+			/* Followed by lower case letters, numbers or underscores */
 			c = default_options[i].name[j];
-			ck_assert(((c >= 'a') && (c <= 'z')) || (c == '_'));
+			ck_assert(((c >= 'a') && (c <= 'z')) || (c == '_')
+			          || ((c >= '0') && (c <= '9')));
 		}
 	}
 

+ 10 - 4
unittest/public_server.c

@@ -790,11 +790,17 @@ START_TEST(test_mg_server_and_client_tls)
 	client_conn =
 	    mg_connect_client("127.0.0.1", 8443, 1, client_err, sizeof(client_err));
 
-	/* cannot connect without client certificate */
-#if defined(__MACH__)
-	/* except for Apple (maybe this is specific to the MacOS container on
-	 * TravisCI?) */
+	/* We tried to connect without client certificate:
+	 * Depending on ???, either mg_conn_client failed entirely, returning NULL.
+	 * or we do get a connection but get an error when we try to use it.
+	 *
+	 * MacOS (Version ?), Ubuntu Bionic and Ububtu Eoan allow to connect,
+	 * while Ubuntu Xenial, Ubuntu Trusty and Windows test containers at
+	 * Travis CI do not. Maybe it is OpenSSL version specific.
+	 */
+#if defined(OPENSSL_API_1_1)
 	if (client_conn) {
+		/* Connect succeeds, but the connection is unusable. */
 		mg_printf(client_conn, "GET / HTTP/1.0\r\n\r\n");
 		client_res =
 		    mg_get_response(client_conn, client_err, sizeof(client_err), 10000);

+ 74 - 27
unittest/timertest.c

@@ -44,8 +44,10 @@
 
 #include "timertest.h"
 
+#define TIMERS_IN_TEST 10
 static int action_dec_ret;
 
+
 static int
 action_dec(void *arg)
 {
@@ -61,6 +63,23 @@ action_dec(void *arg)
 }
 
 
+static void
+action_cancel(void *arg)
+{
+	int *p = (int *)arg;
+
+	/* test convention: store cancel counter after timer counter */
+	p += TIMERS_IN_TEST;
+
+	if (*p != 0) {
+		ck_abort_msg("Double call of timer cancel action");
+		/* return 0 here would be unreachable code */
+	}
+
+	(*p)++;
+}
+
+
 static int
 action_dec_to_0(void *arg)
 {
@@ -79,7 +98,7 @@ action_dec_to_0(void *arg)
 START_TEST(test_timer_cyclic)
 {
 	struct mg_context ctx;
-	int c[10];
+	int c[TIMERS_IN_TEST * 2];
 	memset(&ctx, 0, sizeof(ctx));
 	memset(c, 0, sizeof(c));
 
@@ -91,11 +110,11 @@ START_TEST(test_timer_cyclic)
 	mark_point();
 
 	c[0] = 100;
-	timer_add(&ctx, 0.05, 0.1, 1, action_dec, c + 0);
+	timer_add(&ctx, 0.05, 0.1, 1, action_dec, c + 0, action_cancel);
 	c[2] = 20;
-	timer_add(&ctx, 0.25, 0.5, 1, action_dec, c + 2);
+	timer_add(&ctx, 0.25, 0.5, 1, action_dec, c + 2, action_cancel);
 	c[1] = 50;
-	timer_add(&ctx, 0.1, 0.2, 1, action_dec, c + 1);
+	timer_add(&ctx, 0.1, 0.2, 1, action_dec, c + 1, action_cancel);
 
 	mark_point();
 
@@ -113,6 +132,10 @@ START_TEST(test_timer_cyclic)
 
 	mark_point();
 
+	mg_sleep(2000); /* Sleep 2 second - timers will not run */
+
+	mark_point();
+
 	/* If this test runs in a virtual environment, like the CI unit test
 	 * containers, there might be some timing deviations, so check the
 	 * counter with some tolerance. */
@@ -123,6 +146,11 @@ START_TEST(test_timer_cyclic)
 	ck_assert_int_le(c[1], +1);
 	ck_assert_int_ge(c[2], -1);
 	ck_assert_int_le(c[2], +1);
+
+	/* Every cancel action must be called once */
+	ck_assert_int_eq(c[0 + TIMERS_IN_TEST], 1);
+	ck_assert_int_eq(c[1 + TIMERS_IN_TEST], 1);
+	ck_assert_int_eq(c[2 + TIMERS_IN_TEST], 1);
 }
 END_TEST
 
@@ -130,7 +158,7 @@ END_TEST
 START_TEST(test_timer_oneshot_by_callback_retval)
 {
 	struct mg_context ctx;
-	int c[10];
+	int c[TIMERS_IN_TEST * 2];
 	memset(&ctx, 0, sizeof(ctx));
 	memset(c, 0, sizeof(c));
 
@@ -142,11 +170,11 @@ START_TEST(test_timer_oneshot_by_callback_retval)
 	mark_point();
 
 	c[0] = 10;
-	timer_add(&ctx, 0, 0.1, 1, action_dec, c + 0);
+	timer_add(&ctx, 0, 0.1, 1, action_dec, c + 0, action_cancel);
 	c[2] = 2;
-	timer_add(&ctx, 0, 0.5, 1, action_dec, c + 2);
+	timer_add(&ctx, 0, 0.5, 1, action_dec, c + 2, action_cancel);
 	c[1] = 5;
-	timer_add(&ctx, 0, 0.2, 1, action_dec, c + 1);
+	timer_add(&ctx, 0, 0.2, 1, action_dec, c + 1, action_cancel);
 
 	mark_point();
 
@@ -163,11 +191,20 @@ START_TEST(test_timer_oneshot_by_callback_retval)
 	timers_exit(&ctx);
 
 	mark_point();
-	mg_sleep(100);
 
+	mg_sleep(2000); /* Sleep 2 second - timers will not run */
+
+	mark_point();
+
+	/* Every decrement action must be called once */
 	ck_assert_int_eq(c[0], 9);
 	ck_assert_int_eq(c[1], 4);
 	ck_assert_int_eq(c[2], 1);
+
+	/* Every cancel action must be called once */
+	ck_assert_int_eq(c[0 + TIMERS_IN_TEST], 1);
+	ck_assert_int_eq(c[1 + TIMERS_IN_TEST], 1);
+	ck_assert_int_eq(c[2 + TIMERS_IN_TEST], 1);
 }
 END_TEST
 
@@ -175,7 +212,7 @@ END_TEST
 START_TEST(test_timer_oneshot_by_timer_add)
 {
 	struct mg_context ctx;
-	int c[10];
+	int c[TIMERS_IN_TEST * 2];
 	memset(&ctx, 0, sizeof(ctx));
 	memset(c, 0, sizeof(c));
 
@@ -187,11 +224,11 @@ START_TEST(test_timer_oneshot_by_timer_add)
 	mark_point();
 
 	c[0] = 10;
-	timer_add(&ctx, 0, 0, 1, action_dec, c + 0);
+	timer_add(&ctx, 0, 0, 1, action_dec, c + 0, action_cancel);
 	c[2] = 2;
-	timer_add(&ctx, 0, 0, 1, action_dec, c + 2);
+	timer_add(&ctx, 0, 0, 1, action_dec, c + 2, action_cancel);
 	c[1] = 5;
-	timer_add(&ctx, 0, 0, 1, action_dec, c + 1);
+	timer_add(&ctx, 0, 0, 1, action_dec, c + 1, action_cancel);
 
 	mark_point();
 
@@ -208,11 +245,20 @@ START_TEST(test_timer_oneshot_by_timer_add)
 	timers_exit(&ctx);
 
 	mark_point();
-	mg_sleep(100);
 
+	mg_sleep(2000); /* Sleep 2 second - timers will not run */
+
+	mark_point();
+
+	/* Every decrement action was called once */
 	ck_assert_int_eq(c[0], 9);
 	ck_assert_int_eq(c[1], 4);
 	ck_assert_int_eq(c[2], 1);
+
+	/* Every cancel action must be called once */
+	ck_assert_int_eq(c[0 + TIMERS_IN_TEST], 1);
+	ck_assert_int_eq(c[1 + TIMERS_IN_TEST], 1);
+	ck_assert_int_eq(c[2 + TIMERS_IN_TEST], 1);
 }
 END_TEST
 
@@ -220,7 +266,7 @@ END_TEST
 START_TEST(test_timer_mixed)
 {
 	struct mg_context ctx;
-	int c[10];
+	int c[TIMERS_IN_TEST];
 	memset(&ctx, 0, sizeof(ctx));
 	memset(c, 0, sizeof(c));
 
@@ -231,35 +277,35 @@ START_TEST(test_timer_mixed)
 
 	/* 3 --> 2, because it is a single shot timer */
 	c[0] = 3;
-	timer_add(&ctx, 0, 0, 1, action_dec_to_0, &c[0]);
+	timer_add(&ctx, 0, 0, 1, action_dec_to_0, &c[0], NULL);
 
 	/* 3 --> 0, because it will run until c[1] = 0 and then stop */
 	c[1] = 3;
-	timer_add(&ctx, 0, 0.2, 1, action_dec_to_0, &c[1]);
+	timer_add(&ctx, 0, 0.2, 1, action_dec_to_0, &c[1], NULL);
 
 	/* 3 --> 1, with 750 ms period, it will run once at start,
 	 * then once 750 ms later, but not 1500 ms later, since the
 	 * timer is already stopped then. */
 	c[2] = 3;
-	timer_add(&ctx, 0, 0.75, 1, action_dec_to_0, &c[2]);
+	timer_add(&ctx, 0, 0.75, 1, action_dec_to_0, &c[2], NULL);
 
 	/* 3 --> 2, will run at start, but no cyclic in 1 second */
 	c[3] = 3;
-	timer_add(&ctx, 0, 2.5, 1, action_dec_to_0, &c[3]);
+	timer_add(&ctx, 0, 2.5, 1, action_dec_to_0, &c[3], NULL);
 
 	/* 3 --> 3, will not run at start */
 	c[4] = 3;
-	timer_add(&ctx, 2.5, 0.1, 1, action_dec_to_0, &c[4]);
+	timer_add(&ctx, 2.5, 0.1, 1, action_dec_to_0, &c[4], NULL);
 
 	/* 3 --> 2, an absolute timer in the past (-123.456) will still
 	 * run once at start, and then with the period */
 	c[5] = 3;
-	timer_add(&ctx, -123.456, 2.5, 0, action_dec_to_0, &c[5]);
+	timer_add(&ctx, -123.456, 2.5, 0, action_dec_to_0, &c[5], NULL);
 
 	/* 3 --> 1, an absolute timer in the past (-123.456) will still
 	 * run once at start, and then with the period */
 	c[6] = 3;
-	timer_add(&ctx, -123.456, 0.75, 0, action_dec_to_0, &c[6]);
+	timer_add(&ctx, -123.456, 0.75, 0, action_dec_to_0, &c[6], NULL);
 
 	mark_point();
 
@@ -276,7 +322,10 @@ START_TEST(test_timer_mixed)
 	timers_exit(&ctx);
 
 	mark_point();
-	mg_sleep(100);
+
+	mg_sleep(2000); /* Sleep 2 second - timers will not run */
+
+	mark_point();
 
 	ck_assert_int_eq(c[0], 2);
 	ck_assert_int_eq(c[1], 0);
@@ -314,14 +363,12 @@ make_timertest_suite(void)
 
 	return suite;
 }
-#endif
 
+#else
 
-#ifdef REPLACE_CHECK_FOR_LOCAL_DEBUGGING
 /* Used to debug test cases without using the check framework */
-
 void
-TIMER_PRIVATE(void)
+MAIN_TIMER_PRIVATE(void)
 {
 	unsigned f_avail;
 	unsigned f_ret;

Niektóre pliki nie zostały wyświetlone z powodu dużej ilości zmienionych plików