package QBit::WebInterface::OwnServer;
$QBit::WebInterface::OwnServer::VERSION = '0.008';
use qbit;
use base qw(QBit::WebInterface);
use IO::Socket;
use MIME::Types;
use QBit::WebInterface::OwnServer::Request;
use QBit::WebInterface::Response;
sub run {
my ($self, %opts) = @_;
$opts{'port'} ||= 5000;
my $server_socket = IO::Socket::INET->new(
Proto => 'tcp',
LocalPort => $opts{'port'},
Listen => SOMAXCONN,
Reuse => 1,
)
|| throw Exception gettext('Cannot create socket');
l "http://127.0.0.1:$opts{'port'}";
l "Ctrl+C to terminate";
my $mimetypes = MIME::Types->new();
while (my $socket = $server_socket->accept()) {
try {
$self->request(QBit::WebInterface::OwnServer::Request->new(socket => $socket, port => $opts{'port'}));
l $self->request->uri;
if (length(my $static_path = $self->_is_static($self->request->uri))) {
if ($static_path =~ /\.\.\// || !-f $static_path) {
$self->response(QBit::WebInterface::Response->new(status => 404));
} else {
my $mimetype = $mimetypes->mimeTypeOf($static_path);
$self->response(
QBit::WebInterface::Response->new(
status => 200,
data => \readfile($static_path, binary => $mimetype && $mimetype->isBinary()),
)
);
$self->response->content_type($mimetype->type() . ($mimetype->isAscii() ? '; charset=UTF-8' : ''))
if $mimetype;
}
} else {
$self->build_response();
}
}
catch {
l shift->as_string();
$self->response(QBit::WebInterface::Response->new()) unless $self->response();
$self->response->status(500);
$self->response->data('');
};
binmode($socket);
my $status = $self->response->status || 200;
print $socket "HTTP/1.0 $status "
. (
exists($QBit::WebInterface::HTTP_STATUSES{$status})
? " $QBit::WebInterface::HTTP_STATUSES{$status}"
: 'Unknown'
)
. "\n";
print $socket 'Server: QBit::WebInterface::OwnServer (' . ref($self) . ")\n";
print $socket 'Set-Cookie: ' . $_->as_string() . "\n" foreach values(%{$self->response->cookies});
while (my ($key, $value) = each(%{$self->response->headers})) {
print $socket "$key: $value\n";
}
if ($status == 200) {
print $socket 'Content-Type: ' . $self->response->content_type . "\n\n";
my $filename = $self->response->filename;
if (defined($filename)) {
utf8::encode($filename) if utf8::is_utf8($filename);
print $socket 'Content-Disposition: '
. 'attachment; filename="'
. $self->_escape_filename($self->response->filename) . '"';
}
if (defined($self->response->data)) {
my $data_ref = ref($self->response->data) ? $self->response->data : \$self->response->data;
utf8::encode($$data_ref) if utf8::is_utf8($$data_ref);
print $socket $$data_ref;
}
} elsif ($status == 301 || $status == 302) {
print $socket 'Location: ' . $self->response->location . "\n\n";
} elsif ($status == 404) {
print $socket "Content-Type: text/html\n\n";
print $socket '<html><body><h1>Not found</h1><body></html>';
} elsif ($status == 500) {
print $socket "Content-Type: text/html\n\n";
print $socket '<html><body><h1>Internal server error</h1><body></html>';
} else {
print $socket "\n\n";
}
close($socket);
}
}
sub _is_static {
my ($self, $uri) = @_;
my $path = __normalize_path($uri);
$self->{'static_locations'} ||= {'/qbit' => $self->{'__ORIG_OPTIONS__'}{'FrameworkPath'} . 'QBit/data'};
foreach my $l (sort {length($b) <=> length($a)} keys(%{$self->{'static_locations'}})) {
my $location = $l;
$location =~ s/\/$//;
if ($path =~ /^$location\/(.+)$/) {
return "$self->{'static_locations'}{$l}/$1";
}
}
return '';
}
sub __normalize_path {
my ($path) = @_;
for ($path) {
s/\?.+$//;
s/[^\/a-zA-Z0-9_\.-]//g;
while (s/\/[a-zA-Z0-9_-]+?\/\.\.(\/|$)/$1/) { }
}
return $path;
}
TRUE;
__END__
=encoding utf8
=head1 Name
QBit::WebInterface::OwnServer - WebInterface with own HTTP server.
=head1 GitHub
https://github.com/Madskill/QBit-WebInterface-OwnServer
=head1 Install
=over
=item *
cpanm QBit::WebInterface::OwnServer
=back
For more information. please, see code.
=cut