summaryrefslogtreecommitdiffstats
path: root/plugins/tests/check_http.t
diff options
context:
space:
mode:
Diffstat (limited to 'plugins/tests/check_http.t')
-rwxr-xr-xplugins/tests/check_http.t361
1 files changed, 245 insertions, 116 deletions
diff --git a/plugins/tests/check_http.t b/plugins/tests/check_http.t
index d6d31de1..6078b274 100755
--- a/plugins/tests/check_http.t
+++ b/plugins/tests/check_http.t
@@ -3,25 +3,20 @@
3# Test check_http by having an actual HTTP server running 3# Test check_http by having an actual HTTP server running
4# 4#
5# To create the https server certificate: 5# To create the https server certificate:
6# openssl req -new -x509 -keyout server-key.pem -out server-cert.pem -days 3650 -nodes 6# ./certs/generate-certs.sh
7# Country Name (2 letter code) [AU]:UK
8# State or Province Name (full name) [Some-State]:Derbyshire
9# Locality Name (eg, city) []:Belper
10# Organization Name (eg, company) [Internet Widgits Pty Ltd]:Monitoring Plugins
11# Organizational Unit Name (eg, section) []:
12# Common Name (eg, YOUR name) []:Ton Voon
13# Email Address []:tonvoon@mac.com
14 7
15use strict; 8use strict;
16use Test::More; 9use Test::More;
17use NPTest; 10use NPTest;
18use FindBin qw($Bin); 11use FindBin qw($Bin);
12use IO::Socket::INET;
19 13
20$ENV{'LC_TIME'} = "C"; 14$ENV{'LC_TIME'} = "C";
21 15
22my $common_tests = 70; 16my $common_tests = 71;
23my $virtual_port_tests = 8; 17my $virtual_port_tests = 8;
24my $ssl_only_tests = 8; 18my $ssl_only_tests = 12;
19my $chunked_encoding_special_tests = 1;
25# Check that all dependent modules are available 20# Check that all dependent modules are available
26eval "use HTTP::Daemon 6.01;"; 21eval "use HTTP::Daemon 6.01;";
27plan skip_all => 'HTTP::Daemon >= 6.01 required' if $@; 22plan skip_all => 'HTTP::Daemon >= 6.01 required' if $@;
@@ -30,13 +25,16 @@ eval {
30 require HTTP::Response; 25 require HTTP::Response;
31}; 26};
32 27
28my $plugin = 'check_http';
29$plugin = 'check_curl' if $0 =~ m/check_curl/mx;
30
33if ($@) { 31if ($@) {
34 plan skip_all => "Missing required module for test: $@"; 32 plan skip_all => "Missing required module for test: $@";
35} else { 33} else {
36 if (-x "./check_http") { 34 if (-x "./$plugin") {
37 plan tests => $common_tests * 2 + $ssl_only_tests + $virtual_port_tests; 35 plan tests => $common_tests * 2 + $ssl_only_tests + $virtual_port_tests + $chunked_encoding_special_tests;
38 } else { 36 } else {
39 plan skip_all => "No check_http compiled"; 37 plan skip_all => "No $plugin compiled";
40 } 38 }
41} 39}
42 40
@@ -54,124 +52,217 @@ $HTTP::Daemon::VERSION = "1.00";
54my $port_http = 50000 + int(rand(1000)); 52my $port_http = 50000 + int(rand(1000));
55my $port_https = $port_http + 1; 53my $port_https = $port_http + 1;
56my $port_https_expired = $port_http + 2; 54my $port_https_expired = $port_http + 2;
55my $port_https_clientcert = $port_http + 3;
56my $port_hacked_http = $port_http + 4;
57 57
58# This array keeps sockets around for implementing timeouts 58# This array keeps sockets around for implementing timeouts
59my @persist; 59my @persist;
60 60
61# Start up all servers 61# Start up all servers
62my @pids; 62my @pids;
63my $pid = fork(); 63# Fork a HTTP server
64if ($pid) { 64my $pid = fork;
65 # Parent 65defined $pid or die "Failed to fork";
66 push @pids, $pid; 66if (!$pid) {
67 if (exists $servers->{https}) { 67 undef @pids;
68 # Fork a normal HTTPS server
69 $pid = fork();
70 if ($pid) {
71 # Parent
72 push @pids, $pid;
73 # Fork an expired cert server
74 $pid = fork();
75 if ($pid) {
76 push @pids, $pid;
77 } else {
78 my $d = HTTP::Daemon::SSL->new(
79 LocalPort => $port_https_expired,
80 LocalAddr => "127.0.0.1",
81 SSL_cert_file => "$Bin/certs/expired-cert.pem",
82 SSL_key_file => "$Bin/certs/expired-key.pem",
83 ) || die;
84 print "Please contact https expired at: <URL:", $d->url, ">\n";
85 run_server( $d );
86 exit;
87 }
88 } else {
89 my $d = HTTP::Daemon::SSL->new(
90 LocalPort => $port_https,
91 LocalAddr => "127.0.0.1",
92 SSL_cert_file => "$Bin/certs/server-cert.pem",
93 SSL_key_file => "$Bin/certs/server-key.pem",
94 ) || die;
95 print "Please contact https at: <URL:", $d->url, ">\n";
96 run_server( $d );
97 exit;
98 }
99 }
100 # give our webservers some time to startup
101 sleep(1);
102} else {
103 # Child
104 #print "child\n";
105 my $d = HTTP::Daemon->new( 68 my $d = HTTP::Daemon->new(
106 LocalPort => $port_http, 69 LocalPort => $port_http,
107 LocalAddr => "127.0.0.1", 70 LocalAddr => "127.0.0.1",
108 ) || die; 71 ) || die;
109 print "Please contact http at: <URL:", $d->url, ">\n"; 72 print "Please contact http at: <URL:", $d->url, ">\n";
110 run_server( $d ); 73 run_server( $d );
111 exit; 74 die "webserver stopped";
75}
76push @pids, $pid;
77
78# Fork the hacked HTTP server
79undef $pid;
80$pid = fork;
81defined $pid or die "Failed to fork";
82if (!$pid) {
83 # this is the fork
84 undef @pids;
85 my $socket = new IO::Socket::INET (
86 LocalHost => '0.0.0.0',
87 LocalPort => $port_hacked_http,
88 Proto => 'tcp',
89 Listen => 5,
90 Reuse => 1
91 );
92 die "cannot create socket $!n" unless $socket;
93 my $local_sock = $socket->sockport();
94 print "server waiting for client connection on port $local_sock\n";
95 run_hacked_http_server ( $socket );
96 die "hacked http server stopped";
112} 97}
98push @pids, $pid;
99
100if (exists $servers->{https}) {
101 # Fork a normal HTTPS server
102 $pid = fork;
103 defined $pid or die "Failed to fork";
104 if (!$pid) {
105 undef @pids;
106 # closing the connection after -C cert checks make the daemon exit with a sigpipe otherwise
107 local $SIG{'PIPE'} = 'IGNORE';
108 my $d = HTTP::Daemon::SSL->new(
109 LocalPort => $port_https,
110 LocalAddr => "127.0.0.1",
111 SSL_cert_file => "$Bin/certs/server-cert.pem",
112 SSL_key_file => "$Bin/certs/server-key.pem",
113 ) || die;
114 print "Please contact https at: <URL:", $d->url, ">\n";
115 run_server( $d );
116 die "webserver stopped";
117 }
118 push @pids, $pid;
119
120 # Fork an expired cert server
121 $pid = fork;
122 defined $pid or die "Failed to fork";
123 if (!$pid) {
124 undef @pids;
125 # closing the connection after -C cert checks make the daemon exit with a sigpipe otherwise
126 local $SIG{'PIPE'} = 'IGNORE';
127 my $d = HTTP::Daemon::SSL->new(
128 LocalPort => $port_https_expired,
129 LocalAddr => "127.0.0.1",
130 SSL_cert_file => "$Bin/certs/expired-cert.pem",
131 SSL_key_file => "$Bin/certs/expired-key.pem",
132 ) || die;
133 print "Please contact https expired at: <URL:", $d->url, ">\n";
134 run_server( $d );
135 die "webserver stopped";
136 }
137 push @pids, $pid;
138
139 # Fork an client cert expecting server
140 $pid = fork;
141 defined $pid or die "Failed to fork";
142 if (!$pid) {
143 undef @pids;
144 # closing the connection after -C cert checks make the daemon exit with a sigpipe otherwise
145 local $SIG{'PIPE'} = 'IGNORE';
146 my $d = HTTP::Daemon::SSL->new(
147 LocalPort => $port_https_clientcert,
148 LocalAddr => "127.0.0.1",
149 SSL_cert_file => "$Bin/certs/server-cert.pem",
150 SSL_key_file => "$Bin/certs/server-key.pem",
151 SSL_verify_mode => IO::Socket::SSL->SSL_VERIFY_PEER | IO::Socket::SSL->SSL_VERIFY_FAIL_IF_NO_PEER_CERT,
152 SSL_ca_file => "$Bin/certs/clientca-cert.pem",
153 ) || die;
154 print "Please contact https client cert at: <URL:", $d->url, ">\n";
155 run_server( $d );
156 die "webserver stopped";
157 }
158 push @pids, $pid;
159}
160
161# give our webservers some time to startup
162sleep(3);
113 163
114# Run the same server on http and https 164# Run the same server on http and https
115sub run_server { 165sub run_server {
116 my $d = shift; 166 my $d = shift;
117 MAINLOOP: while (my $c = $d->accept ) { 167 while (1) {
118 while (my $r = $c->get_request) { 168 MAINLOOP: while (my $c = $d->accept) {
119 if ($r->method eq "GET" and $r->url->path =~ m^/statuscode/(\d+)^) { 169 while (my $r = $c->get_request) {
120 $c->send_basic_header($1); 170 if ($r->method eq "GET" and $r->url->path =~ m^/statuscode/(\d+)^) {
121 $c->send_crlf; 171 $c->send_basic_header($1);
122 } elsif ($r->method eq "GET" and $r->url->path =~ m^/file/(.*)^) { 172 $c->send_crlf;
123 $c->send_basic_header; 173 } elsif ($r->method eq "GET" and $r->url->path =~ m^/file/(.*)^) {
124 $c->send_crlf; 174 $c->send_basic_header;
125 $c->send_file_response("$Bin/var/$1"); 175 $c->send_crlf;
126 } elsif ($r->method eq "GET" and $r->url->path eq "/slow") { 176 $c->send_file_response("$Bin/var/$1");
127 $c->send_basic_header; 177 } elsif ($r->method eq "GET" and $r->url->path eq "/slow") {
128 $c->send_crlf; 178 $c->send_basic_header;
129 sleep 1; 179 $c->send_crlf;
130 $c->send_response("slow"); 180 sleep 1;
131 } elsif ($r->url->path eq "/method") { 181 $c->send_response("slow");
132 if ($r->method eq "DELETE") { 182 } elsif ($r->url->path eq "/method") {
133 $c->send_error(HTTP::Status->RC_METHOD_NOT_ALLOWED); 183 if ($r->method eq "DELETE") {
134 } elsif ($r->method eq "foo") { 184 $c->send_error(HTTP::Status->RC_METHOD_NOT_ALLOWED);
135 $c->send_error(HTTP::Status->RC_NOT_IMPLEMENTED); 185 } elsif ($r->method eq "foo") {
186 $c->send_error(HTTP::Status->RC_NOT_IMPLEMENTED);
187 } else {
188 $c->send_status_line(200, $r->method);
189 }
190 } elsif ($r->url->path eq "/postdata") {
191 $c->send_basic_header;
192 $c->send_crlf;
193 $c->send_response($r->method.":".$r->content);
194 } elsif ($r->url->path eq "/redirect") {
195 $c->send_redirect( "/redirect2" );
196 } elsif ($r->url->path eq "/redir_external") {
197 $c->send_redirect(($d->isa('HTTP::Daemon::SSL') ? "https" : "http") . "://169.254.169.254/redirect2" );
198 } elsif ($r->url->path eq "/redirect2") {
199 $c->send_basic_header;
200 $c->send_crlf;
201 $c->send_response(HTTP::Response->new( 200, 'OK', undef, 'redirected' ));
202 } elsif ($r->url->path eq "/redir_timeout") {
203 $c->send_redirect( "/timeout" );
204 } elsif ($r->url->path eq "/timeout") {
205 # Keep $c from being destroyed, but prevent severe leaks
206 unshift @persist, $c;
207 delete($persist[1000]);
208 next MAINLOOP;
209 } elsif ($r->url->path eq "/header_check") {
210 $c->send_basic_header;
211 $c->send_header('foo');
212 $c->send_crlf;
213 } elsif ($r->url->path eq "/virtual_port") {
214 # return sent Host header
215 $c->send_basic_header;
216 $c->send_crlf;
217 $c->send_response(HTTP::Response->new( 200, 'OK', undef, $r->header ('Host')));
218 } elsif ($r->url->path eq "/chunked") {
219 my $chunks = ["chunked", "encoding", "test\n"];
220 $c->send_response(HTTP::Response->new( 200, 'OK', undef, sub {
221 my $chunk = shift @{$chunks};
222 return unless $chunk;
223 sleep(1);
224 return($chunk);
225 }));
136 } else { 226 } else {
137 $c->send_status_line(200, $r->method); 227 $c->send_error(HTTP::Status->RC_FORBIDDEN);
138 } 228 }
139 } elsif ($r->url->path eq "/postdata") { 229 $c->close;
140 $c->send_basic_header;
141 $c->send_crlf;
142 $c->send_response($r->method.":".$r->content);
143 } elsif ($r->url->path eq "/redirect") {
144 $c->send_redirect( "/redirect2" );
145 } elsif ($r->url->path eq "/redir_external") {
146 $c->send_redirect(($d->isa('HTTP::Daemon::SSL') ? "https" : "http") . "://169.254.169.254/redirect2" );
147 } elsif ($r->url->path eq "/redirect2") {
148 $c->send_basic_header;
149 $c->send_crlf;
150 $c->send_response(HTTP::Response->new( 200, 'OK', undef, 'redirected' ));
151 } elsif ($r->url->path eq "/redir_timeout") {
152 $c->send_redirect( "/timeout" );
153 } elsif ($r->url->path eq "/timeout") {
154 # Keep $c from being destroyed, but prevent severe leaks
155 unshift @persist, $c;
156 delete($persist[1000]);
157 next MAINLOOP;
158 } elsif ($r->url->path eq "/header_check") {
159 $c->send_basic_header;
160 $c->send_header('foo');
161 $c->send_crlf;
162 } elsif ($r->url->path eq "/virtual_port") {
163 # return sent Host header
164 $c->send_basic_header;
165 $c->send_crlf;
166 $c->send_response(HTTP::Response->new( 200, 'OK', undef, $r->header ('Host')));
167 } else {
168 $c->send_error(HTTP::Status->RC_FORBIDDEN);
169 } 230 }
170 $c->close;
171 } 231 }
172 } 232 }
173} 233}
174 234
235sub run_hacked_http_server {
236 my $socket = shift;
237
238 # auto-flush on socket
239 $| = 1;
240
241
242 while(1)
243 {
244 # waiting for a new client connection
245 my $client_socket = $socket->accept();
246
247 # get information about a newly connected client
248 my $client_address = $client_socket->peerhost();
249 my $client_portn = $client_socket->peerport();
250 print "connection from $client_address:$client_portn";
251
252 # read up to 1024 characters from the connected client
253 my $data = "";
254 $client_socket->recv($data, 1024);
255 print "received data: $data";
256
257 # write response data to the connected client
258 $data = "HTTP/1.1 200 OK\r\nTransfer-Encoding: chunked\r\n\r\n0\r\n\r\n";
259 $client_socket->send($data);
260
261 # notify client that response has been sent
262 shutdown($client_socket, 1);
263 }
264}
265
175END { 266END {
176 foreach my $pid (@pids) { 267 foreach my $pid (@pids) {
177 if ($pid) { print "Killing $pid\n"; kill "INT", $pid } 268 if ($pid) { print "Killing $pid\n"; kill "INT", $pid }
@@ -185,32 +276,52 @@ if ($ARGV[0] && $ARGV[0] eq "-d") {
185} 276}
186 277
187my $result; 278my $result;
188my $command = "./check_http -H 127.0.0.1"; 279my $command = "./$plugin -H 127.0.0.1";
189 280
281run_chunked_encoding_special_test( {command => "$command -p $port_hacked_http"});
190run_common_tests( { command => "$command -p $port_http" } ); 282run_common_tests( { command => "$command -p $port_http" } );
191SKIP: { 283SKIP: {
192 skip "HTTP::Daemon::SSL not installed", $common_tests + $ssl_only_tests if ! exists $servers->{https}; 284 skip "HTTP::Daemon::SSL not installed", $common_tests + $ssl_only_tests if ! exists $servers->{https};
193 run_common_tests( { command => "$command -p $port_https", ssl => 1 } ); 285 run_common_tests( { command => "$command -p $port_https", ssl => 1 } );
194 286
287 my $expiry = "Thu Nov 28 21:02:11 2030 +0000";
288
195 $result = NPTest->testCmd( "$command -p $port_https -S -C 14" ); 289 $result = NPTest->testCmd( "$command -p $port_https -S -C 14" );
196 is( $result->return_code, 0, "$command -p $port_https -S -C 14" ); 290 is( $result->return_code, 0, "$command -p $port_https -S -C 14" );
197 is( $result->output, 'OK - Certificate \'Ton Voon\' will expire on Sun Mar 3 21:41:28 2019 +0000.', "output ok" ); 291 is( $result->output, "OK - Certificate 'Monitoring Plugins' will expire on $expiry.", "output ok" );
198 292
199 $result = NPTest->testCmd( "$command -p $port_https -S -C 14000" ); 293 $result = NPTest->testCmd( "$command -p $port_https -S -C 14000" );
200 is( $result->return_code, 1, "$command -p $port_https -S -C 14000" ); 294 is( $result->return_code, 1, "$command -p $port_https -S -C 14000" );
201 like( $result->output, '/WARNING - Certificate \'Ton Voon\' expires in \d+ day\(s\) \(Sun Mar 3 21:41:28 2019 \+0000\)./', "output ok" ); 295 like( $result->output, '/WARNING - Certificate \'Monitoring Plugins\' expires in \d+ day\(s\) \(' . quotemeta($expiry) . '\)./', "output ok" );
202 296
203 # Expired cert tests 297 # Expired cert tests
204 $result = NPTest->testCmd( "$command -p $port_https -S -C 13960,14000" ); 298 $result = NPTest->testCmd( "$command -p $port_https -S -C 13960,14000" );
205 is( $result->return_code, 2, "$command -p $port_https -S -C 13960,14000" ); 299 is( $result->return_code, 2, "$command -p $port_https -S -C 13960,14000" );
206 like( $result->output, '/CRITICAL - Certificate \'Ton Voon\' expires in \d+ day\(s\) \(Sun Mar 3 21:41:28 2019 \+0000\)./', "output ok" ); 300 like( $result->output, '/CRITICAL - Certificate \'Monitoring Plugins\' expires in \d+ day\(s\) \(' . quotemeta($expiry) . '\)./', "output ok" );
207 301
208 $result = NPTest->testCmd( "$command -p $port_https_expired -S -C 7" ); 302 $result = NPTest->testCmd( "$command -p $port_https_expired -S -C 7" );
209 is( $result->return_code, 2, "$command -p $port_https_expired -S -C 7" ); 303 is( $result->return_code, 2, "$command -p $port_https_expired -S -C 7" );
210 is( $result->output, 304 is( $result->output,
211 'CRITICAL - Certificate \'Ton Voon\' expired on Thu Mar 5 00:13:16 2009 +0000.', 305 'CRITICAL - Certificate \'Monitoring Plugins\' expired on Wed Jan 2 12:00:00 2008 +0000.',
212 "output ok" ); 306 "output ok" );
213 307
308 # client cert tests
309 my $cmd;
310 $cmd = "$command -p $port_https_clientcert"
311 . " -J \"$Bin/certs/client-cert.pem\""
312 . " -K \"$Bin/certs/client-key.pem\""
313 . " -u /statuscode/200";
314 $result = NPTest->testCmd($cmd);
315 is( $result->return_code, 0, $cmd);
316 like( $result->output, '/^HTTP OK: HTTP/1.1 200 OK - \d+ bytes in [\d\.]+ second/', "Output correct: ".$result->output );
317
318 $cmd = "$command -p $port_https_clientcert"
319 . " -J \"$Bin/certs/clientchain-cert.pem\""
320 . " -K \"$Bin/certs/clientchain-key.pem\""
321 . " -u /statuscode/200";
322 $result = NPTest->testCmd($cmd);
323 is( $result->return_code, 0, $cmd);
324 like( $result->output, '/^HTTP OK: HTTP/1.1 200 OK - \d+ bytes in [\d\.]+ second/', "Output correct: ".$result->output );
214} 325}
215 326
216my $cmd; 327my $cmd;
@@ -409,22 +520,24 @@ sub run_common_tests {
409 520
410 # stickyport - on full urlS port is set back to 80 otherwise 521 # stickyport - on full urlS port is set back to 80 otherwise
411 $cmd = "$command -f stickyport -u /redir_external -t 5 -s redirected"; 522 $cmd = "$command -f stickyport -u /redir_external -t 5 -s redirected";
523 alarm(2);
412 eval { 524 eval {
413 local $SIG{ALRM} = sub { die "alarm\n" }; 525 local $SIG{ALRM} = sub { die "alarm\n" };
414 alarm(2);
415 $result = NPTest->testCmd( $cmd ); 526 $result = NPTest->testCmd( $cmd );
416 alarm(0); }; 527 };
417 isnt( $@, "alarm\n", $cmd ); 528 isnt( $@, "alarm\n", $cmd );
529 alarm(0);
418 is( $result->return_code, 0, $cmd ); 530 is( $result->return_code, 0, $cmd );
419 531
420 # Let's hope there won't be any web server on :80 returning "redirected"! 532 # Let's hope there won't be any web server on :80 returning "redirected"!
421 $cmd = "$command -f sticky -u /redir_external -t 5 -s redirected"; 533 $cmd = "$command -f sticky -u /redir_external -t 5 -s redirected";
534 alarm(2);
422 eval { 535 eval {
423 local $SIG{ALRM} = sub { die "alarm\n" }; 536 local $SIG{ALRM} = sub { die "alarm\n" };
424 alarm(2);
425 $result = NPTest->testCmd( $cmd ); 537 $result = NPTest->testCmd( $cmd );
426 alarm(0); }; 538 };
427 isnt( $@, "alarm\n", $cmd ); 539 isnt( $@, "alarm\n", $cmd );
540 alarm(0);
428 isnt( $result->return_code, 0, $cmd ); 541 isnt( $result->return_code, 0, $cmd );
429 542
430 # Test an external address - timeout 543 # Test an external address - timeout
@@ -449,4 +562,20 @@ sub run_common_tests {
449 }; 562 };
450 is( $@, "", $cmd ); 563 is( $@, "", $cmd );
451 564
565 $cmd = "$command -u /chunked -s 'chunkedencodingtest' -d 'Transfer-Encoding: chunked'";
566 eval {
567 $result = NPTest->testCmd( $cmd, 5 );
568 };
569 is( $@, "", $cmd );
570}
571
572sub run_chunked_encoding_special_test {
573 my ($opts) = @_;
574 my $command = $opts->{command};
575
576 $cmd = "$command -u / -s 'ChunkedEncodingSpecialTest'";
577 eval {
578 $result = NPTest->testCmd( $cmd, 5 );
579 };
580 is( $@, "", $cmd );
452} 581}