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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: dc699a3d9a47acc1b8bdb7196b0da1280847caa14d5405e0150e8608cd701501
4
- data.tar.gz: 3884004454f86b2a7cd7c1ad460c00f6c820edae6b0bccc7a87d177a0cccee72
3
+ metadata.gz: b1e1ab7ce49d13b36bbee671e1598de070bc068921dc46b26671ca8c13a45a19
4
+ data.tar.gz: fefcc712682312abdaf415ca87c9beaa5c81997332aebe3c50d366ba3157353d
5
5
  SHA512:
6
- metadata.gz: 6d487819bdab689d575b8985e84b81d1ad1293126f9a7f64d697853cf723a4cdf67bb607f0e20faa0f1e547baf0eea6d9dc102da576b450bcba5e42f72ad2e95
7
- data.tar.gz: 9f2bf511ff8fc16db342add52bedbfbf1c924c6109346e9d3e877abb2e2ff6f03f40c50ef9a46406df2895b22b6a32845a22a7d444617ed65bc11ab4ee82ae52
6
+ metadata.gz: 98ff4adf8e4570de34415d400c56ece807cfb153619ea1c12a058c9cfb46577f7ff533c8310c1a4d6c7ab1f7936f21d4e9d5559315ae52772a532b71513f585a
7
+ data.tar.gz: 5a6ccfc411717a17d8d6e2309ebd0710bf0226882d9cea239f9c73925115fbcfdccb2b6903dc84c13c9d1b401b5f9001bc20ffe5e1bf7c294f64d3b286ca8f33
checksums.yaml.gz.sig CHANGED
Binary file
@@ -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::DispatcherMiddleware} - Middleware that routes requests to registered services.
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/dispatcher_middleware"
107
+ require "async/grpc/dispatcher"
108
108
 
109
- dispatcher = Async::GRPC::DispatcherMiddleware.new
109
+ dispatcher = Async::GRPC::Dispatcher.new
110
110
 
111
111
  service = GreeterService.new(GreeterInterface, "hello.Greeter")
112
- dispatcher.register("hello.Greeter", service)
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::DispatcherMiddleware.new
155
- dispatcher.register("hello.Greeter", GreeterService.new(GreeterInterface, "hello.Greeter"))
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
- "https://localhost:50051",
487
- protocol: Async::HTTP::Protocol::HTTP2
486
+ "https://localhost:50051",
487
+ protocol: Async::HTTP::Protocol::HTTP2
488
488
  )
489
489
 
490
490
  server = Async::HTTP::Server.new(middleware, endpoint)
@@ -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.close
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
- rescue
220
- response.close
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
- block.call(body) if block_given?
240
- body.close_write unless body.closed?
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.close
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
- return readable_body unless block_given?
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
- readable_body
308
- rescue StandardError
309
- response.close
310
- raise
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
- # Represents middleware that dispatches gRPC requests to registered services.
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 = DispatcherMiddleware.new
25
- # dispatcher.register("hello.Greeter", GreeterService.new(GreeterInterface, "hello.Greeter"))
26
- # dispatcher.register("world.Greeter", WorldService.new(WorldInterface, "world.Greeter"))
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 DispatcherMiddleware < Protocol::GRPC::Middleware
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
- def register(service_name, service)
42
- @services[service_name] = service
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
@@ -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 DispatcherMiddleware for routing.
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 = DispatcherMiddleware.new
36
- # dispatcher.register("hello.Greeter", GreeterService.new(GreeterInterface, "hello.Greeter"))
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.
@@ -7,7 +7,7 @@
7
7
  module Async
8
8
  # @namespace
9
9
  module GRPC
10
- VERSION = "0.2.0"
10
+ VERSION = "0.4.0"
11
11
  end
12
12
  end
13
13
 
data/lib/async/grpc.rb CHANGED
@@ -7,7 +7,7 @@ require_relative "grpc/version"
7
7
  require_relative "grpc/client"
8
8
  require_relative "grpc/service"
9
9
  require_relative "grpc/stub"
10
- require_relative "grpc/dispatcher_middleware"
10
+ require_relative "grpc/dispatcher"
11
11
 
12
12
  module Async
13
13
  module GRPC
data/license.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # MIT License
2
2
 
3
- Copyright, 2025, by Samuel Williams.
3
+ Copyright, 2025-2026, by Samuel Williams.
4
4
 
5
5
  Permission is hereby granted, free of charge, to any person obtaining a copy
6
6
  of this software and associated documentation files (the "Software"), to deal
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** - `DispatcherMiddleware` routes requests to registered services based on path.
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.
@@ -205,11 +205,11 @@ client.unary_binary(service, method, request_binary) # => response_binary
205
205
 
206
206
  ```ruby
207
207
  client.unary(
208
- service,
209
- method,
210
- request,
211
- marshal: ->(obj){obj.to_proto},
212
- unmarshal: ->(data){MyReply.decode(data)}
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.2.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/dispatcher_middleware.rb
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: 3.6.9
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