| line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
|
1
|
|
|
|
|
|
|
package Respite::Client; |
|
2
|
|
|
|
|
|
|
|
|
3
|
|
|
|
|
|
|
# Respite::Client - Generic class for running remote services |
|
4
|
|
|
|
|
|
|
|
|
5
|
4
|
|
|
4
|
|
1511
|
use strict; |
|
|
4
|
|
|
|
|
6
|
|
|
|
4
|
|
|
|
|
133
|
|
|
6
|
4
|
|
|
4
|
|
15
|
use warnings; |
|
|
4
|
|
|
|
|
7
|
|
|
|
4
|
|
|
|
|
250
|
|
|
7
|
4
|
|
|
4
|
|
17
|
use base 'Respite::Common'; # Default _configs |
|
|
4
|
|
|
|
|
4
|
|
|
|
4
|
|
|
|
|
496
|
|
|
8
|
4
|
|
|
4
|
|
8533
|
use IO::Socket::SSL (); |
|
|
4
|
|
|
|
|
355499
|
|
|
|
4
|
|
|
|
|
213
|
|
|
9
|
4
|
|
|
4
|
|
40
|
use Time::HiRes qw(sleep); |
|
|
4
|
|
|
|
|
11
|
|
|
|
4
|
|
|
|
|
58
|
|
|
10
|
4
|
|
|
4
|
|
440
|
use Digest::MD5 qw(md5_hex); |
|
|
4
|
|
|
|
|
11
|
|
|
|
4
|
|
|
|
|
1797
|
|
|
11
|
|
|
|
|
|
|
|
|
12
|
|
|
|
|
|
|
BEGIN { |
|
13
|
4
|
50
|
|
4
|
|
25
|
if (! eval { require Throw }) { |
|
|
4
|
|
|
|
|
34
|
|
|
14
|
0
|
|
|
|
|
0
|
*Throw_::TO_JSON = sub { +{%{$_[0]}} }; |
|
|
0
|
|
|
|
|
0
|
|
|
|
0
|
|
|
|
|
0
|
|
|
15
|
0
|
0
|
0
|
|
|
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" }; |
|
|
0
|
0
|
0
|
|
|
0
|
|
|
|
0
|
|
|
|
|
0
|
|
|
|
0
|
|
|
|
|
0
|
|
|
|
0
|
|
|
|
|
0
|
|
|
|
0
|
|
|
|
|
0
|
|
|
16
|
0
|
0
|
|
|
|
0
|
*throw = *Throw_::throw = sub { my ($m,$a,$l)=@_; $a=ref($m) ? $m : {%{$a||{}}, error => $m}; |
|
|
0
|
0
|
|
|
|
0
|
|
|
|
0
|
|
|
|
|
0
|
|
|
17
|
0
|
0
|
0
|
|
|
0
|
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_' }; |
|
|
0
|
|
0
|
|
|
0
|
|
|
|
0
|
|
|
|
|
0
|
|
|
|
0
|
|
|
|
|
0
|
|
|
|
0
|
|
|
|
|
0
|
|
|
|
0
|
|
|
|
|
0
|
|
|
18
|
0
|
|
|
|
|
0
|
overload::OVERLOAD('Throw_', '""' => \&Throw_::_str, fallback => 1); |
|
19
|
4
|
|
|
|
|
51
|
} else { Throw->import('throw') } |
|
20
|
|
|
|
|
|
|
} |
|
21
|
|
|
|
|
|
|
|
|
22
|
10
|
50
|
33
|
10
|
1
|
89
|
sub service_name { $_[0]->{'service_name'} || $_[0]->{'service'} || throw "Missing service_name" } |
|
23
|
|
|
|
|
|
|
|
|
24
|
|
|
|
|
|
|
sub run_method { |
|
25
|
5
|
|
|
5
|
0
|
14
|
my $self = shift; |
|
26
|
5
|
|
|
|
|
24
|
my $name = $self->service_name; |
|
27
|
5
|
|
33
|
|
|
34
|
my $method = shift || throw "Missing $name service method", undef, 1; |
|
28
|
5
|
|
50
|
|
|
23
|
my $args = shift || {}; |
|
29
|
5
|
50
|
|
|
|
25
|
throw "Invalid $name service args", {method => $method, args => $args}, 1 if ref($args) ne 'HASH'; |
|
30
|
5
|
|
33
|
|
|
190
|
local $args->{'_i'} = $self->{'remote_ip'} || $ENV{'REMOTE_ADDR'} || (($ENV{'REALUSER'} || $ENV{'SUDO_USER'}) ? 'sudo' : 'cmdline'); |
|
31
|
5
|
|
50
|
|
|
1122
|
local $args->{'_w'} = $self->{'remote_user'} || $ENV{'REALUSER'} || $ENV{'SUDO_USER'} || $ENV{'REMOTE_USER'} || $ENV{'USER'} || (getpwuid($<))[0] || '-unknown-'; |
|
32
|
5
|
50
|
33
|
|
|
62
|
local $args->{'_t'} = $self->{'token'} if !$args->{'_t'} && $self->{'token'}; |
|
33
|
5
|
50
|
100
|
|
|
48
|
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); |
|
|
5
|
|
|
|
|
11
|
|
|
|
5
|
|
|
|
|
255
|
|
|
|
5
|
|
|
|
|
39
|
|
|
34
|
5
|
50
|
|
|
|
24
|
local $self->{'flat'} = exists($args->{'_flat'}) ? delete($args->{'_flat'}) : $self->config(flat => undef, $name); |
|
35
|
5
|
50
|
|
|
|
16
|
return $self->_remote_call($method, $args) if $self->_needs_remote($method); |
|
36
|
0
|
|
|
|
|
0
|
return $self->_local_call( $method, $args); |
|
37
|
|
|
|
|
|
|
} |
|
38
|
|
|
|
|
|
|
|
|
39
|
|
|
|
|
|
|
sub _needs_remote { |
|
40
|
10
|
|
|
10
|
|
92
|
my ($self, $method) = @_; |
|
41
|
10
|
|
|
|
|
71
|
return $method !~ /(^local_|_local$)/; |
|
42
|
|
|
|
|
|
|
} |
|
43
|
|
|
|
|
|
|
|
|
44
|
|
|
|
|
|
|
sub _local_call { |
|
45
|
0
|
|
|
0
|
|
0
|
my ($self, $method, $args) = @_; |
|
46
|
0
|
|
|
|
|
0
|
my $name = $self->service_name; |
|
47
|
0
|
|
0
|
|
|
0
|
local $self->{'brand'} ||= $self->api_brand($name); |
|
48
|
|
|
|
|
|
|
my $hash = eval { |
|
49
|
|
|
|
|
|
|
my $code = $self->can("__$method") || throw "Invalid $name service method", {method => $method}, 1; |
|
50
|
|
|
|
|
|
|
return $code->($self, $args); |
|
51
|
0
|
|
0
|
|
|
0
|
} || (ref($@) eq 'HASH' && $@->{'error'} ? $@ : {error => "Trouble running $name service method", service => $name}); |
|
52
|
0
|
|
|
|
|
0
|
return $self->_result({method => $method, args => $args, data => $hash, service => $name, url => 'local'}); |
|
53
|
|
|
|
|
|
|
} |
|
54
|
|
|
|
|
|
|
|
|
55
|
|
|
|
|
|
|
sub config { |
|
56
|
78
|
|
|
78
|
0
|
223
|
my ($self, $key, $def, $name) = @_; |
|
57
|
78
|
|
33
|
|
|
160
|
$name ||= $self->service_name; |
|
58
|
78
|
|
|
|
|
221
|
my $c = $self->_configs($name); |
|
59
|
|
|
|
|
|
|
return exists($self->{$key}) ? $self->{$key} |
|
60
|
|
|
|
|
|
|
: exists($c->{"${name}_service_${key}"}) ? $c->{"${name}_service_${key}"} |
|
61
|
|
|
|
|
|
|
: (ref($c->{"${name}_service"}) && exists $c->{"${name}_service"}->{$key}) ? $c->{"${name}_service"}->{$key} |
|
62
|
|
|
|
|
|
|
: exists($c->{"${name}_${key}"}) ? $c->{"${name}_${key}"} |
|
63
|
78
|
100
|
33
|
|
|
620
|
: (ref($c->{$name}) && exists $c->{$name}->{$key}) ? $c->{$name}->{$key} |
|
|
|
50
|
33
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
64
|
|
|
|
|
|
|
: ref($def) eq 'CODE' ? $def->($self) : $def; |
|
65
|
|
|
|
|
|
|
} |
|
66
|
|
|
|
|
|
|
|
|
67
|
|
|
|
|
|
|
sub api_brand { |
|
68
|
5
|
|
|
5
|
0
|
15
|
my ($self, $name) = @_; |
|
69
|
5
|
|
33
|
|
|
19
|
$name ||= $self->service_name; |
|
70
|
5
|
50
|
|
|
|
16
|
return undef if $self->config(no_brand => undef, $name); ## no critic (ProhibitExplicitReturnUndef) |
|
71
|
5
|
50
|
33
|
5
|
|
68
|
$self->config(brand => sub { eval { config::provider() } || $self->_configs->{'provider'} || do { warn "Missing $name brand"; '-' } }, $name); |
|
|
5
|
|
|
|
|
12
|
|
|
|
5
|
|
|
|
|
155
|
|
|
|
5
|
|
|
|
|
123
|
|
|
|
5
|
|
|
|
|
28
|
|
|
72
|
|
|
|
|
|
|
} |
|
73
|
|
|
|
|
|
|
|
|
74
|
|
|
|
|
|
|
sub _remote_call { |
|
75
|
5
|
|
|
5
|
|
15
|
my ($self, $method, $args) = @_; |
|
76
|
5
|
|
|
|
|
18
|
my $begin = Time::HiRes::time(); |
|
77
|
5
|
|
|
|
|
52
|
my $name = $self->service_name; |
|
78
|
5
|
|
|
|
|
17
|
my $brand = $self->api_brand($name); |
|
79
|
5
|
|
|
58
|
|
48
|
my $val = sub { my ($key, $def) = @_; $self->config($key, $def, $name) }; |
|
|
58
|
|
|
|
|
202
|
|
|
|
58
|
|
|
|
|
114
|
|
|
80
|
5
|
|
|
|
|
16
|
my $no_ssl = $val->(no_ssl => undef); |
|
81
|
5
|
|
|
0
|
|
56
|
my $host = $val->(host => sub {throw "Missing $name service host",undef,1}); |
|
|
0
|
|
|
|
|
0
|
|
|
82
|
5
|
50
|
|
|
|
27
|
my $port = $val->(port => ($no_ssl ? 80 : 443)); |
|
83
|
5
|
50
|
|
5
|
|
40
|
my $path = $val->(path => sub { $name =~ /^(\w+)_service/ ? $1 : $name }); |
|
|
5
|
|
|
|
|
21
|
|
|
84
|
5
|
50
|
|
|
|
21
|
my $pass = $val->(no_sign => undef) ? undef : $val->(pass => undef); # rely on the server to tell us if a password is necessary |
|
85
|
5
|
50
|
|
|
|
22
|
my $utf8 = exists($args->{'_utf8_encoded'}) ? delete($args->{'_utf8_encoded'}) : $val->(utf8_encoded => undef); |
|
86
|
5
|
|
33
|
|
|
14
|
my $enc = $utf8 && (!ref($utf8) || $utf8->{$method}); |
|
87
|
5
|
|
|
|
|
22
|
my $retry = $val->(retry => undef); |
|
88
|
5
|
|
|
|
|
12
|
my $ns = $val->(ns => undef); |
|
89
|
5
|
50
|
|
|
|
15
|
$method = "${ns}_${method}" if $ns; |
|
90
|
5
|
50
|
|
|
|
23
|
my $url = "/$path/$method".($brand ? "/$brand" : ''); |
|
91
|
5
|
|
|
|
|
89
|
my $cookie = $val->(cookie => undef); |
|
92
|
|
|
|
|
|
|
|
|
93
|
5
|
|
|
|
|
10
|
my $req; |
|
94
|
5
|
|
|
0
|
|
119
|
local $SIG{'ALRM'} = sub { die "Timeout on $name\n" }; |
|
|
0
|
|
|
|
|
0
|
|
|
95
|
5
|
|
50
|
|
|
37
|
my $old = alarm($args->{'_timeout'} || $val->(timeout => 120)) || 0; |
|
96
|
5
|
|
|
|
|
23
|
my %head; |
|
97
|
|
|
|
|
|
|
my $hash = eval { |
|
98
|
|
|
|
|
|
|
_decode_utf8_recurse($args) if $enc; |
|
99
|
|
|
|
|
|
|
$req = eval { $self->json->encode($args) } || throw "Trouble encoding $name service json", {msg => $@}, 1; |
|
100
|
|
|
|
|
|
|
my $sign = defined($pass) ? do { my $t = int $begin; "X-Respite-Auth: ".($val->('md5_pass') ? md5_hex($pass) : md5_hex("$pass:$t:$url:".md5_hex($req)).":$t")."\r\n" } : ''; |
|
101
|
|
|
|
|
|
|
$cookie = $cookie ? "Cookie: $cookie\r\n" : ''; |
|
102
|
|
|
|
|
|
|
|
|
103
|
|
|
|
|
|
|
my $sock; |
|
104
|
|
|
|
|
|
|
my $i = 0; |
|
105
|
|
|
|
|
|
|
while (++$i) { |
|
106
|
|
|
|
|
|
|
# Note SSL verify may not work as expected on IO::Socket::SSL versions below v1.46 |
|
107
|
|
|
|
|
|
|
$sock = $no_ssl ? IO::Socket::INET->new("$host:$port") |
|
108
|
|
|
|
|
|
|
: IO::Socket::SSL->new(PeerAddr => $host, PeerPort => $port, SSL_verify_mode => $val->(ssl_verify_mode => 0)); |
|
109
|
|
|
|
|
|
|
last if $sock || !$retry || (Time::HiRes::time() - $begin > 3); |
|
110
|
|
|
|
|
|
|
sleep 0.5; |
|
111
|
|
|
|
|
|
|
} |
|
112
|
|
|
|
|
|
|
if (!$sock) { |
|
113
|
|
|
|
|
|
|
throw "Could not connect to $name service", { |
|
114
|
|
|
|
|
|
|
host => $host, port => $port, url => $url, |
|
115
|
|
|
|
|
|
|
msg => (!$no_ssl && ($IO::Socket::SSL::SSL_ERROR || $!)), detail => "$@", ssl => !$no_ssl, tries => $i, |
|
116
|
|
|
|
|
|
|
}; |
|
117
|
|
|
|
|
|
|
} |
|
118
|
|
|
|
|
|
|
|
|
119
|
|
|
|
|
|
|
my $out = "POST $url HTTP/1.0\r\n${cookie}${sign}Host: $host\r\nContent-length: ".length($req)."\r\nContent-type: application/json\r\n\r\n$req"; |
|
120
|
|
|
|
|
|
|
warn "DEBUG_Respite: Connected to http".($no_ssl?'':'s')."://$host:$port/\n$out\n" if $ENV{'DEBUG_Respite'}; |
|
121
|
|
|
|
|
|
|
print $sock $out; |
|
122
|
|
|
|
|
|
|
my ($len, $type, $line); |
|
123
|
|
|
|
|
|
|
throw "Got non-200 status from $name service", {status => $line, url => $url} if !($line = <$sock>) || $line !~ m{^HTTP/\S+ 200\b}; |
|
124
|
|
|
|
|
|
|
while (defined($line = <$sock>)) { |
|
125
|
|
|
|
|
|
|
$line =~ s/\r?\n$// || throw "Failed to find line termination", {line => $line}; |
|
126
|
|
|
|
|
|
|
last if $line eq ""; |
|
127
|
|
|
|
|
|
|
my ($key, $val) = split /\s*:\s*/, $line, 2; |
|
128
|
|
|
|
|
|
|
$head{$key} = $head{$key} ? ref($head{$key}) ? [@{$head{$key}}, $val] : [$head{$key}, $val] : $val; |
|
129
|
|
|
|
|
|
|
$len = ($val =~ /^\d+$/) ? $val : throw "Invalid content length", {h => \%head} if lc($key) eq 'content-length'; |
|
130
|
|
|
|
|
|
|
} |
|
131
|
|
|
|
|
|
|
throw "Failed to find content length in $name service response" if ! $len; |
|
132
|
|
|
|
|
|
|
throw "Content too large in $name service", {length => $len} if $len > 100_000_000; |
|
133
|
|
|
|
|
|
|
my $data = ''; |
|
134
|
|
|
|
|
|
|
while (1) { |
|
135
|
|
|
|
|
|
|
read($sock, $data, $len, length $data) || throw "Failed to read bytes", {needed => $len, got => length($data)}; |
|
136
|
|
|
|
|
|
|
last if length $data >= $len; |
|
137
|
|
|
|
|
|
|
} |
|
138
|
|
|
|
|
|
|
close $sock; |
|
139
|
|
|
|
|
|
|
alarm($old); |
|
140
|
|
|
|
|
|
|
|
|
141
|
|
|
|
|
|
|
throw "Invalid $name service json object string" if $data !~ /^\s*\{/; |
|
142
|
|
|
|
|
|
|
my $resp = eval { $self->json->decode($data) } || throw "Failed to decode $name service json response data", {msg => $@}; |
|
143
|
|
|
|
|
|
|
_encode_utf8_recurse($resp) if $enc; |
|
144
|
|
|
|
|
|
|
$resp; |
|
145
|
5
|
|
33
|
|
|
12
|
} || do { alarm($old); {error => "Failed to get valid $name service response: $@"} }; |
|
146
|
|
|
|
|
|
|
|
|
147
|
|
|
|
|
|
|
return $self->_result({ |
|
148
|
|
|
|
|
|
|
service => $name, |
|
149
|
|
|
|
|
|
|
method => $method, |
|
150
|
|
|
|
|
|
|
args => $args, |
|
151
|
|
|
|
|
|
|
data => $hash, |
|
152
|
|
|
|
|
|
|
headers => \%head, |
|
153
|
|
|
|
|
|
|
url => $url, host => $host, port => $port, |
|
154
|
|
|
|
|
|
|
brand => $brand, |
|
155
|
|
|
|
|
|
|
elapsed => sprintf('%.05f', Time::HiRes::time() - $begin), |
|
156
|
5
|
50
|
|
|
|
236
|
($self->{'pretty'} ? (pretty => 1) : ()), |
|
157
|
|
|
|
|
|
|
}); |
|
158
|
|
|
|
|
|
|
} |
|
159
|
|
|
|
|
|
|
|
|
160
|
|
|
|
|
|
|
sub _result { |
|
161
|
5
|
|
|
5
|
|
27
|
my ($self, $args) = @_; |
|
162
|
5
|
50
|
|
|
|
33
|
if ($self->{'flat'}) { |
|
163
|
5
|
|
|
|
|
14
|
my $data = $args->{'data'}; |
|
164
|
5
|
50
|
33
|
|
|
75
|
throw {_service => $args->{'service'} || $self->service_name, %$data, ($args->{'pretty'} ? (_pretty => 1) : ())} if $data->{'error'}; |
|
|
|
100
|
|
|
|
|
|
|
165
|
3
|
|
|
|
|
144
|
return $data; |
|
166
|
|
|
|
|
|
|
} |
|
167
|
0
|
|
|
|
|
0
|
return bless $args, $self->_result_class; |
|
168
|
|
|
|
|
|
|
} |
|
169
|
|
|
|
|
|
|
|
|
170
|
0
|
0
|
|
0
|
|
0
|
sub _result_class { shift->{'result_class'} || 'Respite::Client::Result' } |
|
171
|
|
|
|
|
|
|
|
|
172
|
|
|
|
|
|
|
sub _encode_utf8_recurse { |
|
173
|
0
|
|
|
0
|
|
0
|
my $d = shift; |
|
174
|
0
|
0
|
|
|
|
0
|
if (UNIVERSAL::isa($d, 'HASH')) { |
|
|
|
0
|
|
|
|
|
|
|
175
|
0
|
0
|
0
|
|
|
0
|
for my $k (keys %$d) { my $v = $d->{$k}; (ref $v) ? _encode_utf8_recurse($v) : $v and utf8::is_utf8($v) and utf8::encode($d->{$k}) } |
|
|
0
|
0
|
|
|
|
0
|
|
|
|
0
|
|
|
|
|
0
|
|
|
176
|
|
|
|
|
|
|
} elsif (UNIVERSAL::isa($d, 'ARRAY')) { |
|
177
|
0
|
0
|
0
|
|
|
0
|
for my $v (@$d) { (ref $v) ? _encode_utf8_recurse($v) : $v and utf8::is_utf8($v) and utf8::encode($v) } |
|
|
0
|
0
|
|
|
|
0
|
|
|
178
|
|
|
|
|
|
|
} |
|
179
|
|
|
|
|
|
|
} |
|
180
|
|
|
|
|
|
|
|
|
181
|
|
|
|
|
|
|
sub _decode_utf8_recurse { |
|
182
|
0
|
|
|
0
|
|
0
|
my $d = shift; |
|
183
|
0
|
|
0
|
|
|
0
|
my $seen = shift || {}; |
|
184
|
0
|
0
|
|
|
|
0
|
return if $seen->{$d}++; |
|
185
|
0
|
0
|
|
|
|
0
|
if (UNIVERSAL::isa($d, 'HASH')) { |
|
|
|
0
|
|
|
|
|
|
|
186
|
0
|
0
|
0
|
|
|
0
|
for my $k (keys %$d) { my $v = $d->{$k}; (ref $v) ? _decode_utf8_recurse($v, $seen) : $v and !utf8::is_utf8($v) and utf8::decode($d->{$k}) } |
|
|
0
|
0
|
|
|
|
0
|
|
|
|
0
|
|
|
|
|
0
|
|
|
187
|
|
|
|
|
|
|
} elsif (UNIVERSAL::isa($d, 'ARRAY')) { |
|
188
|
0
|
0
|
0
|
|
|
0
|
for my $v (@$d) { (ref $v) ? _decode_utf8_recurse($v, $seen) : $v and !utf8::is_utf8($v) and utf8::decode($v) } |
|
|
0
|
0
|
|
|
|
0
|
|
|
189
|
|
|
|
|
|
|
} |
|
190
|
|
|
|
|
|
|
} |
|
191
|
|
|
|
|
|
|
|
|
192
|
|
|
|
|
|
|
sub AUTOLOAD { |
|
193
|
5
|
|
|
5
|
|
9005
|
my $self = shift; |
|
194
|
5
|
|
50
|
|
|
56
|
my $args = shift || {}; |
|
195
|
5
|
50
|
|
|
|
150
|
my $meth = $Respite::Client::AUTOLOAD =~ /::(\w+)$/ ? $1 : throw "Invalid method\n"; |
|
196
|
5
|
50
|
|
|
|
28
|
throw "Self was not passed while looking up method", {method => $meth, trace => 1} if ! ref $self; |
|
197
|
5
|
0
|
33
|
|
|
50
|
throw "Invalid ".$self->service_name." method \"$meth\"", {trace => 1} if !$self->_needs_remote($meth) && ! $self->can("__${meth}"); |
|
198
|
5
|
|
|
5
|
|
47
|
my $code = sub { $_[0]->run_method($meth => $_[1]) }; |
|
|
5
|
|
|
|
|
31
|
|
|
199
|
4
|
|
|
4
|
|
12397
|
no strict 'refs'; ## no critic |
|
|
4
|
|
|
|
|
9
|
|
|
|
4
|
|
|
|
|
957
|
|
|
200
|
5
|
50
|
|
|
|
35
|
*{ref($self)."::$meth"} = $code if __PACKAGE__ ne ref($self); |
|
|
0
|
|
|
|
|
0
|
|
|
201
|
5
|
|
|
|
|
22
|
return $self->$code($args); |
|
202
|
|
|
|
|
|
|
} |
|
203
|
|
|
|
|
|
|
|
|
204
|
|
|
|
0
|
|
|
sub DESTROY {} |
|
205
|
|
|
|
|
|
|
|
|
206
|
|
|
|
|
|
|
sub run_commandline { |
|
207
|
0
|
|
|
0
|
0
|
|
my $class = shift; |
|
208
|
0
|
0
|
|
|
|
|
my $args = ref($_[0]) ? shift : {@_}; |
|
209
|
0
|
0
|
|
|
|
|
my $self = ref($class) ? $class : $class->new({%$args}); |
|
210
|
0
|
|
|
|
|
|
require Respite::CommandLine; |
|
211
|
0
|
|
|
0
|
|
|
Respite::CommandLine->run({dispatch_factory => sub { $self }}); |
|
|
0
|
|
|
|
|
|
|
|
212
|
|
|
|
|
|
|
} |
|
213
|
|
|
|
|
|
|
|
|
214
|
|
|
|
|
|
|
###----------------------------------------------------------------### |
|
215
|
|
|
|
|
|
|
|
|
216
|
|
|
|
|
|
|
{ |
|
217
|
|
|
|
|
|
|
package Respite::Client::Result; |
|
218
|
4
|
|
|
4
|
|
31
|
use overload 'bool' => sub { ! shift->error }, '""' => \&as_string, fallback => 1; |
|
|
4
|
|
|
0
|
|
5
|
|
|
|
4
|
|
|
|
|
53
|
|
|
|
0
|
|
|
|
|
0
|
|
|
219
|
0
|
|
|
0
|
|
|
sub error { shift->data->{'error'} } |
|
220
|
0
|
|
|
0
|
|
|
sub TO_JSON { return {%{$_[0]}} } |
|
|
0
|
|
|
|
|
|
|
|
221
|
|
|
|
|
|
|
sub as_string { |
|
222
|
0
|
|
|
0
|
|
|
my $self = shift; |
|
223
|
0
|
0
|
|
|
|
|
if (my $err = $self->error) { |
|
224
|
0
|
|
|
|
|
|
my $data = $self->data; |
|
225
|
0
|
0
|
|
|
|
|
my $p = defined($Respite::Client::pretty) ? $Respite::Client::pretty : $self->{'pretty'}; |
|
226
|
0
|
|
|
|
|
|
local $data->{'error'}; delete $data->{'error'}; |
|
|
0
|
|
|
|
|
|
|
|
227
|
0
|
0
|
|
|
|
|
return !scalar keys %$self ? $err : "$err: ".($p ? Respite::Client::jsop():Respite::Client::json())->encode({%$data}); |
|
|
|
0
|
|
|
|
|
|
|
228
|
|
|
|
|
|
|
} |
|
229
|
0
|
|
|
|
|
|
return "Called $self->{'service'} service method $self->{'method'}"; |
|
230
|
|
|
|
|
|
|
} |
|
231
|
0
|
|
0
|
0
|
|
|
sub data { shift->{'data'} ||= {} } |
|
232
|
|
|
|
|
|
|
} |
|
233
|
|
|
|
|
|
|
|
|
234
|
|
|
|
|
|
|
1; |