--- a/webserver.php Mon Mar 31 07:40:30 2008 -0400
+++ b/webserver.php Wed Apr 02 00:23:51 2008 -0400
@@ -95,6 +95,21 @@
var $allow_dir_list = false;
/**
+ * Switch to control forking support.
+ * @var bool
+ */
+
+ var $allow_fork = true;
+
+ /**
+ * Keep-alive support uses this to track what the client requested.
+ * Only used if $allow_fork is set to true.
+ * @var bool
+ */
+
+ var $in_keepalive = false;
+
+ /**
* Constructor.
* @param string IPv4 address to bind to
* @param int Port number
@@ -130,8 +145,12 @@
function __destruct()
{
- status('WebServer: destroying socket');
- @socket_close($this->sock);
+ if ( !defined('HTTPD_WS_CHILD') )
+ {
+ status('WebServer: destroying socket');
+ @socket_shutdown($this->sock, 2);
+ @socket_close($this->sock);
+ }
}
/**
@@ -142,24 +161,59 @@
{
while ( true )
{
+ // if this is a child process, we're finished - close up shop
+ if ( defined('HTTPD_WS_CHILD') && !$this->in_keepalive )
+ {
+ exit(0);
+ }
+
// wait for connection...
// trick from http://us.php.net/manual/en/function.socket-accept.php
- $remote = false;
- switch(@socket_select($r = array($this->sock), $w = array($this->sock), $e = array($this->sock), 5)) {
- case 2:
- break;
- case 1:
- $remote = @socket_accept($this->sock);
- break;
- case 0:
- break;
+ if ( !defined('HTTPD_WS_CHILD') )
+ {
+ $remote = false;
+ $timeout = 5;
+ switch(@socket_select($r = array($this->sock), $w = array($this->sock), $e = array($this->sock), $timeout)) {
+ case 2:
+ break;
+ case 1:
+ $remote = @socket_accept($this->sock);
+ break;
+ case 0:
+ break;
+ }
}
if ( !$remote )
{
+ $this->in_keepalive = false;
continue;
}
+ // fork off if possible
+ if ( function_exists('pcntl_fork') && $this->allow_fork && !$this->in_keepalive )
+ {
+ $pid = pcntl_fork();
+ if ( $pid == -1 )
+ {
+ // do nothing; continue responding to request in single-threaded mode
+ }
+ else if ( $pid )
+ {
+ // we are the parent, continue listening
+ $remote = false;
+ continue;
+ }
+ else
+ {
+ // this is the child
+ define('HTTPD_WS_CHILD', 1);
+ $this->sock = false;
+ }
+ }
+
+ $this->in_keepalive = false;
+
// read request
$last_line = '';
$client_headers = '';
@@ -198,6 +252,12 @@
$_SERVER[$key] = $match[2];
}
+ // enable keep-alive if requested
+ if ( isset($_SERVER['HTTP_CONNECTION']) && defined('HTTPD_WS_CHILD') )
+ {
+ $this->in_keepalive = ( $_SERVER['HTTP_CONNECTION'] === 'keep-alive' );
+ }
+
if ( isset($_SERVER['HTTP_AUTHORIZATION']) )
{
$data = $_SERVER['HTTP_AUTHORIZATION'];
@@ -285,7 +345,19 @@
$this->send_standard_response($remote, $handler, $uri, $params);
- @socket_close($remote);
+ if ( !$this->in_keepalive )
+ {
+ // if ( defined('HTTPD_WS_CHILD') )
+ // status('Closing connection');
+ @socket_close($remote);
+ exit(0);
+ }
+ else
+ {
+ // if ( defined('HTTPD_WS_CHILD') )
+ // status('Continuing connection');
+ @socket_write($remote, "\r\n\r\n");
+ }
}
}
@@ -302,15 +374,17 @@
global $http_responses;
$reason_code = ( isset($http_responses[$http_code]) ) ? $http_responses[$http_code] : 'Unknown';
+ $_SERVER['HTTP_USER_AGENT'] = ( isset($_SERVER['HTTP_USER_AGENT']) ) ? $_SERVER['HTTP_USER_AGENT'] : '(no user agent)';
status("{$_SERVER['REMOTE_ADDR']} {$_SERVER['REQUEST_METHOD']} {$_SERVER['REQUEST_URI']} $http_code {$_SERVER['HTTP_USER_AGENT']}");
$headers = str_replace("\r\n", "\n", $headers);
$headers = str_replace("\n", "\r\n", $headers);
$headers = preg_replace("#[\r\n]+$#", '', $headers);
+ $connection = ( $this->in_keepalive ) ? 'keep-alive' : 'close';
@socket_write($socket, "HTTP/1.1 $http_code $reason_code\r\n");
@socket_write($socket, "Server: $this->server_string");
- @socket_write($socket, "Connection: close\r\n");
+ @socket_write($socket, "Connection: $connection\r\n");
@socket_write($socket, "Content-Type: $contenttype\r\n");
if ( !empty($headers) )
{
@@ -518,6 +592,7 @@
return true;
}
+ $this->header("Content-length: " . strlen($output));
$headers = implode("\r\n", $this->response_headers);
// write headers