Class: Protocol::Rack::Adapter::Generic

Inherits:
Object
  • Object
show all
Defined in:
lib/protocol/rack/adapter/generic.rb

Direct Known Subclasses

Rack2, Rack3

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(app) ⇒ Generic

Initialize the rack adaptor middleware.

Raises:

  • (ArgumentError)


22
23
24
25
26
# File 'lib/protocol/rack/adapter/generic.rb', line 22

def initialize(app)
	@app = app
	
	raise ArgumentError, "App must be callable!" unless @app.respond_to?(:call)
end

Class Method Details

.wrap(app) ⇒ Object



16
17
18
# File 'lib/protocol/rack/adapter/generic.rb', line 16

def self.wrap(app)
	self.new(app)
end

Instance Method Details

#call(request) ⇒ Object

Build a rack ‘env` from the incoming request and apply it to the rack middleware.



95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
# File 'lib/protocol/rack/adapter/generic.rb', line 95

def call(request)
	env = self.make_environment(request)
	
	status, headers, body = @app.call(env)
	
	# The status must always be an integer.
	unless status.is_a?(Integer)
		raise ArgumentError, "Status must be an integer!"
	end
	
	# Headers must always be a hash or equivalent.
	unless headers
		raise ArgumentError, "Headers must not be nil!"
	end
	
	headers, meta = self.wrap_headers(headers)
	
	return Response.wrap(env, status, headers, meta, body, request)
rescue => exception
	Console.logger.error(self) {exception}
	
	body&.close if body.respond_to?(:close)
	
	env&.[](RACK_RESPONSE_FINISHED)&.each do |callback|
		callback.call(env, status, headers, exception)
	end
	
	return failure_response(exception)
end

#failure_response(exception) ⇒ Object

Generate a suitable response for the given exception.



128
129
130
# File 'lib/protocol/rack/adapter/generic.rb', line 128

def failure_response(exception)
	Protocol::HTTP::Response.for_exception(exception)
end

#loggerObject



28
29
30
# File 'lib/protocol/rack/adapter/generic.rb', line 28

def logger
	Console.logger
end

#make_environment(request) ⇒ Object



86
87
88
89
90
# File 'lib/protocol/rack/adapter/generic.rb', line 86

def make_environment(request)
	{
		request: request
	}
end

#unwrap_headers(headers, env) ⇒ Object

Unwrap raw HTTP headers into the CGI-style expected by Rack middleware.

Rack separates multiple headers with the same key, into a single field with multiple lines.



38
39
40
41
42
43
44
45
46
47
48
# File 'lib/protocol/rack/adapter/generic.rb', line 38

def unwrap_headers(headers, env)
	headers.each do |key, value|
		http_key = "HTTP_#{key.upcase.tr('-', '_')}"
		
		if current_value = env[http_key]
			env[http_key] = "#{current_value};#{value}"
		else
			env[http_key] = value
		end
	end
end

#unwrap_request(request, env) ⇒ Object

Process the incoming request into a valid rack ‘env`.

  • Set the ‘env` and `env` based on the incoming request body.

  • Set the ‘env` header to the request authority.

  • Set the ‘env` header to the request scheme.

  • Set ‘env` to the request remote adress.



59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
# File 'lib/protocol/rack/adapter/generic.rb', line 59

def unwrap_request(request, env)
	if content_type = request.headers.delete('content-type')
		env[CGI::CONTENT_TYPE] = content_type
	end
	
	# In some situations we don't know the content length, e.g. when using chunked encoding, or when decompressing the body.
	if body = request.body and length = body.length
		env[CGI::CONTENT_LENGTH] = length.to_s
	end
	
	self.unwrap_headers(request.headers, env)
	
	# For the sake of compatibility, we set the `HTTP_UPGRADE` header to the requested protocol.
	if protocol = request.protocol and request.version.start_with?('http/1')
		env[CGI::HTTP_UPGRADE] = Array(protocol).join(",")
	end
	
	# HTTP/2 prefers `:authority` over `host`, so we do this for backwards compatibility.
	env[CGI::HTTP_HOST] ||= request.authority
				
	if request.respond_to?(:remote_address)
		if remote_address = request.remote_address
			env[CGI::REMOTE_ADDR] = remote_address.ip_address if remote_address.ip?
		end
	end
end