1 # -*- encoding: binary -*-
3 # When processing uploads, unicorn may expose a StreamInput object under
4 # "rack.input" of the Rack environment when
5 # Unicorn::Configurator#rewindable_input is set to +false+
6 class Unicorn::StreamInput
7 # The I/O chunk size (in +bytes+) for I/O operations where
8 # the size cannot be user-specified when a method is called.
9 # The default is 16 kilobytes.
10 @@io_chunk_size = Unicorn::Const::CHUNK_SIZE # :nodoc:
12 # Initializes a new StreamInput object. You normally do not have to call
13 # this unless you are writing an HTTP server.
14 def initialize(socket, request) # :nodoc:
15 @chunked = request.content_length.nil?
21 filter_body(@rbuf, @buf) unless @buf.empty?
25 # ios.read([length [, buffer ]]) => string, buffer, or nil
27 # Reads at most length bytes from the I/O stream, or to the end of
28 # file if length is omitted or is nil. length must be a non-negative
29 # integer or nil. If the optional buffer argument is present, it
30 # must reference a String, which will receive the data.
32 # At end of file, it returns nil or '' depend on length.
33 # ios.read() and ios.read(nil) returns ''.
34 # ios.read(length [, buffer]) returns nil.
36 # If the Content-Length of the HTTP request is known (as is the common
37 # case for POST requests), then ios.read(length [, buffer]) will block
38 # until the specified length is read (or it is the last chunk).
39 # Otherwise, for uncommon "Transfer-Encoding: chunked" requests,
40 # ios.read(length [, buffer]) will return immediately if there is
41 # any data and only block when nothing is available (providing
42 # IO#readpartial semantics).
43 def read(length = nil, rv = '')
45 if length <= @rbuf.size
46 length < 0 and raise ArgumentError, "negative length #{length} given"
47 rv.replace(@rbuf.slice!(0, length))
49 to_read = length - @rbuf.size
50 rv.replace(@rbuf.slice!(0, @rbuf.size))
51 until to_read == 0 || eof? || (rv.size > 0 && @chunked)
52 @socket.kgio_read(to_read, @buf) or eof!
53 filter_body(@rbuf, @buf)
59 rv = nil if rv.empty? && length != 0
67 # ios.gets => string or nil
69 # Reads the next ``line'' from the I/O stream; lines are separated
70 # by the global record separator ($/, typically "\n"). A global
71 # record separator of nil reads the entire unread contents of ios.
72 # Returns nil if called at the end of file.
73 # This takes zero arguments for strict Rack::Lint compatibility,
79 return rv.empty? ? nil : rv
81 re = /\A(.*?#{Regexp.escape(sep)})/
84 @rbuf.sub!(re, '') and return $1
85 return @rbuf.empty? ? nil : @rbuf.slice!(0, @rbuf.size) if eof?
86 @socket.kgio_read(@@io_chunk_size, @buf) or eof!
87 filter_body(once = '', @buf)
93 # ios.each { |line| block } => ios
95 # Executes the block for every ``line'' in *ios*, where lines are
96 # separated by the global record separator ($/, typically "\n").
102 self # Rack does not specify what the return value is here
109 while @chunked && ! @parser.parse
110 once = @socket.kgio_read(@@io_chunk_size) or eof!
120 def filter_body(dst, src)
121 rv = @parser.filter_body(dst, src)
122 @bytes_read += dst.size
130 @socket.kgio_read(@@io_chunk_size, @buf) or eof!
131 filter_body(@rbuf, @buf)
139 # in case client only did a premature shutdown(SHUT_WR)
140 # we do support clients that shutdown(SHUT_WR) after the
141 # _entire_ request has been sent, and those will not have
142 # raised EOFError on us.
143 @socket.shutdown if @socket
145 raise Unicorn::ClientShutdown, "bytes_read=#{@bytes_read}", []