Class: Protocol::HTTP::Body::Readable

Inherits:
Object
  • Object
show all
Defined in:
lib/protocol/http/body/readable.rb

Overview

Represents a readable input streams.

There are two major modes of operation:

  1. Reading chunks using #read (or #each/#join), until the body is empty, or

  2. Streaming chunks using #call, which writes chunks to a provided output stream.

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.

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).

Direct Known Subclasses

Buffered, File, Head, Streamable::Body, Wrapper, Writable

Instance Method Summary collapse

Instance Method Details

#as_jsonObject

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.



173
174
175
176
177
178
179
180
181
# File 'lib/protocol/http/body/readable.rb', line 173

def as_json(...)
	{
		class: self.class.name,
		length: self.length,
		stream: self.stream?,
		ready: self.ready?,
		empty: self.empty?
	}
end

#bufferedObject

Return a buffered representation of this body.

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



67
68
69
# File 'lib/protocol/http/body/readable.rb', line 67

def buffered
	nil
end

#call(stream) ⇒ Object

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.



138
139
140
141
142
143
144
145
146
147
148
149
150
# File 'lib/protocol/http/body/readable.rb', line 138

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

#close(error = nil) ⇒ Object

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`.



28
29
# File 'lib/protocol/http/body/readable.rb', line 28

def close(error = nil)
end

#discardObject

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.



165
166
167
168
# File 'lib/protocol/http/body/readable.rb', line 165

def discard
	while chunk = self.read
	end
end

#eachObject

Enumerate all chunks until finished, then invoke #close.

Closes the stream when finished or if an error occurs.



92
93
94
95
96
97
98
99
100
101
102
103
104
# File 'lib/protocol/http/body/readable.rb', line 92

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

#empty?Boolean

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.

Returns:

  • (Boolean)

    Whether the stream is empty.



37
38
39
# File 'lib/protocol/http/body/readable.rb', line 37

def empty?
	false
end

#finishObject

Read all remaining chunks into a buffered body and close the underlying input.



155
156
157
158
# File 'lib/protocol/http/body/readable.rb', line 155

def finish
	# Internally, this invokes `self.each` which then invokes `self.close`.
	Buffered.read(self)
end

#joinObject

Read all remaining chunks into a single binary string using ‘#each`.



109
110
111
112
113
114
115
116
117
118
119
120
121
# File 'lib/protocol/http/body/readable.rb', line 109

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

#lengthObject

The total length of the body, if known.



74
75
76
# File 'lib/protocol/http/body/readable.rb', line 74

def length
	nil
end

#readObject

Read the next available chunk.



82
83
84
# File 'lib/protocol/http/body/readable.rb', line 82

def read
	nil
end

#ready?Boolean

Whether calling read will return a chunk of data without blocking. We prefer pessimistic implementation, and thus default to ‘false`.

Returns:

  • (Boolean)

    Whether the stream is ready (read will not block).



44
45
46
# File 'lib/protocol/http/body/readable.rb', line 44

def ready?
	false
end

#rewindObject

Rewind the stream to the beginning.



58
59
60
# File 'lib/protocol/http/body/readable.rb', line 58

def rewind
	false
end

#rewindable?Boolean

Whether the stream can be rewound using #rewind.

Returns:

  • (Boolean)

    Whether the stream is rewindable.



51
52
53
# File 'lib/protocol/http/body/readable.rb', line 51

def rewindable?
	false
end

#stream?Boolean

Whether to prefer streaming the body using #call rather than reading it using #read or #each.

Returns:

  • (Boolean)


126
127
128
# File 'lib/protocol/http/body/readable.rb', line 126

def stream?
	false
end

#to_jsonObject

Convert the body to JSON.



186
187
188
# File 'lib/protocol/http/body/readable.rb', line 186

def to_json(...)
	as_json.to_json(...)
end