Merge branch '4.4' into 5.0
* 4.4: Fix closing tag in mailer collector template [HttpClient] Don't read from the network faster than the CPU can deal with [DI] DecoratorServicePass should keep container.service_locator on the decorated definition
This commit is contained in:
commit
8c9e27834d
@ -94,7 +94,7 @@
|
|||||||
{% for transport in events.transports %}
|
{% for transport in events.transports %}
|
||||||
<div class="metric">
|
<div class="metric">
|
||||||
<span class="value">{{ events.messages(transport)|length }}</span>
|
<span class="value">{{ events.messages(transport)|length }}</span>
|
||||||
<span class="label">{{ events.messages(transport)|length == 1 ? 'message' : 'messages' }}</label>
|
<span class="label">{{ events.messages(transport)|length == 1 ? 'message' : 'messages' }}</span>
|
||||||
</div>
|
</div>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</div>
|
</div>
|
||||||
|
@ -78,8 +78,18 @@ class DecoratorServicePass implements CompilerPassInterface
|
|||||||
|
|
||||||
if (isset($decoratingDefinitions[$inner])) {
|
if (isset($decoratingDefinitions[$inner])) {
|
||||||
$decoratingDefinition = $decoratingDefinitions[$inner];
|
$decoratingDefinition = $decoratingDefinitions[$inner];
|
||||||
$definition->setTags(array_merge($decoratingDefinition->getTags(), $definition->getTags()));
|
|
||||||
$decoratingDefinition->setTags([]);
|
$decoratingTags = $decoratingDefinition->getTags();
|
||||||
|
$resetTags = [];
|
||||||
|
|
||||||
|
if (isset($decoratingTags['container.service_locator'])) {
|
||||||
|
// container.service_locator has special logic and it must not be transferred out to decorators
|
||||||
|
$resetTags = ['container.service_locator' => $decoratingTags['container.service_locator']];
|
||||||
|
unset($decoratingTags['container.service_locator']);
|
||||||
|
}
|
||||||
|
|
||||||
|
$definition->setTags(array_merge($decoratingTags, $definition->getTags()));
|
||||||
|
$decoratingDefinition->setTags($resetTags);
|
||||||
$decoratingDefinitions[$inner] = $definition;
|
$decoratingDefinitions[$inner] = $definition;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -223,6 +223,25 @@ class DecoratorServicePassTest extends TestCase
|
|||||||
$this->assertEquals(['bar' => ['attr' => 'baz']], $container->getDefinition('deco2')->getTags());
|
$this->assertEquals(['bar' => ['attr' => 'baz']], $container->getDefinition('deco2')->getTags());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function testProcessLeavesServiceLocatorTagOnOriginalDefinition()
|
||||||
|
{
|
||||||
|
$container = new ContainerBuilder();
|
||||||
|
$container
|
||||||
|
->register('foo')
|
||||||
|
->setTags(['container.service_locator' => [0 => []], 'bar' => ['attr' => 'baz']])
|
||||||
|
;
|
||||||
|
$container
|
||||||
|
->register('baz')
|
||||||
|
->setTags(['foobar' => ['attr' => 'bar']])
|
||||||
|
->setDecoratedService('foo')
|
||||||
|
;
|
||||||
|
|
||||||
|
$this->process($container);
|
||||||
|
|
||||||
|
$this->assertEquals(['container.service_locator' => [0 => []]], $container->getDefinition('baz.inner')->getTags());
|
||||||
|
$this->assertEquals(['bar' => ['attr' => 'baz'], 'foobar' => ['attr' => 'bar']], $container->getDefinition('baz')->getTags());
|
||||||
|
}
|
||||||
|
|
||||||
protected function process(ContainerBuilder $container)
|
protected function process(ContainerBuilder $container)
|
||||||
{
|
{
|
||||||
$pass = new DecoratorServicePass();
|
$pass = new DecoratorServicePass();
|
||||||
|
@ -116,7 +116,7 @@ final class CurlHttpClient implements HttpClientInterface, LoggerAwareInterface,
|
|||||||
$url = implode('', $url);
|
$url = implode('', $url);
|
||||||
|
|
||||||
if (!isset($options['normalized_headers']['user-agent'])) {
|
if (!isset($options['normalized_headers']['user-agent'])) {
|
||||||
$options['normalized_headers']['user-agent'][] = $options['headers'][] = 'User-Agent: Symfony HttpClient/Curl';
|
$options['headers'][] = 'User-Agent: Symfony HttpClient/Curl';
|
||||||
}
|
}
|
||||||
|
|
||||||
$curlopts = [
|
$curlopts = [
|
||||||
@ -217,8 +217,8 @@ final class CurlHttpClient implements HttpClientInterface, LoggerAwareInterface,
|
|||||||
$curlopts[CURLOPT_NOSIGNAL] = true;
|
$curlopts[CURLOPT_NOSIGNAL] = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!isset($options['normalized_headers']['accept-encoding']) && CURL_VERSION_LIBZ & self::$curlVersion['features']) {
|
if (\extension_loaded('zlib') && !isset($options['normalized_headers']['accept-encoding'])) {
|
||||||
$curlopts[CURLOPT_ENCODING] = 'gzip'; // Expose only one encoding, some servers mess up when more are provided
|
$options['headers'][] = 'Accept-Encoding: gzip'; // Expose only one encoding, some servers mess up when more are provided
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach ($options['headers'] as $header) {
|
foreach ($options['headers'] as $header) {
|
||||||
|
@ -77,7 +77,7 @@ final class NativeHttpClient implements HttpClientInterface, LoggerAwareInterfac
|
|||||||
$options['headers'][] = 'Content-Type: application/x-www-form-urlencoded';
|
$options['headers'][] = 'Content-Type: application/x-www-form-urlencoded';
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($gzipEnabled = \extension_loaded('zlib') && !isset($options['normalized_headers']['accept-encoding'])) {
|
if (\extension_loaded('zlib') && !isset($options['normalized_headers']['accept-encoding'])) {
|
||||||
// gzip is the most widely available algo, no need to deal with deflate
|
// gzip is the most widely available algo, no need to deal with deflate
|
||||||
$options['headers'][] = 'Accept-Encoding: gzip';
|
$options['headers'][] = 'Accept-Encoding: gzip';
|
||||||
}
|
}
|
||||||
@ -227,7 +227,7 @@ final class NativeHttpClient implements HttpClientInterface, LoggerAwareInterfac
|
|||||||
$context = stream_context_create($context, ['notification' => $notification]);
|
$context = stream_context_create($context, ['notification' => $notification]);
|
||||||
self::configureHeadersAndProxy($context, $host, $options['headers'], $proxy, $noProxy);
|
self::configureHeadersAndProxy($context, $host, $options['headers'], $proxy, $noProxy);
|
||||||
|
|
||||||
return new NativeResponse($this->multi, $context, implode('', $url), $options, $gzipEnabled, $info, $resolveRedirect, $onProgress, $this->logger);
|
return new NativeResponse($this->multi, $context, implode('', $url), $options, $info, $resolveRedirect, $onProgress, $this->logger);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -52,6 +52,7 @@ final class CurlResponse implements ResponseInterface
|
|||||||
|
|
||||||
$this->id = $id = (int) $ch;
|
$this->id = $id = (int) $ch;
|
||||||
$this->logger = $logger;
|
$this->logger = $logger;
|
||||||
|
$this->shouldBuffer = $options['buffer'] ?? true;
|
||||||
$this->timeout = $options['timeout'] ?? null;
|
$this->timeout = $options['timeout'] ?? null;
|
||||||
$this->info['http_method'] = $method;
|
$this->info['http_method'] = $method;
|
||||||
$this->info['user_data'] = $options['user_data'] ?? null;
|
$this->info['user_data'] = $options['user_data'] ?? null;
|
||||||
@ -65,50 +66,25 @@ final class CurlResponse implements ResponseInterface
|
|||||||
curl_setopt($ch, CURLOPT_PRIVATE, \in_array($method, ['GET', 'HEAD', 'OPTIONS', 'TRACE'], true) && 1.0 < (float) ($options['http_version'] ?? 1.1) ? 'H2' : 'H0'); // H = headers + retry counter
|
curl_setopt($ch, CURLOPT_PRIVATE, \in_array($method, ['GET', 'HEAD', 'OPTIONS', 'TRACE'], true) && 1.0 < (float) ($options['http_version'] ?? 1.1) ? 'H2' : 'H0'); // H = headers + retry counter
|
||||||
}
|
}
|
||||||
|
|
||||||
if (null === $content = &$this->content) {
|
curl_setopt($ch, CURLOPT_HEADERFUNCTION, static function ($ch, string $data) use (&$info, &$headers, $options, $multi, $id, &$location, $resolveRedirect, $logger): int {
|
||||||
$content = null === $options || true === $options['buffer'] ? fopen('php://temp', 'w+') : (\is_resource($options['buffer']) ? $options['buffer'] : null);
|
return self::parseHeaderLine($ch, $data, $info, $headers, $options, $multi, $id, $location, $resolveRedirect, $logger);
|
||||||
} else {
|
|
||||||
// Move the pushed response to the activity list
|
|
||||||
$buffer = $options['buffer'];
|
|
||||||
|
|
||||||
if ('H' !== curl_getinfo($ch, CURLINFO_PRIVATE)[0]) {
|
|
||||||
if ($options['buffer'] instanceof \Closure) {
|
|
||||||
try {
|
|
||||||
[$content, $buffer] = [null, $content];
|
|
||||||
[$content, $buffer] = [$buffer, $options['buffer']($headers)];
|
|
||||||
} catch (\Throwable $e) {
|
|
||||||
$multi->handlesActivity[$id][] = null;
|
|
||||||
$multi->handlesActivity[$id][] = $e;
|
|
||||||
[$content, $buffer] = [$buffer, false];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ftell($content)) {
|
|
||||||
rewind($content);
|
|
||||||
$multi->handlesActivity[$id][] = stream_get_contents($content);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (\is_resource($buffer)) {
|
|
||||||
$content = $buffer;
|
|
||||||
} elseif (true !== $buffer) {
|
|
||||||
$content = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
curl_setopt($ch, CURLOPT_HEADERFUNCTION, static function ($ch, string $data) use (&$info, &$headers, $options, $multi, $id, &$location, $resolveRedirect, $logger, &$content): int {
|
|
||||||
return self::parseHeaderLine($ch, $data, $info, $headers, $options, $multi, $id, $location, $resolveRedirect, $logger, $content);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
if (null === $options) {
|
if (null === $options) {
|
||||||
// Pushed response: buffer until requested
|
// Pushed response: buffer until requested
|
||||||
curl_setopt($ch, CURLOPT_WRITEFUNCTION, static function ($ch, string $data) use (&$content): int {
|
curl_setopt($ch, CURLOPT_WRITEFUNCTION, static function ($ch, string $data) use ($multi, $id): int {
|
||||||
return fwrite($content, $data);
|
$multi->handlesActivity[$id][] = $data;
|
||||||
|
curl_pause($ch, CURLPAUSE_RECV);
|
||||||
|
|
||||||
|
return \strlen($data);
|
||||||
});
|
});
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$this->inflate = !isset($options['normalized_headers']['accept-encoding']);
|
||||||
|
curl_pause($ch, CURLPAUSE_CONT);
|
||||||
|
|
||||||
if ($onProgress = $options['on_progress']) {
|
if ($onProgress = $options['on_progress']) {
|
||||||
$url = isset($info['url']) ? ['url' => $info['url']] : [];
|
$url = isset($info['url']) ? ['url' => $info['url']] : [];
|
||||||
curl_setopt($ch, CURLOPT_NOPROGRESS, false);
|
curl_setopt($ch, CURLOPT_NOPROGRESS, false);
|
||||||
@ -128,33 +104,16 @@ final class CurlResponse implements ResponseInterface
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
curl_setopt($ch, CURLOPT_WRITEFUNCTION, static function ($ch, string $data) use (&$content, $multi, $id): int {
|
curl_setopt($ch, CURLOPT_WRITEFUNCTION, static function ($ch, string $data) use ($multi, $id): int {
|
||||||
$multi->handlesActivity[$id][] = $data;
|
$multi->handlesActivity[$id][] = $data;
|
||||||
|
|
||||||
return null !== $content ? fwrite($content, $data) : \strlen($data);
|
return \strlen($data);
|
||||||
});
|
});
|
||||||
|
|
||||||
$this->initializer = static function (self $response) {
|
$this->initializer = static function (self $response) {
|
||||||
if (null !== $response->info['error']) {
|
|
||||||
throw new TransportException($response->info['error']);
|
|
||||||
}
|
|
||||||
|
|
||||||
$waitFor = curl_getinfo($ch = $response->handle, CURLINFO_PRIVATE);
|
$waitFor = curl_getinfo($ch = $response->handle, CURLINFO_PRIVATE);
|
||||||
|
|
||||||
if ('H' === $waitFor[0] || 'D' === $waitFor[0]) {
|
return 'H' === $waitFor[0] || 'D' === $waitFor[0];
|
||||||
try {
|
|
||||||
foreach (self::stream([$response]) as $chunk) {
|
|
||||||
if ($chunk->isFirst()) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (\Throwable $e) {
|
|
||||||
// Persist timeouts thrown during initialization
|
|
||||||
$response->info['error'] = $e->getMessage();
|
|
||||||
$response->close();
|
|
||||||
throw $e;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// Schedule the request in a non-blocking way
|
// Schedule the request in a non-blocking way
|
||||||
@ -241,6 +200,7 @@ final class CurlResponse implements ResponseInterface
|
|||||||
*/
|
*/
|
||||||
private function close(): void
|
private function close(): void
|
||||||
{
|
{
|
||||||
|
$this->inflate = null;
|
||||||
unset($this->multi->openHandles[$this->id], $this->multi->handlesActivity[$this->id]);
|
unset($this->multi->openHandles[$this->id], $this->multi->handlesActivity[$this->id]);
|
||||||
curl_setopt($this->handle, CURLOPT_PRIVATE, '_0');
|
curl_setopt($this->handle, CURLOPT_PRIVATE, '_0');
|
||||||
|
|
||||||
@ -419,22 +379,6 @@ final class CurlResponse implements ResponseInterface
|
|||||||
}
|
}
|
||||||
|
|
||||||
curl_setopt($ch, CURLOPT_PRIVATE, $waitFor);
|
curl_setopt($ch, CURLOPT_PRIVATE, $waitFor);
|
||||||
|
|
||||||
try {
|
|
||||||
if (!$content && $options['buffer'] instanceof \Closure && $content = $options['buffer']($headers) ?: null) {
|
|
||||||
$content = \is_resource($content) ? $content : fopen('php://temp', 'w+');
|
|
||||||
}
|
|
||||||
|
|
||||||
if (null !== $info['error']) {
|
|
||||||
throw new TransportException($info['error']);
|
|
||||||
}
|
|
||||||
} catch (\Throwable $e) {
|
|
||||||
$multi->handlesActivity[$id] = $multi->handlesActivity[$id] ?? [new FirstChunk()];
|
|
||||||
$multi->handlesActivity[$id][] = null;
|
|
||||||
$multi->handlesActivity[$id][] = $e;
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
} elseif (null !== $info['redirect_url'] && $logger) {
|
} elseif (null !== $info['redirect_url'] && $logger) {
|
||||||
$logger->info(sprintf('Redirecting: "%s %s"', $info['http_code'], $info['redirect_url']));
|
$logger->info(sprintf('Redirecting: "%s %s"', $info['http_code'], $info['redirect_url']));
|
||||||
}
|
}
|
||||||
|
@ -94,6 +94,7 @@ class MockResponse implements ResponseInterface
|
|||||||
*/
|
*/
|
||||||
protected function close(): void
|
protected function close(): void
|
||||||
{
|
{
|
||||||
|
$this->inflate = null;
|
||||||
$this->body = [];
|
$this->body = [];
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -105,22 +106,9 @@ class MockResponse implements ResponseInterface
|
|||||||
$response = new self([]);
|
$response = new self([]);
|
||||||
$response->requestOptions = $options;
|
$response->requestOptions = $options;
|
||||||
$response->id = ++self::$idSequence;
|
$response->id = ++self::$idSequence;
|
||||||
|
$response->shouldBuffer = $options['buffer'] ?? true;
|
||||||
if (!($options['buffer'] ?? null) instanceof \Closure) {
|
|
||||||
$response->content = true === ($options['buffer'] ?? true) ? fopen('php://temp', 'w+') : (\is_resource($options['buffer']) ? $options['buffer'] : null);
|
|
||||||
}
|
|
||||||
$response->initializer = static function (self $response) {
|
$response->initializer = static function (self $response) {
|
||||||
if (null !== $response->info['error']) {
|
return \is_array($response->body[0] ?? null);
|
||||||
throw new TransportException($response->info['error']);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (\is_array($response->body[0] ?? null)) {
|
|
||||||
foreach (self::stream([$response]) as $chunk) {
|
|
||||||
if ($chunk->isFirst()) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
$response->info['redirect_count'] = 0;
|
$response->info['redirect_count'] = 0;
|
||||||
@ -198,11 +186,6 @@ class MockResponse implements ResponseInterface
|
|||||||
} else {
|
} else {
|
||||||
// Data or timeout chunk
|
// Data or timeout chunk
|
||||||
$multi->handlesActivity[$id][] = $chunk;
|
$multi->handlesActivity[$id][] = $chunk;
|
||||||
|
|
||||||
if (\is_string($chunk) && null !== $response->content) {
|
|
||||||
// Buffer response body
|
|
||||||
fwrite($response->content, $chunk);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -32,7 +32,6 @@ final class NativeResponse implements ResponseInterface
|
|||||||
private $onProgress;
|
private $onProgress;
|
||||||
private $remaining;
|
private $remaining;
|
||||||
private $buffer;
|
private $buffer;
|
||||||
private $inflate;
|
|
||||||
private $multi;
|
private $multi;
|
||||||
private $debugBuffer;
|
private $debugBuffer;
|
||||||
private $shouldBuffer;
|
private $shouldBuffer;
|
||||||
@ -40,7 +39,7 @@ final class NativeResponse implements ResponseInterface
|
|||||||
/**
|
/**
|
||||||
* @internal
|
* @internal
|
||||||
*/
|
*/
|
||||||
public function __construct(NativeClientState $multi, $context, string $url, $options, bool $gzipEnabled, array &$info, callable $resolveRedirect, ?callable $onProgress, ?LoggerInterface $logger)
|
public function __construct(NativeClientState $multi, $context, string $url, array $options, array &$info, callable $resolveRedirect, ?callable $onProgress, ?LoggerInterface $logger)
|
||||||
{
|
{
|
||||||
$this->multi = $multi;
|
$this->multi = $multi;
|
||||||
$this->id = (int) $context;
|
$this->id = (int) $context;
|
||||||
@ -51,28 +50,17 @@ final class NativeResponse implements ResponseInterface
|
|||||||
$this->info = &$info;
|
$this->info = &$info;
|
||||||
$this->resolveRedirect = $resolveRedirect;
|
$this->resolveRedirect = $resolveRedirect;
|
||||||
$this->onProgress = $onProgress;
|
$this->onProgress = $onProgress;
|
||||||
$this->content = true === $options['buffer'] ? fopen('php://temp', 'w+') : (\is_resource($options['buffer']) ? $options['buffer'] : null);
|
$this->inflate = !isset($options['normalized_headers']['accept-encoding']);
|
||||||
$this->shouldBuffer = $options['buffer'] instanceof \Closure ? $options['buffer'] : null;
|
$this->shouldBuffer = $options['buffer'] ?? true;
|
||||||
|
|
||||||
// Temporary resources to dechunk/inflate the response stream
|
// Temporary resource to dechunk the response stream
|
||||||
$this->buffer = fopen('php://temp', 'w+');
|
$this->buffer = fopen('php://temp', 'w+');
|
||||||
$this->inflate = $gzipEnabled ? inflate_init(ZLIB_ENCODING_GZIP) : null;
|
|
||||||
|
|
||||||
$info['user_data'] = $options['user_data'];
|
$info['user_data'] = $options['user_data'];
|
||||||
++$multi->responseCount;
|
++$multi->responseCount;
|
||||||
|
|
||||||
$this->initializer = static function (self $response) {
|
$this->initializer = static function (self $response) {
|
||||||
if (null !== $response->info['error']) {
|
return null === $response->remaining;
|
||||||
throw new TransportException($response->info['error']);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (null === $response->remaining) {
|
|
||||||
foreach (self::stream([$response]) as $chunk) {
|
|
||||||
if ($chunk->isFirst()) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -169,7 +157,7 @@ final class NativeResponse implements ResponseInterface
|
|||||||
stream_set_blocking($h, false);
|
stream_set_blocking($h, false);
|
||||||
$this->context = $this->resolveRedirect = null;
|
$this->context = $this->resolveRedirect = null;
|
||||||
|
|
||||||
// Create dechunk and inflate buffers
|
// Create dechunk buffers
|
||||||
if (isset($this->headers['content-length'])) {
|
if (isset($this->headers['content-length'])) {
|
||||||
$this->remaining = (int) $this->headers['content-length'][0];
|
$this->remaining = (int) $this->headers['content-length'][0];
|
||||||
} elseif ('chunked' === ($this->headers['transfer-encoding'][0] ?? null)) {
|
} elseif ('chunked' === ($this->headers['transfer-encoding'][0] ?? null)) {
|
||||||
@ -179,27 +167,6 @@ final class NativeResponse implements ResponseInterface
|
|||||||
$this->remaining = -2;
|
$this->remaining = -2;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($this->inflate && 'gzip' !== ($this->headers['content-encoding'][0] ?? null)) {
|
|
||||||
$this->inflate = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
if (null !== $this->shouldBuffer && null === $this->content && $this->content = ($this->shouldBuffer)($this->headers) ?: null) {
|
|
||||||
$this->content = \is_resource($this->content) ? $this->content : fopen('php://temp', 'w+');
|
|
||||||
}
|
|
||||||
|
|
||||||
if (null !== $this->info['error']) {
|
|
||||||
throw new TransportException($this->info['error']);
|
|
||||||
}
|
|
||||||
} catch (\Throwable $e) {
|
|
||||||
$this->close();
|
|
||||||
$this->multi->handlesActivity[$this->id] = [new FirstChunk()];
|
|
||||||
$this->multi->handlesActivity[$this->id][] = null;
|
|
||||||
$this->multi->handlesActivity[$this->id][] = $e;
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
$this->multi->handlesActivity[$this->id] = [new FirstChunk()];
|
$this->multi->handlesActivity[$this->id] = [new FirstChunk()];
|
||||||
|
|
||||||
if ('HEAD' === $context['http']['method'] || \in_array($this->info['http_code'], [204, 304], true)) {
|
if ('HEAD' === $context['http']['method'] || \in_array($this->info['http_code'], [204, 304], true)) {
|
||||||
@ -209,7 +176,7 @@ final class NativeResponse implements ResponseInterface
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->multi->openHandles[$this->id] = [$h, $this->buffer, $this->inflate, $this->content, $this->onProgress, &$this->remaining, &$this->info];
|
$this->multi->openHandles[$this->id] = [$h, $this->buffer, $this->onProgress, &$this->remaining, &$this->info];
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -249,15 +216,15 @@ final class NativeResponse implements ResponseInterface
|
|||||||
$multi->handles = [];
|
$multi->handles = [];
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach ($multi->openHandles as $i => [$h, $buffer, $inflate, $content, $onProgress]) {
|
foreach ($multi->openHandles as $i => [$h, $buffer, $onProgress]) {
|
||||||
$hasActivity = false;
|
$hasActivity = false;
|
||||||
$remaining = &$multi->openHandles[$i][5];
|
$remaining = &$multi->openHandles[$i][3];
|
||||||
$info = &$multi->openHandles[$i][6];
|
$info = &$multi->openHandles[$i][4];
|
||||||
$e = null;
|
$e = null;
|
||||||
|
|
||||||
// Read incoming buffer and write it to the dechunk one
|
// Read incoming buffer and write it to the dechunk one
|
||||||
try {
|
try {
|
||||||
while ($remaining && '' !== $data = (string) fread($h, 0 > $remaining ? 16372 : $remaining)) {
|
if ($remaining && '' !== $data = (string) fread($h, 0 > $remaining ? 16372 : $remaining)) {
|
||||||
fwrite($buffer, $data);
|
fwrite($buffer, $data);
|
||||||
$hasActivity = true;
|
$hasActivity = true;
|
||||||
$multi->sleep = false;
|
$multi->sleep = false;
|
||||||
@ -285,16 +252,8 @@ final class NativeResponse implements ResponseInterface
|
|||||||
rewind($buffer);
|
rewind($buffer);
|
||||||
ftruncate($buffer, 0);
|
ftruncate($buffer, 0);
|
||||||
|
|
||||||
if (null !== $inflate && false === $data = @inflate_add($inflate, $data)) {
|
if (null === $e) {
|
||||||
$e = new TransportException('Error while processing content unencoding.');
|
|
||||||
}
|
|
||||||
|
|
||||||
if ('' !== $data && null === $e) {
|
|
||||||
$multi->handlesActivity[$i][] = $data;
|
$multi->handlesActivity[$i][] = $data;
|
||||||
|
|
||||||
if (null !== $content && \strlen($data) !== fwrite($content, $data)) {
|
|
||||||
$e = new TransportException(sprintf('Failed writing %d bytes to the response buffer.', \strlen($data)));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -43,11 +43,6 @@ trait ResponseTrait
|
|||||||
*/
|
*/
|
||||||
private $initializer;
|
private $initializer;
|
||||||
|
|
||||||
/**
|
|
||||||
* @var resource A php://temp stream typically
|
|
||||||
*/
|
|
||||||
private $content;
|
|
||||||
|
|
||||||
private $info = [
|
private $info = [
|
||||||
'response_headers' => [],
|
'response_headers' => [],
|
||||||
'http_code' => 0,
|
'http_code' => 0,
|
||||||
@ -59,6 +54,9 @@ trait ResponseTrait
|
|||||||
private $handle;
|
private $handle;
|
||||||
private $id;
|
private $id;
|
||||||
private $timeout = 0;
|
private $timeout = 0;
|
||||||
|
private $inflate;
|
||||||
|
private $shouldBuffer;
|
||||||
|
private $content;
|
||||||
private $finalInfo;
|
private $finalInfo;
|
||||||
private $offset = 0;
|
private $offset = 0;
|
||||||
private $jsonData;
|
private $jsonData;
|
||||||
@ -69,8 +67,7 @@ trait ResponseTrait
|
|||||||
public function getStatusCode(): int
|
public function getStatusCode(): int
|
||||||
{
|
{
|
||||||
if ($this->initializer) {
|
if ($this->initializer) {
|
||||||
($this->initializer)($this);
|
self::initialize($this);
|
||||||
$this->initializer = null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return $this->info['http_code'];
|
return $this->info['http_code'];
|
||||||
@ -82,8 +79,7 @@ trait ResponseTrait
|
|||||||
public function getHeaders(bool $throw = true): array
|
public function getHeaders(bool $throw = true): array
|
||||||
{
|
{
|
||||||
if ($this->initializer) {
|
if ($this->initializer) {
|
||||||
($this->initializer)($this);
|
self::initialize($this);
|
||||||
$this->initializer = null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($throw) {
|
if ($throw) {
|
||||||
@ -99,8 +95,7 @@ trait ResponseTrait
|
|||||||
public function getContent(bool $throw = true): string
|
public function getContent(bool $throw = true): string
|
||||||
{
|
{
|
||||||
if ($this->initializer) {
|
if ($this->initializer) {
|
||||||
($this->initializer)($this);
|
self::initialize($this);
|
||||||
$this->initializer = null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($throw) {
|
if ($throw) {
|
||||||
@ -231,6 +226,30 @@ trait ResponseTrait
|
|||||||
*/
|
*/
|
||||||
abstract protected static function select(ClientState $multi, float $timeout): int;
|
abstract protected static function select(ClientState $multi, float $timeout): int;
|
||||||
|
|
||||||
|
private static function initialize(self $response): void
|
||||||
|
{
|
||||||
|
if (null !== $response->info['error']) {
|
||||||
|
throw new TransportException($response->info['error']);
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
if (($response->initializer)($response)) {
|
||||||
|
foreach (self::stream([$response]) as $chunk) {
|
||||||
|
if ($chunk->isFirst()) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (\Throwable $e) {
|
||||||
|
// Persist timeouts thrown during initialization
|
||||||
|
$response->info['error'] = $e->getMessage();
|
||||||
|
$response->close();
|
||||||
|
throw $e;
|
||||||
|
}
|
||||||
|
|
||||||
|
$response->initializer = null;
|
||||||
|
}
|
||||||
|
|
||||||
private static function addResponseHeaders(array $responseHeaders, array &$info, array &$headers, string &$debug = ''): void
|
private static function addResponseHeaders(array $responseHeaders, array &$info, array &$headers, string &$debug = ''): void
|
||||||
{
|
{
|
||||||
foreach ($responseHeaders as $h) {
|
foreach ($responseHeaders as $h) {
|
||||||
@ -276,8 +295,7 @@ trait ResponseTrait
|
|||||||
private function doDestruct()
|
private function doDestruct()
|
||||||
{
|
{
|
||||||
if ($this->initializer && null === $this->info['error']) {
|
if ($this->initializer && null === $this->info['error']) {
|
||||||
($this->initializer)($this);
|
self::initialize($this);
|
||||||
$this->initializer = null;
|
|
||||||
$this->checkStatusCode();
|
$this->checkStatusCode();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -329,6 +347,16 @@ trait ResponseTrait
|
|||||||
$isTimeout = false;
|
$isTimeout = false;
|
||||||
|
|
||||||
if (\is_string($chunk = array_shift($multi->handlesActivity[$j]))) {
|
if (\is_string($chunk = array_shift($multi->handlesActivity[$j]))) {
|
||||||
|
if (null !== $response->inflate && false === $chunk = @inflate_add($response->inflate, $chunk)) {
|
||||||
|
$multi->handlesActivity[$j] = [null, new TransportException('Error while processing content unencoding.')];
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ('' !== $chunk && null !== $response->content && \strlen($chunk) !== fwrite($response->content, $chunk)) {
|
||||||
|
$multi->handlesActivity[$j] = [null, new TransportException('Failed writing %d bytes to the response buffer.', \strlen($chunk))];
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
$response->offset += \strlen($chunk);
|
$response->offset += \strlen($chunk);
|
||||||
$chunk = new DataChunk($response->offset, $chunk);
|
$chunk = new DataChunk($response->offset, $chunk);
|
||||||
} elseif (null === $chunk) {
|
} elseif (null === $chunk) {
|
||||||
@ -356,6 +384,28 @@ trait ResponseTrait
|
|||||||
$response->logger->info(sprintf('Response: "%s %s"', $info['http_code'], $info['url']));
|
$response->logger->info(sprintf('Response: "%s %s"', $info['http_code'], $info['url']));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$response->inflate = \extension_loaded('zlib') && $response->inflate && 'gzip' === ($response->headers['content-encoding'][0] ?? null) ? inflate_init(ZLIB_ENCODING_GZIP) : null;
|
||||||
|
|
||||||
|
if ($response->shouldBuffer instanceof \Closure) {
|
||||||
|
try {
|
||||||
|
$response->shouldBuffer = ($response->shouldBuffer)($response->headers);
|
||||||
|
|
||||||
|
if (null !== $response->info['error']) {
|
||||||
|
throw new TransportException($response->info['error']);
|
||||||
|
}
|
||||||
|
} catch (\Throwable $e) {
|
||||||
|
$response->close();
|
||||||
|
$multi->handlesActivity[$j] = [null, $e];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (true === $response->shouldBuffer) {
|
||||||
|
$response->content = fopen('php://temp', 'w+');
|
||||||
|
} elseif (\is_resource($response->shouldBuffer)) {
|
||||||
|
$response->content = $response->shouldBuffer;
|
||||||
|
}
|
||||||
|
$response->shouldBuffer = null;
|
||||||
|
|
||||||
yield $response => $chunk;
|
yield $response => $chunk;
|
||||||
|
|
||||||
if ($response->initializer && null === $response->info['error']) {
|
if ($response->initializer && null === $response->info['error']) {
|
||||||
|
Reference in New Issue
Block a user