protocol-grpc 0.3.0 → 0.5.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 +0 -1
- data/design.md +38 -72
- data/lib/protocol/grpc/body/readable_body.rb +5 -0
- data/lib/protocol/grpc/interface.rb +4 -2
- data/lib/protocol/grpc/metadata.rb +10 -44
- data/lib/protocol/grpc/middleware.rb +11 -10
- data/lib/protocol/grpc/version.rb +1 -1
- data/readme.md +4 -0
- data/releases.md +4 -0
- 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: ef695986f77971ac09708cb8bc1cbb694fd402c1c6f2fcc9070b26309a95fbcb
|
|
4
|
+
data.tar.gz: 2ba1216276a20618b2e93034981f15522f5b6dae44a615d51796b08dc67f2dbe
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: a5c9661129270d4ccd3d47b84ae6d3e2058f1c7499fb9302f69a85d08dbe67ea305d72f23d2ab36f62ab728a40982b5ae7f9b1c2aaa29e09378206912b5afec7
|
|
7
|
+
data.tar.gz: bb0825134335b844320744dda1130e3fd9426c5eb4b8894a17cdb408c0d67b978c3a2bcb05c9a5164faa00d0ebf0903097c9533e0695aac8963068265eb3eb90
|
checksums.yaml.gz.sig
CHANGED
|
Binary file
|
data/context/getting-started.md
CHANGED
data/design.md
CHANGED
|
@@ -293,58 +293,22 @@ module Protocol
|
|
|
293
293
|
message ? URI.decode_www_form_component(message) : nil
|
|
294
294
|
end
|
|
295
295
|
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
headers
|
|
306
|
-
end
|
|
307
|
-
|
|
308
|
-
# Mark that trailers will follow (call after sending initial headers)
|
|
309
|
-
# @parameter headers [Protocol::HTTP::Headers]
|
|
310
|
-
# @returns [Protocol::HTTP::Headers]
|
|
311
|
-
def self.prepare_trailers!(headers)
|
|
312
|
-
headers.trailer!
|
|
313
|
-
headers
|
|
314
|
-
end
|
|
296
|
+
# Add gRPC status, message, and optional backtrace to headers.
|
|
297
|
+
# Whether these become headers or trailers is controlled by the protocol layer.
|
|
298
|
+
# @parameter headers [Protocol::HTTP::Headers]
|
|
299
|
+
# @parameter status [Integer] gRPC status code
|
|
300
|
+
# @parameter message [String | Nil] Optional status message
|
|
301
|
+
# @parameter error [Exception | Nil] Optional error object (used to extract backtrace)
|
|
302
|
+
def self.add_status!(headers, status: Status::OK, message: nil, error: nil)
|
|
303
|
+
headers["grpc-status"] = Header::Status.new(status)
|
|
304
|
+
headers["grpc-message"] = Header::Message.new(Header::Message.encode(message)) if message
|
|
315
305
|
|
|
316
|
-
# Add
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
# @parameter message [String, nil] Optional status message
|
|
320
|
-
def self.add_status_trailer!(headers, status: Status::OK, message: nil)
|
|
321
|
-
headers.trailer! unless headers.trailer?
|
|
322
|
-
headers["grpc-status"] = status.to_s
|
|
323
|
-
headers["grpc-message"] = URI.encode_www_form_component(message) if message
|
|
324
|
-
end
|
|
325
|
-
|
|
326
|
-
# Add status as initial headers (for trailers-only responses)
|
|
327
|
-
# @parameter headers [Protocol::HTTP::Headers]
|
|
328
|
-
# @parameter status [Integer] gRPC status code
|
|
329
|
-
# @parameter message [String, nil] Optional status message
|
|
330
|
-
def self.add_status_header!(headers, status: Status::OK, message: nil)
|
|
331
|
-
headers["grpc-status"] = status.to_s
|
|
332
|
-
headers["grpc-message"] = URI.encode_www_form_component(message) if message
|
|
333
|
-
end
|
|
334
|
-
|
|
335
|
-
# Build a trailers-only error response (no body, status in headers)
|
|
336
|
-
# @parameter status [Integer] gRPC status code
|
|
337
|
-
# @parameter message [String, nil] Optional status message
|
|
338
|
-
# @parameter policy [Hash] Header policy to use
|
|
339
|
-
# @returns [Protocol::HTTP::Response]
|
|
340
|
-
def self.build_trailers_only_response(status:, message: nil, policy: HEADER_POLICY)
|
|
341
|
-
headers = Protocol::HTTP::Headers.new([], nil, policy: policy)
|
|
342
|
-
headers["content-type"] = "application/grpc+proto"
|
|
343
|
-
add_status_header!(headers, status: status, message: message)
|
|
344
|
-
|
|
345
|
-
Protocol::HTTP::Response[200, headers, nil]
|
|
306
|
+
# Add backtrace from error if available
|
|
307
|
+
if error && error.backtrace && !error.backtrace.empty?
|
|
308
|
+
headers["backtrace"] = error.backtrace
|
|
346
309
|
end
|
|
347
310
|
end
|
|
311
|
+
end
|
|
348
312
|
end
|
|
349
313
|
end
|
|
350
314
|
```
|
|
@@ -754,7 +718,7 @@ module Protocol
|
|
|
754
718
|
# Find handler
|
|
755
719
|
handler = @services[service_name]
|
|
756
720
|
unless handler
|
|
757
|
-
return
|
|
721
|
+
return make_response(Status::UNIMPLEMENTED, "Service not found: #{service_name}")
|
|
758
722
|
end
|
|
759
723
|
|
|
760
724
|
# Determine handler method and message classes
|
|
@@ -773,17 +737,17 @@ module Protocol
|
|
|
773
737
|
end
|
|
774
738
|
|
|
775
739
|
unless handler.respond_to?(handler_method)
|
|
776
|
-
return
|
|
740
|
+
return make_response(Status::UNIMPLEMENTED, "Method not found: #{method_name}")
|
|
777
741
|
end
|
|
778
742
|
|
|
779
743
|
# Handle the RPC
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
744
|
+
begin
|
|
745
|
+
handle_rpc(request, handler, handler_method, request_class, response_class)
|
|
746
|
+
rescue Error => error
|
|
747
|
+
make_response(error.status_code, error.message, error: error)
|
|
748
|
+
rescue => error
|
|
749
|
+
make_response(Status::INTERNAL, error.message, error: error)
|
|
750
|
+
end
|
|
787
751
|
end
|
|
788
752
|
|
|
789
753
|
protected
|
|
@@ -811,20 +775,22 @@ module Protocol
|
|
|
811
775
|
handler.send(method, input, output, call)
|
|
812
776
|
output.close_write unless output.closed?
|
|
813
777
|
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
Protocol::HTTP::Response[200, response_headers, output]
|
|
819
|
-
end
|
|
778
|
+
# Mark trailers and add status
|
|
779
|
+
response_headers.trailer!
|
|
780
|
+
Metadata.add_status!(response_headers, status: Status::OK)
|
|
820
781
|
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
782
|
+
Protocol::HTTP::Response[200, response_headers, output]
|
|
783
|
+
end
|
|
784
|
+
|
|
785
|
+
protected
|
|
786
|
+
|
|
787
|
+
def make_response(status_code, message, error: nil)
|
|
788
|
+
headers = Protocol::HTTP::Headers.new([], nil, policy: HEADER_POLICY)
|
|
789
|
+
headers["content-type"] = "application/grpc+proto"
|
|
790
|
+
Metadata.add_status!(headers, status: status_code, message: message, error: error)
|
|
791
|
+
|
|
792
|
+
Protocol::HTTP::Response[200, headers, nil]
|
|
793
|
+
end
|
|
828
794
|
end
|
|
829
795
|
end
|
|
830
796
|
end
|
|
@@ -953,7 +919,7 @@ def handle_grpc_request(http_request)
|
|
|
953
919
|
# Add status as trailer - these will be sent after the response body
|
|
954
920
|
# Note: The user just adds them to headers; the @tail marker ensures
|
|
955
921
|
# they're recognized as trailers internally
|
|
956
|
-
Protocol::GRPC::Metadata.
|
|
922
|
+
Protocol::GRPC::Metadata.add_status!(headers, status: Protocol::GRPC::Status::OK)
|
|
957
923
|
|
|
958
924
|
Protocol::HTTP::Response[200, headers, output]
|
|
959
925
|
end
|
|
@@ -15,6 +15,11 @@ module Protocol
|
|
|
15
15
|
# This is the standard readable body for gRPC - all gRPC responses use message framing.
|
|
16
16
|
# Wraps the underlying HTTP body and transforms raw chunks into decoded gRPC messages.
|
|
17
17
|
class ReadableBody < Protocol::HTTP::Body::Wrapper
|
|
18
|
+
# Wrap the body of a message.
|
|
19
|
+
#
|
|
20
|
+
# @parameter message [Request | Response] The message to wrap.
|
|
21
|
+
# @parameter options [Hash] The options to pass to the initializer.
|
|
22
|
+
# @returns [ReadableBody | Nil] The wrapped body or `nil` if the message has no body.
|
|
18
23
|
def self.wrap(message, **options)
|
|
19
24
|
if body = message.body
|
|
20
25
|
message.body = self.new(body, **options)
|
|
@@ -11,8 +11,8 @@ module Protocol
|
|
|
11
11
|
# Can be used by both client stubs and server implementations.
|
|
12
12
|
class Interface
|
|
13
13
|
# RPC method definition
|
|
14
|
-
RPC = Struct.new(:request_class, :response_class, :streaming, :method, keyword_init: true) do
|
|
15
|
-
def initialize(request_class:, response_class:, streaming: :unary, method: nil)
|
|
14
|
+
RPC = Struct.new(:name, :request_class, :response_class, :streaming, :method, keyword_init: true) do
|
|
15
|
+
def initialize(name:, request_class:, response_class:, streaming: :unary, method: nil)
|
|
16
16
|
super
|
|
17
17
|
end
|
|
18
18
|
|
|
@@ -40,6 +40,8 @@ module Protocol
|
|
|
40
40
|
# @parameter streaming [Symbol] Streaming type (:unary, :server_streaming, :client_streaming, :bidirectional)
|
|
41
41
|
# @parameter method [Symbol | Nil] Optional explicit Ruby method name (snake_case). If not provided, automatically converts PascalCase to snake_case.
|
|
42
42
|
def self.rpc(name, **options)
|
|
43
|
+
options[:name] = name
|
|
44
|
+
|
|
43
45
|
# Ensure snake_case method name is always available
|
|
44
46
|
options[:method] ||= pascal_case_to_snake_case(name.to_s).to_sym
|
|
45
47
|
|
|
@@ -75,57 +75,23 @@ module Protocol
|
|
|
75
75
|
end
|
|
76
76
|
end
|
|
77
77
|
|
|
78
|
-
#
|
|
79
|
-
#
|
|
80
|
-
# @parameter message [String | Nil] Optional status message
|
|
81
|
-
# @parameter policy [Hash] Header policy to use
|
|
82
|
-
# @returns [Protocol::HTTP::Headers]
|
|
83
|
-
def self.build_status_headers(status: Status::OK, message: nil, policy: HEADER_POLICY)
|
|
84
|
-
headers = Protocol::HTTP::Headers.new([], nil, policy: policy)
|
|
85
|
-
headers["grpc-status"] = Header::Status.new(status)
|
|
86
|
-
headers["grpc-message"] = Header::Message.new(Header::Message.encode(message)) if message
|
|
87
|
-
headers
|
|
88
|
-
end
|
|
89
|
-
|
|
90
|
-
# Mark that trailers will follow (call after sending initial headers)
|
|
91
|
-
# @parameter headers [Protocol::HTTP::Headers]
|
|
92
|
-
# @returns [Protocol::HTTP::Headers]
|
|
93
|
-
def self.prepare_trailers!(headers)
|
|
94
|
-
headers.trailer!
|
|
95
|
-
headers
|
|
96
|
-
end
|
|
97
|
-
|
|
98
|
-
# Add status as trailers to existing headers
|
|
99
|
-
# @parameter headers [Protocol::HTTP::Headers]
|
|
100
|
-
# @parameter status [Integer] gRPC status code
|
|
101
|
-
# @parameter message [String | Nil] Optional status message
|
|
102
|
-
def self.add_status_trailer!(headers, status: Status::OK, message: nil)
|
|
103
|
-
headers.trailer! unless headers.trailer?
|
|
104
|
-
headers["grpc-status"] = Header::Status.new(status)
|
|
105
|
-
headers["grpc-message"] = Header::Message.new(Header::Message.encode(message)) if message
|
|
106
|
-
end
|
|
107
|
-
|
|
108
|
-
# Add status as initial headers (for trailers-only responses)
|
|
78
|
+
# Add gRPC status, message, and optional backtrace to headers.
|
|
79
|
+
# Whether these become headers or trailers is controlled by the protocol layer.
|
|
109
80
|
# @parameter headers [Protocol::HTTP::Headers]
|
|
110
81
|
# @parameter status [Integer] gRPC status code
|
|
111
82
|
# @parameter message [String | Nil] Optional status message
|
|
112
|
-
|
|
83
|
+
# @parameter error [Exception | Nil] Optional error object (used to extract backtrace)
|
|
84
|
+
def self.add_status!(headers, status: Status::OK, message: nil, error: nil)
|
|
113
85
|
headers["grpc-status"] = Header::Status.new(status)
|
|
114
86
|
headers["grpc-message"] = Header::Message.new(Header::Message.encode(message)) if message
|
|
115
|
-
end
|
|
116
|
-
|
|
117
|
-
# Build a trailers-only error response (no body, status in headers)
|
|
118
|
-
# @parameter status [Integer] gRPC status code
|
|
119
|
-
# @parameter message [String | Nil] Optional status message
|
|
120
|
-
# @parameter policy [Hash] Header policy to use
|
|
121
|
-
# @returns [Protocol::HTTP::Response]
|
|
122
|
-
def self.build_trailers_only_response(status:, message: nil, policy: HEADER_POLICY)
|
|
123
|
-
headers = Protocol::HTTP::Headers.new([], nil, policy: policy)
|
|
124
|
-
headers["content-type"] = "application/grpc+proto"
|
|
125
|
-
add_status_header!(headers, status: status, message: message)
|
|
126
87
|
|
|
127
|
-
|
|
88
|
+
# Add backtrace from error if available
|
|
89
|
+
if error && error.backtrace && !error.backtrace.empty?
|
|
90
|
+
# Assign backtrace array directly - Split header will handle it
|
|
91
|
+
headers["backtrace"] = error.backtrace
|
|
92
|
+
end
|
|
128
93
|
end
|
|
94
|
+
|
|
129
95
|
end
|
|
130
96
|
end
|
|
131
97
|
end
|
|
@@ -30,9 +30,9 @@ module Protocol
|
|
|
30
30
|
begin
|
|
31
31
|
dispatch(request)
|
|
32
32
|
rescue Error => error
|
|
33
|
-
|
|
33
|
+
make_response(error.status_code, error.message, error: error)
|
|
34
34
|
rescue StandardError => error
|
|
35
|
-
|
|
35
|
+
make_response(Status::INTERNAL, error.message, error: error)
|
|
36
36
|
end
|
|
37
37
|
end
|
|
38
38
|
|
|
@@ -45,7 +45,7 @@ module Protocol
|
|
|
45
45
|
raise NotImplementedError, "Subclasses must implement #dispatch"
|
|
46
46
|
end
|
|
47
47
|
|
|
48
|
-
|
|
48
|
+
protected
|
|
49
49
|
|
|
50
50
|
# Check if the request is a gRPC request.
|
|
51
51
|
# @parameter request [Protocol::HTTP::Request]
|
|
@@ -55,16 +55,17 @@ module Protocol
|
|
|
55
55
|
content_type&.start_with?("application/grpc")
|
|
56
56
|
end
|
|
57
57
|
|
|
58
|
-
#
|
|
58
|
+
# Make a gRPC error response with status and optional message.
|
|
59
59
|
# @parameter status_code [Integer] gRPC status code
|
|
60
60
|
# @parameter message [String] Error message
|
|
61
|
+
# @parameter error [Exception] Optional error object (used to extract backtrace)
|
|
61
62
|
# @returns [Protocol::HTTP::Response]
|
|
62
|
-
def
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
63
|
+
def make_response(status_code, message, error: nil)
|
|
64
|
+
headers = Protocol::HTTP::Headers.new([], nil, policy: HEADER_POLICY)
|
|
65
|
+
headers["content-type"] = "application/grpc+proto"
|
|
66
|
+
Metadata.add_status!(headers, status: status_code, message: message, error: error)
|
|
67
|
+
|
|
68
|
+
Protocol::HTTP::Response[200, headers, nil]
|
|
68
69
|
end
|
|
69
70
|
end
|
|
70
71
|
end
|
data/readme.md
CHANGED
|
@@ -28,6 +28,10 @@ Please see the [project documentation](https://socketry.github.io/protocol-grpc/
|
|
|
28
28
|
|
|
29
29
|
Please see the [project releases](https://socketry.github.io/protocol-grpc/releases/index) for all releases.
|
|
30
30
|
|
|
31
|
+
### v0.4.0
|
|
32
|
+
|
|
33
|
+
- Add `RPC#name`.
|
|
34
|
+
|
|
31
35
|
### v0.3.0
|
|
32
36
|
|
|
33
37
|
- **Breaking**: `Protocol::GRPC::Call` now takes a `response` object parameter instead of separate `response_headers`.
|
data/releases.md
CHANGED
data.tar.gz.sig
CHANGED
|
Binary file
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: protocol-grpc
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.5.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Samuel Williams
|
|
@@ -86,14 +86,14 @@ dependencies:
|
|
|
86
86
|
requirements:
|
|
87
87
|
- - "~>"
|
|
88
88
|
- !ruby/object:Gem::Version
|
|
89
|
-
version: '0.
|
|
89
|
+
version: '0.56'
|
|
90
90
|
type: :runtime
|
|
91
91
|
prerelease: false
|
|
92
92
|
version_requirements: !ruby/object:Gem::Requirement
|
|
93
93
|
requirements:
|
|
94
94
|
- - "~>"
|
|
95
95
|
- !ruby/object:Gem::Version
|
|
96
|
-
version: '0.
|
|
96
|
+
version: '0.56'
|
|
97
97
|
executables: []
|
|
98
98
|
extensions: []
|
|
99
99
|
extra_rdoc_files: []
|
metadata.gz.sig
CHANGED
|
Binary file
|