mirror of
https://github.com/CrowCpp/Crow.git
synced 2024-06-07 21:10:44 +00:00
Merge branch 'master' into app-constructor
This commit is contained in:
commit
94fa9f77f6
5
Doxyfile
5
Doxyfile
@ -2129,7 +2129,10 @@ INCLUDE_FILE_PATTERNS =
|
||||
# recursively expanded use the := operator instead of the = operator.
|
||||
# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
|
||||
|
||||
PREDEFINED =
|
||||
PREDEFINED = CROW_ENABLE_SSL \
|
||||
CROW_ENABLE_COMPRESSION \
|
||||
CROW_ENABLE_DEBUG \
|
||||
CROW_ENABLE_LOGGING
|
||||
|
||||
# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then this
|
||||
# tag can be used to specify a list of macro names that should be expanded. The
|
||||
|
@ -12,7 +12,7 @@
|
||||
|
||||
## 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.
|
||||
Crow is a C++ framework for creating HTTP or Websocket 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).
|
||||
@ -27,6 +27,7 @@ Crow is a C++ microframework for running web services. It uses routing similar t
|
||||
- Uses modern C++ (11/14)
|
||||
|
||||
### Still in development
|
||||
- [Async support](https://github.com/CrowCpp/Crow/issues/258)
|
||||
- [HTTP/2 support](https://github.com/crowcpp/crow/issues/8)
|
||||
|
||||
## Documentation
|
||||
|
90
docs/assets/crowlogo_main_light_color.svg
Normal file
90
docs/assets/crowlogo_main_light_color.svg
Normal file
File diff suppressed because one or more lines are too long
After Width: | Height: | Size: 15 KiB |
105
docs/assets/fast_light_icon.svg
Normal file
105
docs/assets/fast_light_icon.svg
Normal file
@ -0,0 +1,105 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
|
||||
<svg
|
||||
width="150mm"
|
||||
height="150mm"
|
||||
viewBox="0 0 150 150"
|
||||
version="1.1"
|
||||
id="svg2098"
|
||||
inkscape:version="1.1.2 (0a00cf5339, 2022-02-04, custom)"
|
||||
sodipodi:docname="fast_light_icon.svg"
|
||||
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">
|
||||
<sodipodi:namedview
|
||||
id="namedview2100"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1.0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pagecheckerboard="0"
|
||||
inkscape:document-units="mm"
|
||||
showgrid="false"
|
||||
inkscape:zoom="0.73673542"
|
||||
inkscape:cx="-200.88623"
|
||||
inkscape:cy="257.89448"
|
||||
inkscape:window-width="1920"
|
||||
inkscape:window-height="1051"
|
||||
inkscape:window-x="0"
|
||||
inkscape:window-y="0"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:current-layer="layer1"
|
||||
width="150mm" />
|
||||
<defs
|
||||
id="defs2095" />
|
||||
<g
|
||||
inkscape:label="Layer 1"
|
||||
inkscape:groupmode="layer"
|
||||
id="layer1"
|
||||
transform="translate(-31.274158,-78.140537)">
|
||||
<circle
|
||||
style="fill:none;fill-opacity:1;stroke:#e5f2f8;stroke-width:6.46499;stroke-linecap:round;stroke-opacity:1"
|
||||
id="path7742"
|
||||
cx="106.62331"
|
||||
cy="153.48967"
|
||||
r="70" />
|
||||
<circle
|
||||
style="fill:#515b60;fill-opacity:1;stroke:none;stroke-width:6.46499;stroke-linecap:round;stroke-opacity:1"
|
||||
id="path7840"
|
||||
cx="106.80286"
|
||||
cy="151.15532"
|
||||
r="10" />
|
||||
<path
|
||||
id="rect7980"
|
||||
style="fill:#515b60;fill-opacity:1;stroke-width:6.46499;stroke-linecap:round"
|
||||
d="m 156.9346,153.30987 -50.40574,1.47566 -0.0786,-6.1047 z"
|
||||
sodipodi:nodetypes="cccc"
|
||||
inkscape:transform-center-x="-24.979325"
|
||||
inkscape:transform-center-y="-0.23024" />
|
||||
<circle
|
||||
style="fill:#e5f2f8;fill-opacity:1;stroke:none;stroke-width:4.99999;stroke-linecap:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
id="path9189"
|
||||
cx="56.373787"
|
||||
cy="185.78271"
|
||||
r="3" />
|
||||
<circle
|
||||
style="fill:#e5f2f8;fill-opacity:1;stroke:none;stroke-width:4.99999;stroke-linecap:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
id="circle9271"
|
||||
cx="46.658237"
|
||||
cy="154.20793"
|
||||
r="3" />
|
||||
<circle
|
||||
style="fill:#e5f2f8;fill-opacity:1;stroke:none;stroke-width:4.99999;stroke-linecap:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
id="circle9273"
|
||||
cx="53.132095"
|
||||
cy="125.30756"
|
||||
r="3" />
|
||||
<circle
|
||||
style="fill:#e5f2f8;fill-opacity:1;stroke:none;stroke-width:4.99999;stroke-linecap:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
id="circle9275"
|
||||
cx="73.13047"
|
||||
cy="102.3995"
|
||||
r="3" />
|
||||
<circle
|
||||
style="fill:#e5f2f8;fill-opacity:1;stroke:none;stroke-width:4.99999;stroke-linecap:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
id="circle9277"
|
||||
cx="106.98246"
|
||||
cy="92.060951"
|
||||
r="3" />
|
||||
<circle
|
||||
style="fill:#e5f2f8;fill-opacity:1;stroke:none;stroke-width:4.99999;stroke-linecap:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
id="circle9279"
|
||||
cx="-73.287689"
|
||||
cy="-160.29289"
|
||||
r="3"
|
||||
transform="rotate(150.48856)" />
|
||||
<path
|
||||
id="circle12049"
|
||||
style="fill:none;stroke:#ff4141;stroke-width:5.66447;stroke-linecap:round;stroke-opacity:1"
|
||||
d="m 163.0663,127.86979 c 3.88122,8.04831 5.15863,16.53521 5.15863,26.06886 0,12.05511 -3.478,23.2979 -9.48531,32.77966"
|
||||
sodipodi:nodetypes="csc" />
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 3.8 KiB |
67
docs/assets/header_light_icon.svg
Normal file
67
docs/assets/header_light_icon.svg
Normal file
@ -0,0 +1,67 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
|
||||
<svg
|
||||
width="130mm"
|
||||
height="130mm"
|
||||
viewBox="0 0 130 130"
|
||||
version="1.1"
|
||||
id="svg5"
|
||||
inkscape:version="1.1.2 (0a00cf5339, 2022-02-04, custom)"
|
||||
sodipodi:docname="header_light_icon.svg"
|
||||
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">
|
||||
<sodipodi:namedview
|
||||
id="namedview7"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1.0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pagecheckerboard="0"
|
||||
inkscape:document-units="mm"
|
||||
showgrid="false"
|
||||
inkscape:zoom="0.73673541"
|
||||
inkscape:cx="-122.16055"
|
||||
inkscape:cy="249.75045"
|
||||
inkscape:window-width="1920"
|
||||
inkscape:window-height="1051"
|
||||
inkscape:window-x="0"
|
||||
inkscape:window-y="0"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:current-layer="layer1"
|
||||
height="130mm" />
|
||||
<defs
|
||||
id="defs2">
|
||||
<rect
|
||||
x="286.39862"
|
||||
y="431.63394"
|
||||
width="213.10229"
|
||||
height="276.89725"
|
||||
id="rect12469" />
|
||||
<rect
|
||||
x="308.11603"
|
||||
y="465.56741"
|
||||
width="176.45412"
|
||||
height="267.39587"
|
||||
id="rect3773" />
|
||||
</defs>
|
||||
<g
|
||||
inkscape:label="Layer 1"
|
||||
inkscape:groupmode="layer"
|
||||
id="layer1"
|
||||
transform="translate(-55.42222,-82.637121)">
|
||||
<path
|
||||
id="path1009"
|
||||
style="fill:#e5f2f8;fill-opacity:1;stroke-width:8.47412"
|
||||
d="M 62 0 L 62 244.93359 L 62 489.86719 L 249.05273 489.86719 L 436.10547 489.86719 L 436.10547 302.15625 L 436.10547 114.44531 L 378.94141 57.222656 L 321.7793 0 L 191.88867 0 L 62 0 z M 228.81055 227.10352 L 246.87695 227.10352 L 246.87695 286.67383 C 251.17383 280.09831 256.2194 275.18294 262.01367 271.92773 C 267.87305 268.67253 274.61133 267.04492 282.22852 267.04492 C 294.79362 267.04492 304.29883 270.95117 310.74414 278.76367 C 317.18945 286.51107 320.41211 297.93685 320.41211 313.04102 L 320.41211 379.05664 L 302.44336 379.05664 L 302.44336 313.62695 C 302.44336 303.27539 300.42513 295.52799 296.38867 290.38477 C 292.35221 285.24154 286.29753 282.66992 278.22461 282.66992 C 268.52409 282.66992 260.87435 285.76237 255.27539 291.94727 C 249.67643 298.13216 246.87695 306.56315 246.87695 317.24023 L 246.87695 379.05664 L 228.81055 379.05664 L 228.81055 227.10352 z M 168.45898 354.25195 L 189.06445 354.25195 L 189.06445 379.05664 L 168.45898 379.05664 L 168.45898 354.25195 z "
|
||||
transform="matrix(0.26458333,0,0,0.26458333,55.42222,82.637121)" />
|
||||
<text
|
||||
xml:space="preserve"
|
||||
transform="scale(0.26458333)"
|
||||
id="text3771"
|
||||
style="font-style:normal;font-weight:normal;font-size:200px;line-height:1.25;font-family:sans-serif;white-space:pre;shape-inside:url(#rect3773);fill:#ffffff;fill-opacity:1;stroke:none" />
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 2.9 KiB |
67
docs/assets/typesafe_light_icon.svg
Normal file
67
docs/assets/typesafe_light_icon.svg
Normal file
@ -0,0 +1,67 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
|
||||
<svg
|
||||
width="128mm"
|
||||
height="128mm"
|
||||
viewBox="0 0 128 128"
|
||||
version="1.1"
|
||||
id="svg5"
|
||||
inkscape:version="1.1.2 (0a00cf5339, 2022-02-04, custom)"
|
||||
sodipodi:docname="typesafe_light_icon.svg"
|
||||
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">
|
||||
<sodipodi:namedview
|
||||
id="namedview7"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1.0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pagecheckerboard="0"
|
||||
inkscape:document-units="mm"
|
||||
showgrid="false"
|
||||
inkscape:zoom="1.0419012"
|
||||
inkscape:cx="114.21428"
|
||||
inkscape:cy="204.43397"
|
||||
inkscape:window-width="1920"
|
||||
inkscape:window-height="1051"
|
||||
inkscape:window-x="0"
|
||||
inkscape:window-y="0"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:current-layer="layer1"
|
||||
height="128mm" />
|
||||
<defs
|
||||
id="defs2">
|
||||
<inkscape:path-effect
|
||||
effect="mirror_symmetry"
|
||||
start_point="83.49758,103.2497"
|
||||
end_point="83.677144,216.55502"
|
||||
center_point="83.587362,159.90236"
|
||||
id="path-effect1148"
|
||||
is_visible="true"
|
||||
lpeversion="1.1"
|
||||
mode="free"
|
||||
discard_orig_path="false"
|
||||
fuse_paths="true"
|
||||
oposite_fuse="false"
|
||||
split_items="false"
|
||||
split_open="false" />
|
||||
</defs>
|
||||
<g
|
||||
inkscape:label="Layer 1"
|
||||
inkscape:groupmode="layer"
|
||||
id="layer1"
|
||||
transform="translate(-35.610134,-93.0589)">
|
||||
<path
|
||||
style="fill:none;fill-opacity:1;stroke:#e5f2f8;stroke-width:6.465;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
d="M 83.489092,97.893689 C 61.657927,122.25262 42.116054,116.87646 39.272073,119.55834 c -2.61289,2.46396 6.066946,69.88504 44.404869,96.86893 38.252208,-27.10527 46.718298,-94.55352 44.097618,-97.00919 -2.85247,-2.67285 -22.37721,2.76522 -44.285468,-21.524391 z"
|
||||
id="path857"
|
||||
sodipodi:nodetypes="csc"
|
||||
inkscape:original-d="m 83.677144,97.68319 c -21.906891,24.60036 -41.552926,19.18557 -44.405071,21.87515 -2.615268,2.4662 6.082752,70.00779 44.5096,96.94249"
|
||||
inkscape:path-effect="#path-effect1148"
|
||||
transform="translate(16.150228,0.25394282)" />
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 2.4 KiB |
48
docs/assets/websocket_light_icon.svg
Normal file
48
docs/assets/websocket_light_icon.svg
Normal file
@ -0,0 +1,48 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
|
||||
<svg
|
||||
width="202mm"
|
||||
height="202mm"
|
||||
viewBox="0 0 202 202"
|
||||
version="1.1"
|
||||
id="svg13908"
|
||||
inkscape:version="1.1.2 (0a00cf5339, 2022-02-04, custom)"
|
||||
sodipodi:docname="websocket_light_icon.svg"
|
||||
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">
|
||||
<sodipodi:namedview
|
||||
id="namedview13910"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1.0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pagecheckerboard="0"
|
||||
inkscape:document-units="mm"
|
||||
showgrid="false"
|
||||
inkscape:zoom="0.73673541"
|
||||
inkscape:cx="160.16605"
|
||||
inkscape:cy="343.40687"
|
||||
inkscape:window-width="1920"
|
||||
inkscape:window-height="1051"
|
||||
inkscape:window-x="0"
|
||||
inkscape:window-y="0"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:current-layer="layer1"
|
||||
width="201.77914mm" />
|
||||
<defs
|
||||
id="defs13905" />
|
||||
<g
|
||||
inkscape:label="Layer 1"
|
||||
inkscape:groupmode="layer"
|
||||
id="layer1"
|
||||
transform="translate(-4.4986958,-69.044219)">
|
||||
<path
|
||||
style="fill:#e5f2f8;stroke-width:0.0841212;fill-opacity:1"
|
||||
d="m 69.265523,230.77706 -14.111288,-14.11148 8.832818,-8.83251 8.832808,-8.83251 L 51.116516,177.2971 29.41317,155.59363 v -12.26062 -12.26062 h 12.534076 12.534076 v 7.08736 7.08737 l 9.169279,9.16899 9.169279,9.16899 13.396176,-13.39618 13.396175,-13.39617 -9.168982,-9.16928 -9.169,-9.16932 H 55.360589 29.44692 L 16.972808,105.92001 4.4986958,93.385901 H 48.103109 91.707532 l 3.746995,3.764422 c 2.060862,2.070436 11.842143,11.897187 21.736193,21.837197 l 17.9892,18.0727 -4.40824,4.40884 -4.40823,4.40882 8.60118,8.60149 8.60117,8.60148 v 17.74937 17.74937 l -17.49722,-17.49708 -17.49722,-17.49705 -4.45581,4.45833 -4.455823,4.45834 17.460153,17.51827 17.46015,17.51826 H 116.84371 99.107375 l -8.622061,-8.62237 -8.622051,-8.62235 -4.43771,4.43711 -4.437711,4.43712 10.452037,10.45228 10.452028,10.45229 h 43.721543 43.72154 l 10.28448,10.23153 c 5.65645,5.62734 11.2686,11.2109 12.47142,12.4079 l 2.18695,2.17636 H 144.82733 83.376811 Z M 156.184,182.57579 v -24.87875 l -10.45194,-10.4522 -10.45193,-10.45218 8.83245,-8.83275 c 4.85784,-4.85802 8.87197,-8.83278 8.92029,-8.83278 0.0483,0 6.41741,6.33451 14.15356,14.07664 l 14.06572,14.07661 v 30.08708 30.08708 H 168.71808 156.184 Z"
|
||||
id="path13999" />
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 2.5 KiB |
@ -6,10 +6,11 @@ Here's how you can install Crow on your favorite GNU/Linux distro.
|
||||
- boost library & development headers (1.64 or later).
|
||||
- **(optional)** ZLib for HTTP Compression.
|
||||
- **(optional)** OpenSSL for HTTPS support.
|
||||
- **(optional)** CMake and Python3 to build tests and/or examples.
|
||||
!!!note
|
||||
- **(optional)** CMake for building tests, examples, and/or installing Crow.
|
||||
- **(optional)** Python3 to build tests and/or examples.
|
||||
!!! note
|
||||
|
||||
Crow's CI uses `g++-9.3` and `clang-7.0` running on AMD64 (x86_64) and ARM64v8 architectures.
|
||||
Crow's CI uses `g++-9.4` and `clang-10.0` running on AMD64 (x86_64) and ARM64v8 architectures.
|
||||
|
||||
|
||||
<br><br>
|
||||
@ -42,6 +43,10 @@ You can also download the `crow_all.h` file and simply include that into your pr
|
||||
|
||||
You can ignore `-DCROW_BUILD_EXAMPLES=OFF -DCROW_BUILD_TESTS=OFF` if you want to build the Examples and Unit Tests.
|
||||
|
||||
!!! note
|
||||
|
||||
While building you can set the `CROW_FEATURES` variable (as a `;` separated list). You can use an argument such as `-DCROW_FEATURES="ssl;compression"`.
|
||||
|
||||
!!! note
|
||||
|
||||
You can uninstall Crow at a later time using `make uninstall`.
|
||||
@ -76,6 +81,10 @@ find_package(Crow)
|
||||
target_link_libraries(your_project PUBLIC Crow::Crow)
|
||||
```
|
||||
From there CMake should handle compiling and linking your project.
|
||||
!!! note
|
||||
|
||||
For optional features like HTTP Compression or HTTPS you can set the `CROW_FEATURES` variable using lines such as `set(CROW_FEATURES "ssl;compression")`, `set(CROW_FEATURES ssl compression)`, or `set(CROW_FEATURES ssl)`.
|
||||
|
||||
### Directly using a compiler
|
||||
All you need to do is run the following command:
|
||||
```
|
||||
|
@ -58,7 +58,7 @@ This will generate a `crow_all.h` file which you can use in the following steps
|
||||
4. `make -j12`
|
||||
!!! note
|
||||
|
||||
You can add options like `-DCROW_ENABLE_SSL`, `-DCROW_ENABLE_COMPRESSION`, or `-DCROW_AMALGAMATE` to `cmake ..` to build their tests/examples.
|
||||
You can add options like `-DCROW_FEATURES="ssl;compression"` or `-DCROW_AMALGAMATE` to `cmake ..` to build optional tests/examples for HTTP Compression or HTTPS.
|
||||
|
||||
## Compiling using a compiler directly
|
||||
All you need to do is run the following command:
|
||||
|
@ -1,7 +0,0 @@
|
||||
Crow Allows a developer to set CORS policies by using the `CORSHandler` middleware.<br><br>
|
||||
|
||||
This middleware can be added to the app simply by defining a `#!cpp crow::App<crow::CORSHandler>`. This will use the default CORS rules globally<br><br>
|
||||
|
||||
The CORS rules can be modified by first getting the middleware via `#!cpp auto& cors = app.get_middleware<crow::CORSHandler>();`. The rules can be set per URL prefix using `prefix()`, per blueprint using `blueprint()`, or globally via `global()`. These will return a `CORSRules` object which contains the actual rules for the prefix, blueprint, or application. For more details go [here](../../reference/structcrow_1_1_c_o_r_s_handler.html).
|
||||
|
||||
`CORSRules` can be modified using the methods `origin()`, `methods()`, `headers()`, `max_age()`, `allow_credentials()`, or `ignore()`. For more details on these methods and what default values they take go [here](../../reference/structcrow_1_1_c_o_r_s_rules.html).
|
@ -3,6 +3,7 @@
|
||||
Using `#!cpp crow::utility::base64encode(mystring, mystring.size())` will return a Base64 encoded string. For URL safe Base64 `#!cpp crow::utility::base64encode_urlsafe(mystring, mystring.size())` can be used. The key used in the encoding process can be changed, it is a string containing all 64 characters to be used.
|
||||
|
||||
## Decoding
|
||||
**Introduced in: `v1.0`**<br><br>
|
||||
<span class="tag">[:octicons-feed-tag-16: v1.0](https://github.com/CrowCpp/Crow/releases/v1.0)</span>
|
||||
|
||||
|
||||
Using `#!cpp crow::utility::base64decode(mystring, mystring.size())` with `mystring` being a Base64 encoded string will return a plain-text string. The function works with both normal and URL safe Base64. However it cannot decode a Base64 string encoded with a custom key.
|
||||
|
@ -1,5 +1,6 @@
|
||||
**Introduced in: `v1.0`**<br><br>
|
||||
|
||||
<span class="tag">[:octicons-feed-tag-16: v1.0](https://github.com/CrowCpp/Crow/releases/v1.0)</span>
|
||||
|
||||
|
||||
Crow supports flask style blueprints.<br>
|
||||
A blueprint is a limited app. It cannot handle networking. But it can handle routes.<br>
|
||||
Blueprints allow developers to compartmentalize their Crow applications, making them a lot more modular.<br><br>
|
||||
|
@ -1,9 +1,11 @@
|
||||
**Introduced in: `v0.3`**<br><br>
|
||||
<span class="tag">[:octicons-feed-tag-16: v0.3](https://github.com/CrowCpp/Crow/releases/v0.3)</span>
|
||||
|
||||
|
||||
Crow supports Zlib compression using Gzip or Deflate algorithms.
|
||||
|
||||
## HTTP Compression
|
||||
HTTP compression is by default disabled in crow. Do the following to enable it: <br>
|
||||
- Define `CROW_ENABLE_COMPRESSION` in your compiler definitions (`g++ main.cpp -DCROW_ENABLE_COMPRESSION` for example) or `CMakeLists.txt`.
|
||||
- Define `CROW_ENABLE_COMPRESSION` in your compiler definitions (`g++ main.cpp -DCROW_ENABLE_COMPRESSION` for example) or `set(CROW_FEATURES compression)` in `CMakeLists.txt`.
|
||||
- Call `#!cpp use_compression(crow::compression::algorithm)` on your Crow app.
|
||||
- When compiling your application, make sure that ZLIB is included as a dependency. Either through `-lz` compiler argument or `find_package(ZLIB)` in CMake.
|
||||
|
||||
|
43
docs/guides/included-middleware.md
Normal file
43
docs/guides/included-middleware.md
Normal file
@ -0,0 +1,43 @@
|
||||
Crow contains some middlewares that are ready to be used in your application.
|
||||
<br>
|
||||
Make sure you understand how to enable and use [middleware](../middleware/).
|
||||
|
||||
## Cookies
|
||||
Include: `crow/middlewares/cookie_parser.h` <br>
|
||||
Examples: `examples/middlewars/example_cookies.cpp`
|
||||
|
||||
This middleware allows to read and write cookies by using `CookieParser`. Once enabled, it parses all incoming cookies.
|
||||
|
||||
Cookies can be read and written with the middleware context. All cookie attributes can be changed as well.
|
||||
|
||||
```cpp
|
||||
auto& ctx = app.get_context<crow::CookieParser>(request);
|
||||
std::string value = ctx.get_cookie("key");
|
||||
ctx.set_cookie("key", "value")
|
||||
.path("/")
|
||||
.max_age(120);
|
||||
```
|
||||
|
||||
!!! note
|
||||
|
||||
Make sure `CookieParser` is listed before any other middleware that relies on it.
|
||||
|
||||
## CORS
|
||||
Include: `crow/middlewares/cors.h` <br>
|
||||
Examples: `examples/middlewars/example_cors.cpp`
|
||||
|
||||
This middleware allows to set CORS policies by using `CORSHandler`. Once enabled, it will apply the default CORS rules globally.
|
||||
|
||||
The CORS rules can be modified by first getting the middleware via `#!cpp auto& cors = app.get_middleware<crow::CORSHandler>();`. The rules can be set per URL prefix using `prefix()`, per blueprint using `blueprint()`, or globally via `global()`. These will return a `CORSRules` object which contains the actual rules for the prefix, blueprint, or application. For more details go [here](../../reference/structcrow_1_1_c_o_r_s_handler.html).
|
||||
|
||||
`CORSRules` can be modified using the methods `origin()`, `methods()`, `headers()`, `max_age()`, `allow_credentials()`, or `ignore()`. For more details on these methods and what default values they take go [here](../../reference/structcrow_1_1_c_o_r_s_rules.html).
|
||||
|
||||
```cpp
|
||||
auto& cors = app.get_middleware<crow::CORSHandler>();
|
||||
cors
|
||||
.global()
|
||||
.headers("X-Custom-Header", "Upgrade-Insecure-Requests")
|
||||
.methods("POST"_method, "GET"_method)
|
||||
.prefix("/cors")
|
||||
.origin("example.com");
|
||||
```
|
@ -31,7 +31,8 @@ Writing a log is as simple as `#!cpp CROW_LOG_<LOG LEVEL> << "Hello";` (replace&
|
||||
Log times are reported in GMT timezone by default. This is because HTTP requires all reported times for requests and responses to be in GMT. This can be changed by using the macro `CROW_USE_LOCALTIMEZONE` which will set **only the log timezone** to the server's local timezone.
|
||||
|
||||
## Creating A custom logger
|
||||
**Introduced in: `v1.0`**<br><br>
|
||||
<span class="tag">[:octicons-feed-tag-16: v1.0](https://github.com/CrowCpp/Crow/releases/v1.0)</span>
|
||||
|
||||
|
||||
Assuming you have an existing logger or Crow's default format just doesn't work for you. Crow allows you to use a custom logger for any log made using the `CROW_LOG_<LOG LEVEL>` macro.<br>
|
||||
All you need is a class extending `#!cpp crow::ILogHandler` containing the method `#!cpp void log(std::string, crow::LogLevel)`.<br>
|
||||
|
@ -1,13 +1,51 @@
|
||||
Middleware is used for altering and inspecting requests before and after the handler call.
|
||||
Middleware is used for altering and inspecting requests before and after calling the handler. In Crow it's very similar to middleware in other web frameworks.
|
||||
|
||||
All middleware is registered in the Crow application
|
||||
|
||||
```cpp
|
||||
crow::App<FirstMW, SecondMW, ThirdMW> app;
|
||||
```
|
||||
|
||||
and is called in this specified order.
|
||||
|
||||
Any middleware requires the following 3 members:
|
||||
|
||||
* A context struct for storing the middleware data.
|
||||
* A `before_handle` method, which is called before the handler. If `res.end()` is called, the operation is halted.
|
||||
* A context struct for storing request local data.
|
||||
* A `before_handle` method, which is called before the handler.
|
||||
* A `after_handle` method, which is called after the handler.
|
||||
|
||||
## before_handle
|
||||
There are two possible signatures for before_handle
|
||||
!!! warning
|
||||
|
||||
As soon as `response.end()` is called, no other handlers and middleware is run, except for after_handlers of already visited middleware.
|
||||
|
||||
|
||||
## Example
|
||||
|
||||
A middleware that can be used to guard admin handlers
|
||||
|
||||
```cpp
|
||||
struct AdminAreaGuard
|
||||
{
|
||||
struct context
|
||||
{};
|
||||
|
||||
void before_handle(crow::request& req, crow::response& res, context& ctx)
|
||||
{
|
||||
if (req.remote_ip_address != ADMIN_IP)
|
||||
{
|
||||
res.code = 403;
|
||||
res.end();
|
||||
}
|
||||
}
|
||||
|
||||
void after_handle(crow::request& req, crow::response& res, context& ctx)
|
||||
{}
|
||||
};
|
||||
```
|
||||
|
||||
|
||||
### before_handle and after_handle
|
||||
There are two possible signatures for before_handle and after_handle
|
||||
|
||||
1. if you only need to access this middleware's context.
|
||||
|
||||
@ -25,43 +63,16 @@ There are two possible signatures for before_handle
|
||||
}
|
||||
```
|
||||
|
||||
## Local middleware
|
||||
|
||||
## after_handle
|
||||
There are two possible signatures for after_handle
|
||||
|
||||
1. if you only need to access this middleware's context.
|
||||
|
||||
```cpp
|
||||
void after_handle(request& req, response& res, context& ctx)
|
||||
```
|
||||
|
||||
2. To get access to other middlewares context
|
||||
|
||||
``` cpp
|
||||
template <typename AllContext>
|
||||
void after_handle(request& req, response& res, context& ctx, AllContext& all_ctx)
|
||||
{
|
||||
auto other_ctx = all_ctx.template get<OtherMiddleware>();
|
||||
}
|
||||
```
|
||||
|
||||
## Using middleware
|
||||
|
||||
All middleware has to be registered in the Crow application and is enabled globally by default.
|
||||
|
||||
```cpp
|
||||
crow::App<FirstMiddleware, SecondMiddleware> app;
|
||||
```
|
||||
|
||||
if you want to enable some middleware only for specific handlers, you have to extend it from `crow::ILocalMiddleware`.
|
||||
By default, every middleware is called for each request. If you want to enable middleware for specific handlers or blueprints, you have to extend it from `crow::ILocalMiddleware`
|
||||
|
||||
```cpp
|
||||
struct LocalMiddleware : crow::ILocalMiddleware
|
||||
{
|
||||
...
|
||||
```
|
||||
|
||||
After this, you can enable it for specific handlers.
|
||||
After this, you can enable it for specific handlers
|
||||
|
||||
```cpp
|
||||
CROW_ROUTE(app, "/with_middleware")
|
||||
@ -71,26 +82,14 @@ CROW_ROUTE(app, "/with_middleware")
|
||||
});
|
||||
```
|
||||
|
||||
## Examples
|
||||
|
||||
A local middleware that can be used to guard admin handlers
|
||||
or blueprints
|
||||
|
||||
```cpp
|
||||
struct AdminAreaGuard : crow::ILocalMiddleware
|
||||
{
|
||||
struct context
|
||||
{};
|
||||
|
||||
void before_handle(crow::request& req, crow::response& res, context& ctx)
|
||||
{
|
||||
if (req.remote_ip_address != ADMIN_IP)
|
||||
{
|
||||
res.code = 403;
|
||||
res.end();
|
||||
}
|
||||
}
|
||||
|
||||
void after_handle(crow::request& req, crow::response& res, context& ctx)
|
||||
{}
|
||||
};
|
||||
Blueprint bp("with_middleware");
|
||||
bp.CROW_MIDDLEWARES(app, FistLocalMiddleware, SecondLocalMiddleware);
|
||||
```
|
||||
|
||||
!!! warning
|
||||
|
||||
Local and global middleware are called separately. First all global middleware is run, then all enabled local middleware for the current handler is run. In both cases middleware is called strongly
|
||||
in the order listed in the Crow application.
|
||||
|
@ -1,4 +1,5 @@
|
||||
**Introduced in: `v0.2`**<br><br>
|
||||
<span class="tag">[:octicons-feed-tag-16: v0.2](https://github.com/CrowCpp/Crow/releases/0.2)</span>
|
||||
|
||||
|
||||
Multipart is a way of forming HTTP requests or responses to contain multiple distinct parts.<br>
|
||||
|
||||
@ -21,7 +22,8 @@ A message can be created either by defining the headers, boundary, and individua
|
||||
|
||||
Once a multipart message has been made, the individual parts can be accessed throughout `msg.parts`, `parts` is an `std::vector`.<br><br>
|
||||
|
||||
**Introduced in: `v1.0`**<br><br>
|
||||
<span class="tag">[:octicons-feed-tag-16: v1.0](https://github.com/CrowCpp/Crow/releases/v1.0)</span>
|
||||
|
||||
|
||||
Part headers are organized in a similar way to request and response headers, and can be retrieved via `crow::multipart::get_header_object("header-key")`. This function returns a `crow::multipart::header` object.<br><br>
|
||||
|
||||
|
@ -2,10 +2,13 @@ A query string is the part of the URL that comes after a `?` character, it is us
|
||||
<br><br>
|
||||
|
||||
Crow supports query strings through `crow::request::url_params`. The object is of type `crow::query_string` and can has the following functions:<br>
|
||||
|
||||
## 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`**<br><br>
|
||||
<span class="tag">[:octicons-feed-tag-16: v0.3](https://github.com/CrowCpp/Crow/releases/v0.3)</span>
|
||||
|
||||
|
||||
Works the same as `get`, but removes the returned value.
|
||||
!!! note
|
||||
|
||||
@ -16,15 +19,19 @@ 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`**<br><br>
|
||||
<span class="tag">[:octicons-feed-tag-16: v0.3](https://github.com/CrowCpp/Crow/releases/v0.3)</span>
|
||||
|
||||
|
||||
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<std::string, std::string>` from a query string such as `?key[sub_key1]=value1&key[sub_key2]=value2&key[sub_key3]=value3`.<br>
|
||||
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`**<br><br>
|
||||
<span class="tag">[:octicons-feed-tag-16: v0.3](https://github.com/CrowCpp/Crow/releases/v0.3)</span>
|
||||
|
||||
|
||||
Works the same as `get_dict` but removing the values from the query string.
|
||||
!!!warning
|
||||
!!! warning
|
||||
|
||||
if your query string contains both a list and dictionary with the same key, it is best to use `pop_list` before either `get_dict` or `pop_dict`, since a map cannot contain more than one value per key, each item in the list will override the previous and only the last will remain with an empty key.
|
||||
|
||||
|
@ -11,7 +11,7 @@ Using `/hello` means the client will need to access `http://example.com/hello` i
|
||||
A path can have parameters, for example `/hello/<int>` will allow a client to input an int into the url which will be in the handler (something like `http://example.com/hello/42`).<br>
|
||||
Parameters can be `<int>`, `<uint>`, `<double>`, `<string>`, or `<path>`.<br>
|
||||
It's worth noting that the parameters also need to be defined in the handler, an example of using parameters would be to add 2 numbers based on input:
|
||||
```cpp
|
||||
```cpp
|
||||
CROW_ROUTE(app, "/add/<int>/<int>")
|
||||
([](int a, int b)
|
||||
{
|
||||
@ -27,6 +27,52 @@ You can change the HTTP methods the route uses from just the default `GET` by us
|
||||
|
||||
Crow handles `HEAD` and `OPTIONS` methods automatically. So adding those to your handler has no effect.
|
||||
|
||||
Crow defines the following methods:
|
||||
```
|
||||
DELETE
|
||||
GET
|
||||
HEAD
|
||||
POST
|
||||
PUT
|
||||
|
||||
CONNECT
|
||||
OPTIONS
|
||||
TRACE
|
||||
|
||||
PATCH
|
||||
PURGE
|
||||
|
||||
COPY
|
||||
LOCK
|
||||
MKCOL
|
||||
MOVE
|
||||
PROPFIND
|
||||
PROPPATCH
|
||||
SEARCH
|
||||
UNLOCK
|
||||
BIND
|
||||
REBIND
|
||||
UNBIND
|
||||
ACL
|
||||
|
||||
REPORT
|
||||
MKACTIVITY
|
||||
CHECKOUT
|
||||
MERGE
|
||||
|
||||
SEARCH
|
||||
NOTIFY
|
||||
SUBSCRIBE
|
||||
UNSUBSCRIBE
|
||||
|
||||
MKCALENDAR
|
||||
|
||||
LINK
|
||||
UNLINK
|
||||
|
||||
SOURCE
|
||||
```
|
||||
|
||||
## Handler
|
||||
Basically a piece of code that gets executed whenever the client calls the associated route, usually in the form of a [lambda expression](https://en.cppreference.com/w/cpp/language/lambda). It can be as simple as `#!cpp ([](){return "Hello World"})`.<br><br>
|
||||
|
||||
@ -35,6 +81,12 @@ Handlers can also use information from the request by adding it as a parameter `
|
||||
|
||||
You can also access the URL parameters in the handler using `#!cpp req.url_params.get("param_name");`. If the parameter doesn't exist, `nullptr` is returned.<br><br>
|
||||
|
||||
|
||||
!!! note "Note <span class="tag">[:octicons-feed-tag-16: master](https://github.com/CrowCpp/Crow)</span>"
|
||||
|
||||
parameters inside the body can be parsed using `#!cpp req.get_body_params();`. which is useful for requests of type `application/x-www-form-urlencoded`. Its format is similar to `url_params`.
|
||||
|
||||
|
||||
For more information on `crow::request` go [here](../../reference/structcrow_1_1request.html).<br><br>
|
||||
|
||||
### Response
|
||||
@ -45,6 +97,56 @@ Please note that in order to return a response defined as a parameter you'll nee
|
||||
Alternatively, you can define the response in the body and return it (`#!cpp ([](){return crow::response()})`).<br>
|
||||
|
||||
For more information on `crow::response` go [here](../../reference/structcrow_1_1response.html).<br><br>
|
||||
|
||||
Crow defines the following status codes:
|
||||
```
|
||||
100 Continue
|
||||
101 Switching Protocols
|
||||
|
||||
200 OK
|
||||
201 Created
|
||||
202 Accepted
|
||||
203 Non-Authoritative Information
|
||||
204 No Content
|
||||
205 Reset Content
|
||||
206 Partial Content
|
||||
|
||||
300 Multiple Choices
|
||||
301 Moved Permanently
|
||||
302 Found
|
||||
303 See Other
|
||||
304 Not Modified
|
||||
307 Temporary Redirect
|
||||
308 Permanent Redirect
|
||||
|
||||
400 Bad Request
|
||||
401 Unauthorized
|
||||
403 Forbidden
|
||||
404 Not Found
|
||||
405 Method Not Allowed
|
||||
407 Proxy Authentication Required
|
||||
409 Conflict
|
||||
410 Gone
|
||||
413 Payload Too Large
|
||||
415 Unsupported Media Type
|
||||
416 Range Not Satisfiable
|
||||
417 Expectation Failed
|
||||
428 Precondition Required
|
||||
429 Too Many Requests
|
||||
451 Unavailable For Legal Reasons
|
||||
|
||||
500 Internal Server Error
|
||||
501 Not Implemented
|
||||
502 Bad Gateway
|
||||
503 Service Unavailable
|
||||
504 Gateway Timeout
|
||||
506 Variant Also Negotiates
|
||||
```
|
||||
|
||||
!!! note
|
||||
|
||||
If your status code is not defined in the list above (e.g. `crow::response(123)`) Crow will return `500 Internal Server Error` instead.
|
||||
|
||||
|
||||
### Return statement
|
||||
A `crow::response` is very strictly tied to a route. If you can have something in a response constructor, you can return it in a handler.<br><br>
|
||||
@ -52,20 +154,22 @@ 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`**<br><br>
|
||||
<span class="tag">[:octicons-feed-tag-16: v0.3](https://github.com/CrowCpp/Crow/releases/v0.3)</span>
|
||||
|
||||
|
||||
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.<br>
|
||||
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.<br><br>
|
||||
|
||||
Your class should look like the following:
|
||||
```cpp
|
||||
class a : public crow::returnable
|
||||
class a : public crow::returnable
|
||||
{
|
||||
a() : returnable("text/plain"){};
|
||||
|
||||
|
||||
...
|
||||
...
|
||||
...
|
||||
|
||||
|
||||
std::string dump() override
|
||||
{
|
||||
return this.as_string();
|
||||
@ -75,12 +179,15 @@ class a : public crow::returnable
|
||||
<br><br>
|
||||
|
||||
## Response codes
|
||||
**Introduced in: `v1.0`**<br><br>
|
||||
<span class="tag">[:octicons-feed-tag-16: v1.0](https://github.com/CrowCpp/Crow/releases/v1.0)</span>
|
||||
|
||||
|
||||
Instead of assigning a response code, you can use the `crow::status` enum, for example you can replace `crow::response(200)` with `crow::response(crow::status::OK)`
|
||||
|
||||
## Catchall routes
|
||||
**Introduced in: `v0.3`**<br><br>
|
||||
<span class="tag">[:octicons-feed-tag-16: v0.3](https://github.com/CrowCpp/Crow/releases/v0.3)</span>
|
||||
|
||||
|
||||
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.
|
||||
!!! note
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
Crow supports HTTPS though SSL or TLS.<br><br>
|
||||
|
||||
!!!note
|
||||
!!! note
|
||||
|
||||
When mentioning SSL in this documentation, it is often a reference to openSSL, which includes TLS.<br><br>
|
||||
|
||||
@ -8,7 +8,7 @@ Crow supports HTTPS though SSL or TLS.<br><br>
|
||||
To enable SSL, first your application needs to define either a `.crt` and `.key` files, or a `.pem` file. Once you have your files, you can add them to your app like this:<br>
|
||||
`#!cpp app.ssl_file("/path/to/cert.crt", "/path/to/keyfile.key")` or `#!cpp app.ssl_file("/path/to/pem_file.pem")`. Please note that this method can be part of the app method chain, which means it can be followed by `.run()` or any other method.<br><br>
|
||||
|
||||
You also need to define `CROW_ENABLE_SSL` in your compiler definitions (`g++ main.cpp -DCROW_ENABLE_SSL` for example) or `CMakeLists.txt`.
|
||||
You also need to define `CROW_ENABLE_SSL` in your compiler definitions (`g++ main.cpp -DCROW_ENABLE_SSL` for example) or `set(CROW_FEATURES ssl)` in `CMakeLists.txt`.
|
||||
|
||||
You can also set your own SSL context (by using `boost::asio::ssl::context ctx`) and then applying it via the `#!cpp app.ssl(ctx)` method.<br><br>
|
||||
|
||||
|
@ -1,4 +1,6 @@
|
||||
**Introduced in: `v0.2`**<br><br>
|
||||
<span class="tag">[:octicons-feed-tag-16: v0.2](https://github.com/CrowCpp/Crow/releases/0.2)</span>
|
||||
|
||||
|
||||
A static file is any file that resides in the server's storage.
|
||||
|
||||
Crow supports returning Static files as responses in 2 ways.
|
||||
|
@ -1,19 +1,19 @@
|
||||
Websockets are a way of connecting a client and a server without the request response nature of HTTP.<br><br>
|
||||
|
||||
## Routes
|
||||
To create a websocket in Crow, you need a websocket route.<br>
|
||||
A websocket route differs from a normal route quite a bit. While it uses the same `CROW_ROUTE(app, "/url")` macro, that's about where the similarities end.<br>
|
||||
A websocket route follows the macro with `.websocket()` which is then followed by a series of methods (with handlers inside) for each event. These are (sorted by order of execution):
|
||||
|
||||
!!! Warning
|
||||
|
||||
By default, Crow allows Clients to send unmasked websocket messages, which is useful for debugging but goes against the protocol specification. Production Crow applications should enforce the protocol by adding `#!cpp #define CROW_ENFORCE_WS_SPEC` to their source code.
|
||||
A websocket route differs from a normal route quite a bit. It uses A slightly altered `CROW_WEBSOCKET_ROUTE(app, "/url")` macro, which is then followed by a series of methods (with handlers inside) for each event. These are (sorted by order of execution):
|
||||
|
||||
|
||||
- `#!cpp onaccept([&](const crow::request&){handler code goes here})` (This handler has to return `bool`)
|
||||
- `#!cpp onopen([&](crow::websocket::connection& conn){handler code goes here})`
|
||||
- `#!cpp onmessage([&](crow::websocket::connection& conn, const std::string message, bool is_binary){handler code goes here})`
|
||||
- `#!cpp onerror([&](crow::websocket::connection& conn){handler code goes here})`
|
||||
- `#!cpp onclose([&](crow::websocket::connection& conn, const std::string reason){handler code goes here})`<br><br>
|
||||
- `#!cpp onclose([&](crow::websocket::connection& conn, const std::string reason){handler code goes here})`
|
||||
|
||||
!!! Warning
|
||||
|
||||
By default, Crow allows Clients to send unmasked websocket messages, which is useful for debugging but goes against the protocol specification. Production Crow applications should enforce the protocol by adding `#!cpp #define CROW_ENFORCE_WS_SPEC` to their source code.
|
||||
|
||||
These event methods and their handlers can be chained. The full Route should look similar to this:
|
||||
```cpp
|
||||
@ -32,5 +32,17 @@ CROW_ROUTE(app, "/ws")
|
||||
do_something_else(data);
|
||||
});
|
||||
```
|
||||
<br><br>
|
||||
For more info go [here](../../reference/classcrow_1_1_web_socket_rule.html).
|
||||
|
||||
## Maximum payload size
|
||||
<span class="tag">[:octicons-feed-tag-16: master](https://github.com/CrowCpp/Crow)</span>
|
||||
|
||||
The maximum payload size that a connection accepts can be adjusted either globally by using `#!cpp app.websocket_max_payload(<value in bytes>)` or per route by using `#!cpp CROW_WEBSOCKET_ROUTE(app, "/url").max_payload(<value in bytes>)`. In case a message was sent that exceeded the limit. The connection would be shut down and `onerror` would be triggered.
|
||||
|
||||
!!! note
|
||||
|
||||
By default, This limit is disabled. To disable the global setting in specific routes, you only need to call `#!cpp CROW_WEBSOCKET_ROUTE(app, "/url").max_payload(UINT64_MAX)`.
|
||||
|
||||
|
||||
For more info about websocket routes go [here](../../reference/classcrow_1_1_web_socket_rule.html).
|
||||
|
||||
For more info about websocket connections go [here](../../reference/classcrow_1_1websocket_1_1_connection.html).
|
||||
|
@ -6,11 +6,11 @@
|
||||
<meta property="og:title" content="CrowCpp"/>
|
||||
<meta property="og:type" content="website" />
|
||||
<meta property="og:description" content="A Fast and Easy to use microframework for the web."/>
|
||||
<meta name="description" content="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.">
|
||||
<meta property="og:image" content="/assets/og_img.png" />
|
||||
<meta name="description" content="Crow is a C++ framework for creating HTTP or Websocket 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.">
|
||||
<meta property="og:image" content="assets/og_img.png" />
|
||||
<meta property="og:url" content="https://crowcpp.org">
|
||||
<meta property="twitter:card" content="summary_large_image">
|
||||
<meta property="twitter:image" content="/assets/og_img.png">
|
||||
<meta property="twitter:image" content="assets/og_img.png">
|
||||
{% endblock %}
|
||||
|
||||
<!-- Content -->
|
||||
@ -25,11 +25,11 @@
|
||||
.ccard{
|
||||
border-style: solid;
|
||||
border-width: .1rem;
|
||||
border-color: #00000080;
|
||||
border-color: var(--home-border-color);
|
||||
border-radius: 0.5rem;
|
||||
width: 10rem;
|
||||
height: 12rem;
|
||||
box-shadow: 2px 5px 5px #00000040;
|
||||
box-shadow: 2px 5px 5px var(--home-shadow-color);
|
||||
margin-inline: 1.5rem;
|
||||
margin-bottom: 1rem;
|
||||
display: inline-block;
|
||||
@ -51,7 +51,7 @@
|
||||
border-bottom: solid;
|
||||
border-width: 0.1rem;
|
||||
border-image-slice: 1;
|
||||
border-image-source: linear-gradient(90deg, rgba(0,0,0,0) 0%, rgb(0, 0, 0) 50%, rgba(0,0,0,0) 100%);
|
||||
border-image-source: var(--home-image-border);
|
||||
}
|
||||
|
||||
.ccard__text{
|
||||
@ -135,28 +135,42 @@ code{
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.md-footer-copyright {
|
||||
color: var(--md-footer-fg-color--light);
|
||||
font-size: .64rem;
|
||||
margin: auto .6rem;
|
||||
padding: .4rem 0;
|
||||
}
|
||||
|
||||
</style>
|
||||
<img class="clogo" alt="logo" src="/assets/crowlogo_main_color.svg">
|
||||
<img class="clogo" alt="logo" src="assets/crowlogo_main_color.svg#only-light">
|
||||
<img class="clogo" alt="logo" src="assets/crowlogo_main_light_color.svg#only-dark">
|
||||
|
||||
<h1 style="text-align:center;">A Fast and Easy to use microframework for the web.</h1>
|
||||
|
||||
<hr>
|
||||
<p style="text-align:center;">Crow is a C++ framework for creating HTTP or Websocket 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.</p>
|
||||
<hr>
|
||||
|
||||
<section class="csection">
|
||||
<div class="ccard">
|
||||
<img class="ccard__image" src= /assets/fast_icon.svg>
|
||||
<img class="ccard__image" src= assets/fast_icon.svg#only-light>
|
||||
<img class="ccard__image" src= assets/fast_light_icon.svg#only-dark>
|
||||
<p class="ccard__text">Blazingly Fast</p>
|
||||
</div>
|
||||
<div class="ccard">
|
||||
<img class="ccard__image" src= /assets/header_icon.svg>
|
||||
<img class="ccard__image" src= assets/header_icon.svg#only-light>
|
||||
<img class="ccard__image" src= assets/header_light_icon.svg#only-dark>
|
||||
<p class="ccard__text">Header Only</p>
|
||||
</div>
|
||||
<div class="ccard">
|
||||
<img class="ccard__image" src= /assets/typesafe_icon.svg>
|
||||
<img class="ccard__image" src= assets/typesafe_icon.svg#only-light>
|
||||
<img class="ccard__image" src= assets/typesafe_light_icon.svg#only-dark>
|
||||
<p class="ccard__text">Typesafe handlers</p>
|
||||
</div>
|
||||
<div class="ccard">
|
||||
<img class="ccard__image" src= /assets/websocket_icon.svg>
|
||||
<img class="ccard__image" src= assets/websocket_icon.svg#only-light>
|
||||
<img class="ccard__image" src= assets/websocket_light_icon.svg#only-dark>
|
||||
<p class="ccard__text">Websocket Support</p>
|
||||
</div>
|
||||
</section>
|
||||
@ -166,11 +180,10 @@ code{
|
||||
|
||||
<section class="ssection">
|
||||
<div class="sdescription">
|
||||
<h2 style="text-align: center;">Easy to get started</h3>
|
||||
<h2 style="text-align: center;">Easy to get started</h2>
|
||||
</div>
|
||||
<div class="scontent">
|
||||
<div class="highlight"><pre id="__code_0"><span></span><button class="md-clipboard md-icon" title="Copy to clipboard" data-clipboard-target="#__code_0 > code"></button><code>
|
||||
<span class="cp">#include</span> <span class="cpf">"crow.h"</span><span class="cp"></span>
|
||||
<div class="highlight"><pre id="__code_0"><span></span><button class="md-clipboard md-icon" title="Copy to clipboard" data-clipboard-target="#__code_0 > code"></button><code><span class="cp">#include</span> <span class="cpf">"crow.h"</span><span class="cp"></span>
|
||||
|
||||
<span class="kt">int</span> <span class="nf">main</span><span class="p">()</span>
|
||||
<span class="p">{</span>
|
||||
@ -197,14 +210,14 @@ code{
|
||||
</code></pre></div>
|
||||
</div>
|
||||
<div class="sdescription">
|
||||
<h2 style="text-align: center;">JSON Support Built-in</h3>
|
||||
<h2 style="text-align: center;">JSON Support Built-in</h2>
|
||||
</div>
|
||||
|
||||
</section>
|
||||
|
||||
<section class="ssection">
|
||||
<div class="sdescription">
|
||||
<h2 style="text-align: center;">URL parameter support as well!</h3>
|
||||
<h2 style="text-align: center;">URL parameter support as well!</h2>
|
||||
</div>
|
||||
<div class="scontent">
|
||||
<div class="highlight"><pre id="__code_2"><span></span><button class="md-clipboard md-icon" title="Copy to clipboard" data-clipboard-target="#__code_2 > code"></button><code><span class="cp">CROW_ROUTE</span><span class="p">(</span><span class="n">app</span><span class="p">,</span><span class="s">"/hello/<int>"</span><span class="p">)</span>
|
||||
@ -237,7 +250,7 @@ code{
|
||||
|
||||
<div class="dcard">
|
||||
<a href="https://github.com/CrowCpp/Crow/releases/latest">
|
||||
<img class="dcard__image" src="/assets/pkg_logos/ubuntu.png">
|
||||
<img class="dcard__image" src="assets/pkg_logos/ubuntu.png">
|
||||
<p class="dcard__title">.deb file</p>
|
||||
<p class="dcard__description">for Ubuntu/Debian based systems</p>
|
||||
</a>
|
||||
@ -245,7 +258,7 @@ code{
|
||||
|
||||
<div class="dcard">
|
||||
<a href="https://aur.archlinux.org/packages/crow">
|
||||
<img class="dcard__image" src="/assets/pkg_logos/arch.png">
|
||||
<img class="dcard__image" src="assets/pkg_logos/arch.png">
|
||||
<p class="dcard__title">AUR</p>
|
||||
<p class="dcard__description">for Arch Linux based systems</p>
|
||||
</a>
|
||||
@ -253,7 +266,7 @@ code{
|
||||
|
||||
<div class="dcard">
|
||||
<a href="https://vcpkg.io">
|
||||
<img class="dcard__image" src="/assets/pkg_logos/vcpkg.png">
|
||||
<img class="dcard__image" src="assets/pkg_logos/vcpkg.png">
|
||||
<p class="dcard__title">VCPKG</p>
|
||||
<p class="dcard__description">for Windows systems</p>
|
||||
</a>
|
||||
@ -261,7 +274,7 @@ code{
|
||||
|
||||
<div class="dcard">
|
||||
<a href="https://conan.io/center/crowcpp-crow">
|
||||
<img class="dcard__image" src="/assets/pkg_logos/conan.png">
|
||||
<img class="dcard__image" src="assets/pkg_logos/conan.png">
|
||||
<p class="dcard__title">Conan Center</p>
|
||||
<p class="dcard__description">for developers using the conan package manager</p>
|
||||
</a>
|
||||
@ -269,7 +282,7 @@ code{
|
||||
|
||||
<div class="dcard">
|
||||
<a href="https://github.com/CrowCpp/Crow/releases/latest">
|
||||
<img class="dcard__image" src="/assets/pkg_logos/github.png">
|
||||
<img class="dcard__image" src="assets/pkg_logos/github.png">
|
||||
<p class="dcard__title">Header File</p>
|
||||
<p class="dcard__description">Download Crow directly from github</p>
|
||||
</a>
|
||||
@ -282,9 +295,9 @@ code{
|
||||
<h3 style="text-align:center;">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.<h3>
|
||||
|
||||
<dev class="sbuttons">
|
||||
<a href="/getting_started/setup/" title="Get Started" class="md-button crow-button">Get Started</a>
|
||||
<a href="/guides/app/" title="Guides" class="md-button crow-button">Guides</a>
|
||||
<a href="/reference/index.html" title="API Reference" class="md-button crow-button">API Reference</a>
|
||||
<a href="getting_started/setup/" title="Get Started" class="md-button crow-button">Get Started</a>
|
||||
<a href="guides/app/" title="Guides" class="md-button crow-button">Guides</a>
|
||||
<a href="reference/index.html" title="API Reference" class="md-button crow-button">API Reference</a>
|
||||
</dev>
|
||||
|
||||
|
||||
@ -302,7 +315,7 @@ function makeCard(name, img_url, ref, size = 6)
|
||||
{
|
||||
img_url = AvatarImage(name);
|
||||
}
|
||||
return `<a title="${name}" href="${ref}" style=\"border-style: solid;border-width: .1rem;border-color: #00000080;border-radius: ${size/12}rem;width: ${size}rem;height: ${size}rem;box-shadow: 2px 5px 5px #00000040;margin-inline: ${size/12}rem;margin-bottom: ${size/12}rem;margin-top: ${size/12}rem;display: inline-block;\"><img style=\"width: ${size/2}rem;height: ${size/2}rem; border-radius: ${size/10}rem;margin-left: auto;margin-right: auto;display: block;margin-top: ${size/7.7}rem;\" src=\"${img_url}\"><p style=\"text-align: center;font-size: ${size/7.7}rem;\">${finalName}</p></a>`;
|
||||
return `<a title="${name}" href="${ref}" style=\"border-style: solid;border-width: .1rem;border-color: var(--home-border-color);border-radius: ${size/12}rem;width: ${size}rem;height: ${size}rem;box-shadow: 2px 5px 5px var(--home-shadow-color);margin-inline: ${size/12}rem;margin-bottom: ${size/12}rem;margin-top: ${size/12}rem;display: inline-block;\"><img style=\"width: ${size/2}rem;height: ${size/2}rem; border-radius: ${size/10}rem;margin-left: auto;margin-right: auto;display: block;margin-top: ${size/7.7}rem;\" src=\"${img_url}\"><p style=\"text-align: center;font-size: ${size/7.7}rem;\">${finalName}</p></a>`;
|
||||
}
|
||||
|
||||
function fixLong(name)
|
||||
@ -504,7 +517,7 @@ hidden
|
||||
<div class="md-footer-meta__inner md-grid">
|
||||
|
||||
<!-- Copyright and theme information -->
|
||||
<div class="md-footer-copyright">
|
||||
<div class="md-footer-copyright" style="flex: 1;display: flex;justify-content: left;">
|
||||
{% if config.copyright %}
|
||||
<div class="md-footer-copyright__highlight">
|
||||
{{ config.copyright }}
|
||||
@ -513,7 +526,7 @@ hidden
|
||||
{{ extracopyright }}
|
||||
</div>
|
||||
|
||||
<a style="margin: auto .6rem; font-size: .64rem;" href="/privacy_policy.html">Privacy Policy</a>
|
||||
<a style="margin: auto .6rem; font-size: .64rem;text-align: center;flex: 1;display: flex;justify-content: center;" href="privacy_policy.html">Privacy Policy</a>
|
||||
|
||||
<!-- Social links -->
|
||||
{% include "partials/social.html" %}
|
||||
|
@ -22,6 +22,15 @@
|
||||
|
||||
{% import "partials/language.html" as lang with context %}
|
||||
|
||||
<style>
|
||||
.md-footer-copyright {
|
||||
color: var(--md-footer-fg-color--light);
|
||||
font-size: .64rem;
|
||||
margin: auto .6rem;
|
||||
padding: .4rem 0;
|
||||
}
|
||||
</style>
|
||||
|
||||
<!-- Footer -->
|
||||
<footer class="md-footer">
|
||||
|
||||
|
37
docs/overrides/partials/social.html
Normal file
37
docs/overrides/partials/social.html
Normal file
@ -0,0 +1,37 @@
|
||||
<!--
|
||||
Copyright (c) 2016-2022 Martin Donath <martin.donath@squidfunk.com>
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to
|
||||
deal in the Software without restriction, including without limitation the
|
||||
rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
sell copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
IN THE SOFTWARE.
|
||||
-->
|
||||
|
||||
<!-- Social links -->
|
||||
<div class="md-social" style="flex: 1;display: flex;justify-content: right;">
|
||||
{% for social in config.extra.social %}
|
||||
{% set title = social.name %}
|
||||
{% if not title and "//" in social.link %}
|
||||
{% set _, url = social.link.split("//") %}
|
||||
{% set title = url.split("/")[0] %}
|
||||
{% endif %}
|
||||
<a
|
||||
href="{{ social.link }}"
|
||||
target="_blank" rel="noopener"
|
||||
title="{{ title | e }}"
|
||||
class="md-social__link"
|
||||
>
|
||||
{% include ".icons/" ~ social.icon ~ ".svg" %}
|
||||
</a>
|
||||
{% endfor %}
|
||||
</div>
|
@ -1,23 +1,67 @@
|
||||
|
||||
:root {
|
||||
[data-md-color-scheme="crow-light"]{
|
||||
|
||||
--md-primary-fg-color: #24404f;
|
||||
--md-accent-fg-color: #122027;
|
||||
--md-typeset-a-color: var(--md-accent-fg-color) !important;
|
||||
--md-accent-fg-color: #122027;
|
||||
--md-typeset-a-color: var(--md-accent-fg-color) !important;
|
||||
--md-default-bg-color: #e5f2f8;
|
||||
--md-code-bg-color: #cfcfcf !important;
|
||||
--md-code-hl-comment-color: var(--md-code-fg-color) !important;
|
||||
--md-code-hl-generic-color: var(--md-code-fg-color) !important;
|
||||
--md-code-hl-variable-color: var(--md-code-fg-color) !important;
|
||||
--md-code-fg-color: #fff !important;
|
||||
--md-code-hl-punctuation-color: #fff !important;
|
||||
--md-code-fg-color: #1d1d1d !important;
|
||||
--md-code-hl-punctuation-color: #1d1d1d !important;
|
||||
--home-border-color: #00000080;
|
||||
--home-shadow-color: #00000040;
|
||||
--home-image-border: linear-gradient(90deg, rgba(0,0,0,0) 0%, rgb(0, 0, 0) 50%, rgba(0,0,0,0) 100%);
|
||||
|
||||
}
|
||||
|
||||
[data-md-color-scheme="crow-dark"]{
|
||||
|
||||
--md-primary-fg-color: #24404f;
|
||||
--md-accent-fg-color: #445e6d;
|
||||
--md-default-fg-color: rgba(255, 255, 255, 0.87);
|
||||
--md-default-fg-color--light: rgba(255, 255, 255, 0.54);
|
||||
--md-default-fg-color--lighter: rgba(255, 255, 255, 0.32);
|
||||
--md-default-fg-color--lightest: rgba(255, 255, 255, 0.07);
|
||||
--md-typeset-a-color: var(--md-accent-fg-color) !important;
|
||||
--md-default-bg-color: #1a2124;
|
||||
--md-code-bg-color: #2f2f2f !important;
|
||||
--md-code-hl-comment-color: var(--md-code-fg-color) !important;
|
||||
--md-code-hl-generic-color: var(--md-code-fg-color) !important;
|
||||
--md-code-hl-variable-color: var(--md-code-fg-color) !important;
|
||||
--md-code-fg-color: #adadad !important;
|
||||
--md-code-hl-punctuation-color: #adadad !important;
|
||||
--home-border-color: #ffffff20;
|
||||
--home-shadow-color: #00000040;
|
||||
--home-image-border: linear-gradient(90deg, rgba(255, 255, 255, 0) 0%, rgba(255, 255, 255, 0.3) 50%, rgba(255, 255, 255, 0) 100%);
|
||||
|
||||
}
|
||||
|
||||
.md-typeset code {
|
||||
background-color: #cfcfcf;
|
||||
color: #1d1d1d;
|
||||
padding: 0.2rem;
|
||||
}
|
||||
|
||||
.md-typeset .md-button {
|
||||
color: var(--md-default-fg-color--light);
|
||||
border-radius: 0.5rem;
|
||||
}
|
||||
|
||||
/*
|
||||
:root{
|
||||
--md-default-fg-color: rgba(255, 255, 255, 0.87);
|
||||
--md-default-fg-color--light: rgba(255, 255, 255, 0.54);
|
||||
--md-default-fg-color--lighter: rgba(255, 255, 255, 0.32);
|
||||
--md-default-fg-color--lightest: rgba(255, 255, 255, 0.07);
|
||||
--md-default-bg-color: #1a2124;
|
||||
--md-typeset-a-color: var(--md-accent-fg-color) !important;
|
||||
}
|
||||
|
||||
.md-typeset code {
|
||||
background-color: #2f2f2f;
|
||||
}
|
||||
|
||||
|
||||
.highlight .c, .highlight .c1, .highlight .ch, .highlight .cm, .highlight .cs, .highlight .sd {
|
||||
color: #606060 !important;
|
||||
}
|
||||
@ -43,4 +87,3 @@
|
||||
.highlight .n {
|
||||
color: #1d1d1d;
|
||||
}
|
||||
|
||||
|
@ -6,3 +6,51 @@
|
||||
.code {
|
||||
border-radius: 5px;
|
||||
}
|
||||
|
||||
.tag
|
||||
{
|
||||
background-color: var(--md-primary-fg-color);
|
||||
color: var(--md-default-bg-color);
|
||||
border-radius: 50px;
|
||||
padding-left: 0.15em;
|
||||
padding-right: 0.35em;
|
||||
padding-top: 0.45em;
|
||||
padding-bottom: 0.35em;
|
||||
}
|
||||
|
||||
.tag a
|
||||
{
|
||||
color: var(--md-default-bg-color);
|
||||
}
|
||||
|
||||
.tag a:hover
|
||||
{
|
||||
color: var(--md-default-bg-color);
|
||||
}
|
||||
|
||||
.md-typeset :is(.emojione, .twemoji, .gemoji)
|
||||
{
|
||||
vertical-align: text-bottom;
|
||||
}
|
||||
|
||||
.md-typeset :is(.emojione, .twemoji, .gemoji) svg
|
||||
{
|
||||
width: 1.25em;
|
||||
}
|
||||
|
||||
[data-md-color-scheme="crow-dark"] img[src$="#only-dark"]
|
||||
{
|
||||
display: block;
|
||||
}
|
||||
[data-md-color-scheme="crow-light"] img[src$="#only-light"]
|
||||
{
|
||||
display: block;
|
||||
}
|
||||
[data-md-color-scheme="crow-light"] img[src$="#only-dark"]
|
||||
{
|
||||
display: none;
|
||||
}
|
||||
[data-md-color-scheme="crow-dark"] img[src$="#only-light"]
|
||||
{
|
||||
display: none;
|
||||
}
|
||||
|
5
docs/versions.json
Normal file
5
docs/versions.json
Normal file
@ -0,0 +1,5 @@
|
||||
[
|
||||
{"version": "master", "title": "master", "aliases": []},
|
||||
{"version": "1.0", "title": "1.0+3", "aliases": []},
|
||||
{"version": "0.3", "title": "0.3+4", "aliases": []}
|
||||
]
|
@ -87,6 +87,10 @@ add_executable(example_cors middlewares/example_cors.cpp)
|
||||
add_warnings_optimizations(example_cors)
|
||||
target_link_libraries(example_cors PUBLIC Crow::Crow)
|
||||
|
||||
add_executable(example_cookies middlewares/example_cookies.cpp)
|
||||
add_warnings_optimizations(example_cookies)
|
||||
target_link_libraries(example_cookies PUBLIC Crow::Crow)
|
||||
|
||||
if(MSVC)
|
||||
add_executable(example_vs example_vs.cpp)
|
||||
add_warnings_optimizations(example_vs)
|
||||
|
@ -5,11 +5,13 @@ struct RequestLogger
|
||||
struct context
|
||||
{};
|
||||
|
||||
// This method is run before handling the request
|
||||
void before_handle(crow::request& req, crow::response& /*res*/, context& /*ctx*/)
|
||||
{
|
||||
CROW_LOG_INFO << "Request to:" + req.url;
|
||||
}
|
||||
|
||||
// This method is run after handling the request
|
||||
void after_handle(crow::request& /*req*/, crow::response& /*res*/, context& /*ctx*/)
|
||||
{}
|
||||
};
|
||||
@ -23,6 +25,7 @@ struct SecretContentGuard : crow::ILocalMiddleware
|
||||
|
||||
void before_handle(crow::request& /*req*/, crow::response& res, context& /*ctx*/)
|
||||
{
|
||||
// A request can be aborted prematurely
|
||||
res.write("SECRET!");
|
||||
res.code = 403;
|
||||
res.end();
|
||||
@ -32,10 +35,28 @@ struct SecretContentGuard : crow::ILocalMiddleware
|
||||
{}
|
||||
};
|
||||
|
||||
struct RequestAppend : crow::ILocalMiddleware
|
||||
{
|
||||
// Values from this context can be accessed from handlers
|
||||
struct context
|
||||
{
|
||||
std::string message;
|
||||
};
|
||||
|
||||
void before_handle(crow::request& /*req*/, crow::response& /*res*/, context& /*ctx*/)
|
||||
{}
|
||||
|
||||
void after_handle(crow::request& /*req*/, crow::response& res, context& ctx)
|
||||
{
|
||||
// The response can be modified
|
||||
res.write(" + (" + ctx.message + ")");
|
||||
}
|
||||
};
|
||||
|
||||
int main()
|
||||
{
|
||||
// ALL middleware (including per handler) is listed
|
||||
crow::App<RequestLogger, SecretContentGuard> app;
|
||||
crow::App<RequestLogger, SecretContentGuard, RequestAppend> app;
|
||||
|
||||
CROW_ROUTE(app, "/")
|
||||
([]() {
|
||||
@ -48,7 +69,20 @@ int main()
|
||||
return "";
|
||||
});
|
||||
|
||||
app.port(18080).run();
|
||||
crow::Blueprint bp("bp", "c", "c");
|
||||
// Register middleware on all routes on a specific blueprint
|
||||
// This also applies to sub blueprints
|
||||
bp.CROW_MIDDLEWARES(app, RequestAppend);
|
||||
|
||||
CROW_BP_ROUTE(bp, "/")
|
||||
([&](const crow::request& req) {
|
||||
// Get RequestAppends context
|
||||
auto& ctx = app.get_context<RequestAppend>(req);
|
||||
ctx.message = "World";
|
||||
return "Hello:";
|
||||
});
|
||||
app.register_blueprint(bp);
|
||||
|
||||
app.port(18080).run();
|
||||
return 0;
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
//#define CROW_STATIC_DRIECTORY "alternative_directory/"
|
||||
//#define CROW_STATIC_DIRECTORY "alternative_directory/"
|
||||
//#define CROW_STATIC_ENDPOINT "/alternative_endpoint/<path>"
|
||||
//#define CROW_DISABLE_STATIC_DIR
|
||||
#include "crow.h"
|
||||
|
32
examples/middlewares/example_cookies.cpp
Normal file
32
examples/middlewares/example_cookies.cpp
Normal file
@ -0,0 +1,32 @@
|
||||
#include "crow.h"
|
||||
#include "crow/middlewares/cookie_parser.h"
|
||||
|
||||
int main()
|
||||
{
|
||||
// Include CookieParser middleware
|
||||
crow::App<crow::CookieParser> app;
|
||||
|
||||
CROW_ROUTE(app, "/read")
|
||||
([&](const crow::request& req) {
|
||||
auto& ctx = app.get_context<crow::CookieParser>(req);
|
||||
// Read cookies with get_cookie
|
||||
auto value = ctx.get_cookie("key");
|
||||
return "value: " + value;
|
||||
});
|
||||
|
||||
CROW_ROUTE(app, "/write")
|
||||
([&](const crow::request& req) {
|
||||
auto& ctx = app.get_context<crow::CookieParser>(req);
|
||||
// Store cookies with set_cookie
|
||||
ctx.set_cookie("key", "word")
|
||||
// configure additional parameters
|
||||
.path("/")
|
||||
.max_age(120)
|
||||
.httponly();
|
||||
return "ok!";
|
||||
});
|
||||
|
||||
app.port(18080).run();
|
||||
|
||||
return 0;
|
||||
}
|
@ -10,8 +10,7 @@ int main()
|
||||
std::mutex mtx;
|
||||
std::unordered_set<crow::websocket::connection*> users;
|
||||
|
||||
CROW_ROUTE(app, "/ws")
|
||||
.websocket()
|
||||
CROW_WEBSOCKET_ROUTE(app, "/ws")
|
||||
.onopen([&](crow::websocket::connection& conn) {
|
||||
CROW_LOG_INFO << "new websocket connection from " << conn.get_remote_ip();
|
||||
std::lock_guard<std::mutex> _(mtx);
|
||||
|
@ -19,8 +19,8 @@ sock.onopen = ()=>{
|
||||
sock.onerror = (e)=>{
|
||||
console.log('error',e)
|
||||
}
|
||||
sock.onclose = ()=>{
|
||||
console.log('close')
|
||||
sock.onclose = (e)=>{
|
||||
console.log('close', e)
|
||||
}
|
||||
sock.onmessage = (e)=>{
|
||||
$("#log").val(
|
||||
|
@ -19,6 +19,7 @@
|
||||
#include "crow/http_request.h"
|
||||
#include "crow/http_server.h"
|
||||
#include "crow/task_timer.h"
|
||||
#include "crow/websocket.h"
|
||||
#ifdef CROW_ENABLE_COMPRESSION
|
||||
#include "crow/compression.h"
|
||||
#endif
|
||||
@ -29,7 +30,8 @@
|
||||
#else
|
||||
#define CROW_ROUTE(app, url) app.route<crow::black_magic::get_parameter_tag(url)>(url)
|
||||
#define CROW_BP_ROUTE(blueprint, url) blueprint.new_rule_tagged<crow::black_magic::get_parameter_tag(url)>(url)
|
||||
#define CROW_MIDDLEWARES(app, ...) middlewares<decltype(app), __VA_ARGS__>()
|
||||
#define CROW_WEBSOCKET_ROUTE(app, url) app.route<crow::black_magic::get_parameter_tag(url)>(url).websocket<decltype(app)>(&app)
|
||||
#define CROW_MIDDLEWARES(app, ...) middlewares<std::remove_reference<decltype(app)>::type, __VA_ARGS__>()
|
||||
#endif
|
||||
#define CROW_CATCHALL_ROUTE(app) app.catchall_route()
|
||||
#define CROW_BP_CATCHALL_ROUTE(blueprint) blueprint.catchall_rule()
|
||||
@ -67,7 +69,7 @@ namespace crow
|
||||
/// Process an Upgrade request
|
||||
|
||||
///
|
||||
/// Currently used to upgrrade an HTTP connection to a WebSocket connection
|
||||
/// Currently used to upgrade an HTTP connection to a WebSocket connection
|
||||
template<typename Adaptor>
|
||||
void handle_upgrade(const request& req, response& res, Adaptor&& adaptor)
|
||||
{
|
||||
@ -77,7 +79,7 @@ namespace crow
|
||||
/// Process the request and generate a response for it
|
||||
void handle(request& req, response& res)
|
||||
{
|
||||
router_.handle(req, res);
|
||||
router_.handle<self_t>(req, res);
|
||||
}
|
||||
|
||||
/// Create a dynamic route using a rule (**Use CROW_ROUTE instead**)
|
||||
@ -108,6 +110,19 @@ namespace crow
|
||||
return router_.catchall_rule();
|
||||
}
|
||||
|
||||
/// Set the default max payload size for websockets
|
||||
self_t& websocket_max_payload(uint64_t max_payload)
|
||||
{
|
||||
max_payload_ = max_payload;
|
||||
return *this;
|
||||
}
|
||||
|
||||
/// Get the default max payload size for websockets
|
||||
uint64_t websocket_max_payload()
|
||||
{
|
||||
return max_payload_;
|
||||
}
|
||||
|
||||
self_t& signal_clear()
|
||||
{
|
||||
signals_.clear();
|
||||
@ -120,6 +135,11 @@ namespace crow
|
||||
return *this;
|
||||
}
|
||||
|
||||
std::vector<int> signals()
|
||||
{
|
||||
return signals_;
|
||||
}
|
||||
|
||||
/// Set the port that Crow will handle requests on
|
||||
self_t& port(std::uint16_t port)
|
||||
{
|
||||
@ -245,7 +265,8 @@ namespace crow
|
||||
|
||||
#ifndef CROW_DISABLE_STATIC_DIR
|
||||
route<crow::black_magic::get_parameter_tag(CROW_STATIC_ENDPOINT)>(CROW_STATIC_ENDPOINT)([](crow::response& res, std::string file_path_partial) {
|
||||
res.set_static_file_info(CROW_STATIC_DIRECTORY + file_path_partial);
|
||||
utility::sanitize_filename(file_path_partial);
|
||||
res.set_static_file_info_unsafe(CROW_STATIC_DIRECTORY + file_path_partial);
|
||||
res.end();
|
||||
});
|
||||
|
||||
@ -258,7 +279,8 @@ namespace crow
|
||||
if (!bp->static_dir().empty())
|
||||
{
|
||||
bp->new_rule_tagged<crow::black_magic::get_parameter_tag(CROW_STATIC_ENDPOINT)>(CROW_STATIC_ENDPOINT)([bp](crow::response& res, std::string file_path_partial) {
|
||||
res.set_static_file_info(bp->static_dir() + '/' + file_path_partial);
|
||||
utility::sanitize_filename(file_path_partial);
|
||||
res.set_static_file_info_unsafe(bp->static_dir() + '/' + file_path_partial);
|
||||
res.end();
|
||||
});
|
||||
}
|
||||
@ -303,7 +325,6 @@ namespace crow
|
||||
{
|
||||
server_ = std::move(std::unique_ptr<server_t>(new server_t(this, bindaddr_, port_, server_name_, &middlewares_, concurrency_, timeout_, nullptr)));
|
||||
server_->set_tick_function(tick_interval_, tick_function_);
|
||||
server_->signal_clear();
|
||||
for (auto snum : signals_)
|
||||
{
|
||||
server_->signal_add(snum);
|
||||
@ -332,10 +353,26 @@ namespace crow
|
||||
else
|
||||
#endif
|
||||
{
|
||||
std::vector<crow::websocket::connection*> websockets_to_close = websockets_;
|
||||
for (auto websocket : websockets_to_close)
|
||||
{
|
||||
CROW_LOG_INFO << "Quitting Websocket: " << websocket;
|
||||
websocket->close("Server Application Terminated");
|
||||
}
|
||||
if (server_) { server_->stop(); }
|
||||
}
|
||||
}
|
||||
|
||||
void add_websocket(crow::websocket::connection* conn)
|
||||
{
|
||||
websockets_.push_back(conn);
|
||||
}
|
||||
|
||||
void remove_websocket(crow::websocket::connection* conn)
|
||||
{
|
||||
std::remove(websockets_.begin(), websockets_.end(), conn);
|
||||
}
|
||||
|
||||
/// Print the routing paths defined for each HTTP method
|
||||
void debug_print()
|
||||
{
|
||||
@ -346,7 +383,7 @@ namespace crow
|
||||
|
||||
#ifdef CROW_ENABLE_SSL
|
||||
|
||||
/// use certificate and key files for SSL
|
||||
/// Use certificate and key files for SSL
|
||||
self_t& ssl_file(const std::string& crt_filename, const std::string& key_filename)
|
||||
{
|
||||
ssl_used_ = true;
|
||||
@ -359,7 +396,7 @@ namespace crow
|
||||
return *this;
|
||||
}
|
||||
|
||||
/// use .pem file for SSL
|
||||
/// Use .pem file for SSL
|
||||
self_t& ssl_file(const std::string& pem_filename)
|
||||
{
|
||||
ssl_used_ = true;
|
||||
@ -371,6 +408,19 @@ namespace crow
|
||||
return *this;
|
||||
}
|
||||
|
||||
/// Use certificate chain and key files for SSL
|
||||
self_t& ssl_chainfile(const std::string& crt_filename, const std::string& key_filename)
|
||||
{
|
||||
ssl_used_ = true;
|
||||
ssl_context_.set_verify_mode(boost::asio::ssl::verify_peer);
|
||||
ssl_context_.set_verify_mode(boost::asio::ssl::verify_client_once);
|
||||
ssl_context_.use_certificate_chain_file(crt_filename);
|
||||
ssl_context_.use_private_key_file(key_filename, ssl_context_t::pem);
|
||||
ssl_context_.set_options(
|
||||
boost::asio::ssl::context::default_workarounds | boost::asio::ssl::context::no_sslv2 | boost::asio::ssl::context::no_sslv3);
|
||||
return *this;
|
||||
}
|
||||
|
||||
self_t& ssl(boost::asio::ssl::context&& ctx)
|
||||
{
|
||||
ssl_used_ = true;
|
||||
@ -394,6 +444,17 @@ namespace crow
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<typename T, typename... Remain>
|
||||
self_t& ssl_chainfile(T&&, Remain&&...)
|
||||
{
|
||||
// We can't call .ssl() member function unless CROW_ENABLE_SSL is defined.
|
||||
static_assert(
|
||||
// make static_assert dependent to T; always false
|
||||
std::is_base_of<T, void>::value,
|
||||
"Define CROW_ENABLE_SSL to enable ssl support.");
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
self_t& ssl(T&&)
|
||||
{
|
||||
@ -452,6 +513,7 @@ namespace crow
|
||||
std::uint8_t timeout_{5};
|
||||
uint16_t port_ = 80;
|
||||
uint16_t concurrency_ = 2;
|
||||
uint64_t max_payload_{UINT64_MAX};
|
||||
bool validated_ = false;
|
||||
std::string server_name_ = std::string("Crow/") + VERSION;
|
||||
std::string bindaddr_ = "0.0.0.0";
|
||||
@ -481,6 +543,7 @@ namespace crow
|
||||
bool server_started_{false};
|
||||
std::condition_variable cv_started_;
|
||||
std::mutex start_mutex_;
|
||||
std::vector<crow::websocket::connection*> websockets_;
|
||||
};
|
||||
template<typename... Middlewares>
|
||||
using App = Crow<Middlewares...>;
|
||||
|
@ -325,7 +325,7 @@ constexpr crow::HTTPMethod method_from_string(const char* str)
|
||||
throw std::runtime_error("invalid http method");
|
||||
}
|
||||
|
||||
constexpr crow::HTTPMethod operator""_method(const char* str, size_t /*len*/)
|
||||
constexpr crow::HTTPMethod operator"" _method(const char* str, size_t /*len*/)
|
||||
{
|
||||
return method_from_string( str );
|
||||
}
|
||||
|
@ -129,27 +129,12 @@ namespace crow
|
||||
}
|
||||
if (req.upgrade)
|
||||
{
|
||||
#ifdef CROW_ENABLE_SSL
|
||||
if (handler_->ssl_used())
|
||||
{
|
||||
if (req.get_header_value("upgrade") == "h2")
|
||||
{
|
||||
// TODO(ipkn): HTTP/2
|
||||
// currently, ignore upgrade header
|
||||
}
|
||||
}
|
||||
else if (req.get_header_value("upgrade") == "h2c")
|
||||
// h2 or h2c headers
|
||||
if (req.get_header_value("upgrade").substr(0, 2) == "h2")
|
||||
{
|
||||
// TODO(ipkn): HTTP/2
|
||||
// currently, ignore upgrade header
|
||||
}
|
||||
#else
|
||||
if (req.get_header_value("upgrade") == "h2c")
|
||||
{
|
||||
// TODO(ipkn): HTTP/2
|
||||
// currently, ignore upgrade header
|
||||
}
|
||||
#endif
|
||||
else
|
||||
{
|
||||
close_connection_ = true;
|
||||
@ -176,7 +161,7 @@ namespace crow
|
||||
req.io_service = &adaptor_.get_io_service();
|
||||
|
||||
detail::middleware_call_helper<detail::middleware_call_criteria_only_global,
|
||||
0, decltype(ctx_), decltype(*middlewares_)>(*middlewares_, req, res, ctx_);
|
||||
0, decltype(ctx_), decltype(*middlewares_)>({}, *middlewares_, req, res, ctx_);
|
||||
|
||||
if (!res.completed_)
|
||||
{
|
||||
@ -213,7 +198,7 @@ namespace crow
|
||||
detail::middleware_call_criteria_only_global,
|
||||
(static_cast<int>(sizeof...(Middlewares)) - 1),
|
||||
decltype(ctx_),
|
||||
decltype(*middlewares_)>(*middlewares_, ctx_, req_, res);
|
||||
decltype(*middlewares_)>({}, *middlewares_, ctx_, req_, res);
|
||||
}
|
||||
#ifdef CROW_ENABLE_COMPRESSION
|
||||
if (handler_->compression_used())
|
||||
@ -332,12 +317,16 @@ namespace crow
|
||||
buffers_.reserve(4 * (res.headers.size() + 5) + 3);
|
||||
|
||||
if (!statusCodes.count(res.code))
|
||||
res.code = 500;
|
||||
{
|
||||
auto& status = statusCodes.find(res.code)->second;
|
||||
buffers_.emplace_back(status.data(), status.size());
|
||||
CROW_LOG_WARNING << this << " status code "
|
||||
<< "(" << res.code << ")"
|
||||
<< " not defined, returning 500 instead";
|
||||
res.code = 500;
|
||||
}
|
||||
|
||||
auto& status = statusCodes.find(res.code)->second;
|
||||
buffers_.emplace_back(status.data(), status.size());
|
||||
|
||||
if (res.code >= 400 && res.body.empty())
|
||||
res.body = statusCodes[res.code].substr(9);
|
||||
|
||||
@ -432,6 +421,7 @@ namespace crow
|
||||
{
|
||||
is_writing = true;
|
||||
boost::asio::write(adaptor_.socket(), buffers_); // Write the response start / headers
|
||||
cancel_deadline_timer();
|
||||
if (res.body.length() > 0)
|
||||
{
|
||||
std::string buf;
|
||||
|
@ -62,6 +62,15 @@ namespace crow
|
||||
return http_ver_major == major && http_ver_minor == minor;
|
||||
}
|
||||
|
||||
/// Get the body as parameters in QS format.
|
||||
|
||||
///
|
||||
/// This is meant to be used with requests of type "application/x-www-form-urlencoded"
|
||||
const query_string get_body_params()
|
||||
{
|
||||
return query_string(body, false);
|
||||
}
|
||||
|
||||
/// Send data to whoever made this request with a completion handler and return immediately.
|
||||
template<typename CompletionHandler>
|
||||
void post(CompletionHandler handler)
|
||||
|
@ -19,11 +19,7 @@ namespace crow
|
||||
template<typename Adaptor, typename Handler, typename... Middlewares>
|
||||
class Connection;
|
||||
|
||||
namespace detail
|
||||
{
|
||||
template<typename F, typename App, typename... Middlewares>
|
||||
struct handler_middleware_wrapper;
|
||||
} // namespace detail
|
||||
class Router;
|
||||
|
||||
/// HTTP response
|
||||
struct response
|
||||
@ -31,8 +27,7 @@ namespace crow
|
||||
template<typename Adaptor, typename Handler, typename... Middlewares>
|
||||
friend class crow::Connection;
|
||||
|
||||
template<typename F, typename App, typename... Middlewares>
|
||||
friend struct crow::detail::handler_middleware_wrapper;
|
||||
friend class Router;
|
||||
|
||||
int code{200}; ///< The Status code for the response.
|
||||
std::string body; ///< The actual payload containing the response data.
|
||||
@ -242,17 +237,20 @@ namespace crow
|
||||
{
|
||||
std::size_t last_dot = path.find_last_of(".");
|
||||
std::string extension = path.substr(last_dot + 1);
|
||||
std::string mimeType = "";
|
||||
code = 200;
|
||||
this->add_header("Content-length", std::to_string(file_info.statbuf.st_size));
|
||||
this->add_header("Content-Length", std::to_string(file_info.statbuf.st_size));
|
||||
|
||||
if (extension != "")
|
||||
if (!extension.empty())
|
||||
{
|
||||
mimeType = mime_types.at(extension);
|
||||
if (mimeType != "")
|
||||
this->add_header("Content-Type", mimeType);
|
||||
const auto mimeType = mime_types.find(extension);
|
||||
if (mimeType != mime_types.end())
|
||||
{
|
||||
this->add_header("Content-Type", mimeType->second);
|
||||
}
|
||||
else
|
||||
this->add_header("content-Type", "text/plain");
|
||||
{
|
||||
this->add_header("Content-Type", "text/plain");
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
|
@ -161,9 +161,18 @@ namespace crow
|
||||
|
||||
void stop()
|
||||
{
|
||||
io_service_.stop();
|
||||
shutting_down_ = true; // Prevent the acceptor from taking new connections
|
||||
for (auto& io_service : io_service_pool_)
|
||||
io_service->stop();
|
||||
{
|
||||
if (io_service != nullptr)
|
||||
{
|
||||
CROW_LOG_INFO << "Closing IO service " << &io_service;
|
||||
io_service->stop(); // Close all io_services (and HTTP connections)
|
||||
}
|
||||
}
|
||||
|
||||
CROW_LOG_INFO << "Closing main IO service (" << &io_service_ << ')';
|
||||
io_service_.stop(); // Close main io_service
|
||||
}
|
||||
|
||||
void signal_clear()
|
||||
@ -182,7 +191,9 @@ namespace crow
|
||||
uint16_t min_queue_idx = 0;
|
||||
|
||||
// TODO improve load balancing
|
||||
for (uint16_t i = 1; i < task_queue_length_pool_.size() && task_queue_length_pool_[min_queue_idx] > 0; i++)
|
||||
// size_t is used here to avoid the security issue https://codeql.github.com/codeql-query-help/cpp/cpp-comparison-with-wider-type/
|
||||
// even though the max value of this can be only uint16_t as concurrency is uint16_t.
|
||||
for (size_t i = 1; i < task_queue_length_pool_.size() && task_queue_length_pool_[min_queue_idx] > 0; i++)
|
||||
// No need to check other io_services if the current one has no tasks
|
||||
{
|
||||
if (task_queue_length_pool_[i] < task_queue_length_pool_[min_queue_idx])
|
||||
@ -193,33 +204,36 @@ namespace crow
|
||||
|
||||
void do_accept()
|
||||
{
|
||||
uint16_t service_idx = pick_io_service_idx();
|
||||
asio::io_service& is = *io_service_pool_[service_idx];
|
||||
task_queue_length_pool_[service_idx]++;
|
||||
CROW_LOG_DEBUG << &is << " {" << service_idx << "} queue length: " << task_queue_length_pool_[service_idx];
|
||||
if (!shutting_down_)
|
||||
{
|
||||
uint16_t service_idx = pick_io_service_idx();
|
||||
asio::io_service& is = *io_service_pool_[service_idx];
|
||||
task_queue_length_pool_[service_idx]++;
|
||||
CROW_LOG_DEBUG << &is << " {" << service_idx << "} queue length: " << task_queue_length_pool_[service_idx];
|
||||
|
||||
auto p = new Connection<Adaptor, Handler, Middlewares...>(
|
||||
is, handler_, server_name_, middlewares_,
|
||||
get_cached_date_str_pool_[service_idx], *task_timer_pool_[service_idx], adaptor_ctx_, task_queue_length_pool_[service_idx]);
|
||||
auto p = new Connection<Adaptor, Handler, Middlewares...>(
|
||||
is, handler_, server_name_, middlewares_,
|
||||
get_cached_date_str_pool_[service_idx], *task_timer_pool_[service_idx], adaptor_ctx_, task_queue_length_pool_[service_idx]);
|
||||
|
||||
acceptor_.async_accept(
|
||||
p->socket(),
|
||||
[this, p, &is, service_idx](boost::system::error_code ec) {
|
||||
if (!ec)
|
||||
{
|
||||
is.post(
|
||||
[p] {
|
||||
p->start();
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
task_queue_length_pool_[service_idx]--;
|
||||
CROW_LOG_DEBUG << &is << " {" << service_idx << "} queue length: " << task_queue_length_pool_[service_idx];
|
||||
delete p;
|
||||
}
|
||||
do_accept();
|
||||
});
|
||||
acceptor_.async_accept(
|
||||
p->socket(),
|
||||
[this, p, &is, service_idx](boost::system::error_code ec) {
|
||||
if (!ec)
|
||||
{
|
||||
is.post(
|
||||
[p] {
|
||||
p->start();
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
task_queue_length_pool_[service_idx]--;
|
||||
CROW_LOG_DEBUG << &is << " {" << service_idx << "} queue length: " << task_queue_length_pool_[service_idx];
|
||||
delete p;
|
||||
}
|
||||
do_accept();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
@ -228,6 +242,7 @@ namespace crow
|
||||
std::vector<detail::task_timer*> task_timer_pool_;
|
||||
std::vector<std::function<std::string()>> get_cached_date_str_pool_;
|
||||
tcp::acceptor acceptor_;
|
||||
bool shutting_down_ = false;
|
||||
boost::asio::signal_set signals_;
|
||||
boost::asio::deadline_timer tick_timer_;
|
||||
|
||||
|
@ -60,7 +60,7 @@ namespace crow
|
||||
prefix = "CRITICAL";
|
||||
break;
|
||||
}
|
||||
std::cerr << "(" << timestamp() << ") [" << prefix << "] " << message << std::endl;
|
||||
std::cerr << std::string("(") + timestamp() + std::string(") [") + prefix + std::string("] ") + message << std::endl;
|
||||
}
|
||||
|
||||
private:
|
||||
|
@ -92,6 +92,18 @@ namespace crow
|
||||
static constexpr bool value = decltype(f<T>(nullptr))::value;
|
||||
};
|
||||
|
||||
template<typename MW>
|
||||
struct is_middleware_global
|
||||
{
|
||||
template<typename C>
|
||||
static std::false_type f(typename check_global_call_false<MW>::template get<C>*);
|
||||
|
||||
template<typename C>
|
||||
static std::true_type f(...);
|
||||
|
||||
static const bool value = decltype(f<MW>(nullptr))::value;
|
||||
};
|
||||
|
||||
template<typename MW, typename Context, typename ParentContext>
|
||||
typename std::enable_if<!is_before_handle_arity_3_impl<MW>::value>::type
|
||||
before_handler_call(MW& mw, request& req, response& res, Context& ctx, ParentContext& /*parent_ctx*/)
|
||||
@ -121,17 +133,17 @@ namespace crow
|
||||
}
|
||||
|
||||
|
||||
template<template<typename QueryMW> class CallCriteria, // Checks if QueryMW should be called in this context
|
||||
template<typename CallCriteria,
|
||||
int N, typename Context, typename Container>
|
||||
typename std::enable_if<(N < std::tuple_size<typename std::remove_reference<Container>::type>::value), bool>::type
|
||||
middleware_call_helper(Container& middlewares, request& req, response& res, Context& ctx)
|
||||
middleware_call_helper(const CallCriteria& cc, Container& middlewares, request& req, response& res, Context& ctx)
|
||||
{
|
||||
|
||||
using CurrentMW = typename std::tuple_element<N, typename std::remove_reference<Container>::type>::type;
|
||||
|
||||
if (!CallCriteria<CurrentMW>::value)
|
||||
if (!cc.template enabled<CurrentMW>(N))
|
||||
{
|
||||
return middleware_call_helper<CallCriteria, N + 1, Context, Container>(middlewares, req, res, ctx);
|
||||
return middleware_call_helper<CallCriteria, N + 1, Context, Container>(cc, middlewares, req, res, ctx);
|
||||
}
|
||||
|
||||
using parent_context_t = typename Context::template partial<N - 1>;
|
||||
@ -142,7 +154,7 @@ namespace crow
|
||||
return true;
|
||||
}
|
||||
|
||||
if (middleware_call_helper<CallCriteria, N + 1, Context, Container>(middlewares, req, res, ctx))
|
||||
if (middleware_call_helper<CallCriteria, N + 1, Context, Container>(cc, middlewares, req, res, ctx))
|
||||
{
|
||||
after_handler_call<CurrentMW, Context, parent_context_t>(std::get<N>(middlewares), req, res, ctx, static_cast<parent_context_t&>(ctx));
|
||||
return true;
|
||||
@ -151,88 +163,68 @@ namespace crow
|
||||
return false;
|
||||
}
|
||||
|
||||
template<template<typename QueryMW> class CallCriteria, int N, typename Context, typename Container>
|
||||
template<typename CallCriteria, int N, typename Context, typename Container>
|
||||
typename std::enable_if<(N >= std::tuple_size<typename std::remove_reference<Container>::type>::value), bool>::type
|
||||
middleware_call_helper(Container& /*middlewares*/, request& /*req*/, response& /*res*/, Context& /*ctx*/)
|
||||
middleware_call_helper(const CallCriteria& /*cc*/, Container& /*middlewares*/, request& /*req*/, response& /*res*/, Context& /*ctx*/)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
template<template<typename QueryMW> class CallCriteria, int N, typename Context, typename Container>
|
||||
template<typename CallCriteria, int N, typename Context, typename Container>
|
||||
typename std::enable_if<(N < 0)>::type
|
||||
after_handlers_call_helper(Container& /*middlewares*/, Context& /*context*/, request& /*req*/, response& /*res*/)
|
||||
after_handlers_call_helper(const CallCriteria& /*cc*/, Container& /*middlewares*/, Context& /*context*/, request& /*req*/, response& /*res*/)
|
||||
{
|
||||
}
|
||||
|
||||
template<template<typename QueryMW> class CallCriteria, int N, typename Context, typename Container>
|
||||
typename std::enable_if<(N == 0)>::type after_handlers_call_helper(Container& middlewares, Context& ctx, request& req, response& res)
|
||||
template<typename CallCriteria, int N, typename Context, typename Container>
|
||||
typename std::enable_if<(N == 0)>::type after_handlers_call_helper(const CallCriteria& cc, Container& middlewares, Context& ctx, request& req, response& res)
|
||||
{
|
||||
using parent_context_t = typename Context::template partial<N - 1>;
|
||||
using CurrentMW = typename std::tuple_element<N, typename std::remove_reference<Container>::type>::type;
|
||||
if (CallCriteria<CurrentMW>::value)
|
||||
if (cc.template enabled<CurrentMW>(N))
|
||||
{
|
||||
after_handler_call<CurrentMW, Context, parent_context_t>(std::get<N>(middlewares), req, res, ctx, static_cast<parent_context_t&>(ctx));
|
||||
}
|
||||
}
|
||||
|
||||
template<template<typename QueryMW> class CallCriteria, int N, typename Context, typename Container>
|
||||
typename std::enable_if<(N > 0)>::type after_handlers_call_helper(Container& middlewares, Context& ctx, request& req, response& res)
|
||||
template<typename CallCriteria, int N, typename Context, typename Container>
|
||||
typename std::enable_if<(N > 0)>::type after_handlers_call_helper(const CallCriteria& cc, Container& middlewares, Context& ctx, request& req, response& res)
|
||||
{
|
||||
using parent_context_t = typename Context::template partial<N - 1>;
|
||||
using CurrentMW = typename std::tuple_element<N, typename std::remove_reference<Container>::type>::type;
|
||||
if (CallCriteria<CurrentMW>::value)
|
||||
if (cc.template enabled<CurrentMW>(N))
|
||||
{
|
||||
after_handler_call<CurrentMW, Context, parent_context_t>(std::get<N>(middlewares), req, res, ctx, static_cast<parent_context_t&>(ctx));
|
||||
}
|
||||
after_handlers_call_helper<CallCriteria, N - 1, Context, Container>(middlewares, ctx, req, res);
|
||||
after_handlers_call_helper<CallCriteria, N - 1, Context, Container>(cc, middlewares, ctx, req, res);
|
||||
}
|
||||
|
||||
// A CallCriteria that accepts only global middleware
|
||||
template<typename MW>
|
||||
struct middleware_call_criteria_only_global
|
||||
{
|
||||
template<typename C>
|
||||
static std::false_type f(typename check_global_call_false<MW>::template get<C>*);
|
||||
|
||||
template<typename C>
|
||||
static std::true_type f(...);
|
||||
|
||||
static const bool value = decltype(f<MW>(nullptr))::value;
|
||||
template<typename MW>
|
||||
constexpr bool enabled(int) const
|
||||
{
|
||||
return is_middleware_global<MW>::value;
|
||||
}
|
||||
};
|
||||
|
||||
// wrapped_handler_call transparently wraps a handler call behind (req, res, args...)
|
||||
template<typename F, typename... Args>
|
||||
typename std::enable_if<black_magic::is_callable<F, const crow::request, crow::response&, Args...>::value>::type
|
||||
wrapped_handler_call(crow::request& req, crow::response& res, const F& f, Args&&... args)
|
||||
{
|
||||
static_assert(std::is_same<void, decltype(f(std::declval<crow::request>(), std::declval<crow::response&>(), std::declval<Args>()...))>::value,
|
||||
"Handler function with response argument should have void return type");
|
||||
|
||||
f(req, res, std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
template<typename F, typename... Args>
|
||||
typename std::enable_if<black_magic::is_callable<F, crow::request&, crow::response&, Args...>::value && !black_magic::is_callable<F, const crow::request, crow::response&, Args...>::value>::type
|
||||
wrapped_handler_call(crow::request& req, crow::response& res, const F& f, Args&&... args)
|
||||
{
|
||||
static_assert(std::is_same<void, decltype(f(std::declval<crow::request&>(), std::declval<crow::response&>(), std::declval<Args>()...))>::value,
|
||||
"Handler function with response argument should have void return type");
|
||||
|
||||
f(req, res, std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
template<typename F, typename... Args>
|
||||
typename std::enable_if<black_magic::is_callable<F, crow::response&, Args...>::value>::type
|
||||
typename std::enable_if<black_magic::CallHelper<F, black_magic::S<Args...>>::value, void>::type
|
||||
wrapped_handler_call(crow::request& /*req*/, crow::response& res, const F& f, Args&&... args)
|
||||
{
|
||||
static_assert(std::is_same<void, decltype(f(std::declval<crow::response&>(), std::declval<Args>()...))>::value,
|
||||
"Handler function with response argument should have void return type");
|
||||
static_assert(!std::is_same<void, decltype(f(std::declval<Args>()...))>::value,
|
||||
"Handler function cannot have void return type; valid return types: string, int, crow::response, crow::returnable");
|
||||
|
||||
f(res, std::forward<Args>(args)...);
|
||||
res = crow::response(f(std::forward<Args>(args)...));
|
||||
res.end();
|
||||
}
|
||||
|
||||
template<typename F, typename... Args>
|
||||
typename std::enable_if<black_magic::is_callable<F, crow::request, Args...>::value>::type
|
||||
typename std::enable_if<
|
||||
!black_magic::CallHelper<F, black_magic::S<Args...>>::value &&
|
||||
black_magic::CallHelper<F, black_magic::S<crow::request&, Args...>>::value,
|
||||
void>::type
|
||||
wrapped_handler_call(crow::request& req, crow::response& res, const F& f, Args&&... args)
|
||||
{
|
||||
static_assert(!std::is_same<void, decltype(f(std::declval<crow::request>(), std::declval<Args>()...))>::value,
|
||||
@ -243,79 +235,96 @@ namespace crow
|
||||
}
|
||||
|
||||
template<typename F, typename... Args>
|
||||
typename std::enable_if<black_magic::is_callable<F, Args...>::value>::type
|
||||
typename std::enable_if<
|
||||
!black_magic::CallHelper<F, black_magic::S<Args...>>::value &&
|
||||
!black_magic::CallHelper<F, black_magic::S<crow::request&, Args...>>::value &&
|
||||
black_magic::CallHelper<F, black_magic::S<crow::response&, Args...>>::value,
|
||||
void>::type
|
||||
wrapped_handler_call(crow::request& /*req*/, crow::response& res, const F& f, Args&&... args)
|
||||
{
|
||||
static_assert(!std::is_same<void, decltype(f(std::declval<Args>()...))>::value,
|
||||
"Handler function cannot have void return type; valid return types: string, int, crow::response, crow::returnable");
|
||||
static_assert(std::is_same<void, decltype(f(std::declval<crow::response&>(), std::declval<Args>()...))>::value,
|
||||
"Handler function with response argument should have void return type");
|
||||
|
||||
res = crow::response(f(std::forward<Args>(args)...));
|
||||
res.end();
|
||||
f(res, std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
template<typename F, typename App, typename... Middlewares>
|
||||
struct handler_middleware_wrapper
|
||||
template<typename F, typename... Args>
|
||||
typename std::enable_if<
|
||||
!black_magic::CallHelper<F, black_magic::S<Args...>>::value &&
|
||||
!black_magic::CallHelper<F, black_magic::S<crow::request&, Args...>>::value &&
|
||||
!black_magic::CallHelper<F, black_magic::S<crow::response&, Args...>>::value &&
|
||||
black_magic::CallHelper<F, black_magic::S<const crow::request&, crow::response&, Args...>>::value,
|
||||
void>::type
|
||||
wrapped_handler_call(crow::request& req, crow::response& res, const F& f, Args&&... args)
|
||||
{
|
||||
// CallCriteria bound to the current Middlewares pack
|
||||
template<typename MW>
|
||||
struct middleware_call_criteria
|
||||
static_assert(std::is_same<void, decltype(f(std::declval<crow::request&>(), std::declval<crow::response&>(), std::declval<Args>()...))>::value,
|
||||
"Handler function with response argument should have void return type");
|
||||
|
||||
f(req, res, std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
// wrapped_handler_call transparently wraps a handler call behind (req, res, args...)
|
||||
template<typename F, typename... Args>
|
||||
typename std::enable_if<
|
||||
!black_magic::CallHelper<F, black_magic::S<Args...>>::value &&
|
||||
!black_magic::CallHelper<F, black_magic::S<crow::request&, Args...>>::value &&
|
||||
!black_magic::CallHelper<F, black_magic::S<crow::response&, Args...>>::value &&
|
||||
!black_magic::CallHelper<F, black_magic::S<const crow::request&, crow::response&, Args...>>::value,
|
||||
void>::type
|
||||
wrapped_handler_call(crow::request& req, crow::response& res, const F& f, Args&&... args)
|
||||
{
|
||||
static_assert(std::is_same<void, decltype(f(std::declval<crow::request&>(), std::declval<crow::response&>(), std::declval<Args>()...))>::value,
|
||||
"Handler function with response argument should have void return type");
|
||||
|
||||
f(req, res, std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
template<bool Reversed>
|
||||
struct middleware_call_criteria_dynamic
|
||||
{};
|
||||
|
||||
template<>
|
||||
struct middleware_call_criteria_dynamic<false>
|
||||
{
|
||||
middleware_call_criteria_dynamic(const std::vector<int>& indices):
|
||||
indices(indices), slider(0) {}
|
||||
|
||||
template<typename>
|
||||
bool enabled(int mw_index) const
|
||||
{
|
||||
static constexpr bool value = black_magic::has_type<MW, std::tuple<Middlewares...>>::value;
|
||||
};
|
||||
|
||||
template<typename... Args>
|
||||
void operator()(crow::request& req, crow::response& res, Args&&... args) const
|
||||
{
|
||||
auto& ctx = *reinterpret_cast<typename App::context_t*>(req.middleware_context);
|
||||
auto& container = *reinterpret_cast<typename App::mw_container_t*>(req.middleware_container);
|
||||
|
||||
auto glob_completion_handler = std::move(res.complete_request_handler_);
|
||||
res.complete_request_handler_ = [] {};
|
||||
|
||||
middleware_call_helper<middleware_call_criteria,
|
||||
0, typename App::context_t, typename App::mw_container_t>(container, req, res, ctx);
|
||||
|
||||
if (res.completed_)
|
||||
if (slider < int(indices.size()) && indices[slider] == mw_index)
|
||||
{
|
||||
glob_completion_handler();
|
||||
return;
|
||||
slider++;
|
||||
return true;
|
||||
}
|
||||
|
||||
res.complete_request_handler_ = [&ctx, &container, &req, &res, &glob_completion_handler] {
|
||||
after_handlers_call_helper<
|
||||
middleware_call_criteria,
|
||||
std::tuple_size<typename App::mw_container_t>::value - 1,
|
||||
typename App::context_t,
|
||||
typename App::mw_container_t>(container, ctx, req, res);
|
||||
glob_completion_handler();
|
||||
};
|
||||
|
||||
wrapped_handler_call(req, res, f, std::forward<Args>(args)...);
|
||||
return false;
|
||||
}
|
||||
|
||||
F f;
|
||||
private:
|
||||
const std::vector<int>& indices;
|
||||
mutable int slider;
|
||||
};
|
||||
|
||||
template<typename Route, typename App, typename... Middlewares>
|
||||
struct handler_call_bridge
|
||||
template<>
|
||||
struct middleware_call_criteria_dynamic<true>
|
||||
{
|
||||
template<typename MW>
|
||||
using check_app_contains = typename black_magic::has_type<MW, typename App::mw_container_t>;
|
||||
middleware_call_criteria_dynamic(const std::vector<int>& indices):
|
||||
indices(indices), slider(int(indices.size()) - 1) {}
|
||||
|
||||
static_assert(black_magic::all_true<(std::is_base_of<crow::ILocalMiddleware, Middlewares>::value)...>::value,
|
||||
"Local middleware has to inherit crow::ILocalMiddleware");
|
||||
|
||||
static_assert(black_magic::all_true<(check_app_contains<Middlewares>::value)...>::value,
|
||||
"Local middleware has to be listed in app middleware");
|
||||
|
||||
template<typename F>
|
||||
void operator()(F&& f) const
|
||||
template<typename>
|
||||
bool enabled(int mw_index) const
|
||||
{
|
||||
auto wrapped = handler_middleware_wrapper<F, App, Middlewares...>{std::forward<F>(f)};
|
||||
tptr->operator()(std::move(wrapped));
|
||||
if (slider >= 0 && indices[slider] == mw_index)
|
||||
{
|
||||
slider--;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
Route* tptr;
|
||||
private:
|
||||
const std::vector<int>& indices;
|
||||
mutable int slider;
|
||||
};
|
||||
|
||||
} // namespace detail
|
||||
|
@ -38,14 +38,14 @@ namespace crow
|
||||
struct context : private partial_context<Middlewares...>
|
||||
//struct context : private Middlewares::context... // simple but less type-safe
|
||||
{
|
||||
template<template<typename QueryMW> class CallCriteria, int N, typename Context, typename Container>
|
||||
friend typename std::enable_if<(N == 0)>::type after_handlers_call_helper(Container& middlewares, Context& ctx, request& req, response& res);
|
||||
template<template<typename QueryMW> class CallCriteria, int N, typename Context, typename Container>
|
||||
friend typename std::enable_if<(N > 0)>::type after_handlers_call_helper(Container& middlewares, Context& ctx, request& req, response& res);
|
||||
template<typename CallCriteria, int N, typename Context, typename Container>
|
||||
friend typename std::enable_if<(N == 0)>::type after_handlers_call_helper(const CallCriteria& cc, Container& middlewares, Context& ctx, request& req, response& res);
|
||||
template<typename CallCriteria, int N, typename Context, typename Container>
|
||||
friend typename std::enable_if<(N > 0)>::type after_handlers_call_helper(const CallCriteria& cc, Container& middlewares, Context& ctx, request& req, response& res);
|
||||
|
||||
template<template<typename QueryMW> class CallCriteria, int N, typename Context, typename Container>
|
||||
template<typename CallCriteria, int N, typename Context, typename Container>
|
||||
friend typename std::enable_if<(N < std::tuple_size<typename std::remove_reference<Container>::type>::value), bool>::type
|
||||
middleware_call_helper(Container& middlewares, request& req, response& res, Context& ctx);
|
||||
middleware_call_helper(const CallCriteria& cc, Container& middlewares, request& req, response& res, Context& ctx);
|
||||
|
||||
template<typename T>
|
||||
typename T::context& get()
|
||||
|
@ -1,4 +1,6 @@
|
||||
#pragma once
|
||||
#include <iomanip>
|
||||
#include <boost/optional.hpp>
|
||||
#include <boost/algorithm/string/trim.hpp>
|
||||
#include "crow/http_request.h"
|
||||
#include "crow/http_response.h"
|
||||
@ -30,10 +32,144 @@ namespace crow
|
||||
|
||||
struct CookieParser
|
||||
{
|
||||
// Cookie stores key, value and attributes
|
||||
struct Cookie
|
||||
{
|
||||
enum class SameSitePolicy
|
||||
{
|
||||
Strict,
|
||||
Lax,
|
||||
None
|
||||
};
|
||||
|
||||
template<typename U>
|
||||
Cookie(const std::string& key, U&& value):
|
||||
Cookie()
|
||||
{
|
||||
key_ = key;
|
||||
value_ = std::forward<U>(value);
|
||||
}
|
||||
|
||||
// format cookie to HTTP header format
|
||||
std::string dump() const
|
||||
{
|
||||
const static char* HTTP_DATE_FORMAT = "%a, %d %b %Y %H:%M:%S GMT";
|
||||
|
||||
std::stringstream ss;
|
||||
ss << key_ << '=';
|
||||
ss << (value_.empty() ? "\"\"" : value_);
|
||||
dumpString(ss, !domain_.empty(), "Domain=", domain_);
|
||||
dumpString(ss, !path_.empty(), "Path=", path_);
|
||||
dumpString(ss, secure_, "Secure");
|
||||
dumpString(ss, httponly_, "HttpOnly");
|
||||
if (expires_at_)
|
||||
{
|
||||
ss << DIVIDER << "Expires="
|
||||
<< std::put_time(expires_at_.get_ptr(), HTTP_DATE_FORMAT);
|
||||
}
|
||||
if (max_age_)
|
||||
{
|
||||
ss << DIVIDER << "Max-Age=" << *max_age_;
|
||||
}
|
||||
if (same_site_)
|
||||
{
|
||||
ss << DIVIDER << "SameSite=";
|
||||
switch (*same_site_)
|
||||
{
|
||||
case SameSitePolicy::Strict:
|
||||
ss << "Strict";
|
||||
break;
|
||||
case SameSitePolicy::Lax:
|
||||
ss << "Lax";
|
||||
break;
|
||||
case SameSitePolicy::None:
|
||||
ss << "None";
|
||||
break;
|
||||
}
|
||||
}
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
// Expires attribute
|
||||
Cookie& expires(const std::tm& time)
|
||||
{
|
||||
expires_at_ = time;
|
||||
return *this;
|
||||
}
|
||||
|
||||
// Max-Age attribute
|
||||
Cookie& max_age(long long seconds)
|
||||
{
|
||||
max_age_ = seconds;
|
||||
return *this;
|
||||
}
|
||||
|
||||
// Domain attribute
|
||||
Cookie& domain(const std::string& name)
|
||||
{
|
||||
domain_ = name;
|
||||
return *this;
|
||||
}
|
||||
|
||||
// Path attribute
|
||||
Cookie& path(const std::string& path)
|
||||
{
|
||||
path_ = path;
|
||||
return *this;
|
||||
}
|
||||
|
||||
// Secured attribute
|
||||
Cookie& secure()
|
||||
{
|
||||
secure_ = true;
|
||||
return *this;
|
||||
}
|
||||
|
||||
// HttpOnly attribute
|
||||
Cookie& httponly()
|
||||
{
|
||||
httponly_ = true;
|
||||
return *this;
|
||||
}
|
||||
|
||||
// SameSite attribute
|
||||
Cookie& same_site(SameSitePolicy ssp)
|
||||
{
|
||||
same_site_ = ssp;
|
||||
return *this;
|
||||
}
|
||||
|
||||
private:
|
||||
Cookie() = default;
|
||||
|
||||
static void dumpString(std::stringstream& ss, bool cond, const char* prefix,
|
||||
const std::string& value = "")
|
||||
{
|
||||
if (cond)
|
||||
{
|
||||
ss << DIVIDER << prefix << value;
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
std::string key_;
|
||||
std::string value_;
|
||||
boost::optional<long long> max_age_{};
|
||||
std::string domain_ = "";
|
||||
std::string path_ = "";
|
||||
bool secure_ = false;
|
||||
bool httponly_ = false;
|
||||
boost::optional<std::tm> expires_at_{};
|
||||
boost::optional<SameSitePolicy> same_site_{};
|
||||
|
||||
static constexpr const char* DIVIDER = "; ";
|
||||
};
|
||||
|
||||
|
||||
struct context
|
||||
{
|
||||
std::unordered_map<std::string, std::string> jar;
|
||||
std::unordered_map<std::string, std::string> cookies_to_add;
|
||||
std::vector<Cookie> cookies_to_add;
|
||||
|
||||
std::string get_cookie(const std::string& key) const
|
||||
{
|
||||
@ -43,14 +179,17 @@ namespace crow
|
||||
return {};
|
||||
}
|
||||
|
||||
void set_cookie(const std::string& key, const std::string& value)
|
||||
template<typename U>
|
||||
Cookie& set_cookie(const std::string& key, U&& value)
|
||||
{
|
||||
cookies_to_add.emplace(key, value);
|
||||
cookies_to_add.emplace_back(key, std::forward<U>(value));
|
||||
return cookies_to_add.back();
|
||||
}
|
||||
};
|
||||
|
||||
void before_handle(request& req, response& res, context& ctx)
|
||||
{
|
||||
// TODO(dranikpg): remove copies, use string_view with c++17
|
||||
int count = req.headers.count("Cookie");
|
||||
if (!count)
|
||||
return;
|
||||
@ -97,12 +236,9 @@ namespace crow
|
||||
|
||||
void after_handle(request& /*req*/, response& res, context& ctx)
|
||||
{
|
||||
for (auto& cookie : ctx.cookies_to_add)
|
||||
for (const auto& cookie : ctx.cookies_to_add)
|
||||
{
|
||||
if (cookie.second.empty())
|
||||
res.add_header("Set-Cookie", cookie.first + "=\"\"");
|
||||
else
|
||||
res.add_header("Set-Cookie", cookie.first + "=" + cookie.second);
|
||||
res.add_header("Set-Cookie", cookie.dump());
|
||||
}
|
||||
}
|
||||
};
|
||||
|
@ -182,17 +182,17 @@ namespace crow
|
||||
CORSRules default_ = CORSRules(this);
|
||||
};
|
||||
|
||||
CORSRules& CORSRules::prefix(const std::string& prefix)
|
||||
inline CORSRules& CORSRules::prefix(const std::string& prefix)
|
||||
{
|
||||
return handler_->prefix(prefix);
|
||||
}
|
||||
|
||||
CORSRules& CORSRules::blueprint(const Blueprint& bp)
|
||||
inline CORSRules& CORSRules::blueprint(const Blueprint& bp)
|
||||
{
|
||||
return handler_->blueprint(bp);
|
||||
}
|
||||
|
||||
CORSRules& CORSRules::global()
|
||||
inline CORSRules& CORSRules::global()
|
||||
{
|
||||
return handler_->global();
|
||||
}
|
||||
|
@ -42,7 +42,7 @@ namespace crow
|
||||
return empty;
|
||||
}
|
||||
|
||||
/// Same as \ref get_header_value_Object() but for \ref multipart.header
|
||||
/// Same as \ref get_header_value_object() but for \ref multipart.header
|
||||
template<typename T>
|
||||
inline const header& get_header_object(const T& headers, const std::string& key)
|
||||
{
|
||||
|
@ -10,19 +10,20 @@
|
||||
|
||||
namespace crow
|
||||
{
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// qs_parse (modified)
|
||||
// https://github.com/bartgrantham/qs_parse
|
||||
// ----------------------------------------------------------------------------
|
||||
/* Similar to strncmp, but handles URL-encoding for either string */
|
||||
int qs_strncmp(const char * s, const char * qs, size_t n);
|
||||
int qs_strncmp(const char* s, const char* qs, size_t n);
|
||||
|
||||
|
||||
/* Finds the beginning of each key/value pair and stores a pointer in qs_kv.
|
||||
* Also decodes the value portion of the k/v pair *in-place*. In a future
|
||||
* enhancement it will also have a compile-time option of sorting qs_kv
|
||||
* alphabetically by key. */
|
||||
int qs_parse(char * qs, char * qs_kv[], int qs_kv_size);
|
||||
int qs_parse(char* qs, char* qs_kv[], int qs_kv_size, bool parse_url);
|
||||
|
||||
|
||||
/* Used by qs_parse to decode the value portion of a k/v pair */
|
||||
@ -96,7 +97,7 @@ inline int qs_strncmp(const char * s, const char * qs, size_t n)
|
||||
}
|
||||
|
||||
|
||||
inline int qs_parse(char * qs, char * qs_kv[], int qs_kv_size)
|
||||
inline int qs_parse(char* qs, char* qs_kv[], int qs_kv_size, bool parse_url = true)
|
||||
{
|
||||
int i, j;
|
||||
char * substr_ptr;
|
||||
@ -104,7 +105,7 @@ inline int qs_parse(char * qs, char * qs_kv[], int qs_kv_size)
|
||||
for(i=0; i<qs_kv_size; i++) qs_kv[i] = NULL;
|
||||
|
||||
// find the beginning of the k/v substrings or the fragment
|
||||
substr_ptr = qs + strcspn(qs, "?#");
|
||||
substr_ptr = parse_url ? qs + strcspn(qs, "?#") : qs;
|
||||
if (substr_ptr[0] != '\0')
|
||||
substr_ptr++;
|
||||
else
|
||||
@ -137,7 +138,7 @@ inline int qs_parse(char * qs, char * qs_kv[], int qs_kv_size)
|
||||
#endif
|
||||
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
inline int qs_decode(char * qs)
|
||||
@ -285,8 +286,9 @@ inline char * qs_scanvalue(const char * key, const char * qs, char * val, size_t
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
|
||||
namespace crow
|
||||
namespace crow
|
||||
{
|
||||
struct request;
|
||||
/// A class to represent any data coming after the `?` in the request URL into key-value pairs.
|
||||
class query_string
|
||||
{
|
||||
@ -295,35 +297,34 @@ namespace crow
|
||||
|
||||
query_string()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
query_string(const query_string& qs)
|
||||
: url_(qs.url_)
|
||||
query_string(const query_string& qs):
|
||||
url_(qs.url_)
|
||||
{
|
||||
for(auto p:qs.key_value_pairs_)
|
||||
for (auto p : qs.key_value_pairs_)
|
||||
{
|
||||
key_value_pairs_.push_back((char*)(p-qs.url_.c_str()+url_.c_str()));
|
||||
key_value_pairs_.push_back((char*)(p - qs.url_.c_str() + url_.c_str()));
|
||||
}
|
||||
}
|
||||
|
||||
query_string& operator = (const query_string& qs)
|
||||
query_string& operator=(const query_string& qs)
|
||||
{
|
||||
url_ = qs.url_;
|
||||
key_value_pairs_.clear();
|
||||
for(auto p:qs.key_value_pairs_)
|
||||
for (auto p : qs.key_value_pairs_)
|
||||
{
|
||||
key_value_pairs_.push_back((char*)(p-qs.url_.c_str()+url_.c_str()));
|
||||
key_value_pairs_.push_back((char*)(p - qs.url_.c_str() + url_.c_str()));
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
query_string& operator = (query_string&& qs)
|
||||
query_string& operator=(query_string&& qs)
|
||||
{
|
||||
key_value_pairs_ = std::move(qs.key_value_pairs_);
|
||||
char* old_data = (char*)qs.url_.c_str();
|
||||
url_ = std::move(qs.url_);
|
||||
for(auto& p:key_value_pairs_)
|
||||
for (auto& p : key_value_pairs_)
|
||||
{
|
||||
p += (char*)url_.c_str() - old_data;
|
||||
}
|
||||
@ -331,19 +332,19 @@ namespace crow
|
||||
}
|
||||
|
||||
|
||||
query_string(std::string url)
|
||||
: url_(std::move(url))
|
||||
query_string(std::string params, bool url = true):
|
||||
url_(std::move(params))
|
||||
{
|
||||
if (url_.empty())
|
||||
return;
|
||||
|
||||
key_value_pairs_.resize(MAX_KEY_VALUE_PAIRS_COUNT);
|
||||
|
||||
int count = qs_parse(&url_[0], &key_value_pairs_[0], MAX_KEY_VALUE_PAIRS_COUNT);
|
||||
int count = qs_parse(&url_[0], &key_value_pairs_[0], MAX_KEY_VALUE_PAIRS_COUNT, url);
|
||||
key_value_pairs_.resize(count);
|
||||
}
|
||||
|
||||
void clear()
|
||||
void clear()
|
||||
{
|
||||
key_value_pairs_.clear();
|
||||
url_.clear();
|
||||
@ -352,38 +353,38 @@ namespace crow
|
||||
friend std::ostream& operator<<(std::ostream& os, const query_string& qs)
|
||||
{
|
||||
os << "[ ";
|
||||
for(size_t i = 0; i < qs.key_value_pairs_.size(); ++i) {
|
||||
for (size_t i = 0; i < qs.key_value_pairs_.size(); ++i)
|
||||
{
|
||||
if (i)
|
||||
os << ", ";
|
||||
os << qs.key_value_pairs_[i];
|
||||
}
|
||||
os << " ]";
|
||||
return os;
|
||||
|
||||
}
|
||||
|
||||
/// Get a value from a name, used for `?name=value`.
|
||||
|
||||
///
|
||||
/// Note: this method returns the value of the first occurrence of the key only, to return all occurrences, see \ref get_list().
|
||||
char* get (const std::string& name) const
|
||||
char* get(const std::string& name) const
|
||||
{
|
||||
char* ret = qs_k2v(name.c_str(), key_value_pairs_.data(), key_value_pairs_.size());
|
||||
return ret;
|
||||
}
|
||||
|
||||
/// Works similar to \ref get() except it removes the item from the query string.
|
||||
char* pop (const std::string& name)
|
||||
char* pop(const std::string& name)
|
||||
{
|
||||
char* ret = get(name);
|
||||
if (ret != nullptr)
|
||||
{
|
||||
for (unsigned int i = 0; i<key_value_pairs_.size(); i++)
|
||||
for (unsigned int i = 0; i < key_value_pairs_.size(); i++)
|
||||
{
|
||||
std::string str_item(key_value_pairs_[i]);
|
||||
if (str_item.substr(0, name.size()+1) == name+'=')
|
||||
if (str_item.substr(0, name.size() + 1) == name + '=')
|
||||
{
|
||||
key_value_pairs_.erase(key_value_pairs_.begin()+i);
|
||||
key_value_pairs_.erase(key_value_pairs_.begin() + i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -395,14 +396,14 @@ namespace crow
|
||||
|
||||
///
|
||||
/// Note: Square brackets in the above example are controlled by `use_brackets` boolean (true by default). If set to false, the example becomes `?name=value1,name=value2...name=valuen`
|
||||
std::vector<char*> get_list (const std::string& name, bool use_brackets = true) const
|
||||
std::vector<char*> get_list(const std::string& name, bool use_brackets = true) const
|
||||
{
|
||||
std::vector<char*> ret;
|
||||
std::string plus = name + (use_brackets ? "[]" : "");
|
||||
char* element = nullptr;
|
||||
|
||||
int count = 0;
|
||||
while(1)
|
||||
while (1)
|
||||
{
|
||||
element = qs_k2v(plus.c_str(), key_value_pairs_.data(), key_value_pairs_.size(), count++);
|
||||
if (!element)
|
||||
@ -413,17 +414,17 @@ namespace crow
|
||||
}
|
||||
|
||||
/// Similar to \ref get_list() but it removes the
|
||||
std::vector<char*> pop_list (const std::string& name, bool use_brackets = true)
|
||||
std::vector<char*> pop_list(const std::string& name, bool use_brackets = true)
|
||||
{
|
||||
std::vector<char*> ret = get_list(name, use_brackets);
|
||||
if (!ret.empty())
|
||||
{
|
||||
for (unsigned int i = 0; i<key_value_pairs_.size(); i++)
|
||||
for (unsigned int i = 0; i < key_value_pairs_.size(); i++)
|
||||
{
|
||||
std::string str_item(key_value_pairs_[i]);
|
||||
if ((use_brackets ? (str_item.substr(0, name.size()+3) == name+"[]=") : (str_item.substr(0, name.size()+1) == name+'=')))
|
||||
if ((use_brackets ? (str_item.substr(0, name.size() + 3) == name + "[]=") : (str_item.substr(0, name.size() + 1) == name + '=')))
|
||||
{
|
||||
key_value_pairs_.erase(key_value_pairs_.begin()+i--);
|
||||
key_value_pairs_.erase(key_value_pairs_.begin() + i--);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -436,12 +437,12 @@ namespace crow
|
||||
/// For example calling `get_dict(yourname)` on `?yourname[sub1]=42&yourname[sub2]=84` would give a map containing `{sub1 : 42, sub2 : 84}`.
|
||||
///
|
||||
/// if your query string has both empty brackets and ones with a key inside, use pop_list() to get all the values without a key before running this method.
|
||||
std::unordered_map<std::string, std::string> get_dict (const std::string& name) const
|
||||
std::unordered_map<std::string, std::string> get_dict(const std::string& name) const
|
||||
{
|
||||
std::unordered_map<std::string, std::string> ret;
|
||||
|
||||
int count = 0;
|
||||
while(1)
|
||||
while (1)
|
||||
{
|
||||
if (auto element = qs_dict_name2kv(name.c_str(), key_value_pairs_.data(), key_value_pairs_.size(), count++))
|
||||
ret.insert(*element);
|
||||
@ -452,17 +453,17 @@ namespace crow
|
||||
}
|
||||
|
||||
/// Works the same as \ref get_dict() but removes the values from the query string.
|
||||
std::unordered_map<std::string, std::string> pop_dict (const std::string& name)
|
||||
std::unordered_map<std::string, std::string> pop_dict(const std::string& name)
|
||||
{
|
||||
std::unordered_map<std::string, std::string> ret = get_dict(name);
|
||||
if (!ret.empty())
|
||||
{
|
||||
for (unsigned int i = 0; i<key_value_pairs_.size(); i++)
|
||||
for (unsigned int i = 0; i < key_value_pairs_.size(); i++)
|
||||
{
|
||||
std::string str_item(key_value_pairs_[i]);
|
||||
if (str_item.substr(0, name.size()+1) == name+'[')
|
||||
if (str_item.substr(0, name.size() + 1) == name + '[')
|
||||
{
|
||||
key_value_pairs_.erase(key_value_pairs_.begin()+i--);
|
||||
key_value_pairs_.erase(key_value_pairs_.begin() + i--);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -472,7 +473,7 @@ namespace crow
|
||||
std::vector<std::string> keys() const
|
||||
{
|
||||
std::vector<std::string> ret;
|
||||
for (auto element: key_value_pairs_)
|
||||
for (auto element : key_value_pairs_)
|
||||
{
|
||||
std::string str_element(element);
|
||||
ret.emplace_back(str_element.substr(0, str_element.find('=')));
|
||||
@ -485,4 +486,4 @@ namespace crow
|
||||
std::vector<char*> key_value_pairs_;
|
||||
};
|
||||
|
||||
} // end namespace
|
||||
} // namespace crow
|
||||
|
@ -7,6 +7,8 @@
|
||||
#include <memory>
|
||||
#include <boost/lexical_cast.hpp>
|
||||
#include <vector>
|
||||
#include <algorithm>
|
||||
#include <type_traits>
|
||||
|
||||
#include "crow/common.h"
|
||||
#include "crow/http_response.h"
|
||||
@ -22,6 +24,63 @@ namespace crow
|
||||
|
||||
constexpr const uint16_t INVALID_BP_ID{((uint16_t)-1)};
|
||||
|
||||
namespace detail
|
||||
{
|
||||
/// Typesafe wrapper for storing lists of middleware as their indices in the App
|
||||
struct middleware_indices
|
||||
{
|
||||
template<typename App>
|
||||
void push()
|
||||
{}
|
||||
|
||||
template<typename App, typename MW, typename... Middlewares>
|
||||
void push()
|
||||
{
|
||||
using MwContainer = typename App::mw_container_t;
|
||||
static_assert(black_magic::has_type<MW, MwContainer>::value, "Middleware must be present in app");
|
||||
static_assert(std::is_base_of<crow::ILocalMiddleware, MW>::value, "Middleware must extend ILocalMiddleware");
|
||||
int idx = black_magic::tuple_index<MW, MwContainer>::value;
|
||||
indices_.push_back(idx);
|
||||
push<App, Middlewares...>();
|
||||
}
|
||||
|
||||
void merge_front(const detail::middleware_indices& other)
|
||||
{
|
||||
indices_.insert(indices_.begin(), other.indices_.cbegin(), other.indices_.cend());
|
||||
}
|
||||
|
||||
void merge_back(const detail::middleware_indices& other)
|
||||
{
|
||||
indices_.insert(indices_.end(), other.indices_.cbegin(), other.indices_.cend());
|
||||
}
|
||||
|
||||
void pop_back(const detail::middleware_indices& other)
|
||||
{
|
||||
indices_.resize(indices_.size() - other.indices_.size());
|
||||
}
|
||||
|
||||
bool empty() const
|
||||
{
|
||||
return indices_.empty();
|
||||
}
|
||||
|
||||
// Sorts indices and filters out duplicates to allow fast lookups with traversal
|
||||
void pack()
|
||||
{
|
||||
std::sort(indices_.begin(), indices_.end());
|
||||
indices_.erase(std::unique(indices_.begin(), indices_.end()), indices_.end());
|
||||
}
|
||||
|
||||
const std::vector<int>& indices()
|
||||
{
|
||||
return indices_;
|
||||
}
|
||||
|
||||
private:
|
||||
std::vector<int> indices_;
|
||||
};
|
||||
} // namespace detail
|
||||
|
||||
/// A base class for all rules.
|
||||
|
||||
///
|
||||
@ -74,7 +133,6 @@ namespace crow
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
std::string custom_templates_base;
|
||||
|
||||
const std::string& rule() { return rule_; }
|
||||
@ -87,6 +145,8 @@ namespace crow
|
||||
|
||||
std::unique_ptr<BaseRule> rule_to_upgrade_;
|
||||
|
||||
detail::middleware_indices mw_indices_;
|
||||
|
||||
friend class Router;
|
||||
friend class Blueprint;
|
||||
template<typename T>
|
||||
@ -368,13 +428,16 @@ namespace crow
|
||||
|
||||
///
|
||||
/// Provides the interface for the user to put in the necessary handlers for a websocket to work.
|
||||
template<typename App>
|
||||
class WebSocketRule : public BaseRule
|
||||
{
|
||||
using self_t = WebSocketRule;
|
||||
|
||||
public:
|
||||
WebSocketRule(std::string rule):
|
||||
BaseRule(std::move(rule))
|
||||
WebSocketRule(std::string rule, App* app):
|
||||
BaseRule(std::move(rule)),
|
||||
app_(app),
|
||||
max_payload_(UINT64_MAX)
|
||||
{}
|
||||
|
||||
void validate() override
|
||||
@ -388,15 +451,24 @@ namespace crow
|
||||
|
||||
void handle_upgrade(const request& req, response&, SocketAdaptor&& adaptor) override
|
||||
{
|
||||
new crow::websocket::Connection<SocketAdaptor>(req, std::move(adaptor), open_handler_, message_handler_, close_handler_, error_handler_, accept_handler_);
|
||||
max_payload_ = max_payload_override_ ? max_payload_ : app_->websocket_max_payload();
|
||||
new crow::websocket::Connection<SocketAdaptor, App>(req, std::move(adaptor), app_, max_payload_, open_handler_, message_handler_, close_handler_, error_handler_, accept_handler_);
|
||||
}
|
||||
#ifdef CROW_ENABLE_SSL
|
||||
void handle_upgrade(const request& req, response&, SSLAdaptor&& adaptor) override
|
||||
{
|
||||
new crow::websocket::Connection<SSLAdaptor>(req, std::move(adaptor), open_handler_, message_handler_, close_handler_, error_handler_, accept_handler_);
|
||||
new crow::websocket::Connection<SSLAdaptor, App>(req, std::move(adaptor), app_, max_payload_, open_handler_, message_handler_, close_handler_, error_handler_, accept_handler_);
|
||||
}
|
||||
#endif
|
||||
|
||||
/// Override the global payload limit for this single WebSocket rule
|
||||
self_t& max_payload(uint64_t max_payload)
|
||||
{
|
||||
max_payload_ = max_payload;
|
||||
max_payload_override_ = true;
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<typename Func>
|
||||
self_t& onopen(Func f)
|
||||
{
|
||||
@ -433,11 +505,14 @@ namespace crow
|
||||
}
|
||||
|
||||
protected:
|
||||
App* app_;
|
||||
std::function<void(crow::websocket::connection&)> open_handler_;
|
||||
std::function<void(crow::websocket::connection&, const std::string&, bool)> message_handler_;
|
||||
std::function<void(crow::websocket::connection&, const std::string&)> close_handler_;
|
||||
std::function<void(crow::websocket::connection&)> error_handler_;
|
||||
std::function<bool(const crow::request&)> accept_handler_;
|
||||
uint64_t max_payload_;
|
||||
bool max_payload_override_ = false;
|
||||
};
|
||||
|
||||
/// Allows the user to assign parameters using functions.
|
||||
@ -448,9 +523,11 @@ namespace crow
|
||||
struct RuleParameterTraits
|
||||
{
|
||||
using self_t = T;
|
||||
WebSocketRule& websocket()
|
||||
|
||||
template<typename App>
|
||||
WebSocketRule<App>& websocket(App* app)
|
||||
{
|
||||
auto p = new WebSocketRule(static_cast<self_t*>(this)->rule_);
|
||||
auto p = new WebSocketRule<App>(static_cast<self_t*>(this)->rule_, app);
|
||||
static_cast<self_t*>(this)->rule_to_upgrade_.reset(p);
|
||||
return *p;
|
||||
}
|
||||
@ -474,6 +551,14 @@ namespace crow
|
||||
static_cast<self_t*>(this)->methods_ |= 1 << static_cast<int>(method);
|
||||
return static_cast<self_t&>(*this);
|
||||
}
|
||||
|
||||
/// Enable local middleware for this handler
|
||||
template<typename App, typename... Middlewares>
|
||||
self_t& middlewares()
|
||||
{
|
||||
static_cast<self_t*>(this)->mw_indices_.template push<App, Middlewares...>();
|
||||
return static_cast<self_t&>(*this);
|
||||
}
|
||||
};
|
||||
|
||||
/// A rule that can change its parameters during runtime.
|
||||
@ -607,16 +692,6 @@ namespace crow
|
||||
detail::routing_handler_call_helper::call_params<decltype(handler_)>{handler_, params, req, res});
|
||||
}
|
||||
|
||||
/// Enable local middleware for this handler
|
||||
template<typename App, typename... Middlewares>
|
||||
crow::detail::handler_call_bridge<TaggedRule<Args...>, App, Middlewares...>
|
||||
middlewares()
|
||||
{
|
||||
// the handler_call_bridge allows the functor to be placed directly after this function
|
||||
// instead of wrapping it with more parentheses
|
||||
return {this};
|
||||
}
|
||||
|
||||
private:
|
||||
std::function<void(crow::request&, crow::response&, Args...)> handler_;
|
||||
};
|
||||
@ -1128,6 +1203,12 @@ namespace crow
|
||||
return catchall_rule_;
|
||||
}
|
||||
|
||||
template<typename App, typename... Middlewares>
|
||||
void middlewares()
|
||||
{
|
||||
mw_indices_.push<App, Middlewares...>();
|
||||
}
|
||||
|
||||
private:
|
||||
void apply_blueprint(Blueprint& blueprint)
|
||||
{
|
||||
@ -1153,6 +1234,7 @@ namespace crow
|
||||
std::vector<std::unique_ptr<BaseRule>> all_rules_;
|
||||
CatchallRule catchall_rule_;
|
||||
std::vector<Blueprint*> blueprints_;
|
||||
detail::middleware_indices mw_indices_;
|
||||
|
||||
friend class Router;
|
||||
};
|
||||
@ -1199,6 +1281,8 @@ namespace crow
|
||||
rule_without_trailing_slash.pop_back();
|
||||
}
|
||||
|
||||
ruleObject->mw_indices_.pack();
|
||||
|
||||
ruleObject->foreach_method([&](int method) {
|
||||
per_methods_[method].rules.emplace_back(ruleObject);
|
||||
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);
|
||||
@ -1207,7 +1291,7 @@ namespace crow
|
||||
// request to '/about' url matches '/about/' rule
|
||||
if (has_trailing_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);
|
||||
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);
|
||||
}
|
||||
});
|
||||
}
|
||||
@ -1244,7 +1328,7 @@ namespace crow
|
||||
}
|
||||
}
|
||||
|
||||
void validate_bp(std::vector<Blueprint*> blueprints)
|
||||
void validate_bp(std::vector<Blueprint*> blueprints, detail::middleware_indices& current_mw)
|
||||
{
|
||||
for (unsigned i = 0; i < blueprints.size(); i++)
|
||||
{
|
||||
@ -1259,6 +1343,8 @@ namespace crow
|
||||
per_methods_[i].trie.add(blueprint->prefix(), 0, blueprint->prefix().length(), i);
|
||||
}
|
||||
}
|
||||
|
||||
current_mw.merge_back(blueprint->mw_indices_);
|
||||
for (auto& rule : blueprint->all_rules_)
|
||||
{
|
||||
if (rule)
|
||||
@ -1267,17 +1353,20 @@ namespace crow
|
||||
if (upgraded)
|
||||
rule = std::move(upgraded);
|
||||
rule->validate();
|
||||
rule->mw_indices_.merge_front(current_mw);
|
||||
internal_add_rule_object(rule->rule(), rule.get(), i, blueprints);
|
||||
}
|
||||
}
|
||||
validate_bp(blueprint->blueprints_);
|
||||
validate_bp(blueprint->blueprints_, current_mw);
|
||||
current_mw.pop_back(blueprint->mw_indices_);
|
||||
}
|
||||
}
|
||||
|
||||
void validate()
|
||||
{
|
||||
//Take all the routes from the registered blueprints and add them to `all_rules_` to be processed.
|
||||
validate_bp(blueprints_);
|
||||
detail::middleware_indices blueprint_mw;
|
||||
validate_bp(blueprints_, blueprint_mw);
|
||||
|
||||
for (auto& rule : all_rules_)
|
||||
{
|
||||
@ -1442,6 +1531,7 @@ namespace crow
|
||||
return std::string();
|
||||
}
|
||||
|
||||
template<typename App>
|
||||
void handle(request& req, response& res)
|
||||
{
|
||||
HTTPMethod method_actual = req.method;
|
||||
@ -1468,7 +1558,6 @@ namespace crow
|
||||
allow = allow.substr(0, allow.size() - 2);
|
||||
res = response(204);
|
||||
res.set_header("Allow", allow);
|
||||
res.manual_length_header = true;
|
||||
res.end();
|
||||
return;
|
||||
}
|
||||
@ -1486,7 +1575,6 @@ namespace crow
|
||||
allow = allow.substr(0, allow.size() - 2);
|
||||
res = response(204);
|
||||
res.set_header("Allow", allow);
|
||||
res.manual_length_header = true;
|
||||
res.end();
|
||||
return;
|
||||
}
|
||||
@ -1554,7 +1642,8 @@ namespace crow
|
||||
// any uncaught exceptions become 500s
|
||||
try
|
||||
{
|
||||
rules[rule_index]->handle(req, res, std::get<2>(found));
|
||||
auto& rule = rules[rule_index];
|
||||
handle_rule<App>(rule, req, res, std::get<2>(found));
|
||||
}
|
||||
catch (std::exception& e)
|
||||
{
|
||||
@ -1572,6 +1661,49 @@ namespace crow
|
||||
}
|
||||
}
|
||||
|
||||
template<typename App>
|
||||
typename std::enable_if<std::tuple_size<typename App::mw_container_t>::value != 0, void>::type
|
||||
handle_rule(BaseRule* rule, crow::request& req, crow::response& res, const crow::routing_params& rp)
|
||||
{
|
||||
if (!rule->mw_indices_.empty())
|
||||
{
|
||||
auto& ctx = *reinterpret_cast<typename App::context_t*>(req.middleware_context);
|
||||
auto& container = *reinterpret_cast<typename App::mw_container_t*>(req.middleware_container);
|
||||
detail::middleware_call_criteria_dynamic<false> crit_fwd(rule->mw_indices_.indices());
|
||||
|
||||
auto glob_completion_handler = std::move(res.complete_request_handler_);
|
||||
res.complete_request_handler_ = [] {};
|
||||
|
||||
detail::middleware_call_helper<decltype(crit_fwd),
|
||||
0, typename App::context_t, typename App::mw_container_t>(crit_fwd, container, req, res, ctx);
|
||||
|
||||
if (res.completed_)
|
||||
{
|
||||
glob_completion_handler();
|
||||
return;
|
||||
}
|
||||
|
||||
res.complete_request_handler_ = [&rule, &ctx, &container, &req, &res, &glob_completion_handler] {
|
||||
detail::middleware_call_criteria_dynamic<true> crit_bwd(rule->mw_indices_.indices());
|
||||
|
||||
detail::after_handlers_call_helper<
|
||||
decltype(crit_bwd),
|
||||
std::tuple_size<typename App::mw_container_t>::value - 1,
|
||||
typename App::context_t,
|
||||
typename App::mw_container_t>(crit_bwd, container, ctx, req, res);
|
||||
glob_completion_handler();
|
||||
};
|
||||
}
|
||||
rule->handle(req, res, rp);
|
||||
}
|
||||
|
||||
template<typename App>
|
||||
typename std::enable_if<std::tuple_size<typename App::mw_container_t>::value == 0, void>::type
|
||||
handle_rule(BaseRule* rule, crow::request& req, crow::response& res, const crow::routing_params& rp)
|
||||
{
|
||||
rule->handle(req, res, rp);
|
||||
}
|
||||
|
||||
void debug_print()
|
||||
{
|
||||
for (int i = 0; i < static_cast<int>(HTTPMethod::InternalMethodCount); i++)
|
||||
|
@ -59,6 +59,6 @@
|
||||
#if __cplusplus > 201103L
|
||||
#define CROW_GCC83_WORKAROUND
|
||||
#else
|
||||
#error "GCC 8.1 - 8.3 has a bug that prevents crow from compiling with C++11. Please update GCC to > 8.3 or use C++ > 11."
|
||||
#error "GCC 8.1 - 8.3 has a bug that prevents Crow from compiling with C++11. Please update GCC to > 8.3 or use C++ > 11."
|
||||
#endif
|
||||
#endif
|
||||
|
@ -233,6 +233,8 @@ namespace crow
|
||||
template<template<typename... Args> class U>
|
||||
using rebind = U<T...>;
|
||||
};
|
||||
|
||||
// Check whether the template function can be called with specific arguments
|
||||
template<typename F, typename Set>
|
||||
struct CallHelper;
|
||||
template<typename F, typename... Args>
|
||||
@ -263,22 +265,6 @@ namespace crow
|
||||
struct has_type<T, std::tuple<T, Ts...>> : std::true_type
|
||||
{};
|
||||
|
||||
// Find index of type in tuple
|
||||
template<class T, class Tuple>
|
||||
struct tuple_index;
|
||||
|
||||
template<class T, class... Types>
|
||||
struct tuple_index<T, std::tuple<T, Types...>>
|
||||
{
|
||||
static const int value = 0;
|
||||
};
|
||||
|
||||
template<class T, class U, class... Types>
|
||||
struct tuple_index<T, std::tuple<U, Types...>>
|
||||
{
|
||||
static const int value = 1 + tuple_index<T, std::tuple<Types...>>::value;
|
||||
};
|
||||
|
||||
// Extract element from forward tuple or get default
|
||||
#ifdef CROW_CAN_USE_CPP14
|
||||
template<typename T, typename Tup>
|
||||
@ -302,18 +288,21 @@ namespace crow
|
||||
{
|
||||
return T{};
|
||||
}
|
||||
|
||||
// Find index of type in tuple
|
||||
template<class T, class Tuple>
|
||||
struct tuple_index;
|
||||
|
||||
// Check F is callable with Args
|
||||
template<typename F, typename... Args>
|
||||
struct is_callable
|
||||
template<class T, class... Types>
|
||||
struct tuple_index<T, std::tuple<T, Types...>>
|
||||
{
|
||||
template<typename F2, typename... Args2>
|
||||
static std::true_type __test(decltype(std::declval<F2>()(std::declval<Args2>()...))*);
|
||||
static const int value = 0;
|
||||
};
|
||||
|
||||
template<typename F2, typename... Args2>
|
||||
static std::false_type __test(...);
|
||||
|
||||
static constexpr bool value = decltype(__test<F, Args...>(nullptr))::value;
|
||||
template<class T, class U, class... Types>
|
||||
struct tuple_index<T, std::tuple<U, Types...>>
|
||||
{
|
||||
static const int value = 1 + tuple_index<T, std::tuple<Types...>>::value;
|
||||
};
|
||||
|
||||
// Kind of fold expressions in C++11
|
||||
|
@ -1,6 +1,7 @@
|
||||
#pragma once
|
||||
#include <boost/algorithm/string/predicate.hpp>
|
||||
#include <boost/array.hpp>
|
||||
#include "crow/logging.h"
|
||||
#include "crow/socket_adaptors.h"
|
||||
#include "crow/http_request.h"
|
||||
#include "crow/TinySHA1.hpp"
|
||||
@ -60,7 +61,8 @@ namespace crow
|
||||
//
|
||||
|
||||
/// A websocket connection.
|
||||
template<typename Adaptor>
|
||||
|
||||
template<typename Adaptor, typename Handler>
|
||||
class Connection : public connection
|
||||
{
|
||||
public:
|
||||
@ -69,18 +71,25 @@ namespace crow
|
||||
///
|
||||
/// Requires a request with an "Upgrade: websocket" header.<br>
|
||||
/// Automatically handles the handshake.
|
||||
Connection(const crow::request& req, Adaptor&& adaptor,
|
||||
Connection(const crow::request& req, Adaptor&& adaptor, Handler* handler, uint64_t max_payload,
|
||||
std::function<void(crow::websocket::connection&)> open_handler,
|
||||
std::function<void(crow::websocket::connection&, const std::string&, bool)> message_handler,
|
||||
std::function<void(crow::websocket::connection&, const std::string&)> close_handler,
|
||||
std::function<void(crow::websocket::connection&)> error_handler,
|
||||
std::function<bool(const crow::request&)> accept_handler):
|
||||
adaptor_(std::move(adaptor)),
|
||||
open_handler_(std::move(open_handler)), message_handler_(std::move(message_handler)), close_handler_(std::move(close_handler)), error_handler_(std::move(error_handler)), accept_handler_(std::move(accept_handler))
|
||||
handler_(handler),
|
||||
max_payload_bytes_(max_payload),
|
||||
open_handler_(std::move(open_handler)),
|
||||
message_handler_(std::move(message_handler)),
|
||||
close_handler_(std::move(close_handler)),
|
||||
error_handler_(std::move(error_handler)),
|
||||
accept_handler_(std::move(accept_handler))
|
||||
{
|
||||
if (!boost::iequals(req.get_header_value("upgrade"), "websocket"))
|
||||
{
|
||||
adaptor.close();
|
||||
handler_->remove_websocket(this);
|
||||
delete this;
|
||||
return;
|
||||
}
|
||||
@ -90,6 +99,7 @@ namespace crow
|
||||
if (!accept_handler_(req))
|
||||
{
|
||||
adaptor.close();
|
||||
handler_->remove_websocket(this);
|
||||
delete this;
|
||||
return;
|
||||
}
|
||||
@ -102,6 +112,7 @@ namespace crow
|
||||
s.processBytes(magic.data(), magic.size());
|
||||
uint8_t digest[20];
|
||||
s.getDigestBytes(digest);
|
||||
|
||||
start(crow::utility::base64encode((unsigned char*)digest, 20));
|
||||
}
|
||||
|
||||
@ -195,6 +206,11 @@ namespace crow
|
||||
return adaptor_.remote_endpoint().address().to_string();
|
||||
}
|
||||
|
||||
void set_max_payload_size(uint64_t payload)
|
||||
{
|
||||
max_payload_bytes_ = payload;
|
||||
}
|
||||
|
||||
protected:
|
||||
/// Generate the websocket headers using an opcode and the message size (in bytes).
|
||||
std::string build_header(int opcode, size_t size)
|
||||
@ -285,6 +301,7 @@ namespace crow
|
||||
has_mask_ = false;
|
||||
#else
|
||||
close_connection_ = true;
|
||||
adaptor_.shutdown_readwrite();
|
||||
adaptor_.close();
|
||||
if (error_handler_)
|
||||
error_handler_(*this);
|
||||
@ -310,6 +327,7 @@ namespace crow
|
||||
else
|
||||
{
|
||||
close_connection_ = true;
|
||||
adaptor_.shutdown_readwrite();
|
||||
adaptor_.close();
|
||||
if (error_handler_)
|
||||
error_handler_(*this);
|
||||
@ -347,6 +365,7 @@ namespace crow
|
||||
else
|
||||
{
|
||||
close_connection_ = true;
|
||||
adaptor_.shutdown_readwrite();
|
||||
adaptor_.close();
|
||||
if (error_handler_)
|
||||
error_handler_(*this);
|
||||
@ -381,6 +400,7 @@ namespace crow
|
||||
else
|
||||
{
|
||||
close_connection_ = true;
|
||||
adaptor_.shutdown_readwrite();
|
||||
adaptor_.close();
|
||||
if (error_handler_)
|
||||
error_handler_(*this);
|
||||
@ -390,7 +410,15 @@ namespace crow
|
||||
}
|
||||
break;
|
||||
case WebSocketReadState::Mask:
|
||||
if (has_mask_)
|
||||
if (remaining_length_ > max_payload_bytes_)
|
||||
{
|
||||
close_connection_ = true;
|
||||
adaptor_.close();
|
||||
if (error_handler_)
|
||||
error_handler_(*this);
|
||||
check_destroy();
|
||||
}
|
||||
else if (has_mask_)
|
||||
{
|
||||
boost::asio::async_read(
|
||||
adaptor_.socket(), boost::asio::buffer((char*)&mask_, 4),
|
||||
@ -417,7 +445,9 @@ namespace crow
|
||||
close_connection_ = true;
|
||||
if (error_handler_)
|
||||
error_handler_(*this);
|
||||
adaptor_.shutdown_readwrite();
|
||||
adaptor_.close();
|
||||
check_destroy();
|
||||
}
|
||||
});
|
||||
}
|
||||
@ -455,7 +485,9 @@ namespace crow
|
||||
close_connection_ = true;
|
||||
if (error_handler_)
|
||||
error_handler_(*this);
|
||||
adaptor_.shutdown_readwrite();
|
||||
adaptor_.close();
|
||||
check_destroy();
|
||||
}
|
||||
});
|
||||
}
|
||||
@ -534,6 +566,7 @@ namespace crow
|
||||
}
|
||||
else
|
||||
{
|
||||
adaptor_.shutdown_readwrite();
|
||||
adaptor_.close();
|
||||
close_connection_ = true;
|
||||
if (!is_close_handler_called_)
|
||||
@ -603,12 +636,14 @@ namespace crow
|
||||
if (!is_close_handler_called_)
|
||||
if (close_handler_)
|
||||
close_handler_(*this, "uncleanly");
|
||||
handler_->remove_websocket(this);
|
||||
if (sending_buffers_.empty() && !is_reading)
|
||||
delete this;
|
||||
}
|
||||
|
||||
private:
|
||||
Adaptor adaptor_;
|
||||
Handler* handler_;
|
||||
|
||||
std::vector<std::string> sending_buffers_;
|
||||
std::vector<std::string> write_buffers_;
|
||||
@ -620,6 +655,7 @@ namespace crow
|
||||
WebSocketReadState state_{WebSocketReadState::MiniHeader};
|
||||
uint16_t remaining_length16_{0};
|
||||
uint64_t remaining_length_{0};
|
||||
uint64_t max_payload_bytes_{UINT64_MAX};
|
||||
bool close_connection_{false};
|
||||
bool is_reading{false};
|
||||
bool has_mask_{false};
|
||||
|
154
mkdocs.yml
154
mkdocs.yml
@ -7,81 +7,103 @@ site_url: https://crowcpp.org
|
||||
edit_uri: ""
|
||||
|
||||
theme:
|
||||
name: material
|
||||
font: false
|
||||
language: 'en'
|
||||
features:
|
||||
navigation.tabs
|
||||
favicon: 'assets/favicon.svg'
|
||||
logo: 'assets/favicon.svg'
|
||||
icon:
|
||||
repo: fontawesome/brands/github-square
|
||||
static_templates:
|
||||
- privacy_policy.html
|
||||
custom_dir: docs/overrides
|
||||
name: material
|
||||
font: false
|
||||
language: 'en'
|
||||
features:
|
||||
navigation.tabs
|
||||
favicon: 'assets/favicon.svg'
|
||||
logo: 'assets/favicon.svg'
|
||||
icon:
|
||||
repo: fontawesome/brands/github-square
|
||||
static_templates:
|
||||
- privacy_policy.html
|
||||
palette:
|
||||
- media: "(prefers-color-scheme: light)"
|
||||
scheme: crow-light
|
||||
toggle:
|
||||
icon: fontawesome/solid/sun
|
||||
name: Using Light Theme
|
||||
- media: "(prefers-color-scheme: dark)"
|
||||
scheme: crow-dark
|
||||
toggle:
|
||||
icon: fontawesome/solid/moon
|
||||
name: Using Dark Theme
|
||||
custom_dir: docs/overrides
|
||||
|
||||
markdown_extensions:
|
||||
- admonition
|
||||
- pymdownx.highlight
|
||||
- pymdownx.tabbed
|
||||
- pymdownx.superfences
|
||||
- pymdownx.inlinehilite
|
||||
- pymdownx.keys
|
||||
- toc:
|
||||
permalink: true
|
||||
- admonition
|
||||
- pymdownx.highlight
|
||||
- pymdownx.tabbed
|
||||
- pymdownx.superfences
|
||||
- pymdownx.inlinehilite
|
||||
- pymdownx.keys
|
||||
- attr_list
|
||||
- pymdownx.emoji:
|
||||
emoji_index: !!python/name:materialx.emoji.twemoji
|
||||
emoji_generator: !!python/name:materialx.emoji.to_svg
|
||||
|
||||
|
||||
nav:
|
||||
- Home: index.md
|
||||
- Getting Started:
|
||||
- Setup:
|
||||
- Linux: getting_started/setup/linux.md
|
||||
- MacOS: getting_started/setup/macos.md
|
||||
- Windows: getting_started/setup/windows.md
|
||||
- Your First Application: getting_started/your_first_application.md
|
||||
- Guides:
|
||||
- Different parts of Crow:
|
||||
- App: guides/app.md
|
||||
- Routes: guides/routes.md
|
||||
- Logging: guides/logging.md
|
||||
- JSON: guides/json.md
|
||||
- Templating (Mustache): guides/templating.md
|
||||
- Multipart: guides/multipart.md
|
||||
- Query Strings: guides/query-string.md
|
||||
- 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
|
||||
- Base64: guides/base64.md
|
||||
- Writing Tests: guides/testing.md
|
||||
- Using Crow:
|
||||
- HTTP Authorization: guides/auth.md
|
||||
- CORS Setup: guides/CORS.md
|
||||
- Server setup:
|
||||
- Proxies: guides/proxies.md
|
||||
- Systemd run on startup: guides/syste.md
|
||||
- API Reference:
|
||||
- API Reference: 'reference/index.html'
|
||||
- Home: index.md
|
||||
- Getting Started:
|
||||
- Setup:
|
||||
- Linux: getting_started/setup/linux.md
|
||||
- MacOS: getting_started/setup/macos.md
|
||||
- Windows: getting_started/setup/windows.md
|
||||
- Your First Application: getting_started/your_first_application.md
|
||||
- Guides:
|
||||
- Different parts of Crow:
|
||||
- App: guides/app.md
|
||||
- Routes: guides/routes.md
|
||||
- Logging: guides/logging.md
|
||||
- JSON: guides/json.md
|
||||
- Templating (Mustache): guides/templating.md
|
||||
- Multipart: guides/multipart.md
|
||||
- Query Strings: guides/query-string.md
|
||||
- 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
|
||||
- Base64: guides/base64.md
|
||||
- Writing Tests: guides/testing.md
|
||||
- Using Crow:
|
||||
- HTTP Authorization: guides/auth.md
|
||||
- Included Middlewares: guides/included-middleware.md
|
||||
- Server setup:
|
||||
- Proxies: guides/proxies.md
|
||||
- Systemd run on startup: guides/syste.md
|
||||
- API Reference:
|
||||
- API Reference: 'reference/index.html'
|
||||
|
||||
extra:
|
||||
analytics:
|
||||
provider: matomo
|
||||
id: 1
|
||||
link: //thee.dev/matomo/
|
||||
social:
|
||||
- icon: fontawesome/brands/github
|
||||
link: https://github.com/crowcpp/crow
|
||||
- icon: fontawesome/brands/gitter
|
||||
link: https://gitter.im/crowfork/community
|
||||
version:
|
||||
provider: mike
|
||||
default: latest
|
||||
analytics:
|
||||
provider: matomo
|
||||
id: 1
|
||||
link: //thee.dev/matomo/
|
||||
social:
|
||||
- icon: fontawesome/brands/github
|
||||
link: https://github.com/crowcpp/crow
|
||||
- icon: fontawesome/brands/gitter
|
||||
link: https://gitter.im/crowfork/community
|
||||
|
||||
plugins:
|
||||
- redirects:
|
||||
redirect_maps:
|
||||
'getting_started/setup/': 'getting_started/setup/linux.md'
|
||||
- meta-descriptions
|
||||
- redirects:
|
||||
redirect_maps:
|
||||
'getting_started/setup/': 'getting_started/setup/linux.md'
|
||||
- meta-descriptions
|
||||
- search
|
||||
|
||||
extra_css:
|
||||
- 'stylesheets/colors.css'
|
||||
- 'stylesheets/latofonts.css'
|
||||
- 'stylesheets/extra.css'
|
||||
- 'stylesheets/colors.css'
|
||||
- 'stylesheets/latofonts.css'
|
||||
- 'stylesheets/extra.css'
|
||||
|
||||
copyright: 'Copyright © 2020-2022 CrowCpp'
|
||||
|
45
scripts/generateDocumentationAndDeploy.sh
Normal file → Executable file
45
scripts/generateDocumentationAndDeploy.sh
Normal file → Executable file
@ -2,14 +2,14 @@
|
||||
################################################################################
|
||||
# Title : generateDocumentationAndDeploy.sh
|
||||
# Date created : 2016/02/22
|
||||
# Notes : This script was modified to suit Crow and work with mkdocs and Drone.io CI.
|
||||
# Notes : This script was modified to suit Crow and work with mkdocs (multiple versions) and Drone.io CI.
|
||||
__AUTHOR__="Jeroen de Bruijn"
|
||||
# Preconditions:
|
||||
# - Packages doxygen doxygen-doc doxygen-latex doxygen-gui graphviz
|
||||
# must be installed.
|
||||
# - Doxygen configuration file must have the destination directory empty and
|
||||
# source code directory with a $(TRAVIS_BUILD_DIR) prefix.
|
||||
# - An gh-pages branch should already exist. See below for mor info on hoe to
|
||||
# - A gh-pages branch should already exist. See below for more info on how to
|
||||
# create a gh-pages branch.
|
||||
#
|
||||
# Required global variables:
|
||||
@ -19,6 +19,7 @@ __AUTHOR__="Jeroen de Bruijn"
|
||||
# - GH_REPO_NAME : The name of the repository.
|
||||
# - GH_REPO_REF : The GitHub reference to the repository.
|
||||
# - GH_REPO_TOKEN : Secure token to the github repository.
|
||||
# - DOCS_VERSION : Used for custom version documentation (defaults to master).
|
||||
#
|
||||
# For information on how to encrypt variables for Travis CI please go to
|
||||
# https://docs.travis-ci.com/user/environment-variables/#Encrypted-Variables
|
||||
@ -30,15 +31,25 @@ __AUTHOR__="Jeroen de Bruijn"
|
||||
# the gh-pages branch of a repository specified by GH_REPO_REF.
|
||||
# Before this script is used there should already be a gh-pages branch in the
|
||||
# repository.
|
||||
#
|
||||
# The branch should be empty except for an empty '.nojekyll' file and an
|
||||
# 'index.html' file that redirects to master/index.html. Optionally you would
|
||||
# also need a 'CNAME' file containing your custom domain (without http or www).
|
||||
#
|
||||
################################################################################
|
||||
################################################################################
|
||||
|
||||
|
||||
################################################################################
|
||||
##### Setup this script and get the current gh-pages branch. #####
|
||||
##### Setup this script and get the current gh-pages branch. #####
|
||||
|
||||
echo 'Setting up the script...'
|
||||
# Exit with nonzero exit code if anything fails
|
||||
set -e
|
||||
|
||||
DOCS_VERSION=${DOCS_VERSION:-"master"}
|
||||
echo "Using $DOCS_VERSION as version."
|
||||
|
||||
# Create a clean working directory for this script.
|
||||
mkdir code_docs
|
||||
cd code_docs
|
||||
@ -55,29 +66,38 @@ git config --global push.default simple
|
||||
git config user.name "Drone CI"
|
||||
git config user.email "drone@drone.io"
|
||||
|
||||
# Remove everything currently in the gh-pages branch.
|
||||
# Create a new directory for this version of the docs if one doesn't exist.
|
||||
# Then navigate to that directory.
|
||||
mkdir -p $DOCS_VERSION
|
||||
cd $DOCS_VERSION
|
||||
|
||||
################################################################################
|
||||
##### Generate the MkDocs documentation to replace the old docs. #####
|
||||
|
||||
echo 'Removing old documentation...'
|
||||
# Remove everything currently in the version directory.
|
||||
# GitHub is smart enough to know which files have changed and which files have
|
||||
# stayed the same and will only update the changed files. So the gh-pages branch
|
||||
# can be safely cleaned, and it is sure that everything pushed later is the new
|
||||
# documentation.
|
||||
cp CNAME ..
|
||||
rm -rf *
|
||||
mv ../CNAME .
|
||||
|
||||
# Copy the mkdocs documentation to the work directory and generate the mkdocs
|
||||
echo 'Generating MkDocs documentation...'
|
||||
# Copy the mkdocs documentation to the work directory and generate the mkdocs'
|
||||
# 'site' directory
|
||||
cp ../../mkdocs.yml .
|
||||
cp -r ../../docs .
|
||||
cp ../../../mkdocs.yml .
|
||||
cp -r ../../../docs .
|
||||
mkdocs build -v
|
||||
|
||||
# Need to create a .nojekyll file to allow filenames starting with an underscore
|
||||
# to be seen on the gh-pages site. Therefore creating an empty .nojekyll file.
|
||||
# Presumably this is only needed when the SHORT_NAMES option in Doxygen is set
|
||||
# to NO, which it is by default. So creating the file just in case.
|
||||
echo "" > .nojekyll
|
||||
#echo "" > .nojekyll
|
||||
|
||||
################################################################################
|
||||
##### Generate the Doxygen code documentation and log the output. #####
|
||||
##### Generate the Doxygen code documentation and log the output. #####
|
||||
|
||||
echo 'Generating Doxygen code documentation...'
|
||||
# Redirect both stderr and stdout to the log file AND the console.
|
||||
doxygen $DOXYFILE 2>&1 | tee doxygen.log
|
||||
@ -89,9 +109,10 @@ mv site/* .
|
||||
rm -r site
|
||||
rm mkdocs.yml
|
||||
rm -r docs
|
||||
mv versions.json ../
|
||||
|
||||
################################################################################
|
||||
##### Upload the documentation to the gh-pages branch of the repository. #####
|
||||
##### Upload the documentation to the gh-pages branch of the repository. #####
|
||||
# Only upload if Doxygen successfully created the documentation.
|
||||
# Check this by verifying that the reference directory (for doxygen) and
|
||||
# the file index.html (for mkdocs) both exist.
|
||||
|
@ -44,7 +44,8 @@ else:
|
||||
middlewares_actual = middlewares
|
||||
print("Middlewares: " + str(middlewares_actual))
|
||||
|
||||
re_depends = re.compile('^#include "(.*)"', re.MULTILINE)
|
||||
re_depends = re.compile('^#include \"(.*)\"\n', re.MULTILINE)
|
||||
re_pragma = re.compile('^(.*)#pragma once(.*)\n', re.MULTILINE)
|
||||
headers = [x.rsplit(sep, 1)[-1] for x in glob(pt.join(header_path, '*.h*'))]
|
||||
headers += ['crow'+sep + x.rsplit(sep, 1)[-1] for x in glob(pt.join(header_path, 'crow'+sep+'*.h*'))]
|
||||
headers += [('crow'+sep+'middlewares'+sep + x + '.h') for x in middlewares_actual]
|
||||
@ -85,10 +86,12 @@ for x in edges:
|
||||
assert order.index(x) < order.index(y), 'cyclic include detected'
|
||||
|
||||
print(order)
|
||||
build = [lsc]
|
||||
build = [lsc, '#pragma once']
|
||||
for header in order:
|
||||
d = open(pt.join(header_path, header)).read()
|
||||
build.append(re_depends.sub(lambda x: '\n', d))
|
||||
build.append('\n')
|
||||
d_no_depend = re_depends.sub(lambda x: '', d)
|
||||
d_no_pragma = re_pragma.sub(lambda x: '', d_no_depend)
|
||||
build.append(d_no_pragma)
|
||||
#build.append('\n')
|
||||
|
||||
open(output_path, 'w').write('\n'.join(build))
|
||||
|
1
tests/img/filewith.badext
Normal file
1
tests/img/filewith.badext
Normal file
@ -0,0 +1 @@
|
||||
Test file with a strange extension.
|
@ -55,6 +55,7 @@ TEST_CASE("SSL")
|
||||
|
||||
size_t x = 0;
|
||||
size_t y = 0;
|
||||
long z = 0;
|
||||
|
||||
while (x < 121)
|
||||
{
|
||||
@ -63,7 +64,20 @@ TEST_CASE("SSL")
|
||||
buf[y] = '\0';
|
||||
}
|
||||
|
||||
CHECK(std::string("Hello world, I'm keycrt.") == std::string(buf));
|
||||
std::string to_test(buf);
|
||||
|
||||
if ((z = to_test.length() - 24) >= 0)
|
||||
{
|
||||
|
||||
CHECK(std::string("Hello world, I'm keycrt.") == to_test.substr(z));
|
||||
}
|
||||
else
|
||||
{
|
||||
CHECK(std::string("Hello world, I'm keycrt.").substr((z * -1)) == to_test);
|
||||
}
|
||||
|
||||
boost::system::error_code ec;
|
||||
c.lowest_layer().shutdown(boost::asio::socket_base::shutdown_type::shutdown_both, ec);
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -8,6 +8,7 @@
|
||||
#include <vector>
|
||||
#include <thread>
|
||||
#include <chrono>
|
||||
#include <type_traits>
|
||||
|
||||
#include "catch.hpp"
|
||||
#include "crow.h"
|
||||
@ -571,6 +572,61 @@ TEST_CASE("multi_server")
|
||||
app2.stop();
|
||||
} // multi_server
|
||||
|
||||
|
||||
TEST_CASE("undefined_status_code")
|
||||
{
|
||||
SimpleApp app;
|
||||
CROW_ROUTE(app, "/get123")
|
||||
([] {
|
||||
//this status does not exists statusCodes map defined in include/crow/http_connection.h
|
||||
const int undefinedStatusCode = 123;
|
||||
return response(undefinedStatusCode, "this should return 500");
|
||||
});
|
||||
|
||||
CROW_ROUTE(app, "/get200")
|
||||
([] {
|
||||
return response(200, "ok");
|
||||
});
|
||||
|
||||
auto _ = app.bindaddr(LOCALHOST_ADDRESS).port(45471).run_async();
|
||||
app.wait_for_server_start();
|
||||
|
||||
asio::io_service is;
|
||||
auto sendRequestAndGetStatusCode = [&](const std::string& route) -> unsigned {
|
||||
asio::ip::tcp::socket socket(is);
|
||||
socket.connect(asio::ip::tcp::endpoint(asio::ip::address::from_string(LOCALHOST_ADDRESS), app.port()));
|
||||
|
||||
boost::asio::streambuf request;
|
||||
std::ostream request_stream(&request);
|
||||
request_stream << "GET " << route << " HTTP/1.0\r\n";
|
||||
request_stream << "Host: " << LOCALHOST_ADDRESS << "\r\n";
|
||||
request_stream << "Accept: */*\r\n";
|
||||
request_stream << "Connection: close\r\n\r\n";
|
||||
|
||||
// Send the request.
|
||||
boost::asio::write(socket, request);
|
||||
|
||||
boost::asio::streambuf response;
|
||||
boost::asio::read_until(socket, response, "\r\n");
|
||||
|
||||
std::istream response_stream(&response);
|
||||
std::string http_version;
|
||||
response_stream >> http_version;
|
||||
unsigned status_code = 0;
|
||||
response_stream >> status_code;
|
||||
|
||||
return status_code;
|
||||
};
|
||||
|
||||
unsigned statusCode = sendRequestAndGetStatusCode("/get200");
|
||||
CHECK(statusCode == 200);
|
||||
|
||||
statusCode = sendRequestAndGetStatusCode("/get123");
|
||||
CHECK(statusCode == 500);
|
||||
|
||||
app.stop();
|
||||
} // undefined_status_code
|
||||
|
||||
TEST_CASE("json_read")
|
||||
{
|
||||
{
|
||||
@ -1253,7 +1309,11 @@ struct IntSettingMiddleware
|
||||
|
||||
std::vector<std::string> test_middleware_context_vector;
|
||||
|
||||
struct FirstMW
|
||||
struct empty_type
|
||||
{};
|
||||
|
||||
template<bool Local>
|
||||
struct FirstMW : public std::conditional<Local, crow::ILocalMiddleware, empty_type>::type
|
||||
{
|
||||
struct context
|
||||
{
|
||||
@ -1272,38 +1332,40 @@ struct FirstMW
|
||||
}
|
||||
};
|
||||
|
||||
struct SecondMW
|
||||
template<bool Local>
|
||||
struct SecondMW : public std::conditional<Local, crow::ILocalMiddleware, empty_type>::type
|
||||
{
|
||||
struct context
|
||||
{};
|
||||
template<typename AllContext>
|
||||
void before_handle(request& req, response& res, context&, AllContext& all_ctx)
|
||||
{
|
||||
all_ctx.template get<FirstMW>().v.push_back("2 before");
|
||||
if (req.url == "/break") res.end();
|
||||
all_ctx.template get<FirstMW<Local>>().v.push_back("2 before");
|
||||
if (req.url.find("/break") != std::string::npos) res.end();
|
||||
}
|
||||
|
||||
template<typename AllContext>
|
||||
void after_handle(request&, response&, context&, AllContext& all_ctx)
|
||||
{
|
||||
all_ctx.template get<FirstMW>().v.push_back("2 after");
|
||||
all_ctx.template get<FirstMW<Local>>().v.push_back("2 after");
|
||||
}
|
||||
};
|
||||
|
||||
struct ThirdMW
|
||||
template<bool Local>
|
||||
struct ThirdMW : public std::conditional<Local, crow::ILocalMiddleware, empty_type>::type
|
||||
{
|
||||
struct context
|
||||
{};
|
||||
template<typename AllContext>
|
||||
void before_handle(request&, response&, context&, AllContext& all_ctx)
|
||||
{
|
||||
all_ctx.template get<FirstMW>().v.push_back("3 before");
|
||||
all_ctx.template get<FirstMW<Local>>().v.push_back("3 before");
|
||||
}
|
||||
|
||||
template<typename AllContext>
|
||||
void after_handle(request&, response&, context&, AllContext& all_ctx)
|
||||
{
|
||||
all_ctx.template get<FirstMW>().v.push_back("3 after");
|
||||
all_ctx.template get<FirstMW<Local>>().v.push_back("3 after");
|
||||
}
|
||||
};
|
||||
|
||||
@ -1316,7 +1378,7 @@ TEST_CASE("middleware_context")
|
||||
// or change the order of FirstMW and SecondMW
|
||||
// App<IntSettingMiddleware, SecondMW, FirstMW> app;
|
||||
|
||||
App<IntSettingMiddleware, FirstMW, SecondMW, ThirdMW> app;
|
||||
App<IntSettingMiddleware, FirstMW<false>, SecondMW<false>, ThirdMW<false>> app;
|
||||
|
||||
int x{};
|
||||
CROW_ROUTE(app, "/")
|
||||
@ -1326,7 +1388,7 @@ TEST_CASE("middleware_context")
|
||||
x = ctx.val;
|
||||
}
|
||||
{
|
||||
auto& ctx = app.get_context<FirstMW>(req);
|
||||
auto& ctx = app.get_context<FirstMW<false>>(req);
|
||||
ctx.v.push_back("handle");
|
||||
}
|
||||
|
||||
@ -1335,7 +1397,7 @@ TEST_CASE("middleware_context")
|
||||
CROW_ROUTE(app, "/break")
|
||||
([&](const request& req) {
|
||||
{
|
||||
auto& ctx = app.get_context<FirstMW>(req);
|
||||
auto& ctx = app.get_context<FirstMW<false>>(req);
|
||||
ctx.v.push_back("handle");
|
||||
}
|
||||
|
||||
@ -1465,7 +1527,92 @@ TEST_CASE("app_constructor")
|
||||
app(OnlyMoveConstructor(1), SecondMW{});
|
||||
} // app_constructor
|
||||
|
||||
TEST_CASE("middleware_cookieparser")
|
||||
TEST_CASE("middleware_blueprint")
|
||||
{
|
||||
// Same logic as middleware_context, but middleware is added with blueprints
|
||||
static char buf[2048];
|
||||
|
||||
App<FirstMW<true>, SecondMW<true>, ThirdMW<true>> app;
|
||||
|
||||
Blueprint bp1("a", "c1", "c1");
|
||||
bp1.CROW_MIDDLEWARES(app, FirstMW<true>);
|
||||
|
||||
Blueprint bp2("b", "c2", "c2");
|
||||
bp2.CROW_MIDDLEWARES(app, SecondMW<true>);
|
||||
|
||||
Blueprint bp3("c", "c3", "c3");
|
||||
|
||||
CROW_BP_ROUTE(bp2, "/")
|
||||
.CROW_MIDDLEWARES(app, ThirdMW<true>)([&app](const crow::request& req) {
|
||||
{
|
||||
auto& ctx = app.get_context<FirstMW<true>>(req);
|
||||
ctx.v.push_back("handle");
|
||||
}
|
||||
return "";
|
||||
});
|
||||
|
||||
CROW_BP_ROUTE(bp3, "/break")
|
||||
.CROW_MIDDLEWARES(app, SecondMW<true>, ThirdMW<true>)([&app](const crow::request& req) {
|
||||
{
|
||||
auto& ctx = app.get_context<FirstMW<true>>(req);
|
||||
ctx.v.push_back("handle");
|
||||
}
|
||||
return "";
|
||||
});
|
||||
|
||||
bp1.register_blueprint(bp3);
|
||||
bp1.register_blueprint(bp2);
|
||||
app.register_blueprint(bp1);
|
||||
|
||||
auto _ = app.bindaddr(LOCALHOST_ADDRESS).port(45451).run_async();
|
||||
app.wait_for_server_start();
|
||||
|
||||
asio::io_service is;
|
||||
{
|
||||
asio::ip::tcp::socket c(is);
|
||||
c.connect(asio::ip::tcp::endpoint(
|
||||
asio::ip::address::from_string(LOCALHOST_ADDRESS), 45451));
|
||||
|
||||
c.send(asio::buffer("GET /a/b/\r\n\r\n"));
|
||||
|
||||
c.receive(asio::buffer(buf, 2048));
|
||||
c.close();
|
||||
}
|
||||
{
|
||||
auto& out = test_middleware_context_vector;
|
||||
CHECK(7 == out.size());
|
||||
CHECK("1 before" == out[0]);
|
||||
CHECK("2 before" == out[1]);
|
||||
CHECK("3 before" == out[2]);
|
||||
CHECK("handle" == out[3]);
|
||||
CHECK("3 after" == out[4]);
|
||||
CHECK("2 after" == out[5]);
|
||||
CHECK("1 after" == out[6]);
|
||||
}
|
||||
{
|
||||
asio::ip::tcp::socket c(is);
|
||||
c.connect(asio::ip::tcp::endpoint(
|
||||
asio::ip::address::from_string(LOCALHOST_ADDRESS), 45451));
|
||||
|
||||
c.send(asio::buffer("GET /a/c/break\r\n\r\n"));
|
||||
|
||||
c.receive(asio::buffer(buf, 2048));
|
||||
c.close();
|
||||
}
|
||||
{
|
||||
auto& out = test_middleware_context_vector;
|
||||
CHECK(4 == out.size());
|
||||
CHECK("1 before" == out[0]);
|
||||
CHECK("2 before" == out[1]);
|
||||
CHECK("2 after" == out[2]);
|
||||
CHECK("1 after" == out[3]);
|
||||
}
|
||||
|
||||
app.stop();
|
||||
} // middleware_blueprint
|
||||
|
||||
|
||||
TEST_CASE("middleware_cookieparser_parse")
|
||||
{
|
||||
static char buf[2048];
|
||||
|
||||
@ -1512,9 +1659,56 @@ TEST_CASE("middleware_cookieparser")
|
||||
CHECK("val\"ue4" == value4);
|
||||
}
|
||||
app.stop();
|
||||
} // middleware_cookieparser
|
||||
} // middleware_cookieparser_parse
|
||||
|
||||
|
||||
TEST_CASE("middleware_cookieparser_format")
|
||||
{
|
||||
using Cookie = CookieParser::Cookie;
|
||||
|
||||
auto valid = [](const std::string& s, int parts) {
|
||||
return std::count(s.begin(), s.end(), ';') == parts - 1;
|
||||
};
|
||||
|
||||
// basic
|
||||
{
|
||||
auto c = Cookie("key", "value");
|
||||
auto s = c.dump();
|
||||
CHECK(valid(s, 1));
|
||||
CHECK(s == "key=value");
|
||||
}
|
||||
// max-age + domain
|
||||
{
|
||||
auto c = Cookie("key", "value")
|
||||
.max_age(123)
|
||||
.domain("example.com");
|
||||
auto s = c.dump();
|
||||
CHECK(valid(s, 3));
|
||||
CHECK(s.find("key=value") != std::string::npos);
|
||||
CHECK(s.find("Max-Age=123") != std::string::npos);
|
||||
CHECK(s.find("Domain=example.com") != std::string::npos);
|
||||
}
|
||||
// samesite + secure
|
||||
{
|
||||
auto c = Cookie("key", "value")
|
||||
.secure()
|
||||
.same_site(Cookie::SameSitePolicy::None);
|
||||
auto s = c.dump();
|
||||
CHECK(valid(s, 3));
|
||||
CHECK(s.find("Secure") != std::string::npos);
|
||||
CHECK(s.find("SameSite=None") != std::string::npos);
|
||||
}
|
||||
// expires
|
||||
{
|
||||
auto tp = boost::posix_time::time_from_string("2000-11-01 23:59:59.000");
|
||||
auto c = Cookie("key", "value")
|
||||
.expires(boost::posix_time::to_tm(tp));
|
||||
auto s = c.dump();
|
||||
CHECK(valid(s, 2));
|
||||
CHECK(s.find("Expires=Wed, 01 Nov 2000 23:59:59 GMT") != std::string::npos);
|
||||
}
|
||||
} // middleware_cookieparser_format
|
||||
|
||||
TEST_CASE("middleware_cors")
|
||||
{
|
||||
static char buf[5012];
|
||||
@ -1939,8 +2133,10 @@ TEST_CASE("multipart")
|
||||
TEST_CASE("send_file")
|
||||
{
|
||||
|
||||
struct stat statbuf;
|
||||
stat("tests/img/cat.jpg", &statbuf);
|
||||
struct stat statbuf_cat;
|
||||
stat("tests/img/cat.jpg", &statbuf_cat);
|
||||
struct stat statbuf_badext;
|
||||
stat("tests/img/filewith.badext", &statbuf_badext);
|
||||
|
||||
SimpleApp app;
|
||||
|
||||
@ -1957,6 +2153,12 @@ TEST_CASE("send_file")
|
||||
res.end();
|
||||
});
|
||||
|
||||
CROW_ROUTE(app, "/filewith.badext")
|
||||
([](const crow::request&, crow::response& res) {
|
||||
res.set_static_file_info("tests/img/filewith.badext");
|
||||
res.end();
|
||||
});
|
||||
|
||||
app.validate();
|
||||
|
||||
//File not found check
|
||||
@ -1983,8 +2185,33 @@ TEST_CASE("send_file")
|
||||
app.handle(req, res);
|
||||
|
||||
CHECK(200 == res.code);
|
||||
CHECK("image/jpeg" == res.headers.find("Content-Type")->second);
|
||||
CHECK(to_string(statbuf.st_size) == res.headers.find("Content-Length")->second);
|
||||
|
||||
CHECK(res.headers.count("Content-Type"));
|
||||
if (res.headers.count("Content-Type"))
|
||||
CHECK("image/jpeg" == res.headers.find("Content-Type")->second);
|
||||
|
||||
CHECK(res.headers.count("Content-Length"));
|
||||
if (res.headers.count("Content-Length"))
|
||||
CHECK(to_string(statbuf_cat.st_size) == res.headers.find("Content-Length")->second);
|
||||
}
|
||||
|
||||
//Unknown extension check
|
||||
{
|
||||
request req;
|
||||
response res;
|
||||
|
||||
req.url = "/filewith.badext";
|
||||
req.http_ver_major = 1;
|
||||
|
||||
CHECK_NOTHROW(app.handle(req, res));
|
||||
CHECK(200 == res.code);
|
||||
CHECK(res.headers.count("Content-Type"));
|
||||
if (res.headers.count("Content-Type"))
|
||||
CHECK("text/plain" == res.headers.find("Content-Type")->second);
|
||||
|
||||
CHECK(res.headers.count("Content-Length"));
|
||||
if (res.headers.count("Content-Length"))
|
||||
CHECK(to_string(statbuf_badext.st_size) == res.headers.find("Content-Length")->second);
|
||||
}
|
||||
} // send_file
|
||||
|
||||
@ -2035,15 +2262,15 @@ TEST_CASE("stream_response")
|
||||
// magic number is 102 (it's the size of the headers, which is how much this line below needs to read)
|
||||
const size_t headers_bytes = 102;
|
||||
while (received_headers_bytes < headers_bytes)
|
||||
received_headers_bytes += c.receive(asio::buffer(buf, 2048));
|
||||
received_headers_bytes += c.receive(asio::buffer(buf, 102));
|
||||
received += received_headers_bytes - headers_bytes; //add any extra that might have been received to the proper received count
|
||||
|
||||
|
||||
while (received < key_response_size)
|
||||
{
|
||||
asio::streambuf::mutable_buffers_type bufs = b.prepare(16384);
|
||||
|
||||
size_t n = c.receive(bufs);
|
||||
size_t n(0);
|
||||
n = c.receive(bufs);
|
||||
b.commit(n);
|
||||
received += n;
|
||||
|
||||
@ -2052,8 +2279,6 @@ TEST_CASE("stream_response")
|
||||
is >> s;
|
||||
|
||||
CHECK(key_response.substr(received - n, n) == s);
|
||||
|
||||
//std::this_thread::sleep_for(std::chrono::milliseconds(20));
|
||||
}
|
||||
}
|
||||
app.stop();
|
||||
@ -2069,10 +2294,10 @@ TEST_CASE("websocket")
|
||||
|
||||
SimpleApp app;
|
||||
|
||||
CROW_ROUTE(app, "/ws").websocket().onopen([&](websocket::connection&) {
|
||||
connected = true;
|
||||
CROW_LOG_INFO << "Connected websocket and value is " << connected;
|
||||
})
|
||||
CROW_WEBSOCKET_ROUTE(app, "/ws").onopen([&](websocket::connection&) {
|
||||
connected = true;
|
||||
CROW_LOG_INFO << "Connected websocket and value is " << connected;
|
||||
})
|
||||
.onmessage([&](websocket::connection& conn, const std::string& message, bool isbin) {
|
||||
CROW_LOG_INFO << "Message is \"" << message << '\"';
|
||||
if (!isbin && message == "PINGME")
|
||||
@ -2219,6 +2444,78 @@ TEST_CASE("websocket")
|
||||
app.stop();
|
||||
} // websocket
|
||||
|
||||
TEST_CASE("websocket_max_payload")
|
||||
{
|
||||
static std::string http_message = "GET /ws HTTP/1.1\r\nConnection: keep-alive, Upgrade\r\nupgrade: websocket\r\nSec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==\r\nSec-WebSocket-Version: 13\r\n\r\n";
|
||||
|
||||
static bool connected{false};
|
||||
|
||||
SimpleApp app;
|
||||
|
||||
CROW_WEBSOCKET_ROUTE(app, "/ws")
|
||||
.onopen([&](websocket::connection&) {
|
||||
connected = true;
|
||||
CROW_LOG_INFO << "Connected websocket and value is " << connected;
|
||||
})
|
||||
.onmessage([&](websocket::connection& conn, const std::string& message, bool isbin) {
|
||||
CROW_LOG_INFO << "Message is \"" << message << '\"';
|
||||
if (!isbin && message == "PINGME")
|
||||
conn.send_ping("");
|
||||
else if (!isbin && message == "Hello")
|
||||
conn.send_text("Hello back");
|
||||
else if (isbin && message == "Hello bin")
|
||||
conn.send_binary("Hello back bin");
|
||||
})
|
||||
.onclose([&](websocket::connection&, const std::string&) {
|
||||
CROW_LOG_INFO << "Closing websocket";
|
||||
});
|
||||
|
||||
app.validate();
|
||||
|
||||
auto _ = app.websocket_max_payload(3).bindaddr(LOCALHOST_ADDRESS).port(45461).run_async();
|
||||
app.wait_for_server_start();
|
||||
asio::io_service is;
|
||||
|
||||
asio::ip::tcp::socket c(is);
|
||||
c.connect(asio::ip::tcp::endpoint(
|
||||
asio::ip::address::from_string(LOCALHOST_ADDRESS), 45461));
|
||||
|
||||
|
||||
char buf[2048];
|
||||
|
||||
//----------Handshake----------
|
||||
{
|
||||
std::fill_n(buf, 2048, 0);
|
||||
c.send(asio::buffer(http_message));
|
||||
|
||||
c.receive(asio::buffer(buf, 2048));
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(5));
|
||||
CHECK(connected);
|
||||
}
|
||||
//----------Text----------
|
||||
{
|
||||
std::fill_n(buf, 2048, 0);
|
||||
char text_message[2 + 5 + 1]("\x81\x05"
|
||||
"Hello");
|
||||
|
||||
c.send(asio::buffer(text_message, 7));
|
||||
try
|
||||
{
|
||||
c.receive(asio::buffer(buf, 2048));
|
||||
FAIL_CHECK();
|
||||
}
|
||||
catch (std::exception& e)
|
||||
{
|
||||
CROW_LOG_DEBUG << "websocket_max_payload test passed due to the exception: " << e.what();
|
||||
}
|
||||
}
|
||||
|
||||
boost::system::error_code ec;
|
||||
c.lowest_layer().shutdown(boost::asio::socket_base::shutdown_type::shutdown_both, ec);
|
||||
|
||||
app.stop();
|
||||
} // websocket_max_payload
|
||||
|
||||
#ifdef CROW_ENABLE_COMPRESSION
|
||||
TEST_CASE("zlib_compression")
|
||||
{
|
||||
|
Loading…
Reference in New Issue
Block a user