class Stream
- Includes
Reader: #read, #read_partial, #readpartial, #each, #read_nonblock, #read_until, #gets
The input stream is an IO-like object which contains the raw HTTP POST data. When applicable, its external encoding must be "ASCII-8BIT" and it must be opened in binary mode, for Ruby 1.9 compatibility. The input stream must respond to gets, each, read and rewind.
Nested Classes and Modules
module ReaderThis provides a read-only interface for data, which is surprisingly tricky to implement correctly.
Definitions
NEWLINE = "\n"
The default line separator, used by gets.
def initialize(input = nil, output = nil)
Initialize the stream with the given input and output.
Signature
-
parameter
inputReadable | Nil The input stream.
-
parameter
outputWritable | Nil The output stream.
Implementation
def initialize(input = nil, output = nil)
@input = input
@output = output
if @output
raise ArgumentError, "Non-writable output!" unless output.respond_to?(:write)
end
# Will hold remaining data in `#read`.
@buffer = nil
@closed = false
@closed_read = false
end
attr :input
Signature
-
attribute
Readable | Nil The input stream.
attr :output
Signature
-
attribute
Writable | Nil The output stream.
def write(buffer)
Write data to the underlying stream.
Signature
-
parameter
bufferString The data to write.
-
raises
IOError If the stream is not writable.
-
returns
Integer The number of bytes written.
Implementation
def write(buffer)
if @output
@output.write(buffer)
return buffer.bytesize
else
raise IOError, "Stream is not writable, output has been closed!"
end
end
def write_nonblock(buffer, exception: nil)
Write data to the stream using Protocol::HTTP::Body::Stream#write.
Provided for compatibility with IO-like objects.
Signature
-
parameter
bufferString The data to write.
-
parameter
exceptionBoolean Whether to raise an exception if the write would block, currently ignored.
-
returns
Integer The number of bytes written.
Implementation
def write_nonblock(buffer, exception: nil)
write(buffer)
end
def <<(buffer)
Write data to the stream using Protocol::HTTP::Body::Stream#write.
Implementation
def << buffer
write(buffer)
end
def puts(*arguments, separator: NEWLINE)
Write lines to the stream.
The current implementation buffers the lines and writes them in a single operation.
Signature
-
parameter
argumentsArray(String) The lines to write.
-
parameter
separatorString The line separator, defaults to
\n.
Implementation
def puts(*arguments, separator: NEWLINE)
buffer = ::String.new
arguments.each do |argument|
buffer << argument << separator
end
write(buffer)
end
def flush
Flush the output stream.
This is currently a no-op.
Implementation
def flush
end
def close_read(error = nil)
Close the application-facing input body. This does not close the output body, which may continue to be written independently.
If the input has not reached end-of-file, any remaining data is abandoned. The protocol implementation must ensure that unread data cannot interfere with subsequent exchanges. Depending on the protocol, it may discard the remaining data, terminate the current exchange, or make the connection non-reusable.
Closing without an error represents orderly application-level abandonment, not a protocol failure.
This method is idempotent. After the first call, subsequent calls have no effect.
If, while processing the data that was read from this stream, an error is encountered, it should be passed to this method.
Signature
-
parameter
errorException | Nil The error that was encountered, if any.
Implementation
def close_read(error = nil)
if input = @input
@input = nil
@closed_read = true
@buffer = nil
input.close(error)
end
end
def close_write(error = nil)
Close the application-facing output body. This does not close the input body, which may continue to be read independently.
Closing without an error indicates that no more output will be produced. Previously written data remains part of the output and should be followed by a normal end-of-stream from the protocol implementation. If an error is provided, the protocol implementation may terminate the exchange instead.
This method is idempotent. After the first call, subsequent calls have no effect.
If, while generating the data that is written to this stream, an error is encountered, it should be passed to this method.
Signature
-
parameter
errorException | Nil The error that was encountered, if any.
Implementation
def close_write(error = nil)
if output = @output
@output = nil
output.close_write(error)
end
end
def close(error = nil)
Close the input and output bodies.
Closing without an error represents orderly completion or abandonment of both application-facing directions, not cancellation. If the peer has not completed the exchange, the protocol implementation may need to terminate it without reporting an application error.
Repeated calls are safe; each underlying direction will be closed at most once.
Signature
-
parameter
errorException | Nil The error that caused this stream to be closed, if any.
Implementation
def close(error = nil)
self.close_read(error)
self.close_write(error)
return nil
ensure
@closed = true
end
def closed?
Signature
-
returns
Boolean Whether the stream has been closed.
Implementation
def closed?
@closed
end
def inspect
Inspect the stream.
Signature
-
returns
String a string representation of the stream.
Implementation
def inspect
buffer_info = @buffer ? "#{@buffer.bytesize} bytes buffered" : "no buffer"
status = []
status << "closed" if @closed
status << "read-closed" if @closed_read
status_info = status.empty? ? "open" : status.join(", ")
return "#<#{self.class} #{buffer_info}, #{status_info}>"
end
def empty?
Signature
-
returns
Boolean Whether there are any output chunks remaining.
Implementation
def empty?
if @output
return @output.empty?
else
return true
end
end
def read_next
Read the next chunk of data from the input stream.
Signature
-
returns
String The next chunk of data.
-
raises
IOError If the input stream was explicitly closed.
Implementation
def read_next
if @input
return @input.read
elsif @closed_read
raise IOError, "Stream is not readable, input has been closed!"
end
end