2008-05-30 11:40:08 +00:00
< ? php
/**
* HTTP Socket connection class .
*
2010-10-03 16:38:58 +00:00
* PHP 5
2008-05-30 11:40:08 +00:00
*
2009-11-06 06:46:59 +00:00
* CakePHP ( tm ) : Rapid Development Framework ( http :// cakephp . org )
2010-01-26 19:18:20 +00:00
* Copyright 2005 - 2010 , Cake Software Foundation , Inc . ( http :// cakefoundation . org )
2008-05-30 11:40:08 +00:00
*
* Licensed under The MIT License
* Redistributions of files must retain the above copyright notice .
*
2010-01-26 19:18:20 +00:00
* @ copyright Copyright 2005 - 2010 , Cake Software Foundation , Inc . ( http :// cakefoundation . org )
2009-11-06 06:00:11 +00:00
* @ link http :// cakephp . org CakePHP ( tm ) Project
2010-12-24 18:57:20 +00:00
* @ package cake . libs
2008-10-30 17:30:26 +00:00
* @ since CakePHP ( tm ) v 1.2 . 0
2009-11-06 06:51:51 +00:00
* @ license MIT License ( http :// www . opensource . org / licenses / mit - license . php )
2008-05-30 11:40:08 +00:00
*/
2010-12-15 05:50:02 +00:00
App :: uses ( 'CakeSocket' , 'Network' );
2010-12-11 05:47:55 +00:00
App :: uses ( 'Router' , 'Routing' );
2009-07-24 19:18:37 +00:00
2008-05-30 11:40:08 +00:00
/**
* Cake network socket connection class .
*
2010-01-25 16:01:05 +00:00
* Core base class for HTTP network communication . HttpSocket can be used as an
* Object Oriented replacement for cURL in many places .
2008-05-30 11:40:08 +00:00
*
2010-12-24 18:57:20 +00:00
* @ package cake . libs
2008-05-30 11:40:08 +00:00
*/
class HttpSocket extends CakeSocket {
2009-07-24 19:18:37 +00:00
2008-05-30 11:40:08 +00:00
/**
2009-05-07 18:49:21 +00:00
* When one activates the $quirksMode by setting it to true , all checks meant to
* enforce RFC 2616 ( HTTP / 1.1 specs ) .
2008-05-30 11:40:08 +00:00
* will be disabled and additional measures to deal with non - standard responses will be enabled .
*
* @ var boolean
*/
2010-04-04 07:14:00 +00:00
public $quirksMode = false ;
2009-07-24 19:18:37 +00:00
2008-05-30 11:40:08 +00:00
/**
2010-12-06 14:04:00 +00:00
* Contain information about the last request ( read only )
2008-05-30 11:40:08 +00:00
*
* @ var array
*/
2010-04-04 07:14:00 +00:00
public $request = array (
2008-05-30 11:40:08 +00:00
'method' => 'GET' ,
'uri' => array (
'scheme' => 'http' ,
'host' => null ,
'port' => 80 ,
'user' => null ,
'pass' => null ,
'path' => null ,
'query' => null ,
'fragment' => null
),
'version' => '1.1' ,
'body' => '' ,
'line' => null ,
'header' => array (
'Connection' => 'close' ,
'User-Agent' => 'CakePHP'
),
'raw' => null ,
'cookies' => array ()
);
2009-07-24 19:18:37 +00:00
2008-05-30 11:40:08 +00:00
/**
2010-12-06 14:04:00 +00:00
* Contain information about the last response ( read only )
2010-12-04 02:58:49 +00:00
*
* @ var array
*/
2010-12-14 03:07:25 +00:00
public $response = null ;
2009-07-24 19:18:37 +00:00
2010-12-14 04:46:31 +00:00
/**
* Response classname
*
* @ var string
*/
public $responseClass = 'HttpResponse' ;
2009-07-24 19:18:37 +00:00
2008-05-30 11:40:08 +00:00
/**
2010-12-06 14:04:00 +00:00
* Configuration settings for the HttpSocket and the requests
2008-05-30 11:40:08 +00:00
*
* @ var array
*/
2010-04-04 07:14:00 +00:00
public $config = array (
2008-05-30 11:40:08 +00:00
'persistent' => false ,
2010-01-25 16:01:05 +00:00
'host' => 'localhost' ,
'protocol' => 'tcp' ,
'port' => 80 ,
'timeout' => 30 ,
2008-05-30 11:40:08 +00:00
'request' => array (
'uri' => array (
'scheme' => 'http' ,
'host' => 'localhost' ,
'port' => 80
),
2008-11-08 02:54:07 +00:00
'cookies' => array ()
2008-05-30 11:40:08 +00:00
)
);
2009-07-24 19:18:37 +00:00
2008-05-30 11:40:08 +00:00
/**
2010-12-03 02:46:11 +00:00
* Authentication settings
2008-05-30 11:40:08 +00:00
*
2010-12-03 02:46:11 +00:00
* @ var array
*/
protected $_auth = array ();
2010-12-04 01:10:07 +00:00
/**
* Proxy settings
*
* @ var array
*/
protected $_proxy = array ();
2010-12-10 14:08:49 +00:00
/**
* Resource to receive the content of request
*
* @ var mixed
2008-05-30 11:40:08 +00:00
*/
2010-12-10 14:08:49 +00:00
protected $_contentResource = null ;
2008-05-30 11:40:08 +00:00
/**
* Build an HTTP Socket using the specified configuration .
*
2010-01-25 16:01:05 +00:00
* You can use a url string to set the url and use default configurations for
* all other options :
*
2010-11-13 04:05:44 +00:00
* `$http = new HttpSockect('http://cakephp.org/');`
2010-01-25 16:01:05 +00:00
*
* Or use an array to configure multiple options :
*
* {{{
2010-11-13 04:05:44 +00:00
* $http = new HttpSocket ( array (
2010-01-25 16:01:05 +00:00
* 'host' => 'cakephp.org' ,
* 'timeout' => 20
* ));
* }}}
*
* See HttpSocket :: $config for options that can be used .
*
* @ param mixed $config Configuration information , either a string url or an array of options .
2008-05-30 11:40:08 +00:00
*/
2010-04-05 03:19:38 +00:00
public function __construct ( $config = array ()) {
2008-05-30 11:40:08 +00:00
if ( is_string ( $config )) {
2009-05-07 18:49:21 +00:00
$this -> _configUri ( $config );
2008-05-30 11:40:08 +00:00
} elseif ( is_array ( $config )) {
if ( isset ( $config [ 'request' ][ 'uri' ]) && is_string ( $config [ 'request' ][ 'uri' ])) {
2009-05-07 18:49:21 +00:00
$this -> _configUri ( $config [ 'request' ][ 'uri' ]);
2008-05-30 11:40:08 +00:00
unset ( $config [ 'request' ][ 'uri' ]);
}
$this -> config = Set :: merge ( $this -> config , $config );
}
parent :: __construct ( $this -> config );
}
2009-07-24 19:18:37 +00:00
2010-12-03 02:46:11 +00:00
/**
* Set authentication settings
*
2010-12-04 01:10:07 +00:00
* @ param string $method Authentication method ( ie . Basic , Digest ) . If empty , disable authentication
2010-12-03 02:46:11 +00:00
* @ param mixed $user Username for authentication . Can be an array with settings to authentication class
* @ param string $pass Password for authentication
* @ return void
*/
2010-12-13 01:48:04 +00:00
public function configAuth ( $method , $user = null , $pass = null ) {
2010-12-03 02:46:11 +00:00
if ( empty ( $method )) {
$this -> _auth = array ();
return ;
}
if ( is_array ( $user )) {
$this -> _auth = array ( $method => $user );
return ;
}
$this -> _auth = array ( $method => compact ( 'user' , 'pass' ));
}
2010-12-04 01:10:07 +00:00
/**
* Set proxy settings
*
* @ param mixed $host Proxy host . Can be an array with settings to authentication class
* @ param integer $port Port . Default 3128.
* @ param string $method Proxy method ( ie , Basic , Digest ) . If empty , disable proxy authentication
* @ param string $user Username if your proxy need authentication
* @ param string $pass Password to proxy authentication
* @ return void
*/
2010-12-13 01:48:04 +00:00
public function configProxy ( $host , $port = 3128 , $method = null , $user = null , $pass = null ) {
2010-12-04 01:10:07 +00:00
if ( empty ( $host )) {
$this -> _proxy = array ();
return ;
}
if ( is_array ( $host )) {
$this -> _proxy = $host + array ( 'host' => null );
return ;
}
$this -> _proxy = compact ( 'host' , 'port' , 'method' , 'user' , 'pass' );
}
2010-12-10 14:08:49 +00:00
/**
* Set the resource to receive the request content . This resource must support fwrite .
*
* @ param mixed $resource Resource or false to disable the resource use
* @ return void
2010-12-15 04:01:00 +00:00
* @ throw SocketException
2010-12-10 14:08:49 +00:00
*/
public function setContentResource ( $resource ) {
if ( $resource === false ) {
$this -> _contentResource = null ;
return ;
}
if ( ! is_resource ( $resource )) {
2011-03-20 15:35:43 +00:00
throw new SocketException ( __d ( 'cake_dev' , 'Invalid resource.' ));
2010-12-10 14:08:49 +00:00
}
$this -> _contentResource = $resource ;
}
2008-05-30 11:40:08 +00:00
/**
2010-01-25 16:01:05 +00:00
* Issue the specified request . HttpSocket :: get () and HttpSocket :: post () wrap this
* method and provide a more granular interface .
2008-05-30 11:40:08 +00:00
*
* @ param mixed $request Either an URI string , or an array defining host / uri
2010-12-14 03:07:25 +00:00
* @ return mixed false on error , HttpResponse on success
2008-05-30 11:40:08 +00:00
*/
2010-04-05 03:19:38 +00:00
public function request ( $request = array ()) {
2008-05-30 11:40:08 +00:00
$this -> reset ( false );
if ( is_string ( $request )) {
$request = array ( 'uri' => $request );
} elseif ( ! is_array ( $request )) {
return false ;
}
if ( ! isset ( $request [ 'uri' ])) {
$request [ 'uri' ] = null ;
}
2009-05-07 18:49:21 +00:00
$uri = $this -> _parseUri ( $request [ 'uri' ]);
2008-05-30 11:40:08 +00:00
if ( ! isset ( $uri [ 'host' ])) {
$host = $this -> config [ 'host' ];
}
if ( isset ( $request [ 'host' ])) {
$host = $request [ 'host' ];
unset ( $request [ 'host' ]);
}
$request [ 'uri' ] = $this -> url ( $request [ 'uri' ]);
2009-05-07 18:49:21 +00:00
$request [ 'uri' ] = $this -> _parseUri ( $request [ 'uri' ], true );
2010-12-11 18:49:19 +00:00
$this -> request = Set :: merge ( $this -> request , array_diff_key ( $this -> config [ 'request' ], array ( 'cookies' => true )), $request );
2008-05-30 11:40:08 +00:00
2009-05-07 18:49:21 +00:00
$this -> _configUri ( $this -> request [ 'uri' ]);
2008-05-30 11:40:08 +00:00
2010-12-11 18:49:19 +00:00
$Host = $this -> request [ 'uri' ][ 'host' ];
if ( ! empty ( $this -> config [ 'request' ][ 'cookies' ][ $Host ])) {
if ( ! isset ( $this -> request [ 'cookies' ])) {
$this -> request [ 'cookies' ] = array ();
}
if ( ! isset ( $request [ 'cookies' ])) {
$request [ 'cookies' ] = array ();
}
$this -> request [ 'cookies' ] = array_merge ( $this -> request [ 'cookies' ], $this -> config [ 'request' ][ 'cookies' ][ $Host ], $request [ 'cookies' ]);
}
2008-05-30 11:40:08 +00:00
if ( isset ( $host )) {
$this -> config [ 'host' ] = $host ;
}
2010-12-06 13:28:40 +00:00
$this -> _setProxy ();
2010-12-10 12:38:49 +00:00
$this -> request [ 'proxy' ] = $this -> _proxy ;
2008-05-30 11:40:08 +00:00
$cookies = null ;
if ( is_array ( $this -> request [ 'header' ])) {
if ( ! empty ( $this -> request [ 'cookies' ])) {
$cookies = $this -> buildCookies ( $this -> request [ 'cookies' ]);
}
2010-07-23 03:13:19 +00:00
$schema = '' ;
$port = 0 ;
if ( isset ( $this -> request [ 'uri' ][ 'schema' ])) {
$schema = $this -> request [ 'uri' ][ 'schema' ];
}
if ( isset ( $this -> request [ 'uri' ][ 'port' ])) {
$port = $this -> request [ 'uri' ][ 'port' ];
}
if (
( $schema === 'http' && $port != 80 ) ||
( $schema === 'https' && $port != 443 ) ||
( $port != 80 && $port != 443 )
) {
$Host .= ':' . $port ;
}
$this -> request [ 'header' ] = array_merge ( compact ( 'Host' ), $this -> request [ 'header' ]);
2008-05-30 11:40:08 +00:00
}
2010-12-03 02:46:11 +00:00
if ( isset ( $this -> request [ 'uri' ][ 'user' ], $this -> request [ 'uri' ][ 'pass' ])) {
2010-12-13 01:48:04 +00:00
$this -> configAuth ( 'Basic' , $this -> request [ 'uri' ][ 'user' ], $this -> request [ 'uri' ][ 'pass' ]);
2008-05-30 11:40:08 +00:00
}
2010-11-10 01:03:43 +00:00
$this -> _setAuth ();
2010-12-10 12:38:49 +00:00
$this -> request [ 'auth' ] = $this -> _auth ;
2008-05-30 11:40:08 +00:00
if ( is_array ( $this -> request [ 'body' ])) {
2009-05-07 18:49:21 +00:00
$this -> request [ 'body' ] = $this -> _httpSerialize ( $this -> request [ 'body' ]);
2008-05-30 11:40:08 +00:00
}
if ( ! empty ( $this -> request [ 'body' ]) && ! isset ( $this -> request [ 'header' ][ 'Content-Type' ])) {
$this -> request [ 'header' ][ 'Content-Type' ] = 'application/x-www-form-urlencoded' ;
}
if ( ! empty ( $this -> request [ 'body' ]) && ! isset ( $this -> request [ 'header' ][ 'Content-Length' ])) {
$this -> request [ 'header' ][ 'Content-Length' ] = strlen ( $this -> request [ 'body' ]);
}
2009-07-15 19:15:14 +00:00
$connectionType = null ;
if ( isset ( $this -> request [ 'header' ][ 'Connection' ])) {
$connectionType = $this -> request [ 'header' ][ 'Connection' ];
}
2009-07-20 16:31:56 +00:00
$this -> request [ 'header' ] = $this -> _buildHeader ( $this -> request [ 'header' ]) . $cookies ;
2008-05-30 11:40:08 +00:00
if ( empty ( $this -> request [ 'line' ])) {
2009-05-07 18:49:21 +00:00
$this -> request [ 'line' ] = $this -> _buildRequestLine ( $this -> request );
2008-05-30 11:40:08 +00:00
}
if ( $this -> quirksMode === false && $this -> request [ 'line' ] === false ) {
2010-12-14 03:07:25 +00:00
return false ;
2008-05-30 11:40:08 +00:00
}
2010-12-06 05:23:09 +00:00
$this -> request [ 'raw' ] = '' ;
2008-05-30 11:40:08 +00:00
if ( $this -> request [ 'line' ] !== false ) {
$this -> request [ 'raw' ] = $this -> request [ 'line' ];
}
if ( $this -> request [ 'header' ] !== false ) {
$this -> request [ 'raw' ] .= $this -> request [ 'header' ];
}
$this -> request [ 'raw' ] .= " \r \n " ;
$this -> request [ 'raw' ] .= $this -> request [ 'body' ];
$this -> write ( $this -> request [ 'raw' ]);
$response = null ;
2010-12-10 14:08:49 +00:00
$inHeader = true ;
2008-05-30 11:40:08 +00:00
while ( $data = $this -> read ()) {
2010-12-10 14:08:49 +00:00
if ( $this -> _contentResource ) {
if ( $inHeader ) {
$response .= $data ;
$pos = strpos ( $response , " \r \n \r \n " );
if ( $pos !== false ) {
$pos += 4 ;
$data = substr ( $response , $pos );
fwrite ( $this -> _contentResource , $data );
$response = substr ( $response , 0 , $pos );
$inHeader = false ;
}
} else {
fwrite ( $this -> _contentResource , $data );
fflush ( $this -> _contentResource );
}
} else {
$response .= $data ;
}
2008-05-30 11:40:08 +00:00
}
2010-12-06 14:03:22 +00:00
if ( $connectionType === 'close' ) {
2008-05-30 11:40:08 +00:00
$this -> disconnect ();
}
2011-04-10 21:27:44 +00:00
list ( $plugin , $responseClass ) = pluginSplit ( $this -> responseClass , true );
App :: uses ( $this -> responseClass , $plugin . 'Network/Http' );
if ( ! class_exists ( $responseClass )) {
2011-03-20 15:35:43 +00:00
throw new SocketException ( __d ( 'cake_dev' , 'Class %s not found.' , $this -> responseClass ));
2010-12-14 04:46:31 +00:00
}
$responseClass = $this -> responseClass ;
$this -> response = new $responseClass ( $response );
2010-12-14 03:07:25 +00:00
if ( ! empty ( $this -> response -> cookies )) {
2010-12-11 18:49:19 +00:00
if ( ! isset ( $this -> config [ 'request' ][ 'cookies' ][ $Host ])) {
$this -> config [ 'request' ][ 'cookies' ][ $Host ] = array ();
}
2010-12-14 03:07:25 +00:00
$this -> config [ 'request' ][ 'cookies' ][ $Host ] = array_merge ( $this -> config [ 'request' ][ 'cookies' ][ $Host ], $this -> response -> cookies );
2008-05-30 11:40:08 +00:00
}
2010-12-14 03:07:25 +00:00
return $this -> response ;
2008-05-30 11:40:08 +00:00
}
2009-07-24 19:18:37 +00:00
2008-05-30 11:40:08 +00:00
/**
* Issues a GET request to the specified URI , query , and request .
*
2010-01-25 16:01:05 +00:00
* Using a string uri and an array of query string parameters :
*
* `$response = $http->get('http://google.com/search', array('q' => 'cakephp', 'client' => 'safari'));`
*
* Would do a GET request to `http://google.com/search?q=cakephp&client=safari`
*
* You could express the same thing using a uri array and query string parameters :
*
* {{{
* $response = $http -> get (
* array ( 'host' => 'google.com' , 'path' => '/search' ),
* array ( 'q' => 'cakephp' , 'client' => 'safari' )
* );
* }}}
*
* @ param mixed $uri URI to request . Either a string uri , or a uri array , see HttpSocket :: _parseUri ()
* @ param array $query Querystring parameters to append to URI
2008-05-30 11:40:08 +00:00
* @ param array $request An indexed array with indexes such as 'method' or uri
2010-01-25 16:01:05 +00:00
* @ return mixed Result of request , either false on failure or the response to the request .
2008-05-30 11:40:08 +00:00
*/
2010-04-05 03:19:38 +00:00
public function get ( $uri = null , $query = array (), $request = array ()) {
2008-05-30 11:40:08 +00:00
if ( ! empty ( $query )) {
2011-04-25 12:34:40 +00:00
$uri = $this -> _parseUri ( $uri , $this -> config [ 'request' ][ 'uri' ]);
2008-05-30 11:40:08 +00:00
if ( isset ( $uri [ 'query' ])) {
$uri [ 'query' ] = array_merge ( $uri [ 'query' ], $query );
} else {
$uri [ 'query' ] = $query ;
}
2009-05-07 18:49:21 +00:00
$uri = $this -> _buildUri ( $uri );
2008-05-30 11:40:08 +00:00
}
$request = Set :: merge ( array ( 'method' => 'GET' , 'uri' => $uri ), $request );
return $this -> request ( $request );
}
/**
* Issues a POST request to the specified URI , query , and request .
*
2010-01-25 16:01:05 +00:00
* `post()` can be used to post simple data arrays to a url :
*
* {{{
* $response = $http -> post ( 'http://example.com' , array (
* 'username' => 'batman' ,
* 'password' => 'bruce_w4yne'
* ));
* }}}
*
* @ param mixed $uri URI to request . See HttpSocket :: _parseUri ()
* @ param array $data Array of POST data keys and values .
2008-05-30 11:40:08 +00:00
* @ param array $request An indexed array with indexes such as 'method' or uri
2010-01-25 16:01:05 +00:00
* @ return mixed Result of request , either false on failure or the response to the request .
2008-05-30 11:40:08 +00:00
*/
2010-04-05 03:19:38 +00:00
public function post ( $uri = null , $data = array (), $request = array ()) {
2008-05-30 11:40:08 +00:00
$request = Set :: merge ( array ( 'method' => 'POST' , 'uri' => $uri , 'body' => $data ), $request );
return $this -> request ( $request );
}
2009-07-24 19:18:37 +00:00
2008-05-30 11:40:08 +00:00
/**
* Issues a PUT request to the specified URI , query , and request .
*
2010-01-25 16:01:05 +00:00
* @ param mixed $uri URI to request , See HttpSocket :: _parseUri ()
* @ param array $data Array of PUT data keys and values .
2008-05-30 11:40:08 +00:00
* @ param array $request An indexed array with indexes such as 'method' or uri
* @ return mixed Result of request
*/
2010-04-05 03:19:38 +00:00
public function put ( $uri = null , $data = array (), $request = array ()) {
2008-05-30 11:40:08 +00:00
$request = Set :: merge ( array ( 'method' => 'PUT' , 'uri' => $uri , 'body' => $data ), $request );
return $this -> request ( $request );
}
2009-07-24 19:18:37 +00:00
2008-05-30 11:40:08 +00:00
/**
* Issues a DELETE request to the specified URI , query , and request .
*
2009-05-07 18:49:21 +00:00
* @ param mixed $uri URI to request ( see { @ link _parseUri ()})
2010-01-25 16:01:05 +00:00
* @ param array $data Query to append to URI
2008-05-30 11:40:08 +00:00
* @ param array $request An indexed array with indexes such as 'method' or uri
* @ return mixed Result of request
*/
2010-04-05 03:19:38 +00:00
public function delete ( $uri = null , $data = array (), $request = array ()) {
2008-05-30 11:40:08 +00:00
$request = Set :: merge ( array ( 'method' => 'DELETE' , 'uri' => $uri , 'body' => $data ), $request );
return $this -> request ( $request );
}
2009-07-24 19:18:37 +00:00
2008-05-30 11:40:08 +00:00
/**
2010-12-04 02:58:49 +00:00
* Normalizes urls into a $uriTemplate . If no template is provided
2010-01-25 16:01:05 +00:00
* a default one will be used . Will generate the url using the
* current config information .
*
* ### Usage:
*
* After configuring part of the request parameters , you can use url () to generate
* urls .
*
* {{{
* $http -> configUri ( 'http://www.cakephp.org' );
* $url = $http -> url ( '/search?q=bar' );
* }}}
*
* Would return `http://www.cakephp.org/search?q=bar`
*
* url () can also be used with custom templates :
*
* `$url = $http->url('http://www.cakephp/search?q=socket', '/%path?%query');`
*
* Would return `/search?q=socket` .
2008-05-30 11:40:08 +00:00
*
2010-01-25 16:01:05 +00:00
* @ param mixed $url Either a string or array of url options to create a url with .
* @ param string $uriTemplate A template string to use for url formatting .
* @ return mixed Either false on failure or a string containing the composed url .
2008-05-30 11:40:08 +00:00
*/
2010-04-05 03:19:38 +00:00
public function url ( $url = null , $uriTemplate = null ) {
2008-05-30 11:40:08 +00:00
if ( is_null ( $url )) {
$url = '/' ;
}
if ( is_string ( $url )) {
if ( $url { 0 } == '/' ) {
2010-12-04 02:58:49 +00:00
$url = $this -> config [ 'request' ][ 'uri' ][ 'host' ] . ':' . $this -> config [ 'request' ][ 'uri' ][ 'port' ] . $url ;
2008-05-30 11:40:08 +00:00
}
if ( ! preg_match ( '/^.+:\/\/|\*|^\//' , $url )) {
2010-12-04 02:58:49 +00:00
$url = $this -> config [ 'request' ][ 'uri' ][ 'scheme' ] . '://' . $url ;
2008-05-30 11:40:08 +00:00
}
} elseif ( ! is_array ( $url ) && ! empty ( $url )) {
return false ;
}
$base = array_merge ( $this -> config [ 'request' ][ 'uri' ], array ( 'scheme' => array ( 'http' , 'https' ), 'port' => array ( 80 , 443 )));
2009-05-07 18:49:21 +00:00
$url = $this -> _parseUri ( $url , $base );
2008-05-30 11:40:08 +00:00
if ( empty ( $url )) {
$url = $this -> config [ 'request' ][ 'uri' ];
}
if ( ! empty ( $uriTemplate )) {
2009-05-07 18:49:21 +00:00
return $this -> _buildUri ( $url , $uriTemplate );
2008-05-30 11:40:08 +00:00
}
2009-05-07 18:49:21 +00:00
return $this -> _buildUri ( $url );
2008-05-30 11:40:08 +00:00
}
2009-07-24 19:18:37 +00:00
2008-05-30 11:40:08 +00:00
/**
2010-11-10 01:03:43 +00:00
* Set authentication in request
2008-05-30 11:40:08 +00:00
*
2010-11-10 01:03:43 +00:00
* @ return void
2010-12-15 04:01:00 +00:00
* @ throws SocketException
2008-05-30 11:40:08 +00:00
*/
2010-11-10 01:03:43 +00:00
protected function _setAuth () {
2010-12-03 02:46:11 +00:00
if ( empty ( $this -> _auth )) {
2010-11-13 19:21:52 +00:00
return ;
2008-05-30 11:40:08 +00:00
}
2010-12-03 02:46:11 +00:00
$method = key ( $this -> _auth );
2011-02-22 04:28:46 +00:00
list ( $plugin , $authClass ) = pluginSplit ( $method , true );
$authClass = Inflector :: camelize ( $authClass ) . 'Authentication' ;
App :: uses ( $authClass , $plugin . 'Network/Http' );
if ( ! class_exists ( $authClass )) {
2011-03-20 15:35:43 +00:00
throw new SocketException ( __d ( 'cake_dev' , 'Unknown authentication method.' ));
2008-05-30 11:40:08 +00:00
}
2010-12-01 15:49:03 +00:00
if ( ! method_exists ( $authClass , 'authentication' )) {
2011-03-20 15:35:43 +00:00
throw new SocketException ( sprintf ( __d ( 'cake_dev' , 'The %s do not support authentication.' ), $authClass ));
2009-07-15 19:15:14 +00:00
}
2010-12-19 22:38:21 +00:00
call_user_func_array ( " $authClass ::authentication " , array ( $this , & $this -> _auth [ $method ]));
2008-05-30 11:40:08 +00:00
}
2009-07-24 19:18:37 +00:00
2008-05-30 11:40:08 +00:00
/**
2010-12-01 15:46:13 +00:00
* Set the proxy configuration and authentication
2008-05-30 11:40:08 +00:00
*
2010-12-01 15:46:13 +00:00
* @ return void
2010-12-15 04:01:00 +00:00
* @ throws SocketException
2008-05-30 11:40:08 +00:00
*/
2010-12-06 13:28:40 +00:00
protected function _setProxy () {
2010-12-04 01:10:07 +00:00
if ( empty ( $this -> _proxy ) || ! isset ( $this -> _proxy [ 'host' ], $this -> _proxy [ 'port' ])) {
2010-12-01 15:46:13 +00:00
return ;
2008-05-30 11:40:08 +00:00
}
2010-12-04 01:10:07 +00:00
$this -> config [ 'host' ] = $this -> _proxy [ 'host' ];
$this -> config [ 'port' ] = $this -> _proxy [ 'port' ];
2008-05-30 11:40:08 +00:00
2010-12-04 01:10:07 +00:00
if ( empty ( $this -> _proxy [ 'method' ]) || ! isset ( $this -> _proxy [ 'user' ], $this -> _proxy [ 'pass' ])) {
2010-12-01 15:46:13 +00:00
return ;
2008-05-30 11:40:08 +00:00
}
2011-02-22 04:28:46 +00:00
list ( $plugin , $authClass ) = pluginSplit ( $this -> _proxy [ 'method' ], true );
$authClass = Inflector :: camelize ( $authClass ) . 'Authentication' ;
App :: uses ( $authClass , $plugin . 'Network/Http' );
if ( ! class_exists ( $authClass )) {
2011-03-20 15:35:43 +00:00
throw new SocketException ( __d ( 'cake_dev' , 'Unknown authentication method for proxy.' ));
2008-05-30 11:40:08 +00:00
}
2010-12-01 15:49:03 +00:00
if ( ! method_exists ( $authClass , 'proxyAuthentication' )) {
2011-03-20 15:35:43 +00:00
throw new SocketException ( sprintf ( __d ( 'cake_dev' , 'The %s do not support proxy authentication.' ), $authClass ));
2008-05-30 11:40:08 +00:00
}
2010-12-19 22:38:21 +00:00
call_user_func_array ( " $authClass ::proxyAuthentication " , array ( $this , & $this -> _proxy ));
2008-05-30 11:40:08 +00:00
}
2009-07-24 19:18:37 +00:00
2008-05-30 11:40:08 +00:00
/**
* Parses and sets the specified URI into current request configuration .
*
2010-01-25 16:01:05 +00:00
* @ param mixed $uri URI , See HttpSocket :: _parseUri ()
2010-12-06 06:02:23 +00:00
* @ return boolean If uri has merged in config
2008-05-30 11:40:08 +00:00
*/
2010-04-05 03:21:28 +00:00
protected function _configUri ( $uri = null ) {
2008-05-30 11:40:08 +00:00
if ( empty ( $uri )) {
return false ;
}
if ( is_array ( $uri )) {
2009-05-07 18:49:21 +00:00
$uri = $this -> _parseUri ( $uri );
2008-05-30 11:40:08 +00:00
} else {
2009-05-07 18:49:21 +00:00
$uri = $this -> _parseUri ( $uri , true );
2008-05-30 11:40:08 +00:00
}
if ( ! isset ( $uri [ 'host' ])) {
return false ;
}
$config = array (
'request' => array (
2010-12-03 02:46:11 +00:00
'uri' => array_intersect_key ( $uri , $this -> config [ 'request' ][ 'uri' ])
2008-05-30 11:40:08 +00:00
)
);
$this -> config = Set :: merge ( $this -> config , $config );
$this -> config = Set :: merge ( $this -> config , array_intersect_key ( $this -> config [ 'request' ][ 'uri' ], $this -> config ));
2010-12-06 06:02:23 +00:00
return true ;
2008-05-30 11:40:08 +00:00
}
2009-07-24 19:18:37 +00:00
2008-05-30 11:40:08 +00:00
/**
* Takes a $uri array and turns it into a fully qualified URL string
*
2010-12-04 02:58:49 +00:00
* @ param mixed $uri Either A $uri array , or a request string . Will use $this -> config if left empty .
2010-01-25 16:01:05 +00:00
* @ param string $uriTemplate The Uri template / format to use .
* @ return mixed A fully qualified URL formated according to $uriTemplate , or false on failure
2008-05-30 11:40:08 +00:00
*/
2010-04-05 03:21:28 +00:00
protected function _buildUri ( $uri = array (), $uriTemplate = '%scheme://%user:%pass@%host:%port/%path?%query#%fragment' ) {
2008-05-30 11:40:08 +00:00
if ( is_string ( $uri )) {
$uri = array ( 'host' => $uri );
}
2009-05-07 18:49:21 +00:00
$uri = $this -> _parseUri ( $uri , true );
2008-05-30 11:40:08 +00:00
if ( ! is_array ( $uri ) || empty ( $uri )) {
return false ;
}
$uri [ 'path' ] = preg_replace ( '/^\//' , null , $uri [ 'path' ]);
2009-05-07 18:49:21 +00:00
$uri [ 'query' ] = $this -> _httpSerialize ( $uri [ 'query' ]);
2008-05-30 11:40:08 +00:00
$stripIfEmpty = array (
'query' => '?%query' ,
'fragment' => '#%fragment' ,
2009-11-13 19:42:40 +00:00
'user' => '%user:%pass@' ,
'host' => '%host:%port/'
2008-05-30 11:40:08 +00:00
);
foreach ( $stripIfEmpty as $key => $strip ) {
if ( empty ( $uri [ $key ])) {
$uriTemplate = str_replace ( $strip , null , $uriTemplate );
}
}
$defaultPorts = array ( 'http' => 80 , 'https' => 443 );
if ( array_key_exists ( $uri [ 'scheme' ], $defaultPorts ) && $defaultPorts [ $uri [ 'scheme' ]] == $uri [ 'port' ]) {
$uriTemplate = str_replace ( ':%port' , null , $uriTemplate );
}
foreach ( $uri as $property => $value ) {
2010-12-04 02:58:49 +00:00
$uriTemplate = str_replace ( '%' . $property , $value , $uriTemplate );
2008-05-30 11:40:08 +00:00
}
if ( $uriTemplate === '/*' ) {
$uriTemplate = '*' ;
}
return $uriTemplate ;
}
2009-07-24 19:18:37 +00:00
2008-05-30 11:40:08 +00:00
/**
* Parses the given URI and breaks it down into pieces as an indexed array with elements
* such as 'scheme' , 'port' , 'query' .
*
* @ param string $uri URI to parse
* @ param mixed $base If true use default URI config , otherwise indexed array to set 'scheme' , 'host' , 'port' , etc .
* @ return array Parsed URI
*/
2010-04-05 03:21:28 +00:00
protected function _parseUri ( $uri = null , $base = array ()) {
2008-05-30 11:40:08 +00:00
$uriBase = array (
'scheme' => array ( 'http' , 'https' ),
'host' => null ,
'port' => array ( 80 , 443 ),
'user' => null ,
'pass' => null ,
'path' => '/' ,
'query' => null ,
'fragment' => null
);
if ( is_string ( $uri )) {
$uri = parse_url ( $uri );
}
if ( ! is_array ( $uri ) || empty ( $uri )) {
return false ;
}
if ( $base === true ) {
$base = $uriBase ;
}
if ( isset ( $base [ 'port' ], $base [ 'scheme' ]) && is_array ( $base [ 'port' ]) && is_array ( $base [ 'scheme' ])) {
if ( isset ( $uri [ 'scheme' ]) && ! isset ( $uri [ 'port' ])) {
$base [ 'port' ] = $base [ 'port' ][ array_search ( $uri [ 'scheme' ], $base [ 'scheme' ])];
} elseif ( isset ( $uri [ 'port' ]) && ! isset ( $uri [ 'scheme' ])) {
$base [ 'scheme' ] = $base [ 'scheme' ][ array_search ( $uri [ 'port' ], $base [ 'port' ])];
}
}
if ( is_array ( $base ) && ! empty ( $base )) {
$uri = array_merge ( $base , $uri );
}
if ( isset ( $uri [ 'scheme' ]) && is_array ( $uri [ 'scheme' ])) {
$uri [ 'scheme' ] = array_shift ( $uri [ 'scheme' ]);
}
if ( isset ( $uri [ 'port' ]) && is_array ( $uri [ 'port' ])) {
$uri [ 'port' ] = array_shift ( $uri [ 'port' ]);
}
if ( array_key_exists ( 'query' , $uri )) {
2009-05-07 18:49:21 +00:00
$uri [ 'query' ] = $this -> _parseQuery ( $uri [ 'query' ]);
2008-05-30 11:40:08 +00:00
}
if ( ! array_intersect_key ( $uriBase , $uri )) {
return false ;
}
return $uri ;
}
2009-07-24 19:18:37 +00:00
2008-05-30 11:40:08 +00:00
/**
* This function can be thought of as a reverse to PHP5 ' s http_build_query () . It takes a given query string and turns it into an array and
* supports nesting by using the php bracket syntax . So this menas you can parse queries like :
*
* - ? key [ subKey ] = value
* - ? key [] = value1 & key [] = value2
*
2010-12-04 02:58:49 +00:00
* A leading '?' mark in $query is optional and does not effect the outcome of this function .
2010-01-25 16:01:05 +00:00
* For the complete capabilities of this implementation take a look at HttpSocketTest :: testparseQuery ()
2008-05-30 11:40:08 +00:00
*
* @ param mixed $query A query string to parse into an array or an array to return directly " as is "
2010-01-25 16:01:05 +00:00
* @ return array The $query parsed into a possibly multi - level array . If an empty $query is
* given , an empty array is returned .
2008-05-30 11:40:08 +00:00
*/
2010-04-05 03:21:28 +00:00
protected function _parseQuery ( $query ) {
2008-05-30 11:40:08 +00:00
if ( is_array ( $query )) {
return $query ;
}
$parsedQuery = array ();
if ( is_string ( $query ) && ! empty ( $query )) {
$query = preg_replace ( '/^\?/' , '' , $query );
$items = explode ( '&' , $query );
foreach ( $items as $item ) {
if ( strpos ( $item , '=' ) !== false ) {
2009-10-30 00:14:36 +00:00
list ( $key , $value ) = explode ( '=' , $item , 2 );
2008-05-30 11:40:08 +00:00
} else {
$key = $item ;
$value = null ;
}
$key = urldecode ( $key );
$value = urldecode ( $value );
if ( preg_match_all ( '/\[([^\[\]]*)\]/iUs' , $key , $matches )) {
$subKeys = $matches [ 1 ];
$rootKey = substr ( $key , 0 , strpos ( $key , '[' ));
if ( ! empty ( $rootKey )) {
array_unshift ( $subKeys , $rootKey );
}
$queryNode =& $parsedQuery ;
foreach ( $subKeys as $subKey ) {
if ( ! is_array ( $queryNode )) {
$queryNode = array ();
}
if ( $subKey === '' ) {
$queryNode [] = array ();
end ( $queryNode );
$subKey = key ( $queryNode );
}
$queryNode =& $queryNode [ $subKey ];
}
$queryNode = $value ;
} else {
$parsedQuery [ $key ] = $value ;
}
}
}
return $parsedQuery ;
}
2009-07-24 19:18:37 +00:00
2008-05-30 11:40:08 +00:00
/**
* Builds a request line according to HTTP / 1.1 specs . Activate quirks mode to work outside specs .
*
* @ param array $request Needs to contain a 'uri' key . Should also contain a 'method' key , otherwise defaults to GET .
* @ param string $versionToken The version token to use , defaults to HTTP / 1.1
* @ return string Request line
2010-12-15 04:01:00 +00:00
* @ throws SocketException
2008-05-30 11:40:08 +00:00
*/
2010-04-05 03:21:28 +00:00
protected function _buildRequestLine ( $request = array (), $versionToken = 'HTTP/1.1' ) {
2008-05-30 11:40:08 +00:00
$asteriskMethods = array ( 'OPTIONS' );
if ( is_string ( $request )) {
$isValid = preg_match ( " /(.+) (.+) (.+) \r \n /U " , $request , $match );
if ( ! $this -> quirksMode && ( ! $isValid || ( $match [ 2 ] == '*' && ! in_array ( $match [ 3 ], $asteriskMethods )))) {
2011-03-20 15:35:43 +00:00
throw new SocketException ( __d ( 'cake_dev' , 'HttpSocket::_buildRequestLine - Passed an invalid request line string. Activate quirks mode to do this.' ));
2008-05-30 11:40:08 +00:00
}
return $request ;
} elseif ( ! is_array ( $request )) {
return false ;
} elseif ( ! array_key_exists ( 'uri' , $request )) {
return false ;
}
2009-05-07 18:49:21 +00:00
$request [ 'uri' ] = $this -> _parseUri ( $request [ 'uri' ]);
2008-05-30 11:40:08 +00:00
$request = array_merge ( array ( 'method' => 'GET' ), $request );
2010-12-04 01:10:07 +00:00
if ( ! empty ( $this -> _proxy [ 'host' ])) {
2010-12-01 15:46:13 +00:00
$request [ 'uri' ] = $this -> _buildUri ( $request [ 'uri' ], '%scheme://%host:%port/%path?%query' );
} else {
$request [ 'uri' ] = $this -> _buildUri ( $request [ 'uri' ], '/%path?%query' );
}
2008-05-30 11:40:08 +00:00
if ( ! $this -> quirksMode && $request [ 'uri' ] === '*' && ! in_array ( $request [ 'method' ], $asteriskMethods )) {
2011-03-20 15:35:43 +00:00
throw new SocketException ( __d ( 'cake_dev' , 'HttpSocket::_buildRequestLine - The "*" asterisk character is only allowed for the following methods: %s. Activate quirks mode to work outside of HTTP/1.1 specs.' , implode ( ',' , $asteriskMethods )));
2008-05-30 11:40:08 +00:00
}
2010-12-14 13:11:36 +00:00
return $request [ 'method' ] . ' ' . $request [ 'uri' ] . ' ' . $versionToken . " \r \n " ;
2008-05-30 11:40:08 +00:00
}
2009-07-24 19:18:37 +00:00
2008-05-30 11:40:08 +00:00
/**
* Serializes an array for transport .
*
* @ param array $data Data to serialize
* @ return string Serialized variable
*/
2010-04-05 03:21:28 +00:00
protected function _httpSerialize ( $data = array ()) {
2008-05-30 11:40:08 +00:00
if ( is_string ( $data )) {
return $data ;
}
if ( empty ( $data ) || ! is_array ( $data )) {
return false ;
}
return substr ( Router :: queryString ( $data ), 1 );
}
2009-07-24 19:18:37 +00:00
2008-05-30 11:40:08 +00:00
/**
* Builds the header .
*
* @ param array $header Header to build
2010-11-13 23:44:11 +00:00
* @ param string $mode
2008-05-30 11:40:08 +00:00
* @ return string Header built from array
*/
2010-04-05 03:21:28 +00:00
protected function _buildHeader ( $header , $mode = 'standard' ) {
2008-05-30 11:40:08 +00:00
if ( is_string ( $header )) {
return $header ;
} elseif ( ! is_array ( $header )) {
return false ;
}
2010-11-13 23:44:11 +00:00
$fieldsInHeader = array ();
foreach ( $header as $key => $value ) {
$lowKey = strtolower ( $key );
if ( array_key_exists ( $lowKey , $fieldsInHeader )) {
$header [ $fieldsInHeader [ $lowKey ]] = $value ;
unset ( $header [ $key ]);
} else {
$fieldsInHeader [ $lowKey ] = $key ;
}
}
2008-05-30 11:40:08 +00:00
$returnHeader = '' ;
foreach ( $header as $field => $contents ) {
if ( is_array ( $contents ) && $mode == 'standard' ) {
2009-11-19 22:13:35 +00:00
$contents = implode ( ',' , $contents );
2008-05-30 11:40:08 +00:00
}
foreach (( array ) $contents as $content ) {
$contents = preg_replace ( " / \r \n (?![ \t ])/ " , " \r \n " , $content );
2009-05-07 18:49:21 +00:00
$field = $this -> _escapeToken ( $field );
2008-05-30 11:40:08 +00:00
2010-12-14 13:11:36 +00:00
$returnHeader .= $field . ': ' . $contents . " \r \n " ;
2008-05-30 11:40:08 +00:00
}
}
return $returnHeader ;
}
/**
2010-01-25 16:01:05 +00:00
* Builds cookie headers for a request .
2008-05-30 11:40:08 +00:00
*
2010-01-25 16:01:05 +00:00
* @ param array $cookies Array of cookies to send with the request .
* @ return string Cookie header string to be sent with the request .
2008-05-30 11:40:08 +00:00
* @ todo Refactor token escape mechanism to be configurable
*/
2010-12-04 02:58:49 +00:00
public function buildCookies ( $cookies ) {
2008-05-30 11:40:08 +00:00
$header = array ();
foreach ( $cookies as $name => $cookie ) {
2010-12-04 02:58:49 +00:00
$header [] = $name . '=' . $this -> _escapeToken ( $cookie [ 'value' ], array ( ';' ));
2008-05-30 11:40:08 +00:00
}
2010-12-04 02:58:49 +00:00
return $this -> _buildHeader ( array ( 'Cookie' => implode ( '; ' , $header )), 'pragmatic' );
2008-05-30 11:40:08 +00:00
}
2009-07-24 19:18:37 +00:00
2008-05-30 11:40:08 +00:00
/**
* Escapes a given $token according to RFC 2616 ( HTTP 1.1 specs )
*
* @ param string $token Token to escape
2010-12-04 02:58:49 +00:00
* @ param array $chars
2008-05-30 11:40:08 +00:00
* @ return string Escaped token
* @ todo Test $chars parameter
*/
2010-12-04 02:58:49 +00:00
protected function _escapeToken ( $token , $chars = null ) {
$regex = '/([' . implode ( '' , $this -> _tokenEscapeChars ( true , $chars )) . '])/' ;
2008-05-30 11:40:08 +00:00
$token = preg_replace ( $regex , '"\\1"' , $token );
return $token ;
}
2009-07-24 19:18:37 +00:00
2008-05-30 11:40:08 +00:00
/**
* Gets escape chars according to RFC 2616 ( HTTP 1.1 specs ) .
*
* @ param boolean $hex true to get them as HEX values , false otherwise
2010-12-04 02:58:49 +00:00
* @ param array $chars
2008-05-30 11:40:08 +00:00
* @ return array Escape chars
* @ todo Test $chars parameter
*/
2010-12-04 02:58:49 +00:00
protected function _tokenEscapeChars ( $hex = true , $chars = null ) {
2008-05-30 11:40:08 +00:00
if ( ! empty ( $chars )) {
$escape = $chars ;
} else {
$escape = array ( '"' , " ( " , " ) " , " < " , " > " , " @ " , " , " , " ; " , " : " , " \\ " , " / " , " [ " , " ] " , " ? " , " = " , " { " , " } " , " " );
for ( $i = 0 ; $i <= 31 ; $i ++ ) {
$escape [] = chr ( $i );
}
$escape [] = chr ( 127 );
}
if ( $hex == false ) {
return $escape ;
}
foreach ( $escape as $key => $char ) {
2010-12-04 02:58:49 +00:00
$escape [ $key ] = '\\x' . str_pad ( dechex ( ord ( $char )), 2 , '0' , STR_PAD_LEFT );
2008-05-30 11:40:08 +00:00
}
return $escape ;
}
2009-07-24 19:18:37 +00:00
2008-05-30 11:40:08 +00:00
/**
* Resets the state of this HttpSocket instance to it ' s initial state ( before Object :: __construct got executed ) or does
* the same thing partially for the request and the response property only .
*
* @ param boolean $full If set to false only HttpSocket :: response and HttpSocket :: request are reseted
* @ return boolean True on success
*/
2010-04-05 03:19:38 +00:00
public function reset ( $full = true ) {
2008-05-30 11:40:08 +00:00
static $initalState = array ();
if ( empty ( $initalState )) {
$initalState = get_class_vars ( __CLASS__ );
}
2010-12-06 05:56:16 +00:00
if ( ! $full ) {
2008-05-30 11:40:08 +00:00
$this -> request = $initalState [ 'request' ];
$this -> response = $initalState [ 'response' ];
return true ;
}
parent :: reset ( $initalState );
return true ;
}
}