view release on metacpan or search on metacpan
lib/AWS/S3.pm view on Meta::CPAN
package AWS::S3;
use Moose;
use Carp 'confess';
use LWP::UserAgent::Determined;
use HTTP::Response;
use HTTP::Request::Common;
use IO::Socket::INET;
use Class::Load 'load_class';
use AWS::S3::ResponseParser;
use AWS::S3::Owner;
use AWS::S3::Bucket;
our $VERSION = '1.00';
has [qw/access_key_id secret_access_key/] => ( is => 'ro', isa => 'Str' );
lib/AWS/S3/HTTPRequest.pm view on Meta::CPAN
isa => 'HashRef',
default => sub { {} },
);
has 'contenttype' => (
is => 'ro',
required => 0,
isa => 'Str',
);
# Make the HTTP::Request object:
sub http_request {
my $s = shift;
my $method = $s->method;
my $headers = $s->headers;
my $content = $s->content;
my $metadata = $s->metadata;
my $uri = $s->bucket_uri( $s->path );
my $signer = AWS::S3::Signer->new(
lib/AWS/S3/HTTPRequest.pm view on Meta::CPAN
content => $content ? \$content : undef,
headers => [ $headers->flatten ],
);
$headers->header( 'Authorization' => $signer->auth_header );
$headers->header( 'Date' => $signer->date );
$headers->header( 'Host' => URI->new( $uri )->host );
$headers->header( 'content-length' => $signer->content_length ) if $content;
$headers->header( 'content-type' => $signer->content_type ) if $content;
my $request = HTTP::Request->new( $method, $uri, $headers, $content );
return $request;
} # end http_request()
__PACKAGE__->meta->make_immutable;
lib/AWS/S3/Roles/BucketAction.pm view on Meta::CPAN
package AWS::S3::Roles::BucketAction;
use Moose::Role;
use HTTP::Request;
use AWS::S3::ResponseParser;
with 'AWS::S3::Roles::Request';
sub request {
my $s = shift;
my $signer = AWS::S3::Signer->new(
s3 => $s->s3,
method => $s->_action,
lib/AWS/S3/Roles/Request.pm view on Meta::CPAN
package AWS::S3::Roles::Request;
use Moose::Role;
use HTTP::Request;
use AWS::S3::ResponseParser;
use MooseX::Types::URI qw(Uri);
use URI::Escape qw/ uri_escape /;
use AWS::S3::Signer::V4;
has 's3' => (
is => 'ro',
isa => 'AWS::S3',
required => 1,
);
lib/AWS/S3/Roles/Request.pm view on Meta::CPAN
AWS::S3::Signer::V4->new(
-access_key => $s->s3->access_key_id,
-secret_key => $s->s3->secret_access_key,
);
}
);
sub _send_request {
my ( $s, $method, $uri, $headers, $content ) = @_;
my $req = HTTP::Request->new( $method => $uri );
$req->content( $content ) if $content;
delete($headers->{Authorization}); # we will use a v4 signature
map { $req->header( $_ => $headers->{$_} ) } keys %$headers;
$s->_sign($req);
my $res = $s->s3->ua->request( $req );
# After creating a bucket and setting its location constraint, we get this
# strange 'TemporaryRedirect' response. Deal with it.
lib/AWS/S3/Signer.pm view on Meta::CPAN
package AWS::S3::Signer;
use Moose;
use HTTP::Request::Common;
use HTTP::Date 'time2str';
use MIME::Base64 qw(encode_base64);
use Digest::HMAC_SHA1;
use Digest::MD5 'md5';
use Moose::Util::TypeConstraints qw(enum);
use MooseX::Types::URI qw(Uri);
has 's3' => (
is => 'ro',
lib/AWS/S3/Signer/V4.pm view on Meta::CPAN
package AWS::S3::Signer::V4;
use strict;
use POSIX 'strftime';
use URI;
use URI::QueryParam;
use URI::Escape;
use Digest::SHA 'sha256_hex', 'hmac_sha256', 'hmac_sha256_hex';
use Date::Parse;
use Carp 'croak';
use HTTP::Request;
# https://webservices.amazon.com/paapi5/documentation/common-request-parameters.html#host-and-region
use constant PAAPI_REGION => {
qw/
webservices.amazon.com.au us-west-2
webservices.amazon.com.br us-east-1
webservices.amazon.ca us-east-1
webservices.amazon.fr eu-west-1
webservices.amazon.de eu-west-1
webservices.amazon.in eu-west-1
lib/AWS/S3/Signer/V4.pm view on Meta::CPAN
/
};
=head1 NAME
AWS::S3::Signer::V4 - Create a version4 signature for Amazon Web Services
=head1 SYNOPSIS
use AWS::S3::Signer::V4;
use HTTP::Request::Common;
use LWP;
my $signer = AWS::S3::Signer::V4->new(-access_key => 'AKIDEXAMPLE',
-secret_key => 'wJalrXUtnFEMI/K7MDENG+bPxRfiCYEXAMPLEKEY');
my $ua = LWP::UserAgent->new();
# Example POST request
my $request = POST('https://iam.amazonaws.com',
[Action=>'ListUsers',
Version=>'2010-05-08']);
lib/AWS/S3/Signer/V4.pm view on Meta::CPAN
),
},
ref $self || $self;
}
sub access_key { shift->{access_key} }
sub secret_key { shift->{secret_key} }
=item $signer->sign($request [,$region] [,$payload_sha256_hex])
Given an HTTP::Request object, add the headers required by AWS and
then sign it with a version 4 signature by adding an "Authorization"
header.
The request must include a URL from which the AWS endpoint and service
can be derived, such as "ec2.us-east-1.amazonaws.com." In some cases
(e.g. S3 bucket operations) the endpoint does not indicate the
region. In this case, the region can be forced by passing a defined
value for $region. The current date and time will be added to the
request using an "X-Amz-Date header." To force the date and time to a
fixed value, include the "Date" header in the request.
The request content, or "payload" is retrieved from the HTTP::Request
object by calling its content() method.. Under some circumstances the
payload is not included directly in the request, but is in an external
file that will be uploaded as the request is executed. In this case,
you must pass a second argument containing the results of running
sha256_hex() (from the Digest::SHA module) on the content.
The method returns a true value if successful. On errors, it will
throw an exception.
=item $url = $signer->signed_url($request)
lib/AWS/S3/Signer/V4.pm view on Meta::CPAN
sub sign {
my $self = shift;
my ( $request, $region, $payload_sha256_hex ) = @_;
$self->_add_date_header($request);
$self->_sign( $request, $region, $payload_sha256_hex );
}
=item my $url $signer->signed_url($request_or_uri [,$expires] [,$verb])
Pass an HTTP::Request, a URI object, or just a plain URL string
containing the proper endpoint and parameters needed for an AWS REST
API Call. This method will return an appropriately signed request as a
URI object, which can be shared with non-AWS users for the purpose of,
e.g., accessing an object in a private S3 bucket.
Pass an optional $expires argument to indicate that the URL will only
be valid for a finite period of time. The value of the argument is in
seconds.
Pass an optional verb which is useful for HEAD requests, this defaults to GET.
lib/AWS/S3/Signer/V4.pm view on Meta::CPAN
my $incorrect_verbs = {
POST => 1,
PUT => 1
};
if ( exists( $incorrect_verbs->{$verb} ) ) {
die "Use AWS::S3::Signer::V4->sign sub for $verb method";
}
if ( ref $arg1 && UNIVERSAL::isa( $arg1, 'HTTP::Request' ) ) {
$request = $arg1;
$uri = $request->uri;
my $content = $request->content;
$uri->query($content) if $content;
if ( my $date =
$request->header('X-Amz-Date') || $request->header('Date') )
{
$uri->query_param( 'Date' => $date );
}
}
$uri ||= URI->new($arg1);
my $date = $uri->query_param_delete('Date')
|| $uri->query_param_delete('X-Amz-Date');
$request = HTTP::Request->new( $verb => $uri );
$request->header( 'Date' => $date );
$uri = $request->uri; # because HTTP::Request->new() copies the uri!
return $uri if $uri->query_param('X-Amz-Signature');
my $scope = $self->_scope($request);
$uri->query_param( 'X-Amz-Algorithm' => $self->_algorithm );
$uri->query_param( 'X-Amz-Credential' => $self->access_key . '/' . $scope );
$uri->query_param( 'X-Amz-Date' => $self->_datetime($request) );
$uri->query_param( 'X-Amz-Expires' => $expires ) if $expires;
$uri->query_param( 'X-Amz-SignedHeaders' => 'host' );
lib/AWS/S3/Signer/V4.pm view on Meta::CPAN
my $self = shift;
my $request = shift;
my $date = $request->header('Date');
my @datetime = $date ? gmtime( str2time($date) ) : gmtime();
return strftime( '%Y%m%dT%H%M%SZ', @datetime );
}
sub _hash_canonical_request {
my $self = shift;
my ( $request, $hashed_payload ) =
@_; # (HTTP::Request,sha256_hex($content))
my $method = $request->method;
my $uri = $request->uri;
my $path = $uri->path || '/';
my @params = $uri->query_form;
my $headers = $request->headers;
$hashed_payload ||= sha256_hex( $request->content );
# canonicalize query string
# in the case of the S3 api, but its still expected to be part of a
t/aws/s3/http_request.t view on Meta::CPAN
s3
method
path
headers
content
metadata
contenttype
/,
);
isa_ok( $request->http_request,'HTTP::Request' );
is( $request->is_dns_bucket( 'foo' ),1,'_is_dns_bucket' );
is( $request->is_dns_bucket( 'Foo' ),0,'_! is_dns_bucket' );
is( $request->is_dns_bucket( 'bar123boz' ),1,'_is_dns_bucket' );
is( $request->is_dns_bucket( 'bar123Boz' ),0,'! _is_dns_bucket' );
is( $request->is_dns_bucket( 'foo!' ),0,'! _is_dns_bucket' );
is( $request->is_dns_bucket( '255.255.255.255' ),0,'! _is_dns_bucket' );
is( $request->is_dns_bucket( 'fo' ),0,'! _is_dns_bucket' );
is( $request->is_dns_bucket( 'x' x 64 ),0,'! _is_dns_bucket' );
is( $request->is_dns_bucket( 'x' x 63 ),1,'_is_dns_bucket' );
t/aws/s3/http_request.t view on Meta::CPAN
isa_ok(
my $request_with_content = AWS::S3::HTTPRequest->new(
s3 => $s3,
method => 'POST',
path => '/bar/baz',
content => 'Hello World!'
),
'AWS::S3::HTTPRequest'
);
isa_ok( my $http_request_with_content = $request_with_content->http_request, 'HTTP::Request' );
my $header = $http_request_with_content->headers;
is( $header->header( 'content-type' ), 'text/plain', '... and content-type got set' );
is( $header->header( 'content-length' ), 12, '... and content-length got set' );
is( $header->header( 'host' ), 's3.baz.com', '... and host got set' );
done_testing();
t/aws/s3/signer/v4.t view on Meta::CPAN
use strict;
use warnings;
use ExtUtils::MakeMaker;
use FindBin '$Bin';
use constant TEST_COUNT => 13;
use Test::More tests => TEST_COUNT;
use_ok('AWS::S3::Signer::V4');
use_ok('HTTP::Request::Common');
my $signer = AWS::S3::Signer::V4->new(
-access_key => 'AKIDEXAMPLE',
-secret_key => 'wJalrXUtnFEMI/K7MDENG+bPxRfiCYEXAMPLEKEY'
);
ok( $signer, 'AWS::S3::Signer::V4->new' );
my $request = POST(
'https://iam.amazonaws.com',
[ Action => 'ListUsers', Version => '2010-05-08' ],
Date => '1 January 2014 01:00:00 -0500',