The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.
#!/usr/bin/perl

# Copyright 2011 Alexandr Gomoliako

use strict;
use warnings;
no  warnings 'uninitialized';

use Data::Dumper;
use Test::More;
use Nginx::Test;
use IO::Socket;
use Socket;


my $nginx = find_nginx_perl;
my $dir   = "objs/t02";
mkdir "objs" unless -e "objs";

plan skip_all => "Can't find executable binary ($nginx) to test"
        if  !$nginx    ||  
            !-x $nginx    ;


# SSL support is required for this test

my %CONFARGS = get_nginx_conf_args_die $nginx;

plan skip_all => "$nginx built without SSL support" 
    unless  $CONFARGS{'--with-http_ssl_module'};


# making sure we can successfully 
# connect to remote hosts  

my $ip = inet_ntoa (inet_aton ("www.google.com"));

wait_for_peer "$ip:443", 1
    or  plan skip_all => "Cannot connect to $ip:443";


plan 'no_plan';


{
    my ($child, $peer) = fork_nginx_handler_die  $nginx, $dir, '',<<'    END';


        sub CRLF { "\x0d\x0a" }


        sub Nginx::reply_finalize {
            my $r   = shift;
            my $buf = shift || '';

            $r->header_out ('x-errno', int ( $! ));
            $r->header_out ('x-errstr', "$!");
            $r->header_out ('Content-Length', length ( $buf ));
            $r->send_http_header ('text/html; charset=UTF-8');

            $r->print ($buf)
                    unless  $r->header_only;

            $r->send_special (NGX_HTTP_LAST);
            $r->finalize_request (NGX_OK);
        }


        sub handler {
            my ($r) = @_;

            $r->main_count_inc;

            
            my ($ip, $port, $timeout) = split ':', $r->args, 3;


            ngx_connector $ip, $port, $timeout, sub {

                my $c = shift;

                $r->reply_finalize,
                  return NGX_CLOSE
                        if  $!;


                ngx_ssl_handshaker $c, $timeout, sub {

                    $r->reply_finalize,
                      return NGX_CLOSE
                            if  $!;

                    my $buf = "GET / HTTP/1.0"        . CRLF .
                              "Host: www.google.com"  . CRLF .
                                                        CRLF  ;

                    ngx_writer $c, $buf, $timeout, sub {

                        $r->reply_finalize,
                          return NGX_CLOSE
                                if  $!;

                        $buf = ''; # reusing this buffer for reading

                        return NGX_READ;
                    };


                    ngx_reader $c, $buf, 0, 0, $timeout, sub {
 
                        $r->reply_finalize,
                          return NGX_CLOSE
                                if  $! && $! != NGX_EOF;

                        $! = 0,
                          $r->reply_finalize ($buf),
                            return NGX_CLOSE
                                  if  $! == NGX_EOF;

                        return NGX_READ;
                    };


                    return NGX_WRITE;
                };

                return NGX_SSL_HANDSHAKE;
            };


            return NGX_DONE;
        }

    END


    wait_for_peer $peer, 5
        or diag "wair_for_peer \"$peer\" failed\n";


    for my $i (1 .. 2) {
        my ($body, $headers) = http_get $peer, "/?$ip:443:4", 6;

        ok $body =~ /Google/i, "google over SSL $i"
            or  diag ($body, Dumper ($headers), cat_nginx_logs $dir),
                  last;

        ok $body =~ />\s*$/is, "reponse ends with angle bracket $i"
            or  diag ($body, Dumper ($headers), cat_nginx_logs $dir),
                  last;

        is $headers->{'x-errno'}->[0], 0, "clean errno"
            or  diag ("errno = $headers->{'x-errno'}");
    }


    # catching timeout 
    
    my $port2 = get_unused_port;
    my $peer2 = "127.0.0.1:$port2";

    my $child2 = fork_child_die sub {
        my $sock = IO::Socket::INET->new('Listen'    => 5,
                                         'LocalAddr' => "127.0.0.1",
                                         'LocalPort' => $port2,
                                         'Proto'     => 'tcp');
        my $newsock = $sock->accept;
        sleep 10;
    };


    for my $i (1 .. 1) {
        my ($body, $headers) = http_get $peer, "/?$peer2:1", 2;

        ok defined $headers->{'x-errno'}->[0], "have errno"
            or  diag ($body, Dumper ($headers), cat_nginx_logs $dir),
                  last;

        isnt $headers->{'x-errno'}->[0], 0, "non-zero errno"
            or  diag ($body, Dumper ($headers), cat_nginx_logs $dir),
                  last;
    }

    undef $child2;
    undef $child;
}