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   322992 use Mojo::Base -base;
  3         13440  
  3         19  
4 3     3   467 use Carp;
  3         6  
  3         179  
5              
6             our $VERSION = 'v2.0.1';
7              
8 3     3   862 use Mojo::UserAgent;
  3         400360  
  3         19  
9 3     3   1336 use JSON::RPC2::Client;
  3         11707  
  3         74  
10 3     3   1708 use JSON::XS;
  3         5625  
  3         150  
11 3     3   15 use Scalar::Util qw(weaken);
  3         4  
  3         106  
12              
13 3     3   12 use constant REQUEST_TIMEOUT => 30;
  3         3  
  3         154  
14 3     3   11 use constant HTTP_200 => 200;
  3         4  
  3         2023  
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 9098 sub call { return shift->_request('call', @_) }
29 2     2 1 1306 sub call_named { return shift->_request('call_named', @_) }
30 3     3 1 1567 sub notify { return shift->_request('notify', @_) }
31 2     2 1 1281 sub notify_named { return shift->_request('notify_named', @_) }
32              
33             sub _request {
34 18     18   67 my ($self, $func, $method, @params) = @_;
35             # work either in blocking mode or non-blocking (if has \&cb in last param)
36 18 100 100     147 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         91 my ($json_request, $call) = $self->_client->$func($method, @params);
40              
41 18         1018 my $tx; # will be set when in blocking mode
42 18         75 weaken(my $this = $self);
43 18 100       186 if ('GET' eq uc $self->method) {
44 4         79 my $json = decode_json($json_request);
45 4 100       20 if (exists $json->{params}) {
46 3         69 $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         75 %{ $self->headers },
54             },
55             form => $json,
56 4 50   2   21 ($cb ? sub {$this && $this->_response($cb, $call, @_)} : ()),
  2 100       14886  
57             );
58             }
59             else {
60             $tx = $self->ua->post(
61             $self->url,
62             {
63             'Content-Type' => $self->type,
64             'Accept' => $self->type,
65 13         250 %{ $self->headers },
66             },
67             $json_request,
68 14 50   2   180 ($cb ? sub {$this && $this->_response($cb, $call, @_)} : ()),
  2 100       18650  
69             );
70             }
71 17 100       927901 return ($cb ? () : $self->_response($cb, $call, undef, $tx));
72             }
73              
74             sub _response {
75 17     17   53 my ($self, $cb, $call, undef, $tx) = @_;
76 17         48 my $is_notify = !defined $call;
77              
78 17         31 my ($failed, $result, $error);
79 17         50 my $res = $tx->res;
80             # transport error (we don't have HTTP reply)
81 17 100 100     114 if ($res->error && !$res->error->{code}) {
    100 100        
    50 100        
    100 66        
82 1         22 $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         175 $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         901 ($failed, $result, $error) = $self->_client->response($res->body);
95             }
96              
97 17 100 100     1334 if ($failed && $call) {
98 1         6 $self->_client->cancel($call);
99             }
100              
101 17 100       79 if ($cb) {
102 4 100       87 return $is_notify ? $cb->($failed) : $cb->($failed, $result, $error);
103             } else {
104 13 100       326 return $is_notify ? $failed : ($failed, $result, $error);
105             }
106             }
107              
108              
109             1; # Magic true value required at end of module
110             __END__