wget/tests/SSLServer.pm

237 lines
6.3 KiB
Perl
Raw Normal View History

2022-05-14 03:18:11 +08:00
package SSLServer;
# This is only HTTPS server for now.
# But it is named SSLServer to easily distinguish from HTTPServer
use strict;
use warnings;
use lib '.';
use HTTP::Daemon;
use HTTP::Status;
use HTTP::Headers;
use HTTP::Response;
# Skip this test rather than fail it when the module isn't installed
if (!eval {require IO::Socket::SSL;1;}) {
print STDERR "This test needs the perl module \"IO::Socket::SSL\".\n";
print STDERR "Install e.g. on Debian with 'apt-get install libio-socket-ssl-perl'\n";
print STDERR " or if using cpanminus 'cpanm IO::Socket::SSL' could be used to install it.\n";
exit 77; # skip
}
#use IO::Socket::SSLX; # 'debug4';
use HTTPServer;
our @ISA = qw(IO::Socket::SSL HTTP::Daemon::ClientConn HTTP::Daemon HTTPServer);
my $VERSION = 0.01;
my $CRLF = "\015\012"; # "\r\n" is not portable
# Config options for server
my $log = undef;
my $DEBUG = undef;
my %ssl_params;
my $sslsock;
my $plaincon;
my %args;
#$HTTP::Daemon::DEBUG=5;
#*DEBUG = \$HTTP::Daemon::DEBUG;
$args{SSL_error_trap} ||= \&ssl_error;
my $class = 'SSLServer';
my $self = {};
$self = bless $self, $class;
sub init
{
my $self = shift;
my %sargs = @_;
%ssl_params = %sargs;
unless (exists($ssl_params{'lhostname'}) &&
exists($ssl_params{'sslport'}) &&
exists($ssl_params{'ciphers'}) &&
exists($ssl_params{'cafile'}) &&
exists($ssl_params{'certfile'}) &&
exists($ssl_params{'keyfile'})) {
die "Required parameters for SSL tests are missing";
}
}
sub ssl_setup_conn
{
$sslsock = IO::Socket::SSL->new(LocalAddr => $ssl_params{'lhostname'},
LocalPort => $ssl_params{'sslport'},
Listen => 10,
Timeout => 30,
ReuseAddr => 1,
SSL_cipher_list => $ssl_params{'ciphers'},
SSL_verify_mode => 0x00,
SSL_ca_file => $ssl_params{'cafile'},
SSL_cert_file => $ssl_params{'certfile'},
SSL_key_file => $ssl_params{'keyfile'});
$sslsock || warn $IO::Socket::SSL::ERROR;
return $sslsock;
}
sub fileno
{
my $self = shift;
my $fn = ${*$self}{'_SSL_fileno'};
return defined($fn) ? $fn : $self->SUPER::fileno();
}
sub accept
{
my $self = shift;
my $pkg = shift || "SSLServer";
my ($sock, $peer) = $sslsock->accept($pkg);
if ($sock) {
${*$sock}{'httpd_daemon'} = $self;
${*$self}{'httpd_daemon'} = $sock;
my $fileno = ${*$self}{'_SSL_fileno'} = &fileno($self);
my $f = $sock->fileno;
return wantarray ? ($sock, $peer) : $sock;
}
else {
print STDERR "Failed to get socket from SSL\n" if $DEBUG;
return;
}
}
sub _default_port { 443; }
sub _default_scheme { "https"; }
sub url
{
my $self = shift;
my $url = $self->SUPER::url;
return $url if ($self->can("HTTP::Daemon::_default_port"));
# Workaround for old versions of HTTP::Daemon
$url =~ s!^http:!https:!;
$url =~ s!/$!:80/! unless ($url =~ m!:(?:\d+)/$!);
$url =~ s!:443/$!/!;
return $url;
}
sub _need_more
{
my $self = shift;
if ($_[1]) {
my($timeout, $fdset) = @_[1,2];
print STDERR "select(,,,$timeout)\n" if $DEBUG;
my $n = select($fdset,undef,undef,$timeout);
unless ($n) {
$self->reason(defined($n) ? "Timeout" : "select: $!");
return;
}
}
my $total = 0;
while (1){
print STDERR sprintf("sysread() already %d\n",$total) if $DEBUG;
my $n = sysread(${*$self}{'httpd_daemon'}, $_[0], 2048, length($_[0]));
print STDERR sprintf("sysread() just \$n=%s\n",(defined $n?$n:'undef')) if $DEBUG;
$total += $n if defined $n;
last if $! =~ 'Resource temporarily unavailable';
#SSL_Error because of aggressive reading
$self->reason(defined($n) ? "Client closed" : "sysread: $!") unless $n;
last unless $n;
last unless $n == 2048;
}
$total;
}
sub daemon
{
my $self = shift;
${*$self}{'httpd_daemon'};
}
sub conn
{
my $self = shift;
${*$self}{'sslcon'};
}
sub run
{
my ($self, $urls, $synch_callback) = @_;
my $initialized = 0;
my $sslsock;
while (1)
{
if (!$initialized)
{
$sslsock = $self->ssl_setup_conn();
$sslsock || warn "Failed to get ssl sock";
$initialized = 1;
open (LOGFILE, '>', "/tmp/wgetserver.log");
LOGFILE->autoflush(1);
print LOGFILE "Starting logging";
$synch_callback->() if $synch_callback;
}
my $con = $self->accept();
${*$self}{'sslcon'} = $con;
while (my $req = $self->get_request)
{
#my $url_path = $req->url->path;
my $url_path = $req->url->as_string;
if ($url_path =~ m{/$})
{ # append 'index.html'
$url_path .= 'index.html';
}
#if ($url_path =~ m{^/}) { # remove trailing '/'
# $url_path = substr ($url_path, 1);
#}
if ($log)
{
print LOGFILE "Method: ", $req->method, "\n";
print LOGFILE "Path: ", $url_path, "\n";
print LOGFILE "Available URLs: ", "\n";
foreach my $key (keys %$urls)
{
print LOGFILE $key, "\n";
}
}
if (exists($urls->{$url_path}))
{
print LOGFILE "Serving requested URL: ", $url_path, "\n" if $log;
next unless ($req->method eq "HEAD" || $req->method eq "GET");
my $url_rec = $urls->{$url_path};
HTTPServer::send_response($self, $req, $url_rec, $con);
last;
}
else
{
print LOGFILE "Requested wrong URL: ", $url_path, "\n" if $log;
$con->send_error($HTTP::Status::RC_FORBIDDEN);
last;
}
last;
}
print LOGFILE "Closing connection\n" if $log;
close(LOGFILE);
$con->close();
}
}
1;
# vim: et ts=4 sw=4