Class: PumaPlus::Input::FrameStream
- Inherits:
-
Object
- Object
- PumaPlus::Input::FrameStream
- Defined in:
- lib/puma_plus/input.rb
Overview
A rack.input backed by BODY_CHUNK frames pulled from the worker connection on demand.
Deliberately NOT rewindable. Rack 3 dropped the rewind requirement, and the payoff is that an arbitrarily large or chunked upload never touches a tempfile in either process. Compare puma, which decodes chunked bodies in Ruby and spools every one of them to a Tempfile (puma/lib/puma/client.rb:600), then rewrites CONTENT_LENGTH to the decoded size.
Time spent blocked here is accumulated into #read_ns and reported in RESP_END, so the controller can subtract it from service time. Without that, a slow uploader would look exactly like an application that got slower, and the scaler would add capacity to fix a problem capacity cannot fix.
Instance Attribute Summary collapse
-
#read_ns ⇒ Object
readonly
Nanoseconds blocked reading body frames.
Instance Method Summary collapse
- #close ⇒ Object
- #closed? ⇒ Boolean
-
#drain ⇒ Object
Consume and discard anything the application did not read.
- #each ⇒ Object
- #eof? ⇒ Boolean
- #gets(sep = $INPUT_RECORD_SEPARATOR || "\n") ⇒ Object
-
#initialize(conn, prefix = nil) ⇒ FrameStream
constructor
Sentinel returned by #read at EOF when a length was requested.
- #read(length = nil, outbuf = nil) ⇒ Object
-
#rewind ⇒ Object
rewind is part of the Rack 2 SPEC but not Rack 3's.
Constructor Details
#initialize(conn, prefix = nil) ⇒ FrameStream
Sentinel returned by #read at EOF when a length was requested.
71 72 73 74 75 76 77 78 |
# File 'lib/puma_plus/input.rb', line 71 def initialize(conn, prefix = nil) @conn = conn @buf = +"" @buf << prefix if prefix && !prefix.empty? @eof = false @closed = false @read_ns = 0 end |
Instance Attribute Details
#read_ns ⇒ Object (readonly)
Nanoseconds blocked reading body frames.
81 82 83 |
# File 'lib/puma_plus/input.rb', line 81 def read_ns @read_ns end |
Instance Method Details
#close ⇒ Object
132 133 134 |
# File 'lib/puma_plus/input.rb', line 132 def close @closed = true end |
#closed? ⇒ Boolean
136 |
# File 'lib/puma_plus/input.rb', line 136 def closed? = @closed |
#drain ⇒ Object
Consume and discard anything the application did not read.
Required before RESP_END: Go writes body frames concurrently with the application running, so if the app returns early (a 413, an auth rejection, a HEAD-like handler) the unread frames are still in flight. Leaving them would desynchronise the connection and the next request on it would read a body chunk where it expected a REQUEST.
152 153 154 155 156 |
# File 'lib/puma_plus/input.rb', line 152 def drain pull until @eof @buf = +"" nil end |
#each ⇒ Object
122 123 124 125 126 |
# File 'lib/puma_plus/input.rb', line 122 def each while (line = gets) yield line end end |
#eof? ⇒ Boolean
128 129 130 |
# File 'lib/puma_plus/input.rb', line 128 def eof? @eof && @buf.empty? end |
#gets(sep = $INPUT_RECORD_SEPARATOR || "\n") ⇒ Object
103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 |
# File 'lib/puma_plus/input.rb', line 103 def gets(sep = $INPUT_RECORD_SEPARATOR || "\n") loop do if (i = @buf.index(sep)) line = @buf.byteslice(0, i + sep.bytesize) @buf = @buf.byteslice(i + sep.bytesize, @buf.bytesize - i - sep.bytesize) || +"" return line end break if @eof pull or break end return nil if @buf.empty? line = @buf @buf = +"" line end |
#read(length = nil, outbuf = nil) ⇒ Object
83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 |
# File 'lib/puma_plus/input.rb', line 83 def read(length = nil, outbuf = nil) if length.nil? fill_all data = @buf @buf = +"" outbuf ? outbuf.replace(data) : data else return outbuf ? outbuf.clear : +"" if length.zero? fill_until(length) if @buf.empty? outbuf&.clear return nil # EOF, per IO#read semantics end data = @buf.byteslice(0, length) @buf = @buf.byteslice(length, @buf.bytesize - length) || +"" outbuf ? outbuf.replace(data) : data end end |
#rewind ⇒ Object
rewind is part of the Rack 2 SPEC but not Rack 3's. Raise rather than silently returning 0, so an app that depends on it fails loudly instead of quietly reading an empty body.
141 142 143 |
# File 'lib/puma_plus/input.rb', line 141 def rewind raise Errno::ESPIPE, "puma-plus streams large request bodies; rack.input is not rewindable" end |