Amazon-S3-Thin

 view release on metacpan or  search on metacpan

lib/Amazon/S3/Thin.pm  view on Meta::CPAN

        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;
    }

    if (ref($content)) {
        # TODO
        # I do not understand what it is :(
        #
        # return $self->_send_request_expect_nothing_probed('PUT',
        #    $self->_resource($bucket, $key), $headers, $content);
        #
        die "unable to handle reference";
    }
    else {
        my $request = $self->_compose_request('PUT', $self->_resource($bucket, $key), $headers, $content);
        return $self->_send($request);
    }
}

sub list_objects {
    my ($self, $bucket, $opt) = @_;
    croak 'must specify bucket' unless $bucket;
    $opt ||= {};

    my $query_string;
    if (%$opt) {
        $query_string = join('&',
                 map { $_ . "=" . Amazon::S3::Thin::Resource->urlencode($opt->{$_}) } sort keys %$opt);
    }

    my $resource = $self->_resource($bucket, undef, $query_string);
    my $request = $self->_compose_request('GET', $resource);
    my $response = $self->_send($request);
    return $response;
}

sub delete_multiple_objects {
    my ($self, $bucket, @keys) = @_;

    my $content = _build_xml_for_delete(@keys);
    # XXX: specify an empty string with `delete` query for calculating signature correctly in AWS::Signature4
    my $resource = $self->_resource($bucket, undef, 'delete=');

lib/Amazon/S3/Thin.pm  view on Meta::CPAN

  my $response;

  $response = $s3client->put_bucket($bucket);

  $response = $s3client->put_object($bucket, $key, "hello world");

  $response = $s3client->get_object($bucket, $key);
  print $response->content; # => "hello world"

  $response = $s3client->delete_object($bucket, $key);

  $response = $s3client->list_objects(
                              $bucket,
                              {prefix => "foo", delimiter => "/"}
                             );

You can also pass any useragent as you like

  my $s3client = Amazon::S3::Thin->new({
          ...
          ua => $any_LWP_copmatible_useragent,
      });

Signature version 4 is used by default. 
To use signature version 2, add a C<signature_version> option:

  my $s3client = Amazon::S3::Thin->new({
          ...
          signature_version     => 2,
      });

=head1 DESCRIPTION

Amazon::S3::Thin is a thin, lightweight, low-level Amazon S3 client.

It's designed for only ONE purpose: Send a request and get a response.

In detail, it offers the following features:

=over

=item Low Level

It returns an L<HTTP::Response> object so you can easily inspect
what's happening inside, and can handle errors as you like.

=item Low Dependency

It does not require any XML::* modules, so installation is easy;

=item Low Learning Cost

The interfaces are designed to follow S3 official REST APIs.
So it is easy to learn.

=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



( run in 2.472 seconds using v1.01-cache-2.11-cpan-98e64b0badf )