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

Source for file Proxy.php

Documentation is available at Proxy.php

  1. <?php
  2.  
  3. /**
  4.  * Zend Framework
  5.  *
  6.  * LICENSE
  7.  *
  8.  * This source file is subject to the new BSD license that is bundled
  9.  * with this package in the file LICENSE.txt.
  10.  * It is also available through the world-wide-web at this URL:
  11.  * http://framework.zend.com/license/new-bsd
  12.  * If you did not receive a copy of the license and are unable to
  13.  * obtain it through the world-wide-web, please send an email
  14.  * to license@zend.com so we can send you a copy immediately.
  15.  *
  16.  * @category   Microsoft
  17.  * @package    Microsoft_Http
  18.  * @subpackage Client_Adapter
  19.  * @version    $Id: Proxy.php 17059 2009-07-25 11:24:49Z shahar $
  20.  * @copyright  Copyright (c) 2005-2009 Zend Technologies USA Inc. (http://www.zend.com)
  21.  * @license    http://framework.zend.com/license/new-bsd     New BSD License
  22.  */
  23.  
  24. /**
  25.  * @see Microsoft_AutoLoader
  26.  */
  27. require_once dirname(__FILE__) . '/../../../AutoLoader.php';
  28.  
  29. /**
  30.  * HTTP Proxy-supporting Microsoft_Http_Client adapter class, based on the default
  31.  * socket based adapter.
  32.  *
  33.  * Should be used if proxy HTTP access is required. If no proxy is set, will
  34.  * fall back to Microsoft_Http_Client_Adapter_Socket behavior. Just like the
  35.  * default Socket adapter, this adapter does not require any special extensions
  36.  * installed.
  37.  *
  38.  * @category   Microsoft
  39.  * @package    Microsoft_Http
  40.  * @subpackage Client_Adapter
  41.  * @copyright  Copyright (c) 2005-2009 Zend Technologies USA Inc. (http://www.zend.com)
  42.  * @license    http://framework.zend.com/license/new-bsd     New BSD License
  43.  */
  44. {
  45.     /**
  46.      * Parameters array
  47.      *
  48.      * @var array 
  49.      */
  50.     protected $config = array(
  51.         'ssltransport'  => 'ssl',
  52.         'sslcert'       => null,
  53.         'sslpassphrase' => null,
  54.         'proxy_host'    => '',
  55.         'proxy_port'    => 8080,
  56.         'proxy_user'    => '',
  57.         'proxy_pass'    => '',
  58.         'proxy_auth'    => Microsoft_Http_Client::AUTH_BASIC,
  59.         'persistent'    => false
  60.     );
  61.  
  62.     /**
  63.      * Whether HTTPS CONNECT was already negotiated with the proxy or not
  64.      *
  65.      * @var boolean 
  66.      */
  67.     protected $negotiated = false;
  68.  
  69.     /**
  70.      * Connect to the remote server
  71.      *
  72.      * Will try to connect to the proxy server. If no proxy was set, will
  73.      * fall back to the target server (behave like regular Socket adapter)
  74.      *
  75.      * @param string  $host 
  76.      * @param int     $port 
  77.      * @param boolean $secure 
  78.      */
  79.     public function connect($host, $port = 80, $secure = false)
  80.     {
  81.         // If no proxy is set, fall back to Socket adapter
  82.         if (! $this->config['proxy_host']) {
  83.             return parent::connect($host, $port, $secure);
  84.         }
  85.  
  86.         // Connect (a non-secure connection) to the proxy server
  87.         return parent::connect(
  88.             $this->config['proxy_host'],
  89.             $this->config['proxy_port'],
  90.             false
  91.         );
  92.     }
  93.  
  94.     /**
  95.      * Send request to the proxy server
  96.      *
  97.      * @param string        $method 
  98.      * @param Microsoft_Uri_Http $uri 
  99.      * @param string        $http_ver 
  100.      * @param array         $headers 
  101.      * @param string        $body 
  102.      * @return string Request as string
  103.      */
  104.     public function write($method, $uri, $http_ver = '1.1', $headers = array(), $body = '')
  105.     {
  106.         // If no proxy is set, fall back to default Socket adapter
  107.         if (! $this->config['proxy_host']) return parent::write($method, $uri, $http_ver, $headers, $body);
  108.  
  109.         // Make sure we're properly connected
  110.         if (! $this->socket) {
  111.             require_once 'Microsoft/Http/Client/Adapter/Exception.php';
  112.             throw new Microsoft_Http_Client_Adapter_Exception("Trying to write but we are not connected");
  113.         }
  114.  
  115.         $host = $this->config['proxy_host'];
  116.         $port = $this->config['proxy_port'];
  117.  
  118.         if ($this->connected_to[0] != "tcp://$host" || $this->connected_to[1] != $port) {
  119.             require_once 'Microsoft/Http/Client/Adapter/Exception.php';
  120.             throw new Microsoft_Http_Client_Adapter_Exception("Trying to write but we are connected to the wrong proxy server");
  121.         }
  122.  
  123.         // Add Proxy-Authorization header
  124.         if ($this->config['proxy_user'] && ! isset($headers['proxy-authorization'])) {
  125.             $headers['proxy-authorization'] = Microsoft_Http_Client::encodeAuthHeader(
  126.                 $this->config['proxy_user'], $this->config['proxy_pass'], $this->config['proxy_auth']
  127.             );
  128.         }
  129.  
  130.         // if we are proxying HTTPS, preform CONNECT handshake with the proxy
  131.         if ($uri->getScheme() == 'https' && (! $this->negotiated)) {
  132.             $this->connectHandshake($uri->getHost(), $uri->getPort(), $http_ver, $headers);
  133.             $this->negotiated = true;
  134.         }
  135.  
  136.         // Save request method for later
  137.         $this->method = $method;
  138.  
  139.         // Build request headers
  140.         if ($this->negotiated) {
  141.             $path = $uri->getPath();
  142.             if ($uri->getQuery()) {
  143.                 $path .= '?' . $uri->getQuery();
  144.             }
  145.             $request = "$method $path HTTP/$http_ver\r\n";
  146.         } else {
  147.             $request = "$method $uri HTTP/$http_ver\r\n";
  148.         }
  149.  
  150.         // Add all headers to the request string
  151.         foreach ($headers as $k => $v) {
  152.             if (is_string($k)) $v = "$k: $v";
  153.             $request .= "$v\r\n";
  154.         }
  155.  
  156.         // Add the request body
  157.         $request .= "\r\n" . $body;
  158.  
  159.         // Send the request
  160.         if (! @fwrite($this->socket, $request)) {
  161.             require_once 'Microsoft/Http/Client/Adapter/Exception.php';
  162.             throw new Microsoft_Http_Client_Adapter_Exception("Error writing request to proxy server");
  163.         }
  164.  
  165.         return $request;
  166.     }
  167.  
  168.     /**
  169.      * Preform handshaking with HTTPS proxy using CONNECT method
  170.      *
  171.      * @param string  $host 
  172.      * @param integer $port 
  173.      * @param string  $http_ver 
  174.      * @param array   $headers 
  175.      */
  176.     protected function connectHandshake($host, $port = 443, $http_ver = '1.1', array &$headers = array())
  177.     {
  178.         $request = "CONNECT $host:$port HTTP/$http_ver\r\n" .
  179.                    "Host: " . $this->config['proxy_host'] . "\r\n";
  180.  
  181.         // Add the user-agent header
  182.         if (isset($this->config['useragent'])) {
  183.             $request .= "User-agent: " . $this->config['useragent'] . "\r\n";
  184.         }
  185.  
  186.         // If the proxy-authorization header is set, send it to proxy but remove
  187.         // it from headers sent to target host
  188.         if (isset($headers['proxy-authorization'])) {
  189.             $request .= "Proxy-authorization: " . $headers['proxy-authorization'] . "\r\n";
  190.             unset($headers['proxy-authorization']);
  191.         }
  192.  
  193.         $request .= "\r\n";
  194.  
  195.         // Send the request
  196.         if (! @fwrite($this->socket, $request)) {
  197.             require_once 'Microsoft/Http/Client/Adapter/Exception.php';
  198.             throw new Microsoft_Http_Client_Adapter_Exception("Error writing request to proxy server");
  199.         }
  200.  
  201.         // Read response headers only
  202.         $response = '';
  203.         $gotStatus = false;
  204.         while ($line = @fgets($this->socket)) {
  205.             $gotStatus = $gotStatus || (strpos($line, 'HTTP') !== false);
  206.             if ($gotStatus) {
  207.                 $response .= $line;
  208.                 if (!chop($line)) break;
  209.             }
  210.         }
  211.  
  212.         // Check that the response from the proxy is 200
  213.         if (Microsoft_Http_Response::extractCode($response) != 200) {
  214.             require_once 'Microsoft/Http/Client/Adapter/Exception.php';
  215.             throw new Microsoft_Http_Client_Adapter_Exception("Unable to connect to HTTPS proxy. Server response: " . $response);
  216.         }
  217.  
  218.         // If all is good, switch socket to secure mode. We have to fall back
  219.         // through the different modes
  220.         $modes = array(
  221.             STREAM_CRYPTO_METHOD_TLS_CLIENT,
  222.             STREAM_CRYPTO_METHOD_SSLv3_CLIENT,
  223.             STREAM_CRYPTO_METHOD_SSLv23_CLIENT,
  224.             STREAM_CRYPTO_METHOD_SSLv2_CLIENT
  225.         );
  226.  
  227.         $success = false;
  228.         foreach($modes as $mode) {
  229.             $success = stream_socket_enable_crypto($this->socket, true, $mode);
  230.             if ($success) break;
  231.         }
  232.  
  233.         if (! $success) {
  234.                 require_once 'Microsoft/Http/Client/Adapter/Exception.php';
  235.                 throw new Microsoft_Http_Client_Adapter_Exception("Unable to connect to" .
  236.                     " HTTPS server through proxy: could not negotiate secure connection.");
  237.         }
  238.     }
  239.  
  240.     /**
  241.      * Close the connection to the server
  242.      *
  243.      */
  244.     public function close()
  245.     {
  246.         parent::close();
  247.         $this->negotiated = false;
  248.     }
  249.  
  250.     /**
  251.      * Destructor: make sure the socket is disconnected
  252.      *
  253.      */
  254.     public function __destruct()
  255.     {
  256.         if ($this->socket) $this->close();
  257.     }
  258. }

Documentation generated on Sat, 03 Dec 2011 13:59:32 +0100 by phpDocumentor 1.4.3