diff --git a/lib/grpc/client/adapters/gun.ex b/lib/grpc/client/adapters/gun.ex index 47f12843..5482a055 100644 --- a/lib/grpc/client/adapters/gun.ex +++ b/lib/grpc/client/adapters/gun.ex @@ -245,11 +245,7 @@ defmodule GRPC.Client.Adapters.Gun do )} end else - {:error, - GRPC.RPCError.exception( - GRPC.Status.internal(), - "status got is #{status} instead of 200" - )} + {:error, GRPC.RPCError.from_http_status(status)} end {:response, :nofin, status, headers} -> @@ -266,11 +262,7 @@ defmodule GRPC.Client.Adapters.Gun do {:response, headers, :nofin} end else - {:error, - GRPC.RPCError.exception( - GRPC.Status.internal(), - "status got is #{status} instead of 200" - )} + {:error, GRPC.RPCError.from_http_status(status)} end {:data, :fin, data} -> diff --git a/lib/grpc/rpc_error.ex b/lib/grpc/rpc_error.ex index 39ec37ed..e07714e1 100644 --- a/lib/grpc/rpc_error.ex +++ b/lib/grpc/rpc_error.ex @@ -71,6 +71,23 @@ defmodule GRPC.RPCError do %{error | message: error.message || Status.status_message(error.status)} end + def from_http_status(401), do: exception(Status.unauthenticated(), status_message(401)) + def from_http_status(403), do: exception(Status.permission_denied(), status_message(403)) + def from_http_status(404), do: exception(Status.unimplemented(), status_message(404)) + def from_http_status(429), do: exception(Status.unavailable(), status_message(429)) + + def from_http_status(status) when status in [502, 503, 504] do + exception(Status.unavailable(), status_message(status)) + end + + def from_http_status(status) do + exception(Status.internal(), status_message(status)) + end + + defp status_message(status) do + "got http status #{status} instead of 200" + end + defp parse_args([], acc), do: acc defp parse_args([{:status, status} | t], acc) when is_integer(status) do diff --git a/test/grpc/integration/stub_test.exs b/test/grpc/integration/stub_test.exs index d24cdc68..a628770a 100644 --- a/test/grpc/integration/stub_test.exs +++ b/test/grpc/integration/stub_test.exs @@ -30,6 +30,32 @@ defmodule GRPC.Integration.StubTest do end) end + error_table = [ + {401, GRPC.Status.unauthenticated()}, + {403, GRPC.Status.permission_denied()}, + {404, GRPC.Status.unimplemented()}, + {429, GRPC.Status.unavailable()}, + {502, GRPC.Status.unavailable()}, + {503, GRPC.Status.unavailable()}, + {504, GRPC.Status.unavailable()}, + # General case + {518, GRPC.Status.internal()} + ] + + client_adapters = [GRPC.Client.Adapters.Gun, GRPC.Client.Adapters.Mint] + + for {http_code, expected_error_code} <- error_table, client_adapter <- client_adapters do + test "#{client_adapter} returns RPC Error when getting HTTP #{http_code}" do + run_error_server(unquote(http_code), fn port -> + {:ok, channel} = GRPC.Stub.connect("localhost:#{port}", adapter: unquote(client_adapter)) + req = %Helloworld.HelloRequest{name: "GRPC"} + + {:error, %GRPC.RPCError{status: unquote(expected_error_code)}} = + Helloworld.Greeter.Stub.say_hello(channel, req) + end) + end + end + test "you can disconnect stubs" do run_server(HelloServer, fn port -> {:ok, channel} = GRPC.Stub.connect("localhost:#{port}") diff --git a/test/support/integration_test_case.ex b/test/support/integration_test_case.ex index 4a4dbe9a..91cb6925 100644 --- a/test/support/integration_test_case.ex +++ b/test/support/integration_test_case.ex @@ -9,6 +9,34 @@ defmodule GRPC.Integration.TestCase do end end + defmodule ErrorHandler do + @behaviour :cowboy_handler + + @impl :cowboy_handler + def init(req, error_code) do + req = :cowboy_req.reply(error_code, %{"content-type" => "text/plain"}, "", req) + + {:ok, req, error_code} + end + end + + def run_error_server(error_code, func, port \\ 0) do + dispatch = + :cowboy_router.compile([ + {:_, [{:_, ErrorHandler, error_code}]} + ]) + + {:ok, _} = :cowboy.start_clear("error_server", [port: port], %{env: %{dispatch: dispatch}}) + port = :ranch.get_port("error_server") + + try do + func.(port) + :ok + after + :ok = :cowboy.stop_listener("error_server") + end + end + def run_server(servers, func, port \\ 0, opts \\ []) do {:ok, _pid, port} = GRPC.Server.start(servers, port, opts)