Upgrade to KH 3.3.0
This commit is contained in:
424
includes/kohana/system/classes/Kohana/Request/Client.php
Normal file
424
includes/kohana/system/classes/Kohana/Request/Client.php
Normal file
@@ -0,0 +1,424 @@
|
||||
<?php defined('SYSPATH') OR die('No direct script access.');
|
||||
/**
|
||||
* Request Client. Processes a [Request] and handles [HTTP_Caching] if
|
||||
* available. Will usually return a [Response] object as a result of the
|
||||
* request unless an unexpected error occurs.
|
||||
*
|
||||
* @package Kohana
|
||||
* @category Base
|
||||
* @author Kohana Team
|
||||
* @copyright (c) 2008-2012 Kohana Team
|
||||
* @license http://kohanaframework.org/license
|
||||
* @since 3.1.0
|
||||
*/
|
||||
abstract class Kohana_Request_Client {
|
||||
|
||||
/**
|
||||
* @var Cache Caching library for request caching
|
||||
*/
|
||||
protected $_cache;
|
||||
|
||||
/**
|
||||
* @var bool Should redirects be followed?
|
||||
*/
|
||||
protected $_follow = FALSE;
|
||||
|
||||
/**
|
||||
* @var array Headers to preserve when following a redirect
|
||||
*/
|
||||
protected $_follow_headers = array('Authorization');
|
||||
|
||||
/**
|
||||
* @var bool Follow 302 redirect with original request method?
|
||||
*/
|
||||
protected $_strict_redirect = TRUE;
|
||||
|
||||
/**
|
||||
* @var array Callbacks to use when response contains given headers
|
||||
*/
|
||||
protected $_header_callbacks = array(
|
||||
'Location' => 'Request_Client::on_header_location'
|
||||
);
|
||||
|
||||
/**
|
||||
* @var int Maximum number of requests that header callbacks can trigger before the request is aborted
|
||||
*/
|
||||
protected $_max_callback_depth = 5;
|
||||
|
||||
/**
|
||||
* @var int Tracks the callback depth of the currently executing request
|
||||
*/
|
||||
protected $_callback_depth = 1;
|
||||
|
||||
/**
|
||||
* @var array Arbitrary parameters that are shared with header callbacks through their Request_Client object
|
||||
*/
|
||||
protected $_callback_params = array();
|
||||
|
||||
/**
|
||||
* Creates a new `Request_Client` object,
|
||||
* allows for dependency injection.
|
||||
*
|
||||
* @param array $params Params
|
||||
*/
|
||||
public function __construct(array $params = array())
|
||||
{
|
||||
foreach ($params as $key => $value)
|
||||
{
|
||||
if (method_exists($this, $key))
|
||||
{
|
||||
$this->$key($value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes the request, executing the controller action that handles this
|
||||
* request, determined by the [Route].
|
||||
*
|
||||
* 1. Before the controller action is called, the [Controller::before] method
|
||||
* will be called.
|
||||
* 2. Next the controller action will be called.
|
||||
* 3. After the controller action is called, the [Controller::after] method
|
||||
* will be called.
|
||||
*
|
||||
* By default, the output from the controller is captured and returned, and
|
||||
* no headers are sent.
|
||||
*
|
||||
* $request->execute();
|
||||
*
|
||||
* @param Request $request
|
||||
* @param Response $response
|
||||
* @return Response
|
||||
* @throws Kohana_Exception
|
||||
* @uses [Kohana::$profiling]
|
||||
* @uses [Profiler]
|
||||
*/
|
||||
public function execute(Request $request)
|
||||
{
|
||||
// Prevent too much recursion of header callback requests
|
||||
if ($this->callback_depth() > $this->max_callback_depth())
|
||||
throw new Request_Client_Recursion_Exception(
|
||||
"Could not execute request to :uri - too many recursions after :depth requests",
|
||||
array(
|
||||
':uri' => $request->uri(),
|
||||
':depth' => $this->callback_depth() - 1,
|
||||
));
|
||||
|
||||
// Execute the request
|
||||
$orig_response = $response = Response::factory();
|
||||
|
||||
if (($cache = $this->cache()) instanceof HTTP_Cache)
|
||||
return $cache->execute($this, $request, $response);
|
||||
|
||||
$response = $this->execute_request($request, $response);
|
||||
|
||||
// Execute response callbacks
|
||||
foreach ($this->header_callbacks() as $header => $callback)
|
||||
{
|
||||
if ($response->headers($header))
|
||||
{
|
||||
$cb_result = call_user_func($callback, $request, $response, $this);
|
||||
|
||||
if ($cb_result instanceof Request)
|
||||
{
|
||||
// If the callback returns a request, automatically assign client params
|
||||
$this->assign_client_properties($cb_result->client());
|
||||
$cb_result->client()->callback_depth($this->callback_depth() + 1);
|
||||
|
||||
// Execute the request
|
||||
$response = $cb_result->execute();
|
||||
}
|
||||
elseif ($cb_result instanceof Response)
|
||||
{
|
||||
// Assign the returned response
|
||||
$response = $cb_result;
|
||||
}
|
||||
|
||||
// If the callback has created a new response, do not process any further
|
||||
if ($response !== $orig_response)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return $response;
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes the request passed to it and returns the response from
|
||||
* the URI resource identified.
|
||||
*
|
||||
* This method must be implemented by all clients.
|
||||
*
|
||||
* @param Request $request request to execute by client
|
||||
* @param Response $response
|
||||
* @return Response
|
||||
* @since 3.2.0
|
||||
*/
|
||||
abstract public function execute_request(Request $request, Response $response);
|
||||
|
||||
/**
|
||||
* Getter and setter for the internal caching engine,
|
||||
* used to cache responses if available and valid.
|
||||
*
|
||||
* @param HTTP_Cache $cache engine to use for caching
|
||||
* @return HTTP_Cache
|
||||
* @return Request_Client
|
||||
*/
|
||||
public function cache(HTTP_Cache $cache = NULL)
|
||||
{
|
||||
if ($cache === NULL)
|
||||
return $this->_cache;
|
||||
|
||||
$this->_cache = $cache;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Getter and setter for the follow redirects
|
||||
* setting.
|
||||
*
|
||||
* @param bool $follow Boolean indicating if redirects should be followed
|
||||
* @return bool
|
||||
* @return Request_Client
|
||||
*/
|
||||
public function follow($follow = NULL)
|
||||
{
|
||||
if ($follow === NULL)
|
||||
return $this->_follow;
|
||||
|
||||
$this->_follow = $follow;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Getter and setter for the follow redirects
|
||||
* headers array.
|
||||
*
|
||||
* @param array $follow_headers Array of headers to be re-used when following a Location header
|
||||
* @return array
|
||||
* @return Request_Client
|
||||
*/
|
||||
public function follow_headers($follow_headers = NULL)
|
||||
{
|
||||
if ($follow_headers === NULL)
|
||||
return $this->_follow_headers;
|
||||
|
||||
$this->_follow_headers = $follow_headers;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Getter and setter for the strict redirects setting
|
||||
*
|
||||
* [!!] HTTP/1.1 specifies that a 302 redirect should be followed using the
|
||||
* original request method. However, the vast majority of clients and servers
|
||||
* get this wrong, with 302 widely used for 'POST - 302 redirect - GET' patterns.
|
||||
* By default, Kohana's client is fully compliant with the HTTP spec. Some
|
||||
* non-compliant third party sites may require that strict_redirect is set
|
||||
* FALSE to force the client to switch to GET following a 302 response.
|
||||
*
|
||||
* @param bool $strict_redirect Boolean indicating if 302 redirects should be followed with the original method
|
||||
* @return Request_Client
|
||||
*/
|
||||
public function strict_redirect($strict_redirect = NULL)
|
||||
{
|
||||
if ($strict_redirect === NULL)
|
||||
return $this->_strict_redirect;
|
||||
|
||||
$this->_strict_redirect = $strict_redirect;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Getter and setter for the header callbacks array.
|
||||
*
|
||||
* Accepts an array with HTTP response headers as keys and a PHP callback
|
||||
* function as values. These callbacks will be triggered if a response contains
|
||||
* the given header and can either issue a subsequent request or manipulate
|
||||
* the response as required.
|
||||
*
|
||||
* By default, the [Request_Client::on_header_location] callback is assigned
|
||||
* to the Location header to support automatic redirect following.
|
||||
*
|
||||
* $client->header_callbacks(array(
|
||||
* 'Location' => 'Request_Client::on_header_location',
|
||||
* 'WWW-Authenticate' => function($request, $response, $client) {return $new_response;},
|
||||
* );
|
||||
*
|
||||
* @param array $header_callbacks Array of callbacks to trigger on presence of given headers
|
||||
* @return Request_Client
|
||||
*/
|
||||
public function header_callbacks($header_callbacks = NULL)
|
||||
{
|
||||
if ($header_callbacks === NULL)
|
||||
return $this->_header_callbacks;
|
||||
|
||||
$this->_header_callbacks = $header_callbacks;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Getter and setter for the maximum callback depth property.
|
||||
*
|
||||
* This protects the main execution from recursive callback execution (eg
|
||||
* following infinite redirects, conflicts between callbacks causing loops
|
||||
* etc). Requests will only be allowed to nest to the level set by this
|
||||
* param before execution is aborted with a Request_Client_Recursion_Exception.
|
||||
*
|
||||
* @param int $depth Maximum number of callback requests to execute before aborting
|
||||
* @return Request_Client|int
|
||||
*/
|
||||
public function max_callback_depth($depth = NULL)
|
||||
{
|
||||
if ($depth === NULL)
|
||||
return $this->_max_callback_depth;
|
||||
|
||||
$this->_max_callback_depth = $depth;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Getter/Setter for the callback depth property, which is used to track
|
||||
* how many recursions have been executed within the current request execution.
|
||||
*
|
||||
* @param int $depth Current recursion depth
|
||||
* @return Request_Client|int
|
||||
*/
|
||||
public function callback_depth($depth = NULL)
|
||||
{
|
||||
if ($depth === NULL)
|
||||
return $this->_callback_depth;
|
||||
|
||||
$this->_callback_depth = $depth;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Getter/Setter for the callback_params array, which allows additional
|
||||
* application-specific parameters to be shared with callbacks.
|
||||
*
|
||||
* As with other Kohana setter/getters, usage is:
|
||||
*
|
||||
* // Set full array
|
||||
* $client->callback_params(array('foo'=>'bar'));
|
||||
*
|
||||
* // Set single key
|
||||
* $client->callback_params('foo','bar');
|
||||
*
|
||||
* // Get full array
|
||||
* $params = $client->callback_params();
|
||||
*
|
||||
* // Get single key
|
||||
* $foo = $client->callback_params('foo');
|
||||
*
|
||||
* @param string|array $param
|
||||
* @param mixed $value
|
||||
* @return Request_Client|mixed
|
||||
*/
|
||||
public function callback_params($param = NULL, $value = NULL)
|
||||
{
|
||||
// Getter for full array
|
||||
if ($param === NULL)
|
||||
return $this->_callback_params;
|
||||
|
||||
// Setter for full array
|
||||
if (is_array($param))
|
||||
{
|
||||
$this->_callback_params = $param;
|
||||
return $this;
|
||||
}
|
||||
// Getter for single value
|
||||
elseif ($value === NULL)
|
||||
{
|
||||
return Arr::get($this->_callback_params, $param);
|
||||
}
|
||||
// Setter for single value
|
||||
else
|
||||
{
|
||||
$this->_callback_params[$param] = $value;
|
||||
return $this;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Assigns the properties of the current Request_Client to another
|
||||
* Request_Client instance - used when setting up a subsequent request.
|
||||
*
|
||||
* @param Request_Client $client
|
||||
*/
|
||||
public function assign_client_properties(Request_Client $client)
|
||||
{
|
||||
$client->cache($this->cache());
|
||||
$client->follow($this->follow());
|
||||
$client->follow_headers($this->follow_headers());
|
||||
$client->header_callbacks($this->header_callbacks());
|
||||
$client->max_callback_depth($this->max_callback_depth());
|
||||
$client->callback_params($this->callback_params());
|
||||
}
|
||||
|
||||
/**
|
||||
* The default handler for following redirects, triggered by the presence of
|
||||
* a Location header in the response.
|
||||
*
|
||||
* The client's follow property must be set TRUE and the HTTP response status
|
||||
* one of 201, 301, 302, 303 or 307 for the redirect to be followed.
|
||||
*
|
||||
* @param Request $request
|
||||
* @param Response $response
|
||||
* @param Request_Client $client
|
||||
*/
|
||||
public static function on_header_location(Request $request, Response $response, Request_Client $client)
|
||||
{
|
||||
// Do we need to follow a Location header ?
|
||||
if ($client->follow() AND in_array($response->status(), array(201, 301, 302, 303, 307)))
|
||||
{
|
||||
// Figure out which method to use for the follow request
|
||||
switch ($response->status())
|
||||
{
|
||||
default:
|
||||
case 301:
|
||||
case 307:
|
||||
$follow_method = $request->method();
|
||||
break;
|
||||
case 201:
|
||||
case 303:
|
||||
$follow_method = Request::GET;
|
||||
break;
|
||||
case 302:
|
||||
// Cater for sites with broken HTTP redirect implementations
|
||||
if ($client->strict_redirect())
|
||||
{
|
||||
$follow_method = $request->method();
|
||||
}
|
||||
else
|
||||
{
|
||||
$follow_method = Request::GET;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
// Prepare the additional request
|
||||
$follow_request = Request::factory($response->headers('Location'))
|
||||
->method($follow_method)
|
||||
->headers(Arr::extract($request->headers(), $client->follow_headers()));
|
||||
|
||||
if ($follow_method !== Request::GET)
|
||||
{
|
||||
$follow_request->body($request->body());
|
||||
}
|
||||
|
||||
return $follow_request;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
}
|
135
includes/kohana/system/classes/Kohana/Request/Client/Curl.php
Normal file
135
includes/kohana/system/classes/Kohana/Request/Client/Curl.php
Normal file
@@ -0,0 +1,135 @@
|
||||
<?php defined('SYSPATH') OR die('No direct script access.');
|
||||
/**
|
||||
* [Request_Client_External] Curl driver performs external requests using the
|
||||
* php-curl extention. This is the default driver for all external requests.
|
||||
*
|
||||
* @package Kohana
|
||||
* @category Base
|
||||
* @author Kohana Team
|
||||
* @copyright (c) 2008-2012 Kohana Team
|
||||
* @license http://kohanaframework.org/license
|
||||
* @uses [PHP cURL](http://php.net/manual/en/book.curl.php)
|
||||
*/
|
||||
class Kohana_Request_Client_Curl extends Request_Client_External {
|
||||
|
||||
/**
|
||||
* Sends the HTTP message [Request] to a remote server and processes
|
||||
* the response.
|
||||
*
|
||||
* @param Request $request request to send
|
||||
* @param Response $request response to send
|
||||
* @return Response
|
||||
*/
|
||||
public function _send_message(Request $request, Response $response)
|
||||
{
|
||||
// Response headers
|
||||
$response_headers = array();
|
||||
|
||||
$options = array();
|
||||
|
||||
// Set the request method
|
||||
$options = $this->_set_curl_request_method($request, $options);
|
||||
|
||||
// Set the request body. This is perfectly legal in CURL even
|
||||
// if using a request other than POST. PUT does support this method
|
||||
// and DOES NOT require writing data to disk before putting it, if
|
||||
// reading the PHP docs you may have got that impression. SdF
|
||||
$options[CURLOPT_POSTFIELDS] = $request->body();
|
||||
|
||||
// Process headers
|
||||
if ($headers = $request->headers())
|
||||
{
|
||||
$http_headers = array();
|
||||
|
||||
foreach ($headers as $key => $value)
|
||||
{
|
||||
$http_headers[] = $key.': '.$value;
|
||||
}
|
||||
|
||||
$options[CURLOPT_HTTPHEADER] = $http_headers;
|
||||
}
|
||||
|
||||
// Process cookies
|
||||
if ($cookies = $request->cookie())
|
||||
{
|
||||
$options[CURLOPT_COOKIE] = http_build_query($cookies, NULL, '; ');
|
||||
}
|
||||
|
||||
// Get any exisiting response headers
|
||||
$response_header = $response->headers();
|
||||
|
||||
// Implement the standard parsing parameters
|
||||
$options[CURLOPT_HEADERFUNCTION] = array($response_header, 'parse_header_string');
|
||||
$this->_options[CURLOPT_RETURNTRANSFER] = TRUE;
|
||||
$this->_options[CURLOPT_HEADER] = FALSE;
|
||||
|
||||
// Apply any additional options set to
|
||||
$options = Arr::merge($options, $this->_options);
|
||||
|
||||
$uri = $request->uri();
|
||||
|
||||
if ($query = $request->query())
|
||||
{
|
||||
$uri .= '?'.http_build_query($query, NULL, '&');
|
||||
}
|
||||
|
||||
// Open a new remote connection
|
||||
$curl = curl_init($uri);
|
||||
|
||||
// Set connection options
|
||||
if ( ! curl_setopt_array($curl, $options))
|
||||
{
|
||||
throw new Request_Exception('Failed to set CURL options, check CURL documentation: :url',
|
||||
array(':url' => 'http://php.net/curl_setopt_array'));
|
||||
}
|
||||
|
||||
// Get the response body
|
||||
$body = curl_exec($curl);
|
||||
|
||||
// Get the response information
|
||||
$code = curl_getinfo($curl, CURLINFO_HTTP_CODE);
|
||||
|
||||
if ($body === FALSE)
|
||||
{
|
||||
$error = curl_error($curl);
|
||||
}
|
||||
|
||||
// Close the connection
|
||||
curl_close($curl);
|
||||
|
||||
if (isset($error))
|
||||
{
|
||||
throw new Request_Exception('Error fetching remote :url [ status :code ] :error',
|
||||
array(':url' => $request->url(), ':code' => $code, ':error' => $error));
|
||||
}
|
||||
|
||||
$response->status($code)
|
||||
->body($body);
|
||||
|
||||
return $response;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the appropriate curl request options. Uses the responding options
|
||||
* for POST and PUT, uses CURLOPT_CUSTOMREQUEST otherwise
|
||||
* @param Request $request
|
||||
* @param array $options
|
||||
* @return array
|
||||
*/
|
||||
public function _set_curl_request_method(Request $request, array $options)
|
||||
{
|
||||
switch ($request->method()) {
|
||||
case Request::POST:
|
||||
$options[CURLOPT_POST] = TRUE;
|
||||
break;
|
||||
case Request::PUT:
|
||||
$options[CURLOPT_PUT] = TRUE;
|
||||
break;
|
||||
default:
|
||||
$options[CURLOPT_CUSTOMREQUEST] = $request->method();
|
||||
break;
|
||||
}
|
||||
return $options;
|
||||
}
|
||||
|
||||
} // End Kohana_Request_Client_Curl
|
@@ -0,0 +1,207 @@
|
||||
<?php defined('SYSPATH') OR die('No direct script access.');
|
||||
/**
|
||||
* [Request_Client_External] provides a wrapper for all external request
|
||||
* processing. This class should be extended by all drivers handling external
|
||||
* requests.
|
||||
*
|
||||
* Supported out of the box:
|
||||
* - Curl (default)
|
||||
* - PECL HTTP
|
||||
* - Streams
|
||||
*
|
||||
* To select a specific external driver to use as the default driver, set the
|
||||
* following property within the Application bootstrap. Alternatively, the
|
||||
* client can be injected into the request object.
|
||||
*
|
||||
* @example
|
||||
*
|
||||
* // In application bootstrap
|
||||
* Request_Client_External::$client = 'Request_Client_Stream';
|
||||
*
|
||||
* // Add client to request
|
||||
* $request = Request::factory('http://some.host.tld/foo/bar')
|
||||
* ->client(Request_Client_External::factory('Request_Client_HTTP));
|
||||
*
|
||||
* @package Kohana
|
||||
* @category Base
|
||||
* @author Kohana Team
|
||||
* @copyright (c) 2008-2012 Kohana Team
|
||||
* @license http://kohanaframework.org/license
|
||||
* @uses [PECL HTTP](http://php.net/manual/en/book.http.php)
|
||||
*/
|
||||
abstract class Kohana_Request_Client_External extends Request_Client {
|
||||
|
||||
/**
|
||||
* Use:
|
||||
* - Request_Client_Curl (default)
|
||||
* - Request_Client_HTTP
|
||||
* - Request_Client_Stream
|
||||
*
|
||||
* @var string defines the external client to use by default
|
||||
*/
|
||||
public static $client = 'Request_Client_Curl';
|
||||
|
||||
/**
|
||||
* Factory method to create a new Request_Client_External object based on
|
||||
* the client name passed, or defaulting to Request_Client_External::$client
|
||||
* by default.
|
||||
*
|
||||
* Request_Client_External::$client can be set in the application bootstrap.
|
||||
*
|
||||
* @param array $params parameters to pass to the client
|
||||
* @param string $client external client to use
|
||||
* @return Request_Client_External
|
||||
* @throws Request_Exception
|
||||
*/
|
||||
public static function factory(array $params = array(), $client = NULL)
|
||||
{
|
||||
if ($client === NULL)
|
||||
{
|
||||
$client = Request_Client_External::$client;
|
||||
}
|
||||
|
||||
$client = new $client($params);
|
||||
|
||||
if ( ! $client instanceof Request_Client_External)
|
||||
{
|
||||
throw new Request_Exception('Selected client is not a Request_Client_External object.');
|
||||
}
|
||||
|
||||
return $client;
|
||||
}
|
||||
|
||||
/**
|
||||
* @var array curl options
|
||||
* @link http://www.php.net/manual/function.curl-setopt
|
||||
* @link http://www.php.net/manual/http.request.options
|
||||
*/
|
||||
protected $_options = array();
|
||||
|
||||
/**
|
||||
* Processes the request, executing the controller action that handles this
|
||||
* request, determined by the [Route].
|
||||
*
|
||||
* 1. Before the controller action is called, the [Controller::before] method
|
||||
* will be called.
|
||||
* 2. Next the controller action will be called.
|
||||
* 3. After the controller action is called, the [Controller::after] method
|
||||
* will be called.
|
||||
*
|
||||
* By default, the output from the controller is captured and returned, and
|
||||
* no headers are sent.
|
||||
*
|
||||
* $request->execute();
|
||||
*
|
||||
* @param Request $request A request object
|
||||
* @param Response $response A response object
|
||||
* @return Response
|
||||
* @throws Kohana_Exception
|
||||
* @uses [Kohana::$profiling]
|
||||
* @uses [Profiler]
|
||||
*/
|
||||
public function execute_request(Request $request, Response $response)
|
||||
{
|
||||
if (Kohana::$profiling)
|
||||
{
|
||||
// Set the benchmark name
|
||||
$benchmark = '"'.$request->uri().'"';
|
||||
|
||||
if ($request !== Request::$initial AND Request::$current)
|
||||
{
|
||||
// Add the parent request uri
|
||||
$benchmark .= ' « "'.Request::$current->uri().'"';
|
||||
}
|
||||
|
||||
// Start benchmarking
|
||||
$benchmark = Profiler::start('Requests', $benchmark);
|
||||
}
|
||||
|
||||
// Store the current active request and replace current with new request
|
||||
$previous = Request::$current;
|
||||
Request::$current = $request;
|
||||
|
||||
// Resolve the POST fields
|
||||
if ($post = $request->post())
|
||||
{
|
||||
$request->body(http_build_query($post, NULL, '&'))
|
||||
->headers('content-type', 'application/x-www-form-urlencoded');
|
||||
}
|
||||
|
||||
// If Kohana expose, set the user-agent
|
||||
if (Kohana::$expose)
|
||||
{
|
||||
$request->headers('user-agent', Kohana::version());
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
$response = $this->_send_message($request, $response);
|
||||
}
|
||||
catch (Exception $e)
|
||||
{
|
||||
// Restore the previous request
|
||||
Request::$current = $previous;
|
||||
|
||||
if (isset($benchmark))
|
||||
{
|
||||
// Delete the benchmark, it is invalid
|
||||
Profiler::delete($benchmark);
|
||||
}
|
||||
|
||||
// Re-throw the exception
|
||||
throw $e;
|
||||
}
|
||||
|
||||
// Restore the previous request
|
||||
Request::$current = $previous;
|
||||
|
||||
if (isset($benchmark))
|
||||
{
|
||||
// Stop the benchmark
|
||||
Profiler::stop($benchmark);
|
||||
}
|
||||
|
||||
// Return the response
|
||||
return $response;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set and get options for this request.
|
||||
*
|
||||
* @param mixed $key Option name, or array of options
|
||||
* @param mixed $value Option value
|
||||
* @return mixed
|
||||
* @return Request_Client_External
|
||||
*/
|
||||
public function options($key = NULL, $value = NULL)
|
||||
{
|
||||
if ($key === NULL)
|
||||
return $this->_options;
|
||||
|
||||
if (is_array($key))
|
||||
{
|
||||
$this->_options = $key;
|
||||
}
|
||||
elseif ($value === NULL)
|
||||
{
|
||||
return Arr::get($this->_options, $key);
|
||||
}
|
||||
else
|
||||
{
|
||||
$this->_options[$key] = $value;
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends the HTTP message [Request] to a remote server and processes
|
||||
* the response.
|
||||
*
|
||||
* @param Request $request Request to send
|
||||
* @param Response $response Response to send
|
||||
* @return Response
|
||||
*/
|
||||
abstract protected function _send_message(Request $request, Response $response);
|
||||
|
||||
} // End Kohana_Request_Client_External
|
121
includes/kohana/system/classes/Kohana/Request/Client/HTTP.php
Normal file
121
includes/kohana/system/classes/Kohana/Request/Client/HTTP.php
Normal file
@@ -0,0 +1,121 @@
|
||||
<?php defined('SYSPATH') OR die('No direct script access.');
|
||||
/**
|
||||
* [Request_Client_External] HTTP driver performs external requests using the
|
||||
* php-http extention. To use this driver, ensure the following is completed
|
||||
* before executing an external request- ideally in the application bootstrap.
|
||||
*
|
||||
* @example
|
||||
*
|
||||
* // In application bootstrap
|
||||
* Request_Client_External::$client = 'Request_Client_HTTP';
|
||||
*
|
||||
* @package Kohana
|
||||
* @category Base
|
||||
* @author Kohana Team
|
||||
* @copyright (c) 2008-2012 Kohana Team
|
||||
* @license http://kohanaframework.org/license
|
||||
* @uses [PECL HTTP](http://php.net/manual/en/book.http.php)
|
||||
*/
|
||||
class Kohana_Request_Client_HTTP extends Request_Client_External {
|
||||
|
||||
/**
|
||||
* Creates a new `Request_Client` object,
|
||||
* allows for dependency injection.
|
||||
*
|
||||
* @param array $params Params
|
||||
* @throws Request_Exception
|
||||
*/
|
||||
public function __construct(array $params = array())
|
||||
{
|
||||
// Check that PECL HTTP supports requests
|
||||
if ( ! http_support(HTTP_SUPPORT_REQUESTS))
|
||||
{
|
||||
throw new Request_Exception('Need HTTP request support!');
|
||||
}
|
||||
|
||||
// Carry on
|
||||
parent::__construct($params);
|
||||
}
|
||||
|
||||
/**
|
||||
* @var array curl options
|
||||
* @link http://www.php.net/manual/function.curl-setopt
|
||||
*/
|
||||
protected $_options = array();
|
||||
|
||||
/**
|
||||
* Sends the HTTP message [Request] to a remote server and processes
|
||||
* the response.
|
||||
*
|
||||
* @param Request $request request to send
|
||||
* @param Response $request response to send
|
||||
* @return Response
|
||||
*/
|
||||
public function _send_message(Request $request, Response $response)
|
||||
{
|
||||
$http_method_mapping = array(
|
||||
HTTP_Request::GET => HTTPRequest::METH_GET,
|
||||
HTTP_Request::HEAD => HTTPRequest::METH_HEAD,
|
||||
HTTP_Request::POST => HTTPRequest::METH_POST,
|
||||
HTTP_Request::PUT => HTTPRequest::METH_PUT,
|
||||
HTTP_Request::DELETE => HTTPRequest::METH_DELETE,
|
||||
HTTP_Request::OPTIONS => HTTPRequest::METH_OPTIONS,
|
||||
HTTP_Request::TRACE => HTTPRequest::METH_TRACE,
|
||||
HTTP_Request::CONNECT => HTTPRequest::METH_CONNECT,
|
||||
);
|
||||
|
||||
// Create an http request object
|
||||
$http_request = new HTTPRequest($request->uri(), $http_method_mapping[$request->method()]);
|
||||
|
||||
if ($this->_options)
|
||||
{
|
||||
// Set custom options
|
||||
$http_request->setOptions($this->_options);
|
||||
}
|
||||
|
||||
// Set headers
|
||||
$http_request->setHeaders($request->headers()->getArrayCopy());
|
||||
|
||||
// Set cookies
|
||||
$http_request->setCookies($request->cookie());
|
||||
|
||||
// Set query data (?foo=bar&bar=foo)
|
||||
$http_request->setQueryData($request->query());
|
||||
|
||||
// Set the body
|
||||
if ($request->method() == HTTP_Request::PUT)
|
||||
{
|
||||
$http_request->addPutData($request->body());
|
||||
}
|
||||
else
|
||||
{
|
||||
$http_request->setBody($request->body());
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
$http_request->send();
|
||||
}
|
||||
catch (HTTPRequestException $e)
|
||||
{
|
||||
throw new Request_Exception($e->getMessage());
|
||||
}
|
||||
catch (HTTPMalformedHeaderException $e)
|
||||
{
|
||||
throw new Request_Exception($e->getMessage());
|
||||
}
|
||||
catch (HTTPEncodingException $e)
|
||||
{
|
||||
throw new Request_Exception($e->getMessage());
|
||||
}
|
||||
|
||||
// Build the response
|
||||
$response->status($http_request->getResponseCode())
|
||||
->headers($http_request->getResponseHeader())
|
||||
->cookie($http_request->getResponseCookies())
|
||||
->body($http_request->getResponseBody());
|
||||
|
||||
return $response;
|
||||
}
|
||||
|
||||
} // End Kohana_Request_Client_HTTP
|
@@ -0,0 +1,128 @@
|
||||
<?php defined('SYSPATH') OR die('No direct script access.');
|
||||
/**
|
||||
* Request Client for internal execution
|
||||
*
|
||||
* @package Kohana
|
||||
* @category Base
|
||||
* @author Kohana Team
|
||||
* @copyright (c) 2008-2012 Kohana Team
|
||||
* @license http://kohanaframework.org/license
|
||||
* @since 3.1.0
|
||||
*/
|
||||
class Kohana_Request_Client_Internal extends Request_Client {
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $_previous_environment;
|
||||
|
||||
/**
|
||||
* Processes the request, executing the controller action that handles this
|
||||
* request, determined by the [Route].
|
||||
*
|
||||
* $request->execute();
|
||||
*
|
||||
* @param Request $request
|
||||
* @return Response
|
||||
* @throws Kohana_Exception
|
||||
* @uses [Kohana::$profiling]
|
||||
* @uses [Profiler]
|
||||
*/
|
||||
public function execute_request(Request $request, Response $response)
|
||||
{
|
||||
// Create the class prefix
|
||||
$prefix = 'Controller_';
|
||||
|
||||
// Directory
|
||||
$directory = $request->directory();
|
||||
|
||||
// Controller
|
||||
$controller = $request->controller();
|
||||
|
||||
if ($directory)
|
||||
{
|
||||
// Add the directory name to the class prefix
|
||||
$prefix .= str_replace(array('\\', '/'), '_', trim($directory, '/')).'_';
|
||||
}
|
||||
|
||||
if (Kohana::$profiling)
|
||||
{
|
||||
// Set the benchmark name
|
||||
$benchmark = '"'.$request->uri().'"';
|
||||
|
||||
if ($request !== Request::$initial AND Request::$current)
|
||||
{
|
||||
// Add the parent request uri
|
||||
$benchmark .= ' « "'.Request::$current->uri().'"';
|
||||
}
|
||||
|
||||
// Start benchmarking
|
||||
$benchmark = Profiler::start('Requests', $benchmark);
|
||||
}
|
||||
|
||||
// Store the currently active request
|
||||
$previous = Request::$current;
|
||||
|
||||
// Change the current request to this request
|
||||
Request::$current = $request;
|
||||
|
||||
// Is this the initial request
|
||||
$initial_request = ($request === Request::$initial);
|
||||
|
||||
try
|
||||
{
|
||||
if ( ! class_exists($prefix.$controller))
|
||||
{
|
||||
throw HTTP_Exception::factory(404,
|
||||
'The requested URL :uri was not found on this server.',
|
||||
array(':uri' => $request->uri())
|
||||
)->request($request);
|
||||
}
|
||||
|
||||
// Load the controller using reflection
|
||||
$class = new ReflectionClass($prefix.$controller);
|
||||
|
||||
if ($class->isAbstract())
|
||||
{
|
||||
throw new Kohana_Exception(
|
||||
'Cannot create instances of abstract :controller',
|
||||
array(':controller' => $prefix.$controller)
|
||||
);
|
||||
}
|
||||
|
||||
// Create a new instance of the controller
|
||||
$controller = $class->newInstance($request, $response);
|
||||
|
||||
// Run the controller's execute() method
|
||||
$response = $class->getMethod('execute')->invoke($controller);
|
||||
|
||||
if ( ! $response instanceof Response)
|
||||
{
|
||||
// Controller failed to return a Response.
|
||||
throw new Kohana_Exception('Controller failed to return a Response');
|
||||
}
|
||||
}
|
||||
catch (HTTP_Exception $e)
|
||||
{
|
||||
// Get the response via the Exception
|
||||
$response = $e->get_response();
|
||||
}
|
||||
catch (Exception $e)
|
||||
{
|
||||
// Generate an appropriate Response object
|
||||
$response = Kohana_Exception::_handler($e);
|
||||
}
|
||||
|
||||
// Restore the previous request
|
||||
Request::$current = $previous;
|
||||
|
||||
if (isset($benchmark))
|
||||
{
|
||||
// Stop the benchmark
|
||||
Profiler::stop($benchmark);
|
||||
}
|
||||
|
||||
// Return the response
|
||||
return $response;
|
||||
}
|
||||
} // End Kohana_Request_Client_Internal
|
@@ -0,0 +1,10 @@
|
||||
<?php
|
||||
defined('SYSPATH') OR die('No direct script access.');
|
||||
/**
|
||||
* @package Kohana
|
||||
* @category Exceptions
|
||||
* @author Kohana Team
|
||||
* @copyright (c) 2009-2012 Kohana Team
|
||||
* @license http://kohanaframework.org/license
|
||||
*/
|
||||
class Kohana_Request_Client_Recursion_Exception extends Kohana_Exception {}
|
109
includes/kohana/system/classes/Kohana/Request/Client/Stream.php
Normal file
109
includes/kohana/system/classes/Kohana/Request/Client/Stream.php
Normal file
@@ -0,0 +1,109 @@
|
||||
<?php defined('SYSPATH') OR die('No direct script access.');
|
||||
/**
|
||||
* [Request_Client_External] Stream driver performs external requests using php
|
||||
* sockets. To use this driver, ensure the following is completed
|
||||
* before executing an external request- ideally in the application bootstrap.
|
||||
*
|
||||
* @example
|
||||
*
|
||||
* // In application bootstrap
|
||||
* Request_Client_External::$client = 'Request_Client_Stream';
|
||||
*
|
||||
* @package Kohana
|
||||
* @category Base
|
||||
* @author Kohana Team
|
||||
* @copyright (c) 2008-2012 Kohana Team
|
||||
* @license http://kohanaframework.org/license
|
||||
* @uses [PHP Streams](http://php.net/manual/en/book.stream.php)
|
||||
*/
|
||||
class Kohana_Request_Client_Stream extends Request_Client_External {
|
||||
|
||||
/**
|
||||
* Sends the HTTP message [Request] to a remote server and processes
|
||||
* the response.
|
||||
*
|
||||
* @param Request $request request to send
|
||||
* @param Response $request response to send
|
||||
* @return Response
|
||||
* @uses [PHP cURL](http://php.net/manual/en/book.curl.php)
|
||||
*/
|
||||
public function _send_message(Request $request, Response $response)
|
||||
{
|
||||
// Calculate stream mode
|
||||
$mode = ($request->method() === HTTP_Request::GET) ? 'r' : 'r+';
|
||||
|
||||
// Process cookies
|
||||
if ($cookies = $request->cookie())
|
||||
{
|
||||
$request->headers('cookie', http_build_query($cookies, NULL, '; '));
|
||||
}
|
||||
|
||||
// Get the message body
|
||||
$body = $request->body();
|
||||
|
||||
if (is_resource($body))
|
||||
{
|
||||
$body = stream_get_contents($body);
|
||||
}
|
||||
|
||||
// Set the content length
|
||||
$request->headers('content-length', (string) strlen($body));
|
||||
|
||||
list($protocol) = explode('/', $request->protocol());
|
||||
|
||||
// Create the context
|
||||
$options = array(
|
||||
strtolower($protocol) => array(
|
||||
'method' => $request->method(),
|
||||
'header' => (string) $request->headers(),
|
||||
'content' => $body
|
||||
)
|
||||
);
|
||||
|
||||
// Create the context stream
|
||||
$context = stream_context_create($options);
|
||||
|
||||
stream_context_set_option($context, $this->_options);
|
||||
|
||||
$uri = $request->uri();
|
||||
|
||||
if ($query = $request->query())
|
||||
{
|
||||
$uri .= '?'.http_build_query($query, NULL, '&');
|
||||
}
|
||||
|
||||
$stream = fopen($uri, $mode, FALSE, $context);
|
||||
|
||||
$meta_data = stream_get_meta_data($stream);
|
||||
|
||||
// Get the HTTP response code
|
||||
$http_response = array_shift($meta_data['wrapper_data']);
|
||||
|
||||
if (preg_match_all('/(\w+\/\d\.\d) (\d{3})/', $http_response, $matches) !== FALSE)
|
||||
{
|
||||
$protocol = $matches[1][0];
|
||||
$status = (int) $matches[2][0];
|
||||
}
|
||||
else
|
||||
{
|
||||
$protocol = NULL;
|
||||
$status = NULL;
|
||||
}
|
||||
|
||||
// Get any exisiting response headers
|
||||
$response_header = $response->headers();
|
||||
|
||||
// Process headers
|
||||
array_map(array($response_header, 'parse_header_string'), array(), $meta_data['wrapper_data']);
|
||||
|
||||
$response->status($status)
|
||||
->protocol($protocol)
|
||||
->body(stream_get_contents($stream));
|
||||
|
||||
// Close the stream after use
|
||||
fclose($stream);
|
||||
|
||||
return $response;
|
||||
}
|
||||
|
||||
} // End Kohana_Request_Client_Stream
|
@@ -0,0 +1,9 @@
|
||||
<?php defined('SYSPATH') OR die('No direct script access.');
|
||||
/**
|
||||
* @package Kohana
|
||||
* @category Exceptions
|
||||
* @author Kohana Team
|
||||
* @copyright (c) 2009-2012 Kohana Team
|
||||
* @license http://kohanaframework.org/license
|
||||
*/
|
||||
class Kohana_Request_Exception extends Kohana_Exception {}
|
Reference in New Issue
Block a user