From dc5d9ba2908c2f7e9346e0d6d7f06011cf2ebdfc Mon Sep 17 00:00:00 2001 From: Huu Nguyen Date: Mon, 28 Sep 2015 20:22:02 -0400 Subject: Add two new gcc compilers to travis configuration --- .travis.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.travis.yml b/.travis.yml index cec0186..8dc7d12 100644 --- a/.travis.yml +++ b/.travis.yml @@ -11,6 +11,8 @@ compiler: env: matrix: - COMPILER=g++-4.8 CCOMPILER=gcc-4.8 PUSH_COVERAGE=ON + - COMPILER=g++-4.9 CCOMPILER=gcc-4.9 + - COMPILER=g++-5 CCOMPILER=gcc-5 addons: apt: @@ -19,6 +21,8 @@ addons: - boost-latest packages: - g++-4.8 + - g++-4.9 + - g++-5 - libboost1.55-all-dev - python-pip -- cgit v1.2.3-54-g00ecf From d42888fa286ea18e8d599e0191198ea3cff24d9d Mon Sep 17 00:00:00 2001 From: Huu Nguyen Date: Mon, 28 Sep 2015 20:22:42 -0400 Subject: Add clang compiler to travis configuration --- .travis.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.travis.yml b/.travis.yml index 8dc7d12..e283093 100644 --- a/.travis.yml +++ b/.travis.yml @@ -13,16 +13,20 @@ env: - COMPILER=g++-4.8 CCOMPILER=gcc-4.8 PUSH_COVERAGE=ON - COMPILER=g++-4.9 CCOMPILER=gcc-4.9 - COMPILER=g++-5 CCOMPILER=gcc-5 + - COMPILER=clang++-3.6 CCOMPILER=clang-3.6 addons: apt: sources: - ubuntu-toolchain-r-test - boost-latest + - llvm-toolchain-precise + - llvm-toolchain-precise-3.6 packages: - g++-4.8 - g++-4.9 - g++-5 + - clang-3.6 - libboost1.55-all-dev - python-pip -- cgit v1.2.3-54-g00ecf From d580ed7d25e6c642215db91a241faf4332be9a64 Mon Sep 17 00:00:00 2001 From: barcarolle Date: Thu, 20 Oct 2016 15:06:32 +0900 Subject: typo README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index bea6c50..f6261b4 100644 --- a/README.md +++ b/README.md @@ -32,7 +32,7 @@ int main() - You can also use [json11](https://github.com/dropbox/json11) or [rapidjson](https://github.com/miloyip/rapidjson) for better speed or readability - [Mustache](http://mustache.github.io/) based templating library (crow::mustache) - Header only - - Provide an amalgamated header file `crow_all.h' with every features + - Provide an amalgamated header file `crow_all.h` with every features - Middleware support - Websocket support -- cgit v1.2.3-54-g00ecf From 366e7c7e4b09096874d712696e79a913c17dd2c3 Mon Sep 17 00:00:00 2001 From: Philip Date: Fri, 21 Oct 2016 18:22:24 +0300 Subject: MS VS2015 compilation fix. It’s better to use native Win32 (strncpy_s, sprintf_s) to avoid compilation errors when building by MS C++. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- include/crow/json.h | 5 +++++ include/crow/query_string.h | 8 ++++++-- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/include/crow/json.h b/include/crow/json.h index 891fedf..35f9068 100644 --- a/include/crow/json.h +++ b/include/crow/json.h @@ -1377,7 +1377,12 @@ namespace crow case type::Number: { char outbuf[128]; +#ifdef _MSC_VER + sprintf_s(outbuf, 128, "%g", v.d); +#else sprintf(outbuf, "%g", v.d); +#endif + out += outbuf; } break; diff --git a/include/crow/query_string.h b/include/crow/query_string.h index 3f8bffe..edde0f6 100644 --- a/include/crow/query_string.h +++ b/include/crow/query_string.h @@ -222,8 +222,12 @@ inline char * qs_scanvalue(const char * key, const char * qs, char * val, size_t { qs++; i = strcspn(qs, "&=#"); - strncpy(val, qs, (val_len-1)<(i+1) ? (val_len-1) : (i+1)); - qs_decode(val); +#ifdef _MSC_VER + strncpy_s(val, val_len, qs, (val_len - 1)<(i + 1) ? (val_len - 1) : (i + 1)); +#else + strncpy(val, qs, (val_len - 1)<(i + 1) ? (val_len - 1) : (i + 1)); +#endif + qs_decode(val); } else { -- cgit v1.2.3-54-g00ecf From 34b050ee8f62d60cbc321ae985f7c5f7e2d70e48 Mon Sep 17 00:00:00 2001 From: Vadim Zeitlin Date: Tue, 15 Nov 2016 16:33:14 +0100 Subject: Show the number of threads used in the startup message This can be useful, especially when using the default hardware concurrency, to see how many threads does the server actually use in the logs. --- include/crow/http_server.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/include/crow/http_server.h b/include/crow/http_server.h index d5abb11..e1a9306 100644 --- a/include/crow/http_server.h +++ b/include/crow/http_server.h @@ -141,7 +141,8 @@ namespace crow }); } - CROW_LOG_INFO << server_name_ << " server is running, local port " << port_; + CROW_LOG_INFO << server_name_ << " server is running on port " << port_ + << " using " << concurrency_ << " threads"; signals_.async_wait( [&](const boost::system::error_code& /*error*/, int /*signal_number*/){ -- cgit v1.2.3-54-g00ecf From bd9a9ace4ae6da3e6600a58f79b1acb51edc831b Mon Sep 17 00:00:00 2001 From: Vadim Zeitlin Date: Tue, 15 Nov 2016 16:44:45 +0100 Subject: Use atomic type for connection count shared between threads Even if this variable is only used in debug mode, it's still bad to have data races on it, as it was the case (and reported by thread sanitizer) before. --- include/crow/http_connection.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/crow/http_connection.h b/include/crow/http_connection.h index 96f2d14..7e0a9f3 100644 --- a/include/crow/http_connection.h +++ b/include/crow/http_connection.h @@ -176,7 +176,7 @@ namespace crow } #ifdef CROW_ENABLE_DEBUG - static int connectionCount; + static std::atomic connectionCount; #endif template class Connection -- cgit v1.2.3-54-g00ecf From 990a8a34d090db2754d5004d0f793ea149fcd330 Mon Sep 17 00:00:00 2001 From: Javier Jerónimo Suárez Date: Sat, 19 Nov 2016 11:18:21 +0100 Subject: Trying to add support for conan.io dependency system... --- CMakeLists.txt | 4 ++++ conanfile.txt | 7 +++++++ 2 files changed, 11 insertions(+) create mode 100644 conanfile.txt diff --git a/CMakeLists.txt b/CMakeLists.txt index e2d3bc7..4d257bd 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,5 +1,9 @@ cmake_minimum_required(VERSION 2.8) project (crow_all) + +include(build/conanbuildinfo.cmake) +conan_basic_setup() + set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/cmake/") find_package(Tcmalloc) find_package(Threads) diff --git a/conanfile.txt b/conanfile.txt new file mode 100644 index 0000000..22b112a --- /dev/null +++ b/conanfile.txt @@ -0,0 +1,7 @@ +[requires] +Boost/1.60.0@lasote/stable +OpenSSL/1.0.2i@lasote/stable + +[generators] +cmake + -- cgit v1.2.3-54-g00ecf From bcf943b9b19552465c2482652444178152799cb4 Mon Sep 17 00:00:00 2001 From: Javier Jerónimo Suárez Date: Sat, 19 Nov 2016 11:35:49 +0100 Subject: Trying to add support for conan.io --- conanfile.py | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) create mode 100644 conanfile.py diff --git a/conanfile.py b/conanfile.py new file mode 100644 index 0000000..416ffd1 --- /dev/null +++ b/conanfile.py @@ -0,0 +1,23 @@ +from conans import ConanFile, CMake + +class CrowConan(ConanFile): + name = "Crow" + version = "0.1" + settings = "os", "compiler", "build_type", "arch" + # No exports necessary + + def source(self): + # this will create a hello subfolder, take it into account + self.run("git clone https://github.com/javierjeronimo/crow.git") + + def build(self): + cmake = CMake(self.settings) + self.run("cmake . %s" % cmake.build_config) + self.run("make") + + def package(self): + self.copy("*.h", dst="include", src="amalgamate") + + def package_info(self): + self.cpp_info.libs = ["crow"] + -- cgit v1.2.3-54-g00ecf From ede9a21dbc7658b64571233af8f06ee671c9888e Mon Sep 17 00:00:00 2001 From: Javier Jerónimo Suárez Date: Sat, 19 Nov 2016 11:40:17 +0100 Subject: More... --- conanfile.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/conanfile.py b/conanfile.py index 416ffd1..677c678 100644 --- a/conanfile.py +++ b/conanfile.py @@ -3,6 +3,8 @@ from conans import ConanFile, CMake class CrowConan(ConanFile): name = "Crow" version = "0.1" + url = "https://github.com/javierjeronimo/crow" + license = "see https://github.com/ipkn/crow/blob/master/LICENSE" settings = "os", "compiler", "build_type", "arch" # No exports necessary @@ -12,6 +14,7 @@ class CrowConan(ConanFile): def build(self): cmake = CMake(self.settings) + self.run('cmake %s %s' % (self.conanfile_directory, cmake.command_line)) self.run("cmake . %s" % cmake.build_config) self.run("make") -- cgit v1.2.3-54-g00ecf From 0f4cfc91086dc2d1f05407ac738a581f5768951b Mon Sep 17 00:00:00 2001 From: Javier Jerónimo Suárez Date: Sat, 19 Nov 2016 11:44:15 +0100 Subject: More --- conanfile.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/conanfile.py b/conanfile.py index 677c678..ba12a10 100644 --- a/conanfile.py +++ b/conanfile.py @@ -14,7 +14,7 @@ class CrowConan(ConanFile): def build(self): cmake = CMake(self.settings) - self.run('cmake %s %s' % (self.conanfile_directory, cmake.command_line)) + # self.run('cmake %s %s' % (self.conanfile_directory, cmake.command_line)) self.run("cmake . %s" % cmake.build_config) self.run("make") -- cgit v1.2.3-54-g00ecf From 748a95c84fe43471c5de58122de4c778f08ae2b3 Mon Sep 17 00:00:00 2001 From: Javier Jerónimo Suárez Date: Sat, 19 Nov 2016 11:51:32 +0100 Subject: More --- conanfile.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/conanfile.py b/conanfile.py index ba12a10..533179f 100644 --- a/conanfile.py +++ b/conanfile.py @@ -1,4 +1,5 @@ from conans import ConanFile, CMake +import shutil class CrowConan(ConanFile): name = "Crow" @@ -11,6 +12,7 @@ class CrowConan(ConanFile): def source(self): # this will create a hello subfolder, take it into account self.run("git clone https://github.com/javierjeronimo/crow.git") + shutil.move("crow/*", ".") def build(self): cmake = CMake(self.settings) -- cgit v1.2.3-54-g00ecf From 09cae9f4c213628f909fd0f8a9dcdaf5e10c17dd Mon Sep 17 00:00:00 2001 From: Javier Jerónimo Suárez Date: Sat, 19 Nov 2016 11:54:34 +0100 Subject: More --- conanfile.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/conanfile.py b/conanfile.py index 533179f..b72db5c 100644 --- a/conanfile.py +++ b/conanfile.py @@ -1,5 +1,4 @@ from conans import ConanFile, CMake -import shutil class CrowConan(ConanFile): name = "Crow" @@ -12,11 +11,10 @@ class CrowConan(ConanFile): def source(self): # this will create a hello subfolder, take it into account self.run("git clone https://github.com/javierjeronimo/crow.git") - shutil.move("crow/*", ".") def build(self): cmake = CMake(self.settings) - # self.run('cmake %s %s' % (self.conanfile_directory, cmake.command_line)) + self.run('cmake %s/crow %s' % (self.conanfile_directory, cmake.command_line)) self.run("cmake . %s" % cmake.build_config) self.run("make") -- cgit v1.2.3-54-g00ecf From 5037825a59811027047c0100dced0760e78b8ff0 Mon Sep 17 00:00:00 2001 From: Javier Jerónimo Suárez Date: Sat, 19 Nov 2016 11:56:33 +0100 Subject: More --- conanfile.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/conanfile.py b/conanfile.py index b72db5c..8a19503 100644 --- a/conanfile.py +++ b/conanfile.py @@ -15,7 +15,7 @@ class CrowConan(ConanFile): def build(self): cmake = CMake(self.settings) self.run('cmake %s/crow %s' % (self.conanfile_directory, cmake.command_line)) - self.run("cmake . %s" % cmake.build_config) + self.run("cmake --build . %s" % cmake.build_config) self.run("make") def package(self): -- cgit v1.2.3-54-g00ecf From d43cd764353cd12a40cdb9c692593d9caba2cd43 Mon Sep 17 00:00:00 2001 From: Javier Jerónimo Suárez Date: Sat, 19 Nov 2016 12:00:26 +0100 Subject: More --- conanfile.py | 1 + 1 file changed, 1 insertion(+) diff --git a/conanfile.py b/conanfile.py index 8a19503..4f2987e 100644 --- a/conanfile.py +++ b/conanfile.py @@ -6,6 +6,7 @@ class CrowConan(ConanFile): url = "https://github.com/javierjeronimo/crow" license = "see https://github.com/ipkn/crow/blob/master/LICENSE" settings = "os", "compiler", "build_type", "arch" + generators = "cmake" # No exports necessary def source(self): -- cgit v1.2.3-54-g00ecf From e9c97b1350025d93247704dc1f47a8b6fd6211cc Mon Sep 17 00:00:00 2001 From: Javier Jerónimo Suárez Date: Sat, 19 Nov 2016 12:02:04 +0100 Subject: More --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 4d257bd..05a90b7 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,7 +1,7 @@ cmake_minimum_required(VERSION 2.8) project (crow_all) -include(build/conanbuildinfo.cmake) +include(${CMAKE_BINARY_DIR}/conanbuildinfo.cmake) conan_basic_setup() set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/cmake/") -- cgit v1.2.3-54-g00ecf From 2877991498b009a16723a2b171967b846c8a74ca Mon Sep 17 00:00:00 2001 From: Javier Jerónimo Suárez Date: Sat, 19 Nov 2016 12:07:14 +0100 Subject: Requirements --- conanfile.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/conanfile.py b/conanfile.py index 4f2987e..cf0c798 100644 --- a/conanfile.py +++ b/conanfile.py @@ -7,6 +7,9 @@ class CrowConan(ConanFile): license = "see https://github.com/ipkn/crow/blob/master/LICENSE" settings = "os", "compiler", "build_type", "arch" generators = "cmake" + + requires = (("Boost/1.60.0@lasote/stable"), + ("OpenSSL/1.0.2i@lasote/stable")) # No exports necessary def source(self): -- cgit v1.2.3-54-g00ecf From c2f3aea0a1f60c7d2d5f586fe7abb2362c40faf3 Mon Sep 17 00:00:00 2001 From: Javier Jerónimo Suárez Date: Sat, 19 Nov 2016 12:39:13 +0100 Subject: Its a header only conan package --- conanfile.py | 1 - 1 file changed, 1 deletion(-) diff --git a/conanfile.py b/conanfile.py index cf0c798..9b06378 100644 --- a/conanfile.py +++ b/conanfile.py @@ -5,7 +5,6 @@ class CrowConan(ConanFile): version = "0.1" url = "https://github.com/javierjeronimo/crow" license = "see https://github.com/ipkn/crow/blob/master/LICENSE" - settings = "os", "compiler", "build_type", "arch" generators = "cmake" requires = (("Boost/1.60.0@lasote/stable"), -- cgit v1.2.3-54-g00ecf From 5f786cdccd2b511f867d08dbe7a10b7566e1e9fe Mon Sep 17 00:00:00 2001 From: Javier Jerónimo Suárez Date: Sun, 20 Nov 2016 22:40:47 +0100 Subject: Fixes --- conanfile.py | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/conanfile.py b/conanfile.py index 9b06378..73d9b14 100644 --- a/conanfile.py +++ b/conanfile.py @@ -6,6 +6,7 @@ class CrowConan(ConanFile): url = "https://github.com/javierjeronimo/crow" license = "see https://github.com/ipkn/crow/blob/master/LICENSE" generators = "cmake" + settings = "os", "compiler", "build_type", "arch" requires = (("Boost/1.60.0@lasote/stable"), ("OpenSSL/1.0.2i@lasote/stable")) @@ -17,13 +18,9 @@ class CrowConan(ConanFile): def build(self): cmake = CMake(self.settings) - self.run('cmake %s/crow %s' % (self.conanfile_directory, cmake.command_line)) - self.run("cmake --build . %s" % cmake.build_config) - self.run("make") + self.run('cmake %s/crow %s' % (self.conanfile_directory, cmake.command_line)) + self.run("cmake --build . %s" % cmake.build_config) + self.run("make") def package(self): self.copy("*.h", dst="include", src="amalgamate") - - def package_info(self): - self.cpp_info.libs = ["crow"] - -- cgit v1.2.3-54-g00ecf From 18bc428f06d71e061e49b71813027d9ef8ab8fb0 Mon Sep 17 00:00:00 2001 From: Javier Jerónimo Suárez Date: Sun, 20 Nov 2016 22:43:34 +0100 Subject: indent --- conanfile.py | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/conanfile.py b/conanfile.py index 73d9b14..36d8ba5 100644 --- a/conanfile.py +++ b/conanfile.py @@ -1,5 +1,6 @@ from conans import ConanFile, CMake + class CrowConan(ConanFile): name = "Crow" version = "0.1" @@ -10,6 +11,7 @@ class CrowConan(ConanFile): requires = (("Boost/1.60.0@lasote/stable"), ("OpenSSL/1.0.2i@lasote/stable")) + # No exports necessary def source(self): @@ -18,9 +20,10 @@ class CrowConan(ConanFile): def build(self): cmake = CMake(self.settings) - self.run('cmake %s/crow %s' % (self.conanfile_directory, cmake.command_line)) - self.run("cmake --build . %s" % cmake.build_config) - self.run("make") + self.run('cmake %s/crow %s' % (self.conanfile_directory, cmake.command_line)) + self.run("cmake --build . %s" % cmake.build_config) + self.run("make") + - def package(self): - self.copy("*.h", dst="include", src="amalgamate") +def package(self): + self.copy("*.h", dst="include", src="amalgamate") -- cgit v1.2.3-54-g00ecf From 79ca284d6c341f43157e038c722c30440859b1ee Mon Sep 17 00:00:00 2001 From: Javier Jerónimo Suárez Date: Sun, 20 Nov 2016 22:47:14 +0100 Subject: indent --- conanfile.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/conanfile.py b/conanfile.py index 36d8ba5..a8eb8c1 100644 --- a/conanfile.py +++ b/conanfile.py @@ -24,6 +24,5 @@ class CrowConan(ConanFile): self.run("cmake --build . %s" % cmake.build_config) self.run("make") - -def package(self): - self.copy("*.h", dst="include", src="amalgamate") + def package(self): + self.copy("*.h", dst="include", src="amalgamate") -- cgit v1.2.3-54-g00ecf From 91ac9111d45fa618a311c9dd2c8f30c031d8f46b Mon Sep 17 00:00:00 2001 From: Javier Jerónimo Suárez Date: Sun, 20 Nov 2016 23:04:25 +0100 Subject: fix --- conanfile.txt | 7 ------- 1 file changed, 7 deletions(-) delete mode 100644 conanfile.txt diff --git a/conanfile.txt b/conanfile.txt deleted file mode 100644 index 22b112a..0000000 --- a/conanfile.txt +++ /dev/null @@ -1,7 +0,0 @@ -[requires] -Boost/1.60.0@lasote/stable -OpenSSL/1.0.2i@lasote/stable - -[generators] -cmake - -- cgit v1.2.3-54-g00ecf From a71c0a0edb9262eb7dc83c67bf523fa7272d701f Mon Sep 17 00:00:00 2001 From: Javier Jerónimo Suárez Date: Sun, 20 Nov 2016 23:20:48 +0100 Subject: ignore --- .gitignore | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.gitignore b/.gitignore index 6dcb0a2..cfe8b64 100644 --- a/.gitignore +++ b/.gitignore @@ -33,3 +33,6 @@ build .directory crow_all.h + +# conan.io +build/ -- cgit v1.2.3-54-g00ecf From c03d3b2d5aebbfc11fd85af93d6c389ca4d39d73 Mon Sep 17 00:00:00 2001 From: Javier Jerónimo Suárez Date: Tue, 22 Nov 2016 17:50:06 +0100 Subject: Make it compatible with conan.io (but optional). --- CMakeLists.txt | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 05a90b7..f7f2d99 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,8 +1,10 @@ cmake_minimum_required(VERSION 2.8) project (crow_all) +if(EXISTS "${CMAKE_BINARY_DIR}/conanbuildinfo.cmake") include(${CMAKE_BINARY_DIR}/conanbuildinfo.cmake) conan_basic_setup() +endif() set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/cmake/") find_package(Tcmalloc) @@ -28,13 +30,13 @@ endif() include_directories( ${Boost_INCLUDE_DIR} ) -set(PROJECT_INCLUDE_DIR +set(PROJECT_INCLUDE_DIR ${PROJECT_SOURCE_DIR}/include ) include_directories("${PROJECT_INCLUDE_DIR}") include_directories("${PROJECT_SOURCE_DIR}") - + #add_subdirectory(src) add_subdirectory(examples) if (MSVC) -- cgit v1.2.3-54-g00ecf From c63113f8250c3ef0eb4dfc25aeab7e2e7c475fc4 Mon Sep 17 00:00:00 2001 From: Sergiu Giurgiu Date: Sun, 4 Dec 2016 14:03:20 -0500 Subject: Removed strict-aliasing warning --- amalgamate/crow_all.h | 12247 +++++++++++++++++++++++---------------------- include/crow/websocket.h | 8 +- 2 files changed, 6132 insertions(+), 6123 deletions(-) diff --git a/amalgamate/crow_all.h b/amalgamate/crow_all.h index 41b06ff..b2bf8d0 100644 --- a/amalgamate/crow_all.h +++ b/amalgamate/crow_all.h @@ -1,350 +1,332 @@ -#pragma once +/* merged revision: 5b951d74bd66ec9d38448e0a85b1cf8b85d97db3 */ +/* Copyright Joyent, Inc. and other Node contributors. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ +#ifndef CROW_http_parser_h +#define CROW_http_parser_h +#ifdef __cplusplus +extern "C" { +#endif -#include -#include -#include -#include -#include +/* Also update SONAME in the Makefile whenever you change these. */ +#define CROW_HTTP_PARSER_VERSION_MAJOR 2 +#define CROW_HTTP_PARSER_VERSION_MINOR 3 +#define CROW_HTTP_PARSER_VERSION_PATCH 0 -// ---------------------------------------------------------------------------- -// qs_parse (modified) -// https://github.com/bartgrantham/qs_parse -// ---------------------------------------------------------------------------- -/* Similar to strncmp, but handles URL-encoding for either string */ -int qs_strncmp(const char * s, const char * qs, size_t n); +#include +#if defined(_WIN32) && !defined(__MINGW32__) && (!defined(_MSC_VER) || _MSC_VER<1600) +#include +#include +typedef __int8 int8_t; +typedef unsigned __int8 uint8_t; +typedef __int16 int16_t; +typedef unsigned __int16 uint16_t; +typedef __int32 int32_t; +typedef unsigned __int32 uint32_t; +typedef __int64 int64_t; +typedef unsigned __int64 uint64_t; +#else +#include +#endif +/* Compile with -DHTTP_PARSER_STRICT=0 to make less checks, but run + * faster + */ +#ifndef CROW_HTTP_PARSER_STRICT +# define CROW_HTTP_PARSER_STRICT 1 +#endif -/* Finds the beginning of each key/value pair and stores a pointer in qs_kv. - * Also decodes the value portion of the k/v pair *in-place*. In a future - * enhancement it will also have a compile-time option of sorting qs_kv - * alphabetically by key. */ -int qs_parse(char * qs, char * qs_kv[], int qs_kv_size); +/* Maximium header size allowed. If the macro is not defined + * before including this header then the default is used. To + * change the maximum header size, define the macro in the build + * environment (e.g. -DHTTP_MAX_HEADER_SIZE=). To remove + * the effective limit on the size of the header, define the macro + * to a very large number (e.g. -DHTTP_MAX_HEADER_SIZE=0x7fffffff) + */ +#ifndef CROW_HTTP_MAX_HEADER_SIZE +# define CROW_HTTP_MAX_HEADER_SIZE (80*1024) +#endif +typedef struct http_parser http_parser; +typedef struct http_parser_settings http_parser_settings; -/* Used by qs_parse to decode the value portion of a k/v pair */ -int qs_decode(char * qs); +/* Callbacks should return non-zero to indicate an error. The parser will + * then halt execution. + * + * The one exception is on_headers_complete. In a HTTP_RESPONSE parser + * returning '1' from on_headers_complete will tell the parser that it + * should not expect a body. This is used when receiving a response to a + * HEAD request which may contain 'Content-Length' or 'Transfer-Encoding: + * chunked' headers that indicate the presence of a body. + * + * http_data_cb does not return data chunks. It will be call arbitrarally + * many times for each string. E.G. you might get 10 callbacks for "on_url" + * each providing just a few characters more data. + */ +typedef int (*http_data_cb) (http_parser*, const char *at, size_t length); +typedef int (*http_cb) (http_parser*); -/* Looks up the value according to the key on a pre-processed query string - * A future enhancement will be a compile-time option to look up the key - * in a pre-sorted qs_kv array via a binary search. */ -//char * qs_k2v(const char * key, char * qs_kv[], int qs_kv_size); - char * qs_k2v(const char * key, char * const * qs_kv, int qs_kv_size, int nth); +/* Request Methods */ +#define CROW_HTTP_METHOD_MAP(CROW_XX) \ + CROW_XX(0, DELETE, DELETE) \ + CROW_XX(1, GET, GET) \ + CROW_XX(2, HEAD, HEAD) \ + CROW_XX(3, POST, POST) \ + CROW_XX(4, PUT, PUT) \ + /* pathological */ \ + CROW_XX(5, CONNECT, CONNECT) \ + CROW_XX(6, OPTIONS, OPTIONS) \ + CROW_XX(7, TRACE, TRACE) \ + /* webdav */ \ + CROW_XX(8, COPY, COPY) \ + CROW_XX(9, LOCK, LOCK) \ + CROW_XX(10, MKCOL, MKCOL) \ + CROW_XX(11, MOVE, MOVE) \ + CROW_XX(12, PROPFIND, PROPFIND) \ + CROW_XX(13, PROPPATCH, PROPPATCH) \ + CROW_XX(14, SEARCH, SEARCH) \ + CROW_XX(15, UNLOCK, UNLOCK) \ + /* subversion */ \ + CROW_XX(16, REPORT, REPORT) \ + CROW_XX(17, MKACTIVITY, MKACTIVITY) \ + CROW_XX(18, CHECKOUT, CHECKOUT) \ + CROW_XX(19, MERGE, MERGE) \ + /* upnp */ \ + CROW_XX(20, MSEARCH, M-SEARCH) \ + CROW_XX(21, NOTIFY, NOTIFY) \ + CROW_XX(22, SUBSCRIBE, SUBSCRIBE) \ + CROW_XX(23, UNSUBSCRIBE, UNSUBSCRIBE) \ + /* RFC-5789 */ \ + CROW_XX(24, PATCH, PATCH) \ + CROW_XX(25, PURGE, PURGE) \ + /* CalDAV */ \ + CROW_XX(26, MKCALENDAR, MKCALENDAR) \ -/* Non-destructive lookup of value, based on key. User provides the - * destinaton string and length. */ -char * qs_scanvalue(const char * key, const char * qs, char * val, size_t val_len); +enum http_method + { +#define CROW_XX(num, name, string) HTTP_##name = num, + CROW_HTTP_METHOD_MAP(CROW_XX) +#undef CROW_XX + }; -// TODO: implement sorting of the qs_kv array; for now ensure it's not compiled -#undef _qsSORTING -// isxdigit _is_ available in , but let's avoid another header instead -#define CROW_QS_ISHEX(x) ((((x)>='0'&&(x)<='9') || ((x)>='A'&&(x)<='F') || ((x)>='a'&&(x)<='f')) ? 1 : 0) -#define CROW_QS_HEX2DEC(x) (((x)>='0'&&(x)<='9') ? (x)-48 : ((x)>='A'&&(x)<='F') ? (x)-55 : ((x)>='a'&&(x)<='f') ? (x)-87 : 0) -#define CROW_QS_ISQSCHR(x) ((((x)=='=')||((x)=='#')||((x)=='&')||((x)=='\0')) ? 0 : 1) +enum http_parser_type { HTTP_REQUEST, HTTP_RESPONSE, HTTP_BOTH }; -inline int qs_strncmp(const char * s, const char * qs, size_t n) -{ - int i=0; - unsigned char u1, u2, unyb, lnyb; - while(n-- > 0) - { - u1 = (unsigned char) *s++; - u2 = (unsigned char) *qs++; +/* Flag values for http_parser.flags field */ +enum flags + { F_CHUNKED = 1 << 0 + , F_CONNECTION_KEEP_ALIVE = 1 << 1 + , F_CONNECTION_CLOSE = 1 << 2 + , F_TRAILING = 1 << 3 + , F_UPGRADE = 1 << 4 + , F_SKIPBODY = 1 << 5 + }; - if ( ! CROW_QS_ISQSCHR(u1) ) { u1 = '\0'; } - if ( ! CROW_QS_ISQSCHR(u2) ) { u2 = '\0'; } - if ( u1 == '+' ) { u1 = ' '; } - if ( u1 == '%' ) // easier/safer than scanf - { - unyb = (unsigned char) *s++; - lnyb = (unsigned char) *s++; - if ( CROW_QS_ISHEX(unyb) && CROW_QS_ISHEX(lnyb) ) - u1 = (CROW_QS_HEX2DEC(unyb) * 16) + CROW_QS_HEX2DEC(lnyb); - else - u1 = '\0'; - } +/* Map for errno-related constants + * + * The provided argument should be a macro that takes 2 arguments. + */ +#define CROW_HTTP_ERRNO_MAP(CROW_XX) \ + /* No error */ \ + CROW_XX(OK, "success") \ + \ + /* Callback-related errors */ \ + CROW_XX(CB_message_begin, "the on_message_begin callback failed") \ + CROW_XX(CB_url, "the on_url callback failed") \ + CROW_XX(CB_header_field, "the on_header_field callback failed") \ + CROW_XX(CB_header_value, "the on_header_value callback failed") \ + CROW_XX(CB_headers_complete, "the on_headers_complete callback failed") \ + CROW_XX(CB_body, "the on_body callback failed") \ + CROW_XX(CB_message_complete, "the on_message_complete callback failed") \ + CROW_XX(CB_status, "the on_status callback failed") \ + \ + /* Parsing-related errors */ \ + CROW_XX(INVALID_EOF_STATE, "stream ended at an unexpected time") \ + CROW_XX(HEADER_OVERFLOW, \ + "too many header bytes seen; overflow detected") \ + CROW_XX(CLOSED_CONNECTION, \ + "data received after completed connection: close message") \ + CROW_XX(INVALID_VERSION, "invalid HTTP version") \ + CROW_XX(INVALID_STATUS, "invalid HTTP status code") \ + CROW_XX(INVALID_METHOD, "invalid HTTP method") \ + CROW_XX(INVALID_URL, "invalid URL") \ + CROW_XX(INVALID_HOST, "invalid host") \ + CROW_XX(INVALID_PORT, "invalid port") \ + CROW_XX(INVALID_PATH, "invalid path") \ + CROW_XX(INVALID_QUERY_STRING, "invalid query string") \ + CROW_XX(INVALID_FRAGMENT, "invalid fragment") \ + CROW_XX(LF_EXPECTED, "CROW_LF character expected") \ + CROW_XX(INVALID_HEADER_TOKEN, "invalid character in header") \ + CROW_XX(INVALID_CONTENT_LENGTH, \ + "invalid character in content-length header") \ + CROW_XX(INVALID_CHUNK_SIZE, \ + "invalid character in chunk size header") \ + CROW_XX(INVALID_CONSTANT, "invalid constant string") \ + CROW_XX(INVALID_INTERNAL_STATE, "encountered unexpected internal state")\ + CROW_XX(STRICT, "strict mode assertion failed") \ + CROW_XX(PAUSED, "parser is paused") \ + CROW_XX(UNKNOWN, "an unknown error occurred") - if ( u2 == '+' ) { u2 = ' '; } - if ( u2 == '%' ) // easier/safer than scanf - { - unyb = (unsigned char) *qs++; - lnyb = (unsigned char) *qs++; - if ( CROW_QS_ISHEX(unyb) && CROW_QS_ISHEX(lnyb) ) - u2 = (CROW_QS_HEX2DEC(unyb) * 16) + CROW_QS_HEX2DEC(lnyb); - else - u2 = '\0'; - } - if ( u1 != u2 ) - return u1 - u2; - if ( u1 == '\0' ) - return 0; - i++; - } - if ( CROW_QS_ISQSCHR(*qs) ) - return -1; - else - return 0; -} +/* Define HPE_* values for each errno value above */ +#define CROW_HTTP_ERRNO_GEN(n, s) HPE_##n, +enum http_errno { + CROW_HTTP_ERRNO_MAP(CROW_HTTP_ERRNO_GEN) +}; +#undef CROW_HTTP_ERRNO_GEN -inline int qs_parse(char * qs, char * qs_kv[], int qs_kv_size) -{ - int i, j; - char * substr_ptr; - - for(i=0; i means x iterations of this loop -> means *x+1* k/v pairs - - // we only decode the values in place, the keys could have '='s in them - // which will hose our ability to distinguish keys from values later - for(j=0; jhttp_errno) -#ifdef _qsSORTING -// TODO: qsort qs_kv, using qs_strncmp() for the comparison -#endif - return i; -} +struct http_parser { + /** PRIVATE **/ + unsigned int type : 2; /* enum http_parser_type */ + unsigned int flags : 6; /* F_* values from 'flags' enum; semi-public */ + unsigned int state : 8; /* enum state from http_parser.c */ + unsigned int header_state : 8; /* enum header_state from http_parser.c */ + unsigned int index : 8; /* index into current matcher */ + uint32_t nread; /* # bytes read in various scenarios */ + uint64_t content_length; /* # bytes in body (0 if no Content-Length header) */ -inline int qs_decode(char * qs) -{ - int i=0, j=0; + /** READ-ONLY **/ + unsigned short http_major; + unsigned short http_minor; + unsigned int status_code : 16; /* responses only */ + unsigned int method : 8; /* requests only */ + unsigned int http_errno : 7; - while( CROW_QS_ISQSCHR(qs[j]) ) - { - if ( qs[j] == '+' ) { qs[i] = ' '; } - else if ( qs[j] == '%' ) // easier/safer than scanf - { - if ( ! CROW_QS_ISHEX(qs[j+1]) || ! CROW_QS_ISHEX(qs[j+2]) ) - { - qs[i] = '\0'; - return i; - } - qs[i] = (CROW_QS_HEX2DEC(qs[j+1]) * 16) + CROW_QS_HEX2DEC(qs[j+2]); - j+=2; - } - else - { - qs[i] = qs[j]; - } - i++; j++; - } - qs[i] = '\0'; + /* 1 = Upgrade header was present and the parser has exited because of that. + * 0 = No upgrade header present. + * Should be checked when http_parser_execute() returns in addition to + * error checking. + */ + unsigned int upgrade : 1; - return i; -} + /** PUBLIC **/ + void *data; /* A pointer to get hook to the "connection" or "socket" object */ +}; -inline char * qs_k2v(const char * key, char * const * qs_kv, int qs_kv_size, int nth = 0) -{ - int i; - size_t key_len, skip; +struct http_parser_settings { + http_cb on_message_begin; + http_data_cb on_url; + http_data_cb on_status; + http_data_cb on_header_field; + http_data_cb on_header_value; + http_cb on_headers_complete; + http_data_cb on_body; + http_cb on_message_complete; +}; - key_len = strlen(key); -#ifdef _qsSORTING -// TODO: binary search for key in the sorted qs_kv -#else // _qsSORTING - for(i=0; i> 16) & 255; + * unsigned minor = (version >> 8) & 255; + * unsigned patch = version & 255; + * printf("http_parser v%u.%u.%u\n", major, minor, version); + */ +unsigned long http_parser_version(void); - if ( qs[0] == '\0' ) return NULL; +void http_parser_init(http_parser *parser, enum http_parser_type type); - qs += strcspn(qs, "=&#"); - if ( qs[0] == '=' ) - { - qs++; - i = strcspn(qs, "&=#"); - strncpy(val, qs, (val_len-1)<(i+1) ? (val_len-1) : (i+1)); - qs_decode(val); - } - else - { - if ( val_len > 0 ) - val[0] = '\0'; - } - return val; -} -// ---------------------------------------------------------------------------- +size_t http_parser_execute(http_parser *parser, + const http_parser_settings *settings, + const char *data, + size_t len); -namespace crow -{ - class query_string - { - public: - static const int MAX_KEY_VALUE_PAIRS_COUNT = 256; +/* If http_should_keep_alive() in the on_headers_complete or + * on_message_complete callback returns 0, then this should be + * the last message on the connection. + * If you are the server, respond with the "Connection: close" header. + * If you are the client, close the connection. + */ +int http_should_keep_alive(const http_parser *parser); - query_string() - { +/* Returns a string version of the HTTP method. */ +const char *http_method_str(enum http_method m); - } +/* Return a string name of the given error */ +const char *http_errno_name(enum http_errno err); - query_string(const query_string& qs) - : url_(qs.url_) - { - for(auto p:qs.key_value_pairs_) - { - key_value_pairs_.push_back((char*)(p-qs.url_.c_str()+url_.c_str())); - } - } +/* Return a string description of the given error */ +const char *http_errno_description(enum http_errno err); - query_string& operator = (const query_string& qs) - { - url_ = qs.url_; - key_value_pairs_.clear(); - for(auto p:qs.key_value_pairs_) - { - key_value_pairs_.push_back((char*)(p-qs.url_.c_str()+url_.c_str())); - } - return *this; - } +/* Parse a URL; return nonzero on failure */ +int http_parser_parse_url(const char *buf, size_t buflen, + int is_connect, + struct http_parser_url *u); - query_string& operator = (query_string&& qs) - { - key_value_pairs_ = std::move(qs.key_value_pairs_); - char* old_data = (char*)qs.url_.c_str(); - url_ = std::move(qs.url_); - for(auto& p:key_value_pairs_) - { - p += (char*)url_.c_str() - old_data; - } - return *this; - } +/* Pause or un-pause the parser; a nonzero value pauses */ +void http_parser_pause(http_parser *parser, int paused); +/* Checks if this is the final chunk of the body. */ +int http_body_is_final(const http_parser *parser); - query_string(std::string url) - : url_(std::move(url)) - { - if (url_.empty()) - return; - - key_value_pairs_.resize(MAX_KEY_VALUE_PAIRS_COUNT); - - int count = qs_parse(&url_[0], &key_value_pairs_[0], MAX_KEY_VALUE_PAIRS_COUNT); - key_value_pairs_.resize(count); - } - - void clear() - { - key_value_pairs_.clear(); - url_.clear(); - } - - friend std::ostream& operator<<(std::ostream& os, const query_string& qs) - { - os << "[ "; - for(size_t i = 0; i < qs.key_value_pairs_.size(); ++i) { - if (i) - os << ", "; - os << qs.key_value_pairs_[i]; - } - os << " ]"; - return os; - - } - - char* get (const std::string& name) const - { - char* ret = qs_k2v(name.c_str(), key_value_pairs_.data(), key_value_pairs_.size()); - return ret; - } - - std::vector get_list (const std::string& name) const - { - std::vector ret; - std::string plus = name + "[]"; - char* element = nullptr; - - int count = 0; - while(1) - { - element = qs_k2v(plus.c_str(), key_value_pairs_.data(), key_value_pairs_.size(), count++); - if (!element) - break; - ret.push_back(element); - } - return ret; - } - - - private: - std::string url_; - std::vector key_value_pairs_; - }; - -} // end namespace - - - -/* merged revision: 5b951d74bd66ec9d38448e0a85b1cf8b85d97db3 */ -/* Copyright Joyent, Inc. and other Node contributors. All rights reserved. +/*#include "http_parser.h"*/ +/* Based on src/http/ngx_http_parse.c from NGINX copyright Igor Sysoev + * + * Additional changes are licensed under the same terms as NGINX and + * copyright Joyent, Inc. and other Node contributors. All rights reserved. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to @@ -364,849 +346,522 @@ namespace crow * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ -#ifndef CROW_http_parser_h -#define CROW_http_parser_h -#ifdef __cplusplus -extern "C" { -#endif - -/* Also update SONAME in the Makefile whenever you change these. */ -#define CROW_HTTP_PARSER_VERSION_MAJOR 2 -#define CROW_HTTP_PARSER_VERSION_MINOR 3 -#define CROW_HTTP_PARSER_VERSION_PATCH 0 - -#include -#if defined(_WIN32) && !defined(__MINGW32__) && (!defined(_MSC_VER) || _MSC_VER<1600) -#include +#include #include -typedef __int8 int8_t; -typedef unsigned __int8 uint8_t; -typedef __int16 int16_t; -typedef unsigned __int16 uint16_t; -typedef __int32 int32_t; -typedef unsigned __int32 uint32_t; -typedef __int64 int64_t; -typedef unsigned __int64 uint64_t; -#else -#include +#include +#include +#include +#include + +#ifndef CROW_ULLONG_MAX +# define CROW_ULLONG_MAX ((uint64_t) -1) /* 2^64-1 */ #endif -/* Compile with -DHTTP_PARSER_STRICT=0 to make less checks, but run - * faster - */ -#ifndef CROW_HTTP_PARSER_STRICT -# define CROW_HTTP_PARSER_STRICT 1 +#ifndef CROW_MIN +# define CROW_MIN(a,b) ((a) < (b) ? (a) : (b)) #endif -/* Maximium header size allowed. If the macro is not defined - * before including this header then the default is used. To - * change the maximum header size, define the macro in the build - * environment (e.g. -DHTTP_MAX_HEADER_SIZE=). To remove - * the effective limit on the size of the header, define the macro - * to a very large number (e.g. -DHTTP_MAX_HEADER_SIZE=0x7fffffff) - */ -#ifndef CROW_HTTP_MAX_HEADER_SIZE -# define CROW_HTTP_MAX_HEADER_SIZE (80*1024) +#ifndef CROW_ARRAY_SIZE +# define CROW_ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0])) #endif -typedef struct http_parser http_parser; -typedef struct http_parser_settings http_parser_settings; +#ifndef CROW_BIT_AT +# define CROW_BIT_AT(a, i) \ + (!!((unsigned int) (a)[(unsigned int) (i) >> 3] & \ + (1 << ((unsigned int) (i) & 7)))) +#endif +#ifndef CROW_ELEM_AT +# define CROW_ELEM_AT(a, i, v) ((unsigned int) (i) < CROW_ARRAY_SIZE(a) ? (a)[(i)] : (v)) +#endif -/* Callbacks should return non-zero to indicate an error. The parser will - * then halt execution. - * - * The one exception is on_headers_complete. In a HTTP_RESPONSE parser - * returning '1' from on_headers_complete will tell the parser that it - * should not expect a body. This is used when receiving a response to a - * HEAD request which may contain 'Content-Length' or 'Transfer-Encoding: - * chunked' headers that indicate the presence of a body. - * - * http_data_cb does not return data chunks. It will be call arbitrarally - * many times for each string. E.G. you might get 10 callbacks for "on_url" - * each providing just a few characters more data. - */ -typedef int (*http_data_cb) (http_parser*, const char *at, size_t length); -typedef int (*http_cb) (http_parser*); +#define CROW_SET_ERRNO(e) \ +do { \ + parser->http_errno = (e); \ +} while(0) -/* Request Methods */ -#define CROW_HTTP_METHOD_MAP(CROW_XX) \ - CROW_XX(0, DELETE, DELETE) \ - CROW_XX(1, GET, GET) \ - CROW_XX(2, HEAD, HEAD) \ - CROW_XX(3, POST, POST) \ - CROW_XX(4, PUT, PUT) \ - /* pathological */ \ - CROW_XX(5, CONNECT, CONNECT) \ - CROW_XX(6, OPTIONS, OPTIONS) \ - CROW_XX(7, TRACE, TRACE) \ - /* webdav */ \ - CROW_XX(8, COPY, COPY) \ - CROW_XX(9, LOCK, LOCK) \ - CROW_XX(10, MKCOL, MKCOL) \ - CROW_XX(11, MOVE, MOVE) \ - CROW_XX(12, PROPFIND, PROPFIND) \ - CROW_XX(13, PROPPATCH, PROPPATCH) \ - CROW_XX(14, SEARCH, SEARCH) \ - CROW_XX(15, UNLOCK, UNLOCK) \ - /* subversion */ \ - CROW_XX(16, REPORT, REPORT) \ - CROW_XX(17, MKACTIVITY, MKACTIVITY) \ - CROW_XX(18, CHECKOUT, CHECKOUT) \ - CROW_XX(19, MERGE, MERGE) \ - /* upnp */ \ - CROW_XX(20, MSEARCH, M-SEARCH) \ - CROW_XX(21, NOTIFY, NOTIFY) \ - CROW_XX(22, SUBSCRIBE, SUBSCRIBE) \ - CROW_XX(23, UNSUBSCRIBE, UNSUBSCRIBE) \ - /* RFC-5789 */ \ - CROW_XX(24, PATCH, PATCH) \ - CROW_XX(25, PURGE, PURGE) \ - /* CalDAV */ \ - CROW_XX(26, MKCALENDAR, MKCALENDAR) \ +/* Run the notify callback FOR, returning ER if it fails */ +#define CROW_CALLBACK_NOTIFY_(FOR, ER) \ +do { \ + assert(CROW_HTTP_PARSER_ERRNO(parser) == HPE_OK); \ + \ + if (settings->on_##FOR) { \ + if (0 != settings->on_##FOR(parser)) { \ + CROW_SET_ERRNO(HPE_CB_##FOR); \ + } \ + \ + /* We either errored above or got paused; get out */ \ + if (CROW_HTTP_PARSER_ERRNO(parser) != HPE_OK) { \ + return (ER); \ + } \ + } \ +} while (0) -enum http_method - { -#define CROW_XX(num, name, string) HTTP_##name = num, - CROW_HTTP_METHOD_MAP(CROW_XX) -#undef CROW_XX - }; +/* Run the notify callback FOR and consume the current byte */ +#define CROW_CALLBACK_NOTIFY(FOR) CROW_CALLBACK_NOTIFY_(FOR, p - data + 1) +/* Run the notify callback FOR and don't consume the current byte */ +#define CROW_CALLBACK_NOTIFY_NOADVANCE(FOR) CROW_CALLBACK_NOTIFY_(FOR, p - data) -enum http_parser_type { HTTP_REQUEST, HTTP_RESPONSE, HTTP_BOTH }; +/* Run data callback FOR with LEN bytes, returning ER if it fails */ +#define CROW_CALLBACK_DATA_(FOR, LEN, ER) \ +do { \ + assert(CROW_HTTP_PARSER_ERRNO(parser) == HPE_OK); \ + \ + if (FOR##_mark) { \ + if (settings->on_##FOR) { \ + if (0 != settings->on_##FOR(parser, FOR##_mark, (LEN))) { \ + CROW_SET_ERRNO(HPE_CB_##FOR); \ + } \ + \ + /* We either errored above or got paused; get out */ \ + if (CROW_HTTP_PARSER_ERRNO(parser) != HPE_OK) { \ + return (ER); \ + } \ + } \ + FOR##_mark = NULL; \ + } \ +} while (0) + +/* Run the data callback FOR and consume the current byte */ +#define CROW_CALLBACK_DATA(FOR) \ + CROW_CALLBACK_DATA_(FOR, p - FOR##_mark, p - data + 1) +/* Run the data callback FOR and don't consume the current byte */ +#define CROW_CALLBACK_DATA_NOADVANCE(FOR) \ + CROW_CALLBACK_DATA_(FOR, p - FOR##_mark, p - data) -/* Flag values for http_parser.flags field */ -enum flags - { F_CHUNKED = 1 << 0 - , F_CONNECTION_KEEP_ALIVE = 1 << 1 - , F_CONNECTION_CLOSE = 1 << 2 - , F_TRAILING = 1 << 3 - , F_UPGRADE = 1 << 4 - , F_SKIPBODY = 1 << 5 - }; +/* Set the mark FOR; non-destructive if mark is already set */ +#define CROW_MARK(FOR) \ +do { \ + if (!FOR##_mark) { \ + FOR##_mark = p; \ + } \ +} while (0) -/* Map for errno-related constants - * - * The provided argument should be a macro that takes 2 arguments. - */ -#define CROW_HTTP_ERRNO_MAP(CROW_XX) \ - /* No error */ \ - CROW_XX(OK, "success") \ - \ - /* Callback-related errors */ \ - CROW_XX(CB_message_begin, "the on_message_begin callback failed") \ - CROW_XX(CB_url, "the on_url callback failed") \ - CROW_XX(CB_header_field, "the on_header_field callback failed") \ - CROW_XX(CB_header_value, "the on_header_value callback failed") \ - CROW_XX(CB_headers_complete, "the on_headers_complete callback failed") \ - CROW_XX(CB_body, "the on_body callback failed") \ - CROW_XX(CB_message_complete, "the on_message_complete callback failed") \ - CROW_XX(CB_status, "the on_status callback failed") \ - \ - /* Parsing-related errors */ \ - CROW_XX(INVALID_EOF_STATE, "stream ended at an unexpected time") \ - CROW_XX(HEADER_OVERFLOW, \ - "too many header bytes seen; overflow detected") \ - CROW_XX(CLOSED_CONNECTION, \ - "data received after completed connection: close message") \ - CROW_XX(INVALID_VERSION, "invalid HTTP version") \ - CROW_XX(INVALID_STATUS, "invalid HTTP status code") \ - CROW_XX(INVALID_METHOD, "invalid HTTP method") \ - CROW_XX(INVALID_URL, "invalid URL") \ - CROW_XX(INVALID_HOST, "invalid host") \ - CROW_XX(INVALID_PORT, "invalid port") \ - CROW_XX(INVALID_PATH, "invalid path") \ - CROW_XX(INVALID_QUERY_STRING, "invalid query string") \ - CROW_XX(INVALID_FRAGMENT, "invalid fragment") \ - CROW_XX(LF_EXPECTED, "CROW_LF character expected") \ - CROW_XX(INVALID_HEADER_TOKEN, "invalid character in header") \ - CROW_XX(INVALID_CONTENT_LENGTH, \ - "invalid character in content-length header") \ - CROW_XX(INVALID_CHUNK_SIZE, \ - "invalid character in chunk size header") \ - CROW_XX(INVALID_CONSTANT, "invalid constant string") \ - CROW_XX(INVALID_INTERNAL_STATE, "encountered unexpected internal state")\ - CROW_XX(STRICT, "strict mode assertion failed") \ - CROW_XX(PAUSED, "parser is paused") \ - CROW_XX(UNKNOWN, "an unknown error occurred") +#define CROW_PROXY_CONNECTION "proxy-connection" +#define CROW_CONNECTION "connection" +#define CROW_CONTENT_LENGTH "content-length" +#define CROW_TRANSFER_ENCODING "transfer-encoding" +#define CROW_UPGRADE "upgrade" +#define CROW_CHUNKED "chunked" +#define CROW_KEEP_ALIVE "keep-alive" +#define CROW_CLOSE "close" -/* Define HPE_* values for each errno value above */ -#define CROW_HTTP_ERRNO_GEN(n, s) HPE_##n, -enum http_errno { - CROW_HTTP_ERRNO_MAP(CROW_HTTP_ERRNO_GEN) -}; -#undef CROW_HTTP_ERRNO_GEN -/* Get an http_errno value from an http_parser */ -#define CROW_HTTP_PARSER_ERRNO(p) ((enum http_errno) (p)->http_errno) +enum state + { s_dead = 1 /* important that this is > 0 */ + , s_start_req_or_res + , s_res_or_resp_H + , s_start_res + , s_res_H + , s_res_HT + , s_res_HTT + , s_res_HTTP + , s_res_first_http_major + , s_res_http_major + , s_res_first_http_minor + , s_res_http_minor + , s_res_first_status_code + , s_res_status_code + , s_res_status_start + , s_res_status + , s_res_line_almost_done -struct http_parser { - /** PRIVATE **/ - unsigned int type : 2; /* enum http_parser_type */ - unsigned int flags : 6; /* F_* values from 'flags' enum; semi-public */ - unsigned int state : 8; /* enum state from http_parser.c */ - unsigned int header_state : 8; /* enum header_state from http_parser.c */ - unsigned int index : 8; /* index into current matcher */ + , s_start_req - uint32_t nread; /* # bytes read in various scenarios */ - uint64_t content_length; /* # bytes in body (0 if no Content-Length header) */ + , s_req_method + , s_req_spaces_before_url + , s_req_schema + , s_req_schema_slash + , s_req_schema_slash_slash + , s_req_server_start + , s_req_server + , s_req_server_with_at + , s_req_path + , s_req_query_string_start + , s_req_query_string + , s_req_fragment_start + , s_req_fragment + , s_req_http_start + , s_req_http_H + , s_req_http_HT + , s_req_http_HTT + , s_req_http_HTTP + , s_req_first_http_major + , s_req_http_major + , s_req_first_http_minor + , s_req_http_minor + , s_req_line_almost_done - /** READ-ONLY **/ - unsigned short http_major; - unsigned short http_minor; - unsigned int status_code : 16; /* responses only */ - unsigned int method : 8; /* requests only */ - unsigned int http_errno : 7; + , s_header_field_start + , s_header_field + , s_header_value_discard_ws + , s_header_value_discard_ws_almost_done + , s_header_value_discard_lws + , s_header_value_start + , s_header_value + , s_header_value_lws - /* 1 = Upgrade header was present and the parser has exited because of that. - * 0 = No upgrade header present. - * Should be checked when http_parser_execute() returns in addition to - * error checking. - */ - unsigned int upgrade : 1; + , s_header_almost_done - /** PUBLIC **/ - void *data; /* A pointer to get hook to the "connection" or "socket" object */ -}; + , s_chunk_size_start + , s_chunk_size + , s_chunk_parameters + , s_chunk_size_almost_done + , s_headers_almost_done + , s_headers_done -struct http_parser_settings { - http_cb on_message_begin; - http_data_cb on_url; - http_data_cb on_status; - http_data_cb on_header_field; - http_data_cb on_header_value; - http_cb on_headers_complete; - http_data_cb on_body; - http_cb on_message_complete; -}; + /* Important: 's_headers_done' must be the last 'header' state. All + * states beyond this must be 'body' states. It is used for overflow + * checking. See the CROW_PARSING_HEADER() macro. + */ + , s_chunk_data + , s_chunk_data_almost_done + , s_chunk_data_done -enum http_parser_url_fields - { UF_SCHEMA = 0 - , UF_HOST = 1 - , UF_PORT = 2 - , UF_PATH = 3 - , UF_QUERY = 4 - , UF_FRAGMENT = 5 - , UF_USERINFO = 6 - , UF_MAX = 7 + , s_body_identity + , s_body_identity_eof + + , s_message_done }; -/* Result structure for http_parser_parse_url(). - * - * Callers should index into field_data[] with UF_* values iff field_set - * has the relevant (1 << UF_*) bit set. As a courtesy to clients (and - * because we probably have padding left over), we convert any port to - * a uint16_t. - */ -struct http_parser_url { - uint16_t field_set; /* Bitmask of (1 << UF_*) values */ - uint16_t port; /* Converted UF_PORT string */ +#define CROW_PARSING_HEADER(state) (state <= s_headers_done) - struct { - uint16_t off; /* Offset into buffer in which field starts */ - uint16_t len; /* Length of run in buffer */ - } field_data[UF_MAX]; -}; +enum header_states + { h_general = 0 + , h_C + , h_CO + , h_CON -/* Returns the library version. Bits 16-23 contain the major version number, - * bits 8-15 the minor version number and bits 0-7 the patch level. - * Usage example: - * - * unsigned long version = http_parser_version(); - * unsigned major = (version >> 16) & 255; - * unsigned minor = (version >> 8) & 255; - * unsigned patch = version & 255; - * printf("http_parser v%u.%u.%u\n", major, minor, version); - */ -unsigned long http_parser_version(void); + , h_matching_connection + , h_matching_proxy_connection + , h_matching_content_length + , h_matching_transfer_encoding + , h_matching_upgrade -void http_parser_init(http_parser *parser, enum http_parser_type type); + , h_connection + , h_content_length + , h_transfer_encoding + , h_upgrade + , h_matching_transfer_encoding_chunked + , h_matching_connection_keep_alive + , h_matching_connection_close -size_t http_parser_execute(http_parser *parser, - const http_parser_settings *settings, - const char *data, - size_t len); + , h_transfer_encoding_chunked + , h_connection_keep_alive + , h_connection_close + }; +enum http_host_state + { + s_http_host_dead = 1 + , s_http_userinfo_start + , s_http_userinfo + , s_http_host_start + , s_http_host_v6_start + , s_http_host + , s_http_host_v6 + , s_http_host_v6_end + , s_http_host_port_start + , s_http_host_port +}; -/* If http_should_keep_alive() in the on_headers_complete or - * on_message_complete callback returns 0, then this should be - * the last message on the connection. - * If you are the server, respond with the "Connection: close" header. - * If you are the client, close the connection. - */ -int http_should_keep_alive(const http_parser *parser); +/* Macros for character classes; depends on strict-mode */ +#define CROW_CR '\r' +#define CROW_LF '\n' +#define CROW_LOWER(c) (unsigned char)(c | 0x20) +#define CROW_IS_ALPHA(c) (CROW_LOWER(c) >= 'a' && CROW_LOWER(c) <= 'z') +#define CROW_IS_NUM(c) ((c) >= '0' && (c) <= '9') +#define CROW_IS_ALPHANUM(c) (CROW_IS_ALPHA(c) || CROW_IS_NUM(c)) +#define CROW_IS_HEX(c) (CROW_IS_NUM(c) || (CROW_LOWER(c) >= 'a' && CROW_LOWER(c) <= 'f')) +#define CROW_IS_MARK(c) ((c) == '-' || (c) == '_' || (c) == '.' || \ + (c) == '!' || (c) == '~' || (c) == '*' || (c) == '\'' || (c) == '(' || \ + (c) == ')') +#define CROW_IS_USERINFO_CHAR(c) (CROW_IS_ALPHANUM(c) || CROW_IS_MARK(c) || (c) == '%' || \ + (c) == ';' || (c) == ':' || (c) == '&' || (c) == '=' || (c) == '+' || \ + (c) == '$' || (c) == ',') -/* Returns a string version of the HTTP method. */ -const char *http_method_str(enum http_method m); +#if CROW_HTTP_PARSER_STRICT +#define CROW_TOKEN(c) (tokens[(unsigned char)c]) +#define CROW_IS_URL_CHAR(c) (CROW_BIT_AT(normal_url_char, (unsigned char)c)) +#define CROW_IS_HOST_CHAR(c) (CROW_IS_ALPHANUM(c) || (c) == '.' || (c) == '-') +#else +#define CROW_TOKEN(c) ((c == ' ') ? ' ' : tokens[(unsigned char)c]) +#define CROW_IS_URL_CHAR(c) \ + (CROW_BIT_AT(normal_url_char, (unsigned char)c) || ((c) & 0x80)) +#define CROW_IS_HOST_CHAR(c) \ + (CROW_IS_ALPHANUM(c) || (c) == '.' || (c) == '-' || (c) == '_') +#endif -/* Return a string name of the given error */ -const char *http_errno_name(enum http_errno err); -/* Return a string description of the given error */ -const char *http_errno_description(enum http_errno err); +#define CROW_start_state (parser->type == HTTP_REQUEST ? s_start_req : s_start_res) -/* Parse a URL; return nonzero on failure */ -int http_parser_parse_url(const char *buf, size_t buflen, - int is_connect, - struct http_parser_url *u); -/* Pause or un-pause the parser; a nonzero value pauses */ -void http_parser_pause(http_parser *parser, int paused); +#if CROW_HTTP_PARSER_STRICT +# define CROW_STRICT_CHECK(cond) \ +do { \ + if (cond) { \ + CROW_SET_ERRNO(HPE_STRICT); \ + goto error; \ + } \ +} while (0) +# define CROW_NEW_MESSAGE() (http_should_keep_alive(parser) ? CROW_start_state : s_dead) +#else +# define CROW_STRICT_CHECK(cond) +# define CROW_NEW_MESSAGE() CROW_start_state +#endif -/* Checks if this is the final chunk of the body. */ -int http_body_is_final(const http_parser *parser); -/*#include "http_parser.h"*/ -/* Based on src/http/ngx_http_parse.c from NGINX copyright Igor Sysoev - * - * Additional changes are licensed under the same terms as NGINX and - * copyright Joyent, Inc. and other Node contributors. All rights reserved. - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to - * deal in the Software without restriction, including without limitation the - * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or - * sell copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: + +int http_message_needs_eof(const http_parser *parser); + +/* Our URL parser. * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. + * This is designed to be shared by http_parser_execute() for URL validation, + * hence it has a state transition + byte-for-byte interface. In addition, it + * is meant to be embedded in http_parser_parse_url(), which does the dirty + * work of turning state transitions URL components for its API. * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS - * IN THE SOFTWARE. + * This function should only be invoked with non-space characters. It is + * assumed that the caller cares about (and can detect) the transition between + * URL and non-URL states by looking for these. */ -#include -#include -#include -#include -#include -#include - -#ifndef CROW_ULLONG_MAX -# define CROW_ULLONG_MAX ((uint64_t) -1) /* 2^64-1 */ +inline enum state +parse_url_char(enum state s, const char ch) +{ +#if CROW_HTTP_PARSER_STRICT +# define CROW_T(v) 0 +#else +# define CROW_T(v) v #endif -#ifndef CROW_MIN -# define CROW_MIN(a,b) ((a) < (b) ? (a) : (b)) -#endif -#ifndef CROW_ARRAY_SIZE -# define CROW_ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0])) -#endif +static const uint8_t normal_url_char[32] = { +/* 0 nul 1 soh 2 stx 3 etx 4 eot 5 enq 6 ack 7 bel */ + 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0, +/* 8 bs 9 ht 10 nl 11 vt 12 np 13 cr 14 so 15 si */ + 0 | CROW_T(2) | 0 | 0 | CROW_T(16) | 0 | 0 | 0, +/* 16 dle 17 dc1 18 dc2 19 dc3 20 dc4 21 nak 22 syn 23 etb */ + 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0, +/* 24 can 25 em 26 sub 27 esc 28 fs 29 gs 30 rs 31 us */ + 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0, +/* 32 sp 33 ! 34 " 35 # 36 $ 37 % 38 & 39 ' */ + 0 | 2 | 4 | 0 | 16 | 32 | 64 | 128, +/* 40 ( 41 ) 42 * 43 + 44 , 45 - 46 . 47 / */ + 1 | 2 | 4 | 8 | 16 | 32 | 64 | 128, +/* 48 0 49 1 50 2 51 3 52 4 53 5 54 6 55 7 */ + 1 | 2 | 4 | 8 | 16 | 32 | 64 | 128, +/* 56 8 57 9 58 : 59 ; 60 < 61 = 62 > 63 ? */ + 1 | 2 | 4 | 8 | 16 | 32 | 64 | 0, +/* 64 @ 65 A 66 B 67 C 68 D 69 E 70 F 71 G */ + 1 | 2 | 4 | 8 | 16 | 32 | 64 | 128, +/* 72 H 73 I 74 J 75 K 76 L 77 M 78 N 79 O */ + 1 | 2 | 4 | 8 | 16 | 32 | 64 | 128, +/* 80 P 81 Q 82 R 83 S 84 CROW_T 85 U 86 V 87 W */ + 1 | 2 | 4 | 8 | 16 | 32 | 64 | 128, +/* 88 X 89 Y 90 Z 91 [ 92 \ 93 ] 94 ^ 95 _ */ + 1 | 2 | 4 | 8 | 16 | 32 | 64 | 128, +/* 96 ` 97 a 98 b 99 c 100 d 101 e 102 f 103 g */ + 1 | 2 | 4 | 8 | 16 | 32 | 64 | 128, +/* 104 h 105 i 106 j 107 k 108 l 109 m 110 n 111 o */ + 1 | 2 | 4 | 8 | 16 | 32 | 64 | 128, +/* 112 p 113 q 114 r 115 s 116 t 117 u 118 v 119 w */ + 1 | 2 | 4 | 8 | 16 | 32 | 64 | 128, +/* 120 x 121 y 122 z 123 { 124 | 125 } 126 ~ 127 del */ + 1 | 2 | 4 | 8 | 16 | 32 | 64 | 0, }; -#ifndef CROW_BIT_AT -# define CROW_BIT_AT(a, i) \ - (!!((unsigned int) (a)[(unsigned int) (i) >> 3] & \ - (1 << ((unsigned int) (i) & 7)))) -#endif +#undef CROW_T -#ifndef CROW_ELEM_AT -# define CROW_ELEM_AT(a, i, v) ((unsigned int) (i) < CROW_ARRAY_SIZE(a) ? (a)[(i)] : (v)) + if (ch == ' ' || ch == '\r' || ch == '\n') { + return s_dead; + } + +#if CROW_HTTP_PARSER_STRICT + if (ch == '\t' || ch == '\f') { + return s_dead; + } #endif -#define CROW_SET_ERRNO(e) \ -do { \ - parser->http_errno = (e); \ -} while(0) + switch (s) { + case s_req_spaces_before_url: + /* Proxied requests are followed by scheme of an absolute URI (alpha). + * All methods except CONNECT are followed by '/' or '*'. + */ + if (ch == '/' || ch == '*') { + return s_req_path; + } -/* Run the notify callback FOR, returning ER if it fails */ -#define CROW_CALLBACK_NOTIFY_(FOR, ER) \ -do { \ - assert(CROW_HTTP_PARSER_ERRNO(parser) == HPE_OK); \ - \ - if (settings->on_##FOR) { \ - if (0 != settings->on_##FOR(parser)) { \ - CROW_SET_ERRNO(HPE_CB_##FOR); \ - } \ - \ - /* We either errored above or got paused; get out */ \ - if (CROW_HTTP_PARSER_ERRNO(parser) != HPE_OK) { \ - return (ER); \ - } \ - } \ -} while (0) + if (CROW_IS_ALPHA(ch)) { + return s_req_schema; + } -/* Run the notify callback FOR and consume the current byte */ -#define CROW_CALLBACK_NOTIFY(FOR) CROW_CALLBACK_NOTIFY_(FOR, p - data + 1) + break; -/* Run the notify callback FOR and don't consume the current byte */ -#define CROW_CALLBACK_NOTIFY_NOADVANCE(FOR) CROW_CALLBACK_NOTIFY_(FOR, p - data) + case s_req_schema: + if (CROW_IS_ALPHA(ch)) { + return s; + } -/* Run data callback FOR with LEN bytes, returning ER if it fails */ -#define CROW_CALLBACK_DATA_(FOR, LEN, ER) \ -do { \ - assert(CROW_HTTP_PARSER_ERRNO(parser) == HPE_OK); \ - \ - if (FOR##_mark) { \ - if (settings->on_##FOR) { \ - if (0 != settings->on_##FOR(parser, FOR##_mark, (LEN))) { \ - CROW_SET_ERRNO(HPE_CB_##FOR); \ - } \ - \ - /* We either errored above or got paused; get out */ \ - if (CROW_HTTP_PARSER_ERRNO(parser) != HPE_OK) { \ - return (ER); \ - } \ - } \ - FOR##_mark = NULL; \ - } \ -} while (0) - -/* Run the data callback FOR and consume the current byte */ -#define CROW_CALLBACK_DATA(FOR) \ - CROW_CALLBACK_DATA_(FOR, p - FOR##_mark, p - data + 1) + if (ch == ':') { + return s_req_schema_slash; + } -/* Run the data callback FOR and don't consume the current byte */ -#define CROW_CALLBACK_DATA_NOADVANCE(FOR) \ - CROW_CALLBACK_DATA_(FOR, p - FOR##_mark, p - data) + break; -/* Set the mark FOR; non-destructive if mark is already set */ -#define CROW_MARK(FOR) \ -do { \ - if (!FOR##_mark) { \ - FOR##_mark = p; \ - } \ -} while (0) + case s_req_schema_slash: + if (ch == '/') { + return s_req_schema_slash_slash; + } + break; -#define CROW_PROXY_CONNECTION "proxy-connection" -#define CROW_CONNECTION "connection" -#define CROW_CONTENT_LENGTH "content-length" -#define CROW_TRANSFER_ENCODING "transfer-encoding" -#define CROW_UPGRADE "upgrade" -#define CROW_CHUNKED "chunked" -#define CROW_KEEP_ALIVE "keep-alive" -#define CROW_CLOSE "close" + case s_req_schema_slash_slash: + if (ch == '/') { + return s_req_server_start; + } + break; + case s_req_server_with_at: + if (ch == '@') { + return s_dead; + } + /* FALLTHROUGH */ + case s_req_server_start: + case s_req_server: + if (ch == '/') { + return s_req_path; + } -enum state - { s_dead = 1 /* important that this is > 0 */ + if (ch == '?') { + return s_req_query_string_start; + } - , s_start_req_or_res - , s_res_or_resp_H - , s_start_res - , s_res_H - , s_res_HT - , s_res_HTT - , s_res_HTTP - , s_res_first_http_major - , s_res_http_major - , s_res_first_http_minor - , s_res_http_minor - , s_res_first_status_code - , s_res_status_code - , s_res_status_start - , s_res_status - , s_res_line_almost_done - - , s_start_req - - , s_req_method - , s_req_spaces_before_url - , s_req_schema - , s_req_schema_slash - , s_req_schema_slash_slash - , s_req_server_start - , s_req_server - , s_req_server_with_at - , s_req_path - , s_req_query_string_start - , s_req_query_string - , s_req_fragment_start - , s_req_fragment - , s_req_http_start - , s_req_http_H - , s_req_http_HT - , s_req_http_HTT - , s_req_http_HTTP - , s_req_first_http_major - , s_req_http_major - , s_req_first_http_minor - , s_req_http_minor - , s_req_line_almost_done - - , s_header_field_start - , s_header_field - , s_header_value_discard_ws - , s_header_value_discard_ws_almost_done - , s_header_value_discard_lws - , s_header_value_start - , s_header_value - , s_header_value_lws - - , s_header_almost_done - - , s_chunk_size_start - , s_chunk_size - , s_chunk_parameters - , s_chunk_size_almost_done - - , s_headers_almost_done - , s_headers_done - - /* Important: 's_headers_done' must be the last 'header' state. All - * states beyond this must be 'body' states. It is used for overflow - * checking. See the CROW_PARSING_HEADER() macro. - */ - - , s_chunk_data - , s_chunk_data_almost_done - , s_chunk_data_done - - , s_body_identity - , s_body_identity_eof + if (ch == '@') { + return s_req_server_with_at; + } - , s_message_done - }; + if (CROW_IS_USERINFO_CHAR(ch) || ch == '[' || ch == ']') { + return s_req_server; + } + break; -#define CROW_PARSING_HEADER(state) (state <= s_headers_done) + case s_req_path: + if (CROW_IS_URL_CHAR(ch)) { + return s; + } + switch (ch) { + case '?': + return s_req_query_string_start; -enum header_states - { h_general = 0 - , h_C - , h_CO - , h_CON + case '#': + return s_req_fragment_start; + } - , h_matching_connection - , h_matching_proxy_connection - , h_matching_content_length - , h_matching_transfer_encoding - , h_matching_upgrade + break; - , h_connection - , h_content_length - , h_transfer_encoding - , h_upgrade + case s_req_query_string_start: + case s_req_query_string: + if (CROW_IS_URL_CHAR(ch)) { + return s_req_query_string; + } - , h_matching_transfer_encoding_chunked - , h_matching_connection_keep_alive - , h_matching_connection_close + switch (ch) { + case '?': + /* allow extra '?' in query string */ + return s_req_query_string; - , h_transfer_encoding_chunked - , h_connection_keep_alive - , h_connection_close - }; + case '#': + return s_req_fragment_start; + } -enum http_host_state - { - s_http_host_dead = 1 - , s_http_userinfo_start - , s_http_userinfo - , s_http_host_start - , s_http_host_v6_start - , s_http_host - , s_http_host_v6 - , s_http_host_v6_end - , s_http_host_port_start - , s_http_host_port -}; + break; -/* Macros for character classes; depends on strict-mode */ -#define CROW_CR '\r' -#define CROW_LF '\n' -#define CROW_LOWER(c) (unsigned char)(c | 0x20) -#define CROW_IS_ALPHA(c) (CROW_LOWER(c) >= 'a' && CROW_LOWER(c) <= 'z') -#define CROW_IS_NUM(c) ((c) >= '0' && (c) <= '9') -#define CROW_IS_ALPHANUM(c) (CROW_IS_ALPHA(c) || CROW_IS_NUM(c)) -#define CROW_IS_HEX(c) (CROW_IS_NUM(c) || (CROW_LOWER(c) >= 'a' && CROW_LOWER(c) <= 'f')) -#define CROW_IS_MARK(c) ((c) == '-' || (c) == '_' || (c) == '.' || \ - (c) == '!' || (c) == '~' || (c) == '*' || (c) == '\'' || (c) == '(' || \ - (c) == ')') -#define CROW_IS_USERINFO_CHAR(c) (CROW_IS_ALPHANUM(c) || CROW_IS_MARK(c) || (c) == '%' || \ - (c) == ';' || (c) == ':' || (c) == '&' || (c) == '=' || (c) == '+' || \ - (c) == '$' || (c) == ',') + case s_req_fragment_start: + if (CROW_IS_URL_CHAR(ch)) { + return s_req_fragment; + } -#if CROW_HTTP_PARSER_STRICT -#define CROW_TOKEN(c) (tokens[(unsigned char)c]) -#define CROW_IS_URL_CHAR(c) (CROW_BIT_AT(normal_url_char, (unsigned char)c)) -#define CROW_IS_HOST_CHAR(c) (CROW_IS_ALPHANUM(c) || (c) == '.' || (c) == '-') -#else -#define CROW_TOKEN(c) ((c == ' ') ? ' ' : tokens[(unsigned char)c]) -#define CROW_IS_URL_CHAR(c) \ - (CROW_BIT_AT(normal_url_char, (unsigned char)c) || ((c) & 0x80)) -#define CROW_IS_HOST_CHAR(c) \ - (CROW_IS_ALPHANUM(c) || (c) == '.' || (c) == '-' || (c) == '_') -#endif + switch (ch) { + case '?': + return s_req_fragment; + case '#': + return s; + } -#define CROW_start_state (parser->type == HTTP_REQUEST ? s_start_req : s_start_res) + break; + case s_req_fragment: + if (CROW_IS_URL_CHAR(ch)) { + return s; + } -#if CROW_HTTP_PARSER_STRICT -# define CROW_STRICT_CHECK(cond) \ -do { \ - if (cond) { \ - CROW_SET_ERRNO(HPE_STRICT); \ - goto error; \ - } \ -} while (0) -# define CROW_NEW_MESSAGE() (http_should_keep_alive(parser) ? CROW_start_state : s_dead) -#else -# define CROW_STRICT_CHECK(cond) -# define CROW_NEW_MESSAGE() CROW_start_state -#endif + switch (ch) { + case '?': + case '#': + return s; + } + break; + default: + break; + } -int http_message_needs_eof(const http_parser *parser); + /* We should never fall out of the switch above unless there's an error */ + return s_dead; +} -/* Our URL parser. - * - * This is designed to be shared by http_parser_execute() for URL validation, - * hence it has a state transition + byte-for-byte interface. In addition, it - * is meant to be embedded in http_parser_parse_url(), which does the dirty - * work of turning state transitions URL components for its API. - * - * This function should only be invoked with non-space characters. It is - * assumed that the caller cares about (and can detect) the transition between - * URL and non-URL states by looking for these. - */ -inline enum state -parse_url_char(enum state s, const char ch) +inline size_t http_parser_execute (http_parser *parser, + const http_parser_settings *settings, + const char *data, + size_t len) { -#if CROW_HTTP_PARSER_STRICT -# define CROW_T(v) 0 -#else -# define CROW_T(v) v -#endif - +static const char *method_strings[] = + { +#define CROW_XX(num, name, string) #string, + CROW_HTTP_METHOD_MAP(CROW_XX) +#undef CROW_XX + }; -static const uint8_t normal_url_char[32] = { +/* Tokens as defined by rfc 2616. Also lowercases them. + * token = 1* + * separators = "(" | ")" | "<" | ">" | "@" + * | "," | ";" | ":" | "\" | <"> + * | "/" | "[" | "]" | "?" | "=" + * | "{" | "}" | SP | HT + */ +static const char tokens[256] = { /* 0 nul 1 soh 2 stx 3 etx 4 eot 5 enq 6 ack 7 bel */ - 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0, + 0, 0, 0, 0, 0, 0, 0, 0, /* 8 bs 9 ht 10 nl 11 vt 12 np 13 cr 14 so 15 si */ - 0 | CROW_T(2) | 0 | 0 | CROW_T(16) | 0 | 0 | 0, + 0, 0, 0, 0, 0, 0, 0, 0, /* 16 dle 17 dc1 18 dc2 19 dc3 20 dc4 21 nak 22 syn 23 etb */ - 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0, + 0, 0, 0, 0, 0, 0, 0, 0, /* 24 can 25 em 26 sub 27 esc 28 fs 29 gs 30 rs 31 us */ - 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0, + 0, 0, 0, 0, 0, 0, 0, 0, /* 32 sp 33 ! 34 " 35 # 36 $ 37 % 38 & 39 ' */ - 0 | 2 | 4 | 0 | 16 | 32 | 64 | 128, + 0, '!', 0, '#', '$', '%', '&', '\'', /* 40 ( 41 ) 42 * 43 + 44 , 45 - 46 . 47 / */ - 1 | 2 | 4 | 8 | 16 | 32 | 64 | 128, + 0, 0, '*', '+', 0, '-', '.', 0, /* 48 0 49 1 50 2 51 3 52 4 53 5 54 6 55 7 */ - 1 | 2 | 4 | 8 | 16 | 32 | 64 | 128, -/* 56 8 57 9 58 : 59 ; 60 < 61 = 62 > 63 ? */ - 1 | 2 | 4 | 8 | 16 | 32 | 64 | 0, -/* 64 @ 65 A 66 B 67 C 68 D 69 E 70 F 71 G */ - 1 | 2 | 4 | 8 | 16 | 32 | 64 | 128, -/* 72 H 73 I 74 J 75 K 76 L 77 M 78 N 79 O */ - 1 | 2 | 4 | 8 | 16 | 32 | 64 | 128, -/* 80 P 81 Q 82 R 83 S 84 CROW_T 85 U 86 V 87 W */ - 1 | 2 | 4 | 8 | 16 | 32 | 64 | 128, -/* 88 X 89 Y 90 Z 91 [ 92 \ 93 ] 94 ^ 95 _ */ - 1 | 2 | 4 | 8 | 16 | 32 | 64 | 128, -/* 96 ` 97 a 98 b 99 c 100 d 101 e 102 f 103 g */ - 1 | 2 | 4 | 8 | 16 | 32 | 64 | 128, -/* 104 h 105 i 106 j 107 k 108 l 109 m 110 n 111 o */ - 1 | 2 | 4 | 8 | 16 | 32 | 64 | 128, -/* 112 p 113 q 114 r 115 s 116 t 117 u 118 v 119 w */ - 1 | 2 | 4 | 8 | 16 | 32 | 64 | 128, -/* 120 x 121 y 122 z 123 { 124 | 125 } 126 ~ 127 del */ - 1 | 2 | 4 | 8 | 16 | 32 | 64 | 0, }; - -#undef CROW_T - - if (ch == ' ' || ch == '\r' || ch == '\n') { - return s_dead; - } - -#if CROW_HTTP_PARSER_STRICT - if (ch == '\t' || ch == '\f') { - return s_dead; - } -#endif - - switch (s) { - case s_req_spaces_before_url: - /* Proxied requests are followed by scheme of an absolute URI (alpha). - * All methods except CONNECT are followed by '/' or '*'. - */ - - if (ch == '/' || ch == '*') { - return s_req_path; - } - - if (CROW_IS_ALPHA(ch)) { - return s_req_schema; - } - - break; - - case s_req_schema: - if (CROW_IS_ALPHA(ch)) { - return s; - } - - if (ch == ':') { - return s_req_schema_slash; - } - - break; - - case s_req_schema_slash: - if (ch == '/') { - return s_req_schema_slash_slash; - } - - break; - - case s_req_schema_slash_slash: - if (ch == '/') { - return s_req_server_start; - } - - break; - - case s_req_server_with_at: - if (ch == '@') { - return s_dead; - } - - /* FALLTHROUGH */ - case s_req_server_start: - case s_req_server: - if (ch == '/') { - return s_req_path; - } - - if (ch == '?') { - return s_req_query_string_start; - } - - if (ch == '@') { - return s_req_server_with_at; - } - - if (CROW_IS_USERINFO_CHAR(ch) || ch == '[' || ch == ']') { - return s_req_server; - } - - break; - - case s_req_path: - if (CROW_IS_URL_CHAR(ch)) { - return s; - } - - switch (ch) { - case '?': - return s_req_query_string_start; - - case '#': - return s_req_fragment_start; - } - - break; - - case s_req_query_string_start: - case s_req_query_string: - if (CROW_IS_URL_CHAR(ch)) { - return s_req_query_string; - } - - switch (ch) { - case '?': - /* allow extra '?' in query string */ - return s_req_query_string; - - case '#': - return s_req_fragment_start; - } - - break; - - case s_req_fragment_start: - if (CROW_IS_URL_CHAR(ch)) { - return s_req_fragment; - } - - switch (ch) { - case '?': - return s_req_fragment; - - case '#': - return s; - } - - break; - - case s_req_fragment: - if (CROW_IS_URL_CHAR(ch)) { - return s; - } - - switch (ch) { - case '?': - case '#': - return s; - } - - break; - - default: - break; - } - - /* We should never fall out of the switch above unless there's an error */ - return s_dead; -} - -inline size_t http_parser_execute (http_parser *parser, - const http_parser_settings *settings, - const char *data, - size_t len) -{ -static const char *method_strings[] = - { -#define CROW_XX(num, name, string) #string, - CROW_HTTP_METHOD_MAP(CROW_XX) -#undef CROW_XX - }; - -/* Tokens as defined by rfc 2616. Also lowercases them. - * token = 1* - * separators = "(" | ")" | "<" | ">" | "@" - * | "," | ";" | ":" | "\" | <"> - * | "/" | "[" | "]" | "?" | "=" - * | "{" | "}" | SP | HT - */ -static const char tokens[256] = { -/* 0 nul 1 soh 2 stx 3 etx 4 eot 5 enq 6 ack 7 bel */ - 0, 0, 0, 0, 0, 0, 0, 0, -/* 8 bs 9 ht 10 nl 11 vt 12 np 13 cr 14 so 15 si */ - 0, 0, 0, 0, 0, 0, 0, 0, -/* 16 dle 17 dc1 18 dc2 19 dc3 20 dc4 21 nak 22 syn 23 etb */ - 0, 0, 0, 0, 0, 0, 0, 0, -/* 24 can 25 em 26 sub 27 esc 28 fs 29 gs 30 rs 31 us */ - 0, 0, 0, 0, 0, 0, 0, 0, -/* 32 sp 33 ! 34 " 35 # 36 $ 37 % 38 & 39 ' */ - 0, '!', 0, '#', '$', '%', '&', '\'', -/* 40 ( 41 ) 42 * 43 + 44 , 45 - 46 . 47 / */ - 0, 0, '*', '+', 0, '-', '.', 0, -/* 48 0 49 1 50 2 51 3 52 4 53 5 54 6 55 7 */ - '0', '1', '2', '3', '4', '5', '6', '7', + '0', '1', '2', '3', '4', '5', '6', '7', /* 56 8 57 9 58 : 59 ; 60 < 61 = 62 > 63 ? */ '8', '9', 0, 0, 0, 0, 0, 0, /* 64 @ 65 A 66 B 67 C 68 D 69 E 70 F 71 G */ @@ -3023,205 +2678,6 @@ namespace crow -/* - * - * TinySHA1 - a header only implementation of the SHA1 algorithm in C++. Based - * on the implementation in boost::uuid::details. - * - * SHA1 Wikipedia Page: http://en.wikipedia.org/wiki/SHA-1 - * - * Copyright (c) 2012-22 SAURAV MOHAPATRA - * - * Permission to use, copy, modify, and distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ -#ifndef _TINY_SHA1_HPP_ -#define _TINY_SHA1_HPP_ -#include -#include -#include -#include -namespace sha1 -{ - class SHA1 - { - public: - typedef uint32_t digest32_t[5]; - typedef uint8_t digest8_t[20]; - inline static uint32_t LeftRotate(uint32_t value, size_t count) { - return (value << count) ^ (value >> (32-count)); - } - SHA1(){ reset(); } - virtual ~SHA1() {} - SHA1(const SHA1& s) { *this = s; } - const SHA1& operator = (const SHA1& s) { - memcpy(m_digest, s.m_digest, 5 * sizeof(uint32_t)); - memcpy(m_block, s.m_block, 64); - m_blockByteIndex = s.m_blockByteIndex; - m_byteCount = s.m_byteCount; - return *this; - } - SHA1& reset() { - m_digest[0] = 0x67452301; - m_digest[1] = 0xEFCDAB89; - m_digest[2] = 0x98BADCFE; - m_digest[3] = 0x10325476; - m_digest[4] = 0xC3D2E1F0; - m_blockByteIndex = 0; - m_byteCount = 0; - return *this; - } - SHA1& processByte(uint8_t octet) { - this->m_block[this->m_blockByteIndex++] = octet; - ++this->m_byteCount; - if(m_blockByteIndex == 64) { - this->m_blockByteIndex = 0; - processBlock(); - } - return *this; - } - SHA1& processBlock(const void* const start, const void* const end) { - const uint8_t* begin = static_cast(start); - const uint8_t* finish = static_cast(end); - while(begin != finish) { - processByte(*begin); - begin++; - } - return *this; - } - SHA1& processBytes(const void* const data, size_t len) { - const uint8_t* block = static_cast(data); - processBlock(block, block + len); - return *this; - } - const uint32_t* getDigest(digest32_t digest) { - size_t bitCount = this->m_byteCount * 8; - processByte(0x80); - if (this->m_blockByteIndex > 56) { - while (m_blockByteIndex != 0) { - processByte(0); - } - while (m_blockByteIndex < 56) { - processByte(0); - } - } else { - while (m_blockByteIndex < 56) { - processByte(0); - } - } - processByte(0); - processByte(0); - processByte(0); - processByte(0); - processByte( static_cast((bitCount>>24) & 0xFF)); - processByte( static_cast((bitCount>>16) & 0xFF)); - processByte( static_cast((bitCount>>8 ) & 0xFF)); - processByte( static_cast((bitCount) & 0xFF)); - - memcpy(digest, m_digest, 5 * sizeof(uint32_t)); - return digest; - } - const uint8_t* getDigestBytes(digest8_t digest) { - digest32_t d32; - getDigest(d32); - size_t di = 0; - digest[di++] = ((d32[0] >> 24) & 0xFF); - digest[di++] = ((d32[0] >> 16) & 0xFF); - digest[di++] = ((d32[0] >> 8) & 0xFF); - digest[di++] = ((d32[0]) & 0xFF); - - digest[di++] = ((d32[1] >> 24) & 0xFF); - digest[di++] = ((d32[1] >> 16) & 0xFF); - digest[di++] = ((d32[1] >> 8) & 0xFF); - digest[di++] = ((d32[1]) & 0xFF); - - digest[di++] = ((d32[2] >> 24) & 0xFF); - digest[di++] = ((d32[2] >> 16) & 0xFF); - digest[di++] = ((d32[2] >> 8) & 0xFF); - digest[di++] = ((d32[2]) & 0xFF); - - digest[di++] = ((d32[3] >> 24) & 0xFF); - digest[di++] = ((d32[3] >> 16) & 0xFF); - digest[di++] = ((d32[3] >> 8) & 0xFF); - digest[di++] = ((d32[3]) & 0xFF); - - digest[di++] = ((d32[4] >> 24) & 0xFF); - digest[di++] = ((d32[4] >> 16) & 0xFF); - digest[di++] = ((d32[4] >> 8) & 0xFF); - digest[di++] = ((d32[4]) & 0xFF); - return digest; - } - - protected: - void processBlock() { - uint32_t w[80]; - for (size_t i = 0; i < 16; i++) { - w[i] = (m_block[i*4 + 0] << 24); - w[i] |= (m_block[i*4 + 1] << 16); - w[i] |= (m_block[i*4 + 2] << 8); - w[i] |= (m_block[i*4 + 3]); - } - for (size_t i = 16; i < 80; i++) { - w[i] = LeftRotate((w[i-3] ^ w[i-8] ^ w[i-14] ^ w[i-16]), 1); - } - - uint32_t a = m_digest[0]; - uint32_t b = m_digest[1]; - uint32_t c = m_digest[2]; - uint32_t d = m_digest[3]; - uint32_t e = m_digest[4]; - - for (std::size_t i=0; i<80; ++i) { - uint32_t f = 0; - uint32_t k = 0; - - if (i<20) { - f = (b & c) | (~b & d); - k = 0x5A827999; - } else if (i<40) { - f = b ^ c ^ d; - k = 0x6ED9EBA1; - } else if (i<60) { - f = (b & c) | (b & d) | (c & d); - k = 0x8F1BBCDC; - } else { - f = b ^ c ^ d; - k = 0xCA62C1D6; - } - uint32_t temp = LeftRotate(a, 5) + f + e + k + w[i]; - e = d; - d = c; - c = LeftRotate(b, 30); - b = a; - a = temp; - } - - m_digest[0] += a; - m_digest[1] += b; - m_digest[2] += c; - m_digest[3] += d; - m_digest[4] += e; - } - private: - digest32_t m_digest; - uint8_t m_block[64]; - size_t m_blockByteIndex; - size_t m_byteCount; - }; -} -#endif - - - #pragma once // settings for crow // TODO - replace with runtime config. libucl? @@ -3264,2919 +2720,3466 @@ namespace sha1 #pragma once -#include -#ifdef CROW_ENABLE_SSL -#include -#endif + +#include +#include +#include +#include +#include +#include +#include + + namespace crow { - using namespace boost; - using tcp = asio::ip::tcp; - - struct SocketAdaptor + namespace black_magic { - using context = void; - SocketAdaptor(boost::asio::io_service& io_service, context*) - : socket_(io_service) +#ifndef CROW_MSVC_WORKAROUND + struct OutOfRange + { + OutOfRange(unsigned /*pos*/, unsigned /*length*/) {} + }; + constexpr unsigned requires_in_range( unsigned i, unsigned len ) { + return i >= len ? throw OutOfRange(i, len) : i; } - boost::asio::io_service& get_io_service() + class const_str { - return socket_.get_io_service(); - } + const char * const begin_; + unsigned size_; - tcp::socket& raw_socket() + public: + template< unsigned N > + constexpr const_str( const char(&arr)[N] ) : begin_(arr), size_(N - 1) { + static_assert( N >= 1, "not a string literal"); + } + constexpr char operator[]( unsigned i ) const { + return requires_in_range(i, size_), begin_[i]; + } + + constexpr operator const char *() const { + return begin_; + } + + constexpr const char* begin() const { return begin_; } + constexpr const char* end() const { return begin_ + size_; } + + constexpr unsigned size() const { + return size_; + } + }; + + constexpr unsigned find_closing_tag(const_str s, unsigned p) { - return socket_; + return s[p] == '>' ? p : find_closing_tag(s, p+1); } - tcp::socket& socket() + constexpr bool is_valid(const_str s, unsigned i = 0, int f = 0) { - return socket_; + return + i == s.size() + ? f == 0 : + f < 0 || f >= 2 + ? false : + s[i] == '<' + ? is_valid(s, i+1, f+1) : + s[i] == '>' + ? is_valid(s, i+1, f-1) : + is_valid(s, i+1, f); } - tcp::endpoint remote_endpoint() + constexpr bool is_equ_p(const char* a, const char* b, unsigned n) { - return socket_.remote_endpoint(); + return + *a == 0 && *b == 0 && n == 0 + ? true : + (*a == 0 || *b == 0) + ? false : + n == 0 + ? true : + *a != *b + ? false : + is_equ_p(a+1, b+1, n-1); } - bool is_open() + constexpr bool is_equ_n(const_str a, unsigned ai, const_str b, unsigned bi, unsigned n) { - return socket_.is_open(); + return + ai + n > a.size() || bi + n > b.size() + ? false : + n == 0 + ? true : + a[ai] != b[bi] + ? false : + is_equ_n(a,ai+1,b,bi+1,n-1); } - void close() + constexpr bool is_int(const_str s, unsigned i) { - socket_.close(); + return is_equ_n(s, i, "", 0, 5); } - template - void start(F f) + constexpr bool is_uint(const_str s, unsigned i) { - f(boost::system::error_code()); + return is_equ_n(s, i, "", 0, 6); } - tcp::socket socket_; - }; + constexpr bool is_float(const_str s, unsigned i) + { + return is_equ_n(s, i, "", 0, 7) || + is_equ_n(s, i, "", 0, 8); + } -#ifdef CROW_ENABLE_SSL - struct SSLAdaptor - { - using context = boost::asio::ssl::context; - using ssl_socket_t = boost::asio::ssl::stream; - SSLAdaptor(boost::asio::io_service& io_service, context* ctx) - : ssl_socket_(new ssl_socket_t(io_service, *ctx)) + constexpr bool is_str(const_str s, unsigned i) { + return is_equ_n(s, i, "", 0, 5) || + is_equ_n(s, i, "", 0, 8); } - boost::asio::ssl::stream& socket() + constexpr bool is_path(const_str s, unsigned i) { - return *ssl_socket_; + return is_equ_n(s, i, "", 0, 6); } +#endif + template + struct parameter_tag + { + static const int value = 0; + }; +#define CROW_INTERNAL_PARAMETER_TAG(t, i) \ +template <> \ +struct parameter_tag \ +{ \ + static const int value = i; \ +} + CROW_INTERNAL_PARAMETER_TAG(int, 1); + CROW_INTERNAL_PARAMETER_TAG(char, 1); + CROW_INTERNAL_PARAMETER_TAG(short, 1); + CROW_INTERNAL_PARAMETER_TAG(long, 1); + CROW_INTERNAL_PARAMETER_TAG(long long, 1); + CROW_INTERNAL_PARAMETER_TAG(unsigned int, 2); + CROW_INTERNAL_PARAMETER_TAG(unsigned char, 2); + CROW_INTERNAL_PARAMETER_TAG(unsigned short, 2); + CROW_INTERNAL_PARAMETER_TAG(unsigned long, 2); + CROW_INTERNAL_PARAMETER_TAG(unsigned long long, 2); + CROW_INTERNAL_PARAMETER_TAG(double, 3); + CROW_INTERNAL_PARAMETER_TAG(std::string, 4); +#undef CROW_INTERNAL_PARAMETER_TAG + template + struct compute_parameter_tag_from_args_list; - tcp::socket::lowest_layer_type& - raw_socket() + template <> + struct compute_parameter_tag_from_args_list<> { - return ssl_socket_->lowest_layer(); - } + static const int value = 0; + }; - tcp::endpoint remote_endpoint() + template + struct compute_parameter_tag_from_args_list { - return raw_socket().remote_endpoint(); - } + static const int sub_value = + compute_parameter_tag_from_args_list::value; + static const int value = + parameter_tag::type>::value + ? sub_value* 6 + parameter_tag::type>::value + : sub_value; + }; - bool is_open() + static inline bool is_parameter_tag_compatible(uint64_t a, uint64_t b) { - return raw_socket().is_open(); + if (a == 0) + return b == 0; + if (b == 0) + return a == 0; + int sa = a%6; + int sb = a%6; + if (sa == 5) sa = 4; + if (sb == 5) sb = 4; + if (sa != sb) + return false; + return is_parameter_tag_compatible(a/6, b/6); } - void close() + static inline unsigned find_closing_tag_runtime(const char* s, unsigned p) { - raw_socket().close(); + return + s[p] == 0 + ? throw std::runtime_error("unmatched tag <") : + s[p] == '>' + ? p : find_closing_tag_runtime(s, p + 1); } - - boost::asio::io_service& get_io_service() + + static inline uint64_t get_parameter_tag_runtime(const char* s, unsigned p = 0) { - return raw_socket().get_io_service(); + return + s[p] == 0 + ? 0 : + s[p] == '<' ? ( + std::strncmp(s+p, "", 5) == 0 + ? get_parameter_tag_runtime(s, find_closing_tag_runtime(s, p)) * 6 + 1 : + std::strncmp(s+p, "", 6) == 0 + ? get_parameter_tag_runtime(s, find_closing_tag_runtime(s, p)) * 6 + 2 : + (std::strncmp(s+p, "", 7) == 0 || + std::strncmp(s+p, "", 8) == 0) + ? get_parameter_tag_runtime(s, find_closing_tag_runtime(s, p)) * 6 + 3 : + (std::strncmp(s+p, "", 5) == 0 || + std::strncmp(s+p, "", 8) == 0) + ? get_parameter_tag_runtime(s, find_closing_tag_runtime(s, p)) * 6 + 4 : + std::strncmp(s+p, "", 6) == 0 + ? get_parameter_tag_runtime(s, find_closing_tag_runtime(s, p)) * 6 + 5 : + throw std::runtime_error("invalid parameter type") + ) : + get_parameter_tag_runtime(s, p+1); } - - template - void start(F f) +#ifndef CROW_MSVC_WORKAROUND + constexpr uint64_t get_parameter_tag(const_str s, unsigned p = 0) { - ssl_socket_->async_handshake(boost::asio::ssl::stream_base::server, - [f](const boost::system::error_code& ec) { - f(ec); - }); + return + p == s.size() + ? 0 : + s[p] == '<' ? ( + is_int(s, p) + ? get_parameter_tag(s, find_closing_tag(s, p)) * 6 + 1 : + is_uint(s, p) + ? get_parameter_tag(s, find_closing_tag(s, p)) * 6 + 2 : + is_float(s, p) + ? get_parameter_tag(s, find_closing_tag(s, p)) * 6 + 3 : + is_str(s, p) + ? get_parameter_tag(s, find_closing_tag(s, p)) * 6 + 4 : + is_path(s, p) + ? get_parameter_tag(s, find_closing_tag(s, p)) * 6 + 5 : + throw std::runtime_error("invalid parameter type") + ) : + get_parameter_tag(s, p+1); } - - std::unique_ptr> ssl_socket_; - }; #endif -} + template + struct S + { + template + using push = S; + template + using push_back = S; + template class U> + using rebind = U; + }; +template + struct CallHelper; + template + struct CallHelper> + { + template ()(std::declval()...)) + > + static char __test(int); + template + static int __test(...); -#pragma once + static constexpr bool value = sizeof(__test(0)) == sizeof(char); + }; -//#define CROW_JSON_NO_ERROR_CHECK -#include -#include -#include -#include -#include -#include -#include -#include -#include + template + struct single_tag_to_type + { + }; + template <> + struct single_tag_to_type<1> + { + using type = int64_t; + }; + template <> + struct single_tag_to_type<2> + { + using type = uint64_t; + }; + template <> + struct single_tag_to_type<3> + { + using type = double; + }; -#if defined(__GNUG__) || defined(__clang__) -#define crow_json_likely(x) __builtin_expect(x, 1) -#define crow_json_unlikely(x) __builtin_expect(x, 0) -#else -#define crow_json_likely(x) x -#define crow_json_unlikely(x) x -#endif + template <> + struct single_tag_to_type<4> + { + using type = std::string; + }; + template <> + struct single_tag_to_type<5> + { + using type = std::string; + }; -namespace crow -{ - namespace mustache - { - class template_t; - } - namespace json - { - inline void escape(const std::string& str, std::string& ret) - { - ret.reserve(ret.size() + str.size()+str.size()/4); - for(char c:str) - { - switch(c) - { - case '"': ret += "\\\""; break; - case '\\': ret += "\\\\"; break; - case '\n': ret += "\\n"; break; - case '\b': ret += "\\b"; break; - case '\f': ret += "\\f"; break; - case '\r': ret += "\\r"; break; - case '\t': ret += "\\t"; break; - default: - if (0 <= c && c < 0x20) - { - ret += "\\u00"; - auto to_hex = [](char c) - { - c = c&0xf; - if (c < 10) - return '0' + c; - return 'a'+c-10; - }; - ret += to_hex(c/16); - ret += to_hex(c%16); - } - else - ret += c; - break; - } - } - } - inline std::string escape(const std::string& str) + template + struct arguments { - std::string ret; - escape(str, ret); - return ret; - } - - enum class type : char + using subarguments = typename arguments::type; + using type = + typename subarguments::template push::type>; + }; + + template <> + struct arguments<0> { - Null, - False, - True, - Number, - String, - List, - Object, + using type = S<>; }; - inline const char* get_type_str(type t) { - switch(t){ - case type::Number: return "Number"; - case type::False: return "False"; - case type::True: return "True"; - case type::List: return "List"; - case type::String: return "String"; - case type::Object: return "Object"; - default: return "Unknown"; - } - } + template + struct last_element_type + { + using type = typename std::tuple_element>::type; + }; - class rvalue; - rvalue load(const char* data, size_t size); - namespace detail + template <> + struct last_element_type<> { + }; - struct r_string - : boost::less_than_comparable, - boost::less_than_comparable, - boost::equality_comparable, - boost::equality_comparable - { - r_string() {}; - r_string(char* s, char* e) - : s_(s), e_(e) - {}; - ~r_string() - { - if (owned_) - delete[] s_; - } - r_string(const r_string& r) - { - *this = r; - } + // from http://stackoverflow.com/questions/13072359/c11-compile-time-array-with-logarithmic-evaluation-depth + template using Invoke = typename T::type; - r_string(r_string&& r) - { - *this = r; - } + template struct seq{ using type = seq; }; - r_string& operator = (r_string&& r) - { - s_ = r.s_; - e_ = r.e_; - owned_ = r.owned_; - if (r.owned_) - r.owned_ = 0; - return *this; - } + template struct concat; - r_string& operator = (const r_string& r) - { - s_ = r.s_; - e_ = r.e_; - owned_ = 0; - return *this; - } + template + struct concat, seq> + : seq{}; - operator std::string () const - { - return std::string(s_, e_); - } + template + using Concat = Invoke>; + template struct gen_seq; + template using GenSeq = Invoke>; - const char* begin() const { return s_; } - const char* end() const { return e_; } - size_t size() const { return end() - begin(); } + template + struct gen_seq : Concat, GenSeq>{}; - using iterator = const char*; - using const_iterator = const char*; + template<> struct gen_seq<0> : seq<>{}; + template<> struct gen_seq<1> : seq<0>{}; - char* s_; - mutable char* e_; - uint8_t owned_{0}; - friend std::ostream& operator << (std::ostream& os, const r_string& s) - { - os << (std::string)s; - return os; - } - private: - void force(char* s, uint32_t /*length*/) - { - s_ = s; - owned_ = 1; - } - friend rvalue crow::json::load(const char* data, size_t size); - }; + template + struct pop_back_helper; - inline bool operator < (const r_string& l, const r_string& r) - { - return boost::lexicographical_compare(l,r); - } + template + struct pop_back_helper, Tuple> + { + template