From OrganicDesign Wiki
| Legacy: This article describes a concept that has been superseded in the course of ongoing development on the Organic Design wiki. Please do not develop this any further or base work on this concept, this is only useful for a historic record of work done. You may find a link to the currently used concept or function in this article, if not you can contact the author to find out what has taken the place of this legacy item.
|
|
use IO::Socket;
use IO::Select;
use MIME::Base64;
sub server {
%::streams = ();
$::port = shift or 80;
$0 = "$::daemon: $::peer (http$::port)";
my $subname = $::subname;
# Initialise server listening on our port
do { unless ( $::server = new IO::Socket::INET
Listen => 1,
LocalPort => $::port,
Proto => 'tcp',
ReuseAddr => 1 ) {
logAdd "Failed to bind to Port$::port, waiting 10 seconds...";
sleep(10);
}
} until ( $::server );
$::select = new IO::Select $::server;
logAdd "Listening on port $::port";
# HTML interface environment
$::template = readFile 'peer.html';
$::template =~ s/<!-- daemon -->/$::title ($::daemon)/g;
$::template =~ s/<!-- peer -->/$::peer/g;
$::deny = readFile "$::peer.401";
my $views = '';
$views .= "*[[$_]]\n" for ( 'Update', 'Filter', 'Events', 'Edit' );
$::template =~ s/<!-- views -->/ wikiParse($views) /e;
my $nav = "*[[$peer|Gir Home]]\n*[http://www.organicdesign.co.nz/User:$peer User:$peer]\n";
$nav .= "*[[$_]]\n" for (
"env/cmd|Environment Info",
"peerlog/cmd|Peer Log",
"syslog/cmd|Syslog",
"restart/cmd|Restart $::peer",
"stop/cmd|Stop $::peer",
"reboot/cmd|Restart this server",
"fileSync/cmd|Manual fileSync",
"wikiSync/cmd|Manual wikiSync",
"swfCompile/cmd|Manual swfCompile",
"serverBackup/cmd|Backup server now",
"wikiBackup/cmd|Backup wiki now",
"peerBackup/cmd|Backup peer now",
"scpBackups/cmd|SCP Backup now",
"Yi|Hexagrams"
);
$::template =~ s/<!-- navigation -->/ wikiParse($nav) /e;
# Simple HTTP server loop
while(1) {
for my $handle ( $::select->can_read(1) ) {
my $stream = fileno $handle;
$::subname = "$subname/stream$stream";
# Handle is the server, set up a new stream
if ( $handle == $::server ) {
my $newhandle = $::server->accept;
$stream = fileno $newhandle;
$::select->add( $newhandle );
$::streams{$stream}{buffer} = '';
$::streams{$stream}{handle} = $newhandle;
logAdd "New connection: Stream$stream", 'main/new';
}
# Handle is an existing stream with data to read
# NOTE: we should disconnect after certain size limit
# - Process (and remove) all complete messages from this peer's buffer
elsif ( sysread $handle, my $input, 10000 ) {
$::streams{$stream}{buffer} .= $input;
if ( $::streams{$stream}{buffer} =~ s/^(.*\r?\n\r?\n\x00?)//s ) {
processMessage( $stream, $_ ) for split /\r?\n\r?\n\x00?/, $1;
}
}
# Handle is an existing stream with no more data to read
else {
$::select->remove( $handle );
delete $::streams{$stream};
$handle->close();
logAdd "Stream$stream disconnected.";
}
}
}
}
# Process an incoming HTTP message
sub processMessage {
my ( $stream, $msg ) = @_;
my $handle = $::streams{$stream}{handle};
my $headers = '';
my $http = '200 OK';
my $respond = 1;
my $date = strftime "%a, %d %b %Y %H:%M:%S %Z", localtime;
my $article = $::deny;
# Extract info from the HTTP GET request
my ( $title, $ct, $ver ) =
$msg =~ /GET\s+\/+(.*?)(\/(.+))?\s+(HTTP\/[0-9.]+)/ ? ( $1, $3, $4 ) : ( $peer, '', 'HTTP/1.1' );
# If request authenticates service it, else return 401
if ( $ct =~ /^cmd/ ? ( $msg =~ /Authorization: Basic (\w+)/ and decode_base64($1) eq "$::peer:$::pwd1" ) : 1 ) {
# Process a command
if ( $ct =~ /^cmd(.*)/ ) {
logAdd "Executing $title command.";
$article = '<pre>'.command($title).'</pre>';
$ct = '' if $1 ne '/raw';
}
# Process request from other peer or peer-related process
# - doesn't return an HTTP response
elsif ( $ct =~ /^peer\/(.*)/ ) {
my $type = $1;
logAdd "PEER command \"$title\" received from an instance of \"$type\"";
# peerd-child is doing initial connection, keep handle in global scope
# - peerd could talk back here over this stream, but doesn't currently
if ( $title eq 'connect' and $type eq 'peerd' ) {
$::peerdHandle = $handle;
}
# Forward any commands from XmlWiki to peerd unchanged
elsif ( $type eq 'notify-peer.php' ) {
print $::peerdHandle "$title/$type\x00" if defined $::peerdHandle;
}
# SWF initiating connection, ask peerd to connect back on a new stream for a session
elsif ( $title eq 'connect' and $type eq 'interface' ) {
# Send stream-id of swf, so we know which one is wanted by the connection back from peerd
print $::peerdHandle "connect-request/stream$stream\x00" if defined $::peerdHandle;
# Remove the handle from this (server.pl) context, but don't close it
# - also keep our local %stream info to give to session-handler
$::select->remove( $handle );
}
# peerd is connecting back from session request, so we can now spawn the session-handler
elsif ( $title eq 'session' and $type =~ /^peerd\/stream([0-9]+)/ ) {
# Spawn a new session-handler child and pass the peerd and peer.swf stream-handles
spawn 'sessionHandler', $handle, $::streams{$1}{handle};
# We can remove the interface from %streams now too
delete $::streams{$1};
}
# These peer requests don't want HTTP returned currently
$respond = 0;
}
# Process request for raw SWF
elsif ( $ct eq 'application/x-shockwave-flash' ) {
$headers .= "Content-Disposition: inline;filename=$::peer.swf\r\n";
logAdd "SWF movie \"$title\" requested";
$article = readFile $title;
}
# Process request for SWF embedded in HTML
elsif ( $ct eq 'swf' ) {
my $width = 640;
my $height = 480;
my $file = "/$::peer.swf/application/x-shockwave-flash";
$article = "<object classid=\"clsid:D27CDB6E-AE6D-11cf-96B8-444553540000\"
codebase=\"http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=6,0,0,0\"
width=\"$width\" height=\"$height\" id=\"$file\" align=\"\"
type=\"application/x-shockwave-flash\" data=\"$file\">
<param name=\"movie\" value=\"$file\">
<param name=\"quality\" value=\"high\">
<param name=\"bgcolor\" value=\"cccccc\">
<embed src=\"$file\" quality=\"high\" bgcolor=\"cccccc\"
width=\"$width\" height=\"$height\" align=\"\"
name=\"$file\" type=\"application/x-shockwave-flash\"
pluginspage=\"http://www.macromedia.com/go/getflashplayer\" />
</object>";
$ct = '';
}
# If still non processed, treat request as local filename to return
else {
logAdd "$title($ct) requested";
$article = readFile $title;
}
# Render article
unless ( $ct ) {
$ct = 'text/html';
my $tmp = $::template;
$tmp =~ s/<!-- title -->/$title/g;
$tmp =~ s/<!-- content -->/ wikiParse $article /e;
$article = $tmp;
}
}
else { $http = "401 Authorization Required\r\nWWW-Authenticate: Basic realm=\"private\"" }
# Send response back to requestor
if ( $respond ) {
$headers = "$ver $http\r\nDate: $date\r\nServer: $::daemon\r\n$headers";
$headers .= "Content-Type: $ct\r\n";
$headers .= "Connection: close\r\n";
$headers .= "Content-Length: ".(length $article)."\r\n";
print $handle "$headers\r\n$article";
}
}