Add a step to cowboy_rest to support HTTP 429. #294

Open
wants to merge 1 commit into
from

Projects

None yet

5 participants

@betawaffle

See rfc6585 for an explaination of status code 429.

The callback for that example might look something like this:

-define(RATE_LIMIT_MSG,
        "I only allow 50 requests per hour "
        "to this Web site per logged in user.  "
        "Try again soon.").

rate_limited(Req, State) ->
    ReqCount = user_requests(Req, 3600),
    if
        ReqCount > 50 ->
            Body = error_html(<<"Too Many Requests">>,
                              <<?RATE_LIMIT_MSG>>),
            {ok, Req2} = cowboy:set_resp_body(Body, Req),
            {{true, 3600}, Req2, State};
        true ->
            {false, Req, State}
    end.

error_html(Title, Message) ->
    <<"<html>"
      "<head>"
      "<title>", Title/binary, "</title>"
      "</head>"
      "<body>"
      "<h1>", Title/binary, "</h1>"
      "<p>", Message/binary, "</p>"
      "</body>"
      "</html>">>.

Bring on the feedback!

@JanHenryNystrom

This seems to breaks the normal style of the callbacks where headers are added explicitly, I would prefer the alternative I have provided below.

I am not at all keen on the idea of replacing 503 with 429 everywhere since it is not the same, although both have Retry-After suggested, 503 in rfc2616 and 429 in rfc6585. Where the 429 should fit in the webmachine flow is not given since when a rate is too high can be argued to belong in several places depending on your application, especially if you want to provide information on other errors in the request even if the request would have to run into a rate limit situation if it had been properly formed. This is very much the case when you can only determine rate exceeded for well formed requests, which is the case in the application I am working on at the moment.

So to summarize, I believe that what is needed is a way to plugin extension return codes not currently covered by the flow rather than replacing 503 with 429. This is of course a much harder nut to crack.

rate_limited(Req, State) ->
    case user_requests(Req, 3600) of
        ReqCount when ReqCount > 50 ->
            Body = error_html(<<"Too Many Requests">>, <<?RATE_LIMIT_MSG>>),
            Req2 = cowboy_req:set_resp_body(Body, Req),
            Req3 = set_resp_header(<<"Retry-After">>, <<"3600">>, Req2),
            {true, Req3, State};
        _ ->
            {false, Req, State}
    end.
@betawaffle

I agree. This needs to be thought about more.

@betawaffle betawaffle closed this Oct 31, 2012
@essen
Nine Nines member

Please leave open to let people think. :)

@essen essen reopened this Oct 31, 2012
@betawaffle

Okie dokie.

@essen
Nine Nines member

I am interesting in having this in in some form. I am not sure where to put this in the flow diagram, and it will need to be thoroughly tested. I'll come back after more thoughts. :)

@bullno1

IMO, the whole point of using cowboy_rest is to avoid using headers and error code directly. I prefer betawaffle's suggestion.

In term of flow, I think putting this check between node G7 and F7 of this diagram https://raw.github.com/wiki/basho/webmachine/images/http-headers-status-v3.png, would be nice. Since preliminary validation and authentication are already done, the handler would have enough information to decide whether to reject the request.

Alternatively, we can push it as early as between B4 and B5. The content type is needed since you may want to limit by content type. e.g: html requires rendering and thus, has a lower rate limit, the resource may already be stored internally as json and thus, has a higher rate limit.

Disclaimer: I have never read any rfcs.

@essen
Nine Nines member

I think this should be right after authentication.

@tnt-dev

Any news on this?

@betawaffle

@tnt-dev I have no immediate plans to work on this. It would have to come from someone else.

@essen
Nine Nines member

This is something that I want to add in Cowboy 2 or soon after that, depending on time constraints.

@essen essen modified the milestone: 2.0.0 Aug 18, 2015
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment