Amazon-S3-Thin
view release on metacpan or search on metacpan
lib/Amazon/S3/Thin.pm view on Meta::CPAN
package Amazon::S3::Thin;
use 5.008001;
use strict;
use warnings;
use Carp;
use LWP::UserAgent;
use Digest::MD5;
use Encode;
use Amazon::S3::Thin::Resource;
use Amazon::S3::Thin::Credentials;
our $VERSION = '0.32';
my $METADATA_PREFIX = 'x-amz-meta-';
my $MAIN_HOST = 's3.amazonaws.com';
sub new {
my $class = shift;
my $self = shift;
# If we have an explicitly-configured credential provider then use that here, otherwise
# existing behaviour will be followed
if ($self->{credential_provider} and $self->{credential_provider} eq 'env') {
$self->{credentials} = Amazon::S3::Thin::Credentials->from_env;
}
elsif ($self->{credential_provider} and $self->{credential_provider} eq 'metadata') {
$self->{credentials} = Amazon::S3::Thin::Credentials->from_metadata($self);
}
elsif ($self->{credential_provider} and $self->{credential_provider} eq 'ecs_container') {
$self->{credentials} = Amazon::S3::Thin::Credentials->from_ecs_container($self);
}
else {
# check existence of credentials
croak "No aws_access_key_id" unless $self->{aws_access_key_id};
croak "No aws_secret_access_key" unless $self->{aws_secret_access_key};
# wrap credentials
$self->{credentials} = Amazon::S3::Thin::Credentials->new(
$self->{aws_access_key_id},
$self->{aws_secret_access_key},
$self->{aws_session_token},
);
delete $self->{aws_access_key_id};
delete $self->{aws_secret_access_key};
delete $self->{aws_session_token};
}
delete $self->{credential_provider};
bless $self, $class;
$self->secure(0) unless defined $self->secure;
$self->ua($self->_default_ua) unless defined $self->ua;
$self->debug(0) unless defined $self->debug;
$self->virtual_host(0) unless defined $self->virtual_host;
$self->{signature_version} = 4 unless defined $self->{signature_version};
if ($self->{signature_version} == 4 && ! $self->{region}) {
croak "Please set region when you use signature v4";
}
$self->{signer} = $self->_load_signer($self->{signature_version});
return $self;
}
sub _load_signer {
my $self = shift;
my $version = shift;
my $signer_class = "Amazon::S3::Thin::Signer::V$version";
eval "require $signer_class" or die $@;
if ($version == 2) {
return $signer_class->new($self->{credentials}, $MAIN_HOST);
} elsif ($version == 4) {
return $signer_class->new($self->{credentials}, $self->{region});
}
}
sub _default_ua {
my $self = shift;
my $ua = LWP::UserAgent->new(
keep_alive => 10,
requests_redirectable => [qw(GET HEAD DELETE PUT)],
);
$ua->timeout(30);
$ua->env_proxy;
return $ua;
}
# Accessors
sub secure {
my $self = shift;
if (@_) {
$self->{secure} = shift;
} else {
return $self->{secure};
}
}
sub debug {
my $self = shift;
if (@_) {
$self->{debug} = shift;
} else {
return $self->{debug};
}
}
sub ua {
my $self = shift;
if (@_) {
$self->{ua} = shift;
} else {
return $self->{ua};
}
}
sub virtual_host {
my $self = shift;
if (@_) {
$self->{virtual_host} = shift;
} else {
return $self->{virtual_host};
}
}
sub _send {
my ($self, $request) = @_;
warn "[Request]\n" , $request->as_string if $self->{debug};
my $response = $self->ua->request($request);
warn "[Response]\n" , $response->as_string if $self->{debug};
return $response;
}
# API calls
sub get_object {
my ($self, $bucket, $key, $headers) = @_;
my $request = $self->_compose_request('GET', $self->_resource($bucket, $key), $headers);
return $self->_send($request);
}
sub head_object {
my ($self, $bucket, $key) = @_;
my $request = $self->_compose_request('HEAD', $self->_resource($bucket, $key));
return $self->_send($request);
}
sub delete_object {
my ($self, $bucket, $key) = @_;
my $request = $self->_compose_request('DELETE', $self->_resource($bucket, $key));
return $self->_send($request);
}
sub copy_object {
my ($self, $src_bucket, $src_key, $dst_bucket, $dst_key, $headers) = @_;
$headers ||= {};
$headers->{'x-amz-copy-source'} = $src_bucket . "/" . $src_key;
my $request = $self->_compose_request('PUT', $self->_resource($dst_bucket, $dst_key), $headers);
my $res = $self->_send($request);
# XXX: Since the COPY request might return error response in 200 OK, we'll rewrite the status code to 500 for convenience
# ref http://docs.aws.amazon.com/AmazonS3/latest/API/RESTObjectCOPY.html
# ref https://github.com/boto/botocore/blob/4e9b4419ec018716ab1a3fe1587fbdc3cfef200e/botocore/handlers.py#L77-L120
if ($self->_looks_like_special_case_error($res)) {
$res->code(500);
}
return $res;
}
sub _looks_like_special_case_error {
my ($self, $res) = @_;
return $res->code == 200 && (length $res->content == 0 || $res->content =~ /<Error>/);
}
sub put_object {
my ($self, $bucket, $key, $content, $headers) = @_;
croak 'must specify key' unless $key && length $key;
if ($headers->{acl_short}) {
$self->_validate_acl_short($headers->{acl_short});
$headers->{'x-amz-acl'} = $headers->{acl_short};
delete $headers->{acl_short};
}
if (ref($content) eq 'SCALAR') {
$headers->{'Content-Length'} ||= -s $$content;
$content = _content_sub($$content);
}
else {
$headers->{'Content-Length'} ||= length $content;
lib/Amazon/S3/Thin.pm view on Meta::CPAN
=back
=head2 Comparison to precedent modules
There are already some useful modules like L<Amazon::S3>, L<Net::Amazon::S3>
on CPAN. They provide a "Perlish" interface, which looks pretty
for Perl programmers, but they also hide low-level behaviors.
For example, the "get_key" method translate HTTP status 404 into C<undef> and
HTTP 5xx status into exception.
In some situations, it is very important to see the raw HTTP communications.
That's why I made this module.
=head1 CONSTRUCTOR
=head2 new( \%params )
B<Receives:> hashref with options.
B<Returns:> Amazon::S3::Thin object
It can receive the following arguments:
=over 4
=item * C<credential_provider> (B<default: credentials>) - specify where to source credentials from. Options are:
=over 2
=item * C<credentials> - existing behaviour, pass in credentials via C<aws_access_key_id> and C<aws_secret_access_key>
=item * C<env> - fetch credentials from environment variables
=item * C<metadata> - fetch credentials from EC2 instance metadata service
=item * C<ecs_container> - fetch credentials from ECS task role
=back
=item * C<region> - (B<REQUIRED>) region of your buckets you access- (currently used only when signature version is 4)
=item * C<aws_access_key_id> (B<REQUIRED [provider: credentials]>) - an access key id
of your credentials.
=item * C<aws_secret_access_key> (B<REQUIRED [provider: credentials]>) - an secret access key
of your credentials.
=item * C<version> (B<OPTIONAL [provider: metadata]>) - version of metadata service to use, either 1 or 2.
L<read more|https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/configuring-instance-metadata-service.html>
=item * C<role> (B<OPTIONAL [provider: metadata]>) - IAM instance role to use, otherwise the first is selected
=item * C<secure> - whether to use https or not. Default is 0 (http).
=item * C<ua> - a user agent object, compatible with LWP::UserAgent.
Default is an instance of L<LWP::UserAgent>.
=item * C<signature_version> - AWS signature version to use. Supported values
are 2 and 4. Default is 4.
=item * C<debug> - debug option. Default is 0 (false).
If set 1, contents of HTTP request and response are shown on stderr
=item * C<virtual_host> - whether to use virtual-hosted style request format. Default is 0 (path-style).
=back
=head1 ACCESSORS
The following accessors are provided. You can use them to get/set your
object's attributes.
=head2 secure
Whether to use https (1) or http (0) when connecting to S3.
=head2 ua
The user agent used internally to perform requests and return responses.
If you set this attribute, please make sure you do so with an object
compatible with L<LWP::UserAgent> (i.e. providing the same interface).
=head2 debug
Debug option.
=head1 Operations on Buckets
=head2 put_bucket( $bucket [, $headers])
B<Arguments>:
=over 2
=item 1. bucket - a string with the bucket
=item 2. headers (B<optional>) - hashref with extra header information
=back
=head2 delete_bucket( $bucket [, $headers])
B<Arguments>:
=over 3
=item 1. bucket - a string with the bucket
=item 2. headers (B<optional>) - hashref with extra header information
=back
=head1 Operations on Objects
=head2 get_object( $bucket, $key [, $headers] )
B<Arguments>:
=over 3
=item 1. bucket - a string with the bucket
=item 2. key - a string with the key
=item 3. headers (B<optional>) - hashref with extra header information
=back
B<Returns>: an L<HTTP::Response> object for the request. Use the C<content()>
method on the returned object to read the contents:
my $res = $s3->get_object( 'my.bucket', 'my/key.ext' );
if ($res->is_success) {
my $content = $res->content;
}
The GET operation retrieves an object from Amazon S3.
For more information, please refer to
L<< Amazon's documentation for GET|http://docs.aws.amazon.com/AmazonS3/latest/API/RESTObjectGET.html >>.
( run in 1.843 second using v1.01-cache-2.11-cpan-39bf76dae61 )