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