1
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
2
# Lightweight WWW Server
4
# This is based on Tuari (http://github.com/crcx/tuari),
5
# but has been modified for use with the WWW framework I
8
# It's not secure - don't use it on a system open to the
9
# public, unless you want to risk your box.
10
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
11
# As with most of my code, this is gifted to the public
12
# domain. Use, modify, and share freely.
13
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
15
################ Configuration ##################################
19
# Change the next line to let tuari know where it lives:
20
# This directory will also be the server root directory
25
# The port on which tuari will listen:
29
# Add your own MIME types here; text/plain is the default.
31
'\.html?' => 'text/html',
32
'\.gif' => 'image/gif',
33
'\.jpe?g' => 'image/jpeg'
37
################### no real need to edit below ##################
40
package tuari; # keep namespace separate from CGI scripts
51
################################## Subroutines ###################
53
sub logerr($$); sub logmsg($); sub cat($$;$); # forward declarations
57
$tuari::basdir= $basdir; # make this variable visible for CGI scripts
62
my $tcp = getprotobyname('tcp');
63
socket(Server, PF_INET, SOCK_STREAM, $tcp) or die "socket: $!";
64
setsockopt(Server, SOL_SOCKET, SO_REUSEADDR, pack("l", 1)) or warn "setsockopt: $!";
65
bind(Server, sockaddr_in($port, INADDR_ANY)) or die "bind: $!";
66
listen(Server,SOMAXCONN) or die "listen: $!";
67
logmsg "server started on port $port";
70
for ( ; accept(Client,Server); close Client) {
78
my $remote_sockaddr = getpeername(STDIN);
79
my (undef, $iaddr) = sockaddr_in($remote_sockaddr);
80
my $peername = gethostbyaddr($iaddr, AF_INET) || "localhost";
81
my $peeraddr = inet_ntoa($iaddr) || "127.0.0.1";
83
my $local_sockaddr = getsockname(STDIN);
84
my (undef, $iaddr) = sockaddr_in($remote_sockaddr);
85
$localname = gethostbyaddr($iaddr, AF_INET) || "localhost";
86
my $localaddr = inet_ntoa($iaddr) || "127.0.0.1";
90
my ($method, $url, $proto, undef) = split;
93
logmsg "<- $peername: $_";
94
my ($file, undef, $arglist) = ($url =~ /([^?]*)(\?(.*))?/); # split at ?
95
my $file_escaped = $file;
96
$file =~ s/%([\dA-Fa-f]{2})/chr(hex($1))/eg; # %20 -> space
98
if ( $method !~ /^(GET|POST|HEAD)$/ ) {
99
logerr 400, "I don't understand method $method";
104
# if (-d "$basdir/$file" ) {
105
# unless ($file =~ m#/$#) {
106
# redirect ("$file/");
109
# my $dir = "$basdir/$file";
110
# if (-f "$dir/index.html") {
111
# $file .= "/index.html";
113
# directory_listing($file);
118
# if ( not -r "$basdir/$file" ) {
119
# logerr 404, "$file: $!";
123
# my @env_vars = qw(USER_AGENT CONTENT_LENGTH CONTENT_TYPE);
124
# foreach my $var (@env_vars) {
125
# $ENV{$var} = ""; # delete $ENV{$var} will crash perl on Netbook :-(
129
# /^User-Agent: (.+)/i and $ENV{USER_AGENT} = $1;
130
# /^Content-length: (\d+)/i and $ENV{CONTENT_LENGTH} = $1;
131
# /^Content-type: (.+)/i and $ENV{CONTENT_TYPE} = $1;
136
print "HTTP/1.0 200 OK\n"; # probably OK by now
140
# if ( $file =~ m/\.cgi$/i) {
141
# $ENV{SERVER_PROTOCOL} = $proto;
142
# $ENV{SERVER_PORT} = $port;
143
# $ENV{SERVER_NAME} = $localname;
144
# $ENV{SERVER_URL} = "http://$localname:$port/";
145
# $ENV{SCRIPT_NAME} = $file;
146
# $ENV{SCRIPT_FILENAME} = "$basdir/$file";
147
# $ENV{REQUEST_URI} = $url;
148
# $ENV{REQUEST_METHOD} = $method;
149
# $ENV{REMOTE_ADDR} = $peeraddr;
150
# $ENV{REMOTE_HOST} = $peername;
151
# $ENV{QUERY_STRING} = $arglist;
152
# $ENV{SERVER_SOFTWARE} = "tuari/$version";
154
# if ($method =~ /POST/) {
155
# logmsg "<- Content-length: $ENV{CONTENT_LENGTH}, type: $ENV{CONTENT_TYPE}";
157
# cgi_run ($file,$arglist);
161
# my $mime_type = "text/plain"; # default
162
# foreach my $suffix (keys %mime_types) {
163
# if ($file =~ /$suffix$/i) {
164
# $mime_type = $mime_types{$suffix};
168
# cat $file, $mime_type, $method || logerr 500, "$file: $!";
170
open my $fh_echo, '-|' or exec "echo $file bye >foo" or die "echo failed: $!\n";
173
#$r = "\Qecho render $file bye >foo";
176
$m = `./retro --with foo webApp`;
179
Content-type: text/html
186
die "Fatal error: accept failed: $!\n"; # This should never happen
189
#################### other subroutines ####################
192
my $fulltime = localtime();
193
my ($hms) = ($fulltime =~ /(\d\d:\d\d:\d\d)/);
194
print STDERR "$$ $hms @_\n";
198
my ($code, $detail) = @_;
201
400 => 'Bad Request',
203
500 => 'Internal Server Error',
204
501 => 'Not Implemented',
206
my $msg = "$code " . $codes{$code};
207
logmsg "-> $msg $detail";
210
Content-type: text/html
216
<p><I>tuari/$version server at $localname port $port</I></p>
223
sub cat($$;$){ # cat ($file, $mimetype) writes Content-type header and $file to STDOUT
224
my ($file, $mimetype, $method) = @_;
225
$method = "GET" unless $method;
226
my $fullpath = "$basdir/$file";
228
my ($x,$x,$x,$x,$x,$x,$x,$length,$x,$mtime) = stat($fullpath);
229
$mtime = gmtime $mtime;
230
my ($day, $mon, $dm, $tm, $yr) =
231
($mtime =~ m/(...) (...) (..) (..:..:..) (....)/);
233
print "Content-length: $length\n";
234
print "Last-Modified: $day, $dm $mon $yr $tm GMT\n";
235
print "Content-type: $mimetype\n\n";
237
if ($method eq "GET") {
238
local $/; undef $/; # gobble whole files
239
open IN, "<$fullpath" || return 0;
242
$sent = length($content);
245
logmsg "-> 200 OK $file: $sent bytes sent as $mimetype";
251
my ($file,$arglist) = @_;
252
my ($dir) = ($file =~ /^(.*\/)/);
253
my $path = "$basdir/$file";
254
chdir "$basdir/$dir" or return logerr 500, "Cannot chdir to $basdir/$dir: $!";
256
logmsg "-> doing $path";
258
{package main; do $path;} # or return logerr 500, "Cannot do $path: $!";
259
$@ and logerr 500, "in $file:<br> $@";
264
sub directory_listing {
267
chdir "$basdir/$dir" or return logerr 500, "Cannot chdir to $basdir/$dir: $!";
268
my @files = glob("*");
271
Content-type: text/html
274
<head><title>$dir</title></head>
278
print "<p><a href=..>..</a></p>\n";
279
foreach my $file (sort @files) {
280
-d $file and $file .= "/";
281
print "<p><a href=./$file>$file</a></p>\n";
285
<p><I>tuari/$version server at $localname port $port</I>
288
logmsg "-> 200 OK listing $dir";
294
print "HTTP/1.0 301 Moved Permanently\nLocation: $redir\n\n";
295
logmsg "-> 301 Moved Permanently to $redir"