summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--lib/farside/application.ex3
-rw-r--r--lib/farside/router.ex1
-rw-r--r--lib/farside/throttle.ex19
-rw-r--r--mix.exs5
-rw-r--r--mix.lock1
-rw-r--r--test/farside_test.exs33
6 files changed, 38 insertions, 24 deletions
diff --git a/lib/farside/application.ex b/lib/farside/application.ex
index 708ff0d..fd199e1 100644
--- a/lib/farside/application.ex
+++ b/lib/farside/application.ex
@@ -8,7 +8,8 @@ defmodule Farside.Application do
def start(_type, _args) do
children = [
Plug.Cowboy.child_spec(scheme: :http, plug: Farside.Router, options: [port: 4001]),
- {Redix, {@redis_conn, [name: :redix]}}
+ {Redix, {@redis_conn, [name: :redix]}},
+ {PlugAttack.Storage.Ets, name: Farside.Throttle.Storage, clean_period: 60_000}
]
opts = [strategy: :one_for_one, name: Farside.Supervisor]
diff --git a/lib/farside/router.ex b/lib/farside/router.ex
index 7f31e04..e2014b2 100644
--- a/lib/farside/router.ex
+++ b/lib/farside/router.ex
@@ -3,6 +3,7 @@ defmodule Farside.Router do
use Plug.Router
+ plug(Farside.Throttle)
plug(:match)
plug(:dispatch)
diff --git a/lib/farside/throttle.ex b/lib/farside/throttle.ex
new file mode 100644
index 0000000..fc8b591
--- /dev/null
+++ b/lib/farside/throttle.ex
@@ -0,0 +1,19 @@
+defmodule Farside.Throttle do
+ import Plug.Conn
+ use PlugAttack
+
+ rule "throttle per ip", conn do
+ # throttle to 1 request per second
+ throttle conn.remote_ip,
+ period: 1_000, limit: 1,
+ storage: {PlugAttack.Storage.Ets, Farside.Throttle.Storage}
+ end
+
+ def allow_action(conn, _data, _opts), do: conn
+
+ def block_action(conn, _data, _opts) do
+ conn
+ |> send_resp(:forbidden, "Exceeded rate limit\n")
+ |> halt
+ end
+end
diff --git a/mix.exs b/mix.exs
index fbbbcc4..f6f91cd 100644
--- a/mix.exs
+++ b/mix.exs
@@ -22,11 +22,12 @@ defmodule Farside.MixProject do
# Run "mix help deps" to learn about dependencies.
defp deps do
[
+ {:httpoison, "~> 1.8"},
{:jason, "~> 1.1"},
+ {:plug_attack, "~> 0.4.2"},
{:plug_cowboy, "~> 2.0"},
{:poison, "~> 5.0"},
- {:httpoison, "~> 1.8"},
- {:redix, "~> 1.1"}
+ {:redix, "~> 1.1"},
]
end
end
diff --git a/mix.lock b/mix.lock
index bb98ecc..26a5cc7 100644
--- a/mix.lock
+++ b/mix.lock
@@ -14,6 +14,7 @@
"parse_trans": {:hex, :parse_trans, "3.3.1", "16328ab840cc09919bd10dab29e431da3af9e9e7e7e6f0089dd5a2d2820011d8", [:rebar3], [], "hexpm", "07cd9577885f56362d414e8c4c4e6bdf10d43a8767abb92d24cbe8b24c54888b"},
"phoenix_view": {:hex, :phoenix_view, "1.0.0", "fea71ecaaed71178b26dd65c401607de5ec22e2e9ef141389c721b3f3d4d8011", [:mix], [{:phoenix_html, "~> 2.14.2 or ~> 3.0", [hex: :phoenix_html, repo: "hexpm", optional: true]}], "hexpm", "82be3e2516f5633220246e2e58181282c71640dab7afc04f70ad94253025db0c"},
"plug": {:hex, :plug, "1.12.1", "645678c800601d8d9f27ad1aebba1fdb9ce5b2623ddb961a074da0b96c35187d", [:mix], [{:mime, "~> 1.0 or ~> 2.0", [hex: :mime, repo: "hexpm", optional: false]}, {:plug_crypto, "~> 1.1.1 or ~> 1.2", [hex: :plug_crypto, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4.3 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "d57e799a777bc20494b784966dc5fbda91eb4a09f571f76545b72a634ce0d30b"},
+ "plug_attack": {:hex, :plug_attack, "0.4.3", "88e6c464d68b1491aa083a0347d59d58ba71a7e591a7f8e1b675e8c7792a0ba8", [:mix], [{:plug, "~> 1.0", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm", "9ed6fb8a6f613a36040f2875130a21187126c5625092f24bc851f7f12a8cbdc1"},
"plug_cowboy": {:hex, :plug_cowboy, "2.5.2", "62894ccd601cf9597e2c23911ff12798a8a18d237e9739f58a6b04e4988899fe", [:mix], [{:cowboy, "~> 2.7", [hex: :cowboy, repo: "hexpm", optional: false]}, {:cowboy_telemetry, "~> 0.3", [hex: :cowboy_telemetry, repo: "hexpm", optional: false]}, {:plug, "~> 1.7", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm", "ea6e87f774c8608d60c8d34022a7d073bd7680a0a013f049fc62bf35efea1044"},
"plug_crypto": {:hex, :plug_crypto, "1.2.2", "05654514ac717ff3a1843204b424477d9e60c143406aa94daf2274fdd280794d", [:mix], [], "hexpm", "87631c7ad914a5a445f0a3809f99b079113ae4ed4b867348dd9eec288cecb6db"},
"poison": {:hex, :poison, "5.0.0", "d2b54589ab4157bbb82ec2050757779bfed724463a544b6e20d79855a9e43b24", [:mix], [{:decimal, "~> 2.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm", "11dc6117c501b80c62a7594f941d043982a1bd05a1184280c0d9166eb4d8d3fc"},
diff --git a/test/farside_test.exs b/test/farside_test.exs
index 786c382..b5561e0 100644
--- a/test/farside_test.exs
+++ b/test/farside_test.exs
@@ -8,22 +8,21 @@ defmodule FarsideTest do
@opts Router.init([])
- test "/" do
- conn =
- :get
- |> conn("/", "")
+ def test_conn(path) do
+ :timer.sleep(1000)
+ :get
+ |> conn(path, "")
|> Router.call(@opts)
+ end
+ test "/" do
+ conn = test_conn("/")
assert conn.state == :sent
assert conn.status == 200
end
test "/ping" do
- conn =
- :get
- |> conn("/ping", "")
- |> Router.call(@opts)
-
+ conn = test_conn("/ping")
assert conn.state == :sent
assert conn.status == 200
assert conn.resp_body == "PONG"
@@ -42,24 +41,16 @@ defmodule FarsideTest do
IO.puts("")
Enum.map(service_names, fn service_name ->
-
- conn =
- :get
- |> conn("/#{service_name}", "")
- |> Router.call(@opts)
-
+ conn = test_conn("/#{service_name}")
first_redirect = elem(List.last(conn.resp_headers), 1)
+
IO.puts(" /#{service_name} (#1) -- #{first_redirect}")
assert conn.state == :set
assert conn.status == 302
-
- conn =
- :get
- |> conn("/#{service_name}", "")
- |> Router.call(@opts)
-
+ conn = test_conn("/#{service_name}")
second_redirect = elem(List.last(conn.resp_headers), 1)
+
IO.puts(" /#{service_name} (#2) -- #{second_redirect}")
assert conn.state == :set
assert conn.status == 302