Jelajahi Sumber

Merge pull request #914 from civetweb/http2

Merge HTTP2 prototype into master
bel2125 4 tahun lalu
induk
melakukan
0a39165041

+ 5 - 0
CREDITS.md

@@ -43,6 +43,7 @@
 * Drew Wells
 * Drew Wells
 * duong2179
 * duong2179
 * ehlertjd
 * ehlertjd
+* Elan P. Kugelmass
 * eugene
 * eugene
 * Eric Tsau
 * Eric Tsau
 * Erick Vieyra
 * Erick Vieyra
@@ -54,6 +55,8 @@
 * feneuilflo
 * feneuilflo
 * Fernando G. Aranda
 * Fernando G. Aranda
 * Frank Hilliger
 * Frank Hilliger
+* Gábor Csárdi
+* goodmenzy
 * Grahack
 * Grahack
 * Gregor Jasny
 * Gregor Jasny
 * grenclave
 * grenclave
@@ -139,6 +142,7 @@
 * Philipp Hasper
 * Philipp Hasper
 * Piotr Zierhoffer
 * Piotr Zierhoffer
 * pkvamme
 * pkvamme
+* Ponnuvel Palaniyappan
 * Radoslaw Zarzynski
 * Radoslaw Zarzynski
 * Red54
 * Red54
 * Retallack Mark mark.retallack
 * Retallack Mark mark.retallack
@@ -159,6 +163,7 @@
 * Simon Hailes
 * Simon Hailes
 * slidertom
 * slidertom
 * SpaceLord
 * SpaceLord
+* Stefan Codrescu
 * sunfch
 * sunfch
 * suzukibitman
 * suzukibitman
 * Símal Rasmussen
 * Símal Rasmussen

+ 6 - 0
Makefile

@@ -113,6 +113,7 @@ else
   LCC = $(CC)
   LCC = $(CC)
 endif
 endif
 
 
+
 ifdef WITH_ALL
 ifdef WITH_ALL
   WITH_WEBSOCKET = 1
   WITH_WEBSOCKET = 1
   WITH_IPV6 = 1
   WITH_IPV6 = 1
@@ -120,6 +121,7 @@ ifdef WITH_ALL
   WITH_DUKTAPE = 1
   WITH_DUKTAPE = 1
   WITH_SERVER_STATS = 1
   WITH_SERVER_STATS = 1
   WITH_ZLIB = 1
   WITH_ZLIB = 1
+  WITH_HTTP2 = 1
   WITH_EXPERIMENTAL = 1
   WITH_EXPERIMENTAL = 1
   #WITH_CPP is not defined, ALL means only real features, not wrappers
   #WITH_CPP is not defined, ALL means only real features, not wrappers
 endif
 endif
@@ -170,6 +172,10 @@ ifdef WITH_ZLIB
   CFLAGS += -DUSE_ZLIB
   CFLAGS += -DUSE_ZLIB
 endif
 endif
 
 
+ifdef WITH_HTTP2
+  CFLAGS += -DUSE_HTTP2
+endif
+
 # Other features
 # Other features
 ifdef WITH_EXPERIMENTAL
 ifdef WITH_EXPERIMENTAL
   CFLAGS += -DMG_EXPERIMENTAL_INTERFACES
   CFLAGS += -DMG_EXPERIMENTAL_INTERFACES

+ 17 - 1
RELEASE_NOTES.md

@@ -1,10 +1,26 @@
 Release Notes v1.13
 Release Notes v1.13
 ===
 ===
-### *work in progress*
+### Objectives: *Various updates and fixes*
 
 
 Changes
 Changes
 -------
 -------
 
 
+- Add arguments for CGI interpreters
+- Support multiple CGi interpreters
+- Some new API functions
+- Fix some memory leaks
+- Extended use of atomic operations (e.g., for server stats)
+- Add fuzz tests
+- Set OpenSSL 1.1 API as default (from 1.0)
+- Add Lua 5.4 support and deprecate Lua 5.1
+- Provide additional Lua API functions
+- Fix Lua websocket memory leak when closing the server
+- Remove obsolete "file in memory" implementation
+- Improvements and fixes in documentation
+- Fixes from static source code analysis
+- Additional unit tests
+- Various small bugfixes
+- Experimental support for some HTTP2 features (not ready for production)
 - Update version number
 - Update version number
 
 
 
 

+ 5 - 4
VisualStudio/civetweb_lua/civetweb_lua.vcxproj

@@ -102,7 +102,7 @@
       </PrecompiledHeader>
       </PrecompiledHeader>
       <WarningLevel>Level3</WarningLevel>
       <WarningLevel>Level3</WarningLevel>
       <Optimization>Disabled</Optimization>
       <Optimization>Disabled</Optimization>
-      <PreprocessorDefinitions>USE_HTTP2;MG_EXPERIMENTAL_INTERFACES;USE_SERVER_STATS;USE_DUKTAPE;USE_IPV6;LUA_COMPAT_ALL;USE_LUA;USE_LUA_SQLITE3;USE_LUA_FILE_SYSTEM;USE_LUA_LUAXML;USE_WEBSOCKET;WIN32;_DEBUG;_WINDOWS;_CRT_SECURE_NO_DEPRECATE;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <PreprocessorDefinitions>STOP_FLAG_NEEDS_LOCK;OPENSSL_API_1_0;USE_HTTP2;MG_EXPERIMENTAL_INTERFACES;USE_SERVER_STATS;USE_DUKTAPE;USE_IPV6;LUA_COMPAT_ALL;USE_LUA;USE_LUA_SQLITE3;USE_LUA_FILE_SYSTEM;USE_LUA_LUAXML;USE_WEBSOCKET;WIN32;_DEBUG;_WINDOWS;_CRT_SECURE_NO_DEPRECATE;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
       <AdditionalIncludeDirectories>$(ProjectDir)..\..\include;$(ProjectDir)..\..\src\third_party;$(ProjectDir)..\..\src\third_party\lua-5.2.4\src;$(ProjectDir)..\..\src\third_party\duktape-1.5.2\src;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
       <AdditionalIncludeDirectories>$(ProjectDir)..\..\include;$(ProjectDir)..\..\src\third_party;$(ProjectDir)..\..\src\third_party\lua-5.2.4\src;$(ProjectDir)..\..\src\third_party\duktape-1.5.2\src;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
     </ClCompile>
     </ClCompile>
     <Link>
     <Link>
@@ -119,7 +119,7 @@
       </PrecompiledHeader>
       </PrecompiledHeader>
       <WarningLevel>Level3</WarningLevel>
       <WarningLevel>Level3</WarningLevel>
       <Optimization>Disabled</Optimization>
       <Optimization>Disabled</Optimization>
-      <PreprocessorDefinitions>USE_SERVER_STATS;USE_DUKTAPE;USE_IPV6;LUA_COMPAT_ALL;USE_LUA;USE_LUA_SQLITE3;USE_LUA_FILE_SYSTEM;USE_WEBSOCKET;WIN32;_DEBUG;_WINDOWS;_CRT_SECURE_NO_DEPRECATE;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <PreprocessorDefinitions>USE_HTTP2;USE_SERVER_STATS;USE_DUKTAPE;USE_IPV6;LUA_COMPAT_ALL;USE_LUA;USE_LUA_SQLITE3;USE_LUA_FILE_SYSTEM;USE_WEBSOCKET;WIN32;_DEBUG;_WINDOWS;_CRT_SECURE_NO_DEPRECATE;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
       <AdditionalIncludeDirectories>$(ProjectDir)..\..\include;$(ProjectDir)..\..\src\third_party;$(ProjectDir)..\..\src\third_party\lua-5.2.4\src;$(ProjectDir)..\..\src\third_party\duktape-1.5.2\src;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
       <AdditionalIncludeDirectories>$(ProjectDir)..\..\include;$(ProjectDir)..\..\src\third_party;$(ProjectDir)..\..\src\third_party\lua-5.2.4\src;$(ProjectDir)..\..\src\third_party\duktape-1.5.2\src;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
     </ClCompile>
     </ClCompile>
     <Link>
     <Link>
@@ -149,7 +149,7 @@
       <Optimization>MaxSpeed</Optimization>
       <Optimization>MaxSpeed</Optimization>
       <FunctionLevelLinking>true</FunctionLevelLinking>
       <FunctionLevelLinking>true</FunctionLevelLinking>
       <IntrinsicFunctions>true</IntrinsicFunctions>
       <IntrinsicFunctions>true</IntrinsicFunctions>
-      <PreprocessorDefinitions>MG_EXPERIMENTAL_INTERFACES;USE_SERVER_STATS;USE_DUKTAPE;USE_IPV6;LUA_COMPAT_ALL;USE_LUA;USE_LUA_SQLITE3;USE_LUA_FILE_SYSTEM;USE_LUA_LUAXML;USE_WEBSOCKET;WIN32;_WINDOWS;_CRT_SECURE_NO_DEPRECATE;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <PreprocessorDefinitions>OPENSSL_API_1_0;MG_EXPERIMENTAL_INTERFACES;USE_SERVER_STATS;USE_DUKTAPE;USE_IPV6;LUA_COMPAT_ALL;USE_LUA;USE_LUA_SQLITE3;USE_LUA_FILE_SYSTEM;USE_LUA_LUAXML;USE_WEBSOCKET;WIN32;_WINDOWS;_CRT_SECURE_NO_DEPRECATE;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
       <AdditionalIncludeDirectories>$(ProjectDir)..\..\include;$(ProjectDir)..\..\src\third_party;$(ProjectDir)..\..\src\third_party\lua-5.2.4\src;$(ProjectDir)..\..\src\third_party\duktape-1.5.2\src;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
       <AdditionalIncludeDirectories>$(ProjectDir)..\..\include;$(ProjectDir)..\..\src\third_party;$(ProjectDir)..\..\src\third_party\lua-5.2.4\src;$(ProjectDir)..\..\src\third_party\duktape-1.5.2\src;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
     </ClCompile>
     </ClCompile>
     <Link>
     <Link>
@@ -170,7 +170,7 @@
       <Optimization>MaxSpeed</Optimization>
       <Optimization>MaxSpeed</Optimization>
       <FunctionLevelLinking>true</FunctionLevelLinking>
       <FunctionLevelLinking>true</FunctionLevelLinking>
       <IntrinsicFunctions>true</IntrinsicFunctions>
       <IntrinsicFunctions>true</IntrinsicFunctions>
-      <PreprocessorDefinitions>USE_SERVER_STATS;USE_DUKTAPE;USE_IPV6;LUA_COMPAT_ALL;USE_LUA;USE_LUA_SQLITE3;USE_LUA_FILE_SYSTEM;USE_WEBSOCKET;WIN32;_WINDOWS;_CRT_SECURE_NO_DEPRECATE;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <PreprocessorDefinitions>USE_HTTP2;USE_SERVER_STATS;USE_DUKTAPE;USE_IPV6;LUA_COMPAT_ALL;USE_LUA;USE_LUA_SQLITE3;USE_LUA_FILE_SYSTEM;USE_WEBSOCKET;WIN32;_DEBUG;_WINDOWS;_CRT_SECURE_NO_DEPRECATE;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
       <AdditionalIncludeDirectories>$(ProjectDir)..\..\include;$(ProjectDir)..\..\src\third_party;$(ProjectDir)..\..\src\third_party\lua-5.2.4\src;$(ProjectDir)..\..\src\third_party\duktape-1.5.2\src;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
       <AdditionalIncludeDirectories>$(ProjectDir)..\..\include;$(ProjectDir)..\..\src\third_party;$(ProjectDir)..\..\src\third_party\lua-5.2.4\src;$(ProjectDir)..\..\src\third_party\duktape-1.5.2\src;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
     </ClCompile>
     </ClCompile>
     <Link>
     <Link>
@@ -209,6 +209,7 @@
     <None Include="..\..\src\md5.inl" />
     <None Include="..\..\src\md5.inl" />
     <None Include="..\..\src\mod_http2.inl" />
     <None Include="..\..\src\mod_http2.inl" />
     <None Include="..\..\src\mod_lua_shared.inl" />
     <None Include="..\..\src\mod_lua_shared.inl" />
+    <None Include="..\..\src\response.inl" />
     <None Include="..\..\src\mod_zlib.inl" />
     <None Include="..\..\src\mod_zlib.inl" />
     <None Include="..\..\src\sha1.inl" />
     <None Include="..\..\src\sha1.inl" />
     <None Include="..\..\src\mod_duktape.inl" />
     <None Include="..\..\src\mod_duktape.inl" />

+ 18 - 68
VisualStudio/civetweb_lua/civetweb_lua.vcxproj.filters

@@ -1,81 +1,31 @@
 <?xml version="1.0" encoding="utf-8"?>
 <?xml version="1.0" encoding="utf-8"?>
 <Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
 <Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
   <ItemGroup>
   <ItemGroup>
-    <Filter Include="Source Files">
-      <UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
-      <Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
-    </Filter>
-    <Filter Include="Header Files">
-      <UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
-      <Extensions>h;hpp;hxx;hm;inl;inc;xsd</Extensions>
-    </Filter>
-    <Filter Include="Resource Files">
-      <UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>
-      <Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions>
-    </Filter>
-    <Filter Include="inl files">
-      <UniqueIdentifier>{1ef3413b-2315-48f2-ad22-57af6b4f7aca}</UniqueIdentifier>
-    </Filter>
+    <ClCompile Include="..\..\src\civetweb.c" />
+    <ClCompile Include="..\..\src\main.c" />
   </ItemGroup>
   </ItemGroup>
   <ItemGroup>
   <ItemGroup>
-    <ClInclude Include="..\..\include\civetweb.h">
-      <Filter>Header Files</Filter>
-    </ClInclude>
-    <ClInclude Include="..\..\src\third_party\lua-5.2.4\src\lua.h">
-      <Filter>Header Files</Filter>
-    </ClInclude>
-    <ClInclude Include="..\..\src\third_party\lua-5.2.4\src\lauxlib.h">
-      <Filter>Header Files</Filter>
-    </ClInclude>
-    <ClInclude Include="..\..\src\third_party\civetweb_lua.h">
-      <Filter>Header Files</Filter>
-    </ClInclude>
+    <ClInclude Include="..\..\include\civetweb.h" />
+    <ClInclude Include="..\..\src\third_party\civetweb_lua.h" />
+    <ClInclude Include="..\..\src\third_party\lua-5.2.4\src\lauxlib.h" />
+    <ClInclude Include="..\..\src\third_party\lua-5.2.4\src\lua.h" />
   </ItemGroup>
   </ItemGroup>
   <ItemGroup>
   <ItemGroup>
-    <ClCompile Include="..\..\src\civetweb.c">
-      <Filter>Source Files</Filter>
-    </ClCompile>
-    <ClCompile Include="..\..\src\main.c">
-      <Filter>Source Files</Filter>
-    </ClCompile>
+    <ResourceCompile Include="..\..\resources\res.rc" />
   </ItemGroup>
   </ItemGroup>
   <ItemGroup>
   <ItemGroup>
-    <ResourceCompile Include="..\..\resources\res.rc">
-      <Filter>Resource Files</Filter>
-    </ResourceCompile>
+    <Image Include="..\..\resources\systray.ico" />
   </ItemGroup>
   </ItemGroup>
   <ItemGroup>
   <ItemGroup>
-    <Image Include="..\..\resources\systray.ico">
-      <Filter>Resource Files</Filter>
-    </Image>
-  </ItemGroup>
-  <ItemGroup>
-    <None Include="..\..\src\md5.inl">
-      <Filter>inl files</Filter>
-    </None>
-    <None Include="..\..\src\sha1.inl">
-      <Filter>inl files</Filter>
-    </None>
-    <None Include="..\..\src\mod_lua.inl">
-      <Filter>inl files</Filter>
-    </None>
-    <None Include="..\..\src\timer.inl">
-      <Filter>inl files</Filter>
-    </None>
-    <None Include="..\..\src\mod_duktape.inl">
-      <Filter>inl files</Filter>
-    </None>
-    <None Include="..\..\src\handle_form.inl">
-      <Filter>inl files</Filter>
-    </None>
-    <None Include="..\..\src\mod_lua_shared.inl">
-      <Filter>inl files</Filter>
-    </None>
-    <None Include="..\..\src\mod_http2.inl">
-      <Filter>inl files</Filter>
-    </None>
-    <None Include="..\..\src\mod_zlib.inl">
-      <Filter>inl files</Filter>
-    </None>
+    <None Include="..\..\src\handle_form.inl" />
+    <None Include="..\..\src\md5.inl" />
+    <None Include="..\..\src\mod_http2.inl" />
+    <None Include="..\..\src\mod_lua_shared.inl" />
+    <None Include="..\..\src\response.inl" />
+    <None Include="..\..\src\mod_zlib.inl" />
+    <None Include="..\..\src\sha1.inl" />
+    <None Include="..\..\src\mod_duktape.inl" />
+    <None Include="..\..\src\mod_lua.inl" />
+    <None Include="..\..\src\timer.inl" />
   </ItemGroup>
   </ItemGroup>
 </Project>
 </Project>

+ 4 - 4
VisualStudio/ex_embed_cpp/ex_embed_cpp.vcxproj

@@ -89,7 +89,7 @@
       </PrecompiledHeader>
       </PrecompiledHeader>
       <WarningLevel>Level3</WarningLevel>
       <WarningLevel>Level3</WarningLevel>
       <Optimization>Disabled</Optimization>
       <Optimization>Disabled</Optimization>
-      <PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;_CRT_SECURE_NO_DEPRECATE;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <PreprocessorDefinitions>WIN32;NO_SSL;_DEBUG;_CONSOLE;_CRT_SECURE_NO_DEPRECATE;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
       <AdditionalIncludeDirectories>$(ProjectDir)..\..\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
       <AdditionalIncludeDirectories>$(ProjectDir)..\..\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
     </ClCompile>
     </ClCompile>
     <Link>
     <Link>
@@ -103,7 +103,7 @@
       </PrecompiledHeader>
       </PrecompiledHeader>
       <WarningLevel>Level3</WarningLevel>
       <WarningLevel>Level3</WarningLevel>
       <Optimization>Disabled</Optimization>
       <Optimization>Disabled</Optimization>
-      <PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;_CRT_SECURE_NO_DEPRECATE;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <PreprocessorDefinitions>WIN32;NO_SSL;_DEBUG;_CONSOLE;_CRT_SECURE_NO_DEPRECATE;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
       <AdditionalIncludeDirectories>$(ProjectDir)..\..\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
       <AdditionalIncludeDirectories>$(ProjectDir)..\..\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
     </ClCompile>
     </ClCompile>
     <Link>
     <Link>
@@ -119,7 +119,7 @@
       <Optimization>MaxSpeed</Optimization>
       <Optimization>MaxSpeed</Optimization>
       <FunctionLevelLinking>true</FunctionLevelLinking>
       <FunctionLevelLinking>true</FunctionLevelLinking>
       <IntrinsicFunctions>true</IntrinsicFunctions>
       <IntrinsicFunctions>true</IntrinsicFunctions>
-      <PreprocessorDefinitions>WIN32;_CONSOLE;_CRT_SECURE_NO_DEPRECATE;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <PreprocessorDefinitions>WIN32;NO_SSL;_CONSOLE;_CRT_SECURE_NO_DEPRECATE;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
       <AdditionalIncludeDirectories>$(ProjectDir)..\..\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
       <AdditionalIncludeDirectories>$(ProjectDir)..\..\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
     </ClCompile>
     </ClCompile>
     <Link>
     <Link>
@@ -137,7 +137,7 @@
       <Optimization>MaxSpeed</Optimization>
       <Optimization>MaxSpeed</Optimization>
       <FunctionLevelLinking>true</FunctionLevelLinking>
       <FunctionLevelLinking>true</FunctionLevelLinking>
       <IntrinsicFunctions>true</IntrinsicFunctions>
       <IntrinsicFunctions>true</IntrinsicFunctions>
-      <PreprocessorDefinitions>WIN32;_CONSOLE;_CRT_SECURE_NO_DEPRECATE;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <PreprocessorDefinitions>WIN32;NO_SSL;_CONSOLE;_CRT_SECURE_NO_DEPRECATE;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
       <AdditionalIncludeDirectories>$(ProjectDir)..\..\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
       <AdditionalIncludeDirectories>$(ProjectDir)..\..\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
     </ClCompile>
     </ClCompile>
     <Link>
     <Link>

+ 4 - 4
VisualStudio/ex_embedded_c/ex_embedded_c.vcxproj

@@ -96,7 +96,7 @@
       </PrecompiledHeader>
       </PrecompiledHeader>
       <WarningLevel>Level3</WarningLevel>
       <WarningLevel>Level3</WarningLevel>
       <Optimization>Disabled</Optimization>
       <Optimization>Disabled</Optimization>
-      <PreprocessorDefinitions>NO_SSL;USE_IPV6;USE_WEBSOCKET;WIN32;_DEBUG;_CONSOLE;_CRT_SECURE_NO_DEPRECATE;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <PreprocessorDefinitions>OPENSSL_API_1_0;USE_IPV6;USE_WEBSOCKET;WIN32;_DEBUG;_CONSOLE;_CRT_SECURE_NO_DEPRECATE;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
       <AdditionalIncludeDirectories>$(ProjectDir)..\..\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
       <AdditionalIncludeDirectories>$(ProjectDir)..\..\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
     </ClCompile>
     </ClCompile>
     <Link>
     <Link>
@@ -111,7 +111,7 @@
       </PrecompiledHeader>
       </PrecompiledHeader>
       <WarningLevel>Level3</WarningLevel>
       <WarningLevel>Level3</WarningLevel>
       <Optimization>Disabled</Optimization>
       <Optimization>Disabled</Optimization>
-      <PreprocessorDefinitions>TEST_WITHOUT_SSL;USE_IPV6;USE_WEBSOCKET;WIN32;_DEBUG;_CONSOLE;_CRT_SECURE_NO_DEPRECATE;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <PreprocessorDefinitions>OPENSSL_API_1_0;USE_IPV6;USE_WEBSOCKET;WIN32;_DEBUG;_CONSOLE;_CRT_SECURE_NO_DEPRECATE;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
       <AdditionalIncludeDirectories>$(ProjectDir)..\..\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
       <AdditionalIncludeDirectories>$(ProjectDir)..\..\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
     </ClCompile>
     </ClCompile>
     <Link>
     <Link>
@@ -127,7 +127,7 @@
       <Optimization>MaxSpeed</Optimization>
       <Optimization>MaxSpeed</Optimization>
       <FunctionLevelLinking>true</FunctionLevelLinking>
       <FunctionLevelLinking>true</FunctionLevelLinking>
       <IntrinsicFunctions>true</IntrinsicFunctions>
       <IntrinsicFunctions>true</IntrinsicFunctions>
-      <PreprocessorDefinitions>USE_IPV6;USE_WEBSOCKET;WIN32;_CONSOLE;_CRT_SECURE_NO_DEPRECATE;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <PreprocessorDefinitions>OPENSSL_API_1_0;USE_IPV6;USE_WEBSOCKET;WIN32;_CONSOLE;_CRT_SECURE_NO_DEPRECATE;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
       <AdditionalIncludeDirectories>$(ProjectDir)..\..\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
       <AdditionalIncludeDirectories>$(ProjectDir)..\..\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
     </ClCompile>
     </ClCompile>
     <Link>
     <Link>
@@ -145,7 +145,7 @@
       <Optimization>MaxSpeed</Optimization>
       <Optimization>MaxSpeed</Optimization>
       <FunctionLevelLinking>true</FunctionLevelLinking>
       <FunctionLevelLinking>true</FunctionLevelLinking>
       <IntrinsicFunctions>true</IntrinsicFunctions>
       <IntrinsicFunctions>true</IntrinsicFunctions>
-      <PreprocessorDefinitions>TEST_WITHOUT_SSL;USE_IPV6;USE_WEBSOCKET;WIN32;_CONSOLE;_CRT_SECURE_NO_DEPRECATE;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <PreprocessorDefinitions>OPENSSL_API_1_0;USE_IPV6;USE_WEBSOCKET;WIN32;_CONSOLE;_CRT_SECURE_NO_DEPRECATE;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
       <AdditionalIncludeDirectories>$(ProjectDir)..\..\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
       <AdditionalIncludeDirectories>$(ProjectDir)..\..\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
     </ClCompile>
     </ClCompile>
     <Link>
     <Link>

+ 1 - 1
VisualStudio/unit_test/unit_test.vcxproj

@@ -73,7 +73,7 @@
       </PrecompiledHeader>
       </PrecompiledHeader>
       <WarningLevel>Level3</WarningLevel>
       <WarningLevel>Level3</WarningLevel>
       <Optimization>Disabled</Optimization>
       <Optimization>Disabled</Optimization>
-      <PreprocessorDefinitions>MAIN_TIMER_PRIVATE=main;LOCAL_TEST;REPLACE_CHECK_FOR_LOCAL_DEBUGGING;LOCAL_TEST;USE_IPV6;USE_WEBSOCKET;MEMORY_DEBUGGING;WIN32;_DEBUG;_CONSOLE;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <PreprocessorDefinitions>MAIN_PUBLIC_SERVER=main;NO_SSL;LOCAL_TEST;REPLACE_CHECK_FOR_LOCAL_DEBUGGING;LOCAL_TEST;USE_IPV6;USE_WEBSOCKET;MEMORY_DEBUGGING;WIN32;_DEBUG;_CONSOLE;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
       <AdditionalIncludeDirectories>$(ProjectDir)..\..\src;$(ProjectDir)..\..\include;$(ProjectDir)..\..\src\third_party\lua-5.2.4\src;$(ProjectDir)..\..\..\check-0.10.0\;$(ProjectDir)..\..\..\check-0.10.0\src\;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
       <AdditionalIncludeDirectories>$(ProjectDir)..\..\src;$(ProjectDir)..\..\include;$(ProjectDir)..\..\src\third_party\lua-5.2.4\src;$(ProjectDir)..\..\..\check-0.10.0\;$(ProjectDir)..\..\..\check-0.10.0\src\;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
     </ClCompile>
     </ClCompile>
     <Link>
     <Link>

+ 25 - 1
docs/Embedding.md

@@ -29,6 +29,7 @@ but all functions required to run a HTTP server.
     - src/md5.inl (MD5 calculation)
     - src/md5.inl (MD5 calculation)
     - src/sha1.inl (SHA calculation)
     - src/sha1.inl (SHA calculation)
     - src/handle\_form.inl (HTML form handling functions)
     - src/handle\_form.inl (HTML form handling functions)
+    - src/response.inl (helper for generating HTTP response headers)
     - src/timer.inl (optional timer support)
     - src/timer.inl (optional timer support)
   - Optional: C++ wrapper
   - Optional: C++ wrapper
     - include/CivetServer.h (C++ interface)
     - include/CivetServer.h (C++ interface)
@@ -39,6 +40,7 @@ but all functions required to run a HTTP server.
 
 
 
 
 Note: The C++ wrapper uses the official C interface (civetweb.h) without adding any features to the server itself. Several features available in the C interface are missing in the C++ interface. While all features should be accessible using the C interface, this is not a design goal of the C++ interface.
 Note: The C++ wrapper uses the official C interface (civetweb.h) without adding any features to the server itself. Several features available in the C interface are missing in the C++ interface. While all features should be accessible using the C interface, this is not a design goal of the C++ interface.
+New code is advised to use the C interface, since this is unit tested and new API functions are often only added there.
 
 
 
 
 #### Additional Source Files for Executables
 #### Additional Source Files for Executables
@@ -80,7 +82,7 @@ By default, the server will automatically serve up files like a normal HTTP serv
 Alternative quick start: Have a look at the examples embedded\_c and embedded\_cpp
 Alternative quick start: Have a look at the examples embedded\_c and embedded\_cpp
 
 
 
 
-Feature selection
+Feature Selection
 ------
 ------
 
 
 CivetWeb is highly customizable at build time, in addition to configuration at start time.
 CivetWeb is highly customizable at build time, in addition to configuration at start time.
@@ -101,6 +103,28 @@ However, this might be annoying when updating the server, pulling new features o
 This customization option is currently in an evaluation phase. In case you need additional function defines, please create an issue on GitHub explaining your use case, to discuss if this would be an appropriate solution - in general, other customization options are preferred.
 This customization option is currently in an evaluation phase. In case you need additional function defines, please create an issue on GitHub explaining your use case, to discuss if this would be an appropriate solution - in general, other customization options are preferred.
 
 
 
 
+Stack Sizes
+------
+
+Stack sizes are usually nothing you need to worry about when running on a system with sufficient memory, such as a desktop PC running Windows or Linux.
+CivetWeb will use the default stack size on those system (typically 1 MB per Windows, up to 8 MB for Linux) - for each thread.
+CivetWeb uses one thread for each HTTP connection, so in case a request handler uses a blocking, only this connection will be blocked, while others are not affected.
+The number of threads can be configured (see [UserManual.md](https://github.com/civetweb/civetweb/blob/master/docs/UserManual.md#num_threads-50)). 
+This number also defines the limit for the number of threads that can be handled simultaneously - additional requests can be queues, but are not processed.
+Since HTTP clients as web browsers tend to open multiple connections to the same server when loading one page, using less then five to ten threads may cause delays, even when there is only one user using one browser.
+The total amount of virtual memory required for all stacks is `num_threads` multiplied by the stack size per thread (e.g., 50 MB for 50 threads on Windows).
+This virtual memory (more precisely: reserved virtual address space) will only require "real" physical memory, when the stack is really used up to this level.
+If one stack in the entire process exceeds its virtual address space limit, the entire process crashes.
+Thus, for a system with sufficient resources, using large stacks with big reserves is advisable.
+
+However, for small embedded devices 50 MB may already be a lot, in particular if they immediately commit physical memory for all virtual memory allocations.
+To limit the stack size, the define `USE_STACK_SIZE` can be set.
+Note that stack (as all memory) comes in pages of 4096 bytes (for X86 CPUs and may others), so `USE_STACK_SIZE` must always be an integer multiple of this page size.
+In case no additional features are used, 4 pages (16 kB) of stack for each thread could be sufficient. 
+Using lower stack sizes may require to reduce some buffer sizes (e.g. `MG_BUF_LEN`).
+(Note: There is no warranty for these numbers, neither for this, nor for any past or future versions.)
+
+
 Lua Support
 Lua Support
 ------
 ------
 
 

+ 17 - 0
examples/embedded_c/embedded_c.c

@@ -745,7 +745,24 @@ WebSocketStartHandler(struct mg_connection *conn, void *cbdata)
 #define MAX_WS_CLIENTS (5)
 #define MAX_WS_CLIENTS (5)
 
 
 struct t_ws_client {
 struct t_ws_client {
+	/* Handle to the connection, used for mg_read/mg_write */
 	struct mg_connection *conn;
 	struct mg_connection *conn;
+
+	/*
+	    WebSocketConnectHandler sets state to 1 ("connected")
+	    the connect handler can accept or reject a connection, but it cannot
+	    send or receive any data at this state
+
+	    WebSocketReadyHandler sets state to 2 ("ready")
+	    reading and writing is possible now
+
+	    WebSocketCloseHandler sets state to 0
+	    the websocket is about to be closed, reading and writing is no longer
+	    possible this callback can be used to cleanup allocated resources
+
+	    InformWebsockets is called cyclic every second, and sends some data
+	    (a counter value) to all websockets in state 2
+	*/
 	int state;
 	int state;
 } static ws_clients[MAX_WS_CLIENTS];
 } static ws_clients[MAX_WS_CLIENTS];
 
 

+ 2 - 0
format.bat

@@ -11,6 +11,8 @@ clang-format -i src/mod_duktape.inl
 clang-format -i src/mod_zlib.inl
 clang-format -i src/mod_zlib.inl
 clang-format -i src/timer.inl
 clang-format -i src/timer.inl
 clang-format -i src/handle_form.inl
 clang-format -i src/handle_form.inl
+clang-format -i src/response.inl
+clang-format -i src/mod_http2.inl
 
 
 clang-format -i src/third_party/civetweb_lua.h
 clang-format -i src/third_party/civetweb_lua.h
 
 

+ 70 - 49
include/civetweb.h

@@ -293,21 +293,8 @@ struct mg_callbacks {
 	                               void **ssl_ctx,
 	                               void **ssl_ctx,
 	                               void *user_data);
 	                               void *user_data);
 
 
-#if defined(MG_LEGACY_INTERFACE)           /* 2015-08-19 */                    \
-    || defined(MG_EXPERIMENTAL_INTERFACES) /* 2019-11-03 */
-	/* Called when websocket request is received, before websocket handshake.
-	   Return value:
-	     0: civetweb proceeds with websocket handshake.
-	     1: connection is closed immediately.
-	   This callback is deprecated: Use mg_set_websocket_handler instead. */
-	int (*websocket_connect)(const struct mg_connection *);
-
-	/* Called when websocket handshake is successfully completed, and
-	   connection is ready for data exchange.
-	   This callback is deprecated: Use mg_set_websocket_handler instead. */
-	void (*websocket_ready)(struct mg_connection *);
-
-	/* Called when data frame has been received from the client.
+#if defined(MG_EXPERIMENTAL_INTERFACES) /* 2019-11-03 */
+	/* Called when data frame has been received from the peer.
 	   Parameters:
 	   Parameters:
 	     bits: first byte of the websocket frame, see websocket RFC at
 	     bits: first byte of the websocket frame, see websocket RFC at
 	           http://tools.ietf.org/html/rfc6455, section 5.2
 	           http://tools.ietf.org/html/rfc6455, section 5.2
@@ -355,14 +342,6 @@ struct mg_callbacks {
 	                 void *lua_context,
 	                 void *lua_context,
 	                 unsigned context_flags);
 	                 unsigned context_flags);
 
 
-#if defined(MG_LEGACY_INTERFACE) /* 2016-05-14 */
-	/* Called when civetweb has uploaded a file to a temporary directory as a
-	   result of mg_upload() call.
-	   Note that mg_upload is deprecated. Use mg_handle_form_request instead.
-	   Parameters:
-	     file_name: full path name to the uploaded file. */
-	void (*upload)(struct mg_connection *, const char *file_name);
-#endif
 
 
 	/* 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.
@@ -694,16 +673,6 @@ CIVETWEB_API int
 mg_get_request_link(const struct mg_connection *conn, char *buf, size_t buflen);
 mg_get_request_link(const struct mg_connection *conn, char *buf, size_t buflen);
 
 
 
 
-#if defined(MG_LEGACY_INTERFACE) /* 2014-02-21 */
-/* Return array of strings that represent valid configuration options.
-   For each option, option name and default value is returned, i.e. the
-   number of entries in the array equals to number_of_options x 2.
-   Array is NULL terminated. */
-/* Deprecated: Use mg_get_valid_options instead. */
-CIVETWEB_API const char **mg_get_valid_option_names(void);
-#endif
-
-
 struct mg_option {
 struct mg_option {
 	const char *name;
 	const char *name;
 	int type;
 	int type;
@@ -876,12 +845,6 @@ CIVETWEB_API void mg_lock_connection(struct mg_connection *conn);
 CIVETWEB_API void mg_unlock_connection(struct mg_connection *conn);
 CIVETWEB_API void mg_unlock_connection(struct mg_connection *conn);
 
 
 
 
-#if defined(MG_LEGACY_INTERFACE) /* 2014-06-21 */
-#define mg_lock mg_lock_connection
-#define mg_unlock mg_unlock_connection
-#endif
-
-
 /* Lock server context.  This lock may be used to protect resources
 /* Lock server context.  This lock may be used to protect resources
    that are shared between different connection/worker threads.
    that are shared between different connection/worker threads.
    If the given context is not server, these functions do nothing. */
    If the given context is not server, these functions do nothing. */
@@ -1243,16 +1206,6 @@ mg_download(const char *host,
 CIVETWEB_API void mg_close_connection(struct mg_connection *conn);
 CIVETWEB_API void mg_close_connection(struct mg_connection *conn);
 
 
 
 
-#if defined(MG_LEGACY_INTERFACE) /* 2016-05-14 */
-/* File upload functionality. Each uploaded file gets saved into a temporary
-   file and MG_UPLOAD event is sent.
-   Return number of uploaded files.
-   Deprecated: Use mg_handle_form_request instead. */
-CIVETWEB_API int mg_upload(struct mg_connection *conn,
-                           const char *destination_dir);
-#endif
-
-
 /* This structure contains callback functions for handling form fields.
 /* This structure contains callback functions for handling form fields.
    It is used as an argument to mg_handle_form_request. */
    It is used as an argument to mg_handle_form_request. */
 struct mg_form_data_handler {
 struct mg_form_data_handler {
@@ -1520,6 +1473,7 @@ enum { TIMEOUT_INFINITE = -1 };
 #endif
 #endif
 enum { MG_TIMEOUT_INFINITE = -1 };
 enum { MG_TIMEOUT_INFINITE = -1 };
 
 
+
 /* Wait for a response from the server
 /* Wait for a response from the server
    Parameters:
    Parameters:
      conn: connection
      conn: connection
@@ -1536,6 +1490,73 @@ CIVETWEB_API int mg_get_response(struct mg_connection *conn,
                                  size_t ebuf_len,
                                  size_t ebuf_len,
                                  int timeout);
                                  int timeout);
 
 
+/* mg_response_header_* functions can be used from server callbacks,
+ */
+
+/* Initialize a new HTTP response
+ * Parameters:
+ *   conn: Current connection handle.
+ *   status: HTTP status code (e.g., 200 for "OK").
+ * Return:
+ *   0:    ok
+ *  -1:    parameter error
+ *  -2:    invalid connection type
+ *  -3:    invalid connection status
+ */
+CIVETWEB_API int mg_response_header_start(struct mg_connection *conn,
+                                          int status);
+
+
+/* Add a new HTTP response header line
+ * Parameters:
+ *   conn: Current connection handle.
+ *   header: Header name.
+ *   value: Header value.
+ *   value_len: Length of header value, excluding the terminating zero.
+ *              Use -1 for "strlen(value)".
+ * Return:
+ *   0:    ok
+ *  -1:    parameter error
+ *  -2:    invalid connection type
+ *  -3:    invalid connection status
+ *  -4:    too many headers
+ *  -5:    out of memory
+ */
+CIVETWEB_API int mg_response_header_add(struct mg_connection *conn,
+                                        const char *header,
+                                        const char *value,
+                                        int value_len);
+
+
+/* Add a complete header string (key + value).
+ * This function is less efficient as compared to mg_response_header_add,
+ * and should only be used to convert complete HTTP/1.x header lines.
+ * Parameters:
+ *   conn: Current connection handle.
+ *   http1_headers: Header line(s) in the form "name: value\r\n".
+ * Return:
+ *  >=0:   no error, number of header lines added
+ *  -1:    parameter error
+ *  -2:    invalid connection type
+ *  -3:    invalid connection status
+ *  -4:    too many headers
+ *  -5:    out of memory
+ */
+CIVETWEB_API int mg_response_header_add_lines(struct mg_connection *conn,
+                                              const char *http1_headers);
+
+
+/* Send http response
+ * Parameters:
+ *   conn: Current connection handle.
+ * Return:
+ *   0:    ok
+ *  -1:    parameter error
+ *  -2:    invalid connection type
+ *  -3:    invalid connection status
+ */
+CIVETWEB_API int mg_response_header_send(struct mg_connection *conn);
+
 
 
 /* Check which features where set when the civetweb library has been compiled.
 /* Check which features where set when the civetweb library has been compiled.
    The function explicitly addresses compile time defines used when building
    The function explicitly addresses compile time defines used when building

File diff ditekan karena terlalu besar
+ 331 - 251
src/civetweb.c


+ 1849 - 3
src/mod_http2.inl

@@ -1,5 +1,1851 @@
-/* Stub for HTTP/2 */
-static void process_new_http2_connection(struct mg_connection *conn)
+/* Prototype implementation for HTTP2. Do not use in production.
+ * There may be memory leaks, security vulnerabilities, ...
+ */
+
+
+/***********************************************************************/
+/***  HPACK                                                          ***/
+/***********************************************************************/
+
+/* hpack predefined table. See:
+ * https://tools.ietf.org/html/rfc7541#appendix-A
+ */
+
+static struct mg_header hpack_predefined[62] = {{NULL, NULL},
+                                                {":authority", NULL},
+                                                {":method", "GET"},
+                                                {":method", "POST"},
+                                                {":path", "/"},
+                                                {":path", "/index.html"},
+                                                {":scheme", "http"},
+                                                {":scheme", "https"},
+                                                {":status", "200"},
+                                                {":status", "204"},
+                                                {":status", "206"},
+                                                {":status", "304"},
+                                                {":status", "400"},
+                                                {":status", "404"},
+                                                {":status", "500"},
+                                                {"accept-charset", NULL},
+                                                {"accept-encoding", NULL},
+                                                {"accept-language", NULL},
+                                                {"accept-ranges", NULL},
+                                                {"accept", NULL},
+                                                {"access-control-allow-origin",
+                                                 NULL},
+                                                {"age", NULL},
+                                                {"allow", NULL},
+                                                {"authorization", NULL},
+                                                {"cache-control", NULL},
+                                                {"content-encoding", NULL},
+                                                {"content-disposition", NULL},
+                                                {"content-language", NULL},
+                                                {"content-length", NULL},
+                                                {"content-location", NULL},
+                                                {"content-range", NULL},
+                                                {"content-type", NULL},
+                                                {"cookie", NULL},
+                                                {"date", NULL},
+                                                {"etag", NULL},
+                                                {"expect", NULL},
+                                                {"expires", NULL},
+                                                {"from", NULL},
+                                                {"host", NULL},
+                                                {"if-match", NULL},
+                                                {"if-modified-since", NULL},
+                                                {"if-none-match", NULL},
+                                                {"if-range", NULL},
+                                                {"if-unmodified-since", NULL},
+                                                {"last-modified", NULL},
+                                                {"link", NULL},
+                                                {"location", NULL},
+                                                {"max-forwards", NULL},
+                                                {"proxy-authenticate", NULL},
+                                                {"proxy-authorization", NULL},
+                                                {"range", NULL},
+                                                {"referer", NULL},
+                                                {"refresh", NULL},
+                                                {"retry-after", NULL},
+                                                {"server", NULL},
+                                                {"set-cookie", NULL},
+                                                {"strict-transport-security",
+                                                 NULL},
+                                                {"transfer-encoding", NULL},
+                                                {"user-agent", NULL},
+                                                {"vary", NULL},
+                                                {"via", NULL},
+                                                {"www-authenticate", NULL}};
+
+/* Huffman decoding: https://tools.ietf.org/html/rfc7541#appendix-B
+
+'0' ( 48)  |00000                                         0  [ 5]
+'1' ( 49)  |00001                                         1  [ 5]
+'2' ( 50)  |00010                                         2  [ 5]
+'a' ( 97)  |00011                                         3  [ 5]
+'c' ( 99)  |00100                                         4  [ 5]
+'e' (101)  |00101                                         5  [ 5]
+'i' (105)  |00110                                         6  [ 5]
+'o' (111)  |00111                                         7  [ 5]
+'s' (115)  |01000                                         8  [ 5]
+'t' (116)  |01001                                         9  [ 5]
+' ' ( 32)  |010100                                       14  [ 6]
+'%' ( 37)  |010101                                       15  [ 6]
+'-' ( 45)  |010110                                       16  [ 6]
+'.' ( 46)  |010111                                       17  [ 6]
+'/' ( 47)  |011000                                       18  [ 6]
+'3' ( 51)  |011001                                       19  [ 6]
+'4' ( 52)  |011010                                       1a  [ 6]
+'5' ( 53)  |011011                                       1b  [ 6]
+'6' ( 54)  |011100                                       1c  [ 6]
+'7' ( 55)  |011101                                       1d  [ 6]
+'8' ( 56)  |011110                                       1e  [ 6]
+'9' ( 57)  |011111                                       1f  [ 6]
+'=' ( 61)  |100000                                       20  [ 6]
+'A' ( 65)  |100001                                       21  [ 6]
+'_' ( 95)  |100010                                       22  [ 6]
+'b' ( 98)  |100011                                       23  [ 6]
+'d' (100)  |100100                                       24  [ 6]
+'f' (102)  |100101                                       25  [ 6]
+'g' (103)  |100110                                       26  [ 6]
+'h' (104)  |100111                                       27  [ 6]
+'l' (108)  |101000                                       28  [ 6]
+'m' (109)  |101001                                       29  [ 6]
+'n' (110)  |101010                                       2a  [ 6]
+'p' (112)  |101011                                       2b  [ 6]
+'r' (114)  |101100                                       2c  [ 6]
+'u' (117)  |101101                                       2d  [ 6]
+':' ( 58)  |1011100                                      5c  [ 7]
+'B' ( 66)  |1011101                                      5d  [ 7]
+'C' ( 67)  |1011110                                      5e  [ 7]
+'D' ( 68)  |1011111                                      5f  [ 7]
+'E' ( 69)  |1100000                                      60  [ 7]
+'F' ( 70)  |1100001                                      61  [ 7]
+'G' ( 71)  |1100010                                      62  [ 7]
+'H' ( 72)  |1100011                                      63  [ 7]
+'I' ( 73)  |1100100                                      64  [ 7]
+'J' ( 74)  |1100101                                      65  [ 7]
+'K' ( 75)  |1100110                                      66  [ 7]
+'L' ( 76)  |1100111                                      67  [ 7]
+'M' ( 77)  |1101000                                      68  [ 7]
+'N' ( 78)  |1101001                                      69  [ 7]
+'O' ( 79)  |1101010                                      6a  [ 7]
+'P' ( 80)  |1101011                                      6b  [ 7]
+'Q' ( 81)  |1101100                                      6c  [ 7]
+'R' ( 82)  |1101101                                      6d  [ 7]
+'S' ( 83)  |1101110                                      6e  [ 7]
+'T' ( 84)  |1101111                                      6f  [ 7]
+'U' ( 85)  |1110000                                      70  [ 7]
+'V' ( 86)  |1110001                                      71  [ 7]
+'W' ( 87)  |1110010                                      72  [ 7]
+'Y' ( 89)  |1110011                                      73  [ 7]
+'j' (106)  |1110100                                      74  [ 7]
+'k' (107)  |1110101                                      75  [ 7]
+'q' (113)  |1110110                                      76  [ 7]
+'v' (118)  |1110111                                      77  [ 7]
+'w' (119)  |1111000                                      78  [ 7]
+'x' (120)  |1111001                                      79  [ 7]
+'y' (121)  |1111010                                      7a  [ 7]
+'z' (122)  |1111011                                      7b  [ 7]
+'&' ( 38)  |11111000                                     f8  [ 8]
+'*' ( 42)  |11111001                                     f9  [ 8]
+',' ( 44)  |11111010                                     fa  [ 8]
+';' ( 59)  |11111011                                     fb  [ 8]
+'X' ( 88)  |11111100                                     fc  [ 8]
+'Z' ( 90)  |11111101                                     fd  [ 8]
+'!' ( 33)  |11111110|00                                 3f8  [10]
+'"' ( 34)  |11111110|01                                 3f9  [10]
+'(' ( 40)  |11111110|10                                 3fa  [10]
+')' ( 41)  |11111110|11                                 3fb  [10]
+'?' ( 63)  |11111111|00                                 3fc  [10]
+''' ( 39)  |11111111|010                                7fa  [11]
+'+' ( 43)  |11111111|011                                7fb  [11]
+'|' (124)  |11111111|100                                7fc  [11]
+'#' ( 35)  |11111111|1010                               ffa  [12]
+'>' ( 62)  |11111111|1011                               ffb  [12]
+    (  0)  |11111111|11000                             1ff8  [13]
+'$' ( 36)  |11111111|11001                             1ff9  [13]
+'@' ( 64)  |11111111|11010                             1ffa  [13]
+'[' ( 91)  |11111111|11011                             1ffb  [13]
+']' ( 93)  |11111111|11100                             1ffc  [13]
+'~' (126)  |11111111|11101                             1ffd  [13]
+'^' ( 94)  |11111111|111100                            3ffc  [14]
+'}' (125)  |11111111|111101                            3ffd  [14]
+'<' ( 60)  |11111111|1111100                           7ffc  [15]
+'`' ( 96)  |11111111|1111101                           7ffd  [15]
+'{' (123)  |11111111|1111110                           7ffe  [15]
+'\' ( 92)  |11111111|11111110|000                     7fff0  [19]
+    (195)  |11111111|11111110|001                     7fff1  [19]
+    (208)  |11111111|11111110|010                     7fff2  [19]
+    (128)  |11111111|11111110|0110                    fffe6  [20]
+    (130)  |11111111|11111110|0111                    fffe7  [20]
+    (131)  |11111111|11111110|1000                    fffe8  [20]
+    (162)  |11111111|11111110|1001                    fffe9  [20]
+    (184)  |11111111|11111110|1010                    fffea  [20]
+    (194)  |11111111|11111110|1011                    fffeb  [20]
+    (224)  |11111111|11111110|1100                    fffec  [20]
+    (226)  |11111111|11111110|1101                    fffed  [20]
+    (153)  |11111111|11111110|11100                  1fffdc  [21]
+    (161)  |11111111|11111110|11101                  1fffdd  [21]
+    (167)  |11111111|11111110|11110                  1fffde  [21]
+    (172)  |11111111|11111110|11111                  1fffdf  [21]
+    (176)  |11111111|11111111|00000                  1fffe0  [21]
+    (177)  |11111111|11111111|00001                  1fffe1  [21]
+    (179)  |11111111|11111111|00010                  1fffe2  [21]
+    (209)  |11111111|11111111|00011                  1fffe3  [21]
+    (216)  |11111111|11111111|00100                  1fffe4  [21]
+    (217)  |11111111|11111111|00101                  1fffe5  [21]
+    (227)  |11111111|11111111|00110                  1fffe6  [21]
+    (229)  |11111111|11111111|00111                  1fffe7  [21]
+    (230)  |11111111|11111111|01000                  1fffe8  [21]
+    (129)  |11111111|11111111|010010                 3fffd2  [22]
+    (132)  |11111111|11111111|010011                 3fffd3  [22]
+    (133)  |11111111|11111111|010100                 3fffd4  [22]
+    (134)  |11111111|11111111|010101                 3fffd5  [22]
+    (136)  |11111111|11111111|010110                 3fffd6  [22]
+    (146)  |11111111|11111111|010111                 3fffd7  [22]
+    (154)  |11111111|11111111|011000                 3fffd8  [22]
+    (156)  |11111111|11111111|011001                 3fffd9  [22]
+    (160)  |11111111|11111111|011010                 3fffda  [22]
+    (163)  |11111111|11111111|011011                 3fffdb  [22]
+    (164)  |11111111|11111111|011100                 3fffdc  [22]
+    (169)  |11111111|11111111|011101                 3fffdd  [22]
+    (170)  |11111111|11111111|011110                 3fffde  [22]
+    (173)  |11111111|11111111|011111                 3fffdf  [22]
+    (178)  |11111111|11111111|100000                 3fffe0  [22]
+    (181)  |11111111|11111111|100001                 3fffe1  [22]
+    (185)  |11111111|11111111|100010                 3fffe2  [22]
+    (186)  |11111111|11111111|100011                 3fffe3  [22]
+    (187)  |11111111|11111111|100100                 3fffe4  [22]
+    (189)  |11111111|11111111|100101                 3fffe5  [22]
+    (190)  |11111111|11111111|100110                 3fffe6  [22]
+    (196)  |11111111|11111111|100111                 3fffe7  [22]
+    (198)  |11111111|11111111|101000                 3fffe8  [22]
+    (228)  |11111111|11111111|101001                 3fffe9  [22]
+    (232)  |11111111|11111111|101010                 3fffea  [22]
+    (233)  |11111111|11111111|101011                 3fffeb  [22]
+    (  1)  |11111111|11111111|1011000                7fffd8  [23]
+    (135)  |11111111|11111111|1011001                7fffd9  [23]
+    (137)  |11111111|11111111|1011010                7fffda  [23]
+    (138)  |11111111|11111111|1011011                7fffdb  [23]
+    (139)  |11111111|11111111|1011100                7fffdc  [23]
+    (140)  |11111111|11111111|1011101                7fffdd  [23]
+    (141)  |11111111|11111111|1011110                7fffde  [23]
+    (143)  |11111111|11111111|1011111                7fffdf  [23]
+    (147)  |11111111|11111111|1100000                7fffe0  [23]
+    (149)  |11111111|11111111|1100001                7fffe1  [23]
+    (150)  |11111111|11111111|1100010                7fffe2  [23]
+    (151)  |11111111|11111111|1100011                7fffe3  [23]
+    (152)  |11111111|11111111|1100100                7fffe4  [23]
+    (155)  |11111111|11111111|1100101                7fffe5  [23]
+    (157)  |11111111|11111111|1100110                7fffe6  [23]
+    (158)  |11111111|11111111|1100111                7fffe7  [23]
+    (165)  |11111111|11111111|1101000                7fffe8  [23]
+    (166)  |11111111|11111111|1101001                7fffe9  [23]
+    (168)  |11111111|11111111|1101010                7fffea  [23]
+    (174)  |11111111|11111111|1101011                7fffeb  [23]
+    (175)  |11111111|11111111|1101100                7fffec  [23]
+    (180)  |11111111|11111111|1101101                7fffed  [23]
+    (182)  |11111111|11111111|1101110                7fffee  [23]
+    (183)  |11111111|11111111|1101111                7fffef  [23]
+    (188)  |11111111|11111111|1110000                7ffff0  [23]
+    (191)  |11111111|11111111|1110001                7ffff1  [23]
+    (197)  |11111111|11111111|1110010                7ffff2  [23]
+    (231)  |11111111|11111111|1110011                7ffff3  [23]
+    (239)  |11111111|11111111|1110100                7ffff4  [23]
+    (  9)  |11111111|11111111|11101010               ffffea  [24]
+    (142)  |11111111|11111111|11101011               ffffeb  [24]
+    (144)  |11111111|11111111|11101100               ffffec  [24]
+    (145)  |11111111|11111111|11101101               ffffed  [24]
+    (148)  |11111111|11111111|11101110               ffffee  [24]
+    (159)  |11111111|11111111|11101111               ffffef  [24]
+    (171)  |11111111|11111111|11110000               fffff0  [24]
+    (206)  |11111111|11111111|11110001               fffff1  [24]
+    (215)  |11111111|11111111|11110010               fffff2  [24]
+    (225)  |11111111|11111111|11110011               fffff3  [24]
+    (236)  |11111111|11111111|11110100               fffff4  [24]
+    (237)  |11111111|11111111|11110101               fffff5  [24]
+    (199)  |11111111|11111111|11110110|0            1ffffec  [25]
+    (207)  |11111111|11111111|11110110|1            1ffffed  [25]
+    (234)  |11111111|11111111|11110111|0            1ffffee  [25]
+    (235)  |11111111|11111111|11110111|1            1ffffef  [25]
+    (192)  |11111111|11111111|11111000|00           3ffffe0  [26]
+    (193)  |11111111|11111111|11111000|01           3ffffe1  [26]
+    (200)  |11111111|11111111|11111000|10           3ffffe2  [26]
+    (201)  |11111111|11111111|11111000|11           3ffffe3  [26]
+    (202)  |11111111|11111111|11111001|00           3ffffe4  [26]
+    (205)  |11111111|11111111|11111001|01           3ffffe5  [26]
+    (210)  |11111111|11111111|11111001|10           3ffffe6  [26]
+    (213)  |11111111|11111111|11111001|11           3ffffe7  [26]
+    (218)  |11111111|11111111|11111010|00           3ffffe8  [26]
+    (219)  |11111111|11111111|11111010|01           3ffffe9  [26]
+    (238)  |11111111|11111111|11111010|10           3ffffea  [26]
+    (240)  |11111111|11111111|11111010|11           3ffffeb  [26]
+    (242)  |11111111|11111111|11111011|00           3ffffec  [26]
+    (243)  |11111111|11111111|11111011|01           3ffffed  [26]
+    (255)  |11111111|11111111|11111011|10           3ffffee  [26]
+    (203)  |11111111|11111111|11111011|110          7ffffde  [27]
+    (204)  |11111111|11111111|11111011|111          7ffffdf  [27]
+    (211)  |11111111|11111111|11111100|000          7ffffe0  [27]
+    (212)  |11111111|11111111|11111100|001          7ffffe1  [27]
+    (214)  |11111111|11111111|11111100|010          7ffffe2  [27]
+    (221)  |11111111|11111111|11111100|011          7ffffe3  [27]
+    (222)  |11111111|11111111|11111100|100          7ffffe4  [27]
+    (223)  |11111111|11111111|11111100|101          7ffffe5  [27]
+    (241)  |11111111|11111111|11111100|110          7ffffe6  [27]
+    (244)  |11111111|11111111|11111100|111          7ffffe7  [27]
+    (245)  |11111111|11111111|11111101|000          7ffffe8  [27]
+    (246)  |11111111|11111111|11111101|001          7ffffe9  [27]
+    (247)  |11111111|11111111|11111101|010          7ffffea  [27]
+    (248)  |11111111|11111111|11111101|011          7ffffeb  [27]
+    (250)  |11111111|11111111|11111101|100          7ffffec  [27]
+    (251)  |11111111|11111111|11111101|101          7ffffed  [27]
+    (252)  |11111111|11111111|11111101|110          7ffffee  [27]
+    (253)  |11111111|11111111|11111101|111          7ffffef  [27]
+    (254)  |11111111|11111111|11111110|000          7fffff0  [27]
+    (  2)  |11111111|11111111|11111110|0010         fffffe2  [28]
+    (  3)  |11111111|11111111|11111110|0011         fffffe3  [28]
+    (  4)  |11111111|11111111|11111110|0100         fffffe4  [28]
+    (  5)  |11111111|11111111|11111110|0101         fffffe5  [28]
+    (  6)  |11111111|11111111|11111110|0110         fffffe6  [28]
+    (  7)  |11111111|11111111|11111110|0111         fffffe7  [28]
+    (  8)  |11111111|11111111|11111110|1000         fffffe8  [28]
+    ( 11)  |11111111|11111111|11111110|1001         fffffe9  [28]
+    ( 12)  |11111111|11111111|11111110|1010         fffffea  [28]
+    ( 14)  |11111111|11111111|11111110|1011         fffffeb  [28]
+    ( 15)  |11111111|11111111|11111110|1100         fffffec  [28]
+    ( 16)  |11111111|11111111|11111110|1101         fffffed  [28]
+    ( 17)  |11111111|11111111|11111110|1110         fffffee  [28]
+    ( 18)  |11111111|11111111|11111110|1111         fffffef  [28]
+    ( 19)  |11111111|11111111|11111111|0000         ffffff0  [28]
+    ( 20)  |11111111|11111111|11111111|0001         ffffff1  [28]
+    ( 21)  |11111111|11111111|11111111|0010         ffffff2  [28]
+    ( 23)  |11111111|11111111|11111111|0011         ffffff3  [28]
+    ( 24)  |11111111|11111111|11111111|0100         ffffff4  [28]
+    ( 25)  |11111111|11111111|11111111|0101         ffffff5  [28]
+    ( 26)  |11111111|11111111|11111111|0110         ffffff6  [28]
+    ( 27)  |11111111|11111111|11111111|0111         ffffff7  [28]
+    ( 28)  |11111111|11111111|11111111|1000         ffffff8  [28]
+    ( 29)  |11111111|11111111|11111111|1001         ffffff9  [28]
+    ( 30)  |11111111|11111111|11111111|1010         ffffffa  [28]
+    ( 31)  |11111111|11111111|11111111|1011         ffffffb  [28]
+    (127)  |11111111|11111111|11111111|1100         ffffffc  [28]
+    (220)  |11111111|11111111|11111111|1101         ffffffd  [28]
+    (249)  |11111111|11111111|11111111|1110         ffffffe  [28]
+    ( 10)  |11111111|11111111|11111111|111100      3ffffffc  [30]
+    ( 13)  |11111111|11111111|11111111|111101      3ffffffd  [30]
+    ( 22)  |11111111|11111111|11111111|111110      3ffffffe  [30]
+    (256)  |11111111|11111111|11111111|111111      3fffffff  [30]
+*/
+
+struct {
+	uint8_t decoded;
+	uint8_t bitcount;
+	uint32_t encoded;
+} hpack_huff_dec[] = {
+    {48, 5, 0x0},
+    {49, 5, 0x1},
+    {50, 5, 0x2},
+    {97, 5, 0x3},
+    {99, 5, 0x4},
+    {101, 5, 0x5},
+    {105, 5, 0x6},
+    {111, 5, 0x7},
+    {115, 5, 0x8},
+    {116, 5, 0x9},
+    {32, 6, 0x14},
+    {37, 6, 0x15},
+    {45, 6, 0x16},
+    {46, 6, 0x17},
+    {47, 6, 0x18},
+    {51, 6, 0x19},
+    {52, 6, 0x1a},
+    {53, 6, 0x1b},
+    {54, 6, 0x1c},
+    {55, 6, 0x1d},
+    {56, 6, 0x1e},
+    {57, 6, 0x1f},
+    {61, 6, 0x20},
+    {65, 6, 0x21},
+    {95, 6, 0x22},
+    {98, 6, 0x23},
+    {100, 6, 0x24},
+    {102, 6, 0x25},
+    {103, 6, 0x26},
+    {104, 6, 0x27},
+    {108, 6, 0x28},
+    {109, 6, 0x29},
+    {110, 6, 0x2a},
+    {112, 6, 0x2b},
+    {114, 6, 0x2c},
+    {117, 6, 0x2d},
+    {58, 7, 0x5c},
+    {66, 7, 0x5d},
+    {67, 7, 0x5e},
+    {68, 7, 0x5f},
+    {69, 7, 0x60},
+    {70, 7, 0x61},
+    {71, 7, 0x62},
+    {72, 7, 0x63},
+    {73, 7, 0x64},
+    {74, 7, 0x65},
+    {75, 7, 0x66},
+    {76, 7, 0x67},
+    {77, 7, 0x68},
+    {78, 7, 0x69},
+    {79, 7, 0x6a},
+    {80, 7, 0x6b},
+    {81, 7, 0x6c},
+    {82, 7, 0x6d},
+    {83, 7, 0x6e},
+    {84, 7, 0x6f},
+    {85, 7, 0x70},
+    {86, 7, 0x71},
+    {87, 7, 0x72},
+    {89, 7, 0x73},
+    {106, 7, 0x74},
+    {107, 7, 0x75},
+    {113, 7, 0x76},
+    {118, 7, 0x77},
+    {119, 7, 0x78},
+    {120, 7, 0x79},
+    {121, 7, 0x7a},
+    {122, 7, 0x7b},
+    {38, 8, 0xf8},
+    {42, 8, 0xf9},
+    {44, 8, 0xfa},
+    {59, 8, 0xfb},
+    {88, 8, 0xfc},
+    {90, 8, 0xfd},
+    {33, 10, 0x3f8},
+    {34, 10, 0x3f9},
+    {40, 10, 0x3fa},
+    {41, 10, 0x3fb},
+    {63, 10, 0x3fc},
+    {39, 11, 0x7fa},
+    {43, 11, 0x7fb},
+    {124, 11, 0x7fc},
+    {35, 12, 0xffa},
+    {62, 12, 0xffb},
+    {0, 13, 0x1ff8},
+    {36, 13, 0x1ff9},
+    {64, 13, 0x1ffa},
+    {91, 13, 0x1ffb},
+    {93, 13, 0x1ffc},
+    {126, 13, 0x1ffd},
+    {94, 14, 0x3ffc},
+    {125, 14, 0x3ffd},
+    {60, 15, 0x7ffc},
+    {96, 15, 0x7ffd},
+    {123, 15, 0x7ffe},
+    {92, 19, 0x7fff0},
+    {195, 19, 0x7fff1},
+    {208, 19, 0x7fff2},
+    {128, 20, 0xfffe6},
+    {130, 20, 0xfffe7},
+    {131, 20, 0xfffe8},
+    {162, 20, 0xfffe9},
+    {184, 20, 0xfffea},
+    {194, 20, 0xfffeb},
+    {224, 20, 0xfffec},
+    {226, 20, 0xfffed},
+    {153, 21, 0x1fffdc},
+    {161, 21, 0x1fffdd},
+    {167, 21, 0x1fffde},
+    {172, 21, 0x1fffdf},
+    {176, 21, 0x1fffe0},
+    {177, 21, 0x1fffe1},
+    {179, 21, 0x1fffe2},
+    {209, 21, 0x1fffe3},
+    {216, 21, 0x1fffe4},
+    {217, 21, 0x1fffe5},
+    {227, 21, 0x1fffe6},
+    {229, 21, 0x1fffe7},
+    {230, 21, 0x1fffe8},
+    {129, 22, 0x3fffd2},
+    {132, 22, 0x3fffd3},
+    {133, 22, 0x3fffd4},
+    {134, 22, 0x3fffd5},
+    {136, 22, 0x3fffd6},
+    {146, 22, 0x3fffd7},
+    {154, 22, 0x3fffd8},
+    {156, 22, 0x3fffd9},
+    {160, 22, 0x3fffda},
+    {163, 22, 0x3fffdb},
+    {164, 22, 0x3fffdc},
+    {169, 22, 0x3fffdd},
+    {170, 22, 0x3fffde},
+    {173, 22, 0x3fffdf},
+    {178, 22, 0x3fffe0},
+    {181, 22, 0x3fffe1},
+    {185, 22, 0x3fffe2},
+    {186, 22, 0x3fffe3},
+    {187, 22, 0x3fffe4},
+    {189, 22, 0x3fffe5},
+    {190, 22, 0x3fffe6},
+    {196, 22, 0x3fffe7},
+    {198, 22, 0x3fffe8},
+    {228, 22, 0x3fffe9},
+    {232, 22, 0x3fffea},
+    {233, 22, 0x3fffeb},
+    {1, 23, 0x7fffd8},
+    {135, 23, 0x7fffd9},
+    {137, 23, 0x7fffda},
+    {138, 23, 0x7fffdb},
+    {139, 23, 0x7fffdc},
+    {140, 23, 0x7fffdd},
+    {141, 23, 0x7fffde},
+    {143, 23, 0x7fffdf},
+    {147, 23, 0x7fffe0},
+    {149, 23, 0x7fffe1},
+    {150, 23, 0x7fffe2},
+    {151, 23, 0x7fffe3},
+    {152, 23, 0x7fffe4},
+    {155, 23, 0x7fffe5},
+    {157, 23, 0x7fffe6},
+    {158, 23, 0x7fffe7},
+    {165, 23, 0x7fffe8},
+    {166, 23, 0x7fffe9},
+    {168, 23, 0x7fffea},
+    {174, 23, 0x7fffeb},
+    {175, 23, 0x7fffec},
+    {180, 23, 0x7fffed},
+    {182, 23, 0x7fffee},
+    {183, 23, 0x7fffef},
+    {188, 23, 0x7ffff0},
+    {191, 23, 0x7ffff1},
+    {197, 23, 0x7ffff2},
+    {231, 23, 0x7ffff3},
+    {239, 23, 0x7ffff4},
+    {9, 24, 0xffffea},
+    {142, 24, 0xffffeb},
+    {144, 24, 0xffffec},
+    {145, 24, 0xffffed},
+    {148, 24, 0xffffee},
+    {159, 24, 0xffffef},
+    {171, 24, 0xfffff0},
+    {206, 24, 0xfffff1},
+    {215, 24, 0xfffff2},
+    {225, 24, 0xfffff3},
+    {236, 24, 0xfffff4},
+    {237, 24, 0xfffff5},
+    {199, 25, 0x1ffffec},
+    {207, 25, 0x1ffffed},
+    {234, 25, 0x1ffffee},
+    {235, 25, 0x1ffffef},
+    {192, 26, 0x3ffffe0},
+    {193, 26, 0x3ffffe1},
+    {200, 26, 0x3ffffe2},
+    {201, 26, 0x3ffffe3},
+    {202, 26, 0x3ffffe4},
+    {205, 26, 0x3ffffe5},
+    {210, 26, 0x3ffffe6},
+    {213, 26, 0x3ffffe7},
+    {218, 26, 0x3ffffe8},
+    {219, 26, 0x3ffffe9},
+    {238, 26, 0x3ffffea},
+    {240, 26, 0x3ffffeb},
+    {242, 26, 0x3ffffec},
+    {243, 26, 0x3ffffed},
+    {255, 26, 0x3ffffee},
+    {203, 27, 0x7ffffde},
+    {204, 27, 0x7ffffdf},
+    {211, 27, 0x7ffffe0},
+    {212, 27, 0x7ffffe1},
+    {214, 27, 0x7ffffe2},
+    {221, 27, 0x7ffffe3},
+    {222, 27, 0x7ffffe4},
+    {223, 27, 0x7ffffe5},
+    {241, 27, 0x7ffffe6},
+    {244, 27, 0x7ffffe7},
+    {245, 27, 0x7ffffe8},
+    {246, 27, 0x7ffffe9},
+    {247, 27, 0x7ffffea},
+    {248, 27, 0x7ffffeb},
+    {250, 27, 0x7ffffec},
+    {251, 27, 0x7ffffed},
+    {252, 27, 0x7ffffee},
+    {253, 27, 0x7ffffef},
+    {254, 27, 0x7fffff0},
+    {2, 28, 0xfffffe2},
+    {3, 28, 0xfffffe3},
+    {4, 28, 0xfffffe4},
+    {5, 28, 0xfffffe5},
+    {6, 28, 0xfffffe6},
+    {7, 28, 0xfffffe7},
+    {8, 28, 0xfffffe8},
+    {11, 28, 0xfffffe9},
+    {12, 28, 0xfffffea},
+    {14, 28, 0xfffffeb},
+    {15, 28, 0xfffffec},
+    {16, 28, 0xfffffed},
+    {17, 28, 0xfffffee},
+    {18, 28, 0xfffffef},
+    {19, 28, 0xffffff0},
+    {20, 28, 0xffffff1},
+    {21, 28, 0xffffff2},
+    {23, 28, 0xffffff3},
+    {24, 28, 0xffffff4},
+    {25, 28, 0xffffff5},
+    {26, 28, 0xffffff6},
+    {27, 28, 0xffffff7},
+    {28, 28, 0xffffff8},
+    {29, 28, 0xffffff9},
+    {30, 28, 0xffffffa},
+    {31, 28, 0xffffffb},
+    {127, 28, 0xffffffc},
+    {220, 28, 0xffffffd},
+    {249, 28, 0xffffffe},
+    {10, 30, 0x3ffffffc},
+    {13, 30, 0x3ffffffd},
+    {22, 30, 0x3ffffffe},
+    {(uint8_t)256, 30, 0x3fffffff} /* filling/termination */
+};
+
+/* highest value with 5, 6, 7, ... 28, 29, 30 and all (32) bits */
+uint32_t hpack_huff_end_code[] = {0x9,       0x2d,       0x7b,       0xfd,
+                                  0,         0x3fc,      0x7fc,      0xffb,
+                                  0x1ffd,    0x3ffd,     0x7ffe,     0,
+                                  0,         0,          0x7fff2,    0xfffed,
+                                  0x1fffe8,  0x3fffeb,   0x7ffff4,   0xfffff5,
+                                  0x1ffffef, 0x3ffffee,  0x7fffff0,  0xffffffe,
+                                  0,         0x3ffffffe, 0xFFFFFFFFu};
+
+/* lowest index with 5, 6, 7, ... 28, 29, 30 and all (32) bits */
+uint8_t hpack_huff_start_index[] = {0,   10,  36,  68,  0,   74,  79, 82,  84,
+                                    90,  92,  0,   0,   0,   95,  98, 106, 119,
+                                    145, 174, 186, 190, 205, 224, 0,  253, 0};
+
+
+/* Function to decode an integer from a HPACK encoded block */
+/* Integers have a variable size encoding, according to the RFC.
+ * The integer starts at index *i, idx_mask masks the available bits in
+ * the first byte. The index *i is advanced until the end of the
+ * encoded integer.
+ */
+static uint64_t
+hpack_getnum(const uint8_t *buf,
+             int *i,
+             uint8_t idx_mask,
+             struct mg_context *ctx)
 {
 {
-	mg_send_http_error(conn, 505, "%s", "HTTP/2 not supported");
+	uint64_t num = (buf[*i] & idx_mask);
+
+	(void)ctx;
+
+	if (num == idx_mask) {
+		/* Algorithm from https://tools.ietf.org/html/rfc7541#section-5.1 */
+		uint32_t M = 0;
+		do {
+			(*i)++;
+			num = num + ((buf[*i] & 0x7F) << M);
+			M += 7;
+		} while ((buf[*i] & 0x80) == 0x80);
+	}
+
+	(*i)++;
+	return num;
+}
+
+
+/* Function to decode a string from a HPACK encoded block */
+/* Strings have a variable size and can be either encoded directly (8 bits
+ * per char), or using huffman encoding (variable bits per char).
+ * The string starts at index *i. This index is advanced until the end of
+ * the encoded string.
+ */
+static char *
+hpack_decode(const uint8_t *buf, int *i, struct mg_context *ctx)
+{
+	uint64_t byte_len64;
+	int byte_len;
+	int bit_len;
+	uint8_t is_huff = ((buf[*i] & 0x80) == 0x80);
+
+	/* Get length of string in bytes */
+	byte_len64 = hpack_getnum(buf, i, 0x7f, ctx);
+	if (byte_len64 > 1024) {
+		/* TODO */
+		return NULL;
+	}
+	byte_len = (int)byte_len64;
+	bit_len = byte_len * 8;
+
+	/* Now read the string */
+	if (!is_huff) {
+		/* Not huffman encoded: Copy directly */
+		char *result = mg_malloc_ctx(byte_len + 1, ctx);
+		if (result) {
+			memcpy(result, buf + (*i), byte_len);
+			result[byte_len] = 0;
+		}
+		(*i) += byte_len;
+		return result;
+
+	} else {
+		/* Huffman encoded: need to decode bitwise */
+		const uint8_t *pData =
+		    buf + (*i);           /* begin pointer of bit input string */
+		int bitRead = 0;          /* number of encoded bits read */
+		uint32_t bytesStored = 0; /* number of decoded bytes stored */
+		uint8_t str[2048];        /* storage buffer for decoded string */
+
+		for (;;) {
+			uint32_t accu = 0; /* accu register: collect bits */
+			uint8_t bc = 0;    /* number of bits collected */
+			int n;
+
+			/* Collect bits in this loop, until we have a valid huff code in
+			 * accu */
+			do {
+				accu <<= 1;
+				accu |= (pData[bitRead / 8] >> (7 - (bitRead & 7))) & 1;
+				bitRead++;
+				bc++;
+				if (bitRead > bit_len) {
+					/* We used all bits. Return the decoded string. */
+					str[bytesStored] = 0; /* Terminate string */
+					(*i) += byte_len;     /* Advance parsing index */
+					return mg_strdup_ctx((char *)str,
+					                     ctx); /* Return a string copy */
+				}
+			} while ((bc < 5) || (accu > hpack_huff_end_code[bc - 5]));
+
+			/* Find matching code in huffman encoding table */
+			for (n = hpack_huff_start_index[bc - 5]; n < 256; n++) {
+				if (accu == hpack_huff_dec[n].encoded) {
+					str[bytesStored] = hpack_huff_dec[n].decoded;
+					bytesStored++;
+					break;
+				}
+			}
+		}
+	}
+}
+
+
+static void
+append_bits(uint8_t *target,
+            uint32_t offset,
+            uint32_t value,
+            uint8_t value_bits)
+{
+	uint32_t offset_bytes = offset / 8;
+	uint32_t offset_bits = offset % 8;
+	uint32_t remaining_bits, ac;
+
+	value &= ~(0xFFFFFFFF << value_bits);
+
+	remaining_bits = 8 - offset_bits;
+
+	if (value_bits <= remaining_bits) {
+		ac = value << (remaining_bits - value_bits);
+		target[offset_bytes] |= ac;
+		return;
+	}
+
+	ac = value >> (value_bits - remaining_bits);
+	target[offset_bytes] |= ac;
+	append_bits(target,
+	            offset + remaining_bits,
+	            value,
+	            value_bits - remaining_bits);
+}
+
+
+static int
+hpack_encode(uint8_t *store, const char *load, int lower)
+{
+	uint32_t nohuff_len = strlen(load);
+
+	uint32_t len_bits = 0;
+	uint32_t len_bytes;
+	uint32_t spare_bits;
+	uint32_t i;
+
+	memset(store, 0, nohuff_len + 1);
+
+	for (i = 0; i < nohuff_len; i++) {
+		uint8_t b = (uint8_t)((char)(lower ? tolower(load[i]) : load[i]));
+		int idx;
+
+		for (idx = 0; idx <= 255; idx++) {
+			if (hpack_huff_dec[idx].decoded == b) {
+				append_bits((uint8_t *)store + 1,
+				            len_bits,
+				            hpack_huff_dec[idx].encoded,
+				            hpack_huff_dec[idx].bitcount);
+				len_bits += hpack_huff_dec[idx].bitcount;
+				break;
+			}
+		}
+	}
+
+	len_bytes = (len_bits + 7) / 8;
+	spare_bits = len_bytes * 8 - len_bits;
+	if (spare_bits) {
+		append_bits((uint8_t *)store + 1, len_bits, 0xFFFFFFFF, spare_bits);
+	}
+
+	if (len_bytes >= 127) {
+		// TODO: Shift string and encode len in more bytes
+		return 0;
+	}
+	*store = 0x80 + (uint8_t)len_bytes;
+
+	if ((len_bytes >= nohuff_len) && (0)) {
+		*store = (uint8_t)nohuff_len;
+		if (lower) {
+			for (i = 1; i <= nohuff_len; i++) {
+				store[i] = tolower(load[i]);
+			}
+		} else {
+			memcpy(store + 1, load, nohuff_len);
+		}
+		return nohuff_len + 1;
+	} else {
+		/*
+		int i = 0;
+		char *test = hpack_decode(store, &i, NULL);
+		i = i; // breakpoint for debugging / testing
+		*/
+	}
+
+	return len_bytes + 1;
+}
+
+
+/***********************************************************************/
+/***  HTTP 2                                                         ***/
+/***********************************************************************/
+
+
+static const char http2_pri[] = "PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n";
+static unsigned char http2_pri_len = 24; /* = strlen(http2_pri) */
+
+
+/* Read and check the HTTP/2 primer/preface:
+ * See https://tools.ietf.org/html/rfc7540#section-3.5 */
+static int
+is_valid_http2_primer(struct mg_connection *conn)
+{
+	size_t pri_len = http2_pri_len;
+	char buf[32];
+
+	if (pri_len > sizeof(buf)) {
+		/* Should never be reached - the RFC primer has 24 bytes */
+		return 0;
+	}
+	int read_pri_len = mg_read(conn, buf, pri_len);
+	if ((read_pri_len != (int)pri_len)
+	    || (0 != memcmp(buf, http2_pri, pri_len))) {
+		return 0;
+	}
+	return 1;
+}
+
+
+#define mg_xwrite(conn, data, len)                                             \
+	push_all((conn)->phys_ctx,                                                 \
+	         NULL,                                                             \
+	         (conn)->client.sock,                                              \
+	         (conn)->ssl,                                                      \
+	         (const char *)(data),                                             \
+	         (int)(len));
+
+
+static void
+http2_settings_acknowledge(struct mg_connection *conn)
+{
+	unsigned char http2_set_ackn_frame[9] = {0, 0, 0, 4, 1, 0, 0, 0, 0};
+
+	DEBUG_TRACE("%s", "Sending settings frame");
+	mg_xwrite(conn, http2_set_ackn_frame, 9);
+}
+
+
+struct http2_settings {
+	uint32_t settings_header_table_size;
+	uint32_t settings_enable_push;
+	uint32_t settings_max_concurrent_streams;
+	uint32_t settings_initial_window_size;
+	uint32_t settings_max_frame_size;
+	uint32_t settings_max_header_list_size;
+};
+
+
+const struct http2_settings http2_default_settings =
+    {4096, 1, UINT32_MAX, 65535, 16384, UINT32_MAX};
+
+const struct http2_settings http2_civetweb_server_settings =
+    {4096, 0, 100, 65535, 16384, 65535};
+
+
+enum {
+	HTTP2_ERR_NO_ERROR = 0,
+	HTTP2_ERR_PROTOCOL_ERROR,
+	HTTP2_ERR_INTERNAL_ERROR,
+	HTTP2_ERR_FLOW_CONTROL_ERROR,
+	HTTP2_ERR_SETTINGS_TIMEOUT,
+	HTTP2_ERR_STREAM_CLOSED,
+	HTTP2_ERR_FRAME_SIZE_ERROR,
+	HTTP2_ERR_REFUSED_STREAM,
+	HTTP2_ERR_CANCEL,
+	HTTP2_ERR_COMPRESSION_ERROR,
+	HTTP2_ERR_CONNECT_ERROR,
+	HTTP2_ERR_ENHANCE_YOUR_CALM,
+	HTTP2_ERR_INADEQUATE_SECURITY,
+	HTTP2_ERR_HTTP_1_1_REQUIRED
+};
+
+
+static void
+http2_send_settings(struct mg_connection *conn,
+                    const struct http2_settings *set)
+{
+	uint16_t id;
+	uint32_t data;
+	uint8_t http2_settings_frame[9] = {0, 0, 36, 4, 0, 0, 0, 0, 0};
+	mg_xwrite(conn, http2_settings_frame, 9);
+
+	id = htons(1);
+	data = htonl(set->settings_header_table_size);
+	mg_xwrite(conn, &id, 2);
+	mg_xwrite(conn, &data, 4);
+
+	id = htons(1);
+	data = htonl(set->settings_enable_push);
+	mg_xwrite(conn, &id, 2);
+	mg_xwrite(conn, &data, 4);
+
+	id = htons(1);
+	data = htonl(set->settings_max_concurrent_streams);
+	mg_xwrite(conn, &id, 2);
+	mg_xwrite(conn, &data, 4);
+
+	id = htons(1);
+	data = htonl(set->settings_initial_window_size);
+	mg_xwrite(conn, &id, 2);
+	mg_xwrite(conn, &data, 4);
+
+	id = htons(1);
+	data = htonl(set->settings_max_frame_size);
+	mg_xwrite(conn, &id, 2);
+	mg_xwrite(conn, &data, 4);
+
+	id = htons(1);
+	data = htonl(set->settings_max_header_list_size);
+	mg_xwrite(conn, &id, 2);
+	mg_xwrite(conn, &data, 4);
+
+	DEBUG_TRACE("%s", "HTTP2 settings sent");
+}
+
+
+static int
+http2_send_response_headers(struct mg_connection *conn)
+{
+	unsigned char http2_header_frame[9] = {0, 0, 0, 1, 4, 0, 0, 0, 0};
+	uint8_t header_bin[1024];
+	uint16_t header_len = 0;
+	int has_date = 0;
+	int has_connection = 0;
+	int i;
+
+	if ((conn->status_code < 100) || (conn->status_code > 999)) {
+		/* Invalid status: Set status to "Internal Server Error" */
+		conn->status_code = 500;
+	}
+
+	switch (conn->status_code) {
+	case 200:
+		header_bin[header_len++] = 0x88;
+		break;
+	case 204:
+		header_bin[header_len++] = 0x89;
+		break;
+	case 206:
+		header_bin[header_len++] = 0x8A;
+		break;
+	case 304:
+		header_bin[header_len++] = 0x8B;
+		break;
+	case 400:
+		header_bin[header_len++] = 0x8C;
+		break;
+	case 404:
+		header_bin[header_len++] = 0x8D;
+		break;
+	case 500:
+		header_bin[header_len++] = 0x8E;
+		break;
+	default:
+		header_bin[header_len++] = 0x48;
+		header_bin[header_len++] = 0x03;
+		header_bin[header_len++] = 0x30 + (conn->status_code / 100);
+		header_bin[header_len++] = 0x30 + ((conn->status_code / 10) % 10);
+		header_bin[header_len++] = 0x30 + (conn->status_code % 10);
+		break;
+	}
+
+	/* Add all headers */
+	for (i = 0; i < conn->response_info.num_headers; i++) {
+		uint16_t predef = 0;
+		uint16_t j;
+
+		/* Filter headers not valid in HTTP/2 */
+		if (!mg_strcasecmp("Connection",
+		                   conn->response_info.http_headers[i].name)) {
+			has_connection = 1;
+			continue; /* do not send */
+		}
+
+		/* Check if this header is known in HPACK (static table index 15 to 61)
+		 * see https://tools.ietf.org/html/rfc7541#appendix-A */
+		for (j = 15; j <= 61; j++) {
+			if (!mg_strcasecmp(hpack_predefined[j].name,
+			                   conn->response_info.http_headers[i].name)) {
+				predef = j;
+				break;
+			}
+		}
+
+		if (predef) {
+			/* Predefined header found */
+			header_bin[header_len++] = 0x40 + predef;
+		} else {
+			/* Rare header, do not index */
+			header_bin[header_len++] = 0x10;
+			j = hpack_encode(header_bin + header_len,
+			                 conn->response_info.http_headers[i].name,
+			                 1);
+			header_len += j;
+		}
+
+		j = hpack_encode(header_bin + header_len,
+		                 conn->response_info.http_headers[i].value,
+		                 0);
+		header_len += j;
+
+		/* Mark required headers as sent */
+		if (!mg_strcasecmp("Date", conn->response_info.http_headers[i].name)) {
+			has_date = 1;
+		}
+	}
+
+	/* Add required headers, if they have not been sent yet */
+	if (!has_date) {
+		/* Create header frame */
+		char date[64];
+		uint8_t date_len;
+		time_t curtime = time(NULL);
+
+		gmt_time_string(date, sizeof(date), &curtime);
+		date_len = (uint8_t)strlen(date);
+
+		header_bin[header_len++] =
+		    0x61; /* "Date" predefined HPACK index 33 (0x21) + 0x40 */
+		header_bin[header_len++] = date_len;
+		memcpy(header_bin + header_len, date, date_len);
+		header_len += date_len;
+	}
+
+	http2_header_frame[1] = (header_len & 0xFF00) >> 8;
+	http2_header_frame[2] = (header_len & 0xFF);
+
+	http2_header_frame[5] = (conn->http2.stream_id & 0xFF000000u) >> 24;
+	http2_header_frame[6] = (conn->http2.stream_id & 0xFF0000u) >> 16;
+	http2_header_frame[7] = (conn->http2.stream_id & 0xFF00u) >> 8;
+	http2_header_frame[8] = (conn->http2.stream_id & 0xFFu);
+
+	/* Send header frame */
+	mg_xwrite(conn, http2_header_frame, 9);
+	mg_xwrite(conn, header_bin, header_len);
+
+	DEBUG_TRACE("HTTP2 response header sent: stream %u", conn->http2.stream_id);
+
+	return 42; /* TODO */
+}
+
+
+static void
+http2_data_frame_head(struct mg_connection *conn,
+                      uint32_t frame_size,
+                      int is_final)
+{
+	unsigned char http2_data_frame[9];
+	uint32_t stream_id = conn->http2.stream_id;
+
+	http2_data_frame[0] = (frame_size & 0xFF0000) >> 16;
+	http2_data_frame[1] = (frame_size & 0xFF00) >> 8;
+	http2_data_frame[2] = (frame_size & 0xFF);
+
+	http2_data_frame[3] = 0; /* frame type "DATA" */
+	http2_data_frame[4] = (is_final ? 1 : 0);
+
+	http2_data_frame[5] = (stream_id & 0xFF000000u) >> 24;
+	http2_data_frame[6] = (stream_id & 0xFF0000u) >> 16;
+	http2_data_frame[7] = (stream_id & 0xFF00u) >> 8;
+	http2_data_frame[8] = (stream_id & 0xFFu);
+
+	DEBUG_TRACE("HTTP2 begin data frame: stream %u, frame_size %u (final: %i)",
+	            stream_id,
+	            frame_size,
+	            is_final);
+
+	mg_xwrite(conn, http2_data_frame, 9);
+}
+
+
+static void
+http2_send_window(struct mg_connection *conn,
+                  uint32_t stream_id,
+                  uint32_t window_size)
+{
+	unsigned char http2_window_frame[9] = {0, 0, 4, 8, 0, 0, 0, 0, 0};
+	uint32_t data = htonl(window_size);
+
+	DEBUG_TRACE("HTTP2 send window_size: stream %u, error %u",
+	            stream_id,
+	            window_size);
+
+	http2_window_frame[5] = (stream_id & 0xFF000000u) >> 24;
+	http2_window_frame[6] = (stream_id & 0xFF0000u) >> 16;
+	http2_window_frame[7] = (stream_id & 0xFF00u) >> 8;
+	http2_window_frame[8] = (stream_id & 0xFFu);
+	mg_xwrite(conn, http2_window_frame, 9);
+	mg_xwrite(conn, &data, 4);
+}
+
+
+static void
+http2_reset_stream(struct mg_connection *conn,
+                   uint32_t stream_id,
+                   uint32_t error_id)
+{
+	unsigned char http2_reset_frame[9] = {0, 0, 4, 3, 0, 0, 0, 0, 0};
+	uint32_t val = htonl(error_id);
+
+	DEBUG_TRACE("HTTP2 send reset: stream %u, error %u", stream_id, error_id);
+
+	http2_reset_frame[5] = (stream_id & 0xFF000000u) >> 24;
+	http2_reset_frame[6] = (stream_id & 0xFF0000u) >> 16;
+	http2_reset_frame[7] = (stream_id & 0xFF00u) >> 8;
+	http2_reset_frame[8] = (stream_id & 0xFFu);
+	mg_xwrite(conn, http2_reset_frame, 9);
+	mg_xwrite(conn, &val, 4);
+}
+
+
+static void
+http2_must_use_http1(struct mg_connection *conn)
+{
+	DEBUG_TRACE("HTTP2 not available for this URL (%s)", conn->path_info);
+	http2_reset_stream(conn, conn->http2.stream_id, 0xd);
+}
+
+
+/* The HTTP2 implementation collects request headers as array of dynamically
+ * allocated string values. This array must be freed once the request is
+ * handled.
+ * This is different to the HTTP/1.x implementation: For HTTP/1.x, the header
+ * list is implemented as pointers into an existing buffer, so free must not
+ * be called for HTTP/1.x.
+ * Thus free_buffered_request_header_list is in mod_http2.inl.
+ */
+#if defined(DEBUG)
+static int mem_h_count = 0;
+static int mem_d_count = 0;
+#define CHECK_LEAK_HDR_ALLOC(ptr)                                              \
+	DEBUG_TRACE("H NEW %08x (%i): %s",                                         \
+	            (uint32_t)ptr,                                                 \
+	            ++mem_h_count,                                                 \
+	            (const char *)ptr)
+#define CHECK_LEAK_HDR_FREE(ptr)                                               \
+	DEBUG_TRACE("H DEL %08x (%i): %s",                                         \
+	            (uint32_t)ptr,                                                 \
+	            --mem_h_count,                                                 \
+	            (const char *)ptr)
+#define CHECK_LEAK_DYN_ALLOC(ptr)                                              \
+	DEBUG_TRACE("D NEW %08x (%i): %s",                                         \
+	            (uint32_t)ptr,                                                 \
+	            ++mem_d_count,                                                 \
+	            (const char *)ptr)
+#define CHECK_LEAK_DYN_FREE(ptr)                                               \
+	DEBUG_TRACE("D DEL %08x (%i): %s",                                         \
+	            (uint32_t)ptr,                                                 \
+	            --mem_d_count,                                                 \
+	            (const char *)ptr)
+#else
+#define CHECK_LEAK_HDR_ALLOC(ptr)
+#define CHECK_LEAK_HDR_FREE(ptr)
+#define CHECK_LEAK_DYN_ALLOC(ptr)
+#define CHECK_LEAK_DYN_FREE(ptr)
+#endif
+
+
+/* The dynamic header table may be resized on a HTTP2 client request.
+ * A tablesize=0 will free all memory.
+ */
+static void
+purge_dynamic_header_table(struct mg_connection *conn, uint32_t tableSize)
+{
+	DEBUG_TRACE("HTTP2 dynamic header table set to %u", tableSize);
+	while (conn->http2.dyn_table_size > tableSize) {
+		conn->http2.dyn_table_size--;
+
+		CHECK_LEAK_DYN_FREE(
+		    conn->http2.dyn_table[conn->http2.dyn_table_size].name);
+		CHECK_LEAK_DYN_FREE(
+		    conn->http2.dyn_table[conn->http2.dyn_table_size].value);
+
+		mg_free((void *)conn->http2.dyn_table[conn->http2.dyn_table_size].name);
+		conn->http2.dyn_table[conn->http2.dyn_table_size].name = 0;
+		mg_free(
+		    (void *)conn->http2.dyn_table[conn->http2.dyn_table_size].value);
+		conn->http2.dyn_table[conn->http2.dyn_table_size].value = 0;
+	}
+}
+
+
+/* Internal function to free request header list.
+ * Not to be confused with the response header list.
+ */
+static void
+free_buffered_request_header_list(struct mg_connection *conn)
+{
+	while (conn->request_info.num_headers > 0) {
+		conn->request_info.num_headers--;
+
+		CHECK_LEAK_HDR_FREE(
+		    conn->request_info.http_headers[conn->request_info.num_headers]
+		        .name);
+		CHECK_LEAK_HDR_FREE(
+		    conn->request_info.http_headers[conn->request_info.num_headers]
+		        .value);
+
+		mg_free((void *)conn->request_info
+		            .http_headers[conn->request_info.num_headers]
+		            .name);
+		conn->request_info.http_headers[conn->request_info.num_headers].name =
+		    0;
+		mg_free((void *)conn->request_info
+		            .http_headers[conn->request_info.num_headers]
+		            .value);
+		conn->request_info.http_headers[conn->request_info.num_headers].value =
+		    0;
+	}
+}
+
+
+/* HTTP2 requires a different handling loop */
+static void
+handle_http2(struct mg_connection *conn)
+{
+	unsigned char http2_frame_head[9];
+	uint32_t http2_frame_size;
+	uint8_t http2_frame_type;
+	uint8_t http2_frame_flags;
+	uint32_t http2_frame_stream_id;
+	uint32_t http_window_length = 0;
+	int bytes_read;
+	uint8_t *buf;
+	int my_settings_accepted = 0;
+	int my_settings_sent;
+	const char *my_hpack_headers[128];
+
+	struct http2_settings client_settings = http2_default_settings;
+	struct http2_settings server_settings = http2_default_settings;
+
+	/* Send own settings */
+	http2_send_settings(conn, &http2_civetweb_server_settings);
+	my_settings_sent = 1;
+	// http2_send_window(conn, 0, /* 0x3fff0001 */ 1024*1024);
+
+	/* initialize hpack header table with predefined header fields */
+	memset((void *)my_hpack_headers, 0, sizeof(my_hpack_headers));
+	memcpy((void *)my_hpack_headers,
+	       hpack_predefined,
+	       sizeof(hpack_predefined));
+
+	buf = (uint8_t *)mg_malloc_ctx(server_settings.settings_max_frame_size,
+	                               conn->phys_ctx);
+	if (!buf) {
+		/* Out of memory */
+		DEBUG_TRACE("%s", "Out of memory for HTTP2 frame");
+		return;
+	}
+
+	for (;;) {
+		/* HTTP/2 is handled frame by frame */
+		int frame_is_end_stream = 0;
+		int frame_is_end_headers = 0;
+		int frame_is_padded = 0;
+		int frame_is_priority = 0;
+
+		bytes_read = mg_read(conn, http2_frame_head, sizeof(http2_frame_head));
+		if (bytes_read != sizeof(http2_frame_head)) {
+			/* TODO: errormsg */
+			goto clean_http2;
+		}
+
+		/* Extract data from frame header */
+		http2_frame_size = ((uint32_t)http2_frame_head[0] * 0x10000u)
+		                   + ((uint32_t)http2_frame_head[1] * 0x100u)
+		                   + ((uint32_t)http2_frame_head[2]);
+		http2_frame_type = http2_frame_head[3];
+		http2_frame_flags = http2_frame_head[4];
+		http2_frame_stream_id = ((uint32_t)http2_frame_head[5] * 0x1000000u)
+		                        + ((uint32_t)http2_frame_head[6] * 0x10000u)
+		                        + ((uint32_t)http2_frame_head[7] * 0x100u)
+		                        + ((uint32_t)http2_frame_head[8]);
+
+		frame_is_end_stream = (0 != (http2_frame_flags & 0x01));
+		frame_is_end_headers = (0 != (http2_frame_flags & 0x04));
+		frame_is_padded = (0 != (http2_frame_flags & 0x08));
+		frame_is_priority = (0 != (http2_frame_flags & 0x20));
+
+		if (http2_frame_size > server_settings.settings_max_frame_size) {
+			/* TODO: Error Message */
+			DEBUG_TRACE("HTTP2 frame too large (%lu)",
+			            (unsigned long)http2_frame_size);
+			goto clean_http2;
+		}
+		bytes_read = mg_read(conn, buf, http2_frame_size);
+		if (bytes_read != (int)http2_frame_size) {
+			/* TODO: Error Message - or read again? */
+			DEBUG_TRACE("HTTP2 read error (%li != %li)",
+			            (signed long int)bytes_read,
+			            (signed long int)http2_frame_size);
+			goto clean_http2;
+		}
+
+		DEBUG_TRACE("HTTP2 frame type %u, size %u, stream %u, flags %02x",
+		            http2_frame_type,
+		            http2_frame_size,
+		            http2_frame_stream_id,
+		            http2_frame_flags);
+
+		/* Further processing according to frame type. See definition: */
+		/* https://tools.ietf.org/html/rfc7540#section-6 */
+		switch (http2_frame_type) {
+
+		case 0: /* DATA */
+		{
+			int i = 0; /* TODO */
+			DEBUG_TRACE("%s", "HTTP2 DATA frame?");
+		} break;
+
+		case 1: /* HEADERS */
+		{
+			int i = 0;
+			uint8_t padding = 0;
+			uint32_t dependency = 0;
+			uint8_t weight = 0;
+			uint8_t exclusive = 0;
+
+			if (frame_is_padded) {
+				padding = buf[i];
+				i++;
+				DEBUG_TRACE("HTTP2 frame padded by %u bytes", padding);
+			}
+			if (frame_is_priority) {
+				uint32_t val = ((uint32_t)buf[0 + i] * 0x1000000u)
+				               + ((uint32_t)buf[1 + i] * 0x10000u)
+				               + ((uint32_t)buf[2 + i] * 0x100u)
+				               + ((uint32_t)buf[3 + i]);
+				dependency = (val & 0x7FFFFFFFu);
+				exclusive = ((val & 0x80000000u) != 0);
+				weight = buf[4 + i];
+				i += 5;
+				DEBUG_TRACE(
+				    "HTTP2 frame weight %u, dependency %u (exclusive: %i)",
+				    weight,
+				    dependency,
+				    exclusive);
+			}
+
+			conn->request_info.num_headers = 0;
+
+			while (i < (int)http2_frame_size - (int)padding) {
+				const char *key = 0;
+				const char *val = 0;
+				uint8_t idx_mask = 0;
+				uint8_t value_known = 0;
+				uint8_t indexing = 0;
+				uint64_t idx = 0;
+
+				/* Classify next entry by checking the bit mask */
+				if ((buf[i] & 0x80u) == 0x80u) {
+					/* Indexed Header Field Representation:
+					 * https://tools.ietf.org/html/rfc7541#section-6.1 */
+					idx_mask = 0x7fu;
+					value_known = 1;
+
+				} else if ((buf[i] & 0xC0u) == 0x40u) {
+					/* Literal Header Field with Incremental Indexing:
+					 * https://tools.ietf.org/html/rfc7541#section-6.2.1 */
+					idx_mask = 0x3fu;
+					indexing = 1;
+
+				} else if ((buf[i] & 0xF0u) == 0x00u) {
+					/* Literal Header Field without Indexing:
+					 * https://tools.ietf.org/html/rfc7541#section-6.2.2 */
+					idx_mask = 0x0fu;
+
+				} else if ((buf[i] & 0xF0u) == 0x10u) {
+					/* Literal Header Field Never Indexed:
+					 * https://tools.ietf.org/html/rfc7541#section-6.2.3 */
+					idx_mask = 0x0fu;
+
+				} else if ((buf[i] & 0xE0u) == 0x20u) {
+					uint64_t tableSize;
+					/* Dynamic Table Size Update:
+					 * https://tools.ietf.org/html/rfc7541#section-6.3 */
+					idx_mask = 0x1fu;
+					tableSize = hpack_getnum(buf, &i, idx_mask, conn->phys_ctx);
+
+					/* TODO: check if tablesize > allowed table size */
+
+					/* Purge additional table entries */
+					purge_dynamic_header_table(conn, (uint32_t)tableSize);
+
+					/* Process next frame */
+					continue;
+
+				} else {
+					DEBUG_TRACE("HTTP2 unknown start pattern %02x", buf[i]);
+					goto clean_http2;
+				}
+
+				/* Get the header name table index */
+				idx = hpack_getnum(buf, &i, idx_mask, conn->phys_ctx);
+
+				/* Get Header name "key" */
+				if (idx == 0) {
+					/* Index 0: Header name encoded in following bytes */
+					key = hpack_decode(buf, &i, conn->phys_ctx);
+					CHECK_LEAK_HDR_ALLOC(key);
+				} else if (/*(idx >= 15) &&*/ (idx <= 61)) {
+					/* Take key name from predefined header table */
+					key = mg_strdup_ctx(hpack_predefined[idx].name,
+					                    conn->phys_ctx); /* leak? */
+					CHECK_LEAK_HDR_ALLOC(key);
+				} else if ((idx >= 62)
+				           && ((idx - 61) <= conn->http2.dyn_table_size)) {
+					/* Take from dynamic header table */
+					uint32_t local_table_idx = (uint32_t)idx - 62;
+					key = mg_strdup_ctx(
+					    conn->http2.dyn_table[local_table_idx].name,
+					    conn->phys_ctx);
+					CHECK_LEAK_HDR_ALLOC(key);
+				} else {
+					/* protocol violation */
+					DEBUG_TRACE("HTTP2 invalid index %lu", (unsigned long)idx);
+					goto clean_http2;
+				}
+				/* key is allocated now and must be freed later */
+
+				/* Get header value */
+				if (value_known) {
+					/* Server must already know the value */
+					if (idx <= 61) {
+						if (hpack_predefined[idx].value) {
+							val = mg_strdup_ctx(hpack_predefined[idx].value,
+							                    conn->phys_ctx); /* leak? */
+							CHECK_LEAK_HDR_ALLOC(val);
+						} else {
+							/* protocol violation */
+							DEBUG_TRACE("HTTP2 indexed header %lu has no value "
+							            "(key: %s)",
+							            (unsigned long)idx,
+							            key);
+							CHECK_LEAK_HDR_FREE(key);
+							mg_free((void *)key);
+							goto clean_http2;
+						}
+					} else if ((idx >= 62)
+					           && ((idx - 61) <= conn->http2.dyn_table_size)) {
+						uint32_t local_table_idx = (uint32_t)idx - 62;
+						val = mg_strdup_ctx(
+						    conn->http2.dyn_table[local_table_idx].value,
+						    conn->phys_ctx);
+						CHECK_LEAK_HDR_ALLOC(val);
+					} else {
+						/* protocol violation */
+						DEBUG_TRACE(
+						    "HTTP2 indexed header %lu out of range (key: %s)",
+						    (unsigned long)idx,
+						    key);
+						CHECK_LEAK_HDR_FREE(key);
+						mg_free((void *)key);
+						goto clean_http2;
+					}
+
+				} else {
+					/* Read value from HTTP2 stream */
+					val = hpack_decode(buf, &i, conn->phys_ctx); /* leak? */
+					CHECK_LEAK_HDR_ALLOC(val);
+
+					if (indexing) {
+						/* Add to index */
+						if (conn->http2.dyn_table_size
+						    >= HTTP2_DYN_TABLE_SIZE) {
+							/* Too many elements */
+							DEBUG_TRACE("HTTP2 index table is full (key: %s, "
+							            "value: %s)",
+							            key,
+							            val);
+
+							CHECK_LEAK_HDR_FREE(key);
+							CHECK_LEAK_HDR_FREE(val);
+
+							mg_free((void *)key);
+							mg_free((void *)val);
+							goto clean_http2;
+						}
+
+						/* Add to table of dynamic headers */
+						conn->http2.dyn_table[conn->http2.dyn_table_size].name =
+						    mg_strdup_ctx(key, conn->phys_ctx); /* leak */
+						conn->http2.dyn_table[conn->http2.dyn_table_size]
+						    .value =
+						    mg_strdup_ctx(val, conn->phys_ctx); /* leak */
+
+						CHECK_LEAK_DYN_ALLOC(
+						    conn->http2.dyn_table[conn->http2.dyn_table_size]
+						        .name);
+						CHECK_LEAK_DYN_ALLOC(
+						    conn->http2.dyn_table[conn->http2.dyn_table_size]
+						        .value);
+
+						conn->http2.dyn_table_size++;
+
+						DEBUG_TRACE("HTTP2 new dynamic header table entry %i "
+						            "(key: %s, value: %s)",
+						            (int)conn->http2.dyn_table_size,
+						            key,
+						            val);
+					}
+				}
+				/* val and key are allocated now and must be freed later */
+				/* Store these pointers in conn->request_info[].http_headers,
+				 * free_buffered_header_list(conn) will clean up later. */
+
+				/* Add header for this request */
+				if ((key != NULL) && (val != NULL)
+				    && (conn->request_info.num_headers < MG_MAX_HEADERS)) {
+					conn->request_info
+					    .http_headers[conn->request_info.num_headers]
+					    .name = key;
+					conn->request_info
+					    .http_headers[conn->request_info.num_headers]
+					    .value = val;
+					conn->request_info.num_headers++;
+
+					/* Some headers need to be stored in the request structure
+					 */
+					if (!strcmp(":method", key)) {
+						conn->request_info.request_method = val;
+					} else if (!strcmp(":path", key)) {
+						conn->request_info.local_uri = val;
+						conn->request_info.request_uri = val;
+					} else if (!strcmp(":status", key)) {
+						conn->status_code = atoi(val);
+					}
+
+					DEBUG_TRACE("HTTP2 request header (key: %s, value: %s)",
+					            key,
+					            val);
+
+				} else {
+					/* - either key or value are NULL (out of memory)
+					 * - or the max. number of headers is reached
+					 * in both cases free all memory
+					 */
+					DEBUG_TRACE("%s", "HTTP2 cannot add header");
+					CHECK_LEAK_HDR_FREE(key);
+					CHECK_LEAK_HDR_FREE(val);
+
+					mg_free((void *)key);
+					key = NULL;
+					mg_free((void *)val);
+					val = NULL;
+				}
+			}
+
+			/* stream id */
+			conn->http2.stream_id = http2_frame_stream_id;
+
+			/* header parsed */
+			DEBUG_TRACE("HTTP2 handle_request (stream %u)",
+			            http2_frame_stream_id);
+			handle_request(conn);
+
+			/* Send "final" frame */
+			DEBUG_TRACE("HTTP2 handle_request done (stream %u)",
+			            http2_frame_stream_id);
+			http2_data_frame_head(conn, 0, 1);
+			free_buffered_response_header_list(conn);
+			free_buffered_request_header_list(conn);
+		} break;
+
+		case 2: /* PRIORITY */
+		{
+			uint32_t dependStream =
+			    ((uint32_t)buf[0] * 0x1000000u) + ((uint32_t)buf[1] * 0x10000u)
+			    + ((uint32_t)buf[2] * 0x100u) + ((uint32_t)buf[3]);
+			uint8_t weight = buf[4];
+			DEBUG_TRACE("HTTP2 priority %u dependent stream %u",
+			            weight,
+			            dependStream);
+		} break;
+
+		case 3: /* RST_STREAM */
+		{
+			uint32_t errorId =
+			    ((uint32_t)buf[0] * 0x1000000u) + ((uint32_t)buf[1] * 0x10000u)
+			    + ((uint32_t)buf[2] * 0x100u) + ((uint32_t)buf[3]);
+			DEBUG_TRACE("HTTP2 reset with error %u", errorId);
+		} break;
+
+		case 4: /* SETTINGS */
+			if (http2_frame_stream_id != 0) {
+				/* Send protocol error */
+				http2_reset_stream(conn,
+				                   http2_frame_stream_id,
+				                   HTTP2_ERR_PROTOCOL_ERROR);
+				DEBUG_TRACE("%s", "HTTP2 received invalid settings frame");
+			} else if (http2_frame_flags) {
+				/* ACK frame. Do not reply. */
+				my_settings_accepted++;
+				DEBUG_TRACE("%s", "CivetWeb settings confirmed by peer");
+			} else {
+				int i;
+				for (i = 0; i < (int)http2_frame_size; i += 6) {
+					uint16_t id =
+					    ((uint16_t)buf[i] * 0x100u) + ((uint16_t)buf[i + 1]);
+					uint32_t val = ((uint32_t)buf[i + 2] * 0x1000000u)
+					               + ((uint32_t)buf[i + 3] * 0x10000u)
+					               + ((uint32_t)buf[i + 4] * 0x100u)
+					               + ((uint32_t)buf[i + 5]);
+					switch (id) {
+					case 1:
+						client_settings.settings_header_table_size = val;
+						DEBUG_TRACE("Received settings header_table_size: %u",
+						            val);
+						break;
+					case 2:
+						client_settings.settings_enable_push = (val != 0);
+						DEBUG_TRACE("Received settings enable_push: %u", val);
+						break;
+					case 3:
+						client_settings.settings_max_concurrent_streams = val;
+						DEBUG_TRACE(
+						    "Received settings max_concurrent_streams: %u",
+						    val);
+						break;
+					case 4:
+						client_settings.settings_initial_window_size = val;
+						DEBUG_TRACE("Received settings initial_window_size: %u",
+						            val);
+						break;
+					case 5:
+						client_settings.settings_max_frame_size = val;
+						DEBUG_TRACE("Received settings max_frame_size: %u",
+						            val);
+						break;
+					case 6:
+						client_settings.settings_max_header_list_size = val;
+						DEBUG_TRACE(
+						    "Received settings max_header_list_size: %u", val);
+						break;
+					default:
+						/* Unknown setting. Ignore it. */
+						DEBUG_TRACE("Received unknown settings id=%u: %u",
+						            id,
+						            val);
+						break;
+					}
+				}
+
+				/* Every settings frame must be acknowledged */
+				http2_settings_acknowledge(conn);
+			}
+			break;
+
+		case 5: /* PUSH_PROMISE */
+			DEBUG_TRACE("%s", "Push promise not supported");
+			break;
+
+		case 6: /* PING */
+			if (http2_frame_flags == 0) {
+				/* Set "reply" flag, and send same data back */
+				DEBUG_TRACE("%s", "Replying to ping");
+				http2_frame_head[4] = 1;
+				mg_xwrite(conn, http2_frame_head, sizeof(http2_frame_head));
+				mg_xwrite(conn, buf, http2_frame_size);
+			}
+			break;
+
+		case 7: /* GOAWAY */
+		{
+			uint32_t lastStream =
+			    ((uint32_t)buf[0] * 0x1000000u) + ((uint32_t)buf[1] * 0x10000u)
+			    + ((uint32_t)buf[2] * 0x100u) + ((uint32_t)buf[3]);
+			uint32_t errorId =
+			    ((uint32_t)buf[4] * 0x1000000u) + ((uint32_t)buf[5] * 0x10000u)
+			    + ((uint32_t)buf[6] * 0x100u) + ((uint32_t)buf[7]);
+			; /* followed by debug data */
+			uint32_t debugDataLen = http2_frame_size - 8;
+			char *debugData = (char *)buf + 8;
+
+			DEBUG_TRACE("HTTP2 goaway stream %u, error %u (%.*s)",
+			            lastStream,
+			            errorId,
+			            debugDataLen,
+			            debugData);
+
+		} break;
+
+		case 8: /* WINDOW_UPDATE */
+		{
+			uint32_t val = ((uint32_t)buf[0] * 0x1000000u)
+			               + ((uint32_t)buf[1] * 0x10000u)
+			               + ((uint32_t)buf[2] * 0x100u) + ((uint32_t)buf[3]);
+			http_window_length = (val & 0x7FFFFFFFu);
+
+			DEBUG_TRACE("HTTP2 window update stream %u, length %u",
+			            http2_frame_stream_id,
+			            http_window_length);
+		} break;
+
+		case 9: /* CONTINUATION */
+			DEBUG_TRACE("%s", "HTTP2 Continue");
+			break;
+
+		default:
+			/* TODO: Error Message */
+			DEBUG_TRACE("%s", "Unknown frame type");
+			goto clean_http2;
+		}
+	}
+
+clean_http2:
+	DEBUG_TRACE("%s", "HTTP2 free buffer, connection handler finished");
+	mg_free(buf);
+}
+
+
+#if 0
+static void
+HPACK_TEST()
+{
+	uint64_t test;
+
+	for (test = 0;; test++) {
+		char in[32] = {0};
+		uint8_t out[32] = {0};
+		char *check;
+		int i;
+		int l;
+
+		memcpy(in, &test, sizeof(test));
+		l = hpack_encode(out, in, 0);
+		i = 0;
+		check = hpack_decode(out, &i, NULL);
+
+		if (strcmp(in, check)) {
+			printf("Error\n");
+		}
+		mg_free(check);
+	}
+}
+
+static void
+HPACK_TABLE_TEST()
+{
+	int i;
+
+	uint32_t hpack_huff_end_code_expected[32] = { 0 };
+	uint8_t hpack_huff_start_index_expected[32] = { 0 };
+	int reverse_map[256] = { 0 };
+
+	for (i = 0; i < 256; i++) {
+		reverse_map[i] = -1;
+	}
+
+	for (i = 0; i < 256; i++) {
+		uint8_t bits = hpack_huff_dec[i].bitcount;
+		uint8_t dec = hpack_huff_dec[i].decoded;
+		if (bits > hpack_huff_dec[i + 1].bitcount) {
+			ck_abort_msg("hpack_huff_dec disorder at index %i", i);
+		}
+		if (hpack_huff_dec[i].encoded & (0xFFFFFFFFul << bits)) {
+			ck_abort_msg("hpack_huff_dec bits inconsistent at index %i", i);
+		}
+		if ((bits < 5) || (bits > 30)) {
+			ck_abort_msg("hpack_huff_dec bits out of range at index %i", i);
+		}
+		if (reverse_map[dec] != -1) {
+			ck_abort_msg("hpack_huff_dec duplicate: %i", hpack_huff_dec[i].decoded);
+		}
+		reverse_map[dec] = i;
+
+		hpack_huff_end_code_expected[bits - 5] = hpack_huff_dec[i].encoded;
+	}
+
+	for (i = 255; i >= 0; i--) {
+		uint8_t bits = hpack_huff_dec[i].bitcount;
+		hpack_huff_start_index_expected[bits - 5] = i;
+	}
+
+	for (i = 0; i < 256; i++) {
+		if (reverse_map[i] == -1) {
+			ck_abort_msg("reverse map at %i mising", i);
+		}
+	}
+
+	i = sizeof(hpack_huff_start_index) / sizeof(hpack_huff_start_index[0]);
+	if (i != 27) {
+		ck_abort_msg("hpack_huff_start_index size error: ", i);
+	}
+
+	i = sizeof(hpack_huff_end_code) / sizeof(hpack_huff_end_code[0]);
+	if (i != 27) {
+		ck_abort_msg("hpack_huff_end_code size error: ", i);
+	}
+
+	for (i = 0; i < 27; i++) {
+		if (hpack_huff_start_index_expected[i] != hpack_huff_start_index[i]) {
+			ck_abort_msg("hpack_huff_start_index error at %i", i);
+		}
+		if (hpack_huff_end_code_expected[i] != hpack_huff_end_code[i]) {
+			ck_abort_msg("hpack_huff_end_code error at %i", i);
+		}
+	}
+
+}
+#endif
+
+
+static void
+process_new_http2_connection(struct mg_connection *conn)
+{
+	if (!is_valid_http2_primer(conn)) {
+		/* Primer does not match expectation from RFC.
+		 * See https://tools.ietf.org/html/rfc7540#section-3.5 */
+		DEBUG_TRACE("%s", "No valid HTTP2 primer");
+		mg_send_http_error(conn, 400, "%s", "Invalid HTTP/2 primer");
+
+	} else {
+		/* Valid HTTP/2 primer received */
+		DEBUG_TRACE("%s", "Start handling HTTP2");
+		handle_http2(conn);
+
+		/* Free memory allocated for headers, if not done yet */
+		DEBUG_TRACE("%s", "Free remaining HTTP2 header memory");
+		free_buffered_response_header_list(conn);
+		free_buffered_request_header_list(conn);
+		purge_dynamic_header_table(conn, 0);
+	}
 }
 }

+ 0 - 1
src/mod_lua.inl

@@ -69,7 +69,6 @@ static const char *const LUABACKGROUNDPARAMS = "mg";
 
 
 
 
 /* Forward declarations */
 /* Forward declarations */
-static void handle_request(struct mg_connection *);
 static int handle_lsp_request(struct mg_connection *,
 static int handle_lsp_request(struct mg_connection *,
                               const char *,
                               const char *,
                               struct mg_file *,
                               struct mg_file *,

+ 271 - 0
src/response.inl

@@ -0,0 +1,271 @@
+/* response.inl
+ *
+ * Bufferring for HTTP headers for HTTP response.
+ * This function are only intended to be used at the server side.
+ * Optional for HTTP/1.0 and HTTP/1.1, mandatory for HTTP/2.
+ *
+ * This file is part of the CivetWeb project.
+ */
+
+
+/* Internal function to free header list */
+static void
+free_buffered_response_header_list(struct mg_connection *conn)
+{
+	while (conn->response_info.num_headers > 0) {
+		conn->response_info.num_headers--;
+		mg_free((void *)conn->response_info
+		            .http_headers[conn->response_info.num_headers]
+		            .name);
+		conn->response_info.http_headers[conn->response_info.num_headers].name =
+		    0;
+		mg_free((void *)conn->response_info
+		            .http_headers[conn->response_info.num_headers]
+		            .value);
+		conn->response_info.http_headers[conn->response_info.num_headers]
+		    .value = 0;
+	}
+}
+
+
+/* Initialize a new HTTP response
+ * Parameters:
+ *   conn: Current connection handle.
+ *   status: HTTP status code (e.g., 200 for "OK").
+ * Return:
+ *   0:    ok
+ *  -1:    parameter error
+ *  -2:    invalid connection type
+ *  -3:    invalid connection status
+ */
+int
+mg_response_header_start(struct mg_connection *conn, int status)
+{
+	if ((conn == NULL) || (status < 100) || (status > 999)) {
+		/* Parameter error */
+		return -1;
+	}
+	if ((conn->connection_type != CONNECTION_TYPE_REQUEST)
+	    || (conn->protocol_type == PROTOCOL_TYPE_WEBSOCKET)) {
+		/* Only allowed in server context */
+		return -2;
+	}
+	if (conn->request_state != 0) {
+		/* only allowed if nothing was sent up to now */
+		return -3;
+	}
+	conn->status_code = status;
+	conn->request_state = 1;
+	free_buffered_response_header_list(conn);
+
+	return 0;
+}
+
+
+/* Add a new HTTP response header line
+ * Parameters:
+ *   conn: Current connection handle.
+ *   header: Header name.
+ *   value: Header value.
+ *   value_len: Length of header value, excluding the terminating zero. Use -1
+ * for "strlen(value)". Return: 0:    ok -1:    parameter error -2:    invalid
+ * connection type -3:    invalid connection status -4:    too many headers -5:
+ * out of memory
+ */
+int
+mg_response_header_add(struct mg_connection *conn,
+                       const char *header,
+                       const char *value,
+                       int value_len)
+{
+	int hidx;
+
+	if ((conn == NULL) || (header == NULL) || (value == NULL)) {
+		/* Parameter error */
+		return -1;
+	}
+	if ((conn->connection_type != CONNECTION_TYPE_REQUEST)
+	    || (conn->protocol_type == PROTOCOL_TYPE_WEBSOCKET)) {
+		/* Only allowed in server context */
+		return -2;
+	}
+	if (conn->request_state != 1) {
+		/* only allowed if mg_response_header_start has been called before */
+		return -3;
+	}
+
+	hidx = conn->response_info.num_headers;
+	if (hidx >= MG_MAX_HEADERS) {
+		/* Too many headers */
+		return -4;
+	}
+
+	/* Alloc new element */
+	conn->response_info.http_headers[hidx].name =
+	    mg_strdup_ctx(header, conn->phys_ctx);
+	if (value_len >= 0) {
+		char *hbuf = mg_malloc_ctx((unsigned)value_len + 1, conn->phys_ctx);
+		if (hbuf) {
+			memcpy(hbuf, value, (unsigned)value_len);
+			hbuf[value_len] = 0;
+		}
+		conn->response_info.http_headers[hidx].value = hbuf;
+	} else {
+		conn->response_info.http_headers[hidx].value =
+		    mg_strdup_ctx(value, conn->phys_ctx);
+	}
+
+	if ((conn->response_info.http_headers[hidx].name == 0)
+	    || (conn->response_info.http_headers[hidx].value == 0)) {
+		/* Out of memory */
+		mg_free((void *)conn->response_info.http_headers[hidx].name);
+		conn->response_info.http_headers[hidx].name = 0;
+		mg_free((void *)conn->response_info.http_headers[hidx].value);
+		conn->response_info.http_headers[hidx].value = 0;
+		return -5;
+	}
+
+	/* OK, header stored */
+	conn->response_info.num_headers++;
+	return 0;
+}
+
+
+/* forward */
+static int parse_http_headers(char **buf, struct mg_header hdr[MG_MAX_HEADERS]);
+
+
+/* Add a complete header string (key + value).
+ * Parameters:
+ *   conn: Current connection handle.
+ *   http1_headers: Header line(s) in the form "name: value".
+ * Return:
+ *  >=0:   no error, number of header lines added
+ *  -1:    parameter error
+ *  -2:    invalid connection type
+ *  -3:    invalid connection status
+ *  -4:    too many headers
+ *  -5:    out of memory
+ */
+int
+mg_response_header_add_lines(struct mg_connection *conn,
+                             const char *http1_headers)
+{
+	struct mg_header add_hdr[MG_MAX_HEADERS];
+	int num_hdr, i, ret;
+	char *workbuffer, *parse;
+
+	/* We need to work on a copy of the work buffer, sice parse_http_headers
+	 * will modify */
+	workbuffer = mg_strdup_ctx(http1_headers, conn->phys_ctx);
+	if (!workbuffer) {
+		/* Out of memory */
+		return -5;
+	}
+
+	/* Call existing method to split header buffer */
+	parse = workbuffer;
+	num_hdr = parse_http_headers(&parse, add_hdr);
+	ret = num_hdr;
+
+	for (i = 0; i < num_hdr; i++) {
+		int lret =
+		    mg_response_header_add(conn, add_hdr[i].name, add_hdr[i].value, -1);
+		if ((ret > 0) && (lret < 0)) {
+			/* Store error return value */
+			ret = lret;
+		}
+	}
+
+	/* mg_response_header_add created a copy, so we can free the original */
+	mg_free(workbuffer);
+	return ret;
+}
+
+
+#if defined USE_HTTP2
+static int http2_send_response_headers(struct mg_connection *conn);
+#endif
+
+
+/* Send http response
+ * Parameters:
+ *   conn: Current connection handle.
+ * Return:
+ *   0:    ok
+ *  -1:    parameter error
+ *  -2:    invalid connection type
+ *  -3:    invalid connection status
+ */
+int
+mg_response_header_send(struct mg_connection *conn)
+{
+	const char *txt;
+	int i;
+	int has_date = 0;
+	int has_connection = 0;
+
+	if (conn == NULL) {
+		/* Parameter error */
+		return -1;
+	}
+	if ((conn->connection_type != CONNECTION_TYPE_REQUEST)
+	    || (conn->protocol_type == PROTOCOL_TYPE_WEBSOCKET)) {
+		/* Only allowed in server context */
+		return -2;
+	}
+	if (conn->request_state != 1) {
+		/* only allowed if mg_response_header_start has been called before */
+		return -3;
+	}
+
+	/* State: 2 */
+	conn->request_state = 2;
+	if (conn->protocol_type == PROTOCOL_TYPE_HTTP2) {
+#if defined USE_HTTP2
+		int ret = http2_send_response_headers(conn);
+		return ret ? 0 : 0; /* todo */
+#else
+		return -2;
+#endif
+	}
+
+	/* Send */
+	txt = mg_get_response_code_text(conn, conn->status_code);
+	mg_printf(conn,
+	          "HTTP/%s %i %s\r\n",
+	          conn->request_info.http_version,
+	          conn->status_code,
+	          txt);
+	for (i = 0; i < conn->response_info.num_headers; i++) {
+		mg_printf(conn,
+		          "%s: %s\r\n",
+		          conn->response_info.http_headers[i].name,
+		          conn->response_info.http_headers[i].value);
+
+		/* Check for some special headers */
+		if (!mg_strcasecmp("Date", conn->response_info.http_headers[i].name)) {
+			has_date = 1;
+		}
+		if (!mg_strcasecmp("Connection",
+		                   conn->response_info.http_headers[i].name)) {
+			has_connection = 1;
+		}
+	}
+
+	if (!has_date) {
+		time_t curtime = time(NULL);
+		char date[64];
+		gmt_time_string(date, sizeof(date), &curtime);
+		mg_printf(conn, "Date: %s\r\n", date);
+	}
+	if (!has_connection) {
+		mg_printf(conn, "Connection: %s\r\n", suggest_connection_header(conn));
+	}
+
+	mg_write(conn, "\r\n", 2);
+	conn->request_state = 3;
+
+	/* ok */
+	return 0;
+}

Beberapa file tidak ditampilkan karena terlalu banyak file yang berubah dalam diff ini