Class: Unicorn::TeeInput
- Inherits:
-
Struct
- Object
- Struct
- Unicorn::TeeInput
- Defined in:
- lib/unicorn/tee_input.rb
Overview
acts like tee(1) on an input input to provide a input-like stream while providing rewindable semantics through a File/StringIO backing store. On the first pass, the input is only read on demand so your Rack application can use input notification (upload progress and like). This should fully conform to the Rack::InputWrapper specification on the public API. This class is intended to be a strict interpretation of Rack::InputWrapper functionality and will not support any deviations from it.
Instance Attribute Summary collapse
-
#buf ⇒ Object
Returns the value of attribute buf.
-
#parser ⇒ Object
Returns the value of attribute parser.
-
#req ⇒ Object
Returns the value of attribute req.
-
#socket ⇒ Object
Returns the value of attribute socket.
Instance Method Summary collapse
- #each(&block) ⇒ Object
-
#gets ⇒ Object
takes zero arguments for strict Rack::Lint compatibility, unlike IO#gets.
-
#initialize(*args) ⇒ TeeInput
constructor
A new instance of TeeInput.
-
#read(*args) ⇒ Object
call-seq: ios = env ios.read([length [, buffer ]]) => string, buffer, or nil.
- #rewind ⇒ Object
-
#size ⇒ Object
returns the size of the input.
Constructor Details
#initialize(*args) ⇒ TeeInput
Returns a new instance of TeeInput.
15 16 17 18 19 20 21 22 23 24 25 |
# File 'lib/unicorn/tee_input.rb', line 15 def initialize(*args) super(*args) @size = parser.content_length @tmp = @size && @size < Const::MAX_BODY ? StringIO.new("") : Util.tmpio @buf2 = buf.dup if buf.size > 0 parser.filter_body(@buf2, buf) and finalize_input @tmp.write(@buf2) @tmp.seek(0) end end |
Instance Attribute Details
#buf ⇒ Object
Returns the value of attribute buf
13 14 15 |
# File 'lib/unicorn/tee_input.rb', line 13 def buf @buf end |
#parser ⇒ Object
Returns the value of attribute parser
13 14 15 |
# File 'lib/unicorn/tee_input.rb', line 13 def parser @parser end |
#req ⇒ Object
Returns the value of attribute req
13 14 15 |
# File 'lib/unicorn/tee_input.rb', line 13 def req @req end |
#socket ⇒ Object
Returns the value of attribute socket
13 14 15 |
# File 'lib/unicorn/tee_input.rb', line 13 def socket @socket end |
Instance Method Details
#each(&block) ⇒ Object
112 113 114 115 116 117 118 |
# File 'lib/unicorn/tee_input.rb', line 112 def each(&block) while line = gets yield line end self # Rack does not specify what the return value is here end |
#gets ⇒ Object
takes zero arguments for strict Rack::Lint compatibility, unlike IO#gets
86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 |
# File 'lib/unicorn/tee_input.rb', line 86 def gets socket or return @tmp.gets nil == $/ and return read orig_size = tmp_size if @tmp.pos == orig_size tee(Const::CHUNK_SIZE, @buf2) or return nil @tmp.seek(orig_size) end line = @tmp.gets # cannot be nil here since size > pos $/ == line[-$/.size, $/.size] and return line # unlikely, if we got here, then @tmp is at EOF begin orig_size = @tmp.pos tee(Const::CHUNK_SIZE, @buf2) or break @tmp.seek(orig_size) line << @tmp.gets $/ == line[-$/.size, $/.size] and return line # @tmp is at EOF again here, retry the loop end while true line end |
#read(*args) ⇒ Object
call-seq:
ios = env['rack.input']
ios.read([length [, buffer ]]) => string, buffer, or nil
Reads at most length bytes from the I/O stream, or to the end of file if length is omitted or is nil. length must be a non-negative integer or nil. If the optional buffer argument is present, it must reference a String, which will receive the data.
At end of file, it returns nil or “” depend on length. ios.read() and ios.read(nil) returns “”. ios.read(length [, buffer]) returns nil.
If the Content-Length of the HTTP request is known (as is the common case for POST requests), then ios.read(length [, buffer]) will block until the specified length is read (or it is the last chunk). Otherwise, for uncommon “Transfer-Encoding: chunked” requests, ios.read(length [, buffer]) will return immediately if there is any data and only block when nothing is available (providing IO#readpartial semantics).
64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 |
# File 'lib/unicorn/tee_input.rb', line 64 def read(*args) socket or return @tmp.read(*args) length = args.shift if nil == length rv = @tmp.read || "" while tee(Const::CHUNK_SIZE, @buf2) rv << @buf2 end rv else rv = args.shift || @buf2.dup diff = tmp_size - @tmp.pos if 0 == diff ensure_length(tee(length, rv), length) else ensure_length(@tmp.read(diff > length ? length : diff, rv), length) end end end |
#rewind ⇒ Object
120 121 122 |
# File 'lib/unicorn/tee_input.rb', line 120 def rewind @tmp.rewind # Rack does not specify what the return value is here end |
#size ⇒ Object
returns the size of the input. This is what the Content-Length header value should be, and how large our input is expected to be. For TE:chunked, this requires consuming all of the input stream before returning since there’s no other way
31 32 33 34 35 36 37 38 39 40 41 42 |
# File 'lib/unicorn/tee_input.rb', line 31 def size @size and return @size if socket pos = @tmp.pos while tee(Const::CHUNK_SIZE, @buf2) end @tmp.seek(pos) end @size = tmp_size end |