@@ -293,58 +293,22 @@ module Protocol
293293 message ? URI .decode_www_form_component(message) : nil
294294 end
295295
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
315305
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
346309 end
347310 end
311+ end
348312 end
349313end
350314```
@@ -754,7 +718,7 @@ module Protocol
754718 # Find handler
755719 handler = @services [service_name]
756720 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 } " )
758722 end
759723
760724 # Determine handler method and message classes
@@ -773,17 +737,17 @@ module Protocol
773737 end
774738
775739 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 } " )
777741 end
778742
779743 # 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
787751 end
788752
789753 protected
@@ -811,20 +775,22 @@ module Protocol
811775 handler.send(method, input, output, call)
812776 output.close_write unless output.closed?
813777
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 )
820781
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
828794 end
829795 end
830796end
@@ -953,7 +919,7 @@ def handle_grpc_request(http_request)
953919 # Add status as trailer - these will be sent after the response body
954920 # Note: The user just adds them to headers; the @tail marker ensures
955921 # 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 )
957923
958924 Protocol ::HTTP ::Response [200 , headers, output]
959925end
0 commit comments