Class: Protocol::Rack::Response

Inherits:
HTTP::Response
  • Object
show all
Defined in:
lib/protocol/rack/response.rb

Overview

A wrapper for a `Rack` response.

A Rack response consisting of `[status, headers, body]` includes various rack-specific elements, including:

  • A `headers` callback which bypasses normal response handling.

  • Potentially invalid content length.

  • Potentially invalid body when processing a `HEAD` request.

  • Newline-separated header values.

  • Other `rack.` specific header key/value pairs.

This wrapper takes those issues into account and adapts the rack response tuple into a HTTP::Response.

Constant Summary collapse

HOP_HEADERS =

HTTP hop headers which should not be passed through the proxy.

[
	'connection',
	'keep-alive',
	'public',
	'proxy-authenticate',
	'transfer-encoding',
	'upgrade',
]

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(status, headers, body, protocol = nil) ⇒ Response

Initialize the response wrapper.



113
114
115
# File 'lib/protocol/rack/response.rb', line 113

def initialize(status, headers, body, protocol = nil)
	super(nil, status, headers, body, protocol)
end

Class Method Details

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

Wrap a rack response.



82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
# File 'lib/protocol/rack/response.rb', line 82

def self.wrap(status, headers, body, request = nil)
	headers, meta = wrap_headers(headers)

	ignored = headers.extract(HOP_HEADERS)
	unless ignored.empty?
		Console.logger.warn(self, "Ignoring protocol-level headers: #{ignored.inspect}")
	end

	body = Body.wrap(status, headers, body, request&.body)

	if request&.head?
		# I thought about doing this in Output.wrap, but decided the semantics are too tricky. Specifically, the various ways a rack response body can be wrapped, and the need to invoke #close at the right point.
		body = ::Protocol::HTTP::Body::Head.for(body)
	end
	
	protocol = meta[RACK_PROTOCOL]
	
	# https://tools.ietf.org/html/rfc7231#section-7.4.2
	# headers.add('server', "falcon/#{Falcon::VERSION}")
	
	# https://tools.ietf.org/html/rfc7231#section-7.1.1.2
	# headers.add('date', Time.now.httpdate)
	
	return self.new(status, headers, body, protocol)
end

.wrap_headers(fields) ⇒ Object

Process the rack response headers into into a HTTP::Headers instance, along with any extra `rack.` metadata.



56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
# File 'lib/protocol/rack/response.rb', line 56

def self.wrap_headers(fields)
	headers = ::Protocol::HTTP::Headers.new
	meta = {}
	
	fields.each do |key, value|
		key = key.downcase
		
		if key.start_with?('rack.')
			meta[key] = value
		elsif value.is_a?(Array)
			value.each do |value|
				headers[key] = value
			end
		else
			headers[key] = value
		end
	end
	
	return headers, meta
end