class PhusionPassenger::Utils::TeeInput
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::Lint::InputWrapper specification on the public API. This class is intended to be a strict interpretation of Rack::Lint::InputWrapper functionality and will not support any deviations from it.
When processing uploads, Unicorn exposes a TeeInput object under “rack.input” of the Rack environment.
Constants
- CHUNKED
- CONTENT_LENGTH
- HTTP_TRANSFER_ENCODING
Public Class Methods
returns the maximum size of request bodies to buffer in memory, amounts larger than this are buffered to the filesystem
# File lib/phusion_passenger/utils/tee_input.rb, line 89 def self.client_body_buffer_size @@client_body_buffer_size end
sets the maximum size of request bodies to buffer in memory, amounts larger than this are buffered to the filesystem
# File lib/phusion_passenger/utils/tee_input.rb, line 83 def self.client_body_buffer_size=(bytes) @@client_body_buffer_size = bytes end
Initializes a new TeeInput object. You normally do not have to call this unless you are writing an HTTP server.
# File lib/phusion_passenger/utils/tee_input.rb, line 95 def initialize(socket, env) if @len = env[CONTENT_LENGTH] @len = @len.to_i elsif env[HTTP_TRANSFER_ENCODING] != CHUNKED @len = 0 end @socket = socket @bytes_read = 0 if @len && @len <= @@client_body_buffer_size @tmp = StringIO.new("") else @tmp = TmpIO.new("PassengerTeeInput") end @tmp.binmode end
Public Instance Methods
# File lib/phusion_passenger/utils/tee_input.rb, line 111 def close @tmp.close end
# File lib/phusion_passenger/utils/tee_input.rb, line 175 def each while line = gets yield line end self # Rack does not specify what the return value is here end
# File lib/phusion_passenger/utils/tee_input.rb, line 150 def gets if socket_drained? @tmp.gets else if @bytes_read == @len nil elsif line = @socket.gets if @len max_len = @len - @bytes_read line.slice!(max_len, line.size - max_len) end @bytes_read += line.size tee(line) else nil end end end
# File lib/phusion_passenger/utils/tee_input.rb, line 126 def read(len = nil, buf = "") buf ||= "" if len if len < 0 raise ArgumentError, "negative length #{len} given" elsif len == 0 buf.replace('') buf else if socket_drained? @tmp.read(len, buf) else tee(read_exact(len, buf)) end end else if socket_drained? @tmp.read(nil, buf) else tee(read_all(buf)) end end end
# File lib/phusion_passenger/utils/tee_input.rb, line 169 def rewind return 0 if 0 == @tmp.size consume! if !socket_drained? @tmp.rewind # Rack does not specify what the return value is here end
# File lib/phusion_passenger/utils/tee_input.rb, line 115 def size if @len @len else pos = @tmp.pos consume! @tmp.pos = pos @len = @tmp.size end end
Private Instance Methods
consumes the stream of the socket
# File lib/phusion_passenger/utils/tee_input.rb, line 199 def consume! junk = "" nil while read(16 * 1024, junk) @socket = nil end
# File lib/phusion_passenger/utils/tee_input.rb, line 223 def read_all(buf) if @len ret = @socket.read(@len - @bytes_read, buf) if ret @bytes_read += ret.size ret else buf.replace("") buf end else ret = @socket.read(nil, buf) @bytes_read += ret.size ret end end
# File lib/phusion_passenger/utils/tee_input.rb, line 212 def read_exact(len, buf) if @len max_len = @len - @bytes_read len = max_len if len > max_len return nil if len == 0 end ret = @socket.read(len, buf) @bytes_read += ret.size if ret ret end
# File lib/phusion_passenger/utils/tee_input.rb, line 185 def socket_drained? if @socket if @socket.eof? @socket = nil true else false end else true end end
# File lib/phusion_passenger/utils/tee_input.rb, line 205 def tee(buffer) if buffer && buffer.size > 0 @tmp.write(buffer) end buffer end