diff -r 7a1573676cc4 -r 2adca0f363fd webserver.php --- 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