Added the allow_redirects option to CURLRequest.

This commit is contained in:
Lonnie Ezell 2015-11-19 23:55:52 -06:00
parent 1bb77d51ba
commit 869fb76dbc
3 changed files with 169 additions and 41 deletions

View File

@ -26,12 +26,25 @@ class CURLRequest extends Request
/**
* The setting values
*
* @var array
*/
protected $config = [
'timeout' => 0.0,
'connect_timeout' => 150,
'debug' => false
'timeout' => 0.0,
'connect_timeout' => 150,
'debug' => false,
];
/**
* Default values for when 'allow_redirects'
* option is true.
*
* @var array
*/
protected $redirectDefaults = [
'max' => 5,
'strict' => true,
'protocols' => ['http', 'https'],
];
//--------------------------------------------------------------------
@ -45,9 +58,9 @@ class CURLRequest extends Request
*
* @param array $options
*/
public function __construct(AppConfig $config, URI $uri, ResponseInterface $response=null, array $options=[])
public function __construct(AppConfig $config, URI $uri, ResponseInterface $response = null, array $options = [])
{
if (! function_exists('curl_version'))
if ( ! function_exists('curl_version'))
{
throw new \RuntimeException('CURL must be enabled to use the CURLRequest class.');
}
@ -258,13 +271,12 @@ class CURLRequest extends Request
public function method($upper = false): string
{
return ($upper)
? strtoupper($this->method)
: strtolower($this->method);
? strtoupper($this->method)
: strtolower($this->method);
}
//--------------------------------------------------------------------
/**
* Fires the actual cURL request.
*
@ -275,10 +287,10 @@ class CURLRequest extends Request
// Reset our curl options so we're on a fresh slate.
$curl_options = [];
$curl_options[CURLOPT_URL] = $url;
$curl_options[CURLOPT_URL] = $url;
$curl_options[CURLOPT_RETURNTRANSFER] = true;
$curl_options[CURLOPT_HEADER] = true;
$curl_options[CURLOPT_FRESH_CONNECT] = true;
$curl_options[CURLOPT_HEADER] = true;
$curl_options[CURLOPT_FRESH_CONNECT] = true;
$curl_options = $this->setCURLOptions($curl_options, $this->config);
$curl_options = $this->applyMethod($method, $curl_options);
@ -297,7 +309,7 @@ class CURLRequest extends Request
$this->setResponseHeaders($headers);
// Our body
$body = substr($output, $break+4);
$body = substr($output, $break + 4);
$this->response->setBody($body);
}
else
@ -316,17 +328,20 @@ class CURLRequest extends Request
*
* @param array $curl_options
*/
protected function applyRequestHeaders(array $curl_options=[]): array
protected function applyRequestHeaders(array $curl_options = []): array
{
$headers = $this->headers();
$headers = $this->headers();
if (empty($head)) return $curl_options;
if (empty($head))
{
return $curl_options;
}
$set = [];
foreach ($headers as $name => $value)
{
$set[] = $name.': '. $this->headerLine($name);
$set[] = $name.': '.$this->headerLine($name);
}
$curl_options[CURLOPT_HTTPHEADER] = $set;
@ -340,7 +355,7 @@ class CURLRequest extends Request
{
$method = strtoupper($method);
$this->method = $method;
$this->method = $method;
$curl_options[CURLOPT_CUSTOMREQUEST] = $method;
$size = strlen($this->body);
@ -349,6 +364,7 @@ class CURLRequest extends Request
if ($size === null || $size > 0)
{
$curl_options = $this->applyBody($curl_options);
return $curl_options;
}
@ -370,9 +386,9 @@ class CURLRequest extends Request
//--------------------------------------------------------------------
protected function applyBody(array $curl_options=[]): array
protected function applyBody(array $curl_options = []): array
{
if (! empty($this->body))
if ( ! empty($this->body))
{
$curl_options[CURLOPT_POSTFIELDS] = (string)$this->body();
}
@ -398,7 +414,7 @@ class CURLRequest extends Request
if (($pos = strpos($header, ':')) !== false)
{
$title = substr($header, 0, $pos);
$value = substr($header, $pos+1);
$value = substr($header, $pos + 1);
$this->response->setHeader($title, $value);
}
@ -421,14 +437,14 @@ class CURLRequest extends Request
//--------------------------------------------------------------------
protected function setCURLOptions(array $curl_options=[], array $config=[])
protected function setCURLOptions(array $curl_options = [], array $config = [])
{
// Auth Headers
if (! empty($config['auth']))
if ( ! empty($config['auth']))
{
$curl_options[CURLOPT_USERPWD] = $config['auth'][0].':'.$config['auth'][1];
if (! empty($config['auth'][2]) && strtolower($config['auth'][2]) == 'digest')
if ( ! empty($config['auth'][2]) && strtolower($config['auth'][2]) == 'digest')
{
$curl_options[CURLOPT_HTTPAUTH] = CURLAUTH_DIGEST;
}
@ -439,19 +455,19 @@ class CURLRequest extends Request
}
// Certificate
if (! empty($config['cert']))
if ( ! empty($config['cert']))
{
$cert = $config['cert'];
if (is_array($cert))
{
$curl_options[CURLOPT_SSLCERTPASSWD] = $cert[1];
$cert = $cert[0];
$cert = $cert[0];
}
if (! file_exists($cert))
if ( ! file_exists($cert))
{
throw new \InvalidArgumentException('SSL certificate not found at: '. $cert);
throw new \InvalidArgumentException('SSL certificate not found at: '.$cert);
}
$curl_options[CURLOPT_SSLCERT] = $cert;
@ -461,11 +477,11 @@ class CURLRequest extends Request
if (isset($config['debug']))
{
$curl_options[CURLOPT_VERBOSE] = 1;
$curl_options[CURLOPT_STDERR] = is_bool($config['debug']) ? fopen('php://output', 'w+') : $config['debug'];
$curl_options[CURLOPT_STDERR] = is_bool($config['debug']) ? fopen('php://output', 'w+') : $config['debug'];
}
// Decode Content
if (! empty($config['decode_content']))
if ( ! empty($config['decode_content']))
{
$accept = $this->headerLine('Accept-Encoding');
@ -475,11 +491,45 @@ class CURLRequest extends Request
}
else
{
$curl_options[CURLOPT_ENCODING] = '';
$curl_options[CURLOPT_ENCODING] = '';
$curl_options[CURLOPT_HTTPHEADER] = 'Accept-Encoding';
}
}
// Allow Redirects
if (array_key_exists('allow_redirects', $config))
{
$settings = $this->redirectDefaults;
if (is_array($config['allow_redirects']))
{
$settings = array_merge($settings, $config['allow_redirects']);
}
if ($config['allow_redirects'] === false)
{
$curl_options[CURLOPT_FOLLOWLOCATION] = 0;
}
else
{
$curl_options[CURLOPT_FOLLOWLOCATION] = 1;
$curl_options[CURLOPT_MAXREDIRS] = $settings['max'];
if ($settings['strict'] == true)
{
$curl_options[CURLOPT_POSTREDIR] = 1|2|4;
}
$protocols = 0;
foreach ($settings['protocols'] as $proto)
{
$protocols += constant('CURLPROTO_'.strtoupper($proto));
}
$curl_options[CURLOPT_REDIR_PROTOCOLS] = $protocols;
}
}
// Timeout
$curl_options[CURLOPT_TIMEOUT_MS] = (float)$config['timeout'] * 1000;
@ -508,9 +558,9 @@ class CURLRequest extends Request
// Send the request and wait for a response.
$output = curl_exec($ch);
if($output === false)
if ($output === false)
{
throw new \RuntimeException(curl_errno($ch) .': '. curl_error($ch));
throw new \RuntimeException(curl_errno($ch).': '.curl_error($ch));
}
curl_close($ch);

View File

@ -322,4 +322,56 @@ class CURLRequestTest extends PHPUnit_Framework_TestCase
//--------------------------------------------------------------------
public function testAllowRedirectsOptionFalse()
{
$response = $this->request->request('get', 'http://example.com', [
'allow_redirects' => false
]);
$options = $this->request->curl_options;
$this->assertTrue(isset($options[CURLOPT_FOLLOWLOCATION]));
$this->assertEquals(0, $options[CURLOPT_FOLLOWLOCATION]);
$this->assertFalse(isset($options[CURLOPT_MAXREDIRS]));
$this->assertFalse(isset($options[CURLOPT_REDIR_PROTOCOLS]));
}
//--------------------------------------------------------------------
public function testAllowRedirectsOptionTrue()
{
$response = $this->request->request('get', 'http://example.com', [
'allow_redirects' => true
]);
$options = $this->request->curl_options;
$this->assertTrue(isset($options[CURLOPT_FOLLOWLOCATION]));
$this->assertEquals(1, $options[CURLOPT_FOLLOWLOCATION]);
$this->assertTrue(isset($options[CURLOPT_MAXREDIRS]));
$this->assertEquals(5, $options[CURLOPT_MAXREDIRS]);
$this->assertTrue(isset($options[CURLOPT_REDIR_PROTOCOLS]));
$this->assertEquals(CURLPROTO_HTTP|CURLPROTO_HTTPS, $options[CURLOPT_REDIR_PROTOCOLS]);
}
//--------------------------------------------------------------------
public function testAllowRedirectsOptionDefaults()
{
$response = $this->request->request('get', 'http://example.com', [
'allow_redirects' => true
]);
$options = $this->request->curl_options;
$this->assertTrue(isset($options[CURLOPT_FOLLOWLOCATION]));
$this->assertEquals(1, $options[CURLOPT_FOLLOWLOCATION]);
$this->assertTrue(isset($options[CURLOPT_MAXREDIRS]));
$this->assertTrue(isset($options[CURLOPT_REDIR_PROTOCOLS]));
}
//--------------------------------------------------------------------
}

View File

@ -159,8 +159,34 @@ Request Options
This section describes all of the available options you may pass into the constructor, the ``request()`` method,
or any of the shortcut methods.
allow_redirects
===============
Auth
By default, cURL will follow all "Location:" headers the remote servers send back. The ``allow_redirects`` option
allows you to modify how that works.
If you set the value to ``false``, then it will not follow any redirects at all::
$client->request('GET', 'http://example.com', ['allow_redirects' => false]);
Setting it to ``true`` will apply the default settings to the request::
$client->request('GET', 'http://example.com', ['allow_redirects' => true]);
// Sets the following defaults:
'max' => 5, // Maximum number of redirects to follow before stopping
'strict' => true, // Ensure POST requests stay POST requests through redirects
'protocols' => ['http', 'https'] // Restrict redirects to one or more protocols
You can pass in array as the value of the ``allow_redirects`` option to specify new settings in place of the defaults::
$client->request('GET', 'http://example.com', ['allow_redirects' => [
'max' => 10,
'protocols => ['https'] // Force HTTPS domains only.
]]);
.. :note:: Following redirects does not work when PHP is in safe_mode or open_basedir is enabled.
auth
====
Allows you to provide Authentication details for `HTTP Basic <http://www.ietf.org/rfc/rfc2069.txt>`_ and
@ -171,7 +197,7 @@ the type of authentication to use, either ``basic`` or ``digest``.::
$client->request('GET', 'http://example.com', ['auth' => ['username', 'password', 'digest']]);
Body
body
====
There are two ways to set the body of the request for request types that support them, like PUT, OR POST.
@ -187,8 +213,8 @@ and functions the exact same way as the previous example. The value must be a st
Certificates
============
cert
====
To specify the location of a PEM formatted client-side certificate, pass a string with the full path to the
file as the ``cert`` option. If a password is required, set the value to an array with the first element
@ -196,8 +222,8 @@ file as the ``cert`` option. If a password is required, set the value to an arra
$client->request('get', '/', ['cert' => ['/path/server.pem', 'password']);
Connection Timeout
==================
Connect_timeout
===============
By default, CodeIgniter does not impose a limit for cURL to attempt to connect to a website. If you need to
modify this value, you can do so by passing the amount of time in seconds with the ``connect_timeout`` option.
@ -205,7 +231,7 @@ You can pass 0 to wait indefinitely::
$response->request('GET', 'http://example.com', ['connect_timeout' => 0]);
Debug
debug
=====
When ``debug`` is passed and set to ``true``, this will enable additional debugging to echo to STDOUT during the
@ -217,7 +243,7 @@ You can pass a filename as the value for debug to have the output written to a f
$response->request('GET', 'http://example.com', ['debug' => '/usr/local/curl_log.txt']);
Headers
headers
=======
While you can set any headers this request needs by using the ``setHeader()`` method, you can also pass an associative
@ -235,7 +261,7 @@ representing the header field values.::
If headers are passed into the constructor they are treated as default values that will be overridden later by any
further headers arrays or calls to ``setHeader()``.
Timeout
timeout
=======
By default, cURL functions are allowed to run as long as they take, with no time limit. You can modify this with the ``timeout``