Home > other >  Python AWS Lambda function 502 null body {"message": "Internal server error"}
Python AWS Lambda function 502 null body {"message": "Internal server error"}

Time:11-30

I am investigating the feasibility of using a python lambda to serve a throttled endpoint connecting to a dynamo database. There is a basic token authentication and the counts for the throttling are kept in a dynamodb with a TTL of a day. I call it throttling but its actualy a daily count of requests. All the complicated parts work and if the user is unauthenticated I get the expected response {"message": "No token, or your token is invalid!"} and if the daily count of requests is exhausted i also get the expected message {"message": "Daily Invocations Exhausted!"} however when the authentication and throttle allows a response from the lambda_handler i get a 502 {"message": "Internal server error"}. When I look at the API Gateway test i see this log:

Mon Nov 29 14:56:28 UTC 2021 : Endpoint response body before transformations: null
Mon Nov 29 14:56:28 UTC 2021 : Execution failed due to configuration error: Malformed Lambda proxy response
Mon Nov 29 14:56:28 UTC 2021 : Method completed with status: 502

And when I print the body and see the logs in CloudFormation i see this:

2021-11-29T15:56:28.103 01:00   {"message": "stuff!"}

I am new with python lambdas so I am completely at a loss here, this behaviour makes no sense to me, I am hoping you can see something in the code which I am missing.

import json
import time

# import requests
import boto3
from botocore.exceptions import ClientError

dynamodb = boto3.client('dynamodb')
DAY_LIMIT = 5


def get_token(token):
    try:
        response = dynamodb.get_item(TableName='tokens', Key={'token': {'S': token}})
    except ClientError as e:
        print(e.response['Error']['Message'])
    else:
        return None if 'Item' not in response else response['Item']

def get_token_throttle(token):
    try:
        response = dynamodb.get_item(TableName='token_throttle',
                              Key={'token': {'S': token}})
    except ClientError as e:
        print(e.response['Error']['Message'])
    else:
        return None if 'Item' not in response else response['Item']

def authenticated(function):
    def wrapper(event, context):
        if (event['queryStringParameters'] is None or "token" not in event['queryStringParameters'] or
                get_token(event['queryStringParameters']['token']) is None):
            return {
                "statusCode": 401,
                "body": json.dumps({
                    "message": "No token, or your token is invalid!"
                }),
            }
        else:
            return function(event, context)

    return wrapper


def lambda_limit_per_day(function):
    def wrapper(event, context):
        throttle = get_token_throttle(event['queryStringParameters']['token'])

        if throttle == None:
            dynamodb.put_item(TableName='token_throttle', Item={
                "token": {"S": event['queryStringParameters']['token']},
                "invocations": {"N": str(1)},
                "deletion": {"N": str(int(time.time())   86400)}  # one day
            })
            function(event, context)
        elif int(throttle['invocations']['N']) < DAY_LIMIT:
            dynamodb.put_item(TableName='token_throttle', Item={
                "token": {"S": event['queryStringParameters']['token']},
                "invocations": {"N": str(int(throttle['invocations']['N'])   1)},
                "deletion": {"N": str(throttle['deletion']['N'])}  # one day
            })
            function(event, context)
        else:
            return {
                "statusCode": 200,
                "body": json.dumps({
                    "message": "Daily Invocations Exhausted!"
                }),
            }
    return wrapper


@authenticated
@lambda_limit_per_day
def lambda_handler(event, context):
    """Sample pure Lambda function

    Parameters
    ----------
    event: dict, required
        API Gateway Lambda Proxy Input Format

        Event doc: https://docs.aws.amazon.com/apigateway/latest/developerguide/set-up-lambda-proxy-integrations.html#api-gateway-simple-proxy-for-lambda-input-format

    context: object, required
        Lambda Context runtime methods and attributes

        Context doc: https://docs.aws.amazon.com/lambda/latest/dg/python-context-object.html

    Returns
    ------
    API Gateway Lambda Proxy Output Format: dict

        Return doc: https://docs.aws.amazon.com/apigateway/latest/developerguide/set-up-lambda-proxy-integrations.html
    """
    print("Running Final function")
    body = json.dumps({
            "message": "stuff!"
        })
    print(body)
    return {
        "statusCode": 200,
        "body": body,
    }

CodePudding user response:

Your lambda_handler function is decorated and has an associated decorator function named lambda_limit_per_day. The return value of your Lambda function will therefore be the return value of the decorator's wrapper function.

At present, your wrapper function is not returning anything in the two paths where you handle 'not throttled' and 'within throttling limits' -- your code simply calls function(event, context) but discards the return value from those calls (to the lambda_handler decorated function). So, the return value of the wrapper function, and hence your decorated Lambda function, is implicitly None and that's a malformed response, causing API Gateway to generate a 502 response.

Change those two instances of function(event, context) to:

return function(event, context)
  • Related