source: trunk/www.guidonia.net/wp/wp-content/plugins/webtv/Drivers/Zend/Http/Client/Adapter/Socket.php@ 44

Last change on this file since 44 was 44, checked in by luciano, 14 years ago
File size: 12.0 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: Socket.php 13013 2008-12-04 12:04:24Z yoshida@zend.co.jp $
20 * @copyright Copyright (c) 2005-2008 Zend Technologies USA Inc. (http://www.zend.com)
21 * @license http://framework.zend.com/license/new-bsd New BSD License
22 */
23
24require_once 'Zend/Uri/Http.php';
25require_once 'Zend/Http/Client/Adapter/Interface.php';
26
27/**
28 * A sockets based (stream_socket_client) adapter class for Zend_Http_Client. Can be used
29 * on almost every PHP environment, and does not require any special extensions.
30 *
31 * @category Zend
32 * @package Zend_Http
33 * @subpackage Client_Adapter
34 * @copyright Copyright (c) 2005-2008 Zend Technologies USA Inc. (http://www.zend.com)
35 * @license http://framework.zend.com/license/new-bsd New BSD License
36 */
37class Zend_Http_Client_Adapter_Socket implements Zend_Http_Client_Adapter_Interface
38{
39 /**
40 * The socket for server connection
41 *
42 * @var resource|null
43 */
44 protected $socket = null;
45
46 /**
47 * What host/port are we connected to?
48 *
49 * @var array
50 */
51 protected $connected_to = array(null, null);
52
53 /**
54 * Parameters array
55 *
56 * @var array
57 */
58 protected $config = array(
59 'persistent' => false,
60 'ssltransport' => 'ssl',
61 'sslcert' => null,
62 'sslpassphrase' => null
63 );
64
65 /**
66 * Request method - will be set by write() and might be used by read()
67 *
68 * @var string
69 */
70 protected $method = null;
71
72 /**
73 * Adapter constructor, currently empty. Config is set using setConfig()
74 *
75 */
76 public function __construct()
77 {
78 }
79
80 /**
81 * Set the configuration array for the adapter
82 *
83 * @param array $config
84 */
85 public function setConfig($config = array())
86 {
87 if (! is_array($config)) {
88 require_once 'Zend/Http/Client/Adapter/Exception.php';
89 throw new Zend_Http_Client_Adapter_Exception(
90 '$concig expects an array, ' . gettype($config) . ' recieved.');
91 }
92
93 foreach ($config as $k => $v) {
94 $this->config[strtolower($k)] = $v;
95 }
96 }
97
98 /**
99 * Connect to the remote server
100 *
101 * @param string $host
102 * @param int $port
103 * @param boolean $secure
104 * @param int $timeout
105 */
106 public function connect($host, $port = 80, $secure = false)
107 {
108 // If the URI should be accessed via SSL, prepend the Hostname with ssl://
109 $host = ($secure ? $this->config['ssltransport'] : 'tcp') . '://' . $host;
110
111 // If we are connected to the wrong host, disconnect first
112 if (($this->connected_to[0] != $host || $this->connected_to[1] != $port)) {
113 if (is_resource($this->socket)) $this->close();
114 }
115
116 // Now, if we are not connected, connect
117 if (! is_resource($this->socket) || ! $this->config['keepalive']) {
118 $context = stream_context_create();
119 if ($secure) {
120 if ($this->config['sslcert'] !== null) {
121 if (! stream_context_set_option($context, 'ssl', 'local_cert',
122 $this->config['sslcert'])) {
123 require_once 'Zend/Http/Client/Adapter/Exception.php';
124 throw new Zend_Http_Client_Adapter_Exception('Unable to set sslcert option');
125 }
126 }
127 if ($this->config['sslpassphrase'] !== null) {
128 if (! stream_context_set_option($context, 'ssl', 'passphrase',
129 $this->config['sslpassphrase'])) {
130 require_once 'Zend/Http/Client/Adapter/Exception.php';
131 throw new Zend_Http_Client_Adapter_Exception('Unable to set sslpassphrase option');
132 }
133 }
134 }
135
136 $flags = STREAM_CLIENT_CONNECT;
137 if ($this->config['persistent']) $flags |= STREAM_CLIENT_PERSISTENT;
138
139 $this->socket = @stream_socket_client($host . ':' . $port,
140 $errno,
141 $errstr,
142 (int) $this->config['timeout'],
143 $flags,
144 $context);
145 if (! $this->socket) {
146 $this->close();
147 require_once 'Zend/Http/Client/Adapter/Exception.php';
148 throw new Zend_Http_Client_Adapter_Exception(
149 'Unable to Connect to ' . $host . ':' . $port . '. Error #' . $errno . ': ' . $errstr);
150 }
151
152 // Set the stream timeout
153 if (! stream_set_timeout($this->socket, (int) $this->config['timeout'])) {
154 require_once 'Zend/Http/Client/Adapter/Exception.php';
155 throw new Zend_Http_Client_Adapter_Exception('Unable to set the connection timeout');
156 }
157
158 // Update connected_to
159 $this->connected_to = array($host, $port);
160 }
161 }
162
163 /**
164 * Send request to the remote server
165 *
166 * @param string $method
167 * @param Zend_Uri_Http $uri
168 * @param string $http_ver
169 * @param array $headers
170 * @param string $body
171 * @return string Request as string
172 */
173 public function write($method, $uri, $http_ver = '1.1', $headers = array(), $body = '')
174 {
175 // Make sure we're properly connected
176 if (! $this->socket) {
177 require_once 'Zend/Http/Client/Adapter/Exception.php';
178 throw new Zend_Http_Client_Adapter_Exception('Trying to write but we are not connected');
179 }
180
181 $host = $uri->getHost();
182 $host = (strtolower($uri->getScheme()) == 'https' ? $this->config['ssltransport'] : 'tcp') . '://' . $host;
183 if ($this->connected_to[0] != $host || $this->connected_to[1] != $uri->getPort()) {
184 require_once 'Zend/Http/Client/Adapter/Exception.php';
185 throw new Zend_Http_Client_Adapter_Exception('Trying to write but we are connected to the wrong host');
186 }
187
188 // Save request method for later
189 $this->method = $method;
190
191 // Build request headers
192 $path = $uri->getPath();
193 if ($uri->getQuery()) $path .= '?' . $uri->getQuery();
194 $request = "{$method} {$path} HTTP/{$http_ver}\r\n";
195 foreach ($headers as $k => $v) {
196 if (is_string($k)) $v = ucfirst($k) . ": $v";
197 $request .= "$v\r\n";
198 }
199
200 // Add the request body
201 $request .= "\r\n" . $body;
202
203 // Send the request
204 if (! @fwrite($this->socket, $request)) {
205 require_once 'Zend/Http/Client/Adapter/Exception.php';
206 throw new Zend_Http_Client_Adapter_Exception('Error writing request to server');
207 }
208
209 return $request;
210 }
211
212 /**
213 * Read response from server
214 *
215 * @return string
216 */
217 public function read()
218 {
219 // First, read headers only
220 $response = '';
221 $gotStatus = false;
222 while (($line = @fgets($this->socket)) !== false) {
223 $gotStatus = $gotStatus || (strpos($line, 'HTTP') !== false);
224 if ($gotStatus) {
225 $response .= $line;
226 if (rtrim($line) === '') break;
227 }
228 }
229
230 $statusCode = Zend_Http_Response::extractCode($response);
231
232 // Handle 100 and 101 responses internally by restarting the read again
233 if ($statusCode == 100 || $statusCode == 101) return $this->read();
234
235 /**
236 * Responses to HEAD requests and 204 or 304 responses are not expected
237 * to have a body - stop reading here
238 */
239 if ($statusCode == 304 || $statusCode == 204 ||
240 $this->method == Zend_Http_Client::HEAD) return $response;
241
242 // Check headers to see what kind of connection / transfer encoding we have
243 $headers = Zend_Http_Response::extractHeaders($response);
244
245 // If we got a 'transfer-encoding: chunked' header
246 if (isset($headers['transfer-encoding'])) {
247 if ($headers['transfer-encoding'] == 'chunked') {
248 do {
249 $line = @fgets($this->socket);
250 $chunk = $line;
251
252 // Figure out the next chunk size
253 $chunksize = trim($line);
254 if (! ctype_xdigit($chunksize)) {
255 $this->close();
256 require_once 'Zend/Http/Client/Adapter/Exception.php';
257 throw new Zend_Http_Client_Adapter_Exception('Invalid chunk size "' .
258 $chunksize . '" unable to read chunked body');
259 }
260
261 // Convert the hexadecimal value to plain integer
262 $chunksize = hexdec($chunksize);
263
264 // Read chunk
265 $left_to_read = $chunksize;
266 while ($left_to_read > 0) {
267 $line = @fread($this->socket, $left_to_read);
268 if ($line === false || strlen($line) === 0)
269 {
270 break;
271 } else {
272 $chunk .= $line;
273 $left_to_read -= strlen($line);
274 }
275
276 // Break if the connection ended prematurely
277 if (feof($this->socket)) break;
278 }
279
280 $chunk .= @fgets($this->socket);
281 $response .= $chunk;
282 } while ($chunksize > 0);
283
284 } else {
285 throw new Zend_Http_Client_Adapter_Exception('Cannot handle "' .
286 $headers['transfer-encoding'] . '" transfer encoding');
287 }
288
289 // Else, if we got the content-length header, read this number of bytes
290 } elseif (isset($headers['content-length'])) {
291 $left_to_read = $headers['content-length'];
292 $chunk = '';
293 while ($left_to_read > 0) {
294 $chunk = @fread($this->socket, $left_to_read);
295 if ($chunk === false || strlen($chunk) === 0)
296 {
297 break;
298 } else {
299 $left_to_read -= strlen($chunk);
300 $response .= $chunk;
301 }
302
303 // Break if the connection ended prematurely
304 if (feof($this->socket)) break;
305 }
306
307 // Fallback: just read the response until EOF
308 } else {
309 do {
310 $buff = @fread($this->socket, 8192);
311 if ($buff === false || strlen($buff) === 0)
312 {
313 break;
314 } else {
315 $response .= $buff;
316 }
317 } while (feof($this->socket) === false);
318
319 $this->close();
320 }
321
322 // Close the connection if requested to do so by the server
323 if (isset($headers['connection']) && $headers['connection'] == 'close') {
324 $this->close();
325 }
326
327 return $response;
328 }
329
330 /**
331 * Close the connection to the server
332 *
333 */
334 public function close()
335 {
336 if (is_resource($this->socket)) @fclose($this->socket);
337 $this->socket = null;
338 $this->connected_to = array(null, null);
339 }
340
341 /**
342 * Destructor: make sure the socket is disconnected
343 *
344 * If we are in persistent TCP mode, will not close the connection
345 *
346 */
347 public function __destruct()
348 {
349 if (! $this->config['persistent']) {
350 if ($this->socket) $this->close();
351 }
352 }
353}
Note: See TracBrowser for help on using the repository browser.