AWS-Lambda

 view release on metacpan or  search on metacpan

Changes  view on Meta::CPAN

0.0.40 2023-07-09T17:55:20Z
    - Perl 5.38.0 is released
    - Perl 5.36.1 is released

0.0.39 2022-11-20T18:56:48Z
    - update dependency modules

0.0.38 2022-11-13T05:40:29Z
    - fix Untagged opnames warnings, closes https://github.com/shogo82148/p5-aws-lambda/issues/97
    - Off load perlstrip to AWS Lambda.
    - add fallback to content-type

0.0.37 2022-10-08T13:28:41Z
    - arm64 compatible layers are now available on 12 additional regions
        - Africa (Cape Town) af-south-1
        - Asia Pacific (Seoul) ap-northeast-2
        - Asia Pacific (Jakarta) ap-southeast-3
        - Asia Pacific (Hong Kong) ap-east-1
        - Asia Pacific (Osaka) ap-northeast-3
        - Canada (Central) ca-central-1
        - Europe (Paris) eu-west-3

author/publish-perl-runtime-archives.pl  view on Meta::CPAN

    for my $zip(glob "$FindBin::Bin/../.perl-layer/dist/perl-*-$suffix-$arch.zip") {
        chomp(my $sha256 = `openssl dgst -sha256 -r "$zip" | cut -d" " -f1`);
        my $name = basename($zip, '.zip');
        next unless $name =~ /^perl-([0-9]+)-([0-9]+)-/;
        my $perl_version = "$1.$2";

        head_or_put("$name/$sha256.zip", $zip);
        my $metadata = encode_json({
            url => "https://shogo82148-lambda-perl-runtime-us-east-1.s3.amazonaws.com/$name/$sha256.zip",
        });
        run_command("echo '$metadata' | aws --region 'us-east-1' s3 cp --content-type application/json - s3://shogo82148-lambda-perl-runtime-us-east-1/$name.json");
    }
}

if ($force) {
    say STDERR "FORCE TO DEPLOY";
} else {
    say STDERR "DRY RUN";
}

publish("runtime-al2023", "x86_64");

lib/AWS/Lambda/PSGI.pm  view on Meta::CPAN

    if (reftype($body) eq 'ARRAY') {
        $content = join '', grep defined, @$body;
    } else {
        local $/ = \4096;
        while (defined(my $buf = $body->getline)) {
            $content .= $buf;
        }
        $body->close;
    }

    my $type = $singleValueHeaders->{'content-type'} // 'application/octet-stream';
    my $isBase64Encoded = $type !~ m(^text/.*|application/(:?json|javascript|xml))i;
    if ($isBase64Encoded) {
        $content = encode_base64 $content, '';
    } else {
        $content = try {
            # is valid utf-8 string? try to decode as utf-8.
            decode_utf8($content, Encode::FB_CROAK | Encode::LEAVE_SRC);
        } catch {
            # it looks not utf-8 encoding. fallback to base64 encoding.
            $isBase64Encoded = 1;

lib/AWS/Lambda/ResponseWriter.pm  view on Meta::CPAN

    my $host_port = ($port == $DefaultPort{$scheme} ? $host : "$host:$port");
    my $request = {
        method    => "POST",
        scheme    => $scheme,
        host      => $host,
        port      => $port,
        host_port => $host_port,
        uri       => $path_query,
        headers   => {
            "host"              => $host_port,
            "content-type"      => $content_type,
            "transfer-encoding" => "chunked",
            "trailer"           => "Lambda-Runtime-Function-Error-Type, Lambda-Runtime-Function-Error-Body",
            "lambda-runtime-function-response-mode" => "streaming",
        },
        header_case => {
            "host"              => "Host",
            "content-type"      => "Content-Type",
            "transfer-encoding" => "Transfer-Encoding",
            "trailer"           => "Trailer",
            "lambda-runtime-function-response-mode" => "Lambda-Runtime-Function-Response-Mode",
        },
    };
    my $peer = $host;

    # We remove the cached handle so it is not reused in the case of redirect.
    # reuse for the same scheme, host and port
    my $handle = delete $http->{handle};

t/20_psgi.t  view on Meta::CPAN

    is $req->query_string, 'query=hoge&query=fuga', 'query string';
    is $req->header('Header-Name'), 'Value1, Value2', 'header';
    ok !$req->env->{'psgi.streaming'}, 'psgi.streaming';
};

subtest "API Gateway POST Request" => sub {
    my $input = slurp_json("testdata/apigateway-post-request.json");
    my $output = $app->format_input($input);
    my $req = Plack::Request->new($output);
    is $req->method, 'POST', 'method';
    is $req->content_type, 'application/json', 'content-type';
    my $content;
    warning_is { $content = slurp_fh($req->input) } undef, 'no warning';
    is $content, '{"hello":"こんにちは世界"}', 'content';
    is $req->request_uri, '/', 'request uri';
    is $req->path_info, '/', 'path info';
    is $req->query_string, '', 'query string';
    ok !$req->env->{'psgi.streaming'}, 'psgi.streaming';
};

subtest "API Gateway Base64 encoded POST Request" => sub {
    my $input = slurp_json("testdata/apigateway-base64-request.json");
    my $output = $app->format_input($input);
    my $req = Plack::Request->new($output);
    is $req->method, 'POST', 'method';

    # You have to add 'application/octet-stream' to binary media types.
    # https://docs.aws.amazon.com/apigateway/latest/developerguide/api-gateway-payload-encodings-configure-with-console.html
    is $req->content_type, 'application/octet-stream', 'content-type';
    my $content;
    warning_is { $content = slurp_fh($req->input) } undef, 'no warning';
    is $content, '{"hello":"world"}', 'content';
    is $req->request_uri, '/', 'request uri';
    is $req->path_info, '/', 'path info';
    is $req->query_string, '', 'query string';
    ok !$req->env->{'psgi.streaming'}, 'psgi.streaming';
};

subtest "API Gateway v2 GET Request" => sub {

t/20_psgi.t  view on Meta::CPAN

    is $req->query_string, 'query=hoge&query=fuga', 'query string';
    is $req->header('Header-Name'), 'Value1, Value2', 'header';
    ok !$req->env->{'psgi.streaming'}, 'psgi.streaming';
};

subtest "ALB POST Request" => sub {
    my $input = slurp_json("testdata/alb-post-request.json");
    my $output = $app->format_input($input);
    my $req = Plack::Request->new($output);
    is $req->method, 'POST', 'method';
    is $req->content_type, 'application/json', 'content-type';
    is slurp_fh($req->input), '{"hello":"こんにちは世界"}', 'content';
    is $req->request_uri, '/', 'request uri';
    is $req->path_info, '/', 'path info';
    is $req->query_string, '', 'query string';
    ok !$req->env->{'psgi.streaming'}, 'psgi.streaming';
};

subtest "ALB POST Base64 Request" => sub {
    my $input = slurp_json("testdata/alb-base64-request.json");
    my $output = $app->format_input($input);
    my $req = Plack::Request->new($output);
    is $req->method, 'POST', 'method';
    is $req->content_type, 'application/octet-stream', 'content-type';
    is slurp_fh($req->input), '{"hello":"world"}', 'content';
    is $req->request_uri, '/foo/bar', 'request uri';
    is $req->path_info, '/foo/bar', 'path info';
    is $req->query_string, '', 'query string';
    ok !$req->env->{'psgi.streaming'}, 'psgi.streaming';
};

subtest "Function URLs GET Request" => sub {
    my $input = slurp_json("testdata/function-urls-get-request.json");
    my $output = $app->format_input($input);

t/20_psgi.t  view on Meta::CPAN

    is $req->query_string, 'parameter1=value1&parameter1=value2&parameter2=value', 'query string';
    is $req->header('header1'), 'value1,value2', 'header';
    ok !$req->env->{'psgi.streaming'}, 'psgi.streaming';
};

subtest "Function URLs POST Request" => sub {
    my $input = slurp_json("testdata/function-urls-post-request.json");
    my $output = $app->format_input($input);
    my $req = Plack::Request->new($output);
    is $req->method, 'POST', 'method';
    is $req->content_type, 'application/json', 'content-type';
    is slurp_fh($req->input), '{"hello":"world"}', 'content';
    is $req->request_uri, '/my/path', 'request uri';
    is $req->path_info, '/my/path', 'path info';
    is $req->query_string, '', 'query string';
    ok !$req->env->{'psgi.streaming'}, 'psgi.streaming';
};

subtest "Function URLs POST Base64 Request" => sub {
    my $input = slurp_json("testdata/function-urls-post-base64-request.json");
    my $output = $app->format_input($input);
    my $req = Plack::Request->new($output);
    is $req->method, 'POST', 'method';
    is $req->content_type, 'application/octet-stream', 'content-type';
    is slurp_fh($req->input), '{"hello":"world"}', 'content';
    is $req->request_uri, '/my/path', 'request uri';
    is $req->path_info, '/my/path', 'path info';
    is $req->query_string, '', 'query string';
    ok !$req->env->{'psgi.streaming'}, 'psgi.streaming';
};

subtest "plain text response" => sub {
    my $response = [
        200,

t/20_psgi.t  view on Meta::CPAN

        [
            "Hello",
            "World",
            encode_utf8("こんにちは世界"),
        ]
    ];
    my $res = $app->format_output($response);
    cmp_deeply $res, {
        isBase64Encoded => bool 0,
        headers => {
            'content-type' => 'text/plain',
            'header-name' => 'value2',
        },
        multiValueHeaders => {
            'content-type' => ['text/plain'],
            'header-name' => ['value1', 'value2'],
        },
        statusCode => 200,
        body => "HelloWorldこんにちは世界",
    };
    diag encode_json $res;
};

subtest "binary response" => sub {
    my $response = [

t/20_psgi.t  view on Meta::CPAN

            'Content-Type' => 'application/octet-stream',
        ],
        [
            '{"hello":"world"}',
        ]
    ];
    my $res = $app->format_output($response);
    cmp_deeply $res, {
        isBase64Encoded => bool 1,
        headers => {
            'content-type' => 'application/octet-stream',
        },
        multiValueHeaders => {
            'content-type' => ['application/octet-stream'],
        },
        statusCode => 200,
        body => "eyJoZWxsbyI6IndvcmxkIn0=",
    };
    diag encode_json $res;
};

subtest "IO::Handle-like response" => sub {
    open my $f, "<", \"HelloWorld";
    my $response = [
        200,
        [
            'Content-Type' => 'text/plain',
        ],
        $f,
    ];
    my $res = $app->format_output($response);
    cmp_deeply $res, {
        isBase64Encoded => bool 0,
        headers => {
            'content-type' => 'text/plain',
        },
        multiValueHeaders => {
            'content-type' => ['text/plain'],
        },
        statusCode => 200,
        body => "HelloWorld",
    };
    diag encode_json $res;
};

subtest "EUC-JP encoded response" => sub {
    my $response = [
        200,

t/20_psgi.t  view on Meta::CPAN

            'Content-Type' => 'text/plain',
        ],
        [
            "\xC8\xFE\xC6\xFD",
        ]
    ];
    my $res = $app->format_output($response);
    cmp_deeply $res, {
        isBase64Encoded => bool 1,
        headers => {
            'content-type' => 'text/plain',
        },
        multiValueHeaders => {
            'content-type' => ['text/plain'],
        },
        statusCode => 200,
        body => "yP7G/Q==",
    };
    diag encode_json $res;
};

subtest "query string enconding" => sub {
    my $input = slurp_json("testdata/apigateway-get-request.json");
    $input->{queryStringParameters} = {

t/21_psgi_response_streaming.t  view on Meta::CPAN

        open my $fh, ">", \$buf or die "failed to open: $!";
        return $fh;
    };

    $res->($responder);
    is $content_type, "application/vnd.awslambda.http-integration-response", "content type";
    my ($prelude, $body) = split /\x00\x00\x00\x00\x00\x00\x00\x00/, $buf;
    cmp_deeply decode_json($prelude), {
        statusCode => 200,
        headers => {
            "content-type" => "text/plain",
        },
        cookies => [],
    }, "prelude";
    is $body, "hello", "body";
};

subtest "IO::Handle-like object" => sub {
    my $content_type = undef;
    open my $fh, "<", \"hello" or die "failed to open: $!";
    my $buf = "";

t/21_psgi_response_streaming.t  view on Meta::CPAN

        open my $fh, ">", \$buf or die "failed to open: $!";
        return $fh;
    };

    $res->($responder);
    is $content_type, "application/vnd.awslambda.http-integration-response", "content type";
    my ($prelude, $body) = split /\x00\x00\x00\x00\x00\x00\x00\x00/, $buf;
    cmp_deeply decode_json($prelude), {
        statusCode => 200,
        headers => {
            "content-type" => "text/plain",
        },
        cookies => [],
    }, "prelude";
    is $body, "hello", "body";
};

subtest "streaming response" => sub {
    my $content_type = undef;
    my $buf = "";
    my $res = $app->_handle_response_stream(sub {

t/21_psgi_response_streaming.t  view on Meta::CPAN

        open my $fh, ">", \$buf or die "failed to open: $!";
        return $fh;
    };

    $res->($responder);
    is $content_type, "application/vnd.awslambda.http-integration-response", "content type";
    my ($prelude, $body) = split /\x00\x00\x00\x00\x00\x00\x00\x00/, $buf;
    cmp_deeply decode_json($prelude), {
        statusCode => 200,
        headers => {
            "content-type" => "text/plain",
        },
        cookies => [],
    }, "prelude";
    is $body, "hello", "body";
};

done_testing;

t/testdata/alb-base64-request.json  view on Meta::CPAN

        "elb": {
            "targetGroupArn": "arn:aws:elasticloadbalancing:ap-northeast-1:123456789012:targetgroup/lambda-target/a8d0882e91e66540"
        }
    },
    "httpMethod": "POST",
    "path": "/foo/bar",
    "queryStringParameters": {},
    "headers": {
        "accept": "*/*",
        "content-length": "17",
        "content-type": "application/octet-stream",
        "host": "lambda-test-1234567890.ap-northeast-1.elb.amazonaws.com",
        "user-agent": "curl/7.54.0",
        "x-amzn-trace-id": "Root=1-5c0f091f-56087f0297986bc017de21bc",
        "x-forwarded-for": "192.0.2.1",
        "x-forwarded-port": "80",
        "x-forwarded-proto": "http"
    },
    "body": "eyJoZWxsbyI6IndvcmxkIn0=",
    "isBase64Encoded": true
}

t/testdata/alb-post-request.json  view on Meta::CPAN

        "elb": {
            "targetGroupArn": "arn:aws:elasticloadbalancing:ap-northeast-1:123456789012:targetgroup/lambda-target/a8d0882e91e66540"
        }
    },
    "httpMethod": "POST",
    "path": "/",
    "queryStringParameters": {},
    "headers": {
        "accept": "*/*",
        "content-length": "17",
        "content-type": "application/json",
        "host": "lambda-test-1234567890.ap-northeast-1.elb.amazonaws.com",
        "user-agent": "curl/7.54.0",
        "x-amzn-trace-id": "Root=1-5c0f08ec-8a00ddf3c79b53a01fa905ba",
        "x-forwarded-for": "192.0.2.1",
        "x-forwarded-port": "443",
        "x-forwarded-proto": "https"
    },
    "body": "{\"hello\":\"こんにちは世界\"}",
    "isBase64Encoded": false
}

t/testdata/apigateway-base64-request.json  view on Meta::CPAN

{
    "resource": "/",
    "path": "/",
    "httpMethod": "POST",
    "headers": {
        "accept": "*/*",
        "content-type": "application/octet-stream",
        "Host": "xxxxxxxxxx.execute-api.ap-northeast-1.amazonaws.com",
        "User-Agent": "curl/7.54.0",
        "X-Amzn-Trace-Id": "Root=1-5c0f0502-7611e42628aee5c8cce21a72",
        "X-Forwarded-For": "192.0.2.1",
        "X-Forwarded-Port": "443",
        "X-Forwarded-Proto": "https"
    },
    "multiValueHeaders": {
        "accept": [
            "*/*"
        ],
        "content-type": [
            "application/octet-stream"
        ],
        "Host": [
            "xxxxxxxxxx.execute-api.ap-northeast-1.amazonaws.com"
        ],
        "User-Agent": [
            "curl/7.54.0"
        ],
        "X-Amzn-Trace-Id": [
            "Root=1-5c0f0502-7611e42628aee5c8cce21a72"

t/testdata/apigateway-post-request.json  view on Meta::CPAN

{
    "resource": "/",
    "path": "/",
    "httpMethod": "POST",
    "headers": {
        "accept": "*/*",
        "content-type": "application/json",
        "Host": "xxxxxxxxxx.execute-api.ap-northeast-1.amazonaws.com",
        "User-Agent": "curl/7.54.0",
        "X-Amzn-Trace-Id": "Root=1-5c0f033a-32eff4c8b894f0d4f8fa213c",
        "X-Forwarded-For": "192.0.2.1",
        "X-Forwarded-Port": "443",
        "X-Forwarded-Proto": "https"
    },
    "multiValueHeaders": {
        "accept": [
            "*/*"
        ],
        "content-type": [
            "application/json"
        ],
        "Host": [
            "xxxxxxxxxx.execute-api.ap-northeast-1.amazonaws.com"
        ],
        "User-Agent": [
            "curl/7.54.0"
        ],
        "X-Amzn-Trace-Id": [
            "Root=1-5c0f033a-32eff4c8b894f0d4f8fa213c"

t/testdata/apigateway-v2-base64-request.json  view on Meta::CPAN

{
    "version": "2.0",
    "routeKey": "$default",
    "rawPath": "/my/path",
    "rawQueryString": "",
    "headers": {
        "accept": "*/*",
        "content-length": "17",
        "content-type": "application/octet-stream",
        "host": "xxxxxxxxxx.execute-api.ap-northeast-1.amazonaws.com",
        "user-agent": "curl/7.64.1",
        "x-amzn-trace-id": "Root=1-5e8a9b8c-5fd89a5053994a40c1343990",
        "x-forwarded-for":  "192.0.2.1",
        "x-forwarded-port": "443",
        "x-forwarded-proto": "https"
    },
    "requestContext": {
        "accountId": "123456789012",
        "apiId": "xxxxxxxxxx",

t/testdata/apigateway-v2-post-request.json  view on Meta::CPAN

{
    "version": "2.0",
    "routeKey": "$default",
    "rawPath": "/my/path",
    "rawQueryString": "",
    "headers": {
        "accept": "*/*",
        "content-length": "17",
        "content-type": "application/json",
        "host": "xxxxxxxxxx.execute-api.ap-northeast-1.amazonaws.com",
        "user-agent": "curl/7.64.1",
        "x-amzn-trace-id": "Root=1-5e8a9af6-43dfdbb5896d7684515eb0ba",
        "x-forwarded-for": "192.0.2.1",
        "x-forwarded-port": "443",
        "x-forwarded-proto": "https"
    },
    "requestContext": {
        "accountId": "123456789012",
        "apiId": "xxxxxxxxxx",

t/testdata/function-urls-post-base64-request.json  view on Meta::CPAN

{
    "body": "eyJoZWxsbyI6IndvcmxkIn0=",
    "headers": {
        "accept": "*/*",
        "content-type": "application/octet-stream",
        "host": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx.lambda-url.ap-northeast-1.on.aws",
        "user-agent": "curl/7.79.1",
        "x-amzn-trace-id": "Root=1-62577702-3f59d86f1830be3f76a10c1e",
        "x-forwarded-for": "192.0.2.1",
        "x-forwarded-port": "443",
        "x-forwarded-proto": "https"
    },
    "isBase64Encoded": true,
    "rawPath": "/my/path",
    "rawQueryString": "",

t/testdata/function-urls-post-request.json  view on Meta::CPAN

{
    "body": "{\"hello\":\"world\"}",
    "headers": {
        "accept": "*/*",
        "content-type": "application/json",
        "host": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx.lambda-url.ap-northeast-1.on.aws",
        "user-agent": "curl/7.79.1",
        "x-amzn-trace-id": "Root=1-62577683-4e8059702ee4af274153a8a9",
        "x-forwarded-for": "192.0.2.1",
        "x-forwarded-port": "443",
        "x-forwarded-proto": "https"
    },
    "isBase64Encoded": false,
    "rawPath": "/my/path",
    "rawQueryString": "",



( run in 2.157 seconds using v1.01-cache-2.11-cpan-df04353d9ac )