File Coverage

blib/lib/Elasticsearch/Role/Cxn/HTTP.pm
Criterion Covered Total %
statement 52 63 82.5
branch 13 16 81.2
condition 14 17 82.3
subroutine 9 11 81.8
pod 1 6 16.6
total 89 113 78.7


line stmt bran cond sub pod time code
1             package Elasticsearch::Role::Cxn::HTTP;
2             $Elasticsearch::Role::Cxn::HTTP::VERSION = '1.05';
3 42     42   42834 use Moo::Role;
  42         140  
  42         516  
4              
5 42     42   18583 use URI();
  42         94  
  42         1057  
6 42     42   285 use Elasticsearch::Util qw(parse_params throw);
  42         296  
  42         448  
7 42     42   21872 use namespace::clean;
  42         91  
  42         373  
8              
9             has 'scheme' => ( is => 'ro' );
10             has 'is_https' => ( is => 'ro' );
11             has 'userinfo' => ( is => 'ro' );
12             has 'max_content_length' => ( is => 'ro' );
13             has 'default_headers' => ( is => 'ro' );
14             has 'handle' => ( is => 'lazy' );
15              
16             #===================================
17 78     78 0 289 sub protocol {'http'}
18 71     71 0 339 sub default_host {'http://localhost:9200'}
19 555     555 0 4506 sub stringify { shift->uri . '' }
20             #===================================
21              
22             #===================================
23             sub BUILDARGS {
24             #===================================
25 147     147 0 152994 my ( $class, $params ) = parse_params(@_);
26              
27 147   50     766 my $node = $params->{node}
28             || { host => 'localhost', port => '9200' };
29              
30 147 100       595 unless ( ref $node eq 'HASH' ) {
31 144 100       665 unless ( $node =~ m{^http(s)?://} ) {
32 119 100       534 $node = ( $params->{use_https} ? 'https://' : 'http://' ) . $node;
33             }
34 144 100 100     691 if ( $params->{port} && $node !~ m{//[^/]+:\d+} ) {
35 1         12 $node =~ s{(//[^/]+)}{$1:$params->{port}};
36             }
37 144         948 my $uri = URI->new($node);
38 144         528415 $node = {
39             scheme => $uri->scheme,
40             host => $uri->host,
41             port => $uri->port,
42             path => $uri->path,
43             userinfo => $uri->userinfo
44             };
45             }
46              
47 147   100     25311 my $host = $node->{host} || 'localhost';
48 147   100     6806 my $userinfo = $node->{userinfo} || $params->{userinfo} || '';
49 147   66     685 my $scheme
50             = $node->{scheme} || ( $params->{use_https} ? 'https' : 'http' );
51 147   66     1128 my $port
52             = $node->{port}
53             || $params->{port}
54             || ( $scheme eq 'http' ? 80 : 443 );
55 147   100     1447 my $path = $node->{path} || $params->{path_prefix} || '';
56 147         830 $path =~ s{^/?}{/}g;
57 147         602 $path =~ s{/+$}{};
58              
59 147 50       308 my %default_headers = %{ $params->{default_headers} || {} };
  147         1128  
60              
61 147 100       599 if ($userinfo) {
62 3         1174 require MIME::Base64;
63 3         889 my $auth = MIME::Base64::encode_base64($userinfo);
64 3         9 chomp $auth;
65 3         15 $default_headers{Authorization} = "Basic $auth";
66             }
67              
68 147 100       609 if ( $params->{deflate} ) {
69 1         4 $default_headers{'Accept-Encoding'} = "deflate";
70             }
71              
72 147         460 $params->{scheme} = $scheme;
73 147         416 $params->{is_http} = $scheme eq 'https';
74 147         381 $params->{host} = $host;
75 147         404 $params->{port} = $port;
76 147         347 $params->{path} = $path;
77 147         19425 $params->{userinfo} = $userinfo;
78 147         1157 $params->{uri} = URI->new("$scheme://$host:$port$path");
79 147         10436 $params->{default_headers} = \%default_headers;
80              
81 147         4875 return $params;
82              
83             }
84              
85             #===================================
86             sub build_uri {
87             #===================================
88 0     0 1   my ( $self, $params ) = @_;
89 0           my $uri = $self->uri->clone;
90 0           $uri->path( $uri->path . $params->{path} );
91 0           $uri->query_form( $params->{qs} );
92 0           return $uri;
93             }
94              
95             #===================================
96             before 'perform_request' => sub {
97             #===================================
98             my ( $self, $params ) = @_;
99             return unless defined $params->{data};
100              
101             my $max = $self->max_content_length
102             or return;
103              
104             return if length( $params->{data} ) < $max;
105              
106             $self->logger->throw_error( 'ContentLength',
107             "Body is longer than max_content_length ($max)",
108             );
109             };
110              
111             #===================================
112             around 'process_response' => sub {
113             #===================================
114             my ( $orig, $self, $params, $code, $msg, $body, $headers ) = @_;
115              
116             if ( my $encoding = $headers->{'content-encoding'} ) {
117             $body = $self->inflate($body)
118             if $encoding eq 'deflate';
119             }
120              
121             my ($mime_type) = split /\s*;\s*/, ( $headers->{'content-type'} || '' );
122             $orig->( $self, $params, $code, $msg, $body, $mime_type );
123             };
124              
125             #===================================
126             sub inflate {
127             #===================================
128 0     0 0   my $self = shift;
129 0           my $content = shift;
130              
131 0           my $output;
132 0           require IO::Uncompress::Inflate;
133 42     42   77663 no warnings 'once';
  42         109  
  42         5301  
134              
135 0 0         IO::Uncompress::Inflate::inflate( \$content, \$output, Transparent => 0 )
136             or throw( 'Request',
137             "Couldn't inflate response: $IO::Uncompress::Inflate::InflateError" );
138              
139 0           return $output;
140             }
141              
142             1;
143              
144             # ABSTRACT: Provides common functionality to HTTP Cxn implementations
145              
146             __END__
147              
148             =pod
149              
150             =encoding UTF-8
151              
152             =head1 NAME
153              
154             Elasticsearch::Role::Cxn::HTTP - Provides common functionality to HTTP Cxn implementations
155              
156             =head1 VERSION
157              
158             version 1.05
159              
160             =head1 DESCRIPTION
161              
162             L<Elasticsearch::Role::Cxn::HTTP> provides common functionality to the Cxn
163             implementations which use the HTTP protocol. Cxn instances are created by a
164             L<Elasticsearch::Role::CxnPool> implementation, using the
165             L<Elasticsearch::Cxn::Factory> class.
166              
167             =head1 CONFIGURATION
168              
169             The configuration options are as follows:
170              
171             =head2 C<node>
172              
173             A single C<node> is passed to C<new()> by the L<Elasticsearch::Cxn::Factory>
174             class. It can either be a URI or a hash containing each part. For instance:
175              
176             node => 'localhost'; # equiv of 'http://localhost:80'
177             node => 'localhost:9200'; # equiv of 'http://localhost:9200'
178             node => 'http://localhost:9200';
179              
180             node => 'https://localhost'; # equiv of 'https://localhost:443'
181             node => 'localhost/path'; # equiv of 'http://localhost:80/path'
182              
183              
184             node => 'http://user:pass@localhost'; # equiv of 'http://localhost:80'
185             # with userinfo => 'user:pass'
186              
187             Alternatively, a C<node> can be specified as a hash:
188              
189             {
190             scheme => 'http',
191             host => 'search.domain.com',
192             port => '9200',
193             path => '/path',
194             userinfo => 'user:pass'
195             }
196              
197             Similarly, default values can be specified with C<port>, C<path_prefix>,
198             C<userinfo> and C<use_https>:
199              
200             $e = Elasticsearch->new(
201             port => 9201,
202             path_prefix => '/path',
203             userinfo => 'user:pass',
204             use_https => 1,
205             nodes => [ 'search1', 'search2' ]
206             )
207              
208             =head2 C<max_content_length>
209              
210             By default, Elasticsearch nodes accept a maximum post body of 100MB or
211             C<104_857_600> bytes. This client enforces that limit. The limit can
212             be customised with the C<max_content_length> parameter (specified in bytes).
213              
214             If you're using the L<Elasticsearch::CxnPool::Sniff> module, then the
215             C<max_content_length> will be automatically retrieved from the live cluster,
216             unless you specify a custom C<max_content_length>:
217              
218             # max_content_length retrieved from cluster
219             $e = Elasticsearch->new(
220             cxn_pool => 'Sniff'
221             );
222              
223             # max_content_length fixed at 10,000 bytes
224             $e = Elasticsearch->new(
225             cxn_pool => 'Sniff',
226             max_content_length => 10_000
227             );
228              
229             =head2 C<deflate>
230              
231             This client can request compressed responses from Elasticsearch by
232             enabling the C<http.compression> config setting in
233             L<Elasticsearch|http://www.elasticsearch.org/guide/reference/modules/http/>
234             and setting C<deflate> to C<true>:
235              
236             $e = Elasticsearch->new(
237             deflate => 1
238             );
239              
240             =head1 METHODS
241              
242             None of the methods listed below are useful to the user. They are
243             documented for those who are writing alternative implementations only.
244              
245             =head2 C<scheme()>
246              
247             $scheme = $cxn->scheme;
248              
249             Returns the scheme of the connection, ie C<http> or C<https>.
250              
251             =head2 C<is_https()>
252              
253             $bool = $cxn->is_https;
254              
255             Returns C<true> or C<false> depending on whether the C</scheme()> is C<https>
256             or not.
257              
258             =head2 C<userinfo()>
259              
260             $userinfo = $cxn->userinfo
261              
262             Returns the username and password of the cxn, if any, eg C<"user:pass">.
263             If C<userinfo> is provided, then a Basic Authorization header is added
264             to each request.
265              
266             =head2 C<default_headers()>
267              
268             $headers = $cxn->default_headers
269              
270             The default headers that are passed with each request. This includes
271             the C<Accept-Encoding> header if C</deflate> is true, and the C<Authorization>
272             header if C</userinfo> has a value.
273              
274             =head2 C<max_content_length()>
275              
276             $int = $cxn->max_content_length;
277              
278             Returns the maximum length in bytes that the HTTP body can have.
279              
280             =head2 C<build_uri()>
281              
282             $uri = $cxn->build_uri({ path => '/_search', qs => { size => 10 }});
283              
284             Returns the HTTP URI to use for a particular request, combining the passed
285             in C<path> parameter with any defined C<path_prefix>, and adding the
286             query-string parameters.
287              
288             =head1 AUTHOR
289              
290             Clinton Gormley <drtech@cpan.org>
291              
292             =head1 COPYRIGHT AND LICENSE
293              
294             This software is Copyright (c) 2014 by Elasticsearch BV.
295              
296             This is free software, licensed under:
297              
298             The Apache License, Version 2.0, January 2004
299              
300             =cut