Class: Protocol::HTTP1::Body::Chunked
- Inherits:
-
HTTP::Body::Readable
- Object
- HTTP::Body::Readable
- Protocol::HTTP1::Body::Chunked
- Defined in:
- lib/protocol/http1/body/chunked.rb
Overview
Represents a chunked body, which is a series of chunks, each with a length prefix.
See https://tools.ietf.org/html/rfc7230#section-4.1 for more details on the chunked transfer encoding.
Constant Summary collapse
- CRLF =
"\r\n"- BLOCK_SIZE =
The maximum amount of body data returned by a single read.
1024 * 64
- VALID_CHUNK_LENGTH =
/\A[0-9a-fA-F]+\z/
Instance Attribute Summary collapse
-
#count ⇒ Object
readonly
Returns the value of attribute count.
- #the length of the body if known.(lengthofthebody) ⇒ Object readonly
- #the number of chunks read so far.(numberofchunksreadsofar.) ⇒ Object readonly
Instance Method Summary collapse
- #as_json ⇒ Object
-
#close(error = nil) ⇒ Object
Close the connection and mark the body as finished.
- #empty? ⇒ Boolean
-
#initialize(connection, headers) ⇒ Chunked
constructor
Initialize the chunked body.
- #inspect ⇒ Object
- #length ⇒ Object
-
#read ⇒ Object
Read a chunk of data.
Constructor Details
#initialize(connection, headers) ⇒ Chunked
Initialize the chunked body.
25 26 27 28 29 30 31 32 33 34 |
# File 'lib/protocol/http1/body/chunked.rb', line 25 def initialize(connection, headers) @connection = connection @finished = false @headers = headers @length = 0 @count = 0 @remaining = nil end |
Instance Attribute Details
#count ⇒ Object (readonly)
Returns the value of attribute count.
37 38 39 |
# File 'lib/protocol/http1/body/chunked.rb', line 37 def count @count end |
#the length of the body if known.(lengthofthebody) ⇒ Object (readonly)
40 41 42 43 44 45 |
# File 'lib/protocol/http1/body/chunked.rb', line 40 def length # We only know the length once we've read the final chunk: if @finished @length end end |
#the number of chunks read so far.(numberofchunksreadsofar.) ⇒ Object (readonly)
37 |
# File 'lib/protocol/http1/body/chunked.rb', line 37 attr :count |
Instance Method Details
#as_json ⇒ Object
140 141 142 143 144 145 146 |
# File 'lib/protocol/http1/body/chunked.rb', line 140 def as_json(...) super.merge( count: @count, finished: @finished, state: @connection ? "open" : "closed" ) end |
#close(error = nil) ⇒ Object
Close the connection and mark the body as finished.
55 56 57 58 59 60 61 62 63 64 65 |
# File 'lib/protocol/http1/body/chunked.rb', line 55 def close(error = nil) if connection = @connection @connection = nil unless @finished connection.close_read end end super end |
#empty? ⇒ Boolean
48 49 50 |
# File 'lib/protocol/http1/body/chunked.rb', line 48 def empty? @connection.nil? end |
#inspect ⇒ Object
135 136 137 |
# File 'lib/protocol/http1/body/chunked.rb', line 135 def inspect "\#<#{self.class} #{@length} bytes read in #{@count} chunks, #{@finished ? 'finished' : 'reading'}>" end |
#length ⇒ Object
40 41 42 43 44 45 |
# File 'lib/protocol/http1/body/chunked.rb', line 40 def length # We only know the length once we've read the final chunk: if @finished @length end end |
#read ⇒ Object
Read a chunk of data.
Follows the procedure outlined in https://tools.ietf.org/html/rfc7230#section-4.1.3
75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 |
# File 'lib/protocol/http1/body/chunked.rb', line 75 def read while !@finished unless @connection raise EOFError, "Connection closed before expected length was read!" end if @remaining if @remaining > 0 chunk = @connection.readpartial([@remaining, BLOCK_SIZE].min) @remaining -= chunk.bytesize @length += chunk.bytesize return chunk end terminator = @connection.read(CRLF.bytesize) unless terminator&.bytesize == CRLF.bytesize raise EOFError, "Connection closed before expected length was read!" end unless terminator == CRLF raise BadRequest, "Invalid chunk terminator: #{terminator.inspect}" end @remaining = nil @count += 1 else length, _extensions = @connection.read_line.split(";", 2) unless length =~ VALID_CHUNK_LENGTH raise BadRequest, "Invalid chunk length: #{length.inspect}" end length = Integer(length, 16) if length == 0 read_trailer # The final chunk has been read and the connection is now closed: @connection.receive_end_stream! @connection = nil @finished = true return nil end @remaining = length end end rescue EOFError if connection = @connection @connection = nil connection.close_read end raise EOFError, "Connection closed before expected length was read!" end |