File Coverage

blib/lib/AWS/S3/Roles/Request.pm
Criterion Covered Total %
statement 43 44 97.7
branch 5 6 83.3
condition 3 3 100.0
subroutine 10 10 100.0
pod 0 1 0.0
total 61 64 95.3


line stmt bran cond sub pod time code
1             package AWS::S3::Roles::Request;
2 4     4   4979 use Moose::Role;
  4         15448  
  4         22  
3 4     4   25144 use HTTP::Request;
  4         9  
  4         140  
4 4     4   22 use AWS::S3::ResponseParser;
  4         8  
  4         146  
5 4     4   22 use MooseX::Types::URI qw(Uri);
  4         8  
  4         52  
6 4     4   8599 use URI::Escape qw/ uri_escape /;
  4         11  
  4         342  
7 4     4   2908 use AWS::S3::Signer::V4;
  4         14  
  4         173  
8 4     4   30 use Log::Any qw( $LOG );
  4         7  
  4         80  
9              
10             has 's3' => (
11             is => 'ro',
12             isa => 'AWS::S3',
13             required => 1,
14             );
15              
16             has 'type' => (
17             is => 'ro',
18             isa => 'Str',
19             required => 1,
20             );
21              
22             has 'protocol' => (
23             is => 'ro',
24             isa => 'Str',
25             lazy => 1,
26             default => sub {
27             shift->s3->secure ? 'https' : 'http';
28             }
29             );
30              
31             has 'endpoint' => (
32             is => 'ro',
33             isa => 'Str',
34             lazy => 1,
35             default => sub {
36             shift->s3->endpoint;
37             }
38             );
39              
40             # XXX should be required=>1; https://rt.cpan.org/Ticket/Display.html?id=77863
41             has "_action" => (
42             isa => 'Str',
43             is => 'ro',
44             init_arg => undef,
45             #required => 1
46             );
47              
48             has '_expect_nothing' => ( isa => 'Bool', is => 'ro', init_arg => undef );
49              
50             has '_uri' => (
51             isa => Uri,
52             is => 'ro',
53             lazy => 1,
54             default => sub {
55             my $self = shift;
56             my $m = $self->meta;
57              
58             my $uri = URI->new(
59             $self->protocol . '://'
60             . ( $m->has_attribute('bucket') ? $self->bucket . '.' : '' )
61             . $self->endpoint
62             . '/'
63             );
64              
65             # note we add some extra exceptions to uri_escape to prevent
66             # encoding of things like "/", ":", "="
67             if ( $m->has_attribute('key') ) {
68             my $escaped_path = uri_escape( $self->key,"^A-Za-z0-9\-\._~\/:=" );
69              
70             # if we have a leading slash in the key name we need to add *another*
71             # slash in the call to ->path to ensure it is retained
72             if ( $escaped_path =~ m!^/! && $self->s3->honor_leading_slashes ) {
73             $uri->path( '/'.$escaped_path )
74             } else {
75             $uri->path( $escaped_path )
76             }
77             }
78              
79             $uri->query_keywords( $self->_subresource )
80             if $m->has_attribute('_subresource');
81              
82             $uri;
83             }
84             );
85              
86             has 'signerv4' => (
87             is => 'ro',
88             isa => 'AWS::S3::Signer::V4',
89             lazy => 1,
90             default => sub {
91             my $s = shift;
92             AWS::S3::Signer::V4->new(
93             -access_key => $s->s3->access_key_id,
94             -secret_key => $s->s3->secret_access_key,
95             );
96             }
97             );
98              
99             sub _send_request {
100 27     27   113 my ( $s, $method, $uri, $headers, $content ) = @_;
101 27         218 $LOG->debug('Making AWS request', {method => $method, uri => "$uri"});
102              
103 27         838 my $req = HTTP::Request->new( $method => $uri );
104 27 100       3154 $req->content( $content ) if $content;
105              
106 27         236 delete($headers->{Authorization}); # we will use a v4 signature
107 27         115 map { $req->header( $_ => $headers->{$_} ) } keys %$headers;
  37         1204  
108              
109 27         2487 $s->_sign($req);
110 27         1436 my $res = $s->s3->ua->request( $req );
111              
112             # After creating a bucket and setting its location constraint, we get this
113             # strange 'TemporaryRedirect' response. Deal with it.
114 27 100 100     19076560 if ( $res->header( 'location' ) && $res->content =~ m{>TemporaryRedirect<}s ) {
115 1         83 $req->uri( $res->header( 'location' ) );
116 1         238 $res = $s->s3->ua->request( $req );
117             }
118 27         3407 return $s->parse_response( $res );
119             }
120              
121             sub _sign {
122 27     27   104 my ($s, $request) = @_;
123 27         1382 my $signer = $s->signerv4;
124 27 50       1022 if (defined $s->s3->session_token) {
125 0         0 $request->header('X-Amz-Security-Token', $s->s3->session_token);
126             }
127 27         163 my $digest = Digest::SHA::sha256_hex($request->content);
128 27         762 $request->header('X-Amz-Content-SHA256', $digest);
129 27         3682 $signer->sign($request, $s->s3->region, $digest);
130 27         1979 $request;
131             }
132              
133             sub parse_response {
134 27     27 0 91 my ( $self, $res ) = @_;
135              
136 27         1426 AWS::S3::ResponseParser->new(
137             response => $res,
138             expect_nothing => $self->_expect_nothing,
139             type => $self->type,
140             );
141             }
142              
143             1;