Completed
Push — master ( 85e358...cf3219 )
by Rakesh
04:24 queued 10s
created

Zend_Http_Client_Adapter_Proxy::write()   F

Complexity

Conditions 20
Paths 1011

Size

Total Lines 103
Code Lines 54

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 1
Metric Value
cc 20
eloc 54
c 1
b 0
f 1
nc 1011
nop 5
dl 0
loc 103
rs 0

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

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 [email protected] so we can send you a copy immediately.
15
 *
16
 * @category   Zend
17
 * @package    Zend_Http
18
 * @subpackage Client_Adapter
19
 * @version    $Id$
20
 * @copyright  Copyright (c) 2005-2014 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
 */
27
require_once 'Zend/Uri/Http.php';
28
/**
29
 * @see Zend_Http_Client
30
 */
31
require_once 'Zend/Http/Client.php';
32
/**
33
 * @see Zend_Http_Client_Adapter_Socket
34
 */
35
require_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-2014 Zend Technologies USA Inc. (http://www.zend.com)
50
 * @license    http://framework.zend.com/license/new-bsd     New BSD License
51
 */
52
class 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
     * Stores the last CONNECT handshake request
81
     * 
82
     * @var string
83
     */
84
    protected $connectHandshakeRequest;
85
86
    /**
87
     * Connect to the remote server
88
     *
89
     * Will try to connect to the proxy server. If no proxy was set, will
90
     * fall back to the target server (behave like regular Socket adapter)
91
     *
92
     * @param string  $host
93
     * @param int     $port
94
     * @param boolean $secure
95
     */
96
    public function connect($host, $port = 80, $secure = false)
97
    {
98
        // If no proxy is set, fall back to Socket adapter
99
        if (!$this->config['proxy_host']) {
100
            return parent::connect($host, $port, $secure);
0 ignored issues
show
Bug introduced by
Are you sure the usage of parent::connect($host, $port, $secure) targeting Zend_Http_Client_Adapter_Socket::connect() seems to always return null.

This check looks for function or method calls that always return null and whose return value is used.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
if ($a->getObject()) {

The method getObject() can return nothing but null, so it makes no sense to use the return value.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
101
        }
102
103
        /* Url might require stream context even if proxy connection doesn't */
104
        if ($secure) {
105
            $this->config['sslusecontext'] = true;
106
        }
107
108
        // Connect (a non-secure connection) to the proxy server
109
        return parent::connect(
0 ignored issues
show
Bug introduced by
Are you sure the usage of parent::connect($this->c...g['proxy_port'], false) targeting Zend_Http_Client_Adapter_Socket::connect() seems to always return null.

This check looks for function or method calls that always return null and whose return value is used.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
if ($a->getObject()) {

The method getObject() can return nothing but null, so it makes no sense to use the return value.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
110
            $this->config['proxy_host'],
111
            $this->config['proxy_port'],
112
            false
113
        );
114
    }
115
116
    /**
117
     * Send request to the proxy server
118
     *
119
     * @param string        $method
120
     * @param Zend_Uri_Http $uri
121
     * @param string        $http_ver
122
     * @param array         $headers
123
     * @param string        $body
124
     * @return string Request as string
125
     * @throws Zend_Http_Client_Adapter_Exception
126
     */
127
    public function write(
128
        $method, $uri, $http_ver = '1.1', $headers = array(), $body = ''
129
    )
130
    {
131
        // If no proxy is set, fall back to default Socket adapter
132
        if (!$this->config['proxy_host']) {
133
            return parent::write($method, $uri, $http_ver, $headers, $body);
134
        }
135
136
        // Make sure we're properly connected
137
        if (!$this->socket) {
138
            require_once 'Zend/Http/Client/Adapter/Exception.php';
139
            throw new Zend_Http_Client_Adapter_Exception(
140
                'Trying to write but we are not connected'
141
            );
142
        }
143
144
        $host = $this->config['proxy_host'];
145
        $port = $this->config['proxy_port'];
146
147
        if ($this->connected_to[0] != "tcp://$host"
148
            || $this->connected_to[1] != $port
149
        ) {
150
            require_once 'Zend/Http/Client/Adapter/Exception.php';
151
            throw new Zend_Http_Client_Adapter_Exception(
152
                'Trying to write but we are connected to the wrong proxy server'
153
            );
154
        }
155
156
        // Add Proxy-Authorization header
157
        if ($this->config['proxy_user']) {
158
            // Check to see if one already exists
159
            $hasProxyAuthHeader = false;
160
            foreach ($headers as $k => $v) {
161
                if ((string) $k == 'proxy-authorization'
162
                    || preg_match("/^proxy-authorization:/i", $v)
163
                ) {
164
                    $hasProxyAuthHeader = true;
165
                    break;
166
                }
167
            }
168
            if (!$hasProxyAuthHeader) {
169
                $headers[] = 'Proxy-authorization: '
170
                    . Zend_Http_Client::encodeAuthHeader(
171
                        $this->config['proxy_user'],
172
                        $this->config['proxy_pass'], $this->config['proxy_auth']
173
                    );
174
            }
175
        }
176
177
        // if we are proxying HTTPS, preform CONNECT handshake with the proxy
178
        if ($uri->getScheme() == 'https' && (!$this->negotiated)) {
179
            $this->connectHandshake(
180
                $uri->getHost(), $uri->getPort(), $http_ver, $headers
0 ignored issues
show
Bug introduced by
$uri->getPort() of type string is incompatible with the type integer expected by parameter $port of Zend_Http_Client_Adapter_Proxy::connectHandshake(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

180
                $uri->getHost(), /** @scrutinizer ignore-type */ $uri->getPort(), $http_ver, $headers
Loading history...
181
            );
182
            $this->negotiated = true;
183
        }
184
185
        // Save request method for later
186
        $this->method = $method;
187
188
        // Build request headers
189
        if ($this->negotiated) {
190
            $path = $uri->getPath();
191
            if ($uri->getQuery()) {
192
                $path .= '?' . $uri->getQuery();
193
            }
194
            $request = "$method $path HTTP/$http_ver\r\n";
195
        } else {
196
            $request = "$method $uri HTTP/$http_ver\r\n";
197
        }
198
199
        // Add all headers to the request string
200
        foreach ($headers as $k => $v) {
201
            if (is_string($k)) $v = "$k: $v";
202
            $request .= "$v\r\n";
203
        }
204
205
        if(is_resource($body)) {
0 ignored issues
show
introduced by
The condition is_resource($body) is always false.
Loading history...
206
            $request .= "\r\n";
207
        } else {
208
            // Add the request body
209
            $request .= "\r\n" . $body;
210
        }
211
212
        // Send the request
213
        if (!@fwrite($this->socket, $request)) {
214
            require_once 'Zend/Http/Client/Adapter/Exception.php';
215
            throw new Zend_Http_Client_Adapter_Exception(
216
                'Error writing request to proxy server'
217
            );
218
        }
219
220
        if(is_resource($body)) {
0 ignored issues
show
introduced by
The condition is_resource($body) is always false.
Loading history...
221
            if(stream_copy_to_stream($body, $this->socket) == 0) {
222
                require_once 'Zend/Http/Client/Adapter/Exception.php';
223
                throw new Zend_Http_Client_Adapter_Exception(
224
                    'Error writing request to server'
225
                );
226
            }
227
        }
228
229
        return $request;
230
    }
231
232
    /**
233
     * Preform handshaking with HTTPS proxy using CONNECT method
234
     *
235
     * @param string  $host
236
     * @param integer $port
237
     * @param string  $http_ver
238
     * @param array   $headers
239
     * @return void
240
     * @throws Zend_Http_Client_Adapter_Exception
241
     */
242
    protected function connectHandshake(
243
        $host, $port = 443, $http_ver = '1.1', array &$headers = array()
244
    )
245
    {
246
        $request = "CONNECT $host:$port HTTP/$http_ver\r\n" .
247
                   "Host: " . $this->config['proxy_host'] . "\r\n";
248
249
        // Process provided headers, including important ones to CONNECT request
250
        foreach ($headers as $k => $v) {
251
            switch (strtolower(substr($v,0,strpos($v,':')))) {
252
                case 'proxy-authorization':
253
                    // break intentionally omitted
254
255
                case 'user-agent':
256
                    $request .= $v . "\r\n";
257
                    break;
258
259
                default:
260
                    break;
261
            }
262
        }
263
        $request .= "\r\n";
264
        
265
        // @see ZF-3189
266
        $this->connectHandshakeRequest = $request;
267
268
        // Send the request
269
        if (!@fwrite($this->socket, $request)) {
270
            require_once 'Zend/Http/Client/Adapter/Exception.php';
271
            throw new Zend_Http_Client_Adapter_Exception(
272
                'Error writing request to proxy server'
273
            );
274
        }
275
276
        // Read response headers only
277
        $response = '';
278
        $gotStatus = false;
279
        while ($line = @fgets($this->socket)) {
280
            $gotStatus = $gotStatus || (strpos($line, 'HTTP') !== false);
281
            if ($gotStatus) {
282
                $response .= $line;
283
                if (!chop($line)) {
284
                    break;
285
                }
286
            }
287
        }
288
289
        // Check that the response from the proxy is 200
290
        if (Zend_Http_Response::extractCode($response) != 200) {
291
            require_once 'Zend/Http/Client/Adapter/Exception.php';
292
            throw new Zend_Http_Client_Adapter_Exception(
293
                'Unable to connect to HTTPS proxy. Server response: ' . $response
294
            );
295
        }
296
297
        // If all is good, switch socket to secure mode. We have to fall back
298
        // through the different modes
299
        $modes = array(
300
            STREAM_CRYPTO_METHOD_TLS_CLIENT,
301
            STREAM_CRYPTO_METHOD_SSLv3_CLIENT,
302
            STREAM_CRYPTO_METHOD_SSLv23_CLIENT,
303
            STREAM_CRYPTO_METHOD_SSLv2_CLIENT
304
        );
305
306
        $success = false;
307
        foreach($modes as $mode) {
308
            $success = stream_socket_enable_crypto($this->socket, true, $mode);
309
            if ($success) {
310
                break;
311
            }
312
        }
313
314
        if (!$success) {
315
            require_once 'Zend/Http/Client/Adapter/Exception.php';
316
            throw new Zend_Http_Client_Adapter_Exception(
317
                'Unable to connect to HTTPS server through proxy: could not '
318
                . 'negotiate secure connection.'
319
            );
320
        }
321
    }
322
323
    /**
324
     * Close the connection to the server
325
     *
326
     */
327
    public function close()
328
    {
329
        parent::close();
330
        $this->negotiated = false;
331
    }
332
333
    /**
334
     * Destructor: make sure the socket is disconnected
335
     *
336
     */
337
    public function __destruct()
338
    {
339
        if ($this->socket) {
340
            $this->close();
341
        }
342
    }
343
}
344