Page MenuHomePhorge

No OneTemporary

Size
3 KB
Referenced Files
None
Subscribers
None
diff --git a/lib/tesla/middleware/timeout.ex b/lib/tesla/middleware/timeout.ex
new file mode 100644
index 0000000..e6acfe0
--- /dev/null
+++ b/lib/tesla/middleware/timeout.ex
@@ -0,0 +1,61 @@
+defmodule Tesla.Middleware.Timeout do
+ @behaviour Tesla.Middleware
+
+ @moduledoc """
+ Timeout http request after X seconds.
+
+ ### Example
+ ```
+ defmodule MyClient do
+ use Tesla
+
+ plug Tesla.Middleware.Timeout, timeout: 2_000
+ end
+ """
+
+ @timeout_error %Tesla.Error{
+ reason: :timeout,
+ message: "#{__MODULE__}: Request timeout."
+ }
+
+ @default_timeout 1_000
+
+ def call(env, next, opts) do
+ opts = opts || []
+ timeout = Keyword.get(opts, :timeout, @default_timeout)
+
+ task = safe_async(fn -> Tesla.run(env, next) end)
+ try do
+ task
+ |> Task.await(timeout)
+ |> repass_error
+ catch :exit, {:timeout, _} ->
+ Task.shutdown(task, 0)
+ raise @timeout_error
+ end
+ end
+
+ defp safe_async(func) do
+ Task.async(fn ->
+ try do
+ {:ok, func.()}
+ rescue e in _ ->
+ {:error, e}
+ catch type, value ->
+ {type, value}
+ end
+ end)
+ end
+
+ defp repass_error({:error, error}),
+ do: raise error
+
+ defp repass_error({:throw, value}),
+ do: throw value
+
+ defp repass_error({:exit, value}),
+ do: exit value
+
+ defp repass_error({:ok, result}),
+ do: result
+end
diff --git a/test/tesla/middleware/timeout_test.exs b/test/tesla/middleware/timeout_test.exs
new file mode 100644
index 0000000..53d0293
--- /dev/null
+++ b/test/tesla/middleware/timeout_test.exs
@@ -0,0 +1,67 @@
+defmodule Tesla.Middleware.TimeoutTest do
+ use ExUnit.Case, async: false
+
+ defmodule Client do
+ use Tesla
+
+ plug Tesla.Middleware.Timeout, timeout: 100
+
+ adapter fn(env) ->
+ status = case env.url do
+ "/sleep_50ms" ->
+ Process.sleep(50)
+ 200
+ "/sleep_150ms" ->
+ Process.sleep(150)
+ 200
+ end
+
+ %{env | status: status}
+ end
+ end
+
+ defmodule DefaultTimeoutClient do
+ use Tesla
+
+ plug Tesla.Middleware.Timeout
+
+ adapter fn(env) ->
+ status = case env.url do
+ "/sleep_950ms" ->
+ Process.sleep(950)
+ 200
+ "/sleep_1050ms" ->
+ Process.sleep(1_050)
+ 200
+ end
+
+ %{env | status: status}
+ end
+ end
+
+ describe "using custom timeout (100ms)" do
+ test "should raise a Tesla.Error when the stack timeout" do
+ error = assert_raise Tesla.Error, fn ->
+ Client.get("/sleep_150ms")
+ end
+ assert error.reason == :timeout
+ end
+
+ test "should return the response when not timeout" do
+ assert %Tesla.Env{status: 200} = Client.get("/sleep_50ms")
+ end
+ end
+
+ describe "using default timeout (1_000ms)" do
+ test "should raise a Tesla.Error when the stack timeout" do
+ error = assert_raise Tesla.Error, fn ->
+ DefaultTimeoutClient.get("/sleep_1050ms")
+ end
+ assert error.reason == :timeout
+ end
+
+ test "should return the response when not timeout" do
+ assert %Tesla.Env{status: 200} = DefaultTimeoutClient.get("/sleep_950ms")
+ end
+ end
+end

File Metadata

Mime Type
text/x-diff
Expires
Mon, Jan 20, 5:09 PM (1 d, 9 h)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
55587
Default Alt Text
(3 KB)

Event Timeline