class Protocol::HTTP::Body::Readable

At any point, you can use {close} to close the stream and release any resources, or {discard} to read all remaining data without processing it which may allow the underlying connection to be reused (but can be slower).
In both cases, reading can 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, or the stream will be closed with an error.
2. Streaming chunks using {call}, which writes chunks to a provided output stream.
1. Reading chunks using {read} (or {each}/{join}), until the body is empty, or
There are two major modes of operation:
Represents a readable input streams.

def as_json(...)

@returns [Hash] The body as a hash.

Convert the body to a hash suitable for serialization. This won't include the contents of the body, but will include metadata such as the length, streamability, and readiness, etc.
def as_json(...)
	{
		class: self.class.name,
		length: self.length,
		stream: self.stream?,
		ready: self.ready?,
		empty: self.empty?
	}
end

def buffered

@returns [Buffered | Nil] The buffered body.

This method must return a buffered body if `#rewindable?`.

Return a buffered representation of this body.
def buffered
	nil
end

def call(stream)

@returns [Boolean] Whether the ownership of the stream was transferred.
@parameter stream [IO | Object] An `IO`-like object that responds to `#read`, `#write` and `#flush`.

Write the body to 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.

Invoke the body with the given stream.
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
	# TODO Should this invoke close_write(error) instead?
	stream.close
end

def close(error = nil)

@parameter error [Exception | Nil] The error that caused this stream to be closed, if any.

Invoking {read} after {close} will return `nil`.

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.

Close the stream immediately. After invoking this method, the stream should be considered closed, and all internal resources should be released.
def close(error = nil)
end

def discard

Useful for discarding the body when it is not needed, but preserving the underlying connection.

The default implementation simply reads all chunks until the body is empty.

Discard the body as efficiently as possible.
def discard
	while chunk = self.read
	end
end

def each

@parameter chunk [String | Nil] The chunk of data, or `nil` if the stream has finished.
@yields {|chunk| ...} The block to call with each chunk of data.

Closes the stream when finished or if an error occurs.

Enumerate all chunks until finished, then invoke {close}.
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 empty?

Returns:
  • (Boolean) - Whether the stream is empty.
def empty?
	false
end

def finish

@returns [Buffered] The buffered body.

Read all remaining chunks into a buffered body and close the underlying input.
def finish
	# Internally, this invokes `self.each` which then invokes `self.close`.
	Buffered.read(self)
end

def join

@returns [String | Nil] The binary string containing all chunks of data, or `nil` if the stream has finished (or did not contain any data).

Read all remaining chunks into a single binary string using `#each`.
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 length

@returns [Integer | Nil] The total length of the body, or `nil` if the length is unknown.

The total length of the body, if known.
def length
	nil
end

def read

@raises [StandardError] If an error occurs while reading.
@returns [String | Nil] The chunk of data, or `nil` if the stream has finished.

Read the next available chunk.
def read
	nil
end

def ready?

Returns:
  • (Boolean) - Whether the stream is ready (read will not block).
def ready?
	false
end

def rewind

@returns [Boolean] Whether the stream was successfully rewound.

Rewind the stream to the beginning.
def rewind
	false
end

def rewindable?

Returns:
  • (Boolean) - Whether the stream is rewindable.
def rewindable?
	false
end

def stream?

@returns [Boolean] Whether the body should be streamed.

Whether to prefer streaming the body using {call} rather than reading it using {read} or {each}.
def stream?
	false
end

def to_json(...)

@returns [String] The body as JSON.

Convert the body to JSON.
def to_json(...)
	as_json.to_json(...)
end