AWS-Lambda
view release on metacpan or search on metacpan
lib/AWS/Lambda/PSGI.pm view on Meta::CPAN
if (my $context = $payload->{requestContext}) {
if ($context->{elb}) {
# Application Load Balancer https://docs.aws.amazon.com/elasticloadbalancing/latest/application/lambda-functions.html
return $self->_format_input_v1($payload, $ctx);
}
}
if (my $version = $payload->{version}) {
if ($version =~ /^1[.]/) {
# API Gateway for REST https://docs.aws.amazon.com/apigateway/latest/developerguide/set-up-lambda-proxy-integrations.html
return $self->_format_input_v1($payload, $ctx);
}
if ($version =~ /^2[.]/) {
# API Gateway for HTTP https://docs.aws.amazon.com/apigateway/latest/developerguide/http-api-develop-integrations-lambda.html
return $self->_format_input_v2($payload, $ctx);
}
}
return $self->_format_input_v1($payload, $ctx);
}
sub _format_input_v1 {
my ($self, $payload, $ctx) = @_;
my $env = {};
# merge queryStringParameters and multiValueQueryStringParameters
my $query = {
%{$payload->{queryStringParameters} // {}},
%{$payload->{multiValueQueryStringParameters} // {}},
};
my @params;
while (my ($key, $value) = each %$query) {
if (ref($value) eq 'ARRAY') {
for my $v (@$value) {
push @params, "$key=$v";
}
} else {
push @params, "$key=$value";
}
}
$env->{QUERY_STRING} = join '&', @params;
# merge headers and multiValueHeaders
my $headers = {
%{$payload->{headers} // {}},
%{$payload->{multiValueHeaders} // {}},
};
while (my ($key, $value) = each %$headers) {
$key =~ s/-/_/g;
$key = uc $key;
if ($key !~ /^(?:CONTENT_LENGTH|CONTENT_TYPE)$/) {
$key = "HTTP_$key";
}
if (ref $value eq "ARRAY") {
$value = join ", ", @$value;
}
$env->{$key} = $value;
}
$env->{'psgi.version'} = [1, 1];
$env->{'psgi.errors'} = *STDERR;
$env->{'psgi.run_once'} = Plack::Util::FALSE;
$env->{'psgi.multithread'} = Plack::Util::FALSE;
$env->{'psgi.multiprocess'} = Plack::Util::FALSE;
$env->{'psgi.streaming'} = Plack::Util::FALSE;
$env->{'psgi.nonblocking'} = Plack::Util::FALSE;
$env->{'psgix.harakiri'} = Plack::Util::TRUE;
$env->{'psgix.input.buffered'} = Plack::Util::TRUE;
# inject the request id that compatible with Plack::Middleware::RequestId
if ($ctx) {
$env->{'psgix.request_id'} = $ctx->aws_request_id;
$env->{'HTTP_X_REQUEST_ID'} = $ctx->aws_request_id;
}
my $body = encode_utf8($payload->{body} // '');
if ($payload->{isBase64Encoded}) {
$body = decode_base64 $body;
}
open my $input, "<", \$body;
$env->{REQUEST_METHOD} = $payload->{httpMethod};
$env->{'psgi.input'} = $input;
$env->{CONTENT_LENGTH} //= bytes::length($body);
$env->{REQUEST_URI} = $payload->{path};
if ($env->{QUERY_STRING}) {
$env->{REQUEST_URI} .= '?' . $env->{QUERY_STRING};
}
$env->{PATH_INFO} = URI::Escape::uri_unescape($payload->{path});
$env->{SCRIPT_NAME} = '';
my $requestContext = $payload->{requestContext};
if ($requestContext) {
my $path = $requestContext->{path};
my $stage = $requestContext->{stage};
if ($stage && $path && $path ne $payload->{path}) {
$env->{SCRIPT_NAME} = "/$stage";
}
}
return $env;
}
sub _format_input_v2 {
my ($self, $payload, $ctx) = @_;
my $env = {};
$env->{QUERY_STRING} = $payload->{rawQueryString};
my $headers = $payload->{headers} // {};
while (my ($key, $value) = each %$headers) {
$key =~ s/-/_/g;
$key = uc $key;
if ($key !~ /^(?:CONTENT_LENGTH|CONTENT_TYPE)$/) {
$key = "HTTP_$key";
}
$env->{$key} = $value;
}
$env->{'psgi.version'} = [1, 1];
$env->{'psgi.errors'} = *STDERR;
$env->{'psgi.run_once'} = Plack::Util::FALSE;
$env->{'psgi.multithread'} = Plack::Util::FALSE;
$env->{'psgi.multiprocess'} = Plack::Util::FALSE;
$env->{'psgi.streaming'} = Plack::Util::FALSE;
$env->{'psgi.nonblocking'} = Plack::Util::FALSE;
$env->{'psgix.harakiri'} = Plack::Util::TRUE;
$env->{'psgix.input.buffered'} = Plack::Util::TRUE;
# inject the request id that compatible with Plack::Middleware::RequestId
if ($ctx) {
$env->{'psgix.request_id'} = $ctx->aws_request_id;
$env->{'HTTP_X_REQUEST_ID'} = $ctx->aws_request_id;
}
my $body = encode_utf8($payload->{body} // '');
if ($payload->{isBase64Encoded}) {
$body = decode_base64 $body;
}
open my $input, "<", \$body;
$env->{'psgi.input'} = $input;
$env->{CONTENT_LENGTH} //= bytes::length($body);
my $requestContext = $payload->{requestContext};
$env->{REQUEST_METHOD} = $requestContext->{http}{method};
$env->{REQUEST_URI} = $payload->{rawPath};
if ($env->{QUERY_STRING}) {
$env->{REQUEST_URI} .= '?' . $env->{QUERY_STRING};
}
$env->{PATH_INFO} = $requestContext->{http}{path};
$env->{SCRIPT_NAME} = '';
return $env;
}
sub format_output {
my ($self, $response) = @_;
my ($status, $headers, $body) = @$response;
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.
( run in 2.030 seconds using v1.01-cache-2.11-cpan-39bf76dae61 )