From 8c9ad7bf078871295e391f416bfcb10c9156a35f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lo=C3=AFc=20Hoguin?= Date: Sun, 4 Nov 2018 11:51:35 +0100 Subject: Add the rate_limited/2 REST callback --- doc/src/guide/resource_design.asciidoc | 3 + doc/src/guide/rest_handlers.asciidoc | 1 + doc/src/guide/rest_start.png | Bin 105640 -> 110820 bytes doc/src/guide/rest_start.svg | 678 ++++++++++++++++++++++++--------- doc/src/manual/cowboy_rest.asciidoc | 25 ++ 5 files changed, 518 insertions(+), 189 deletions(-) (limited to 'doc') 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 Binary files a/doc/src/guide/rest_start.png and b/doc/src/guide/rest_start.png 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" /> + id="g5650-7" + inkscape:export-xdpi="90" + inkscape:export-ydpi="90"> + id="g5650-9" + inkscape:export-xdpi="90" + inkscape:export-ydpi="90"> + id="g5650-0" + inkscape:export-xdpi="90" + inkscape:export-ydpi="90"> + id="g5650-94" + inkscape:export-xdpi="90" + inkscape:export-ydpi="90"> + id="g5650-93" + inkscape:export-xdpi="90" + inkscape:export-ydpi="90"> + id="g5650-3" + inkscape:export-xdpi="90" + inkscape:export-ydpi="90"> + id="g5650-6" + inkscape:export-xdpi="90" + inkscape:export-ydpi="90"> + id="g5650-34" + inkscape:export-xdpi="90" + inkscape:export-ydpi="90"> + id="g5650-5" + inkscape:export-xdpi="90" + inkscape:export-ydpi="90"> + id="g5650-1" + inkscape:export-xdpi="90" + inkscape:export-ydpi="90"> + id="g5650-2-0" + inkscape:export-xdpi="90" + inkscape:export-ydpi="90"> + id="g5650-2-6" + inkscape:export-xdpi="90" + inkscape:export-ydpi="90"> + id="g5650-2-4" + inkscape:export-xdpi="90" + inkscape:export-ydpi="90"> + id="g5650-2-04" + inkscape:export-xdpi="90" + inkscape:export-ydpi="90"> + id="g5650-2-8" + inkscape:export-xdpi="90" + inkscape:export-ydpi="90"> + id="g5650-2-1" + inkscape:export-xdpi="90" + inkscape:export-ydpi="90"> + id="g5650-2-3" + inkscape:export-xdpi="90" + inkscape:export-ydpi="90"> + id="g5650-2-44" + inkscape:export-xdpi="90" + inkscape:export-ydpi="90"> + id="g5650-2-12" + inkscape:export-xdpi="90" + inkscape:export-ydpi="90"> + rx="15" + inkscape:export-xdpi="90" + inkscape:export-ydpi="90" /> + rx="15" + inkscape:export-xdpi="90" + inkscape:export-ydpi="90" /> + rx="15" + inkscape:export-xdpi="90" + inkscape:export-ydpi="90" /> + rx="15" + inkscape:export-xdpi="90" + inkscape:export-ydpi="90" /> + rx="15" + inkscape:export-xdpi="90" + inkscape:export-ydpi="90" /> + rx="15" + inkscape:export-xdpi="90" + inkscape:export-ydpi="90" /> + rx="15" + inkscape:export-xdpi="90" + inkscape:export-ydpi="90" /> - + rx="15" + inkscape:export-xdpi="90" + inkscape:export-ydpi="90" /> + rx="15" + inkscape:export-xdpi="90" + inkscape:export-ydpi="90" /> @@ -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" /> some text + y="114.39204" + style="font-size:16px;line-height:1.25;font-family:sans-serif">some text some text + y="53.112247" + style="font-size:16px;line-height:1.25;font-family:sans-serif">some text uri_too_long + y="310.19913" + style="font-size:16px;line-height:1.25;font-family:sans-serif">uri_too_long malformed_request + y="477.47531" + style="font-size:16px;line-height:1.25;font-family:sans-serif">malformed_request some text + y="236.95154" + style="font-size:16px;line-height:1.25;font-family:sans-serif">some text init + id="tspan17171" + style="font-size:16px;line-height:1.25;font-family:sans-serif">init is_authorized + y="561.14258" + style="font-size:16px;line-height:1.25;font-family:sans-serif">is_authorized forbidden + y="646.58331" + style="font-size:16px;line-height:1.25;font-family:sans-serif">forbidden valid_content_headers + y="728.47717" + style="font-size:16px;line-height:1.25;font-family:sans-serif">valid_content_headers valid_entity_length + y="812.14441" + style="font-size:16px;line-height:1.25;font-family:sans-serif">valid_entity_length ... + y="895.81165" + style="font-size:16px;line-height:1.25;font-family:sans-serif">... service_available + y="142.80627" + style="font-size:16px;line-height:1.25;font-family:sans-serif">service_available known_methods + y="226.4736" + style="font-size:16px;line-height:1.25;font-family:sans-serif">known_methods allowed_methods + y="393.80801" + style="font-size:16px;line-height:1.25;font-family:sans-serif">allowed_methods true + y="185.95248" + style="font-size:16px;line-height:1.25;font-family:sans-serif">true known* + y="269.61978" + style="font-size:16px;line-height:1.25;font-family:sans-serif">known* false + y="353.28702" + style="font-size:16px;line-height:1.25;font-family:sans-serif">false allowed* + y="436.95425" + style="font-size:16px;line-height:1.25;font-family:sans-serif">allowed* false + y="520.62152" + style="font-size:16px;line-height:1.25;font-family:sans-serif">false true + y="604.28876" + style="font-size:16px;line-height:1.25;font-family:sans-serif">true false + y="687.95599" + style="font-size:16px;line-height:1.25;font-family:sans-serif">false true + y="771.62329" + style="font-size:16px;line-height:1.25;font-family:sans-serif">true true + y="855.29053" + style="font-size:16px;line-height:1.25;font-family:sans-serif">true @@ -1058,103 +1140,121 @@ false + y="123.86062" + style="font-size:16px;line-height:1.25;font-family:sans-serif">false unknown* + y="207.30568" + style="font-size:16px;line-height:1.25;font-family:sans-serif">unknown* true + y="290.75076" + style="font-size:16px;line-height:1.25;font-family:sans-serif">true unallowed* + y="374.19577" + style="font-size:16px;line-height:1.25;font-family:sans-serif">unallowed* true + y="457.64084" + style="font-size:16px;line-height:1.25;font-family:sans-serif">true false* + y="541.08588" + style="font-size:16px;line-height:1.25;font-family:sans-serif">false* true + y="624.53094" + style="font-size:16px;line-height:1.25;font-family:sans-serif">true false + y="707.97595" + style="font-size:16px;line-height:1.25;font-family:sans-serif">false false + y="791.42102" + style="font-size:16px;line-height:1.25;font-family:sans-serif">false + rx="15" + inkscape:export-xdpi="90" + inkscape:export-ydpi="90" /> 503 service unavailable + id="tspan18994" + style="font-size:16px;line-height:1.25;font-family:sans-serif">503 service unavailable + rx="15" + inkscape:export-xdpi="90" + inkscape:export-ydpi="90" /> + rx="15" + inkscape:export-xdpi="90" + inkscape:export-ydpi="90" /> + rx="15" + inkscape:export-xdpi="90" + inkscape:export-ydpi="90" /> + rx="15" + inkscape:export-xdpi="90" + inkscape:export-ydpi="90" /> + rx="15" + inkscape:export-xdpi="90" + inkscape:export-ydpi="90" /> + rx="15" + inkscape:export-xdpi="90" + inkscape:export-ydpi="90" /> + rx="15" + inkscape:export-xdpi="90" + inkscape:export-ydpi="90" /> + rx="15" + inkscape:export-xdpi="90" + inkscape:export-ydpi="90" /> 501 not implemented + y="227.80464" + style="font-size:16px;line-height:1.25;font-family:sans-serif">501 not implemented 414 request URI too long + y="311.49661" + style="font-size:16px;line-height:1.25;font-family:sans-serif">414 request URI too long 405 method not allowed + y="395.18857" + style="font-size:16px;line-height:1.25;font-family:sans-serif">405 method not allowed 400 bad request + y="478.88046" + style="font-size:16px;line-height:1.25;font-family:sans-serif">400 bad request 401 unauthorized + y="562.57239" + style="font-size:16px;line-height:1.25;font-family:sans-serif">401 unauthorized 403 forbidden + y="646.26434" + style="font-size:16px;line-height:1.25;font-family:sans-serif">403 forbidden 501 not implemented + y="729.9563" + style="font-size:16px;line-height:1.25;font-family:sans-serif">501 not implemented 413 request entity too large + y="813.64819" + style="font-size:16px;line-height:1.25;font-family:sans-serif">413 request entity too large middlewares + y="-354.17184" + style="font-size:16px;line-height:1.25;font-family:sans-serif">middlewares + + + + + + + + + + + rate_limited + ... + true + false + true* + + 429 too many requests 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. -- cgit v1.2.3