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

META.json  view on Meta::CPAN

         "file" : "lib/AWS/Lambda/ResponseWriter.pm"
      }
   },
   "release_status" : "stable",
   "resources" : {
      "bugtracker" : {
         "web" : "https://github.com/shogo82148/p5-aws-lambda/issues"
      },
      "homepage" : "https://github.com/shogo82148/p5-aws-lambda",
      "repository" : {
         "type" : "git",
         "url" : "https://github.com/shogo82148/p5-aws-lambda.git",
         "web" : "https://github.com/shogo82148/p5-aws-lambda"
      }
   },
   "version" : "0.9.0",
   "x_contributors" : [
      "ICHINOSE Shogo <shogo82148@gmail.com>",
      "Jose Luis Martinez <jlmartinez@capside.com>",
      "Mohammad S Anwar <mohammad.anwar@yahoo.com>",
      "dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>",

README.md  view on Meta::CPAN


    perl -MAWS::Lambda -e 'AWS::Lambda::print_runtime_arn_al2023("5.38", "us-east-1")'
    perl -MAWS::Lambda -e 'AWS::Lambda::print_paws_arn_al2023("5.38", "us-east-1")'

The list of all layer ARNs is available on [AWS::Lambda::AL2023](https://metacpan.org/pod/AWS%3A%3ALambda%3A%3AAL2023).

## Use Pre-built Zip Archives

1. Login to your AWS Account and go to the Lambda Console.
2. Create a new layer and give it a name.
3. For the "Code entry type" selection, select **Upload a file from Amazon S3**.
4. In the "License" section, input [https://github.com/shogo82148/p5-aws-lambda/blob/main/LICENSE](https://github.com/shogo82148/p5-aws-lambda/blob/main/LICENSE).
5. Click **Create** button.
6. Use the layer created. For detail, see Use Prebuilt Public Lambda Layer section.

URLs for Zip archives are here.

`https://shogo82148-lambda-perl-runtime-$REGION.s3.amazonaws.com/perl-$VERSION-runtime-al2023-$ARCHITECTURE.zip`

## Use Pre-built Docker Images

author/build-paws-al2023.sh  view on Meta::CPAN

    ARCH=arm64;;
  *)
    echo "unknown architecture: $(uname -m)"
esac

cd /opt
unzip "/var/task/.perl-layer/dist/perl-$TAG-runtime-al2023-$ARCH.zip"
/opt/bin/cpanm --notest --no-man-pages "Paws@$PAWS_VERSION"

# remove pods
find /opt/lib/perl5/site_perl -type f -a -name '*.pod' -delete

author/build-paws-layer-al2023.sh  view on Meta::CPAN

esac

docker run \
    -v "$ROOT:/var/task" \
    -v "$OPT-$PLATFORM/lib/perl5/site_perl:/opt/lib/perl5/site_perl" \
    -v "$OPT-$PLATFORM/lib:/opt-lib" \
    --platform "$DOCKER_PLATFORM" \
    "public.ecr.aws/sam/build-provided.al2023:1-$PLATFORM" \
    ./author/build-paws-al2023.sh "$TAG"

find "$OPT-$PLATFORM" -type f -a -name '*.pm' -print0 | parallel -0 -j 32 "$ROOT/author/perlstrip.sh"

cd "$OPT-$PLATFORM"
mkdir -p "$DIST"
zip -9 -r "$DIST/perl-$TAG-paws-al2023-$PLATFORM.zip" .

author/perl-stripper/perl-stripper/cpanfile.snapshot  view on Meta::CPAN

      PPI::Token::Magic 1.276
      PPI::Token::Number 1.276
      PPI::Token::Number::Binary 1.276
      PPI::Token::Number::Exp 1.276
      PPI::Token::Number::Float 1.276
      PPI::Token::Number::Hex 1.276
      PPI::Token::Number::Octal 1.276
      PPI::Token::Number::Version 1.276
      PPI::Token::Operator 1.276
      PPI::Token::Pod 1.276
      PPI::Token::Prototype 1.276
      PPI::Token::Quote 1.276
      PPI::Token::Quote::Double 1.276
      PPI::Token::Quote::Interpolate 1.276
      PPI::Token::Quote::Literal 1.276
      PPI::Token::Quote::Single 1.276
      PPI::Token::QuoteLike 1.276
      PPI::Token::QuoteLike::Backtick 1.276
      PPI::Token::QuoteLike::Command 1.276
      PPI::Token::QuoteLike::Readline 1.276
      PPI::Token::QuoteLike::Regexp 1.276

author/perl-stripper/perl-stripper/handler.pl  view on Meta::CPAN

);

my $app = sub {
    my $env = shift;
    my $req = Plack::Request->new($env);

    my $code = do { local $/; my $body = $req->body; <$body> };
    my $stripped = $stripper->strip($code);

    my $res = $req->new_response(200);
    $res->content_type('text/plain');
    $res->body($stripped);
    return $res->finalize;
};

my $func = AWS::Lambda::PSGI->wrap($app);

sub handle($payload, $context) {
    return $func->($payload);
}

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");

author/update-aws-lambda-al.pl  view on Meta::CPAN

chomp(my $module_version = `cat $FindBin::Bin/../META.json | jq -r .version`);
my $latest_perl = $versions->[0];
my $latest_perl_layer = $latest_perl =~ s/[.]/-/r;
my $latest_runtime_arn = $layers->{$latest_perl}{'us-east-1'}{runtime_arn};
my $latest_runtime_version = $layers->{$latest_perl}{'us-east-1'}{runtime_version};
my $latest_paws_arn = $layers->{$latest_perl}{'us-east-1'}{paws_arn};
my $latest_paws_version = $layers->{$latest_perl}{'us-east-1'}{paws_version};

open my $fh, '>', "$FindBin::Bin/../lib/AWS/Lambda/AL.pm" or die "$!";

sub printfh :prototype($) {
    my $contents = shift;
    $contents =~ s/\@\@VERSION\@\@/$module_version/g;
    $contents =~ s/\@\@LATEST_PERL\@\@/$latest_perl/g;
    $contents =~ s/\@\@LATEST_PERL_LAYER\@\@/$latest_perl_layer/g;
    $contents =~ s/\@\@LATEST_RUNTIME_ARN\@\@/$latest_runtime_arn/g;
    $contents =~ s/\@\@LATEST_RUNTIME_VERSION\@\@/$latest_runtime_version/g;
    $contents =~ s/\@\@LATEST_PAWS_ARN\@\@/$latest_paws_arn/g;
    $contents =~ s/\@\@LATEST_PAWS_VERSION\@\@/$latest_paws_version/g;
    print $fh $contents;
}

author/update-aws-lambda-al2.pl  view on Meta::CPAN

chomp(my $module_version = `cat $FindBin::Bin/../META.json | jq -r .version`);
my $latest_perl = $versions_al2->[0];
my $latest_perl_layer = $latest_perl =~ s/[.]/-/r;
my $latest_runtime_arn = $layers_al2->{$latest_perl}{'us-east-1'}{x86_64}{runtime_arn};
my $latest_runtime_version = $layers_al2->{$latest_perl}{'us-east-1'}{x86_64}{runtime_version};
my $latest_paws_arn = $layers_al2->{$latest_perl}{'us-east-1'}{x86_64}{paws_arn};
my $latest_paws_version = $layers_al2->{$latest_perl}{'us-east-1'}{x86_64}{paws_version};

open my $fh, '>', "$FindBin::Bin/../lib/AWS/Lambda/AL2.pm" or die "$!";

sub printfh :prototype($) {
    my $contents = shift;
    $contents =~ s/\@\@VERSION\@\@/$module_version/g;
    $contents =~ s/\@\@LATEST_PERL\@\@/$latest_perl/g;
    $contents =~ s/\@\@LATEST_PERL_LAYER\@\@/$latest_perl_layer/g;
    $contents =~ s/\@\@LATEST_RUNTIME_ARN\@\@/$latest_runtime_arn/g;
    $contents =~ s/\@\@LATEST_RUNTIME_VERSION\@\@/$latest_runtime_version/g;
    $contents =~ s/\@\@LATEST_PAWS_ARN\@\@/$latest_paws_arn/g;
    $contents =~ s/\@\@LATEST_PAWS_VERSION\@\@/$latest_paws_version/g;
    print $fh $contents;
}

author/update-aws-lambda-al2023.pl  view on Meta::CPAN

chomp(my $module_version = `cat $FindBin::Bin/../META.json | jq -r .version`);
my $latest_perl = $versions_al2023->[0];
my $latest_perl_layer = $latest_perl =~ s/[.]/-/r;
my $latest_runtime_arn = $layers_al2023->{$latest_perl}{'us-east-1'}{x86_64}{runtime_arn};
my $latest_runtime_version = $layers_al2023->{$latest_perl}{'us-east-1'}{x86_64}{runtime_version};
my $latest_paws_arn = $layers_al2023->{$latest_perl}{'us-east-1'}{x86_64}{paws_arn};
my $latest_paws_version = $layers_al2023->{$latest_perl}{'us-east-1'}{x86_64}{paws_version};

open my $fh, '>', "$FindBin::Bin/../lib/AWS/Lambda/AL2023.pm" or die "$!";

sub printfh :prototype($) {
    my $contents = shift;
    $contents =~ s/\@\@VERSION\@\@/$module_version/g;
    $contents =~ s/\@\@LATEST_PERL\@\@/$latest_perl/g;
    $contents =~ s/\@\@LATEST_PERL_LAYER\@\@/$latest_perl_layer/g;
    $contents =~ s/\@\@LATEST_RUNTIME_ARN\@\@/$latest_runtime_arn/g;
    $contents =~ s/\@\@LATEST_RUNTIME_VERSION\@\@/$latest_runtime_version/g;
    $contents =~ s/\@\@LATEST_PAWS_ARN\@\@/$latest_paws_arn/g;
    $contents =~ s/\@\@LATEST_PAWS_VERSION\@\@/$latest_paws_version/g;
    print $fh $contents;
}

examples/cgi/WwwCounter/readme.html  view on Meta::CPAN

<html>
<head>
<meta http-equiv="Content-type" content="text/html; charset=Shift_JIS">
<title>WwwCounter Ver3.15利用メモ - とほほのWWW入門</title>
<style type="text/css">
<!--
BODY {
    color: #333333;
}
H2 {
    width: 100%;
    font-weight: bold;
    color: #ffffff;
    background-color: #8484ee;
    border: 1px none #8888f8;

examples/cgi/WwwCounter/sample.html  view on Meta::CPAN

<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>WwwCounterサンプル - とほほのWWW入門</title>
<meta name="viewport" content="width=device-width,initial-scale=1.0">
<style>
.i { margin-left: 1rem; }
</style>
</head>
<body>

examples/cgi/WwwCounter/style3.css  view on Meta::CPAN

 */

/* Font */
body {
  /* font-family: verdana, 'Meiryo UI', 'メイリオ', 'MS Pゴシック', 'ヒラギノ角ゴシック', sans-selif; */
  font-family: 'Meiryo UI', 'メイリオ', 'MS Pゴシック', 'ヒラギノ角ゴシック', sans-selif;
}
pre, code {
  font-family: Consolas, 'Meiryo UI', 'メイリオ', 'MS Pゴシック', 'ヒラギノ角ゴシック', sans-selif;
}
.teletype, .tt {
  font-family: Consolas, 'Courier New', 'MS ゴシック', monospace;
}
.notbold, .nb {
  font-weight: normal;
}
.meiryo, .mr {
  font-family: 'Meiryo UI';
}

/* Color */

examples/cgi/WwwCounter/style3.css  view on Meta::CPAN

}
.mt1 {
  margin-top: 1em;
}
.mb1 {
  margin-bottom: 1em;
}

/* List */
.nomark {
  list-style-type: none;
  margin-top: 0;
  margin-left: -1em;
}
.dl1 dt {
  margin-top: 1em;
  font-weight: bold;
}
.dl1 dd {
  margin-left: 1em;
}

examples/cgi/WwwCounter/wwwcount.cgi  view on Meta::CPAN


	# カウンター文字列(例:000123)を得る
	if ($g_figure != 0) {
		$count_str = sprintf(sprintf("%%0%dld", $g_figure), $count);
	} else {
		$count_str = sprintf("%ld", $count);
	}

	# テキストモード
	if ($g_mode eq "text") {
		printf("Content-type: text/html\n");
		printf("\n");
		printf("$count_str\n");

	# GIFモード
	} elsif ($g_mode eq "gif") {
		printf("Content-type: image/gif\n");
		printf("\n");
		@files = ();
		for (my $i = 0; $i < length($count_str); $i++) {
			$n = substr($count_str, $i, 1);
			push(@files, "$n.gif");
		}
		require "./gifcat.pl";
		binmode(STDOUT);
		print gifcat'gifcat(@files);

	# 隠しカウンターモード
	} elsif ($g_mode eq "hide") {
		printf("Content-type: image/gif\n");
		printf("\n");
		$size = -s $g_gif_file;
		open(IN, $g_gif_file);
		binmode(IN);
		binmode(STDOUT);
		read(IN, $buf, $size);
		print $buf;
		close(IN);
	}
}

examples/cgi/WwwCounter/wwwcount.cgi  view on Meta::CPAN

sub unlockLock {
	if ($g_lock_flag) {
		rmdir($g_file_lock);
	}
}

#
# CGIが使用できるかテストを行う。
#
sub test {
	print "Content-type: text/html\n";
	print "\n";
	print "<!doctype html>\n";
	print "<html>\n";
	print "<head>\n";
	print "<meta charset='utf-8'>\n";
    print "<title>Test</title>\n";
    print "</head>\n";
	print "<body>\n";
	print "<p>OK. CGIスクリプトは正常に動いています。</p>\n";
	if ($g_mailto ne "") {
		if (! -f $g_sendmail) {
			print "<p>ERROR: $g_sendmail が存在しません。</p>\n";

examples/s3-get-object/handler.pl  view on Meta::CPAN

use 5.30.0;
use lib "$ENV{'LAMBDA_TASK_ROOT'}/extlocal/lib/perl5";
use Paws;
use Try::Tiny;
use URI::Escape;

my $obj = Paws->service('S3', region => 'ap-northeast-1');

sub handle {
    my $payload = shift;
    # Get the object from the event and show its content type
    my $bucket = $payload->{Records}[0]{s3}{bucket}{name};
    my $key = uri_unescape($payload->{Records}[0]{s3}{object}{key} =~ s/\+/ /gr);
    my $resp = try {
        $obj->GetObject(
            Bucket => $bucket,
            Key    => $key,
        );
    } catch {
        print STDERR "$_\n";
        my $message = "Error getting object $key from bucket $bucket. Make sure they exist and your bucket is in the same region as this function.";

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

=item 1

Login to your AWS Account and go to the Lambda Console.

=item 2

Create a new layer and give it a name.

=item 3

For the "Code entry type" selection, select B<Upload a file from Amazon S3>.

=item 4

In the "License" section, input L<https://github.com/shogo82148/p5-aws-lambda/blob/main/LICENSE>.

=item 5

Click B<Create> button.

=item 6

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

sub lambda_response_streaming {
    my $self = shift;
    my ($response, $context) = @_;
    my $runtime_api = $self->{runtime_api};
    my $api_version = $self->{api_version};
    my $request_id = $context->aws_request_id;
    my $url = "http://${runtime_api}/${api_version}/runtime/invocation/${request_id}/response";
    my $writer = undef;
    try {
        $response->(sub {
            my $content_type = shift;
            $writer = AWS::Lambda::ResponseWriter->new(
                response_url => $url,
                http         => $self->{http},
            );
            $writer->_request($content_type);
            return $writer;
        });
    } catch {
        my $err = $_;
        print STDERR "$err";
        if ($writer) {
            $writer->_close_with_error($err);
        } else {
            $self->lambda_error($err, $context);
        }

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

    }
}

sub lambda_error {
    my $self = shift;
    my ($error, $context) = @_;
    my $runtime_api = $self->{runtime_api};
    my $api_version = $self->{api_version};
    my $request_id = $context->aws_request_id;
    my $url = "http://${runtime_api}/${api_version}/runtime/invocation/${request_id}/error";
    my $type = blessed($error) // "Error";
    my $resp = $self->{http}->post($url, {
        content => encode_json({
            errorMessage => "$error",
            errorType => "$type",
        }),
    });
    if (!$resp->{success}) {
        die "failed to send error of execution: $resp->{status} $resp->{reason}";
    }
}

sub lambda_init_error {
    my $self = shift;
    my $error = shift;
    my $runtime_api = $self->{runtime_api};
    my $api_version = $self->{api_version};
    my $url = "http://${runtime_api}/${api_version}/runtime/init/error";
    my $type = blessed($error) // "Error";
    my $resp = $self->{http}->post($url, {
        content => encode_json({
            errorMessage => "$error",
            errorType => "$type",
        }),
    });
    if (!$resp->{success}) {
        die "failed to send error of execution: $resp->{status} $resp->{reason}";
    }
}

1;
__END__

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

use warnings;
use URI::Escape;
use Plack::Util;
use bytes ();
use MIME::Base64;
use JSON::Types;
use Encode;
use Try::Tiny;
use Plack::Middleware::ReverseProxy;
use AWS::Lambda;
use Scalar::Util qw(reftype);
use JSON::XS qw(encode_json);

sub new {
    my $proto = shift;
    my $class = ref $proto || $proto;
 
    my $self;
    if (@_ == 1 && ref $_[0] eq 'HASH') {
        $self = bless {%{$_[0]}}, $class;
    } else {

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


    my $singleValueHeaders = {};
    my $multiValueHeaders = {};
    Plack::Util::header_iter($headers, sub {
        my ($k, $v) = @_;
        $singleValueHeaders->{lc $k} = string $v;
        push @{$multiValueHeaders->{lc $k} //= []}, string $v;
    });

    my $content = '';
    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;
            encode_base64 $content, '';

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

        isBase64Encoded => bool $isBase64Encoded,
        headers => $singleValueHeaders,
        multiValueHeaders => $multiValueHeaders,
        statusCode => number $status,
        body => string $content,
    }
}

sub _handle_response_stream {
    my ($self, $response) = @_;
    if (reftype($response) ne "CODE") {
        my $orig = $response;
        $response = sub {
            my $responder = shift;
            $responder->($orig);
        };
    }

    return sub {
        my $lambda_responder = shift;
        my $psgi_responder = sub {

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

            my $writer = $lambda_responder->("application/vnd.awslambda.http-integration-response");
            my $prelude = encode_json($self->_format_response_stream($status, $headers));
            $prelude .= "\x00\x00\x00\x00\x00\x00\x00\x00";
            $writer->write($prelude) or die "failed to write prelude: $!";

            # write the body.
            if (!defined $body) {
                # the caller will write the body.
                return $writer;
            }
            if (reftype($body) eq 'ARRAY') {
                # array-ref
                for my $chunk (@$body) {
                    $writer->write($chunk) or die "failed to write chunk: $!";
                }
            } else {
                # IO::Handle-like object
                local $/ = \4096;
                while (defined(my $chunk = $body->getline)) {
                    $writer->write($chunk) or die "failed to write chunk: $!";
                }

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

    my $class = ref $proto || $proto;
    my %args;
    if (@_ == 1 && ref $_[0] eq 'HASH') {
        %args = %{$_[0]};
    } else {
        %args = @_;
    }

    my $http = $args{http} // HTTP::Tiny->new;
    my $response_url = $args{response_url} // die '$LAMBDA_TASK_ROOT is not found';
    my $content_type = $args{content_type} // 'application/json';
    my $self = bless +{
        response_url => $response_url,
        http         => $http,
        handle       => undef,
        closed       => 0,
    }, $class;
    return $self;
}

sub _request {
    my ($self, $content_type) = @_;
    my $response_url = $self->{response_url};
    my $http = $self->{http};
    my ($scheme, $host, $port, $path_query, $auth) = $http->_split_url($response_url);
    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};

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


sub _close_with_error {
    my ($self, $error) = @_;
    if ($self->{closed}) {
        # already closed
        return;
    }
    $self->{closed} = 1;
    my $handle = $self->{handle};
    $handle->write("0\x0D\x0A");
    my $type = blessed($error) // "Error";
    return $handle->write_header_lines({
        "lambda-runtime-function-error-type" => "$type",
        "lambda-runtime-function-error-body" => encode_base64("$error", ""),
    }, {
        "lambda-runtime-function-error-type" => "Lambda-Runtime-Function-Error-Type",
        "lambda-runtime-function-error-body" => "Lambda-Runtime-Function-Error-Body",
    });
}

1;

t/15_lambda_response_streaming.t  view on Meta::CPAN

            my $env = shift;
            my $req = Plack::Request->new($env);
            my $headers = $req->headers;
            my $body = slurp($req->body);

            is $req->method, "POST", "http method is POST";
            is $headers->header('Lambda-Runtime-Function-Response-Mode'), "streaming", "streaming is enabled";
            is $body, '{"key1":"a","key2":"b","key3":"c"}', "response body is correct";

            my $res = $req->new_response(200);
            $res->content_type("application/json");
            $res->body('{}');
            $res->finalize;
        };
        $server->run($app, {
            port => $port, host => '127.0.0.1',
        });
    },
    max_wait => 10, # seconds
);

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

sub slurp_fh {
    my $fh = $_[0];
    local $/;
    my $v = <$fh>;
    defined $v ? decode_utf8($v) : '';
}

my $app = AWS::Lambda::PSGI->new;

subtest "array reference" => sub {
    my $content_type = undef;
    my $buf = "";
    my $res = $app->_handle_response_stream([200, ["Content-Type" => "text/plain"], ["hello"]]);
    my $responder = sub {
        $content_type = shift;
        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 = "";
    my $res = $app->_handle_response_stream([200, ["Content-Type" => "text/plain"], $fh]);
    my $responder = sub {
        $content_type = shift;
        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 {
        my $responder = shift;
        my $writer = $responder->([200, ["Content-Type" => "text/plain"]]);
        $writer->write("hello");
        $writer->close;
    });
    my $responder = sub {
        $content_type = shift;
        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": "",



( run in 1.383 second using v1.01-cache-2.11-cpan-bbb979687b5 )