AWS-S3

 view release on metacpan or  search on metacpan

Changes  view on Meta::CPAN


0.040 2014-10-23
  Work done by leejo
  - Add signed_url method to AWS::S3::File and tests
  - Fix load / hash order bugs in AWS::S3::Signer
  - Resolve https://rt.cpan.org/Ticket/Display.html?id=92423

0.03_01 2012-06-14
  Work done by Evan Carroll (http://www.evancarroll.com)
  - Migrated to Moose & removed dependencies on VSO
  - Fixed bug introduced by previous commit, reverted to contenttype & all tests pass

0.028 2012-03-01
  - Fixed: AWS/S3.pm required a higher version of VSO than the Makefile.PL and META.yml.
  - Strange: Local testing shows $bucket->delete_multi works, but it gives me
    some problems in production.

0.027 2012-02-24
  - Fixed sporadic error:
    "Invalid value for AWS::S3::File.size: isn't a Int: [Str] 'contenttype': Must contain only numbers 0-9"

0.026 2012-01-30
  - $bucket->file( $missing_file ) was failing silently, causing strange behavior
    further upstream.  Now it will confess with the http response from S3.

0.025 2012-01-12
  - $bucket->delete_multi( @keys ) actually works now.

0.024 2012-01-05
  - Better integration with CloudFront.

Changes  view on Meta::CPAN

  - File's S3 was a weak ref, which caused problems.  No longer...

0.020 2011-12-21
  - Updated to be compatible with VSO 0.21

0.019 2011-12-14
  - Bucket's S3 was a weak ref, which caused problems sometimes.  Not anymore...
    we'll see if that fixes it.

0.018 2011-12-13
  - Thanks to ukautz++ (Ulrich Kautz) for adding the ability to set the content-type
    of files stored in S3.
  - He even updated the POD to show how to set the content-type (via 'contenttype').

0.017 2011-12-11
  - Fixed broken META.yml (again)

0.016 2011-12-10
  - Fixed broken META.yml

0.015 2011-12-10
  - Migrated to github.
  - Added repository url to meta.

lib/AWS/S3.pm  view on Meta::CPAN

    default => sub { LWP::UserAgent::Determined->new }
);

has 'honor_leading_slashes' => (
    is      => 'ro',
    isa     => 'Bool',
    default => sub { 0 },
);

sub request {
    my ( $s, $type, %args ) = @_;

    my $class = "AWS::S3::Request::$type";

    load_class( $class );
    return $class->new( %args, s3 => $s, type => $type );
}    # end request()

sub owner {
    my $s = shift;

    my $type     = 'ListAllMyBuckets';
    my $request  = $s->request( $type );
    my $response = $request->request();

    # Linode/Akamai E3 endpoints do not include the `xmlns` in
    # ListAllMyBuckets, so use the localname to work with or
    # without a declared XML namespace.
    my $xml      = $response->xml;
    my ($node)   = $xml->getElementsByLocalName('Owner');
    return AWS::S3::Owner->new(
        id           => $node->getElementsByLocalName('ID')->string_value,
        display_name => $node->getElementsByLocalName('DisplayName')->string_value,
    );
}    # end owner()

sub buckets {
    my ( $s ) = @_;

    my $type     = 'ListAllMyBuckets';
    my $request  = $s->request( $type );
    my $response = $request->request();

    # Linode/Akamai E3 endpoints do not include the `xmlns` in
    # ListAllMyBuckets, so use the localname to work with or
    # without a declared XML namespace.
    my $xml     = $response->xml;
    my @buckets = ();
    foreach my $node ( $xml->getElementsByLocalName( 'Bucket' ) ) {
        push @buckets,
          AWS::S3::Bucket->new(

lib/AWS/S3.pm  view on Meta::CPAN

    my ( $s, $name ) = @_;

    my ( $bucket ) = grep { $_->name eq $name } $s->buckets
      or return;
    $bucket;
}    # end bucket()

sub add_bucket {
    my ( $s, %args ) = @_;

    my $type     = 'CreateBucket';
    my $request  = $s->request(
        $type,
        bucket => $args{name},
        (
            $args{location} ? ( location => $args{location} )
                : $s->region ? ( location => $s->region )
                : ()
        ),
    );
    my $response = $request->request();

    if ( my $msg = $response->friendly_error() ) {

lib/AWS/S3/Bucket.pm  view on Meta::CPAN

use MooseX::Types -declare => [qw/ACLShorts/];

has 'acl' => (
    is       => 'rw',
    isa      => 'Str',
    required => 0,
    lazy     => 1,
    clearer  => '_clear_acl',
    default  => sub {
        my $self = shift;
        my $type = 'GetBucketAccessControl';
        return $self->_get_property( $type )->response->decoded_content();
    },
    trigger  => sub {
        my ( $self, $new_val, $old_val ) = @_;

        my %shorts = map { $_ => undef } qw(
          private public-read public-read-write authenticated-read
        );

        my %acl = ();
        if ( $new_val =~ m{<} ) {
            $acl{acl_xml} = $new_val;
        }
        elsif ( exists $shorts{$new_val} ) {
            $acl{acl_short} = $new_val;
        }
        else {
            die "Attempt to set an invalid value for acl: '$new_val'";
        }

        my $type     = 'SetBucketAccessControl';
        my $req      = $self->s3->request( $type, %acl, bucket => $self->name, );
        my $response = $req->request();

        return if $response->response->code == 404;

        if ( my $msg = $response->friendly_error() ) {
            die $msg;
        }    # end if()

        $self->_clear_acl;
    }
);

has 'location_constraint' => (
    is       => 'ro',
    isa      => 'Str',
    required => 0,
    lazy     => 1,
    default  => sub {
        my $self = shift;

        my $type     = 'GetBucketLocationConstraint';
        my $response = $self->_get_property( $type );

        my $constraint = $response->xpc->findvalue( '//s3:LocationConstraint' );
        if ( defined $constraint && $constraint eq '' ) {
            return;
        } else {
            return $constraint;
        }
    }
);

has 'policy' => (
    is       => 'rw',
    isa      => 'Str',
    required => 0,
    lazy     => 1,
    clearer  => '_clear_policy',
    default  => sub {
        my $self = shift;

        my $type     = 'GetBucketPolicy';
        my $req      = $self->s3->request( $type, bucket => $self->name, );
        my $response = $req->request();

        eval { $response->_parse_errors };
        if ( my $msg = $response->friendly_error() ) {
            if ( $response->error_code eq 'NoSuchBucketPolicy' ) {
                return '';
            } else {
                die $msg;
            }    # end if()
        }    # end if()

        return $response->response->decoded_content();
    },
    trigger  => sub {
        my ( $self, $policy ) = @_;

        my $type = 'SetBucketPolicy';
        my $req  = $self->s3->request(
            $type,
            bucket => $self->name,
            policy => $policy,
        );
        my $response = $req->request();

        #warn "NewPolicy:($policy).......\n";
        #warn $response->response->as_string;
        if ( my $msg = $response->friendly_error() ) {
            die $msg;
        }    # end if()

lib/AWS/S3/Bucket.pm  view on Meta::CPAN


sub files {
    my ( $s, %args ) = @_;

    return AWS::S3::FileIterator->new( %args, bucket => $s, );
}    # end files()

sub file {
    my ( $s, $key ) = @_;

    my $type = 'GetFileInfo';

    my $parser = $s->_get_property( $type, key => $key )
      or return;

    my $res = $parser->response;
    confess "Cannot get file: ", $res->as_string, " " unless $res->is_success;
    return AWS::S3::File->new(
        bucket       => $s,
        key          => $key || undef,
        size         => $res->header( 'content-length' ) || 0,
        contenttype  => $res->header( 'content-type' ) || 'application/octet-stream',
        etag         => $res->header( 'etag' ) || undef,
        lastmodified => $res->header( 'last-modified' ) || undef,
        is_encrypted => ( $res->header( 'x-amz-server-side-encryption' ) || '' ) eq 'AES256' ? 1 : 0,
    );
}    # end file()

sub add_file {
    my ( $s, %args ) = @_;

    my $file = AWS::S3::File->new(
        %args,
        bucket => $s
    );
    $file->contents( $args{contents} );
    return $file;
}    # end add_file()

sub delete {
    my ( $s ) = @_;

    my $type = 'DeleteBucket';

    my $req = $s->s3->request( $type, bucket => $s->name, );
    my $response = $req->request();

    if ( my $msg = $response->friendly_error() ) {
        die $msg;
    }    # end if()

    return 1;
}    # end delete()

# Working as of v0.023
sub delete_multi {
    my ( $s, @keys ) = @_;

    die "You can only delete up to 1000 keys at once"
      if @keys > 1000;
    my $type = 'DeleteMulti';

    my $req = $s->s3->request(
        $type,
        bucket => $s->name,
        keys   => \@keys,
    );
    my $response = $req->request();

    if ( my $msg = $response->friendly_error() ) {
        die $msg;
    }    # end if()

    return 1;
}    # end delete_multi()

sub _get_property {
    my ( $s, $type, %args ) = @_;

    my $req = $s->s3->request( $type, bucket => $s->name, %args );
    my $response = $req->request();

    return if $response->response->code == 404;

    if ( my $msg = $response->friendly_error() ) {
        die $msg;
    }    # end if()

    return $response;
}    # end _get_property()

lib/AWS/S3/File.pm  view on Meta::CPAN


package AWS::S3::File;

use Moose;
use Carp 'confess';

use MooseX::Types -declare => [qw/fileContents/];
use MooseX::Types::Moose qw/Str ScalarRef CodeRef/;

subtype fileContents, as ScalarRef;
coerce fileContents,
  from  CodeRef,
  via   {
    my $ref = $_[0];
    my $v = $ref->();
    ref $v ? $v : \$v
  }
;

has 'key' => (

lib/AWS/S3/File.pm  view on Meta::CPAN

    default  => 'STANDARD',
    required => 1,
);

has 'lastmodified' => (
    is       => 'ro',
    isa      => 'Str',
    required => 0,
);

has 'contenttype' => (
    is       => 'rw',
    isa      => 'Str',
    required => 0,
    default  => 'binary/octet-stream'
);

has 'is_encrypted' => (
    is       => 'rw',
    isa      => 'Bool',
    required => 1,
    lazy     => 1,
    default  => sub {
        my $s = shift;

        my $type = 'GetFileInfo';
        my $req  = $s->bucket->s3->request(
            $type,
            bucket => $s->bucket->name,
            key    => $s->key,
        );

        return $req->request->response->header( 'x-amz-server-side-encryption' ) ? 1 : 0;
    },
);

has 'contents' => (
    is       => 'rw',

lib/AWS/S3/File.pm  view on Meta::CPAN


    return unless $s->etag;
    ( my $etag = $s->etag ) =~ s{^"}{};
    $etag =~ s{"$}{};
    $s->{etag} = $etag;
}    # end BUILD()

sub update {
    my $s       = shift;
    my %args    = @_;
    my @args_ok = grep { /^content(?:s|type)$/ } keys %args;
    if ( @args_ok ) {
        $s->{$_} = $args{$_} for @args_ok;
        $s->_set_contents();
        return 1;
    }
    return;
}    # end update()

sub _get_contents {
    my $s = shift;

    my $type = 'GetFileContents';
    my $req  = $s->bucket->s3->request(
        $type,
        bucket => $s->bucket->name,
        key    => $s->key,
    );

    return \$req->request->response->decoded_content;
}    # end contents()

sub _set_contents {
    my ( $s, $ref ) = @_;

    my $type     = 'SetFileContents';
    my %args     = ();
    my $response = $s->bucket->s3->request(
        $type,
        bucket                 => $s->bucket->name,
        file                   => $s,
        contents               => $ref,
        content_type           => $s->contenttype,
        server_side_encryption => $s->is_encrypted ? 'AES256' : undef,
    )->request();

    ( my $etag = $response->response->header( 'etag' ) ) =~ s{^"}{};
    $etag =~ s{"$}{};
    $s->{etag} = $etag;

    if ( my $msg = $response->friendly_error() ) {
        die $msg;
    }    # end if()

lib/AWS/S3/File.pm  view on Meta::CPAN

    if ( $expires > time ) {
        $expires -= time;
    }

	my $key = $s->key;

	if ( ! $s->bucket->s3->honor_leading_slashes ) {
		$key =~ s!^/!!;
	}

    my $type = "GetPreSignedUrl";
    my $uri  = $s->bucket->s3->request(
        $type,
        bucket  => $s->bucket->name,
        key     => $key,
        expires => $expires,
    )->request;

    return $uri;
}

sub delete {
    my $s = shift;

    my $type = 'DeleteFile';
    my $req  = $s->bucket->s3->request(
        $type,
        bucket => $s->bucket->name,
        key    => $s->key,
    );
    my $response = $req->request();

    if ( my $msg = $response->friendly_error() ) {
        die $msg;
    }    # end if()

    return 1;

lib/AWS/S3/File.pm  view on Meta::CPAN

  $file->contents( \$new_contents );
  
  # Set the contents with a coderef:
  $file->contents( sub {
    return \$new_contents;
  });
  
  # Alternative update
  $file->update( 
    contents => \'New contents', # optional
    contenttype => 'text/plain'  # optional
  );

  # Get signed URL for the file for public access
  print $file->signed_url( $expiry_time );
  
  # Delete the file:
  $file->delete();

=head1 DESCRIPTION

lib/AWS/S3/File.pm  view on Meta::CPAN

=head2 owner

L<ASW::S3::Owner> - read-only.

The L<ASW::S3::Owner> that the file belongs to.

=head2 storage_class

String - read-only.

The type of storage used by the file.

=head2 lastmodified

String - read-only.

A date in this format:

  2009-10-28T22:32:00

=head2 contents

lib/AWS/S3/File.pm  view on Meta::CPAN

  $file->contents( sub { return $value } );

=head1 PUBLIC METHODS

=head2 delete()

Deletes the file from Amazon S3.

=head2 update()

Update contents and/or contenttype of the file.

=head2 signed_url( $expiry_time )

Will return a signed URL for public access to the file. $expiry_time should be a
Unix seconds since epoch, and will default to now + 1 hour is not passed.

Note that the Signature parameter value will be URI encoded to prevent reserved
characters (+, =, etc) causing a bad request.

=head1 SEE ALSO

lib/AWS/S3/FileIterator.pm  view on Meta::CPAN

sub _fetch {
    my ( $s ) = @_;

    my $path   = $s->{bucket}->name . '/';
    my %params = ();
    $params{marker} = $s->{marker} if $s->{marker};
    $params{prefix} = $s->{prefix} if $s->{prefix};
    $params{max_keys} = 1000;
    $params{delimiter} = $s->{delimiter} if $s->{delimiter};

    my $type     = 'ListBucket';
    my $request  = $s->{bucket}->s3->request( $type, %params, bucket => $s->{bucket}->name );
    my $response = $request->request();

    $s->{has_next} = ( $response->xpc->findvalue( '//s3:IsTruncated' ) || '' ) eq 'true' ? 1 : 0;

    my @files = ();
    foreach my $node ( $response->xpc->findnodes( '//s3:Contents' ) ) {
        my ( $owner_node ) = $response->xpc->findnodes( './/s3:Owner', $node );
        my $owner = {
            id           => $response->xpc->findvalue( './/s3:ID',          $owner_node ),
            display_name => $response->xpc->findvalue( './/s3:DisplayName', $owner_node )

lib/AWS/S3/HTTPRequest.pm  view on Meta::CPAN

    required => 1,
    isa      => 'HTTPMethod'
);

has 'path' => (
    is       => 'ro',
    required => 1,
    isa      => 'Str',
);

class_type( 'HTTP::Headers' );

coerce 'HTTP::Headers'
    => from 'HashRef'
    => via { my $h = HTTP::Headers->new( %$_ ) };

has 'headers' => (
    is       => 'ro',
    required => 1,
    isa      => 'HTTP::Headers',
    lazy     => 1,

lib/AWS/S3/HTTPRequest.pm  view on Meta::CPAN

    default  => '',
);

has 'metadata' => (
    is       => 'ro',
    required => 1,
    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;

lib/AWS/S3/HTTPRequest.pm  view on Meta::CPAN

        method  => $method,
        uri     => $uri,
        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/Request/CreateBucket.pm  view on Meta::CPAN

<CreateBucketConfiguration xmlns="http://s3.amazonaws.com/doc/2006-03-01/"> 
  <LocationConstraint>@{[ $s->location ]}</LocationConstraint>
</CreateBucketConfiguration>
XML
    }

    my $signer = AWS::S3::Signer->new(
        s3           => $s->s3,
        method       => 'PUT',
        uri          => $s->protocol . '://' . $s->bucket . '.' . $s->endpoint . '/',
        content_type => 'text/plain',
        content_md5  => '',
        content      => \$xml,
    );

    return $s->_send_request(
        $signer->method => $signer->uri => {
            Authorization  => $signer->auth_header,
            Date           => $signer->date,
            'content-type' => 'text/plain',
        },
        $xml
    );
}

__PACKAGE__->meta->make_immutable;

lib/AWS/S3/Request/DeleteMulti.pm  view on Meta::CPAN

<Delete>
$objects
</Delete>
XML

    my $signer = AWS::S3::Signer->new(
        s3           => $s->s3,
        method       => 'POST',
        uri          => $s->_uri,
        content      => \$xml,
        content_type => '',
    );

    $s->_send_request(
        $signer->method => $signer->uri => {
            Authorization => $signer->auth_header,
            Date          => $signer->date,
            'content-md5' => $signer->content_md5,
        },
        $xml
    );

lib/AWS/S3/Request/SetBucketAccessControl.pm  view on Meta::CPAN

                'x-amz-acl'   => $s->acl_short
            },
            $s->acl_xml
        );
    } elsif ( $s->acl_xml ) {
        my $signer = AWS::S3::Signer->new(
            s3             => $s->s3,
            method         => 'PUT',
            uri            => $s->protocol . '://' . $s->bucket . '.' . $s->endpoint . '/?acl',
            content        => \$s->acl_xml,
            'content-type' => 'text/xml',
        );
        return $s->_send_request(
            $signer->method => $signer->uri => {
                Authorization => $signer->auth_header,
                Date          => $signer->date,
            },
            $s->acl_xml
        );
    }    # end if()
}    # end request()

lib/AWS/S3/Request/SetBucketPolicy.pm  view on Meta::CPAN

has '+_expect_nothing' => ( default => 1 );

sub request {
    my $s = shift;

    my $signer = AWS::S3::Signer->new(
        s3           => $s->s3,
        method       => 'PUT',
        uri          => $s->_uri,
        content      => \$s->policy,
        content_type => '',
        content_md5  => '',
    );

    #warn "SetPolicy.string_to_sign(" . $signer->string_to_sign . ")";
    $s->_send_request(
        $signer->method => $signer->uri => {
            Authorization => $signer->auth_header,
            Date          => $signer->date,
        },
        $s->policy

lib/AWS/S3/Request/SetFileContents.pm  view on Meta::CPAN

    isa      => 'Str',
    required => 1,
);

has 'file' => (
    is       => 'ro',
    isa      => 'AWS::S3::File',
    required => 1,
);

has 'content_type' => (
    is       => 'ro',
    isa      => 'Str',
    required => 1,
    lazy     => 1,
    default  => sub { 'binary/octet-stream' },
);

has '+_expect_nothing' => ( default => 0 );

sub request {

lib/AWS/S3/Request/SetFileContents.pm  view on Meta::CPAN

        $contents = $s->file->contents;
    }    # end if()

    my %other_args = ();
    $other_args{'x-amz-server-side-encryption'} = 'AES256' if $s->file->is_encrypted;

    my $signer = AWS::S3::Signer->new(
        s3           => $s->s3,
        method       => 'PUT',
        uri          => $s->protocol . '://' . $s->bucket . '.' . $s->endpoint . '/' . $s->file->key,
        content_type => $s->content_type,
        content      => $contents,
        headers      => [ 'x-amz-storage-class', $s->file->storage_class ],
    );
    $s->_send_request(
        $signer->method => $signer->uri => {
            Authorization         => $signer->auth_header,
            Date                  => $signer->date,
            'content-type'        => $s->content_type,
            'content-md5'         => $signer->content_md5,
            'x-amz-storage-class' => $s->file->storage_class,
        },
        $$contents
    );
}    # end request()

__PACKAGE__->meta->make_immutable;

lib/AWS/S3/ResponseParser.pm  view on Meta::CPAN

        }
    }
);

has 'response' => (
    is       => 'ro',
    isa      => 'HTTP::Response',
    required => 1,
);

has 'type' => (
    is       => 'ro',
    isa      => 'Str',
    required => 1,
);

has 'libxml' => (
    is       => 'ro',
    isa      => 'XML::LibXML',
    required => 1,
    default  => sub { return XML::LibXML->new() },

lib/AWS/S3/ResponseParser.pm  view on Meta::CPAN


has 'friendly_error' => (
    is       => 'ro',
    isa      => 'Maybe[Str]',
    lazy     => 1,
    required => 0,
    default  => sub {
        my $s = shift;

        return unless $s->error_code || $s->error_message;
        $s->type . " call had errors: [" . $s->error_code . "] " . $s->error_message;
    }
);

sub _parse_errors {
    my $self = shift;

    my $src = $self->response->content;

    # Do not try to parse non-xml:
    unless ( $src =~ m/^[[:space:]]*</s ) {

lib/AWS/S3/Roles/Request.pm  view on Meta::CPAN

use URI::Escape qw/ uri_escape /;
use AWS::S3::Signer::V4;
use Log::Any qw( $LOG );

has 's3' => (
    is       => 'ro',
    isa      => 'AWS::S3',
    required => 1,
);

has 'type' => (
    is       => 'ro',
    isa      => 'Str',
    required => 1,
);

has 'protocol' => (
    is      => 'ro',
    isa     => 'Str',
    lazy    => 1,
    default => sub {

lib/AWS/S3/Roles/Request.pm  view on Meta::CPAN

  $signer->sign($request, $s->s3->region, $digest);
  $request;
}

sub parse_response {
    my ( $self, $res ) = @_;

    AWS::S3::ResponseParser->new(
        response       => $res,
        expect_nothing => $self->_expect_nothing,
        type           => $self->type,
    );
}

1;

lib/AWS/S3/Signer.pm  view on Meta::CPAN

has 'string_to_sign' => (
    is       => 'ro',
    isa      => 'Str',
    lazy     => 1,
    default  => sub {
        my $s = shift;

        join "\n",
          (
            $s->method, $s->content_md5,
            $s->content ? $s->content_type : '',
            $s->date || '',
            ( join "\n", grep { $_ } ( $s->canonicalized_amz_headers, $s->canonicalized_resource ) )
          );
    }
);

has 'canonicalized_amz_headers' => (
    is      => 'ro',
    isa     => 'Str',
    lazy    => 1,

lib/AWS/S3/Signer.pm  view on Meta::CPAN


        if ( my ( $resource ) =
            ( $s->uri->query || '' ) =~ m{[&]*(acl|website|location|policy|delete|lifecycle)(?!\=)} )
        {
            $str .= '?' . $resource;
        }    # end if()
        return $str;
    }
);

has 'content_type' => (
    is       => 'ro',
    isa      => 'Str',
	lazy     => 1,
    default  => sub {
        my $s = shift;
        return '' if $s->method eq 'GET';
        return '' unless $s->content;
        return 'text/plain';
    }
);

t/010_basic.t  view on Meta::CPAN

    foreach( sort keys %info )
    {
      ok ! eval {$bucket->file($_)}, "bucket(@{[ $bucket->name ]}).file($_) doesn't exist";
    }# end foreach()
#    map {
#      ok $bucket->file($_)->delete && ! $bucket->file($_), "bucket.file($_).delete worked"
#    } sort keys %info;
  };
  
  
  # proof content type reading and writing
  CONTENT_TYPE: {
    
    foreach my $ct( qw( text/plain image/jpeg application/zip ) ) {
      
      # write file with specific content type
      ( my $ct_name = $ct ) =~ s#/#-#;
      ok( $bucket->add_file(
        key         => "$ct_name.dat",
        contents    => \( 'This is '. $ct ),
        contenttype => $ct
      ), "Put file with content type $ct" );
      
      # read file
      my $ct_file = $bucket->file( "$ct_name.dat" );
      ok( $ct_file && $ct_file->contenttype eq $ct, 'Content type '. $ct. ' read' );
      
      # change content type
      $ct_file->update( contenttype => 'text/csv' );
      $ct_file = $bucket->file( "$ct_name.dat" );
      ok( $ct_file && $ct_file->contenttype eq 'text/csv', 'Content type '. $ct. ' changed to text/csv' );
      
      # remove file
      $ct_file->delete();
    }
  };
  
  # Cleanup:
  ok $bucket->delete, 'bucket.delete succeeds when bucket IS empty.';
}# end if()

t/aws/s3/file.t  view on Meta::CPAN

can_ok(
    $file,
    qw/
        key
        bucket
        size
        etag
        owner
        storage_class
        lastmodified
        contenttype
        is_encrypted
        contents
    /,
);

note( "attributes" );
isa_ok( $file->bucket,'AWS::S3::Bucket','bucket' );
is( $file->key,$path . $key,'key' );
is( $file->size,'18','size' );
isa_ok( $file->etag,'main','etag' );
is( $file->owner,undef,'owner' );
is( $file->storage_class,'STANDARD','storage_class' );
is( $file->lastmodified,undef,'lastmodified' );
is( $file->contenttype,'binary/octet-stream','contenttype' );
is( $file->is_encrypted,0,'is_encrypted' );
isa_ok( $file->contents,'SCALAR','contents' );

note( "methods" );
ok( !$file->update,'update without args' );
ok( $file->update( contents => \'new contents' ),'update with args' );

like(
    $file->signed_url( 1406712744 ),
	qr/X-Amz-Algorithm.*X-Amz-Credential.*X-Amz-Date.*X-Amz-Signature/,

t/aws/s3/http_request.t  view on Meta::CPAN


can_ok(
    $request,
    qw/
		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' );

t/aws/s3/http_request.t  view on Meta::CPAN

        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.t  view on Meta::CPAN

    qw/
        s3
        method
        bucket_name
        uri
        headers
        date
        string_to_sign
        canonicalized_amz_headers
        canonicalized_resource
        content_type
        content_md5
        content
        content_length
        signature
    /,
);


note( "attributes" );
isa_ok( $signer->s3,'AWS::S3' );

t/aws/s3/signer.t  view on Meta::CPAN

isa_ok( $signer->uri,'URI' );
cmp_deeply( $signer->headers,[],'headers' );
like( $signer->date,qr/\w+, +\d{1,2} \w+ \d{4} \d{2}:\d{2}:\d{2}/,'date' );
is(
    $signer->string_to_sign,
    "HEAD\nXrY7u+Ae7tCTyyK7j1rNww==\ntext/plain\n".$signer->date."\n/maibucket/boz",
    'string_to_sign'
);
is( $signer->canonicalized_amz_headers,'','canonicalized_amz_headers' );
is( $signer->canonicalized_resource,'/maibucket/boz','canonicalized_resource' );
is( $signer->content_type,'text/plain','content_type' );
is( $signer->content_md5,'XrY7u+Ae7tCTyyK7j1rNww==','content_md5' );
is( ${ $signer->content },'hello world','content' );
is( $signer->content_length,11,'content_length' );
like( $signer->signature,qr/^.{28}$/,'signature' );

note( "methods" );
like( $signer->auth_header,qr/AWS foo:.{28}/,'auth_header' );

done_testing();

t/aws/s3/signer/v4.t  view on Meta::CPAN

is( $request->method,         'POST',              'request method correct' );
is( $request->header('Host'), 'iam.amazonaws.com', 'host correct' );
is( $request->header('X-Amz-Date'), '20140101T060000Z', 'timestamp correct' );
is(
    $request->content,
    'Action=ListUsers&Version=2010-05-08',
    'payload correct'
);
is(
    $request->header('Authorization'),
'AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20140101/us-east-1/iam/aws4_request, SignedHeaders=content-length;content-type;host;x-amz-date, Signature=0233049369ae675cea7616efa5d2e5216c37a4b1496a36595f32181f078e3549',
    'signature correct'
);

$request = GET( 'https://iam.amazonaws.com?Action=ListUsers&Version=2010-05-08',
    Date => '1 January 2014 01:00:00 -0500' );

my $expected =
'https://iam.amazonaws.com?Action=ListUsers&Version=2010-05-08&X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=AKIDEXAMPLE%2F20140101%2Fus-east-1%2Fiam%2Faws4_request&X-Amz-Date=20140101T060000Z&X-Amz-SignedHeaders=host&X-Amz-Signature=9d0b832ec5c5...

is( $signer->signed_url($request),

t/aws/s3/signer/v4.t  view on Meta::CPAN

  Date => '1 January 2014 01:00:00 -0500'
);

is( $signer->signed_url($request), $expected, 'domain bucket url' );

$request = POST('https://cognito-identity.us-east-1.amazonaws.com',
		   Date    => '1 January 2014 01:00:00 -0500');

$signer->sign($request);

is($request->header('Authorization'),'AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20140101/us-east-1/cognito-identity/aws4_request, SignedHeaders=content-length;content-type;host;x-amz-date, Signature=047c9335c6a34448efc59c2a1813711602e208dcb42ae95cd3b88...

exit 0;



( run in 2.165 seconds using v1.01-cache-2.11-cpan-39a47a84364 )