# This test requires a locally deployed httpbin
#
# $ docker pull kennethreitz/httpbin
# $ docker run -p 31234:80 kennethreitz/httpbin

BEGIN {
  if ( $ENV{NO_NETWORK_TESTING} ) {
    print "1..0 # SKIP Live tests disabled due to NO_NETWORK_TESTING\n";
    exit;
  }
  eval {
        require IO::Socket::INET;
        my $s = IO::Socket::INET->new(
            PeerHost => "localhost:31234",
            Timeout  => 5,
        );
        die "Can't connect: $@" unless $s;
  };
  if ($@) {
        print "1..0 # SKIP Can't connect to localhost\n";
        print $@;
        exit;
  }
}

use strict;
use warnings;
use Test::More;
use Net::HTTP;

# Attempt to verify that RT#112313 (Hang in my_readline() when keep-alive => 1 and $response_size % 1024 == 0) is fixed

# To do that, we need responses (headers + body) that are even multiples of 1024 bytes. So we
# iterate over the same URL, trying to grow the response size incrementally...

# There's a chance this test won't work if, for example, the response body grows by one byte while
# the Content-Length also rolls over to one more digit, thus increasing the total response by two
# bytes.

# So, we check that the response growth is only one byte after each iteration and also test multiple
# times across the 1024, 2048 and 3072 boundaries...


sub try
{
    my $n = shift;

    # Need a new socket every time because we're testing with Keep-Alive...
    my $s = Net::HTTP->new(
        Host            => "localhost:31234",
        KeepAlive       => 1,
        PeerHTTPVersion => "1.1",
    ) or die "$@";

    $s->write_request(GET => '/headers',
        'User-Agent' => "Net::HTTP - $0",
        'X-Foo'      => ('x' x $n),
    );

    # Wait until all data is probably available on the socket...
    sleep 1;

    my ($code, $mess, @headers) = $s->read_response_headers();

    # XXX remove X-Processed-Time header
    for my $i (0..$#headers) {
        if ($headers[$i] eq 'X-Processed-Time') {
            splice @headers, $i, 2;
            last;
        }
    }

    my $body = '';
    while ($s->read_entity_body(my $buf, 1024))
    {
        $body .= $buf;
    }

    # Compute what is probably the total response length...
    my $total_len = length(join "\r\n", 'HTTP/1.1', "$code $mess", @headers, '', $body) - 1;

    # diag("$n - $code $mess => $total_len");
    # diag(join "\r\n", 'HTTP/1.1', "$code $mess", @headers, '', $body);

    $code == 200
        or die "$code $mess";

    return $total_len;
}

my $timeout = 15;
my $wiggle_room = 3;

local $SIG{ALRM} = sub { die 'timeout' };

my $base_len = try(1);
ok($base_len < 1024, "base response length is less than 1024: $base_len");

for my $kb (1024, 2048, 3072)
{
    my $last;

    # Calculate range that will take us across the 1024 boundary...
    for my $n (($kb - $base_len - $wiggle_room) .. ($kb - $base_len + $wiggle_room))
    {
        my $len = -1;

        eval {
            alarm $timeout;
            $len = try($n);
        };

        ok(!$@, "ok for n $n -> response length $len")
            or diag("error: $@");

        # Verify that response length only increased by one since the whole test rests on that assumption...
        is($len - $last, 1, 'response length increased by 1') if $last;

        $last = $len;
    }
}

done_testing();
