Skip to content

Commit bae8b92

Browse files
committed
Merge remote-tracking branch 'upstream/master'
2 parents 4feee64 + 0441ea8 commit bae8b92

File tree

22 files changed

+924
-1775
lines changed

22 files changed

+924
-1775
lines changed

.github/workflows/ci.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ jobs:
5252
- name: Install Dependencies
5353
run: mix deps.get 1>/dev/null
5454
- name: Run Tests
55-
run: mix test
55+
run: mix test --warnings-as-errors
5656
interop-tests:
5757
runs-on: ubuntu-latest
5858
name: Interop tests

lib/google/api/annotations.pb.ex

Lines changed: 0 additions & 8 deletions
This file was deleted.

lib/google/api/http.pb.ex

Lines changed: 0 additions & 43 deletions
This file was deleted.

lib/grpc/client/adapters/gun.ex

Lines changed: 55 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -155,24 +155,59 @@ defmodule GRPC.Client.Adapters.Gun do
155155
) do
156156
%{channel: %{adapter_payload: adapter_payload}, payload: payload} = stream
157157

158-
with {:ok, headers, is_fin} <- recv_headers(adapter_payload, payload, opts) do
159-
response = response_stream(is_fin, stream, opts)
158+
case recv_headers(adapter_payload, payload, opts) do
159+
{:ok, headers, :fin} ->
160+
handle_fin_response(headers, opts)
160161

161-
if(opts[:return_headers]) do
162-
{:ok, response, %{headers: headers}}
163-
else
164-
{:ok, response}
165-
end
162+
{:ok, headers, :nofin} ->
163+
handle_streaming_nofin_response(stream, headers, opts)
164+
165+
{:error, _} = error ->
166+
error
166167
end
167168
end
168169

169170
def receive_data(stream, opts) do
170171
%{payload: payload, channel: %{adapter_payload: adapter_payload}} = stream
171172

172-
with {:ok, headers, _is_fin} <- recv_headers(adapter_payload, payload, opts),
173-
{:ok, body, trailers} <- recv_body(adapter_payload, payload, opts),
173+
case recv_headers(adapter_payload, payload, opts) do
174+
{:ok, headers, :fin} ->
175+
handle_fin_response(headers, opts)
176+
177+
{:ok, headers, :nofin} ->
178+
handle_nofin_response(adapter_payload, payload, stream, headers, opts)
179+
180+
{:error, _} = error ->
181+
error
182+
end
183+
end
184+
185+
defp handle_fin_response(headers, opts) do
186+
# Trailers-only response: headers contain trailers, check status
187+
with :ok <- parse_trailers(headers) do
188+
if opts[:return_headers] do
189+
{:ok, [], %{headers: headers}}
190+
else
191+
{:ok, []}
192+
end
193+
end
194+
end
195+
196+
defp handle_streaming_nofin_response(stream, headers, opts) do
197+
response = response_stream(:nofin, stream, opts)
198+
199+
if opts[:return_headers] do
200+
{:ok, response, %{headers: headers}}
201+
else
202+
{:ok, response}
203+
end
204+
end
205+
206+
defp handle_nofin_response(adapter_payload, payload, stream, headers, opts) do
207+
# Regular response: fetch body and trailers
208+
with {:ok, body, trailers} <- recv_body(adapter_payload, payload, opts),
174209
{:ok, response} <- parse_response(stream, headers, body, trailers) do
175-
if(opts[:return_headers]) do
210+
if opts[:return_headers] do
176211
{:ok, response, %{headers: headers, trailers: trailers}}
177212
else
178213
{:ok, response}
@@ -230,25 +265,7 @@ defmodule GRPC.Client.Adapters.Gun do
230265
{:response, :fin, status, headers} ->
231266
if status == 200 do
232267
headers = GRPC.Transport.HTTP2.decode_headers(headers)
233-
234-
case headers["grpc-status"] do
235-
nil ->
236-
{:error,
237-
GRPC.RPCError.exception(
238-
GRPC.Status.internal(),
239-
"shouldn't finish when getting headers"
240-
)}
241-
242-
"0" ->
243-
{:response, headers, :fin}
244-
245-
_ ->
246-
{:error,
247-
GRPC.RPCError.exception(
248-
String.to_integer(headers["grpc-status"]),
249-
headers["grpc-message"]
250-
)}
251-
end
268+
{:response, headers, :fin}
252269
else
253270
{:error,
254271
GRPC.RPCError.exception(
@@ -260,16 +277,7 @@ defmodule GRPC.Client.Adapters.Gun do
260277
{:response, :nofin, status, headers} ->
261278
if status == 200 do
262279
headers = GRPC.Transport.HTTP2.decode_headers(headers)
263-
264-
if headers["grpc-status"] && headers["grpc-status"] != "0" do
265-
{:error,
266-
GRPC.RPCError.exception(
267-
String.to_integer(headers["grpc-status"]),
268-
headers["grpc-message"]
269-
)}
270-
else
271-
{:response, headers, :nofin}
272-
end
280+
{:response, headers, :nofin}
273281
else
274282
{:error,
275283
GRPC.RPCError.exception(
@@ -349,8 +357,6 @@ defmodule GRPC.Client.Adapters.Gun do
349357
end
350358
end
351359

352-
defp response_stream(:fin, _stream, _opts), do: []
353-
354360
defp response_stream(
355361
:nofin,
356362
%{
@@ -453,7 +459,14 @@ defmodule GRPC.Client.Adapters.Gun do
453459
if status == GRPC.Status.ok() do
454460
:ok
455461
else
456-
{:error, %GRPC.RPCError{status: status, message: trailers["grpc-message"]}}
462+
rpc_error =
463+
GRPC.RPCError.from_grpc_status_details_bin(%{
464+
status: status,
465+
message: trailers["grpc-message"],
466+
encoded_details_bin: trailers["grpc-status-details-bin"]
467+
})
468+
469+
{:error, rpc_error}
457470
end
458471
end
459472

lib/grpc/client/adapters/mint/stream_response_process.ex

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -182,7 +182,13 @@ defmodule GRPC.Client.Adapters.Mint.StreamResponseProcess do
182182
if status == GRPC.Status.ok() do
183183
{type, decoded_trailers}
184184
else
185-
rpc_error = %GRPC.RPCError{status: status, message: decoded_trailers["grpc-message"]}
185+
rpc_error =
186+
GRPC.RPCError.from_grpc_status_details_bin(%{
187+
status: status,
188+
message: decoded_trailers["grpc-message"],
189+
encoded_details_bin: decoded_trailers["grpc-status-details-bin"]
190+
})
191+
186192
{:error, rpc_error}
187193
end
188194
end

lib/grpc/google/rpc.ex

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
defmodule GRPC.Google.RPC do
2+
@moduledoc false
3+
4+
@spec encode_status(Google.Rpc.Status.t()) :: String.t()
5+
def encode_status(%Google.Rpc.Status{} = status) do
6+
status
7+
|> Google.Rpc.Status.encode()
8+
|> Base.encode64(padding: true)
9+
end
10+
11+
@spec decode_status(String.t()) :: {:ok, Google.Rpc.Status.t()} | {:error, term()}
12+
def decode_status(encoded_details_bin) when is_binary(encoded_details_bin) do
13+
{:ok,
14+
encoded_details_bin
15+
|> decode64()
16+
|> Google.Rpc.Status.decode()}
17+
rescue
18+
e -> {:error, e}
19+
end
20+
21+
defp decode64(str) when rem(byte_size(str), 4) == 0 do
22+
Base.decode64!(str, padding: true)
23+
end
24+
25+
defp decode64(str) do
26+
Base.decode64!(str, padding: false)
27+
end
28+
end

lib/grpc/rpc_error.ex

Lines changed: 40 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -51,11 +51,15 @@ defmodule GRPC.RPCError do
5151
See `GRPC.Status` for more details on possible statuses.
5252
"""
5353

54-
defexception [:status, :message]
54+
defexception [:status, :message, :details]
5555

5656
defguard is_rpc_error(e, status) when is_struct(e, __MODULE__) and e.status == status
5757

58-
@type t :: %__MODULE__{status: GRPC.Status.t(), message: String.t()}
58+
@type t :: %__MODULE__{
59+
status: GRPC.Status.t(),
60+
message: String.t(),
61+
details: [Google.Protobuf.Any.t()]
62+
}
5963

6064
alias GRPC.Status
6165

@@ -68,7 +72,11 @@ defmodule GRPC.RPCError do
6872
def exception(args) when is_list(args) do
6973
error = parse_args(args, %__MODULE__{})
7074

71-
%{error | message: error.message || Status.status_message(error.status)}
75+
%{
76+
error
77+
| message: error.message || Status.status_message(error.status),
78+
details: error.details
79+
}
7280
end
7381

7482
defp parse_args([], acc), do: acc
@@ -88,6 +96,11 @@ defmodule GRPC.RPCError do
8896
parse_args(t, acc)
8997
end
9098

99+
defp parse_args([{:details, details} | t], acc) when is_list(details) do
100+
acc = %{acc | details: details}
101+
parse_args(t, acc)
102+
end
103+
91104
@spec exception(status :: Status.t() | atom(), message :: String.t()) :: t()
92105
def exception(status, message) when is_atom(status) do
93106
%GRPC.RPCError{status: apply(GRPC.Status, status, []), message: message}
@@ -96,4 +109,28 @@ defmodule GRPC.RPCError do
96109
def exception(status, message) when is_integer(status) do
97110
%GRPC.RPCError{status: status, message: message}
98111
end
112+
113+
@doc false
114+
def from_grpc_status_details_bin(%{
115+
status: status,
116+
message: message,
117+
encoded_details_bin: encoded_details_bin
118+
})
119+
when is_binary(encoded_details_bin) do
120+
case GRPC.Google.RPC.decode_status(encoded_details_bin) do
121+
{:ok, rpc_status} ->
122+
%__MODULE__{
123+
status: status,
124+
message: rpc_status.message,
125+
details: rpc_status.details
126+
}
127+
128+
{:error, _} ->
129+
%__MODULE__{status: status, message: message}
130+
end
131+
end
132+
133+
def from_grpc_status_details_bin(%{status: status, message: message}) do
134+
%__MODULE__{status: status, message: message}
135+
end
99136
end

lib/grpc/server/adapters/cowboy/handler.ex

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -464,6 +464,10 @@ defmodule GRPC.Server.Adapters.Cowboy.Handler do
464464
error = %RPCError{status: GRPC.Status.deadline_exceeded(), message: "Deadline expired"}
465465
req = send_error(req, error, state, :timeout)
466466

467+
[req: req]
468+
|> ReportException.new(error)
469+
|> log_error()
470+
467471
{:stop, req, state}
468472
end
469473

@@ -660,7 +664,7 @@ defmodule GRPC.Server.Adapters.Cowboy.Handler do
660664
defp preflight?(_), do: false
661665

662666
defp send_error(req, error, state, reason) do
663-
trailers = HTTP2.server_trailers(error.status, error.message)
667+
trailers = HTTP2.server_trailers(error.status, error.message, error.details)
664668

665669
status =
666670
if state.access_mode == :http_transcoding,
@@ -694,7 +698,7 @@ defmodule GRPC.Server.Adapters.Cowboy.Handler do
694698
{:wait, ref}
695699
end
696700

697-
defp log_error(%ReportException{kind: kind} = exception, stacktrace) do
701+
defp log_error(%ReportException{kind: kind} = exception, stacktrace \\ []) do
698702
crash_reason = GRPC.Logger.crash_reason(kind, exception, stacktrace)
699703

700704
kind

lib/grpc/stub.ex

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -309,7 +309,11 @@ defmodule GRPC.Stub do
309309
end
310310

311311
compressor = Keyword.get(opts, :compressor, channel.compressor)
312-
accepted_compressors = Keyword.get(opts, :accepted_compressors, [])
312+
accepted_compressors = Keyword.get(opts, :accepted_compressors, channel.accepted_compressors)
313+
314+
if not is_list(accepted_compressors) do
315+
raise ArgumentError, "accepted_compressors is not a list"
316+
end
313317

314318
accepted_compressors =
315319
if compressor do
@@ -321,7 +325,7 @@ defmodule GRPC.Stub do
321325
stream = %{
322326
stream
323327
| codec: Keyword.get(opts, :codec, channel.codec),
324-
compressor: Keyword.get(opts, :compressor, channel.compressor),
328+
compressor: compressor,
325329
accepted_compressors: accepted_compressors
326330
}
327331

0 commit comments

Comments
 (0)