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