in samcli/local/apigw/local_apigw_service.py [0:0]
def _request_handler(self, **kwargs):
"""
We handle all requests to the host:port. The general flow of handling a request is as follows
* Fetch request from the Flask Global state. This is where Flask places the request and is per thread so
multiple requests are still handled correctly
* Find the Lambda function to invoke by doing a look up based on the request.endpoint and method
* If we don't find the function, we will throw a 502 (just like the 404 and 405 responses we get
from Flask.
* Since we found a Lambda function to invoke, we construct the Lambda Event from the request
* Then Invoke the Lambda function (docker container)
* We then transform the response or errors we get from the Invoke and return the data back to
the caller
Parameters
----------
kwargs dict
Keyword Args that are passed to the function from Flask. This happens when we have path parameters
Returns
-------
Response object
"""
route: Route = self._get_current_route(request)
request_origin = request.headers.get("Origin")
cors_headers = Cors.cors_to_headers(self.api.cors, request_origin, route.event_type)
lambda_authorizer = route.authorizer_object
# payloadFormatVersion can only support 2 values: "1.0" and "2.0"
# so we want to do strict validation to make sure it has proper value if provided
# https://docs.aws.amazon.com/apigateway/latest/developerguide/http-api-develop-integrations-lambda.html
if route.payload_format_version not in [None, "1.0", "2.0"]:
raise PayloadFormatVersionValidateException(
f'{route.payload_format_version} is not a valid value. PayloadFormatVersion must be "1.0" or "2.0"'
)
method, endpoint = self.get_request_methods_endpoints(request)
if method == "OPTIONS" and self.api.cors:
headers = Headers(cors_headers)
return self.service_response("", headers, 200)
# check for LambdaAuthorizer since that is the only authorizer we currently support
if isinstance(lambda_authorizer, LambdaAuthorizer) and not self._valid_identity_sources(request, route):
return ServiceErrorResponses.missing_lambda_auth_identity_sources()
try:
route_lambda_event = self._generate_lambda_event(request, route, method, endpoint)
auth_lambda_event = None
if lambda_authorizer:
auth_lambda_event = self._generate_lambda_authorizer_event(request, route, lambda_authorizer)
except UnicodeDecodeError as error:
LOG.error("UnicodeDecodeError while processing HTTP request: %s", error)
return ServiceErrorResponses.lambda_failure_response()
lambda_authorizer_exception = None
try:
auth_service_error = None
if lambda_authorizer:
self._invoke_parse_lambda_authorizer(lambda_authorizer, auth_lambda_event, route_lambda_event, route)
except AuthorizerUnauthorizedRequest as ex:
auth_service_error = ServiceErrorResponses.lambda_authorizer_unauthorized()
lambda_authorizer_exception = ex
except InvalidLambdaAuthorizerResponse as ex:
auth_service_error = ServiceErrorResponses.lambda_failure_response()
lambda_authorizer_exception = ex
except FunctionNotFound as ex:
lambda_authorizer_exception = ex
LOG.warning(
"Failed to find a Function to invoke a Lambda authorizer, verify that "
"this Function is defined and exists locally in the template."
)
except Exception as ex:
# re-raise the catch all exception after we track it in our telemetry
lambda_authorizer_exception = ex
raise ex
finally:
exception_name = type(lambda_authorizer_exception).__name__ if lambda_authorizer_exception else None
EventTracker.track_event(
event_name=EventName.USED_FEATURE.value,
event_value=UsedFeature.INVOKED_CUSTOM_LAMBDA_AUTHORIZERS.value,
session_id=self._click_session_id,
exception_name=exception_name,
)
if lambda_authorizer_exception:
LOG.error("Lambda authorizer failed to invoke successfully: %s", str(lambda_authorizer_exception))
if auth_service_error:
# Return the Flask service error if there is one, since these are the only exceptions
# we are anticipating from the authorizer, anything else indicates a local issue.
#
# Note that returning within a finally block will have the effect of swallowing
# any reraised exceptions.
return auth_service_error
endpoint_service_error = None
try:
# invoke the route's Lambda function
lambda_response = self._invoke_lambda_function(route.function_name, route_lambda_event)
except FunctionNotFound:
endpoint_service_error = ServiceErrorResponses.lambda_not_found_response()
except UnsupportedInlineCodeError:
endpoint_service_error = ServiceErrorResponses.not_implemented_locally(
"Inline code is not supported for sam local commands. Please write your code in a separate file."
)
except LambdaResponseParseException:
endpoint_service_error = ServiceErrorResponses.lambda_body_failure_response()
except DockerContainerCreationFailedException as ex:
endpoint_service_error = ServiceErrorResponses.container_creation_failed(ex.message)
except MissingFunctionNameException as ex:
endpoint_service_error = ServiceErrorResponses.lambda_failure_response(
f"Failed to execute endpoint. Got an invalid function name ({str(ex)})",
)
if endpoint_service_error:
return endpoint_service_error
try:
if route.event_type == Route.HTTP and (
not route.payload_format_version or route.payload_format_version == "2.0"
):
(status_code, headers, body) = self._parse_v2_payload_format_lambda_output(
lambda_response, self.api.binary_media_types, request
)
else:
(status_code, headers, body) = self._parse_v1_payload_format_lambda_output(
lambda_response, self.api.binary_media_types, request, route.event_type
)
except LambdaResponseParseException as ex:
LOG.error("Invalid lambda response received: %s", ex)
return ServiceErrorResponses.lambda_failure_response()
# Add CORS headers to the response
headers.update(cors_headers)
return self.service_response(body, headers, status_code)