1
0
mirror of https://github.com/guzzle/guzzle.git synced 2025-04-21 07:02:01 +02:00

Fix issue 2423 and issue 2448

This commit is contained in:
Mponos George 2019-12-11 19:52:13 +02:00
parent f83124f46f
commit 19964371e8
5 changed files with 101 additions and 31 deletions

View File

@ -215,36 +215,9 @@ class Client implements ClientInterface
$uri = Psr7\UriResolver::resolve(Psr7\uri_for($config['base_uri']), $uri);
}
if ($uri->getHost() && isset($config['idn_conversion']) && ($config['idn_conversion'] !== false)) {
if (isset($config['idn_conversion']) && ($config['idn_conversion'] !== false)) {
$idnOptions = ($config['idn_conversion'] === true) ? IDNA_DEFAULT : $config['idn_conversion'];
$asciiHost = idn_to_ascii($uri->getHost(), $idnOptions, INTL_IDNA_VARIANT_UTS46, $info);
if ($asciiHost === false) {
$errorBitSet = isset($info['errors']) ? $info['errors'] : 0;
$errorConstants = array_filter(array_keys(get_defined_constants()), function ($name) {
return substr($name, 0, 11) === 'IDNA_ERROR_';
});
$errors = [];
foreach ($errorConstants as $errorConstant) {
if ($errorBitSet & constant($errorConstant)) {
$errors[] = $errorConstant;
}
}
$errorMessage = 'IDN conversion failed';
if ($errors) {
$errorMessage .= ' (errors: ' . implode(', ', $errors) . ')';
}
throw new InvalidArgumentException($errorMessage);
} else {
if ($uri->getHost() !== $asciiHost) {
// Replace URI only if the ASCII version is different
$uri = $uri->withHost($asciiHost);
}
}
$uri = _idn_uri_convert($uri, $idnOptions);
}
return $uri->getScheme() === '' && $uri->getHost() !== '' ? $uri->withScheme('http') : $uri;

View File

@ -13,7 +13,7 @@ use Psr\Http\Message\UriInterface;
* Request redirect middleware.
*
* Apply this middleware like other middleware using
* {@see GuzzleHttp\Middleware::redirect()}.
* {@see \GuzzleHttp\Middleware::redirect()}.
*/
class RedirectMiddleware
{
@ -190,7 +190,13 @@ class RedirectMiddleware
$modify['body'] = '';
}
$modify['uri'] = $this->redirectUri($request, $response, $protocols);
$uri = $this->redirectUri($request, $response, $protocols);
if (isset($options['idn_conversion']) && ($options['idn_conversion'] !== false)) {
$idnOptions = ($options['idn_conversion'] === true) ? IDNA_DEFAULT : $options['idn_conversion'];
$uri = _idn_uri_convert($uri, $idnOptions);
}
$modify['uri'] = $uri;
Psr7\rewind_body($request);
// Add the Referer header if it is told to do so and only

View File

@ -1,10 +1,12 @@
<?php
namespace GuzzleHttp;
use GuzzleHttp\Exception\InvalidArgumentException;
use GuzzleHttp\Handler\CurlHandler;
use GuzzleHttp\Handler\CurlMultiHandler;
use GuzzleHttp\Handler\Proxy;
use GuzzleHttp\Handler\StreamHandler;
use Psr\Http\Message\UriInterface;
/**
* Expands a URI template
@ -344,3 +346,47 @@ function _current_time()
{
return function_exists('hrtime') ? hrtime(true) / 1e9 : microtime(true);
}
/**
* @param int $options
*
* @return UriInterface
*
* @internal
*/
function _idn_uri_convert(UriInterface $uri, $options = 0)
{
if ($uri->getHost()) {
$variant = defined('INTL_IDNA_VARIANT_UTS46') ? INTL_IDNA_VARIANT_UTS46 : 0;
$asciiHost = idn_to_ascii($uri->getHost(), $options, $variant, $info);
if ($asciiHost === false) {
$errorBitSet = isset($info['errors']) ? $info['errors'] : 0;
$errorConstants = array_filter(array_keys(get_defined_constants()), function ($name) {
return substr($name, 0, 11) === 'IDNA_ERROR_';
});
$errors = [];
foreach ($errorConstants as $errorConstant) {
if ($errorBitSet & constant($errorConstant)) {
$errors[] = $errorConstant;
}
}
$errorMessage = 'IDN conversion failed';
if ($errors) {
$errorMessage .= ' (errors: ' . implode(', ', $errors) . ')';
}
throw new InvalidArgumentException($errorMessage);
} else {
if ($uri->getHost() !== $asciiHost) {
// Replace URI only if the ASCII version is different
$uri = $uri->withHost($asciiHost);
}
}
}
return $uri;
}

View File

@ -5,11 +5,13 @@ use GuzzleHttp\Client;
use GuzzleHttp\Cookie\CookieJar;
use GuzzleHttp\Handler\MockHandler;
use GuzzleHttp\HandlerStack;
use GuzzleHttp\Middleware;
use GuzzleHttp\Promise\PromiseInterface;
use GuzzleHttp\Psr7;
use GuzzleHttp\Psr7\Request;
use GuzzleHttp\Psr7\Response;
use GuzzleHttp\Psr7\Uri;
use GuzzleHttp\RequestOptions;
use PHPUnit\Framework\TestCase;
use Psr\Http\Message\ResponseInterface;
@ -791,4 +793,36 @@ class ClientTest extends TestCase
self::assertSame('http://xn--d1acpjx3f.xn--p1ai/baz', (string) $mock->getLastRequest()->getUri());
self::assertSame('xn--d1acpjx3f.xn--p1ai', (string) $mock->getLastRequest()->getHeaderLine('Host'));
}
public function testIdnWithRedirect()
{
if (!extension_loaded('intl')) {
self::markTestSkipped('intl PHP extension is not loaded');
}
$mockHandler = new MockHandler([
new Response(302, ['Location' => 'http://www.tést.com/whatever']),
new Response()
]);
$handler = HandlerStack::create($mockHandler);
$requests = [];
$handler->push(Middleware::history($requests));
$client = new Client(['handler' => $handler]);
$client->request('GET', 'https://яндекс.рф/images', [
RequestOptions::ALLOW_REDIRECTS => [
'referer' => true,
'track_redirects' => true
],
'idn_conversion' => true
]);
$request = $mockHandler->getLastRequest();
self::assertSame('http://www.xn--tst-bma.com/whatever', (string) $request->getUri());
self::assertSame('www.xn--tst-bma.com', (string) $request->getHeaderLine('Host'));
$request = $requests[0]['request'];
self::assertSame('https://xn--d1acpjx3f.xn--p1ai/images', (string) $request->getUri());
self::assertSame('xn--d1acpjx3f.xn--p1ai', (string) $request->getHeaderLine('Host'));
}
}

View File

@ -133,6 +133,17 @@ class FunctionsTest extends TestCase
{
self::assertGreaterThan(0, GuzzleHttp\_current_time());
}
public function testIdnConvert()
{
if (!extension_loaded('intl')) {
self::markTestSkipped('intl PHP extension is not loaded');
}
$uri = GuzzleHttp\Psr7\uri_for('https://яндекс.рф/images');
$uri = GuzzleHttp\_idn_uri_convert($uri);
self::assertSame('xn--d1acpjx3f.xn--p1ai', $uri->getHost());
}
}
final class StrClass