Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Track redirects followed by curl #2

Open
wants to merge 4 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 26 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ The get/post/head functions will return an array with the following properties:
* `headers` - array, the HTTP headers returned
* `rels` - array, the parsed HTTP rel values from any `Link` headers
* `body` - string, the body of the HTTP response, or false/omit for a HEAD request
* `redirects` - array, the redirects followed, or omitted when not supported
* `error` - string, an error string. see below for the enumerated list.
* `error_description` - string,
* `url` - string, the final URL retrieved after following any redirects
Expand Down Expand Up @@ -112,6 +113,31 @@ The `rels` key will be the parsed version of any HTTP `Link` headers that contai
)
```

#### `redirects`

The `redirects` key will be an array of all the redirects followed to retrieve the final response. This key may be omitted if the transport does not support retrieving the data.

```php
Array
(
[0] => Array
(
[code] => 301
[from] => http://aaronpk.com/
[to] => https://aaronpk.com/
)

[1] => Array
(
[code] => 301
[from] => https://aaronpk.com/
[to] => https://aaronparecki.com/
)

)
```

The `code` key within a redirect array is the HTTP status code that came with it. This can be used for separating permanent redirects (301) from temporary ones (302).

### Options

Expand Down
65 changes: 47 additions & 18 deletions src/p3k/HTTP/Curl.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,11 @@ class Curl implements Transport {
protected $_timeout = 4;
protected $_max_redirects = 8;
static protected $_http_version = null;
private $_last_seen_url = null;
private $_last_seen_code = null;
private $_current_headers = [];
private $_current_redirects = [];
private $_debug_header = '';

public function set_max_redirects($max) {
$this->_max_redirects = $max;
Expand All @@ -21,16 +26,15 @@ public function get($url, $headers=[]) {
if($headers)
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
$response = curl_exec($ch);
$header_size = curl_getinfo($ch, CURLINFO_HEADER_SIZE);
$header_str = trim(substr($response, 0, $header_size));
return [
'code' => curl_getinfo($ch, CURLINFO_HTTP_CODE),
'header' => $header_str,
'body' => substr($response, $header_size),
'header' => implode("\r\n", array_map(function ($a) { return $a[0] . ': ' . $a[1]; }, $this->_current_headers)),
'body' => $response,
'redirects' => $this->_current_redirects,
'error' => self::error_string_from_code(curl_errno($ch)),
'error_description' => curl_error($ch),
'url' => curl_getinfo($ch, CURLINFO_EFFECTIVE_URL),
'debug' => $response
'url' => $this->_last_seen_url,
'debug' => $this->_debug_header . "\r\n" . $response
];
}

Expand All @@ -42,16 +46,15 @@ public function post($url, $body, $headers=[]) {
if($headers)
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
$response = curl_exec($ch);
$header_size = curl_getinfo($ch, CURLINFO_HEADER_SIZE);
$header_str = trim(substr($response, 0, $header_size));
return [
'code' => curl_getinfo($ch, CURLINFO_HTTP_CODE),
'header' => $header_str,
'body' => substr($response, $header_size),
'header' => implode("\r\n", array_map(function ($a) { return $a[0] . ': ' . $a[1]; }, $this->_current_headers)),
'body' => $response,
'redirects' => $this->_current_redirects,
'error' => self::error_string_from_code(curl_errno($ch)),
'error_description' => curl_error($ch),
'url' => curl_getinfo($ch, CURLINFO_EFFECTIVE_URL),
'debug' => $response
'url' => $this->_last_seen_url,
'debug' => $this->_debug_header . "\r\n" . $response
];
}

Expand All @@ -64,23 +67,23 @@ public function head($url, $headers=[]) {
$response = curl_exec($ch);
return [
'code' => curl_getinfo($ch, CURLINFO_HTTP_CODE),
'header' => trim($response),
'header' => implode("\r\n", array_map(function ($a) { return $a[0] . ': ' . $a[1]; }, $this->_current_headers)),
'redirects' => $this->_current_redirects,
'error' => self::error_string_from_code(curl_errno($ch)),
'error_description' => curl_error($ch),
'url' => curl_getinfo($ch, CURLINFO_EFFECTIVE_URL),
'debug' => $response
'url' => $this->_last_seen_url,
'debug' => $this->_debug_header . "\r\n" . $response
];
}

private function _set_curlopts($ch, $url) {
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HEADER, true);
if($this->_max_redirects > 0)
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, $this->_max_redirects > 0);
curl_setopt($ch, CURLOPT_MAXREDIRS, $this->_max_redirects);
curl_setopt($ch, CURLOPT_TIMEOUT_MS, round($this->_timeout * 1000));
curl_setopt($ch, CURLOPT_CONNECTTIMEOUT_MS, 2000);
curl_setopt($ch, CURLOPT_HTTP_VERSION, $this->_http_version());
curl_setopt($ch, CURLOPT_HEADERFUNCTION, [$this, '_header_function']);
}

private function _http_version() {
Expand All @@ -96,6 +99,32 @@ private function _http_version() {
return static::$_http_version;
}

private function _header_function($curl, $header) {
$this->_debug_header .= $header;
$current_url = curl_getinfo($curl, CURLINFO_EFFECTIVE_URL);
if ($current_url !== $this->_last_seen_url) {
if ($this->_last_seen_url !== null) {
$this->_current_redirects[] = [
'code' => $this->_last_seen_code,
'from' => $this->_last_seen_url,
'to' => $current_url,
];
} else {
$this->_current_redirects = [];
}
$this->_current_headers = [];
$this->_last_seen_url = $current_url;
$this->_last_seen_code = curl_getinfo($curl, CURLINFO_HTTP_CODE);
}
$length = strlen($header);
$header = explode(':', $header, 2);
if (count($header) !== 2) {
return $length;
}
$this->_current_headers[] = array_map('trim', [$header[0], $header[1]]);
return $length;
}

public static function error_string_from_code($code) {
switch($code) {
case 0:
Expand Down