Click to See Complete Forum and Search --> : Webserver


bubblenut
02-06-2004, 05:39 AM
Yea, I know, not really practilcle but what the hell. I have a couple of changes which should be made to it. Rather than storing the number of threads in a file (lots of head movements with all that reading an writing) could use environment variables like so (exec("THREADCOUNT=".$threadcount);), also have another variable, set to true just after the line $sock->socket=socket_accept($sock), to tell the webserver class whether or not to start another thread as at the moment this will open up the full $MAX_THREADS regardless of whether anyone is waiting to be served.
There was another thing I thought of last night but I can't remember it now. I'm sure there are a lot of holes to be found though.
here goes Webserver.php
<?php
class Webserver {
var $MAX_THREADS=10;
var $THREAD_C_FILE='./threadcount';
var $ERRFILE='./webserver_err';

var $port=80;

function Webserver($port=false)
{
//check provided port number is a num
if($port && $port%10==0) {
$this->port=$port;
}
//create a listener socket on port
$listen=socket_create_listen($port);
//initialize the thread count file
$fh=fopen($this->THREAD_C_FILE,'w');
fwrite($fh,'0');
fclose($fh);
//start the webserver
while(true) {
//check the number of open threads
$num=$this->getOpenThreads();
//if the number of open threads is low enough start another
if($num<$this->MAX_THREADS) {
//forkit!!
exec('/usr/bin/php -q ./Listener.php '.$this->port.' '.$this->THREAD_C_FILE.' &');
//keep tryin untill any lock is lifted
while(!$fh=fopen($this->THREAD_C_FILE,'w')) {}
fwrite($fh,++$num);
fclose($fh);
}
}
}

function getOpenThreads()
{
//keep trying untill any lock is lifted
while(!$fh=fopen($this->THREAD_C_FILE,'r')) {}
$num=fread($fh);
fclose($fh);
if(is_numeric($num)) {
return $num;
} else {
//FAIL
$this->ERR('BAD THREAD COUNT: '.$num."\n");
}
}

function ERR($string)
{
$fh=fopen($this->ERRFILE,'a+');
fwrite($fh,$string."\n");
fclose($fh);
exit(0);
}
}
$webserver=new Webserver();
?>

And That calls Listener.php

<?php

class Listener
{
var $THREAD_C_FILE;
var $ERRFILE='./listen_err';

var $port;
var $socket;
var $request;
var $error;


function Listener($port, $thread_c_file)
{
$this->setPort($port);
$this->setThreadCFile($thread_c_file);
$this->createListener();
if($this->getRequest()) {
$this->sendFile();
} else {
$this->sendError();
}
$this->decrementCount();
$this->close();
}

function close()
{
exit(0);
}

function decrementCount()
{
while(!$fh=fopen($this->THREAD_C_FILE,'w+')) {}
$num=fread($fh,filesize($this->THREAD_C_FILE));
if(!is_numeric($num)) {
$this->ERR("THREAD COUNT FILE FKUP:\n"); //not much else to say really :/
}
fwrite($fh,--$num);
fclose($fh);
}

function sendFile()
{
$fh=fopen($this->request,'r');
while($data=fread($fh,1024)) {
socket_write($this->socket,$data);
}
}

function sendError()
{
socket_write($this->socket,$this->error);
}

function getRequest()
{
while($data=socket_read($this->socket)) {
$request.=$data;
}
if(preg_match('/^GET \/?(\S*).*/',$request,$matches)) {
$request=$matches[1];
if(substr($request,-1)=='/' || $request=='')
$request.='index.php';
if(is_file($request)) {
$this->request=$request;
return true;
} else {
$this->error='404 Object Not Found\n';
return false;
}
} else {
$this->error='400 Bad Request\n';
return false;
}
}

function createListener()
{
if($sock=socket_create_listen($this->port)) {
$this->socket=socket_accept($sock);
return true;
} else {
$this->ERR("CREATE LISTENER !1: ".socket_strerror(socket_last_error()));
}
}

function setPort($port)
{
if(is_numeric($port)) {
$this->port=(int)$port;
return true;
}
//FAIL
$this->ERR("BAD PORT NO.: ".$port);
}

function setThreadCFile($thread_c_file)
{
if(is_file($thread_c_file)) {
if(is_readable($thread_c_file)) {
$this->THREAD_C_FILE=$thread_c_file;
return true;
}
}
//FAIL
$this->ERR("BAD THREAD_C_FILE: ".$thread_c_file);
}

function ERR($string)
{
$fh=fopen($this->ERRFILE,'a+');
fwrite($fh,$string."\n");
fclose($fh);
exit(0);
}
}
$listener=new Listener($argv[1],$argv[2]);
?>

Cheers Guys
Bubble

bbaassiri
02-06-2004, 04:00 PM
2 small things as for code perpective; your check for valid port number does not include valid range 1-65536. As for you opening if file handles you should really check for error conditions cause your code will happily execute with an invalid file handle

my 2 cents

OhLordy
02-06-2004, 08:31 PM
I'm at home now and forgot my bubblenut password so I'm using an old account.
Thanks for the input bbaassiri I didn't know about port number limits so that's usefull to know even outside this project. With regards to the checking file handles do you just mean in the Webserver constructor or for the fopens in the while loops as well? I was thinking about putting a max limit on the number of times it could try fopen on the file but haven't got around to it. Sorted things out a little now, I had to put all the data (number of open threads and whether or not to open a new thread, used to restrict Webserver) in a file becasue found that if I set environment variables as I said before then they didn't seem to last through to when I needed them, they seemed to disapeat straight after the shell_exec().
Git it up and running and tried to access it with http://localhost:1234/index.php (I'm using port 1234 obviously) but it's not working in the slightest yet.
I'll keep you posted. Any more thoughts would be greatly apreciated.
Bubble

Moonglobe
02-06-2004, 09:52 PM
isn't having multiple accounts a no-no arround here?

OhLordy
02-06-2004, 10:38 PM
dunno ... didn't know; I can happily have the OhLordy account deleted as of Monday when I'll be able to get the password for my bubblenut account so I can bring it home.