├── .gitignore ├── .gitmodules ├── .travis.yml ├── CMakeLists.txt ├── LICENSE ├── README.md ├── amalgamate └── merge_all.py ├── cmake └── FindTcmalloc.cmake ├── conanfile.py ├── examples ├── CMakeLists.txt ├── example.cpp ├── example.py ├── example_chat.cpp ├── example_chat.html ├── example_test.py ├── example_vs.cpp ├── example_with_all.cpp ├── helloworld.cpp ├── ssl │ └── example_ssl.cpp └── websocket │ ├── example_ws.cpp │ └── templates │ └── ws.html ├── include ├── crow.h └── crow │ ├── TinySHA1.hpp │ ├── app.h │ ├── ci_map.h │ ├── common.h │ ├── dumb_timer_queue.h │ ├── http_connection.h │ ├── http_parser_merged.h │ ├── http_request.h │ ├── http_response.h │ ├── http_server.h │ ├── json.h │ ├── logging.h │ ├── middleware.h │ ├── middleware_context.h │ ├── mustache.h │ ├── parser.h │ ├── query_string.h │ ├── routing.h │ ├── settings.h │ ├── socket_adaptors.h │ ├── utility.h │ └── websocket.h └── tests ├── CMakeLists.txt ├── template ├── CMakeLists.txt ├── Makefile ├── README.template_test ├── comments.json ├── comments.yml ├── delimiters.json ├── delimiters.yml ├── interpolation.json ├── interpolation.yml ├── inverted.json ├── inverted.yml ├── mustachetest.cpp ├── partials.json ├── partials.yml ├── sections.json ├── sections.yml ├── test.py ├── ~lambdas.json └── ~lambdas.yml └── unittest.cpp /.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled Object files 2 | *.slo 3 | *.lo 4 | *.o 5 | *.obj 6 | 7 | # Compiled Dynamic libraries 8 | *.so 9 | *.dylib 10 | *.dll 11 | 12 | # Compiled Static libraries 13 | *.lai 14 | *.la 15 | *.a 16 | *.lib 17 | 18 | # Executables 19 | *.exe 20 | *.out 21 | *.app 22 | 23 | example 24 | unittest 25 | 26 | *.swp 27 | *.gcov 28 | 29 | *.gcda 30 | *.gcno 31 | 32 | build 33 | 34 | .directory 35 | crow_all.h 36 | 37 | # conan.io 38 | build/ 39 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ipkn/crow/2b43d3cd6a9a9cdbc99dfef9b86ff3f3027f3d1f/.gitmodules -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: cpp 2 | 3 | sudo: false 4 | cache: ccache 5 | 6 | notifications: 7 | irc: "chat.freenode.net##crow" 8 | 9 | compiler: 10 | - gcc 11 | 12 | env: 13 | matrix: 14 | - COMPILER=g++-4.8 CCOMPILER=gcc-4.8 PUSH_COVERAGE=ON 15 | - COMPILER=g++-4.9 CCOMPILER=gcc-4.9 16 | - COMPILER=g++-5 CCOMPILER=gcc-5 17 | - COMPILER=clang++-3.6 CCOMPILER=clang-3.6 18 | 19 | addons: 20 | apt: 21 | sources: 22 | - ubuntu-toolchain-r-test 23 | - boost-latest 24 | - llvm-toolchain-precise 25 | - llvm-toolchain-precise-3.6 26 | packages: 27 | - g++-4.8 28 | - g++-4.9 29 | - g++-5 30 | - clang-3.6 31 | - libboost1.55-all-dev 32 | - python-pip 33 | 34 | install: 35 | - if [ "$PUSH_COVERAGE" == "ON" ]; then pip install --user git+git://github.com/eddyxu/cpp-coveralls.git; fi 36 | 37 | before_script: 38 | - export CXX=$COMPILER CC=$CCOMPILER 39 | - mkdir build 40 | - cd build 41 | - cmake --version 42 | - cmake .. 43 | 44 | script: make -j2 && ctest -j2 45 | 46 | after_success: 47 | - cd .. 48 | - if [ "$PUSH_COVERAGE" == "ON" ]; then coveralls --gcov gcov-4.8 -i include --gcov-options '\-lp'; fi 49 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 2.8) 2 | project (crow_all) 3 | 4 | if(EXISTS "${CMAKE_BINARY_DIR}/conanbuildinfo.cmake") 5 | include(${CMAKE_BINARY_DIR}/conanbuildinfo.cmake) 6 | conan_basic_setup() 7 | endif() 8 | 9 | set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/cmake/") 10 | 11 | find_package(Tcmalloc) 12 | find_package(Threads) 13 | find_package(OpenSSL) 14 | 15 | if(OPENSSL_FOUND) 16 | include_directories(${OPENSSL_INCLUDE_DIR}) 17 | endif() 18 | 19 | find_program(CCACHE_FOUND ccache) 20 | if(CCACHE_FOUND) 21 | message("Found ccache ${CCACHE_FOUND}") 22 | message("Using ccache to speed up compilation") 23 | set(ENV{CCACHE_CPP2} "yes") 24 | set_property(GLOBAL PROPERTY RULE_LAUNCH_COMPILE ccache) 25 | set_property(GLOBAL PROPERTY RULE_LAUNCH_LINK ccache) 26 | endif(CCACHE_FOUND) 27 | 28 | if (NOT CMAKE_BUILD_TYPE) 29 | message(STATUS "No build type selected, default to Release") 30 | set(CMAKE_BUILD_TYPE "Release") 31 | endif() 32 | 33 | if (MSVC) 34 | set(Boost_USE_STATIC_LIBS "On") 35 | find_package( Boost 1.52 COMPONENTS system thread regex REQUIRED ) 36 | else() 37 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -std=c++1y -pedantic -Wextra") 38 | find_package( Boost 1.52 COMPONENTS system thread REQUIRED ) 39 | endif() 40 | 41 | include_directories( ${Boost_INCLUDE_DIR} ) 42 | 43 | set(PROJECT_INCLUDE_DIR 44 | ${PROJECT_SOURCE_DIR}/include 45 | ) 46 | 47 | include_directories("${PROJECT_INCLUDE_DIR}") 48 | include_directories("${PROJECT_SOURCE_DIR}") 49 | 50 | #add_subdirectory(src) 51 | add_subdirectory(examples) 52 | 53 | if (MSVC) 54 | else() 55 | add_subdirectory(tests) 56 | 57 | enable_testing() 58 | add_test(NAME crow_test COMMAND ${CMAKE_CURRENT_BINARY_DIR}/tests/unittest) 59 | add_test(NAME template_test COMMAND ${CMAKE_CURRENT_BINARY_DIR}/tests/template/test.py WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/tests/template) 60 | 61 | file(MAKE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/amalgamate) 62 | 63 | add_custom_command(OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/amalgamate/crow_all.h 64 | COMMAND python ${PROJECT_SOURCE_DIR}/amalgamate/merge_all.py ${PROJECT_SOURCE_DIR}/include 65 | COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_CURRENT_BINARY_DIR}/amalgamate/crow_all.h ${PROJECT_SOURCE_DIR}/amalgamate 66 | WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/amalgamate 67 | DEPENDS ${PROJECT_SOURCE_DIR}/include/*.h ${PROJECT_SOURCE_DIR}/include/crow/*.h 68 | ) 69 | 70 | add_custom_target(amalgamation ALL DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/amalgamate/crow_all.h) 71 | endif() 72 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2014, ipkn 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are met: 6 | 7 | * Redistributions of source code must retain the above copyright notice, this 8 | list of conditions and the following disclaimer. 9 | 10 | * Redistributions in binary form must reproduce the above copyright notice, 11 | this list of conditions and the following disclaimer in the documentation 12 | and/or other materials provided with the distribution. 13 | 14 | * Neither the name of the author nor the names of its 15 | contributors may be used to endorse or promote products derived from 16 | this software without specific prior written permission. 17 | 18 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 19 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 21 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 22 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 23 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 24 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 25 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 26 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 27 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # IMPORTANT NOTICE 2 | 3 | NOT MAINTAINED ANYMORE. 4 | 5 | Working fork is here: [CrowCpp/Crow](https://github.com/CrowCpp/Crow) 6 | 7 | 8 | 9 | ![Crow logo](http://i.imgur.com/wqivvjK.jpg) 10 | 11 | Crow is C++ microframework for web. (inspired by Python Flask) 12 | 13 | [![Travis Build](https://travis-ci.org/ipkn/crow.svg?branch=master)](https://travis-ci.org/ipkn/crow) 14 | [![Coverage Status](https://coveralls.io/repos/ipkn/crow/badge.svg?branch=master)](https://coveralls.io/r/ipkn/crow?branch=master) 15 | 16 | ```c++ 17 | #include "crow.h" 18 | 19 | int main() 20 | { 21 | crow::SimpleApp app; 22 | 23 | CROW_ROUTE(app, "/")([](){ 24 | return "Hello world"; 25 | }); 26 | 27 | app.port(18080).multithreaded().run(); 28 | } 29 | ``` 30 | 31 | ## Features 32 | 33 | - Easy routing 34 | - Similiar to Flask 35 | - Type-safe Handlers (see Example) 36 | - Very Fast 37 | - ![Benchmark Result in one chart](https://docs.google.com/spreadsheets/d/1KidO9XpuwCRZ2p_JRDJj2aep61H8Sh_KDOhApizv4LE/pubchart?oid=2041467789&format=image) 38 | - More data on [crow-benchmark](https://github.com/ipkn/crow-benchmark) 39 | - Fast built-in JSON parser (crow::json) 40 | - You can also use [json11](https://github.com/dropbox/json11) or [rapidjson](https://github.com/miloyip/rapidjson) for better speed or readability 41 | - [Mustache](http://mustache.github.io/) based templating library (crow::mustache) 42 | - Header only 43 | - Provide an amalgamated header file [`crow_all.h`](https://github.com/ipkn/crow/releases/download/v0.1/crow_all.h) with every features ([Download from here](https://github.com/ipkn/crow/releases/download/v0.1/crow_all.h)) 44 | - Middleware support 45 | - Websocket support 46 | 47 | ## Still in development 48 | - ~~Built-in ORM~~ 49 | - Check [sqlpp11](https://github.com/rbock/sqlpp11) if you want one. 50 | 51 | ## Examples 52 | 53 | #### JSON Response 54 | ```c++ 55 | CROW_ROUTE(app, "/json") 56 | ([]{ 57 | crow::json::wvalue x; 58 | x["message"] = "Hello, World!"; 59 | return x; 60 | }); 61 | ``` 62 | 63 | #### Arguments 64 | ```c++ 65 | CROW_ROUTE(app,"/hello/") 66 | ([](int count){ 67 | if (count > 100) 68 | return crow::response(400); 69 | std::ostringstream os; 70 | os << count << " bottles of beer!"; 71 | return crow::response(os.str()); 72 | }); 73 | ``` 74 | Handler arguments type check at compile time 75 | ```c++ 76 | // Compile error with message "Handler type is mismatched with URL paramters" 77 | CROW_ROUTE(app,"/another/") 78 | ([](int a, int b){ 79 | return crow::response(500); 80 | }); 81 | ``` 82 | 83 | #### Handling JSON Requests 84 | ```c++ 85 | CROW_ROUTE(app, "/add_json") 86 | .methods("POST"_method) 87 | ([](const crow::request& req){ 88 | auto x = crow::json::load(req.body); 89 | if (!x) 90 | return crow::response(400); 91 | int sum = x["a"].i()+x["b"].i(); 92 | std::ostringstream os; 93 | os << sum; 94 | return crow::response{os.str()}; 95 | }); 96 | ``` 97 | 98 | ## How to Build 99 | 100 | If you just want to use crow, copy amalgamate/crow_all.h and include it. 101 | 102 | ### Requirements 103 | 104 | - C++ compiler with good C++11 support (tested with g++>=4.8) 105 | - boost library 106 | - CMake for build examples 107 | - Linking with tcmalloc/jemalloc is recommended for speed. 108 | 109 | - Now supporting VS2013 with limited functionality (only run-time check for url is available.) 110 | 111 | ### Building (Tests, Examples) 112 | 113 | Out-of-source build with CMake is recommended. 114 | 115 | ``` 116 | mkdir build 117 | cd build 118 | cmake .. 119 | make 120 | ``` 121 | 122 | You can run tests with following commands: 123 | ``` 124 | ctest 125 | ``` 126 | 127 | 128 | ### Installing missing dependencies 129 | 130 | #### Ubuntu 131 | sudo apt-get install build-essential libtcmalloc-minimal4 && sudo ln -s /usr/lib/libtcmalloc_minimal.so.4 /usr/lib/libtcmalloc_minimal.so 132 | 133 | #### OSX 134 | brew install boost google-perftools 135 | 136 | ### Attributions 137 | 138 | Crow uses the following libraries. 139 | 140 | http-parser 141 | 142 | https://github.com/nodejs/http-parser 143 | 144 | http_parser.c is based on src/http/ngx_http_parse.c from NGINX copyright 145 | Igor Sysoev. 146 | 147 | Additional changes are licensed under the same terms as NGINX and 148 | copyright Joyent, Inc. and other Node contributors. All rights reserved. 149 | 150 | Permission is hereby granted, free of charge, to any person obtaining a copy 151 | of this software and associated documentation files (the "Software"), to 152 | deal in the Software without restriction, including without limitation the 153 | rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 154 | sell copies of the Software, and to permit persons to whom the Software is 155 | furnished to do so, subject to the following conditions: 156 | 157 | The above copyright notice and this permission notice shall be included in 158 | all copies or substantial portions of the Software. 159 | 160 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 161 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 162 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 163 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 164 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 165 | FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 166 | IN THE SOFTWARE. 167 | 168 | 169 | qs_parse 170 | 171 | https://github.com/bartgrantham/qs_parse 172 | 173 | Copyright (c) 2010 Bart Grantham 174 | Permission is hereby granted, free of charge, to any person obtaining a copy 175 | of this software and associated documentation files (the "Software"), to deal 176 | in the Software without restriction, including without limitation the rights 177 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 178 | copies of the Software, and to permit persons to whom the Software is 179 | furnished to do so, subject to the following conditions: 180 | The above copyright notice and this permission notice shall be included in 181 | all copies or substantial portions of the Software. 182 | 183 | 184 | TinySHA1 185 | 186 | https://github.com/mohaps/TinySHA1 187 | 188 | TinySHA1 - a header only implementation of the SHA1 algorithm. Based on the implementation in boost::uuid::details 189 | 190 | Copyright (c) 2012-22 SAURAV MOHAPATRA mohaps@gmail.com 191 | 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. 192 | 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. 193 | -------------------------------------------------------------------------------- /amalgamate/merge_all.py: -------------------------------------------------------------------------------- 1 | """Merges all the header files.""" 2 | from glob import glob 3 | from os import path as pt 4 | import re 5 | from collections import defaultdict 6 | import sys 7 | 8 | header_path = "../include" 9 | if len(sys.argv) > 1: 10 | header_path = sys.argv[1] 11 | 12 | OUTPUT = 'crow_all.h' 13 | re_depends = re.compile('^#include "(.*)"', re.MULTILINE) 14 | headers = [x.rsplit('/', 1)[-1] for x in glob(pt.join(header_path, '*.h*'))] 15 | headers += ['crow/' + x.rsplit('/', 1)[-1] for x in glob(pt.join(header_path, 'crow/*.h*'))] 16 | print(headers) 17 | edges = defaultdict(list) 18 | for header in headers: 19 | d = open(pt.join(header_path, header)).read() 20 | match = re_depends.findall(d) 21 | for m in match: 22 | # m should included before header 23 | edges[m].append(header) 24 | 25 | visited = defaultdict(bool) 26 | order = [] 27 | 28 | 29 | def dfs(x): 30 | """Ensure all header files are visited.""" 31 | visited[x] = True 32 | for y in edges[x]: 33 | if not visited[y]: 34 | dfs(y) 35 | order.append(x) 36 | 37 | for header in headers: 38 | if not visited[header]: 39 | dfs(header) 40 | 41 | order = order[::-1] 42 | for x in edges: 43 | print(x, edges[x]) 44 | for x in edges: 45 | for y in edges[x]: 46 | assert order.index(x) < order.index(y), 'cyclic include detected' 47 | 48 | print(order) 49 | build = [] 50 | for header in order: 51 | d = open(pt.join(header_path, header)).read() 52 | build.append(re_depends.sub(lambda x: '\n', d)) 53 | build.append('\n') 54 | 55 | open(OUTPUT, 'w').write('\n'.join(build)) 56 | -------------------------------------------------------------------------------- /cmake/FindTcmalloc.cmake: -------------------------------------------------------------------------------- 1 | # - Find Tcmalloc 2 | # Find the native Tcmalloc library 3 | # 4 | # Tcmalloc_LIBRARIES - List of libraries when using Tcmalloc. 5 | # Tcmalloc_FOUND - True if Tcmalloc found. 6 | 7 | if (USE_TCMALLOC) 8 | set(Tcmalloc_NAMES tcmalloc) 9 | else () 10 | set(Tcmalloc_NAMES tcmalloc_minimal tcmalloc) 11 | endif () 12 | 13 | find_library(Tcmalloc_LIBRARY NO_DEFAULT_PATH 14 | NAMES ${Tcmalloc_NAMES} 15 | PATHS ${HT_DEPENDENCY_LIB_DIR} /lib /usr/lib /usr/local/lib /opt/local/lib 16 | ) 17 | 18 | if (Tcmalloc_LIBRARY) 19 | set(Tcmalloc_FOUND TRUE) 20 | set( Tcmalloc_LIBRARIES ${Tcmalloc_LIBRARY} ) 21 | else () 22 | set(Tcmalloc_FOUND FALSE) 23 | set( Tcmalloc_LIBRARIES ) 24 | endif () 25 | 26 | if (Tcmalloc_FOUND) 27 | message(STATUS "Found Tcmalloc: ${Tcmalloc_LIBRARY}") 28 | else () 29 | message(STATUS "Not Found Tcmalloc: ${Tcmalloc_LIBRARY}") 30 | if (Tcmalloc_FIND_REQUIRED) 31 | message(STATUS "Looked for Tcmalloc libraries named ${Tcmalloc_NAMES}.") 32 | message(FATAL_ERROR "Could NOT find Tcmalloc library") 33 | endif () 34 | endif () 35 | 36 | mark_as_advanced( 37 | Tcmalloc_LIBRARY 38 | ) 39 | -------------------------------------------------------------------------------- /conanfile.py: -------------------------------------------------------------------------------- 1 | from conans import ConanFile, CMake 2 | 3 | 4 | class CrowConan(ConanFile): 5 | name = "Crow" 6 | version = "0.1" 7 | url = "https://github.com/ipkn/crow" 8 | license = "MIT; see https://github.com/ipkn/crow/blob/master/LICENSE" 9 | generators = "cmake" 10 | settings = "os", "compiler", "build_type", "arch" 11 | 12 | requires = (("Boost/1.60.0@lasote/stable"), 13 | ("OpenSSL/1.0.2i@lasote/stable")) 14 | 15 | # No exports necessary 16 | 17 | def source(self): 18 | # this will create a hello subfolder, take it into account 19 | self.run("git clone https://github.com/ipkn/crow.git") 20 | 21 | def build(self): 22 | cmake = CMake(self.settings) 23 | self.run('cmake %s/crow %s' % (self.conanfile_directory, cmake.command_line)) 24 | self.run("cmake --build . %s" % cmake.build_config) 25 | self.run("make") 26 | 27 | def package(self): 28 | self.copy("*.h", dst="include", src="amalgamate") 29 | -------------------------------------------------------------------------------- /examples/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 2.8) 2 | project (crow_examples) 3 | 4 | if (MSVC) 5 | add_executable(example_vs example_vs.cpp) 6 | target_link_libraries(example_vs ${Boost_LIBRARIES}) 7 | target_link_libraries(example_vs ${CMAKE_THREAD_LIBS_INIT}) 8 | else () 9 | add_executable(helloworld helloworld.cpp) 10 | target_link_libraries(helloworld ${Boost_LIBRARIES}) 11 | target_link_libraries(helloworld ${CMAKE_THREAD_LIBS_INIT}) 12 | 13 | if (OPENSSL_FOUND) 14 | add_executable(example_ssl ssl/example_ssl.cpp) 15 | target_link_libraries(example_ssl ${Boost_LIBRARIES}) 16 | target_link_libraries(example_ssl ${CMAKE_THREAD_LIBS_INIT} ${OPENSSL_LIBRARIES}) 17 | endif() 18 | 19 | add_executable(example_websocket websocket/example_ws.cpp) 20 | target_link_libraries(example_websocket ${Boost_LIBRARIES}) 21 | target_link_libraries(example_websocket ${CMAKE_THREAD_LIBS_INIT} ${OPENSSL_LIBRARIES}) 22 | add_custom_command(OUTPUT ws.html 23 | COMMAND ${CMAKE_COMMAND} -E 24 | copy ${PROJECT_SOURCE_DIR}/websocket/templates/ws.html ${CMAKE_CURRENT_BINARY_DIR}/templates/ws.html 25 | DEPENDS ${PROJECT_SOURCE_DIR}/websocket/templates/ws.html 26 | ) 27 | add_custom_target(example_ws_copy ALL DEPENDS ws.html) 28 | 29 | add_executable(example example.cpp) 30 | #target_link_libraries(example crow) 31 | target_link_libraries(example ${Boost_LIBRARIES}) 32 | target_link_libraries(example ${CMAKE_THREAD_LIBS_INIT}) 33 | 34 | if (Tcmalloc_FOUND) 35 | target_link_libraries(example ${Tcmalloc_LIBRARIES}) 36 | endif(Tcmalloc_FOUND) 37 | 38 | add_executable(example_with_all example_with_all.cpp) 39 | add_dependencies(example_with_all amalgamation) 40 | #target_link_libraries(example crow) 41 | target_link_libraries(example_with_all ${Boost_LIBRARIES}) 42 | target_link_libraries(example_with_all ${CMAKE_THREAD_LIBS_INIT}) 43 | 44 | add_custom_command(OUTPUT example_test.py 45 | COMMAND ${CMAKE_COMMAND} -E 46 | copy ${PROJECT_SOURCE_DIR}/example_test.py ${CMAKE_CURRENT_BINARY_DIR}/example_test.py 47 | DEPENDS ${PROJECT_SOURCE_DIR}/example_test.py 48 | ) 49 | add_custom_target(example_copy ALL DEPENDS example_test.py) 50 | 51 | add_executable(example_chat example_chat.cpp) 52 | target_link_libraries(example_chat ${Boost_LIBRARIES}) 53 | target_link_libraries(example_chat ${CMAKE_THREAD_LIBS_INIT}) 54 | add_custom_command(OUTPUT example_chat.html 55 | COMMAND ${CMAKE_COMMAND} -E 56 | copy ${PROJECT_SOURCE_DIR}/example_chat.html ${CMAKE_CURRENT_BINARY_DIR}/example_chat.html 57 | DEPENDS ${PROJECT_SOURCE_DIR}/example_chat.html 58 | ) 59 | add_custom_target(example_chat_copy ALL DEPENDS example_chat.html) 60 | 61 | #SET( CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -g -pg" ) 62 | #SET( CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -g -pg" ) 63 | endif() 64 | -------------------------------------------------------------------------------- /examples/example.cpp: -------------------------------------------------------------------------------- 1 | #include "crow.h" 2 | 3 | #include 4 | 5 | class ExampleLogHandler : public crow::ILogHandler { 6 | public: 7 | void log(std::string /*message*/, crow::LogLevel /*level*/) override { 8 | // cerr << "ExampleLogHandler -> " << message; 9 | } 10 | }; 11 | 12 | struct ExampleMiddleware 13 | { 14 | std::string message; 15 | 16 | ExampleMiddleware() 17 | { 18 | message = "foo"; 19 | } 20 | 21 | void setMessage(std::string newMsg) 22 | { 23 | message = newMsg; 24 | } 25 | 26 | struct context 27 | { 28 | }; 29 | 30 | void before_handle(crow::request& /*req*/, crow::response& /*res*/, context& /*ctx*/) 31 | { 32 | CROW_LOG_DEBUG << " - MESSAGE: " << message; 33 | } 34 | 35 | void after_handle(crow::request& /*req*/, crow::response& /*res*/, context& /*ctx*/) 36 | { 37 | // no-op 38 | } 39 | }; 40 | 41 | int main() 42 | { 43 | crow::App app; 44 | 45 | app.get_middleware().setMessage("hello"); 46 | 47 | CROW_ROUTE(app, "/") 48 | .name("hello") 49 | ([]{ 50 | return "Hello World!"; 51 | }); 52 | 53 | CROW_ROUTE(app, "/about") 54 | ([](){ 55 | return "About Crow example."; 56 | }); 57 | 58 | // a request to /path should be forwarded to /path/ 59 | CROW_ROUTE(app, "/path/") 60 | ([](){ 61 | return "Trailing slash test case.."; 62 | }); 63 | 64 | 65 | // simple json response 66 | // To see it in action enter {ip}:18080/json 67 | CROW_ROUTE(app, "/json") 68 | ([]{ 69 | crow::json::wvalue x; 70 | x["message"] = "Hello, World!"; 71 | return x; 72 | }); 73 | 74 | // To see it in action enter {ip}:18080/hello/{integer_between -2^32 and 100} and you should receive 75 | // {integer_between -2^31 and 100} bottles of beer! 76 | CROW_ROUTE(app,"/hello/") 77 | ([](int count){ 78 | if (count > 100) 79 | return crow::response(400); 80 | std::ostringstream os; 81 | os << count << " bottles of beer!"; 82 | return crow::response(os.str()); 83 | }); 84 | 85 | // To see it in action submit {ip}:18080/add/1/2 and you should receive 3 (exciting, isn't it) 86 | CROW_ROUTE(app,"/add//") 87 | ([](const crow::request& /*req*/, crow::response& res, int a, int b){ 88 | std::ostringstream os; 89 | os << a+b; 90 | res.write(os.str()); 91 | res.end(); 92 | }); 93 | 94 | // Compile error with message "Handler type is mismatched with URL paramters" 95 | //CROW_ROUTE(app,"/another/") 96 | //([](int a, int b){ 97 | //return crow::response(500); 98 | //}); 99 | 100 | // more json example 101 | 102 | // To see it in action, I recommend to use the Postman Chrome extension: 103 | // * Set the address to {ip}:18080/add_json 104 | // * Set the method to post 105 | // * Select 'raw' and then JSON 106 | // * Add {"a": 1, "b": 1} 107 | // * Send and you should receive 2 108 | 109 | // A simpler way for json example: 110 | // * curl -d '{"a":1,"b":2}' {ip}:18080/add_json 111 | CROW_ROUTE(app, "/add_json") 112 | .methods("POST"_method) 113 | ([](const crow::request& req){ 114 | auto x = crow::json::load(req.body); 115 | if (!x) 116 | return crow::response(400); 117 | int sum = x["a"].i()+x["b"].i(); 118 | std::ostringstream os; 119 | os << sum; 120 | return crow::response{os.str()}; 121 | }); 122 | 123 | // Example of a request taking URL parameters 124 | // If you want to activate all the functions just query 125 | // {ip}:18080/params?foo='blabla'&pew=32&count[]=a&count[]=b 126 | CROW_ROUTE(app, "/params") 127 | ([](const crow::request& req){ 128 | std::ostringstream os; 129 | 130 | // To get a simple string from the url params 131 | // To see it in action /params?foo='blabla' 132 | os << "Params: " << req.url_params << "\n\n"; 133 | os << "The key 'foo' was " << (req.url_params.get("foo") == nullptr ? "not " : "") << "found.\n"; 134 | 135 | // To get a double from the request 136 | // To see in action submit something like '/params?pew=42' 137 | if(req.url_params.get("pew") != nullptr) { 138 | double countD = boost::lexical_cast(req.url_params.get("pew")); 139 | os << "The value of 'pew' is " << countD << '\n'; 140 | } 141 | 142 | // To get a list from the request 143 | // You have to submit something like '/params?count[]=a&count[]=b' to have a list with two values (a and b) 144 | auto count = req.url_params.get_list("count"); 145 | os << "The key 'count' contains " << count.size() << " value(s).\n"; 146 | for(const auto& countVal : count) { 147 | os << " - " << countVal << '\n'; 148 | } 149 | 150 | // To get a dictionary from the request 151 | // You have to submit something like '/params?mydict[a]=b&mydict[abcd]=42' to have a list of pairs ((a, b) and (abcd, 42)) 152 | auto mydict = req.url_params.get_dict("mydict"); 153 | os << "The key 'dict' contains " << mydict.size() << " value(s).\n"; 154 | for(const auto& mydictVal : mydict) { 155 | os << " - " << mydictVal.first << " -> " << mydictVal.second << '\n'; 156 | } 157 | 158 | return crow::response{os.str()}; 159 | }); 160 | 161 | CROW_ROUTE(app, "/large") 162 | ([]{ 163 | return std::string(512*1024, ' '); 164 | }); 165 | 166 | // enables all log 167 | app.loglevel(crow::LogLevel::DEBUG); 168 | //crow::logger::setHandler(std::make_shared()); 169 | 170 | app.port(18080) 171 | .multithreaded() 172 | .run(); 173 | } 174 | -------------------------------------------------------------------------------- /examples/example.py: -------------------------------------------------------------------------------- 1 | from flask import Flask 2 | app = Flask(__name__) 3 | 4 | @app.route("/") 5 | def hello(): 6 | return "Hello World!" 7 | 8 | @app.route("/about//hello") 9 | def hello1(path): 10 | return "about1" 11 | 12 | @app.route("/about") 13 | def hello2(): 14 | return "about2" 15 | 16 | print app.url_map 17 | 18 | if __name__ == "__main__": 19 | app.run(host="0.0.0.0", port=8888) 20 | -------------------------------------------------------------------------------- /examples/example_chat.cpp: -------------------------------------------------------------------------------- 1 | #include "crow.h" 2 | #include 3 | #include 4 | #include 5 | 6 | using namespace std; 7 | 8 | vector msgs; 9 | vector> ress; 10 | 11 | void broadcast(const string& msg) 12 | { 13 | msgs.push_back(msg); 14 | crow::json::wvalue x; 15 | x["msgs"][0] = msgs.back(); 16 | x["last"] = msgs.size(); 17 | string body = crow::json::dump(x); 18 | for(auto p : ress) 19 | { 20 | auto* res = p.first; 21 | CROW_LOG_DEBUG << res << " replied: " << body; 22 | res->end(body); 23 | } 24 | ress.clear(); 25 | } 26 | // To see how it works go on {ip}:40080 but I just got it working with external build (not directly in IDE, I guess a problem with dependency) 27 | int main() 28 | { 29 | crow::SimpleApp app; 30 | crow::mustache::set_base("."); 31 | 32 | CROW_ROUTE(app, "/") 33 | ([]{ 34 | crow::mustache::context ctx; 35 | return crow::mustache::load("example_chat.html").render(); 36 | }); 37 | 38 | CROW_ROUTE(app, "/logs") 39 | ([]{ 40 | CROW_LOG_INFO << "logs requested"; 41 | crow::json::wvalue x; 42 | int start = max(0, (int)msgs.size()-100); 43 | for(int i = start; i < (int)msgs.size(); i++) 44 | x["msgs"][i-start] = msgs[i]; 45 | x["last"] = msgs.size(); 46 | CROW_LOG_INFO << "logs completed"; 47 | return x; 48 | }); 49 | 50 | CROW_ROUTE(app, "/logs/") 51 | ([](const crow::request& /*req*/, crow::response& res, int after){ 52 | CROW_LOG_INFO << "logs with last " << after; 53 | if (after < (int)msgs.size()) 54 | { 55 | crow::json::wvalue x; 56 | for(int i = after; i < (int)msgs.size(); i ++) 57 | x["msgs"][i-after] = msgs[i]; 58 | x["last"] = msgs.size(); 59 | 60 | res.write(crow::json::dump(x)); 61 | res.end(); 62 | } 63 | else 64 | { 65 | vector> filtered; 66 | for(auto p : ress) 67 | { 68 | if (p.first->is_alive() && chrono::steady_clock::now() - p.second < chrono::seconds(30)) 69 | filtered.push_back(p); 70 | else 71 | p.first->end(); 72 | } 73 | ress.swap(filtered); 74 | ress.push_back({&res, chrono::steady_clock::now()}); 75 | CROW_LOG_DEBUG << &res << " stored " << ress.size(); 76 | } 77 | }); 78 | 79 | CROW_ROUTE(app, "/send") 80 | .methods("GET"_method, "POST"_method) 81 | ([](const crow::request& req) 82 | { 83 | CROW_LOG_INFO << "msg from client: " << req.body; 84 | broadcast(req.body); 85 | return ""; 86 | }); 87 | 88 | app.port(40080) 89 | //.multithreaded() 90 | .run(); 91 | } 92 | -------------------------------------------------------------------------------- /examples/example_chat.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |
9 |
10 | 53 | 54 | 55 | -------------------------------------------------------------------------------- /examples/example_test.py: -------------------------------------------------------------------------------- 1 | import urllib 2 | assert "Hello World!" == urllib.urlopen('http://localhost:18080').read() 3 | assert "About Crow example." == urllib.urlopen('http://localhost:18080/about').read() 4 | assert 404 == urllib.urlopen('http://localhost:18080/list').getcode() 5 | assert "3 bottles of beer!" == urllib.urlopen('http://localhost:18080/hello/3').read() 6 | assert "100 bottles of beer!" == urllib.urlopen('http://localhost:18080/hello/100').read() 7 | assert 400 == urllib.urlopen('http://localhost:18080/hello/500').getcode() 8 | assert "3" == urllib.urlopen('http://localhost:18080/add_json', data='{"a":1,"b":2}').read() 9 | assert "3" == urllib.urlopen('http://localhost:18080/add/1/2').read() 10 | 11 | # test persistent connection 12 | import socket 13 | import time 14 | s = socket.socket() 15 | s.connect(('localhost', 18080)) 16 | for i in xrange(10): 17 | s.send('''GET / HTTP/1.1 18 | Host: localhost\r\n\r\n'''); 19 | assert 'Hello World!' in s.recv(1024) 20 | 21 | # test large 22 | s = socket.socket() 23 | s.connect(('localhost', 18080)) 24 | s.send('''GET /large HTTP/1.1 25 | Host: localhost\r\nConnection: close\r\n\r\n''') 26 | r = '' 27 | while True: 28 | d = s.recv(1024*1024) 29 | if not d: 30 | break; 31 | r += d 32 | print len(r), len(d) 33 | print len(r), r[:100] 34 | assert len(r) > 512*1024 35 | 36 | # test timeout 37 | s = socket.socket() 38 | s.connect(('localhost', 18080)) 39 | # invalid request, connection will be closed after timeout 40 | s.send('''GET / HTTP/1.1 41 | hHhHHefhwjkefhklwejfklwejf 42 | ''') 43 | print s.recv(1024) 44 | 45 | -------------------------------------------------------------------------------- /examples/example_vs.cpp: -------------------------------------------------------------------------------- 1 | #include "crow.h" 2 | 3 | #include 4 | 5 | class ExampleLogHandler : public crow::ILogHandler { 6 | public: 7 | void log(std::string message, crow::LogLevel level) override { 8 | // cerr << "ExampleLogHandler -> " << message; 9 | } 10 | }; 11 | 12 | struct ExampleMiddleware 13 | { 14 | std::string message; 15 | 16 | ExampleMiddleware() 17 | { 18 | message = "foo"; 19 | } 20 | 21 | void setMessage(std::string newMsg) 22 | { 23 | message = newMsg; 24 | } 25 | 26 | struct context 27 | { 28 | }; 29 | 30 | void before_handle(crow::request& req, crow::response& res, context& ctx) 31 | { 32 | CROW_LOG_DEBUG << " - MESSAGE: " << message; 33 | } 34 | 35 | void after_handle(crow::request& req, crow::response& res, context& ctx) 36 | { 37 | // no-op 38 | } 39 | }; 40 | 41 | int main() 42 | { 43 | crow::App app; 44 | 45 | app.get_middleware().setMessage("hello"); 46 | 47 | app.route_dynamic("/") 48 | ([]{ 49 | return "Hello World!"; 50 | }); 51 | 52 | app.route_dynamic("/about") 53 | ([](){ 54 | return "About Crow example."; 55 | }); 56 | 57 | // a request to /path should be forwarded to /path/ 58 | app.route_dynamic("/path/") 59 | ([](){ 60 | return "Trailing slash test case.."; 61 | }); 62 | 63 | // simple json response 64 | app.route_dynamic("/json") 65 | ([]{ 66 | crow::json::wvalue x; 67 | x["message"] = "Hello, World!"; 68 | return x; 69 | }); 70 | 71 | app.route_dynamic("/hello/") 72 | ([](int count){ 73 | if (count > 100) 74 | return crow::response(400); 75 | std::ostringstream os; 76 | os << count << " bottles of beer!"; 77 | return crow::response(os.str()); 78 | }); 79 | 80 | app.route_dynamic("/add//") 81 | ([](const crow::request& req, crow::response& res, int a, int b){ 82 | std::ostringstream os; 83 | os << a+b; 84 | res.write(os.str()); 85 | res.end(); 86 | }); 87 | 88 | // Compile error with message "Handler type is mismatched with URL paramters" 89 | //CROW_ROUTE(app,"/another/") 90 | //([](int a, int b){ 91 | //return crow::response(500); 92 | //}); 93 | 94 | // more json example 95 | app.route_dynamic("/add_json") 96 | .methods(crow::HTTPMethod::POST) 97 | ([](const crow::request& req){ 98 | auto x = crow::json::load(req.body); 99 | if (!x) 100 | return crow::response(400); 101 | auto sum = x["a"].i()+x["b"].i(); 102 | std::ostringstream os; 103 | os << sum; 104 | return crow::response{os.str()}; 105 | }); 106 | 107 | app.route_dynamic("/params") 108 | ([](const crow::request& req){ 109 | std::ostringstream os; 110 | os << "Params: " << req.url_params << "\n\n"; 111 | os << "The key 'foo' was " << (req.url_params.get("foo") == nullptr ? "not " : "") << "found.\n"; 112 | if(req.url_params.get("pew") != nullptr) { 113 | double countD = boost::lexical_cast(req.url_params.get("pew")); 114 | os << "The value of 'pew' is " << countD << '\n'; 115 | } 116 | auto count = req.url_params.get_list("count"); 117 | os << "The key 'count' contains " << count.size() << " value(s).\n"; 118 | for(const auto& countVal : count) { 119 | os << " - " << countVal << '\n'; 120 | } 121 | return crow::response{os.str()}; 122 | }); 123 | 124 | // ignore all log 125 | crow::logger::setLogLevel(crow::LogLevel::DEBUG); 126 | //crow::logger::setHandler(std::make_shared()); 127 | 128 | app.port(18080) 129 | .multithreaded() 130 | .run(); 131 | } 132 | -------------------------------------------------------------------------------- /examples/example_with_all.cpp: -------------------------------------------------------------------------------- 1 | #include "../amalgamate/crow_all.h" 2 | 3 | #include 4 | 5 | class ExampleLogHandler : public crow::ILogHandler { 6 | public: 7 | void log(std::string /*message*/, crow::LogLevel /*level*/) override { 8 | // cerr << "ExampleLogHandler -> " << message; 9 | } 10 | }; 11 | 12 | int main() 13 | { 14 | crow::SimpleApp app; 15 | 16 | CROW_ROUTE(app, "/") 17 | .name("hello") 18 | ([]{ 19 | return "Hello World!"; 20 | }); 21 | 22 | CROW_ROUTE(app, "/about") 23 | ([](){ 24 | return "About Crow example."; 25 | }); 26 | 27 | // simple json response 28 | CROW_ROUTE(app, "/json") 29 | ([]{ 30 | crow::json::wvalue x; 31 | x["message"] = "Hello, World!"; 32 | return x; 33 | }); 34 | 35 | CROW_ROUTE(app,"/hello/") 36 | ([](int count){ 37 | if (count > 100) 38 | return crow::response(400); 39 | std::ostringstream os; 40 | os << count << " bottles of beer!"; 41 | return crow::response(os.str()); 42 | }); 43 | 44 | CROW_ROUTE(app,"/add//") 45 | ([](const crow::request& /*req*/, crow::response& res, int a, int b){ 46 | std::ostringstream os; 47 | os << a+b; 48 | res.write(os.str()); 49 | res.end(); 50 | }); 51 | 52 | // Compile error with message "Handler type is mismatched with URL paramters" 53 | //CROW_ROUTE(app,"/another/") 54 | //([](int a, int b){ 55 | //return crow::response(500); 56 | //}); 57 | 58 | // more json example 59 | CROW_ROUTE(app, "/add_json") 60 | ([](const crow::request& req){ 61 | auto x = crow::json::load(req.body); 62 | if (!x) 63 | return crow::response(400); 64 | int sum = x["a"].i()+x["b"].i(); 65 | std::ostringstream os; 66 | os << sum; 67 | return crow::response{os.str()}; 68 | }); 69 | 70 | CROW_ROUTE(app, "/params") 71 | ([](const crow::request& req){ 72 | std::ostringstream os; 73 | os << "Params: " << req.url_params << "\n\n"; 74 | os << "The key 'foo' was " << (req.url_params.get("foo") == nullptr ? "not " : "") << "found.\n"; 75 | if(req.url_params.get("pew") != nullptr) { 76 | double countD = boost::lexical_cast(req.url_params.get("pew")); 77 | os << "The value of 'pew' is " << countD << '\n'; 78 | } 79 | auto count = req.url_params.get_list("count"); 80 | os << "The key 'count' contains " << count.size() << " value(s).\n"; 81 | for(const auto& countVal : count) { 82 | os << " - " << countVal << '\n'; 83 | } 84 | return crow::response{os.str()}; 85 | }); 86 | 87 | // ignore all log 88 | crow::logger::setLogLevel(crow::LogLevel::Debug); 89 | //crow::logger::setHandler(std::make_shared()); 90 | 91 | app.port(18080) 92 | .multithreaded() 93 | .run(); 94 | } 95 | -------------------------------------------------------------------------------- /examples/helloworld.cpp: -------------------------------------------------------------------------------- 1 | #include "crow.h" 2 | 3 | int main() 4 | { 5 | crow::SimpleApp app; 6 | 7 | CROW_ROUTE(app, "/") 8 | ([]() { 9 | return "Hello world!"; 10 | }); 11 | 12 | app.port(18080).run(); 13 | } 14 | -------------------------------------------------------------------------------- /examples/ssl/example_ssl.cpp: -------------------------------------------------------------------------------- 1 | #define CROW_ENABLE_SSL 2 | #include "crow.h" 3 | 4 | int main() 5 | { 6 | crow::SimpleApp app; 7 | 8 | CROW_ROUTE(app, "/") 9 | ([]() { 10 | return "Hello world!"; 11 | }); 12 | 13 | app.port(18080).ssl_file("test.crt", "test.key").run(); 14 | 15 | // Use .pem file 16 | //app.port(18080).ssl_file("test.pem").run(); 17 | 18 | // Use custom context; see boost::asio::ssl::context 19 | /* 20 | * crow::ssl_context_t ctx; 21 | * ctx.set_verify_mode(...) 22 | * 23 | * ... configuring ctx 24 | * 25 | * app.port(18080).ssl(ctx).run(); 26 | */ 27 | } 28 | -------------------------------------------------------------------------------- /examples/websocket/example_ws.cpp: -------------------------------------------------------------------------------- 1 | #include "crow.h" 2 | #include 3 | #include 4 | 5 | 6 | int main() 7 | { 8 | crow::SimpleApp app; 9 | 10 | std::mutex mtx;; 11 | std::unordered_set users; 12 | 13 | CROW_ROUTE(app, "/ws") 14 | .websocket() 15 | .onopen([&](crow::websocket::connection& conn){ 16 | CROW_LOG_INFO << "new websocket connection"; 17 | std::lock_guard _(mtx); 18 | users.insert(&conn); 19 | }) 20 | .onclose([&](crow::websocket::connection& conn, const std::string& reason){ 21 | CROW_LOG_INFO << "websocket connection closed: " << reason; 22 | std::lock_guard _(mtx); 23 | users.erase(&conn); 24 | }) 25 | .onmessage([&](crow::websocket::connection& /*conn*/, const std::string& data, bool is_binary){ 26 | std::lock_guard _(mtx); 27 | for(auto u:users) 28 | if (is_binary) 29 | u->send_binary(data); 30 | else 31 | u->send_text(data); 32 | }); 33 | 34 | CROW_ROUTE(app, "/") 35 | ([]{ 36 | char name[256]; 37 | gethostname(name, 256); 38 | crow::mustache::context x; 39 | x["servername"] = name; 40 | 41 | auto page = crow::mustache::load("ws.html"); 42 | return page.render(x); 43 | }); 44 | 45 | app.port(40080) 46 | .multithreaded() 47 | .run(); 48 | } 49 | -------------------------------------------------------------------------------- /examples/websocket/templates/ws.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |
11 | 13 | 41 | 42 | 43 | -------------------------------------------------------------------------------- /include/crow.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "crow/query_string.h" 3 | #include "crow/http_parser_merged.h" 4 | #include "crow/ci_map.h" 5 | #include "crow/TinySHA1.hpp" 6 | #include "crow/settings.h" 7 | #include "crow/socket_adaptors.h" 8 | #include "crow/json.h" 9 | #include "crow/mustache.h" 10 | #include "crow/logging.h" 11 | #include "crow/dumb_timer_queue.h" 12 | #include "crow/utility.h" 13 | #include "crow/common.h" 14 | #include "crow/http_request.h" 15 | #include "crow/websocket.h" 16 | #include "crow/parser.h" 17 | #include "crow/http_response.h" 18 | #include "crow/middleware.h" 19 | #include "crow/routing.h" 20 | #include "crow/middleware_context.h" 21 | #include "crow/http_connection.h" 22 | #include "crow/http_server.h" 23 | #include "crow/app.h" 24 | -------------------------------------------------------------------------------- /include/crow/TinySHA1.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * TinySHA1 - a header only implementation of the SHA1 algorithm in C++. Based 4 | * on the implementation in boost::uuid::details. 5 | * 6 | * SHA1 Wikipedia Page: http://en.wikipedia.org/wiki/SHA-1 7 | * 8 | * Copyright (c) 2012-22 SAURAV MOHAPATRA 9 | * 10 | * Permission to use, copy, modify, and distribute this software for any 11 | * purpose with or without fee is hereby granted, provided that the above 12 | * copyright notice and this permission notice appear in all copies. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 15 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 16 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 17 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 18 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 19 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 20 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 21 | */ 22 | #ifndef _TINY_SHA1_HPP_ 23 | #define _TINY_SHA1_HPP_ 24 | #include 25 | #include 26 | #include 27 | #include 28 | namespace sha1 29 | { 30 | class SHA1 31 | { 32 | public: 33 | typedef uint32_t digest32_t[5]; 34 | typedef uint8_t digest8_t[20]; 35 | inline static uint32_t LeftRotate(uint32_t value, size_t count) { 36 | return (value << count) ^ (value >> (32-count)); 37 | } 38 | SHA1(){ reset(); } 39 | virtual ~SHA1() {} 40 | SHA1(const SHA1& s) { *this = s; } 41 | const SHA1& operator = (const SHA1& s) { 42 | memcpy(m_digest, s.m_digest, 5 * sizeof(uint32_t)); 43 | memcpy(m_block, s.m_block, 64); 44 | m_blockByteIndex = s.m_blockByteIndex; 45 | m_byteCount = s.m_byteCount; 46 | return *this; 47 | } 48 | SHA1& reset() { 49 | m_digest[0] = 0x67452301; 50 | m_digest[1] = 0xEFCDAB89; 51 | m_digest[2] = 0x98BADCFE; 52 | m_digest[3] = 0x10325476; 53 | m_digest[4] = 0xC3D2E1F0; 54 | m_blockByteIndex = 0; 55 | m_byteCount = 0; 56 | return *this; 57 | } 58 | SHA1& processByte(uint8_t octet) { 59 | this->m_block[this->m_blockByteIndex++] = octet; 60 | ++this->m_byteCount; 61 | if(m_blockByteIndex == 64) { 62 | this->m_blockByteIndex = 0; 63 | processBlock(); 64 | } 65 | return *this; 66 | } 67 | SHA1& processBlock(const void* const start, const void* const end) { 68 | const uint8_t* begin = static_cast(start); 69 | const uint8_t* finish = static_cast(end); 70 | while(begin != finish) { 71 | processByte(*begin); 72 | begin++; 73 | } 74 | return *this; 75 | } 76 | SHA1& processBytes(const void* const data, size_t len) { 77 | const uint8_t* block = static_cast(data); 78 | processBlock(block, block + len); 79 | return *this; 80 | } 81 | const uint32_t* getDigest(digest32_t digest) { 82 | size_t bitCount = this->m_byteCount * 8; 83 | processByte(0x80); 84 | if (this->m_blockByteIndex > 56) { 85 | while (m_blockByteIndex != 0) { 86 | processByte(0); 87 | } 88 | while (m_blockByteIndex < 56) { 89 | processByte(0); 90 | } 91 | } else { 92 | while (m_blockByteIndex < 56) { 93 | processByte(0); 94 | } 95 | } 96 | processByte(0); 97 | processByte(0); 98 | processByte(0); 99 | processByte(0); 100 | processByte( static_cast((bitCount>>24) & 0xFF)); 101 | processByte( static_cast((bitCount>>16) & 0xFF)); 102 | processByte( static_cast((bitCount>>8 ) & 0xFF)); 103 | processByte( static_cast((bitCount) & 0xFF)); 104 | 105 | memcpy(digest, m_digest, 5 * sizeof(uint32_t)); 106 | return digest; 107 | } 108 | const uint8_t* getDigestBytes(digest8_t digest) { 109 | digest32_t d32; 110 | getDigest(d32); 111 | size_t di = 0; 112 | digest[di++] = ((d32[0] >> 24) & 0xFF); 113 | digest[di++] = ((d32[0] >> 16) & 0xFF); 114 | digest[di++] = ((d32[0] >> 8) & 0xFF); 115 | digest[di++] = ((d32[0]) & 0xFF); 116 | 117 | digest[di++] = ((d32[1] >> 24) & 0xFF); 118 | digest[di++] = ((d32[1] >> 16) & 0xFF); 119 | digest[di++] = ((d32[1] >> 8) & 0xFF); 120 | digest[di++] = ((d32[1]) & 0xFF); 121 | 122 | digest[di++] = ((d32[2] >> 24) & 0xFF); 123 | digest[di++] = ((d32[2] >> 16) & 0xFF); 124 | digest[di++] = ((d32[2] >> 8) & 0xFF); 125 | digest[di++] = ((d32[2]) & 0xFF); 126 | 127 | digest[di++] = ((d32[3] >> 24) & 0xFF); 128 | digest[di++] = ((d32[3] >> 16) & 0xFF); 129 | digest[di++] = ((d32[3] >> 8) & 0xFF); 130 | digest[di++] = ((d32[3]) & 0xFF); 131 | 132 | digest[di++] = ((d32[4] >> 24) & 0xFF); 133 | digest[di++] = ((d32[4] >> 16) & 0xFF); 134 | digest[di++] = ((d32[4] >> 8) & 0xFF); 135 | digest[di++] = ((d32[4]) & 0xFF); 136 | return digest; 137 | } 138 | 139 | protected: 140 | void processBlock() { 141 | uint32_t w[80]; 142 | for (size_t i = 0; i < 16; i++) { 143 | w[i] = (m_block[i*4 + 0] << 24); 144 | w[i] |= (m_block[i*4 + 1] << 16); 145 | w[i] |= (m_block[i*4 + 2] << 8); 146 | w[i] |= (m_block[i*4 + 3]); 147 | } 148 | for (size_t i = 16; i < 80; i++) { 149 | w[i] = LeftRotate((w[i-3] ^ w[i-8] ^ w[i-14] ^ w[i-16]), 1); 150 | } 151 | 152 | uint32_t a = m_digest[0]; 153 | uint32_t b = m_digest[1]; 154 | uint32_t c = m_digest[2]; 155 | uint32_t d = m_digest[3]; 156 | uint32_t e = m_digest[4]; 157 | 158 | for (std::size_t i=0; i<80; ++i) { 159 | uint32_t f = 0; 160 | uint32_t k = 0; 161 | 162 | if (i<20) { 163 | f = (b & c) | (~b & d); 164 | k = 0x5A827999; 165 | } else if (i<40) { 166 | f = b ^ c ^ d; 167 | k = 0x6ED9EBA1; 168 | } else if (i<60) { 169 | f = (b & c) | (b & d) | (c & d); 170 | k = 0x8F1BBCDC; 171 | } else { 172 | f = b ^ c ^ d; 173 | k = 0xCA62C1D6; 174 | } 175 | uint32_t temp = LeftRotate(a, 5) + f + e + k + w[i]; 176 | e = d; 177 | d = c; 178 | c = LeftRotate(b, 30); 179 | b = a; 180 | a = temp; 181 | } 182 | 183 | m_digest[0] += a; 184 | m_digest[1] += b; 185 | m_digest[2] += c; 186 | m_digest[3] += d; 187 | m_digest[4] += e; 188 | } 189 | private: 190 | digest32_t m_digest; 191 | uint8_t m_block[64]; 192 | size_t m_blockByteIndex; 193 | size_t m_byteCount; 194 | }; 195 | } 196 | #endif 197 | -------------------------------------------------------------------------------- /include/crow/app.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | #include "crow/settings.h" 14 | #include "crow/logging.h" 15 | #include "crow/utility.h" 16 | #include "crow/routing.h" 17 | #include "crow/middleware_context.h" 18 | #include "crow/http_request.h" 19 | #include "crow/http_server.h" 20 | 21 | 22 | #ifdef CROW_MSVC_WORKAROUND 23 | #define CROW_ROUTE(app, url) app.route_dynamic(url) 24 | #else 25 | #define CROW_ROUTE(app, url) app.route(url) 26 | #endif 27 | 28 | namespace crow 29 | { 30 | #ifdef CROW_ENABLE_SSL 31 | using ssl_context_t = boost::asio::ssl::context; 32 | #endif 33 | template 34 | class Crow 35 | { 36 | public: 37 | using self_t = Crow; 38 | using server_t = Server; 39 | #ifdef CROW_ENABLE_SSL 40 | using ssl_server_t = Server; 41 | #endif 42 | Crow() 43 | { 44 | } 45 | 46 | template 47 | void handle_upgrade(const request& req, response& res, Adaptor&& adaptor) 48 | { 49 | router_.handle_upgrade(req, res, adaptor); 50 | } 51 | 52 | void handle(const request& req, response& res) 53 | { 54 | router_.handle(req, res); 55 | } 56 | 57 | DynamicRule& route_dynamic(std::string&& rule) 58 | { 59 | return router_.new_rule_dynamic(std::move(rule)); 60 | } 61 | 62 | template 63 | auto route(std::string&& rule) 64 | -> typename std::result_of)(Router, std::string&&)>::type 65 | { 66 | return router_.new_rule_tagged(std::move(rule)); 67 | } 68 | 69 | self_t& port(std::uint16_t port) 70 | { 71 | port_ = port; 72 | return *this; 73 | } 74 | 75 | self_t& bindaddr(std::string bindaddr) 76 | { 77 | bindaddr_ = bindaddr; 78 | return *this; 79 | } 80 | 81 | self_t& multithreaded() 82 | { 83 | return concurrency(std::thread::hardware_concurrency()); 84 | } 85 | 86 | self_t& concurrency(std::uint16_t concurrency) 87 | { 88 | if (concurrency < 1) 89 | concurrency = 1; 90 | concurrency_ = concurrency; 91 | return *this; 92 | } 93 | 94 | void validate() 95 | { 96 | router_.validate(); 97 | } 98 | 99 | void notify_server_start() 100 | { 101 | std::unique_lock lock(start_mutex_); 102 | server_started_ = true; 103 | cv_started_.notify_all(); 104 | } 105 | 106 | void run() 107 | { 108 | validate(); 109 | #ifdef CROW_ENABLE_SSL 110 | if (use_ssl_) 111 | { 112 | ssl_server_ = std::move(std::unique_ptr(new ssl_server_t(this, bindaddr_, port_, &middlewares_, concurrency_, &ssl_context_))); 113 | ssl_server_->set_tick_function(tick_interval_, tick_function_); 114 | notify_server_start(); 115 | ssl_server_->run(); 116 | } 117 | else 118 | #endif 119 | { 120 | server_ = std::move(std::unique_ptr(new server_t(this, bindaddr_, port_, &middlewares_, concurrency_, nullptr))); 121 | server_->set_tick_function(tick_interval_, tick_function_); 122 | notify_server_start(); 123 | server_->run(); 124 | } 125 | } 126 | 127 | void stop() 128 | { 129 | #ifdef CROW_ENABLE_SSL 130 | if (use_ssl_) 131 | { 132 | ssl_server_->stop(); 133 | } 134 | else 135 | #endif 136 | { 137 | server_->stop(); 138 | } 139 | } 140 | 141 | void debug_print() 142 | { 143 | CROW_LOG_DEBUG << "Routing:"; 144 | router_.debug_print(); 145 | } 146 | 147 | self_t& loglevel(crow::LogLevel level) 148 | { 149 | crow::logger::setLogLevel(level); 150 | return *this; 151 | } 152 | 153 | #ifdef CROW_ENABLE_SSL 154 | self_t& ssl_file(const std::string& crt_filename, const std::string& key_filename) 155 | { 156 | use_ssl_ = true; 157 | ssl_context_.set_verify_mode(boost::asio::ssl::verify_peer); 158 | ssl_context_.use_certificate_file(crt_filename, ssl_context_t::pem); 159 | ssl_context_.use_private_key_file(key_filename, ssl_context_t::pem); 160 | ssl_context_.set_options( 161 | boost::asio::ssl::context::default_workarounds 162 | | boost::asio::ssl::context::no_sslv2 163 | | boost::asio::ssl::context::no_sslv3 164 | ); 165 | return *this; 166 | } 167 | 168 | self_t& ssl_file(const std::string& pem_filename) 169 | { 170 | use_ssl_ = true; 171 | ssl_context_.set_verify_mode(boost::asio::ssl::verify_peer); 172 | ssl_context_.load_verify_file(pem_filename); 173 | ssl_context_.set_options( 174 | boost::asio::ssl::context::default_workarounds 175 | | boost::asio::ssl::context::no_sslv2 176 | | boost::asio::ssl::context::no_sslv3 177 | ); 178 | return *this; 179 | } 180 | 181 | self_t& ssl(boost::asio::ssl::context&& ctx) 182 | { 183 | use_ssl_ = true; 184 | ssl_context_ = std::move(ctx); 185 | return *this; 186 | } 187 | 188 | 189 | bool use_ssl_{false}; 190 | ssl_context_t ssl_context_{boost::asio::ssl::context::sslv23}; 191 | 192 | #else 193 | template 194 | self_t& ssl_file(T&&, Remain&&...) 195 | { 196 | // We can't call .ssl() member function unless CROW_ENABLE_SSL is defined. 197 | static_assert( 198 | // make static_assert dependent to T; always false 199 | std::is_base_of::value, 200 | "Define CROW_ENABLE_SSL to enable ssl support."); 201 | return *this; 202 | } 203 | 204 | template 205 | self_t& ssl(T&&) 206 | { 207 | // We can't call .ssl() member function unless CROW_ENABLE_SSL is defined. 208 | static_assert( 209 | // make static_assert dependent to T; always false 210 | std::is_base_of::value, 211 | "Define CROW_ENABLE_SSL to enable ssl support."); 212 | return *this; 213 | } 214 | #endif 215 | 216 | // middleware 217 | using context_t = detail::context; 218 | template 219 | typename T::context& get_context(const request& req) 220 | { 221 | static_assert(black_magic::contains::value, "App doesn't have the specified middleware type."); 222 | auto& ctx = *reinterpret_cast(req.middleware_context); 223 | return ctx.template get(); 224 | } 225 | 226 | template 227 | T& get_middleware() 228 | { 229 | return utility::get_element_by_type(middlewares_); 230 | } 231 | 232 | template 233 | self_t& tick(Duration d, Func f) { 234 | tick_interval_ = std::chrono::duration_cast(d); 235 | tick_function_ = f; 236 | return *this; 237 | } 238 | 239 | void wait_for_server_start() 240 | { 241 | std::unique_lock lock(start_mutex_); 242 | if (server_started_) 243 | return; 244 | cv_started_.wait(lock); 245 | } 246 | 247 | private: 248 | uint16_t port_ = 80; 249 | uint16_t concurrency_ = 1; 250 | std::string bindaddr_ = "0.0.0.0"; 251 | Router router_; 252 | 253 | std::chrono::milliseconds tick_interval_; 254 | std::function tick_function_; 255 | 256 | std::tuple middlewares_; 257 | 258 | #ifdef CROW_ENABLE_SSL 259 | std::unique_ptr ssl_server_; 260 | #endif 261 | std::unique_ptr server_; 262 | 263 | bool server_started_{false}; 264 | std::condition_variable cv_started_; 265 | std::mutex start_mutex_; 266 | }; 267 | template 268 | using App = Crow; 269 | using SimpleApp = Crow<>; 270 | } 271 | -------------------------------------------------------------------------------- /include/crow/ci_map.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | namespace crow 8 | { 9 | struct ci_hash 10 | { 11 | size_t operator()(const std::string& key) const 12 | { 13 | std::size_t seed = 0; 14 | std::locale locale; 15 | 16 | for(auto c : key) 17 | { 18 | boost::hash_combine(seed, std::toupper(c, locale)); 19 | } 20 | 21 | return seed; 22 | } 23 | }; 24 | 25 | struct ci_key_eq 26 | { 27 | bool operator()(const std::string& l, const std::string& r) const 28 | { 29 | return boost::iequals(l, r); 30 | } 31 | }; 32 | 33 | using ci_map = std::unordered_multimap; 34 | } 35 | -------------------------------------------------------------------------------- /include/crow/common.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include "crow/utility.h" 8 | 9 | namespace crow 10 | { 11 | enum class HTTPMethod 12 | { 13 | #ifndef DELETE 14 | DELETE = 0, 15 | GET, 16 | HEAD, 17 | POST, 18 | PUT, 19 | CONNECT, 20 | OPTIONS, 21 | TRACE, 22 | PATCH, 23 | PURGE, 24 | #endif 25 | 26 | Delete = 0, 27 | Get, 28 | Head, 29 | Post, 30 | Put, 31 | Connect, 32 | Options, 33 | Trace, 34 | Patch, 35 | Purge, 36 | 37 | 38 | InternalMethodCount, 39 | // should not add an item below this line: used for array count 40 | }; 41 | 42 | inline std::string method_name(HTTPMethod method) 43 | { 44 | switch(method) 45 | { 46 | case HTTPMethod::Delete: 47 | return "DELETE"; 48 | case HTTPMethod::Get: 49 | return "GET"; 50 | case HTTPMethod::Head: 51 | return "HEAD"; 52 | case HTTPMethod::Post: 53 | return "POST"; 54 | case HTTPMethod::Put: 55 | return "PUT"; 56 | case HTTPMethod::Connect: 57 | return "CONNECT"; 58 | case HTTPMethod::Options: 59 | return "OPTIONS"; 60 | case HTTPMethod::Trace: 61 | return "TRACE"; 62 | case HTTPMethod::Patch: 63 | return "PATCH"; 64 | case HTTPMethod::Purge: 65 | return "PURGE"; 66 | default: 67 | return "invalid"; 68 | } 69 | return "invalid"; 70 | } 71 | 72 | enum class ParamType 73 | { 74 | INT, 75 | UINT, 76 | DOUBLE, 77 | STRING, 78 | PATH, 79 | 80 | MAX 81 | }; 82 | 83 | struct routing_params 84 | { 85 | std::vector int_params; 86 | std::vector uint_params; 87 | std::vector double_params; 88 | std::vector string_params; 89 | 90 | void debug_print() const 91 | { 92 | std::cerr << "routing_params" << std::endl; 93 | for(auto i:int_params) 94 | std::cerr< 108 | T get(unsigned) const; 109 | 110 | }; 111 | 112 | template<> 113 | inline int64_t routing_params::get(unsigned index) const 114 | { 115 | return int_params[index]; 116 | } 117 | 118 | template<> 119 | inline uint64_t routing_params::get(unsigned index) const 120 | { 121 | return uint_params[index]; 122 | } 123 | 124 | template<> 125 | inline double routing_params::get(unsigned index) const 126 | { 127 | return double_params[index]; 128 | } 129 | 130 | template<> 131 | inline std::string routing_params::get(unsigned index) const 132 | { 133 | return string_params[index]; 134 | } 135 | } 136 | 137 | #ifndef CROW_MSVC_WORKAROUND 138 | constexpr crow::HTTPMethod operator "" _method(const char* str, size_t /*len*/) 139 | { 140 | return 141 | crow::black_magic::is_equ_p(str, "GET", 3) ? crow::HTTPMethod::Get : 142 | crow::black_magic::is_equ_p(str, "DELETE", 6) ? crow::HTTPMethod::Delete : 143 | crow::black_magic::is_equ_p(str, "HEAD", 4) ? crow::HTTPMethod::Head : 144 | crow::black_magic::is_equ_p(str, "POST", 4) ? crow::HTTPMethod::Post : 145 | crow::black_magic::is_equ_p(str, "PUT", 3) ? crow::HTTPMethod::Put : 146 | crow::black_magic::is_equ_p(str, "OPTIONS", 7) ? crow::HTTPMethod::Options : 147 | crow::black_magic::is_equ_p(str, "CONNECT", 7) ? crow::HTTPMethod::Connect : 148 | crow::black_magic::is_equ_p(str, "TRACE", 5) ? crow::HTTPMethod::Trace : 149 | crow::black_magic::is_equ_p(str, "PATCH", 5) ? crow::HTTPMethod::Patch : 150 | crow::black_magic::is_equ_p(str, "PURGE", 5) ? crow::HTTPMethod::Purge : 151 | throw std::runtime_error("invalid http method"); 152 | } 153 | #endif 154 | -------------------------------------------------------------------------------- /include/crow/dumb_timer_queue.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #include "crow/logging.h" 10 | 11 | namespace crow 12 | { 13 | namespace detail 14 | { 15 | // fast timer queue for fixed tick value. 16 | class dumb_timer_queue 17 | { 18 | public: 19 | using key = std::pair; 20 | 21 | void cancel(key& k) 22 | { 23 | auto self = k.first; 24 | k.first = nullptr; 25 | if (!self) 26 | return; 27 | 28 | unsigned int index = (unsigned int)(k.second - self->step_); 29 | if (index < self->dq_.size()) 30 | self->dq_[index].second = nullptr; 31 | } 32 | 33 | key add(std::function f) 34 | { 35 | dq_.emplace_back(std::chrono::steady_clock::now(), std::move(f)); 36 | int ret = step_+dq_.size()-1; 37 | 38 | CROW_LOG_DEBUG << "timer add inside: " << this << ' ' << ret ; 39 | return {this, ret}; 40 | } 41 | 42 | void process() 43 | { 44 | if (!io_service_) 45 | return; 46 | 47 | auto now = std::chrono::steady_clock::now(); 48 | while(!dq_.empty()) 49 | { 50 | auto& x = dq_.front(); 51 | if (now - x.first < std::chrono::seconds(tick)) 52 | break; 53 | if (x.second) 54 | { 55 | CROW_LOG_DEBUG << "timer call: " << this << ' ' << step_; 56 | // we know that timer handlers are very simple currenty; call here 57 | x.second(); 58 | } 59 | dq_.pop_front(); 60 | step_++; 61 | } 62 | } 63 | 64 | void set_io_service(boost::asio::io_service& io_service) 65 | { 66 | io_service_ = &io_service; 67 | } 68 | 69 | dumb_timer_queue() noexcept 70 | { 71 | } 72 | 73 | private: 74 | 75 | int tick{5}; 76 | boost::asio::io_service* io_service_{}; 77 | std::deque>> dq_; 78 | int step_{}; 79 | }; 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /include/crow/http_request.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include "crow/common.h" 6 | #include "crow/ci_map.h" 7 | #include "crow/query_string.h" 8 | 9 | namespace crow 10 | { 11 | template 12 | inline const std::string& get_header_value(const T& headers, const std::string& key) 13 | { 14 | if (headers.count(key)) 15 | { 16 | return headers.find(key)->second; 17 | } 18 | static std::string empty; 19 | return empty; 20 | } 21 | 22 | struct DetachHelper; 23 | 24 | struct request 25 | { 26 | HTTPMethod method; 27 | std::string raw_url; 28 | std::string url; 29 | query_string url_params; 30 | ci_map headers; 31 | std::string body; 32 | 33 | void* middleware_context{}; 34 | boost::asio::io_service* io_service{}; 35 | 36 | request() 37 | : method(HTTPMethod::Get) 38 | { 39 | } 40 | 41 | request(HTTPMethod method, std::string raw_url, std::string url, query_string url_params, ci_map headers, std::string body) 42 | : method(method), raw_url(std::move(raw_url)), url(std::move(url)), url_params(std::move(url_params)), headers(std::move(headers)), body(std::move(body)) 43 | { 44 | } 45 | 46 | void add_header(std::string key, std::string value) 47 | { 48 | headers.emplace(std::move(key), std::move(value)); 49 | } 50 | 51 | const std::string& get_header_value(const std::string& key) const 52 | { 53 | return crow::get_header_value(headers, key); 54 | } 55 | 56 | template 57 | void post(CompletionHandler handler) 58 | { 59 | io_service->post(handler); 60 | } 61 | 62 | template 63 | void dispatch(CompletionHandler handler) 64 | { 65 | io_service->dispatch(handler); 66 | } 67 | 68 | }; 69 | } 70 | -------------------------------------------------------------------------------- /include/crow/http_response.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | 5 | #include "crow/json.h" 6 | #include "crow/http_request.h" 7 | #include "crow/ci_map.h" 8 | 9 | namespace crow 10 | { 11 | template 12 | class Connection; 13 | struct response 14 | { 15 | template 16 | friend class crow::Connection; 17 | 18 | int code{200}; 19 | std::string body; 20 | json::wvalue json_value; 21 | 22 | // `headers' stores HTTP headers. 23 | ci_map headers; 24 | 25 | void set_header(std::string key, std::string value) 26 | { 27 | headers.erase(key); 28 | headers.emplace(std::move(key), std::move(value)); 29 | } 30 | void add_header(std::string key, std::string value) 31 | { 32 | headers.emplace(std::move(key), std::move(value)); 33 | } 34 | 35 | const std::string& get_header_value(const std::string& key) 36 | { 37 | return crow::get_header_value(headers, key); 38 | } 39 | 40 | 41 | response() {} 42 | explicit response(int code) : code(code) {} 43 | response(std::string body) : body(std::move(body)) {} 44 | response(json::wvalue&& json_value) : json_value(std::move(json_value)) 45 | { 46 | json_mode(); 47 | } 48 | response(int code, std::string body) : code(code), body(std::move(body)) {} 49 | response(const json::wvalue& json_value) : body(json::dump(json_value)) 50 | { 51 | json_mode(); 52 | } 53 | response(int code, const json::wvalue& json_value) : code(code), body(json::dump(json_value)) 54 | { 55 | json_mode(); 56 | } 57 | 58 | response(response&& r) 59 | { 60 | *this = std::move(r); 61 | } 62 | 63 | response& operator = (const response& r) = delete; 64 | 65 | response& operator = (response&& r) noexcept 66 | { 67 | body = std::move(r.body); 68 | json_value = std::move(r.json_value); 69 | code = r.code; 70 | headers = std::move(r.headers); 71 | completed_ = r.completed_; 72 | return *this; 73 | } 74 | 75 | bool is_completed() const noexcept 76 | { 77 | return completed_; 78 | } 79 | 80 | void clear() 81 | { 82 | body.clear(); 83 | json_value.clear(); 84 | code = 200; 85 | headers.clear(); 86 | completed_ = false; 87 | } 88 | 89 | void redirect(const std::string& location) 90 | { 91 | code = 301; 92 | set_header("Location", location); 93 | } 94 | 95 | void write(const std::string& body_part) 96 | { 97 | body += body_part; 98 | } 99 | 100 | void end() 101 | { 102 | if (!completed_) 103 | { 104 | completed_ = true; 105 | 106 | if (complete_request_handler_) 107 | { 108 | complete_request_handler_(); 109 | } 110 | } 111 | } 112 | 113 | void end(const std::string& body_part) 114 | { 115 | body += body_part; 116 | end(); 117 | } 118 | 119 | bool is_alive() 120 | { 121 | return is_alive_helper_ && is_alive_helper_(); 122 | } 123 | 124 | private: 125 | bool completed_{}; 126 | std::function complete_request_handler_; 127 | std::function is_alive_helper_; 128 | 129 | //In case of a JSON object, set the Content-Type header 130 | void json_mode() 131 | { 132 | set_header("Content-Type", "application/json"); 133 | } 134 | }; 135 | } 136 | -------------------------------------------------------------------------------- /include/crow/http_server.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #ifdef CROW_ENABLE_SSL 7 | #include 8 | #endif 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | #include 15 | 16 | #include "crow/http_connection.h" 17 | #include "crow/logging.h" 18 | #include "crow/dumb_timer_queue.h" 19 | 20 | namespace crow 21 | { 22 | using namespace boost; 23 | using tcp = asio::ip::tcp; 24 | 25 | template 26 | class Server 27 | { 28 | public: 29 | Server(Handler* handler, std::string bindaddr, uint16_t port, std::tuple* middlewares = nullptr, uint16_t concurrency = 1, typename Adaptor::context* adaptor_ctx = nullptr) 30 | : acceptor_(io_service_, tcp::endpoint(boost::asio::ip::address::from_string(bindaddr), port)), 31 | signals_(io_service_, SIGINT, SIGTERM), 32 | tick_timer_(io_service_), 33 | handler_(handler), 34 | concurrency_(concurrency), 35 | port_(port), 36 | bindaddr_(bindaddr), 37 | middlewares_(middlewares), 38 | adaptor_ctx_(adaptor_ctx) 39 | { 40 | } 41 | 42 | void set_tick_function(std::chrono::milliseconds d, std::function f) 43 | { 44 | tick_interval_ = d; 45 | tick_function_ = f; 46 | } 47 | 48 | void on_tick() 49 | { 50 | tick_function_(); 51 | tick_timer_.expires_from_now(boost::posix_time::milliseconds(tick_interval_.count())); 52 | tick_timer_.async_wait([this](const boost::system::error_code& ec) 53 | { 54 | if (ec) 55 | return; 56 | on_tick(); 57 | }); 58 | } 59 | 60 | void run() 61 | { 62 | if (concurrency_ < 0) 63 | concurrency_ = 1; 64 | 65 | for(int i = 0; i < concurrency_; i++) 66 | io_service_pool_.emplace_back(new boost::asio::io_service()); 67 | get_cached_date_str_pool_.resize(concurrency_); 68 | timer_queue_pool_.resize(concurrency_); 69 | 70 | std::vector> v; 71 | std::atomic init_count(0); 72 | for(uint16_t i = 0; i < concurrency_; i ++) 73 | v.push_back( 74 | std::async(std::launch::async, [this, i, &init_count]{ 75 | 76 | // thread local date string get function 77 | auto last = std::chrono::steady_clock::now(); 78 | 79 | std::string date_str; 80 | auto update_date_str = [&] 81 | { 82 | auto last_time_t = time(0); 83 | tm my_tm; 84 | 85 | #if defined(_MSC_VER) or defined(__MINGW32__) 86 | gmtime_s(&my_tm, &last_time_t); 87 | #else 88 | gmtime_r(&last_time_t, &my_tm); 89 | #endif 90 | date_str.resize(100); 91 | size_t date_str_sz = strftime(&date_str[0], 99, "%a, %d %b %Y %H:%M:%S GMT", &my_tm); 92 | date_str.resize(date_str_sz); 93 | }; 94 | update_date_str(); 95 | get_cached_date_str_pool_[i] = [&]()->std::string 96 | { 97 | if (std::chrono::steady_clock::now() - last >= std::chrono::seconds(1)) 98 | { 99 | last = std::chrono::steady_clock::now(); 100 | update_date_str(); 101 | } 102 | return date_str; 103 | }; 104 | 105 | // initializing timer queue 106 | detail::dumb_timer_queue timer_queue; 107 | timer_queue_pool_[i] = &timer_queue; 108 | 109 | timer_queue.set_io_service(*io_service_pool_[i]); 110 | boost::asio::deadline_timer timer(*io_service_pool_[i]); 111 | timer.expires_from_now(boost::posix_time::seconds(1)); 112 | 113 | std::function handler; 114 | handler = [&](const boost::system::error_code& ec){ 115 | if (ec) 116 | return; 117 | timer_queue.process(); 118 | timer.expires_from_now(boost::posix_time::seconds(1)); 119 | timer.async_wait(handler); 120 | }; 121 | timer.async_wait(handler); 122 | 123 | init_count ++; 124 | while(1) 125 | { 126 | try 127 | { 128 | if (io_service_pool_[i]->run() == 0) 129 | { 130 | // when io_service.run returns 0, there are no more works to do. 131 | break; 132 | } 133 | } catch(std::exception& e) 134 | { 135 | CROW_LOG_ERROR << "Worker Crash: An uncaught exception occurred: " << e.what(); 136 | } 137 | } 138 | })); 139 | 140 | if (tick_function_ && tick_interval_.count() > 0) 141 | { 142 | tick_timer_.expires_from_now(boost::posix_time::milliseconds(tick_interval_.count())); 143 | tick_timer_.async_wait([this](const boost::system::error_code& ec) 144 | { 145 | if (ec) 146 | return; 147 | on_tick(); 148 | }); 149 | } 150 | 151 | CROW_LOG_INFO << server_name_ << " server is running at " << bindaddr_ <<":" << port_ 152 | << " using " << concurrency_ << " threads"; 153 | CROW_LOG_INFO << "Call `app.loglevel(crow::LogLevel::Warning)` to hide Info level logs."; 154 | 155 | signals_.async_wait( 156 | [&](const boost::system::error_code& /*error*/, int /*signal_number*/){ 157 | stop(); 158 | }); 159 | 160 | while(concurrency_ != init_count) 161 | std::this_thread::yield(); 162 | 163 | do_accept(); 164 | 165 | std::thread([this]{ 166 | io_service_.run(); 167 | CROW_LOG_INFO << "Exiting."; 168 | }).join(); 169 | } 170 | 171 | void stop() 172 | { 173 | io_service_.stop(); 174 | for(auto& io_service:io_service_pool_) 175 | io_service->stop(); 176 | } 177 | 178 | private: 179 | asio::io_service& pick_io_service() 180 | { 181 | // TODO load balancing 182 | roundrobin_index_++; 183 | if (roundrobin_index_ >= io_service_pool_.size()) 184 | roundrobin_index_ = 0; 185 | return *io_service_pool_[roundrobin_index_]; 186 | } 187 | 188 | void do_accept() 189 | { 190 | asio::io_service& is = pick_io_service(); 191 | auto p = new Connection( 192 | is, handler_, server_name_, middlewares_, 193 | get_cached_date_str_pool_[roundrobin_index_], *timer_queue_pool_[roundrobin_index_], 194 | adaptor_ctx_); 195 | acceptor_.async_accept(p->socket(), 196 | [this, p, &is](boost::system::error_code ec) 197 | { 198 | if (!ec) 199 | { 200 | is.post([p] 201 | { 202 | p->start(); 203 | }); 204 | } 205 | else 206 | { 207 | delete p; 208 | } 209 | do_accept(); 210 | }); 211 | } 212 | 213 | private: 214 | asio::io_service io_service_; 215 | std::vector> io_service_pool_; 216 | std::vector timer_queue_pool_; 217 | std::vector> get_cached_date_str_pool_; 218 | tcp::acceptor acceptor_; 219 | boost::asio::signal_set signals_; 220 | boost::asio::deadline_timer tick_timer_; 221 | 222 | Handler* handler_; 223 | uint16_t concurrency_{1}; 224 | std::string server_name_ = "Crow/0.1"; 225 | uint16_t port_; 226 | std::string bindaddr_; 227 | unsigned int roundrobin_index_{}; 228 | 229 | std::chrono::milliseconds tick_interval_; 230 | std::function tick_function_; 231 | 232 | std::tuple* middlewares_; 233 | 234 | #ifdef CROW_ENABLE_SSL 235 | bool use_ssl_{false}; 236 | boost::asio::ssl::context ssl_context_{boost::asio::ssl::context::sslv23}; 237 | #endif 238 | typename Adaptor::context* adaptor_ctx_; 239 | }; 240 | } 241 | -------------------------------------------------------------------------------- /include/crow/logging.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #include "crow/settings.h" 11 | 12 | namespace crow 13 | { 14 | enum class LogLevel 15 | { 16 | #ifndef ERROR 17 | DEBUG = 0, 18 | INFO, 19 | WARNING, 20 | ERROR, 21 | CRITICAL, 22 | #endif 23 | 24 | Debug = 0, 25 | Info, 26 | Warning, 27 | Error, 28 | Critical, 29 | }; 30 | 31 | class ILogHandler { 32 | public: 33 | virtual void log(std::string message, LogLevel level) = 0; 34 | }; 35 | 36 | class CerrLogHandler : public ILogHandler { 37 | public: 38 | void log(std::string message, LogLevel /*level*/) override { 39 | std::cerr << message; 40 | } 41 | }; 42 | 43 | class logger { 44 | 45 | private: 46 | // 47 | static std::string timestamp() 48 | { 49 | char date[32]; 50 | time_t t = time(0); 51 | 52 | tm my_tm; 53 | 54 | #if defined(_MSC_VER) or defined(__MINGW32__) 55 | gmtime_s(&my_tm, &t); 56 | #else 57 | gmtime_r(&t, &my_tm); 58 | #endif 59 | 60 | size_t sz = strftime(date, sizeof(date), "%Y-%m-%d %H:%M:%S", &my_tm); 61 | return std::string(date, date+sz); 62 | } 63 | 64 | public: 65 | 66 | 67 | logger(std::string prefix, LogLevel level) : level_(level) { 68 | #ifdef CROW_ENABLE_LOGGING 69 | stringstream_ << "(" << timestamp() << ") [" << prefix << "] "; 70 | #endif 71 | 72 | } 73 | ~logger() { 74 | #ifdef CROW_ENABLE_LOGGING 75 | if(level_ >= get_current_log_level()) { 76 | stringstream_ << std::endl; 77 | get_handler_ref()->log(stringstream_.str(), level_); 78 | } 79 | #endif 80 | } 81 | 82 | // 83 | template 84 | logger& operator<<(T const &value) { 85 | 86 | #ifdef CROW_ENABLE_LOGGING 87 | if(level_ >= get_current_log_level()) { 88 | stringstream_ << value; 89 | } 90 | #endif 91 | return *this; 92 | } 93 | 94 | // 95 | static void setLogLevel(LogLevel level) { 96 | get_log_level_ref() = level; 97 | } 98 | 99 | static void setHandler(ILogHandler* handler) { 100 | get_handler_ref() = handler; 101 | } 102 | 103 | static LogLevel get_current_log_level() { 104 | return get_log_level_ref(); 105 | } 106 | 107 | private: 108 | // 109 | static LogLevel& get_log_level_ref() 110 | { 111 | static LogLevel current_level = (LogLevel)CROW_LOG_LEVEL; 112 | return current_level; 113 | } 114 | static ILogHandler*& get_handler_ref() 115 | { 116 | static CerrLogHandler default_handler; 117 | static ILogHandler* current_handler = &default_handler; 118 | return current_handler; 119 | } 120 | 121 | // 122 | std::ostringstream stringstream_; 123 | LogLevel level_; 124 | }; 125 | } 126 | 127 | #define CROW_LOG_CRITICAL \ 128 | if (crow::logger::get_current_log_level() <= crow::LogLevel::Critical) \ 129 | crow::logger("CRITICAL", crow::LogLevel::Critical) 130 | #define CROW_LOG_ERROR \ 131 | if (crow::logger::get_current_log_level() <= crow::LogLevel::Error) \ 132 | crow::logger("ERROR ", crow::LogLevel::Error) 133 | #define CROW_LOG_WARNING \ 134 | if (crow::logger::get_current_log_level() <= crow::LogLevel::Warning) \ 135 | crow::logger("WARNING ", crow::LogLevel::Warning) 136 | #define CROW_LOG_INFO \ 137 | if (crow::logger::get_current_log_level() <= crow::LogLevel::Info) \ 138 | crow::logger("INFO ", crow::LogLevel::Info) 139 | #define CROW_LOG_DEBUG \ 140 | if (crow::logger::get_current_log_level() <= crow::LogLevel::Debug) \ 141 | crow::logger("DEBUG ", crow::LogLevel::Debug) 142 | 143 | -------------------------------------------------------------------------------- /include/crow/middleware.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include "crow/http_request.h" 4 | #include "crow/http_response.h" 5 | 6 | namespace crow 7 | { 8 | // Any middleware requires following 3 members: 9 | 10 | // struct context; 11 | // storing data for the middleware; can be read from another middleware or handlers 12 | 13 | // before_handle 14 | // called before handling the request. 15 | // if res.end() is called, the operation is halted. 16 | // (still call after_handle of this middleware) 17 | // 2 signatures: 18 | // void before_handle(request& req, response& res, context& ctx) 19 | // if you only need to access this middlewares context. 20 | // template 21 | // void before_handle(request& req, response& res, context& ctx, AllContext& all_ctx) 22 | // you can access another middlewares' context by calling `all_ctx.template get()' 23 | // ctx == all_ctx.template get() 24 | 25 | // after_handle 26 | // called after handling the request. 27 | // void after_handle(request& req, response& res, context& ctx) 28 | // template 29 | // void after_handle(request& req, response& res, context& ctx, AllContext& all_ctx) 30 | 31 | struct CookieParser 32 | { 33 | struct context 34 | { 35 | std::unordered_map jar; 36 | std::unordered_map cookies_to_add; 37 | 38 | std::string get_cookie(const std::string& key) const 39 | { 40 | auto cookie = jar.find(key); 41 | if (cookie != jar.end()) 42 | return cookie->second; 43 | return {}; 44 | } 45 | 46 | void set_cookie(const std::string& key, const std::string& value) 47 | { 48 | cookies_to_add.emplace(key, value); 49 | } 50 | }; 51 | 52 | void before_handle(request& req, response& res, context& ctx) 53 | { 54 | int count = req.headers.count("Cookie"); 55 | if (!count) 56 | return; 57 | if (count > 1) 58 | { 59 | res.code = 400; 60 | res.end(); 61 | return; 62 | } 63 | std::string cookies = req.get_header_value("Cookie"); 64 | size_t pos = 0; 65 | while(pos < cookies.size()) 66 | { 67 | size_t pos_equal = cookies.find('=', pos); 68 | if (pos_equal == cookies.npos) 69 | break; 70 | std::string name = cookies.substr(pos, pos_equal-pos); 71 | boost::trim(name); 72 | pos = pos_equal+1; 73 | while(pos < cookies.size() && cookies[pos] == ' ') pos++; 74 | if (pos == cookies.size()) 75 | break; 76 | 77 | size_t pos_semicolon = cookies.find(';', pos); 78 | std::string value = cookies.substr(pos, pos_semicolon-pos); 79 | 80 | boost::trim(value); 81 | if (value[0] == '"' && value[value.size()-1] == '"') 82 | { 83 | value = value.substr(1, value.size()-2); 84 | } 85 | 86 | ctx.jar.emplace(std::move(name), std::move(value)); 87 | 88 | pos = pos_semicolon; 89 | if (pos == cookies.npos) 90 | break; 91 | pos++; 92 | while(pos < cookies.size() && cookies[pos] == ' ') pos++; 93 | } 94 | } 95 | 96 | void after_handle(request& /*req*/, response& res, context& ctx) 97 | { 98 | for(auto& cookie:ctx.cookies_to_add) 99 | { 100 | if (cookie.second.empty()) 101 | res.add_header("Set-Cookie", cookie.first + "=\"\""); 102 | else 103 | res.add_header("Set-Cookie", cookie.first + "=" + cookie.second); 104 | } 105 | } 106 | }; 107 | 108 | /* 109 | App app; 110 | A B C 111 | A::context 112 | int aa; 113 | 114 | ctx1 : public A::context 115 | ctx2 : public ctx1, public B::context 116 | ctx3 : public ctx2, public C::context 117 | 118 | C depends on A 119 | 120 | C::handle 121 | context.aaa 122 | 123 | App::context : private CookieParser::contetx, ... 124 | { 125 | jar 126 | 127 | } 128 | 129 | SimpleApp 130 | */ 131 | } 132 | -------------------------------------------------------------------------------- /include/crow/middleware_context.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "crow/utility.h" 4 | #include "crow/http_request.h" 5 | #include "crow/http_response.h" 6 | 7 | namespace crow 8 | { 9 | namespace detail 10 | { 11 | template 12 | struct partial_context 13 | : public black_magic::pop_back::template rebind 14 | , public black_magic::last_element_type::type::context 15 | { 16 | using parent_context = typename black_magic::pop_back::template rebind<::crow::detail::partial_context>; 17 | template 18 | using partial = typename std::conditional>::type; 19 | 20 | template 21 | typename T::context& get() 22 | { 23 | return static_cast(*this); 24 | } 25 | }; 26 | 27 | template <> 28 | struct partial_context<> 29 | { 30 | template 31 | using partial = partial_context; 32 | }; 33 | 34 | template 35 | bool middleware_call_helper(Container& middlewares, request& req, response& res, Context& ctx); 36 | 37 | template 38 | struct context : private partial_context 39 | //struct context : private Middlewares::context... // simple but less type-safe 40 | { 41 | template 42 | friend typename std::enable_if<(N==0)>::type after_handlers_call_helper(Container& middlewares, Context& ctx, request& req, response& res); 43 | template 44 | friend typename std::enable_if<(N>0)>::type after_handlers_call_helper(Container& middlewares, Context& ctx, request& req, response& res); 45 | 46 | template 47 | friend bool middleware_call_helper(Container& middlewares, request& req, response& res, Context& ctx); 48 | 49 | template 50 | typename T::context& get() 51 | { 52 | return static_cast(*this); 53 | } 54 | 55 | template 56 | using partial = typename partial_context::template partial; 57 | }; 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /include/crow/parser.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #include "crow/http_parser_merged.h" 9 | #include "crow/http_request.h" 10 | 11 | namespace crow 12 | { 13 | template 14 | struct HTTPParser : public http_parser 15 | { 16 | static int on_message_begin(http_parser* self_) 17 | { 18 | HTTPParser* self = static_cast(self_); 19 | self->clear(); 20 | return 0; 21 | } 22 | static int on_url(http_parser* self_, const char* at, size_t length) 23 | { 24 | HTTPParser* self = static_cast(self_); 25 | self->raw_url.insert(self->raw_url.end(), at, at+length); 26 | return 0; 27 | } 28 | static int on_header_field(http_parser* self_, const char* at, size_t length) 29 | { 30 | HTTPParser* self = static_cast(self_); 31 | switch (self->header_building_state) 32 | { 33 | case 0: 34 | if (!self->header_value.empty()) 35 | { 36 | self->headers.emplace(std::move(self->header_field), std::move(self->header_value)); 37 | } 38 | self->header_field.assign(at, at+length); 39 | self->header_building_state = 1; 40 | break; 41 | case 1: 42 | self->header_field.insert(self->header_field.end(), at, at+length); 43 | break; 44 | } 45 | return 0; 46 | } 47 | static int on_header_value(http_parser* self_, const char* at, size_t length) 48 | { 49 | HTTPParser* self = static_cast(self_); 50 | switch (self->header_building_state) 51 | { 52 | case 0: 53 | self->header_value.insert(self->header_value.end(), at, at+length); 54 | break; 55 | case 1: 56 | self->header_building_state = 0; 57 | self->header_value.assign(at, at+length); 58 | break; 59 | } 60 | return 0; 61 | } 62 | static int on_headers_complete(http_parser* self_) 63 | { 64 | HTTPParser* self = static_cast(self_); 65 | if (!self->header_field.empty()) 66 | { 67 | self->headers.emplace(std::move(self->header_field), std::move(self->header_value)); 68 | } 69 | self->process_header(); 70 | return 0; 71 | } 72 | static int on_body(http_parser* self_, const char* at, size_t length) 73 | { 74 | HTTPParser* self = static_cast(self_); 75 | self->body.insert(self->body.end(), at, at+length); 76 | return 0; 77 | } 78 | static int on_message_complete(http_parser* self_) 79 | { 80 | HTTPParser* self = static_cast(self_); 81 | 82 | // url params 83 | self->url = self->raw_url.substr(0, self->raw_url.find("?")); 84 | self->url_params = query_string(self->raw_url); 85 | 86 | self->process_message(); 87 | return 0; 88 | } 89 | HTTPParser(Handler* handler) : 90 | handler_(handler) 91 | { 92 | http_parser_init(this, HTTP_REQUEST); 93 | } 94 | 95 | // return false on error 96 | bool feed(const char* buffer, int length) 97 | { 98 | const static http_parser_settings settings_{ 99 | on_message_begin, 100 | on_url, 101 | nullptr, 102 | on_header_field, 103 | on_header_value, 104 | on_headers_complete, 105 | on_body, 106 | on_message_complete, 107 | }; 108 | 109 | int nparsed = http_parser_execute(this, &settings_, buffer, length); 110 | return nparsed == length; 111 | } 112 | 113 | bool done() 114 | { 115 | return feed(nullptr, 0); 116 | } 117 | 118 | void clear() 119 | { 120 | url.clear(); 121 | raw_url.clear(); 122 | header_building_state = 0; 123 | header_field.clear(); 124 | header_value.clear(); 125 | headers.clear(); 126 | url_params.clear(); 127 | body.clear(); 128 | } 129 | 130 | void process_header() 131 | { 132 | handler_->handle_header(); 133 | } 134 | 135 | void process_message() 136 | { 137 | handler_->handle(); 138 | } 139 | 140 | request to_request() const 141 | { 142 | return request{(HTTPMethod)method, std::move(raw_url), std::move(url), std::move(url_params), std::move(headers), std::move(body)}; 143 | } 144 | 145 | bool is_upgrade() const 146 | { 147 | return upgrade; 148 | } 149 | 150 | bool check_version(int major, int minor) const 151 | { 152 | return http_major == major && http_minor == minor; 153 | } 154 | 155 | std::string raw_url; 156 | std::string url; 157 | 158 | int header_building_state = 0; 159 | std::string header_field; 160 | std::string header_value; 161 | ci_map headers; 162 | query_string url_params; 163 | std::string body; 164 | 165 | Handler* handler_; 166 | }; 167 | } 168 | -------------------------------------------------------------------------------- /include/crow/query_string.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | namespace crow 12 | { 13 | // ---------------------------------------------------------------------------- 14 | // qs_parse (modified) 15 | // https://github.com/bartgrantham/qs_parse 16 | // ---------------------------------------------------------------------------- 17 | /* Similar to strncmp, but handles URL-encoding for either string */ 18 | int qs_strncmp(const char * s, const char * qs, size_t n); 19 | 20 | 21 | /* Finds the beginning of each key/value pair and stores a pointer in qs_kv. 22 | * Also decodes the value portion of the k/v pair *in-place*. In a future 23 | * enhancement it will also have a compile-time option of sorting qs_kv 24 | * alphabetically by key. */ 25 | int qs_parse(char * qs, char * qs_kv[], int qs_kv_size); 26 | 27 | 28 | /* Used by qs_parse to decode the value portion of a k/v pair */ 29 | int qs_decode(char * qs); 30 | 31 | 32 | /* Looks up the value according to the key on a pre-processed query string 33 | * A future enhancement will be a compile-time option to look up the key 34 | * in a pre-sorted qs_kv array via a binary search. */ 35 | //char * qs_k2v(const char * key, char * qs_kv[], int qs_kv_size); 36 | char * qs_k2v(const char * key, char * const * qs_kv, int qs_kv_size, int nth); 37 | 38 | 39 | /* Non-destructive lookup of value, based on key. User provides the 40 | * destinaton string and length. */ 41 | char * qs_scanvalue(const char * key, const char * qs, char * val, size_t val_len); 42 | 43 | // TODO: implement sorting of the qs_kv array; for now ensure it's not compiled 44 | #undef _qsSORTING 45 | 46 | // isxdigit _is_ available in , but let's avoid another header instead 47 | #define CROW_QS_ISHEX(x) ((((x)>='0'&&(x)<='9') || ((x)>='A'&&(x)<='F') || ((x)>='a'&&(x)<='f')) ? 1 : 0) 48 | #define CROW_QS_HEX2DEC(x) (((x)>='0'&&(x)<='9') ? (x)-48 : ((x)>='A'&&(x)<='F') ? (x)-55 : ((x)>='a'&&(x)<='f') ? (x)-87 : 0) 49 | #define CROW_QS_ISQSCHR(x) ((((x)=='=')||((x)=='#')||((x)=='&')||((x)=='\0')) ? 0 : 1) 50 | 51 | inline int qs_strncmp(const char * s, const char * qs, size_t n) 52 | { 53 | int i=0; 54 | unsigned char u1, u2, unyb, lnyb; 55 | 56 | while(n-- > 0) 57 | { 58 | u1 = (unsigned char) *s++; 59 | u2 = (unsigned char) *qs++; 60 | 61 | if ( ! CROW_QS_ISQSCHR(u1) ) { u1 = '\0'; } 62 | if ( ! CROW_QS_ISQSCHR(u2) ) { u2 = '\0'; } 63 | 64 | if ( u1 == '+' ) { u1 = ' '; } 65 | if ( u1 == '%' ) // easier/safer than scanf 66 | { 67 | unyb = (unsigned char) *s++; 68 | lnyb = (unsigned char) *s++; 69 | if ( CROW_QS_ISHEX(unyb) && CROW_QS_ISHEX(lnyb) ) 70 | u1 = (CROW_QS_HEX2DEC(unyb) * 16) + CROW_QS_HEX2DEC(lnyb); 71 | else 72 | u1 = '\0'; 73 | } 74 | 75 | if ( u2 == '+' ) { u2 = ' '; } 76 | if ( u2 == '%' ) // easier/safer than scanf 77 | { 78 | unyb = (unsigned char) *qs++; 79 | lnyb = (unsigned char) *qs++; 80 | if ( CROW_QS_ISHEX(unyb) && CROW_QS_ISHEX(lnyb) ) 81 | u2 = (CROW_QS_HEX2DEC(unyb) * 16) + CROW_QS_HEX2DEC(lnyb); 82 | else 83 | u2 = '\0'; 84 | } 85 | 86 | if ( u1 != u2 ) 87 | return u1 - u2; 88 | if ( u1 == '\0' ) 89 | return 0; 90 | i++; 91 | } 92 | if ( CROW_QS_ISQSCHR(*qs) ) 93 | return -1; 94 | else 95 | return 0; 96 | } 97 | 98 | 99 | inline int qs_parse(char * qs, char * qs_kv[], int qs_kv_size) 100 | { 101 | int i, j; 102 | char * substr_ptr; 103 | 104 | for(i=0; i means x iterations of this loop -> means *x+1* k/v pairs 123 | 124 | // we only decode the values in place, the keys could have '='s in them 125 | // which will hose our ability to distinguish keys from values later 126 | for(j=0; j> qs_dict_name2kv(const char * dict_name, char * const * qs_kv, int qs_kv_size, int nth = 0) 203 | { 204 | int i; 205 | size_t name_len, skip_to_eq, skip_to_brace_open, skip_to_brace_close; 206 | 207 | name_len = strlen(dict_name); 208 | 209 | #ifdef _qsSORTING 210 | // TODO: binary search for key in the sorted qs_kv 211 | #else // _qsSORTING 212 | for(i=0; i 0 && 226 | skip_to_brace_close > 0 && 227 | nth == 0 ) 228 | { 229 | auto key = std::string(qs_kv[i] + skip_to_brace_open, skip_to_brace_close - skip_to_brace_open); 230 | auto value = std::string(qs_kv[i] + skip_to_eq); 231 | return boost::make_optional(std::make_pair(key, value)); 232 | } 233 | else 234 | { 235 | --nth; 236 | } 237 | } 238 | } 239 | #endif // _qsSORTING 240 | 241 | return boost::none; 242 | } 243 | 244 | 245 | inline char * qs_scanvalue(const char * key, const char * qs, char * val, size_t val_len) 246 | { 247 | size_t i, key_len; 248 | const char * tmp; 249 | 250 | // find the beginning of the k/v substrings 251 | if ( (tmp = strchr(qs, '?')) != NULL ) 252 | qs = tmp + 1; 253 | 254 | key_len = strlen(key); 255 | while(qs[0] != '#' && qs[0] != '\0') 256 | { 257 | if ( qs_strncmp(key, qs, key_len) == 0 ) 258 | break; 259 | qs += strcspn(qs, "&") + 1; 260 | } 261 | 262 | if ( qs[0] == '\0' ) return NULL; 263 | 264 | qs += strcspn(qs, "=&#"); 265 | if ( qs[0] == '=' ) 266 | { 267 | qs++; 268 | i = strcspn(qs, "&=#"); 269 | #ifdef _MSC_VER 270 | strncpy_s(val, val_len, qs, (val_len - 1)<(i + 1) ? (val_len - 1) : (i + 1)); 271 | #else 272 | strncpy(val, qs, (val_len - 1)<(i + 1) ? (val_len - 1) : (i + 1)); 273 | #endif 274 | qs_decode(val); 275 | } 276 | else 277 | { 278 | if ( val_len > 0 ) 279 | val[0] = '\0'; 280 | } 281 | 282 | return val; 283 | } 284 | } 285 | // ---------------------------------------------------------------------------- 286 | 287 | 288 | namespace crow 289 | { 290 | class query_string 291 | { 292 | public: 293 | static const int MAX_KEY_VALUE_PAIRS_COUNT = 256; 294 | 295 | query_string() 296 | { 297 | 298 | } 299 | 300 | query_string(const query_string& qs) 301 | : url_(qs.url_) 302 | { 303 | for(auto p:qs.key_value_pairs_) 304 | { 305 | key_value_pairs_.push_back((char*)(p-qs.url_.c_str()+url_.c_str())); 306 | } 307 | } 308 | 309 | query_string& operator = (const query_string& qs) 310 | { 311 | url_ = qs.url_; 312 | key_value_pairs_.clear(); 313 | for(auto p:qs.key_value_pairs_) 314 | { 315 | key_value_pairs_.push_back((char*)(p-qs.url_.c_str()+url_.c_str())); 316 | } 317 | return *this; 318 | } 319 | 320 | query_string& operator = (query_string&& qs) 321 | { 322 | key_value_pairs_ = std::move(qs.key_value_pairs_); 323 | char* old_data = (char*)qs.url_.c_str(); 324 | url_ = std::move(qs.url_); 325 | for(auto& p:key_value_pairs_) 326 | { 327 | p += (char*)url_.c_str() - old_data; 328 | } 329 | return *this; 330 | } 331 | 332 | 333 | query_string(std::string url) 334 | : url_(std::move(url)) 335 | { 336 | if (url_.empty()) 337 | return; 338 | 339 | key_value_pairs_.resize(MAX_KEY_VALUE_PAIRS_COUNT); 340 | 341 | int count = qs_parse(&url_[0], &key_value_pairs_[0], MAX_KEY_VALUE_PAIRS_COUNT); 342 | key_value_pairs_.resize(count); 343 | } 344 | 345 | void clear() 346 | { 347 | key_value_pairs_.clear(); 348 | url_.clear(); 349 | } 350 | 351 | friend std::ostream& operator<<(std::ostream& os, const query_string& qs) 352 | { 353 | os << "[ "; 354 | for(size_t i = 0; i < qs.key_value_pairs_.size(); ++i) { 355 | if (i) 356 | os << ", "; 357 | os << qs.key_value_pairs_[i]; 358 | } 359 | os << " ]"; 360 | return os; 361 | 362 | } 363 | 364 | char* get (const std::string& name) const 365 | { 366 | char* ret = qs_k2v(name.c_str(), key_value_pairs_.data(), key_value_pairs_.size()); 367 | return ret; 368 | } 369 | 370 | std::vector get_list (const std::string& name) const 371 | { 372 | std::vector ret; 373 | std::string plus = name + "[]"; 374 | char* element = nullptr; 375 | 376 | int count = 0; 377 | while(1) 378 | { 379 | element = qs_k2v(plus.c_str(), key_value_pairs_.data(), key_value_pairs_.size(), count++); 380 | if (!element) 381 | break; 382 | ret.push_back(element); 383 | } 384 | return ret; 385 | } 386 | 387 | std::unordered_map get_dict (const std::string& name) const 388 | { 389 | std::unordered_map ret; 390 | 391 | int count = 0; 392 | while(1) 393 | { 394 | if (auto element = qs_dict_name2kv(name.c_str(), key_value_pairs_.data(), key_value_pairs_.size(), count++)) 395 | ret.insert(*element); 396 | else 397 | break; 398 | } 399 | return ret; 400 | } 401 | 402 | private: 403 | std::string url_; 404 | std::vector key_value_pairs_; 405 | }; 406 | 407 | } // end namespace 408 | -------------------------------------------------------------------------------- /include/crow/settings.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | // settings for crow 3 | // TODO - replace with runtime config. libucl? 4 | 5 | /* #ifdef - enables debug mode */ 6 | //#define CROW_ENABLE_DEBUG 7 | 8 | /* #ifdef - enables logging */ 9 | #define CROW_ENABLE_LOGGING 10 | 11 | /* #ifdef - enables ssl */ 12 | //#define CROW_ENABLE_SSL 13 | 14 | /* #define - specifies log level */ 15 | /* 16 | Debug = 0 17 | Info = 1 18 | Warning = 2 19 | Error = 3 20 | Critical = 4 21 | 22 | default to INFO 23 | */ 24 | #ifndef CROW_LOG_LEVEL 25 | #define CROW_LOG_LEVEL 1 26 | #endif 27 | 28 | 29 | // compiler flags 30 | #if __cplusplus >= 201402L 31 | #define CROW_CAN_USE_CPP14 32 | #endif 33 | 34 | #if defined(_MSC_VER) 35 | #if _MSC_VER < 1900 36 | #define CROW_MSVC_WORKAROUND 37 | #define constexpr const 38 | #define noexcept throw() 39 | #endif 40 | #endif 41 | -------------------------------------------------------------------------------- /include/crow/socket_adaptors.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #ifdef CROW_ENABLE_SSL 4 | #include 5 | #endif 6 | #include "crow/settings.h" 7 | namespace crow 8 | { 9 | using namespace boost; 10 | using tcp = asio::ip::tcp; 11 | 12 | struct SocketAdaptor 13 | { 14 | using context = void; 15 | SocketAdaptor(boost::asio::io_service& io_service, context*) 16 | : socket_(io_service) 17 | { 18 | } 19 | 20 | boost::asio::io_service& get_io_service() 21 | { 22 | return socket_.get_io_service(); 23 | } 24 | 25 | tcp::socket& raw_socket() 26 | { 27 | return socket_; 28 | } 29 | 30 | tcp::socket& socket() 31 | { 32 | return socket_; 33 | } 34 | 35 | tcp::endpoint remote_endpoint() 36 | { 37 | return socket_.remote_endpoint(); 38 | } 39 | 40 | bool is_open() 41 | { 42 | return socket_.is_open(); 43 | } 44 | 45 | void close() 46 | { 47 | boost::system::error_code ec; 48 | socket_.close(ec); 49 | } 50 | 51 | template 52 | void start(F f) 53 | { 54 | f(boost::system::error_code()); 55 | } 56 | 57 | tcp::socket socket_; 58 | }; 59 | 60 | #ifdef CROW_ENABLE_SSL 61 | struct SSLAdaptor 62 | { 63 | using context = boost::asio::ssl::context; 64 | using ssl_socket_t = boost::asio::ssl::stream; 65 | SSLAdaptor(boost::asio::io_service& io_service, context* ctx) 66 | : ssl_socket_(new ssl_socket_t(io_service, *ctx)) 67 | { 68 | } 69 | 70 | boost::asio::ssl::stream& socket() 71 | { 72 | return *ssl_socket_; 73 | } 74 | 75 | tcp::socket::lowest_layer_type& 76 | raw_socket() 77 | { 78 | return ssl_socket_->lowest_layer(); 79 | } 80 | 81 | tcp::endpoint remote_endpoint() 82 | { 83 | return raw_socket().remote_endpoint(); 84 | } 85 | 86 | bool is_open() 87 | { 88 | return raw_socket().is_open(); 89 | } 90 | 91 | void close() 92 | { 93 | boost::system::error_code ec; 94 | raw_socket().close(ec); 95 | } 96 | 97 | boost::asio::io_service& get_io_service() 98 | { 99 | return raw_socket().get_io_service(); 100 | } 101 | 102 | template 103 | void start(F f) 104 | { 105 | ssl_socket_->async_handshake(boost::asio::ssl::stream_base::server, 106 | [f](const boost::system::error_code& ec) { 107 | f(ec); 108 | }); 109 | } 110 | 111 | std::unique_ptr> ssl_socket_; 112 | }; 113 | #endif 114 | } 115 | -------------------------------------------------------------------------------- /include/crow/utility.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | #include "crow/settings.h" 12 | 13 | namespace crow 14 | { 15 | namespace black_magic 16 | { 17 | #ifndef CROW_MSVC_WORKAROUND 18 | struct OutOfRange 19 | { 20 | OutOfRange(unsigned /*pos*/, unsigned /*length*/) {} 21 | }; 22 | constexpr unsigned requires_in_range( unsigned i, unsigned len ) 23 | { 24 | return i >= len ? throw OutOfRange(i, len) : i; 25 | } 26 | 27 | class const_str 28 | { 29 | const char * const begin_; 30 | unsigned size_; 31 | 32 | public: 33 | template< unsigned N > 34 | constexpr const_str( const char(&arr)[N] ) : begin_(arr), size_(N - 1) { 35 | static_assert( N >= 1, "not a string literal"); 36 | } 37 | constexpr char operator[]( unsigned i ) const { 38 | return requires_in_range(i, size_), begin_[i]; 39 | } 40 | 41 | constexpr operator const char *() const { 42 | return begin_; 43 | } 44 | 45 | constexpr const char* begin() const { return begin_; } 46 | constexpr const char* end() const { return begin_ + size_; } 47 | 48 | constexpr unsigned size() const { 49 | return size_; 50 | } 51 | }; 52 | 53 | constexpr unsigned find_closing_tag(const_str s, unsigned p) 54 | { 55 | return s[p] == '>' ? p : find_closing_tag(s, p+1); 56 | } 57 | 58 | constexpr bool is_valid(const_str s, unsigned i = 0, int f = 0) 59 | { 60 | return 61 | i == s.size() 62 | ? f == 0 : 63 | f < 0 || f >= 2 64 | ? false : 65 | s[i] == '<' 66 | ? is_valid(s, i+1, f+1) : 67 | s[i] == '>' 68 | ? is_valid(s, i+1, f-1) : 69 | is_valid(s, i+1, f); 70 | } 71 | 72 | constexpr bool is_equ_p(const char* a, const char* b, unsigned n) 73 | { 74 | return 75 | *a == 0 && *b == 0 && n == 0 76 | ? true : 77 | (*a == 0 || *b == 0) 78 | ? false : 79 | n == 0 80 | ? true : 81 | *a != *b 82 | ? false : 83 | is_equ_p(a+1, b+1, n-1); 84 | } 85 | 86 | constexpr bool is_equ_n(const_str a, unsigned ai, const_str b, unsigned bi, unsigned n) 87 | { 88 | return 89 | ai + n > a.size() || bi + n > b.size() 90 | ? false : 91 | n == 0 92 | ? true : 93 | a[ai] != b[bi] 94 | ? false : 95 | is_equ_n(a,ai+1,b,bi+1,n-1); 96 | } 97 | 98 | constexpr bool is_int(const_str s, unsigned i) 99 | { 100 | return is_equ_n(s, i, "", 0, 5); 101 | } 102 | 103 | constexpr bool is_uint(const_str s, unsigned i) 104 | { 105 | return is_equ_n(s, i, "", 0, 6); 106 | } 107 | 108 | constexpr bool is_float(const_str s, unsigned i) 109 | { 110 | return is_equ_n(s, i, "", 0, 7) || 111 | is_equ_n(s, i, "", 0, 8); 112 | } 113 | 114 | constexpr bool is_str(const_str s, unsigned i) 115 | { 116 | return is_equ_n(s, i, "", 0, 5) || 117 | is_equ_n(s, i, "", 0, 8); 118 | } 119 | 120 | constexpr bool is_path(const_str s, unsigned i) 121 | { 122 | return is_equ_n(s, i, "", 0, 6); 123 | } 124 | #endif 125 | template 126 | struct parameter_tag 127 | { 128 | static const int value = 0; 129 | }; 130 | #define CROW_INTERNAL_PARAMETER_TAG(t, i) \ 131 | template <> \ 132 | struct parameter_tag \ 133 | { \ 134 | static const int value = i; \ 135 | } 136 | CROW_INTERNAL_PARAMETER_TAG(int, 1); 137 | CROW_INTERNAL_PARAMETER_TAG(char, 1); 138 | CROW_INTERNAL_PARAMETER_TAG(short, 1); 139 | CROW_INTERNAL_PARAMETER_TAG(long, 1); 140 | CROW_INTERNAL_PARAMETER_TAG(long long, 1); 141 | CROW_INTERNAL_PARAMETER_TAG(unsigned int, 2); 142 | CROW_INTERNAL_PARAMETER_TAG(unsigned char, 2); 143 | CROW_INTERNAL_PARAMETER_TAG(unsigned short, 2); 144 | CROW_INTERNAL_PARAMETER_TAG(unsigned long, 2); 145 | CROW_INTERNAL_PARAMETER_TAG(unsigned long long, 2); 146 | CROW_INTERNAL_PARAMETER_TAG(double, 3); 147 | CROW_INTERNAL_PARAMETER_TAG(std::string, 4); 148 | #undef CROW_INTERNAL_PARAMETER_TAG 149 | template 150 | struct compute_parameter_tag_from_args_list; 151 | 152 | template <> 153 | struct compute_parameter_tag_from_args_list<> 154 | { 155 | static const int value = 0; 156 | }; 157 | 158 | template 159 | struct compute_parameter_tag_from_args_list 160 | { 161 | static const int sub_value = 162 | compute_parameter_tag_from_args_list::value; 163 | static const int value = 164 | parameter_tag::type>::value 165 | ? sub_value* 6 + parameter_tag::type>::value 166 | : sub_value; 167 | }; 168 | 169 | static inline bool is_parameter_tag_compatible(uint64_t a, uint64_t b) 170 | { 171 | if (a == 0) 172 | return b == 0; 173 | if (b == 0) 174 | return a == 0; 175 | int sa = a%6; 176 | int sb = a%6; 177 | if (sa == 5) sa = 4; 178 | if (sb == 5) sb = 4; 179 | if (sa != sb) 180 | return false; 181 | return is_parameter_tag_compatible(a/6, b/6); 182 | } 183 | 184 | static inline unsigned find_closing_tag_runtime(const char* s, unsigned p) 185 | { 186 | return 187 | s[p] == 0 188 | ? throw std::runtime_error("unmatched tag <") : 189 | s[p] == '>' 190 | ? p : find_closing_tag_runtime(s, p + 1); 191 | } 192 | 193 | static inline uint64_t get_parameter_tag_runtime(const char* s, unsigned p = 0) 194 | { 195 | return 196 | s[p] == 0 197 | ? 0 : 198 | s[p] == '<' ? ( 199 | std::strncmp(s+p, "", 5) == 0 200 | ? get_parameter_tag_runtime(s, find_closing_tag_runtime(s, p)) * 6 + 1 : 201 | std::strncmp(s+p, "", 6) == 0 202 | ? get_parameter_tag_runtime(s, find_closing_tag_runtime(s, p)) * 6 + 2 : 203 | (std::strncmp(s+p, "", 7) == 0 || 204 | std::strncmp(s+p, "", 8) == 0) 205 | ? get_parameter_tag_runtime(s, find_closing_tag_runtime(s, p)) * 6 + 3 : 206 | (std::strncmp(s+p, "", 5) == 0 || 207 | std::strncmp(s+p, "", 8) == 0) 208 | ? get_parameter_tag_runtime(s, find_closing_tag_runtime(s, p)) * 6 + 4 : 209 | std::strncmp(s+p, "", 6) == 0 210 | ? get_parameter_tag_runtime(s, find_closing_tag_runtime(s, p)) * 6 + 5 : 211 | throw std::runtime_error("invalid parameter type") 212 | ) : 213 | get_parameter_tag_runtime(s, p+1); 214 | } 215 | #ifndef CROW_MSVC_WORKAROUND 216 | constexpr uint64_t get_parameter_tag(const_str s, unsigned p = 0) 217 | { 218 | return 219 | p == s.size() 220 | ? 0 : 221 | s[p] == '<' ? ( 222 | is_int(s, p) 223 | ? get_parameter_tag(s, find_closing_tag(s, p)) * 6 + 1 : 224 | is_uint(s, p) 225 | ? get_parameter_tag(s, find_closing_tag(s, p)) * 6 + 2 : 226 | is_float(s, p) 227 | ? get_parameter_tag(s, find_closing_tag(s, p)) * 6 + 3 : 228 | is_str(s, p) 229 | ? get_parameter_tag(s, find_closing_tag(s, p)) * 6 + 4 : 230 | is_path(s, p) 231 | ? get_parameter_tag(s, find_closing_tag(s, p)) * 6 + 5 : 232 | throw std::runtime_error("invalid parameter type") 233 | ) : 234 | get_parameter_tag(s, p+1); 235 | } 236 | #endif 237 | 238 | template 239 | struct S 240 | { 241 | template 242 | using push = S; 243 | template 244 | using push_back = S; 245 | template class U> 246 | using rebind = U; 247 | }; 248 | template 249 | struct CallHelper; 250 | template 251 | struct CallHelper> 252 | { 253 | template ()(std::declval()...)) 255 | > 256 | static char __test(int); 257 | 258 | template 259 | static int __test(...); 260 | 261 | static constexpr bool value = sizeof(__test(0)) == sizeof(char); 262 | }; 263 | 264 | 265 | template 266 | struct single_tag_to_type 267 | { 268 | }; 269 | 270 | template <> 271 | struct single_tag_to_type<1> 272 | { 273 | using type = int64_t; 274 | }; 275 | 276 | template <> 277 | struct single_tag_to_type<2> 278 | { 279 | using type = uint64_t; 280 | }; 281 | 282 | template <> 283 | struct single_tag_to_type<3> 284 | { 285 | using type = double; 286 | }; 287 | 288 | template <> 289 | struct single_tag_to_type<4> 290 | { 291 | using type = std::string; 292 | }; 293 | 294 | template <> 295 | struct single_tag_to_type<5> 296 | { 297 | using type = std::string; 298 | }; 299 | 300 | 301 | template 302 | struct arguments 303 | { 304 | using subarguments = typename arguments::type; 305 | using type = 306 | typename subarguments::template push::type>; 307 | }; 308 | 309 | template <> 310 | struct arguments<0> 311 | { 312 | using type = S<>; 313 | }; 314 | 315 | template 316 | struct last_element_type 317 | { 318 | using type = typename std::tuple_element>::type; 319 | }; 320 | 321 | 322 | template <> 323 | struct last_element_type<> 324 | { 325 | }; 326 | 327 | 328 | // from http://stackoverflow.com/questions/13072359/c11-compile-time-array-with-logarithmic-evaluation-depth 329 | template using Invoke = typename T::type; 330 | 331 | template struct seq{ using type = seq; }; 332 | 333 | template struct concat; 334 | 335 | template 336 | struct concat, seq> 337 | : seq{}; 338 | 339 | template 340 | using Concat = Invoke>; 341 | 342 | template struct gen_seq; 343 | template using GenSeq = Invoke>; 344 | 345 | template 346 | struct gen_seq : Concat, GenSeq>{}; 347 | 348 | template<> struct gen_seq<0> : seq<>{}; 349 | template<> struct gen_seq<1> : seq<0>{}; 350 | 351 | template 352 | struct pop_back_helper; 353 | 354 | template 355 | struct pop_back_helper, Tuple> 356 | { 357 | template