my
$PORT
= 40000 +
int
(
rand
(10000));
my
$RUN_IPV6
=
eval
{
my
$ipv6_host
= get_localhost(AF_INET6);
socket
my
$sockh
, Socket::PF_INET6(), SOCK_STREAM, 0 or
die
"Cannot socket(PF_INET6) - $!"
;
my
(
$err
,
@res
) = Socket::getaddrinfo(
$ipv6_host
,
$PORT
, {
family
=> AF_INET6,
socktype
=> SOCK_STREAM } );
die
$err
if
$err
;
for
my
$r
(
@res
) {
next
unless
(
$r
->{
'family'
} == AF_INET6);
bind
$sockh
,
$r
->{
'addr'
} or
die
"Cannot bind - $!"
;
last
;
}
return
1;
};
if
(
$RUN_IPV6
) {
plan
tests
=> 34;
}
else
{
diag(
"Skipping IPv6"
);
plan
tests
=> 17;
}
sub
setup_listener {
my
$self
=
shift
;
$self
->SUPER::setup_listener();
sleep
2;
}
1;
my
$DEBUG
= 1
if
@ARGV
;
my
@pids
= ();
my
@classes
= (
qw(HTTP::Server::Simple SlowServer)
);
for
my
$class
(
@classes
) {
run_server_tests(
$class
, AF_INET);
run_server_tests(
$class
, AF_INET6)
if
$RUN_IPV6
;
$PORT
++;
}
for
my
$fam
( AF_INET, AF_INET6 ) {
next
if
(
$fam
== AF_INET6 && not
$RUN_IPV6
);
my
$s
=HTTP::Server::Simple::CGI->new(
$PORT
,
$fam
);
is(
$fam
,
$s
->family(),
'family OK'
);
$s
->host(get_localhost(
$fam
));
my
$pid
=
$s
->background();
diag(
"started server PID='$pid'"
)
if
(
$ENV
{
'TEST_VERBOSE'
});
like(
$pid
,
'/^-?\d+$/'
,
'pid is numeric'
);
select
(
undef
,
undef
,
undef
,0.2);
SKIP: {
skip
"No localhost for $fam"
, 4
unless
defined
$s
->host;
my
$content
=fetch(
$fam
,
"GET / HTTP/1.1"
,
""
);
like(
$content
,
'/Congratulations/'
,
"Returns a page"
);
eval
{
like(fetch(
$fam
,
"GET a bogus request"
),
'/bad request/i'
,
"knows what a request isn't"
);
};
fail(
"got exception in client: $@"
)
if
$@;
like(fetch(
$fam
,
"GET / HTTP/1.1"
,
""
),
'/Congratulations/'
,
"HTTP/1.1 request"
);
like(fetch(
$fam
,
"GET /"
),
'/Congratulations/'
,
"HTTP/0.9 request"
);
}
is(
kill
(9,
$pid
),1,
'Signaled 1 process successfully'
);
}
is(
kill
( 9,
$_
), 1,
"Killed PID: $_"
)
for
@pids
;
sub
fetch {
my
$family
=
shift
;
my
$hostname
= get_localhost(
$family
);
my
$port
=
$PORT
;
my
$message
=
join
""
,
map
{
"$_\015\012"
}
@_
;
my
$timeout
= 5;
my
$response
;
my
$proto
=
getprotobyname
(
'tcp'
) ||
die
"getprotobyname: $!"
;
my
$socktype
= SOCK_STREAM;
eval
{
local
$SIG
{ALRM} =
sub
{
die
"early exit - SIGALRM caught"
};
alarm
$timeout
*2;
my
$paddr
;
my
(
$err
,
@res
) = Socket::getaddrinfo(
$hostname
,
$port
, {
family
=>
$family
,
socktype
=>
$socktype
,
protocol
=>
$proto
});
die
"getaddrinfo: $err operating on [$hostname] [$port] [$family] [$socktype] [$proto]"
if
(
$err
);
while
(
$a
=
shift
(
@res
)) {
next
unless
(
$family
==
$a
->{
'family'
});
next
unless
(
$proto
==
$a
->{
'protocol'
});
next
unless
(
$socktype
==
$a
->{
'socktype'
});
$paddr
=
$a
->{
'addr'
};
last
}
socket
(SOCK,
$family
,
$socktype
,
$proto
) ||
die
"socket: $!"
;
connect
(SOCK,
$paddr
) ||
die
"connect: $!"
;
(
send
SOCK,
$message
, 0) ||
die
"send: $!"
;
my
$rvec
=
''
;
vec
(
$rvec
,
fileno
(SOCK), 1) = 1;
die
"vec(): $!"
unless
$rvec
;
$response
=
''
;
for
(;;) {
my
$r
=
select
(
$rvec
,
undef
,
undef
,
$timeout
);
die
"select: timeout - no data to read from server"
unless
(
$r
> 0);
my
$l
=
sysread
(SOCK,
$response
, 1024,
length
(
$response
));
die
"sysread: $!"
unless
defined
(
$l
);
last
if
(
$l
== 0);
}
$response
=~ s/\015\012/\n/g;
(
close
SOCK) ||
die
"close(): $!"
;
alarm
0;
};
if
($@) {
return
"[ERROR] $@"
;
}
else
{
return
$response
;
}
}
sub
run_server_tests {
my
$class
=
shift
;
my
$fam
=
shift
;
my
$s
=
$class
->new(
$PORT
,
$fam
);
is(
$s
->family(),
$fam
,
'constructor set family properly'
);
is(
$s
->port(),
$PORT
,
"Constructor set port correctly"
);
my
$localhost
= get_localhost(
$fam
);
$s
->host(
$localhost
);
my
$pid
=
$s
->background();
select
(
undef
,
undef
,
undef
,0.2);
like(
$pid
,
'/^-?\d+$/'
,
'pid is numeric'
);
SKIP: {
skip
"No localhost defined for $fam"
, 1
unless
defined
$localhost
;
my
$content
=fetch(
$fam
,
"GET / HTTP/1.1"
,
""
);
like(
$content
,
'/Congratulations/'
,
"Returns a page"
);
}
push
@pids
,
$pid
;
}
{
my
%localhost
;
sub
get_localhost {
my
$family
=
shift
;
return
$localhost
{
$family
}
if
$localhost
{
$family
};
if
(
$family
== AF_INET) {
$localhost
{
$family
} =
gethostbyaddr
(INADDR_LOOPBACK,
$family
);
}
else
{
$localhost
{
$family
} =
gethostbyaddr
(Socket::IN6ADDR_LOOPBACK,
$family
);
}
return
$localhost
{
$family
};
}
}