async-grpc 0.2.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
- checksums.yaml.gz.sig +0 -0
- data/context/getting-started.md +6 -6
- data/design.md +2 -2
- data/lib/async/grpc/client.rb +28 -23
- data/lib/async/grpc/{dispatcher_middleware.rb → dispatcher.rb} +9 -9
- data/lib/async/grpc/service.rb +3 -3
- data/lib/async/grpc/version.rb +1 -1
- data/lib/async/grpc.rb +1 -1
- data/license.md +1 -1
- data/readme.md +10 -1
- data/releases.md +9 -0
- data/spanner_integration.md +5 -5
- data.tar.gz.sig +0 -0
- metadata +3 -3
- metadata.gz.sig +0 -0
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: b1e1ab7ce49d13b36bbee671e1598de070bc068921dc46b26671ca8c13a45a19
|
|
4
|
+
data.tar.gz: fefcc712682312abdaf415ca87c9beaa5c81997332aebe3c50d366ba3157353d
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 98ff4adf8e4570de34415d400c56ece807cfb153619ea1c12a058c9cfb46577f7ff533c8310c1a4d6c7ab1f7936f21d4e9d5559315ae52772a532b71513f585a
|
|
7
|
+
data.tar.gz: 5a6ccfc411717a17d8d6e2309ebd0710bf0226882d9cea239f9c73925115fbcfdccb2b6903dc84c13c9d1b401b5f9001bc20ffe5e1bf7c294f64d3b286ca8f33
|
checksums.yaml.gz.sig
CHANGED
|
Binary file
|
data/context/getting-started.md
CHANGED
|
@@ -23,7 +23,7 @@ $ bundle add protocol-grpc async-http
|
|
|
23
23
|
- {ruby Async::GRPC::Client} - An asynchronous gRPC client that wraps `Async::HTTP::Client`.
|
|
24
24
|
- {ruby Async::GRPC::Stub} - A method-based stub for making RPC calls.
|
|
25
25
|
- {ruby Async::GRPC::Service} - A concrete service implementation that uses a `Protocol::GRPC::Interface`.
|
|
26
|
-
- {ruby Async::GRPC::
|
|
26
|
+
- {ruby Async::GRPC::Dispatcher} - Middleware that routes requests to registered services.
|
|
27
27
|
|
|
28
28
|
## Client Usage
|
|
29
29
|
|
|
@@ -104,12 +104,12 @@ end
|
|
|
104
104
|
### Registering Services
|
|
105
105
|
|
|
106
106
|
``` ruby
|
|
107
|
-
require "async/grpc/
|
|
107
|
+
require "async/grpc/dispatcher"
|
|
108
108
|
|
|
109
|
-
dispatcher = Async::GRPC::
|
|
109
|
+
dispatcher = Async::GRPC::Dispatcher.new
|
|
110
110
|
|
|
111
111
|
service = GreeterService.new(GreeterInterface, "hello.Greeter")
|
|
112
|
-
dispatcher.register(
|
|
112
|
+
dispatcher.register(service)
|
|
113
113
|
```
|
|
114
114
|
|
|
115
115
|
### Running a Server
|
|
@@ -151,8 +151,8 @@ end
|
|
|
151
151
|
Async do
|
|
152
152
|
# Setup server
|
|
153
153
|
endpoint = Async::HTTP::Endpoint.parse("http://localhost:50051")
|
|
154
|
-
dispatcher = Async::GRPC::
|
|
155
|
-
dispatcher.register(
|
|
154
|
+
dispatcher = Async::GRPC::Dispatcher.new
|
|
155
|
+
dispatcher.register(GreeterService.new(GreeterInterface, "hello.Greeter"))
|
|
156
156
|
server = Async::HTTP::Server.for(endpoint, dispatcher)
|
|
157
157
|
|
|
158
158
|
# Setup client
|
data/design.md
CHANGED
|
@@ -483,8 +483,8 @@ middleware.register("my_service.Greeter", GreeterService.new)
|
|
|
483
483
|
|
|
484
484
|
# Use with Async::HTTP::Server - it handles everything!
|
|
485
485
|
endpoint = Async::HTTP::Endpoint.parse(
|
|
486
|
-
|
|
487
|
-
|
|
486
|
+
"https://localhost:50051",
|
|
487
|
+
protocol: Async::HTTP::Protocol::HTTP2
|
|
488
488
|
)
|
|
489
489
|
|
|
490
490
|
server = Async::HTTP::Server.new(middleware, endpoint)
|
data/lib/async/grpc/client.rb
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
# Released under the MIT License.
|
|
4
|
-
# Copyright, 2025, by Samuel Williams.
|
|
4
|
+
# Copyright, 2025-2026, by Samuel Williams.
|
|
5
5
|
|
|
6
6
|
require "async"
|
|
7
7
|
require "async/http/client"
|
|
@@ -173,7 +173,7 @@ module Async
|
|
|
173
173
|
|
|
174
174
|
if response_body
|
|
175
175
|
response_value = response_body.read
|
|
176
|
-
response_body.
|
|
176
|
+
response_body.finish
|
|
177
177
|
end
|
|
178
178
|
|
|
179
179
|
# Check status after reading body (trailers are now available)
|
|
@@ -215,10 +215,12 @@ module Async
|
|
|
215
215
|
# Check status after reading all body chunks (trailers are now available):
|
|
216
216
|
check_status!(response)
|
|
217
217
|
|
|
218
|
+
# Nullify the response so we don't close it in ensure:
|
|
219
|
+
response = nil
|
|
220
|
+
|
|
218
221
|
return response_body
|
|
219
|
-
|
|
220
|
-
response
|
|
221
|
-
raise
|
|
222
|
+
ensure
|
|
223
|
+
response&.close
|
|
222
224
|
end
|
|
223
225
|
end
|
|
224
226
|
|
|
@@ -236,8 +238,11 @@ module Async
|
|
|
236
238
|
|
|
237
239
|
http_request = Protocol::HTTP::Request["POST", path, headers, body]
|
|
238
240
|
|
|
239
|
-
|
|
240
|
-
|
|
241
|
+
if block_given?
|
|
242
|
+
block.call(body)
|
|
243
|
+
end
|
|
244
|
+
|
|
245
|
+
body.close_write
|
|
241
246
|
|
|
242
247
|
response = call(http_request)
|
|
243
248
|
|
|
@@ -251,12 +256,12 @@ module Async
|
|
|
251
256
|
)
|
|
252
257
|
|
|
253
258
|
message = readable_body.read
|
|
254
|
-
readable_body.
|
|
259
|
+
readable_body.finish
|
|
255
260
|
|
|
256
261
|
# Check status after reading body (trailers are now available):
|
|
257
262
|
check_status!(response)
|
|
258
263
|
|
|
259
|
-
message
|
|
264
|
+
return message
|
|
260
265
|
ensure
|
|
261
266
|
response.close
|
|
262
267
|
end
|
|
@@ -289,25 +294,25 @@ module Async
|
|
|
289
294
|
encoding: response_encoding
|
|
290
295
|
)
|
|
291
296
|
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
begin
|
|
295
|
-
block.call(readable_body, body)
|
|
296
|
-
body.close_write unless body.closed?
|
|
297
|
-
|
|
298
|
-
# Consume all response chunks to ensure trailers are available:
|
|
299
|
-
readable_body.each{|_|}
|
|
300
|
-
ensure
|
|
301
|
-
readable_body.close
|
|
297
|
+
unless block_given?
|
|
298
|
+
return readable_body
|
|
302
299
|
end
|
|
303
300
|
|
|
301
|
+
block.call(body, readable_body)
|
|
302
|
+
body.close_write
|
|
303
|
+
|
|
304
|
+
# Consume all response chunks to ensure trailers are available:
|
|
305
|
+
readable_body.finish
|
|
306
|
+
|
|
304
307
|
# Check status after reading all body chunks (trailers are now available):
|
|
305
308
|
check_status!(response)
|
|
306
309
|
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
310
|
+
# Nullify the response so we don't close it in ensure:
|
|
311
|
+
response = nil
|
|
312
|
+
|
|
313
|
+
return readable_body
|
|
314
|
+
ensure
|
|
315
|
+
response&.close
|
|
311
316
|
end
|
|
312
317
|
end
|
|
313
318
|
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
# Released under the MIT License.
|
|
4
|
-
# Copyright, 2025, by Samuel Williams.
|
|
4
|
+
# Copyright, 2025-2026, by Samuel Williams.
|
|
5
5
|
|
|
6
6
|
require "async"
|
|
7
7
|
require "async/deadline"
|
|
@@ -17,16 +17,16 @@ require "protocol/grpc/status"
|
|
|
17
17
|
|
|
18
18
|
module Async
|
|
19
19
|
module GRPC
|
|
20
|
-
#
|
|
20
|
+
# Dispatches gRPC requests to registered services.
|
|
21
21
|
# Handles routing based on service name from the request path.
|
|
22
22
|
#
|
|
23
23
|
# @example Registering services:
|
|
24
|
-
# dispatcher =
|
|
25
|
-
# dispatcher.register(
|
|
26
|
-
# dispatcher.register(
|
|
24
|
+
# dispatcher = Dispatcher.new
|
|
25
|
+
# dispatcher.register(GreeterService.new(GreeterInterface, "hello.Greeter"))
|
|
26
|
+
# dispatcher.register(WorldService.new(WorldInterface, "world.Greeter"))
|
|
27
27
|
#
|
|
28
28
|
# server = Async::HTTP::Server.for(endpoint, dispatcher)
|
|
29
|
-
class
|
|
29
|
+
class Dispatcher < Protocol::GRPC::Middleware
|
|
30
30
|
# Initialize the dispatcher.
|
|
31
31
|
# @parameter app [#call | Nil] The next middleware in the chain
|
|
32
32
|
# @parameter services [Hash] Optional initial services hash (service_name => service_instance)
|
|
@@ -36,10 +36,10 @@ module Async
|
|
|
36
36
|
end
|
|
37
37
|
|
|
38
38
|
# Register a service.
|
|
39
|
-
# @parameter service_name [String] Service name (e.g., "hello.Greeter")
|
|
40
39
|
# @parameter service [Async::GRPC::Service] Service instance
|
|
41
|
-
|
|
42
|
-
|
|
40
|
+
# @parameter name [String] Service name (defaults to service.service_name)
|
|
41
|
+
def register(service, name: service.service_name)
|
|
42
|
+
@services[name] = service
|
|
43
43
|
end
|
|
44
44
|
|
|
45
45
|
protected
|
data/lib/async/grpc/service.rb
CHANGED
|
@@ -9,7 +9,7 @@ module Async
|
|
|
9
9
|
module GRPC
|
|
10
10
|
# Represents a concrete service implementation that uses an Interface.
|
|
11
11
|
# Subclass this and implement the RPC methods defined in the interface.
|
|
12
|
-
# Services are registered with
|
|
12
|
+
# Services are registered with Dispatcher for routing.
|
|
13
13
|
#
|
|
14
14
|
# @example Example service implementation:
|
|
15
15
|
# class GreeterInterface < Protocol::GRPC::Interface
|
|
@@ -32,8 +32,8 @@ module Async
|
|
|
32
32
|
# end
|
|
33
33
|
#
|
|
34
34
|
# # Register with dispatcher:
|
|
35
|
-
# dispatcher =
|
|
36
|
-
# dispatcher.register(
|
|
35
|
+
# dispatcher = Dispatcher.new
|
|
36
|
+
# dispatcher.register(GreeterService.new(GreeterInterface, "hello.Greeter"))
|
|
37
37
|
# server = Async::HTTP::Server.for(endpoint, dispatcher)
|
|
38
38
|
class Service
|
|
39
39
|
# Initialize a new service instance.
|
data/lib/async/grpc/version.rb
CHANGED
data/lib/async/grpc.rb
CHANGED
data/license.md
CHANGED
data/readme.md
CHANGED
|
@@ -10,7 +10,7 @@ Asynchronous gRPC client and server implementation built on top of `protocol-grp
|
|
|
10
10
|
|
|
11
11
|
- **Asynchronous client** - Wraps `Async::HTTP::Client` to provide gRPC-specific call methods with automatic message framing and status handling.
|
|
12
12
|
- **Method-based stubs** - Create type-safe stubs from `Protocol::GRPC::Interface` definitions. Accepts both PascalCase and snake\_case method names for convenience.
|
|
13
|
-
- **Server middleware** - `
|
|
13
|
+
- **Server middleware** - `Dispatcher` routes requests to registered services based on path.
|
|
14
14
|
- **All RPC patterns** - Supports unary, server streaming, client streaming, and bidirectional streaming RPCs.
|
|
15
15
|
- **HTTP/1 and HTTP/2 transport** - Built on `async-http` with automatic HTTP/2 multiplexing and connection pooling.
|
|
16
16
|
|
|
@@ -24,6 +24,15 @@ Please see the [project documentation](https://socketry.github.io/async-grpc/) f
|
|
|
24
24
|
|
|
25
25
|
Please see the [project releases](https://socketry.github.io/async-grpc/releases/index) for all releases.
|
|
26
26
|
|
|
27
|
+
### v0.4.0
|
|
28
|
+
|
|
29
|
+
- Fix handling of trailers.
|
|
30
|
+
|
|
31
|
+
### v0.3.0
|
|
32
|
+
|
|
33
|
+
- **Breaking**: Renamed `DispatcherMiddleware` to `Dispatcher` for cleaner API.
|
|
34
|
+
- **Breaking**: Simplified `Dispatcher#register` API to `register(service, name: service.service_name)`, eliminating redundant service name specification.
|
|
35
|
+
|
|
27
36
|
### v0.2.0
|
|
28
37
|
|
|
29
38
|
- Added `Async::GRPC::RemoteError` class to encapsulate remote error details including message and backtrace extracted from response headers.
|
data/releases.md
CHANGED
|
@@ -1,5 +1,14 @@
|
|
|
1
1
|
# Releases
|
|
2
2
|
|
|
3
|
+
## v0.4.0
|
|
4
|
+
|
|
5
|
+
- Fix handling of trailers.
|
|
6
|
+
|
|
7
|
+
## v0.3.0
|
|
8
|
+
|
|
9
|
+
- **Breaking**: Renamed `DispatcherMiddleware` to `Dispatcher` for cleaner API.
|
|
10
|
+
- **Breaking**: Simplified `Dispatcher#register` API to `register(service, name: service.service_name)`, eliminating redundant service name specification.
|
|
11
|
+
|
|
3
12
|
## v0.2.0
|
|
4
13
|
|
|
5
14
|
- Added `Async::GRPC::RemoteError` class to encapsulate remote error details including message and backtrace extracted from response headers.
|
data/spanner_integration.md
CHANGED
|
@@ -205,11 +205,11 @@ client.unary_binary(service, method, request_binary) # => response_binary
|
|
|
205
205
|
|
|
206
206
|
```ruby
|
|
207
207
|
client.unary(
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
208
|
+
service,
|
|
209
|
+
method,
|
|
210
|
+
request,
|
|
211
|
+
marshal: ->(obj){obj.to_proto},
|
|
212
|
+
unmarshal: ->(data){MyReply.decode(data)}
|
|
213
213
|
)
|
|
214
214
|
```
|
|
215
215
|
|
data.tar.gz.sig
CHANGED
|
Binary file
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: async-grpc
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.4.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Samuel Williams
|
|
@@ -76,7 +76,7 @@ files:
|
|
|
76
76
|
- design.md
|
|
77
77
|
- lib/async/grpc.rb
|
|
78
78
|
- lib/async/grpc/client.rb
|
|
79
|
-
- lib/async/grpc/
|
|
79
|
+
- lib/async/grpc/dispatcher.rb
|
|
80
80
|
- lib/async/grpc/remote_error.rb
|
|
81
81
|
- lib/async/grpc/service.rb
|
|
82
82
|
- lib/async/grpc/stub.rb
|
|
@@ -105,7 +105,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
105
105
|
- !ruby/object:Gem::Version
|
|
106
106
|
version: '0'
|
|
107
107
|
requirements: []
|
|
108
|
-
rubygems_version:
|
|
108
|
+
rubygems_version: 4.0.3
|
|
109
109
|
specification_version: 4
|
|
110
110
|
summary: Client and server implementation for gRPC using Async.
|
|
111
111
|
test_files: []
|
metadata.gz.sig
CHANGED
|
Binary file
|