protocol-grpc 0.4.0 → 0.5.1

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: 301c1d74991d960be7af4b50850ffa24aab9e4c2c7aedc97759518027aacfe18
4
- data.tar.gz: ac2fe4b32aa211ef8dbf4329a8677ae09deced47ab21816c0b6fb361d346dcd2
3
+ metadata.gz: 121472581aa9d3d84bc5b1542bbe6d49274180ae0129d151a2b8f6aca9377026
4
+ data.tar.gz: 820130b179cf141d525cc483900a14edbc68705c6f318e469c258c1e6cb0735d
5
5
  SHA512:
6
- metadata.gz: 2467aaa36d36c6d011ee4913984defc1974d008bf25081777863d83b4c3c7c7825556ae571a99cc74858a7eece0c99cf253953230bbb7220244082a2c32bd95f
7
- data.tar.gz: 222fe2052157790f288d72f6f6feff1ad42d507ab188e2bce23cbf0f2e9fa63d7eb0966f774935e5ae0ad3e5a497170f8b126f9dfa4d10ccf53b7f385d7b8437
6
+ metadata.gz: 8576fe2b1c9f6f0b88e2472c34bd5720e48e33c43da18845360e52c10017410ea1b730d206a71c47ad8e8b5e7b9e132e9e795524e8baea89f67156bb660e0bfc
7
+ data.tar.gz: cc61e8bc843aa2e27ad8a6c9d9e63788be37729a53060b19a486b572e6761aa35fb066edf1493b63ff4b71bb1f5058af6014a7c8deb3c9f92d6c76a272925e9a
checksums.yaml.gz.sig CHANGED
Binary file
@@ -129,3 +129,4 @@ call.deadline.exceeded? # => false
129
129
  # Access peer information
130
130
  call.peer # => Protocol::HTTP::Address
131
131
  ```
132
+
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
- # Build headers with gRPC status and message
297
- # @parameter status [Integer] gRPC status code
298
- # @parameter message [String, nil] Optional status message
299
- # @parameter policy [Hash] Header policy to use
300
- # @returns [Protocol::HTTP::Headers]
301
- def self.build_status_headers(status: Status::OK, message: nil, policy: HEADER_POLICY)
302
- headers = Protocol::HTTP::Headers.new([], nil, policy: policy)
303
- headers["grpc-status"] = status.to_s
304
- headers["grpc-message"] = URI.encode_www_form_component(message) if message
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 status as trailers to existing headers
317
- # @parameter headers [Protocol::HTTP::Headers]
318
- # @parameter status [Integer] gRPC status code
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 trailers_only_error(Status::UNIMPLEMENTED, "Service not found: #{service_name}")
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 trailers_only_error(Status::UNIMPLEMENTED, "Method not found: #{method_name}")
740
+ return make_response(Status::UNIMPLEMENTED, "Method not found: #{method_name}")
777
741
  end
778
742
 
779
743
  # Handle the RPC
780
- begin
781
- handle_rpc(request, handler, handler_method, request_class, response_class)
782
- rescue Error => error
783
- trailers_only_error(e.status_code, error.message)
784
- rescue => error
785
- trailers_only_error(Status::INTERNAL, error.message)
786
- end
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
- # Mark trailers and add status
815
- response_headers.trailer!
816
- Metadata.add_status_trailer!(response_headers, status: Status::OK)
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
- def trailers_only_error(status_code, message)
822
- Metadata.build_trailers_only_response(
823
- status: status_code,
824
- message: message,
825
- policy: HEADER_POLICY
826
- )
827
- end
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.add_status_trailer!(headers, status: Protocol::GRPC::Status::OK)
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
@@ -75,57 +75,23 @@ module Protocol
75
75
  end
76
76
  end
77
77
 
78
- # Build headers with gRPC status and message
79
- # @parameter status [Integer] gRPC status code
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
- def self.add_status_header!(headers, status: Status::OK, message: nil)
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
- Protocol::HTTP::Response[200, headers, nil]
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
- trailers_only_error(error.status_code, error.message)
33
+ make_response(error.status_code, error.message, error: error)
34
34
  rescue StandardError => error
35
- trailers_only_error(Status::INTERNAL, error.message)
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
- protected
48
+ protected
49
49
 
50
50
  # Check if the request is a gRPC request.
51
51
  # @parameter request [Protocol::HTTP::Request]
@@ -55,16 +55,18 @@ module Protocol
55
55
  content_type&.start_with?("application/grpc")
56
56
  end
57
57
 
58
- # Build a trailers-only error response.
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 trailers_only_error(status_code, message)
63
- Metadata.build_trailers_only_response(
64
- status: status_code,
65
- message: message,
66
- policy: HEADER_POLICY
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
+
67
+ Metadata.add_status!(headers, status: status_code, message: message, error: error)
68
+
69
+ Protocol::HTTP::Response[200, headers, nil]
68
70
  end
69
71
  end
70
72
  end
@@ -7,6 +7,7 @@
7
7
  module Protocol
8
8
  # @namespace
9
9
  module GRPC
10
- VERSION = "0.4.0"
10
+ VERSION = "0.5.1"
11
11
  end
12
12
  end
13
+
data/readme.md CHANGED
@@ -28,6 +28,12 @@ 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.5.0
32
+
33
+ - Server-side errors now automatically include backtraces in response headers when an error object is provided. Backtraces are transmitted as arrays via Split headers and can be extracted by clients.
34
+ - Consolidated `add_status_trailer!`, `add_status_header!`, `build_status_headers`, `prepare_trailers!`, and `build_trailers_only_response` into a single `add_status!` method. Whether status becomes headers or trailers is now controlled by the protocol layer.
35
+ - Renamed `trailers_only_error` to `make_response` and inlined response creation logic. The method now accepts an `error:` parameter for automatic backtrace extraction.
36
+
31
37
  ### v0.4.0
32
38
 
33
39
  - Add `RPC#name`.
data/releases.md CHANGED
@@ -1,5 +1,11 @@
1
1
  # Releases
2
2
 
3
+ ## v0.5.0
4
+
5
+ - Server-side errors now automatically include backtraces in response headers when an error object is provided. Backtraces are transmitted as arrays via Split headers and can be extracted by clients.
6
+ - Consolidated `add_status_trailer!`, `add_status_header!`, `build_status_headers`, `prepare_trailers!`, and `build_trailers_only_response` into a single `add_status!` method. Whether status becomes headers or trailers is now controlled by the protocol layer.
7
+ - Renamed `trailers_only_error` to `make_response` and inlined response creation logic. The method now accepts an `error:` parameter for automatic backtrace extraction.
8
+
3
9
  ## v0.4.0
4
10
 
5
11
  - Add `RPC#name`.
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.0
4
+ version: 0.5.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Samuel Williams
@@ -70,30 +70,30 @@ dependencies:
70
70
  name: google-protobuf
71
71
  requirement: !ruby/object:Gem::Requirement
72
72
  requirements:
73
- - - "~>"
73
+ - - ">="
74
74
  - !ruby/object:Gem::Version
75
- version: '4.0'
75
+ version: '0'
76
76
  type: :runtime
77
77
  prerelease: false
78
78
  version_requirements: !ruby/object:Gem::Requirement
79
79
  requirements:
80
- - - "~>"
80
+ - - ">="
81
81
  - !ruby/object:Gem::Version
82
- version: '4.0'
82
+ version: '0'
83
83
  - !ruby/object:Gem::Dependency
84
84
  name: protocol-http
85
85
  requirement: !ruby/object:Gem::Requirement
86
86
  requirements:
87
87
  - - "~>"
88
88
  - !ruby/object:Gem::Version
89
- version: '0.28'
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.28'
96
+ version: '0.56'
97
97
  executables: []
98
98
  extensions: []
99
99
  extra_rdoc_files: []
metadata.gz.sig CHANGED
Binary file