source: extensions/Google2Piwigo/include/Zend/Http/Client/Adapter/Proxy.php @ 17475

Last change on this file since 17475 was 17475, checked in by mistic100, 12 years ago

new extension: Google2Piwigo

File size: 9.2 KB
Line 
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   Zend
17 * @package    Zend_Http
18 * @subpackage Client_Adapter
19 * @version    $Id: Proxy.php 24594 2012-01-05 21:27:01Z matthew $
20 * @copyright  Copyright (c) 2005-2012 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 Zend_Uri_Http
26 */
27require_once 'Zend/Uri/Http.php';
28/**
29 * @see Zend_Http_Client
30 */
31require_once 'Zend/Http/Client.php';
32/**
33 * @see Zend_Http_Client_Adapter_Socket
34 */
35require_once 'Zend/Http/Client/Adapter/Socket.php';
36
37/**
38 * HTTP Proxy-supporting Zend_Http_Client adapter class, based on the default
39 * socket based adapter.
40 *
41 * Should be used if proxy HTTP access is required. If no proxy is set, will
42 * fall back to Zend_Http_Client_Adapter_Socket behavior. Just like the
43 * default Socket adapter, this adapter does not require any special extensions
44 * installed.
45 *
46 * @category   Zend
47 * @package    Zend_Http
48 * @subpackage Client_Adapter
49 * @copyright  Copyright (c) 2005-2012 Zend Technologies USA Inc. (http://www.zend.com)
50 * @license    http://framework.zend.com/license/new-bsd     New BSD License
51 */
52class Zend_Http_Client_Adapter_Proxy extends Zend_Http_Client_Adapter_Socket
53{
54    /**
55     * Parameters array
56     *
57     * @var array
58     */
59    protected $config = array(
60        'ssltransport'  => 'ssl',
61        'sslcert'       => null,
62        'sslpassphrase' => null,
63        'sslusecontext' => false,
64        'proxy_host'    => '',
65        'proxy_port'    => 8080,
66        'proxy_user'    => '',
67        'proxy_pass'    => '',
68        'proxy_auth'    => Zend_Http_Client::AUTH_BASIC,
69        'persistent'    => false
70    );
71
72    /**
73     * Whether HTTPS CONNECT was already negotiated with the proxy or not
74     *
75     * @var boolean
76     */
77    protected $negotiated = false;
78
79    /**
80     * Connect to the remote server
81     *
82     * Will try to connect to the proxy server. If no proxy was set, will
83     * fall back to the target server (behave like regular Socket adapter)
84     *
85     * @param string  $host
86     * @param int     $port
87     * @param boolean $secure
88     */
89    public function connect($host, $port = 80, $secure = false)
90    {
91        // If no proxy is set, fall back to Socket adapter
92        if (! $this->config['proxy_host']) {
93            return parent::connect($host, $port, $secure);
94        }
95
96        /* Url might require stream context even if proxy connection doesn't */
97        if ($secure) {
98            $this->config['sslusecontext'] = true;
99        }
100
101        // Connect (a non-secure connection) to the proxy server
102        return parent::connect(
103            $this->config['proxy_host'],
104            $this->config['proxy_port'],
105            false
106        );
107    }
108
109    /**
110     * Send request to the proxy server
111     *
112     * @param string        $method
113     * @param Zend_Uri_Http $uri
114     * @param string        $http_ver
115     * @param array         $headers
116     * @param string        $body
117     * @return string Request as string
118     */
119    public function write($method, $uri, $http_ver = '1.1', $headers = array(), $body = '')
120    {
121        // If no proxy is set, fall back to default Socket adapter
122        if (! $this->config['proxy_host']) return parent::write($method, $uri, $http_ver, $headers, $body);
123
124        // Make sure we're properly connected
125        if (! $this->socket) {
126            require_once 'Zend/Http/Client/Adapter/Exception.php';
127            throw new Zend_Http_Client_Adapter_Exception("Trying to write but we are not connected");
128        }
129
130        $host = $this->config['proxy_host'];
131        $port = $this->config['proxy_port'];
132
133        if ($this->connected_to[0] != "tcp://$host" || $this->connected_to[1] != $port) {
134            require_once 'Zend/Http/Client/Adapter/Exception.php';
135            throw new Zend_Http_Client_Adapter_Exception("Trying to write but we are connected to the wrong proxy server");
136        }
137
138        // Add Proxy-Authorization header
139        if ($this->config['proxy_user'] && ! isset($headers['proxy-authorization'])) {
140            $headers['proxy-authorization'] = Zend_Http_Client::encodeAuthHeader(
141                $this->config['proxy_user'], $this->config['proxy_pass'], $this->config['proxy_auth']
142            );
143        }
144
145        // if we are proxying HTTPS, preform CONNECT handshake with the proxy
146        if ($uri->getScheme() == 'https' && (! $this->negotiated)) {
147            $this->connectHandshake($uri->getHost(), $uri->getPort(), $http_ver, $headers);
148            $this->negotiated = true;
149        }
150
151        // Save request method for later
152        $this->method = $method;
153
154        // Build request headers
155        if ($this->negotiated) {
156            $path = $uri->getPath();
157            if ($uri->getQuery()) {
158                $path .= '?' . $uri->getQuery();
159            }
160            $request = "$method $path HTTP/$http_ver\r\n";
161        } else {
162            $request = "$method $uri HTTP/$http_ver\r\n";
163        }
164
165        // Add all headers to the request string
166        foreach ($headers as $k => $v) {
167            if (is_string($k)) $v = "$k: $v";
168            $request .= "$v\r\n";
169        }
170
171        if(is_resource($body)) {
172            $request .= "\r\n";
173        } else {
174            // Add the request body
175            $request .= "\r\n" . $body;
176        }
177
178        // Send the request
179        if (! @fwrite($this->socket, $request)) {
180            require_once 'Zend/Http/Client/Adapter/Exception.php';
181            throw new Zend_Http_Client_Adapter_Exception("Error writing request to proxy server");
182        }
183
184        if(is_resource($body)) {
185            if(stream_copy_to_stream($body, $this->socket) == 0) {
186                require_once 'Zend/Http/Client/Adapter/Exception.php';
187                throw new Zend_Http_Client_Adapter_Exception('Error writing request to server');
188            }
189        }
190
191        return $request;
192    }
193
194    /**
195     * Preform handshaking with HTTPS proxy using CONNECT method
196     *
197     * @param string  $host
198     * @param integer $port
199     * @param string  $http_ver
200     * @param array   $headers
201     */
202    protected function connectHandshake($host, $port = 443, $http_ver = '1.1', array &$headers = array())
203    {
204        $request = "CONNECT $host:$port HTTP/$http_ver\r\n" .
205                   "Host: " . $this->config['proxy_host'] . "\r\n";
206
207        // Add the user-agent header
208        if (isset($this->config['useragent'])) {
209            $request .= "User-agent: " . $this->config['useragent'] . "\r\n";
210        }
211
212        // If the proxy-authorization header is set, send it to proxy but remove
213        // it from headers sent to target host
214        if (isset($headers['proxy-authorization'])) {
215            $request .= "Proxy-authorization: " . $headers['proxy-authorization'] . "\r\n";
216            unset($headers['proxy-authorization']);
217        }
218
219        $request .= "\r\n";
220
221        // Send the request
222        if (! @fwrite($this->socket, $request)) {
223            require_once 'Zend/Http/Client/Adapter/Exception.php';
224            throw new Zend_Http_Client_Adapter_Exception("Error writing request to proxy server");
225        }
226
227        // Read response headers only
228        $response = '';
229        $gotStatus = false;
230        while ($line = @fgets($this->socket)) {
231            $gotStatus = $gotStatus || (strpos($line, 'HTTP') !== false);
232            if ($gotStatus) {
233                $response .= $line;
234                if (!chop($line)) break;
235            }
236        }
237
238        // Check that the response from the proxy is 200
239        if (Zend_Http_Response::extractCode($response) != 200) {
240            require_once 'Zend/Http/Client/Adapter/Exception.php';
241            throw new Zend_Http_Client_Adapter_Exception("Unable to connect to HTTPS proxy. Server response: " . $response);
242        }
243
244        // If all is good, switch socket to secure mode. We have to fall back
245        // through the different modes
246        $modes = array(
247            STREAM_CRYPTO_METHOD_TLS_CLIENT,
248            STREAM_CRYPTO_METHOD_SSLv3_CLIENT,
249            STREAM_CRYPTO_METHOD_SSLv23_CLIENT,
250            STREAM_CRYPTO_METHOD_SSLv2_CLIENT
251        );
252
253        $success = false;
254        foreach($modes as $mode) {
255            $success = stream_socket_enable_crypto($this->socket, true, $mode);
256            if ($success) break;
257        }
258
259        if (! $success) {
260                require_once 'Zend/Http/Client/Adapter/Exception.php';
261                throw new Zend_Http_Client_Adapter_Exception("Unable to connect to" .
262                    " HTTPS server through proxy: could not negotiate secure connection.");
263        }
264    }
265
266    /**
267     * Close the connection to the server
268     *
269     */
270    public function close()
271    {
272        parent::close();
273        $this->negotiated = false;
274    }
275
276    /**
277     * Destructor: make sure the socket is disconnected
278     *
279     */
280    public function __destruct()
281    {
282        if ($this->socket) $this->close();
283    }
284}
Note: See TracBrowser for help on using the repository browser.