88
99module Protocol
1010 module GRPC
11+ # @namespace
1112 module Body
12- # Reads length-prefixed gRPC messages from an HTTP body
13- # This is the standard readable body for gRPC - all gRPC responses use message framing
13+ # Represents a readable body for gRPC messages with length-prefixed framing.
14+ # This is the standard readable body for gRPC - all gRPC responses use message framing.
1415 class ReadableBody
16+ # Initialize a new readable body for gRPC messages.
1517 # @parameter body [Protocol::HTTP::Body::Readable] The underlying HTTP body
16- # @parameter message_class [Class, nil ] Protobuf message class with .decode method
17- # If nil, returns raw binary data (useful for channel adapters)
18- # @parameter encoding [String, nil ] Compression encoding (from grpc-encoding header)
18+ # @parameter message_class [Class | Nil ] Protobuf message class with .decode method.
19+ # If ` nil` , returns raw binary data (useful for channel adapters)
20+ # @parameter encoding [String | Nil ] Compression encoding (from grpc-encoding header)
1921 def initialize ( body , message_class : nil , encoding : nil )
2022 @body = body
2123 @message_class = message_class
@@ -24,15 +26,15 @@ def initialize(body, message_class: nil, encoding: nil)
2426 @closed = false
2527 end
2628
27- # The input body.
28- # @attribute [Protocol::HTTP::Body::Readable]
29+ # @attribute [Protocol::HTTP::Body::Readable] The underlying HTTP body.
2930 attr_reader :body
3031
31- # The compression encoding.
32- # @attribute [String, nil]
32+ # @attribute [String | Nil] The compression encoding.
3333 attr_reader :encoding
3434
35- # Close the input and output bodies.
35+ # Close the input body.
36+ # @parameter error [Exception | Nil] Optional error that caused the close
37+ # @returns [Nil]
3638 def close ( error = nil )
3739 @closed = true
3840
@@ -44,18 +46,20 @@ def close(error = nil)
4446 nil
4547 end
4648
47- # Whether the stream has been closed.
49+ # Check if the stream has been closed.
50+ # @returns [Boolean] `true` if the stream is closed, `false` otherwise
4851 def closed?
4952 @closed or @body . nil?
5053 end
5154
52- # Whether there are any input chunks remaining?
55+ # Check if there are any input chunks remaining.
56+ # @returns [Boolean] `true` if the body is empty, `false` otherwise
5357 def empty?
5458 @body . nil?
5559 end
5660
57- # Read the next gRPC message
58- # @returns [Object | String | Nil] Decoded message, raw binary, or nil if stream ended
61+ # Read the next gRPC message.
62+ # @returns [Object | String | Nil] Decoded message, raw binary, or `Nil` if stream ended
5963 def read
6064 return nil if closed?
6165
@@ -66,17 +70,17 @@ def read
6670 compressed = prefix [ 0 ] . unpack1 ( "C" ) == 1
6771 length = prefix [ 1 ..4 ] . unpack1 ( "N" )
6872
69- # Read the message body
73+ # Read the message body:
7074 data = read_exactly ( length )
7175 return nil unless data
7276
73- # Decompress if needed
77+ # Decompress if needed:
7478 data = decompress ( data ) if compressed
7579
76- # Decode using message class if provided, otherwise return binary
80+ # Decode using message class if provided, otherwise return binary:
7781 # This allows binary mode for channel adapters
7882 if @message_class
79- # Use protobuf gem's decode method
83+ # Use protobuf gem's decode method:
8084 @message_class . decode ( data )
8185 else
8286 data # Return raw binary
@@ -103,39 +107,46 @@ def each
103107
104108 private
105109
110+ # Read exactly n bytes from the underlying body.
111+ # @parameter n [Integer] The number of bytes to read
112+ # @returns [String | Nil] The data read, or `Nil` if the stream ended
106113 def read_exactly ( n )
107- # Fill buffer until we have enough data
114+ # Fill buffer until we have enough data:
108115 while @buffer . bytesize < n
109116 return nil if closed?
110117
111- # Read chunk from underlying body
118+ # Read chunk from underlying body:
112119 chunk = @body . read
113120
114121 if chunk . nil?
115- # End of stream
122+ # End of stream:
116123 if @body && !@closed
117124 @body . close
118125 @closed = true
119126 end
120127 return nil
121128 end
122129
123- # Append to buffer
130+ # Append to buffer:
124131 @buffer << chunk . force_encoding ( Encoding ::BINARY )
125132
126- # Check if body is empty and close if needed
133+ # Check if body is empty and close if needed:
127134 if @body . empty?
128135 @body . close
129136 @closed = true
130137 end
131138 end
132139
133- # Extract the required data
140+ # Extract the required data:
134141 data = @buffer [ 0 ...n ]
135142 @buffer = @buffer [ n ..]
136143 data
137144 end
138145
146+ # Decompress data using the configured encoding.
147+ # @parameter data [String] The compressed data
148+ # @returns [String] The decompressed data
149+ # @raises [Error] If decompression fails
139150 def decompress ( data )
140151 case @encoding
141152 when "gzip"
0 commit comments