File Coverage

blib/lib/AWS/Lambda/ResponseWriter.pm
Criterion Covered Total %
statement 72 89 80.9
branch 7 18 38.8
condition 7 21 33.3
subroutine 13 14 92.8
pod 0 3 0.0
total 99 145 68.2


line stmt bran cond sub pod time code
1             package AWS::Lambda::ResponseWriter;
2 17     17   298 use 5.026000;
  17         69  
3 17     17   93 use utf8;
  17         34  
  17         111  
4 17     17   371 use strict;
  17         30  
  17         438  
5 17     17   90 use warnings;
  17         146  
  17         460  
6 17     17   88 use Carp qw(croak);
  17         34  
  17         966  
7 17     17   153 use Scalar::Util qw(blessed);
  17         36  
  17         825  
8 17     17   8321 use MIME::Base64 qw(encode_base64);
  17         11257  
  17         1014  
9 17     17   120 use HTTP::Tiny;
  17         42  
  17         16041  
10              
11             my %DefaultPort = (
12             http => 80,
13             https => 443,
14             );
15              
16             sub new {
17 1     1 0 3 my $proto = shift;
18 1   33     23 my $class = ref $proto || $proto;
19 1         11 my %args;
20 1 50 33     9 if (@_ == 1 && ref $_[0] eq 'HASH') {
21 0         0 %args = %{$_[0]};
  0         0  
22             } else {
23 1         14 %args = @_;
24             }
25              
26 1   33     13 my $http = $args{http} // HTTP::Tiny->new;
27 1   50     11 my $response_url = $args{response_url} // die '$LAMBDA_TASK_ROOT is not found';
28 1   50     27 my $content_type = $args{content_type} // 'application/json';
29 1         26 my $self = bless +{
30             response_url => $response_url,
31             http => $http,
32             handle => undef,
33             closed => 0,
34             }, $class;
35 1         6 return $self;
36             }
37              
38             sub _request {
39 1     1   3 my ($self, $content_type) = @_;
40 1         13 my $response_url = $self->{response_url};
41 1         3 my $http = $self->{http};
42 1         27 my ($scheme, $host, $port, $path_query, $auth) = $http->_split_url($response_url);
43 1 50       105 my $host_port = ($port == $DefaultPort{$scheme} ? $host : "$host:$port");
44 1         123 my $request = {
45             method => "POST",
46             scheme => $scheme,
47             host => $host,
48             port => $port,
49             host_port => $host_port,
50             uri => $path_query,
51             headers => {
52             "host" => $host_port,
53             "content-type" => $content_type,
54             "transfer-encoding" => "chunked",
55             "trailer" => "Lambda-Runtime-Function-Error-Type, Lambda-Runtime-Function-Error-Body",
56             "lambda-runtime-function-response-mode" => "streaming",
57             },
58             header_case => {
59             "host" => "Host",
60             "content-type" => "Content-Type",
61             "transfer-encoding" => "Transfer-Encoding",
62             "trailer" => "Trailer",
63             "lambda-runtime-function-response-mode" => "Lambda-Runtime-Function-Response-Mode",
64             },
65             };
66 1         6 my $peer = $host;
67              
68             # We remove the cached handle so it is not reused in the case of redirect.
69             # reuse for the same scheme, host and port
70 1         5 my $handle = delete $http->{handle};
71 1 50       8 if ( $handle ) {
72 0 0       0 unless ( $handle->can_reuse( $scheme, $host, $port, $peer ) ) {
73 0         0 $handle->close;
74 0         0 undef $handle;
75             }
76             }
77 1   33     38 $handle ||= $http->_open_handle( $request, $scheme, $host, $port, $peer );
78 1         1136 $self->{handle} = $handle;
79              
80 1         2 $handle->write_request_header(@{$request}{qw/method uri headers header_case/});
  1         13  
81             }
82              
83             sub _handle_response {
84 1     1   2 my $self = shift;
85 1 50       7 if (!$self->{closed}) {
86 0         0 $self->close;
87             }
88              
89 1         2 my $http = $self->{http};
90 1         2 my $handle = $self->{handle};
91 1         2 my $response;
92 1         30 do { $response = $handle->read_response_header }
93 1         14 until (substr($response->{status},0,1) ne '1');
94              
95 1         11824 my $data_cb = $http->_prepare_data_cb($response, {});
96 1         55 my $known_message_length = $handle->read_body($data_cb, $response);
97 1         330 $handle->close;
98              
99 1         93 $response->{success} = substr( $response->{status}, 0, 1 ) eq '2';
100 1         5 $response->{url} = $self->{response_url};
101 1         9 return $response;
102             }
103              
104             sub write {
105 2     2 0 142 my ($self, $data) = @_;
106              
107 2 50       11 if ($self->{closed}) {
108             # already closed
109 0         0 croak "write failed: already closed";
110             }
111              
112 2 50 33     26 if (!defined($data) || length($data) == 0) {
113 0         0 return "0E0";
114             }
115              
116 2         12 my $chunk = sprintf '%X', length $data;
117 2         6 $chunk .= "\x0D\x0A";
118 2         3 $chunk .= $data;
119 2         3 $chunk .= "\x0D\x0A";
120 2         15 return $self->{handle}->write($chunk);
121             }
122              
123             sub close {
124 1     1 0 119 my $self = shift;
125 1 50       82 if ($self->{closed}) {
126             # already closed
127 0         0 return;
128             }
129 1         3 my $handle = $self->{handle};
130 1         3 $self->{closed} = 1;
131 1         4 return $handle->write("0\x0D\x0A\x0D\x0A");
132             }
133              
134             sub _close_with_error {
135 0     0     my ($self, $error) = @_;
136 0 0         if ($self->{closed}) {
137             # already closed
138 0           return;
139             }
140 0           $self->{closed} = 1;
141 0           my $handle = $self->{handle};
142 0           $handle->write("0\x0D\x0A");
143 0   0       my $type = blessed($error) // "Error";
144 0           return $handle->write_header_lines({
145             "lambda-runtime-function-error-type" => "$type",
146             "lambda-runtime-function-error-body" => encode_base64("$error", ""),
147             }, {
148             "lambda-runtime-function-error-type" => "Lambda-Runtime-Function-Error-Type",
149             "lambda-runtime-function-error-body" => "Lambda-Runtime-Function-Error-Body",
150             });
151             }
152              
153             1;