Sockd ServerZeus Server provides a high-performance CGI replacement which allows dynamic documents to be provided to its clients. The dynamic document API utilizes a TCP/IP socket interface to allow the document generation code to be implemented on a separate machine to facilitate load-balancing, and also makes no restrictions on the implementation language. The dynamic document generation code, called sockd here in, runs as an external daemon to the server and listens for the request from the server to generate a page. The server then passes information, similar to the environment setup for a CGI process, to the sockd server, which can then use this information to determine the data for the page to generate. This information is then routed back through the server down to the client. This offers significant performance increases over frequently forking a large CGI script. Simple ExampleHere is an example of a sockd server. This is a simple page-counter implemented in Perl which demonstrates how to interact with the API. It is intended to interact with the non-forking CGI API of Zeus Server. CGI daemon protocol: See specifications for more information. Input: ENVIRONMENT\r\n DATA\r\n\r\n Where: ENVIRONMENT = (LINE\r\n)+ LINE = environment variable line (eg. HTTP_USER_AGENT=ZeusBrowser) DATA = data supplied to POST, or arguments supplied to GET ENVIRONMENT contains all of sames fields that are set in the environment of a normal CGI program. Output: [as normal CGI] Presented here is a small example of a CGId daemon (herein called sockd) capable of interacting with the Zeus Server non-forking CGI API. Upon startup, the sockd requires a port number to run on. On recieving a complete request, the sockd opens up a counter file, increments the value in there, and returns a page to the user displaying the counter value. The webserver should be configured to map requests to this sockd server.
#!/usr/bin/perl
require 5.001;
use Socket;
use POSIX;
# complete(string)
# Determines whether a complete request has been recieved yet. Returns
# non-zero if so.
sub complete { return ($_[0] =~ /\r\n\r\n.*\r\n\r\n/) }
# process(string)
# Takes a complete request, and returns the data to output.
# Trivial example simply opens up the counter file specified in the request,
# increments it, and returns a page containing the value.
sub process {
my $buff = $_[0];
my $environ, $data;
my $countdir = "/tmp/"; # Directory where counter files are stored
my $num;
$buff =~ /\r\n\r\n/;
$environ = $`;
$data = $';
substr( $data, -4, 4, '' ); # eat \r\n\r\n
if (! -f "$countdir/$data")
{
open(NUM,">$countdir/$data");
close(NUM);
}
open(NUM,"+<$countdir/$data");
$num = <NUM>;
$num++;
seek(NUM,0,0);
print NUM "$num\n";
close NUM;
return
join( '' ,
"Content-Type: text/html\r\n\r\n" ,
"<body bgcolor=#ffffff><h3>" ,
"You've seen this page $num times before!" ,
"<p><hr noshade size=2>" ,
"<a href=\"http://www.zeus.co.uk/products/server/\">" ,
"<center><font size=7>P<i>owered</i> B<i>y</i> Z<i>eus</i> " ,
"S<i>erver</i></a>" );
}
# ----------------------------------------------------------------------------
# Server code
if( $#ARGV!=0 )
{
print "Usage: sockd port\n";
exit 1;
}
$port = shift;
my $proto = getprotobyname('tcp');
socket(SERVER, PF_INET, SOCK_STREAM, $proto) || die "socket: $!";
setsockopt(SERVER, SOL_SOCKET, SO_REUSEADDR, 1) || die "setsockopt: $!";
$sockaddr = 'S n a4 x8';
$this = pack($sockaddr, AF_INET, $port, "\0\0\0\0");
bind(SERVER, $this) || die "bind: $!";
listen(SERVER,5) || die "listen: $!";
print "CGId server started on port $port\n";
$child = fork;
if ($child < 0) { # fork failed
die "error on fork: $!";
} elsif ($child > 0) { # parent
exit(0);
} else { # child
if (!setsid()) {
die "failed setsid: $!";
}
}
$rin = ''; vec($rin,fileno(SERVER),1) = 1;
$win = '';
$ein = '';
# Main program loop
while(1) {
$nfound = select($rout=$rin,$wout=$win,$eout=$ein, undef);
if(vec($rout,fileno(SERVER),1)) { # new connection
my $NS = FileHandle->new();
if(accept($NS,SERVER)) {
vec($rin,fileno($NS),1) = 1;
vec($ein,fileno($NS),1) = 1;
$input[fileno($NS)] = $output[fileno($NS)] = '';
$fd2file[fileno($NS)] = $NS;
push ((@clients),fileno($NS));
}
}
else {
# look through read/write/except bits for clients
@cl = @clients;
while($fd = shift @cl) {
if(vec($eout,$fd,1)) { # Exception on $fd
&killclient($fd);
}
if(vec($wout,$fd,1)) { # Write on $fd
local($len) = length($output[$fd]);
if($len) { # data left to write
$w=syswrite $fd2file[$fd],$output[$fd],$len;
if(!defined($w)) { &killclient($fd); }
else { $output[$fd] = substr($output[$fd],$w,$len-$w); }
}
else { &killclient($fd); } # finished
}
if(vec($rout,$fd,1)) { # Read on $fd
$r=sysread $fd2file[$fd],$input[$fd],128,length($input[$fd]);
if(!defined($r) || !$r) { &killclient($fd); }
else { &parse($fd); }
}
}
}
}
# Removes a client of given fd from the system
sub killclient {
local($fd) = $_[0];
local(@cl);
local($i);
vec($rin,$fd,1) = 0;
vec($win,$fd,1) = 0;
vec($ein,$fd,1) = 0;
$input[$fd] = $output[$fd] = '';
close $fd2file[$fd];
while($i = shift @clients) {
if($i != $fd) { push ((@cl),$i); }
}
@clients = @cl;
}
# Determines whether a complete request on an fd has been recieved, and if so
# processes it
sub parse {
local($fd) = $_[0];
if(&complete($input[$fd])) { # got a complete request
vec($rin,$fd,1) = 0;
vec($win,$fd,1) = 1;
$output[$fd] = &process($input[$fd]);
}
}
FILEHANDLE:
{
use strict;
require FileHandle; # make sure real one is loaded
package FileHandle;
sub new {
my $self = shift;
my $class = ref($self) || $self; # for inheritance
return bless(&_genfh, $class);
}
sub DESTROY {
my $self = shift;
if (defined fileno $self) {
close $self;
}
}
########
# internal only
########
sub _genfh {
no strict 'refs';
local *{'FileHandle::DEMI_ANON_GLOB'};
return \delete $FileHandle::{DEMI_ANON_GLOB};
}
1;
}
Content Manager
[Administrator] 16 December 2005
|
Recent Articles
Other Resources
|


