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
@@ -19,7 +19,7 @@ class Jets::Controller
|
|
19
19
|
|
20
20
|
redirect_url = ensure_protocol(redirect_url)
|
21
21
|
|
22
|
-
aws_proxy = Renderers::
|
22
|
+
aws_proxy = Renderers::RackRenderer.new(self,
|
23
23
|
status: options[:status] || 302,
|
24
24
|
headers: { "Location" => redirect_url },
|
25
25
|
body: "",
|
@@ -1,5 +1,5 @@
|
|
1
1
|
module Jets::Controller::Renderers
|
2
|
-
autoload :AwsProxyRenderer, "jets/controller/renderers/aws_proxy_renderer"
|
3
2
|
autoload :BaseRenderer, "jets/controller/renderers/base_renderer"
|
3
|
+
autoload :RackRenderer, "jets/controller/renderers/rack_renderer"
|
4
4
|
autoload :TemplateRenderer, "jets/controller/renderers/template_renderer"
|
5
5
|
end
|
@@ -2,17 +2,10 @@ require "rack/utils"
|
|
2
2
|
|
3
3
|
# Special renderer. All the other renderers lead here
|
4
4
|
module Jets::Controller::Renderers
|
5
|
-
class
|
6
|
-
# Transform the structure to AWS_PROXY compatiable structure
|
7
|
-
# http://docs.aws.amazon.com/apigateway/latest/developerguide/set-up-lambda-proxy-integrations.html#api-gateway-simple-proxy-for-lambda-output-format
|
5
|
+
class RackRenderer < BaseRenderer
|
8
6
|
# Example response:
|
9
7
|
#
|
10
|
-
# {
|
11
|
-
# "statusCode" => status,
|
12
|
-
# "headers" => headers,
|
13
|
-
# "body" => body,
|
14
|
-
# "isBase64Encoded" => base64,
|
15
|
-
# }
|
8
|
+
# [200, {"my-header" = > "value" }, "my body" ]
|
16
9
|
def render
|
17
10
|
# we do some normalization here
|
18
11
|
status = map_status_code(@options[:status]) || 200
|
@@ -22,16 +15,11 @@ module Jets::Controller::Renderers
|
|
22
15
|
|
23
16
|
headers = @options[:headers] || {}
|
24
17
|
headers = cors_headers.merge(headers)
|
25
|
-
headers["Content-Type"] ||= @options[:content_type] ||
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
"statusCode" => status,
|
31
|
-
"headers" => headers,
|
32
|
-
"body" => body,
|
33
|
-
"isBase64Encoded" => base64,
|
34
|
-
}
|
18
|
+
headers["Content-Type"] ||= @options[:content_type] || Jets::Controller::DEFAULT_CONTENT_TYPE
|
19
|
+
# x-jets-base64 to convert this Rack triplet to a API Gateway hash structure later
|
20
|
+
headers["x-jets-base64"] = base64 ? "true" : "false"
|
21
|
+
body = StringIO.new(body)
|
22
|
+
[status, headers, body] # triplet
|
35
23
|
end
|
36
24
|
|
37
25
|
# maps:
|
@@ -16,7 +16,7 @@ module Jets::Controller::Renderers
|
|
16
16
|
body = renderer.render(render_options)
|
17
17
|
@options[:body] = body # important to set as it was originally nil
|
18
18
|
|
19
|
-
|
19
|
+
RackRenderer.new(@controller, @options).render
|
20
20
|
end
|
21
21
|
|
22
22
|
# Example: posts/index
|
@@ -1,42 +1,21 @@
|
|
1
|
-
|
1
|
+
require 'rack/request'
|
2
|
+
|
2
3
|
class Jets::Controller
|
3
|
-
class Request
|
4
|
-
def initialize(event)
|
5
|
-
@event = event
|
4
|
+
class Request < ::Rack::Request
|
5
|
+
def initialize(event, context)
|
6
|
+
@event, @context = event, context
|
7
|
+
super(env)
|
6
8
|
end
|
7
9
|
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
Accept-Encoding
|
12
|
-
Accept-Language
|
13
|
-
cache-control
|
14
|
-
CloudFront-Forwarded-Proto
|
15
|
-
CloudFront-Is-Desktop-Viewer
|
16
|
-
CloudFront-Is-Mobile-Viewer
|
17
|
-
CloudFront-Is-SmartTV-Viewer
|
18
|
-
CloudFront-Is-Tablet-Viewer
|
19
|
-
CloudFront-Viewer-Country
|
20
|
-
content-type
|
21
|
-
Host
|
22
|
-
origin
|
23
|
-
Referer
|
24
|
-
upgrade-insecure-requests
|
25
|
-
User-Agent
|
26
|
-
Via
|
27
|
-
X-Amz-Cf-Id
|
28
|
-
X-Amzn-Trace-Id
|
29
|
-
X-Forwarded-For
|
30
|
-
X-Forwarded-Port
|
31
|
-
X-Forwarded-Proto
|
32
|
-
].freeze
|
10
|
+
def env
|
11
|
+
@env ||= Jets::Controller::Rack::Env.new(@event, @context).convert # convert to Rack env
|
12
|
+
end
|
33
13
|
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
METHOD
|
14
|
+
# When request hits the middleware Controller::Rack::Middleware::Main endpoint
|
15
|
+
# We set the it with the updated env since it could had been mutated down the
|
16
|
+
# middleware stack.
|
17
|
+
def set_env!(env)
|
18
|
+
@env = env
|
40
19
|
end
|
41
20
|
|
42
21
|
# API Gateway is inconsistent about how it cases it keys.
|
@@ -46,14 +25,5 @@ class Jets::Controller
|
|
46
25
|
headers = @event["headers"] || {}
|
47
26
|
headers.transform_keys { |key| key.downcase }
|
48
27
|
end
|
49
|
-
|
50
|
-
def xhr?
|
51
|
-
headers["x-requested-with"] == "XMLHttpRequest"
|
52
|
-
end
|
53
|
-
|
54
|
-
def path
|
55
|
-
@event["path"]
|
56
|
-
end
|
57
|
-
|
58
28
|
end
|
59
29
|
end
|
@@ -1,13 +1,61 @@
|
|
1
|
+
require 'rack/response'
|
2
|
+
|
1
3
|
class Jets::Controller
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
4
|
+
# The response object. See Rack::Response and Rack::Response::Helpers for
|
5
|
+
# more info:
|
6
|
+
# http://rubydoc.info/github/rack/rack/master/Rack/Response
|
7
|
+
# http://rubydoc.info/github/rack/rack/master/Rack/Response/Helpers
|
8
|
+
class Response < ::Rack::Response
|
9
|
+
DROP_BODY_RESPONSES = [204, 304]
|
10
|
+
def initialize(*)
|
11
|
+
super
|
12
|
+
# headers['Content-Type'] ||= 'text/html'
|
13
|
+
end
|
14
|
+
|
15
|
+
# TODO: unsure if we should even have these methods. We dont really use them.
|
16
|
+
def body=(value)
|
17
|
+
value = value.body while Rack::Response === value
|
18
|
+
@body = String === value ? [value.to_str] : value
|
19
|
+
end
|
20
|
+
|
21
|
+
def each
|
22
|
+
block_given? ? super : enum_for(:each)
|
23
|
+
end
|
24
|
+
|
25
|
+
def finish
|
26
|
+
result = body
|
27
|
+
|
28
|
+
if drop_content_info?
|
29
|
+
headers.delete "Content-Length"
|
30
|
+
headers.delete "Content-Type"
|
31
|
+
end
|
32
|
+
|
33
|
+
if drop_body?
|
34
|
+
close
|
35
|
+
result = []
|
36
|
+
end
|
37
|
+
|
38
|
+
if calculate_content_length?
|
39
|
+
# if some other code has already set Content-Length, don't muck with it
|
40
|
+
# currently, this would be the static file-handler
|
41
|
+
headers["Content-Length"] = body.inject(0) { |l, p| l + p.bytesize }.to_s
|
42
|
+
end
|
43
|
+
|
44
|
+
[status.to_i, headers, result]
|
45
|
+
end
|
46
|
+
|
47
|
+
private
|
48
|
+
|
49
|
+
def calculate_content_length?
|
50
|
+
headers["Content-Type"] and not headers["Content-Length"] and Array === body
|
51
|
+
end
|
52
|
+
|
53
|
+
def drop_content_info?
|
54
|
+
status.to_i / 100 == 1 or drop_body?
|
7
55
|
end
|
8
56
|
|
9
|
-
def
|
10
|
-
|
57
|
+
def drop_body?
|
58
|
+
DROP_BODY_RESPONSES.include?(status.to_i)
|
11
59
|
end
|
12
60
|
end
|
13
61
|
end
|
@@ -4,12 +4,22 @@ class Jets::RackController < Jets::Controller::Base
|
|
4
4
|
|
5
5
|
# Megamode
|
6
6
|
def process
|
7
|
-
resp =
|
7
|
+
resp = mega_request
|
8
8
|
render(resp)
|
9
9
|
end
|
10
10
|
|
11
11
|
private
|
12
|
-
|
13
|
-
|
12
|
+
# Override process! so it doesnt go through middleware adapter and hits
|
13
|
+
# process logic directly. This handles the case for AWS Lambda.
|
14
|
+
# For local server, we adjust the Middleware::Local logic.
|
15
|
+
def process!
|
16
|
+
status, headers, body = dispatch!
|
17
|
+
# Use the adapter only to convert the Rack triplet to a API Gateway hash structure
|
18
|
+
adapter = Jets::Controller::Rack::Adapter.new(event, context, meth)
|
19
|
+
adapter.convert_to_api_gateway(status, headers, body)
|
20
|
+
end
|
21
|
+
|
22
|
+
def mega_request
|
23
|
+
Jets::Mega::Request.new(event, self).proxy
|
14
24
|
end
|
15
25
|
end
|
data/lib/jets/mega.rb
ADDED
@@ -1,18 +1,21 @@
|
|
1
1
|
require 'net/http'
|
2
|
+
require 'rack'
|
2
3
|
|
3
|
-
module Jets::
|
4
|
+
module Jets::Mega
|
4
5
|
class Request
|
5
6
|
def initialize(event, controller)
|
6
7
|
@event = event
|
7
8
|
@controller = controller # Jets::Controller instance
|
8
9
|
end
|
9
10
|
|
10
|
-
def
|
11
|
+
def proxy
|
11
12
|
http_method = @event['httpMethod'] # GET, POST, PUT, DELETE, etc
|
12
13
|
params = @controller.params(raw: true, path_parameters: false)
|
13
14
|
|
14
15
|
uri = URI("http://localhost:9292#{@controller.request.path}") # local rack server
|
15
16
|
http = Net::HTTP.new(uri.host, uri.port)
|
17
|
+
http.open_timeout = 60
|
18
|
+
http.read_timeout = 60
|
16
19
|
|
17
20
|
# Rails sets _method=patch or _method=put as workaround
|
18
21
|
# Falls back to GET when testing in lambda console
|
@@ -20,7 +23,7 @@ module Jets::Rack
|
|
20
23
|
http_class.capitalize!
|
21
24
|
|
22
25
|
request_class = "Net::HTTP::#{http_class}".constantize # IE: Net::HTTP::Get
|
23
|
-
request = request_class.new(
|
26
|
+
request = request_class.new(uri.path)
|
24
27
|
if %w[Post Patch Put].include?(http_class)
|
25
28
|
params = HashConverter.encode(params)
|
26
29
|
request.set_form_data(params)
|
@@ -28,8 +31,18 @@ module Jets::Rack
|
|
28
31
|
|
29
32
|
request = set_headers!(request)
|
30
33
|
|
31
|
-
#
|
34
|
+
# Setup body
|
35
|
+
env = Jets::Controller::Rack::Env.new(@event, {}).convert # convert to Rack env
|
36
|
+
source_request = Rack::Request.new(env)
|
37
|
+
if source_request.body.respond_to?(:read)
|
38
|
+
request.body = source_request.body.read
|
39
|
+
request.content_length = source_request.content_length.to_i
|
40
|
+
source_request.body.rewind
|
41
|
+
end
|
42
|
+
|
32
43
|
response = http.request(request)
|
44
|
+
|
45
|
+
# TODO: handle binary
|
33
46
|
{
|
34
47
|
status: response.code.to_i,
|
35
48
|
headers: response.each_header.to_h,
|
@@ -0,0 +1,38 @@
|
|
1
|
+
module Jets
|
2
|
+
module Middleware
|
3
|
+
extend Memoist
|
4
|
+
|
5
|
+
autoload :Configurator, 'jets/middleware/configurator'
|
6
|
+
autoload :DefaultStack, 'jets/middleware/default_stack'
|
7
|
+
autoload :Layer, 'jets/middleware/layer'
|
8
|
+
autoload :Stack, 'jets/middleware/stack'
|
9
|
+
|
10
|
+
def call(env)
|
11
|
+
stack = middlewares.build(endpoint)
|
12
|
+
stack.call(env)
|
13
|
+
end
|
14
|
+
|
15
|
+
# Final middleware in the stack
|
16
|
+
def endpoint
|
17
|
+
Jets::Controller::Middleware::Main
|
18
|
+
end
|
19
|
+
|
20
|
+
# Called in Jets::Booter to build middleware stack only once during bootup
|
21
|
+
def build_stack
|
22
|
+
middlewares
|
23
|
+
end
|
24
|
+
|
25
|
+
def middlewares
|
26
|
+
config_middleware.merge_into(default_stack) # returns Jets::Middleware::Stack
|
27
|
+
end
|
28
|
+
memoize :middlewares
|
29
|
+
|
30
|
+
def default_stack
|
31
|
+
Jets::Middleware::DefaultStack.new(Jets.config, Jets.application).build_stack # returns Jets::Middleware::Stack
|
32
|
+
end
|
33
|
+
|
34
|
+
def config_middleware
|
35
|
+
Jets.config.middleware # returns Jets::Middleware::Configurator
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,84 @@
|
|
1
|
+
# Based on Rails MiddlewareStackProxy
|
2
|
+
#
|
3
|
+
# Configurator is a proxy for the Jets middleware stack that allows
|
4
|
+
# you to configure middlewares in your application. It works basically as a
|
5
|
+
# command recorder, saving each command to be applied after initialization
|
6
|
+
# over the default middleware stack, so you can add, swap, or remove any
|
7
|
+
# middleware in Jets.
|
8
|
+
#
|
9
|
+
# You can add your own middlewares by using the +config.middleware.use+ method:
|
10
|
+
#
|
11
|
+
# config.middleware.use Magical::Unicorns
|
12
|
+
#
|
13
|
+
# This will put the <tt>Magical::Unicorns</tt> middleware on the end of the stack.
|
14
|
+
# You can use +insert_before+ if you wish to add a middleware before another:
|
15
|
+
#
|
16
|
+
# config.middleware.insert_before Rack::Head, Magical::Unicorns
|
17
|
+
#
|
18
|
+
# There's also +insert_after+ which will insert a middleware after another:
|
19
|
+
#
|
20
|
+
# config.middleware.insert_after Rack::Head, Magical::Unicorns
|
21
|
+
#
|
22
|
+
# Middlewares can also be completely swapped out and replaced with others:
|
23
|
+
#
|
24
|
+
# config.middleware.swap ActionDispatch::Flash, Magical::Unicorns
|
25
|
+
#
|
26
|
+
# And finally they can also be removed from the stack completely:
|
27
|
+
#
|
28
|
+
# config.middleware.delete ActionDispatch::Flash
|
29
|
+
#
|
30
|
+
module Jets::Middleware
|
31
|
+
class Configurator
|
32
|
+
def initialize(operations = [], delete_operations = [])
|
33
|
+
@operations = operations
|
34
|
+
@delete_operations = delete_operations
|
35
|
+
end
|
36
|
+
|
37
|
+
def insert_before(*args, &block)
|
38
|
+
@operations << [__method__, args, block]
|
39
|
+
end
|
40
|
+
|
41
|
+
alias :insert :insert_before
|
42
|
+
|
43
|
+
def insert_after(*args, &block)
|
44
|
+
@operations << [__method__, args, block]
|
45
|
+
end
|
46
|
+
|
47
|
+
def swap(*args, &block)
|
48
|
+
@operations << [__method__, args, block]
|
49
|
+
end
|
50
|
+
|
51
|
+
def use(*args, &block)
|
52
|
+
@operations << [__method__, args, block]
|
53
|
+
end
|
54
|
+
|
55
|
+
def delete(*args, &block)
|
56
|
+
@delete_operations << [__method__, args, block]
|
57
|
+
end
|
58
|
+
|
59
|
+
def unshift(*args, &block)
|
60
|
+
@operations << [__method__, args, block]
|
61
|
+
end
|
62
|
+
|
63
|
+
def merge_into(other) #:nodoc:
|
64
|
+
(@operations + @delete_operations).each do |operation, args, block|
|
65
|
+
other.send(operation, *args, &block)
|
66
|
+
end
|
67
|
+
|
68
|
+
other
|
69
|
+
end
|
70
|
+
|
71
|
+
def +(other) # :nodoc:
|
72
|
+
Configurator.new(@operations + other.operations, @delete_operations + other.delete_operations)
|
73
|
+
end
|
74
|
+
|
75
|
+
protected
|
76
|
+
def operations
|
77
|
+
@operations
|
78
|
+
end
|
79
|
+
|
80
|
+
def delete_operations
|
81
|
+
@delete_operations
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
module Jets::Middleware
|
2
|
+
class DefaultStack
|
3
|
+
attr_reader :config, :app
|
4
|
+
def initialize(app, config)
|
5
|
+
@app = app
|
6
|
+
@config = config
|
7
|
+
end
|
8
|
+
|
9
|
+
def build_stack
|
10
|
+
Stack.new do |middleware|
|
11
|
+
middleware.use Jets::Controller::Middleware::Local # mimics AWS Lambda for local server only
|
12
|
+
middleware.use Rack::Runtime
|
13
|
+
middleware.use Rack::MethodOverride
|
14
|
+
middleware.use session_store, session_options # use session_store, session_options
|
15
|
+
middleware.use Rack::Head
|
16
|
+
middleware.use Rack::ConditionalGet
|
17
|
+
middleware.use Rack::ETag
|
18
|
+
use_webpacker(middleware)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
private
|
23
|
+
# Written as method to easily not include webpacker for case when either
|
24
|
+
# webpacker not installed at all or disabled upon `jets deploy`.
|
25
|
+
def use_webpacker(middleware)
|
26
|
+
return unless Jets.webpacker? # checks for local development if webpacker installed
|
27
|
+
# Different check for middleware because we need webpacker helpers for url helpers.
|
28
|
+
# But we dont want to actually serve via webpacker middleware when running on AWS.
|
29
|
+
# By this time the url helpers are serving assets out of s3.
|
30
|
+
return if File.exist?("#{Jets.root}config/disable-webpacker-middleware.txt") # created as part of `jets deploy`
|
31
|
+
require "jets/controller/middleware/webpacker_setup"
|
32
|
+
middleware.use Webpacker::DevServerProxy
|
33
|
+
end
|
34
|
+
|
35
|
+
def session_store
|
36
|
+
Jets.config.session[:store] # do not use dot notation. session.store is a method on ActiveSupport::OrderedOptions.new
|
37
|
+
end
|
38
|
+
|
39
|
+
def session_options
|
40
|
+
defaults = { secret: ENV['SECRET_KEY_BASE'] }
|
41
|
+
defaults.merge(Jets.config.session.options)
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|