Merge branch '#421-Websockets-should-support-a-maximum-payload' of https://github.com/DeuSCaNoN/Crow into #421-Websockets-should-support-a-maximum-payload

This commit is contained in:
oscar.chen 2022-05-14 17:07:54 -07:00
commit 45dc03cfa5
36 changed files with 1046 additions and 170 deletions

View File

@ -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

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 15 KiB

View 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

View 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

View 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

View 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

View File

@ -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).

View File

@ -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.

View File

@ -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>

View File

@ -1,4 +1,6 @@
**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

View 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");
```

View File

@ -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>

View File

@ -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>

View File

@ -5,7 +5,9 @@ Crow supports query strings through `crow::request::url_params`. The object is o
## get(name)
Returns the value (as char*) based on the given key (or name). Returns `nullptr` if the key is not found.
## pop(name)
**Introduced in: `v0.3`**<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,13 +18,17 @@ 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

View File

@ -52,7 +52,9 @@ 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>
@ -75,12 +77,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

View File

@ -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.

View File

@ -7,10 +7,10 @@
<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 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{
@ -143,7 +143,8 @@ code{
}
</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>
@ -151,19 +152,23 @@ code{
<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>
@ -176,8 +181,7 @@ code{
<h2 style="text-align: center;">Easy to get started</h3>
</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>
@ -244,7 +248,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>
@ -252,7 +256,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>
@ -260,7 +264,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>
@ -268,7 +272,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>
@ -276,7 +280,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>
@ -289,9 +293,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>
@ -309,7 +313,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)
@ -511,7 +515,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 }}
@ -520,7 +524,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" %}

View File

@ -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">

View 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>

View File

@ -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;
}

View File

@ -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
View 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": []}
]

View File

@ -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)

View File

@ -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"

View 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;
}

View File

@ -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);

View File

@ -29,6 +29,7 @@
#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_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()
@ -61,7 +62,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)
{
@ -342,7 +343,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;
@ -355,7 +356,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;
@ -367,6 +368,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;
@ -390,6 +404,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&&)
{

View File

@ -417,6 +417,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;

View File

@ -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());
}
}
};

View File

@ -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)
{

View File

@ -368,13 +368,15 @@ 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)
{}
void validate() override
@ -388,12 +390,12 @@ 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_);
new crow::websocket::Connection<SocketAdaptor, App>(req, std::move(adaptor), app_, 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_, open_handler_, message_handler_, close_handler_, error_handler_, accept_handler_);
}
#endif
@ -433,6 +435,7 @@ 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_;
@ -448,9 +451,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;
}
@ -1468,7 +1473,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 +1490,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;
}

View File

@ -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

View File

@ -60,7 +60,7 @@ namespace crow
//
/// A websocket connection.
template<typename Adaptor>
template<typename Adaptor, typename Handler>
class Connection : public connection
{
public:
@ -69,14 +69,19 @@ 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,
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),
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"))
{
@ -617,6 +622,7 @@ namespace crow
private:
Adaptor adaptor_;
Handler* handler_;
std::vector<std::string> sending_buffers_;
std::vector<std::string> write_buffers_;

View File

@ -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 &copy; 2020-2022 CrowCpp'

45
scripts/generateDocumentationAndDeploy.sh Normal file → Executable file
View 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.

View File

@ -1452,7 +1452,7 @@ TEST_CASE("local_middleware")
app.stop();
} // local_middleware
TEST_CASE("middleware_cookieparser")
TEST_CASE("middleware_cookieparser_parse")
{
static char buf[2048];
@ -1499,9 +1499,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];
@ -2044,15 +2091,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;
@ -2061,8 +2108,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();
@ -2078,10 +2123,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")