require 'kramdown' # Handles mimicing of API Gateway to Lambda function call locally module Jets::Controller::Middleware class Local extend Memoist def initialize(app) @app = app end def call(env) route = RouteMatcher.new(env).find_route unless route return [404, {'Content-Type' => 'text/html'}, not_found(env)] end mimic = MimicAwsCall.new(route, env) # Make @controller and @meth instance available so we dont not have to pass it around. @controller, @meth, @event = mimic.controller, mimic.meth, mimic.event if route.to == 'jets/rack#process' # megamode # Bypass the Jets middlewares since it could interfere with the Rack # application's middleware stack. # # Rails sends back a transfer-encoding=chunked. Curling Rails directly works, # but passing the Rails response back through this middleware results in errors. # Disable chunking responses by deleting the transfer-encoding response header. # Would like to understand why this happens this better, if someone can explain please let me know. status, headers, body = @controller.dispatch! # jets/rack_controller headers.delete "transfer-encoding" [status, headers, body] elsif route.to == 'jets/mount#call' # mount route status, headers, body = @controller.dispatch! # jets/mount_controller [status, headers, body] elsif polymorphic_function? run_polymophic_function else # Normal Jets request mimic_aws_lambda!(env, mimic.vars) unless on_aws?(env) @app.call(env) end end # Never hit when calling polymorphic function on AWS Lambda. Can only get called with the local server. def polymorphic_function? return false if ENV['_HANDLER'] # slight speed improvement on Lambda polymorphic_function.task.lang != :ruby end def polymorphic_function # Abusing PolyFun to run polymorphic code, should call LambdaExecutor directly # after reworking LambdaExecutor so it has a better interface. Jets::PolyFun.new(@controller.class, @meth) end memoize :polymorphic_function def run_polymophic_function resp = polymorphic_function.run(@event, @meth) # polymorphic code status = resp['statusCode'] headers = resp['headers'] body = StringIO.new(resp['body']) [status, headers, body] # triplet end # Modifies env the in the same way real call from AWS lambda would modify env def mimic_aws_lambda!(env, vars) env.merge!(vars) env end def on_aws?(env) return false if Jets.env.test? # usually with test we're passing in full API Gateway fixtures with the HTTP_X_AMZN_TRACE_ID return false if ENV['JETS_ELB'] # If we're using an ELB and Jets is inside a container running jets server, we don't want to pretend we're on AWS. on_cloud9 = !!(env['HTTP_HOST'] =~ /cloud9\..*\.amazonaws\.com/) !!env['HTTP_X_AMZN_TRACE_ID'] && !on_cloud9 end def routes_error_message(env) message = "
Here are the routes defined in your application:
" message << "#{routes_table}" end message end def not_found(env) message = routes_error_message(env) body = <<~HTML