Skip to content

Commit 3b7fca8

Browse files
authored
Improve lambda<->rack interface adherence to Rack's SPEC.
* HTTP header names must be capitalized. * `CONTENT_TYPE` and `CONTENT_LENGTH` lack `HTTP_` prefix. * Don't hard-code server name, port, or scheme, when they are available from the Lambda event.
1 parent 0c87619 commit 3b7fca8

File tree

1 file changed

+36
-23
lines changed

1 file changed

+36
-23
lines changed

lambda.rb

Lines changed: 36 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -20,40 +20,52 @@
2020
# Global object that responds to the call method. Stay outside of the handler
2121
# to take advantage of container reuse
2222
$app ||= Rack::Builder.parse_file("#{__dir__}/app/config.ru").first
23+
ENV['RACK_ENV'] ||= 'production'
2324

24-
def handler(event:, context:)
25+
26+
def lambda_handler(event:, context:)
2527
# Check if the body is base64 encoded. If it is, try to decode it
26-
body =
27-
if event['isBase64Encoded']
28-
Base64.decode64(event['body'])
29-
else
30-
event['body']
31-
end
28+
body = if event['isBase64Encoded']
29+
Base64.decode64 event['body']
30+
else
31+
event['body']
32+
end || ''
33+
3234
# Rack expects the querystring in plain text, not a hash
33-
querystring = Rack::Utils.build_query(event['queryStringParameters']) if event['queryStringParameters']
35+
headers = event.fetch 'headers', {}
36+
3437
# Environment required by Rack (http://www.rubydoc.info/github/rack/rack/file/SPEC)
3538
env = {
36-
'REQUEST_METHOD' => event['httpMethod'],
39+
'REQUEST_METHOD' => event.fetch('httpMethod'),
3740
'SCRIPT_NAME' => '',
38-
'PATH_INFO' => event['path'] || '',
39-
'QUERY_STRING' => querystring || '',
40-
'SERVER_NAME' => 'localhost',
41-
'SERVER_PORT' => 443,
42-
'CONTENT_TYPE' => event['headers']['content-type'],
41+
'PATH_INFO' => event.fetch('path', ''),
42+
'QUERY_STRING' => Rack::Utils.build_query(event['queryStringParameters'] || {}),
43+
'SERVER_NAME' => headers.fetch('Host', 'localhost'),
44+
'SERVER_PORT' => headers.fetch('X-Forwarded-Port', 443).to_s,
4345

4446
'rack.version' => Rack::VERSION,
45-
'rack.url_scheme' => 'https',
46-
'rack.input' => StringIO.new(body || ''),
47+
'rack.url_scheme' => headers.fetch('CloudFront-Forwarded-Proto') { headers.fetch('X-Forwarded-Proto', 'https') },
48+
'rack.input' => StringIO.new(body),
4749
'rack.errors' => $stderr,
4850
}
51+
4952
# Pass request headers to Rack if they are available
50-
unless event['headers'].nil?
51-
event['headers'].each { |key, value| env["HTTP_#{key}"] = value }
53+
headers.each_pair do |key, value|
54+
# 'CloudFront-Forwarded-Proto' => 'CLOUDFRONT_FORWARDED_PROTO'
55+
# Content-Type and Content-Length are handled specially per the Rack SPEC linked above.
56+
name = key.upcase.gsub '-', '_'
57+
header = case name
58+
when 'CONTENT_TYPE', 'CONTENT_LENGTH'
59+
name
60+
else
61+
"HTTP_#{name}"
62+
end
63+
env[header] = value.to_s
5264
end
5365

5466
begin
5567
# Response from Rack must have status, headers and body
56-
status, headers, body = $app.call(env)
68+
status, headers, body = $app.call env
5769

5870
# body is an array. We combine all the items to a single string
5971
body_content = ""
@@ -68,17 +80,18 @@ def handler(event:, context:)
6880
'headers' => headers,
6981
'body' => body_content
7082
}
71-
if event['requestContext'].key?('elb')
83+
if event['requestContext'].has_key?('elb')
7284
# Required if we use Application Load Balancer instead of API Gateway
7385
response['isBase64Encoded'] = false
7486
end
75-
rescue Exception => msg
76-
# If there is any exception, we return a 500 error with an error message
87+
rescue Exception => exception
88+
# If there is _any_ exception, we return a 500 error with an error message
7789
response = {
7890
'statusCode' => 500,
79-
'body' => msg
91+
'body' => exception.message
8092
}
8193
end
94+
8295
# By default, the response serializer will call #to_json for us
8396
response
8497
end

0 commit comments

Comments
 (0)