Class: Yahns::StreamInput

Inherits:
Object
  • Object
show all
Defined in:
lib/yahns/stream_input.rb

Overview

When processing uploads, Yahns may expose a StreamInput object under “rack.input” of the (future) Rack (2.x) environment.

Direct Known Subclasses

TeeInput

Instance Method Summary collapse

Constructor Details

#initialize(client, request) ⇒ StreamInput

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



10
11
12
13
14
15
16
17
18
# File 'lib/yahns/stream_input.rb', line 10

def initialize(client, request)
  @chunked = request.content_length.nil?
  @client = client
  @parser = request
  @buf = request.buf
  @rbuf = ''
  @bytes_read = 0
  filter_body(@rbuf, @buf) unless @buf.empty?
end

Instance Method Details

#__rsizeObject



62
63
64
# File 'lib/yahns/stream_input.rb', line 62

def __rsize
  @client ? @client.class.client_body_buffer_size : nil
end

#__tlsbufObject



66
67
68
# File 'lib/yahns/stream_input.rb', line 66

def __tlsbuf
  Thread.current[:yahns_rbuf]
end

#closeObject

return nil



153
154
# File 'lib/yahns/stream_input.rb', line 153

def close # return nil
end

#eachObject

:call-seq:

ios.each { |line| block }  => ios

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



102
103
104
105
106
107
108
# File 'lib/yahns/stream_input.rb', line 102

def each
  while line = gets
    yield line
  end

  self # Rack does not specify what the return value is here
end

#eof!Object



143
144
145
146
147
148
149
150
151
# File 'lib/yahns/stream_input.rb', line 143

def eof!
  # in case client only did a premature shutdown(SHUT_WR)
  # we do support clients that shutdown(SHUT_WR) after the
  # _entire_ request has been sent, and those will not have
  # raised EOFError on us.
  @client.shutdown if @client
ensure
  raise Yahns::ClientShutdown, "bytes_read=#{@bytes_read}", []
end

#eof?Boolean

Returns:

  • (Boolean)


110
111
112
113
114
115
116
117
118
119
120
121
122
123
# File 'lib/yahns/stream_input.rb', line 110

def eof?
  if @parser.body_eof?
    rsize = __rsize
    tlsbuf = __tlsbuf
    while @chunked && ! @parser.parse
      @client.yahns_read(rsize, tlsbuf) or eof!
      @buf << tlsbuf
    end
    @client = nil
    true
  else
    false
  end
end

#filter_body(dst, src) ⇒ Object



125
126
127
128
129
# File 'lib/yahns/stream_input.rb', line 125

def filter_body(dst, src)
  rv = @parser.filter_body(dst, src)
  @bytes_read += dst.size
  rv
end

#getsObject

:call-seq:

ios.gets   => string or nil

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.



79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
# File 'lib/yahns/stream_input.rb', line 79

def gets
  sep = $/
  if sep.nil?
    read_all(rv = '')
    return rv.empty? ? nil : rv
  end
  re = /\A(.*?#{Regexp.escape(sep)})/
  rsize = __rsize or return
  tlsbuf = __tlsbuf
  begin
    @rbuf.sub!(re, '') and return $1
    return @rbuf.empty? ? nil : @rbuf.slice!(0, @rbuf.size) if eof?
    @client.yahns_read(rsize, @buf) or eof!
    filter_body(tlsbuf, @buf)
    @rbuf << tlsbuf
  end while true
end

#read(length = nil, rv = '') ⇒ Object

:call-seq:

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



39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
# File 'lib/yahns/stream_input.rb', line 39

def read(length = nil, rv = '')
  if length
    if length <= @rbuf.size
      length < 0 and raise ArgumentError, "negative length #{length} given"
      rv.replace(@rbuf.slice!(0, length))
    else
      to_read = length - @rbuf.size
      rv.replace(@rbuf.slice!(0, @rbuf.size))
      until to_read == 0 || eof? || (rv.size > 0 && @chunked)
        @client.yahns_read(to_read, @buf) or eof!
        filter_body(@rbuf, @buf)
        rv << @rbuf
        to_read -= @rbuf.size
      end
      @rbuf.replace('')
    end
    rv = nil if rv.empty? && length != 0
  else
    read_all(rv)
  end
  rv
end

#read_all(dst) ⇒ Object



131
132
133
134
135
136
137
138
139
140
141
# File 'lib/yahns/stream_input.rb', line 131

def read_all(dst)
  dst.replace(@rbuf)
  rsize = __rsize or return
  until eof?
    @client.yahns_read(rsize, @buf) or eof!
    filter_body(@rbuf, @buf)
    dst << @rbuf
  end
ensure
  @rbuf.replace('')
end