File Coverage

blib/lib/Net/Prober/Probe/HTTP.pm
Criterion Covered Total %
statement 79 94 84.0
branch 23 46 50.0
condition 13 32 40.6
subroutine 10 10 100.0
pod 0 3 0.0
total 125 185 67.5


line stmt bran cond sub pod time code
1             package Net::Prober::Probe::HTTP;
2             $Net::Prober::Probe::HTTP::VERSION = '0.17';
3 2     2   478 use strict;
  2         5  
  2         47  
4 2     2   9 use warnings;
  2         4  
  2         54  
5              
6 2     2   10 use base 'Net::Prober::Probe::TCP';
  2         4  
  2         753  
7              
8 2     2   17 use Carp ();
  2         5  
  2         32  
9 2     2   13 use Digest::MD5 ();
  2         6  
  2         35  
10 2     2   13 use LWPx::ParanoidAgent ();
  2         5  
  2         1746  
11              
12             sub defaults {
13 5     5 0 12 my ($self) = @_;
14 5         24 my $defaults = $self->SUPER::defaults;
15              
16             my %http_defaults = (
17 5         10 %{ $defaults },
  5         47  
18             headers => undef,
19             md5 => undef,
20             method => 'GET',
21             port => 80,
22             scheme => 'http',
23             url => '/',
24             match => undef,
25             body => undef,
26             up_status_re => '^[23]\d\d$',
27             );
28              
29 5         22 return \%http_defaults;
30             }
31              
32             sub agent {
33              
34 2     2 0 16 my $ua = LWPx::ParanoidAgent->new();
35 2   50     677 my $ver = $Net::Prober::VERSION || 'dev';
36 2         16 $ua->agent("Net::Prober/$ver");
37 2         167 $ua->max_redirect(0);
38              
39 2         44 return $ua;
40             }
41              
42             sub _prepare_request {
43 3     3   18 my ($self, $args) = @_;
44              
45 3         19 my ($host, $port, $timeout, $scheme, $url, $method, $body, $headers) =
46             $self->parse_args($args, qw(host port timeout scheme url method body headers));
47              
48 3   50     13 $method ||= "GET";
49              
50 3 50       10 if (defined $scheme) {
51 3 50       12 if ($scheme eq 'http') {
    0          
52 3   50     10 $port ||= 80;
53             }
54             elsif ($scheme eq 'https') {
55 0   0     0 $port ||= 443;
56             }
57             }
58              
59 3 50       12 if (defined $port) {
60 3 50       10 $scheme = $port == 443 ? "https" : "http";
61             }
62              
63 3         17 $url =~ s{^/+}{};
64              
65             # We don't want to add :80 or :443 because some pesky Asian CDN
66             # doesn't like when Host header contains those default ports
67 3         12 my $probe_url = "$scheme://$host/$url";
68 3 100 66     18 if ($port != 80 && $port != 443) {
69 1         6 $probe_url = "$scheme://$host:$port/$url";
70             }
71              
72 3         9 my @req_args = ($method, $probe_url);
73              
74 3 100 66     15 if ($headers && ref $headers eq "ARRAY") {
75 1         15 my $req_headers = HTTP::Headers->new();
76 1         12 $req_headers->header(@{ $headers });
  1         6  
77 1         75 push @req_args, $req_headers;
78             }
79              
80 3 50       11 if ($body) {
81 0         0 push @req_args, $body;
82             }
83              
84 3         30 return HTTP::Request->new(@req_args);
85             }
86              
87             sub probe {
88 2     2 0 6 my ($self, $args) = @_;
89              
90 2         10 my ($expected_md5, $content_match, $up_status_re, $timeout) =
91             $self->parse_args($args, qw(md5 match up_status_re timeout));
92              
93 2         16 $self->time_now();
94              
95 2         7 my $ua = $self->agent();
96              
97 2 50 33     19 if (defined $timeout && $timeout > 0) {
98 2         15 $ua->timeout($timeout);
99             }
100              
101 2         29 my $req = $self->_prepare_request($args);
102              
103             # Fire in the hole!
104 2         467 my $resp = $ua->request($req);
105              
106 2         44399 my $elapsed = $self->time_elapsed();
107 2         10 my $content = $resp->content();
108 2         32 my $status = $resp->code();
109              
110 2         21 my $good = 0;
111 2         3 my $reason;
112              
113 2 50 33     25 if (! $up_status_re || ! defined $status || ! $status) {
    50 33        
114 0   0     0 $good = $resp->is_redirect() || $resp->is_success();
115 0 0       0 if (! $good) {
116 0         0 $reason = "Response HTTP status code wasn't successful (2xx or 3xx)";
117             }
118             }
119             elsif ($up_status_re && defined $status) {
120 2         26 my $match_re;
121             eval {
122 2         48 $match_re = qr{$up_status_re}ms;
123 2 50       4 } or do {
124 0         0 Carp::croak("Invalid regex for HTTP status match '$up_status_re'\n");
125             };
126 2         12 $good = $status =~ $match_re;
127 2 50       9 if (! $good) {
128 0         0 $reason = "Response HTTP status code didn't match the specified regex ('$up_status_re')";
129             }
130             }
131              
132 2 50 33     21 if ($good and defined $expected_md5) {
133 0         0 my $md5 = Digest::MD5::md5_hex($content);
134 0 0       0 if ($md5 ne $expected_md5) {
135 0         0 $good = 0;
136 0         0 $reason = "Response body MD5 sum wasn't the expected ($expected_md5)";
137             }
138             }
139              
140 2 100 66     9 if ($good and defined $content_match) {
141 1         3 my $match_re;
142             eval {
143 1         14 $match_re = qr{$content_match}ms;
144 1 50       2 } or do {
145 0         0 Carp::croak("Invalid regex for http content match '$content_match'\n");
146             };
147 1 50       12 if ($content !~ $match_re) {
148 0         0 $good = 0;
149 0         0 $reason = "Content didn't match the specified '$content_match' regex";
150             }
151             }
152              
153 2         9 my %status = (
154             status => $resp->status_line,
155             content => $content,
156             elapsed => $elapsed,
157             );
158              
159 2 50       67 my $md5 = $content
160             ? Digest::MD5::md5_hex($content)
161             : undef;
162              
163 2 50       15 $status{md5} = $md5 if $md5;
164 2 50       9 $status{reason} = $reason if defined $reason;
165              
166 2 50       9 if ($good) {
167 2         18 return $self->probe_ok(%status);
168             }
169              
170 0           return $self->probe_failed(%status);
171             }
172              
173             1;
174              
175             __END__