File Coverage

blib/lib/Connector/Proxy/HTTP.pm
Criterion Covered Total %
statement 37 96 38.5
branch 4 32 12.5
condition 0 15 0.0
subroutine 8 12 66.6
pod 3 3 100.0
total 52 158 32.9


line stmt bran cond sub pod time code
1             # Proxy class to get/set data using HTTP POST
2             #
3              
4             use strict;
5 1     1   142533 use warnings;
  1         13  
  1         31  
6 1     1   5 use English;
  1         1  
  1         28  
7 1     1   6 use Template;
  1         2  
  1         8  
8 1     1   827  
  1         20742  
  1         34  
9             use Moose;
10 1     1   609 extends 'Connector::Proxy';
  1         453829  
  1         8  
11             with qw(
12             Connector::Role::SSLUserAgent
13             Connector::Role::LocalPath
14             );
15            
16             # If not set, the path items are added to the base url as uri path
17             # if set, the keys from named parameters are combined and used as query string
18             # not implemented
19             #has named_parameters => (
20             # is => 'rw',
21             # isa => 'ArrayRef|Str|Undef',
22             # trigger => \&_convert_parameters,
23             # );
24            
25              
26             has content => (
27             is => 'rw',
28             isa => 'Str',
29             );
30              
31             has header => (
32             is => 'ro',
33             isa => 'HashRef',
34             );
35              
36             has content_type => (
37             is => 'rw',
38             isa => 'Str',
39             );
40              
41             has http_method => (
42             is => 'rw',
43             isa => 'Str',
44             default => 'PUT',
45             );
46              
47             has http_auth => (
48             is => 'ro',
49             isa => 'HashRef',
50             );
51              
52             has undef_on_404 => (
53             is => 'ro',
54             isa => 'Bool',
55             default => 0,
56             );
57              
58             has chomp_result => (
59             is => 'ro',
60             isa => 'Bool',
61             default => 0,
62             );
63              
64              
65             # If named_parameters is set using a string (necessary atm for Config::Std)
66             # its converted to an arrayref. Might be removed if Config::* improves
67             # This might create indefinite loops if something goes wrong on the conversion!
68             my ( $self, $new, $old ) = @_;
69              
70 0     0   0 # Test if the given value is a non empty scalar
71             if ($new && !ref $new && (!$old || $new ne $old)) {
72             my @attrs = split(" ", $new);
73 0 0 0     0 $self->named_parameters( \@attrs )
      0        
      0        
74 0         0 }
75 0         0  
76             }
77            
78             my $self = shift;
79              
80             my @args = $self->_build_path( shift );
81 1     1 1 10  
82             my $url = $self->LOCATION();
83 1         9 if (@args) {
84             $url .= '/'.join('/', @args);
85 1         39 }
86 1 50       4 $self->log()->debug('Make LWP call to ' . $url );
87 0         0  
88             my $req = HTTP::Request->new('GET' => $url);
89 1         20  
90             # use basic auth if supplied
91 1         15 my $auth=$self->http_auth();
92             if ($auth){
93             $req->authorization_basic($auth->{user},$auth->{pass});
94 1         7827 }
95 1 50       4  
96 0         0 # extra headers
97             my $header = $self->header();
98             foreach my $key (%{$header}) {
99             $req->header($key, $header->{$key} );
100 1         24 }
101 1         3  
  1         5  
102 0         0 my $response = $self->agent()->request($req);
103            
104             if (!$response->is_success) {
105 1         25 if ( $response->code == 404 && $self->undef_on_404()) {
106             $self->log()->warn("Resource not found");
107 1 50       432596 return $self->_node_not_exists();
108 0 0 0     0 }
109 0         0 $self->log()->error($response->status_line);
110 0         0 die "Unable to retrieve data from server";
111             }
112 0         0  
113 0         0 return $self->_parse_result($response);
114             }
115              
116 1         22  
117             my $self = shift;
118             my $file = shift;
119             my $data = shift;
120             # build url
121 0     0 1 0 my $url = $self->_sanitize_path( $file, $data );
122 0         0 # create content from template
123 0         0 my $content;
124             if ($self->content()) {
125 0         0 $self->log()->debug('Process template for content ' . $self->content());
126             my $template = Template->new({});
127 0         0  
128 0 0       0 $data = { DATA => $data } if (ref $data eq '');
129 0         0  
130 0         0 $template->process( \$self->content(), $data, \$content) || die "Error processing content template.";
131             } else {
132 0 0       0 if (ref $data ne '') {
133             die "You need to define a content template if data is not a scalar";
134 0 0       0 }
135             $content = $data;
136 0 0       0 }
137 0         0  
138             # create request
139 0         0 my $req = HTTP::Request->new($self->http_method() => $url);
140             # use basic auth if supplied
141             my $auth=$self->http_auth();
142             if ($auth){
143 0         0 $req->authorization_basic($auth->{user},$auth->{pass});
144             }
145 0         0 # set content_type if supplied
146 0 0       0 if ($self->content_type()){
147 0         0 $req->content_type($self->content_type());
148             }
149              
150 0 0       0 # extra headers
151 0         0 my $header = $self->header();
152             foreach my $key (%{$header}) {
153             $req->header($key, $header->{$key} );
154             }
155 0         0  
156 0         0 # set generated content
  0         0  
157 0         0 $req->content($content);
158              
159             my $response = $self->agent()->request($req);
160             # error handling
161 0         0 if (!$response->is_success) {
162             $self->log()->error($response->status_line);
163 0         0 $self->log()->error($response->decoded_content);
164             die "Unable to upload data to server";
165 0 0       0 }
166 0         0  
167 0         0 $self->log()->debug("Set responded with: " . $response->status_line);
168 0         0 $self->log()->trace($response->decoded_content) if ($self->log()->is_trace());
169              
170             return 1;
171 0         0 }
172 0 0       0  
173             my $self = shift;
174 0         0  
175             # If we have no path, we tell the caller that we are a connector
176             my @path = $self->_build_path_with_prefix( shift );
177             if (scalar @path == 0) {
178 0     0 1 0 return { TYPE => "connector" };
179             }
180              
181 0         0 return {TYPE => "scalar" };
182 0 0       0 }
183 0         0  
184              
185              
186 0         0 my $self = shift;
187             my $inargs = shift;
188             my $data = shift;
189              
190             my @args = $self->_build_path_with_prefix( $inargs );
191              
192 0     0   0 my $file = $self->_render_local_path( \@args, $data );
193 0         0  
194 0         0 my $filename = $self->LOCATION();
195             if (defined $file && $file ne "") {
196 0         0 $filename .= '/'.$file;
197             }
198 0         0  
199             $self->log()->debug('Filename evaluated to ' . $filename);
200 0         0  
201 0 0 0     0 return $filename;
202 0         0 }
203              
204              
205 0         0 my $self = shift;
206             my $response = shift;
207 0         0  
208             my $res = $response->decoded_content;
209             chomp $res if ($self->chomp_result());
210             return $res;
211             }
212 1     1   3  
213 1         2  
214             no Moose;
215 1         9 __PACKAGE__->meta->make_immutable;
216 1 50       15044  
217 1         38 1;
218              
219             =head1 NAME
220              
221 1     1   7778 Connector::Proxy::HTTP
  1         2  
  1         5  
222              
223             =head1 DESCRIPTION
224              
225             Send or retrieve data from a defined URI using HTTP.
226              
227             =head1 USAGE
228              
229             =head2 minimal setup
230              
231             Connector::Proxy::HTTP->new({
232             LOCATION => 'https://127.0.0.1/my/base/url',
233             });
234              
235             =head2 connection settings
236              
237             See Connector::Role::SSLUserAgent for SSL and HTTP related settings
238              
239             =head2 additional options
240              
241             =over
242              
243             =item named_parameters
244              
245             not implemented yet
246              
247             =item header
248              
249             A HashRef, the key/value pairs are set as HTTP headers.
250              
251             =item http_auth
252              
253             A HashRef with I<user> and I<pass> used as credentials to perform a
254             HTTP Basic Authentication.
255              
256             =item chomp_result
257              
258             When working with text documents the transport layer adds a trailing
259             newline which might be unhandy when working with scalar values. If
260             set to a true value, a trailing newline will be removed by calling C<chomp>.
261              
262             =item undef_on_404
263              
264             By default, the connector will die if a resource is not found. If set
265             to a true value the connector returns undef, note that die_on_undef
266             will be obeyed.
267              
268             =back
269              
270             =head2 Parameter used with set
271              
272             =over
273              
274             =item file/path
275              
276             You can append a templated string to the LOCATION by setting I<file>,
277             I<path> or simply pass I<ARGS>. See Connector::Role::LocalPath for details.
278              
279             =item content
280              
281             A template toolkit string to generate the payload, receives the payload
282             argument as HasRef in I<DATA>.
283              
284             =item content_type
285              
286             The Content-Type header to use, default is no header.
287              
288             =item http_method
289              
290             The http method to use, default is PUT.
291              
292             =back
293              
294              
295             =head1 Result Handling
296              
297             If you need to parse the result returned by get, inherit from the class
298             an implement I<_parse_result>. This method receives the response object
299             from the user agent call and must return a scalar value which is returned
300             to the caller.