use strict;
use warnings FATAL => 'all';

use Apache::Test;
use Apache::TestRequest;
use Socket;

# send
#       arg #1: url prefix
#       arg #2: Host header (none if undef)
# expected results:
#       arg #3:  response code
#       arg #4:  SERVER_NAME
#       arg #5:  SERVER_PORT (canonical port if 'REMOTE')
#    undef == don't care

my $url_suffix = 'modules/cgi/env.pl';

my @test_cases = (
    [ "/",                     "righthost"     => 200, 'righthost', 'REMOTE' ],
    [ "/",                     "righthost:123" => 200, 'righthost', '123'    ],
    [ "/",                     "Righthost"     => 200, 'righthost', 'REMOTE' ],
    [ "/",                     "Righthost:123" => 200, 'righthost', '123'    ],
    [ "/",                     "128.0.0.1"     => 200, '128.0.0.1', 'REMOTE' ],
    [ "/",                     "128.0.0.1:123" => 200, '128.0.0.1', '123'    ],
    [ "/",                     "[::1]"         => 200, '[::1]',     'REMOTE' ],
    [ "/",                     "[::1]:123"     => 200, '[::1]',     '123'    ],
    [ "/",                     "[a::1]"        => 200, '[a::1]',    'REMOTE' ],
    [ "/",                     "[a::1]:123"    => 200, '[a::1]',    '123'    ],
    [ "/",                     "[A::1]"        => 200, '[a::1]',    'REMOTE' ],
    [ "/",                     "[A::1]:123"    => 200, '[a::1]',    '123'    ],
    [ "http://righthost/",     undef           => 200, 'righthost', 'REMOTE' ],
    [ "http://righthost:123/", undef           => 200, 'righthost', '123'    ],
    [ "http://Righthost/",     undef           => 200, 'righthost', 'REMOTE' ],
    [ "http://Righthost:123/", undef           => 200, 'righthost', '123'    ],
    [ "http://128.0.0.1/",     undef           => 200, '128.0.0.1', 'REMOTE' ],
    [ "http://128.0.0.1:123/", undef           => 200, '128.0.0.1', '123'    ],
    [ "http://[::1]/",         undef           => 200, '[::1]',     'REMOTE' ],
    [ "http://[::1]:123/",     undef           => 200, '[::1]',     '123'    ],
    [ "http://righthost/",     "wronghost"     => 200, 'righthost', 'REMOTE' ],
    [ "http://righthost:123/", "wronghost:321" => 200, 'righthost', '123'    ],
    [ "http://Righthost/",     "wronghost"     => 200, 'righthost', 'REMOTE' ],
    [ "http://Righthost:123/", "wronghost:321" => 200, 'righthost', '123'    ],
    [ "http://128.0.0.1/",     "126.0.0.1"     => 200, '128.0.0.1', 'REMOTE' ],
    [ "http://128.0.0.1:123/", "126.0.0.1:321" => 200, '128.0.0.1', '123'    ],
    [ "http://[::1]/",         "[::2]"         => 200, '[::1]',     'REMOTE' ],
    [ "http://[::1]:123/",     "[::2]:321"     => 200, '[::1]',     '123'    ],
);

my @todo;
if (!have_min_apache_version('2.4.24')) {
   # r1426827
   push @todo, 32, 35, 56, 59, 80, 83;
}
if (!have_min_apache_version('2.4')) {
   # r1147614, PR 26005
   push @todo, 20, 23, 26, 29;
}

plan tests => 3 * scalar(@test_cases), todo => \@todo, need need_min_apache_version('2.2'), need_cgi;

foreach my $t (@test_cases) {
    my $req = "GET $t->[0]$url_suffix HTTP/1.1\r\nConnection: close\r\n";
    $req .= "Host: $t->[1]\r\n" if defined $t->[1];
    $req .= "\r\n";
    	
    my %ex = (
        rc           => $t->[2],
        SERVER_NAME  => $t->[3],
        SERVER_PORT  => $t->[4],
    );

    my $sock = Apache::TestRequest::vhost_socket();
    if (!$sock) {
        print "# failed to connect\n";
        ok(0);
        next;
    }
    if (defined $ex{SERVER_PORT} && $ex{SERVER_PORT} eq 'REMOTE') {
        my $peername = getpeername($sock);
        my ($port) = sockaddr_in($peername);
        $ex{SERVER_PORT} = "$port";
    }

    $sock->print($req);
    $sock->shutdown(1);
    sleep(0.1);
    print "# SENDING:\n# ", escape($req), "\n";

    my $response_data = "";
    my $buf;
    while ($sock->read($buf, 10000) > 0) {
        $response_data .= $buf;
    }
    my $response = HTTP::Response->parse($response_data);
    if (! defined $response) {
        die "HTTP::Response->parse failed";
    }
    my $rc = $response->code;
    if (! defined $rc) {
        print "# HTTPD dropped the connection\n";
        ok(0);
    }
    else {
        print "# expecting $ex{rc}, got ", $rc, "\n";
        ok ($rc == $ex{rc});
    }

    foreach my $var (qw/SERVER_NAME SERVER_PORT/) {
        if (! defined $ex{$var}) {
            print "# don't care about $var\n";
            ok(1);
        }
        elsif ($response_data =~ /^$var = (.*)$/m) {
            my $val = $1;
            print "# got $var='$val', expected '$ex{$var}'\n";
            ok($val eq $ex{$var});
        }
        else {
            print "# no $var in response, expected '$ex{$var}'\n";
            ok(0);
        }
    }
}

sub escape
{
    my $in = shift;
    $in =~ s{\\}{\\\\}g;
    $in =~ s{\r}{\\r}g;
    $in =~ s{\n}{\\n}g;
    $in =~ s{\t}{\\t}g;
    $in =~ s{([\x00-\x1f])}{sprintf("\\x%02x", ord($1))}ge;
    return $in;
}
