33# Released under the MIT License.
44# Copyright, 2025, by Samuel Williams.
55
6+ require "async"
7+ require "async/deadline"
8+
69require "protocol/grpc/middleware"
710require "protocol/grpc/methods"
811require "protocol/grpc/call"
@@ -41,6 +44,38 @@ def register(service_name, service)
4144
4245 protected
4346
47+ def invoke_service ( service , handler_method , input , output , call )
48+ begin
49+ service . send ( handler_method , input , output , call )
50+ ensure
51+ # Close input stream:
52+ input . close
53+
54+ # Close output stream:
55+ output . close_write unless output . closed?
56+ end
57+
58+ # Mark trailers and add status (if not already set by handler):
59+ if call . response &.headers
60+ call . response . headers . trailer!
61+
62+ # Only add OK status if grpc-status hasn't been set by the handler:
63+ unless call . response . headers [ "grpc-status" ]
64+ Protocol ::GRPC ::Metadata . add_status_trailer! ( call . response . headers , status : Protocol ::GRPC ::Status ::OK )
65+ end
66+ end
67+ end
68+
69+ def dispatch_to_service ( service , handler_method , input , output , call , deadline , parent : Async ::Task . current )
70+ if deadline
71+ parent . with_timeout ( deadline ) do
72+ invoke_service ( service , handler_method , input , output , call )
73+ end
74+ else
75+ invoke_service ( service , handler_method , input , output , call )
76+ end
77+ end
78+
4479 # Dispatch the request to the appropriate service.
4580 # @parameter request [Protocol::HTTP::Request] The HTTP request
4681 # @returns [Protocol::HTTP::Response] The HTTP response
@@ -66,9 +101,9 @@ def dispatch(request)
66101 raise Protocol ::GRPC ::Error . new ( Protocol ::GRPC ::Status ::UNIMPLEMENTED , "Method not found: #{ method_name } " )
67102 end
68103
69- handler_method = rpc_descriptor [ : method]
70- request_class = rpc_descriptor [ : request_class]
71- response_class = rpc_descriptor [ : response_class]
104+ handler_method = rpc_descriptor . method
105+ request_class = rpc_descriptor . request_class
106+ response_class = rpc_descriptor . response_class
72107
73108 # Verify handler method exists:
74109 unless service . respond_to? ( handler_method , true )
@@ -80,34 +115,34 @@ def dispatch(request)
80115 input = Protocol ::GRPC ::Body ::ReadableBody . new ( request . body , message_class : request_class , encoding : encoding )
81116 output = Protocol ::GRPC ::Body ::WritableBody . new ( message_class : response_class , encoding : encoding )
82117
83- # Create call context :
118+ # Create response headers :
84119 response_headers = Protocol ::HTTP ::Headers . new ( [ ] , nil , policy : Protocol ::GRPC ::HEADER_POLICY )
85120 response_headers [ "content-type" ] = "application/grpc+proto"
86121 response_headers [ "grpc-encoding" ] = encoding if encoding
87122
123+ # Create response object:
124+ response = Protocol ::HTTP ::Response [ 200 , response_headers , output ]
125+
88126 # Parse deadline from timeout header:
89- timeout_value = request . headers [ "grpc-timeout" ]
90- deadline = if timeout_value
91- timeout_seconds = Protocol ::GRPC ::Methods . parse_timeout ( timeout_value )
92- require "async/deadline"
93- Async ::Deadline . start ( timeout_seconds ) if timeout_seconds
127+ timeout = Protocol ::GRPC ::Methods . parse_timeout ( request . headers [ "grpc-timeout" ] )
128+ deadline = if timeout
129+ Async ::Deadline . start ( timeout )
94130 end
95131
96- call = Protocol ::GRPC ::Call . new ( request , deadline : deadline )
97-
98- # Call the handler method on the service:
99- service . send ( handler_method , input , output , call )
132+ # Create call context with request and response:
133+ call = Protocol ::GRPC ::Call . new ( request , response , deadline : deadline )
100134
101- # Close output stream:
102- output . close_write unless output . closed?
103-
104- # Mark trailers and add status:
105- response_headers . trailer!
106- Protocol ::GRPC ::Metadata . add_status_trailer! ( response_headers , status : Protocol ::GRPC ::Status ::OK )
135+ if rpc_descriptor . streaming?
136+ Async do |task |
137+ dispatch_to_service ( service , handler_method , input , output , call , deadline , parent : task )
138+ end
139+ else
140+ # Unary call:
141+ dispatch_to_service ( service , handler_method , input , output , call , deadline )
142+ end
107143
108- Protocol :: HTTP :: Response [ 200 , response_headers , output ]
144+ response
109145 end
110146 end
111147 end
112148end
113-
0 commit comments