Exemple #1
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

        :param kwargs dict: Keyword Args that are passed to the function from Flask. This happens when we have
            Path Parameters.
        :return: Response object
        """
        route = self._get_current_route(request)

        try:
            event = self._construct_event(request, self.port,
                                          route.binary_types)
        except UnicodeDecodeError:
            return ServiceErrorResponses.lambda_failure_response()

        stdout_stream = io.BytesIO()

        try:
            self.lambda_runner.invoke(route.function_name,
                                      event,
                                      stdout=stdout_stream,
                                      stderr=self.stderr)
        except FunctionNotFound:
            return ServiceErrorResponses.lambda_not_found_response()

        lambda_response, lambda_logs, _ = LambdaOutputParser.get_lambda_output(
            stdout_stream)

        if self.stderr and lambda_logs:
            # Write the logs to stderr if available.
            self.stderr.write(lambda_logs)

        try:
            (status_code, headers,
             body) = self._parse_lambda_output(lambda_response,
                                               route.binary_types, request)
        except (KeyError, TypeError, ValueError):
            LOG.error(
                "Function returned an invalid response (must include one of: body, headers or "
                "statusCode in the response object). Response received: %s",
                lambda_response)
            return ServiceErrorResponses.lambda_failure_response()

        return self.service_response(body, headers, status_code)
Exemple #2
0
    def test_get_lambda_output_extracts_response(self, test_case_name,
                                                 stdout_data, expected_logs,
                                                 expected_response):
        stdout = Mock()
        stdout.getvalue.return_value = stdout_data

        response, logs, is_customer_error = LambdaOutputParser.get_lambda_output(
            stdout)
        self.assertEqual(logs, expected_logs)
        self.assertEqual(response, expected_response)
        self.assertFalse(is_customer_error)
    def _invoke_request_handler(self, function_name):
        """
        Request Handler for the Local Lambda Invoke path. This method is responsible for understanding the incoming
        request and invoking the Local Lambda Function

        Parameters
        ----------
        function_name str
            Name of the function to invoke

        Returns
        -------
        A Flask Response response object as if it was returned from Lambda
        """
        flask_request = request

        request_data = flask_request.get_data()

        if not request_data:
            request_data = b'{}'

        request_data = request_data.decode('utf-8')

        stdout_stream = io.BytesIO()
        stdout_stream_writer = StreamWriter(stdout_stream, self.is_debugging)

        try:
            self.lambda_runner.invoke(function_name,
                                      request_data,
                                      stdout=stdout_stream_writer,
                                      stderr=self.stderr)
        except FunctionNotFound:
            LOG.debug('%s was not found to invoke.', function_name)
            return LambdaErrorResponses.resource_not_found(function_name)

        lambda_response, lambda_logs, is_lambda_user_error_response = \
            LambdaOutputParser.get_lambda_output(stdout_stream)

        if self.stderr and lambda_logs:
            # Write the logs to stderr if available.
            self.stderr.write(lambda_logs)

        if is_lambda_user_error_response:
            return self.service_response(
                lambda_response, {
                    'Content-Type': 'application/json',
                    'x-amz-function-error': 'Unhandled'
                }, 200)

        return self.service_response(lambda_response,
                                     {'Content-Type': 'application/json'}, 200)
    def _invoke_request_handler(self, function_name):
        """
        Request Handler for the Local Lambda Invoke path. This method is responsible for understanding the incoming
        request and invoking the Local Lambda Function

        Parameters
        ----------
        function_name str
            Name of the function to invoke

        Returns
        -------
        A Flask Response response object as if it was returned from Lambda
        """
        flask_request = request

        request_data = flask_request.get_data()

        if not request_data:
            request_data = b'{}'

        request_data = request_data.decode('utf-8')

        stdout_stream = io.BytesIO()
        stdout_stream_writer = StreamWriter(stdout_stream, self.is_debugging)

        try:
            self.lambda_runner.invoke(function_name, request_data, stdout=stdout_stream_writer, stderr=self.stderr)
        except FunctionNotFound:
            LOG.debug('%s was not found to invoke.', function_name)
            return LambdaErrorResponses.resource_not_found(function_name)

        lambda_response, lambda_logs, is_lambda_user_error_response = \
            LambdaOutputParser.get_lambda_output(stdout_stream)

        if self.stderr and lambda_logs:
            # Write the logs to stderr if available.
            self.stderr.write(lambda_logs)

        if is_lambda_user_error_response:
            return self.service_response(lambda_response,
                                         {'Content-Type': 'application/json', 'x-amz-function-error': 'Unhandled'},
                                         200)

        return self.service_response(lambda_response, {'Content-Type': 'application/json'}, 200)
    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 = self._get_current_route(request)
        cors_headers = Cors.cors_to_headers(self.api.cors)

        method, _ = self.get_request_methods_endpoints(request)
        if method == "OPTIONS" and self.api.cors:
            headers = Headers(cors_headers)
            return self.service_response("", headers, 200)

        try:
            event = self._construct_event(request, self.port,
                                          self.api.binary_media_types,
                                          self.api.stage_name,
                                          self.api.stage_variables)
        except UnicodeDecodeError:
            return ServiceErrorResponses.lambda_failure_response()

        stdout_stream = io.BytesIO()
        stdout_stream_writer = StreamWriter(stdout_stream, self.is_debugging)

        try:
            self.lambda_runner.invoke(route.function_name,
                                      event,
                                      stdout=stdout_stream_writer,
                                      stderr=self.stderr)
        except FunctionNotFound:
            return ServiceErrorResponses.lambda_not_found_response()

        lambda_response, lambda_logs, _ = LambdaOutputParser.get_lambda_output(
            stdout_stream)

        if self.stderr and lambda_logs:
            # Write the logs to stderr if available.
            self.stderr.write(lambda_logs)

        try:
            (status_code, headers,
             body) = self._parse_lambda_output(lambda_response,
                                               self.api.binary_media_types,
                                               request)
        except LambdaResponseParseException:
            LOG.error(str(LambdaResponseParseException))
            return ServiceErrorResponses.lambda_failure_response()

        return self.service_response(body, headers, status_code)
Exemple #6
0
 def test_is_lambda_error_response(self, input, exected_result):
     self.assertEqual(LambdaOutputParser.is_lambda_error_response(input),
                      exected_result)
Exemple #7
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 = self._get_current_route(request)
        cors_headers = Cors.cors_to_headers(self.api.cors)

        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)

        try:
            # the Lambda Event 2.0 is only used for the HTTP API gateway with defined payload format version equal 2.0
            # or none, as the default value to be used is 2.0
            # https://docs.aws.amazon.com/apigatewayv2/latest/api-reference/apis-apiid-integrations.html#apis-apiid-integrations-prop-createintegrationinput-payloadformatversion
            if route.event_type == Route.HTTP and route.payload_format_version in [
                    None, "2.0"
            ]:
                route_key = self._v2_route_key(method, endpoint,
                                               route.is_default_route)
                event = self._construct_v_2_0_event_http(
                    request,
                    self.port,
                    self.api.binary_media_types,
                    self.api.stage_name,
                    self.api.stage_variables,
                    route_key,
                )
            else:
                event = self._construct_v_1_0_event(
                    request, self.port, self.api.binary_media_types,
                    self.api.stage_name, self.api.stage_variables)
        except UnicodeDecodeError:
            return ServiceErrorResponses.lambda_failure_response()

        stdout_stream = io.BytesIO()
        stdout_stream_writer = StreamWriter(stdout_stream, self.is_debugging)

        try:
            self.lambda_runner.invoke(route.function_name,
                                      event,
                                      stdout=stdout_stream_writer,
                                      stderr=self.stderr)
        except FunctionNotFound:
            return ServiceErrorResponses.lambda_not_found_response()

        lambda_response, lambda_logs, _ = LambdaOutputParser.get_lambda_output(
            stdout_stream)

        if self.stderr and lambda_logs:
            # Write the logs to stderr if available.
            self.stderr.write(lambda_logs)

        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)
        except LambdaResponseParseException as ex:
            LOG.error("Invalid lambda response received: %s", ex)
            return ServiceErrorResponses.lambda_failure_response()

        return self.service_response(body, headers, status_code)