lamby_updated 5.2.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.devcontainer/Dockerfile +1 -0
- data/.devcontainer/devcontainer.json +20 -0
- data/.github/workflows/test.yml +35 -0
- data/.gitignore +15 -0
- data/CHANGELOG.md +364 -0
- data/CODE_OF_CONDUCT.md +74 -0
- data/Gemfile +9 -0
- data/Gemfile.lock +204 -0
- data/LICENSE.txt +21 -0
- data/README.md +37 -0
- data/Rakefile +27 -0
- data/bin/bootstrap +2 -0
- data/bin/console +14 -0
- data/bin/setup +6 -0
- data/bin/test +8 -0
- data/bin/update +5 -0
- data/images/social.png +0 -0
- data/images/social2.png +0 -0
- data/lamby.gemspec +32 -0
- data/lib/lamby/cold_start_metrics.rb +83 -0
- data/lib/lamby/config.rb +86 -0
- data/lib/lamby/debug.rb +47 -0
- data/lib/lamby/handler.rb +137 -0
- data/lib/lamby/logger.rb +18 -0
- data/lib/lamby/proxy_context.rb +25 -0
- data/lib/lamby/proxy_server.rb +36 -0
- data/lib/lamby/rack.rb +115 -0
- data/lib/lamby/rack_alb.rb +82 -0
- data/lib/lamby/rack_http.rb +107 -0
- data/lib/lamby/rack_rest.rb +53 -0
- data/lib/lamby/railtie.rb +9 -0
- data/lib/lamby/ssm_parameter_store.rb +155 -0
- data/lib/lamby/tasks.rake +19 -0
- data/lib/lamby/version.rb +3 -0
- data/lib/lamby.rb +53 -0
- data/vendor/.keep +0 -0
- metadata +233 -0
@@ -0,0 +1,137 @@
|
|
1
|
+
module Lamby
|
2
|
+
class Handler
|
3
|
+
|
4
|
+
class << self
|
5
|
+
|
6
|
+
def call(app, event, context, options = {})
|
7
|
+
Lamby::ColdStartMetrics.instrument! if Lamby.config.cold_start_metrics?
|
8
|
+
new(app, event, context, options).call.response
|
9
|
+
end
|
10
|
+
|
11
|
+
end
|
12
|
+
|
13
|
+
def initialize(app, event, context, options = {})
|
14
|
+
@app = app
|
15
|
+
@event = event
|
16
|
+
@context = context
|
17
|
+
@options = options
|
18
|
+
end
|
19
|
+
|
20
|
+
def response
|
21
|
+
@response
|
22
|
+
end
|
23
|
+
|
24
|
+
def status
|
25
|
+
@status
|
26
|
+
end
|
27
|
+
|
28
|
+
def headers
|
29
|
+
@headers
|
30
|
+
end
|
31
|
+
|
32
|
+
def set_cookies
|
33
|
+
return @set_cookies if defined?(@set_cookies)
|
34
|
+
@set_cookies = if @headers && @headers['Set-Cookie']
|
35
|
+
@headers.delete('Set-Cookie').split("\n")
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
def body
|
40
|
+
@rbody ||= ''.tap do |rbody|
|
41
|
+
@body.each { |part| rbody << part if part }
|
42
|
+
@body.close if @body.respond_to? :close
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
def call
|
47
|
+
@response ||= call_app
|
48
|
+
self
|
49
|
+
end
|
50
|
+
|
51
|
+
def base64_encodeable?(hdrs = @headers)
|
52
|
+
hdrs && (
|
53
|
+
hdrs['Content-Transfer-Encoding'] == 'binary' ||
|
54
|
+
content_encoding_compressed?(hdrs) ||
|
55
|
+
hdrs['X-Lamby-Base64'] == '1'
|
56
|
+
)
|
57
|
+
end
|
58
|
+
|
59
|
+
def body64
|
60
|
+
Base64.strict_encode64(body)
|
61
|
+
end
|
62
|
+
|
63
|
+
private
|
64
|
+
|
65
|
+
def rack
|
66
|
+
return @rack if defined?(@rack)
|
67
|
+
@rack = begin
|
68
|
+
type = rack_option
|
69
|
+
klass = Lamby::Rack.lookup type, @event
|
70
|
+
(klass && klass.handle?(@event)) ? klass.new(@event, @context) : false
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
def rack_option
|
75
|
+
return if ENV['LAMBY_TEST_DYNAMIC_HANDLER']
|
76
|
+
@options[:rack]
|
77
|
+
end
|
78
|
+
|
79
|
+
def rack_response
|
80
|
+
{ statusCode: status,
|
81
|
+
headers: headers,
|
82
|
+
body: body }.merge(rack.response(self))
|
83
|
+
end
|
84
|
+
|
85
|
+
|
86
|
+
def call_app
|
87
|
+
retries ||= 0
|
88
|
+
if Debug.on?(@event)
|
89
|
+
Debug.call @event, @context, rack.env
|
90
|
+
elsif rack?
|
91
|
+
@status, @headers, @body = @app.call rack.env
|
92
|
+
set_cookies
|
93
|
+
rack_response
|
94
|
+
elsif lambdakiq?
|
95
|
+
Lambdakiq.cmd event: @event, context: @context
|
96
|
+
elsif lambda_cable?
|
97
|
+
LambdaCable.cmd event: @event, context: @context
|
98
|
+
elsif LambdaConsole.handle?(@event)
|
99
|
+
LambdaConsole.handle(@event)
|
100
|
+
elsif event_bridge?
|
101
|
+
Lamby.config.event_bridge_handler.call @event, @context
|
102
|
+
else
|
103
|
+
[404, {}, StringIO.new('')]
|
104
|
+
end
|
105
|
+
rescue ActiveRecord::ConnectionNotEstablished => e
|
106
|
+
retries += 1
|
107
|
+
if retries < 3
|
108
|
+
sleep(2 ** retries) # Exponential backoff
|
109
|
+
retry
|
110
|
+
else
|
111
|
+
raise e
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
def content_encoding_compressed?(hdrs)
|
116
|
+
content_encoding_header = hdrs['Content-Encoding'] || ''
|
117
|
+
content_encoding_header.split(', ').any? { |h| ['br', 'gzip'].include?(h) }
|
118
|
+
end
|
119
|
+
|
120
|
+
def rack?
|
121
|
+
rack
|
122
|
+
end
|
123
|
+
|
124
|
+
def event_bridge?
|
125
|
+
Lamby.config.event_bridge_handler &&
|
126
|
+
@event.key?('source') && @event.key?('detail') && @event.key?('detail-type')
|
127
|
+
end
|
128
|
+
|
129
|
+
def lambdakiq?
|
130
|
+
defined?(::Lambdakiq) && ::Lambdakiq.jobs?(@event)
|
131
|
+
end
|
132
|
+
|
133
|
+
def lambda_cable?
|
134
|
+
defined?(::LambdaCable) && ::LambdaCable.handle?(@event, @context)
|
135
|
+
end
|
136
|
+
end
|
137
|
+
end
|
data/lib/lamby/logger.rb
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
require 'logger'
|
2
|
+
|
3
|
+
if ENV['AWS_EXECUTION_ENV']
|
4
|
+
ENV['RAILS_LOG_TO_STDOUT'] = '1'
|
5
|
+
|
6
|
+
module Lamby
|
7
|
+
module Logger
|
8
|
+
|
9
|
+
def initialize(*args, **kwargs)
|
10
|
+
args[0] = STDOUT
|
11
|
+
super(*args, **kwargs)
|
12
|
+
end
|
13
|
+
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
Logger.prepend Lamby::Logger unless ENV['LAMBY_TEST']
|
18
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module Lamby
|
2
|
+
# This class is used by the `lamby:proxy_server` Rake task to run a
|
3
|
+
# Rack server for local development proxy. Specifically, this class
|
4
|
+
# accepts a JSON respresentation of a Lambda context object converted
|
5
|
+
# to a Hash as the single arugment.
|
6
|
+
#
|
7
|
+
class ProxyContext
|
8
|
+
def initialize(data)
|
9
|
+
@data = data
|
10
|
+
end
|
11
|
+
|
12
|
+
def method_missing(method_name, *args, &block)
|
13
|
+
key = method_name.to_s
|
14
|
+
if @data.key?(key)
|
15
|
+
@data[key]
|
16
|
+
else
|
17
|
+
super
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def respond_to_missing?(method_name, include_private = false)
|
22
|
+
@data.key?(method_name.to_s) || super
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
module Lamby
|
2
|
+
class ProxyServer
|
3
|
+
|
4
|
+
METHOD_NOT_ALLOWED = <<-HEREDOC.strip
|
5
|
+
<h1>Method Not Allowed</h1>
|
6
|
+
<p>Please POST to this endpoint with an application/json content type and JSON payload of your Lambda's event and context.<p>
|
7
|
+
<p>Example: <code>{ "event": event, "context": context }</code></p>
|
8
|
+
HEREDOC
|
9
|
+
|
10
|
+
def call(env)
|
11
|
+
return method_not_allowed unless method_allowed?(env)
|
12
|
+
event, context = event_and_context(env)
|
13
|
+
lambda_to_rack Lamby.cmd(event: event, context: context)
|
14
|
+
end
|
15
|
+
|
16
|
+
private
|
17
|
+
|
18
|
+
def event_and_context(env)
|
19
|
+
data = env['rack.input'].dup.read
|
20
|
+
json = JSON.parse(data)
|
21
|
+
[ json['event'], Lamby::ProxyContext.new(json['context']) ]
|
22
|
+
end
|
23
|
+
|
24
|
+
def method_allowed?(env)
|
25
|
+
env['REQUEST_METHOD'] == 'POST' && env['CONTENT_TYPE'] == 'application/json'
|
26
|
+
end
|
27
|
+
|
28
|
+
def method_not_allowed
|
29
|
+
[405, {"Content-Type" => "text/html"}, [ METHOD_NOT_ALLOWED.dup ]]
|
30
|
+
end
|
31
|
+
|
32
|
+
def lambda_to_rack(response)
|
33
|
+
[ 200, {"Content-Type" => "application/json"}, [ response.to_json ] ]
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
data/lib/lamby/rack.rb
ADDED
@@ -0,0 +1,115 @@
|
|
1
|
+
module Lamby
|
2
|
+
class Rack
|
3
|
+
LAMBDA_EVENT = 'lambda.event'.freeze
|
4
|
+
LAMBDA_CONTEXT = 'lambda.context'.freeze
|
5
|
+
HTTP_X_REQUESTID = 'HTTP_X_REQUEST_ID'.freeze
|
6
|
+
HTTP_X_REQUEST_START = 'HTTP_X_REQUEST_START'.freeze
|
7
|
+
HTTP_COOKIE = 'HTTP_COOKIE'.freeze
|
8
|
+
|
9
|
+
class << self
|
10
|
+
|
11
|
+
def lookup(type, event)
|
12
|
+
types[type] || types.values.detect { |t| t.handle?(event) }
|
13
|
+
end
|
14
|
+
|
15
|
+
# Order is important. REST is hardest to isolated with handle? method.
|
16
|
+
def types
|
17
|
+
{ alb: RackAlb,
|
18
|
+
http: RackHttp,
|
19
|
+
rest: RackRest,
|
20
|
+
api: RackRest }
|
21
|
+
end
|
22
|
+
|
23
|
+
end
|
24
|
+
|
25
|
+
attr_reader :event, :context
|
26
|
+
|
27
|
+
def initialize(event, context)
|
28
|
+
@event = event
|
29
|
+
@context = context
|
30
|
+
end
|
31
|
+
|
32
|
+
def env
|
33
|
+
@env ||= env_base.merge!(env_headers)
|
34
|
+
end
|
35
|
+
|
36
|
+
def response(_handler)
|
37
|
+
{}
|
38
|
+
end
|
39
|
+
|
40
|
+
def multi_value?
|
41
|
+
false
|
42
|
+
end
|
43
|
+
|
44
|
+
private
|
45
|
+
|
46
|
+
def env_base
|
47
|
+
raise NotImplementedError
|
48
|
+
end
|
49
|
+
|
50
|
+
def env_headers
|
51
|
+
headers.transform_keys do |key|
|
52
|
+
"HTTP_#{key.to_s.upcase.tr '-', '_'}"
|
53
|
+
end.tap do |hdrs|
|
54
|
+
hdrs[HTTP_X_REQUESTID] = request_id
|
55
|
+
hdrs[HTTP_X_REQUEST_START] = "t=#{request_start}" if request_start
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
def content_type
|
60
|
+
headers.delete('Content-Type') || headers.delete('content-type') || headers.delete('CONTENT_TYPE')
|
61
|
+
end
|
62
|
+
|
63
|
+
def content_length
|
64
|
+
bytesize = body.bytesize.to_s if body
|
65
|
+
headers.delete('Content-Length') || headers.delete('content-length') || headers.delete('CONTENT_LENGTH') || bytesize
|
66
|
+
end
|
67
|
+
|
68
|
+
def body
|
69
|
+
@body ||= if event['body'] && base64_encoded?
|
70
|
+
Base64.decode64 event['body']
|
71
|
+
else
|
72
|
+
event['body']
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
def headers
|
77
|
+
@headers ||= event['headers'] || {}
|
78
|
+
end
|
79
|
+
|
80
|
+
def query_string
|
81
|
+
@query_string ||= if event.key?('rawQueryString')
|
82
|
+
event['rawQueryString']
|
83
|
+
elsif event.key?('multiValueQueryStringParameters')
|
84
|
+
query = event['multiValueQueryStringParameters'] || {}
|
85
|
+
query.map do |key, value|
|
86
|
+
value.map{ |v| "#{key}=#{v}" }.join('&')
|
87
|
+
end.flatten.join('&')
|
88
|
+
else
|
89
|
+
build_query_string
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
def build_query_string
|
94
|
+
return if event['queryStringParameters'].nil?
|
95
|
+
::Rack::Utils.build_nested_query(
|
96
|
+
event.fetch('queryStringParameters')
|
97
|
+
).gsub('[', '%5B')
|
98
|
+
.gsub(']', '%5D')
|
99
|
+
end
|
100
|
+
|
101
|
+
def base64_encoded?
|
102
|
+
event['isBase64Encoded']
|
103
|
+
end
|
104
|
+
|
105
|
+
def request_id
|
106
|
+
context.aws_request_id
|
107
|
+
end
|
108
|
+
|
109
|
+
def request_start
|
110
|
+
event.dig('requestContext', 'timeEpoch') ||
|
111
|
+
event.dig('requestContext', 'requestTimeEpoch')
|
112
|
+
end
|
113
|
+
|
114
|
+
end
|
115
|
+
end
|
@@ -0,0 +1,82 @@
|
|
1
|
+
module Lamby
|
2
|
+
class RackAlb < Lamby::Rack
|
3
|
+
|
4
|
+
class << self
|
5
|
+
|
6
|
+
def handle?(event)
|
7
|
+
event.key?('httpMethod') &&
|
8
|
+
event.dig('requestContext', 'elb')
|
9
|
+
end
|
10
|
+
|
11
|
+
end
|
12
|
+
|
13
|
+
def alb?
|
14
|
+
true
|
15
|
+
end
|
16
|
+
|
17
|
+
def multi_value?
|
18
|
+
event.key? 'multiValueHeaders'
|
19
|
+
end
|
20
|
+
|
21
|
+
def response(handler)
|
22
|
+
hhdrs = handler.headers
|
23
|
+
if multi_value?
|
24
|
+
multivalue_headers = hhdrs.transform_values { |v| Array[v].compact.flatten }
|
25
|
+
multivalue_headers['Set-Cookie'] = handler.set_cookies if handler.set_cookies
|
26
|
+
end
|
27
|
+
status_description = "#{handler.status} #{::Rack::Utils::HTTP_STATUS_CODES[handler.status]}"
|
28
|
+
base64_encode = handler.base64_encodeable?(hhdrs)
|
29
|
+
body = Base64.strict_encode64(handler.body) if base64_encode
|
30
|
+
{ multiValueHeaders: multivalue_headers,
|
31
|
+
statusDescription: status_description,
|
32
|
+
isBase64Encoded: base64_encode,
|
33
|
+
body: body }.compact
|
34
|
+
end
|
35
|
+
|
36
|
+
private
|
37
|
+
|
38
|
+
def env_base
|
39
|
+
{ ::Rack::REQUEST_METHOD => event['httpMethod'],
|
40
|
+
::Rack::SCRIPT_NAME => '',
|
41
|
+
::Rack::PATH_INFO => event['path'] || '',
|
42
|
+
::Rack::QUERY_STRING => query_string,
|
43
|
+
::Rack::SERVER_NAME => headers['host'],
|
44
|
+
::Rack::SERVER_PORT => headers['x-forwarded-port'],
|
45
|
+
::Rack::SERVER_PROTOCOL => 'HTTP/1.1',
|
46
|
+
::Rack::RACK_VERSION => ::Rack::VERSION,
|
47
|
+
::Rack::RACK_URL_SCHEME => headers['x-forwarded-proto'],
|
48
|
+
::Rack::RACK_INPUT => StringIO.new(body || ''),
|
49
|
+
::Rack::RACK_ERRORS => $stderr,
|
50
|
+
::Rack::RACK_MULTITHREAD => false,
|
51
|
+
::Rack::RACK_MULTIPROCESS => false,
|
52
|
+
::Rack::RACK_RUNONCE => false,
|
53
|
+
LAMBDA_EVENT => event,
|
54
|
+
LAMBDA_CONTEXT => context
|
55
|
+
}.tap do |env|
|
56
|
+
ct = content_type
|
57
|
+
cl = content_length
|
58
|
+
env['CONTENT_TYPE'] = ct if ct
|
59
|
+
env['CONTENT_LENGTH'] = cl if cl
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
def headers
|
64
|
+
@headers ||= multi_value? ? headers_multi : super
|
65
|
+
end
|
66
|
+
|
67
|
+
def headers_multi
|
68
|
+
Hash[(event['multiValueHeaders'] || {}).map do |k,v|
|
69
|
+
if v.is_a?(Array)
|
70
|
+
if k == 'x-forwarded-for'
|
71
|
+
[k, v.join(', ')]
|
72
|
+
else
|
73
|
+
[k, v.first]
|
74
|
+
end
|
75
|
+
else
|
76
|
+
[k,v]
|
77
|
+
end
|
78
|
+
end]
|
79
|
+
end
|
80
|
+
|
81
|
+
end
|
82
|
+
end
|
@@ -0,0 +1,107 @@
|
|
1
|
+
module Lamby
|
2
|
+
class RackHttp < Lamby::Rack
|
3
|
+
|
4
|
+
class << self
|
5
|
+
|
6
|
+
def handle?(event)
|
7
|
+
event.key?('version') &&
|
8
|
+
( event.dig('requestContext', 'http') ||
|
9
|
+
event.dig('requestContext', 'httpMethod') )
|
10
|
+
end
|
11
|
+
|
12
|
+
end
|
13
|
+
|
14
|
+
def response(handler)
|
15
|
+
if handler.base64_encodeable?
|
16
|
+
{ isBase64Encoded: true, body: handler.body64 }
|
17
|
+
else
|
18
|
+
super
|
19
|
+
end.tap do |r|
|
20
|
+
if cookies = handler.set_cookies
|
21
|
+
if payload_version_one?
|
22
|
+
r[:multiValueHeaders] ||= {}
|
23
|
+
r[:multiValueHeaders]['Set-Cookie'] = cookies
|
24
|
+
else
|
25
|
+
r[:cookies] = cookies
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
private
|
32
|
+
|
33
|
+
def env_base
|
34
|
+
{ ::Rack::REQUEST_METHOD => request_method,
|
35
|
+
::Rack::SCRIPT_NAME => '',
|
36
|
+
::Rack::PATH_INFO => path_info,
|
37
|
+
::Rack::QUERY_STRING => query_string,
|
38
|
+
::Rack::SERVER_NAME => server_name,
|
39
|
+
::Rack::SERVER_PORT => server_port,
|
40
|
+
::Rack::SERVER_PROTOCOL => server_protocol,
|
41
|
+
::Rack::RACK_VERSION => ::Rack::VERSION,
|
42
|
+
::Rack::RACK_URL_SCHEME => 'https',
|
43
|
+
::Rack::RACK_INPUT => StringIO.new(body || ''),
|
44
|
+
::Rack::RACK_ERRORS => $stderr,
|
45
|
+
::Rack::RACK_MULTITHREAD => false,
|
46
|
+
::Rack::RACK_MULTIPROCESS => false,
|
47
|
+
::Rack::RACK_RUNONCE => false,
|
48
|
+
LAMBDA_EVENT => event,
|
49
|
+
LAMBDA_CONTEXT => context
|
50
|
+
}.tap do |env|
|
51
|
+
ct = content_type
|
52
|
+
cl = content_length
|
53
|
+
env['CONTENT_TYPE'] = ct if ct
|
54
|
+
env['CONTENT_LENGTH'] = cl if cl
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
def env_headers
|
59
|
+
super.tap do |hdrs|
|
60
|
+
if cookies.any?
|
61
|
+
hdrs[HTTP_COOKIE] = cookies.join('; ')
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
def request_method
|
67
|
+
event.dig('requestContext', 'http', 'method') || event['httpMethod']
|
68
|
+
end
|
69
|
+
|
70
|
+
def cookies
|
71
|
+
event['cookies'] || []
|
72
|
+
end
|
73
|
+
|
74
|
+
# Using custom domain names with v1.0 yields a good `path` parameter sans
|
75
|
+
# stage. However, v2.0 and others do not. So we are just going to remove stage
|
76
|
+
# no matter waht from other places for both.
|
77
|
+
#
|
78
|
+
def path_info
|
79
|
+
stage = event.dig('requestContext', 'stage')
|
80
|
+
spath = event.dig('requestContext', 'http', 'path') || event.dig('requestContext', 'path')
|
81
|
+
spath = event['rawPath'] if spath != event['rawPath'] && !payload_version_one?
|
82
|
+
spath.sub /\A\/#{stage}/, ''
|
83
|
+
end
|
84
|
+
|
85
|
+
def server_name
|
86
|
+
headers['x-forwarded-host'] ||
|
87
|
+
headers['X-Forwarded-Host'] ||
|
88
|
+
headers['host'] ||
|
89
|
+
headers['Host']
|
90
|
+
end
|
91
|
+
|
92
|
+
def server_port
|
93
|
+
headers['x-forwarded-port'] || headers['X-Forwarded-Port']
|
94
|
+
end
|
95
|
+
|
96
|
+
def server_protocol
|
97
|
+
event.dig('requestContext', 'http', 'protocol') ||
|
98
|
+
event.dig('requestContext', 'protocol') ||
|
99
|
+
'HTTP/1.1'
|
100
|
+
end
|
101
|
+
|
102
|
+
def payload_version_one?
|
103
|
+
event['version'] == '1.0'
|
104
|
+
end
|
105
|
+
|
106
|
+
end
|
107
|
+
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
module Lamby
|
2
|
+
class RackRest < Lamby::Rack
|
3
|
+
|
4
|
+
class << self
|
5
|
+
|
6
|
+
def handle?(event)
|
7
|
+
event.key?('httpMethod')
|
8
|
+
end
|
9
|
+
|
10
|
+
end
|
11
|
+
|
12
|
+
def response(handler)
|
13
|
+
if handler.base64_encodeable?
|
14
|
+
{ isBase64Encoded: true, body: handler.body64 }
|
15
|
+
else
|
16
|
+
super
|
17
|
+
end.tap do |r|
|
18
|
+
if cookies = handler.set_cookies
|
19
|
+
r[:multiValueHeaders] ||= {}
|
20
|
+
r[:multiValueHeaders]['Set-Cookie'] = cookies
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
private
|
26
|
+
|
27
|
+
def env_base
|
28
|
+
{ ::Rack::REQUEST_METHOD => event['httpMethod'],
|
29
|
+
::Rack::SCRIPT_NAME => '',
|
30
|
+
::Rack::PATH_INFO => event['path'] || '',
|
31
|
+
::Rack::QUERY_STRING => query_string,
|
32
|
+
::Rack::SERVER_NAME => headers['Host'],
|
33
|
+
::Rack::SERVER_PORT => headers['X-Forwarded-Port'],
|
34
|
+
::Rack::SERVER_PROTOCOL => event.dig('requestContext', 'protocol') || 'HTTP/1.1',
|
35
|
+
::Rack::RACK_VERSION => ::Rack::VERSION,
|
36
|
+
::Rack::RACK_URL_SCHEME => 'https',
|
37
|
+
::Rack::RACK_INPUT => StringIO.new(body || ''),
|
38
|
+
::Rack::RACK_ERRORS => $stderr,
|
39
|
+
::Rack::RACK_MULTITHREAD => false,
|
40
|
+
::Rack::RACK_MULTIPROCESS => false,
|
41
|
+
::Rack::RACK_RUNONCE => false,
|
42
|
+
LAMBDA_EVENT => event,
|
43
|
+
LAMBDA_CONTEXT => context
|
44
|
+
}.tap do |env|
|
45
|
+
ct = content_type
|
46
|
+
cl = content_length
|
47
|
+
env['CONTENT_TYPE'] = ct if ct
|
48
|
+
env['CONTENT_LENGTH'] = cl if cl
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
end
|
53
|
+
end
|