aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--doc/src/guide/resource_design.asciidoc3
-rw-r--r--doc/src/guide/rest_handlers.asciidoc1
-rw-r--r--doc/src/guide/rest_start.pngbin105640 -> 110820 bytes
-rw-r--r--doc/src/guide/rest_start.svg678
-rw-r--r--doc/src/manual/cowboy_rest.asciidoc25
-rw-r--r--src/cowboy_rest.erl30
-rw-r--r--test/handlers/rate_limited_h.erl24
-rw-r--r--test/rest_handler_SUITE.erl26
8 files changed, 597 insertions, 190 deletions
diff --git a/doc/src/guide/resource_design.asciidoc b/doc/src/guide/resource_design.asciidoc
index fa0c612..4ea0390 100644
--- a/doc/src/guide/resource_design.asciidoc
+++ b/doc/src/guide/resource_design.asciidoc
@@ -122,6 +122,9 @@ Can access to a resource be forbidden regardless of access
being authorized? A simple example of that is censorship
of a resource. Implement the `forbidden` callback.
+Can access be rate-limited for authenticated users? Use the
+`rate_limited` callback.
+
Are there any constraints on the length of the resource URI?
For example, the URI may be used as a key in storage and may
have a limit in length. Implement `uri_too_long`.
diff --git a/doc/src/guide/rest_handlers.asciidoc b/doc/src/guide/rest_handlers.asciidoc
index dab5bea..baf8e6a 100644
--- a/doc/src/guide/rest_handlers.asciidoc
+++ b/doc/src/guide/rest_handlers.asciidoc
@@ -84,6 +84,7 @@ if it is undefined, moving directly to the next step. Similarly,
| multiple_choices | `false`
| options | `ok`
| previously_existed | `false`
+| rate_limited | `false`
| resource_exists | `true`
| service_available | `true`
| uri_too_long | `false`
diff --git a/doc/src/guide/rest_start.png b/doc/src/guide/rest_start.png
index 1f1e312..4c230a0 100644
--- a/doc/src/guide/rest_start.png
+++ b/doc/src/guide/rest_start.png
Binary files differ
diff --git a/doc/src/guide/rest_start.svg b/doc/src/guide/rest_start.svg
index 076c619..6f1dd87 100644
--- a/doc/src/guide/rest_start.svg
+++ b/doc/src/guide/rest_start.svg
@@ -15,7 +15,7 @@
height="1052.3622047"
id="svg2"
version="1.1"
- inkscape:version="0.48.4 r9939"
+ inkscape:version="0.92.2 2405546, 2018-03-11"
sodipodi:docname="rest_start.svg"
inkscape:export-filename="/home/essen/Dropbox/Public/drawing.png"
inkscape:export-xdpi="90"
@@ -65,15 +65,15 @@
inkscape:pageopacity="1"
inkscape:pageshadow="2"
inkscape:zoom="1.0000001"
- inkscape:cx="171.11305"
- inkscape:cy="549.52821"
+ inkscape:cx="213.11305"
+ inkscape:cy="726.77495"
inkscape:document-units="px"
inkscape:current-layer="layer1"
showgrid="false"
- inkscape:window-width="2560"
- inkscape:window-height="1402"
+ inkscape:window-width="1920"
+ inkscape:window-height="1043"
inkscape:window-x="0"
- inkscape:window-y="38"
+ inkscape:window-y="0"
inkscape:window-maximized="1"
inkscape:snap-global="true"
showguides="true">
@@ -111,7 +111,9 @@
style="fill:none;stroke:#6d8e41;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:1.99999999, 3.99999998;stroke-dashoffset:0" />
<g
transform="translate(205.92143,-296.03137)"
- id="g5650-7">
+ id="g5650-7"
+ inkscape:export-xdpi="90"
+ inkscape:export-ydpi="90">
<path
inkscape:connector-curvature="0"
id="path5570-9"
@@ -137,7 +139,9 @@
</g>
<g
transform="translate(205.92143,-212.00698)"
- id="g5650-9">
+ id="g5650-9"
+ inkscape:export-xdpi="90"
+ inkscape:export-ydpi="90">
<path
inkscape:connector-curvature="0"
id="path5570-7"
@@ -163,7 +167,9 @@
</g>
<g
transform="translate(205.92143,-129.04326)"
- id="g5650-0">
+ id="g5650-0"
+ inkscape:export-xdpi="90"
+ inkscape:export-ydpi="90">
<path
inkscape:connector-curvature="0"
id="path5570-5"
@@ -189,7 +195,9 @@
</g>
<g
transform="translate(205.92143,-44.866334)"
- id="g5650-94">
+ id="g5650-94"
+ inkscape:export-xdpi="90"
+ inkscape:export-ydpi="90">
<path
inkscape:connector-curvature="0"
id="path5570-71"
@@ -215,7 +223,9 @@
</g>
<g
transform="translate(205.92143,38.329623)"
- id="g5650-93">
+ id="g5650-93"
+ inkscape:export-xdpi="90"
+ inkscape:export-ydpi="90">
<path
inkscape:connector-curvature="0"
id="path5570-3"
@@ -241,7 +251,9 @@
</g>
<g
transform="translate(205.92143,122.59665)"
- id="g5650-3">
+ id="g5650-3"
+ inkscape:export-xdpi="90"
+ inkscape:export-ydpi="90">
<path
inkscape:connector-curvature="0"
id="path5570-93"
@@ -267,7 +279,9 @@
</g>
<g
transform="translate(205.92143,206.62103)"
- id="g5650-6">
+ id="g5650-6"
+ inkscape:export-xdpi="90"
+ inkscape:export-ydpi="90">
<path
inkscape:connector-curvature="0"
id="path5570-78"
@@ -293,7 +307,9 @@
</g>
<g
transform="translate(205.92143,289.59516)"
- id="g5650-34">
+ id="g5650-34"
+ inkscape:export-xdpi="90"
+ inkscape:export-ydpi="90">
<path
inkscape:connector-curvature="0"
id="path5570-30"
@@ -319,7 +335,9 @@
</g>
<g
transform="translate(205.92143,373.37691)"
- id="g5650-5">
+ id="g5650-5"
+ inkscape:export-xdpi="90"
+ inkscape:export-ydpi="90">
<path
inkscape:connector-curvature="0"
id="path5570-90"
@@ -345,7 +363,9 @@
</g>
<g
transform="translate(205.92143,457.4117)"
- id="g5650-1">
+ id="g5650-1"
+ inkscape:export-xdpi="90"
+ inkscape:export-ydpi="90">
<path
inkscape:connector-curvature="0"
id="path5570-33"
@@ -371,7 +391,9 @@
</g>
<g
transform="matrix(0,-1,1,0,-25.673146,80.832304)"
- id="g5650-2-0">
+ id="g5650-2-0"
+ inkscape:export-xdpi="90"
+ inkscape:export-ydpi="90">
<path
inkscape:connector-curvature="0"
id="path5570-2-5"
@@ -397,7 +419,9 @@
</g>
<g
transform="matrix(0,-1,1,0,-25.673146,164.49956)"
- id="g5650-2-6">
+ id="g5650-2-6"
+ inkscape:export-xdpi="90"
+ inkscape:export-ydpi="90">
<path
inkscape:connector-curvature="0"
id="path5570-2-1"
@@ -423,7 +447,9 @@
</g>
<g
transform="matrix(0,-1,1,0,-25.673146,248.1668)"
- id="g5650-2-4">
+ id="g5650-2-4"
+ inkscape:export-xdpi="90"
+ inkscape:export-ydpi="90">
<path
inkscape:connector-curvature="0"
id="path5570-2-0"
@@ -449,7 +475,9 @@
</g>
<g
transform="matrix(0,-1,1,0,-25.673146,331.8341)"
- id="g5650-2-04">
+ id="g5650-2-04"
+ inkscape:export-xdpi="90"
+ inkscape:export-ydpi="90">
<path
inkscape:connector-curvature="0"
id="path5570-2-9"
@@ -475,7 +503,9 @@
</g>
<g
transform="matrix(0,-1,1,0,-25.673146,415.50138)"
- id="g5650-2-8">
+ id="g5650-2-8"
+ inkscape:export-xdpi="90"
+ inkscape:export-ydpi="90">
<path
inkscape:connector-curvature="0"
id="path5570-2-50"
@@ -501,7 +531,9 @@
</g>
<g
transform="matrix(0,-1,1,0,-25.673146,499.16862)"
- id="g5650-2-1">
+ id="g5650-2-1"
+ inkscape:export-xdpi="90"
+ inkscape:export-ydpi="90">
<path
inkscape:connector-curvature="0"
id="path5570-2-2"
@@ -527,7 +559,9 @@
</g>
<g
transform="matrix(0,-1,1,0,-25.673146,582.83589)"
- id="g5650-2-3">
+ id="g5650-2-3"
+ inkscape:export-xdpi="90"
+ inkscape:export-ydpi="90">
<path
inkscape:connector-curvature="0"
id="path5570-2-10"
@@ -553,7 +587,9 @@
</g>
<g
transform="matrix(0,-1,1,0,-25.673146,666.50315)"
- id="g5650-2-44">
+ id="g5650-2-44"
+ inkscape:export-xdpi="90"
+ inkscape:export-ydpi="90">
<path
inkscape:connector-curvature="0"
id="path5570-2-11"
@@ -579,7 +615,9 @@
</g>
<g
transform="matrix(0,-1,1,0,-25.673146,750.17041)"
- id="g5650-2-12">
+ id="g5650-2-12"
+ inkscape:export-xdpi="90"
+ inkscape:export-ydpi="90">
<path
inkscape:connector-curvature="0"
id="path5570-2-6"
@@ -610,7 +648,9 @@
height="35.209244"
x="43.049091"
y="204.67757"
- rx="15" />
+ rx="15"
+ inkscape:export-xdpi="90"
+ inkscape:export-ydpi="90" />
<rect
style="fill:#d1f2a5;fill-opacity:1;fill-rule:nonzero;stroke:#a9ca7d;stroke-width:3;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0"
id="rect5273-1-5"
@@ -618,7 +658,9 @@
height="35.209244"
x="43.049091"
y="288.40311"
- rx="15" />
+ rx="15"
+ inkscape:export-xdpi="90"
+ inkscape:export-ydpi="90" />
<rect
style="fill:#d1f2a5;fill-opacity:1;fill-rule:nonzero;stroke:#a9ca7d;stroke-width:3;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0"
id="rect5273-1-8"
@@ -626,7 +668,9 @@
height="35.209244"
x="43.049091"
y="372.01199"
- rx="15" />
+ rx="15"
+ inkscape:export-xdpi="90"
+ inkscape:export-ydpi="90" />
<rect
style="fill:#d1f2a5;fill-opacity:1;fill-rule:nonzero;stroke:#a9ca7d;stroke-width:3;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0"
id="rect5273-1-4"
@@ -634,7 +678,9 @@
height="35.209244"
x="43.049091"
y="455.67929"
- rx="15" />
+ rx="15"
+ inkscape:export-xdpi="90"
+ inkscape:export-ydpi="90" />
<rect
style="fill:#d1f2a5;fill-opacity:1;fill-rule:nonzero;stroke:#a9ca7d;stroke-width:3;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0"
id="rect5273-1-87"
@@ -642,7 +688,9 @@
height="35.209244"
x="43.049091"
y="539.34656"
- rx="15" />
+ rx="15"
+ inkscape:export-xdpi="90"
+ inkscape:export-ydpi="90" />
<rect
style="fill:#d1f2a5;fill-opacity:1;fill-rule:nonzero;stroke:#a9ca7d;stroke-width:3;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0"
id="rect5273-1-41"
@@ -650,7 +698,9 @@
height="35.209244"
x="43.049091"
y="623.01385"
- rx="15" />
+ rx="15"
+ inkscape:export-xdpi="90"
+ inkscape:export-ydpi="90" />
<rect
style="fill:#d1f2a5;fill-opacity:1;fill-rule:nonzero;stroke:#a9ca7d;stroke-width:3;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0"
id="rect5273-1-7"
@@ -658,7 +708,9 @@
height="35.209244"
x="43.049091"
y="706.68115"
- rx="15" />
+ rx="15"
+ inkscape:export-xdpi="90"
+ inkscape:export-ydpi="90" />
<rect
style="fill:#d1f2a5;fill-opacity:1;fill-rule:nonzero;stroke:#a9ca7d;stroke-width:3;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0"
id="rect5273-1-20"
@@ -666,15 +718,9 @@
height="35.209244"
x="43.049091"
y="790.34839"
- rx="15" />
- <rect
- style="fill:#d1f2a5;fill-opacity:1;fill-rule:nonzero;stroke:#a9ca7d;stroke-width:3;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0"
- id="rect5273-1-3"
- width="210.17955"
- height="35.209244"
- x="43.049091"
- y="874.01562"
- rx="15" />
+ rx="15"
+ inkscape:export-xdpi="90"
+ inkscape:export-ydpi="90" />
<rect
style="fill:#d1f2a5;fill-opacity:1;fill-rule:nonzero;stroke:#a9ca7d;stroke-width:3;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0"
id="rect5273-1"
@@ -682,7 +728,9 @@
height="35.209244"
x="43.049091"
y="121.0042"
- rx="15" />
+ rx="15"
+ inkscape:export-xdpi="90"
+ inkscape:export-ydpi="90" />
<g
id="g5650"
transform="translate(0,-0.47597102)">
@@ -758,171 +806,187 @@
width="744.09448"
height="1052.3622"
inkscape:export-filename="/home/essen/extend/cowboy/guide/http_req_resp.png"
- inkscape:export-xdpi="89.926643"
- inkscape:export-ydpi="89.926643" />
+ inkscape:export-xdpi="90"
+ inkscape:export-ydpi="90" />
<text
xml:space="preserve"
- style="font-size:16px;font-style:normal;font-weight:normal;text-align:center;line-height:125%;letter-spacing:0px;word-spacing:0px;text-anchor:middle;fill:#77823c;fill-opacity:1;stroke:none;font-family:Sans;-inkscape-font-specification:Sans"
+ style="font-style:normal;font-weight:normal;line-height:0%;font-family:sans-serif;-inkscape-font-specification:Sans;text-align:center;letter-spacing:0px;word-spacing:0px;text-anchor:middle;fill:#77823c;fill-opacity:1;stroke:none"
x="-58.692513"
y="114.39204"
- id="text5371"
- sodipodi:linespacing="125%"><tspan
+ id="text5371"><tspan
sodipodi:role="line"
id="tspan5373"
x="-58.692513"
- y="114.39204">some text</tspan></text>
+ y="114.39204"
+ style="font-size:16px;line-height:1.25;font-family:sans-serif">some text</tspan></text>
<text
xml:space="preserve"
- style="font-size:16px;font-style:normal;font-weight:normal;text-align:center;line-height:125%;letter-spacing:0px;word-spacing:0px;text-anchor:middle;fill:#6d8e41;fill-opacity:1;stroke:none;font-family:Sans;-inkscape-font-specification:Sans"
+ style="font-style:normal;font-weight:normal;line-height:0%;font-family:sans-serif;-inkscape-font-specification:Sans;text-align:center;letter-spacing:0px;word-spacing:0px;text-anchor:middle;fill:#6d8e41;fill-opacity:1;stroke:none"
x="-58.692513"
y="53.112247"
- id="text5371-2"
- sodipodi:linespacing="125%"><tspan
+ id="text5371-2"><tspan
sodipodi:role="line"
id="tspan5373-6"
x="-58.692513"
- y="53.112247">some text</tspan></text>
+ y="53.112247"
+ style="font-size:16px;line-height:1.25;font-family:sans-serif">some text</tspan></text>
<text
xml:space="preserve"
- style="font-size:16px;font-style:normal;font-weight:normal;text-align:center;line-height:125%;letter-spacing:0px;word-spacing:0px;text-anchor:middle;fill:#6d8e41;fill-opacity:1;stroke:none;font-family:Sans;-inkscape-font-specification:Sans"
+ style="font-style:normal;font-weight:normal;line-height:0%;font-family:sans-serif;-inkscape-font-specification:Sans;text-align:center;letter-spacing:0px;word-spacing:0px;text-anchor:middle;fill:#6d8e41;fill-opacity:1;stroke:none"
x="148.18575"
y="310.19913"
id="text5371-2-3"
- sodipodi:linespacing="125%"
inkscape:export-filename="/home/essen/extend/cowboy/guide/http_req_resp.png"
- inkscape:export-xdpi="89.926643"
- inkscape:export-ydpi="89.926643"><tspan
+ inkscape:export-xdpi="90"
+ inkscape:export-ydpi="90"><tspan
sodipodi:role="line"
id="tspan5373-6-7"
x="148.18575"
- y="310.19913">uri_too_long</tspan></text>
+ y="310.19913"
+ style="font-size:16px;line-height:1.25;font-family:sans-serif">uri_too_long</tspan></text>
<text
xml:space="preserve"
- style="font-size:16px;font-style:normal;font-weight:normal;text-align:center;line-height:125%;letter-spacing:0px;word-spacing:0px;text-anchor:middle;fill:#6d8e41;fill-opacity:1;stroke:none;font-family:Sans;-inkscape-font-specification:Sans"
+ style="font-style:normal;font-weight:normal;line-height:0%;font-family:sans-serif;-inkscape-font-specification:Sans;text-align:center;letter-spacing:0px;word-spacing:0px;text-anchor:middle;fill:#6d8e41;fill-opacity:1;stroke:none"
x="147.60762"
y="477.47531"
id="text5371-2-3-0"
- sodipodi:linespacing="125%"
inkscape:export-filename="/home/essen/extend/cowboy/guide/http_req_resp.png"
- inkscape:export-xdpi="89.926643"
- inkscape:export-ydpi="89.926643"><tspan
+ inkscape:export-xdpi="90"
+ inkscape:export-ydpi="90"><tspan
sodipodi:role="line"
id="tspan5373-6-7-3"
x="147.60762"
- y="477.47531">malformed_request</tspan></text>
+ y="477.47531"
+ style="font-size:16px;line-height:1.25;font-family:sans-serif">malformed_request</tspan></text>
<text
xml:space="preserve"
- style="font-size:16px;font-style:normal;font-weight:normal;text-align:center;line-height:125%;letter-spacing:0px;word-spacing:0px;text-anchor:middle;fill:#9b3b1c;fill-opacity:1;stroke:none;font-family:Sans;-inkscape-font-specification:Sans"
+ style="font-style:normal;font-weight:normal;line-height:0%;font-family:sans-serif;-inkscape-font-specification:Sans;text-align:center;letter-spacing:0px;word-spacing:0px;text-anchor:middle;fill:#9b3b1c;fill-opacity:1;stroke:none"
x="-58.692513"
y="236.95154"
- id="text5371-4"
- sodipodi:linespacing="125%"><tspan
+ id="text5371-4"><tspan
sodipodi:role="line"
id="tspan5373-9"
x="-58.692513"
- y="236.95154">some text</tspan></text>
+ y="236.95154"
+ style="font-size:16px;line-height:1.25;font-family:sans-serif">some text</tspan></text>
<text
xml:space="preserve"
- style="font-size:16px;font-style:normal;font-weight:normal;text-align:center;line-height:125%;letter-spacing:0px;word-spacing:0px;text-anchor:middle;fill:#9b3b1c;fill-opacity:1;stroke:none;font-family:Sans;-inkscape-font-specification:Sans"
+ style="font-style:normal;font-weight:normal;line-height:0%;font-family:sans-serif;-inkscape-font-specification:Sans;text-align:center;letter-spacing:0px;word-spacing:0px;text-anchor:middle;fill:#9b3b1c;fill-opacity:1;stroke:none"
x="147.00391"
y="60.912468"
id="text5371-4-0"
- sodipodi:linespacing="125%"
inkscape:export-filename="/home/essen/extend/cowboy/guide/http_req_resp.png"
- inkscape:export-xdpi="89.926643"
- inkscape:export-ydpi="89.926643"><tspan
+ inkscape:export-xdpi="90"
+ inkscape:export-ydpi="90"><tspan
sodipodi:role="line"
x="147.00391"
y="60.912468"
- id="tspan17171">init</tspan></text>
+ id="tspan17171"
+ style="font-size:16px;line-height:1.25;font-family:sans-serif">init</tspan></text>
<text
xml:space="preserve"
- style="font-size:16px;font-style:normal;font-weight:normal;text-align:center;line-height:125%;letter-spacing:0px;word-spacing:0px;text-anchor:middle;fill:#6d8e41;fill-opacity:1;stroke:none;font-family:Sans;-inkscape-font-specification:Sans"
+ style="font-style:normal;font-weight:normal;line-height:0%;font-family:sans-serif;-inkscape-font-specification:Sans;text-align:center;letter-spacing:0px;word-spacing:0px;text-anchor:middle;fill:#6d8e41;fill-opacity:1;stroke:none"
x="148.11153"
y="561.14258"
id="text5371-2-9"
- sodipodi:linespacing="125%"><tspan
+ inkscape:export-xdpi="90"
+ inkscape:export-ydpi="90"><tspan
sodipodi:role="line"
id="tspan5373-6-8"
x="148.11153"
- y="561.14258">is_authorized</tspan></text>
+ y="561.14258"
+ style="font-size:16px;line-height:1.25;font-family:sans-serif">is_authorized</tspan></text>
<text
xml:space="preserve"
- style="font-size:16px;font-style:normal;font-weight:normal;text-align:center;line-height:125%;letter-spacing:0px;word-spacing:0px;text-anchor:middle;fill:#6d8e41;fill-opacity:1;stroke:none;font-family:Sans;-inkscape-font-specification:Sans"
+ style="font-style:normal;font-weight:normal;line-height:0%;font-family:sans-serif;-inkscape-font-specification:Sans;text-align:center;letter-spacing:0px;word-spacing:0px;text-anchor:middle;fill:#6d8e41;fill-opacity:1;stroke:none"
x="148.64278"
y="646.58331"
id="text5371-2-7"
- sodipodi:linespacing="125%"><tspan
+ inkscape:export-xdpi="90"
+ inkscape:export-ydpi="90"><tspan
sodipodi:role="line"
id="tspan5373-6-2"
x="148.64278"
- y="646.58331">forbidden</tspan></text>
+ y="646.58331"
+ style="font-size:16px;line-height:1.25;font-family:sans-serif">forbidden</tspan></text>
<text
xml:space="preserve"
- style="font-size:16px;font-style:normal;font-weight:normal;text-align:center;line-height:125%;letter-spacing:0px;word-spacing:0px;text-anchor:middle;fill:#6d8e41;fill-opacity:1;stroke:none;font-family:Sans;-inkscape-font-specification:Sans"
+ style="font-style:normal;font-weight:normal;line-height:0%;font-family:sans-serif;-inkscape-font-specification:Sans;text-align:center;letter-spacing:0px;word-spacing:0px;text-anchor:middle;fill:#6d8e41;fill-opacity:1;stroke:none"
x="148.29512"
y="728.47717"
id="text5371-2-73"
- sodipodi:linespacing="125%"><tspan
+ inkscape:export-xdpi="90"
+ inkscape:export-ydpi="90"><tspan
sodipodi:role="line"
id="tspan5373-6-5"
x="148.29512"
- y="728.47717">valid_content_headers</tspan></text>
+ y="728.47717"
+ style="font-size:16px;line-height:1.25;font-family:sans-serif">valid_content_headers</tspan></text>
<text
xml:space="preserve"
- style="font-size:16px;font-style:normal;font-weight:normal;text-align:center;line-height:125%;letter-spacing:0px;word-spacing:0px;text-anchor:middle;fill:#6d8e41;fill-opacity:1;stroke:none;font-family:Sans;-inkscape-font-specification:Sans"
+ style="font-style:normal;font-weight:normal;line-height:0%;font-family:sans-serif;-inkscape-font-specification:Sans;text-align:center;letter-spacing:0px;word-spacing:0px;text-anchor:middle;fill:#6d8e41;fill-opacity:1;stroke:none"
x="147.83809"
y="812.14441"
id="text5371-2-5"
- sodipodi:linespacing="125%"><tspan
+ inkscape:export-xdpi="90"
+ inkscape:export-ydpi="90"><tspan
sodipodi:role="line"
id="tspan5373-6-1"
x="147.83809"
- y="812.14441">valid_entity_length</tspan></text>
+ y="812.14441"
+ style="font-size:16px;line-height:1.25;font-family:sans-serif">valid_entity_length</tspan></text>
<text
xml:space="preserve"
- style="font-size:16px;font-style:normal;font-weight:normal;text-align:center;line-height:125%;letter-spacing:0px;word-spacing:0px;text-anchor:middle;fill:#6d8e41;fill-opacity:1;stroke:none;font-family:Sans;-inkscape-font-specification:Sans"
+ style="font-style:normal;font-weight:normal;line-height:0%;font-family:sans-serif;-inkscape-font-specification:Sans;text-align:center;letter-spacing:0px;word-spacing:0px;text-anchor:middle;fill:#6d8e41;fill-opacity:1;stroke:none"
x="148.58809"
y="895.81165"
id="text5371-2-1"
- sodipodi:linespacing="125%"><tspan
+ inkscape:export-xdpi="90"
+ inkscape:export-ydpi="90"><tspan
sodipodi:role="line"
id="tspan5373-6-71"
x="148.58809"
- y="895.81165">...</tspan></text>
+ y="895.81165"
+ style="font-size:16px;line-height:1.25;font-family:sans-serif">...</tspan></text>
<text
xml:space="preserve"
- style="font-size:16px;font-style:normal;font-weight:normal;text-align:center;line-height:125%;letter-spacing:0px;word-spacing:0px;text-anchor:middle;fill:#6d8e41;fill-opacity:1;stroke:none;font-family:Sans;-inkscape-font-specification:Sans"
+ style="font-style:normal;font-weight:normal;line-height:0%;font-family:sans-serif;-inkscape-font-specification:Sans;text-align:center;letter-spacing:0px;word-spacing:0px;text-anchor:middle;fill:#6d8e41;fill-opacity:1;stroke:none"
x="148.13106"
y="142.80627"
id="text5371-2-95"
- sodipodi:linespacing="125%"><tspan
+ inkscape:export-xdpi="90"
+ inkscape:export-ydpi="90"><tspan
sodipodi:role="line"
id="tspan5373-6-80"
x="148.13106"
- y="142.80627">service_available</tspan></text>
+ y="142.80627"
+ style="font-size:16px;line-height:1.25;font-family:sans-serif">service_available</tspan></text>
<text
xml:space="preserve"
- style="font-size:16px;font-style:normal;font-weight:normal;text-align:center;line-height:125%;letter-spacing:0px;word-spacing:0px;text-anchor:middle;fill:#6d8e41;fill-opacity:1;stroke:none;font-family:Sans;-inkscape-font-specification:Sans"
+ style="font-style:normal;font-weight:normal;line-height:0%;font-family:sans-serif;-inkscape-font-specification:Sans;text-align:center;letter-spacing:0px;word-spacing:0px;text-anchor:middle;fill:#6d8e41;fill-opacity:1;stroke:none"
x="147.80684"
y="226.4736"
id="text5371-2-32"
- sodipodi:linespacing="125%"><tspan
+ inkscape:export-xdpi="90"
+ inkscape:export-ydpi="90"><tspan
sodipodi:role="line"
id="tspan5373-6-27"
x="147.80684"
- y="226.4736">known_methods</tspan></text>
+ y="226.4736"
+ style="font-size:16px;line-height:1.25;font-family:sans-serif">known_methods</tspan></text>
<text
xml:space="preserve"
- style="font-size:16px;font-style:normal;font-weight:normal;text-align:center;line-height:125%;letter-spacing:0px;word-spacing:0px;text-anchor:middle;fill:#6d8e41;fill-opacity:1;stroke:none;font-family:Sans;-inkscape-font-specification:Sans"
+ style="font-style:normal;font-weight:normal;line-height:0%;font-family:sans-serif;-inkscape-font-specification:Sans;text-align:center;letter-spacing:0px;word-spacing:0px;text-anchor:middle;fill:#6d8e41;fill-opacity:1;stroke:none"
x="148.05293"
y="393.80801"
id="text5371-2-74"
- sodipodi:linespacing="125%"><tspan
+ inkscape:export-xdpi="90"
+ inkscape:export-ydpi="90"><tspan
sodipodi:role="line"
id="tspan5373-6-18"
x="148.05293"
- y="393.80801">allowed_methods</tspan></text>
+ y="393.80801"
+ style="font-size:16px;line-height:1.25;font-family:sans-serif">allowed_methods</tspan></text>
<rect
style="fill:#d1f2a5;fill-opacity:1;fill-rule:nonzero;stroke:#a9ca7d;stroke-width:3;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0"
id="rect5273-1-2"
@@ -933,103 +997,121 @@
rx="15" />
<text
xml:space="preserve"
- style="font-size:16px;font-style:normal;font-weight:normal;text-align:start;line-height:125%;letter-spacing:0px;word-spacing:0px;text-anchor:start;fill:#6d8e41;fill-opacity:1;stroke:none;font-family:Sans;-inkscape-font-specification:Sans"
+ style="font-style:normal;font-weight:normal;line-height:0%;font-family:sans-serif;-inkscape-font-specification:Sans;text-align:start;letter-spacing:0px;word-spacing:0px;text-anchor:start;fill:#6d8e41;fill-opacity:1;stroke:none"
x="164.26562"
y="185.95248"
id="text5371-2-391"
- sodipodi:linespacing="125%"><tspan
+ inkscape:export-xdpi="90"
+ inkscape:export-ydpi="90"><tspan
sodipodi:role="line"
id="tspan5373-6-63"
x="164.26562"
- y="185.95248">true</tspan></text>
+ y="185.95248"
+ style="font-size:16px;line-height:1.25;font-family:sans-serif">true</tspan></text>
<text
xml:space="preserve"
- style="font-size:16px;font-style:normal;font-weight:normal;text-align:start;line-height:125%;letter-spacing:0px;word-spacing:0px;text-anchor:start;fill:#6d8e41;fill-opacity:1;stroke:none;font-family:Sans;-inkscape-font-specification:Sans"
+ style="font-style:normal;font-weight:normal;line-height:0%;font-family:sans-serif;-inkscape-font-specification:Sans;text-align:start;letter-spacing:0px;word-spacing:0px;text-anchor:start;fill:#6d8e41;fill-opacity:1;stroke:none"
x="164.26562"
y="269.61978"
id="text5371-2-954"
- sodipodi:linespacing="125%"><tspan
+ inkscape:export-xdpi="90"
+ inkscape:export-ydpi="90"><tspan
sodipodi:role="line"
id="tspan5373-6-808"
x="164.26562"
- y="269.61978">known*</tspan></text>
+ y="269.61978"
+ style="font-size:16px;line-height:1.25;font-family:sans-serif">known*</tspan></text>
<text
xml:space="preserve"
- style="font-size:16px;font-style:normal;font-weight:normal;text-align:start;line-height:125%;letter-spacing:0px;word-spacing:0px;text-anchor:start;fill:#6d8e41;fill-opacity:1;stroke:none;font-family:Sans;-inkscape-font-specification:Sans"
+ style="font-style:normal;font-weight:normal;line-height:0%;font-family:sans-serif;-inkscape-font-specification:Sans;text-align:start;letter-spacing:0px;word-spacing:0px;text-anchor:start;fill:#6d8e41;fill-opacity:1;stroke:none"
x="164.26562"
y="353.28702"
id="text5371-2-4"
- sodipodi:linespacing="125%"><tspan
+ inkscape:export-xdpi="90"
+ inkscape:export-ydpi="90"><tspan
sodipodi:role="line"
id="tspan5373-6-11"
x="164.26562"
- y="353.28702">false</tspan></text>
+ y="353.28702"
+ style="font-size:16px;line-height:1.25;font-family:sans-serif">false</tspan></text>
<text
xml:space="preserve"
- style="font-size:16px;font-style:normal;font-weight:normal;text-align:start;line-height:125%;letter-spacing:0px;word-spacing:0px;text-anchor:start;fill:#6d8e41;fill-opacity:1;stroke:none;font-family:Sans;-inkscape-font-specification:Sans"
+ style="font-style:normal;font-weight:normal;line-height:0%;font-family:sans-serif;-inkscape-font-specification:Sans;text-align:start;letter-spacing:0px;word-spacing:0px;text-anchor:start;fill:#6d8e41;fill-opacity:1;stroke:none"
x="164.26562"
y="436.95425"
id="text5371-2-92"
- sodipodi:linespacing="125%"><tspan
+ inkscape:export-xdpi="90"
+ inkscape:export-ydpi="90"><tspan
sodipodi:role="line"
id="tspan5373-6-4"
x="164.26562"
- y="436.95425">allowed*</tspan></text>
+ y="436.95425"
+ style="font-size:16px;line-height:1.25;font-family:sans-serif">allowed*</tspan></text>
<text
xml:space="preserve"
- style="font-size:16px;font-style:normal;font-weight:normal;text-align:start;line-height:125%;letter-spacing:0px;word-spacing:0px;text-anchor:start;fill:#6d8e41;fill-opacity:1;stroke:none;font-family:Sans;-inkscape-font-specification:Sans"
+ style="font-style:normal;font-weight:normal;line-height:0%;font-family:sans-serif;-inkscape-font-specification:Sans;text-align:start;letter-spacing:0px;word-spacing:0px;text-anchor:start;fill:#6d8e41;fill-opacity:1;stroke:none"
x="164.26562"
y="520.62152"
id="text5371-2-739"
- sodipodi:linespacing="125%"><tspan
+ inkscape:export-xdpi="90"
+ inkscape:export-ydpi="90"><tspan
sodipodi:role="line"
id="tspan5373-6-0"
x="164.26562"
- y="520.62152">false</tspan></text>
+ y="520.62152"
+ style="font-size:16px;line-height:1.25;font-family:sans-serif">false</tspan></text>
<text
xml:space="preserve"
- style="font-size:16px;font-style:normal;font-weight:normal;text-align:start;line-height:125%;letter-spacing:0px;word-spacing:0px;text-anchor:start;fill:#6d8e41;fill-opacity:1;stroke:none;font-family:Sans;-inkscape-font-specification:Sans"
+ style="font-style:normal;font-weight:normal;line-height:0%;font-family:sans-serif;-inkscape-font-specification:Sans;text-align:start;letter-spacing:0px;word-spacing:0px;text-anchor:start;fill:#6d8e41;fill-opacity:1;stroke:none"
x="164.26562"
y="604.28876"
id="text5371-2-8"
- sodipodi:linespacing="125%"><tspan
+ inkscape:export-xdpi="90"
+ inkscape:export-ydpi="90"><tspan
sodipodi:role="line"
id="tspan5373-6-3"
x="164.26562"
- y="604.28876">true</tspan></text>
+ y="604.28876"
+ style="font-size:16px;line-height:1.25;font-family:sans-serif">true</tspan></text>
<text
xml:space="preserve"
- style="font-size:16px;font-style:normal;font-weight:normal;text-align:start;line-height:125%;letter-spacing:0px;word-spacing:0px;text-anchor:start;fill:#6d8e41;fill-opacity:1;stroke:none;font-family:Sans;-inkscape-font-specification:Sans"
+ style="font-style:normal;font-weight:normal;line-height:0%;font-family:sans-serif;-inkscape-font-specification:Sans;text-align:start;letter-spacing:0px;word-spacing:0px;text-anchor:start;fill:#6d8e41;fill-opacity:1;stroke:none"
x="164.26562"
y="687.95599"
id="text5371-2-0"
- sodipodi:linespacing="125%"><tspan
+ inkscape:export-xdpi="90"
+ inkscape:export-ydpi="90"><tspan
sodipodi:role="line"
id="tspan5373-6-01"
x="164.26562"
- y="687.95599">false</tspan></text>
+ y="687.95599"
+ style="font-size:16px;line-height:1.25;font-family:sans-serif">false</tspan></text>
<text
xml:space="preserve"
- style="font-size:16px;font-style:normal;font-weight:normal;text-align:start;line-height:125%;letter-spacing:0px;word-spacing:0px;text-anchor:start;fill:#6d8e41;fill-opacity:1;stroke:none;font-family:Sans;-inkscape-font-specification:Sans"
+ style="font-style:normal;font-weight:normal;line-height:0%;font-family:sans-serif;-inkscape-font-specification:Sans;text-align:start;letter-spacing:0px;word-spacing:0px;text-anchor:start;fill:#6d8e41;fill-opacity:1;stroke:none"
x="164.26562"
y="771.62329"
id="text5371-2-2"
- sodipodi:linespacing="125%"><tspan
+ inkscape:export-xdpi="90"
+ inkscape:export-ydpi="90"><tspan
sodipodi:role="line"
id="tspan5373-6-33"
x="164.26562"
- y="771.62329">true</tspan></text>
+ y="771.62329"
+ style="font-size:16px;line-height:1.25;font-family:sans-serif">true</tspan></text>
<text
xml:space="preserve"
- style="font-size:16px;font-style:normal;font-weight:normal;text-align:start;line-height:125%;letter-spacing:0px;word-spacing:0px;text-anchor:start;fill:#6d8e41;fill-opacity:1;stroke:none;font-family:Sans;-inkscape-font-specification:Sans"
+ style="font-style:normal;font-weight:normal;line-height:0%;font-family:sans-serif;-inkscape-font-specification:Sans;text-align:start;letter-spacing:0px;word-spacing:0px;text-anchor:start;fill:#6d8e41;fill-opacity:1;stroke:none"
x="164.26562"
y="855.29053"
id="text5371-2-21"
- sodipodi:linespacing="125%"><tspan
+ inkscape:export-xdpi="90"
+ inkscape:export-ydpi="90"><tspan
sodipodi:role="line"
id="tspan5373-6-9"
x="164.26562"
- y="855.29053">true</tspan></text>
+ y="855.29053"
+ style="font-size:16px;line-height:1.25;font-family:sans-serif">true</tspan></text>
<g
transform="matrix(0,-1,1,0,-513.31414,353.05561)"
id="g5650-2">
@@ -1058,103 +1140,121 @@
</g>
<text
xml:space="preserve"
- style="font-size:16px;font-style:normal;font-weight:normal;text-align:center;line-height:125%;letter-spacing:0px;word-spacing:0px;text-anchor:middle;fill:#9b3b1c;fill-opacity:1;stroke:none;font-family:Sans;-inkscape-font-specification:Sans"
+ style="font-style:normal;font-weight:normal;line-height:0%;font-family:sans-serif;-inkscape-font-specification:Sans;text-align:center;letter-spacing:0px;word-spacing:0px;text-anchor:middle;fill:#9b3b1c;fill-opacity:1;stroke:none"
x="315.26172"
y="123.86062"
id="text5371-4-6"
- sodipodi:linespacing="125%"><tspan
+ inkscape:export-xdpi="90"
+ inkscape:export-ydpi="90"><tspan
sodipodi:role="line"
id="tspan5373-9-0"
x="315.26172"
- y="123.86062">false</tspan></text>
+ y="123.86062"
+ style="font-size:16px;line-height:1.25;font-family:sans-serif">false</tspan></text>
<text
xml:space="preserve"
- style="font-size:16px;font-style:normal;font-weight:normal;text-align:center;line-height:125%;letter-spacing:0px;word-spacing:0px;text-anchor:middle;fill:#9b3b1c;fill-opacity:1;stroke:none;font-family:Sans;-inkscape-font-specification:Sans"
+ style="font-style:normal;font-weight:normal;line-height:0%;font-family:sans-serif;-inkscape-font-specification:Sans;text-align:center;letter-spacing:0px;word-spacing:0px;text-anchor:middle;fill:#9b3b1c;fill-opacity:1;stroke:none"
x="315.26172"
y="207.30568"
id="text5371-4-7"
- sodipodi:linespacing="125%"><tspan
+ inkscape:export-xdpi="90"
+ inkscape:export-ydpi="90"><tspan
sodipodi:role="line"
id="tspan5373-9-6"
x="315.26172"
- y="207.30568">unknown*</tspan></text>
+ y="207.30568"
+ style="font-size:16px;line-height:1.25;font-family:sans-serif">unknown*</tspan></text>
<text
xml:space="preserve"
- style="font-size:16px;font-style:normal;font-weight:normal;text-align:center;line-height:125%;letter-spacing:0px;word-spacing:0px;text-anchor:middle;fill:#9b3b1c;fill-opacity:1;stroke:none;font-family:Sans;-inkscape-font-specification:Sans"
+ style="font-style:normal;font-weight:normal;line-height:0%;font-family:sans-serif;-inkscape-font-specification:Sans;text-align:center;letter-spacing:0px;word-spacing:0px;text-anchor:middle;fill:#9b3b1c;fill-opacity:1;stroke:none"
x="315.26172"
y="290.75076"
id="text5371-4-2"
- sodipodi:linespacing="125%"><tspan
+ inkscape:export-xdpi="90"
+ inkscape:export-ydpi="90"><tspan
sodipodi:role="line"
id="tspan5373-9-01"
x="315.26172"
- y="290.75076">true</tspan></text>
+ y="290.75076"
+ style="font-size:16px;line-height:1.25;font-family:sans-serif">true</tspan></text>
<text
xml:space="preserve"
- style="font-size:16px;font-style:normal;font-weight:normal;text-align:center;line-height:125%;letter-spacing:0px;word-spacing:0px;text-anchor:middle;fill:#9b3b1c;fill-opacity:1;stroke:none;font-family:Sans;-inkscape-font-specification:Sans"
+ style="font-style:normal;font-weight:normal;line-height:0%;font-family:sans-serif;-inkscape-font-specification:Sans;text-align:center;letter-spacing:0px;word-spacing:0px;text-anchor:middle;fill:#9b3b1c;fill-opacity:1;stroke:none"
x="315.26172"
y="374.19577"
id="text5371-4-3"
- sodipodi:linespacing="125%"><tspan
+ inkscape:export-xdpi="90"
+ inkscape:export-ydpi="90"><tspan
sodipodi:role="line"
id="tspan5373-9-62"
x="315.26172"
- y="374.19577">unallowed*</tspan></text>
+ y="374.19577"
+ style="font-size:16px;line-height:1.25;font-family:sans-serif">unallowed*</tspan></text>
<text
xml:space="preserve"
- style="font-size:16px;font-style:normal;font-weight:normal;text-align:center;line-height:125%;letter-spacing:0px;word-spacing:0px;text-anchor:middle;fill:#9b3b1c;fill-opacity:1;stroke:none;font-family:Sans;-inkscape-font-specification:Sans"
+ style="font-style:normal;font-weight:normal;line-height:0%;font-family:sans-serif;-inkscape-font-specification:Sans;text-align:center;letter-spacing:0px;word-spacing:0px;text-anchor:middle;fill:#9b3b1c;fill-opacity:1;stroke:none"
x="315.26172"
y="457.64084"
id="text5371-4-4"
- sodipodi:linespacing="125%"><tspan
+ inkscape:export-xdpi="90"
+ inkscape:export-ydpi="90"><tspan
sodipodi:role="line"
id="tspan5373-9-2"
x="315.26172"
- y="457.64084">true</tspan></text>
+ y="457.64084"
+ style="font-size:16px;line-height:1.25;font-family:sans-serif">true</tspan></text>
<text
xml:space="preserve"
- style="font-size:16px;font-style:normal;font-weight:normal;text-align:center;line-height:125%;letter-spacing:0px;word-spacing:0px;text-anchor:middle;fill:#9b3b1c;fill-opacity:1;stroke:none;font-family:Sans;-inkscape-font-specification:Sans"
+ style="font-style:normal;font-weight:normal;line-height:0%;font-family:sans-serif;-inkscape-font-specification:Sans;text-align:center;letter-spacing:0px;word-spacing:0px;text-anchor:middle;fill:#9b3b1c;fill-opacity:1;stroke:none"
x="315.26172"
y="541.08588"
id="text5371-4-5"
- sodipodi:linespacing="125%"><tspan
+ inkscape:export-xdpi="90"
+ inkscape:export-ydpi="90"><tspan
sodipodi:role="line"
id="tspan5373-9-09"
x="315.26172"
- y="541.08588">false*</tspan></text>
+ y="541.08588"
+ style="font-size:16px;line-height:1.25;font-family:sans-serif">false*</tspan></text>
<text
xml:space="preserve"
- style="font-size:16px;font-style:normal;font-weight:normal;text-align:center;line-height:125%;letter-spacing:0px;word-spacing:0px;text-anchor:middle;fill:#9b3b1c;fill-opacity:1;stroke:none;font-family:Sans;-inkscape-font-specification:Sans"
+ style="font-style:normal;font-weight:normal;line-height:0%;font-family:sans-serif;-inkscape-font-specification:Sans;text-align:center;letter-spacing:0px;word-spacing:0px;text-anchor:middle;fill:#9b3b1c;fill-opacity:1;stroke:none"
x="315.26172"
y="624.53094"
id="text5371-4-61"
- sodipodi:linespacing="125%"><tspan
+ inkscape:export-xdpi="90"
+ inkscape:export-ydpi="90"><tspan
sodipodi:role="line"
id="tspan5373-9-69"
x="315.26172"
- y="624.53094">true</tspan></text>
+ y="624.53094"
+ style="font-size:16px;line-height:1.25;font-family:sans-serif">true</tspan></text>
<text
xml:space="preserve"
- style="font-size:16px;font-style:normal;font-weight:normal;text-align:center;line-height:125%;letter-spacing:0px;word-spacing:0px;text-anchor:middle;fill:#9b3b1c;fill-opacity:1;stroke:none;font-family:Sans;-inkscape-font-specification:Sans"
+ style="font-style:normal;font-weight:normal;line-height:0%;font-family:sans-serif;-inkscape-font-specification:Sans;text-align:center;letter-spacing:0px;word-spacing:0px;text-anchor:middle;fill:#9b3b1c;fill-opacity:1;stroke:none"
x="315.26172"
y="707.97595"
id="text5371-4-58"
- sodipodi:linespacing="125%"><tspan
+ inkscape:export-xdpi="90"
+ inkscape:export-ydpi="90"><tspan
sodipodi:role="line"
id="tspan5373-9-5"
x="315.26172"
- y="707.97595">false</tspan></text>
+ y="707.97595"
+ style="font-size:16px;line-height:1.25;font-family:sans-serif">false</tspan></text>
<text
xml:space="preserve"
- style="font-size:16px;font-style:normal;font-weight:normal;text-align:center;line-height:125%;letter-spacing:0px;word-spacing:0px;text-anchor:middle;fill:#9b3b1c;fill-opacity:1;stroke:none;font-family:Sans;-inkscape-font-specification:Sans"
+ style="font-style:normal;font-weight:normal;line-height:0%;font-family:sans-serif;-inkscape-font-specification:Sans;text-align:center;letter-spacing:0px;word-spacing:0px;text-anchor:middle;fill:#9b3b1c;fill-opacity:1;stroke:none"
x="315.26172"
y="791.42102"
id="text5371-4-54"
- sodipodi:linespacing="125%"><tspan
+ inkscape:export-xdpi="90"
+ inkscape:export-ydpi="90"><tspan
sodipodi:role="line"
id="tspan5373-9-012"
x="315.26172"
- y="791.42102">false</tspan></text>
+ y="791.42102"
+ style="font-size:16px;line-height:1.25;font-family:sans-serif">false</tspan></text>
<rect
style="fill:#effab4;fill-opacity:1;fill-rule:nonzero;stroke:#c7d28c;stroke-width:3;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0"
id="rect5273-7-3"
@@ -1162,18 +1262,22 @@
height="34.728001"
x="392.03732"
y="121.24477"
- rx="15" />
+ rx="15"
+ inkscape:export-xdpi="90"
+ inkscape:export-ydpi="90" />
<text
xml:space="preserve"
- style="font-size:16px;font-style:normal;font-weight:normal;text-align:center;line-height:125%;letter-spacing:0px;word-spacing:0px;text-anchor:middle;fill:#77823c;fill-opacity:1;stroke:none;font-family:Sans;-inkscape-font-specification:Sans"
+ style="font-style:normal;font-weight:normal;line-height:0%;font-family:sans-serif;-inkscape-font-specification:Sans;text-align:center;letter-spacing:0px;word-spacing:0px;text-anchor:middle;fill:#77823c;fill-opacity:1;stroke:none"
x="524.53516"
y="143.02283"
id="text5371-6"
- sodipodi:linespacing="125%"><tspan
+ inkscape:export-xdpi="90"
+ inkscape:export-ydpi="90"><tspan
sodipodi:role="line"
x="524.53516"
y="143.02283"
- id="tspan18994">503 service unavailable</tspan></text>
+ id="tspan18994"
+ style="font-size:16px;line-height:1.25;font-family:sans-serif">503 service unavailable</tspan></text>
<rect
style="fill:#effab4;fill-opacity:1;fill-rule:nonzero;stroke:#c7d28c;stroke-width:3;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0"
id="rect5273-7-3-9"
@@ -1181,7 +1285,9 @@
height="34.728001"
x="392.03732"
y="204.93674"
- rx="15" />
+ rx="15"
+ inkscape:export-xdpi="90"
+ inkscape:export-ydpi="90" />
<rect
style="fill:#effab4;fill-opacity:1;fill-rule:nonzero;stroke:#c7d28c;stroke-width:3;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0"
id="rect5273-7-3-6"
@@ -1189,7 +1295,9 @@
height="34.728001"
x="392.03732"
y="288.62869"
- rx="15" />
+ rx="15"
+ inkscape:export-xdpi="90"
+ inkscape:export-ydpi="90" />
<rect
style="fill:#effab4;fill-opacity:1;fill-rule:nonzero;stroke:#c7d28c;stroke-width:3;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0"
id="rect5273-7-3-1"
@@ -1197,7 +1305,9 @@
height="34.728001"
x="392.03732"
y="372.32065"
- rx="15" />
+ rx="15"
+ inkscape:export-xdpi="90"
+ inkscape:export-ydpi="90" />
<rect
style="fill:#effab4;fill-opacity:1;fill-rule:nonzero;stroke:#c7d28c;stroke-width:3;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0"
id="rect5273-7-3-90"
@@ -1205,7 +1315,9 @@
height="34.728001"
x="392.03732"
y="456.01254"
- rx="15" />
+ rx="15"
+ inkscape:export-xdpi="90"
+ inkscape:export-ydpi="90" />
<rect
style="fill:#effab4;fill-opacity:1;fill-rule:nonzero;stroke:#c7d28c;stroke-width:3;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0"
id="rect5273-7-3-2"
@@ -1213,7 +1325,9 @@
height="34.728001"
x="392.03732"
y="539.70447"
- rx="15" />
+ rx="15"
+ inkscape:export-xdpi="90"
+ inkscape:export-ydpi="90" />
<rect
style="fill:#effab4;fill-opacity:1;fill-rule:nonzero;stroke:#c7d28c;stroke-width:3;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0"
id="rect5273-7-3-3"
@@ -1221,7 +1335,9 @@
height="34.728001"
x="392.03732"
y="623.39642"
- rx="15" />
+ rx="15"
+ inkscape:export-xdpi="90"
+ inkscape:export-ydpi="90" />
<rect
style="fill:#effab4;fill-opacity:1;fill-rule:nonzero;stroke:#c7d28c;stroke-width:3;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0"
id="rect5273-7-3-39"
@@ -1229,7 +1345,9 @@
height="34.728001"
x="392.03732"
y="707.08838"
- rx="15" />
+ rx="15"
+ inkscape:export-xdpi="90"
+ inkscape:export-ydpi="90" />
<rect
style="fill:#effab4;fill-opacity:1;fill-rule:nonzero;stroke:#c7d28c;stroke-width:3;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0"
id="rect5273-7-3-5"
@@ -1237,95 +1355,113 @@
height="34.728001"
x="392.03732"
y="790.78027"
- rx="15" />
+ rx="15"
+ inkscape:export-xdpi="90"
+ inkscape:export-ydpi="90" />
<text
xml:space="preserve"
- style="font-size:16px;font-style:normal;font-weight:normal;text-align:center;line-height:125%;letter-spacing:0px;word-spacing:0px;text-anchor:middle;fill:#77823c;fill-opacity:1;stroke:none;font-family:Sans;-inkscape-font-specification:Sans"
+ style="font-style:normal;font-weight:normal;line-height:0%;font-family:sans-serif;-inkscape-font-specification:Sans;text-align:center;letter-spacing:0px;word-spacing:0px;text-anchor:middle;fill:#77823c;fill-opacity:1;stroke:none"
x="524.26172"
y="227.80464"
id="text5371-7"
- sodipodi:linespacing="125%"><tspan
+ inkscape:export-xdpi="90"
+ inkscape:export-ydpi="90"><tspan
sodipodi:role="line"
id="tspan5373-69"
x="524.26172"
- y="227.80464">501 not implemented</tspan></text>
+ y="227.80464"
+ style="font-size:16px;line-height:1.25;font-family:sans-serif">501 not implemented</tspan></text>
<text
xml:space="preserve"
- style="font-size:16px;font-style:normal;font-weight:normal;text-align:center;line-height:125%;letter-spacing:0px;word-spacing:0px;text-anchor:middle;fill:#77823c;fill-opacity:1;stroke:none;font-family:Sans;-inkscape-font-specification:Sans"
+ style="font-style:normal;font-weight:normal;line-height:0%;font-family:sans-serif;-inkscape-font-specification:Sans;text-align:center;letter-spacing:0px;word-spacing:0px;text-anchor:middle;fill:#77823c;fill-opacity:1;stroke:none"
x="524.26172"
y="311.49661"
id="text5371-9"
- sodipodi:linespacing="125%"><tspan
+ inkscape:export-xdpi="90"
+ inkscape:export-ydpi="90"><tspan
sodipodi:role="line"
id="tspan5373-5"
x="524.26172"
- y="311.49661">414 request URI too long</tspan></text>
+ y="311.49661"
+ style="font-size:16px;line-height:1.25;font-family:sans-serif">414 request URI too long</tspan></text>
<text
xml:space="preserve"
- style="font-size:16px;font-style:normal;font-weight:normal;text-align:center;line-height:125%;letter-spacing:0px;word-spacing:0px;text-anchor:middle;fill:#77823c;fill-opacity:1;stroke:none;font-family:Sans;-inkscape-font-specification:Sans"
+ style="font-style:normal;font-weight:normal;line-height:0%;font-family:sans-serif;-inkscape-font-specification:Sans;text-align:center;letter-spacing:0px;word-spacing:0px;text-anchor:middle;fill:#77823c;fill-opacity:1;stroke:none"
x="524.26172"
y="395.18857"
id="text5371-43"
- sodipodi:linespacing="125%"><tspan
+ inkscape:export-xdpi="90"
+ inkscape:export-ydpi="90"><tspan
sodipodi:role="line"
id="tspan5373-3"
x="524.26172"
- y="395.18857">405 method not allowed</tspan></text>
+ y="395.18857"
+ style="font-size:16px;line-height:1.25;font-family:sans-serif">405 method not allowed</tspan></text>
<text
xml:space="preserve"
- style="font-size:16px;font-style:normal;font-weight:normal;text-align:center;line-height:125%;letter-spacing:0px;word-spacing:0px;text-anchor:middle;fill:#77823c;fill-opacity:1;stroke:none;font-family:Sans;-inkscape-font-specification:Sans"
+ style="font-style:normal;font-weight:normal;line-height:0%;font-family:sans-serif;-inkscape-font-specification:Sans;text-align:center;letter-spacing:0px;word-spacing:0px;text-anchor:middle;fill:#77823c;fill-opacity:1;stroke:none"
x="524.26172"
y="478.88046"
id="text5371-3"
- sodipodi:linespacing="125%"><tspan
+ inkscape:export-xdpi="90"
+ inkscape:export-ydpi="90"><tspan
sodipodi:role="line"
id="tspan5373-67"
x="524.26172"
- y="478.88046">400 bad request</tspan></text>
+ y="478.88046"
+ style="font-size:16px;line-height:1.25;font-family:sans-serif">400 bad request</tspan></text>
<text
xml:space="preserve"
- style="font-size:16px;font-style:normal;font-weight:normal;text-align:center;line-height:125%;letter-spacing:0px;word-spacing:0px;text-anchor:middle;fill:#77823c;fill-opacity:1;stroke:none;font-family:Sans;-inkscape-font-specification:Sans"
+ style="font-style:normal;font-weight:normal;line-height:0%;font-family:sans-serif;-inkscape-font-specification:Sans;text-align:center;letter-spacing:0px;word-spacing:0px;text-anchor:middle;fill:#77823c;fill-opacity:1;stroke:none"
x="524.26172"
y="562.57239"
id="text5371-1"
- sodipodi:linespacing="125%"><tspan
+ inkscape:export-xdpi="90"
+ inkscape:export-ydpi="90"><tspan
sodipodi:role="line"
id="tspan5373-53"
x="524.26172"
- y="562.57239">401 unauthorized</tspan></text>
+ y="562.57239"
+ style="font-size:16px;line-height:1.25;font-family:sans-serif">401 unauthorized</tspan></text>
<text
xml:space="preserve"
- style="font-size:16px;font-style:normal;font-weight:normal;text-align:center;line-height:125%;letter-spacing:0px;word-spacing:0px;text-anchor:middle;fill:#77823c;fill-opacity:1;stroke:none;font-family:Sans;-inkscape-font-specification:Sans"
+ style="font-style:normal;font-weight:normal;line-height:0%;font-family:sans-serif;-inkscape-font-specification:Sans;text-align:center;letter-spacing:0px;word-spacing:0px;text-anchor:middle;fill:#77823c;fill-opacity:1;stroke:none"
x="524.26172"
y="646.26434"
id="text5371-27"
- sodipodi:linespacing="125%"><tspan
+ inkscape:export-xdpi="90"
+ inkscape:export-ydpi="90"><tspan
sodipodi:role="line"
id="tspan5373-64"
x="524.26172"
- y="646.26434">403 forbidden</tspan></text>
+ y="646.26434"
+ style="font-size:16px;line-height:1.25;font-family:sans-serif">403 forbidden</tspan></text>
<text
xml:space="preserve"
- style="font-size:16px;font-style:normal;font-weight:normal;text-align:center;line-height:125%;letter-spacing:0px;word-spacing:0px;text-anchor:middle;fill:#77823c;fill-opacity:1;stroke:none;font-family:Sans;-inkscape-font-specification:Sans"
+ style="font-style:normal;font-weight:normal;line-height:0%;font-family:sans-serif;-inkscape-font-specification:Sans;text-align:center;letter-spacing:0px;word-spacing:0px;text-anchor:middle;fill:#77823c;fill-opacity:1;stroke:none"
x="524.26172"
y="729.9563"
id="text5371-21"
- sodipodi:linespacing="125%"><tspan
+ inkscape:export-xdpi="90"
+ inkscape:export-ydpi="90"><tspan
sodipodi:role="line"
id="tspan5373-58"
x="524.26172"
- y="729.9563">501 not implemented</tspan></text>
+ y="729.9563"
+ style="font-size:16px;line-height:1.25;font-family:sans-serif">501 not implemented</tspan></text>
<text
xml:space="preserve"
- style="font-size:16px;font-style:normal;font-weight:normal;text-align:center;line-height:125%;letter-spacing:0px;word-spacing:0px;text-anchor:middle;fill:#77823c;fill-opacity:1;stroke:none;font-family:Sans;-inkscape-font-specification:Sans"
+ style="font-style:normal;font-weight:normal;line-height:0%;font-family:sans-serif;-inkscape-font-specification:Sans;text-align:center;letter-spacing:0px;word-spacing:0px;text-anchor:middle;fill:#77823c;fill-opacity:1;stroke:none"
x="524.26172"
y="813.64819"
id="text5371-5"
- sodipodi:linespacing="125%"><tspan
+ inkscape:export-xdpi="90"
+ inkscape:export-ydpi="90"><tspan
sodipodi:role="line"
id="tspan5373-60"
x="524.26172"
- y="813.64819">413 request entity too large</tspan></text>
+ y="813.64819"
+ style="font-size:16px;line-height:1.25;font-family:sans-serif">413 request entity too large</tspan></text>
<rect
style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:#a9ca7d;stroke-width:2.44279909;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0"
id="rect5367"
@@ -1339,18 +1475,182 @@
inkscape:export-ydpi="89.926643" />
<text
xml:space="preserve"
- style="font-size:16px;font-style:normal;font-weight:normal;text-align:center;line-height:125%;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:middle;fill:#6d8e41;fill-opacity:1;stroke:none;font-family:Sans;-inkscape-font-specification:Sans"
+ style="font-style:normal;font-weight:normal;line-height:0%;font-family:sans-serif;-inkscape-font-specification:Sans;text-align:center;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:middle;fill:#6d8e41;fill-opacity:1;stroke:none"
x="-544.69421"
y="-354.17184"
id="text5371-2-3-0-7"
- sodipodi:linespacing="125%"
- transform="matrix(0,-1,1,0,0,0)"
+ transform="rotate(-90)"
inkscape:export-filename="/home/essen/extend/cowboy/guide/http_req_resp.png"
inkscape:export-xdpi="89.926643"
inkscape:export-ydpi="89.926643"><tspan
sodipodi:role="line"
id="tspan5373-6-7-3-9"
x="-544.69421"
- y="-354.17184">middlewares</tspan></text>
+ y="-354.17184"
+ style="font-size:16px;line-height:1.25;font-family:sans-serif">middlewares</tspan></text>
+ <g
+ transform="translate(205.92143,541.07893)"
+ id="g5650-1-3"
+ inkscape:export-xdpi="90"
+ inkscape:export-ydpi="90">
+ <path
+ inkscape:connector-curvature="0"
+ id="path5570-33-6"
+ d="m -57.78256,351.41962 v 52.3259"
+ style="opacity:0.8;fill:none;stroke:#6d8e41;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
+ <path
+ transform="matrix(0.58787746,0,0,0.58787746,73.160466,163.35774)"
+ inkscape:transform-center-y="2.1823437"
+ d="m -222.73865,430.10821 -12.85982,-22.27386 25.71964,0 z"
+ inkscape:randomized="0"
+ inkscape:rounded="0"
+ inkscape:flatsided="true"
+ sodipodi:arg2="2.6179939"
+ sodipodi:arg1="1.5707963"
+ sodipodi:r2="7.4246211"
+ sodipodi:r1="14.849242"
+ sodipodi:cy="415.25897"
+ sodipodi:cx="-222.73865"
+ sodipodi:sides="3"
+ id="path5576-2-7"
+ style="opacity:0.8;fill:#6d8e41;fill-opacity:1;fill-rule:nonzero;stroke:#6d8e41;stroke-width:0;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+ sodipodi:type="star" />
+ </g>
+ <g
+ transform="rotate(-90,404.08225,429.75539)"
+ id="g5650-2-12-5"
+ inkscape:export-xdpi="90"
+ inkscape:export-ydpi="90">
+ <path
+ inkscape:connector-curvature="0"
+ id="path5570-2-6-3"
+ d="M -57.78256,275.13761 V 404.27753"
+ style="opacity:0.8;fill:none;stroke:#9b3b1c;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
+ <path
+ transform="matrix(0.58787746,0,0,0.58787746,73.160466,163.35774)"
+ inkscape:transform-center-y="2.1823437"
+ d="m -222.73865,430.10821 -12.85982,-22.27386 25.71964,0 z"
+ inkscape:randomized="0"
+ inkscape:rounded="0"
+ inkscape:flatsided="true"
+ sodipodi:arg2="2.6179939"
+ sodipodi:arg1="1.5707963"
+ sodipodi:r2="7.4246211"
+ sodipodi:r1="14.849242"
+ sodipodi:cy="415.25897"
+ sodipodi:cx="-222.73865"
+ sodipodi:sides="3"
+ id="path5576-12-04-5"
+ style="opacity:0.8;fill:#9b3b1c;fill-opacity:1;fill-rule:nonzero;stroke:#9b3b1c;stroke-width:0;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+ sodipodi:type="star" />
+ </g>
+ <rect
+ style="fill:#d1f2a5;fill-opacity:1;fill-rule:nonzero;stroke:#a9ca7d;stroke-width:3;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0"
+ id="rect5273-1-3"
+ width="210.17955"
+ height="35.209244"
+ x="43.049091"
+ y="874.01562"
+ rx="15"
+ inkscape:export-xdpi="90"
+ inkscape:export-ydpi="90" />
+ <rect
+ style="fill:#d1f2a5;fill-opacity:1;fill-rule:nonzero;stroke:#a9ca7d;stroke-width:3;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+ id="rect5273-1-3-2"
+ width="210.17955"
+ height="35.209244"
+ x="43.049088"
+ y="957.68286"
+ rx="15"
+ inkscape:export-xdpi="90"
+ inkscape:export-ydpi="90" />
+ <text
+ xml:space="preserve"
+ style="font-style:normal;font-weight:normal;line-height:0%;font-family:sans-serif;-inkscape-font-specification:Sans;text-align:center;letter-spacing:0px;word-spacing:0px;text-anchor:middle;fill:#6d8e41;fill-opacity:1;stroke:none"
+ x="147.8381"
+ y="895.81165"
+ id="text5371-2-5-9"
+ inkscape:export-xdpi="90"
+ inkscape:export-ydpi="90"><tspan
+ sodipodi:role="line"
+ id="tspan5373-6-1-1"
+ x="147.8381"
+ y="895.81165"
+ style="font-size:16px;line-height:1.25;font-family:sans-serif">rate_limited</tspan></text>
+ <text
+ xml:space="preserve"
+ style="font-style:normal;font-weight:normal;line-height:0%;font-family:sans-serif;-inkscape-font-specification:Sans;text-align:center;letter-spacing:0px;word-spacing:0px;text-anchor:middle;fill:#6d8e41;fill-opacity:1;stroke:none"
+ x="148.5881"
+ y="979.47888"
+ id="text5371-2-1-2"
+ inkscape:export-xdpi="90"
+ inkscape:export-ydpi="90"><tspan
+ sodipodi:role="line"
+ id="tspan5373-6-71-7"
+ x="148.5881"
+ y="979.47888"
+ style="font-size:16px;line-height:1.25;font-family:sans-serif">...</tspan></text>
+ <text
+ xml:space="preserve"
+ style="font-style:normal;font-weight:normal;line-height:0%;font-family:sans-serif;-inkscape-font-specification:Sans;text-align:start;letter-spacing:0px;word-spacing:0px;text-anchor:start;fill:#6d8e41;fill-opacity:1;stroke:none"
+ x="164.26562"
+ y="855.29053"
+ id="text5371-2-2-0"
+ inkscape:export-xdpi="90"
+ inkscape:export-ydpi="90"><tspan
+ sodipodi:role="line"
+ id="tspan5373-6-33-9"
+ x="164.26562"
+ y="855.29053"
+ style="font-size:16px;line-height:1.25;font-family:sans-serif">true</tspan></text>
+ <text
+ xml:space="preserve"
+ style="font-style:normal;font-weight:normal;line-height:0%;font-family:sans-serif;-inkscape-font-specification:Sans;text-align:start;letter-spacing:0px;word-spacing:0px;text-anchor:start;fill:#6d8e41;fill-opacity:1;stroke:none"
+ x="164.26562"
+ y="938.95776"
+ id="text5371-2-21-3"
+ inkscape:export-xdpi="90"
+ inkscape:export-ydpi="90"><tspan
+ sodipodi:role="line"
+ id="tspan5373-6-9-6"
+ x="164.26562"
+ y="938.95776"
+ style="font-size:16px;line-height:1.25;font-family:sans-serif">false</tspan></text>
+ <text
+ xml:space="preserve"
+ style="font-style:normal;font-weight:normal;line-height:0%;font-family:sans-serif;-inkscape-font-specification:Sans;text-align:center;letter-spacing:0px;word-spacing:0px;text-anchor:middle;fill:#9b3b1c;fill-opacity:1;stroke:none"
+ x="315.26172"
+ y="875.08826"
+ id="text5371-4-54-0"
+ inkscape:export-xdpi="90"
+ inkscape:export-ydpi="90"><tspan
+ sodipodi:role="line"
+ id="tspan5373-9-012-6"
+ x="315.26172"
+ y="875.08826"
+ style="font-size:16px;line-height:1.25;font-family:sans-serif">true*</tspan></text>
+ <rect
+ style="fill:#effab4;fill-opacity:1;fill-rule:nonzero;stroke:#c7d28c;stroke-width:3;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+ id="rect5273-7-3-5-2"
+ width="264.92532"
+ height="34.728001"
+ x="392.03729"
+ y="874.44751"
+ rx="15"
+ inkscape:export-xdpi="90"
+ inkscape:export-ydpi="90" />
+ <text
+ xml:space="preserve"
+ style="font-style:normal;font-weight:normal;line-height:0%;font-family:sans-serif;-inkscape-font-specification:Sans;text-align:center;letter-spacing:0px;word-spacing:0px;text-anchor:middle;fill:#77823c;fill-opacity:1;stroke:none"
+ x="524.26172"
+ y="897.31543"
+ id="text5371-5-6"
+ inkscape:export-xdpi="90"
+ inkscape:export-ydpi="90"><tspan
+ sodipodi:role="line"
+ id="tspan5373-60-1"
+ x="524.26172"
+ y="897.31543"
+ style="font-size:16px;line-height:1.25;font-family:sans-serif">429 too many requests</tspan></text>
</g>
</svg>
diff --git a/doc/src/manual/cowboy_rest.asciidoc b/doc/src/manual/cowboy_rest.asciidoc
index dd5fa7e..4babcc5 100644
--- a/doc/src/manual/cowboy_rest.asciidoc
+++ b/doc/src/manual/cowboy_rest.asciidoc
@@ -603,6 +603,30 @@ release.
// @todo Add a way to switch to loop handler for streaming the body.
+=== rate_limited
+
+[source,erlang]
+----
+rate_limited(Req, State) -> {Result, Req, State}
+
+Result :: false | {true, RetryAfter}
+RetryAfter :: non_neg_integer() | calendar:datetime()
+Default - false
+----
+
+Return whether the user is rate limited.
+
+This function can be used to temporarily restrict
+access to a resource when the user has issued too
+many requests.
+
+When the resource is rate limited the `RetryAfter`
+value will be sent in the retry-after header for the
+'429 Too Many Requests' response. It indicates when
+the resource will become available again and can be
+specified as a number of seconds in the future or a
+specific date/time.
+
=== resource_exists
[source,erlang]
@@ -696,6 +720,7 @@ listed here, like the authorization header.
== Changelog
+* *2.6*: The callback `rate_limited` was added.
* *2.1*: The `switch_handler` return value was added.
* *1.0*: Behavior introduced.
diff --git a/src/cowboy_rest.erl b/src/cowboy_rest.erl
index 512dc5b..8ab576c 100644
--- a/src/cowboy_rest.erl
+++ b/src/cowboy_rest.erl
@@ -180,6 +180,13 @@
when Req::cowboy_req:req(), State::any().
-optional_callbacks([previously_existed/2]).
+-callback rate_limited(Req, State)
+ -> {{true, non_neg_integer() | calendar:datetime()} | false, Req, State}
+ | {stop, Req, State}
+ | {switch_handler(), Req, State}
+ when Req::cowboy_req:req(), State::any().
+-optional_callbacks([rate_limited/2]).
+
-callback resource_exists(Req, State)
-> {boolean(), Req, State}
| {stop, Req, State}
@@ -363,7 +370,28 @@ is_authorized(Req, State) ->
end.
forbidden(Req, State) ->
- expect(Req, State, forbidden, false, fun valid_content_headers/2, 403).
+ expect(Req, State, forbidden, false, fun rate_limited/2, 403).
+
+rate_limited(Req, State) ->
+ case call(Req, State, rate_limited) of
+ no_call ->
+ valid_content_headers(Req, State);
+ {stop, Req2, HandlerState} ->
+ terminate(Req2, State#state{handler_state=HandlerState});
+ {Switch, Req2, HandlerState} when element(1, Switch) =:= switch_handler ->
+ switch_handler(Switch, Req2, HandlerState);
+ {false, Req2, HandlerState} ->
+ valid_content_headers(Req2, State#state{handler_state=HandlerState});
+ {{true, RetryAfter0}, Req2, HandlerState} ->
+ RetryAfter = if
+ is_integer(RetryAfter0), RetryAfter0 >= 0 ->
+ integer_to_binary(RetryAfter0);
+ is_tuple(RetryAfter0) ->
+ cowboy_clock:rfc1123(RetryAfter0)
+ end,
+ Req3 = cowboy_req:set_resp_header(<<"retry-after">>, RetryAfter, Req2),
+ respond(Req3, State#state{handler_state=HandlerState}, 429)
+ end.
valid_content_headers(Req, State) ->
expect(Req, State, valid_content_headers, true,
diff --git a/test/handlers/rate_limited_h.erl b/test/handlers/rate_limited_h.erl
new file mode 100644
index 0000000..e54249c
--- /dev/null
+++ b/test/handlers/rate_limited_h.erl
@@ -0,0 +1,24 @@
+%% This module does rate limiting based on the query string value.
+
+-module(rate_limited_h).
+
+-export([init/2]).
+-export([rate_limited/2]).
+-export([content_types_provided/2]).
+-export([get_text_plain/2]).
+
+init(Req, State) ->
+ {cowboy_rest, Req, State}.
+
+rate_limited(Req=#{qs := <<"false">>}, State) ->
+ {false, Req, State};
+rate_limited(Req=#{qs := <<"true-date">>}, State) ->
+ {{true, {{2222, 2, 22}, {11, 11, 11}}}, Req, State};
+rate_limited(Req=#{qs := <<"true">>}, State) ->
+ {{true, 3600}, Req, State}.
+
+content_types_provided(Req, State) ->
+ {[{{<<"text">>, <<"plain">>, []}, get_text_plain}], Req, State}.
+
+get_text_plain(Req, State) ->
+ {<<"This is REST!">>, Req, State}.
diff --git a/test/rest_handler_SUITE.erl b/test/rest_handler_SUITE.erl
index 93532fe..09ce8fb 100644
--- a/test/rest_handler_SUITE.erl
+++ b/test/rest_handler_SUITE.erl
@@ -48,6 +48,7 @@ init_dispatch(_) ->
{"/charset_in_content_types_provided_implicit_no_callback",
charset_in_content_types_provided_implicit_no_callback_h, []},
{"/provide_callback_missing", provide_callback_missing_h, []},
+ {"/rate_limited", rate_limited_h, []},
{"/switch_handler", switch_handler_h, run},
{"/switch_handler_opts", switch_handler_h, hibernate}
]}]).
@@ -287,6 +288,31 @@ provide_callback_missing(Config) ->
{response, fin, 500, _} = gun:await(ConnPid, Ref),
ok.
+rate_limited(Config) ->
+ doc("A 429 response must be sent when the rate_limited callback returns true. "
+ "The retry-after header is specified as an integer."),
+ ConnPid = gun_open(Config),
+ Ref = gun:get(ConnPid, "/rate_limited?true", [{<<"accept-encoding">>, <<"gzip">>}]),
+ {response, fin, 429, Headers} = gun:await(ConnPid, Ref),
+ {_, <<"3600">>} = lists:keyfind(<<"retry-after">>, 1, Headers),
+ ok.
+
+rate_limited_datetime(Config) ->
+ doc("A 429 response must be sent when the rate_limited callback returns true. "
+ "The retry-after header is specified as a date/time tuple."),
+ ConnPid = gun_open(Config),
+ Ref = gun:get(ConnPid, "/rate_limited?true-date", [{<<"accept-encoding">>, <<"gzip">>}]),
+ {response, fin, 429, Headers} = gun:await(ConnPid, Ref),
+ {_, <<"Fri, 22 Feb 2222 11:11:11 GMT">>} = lists:keyfind(<<"retry-after">>, 1, Headers),
+ ok.
+
+rate_not_limited(Config) ->
+ doc("A success response must be sent when the rate_limited callback returns false."),
+ ConnPid = gun_open(Config),
+ Ref = gun:get(ConnPid, "/rate_limited?false", [{<<"accept-encoding">>, <<"gzip">>}]),
+ {response, nofin, 200, _} = gun:await(ConnPid, Ref),
+ ok.
+
switch_handler(Config) ->
doc("Switch REST to loop handler for streaming the response body."),
ConnPid = gun_open(Config),