123456789_123456789_123456789_123456789_123456789_

Class: Unicorn::TeeInput

Relationships & Source Files
Super Chains via Extension / Inclusion / Inheritance
Class Chain:
self, StreamInput
Instance Chain:
self, StreamInput
Inherits: Unicorn::StreamInput
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::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 by default.

Class Attribute Summary

  • .client_body_buffer_size rw Internal use only

    returns the maximum size of request bodies to buffer in memory, amounts larger than this are buffered to the filesystem.

  • .client_body_buffer_size=(bytes) rw Internal use only

    sets the maximum size of request bodies to buffer in memory, amounts larger than this are buffered to the filesystem.

Class Method Summary

StreamInput - Inherited

.new

Initializes a new StreamInput object.

Instance Attribute Summary

StreamInput - Inherited

Instance Method Summary

StreamInput - Inherited

#each

Executes the block for every “line” in ios, where lines are separated by the global record separator ($/, typically “n”).

#gets

Reads the next “line” from the I/O stream; lines are separated by the global record separator ($/, typically “n”).

#read

Reads at most length bytes from the I/O stream, or to the end of file if length is omitted or is nil.

#eof!, #filter_body, #read_all

Constructor Details

.new(socket, request) ⇒ TeeInput

This method is for internal use only.

Initializes a new TeeInput object. You normally do not have to call this unless you are writing an HTTP server.

[ GitHub ]

  
# File 'lib/unicorn/tee_input.rb', line 41

def initialize(socket, request) # :nodoc:
  @len = request.content_length
  super
  @tmp = @len && @len <= @@client_body_buffer_size ?
         StringIO.new("") : new_tmpio
end

Class Attribute Details

.client_body_buffer_size (rw)

This method is for internal use only.

returns the maximum size of request bodies to buffer in memory, amounts larger than this are buffered to the filesystem

[ GitHub ]

  
# File 'lib/unicorn/tee_input.rb', line 28

def self.client_body_buffer_size # :nodoc:
  @@client_body_buffer_size
end

.client_body_buffer_size=(bytes) (rw)

This method is for internal use only.

sets the maximum size of request bodies to buffer in memory, amounts larger than this are buffered to the filesystem

[ GitHub ]

  
# File 'lib/unicorn/tee_input.rb', line 22

def self.client_body_buffer_size=(bytes) # :nodoc:
  @@client_body_buffer_size = bytes
end

Instance Method Details

#consume! (private)

consumes the stream of the socket

[ GitHub ]

  
# File 'lib/unicorn/tee_input.rb', line 123

def consume!
  junk = ""
  nil while read(@@io_chunk_size, junk)
end

#getsString?

Reads the next “line” from the I/O stream; lines are separated by the global record separator ($/, typically “n”). A global record separator of nil reads the entire unread contents of ios. Returns nil if called at the end of file. This takes zero arguments for strict Rack::Lint compatibility, unlike IO#gets.

[ GitHub ]

  
# File 'lib/unicorn/tee_input.rb', line 104

def gets
  @socket ? tee(super) : @tmp.gets
end

#new_tmpio

This method is for internal use only.

for Rack::TempfileReaper in rack 1.6+

[ GitHub ]

  
# File 'lib/unicorn/tee_input.rb', line 33

def new_tmpio # :nodoc:
  tmpio = Unicorn::TmpIO.new
  (@parser.env['rack.tempfiles'] ||= []) << tmpio
  tmpio
end

#read([length [, buffer ]]) ⇒ String, ...

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

[ GitHub ]

  
# File 'lib/unicorn/tee_input.rb', line 91

def read(*args)
  @socket ? tee(super) : @tmp.read(*args)
end

#rewind0

Positions the ios pointer to the beginning of input, returns the offset (zero) of the ios pointer. Subsequent reads will start from the beginning of the previously-buffered input.

[ GitHub ]

  
# File 'lib/unicorn/tee_input.rb', line 114

def rewind
  return 0 if 0 == @tmp.size
  consume! if @socket
  @tmp.rewind # Rack does not specify what the return value is here
end

#sizeInteger

Returns the size of the input. For requests with a Content-Length header value, this will not read data off the socket and just return the value of the Content-Length header as an Integer.

For Transfer-Encoding:chunked requests, this requires consuming all of the input stream before returning since there’s no other way to determine the size of the request body beforehand.

This method is no longer part of the Rack specification as of Rack 1.2, so its use is not recommended. This method only exists for compatibility with Rack applications designed for Rack 1.1 and earlier. Most applications should only need to call #read with a specified length in a loop until it returns nil.

[ GitHub ]

  
# File 'lib/unicorn/tee_input.rb', line 64

def size
  @len and return @len
  pos = @tmp.pos
  consume!
  @tmp.pos = pos
  @len = @tmp.size
end

#tee(buffer) (private)

[ GitHub ]

  
# File 'lib/unicorn/tee_input.rb', line 128

def tee(buffer)
  @tmp.write(buffer) if buffer
  buffer
end