diff options
| -rwxr-xr-x | plugins/tests/check_curl.t | 194 |
1 files changed, 193 insertions, 1 deletions
diff --git a/plugins/tests/check_curl.t b/plugins/tests/check_curl.t index 52c5ad1c..bc0101a2 100755 --- a/plugins/tests/check_curl.t +++ b/plugins/tests/check_curl.t | |||
| @@ -20,6 +20,11 @@ use Test::More; | |||
| 20 | use NPTest; | 20 | use NPTest; |
| 21 | use FindBin qw($Bin); | 21 | use FindBin qw($Bin); |
| 22 | 22 | ||
| 23 | use URI; | ||
| 24 | use URI::QueryParam; | ||
| 25 | use HTTP::Daemon; | ||
| 26 | use HTTP::Daemon::SSL; | ||
| 27 | |||
| 23 | $ENV{'LC_TIME'} = "C"; | 28 | $ENV{'LC_TIME'} = "C"; |
| 24 | 29 | ||
| 25 | my $common_tests = 75; | 30 | my $common_tests = 75; |
| @@ -186,6 +191,149 @@ sub run_server { | |||
| 186 | $c->send_response('moved to /redirect2'); | 191 | $c->send_response('moved to /redirect2'); |
| 187 | } elsif ($r->url->path eq "/redir_timeout") { | 192 | } elsif ($r->url->path eq "/redir_timeout") { |
| 188 | $c->send_redirect( "/timeout" ); | 193 | $c->send_redirect( "/timeout" ); |
| 194 | } elsif ($r->url->path =~ m{^/redirect_with_increment}) { | ||
| 195 | # <scheme>://<username>:<password>@<host>:<port>/<path>;<parameters>?<query>#<fragment> | ||
| 196 | # Find every parameter, query , and fragment keys and increment them | ||
| 197 | |||
| 198 | my $content = ""; | ||
| 199 | |||
| 200 | # Use URI to help with query/fragment; parse path params manually. | ||
| 201 | my $original_url = $r->url->as_string; | ||
| 202 | $content .= " original_url: ${original_url}\n"; | ||
| 203 | my $uri = URI->new($original_url); | ||
| 204 | $content .= " uri: ${uri}\n"; | ||
| 205 | |||
| 206 | my $path = $uri->path // ''; | ||
| 207 | my $query = $uri->query // ''; | ||
| 208 | my $fragment = $uri->fragment // ''; | ||
| 209 | |||
| 210 | $content .= " path: ${path}\n"; | ||
| 211 | $content .= " query: ${query}\n"; | ||
| 212 | $content .= " fragment: ${fragment}\n"; | ||
| 213 | |||
| 214 | # Sets and returns the scheme-specific part of the $uri (everything between the scheme and the fragment) as an escaped string. | ||
| 215 | #my $opaque = $uri->opaque; | ||
| 216 | #$content .= " opaque: ${opaque}\n"; | ||
| 217 | |||
| 218 | # group 1 is captured: anything that is not '/' : ([^/]*) | ||
| 219 | # / matches the / directly | ||
| 220 | # group 2 is captured: anything : (.*) | ||
| 221 | #my ($before_slash, $after_slash) = $opaque =~ m{^/([^/]*)/(.*)$}; | ||
| 222 | #$before_slash //= ''; | ||
| 223 | #$after_slash //= ''; | ||
| 224 | #$content .= " before_slash: ${before_slash}\n"; | ||
| 225 | #$content .= " after_slash: ${after_slash}\n"; | ||
| 226 | |||
| 227 | # split the uri part and parameters. uri package cannot do this | ||
| 228 | # group 1 is captured: anything without a semicolon: ([^;]) | ||
| 229 | # group 2 is uncaptured: (?:;(.*))? | ||
| 230 | # (? )? prevents the capture | ||
| 231 | # in between the ';' matches the first ever semicolon | ||
| 232 | # group3 is captured: any character stirng : (.*) | ||
| 233 | my ($before_params, $params) = $uri =~ m{^([^;]*)(?:;(.*))?\?}; | ||
| 234 | $before_params //= ''; | ||
| 235 | $params //= ''; | ||
| 236 | $content .= " before_params: ${before_params}\n"; | ||
| 237 | $content .= " params: ${params}\n"; | ||
| 238 | my @parameter_pairs; | ||
| 239 | if (defined $params && length $params) { | ||
| 240 | for my $p (split /;/, $params) { | ||
| 241 | my ($key,$value) = split /=/, $p, 2; | ||
| 242 | $value //= ''; | ||
| 243 | push @parameter_pairs, [ $key, $value ]; | ||
| 244 | $content .= " parameter: ${key} -> ${value}\n"; | ||
| 245 | } | ||
| 246 | } | ||
| 247 | |||
| 248 | # query parameters are offered directly from the library | ||
| 249 | my @query_form = $uri->query_form; | ||
| 250 | my @query_parameter_pairs; | ||
| 251 | while (@query_form) { | ||
| 252 | my $key = shift @query_form; | ||
| 253 | my $value = shift @query_form; | ||
| 254 | $value //= ''; # there can be valueless keys | ||
| 255 | push @query_parameter_pairs, [ $key, $value ]; | ||
| 256 | $content .= " query: ${key} -> ${value}\n"; | ||
| 257 | } | ||
| 258 | |||
| 259 | # fragment: try to split into key=value pairs on ';' or '&' if present | ||
| 260 | my @fragment_pairs; | ||
| 261 | my $fragment_seperator = ''; | ||
| 262 | if ($fragment ne '') { | ||
| 263 | $fragment_seperator = ($fragment =~ /&/ ? '&' : ';'); | ||
| 264 | for my $f (split /[&;]/, $fragment) { | ||
| 265 | next unless length $f; | ||
| 266 | my ($key,$value) = split /=/, $f, 2; | ||
| 267 | $value //= ''; | ||
| 268 | push @fragment_pairs, [ $key, $value ]; | ||
| 269 | $content .= " fragment: ${key} -> ${value}\n"; | ||
| 270 | } | ||
| 271 | } | ||
| 272 | |||
| 273 | # helper to increment value | ||
| 274 | my $increment = sub { | ||
| 275 | my ($v) = @_; | ||
| 276 | return $v if !defined $v || $v eq ''; | ||
| 277 | # numeric integer | ||
| 278 | if ($v =~ /^-?\d+$/) { | ||
| 279 | return $v + 1; | ||
| 280 | } | ||
| 281 | # otherwise -> increment as if its an ascii character | ||
| 282 | # sed replacement syntax, but the $& holds the matched character | ||
| 283 | if (length($v)) { | ||
| 284 | (my $new_v = $v) =~ s/./chr(ord($&) + 1)/ge; | ||
| 285 | return $new_v; | ||
| 286 | } | ||
| 287 | }; | ||
| 288 | |||
| 289 | # increment values in pairs | ||
| 290 | for my $pair (@parameter_pairs) { | ||
| 291 | $pair->[1] = $increment->($pair->[1]); | ||
| 292 | $content .= " parameter new: " . $pair->[0] . " -> " . $pair->[1] . "\n"; | ||
| 293 | } | ||
| 294 | for my $pair (@query_parameter_pairs) { | ||
| 295 | $pair->[1] = $increment->($pair->[1]); | ||
| 296 | $content .= " query parameter new: " . $pair->[0] . " -> " . $pair->[1] . "\n"; | ||
| 297 | } | ||
| 298 | for my $pair (@fragment_pairs) { | ||
| 299 | $pair->[1] = $increment->($pair->[1]); | ||
| 300 | $content .= " fragment new: " . $pair->[0] . " -> " . $pair->[1] . "\n"; | ||
| 301 | } | ||
| 302 | |||
| 303 | # rebuild strings | ||
| 304 | my $new_parameter_str = join(';', map { $_->[0] . '=' . $_->[1] } @parameter_pairs); | ||
| 305 | $content .= " new_parameter_str: ${new_parameter_str}\n"; | ||
| 306 | |||
| 307 | # library can rebuild from an array | ||
| 308 | my @new_query_form; | ||
| 309 | for my $p (@query_parameter_pairs) { push @new_query_form, $p->[0], $p->[1] } | ||
| 310 | |||
| 311 | my $new_fragment_str = ''; | ||
| 312 | if (@fragment_pairs) { | ||
| 313 | $new_fragment_str = join($fragment_seperator, map { $_->[0] . '=' . $_->[1] } @fragment_pairs); | ||
| 314 | } | ||
| 315 | $content .= " new_fragment_str: ${new_fragment_str}\n"; | ||
| 316 | |||
| 317 | # construct new URI using the library | ||
| 318 | my $new_uri = URI->new(''); | ||
| 319 | $new_uri->path( $before_params . ($new_parameter_str ? ';' . $new_parameter_str : '') ); | ||
| 320 | $new_uri->query_form( \@new_query_form ) if @new_query_form; | ||
| 321 | $new_uri->fragment( $new_fragment_str ) if $new_fragment_str ne ''; | ||
| 322 | $content .= " new_uri: ${new_uri}\n"; | ||
| 323 | |||
| 324 | # Redirect until fail_count or redirect_count reaches 3 | ||
| 325 | if ($new_uri =~ /fail_count=3/){ | ||
| 326 | $c->send_error(HTTP::Status->RC_FORBIDDEN, "fail count reached 3, url path:" . $r->url->path ); | ||
| 327 | } elsif ($new_uri =~ /redirect_count=3/){ | ||
| 328 | $c->send_response(HTTP::Response->new( 200, 'OK', undef , $content )); | ||
| 329 | } elsif ($new_uri =~ /location_redirect_count=3/){ | ||
| 330 | $c->send_basic_header(302); | ||
| 331 | $c->send_header("Location", "$new_uri" ); | ||
| 332 | $c->send_crlf; | ||
| 333 | $c->send_response("$content \n moved to $new_uri"); | ||
| 334 | } else { | ||
| 335 | $c->send_redirect( $new_uri->as_string, 301, $content ); | ||
| 336 | } | ||
| 189 | } elsif ($r->url->path eq "/timeout") { | 337 | } elsif ($r->url->path eq "/timeout") { |
| 190 | # Keep $c from being destroyed, but prevent severe leaks | 338 | # Keep $c from being destroyed, but prevent severe leaks |
| 191 | unshift @persist, $c; | 339 | unshift @persist, $c; |
| @@ -215,7 +363,7 @@ sub run_server { | |||
| 215 | return($chunk); | 363 | return($chunk); |
| 216 | })); | 364 | })); |
| 217 | } else { | 365 | } else { |
| 218 | $c->send_error(HTTP::Status->RC_FORBIDDEN); | 366 | $c->send_error(HTTP::Status->RC_FORBIDDEN, "unknown url path:" . $r->url->path ); |
| 219 | } | 367 | } |
| 220 | $c->close; | 368 | $c->close; |
| 221 | } | 369 | } |
| @@ -482,6 +630,50 @@ sub run_common_tests { | |||
| 482 | is( $result->return_code, 0, $cmd); | 630 | is( $result->return_code, 0, $cmd); |
| 483 | like( $result->output, '/.*HTTP/1.1 200 OK - \d+ bytes in [\d\.]+ second.*/', "Output correct: ".$result->output ); | 631 | like( $result->output, '/.*HTTP/1.1 200 OK - \d+ bytes in [\d\.]+ second.*/', "Output correct: ".$result->output ); |
| 484 | 632 | ||
| 633 | # Redirect with increment tests. These are for checking if the url parameters, query parameters and fragment are parsed. | ||
| 634 | # The server at this point has dynamic redirection. It tries to increment values that it sees in these fields, then redirects. | ||
| 635 | # It also appends some debug log and writes it into HTTP content, pass the -vvv parameter to see them. | ||
| 636 | |||
| 637 | $cmd = "$command -p $port_http -u '/redirect_with_increment/path1/path2/path3/path4' --onredirect=follow -vvv"; | ||
| 638 | $result = NPTest->testCmd( "$cmd" ); | ||
| 639 | is( $result->return_code, 1, $cmd); | ||
| 640 | like( $result->output, '/.*HTTP/1.1 403 Forbidden - \d+ bytes in [\d\.]+ second.*/', "Output correct, redirect_count was not present, got redirected to / : ".$result->output ); | ||
| 641 | |||
| 642 | $cmd = "$command -p $port_http -u '/redirect_with_increment/path1/path2;redirect_count=0;p1=1;p2=ab?qp1=10&qp2=kl#f1=test' --onredirect=follow -vvv"; | ||
| 643 | $result = NPTest->testCmd( "$cmd" ); | ||
| 644 | is( $result->return_code, 0, $cmd); | ||
| 645 | like( $result->output, '/.*HTTP/1.1 200 OK - \d+ bytes in [\d\.]+ second.*/', "Output correct, redirect_count went up to 3: ".$result->output ); | ||
| 646 | |||
| 647 | $cmd = "$command -p $port_http -u '/redirect_with_increment/path1/path2;location_redirect_count=0;p1=1;p2=ab?qp1=10&qp2=kl#f1=test' --onredirect=follow -vvv"; | ||
| 648 | $result = NPTest->testCmd( "$cmd" ); | ||
| 649 | is( $result->return_code, 0, $cmd); | ||
| 650 | like( $result->output, '/.*HTTP/1.1 200 OK - \d+ bytes in [\d\.]+ second.*/', "Output correct, location_redirect_count went up to 3: ".$result->output ); | ||
| 651 | |||
| 652 | $cmd = "$command -p $port_http -u '/redirect_with_increment/path1/path2;redirect_count=0;fail_count=2' --onredirect=follow -vvv"; | ||
| 653 | $result = NPTest->testCmd( "$cmd" ); | ||
| 654 | is( $result->return_code, 1, $cmd); | ||
| 655 | like( $result->output, '/.*HTTP/1.1 403 Forbidden - \d+ bytes in [\d\.]+ second.*/', "Output correct, early due to fail_count reaching 3: ".$result->output ); | ||
| 656 | |||
| 657 | $cmd = "$command -p $port_http -u '/redirect_with_increment/path1/path2;redirect_count=0;p1=1;p2=ab?qp1=10&qp2=kl#f1=test' --onredirect=follow -vvv"; | ||
| 658 | $result = NPTest->testCmd( "$cmd" ); | ||
| 659 | is( $result->return_code, 0, $cmd); | ||
| 660 | like( $result->output, '/.*;p1=3;p2=cd\?*/', "Output correct, parsed and incremented both parameters p1 and p2 : ".$result->output ); | ||
| 661 | |||
| 662 | $cmd = "$command -p $port_http -u '/redirect_with_increment/path1/path2;redirect_count=0;p1=1;p2=ab?qp1=10&qp2=kl#f1=test' --onredirect=follow -vvv"; | ||
| 663 | $result = NPTest->testCmd( "$cmd" ); | ||
| 664 | is( $result->return_code, 0, $cmd); | ||
| 665 | like( $result->output, '/.*\?qp1=12&qp2=mn*/', "Output correct, parsed and incremented both query parameters qp1 and qp2 : ".$result->output ); | ||
| 666 | |||
| 667 | $cmd = "$command -p $port_http -u '/redirect_with_increment;redirect_count=0;?qp0=0&qp1=1&qp2=2&qp3=3&qp4=4&qp5=5' --onredirect=follow -vvv"; | ||
| 668 | $result = NPTest->testCmd( "$cmd" ); | ||
| 669 | is( $result->return_code, 0, $cmd); | ||
| 670 | like( $result->output, '/.*\?qp0=2&qp1=3&qp2=4&qp3=5&qp4=6&qp5=7*/', "Output correct, parsed and incremented query parameters qp1,qp2,qp3,qp4,qp5 in order : ".$result->output ); | ||
| 671 | |||
| 672 | $cmd = "$command -p $port_http -u '/redirect_with_increment/path1/path2;redirect_count=0;p1=1;p2=ab?qp1=10&qp2=kl#f1=test' --onredirect=follow -vvv"; | ||
| 673 | $result = NPTest->testCmd( "$cmd" ); | ||
| 674 | is( $result->return_code, 0, $cmd); | ||
| 675 | like( $result->output, '/.*#f1=vguv*/', "Output correct, parsed and incremented fragment f1 : ".$result->output ); | ||
| 676 | |||
| 485 | # These tests may block | 677 | # These tests may block |
| 486 | # stickyport - on full urlS port is set back to 80 otherwise | 678 | # stickyport - on full urlS port is set back to 80 otherwise |
| 487 | $cmd = "$command -f stickyport -u /redir_external -t 5 -s redirected"; | 679 | $cmd = "$command -f stickyport -u /redir_external -t 5 -s redirected"; |
