Source for file http.php

Documentation is available at http.php

  1. <?php
  2. /**
  3.  *  base include file for SimpleTest
  4.  *  @package    SimpleTest
  5.  *  @subpackage WebTester
  6.  *  @version    $Id: http.php 1829 2008-12-08 17:56:37Z edwardzyang $
  7.  */
  8.  
  9. /**#@+
  10.  *  include other SimpleTest class files
  11.  */
  12. require_once(dirname(__FILE__'/socket.php');
  13. require_once(dirname(__FILE__'/cookies.php');
  14. require_once(dirname(__FILE__'/url.php');
  15. /**#@-*/
  16.  
  17. /**
  18.  *    Creates HTTP headers for the end point of
  19.  *    a HTTP request.
  20.  *    @package SimpleTest
  21.  *    @subpackage WebTester
  22.  */
  23. class SimpleRoute {
  24.     private $url;
  25.     
  26.     /**
  27.      *    Sets the target URL.
  28.      *    @param SimpleUrl $url   URL as object.
  29.      *    @access public
  30.      */
  31.     function __construct($url{
  32.         $this->url $url;
  33.     }
  34.     
  35.     /**
  36.      *    Resource name.
  37.      *    @return SimpleUrl        Current url.
  38.      *    @access protected
  39.      */
  40.     function getUrl({
  41.         return $this->url;
  42.     }
  43.     
  44.     /**
  45.      *    Creates the first line which is the actual request.
  46.      *    @param string $method   HTTP request method, usually GET.
  47.      *    @return string          Request line content.
  48.      *    @access protected
  49.      */
  50.     protected function getRequestLine($method{
  51.         return $method ' ' $this->url->getPath(.
  52.                 $this->url->getEncodedRequest(' HTTP/1.0';
  53.     }
  54.     
  55.     /**
  56.      *    Creates the host part of the request.
  57.      *    @return string          Host line content.
  58.      *    @access protected
  59.      */
  60.     protected function getHostLine({
  61.         $line 'Host: ' $this->url->getHost();
  62.         if ($this->url->getPort()) {
  63.             $line .= ':' $this->url->getPort();
  64.         }
  65.         return $line;
  66.     }
  67.     
  68.     /**
  69.      *    Opens a socket to the route.
  70.      *    @param string $method      HTTP request method, usually GET.
  71.      *    @param integer $timeout    Connection timeout.
  72.      *    @return SimpleSocket       New socket.
  73.      *    @access public
  74.      */
  75.     function createConnection($method$timeout{
  76.         $default_port ('https' == $this->url->getScheme()) 443 80;
  77.         $socket $this->createSocket(
  78.                 $this->url->getScheme($this->url->getScheme('http',
  79.                 $this->url->getHost(),
  80.                 $this->url->getPort($this->url->getPort($default_port,
  81.                 $timeout);
  82.         if ($socket->isError()) {
  83.             $socket->write($this->getRequestLine($method"\r\n");
  84.             $socket->write($this->getHostLine("\r\n");
  85.             $socket->write("Connection: close\r\n");
  86.         }
  87.         return $socket;
  88.     }
  89.     
  90.     /**
  91.      *    Factory for socket.
  92.      *    @param string $scheme                   Protocol to use.
  93.      *    @param string $host                     Hostname to connect to.
  94.      *    @param integer $port                    Remote port.
  95.      *    @param integer $timeout                 Connection timeout.
  96.      *    @return SimpleSocket/SimpleSecureSocket New socket.
  97.      *    @access protected
  98.      */
  99.     protected function createSocket($scheme$host$port$timeout{
  100.         if (in_array($schemearray('file'))) {
  101.             return new SimpleFileSocket($this->url);
  102.         elseif (in_array($schemearray('https'))) {
  103.             return new SimpleSecureSocket($host$port$timeout);
  104.         else {
  105.             return new SimpleSocket($host$port$timeout);
  106.         }
  107.     }
  108. }
  109.  
  110. /**
  111.  *    Creates HTTP headers for the end point of
  112.  *    a HTTP request via a proxy server.
  113.  *    @package SimpleTest
  114.  *    @subpackage WebTester
  115.  */
  116. class SimpleProxyRoute extends SimpleRoute {
  117.     private $proxy;
  118.     private $username;
  119.     private $password;
  120.     
  121.     /**
  122.      *    Stashes the proxy address.
  123.      *    @param SimpleUrl $url     URL as object.
  124.      *    @param string $proxy      Proxy URL.
  125.      *    @param string $username   Username for autentication.
  126.      *    @param string $password   Password for autentication.
  127.      *    @access public
  128.      */
  129.     function __construct($url$proxy$username false$password false{
  130.         parent::__construct($url);
  131.         $this->proxy $proxy;
  132.         $this->username $username;
  133.         $this->password $password;
  134.     }
  135.     
  136.     /**
  137.      *    Creates the first line which is the actual request.
  138.      *    @param string $method   HTTP request method, usually GET.
  139.      *    @param SimpleUrl $url   URL as object.
  140.      *    @return string          Request line content.
  141.      *    @access protected
  142.      */
  143.     function getRequestLine($method{
  144.         $url $this->getUrl();
  145.         $scheme $url->getScheme($url->getScheme('http';
  146.         $port $url->getPort(':' $url->getPort('';
  147.         return $method ' ' $scheme '://' $url->getHost($port .
  148.                 $url->getPath($url->getEncodedRequest(' HTTP/1.0';
  149.     }
  150.     
  151.     /**
  152.      *    Creates the host part of the request.
  153.      *    @param SimpleUrl $url   URL as object.
  154.      *    @return string          Host line content.
  155.      *    @access protected
  156.      */
  157.     function getHostLine({
  158.         $host 'Host: ' $this->proxy->getHost();
  159.         $port $this->proxy->getPort($this->proxy->getPort(8080;
  160.         return "$host:$port";
  161.     }
  162.     
  163.     /**
  164.      *    Opens a socket to the route.
  165.      *    @param string $method       HTTP request method, usually GET.
  166.      *    @param integer $timeout     Connection timeout.
  167.      *    @return SimpleSocket        New socket.
  168.      *    @access public
  169.      */
  170.     function createConnection($method$timeout{
  171.         $socket $this->createSocket(
  172.                 $this->proxy->getScheme($this->proxy->getScheme('http',
  173.                 $this->proxy->getHost(),
  174.                 $this->proxy->getPort($this->proxy->getPort(8080,
  175.                 $timeout);
  176.         if ($socket->isError()) {
  177.             return $socket;
  178.         }
  179.         $socket->write($this->getRequestLine($method"\r\n");
  180.         $socket->write($this->getHostLine("\r\n");
  181.         if ($this->username && $this->password{
  182.             $socket->write('Proxy-Authorization: Basic ' .
  183.                     base64_encode($this->username ':' $this->password.
  184.                     "\r\n");
  185.         }
  186.         $socket->write("Connection: close\r\n");
  187.         return $socket;
  188.     }
  189. }
  190.  
  191. /**
  192.  *    HTTP request for a web page. Factory for
  193.  *    HttpResponse object.
  194.  *    @package SimpleTest
  195.  *    @subpackage WebTester
  196.  */
  197.     private $route;
  198.     private $encoding;
  199.     private $headers;
  200.     private $cookies;
  201.     
  202.     /**
  203.      *    Builds the socket request from the different pieces.
  204.      *    These include proxy information, URL, cookies, headers,
  205.      *    request method and choice of encoding.
  206.      *    @param SimpleRoute $route              Request route.
  207.      *    @param SimpleFormEncoding $encoding    Content to send with
  208.      *                                            request.
  209.      *    @access public
  210.      */
  211.     function __construct($route$encoding{
  212.         $this->route $route;
  213.         $this->encoding $encoding;
  214.         $this->headers array();
  215.         $this->cookies array();
  216.     }
  217.     
  218.     /**
  219.      *    Dispatches the content to the route's socket.
  220.      *    @param integer $timeout      Connection timeout.
  221.      *    @return SimpleHttpResponse   A response which may only have
  222.      *                                  an error, but hopefully has a
  223.      *                                  complete web page.
  224.      *    @access public
  225.      */
  226.     function fetch($timeout{
  227.         $socket $this->route->createConnection($this->encoding->getMethod()$timeout);
  228.         if ($socket->isError()) {
  229.             $this->dispatchRequest($socket$this->encoding);
  230.         }
  231.         return $this->createResponse($socket);
  232.     }
  233.     
  234.     /**
  235.      *    Sends the headers.
  236.      *    @param SimpleSocket $socket           Open socket.
  237.      *    @param string $method                 HTTP request method,
  238.      *                                           usually GET.
  239.      *    @param SimpleFormEncoding $encoding   Content to send with request.
  240.      *    @access private
  241.      */
  242.     protected function dispatchRequest($socket$encoding{
  243.         foreach ($this->headers as $header_line{
  244.             $socket->write($header_line "\r\n");
  245.         }
  246.         if (count($this->cookies0{
  247.             $socket->write("Cookie: " implode(";"$this->cookies"\r\n");
  248.         }
  249.         $encoding->writeHeadersTo($socket);
  250.         $socket->write("\r\n");
  251.         $encoding->writeTo($socket);
  252.     }
  253.     
  254.     /**
  255.      *    Adds a header line to the request.
  256.      *    @param string $header_line    Text of full header line.
  257.      *    @access public
  258.      */
  259.     function addHeaderLine($header_line{
  260.         $this->headers[$header_line;
  261.     }
  262.     
  263.     /**
  264.      *    Reads all the relevant cookies from the
  265.      *    cookie jar.
  266.      *    @param SimpleCookieJar $jar     Jar to read
  267.      *    @param SimpleUrl $url           Url to use for scope.
  268.      *    @access public
  269.      */
  270.     function readCookiesFromJar($jar$url{
  271.         $this->cookies $jar->selectAsPairs($url);
  272.     }
  273.     
  274.     /**
  275.      *    Wraps the socket in a response parser.
  276.      *    @param SimpleSocket $socket   Responding socket.
  277.      *    @return SimpleHttpResponse    Parsed response object.
  278.      *    @access protected
  279.      */
  280.     protected function createResponse($socket{
  281.         $response new SimpleHttpResponse(
  282.                 $socket,
  283.                 $this->route->getUrl(),
  284.                 $this->encoding);
  285.         $socket->close();
  286.         return $response;
  287.     }
  288. }
  289.  
  290. /**
  291.  *    Collection of header lines in the response.
  292.  *    @package SimpleTest
  293.  *    @subpackage WebTester
  294.  */
  295.     private $raw_headers;
  296.     private $response_code;
  297.     private $http_version;
  298.     private $mime_type;
  299.     private $location;
  300.     private $cookies;
  301.     private $authentication;
  302.     private $realm;
  303.     
  304.     /**
  305.      *    Parses the incoming header block.
  306.      *    @param string $headers     Header block.
  307.      *    @access public
  308.      */
  309.     function __construct($headers{
  310.         $this->raw_headers $headers;
  311.         $this->response_code false;
  312.         $this->http_version false;
  313.         $this->mime_type '';
  314.         $this->location false;
  315.         $this->cookies array();
  316.         $this->authentication false;
  317.         $this->realm false;
  318.         foreach (explode("\r\n"$headersas $header_line{
  319.             $this->parseHeaderLine($header_line);
  320.         }
  321.     }
  322.     
  323.     /**
  324.      *    Accessor for parsed HTTP protocol version.
  325.      *    @return integer           HTTP error code.
  326.      *    @access public
  327.      */
  328.     function getHttpVersion({
  329.         return $this->http_version;
  330.     }
  331.     
  332.     /**
  333.      *    Accessor for raw header block.
  334.      *    @return string        All headers as raw string.
  335.      *    @access public
  336.      */
  337.     function getRaw({
  338.         return $this->raw_headers;
  339.     }
  340.     
  341.     /**
  342.      *    Accessor for parsed HTTP error code.
  343.      *    @return integer           HTTP error code.
  344.      *    @access public
  345.      */
  346.     function getResponseCode({
  347.         return (integer)$this->response_code;
  348.     }
  349.     
  350.     /**
  351.      *    Returns the redirected URL or false if
  352.      *    no redirection.
  353.      *    @return string      URL or false for none.
  354.      *    @access public
  355.      */
  356.     function getLocation({
  357.         return $this->location;
  358.     }
  359.     
  360.     /**
  361.      *    Test to see if the response is a valid redirect.
  362.      *    @return boolean       True if valid redirect.
  363.      *    @access public
  364.      */
  365.     function isRedirect({
  366.         return in_array($this->response_codearray(301302303307)) &&
  367.                 (boolean)$this->getLocation();
  368.     }
  369.     
  370.     /**
  371.      *    Test to see if the response is an authentication
  372.      *    challenge.
  373.      *    @return boolean       True if challenge.
  374.      *    @access public
  375.      */
  376.     function isChallenge({
  377.         return ($this->response_code == 401&&
  378.                 (boolean)$this->authentication &&
  379.                 (boolean)$this->realm;
  380.     }
  381.     
  382.     /**
  383.      *    Accessor for MIME type header information.
  384.      *    @return string           MIME type.
  385.      *    @access public
  386.      */
  387.     function getMimeType({
  388.         return $this->mime_type;
  389.     }
  390.     
  391.     /**
  392.      *    Accessor for authentication type.
  393.      *    @return string        Type.
  394.      *    @access public
  395.      */
  396.     function getAuthentication({
  397.         return $this->authentication;
  398.     }
  399.     
  400.     /**
  401.      *    Accessor for security realm.
  402.      *    @return string        Realm.
  403.      *    @access public
  404.      */
  405.     function getRealm({
  406.         return $this->realm;
  407.     }
  408.     
  409.     /**
  410.      *    Writes new cookies to the cookie jar.
  411.      *    @param SimpleCookieJar $jar   Jar to write to.
  412.      *    @param SimpleUrl $url         Host and path to write under.
  413.      *    @access public
  414.      */
  415.     function writeCookiesToJar($jar$url{
  416.         foreach ($this->cookies as $cookie{
  417.             $jar->setCookie(
  418.                     $cookie->getName(),
  419.                     $cookie->getValue(),
  420.                     $url->getHost(),
  421.                     $cookie->getPath(),
  422.                     $cookie->getExpiry());
  423.         }
  424.     }
  425.  
  426.     /**
  427.      *    Called on each header line to accumulate the held
  428.      *    data within the class.
  429.      *    @param string $header_line        One line of header.
  430.      *    @access protected
  431.      */
  432.     protected function parseHeaderLine($header_line{
  433.         if (preg_match('/HTTP\/(\d+\.\d+)\s+(\d+)/i'$header_line$matches)) {
  434.             $this->http_version $matches[1];
  435.             $this->response_code $matches[2];
  436.         }
  437.         if (preg_match('/Content-type:\s*(.*)/i'$header_line$matches)) {
  438.             $this->mime_type trim($matches[1]);
  439.         }
  440.         if (preg_match('/Location:\s*(.*)/i'$header_line$matches)) {
  441.             $this->location trim($matches[1]);
  442.         }
  443.         if (preg_match('/Set-cookie:(.*)/i'$header_line$matches)) {
  444.             $this->cookies[$this->parseCookie($matches[1]);
  445.         }
  446.         if (preg_match('/WWW-Authenticate:\s+(\S+)\s+realm=\"(.*?)\"/i'$header_line$matches)) {
  447.             $this->authentication $matches[1];
  448.             $this->realm trim($matches[2]);
  449.         }
  450.     }
  451.     
  452.     /**
  453.      *    Parse the Set-cookie content.
  454.      *    @param string $cookie_line    Text after "Set-cookie:"
  455.      *    @return SimpleCookie          New cookie object.
  456.      *    @access private
  457.      */
  458.     protected function parseCookie($cookie_line{
  459.         $parts explode(";"$cookie_line);
  460.         $cookie array();
  461.         preg_match('/\s*(.*?)\s*=(.*)/'array_shift($parts)$cookie);
  462.         foreach ($parts as $part{
  463.             if (preg_match('/\s*(.*?)\s*=(.*)/'$part$matches)) {
  464.                 $cookie[$matches[1]] trim($matches[2]);
  465.             }
  466.         }
  467.         return new SimpleCookie(
  468.                 $cookie[1],
  469.                 trim($cookie[2]),
  470.                 isset($cookie["path"]$cookie["path""",
  471.                 isset($cookie["expires"]$cookie["expires"false);
  472.     }
  473. }
  474.  
  475. /**
  476.  *    Basic HTTP response.
  477.  *    @package SimpleTest
  478.  *    @subpackage WebTester
  479.  */
  480.     private $url;
  481.     private $encoding;
  482.     private $sent;
  483.     private $content;
  484.     private $headers;
  485.     
  486.     /**
  487.      *    Constructor. Reads and parses the incoming
  488.      *    content and headers.
  489.      *    @param SimpleSocket $socket   Network connection to fetch
  490.      *                                   response text from.
  491.      *    @param SimpleUrl $url         Resource name.
  492.      *    @param mixed $encoding        Record of content sent.
  493.      *    @access public
  494.      */
  495.     function __construct($socket$url$encoding{
  496.         parent::__construct();
  497.         $this->url $url;
  498.         $this->encoding $encoding;
  499.         $this->sent $socket->getSent();
  500.         $this->content false;
  501.         $raw $this->readAll($socket);
  502.         if ($socket->isError()) {
  503.             $this->setError('Error reading socket [' $socket->getError(']');
  504.             return;
  505.         }
  506.         $this->parse($raw);
  507.     }
  508.     
  509.     /**
  510.      *    Splits up the headers and the rest of the content.
  511.      *    @param string $raw    Content to parse.
  512.      *    @access private
  513.      */
  514.     protected function parse($raw{
  515.         if ($raw{
  516.             $this->setError('Nothing fetched');
  517.             $this->headers new SimpleHttpHeaders('');
  518.         elseif ('file' == $this->url->getScheme()) {
  519.             $this->headers new SimpleHttpHeaders('');
  520.             $this->content $raw;
  521.         elseif (strstr($raw"\r\n\r\n")) {
  522.             $this->setError('Could not split headers from content');
  523.             $this->headers new SimpleHttpHeaders($raw);
  524.         else {
  525.             list($headers$this->contentexplode("\r\n\r\n"$raw2);
  526.             $this->headers new SimpleHttpHeaders($headers);
  527.         }
  528.     }
  529.     
  530.     /**
  531.      *    Original request method.
  532.      *    @return string        GET, POST or HEAD.
  533.      *    @access public
  534.      */
  535.     function getMethod({
  536.         return $this->encoding->getMethod();
  537.     }
  538.     
  539.     /**
  540.      *    Resource name.
  541.      *    @return SimpleUrl        Current url.
  542.      *    @access public
  543.      */
  544.     function getUrl({
  545.         return $this->url;
  546.     }
  547.     
  548.     /**
  549.      *    Original request data.
  550.      *    @return mixed              Sent content.
  551.      *    @access public
  552.      */
  553.     function getRequestData({
  554.         return $this->encoding;
  555.     }
  556.     
  557.     /**
  558.      *    Raw request that was sent down the wire.
  559.      *    @return string        Bytes actually sent.
  560.      *    @access public
  561.      */
  562.     function getSent({
  563.         return $this->sent;
  564.     }
  565.     
  566.     /**
  567.      *    Accessor for the content after the last
  568.      *    header line.
  569.      *    @return string           All content.
  570.      *    @access public
  571.      */
  572.     function getContent({
  573.         return $this->content;
  574.     }
  575.     
  576.     /**
  577.      *    Accessor for header block. The response is the
  578.      *    combination of this and the content.
  579.      *    @return SimpleHeaders        Wrapped header block.
  580.      *    @access public
  581.      */
  582.     function getHeaders({
  583.         return $this->headers;
  584.     }
  585.     
  586.     /**
  587.      *    Accessor for any new cookies.
  588.      *    @return array       List of new cookies.
  589.      *    @access public
  590.      */
  591.     function getNewCookies({
  592.         return $this->headers->getNewCookies();
  593.     }
  594.     
  595.     /**
  596.      *    Reads the whole of the socket output into a
  597.      *    single string.
  598.      *    @param SimpleSocket $socket  Unread socket.
  599.      *    @return string               Raw output if successful
  600.      *                                  else false.
  601.      *    @access private
  602.      */
  603.     protected function readAll($socket{
  604.         $all '';
  605.         while ($this->isLastPacket($next $socket->read())) {
  606.             $all .= $next;
  607.         }
  608.         return $all;
  609.     }
  610.     
  611.     /**
  612.      *    Test to see if the packet from the socket is the
  613.      *    last one.
  614.      *    @param string $packet    Chunk to interpret.
  615.      *    @return boolean          True if empty or EOF.
  616.      *    @access private
  617.      */
  618.     protected function isLastPacket($packet{
  619.         if (is_string($packet)) {
  620.             return $packet === '';
  621.         }
  622.         return $packet;
  623.     }
  624. }
  625. ?>

Documentation generated on Sun, 31 Oct 2010 16:31:38 -0500 by phpDocumentor 1.4.3