| line | stmt | bran | cond | sub | pod | time | code | 
| 1 |  |  |  |  |  |  | package AWS::Lambda::Bootstrap; | 
| 2 | 17 |  |  | 17 |  | 2628069 | use 5.026000; | 
|  | 17 |  |  |  |  | 184 |  | 
| 3 | 17 |  |  | 17 |  | 752 | use utf8; | 
|  | 17 |  |  |  |  | 49 |  | 
|  | 17 |  |  |  |  | 140 |  | 
| 4 | 17 |  |  | 17 |  | 413 | use strict; | 
|  | 17 |  |  |  |  | 23 |  | 
|  | 17 |  |  |  |  | 311 |  | 
| 5 | 17 |  |  | 17 |  | 80 | use warnings; | 
|  | 17 |  |  |  |  | 47 |  | 
|  | 17 |  |  |  |  | 414 |  | 
| 6 | 17 |  |  | 17 |  | 12504 | use HTTP::Tiny; | 
|  | 17 |  |  |  |  | 459595 |  | 
|  | 17 |  |  |  |  | 725 |  | 
| 7 | 17 |  |  | 17 |  | 12287 | use JSON::XS qw/decode_json encode_json/; | 
|  | 17 |  |  |  |  | 92179 |  | 
|  | 17 |  |  |  |  | 1355 |  | 
| 8 | 17 |  |  | 17 |  | 6511 | use Try::Tiny; | 
|  | 17 |  |  |  |  | 26650 |  | 
|  | 17 |  |  |  |  | 931 |  | 
| 9 | 17 |  |  | 17 |  | 18502 | use AWS::Lambda; | 
|  | 17 |  |  |  |  | 163 |  | 
|  | 17 |  |  |  |  | 761 |  | 
| 10 | 17 |  |  | 17 |  | 7260 | use AWS::Lambda::Context; | 
|  | 17 |  |  |  |  | 106 |  | 
|  | 17 |  |  |  |  | 544 |  | 
| 11 | 17 |  |  | 17 |  | 7127 | use AWS::Lambda::ResponseWriter; | 
|  | 17 |  |  |  |  | 39 |  | 
|  | 17 |  |  |  |  | 574 |  | 
| 12 | 17 |  |  | 17 |  | 142 | use Scalar::Util qw(blessed); | 
|  | 17 |  |  |  |  | 34 |  | 
|  | 17 |  |  |  |  | 772 |  | 
| 13 | 17 |  |  | 17 |  | 89 | use Exporter 'import'; | 
|  | 17 |  |  |  |  | 35 |  | 
|  | 17 |  |  |  |  | 27664 |  | 
| 14 |  |  |  |  |  |  |  | 
| 15 |  |  |  |  |  |  | our @EXPORT = ('bootstrap'); | 
| 16 |  |  |  |  |  |  |  | 
| 17 |  |  |  |  |  |  | sub bootstrap { | 
| 18 | 0 |  |  | 0 |  | 0 | my $handler = shift; | 
| 19 | 0 |  |  |  |  | 0 | my $bootstrap = AWS::Lambda::Bootstrap->new( | 
| 20 |  |  |  |  |  |  | handler => $handler, | 
| 21 |  |  |  |  |  |  | ); | 
| 22 | 0 |  |  |  |  | 0 | $bootstrap->handle_events; | 
| 23 |  |  |  |  |  |  | } | 
| 24 |  |  |  |  |  |  |  | 
| 25 |  |  |  |  |  |  | sub new { | 
| 26 | 10 |  |  | 10 | 0 | 28193 | my $proto = shift; | 
| 27 | 10 |  | 33 |  |  | 528 | my $class = ref $proto || $proto; | 
| 28 | 10 |  |  |  |  | 98 | my %args; | 
| 29 | 10 | 50 | 33 |  |  | 273 | if (@_ == 1 && ref $_[0] eq 'HASH') { | 
| 30 | 0 |  |  |  |  | 0 | %args = %{$_[0]}; | 
|  | 0 |  |  |  |  | 0 |  | 
| 31 |  |  |  |  |  |  | } else { | 
| 32 | 10 |  |  |  |  | 328 | %args = @_; | 
| 33 |  |  |  |  |  |  | } | 
| 34 |  |  |  |  |  |  |  | 
| 35 | 10 |  |  |  |  | 151 | my $api_version = '2018-06-01'; | 
| 36 | 10 |  | 33 |  |  | 140 | my $env_handler = $args{handler} // $ENV{'_HANDLER'} // die '$_HANDLER is not found'; | 
|  |  |  | 0 |  |  |  |  | 
| 37 | 10 |  |  |  |  | 175 | my ($handler, $function) = split(/[.]/, $env_handler, 2); | 
| 38 | 10 |  | 33 |  |  | 160 | my $runtime_api = $args{runtime_api} // $ENV{'AWS_LAMBDA_RUNTIME_API'} // die '$AWS_LAMBDA_RUNTIME_API is not found'; | 
|  |  |  | 0 |  |  |  |  | 
| 39 | 10 |  | 33 |  |  | 148 | my $task_root = $args{task_root} // $ENV{'LAMBDA_TASK_ROOT'} // die '$LAMBDA_TASK_ROOT is not found'; | 
|  |  |  | 0 |  |  |  |  | 
| 40 | 10 |  |  |  |  | 381 | my $self = bless +{ | 
| 41 |  |  |  |  |  |  | task_root      => $task_root, | 
| 42 |  |  |  |  |  |  | handler        => $handler, | 
| 43 |  |  |  |  |  |  | function_name  => $function, | 
| 44 |  |  |  |  |  |  | runtime_api    => $runtime_api, | 
| 45 |  |  |  |  |  |  | api_version    => $api_version, | 
| 46 |  |  |  |  |  |  | next_event_url => "http://${runtime_api}/${api_version}/runtime/invocation/next", | 
| 47 |  |  |  |  |  |  | http           => HTTP::Tiny->new, | 
| 48 |  |  |  |  |  |  | }, $class; | 
| 49 | 10 |  |  |  |  | 2627 | return $self; | 
| 50 |  |  |  |  |  |  | } | 
| 51 |  |  |  |  |  |  |  | 
| 52 |  |  |  |  |  |  | sub handle_events { | 
| 53 | 0 |  |  | 0 | 0 | 0 | my $self = shift; | 
| 54 | 0 | 0 |  |  |  | 0 | $self->_init or return; | 
| 55 | 0 |  |  |  |  | 0 | while(1) { | 
| 56 | 0 |  |  |  |  | 0 | $self->handle_event; | 
| 57 |  |  |  |  |  |  | } | 
| 58 |  |  |  |  |  |  | } | 
| 59 |  |  |  |  |  |  |  | 
| 60 |  |  |  |  |  |  | sub _init { | 
| 61 | 5 |  |  | 5 |  | 12 | my $self = shift; | 
| 62 | 5 | 50 |  |  |  | 19 | if (my $func = $self->{function}) { | 
| 63 | 0 |  |  |  |  | 0 | return $func; | 
| 64 |  |  |  |  |  |  | } | 
| 65 |  |  |  |  |  |  |  | 
| 66 | 5 |  |  |  |  | 12 | my $task_root = $self->{task_root}; | 
| 67 | 5 |  |  |  |  | 13 | my $handler = $self->{handler}; | 
| 68 | 5 |  |  |  |  | 17 | my $name = $self->{function_name}; | 
| 69 |  |  |  |  |  |  | return try { | 
| 70 |  |  |  |  |  |  | package main; | 
| 71 | 5 |  |  | 5 |  | 2210 | require "${task_root}/${handler}.pl"; | 
| 72 | 4 |  | 100 |  |  | 898 | my $f = main->can($name) // die "handler $name is not found"; | 
| 73 | 3 |  |  |  |  | 19 | $self->{function} = $f; | 
| 74 |  |  |  |  |  |  | } catch { | 
| 75 | 2 |  |  | 2 |  | 63 | $self->lambda_init_error($_); | 
| 76 | 2 |  |  |  |  | 28 | $self->{function} = sub {}; | 
| 77 | 2 |  |  |  |  | 48 | undef; | 
| 78 | 5 |  |  |  |  | 42 | }; | 
| 79 |  |  |  |  |  |  | } | 
| 80 |  |  |  |  |  |  |  | 
| 81 |  |  |  |  |  |  | sub handle_event { | 
| 82 | 5 |  |  | 5 | 0 | 173 | my $self = shift; | 
| 83 | 5 | 100 |  |  |  | 29 | $self->_init or return; | 
| 84 | 3 |  |  |  |  | 99 | my ($payload, $context) = $self->lambda_next; | 
| 85 |  |  |  |  |  |  | my $response = try { | 
| 86 | 3 |  |  | 3 |  | 128 | local $AWS::Lambda::context = $context; | 
| 87 | 3 |  |  |  |  | 31 | local $ENV{_X_AMZN_TRACE_ID} = $context->{trace_id}; | 
| 88 | 3 |  |  |  |  | 13 | $self->{function}->($payload, $context); | 
| 89 |  |  |  |  |  |  | } catch { | 
| 90 | 1 |  |  | 1 |  | 26 | my $err = $_; | 
| 91 | 1 |  |  |  |  | 66 | print STDERR "$err"; | 
| 92 | 1 |  |  |  |  | 8 | $self->lambda_error($err, $context); | 
| 93 | 1 |  |  |  |  | 22 | bless {}, 'AWS::Lambda::ErrorSentinel'; | 
| 94 | 3 |  |  |  |  | 57 | }; | 
| 95 | 3 |  |  |  |  | 71 | my $ref = ref($response); | 
| 96 | 3 | 100 |  |  |  | 13 | if ($ref eq 'AWS::Lambda::ErrorSentinel') { | 
| 97 | 1 |  |  |  |  | 11 | return; | 
| 98 |  |  |  |  |  |  | } | 
| 99 | 2 | 100 |  |  |  | 8 | if ($ref eq 'CODE') { | 
| 100 | 1 |  |  |  |  | 4 | $self->lambda_response_streaming($response, $context); | 
| 101 |  |  |  |  |  |  | } else { | 
| 102 | 1 |  |  |  |  | 4 | $self->lambda_response($response, $context); | 
| 103 |  |  |  |  |  |  | } | 
| 104 | 2 |  |  |  |  | 26 | return 1; | 
| 105 |  |  |  |  |  |  | } | 
| 106 |  |  |  |  |  |  |  | 
| 107 |  |  |  |  |  |  | sub lambda_next { | 
| 108 | 1 |  |  | 1 | 0 | 17 | my $self = shift; | 
| 109 | 1 |  |  |  |  | 205 | my $resp = $self->{http}->get($self->{next_event_url}); | 
| 110 | 1 | 50 |  |  |  | 7290 | if (!$resp->{success}) { | 
| 111 | 0 |  |  |  |  | 0 | die "failed to retrieve the next event: $resp->{status} $resp->{reason}"; | 
| 112 |  |  |  |  |  |  | } | 
| 113 | 1 |  |  |  |  | 3 | my $h = $resp->{headers}; | 
| 114 | 1 |  |  |  |  | 25 | my $payload = decode_json($resp->{content}); | 
| 115 |  |  |  |  |  |  | return $payload, AWS::Lambda::Context->new( | 
| 116 |  |  |  |  |  |  | deadline_ms          => $h->{'lambda-runtime-deadline-ms'}, | 
| 117 |  |  |  |  |  |  | aws_request_id       => $h->{'lambda-runtime-aws-request-id'}, | 
| 118 |  |  |  |  |  |  | invoked_function_arn => $h->{'lambda-runtime-invoked-function-arn'}, | 
| 119 | 1 |  |  |  |  | 46 | trace_id             => $h->{'lambda-runtime-trace-id'}, | 
| 120 |  |  |  |  |  |  | ); | 
| 121 |  |  |  |  |  |  | } | 
| 122 |  |  |  |  |  |  |  | 
| 123 |  |  |  |  |  |  | sub lambda_response { | 
| 124 | 1 |  |  | 1 | 0 | 38 | my $self = shift; | 
| 125 | 1 |  |  |  |  | 4 | my ($response, $context) = @_; | 
| 126 | 1 |  |  |  |  | 34 | my $runtime_api = $self->{runtime_api}; | 
| 127 | 1 |  |  |  |  | 4 | my $api_version = $self->{api_version}; | 
| 128 | 1 |  |  |  |  | 23 | my $request_id = $context->aws_request_id; | 
| 129 | 1 |  |  |  |  | 23 | my $url = "http://${runtime_api}/${api_version}/runtime/invocation/${request_id}/response"; | 
| 130 | 1 |  |  |  |  | 253 | my $resp = $self->{http}->post($url, { | 
| 131 |  |  |  |  |  |  | content => encode_json($response), | 
| 132 |  |  |  |  |  |  | }); | 
| 133 | 1 | 50 |  |  |  | 26758 | if (!$resp->{success}) { | 
| 134 | 0 |  |  |  |  | 0 | die "failed to response of execution: $resp->{status} $resp->{reason}"; | 
| 135 |  |  |  |  |  |  | } | 
| 136 |  |  |  |  |  |  | } | 
| 137 |  |  |  |  |  |  |  | 
| 138 |  |  |  |  |  |  | sub lambda_response_streaming { | 
| 139 | 1 |  |  | 1 | 0 | 41 | my $self = shift; | 
| 140 | 1 |  |  |  |  | 6 | my ($response, $context) = @_; | 
| 141 | 1 |  |  |  |  | 28 | my $runtime_api = $self->{runtime_api}; | 
| 142 | 1 |  |  |  |  | 8 | my $api_version = $self->{api_version}; | 
| 143 | 1 |  |  |  |  | 13 | my $request_id = $context->aws_request_id; | 
| 144 | 1 |  |  |  |  | 12 | my $url = "http://${runtime_api}/${api_version}/runtime/invocation/${request_id}/response"; | 
| 145 | 1 |  |  |  |  | 3 | my $writer = undef; | 
| 146 |  |  |  |  |  |  | try { | 
| 147 |  |  |  |  |  |  | $response->(sub { | 
| 148 | 1 |  |  |  |  | 6 | my $content_type = shift; | 
| 149 |  |  |  |  |  |  | $writer = AWS::Lambda::ResponseWriter->new( | 
| 150 |  |  |  |  |  |  | response_url => $url, | 
| 151 |  |  |  |  |  |  | http         => $self->{http}, | 
| 152 | 1 |  |  |  |  | 29 | ); | 
| 153 | 1 |  |  |  |  | 9 | $writer->_request($content_type); | 
| 154 | 1 |  |  |  |  | 587 | return $writer; | 
| 155 | 1 |  |  | 1 |  | 182 | }); | 
| 156 |  |  |  |  |  |  | } catch { | 
| 157 | 0 |  |  | 0 |  | 0 | my $err = $_; | 
| 158 | 0 |  |  |  |  | 0 | print STDERR "$err"; | 
| 159 | 0 | 0 |  |  |  | 0 | if ($writer) { | 
| 160 | 0 |  |  |  |  | 0 | $writer->_close_with_error($err); | 
| 161 |  |  |  |  |  |  | } else { | 
| 162 | 0 |  |  |  |  | 0 | $self->lambda_error($err, $context); | 
| 163 |  |  |  |  |  |  | } | 
| 164 | 1 |  |  |  |  | 81 | }; | 
| 165 | 1 | 50 |  |  |  | 166 | if ($writer) { | 
| 166 | 1 |  |  |  |  | 7 | my $response = $writer->_handle_response; | 
| 167 | 1 | 50 |  |  |  | 74 | if (!$response->{success}) { | 
| 168 | 0 |  |  |  |  | 0 | die "failed to response of execution: $response->{status} $response->{reason}"; | 
| 169 |  |  |  |  |  |  | } | 
| 170 |  |  |  |  |  |  | } | 
| 171 |  |  |  |  |  |  | } | 
| 172 |  |  |  |  |  |  |  | 
| 173 |  |  |  |  |  |  | sub lambda_error { | 
| 174 | 1 |  |  | 1 | 0 | 23 | my $self = shift; | 
| 175 | 1 |  |  |  |  | 9 | my ($error, $context) = @_; | 
| 176 | 1 |  |  |  |  | 43 | my $runtime_api = $self->{runtime_api}; | 
| 177 | 1 |  |  |  |  | 3 | my $api_version = $self->{api_version}; | 
| 178 | 1 |  |  |  |  | 13 | my $request_id = $context->aws_request_id; | 
| 179 | 1 |  |  |  |  | 9 | my $url = "http://${runtime_api}/${api_version}/runtime/invocation/${request_id}/error"; | 
| 180 | 1 |  | 50 |  |  | 37 | my $type = blessed($error) // "Error"; | 
| 181 | 1 |  |  |  |  | 244 | my $resp = $self->{http}->post($url, { | 
| 182 |  |  |  |  |  |  | content => encode_json({ | 
| 183 |  |  |  |  |  |  | errorMessage => "$error", | 
| 184 |  |  |  |  |  |  | errorType => "$type", | 
| 185 |  |  |  |  |  |  | }), | 
| 186 |  |  |  |  |  |  | }); | 
| 187 | 1 | 50 |  |  |  | 25914 | if (!$resp->{success}) { | 
| 188 | 0 |  |  |  |  | 0 | die "failed to send error of execution: $resp->{status} $resp->{reason}"; | 
| 189 |  |  |  |  |  |  | } | 
| 190 |  |  |  |  |  |  | } | 
| 191 |  |  |  |  |  |  |  | 
| 192 |  |  |  |  |  |  | sub lambda_init_error { | 
| 193 | 1 |  |  | 1 | 0 | 24 | my $self = shift; | 
| 194 | 1 |  |  |  |  | 16 | my $error = shift; | 
| 195 | 1 |  |  |  |  | 35 | my $runtime_api = $self->{runtime_api}; | 
| 196 | 1 |  |  |  |  | 4 | my $api_version = $self->{api_version}; | 
| 197 | 1 |  |  |  |  | 5 | my $url = "http://${runtime_api}/${api_version}/runtime/init/error"; | 
| 198 | 1 |  | 50 |  |  | 40 | my $type = blessed($error) // "Error"; | 
| 199 | 1 |  |  |  |  | 275 | my $resp = $self->{http}->post($url, { | 
| 200 |  |  |  |  |  |  | content => encode_json({ | 
| 201 |  |  |  |  |  |  | errorMessage => "$error", | 
| 202 |  |  |  |  |  |  | errorType => "$type", | 
| 203 |  |  |  |  |  |  | }), | 
| 204 |  |  |  |  |  |  | }); | 
| 205 | 1 | 50 |  |  |  | 27131 | if (!$resp->{success}) { | 
| 206 | 0 |  |  |  |  |  | die "failed to send error of execution: $resp->{status} $resp->{reason}"; | 
| 207 |  |  |  |  |  |  | } | 
| 208 |  |  |  |  |  |  | } | 
| 209 |  |  |  |  |  |  |  | 
| 210 |  |  |  |  |  |  | 1; | 
| 211 |  |  |  |  |  |  | __END__ |