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.t341
1 files changed, 230 insertions, 111 deletions
diff --git a/plugins/tests/check_http.t b/plugins/tests/check_http.t
index 188f5e7..6078b27 100755
--- a/plugins/tests/check_http.t
+++ b/plugins/tests/check_http.t
@@ -3,27 +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# to create a new expired certificate:
8# faketime '2008-01-01 12:00:00' openssl req -new -x509 -keyout expired-key.pem -out expired-cert.pem -days 1 -nodes
9# Country Name (2 letter code) [AU]:DE
10# State or Province Name (full name) [Some-State]:Bavaria
11# Locality Name (eg, city) []:Munich
12# Organization Name (eg, company) [Internet Widgits Pty Ltd]:Monitoring Plugins
13# Organizational Unit Name (eg, section) []:
14# Common Name (e.g. server FQDN or YOUR name) []:Monitoring Plugins
15# Email Address []:devel@monitoring-plugins.org
16 7
17use strict; 8use strict;
18use Test::More; 9use Test::More;
19use NPTest; 10use NPTest;
20use FindBin qw($Bin); 11use FindBin qw($Bin);
12use IO::Socket::INET;
21 13
22$ENV{'LC_TIME'} = "C"; 14$ENV{'LC_TIME'} = "C";
23 15
24my $common_tests = 70; 16my $common_tests = 71;
25my $virtual_port_tests = 8; 17my $virtual_port_tests = 8;
26my $ssl_only_tests = 8; 18my $ssl_only_tests = 12;
19my $chunked_encoding_special_tests = 1;
27# Check that all dependent modules are available 20# Check that all dependent modules are available
28eval "use HTTP::Daemon 6.01;"; 21eval "use HTTP::Daemon 6.01;";
29plan skip_all => 'HTTP::Daemon >= 6.01 required' if $@; 22plan skip_all => 'HTTP::Daemon >= 6.01 required' if $@;
@@ -39,7 +32,7 @@ if ($@) {
39 plan skip_all => "Missing required module for test: $@"; 32 plan skip_all => "Missing required module for test: $@";
40} else { 33} else {
41 if (-x "./$plugin") { 34 if (-x "./$plugin") {
42 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;
43 } else { 36 } else {
44 plan skip_all => "No $plugin compiled"; 37 plan skip_all => "No $plugin compiled";
45 } 38 }
@@ -59,61 +52,110 @@ $HTTP::Daemon::VERSION = "1.00";
59my $port_http = 50000 + int(rand(1000)); 52my $port_http = 50000 + int(rand(1000));
60my $port_https = $port_http + 1; 53my $port_https = $port_http + 1;
61my $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;
62 57
63# This array keeps sockets around for implementing timeouts 58# This array keeps sockets around for implementing timeouts
64my @persist; 59my @persist;
65 60
66# Start up all servers 61# Start up all servers
67my @pids; 62my @pids;
68my $pid = fork(); 63# Fork a HTTP server
69if ($pid) { 64my $pid = fork;
70 # Parent 65defined $pid or die "Failed to fork";
71 push @pids, $pid; 66if (!$pid) {
72 if (exists $servers->{https}) { 67 undef @pids;
73 # Fork a normal HTTPS server
74 $pid = fork();
75 if ($pid) {
76 # Parent
77 push @pids, $pid;
78 # Fork an expired cert server
79 $pid = fork();
80 if ($pid) {
81 push @pids, $pid;
82 } else {
83 my $d = HTTP::Daemon::SSL->new(
84 LocalPort => $port_https_expired,
85 LocalAddr => "127.0.0.1",
86 SSL_cert_file => "$Bin/certs/expired-cert.pem",
87 SSL_key_file => "$Bin/certs/expired-key.pem",
88 ) || die;
89 print "Please contact https expired at: <URL:", $d->url, ">\n";
90 run_server( $d );
91 exit;
92 }
93 } else {
94 # closing the connection after -C cert checks make the daemon exit with a sigpipe otherwise
95 local $SIG{'PIPE'} = 'IGNORE';
96 my $d = HTTP::Daemon::SSL->new(
97 LocalPort => $port_https,
98 LocalAddr => "127.0.0.1",
99 SSL_cert_file => "$Bin/certs/server-cert.pem",
100 SSL_key_file => "$Bin/certs/server-key.pem",
101 ) || die;
102 print "Please contact https at: <URL:", $d->url, ">\n";
103 run_server( $d );
104 exit;
105 }
106 }
107} else {
108 # Child
109 #print "child\n";
110 my $d = HTTP::Daemon->new( 68 my $d = HTTP::Daemon->new(
111 LocalPort => $port_http, 69 LocalPort => $port_http,
112 LocalAddr => "127.0.0.1", 70 LocalAddr => "127.0.0.1",
113 ) || die; 71 ) || die;
114 print "Please contact http at: <URL:", $d->url, ">\n"; 72 print "Please contact http at: <URL:", $d->url, ">\n";
115 run_server( $d ); 73 run_server( $d );
116 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";
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;
117} 159}
118 160
119# give our webservers some time to startup 161# give our webservers some time to startup
@@ -122,64 +164,105 @@ sleep(3);
122# Run the same server on http and https 164# Run the same server on http and https
123sub run_server { 165sub run_server {
124 my $d = shift; 166 my $d = shift;
125 MAINLOOP: while (my $c = $d->accept ) { 167 while (1) {
126 while (my $r = $c->get_request) { 168 MAINLOOP: while (my $c = $d->accept) {
127 if ($r->method eq "GET" and $r->url->path =~ m^/statuscode/(\d+)^) { 169 while (my $r = $c->get_request) {
128 $c->send_basic_header($1); 170 if ($r->method eq "GET" and $r->url->path =~ m^/statuscode/(\d+)^) {
129 $c->send_crlf; 171 $c->send_basic_header($1);
130 } elsif ($r->method eq "GET" and $r->url->path =~ m^/file/(.*)^) { 172 $c->send_crlf;
131 $c->send_basic_header; 173 } elsif ($r->method eq "GET" and $r->url->path =~ m^/file/(.*)^) {
132 $c->send_crlf; 174 $c->send_basic_header;
133 $c->send_file_response("$Bin/var/$1"); 175 $c->send_crlf;
134 } elsif ($r->method eq "GET" and $r->url->path eq "/slow") { 176 $c->send_file_response("$Bin/var/$1");
135 $c->send_basic_header; 177 } elsif ($r->method eq "GET" and $r->url->path eq "/slow") {
136 $c->send_crlf; 178 $c->send_basic_header;
137 sleep 1; 179 $c->send_crlf;
138 $c->send_response("slow"); 180 sleep 1;
139 } elsif ($r->url->path eq "/method") { 181 $c->send_response("slow");
140 if ($r->method eq "DELETE") { 182 } elsif ($r->url->path eq "/method") {
141 $c->send_error(HTTP::Status->RC_METHOD_NOT_ALLOWED); 183 if ($r->method eq "DELETE") {
142 } elsif ($r->method eq "foo") { 184 $c->send_error(HTTP::Status->RC_METHOD_NOT_ALLOWED);
143 $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 }));
144 } else { 226 } else {
145 $c->send_status_line(200, $r->method); 227 $c->send_error(HTTP::Status->RC_FORBIDDEN);
146 } 228 }
147 } elsif ($r->url->path eq "/postdata") { 229 $c->close;
148 $c->send_basic_header;
149 $c->send_crlf;
150 $c->send_response($r->method.":".$r->content);
151 } elsif ($r->url->path eq "/redirect") {
152 $c->send_redirect( "/redirect2" );
153 } elsif ($r->url->path eq "/redir_external") {
154 $c->send_redirect(($d->isa('HTTP::Daemon::SSL') ? "https" : "http") . "://169.254.169.254/redirect2" );
155 } elsif ($r->url->path eq "/redirect2") {
156 $c->send_basic_header;
157 $c->send_crlf;
158 $c->send_response(HTTP::Response->new( 200, 'OK', undef, 'redirected' ));
159 } elsif ($r->url->path eq "/redir_timeout") {
160 $c->send_redirect( "/timeout" );
161 } elsif ($r->url->path eq "/timeout") {
162 # Keep $c from being destroyed, but prevent severe leaks
163 unshift @persist, $c;
164 delete($persist[1000]);
165 next MAINLOOP;
166 } elsif ($r->url->path eq "/header_check") {
167 $c->send_basic_header;
168 $c->send_header('foo');
169 $c->send_crlf;
170 } elsif ($r->url->path eq "/virtual_port") {
171 # return sent Host header
172 $c->send_basic_header;
173 $c->send_crlf;
174 $c->send_response(HTTP::Response->new( 200, 'OK', undef, $r->header ('Host')));
175 } else {
176 $c->send_error(HTTP::Status->RC_FORBIDDEN);
177 } 230 }
178 $c->close;
179 } 231 }
180 } 232 }
181} 233}
182 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
183END { 266END {
184 foreach my $pid (@pids) { 267 foreach my $pid (@pids) {
185 if ($pid) { print "Killing $pid\n"; kill "INT", $pid } 268 if ($pid) { print "Killing $pid\n"; kill "INT", $pid }
@@ -195,30 +278,50 @@ if ($ARGV[0] && $ARGV[0] eq "-d") {
195my $result; 278my $result;
196my $command = "./$plugin -H 127.0.0.1"; 279my $command = "./$plugin -H 127.0.0.1";
197 280
281run_chunked_encoding_special_test( {command => "$command -p $port_hacked_http"});
198run_common_tests( { command => "$command -p $port_http" } ); 282run_common_tests( { command => "$command -p $port_http" } );
199SKIP: { 283SKIP: {
200 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};
201 run_common_tests( { command => "$command -p $port_https", ssl => 1 } ); 285 run_common_tests( { command => "$command -p $port_https", ssl => 1 } );
202 286
287 my $expiry = "Thu Nov 28 21:02:11 2030 +0000";
288
203 $result = NPTest->testCmd( "$command -p $port_https -S -C 14" ); 289 $result = NPTest->testCmd( "$command -p $port_https -S -C 14" );
204 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" );
205 is( $result->output, "OK - Certificate 'Monitoring Plugins' will expire on Fri Feb 16 15:31:44 2029 +0000.", "output ok" ); 291 is( $result->output, "OK - Certificate 'Monitoring Plugins' will expire on $expiry.", "output ok" );
206 292
207 $result = NPTest->testCmd( "$command -p $port_https -S -C 14000" ); 293 $result = NPTest->testCmd( "$command -p $port_https -S -C 14000" );
208 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" );
209 like( $result->output, '/WARNING - Certificate \'Monitoring Plugins\' expires in \d+ day\(s\) \(Fri Feb 16 15:31:44 2029 \+0000\)./', "output ok" ); 295 like( $result->output, '/WARNING - Certificate \'Monitoring Plugins\' expires in \d+ day\(s\) \(' . quotemeta($expiry) . '\)./', "output ok" );
210 296
211 # Expired cert tests 297 # Expired cert tests
212 $result = NPTest->testCmd( "$command -p $port_https -S -C 13960,14000" ); 298 $result = NPTest->testCmd( "$command -p $port_https -S -C 13960,14000" );
213 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" );
214 like( $result->output, '/CRITICAL - Certificate \'Monitoring Plugins\' expires in \d+ day\(s\) \(Fri Feb 16 15:31:44 2029 \+0000\)./', "output ok" ); 300 like( $result->output, '/CRITICAL - Certificate \'Monitoring Plugins\' expires in \d+ day\(s\) \(' . quotemeta($expiry) . '\)./', "output ok" );
215 301
216 $result = NPTest->testCmd( "$command -p $port_https_expired -S -C 7" ); 302 $result = NPTest->testCmd( "$command -p $port_https_expired -S -C 7" );
217 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" );
218 is( $result->output, 304 is( $result->output,
219 'CRITICAL - Certificate \'Monitoring Plugins\' expired on Wed Jan 2 11:00:26 2008 +0000.', 305 'CRITICAL - Certificate \'Monitoring Plugins\' expired on Wed Jan 2 12:00:00 2008 +0000.',
220 "output ok" ); 306 "output ok" );
221 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 );
222} 325}
223 326
224my $cmd; 327my $cmd;
@@ -459,4 +562,20 @@ sub run_common_tests {
459 }; 562 };
460 is( $@, "", $cmd ); 563 is( $@, "", $cmd );
461 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 );
462} 581}