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.



179
180
181
182
183
184
185
186
187
# File 'lib/protocol/http/body/readable.rb', line 179

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



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

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.



144
145
146
147
148
149
150
151
152
153
154
155
156
# File 'lib/protocol/http/body/readable.rb', line 144

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.

Closing the stream before it reaches end-of-file abandons any remaining input. The protocol implementation must account for that unread data before the associated exchange or connection can be reused. Depending on the protocol, it may discard the remaining data, terminate the exchange, or make the connection non-reusable. Use #discard when preserving the exchange or connection is preferred.

When closing before end-of-file, omitting the error represents deliberate application-level abandonment, not a protocol failure.

If an error occurred 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.



34
35
# File 'lib/protocol/http/body/readable.rb', line 34

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.



171
172
173
174
# File 'lib/protocol/http/body/readable.rb', line 171

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.



98
99
100
101
102
103
104
105
106
107
108
109
110
# File 'lib/protocol/http/body/readable.rb', line 98

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.



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

def empty?
  false
end

#finishObject

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



161
162
163
164
# File 'lib/protocol/http/body/readable.rb', line 161

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.



115
116
117
118
119
120
121
122
123
124
125
126
127
# File 'lib/protocol/http/body/readable.rb', line 115

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.



80
81
82
# File 'lib/protocol/http/body/readable.rb', line 80

def length
  nil
end

#readObject

Read the next available chunk.



88
89
90
# File 'lib/protocol/http/body/readable.rb', line 88

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



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

def ready?
  false
end

#rewindObject

Rewind the stream to the beginning.



64
65
66
# File 'lib/protocol/http/body/readable.rb', line 64

def rewind
  false
end

#rewindable?Boolean

Whether the stream can be rewound using #rewind.

Returns:

  • (Boolean)

    Whether the stream is rewindable.



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

def rewindable?
  false
end

#stream?Boolean

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

Returns:

  • (Boolean)


132
133
134
# File 'lib/protocol/http/body/readable.rb', line 132

def stream?
  false
end

#to_ioObject

Return an IO-compatible stream for reading this body.



198
199
200
# File 'lib/protocol/http/body/readable.rb', line 198

def to_io
  return Stream.new(self)
end

#to_jsonObject

Convert the body to JSON.



192
193
194
# File 'lib/protocol/http/body/readable.rb', line 192

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