Azure-Storage-Blob-Client

 view release on metacpan or  search on metacpan

lib/Azure/Storage/Blob/Client/Caller.pm  view on Meta::CPAN

package Azure::Storage::Blob::Client::Caller;
use Moose;
use Data::Dumper;
use LWP::UserAgent;
use HTTP::Tiny;
use HTTP::Request;
use HTTP::Headers;
use HTTP::Date;
use Digest::SHA qw(hmac_sha256_base64);
use MIME::Base64;
use Encode;

use Azure::Storage::Blob::Client::Service::Signer;
use Azure::Storage::Blob::Client::Exception;

has user_agent => (
  is => 'ro',
  lazy => 1,
  default => sub { return LWP::UserAgent->new() },
);

has signer => (
  is => 'ro',
  isa => 'Azure::Storage::Blob::Client::Service::Signer',
  lazy => 1,
  default => sub { Azure::Storage::Blob::Client::Service::Signer->new() },
);

sub request {
  my ($self, $account_name, $account_key, $call_object) = @_;

  my $request = $self->_prepare_request($account_name, $account_key, $call_object);
  my $response = $self->user_agent->request($request);
  $self->_handle_storage_account_api_exceptions($response);

  return $call_object->parse_response($response);
}

sub _prepare_request {
  my ($self, $account_name, $account_key, $call_object) = @_;

  if (
    $call_object->operation ne 'DeleteBlob' and
    $call_object->operation ne 'GetBlobProperties' and
    $call_object->operation ne 'ListBlobs' and
    $call_object->operation ne 'PutBlob'
  ) {
    die 'Unimplemented.';
  }

  my $url_encoded_parameters = HTTP::Tiny->new->www_form_urlencode(
    $call_object->serialize_uri_parameters(),
  );
  my $url = $url_encoded_parameters
    ? sprintf("%s&%s", $call_object->endpoint, $url_encoded_parameters)
    : $call_object->endpoint;

  my $body = $self->_build_body_content($call_object);
  my $headers = $self->_build_headers($call_object, $body);
  my $request = HTTP::Request->new($call_object->method, $url, $headers, $body);
  $self->_sign_request($request, $account_name, $account_key, $call_object);

  return $request;
}

sub _handle_storage_account_api_exceptions {
  my ($self, $response) = @_;

  return unless ($response->code >= 400);

  if ($response->header('x-ms-error-code')) {
    Azure::Storage::Blob::Client::Exception->throw({
      code => $response->header('x-ms-error-code'),
      message => $response->message,
    });
  }
  else {
    Azure::Storage::Blob::Client::Exception->throw({
      code => 'UnknownAzureStorageAPIError',
      message => 'Unknown Azure Storage Blob API error (x-ms-error-code not found in the '
                .' response). Response: '.Dumper($response),
    });
  }
}

sub _build_body_content {
  my ($self, $call_object) = @_;

  return join('',
    values %{ $call_object->serialize_body_parameters() }
  );
}

sub _build_headers {
  my ($self, $call_object, $body) = @_;

  return HTTP::Headers->new(
    'Date'=> HTTP::Date::time2str(),
    $body ? ('Content-Length' => length(Encode::encode_utf8($body))) : (),
    %{ $call_object->serialize_header_parameters() },
  );
}

sub _sign_request {
  my ($self, $request, $account_name, $account_key, $call_object) = @_;
  $request->header('Authorization',
    sprintf(
      "SharedKey %s:%s",
      $call_object->account_name,
      $self->signer->calculate_signature(
        $request,
        $account_name,
        $account_key,
      ),
    ),
  );
}

__PACKAGE__->meta->make_immutable();



( run in 1.049 second using v1.01-cache-2.11-cpan-39bf76dae61 )