jets 1.0.18 → 1.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +4 -0
- data/Gemfile.lock +10 -10
- data/README/testing.md +5 -2
- data/lib/jets.rb +2 -2
- data/lib/jets/application.rb +69 -40
- data/lib/jets/booter.rb +17 -20
- data/lib/jets/builders/code_builder.rb +7 -8
- data/lib/jets/cfn/ship.rb +0 -6
- data/lib/jets/commands/build.rb +0 -5
- data/lib/jets/commands/deploy.rb +0 -4
- data/lib/jets/commands/main.rb +31 -4
- data/lib/jets/commands/templates/skeleton/{.env → .env.tt} +1 -0
- data/lib/jets/commands/templates/skeleton/config.ru +1 -0
- data/lib/jets/commands/upgrade/v1.rb +12 -0
- data/lib/jets/controller.rb +5 -0
- data/lib/jets/controller/base.rb +43 -21
- data/lib/jets/controller/cookies.rb +40 -0
- data/lib/jets/controller/cookies/jar.rb +269 -0
- data/lib/jets/controller/middleware.rb +4 -0
- data/lib/jets/controller/middleware/local.rb +119 -0
- data/lib/jets/{server/lambda_aws_proxy.rb → controller/middleware/local/api_gateway.rb} +11 -49
- data/lib/jets/controller/middleware/local/mimic_aws_call.rb +38 -0
- data/lib/jets/{server → controller/middleware/local}/route_matcher.rb +4 -4
- data/lib/jets/controller/middleware/main.rb +46 -0
- data/lib/jets/{server → controller/middleware}/webpacker_setup.rb +0 -1
- data/lib/jets/controller/params.rb +2 -1
- data/lib/jets/controller/rack.rb +5 -0
- data/lib/jets/controller/rack/adapter.rb +60 -0
- data/lib/jets/controller/rack/env.rb +96 -0
- data/lib/jets/controller/redirection.rb +1 -1
- data/lib/jets/controller/renderers.rb +1 -1
- data/lib/jets/controller/renderers/base_renderer.rb +0 -4
- data/lib/jets/controller/renderers/{aws_proxy_renderer.rb → rack_renderer.rb} +7 -19
- data/lib/jets/controller/renderers/template_renderer.rb +1 -1
- data/lib/jets/controller/request.rb +14 -44
- data/lib/jets/controller/response.rb +55 -7
- data/lib/jets/internal/app/controllers/jets/rack_controller.rb +13 -3
- data/lib/jets/mega.rb +7 -0
- data/lib/jets/{rack → mega}/hash_converter.rb +1 -1
- data/lib/jets/{rack → mega}/request.rb +17 -4
- data/lib/jets/middleware.rb +38 -0
- data/lib/jets/middleware/configurator.rb +84 -0
- data/lib/jets/middleware/default_stack.rb +44 -0
- data/lib/jets/middleware/layer.rb +34 -0
- data/lib/jets/middleware/stack.rb +77 -0
- data/lib/jets/resource/function.rb +1 -1
- data/lib/jets/ruby_server.rb +1 -1
- data/lib/jets/server.rb +48 -13
- data/lib/jets/version.rb +1 -1
- metadata +24 -17
- data/lib/jets/application/middleware.rb +0 -23
- data/lib/jets/default/application.rb +0 -23
- data/lib/jets/rack.rb +0 -7
- data/lib/jets/rack/server.rb +0 -47
- data/lib/jets/server/api_gateway.rb +0 -39
- data/lib/jets/server/timing_middleware.rb +0 -33
- data/lib/jets/timing.rb +0 -65
- data/lib/jets/timing/report.rb +0 -82
@@ -0,0 +1,119 @@
|
|
1
|
+
require 'kramdown'
|
2
|
+
|
3
|
+
# Handles mimicking of API Gateway to Lambda function call locally
|
4
|
+
module Jets::Controller::Middleware
|
5
|
+
class Local
|
6
|
+
extend Memoist
|
7
|
+
|
8
|
+
autoload :ApiGateway, 'jets/controller/middleware/local/api_gateway'
|
9
|
+
autoload :MimicAwsCall, 'jets/controller/middleware/local/mimic_aws_call'
|
10
|
+
autoload :RouteMatcher, 'jets/controller/middleware/local/route_matcher'
|
11
|
+
|
12
|
+
def initialize(app)
|
13
|
+
@app = app
|
14
|
+
end
|
15
|
+
|
16
|
+
def call(env)
|
17
|
+
route = RouteMatcher.new(env).find_route
|
18
|
+
unless route
|
19
|
+
return [404, {'Content-Type' => 'text/html'}, not_found(env)]
|
20
|
+
end
|
21
|
+
|
22
|
+
mimic = MimicAwsCall.new(route, env)
|
23
|
+
# Make @controller and @meth instance available so we dont not have to pass it around.
|
24
|
+
@controller, @meth, @event = mimic.controller, mimic.meth, mimic.event
|
25
|
+
|
26
|
+
if route.to == 'jets/rack#process' # megamode
|
27
|
+
# Bypass the Jets middlewares since it could interfere with the Rack
|
28
|
+
# application's middleware stack.
|
29
|
+
#
|
30
|
+
# Rails sends back a transfer-encoding=chunked. Curling Rails directly works,
|
31
|
+
# but passing the Rails response back through this middleware results in errors.
|
32
|
+
# Disable chunking responses by deleting the transfer-encoding response header.
|
33
|
+
# Would like to understand why this happens this better, if someone can explain please let me know.
|
34
|
+
status, headers, body = @controller.dispatch! # jets/rack_controller
|
35
|
+
headers.delete "transfer-encoding"
|
36
|
+
[status, headers, body]
|
37
|
+
elsif polymorphic_function?
|
38
|
+
# Will never hit when calling polymorphic function on AWS Lambda.
|
39
|
+
# This can only really get called with the local server.
|
40
|
+
run_polymophic_function
|
41
|
+
else # Normal Jets request
|
42
|
+
mimick_aws_lambda!(env, mimic.vars) unless on_aws?(env)
|
43
|
+
@app.call(env)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
def polymorphic_function?
|
48
|
+
polymorphic_function.task.lang != :ruby
|
49
|
+
end
|
50
|
+
|
51
|
+
def polymorphic_function
|
52
|
+
# Abusing PolyFun to run polymorphic code, should call LambdaExecutor directly
|
53
|
+
# after reworking LambdaExecutor so it has a better interface.
|
54
|
+
Jets::PolyFun.new(@controller.class, @meth)
|
55
|
+
end
|
56
|
+
memoize :polymorphic_function
|
57
|
+
|
58
|
+
def run_polymophic_function
|
59
|
+
resp = polymorphic_function.run(@event, @meth) # polymorphic code
|
60
|
+
status = resp['statusCode']
|
61
|
+
headers = resp['headers']
|
62
|
+
body = StringIO.new(resp['body'])
|
63
|
+
[status, headers, body] # triplet
|
64
|
+
end
|
65
|
+
|
66
|
+
# Modifies env the in the same way real call from AWS lambda would modify env
|
67
|
+
def mimick_aws_lambda!(env, vars)
|
68
|
+
env.merge!(vars)
|
69
|
+
env
|
70
|
+
end
|
71
|
+
|
72
|
+
def on_aws?(env)
|
73
|
+
return false if ENV['TEST'] # usually with test we're passing in full API Gateway fixtures with the HTTP_X_AMZN_TRACE_ID
|
74
|
+
!!env['HTTP_X_AMZN_TRACE_ID']
|
75
|
+
end
|
76
|
+
|
77
|
+
def routes_error_message(env)
|
78
|
+
message = "<h2>404 Error: Route #{env['PATH_INFO'].sub('/','')} not found</h2>"
|
79
|
+
if Jets.env != "production"
|
80
|
+
message << "<p>Here are the routes defined in your application:</p>"
|
81
|
+
message << "#{routes_table}"
|
82
|
+
end
|
83
|
+
message
|
84
|
+
end
|
85
|
+
|
86
|
+
def not_found(env)
|
87
|
+
message = routes_error_message(env)
|
88
|
+
body = <<~HTML
|
89
|
+
<!DOCTYPE html>
|
90
|
+
<html>
|
91
|
+
<head>
|
92
|
+
<meta charset="utf-8">
|
93
|
+
<title>Route not found</title>
|
94
|
+
</head>
|
95
|
+
<body>
|
96
|
+
#{message}
|
97
|
+
</body>
|
98
|
+
</html>
|
99
|
+
HTML
|
100
|
+
StringIO.new(body)
|
101
|
+
end
|
102
|
+
|
103
|
+
# Show pretty route table for user to help with debugging in non-production mode
|
104
|
+
def routes_table
|
105
|
+
routes = Jets::Router.routes
|
106
|
+
|
107
|
+
return "Your routes table is empty." if routes.empty?
|
108
|
+
|
109
|
+
text = "Verb | Path | Controller#action\n"
|
110
|
+
text << "--- | --- | ---\n"
|
111
|
+
routes.each do |route|
|
112
|
+
text << "#{route.method} | #{route.path} | #{route.to}\n"
|
113
|
+
end
|
114
|
+
html = Kramdown::Document.new(text).to_html
|
115
|
+
puts html
|
116
|
+
html
|
117
|
+
end
|
118
|
+
end
|
119
|
+
end
|
@@ -1,38 +1,13 @@
|
|
1
|
-
|
2
|
-
|
1
|
+
# Takes a Rack env and converts to ApiGateway event
|
2
|
+
class Jets::Controller::Middleware::Local
|
3
|
+
class ApiGateway
|
4
|
+
extend Memoist
|
3
5
|
|
4
|
-
class Jets::Server
|
5
|
-
# This doesnt really need to be middleware
|
6
|
-
class LambdaAwsProxy
|
7
6
|
def initialize(route, env)
|
8
|
-
@route = route
|
9
|
-
@env = env
|
10
|
-
# puts "Rack env:".colorize(:yellow)
|
11
|
-
# @env.each do |k,v|
|
12
|
-
# puts "#{k}: #{v}"
|
13
|
-
# end
|
7
|
+
@route, @env = route, env
|
14
8
|
end
|
15
9
|
|
16
|
-
def
|
17
|
-
event = build_event
|
18
|
-
context = {}
|
19
|
-
|
20
|
-
controller_class = find_controller_class
|
21
|
-
controller_action = find_controller_action
|
22
|
-
|
23
|
-
fun = Jets::PolyFun.new(controller_class, controller_action)
|
24
|
-
resp = fun.run(event, context) # check the logs for polymorphic function errors
|
25
|
-
|
26
|
-
# Map lambda proxy response format to rack format
|
27
|
-
status = resp["statusCode"]
|
28
|
-
headers = resp["headers"] || {}
|
29
|
-
headers = {'Content-Type' => 'text/html'}.merge(headers)
|
30
|
-
body = resp["body"]
|
31
|
-
|
32
|
-
[status, headers, [body]]
|
33
|
-
end
|
34
|
-
|
35
|
-
def build_event
|
10
|
+
def event
|
36
11
|
resource = @route.path(:api_gateway) # posts/{id}/edit
|
37
12
|
path = @env['PATH_INFO'].sub('/','') # remove beginning slash
|
38
13
|
{
|
@@ -48,6 +23,7 @@ class Jets::Server
|
|
48
23
|
"isBase64Encoded" => false,
|
49
24
|
}
|
50
25
|
end
|
26
|
+
memoize :event
|
51
27
|
|
52
28
|
# Annoying. The headers part of the AWS Lambda proxy structure
|
53
29
|
# does not consisently use the same casing scheme for the header keys.
|
@@ -97,12 +73,6 @@ class Jets::Server
|
|
97
73
|
end
|
98
74
|
end
|
99
75
|
|
100
|
-
# Way to fake X-Amzn-Trace-Id which on_aws? helper checks.
|
101
|
-
# This is how we distinguish a request from API gateway vs local.
|
102
|
-
if ENV['JETS_ON_AWS']
|
103
|
-
headers["X-Amzn-Trace-Id"] = "Root=fake-trace-id"
|
104
|
-
end
|
105
|
-
|
106
76
|
headers
|
107
77
|
end
|
108
78
|
|
@@ -114,21 +84,13 @@ class Jets::Server
|
|
114
84
|
# rack.input: #<StringIO:0x007f8ccf8db9a0>
|
115
85
|
def get_body
|
116
86
|
# @env["rack.input"] is always provided by rack and we should make
|
117
|
-
# the test data always have rack.input to mimic rack
|
118
|
-
#
|
87
|
+
# the test data always have rack.input to mimic rack defaulting to
|
88
|
+
# StringIO.new to help make testing easier
|
119
89
|
input = @env["rack.input"] || StringIO.new
|
120
90
|
body = input.read
|
121
|
-
#
|
91
|
+
input.rewind # IMPORTANT or else it screws up other middlewares that use the body
|
92
|
+
# return nil for blank string, because Lambda AWS_PROXY does this
|
122
93
|
body unless body.empty?
|
123
94
|
end
|
124
|
-
|
125
|
-
def find_controller_class
|
126
|
-
# posts#edit => PostsController
|
127
|
-
@route.controller_name.constantize
|
128
|
-
end
|
129
|
-
|
130
|
-
def find_controller_action
|
131
|
-
@route.action_name
|
132
|
-
end
|
133
95
|
end
|
134
96
|
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
class Jets::Controller::Middleware::Local
|
2
|
+
class MimicAwsCall
|
3
|
+
extend Memoist
|
4
|
+
|
5
|
+
def initialize(route, env)
|
6
|
+
@route, @env = route, env
|
7
|
+
end
|
8
|
+
|
9
|
+
def vars
|
10
|
+
{
|
11
|
+
'jets.controller' => controller,
|
12
|
+
'lambda.context' => context,
|
13
|
+
'lambda.event' => event,
|
14
|
+
'lambda.meth' => meth,
|
15
|
+
}
|
16
|
+
end
|
17
|
+
|
18
|
+
# Actual controller instance
|
19
|
+
def controller
|
20
|
+
controller_class = @route.controller_name.constantize
|
21
|
+
meth = @route.action_name
|
22
|
+
controller_class.new(event, context, meth)
|
23
|
+
end
|
24
|
+
|
25
|
+
def meth
|
26
|
+
@route.action_name
|
27
|
+
end
|
28
|
+
|
29
|
+
def event
|
30
|
+
ApiGateway.new(@route, @env).event
|
31
|
+
end
|
32
|
+
memoize :event
|
33
|
+
|
34
|
+
def context
|
35
|
+
{}
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
@@ -1,4 +1,4 @@
|
|
1
|
-
class Jets::
|
1
|
+
class Jets::Controller::Middleware::Local
|
2
2
|
class RouteMatcher
|
3
3
|
def initialize(env)
|
4
4
|
@env = env
|
@@ -12,14 +12,14 @@ class Jets::Server
|
|
12
12
|
# Within these 2 groups we consider the routes with the longest path first
|
13
13
|
# since posts/:id and posts/:id/edit can both match.
|
14
14
|
routes = router.ordered_routes
|
15
|
-
route = routes.find do |
|
16
|
-
route_found?(
|
15
|
+
route = routes.find do |r|
|
16
|
+
route_found?(r)
|
17
17
|
end
|
18
18
|
route
|
19
19
|
end
|
20
20
|
|
21
21
|
def route_found?(route)
|
22
|
-
request_method = @env["REQUEST_METHOD"]
|
22
|
+
request_method = @env["REQUEST_METHOD"] || "GET"
|
23
23
|
actual_path = @env["PATH_INFO"].sub(/^\//,'') # remove beginning slash
|
24
24
|
|
25
25
|
# Immediately stop checking when the request method: GET, POST, ANY, etc
|
@@ -0,0 +1,46 @@
|
|
1
|
+
# All roads lead here
|
2
|
+
#
|
3
|
+
# 1. AWS Lambda: PostsController - Rack::Adapter - Jets.application.call
|
4
|
+
# 2. Local server: config.ru - run Jet.application - Jets.application.call
|
5
|
+
#
|
6
|
+
# Then eventually:
|
7
|
+
#
|
8
|
+
# Jets.application.call - Middleware stack - Jets::Controller::Middleware::Main
|
9
|
+
#
|
10
|
+
module Jets::Controller::Middleware
|
11
|
+
class Main
|
12
|
+
def initialize(env)
|
13
|
+
@env = env
|
14
|
+
@controller = env['jets.controller']
|
15
|
+
@event = env['lambda.event']
|
16
|
+
@context = env['lambda.context']
|
17
|
+
@meth = env['lambda.meth']
|
18
|
+
end
|
19
|
+
|
20
|
+
def call
|
21
|
+
dup.call!
|
22
|
+
end
|
23
|
+
|
24
|
+
def call!
|
25
|
+
setup
|
26
|
+
@controller.dispatch! # Returns triplet
|
27
|
+
end
|
28
|
+
|
29
|
+
# Common setup logical at this point of middleware processing right before
|
30
|
+
# calling any controller actions.
|
31
|
+
def setup
|
32
|
+
# We already recreated a mimicke rack env earlier as part of the very first
|
33
|
+
# middleware layer. However, by the time the rack env reaches the main middleware
|
34
|
+
# it could had been updated by other middlewares. We update the env here again.
|
35
|
+
@controller.request.set_env!(@env)
|
36
|
+
# This allows sesison helpers to work. Sessions are managed by
|
37
|
+
# the Rack::Session::Cookie middleware by default.
|
38
|
+
@controller.session = @env['rack.session'] || {}
|
39
|
+
end
|
40
|
+
|
41
|
+
def self.call(env)
|
42
|
+
instance = new(env)
|
43
|
+
instance.call
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
@@ -1,4 +1,5 @@
|
|
1
1
|
require "action_controller/metal/strong_parameters"
|
2
|
+
require "rack"
|
2
3
|
|
3
4
|
class Jets::Controller
|
4
5
|
module Params
|
@@ -39,7 +40,7 @@ class Jets::Controller
|
|
39
40
|
# API Gateway seems to use either: content-type or Content-Type
|
40
41
|
content_type = headers["content-type"]
|
41
42
|
if content_type.to_s.include?("application/x-www-form-urlencoded")
|
42
|
-
return Rack::Utils.parse_nested_query(body)
|
43
|
+
return ::Rack::Utils.parse_nested_query(body)
|
43
44
|
end
|
44
45
|
|
45
46
|
{} # fallback to empty Hash
|
@@ -0,0 +1,60 @@
|
|
1
|
+
module Jets::Controller::Rack
|
2
|
+
class Adapter
|
3
|
+
extend Memoist
|
4
|
+
|
5
|
+
# Returns back API Gateway response hash structure
|
6
|
+
def self.process(event, context, meth)
|
7
|
+
adapter = new(event, context, meth)
|
8
|
+
adapter.process
|
9
|
+
end
|
10
|
+
|
11
|
+
def initialize(event, context, meth)
|
12
|
+
@event, @context, @meth = event, context, meth
|
13
|
+
end
|
14
|
+
|
15
|
+
# 1. Convert API Gateway event event to Rack env
|
16
|
+
# 2. Process using full Rack middleware stack
|
17
|
+
# 3. Convert back to API gateway response structure payload
|
18
|
+
def process
|
19
|
+
status, headers, body = Jets.application.call(env)
|
20
|
+
convert_to_api_gateway(status, headers, body)
|
21
|
+
end
|
22
|
+
|
23
|
+
def env
|
24
|
+
Env.new(@event, @context).convert # convert to Rack env
|
25
|
+
end
|
26
|
+
memoize :env
|
27
|
+
|
28
|
+
# Transform the structure to AWS_PROXY compatiable structure
|
29
|
+
# http://docs.aws.amazon.com/apigateway/latest/developerguide/set-up-lambda-proxy-integrations.html#api-gateway-simple-proxy-for-lambda-output-format
|
30
|
+
def convert_to_api_gateway(status, headers, body)
|
31
|
+
base64 = headers["x-jets-base64"] == 'true'
|
32
|
+
body = body.respond_to?(:read) ? body.read : body
|
33
|
+
{
|
34
|
+
"statusCode" => status,
|
35
|
+
"headers" => headers,
|
36
|
+
"body" => body,
|
37
|
+
"isBase64Encoded" => base64,
|
38
|
+
}
|
39
|
+
end
|
40
|
+
|
41
|
+
# Called from Jets::Controller::Base.process. Example:
|
42
|
+
#
|
43
|
+
# adapter.rack_vars(
|
44
|
+
# 'jets.controller' => self,
|
45
|
+
# 'lambda.context' => context,
|
46
|
+
# 'lambda.event' => event,
|
47
|
+
# 'lambda.meth' => meth,
|
48
|
+
# )
|
49
|
+
#
|
50
|
+
# Passes a these special variables so we have access to them in the middleware.
|
51
|
+
# The controller instance is called in the Main middleware.
|
52
|
+
# The lambda.* info is used by the Rack::Local middleware to create a mimicked
|
53
|
+
# controller for the local server.
|
54
|
+
#
|
55
|
+
def rack_vars(vars)
|
56
|
+
env.merge!(vars)
|
57
|
+
end
|
58
|
+
|
59
|
+
end
|
60
|
+
end
|
@@ -0,0 +1,96 @@
|
|
1
|
+
require 'rack'
|
2
|
+
|
3
|
+
# Takes an ApiGateway event and converts it to an Rack env that can be used for
|
4
|
+
# rack.call(env).
|
5
|
+
module Jets::Controller::Rack
|
6
|
+
class Env
|
7
|
+
def initialize(event, context)
|
8
|
+
@event, @context = event, context
|
9
|
+
end
|
10
|
+
|
11
|
+
def convert
|
12
|
+
options = {}
|
13
|
+
options = add_top_level(options)
|
14
|
+
options = add_http_headers(options)
|
15
|
+
path = @event['path'] || '/' # always set by API Gateway but might not be when testing shim, so setting it to make testing easier
|
16
|
+
Rack::MockRequest.env_for(path, options)
|
17
|
+
end
|
18
|
+
|
19
|
+
private
|
20
|
+
def add_top_level(options)
|
21
|
+
map = {
|
22
|
+
'CONTENT_TYPE' => content_type,
|
23
|
+
'QUERY_STRING' => query_string,
|
24
|
+
'REMOTE_ADDR' => headers['X-Forwarded-For'],
|
25
|
+
'REMOTE_HOST' => headers['Host'],
|
26
|
+
'REQUEST_METHOD' => @event['httpMethod'],
|
27
|
+
'REQUEST_PATH' => @event['path'],
|
28
|
+
'REQUEST_URI' => request_uri,
|
29
|
+
'SCRIPT_NAME' => "",
|
30
|
+
'SERVER_NAME' => headers['Host'],
|
31
|
+
'SERVER_PORT' => headers['X-Forwarded-Port'],
|
32
|
+
'SERVER_PROTOCOL' => "HTTP/1.1", # unsure if this should be set
|
33
|
+
'SERVER_SOFTWARE' => "WEBrick/1.3.1 (Ruby/2.2.2/2015-04-13)",
|
34
|
+
}
|
35
|
+
|
36
|
+
map['CONTENT_LENGTH'] = content_length if content_length
|
37
|
+
# Even if not set, Rack always assigns an StringIO to "rack.input"
|
38
|
+
map[:input] = StringIO.new(body) if body
|
39
|
+
|
40
|
+
# TODO: handle decoding base64 encoded body from API Gateaway
|
41
|
+
# Will need to make sure that pass the base64 info via a request header
|
42
|
+
|
43
|
+
options.merge(map)
|
44
|
+
end
|
45
|
+
|
46
|
+
def content_type
|
47
|
+
headers['Content-Type'] || Jets::Controller::DEFAULT_CONTENT_TYPE
|
48
|
+
end
|
49
|
+
|
50
|
+
def content_length
|
51
|
+
bytesize = body.bytesize.to_s if body
|
52
|
+
headers['Content-Length'] || bytesize
|
53
|
+
end
|
54
|
+
|
55
|
+
def body
|
56
|
+
@event['body']
|
57
|
+
end
|
58
|
+
|
59
|
+
def add_http_headers(options)
|
60
|
+
headers.each do |k,v|
|
61
|
+
# content-type => HTTP_CONTENT_TYPE
|
62
|
+
key = k.gsub('-','_').upcase
|
63
|
+
key = "HTTP_#{key}"
|
64
|
+
options[key] = v
|
65
|
+
end
|
66
|
+
options
|
67
|
+
end
|
68
|
+
|
69
|
+
def request_uri
|
70
|
+
# IE: "http://localhost:8888/posts/tung/edit?foo=bar"
|
71
|
+
proto = headers['X-Forwarded-Proto']
|
72
|
+
host = headers['Host']
|
73
|
+
port = headers['X-Forwarded-Port']
|
74
|
+
|
75
|
+
# Add port if needed
|
76
|
+
if proto == 'https' && port != '443' or
|
77
|
+
proto == 'http' && port != '80'
|
78
|
+
host = "#{host}:#{port}"
|
79
|
+
end
|
80
|
+
|
81
|
+
path = @event['path']
|
82
|
+
qs = "?#{query_string}" unless query_string.empty?
|
83
|
+
"#{proto}://#{host}#{path}#{qs}"
|
84
|
+
end
|
85
|
+
|
86
|
+
def query_string
|
87
|
+
qs_params = @event["queryStringParameters"] || {} # always set with API Gateway but when testing node shim might not be
|
88
|
+
hash = Jets::Mega::HashConverter.encode(qs_params)
|
89
|
+
hash.to_query
|
90
|
+
end
|
91
|
+
|
92
|
+
def headers
|
93
|
+
@event['headers'] || {}
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|