Skip to content

Commit b65ed41

Browse files
authored
Merge pull request #407 from jeregrine/js/error-wrapping
if an erlang error escapes it causes ReportException to fail and hide…
2 parents 5827e4f + 585a698 commit b65ed41

File tree

2 files changed

+106
-1
lines changed

2 files changed

+106
-1
lines changed

lib/grpc/server/adapters/report_exception.ex

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,24 @@ defmodule GRPC.Server.Adapters.ReportException do
55

66
defexception [:kind, :reason, :stack, :adapter_extra]
77

8-
def new(adapter_extra, %{__exception__: _} = exception, stack \\ [], kind \\ :error) do
8+
def new(adapter_extra, exception, stack \\ [], kind \\ :error)
9+
10+
def new(adapter_extra, %{__exception__: _} = exception, stack, kind) do
911
exception(kind: kind, reason: exception, stack: stack, adapter_extra: adapter_extra)
1012
end
1113

14+
def new(adapter_extra, {erl_error, erl_stack}, _stack, kind) do
15+
new(adapter_extra, Exception.normalize(:error, erl_error, erl_stack), erl_stack, kind)
16+
end
17+
18+
def new(adapter_extra, erl_error, stack, kind) do
19+
new(adapter_extra, Exception.normalize(:error, erl_error, stack), stack, kind)
20+
end
21+
22+
def message(%__MODULE__{adapter_extra: [{:req, :ok}], kind: kind, reason: reason, stack: stack}) do
23+
Exception.format_banner(kind, reason, stack)
24+
end
25+
1226
def message(%__MODULE__{adapter_extra: [{:req, req}], kind: kind, reason: reason, stack: stack}) do
1327
# Cowboy adapter message builder
1428
path = :cowboy_req.path(req)
Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
defmodule ExceptionServer do
2+
use GenServer
3+
4+
# Callbacks
5+
6+
@impl true
7+
def init(pid) do
8+
{:ok, pid}
9+
end
10+
11+
@impl true
12+
def handle_cast(:case_boom, state) do
13+
a = fn -> :ok end
14+
15+
case a.() do
16+
:error -> :boom
17+
end
18+
19+
{:noreply, state}
20+
end
21+
22+
@impl true
23+
def handle_cast(:bad_arg_boom, state) do
24+
ets = :ets.new(:foo, [])
25+
:ets.delete(ets)
26+
:ets.insert(ets, 1)
27+
28+
{:noreply, state}
29+
end
30+
31+
@impl true
32+
def terminate(reason, state) do
33+
send(state, {:boom, reason})
34+
:ok
35+
end
36+
end
37+
38+
defmodule GRPC.Server.Adapters.ReportExceptionTest do
39+
use ExUnit.Case, async: true
40+
alias GRPC.Server.Adapters.ReportException
41+
42+
describe "new/3" do
43+
test "with runtime error" do
44+
assert %GRPC.Server.Adapters.ReportException{
45+
__exception__: true,
46+
adapter_extra: [req: :ok],
47+
kind: :error,
48+
reason: %RuntimeError{message: "hi", __exception__: true},
49+
stack: []
50+
} == ReportException.new([req: :ok], RuntimeError.exception("hi"))
51+
end
52+
53+
test "with case clause error" do
54+
{:ok, pid} = GenServer.start_link(ExceptionServer, self())
55+
56+
GenServer.cast(pid, :case_boom)
57+
58+
receive do
59+
{:boom, {_reason, stack} = err} ->
60+
assert %GRPC.Server.Adapters.ReportException{
61+
__exception__: true,
62+
adapter_extra: [req: :ok],
63+
kind: :error,
64+
reason: %CaseClauseError{term: :ok},
65+
stack: stack
66+
} == ReportException.new([{:req, :ok}], err)
67+
end
68+
end
69+
70+
test "with badarg error" do
71+
{:ok, pid} = GenServer.start_link(ExceptionServer, self())
72+
73+
GenServer.cast(pid, :bad_arg_boom)
74+
75+
receive do
76+
{:boom, {_reason, stack} = err} ->
77+
assert %GRPC.Server.Adapters.ReportException{
78+
__exception__: true,
79+
adapter_extra: [req: :ok],
80+
kind: :error,
81+
reason: %ArgumentError{
82+
__exception__: true,
83+
message:
84+
"errors were found at the given arguments:\n\n * 1st argument: the table identifier does not refer to an existing ETS table\n * 2nd argument: not a tuple\n"
85+
},
86+
stack: stack
87+
} == ReportException.new([{:req, :ok}], err)
88+
end
89+
end
90+
end
91+
end

0 commit comments

Comments
 (0)