Class: PumaPlus::WorkerThread::ChunkWriter
- Inherits:
-
Object
- Object
- PumaPlus::WorkerThread::ChunkWriter
- Defined in:
- lib/puma_plus/worker_thread.rb
Overview
The stream object handed to a Rack 3 streaming body.
The Rack SPEC requires read, write, <<, flush, close, close_read, close_write and closed?, and says their semantics "must be a best effort match to those of a normal Ruby IO or Socket object, using standard arguments and raising standard exceptions" (rack SPEC.rdoc:252). So this behaves like a Ruby IO opened for writing only -- including raising the same errors that one does, rather than inventing gentler ones.
Rack::Lint enforces only that the methods exist, which is how an earlier version passed every other check while omitting three of them and failing the moment a streaming body was actually used.
Instance Method Summary collapse
-
#<<(chunk) ⇒ Object
IO#<< returns self so it can be chained, where #write returns a count.
-
#close ⇒ Object
(also: #close_write)
Closing the stream ends the body but must NOT close the connection: the connection outlives the request and goes back to the idle pool.
- #close_read ⇒ Object
- #closed? ⇒ Boolean
-
#flush ⇒ Object
Nothing is buffered on this side: every write is already a frame on the wire, which is the point (puma-dev batches SSE into one-second hiccups with a 1s FlushInterval).
-
#initialize(conn) ⇒ ChunkWriter
constructor
A new instance of ChunkWriter.
-
#read ⇒ Object
Write-only, so reading raises exactly what a Ruby IO opened "w" raises.
-
#write(*chunks) ⇒ Object
IO#write takes any number of arguments and returns the total bytes.
Constructor Details
#initialize(conn) ⇒ ChunkWriter
Returns a new instance of ChunkWriter.
227 228 229 230 |
# File 'lib/puma_plus/worker_thread.rb', line 227 def initialize(conn) @conn = conn @closed = false end |
Instance Method Details
#<<(chunk) ⇒ Object
IO#<< returns self so it can be chained, where #write returns a count.
Aliasing the two would break stream << a << b.
249 250 251 252 |
# File 'lib/puma_plus/worker_thread.rb', line 249 def <<(chunk) write(chunk) self end |
#close ⇒ Object Also known as: close_write
Closing the stream ends the body but must NOT close the connection: the connection outlives the request and goes back to the idle pool. RESP_END is what terminates the body, and write_response sends it.
274 275 276 277 |
# File 'lib/puma_plus/worker_thread.rb', line 274 def close @closed = true nil end |
#close_read ⇒ Object
267 268 269 |
# File 'lib/puma_plus/worker_thread.rb', line 267 def close_read raise IOError, "closing non-duplex IO for reading" end |
#closed? ⇒ Boolean
280 |
# File 'lib/puma_plus/worker_thread.rb', line 280 def closed? = @closed |
#flush ⇒ Object
Nothing is buffered on this side: every write is already a frame on the wire, which is the point (puma-dev batches SSE into one-second hiccups with a 1s FlushInterval). IO#flush returns self.
257 |
# File 'lib/puma_plus/worker_thread.rb', line 257 def flush = self |
#read ⇒ Object
Write-only, so reading raises exactly what a Ruby IO opened "w" raises. Full-duplex streaming would mean interleaving BODY_CHUNK reads with RESP_CHUNK writes on one connection, which the protocol's drain invariant (consume the body to BODY_END before RESP_END) forbids.
263 264 265 |
# File 'lib/puma_plus/worker_thread.rb', line 263 def read(*) raise IOError, "not opened for reading" end |
#write(*chunks) ⇒ Object
IO#write takes any number of arguments and returns the total bytes.
233 234 235 236 237 238 239 240 241 242 243 244 245 |
# File 'lib/puma_plus/worker_thread.rb', line 233 def write(*chunks) raise IOError, "closed stream" if @closed total = 0 chunks.each do |chunk| data = chunk.to_s next if data.empty? @conn.write Wire.frame(Wire::RESP_CHUNK, data) total += data.bytesize end total end |