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.



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

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



69
70
71
# File 'lib/protocol/http/body/readable.rb', line 69

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.



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

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.



30
31
# File 'lib/protocol/http/body/readable.rb', line 30

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.



167
168
169
170
# File 'lib/protocol/http/body/readable.rb', line 167

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.



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

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.



39
40
41
# File 'lib/protocol/http/body/readable.rb', line 39

def empty?
	false
end

#finishObject

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



157
158
159
160
# File 'lib/protocol/http/body/readable.rb', line 157

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.



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

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.



76
77
78
# File 'lib/protocol/http/body/readable.rb', line 76

def length
	nil
end

#readObject

Read the next available chunk.



84
85
86
# File 'lib/protocol/http/body/readable.rb', line 84

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



46
47
48
# File 'lib/protocol/http/body/readable.rb', line 46

def ready?
	false
end

#rewindObject

Rewind the stream to the beginning.



60
61
62
# File 'lib/protocol/http/body/readable.rb', line 60

def rewind
	false
end

#rewindable?Boolean

Whether the stream can be rewound using #rewind.

Returns:

  • (Boolean)

    Whether the stream is rewindable.



53
54
55
# File 'lib/protocol/http/body/readable.rb', line 53

def rewindable?
	false
end

#stream?Boolean

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

Returns:

  • (Boolean)


128
129
130
# File 'lib/protocol/http/body/readable.rb', line 128

def stream?
	false
end

#to_ioObject

Return an IO-compatible stream for reading this body.



194
195
196
# File 'lib/protocol/http/body/readable.rb', line 194

def to_io
	return Stream.new(self)
end

#to_jsonObject

Convert the body to JSON.



188
189
190
# File 'lib/protocol/http/body/readable.rb', line 188

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