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   656601 use Mojo::Base -base;
  3         316906  
  3         20  
4 3     3   483 use Carp;
  3         7  
  3         232  
5              
6             our $VERSION = 'v2.0.3';
7              
8 3     3   987 use Mojo::UserAgent;
  3         432780  
  3         22  
9 3     3   1366 use JSON::RPC2::Client;
  3         7073  
  3         93  
10 3     3   1713 use JSON::XS;
  3         6813  
  3         162  
11 3     3   19 use Scalar::Util qw(weaken);
  3         5  
  3         115  
12              
13 3     3   16 use constant REQUEST_TIMEOUT => 30;
  3         5  
  3         151  
14 3     3   15 use constant HTTP_200 => 200;
  3         6  
  3         2778  
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 4659 sub call { return shift->_request('call', @_) }
29 2     2 1 831 sub call_named { return shift->_request('call_named', @_) }
30 3     3 1 870 sub notify { return shift->_request('notify', @_) }
31 2     2 1 511 sub notify_named { return shift->_request('notify_named', @_) }
32              
33             sub _request {
34 18     18   50 my ($self, $func, $method, @params) = @_;
35             # work either in blocking mode or non-blocking (if has \&cb in last param)
36 18 100 100     88 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         50 my ($json_request, $call) = $self->_client->$func($method, @params);
40              
41 18         702 my $tx; # will be set when in blocking mode
42 18         50 weaken(my $this = $self);
43 18 100       49 if ('GET' eq uc $self->method) {
44 4         40 my $json = decode_json($json_request);
45 4 100       15 if (exists $json->{params}) {
46 3         29 $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         45 %{ $self->headers },
54             },
55             form => $json,
56 4 50   2   15 ($cb ? sub {$this && $this->_response($cb, $call, @_)} : ()),
  2 100       9039  
57             );
58             }
59             else {
60             $tx = $self->ua->post(
61             $self->url,
62             {
63             'Content-Type' => $self->type,
64             'Accept' => $self->type,
65 13         141 %{ $self->headers },
66             },
67             $json_request,
68 14 50   2   95 ($cb ? sub {$this && $this->_response($cb, $call, @_)} : ()),
  2 100       12068  
69             );
70             }
71 17 100       1752058 return ($cb ? () : $self->_response($cb, $call, undef, $tx));
72             }
73              
74             sub _response {
75 17     17   46 my ($self, $cb, $call, undef, $tx) = @_;
76 17         30 my $is_notify = !defined $call;
77              
78 17         24 my ($failed, $result, $error);
79 17         47 my $res = $tx->res;
80             # transport error (we don't have HTTP reply)
81 17 100 100     88 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         114 $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         499 ($failed, $result, $error) = $self->_client->response($res->body);
95             }
96              
97 17 100 100     809 if ($failed && $call) {
98 1         4 $self->_client->cancel($call);
99             }
100              
101 17 100       57 if ($cb) {
102 4 100       14 return $is_notify ? $cb->($failed) : $cb->($failed, $result, $error);
103             } else {
104 13 100       159 return $is_notify ? $failed : ($failed, $result, $error);
105             }
106             }
107              
108              
109             1; # Magic true value required at end of module
110             __END__