tonic-rails 0.3.0 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +11 -1
- data/Cargo.lock +35 -1
- data/README.md +41 -0
- data/ext/tonic_rails_native/Cargo.toml +3 -1
- data/ext/tonic_rails_native/src/lib.rs +1 -0
- data/ext/tonic_rails_native/src/server.rs +100 -1
- data/lib/tonic/rails/configuration.rb +33 -0
- data/lib/tonic/rails/server.rb +27 -5
- data/lib/tonic/rails/version.rb +1 -1
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: df7cc56c6ca7ca2594bc38ab5e676d9c68f6dbcd6464155d14b3d7a83ef98ef2
|
|
4
|
+
data.tar.gz: 9763e38411184f3c9a796f9e72bf9238ae3e20c16c7687cefa3792192cba7516
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: d2bf2cad63a78055da0874e0a934a44f41a015c9dd98d2512b1b8ef8b6830cfdb64d262d65c55fad5b532574ec6b7a63f4777f79b6f13e7d23dd06808417c46c
|
|
7
|
+
data.tar.gz: 04df783ef65a2eb4d8a717d4643e4e406a0040a053289d5c2841014c6bb69ec267223e2bf52b9323dc1314b179969e7bb6da2606fee8bf4f93121b2932f5ce2a
|
data/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,15 @@ minor versions may contain breaking changes.
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [0.4.0] - 2026-10-11
|
|
11
|
+
|
|
12
|
+
### Added
|
|
13
|
+
|
|
14
|
+
- gRPC-Web: `config.grpc_web = true` serves browser clients (unary and
|
|
15
|
+
server-streaming RPCs, binary and text encodings) without a proxy, on the
|
|
16
|
+
same port as native gRPC, with CORS via `grpc_web_origins`,
|
|
17
|
+
`grpc_web_allowed_headers` and `grpc_web_exposed_headers`. Closes #22.
|
|
18
|
+
|
|
10
19
|
## [0.3.0] - 2026-10-11
|
|
11
20
|
|
|
12
21
|
### Added
|
|
@@ -116,7 +125,8 @@ First public release.
|
|
|
116
125
|
- Precompiled native gems for Linux (x86_64, aarch64; glibc and musl) and
|
|
117
126
|
macOS (x86_64, arm64), Ruby 3.3 to 4.0.
|
|
118
127
|
|
|
119
|
-
[Unreleased]: https://github.com/CodingAnarchy/tonic-rails/compare/v0.
|
|
128
|
+
[Unreleased]: https://github.com/CodingAnarchy/tonic-rails/compare/v0.4.0...HEAD
|
|
129
|
+
[0.4.0]: https://github.com/CodingAnarchy/tonic-rails/compare/v0.3.0...v0.4.0
|
|
120
130
|
[0.3.0]: https://github.com/CodingAnarchy/tonic-rails/compare/v0.2.0...v0.3.0
|
|
121
131
|
[0.2.0]: https://github.com/CodingAnarchy/tonic-rails/compare/v0.1.0...v0.2.0
|
|
122
132
|
[0.1.0]: https://github.com/CodingAnarchy/tonic-rails/releases/tag/v0.1.0
|
data/Cargo.lock
CHANGED
|
@@ -1021,9 +1021,27 @@ dependencies = [
|
|
|
1021
1021
|
"tonic-prost",
|
|
1022
1022
|
]
|
|
1023
1023
|
|
|
1024
|
+
[[package]]
|
|
1025
|
+
name = "tonic-web"
|
|
1026
|
+
version = "0.14.6"
|
|
1027
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
1028
|
+
checksum = "b5e6a1b6319ca4b61a4c0f0c94d439c8f3ed344cca56fe0df40e1fe4be11380b"
|
|
1029
|
+
dependencies = [
|
|
1030
|
+
"base64",
|
|
1031
|
+
"bytes",
|
|
1032
|
+
"http",
|
|
1033
|
+
"http-body",
|
|
1034
|
+
"pin-project",
|
|
1035
|
+
"tokio-stream",
|
|
1036
|
+
"tonic",
|
|
1037
|
+
"tower-layer",
|
|
1038
|
+
"tower-service",
|
|
1039
|
+
"tracing",
|
|
1040
|
+
]
|
|
1041
|
+
|
|
1024
1042
|
[[package]]
|
|
1025
1043
|
name = "tonic_rails_native"
|
|
1026
|
-
version = "0.
|
|
1044
|
+
version = "0.4.0"
|
|
1027
1045
|
dependencies = [
|
|
1028
1046
|
"axum",
|
|
1029
1047
|
"bytes",
|
|
@@ -1043,7 +1061,9 @@ dependencies = [
|
|
|
1043
1061
|
"tonic",
|
|
1044
1062
|
"tonic-health",
|
|
1045
1063
|
"tonic-reflection",
|
|
1064
|
+
"tonic-web",
|
|
1046
1065
|
"tower",
|
|
1066
|
+
"tower-http",
|
|
1047
1067
|
]
|
|
1048
1068
|
|
|
1049
1069
|
[[package]]
|
|
@@ -1065,6 +1085,20 @@ dependencies = [
|
|
|
1065
1085
|
"tracing",
|
|
1066
1086
|
]
|
|
1067
1087
|
|
|
1088
|
+
[[package]]
|
|
1089
|
+
name = "tower-http"
|
|
1090
|
+
version = "0.6.11"
|
|
1091
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
1092
|
+
checksum = "4cfcf7e2740e6fc6d4d688b4ef00650406bb94adf4731e43c096c3a19fe40840"
|
|
1093
|
+
dependencies = [
|
|
1094
|
+
"bitflags",
|
|
1095
|
+
"bytes",
|
|
1096
|
+
"http",
|
|
1097
|
+
"pin-project-lite",
|
|
1098
|
+
"tower-layer",
|
|
1099
|
+
"tower-service",
|
|
1100
|
+
]
|
|
1101
|
+
|
|
1068
1102
|
[[package]]
|
|
1069
1103
|
name = "tower-layer"
|
|
1070
1104
|
version = "0.3.3"
|
data/README.md
CHANGED
|
@@ -363,6 +363,44 @@ Tonic::Rails.configure do |config|
|
|
|
363
363
|
end
|
|
364
364
|
```
|
|
365
365
|
|
|
366
|
+
## Browser clients (gRPC-Web)
|
|
367
|
+
|
|
368
|
+
Browsers can't speak native gRPC, but they can speak
|
|
369
|
+
[gRPC-Web](https://github.com/grpc/grpc/blob/master/doc/PROTOCOL-WEB.md).
|
|
370
|
+
Turn it on and your existing handlers serve web frontends directly, with
|
|
371
|
+
no Envoy or other proxy:
|
|
372
|
+
|
|
373
|
+
```ruby
|
|
374
|
+
Tonic::Rails.configure do |config|
|
|
375
|
+
config.grpc_web = true
|
|
376
|
+
config.grpc_web_origins = ["https://app.example.com"] # CORS; omit for same-origin only
|
|
377
|
+
config.grpc_web_exposed_headers = %w[x-request-id] # response metadata browsers may read
|
|
378
|
+
end
|
|
379
|
+
```
|
|
380
|
+
|
|
381
|
+
Then call it from the browser with any gRPC-Web client, for example
|
|
382
|
+
[Connect](https://connectrpc.com/docs/web/using-clients):
|
|
383
|
+
|
|
384
|
+
```ts
|
|
385
|
+
import { createClient } from "@connectrpc/connect";
|
|
386
|
+
import { createGrpcWebTransport } from "@connectrpc/connect-web";
|
|
387
|
+
import { ItemService } from "./gen/demo/v1/items_pb";
|
|
388
|
+
|
|
389
|
+
const client = createClient(ItemService, createGrpcWebTransport({ baseUrl: "https://api.example.com" }));
|
|
390
|
+
const item = await client.getItem({ id: 1n });
|
|
391
|
+
```
|
|
392
|
+
|
|
393
|
+
- Unary and server-streaming RPCs work; browsers can't do client or
|
|
394
|
+
bidirectional streaming.
|
|
395
|
+
- Errors, response headers and trailers reach the browser as usual.
|
|
396
|
+
- Native gRPC clients keep working on the same port. Enabling gRPC-Web
|
|
397
|
+
also accepts HTTP/1.1, which browsers use without TLS; over HTTPS they
|
|
398
|
+
negotiate HTTP/2.
|
|
399
|
+
- `grpc_web_origins` sets CORS: listed origins may call with credentials
|
|
400
|
+
(cookies); `"*"` allows any origin without credentials (logged as a
|
|
401
|
+
warning). `grpc_web_allowed_headers` adds request headers beyond the
|
|
402
|
+
standard ones (`authorization` is already allowed).
|
|
403
|
+
|
|
366
404
|
## Testing handlers
|
|
367
405
|
|
|
368
406
|
`Tonic::Rails::Testing` calls a handler in-process, with real protobuf
|
|
@@ -441,6 +479,9 @@ end
|
|
|
441
479
|
| `tls_client_ca_path` | `nil` | PEM CA bundle; requires client certificates (mutual TLS) |
|
|
442
480
|
| `drain_timeout` | `10` | Seconds to let in-flight calls finish on shutdown |
|
|
443
481
|
| `keepalive_interval`, `keepalive_timeout` | `15`, `5` | HTTP/2 PING interval and timeout, seconds |
|
|
482
|
+
| `grpc_web` | `false` | Serve gRPC-Web for browser clients (see [Browser clients](#browser-clients-grpc-web)) |
|
|
483
|
+
| `grpc_web_origins` | `[]` | Browser origins allowed via CORS; `"*"` for any (no credentials) |
|
|
484
|
+
| `grpc_web_allowed_headers`, `grpc_web_exposed_headers` | `[]` | Extra CORS request / response headers |
|
|
444
485
|
| `health_check` | `nil` | Callable; while false or raising, health reports `NOT_SERVING` |
|
|
445
486
|
| `health_check_interval` | `5` | Seconds between health checks |
|
|
446
487
|
| `compression` | `false` | gzip responses over 1 KiB for clients that accept it |
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[package]
|
|
2
2
|
name = "tonic_rails_native"
|
|
3
|
-
version = "0.
|
|
3
|
+
version = "0.4.0"
|
|
4
4
|
edition = "2024"
|
|
5
5
|
rust-version = "1.88"
|
|
6
6
|
publish = false
|
|
@@ -28,6 +28,8 @@ rb-sys = "0.9"
|
|
|
28
28
|
tonic = { version = "0.14", default-features = false, features = ["transport", "router", "server", "tls-ring"] }
|
|
29
29
|
tonic-reflection = { version = "0.14", default-features = false, features = ["server"] }
|
|
30
30
|
tonic-health = { version = "0.14", default-features = false }
|
|
31
|
+
tonic-web = { version = "0.14", default-features = false }
|
|
32
|
+
tower-http = { version = "0.6", default-features = false, features = ["cors"] }
|
|
31
33
|
axum = { version = "0.8", default-features = false }
|
|
32
34
|
tokio = { version = "1", features = ["rt-multi-thread", "net", "sync", "macros", "time"] }
|
|
33
35
|
tokio-stream = { version = "0.1", features = ["net"] }
|
|
@@ -64,6 +64,7 @@ fn init(ruby: &Ruby) -> Result<(), Error> {
|
|
|
64
64
|
)?;
|
|
65
65
|
class.define_method("set_compression", method!(NativeServer::set_compression, 1))?;
|
|
66
66
|
class.define_method("set_reflection", method!(NativeServer::set_reflection, 1))?;
|
|
67
|
+
class.define_method("set_grpc_web", method!(NativeServer::set_grpc_web, 3))?;
|
|
67
68
|
class.define_method("set_routes", method!(NativeServer::set_routes, 1))?;
|
|
68
69
|
class.define_method(
|
|
69
70
|
"set_reflection_descriptors",
|
|
@@ -29,6 +29,9 @@ use tokio::sync::{Semaphore, watch};
|
|
|
29
29
|
use tonic_health::ServingStatus;
|
|
30
30
|
use tonic_health::server::HealthReporter;
|
|
31
31
|
|
|
32
|
+
use http::{HeaderName, HeaderValue, Method};
|
|
33
|
+
use tower_http::cors::{AllowOrigin, CorsLayer};
|
|
34
|
+
|
|
32
35
|
use crate::body::ServerStats;
|
|
33
36
|
use crate::dispatch::{DispatchInner, DispatchService};
|
|
34
37
|
use crate::gvl::{self, Cancel, DispatchContext};
|
|
@@ -47,6 +50,7 @@ struct Options {
|
|
|
47
50
|
keepalive_interval: Duration,
|
|
48
51
|
keepalive_timeout: Duration,
|
|
49
52
|
reflection: bool,
|
|
53
|
+
grpc_web: Option<GrpcWebOptions>,
|
|
50
54
|
dispatch_queue_size: usize,
|
|
51
55
|
routes: Vec<String>,
|
|
52
56
|
descriptors: Vec<Vec<u8>>,
|
|
@@ -63,6 +67,7 @@ impl Default for Options {
|
|
|
63
67
|
keepalive_interval: Duration::from_secs(15),
|
|
64
68
|
keepalive_timeout: Duration::from_secs(5),
|
|
65
69
|
reflection: false,
|
|
70
|
+
grpc_web: None,
|
|
66
71
|
dispatch_queue_size: 128,
|
|
67
72
|
routes: Vec::new(),
|
|
68
73
|
descriptors: Vec::new(),
|
|
@@ -70,6 +75,76 @@ impl Default for Options {
|
|
|
70
75
|
}
|
|
71
76
|
}
|
|
72
77
|
|
|
78
|
+
/// gRPC-Web and the CORS policy browsers need to use it.
|
|
79
|
+
struct GrpcWebOptions {
|
|
80
|
+
/// Allowed browser origins; empty = same-origin only; ["*"] = any.
|
|
81
|
+
origins: Vec<String>,
|
|
82
|
+
allow_headers: Vec<String>,
|
|
83
|
+
expose_headers: Vec<String>,
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/// Request headers gRPC-Web clients send, beyond the CORS-safelisted ones.
|
|
87
|
+
const GRPC_WEB_REQUEST_HEADERS: [&str; 5] = [
|
|
88
|
+
"content-type",
|
|
89
|
+
"x-grpc-web",
|
|
90
|
+
"x-user-agent",
|
|
91
|
+
"grpc-timeout",
|
|
92
|
+
"authorization",
|
|
93
|
+
];
|
|
94
|
+
/// Response headers browsers must be allowed to read for gRPC-Web.
|
|
95
|
+
const GRPC_WEB_RESPONSE_HEADERS: [&str; 3] = ["grpc-status", "grpc-message", "grpc-status-details-bin"];
|
|
96
|
+
|
|
97
|
+
fn header_names(ruby: &Ruby, defaults: &[&str], extra: &[String]) -> Result<Vec<HeaderName>, Error> {
|
|
98
|
+
defaults
|
|
99
|
+
.iter()
|
|
100
|
+
.map(|name| name.to_string())
|
|
101
|
+
.chain(extra.iter().map(|name| name.to_ascii_lowercase()))
|
|
102
|
+
.map(|name| {
|
|
103
|
+
HeaderName::from_bytes(name.as_bytes())
|
|
104
|
+
.map_err(|_| arg_error(ruby, format!("invalid gRPC-Web header name: {name:?}")))
|
|
105
|
+
})
|
|
106
|
+
.collect()
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/// None when no cross-origin access is configured (same-origin only).
|
|
110
|
+
fn cors_layer(ruby: &Ruby, web: &GrpcWebOptions) -> Result<Option<CorsLayer>, Error> {
|
|
111
|
+
if web.origins.is_empty() {
|
|
112
|
+
return Ok(None);
|
|
113
|
+
}
|
|
114
|
+
let layer = CorsLayer::new()
|
|
115
|
+
.allow_methods([Method::POST])
|
|
116
|
+
.allow_headers(header_names(ruby, &GRPC_WEB_REQUEST_HEADERS, &web.allow_headers)?)
|
|
117
|
+
.expose_headers(header_names(
|
|
118
|
+
ruby,
|
|
119
|
+
&GRPC_WEB_RESPONSE_HEADERS,
|
|
120
|
+
&web.expose_headers,
|
|
121
|
+
)?)
|
|
122
|
+
.max_age(Duration::from_secs(24 * 60 * 60));
|
|
123
|
+
if web.origins.iter().any(|origin| origin == "*") {
|
|
124
|
+
if web.origins.len() > 1 {
|
|
125
|
+
return Err(arg_error(
|
|
126
|
+
ruby,
|
|
127
|
+
"grpc_web_origins: \"*\" can't be combined with other origins",
|
|
128
|
+
));
|
|
129
|
+
}
|
|
130
|
+
// Browsers refuse credentials with a wildcard origin, so none here.
|
|
131
|
+
return Ok(Some(layer.allow_origin(AllowOrigin::any())));
|
|
132
|
+
}
|
|
133
|
+
let origins = web
|
|
134
|
+
.origins
|
|
135
|
+
.iter()
|
|
136
|
+
.map(|origin| {
|
|
137
|
+
HeaderValue::from_str(origin.trim_end_matches('/'))
|
|
138
|
+
.map_err(|_| arg_error(ruby, format!("invalid gRPC-Web origin: {origin:?}")))
|
|
139
|
+
})
|
|
140
|
+
.collect::<Result<Vec<_>, _>>()?;
|
|
141
|
+
Ok(Some(
|
|
142
|
+
layer
|
|
143
|
+
.allow_origin(AllowOrigin::list(origins))
|
|
144
|
+
.allow_credentials(true),
|
|
145
|
+
))
|
|
146
|
+
}
|
|
147
|
+
|
|
73
148
|
/// State that exists only while the server is running.
|
|
74
149
|
struct Running {
|
|
75
150
|
runtime: Runtime,
|
|
@@ -199,6 +274,20 @@ impl NativeServer {
|
|
|
199
274
|
self.compression.store(enabled, Ordering::Relaxed);
|
|
200
275
|
}
|
|
201
276
|
|
|
277
|
+
/// Enable gRPC-Web (and HTTP/1.1, which browsers use without TLS).
|
|
278
|
+
pub fn set_grpc_web(
|
|
279
|
+
&self,
|
|
280
|
+
origins: Vec<String>,
|
|
281
|
+
allow_headers: Vec<String>,
|
|
282
|
+
expose_headers: Vec<String>,
|
|
283
|
+
) {
|
|
284
|
+
self.options.lock().grpc_web = Some(GrpcWebOptions {
|
|
285
|
+
origins,
|
|
286
|
+
allow_headers,
|
|
287
|
+
expose_headers,
|
|
288
|
+
});
|
|
289
|
+
}
|
|
290
|
+
|
|
202
291
|
pub fn set_reflection(&self, enabled: bool) {
|
|
203
292
|
self.options.lock().reflection = enabled;
|
|
204
293
|
}
|
|
@@ -347,7 +436,17 @@ impl NativeServer {
|
|
|
347
436
|
.tls_config(tls)
|
|
348
437
|
.map_err(|e| runtime_error(ruby, format!("invalid TLS configuration: {e}")))?;
|
|
349
438
|
}
|
|
350
|
-
|
|
439
|
+
// CORS outermost, so preflight requests are answered before the
|
|
440
|
+
// gRPC-Web translation sees them. Both are no-ops when disabled.
|
|
441
|
+
let (cors, grpc_web) = match &options.grpc_web {
|
|
442
|
+
Some(web) => (cors_layer(ruby, web)?, Some(tonic_web::GrpcWebLayer::new())),
|
|
443
|
+
None => (None, None),
|
|
444
|
+
};
|
|
445
|
+
let router = builder
|
|
446
|
+
.accept_http1(options.grpc_web.is_some())
|
|
447
|
+
.layer(tower::util::option_layer(cors))
|
|
448
|
+
.layer(tower::util::option_layer(grpc_web))
|
|
449
|
+
.add_routes(grpc_routes);
|
|
351
450
|
|
|
352
451
|
let permits = match options.max_connections {
|
|
353
452
|
0 => Semaphore::MAX_PERMITS,
|
|
@@ -66,6 +66,25 @@ module Tonic
|
|
|
66
66
|
# API schema; keep it off in production. Default false.
|
|
67
67
|
attr_accessor :reflection
|
|
68
68
|
|
|
69
|
+
# Accept gRPC-Web requests, so browser apps can call services
|
|
70
|
+
# directly (unary and server-streaming RPCs). Also enables HTTP/1.1,
|
|
71
|
+
# which browsers use when there's no TLS. Default false.
|
|
72
|
+
attr_accessor :grpc_web
|
|
73
|
+
|
|
74
|
+
# Browser origins allowed to call gRPC-Web services (CORS), e.g.
|
|
75
|
+
# ["https://app.example.com"]. Empty = same-origin only. "*" allows
|
|
76
|
+
# any origin, without credentials (cookies). Default [].
|
|
77
|
+
attr_accessor :grpc_web_origins
|
|
78
|
+
|
|
79
|
+
# Extra request headers browsers may send (beyond content-type,
|
|
80
|
+
# x-grpc-web, x-user-agent, grpc-timeout and authorization).
|
|
81
|
+
attr_accessor :grpc_web_allowed_headers
|
|
82
|
+
|
|
83
|
+
# Extra response headers browsers may read, e.g. custom response
|
|
84
|
+
# metadata (grpc-status, grpc-message and grpc-status-details-bin are
|
|
85
|
+
# always exposed).
|
|
86
|
+
attr_accessor :grpc_web_exposed_headers
|
|
87
|
+
|
|
69
88
|
# HTTP/2 PING interval and ack timeout, in seconds. Defaults 15 / 5.
|
|
70
89
|
attr_accessor :keepalive_interval, :keepalive_timeout
|
|
71
90
|
|
|
@@ -105,6 +124,10 @@ module Tonic
|
|
|
105
124
|
@keepalive_interval = 15
|
|
106
125
|
@keepalive_timeout = 5
|
|
107
126
|
@reload_handlers = false
|
|
127
|
+
@grpc_web = false
|
|
128
|
+
@grpc_web_origins = []
|
|
129
|
+
@grpc_web_allowed_headers = []
|
|
130
|
+
@grpc_web_exposed_headers = []
|
|
108
131
|
@health_check = nil
|
|
109
132
|
@health_check_interval = 5
|
|
110
133
|
end
|
|
@@ -128,12 +151,22 @@ module Tonic
|
|
|
128
151
|
raise ArgumentError, "tls_client_ca_path requires tls_cert_path and tls_key_path" if tls_client_ca_path && !tls?
|
|
129
152
|
|
|
130
153
|
validate_callables!
|
|
154
|
+
validate_grpc_web!
|
|
131
155
|
|
|
132
156
|
self
|
|
133
157
|
end
|
|
134
158
|
|
|
135
159
|
private
|
|
136
160
|
|
|
161
|
+
def validate_grpc_web!
|
|
162
|
+
%i[grpc_web_origins grpc_web_allowed_headers grpc_web_exposed_headers].each do |name|
|
|
163
|
+
value = public_send(name)
|
|
164
|
+
next if value.is_a?(Array) && value.all?(String)
|
|
165
|
+
|
|
166
|
+
raise ArgumentError, "#{name} must be an Array of Strings, got #{value.inspect}"
|
|
167
|
+
end
|
|
168
|
+
end
|
|
169
|
+
|
|
137
170
|
def validate_callables!
|
|
138
171
|
raise ArgumentError, "health_check must respond to #call" if health_check && !health_check.respond_to?(:call)
|
|
139
172
|
unless health_check_interval.is_a?(Numeric) && health_check_interval.positive?
|
data/lib/tonic/rails/server.rb
CHANGED
|
@@ -130,19 +130,30 @@ module Tonic
|
|
|
130
130
|
native = TonicRails::Native::Server.new(config.bind_address, config.max_message_size)
|
|
131
131
|
native.on_request(build_callback)
|
|
132
132
|
native.set_routes(@router.paths)
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
133
|
+
apply_connection_settings(native, config)
|
|
134
|
+
apply_features(native, config)
|
|
135
|
+
native
|
|
136
|
+
end
|
|
137
|
+
|
|
138
|
+
def apply_connection_settings(native, config)
|
|
137
139
|
native.set_max_connections(config.max_connections)
|
|
138
140
|
native.set_concurrency_limit_per_connection(config.concurrency_limit_per_connection)
|
|
139
141
|
native.set_worker_threads(config.worker_threads || 0)
|
|
140
142
|
native.set_keepalive(config.keepalive_interval.to_f, config.keepalive_timeout.to_f)
|
|
141
143
|
native.set_dispatch_queue_size(config.dispatch_queue_size)
|
|
144
|
+
end
|
|
145
|
+
|
|
146
|
+
def apply_features(native, config)
|
|
147
|
+
if config.tls?
|
|
148
|
+
native.set_tls(config.tls_cert_path, config.tls_key_path)
|
|
149
|
+
native.set_tls_client_ca(config.tls_client_ca_path) if config.tls_client_ca_path
|
|
150
|
+
end
|
|
142
151
|
native.set_compression(config.compression ? true : false)
|
|
143
152
|
native.set_reflection(config.reflection ? true : false)
|
|
144
153
|
native.set_reflection_descriptors(Reflection.file_descriptors(@router.service_names)) if config.reflection
|
|
145
|
-
|
|
154
|
+
return unless config.grpc_web
|
|
155
|
+
|
|
156
|
+
native.set_grpc_web(config.grpc_web_origins, config.grpc_web_allowed_headers, config.grpc_web_exposed_headers)
|
|
146
157
|
end
|
|
147
158
|
|
|
148
159
|
# The proc the transport calls for every RPC:
|
|
@@ -273,6 +284,16 @@ module Tonic
|
|
|
273
284
|
logger&.info("tonic-rails: stopped")
|
|
274
285
|
end
|
|
275
286
|
|
|
287
|
+
def log_grpc_web(logger, config)
|
|
288
|
+
return unless config.grpc_web
|
|
289
|
+
|
|
290
|
+
origins = config.grpc_web_origins
|
|
291
|
+
logger.info("tonic-rails: gRPC-Web enabled (#{origins.empty? ? 'same-origin only' : "origins: #{origins.join(', ')}"})")
|
|
292
|
+
return unless origins.include?("*")
|
|
293
|
+
|
|
294
|
+
logger.warn("tonic-rails: gRPC-Web allows any origin; any website can call these services from a visitor's browser")
|
|
295
|
+
end
|
|
296
|
+
|
|
276
297
|
def log_startup
|
|
277
298
|
config = @configuration
|
|
278
299
|
logger = config.logger
|
|
@@ -284,6 +305,7 @@ module Tonic
|
|
|
284
305
|
logger.warn("tonic-rails: TLS is not configured; gRPC traffic is plaintext")
|
|
285
306
|
end
|
|
286
307
|
logger.info("tonic-rails: reflection enabled") if config.reflection
|
|
308
|
+
log_grpc_web(logger, config)
|
|
287
309
|
logger.info(
|
|
288
310
|
"tonic-rails: serving #{@router.service_names.join(', ')} on #{config.bind_address} " \
|
|
289
311
|
"(#{config.dispatch_threads} dispatch threads)",
|
data/lib/tonic/rails/version.rb
CHANGED