File Coverage

blib/lib/Plack/Client.pm
Criterion Covered Total %
statement 106 116 91.3
branch 23 38 60.5
condition 6 11 54.5
subroutine 28 28 100.0
pod 8 8 100.0
total 171 201 85.0


line stmt bran cond sub pod time code
1             package Plack::Client;
2             BEGIN {
3 4     4   658820 $Plack::Client::VERSION = '0.06';
4             }
5 4     4   37 use strict;
  4         8  
  4         124  
6 4     4   21 use warnings;
  4         9  
  4         102  
7             # ABSTRACT: abstract interface to remote web servers and local PSGI apps
8              
9 4     4   21 use Carp;
  4         5  
  4         223  
10 4     4   3672 use Class::Load;
  4         131493  
  4         207  
11 4     4   3920 use HTTP::Message::PSGI;
  4         52922  
  4         1010  
12 4     4   3544 use HTTP::Request;
  4         62382  
  4         167  
13 4     4   4274 use Plack::Request;
  4         135271  
  4         186  
14 4     4   3582 use Plack::Response;
  4         12442  
  4         161  
15 4     4   36 use Scalar::Util qw(blessed reftype);
  4         12  
  4         6602  
16              
17              
18              
19             sub new {
20 8     8 1 657976 my $class = shift;
21 8         315 my %params = @_;
22              
23 8         33 my %backends;
24 8         70 for my $scheme (keys %params) {
25 8         40 my $backend = $params{$scheme};
26 8 50 33     291 if (blessed($backend) && $backend->isa('Plack::Client::Backend')) {
    50          
    50          
27 0         0 $backends{$scheme} = $backend->as_code;
28             }
29             elsif (reftype($backend) eq 'CODE') {
30 0         0 $backends{$scheme} = $backend;
31             }
32             elsif (ref($backend)) {
33 8         69 (my $normal_scheme = $scheme) =~ s/-/_/g;
34 8         37 my $backend_class = "Plack::Client::Backend::$normal_scheme";
35 8         160 Class::Load::load_class($backend_class);
36 8 50       468 croak "Backend classes must inherit from Plack::Client::Backend"
37             unless $backend_class->isa('Plack::Client::Backend');
38 8 0       100 $backends{$scheme} = $backend_class->new(
    50          
39             reftype($backend) eq 'HASH' ? %$backend
40             : reftype($backend) eq 'ARRAY' ? @$backend
41             : $$backend
42             )->as_code;
43             }
44             else {
45 0         0 croak "Backends must be a coderef or a Plack::Client::Backend instance";
46             }
47             }
48              
49             bless {
50 8         184 backends => \%backends,
51             }, $class;
52             }
53              
54              
55             sub backend {
56 76     76 1 151 my $self = shift;
57 76         163 my ($scheme) = @_;
58 76 50       734 $scheme = $scheme->scheme if blessed($scheme);
59 76         1279 my $backend = $self->_backend($scheme);
60 76 50       295 return $backend if defined $backend;
61 0 0       0 $scheme = 'http' if $scheme eq 'https';
62 0         0 $scheme =~ s/-ssl$//;
63 0         0 return $self->_backend($scheme);
64             }
65              
66             sub _backend {
67 76     76   128 my $self = shift;
68 76         135 my ($scheme) = @_;
69 76         393 return $self->{backends}->{$scheme};
70             }
71              
72              
73             sub request {
74 76     76 1 872814 my $self = shift;
75              
76 76         376 my ($app, $env) = $self->_parse_request_args(@_);
77              
78 76         1252 my $psgi_res = $self->_resolve_response($app->($env));
79             # is there a better place to do this? Plack::App::Proxy already takes care
80             # of this (since it's making a real http request)
81 76 100       1961 $psgi_res->[2] = [] if $env->{REQUEST_METHOD} eq 'HEAD';
82              
83             # XXX: or just return the arrayref?
84 76         876 return Plack::Response->new(@$psgi_res);
85             }
86              
87             sub _parse_request_args {
88 76     76   197 my $self = shift;
89              
90 76 100 100     2220 if (blessed($_[0])) {
    100          
91 12 100       139 if ($_[0]->isa('HTTP::Request')) {
    50          
92 6         34 return $self->_request_from_http_request(@_);
93             }
94             elsif ($_[0]->isa('Plack::Request')) {
95 6         32 return $self->_request_from_plack_request(@_);
96             }
97             else {
98 0         0 croak 'Request object must be either an HTTP::Request or a Plack::Request';
99             }
100             }
101             elsif ((reftype($_[0]) || '') eq 'HASH') {
102 6         27 return $self->_request_from_env(@_);
103             }
104             else {
105 58         388 return $self->_request_from_http_request_args(@_);
106             }
107             }
108              
109             sub _request_from_http_request {
110 64     64   97261 my $self = shift;
111 64         159 my ($http_request) = @_;
112 64         351 my $env = $self->_http_request_to_env($http_request);
113 64         239 return $self->_request_from_env($env);
114             }
115              
116             sub _request_from_plack_request {
117 76     76   923 my $self = shift;
118 76         151 my ($req) = @_;
119              
120 76         275 return ($self->_app_from_request($req), $req->env);
121             }
122              
123             sub _request_from_env {
124 70     70   124 my $self = shift;
125 70         1302 return $self->_request_from_plack_request(Plack::Request->new(@_));
126             }
127              
128             sub _request_from_http_request_args {
129 58     58   178 my $self = shift;
130 58         1021 return $self->_request_from_http_request(HTTP::Request->new(@_));
131             }
132              
133             sub _http_request_to_env {
134 64     64   137 my $self = shift;
135 64         104 my ($req) = @_;
136              
137 64         227 my $scheme = $req->uri->scheme;
138 64         3385 my $original_uri = $req->uri->clone;
139              
140             # hack around with this - psgi requires a host and port to exist, and
141             # for the scheme to be either http or https
142 64 100 66     1445 if ($scheme ne 'http' && $scheme ne 'https') {
143 32 50       135 if ($scheme =~ /-ssl$/) {
144 0         0 $req->uri->scheme('https');
145             }
146             else {
147 32         113 $req->uri->scheme('http');
148             }
149 32         3824 $req->uri->host('Plack::Client');
150 32         3765 $req->uri->port(-1);
151             }
152              
153 64         2879 my $env = $req->to_psgi;
154              
155 64         55030 $env->{'plack.client.original_uri'} = $original_uri;
156              
157 64         198 return $env;
158             }
159              
160             sub _app_from_request {
161 76     76   197 my $self = shift;
162 76         111 my ($req) = @_;
163              
164 76   33     481 my $uri = $req->env->{'plack.client.original_uri'} || $req->uri;
165              
166 76         2151 my $backend = $self->backend($uri);
167 76         666 my $app = $backend->($req);
168              
169 76 50       2975 croak "Couldn't find app" unless $app;
170              
171 76         346 return $app;
172             }
173              
174             sub _resolve_response {
175 76     76   283078 my $self = shift;
176 76         160 my ($psgi_res) = @_;
177              
178 76 100       335 if (ref($psgi_res) eq 'CODE') {
179 62         143 my $body = [];
180             $psgi_res->(sub {
181 62     62   323461 $psgi_res = shift;
182             return Plack::Util::inline_object(
183 88         6945 write => sub { push @$body, $_[0] },
184 50         14016 close => sub { push @$psgi_res, $body },
185 62         735 );
186 62         462 });
187             }
188              
189 76 50       20659 if (ref($psgi_res) ne 'ARRAY') {
190 0         0 require Data::Dumper;
191 0         0 croak "Unable to understand app response:\n"
192             . Data::Dumper::Dumper($psgi_res);
193             }
194              
195 76         240 return $psgi_res;
196             }
197              
198              
199 34     34 1 1007026 sub get { shift->request('GET', @_) }
200 6     6 1 258730 sub head { shift->request('HEAD', @_) }
201 6     6 1 246259 sub post { shift->request('POST', @_) }
202 6     6 1 242865 sub put { shift->request('PUT', @_) }
203 6     6 1 262210 sub delete { shift->request('DELETE', @_) }
204              
205              
206             1;
207              
208             __END__