File Coverage

blib/lib/MojoX/JSONRPC2/HTTP.pm
Criterion Covered Total %
statement 57 58 98.2
branch 29 32 90.6
condition 17 18 94.4
subroutine 16 16 100.0
pod 4 4 100.0
total 123 128 96.0


line stmt bran cond sub pod time code
1             package MojoX::JSONRPC2::HTTP;
2              
3 3     3   845135 use Mojo::Base -base;
  3         421499  
  3         34  
4 3     3   795 use Carp;
  3         7  
  3         290  
5              
6             our $VERSION = 'v2.0.2';
7              
8 3     3   2646 use Mojo::UserAgent;
  3         551321  
  3         26  
9 3     3   1779 use JSON::RPC2::Client;
  3         8845  
  3         118  
10 3     3   2258 use JSON::XS;
  3         8649  
  3         207  
11 3     3   80 use Scalar::Util qw(weaken);
  3         11  
  3         154  
12              
13 3     3   20 use constant REQUEST_TIMEOUT => 30;
  3         7  
  3         198  
14 3     3   19 use constant HTTP_200 => 200;
  3         7  
  3         3441  
15              
16              
17             has url => sub { croak '->url() not defined' };
18             has method => 'POST';
19             has type => 'application/json';
20             has headers => sub { {} };
21             has ua => sub {
22             Mojo::UserAgent->new
23             ->inactivity_timeout(0)
24             ->request_timeout(REQUEST_TIMEOUT)
25             };
26             has _client => sub { JSON::RPC2::Client->new };
27              
28 11     11 1 6131 sub call { return shift->_request('call', @_) }
29 2     2 1 1287 sub call_named { return shift->_request('call_named', @_) }
30 3     3 1 1456 sub notify { return shift->_request('notify', @_) }
31 2     2 1 908 sub notify_named { return shift->_request('notify_named', @_) }
32              
33             sub _request {
34 18     18   76 my ($self, $func, $method, @params) = @_;
35             # work either in blocking mode or non-blocking (if has \&cb in last param)
36 18 100 100     155 my $cb = @params && ref $params[-1] eq 'CODE' ? pop @params : undef;
37              
38             # if $func is notify/notify_named then $call will be undef
39 18         89 my ($json_request, $call) = $self->_client->$func($method, @params);
40              
41 18         1044 my $tx; # will be set when in blocking mode
42 18         92 weaken(my $this = $self);
43 18 100       103 if ('GET' eq uc $self->method) {
44 4         73 my $json = decode_json($json_request);
45 4 100       19 if (exists $json->{params}) {
46 3         47 $json->{params} = encode_json($json->{params});
47             }
48             $tx = $self->ua->get(
49             $self->url,
50             {
51             'Content-Type' => $self->type,
52             'Accept' => $self->type,
53 4         74 %{ $self->headers },
54             },
55             form => $json,
56 4 50   2   20 ($cb ? sub {$this && $this->_response($cb, $call, @_)} : ()),
  2 100       14862  
57             );
58             }
59             else {
60             $tx = $self->ua->post(
61             $self->url,
62             {
63             'Content-Type' => $self->type,
64             'Accept' => $self->type,
65 13         209 %{ $self->headers },
66             },
67             $json_request,
68 14 50   2   134 ($cb ? sub {$this && $this->_response($cb, $call, @_)} : ()),
  2 100       18078  
69             );
70             }
71 17 100       244337 return ($cb ? () : $self->_response($cb, $call, undef, $tx));
72             }
73              
74             sub _response {
75 17     17   78 my ($self, $cb, $call, undef, $tx) = @_;
76 17         53 my $is_notify = !defined $call;
77              
78 17         57 my ($failed, $result, $error);
79 17         67 my $res = $tx->res;
80             # transport error (we don't have HTTP reply)
81 17 100 100     127 if ($res->error && !$res->error->{code}) {
    100 100        
    50 100        
    100 66        
82 1         19 $failed = $res->error->{message};
83             }
84             # use HTTP code as error message instead of 'Parse error' for:
85             # - strange HTTP reply code or non-empty content (notify)
86             elsif ($is_notify && !($res->is_success && $res->body =~ /\A\s*\z/ms)) {
87 3         182 $failed = sprintf '%d %s', $res->code, $res->message;
88             }
89             # - strange HTTP reply code or non-json content (call)
90             elsif (!$is_notify && !($res->is_success && $res->body =~ /\A\s*[{\[]/ms)) {
91 0         0 $failed = sprintf '%d %s', $res->code, $res->message;
92             }
93             elsif (!$is_notify) {
94 11         760 ($failed, $result, $error) = $self->_client->response($res->body);
95             }
96              
97 17 100 100     1421 if ($failed && $call) {
98 1         4 $self->_client->cancel($call);
99             }
100              
101 17 100       91 if ($cb) {
102 4 100       22 return $is_notify ? $cb->($failed) : $cb->($failed, $result, $error);
103             } else {
104 13 100       289 return $is_notify ? $failed : ($failed, $result, $error);
105             }
106             }
107              
108              
109             1; # Magic true value required at end of module
110             __END__