14 * to license@zend.com so we can send you a copy immediately. |
14 * to license@zend.com so we can send you a copy immediately. |
15 * |
15 * |
16 * @category Zend |
16 * @category Zend |
17 * @package Zend_Http |
17 * @package Zend_Http |
18 * @subpackage Client_Adapter |
18 * @subpackage Client_Adapter |
19 * @version $Id: Socket.php 22576 2010-07-16 15:49:24Z dragonbe $ |
19 * @version $Id: Socket.php 24593 2012-01-05 20:35:02Z matthew $ |
20 * @copyright Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com) |
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 |
21 * @license http://framework.zend.com/license/new-bsd New BSD License |
22 */ |
22 */ |
23 |
23 |
24 /** |
24 /** |
25 * @see Zend_Uri_Http |
25 * @see Zend_Uri_Http |
39 * on almost every PHP environment, and does not require any special extensions. |
39 * on almost every PHP environment, and does not require any special extensions. |
40 * |
40 * |
41 * @category Zend |
41 * @category Zend |
42 * @package Zend_Http |
42 * @package Zend_Http |
43 * @subpackage Client_Adapter |
43 * @subpackage Client_Adapter |
44 * @copyright Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com) |
44 * @copyright Copyright (c) 2005-2012 Zend Technologies USA Inc. (http://www.zend.com) |
45 * @license http://framework.zend.com/license/new-bsd New BSD License |
45 * @license http://framework.zend.com/license/new-bsd New BSD License |
46 */ |
46 */ |
47 class Zend_Http_Client_Adapter_Socket implements Zend_Http_Client_Adapter_Interface, Zend_Http_Client_Adapter_Stream |
47 class Zend_Http_Client_Adapter_Socket implements Zend_Http_Client_Adapter_Interface, Zend_Http_Client_Adapter_Stream |
48 { |
48 { |
49 /** |
49 /** |
122 foreach ($config as $k => $v) { |
122 foreach ($config as $k => $v) { |
123 $this->config[strtolower($k)] = $v; |
123 $this->config[strtolower($k)] = $v; |
124 } |
124 } |
125 } |
125 } |
126 |
126 |
127 /** |
127 /** |
128 * Retrieve the array of all configuration options |
128 * Retrieve the array of all configuration options |
129 * |
129 * |
130 * @return array |
130 * @return array |
131 */ |
131 */ |
132 public function getConfig() |
132 public function getConfig() |
133 { |
133 { |
134 return $this->config; |
134 return $this->config; |
135 } |
135 } |
136 |
136 |
137 /** |
137 /** |
138 * Set the stream context for the TCP connection to the server |
138 * Set the stream context for the TCP connection to the server |
139 * |
139 * |
140 * Can accept either a pre-existing stream context resource, or an array |
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 |
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 |
142 * stream_context_create() PHP function. In such case a new stream context |
288 $request .= "\r\n"; |
288 $request .= "\r\n"; |
289 } else { |
289 } else { |
290 // Add the request body |
290 // Add the request body |
291 $request .= "\r\n" . $body; |
291 $request .= "\r\n" . $body; |
292 } |
292 } |
293 |
293 |
294 // Send the request |
294 // Send the request |
295 if (! @fwrite($this->socket, $request)) { |
295 if (! @fwrite($this->socket, $request)) { |
296 require_once 'Zend/Http/Client/Adapter/Exception.php'; |
296 require_once 'Zend/Http/Client/Adapter/Exception.php'; |
297 throw new Zend_Http_Client_Adapter_Exception('Error writing request to server'); |
297 throw new Zend_Http_Client_Adapter_Exception('Error writing request to server'); |
298 } |
298 } |
299 |
299 |
300 if(is_resource($body)) { |
300 if(is_resource($body)) { |
301 if(stream_copy_to_stream($body, $this->socket) == 0) { |
301 if(stream_copy_to_stream($body, $this->socket) == 0) { |
302 require_once 'Zend/Http/Client/Adapter/Exception.php'; |
302 require_once 'Zend/Http/Client/Adapter/Exception.php'; |
303 throw new Zend_Http_Client_Adapter_Exception('Error writing request to server'); |
303 throw new Zend_Http_Client_Adapter_Exception('Error writing request to server'); |
304 } |
304 } |
315 public function read() |
315 public function read() |
316 { |
316 { |
317 // First, read headers only |
317 // First, read headers only |
318 $response = ''; |
318 $response = ''; |
319 $gotStatus = false; |
319 $gotStatus = false; |
320 $stream = !empty($this->config['stream']); |
|
321 |
320 |
322 while (($line = @fgets($this->socket)) !== false) { |
321 while (($line = @fgets($this->socket)) !== false) { |
323 $gotStatus = $gotStatus || (strpos($line, 'HTTP') !== false); |
322 $gotStatus = $gotStatus || (strpos($line, 'HTTP') !== false); |
324 if ($gotStatus) { |
323 if ($gotStatus) { |
325 $response .= $line; |
324 $response .= $line; |
326 if (rtrim($line) === '') break; |
325 if (rtrim($line) === '') break; |
327 } |
326 } |
328 } |
327 } |
329 |
328 |
330 $this->_checkSocketReadTimeout(); |
329 $this->_checkSocketReadTimeout(); |
331 |
330 |
332 $statusCode = Zend_Http_Response::extractCode($response); |
331 $statusCode = Zend_Http_Response::extractCode($response); |
333 |
332 |
334 // Handle 100 and 101 responses internally by restarting the read again |
333 // Handle 100 and 101 responses internally by restarting the read again |
351 return $response; |
350 return $response; |
352 } |
351 } |
353 |
352 |
354 // If we got a 'transfer-encoding: chunked' header |
353 // If we got a 'transfer-encoding: chunked' header |
355 if (isset($headers['transfer-encoding'])) { |
354 if (isset($headers['transfer-encoding'])) { |
356 |
355 |
357 if (strtolower($headers['transfer-encoding']) == 'chunked') { |
356 if (strtolower($headers['transfer-encoding']) == 'chunked') { |
358 |
357 |
359 do { |
358 do { |
360 $line = @fgets($this->socket); |
359 $line = @fgets($this->socket); |
361 $this->_checkSocketReadTimeout(); |
360 $this->_checkSocketReadTimeout(); |
382 if ($current_pos >= $read_to) break; |
381 if ($current_pos >= $read_to) break; |
383 |
382 |
384 if($this->out_stream) { |
383 if($this->out_stream) { |
385 if(stream_copy_to_stream($this->socket, $this->out_stream, $read_to - $current_pos) == 0) { |
384 if(stream_copy_to_stream($this->socket, $this->out_stream, $read_to - $current_pos) == 0) { |
386 $this->_checkSocketReadTimeout(); |
385 $this->_checkSocketReadTimeout(); |
387 break; |
386 break; |
388 } |
387 } |
389 } else { |
388 } else { |
390 $line = @fread($this->socket, $read_to - $current_pos); |
389 $line = @fread($this->socket, $read_to - $current_pos); |
391 if ($line === false || strlen($line) === 0) { |
390 if ($line === false || strlen($line) === 0) { |
392 $this->_checkSocketReadTimeout(); |
391 $this->_checkSocketReadTimeout(); |
403 $response .= $chunk; |
402 $response .= $chunk; |
404 } |
403 } |
405 } while ($chunksize > 0); |
404 } while ($chunksize > 0); |
406 } else { |
405 } else { |
407 $this->close(); |
406 $this->close(); |
408 require_once 'Zend/Http/Client/Adapter/Exception.php'; |
407 require_once 'Zend/Http/Client/Adapter/Exception.php'; |
409 throw new Zend_Http_Client_Adapter_Exception('Cannot handle "' . |
408 throw new Zend_Http_Client_Adapter_Exception('Cannot handle "' . |
410 $headers['transfer-encoding'] . '" transfer encoding'); |
409 $headers['transfer-encoding'] . '" transfer encoding'); |
411 } |
410 } |
412 |
411 |
413 // We automatically decode chunked-messages when writing to a stream |
412 // We automatically decode chunked-messages when writing to a stream |
414 // this means we have to disallow the Zend_Http_Response to do it again |
413 // this means we have to disallow the Zend_Http_Response to do it again |
415 if ($this->out_stream) { |
414 if ($this->out_stream) { |
416 $response = str_ireplace("Transfer-Encoding: chunked\r\n", '', $response); |
415 $response = str_ireplace("Transfer-Encoding: chunked\r\n", '', $response); |
417 } |
416 } |
419 } elseif (isset($headers['content-length'])) { |
418 } elseif (isset($headers['content-length'])) { |
420 |
419 |
421 // If we got more than one Content-Length header (see ZF-9404) use |
420 // If we got more than one Content-Length header (see ZF-9404) use |
422 // the last value sent |
421 // the last value sent |
423 if (is_array($headers['content-length'])) { |
422 if (is_array($headers['content-length'])) { |
424 $contentLength = $headers['content-length'][count($headers['content-length']) - 1]; |
423 $contentLength = $headers['content-length'][count($headers['content-length']) - 1]; |
425 } else { |
424 } else { |
426 $contentLength = $headers['content-length']; |
425 $contentLength = $headers['content-length']; |
427 } |
426 } |
428 |
427 |
429 $current_pos = ftell($this->socket); |
428 $current_pos = ftell($this->socket); |
430 $chunk = ''; |
429 $chunk = ''; |
431 |
430 |
432 for ($read_to = $current_pos + $contentLength; |
431 for ($read_to = $current_pos + $contentLength; |
433 $read_to > $current_pos; |
432 $read_to > $current_pos; |
434 $current_pos = ftell($this->socket)) { |
433 $current_pos = ftell($this->socket)) { |
435 |
434 |
436 if($this->out_stream) { |
435 if($this->out_stream) { |
437 if(@stream_copy_to_stream($this->socket, $this->out_stream, $read_to - $current_pos) == 0) { |
436 if(@stream_copy_to_stream($this->socket, $this->out_stream, $read_to - $current_pos) == 0) { |
438 $this->_checkSocketReadTimeout(); |
437 $this->_checkSocketReadTimeout(); |
439 break; |
438 break; |
440 } |
439 } |
441 } else { |
440 } else { |
442 $chunk = @fread($this->socket, $read_to - $current_pos); |
441 $chunk = @fread($this->socket, $read_to - $current_pos); |
443 if ($chunk === false || strlen($chunk) === 0) { |
442 if ($chunk === false || strlen($chunk) === 0) { |
444 $this->_checkSocketReadTimeout(); |
443 $this->_checkSocketReadTimeout(); |
457 |
456 |
458 do { |
457 do { |
459 if($this->out_stream) { |
458 if($this->out_stream) { |
460 if(@stream_copy_to_stream($this->socket, $this->out_stream) == 0) { |
459 if(@stream_copy_to_stream($this->socket, $this->out_stream) == 0) { |
461 $this->_checkSocketReadTimeout(); |
460 $this->_checkSocketReadTimeout(); |
462 break; |
461 break; |
463 } |
462 } |
464 } else { |
463 } else { |
465 $buff = @fread($this->socket, 8192); |
464 $buff = @fread($this->socket, 8192); |
466 if ($buff === false || strlen($buff) === 0) { |
465 if ($buff === false || strlen($buff) === 0) { |
467 $this->_checkSocketReadTimeout(); |
466 $this->_checkSocketReadTimeout(); |
514 Zend_Http_Client_Adapter_Exception::READ_TIMEOUT |
513 Zend_Http_Client_Adapter_Exception::READ_TIMEOUT |
515 ); |
514 ); |
516 } |
515 } |
517 } |
516 } |
518 } |
517 } |
519 |
518 |
520 /** |
519 /** |
521 * Set output stream for the response |
520 * Set output stream for the response |
522 * |
521 * |
523 * @param resource $stream |
522 * @param resource $stream |
524 * @return Zend_Http_Client_Adapter_Socket |
523 * @return Zend_Http_Client_Adapter_Socket |
525 */ |
524 */ |
526 public function setOutputStream($stream) |
525 public function setOutputStream($stream) |
527 { |
526 { |
528 $this->out_stream = $stream; |
527 $this->out_stream = $stream; |
529 return $this; |
528 return $this; |
530 } |
529 } |
531 |
530 |
532 /** |
531 /** |
533 * Destructor: make sure the socket is disconnected |
532 * Destructor: make sure the socket is disconnected |
534 * |
533 * |
535 * If we are in persistent TCP mode, will not close the connection |
534 * If we are in persistent TCP mode, will not close the connection |
536 * |
535 * |