87 * @param boolean $secure |
94 * @param boolean $secure |
88 */ |
95 */ |
89 public function connect($host, $port = 80, $secure = false) |
96 public function connect($host, $port = 80, $secure = false) |
90 { |
97 { |
91 // If no proxy is set, fall back to Socket adapter |
98 // If no proxy is set, fall back to Socket adapter |
92 if (! $this->config['proxy_host']) { |
99 if (!$this->config['proxy_host']) { |
93 return parent::connect($host, $port, $secure); |
100 return parent::connect($host, $port, $secure); |
94 } |
101 } |
95 |
102 |
96 /* Url might require stream context even if proxy connection doesn't */ |
103 /* Url might require stream context even if proxy connection doesn't */ |
97 if ($secure) { |
104 if ($secure) { |
98 $this->config['sslusecontext'] = true; |
105 $this->config['sslusecontext'] = true; |
99 } |
106 } |
100 |
107 |
101 // Connect (a non-secure connection) to the proxy server |
108 // Connect (a non-secure connection) to the proxy server |
102 return parent::connect( |
109 return parent::connect( |
103 $this->config['proxy_host'], |
110 $this->config['proxy_host'], |
113 * @param Zend_Uri_Http $uri |
120 * @param Zend_Uri_Http $uri |
114 * @param string $http_ver |
121 * @param string $http_ver |
115 * @param array $headers |
122 * @param array $headers |
116 * @param string $body |
123 * @param string $body |
117 * @return string Request as string |
124 * @return string Request as string |
118 */ |
125 * @throws Zend_Http_Client_Adapter_Exception |
119 public function write($method, $uri, $http_ver = '1.1', $headers = array(), $body = '') |
126 */ |
|
127 public function write( |
|
128 $method, $uri, $http_ver = '1.1', $headers = array(), $body = '' |
|
129 ) |
120 { |
130 { |
121 // If no proxy is set, fall back to default Socket adapter |
131 // 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); |
132 if (!$this->config['proxy_host']) { |
|
133 return parent::write($method, $uri, $http_ver, $headers, $body); |
|
134 } |
123 |
135 |
124 // Make sure we're properly connected |
136 // Make sure we're properly connected |
125 if (! $this->socket) { |
137 if (!$this->socket) { |
126 require_once 'Zend/Http/Client/Adapter/Exception.php'; |
138 require_once 'Zend/Http/Client/Adapter/Exception.php'; |
127 throw new Zend_Http_Client_Adapter_Exception("Trying to write but we are not connected"); |
139 throw new Zend_Http_Client_Adapter_Exception( |
|
140 'Trying to write but we are not connected' |
|
141 ); |
128 } |
142 } |
129 |
143 |
130 $host = $this->config['proxy_host']; |
144 $host = $this->config['proxy_host']; |
131 $port = $this->config['proxy_port']; |
145 $port = $this->config['proxy_port']; |
132 |
146 |
133 if ($this->connected_to[0] != "tcp://$host" || $this->connected_to[1] != $port) { |
147 if ($this->connected_to[0] != "tcp://$host" |
134 require_once 'Zend/Http/Client/Adapter/Exception.php'; |
148 || $this->connected_to[1] != $port |
135 throw new Zend_Http_Client_Adapter_Exception("Trying to write but we are connected to the wrong proxy server"); |
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 ); |
136 } |
154 } |
137 |
155 |
138 // Add Proxy-Authorization header |
156 // Add Proxy-Authorization header |
139 if ($this->config['proxy_user'] && ! isset($headers['proxy-authorization'])) { |
157 if ($this->config['proxy_user']) { |
140 $headers['proxy-authorization'] = Zend_Http_Client::encodeAuthHeader( |
158 // Check to see if one already exists |
141 $this->config['proxy_user'], $this->config['proxy_pass'], $this->config['proxy_auth'] |
159 $hasProxyAuthHeader = false; |
142 ); |
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 } |
143 } |
175 } |
144 |
176 |
145 // if we are proxying HTTPS, preform CONNECT handshake with the proxy |
177 // if we are proxying HTTPS, preform CONNECT handshake with the proxy |
146 if ($uri->getScheme() == 'https' && (! $this->negotiated)) { |
178 if ($uri->getScheme() == 'https' && (!$this->negotiated)) { |
147 $this->connectHandshake($uri->getHost(), $uri->getPort(), $http_ver, $headers); |
179 $this->connectHandshake( |
|
180 $uri->getHost(), $uri->getPort(), $http_ver, $headers |
|
181 ); |
148 $this->negotiated = true; |
182 $this->negotiated = true; |
149 } |
183 } |
150 |
184 |
151 // Save request method for later |
185 // Save request method for later |
152 $this->method = $method; |
186 $this->method = $method; |
172 $request .= "\r\n"; |
206 $request .= "\r\n"; |
173 } else { |
207 } else { |
174 // Add the request body |
208 // Add the request body |
175 $request .= "\r\n" . $body; |
209 $request .= "\r\n" . $body; |
176 } |
210 } |
177 |
211 |
178 // Send the request |
212 // Send the request |
179 if (! @fwrite($this->socket, $request)) { |
213 if (!@fwrite($this->socket, $request)) { |
180 require_once 'Zend/Http/Client/Adapter/Exception.php'; |
214 require_once 'Zend/Http/Client/Adapter/Exception.php'; |
181 throw new Zend_Http_Client_Adapter_Exception("Error writing request to proxy server"); |
215 throw new Zend_Http_Client_Adapter_Exception( |
182 } |
216 'Error writing request to proxy server' |
183 |
217 ); |
|
218 } |
|
219 |
184 if(is_resource($body)) { |
220 if(is_resource($body)) { |
185 if(stream_copy_to_stream($body, $this->socket) == 0) { |
221 if(stream_copy_to_stream($body, $this->socket) == 0) { |
186 require_once 'Zend/Http/Client/Adapter/Exception.php'; |
222 require_once 'Zend/Http/Client/Adapter/Exception.php'; |
187 throw new Zend_Http_Client_Adapter_Exception('Error writing request to server'); |
223 throw new Zend_Http_Client_Adapter_Exception( |
188 } |
224 'Error writing request to server' |
189 } |
225 ); |
190 |
226 } |
|
227 } |
|
228 |
191 return $request; |
229 return $request; |
192 } |
230 } |
193 |
231 |
194 /** |
232 /** |
195 * Preform handshaking with HTTPS proxy using CONNECT method |
233 * Preform handshaking with HTTPS proxy using CONNECT method |
196 * |
234 * |
197 * @param string $host |
235 * @param string $host |
198 * @param integer $port |
236 * @param integer $port |
199 * @param string $http_ver |
237 * @param string $http_ver |
200 * @param array $headers |
238 * @param array $headers |
201 */ |
239 * @return void |
202 protected function connectHandshake($host, $port = 443, $http_ver = '1.1', array &$headers = array()) |
240 * @throws Zend_Http_Client_Adapter_Exception |
|
241 */ |
|
242 protected function connectHandshake( |
|
243 $host, $port = 443, $http_ver = '1.1', array &$headers = array() |
|
244 ) |
203 { |
245 { |
204 $request = "CONNECT $host:$port HTTP/$http_ver\r\n" . |
246 $request = "CONNECT $host:$port HTTP/$http_ver\r\n" . |
205 "Host: " . $this->config['proxy_host'] . "\r\n"; |
247 "Host: " . $this->config['proxy_host'] . "\r\n"; |
206 |
248 |
207 // Add the user-agent header |
249 // Process provided headers, including important ones to CONNECT request |
208 if (isset($this->config['useragent'])) { |
250 foreach ($headers as $k => $v) { |
209 $request .= "User-agent: " . $this->config['useragent'] . "\r\n"; |
251 switch (strtolower(substr($v,0,strpos($v,':')))) { |
210 } |
252 case 'proxy-authorization': |
211 |
253 // break intentionally omitted |
212 // If the proxy-authorization header is set, send it to proxy but remove |
254 |
213 // it from headers sent to target host |
255 case 'user-agent': |
214 if (isset($headers['proxy-authorization'])) { |
256 $request .= $v . "\r\n"; |
215 $request .= "Proxy-authorization: " . $headers['proxy-authorization'] . "\r\n"; |
257 break; |
216 unset($headers['proxy-authorization']); |
258 |
217 } |
259 default: |
218 |
260 break; |
|
261 } |
|
262 } |
219 $request .= "\r\n"; |
263 $request .= "\r\n"; |
|
264 |
|
265 // @see ZF-3189 |
|
266 $this->connectHandshakeRequest = $request; |
220 |
267 |
221 // Send the request |
268 // Send the request |
222 if (! @fwrite($this->socket, $request)) { |
269 if (!@fwrite($this->socket, $request)) { |
223 require_once 'Zend/Http/Client/Adapter/Exception.php'; |
270 require_once 'Zend/Http/Client/Adapter/Exception.php'; |
224 throw new Zend_Http_Client_Adapter_Exception("Error writing request to proxy server"); |
271 throw new Zend_Http_Client_Adapter_Exception( |
|
272 'Error writing request to proxy server' |
|
273 ); |
225 } |
274 } |
226 |
275 |
227 // Read response headers only |
276 // Read response headers only |
228 $response = ''; |
277 $response = ''; |
229 $gotStatus = false; |
278 $gotStatus = false; |
230 while ($line = @fgets($this->socket)) { |
279 while ($line = @fgets($this->socket)) { |
231 $gotStatus = $gotStatus || (strpos($line, 'HTTP') !== false); |
280 $gotStatus = $gotStatus || (strpos($line, 'HTTP') !== false); |
232 if ($gotStatus) { |
281 if ($gotStatus) { |
233 $response .= $line; |
282 $response .= $line; |
234 if (!chop($line)) break; |
283 if (!chop($line)) { |
|
284 break; |
|
285 } |
235 } |
286 } |
236 } |
287 } |
237 |
288 |
238 // Check that the response from the proxy is 200 |
289 // Check that the response from the proxy is 200 |
239 if (Zend_Http_Response::extractCode($response) != 200) { |
290 if (Zend_Http_Response::extractCode($response) != 200) { |
240 require_once 'Zend/Http/Client/Adapter/Exception.php'; |
291 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); |
292 throw new Zend_Http_Client_Adapter_Exception( |
|
293 'Unable to connect to HTTPS proxy. Server response: ' . $response |
|
294 ); |
242 } |
295 } |
243 |
296 |
244 // If all is good, switch socket to secure mode. We have to fall back |
297 // If all is good, switch socket to secure mode. We have to fall back |
245 // through the different modes |
298 // through the different modes |
246 $modes = array( |
299 $modes = array( |