class Readable
Represents a readable input streams.
Typically, you'd override #read
to return chunks of data.
In general, you read chunks of data from a body until it is empty and returns nil
. Upon reading nil
, the body is considered consumed and should not be read from again.
Reading can also fail, for example if the body represents a streaming upload, and the connection is lost. In this case, #read
will raise some kind of error.
If you don't want to read from a stream, and instead want to close it immediately, you can call close
on the body. If the body is already completely consumed, close
will do nothing, but if there is still data to be read, it will cause the underlying stream to be reset (and possibly closed).
Definitions
def close(error = nil)
Close the stream immediately. After invoking this method, the stream should be considered closed, and all internal resources should be released.
If an error occured while handling the output, it can be passed as an argument. This may be propagated to the client, for example the client may be informed that the stream was not fully read correctly.
Invoking #read
after #close
will return nil
.
Implementation
def close(error = nil)
end
def empty?
Optimistically determine whether read (may) return any data. If this returns true, then calling read will definitely return nil. If this returns false, then calling read may return nil.
Implementation
def empty?
false
end
def ready?
Whether calling read will return a chunk of data without blocking.
We prefer pessimistic implementation, and thus default to false
.
Implementation
def ready?
false
end
def rewindable?
Whether the stream can be rewound using Protocol::HTTP::Body::Readable#rewind
.
Implementation
def rewindable?
false
end
def rewind
Rewind the stream to the beginning.
Signature
-
returns
Boolean
Whether the stream was successfully rewound.
Implementation
def rewind
false
end
def buffered
Return a buffered representation of this body.
This method must return a buffered body if #rewindable?
.
Signature
-
returns
Buffered | Nil
The buffered body.
Implementation
def buffered
nil
end
def length
The total length of the body, if known.
Signature
-
returns
Integer | Nil
The total length of the body, or
nil
if the length is unknown.
Implementation
def length
nil
end
def read
Read the next available chunk.
Signature
-
returns
String | Nil
The chunk of data, or
nil
if the stream has finished.-
raises
StandardError
If an error occurs while reading.
Implementation
def read
nil
end
def each
Enumerate all chunks until finished, then invoke #close
.
Closes the stream when finished or if an error occurs.
Signature
-
yields
{|chunk| ...}
The block to call with each chunk of data.
-
parameter
chunk
String | Nil
The chunk of data, or
nil
if the stream has finished.
-
parameter
Implementation
def each
return to_enum unless block_given?
begin
while chunk = self.read
yield chunk
end
rescue => error
raise
ensure
self.close(error)
end
end
def join
Read all remaining chunks into a single binary string using #each
.
Signature
-
returns
String | Nil
The binary string containing all chunks of data, or
nil
if the stream has finished (or did not contain any data).
Implementation
def join
buffer = String.new.force_encoding(Encoding::BINARY)
self.each do |chunk|
buffer << chunk
end
if buffer.empty?
return nil
else
return buffer
end
end
def call(stream)
Invoke the body with the given stream.
The default implementation simply writes each chunk to the stream. If the body is not ready, it will be flushed after each chunk. Closes the stream when finished or if an error occurs.
Write the body to the given stream.
Signature
-
parameter
stream
IO | Object
An
IO
-like object that responds to#read
,#write
and#flush
.-
returns
Boolean
Whether the ownership of the stream was transferred.
Implementation
def call(stream)
self.each do |chunk|
stream.write(chunk)
# Flush the stream unless we are immediately expecting more data:
unless self.ready?
stream.flush
end
end
ensure
stream.close
end
def finish
Read all remaining chunks into a buffered body and close the underlying input.
Signature
-
returns
Buffered
The buffered body.
Implementation
def finish
# Internally, this invokes `self.each` which then invokes `self.close`.
Buffered.read(self)
end
def discard
Discard the body as efficiently as possible.
The default implementation simply reads all chunks until the body is empty.
Useful for discarding the body when it is not needed, but preserving the underlying connection.
Implementation
def discard
while chunk = self.read
end
end