Class: Falcon::Adapters::Output

Inherits:
Protocol::HTTP::Body::Readable
  • Object
show all
Defined in:
lib/falcon/adapters/output.rb

Overview

Wraps the rack response body.

The `rack` body must respond to `each` and must only yield `String` values. If the body responds to `close`, it will be called after iteration. If the body is replaced by a middleware after action, the original body must be closed first, if it responds to `close`. If the body responds to `to_path`, it must return a String identifying the location of a file whose contents are identical to that produced by calling `each`; this may be used by the server as an alternative, possibly more efficient way to transport the response. The body commonly is an `Array` of strings, the application instance itself, or a `File`-like object.

Constant Summary collapse

CONTENT_LENGTH =
'content-length'.freeze

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(body, length) ⇒ Output

Initialize the output wrapper.



79
80
81
82
83
84
# File 'lib/falcon/adapters/output.rb', line 79

def initialize(body, length)
	@length = length
	@body = body
	
	@chunks = nil
end

Instance Attribute Details

#bodyObject (readonly)

The rack response body.



87
88
89
# File 'lib/falcon/adapters/output.rb', line 87

def body
  @body
end

#lengthObject (readonly)

The content length of the rack response body.



90
91
92
# File 'lib/falcon/adapters/output.rb', line 90

def length
  @length
end

Class Method Details

.wrap(status, headers, body, request = nil) ⇒ Object

Wraps an array into a buffered body.



38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
# File 'lib/falcon/adapters/output.rb', line 38

def self.wrap(status, headers, body, request = nil)
	# In no circumstance do we want this header propagating out:
	if length = headers.delete(CONTENT_LENGTH)
		# We don't really trust the user to provide the right length to the transport.
		length = Integer(length)
	end
	
	# If we have an Async::HTTP body, we return it directly:
	if body.is_a?(::Protocol::HTTP::Body::Readable)
		# warn "Returning #{body.class} as body is falcon-specific and may be removed in the future!"
		return body
	end
	
	# Otherwise, we have a more typical response body:
	if status == 200 and body.respond_to?(:to_path)
		begin
			# Don't mangle partial responses (206)
			return ::Protocol::HTTP::Body::File.open(body.to_path).tap do
				body.close if body.respond_to?(:close) # Close the original body.
			end
		rescue Errno::ENOENT
			# If the file is not available, ignore.
		end
	end
	
	# If we have a streaming body, we hijack the connection:
	unless body.respond_to?(:each)
		return Async::HTTP::Body::Hijack.new(body, request&.body)
	end
	
	if body.is_a?(Array)
		length ||= body.sum(&:bytesize)
		return self.new(body, length)
	else
		return self.new(body, length)
	end
end

Instance Method Details

#close(error = nil) ⇒ Object

Close the response body.



103
104
105
106
107
108
109
110
111
112
# File 'lib/falcon/adapters/output.rb', line 103

def close(error = nil)
	if @body and @body.respond_to?(:close)
		@body.close
	end
	
	@body = nil
	@chunks = nil
	
	super
end

#each(&block) ⇒ Object

Enumerate the response body.



117
118
119
120
121
# File 'lib/falcon/adapters/output.rb', line 117

def each(&block)
	@body.each(&block)
ensure
	self.close($!)
end

#empty?Boolean

Whether the body is empty.

Returns:

  • (Boolean)


93
94
95
# File 'lib/falcon/adapters/output.rb', line 93

def empty?
	@length == 0 or (@body.respond_to?(:empty?) and @body.empty?)
end

#inspectObject



133
134
135
# File 'lib/falcon/adapters/output.rb', line 133

def inspect
	"\#<#{self.class} length=#{@length.inspect} body=#{@body.class}>"
end

#readObject

Read the next chunk from the response body.



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

def read
	@chunks ||= @body.to_enum(:each)
	
	return @chunks.next
rescue StopIteration
	return nil
end

#ready?Boolean

Whether the body can be read immediately.

Returns:

  • (Boolean)


98
99
100
# File 'lib/falcon/adapters/output.rb', line 98

def ready?
	body.is_a?(Array) or body.respond_to?(:to_ary)
end