Respite
view release on metacpan or search on metacpan
lib/Respite/Client.pm view on Meta::CPAN
package Respite::Client;
# Respite::Client - Generic class for running remote services
use strict;
use warnings;
use base 'Respite::Common'; # Default _configs
use IO::Socket::SSL ();
use Time::HiRes qw(sleep);
use Digest::MD5 qw(md5_hex);
BEGIN {
if (! eval { require Throw }) {
*Throw_::TO_JSON = sub { +{%{$_[0]}} };
*Throw_::_str = sub { my ($s) = @_; my ($e,$p) = delete(@$s{qw(error _pretty)}); $e||="throw"; $e .= ': '.($p||$Throw::pretty?jsop():json())->encode($s) if %$s; "$e\n" };
*throw = *Throw_::throw = sub { my ($m,$a,$l)=@_; $a=ref($m) ? $m : {%{$a||{}}, error => $m};
do {my$i=$l||0;$i++while __PACKAGE__ eq caller$i; $a->{'trace'}=sprintf "%s at %s line %s\n",(caller$i)[3,1,2]} if $a->{'trace'}||$l; die bless $a, 'Throw_' };
overload::OVERLOAD('Throw_', '""' => \&Throw_::_str, fallback => 1);
} else { Throw->import('throw') }
}
sub service_name { $_[0]->{'service_name'} || $_[0]->{'service'} || throw "Missing service_name" }
sub run_method {
my $self = shift;
my $name = $self->service_name;
my $method = shift || throw "Missing $name service method", undef, 1;
my $args = shift || {};
throw "Invalid $name service args", {method => $method, args => $args}, 1 if ref($args) ne 'HASH';
local $args->{'_i'} = $self->{'remote_ip'} || $ENV{'REMOTE_ADDR'} || (($ENV{'REALUSER'} || $ENV{'SUDO_USER'}) ? 'sudo' : 'cmdline');
local $args->{'_w'} = $self->{'remote_user'} || $ENV{'REALUSER'} || $ENV{'SUDO_USER'} || $ENV{'REMOTE_USER'} || $ENV{'USER'} || (getpwuid($<))[0] || '-unknown-';
local $args->{'_t'} = $self->{'token'} if !$args->{'_t'} && $self->{'token'};
local $args->{'_c'} = do {my $i = my $c = 0; $c = [(caller $i++)[0..3]] while !$i || $c->[0]->isa(__PACKAGE__); join '; ', @$c} if ! $self->config(no_trace => undef, $name);
local $self->{'flat'} = exists($args->{'_flat'}) ? delete($args->{'_flat'}) : $self->config(flat => undef, $name);
return $self->_remote_call($method, $args) if $self->_needs_remote($method);
return $self->_local_call( $method, $args);
}
sub _needs_remote {
my ($self, $method) = @_;
return $method !~ /(^local_|_local$)/;
}
sub _local_call {
my ($self, $method, $args) = @_;
my $name = $self->service_name;
local $self->{'brand'} ||= $self->api_brand($name);
my $hash = eval {
my $code = $self->can("__$method") || throw "Invalid $name service method", {method => $method}, 1;
return $code->($self, $args);
} || (ref($@) eq 'HASH' && $@->{'error'} ? $@ : {error => "Trouble running $name service method", service => $name});
return $self->_result({method => $method, args => $args, data => $hash, service => $name, url => 'local'});
}
sub config {
my ($self, $key, $def, $name) = @_;
$name ||= $self->service_name;
my $c = $self->_configs($name);
return exists($self->{$key}) ? $self->{$key}
: exists($c->{"${name}_service_${key}"}) ? $c->{"${name}_service_${key}"}
: (ref($c->{"${name}_service"}) && exists $c->{"${name}_service"}->{$key}) ? $c->{"${name}_service"}->{$key}
: exists($c->{"${name}_${key}"}) ? $c->{"${name}_${key}"}
: (ref($c->{$name}) && exists $c->{$name}->{$key}) ? $c->{$name}->{$key}
: ref($def) eq 'CODE' ? $def->($self) : $def;
}
sub api_brand {
my ($self, $name) = @_;
$name ||= $self->service_name;
return undef if $self->config(no_brand => undef, $name); ## no critic (ProhibitExplicitReturnUndef)
$self->config(brand => sub { eval { config::provider() } || $self->_configs->{'provider'} || do { warn "Missing $name brand"; '-' } }, $name);
}
sub _remote_call {
my ($self, $method, $args) = @_;
my $begin = Time::HiRes::time();
my $name = $self->service_name;
my $brand = $self->api_brand($name);
my $val = sub { my ($key, $def) = @_; $self->config($key, $def, $name) };
my $no_ssl = $val->(no_ssl => undef);
my $host = $val->(host => sub {throw "Missing $name service host",undef,1});
my $port = $val->(port => ($no_ssl ? 80 : 443));
my $path = $val->(path => sub { $name =~ /^(\w+)_service/ ? $1 : $name });
my $pass = $val->(no_sign => undef) ? undef : $val->(pass => undef); # rely on the server to tell us if a password is necessary
my $utf8 = exists($args->{'_utf8_encoded'}) ? delete($args->{'_utf8_encoded'}) : $val->(utf8_encoded => undef);
my $enc = $utf8 && (!ref($utf8) || $utf8->{$method});
my $retry = $val->(retry => undef);
my $ns = $val->(ns => undef);
$method = "${ns}_${method}" if $ns;
my $url = "/$path/$method".($brand ? "/$brand" : '');
my $cookie = $val->(cookie => undef);
( run in 1.640 second using v1.01-cache-2.11-cpan-39bf76dae61 )