nanoserv
[ class tree: nanoserv ] [ index: nanoserv ] [ all elements ]

Source for file NS_HTTP_Service_Handler.php

Documentation is available at NS_HTTP_Service_Handler.php

  1. <?php
  2.  
  3. /**
  4.  *
  5.  * nanoserv handlers - HTTP service handler
  6.  * 
  7.  * Copyright (C) 2004-2010 Vincent Negrier aka. sIX <six@aegis-corp.org>
  8.  * 
  9.  * This library is free software; you can redistribute it and/or
  10.  * modify it under the terms of the GNU Lesser General Public
  11.  * License as published by the Free Software Foundation; either
  12.  * version 2.1 of the License, or (at your option) any later version.
  13.  * 
  14.  * This library is distributed in the hope that it will be useful,
  15.  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  16.  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  17.  * Lesser General Public License for more details.
  18.  * 
  19.  * You should have received a copy of the GNU Lesser General Public
  20.  * License along with this library; if not, write to the Free Software
  21.  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA *
  22.  *
  23.  * @package nanoserv
  24.  * @subpackage Handlers
  25.  */
  26.  
  27. /**
  28.  * Require the nanoserv core
  29.  */
  30. require_once "nanoserv-compat/nanoserv.php";
  31.  
  32. /**
  33.  * HTTP Service handler class
  34.  *
  35.  * @package nanoserv
  36.  * @subpackage Handlers
  37.  */
  38. abstract class NS_HTTP_Service_Handler extends NS_Connection_Handler {
  39.  
  40.     /**
  41.      * Server string
  42.      */
  43.     const SERVER_STRING = "";
  44.  
  45.     /**
  46.      * Max request length
  47.      */
  48.     const MAX_REQUEST_LENGTH = 1048576;
  49.     
  50.     /**
  51.      * Default content type
  52.      */
  53.     const DEFAULT_CONTENT_TYPE = "text/html";
  54.     
  55.     /**#@+
  56.      * Compression options
  57.      * @var int
  58.      */
  59.     const COMPRESS_AUTO = 1;
  60.     const COMPRESS_OFF = 2;
  61.     /**#@-*/
  62.     
  63.     /**
  64.      * Response status codes and strings
  65.      */
  66.     private $STATUS_CODES array(100 => "100 Continue",
  67.             200 => "OK",
  68.             201 => "Created",
  69.             204 => "No Content",
  70.             206 => "Partial Content",
  71.             300 => "Multiple Choices",
  72.             301 => "Moved Permanently",
  73.             302 => "Found",
  74.             303 => "See Other",
  75.             304 => "Not Modified",
  76.             307 => "Temporary Redirect",
  77.             400 => "Bad Request",
  78.             401 => "Unauthorized",
  79.             403 => "Forbidden",
  80.             404 => "Not Found",
  81.             405 => "Method Not Allowed",
  82.             406 => "Not Acceptable",
  83.             408 => "Request Timeout",
  84.             410 => "Gone",
  85.             413 => "Request Entity Too Large",
  86.             414 => "Request URI Too Long",
  87.             415 => "Unsupported Media Type",
  88.             416 => "Requested Range Not Satisfiable",
  89.             417 => "Expectation Failed",
  90.             500 => "Internal Server Error",
  91.             501 => "Method Not Implemented",
  92.             503 => "Service Unavailable",
  93.             506 => "Variant Also Negotiates");
  94.     
  95.     /**
  96.      * Request headers
  97.      * @var array 
  98.      */
  99.     protected $request_headers = array();
  100.     
  101.     /**
  102.      * Request method
  103.      * @var string 
  104.      */
  105.     protected $request_method = "";
  106.     
  107.     /**
  108.      * Request protocol
  109.      * @var string 
  110.      */
  111.     protected $request_protocol = "";
  112.     
  113.     /**
  114.      * Request content (raw POST data)
  115.      * @var string 
  116.      */
  117.     protected $request_content = "";
  118.     
  119.     /**
  120.      * Compress option
  121.      * @var int 
  122.      */
  123.     protected $compress = self::COMPRESS_OFF;
  124.  
  125.     /**
  126.      * Compression level
  127.      * @var int 
  128.      */
  129.     protected $compress_level = 3;
  130.     
  131.     /**
  132.      * Request buffer
  133.      * @var string 
  134.      */
  135.     private $request_buffer "";
  136.     
  137.     /**
  138.      * Response headers
  139.      * @var array 
  140.      */
  141.     private $response_headers array();
  142.     
  143.     /**
  144.      * Response content type
  145.      * @var string 
  146.      */
  147.     private $response_content_type;
  148.     
  149.     /**
  150.      * Response status code
  151.      * @var int 
  152.      */
  153.     private $response_status 200;
  154.     
  155.     public function __construct({
  156.  
  157.         $this->response_content_type static::DEFAULT_CONTENT_TYPE;
  158.  
  159.     }
  160.     
  161.     protected function Handle_Request($url{
  162.  
  163.         $this->Send_Response($this->on_Request($url));
  164.     
  165.     }
  166.     
  167.     final public function on_Read($data{
  168.  
  169.         $this->request_buffer .= $data;
  170.  
  171.         while ($this->request_buffer{
  172.         
  173.             if (($p strpos($this->request_buffer"\r\n\r\n")) !== false{
  174.  
  175.                 $hdrs substr($this->request_buffer0$p);
  176.                 $cnt substr($this->request_buffer$p 4);
  177.             
  178.             else if (($p strpos($this->request_buffer"\n\n")) !== false{
  179.  
  180.                 $hdrs substr($this->request_buffer0$p);
  181.                 $cnt substr($this->request_buffer$p 2);
  182.             
  183.             else {
  184.  
  185.                 if (isset($this->request_buffer[static::MAX_REQUEST_LENGTH])) {
  186.  
  187.                     $this->Set_Response_Status(414);
  188.                     $this->Send_Response("Request too large");
  189.                 
  190.                 else {
  191.                 
  192.                     return;
  193.  
  194.                 }
  195.  
  196.             }
  197.  
  198.             // Extract first line
  199.  
  200.             list($this->request_method$url$this->request_protocolexplode(" "strtok($hdrs"\r\n"));
  201.  
  202.             // And process headers
  203.             
  204.             $hdrs explode("\n"trim(strtok("")));
  205.             $tmp array();
  206.  
  207.             foreach ($hdrs as $hdr{
  208.                 
  209.                 $k strtoupper(strtok($hdr":"));
  210.                 $tmp[$ktrim(strtok(""));
  211.             
  212.             }
  213.  
  214.             $this->request_headers = $tmp;
  215.  
  216.             if ((isset($this->request_headers["CONTENT-LENGTH"])) && ($cl $this->request_headers["CONTENT-LENGTH"])) {
  217.  
  218.                 if ($cl static::MAX_REQUEST_LENGTH{
  219.  
  220.                     $this->Set_Response_Status(413);
  221.                     $this->Send_Response("Request too large");
  222.                 
  223.                 else if (strlen($cnt$cl{
  224.                 
  225.                     return;
  226.  
  227.                 }
  228.  
  229.                 $this->request_content = substr($cnt0$cl);
  230.                 $this->request_buffer ltrim(substr($cnt$cl));
  231.  
  232.             else {
  233.             
  234.                 $this->request_content = "";
  235.                 $this->request_buffer $cnt;
  236.  
  237.             }
  238.  
  239.             if (($this->request_protocol !== "HTTP/1.0"&& ($this->request_protocol !== "HTTP/1.1")) {
  240.  
  241.                 $this->Set_Response_Status(400);
  242.                 $this->Send_Response("Bad Request");
  243.             
  244.             else {
  245.  
  246.                 $this->Handle_Request($url);
  247.  
  248.             }
  249.  
  250.         }
  251.     
  252.     }
  253.  
  254.     /**
  255.      * Event called on HTTP request
  256.      *
  257.      * the string returned by on_Request() will be sent back as the HTTP response
  258.      *
  259.      * @param string $url 
  260.      * @return string 
  261.      */
  262.     abstract public function on_Request($url);
  263.  
  264.     /**
  265. /**
  266.      * Add HTTP header to the response
  267.      *
  268.      * @param string $header 
  269.      */
  270.     public function Add_Header($header{
  271.  
  272.         $this->response_headers[$header;
  273.     
  274.     }
  275.     
  276.     /**
  277.      * Set response content type
  278.      *
  279.      * @param string $content_type 
  280.      */
  281.     public function Set_Content_Type($content_type{
  282.  
  283.         $this->response_content_type $content_type;
  284.  
  285.     }
  286.     
  287.     /**
  288.      * Set HTTP response status code
  289.      *
  290.      * 200 = OK, 403 = Forbidden, 404 = Not found, ...
  291.      *
  292.      * @param int $code 
  293.      */
  294.     public function Set_Response_Status($code 200{
  295.  
  296.         $this->response_status $code;
  297.     
  298.     }
  299.     
  300.     /**
  301.      * Set compression option
  302.      *
  303.      * @param int $option 
  304.      */
  305.     public function Set_Compression($opt self::COMPRESS_AUTO{
  306.  
  307.         switch ($opt{
  308.  
  309.             case self::COMPRESS_AUTO:    $this->compress extension_loaded("zlib"self::COMPRESS_AUTO self::COMPRESS_OFF;        break;
  310.             case self::COMPRESS_OFF:    $this->compress $opt;                                                                        break;
  311.             default:                    throw new NS_Exception("invalid compress option '{$opt}'");
  312.  
  313.         }
  314.     
  315.     }
  316.     
  317.     /**
  318.      * Compress a response if possible and needed
  319.      *
  320.      * @param string &$data 
  321.      * @param string &$encoding 
  322.      * @return bool 
  323.      */
  324.     protected function Compress_Response(&$data&$encoding NULL{
  325.  
  326.         $methods array(    "deflate"    => "gzdeflate",
  327.                             "gzip"        => "gzencode",
  328.                             "compress"    => "gzcompress");
  329.         
  330.         foreach ($methods as $m => $func{
  331.  
  332.             if (strpos($this->request_headers["ACCEPT-ENCODING"]$m!== false{
  333.  
  334.                 $method $m;
  335.                 break;
  336.  
  337.             }
  338.  
  339.         }
  340.     
  341.         if (!isset($method)) return false;
  342.  
  343.         $data $func($data$this->compress_level);
  344.         $encoding $method;
  345.  
  346.         return true;
  347.     
  348.     }
  349.     
  350.     /**
  351.      * Send HTTP response back to client
  352.      *
  353.      * This method is only invoked by the on_Read() handler
  354.      *
  355.      * @param string $data 
  356.      */
  357.     protected function Send_Response($data$length null{
  358.  
  359.         $keep = isset($this->request_headers["CONNECTION"](strtoupper($this->request_headers["CONNECTION"]=== "KEEP-ALIVE"false;
  360.         
  361.         $resp "HTTP/1.1 " . (int)$this->response_status " " $this->STATUS_CODES[$this->response_status"\r\n"
  362.               . "Date: " gmdate("D, d M Y H:i:s T""\r\n"
  363.               . "Server: " (static::SERVER_STRING static::SERVER_STRING "nanoserv/2.1.1-dev""\r\n"
  364.               . "Content-Type: " $this->response_content_type "\r\n"
  365.               . "Content-Length: " (isset($length$length strlen($data)) "\r\n"
  366.               . "Connection: " ($keep "Keep-Alive" "Close""\r\n";
  367.  
  368.         if (($this->compress !== self::COMPRESS_OFF&& $this->Compress_Response($data$encoding)) $resp .= "Content-Encoding: " $encoding "\r\n";
  369.         if ($this->response_headers$resp .= implode("\r\n"$this->response_headers"\r\n";
  370.  
  371.         $resp .= "\r\n" $data;
  372.         
  373.         $this->Write($resp($keep false array($this"Disconnect")));
  374.  
  375.         $this->response_content_type static::DEFAULT_CONTENT_TYPE;
  376.         $this->response_headers array();
  377.         $this->response_status 200;
  378.  
  379.     }
  380.  
  381. }
  382.  
  383. /**
  384.  * HTTP Asynchronous service handler class
  385.  *
  386.  * @package nanoserv
  387.  * @subpackage Handlers
  388.  */
  389.  
  390.     protected function Handle_Request($url{
  391.  
  392.         $this->on_Request($url);
  393.     
  394.     }
  395.  
  396. }
  397.  
  398. ?>

Documentation generated on Wed, 30 Nov 2011 22:03:24 +0100 by phpDocumentor 1.4.3