Class: Falcon::Adapters::Input

Inherits:
Object
  • Object
show all
Defined in:
lib/falcon/adapters/input.rb

Overview

Wraps a streaming input body into the interface required by ‘rack.input`.

The input stream is an ‘IO`-like object which contains the raw HTTP POST data. When applicable, its external encoding must be `ASCII-8BIT` and it must be opened in binary mode, for Ruby 1.9 compatibility. The input stream must respond to `gets`, `each`, `read` and `rewind`.

This implementation is not always rewindable, to avoid buffering the input when handling large uploads. See Rewindable for more details.

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(body) ⇒ Input

Initialize the input wrapper.



36
37
38
39
40
41
42
# File 'lib/falcon/adapters/input.rb', line 36

def initialize(body)
  @body = body
  
  # Will hold remaining data in `#read`.
  @buffer = nil
  @finished = @body.nil?
end

Instance Attribute Details

#bodyObject (readonly)

The input body.



46
47
48
# File 'lib/falcon/adapters/input.rb', line 46

def body
  @body
end

Instance Method Details

#closeObject

Close and discard the remainder of the input stream.



134
135
136
# File 'lib/falcon/adapters/input.rb', line 134

def close
  @body&.close
end

#each(&block) ⇒ Object

Enumerate chunks of the request body.



51
52
53
54
55
56
57
# File 'lib/falcon/adapters/input.rb', line 51

def each(&block)
  return to_enum unless block_given?
  
  while chunk = gets
    yield chunk
  end
end

#eof?Boolean

Has the input stream been read completely?

Returns:

  • (Boolean)


114
115
116
# File 'lib/falcon/adapters/input.rb', line 114

def eof?
  @finished and @buffer.nil?
end

#getsObject

Read the next chunk of data from the input stream.

‘gets` must be called without arguments and return a `String`, or `nil` when the input stream has no more data.



123
124
125
126
127
128
129
130
131
# File 'lib/falcon/adapters/input.rb', line 123

def gets
  if @buffer.nil?
    return read_next
  else
    buffer = @buffer
    @buffer = nil
    return buffer
  end
end

#read(length = nil, buffer = nil) ⇒ Object

Read data from the input stream.

‘read` behaves like `IO#read`. Its signature is `read(length = nil, buffer = nil)`. If given, length must be a non-negative `Integer` (>= 0) or `nil`, and buffer must be a `String` and may not be nil. If `length` is given and not `nil`, then this method reads at most `length` bytes from the input stream. If `length` is not given or `nil`, then this method reads all data. When the end is reached, this method returns `nil` if `length` is given and not `nil`, or an empty `String` if `length` is not given or is `nil`. If `buffer` is given, then the read data will be placed into the `buffer` instead of a newly created `String` object.



84
85
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/falcon/adapters/input.rb', line 84

def read(length = nil, buffer = nil)
  buffer ||= Async::IO::Buffer.new
  buffer.clear
  
  until buffer.bytesize == length
    @buffer = read_next if @buffer.nil?
    break if @buffer.nil?
    
    remaining_length = length - buffer.bytesize if length
    
    if remaining_length && remaining_length < @buffer.bytesize
      # We know that we are not going to reuse the original buffer.
      # But byteslice will generate a hidden copy. So let's freeze it first:
      @buffer.freeze
      
      buffer << @buffer.byteslice(0, remaining_length)
      @buffer = @buffer.byteslice(remaining_length, @buffer.bytesize)
    else
      buffer << @buffer
      @buffer = nil
    end
  end
  
  return nil if buffer.empty? && length && length > 0
  
  return buffer
end

#rewindObject

Rewind the input stream back to the start.

‘rewind` must be called without arguments. It rewinds the input stream back to the beginning. It must not raise Errno::ESPIPE: that is, it may not be a pipe or a socket. Therefore, handler developers must buffer the input data into some rewindable object if the underlying input stream is not rewindable.



64
65
66
67
68
69
70
71
72
73
74
75
# File 'lib/falcon/adapters/input.rb', line 64

def rewind
  if @body and @body.respond_to? :rewind
    # If the body is not rewindable, this will fail.
    @body.rewind
    @buffer = nil
    @finished = false
    
    return true
  end
  
  return false
end