diff --git a/.drone.yml b/.drone.yml
index a649d5057..fa19175ed 100644
--- a/.drone.yml
+++ b/.drone.yml
@@ -15,7 +15,7 @@ steps:
commands:
- export DEBIAN_FRONTEND=noninteractive
- - export TRAVIS_BRANCH=$DRONE_SOURCE_BRANCH
+ - export TRAVIS_BRANCH=$DRONE_BRANCH
- export TRAVIS_JOB_ID=$DRONE_BUILD_NUMBER
- apt-get -y update
- apt-get -y install libboost-all-dev doxygen mkdocs graphviz zlib1g-dev gcc clang make cmake python3 python3-pip git
diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml
new file mode 100644
index 000000000..dff867dc5
--- /dev/null
+++ b/.github/FUNDING.yml
@@ -0,0 +1,3 @@
+# These are supported funding model platforms
+
+open_collective: crow
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 1deb47b6d..55b39a1bb 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -94,3 +94,9 @@ include(CMakePackageConfigHelpers)
configure_package_config_file("${CMAKE_CURRENT_SOURCE_DIR}/cmake/CrowConfig.cmake.in" "${CMAKE_CURRENT_BINARY_DIR}/CrowConfig.cmake" INSTALL_DESTINATION lib/cmake/Crow)
write_basic_package_version_file("${CMAKE_CURRENT_BINARY_DIR}/CrowConfigVersion.cmake" VERSION 0.2 COMPATIBILITY AnyNewerVersion)
install(FILES "${CMAKE_CURRENT_BINARY_DIR}/CrowConfig.cmake" "${CMAKE_CURRENT_BINARY_DIR}/CrowConfigVersion.cmake" DESTINATION lib/cmake/Crow)
+
+set(CPACK_GENERATOR "DEB")
+set(CPACK_DEBIAN_PACKAGE_MAINTAINER "CrowCpp")
+set(CPACK_DEBIAN_PACKAGE_SHLIBDEPS ON)
+
+include(CPack)
diff --git a/LICENSE b/LICENSE
index 7295908f7..9b0cb0f68 100644
--- a/LICENSE
+++ b/LICENSE
@@ -1,4 +1,5 @@
-Copyright (c) 2014, ipkn
+Copyright (c) 2014-2017, ipkn
+ 2020-2021, CrowCpp
All rights reserved.
Redistribution and use in source and binary forms, with or without
@@ -25,3 +26,5 @@ SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+The Crow logo and other graphic material (excluding third party logos) used are under Copyright (c) 2021, Farook Al-Sammarraie (The-EDev), All rights reserved.
diff --git a/README.md b/README.md
index 15d8e8292..28b1f1e26 100644
--- a/README.md
+++ b/README.md
@@ -1,11 +1,12 @@
-
+
A Fast and Easy to use microframework for the web.
-
-
-
+
+
+
+
@@ -29,12 +30,13 @@ Crow is a C++ microframework for running web services. It uses routing similar t
- [HTTP/2 support](https://github.com/crowcpp/crow/issues/8)
## Documentation
-Available [here](https://crowcpp.github.io/crow).
+Available [here](https://crowcpp.org).
## Examples
#### Hello World
-```c++
+```cpp
+#define CROW_MAIN
#include "crow.h"
int main()
@@ -97,12 +99,13 @@ CROW_ROUTE(app, "/add_json")
More examples can be found [here](https://github.com/crowcpp/crow/tree/master/examples).
## Setting Up / Building
-Available [here](https://crowcpp.github.io/crow/getting_started/setup).
-
+Available [here](https://crowcpp.org/getting_started/setup).
-### Attributions
+## Disclaimer
+CrowCpp/Crow is a project based on ipkn/crow. Neither CrowCpp, it's members, or this project have been associated with, or endorsed or supported by ipkn (Jaeseung Ha) in any way. We do use ipkn/crow's source code under the BSD-3 clause license and sometimes refer to the public comments available on the github repository. But we do not in any way claim to be associated with or in contact with ipkn (Jaeseung Ha) regarding CrowCpp or CrowCpp/Crow
-Crow uses the following libraries.
+## Attributions
+Crow has incorporated the following libraries into its source.
http-parser (used for converting http strings to crow::request objects)
diff --git a/docs/assets/crowlogo.svg b/docs/assets/crowlogo.svg
index 056246387..e33d0f75d 100644
--- a/docs/assets/crowlogo.svg
+++ b/docs/assets/crowlogo.svg
@@ -1,22 +1,22 @@
+ inkscape:export-ydpi="324.99619"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:dc="http://purl.org/dc/elements/1.1/">
+ units="px"
+ inkscape:pagecheckerboard="0" />
@@ -47,7 +48,7 @@
image/svg+xml
-
+
@@ -57,14 +58,70 @@
id="layer1"
transform="translate(2.890729,-78.735456)">
+ id="rect51232"
+ style="stroke-width:1.621;stroke-linecap:round;stroke-linejoin:round"
+ d="m 86.263122,129.89227 c -1.679885,0.94306 -0.970661,50.071 -0.970661,50.071 h 8.67359 c 0,0 -1.146978,-23.39877 0.97066,-22.01299 5.738329,3.75514 18.678879,22.04105 18.678879,22.04105 l 10.82099,0.0233 c 0,0 -22.33706,-23.42311 -21.33927,-25.78171 0.99777,-2.35858 13.45284,-3.56535 14.28699,-5.61353 0.83414,-2.04817 0.74681,-7.53026 0.0119,-9.332 -0.73488,-1.80175 -17.951097,-8.50923 -22.459538,-9.39514 -4.508449,-0.88592 -6.875855,-1.00922 -8.67359,0 z m 8.673589,7.89629 c 0.435027,-0.21177 12.515509,4.68197 12.527659,5.49714 0.0113,0.76226 -12.065069,5.41359 -12.527659,5.04327 -0.327731,-0.26235 -0.43502,-10.32863 0,-10.54041 z"
+ sodipodi:nodetypes="sccscczzzzszssz" />
+
+
+ style="color:#000000;fill:#000000;stroke-width:0.669208;-inkscape-stroke:none"
+ d="m 75.361766,164.31848 c -1.522517,0.0929 -3.225106,0.23753 -5.059422,0.41007 -14.861309,1.39786 -38.370023,4.6261 -44.831721,-3.01508 -7.259264,-8.58431 -5.992766,-15.11802 -3.7379,-29.04552 2.254865,-13.92751 11.768175,-32.3183 23.453693,-33.61763 11.685519,-1.299327 24.289435,-0.14247 28.381026,1.21309 L 77.417321,85.791987 C 62.828148,82.27312 56.039475,80.867702 37.328615,83.440263 18.617756,86.012824 9.3053305,108.67011 6.7920712,129.55115 c -2.5132504,20.88098 -0.630659,37.59767 5.7039948,44.13904 6.334653,6.54136 43.941662,8.861 64.480651,5.0425 z"
+ id="path830"
+ sodipodi:nodetypes="csszzcczzzcc" />
+
+
+
+
+
+
+
+
+
+
diff --git a/docs/assets/crowlogo_dual_color.svg b/docs/assets/crowlogo_dual_color.svg
new file mode 100644
index 000000000..b70a954c6
--- /dev/null
+++ b/docs/assets/crowlogo_dual_color.svg
@@ -0,0 +1,90 @@
+
+
+
+
+
+
+
+ image/svg+xml
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/docs/assets/crowlogo_inverted.svg b/docs/assets/crowlogo_inverted.svg
new file mode 100644
index 000000000..2784f9c50
--- /dev/null
+++ b/docs/assets/crowlogo_inverted.svg
@@ -0,0 +1,121 @@
+
+
+
+
+
+
+
+ image/svg+xml
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/docs/assets/crowlogo_main_color.svg b/docs/assets/crowlogo_main_color.svg
new file mode 100644
index 000000000..5929a8b91
--- /dev/null
+++ b/docs/assets/crowlogo_main_color.svg
@@ -0,0 +1,90 @@
+
+
+
+
+
+
+
+ image/svg+xml
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/docs/assets/fast_icon.svg b/docs/assets/fast_icon.svg
new file mode 100644
index 000000000..3c87ba259
--- /dev/null
+++ b/docs/assets/fast_icon.svg
@@ -0,0 +1,105 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/docs/assets/favicon.png b/docs/assets/favicon.png
index 07d6489fa..81ff297d2 100644
Binary files a/docs/assets/favicon.png and b/docs/assets/favicon.png differ
diff --git a/docs/assets/favicon.svg b/docs/assets/favicon.svg
index 1ee597f54..3ef8866f9 100644
--- a/docs/assets/favicon.svg
+++ b/docs/assets/favicon.svg
@@ -1,19 +1,19 @@
+ sodipodi:docname="favicon.svg"
+ inkscape:version="1.1 (c4e8f9ed74, 2021-05-24)"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:dc="http://purl.org/dc/elements/1.1/">
+ inkscape:window-maximized="1"
+ inkscape:pagecheckerboard="0" />
@@ -43,7 +44,6 @@
image/svg+xml
-
@@ -53,14 +53,50 @@
id="layer1"
transform="translate(-52.906909,-78.735456)">
+ style="color:#000000;fill:#ffffff;stroke-width:0.633931;-inkscape-stroke:none;fill-opacity:1"
+ d="m 131.60351,157.53369 c -1.44225,0.088 -3.05509,0.225 -4.79271,0.38845 -14.0779,1.32417 -36.347368,4.38223 -42.468437,-2.85614 -6.876591,-8.13179 -5.676856,-14.32107 -3.540856,-27.51438 2.136,-13.19332 11.147817,-30.61464 22.217343,-31.845476 11.06952,-1.230833 23.00902,-0.134959 26.88493,1.149142 l 3.64693,-13.70856 c -13.8201,-3.333369 -20.25092,-4.6647 -37.975455,-2.227752 -17.724509,2.436948 -26.546029,23.899856 -28.926801,43.680146 -2.380764,19.78024 -0.597414,35.6157 5.403308,41.81224 6.000721,6.19654 41.625298,8.3939 61.081568,4.77669 z"
+ id="path830"
+ sodipodi:nodetypes="csszzcczzzcc" />
+
+
+
+
+
+
+
+ style="fill:none;stroke:none;stroke-width:0.141638px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ d="m 102.26433,110.83508 c 0,0 4.57649,0.7119 5.79689,2.03401 1.2204,1.3221 2.64419,2.44079 2.44079,4.16968 -0.2034,1.7289 0.4068,2.8476 -0.30509,3.86461 -0.7119,1.01698 -2.8476,3.76288 -4.77989,4.98328 -1.9323,1.2204 -7.220698,21.45867 -6.407099,23.08587 0.813599,1.62719 1.525499,0.10169 1.423799,1.1187 -0.1017,1.01699 -1.118698,4.88159 -1.322096,6.50878 -0.203402,1.6272 0.203398,5.69519 -0.101703,6.81389 -0.305097,1.1187 -1.322093,4.47479 -1.322093,4.47479 l 10.576782,0.5085 2.44079,-7.32239 c 0,0 2.5425,-7.01728 2.9493,-7.62748 0.4068,-0.61021 5.59349,-0.9153 7.93259,-2.034 0.36582,-0.17496 2.12886,-2.01002 2.1519,-0.807 0.0203,1.06109 -1.70612,5.16186 -1.3613,5.25648 0.3559,0.0977 2.22569,-4.57149 2.44526,-4.17875 0.34889,0.62408 -0.23447,3.55624 0.23509,3.21194 0.69943,-0.51286 2.55912,-7.10284 2.93614,-7.75406 0.25365,-0.43813 1.28141,3.12689 1.70873,2.26144 0.2816,-0.57033 -0.0181,-5.90925 0.16501,-5.49611 0.32584,0.73534 0.67896,0.003 0.64824,-0.35721 -0.11292,-1.32339 0.47718,1.22581 0.5992,1.02006 0.1483,-0.25009 -0.0609,-1.9612 0.0782,-2.23063 0.0919,-0.17795 0.28677,0.0263 0.37136,-0.16703 0.0686,-0.15669 -0.21818,-2.29341 -0.15223,-2.44361 0.34625,-0.78862 1.03546,-1.00099 1.15799,-1.63819 0.26212,-1.36304 0.57981,-4.46484 -1.63944,-7.24958 -0.14669,-0.18406 0.3442,-0.59186 0.28792,-0.8065 -0.55394,-2.11267 -0.83313,-3.20434 -1.19098,-3.74111 -0.81359,-1.2204 -1.3221,-0.1017 -2.54249,-1.72889 -1.2204,-1.6272 -2.034,-0.91531 -1.8306,-2.4408 0.0298,-0.22308 0.27158,-1.19455 0.13911,-2.14899 -0.0642,-0.4628 -0.59951,-1.0748 -0.55695,-1.30247 0.0732,-0.39183 0.94,0.97767 0.89568,0.59958 -0.0755,-0.64466 -0.77244,-1.07679 -0.86951,-1.55887 -0.0631,-0.31315 0.83737,0.1895 0.6225,-0.0808 -0.62916,-0.79141 -1.09222,-1.63098 -1.3183,-2.27693 -0.71191,-2.034 -3.18392,-6.45253 -5.92982,-7.26612 -2.74589,-0.8136 -3.71474,-0.52713 -6.20368,-0.38138 -1.64439,0.0963 -3.36191,-0.13787 -5.3099,-0.27042 -1.57517,-0.10718 -5.26689,0.44839 -5.87709,1.56709 -0.6102,1.1187 -0.14778,1.10889 -0.14778,1.10889 0,0 0.25083,-0.0891 0.30563,-0.0899 0.2612,-0.004 0.85915,0.81159 0.85915,0.81159 z"
+ id="path3379"
+ sodipodi:nodetypes="csssssssscccsssssssssssssssssssssssssssssscsc" />
diff --git a/docs/assets/header_icon.svg b/docs/assets/header_icon.svg
new file mode 100644
index 000000000..f82df10fd
--- /dev/null
+++ b/docs/assets/header_icon.svg
@@ -0,0 +1,67 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/docs/assets/og_img.png b/docs/assets/og_img.png
new file mode 100644
index 000000000..36574ddad
Binary files /dev/null and b/docs/assets/og_img.png differ
diff --git a/docs/assets/pkg_logos/arch.png b/docs/assets/pkg_logos/arch.png
new file mode 100644
index 000000000..8fd851d44
Binary files /dev/null and b/docs/assets/pkg_logos/arch.png differ
diff --git a/docs/assets/pkg_logos/conan.png b/docs/assets/pkg_logos/conan.png
new file mode 100644
index 000000000..b0ec063e1
Binary files /dev/null and b/docs/assets/pkg_logos/conan.png differ
diff --git a/docs/assets/pkg_logos/github.png b/docs/assets/pkg_logos/github.png
new file mode 100644
index 000000000..402191e09
Binary files /dev/null and b/docs/assets/pkg_logos/github.png differ
diff --git a/docs/assets/pkg_logos/ubuntu.png b/docs/assets/pkg_logos/ubuntu.png
new file mode 100644
index 000000000..5ef25b7c0
Binary files /dev/null and b/docs/assets/pkg_logos/ubuntu.png differ
diff --git a/docs/assets/pkg_logos/vcpkg.png b/docs/assets/pkg_logos/vcpkg.png
new file mode 100644
index 000000000..899ab89ff
Binary files /dev/null and b/docs/assets/pkg_logos/vcpkg.png differ
diff --git a/docs/assets/typesafe_icon.svg b/docs/assets/typesafe_icon.svg
new file mode 100644
index 000000000..582213166
--- /dev/null
+++ b/docs/assets/typesafe_icon.svg
@@ -0,0 +1,67 @@
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/docs/assets/websocket_icon.svg b/docs/assets/websocket_icon.svg
new file mode 100644
index 000000000..83d54206a
--- /dev/null
+++ b/docs/assets/websocket_icon.svg
@@ -0,0 +1,48 @@
+
+
+
+
+
+
+
+
+
+
diff --git a/docs/getting_started/your_first_application.md b/docs/getting_started/your_first_application.md
index 3257b6dd9..95edbbe7f 100644
--- a/docs/getting_started/your_first_application.md
+++ b/docs/getting_started/your_first_application.md
@@ -1,7 +1,7 @@
This page shows how you can get started with a simple hello world application.
##1. Include
-Starting with an empty `main.cpp` file, add `#!cpp #define CROW_MAIN` then `#!cpp #include "crow.h"` or `#!cpp #include "crow_all.h"` if you're using the single header file.
+Starting with an empty `main.cpp` file, first add `#!cpp #define CROW_MAIN` then `#!cpp #include "crow.h"` or `#!cpp #include "crow_all.h"` if you're using the single header file.
!!! note
@@ -39,6 +39,7 @@ Please note that the `port()` and `multithreaded()` methods aren't needed, Thoug
Once you've followed all the steps above, your code should look similar to this
``` cpp linenums="1"
+#define CROW_MAIN //let the compiler know this is your main cpp file
#include "crow.h"
//#include "crow_all.h"
diff --git a/docs/guides/blueprints.md b/docs/guides/blueprints.md
new file mode 100644
index 000000000..2a0637611
--- /dev/null
+++ b/docs/guides/blueprints.md
@@ -0,0 +1,43 @@
+!!!Warning
+
+ This feature is currently only available on the "master" branch.
+
+Crow supports flask style blueprints.
+A blueprint is a limited app. It cannot handle networking. But it can handle routes.
+Blueprints allow developers to compartmentalize their Crow applications, making them a lot more modular.
+
+In order for a blueprint to work, it has to be registered with a Crow app before the app is run. This can be done using `#!cpp app.register_blueprint(blueprint);`.
+
+Blueprints let you do the following:
+
+### Define Routes
+You can define routes in a blueprint, similarly to how `#!cpp CROW_ROUTE(app, "/xyz")` works, you can use `#!cpp CROW_BP_ROUTE(blueprint, "/xyz")` to define a blueprint route.
+
+### Define a Prefix
+Blueprints can have a prefix assigned to them. This can be done when creating a new blueprint as in `#!cpp crow::blueprint bp("prefix");`. This prefix will be applied to all routes belonging to the blueprint. truning a route such as `/crow/rocks` into `/prefix/crow/rocks`.
+
+!!!Warning
+
+ Unlike routes, blueprint prefixes should contain no slashes.
+
+
+### Use a custom Static directory
+Blueprints let you define a custom static directory (relative to your working directory). This can be done by initializing a blueprint as `#!cpp crow::blueprint bp("prefix", "custom_static");`. This does not have an effect on `#!cpp set_static_file_info()`, it's only for when you want direct access to a file.
+
+!!!note
+
+ Currently changing which endpoint the blueprint uses isn't possible, so whatever you've set in `CROW_STATIC_ENDPOINT` (default is "static") will be used. Making your final route `/prefix/static/filename`.
+
+
+### Use a custom Templates directory
+Similar to static directories, You can set a custom templates directory (relative to your working directory). To do this you initialize the blueprint as `#!cpp crow::blueprint bp("prefix", "custom_static", "custom_templates");`. Any routes defined for the blueprint will use that directory when calling `#!cpp crow::mustache::load("filename.html")`.
+
+!!!note
+
+ If you want to define a custom templates directory without defining a custom static directory, you can pass the static directory as an empty string. Making your constructor `#!cpp crow::blueprint bp("prefix", "", "custom_templates");`.
+
+### Define a custom Catchall route
+You can define a custom catchall route for a blueprint by calling `#!cpp CROW_BP_CATCHALL_ROUTE(blueprint)`. This causes any requests with a URL starting with `/prefix` and no route found to call the blueprint's catchall route. if no catchall route is defined, Crow will default to either the parent blueprint or the app's catchall route.
+
+### Register other Blueprints
+Blueprints can also register other blueprints. This is done through `#!cpp blueprint.register_blueprint(blueprint_2);`. The child blueprint's routes become `/prefix/prefix_2/abc/xyz`.
diff --git a/docs/guides/compression.md b/docs/guides/compression.md
index 89b8e80de..d614a3a9b 100644
--- a/docs/guides/compression.md
+++ b/docs/guides/compression.md
@@ -1,3 +1,4 @@
+**Introduced in: `v0.3`**
Crow supports Zlib compression using Gzip or Deflate algorithms.
## HTTP Compression
diff --git a/docs/guides/json.md b/docs/guides/json.md
index 15123e385..9ed7748a4 100644
--- a/docs/guides/json.md
+++ b/docs/guides/json.md
@@ -36,3 +36,7 @@ An object type `wvalue` uses `std::unordered_map` by default, if you want to hav
A JSON `wvalue` can be returned directly inside a route handler, this will cause the `content-type` header to automatically be set to `Application/json` and the JSON value will be converted to string and placed in the response body. For more information go to [Routes](../routes).
For more info on write values go [here](../../reference/classcrow_1_1json_1_1wvalue.html).
+
+!!!note
+
+ Crow's json exceptions can be disabled by using the `#!cpp #define CROW_JSON_NO_ERROR_CHECK` macro. This should increase the program speed with the drawback of having unexpected behavious when used incorrectly (e.g. by attempting to parse an invalid json object).
diff --git a/docs/guides/multipart.md b/docs/guides/multipart.md
index 61cc47183..b67c87975 100644
--- a/docs/guides/multipart.md
+++ b/docs/guides/multipart.md
@@ -1,3 +1,4 @@
+**Introduced in: `v0.2`**
Multipart is a way of forming HTTP requests or responses to contain multiple distinct parts.
Such an approach allows a request to contain multiple different pieces of data with potentially conflicting data types in a single response payload.
It is typically used either in html forms, or when uploading multiple files.
diff --git a/docs/guides/query-string.md b/docs/guides/query-string.md
index 34e29be82..dce718b92 100644
--- a/docs/guides/query-string.md
+++ b/docs/guides/query-string.md
@@ -5,6 +5,7 @@ Crow supports query strings through `crow::request::url_params`. The object is o
##get(name)
Returns the value (as char*) based on the given key (or name). Returns `nullptr` if the key is not found.
##pop(name)
+**Introduced in: `v0.3`**
Works the same as `get`, but removes the returned value.
!!! note
@@ -15,11 +16,13 @@ A url can be `http://example.com?key[]=value1&key[]=value2&key[]=value3`. Using
`#!cpp get_list("key", false)` can be used to parse `http://example.com?key=value1&key=value2&key=value3`
##pop_list(name)
+**Introduced in: `v0.3`**
Works the same as `get_list` but removes all instances of values having the given key (`use_brackets` is also available here).
##get_dict(name)
Returns an `std::unordered_map` from a query string such as `?key[sub_key1]=value1&key[sub_key2]=value2&key[sub_key3]=value3`.
The key in the map is what's in the brackets (`sub_key1` for example), and the value being what's after the `=` sign (`value1`). The name passed to the function is not part of the returned value.
##pop_dict(name)
+**Introduced in: `v0.3`**
Works the same as `get_dict` but removing the values from the query string.
!!!warning
diff --git a/docs/guides/routes.md b/docs/guides/routes.md
index 977df25e1..e7fe72435 100644
--- a/docs/guides/routes.md
+++ b/docs/guides/routes.md
@@ -52,6 +52,7 @@ The main return type is `std::string`. although you could also return a `crow::j
For more information on the specific constructors for a `crow::response` go [here](../../reference/structcrow_1_1response.html).
##Returning custom classes
+**Introduced in: `v0.3`**
If you have your own class you want to return (without converting it to string and returning that), you can use the `crow::returnable` class.
to use the returnable class, you only need your class to publicly extend `crow::returnable`, add a `dump()` method that returns your class as an `std::string`, and add a constructor that has a `Content-Type` header as a string argument.
@@ -73,4 +74,5 @@ class a : public crow::returnable
```
##Catchall routes
+**Introduced in: `v0.3`**
By default, any request that Crow can't find a route for will return a simple 404 response. You can change that to return a default route using the `CROW_CATCHALL_ROUTE(app)` macro. Defining it is identical to a normal route, even when it comes to the `const crow::request&` and `crow::response&` parameters being optional.
diff --git a/docs/guides/static.md b/docs/guides/static.md
index 9be03dbbf..7659d2dbf 100644
--- a/docs/guides/static.md
+++ b/docs/guides/static.md
@@ -1,3 +1,4 @@
+**Introduced in: `v0.2`**
A static file is any file that resides in the server's storage.
Crow supports returning Static files as responses in 2 ways.
diff --git a/docs/index.md b/docs/index.md
index d1835284d..a572d90d6 100644
--- a/docs/index.md
+++ b/docs/index.md
@@ -1,101 +1,3 @@
-#
-
-
-A Fast and Easy to use microframework for the web.
-
-
-
-
-
-
-
-
-## Description
-
-Crow is a C++ microframework for running web services. It uses routing similar to Python's Flask which makes it easy to use. It is also extremely fast, beating multiple existing C++ frameworks as well as non C++ frameworks.
-
-### Features
- - Easy Routing (similar to flask).
- - Type-safe Handlers.
- - Blazingly fast (see [this benchmark](https://github.com/ipkn/crow-benchmark) and [this benchmark](https://github.com/guteksan/REST-CPP-benchmark)).
- - Built in JSON support.
- - [Mustache](http://mustache.github.io/) based templating library (`crow::mustache`).
- - Header only library (single header file available).
- - Middleware support for extensions.
- - HTTP/1.1 and Websocket support.
- - Multi-part request and response support.
- - Uses modern C++ (11/14)
-
-### Still in development
- - [HTTP/2 support](https://github.com/crowcpp/crow/issues/8)
-
-## Documentation
-Available [here](https://crowcpp.github.io/crow).
-
-## Examples
-
-#### Hello World
-```c++
-#include "crow.h"
-
-int main()
-{
- crow::SimpleApp app;
-
- CROW_ROUTE(app, "/")([](){
- return "Hello world";
- });
-
- app.port(18080).multithreaded().run();
-}
-```
-
-#### JSON Response
-```cpp
-CROW_ROUTE(app, "/json")
-([]{
- crow::json::wvalue x;
- x["message"] = "Hello, World!";
- return x;
-});
-```
-
-#### Arguments
-```cpp
-CROW_ROUTE(app,"/hello/")
-([](int count){
- if (count > 100)
- return crow::response(400);
- std::ostringstream os;
- os << count << " bottles of beer!";
- return crow::response(os.str());
-});
-```
-Handler arguments type check at compile time
-```cpp
-// Compile error with message "Handler type is mismatched with URL paramters"
-CROW_ROUTE(app,"/another/")
-([](int a, int b){
- return crow::response(500);
-});
-```
-
-#### Handling JSON Requests
-```cpp
-CROW_ROUTE(app, "/add_json")
-.methods("POST"_method)
-([](const crow::request& req){
- auto x = crow::json::load(req.body);
- if (!x)
- return crow::response(400);
- int sum = x["a"].i()+x["b"].i();
- std::ostringstream os;
- os << sum;
- return crow::response{os.str()};
-});
-```
-
-More examples can be found [here](https://github.com/crowcpp/crow/tree/master/examples).
-
-## Setting Up / Building
-Available [here](https://crowcpp.github.io/crow/getting_started/setup).
+---
+template: home.html
+---
diff --git a/docs/overrides/home.html b/docs/overrides/home.html
new file mode 100644
index 000000000..0fabf55f1
--- /dev/null
+++ b/docs/overrides/home.html
@@ -0,0 +1,300 @@
+{% extends "main.html" %}
+
+
+
+
+{% block content %}
+
+
+
+A Fast and Easy to use microframework for the web.
+
+
+
+
+
+
+
Blazingly Fast
+
+
+
+
Header Only
+
+
+
+
Typesafe handlers
+
+
+
+
Websocket Support
+
+
+
+
+
+
+
+
+
Easy to get started
+
+
+
#define CROW_MAIN
+#include "crow.h"
+
+int main ()
+{
+ crow :: SimpleApp app ;
+
+ CROW_ROUTE ( app , "/" )([](){
+ return "Hello world" ;
+ });
+
+ app . port ( 18080 ). run ();
+}
+
+
+
+
+
+
+
CROW_ROUTE ( app , "/json" )
+([]{
+ crow :: json :: wvalue x ;
+ x [ "message" ] = "Hello, World!" ;
+ return x ;
+});
+
+
+
+
JSON Support Built-in
+
+
+
+
+
+
+
URL parameter support as well!
+
+
+
CROW_ROUTE ( app , "/hello/<int>" )
+([]( int count ){
+ return crow :: response (std::to_string(count));
+});
+
+
+
+
+
+
+Support Crow
+Crow is provided free of charge courtesy of everyone who is donating their money, time, and expertise to keep it going.
+Help us make something great!
+
+
+ Fund Crow
+ Develop Crow
+ Chat with us
+
+
+
+Get Crow
+Crow is everywhere, you just need to grab it!
+
+
+
+
+Learn Crow
+The 1000 mile journey begins with a single step. Get started by installing Crow and building you first application. Or go through the guides if you're stuck somewhere.
+
+
+ Get Started
+ Guides
+ API Reference
+
+
+
+
+
+{% endblock %}
+
+{% block site_nav %}{% endblock %}
+{% block footer %}
+
+
+{% endblock %}
diff --git a/docs/overrides/main.html b/docs/overrides/main.html
new file mode 100644
index 000000000..e3dfe8716
--- /dev/null
+++ b/docs/overrides/main.html
@@ -0,0 +1,9 @@
+{% extends "base.html" %}
+
+{% block extrahead %}
+
+
+
+
+
+{% endblock %}
diff --git a/docs/overrides/partials/header.html b/docs/overrides/partials/header.html
new file mode 100644
index 000000000..38ad4a17a
--- /dev/null
+++ b/docs/overrides/partials/header.html
@@ -0,0 +1,150 @@
+
+
+
+
diff --git a/docs/stylesheets/colors.css b/docs/stylesheets/colors.css
index aa0a82f38..25a055a1c 100644
--- a/docs/stylesheets/colors.css
+++ b/docs/stylesheets/colors.css
@@ -1,6 +1,31 @@
:root {
- --md-primary-fg-color: #161616;
- --md-accent-fg-color: #396A97;
- --md-typeset-a-color: var(--md-accent-fg-color);
+ --md-primary-fg-color: #24404f;
+ --md-accent-fg-color: #122027;
+ --md-typeset-a-color: var(--md-accent-fg-color) !important;
+ --md-default-bg-color: #e5f2f8;
+ --md-code-bg-color: #2e3639 !important;
+ --md-code-hl-punctuation-color: #fff !important;
+}
+
+.md-typeset code {
+ background-color: #2e3639;
+ color: #afafaf;
+}
+
+.linenos {
+ background-color: #2e3639 !important;
+ color: #afafaf !important;
+}
+
+.highlight .p {
+ color: #bbb;
+}
+
+.highlight .o {
+ color: #afafaf;
+}
+
+.highlight .n {
+ color: #afafaf;
}
diff --git a/docs/stylesheets/extra.css b/docs/stylesheets/extra.css
new file mode 100644
index 000000000..bb9c440a6
--- /dev/null
+++ b/docs/stylesheets/extra.css
@@ -0,0 +1,8 @@
+.md-header__source {
+ margin-left: 0px;
+ width: autho;
+ min-width: 9.6rem;
+}
+.code {
+ border-radius: 5px;
+}
diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt
index cb9be6a29..4841e37ff 100644
--- a/examples/CMakeLists.txt
+++ b/examples/CMakeLists.txt
@@ -65,6 +65,10 @@ else ()
target_compile_options(example_chat PRIVATE "${compiler_options}")
target_link_libraries(example_chat PUBLIC ${REQUIRED_LIBRARIES})
+ add_executable(example_static_file example_static_file.cpp)
+ target_compile_options(example_static_file PRIVATE "${compiler_options}")
+ target_link_libraries(example_static_file PUBLIC ${REQUIRED_LIBRARIES})
+
add_executable(example_catchall example_catchall.cpp)
target_compile_options(example_catchall PRIVATE "${compiler_options}")
target_link_libraries(example_catchall PUBLIC ${REQUIRED_LIBRARIES})
@@ -73,6 +77,10 @@ else ()
target_compile_options(example_json_map PRIVATE "${compiler_options}")
target_link_libraries(example_json_map PUBLIC ${REQUIRED_LIBRARIES})
+ add_executable(example_blueprint example_blueprint.cpp)
+ target_compile_options(example_blueprint PRIVATE "${compiler_options}")
+ target_link_libraries(example_blueprint PUBLIC ${REQUIRED_LIBRARIES})
+
add_custom_command(OUTPUT example_chat.html
COMMAND ${CMAKE_COMMAND} -E
copy ${PROJECT_SOURCE_DIR}/example_chat.html ${CMAKE_CURRENT_BINARY_DIR}/example_chat.html
diff --git a/examples/example_blueprint.cpp b/examples/example_blueprint.cpp
new file mode 100644
index 000000000..af007bd09
--- /dev/null
+++ b/examples/example_blueprint.cpp
@@ -0,0 +1,35 @@
+#define CROW_MAIN
+#include "crow.h"
+
+int main()
+{
+ crow::SimpleApp app;
+
+ crow::Blueprint bp("bp_prefix", "cstat", "ctemplate");
+
+
+ crow::Blueprint sub_bp("bp2", "csstat", "cstemplate");
+
+ CROW_BP_ROUTE(sub_bp, "/")
+ ([]() {
+ return "Hello world!";
+ });
+
+/* CROW_BP_ROUTE(bp, "/templatt")
+ ([]() {
+ crow::mustache::context ctxdat;
+ ctxdat["messg"] = "fifty five!!";
+
+ auto page = crow::mustache::load("indks.html");
+
+ return page.render(ctxdat);
+ });
+*/
+ CROW_BP_CATCHALL_ROUTE(sub_bp)([](){return "WRONG!!";});
+
+
+ bp.register_blueprint(sub_bp);
+ app.register_blueprint(bp);
+
+ app.loglevel(crow::LogLevel::DEBUG).port(18080).run();
+}
diff --git a/examples/example_json_map.cpp b/examples/example_json_map.cpp
index 4ce1884c3..043035146 100644
--- a/examples/example_json_map.cpp
+++ b/examples/example_json_map.cpp
@@ -2,8 +2,6 @@
#define CROW_JSON_USE_MAP
#include "crow.h"
-
-
int main()
{
crow::SimpleApp app;
@@ -19,6 +17,22 @@ CROW_ROUTE(app, "/json")
return x;
});
+CROW_ROUTE(app, "/json-initializer-list-constructor")
+([] {
+ return crow::json::wvalue({
+ {"first", "Hello world!"}, /* stores a char const* hence a json::type::String */
+ {"second", std::string("How are you today?")}, /* stores a std::string hence a json::type::String. */
+ {"third", 54}, /* stores an int (as 54 is an int literal) hence a std::int64_t. */
+ {"fourth", 54l}, /* stores a long (as 54l is a long literal) hence a std::int64_t. */
+ {"fifth", 54u}, /* stores an unsigned int (as 54u is a unsigned int literal) hence a std::uint64_t. */
+ {"sixth", 54ul}, /* stores an unsigned long (as 54ul is an unsigned long literal) hence a std::uint64_t. */
+ {"seventh", 2.f}, /* stores a float (as 2.f is a float literal) hence a double. */
+ {"eighth", 2.}, /* stores a double (as 2. is a double literal) hence a double. */
+ {"ninth", nullptr}, /* stores a std::nullptr hence json::type::Null . */
+ {"tenth", true} /* stores a bool hence json::type::True . */
+ });
+});
+
// enables all log
app.loglevel(crow::LogLevel::Debug);
diff --git a/examples/example_static_file.cpp b/examples/example_static_file.cpp
index 2744fc1a1..908f9e27f 100644
--- a/examples/example_static_file.cpp
+++ b/examples/example_static_file.cpp
@@ -1,5 +1,6 @@
//#define CROW_STATIC_DRIECTORY "alternative_directory/"
//#define CROW_STATIC_ENDPOINT "/alternative_endpoint/"
+//#define CROW_DISABLE_STATIC_DIR
#define CROW_MAIN
#include "crow.h"
diff --git a/examples/example_with_all.cpp b/examples/example_with_all.cpp
index 3874cdc86..a015e6b3a 100644
--- a/examples/example_with_all.cpp
+++ b/examples/example_with_all.cpp
@@ -42,7 +42,7 @@ int main()
return crow::response(os.str());
});
- // example which uses only response as a paramter wihtout
+ // example which uses only response as a paramter without
// request being a parameter.
CROW_ROUTE(app,"/add//")
([](crow::response& res, int a, int b){
diff --git a/examples/websocket/example_ws.cpp b/examples/websocket/example_ws.cpp
index df52632d0..0543c0eec 100644
--- a/examples/websocket/example_ws.cpp
+++ b/examples/websocket/example_ws.cpp
@@ -8,7 +8,7 @@ int main()
{
crow::SimpleApp app;
- std::mutex mtx;;
+ std::mutex mtx;
std::unordered_set users;
CROW_ROUTE(app, "/ws")
diff --git a/include/crow/app.h b/include/crow/app.h
index 1c9f03e7a..2491ebc6a 100644
--- a/include/crow/app.h
+++ b/include/crow/app.h
@@ -10,6 +10,7 @@
#include
#include
+#include "crow/version.h"
#include "crow/settings.h"
#include "crow/logging.h"
#include "crow/utility.h"
@@ -24,10 +25,13 @@
#ifdef CROW_MSVC_WORKAROUND
#define CROW_ROUTE(app, url) app.route_dynamic(url)
+#define CROW_BP_ROUTE(blueprint, url) blueprint.new_rule_dynamic(url)
#else
#define CROW_ROUTE(app, url) app.route(url)
+#define CROW_BP_ROUTE(blueprint, url) blueprint.new_rule_tagged(url)
#endif
#define CROW_CATCHALL_ROUTE(app) app.catchall_route()
+#define CROW_BP_CATCHALL_ROUTE(blueprint) blueprint.catchall_rule()
namespace crow
{
@@ -120,7 +124,7 @@ namespace crow
return *this;
}
- ///Set the server name (default Crow/0.3)
+ ///Set the server name
self_t& server_name(std::string server_name)
{
server_name_ = server_name;
@@ -158,12 +162,18 @@ namespace crow
/// crow::LogLevel::Warning (2)
/// crow::LogLevel::Error (3)
/// crow::LogLevel::Critical (4)
- self_t& loglevel(crow::LogLevel level)
+ self_t& loglevel(LogLevel level)
{
crow::logger::setLogLevel(level);
return *this;
}
+ self_t& register_blueprint(Blueprint& blueprint)
+ {
+ router_.register_blueprint(blueprint);
+ return *this;
+ }
+
///Set a custom duration and function to run on every tick
template
self_t& tick(Duration d, Func f) {
@@ -191,7 +201,34 @@ namespace crow
///Go through the rules, upgrade them if possible, and add them to the list of rules
void validate()
{
- router_.validate();
+ if (!validated_)
+ {
+
+#ifndef CROW_DISABLE_STATIC_DIR
+ route(CROW_STATIC_ENDPOINT)
+ ([](crow::response& res, std::string file_path_partial)
+ {
+ res.set_static_file_info(CROW_STATIC_DIRECTORY + file_path_partial);
+ res.end();
+ });
+
+ for (auto& bp : router_.blueprints())
+ {
+ if (!bp->static_dir().empty())
+ {
+ bp->new_rule_tagged(CROW_STATIC_ENDPOINT)
+ ([bp](crow::response& res, std::string file_path_partial)
+ {
+ res.set_static_file_info(bp->static_dir() + '/' + file_path_partial);
+ res.end();
+ });
+ }
+ }
+#endif
+
+ router_.validate();
+ validated_ = true;
+ }
}
///Notify anything using `wait_for_server_start()` to proceed
@@ -205,15 +242,8 @@ namespace crow
///Run the server
void run()
{
-#ifndef CROW_DISABLE_STATIC_DIR
- route(CROW_STATIC_ENDPOINT)
- ([](crow::response& res, std::string file_path_partial)
- {
- res.set_static_file_info(CROW_STATIC_DIRECTORY + file_path_partial);
- res.end();
- });
+
validate();
-#endif
#ifdef CROW_ENABLE_SSL
if (use_ssl_)
@@ -360,7 +390,8 @@ namespace crow
private:
uint16_t port_ = 80;
uint16_t concurrency_ = 1;
- std::string server_name_ = "Crow/0.3";
+ bool validated_ = false;
+ std::string server_name_ = std::string("Crow/") + VERSION;
std::string bindaddr_ = "0.0.0.0";
Router router_;
diff --git a/include/crow/common.h b/include/crow/common.h
index f38a3c767..2aa95a388 100644
--- a/include/crow/common.h
+++ b/include/crow/common.h
@@ -8,7 +8,7 @@
namespace crow
{
- enum class HTTPMethod
+ enum class HTTPMethod : char
{
#ifndef DELETE
DELETE = 0,
@@ -69,7 +69,7 @@ namespace crow
return "invalid";
}
- enum class ParamType
+ enum class ParamType : char
{
INT,
UINT,
diff --git a/include/crow/http_connection.h b/include/crow/http_connection.h
index 156b565e3..2300757fe 100644
--- a/include/crow/http_connection.h
+++ b/include/crow/http_connection.h
@@ -262,7 +262,7 @@ namespace crow
req_ = std::move(parser_.to_request());
request& req = req_;
- req.remoteIpAddress = adaptor_.remote_endpoint().address().to_string();
+ req.remote_ip_address = adaptor_.remote_endpoint().address().to_string();
if (parser_.check_version(1, 0))
{
diff --git a/include/crow/http_request.h b/include/crow/http_request.h
index eb7e902ba..fbd002669 100644
--- a/include/crow/http_request.h
+++ b/include/crow/http_request.h
@@ -31,7 +31,7 @@ namespace crow
query_string url_params; ///< The parameters associated with the request. (everything after the `?`)
ci_map headers;
std::string body;
- std::string remoteIpAddress; ///< The IP address from which the request was sent.
+ std::string remote_ip_address; ///< The IP address from which the request was sent.
void* middleware_context{};
boost::asio::io_service* io_service{};
diff --git a/include/crow/http_response.h b/include/crow/http_response.h
index 2b28353b8..18ac7cc72 100644
--- a/include/crow/http_response.h
+++ b/include/crow/http_response.h
@@ -87,6 +87,7 @@ namespace crow
code = r.code;
headers = std::move(r.headers);
completed_ = r.completed_;
+ file_info = std::move(r.file_info);
return *this;
}
diff --git a/include/crow/http_server.h b/include/crow/http_server.h
index 3e3a65a51..e6efa6fb0 100644
--- a/include/crow/http_server.h
+++ b/include/crow/http_server.h
@@ -13,6 +13,7 @@
#include
+#include "crow/version.h"
#include "crow/http_connection.h"
#include "crow/logging.h"
#include "crow/dumb_timer_queue.h"
@@ -26,7 +27,7 @@ namespace crow
class Server
{
public:
- Server(Handler* handler, std::string bindaddr, uint16_t port, std::string server_name = "Crow/0.3", std::tuple* middlewares = nullptr, uint16_t concurrency = 1, typename Adaptor::context* adaptor_ctx = nullptr)
+ Server(Handler* handler, std::string bindaddr, uint16_t port, std::string server_name = std::string("Crow/") + VERSION, std::tuple* middlewares = nullptr, uint16_t concurrency = 1, typename Adaptor::context* adaptor_ctx = nullptr)
: acceptor_(io_service_, tcp::endpoint(boost::asio::ip::address::from_string(bindaddr), port)),
signals_(io_service_, SIGINT, SIGTERM),
tick_timer_(io_service_),
@@ -121,7 +122,7 @@ namespace crow
init_count ++;
while(1)
{
- try
+ try
{
if (io_service_pool_[i]->run() == 0)
{
@@ -135,7 +136,7 @@ namespace crow
}
}));
- if (tick_function_ && tick_interval_.count() > 0)
+ if (tick_function_ && tick_interval_.count() > 0)
{
tick_timer_.expires_from_now(boost::posix_time::milliseconds(tick_interval_.count()));
tick_timer_.async_wait([this](const boost::system::error_code& ec)
diff --git a/include/crow/json.h b/include/crow/json.h
index d53dee275..04a6209ae 100644
--- a/include/crow/json.h
+++ b/include/crow/json.h
@@ -41,7 +41,7 @@ namespace crow
inline void escape(const std::string& str, std::string& ret)
{
ret.reserve(ret.size() + str.size()+str.size()/4);
- for(unsigned char c:str)
+ for(auto c:str)
{
switch(c)
{
@@ -1224,15 +1224,30 @@ namespace crow
class wvalue : public returnable
{
friend class crow::mustache::template_t;
+
+ public:
+ using object_type =
+#ifdef CROW_JSON_USE_MAP
+ std::map;
+#else
+ std::unordered_map;
+#endif
+
public:
type t() const { return t_; }
private:
type t_{type::Null}; ///< The type of the value.
num_type nt{num_type::Null}; ///< The specific type of the number if \ref t_ is a number.
- union {
+ union number {
double d;
int64_t si;
- uint64_t ui {};
+ uint64_t ui;
+
+ public:
+ constexpr number() noexcept : ui() {} /* default constructor initializes unsigned integer. */
+ constexpr number(std::uint64_t value) noexcept : ui(value) {}
+ constexpr number(std::int64_t value) noexcept : si(value) {}
+ constexpr number(double value) noexcept : d(value) {}
} num; ///< Value if type is a number.
std::string s; ///< Value if type is a string.
std::unique_ptr> l; ///< Value if type is a list.
@@ -1243,9 +1258,35 @@ namespace crow
#endif
public:
-
wvalue() : returnable("application/json") {}
+ wvalue(std::nullptr_t) : returnable("application/json"), t_(type::Null) {}
+
+ wvalue(bool value) : returnable("application/json"), t_(value ? type::True : type::False) {}
+
+ wvalue(std::uint8_t value) : returnable("application/json"), t_(type::Number), nt(num_type::Unsigned_integer), num(static_cast(value)) {}
+ wvalue(std::uint16_t value) : returnable("application/json"), t_(type::Number), nt(num_type::Unsigned_integer), num(static_cast(value)) {}
+ wvalue(std::uint32_t value) : returnable("application/json"), t_(type::Number), nt(num_type::Unsigned_integer), num(static_cast(value)) {}
+ wvalue(std::uint64_t value) : returnable("application/json"), t_(type::Number), nt(num_type::Unsigned_integer), num(static_cast(value)) {}
+
+ wvalue(std::int8_t value) : returnable("application/json"), t_(type::Number), nt(num_type::Signed_integer), num(static_cast(value)) {}
+ wvalue(std::int16_t value) : returnable("application/json"), t_(type::Number), nt(num_type::Signed_integer), num(static_cast(value)) {}
+ wvalue(std::int32_t value) : returnable("application/json"), t_(type::Number), nt(num_type::Signed_integer), num(static_cast(value)) {}
+ wvalue(std::int64_t value) : returnable("application/json"), t_(type::Number), nt(num_type::Signed_integer), num(static_cast(value)) {}
+
+ wvalue(float value) : returnable("application/json"), t_(type::Number), nt(num_type::Floating_point), num(static_cast(value)) {}
+ wvalue(double value) : returnable("application/json"), t_(type::Number), nt(num_type::Floating_point), num(static_cast(value)) {}
+
+ wvalue(char const* value) : returnable("application/json"), t_(type::String), s(value) {}
+
+ wvalue(std::string const& value) : returnable("application/json"), t_(type::String), s(value) {}
+ wvalue(std::string&& value) : returnable("application/json"), t_(type::String), s(std::move(value)) {}
+
+ wvalue(std::initializer_list> initializer_list) : returnable("application/json"), t_(type::Object), o(new object_type(initializer_list)) {}
+
+ wvalue(object_type const& value) : returnable("application/json"), t_(type::Object), o(new object_type(value)) {}
+ wvalue(object_type&& value) : returnable("application/json"), t_(type::Object), o(new object_type(std::move(value))) {}
+
wvalue(std::vector& r) : returnable("application/json")
{
t_ = type::List;
@@ -1508,6 +1549,42 @@ namespace crow
return *this;
}
+ wvalue& operator=(std::initializer_list> initializer_list)
+ {
+ if (t_ != type::Object) {
+ reset();
+ t_ = type::Object;
+ o = std::unique_ptr(new object_type(initializer_list));
+ } else {
+ (*o) = initializer_list;
+ }
+ return *this;
+ }
+
+ wvalue& operator=(object_type const& value)
+ {
+ if (t_ != type::Object) {
+ reset();
+ t_ = type::Object;
+ o = std::unique_ptr(new object_type(value));
+ } else {
+ (*o) = value;
+ }
+ return *this;
+ }
+
+ wvalue& operator=(object_type&& value)
+ {
+ if (t_ != type::Object) {
+ reset();
+ t_ = type::Object;
+ o = std::unique_ptr(new object_type(std::move(value)));
+ } else {
+ (*o) = std::move(value);
+ }
+ return *this;
+ }
+
wvalue& operator[](unsigned index)
{
if (t_ != type::List)
diff --git a/include/crow/middlewares/utf-8.h b/include/crow/middlewares/utf-8.h
new file mode 100644
index 000000000..909c9a2de
--- /dev/null
+++ b/include/crow/middlewares/utf-8.h
@@ -0,0 +1,27 @@
+#pragma once
+#include "crow/http_request.h"
+#include "crow/http_response.h"
+
+namespace crow
+{
+
+ struct UTF8
+ {
+ struct context
+ {
+ };
+
+ void before_handle(request& /*req*/, response& /*res*/, context& /*ctx*/)
+ {
+ }
+
+ void after_handle(request& /*req*/, response& res, context& ctx)
+ {
+ if (get_header_value(res.headers, "Content-Type").empty())
+ {
+ res.set_header("Content-Type", "text/plain; charset=utf-8");
+ }
+ }
+ };
+
+}
diff --git a/include/crow/routing.h b/include/crow/routing.h
index f31c8386b..65746c6e5 100644
--- a/include/crow/routing.h
+++ b/include/crow/routing.h
@@ -14,9 +14,16 @@
#include "crow/utility.h"
#include "crow/logging.h"
#include "crow/websocket.h"
+#include "crow/mustache.h"
namespace crow
{
+
+#ifdef CROW_MAIN
+ uint16_t INVALID_BP_ID{0xFFFF};
+#else
+ extern uint16_t INVALID_BP_ID;
+#endif
/// A base class for all rules.
/// Used to provide a common interface for code dealing with different types of rules.
@@ -70,6 +77,9 @@ namespace crow
}
}
+
+ std::string custom_templates_base;
+
const std::string& rule() { return rule_; }
protected:
@@ -81,6 +91,7 @@ namespace crow
std::unique_ptr rule_to_upgrade_;
friend class Router;
+ friend class Blueprint;
template
friend struct RuleParameterTraits;
};
@@ -499,6 +510,10 @@ namespace crow
void handle(const request& req, response& res, const routing_params& params) override
{
+ if (!custom_templates_base.empty())
+ mustache::set_base(custom_templates_base);
+ else if (mustache::detail::get_template_base_directory_ref() != "templates")
+ mustache::set_base("templates");
erased_handler_(req, res, params);
}
@@ -674,6 +689,11 @@ namespace crow
void handle(const request& req, response& res, const routing_params& params) override
{
+ if (!custom_templates_base.empty())
+ mustache::set_base(custom_templates_base);
+ else if (mustache::detail::get_template_base_directory_ref() != "templates")
+ mustache::set_base("templates");
+
detail::routing_handler_call_helper::call<
detail::routing_handler_call_helper::call_params<
decltype(handler_)>,
@@ -694,222 +714,297 @@ namespace crow
const int RULE_SPECIAL_REDIRECT_SLASH = 1;
+
/// A search tree.
class Trie
{
public:
struct Node
{
- unsigned rule_index{};
- std::array(ParamType::MAX)> param_childrens{};
- std::unordered_map children;
+ uint16_t rule_index{};
+ // Assign the index to the maximum 32 unsigned integer value by default so that any other number (specifically 0) is a valid BP id.
+ uint16_t blueprint_index{INVALID_BP_ID};
+ std::string key;
+ ParamType param = ParamType::MAX; // MAX = No param.
+ std::vector children;
bool IsSimpleNode() const
{
return
!rule_index &&
- std::all_of(
- std::begin(param_childrens),
- std::end(param_childrens),
- [](unsigned x){ return !x; });
+ blueprint_index == INVALID_BP_ID &&
+ children.size() < 2 &&
+ param == ParamType::MAX &&
+ std::all_of(std::begin(children), std::end(children), [](Node* x){ return x->param == ParamType::MAX; });
}
};
- Trie() : nodes_(1)
+
+ Trie()
{
}
///Check whether or not the trie is empty.
bool is_empty()
{
- return nodes_.size() > 1;
+ return head_.children.empty();
}
- private:
- void optimizeNode(Node* node)
+ void optimize()
{
- for(auto x : node->param_childrens)
+ for (auto child: head_.children)
{
- if (!x)
- continue;
- Node* child = &nodes_[x];
optimizeNode(child);
}
+ }
+
+
+ private:
+
+
+ void optimizeNode(Node* node)
+ {
if (node->children.empty())
return;
- bool mergeWithChild = true;
- for(auto& kv : node->children)
+ if (node->IsSimpleNode())
+ {
+ Node* child_temp = node->children[0];
+ node->key = node->key + child_temp->key;
+ node->rule_index = child_temp->rule_index;
+ node->blueprint_index = child_temp->blueprint_index;
+ node->children = std::move(child_temp->children);
+ delete(child_temp);
+ optimizeNode(node);
+ }
+ else
{
- Node* child = &nodes_[kv.second];
- if (!child->IsSimpleNode())
+ for(auto& child : node->children)
{
- mergeWithChild = false;
- break;
+ optimizeNode(child);
}
}
- if (mergeWithChild)
+ }
+
+ void debug_node_print(Node* node, int level)
+ {
+ if (node->param != ParamType::MAX)
{
- decltype(node->children) merged;
- for(auto& kv : node->children)
+ switch(node->param)
{
- Node* child = &nodes_[kv.second];
- for(auto& child_kv : child->children)
- {
- merged[kv.first + child_kv.first] = child_kv.second;
- }
+ case ParamType::INT:
+ CROW_LOG_DEBUG << std::string(2*level, ' ') << "";
+ break;
+ case ParamType::UINT:
+ CROW_LOG_DEBUG << std::string(2*level, ' ') << "";
+ break;
+ case ParamType::DOUBLE:
+ CROW_LOG_DEBUG << std::string(2*level, ' ') << "";
+ break;
+ case ParamType::STRING:
+ CROW_LOG_DEBUG << std::string(2*level, ' ') << "";
+ break;
+ case ParamType::PATH:
+ CROW_LOG_DEBUG << std::string(2*level, ' ') << "";
+ break;
+ default:
+ CROW_LOG_DEBUG << std::string(2*level, ' ') << "";
+ break;
}
- node->children = std::move(merged);
- optimizeNode(node);
}
else
+ CROW_LOG_DEBUG << std::string(2*level, ' ') << node->key;
+
+ for(auto& child : node->children)
{
- for(auto& kv : node->children)
- {
- Node* child = &nodes_[kv.second];
- optimizeNode(child);
- }
+ debug_node_print(child, level+1);
}
}
+ public:
- void optimize()
+ void debug_print()
{
- optimizeNode(head());
+ CROW_LOG_DEBUG << "HEAD";
+ for (auto& child : head_.children)
+ debug_node_print(child, 1);
}
- public:
void validate()
{
- if (!head()->IsSimpleNode())
+ if (!head_.IsSimpleNode())
throw std::runtime_error("Internal error: Trie header should be simple!");
optimize();
}
- std::pair find(const std::string& req_url, const Node* node = nullptr, unsigned pos = 0, routing_params* params = nullptr) const
+ //Rule_index, Blueprint_index, routing_params
+ std::tuple, routing_params> find(const std::string& req_url, const Node* node = nullptr, unsigned pos = 0, routing_params* params = nullptr, std::vector* blueprints = nullptr) const
{
+ //start params as an empty struct
routing_params empty;
if (params == nullptr)
params = ∅
+ //same for blueprint vector
+ std::vector MT;
+ if (blueprints == nullptr)
+ blueprints = &MT;
- unsigned found{};
- routing_params match_params;
+ uint16_t found{}; //The rule index to be found
+ std::vector found_BP; //The Blueprint indices to be found
+ routing_params match_params; //supposedly the final matched parameters
+ //start from the head node
if (node == nullptr)
- node = head();
- if (pos == req_url.size())
- return {node->rule_index, *params};
+ node = &head_;
- auto update_found = [&found, &match_params](std::pair& ret)
+ auto update_found = [&found, &found_BP, &match_params](std::tuple, routing_params>& ret)
{
- if (ret.first && (!found || found > ret.first))
+ found_BP = std::move(std::get<1>(ret));
+ if (std::get<0>(ret) && (!found || found > std::get<0>(ret)))
{
- found = ret.first;
- match_params = std::move(ret.second);
+ found = std::get<0>(ret);
+ match_params = std::move(std::get<2>(ret));
}
};
- if (node->param_childrens[static_cast(ParamType::INT)])
+ //if the function was called on a node at the end of the string (the last recursion), return the nodes rule index, and whatever params were passed to the function
+ if (pos == req_url.size())
{
- char c = req_url[pos];
- if ((c >= '0' && c <= '9') || c == '+' || c == '-')
- {
- char* eptr;
- errno = 0;
- long long int value = strtoll(req_url.data()+pos, &eptr, 10);
- if (errno != ERANGE && eptr != req_url.data()+pos)
- {
- params->int_params.push_back(value);
- auto ret = find(req_url, &nodes_[node->param_childrens[static_cast(ParamType::INT)]], eptr - req_url.data(), params);
- update_found(ret);
- params->int_params.pop_back();
- }
- }
+ found_BP = std::move(*blueprints);
+ return {node->rule_index, *blueprints, *params};
}
- if (node->param_childrens[static_cast(ParamType::UINT)])
+ bool found_fragment = false;
+
+ for(auto& child : node->children)
{
- char c = req_url[pos];
- if ((c >= '0' && c <= '9') || c == '+')
+ if (child->param != ParamType::MAX)
{
- char* eptr;
- errno = 0;
- unsigned long long int value = strtoull(req_url.data()+pos, &eptr, 10);
- if (errno != ERANGE && eptr != req_url.data()+pos)
+ if (child->param == ParamType::INT)
{
- params->uint_params.push_back(value);
- auto ret = find(req_url, &nodes_[node->param_childrens[static_cast(ParamType::UINT)]], eptr - req_url.data(), params);
- update_found(ret);
- params->uint_params.pop_back();
+ char c = req_url[pos];
+ if ((c >= '0' && c <= '9') || c == '+' || c == '-')
+ {
+ char* eptr;
+ errno = 0;
+ long long int value = strtoll(req_url.data()+pos, &eptr, 10);
+ if (errno != ERANGE && eptr != req_url.data()+pos)
+ {
+ found_fragment = true;
+ params->int_params.push_back(value);
+ if (child->blueprint_index != INVALID_BP_ID) blueprints->push_back(child->blueprint_index);
+ auto ret = find(req_url, child, eptr - req_url.data(), params, blueprints);
+ update_found(ret);
+ params->int_params.pop_back();
+ blueprints->pop_back();
+ }
+ }
}
- }
- }
- if (node->param_childrens[static_cast(ParamType::DOUBLE)])
- {
- char c = req_url[pos];
- if ((c >= '0' && c <= '9') || c == '+' || c == '-' || c == '.')
- {
- char* eptr;
- errno = 0;
- double value = strtod(req_url.data()+pos, &eptr);
- if (errno != ERANGE && eptr != req_url.data()+pos)
+ else if (child->param == ParamType::UINT)
{
- params->double_params.push_back(value);
- auto ret = find(req_url, &nodes_[node->param_childrens[static_cast(ParamType::DOUBLE)]], eptr - req_url.data(), params);
- update_found(ret);
- params->double_params.pop_back();
+ char c = req_url[pos];
+ if ((c >= '0' && c <= '9') || c == '+')
+ {
+ char* eptr;
+ errno = 0;
+ unsigned long long int value = strtoull(req_url.data()+pos, &eptr, 10);
+ if (errno != ERANGE && eptr != req_url.data()+pos)
+ {
+ found_fragment = true;
+ params->uint_params.push_back(value);
+ if (child->blueprint_index != INVALID_BP_ID) blueprints->push_back(child->blueprint_index);
+ auto ret = find(req_url, child, eptr - req_url.data(), params, blueprints);
+ update_found(ret);
+ params->uint_params.pop_back();
+ blueprints->pop_back();
+ }
+ }
}
- }
- }
- if (node->param_childrens[static_cast(ParamType::STRING)])
- {
- size_t epos = pos;
- for(; epos < req_url.size(); epos ++)
- {
- if (req_url[epos] == '/')
- break;
- }
+ else if (child->param == ParamType::DOUBLE)
+ {
+ char c = req_url[pos];
+ if ((c >= '0' && c <= '9') || c == '+' || c == '-' || c == '.')
+ {
+ char* eptr;
+ errno = 0;
+ double value = strtod(req_url.data()+pos, &eptr);
+ if (errno != ERANGE && eptr != req_url.data()+pos)
+ {
+ found_fragment = true;
+ params->double_params.push_back(value);
+ if (child->blueprint_index != INVALID_BP_ID) blueprints->push_back(child->blueprint_index);
+ auto ret = find(req_url, child, eptr - req_url.data(), params, blueprints);
+ update_found(ret);
+ params->double_params.pop_back();
+ blueprints->pop_back();
+ }
+ }
+ }
- if (epos != pos)
- {
- params->string_params.push_back(req_url.substr(pos, epos-pos));
- auto ret = find(req_url, &nodes_[node->param_childrens[static_cast(ParamType::STRING)]], epos, params);
- update_found(ret);
- params->string_params.pop_back();
- }
- }
+ else if (child->param == ParamType::STRING)
+ {
+ size_t epos = pos;
+ for(; epos < req_url.size(); epos ++)
+ {
+ if (req_url[epos] == '/')
+ break;
+ }
- if (node->param_childrens[static_cast(ParamType::PATH)])
- {
- size_t epos = req_url.size();
+ if (epos != pos)
+ {
+ found_fragment = true;
+ params->string_params.push_back(req_url.substr(pos, epos-pos));
+ if (child->blueprint_index != INVALID_BP_ID) blueprints->push_back(child->blueprint_index);
+ auto ret = find(req_url, child, epos, params, blueprints);
+ update_found(ret);
+ params->string_params.pop_back();
+ blueprints->pop_back();
+ }
+ }
- if (epos != pos)
- {
- params->string_params.push_back(req_url.substr(pos, epos-pos));
- auto ret = find(req_url, &nodes_[node->param_childrens[static_cast(ParamType::PATH)]], epos, params);
- update_found(ret);
- params->string_params.pop_back();
- }
- }
+ else if (child->param == ParamType::PATH)
+ {
+ size_t epos = req_url.size();
- for(auto& kv : node->children)
- {
- const std::string& fragment = kv.first;
- const Node* child = &nodes_[kv.second];
+ if (epos != pos)
+ {
+ found_fragment = true;
+ params->string_params.push_back(req_url.substr(pos, epos-pos));
+ if (child->blueprint_index != INVALID_BP_ID) blueprints->push_back(child->blueprint_index);
+ auto ret = find(req_url, child, epos, params, blueprints);
+ update_found(ret);
+ params->string_params.pop_back();
+ blueprints->pop_back();
+ }
+ }
+ }
- if (req_url.compare(pos, fragment.size(), fragment) == 0)
+ else
{
- auto ret = find(req_url, child, pos + fragment.size(), params);
- update_found(ret);
+ const std::string& fragment = child->key;
+ if (req_url.compare(pos, fragment.size(), fragment) == 0)
+ {
+ found_fragment = true;
+ if (child->blueprint_index != INVALID_BP_ID) blueprints->push_back(child->blueprint_index);
+ auto ret = find(req_url, child, pos + fragment.size(), params, blueprints);
+ update_found(ret);
+ blueprints->pop_back();
+ }
}
}
- return {found, match_params};
+ if (!found_fragment)
+ found_BP = std::move(*blueprints);
+
+ return {found, found_BP, match_params}; //Called after all the recursions have been done
}
- void add(const std::string& url, unsigned rule_index)
+ //This functions assumes any blueprint info passed is valid
+ void add(const std::string& url, uint16_t rule_index, unsigned bp_prefix_length = 0, uint16_t blueprint_index = INVALID_BP_ID)
{
- unsigned idx{0};
+ Node* idx = &head_;
+
+ bool has_blueprint = bp_prefix_length != 0 && blueprint_index != INVALID_BP_ID;
for(unsigned i = 0; i < url.size(); i ++)
{
@@ -935,12 +1030,23 @@ namespace crow
{
if (url.compare(i, x.name.size(), x.name) == 0)
{
- if (!nodes_[idx].param_childrens[static_cast(x.type)])
+ bool found = false;
+ for (Node* child : idx->children)
{
- auto new_node_idx = new_node();
- nodes_[idx].param_childrens[static_cast(x.type)] = new_node_idx;
+ if (child->param == x.type)
+ {
+ idx = child;
+ i += x.name.size();
+ found = true;
+ break;
+ }
}
- idx = nodes_[idx].param_childrens[static_cast(x.type)];
+ if (found)
+ break;
+
+ auto new_node_idx = new_node(idx);
+ new_node_idx->param = x.type;
+ idx = new_node_idx;
i += x.name.size();
break;
}
@@ -950,85 +1056,202 @@ namespace crow
}
else
{
- std::string piece(&c, 1);
- if (!nodes_[idx].children.count(piece))
+ //This part assumes the tree is unoptimized (every node has a max 1 character key)
+ bool piece_found = false;
+ for (auto& child : idx->children)
+ {
+ if (child->key[0] == c)
+ {
+ idx = child;
+ piece_found = true;
+ break;
+ }
+ }
+ if (!piece_found)
{
- auto new_node_idx = new_node();
- nodes_[idx].children.emplace(piece, new_node_idx);
+ auto new_node_idx = new_node(idx);
+ new_node_idx->key = c;
+ //The assumption here is that you'd only need to add a blueprint index if the tree didn't have the BP prefix.
+ if (has_blueprint && i == bp_prefix_length)
+ new_node_idx->blueprint_index = blueprint_index;
+ idx = new_node_idx;
}
- idx = nodes_[idx].children[piece];
}
}
- if (nodes_[idx].rule_index)
+
+ //check if the last node already has a value (exact url already in Trie)
+ if (idx->rule_index)
throw std::runtime_error("handler already exists for " + url);
- nodes_[idx].rule_index = rule_index;
+ idx->rule_index = rule_index;
}
- private:
- void debug_node_print(Node* n, int level)
+
+ size_t get_size()
{
- for(int i = 0; i < static_cast(ParamType::MAX); i ++)
- {
- if (n->param_childrens[i])
- {
- CROW_LOG_DEBUG << std::string(2*level, ' ') /*<< "("<param_childrens[i]<<") "*/;
- switch(static_cast(i))
- {
- case ParamType::INT:
- CROW_LOG_DEBUG << "";
- break;
- case ParamType::UINT:
- CROW_LOG_DEBUG << "";
- break;
- case ParamType::DOUBLE:
- CROW_LOG_DEBUG << "";
- break;
- case ParamType::STRING:
- CROW_LOG_DEBUG << "";
- break;
- case ParamType::PATH:
- CROW_LOG_DEBUG << "";
- break;
- default:
- CROW_LOG_DEBUG << "";
- break;
- }
+ return get_size(&head_);
+ }
- debug_node_print(&nodes_[n->param_childrens[i]], level+1);
- }
- }
- for(auto& kv : n->children)
+ size_t get_size(Node* node)
+ {
+ unsigned size = 5 ; //rule_index, blueprint_index, and param
+ size += (node->key.size()); //each character in the key is 1 byte
+ for (auto child: node->children)
{
- CROW_LOG_DEBUG << std::string(2*level, ' ') /*<< "(" << kv.second << ") "*/ << kv.first;
- debug_node_print(&nodes_[kv.second], level+1);
+ size += get_size(child);
}
+ return size;
+ }
+
+
+ private:
+
+ Node* new_node(Node* parent)
+ {
+ auto& children = parent->children;
+ children.resize(children.size()+1);
+ children[children.size()-1] = new Node();
+ return children[children.size()-1];
}
+
+ Node head_;
+ };
+
+ /// A blueprint can be considered a smaller section of a Crow app, specifically where the router is conecerned.
+ ///
+ /// You can use blueprints to assign a common prefix to rules' prefix, set custom static and template folders, and set a custom catchall route.
+ /// You can also assign nest blueprints for maximum Compartmentalization.
+ class Blueprint
+ {
public:
- void debug_print()
+
+ Blueprint(const std::string& prefix):
+ prefix_(prefix){};
+
+ Blueprint(const std::string& prefix, const std::string& static_dir):
+ prefix_(prefix), static_dir_(static_dir){};
+
+ Blueprint(const std::string& prefix, const std::string& static_dir, const std::string& templates_dir):
+ prefix_(prefix), static_dir_(static_dir), templates_dir_(templates_dir){};
+
+/*
+ Blueprint(Blueprint& other)
{
- debug_node_print(head(), 0);
+ prefix_ = std::move(other.prefix_);
+ all_rules_ = std::move(other.all_rules_);
}
- private:
- const Node* head() const
+ Blueprint(const Blueprint& other)
{
- return &nodes_.front();
+ prefix_ = other.prefix_;
+ all_rules_ = other.all_rules_;
+ }
+*/
+ Blueprint(Blueprint&& value)
+ {
+ *this = std::move(value);
}
- Node* head()
+ Blueprint& operator = (const Blueprint& value) = delete;
+
+ Blueprint& operator = (Blueprint&& value) noexcept
{
- return &nodes_.front();
+ prefix_ = std::move(value.prefix_);
+ all_rules_ = std::move(value.all_rules_);
+ catchall_rule_ = std::move(value.catchall_rule_);
+ return *this;
}
- unsigned new_node()
+ bool operator == (const Blueprint& value)
{
- nodes_.resize(nodes_.size()+1);
- return nodes_.size() - 1;
+ return value.prefix() == prefix_;
}
- std::vector nodes_;
- };
+ bool operator != (const Blueprint& value)
+ {
+ return value.prefix() != prefix_;
+ }
+
+ std::string prefix() const
+ {
+ return prefix_;
+ }
+
+ std::string static_dir() const
+ {
+ return static_dir_;
+ }
+
+ DynamicRule& new_rule_dynamic(std::string&& rule)
+ {
+ std::string new_rule = std::move(rule);
+ new_rule = '/' + prefix_ + new_rule;
+ auto ruleObject = new DynamicRule(new_rule);
+ ruleObject->custom_templates_base = templates_dir_;
+ all_rules_.emplace_back(ruleObject);
+
+ return *ruleObject;
+ }
+
+ template
+ typename black_magic::arguments::type::template rebind& new_rule_tagged(std::string&& rule)
+ {
+ std::string new_rule = std::move(rule);
+ new_rule = '/' + prefix_ + new_rule;
+ using RuleT = typename black_magic::arguments::type::template rebind;
+
+ auto ruleObject = new RuleT(new_rule);
+ ruleObject->custom_templates_base = templates_dir_;
+ all_rules_.emplace_back(ruleObject);
+
+ return *ruleObject;
+ }
+
+ void register_blueprint(Blueprint& blueprint)
+ {
+ if (blueprints_.empty() || std::find(blueprints_.begin(), blueprints_.end(), &blueprint) == blueprints_.end())
+ {
+ apply_blueprint(blueprint);
+ blueprints_.emplace_back(&blueprint);
+ }
+ else
+ throw std::runtime_error("blueprint \"" + blueprint.prefix_ + "\" already exists in blueprint \"" + prefix_ + '\"');
+ }
+
+
+ CatchallRule& catchall_rule()
+ {
+ return catchall_rule_;
+ }
+
+ private:
+
+ void apply_blueprint(Blueprint& blueprint)
+ {
+
+ blueprint.prefix_ = prefix_ + '/' + blueprint.prefix_;
+ blueprint.static_dir_ = static_dir_ + '/' + blueprint.static_dir_;
+ blueprint.templates_dir_ = templates_dir_ + '/' + blueprint.templates_dir_;
+ for (auto& rule : blueprint.all_rules_)
+ {
+ std::string new_rule = '/' + prefix_ + rule->rule_;
+ rule->rule_ = new_rule;
+ }
+ for (Blueprint* bp_child : blueprint.blueprints_)
+ {
+ Blueprint& bp_ref = *bp_child;
+ apply_blueprint(bp_ref);
+ }
+ }
+
+ std::string prefix_;
+ std::string static_dir_;
+ std::string templates_dir_;
+ std::vector> all_rules_;
+ CatchallRule catchall_rule_;
+ std::vector blueprints_;
+ friend class Router;
+ };
/// Handles matching requests to existing rules and upgrade requests.
class Router
@@ -1062,7 +1285,7 @@ namespace crow
return catchall_rule_;
}
- void internal_add_rule_object(const std::string& rule, BaseRule* ruleObject)
+ void internal_add_rule_object(const std::string& rule, BaseRule* ruleObject, const uint16_t& BP_index, std::vector& blueprints)
{
bool has_trailing_slash = false;
std::string rule_without_trailing_slash;
@@ -1076,20 +1299,85 @@ namespace crow
ruleObject->foreach_method([&](int method)
{
per_methods_[method].rules.emplace_back(ruleObject);
- per_methods_[method].trie.add(rule, per_methods_[method].rules.size() - 1);
+ per_methods_[method].trie.add(rule, per_methods_[method].rules.size() - 1, BP_index != INVALID_BP_ID ? blueprints[BP_index]->prefix().length() : 0, BP_index);
// directory case:
// request to '/about' url matches '/about/' rule
if (has_trailing_slash)
{
- per_methods_[method].trie.add(rule_without_trailing_slash, RULE_SPECIAL_REDIRECT_SLASH);
+ per_methods_[method].trie.add(rule_without_trailing_slash, RULE_SPECIAL_REDIRECT_SLASH, BP_index != INVALID_BP_ID ? blueprints_[BP_index]->prefix().length() : 0, BP_index);
}
});
}
+ void register_blueprint(Blueprint& blueprint)
+ {
+ if (std::find(blueprints_.begin(), blueprints_.end(), &blueprint) == blueprints_.end())
+ {
+ blueprints_.emplace_back(&blueprint);
+ }
+ else
+ throw std::runtime_error("blueprint \"" + blueprint.prefix_ + "\" already exists in router");
+ }
+
+ void get_recursive_child_methods(Blueprint* blueprint, std::vector& methods)
+ {
+ //we only need to deal with children if the blueprint has absolutely no methods (meaning its index won't be added to the trie)
+ if (blueprint->static_dir_.empty() && blueprint->all_rules_.empty())
+ {
+ for(Blueprint* bp : blueprint->blueprints_)
+ {
+ get_recursive_child_methods(bp, methods);
+ }
+ }
+ else if (!blueprint->static_dir_.empty())
+ methods.emplace_back(HTTPMethod::Get);
+ for (auto& rule: blueprint->all_rules_)
+ {
+ rule->foreach_method([&methods](unsigned method){
+ HTTPMethod method_final = static_cast(method);
+ if (std::find(methods.begin(), methods.end(), method_final) == methods.end())
+ methods.emplace_back(method_final);
+ });
+ }
+ }
+
+ void validate_bp(std::vector blueprints)
+ {
+ for (unsigned i = 0; i < blueprints.size(); i++)
+ {
+ Blueprint* blueprint = blueprints[i];
+ if (blueprint->static_dir_ == "" && blueprint->all_rules_.empty())
+ {
+ std::vector methods;
+ get_recursive_child_methods(blueprint, methods);
+ for (HTTPMethod x : methods)
+ {
+ int i = static_cast(x);
+ per_methods_[i].trie.add(blueprint->prefix(), 0, blueprint->prefix().length(), i);
+ }
+ }
+ for (auto& rule: blueprint->all_rules_)
+ {
+ if (rule)
+ {
+ auto upgraded = rule->upgrade();
+ if (upgraded)
+ rule = std::move(upgraded);
+ rule->validate();
+ internal_add_rule_object(rule->rule(), rule.get(), i, blueprints);
+ }
+ }
+ validate_bp(blueprint->blueprints_);
+ }
+ }
+
void validate()
{
+ //Take all the routes from the registered blueprints and add them to `all_rules_` to be processed.
+ validate_bp(blueprints_);
+
for(auto& rule:all_rules_)
{
if (rule)
@@ -1098,7 +1386,7 @@ namespace crow
if (upgraded)
rule = std::move(upgraded);
rule->validate();
- internal_add_rule_object(rule->rule(), rule.get());
+ internal_add_rule_object(rule->rule(), rule.get(), INVALID_BP_ID, blueprints_);
}
}
for(auto& per_method:per_methods_)
@@ -1116,13 +1404,13 @@ namespace crow
auto& per_method = per_methods_[static_cast(req.method)];
auto& rules = per_method.rules;
- unsigned rule_index = per_method.trie.find(req.url).first;
+ unsigned rule_index = std::get<0>(per_method.trie.find(req.url));
if (!rule_index)
{
for (auto& per_method: per_methods_)
{
- if (per_method.trie.find(req.url).first)
+ if (std::get<0>(per_method.trie.find(req.url)))
{
CROW_LOG_DEBUG << "Cannot match method " << req.url << " " << method_name(req.method);
res = response(405);
@@ -1181,6 +1469,51 @@ namespace crow
}
}
+ void get_found_bp(std::vector& bp_i, std::vector& blueprints, std::vector& found_bps, uint16_t index = 0)
+ {
+ // This statement makes 3 assertions:
+ // 1. The index is above 0.
+ // 2. The index does not lie outside the given blueprint list.
+ // 3. The next blueprint we're adding has a prefix that starts the same as the already added blueprint + a slash (the rest is irrelevant).
+ //
+ // This is done to prevent a blueprint that has a prefix of "bp_prefix2" to be assumed as a child of one that has "bp_prefix".
+ //
+ // If any of the assertions is untrue, we delete the last item added, and continue using the blueprint list of the blueprint found before, the topmost being the router's list
+ auto verify_prefix = [&bp_i, &index, &blueprints, &found_bps]()
+ {
+ return index > 0 &&
+ bp_i[index] < blueprints.size() &&
+ blueprints[bp_i[index]]->prefix().substr(0,found_bps[index-1]->prefix().length()+1)
+ .compare(std::string(found_bps[index-1]->prefix()+'/')) == 0;
+ };
+ if (index < bp_i.size())
+ {
+
+ if (verify_prefix())
+ {
+ found_bps.push_back(blueprints[bp_i[index]]);
+ get_found_bp(bp_i, found_bps.back()->blueprints_, found_bps, ++index);
+ }
+ else
+ {
+ if (!found_bps.empty())
+ found_bps.pop_back();
+
+ if (found_bps.empty())
+ {
+ found_bps.push_back(blueprints_[bp_i[index]]);
+ get_found_bp(bp_i, found_bps.back()->blueprints_, found_bps, ++index);
+ }
+ else
+ {
+ Blueprint* last_element = found_bps.back();
+ found_bps.push_back(last_element->blueprints_[bp_i[index]]);
+ get_found_bp(bp_i, found_bps.back()->blueprints_, found_bps, ++index);
+ }
+ }
+ }
+ }
+
void handle(const request& req, response& res)
{
HTTPMethod method_actual = req.method;
@@ -1199,7 +1532,7 @@ namespace crow
{
for(int i = 0; i < static_cast(HTTPMethod::InternalMethodCount); i ++)
{
- if (per_methods_[i].trie.is_empty())
+ if (!per_methods_[i].trie.is_empty())
{
allow += method_name(static_cast(i)) + ", ";
}
@@ -1215,7 +1548,7 @@ namespace crow
{
for(int i = 0; i < static_cast(HTTPMethod::InternalMethodCount); i ++)
{
- if (per_methods_[i].trie.find(req.url).first)
+ if (std::get<0>(per_methods_[i].trie.find(req.url)))
{
allow += method_name(static_cast(i)) + ", ";
}
@@ -1245,13 +1578,13 @@ namespace crow
auto found = trie.find(req.url);
- unsigned rule_index = found.first;
+ unsigned rule_index = std::get<0>(found);
if (!rule_index)
{
for (auto& per_method: per_methods_)
{
- if (per_method.trie.find(req.url).first)
+ if (std::get<0>(per_method.trie.find(req.url)))
{
CROW_LOG_DEBUG << "Cannot match method " << req.url << " " << method_name(method_actual);
res = response(405);
@@ -1260,15 +1593,32 @@ namespace crow
}
}
- if (catchall_rule_.has_handler())
+ std::vector bps_found;
+ get_found_bp(std::get<1>(found), blueprints_, bps_found);
+ bool no_bp_catchall = true;
+ for (int i = bps_found.size()-1; i > 0; i--)
{
- CROW_LOG_DEBUG << "Cannot match rules " << req.url << ". Redirecting to Catchall rule";
- catchall_rule_.handler_(req, res);
+ std::vector bpi = std::get<1>(found);
+ if (bps_found[i]->catchall_rule().has_handler())
+ {
+ no_bp_catchall = false;
+ CROW_LOG_DEBUG << "Cannot match rules " << req.url << ". Redirecting to Blueprint \"" << bps_found[i]->prefix() << "\" Catchall rule";
+ bps_found[i]->catchall_rule().handler_(req, res);
+ break;
+ }
}
- else
+ if (no_bp_catchall)
{
- CROW_LOG_DEBUG << "Cannot match rules " << req.url;
- res = response(404);
+ if (catchall_rule_.has_handler())
+ {
+ CROW_LOG_DEBUG << "Cannot match rules " << req.url << ". Redirecting to global Catchall rule";
+ catchall_rule_.handler_(req, res);
+ }
+ else
+ {
+ CROW_LOG_DEBUG << "Cannot match rules " << req.url;
+ res = response(404);
+ }
}
res.end();
return;
@@ -1300,7 +1650,7 @@ namespace crow
// any uncaught exceptions become 500s
try
{
- rules[rule_index]->handle(req, res, found.second);
+ rules[rule_index]->handle(req, res, std::get<2>(found));
}
catch(std::exception& e)
{
@@ -1327,6 +1677,11 @@ namespace crow
}
}
+ std::vector& blueprints()
+ {
+ return blueprints_;
+ }
+
private:
CatchallRule catchall_rule_;
@@ -1340,6 +1695,7 @@ namespace crow
};
std::array(HTTPMethod::InternalMethodCount)> per_methods_;
std::vector> all_rules_;
+ std::vector blueprints_;
};
}
diff --git a/include/crow/version.h b/include/crow/version.h
new file mode 100644
index 000000000..60d39ed6f
--- /dev/null
+++ b/include/crow/version.h
@@ -0,0 +1,10 @@
+#pragma once
+
+namespace crow {
+
+#ifdef CROW_MAIN
+ char VERSION[] = "master";
+#else
+ extern char VERSION[];
+#endif
+}
diff --git a/include/crow/websocket.h b/include/crow/websocket.h
index 6b2d4e73d..f32b5189d 100644
--- a/include/crow/websocket.h
+++ b/include/crow/websocket.h
@@ -194,7 +194,7 @@ namespace crow
buf[0] += opcode;
if (size < 126)
{
- buf[1] += size;
+ buf[1] += static_cast(size);
return {buf, buf+2};
}
else if (size < 0x10000)
@@ -408,10 +408,10 @@ namespace crow
break;
case WebSocketReadState::Payload:
{
- size_t to_read = buffer_.size();
+ auto to_read = static_cast(buffer_.size());
if (remaining_length_ < to_read)
to_read = remaining_length_;
- adaptor_.socket().async_read_some( boost::asio::buffer(buffer_, to_read),
+ adaptor_.socket().async_read_some(boost::asio::buffer(buffer_, static_cast(to_read)),
[this](const boost::system::error_code& ec, std::size_t bytes_transferred)
{
is_reading = false;
diff --git a/logo55.png b/logo55.png
index b98b46cc2..c6cd8bf58 100644
Binary files a/logo55.png and b/logo55.png differ
diff --git a/mkdocs.yml b/mkdocs.yml
index d3507a839..968324c90 100644
--- a/mkdocs.yml
+++ b/mkdocs.yml
@@ -1,9 +1,9 @@
site_name: Crow
# Repository
-repo_name: crowcpp/crow
-repo_url: https://github.com/crowcpp/crow
-site_url: https://crowcpp.github.io/crow
+repo_name: CrowCpp/Crow
+repo_url: https://github.com/CrowCpp/Crow
+site_url: https://crowcpp.org
edit_uri: ""
theme:
@@ -16,6 +16,7 @@ theme:
logo: 'assets/favicon.svg'
icon:
repo: fontawesome/brands/github-square
+ custom_dir: docs/overrides
markdown_extensions:
- admonition
@@ -41,6 +42,7 @@ nav:
- Middleware: guides/middleware.md
- SSL: guides/ssl.md
- Static Files: guides/static.md
+ - Blueprints: guides/blueprints.md
- Compression: guides/compression.md
- Websockets: guides/websockets.md
- Writing Tests: guides/testing.md
@@ -60,5 +62,6 @@ extra:
extra_css:
- 'stylesheets/colors.css'
- 'stylesheets/latofonts.css'
+ - 'stylesheets/extra.css'
-copyright: 'Copyright © 2021 CrowCpp'
+copyright: 'Copyright © 2020-2021 CrowCpp'
diff --git a/scripts/merge_all.py b/scripts/merge_all.py
index 25cd19ce2..f50bcaecf 100755
--- a/scripts/merge_all.py
+++ b/scripts/merge_all.py
@@ -18,6 +18,8 @@
middlewares = [x.rsplit(sep, 1)[-1][:-2] for x in glob(pt.join(header_path, ('crow'+sep+'middlewares'+sep+'*.h*')))]
+with open(header_path+'/../LICENSE', 'r') as file:
+ lsc = '/*' + file.read() + '*/'
middlewares_actual = []
if len(sys.argv) > 3:
@@ -83,10 +85,10 @@ def dfs(x):
assert order.index(x) < order.index(y), 'cyclic include detected'
print(order)
-build = []
+build = [lsc]
for header in order:
d = open(pt.join(header_path, header)).read()
build.append(re_depends.sub(lambda x: '\n', d))
build.append('\n')
-open(output_path, 'w').write('\n'.join(build))
\ No newline at end of file
+open(output_path, 'w').write('\n'.join(build))
diff --git a/scripts/release.py b/scripts/release.py
new file mode 100755
index 000000000..3072dd195
--- /dev/null
+++ b/scripts/release.py
@@ -0,0 +1,21 @@
+#!/bin/env python3
+import os
+import sys
+import shutil
+
+if len(sys.argv) != 2:
+ print("Usage: {} VERSION".format(sys.argv[0]))
+ sys.exit(1)
+
+version = str(sys.argv[1])
+
+releasePath = os.path.join(os.path.dirname(__file__), "..", "build_release")
+
+if os.path.exists(releasePath):
+ shutil.rmtree(releasePath)
+
+os.mkdir(releasePath)
+os.chdir(releasePath)
+os.system("cmake -DBUILD_EXAMPLES=OFF -DBUILD_TESTING=OFF -DCPACK_PACKAGE_FILE_NAME=\"crow-{}\" .. && make -j5".format(version))
+os.system("sed -i 's/constexpr char VERSION\\[\\] = \"master\";/constexpr char VERSION\\[\\] = \"{}\";/g' crow_all.h".format(version))
+os.system("cpack")
diff --git a/tests/img/cat.jpg b/tests/img/cat.jpg
index 2cd578ec1..b5fcec3bd 100644
Binary files a/tests/img/cat.jpg and b/tests/img/cat.jpg differ
diff --git a/tests/unittest.cpp b/tests/unittest.cpp
index 70d9fab58..ebb15c854 100644
--- a/tests/unittest.cpp
+++ b/tests/unittest.cpp
@@ -746,6 +746,261 @@ TEST_CASE("json_copy_r_to_w_to_w_to_r")
CHECK("other" == x["obj"]["other"].key());
}
+TEST_CASE("json::wvalue::wvalue(bool)") {
+ CHECK(json::wvalue(true).t() == json::type::True);
+ CHECK(json::wvalue(false).t() == json::type::False);
+}
+
+TEST_CASE("json::wvalue::wvalue(std::uint8_t)") {
+ std::uint8_t i = 42;
+ json::wvalue value = i;
+
+ CHECK(value.t() == json::type::Number);
+ CHECK(value.dump() == "42");
+}
+
+TEST_CASE("json::wvalue::wvalue(std::uint16_t)") {
+ std::uint16_t i = 42;
+ json::wvalue value = i;
+
+ CHECK(value.t() == json::type::Number);
+ CHECK(value.dump() == "42");
+}
+
+TEST_CASE("json::wvalue::wvalue(std::uint32_t)") {
+ std::uint32_t i = 42;
+ json::wvalue value = i;
+
+ CHECK(value.t() == json::type::Number);
+ CHECK(value.dump() == "42");
+}
+
+TEST_CASE("json::wvalue::wvalue(std::uint64_t)") {
+ std::uint64_t i = 42;
+ json::wvalue value = i;
+
+ CHECK(value.t() == json::type::Number);
+ CHECK(value.dump() == "42");
+}
+
+TEST_CASE("json::wvalue::wvalue(std::int8_t)") {
+ std::int8_t i = -42;
+ json::wvalue value = i;
+
+ CHECK(value.t() == json::type::Number);
+ CHECK(value.dump() == "-42");
+}
+
+TEST_CASE("json::wvalue::wvalue(std::int16_t)") {
+ std::int16_t i = -42;
+ json::wvalue value = i;
+
+ CHECK(value.t() == json::type::Number);
+ CHECK(value.dump() == "-42");
+}
+
+TEST_CASE("json::wvalue::wvalue(std::int32_t)") {
+ std::int32_t i = -42;
+ json::wvalue value = i;
+
+ CHECK(value.t() == json::type::Number);
+ CHECK(value.dump() == "-42");
+}
+
+TEST_CASE("json::wvalue::wvalue(std::int64_t)") {
+ std::int64_t i = -42;
+ json::wvalue value = i;
+
+ CHECK(value.t() == json::type::Number);
+ CHECK(value.dump() == "-42");
+}
+
+TEST_CASE("json::wvalue::wvalue(float)") {
+ float f = 4.2;
+ json::wvalue value = f;
+
+ CHECK(value.t() == json::type::Number);
+ CHECK(value.dump() == "4.2");
+}
+
+TEST_CASE("json::wvalue::wvalue(double)") {
+ double d = 4.2;
+ json::wvalue value = d;
+
+ CHECK(value.t() == json::type::Number);
+ CHECK(value.dump() == "4.2");
+}
+
+TEST_CASE("json::wvalue::wvalue(char const*)") {
+ char const* str = "Hello world!";
+ json::wvalue value = str;
+
+ CHECK(value.t() == json::type::String);
+ CHECK(value.dump() == "\"Hello world!\"");
+}
+
+TEST_CASE("json::wvalue::wvalue(std::string const&)") {
+ std::string str = "Hello world!";
+ json::wvalue value = str;
+
+ CHECK(value.t() == json::type::String);
+ CHECK(value.dump() == "\"Hello world!\"");
+}
+
+TEST_CASE("json::wvalue::wvalue(std::string&&)") {
+ std::string str = "Hello world!";
+ json::wvalue value = std::move(str);
+
+ CHECK(value.t() == json::type::String);
+ CHECK(value.dump() == "\"Hello world!\"");
+}
+
+TEST_CASE("json::wvalue::wvalue(std::initializer_list>)") {
+ json::wvalue integer, number, truth, lie, null;
+ integer = 2147483647;
+ number = 23.43;
+ truth = true;
+ lie = false;
+
+ /* initializer-list constructor. */
+ json::wvalue value({
+ {"integer", integer},
+ {"number", number},
+ {"truth", truth},
+ {"lie", lie},
+ {"null", null}
+ });
+
+ CHECK(value["integer"].dump() == integer.dump());
+ CHECK(value["number"].dump() == number.dump());
+ CHECK(value["truth"].dump() == truth.dump());
+ CHECK(value["lie"].dump() == lie.dump());
+ CHECK(value["null"].dump() == null.dump());
+}
+
+TEST_CASE("json::wvalue::wvalue(std::[unordered_]map const&)") {
+ json::wvalue integer, number, truth, lie, null;
+ integer = 2147483647;
+ number = 23.43;
+ truth = true;
+ lie = false;
+
+ json::wvalue::object_type map({
+ {"integer", integer},
+ {"number", number},
+ {"truth", truth},
+ {"lie", lie},
+ {"null", null}
+ });
+
+ json::wvalue value(map); /* copy-constructor. */
+
+ CHECK(value["integer"].dump() == integer.dump());
+ CHECK(value["number"].dump() == number.dump());
+ CHECK(value["truth"].dump() == truth.dump());
+ CHECK(value["lie"].dump() == lie.dump());
+ CHECK(value["null"].dump() == null.dump());
+}
+
+TEST_CASE("json::wvalue::wvalue(std::[unordered_]map&&)") {
+ json::wvalue integer, number, truth, lie, null;
+ integer = 2147483647;
+ number = 23.43;
+ truth = true;
+ lie = false;
+
+ json::wvalue::object_type map = {{
+ {"integer", integer},
+ {"number", number},
+ {"truth", truth},
+ {"lie", lie},
+ {"null", null}
+ }};
+
+ json::wvalue value(std::move(map)); /* move constructor. */
+ // json::wvalue value = std::move(map); /* move constructor. */
+
+ CHECK(value["integer"].dump() == integer.dump());
+ CHECK(value["number"].dump() == number.dump());
+ CHECK(value["truth"].dump() == truth.dump());
+ CHECK(value["lie"].dump() == lie.dump());
+ CHECK(value["null"].dump() == null.dump());
+}
+
+TEST_CASE("json::wvalue::operator=(std::initializer_list>)") {
+ json::wvalue integer, number, truth, lie, null;
+ integer = 2147483647;
+ number = 23.43;
+ truth = true;
+ lie = false;
+
+ json::wvalue value;
+ /* initializer-list assignment. */
+ value = {
+ {"integer", integer},
+ {"number", number},
+ {"truth", truth},
+ {"lie", lie},
+ {"null", null}
+ };
+
+ CHECK(value["integer"].dump() == integer.dump());
+ CHECK(value["number"].dump() == number.dump());
+ CHECK(value["truth"].dump() == truth.dump());
+ CHECK(value["lie"].dump() == lie.dump());
+ CHECK(value["null"].dump() == null.dump());
+}
+
+TEST_CASE("json::wvalue::operator=(std::[unordered_]map const&)") {
+ json::wvalue integer, number, truth, lie, null;
+ integer = 2147483647;
+ number = 23.43;
+ truth = true;
+ lie = false;
+
+ json::wvalue::object_type map({
+ {"integer", integer},
+ {"number", number},
+ {"truth", truth},
+ {"lie", lie},
+ {"null", null}
+ });
+
+ json::wvalue value;
+ value = map; /* copy assignment. */
+
+ CHECK(value["integer"].dump() == integer.dump());
+ CHECK(value["number"].dump() == number.dump());
+ CHECK(value["truth"].dump() == truth.dump());
+ CHECK(value["lie"].dump() == lie.dump());
+ CHECK(value["null"].dump() == null.dump());
+}
+
+TEST_CASE("json::wvalue::operator=(std::[unordered_]map&&)") {
+ json::wvalue integer, number, truth, lie, null;
+ integer = 2147483647;
+ number = 23.43;
+ truth = true;
+ lie = false;
+
+ json::wvalue::object_type map({
+ {"integer", integer},
+ {"number", number},
+ {"truth", truth},
+ {"lie", lie},
+ {"null", null}
+ });
+
+ json::wvalue value;
+ value = std::move(map); /* move assignment. */
+
+ CHECK(value["integer"].dump() == integer.dump());
+ CHECK(value["number"].dump() == number.dump());
+ CHECK(value["truth"].dump() == truth.dump());
+ CHECK(value["lie"].dump() == lie.dump());
+ CHECK(value["null"].dump() == null.dump());
+}
+
TEST_CASE("json_vector")
{//TODO probably make constructors for the same values as = operator
json::wvalue a;
@@ -1418,14 +1673,15 @@ TEST_CASE("stream_response")
SimpleApp app;
+
+ std::string keyword_ = "hello";
+ std::string key_response;
+ for (unsigned int i = 0; i<250000; i++)
+ key_response += keyword_;
+
CROW_ROUTE(app, "/test")
- ([](const crow::request&, crow::response& res)
+ ([&key_response](const crow::request&, crow::response& res)
{
- std::string keyword_ = "hello";
- std::string key_response;
- for (unsigned int i = 0; i<250000; i++)
- key_response += keyword_;
-
res.body = key_response;
res.end();
});
@@ -1433,7 +1689,7 @@ TEST_CASE("stream_response")
app.validate();
//running the test on a separate thread to allow the client to sleep
- std::thread runTest([&app](){
+ std::thread runTest([&app, &key_response](){
auto _ = async(launch::async,
[&] { app.bindaddr(LOCALHOST_ADDRESS).port(45451).run(); });
@@ -1447,9 +1703,6 @@ TEST_CASE("stream_response")
{
asio::streambuf b;
- std::string keyword_ = "hello";
- std::string key_response;
-
asio::ip::tcp::socket c(is);
c.connect(asio::ip::tcp::endpoint(
asio::ip::address::from_string(LOCALHOST_ADDRESS), 45451));
@@ -1459,10 +1712,6 @@ TEST_CASE("stream_response")
static char buf[2048];
c.receive(asio::buffer(buf, 2048));
- //creating the string to compare against
- for (unsigned int i = 0; i<250000; i++)
- key_response += keyword_;
-
//"hello" is 5 bytes, (5*250000)/16384 = 76.2939
for (unsigned int i = 0; i<76; i++)
{
@@ -1887,3 +2136,115 @@ TEST_CASE("catchall")
CHECK(404 == res.code);
}
}
+
+TEST_CASE("blueprint")
+{
+ SimpleApp app;
+ crow::Blueprint bp("bp_prefix", "cstat", "ctemplate");
+ crow::Blueprint bp_not_sub("bp_prefix_second");
+ crow::Blueprint sub_bp("bp2", "csstat", "cstemplate");
+ crow::Blueprint sub_sub_bp("bp3");
+
+ CROW_BP_ROUTE(sub_bp, "/hello")
+ ([]() {
+ return "Hello world!";
+ });
+
+ CROW_BP_ROUTE(bp_not_sub, "/hello")
+ ([]() {
+ return "Hello world!";
+ });
+
+ CROW_BP_ROUTE(sub_sub_bp, "/hi")
+ ([]() {
+ return "Hi world!";
+ });
+
+ CROW_BP_CATCHALL_ROUTE(sub_bp)([](){return response(200, "WRONG!!");});
+
+ app. register_blueprint(bp);
+ app. register_blueprint(bp_not_sub);
+ bp. register_blueprint(sub_bp);
+ sub_bp.register_blueprint(sub_sub_bp);
+
+ app.validate();
+
+ {
+ request req;
+ response res;
+
+ req.url = "/bp_prefix/bp2/hello";
+
+ app.handle(req, res);
+
+ CHECK("Hello world!" == res.body);
+ }
+
+ {
+ request req;
+ response res;
+
+ req.url = "/bp_prefix_second/hello";
+
+ app.handle(req, res);
+
+ CHECK("Hello world!" == res.body);
+ }
+
+ {
+ request req;
+ response res;
+
+ req.url = "/bp_prefix/bp2/bp3/hi";
+
+ app.handle(req, res);
+
+ CHECK("Hi world!" == res.body);
+ }
+
+ {
+ request req;
+ response res;
+
+ req.url = "/bp_prefix/nonexistent";
+
+ app.handle(req, res);
+
+ CHECK(404 == res.code);
+ }
+
+ {
+ request req;
+ response res;
+
+ req.url = "/bp_prefix_second/nonexistent";
+
+ app.handle(req, res);
+
+ CHECK(404 == res.code);
+ }
+
+ {
+ request req;
+ response res;
+
+ req.url = "/bp_prefix/bp2/nonexistent";
+
+ app.handle(req, res);
+
+ CHECK(200 == res.code);
+ CHECK("WRONG!!" == res.body);
+ }
+
+ {
+ request req;
+ response res;
+
+ req.url = "/bp_prefix/bp2/bp3/nonexistent";
+
+ app.handle(req, res);
+
+ CHECK(200 == res.code);
+ CHECK("WRONG!!" == res.body);
+ }
+}
diff --git a/vcpkg.json b/vcpkg.json
index e9bfcd371..3b79e4a4a 100644
--- a/vcpkg.json
+++ b/vcpkg.json
@@ -1,37 +1,37 @@
{
"name": "crow-examples",
- "version": "0.3",
+ "version": "master",
"dependencies": [
{
"name": "boost-array",
- "version>=": "1.70.0"
+ "version>=": "1.70.0"
},
{
"name": "boost-algorithm",
- "version>=": "1.70.0"
+ "version>=": "1.70.0"
},
{
"name": "boost-asio",
- "version>=": "1.70.0"
+ "version>=": "1.70.0"
},
{
"name": "boost-date-time",
- "version>=": "1.70.0"
+ "version>=": "1.70.0"
},
{
"name": "boost-functional",
- "version>=": "1.70.0"
+ "version>=": "1.70.0"
},
{
"name": "boost-lexical-cast",
- "version>=": "1.70.0"
+ "version>=": "1.70.0"
},
{
"name": "boost-optional",
- "version>=": "1.70.0"
+ "version>=": "1.70.0"
},
{
- "name": "openssl-windows"
+ "name": "openssl-windows"
},
{
"name": "zlib"
@@ -40,31 +40,31 @@
"overrides": [
{
"name": "boost-array",
- "version": "1.70.0"
+ "version": "1.70.0"
},
{
"name": "boost-algorithm",
- "version": "1.70.0"
+ "version": "1.70.0"
},
{
"name": "boost-asio",
- "version-semver": "1.70.0-2"
+ "version-semver": "1.70.0-2"
},
{
"name": "boost-date-time",
- "version": "1.70.0"
+ "version": "1.70.0"
},
{
"name": "boost-functional",
- "version": "1.70.0"
+ "version": "1.70.0"
},
{
"name": "boost-lexical-cast",
- "version": "1.70.0"
+ "version": "1.70.0"
},
{
"name": "boost-optional",
- "version": "1.70.0"
+ "version": "1.70.0"
}
],
"builtin-baseline": "44d94c2edbd44f0c01d66c2ad95eb6982a9a61bc"