File Coverage

blib/lib/Net/Prober/Probe/HTTP.pm
Criterion Covered Total %
statement 81 94 86.1
branch 25 46 54.3
condition 15 32 46.8
subroutine 10 10 100.0
pod 0 3 0.0
total 131 185 70.8


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