tonic-rails 0.3.0-x86_64-linux-gnu → 0.4.0-x86_64-linux-gnu
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/README.md +41 -0
- data/lib/tonic/rails/3.3/tonic_rails_native.so +0 -0
- data/lib/tonic/rails/3.4/tonic_rails_native.so +0 -0
- data/lib/tonic/rails/4.0/tonic_rails_native.so +0 -0
- 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: 06cf21277f2b5442bae8386269a576c89515959d2144d9d8ed3e171b24e73c7d
|
|
4
|
+
data.tar.gz: 9862766277deb1548e0e6e88d48e611746282d430578b198d1f81430e759cf69
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 9a72581a9b168d58c71402037b41e0df2650dcad092fa1163b0bf738508da03d3db6babf9c0651b58636fe989be50431231176152b1e7ed894838871a8a706e5
|
|
7
|
+
data.tar.gz: 5397e3379da5008ccc472b4b3d6f3f88e649675e0290b271dde7a2cfeb1843f45287cbd4d0776750304d12bcb19179c7c2206ef0c05cd7af1744721e6d57c790
|
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/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 |
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -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