lamby_updated 5.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|