1package Test::Nginx::Socket; 2 3use lib 'lib'; 4use lib 'inc'; 5 6use Test::Base -Base; 7 8our $VERSION = '0.18'; 9 10use Encode; 11use Data::Dumper; 12use Time::HiRes qw(sleep time); 13use Test::LongString; 14use List::MoreUtils qw( any ); 15use IO::Select (); 16 17our $ServerAddr = 'localhost'; 18our $Timeout = $ENV{TEST_NGINX_TIMEOUT} || 2; 19 20use Test::Nginx::Util qw( 21 setup_server_root 22 write_config_file 23 get_canon_version 24 get_nginx_version 25 trim 26 show_all_chars 27 parse_headers 28 run_tests 29 $ServerPortForClient 30 $ServerPort 31 $PidFile 32 $ServRoot 33 $ConfFile 34 $RunTestHelper 35 $RepeatEach 36 error_log_data 37 worker_connections 38 master_process_enabled 39 config_preamble 40 repeat_each 41 workers 42 master_on 43 master_off 44 log_level 45 no_shuffle 46 no_root_location 47 server_root 48 html_dir 49 server_port 50 no_nginx_manager 51); 52 53#use Smart::Comments::JSON '###'; 54use Fcntl qw(F_GETFL F_SETFL O_NONBLOCK); 55use POSIX qw(EAGAIN); 56use IO::Socket; 57 58#our ($PrevRequest, $PrevConfig); 59 60our $NoLongString = undef; 61 62our @EXPORT = qw( plan run_tests run_test 63 repeat_each config_preamble worker_connections 64 master_process_enabled 65 no_long_string workers master_on master_off 66 log_level no_shuffle no_root_location 67 server_addr server_root html_dir server_port 68 timeout no_nginx_manager 69); 70 71sub send_request ($$$$@); 72 73sub run_test_helper ($$); 74 75sub error_event_handler ($); 76sub read_event_handler ($); 77sub write_event_handler ($); 78sub check_response_body ($$$$$); 79sub fmt_str ($); 80 81sub no_long_string () { 82 $NoLongString = 1; 83} 84 85sub server_addr (@) { 86 if (@_) { 87 88 #warn "setting server addr to $_[0]\n"; 89 $ServerAddr = shift; 90 } 91 else { 92 return $ServerAddr; 93 } 94} 95 96sub timeout (@) { 97 if (@_) { 98 $Timeout = shift; 99 } 100 else { 101 $Timeout; 102 } 103} 104 105$RunTestHelper = \&run_test_helper; 106 107# This will parse a "request"" string. The expected format is: 108# - One line for the HTTP verb (POST, GET, etc.) plus optional relative URL 109# (default is /) plus optional HTTP version (default is HTTP/1.1). 110# - More lines considered as the body of the request. 111# Most people don't care about headers and this is enough. 112# 113# This function will return a reference to a hash with the parsed elements 114# plus information on the parsing itself like "how many white spaces were 115# skipped before the VERB" (skipped_before_method), "was the version provided" 116# (http_ver_size = 0). 117sub parse_request ($$) { 118 my ( $name, $rrequest ) = @_; 119 open my $in, '<', $rrequest; 120 my $first = <$in>; 121 if ( !$first ) { 122 Test::More::BAIL_OUT("$name - Request line should be non-empty"); 123 die; 124 } 125 #$first =~ s/^\s+|\s+$//gs; 126 my ($before_meth, $meth, $after_meth); 127 my ($rel_url, $rel_url_size, $after_rel_url); 128 my ($http_ver, $http_ver_size, $after_http_ver); 129 my $end_line_size; 130 if ($first =~ /^(\s*)(\S+)( *)((\S+)( *))?((\S+)( *))?(\s*)/) { 131 $before_meth = defined $1 ? length($1) : undef; 132 $meth = $2; 133 $after_meth = defined $3 ? length($3) : undef; 134 $rel_url = $5; 135 $rel_url_size = defined $5 ? length($5) : undef; 136 $after_rel_url = defined $6 ? length($6) : undef; 137 $http_ver = $8; 138 if (!defined $8) { 139 $http_ver_size = undef; 140 } else { 141 $http_ver_size = defined $8 ? length($8) : undef; 142 } 143 if (!defined $9) { 144 $after_http_ver = undef; 145 } else { 146 $after_http_ver = defined $9 ? length($9) : undef; 147 } 148 $end_line_size = defined $10 ? length($10) : undef; 149 } else { 150 Test::More::BAIL_OUT("$name - Request line is not valid. Should be 'meth [url [version]]'"); 151 die; 152 } 153 if ( !defined $rel_url ) { 154 $rel_url = '/'; 155 $rel_url_size = 0; 156 $after_rel_url = 0; 157 } 158 if ( !defined $http_ver ) { 159 $http_ver = 'HTTP/1.1'; 160 $http_ver_size = 0; 161 $after_http_ver = 0; 162 } 163 164 #my $url = "http://localhost:$ServerPortForClient" . $rel_url; 165 166 my $content = do { local $/; <$in> }; 167 my $content_size; 168 if ( !defined $content ) { 169 $content = ""; 170 $content_size = 0; 171 } else { 172 $content_size = length($content); 173 } 174 175 #warn Dumper($content); 176 177 close $in; 178 179 return { 180 method => $meth, 181 url => $rel_url, 182 content => $content, 183 http_ver => $http_ver, 184 skipped_before_method => $before_meth, 185 method_size => length($meth), 186 skipped_after_method => $after_meth, 187 url_size => $rel_url_size, 188 skipped_after_url => $after_rel_url, 189 http_ver_size => $http_ver_size, 190 skipped_after_http_ver => $after_http_ver + $end_line_size, 191 content_size => $content_size, 192 }; 193} 194 195# From a parsed request, builds the "moves" to apply to the original request 196# to transform it (e.g. add missing version). Elements of the returned array 197# are of 2 types: 198# - d : number of characters to remove. 199# - s_* : number of characters (s_s) to replace by value (s_v). 200sub get_moves($) { 201 my ($parsed_req) = @_; 202 return ({d => $parsed_req->{skipped_before_method}}, 203 {s_s => $parsed_req->{method_size}, 204 s_v => $parsed_req->{method}}, 205 {d => $parsed_req->{skipped_after_method}}, 206 {s_s => $parsed_req->{url_size}, 207 s_v => $parsed_req->{url}}, 208 {d => $parsed_req->{skipped_after_url}}, 209 {s_s => $parsed_req->{http_ver_size}, 210 s_v => $parsed_req->{http_ver}}, 211 {d => $parsed_req->{skipped_after_http_ver}}, 212 {s_s => 0, 213 s_v => $parsed_req->{headers}}, 214 {s_s => $parsed_req->{content_size}, 215 s_v => $parsed_req->{content}} 216 ); 217} 218 219# Apply moves (see above) to an array of packets that correspond to a request. 220# The use of this function is explained in the build_request_from_packets 221# function. 222sub apply_moves($$) { 223 my ($r_packet, $r_move) = @_; 224 my $current_packet = shift @$r_packet; 225 my $current_move = shift @$r_move; 226 my $in_packet_cursor = 0; 227 my @result = (); 228 while (defined $current_packet) { 229 if (!defined $current_move) { 230 push @result, $current_packet; 231 $current_packet = shift @$r_packet; 232 $in_packet_cursor = 0; 233 } elsif (defined $current_move->{d}) { 234 # Remove stuff from packet 235 if ($current_move->{d} > length($current_packet) - $in_packet_cursor) { 236 # Eat up what is left of packet. 237 $current_move->{d} -= length($current_packet) - $in_packet_cursor; 238 if ($in_packet_cursor > 0) { 239 # Something in packet from previous iteration. 240 push @result, $current_packet; 241 } 242 $current_packet = shift @$r_packet; 243 $in_packet_cursor = 0; 244 } else { 245 # Remove from current point in current packet 246 substr($current_packet, $in_packet_cursor, $current_move->{d}) = ''; 247 $current_move = shift @$r_move; 248 } 249 } else { 250 # Substitute stuff 251 if ($current_move->{s_s} > length($current_packet) - $in_packet_cursor) { 252 # {s_s=>3, s_v=>GET} on ['GE', 'T /foo'] 253 $current_move->{s_s} -= length($current_packet) - $in_packet_cursor; 254 substr($current_packet, $in_packet_cursor) = substr($current_move->{s_v}, 0, length($current_packet) - $in_packet_cursor); 255 push @result, $current_packet; 256 $current_move->{s_v} = substr($current_move->{s_v}, length($current_packet) - $in_packet_cursor); 257 $current_packet = shift @$r_packet; 258 $in_packet_cursor = 0; 259 } else { 260 substr($current_packet, $in_packet_cursor, $current_move->{s_s}) = $current_move->{s_v}; 261 $in_packet_cursor += length($current_move->{s_v}); 262 $current_move = shift @$r_move; 263 } 264 } 265 } 266 return \@result; 267} 268# Given a request as an array of packets, will parse it, append the appropriate 269# headers and return another array of packets. 270# The function implemented here can be high-level summarized as: 271# 1 - Concatenate all packets to obtain a string representation of request. 272# 2 - Parse the string representation 273# 3 - Get the "moves" from the parsing 274# 4 - Apply the "moves" to the packets. 275sub build_request_from_packets($$$$$) { 276 my ( $name, $more_headers, $is_chunked, $conn_header, $request_packets ) = @_; 277 # Concatenate packets as a string 278 my $parsable_request = ''; 279 my @packet_length; 280 for my $one_packet (@$request_packets) { 281 $parsable_request .= $one_packet; 282 push @packet_length, length($one_packet); 283 } 284 # Parse the string representation. 285 my $parsed_req = parse_request( $name, \$parsable_request ); 286 287 # Append headers 288 my $len_header = ''; 289 if ( !$is_chunked 290 && defined $parsed_req->{content} 291 && $parsed_req->{content} ne '' 292 && $more_headers !~ /\bContent-Length:/ ) 293 { 294 $parsed_req->{content} =~ s/^\s+|\s+$//gs; 295 296 $len_header .= 297 "Content-Length: " . length( $parsed_req->{content} ) . "\r\n"; 298 } 299 $parsed_req->{method} .= ' '; 300 $parsed_req->{url} .= ' '; 301 $parsed_req->{http_ver} .= "\r\n"; 302 $parsed_req->{headers} = "Host: localhost\r\nConnection: $conn_header\r\n$more_headers$len_header\r\n"; 303 304 # Get the moves from parsing 305 my @elements_moves = get_moves($parsed_req); 306 # Apply them to the packets. 307 return apply_moves($request_packets, \@elements_moves); 308} 309 310# Returns an array of array of hashes from the block. Each element of 311# the first-level array is a request. 312# Each request is an array of the "packets" to be sent. Each packet is a 313# string to send, with an (optionnal) delay before sending it. 314# This function parses (and therefore defines the syntax) of "request*" 315# sections. See documentation for supported syntax. 316sub get_req_from_block ($) { 317 my ($block) = @_; 318 my $name = $block->name; 319 320 my @req_list = (); 321 322 if ( defined $block->raw_request ) { 323 324 # Should be deprecated. 325 if ( ref $block->raw_request && ref $block->raw_request eq 'ARRAY' ) { 326 327 # User already provided an array. So, he/she specified where the 328 # data should be split. This allows for backward compatibility but 329 # should use request with arrays as it provides the same functionnality. 330 my @rr_list = (); 331 for my $elt ( @{ $block->raw_request } ) { 332 push @rr_list, {value => $elt}; 333 } 334 push @req_list, \@rr_list; 335 } 336 else { 337 push @req_list, [{value => $block->raw_request}]; 338 } 339 } 340 else { 341 my $request; 342 if ( defined $block->request_eval ) { 343 344 diag "$name - request_eval DEPRECATED. Use request eval instead."; 345 $request = eval $block->request_eval; 346 if ($@) { 347 warn $@; 348 } 349 } 350 else { 351 $request = $block->request; 352 } 353 354 my $is_chunked = 0; 355 my $more_headers = ''; 356 if ( $block->more_headers ) { 357 my @headers = split /\n+/, $block->more_headers; 358 for my $header (@headers) { 359 next if $header =~ /^\s*\#/; 360 my ( $key, $val ) = split /:\s*/, $header, 2; 361 if ( lc($key) eq 'transfer-encoding' and $val eq 'chunked' ) { 362 $is_chunked = 1; 363 } 364 365 #warn "[$key, $val]\n"; 366 $more_headers .= "$key: $val\r\n"; 367 } 368 } 369 370 if ( $block->pipelined_requests ) { 371 my $reqs = $block->pipelined_requests; 372 if ( !ref $reqs || ref $reqs ne 'ARRAY' ) { 373 Test::More::BAIL_OUT( 374 "$name - invalid entries in --- pipelined_requests"); 375 } 376 my $i = 0; 377 my $prq = ""; 378 for my $request (@$reqs) { 379 my $conn_type; 380 if ( $i++ == @$reqs - 1 ) { 381 $conn_type = 'close'; 382 } 383 else { 384 $conn_type = 'keep-alive'; 385 } 386 my $r_br = build_request_from_packets($name, $more_headers, 387 $is_chunked, $conn_type, 388 [$request] ); 389 $prq .= $$r_br[0]; 390 } 391 push @req_list, [{value =>$prq}]; 392 } 393 else { 394 # request section. 395 if (!ref $request) { 396 # One request and it is a good old string. 397 my $r_br = build_request_from_packets($name, $more_headers, 398 $is_chunked, 'Close', 399 [$request] ); 400 push @req_list, [{value => $$r_br[0]}]; 401 } elsif (ref $request eq 'ARRAY') { 402 # A bunch of requests... 403 for my $one_req (@$request) { 404 if (!ref $one_req) { 405 # This request is a good old string. 406 my $r_br = build_request_from_packets($name, $more_headers, 407 $is_chunked, 'Close', 408 [$one_req] ); 409 push @req_list, [{value => $$r_br[0]}]; 410 } elsif (ref $one_req eq 'ARRAY') { 411 # Request expressed as a serie of packets 412 my @packet_array = (); 413 for my $one_packet (@$one_req) { 414 if (!ref $one_packet) { 415 # Packet is a string. 416 push @packet_array, $one_packet; 417 } elsif (ref $one_packet eq 'HASH'){ 418 # Packet is a hash with a value... 419 push @packet_array, $one_packet->{value}; 420 } else { 421 Test::More::BAIL_OUT "$name - Invalid syntax. $one_packet should be a string or hash with value."; 422 } 423 } 424 my $transformed_packet_array = build_request_from_packets($name, $more_headers, 425 $is_chunked, 'Close', 426 \@packet_array); 427 my @transformed_req = (); 428 my $idx = 0; 429 for my $one_transformed_packet (@$transformed_packet_array) { 430 if (!ref $$one_req[$idx]) { 431 push @transformed_req, {value => $one_transformed_packet}; 432 } else { 433 # Is a HASH (checked above as $one_packet) 434 $$one_req[$idx]->{value} = $one_transformed_packet; 435 push @transformed_req, $$one_req[$idx]; 436 } 437 $idx++; 438 } 439 push @req_list, \@transformed_req; 440 } else { 441 Test::More::BAIL_OUT "$name - Invalid syntax. $one_req should be a string or an array of packets."; 442 } 443 } 444 } else { 445 Test::More::BAIL_OUT( 446 "$name - invalid ---request : MUST be string or array of requests"); 447 } 448 } 449 450 } 451 return \@req_list; 452} 453 454sub run_test_helper ($$) { 455 my ( $block, $dry_run ) = @_; 456 457 my $name = $block->name; 458 459 my $r_req_list = get_req_from_block($block); 460 461 if ( $#$r_req_list < 0 ) { 462 Test::More::BAIL_OUT("$name - request empty"); 463 } 464 465 #warn "request: $req\n"; 466 467 my $timeout = $block->timeout; 468 if ( !defined $timeout ) { 469 $timeout = $Timeout; 470 } 471 472 my $req_idx = 0; 473 for my $one_req (@$r_req_list) { 474 my $raw_resp; 475 476 if ($dry_run) { 477 $raw_resp = "200 OK HTTP/1.0\r\nContent-Length: 0\r\n\r\n"; 478 } 479 else { 480 $raw_resp = send_request( $one_req, $block->raw_request_middle_delay, 481 $timeout, $block->name ); 482 } 483 484 #warn "raw resonse: [$raw_resp]\n"; 485 486 my ($n, $need_array); 487 488 if ($block->pipelined_requests) { 489 $n = @{ $block->pipelined_requests }; 490 $need_array = 1; 491 492 } else { 493 $need_array = $#$r_req_list > 0; 494 } 495 496again: 497 #warn "!!! resp: [$raw_resp]"; 498 if (!defined $raw_resp) { 499 $raw_resp = ''; 500 } 501 502 my ( $res, $raw_headers, $left ); 503 504 if (!defined $block->ignore_response) { 505 ( $res, $raw_headers, $left ) = parse_response( $name, $raw_resp ); 506 } 507 508 if (!$n) { 509 if ($left) { 510 my $name = $block->name; 511 $left =~ s/([\0-\037\200-\377])/sprintf('\x{%02x}',ord $1)/eg; 512 warn "WARNING: $name - unexpected extra bytes after last chunk in ", 513 "response: \"$left\"\n"; 514 } 515 516 } else { 517 $raw_resp = $left; 518 $n--; 519 } 520 521 if (!defined $block->ignore_response) { 522 check_error_code($block, $res, $dry_run, $req_idx, $need_array); 523 check_raw_response_headers($block, $raw_headers, $dry_run, $req_idx, $need_array); 524 check_response_headers($block, $res, $raw_headers, $dry_run, $req_idx, $need_array); 525 check_response_body($block, $res, $dry_run, $req_idx, $need_array); 526 } 527 528 check_error_log($block, $res, $dry_run, $req_idx, $need_array); 529 530 $req_idx++; 531 532 if ($n) { 533 goto again; 534 } 535 } 536} 537 538# Helper function to retrieve a "check" (e.g. error_code) section. This also 539# checks that tests with arrays of requests are arrays themselves. 540sub get_indexed_value($$$$) { 541 my ($name, $value, $req_idx, $need_array) = @_; 542 if ($need_array) { 543 if (ref $value && ref $value eq 'ARRAY') { 544 return $$value[$req_idx]; 545 } else { 546 Test::More::BAIL_OUT("$name - You asked for many requests, the expected results should be arrays as well."); 547 } 548 } else { 549 # One element but still provided as an array. 550 if (ref $value && ref $value eq 'ARRAY') { 551 if ($req_idx != 0) { 552 Test::More::BAIL_OUT("$name - SHOULD NOT HAPPEN: idx != 0 and don't need array."); 553 } else { 554 return $$value[0]; 555 } 556 } else { 557 return $value; 558 } 559 } 560} 561sub check_error_code($$$$$) { 562 my ($block, $res, $dry_run, $req_idx, $need_array) = @_; 563 my $name = $block->name; 564 SKIP: { 565 skip "$name - tests skipped due to the lack of directive $dry_run", 1 if $dry_run; 566 if ( defined $block->error_code ) { 567 is( ($res && $res->code) || '', 568 get_indexed_value($name, $block->error_code, $req_idx, $need_array), 569 "$name - status code ok" ); 570 } else { 571 is( ($res && $res->code) || '', 200, "$name - status code ok" ); 572 } 573 } 574} 575sub check_raw_response_headers($$$$$) { 576 my ($block, $raw_headers, $dry_run, $req_idx, $need_array) = @_; 577 my $name = $block->name; 578 if ( defined $block->raw_response_headers_like ) { 579 SKIP: { 580 skip "$name - tests skipped due to the lack of directive $dry_run", 1 if $dry_run; 581 my $expected = get_indexed_value($name, 582 $block->raw_response_headers_like, 583 $req_idx, 584 $need_array); 585 like $raw_headers, qr/$expected/s, "$name - raw resp headers like"; 586 } 587 } 588} 589sub check_response_headers($$$$$) { 590 my ($block, $res, $raw_headers, $dry_run, $req_idx, $need_array) = @_; 591 my $name = $block->name; 592 if ( defined $block->response_headers ) { 593 my $headers = parse_headers( get_indexed_value($name, 594 $block->response_headers, 595 $req_idx, 596 $need_array)); 597 while ( my ( $key, $val ) = each %$headers ) { 598 if ( !defined $val ) { 599 600 #warn "HIT"; 601 SKIP: { 602 skip "$name - tests skipped due to the lack of directive $dry_run", 1 if $dry_run; 603 unlike $raw_headers, qr/^\s*\Q$key\E\s*:/ms, 604 "$name - header $key not present in the raw headers"; 605 } 606 next; 607 } 608 609 my $actual_val = $res ? $res->header($key) : undef; 610 if ( !defined $actual_val ) { 611 $actual_val = ''; 612 } 613 614 SKIP: { 615 skip "$name - tests skipped due to the lack of directive $dry_run", 1 if $dry_run; 616 is $actual_val, $val, "$name - header $key ok"; 617 } 618 } 619 } 620 elsif ( defined $block->response_headers_like ) { 621 my $headers = parse_headers( get_indexed_value($name, 622 $block->response_headers_like, 623 $req_idx, 624 $need_array) ); 625 while ( my ( $key, $val ) = each %$headers ) { 626 my $expected_val = $res->header($key); 627 if ( !defined $expected_val ) { 628 $expected_val = ''; 629 } 630 SKIP: { 631 skip "$name - tests skipped due to the lack of directive $dry_run", 1 if $dry_run; 632 like $expected_val, qr/^$val$/, "$name - header $key like ok"; 633 } 634 } 635 } 636} 637 638sub check_error_log ($$$$$) { 639 my ($block, $res, $dry_run, $req_idx, $need_array) = @_; 640 my $name = $block->name; 641 my $lines; 642 643 if (defined $block->error_log) { 644 my $pats = $block->error_log; 645 if (!ref $pats) { 646 chomp $pats; 647 my @lines = split /\n+/, $pats; 648 $pats = \@lines; 649 650 } else { 651 my @clone = @$pats; 652 $pats = \@clone; 653 } 654 655 $lines = error_log_data(); 656 for my $line (@$lines) { 657 for my $pat (@$pats) { 658 next if !defined $pat; 659 if (ref $pat && $line =~ /$pat/ || $line =~ /\Q$pat\E/) { 660 SKIP: { 661 skip "$name - tests skipped due to the lack of directive $dry_run", 1 if $dry_run; 662 pass("$name - pattern \"$pat\" matches a line in error.log"); 663 } 664 undef $pat; 665 } 666 } 667 } 668 669 for my $pat (@$pats) { 670 if (defined $pat) { 671 SKIP: { 672 skip "$name - tests skipped due to the lack of directive $dry_run", 1 if $dry_run; 673 fail("$name - pattern \"$pat\" matches a line in error.log"); 674 } 675 } 676 } 677 } 678 679 if (defined $block->no_error_log) { 680 #warn "HERE"; 681 my $pats = $block->no_error_log; 682 if (!ref $pats) { 683 chomp $pats; 684 $pats = [$pats]; 685 686 } else { 687 my @clone = @$pats; 688 $pats = \@clone; 689 } 690 691 $lines ||= error_log_data(); 692 for my $line (@$lines) { 693 for my $pat (@$pats) { 694 next if !defined $pat; 695 #warn "test $pat\n"; 696 if ((ref $pat && $line =~ /$pat/) || $line =~ /\Q$pat\E/) { 697 SKIP: { 698 skip "$name - tests skipped due to the lack of directive $dry_run", 1 if $dry_run; 699 my $ln = fmt_str($line); 700 my $p = fmt_str($pat); 701 fail("$name - pattern \"$p\" should not match any line in error.log but matches line \"$ln\""); 702 } 703 undef $pat; 704 } 705 } 706 } 707 708 for my $pat (@$pats) { 709 if (defined $pat) { 710 SKIP: { 711 skip "$name - tests skipped due to the lack of directive $dry_run", 1 if $dry_run; 712 my $p = fmt_str($pat); 713 pass("$name - pattern \"$p\" does not match a line in error.log"); 714 } 715 } 716 } 717 } 718 719} 720 721sub fmt_str ($) { 722 my $str = shift; 723 chomp $str; 724 $str =~ s/"/\\"/g; 725 $str =~ s/\r/\\r/g; 726 $str =~ s/\n/\\n/g; 727 $str; 728} 729 730sub check_response_body ($$$$$) { 731 my ($block, $res, $dry_run, $req_idx, $need_array) = @_; 732 my $name = $block->name; 733 if ( defined $block->response_body 734 || defined $block->response_body_eval ) 735 { 736 my $content = $res ? $res->content : undef; 737 if ( defined $content ) { 738 $content =~ s/^TE: deflate,gzip;q=0\.3\r\n//gms; 739 $content =~ s/^Connection: TE, close\r\n//gms; 740 } 741 742 my $expected; 743 if ( $block->response_body_eval ) { 744 diag "$name - response_body_eval is DEPRECATED. Use response_body eval instead."; 745 $expected = eval get_indexed_value($name, 746 $block->response_body_eval, 747 $req_idx, 748 $need_array); 749 if ($@) { 750 warn $@; 751 } 752 } 753 else { 754 $expected = get_indexed_value($name, 755 $block->response_body, 756 $req_idx, 757 $need_array); 758 } 759 760 if ( $block->charset ) { 761 Encode::from_to( $expected, 'UTF-8', $block->charset ); 762 } 763 764 unless (ref $expected) { 765 $expected =~ s/\$ServerPort\b/$ServerPort/g; 766 $expected =~ s/\$ServerPortForClient\b/$ServerPortForClient/g; 767 } 768 769 #warn show_all_chars($content); 770 771 #warn "no long string: $NoLongString"; 772 SKIP: { 773 skip "$name - tests skipped due to the lack of directive $dry_run", 1 if $dry_run; 774 if (ref $expected) { 775 like $content, $expected, "$name - response_body - like"; 776 777 } else { 778 if ($NoLongString) { 779 is( $content, $expected, 780 "$name - response_body - response is expected" ); 781 } 782 else { 783 is_string( $content, $expected, 784 "$name - response_body - response is expected" ); 785 } 786 } 787 } 788 789 } 790 elsif ( defined $block->response_body_like ) { 791 my $content = $res ? $res->content : undef; 792 if ( defined $content ) { 793 $content =~ s/^TE: deflate,gzip;q=0\.3\r\n//gms; 794 $content =~ s/^Connection: TE, close\r\n//gms; 795 } 796 my $expected_pat = get_indexed_value($name, 797 $block->response_body_like, 798 $req_idx, 799 $need_array); 800 $expected_pat =~ s/\$ServerPort\b/$ServerPort/g; 801 $expected_pat =~ s/\$ServerPortForClient\b/$ServerPortForClient/g; 802 my $summary = trim($content); 803 if (!defined $summary) { 804 $summary = ""; 805 } 806 807 SKIP: { 808 skip "$name - tests skipped due to the lack of directive $dry_run", 1 if $dry_run; 809 like( $content, qr/$expected_pat/s, 810 "$name - response_body_like - response is expected ($summary)" 811 ); 812 } 813 } 814} 815 816sub parse_response($$) { 817 my ( $name, $raw_resp ) = @_; 818 819 my $left; 820 821 my $raw_headers = ''; 822 if ( $raw_resp =~ /(.*?\r\n)\r\n/s ) { 823 824 #warn "\$1: $1"; 825 $raw_headers = $1; 826 } 827 828 #warn "raw headers: $raw_headers\n"; 829 830 my $res = HTTP::Response->parse($raw_resp); 831 my $enc = $res->header('Transfer-Encoding'); 832 833 my $len = $res->header('Content-Length'); 834 835 if ( defined $enc && $enc eq 'chunked' ) { 836 837 #warn "Found chunked!"; 838 my $raw = $res->content; 839 if ( !defined $raw ) { 840 $raw = ''; 841 } 842 843 my $decoded = ''; 844 while (1) { 845 if ( $raw =~ /\G 0 [\ \t]* \r\n \r\n /gcsx ) { 846 if ( $raw =~ /\G (.+) /gcsx ) { 847 $left = $1; 848 } 849 850 last; 851 } 852 if ( $raw =~ m{ \G [\ \t]* ( [A-Fa-f0-9]+ ) [\ \t]* \r\n }gcsx ) { 853 my $rest = hex($1); 854 855 #warn "chunk size: $rest\n"; 856 my $bit_sz = 32765; 857 while ( $rest > 0 ) { 858 my $bit = $rest < $bit_sz ? $rest : $bit_sz; 859 860 #warn "bit: $bit\n"; 861 if ( $raw =~ /\G(.{$bit})/gcs ) { 862 $decoded .= $1; 863 864 #warn "decoded: [$1]\n"; 865 } 866 else { 867 fail( 868"$name - invalid chunked data received (not enought octets for the data section)" 869 ); 870 return; 871 } 872 873 $rest -= $bit; 874 } 875 if ( $raw !~ /\G\r\n/gcs ) { 876 fail( 877 "$name - invalid chunked data received (expected CRLF)." 878 ); 879 return; 880 } 881 } 882 elsif ( $raw =~ /\G.+/gcs ) { 883 fail "$name - invalid chunked body received: $&"; 884 return; 885 } 886 else { 887 fail "$name - no last chunk found - $raw"; 888 return; 889 } 890 } 891 892 #warn "decoded: $decoded\n"; 893 $res->content($decoded); 894 895 } elsif (defined $len && $len ne '' && $len >= 0) { 896 my $raw = $res->content; 897 if (length $raw < $len) { 898 warn "WARNING: $name - response body truncated: ", 899 "$len expected, but got ", length $raw, "\n"; 900 901 } elsif (length $raw > $len) { 902 my $content = substr $raw, 0, $len; 903 $left = substr $raw, $len; 904 $res->content($content); 905 #warn "parsed body: [", $res->content, "]\n"; 906 } 907 } 908 909 return ( $res, $raw_headers, $left ); 910} 911 912sub send_request ($$$$@) { 913 my ( $req, $middle_delay, $timeout, $name, $tries ) = @_; 914 915 my $sock = IO::Socket::INET->new( 916 PeerAddr => $ServerAddr, 917 PeerPort => $ServerPortForClient, 918 Proto => 'tcp' 919 ); 920 921 if (! defined $sock) { 922 $tries ||= 0; 923 if ($tries < 10) { 924 warn "Can't connect to $ServerAddr:$ServerPortForClient: $!\n"; 925 sleep 1; 926 return send_request($req, $middle_delay, $timeout, $name, $tries + 1); 927 } else { 928 die "Can't connect to $ServerAddr:$ServerPortForClient: $!\n"; 929 } 930 } 931 932 my @req_bits = ref $req ? @$req : ($req); 933 934 my $flags = fcntl $sock, F_GETFL, 0 935 or die "Failed to get flags: $!\n"; 936 937 fcntl $sock, F_SETFL, $flags | O_NONBLOCK 938 or die "Failed to set flags: $!\n"; 939 940 my $ctx = { 941 resp => '', 942 write_offset => 0, 943 buf_size => 1024, 944 req_bits => \@req_bits, 945 write_buf => (shift @req_bits)->{"value"}, 946 middle_delay => $middle_delay, 947 sock => $sock, 948 name => $name, 949 }; 950 951 my $readable_hdls = IO::Select->new($sock); 952 my $writable_hdls = IO::Select->new($sock); 953 my $err_hdls = IO::Select->new($sock); 954 955 while (1) { 956 if ( $readable_hdls->count == 0 957 && $writable_hdls->count == 0 958 && $err_hdls->count == 0 ) 959 { 960 last; 961 } 962 963 my ( $new_readable, $new_writable, $new_err ) = 964 IO::Select->select( $readable_hdls, $writable_hdls, $err_hdls, 965 $timeout ); 966 967 if ( !defined $new_err 968 && !defined $new_readable 969 && !defined $new_writable ) 970 { 971 972 # timed out 973 timeout_event_handler($ctx); 974 last; 975 } 976 977 for my $hdl (@$new_err) { 978 next if !defined $hdl; 979 980 error_event_handler($ctx); 981 982 if ( $err_hdls->exists($hdl) ) { 983 $err_hdls->remove($hdl); 984 } 985 986 if ( $readable_hdls->exists($hdl) ) { 987 $readable_hdls->remove($hdl); 988 } 989 990 if ( $writable_hdls->exists($hdl) ) { 991 $writable_hdls->remove($hdl); 992 } 993 994 for my $h (@$readable_hdls) { 995 next if !defined $h; 996 if ( $h eq $hdl ) { 997 undef $h; 998 last; 999 } 1000 } 1001 1002 for my $h (@$writable_hdls) { 1003 next if !defined $h; 1004 if ( $h eq $hdl ) { 1005 undef $h; 1006 last; 1007 } 1008 } 1009 1010 close $hdl; 1011 } 1012 1013 for my $hdl (@$new_readable) { 1014 next if !defined $hdl; 1015 1016 my $res = read_event_handler($ctx); 1017 if ( !$res ) { 1018 1019 # error occured 1020 if ( $err_hdls->exists($hdl) ) { 1021 $err_hdls->remove($hdl); 1022 } 1023 1024 if ( $readable_hdls->exists($hdl) ) { 1025 $readable_hdls->remove($hdl); 1026 } 1027 1028 if ( $writable_hdls->exists($hdl) ) { 1029 $writable_hdls->remove($hdl); 1030 } 1031 1032 for my $h (@$writable_hdls) { 1033 next if !defined $h; 1034 if ( $h eq $hdl ) { 1035 undef $h; 1036 last; 1037 } 1038 } 1039 1040 close $hdl; 1041 } 1042 } 1043 1044 for my $hdl (@$new_writable) { 1045 next if !defined $hdl; 1046 1047 my $res = write_event_handler($ctx); 1048 if ( !$res ) { 1049 1050 # error occured 1051 if ( $err_hdls->exists($hdl) ) { 1052 $err_hdls->remove($hdl); 1053 } 1054 1055 if ( $readable_hdls->exists($hdl) ) { 1056 $readable_hdls->remove($hdl); 1057 } 1058 1059 if ( $writable_hdls->exists($hdl) ) { 1060 $writable_hdls->remove($hdl); 1061 } 1062 1063 close $hdl; 1064 1065 } elsif ( $res == 2 ) { 1066 if ( $writable_hdls->exists($hdl) ) { 1067 $writable_hdls->remove($hdl); 1068 } 1069 } 1070 } 1071 } 1072 1073 return $ctx->{resp}; 1074} 1075 1076sub timeout_event_handler ($) { 1077 my $ctx = shift; 1078 warn "ERROR: socket client: timed out - $ctx->{name}\n"; 1079} 1080 1081sub error_event_handler ($) { 1082 warn "exception occurs on the socket: $!\n"; 1083} 1084 1085sub write_event_handler ($) { 1086 my ($ctx) = @_; 1087 1088 while (1) { 1089 return undef if !defined $ctx->{write_buf}; 1090 1091 my $rest = length( $ctx->{write_buf} ) - $ctx->{write_offset}; 1092 1093 #warn "offset: $write_offset, rest: $rest, length ", length($write_buf), "\n"; 1094 #die; 1095 1096 if ( $rest > 0 ) { 1097 my $bytes; 1098 eval { 1099 $bytes = syswrite( 1100 $ctx->{sock}, $ctx->{write_buf}, 1101 $rest, $ctx->{write_offset} 1102 ); 1103 }; 1104 1105 if ($@) { 1106 my $errmsg = "write failed: $@"; 1107 warn "$errmsg\n"; 1108 $ctx->{resp} = $errmsg; 1109 return undef; 1110 } 1111 1112 if ( !defined $bytes ) { 1113 if ( $! == EAGAIN ) { 1114 1115 #warn "write again..."; 1116 #sleep 0.002; 1117 return 1; 1118 } 1119 my $errmsg = "write failed: $!"; 1120 warn "$errmsg\n"; 1121 if ( !$ctx->{resp} ) { 1122 $ctx->{resp} = "$errmsg"; 1123 } 1124 return undef; 1125 } 1126 1127 #warn "wrote $bytes bytes.\n"; 1128 $ctx->{write_offset} += $bytes; 1129 } 1130 else { 1131 my $next_send = shift @{ $ctx->{req_bits} } or return 2; 1132 $ctx->{write_buf} = $next_send->{'value'}; 1133 $ctx->{write_offset} = 0; 1134 my $wait_time; 1135 if (!defined $next_send->{'delay_before'}) { 1136 if (defined $ctx->{middle_delay}) { 1137 $wait_time = $ctx->{middle_delay}; 1138 } 1139 } else { 1140 $wait_time = $next_send->{'delay_before'}; 1141 } 1142 if ($wait_time) { 1143 #warn "sleeping.."; 1144 sleep $wait_time; 1145 } 1146 } 1147 } 1148 1149 # impossible to reach here... 1150 return undef; 1151} 1152 1153sub read_event_handler ($) { 1154 my ($ctx) = @_; 1155 while (1) { 1156 my $read_buf; 1157 my $bytes = sysread( $ctx->{sock}, $read_buf, $ctx->{buf_size} ); 1158 1159 if ( !defined $bytes ) { 1160 if ( $! == EAGAIN ) { 1161 1162 #warn "read again..."; 1163 #sleep 0.002; 1164 return 1; 1165 } 1166 $ctx->{resp} = "500 read failed: $!"; 1167 return undef; 1168 } 1169 1170 if ( $bytes == 0 ) { 1171 return undef; # connection closed 1172 } 1173 1174 $ctx->{resp} .= $read_buf; 1175 1176 #warn "read $bytes ($read_buf) bytes.\n"; 1177 } 1178 1179 # impossible to reach here... 1180 return undef; 1181} 1182 11831; 1184__END__ 1185 1186=encoding utf-8 1187 1188=head1 NAME 1189 1190Test::Nginx::Socket - Socket-backed test scaffold for the Nginx C modules 1191 1192=head1 SYNOPSIS 1193 1194 use Test::Nginx::Socket; 1195 1196 plan tests => $Test::Nginx::Socket::RepeatEach * 2 * blocks(); 1197 1198 run_tests(); 1199 1200 __DATA__ 1201 1202 === TEST 1: sanity 1203 --- config 1204 location /echo { 1205 echo_before_body hello; 1206 echo world; 1207 } 1208 --- request 1209 GET /echo 1210 --- response_body 1211 hello 1212 world 1213 --- error_code: 200 1214 1215 1216 === TEST 2: set Server 1217 --- config 1218 location /foo { 1219 echo hi; 1220 more_set_headers 'Server: Foo'; 1221 } 1222 --- request 1223 GET /foo 1224 --- response_headers 1225 Server: Foo 1226 --- response_body 1227 hi 1228 1229 1230 === TEST 3: clear Server 1231 --- config 1232 location /foo { 1233 echo hi; 1234 more_clear_headers 'Server: '; 1235 } 1236 --- request 1237 GET /foo 1238 --- response_headers_like 1239 Server: nginx.* 1240 --- response_body 1241 hi 1242 1243 1244 === TEST 3: chunk size too small 1245 --- config 1246 chunkin on; 1247 location /main { 1248 echo_request_body; 1249 } 1250 --- more_headers 1251 Transfer-Encoding: chunked 1252 --- request eval 1253 "POST /main 1254 4\r 1255 hello\r 1256 0\r 1257 \r 1258 " 1259 --- error_code: 400 1260 --- response_body_like: 400 Bad Request 1261 1262=head1 DESCRIPTION 1263 1264This module provides a test scaffold based on non-blocking L<IO::Socket> for automated testing in Nginx C module development. 1265 1266This class inherits from L<Test::Base>, thus bringing all its 1267declarative power to the Nginx C module testing practices. 1268 1269You need to terminate or kill any Nginx processes before running the test suite if you have changed the Nginx server binary. Normally it's as simple as 1270 1271 killall nginx 1272 PATH=/path/to/your/nginx-with-memc-module:$PATH prove -r t 1273 1274This module will create a temporary server root under t/servroot/ of the current working directory and starts and uses the nginx executable in the PATH environment. 1275 1276You will often want to look into F<t/servroot/logs/error.log> 1277when things go wrong ;) 1278 1279=head1 Sections supported 1280 1281The following sections are supported: 1282 1283=head2 config 1284 1285Content of this section will be included in the "server" part of the generated 1286config file. This is the place where you want to put the "location" directive 1287enabling the module you want to test. Example: 1288 1289 location /echo { 1290 echo_before_body hello; 1291 echo world; 1292 } 1293 1294Sometimes you simply don't want to bother copying ten times the same 1295configuration for the ten tests you want to run against your module. One way 1296to do this is to write a config section only for the first test in your C<.t> 1297file. All subsequent tests will re-use the same config. Please note that this 1298depends on the order of test, so you should run C<prove> with variable 1299C<TEST_NGINX_NO_SHUFFLE=1> (see below for more on this variable). 1300 1301Please note that config section goes through environment variable expansion 1302provided the variables to expand start with TEST_NGINX. 1303So, the following is a perfectly legal (provided C<TEST_NGINX_HTML_DIR> is 1304set correctly): 1305 1306 location /main { 1307 echo_subrequest POST /sub -f $TEST_NGINX_HTML_DIR/blah.txt; 1308 } 1309 1310=head2 http_config 1311 1312Content of this section will be included in the "http" part of the generated 1313config file. This is the place where you want to put the "upstream" directive 1314you might want to test. Example: 1315 1316 upstream database { 1317 postgres_server 127.0.0.1:$TEST_NGINX_POSTGRESQL_PORT 1318 dbname=ngx_test user=ngx_test 1319 password=wrong_pass; 1320 } 1321 1322As you guessed from the example above, this section goes through environment 1323variable expansion (variables have to start with TEST_NGINX). 1324 1325=head2 main_config 1326 1327Content of this section will be included in the "main" part of the generated 1328config file. This is very rarely used, except if you are testing nginx core 1329itself. 1330 1331This section goes through environment 1332variable expansion (variables have to start with TEST_NGINX). 1333 1334=head2 request 1335 1336This is probably the most important section. It defines the request(s) you 1337are going to send to the nginx server. It offers a pretty powerful grammar 1338which we are going to walk through one example at a time. 1339 1340In its most basic form, this section looks like that: 1341 1342 --- request 1343 GET 1344 1345This will just do a GET request on the root (i.e. /) of the server using 1346HTTP/1.1. 1347 1348Of course, you might want to test something else than the root of your 1349web server and even use a different version of HTTP. This is possible: 1350 1351 --- request 1352 GET /foo HTTP/1.0 1353 1354Please note that specifying HTTP/1.0 will not prevent Test::Nginx from 1355sending the C<Host> header. Actually Test::Nginx always sends 2 headers: 1356C<Host> (with value localhost) and C<Connection> (with value Close for 1357simple requests and keep-alive for all but the last pipelined_requests). 1358 1359You can also add a content to your request: 1360 1361 --- request 1362 POST /foo 1363 Hello world 1364 1365Test::Nginx will automatically calculate the content length and add the 1366corresponding header for you. 1367 1368This being said, as soon as you want to POST real data, you will be interested 1369in using the more_headers section and using the power of Test::Base filters 1370to urlencode the content you are sending. Which gives us a 1371slightly more realistic example: 1372 1373 --- more_headers 1374 Content-type: application/x-www-form-urlencoded 1375 --- request eval 1376 use URI::Escape; 1377 "POST /rrd/foo 1378 value=".uri_escape("N:12345") 1379 1380Sometimes a test is more than one request. Typically you want to POST some 1381data and make sure the data has been taken into account with a GET. You can 1382do it using arrays: 1383 1384 --- request eval 1385 ["POST /users 1386 name=foo", "GET /users/foo"] 1387 1388This way, REST-like interfaces are pretty easy to test. 1389 1390When you develop nifty nginx modules you will eventually want to test things 1391with buffers and "weird" network conditions. This is where you split 1392your request into network packets: 1393 1394 --- request eval 1395 [["POST /users\nna", "me=foo"]] 1396 1397Here, Test::Nginx will first send the request line, the headers it 1398automatically added for you and the first two letters of the body ("na" in 1399our example) in ONE network packet. Then, it will send the next packet (here 1400it's "me=foo"). When we talk about packets here, this is nto exactly correct 1401as there is no way to guarantee the behavior of the TCP/IP stack. What 1402Test::Nginx can guarantee is that this will result in two calls to 1403C<syswrite>. 1404 1405A good way to make I<almost> sure the two calls result in two packets is to 1406introduce a delay (let's say 2 seconds)before sending the second packet: 1407 1408 --- request eval 1409 [["POST /users\nna", {value => "me=foo", delay_before => 2}]] 1410 1411Of course, everything can be combined till your brain starts boiling ;) : 1412 1413 --- request eval 1414 use URI::Escape; 1415 my $val="value=".uri_escape("N:12346"); 1416 [["POST /rrd/foo 1417 ".substr($val, 0, 6), 1418 {value => substr($val, 6, 5), delay_before=>5}, 1419 substr($val, 11)], "GET /rrd/foo"] 1420 1421=head2 request_eval 1422 1423Use of this section is deprecated and tests using it should replace it with 1424a C<request> section with an C<eval> filter. More explicitly: 1425 1426 --- request_eval 1427 "POST /echo_body 1428 hello\x00\x01\x02 1429 world\x03\x04\xff" 1430 1431should be replaced by: 1432 1433 --- request eval 1434 "POST /echo_body 1435 hello\x00\x01\x02 1436 world\x03\x04\xff" 1437 1438=head2 pipelined_requests 1439 1440Specify pipelined requests that use a single keep-alive connection to the server. 1441 1442Here is an example from ngx_lua's test suite: 1443 1444 === TEST 7: discard body 1445 --- config 1446 location = /foo { 1447 content_by_lua ' 1448 ngx.req.discard_body() 1449 ngx.say("body: ", ngx.var.request_body) 1450 '; 1451 } 1452 location = /bar { 1453 content_by_lua ' 1454 ngx.req.read_body() 1455 ngx.say("body: ", ngx.var.request_body) 1456 '; 1457 } 1458 --- pipelined_requests eval 1459 ["POST /foo 1460 hello, world", 1461 "POST /bar 1462 hiya, world"] 1463 --- response_body eval 1464 ["body: nil\n", 1465 "body: hiya, world\n"] 1466 1467=head2 more_headers 1468 1469Adds the content of this section as headers to the request being sent. Example: 1470 1471 --- more_headers 1472 X-Foo: blah 1473 1474This will add C<X-Foo: blah> to the request (on top of the automatically 1475generated headers like C<Host>, C<Connection> and potentially 1476C<Content-Length>). 1477 1478=head2 response_body 1479 1480The expected value for the body of the submitted request. 1481 1482 --- response_body 1483 hello 1484 1485If the test is made of multiple requests, then the response_body B<MUST> 1486be an array and each request B<MUST> return the corresponding expected 1487body: 1488 1489 --- request eval 1490 ["GET /hello", "GET /world"] 1491 --- response_body eval 1492 ["hello", "world"] 1493 1494=head2 response_body_eval 1495 1496Use of this section is deprecated and tests using it should replace it 1497with a C<request> section with an C<eval> filter. Therefore: 1498 1499 --- response_body_eval 1500 "hello\x00\x01\x02 1501 world\x03\x04\xff" 1502 1503should be replaced by: 1504 1505 --- response_body eval 1506 "hello\x00\x01\x02 1507 world\x03\x04\xff" 1508 1509=head2 response_body_like 1510 1511The body returned by the request MUST match the pattern provided by this 1512section. Example: 1513 1514 --- response_body_like 1515 ^elapsed 0\.00[0-5] sec\.$ 1516 1517If the test is made of multiple requests, then response_body_like B<MUST> 1518be an array and each request B<MUST> match the corresponding pattern. 1519 1520=head2 response_headers 1521 1522The headers specified in this section are in the response sent by nginx. 1523 1524 --- response_headers 1525 Content-Type: application/x-resty-dbd-stream 1526 1527Of course, you can specify many headers in this section: 1528 1529 --- response_headers 1530 X-Resty-DBD-Module: 1531 Content-Type: application/x-resty-dbd-stream 1532 1533The test will be successful only if all headers are found in the response with 1534the appropriate values. 1535 1536If the test is made of multiple requests, then response_headers B<MUST> 1537be an array and each element of the array is checked against the 1538response to the corresponding request. 1539 1540=head2 response_headers_like 1541 1542The value of the headers returned by nginx match the patterns. 1543 1544 --- response_headers_like 1545 X-Resty-DBD-Module: ngx_drizzle \d+\.\d+\.\d+ 1546 Content-Type: application/x-resty-dbd-stream 1547 1548This will check that the response's C<Content-Type> is 1549application/x-resty-dbd-stream and that the C<X-Resty-DBD-Module> matches 1550C<ngx_drizzle \d+\.\d+\.\d+>. 1551 1552The test will be successful only if all headers are found in the response and 1553if the values match the patterns. 1554 1555If the test is made of multiple requests, then response_headers_like B<MUST> 1556be an array and each element of the array is checked against the 1557response to the corresponding request. 1558 1559=head2 raw_response_headers_like 1560 1561Checks the headers part of the response against this pattern. This is 1562particularly useful when you want to write tests of redirect functions 1563that are not bound to the value of the port your nginx server (under 1564test) is listening to: 1565 1566 --- raw_response_headers_like: Location: http://localhost(?::\d+)?/foo\r\n 1567 1568As usual, if the test is made of multiple requests, then 1569raw_response_headers_like B<MUST> be an array. 1570 1571=head2 error_code 1572 1573The expected value of the HTTP response code. If not set, this is assumed 1574to be 200. But you can expect other things such as a redirect: 1575 1576 --- error_code: 302 1577 1578If the test is made of multiple requests, then 1579error_code B<MUST> be an array with the expected value for the response status 1580of each request in the test. 1581 1582=head2 error_log 1583 1584Checks if the pattern or multiple patterns all appear in lines of the F<error.log> file. 1585 1586For example, 1587 1588 === TEST 1: matched with j 1589 --- config 1590 location /re { 1591 content_by_lua ' 1592 m = ngx.re.match("hello, 1234", "([0-9]+)", "j") 1593 if m then 1594 ngx.say(m[0]) 1595 else 1596 ngx.say("not matched!") 1597 end 1598 '; 1599 } 1600 --- request 1601 GET /re 1602 --- response_body 1603 1234 1604 --- error_log: pcre JIT compiling result: 1 1605 1606Then the substring "pcre JIT compiling result: 1" must appear literally in a line of F<error.log>. 1607 1608Multiple patterns are also supported, for example: 1609 1610 --- error_log eval 1611 ["abc", qr/blah/] 1612 1613then the substring "abc" must appear literally in a line of F<error.log>, and the regex C<qr/blah> 1614must also match a line in F<error.log>. 1615 1616=head2 no_error_log 1617 1618Very much like the C<--- error_log> section, but does the opposite test, i.e., 1619pass only when the specified patterns of lines do not appear in the F<error.log> file at all. 1620 1621Here is an example: 1622 1623 --- no_error_log 1624 [error] 1625 1626This test will fail when any of the line in the F<error.log> file contains the string C<"[error]">. 1627 1628Just like the C<--- error_log> section, one can also specify multiple patterns: 1629 1630 --- no_error_log eval 1631 ["abc", qr/blah/] 1632 1633Then if any line in F<error.log> contains the string C<"abc"> or match the Perl regex C<qr/blah/>, then the test will fail. 1634 1635=head2 raw_request 1636 1637The exact request to send to nginx. This is useful when you want to test 1638soem behaviors that are not available with "request" such as an erroneous 1639C<Content-Length> header or splitting packets right in the middle of headers: 1640 1641 --- raw_request eval 1642 ["POST /rrd/taratata HTTP/1.1\r 1643 Host: localhost\r 1644 Connection: Close\r 1645 Content-Type: application/", 1646 "x-www-form-urlencoded\r 1647 Content-Length:15\r\n\r\nvalue=N%3A12345"] 1648 1649This can also be useful to tests "invalid" request lines: 1650 1651 --- raw_request 1652 GET /foo HTTP/2.0 THE_FUTURE_IS_NOW 1653 1654=head2 ignore_response 1655 1656Do not attempt to parse the response or run the response related subtests. 1657 1658=head2 user_files 1659 1660With this section you can create a file that will be copied in the 1661html directory of the nginx server under test. For example: 1662 1663 --- user_files 1664 >>> blah.txt 1665 Hello, world 1666 1667will create a file named C<blah.txt> in the html directory of the nginx 1668server tested. The file will contain the text "Hello, world". 1669 1670=head2 skip_nginx 1671 1672=head2 skip_nginx2 1673 1674Both string scalar and string arrays are supported as values. 1675 1676=head2 raw_request_middle_delay 1677 1678Delay in sec between sending successive packets in the "raw_request" array 1679value. Also used when a request is split in packets. 1680 1681=head1 Environment variables 1682 1683All environment variables starting with C<TEST_NGINX_> are expanded in the 1684sections used to build the configuration of the server that tests automatically 1685starts. The following environment variables are supported by this module: 1686 1687=head2 TEST_NGINX_VERBOSE 1688 1689Controls whether to output verbose debugging messages in Test::Nginx. Default to empty. 1690 1691=head2 TEST_NGINX_USE_HUP 1692 1693When set to 1, Test::Nginx will try to send HUP signal to the 1694nginx master process to reload the config file between 1695successive C<repeast_each> tests. When this envirnoment is set 1696to 1, it will also enfornce the "master_process on" config line 1697in the F<nginx.conf> file, 1698because Nginx is buggy in processing HUP signal when the master process is off. 1699 1700=head2 TEST_NGINX_POSTPONE_OUTPUT 1701 1702Defaults to empty. This environment takes positive integer numbers as its value and it will cause the auto-generated nginx.conf file to have a "postpone_output" setting in the http {} block. 1703 1704For example, setting TEST_NGINX_POSTPONE_OUTPUT to 1 will have the following line in nginx.conf's http {} block: 1705 1706 postpone_output 1; 1707 1708and it will effectively disable the write buffering in nginx's ngx_http_write_module. 1709 1710=head2 TEST_NGINX_NO_NGINX_MANAGER 1711 1712Defaults to 0. If set to 1, Test::Nginx module will not manage 1713(configure/start/stop) the C<nginx> process. Can be useful to run tests 1714against an already configured (and running) nginx server. 1715 1716=head2 TEST_NGINX_NO_SHUFFLE 1717 1718Dafaults to 0. If set to 1, will make sure the tests are run in the order 1719they appear in the test file (and not in random order). 1720 1721=head2 TEST_NGINX_USE_VALGRIND 1722 1723If set, Test::Nginx will start nginx with valgrind with the the value of this environment as the options. 1724 1725Nginx is actually started with 1726C<valgrind -q $TEST_NGINX_USE_VALGRIND --gen-suppressions=all --suppressions=valgrind.suppress>, 1727the suppressions option being used only if there is actually 1728a valgrind.suppress file. 1729 1730If this environment is set to the number C<1> or any other 1731non-zero numbers, then it is equivalent to taking the value 1732C<--tool=memcheck --leak-check=full>. 1733 1734=head2 TEST_NGINX_BINARY 1735 1736The command to start nginx. Defaults to C<nginx>. Can be used as an alternative 1737to setting C<PATH> to run a specific nginx instance. 1738 1739=head2 TEST_NGINX_LOG_LEVEL 1740 1741Value of the last argument of the C<error_log> configuration directive. 1742Defaults to C<debug>. 1743 1744=head2 TEST_NGINX_MASTER_PROCESS 1745 1746Value of the C<master_process> configuration directive. Defaults to C<off>. 1747 1748=head2 TEST_NGINX_SERVER_PORT 1749 1750Value of the port the server started by Test::Nginx will listen to. If not 1751set, C<TEST_NGINX_PORT> is used. If C<TEST_NGINX_PORT> is not set, 1752then C<1984> is used. See below for typical use. 1753 1754=head2 TEST_NGINX_CLIENT_PORT 1755 1756Value of the port Test::Nginx will diirect requests to. If not 1757set, C<TEST_NGINX_PORT> is used. If C<TEST_NGINX_PORT> is not set, 1758then C<1984> is used. A typical use of this feature is to test extreme 1759network conditions by adding a "proxy" between Test::Nginx and nginx 1760itself. This is described in the C<etcproxy integration> section of this 1761module README. 1762 1763=head2 TEST_NGINX_PORT 1764 1765A shortcut for setting both C<TEST_NGINX_CLIENT_PORT> and 1766C<TEST_NGINX_SERVER_PORT>. 1767 1768=head2 TEST_NGINX_SLEEP 1769 1770How much time (in seconds) should Test::Nginx sleep between two calls to C<syswrite> when 1771sending request data. Defaults to 0. 1772 1773=head2 TEST_NGINX_FORCE_RESTART_ON_TEST 1774 1775Defaults to 1. If set to 0, Test::Nginx will not restart the nginx 1776server when the config does not change between two tests. 1777 1778=head2 TEST_NGINX_SERVROOT 1779 1780The root of the nginx "hierarchy" (where you find the conf, *_tmp and logs 1781directories). This value will be used with the C<-p> option of C<nginx>. 1782Defaults to appending C<t/servroot> to the current directory. 1783 1784=head2 TEST_NGINX_IGNORE_MISSING_DIRECTIVES 1785 1786If set to 1 will SKIP all tests which C<config> sections resulted in a 1787C<unknown directive> when trying to start C<nginx>. Useful when you want to 1788run tests on a build of nginx that does not include all modules it should. 1789By default, these tests will FAIL. 1790 1791=head2 TEST_NGINX_EVENT_TYPE 1792 1793This environment can be used to specify a event API type to be used by Nginx. Possible values are C<epoll>, C<kqueue>, C<select>, C<rtsig>, C<poll>, and others. 1794 1795For example, 1796 1797 $ TEST_NGINX_EVENT_TYPE=select prove -r t 1798 1799=head2 TEST_NGINX_ERROR_LOG 1800 1801Error log files from all tests will be appended to the file specified with 1802this variable. There is no default value which disables the feature. This 1803is very useful when debugging. By default, each test triggers a start/stop 1804cycle for C<nginx>. All logs are removed before each restart, so you can 1805only see the logs for the last test run (which you usually do not control 1806except if you set C<TEST_NGINX_NO_SHUFFLE=1>). With this, you accumulate 1807all logs into a single file that is never cleaned up by Test::Nginx. 1808 1809=head1 Samples 1810 1811You'll find live samples in the following Nginx 3rd-party modules: 1812 1813=over 1814 1815=item ngx_echo 1816 1817L<http://github.com/agentzh/echo-nginx-module> 1818 1819=item ngx_chunkin 1820 1821L<http://wiki.nginx.org/NginxHttpChunkinModule> 1822 1823=item ngx_memc 1824 1825L<http://wiki.nginx.org/NginxHttpMemcModule> 1826 1827=item ngx_drizzle 1828 1829L<http://github.com/chaoslawful/drizzle-nginx-module> 1830 1831=item ngx_rds_json 1832 1833L<http://github.com/agentzh/rds-json-nginx-module> 1834 1835=item ngx_xss 1836 1837L<http://github.com/agentzh/xss-nginx-module> 1838 1839=item ngx_srcache 1840 1841L<http://github.com/agentzh/srcache-nginx-module> 1842 1843=item ngx_lua 1844 1845L<http://github.com/chaoslawful/lua-nginx-module> 1846 1847=item ngx_set_misc 1848 1849L<http://github.com/agentzh/set-misc-nginx-module> 1850 1851=item ngx_array_var 1852 1853L<http://github.com/agentzh/array-var-nginx-module> 1854 1855=item ngx_form_input 1856 1857L<http://github.com/calio/form-input-nginx-module> 1858 1859=item ngx_iconv 1860 1861L<http://github.com/calio/iconv-nginx-module> 1862 1863=item ngx_set_cconv 1864 1865L<http://github.com/liseen/set-cconv-nginx-module> 1866 1867=item ngx_postgres 1868 1869L<http://github.com/FRiCKLE/ngx_postgres> 1870 1871=item ngx_coolkit 1872 1873L<http://github.com/FRiCKLE/ngx_coolkit> 1874 1875=back 1876 1877=head1 SOURCE REPOSITORY 1878 1879This module has a Git repository on Github, which has access for all. 1880 1881 http://github.com/agentzh/test-nginx 1882 1883If you want a commit bit, feel free to drop me a line. 1884 1885=head1 AUTHORS 1886 1887agentzh (章亦春) C<< <agentzh@gmail.com> >> 1888 1889Antoine BONAVITA C<< <antoine.bonavita@gmail.com> >> 1890 1891=head1 COPYRIGHT & LICENSE 1892 1893Copyright (c) 2009-2012, agentzh C<< <agentzh@gmail.com> >>. 1894 1895Copyright (c) 2011-2012, Antoine BONAVITA C<< <antoine.bonavita@gmail.com> >>. 1896 1897This module is licensed under the terms of the BSD license. 1898 1899Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1900 1901=over 1902 1903=item * 1904 1905Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 1906 1907=item * 1908 1909Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 1910 1911=item * 1912 1913Neither the name of the authors nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. 1914 1915=back 1916 1917THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 1918 1919=head1 SEE ALSO 1920 1921L<Test::Nginx::LWP>, L<Test::Base>. 1922 1923