mirror of https://github.com/BOINC/boinc.git
246 lines
5.8 KiB
Perl
Executable File
246 lines
5.8 KiB
Perl
Executable File
#!/usr/bin/env perl
|
|
|
|
# -T
|
|
|
|
# $Id$
|
|
|
|
# testproxy - proxy a TCP/IP connection with configurable mid-way
|
|
# disconnections, for simulating internet transfer failures
|
|
|
|
use strict;
|
|
use warnings;
|
|
use Carp;
|
|
use IO::Socket::INET;
|
|
|
|
my $listen_port = shift;
|
|
my $target_server = shift;
|
|
my $testcode = join(' ', @ARGV);
|
|
|
|
if (!$listen_port || !$target_server) {
|
|
print STDERR <<HELP;
|
|
syntax: $0 <listen_port> <target_server:port> CODE...
|
|
|
|
CODE is evaluated every 128 bytes transferred from server to client.
|
|
Some variables you can access/modify:
|
|
\$target, \$client : perl IO::Handle::INET objects
|
|
\$nconnections : number of connections so far
|
|
\$url : url of request (if applicable)
|
|
\$time : seconds since server started
|
|
\$chars, \$nchars : characters & length about to send to client.
|
|
\$bytes_transferred : characters already sent to client
|
|
\$start : beginning of connection
|
|
\$done, \$success : finished transfer; successful transfer
|
|
\$n, \$m : unused variables initialized to 0
|
|
|
|
For more, view the code.
|
|
|
|
Functions:
|
|
close_connection, kill_server, if_done_kill, if_done_ping, logmsg
|
|
|
|
You can also call standard perl functions such as print, sleep, exit.
|
|
|
|
Examples:
|
|
# fail connections for first 3 connections
|
|
$0 8080 localhost:80 'close_connection if \$nconnections < 4'
|
|
|
|
# sleep 5 seconds in the middle of transfer, and print "success" if
|
|
# transfer succeeds; kill the server after the first connection
|
|
$0 8080 localhost:80 'sleep 5 if \$bytes_transferred == 256;
|
|
if (\$done) { print "success\\n" if \$success; kill_server; \$success }'
|
|
|
|
# equivalent to above:
|
|
$0 8080 localhost:80 'sleep 5 if \$bytes_transferred == 256;
|
|
if_done_kill(); if_done_ping();'
|
|
|
|
HELP
|
|
;
|
|
exit(1);
|
|
}
|
|
|
|
if ($target_server !~ /:/) {
|
|
$target_server .= ':http';
|
|
}
|
|
|
|
my $N = "\015\012";
|
|
|
|
sub proxy;
|
|
sub spawn;
|
|
use POSIX qw/strftime/;
|
|
sub logmsg { print STDERR "$0 $$ ", strftime("%Y/%m/%d %H:%M:%S", localtime), ": @_\n" }
|
|
|
|
my $server = IO::Socket::INET->new(Listen => 5,
|
|
LocalAddr => inet_ntoa(INADDR_ANY),
|
|
LocalPort => $listen_port,
|
|
Proto => 'tcp',
|
|
ReuseAddr => 1)
|
|
or die "$0: creating socket on port $listen_port: $!";
|
|
|
|
logmsg "server started on port $listen_port proxy to $target_server";
|
|
|
|
my $waitedpid = 0;
|
|
my $paddr;
|
|
my $server_pid = $$;
|
|
|
|
use POSIX ":sys_wait_h";
|
|
sub REAPER {
|
|
while (($waitedpid = waitpid(-1,WNOHANG)) > 0) {
|
|
logmsg "reaped $waitedpid" . ($? ? " with exit $?" : '');
|
|
}
|
|
# $SIG{CHLD} = \&REAPER; # loathe sysV
|
|
}
|
|
|
|
# $SIG{CHLD} = \&REAPER;
|
|
|
|
my $time_started = time();
|
|
my $nconnections = 0;
|
|
my $cclient;
|
|
|
|
# for ( $waitedpid = 0;
|
|
# ($cclient = $server->accept()) || $waitedpid;
|
|
# $waitedpid = 0)
|
|
while (($cclient = $server->accept()))
|
|
{
|
|
# next if $waitedpid and not $cclient;
|
|
die unless $cclient;
|
|
REAPER();
|
|
my $paddr = $cclient->peername();
|
|
my($port,$iaddr) = sockaddr_in($paddr);
|
|
my $name = gethostbyaddr($iaddr,AF_INET);
|
|
|
|
logmsg "connection from $name:$port"; # [", inet_ntoa($iaddr), "]"
|
|
|
|
++$nconnections;
|
|
|
|
spawn \&proxy, $cclient;
|
|
}
|
|
|
|
sub spawn {
|
|
my $coderef = shift;
|
|
|
|
unless ($coderef && ref($coderef) eq 'CODE') {
|
|
confess "usage: spawn CODEREF";
|
|
}
|
|
|
|
my $pid;
|
|
if (!defined($pid = fork)) {
|
|
logmsg "cannot fork: $!";
|
|
return;
|
|
} elsif ($pid) {
|
|
logmsg "begat $pid";
|
|
return; # I'm the parent
|
|
}
|
|
# else I'm the child -- go spawn
|
|
|
|
exit &$coderef(@_);
|
|
}
|
|
|
|
sub kill_server()
|
|
{
|
|
kill "INT", $server_pid;
|
|
}
|
|
|
|
my $start = 0;
|
|
my $done = 0;
|
|
my $success = 0;
|
|
my $url;
|
|
my $n = 0;
|
|
my $m = 0;
|
|
my $bytes_transferred = 0;
|
|
my $chars;
|
|
my $nchars;
|
|
|
|
sub if_done_ping()
|
|
{
|
|
if ($done) {
|
|
if ($success) {
|
|
print "success\n";
|
|
} else {
|
|
print "failed\n";
|
|
return 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
sub if_done_kill()
|
|
{
|
|
if ($done) {
|
|
kill_server();
|
|
}
|
|
}
|
|
|
|
sub eval_test_code()
|
|
{
|
|
return unless $testcode;
|
|
my $time = time() - $time_started;
|
|
warn "test code failed: $!" unless defined eval $testcode;
|
|
}
|
|
|
|
my ($client, $target);
|
|
|
|
sub close_connection {
|
|
my $ok = (shift) ? 1 : 0;
|
|
logmsg "closing connection ok=$ok";
|
|
# $client->close(), $target->close() doesn't always work for some reason
|
|
# (maybe to do with forked processes)
|
|
$client->shutdown(2);
|
|
$target->shutdown(2);
|
|
logmsg "exiting";
|
|
exit !$ok;
|
|
}
|
|
|
|
sub proxy {
|
|
$client = shift or die;
|
|
|
|
$target = IO::Socket::INET->new(PeerAddr => $target_server)
|
|
or die "$0: couldn't connect to $target_server: $!";
|
|
|
|
$client->autoflush(1);
|
|
$target->autoflush(1);
|
|
|
|
{
|
|
$bytes_transferred = 0;
|
|
$chars = undef; $nchars = 0;
|
|
$done = 0;
|
|
$success = 0;
|
|
$start = 1;
|
|
$url = '';
|
|
eval_test_code();
|
|
$start = 0;
|
|
}
|
|
|
|
# transfer lines from client -> server until we get an empty line
|
|
|
|
while (my $line = $client->getline()) {
|
|
if ($. == 1 && $line =~ /^(GET|PUT|POST) ([^\s]+)/) {
|
|
$url = $2;
|
|
logmsg "url = $url";
|
|
}
|
|
$target->print($line);
|
|
$line =~ s/[\015\012]+$//;
|
|
last unless $line;
|
|
}
|
|
|
|
# indicate we have stopped reading data from client and stopped writing
|
|
# data to server (not sure if this helps)
|
|
$client->shutdown(0);
|
|
$target->shutdown(1);
|
|
|
|
# transfer from server->client
|
|
|
|
while ($nchars = $target->read($chars, 128)) {
|
|
eval_test_code();
|
|
$bytes_transferred += $nchars;
|
|
$client->write($chars, $nchars);
|
|
}
|
|
|
|
{
|
|
$chars = undef; $nchars = 0;
|
|
$done = 1;
|
|
$success = $client->connected() && 1;
|
|
eval_test_code();
|
|
}
|
|
|
|
close_connection(1);
|
|
return 0;
|
|
}
|