浏览代码

Merge branch 'master' of https://github.com/bel2125/civetweb

Maarten Fremouw 10 年之前
父节点
当前提交
cb52bd2cf3

+ 2 - 0
.gitignore

@@ -10,6 +10,8 @@ out
 *.zip
 *.zip
 Output
 Output
 
 
+*.o
+
 #################
 #################
 ## Eclipse
 ## Eclipse
 #################
 #################

+ 4 - 2
CREDITS.md

@@ -2,20 +2,22 @@
 
 
 * Alex Kozlov
 * Alex Kozlov
 * bel2125
 * bel2125
+* Brian Spratke
 * celeron55
 * celeron55
 * Daniel Oaks
 * Daniel Oaks
 * Danny Al-Gaaf
 * Danny Al-Gaaf
 * F-Secure Corporation
 * F-Secure Corporation
-* Brian Spratke
 * HariKamath Kamath
 * HariKamath Kamath
+* Jordan Shelley
 * Kimmo Mustonen
 * Kimmo Mustonen
 * Morgan McGuire
 * Morgan McGuire
 * No Face Press
 * No Face Press
 * Paul Sokolovsky
 * Paul Sokolovsky
+* Richard Screene
 * Thomas Davis
 * Thomas Davis
 * Toni Wilk
 * Toni Wilk
 * William Greathouse
 * William Greathouse
-* Jordan Shelley
+* Yehuda Sadeh
 
 
 # Mongoose Contributors
 # Mongoose Contributors
 Civetweb is based on the Mongoose code.  The following users contributed to the original Mongoose release between 2010 and 2013.  This list was generated from the Mongoose GIT logs.  There is no record for contributors prior to 2010.
 Civetweb is based on the Mongoose code.  The following users contributed to the original Mongoose release between 2010 and 2013.  This list was generated from the Mongoose GIT logs.  There is no record for contributors prior to 2010.

+ 15 - 14
Makefile

@@ -56,8 +56,8 @@ endif
 
 
 # only set main compile options if none were chosen
 # only set main compile options if none were chosen
 CFLAGS += -W -Wall -O2 -D$(TARGET_OS) -Iinclude $(COPT) -DUSE_STACK_SIZE=102400
 CFLAGS += -W -Wall -O2 -D$(TARGET_OS) -Iinclude $(COPT) -DUSE_STACK_SIZE=102400
-
-LIBS = -lpthread -lm
+
+LIBS = -lpthread -lm
 
 
 ifdef WITH_DEBUG
 ifdef WITH_DEBUG
   CFLAGS += -g -DDEBUG_ENABLED
   CFLAGS += -g -DDEBUG_ENABLED
@@ -71,10 +71,10 @@ ifdef WITH_CPP
 else
 else
   LCC = $(CC)
   LCC = $(CC)
 endif
 endif
-
-ifdef WITH_LUA_SHARED
-  WITH_LUA = 1
-endif
+
+ifdef WITH_LUA_SHARED
+  WITH_LUA = 1
+endif
 
 
 ifdef WITH_LUA
 ifdef WITH_LUA
   include resources/Makefile.in-lua
   include resources/Makefile.in-lua
@@ -85,10 +85,10 @@ ifdef WITH_IPV6
 endif
 endif
 
 
 ifdef WITH_WEBSOCKET
 ifdef WITH_WEBSOCKET
-  CFLAGS += -DUSE_WEBSOCKET
-  ifdef WITH_LUA
-    CFLAGS += -DUSE_TIMERS
-    LIBS += -lrt
+  CFLAGS += -DUSE_WEBSOCKET
+  ifdef WITH_LUA
+    CFLAGS += -DUSE_TIMERS
+    LIBS += -lrt
   endif
   endif
 endif
 endif
 
 
@@ -119,10 +119,10 @@ endif
 
 
 ifeq ($(TARGET_OS),LINUX)
 ifeq ($(TARGET_OS),LINUX)
   CAN_INSTALL = 1
   CAN_INSTALL = 1
-endif
-
-ifdef WITH_LUA_SHARED
-  LIBS += -llua5.2
+endif
+
+ifdef WITH_LUA_SHARED
+  LIBS += -llua5.2
 endif
 endif
 
 
 ifneq (, $(findstring MINGW32, $(UNAME)))
 ifneq (, $(findstring MINGW32, $(UNAME)))
@@ -165,6 +165,7 @@ help:
 	@echo "   NO_CGI                disable CGI support"
 	@echo "   NO_CGI                disable CGI support"
 	@echo "   NO_SSL                disable SSL functionality"
 	@echo "   NO_SSL                disable SSL functionality"
 	@echo "   NO_SSL_DL             link against system libssl library"
 	@echo "   NO_SSL_DL             link against system libssl library"
+	@echo "   NO_FILES              do not serve files from a directory"
 	@echo "   MAX_REQUEST_SIZE      maximum header size, default 16384"
 	@echo "   MAX_REQUEST_SIZE      maximum header size, default 16384"
 	@echo ""
 	@echo ""
 	@echo " Variables"
 	@echo " Variables"

+ 14 - 0
RELEASE_NOTES.md

@@ -5,9 +5,23 @@ Release Notes v1.7 (Under Development)
 Changes
 Changes
 -------
 -------
 
 
+- Return more differentiated HTTP error codes
+- Add log_access callback
+- Rewrite and comment request handling function
+- Specify in detail and document return values of callback functions
+- Set names for all threads (unless NO_THREAD_NAME is defined)
+- New API functions for TCP/HTTP clients
+- Fix upload of huge files
+- Allow multiple SSL instances within one application
+- Improve API and user documentation
+- Allow to chose between static and dynamic Lua library
+- Improve unit test
+- Use temporary file name for partially uploaded files
+- Additional API functions exported to C++
 - Add a websocket client example
 - Add a websocket client example
 - Add a websocket client API
 - Add a websocket client API
 - Update websocket example
 - Update websocket example
+- Make content length available in request_info
 - New API functions: access context, callback for create/delete, access user data
 - New API functions: access context, callback for create/delete, access user data
 - Upgraded Lua from 5.2.2 to 5.2.3
 - Upgraded Lua from 5.2.2 to 5.2.3
 - Integrate LuaXML
 - Integrate LuaXML

+ 39 - 38
VS2012/lua_lib/lua_lib.vcxproj

@@ -28,7 +28,7 @@
     <ConfigurationType>StaticLibrary</ConfigurationType>
     <ConfigurationType>StaticLibrary</ConfigurationType>
     <UseDebugLibraries>true</UseDebugLibraries>
     <UseDebugLibraries>true</UseDebugLibraries>
     <CharacterSet>Unicode</CharacterSet>
     <CharacterSet>Unicode</CharacterSet>
-    <PlatformToolset>v110_xp</PlatformToolset>
+    <PlatformToolset>v100</PlatformToolset>
   </PropertyGroup>
   </PropertyGroup>
   <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
   <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
     <ConfigurationType>StaticLibrary</ConfigurationType>
     <ConfigurationType>StaticLibrary</ConfigurationType>
@@ -41,7 +41,7 @@
     <UseDebugLibraries>false</UseDebugLibraries>
     <UseDebugLibraries>false</UseDebugLibraries>
     <WholeProgramOptimization>true</WholeProgramOptimization>
     <WholeProgramOptimization>true</WholeProgramOptimization>
     <CharacterSet>Unicode</CharacterSet>
     <CharacterSet>Unicode</CharacterSet>
-    <PlatformToolset>v110_xp</PlatformToolset>
+    <PlatformToolset>v100</PlatformToolset>
   </PropertyGroup>
   </PropertyGroup>
   <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
   <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
     <ConfigurationType>StaticLibrary</ConfigurationType>
     <ConfigurationType>StaticLibrary</ConfigurationType>
@@ -85,7 +85,7 @@
       <WarningLevel>Level3</WarningLevel>
       <WarningLevel>Level3</WarningLevel>
       <Optimization>Disabled</Optimization>
       <Optimization>Disabled</Optimization>
       <PreprocessorDefinitions>LUA_COMPAT_ALL;THREADSAFE=1;SQLITE_ENABLE_FTS3;SQLITE_ENABLE_FTS3_PARENTHESIS;WIN32;_DEBUG;_WINDOWS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
       <PreprocessorDefinitions>LUA_COMPAT_ALL;THREADSAFE=1;SQLITE_ENABLE_FTS3;SQLITE_ENABLE_FTS3_PARENTHESIS;WIN32;_DEBUG;_WINDOWS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
-      <AdditionalIncludeDirectories>$(ProjectDir)..\..\src\third_party\lua-5.2.2\src;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+      <AdditionalIncludeDirectories>$(ProjectDir)..\..\src\third_party\lua-5.2.3\src;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
     </ClCompile>
     </ClCompile>
     <Link>
     <Link>
       <SubSystem>Windows</SubSystem>
       <SubSystem>Windows</SubSystem>
@@ -99,7 +99,7 @@
       <WarningLevel>Level3</WarningLevel>
       <WarningLevel>Level3</WarningLevel>
       <Optimization>Disabled</Optimization>
       <Optimization>Disabled</Optimization>
       <PreprocessorDefinitions>LUA_COMPAT_ALL;THREADSAFE=1;SQLITE_ENABLE_FTS3;SQLITE_ENABLE_FTS3_PARENTHESIS;WIN32;_DEBUG;_WINDOWS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
       <PreprocessorDefinitions>LUA_COMPAT_ALL;THREADSAFE=1;SQLITE_ENABLE_FTS3;SQLITE_ENABLE_FTS3_PARENTHESIS;WIN32;_DEBUG;_WINDOWS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
-      <AdditionalIncludeDirectories>$(ProjectDir)..\..\src\third_party\lua-5.2.2\src;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+      <AdditionalIncludeDirectories>$(ProjectDir)..\..\src\third_party\lua-5.2.3\src;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
     </ClCompile>
     </ClCompile>
     <Link>
     <Link>
       <SubSystem>Windows</SubSystem>
       <SubSystem>Windows</SubSystem>
@@ -115,7 +115,7 @@
       <FunctionLevelLinking>true</FunctionLevelLinking>
       <FunctionLevelLinking>true</FunctionLevelLinking>
       <IntrinsicFunctions>true</IntrinsicFunctions>
       <IntrinsicFunctions>true</IntrinsicFunctions>
       <PreprocessorDefinitions>LUA_COMPAT_ALL;THREADSAFE=1;SQLITE_ENABLE_FTS3;SQLITE_ENABLE_FTS3_PARENTHESIS;WIN32;NDEBUG;_WINDOWS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
       <PreprocessorDefinitions>LUA_COMPAT_ALL;THREADSAFE=1;SQLITE_ENABLE_FTS3;SQLITE_ENABLE_FTS3_PARENTHESIS;WIN32;NDEBUG;_WINDOWS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
-      <AdditionalIncludeDirectories>$(ProjectDir)..\..\src\third_party\lua-5.2.2\src;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+      <AdditionalIncludeDirectories>$(ProjectDir)..\..\src\third_party\lua-5.2.3\src;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
     </ClCompile>
     </ClCompile>
     <Link>
     <Link>
       <SubSystem>Windows</SubSystem>
       <SubSystem>Windows</SubSystem>
@@ -133,7 +133,7 @@
       <FunctionLevelLinking>true</FunctionLevelLinking>
       <FunctionLevelLinking>true</FunctionLevelLinking>
       <IntrinsicFunctions>true</IntrinsicFunctions>
       <IntrinsicFunctions>true</IntrinsicFunctions>
       <PreprocessorDefinitions>LUA_COMPAT_ALL;THREADSAFE=1;SQLITE_ENABLE_FTS3;SQLITE_ENABLE_FTS3_PARENTHESIS;WIN32;NDEBUG;_WINDOWS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
       <PreprocessorDefinitions>LUA_COMPAT_ALL;THREADSAFE=1;SQLITE_ENABLE_FTS3;SQLITE_ENABLE_FTS3_PARENTHESIS;WIN32;NDEBUG;_WINDOWS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
-      <AdditionalIncludeDirectories>$(ProjectDir)..\..\src\third_party\lua-5.2.2\src;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+      <AdditionalIncludeDirectories>$(ProjectDir)..\..\src\third_party\lua-5.2.3\src;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
     </ClCompile>
     </ClCompile>
     <Link>
     <Link>
       <SubSystem>Windows</SubSystem>
       <SubSystem>Windows</SubSystem>
@@ -144,39 +144,40 @@
   </ItemDefinitionGroup>
   </ItemDefinitionGroup>
   <ItemGroup>
   <ItemGroup>
     <ClCompile Include="..\..\src\third_party\lfs.c" />
     <ClCompile Include="..\..\src\third_party\lfs.c" />
-    <ClCompile Include="..\..\src\third_party\lua-5.2.2\src\lapi.c" />
-    <ClCompile Include="..\..\src\third_party\lua-5.2.2\src\lauxlib.c" />
-    <ClCompile Include="..\..\src\third_party\lua-5.2.2\src\lbaselib.c" />
-    <ClCompile Include="..\..\src\third_party\lua-5.2.2\src\lbitlib.c" />
-    <ClCompile Include="..\..\src\third_party\lua-5.2.2\src\lcode.c" />
-    <ClCompile Include="..\..\src\third_party\lua-5.2.2\src\lcorolib.c" />
-    <ClCompile Include="..\..\src\third_party\lua-5.2.2\src\lctype.c" />
-    <ClCompile Include="..\..\src\third_party\lua-5.2.2\src\ldblib.c" />
-    <ClCompile Include="..\..\src\third_party\lua-5.2.2\src\ldebug.c" />
-    <ClCompile Include="..\..\src\third_party\lua-5.2.2\src\ldo.c" />
-    <ClCompile Include="..\..\src\third_party\lua-5.2.2\src\ldump.c" />
-    <ClCompile Include="..\..\src\third_party\lua-5.2.2\src\lfunc.c" />
-    <ClCompile Include="..\..\src\third_party\lua-5.2.2\src\lgc.c" />
-    <ClCompile Include="..\..\src\third_party\lua-5.2.2\src\linit.c" />
-    <ClCompile Include="..\..\src\third_party\lua-5.2.2\src\liolib.c" />
-    <ClCompile Include="..\..\src\third_party\lua-5.2.2\src\llex.c" />
-    <ClCompile Include="..\..\src\third_party\lua-5.2.2\src\lmathlib.c" />
-    <ClCompile Include="..\..\src\third_party\lua-5.2.2\src\lmem.c" />
-    <ClCompile Include="..\..\src\third_party\lua-5.2.2\src\loadlib.c" />
-    <ClCompile Include="..\..\src\third_party\lua-5.2.2\src\lobject.c" />
-    <ClCompile Include="..\..\src\third_party\lua-5.2.2\src\lopcodes.c" />
-    <ClCompile Include="..\..\src\third_party\lua-5.2.2\src\loslib.c" />
-    <ClCompile Include="..\..\src\third_party\lua-5.2.2\src\lparser.c" />
-    <ClCompile Include="..\..\src\third_party\lua-5.2.2\src\lstate.c" />
-    <ClCompile Include="..\..\src\third_party\lua-5.2.2\src\lstring.c" />
-    <ClCompile Include="..\..\src\third_party\lua-5.2.2\src\lstrlib.c" />
-    <ClCompile Include="..\..\src\third_party\lua-5.2.2\src\ltable.c" />
-    <ClCompile Include="..\..\src\third_party\lua-5.2.2\src\ltablib.c" />
-    <ClCompile Include="..\..\src\third_party\lua-5.2.2\src\ltm.c" />
-    <ClCompile Include="..\..\src\third_party\lua-5.2.2\src\lundump.c" />
-    <ClCompile Include="..\..\src\third_party\lua-5.2.2\src\lvm.c" />
-    <ClCompile Include="..\..\src\third_party\lua-5.2.2\src\lzio.c" />
+    <ClCompile Include="..\..\src\third_party\lua-5.2.3\src\lapi.c" />
+    <ClCompile Include="..\..\src\third_party\lua-5.2.3\src\lauxlib.c" />
+    <ClCompile Include="..\..\src\third_party\lua-5.2.3\src\lbaselib.c" />
+    <ClCompile Include="..\..\src\third_party\lua-5.2.3\src\lbitlib.c" />
+    <ClCompile Include="..\..\src\third_party\lua-5.2.3\src\lcode.c" />
+    <ClCompile Include="..\..\src\third_party\lua-5.2.3\src\lcorolib.c" />
+    <ClCompile Include="..\..\src\third_party\lua-5.2.3\src\lctype.c" />
+    <ClCompile Include="..\..\src\third_party\lua-5.2.3\src\ldblib.c" />
+    <ClCompile Include="..\..\src\third_party\lua-5.2.3\src\ldebug.c" />
+    <ClCompile Include="..\..\src\third_party\lua-5.2.3\src\ldo.c" />
+    <ClCompile Include="..\..\src\third_party\lua-5.2.3\src\ldump.c" />
+    <ClCompile Include="..\..\src\third_party\lua-5.2.3\src\lfunc.c" />
+    <ClCompile Include="..\..\src\third_party\lua-5.2.3\src\lgc.c" />
+    <ClCompile Include="..\..\src\third_party\lua-5.2.3\src\linit.c" />
+    <ClCompile Include="..\..\src\third_party\lua-5.2.3\src\liolib.c" />
+    <ClCompile Include="..\..\src\third_party\lua-5.2.3\src\llex.c" />
+    <ClCompile Include="..\..\src\third_party\lua-5.2.3\src\lmathlib.c" />
+    <ClCompile Include="..\..\src\third_party\lua-5.2.3\src\lmem.c" />
+    <ClCompile Include="..\..\src\third_party\lua-5.2.3\src\loadlib.c" />
+    <ClCompile Include="..\..\src\third_party\lua-5.2.3\src\lobject.c" />
+    <ClCompile Include="..\..\src\third_party\lua-5.2.3\src\lopcodes.c" />
+    <ClCompile Include="..\..\src\third_party\lua-5.2.3\src\loslib.c" />
+    <ClCompile Include="..\..\src\third_party\lua-5.2.3\src\lparser.c" />
+    <ClCompile Include="..\..\src\third_party\lua-5.2.3\src\lstate.c" />
+    <ClCompile Include="..\..\src\third_party\lua-5.2.3\src\lstring.c" />
+    <ClCompile Include="..\..\src\third_party\lua-5.2.3\src\lstrlib.c" />
+    <ClCompile Include="..\..\src\third_party\lua-5.2.3\src\ltable.c" />
+    <ClCompile Include="..\..\src\third_party\lua-5.2.3\src\ltablib.c" />
+    <ClCompile Include="..\..\src\third_party\lua-5.2.3\src\ltm.c" />
+    <ClCompile Include="..\..\src\third_party\lua-5.2.3\src\lundump.c" />
+    <ClCompile Include="..\..\src\third_party\lua-5.2.3\src\lvm.c" />
+    <ClCompile Include="..\..\src\third_party\lua-5.2.3\src\lzio.c" />
     <ClCompile Include="..\..\src\third_party\lsqlite3.c" />
     <ClCompile Include="..\..\src\third_party\lsqlite3.c" />
+    <ClCompile Include="..\..\src\third_party\LuaXML_lib.c" />
     <ClCompile Include="..\..\src\third_party\sqlite3.c" />
     <ClCompile Include="..\..\src\third_party\sqlite3.c" />
   </ItemGroup>
   </ItemGroup>
   <ItemGroup>
   <ItemGroup>

+ 2 - 2
VS2012/upload/upload.vcxproj

@@ -95,7 +95,7 @@
       </PrecompiledHeader>
       </PrecompiledHeader>
       <WarningLevel>Level3</WarningLevel>
       <WarningLevel>Level3</WarningLevel>
       <Optimization>Disabled</Optimization>
       <Optimization>Disabled</Optimization>
-      <PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <PreprocessorDefinitions>NO_FILES;WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
       <AdditionalIncludeDirectories>$(ProjectDir)..\..\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
       <AdditionalIncludeDirectories>$(ProjectDir)..\..\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
     </ClCompile>
     </ClCompile>
     <Link>
     <Link>
@@ -125,7 +125,7 @@
       <Optimization>MaxSpeed</Optimization>
       <Optimization>MaxSpeed</Optimization>
       <FunctionLevelLinking>true</FunctionLevelLinking>
       <FunctionLevelLinking>true</FunctionLevelLinking>
       <IntrinsicFunctions>true</IntrinsicFunctions>
       <IntrinsicFunctions>true</IntrinsicFunctions>
-      <PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <PreprocessorDefinitions>NO_FILES;WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
       <AdditionalIncludeDirectories>$(ProjectDir)..\..\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
       <AdditionalIncludeDirectories>$(ProjectDir)..\..\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
     </ClCompile>
     </ClCompile>
     <Link>
     <Link>

+ 2 - 0
docs/Building.md

@@ -96,7 +96,9 @@ make build COPT="-DNDEBUG -DNO_CGI"
 | NO_CGI                    | disable CGI support                  |
 | NO_CGI                    | disable CGI support                  |
 | NO_SSL                    | disable SSL functionality            |
 | NO_SSL                    | disable SSL functionality            |
 | NO_SSL_DL                 | link against system libssl library   |
 | NO_SSL_DL                 | link against system libssl library   |
+| NO_FILES                  | do not serve files from a directory  |
 | SQLITE_DISABLE_LFS        | disables large files (Lua only)      |
 | SQLITE_DISABLE_LFS        | disables large files (Lua only)      |
+| SSL_ALREADY_INITIALIZED   | do not initialize libcrypto          |
 
 
 ## Cross Compiling
 ## Cross Compiling
 
 

+ 4 - 0
docs/Embedding.md

@@ -125,6 +125,10 @@ two threads: a master thread, that accepts new connections, and several
 worker threads, that process accepted connections. The number of worker threads
 worker threads, that process accepted connections. The number of worker threads
 is configurable via `num_threads` configuration option. That number puts a
 is configurable via `num_threads` configuration option. That number puts a
 limit on number of simultaneous requests that can be handled by civetweb.
 limit on number of simultaneous requests that can be handled by civetweb.
+If you embed civetweb into a program that uses SSL outside civetweb as well,
+you may need to initialize SSL before calling `mg_start()`, and set the pre-
+processor define SSL_ALREADY_INITIALIZED. This is not required if SSL is used
+only within civetweb.
 
 
 When master thread accepts new connection, a new accepted socket (described by
 When master thread accepts new connection, a new accepted socket (described by
 `struct socket`) it placed into the accepted sockets queue,
 `struct socket`) it placed into the accepted sockets queue,

+ 5 - 2
docs/UserManual.md

@@ -279,7 +279,7 @@ For example, to bind to a loopback interface on port 80 and to
 all interfaces on HTTPS port 443, use `127.0.0.1:80,443s`.
 all interfaces on HTTPS port 443, use `127.0.0.1:80,443s`.
 
 
 ### document\_root `.`
 ### document\_root `.`
-A directory to serve. By default, the current workubg directory is served.
+A directory to serve. By default, the current working directory is served.
 The current directory is commonly referenced as dot (`.`).
 The current directory is commonly referenced as dot (`.`).
 
 
 ### ssl\_certificate
 ### ssl\_certificate
@@ -416,7 +416,10 @@ Note that this example uses function `mg.write()`, which sends data to the
 web client. Using `mg.write()` is the way to generate web content from inside
 web client. Using `mg.write()` is the way to generate web content from inside
 Lua code. In addition to `mg.write()`, all standard Lua library functions
 Lua code. In addition to `mg.write()`, all standard Lua library functions
 are accessible from the Lua code (please check the reference manual for
 are accessible from the Lua code (please check the reference manual for
-details). Information on the request is available in the `mg.request_info`
+details). Lua functions working on files (e.g., `io.open`) use a path
+relative to the working path of the civetweb process. The web server content
+is located in the path `mg.document_root`.
+Information on the request is available in the `mg.request_info`
 object, like the request method, all HTTP headers, etcetera.
 object, like the request method, all HTTP headers, etcetera.
 
 
 [page2.lua](https://github.com/bel2125/civetweb/blob/master/test/page2.lua)
 [page2.lua](https://github.com/bel2125/civetweb/blob/master/test/page2.lua)

+ 2 - 2
examples/docroot/index.html

@@ -2,7 +2,7 @@
   "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> 
   "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> 
 <html xmlns="http://www.w3.org/1999/xhtml" lang="en" dir="ltr"> 
 <html xmlns="http://www.w3.org/1999/xhtml" lang="en" dir="ltr"> 
   <!-- This file is part of the  Civetweb project,
   <!-- This file is part of the  Civetweb project,
-    http://code.google.com/p/civetweb -->
+    http://sourceforge.net/projects/civetweb/ -->
   <head>
   <head>
     <title>Civetweb chat server</title>
     <title>Civetweb chat server</title>
     <meta http-equiv="Content-Type" content="text/html;charset=utf-8"/>
     <meta http-equiv="Content-Type" content="text/html;charset=utf-8"/>
@@ -16,7 +16,7 @@
     <div id="logo"></div>
     <div id="logo"></div>
     <div class="rounded infobox help-message" id="motd">
     <div class="rounded infobox help-message" id="motd">
       Chat room implemented using
       Chat room implemented using
-      <a href="http://code.google.com/p/civetweb" target="_blank">Civetweb</a>
+      <a href="http://sourceforge.net/projects/civetweb/" target="_blank">Civetweb</a>
       embeddable web server.
       embeddable web server.
       This application was written for educational purposes demonstrating
       This application was written for educational purposes demonstrating
       how web interface could be decoupled from the business logic. Not a
       how web interface could be decoupled from the business logic. Not a

+ 1 - 1
examples/docroot/login.html

@@ -2,7 +2,7 @@
   "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> 
   "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> 
 <html xmlns="http://www.w3.org/1999/xhtml" lang="en" dir="ltr"> 
 <html xmlns="http://www.w3.org/1999/xhtml" lang="en" dir="ltr"> 
   <!-- This file is part of the  Civetweb project,
   <!-- This file is part of the  Civetweb project,
-    http://code.google.com/p/civetweb -->
+    http://sourceforge.net/projects/civetweb/ -->
   <head>
   <head>
     <title>Civetweb chat: login</title>
     <title>Civetweb chat: login</title>
     <meta http-equiv="Content-Type" content="text/html;charset=utf-8"/>
     <meta http-equiv="Content-Type" content="text/html;charset=utf-8"/>

+ 2 - 1
examples/docroot/main.js

@@ -1,4 +1,5 @@
-// This file is part of Civetweb project, http://code.google.com/p/civetweb
+// This file is part of Civetweb project,
+// http://sourceforge.net/projects/civetweb/
 
 
 var chat = {
 var chat = {
   // Backend URL, string.
   // Backend URL, string.

+ 34 - 6
examples/upload/upload.c

@@ -1,5 +1,7 @@
-// Copyright (c) 2004-2012 Sergey Lyubka
-// This file is a part of civetweb project, http://github.com/bel2125/civetweb
+/* Copyright (c) 2014 the Civetweb developers
+ * Copyright (c) 2004-2012 Sergey Lyubka
+ * This file is a part of civetweb project, http://github.com/bel2125/civetweb
+ */
 
 
 #include <stdio.h>
 #include <stdio.h>
 #include <string.h>
 #include <string.h>
@@ -17,20 +19,28 @@ typedef __int64 int64_t;
 
 
 #include "civetweb.h"
 #include "civetweb.h"
 
 
+
+/* callback: used to generate all content */
 static int begin_request_handler(struct mg_connection *conn)
 static int begin_request_handler(struct mg_connection *conn)
 {
 {
     if (!strcmp(mg_get_request_info(conn)->uri, "/handle_post_request")) {
     if (!strcmp(mg_get_request_info(conn)->uri, "/handle_post_request")) {
         mg_printf(conn, "%s", "HTTP/1.0 200 OK\r\n\r\n");
         mg_printf(conn, "%s", "HTTP/1.0 200 OK\r\n\r\n");
         mg_upload(conn, "/tmp");
         mg_upload(conn, "/tmp");
     } else {
     } else {
-        // Show HTML form. Make sure it has enctype="multipart/form-data" attr.
+        /* Show HTML form. */
+        /* See http://www.w3.org/TR/html401/interact/forms.html#h-17.13.4.1 */
         static const char *html_form =
         static const char *html_form =
             "<html><body>Upload example."
             "<html><body>Upload example."
+            ""
+            /* enctype="multipart/form-data" */
             "<form method=\"POST\" action=\"/handle_post_request\" "
             "<form method=\"POST\" action=\"/handle_post_request\" "
             "  enctype=\"multipart/form-data\">"
             "  enctype=\"multipart/form-data\">"
             "<input type=\"file\" name=\"file\" /> <br/>"
             "<input type=\"file\" name=\"file\" /> <br/>"
+            "<input type=\"file\" name=\"file2\" /> <br/>"
             "<input type=\"submit\" value=\"Upload\" />"
             "<input type=\"submit\" value=\"Upload\" />"
-            "</form></body></html>";
+            "</form>"
+            ""
+            "</body></html>";
 
 
         mg_printf(conn, "HTTP/1.0 200 OK\r\n"
         mg_printf(conn, "HTTP/1.0 200 OK\r\n"
                   "Content-Length: %d\r\n"
                   "Content-Length: %d\r\n"
@@ -42,22 +52,40 @@ static int begin_request_handler(struct mg_connection *conn)
     return 1;
     return 1;
 }
 }
 
 
+
+/* callback: called after uploading a file is completed */
 static void upload_handler(struct mg_connection *conn, const char *path)
 static void upload_handler(struct mg_connection *conn, const char *path)
 {
 {
     mg_printf(conn, "Saved [%s]", path);
     mg_printf(conn, "Saved [%s]", path);
 }
 }
 
 
+
+/* Main program: Set callbacks and start the server.  */
 int main(void)
 int main(void)
 {
 {
+    /* Test server will use this port */
+    const char * PORT = "8080";
+
+    /* Startup options for the server */
     struct mg_context *ctx;
     struct mg_context *ctx;
-    const char *options[] = {"listening_ports", "8080", NULL};
+    const char *options[] = {
+        "listening_ports", PORT,
+        NULL};
     struct mg_callbacks callbacks;
     struct mg_callbacks callbacks;
 
 
     memset(&callbacks, 0, sizeof(callbacks));
     memset(&callbacks, 0, sizeof(callbacks));
     callbacks.begin_request = begin_request_handler;
     callbacks.begin_request = begin_request_handler;
     callbacks.upload = upload_handler;
     callbacks.upload = upload_handler;
+
+    /* Display a welcome message */
+    printf("File upload demo.\n");
+    printf("Open http://localhost:%s/ im your browser.\n\n", PORT);
+
+    /* Start the server */
     ctx = mg_start(&callbacks, NULL, options);
     ctx = mg_start(&callbacks, NULL, options);
-    getchar();  // Wait until user hits "enter"
+
+    /* Wait until thr user hits "enter", then stop the server */
+    getchar();
     mg_stop(ctx);
     mg_stop(ctx);
 
 
     return 0;
     return 0;

+ 8 - 0
include/CivetServer.h

@@ -65,6 +65,14 @@ public:
      */
      */
     virtual bool handleDelete(CivetServer *server, struct mg_connection *conn);
     virtual bool handleDelete(CivetServer *server, struct mg_connection *conn);
 
 
+    /**
+     * Callback method for OPTIONS request.
+     *
+     * @param server - the calling server
+     * @param conn - the connection information
+     * @returns true if implemented, false otherwise
+     */
+    virtual bool handleOptions(CivetServer *server, struct mg_connection *conn);
 };
 };
 
 
 /**
 /**

+ 83 - 26
include/civetweb.h

@@ -1,4 +1,4 @@
-/* Copyright (c) 2013-2014 the Civetweb developers
+/* Copyright (c) 2013-2015 the Civetweb developers
  * Copyright (c) 2004-2013 Sergey Lyubka
  * Copyright (c) 2004-2013 Sergey Lyubka
  *
  *
  * Permission is hereby granted, free of charge, to any person obtaining a copy
  * Permission is hereby granted, free of charge, to any person obtaining a copy
@@ -61,9 +61,9 @@ struct mg_request_info {
                                    NULL */
                                    NULL */
     const char *remote_user;    /* Authenticated user, or NULL if no auth
     const char *remote_user;    /* Authenticated user, or NULL if no auth
                                    used */
                                    used */
-    char remote_addr[48];       /* Client's IP address as a string. */
-    long remote_ip;             /* Client's IP address. Deprecated: use remote_addr instead */
-
+    char remote_addr[48];       /* Client's IP address as a string. */
+    long remote_ip;             /* Client's IP address. Deprecated: use remote_addr instead */
+
     long long content_length;   /* Length (in bytes) of the request body,
     long long content_length;   /* Length (in bytes) of the request body,
                                    can be -1 if no length was given. */
                                    can be -1 if no length was given. */
     int remote_port;            /* Client's port */
     int remote_port;            /* Client's port */
@@ -80,15 +80,21 @@ struct mg_request_info {
 
 
 
 
 /* This structure needs to be passed to mg_start(), to let civetweb know
 /* This structure needs to be passed to mg_start(), to let civetweb know
-   which callbacks to invoke. For detailed description, see
+   which callbacks to invoke. For a detailed description, see
    https://github.com/bel2125/civetweb/blob/master/docs/UserManual.md */
    https://github.com/bel2125/civetweb/blob/master/docs/UserManual.md */
 struct mg_callbacks {
 struct mg_callbacks {
     /* Called when civetweb has received new HTTP request.
     /* Called when civetweb has received new HTTP request.
-       If callback returns non-zero,
-       callback must process the request by sending valid HTTP headers and
-       body, and civetweb will not do any further processing.
-       If callback returns 0, civetweb processes the request itself. In this
-       case, callback must not send any data to the client. */
+       If the callback returns one, it must process the request
+       by sending valid HTTP headers and a body. Civetweb will not do
+       any further processing. Otherwise it must return zero.
+       Note that since V1.7 the "begin_request" function is called
+       before an authorization check. If an authorization check is
+       required, use a request_handler instead.
+       Return value:
+         0: civetweb will process the request itself. In this case,
+            the callback must not send any data to the client.
+         1: callback already processed the request. Civetweb will
+            not send any data after the callback returned. */
     int  (*begin_request)(struct mg_connection *);
     int  (*begin_request)(struct mg_connection *);
 
 
     /* Called when civetweb has finished processing request. */
     /* Called when civetweb has finished processing request. */
@@ -98,12 +104,23 @@ struct mg_callbacks {
        non-zero, civetweb does not log anything. */
        non-zero, civetweb does not log anything. */
     int  (*log_message)(const struct mg_connection *, const char *message);
     int  (*log_message)(const struct mg_connection *, const char *message);
 
 
-    /* Called when civetweb initializes SSL library. */
+    /* Called when civetweb is about to log access. If callback returns
+       non-zero, civetweb does not log anything. */
+    int  (*log_access)(const struct mg_connection *, const char *message);
+
+    /* Called when civetweb initializes SSL library.
+       Parameters:
+         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_context, void *user_data);
 
 
     /* Called when websocket request is received, before websocket handshake.
     /* Called when websocket request is received, before websocket handshake.
-       If callback returns 0, civetweb proceeds with handshake, otherwise
-       cinnection is closed immediately. */
+       Return value:
+         0: civetweb proceeds with websocket handshake.
+         1: connection is closed immediately. */
     int (*websocket_connect)(const struct mg_connection *);
     int (*websocket_connect)(const struct mg_connection *);
 
 
     /* Called when websocket handshake is successfully completed, and
     /* Called when websocket handshake is successfully completed, and
@@ -112,12 +129,12 @@ struct mg_callbacks {
 
 
     /* Called when data frame has been received from the client.
     /* Called when data frame has been received from the client.
        Parameters:
        Parameters:
-          bits: first byte of the websocket frame, see websocket RFC at
-                http://tools.ietf.org/html/rfc6455, section 5.2
-          data, data_len: payload, with mask (if any) already applied.
+         bits: first byte of the websocket frame, see websocket RFC at
+               http://tools.ietf.org/html/rfc6455, section 5.2
+         data, data_len: payload, with mask (if any) already applied.
        Return value:
        Return value:
-          non-0: keep this websocket connection opened.
-          0:     close this websocket connection. */
+         1: keep this websocket connection open.
+         0: close this websocket connection. */
     int  (*websocket_data)(struct mg_connection *, int bits,
     int  (*websocket_data)(struct mg_connection *, int bits,
                            char *data, size_t data_len);
                            char *data, size_t data_len);
 
 
@@ -134,13 +151,13 @@ struct mg_callbacks {
           data_len: Placeholder for the file size, if file is served from
           data_len: Placeholder for the file size, if file is served from
                     memory.
                     memory.
        Return value:
        Return value:
-          NULL: do not serve file from memory, proceed with normal file open.
-          non-NULL: pointer to the file contents in memory. data_len must be
-          initilized with the size of the memory block. */
+         NULL: do not serve file from memory, proceed with normal file open.
+         non-NULL: pointer to the file contents in memory. data_len must be
+           initilized with the size of the memory block. */
     const char * (*open_file)(const struct mg_connection *,
     const char * (*open_file)(const struct mg_connection *,
                               const char *path, size_t *data_len);
                               const char *path, size_t *data_len);
 
 
-    /* Called when civetweb is about to serve Lua server page (.lp file), if
+    /* Called when civetweb is about to serve Lua server page, if
        Lua support is enabled.
        Lua support is enabled.
        Parameters:
        Parameters:
          lua_context: "lua_State *" pointer. */
          lua_context: "lua_State *" pointer. */
@@ -149,13 +166,16 @@ struct mg_callbacks {
     /* Called when civetweb has uploaded a file to a temporary directory as a
     /* Called when civetweb has uploaded a file to a temporary directory as a
        result of mg_upload() call.
        result of mg_upload() call.
        Parameters:
        Parameters:
-          file_file: full path name to the uploaded file. */
+         file_name: full path name to the uploaded file. */
     void (*upload)(struct mg_connection *, const char *file_name);
     void (*upload)(struct mg_connection *, const char *file_name);
 
 
     /* Called when civetweb is about to send HTTP error to the client.
     /* Called when civetweb is about to send HTTP error to the client.
        Implementing this callback allows to create custom error pages.
        Implementing this callback allows to create custom error pages.
        Parameters:
        Parameters:
-         status: HTTP error status code. */
+         status: HTTP error status code.
+       Return value:
+         1: run civetweb error handler.
+         0: callback already handled the error. */
     int  (*http_error)(struct mg_connection *, int status);
     int  (*http_error)(struct mg_connection *, int status);
 
 
     /* Called after civetweb context has been created, before requests
     /* Called after civetweb context has been created, before requests
@@ -575,12 +595,13 @@ CIVETWEB_API void mg_cry(struct mg_connection *conn,
 /* utility method to compare two buffers, case incensitive. */
 /* utility method to compare two buffers, case incensitive. */
 CIVETWEB_API int mg_strncasecmp(const char *s1, const char *s2, size_t len);
 CIVETWEB_API int mg_strncasecmp(const char *s1, const char *s2, size_t len);
 
 
+
 /* Connect to a websocket as a client
 /* Connect to a websocket as a client
    Parameters:
    Parameters:
      host: host to connect to, i.e. "echo.websocket.org" or "192.168.1.1" or "localhost"
      host: host to connect to, i.e. "echo.websocket.org" or "192.168.1.1" or "localhost"
      port: server port
      port: server port
      use_ssl: make a secure connection to server
      use_ssl: make a secure connection to server
-     error_buffer, error_buffer_size: error message placeholder.
+     error_buffer, error_buffer_size: buffer for an error message
      path: server path you are trying to connect to, i.e. if connection to localhost/app, path should be "/app"
      path: server path you are trying to connect to, i.e. if connection to localhost/app, path should be "/app"
      origin: value of the Origin HTTP header
      origin: value of the Origin HTTP header
      data_func: callback that should be used when data is received from the server
      data_func: callback that should be used when data is received from the server
@@ -588,7 +609,8 @@ CIVETWEB_API int mg_strncasecmp(const char *s1, const char *s2, size_t len);
 
 
    Return:
    Return:
      On success, valid mg_connection object.
      On success, valid mg_connection object.
-     On error, NULL. */
+     On error, NULL. Se error_buffer for details.
+*/
 
 
 typedef int  (*websocket_data_func)(struct mg_connection *, int bits,
 typedef int  (*websocket_data_func)(struct mg_connection *, int bits,
                            char *data, size_t data_len);
                            char *data, size_t data_len);
@@ -600,6 +622,41 @@ CIVETWEB_API struct mg_connection *mg_connect_websocket_client(const char *host,
                                                const char *path, const char *origin,
                                                const char *path, const char *origin,
                                                websocket_data_func data_func, websocket_close_func close_func,
                                                websocket_data_func data_func, websocket_close_func close_func,
                                                void * user_data);
                                                void * user_data);
+
+
+/* Connect to a TCP server as a client (can be used to connect to a HTTP server)
+   Parameters:
+     host: host to connect to, i.e. "www.wikipedia.org" or "192.168.1.1" or "localhost"
+     port: server port
+     use_ssl: make a secure connection to server
+     error_buffer, error_buffer_size: buffer for an error message
+
+   Return:
+     On success, valid mg_connection object.
+     On error, NULL. Se error_buffer for details.
+*/
+CIVETWEB_API struct mg_connection *mg_connect_client(const char *host, int port, int use_ssl,
+                                               char *error_buffer, size_t error_buffer_size);
+
+
+enum {
+    TIMEOUT_INFINITE = -1
+};
+
+
+/* Wait for a response from the server
+   Parameters:
+     conn: connection
+     ebuf, ebuf_len: error message placeholder.
+     timeout: time to wait for a response in milliseconds (if < 0 then wait forever)
+
+   Return:
+     On success, >= 0
+     On error/timeout, < 0
+*/
+CIVETWEB_API int mg_get_response(struct mg_connection *conn, char *ebuf, size_t ebuf_len, int timeout);
+
+
 #ifdef __cplusplus
 #ifdef __cplusplus
 }
 }
 #endif /* __cplusplus */
 #endif /* __cplusplus */

+ 26 - 20
resources/Makefile.in-lua

@@ -2,23 +2,23 @@
 # Copyright (c) 2013 No Face Press, LLC
 # Copyright (c) 2013 No Face Press, LLC
 # License http://opensource.org/licenses/mit-license.php MIT License
 # License http://opensource.org/licenses/mit-license.php MIT License
 #
 #
-
-ifndef WITH_LUA
-  $(error WITH_LUA is not defined)
-endif
+
+ifndef WITH_LUA
+  $(error WITH_LUA is not defined)
+endif
 
 
 LUA_DIR = src/third_party/lua-5.2.3/src
 LUA_DIR = src/third_party/lua-5.2.3/src
-LUA_CFLAGS = -I$(LUA_DIR) -DLUA_COMPAT_ALL -DUSE_LUA
-
-ifdef WITH_LUA_SHARED
-
-  LUA_CFLAGS += -DLUA_USE_POSIX -DLUA_USE_DLOPEN
-  LUA_SOURCE_FILES =
-
-  $(info Lua: using dynamic linking)
-
+LUA_CFLAGS = -I$(LUA_DIR) -DLUA_COMPAT_ALL -DUSE_LUA
+
+ifdef WITH_LUA_SHARED
+
+  LUA_CFLAGS += -DLUA_USE_POSIX -DLUA_USE_DLOPEN
+  LUA_SOURCE_FILES =
+
+  $(info Lua: using dynamic linking)
+
 else
 else
-
+
   LUA_SOURCE_FILES = lapi.c  \
   LUA_SOURCE_FILES = lapi.c  \
     lauxlib.c \
     lauxlib.c \
     lbaselib.c  \
     lbaselib.c  \
@@ -51,9 +51,9 @@ else
     lundump.c \
     lundump.c \
     lvm.c  \
     lvm.c  \
     lzio.c
     lzio.c
-
-  $(info Lua: using static library)
-
+
+  $(info Lua: using static library)
+
 endif
 endif
 
 
 LUA_SOURCES = $(addprefix $(LUA_DIR)/, $(LUA_SOURCE_FILES))
 LUA_SOURCES = $(addprefix $(LUA_DIR)/, $(LUA_SOURCE_FILES))
@@ -71,7 +71,13 @@ LFS_SOURCES = $(addprefix $(LFS_DIR)/, $(LFS_SOURCE_FILES))
 LFS_OBJECTS = $(LFS_SOURCES:.c=.o)
 LFS_OBJECTS = $(LFS_SOURCES:.c=.o)
 LFS_CFLAGS = -I$(LFS_DIR)
 LFS_CFLAGS = -I$(LFS_DIR)
 
 
-OBJECTS += $(LUA_OBJECTS) $(SQLITE_OBJECTS) $(LFS_OBJECTS)
-CFLAGS += $(LUA_CFLAGS) $(SQLITE_CFLAGS) $(LFS_CFLAGS) -DUSE_LUA_SQLITE3 -DUSE_LUA_FILE_SYSTEM
-SOURCE_DIRS = $(LUA_DIR) $(SQLITE_DIR) %(LFS_DIR)
+LXML_DIR = src/third_party
+LXML_SOURCE_FILES = LuaXML_lib.c
+LXML_SOURCES = $(addprefix $(LXML_DIR)/, $(LXML_SOURCE_FILES))
+LXML_OBJECTS = $(LXML_SOURCES:.c=.o)
+LXML_CFLAGS = -I$(LXML_DIR)
+
+OBJECTS += $(LUA_OBJECTS) $(SQLITE_OBJECTS) $(LFS_OBJECTS) $(LXML_OBJECTS)
+CFLAGS += $(LUA_CFLAGS) $(SQLITE_CFLAGS) $(LFS_CFLAGS) -DUSE_LUA_SQLITE3 -DUSE_LUA_FILE_SYSTEM -DUSE_LUA_LUAXML
+SOURCE_DIRS = $(LUA_DIR) $(SQLITE_DIR) $(LFS_DIR) $(LXML_DIR)
 
 

+ 66 - 0
resources/cleanup.lua

@@ -0,0 +1,66 @@
+-- Lua script used to clean up tabs and spaces in C, CPP and H files.
+-- Copyright (c) 2014, bel
+-- MIT License (http://opensource.org/licenses/mit-license.php)
+--
+-- It can be used from the command line:
+-- Call Lua5.1 or Lua5.2 + this script file + the C/CPP/H file to clean
+--
+-- It can be used in Visual Studio as an external tool:
+-- command: Lua5.1.exe or Lua5.2.exe
+-- argument: "X:\civetweb\resources\cleanup.lua" $(ItemPath)
+--
+
+clean = arg[1]
+print("Cleaning " .. clean)
+
+lines = io.lines(clean)
+if not lines then
+    print("Can not open file " .. clean)
+    return
+end
+
+function trimright(s)
+  return s:match "^(.-)%s*$"
+end
+
+local lineend = false
+local tabspace = false
+local changed = false
+local invalid = false
+local newfile = {}
+
+for l in lines do
+    local lt = trimright(l)
+    if (lt ~= l) then
+        lineend = true
+        changed = true
+    end
+    local lts = lt:gsub('\t', '    ')
+    if (lts ~= lt) then
+        tabspace = true
+        changed = true
+    end
+    for i=1,#lts do
+        local b = string.byte(lts,i)
+        if b<32 or b>=127 then
+            print("Letter " .. string.byte(l,i) .. " (" .. b .. ") found in line " .. lts)
+            invalid = true
+        end
+    end
+
+    newfile[#newfile + 1] = lts
+end
+
+print("Line endings trimmed:     " .. tostring(lineend))
+print("Tabs converted to spaces: " .. tostring(tabspace))
+print("Invalid characters:       " .. tostring(invalid))
+
+if changed then
+    local f = io.open(clean, "wb")
+    for i=1,#newfile do
+        f:write(newfile[i])
+        f:write("\n")
+    end
+    f:close()
+    print("File cleaned")
+end

+ 9 - 0
src/CivetServer.cpp

@@ -42,6 +42,13 @@ bool CivetHandler::handleDelete(CivetServer *server, struct mg_connection *conn)
     return false;
     return false;
 }
 }
 
 
+bool CivetHandler::handleOptions(CivetServer *server, struct mg_connection *conn)
+{
+    UNUSED_PARAMETER(server);
+    UNUSED_PARAMETER(conn);
+    return false;
+}
+
 int CivetServer::requestHandler(struct mg_connection *conn, void *cbdata)
 int CivetServer::requestHandler(struct mg_connection *conn, void *cbdata)
 {
 {
     struct mg_request_info *request_info = mg_get_request_info(conn);
     struct mg_request_info *request_info = mg_get_request_info(conn);
@@ -63,6 +70,8 @@ int CivetServer::requestHandler(struct mg_connection *conn, void *cbdata)
             return handler->handlePut(me, conn) ? 1 : 0;
             return handler->handlePut(me, conn) ? 1 : 0;
         } else if (strcmp(request_info->request_method, "DELETE") == 0) {
         } else if (strcmp(request_info->request_method, "DELETE") == 0) {
             return handler->handleDelete(me, conn) ? 1 : 0;
             return handler->handleDelete(me, conn) ? 1 : 0;
+        } else if (strcmp(request_info->request_method, "OPTIONS") == 0) {
+            return handler->handleOptions(me, conn) ? 1 : 0;
         }
         }
     }
     }
 
 

文件差异内容过多而无法显示
+ 494 - 115
src/civetweb.c


+ 32 - 13
src/main.c

@@ -1,4 +1,4 @@
-/* Copyright (c) 2013-2014 the Civetweb developers
+/* Copyright (c) 2013-2015 the Civetweb developers
  * Copyright (c) 2004-2013 Sergey Lyubka
  * Copyright (c) 2004-2013 Sergey Lyubka
  *
  *
  * Permission is hereby granted, free of charge, to any person obtaining a copy
  * Permission is hereby granted, free of charge, to any person obtaining a copy
@@ -50,7 +50,9 @@
 #include <shlobj.h>
 #include <shlobj.h>
 
 
 #define getcwd(a,b) _getcwd(a,b)
 #define getcwd(a,b) _getcwd(a,b)
+#if !defined(__MINGW32__)
 extern char *_getcwd(char *buf, size_t size);
 extern char *_getcwd(char *buf, size_t size);
+#endif
 static int guard = 0;                   /* test if any dialog is already open */
 static int guard = 0;                   /* test if any dialog is already open */
 
 
 #ifndef PATH_MAX
 #ifndef PATH_MAX
@@ -138,7 +140,7 @@ static void die(const char *fmt, ...)
 static int MakeConsole();
 static int MakeConsole();
 #endif
 #endif
 
 
-static void show_usage_and_exit(void)
+static void show_usage_and_exit(const char *exeName)
 {
 {
     const struct mg_option *options;
     const struct mg_option *options;
     int i;
     int i;
@@ -147,12 +149,20 @@ static void show_usage_and_exit(void)
     MakeConsole();
     MakeConsole();
 #endif
 #endif
 
 
+    if (exeName==0 || *exeName==0) {
+        exeName = "civetweb";
+    }
+
     fprintf(stderr, "Civetweb v%s, built on %s\n",
     fprintf(stderr, "Civetweb v%s, built on %s\n",
             mg_version(), __DATE__);
             mg_version(), __DATE__);
     fprintf(stderr, "Usage:\n");
     fprintf(stderr, "Usage:\n");
-    fprintf(stderr, "  civetweb -A <htpasswd_file> <realm> <user> <passwd>\n");
-    fprintf(stderr, "  civetweb [config_file]\n");
-    fprintf(stderr, "  civetweb [-option value ...]\n");
+    fprintf(stderr, "  Start server with a set of options:\n");
+    fprintf(stderr, "    %s [config_file]\n", exeName);
+    fprintf(stderr, "    %s [-option value ...]\n", exeName);
+    fprintf(stderr, "  Add user/change password:\n");
+    fprintf(stderr, "    %s -A <htpasswd_file> <realm> <user> <passwd>\n", exeName);
+    fprintf(stderr, "  Remove user:\n");
+    fprintf(stderr, "    %s -R <htpasswd_file> <realm> <user>\n", exeName);
     fprintf(stderr, "\nOPTIONS:\n");
     fprintf(stderr, "\nOPTIONS:\n");
 
 
     options = mg_get_valid_options();
     options = mg_get_valid_options();
@@ -432,7 +442,7 @@ static void process_command_line_arguments(char *argv[], char **options)
            They override config file and default settings. */
            They override config file and default settings. */
         for (i = cmd_line_opts_start; argv[i] != NULL; i += 2) {
         for (i = cmd_line_opts_start; argv[i] != NULL; i += 2) {
             if (argv[i][0] != '-' || argv[i + 1] == NULL) {
             if (argv[i][0] != '-' || argv[i + 1] == NULL) {
-                show_usage_and_exit();
+                show_usage_and_exit(argv[0]);
             }
             }
             if (!set_option(options, &argv[i][1], argv[i + 1])) {
             if (!set_option(options, &argv[i][1], argv[i + 1])) {
                 printf("command line option is invalid, ignoring it:\n %s %s\n",
                 printf("command line option is invalid, ignoring it:\n %s %s\n",
@@ -561,15 +571,24 @@ static void start_civetweb(int argc, char *argv[])
     char *options[2*MAX_OPTIONS+1];
     char *options[2*MAX_OPTIONS+1];
     int i;
     int i;
 
 
-    /* Edit passwords file, if -A option is specified */
+    /* Edit passwords file: Add user or change password, if -A option is specified */
     if (argc > 1 && !strcmp(argv[1], "-A")) {
     if (argc > 1 && !strcmp(argv[1], "-A")) {
         if (argc != 6) {
         if (argc != 6) {
-            show_usage_and_exit();
+            show_usage_and_exit(argv[0]);
         }
         }
         exit(mg_modify_passwords_file(argv[2], argv[3], argv[4], argv[5]) ?
         exit(mg_modify_passwords_file(argv[2], argv[3], argv[4], argv[5]) ?
              EXIT_SUCCESS : EXIT_FAILURE);
              EXIT_SUCCESS : EXIT_FAILURE);
     }
     }
 
 
+    /* Edit passwords file: Remove user, if -R option is specified */
+    if (argc > 1 && !strcmp(argv[1], "-R")) {
+        if (argc != 5) {
+            show_usage_and_exit(argv[0]);
+        }
+        exit(mg_modify_passwords_file(argv[2], argv[3], argv[4], NULL) ?
+             EXIT_SUCCESS : EXIT_FAILURE);
+    }
+
     /* Call Lua with additional Civetweb specific Lua functions, if -L option is specified */
     /* Call Lua with additional Civetweb specific Lua functions, if -L option is specified */
     if (argc > 1 && !strcmp(argv[1], "-L")) {
     if (argc > 1 && !strcmp(argv[1], "-L")) {
 #ifdef WIN32
 #ifdef WIN32
@@ -582,8 +601,8 @@ static void start_civetweb(int argc, char *argv[])
     }
     }
 
 
     /* Show usage if -h or --help options are specified */
     /* Show usage if -h or --help options are specified */
-    if (argc == 2 && (!strcmp(argv[1], "-h") || !strcmp(argv[1], "--help"))) {
-        show_usage_and_exit();
+    if (argc == 2 && (!strcmp(argv[1], "-h") || !strcmp(argv[1], "-H") || !strcmp(argv[1], "--help"))) {
+        show_usage_and_exit(argv[0]);
     }
     }
 
 
     options[0] = NULL;
     options[0] = NULL;
@@ -1501,9 +1520,9 @@ static int MakeConsole() {
 
 
         ok = (GetConsoleWindow() != NULL);
         ok = (GetConsoleWindow() != NULL);
         if (ok) {
         if (ok) {
-            freopen("CONIN$", "r", stdin); 
-            freopen("CONOUT$", "w", stdout); 
-            freopen("CONOUT$", "w", stderr); 
+            freopen("CONIN$", "r", stdin);
+            freopen("CONOUT$", "w", stdout);
+            freopen("CONOUT$", "w", stderr);
         }
         }
     }
     }
 
 

+ 73 - 40
src/mod_lua.inl

@@ -227,7 +227,7 @@ static const char * lsp_var_reader(lua_State *L, void *ud, size_t *sz)
 {
 {
     struct lsp_var_reader_data * reader = (struct lsp_var_reader_data *)ud;
     struct lsp_var_reader_data * reader = (struct lsp_var_reader_data *)ud;
     const char * ret;
     const char * ret;
-	(void)(L); /* unused */
+    (void)(L); /* unused */
 
 
     switch (reader->state) {
     switch (reader->state) {
     case 0:
     case 0:
@@ -318,7 +318,7 @@ static int lsp(struct mg_connection *conn, const char *path,
 /* mg.write: Send data to the client */
 /* mg.write: Send data to the client */
 static int lsp_write(lua_State *L)
 static int lsp_write(lua_State *L)
 {
 {
-    struct mg_connection *conn = lua_touserdata(L, lua_upvalueindex(1));
+    struct mg_connection *conn = (struct mg_connection *)lua_touserdata(L, lua_upvalueindex(1));
     int num_args = lua_gettop(L);
     int num_args = lua_gettop(L);
     const char *str;
     const char *str;
     size_t size;
     size_t size;
@@ -337,7 +337,7 @@ static int lsp_write(lua_State *L)
 /* mg.read: Read data from the client (e.g., from a POST request) */
 /* mg.read: Read data from the client (e.g., from a POST request) */
 static int lsp_read(lua_State *L)
 static int lsp_read(lua_State *L)
 {
 {
-    struct mg_connection *conn = lua_touserdata(L, lua_upvalueindex(1));
+    struct mg_connection *conn = (struct mg_connection *)lua_touserdata(L, lua_upvalueindex(1));
     char buf[1024];
     char buf[1024];
     int len = mg_read(conn, buf, sizeof(buf));
     int len = mg_read(conn, buf, sizeof(buf));
 
 
@@ -350,7 +350,7 @@ static int lsp_read(lua_State *L)
 /* mg.keep_alive: Allow Lua pages to use the http keep-alive mechanism */
 /* mg.keep_alive: Allow Lua pages to use the http keep-alive mechanism */
 static int lsp_keep_alive(lua_State *L)
 static int lsp_keep_alive(lua_State *L)
 {
 {
-    struct mg_connection *conn = lua_touserdata(L, lua_upvalueindex(1));
+    struct mg_connection *conn = (struct mg_connection *)lua_touserdata(L, lua_upvalueindex(1));
     int num_args = lua_gettop(L);
     int num_args = lua_gettop(L);
 
 
     /* This function may be called with one parameter (boolean) to set the keep_alive state.
     /* This function may be called with one parameter (boolean) to set the keep_alive state.
@@ -370,7 +370,7 @@ static int lsp_keep_alive(lua_State *L)
 /* mg.include: Include another .lp file */
 /* mg.include: Include another .lp file */
 static int lsp_include(lua_State *L)
 static int lsp_include(lua_State *L)
 {
 {
-    struct mg_connection *conn = lua_touserdata(L, lua_upvalueindex(1));
+    struct mg_connection *conn = (struct mg_connection *)lua_touserdata(L, lua_upvalueindex(1));
     int num_args = lua_gettop(L);
     int num_args = lua_gettop(L);
     struct file file = STRUCT_FILE_INITIALIZER;
     struct file file = STRUCT_FILE_INITIALIZER;
     const char * filename = (num_args == 1) ? lua_tostring(L, 1) : NULL;
     const char * filename = (num_args == 1) ? lua_tostring(L, 1) : NULL;
@@ -391,7 +391,7 @@ static int lsp_include(lua_State *L)
 /* mg.cry: Log an error. Default value for mg.onerror. */
 /* mg.cry: Log an error. Default value for mg.onerror. */
 static int lsp_cry(lua_State *L)
 static int lsp_cry(lua_State *L)
 {
 {
-    struct mg_connection *conn = lua_touserdata(L, lua_upvalueindex(1));
+    struct mg_connection *conn = (struct mg_connection *)lua_touserdata(L, lua_upvalueindex(1));
     int num_args = lua_gettop(L);
     int num_args = lua_gettop(L);
     const char * text = (num_args == 1) ? lua_tostring(L, 1) : NULL;
     const char * text = (num_args == 1) ? lua_tostring(L, 1) : NULL;
 
 
@@ -407,7 +407,7 @@ static int lsp_cry(lua_State *L)
 /* mg.redirect: Redirect the request (internally). */
 /* mg.redirect: Redirect the request (internally). */
 static int lsp_redirect(lua_State *L)
 static int lsp_redirect(lua_State *L)
 {
 {
-    struct mg_connection *conn = lua_touserdata(L, lua_upvalueindex(1));
+    struct mg_connection *conn = (struct mg_connection *)lua_touserdata(L, lua_upvalueindex(1));
     int num_args = lua_gettop(L);
     int num_args = lua_gettop(L);
     const char * target = (num_args == 1) ? lua_tostring(L, 1) : NULL;
     const char * target = (num_args == 1) ? lua_tostring(L, 1) : NULL;
 
 
@@ -425,7 +425,7 @@ static int lsp_redirect(lua_State *L)
 /* mg.send_file */
 /* mg.send_file */
 static int lsp_send_file(lua_State *L)
 static int lsp_send_file(lua_State *L)
 {
 {
-    struct mg_connection *conn = lua_touserdata(L, lua_upvalueindex(1));
+    struct mg_connection *conn = (struct mg_connection *)lua_touserdata(L, lua_upvalueindex(1));
     int num_args = lua_gettop(L);
     int num_args = lua_gettop(L);
     const char * filename = (num_args == 1) ? lua_tostring(L, 1) : NULL;
     const char * filename = (num_args == 1) ? lua_tostring(L, 1) : NULL;
 
 
@@ -438,6 +438,20 @@ static int lsp_send_file(lua_State *L)
     return 0;
     return 0;
 }
 }
 
 
+/* mg.get_time */
+static int lsp_get_time(lua_State *L)
+{
+    int num_args = lua_gettop(L);
+    int monotonic = (num_args > 0) ? lua_toboolean(L, 1) : 0;
+    struct timespec ts;
+    double d;
+
+    clock_gettime(monotonic ? CLOCK_MONOTONIC : CLOCK_REALTIME, &ts);
+    d = (double)ts.tv_sec + ((double)ts.tv_nsec * 1.0E-9);
+    lua_pushnumber(L, d);
+    return 1;
+}
+
 /* mg.get_var */
 /* mg.get_var */
 static int lsp_get_var(lua_State *L)
 static int lsp_get_var(lua_State *L)
 {
 {
@@ -617,7 +631,7 @@ static int lsp_base64_encode(lua_State *L)
     if (num_args==1) {
     if (num_args==1) {
         text = lua_tolstring(L, 1, &text_len);
         text = lua_tolstring(L, 1, &text_len);
         if (text) {
         if (text) {
-            dst = mg_malloc(text_len*8/6+4);
+            dst = (char *)mg_malloc(text_len*8/6+4);
             if (dst) {
             if (dst) {
                 base64_encode((const unsigned char *)text, text_len, dst);
                 base64_encode((const unsigned char *)text, text_len, dst);
                 lua_pushstring(L, dst);
                 lua_pushstring(L, dst);
@@ -647,7 +661,7 @@ static int lsp_base64_decode(lua_State *L)
     if (num_args==1) {
     if (num_args==1) {
         text = lua_tolstring(L, 1, &text_len);
         text = lua_tolstring(L, 1, &text_len);
         if (text) {
         if (text) {
-            dst = mg_malloc(text_len);
+            dst = (char *)mg_malloc(text_len);
             if (dst) {
             if (dst) {
                 ret = base64_decode((const unsigned char *)text, text_len, dst, &dst_len);
                 ret = base64_decode((const unsigned char *)text, text_len, dst, &dst_len);
                 if (ret != -1) {
                 if (ret != -1) {
@@ -670,6 +684,29 @@ static int lsp_base64_decode(lua_State *L)
     return 1;
     return 1;
 }
 }
 
 
+/* mg.get_response_code_text */
+static int lsp_get_response_code_text(lua_State *L) {
+    int num_args = lua_gettop(L);
+    int type1;
+    double code;
+    const char *text;
+
+    if (num_args==1) {
+        type1 = lua_type(L, 1);
+        if (type1 == LUA_TNUMBER) {
+            /* If the first argument is a number,
+               convert it to the corresponding text. */
+            code = lua_tonumber(L, 1);
+            text = mg_get_response_code_text((int)code, NULL);
+            if (text) lua_pushstring(L, text);
+            return text ? 1 : 0;
+        }
+    }
+
+    /* Syntax error */
+    return luaL_error(L, "invalid get_response_code_text() call");
+}
+
 #ifdef USE_WEBSOCKET
 #ifdef USE_WEBSOCKET
 struct lua_websock_data {
 struct lua_websock_data {
     lua_State *state;
     lua_State *state;
@@ -755,7 +792,7 @@ static int lwebsock_write(lua_State *L)
         return luaL_error(L, "invalid websocket write() call");
         return luaL_error(L, "invalid websocket write() call");
     }
     }
 #else
 #else
-	(void)(L); /* unused */
+    (void)(L); /* unused */
 #endif
 #endif
     return 0;
     return 0;
 }
 }
@@ -848,7 +885,7 @@ static int lwebsocket_set_timer(lua_State *L, int is_periodic)
         timediff = (double)lua_tonumber(L, 2);
         timediff = (double)lua_tonumber(L, 2);
         txt = lua_tostring(L, 1);
         txt = lua_tostring(L, 1);
         txt_len = strlen(txt);
         txt_len = strlen(txt);
-        arg = mg_malloc(sizeof(struct laction_arg) + txt_len + 10);
+        arg = (struct laction_arg *) mg_malloc(sizeof(struct laction_arg) + txt_len + 10);
         arg->state = L;
         arg->state = L;
         arg->script = ws->script;
         arg->script = ws->script;
         arg->pmutex = &(ws->ws_mutex);
         arg->pmutex = &(ws->ws_mutex);
@@ -960,8 +997,8 @@ static void prepare_lua_environment(struct mg_context * ctx, struct mg_connectio
 #endif
 #endif
 #ifdef USE_LUA_LUAXML
 #ifdef USE_LUA_LUAXML
     {
     {
-        extern int luaopen_LuaXML(lua_State *);
-        luaopen_LuaXML(L);
+        extern int luaopen_LuaXML_lib(lua_State *);
+        luaopen_LuaXML_lib(L);
     }
     }
 #endif
 #endif
 #ifdef USE_LUA_FILE_SYSTEM
 #ifdef USE_LUA_FILE_SYSTEM
@@ -1027,6 +1064,7 @@ static void prepare_lua_environment(struct mg_context * ctx, struct mg_connectio
         /* reg_conn_function(L, "send_file", lsp_send_file, conn); */
         /* reg_conn_function(L, "send_file", lsp_send_file, conn); */
     }
     }
 
 
+    reg_function(L, "time", lsp_get_time);
     reg_function(L, "get_var", lsp_get_var);
     reg_function(L, "get_var", lsp_get_var);
     reg_function(L, "get_mime_type", lsp_get_mime_type);
     reg_function(L, "get_mime_type", lsp_get_mime_type);
     reg_function(L, "get_cookie", lsp_get_cookie);
     reg_function(L, "get_cookie", lsp_get_cookie);
@@ -1035,6 +1073,7 @@ static void prepare_lua_environment(struct mg_context * ctx, struct mg_connectio
     reg_function(L, "url_decode", lsp_url_decode);
     reg_function(L, "url_decode", lsp_url_decode);
     reg_function(L, "base64_encode", lsp_base64_encode);
     reg_function(L, "base64_encode", lsp_base64_encode);
     reg_function(L, "base64_decode", lsp_base64_decode);
     reg_function(L, "base64_decode", lsp_base64_decode);
+    reg_function(L, "get_response_code_text", lsp_get_response_code_text);
 
 
     reg_string(L, "version", CIVETWEB_VERSION);
     reg_string(L, "version", CIVETWEB_VERSION);
     reg_string(L, "document_root", ctx->config[DOCUMENT_ROOT]);
     reg_string(L, "document_root", ctx->config[DOCUMENT_ROOT]);
@@ -1123,7 +1162,7 @@ void mg_exec_lua_script(struct mg_connection *conn, const char *path,
         if (exports != NULL) {
         if (exports != NULL) {
             lua_pushglobaltable(L);
             lua_pushglobaltable(L);
             for (i = 0; exports[i] != NULL && exports[i + 1] != NULL; i += 2) {
             for (i = 0; exports[i] != NULL && exports[i + 1] != NULL; i += 2) {
-                lua_pushstring(L, exports[i]);
+                lua_pushstring(L, (const char *)(exports[i]));
                 lua_pushcclosure(L, (lua_CFunction) exports[i + 1], 0);
                 lua_pushcclosure(L, (lua_CFunction) exports[i + 1], 0);
                 lua_rawset(L, -3);
                 lua_rawset(L, -3);
             }
             }
@@ -1137,25 +1176,6 @@ void mg_exec_lua_script(struct mg_connection *conn, const char *path,
     }
     }
 }
 }
 
 
-static void lsp_send_err(struct mg_connection *conn, struct lua_State *L,
-    const char *fmt, ...)
-{
-    char buf[MG_BUF_LEN];
-    va_list ap;
-    int len;
-
-    va_start(ap, fmt);
-    len = vsnprintf(buf, sizeof(buf), fmt, ap);
-    va_end(ap);
-
-    if (L == NULL) {
-        send_http_error(conn, 500, http_500_error, "%s", buf);
-    } else {
-        lua_pushstring(L, buf);
-        lua_error(L);
-    }
-}
-
 static int handle_lsp_request(struct mg_connection *conn, const char *path, struct file *filep, struct lua_State *ls)
 static int handle_lsp_request(struct mg_connection *conn, const char *path, struct file *filep, struct lua_State *ls)
 {
 {
     void *p = NULL;
     void *p = NULL;
@@ -1167,20 +1187,33 @@ static int handle_lsp_request(struct mg_connection *conn, const char *path, stru
 
 
     /* We need both mg_stat to get file size, and mg_fopen to get fd */
     /* We need both mg_stat to get file size, and mg_fopen to get fd */
     if (!mg_stat(conn, path, filep) || !mg_fopen(conn, path, "r", filep)) {
     if (!mg_stat(conn, path, filep) || !mg_fopen(conn, path, "r", filep)) {
-        lsp_send_err(conn, ls, "File [%s] not found", path);
+        /* File not found or not accessible */
+        if (ls == NULL) {
+            send_http_error(conn, 500,
+                "Error: Cannot open script\nFile %s can not be read", path);
+        } else {
+            luaL_error(ls, "File [%s] not found", path);
+        }
     } else if (filep->membuf == NULL &&
     } else if (filep->membuf == NULL &&
         (p = mmap(NULL, (size_t) filep->size, PROT_READ, MAP_PRIVATE,
         (p = mmap(NULL, (size_t) filep->size, PROT_READ, MAP_PRIVATE,
         fileno(filep->fp), 0)) == MAP_FAILED) {
         fileno(filep->fp), 0)) == MAP_FAILED) {
-            lsp_send_err(conn, ls, "mmap(%s, %zu, %d): %s", path, (size_t) filep->size,
+        /* mmap failed */
+        if (ls == NULL) {
+            send_http_error(conn, 500,
+                "Error: Cannot open script\nFile %s can not be mapped", path);
+        } else {
+            luaL_error(ls, "mmap(%s, %zu, %d): %s", path, (size_t) filep->size,
                 fileno(filep->fp), strerror(errno));
                 fileno(filep->fp), strerror(errno));
+        }
     } else if ((L = (ls != NULL ? ls : lua_newstate(lua_allocator, NULL))) == NULL) {
     } else if ((L = (ls != NULL ? ls : lua_newstate(lua_allocator, NULL))) == NULL) {
-        send_http_error(conn, 500, http_500_error, "%s", "luaL_newstate failed");
+        send_http_error(conn, 500, "%s",
+            "Error: Cannot execute script\nlua_newstate failed");
     } else {
     } else {
         /* We're not sending HTTP headers here, Lua page must do it. */
         /* We're not sending HTTP headers here, Lua page must do it. */
         if (ls == NULL) {
         if (ls == NULL) {
             prepare_lua_environment(conn->ctx, conn, NULL, L, path, LUA_ENV_TYPE_LUA_SERVER_PAGE);
             prepare_lua_environment(conn->ctx, conn, NULL, L, path, LUA_ENV_TYPE_LUA_SERVER_PAGE);
         }
         }
-        error = lsp(conn, path, filep->membuf == NULL ? p : filep->membuf,
+        error = lsp(conn, path, (filep->membuf == NULL) ? (const char *)p : (const char *)filep->membuf,
             filep->size, L);
             filep->size, L);
     }
     }
 
 
@@ -1215,7 +1248,7 @@ static void * lua_websocket_new(const char * script, struct mg_connection *conn)
     }
     }
     if (*shared_websock_list == NULL) {
     if (*shared_websock_list == NULL) {
         /* add ws to list */
         /* add ws to list */
-        *shared_websock_list = mg_calloc(sizeof(struct mg_shared_lua_websocket_list), 1);
+        *shared_websock_list = (struct mg_shared_lua_websocket_list *) mg_calloc(sizeof(struct mg_shared_lua_websocket_list), 1);
         if (*shared_websock_list == NULL) {
         if (*shared_websock_list == NULL) {
             mg_unlock_context(conn->ctx);
             mg_unlock_context(conn->ctx);
             mg_cry(conn, "Cannot create shared websocket struct, OOM");
             mg_cry(conn, "Cannot create shared websocket struct, OOM");
@@ -1266,7 +1299,7 @@ static void * lua_websocket_new(const char * script, struct mg_connection *conn)
     if (!ok) {
     if (!ok) {
         /* Remove from ws connection list. */
         /* Remove from ws connection list. */
         /* TODO: Check if list entry and Lua state needs to be deleted (see websocket_close). */
         /* TODO: Check if list entry and Lua state needs to be deleted (see websocket_close). */
-        (*shared_websock_list)->ws.conn[--(ws->references)] = 0;    
+        (*shared_websock_list)->ws.conn[--(ws->references)] = 0;
     }
     }
 
 
     (void)pthread_mutex_unlock(&(ws->ws_mutex));
     (void)pthread_mutex_unlock(&(ws->ws_mutex));

+ 116 - 0
src/third_party/LuaXML.lua

@@ -0,0 +1,116 @@
+local base = _G
+
+-- symbolic name for tag index, this allows accessing the tag by var[xml.TAG]
+xml.TAG = 0
+
+-- sets or returns tag of a LuaXML object
+function xml.tag(var,tag)
+	if base.type(var)~="table" then return end
+	if base.type(tag)=="nil" then 
+		return var[xml.TAG]
+	end
+	var[xml.TAG] = tag
+end
+
+-- creates a new LuaXML object either by setting the metatable of an existing Lua table or by setting its tag
+function xml.new(arg)
+	if base.type(arg)=="table" then 
+		base.setmetatable(arg,{__index=xml, __tostring=xml.str})
+		return arg
+	end
+	local var={}
+	base.setmetatable(var,{__index=xml, __tostring=xml.str})
+	if base.type(arg)=="string" then var[xml.TAG]=arg end
+	return var
+end
+
+-- appends a new subordinate LuaXML object to an existing one, optionally sets tag
+function xml.append(var,tag)
+	if base.type(var)~="table" then return end
+	local newVar = xml.new(tag)
+	var[#var+1] = newVar
+	return newVar
+end
+
+-- converts any Lua var into an XML string
+function xml.str(var,indent,tagValue)
+	if base.type(var)=="nil" then return end
+	local indent = indent or 0
+	local indentStr=""
+	for i = 1,indent do indentStr=indentStr.."	" end
+	local tableStr=""
+
+	if base.type(var)=="table" then
+		local tag = var[0] or tagValue or base.type(var)
+		local s = indentStr.."<"..tag
+		for k,v in base.pairs(var) do -- attributes 
+			if base.type(k)=="string" then
+				if base.type(v)=="table" and k~="_M" then -- otherwise recursiveness imminent
+					tableStr = tableStr..xml.str(v,indent+1,k)
+				else
+					s = s.." "..k.."=\""..xml.encode(base.tostring(v)).."\""
+				end
+			end
+		end
+		if #var==0 and #tableStr==0 then
+			s = s.." />\n"
+		elseif #var==1 and base.type(var[1])~="table" and #tableStr==0 then -- single element
+			s = s..">"..xml.encode(base.tostring(var[1])).."</"..tag..">\n"
+		else
+			s = s..">\n"
+			for k,v in base.ipairs(var) do -- elements
+				if base.type(v)=="string" then
+					s = s..indentStr.."	"..xml.encode(v).." \n"
+				else
+					s = s..xml.str(v,indent+1)
+				end
+			end
+			s=s..tableStr..indentStr.."</"..tag..">\n"
+		end
+		return s
+	else
+		local tag = base.type(var)
+		return indentStr.."<"..tag.."> "..xml.encode(base.tostring(var)).." </"..tag..">\n"
+	end
+end
+
+
+-- saves a Lua var as xml file
+function xml.save(var,filename)
+	if not var then return end
+	if not filename or #filename==0 then return end
+	local file = base.io.open(filename,"w")
+	file:write("<?xml version=\"1.0\"?>\n<!-- file \"",filename, "\", generated by LuaXML -->\n\n")
+	file:write(xml.str(var))
+	base.io.close(file)
+end
+
+
+-- recursively parses a Lua table for a substatement fitting to the provided tag and attribute
+function xml.find(var, tag, attributeKey,attributeValue)
+	-- check input:
+	if base.type(var)~="table" then return end
+	if base.type(tag)=="string" and #tag==0 then tag=nil end
+	if base.type(attributeKey)~="string" or #attributeKey==0 then attributeKey=nil end
+	if base.type(attributeValue)=="string" and #attributeValue==0 then attributeValue=nil end
+	-- compare this table:
+	if tag~=nil then
+		if var[0]==tag and ( attributeValue == nil or var[attributeKey]==attributeValue ) then
+			base.setmetatable(var,{__index=xml, __tostring=xml.str})
+			return var
+		end
+	else
+		if attributeValue == nil or var[attributeKey]==attributeValue then
+			base.setmetatable(var,{__index=xml, __tostring=xml.str})
+			return var
+		end
+	end
+	-- recursively parse subtags:
+	for k,v in base.ipairs(var) do
+		if base.type(v)=="table" then
+			local ret = xml.find(v, tag, attributeKey,attributeValue)
+			if ret ~= nil then return ret end
+		end
+	end
+end
+

+ 476 - 458
src/third_party/LuaXML_lib.c

@@ -1,458 +1,476 @@
-/**
-LuaXML License
-
-LuaXml is licensed under the terms of the MIT license reproduced below,
-the same as Lua itself. This means that LuaXml is free software and can be
-used for both academic and commercial purposes at absolutely no cost.
-
-Copyright (C) 2007-2013 Gerald Franz, eludi.net
-
-Permission is hereby granted, free of charge, to any person obtaining a copy
-of this software and associated documentation files (the "Software"), to deal
-in the Software without restriction, including without limitation the rights
-to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-copies of the Software, and to permit persons to whom the Software is
-furnished to do so, subject to the following conditions:
-
-The above copyright notice and this permission notice shall be included in
-all copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
-AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-THE SOFTWARE.
-*/
-
-#if defined __WIN32__ || defined WIN32
-# include <windows.h>
-#endif
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-#include <lua.h>
-#include <lauxlib.h>
-#include <lualib.h>
-#ifdef __cplusplus
-}
-#endif
-
-#include <stdio.h>
-#include <string.h>
-#include <ctype.h>
-#include <stdlib.h>
-
-static const char ESC=27;
-static const char OPN=28;
-static const char CLS=29;
-
-/*--- auxliary functions -------------------------------------------*/
-
-static const char* char2code(unsigned char ch, char buf[8]) {
-    unsigned char i=0;
-    buf[i++]='&';
-    buf[i++]='#';
-    if(ch>99) buf[i++]=ch/100+48;
-    if(ch>9) buf[i++]=(ch%100)/10+48;
-    buf[i++]=ch%10+48;
-    buf[i++]=';';
-    buf[i]=0;
-    return buf;
-}
-
-static size_t find(const char* s, const char* pattern, size_t start) {
-    const char* found =strstr(s+start, pattern);
-    return found ? found-s : strlen(s);
-}
-
-/*--- internal tokenizer -------------------------------------------*/
-typedef struct Tokenizer_s  {    
-    const char* s; /* stores string to be tokenized */    
-    size_t s_size; /* stores size of string to be tokenized */    
-    size_t i; /* stores current read position */    
-    int tagMode; /* stores current read context */    
-    const char* m_next; /* stores next token, if already determined */    
-    size_t m_next_size; /* size of next token */    
-    char* m_token; /* pointer to current token */    
-    size_t m_token_size; /* size of current token */    
-    size_t m_token_capacity; /* capacity of current token */
-} Tokenizer;
-
-Tokenizer* Tokenizer_new(const char* str, size_t str_size) {
-    Tokenizer *tok = (Tokenizer*)malloc(sizeof(Tokenizer));
-    memset(tok, 0, sizeof(Tokenizer));
-    tok->s_size = str_size;
-    tok->s = str;
-    return tok;
-}
-
-void Tokenizer_delete(Tokenizer* tok) {
-    free(tok->m_token);
-    free(tok);
-}
-
-/*
-void Tokenizer_print(Tokenizer* tok) { printf("  @%u %s\n", tok->i, !tok->m_token ? "(null)" : (tok->m_token[0]==ESC)?"(esc)" : (tok->m_token[0]==OPN)?"(open)": (tok->m_token[0]==CLS)?"(close)" : tok->m_token); fflush(stdout); }
-*/
-
-static const char* Tokenizer_set(Tokenizer* tok, const char* s, size_t size) {
-    if(!size||!s) return 0;
-    free(tok->m_token);
-    tok->m_token = (char*)malloc(size+1);
-    strncpy(tok->m_token,s, size);
-    tok->m_token[size] = 0;
-    tok->m_token_size = tok->m_token_capacity = size;
-    /*Tokenizer_print(tok);*/
-    return tok->m_token;
-}
-
-static void Tokenizer_append(Tokenizer* tok, char ch) {
-    if(tok->m_token_size+1>=tok->m_token_capacity) {
-        tok->m_token_capacity = (tok->m_token_capacity==0) ? 16 : tok->m_token_capacity*2;
-        tok->m_token = (char*)realloc(tok->m_token, tok->m_token_capacity);
-    }
-    tok->m_token[tok->m_token_size]=ch;
-    tok->m_token[++tok->m_token_size]=0;
-}
-
-const char* Tokenizer_next(Tokenizer* tok) {
-    const char* ESC_str = "\033";
-    const char* OPEN_str = "\034";
-    const char* CLOSE_str = "\035";
-    int quotMode=0;
-    int tokenComplete = 0;
-
-    if(tok->m_token) {
-        free(tok->m_token);
-        tok->m_token = 0;
-        tok->m_token_size=tok->m_token_capacity = 0;
-    }
-
-    while(tok->m_next_size || (tok->i < tok->s_size)) {
-
-        if(tok->m_next_size) {
-            Tokenizer_set(tok, tok->m_next, tok->m_next_size);
-            tok->m_next=0;
-            tok->m_next_size=0;
-            return tok->m_token;
-        }
-
-        switch(tok->s[tok->i]) {
-        case '"':
-        case '\'':
-            if(tok->tagMode) {
-                if(!quotMode) quotMode=tok->s[tok->i];
-                else if(quotMode==tok->s[tok->i]) quotMode=0;
-            }
-            Tokenizer_append(tok, tok->s[tok->i]);
-            break;
-        case '<':
-            if(!quotMode&&(tok->i+4<tok->s_size)&&(strncmp(tok->s+tok->i,"<!--",4)==0)) /* strip comments */
-                tok->i=find(tok->s, "-->", tok->i+4)+2;
-            else if(!quotMode&&(tok->i+9<tok->s_size)&&(strncmp(tok->s+tok->i,"<![CDATA[",9)==0)) { /* interpet CDATA */
-                size_t b=tok->i+9;
-                tok->i=find(tok->s, "]]>",b)+3;
-                if(!tok->m_token_size) return Tokenizer_set(tok, tok->s+b, tok->i-b-3);
-                tokenComplete = 1;
-                tok->m_next = tok->s+b;
-                tok->m_next_size = tok->i-b-3;
-                --tok->i;
-            }
-            else if(!quotMode&&(tok->i+1<tok->s_size)&&((tok->s[tok->i+1]=='?')||(tok->s[tok->i+1]=='!'))) /* strip meta information */
-                tok->i=find(tok->s, ">", tok->i+2);
-            else if(!quotMode&&!tok->tagMode) {
-                if((tok->i+1<tok->s_size)&&(tok->s[tok->i+1]=='/')) {
-                    tok->m_next=ESC_str;
-                    tok->m_next_size = 1;
-                    tok->i=find(tok->s, ">", tok->i+2);
-                }
-                else {
-                    tok->m_next = OPEN_str;
-                    tok->m_next_size = 1;
-                    tok->tagMode=1;
-                }
-                tokenComplete = 1;
-            }
-            else Tokenizer_append(tok, tok->s[tok->i]);
-            break;
-        case '/':
-            if(tok->tagMode&&!quotMode) {
-                tokenComplete = 1;
-                if((tok->i+1 < tok->s_size) && (tok->s[tok->i+1]=='>')) {
-                    tok->tagMode=0;
-                    tok->m_next=ESC_str;
-                    tok->m_next_size = 1;
-                    ++tok->i;
-                }
-                else Tokenizer_append(tok, tok->s[tok->i]);
-            }
-            else Tokenizer_append(tok, tok->s[tok->i]);
-            break;
-        case '>':
-            if(!quotMode&&tok->tagMode) {
-                tok->tagMode=0;
-                tokenComplete = 1;
-                tok->m_next = CLOSE_str;
-                tok->m_next_size = 1;
-            }
-            else Tokenizer_append(tok, tok->s[tok->i]);
-            break;
-        case ' ':
-        case '\r':
-        case '\n':
-        case '\t':
-            if(tok->tagMode&&!quotMode) {
-                if(tok->m_token_size) tokenComplete=1;
-            }
-            else if(tok->m_token_size) Tokenizer_append(tok, tok->s[tok->i]);
-            break;
-        default: Tokenizer_append(tok, tok->s[tok->i]);
-        }
-        ++tok->i;
-        if((tok->i>=tok->s_size)||(tokenComplete&&tok->m_token_size)) {
-            tokenComplete=0;
-            while(tok->m_token_size&&isspace(tok->m_token[tok->m_token_size-1])) /* trim whitespace */
-                tok->m_token[--tok->m_token_size]=0;
-            if(tok->m_token_size) break;
-        }
-    }
-    /*Tokenizer_print(tok);*/
-    return tok->m_token;
-}
-
-/*--- local variables ----------------------------------------------*/
-static size_t sv_code_size=0; /* stores number of special character codes */
-static size_t sv_code_capacity=16; /* stores currently allocated capacity for special character codes */
-static char** sv_code=0; /* stores code table for special characters */
-
-/*--- public methods -----------------------------------------------*/
-static void Xml_pushDecode(lua_State* L, const char* s, size_t s_size) {
-
-    luaL_Buffer b;
-    const char* found;
-    size_t start=0, pos;
-    size_t i;
-
-    if(!s_size)
-        s_size=strlen(s);
-    luaL_buffinit(L, &b);
-    found = strstr(s, "&#");
-
-    pos = found ? found-s : s_size;
-    while(found) {
-        char ch = 0;
-        size_t i=0;
-        for(found += 2; i<3; ++i, ++found)
-            if(isdigit(*found))
-                ch = ch * 10 + (*found - 48);
-            else break;
-            if(*found == ';') {
-                if(pos>start)
-                    luaL_addlstring(&b, s+start, pos-start);
-                luaL_addchar(&b, ch);
-                start = pos + 3 + i;
-            }
-            found = strstr(found+1, "&#");
-            pos = found ? found-s : s_size;
-    }
-    if(pos>start)
-        luaL_addlstring(&b,s+start, pos-start);
-    luaL_pushresult(&b);
-
-    for(i=sv_code_size-1; i<sv_code_size; i-=2) {
-        luaL_gsub(L, lua_tostring(L,-1), sv_code[i], sv_code[i-1]);
-        lua_remove(L,-2);
-    }
-}
-
-int Xml_eval(lua_State *L) {
-    char* str = 0;
-    size_t str_size=0;
-    Tokenizer* tok;
-    const char* token=0;
-    int firstStatement = 1;
-
-    if(lua_isuserdata(L,1))
-        str = (char*)lua_touserdata(L,1);
-    else {
-        const char * sTmp = luaL_checklstring(L,1,&str_size);
-        str = (char*)malloc(str_size+1);
-        memcpy(str, sTmp, str_size);
-        str[str_size]=0;
-    }
-    tok = Tokenizer_new(str, str_size ? str_size : strlen(str));
-    lua_settop(L,0);
-
-    while((token=Tokenizer_next(tok))!=0) if(token[0]==OPN) { /* new tag found */
-        if(lua_gettop(L)) {
-            int newIndex=lua_rawlen(L,-1)+1;
-            lua_pushnumber(L,newIndex);
-            lua_newtable(L);
-            lua_settable(L, -3);
-            lua_pushnumber(L,newIndex);
-            lua_gettable(L,-2);
-        }
-        else {
-            if (firstStatement) {
-                lua_newtable(L);
-                firstStatement = 0;
-            }
-            else return lua_gettop(L);
-        }
-        /* set metatable: */
-        lua_newtable(L);
-        lua_pushliteral(L, "__index");
-        lua_getglobal(L, "xml");
-        lua_settable(L, -3);
-
-        lua_pushliteral(L, "__tostring"); /* set __tostring metamethod */
-        lua_getglobal(L, "xml");
-        lua_pushliteral(L,"str");
-        lua_gettable(L, -2);
-        lua_remove(L, -2);
-        lua_settable(L, -3);
-        lua_setmetatable(L, -2);
-
-        /* parse tag and content: */
-        lua_pushnumber(L,0); /* use index 0 for storing the tag */
-        lua_pushstring(L, Tokenizer_next(tok));
-        lua_settable(L, -3);
-
-        while(((token = Tokenizer_next(tok))!=0)&&(token[0]!=CLS)&&(token[0]!=ESC)) { /* parse tag header */
-            size_t sepPos=find(token, "=", 0);
-            if(token[sepPos]) { /* regular attribute */
-                const char* aVal =token+sepPos+2;
-                size_t lenVal;
-                lua_pushlstring(L, token, sepPos);
-                lenVal = strlen(aVal)-1;
-                if(!lenVal) Xml_pushDecode(L, "", 0);
-                else Xml_pushDecode(L, aVal, lenVal);
-                lua_settable(L, -3);
-            }
-        }
-        if(!token||(token[0]==ESC)) {
-            if(lua_gettop(L)>1) lua_settop(L,-2); /* this tag has no content, only attributes */
-            else break;
-        }
-    }
-    else if(token[0]==ESC) { /* previous tag is over */
-        if(lua_gettop(L)>1) lua_settop(L,-2); /* pop current table */
-        else break;
-    }
-    else { /* read elements */
-        lua_pushnumber(L,lua_rawlen(L,-1)+1);
-        Xml_pushDecode(L, token, 0);
-        lua_settable(L, -3);
-    }
-    Tokenizer_delete(tok);
-    free(str);
-    return lua_gettop(L);
-}
-
-int Xml_load (lua_State *L) {
-    const char * filename = luaL_checkstring(L,1);
-    size_t sz;
-    FILE * file=fopen(filename,"r");
-    char* buffer;
-
-    if(!file)
-        return luaL_error(L,"LuaXml ERROR: \"%s\" file error or file not found!",filename);
-
-    fseek (file , 0 , SEEK_END);
-    sz = ftell (file);
-    rewind (file);
-    buffer = (char*)malloc(sz+1);
-    sz = fread (buffer,1,sz,file);
-    fclose(file);
-    buffer[sz]=0;
-    lua_pushlightuserdata(L,buffer);
-    lua_replace(L,1);
-    return Xml_eval(L);
-};
-
-int Xml_registerCode(lua_State *L) {
-    const char * decoded = luaL_checkstring(L,1);
-    const char * encoded = luaL_checkstring(L,2);
-
-    size_t i;
-    for(i=0; i<sv_code_size; i+=2)
-        if(strcmp(sv_code[i],decoded)==0)
-            return luaL_error(L,"LuaXml ERROR: code already exists.");
-    if(sv_code_size+2>sv_code_capacity) {
-        sv_code_capacity*=2;
-        sv_code = (char**)realloc(sv_code, sv_code_capacity*sizeof(char*));
-    }
-    sv_code[sv_code_size]=(char*)malloc(strlen(decoded)+1);
-    strcpy(sv_code[sv_code_size++], decoded);
-    sv_code[sv_code_size]=(char*)malloc(strlen(encoded)+1);
-    strcpy(sv_code[sv_code_size++],encoded);
-    return 0;
-}
-
-int Xml_encode(lua_State *L) {
-    char buf[8];
-    size_t i, start, pos;
-    luaL_Buffer b;
-    const char* s;
-
-    if(lua_gettop(L)!=1)
-        return 0;
-    luaL_checkstring(L,-1);
-
-    for(i=0; i<sv_code_size; i+=2) {
-        luaL_gsub(L, lua_tostring(L,-1), sv_code[i], sv_code[i+1]);
-        lua_remove(L,-2);
-    }
-
-    s=lua_tostring(L,1);
-    luaL_buffinit(L, &b);
-    for(start=pos=0; s[pos]!=0; ++pos) if(s[pos]<0) {
-        if(pos>start) luaL_addlstring(&b,s+start, pos-start);
-        luaL_addstring(&b,char2code((unsigned char)(s[pos]),buf));
-        start=pos+1;
-    }
-    if(pos>start)
-        luaL_addlstring(&b,s+start, pos-start);
-    luaL_pushresult(&b);
-    lua_remove(L,-2);
-    return 1;
-}
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-    int luaopen_LuaXML(lua_State* L) {
-        static const struct luaL_Reg funcs[] = {
-            {"load", Xml_load},
-            {"eval", Xml_eval},
-            {"encode", Xml_encode},
-            /* {"registerCode", Xml_registerCode}, */
-            {NULL, NULL}
-        };
-
-        luaL_newlibtable(L, funcs);
-        luaL_setfuncs(L, funcs, 0);
-        lua_setglobal(L, "xml");
-
-        /* register default codes: */
-        if(!sv_code) {
-            sv_code=(char**)malloc(sv_code_capacity*sizeof(char*));
-            sv_code[sv_code_size++]="&";
-            sv_code[sv_code_size++]="&amp;";
-            sv_code[sv_code_size++]="<";
-            sv_code[sv_code_size++]="&lt;";
-            sv_code[sv_code_size++]=">";
-            sv_code[sv_code_size++]="&gt;";
-            sv_code[sv_code_size++]="\"";
-            sv_code[sv_code_size++]="&quot;";
-            sv_code[sv_code_size++]="'";
-            sv_code[sv_code_size++]="&apos;";
-        }
-        return 1;
-    }
-#ifdef __cplusplus
-}
-#endif
+/**
+LuaXML License
+
+LuaXml is licensed under the terms of the MIT license reproduced below,
+the same as Lua itself. This means that LuaXml is free software and can be
+used for both academic and commercial purposes at absolutely no cost.
+
+Copyright (C) 2007-2013 Gerald Franz, eludi.net
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
+*/
+
+#if defined __WIN32__ || defined WIN32
+# include <windows.h>
+# define _EXPORT __declspec(dllexport)
+#else
+# define _EXPORT
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+#include <lua.h>
+#include <lauxlib.h>
+#include <lualib.h>
+#ifdef __cplusplus
+} // extern "C"
+#endif
+
+#include <stdio.h>
+#include <string.h>
+#include <ctype.h>
+#include <stdlib.h>
+
+static const char ESC=27;
+static const char OPN=28;
+static const char CLS=29;
+
+//--- auxliary functions -------------------------------------------
+
+static const char* char2code(unsigned char ch, char buf[8]) {
+    unsigned char i=0;
+    buf[i++]='&';
+    buf[i++]='#';
+    if(ch>99) buf[i++]=ch/100+48;
+    if(ch>9) buf[i++]=(ch%100)/10+48;
+    buf[i++]=ch%10+48;
+    buf[i++]=';';
+    buf[i]=0;
+    return buf;
+}
+
+static size_t find(const char* s, const char* pattern, size_t start) {
+    const char* found =strstr(s+start, pattern);
+    return found ? found-s : strlen(s);
+}
+
+//--- internal tokenizer -------------------------------------------
+
+typedef struct Tokenizer_s  {
+    /// stores string to be tokenized
+    const char* s;
+    /// stores size of string to be tokenized
+    size_t s_size;
+    /// stores current read position
+    size_t i;
+    /// stores current read context
+    int tagMode;
+    /// stores next token, if already determined
+    const char* m_next;
+    /// size of next token
+    size_t m_next_size;
+    /// pointer to current token
+    char* m_token;
+    /// size of current token
+    size_t m_token_size;
+    /// capacity of current token
+    size_t m_token_capacity;
+} Tokenizer;
+
+Tokenizer* Tokenizer_new(const char* str, size_t str_size) {
+    Tokenizer *tok = (Tokenizer*)malloc(sizeof(Tokenizer));
+    memset(tok, 0, sizeof(Tokenizer));
+    tok->s_size = str_size;
+    tok->s = str;
+    return tok;
+}
+
+void Tokenizer_delete(Tokenizer* tok) {
+    free(tok->m_token);
+    free(tok);
+}
+
+//void Tokenizer_print(Tokenizer* tok) { printf("  @%u %s\n", tok->i, !tok->m_token ? "(null)" : (tok->m_token[0]==ESC)?"(esc)" : (tok->m_token[0]==OPN)?"(open)": (tok->m_token[0]==CLS)?"(close)" : tok->m_token); fflush(stdout); }
+
+static const char* Tokenizer_set(Tokenizer* tok, const char* s, size_t size) {
+    if(!size||!s) return 0;
+    free(tok->m_token);
+    tok->m_token = (char*)malloc(size+1);
+    strncpy(tok->m_token,s, size);
+    tok->m_token[size] = 0;
+    tok->m_token_size = tok->m_token_capacity = size;
+    //Tokenizer_print(tok);
+    return tok->m_token;
+}
+
+static void Tokenizer_append(Tokenizer* tok, char ch) {
+    if(tok->m_token_size+1>=tok->m_token_capacity) {
+        tok->m_token_capacity = (tok->m_token_capacity==0) ? 16 : tok->m_token_capacity*2;
+        tok->m_token = (char*)realloc(tok->m_token, tok->m_token_capacity);
+    }
+    tok->m_token[tok->m_token_size]=ch;
+    tok->m_token[++tok->m_token_size]=0;
+}
+
+const char* Tokenizer_next(Tokenizer* tok) {
+    const char* ESC_str = "\033";
+    const char* OPEN_str = "\034";
+    const char* CLOSE_str = "\035";
+    int quotMode=0;
+    int tokenComplete = 0;
+
+    if(tok->m_token) {
+        free(tok->m_token);
+        tok->m_token = 0;
+        tok->m_token_size=tok->m_token_capacity = 0;
+    }
+
+    while(tok->m_next_size || (tok->i < tok->s_size)) {
+
+        if(tok->m_next_size) {
+            Tokenizer_set(tok, tok->m_next, tok->m_next_size);
+            tok->m_next=0;
+            tok->m_next_size=0;
+            return tok->m_token;
+        }
+
+        switch(tok->s[tok->i]) {
+            case '"':
+            case '\'':
+            if(tok->tagMode) {
+                if(!quotMode) quotMode=tok->s[tok->i];
+                else if(quotMode==tok->s[tok->i]) quotMode=0;
+            }
+            Tokenizer_append(tok, tok->s[tok->i]);
+            break;
+            case '<':
+            if(!quotMode&&(tok->i+4<tok->s_size)&&(strncmp(tok->s+tok->i,"<!--",4)==0)) // strip comments
+                tok->i=find(tok->s, "-->", tok->i+4)+2;
+            else if(!quotMode&&(tok->i+9<tok->s_size)&&(strncmp(tok->s+tok->i,"<![CDATA[",9)==0)) { // interpet CDATA
+                size_t b=tok->i+9;
+                tok->i=find(tok->s, "]]>",b)+3;
+                if(!tok->m_token_size) return Tokenizer_set(tok, tok->s+b, tok->i-b-3);
+                tokenComplete = 1;
+                tok->m_next = tok->s+b;
+                tok->m_next_size = tok->i-b-3;
+                --tok->i;
+            }
+            else if(!quotMode&&(tok->i+1<tok->s_size)&&((tok->s[tok->i+1]=='?')||(tok->s[tok->i+1]=='!'))) // strip meta information
+                tok->i=find(tok->s, ">", tok->i+2);
+            else if(!quotMode&&!tok->tagMode) {
+                if((tok->i+1<tok->s_size)&&(tok->s[tok->i+1]=='/')) {
+                    tok->m_next=ESC_str;
+                    tok->m_next_size = 1;
+                    tok->i=find(tok->s, ">", tok->i+2);
+                }
+                else {
+                    tok->m_next = OPEN_str;
+                    tok->m_next_size = 1;
+                    tok->tagMode=1;
+                }
+                tokenComplete = 1;
+            }
+            else Tokenizer_append(tok, tok->s[tok->i]);
+            break;
+            case '/':
+            if(tok->tagMode&&!quotMode) {
+                tokenComplete = 1;
+                if((tok->i+1 < tok->s_size) && (tok->s[tok->i+1]=='>')) {
+                    tok->tagMode=0;
+                    tok->m_next=ESC_str;
+                    tok->m_next_size = 1;
+                    ++tok->i;
+                }
+                else Tokenizer_append(tok, tok->s[tok->i]);
+            }
+            else Tokenizer_append(tok, tok->s[tok->i]);
+            break;
+            case '>':
+            if(!quotMode&&tok->tagMode) {
+                tok->tagMode=0;
+                tokenComplete = 1;
+                tok->m_next = CLOSE_str;
+                tok->m_next_size = 1;
+            }
+            else Tokenizer_append(tok, tok->s[tok->i]);
+            break;
+            case ' ':
+            case '\r':
+            case '\n':
+            case '\t':
+            if(tok->tagMode&&!quotMode) {
+                if(tok->m_token_size) tokenComplete=1;
+            }
+            else if(tok->m_token_size) Tokenizer_append(tok, tok->s[tok->i]);
+            break;
+            default: Tokenizer_append(tok, tok->s[tok->i]);
+        }
+        ++tok->i;
+        if((tok->i>=tok->s_size)||(tokenComplete&&tok->m_token_size)) {
+            tokenComplete=0;
+            while(tok->m_token_size&&isspace(tok->m_token[tok->m_token_size-1])) // trim whitespace
+                tok->m_token[--tok->m_token_size]=0;
+            if(tok->m_token_size) break;
+        }
+    }
+    //Tokenizer_print(tok);
+    return tok->m_token;
+}
+
+//--- local variables ----------------------------------------------
+
+/// stores number of special character codes
+static size_t sv_code_size=0;
+/// stores currently allocated capacity for special character codes
+static size_t sv_code_capacity=16;
+/// stores code table for special characters
+static char** sv_code=0;
+
+//--- public methods -----------------------------------------------
+
+static void Xml_pushDecode(lua_State* L, const char* s, size_t s_size) {
+
+    luaL_Buffer b;
+    const char* found = strstr(s, "&#");
+    size_t start=0, pos, i;
+
+    if(!s_size)
+        s_size=strlen(s);
+
+    luaL_buffinit(L, &b);
+    found = strstr(s, "&#");
+    pos = found ? found-s : s_size;
+
+    while(found) {
+        char ch = 0;
+        size_t i=0;
+        for(found += 2; i<3; ++i, ++found)
+            if(isdigit(*found))
+                ch = ch * 10 + (*found - 48);
+            else break;
+        if(*found == ';') {
+            if(pos>start)
+                luaL_addlstring(&b, s+start, pos-start);
+            luaL_addchar(&b, ch);
+            start = pos + 3 + i;
+        }
+        found = strstr(found+1, "&#");
+        pos = found ? found-s : s_size;
+    }
+    if(pos>start)
+        luaL_addlstring(&b,s+start, pos-start);
+    luaL_pushresult(&b);
+
+    for(i=sv_code_size-1; i<sv_code_size; i-=2) {
+        luaL_gsub(L, lua_tostring(L,-1), sv_code[i], sv_code[i-1]);
+        lua_remove(L,-2);
+    }
+}
+
+int Xml_eval(lua_State *L) {
+    char* str = 0;
+    size_t str_size=0;
+    Tokenizer* tok;
+    const char* token=0;
+    int firstStatement = 1;
+
+    if(lua_isuserdata(L,1))
+        str = (char*)lua_touserdata(L,1);
+    else {
+        const char * sTmp = luaL_checklstring(L,1,&str_size);
+        str = (char*)malloc(str_size+1);
+        memcpy(str, sTmp, str_size);
+        str[str_size]=0;
+    }
+    tok = Tokenizer_new(str, str_size ? str_size : strlen(str));
+    lua_settop(L,0);
+
+    while((token=Tokenizer_next(tok))!=0) if(token[0]==OPN) { // new tag found
+        if(lua_gettop(L)) {
+            int newIndex=lua_rawlen(L,-1)+1;
+            lua_pushnumber(L,newIndex);
+            lua_newtable(L);
+            lua_settable(L, -3);
+            lua_pushnumber(L,newIndex);
+            lua_gettable(L,-2);
+        }
+        else {
+            if (firstStatement) {
+                lua_newtable(L);
+                firstStatement = 0;
+            }
+            else return lua_gettop(L);
+        }
+        // set metatable:
+        lua_newtable(L);
+        lua_pushliteral(L, "__index");
+        lua_getglobal(L, "xml");
+        lua_settable(L, -3);
+
+        lua_pushliteral(L, "__tostring"); // set __tostring metamethod
+        lua_getglobal(L, "xml");
+        lua_pushliteral(L,"str");
+        lua_gettable(L, -2);
+        lua_remove(L, -2);
+        lua_settable(L, -3);
+        lua_setmetatable(L, -2);
+
+        // parse tag and content:
+        lua_pushnumber(L,0); // use index 0 for storing the tag
+        lua_pushstring(L, Tokenizer_next(tok));
+        lua_settable(L, -3);
+
+        while(((token = Tokenizer_next(tok))!=0)&&(token[0]!=CLS)&&(token[0]!=ESC)) { // parse tag header
+            size_t sepPos=find(token, "=", 0);
+            if(token[sepPos]) { // regular attribute
+                const char* aVal =token+sepPos+2;
+                size_t lenVal;
+
+                lua_pushlstring(L, token, sepPos);
+                lenVal = strlen(aVal)-1;
+                if(!lenVal) Xml_pushDecode(L, "", 0);
+                else Xml_pushDecode(L, aVal, lenVal);
+                lua_settable(L, -3);
+            }
+        }
+        if(!token||(token[0]==ESC)) {
+            if(lua_gettop(L)>1) lua_settop(L,-2); // this tag has no content, only attributes
+            else break;
+        }
+    }
+    else if(token[0]==ESC) { // previous tag is over
+        if(lua_gettop(L)>1) lua_settop(L,-2); // pop current table
+        else break;
+    }
+    else { // read elements
+        lua_pushnumber(L,lua_rawlen(L,-1)+1);
+        Xml_pushDecode(L, token, 0);
+        lua_settable(L, -3);
+    }
+    Tokenizer_delete(tok);
+    free(str);
+    return lua_gettop(L);
+}
+
+int Xml_load (lua_State *L) {
+    const char * filename = luaL_checkstring(L,1);
+    FILE * file=fopen(filename,"r");
+    char* buffer;
+    size_t sz;
+
+    if(!file)
+        return luaL_error(L,"LuaXml ERROR: \"%s\" file error or file not found!",filename);
+
+    fseek (file , 0 , SEEK_END);
+    sz = ftell (file);
+    rewind (file);
+    buffer = (char*)malloc(sz+1);
+    sz = fread (buffer,1,sz,file);
+    fclose(file);
+    buffer[sz]=0;
+    lua_pushlightuserdata(L,buffer);
+    lua_replace(L,1);
+    return Xml_eval(L);
+};
+
+int Xml_registerCode(lua_State *L) {
+    const char * decoded = luaL_checkstring(L,1);
+    const char * encoded = luaL_checkstring(L,2);
+
+    size_t i;
+    for(i=0; i<sv_code_size; i+=2)
+        if(strcmp(sv_code[i],decoded)==0)
+            return luaL_error(L,"LuaXml ERROR: code already exists.");
+    if(sv_code_size+2>sv_code_capacity) {
+        sv_code_capacity*=2;
+        sv_code = (char**)realloc(sv_code, sv_code_capacity*sizeof(char*));
+    }
+    sv_code[sv_code_size]=(char*)malloc(strlen(decoded)+1);
+    strcpy(sv_code[sv_code_size++], decoded);
+    sv_code[sv_code_size]=(char*)malloc(strlen(encoded)+1);
+    strcpy(sv_code[sv_code_size++],encoded);
+    return 0;
+}
+
+int Xml_encode(lua_State *L) {
+
+    char buf[8];
+    size_t start, pos;
+    luaL_Buffer b;
+    const char* s;
+    size_t i;
+
+    if(lua_gettop(L)!=1)
+        return 0;
+    luaL_checkstring(L,-1);
+
+    for(i=0; i<sv_code_size; i+=2) {
+        luaL_gsub(L, lua_tostring(L,-1), sv_code[i], sv_code[i+1]);
+        lua_remove(L,-2);
+    }
+    s=lua_tostring(L,1);
+    luaL_buffinit(L, &b);
+    for(start=pos=0; s[pos]!=0; ++pos) if(s[pos]<0) {
+        if(pos>start) luaL_addlstring(&b,s+start, pos-start);
+        luaL_addstring(&b,char2code((unsigned char)(s[pos]),buf));
+        start=pos+1;
+    }
+    if(pos>start)
+        luaL_addlstring(&b,s+start, pos-start);
+    luaL_pushresult(&b);
+    lua_remove(L,-2);
+    return 1;
+}
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+int _EXPORT luaopen_LuaXML_lib (lua_State* L) {
+    static const struct luaL_Reg funcs[] = {
+        {"load", Xml_load},
+        {"eval", Xml_eval},
+        {"encode", Xml_encode},
+        {"registerCode", Xml_registerCode},
+        {NULL, NULL}
+    };
+
+    luaL_newlibtable(L, funcs);
+    luaL_setfuncs(L, funcs, 0);
+    lua_setglobal(L, "xml");
+
+    // register default codes:
+    if(!sv_code) {
+        sv_code=(char**)malloc(sv_code_capacity*sizeof(char*));
+        sv_code[sv_code_size++]="&";
+        sv_code[sv_code_size++]="&amp;";
+        sv_code[sv_code_size++]="<";
+        sv_code[sv_code_size++]="&lt;";
+        sv_code[sv_code_size++]=">";
+        sv_code[sv_code_size++]="&gt;";
+        sv_code[sv_code_size++]="\"";
+        sv_code[sv_code_size++]="&quot;";
+        sv_code[sv_code_size++]="'";
+        sv_code[sv_code_size++]="&apos;";
+    }
+    return 1;
+}
+#ifdef __cplusplus
+} // extern "C"
+#endif

+ 5 - 5
src/timer.inl

@@ -5,17 +5,17 @@
 
 
 typedef int (*taction)(void *arg);
 typedef int (*taction)(void *arg);
 
 
-struct timer {
+struct ttimer {
     double time;
     double time;
     double period;
     double period;
     taction action;
     taction action;
     void * arg;
     void * arg;
 };
 };
 
 
-struct timers {
+struct ttimers {
     pthread_t threadid;               /* Timer thread ID */
     pthread_t threadid;               /* Timer thread ID */
     pthread_mutex_t mutex;            /* Protects timer lists */
     pthread_mutex_t mutex;            /* Protects timer lists */
-    struct timer timers[MAX_TIMERS];  /* List of timers */
+    struct ttimer timers[MAX_TIMERS];  /* List of timers */
     unsigned timer_count;             /* Current size of timer list */
     unsigned timer_count;             /* Current size of timer list */
 };
 };
 
 
@@ -64,7 +64,7 @@ static void timer_thread_run(void *thread_func_param)
     double d;
     double d;
     unsigned u;
     unsigned u;
     int re_schedule;
     int re_schedule;
-    struct timer t;
+    struct ttimer t;
 
 
 #if defined(HAVE_CLOCK_NANOSLEEP) /* Linux with librt */
 #if defined(HAVE_CLOCK_NANOSLEEP) /* Linux with librt */
     /* TODO */
     /* TODO */
@@ -113,7 +113,7 @@ static void *timer_thread(void *thread_func_param)
 
 
 static int timers_init(struct mg_context * ctx)
 static int timers_init(struct mg_context * ctx)
 {
 {
-    ctx->timers = (struct timers*) mg_calloc(sizeof(struct timers), 1);
+    ctx->timers = (struct ttimers*) mg_calloc(sizeof(struct ttimers), 1);
     (void) pthread_mutex_init(&ctx->timers->mutex, NULL);
     (void) pthread_mutex_init(&ctx->timers->mutex, NULL);
 
 
     /* Start timer thread */
     /* Start timer thread */

+ 200 - 0
test/MethodTest.xhtml

@@ -0,0 +1,200 @@
+<!DOCTYPE HTML>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<meta http-equiv="content-type" content="text/html; charset=UTF-8" />
+  <title>HTTP method test</title>
+  <style type="text/css" media="screen">
+    body {background:#eee; margin:0%; padding:0%; padding-top:0%; padding-left:1%}
+    .cform {margin:0%; padding:0%; padding-top:0%; padding-left:2%;}
+    h3 {margin:0%; padding:0%; padding-top:0%; padding-left:0%;}
+    td {vertical-align:top; text-align:left;}
+  </style>
+  <script type="text/javascript"><![CDATA[
+
+    function getParams() {
+      var result = {};
+      var kvPairs = location.search.slice(1).split('&');
+
+      kvPairs.forEach(
+        function(kvPair) {
+          kvPair = kvPair.split('=');
+          result[kvPair[0]] = kvPair[1] || '';
+        }
+      );
+
+      return result;
+    }
+
+    function noBody() {
+      document.getElementById("body_none").checked = true;
+    }
+
+    function load() {
+      var params = getParams();
+      var method = params["method"];
+      if (!method) {
+        method = "GET";
+      }
+      var path = params["path"];
+      if (!path) {
+        path = "";
+      }
+
+      var elem = document.getElementById('h1');
+      elem.innerHTML = "HTTP method test page";
+
+      document.getElementById("proto_http").checked = (window.location.protocol != "https:");
+      document.getElementById("proto_https").checked = (window.location.protocol == "https:");
+      document.getElementById("server").value = location.host;
+      document.getElementById("resource").value = path;
+
+      setRadioValue("method", method);
+      noBody();
+    }
+
+    function setRadioValue(elmname, value) {
+      var elms = document.getElementsByName(elmname);
+      var len = elms.length;
+      var ret = false;
+
+      for (var i=0; i<len; i++) {
+        elms[i].checked = (elms[i].value == value);
+        ret |= elms[i].checked;
+      }
+      return ret;
+    }
+
+    function getRadioValue(elmname) {
+
+      var elms = document.getElementsByName(elmname);
+      var len = elms.length;
+      var ret = "";
+
+      for (var i=0; i<len; i++) {
+        if (elms[i].checked) {
+          ret = elms[i].value;
+        }
+      }
+      return ret;
+    }
+
+    function sendreq() {
+      var proto = getRadioValue("protocol");
+      var host = document.getElementById("server").value;
+      var res = document.getElementById("resource").value;
+      var addr = proto + "://" + host + "/" + res;
+      var meth = getRadioValue("method");
+      var body = getRadioValue("body");
+
+      xmlhttp = new XMLHttpRequest();
+      if (!xmlhttp) {
+        alert("XMLHttpRequest not available");
+        window.history.back();
+      }
+
+      xmlhttp.open(meth,addr,true);
+
+      if (body == '*') {
+        body = null;
+      } else {
+        if (body == '**') {
+          var body_bytes = document.getElementById("body_bytes").value;
+          body_bytes = parseInt(Number(body_bytes) || 0) || 0;
+          body = "";
+          for (var i=0; i<body_bytes; i++) {
+            var ascii = Math.floor((Math.random() * 94) + 32);
+            body = body + String.fromCharCode(ascii);
+          }
+        }
+        xmlhttp.setRequestHeader("Content-Length", body.length);
+      }
+
+      xmlhttp.onreadystatechange = function()
+      {
+          var laddr = addr;
+          var lmeth = meth;
+          var blen = "";
+          if (body) {
+            blen = "\nWith " + body.length + " bytes body data";
+          }
+
+          if (xmlhttp.readyState == 4)
+          {
+              alert(lmeth + " " + laddr + blen + "\n\nResponse: " + xmlhttp.status + "\n\n" + xmlhttp.responseText);
+          }
+      }
+
+      xmlhttp.send(body);
+
+    }
+
+  ]]></script>
+
+</head>
+<body onload="load()">
+
+<h1 id='h1'>Fatal error: Javascript not available!</h1>
+
+<h2>Test parameters</h2>
+<form lass="cform">
+
+<h3>Protocol</h3>
+<input id="proto_http" type="radio" name="protocol" value="http" /> http <br />
+<input id="proto_https" type="radio" name="protocol" value="https" /> https
+
+<h3>Server/Host</h3>
+<input id="server" type="text" name="server" value="" />
+
+<h3>Resource</h3>
+<input id="resource" type="text" name="resource" value="" />
+
+<h3>Method</h3>
+<!-- http://www.restpatterns.org/HTTP_Methods -->
+<!-- http://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html -->
+<table style="border-spacing:15px 0px;">
+  <tr>
+    <td><a href="http://tools.ietf.org/html/rfc7231#section-4.2.1">Save Methods</a></td>
+    <td>"Unsave" <a href="http://tools.ietf.org/html/rfc7231#section-4.2.2">Idempotent Methods</a></td>
+    <td>Non-Idempotent Methods</td>
+    <td>Special</td>
+  </tr>
+  <tr>
+    <td>
+<input id="method_opt" type="radio" name="method" value="OPTIONS" onclick="noBody()" /> OPTIONS <br />
+<input id="method_get" type="radio" name="method" value="GET" onclick="noBody()" /> GET <br />
+<input id="method_hea" type="radio" name="method" value="HEAD" onclick="noBody()" /> HEAD <br />
+<input id="method_tra" type="radio" name="method" value="TRACE" /> TRACE <br />
+<input id="method_pro" type="radio" name="method" value="PROPFIND" /> PROPFIND <br />
+    </td>
+    <td>
+<input id="method_put" type="radio" name="method" value="PUT" /> PUT <br />
+<input id="method_del" type="radio" name="method" value="DELETE" /> DELETE <br />
+<input id="method_cop" type="radio" name="method" value="COPY" /> COPY <br />
+<input id="method_cop" type="radio" name="method" value="MOVE" /> MOVE <br />
+<input id="method_ppa" type="radio" name="method" value="PROPPATCH" /> PROPPATCH <br />
+<input id="method_unl" type="radio" name="method" value="UNLOCK" /> UNLOCK <br />
+    </td>
+    <td>
+<input id="method_pos" type="radio" name="method" value="POST" /> POST <br />
+<input id="method_pat" type="radio" name="method" value="PATCH" /> PATCH <br />
+<input id="method_mkc" type="radio" name="method" value="MKCOL" /> MKCOL <br />
+<input id="method_loc" type="radio" name="method" value="LOCK" /> LOCK <br />
+    </td>
+    <td>
+<input id="method_con" type="radio" name="method" value="CONNECT" /> CONNECT <br />
+<input id="method_userdef" type="radio" name="method" value="INVALID" /> <input id="method_name" type="text" name="method_name" value="INVALID" oninput="var elem = document.getElementById('method_userdef'); elem.checked = true; elem.value=value" /> <br />
+    </td>
+  </tr>
+</table>
+
+<h3>Body data</h3>
+<input id="body_none" type="radio" name="body" value="*" /> No body data <br />
+<input id="body_10" type="radio" name="body" value="1234567890" /> 10 Bytes ("1234567890") <br />
+<input id="body_rnd" type="radio" name="body" value="**" /> <input id="body_bytes" type="number" name="body_bytes" value="100" min="0" step="0" max="999999999" oninput="document.getElementById('body_rnd').checked = true" /> Bytes random data <br />
+
+<h3>Submit</h3>
+<input id="send" type="button" onclick="sendreq()" value="Send request" />
+
+</form>
+
+</body></html>

+ 3 - 1
test/page.lp

@@ -5,7 +5,7 @@ Content-Type: text/html
 
 
 
 
 <p>This is another example of a Lua server page, served by
 <p>This is another example of a Lua server page, served by
-<a href="http://code.google.com/p/civetweb">Civetweb web server</a>.
+<a href="http://sourceforge.net/projects/civetweb/">Civetweb web server</a>.
 </p><p>
 </p><p>
 The following features are available:
 The following features are available:
 <ul>
 <ul>
@@ -28,6 +28,8 @@ The following features are available:
 <?
 <?
   -- Open database
   -- Open database
   local db = sqlite3.open('requests.db')
   local db = sqlite3.open('requests.db')
+  -- Note that the data base is located in the current working directory
+  -- of the process if no other path is given here.
 
 
   -- Setup a trace callback, to show SQL statements we'll be executing.
   -- Setup a trace callback, to show SQL statements we'll be executing.
   -- db:trace(function(data, sql) mg.write('Executing: ', sql: '\n') end, nil)
   -- db:trace(function(data, sql) mg.write('Executing: ', sql: '\n') end, nil)

+ 3 - 1
test/page.lua

@@ -3,7 +3,7 @@ mg.write("HTTP/1.0 200 OK\r\nContent-Type: text/html\r\n\r\n")
 mg.write([[
 mg.write([[
 <html><body>
 <html><body>
 <p>This is another example of a Lua script, creating a web page served by the
 <p>This is another example of a Lua script, creating a web page served by the
-<a href="http://code.google.com/p/civetweb">Civetweb web server</a>.
+<a href="http://sourceforge.net/projects/civetweb/">Civetweb web server</a>.
 </p><p>
 </p><p>
 The following features are available:
 The following features are available:
 <ul>
 <ul>
@@ -26,6 +26,8 @@ mg.write("<p>Database example:\r\n<pre>\r\n")
 
 
   -- Open database
   -- Open database
   local db = sqlite3.open('requests.db')
   local db = sqlite3.open('requests.db')
+  -- Note that the data base is located in the current working directory
+  -- of the process if no other path is given here.
 
 
   -- Setup a trace callback, to show SQL statements we'll be executing.
   -- Setup a trace callback, to show SQL statements we'll be executing.
   -- db:trace(function(data, sql) mg.write('Executing: ', sql: '\n') end, nil)
   -- db:trace(function(data, sql) mg.write('Executing: ', sql: '\n') end, nil)

+ 1 - 1
test/page2.lp

@@ -4,7 +4,7 @@
 <html><body>
 <html><body>
 
 
 <p>This is another example of a Lua server page, served by
 <p>This is another example of a Lua server page, served by
-<a href="http://code.google.com/p/civetweb">Civetweb web server</a>.
+<a href="http://sourceforge.net/projects/civetweb/">Civetweb web server</a>.
 </p><p>
 </p><p>
 The following features are available:
 The following features are available:
 <ul>
 <ul>

+ 1 - 1
test/page2.lua

@@ -4,7 +4,7 @@ mg.write("\r\n")
 mg.write([[<html><body>
 mg.write([[<html><body>
 
 
 <p>This is another example of a Lua server page, served by
 <p>This is another example of a Lua server page, served by
-<a href="http://code.google.com/p/civetweb">Civetweb web server</a>.
+<a href="http://sourceforge.net/projects/civetweb/">Civetweb web server</a>.
 </p><p>
 </p><p>
 The following features are available:
 The following features are available:
 <ul>
 <ul>

+ 14 - 0
test/page4.lua

@@ -98,6 +98,20 @@ else
 end
 end
 mg.write("\r\n")
 mg.write("\r\n")
 
 
+-- test get_response_code_text
+mg.write("HTTP helper methods test:\r\n")
+if (htmlEscape("<a b & c d>") == "&lt;a b &amp; c d&gt;") then
+    mg.write("  htmlEscape test OK\r\n")
+else
+    mg.write("  Error: htmlEscape test NOT OK\r\n")
+end
+if (mg.get_response_code_text(200) == "OK") then
+    mg.write("  get_response_code_text test OK\r\n")
+else
+    mg.write("  Error: get_response_code_text test NOT OK\r\n")
+end
+mg.write("\r\n")
+
 -- url_encode
 -- url_encode
 mg.write("URL encode/decode test:\r\n")
 mg.write("URL encode/decode test:\r\n")
 if mg.url_encode("") == "" then
 if mg.url_encode("") == "" then

+ 1 - 1
test/page_keep_alive.lua

@@ -5,7 +5,7 @@ if canKeepAlive then
     -- Create the entire response in a string variable first. Content-Length will be set to the length of this string.
     -- Create the entire response in a string variable first. Content-Length will be set to the length of this string.
     reply = [[
     reply = [[
         <html><body>
         <html><body>
-        <p>This is a Lua script supporting html keep-alive with the <a href="http://code.google.com/p/civetweb">Civetweb web server</a>.</p>
+        <p>This is a Lua script supporting html keep-alive with the <a href="http://sourceforge.net/projects/civetweb/">Civetweb web server</a>.</p>
         <p>It works by setting the Content-Length header field properly.
         <p>It works by setting the Content-Length header field properly.
         </body></html>
         </body></html>
     ]]
     ]]

+ 151 - 0
test/testclient.c

@@ -0,0 +1,151 @@
+#include <stdio.h>
+#include <time.h>
+
+#if defined(_WIN32) || defined(WIN32) 
+#include <Windows.h>
+void INIT(void) {WSADATA wsaData; WSAStartup(MAKEWORD(2,2), &wsaData);}
+#else
+#define INIT()
+#include <unistd.h>
+#include <netdb.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#endif
+
+int connect_to_server(const struct sockaddr_in * serv_addr)
+{
+    int sockfd;
+
+    /* Create a socket */
+    sockfd = socket(AF_INET, SOCK_STREAM, 0);
+    if (sockfd < 0) {
+        perror("ERROR opening socket");
+        return -1;
+    }
+
+    /* Connect to the server */
+    if (connect(sockfd, (const sockaddr *)serv_addr, sizeof(*serv_addr)) < 0) {
+         perror("ERROR connecting");
+         close(sockfd);
+         return -2;
+    }
+
+    return sockfd;
+}
+
+int send_to_server(int conn, const char * request)
+{
+    int req_len = strlen(request);
+    int n;
+
+    n = write(conn, request, req_len);
+    if (n < 0) {
+         perror("ERROR writing to socket");
+         return 0;
+    }
+
+    return (n==req_len);
+}
+
+int read_from_server(int conn)
+{
+    char rbuffer[1024];
+    int n;
+    long ret;
+
+    n = read(conn, rbuffer, sizeof(rbuffer));
+    if (n < 0) {
+         perror("ERROR reading from socket");
+         return 0;
+    }
+
+    if (strncmp("HTTP/1.", rbuffer, 7)) {
+         perror("ERROR not a HTTP response");
+         return 0;
+    }
+
+    ret = atol(rbuffer + 9);
+
+    return ret;
+}
+
+
+int main(int argc, char *argv[])
+{
+    long portno;
+    int i, con_count=1;
+    time_t t1,t2,t3,t4;
+    char wbuffer[256];
+    int connlist[1024*65];
+    int result[1024*65];
+    struct hostent *server;
+    struct sockaddr_in serv_addr;
+
+    INIT();
+
+    if (argc != 4) {
+        fprintf(stderr,"Usage:\n\t%s hostname port clients\n\n", argv[0]);
+        exit(0);
+    }
+
+    con_count = atol(argv[3]);
+    if (con_count<1) con_count=1;
+    if (con_count>1024*65) con_count=1024*65;
+
+    portno = atol(argv[2]);
+    if (portno<1l || portno>0xFFFFl) {
+        fprintf(stderr, "ERROR, invalid port\n");
+        exit(0);
+    }
+
+    server = gethostbyname(argv[1]);
+    if (server == NULL) {
+        fprintf(stderr, "ERROR, no such host\n");
+        exit(0);
+    }
+
+    memset(&serv_addr, 0, sizeof(serv_addr));
+    serv_addr.sin_family = AF_INET;
+    memcpy(server->h_addr, &serv_addr.sin_addr.s_addr, server->h_length);
+    serv_addr.sin_port = htons((short)portno);
+
+    sprintf(wbuffer, "GET / HTTP/1.0\r\n\r\n");
+
+    t1 = time(0);
+    for (i=0;i<con_count;i++) {
+        result[i] = connlist[i] = connect_to_server(&serv_addr);
+    }
+    t2 = time(0);
+    for (i=0;i<con_count;i++) {
+        if (result[i]>=0) {
+            result[i] = send_to_server(connlist[i], wbuffer);
+        }
+    }
+    t3 = time(0);
+    for (i=0;i<con_count;i++) {
+        if (result[i]>=0) {
+            result[i] = read_from_server(connlist[i]);
+        }
+    }
+    t4 = time(0);
+
+    printf("\n");
+    printf("conn:  %.0lf\n", difftime(t2,t1));
+    printf("write: %.0lf\n", difftime(t3,t2));
+    printf("read:  %.0lf\n", difftime(t4,t3));
+
+    for (i=-10;i<1000;i++) {
+        int j,cnt=0;
+        for(j=0;j<con_count;j++) {
+            if (result[j]==i) cnt++;
+        }
+        if (cnt>0) {
+            printf("%5i\t%7u\n", i, cnt);
+        }
+    }
+
+    return 0;
+}
+
+

部分文件因为文件数量过多而无法显示