[HttpClient] Don't throw InvalidArgumentException on bad Location header

This commit is contained in:
Nicolas Grekas 2019-06-04 10:18:38 +02:00
parent f6a6fb6834
commit 4acca42330
5 changed files with 49 additions and 10 deletions

View File

@ -14,6 +14,7 @@ namespace Symfony\Component\HttpClient;
use Psr\Log\LoggerAwareInterface;
use Psr\Log\LoggerAwareTrait;
use Psr\Log\LoggerInterface;
use Symfony\Component\HttpClient\Exception\InvalidArgumentException;
use Symfony\Component\HttpClient\Exception\TransportException;
use Symfony\Component\HttpClient\Internal\CurlClientState;
use Symfony\Component\HttpClient\Internal\PushedResponse;
@ -392,14 +393,20 @@ final class CurlHttpClient implements HttpClientInterface, LoggerAwareInterface
}
return static function ($ch, string $location) use ($redirectHeaders) {
if ($redirectHeaders && $host = parse_url($location, PHP_URL_HOST)) {
try {
$location = self::parseUrl($location);
} catch (InvalidArgumentException $e) {
return null;
}
if ($redirectHeaders && $host = parse_url('http:'.$location['authority'], PHP_URL_HOST)) {
$requestHeaders = $redirectHeaders['host'] === $host ? $redirectHeaders['with_auth'] : $redirectHeaders['no_auth'];
curl_setopt($ch, CURLOPT_HTTPHEADER, $requestHeaders);
}
$url = self::parseUrl(curl_getinfo($ch, CURLINFO_EFFECTIVE_URL));
return implode('', self::resolveUrl(self::parseUrl($location), $url));
return implode('', self::resolveUrl($location, $url));
};
}
}

View File

@ -13,6 +13,7 @@ namespace Symfony\Component\HttpClient;
use Psr\Log\LoggerAwareInterface;
use Psr\Log\LoggerAwareTrait;
use Symfony\Component\HttpClient\Exception\InvalidArgumentException;
use Symfony\Component\HttpClient\Exception\TransportException;
use Symfony\Component\HttpClient\Internal\NativeClientState;
use Symfony\Component\HttpClient\Response\NativeResponse;
@ -352,7 +353,15 @@ final class NativeHttpClient implements HttpClientInterface, LoggerAwareInterfac
return null;
}
$url = self::resolveUrl(self::parseUrl($location), $info['url']);
try {
$url = self::parseUrl($location);
} catch (InvalidArgumentException $e) {
$info['redirect_url'] = null;
return null;
}
$url = self::resolveUrl($url, $info['url']);
$info['redirect_url'] = implode('', $url);
if ($info['redirect_count'] >= $maxRedirects) {

View File

@ -310,14 +310,19 @@ final class CurlResponse implements ResponseInterface
$info['redirect_url'] = null;
if (300 <= $statusCode && $statusCode < 400 && null !== $location) {
$info['redirect_url'] = $resolveRedirect($ch, $location);
$url = parse_url($location ?? ':');
if (null === $info['redirect_url'] = $resolveRedirect($ch, $location)) {
$options['max_redirects'] = curl_getinfo($ch, CURLINFO_REDIRECT_COUNT);
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, false);
curl_setopt($ch, CURLOPT_MAXREDIRS, $options['max_redirects']);
} else {
$url = parse_url($location ?? ':');
if (isset($url['host']) && null !== $ip = $multi->dnsCache->hostnames[$url['host'] = strtolower($url['host'])] ?? null) {
// Populate DNS cache for redirects if needed
$port = $url['port'] ?? ('http' === ($url['scheme'] ?? parse_url(curl_getinfo($ch, CURLINFO_EFFECTIVE_URL), PHP_URL_SCHEME)) ? 80 : 443);
curl_setopt($ch, CURLOPT_RESOLVE, ["{$url['host']}:$port:$ip"]);
$multi->dnsCache->removals["-{$url['host']}:$port"] = "-{$url['host']}:$port";
if (isset($url['host']) && null !== $ip = $multi->dnsCache->hostnames[$url['host'] = strtolower($url['host'])] ?? null) {
// Populate DNS cache for redirects if needed
$port = $url['port'] ?? ('http' === ($url['scheme'] ?? parse_url(curl_getinfo($ch, CURLINFO_EFFECTIVE_URL), PHP_URL_SCHEME)) ? 80 : 443);
curl_setopt($ch, CURLOPT_RESOLVE, ["{$url['host']}:$port:$ip"]);
$multi->dnsCache->removals["-{$url['host']}:$port"] = "-{$url['host']}:$port";
}
}
}

View File

@ -55,6 +55,10 @@ switch ($vars['REQUEST_URI']) {
header('Location: http://foo.example.', true, 301);
break;
case '/301/invalid':
header('Location: //?foo=bar', true, 301);
break;
case '/302':
if (!isset($vars['HTTP_AUTHORIZATION'])) {
header('Location: http://localhost:8057/', true, 302);

View File

@ -259,6 +259,20 @@ abstract class HttpClientTestCase extends TestCase
$this->assertSame($expected, $filteredHeaders);
}
public function testInvalidRedirect()
{
$client = $this->getHttpClient(__FUNCTION__);
$response = $client->request('GET', 'http://localhost:8057/301/invalid');
$this->assertSame(301, $response->getStatusCode());
$this->assertSame(['//?foo=bar'], $response->getHeaders(false)['location']);
$this->assertSame(0, $response->getInfo('redirect_count'));
$this->assertNull($response->getInfo('redirect_url'));
$this->expectException(RedirectionExceptionInterface::class);
$response->getHeaders();
}
public function testRelativeRedirects()
{
$client = $this->getHttpClient(__FUNCTION__);