Skip to content

Instantly share code, notes, and snippets.

@simonprev
Last active August 29, 2015 14:07
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save simonprev/752217341e89afe1137d to your computer and use it in GitHub Desktop.
Save simonprev/752217341e89afe1137d to your computer and use it in GitHub Desktop.
Redis-backed rate limiting API
defmodule MyProject.Plugs.RateLimting do
import Plug.Conn
alias Exredis.Api, as: Redis
@ttl 60 # seconds
@limit 5 # requests count
@header_limit "x-rate-limit-limit"
@header_remaining "x-rate-limit-remaining"
@header_reset "x-rate-limit-reset"
@key_prefix "rate-limit:"
@request_identifier "authorization"
def init(_), do: Exredis.start
def call(conn, _) do
key(conn)
|> get_remainings_and_ttl
|> put_new_headers(conn)
end
# Fetch the current ttl
# Increment the key
# Set an expire time to the key if the ttl is over
# Return the remaining count and the ttl.
defp get_remainings_and_ttl(key) do
ttl_over = Redis.ttl(key) < 0
count = Redis.incr(key)
if ttl_over, do: Redis.expire(key, @ttl)
{@limit - count, Redis.ttl(key)}
end
# When remainings is less than 0, returns 429 Too Many Requests.
defp put_new_headers({remainings, _}, conn) when remainings < 0, do: conn |> send_resp(429, "") |> halt
defp put_new_headers({remainings, new_ttl}, conn) do
conn
|> put_resp_header(@header_limit, "#{@limit}")
|> put_resp_header(@header_remaining, "#{remainings}")
|> put_resp_header(@header_reset, "#{new_ttl}")
end
defp key(conn) do
@key_prefix <> authorization_header(conn)
end
defp authorization_header(conn) do
get_req_header(conn, @request_identifier) |> Enum.at(0) || ""
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment