Merge branch '4.3' into 4.4

* 4.3:
  [Console] Add check for Konsole/Yakuake to disable hyperlinks
  [HttpClient] work around PHP 7.3 bug related to json_encode()
  [VarDumper] fix dumping the cloner itself
  Rename the Symfony Mailer service config to avoid conflict with SwitMailer
  Set default crypto method - Fix #31105
  [Form] add missing symfony/service-contracts dependency
  [HttpClient] Don't throw InvalidArgumentException on bad Location header
This commit is contained in:
Fabien Potencier 2019-06-05 04:26:21 +02:00
commit 7207849037
12 changed files with 68 additions and 20 deletions

View File

@ -5,11 +5,12 @@
xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd">
<services>
<service id="mailer" class="Symfony\Component\Mailer\Mailer">
<service id="mailer.mailer" class="Symfony\Component\Mailer\Mailer">
<argument type="service" id="mailer.transport" />
<argument type="service" id="messenger.default_bus" on-invalid="ignore" />
</service>
<service id="Symfony\Component\Mailer\MailerInterface" alias="mailer" />
<service id="mailer" alias="mailer.mailer" />
<service id="Symfony\Component\Mailer\MailerInterface" alias="mailer.mailer" />
<service id="mailer.transport" class="Symfony\Component\Mailer\Transport\TransportInterface">
<factory class="Symfony\Component\Mailer\Transport" method="fromDsn" />

View File

@ -187,7 +187,7 @@ class OutputFormatterStyle implements OutputFormatterStyleInterface
$unsetCodes = [];
if (null === $this->handlesHrefGracefully) {
$this->handlesHrefGracefully = 'JetBrains-JediTerm' !== getenv('TERMINAL_EMULATOR');
$this->handlesHrefGracefully = 'JetBrains-JediTerm' !== getenv('TERMINAL_EMULATOR') && !getenv('KONSOLE_VERSION');
}
if (null !== $this->foreground) {

View File

@ -22,7 +22,8 @@
"symfony/options-resolver": "~4.3|^5.0",
"symfony/polyfill-ctype": "~1.8",
"symfony/polyfill-mbstring": "~1.0",
"symfony/property-access": "^3.4|^4.0|^5.0"
"symfony/property-access": "^3.4|^4.0|^5.0",
"symfony/service-contracts": "~1.1"
},
"require-dev": {
"doctrine/collections": "~1.0",

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

@ -301,7 +301,13 @@ trait HttpClientTrait
}
try {
$value = json_encode($value, $flags | (\PHP_VERSION_ID >= 70300 ? JSON_THROW_ON_ERROR : 0), $maxDepth);
if (\PHP_VERSION_ID >= 70300) {
// Work around https://bugs.php.net/77997
json_encode(null);
$flags |= JSON_THROW_ON_ERROR;
}
$value = json_encode($value, $flags, $maxDepth);
} catch (\JsonException $e) {
throw new InvalidArgumentException(sprintf('Invalid value for "json" option: %s.', $e->getMessage()));
}

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

@ -140,6 +140,9 @@ final class SocketStream extends AbstractStream
if ($this->streamContextOptions) {
$options = array_merge($options, $this->streamContextOptions);
}
if ($this->isTLS()) {
$options['ssl']['crypto_method'] = $options['ssl']['crypto_method'] ?? STREAM_CRYPTO_METHOD_TLS_CLIENT | STREAM_CRYPTO_METHOD_TLSv1_2_CLIENT | STREAM_CRYPTO_METHOD_TLSv1_1_CLIENT;
}
$streamContext = stream_context_create($options);
$this->stream = @stream_socket_client($this->url, $errno, $errstr, $this->timeout, STREAM_CLIENT_CONNECT, $streamContext);
if (false === $this->stream) {

View File

@ -84,6 +84,7 @@ abstract class AbstractCloner implements ClonerInterface
'Symfony\Component\VarDumper\Exception\ThrowingCasterException' => ['Symfony\Component\VarDumper\Caster\ExceptionCaster', 'castThrowingCasterException'],
'Symfony\Component\VarDumper\Caster\TraceStub' => ['Symfony\Component\VarDumper\Caster\ExceptionCaster', 'castTraceStub'],
'Symfony\Component\VarDumper\Caster\FrameStub' => ['Symfony\Component\VarDumper\Caster\ExceptionCaster', 'castFrameStub'],
'Symfony\Component\VarDumper\Cloner\AbstractCloner' => ['Symfony\Component\VarDumper\Caster\StubCaster', 'cutInternals'],
'Symfony\Component\Debug\Exception\SilencedErrorContext' => ['Symfony\Component\VarDumper\Caster\ExceptionCaster', 'castSilencedErrorContext'],
'ProxyManager\Proxy\ProxyInterface' => ['Symfony\Component\VarDumper\Caster\ProxyManagerCaster', 'castProxy'],
@ -188,10 +189,7 @@ abstract class AbstractCloner implements ClonerInterface
public function addCasters(array $casters)
{
foreach ($casters as $type => $callback) {
$closure = &$this->casters[$type][];
$closure = $callback instanceof \Closure ? $callback : static function (...$args) use ($callback, &$closure) {
return ($closure = \Closure::fromCallable($callback))(...$args);
};
$this->casters[$type][] = $callback;
}
}

View File

@ -435,7 +435,7 @@ class CliDumper extends AbstractDumper
}
if (null === $this->handlesHrefGracefully) {
$this->handlesHrefGracefully = 'JetBrains-JediTerm' !== getenv('TERMINAL_EMULATOR');
$this->handlesHrefGracefully = 'JetBrains-JediTerm' !== getenv('TERMINAL_EMULATOR') && !getenv('KONSOLE_VERSION');
}
if (isset($attr['ellipsis'], $attr['ellipsis-type'])) {

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__);