root > WikiSense-trunk > common > FifoServer.php

FifoServer.php

application/x-php, 9270 bytes (load raw)
<?php

if (!defined('WS_WEB') && !@$_SERVER['REQUEST_URI'] && @$argv[1]==='--test') {
    define('WS_CONSOLE',1);
    define('FIFO_TEST',1);
}

require_once("WSInit.php");

if (!function_exists('mkfifo')) {
    function mkfifo($name, $mode = NULL) {
        if (function_exists('posix_mkfifo')) {
            if ($mode===NULL) $mode= '644';
            return posix_mkfifo($name, $mode);
        }
   
        system('mkfifo '.escapeshellarg($name),$code);
       
        if ($code) {
            wsfLog("mkfifo failed for $name: code $code",LL_ERROR);
            return false;
        }
        else {
            if ($mode) chmod($name,$mode);
       
            wsfLog("created fifo $name",LL_DEBUG);
            return true;
        }
    }
}

class FifoResponse {
    var $buffer;
    var $inHeader;
   
    function FifoResponse( $buffer ) {
        $this->buffer= $buffer;
        $this->inHeader= true;
    }

    function header($h, $override=true, $code=NULL) {
        if (!$this->inHeader) {
            trigger_error('Header already sent!',E_USER_ERROR);
            return false;
        }
   
        if ($code) fwrite($this->buffer, "Status: $code\r\n");
       
        $h= preg_replace('!^HTTP/\d\.\d\s+!','Status: ',$h);
        fwrite($this->buffer, $h . "\r\n");
       
        wsfLog("header: $h",LL_DEBUG);
        return true;
    }
   
    function write($data) {
        if ($this->inHeader) {
            fwrite($this->buffer, "\r\n");
            $this->flush($this->buffer);
           
            $this->inHeader= false;
        }
       
        if ($data==='' || $data===NULL || $data===false) return;
       
        fwrite($this->buffer, $data);
    }
   
    function flush() {
        fflush($this->buffer);
    }

}

class FifoServer {
   
    var $sessionBufferSize;
    var $sessionBuffers;
    var $fifo;
   
    function FifoServer( $fifo ) {
        $this->fifo= $fifo;
        $this->sessionBufferSize= 16;
        $this->sessionBuffers= array();
    }
   
    function handleRequest( $req, &$response ) {
        $response->header("Status: 200 OK",true,200);
       
        foreach ($req as $k => $v) {
            $response->write("$k: $v\n");
        }
    }
   
    function handleSession( $req ) {
        $this->inHeader= true;
       
        if (!is_array($req)) {
            $r= array();
       
            $req= explode('&',$req);
           
            foreach ($req as $s) {
                $s= trim($s);
                $s= explode('=',$s,2);
                if (!$s) continue;
               
                $k= strtolower(urldecode($s[0]));
                $v= ( sizeof($s) > 1 ? urldecode($s[1]) : true );
       
                $r[$k]= $v;   
            }
           
            $req= $r;
        }
               
        $sessionBuffer= @$req['buffer'];
       
        if (!$sessionBuffer) {
            wsfLog("missing buffer field in request!",LL_WARN);
            return false;
        }
       
        $connection= @$req['connection'];
        if (!$connection) $connection= 'close';
        else $connection= strtolower($connection);
       
        $bufftype= @$req['buffer-type'];
        if (!$bufftype) $bufftype= 'fifo';
       
        $separator= NULL;
        if ($connection==='keep-alive') {
            $separator= @$req['separator'];
            if (!$separator) $separator= "======== $sessionBuffer ========";
        }
       
        if ($bufftype=='fifo') {
            if (!file_exists($sessionBuffer)) {
                $ok= mkfifo($sessionBuffer,0644); #FIXME: mode!
               
                if (!$ok) {
                    wsfLog("unable to create session-fifo $sessionBuffer",LL_WARN);
                    return false;
                }
                else {
                    wsfLog("created session-fifo $sessionBuffer",LL_DEBUG);
                }
            }
            else {
                if (is_file($sessionBuffer) || is_dir($sessionBuffer) || is_link($sessionBuffer)) {
                    wsfLog("not a fifo: $sessionBuffer",LL_WARN);
                    return false;
                }
                else {
                    wsfLog("re-using session-fifo $sessionBuffer",LL_DEBUG);
                }
            }
        }
        else if (file_exists($sessionBuffer)) {
            if (!is_file($sessionBuffer)) {
                wsfLog("not a file: $sessionBuffer",LL_WARN);
                return false;
            }
            else {
                $sz= filesize($sessionBuffer);
                if ($sz) {
                    wsfLog("not empty ($sz bytes): $sessionBuffer",LL_WARN);
                    return false;
                }
                else {
                    wsfLog("using existing session-buffer file $sessionBuffer",LL_DEBUG);
                }
            }
        }
        else {
            wsfLog("using new session-buffer file $sessionBuffer",LL_DEBUG);
        }
       
        $buffer= @$this->sessionBuffers[$sessionBuffer];
       
        if ($buffer) {
            if (feof($buffer)) $buffer= NULL;
            else {
                wsfLog("using open handle for session-buffer $sessionBuffer",LL_DEBUG);
               
                #move to top of cache
                unset($this->sessionBuffers[$sessionBuffer]);
                $this->sessionBuffers[$sessionBuffer]= $buffer;
            }
        }
       
        if (!$buffer) {
            $buffer= fopen($sessionBuffer, 'w');
            if (!$buffer) {
                wsfLog("unable to open session-buffer $sessionBuffer for writing",LL_WARN);
                return false;
            }
            else wsfLog("open handle for session-buffer $sessionBuffer",LL_DEBUG);
        }
       
        @touch($sessionBuffer);
       
        $response= new FifoResponse( $buffer );
       
        $method = @$req['method'];
       
        if ($method==='open' || $method==='ping' || $method==='close') {
            wsfLog("received close signal for $sessionBuffer",LL_VERBOSE);
           
            if ($method==='close') {
                if ($connection!='destroy') $connection= 'close';
            }
           
            $response->header("Status: 200 OK",true,200,$buffer);
        }
        else {
            $this->handleRequest($req, $response);
        }
       
        wsfLog("response complete for $sessionBuffer",LL_VERBOSE);
           
        if ($connection=='keep-alive' && !$separator) $separator= 'close';
       
        if ($connection=='keep-alive') {
            fwrite($buffer,"\r\n$separator\r\n");
            fflush($buffer);
           
            while (sizeof($this->sessionBuffers) > ($this->sessionBufferSize - 1)) {
                $k= array_keys($this->sessionBuffers);
                $k= $k[0];
               
                $v= $this->sessionBuffers[$k];
                @ fclose($v); #FIXME: delete if stale...
                unset($this->sessionBuffers[$k]);
            }
           
            $this->sessionBuffers[$sessionBuffer]= $buffer;
            wsfLog("keeping session-buffer $sessionBuffer in keep-alive buffer",LL_DEBUG);
        }
        else {
            fflush($buffer);
            fclose($buffer);
            unset($this->sessionBuffers[$sessionBuffer]);
            wsfLog("closed session-buffer $sessionBuffer",LL_DEBUG);
        }
       
        if ($connection=='destroy') {
            unlink($sessionBuffer);
            wsfLog("deleted session-buffer $sessionBuffer",LL_VERBOSE);
        }
       
        return true;
    }

    function runServer() {
        if (!file_exists($this->fifo)) {
            $ok= mkfifo($this->fifo,0622); #FIXME: mode!
           
            if (!$ok) {
                wsfLog("unable to create server-fifo $this->fifo",LL_WARN);
                return false;
            }
            else {
                wsfLog("created server-fifo $this->fifo",LL_INFO);
            }
        }
       
        $fifo= NULL;
       
        while (true) {
            wsfLog("Waiting for request...",LL_DEBUG);
           
            $s= ( $fifo===NULL ? NULL : fgets($fifo) );
            if ($s===NULL || $s===false) {
                #got eof. on some systems, this happens whenever the client closes the fifo.
                #so, re-open it.
               
                if ($fifo && !feof($fifo)) continue;
                if ($fifo) fclose($fifo);
               
                wsfLog("Opening fifo {$this->fifo} (waiting for request)...", $fifo==NULL ? LL_INFO : LL_DEBUG);
                $fifo= fopen($this->fifo, 'r');
               
                if (!$fifo) {
                    wsfLog("unable to open server-fifo $this->fifo for reading",LL_ERROR);
                    return false;
                }
                else wsfLog("opened server-fifo $this->fifo",LL_DEBUG);
            }
           
            $s= trim($s);
            if ($s==='') continue;
           
            $this->handleSession($s);
        }
       
        @ fclose($fifo);
       
        wsfLog("Server terminated.",LL_INFO);
    }
}

if (defined('FIFO_TEST')) {
    $wsgLogLevel= LL_DEBUG;
    $server= new FifoServer($args[0]);
    $server->runServer();
}
?>