Skip to content

Commit 3af0153

Browse files
committed
Merge remote-tracking branch 'upstream/master'
2 parents 2a76e93 + 2003255 commit 3af0153

File tree

9 files changed

+74
-83
lines changed

9 files changed

+74
-83
lines changed

.tool-versions

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

README.md

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,8 @@
33
[![GitHub CI](https://github.com/elixir-grpc/grpc/actions/workflows/ci.yml/badge.svg)](https://github.com/elixir-grpc/grpc/actions/workflows/ci.yml)
44
[![Hex.pm](https://img.shields.io/hexpm/v/grpc.svg)](https://hex.pm/packages/grpc)
55
[![Hex Docs](https://img.shields.io/badge/hex-docs-lightgreen.svg)](https://hexdocs.pm/grpc/)
6-
[![License](https://img.shields.io/hexpm/l/grpc.svg)](https://github.com/elixir-grpc/grpc/blob/master/LICENSE.md)
7-
[![Total Download](https://img.shields.io/hexpm/dt/grpc.svg)](https://hex.pm/packages/elixir-grpc/grpc)
6+
[![License](https://img.shields.io/hexpm/l/grpc.svg)](https://github.com/elixir-grpc/grpc/blob/master/LICENSE)
7+
[![Total Downloads](https://img.shields.io/hexpm/dt/grpc.svg)](https://hex.pm/packages/grpc)
88
[![Last Updated](https://img.shields.io/github/last-commit/elixir-grpc/grpc.svg)](https://github.com/elixir-grpc/grpc/commits/master)
99

1010
**gRPC Elixir** is a full-featured Elixir implementation of the [gRPC](https://grpc.io) protocol, supporting unary and streaming RPCs, interceptors, HTTP transcoding, and TLS. This version adopts a unified stream-based model for all types of calls.
@@ -34,7 +34,7 @@ def deps do
3434
[
3535
{:grpc, "~> 0.10"},
3636
{:protobuf, "~> 0.14"}, # optional for import wellknown google types
37-
{:grpc_reflection, "~> 0.1"} # optional enable grpc reflection
37+
{:grpc_reflection, "~> 0.2"} # optional enable grpc reflection
3838
]
3939
end
4040
```
@@ -150,8 +150,8 @@ defmodule Helloworld.Application do
150150
GrpcReflection,
151151
{
152152
GRPC.Server.Supervisor, [
153-
endpoint: Helloworld.Endpoint,
154-
port: 50051,
153+
endpoint: Helloworld.Endpoint,
154+
port: 50051,
155155
start_server: true,
156156
# adapter_opts: [# any adapter-specific options like tls configuration....]
157157
]
@@ -162,7 +162,7 @@ defmodule Helloworld.Application do
162162
Supervisor.start_link(children, opts)
163163
end
164164
end
165-
```
165+
```
166166

167167
# Client Usage
168168

@@ -234,8 +234,8 @@ In mix.exs:
234234
```elixir
235235
def deps do
236236
[
237-
{:grpc, "~> 0.7"},
238-
{:protobuf_generate, "~> 0.1.1"}
237+
{:grpc, "~> 0.10"},
238+
{:protobuf_generate, "~> 0.1.3"}
239239
]
240240
end
241241
```
@@ -268,7 +268,7 @@ See full application code in [helloworld_transcoding](examples/helloworld_transc
268268

269269
### **CORS**
270270

271-
When accessing gRPC from a browser via HTTP transcoding or gRPC-Web, CORS headers may be required for the browser to allow access to the gRPC endpoint. Adding CORS headers can be done by using `GRPC.Server.Interceptors.CORS` as an interceptor in your `GRPC.Endpoint` module, configuring it as decribed in the module documentation:
271+
When accessing gRPC from a browser via HTTP transcoding or gRPC-Web, CORS headers may be required for the browser to allow access to the gRPC endpoint. Adding CORS headers can be done by using `GRPC.Server.Interceptors.CORS` as an interceptor in your `GRPC.Endpoint` module, configuring it as described in the module documentation:
272272

273273
Example:
274274

@@ -292,10 +292,10 @@ end
292292
- [Bidirectional-streaming](https://grpc.io/docs/what-is-grpc/core-concepts/#bidirectional-streaming-rpc)
293293
- [HTTP Transcoding](https://cloud.google.com/endpoints/docs/grpc/transcoding)
294294
- [TLS Authentication](https://grpc.io/docs/guides/auth/#supported-auth-mechanisms)
295-
- [Error handling](https://grpc.io/docs/guides/error/)
296-
- [Interceptors](`GRPC.Endpoint`)
295+
- [Error Handling](https://grpc.io/docs/guides/error/)
296+
- [Interceptors](https://grpc.io/docs/guides/interceptors/)
297297
- [Connection Backoff](https://github.com/grpc/grpc/blob/master/doc/connection-backoff.md)
298-
- Data compression
298+
- [Data Compression](https://grpc.io/docs/guides/compression/)
299299
- [gRPC Reflection](https://github.com/elixir-grpc/grpc-reflection)
300300

301301
## Benchmark

lib/grpc/server/adapters/cowboy.ex

Lines changed: 23 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -30,8 +30,10 @@ defmodule GRPC.Server.Adapters.Cowboy do
3030
"""
3131
@impl true
3232
def start(endpoint, servers, port, opts) do
33-
start_args = cowboy_start_args(endpoint, servers, port, opts)
34-
start_func = if opts[:cred], do: :start_tls, else: :start_clear
33+
[_ref, _trans_opts, proto_opts] =
34+
start_args = cowboy_start_args(endpoint, servers, port, opts)
35+
36+
start_func = if cred_opts(proto_opts), do: :start_tls, else: :start_clear
3537

3638
case apply(:cowboy, start_func, start_args) do
3739
{:ok, pid} ->
@@ -52,8 +54,10 @@ defmodule GRPC.Server.Adapters.Cowboy do
5254
[ref, trans_opts, proto_opts] = cowboy_start_args(endpoint, servers, port, opts)
5355
trans_opts = Map.put(trans_opts, :connection_type, :supervisor)
5456

57+
cred_opts = cred_opts(proto_opts)
58+
5559
{transport, protocol} =
56-
if opts[:cred] do
60+
if cred_opts do
5761
{:ranch_ssl, :cowboy_tls}
5862
else
5963
{:ranch_tcp, :cowboy_clear}
@@ -63,7 +67,7 @@ defmodule GRPC.Server.Adapters.Cowboy do
6367
# So we just support both child spec versions here instead
6468
case :ranch.child_spec(ref, transport, trans_opts, protocol, proto_opts) do
6569
{ref, mfa, type, timeout, kind, modules} ->
66-
scheme = if opts[:cred], do: :https, else: :http
70+
scheme = if cred_opts, do: :https, else: :http
6771
# Wrap real mfa to print starting log
6872
wrapped_mfa = {__MODULE__, :start_link, [scheme, endpoint, servers, mfa]}
6973

@@ -226,12 +230,12 @@ defmodule GRPC.Server.Adapters.Cowboy do
226230
dispatch_key = Module.concat(endpoint, Dispatch)
227231
:persistent_term.put(dispatch_key, dispatch)
228232

229-
idle_timeout = Keyword.get(opts, :idle_timeout) || :infinity
230-
num_acceptors = Keyword.get(opts, :num_acceptors) || @default_num_acceptors
231-
max_connections = Keyword.get(opts, :max_connections) || @default_max_connections
233+
idle_timeout = Keyword.get(adapter_opts, :idle_timeout) || :infinity
234+
num_acceptors = Keyword.get(adapter_opts, :num_acceptors) || @default_num_acceptors
235+
max_connections = Keyword.get(adapter_opts, :max_connections) || @default_max_connections
232236

233237
# https://ninenines.eu/docs/en/cowboy/2.7/manual/cowboy_http2/
234-
opts =
238+
merged_adapter_opts =
235239
Map.merge(
236240
%{
237241
env: %{dispatch: {:persistent_term, dispatch_key}},
@@ -245,17 +249,17 @@ defmodule GRPC.Server.Adapters.Cowboy do
245249
max_received_frame_rate: {10_000_000, 10_000},
246250
max_reset_stream_rate: {10_000, 10_000}
247251
},
248-
Enum.into(opts, %{})
252+
Enum.into(adapter_opts, %{})
249253
)
250254

251255
[
252256
servers_name(endpoint, servers),
253257
%{
254258
num_acceptors: num_acceptors,
255259
max_connections: max_connections,
256-
socket_opts: socket_opts(port, opts)
260+
socket_opts: socket_opts(port, adapter_opts)
257261
},
258-
opts
262+
merged_adapter_opts
259263
]
260264
end
261265

@@ -270,8 +274,10 @@ defmodule GRPC.Server.Adapters.Cowboy do
270274
_, acc -> acc
271275
end)
272276

273-
if opts[:cred] do
274-
opts[:cred].ssl ++
277+
cred_opts = cred_opts(opts)
278+
279+
if cred_opts do
280+
cred_opts.ssl ++
275281
[
276282
# These NPN/ALPN options are hardcoded in :cowboy.start_tls/3 (when calling start/3),
277283
# but not in :ranch.child_spec/5 (when calling child_spec/3). We must make sure they
@@ -285,6 +291,10 @@ defmodule GRPC.Server.Adapters.Cowboy do
285291
end
286292
end
287293

294+
defp cred_opts(opts) do
295+
Kernel.get_in(opts, [:cred])
296+
end
297+
288298
defp running_info(scheme, endpoint, servers, ref) do
289299
{addr, port} = :ranch.get_addr(ref)
290300

lib/grpc/server/interceptors/cors.ex

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -77,8 +77,7 @@ defmodule GRPC.Server.Interceptors.CORS do
7777

7878
@impl true
7979
def call(req, stream, next, {allow_origin, allow_headers}) do
80-
if stream.access_mode != :grpc and
81-
Map.get(stream.http_request_headers, "sec-fetch-mode") == "cors" do
80+
if stream.access_mode != :grpc do
8281
headers =
8382
%{}
8483
|> add_allowed_origins(req, stream, allow_origin)

lib/grpc/server/supervisor.ex

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -68,9 +68,14 @@ defmodule GRPC.Server.Supervisor do
6868

6969
{:error, _} ->
7070
raise ArgumentError,
71-
"just [:endpoint, :servers, :start_server, :port,] are accepted as arguments, and any other keys for adapters should be passed as adapter_opts!"
71+
"just [:endpoint, :servers, :start_server, :port, :adapter_opts] are accepted as arguments, and any other keys for adapters should be passed as adapter_opts!"
7272
end
7373

74+
case validate_cred(opts) do
75+
{:ok, _cred} -> :ok
76+
{:error, err} -> raise ArgumentError, err
77+
end
78+
7479
endpoint_or_servers =
7580
case {opts[:endpoint], opts[:servers]} do
7681
{endpoint, servers}
@@ -136,4 +141,15 @@ defmodule GRPC.Server.Supervisor do
136141
servers = GRPC.Server.servers_to_map(servers)
137142
adapter.child_spec(nil, servers, port, opts)
138143
end
144+
145+
defp validate_cred(opts) do
146+
with cred <- Kernel.get_in(opts, [:adapter_opts, :cred]),
147+
true <- cred == nil or (is_map(cred) and is_list(Map.get(cred, :ssl))) do
148+
{:ok, cred}
149+
else
150+
_ ->
151+
{:error,
152+
"the :cred option must be a map with an :ssl key containing a list of SSL options"}
153+
end
154+
end
139155
end

test/grpc/client/adapters/gun_test.exs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,9 @@ defmodule GRPC.Client.Adapters.GunTest do
66
describe "connect/2" do
77
setup do
88
server_credential = build(:credential)
9-
{:ok, _, port} = GRPC.Server.start(FeatureServer, 0, cred: server_credential)
9+
10+
{:ok, _, port} =
11+
GRPC.Server.start(FeatureServer, 0, adapter_opts: [cred: server_credential])
1012

1113
on_exit(fn ->
1214
:ok = GRPC.Server.stop(FeatureServer)

test/grpc/integration/connection_test.exs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ defmodule GRPC.Integration.ConnectionTest do
1818
server = FeatureServer
1919
File.rm(socket_path)
2020

21-
{:ok, _, _} = GRPC.Server.start(server, 0, ip: {:local, socket_path})
21+
{:ok, _, _} = GRPC.Server.start(server, 0, adapter_opts: [ip: {:local, socket_path}])
2222
{:ok, channel} = GRPC.Stub.connect(socket_path, adapter_opts: [retry_timeout: 10])
2323

2424
point = %Routeguide.Point{latitude: 409_146_138, longitude: -746_188_906}
@@ -31,7 +31,7 @@ defmodule GRPC.Integration.ConnectionTest do
3131

3232
cred = GRPC.Factory.build(:credential, verify: :verify_peer)
3333

34-
{:ok, _, port} = GRPC.Server.start(server, 0, cred: cred)
34+
{:ok, _, port} = GRPC.Server.start(server, 0, adapter_opts: [cred: cred])
3535

3636
try do
3737
point = %Routeguide.Point{latitude: 409_146_138, longitude: -746_188_906}

test/grpc/server/adapters/cowboy_test.exs

Lines changed: 16 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,14 @@ defmodule GRPC.Server.Adapters.CowboyTest do
77
test "produces the correct socket opts for ranch_tcp for inet" do
88
spec =
99
Cowboy.child_spec(:endpoint, [], 8080, [
10-
{:foo, :bar},
11-
{:ip, {127, 0, 0, 1}},
12-
{:ipv6_v6only, false},
13-
{:net, :inet},
14-
{:baz, :foo}
10+
{:adapter_opts,
11+
[
12+
{:foo, :bar},
13+
{:ip, {127, 0, 0, 1}},
14+
{:ipv6_v6only, false},
15+
{:net, :inet},
16+
{:baz, :foo}
17+
]}
1518
])
1619

1720
socket_opts = get_socket_opts_from_child_spec(spec)
@@ -23,11 +26,14 @@ defmodule GRPC.Server.Adapters.CowboyTest do
2326
test "produces the correct socket opts for ranch_tcp for inet6" do
2427
spec =
2528
Cowboy.child_spec(:endpoint, [], 8081, [
26-
{:foo, :bar},
27-
{:ip, {0, 0, 0, 0, 0, 0, 0, 1}},
28-
{:ipv6_v6only, true},
29-
{:net, :inet6},
30-
{:baz, :foo}
29+
{:adapter_opts,
30+
[
31+
{:foo, :bar},
32+
{:ip, {0, 0, 0, 0, 0, 0, 0, 1}},
33+
{:ipv6_v6only, true},
34+
{:net, :inet6},
35+
{:baz, :foo}
36+
]}
3137
])
3238

3339
socket_opts = get_socket_opts_from_child_spec(spec)

test/grpc/server/interceptors/cors_test.exs

Lines changed: 0 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -271,44 +271,4 @@ defmodule GRPC.Server.Interceptors.CORSTest do
271271
"Incorrect header when using function"
272272
)
273273
end
274-
275-
test "CORS only on cors sec-fetch-mode" do
276-
request = %FakeRequest{}
277-
278-
stream = %{
279-
create_stream()
280-
| access_mode: :grpcweb,
281-
http_request_headers: Map.put(@default_http_headers, "sec-fetch-mode", "same-origin")
282-
}
283-
284-
{:ok, :ok} =
285-
CORSInterceptor.call(
286-
request,
287-
%{stream | access_mode: :grpcweb},
288-
fn _request, _stream -> {:ok, :ok} end,
289-
CORSInterceptor.init(allow_origin: "*")
290-
)
291-
292-
refute_received({:setting_headers, _}, "Set CORS header")
293-
end
294-
295-
test "No CORS if missing sec-fetch-mode header" do
296-
request = %FakeRequest{}
297-
298-
stream = %{
299-
create_stream()
300-
| access_mode: :grpcweb,
301-
http_request_headers: Map.delete(@default_http_headers, "sec-fetch-mode")
302-
}
303-
304-
{:ok, :ok} =
305-
CORSInterceptor.call(
306-
request,
307-
%{stream | access_mode: :grpcweb},
308-
fn _request, _stream -> {:ok, :ok} end,
309-
CORSInterceptor.init(allow_origin: "*")
310-
)
311-
312-
refute_received({:setting_headers, _}, "Set CORS header")
313-
end
314274
end

0 commit comments

Comments
 (0)