[ Index ] |
PHP Cross Reference of moodle-2.8 |
[Summary view] [Print] [Text view]
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-2010 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_Adapter_Interface 30 */ 31 require_once 'Zend/Http/Client/Adapter/Interface.php'; 32 /** 33 * @see Zend_Http_Client_Adapter_Stream 34 */ 35 require_once 'Zend/Http/Client/Adapter/Stream.php'; 36 37 /** 38 * A sockets based (stream_socket_client) adapter class for Zend_Http_Client. Can be used 39 * on almost every PHP environment, and does not require any special extensions. 40 * 41 * @category Zend 42 * @package Zend_Http 43 * @subpackage Client_Adapter 44 * @copyright Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com) 45 * @license http://framework.zend.com/license/new-bsd New BSD License 46 */ 47 class Zend_Http_Client_Adapter_Socket implements Zend_Http_Client_Adapter_Interface, Zend_Http_Client_Adapter_Stream 48 { 49 /** 50 * The socket for server connection 51 * 52 * @var resource|null 53 */ 54 protected $socket = null; 55 56 /** 57 * What host/port are we connected to? 58 * 59 * @var array 60 */ 61 protected $connected_to = array(null, null); 62 63 /** 64 * Stream for storing output 65 * 66 * @var resource 67 */ 68 protected $out_stream = null; 69 70 /** 71 * Parameters array 72 * 73 * @var array 74 */ 75 protected $config = array( 76 'persistent' => false, 77 'ssltransport' => 'ssl', 78 'sslcert' => null, 79 'sslpassphrase' => null, 80 'sslusecontext' => false 81 ); 82 83 /** 84 * Request method - will be set by write() and might be used by read() 85 * 86 * @var string 87 */ 88 protected $method = null; 89 90 /** 91 * Stream context 92 * 93 * @var resource 94 */ 95 protected $_context = null; 96 97 /** 98 * Adapter constructor, currently empty. Config is set using setConfig() 99 * 100 */ 101 public function __construct() 102 { 103 } 104 105 /** 106 * Set the configuration array for the adapter 107 * 108 * @param Zend_Config | array $config 109 */ 110 public function setConfig($config = array()) 111 { 112 if ($config instanceof Zend_Config) { 113 $config = $config->toArray(); 114 115 } elseif (! is_array($config)) { 116 require_once 'Zend/Http/Client/Adapter/Exception.php'; 117 throw new Zend_Http_Client_Adapter_Exception( 118 'Array or Zend_Config object expected, got ' . gettype($config) 119 ); 120 } 121 122 foreach ($config as $k => $v) { 123 $this->config[strtolower($k)] = $v; 124 } 125 } 126 127 /** 128 * Retrieve the array of all configuration options 129 * 130 * @return array 131 */ 132 public function getConfig() 133 { 134 return $this->config; 135 } 136 137 /** 138 * Set the stream context for the TCP connection to the server 139 * 140 * Can accept either a pre-existing stream context resource, or an array 141 * of stream options, similar to the options array passed to the 142 * stream_context_create() PHP function. In such case a new stream context 143 * will be created using the passed options. 144 * 145 * @since Zend Framework 1.9 146 * 147 * @param mixed $context Stream context or array of context options 148 * @return Zend_Http_Client_Adapter_Socket 149 */ 150 public function setStreamContext($context) 151 { 152 if (is_resource($context) && get_resource_type($context) == 'stream-context') { 153 $this->_context = $context; 154 155 } elseif (is_array($context)) { 156 $this->_context = stream_context_create($context); 157 158 } else { 159 // Invalid parameter 160 require_once 'Zend/Http/Client/Adapter/Exception.php'; 161 throw new Zend_Http_Client_Adapter_Exception( 162 "Expecting either a stream context resource or array, got " . gettype($context) 163 ); 164 } 165 166 return $this; 167 } 168 169 /** 170 * Get the stream context for the TCP connection to the server. 171 * 172 * If no stream context is set, will create a default one. 173 * 174 * @return resource 175 */ 176 public function getStreamContext() 177 { 178 if (! $this->_context) { 179 $this->_context = stream_context_create(); 180 } 181 182 return $this->_context; 183 } 184 185 /** 186 * Connect to the remote server 187 * 188 * @param string $host 189 * @param int $port 190 * @param boolean $secure 191 */ 192 public function connect($host, $port = 80, $secure = false) 193 { 194 // If the URI should be accessed via SSL, prepend the Hostname with ssl:// 195 $host = ($secure ? $this->config['ssltransport'] : 'tcp') . '://' . $host; 196 197 // If we are connected to the wrong host, disconnect first 198 if (($this->connected_to[0] != $host || $this->connected_to[1] != $port)) { 199 if (is_resource($this->socket)) $this->close(); 200 } 201 202 // Now, if we are not connected, connect 203 if (! is_resource($this->socket) || ! $this->config['keepalive']) { 204 $context = $this->getStreamContext(); 205 if ($secure || $this->config['sslusecontext']) { 206 if ($this->config['sslcert'] !== null) { 207 if (! stream_context_set_option($context, 'ssl', 'local_cert', 208 $this->config['sslcert'])) { 209 require_once 'Zend/Http/Client/Adapter/Exception.php'; 210 throw new Zend_Http_Client_Adapter_Exception('Unable to set sslcert option'); 211 } 212 } 213 if ($this->config['sslpassphrase'] !== null) { 214 if (! stream_context_set_option($context, 'ssl', 'passphrase', 215 $this->config['sslpassphrase'])) { 216 require_once 'Zend/Http/Client/Adapter/Exception.php'; 217 throw new Zend_Http_Client_Adapter_Exception('Unable to set sslpassphrase option'); 218 } 219 } 220 } 221 222 $flags = STREAM_CLIENT_CONNECT; 223 if ($this->config['persistent']) $flags |= STREAM_CLIENT_PERSISTENT; 224 225 $this->socket = @stream_socket_client($host . ':' . $port, 226 $errno, 227 $errstr, 228 (int) $this->config['timeout'], 229 $flags, 230 $context); 231 232 if (! $this->socket) { 233 $this->close(); 234 require_once 'Zend/Http/Client/Adapter/Exception.php'; 235 throw new Zend_Http_Client_Adapter_Exception( 236 'Unable to Connect to ' . $host . ':' . $port . '. Error #' . $errno . ': ' . $errstr); 237 } 238 239 // Set the stream timeout 240 if (! stream_set_timeout($this->socket, (int) $this->config['timeout'])) { 241 require_once 'Zend/Http/Client/Adapter/Exception.php'; 242 throw new Zend_Http_Client_Adapter_Exception('Unable to set the connection timeout'); 243 } 244 245 // Update connected_to 246 $this->connected_to = array($host, $port); 247 } 248 } 249 250 /** 251 * Send request to the remote server 252 * 253 * @param string $method 254 * @param Zend_Uri_Http $uri 255 * @param string $http_ver 256 * @param array $headers 257 * @param string $body 258 * @return string Request as string 259 */ 260 public function write($method, $uri, $http_ver = '1.1', $headers = array(), $body = '') 261 { 262 // Make sure we're properly connected 263 if (! $this->socket) { 264 require_once 'Zend/Http/Client/Adapter/Exception.php'; 265 throw new Zend_Http_Client_Adapter_Exception('Trying to write but we are not connected'); 266 } 267 268 $host = $uri->getHost(); 269 $host = (strtolower($uri->getScheme()) == 'https' ? $this->config['ssltransport'] : 'tcp') . '://' . $host; 270 if ($this->connected_to[0] != $host || $this->connected_to[1] != $uri->getPort()) { 271 require_once 'Zend/Http/Client/Adapter/Exception.php'; 272 throw new Zend_Http_Client_Adapter_Exception('Trying to write but we are connected to the wrong host'); 273 } 274 275 // Save request method for later 276 $this->method = $method; 277 278 // Build request headers 279 $path = $uri->getPath(); 280 if ($uri->getQuery()) $path .= '?' . $uri->getQuery(); 281 $request = "{$method} {$path} HTTP/{$http_ver}\r\n"; 282 foreach ($headers as $k => $v) { 283 if (is_string($k)) $v = ucfirst($k) . ": $v"; 284 $request .= "$v\r\n"; 285 } 286 287 if(is_resource($body)) { 288 $request .= "\r\n"; 289 } else { 290 // Add the request body 291 $request .= "\r\n" . $body; 292 } 293 294 // Send the request 295 if (! @fwrite($this->socket, $request)) { 296 require_once 'Zend/Http/Client/Adapter/Exception.php'; 297 throw new Zend_Http_Client_Adapter_Exception('Error writing request to server'); 298 } 299 300 if(is_resource($body)) { 301 if(stream_copy_to_stream($body, $this->socket) == 0) { 302 require_once 'Zend/Http/Client/Adapter/Exception.php'; 303 throw new Zend_Http_Client_Adapter_Exception('Error writing request to server'); 304 } 305 } 306 307 return $request; 308 } 309 310 /** 311 * Read response from server 312 * 313 * @return string 314 */ 315 public function read() 316 { 317 // First, read headers only 318 $response = ''; 319 $gotStatus = false; 320 $stream = !empty($this->config['stream']); 321 322 while (($line = @fgets($this->socket)) !== false) { 323 $gotStatus = $gotStatus || (strpos($line, 'HTTP') !== false); 324 if ($gotStatus) { 325 $response .= $line; 326 if (rtrim($line) === '') break; 327 } 328 } 329 330 $this->_checkSocketReadTimeout(); 331 332 $statusCode = Zend_Http_Response::extractCode($response); 333 334 // Handle 100 and 101 responses internally by restarting the read again 335 if ($statusCode == 100 || $statusCode == 101) return $this->read(); 336 337 // Check headers to see what kind of connection / transfer encoding we have 338 $headers = Zend_Http_Response::extractHeaders($response); 339 340 /** 341 * Responses to HEAD requests and 204 or 304 responses are not expected 342 * to have a body - stop reading here 343 */ 344 if ($statusCode == 304 || $statusCode == 204 || 345 $this->method == Zend_Http_Client::HEAD) { 346 347 // Close the connection if requested to do so by the server 348 if (isset($headers['connection']) && $headers['connection'] == 'close') { 349 $this->close(); 350 } 351 return $response; 352 } 353 354 // If we got a 'transfer-encoding: chunked' header 355 if (isset($headers['transfer-encoding'])) { 356 357 if (strtolower($headers['transfer-encoding']) == 'chunked') { 358 359 do { 360 $line = @fgets($this->socket); 361 $this->_checkSocketReadTimeout(); 362 363 $chunk = $line; 364 365 // Figure out the next chunk size 366 $chunksize = trim($line); 367 if (! ctype_xdigit($chunksize)) { 368 $this->close(); 369 require_once 'Zend/Http/Client/Adapter/Exception.php'; 370 throw new Zend_Http_Client_Adapter_Exception('Invalid chunk size "' . 371 $chunksize . '" unable to read chunked body'); 372 } 373 374 // Convert the hexadecimal value to plain integer 375 $chunksize = hexdec($chunksize); 376 377 // Read next chunk 378 $read_to = ftell($this->socket) + $chunksize; 379 380 do { 381 $current_pos = ftell($this->socket); 382 if ($current_pos >= $read_to) break; 383 384 if($this->out_stream) { 385 if(stream_copy_to_stream($this->socket, $this->out_stream, $read_to - $current_pos) == 0) { 386 $this->_checkSocketReadTimeout(); 387 break; 388 } 389 } else { 390 $line = @fread($this->socket, $read_to - $current_pos); 391 if ($line === false || strlen($line) === 0) { 392 $this->_checkSocketReadTimeout(); 393 break; 394 } 395 $chunk .= $line; 396 } 397 } while (! feof($this->socket)); 398 399 $chunk .= @fgets($this->socket); 400 $this->_checkSocketReadTimeout(); 401 402 if(!$this->out_stream) { 403 $response .= $chunk; 404 } 405 } while ($chunksize > 0); 406 } else { 407 $this->close(); 408 throw new Zend_Http_Client_Adapter_Exception('Cannot handle "' . 409 $headers['transfer-encoding'] . '" transfer encoding'); 410 } 411 412 // We automatically decode chunked-messages when writing to a stream 413 // this means we have to disallow the Zend_Http_Response to do it again 414 if ($this->out_stream) { 415 $response = str_ireplace("Transfer-Encoding: chunked\r\n", '', $response); 416 } 417 // Else, if we got the content-length header, read this number of bytes 418 } elseif (isset($headers['content-length'])) { 419 420 // If we got more than one Content-Length header (see ZF-9404) use 421 // the last value sent 422 if (is_array($headers['content-length'])) { 423 $contentLength = $headers['content-length'][count($headers['content-length']) - 1]; 424 } else { 425 $contentLength = $headers['content-length']; 426 } 427 428 $current_pos = ftell($this->socket); 429 $chunk = ''; 430 431 for ($read_to = $current_pos + $contentLength; 432 $read_to > $current_pos; 433 $current_pos = ftell($this->socket)) { 434 435 if($this->out_stream) { 436 if(@stream_copy_to_stream($this->socket, $this->out_stream, $read_to - $current_pos) == 0) { 437 $this->_checkSocketReadTimeout(); 438 break; 439 } 440 } else { 441 $chunk = @fread($this->socket, $read_to - $current_pos); 442 if ($chunk === false || strlen($chunk) === 0) { 443 $this->_checkSocketReadTimeout(); 444 break; 445 } 446 447 $response .= $chunk; 448 } 449 450 // Break if the connection ended prematurely 451 if (feof($this->socket)) break; 452 } 453 454 // Fallback: just read the response until EOF 455 } else { 456 457 do { 458 if($this->out_stream) { 459 if(@stream_copy_to_stream($this->socket, $this->out_stream) == 0) { 460 $this->_checkSocketReadTimeout(); 461 break; 462 } 463 } else { 464 $buff = @fread($this->socket, 8192); 465 if ($buff === false || strlen($buff) === 0) { 466 $this->_checkSocketReadTimeout(); 467 break; 468 } else { 469 $response .= $buff; 470 } 471 } 472 473 } while (feof($this->socket) === false); 474 475 $this->close(); 476 } 477 478 // Close the connection if requested to do so by the server 479 if (isset($headers['connection']) && $headers['connection'] == 'close') { 480 $this->close(); 481 } 482 483 return $response; 484 } 485 486 /** 487 * Close the connection to the server 488 * 489 */ 490 public function close() 491 { 492 if (is_resource($this->socket)) @fclose($this->socket); 493 $this->socket = null; 494 $this->connected_to = array(null, null); 495 } 496 497 /** 498 * Check if the socket has timed out - if so close connection and throw 499 * an exception 500 * 501 * @throws Zend_Http_Client_Adapter_Exception with READ_TIMEOUT code 502 */ 503 protected function _checkSocketReadTimeout() 504 { 505 if ($this->socket) { 506 $info = stream_get_meta_data($this->socket); 507 $timedout = $info['timed_out']; 508 if ($timedout) { 509 $this->close(); 510 require_once 'Zend/Http/Client/Adapter/Exception.php'; 511 throw new Zend_Http_Client_Adapter_Exception( 512 "Read timed out after {$this->config['timeout']} seconds", 513 Zend_Http_Client_Adapter_Exception::READ_TIMEOUT 514 ); 515 } 516 } 517 } 518 519 /** 520 * Set output stream for the response 521 * 522 * @param resource $stream 523 * @return Zend_Http_Client_Adapter_Socket 524 */ 525 public function setOutputStream($stream) 526 { 527 $this->out_stream = $stream; 528 return $this; 529 } 530 531 /** 532 * Destructor: make sure the socket is disconnected 533 * 534 * If we are in persistent TCP mode, will not close the connection 535 * 536 */ 537 public function __destruct() 538 { 539 if (! $this->config['persistent']) { 540 if ($this->socket) $this->close(); 541 } 542 } 543 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
Generated: Fri Nov 28 20:29:05 2014 | Cross-referenced by PHPXref 0.7.1 |