webserver.php
changeset 15 2adca0f363fd
parent 13 b5db2345c397
child 16 23d4cf2f183b
equal deleted inserted replaced
14:7a1573676cc4 15:2adca0f363fd
    91    * Switch to control if directory listing is enabled
    91    * Switch to control if directory listing is enabled
    92    * @var bool
    92    * @var bool
    93    */
    93    */
    94   
    94   
    95   var $allow_dir_list = false;
    95   var $allow_dir_list = false;
       
    96   
       
    97   /**
       
    98    * Switch to control forking support.
       
    99    * @var bool
       
   100    */
       
   101   
       
   102   var $allow_fork = true;
       
   103   
       
   104   /**
       
   105    * Keep-alive support uses this to track what the client requested.
       
   106    * Only used if $allow_fork is set to true.
       
   107    * @var bool
       
   108    */
       
   109   
       
   110   var $in_keepalive = false;
    96   
   111   
    97   /**
   112   /**
    98    * Constructor.
   113    * Constructor.
    99    * @param string IPv4 address to bind to
   114    * @param string IPv4 address to bind to
   100    * @param int Port number
   115    * @param int Port number
   128    * Destructor.
   143    * Destructor.
   129    */
   144    */
   130   
   145   
   131   function __destruct()
   146   function __destruct()
   132   {
   147   {
   133     status('WebServer: destroying socket');
   148     if ( !defined('HTTPD_WS_CHILD') )
   134     @socket_close($this->sock);
   149     {
       
   150       status('WebServer: destroying socket');
       
   151       @socket_shutdown($this->sock, 2);
       
   152       @socket_close($this->sock);
       
   153     }
   135   }
   154   }
   136   
   155   
   137   /**
   156   /**
   138    * Main server loop
   157    * Main server loop
   139    */
   158    */
   140   
   159   
   141   function serve()
   160   function serve()
   142   {
   161   {
   143     while ( true )
   162     while ( true )
   144     {
   163     {
       
   164       // if this is a child process, we're finished - close up shop
       
   165       if ( defined('HTTPD_WS_CHILD') && !$this->in_keepalive )
       
   166       {
       
   167         exit(0);
       
   168       }
       
   169       
   145       // wait for connection...
   170       // wait for connection...
   146       // trick from http://us.php.net/manual/en/function.socket-accept.php
   171       // trick from http://us.php.net/manual/en/function.socket-accept.php
   147       $remote = false;
   172       if ( !defined('HTTPD_WS_CHILD') )
   148       switch(@socket_select($r = array($this->sock), $w = array($this->sock), $e = array($this->sock), 5)) {
   173       {
   149         case 2:
   174         $remote = false;
   150           break;
   175         $timeout = 5;
   151         case 1:
   176         switch(@socket_select($r = array($this->sock), $w = array($this->sock), $e = array($this->sock), $timeout)) {
   152           $remote = @socket_accept($this->sock);
   177           case 2:
   153           break;
   178             break;
   154         case 0:
   179           case 1:
   155           break;
   180             $remote = @socket_accept($this->sock);
       
   181             break;
       
   182           case 0:
       
   183             break;
       
   184         }
   156       }
   185       }
   157          
   186          
   158       if ( !$remote )
   187       if ( !$remote )
   159       {
   188       {
       
   189         $this->in_keepalive = false;
   160         continue;
   190         continue;
   161       }
   191       }
       
   192       
       
   193       // fork off if possible
       
   194       if ( function_exists('pcntl_fork') && $this->allow_fork && !$this->in_keepalive )
       
   195       {
       
   196         $pid = pcntl_fork();
       
   197         if ( $pid == -1 )
       
   198         {
       
   199           // do nothing; continue responding to request in single-threaded mode
       
   200         }
       
   201         else if ( $pid )
       
   202         {
       
   203           // we are the parent, continue listening
       
   204           $remote = false;
       
   205           continue;
       
   206         }
       
   207         else
       
   208         {
       
   209           // this is the child
       
   210           define('HTTPD_WS_CHILD', 1);
       
   211           $this->sock = false;
       
   212         }
       
   213       }
       
   214       
       
   215       $this->in_keepalive = false;
   162       
   216       
   163       // read request
   217       // read request
   164       $last_line = '';
   218       $last_line = '';
   165       $client_headers = '';
   219       $client_headers = '';
   166       while ( $line = @socket_read($remote, 1024, PHP_NORMAL_READ) )
   220       while ( $line = @socket_read($remote, 1024, PHP_NORMAL_READ) )
   196           continue;
   250           continue;
   197         $key = 'HTTP_' . strtoupper(str_replace('-', '_', $match[1]));
   251         $key = 'HTTP_' . strtoupper(str_replace('-', '_', $match[1]));
   198         $_SERVER[$key] = $match[2];
   252         $_SERVER[$key] = $match[2];
   199       }
   253       }
   200       
   254       
       
   255       // enable keep-alive if requested
       
   256       if ( isset($_SERVER['HTTP_CONNECTION']) && defined('HTTPD_WS_CHILD') )
       
   257       {
       
   258         $this->in_keepalive = ( $_SERVER['HTTP_CONNECTION'] === 'keep-alive' );
       
   259       }
       
   260       
   201       if ( isset($_SERVER['HTTP_AUTHORIZATION']) )
   261       if ( isset($_SERVER['HTTP_AUTHORIZATION']) )
   202       {
   262       {
   203         $data = $_SERVER['HTTP_AUTHORIZATION'];
   263         $data = $_SERVER['HTTP_AUTHORIZATION'];
   204         $data = substr(strstr($data, ' '), 1);
   264         $data = substr(strstr($data, ' '), 1);
   205         $data = base64_decode($data);
   265         $data = base64_decode($data);
   283         continue;
   343         continue;
   284       }
   344       }
   285       
   345       
   286       $this->send_standard_response($remote, $handler, $uri, $params);
   346       $this->send_standard_response($remote, $handler, $uri, $params);
   287       
   347       
   288       @socket_close($remote);
   348       if ( !$this->in_keepalive )
       
   349       {
       
   350         // if ( defined('HTTPD_WS_CHILD') )
       
   351         //   status('Closing connection');
       
   352         @socket_close($remote);
       
   353         exit(0);
       
   354       }
       
   355       else
       
   356       {
       
   357         // if ( defined('HTTPD_WS_CHILD') )
       
   358         //   status('Continuing connection');
       
   359         @socket_write($remote, "\r\n\r\n");
       
   360       }
   289     }
   361     }
   290   }
   362   }
   291   
   363   
   292   /**
   364   /**
   293    * Sends the client appropriate response headers.
   365    * Sends the client appropriate response headers.
   300   function send_client_headers($socket, $http_code = 200, $contenttype = 'text/html', $headers = '')
   372   function send_client_headers($socket, $http_code = 200, $contenttype = 'text/html', $headers = '')
   301   {
   373   {
   302     global $http_responses;
   374     global $http_responses;
   303     $reason_code = ( isset($http_responses[$http_code]) ) ? $http_responses[$http_code] : 'Unknown';
   375     $reason_code = ( isset($http_responses[$http_code]) ) ? $http_responses[$http_code] : 'Unknown';
   304     
   376     
       
   377     $_SERVER['HTTP_USER_AGENT'] = ( isset($_SERVER['HTTP_USER_AGENT']) ) ? $_SERVER['HTTP_USER_AGENT'] : '(no user agent)';
   305     status("{$_SERVER['REMOTE_ADDR']} {$_SERVER['REQUEST_METHOD']} {$_SERVER['REQUEST_URI']} $http_code {$_SERVER['HTTP_USER_AGENT']}");
   378     status("{$_SERVER['REMOTE_ADDR']} {$_SERVER['REQUEST_METHOD']} {$_SERVER['REQUEST_URI']} $http_code {$_SERVER['HTTP_USER_AGENT']}");
   306     
   379     
   307     $headers = str_replace("\r\n", "\n", $headers);
   380     $headers = str_replace("\r\n", "\n", $headers);
   308     $headers = str_replace("\n", "\r\n", $headers);
   381     $headers = str_replace("\n", "\r\n", $headers);
   309     $headers = preg_replace("#[\r\n]+$#", '', $headers);
   382     $headers = preg_replace("#[\r\n]+$#", '', $headers);
       
   383     $connection = ( $this->in_keepalive ) ? 'keep-alive' : 'close';
   310     
   384     
   311     @socket_write($socket, "HTTP/1.1 $http_code $reason_code\r\n");
   385     @socket_write($socket, "HTTP/1.1 $http_code $reason_code\r\n");
   312     @socket_write($socket, "Server: $this->server_string");
   386     @socket_write($socket, "Server: $this->server_string");
   313     @socket_write($socket, "Connection: close\r\n");
   387     @socket_write($socket, "Connection: $connection\r\n");
   314     @socket_write($socket, "Content-Type: $contenttype\r\n");
   388     @socket_write($socket, "Content-Type: $contenttype\r\n");
   315     if ( !empty($headers) )
   389     if ( !empty($headers) )
   316     {
   390     {
   317       @socket_write($socket, "$headers\r\n");
   391       @socket_write($socket, "$headers\r\n");
   318     }
   392     }
   516         if ( $output == '__break__' )
   590         if ( $output == '__break__' )
   517         {
   591         {
   518           return true;
   592           return true;
   519         }
   593         }
   520         
   594         
       
   595         $this->header("Content-length: " . strlen($output));
   521         $headers = implode("\r\n", $this->response_headers);
   596         $headers = implode("\r\n", $this->response_headers);
   522         
   597         
   523         // write headers
   598         // write headers
   524         $this->send_client_headers($socket, $this->response_code, $this->content_type, $headers);
   599         $this->send_client_headers($socket, $this->response_code, $this->content_type, $headers);
   525         
   600