8000 feature #35407 [HttpClient] collect the body of responses when possib… · symfony/symfony@07818f2 · GitHub
[go: up one dir, main page]

Skip to content

Commit 07818f2

Browse files
committed
feature #35407 [HttpClient] collect the body of responses when possible (nicolas-grekas)
This PR was merged into the 5.1-dev branch. Discussion ---------- [HttpClient] collect the body of responses when possible | Q | A | ------------- | --- | Branch? | master | Bug fix? | no | New feature? | yes | Deprecations? | no | Tickets | Part of #33311 | License | MIT | Doc PR | - This is missing one thing: the HTML part in the profiler. ![image](https://user-images.githubusercontent.com/243674/72798816-29813e00-3c44-11ea-9586-99c2c6b91640.png) ![image](https://user-images.githubusercontent.com/243674/72798851-3f8efe80-3c44-11ea-973b-7ecc64a5a542.png) Commits ------- 121f728 [HttpClient] collect the body of responses when possible
2 parents c4d15bc + 121f728 commit 07818f2

File tree

9 files changed

+172
-10
lines changed

9 files changed

+172
-10
lines changed

src/Symfony/Component/HttpClient/DataCollector/HttpClientDataCollector.php

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
use Symfony\Component\HttpFoundation\Response;
1717
use Symfony\Component\HttpKernel\DataCollector\DataCollector;
1818
use Symfony\Component\HttpKernel\DataCollector\LateDataCollectorInterface;
19+
use Symfony\Component\VarDumper\Caster\ImgStub;
1920

2021
/**
2122
* @author Jérémy Romey <jeremy@free-agent.fr>
@@ -128,8 +129,29 @@ private function collectOnClient(TraceableHttpClient $client): array
128129
}
129130
}
130131

132+
if (\is_string($content = $trace['content'])) {
133+
$contentType = 'application/octet-stream';
134+
135+
foreach ($info['response_headers'] ?? [] as $h) {
136+
if (0 === stripos($h, 'content-type: ')) {
137+
$contentType = substr($h, \strlen('content-type: '));
138+
break;
139+
}
140+
}
141+
142+
if (0 === strpos($contentType, 'image/') && class_exists(ImgStub::class)) {
143+
$content = new ImgStub($content, $contentType, '');
144+
} else {
145+
$content = [$content];
146+
}
147+
148+
$k = 'response_content';
149+
} else {
150+
$k = 'response_json';
151+
}
152+
131153
$debugInfo = array_diff_key($info, $baseInfo);
132-
$info = array_diff_key($info, $debugInfo) + ['debug_info' => $debugInfo];
154+
$info = ['info' => $debugInfo] + array_diff_key($info, $debugInfo) + [$k => $content];
133155
unset($traces[$i]['info']); // break PHP reference used by TraceableHttpClient
134156
$traces[$i]['info'] = $this->cloneVar($info);
135157
$traces[$i]['options'] = $this->cloneVar($trace['options']);
Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <fabien@symfony.com>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Symfony\Component\HttpClient\Response;
13+
14+
use Symfony\Component\HttpClient\Exception\ClientException;
15+
use Symfony\Component\HttpClient\Exception\RedirectionException;
16+
use Symfony\Component\HttpClient\Exception\ServerException;
17+
use Symfony\Contracts\HttpClient\Exception\ClientExceptionInterface;
18+
use Symfony\Contracts\HttpClient\Exception\RedirectionExceptionInterface;
19+
use Symfony\Contracts\HttpClient\Exception\ServerExceptionInterface;
20+
use Symfony\Contracts\HttpClient\Exception\TransportExceptionInterface;
21+
use Symfony\Contracts\HttpClient\HttpClientInterface;
22+
use Symfony\Contracts\HttpClient\ResponseInterface;
23+
24+
/**
25+
* @author Nicolas Grekas <p@tchwork.com>
26+
*
27+
* @internal
28+
*/
29+
class TraceableResponse implements ResponseInterface
30+
{
31+
private $client;
32+
private $response;
33+
private $content;
34+
35+
public function __construct(HttpClientInterface $client, ResponseInterface $response, &$content)
36+
{
37+
$this->client = $client;
38+
$this->response = $response;
39+
$this->content = &$content;
40+
}
41+
42+
public function getStatusCode(): int
43+
{
44+
return $this->response->getStatusCode();
45+
}
46+
47+
public function getHeaders(bool $throw = true): array
48+
{
49+
return $this->response->getHeaders($throw);
50+
}
51+
52+
public function getContent(bool $throw = true): string
53+
{
54+
$this->content = $this->response->getContent(false);
55+
56+
if ($throw) {
57+
$this->checkStatusCode($this->response->getStatusCode());
58+
}
59+
60+
return $this->content;
61+
}
62+
63+
public function toArray(bool $throw = true): array
64+
{
65+
$this->content = $this->response->toArray(false);
66+
67+
if ($throw) {
68+
$this->checkStatusCode($this->response->getStatusCode());
69+
}
70+
71+
return $this->content;
72+
}
73+
74+
public function cancel(): void
75+
{
76+
$this->response->cancel();
77+
}
78+
79+
public function getInfo(string $type = null)
80+
{
81+
return $this->response->getInfo($type);
82+
}
83+
84+
/**
85+
* Casts the response to a PHP stream resource.
86+
*
87+
* @return resource
88+
*
89+
* @throws TransportExceptionInterface When a network error occurs
90+
* @throws RedirectionExceptionInterface On a 3xx when $throw is true and the "max_redirects" option has been reached
91+
* @throws ClientExceptionInterface On a 4xx when $throw is true
92+
* @throws ServerExceptionInterface On a 5xx when $throw is true
93+
*/
94+
public function toStream(bool $throw = true)
95+
{
96+
if ($throw) {
97+
// Ensure headers arrived
98+
$this->response->getHeaders(true);
99+
}
100+
101+
if (\is_callable([$this->response, 'toStream'])) {
102+
return $this->response->toStream(false);
103+
}
104+
105+
return StreamWrapper::createResource($this->response, $this->client);
106+
}
107+
108+
private function checkStatusCode($code)
109+
{
110+
if (500 <= $code) {
111+
throw new ServerException($this);
112+
}
113+
114+
if (400 <= $code) {
115+
throw new ClientException($this);
116+
}
117+
118+
if (300 <= $code) {
119+
throw new RedirectionException($this);
120+
}
121+
}
122+
}

src/Symfony/Component/HttpClient/Tests/TraceableHttpClientTest.php

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,17 +36,18 @@ public function testItTracesRequest()
3636
return true;
3737
})
3838
)
39-
->willReturn(MockResponse::fromRequest('GET', '/foo/bar', ['options1' => 'foo'], new MockResponse()))
39+
->willReturn(MockResponse::fromRequest('GET', '/foo/bar', ['options1' => 'foo'], new MockResponse('hello')))
4040
;
4141
$sut = new TraceableHttpClient($httpClient);
42-
$sut->request('GET', '/foo/bar', ['options1' => 'foo']);
42+
$sut->request('GET', '/foo/bar', ['options1' => 'foo'])->getContent();
4343
$this->assertCount(1, $tracedRequests = $sut->getTracedRequests());
4444
$actualTracedRequest = $tracedRequests[0];
4545
$this->assertEquals([
4646
'method' => 'GET',
4747
'url' => '/foo/bar',
4848
'options' => ['options1' => 'foo'],
4949
'info' => [],
50+
'content' => 'hello',
5051
], $actualTracedRequest);
5152
}
5253

src/Symfony/Component/HttpClient/TraceableHttpClient.php

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313

1414
use Psr\Log\LoggerAwareInterface;
1515
use Psr\Log\LoggerInterface;
16+
use Symfony\Component\HttpClient\Response\TraceableResponse;
1617
use Symfony\Contracts\HttpClient\HttpClientInterface;
1718
use Symfony\Contracts\HttpClient\ResponseInterface;
1819
use Symfony\Contracts\HttpClient\ResponseStreamInterface;
@@ -36,12 +37,14 @@ public function __construct(HttpClientInterface $client)
3637
*/
3738
public function request(string $method, string $url, array $options = []): ResponseInterface
3839
{
40+
$content = '';
3941
$traceInfo = [];
4042
$this->tracedRequests[] = [
4143
'method' => $method,
4244
'url' => $url,
4345
'options' => $options,
4446
'info' => &$traceInfo,
47+
'content' => &$content,
4548
];
4649
$onProgress = $options['on_progress'] ?? null;
4750

@@ -53,15 +56,29 @@ public function request(string $method, string $url, array $options = []): Respo
5356
}
5457
};
5558

56-
return $this->client->request($method, $url, $options);
59+
return new TraceableResponse($this->client, $this->client->request($method, $url, $options), $content);
5760
}
5861

5962
/**
6063
* {@inheritdoc}
6164
*/
6265
public function stream($responses, float $timeout = null): ResponseStreamInterface
6366
{
64-
return $this->client->stream($responses, $timeout);
67+
if ($responses instanceof TraceableResponse) {
68+
$responses = [$responses];
69+
} elseif (!is_iterable($responses)) {
70+
throw new \TypeError(sprintf('%s() expects parameter 1 to be an iterable of TraceableResponse objects, %s given.', __METHOD__, \is_object($responses) ? \get_class($responses) : \gettype($responses)));
71+
}
72+
73+
return $this->client->stream(\Closure::bind(static function () use ($responses) {
74+
foreach ($responses as $k => $r) {
75+
if (!$ 10000 r instanceof TraceableResponse) {
76+
throw new \TypeError(sprintf('%s() expects parameter 1 to be an iterable of TraceableResponse objects, %s given.', __METHOD__, \is_object($r) ? \get_class($r) : \gettype($r)));
77+
}
78+
79+
yield $k => $r->response;
80+
}
81+
}, null, TraceableResponse::class), $timeout);
6582
}
6683

6784
public function getTracedRequests(): array

src/Symfony/Component/VarDumper/Caster/ImgStub.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
*/
1717
class ImgStub extends ConstStub
1818
{
19-
public function __construct(string $data, string $contentType, string $size)
19+
public function __construct(string $data, string $contentType, string $size = '')
2020
{
2121
$this->value = '';
2222
$this->attr['img-data'] = $data;

src/Symfony/Component/VarDumper/Dumper/CliDumper.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -195,7 +195,7 @@ public function dumpString(Cursor $cursor, string $str, bool $bin, int $cut)
195195
'length' => 0 <= $cut ? mb_strlen($str, 'UTF-8') + $cut : 0,
196196
'binary' => $bin,
197197
];
198-
$str = explode("\n", $str);
198+
$str = $bin && false !== strpos($str, "\0") ? [$str] : explode("\n", $str);
199199
if (isset($str[1]) && !isset($str[2]) && !isset($str[1][0])) {
200200
unset($str[1]);
201201
$str[0] .= "\n";

src/Symfony/Component/VarDumper/Tests/Dumper/CliDumperTest.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ public function testGet()
6262
6 => {$intMax}
6363
"str" => "déjà\\n"
6464
7 => b"""
65-
é\\x00test\\t\\n
65+
é\\x01test\\t\\n
6666
ing
6767
"""
6868
"[]" => []

src/Symfony/Component/VarDumper/Tests/Dumper/HtmlDumperTest.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ public function testGet()
6666
<span class=sf-dump-key>6</span> => <span class=sf-dump-num>{$intMax}</span>
6767
"<span class=sf-dump-key>str</span>" => "<span class=sf-dump-str title="5 characters">d&%s;j&%s;<span class="sf-dump-default sf-dump-ns">\\n</span></span>"
6868
<span class=sf-dump-key>7</span> => b"""
69-
<span class=sf-dump-str title="11 binary or non-UTF-8 characters">&eacute;<span class="sf-dump-default">\\x00</span>test<span class="sf-dump-default">\\t</span><span class="sf-dump-default sf-dump-ns">\\n</span></span>
69+
<span class=sf-dump-str title="11 binary or non-UTF-8 characters">&eacute;<span class="sf-dump-default">\\x01</span>test<span class="sf-dump-default">\\t</span><span class="sf-dump-default sf-dump-ns">\\n</span></span>
7070
<span class=sf-dump-str title="11 binary or non-UTF-8 characters">ing</span>
7171
"""
7272
"<span class=sf-dump-key>[]</span>" => []

src/Symfony/Component/VarDumper/Tests/Fixtures/dumb-var.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ class DumbFoo
1717
$var = [
1818
'number' => 1, null,
1919
'const' => 1.1, true, false, NAN, INF, -INF, PHP_INT_MAX,
20-
'str' => "déjà\n", "\xE9\x00test\t\ning",
20+
'str' => "déjà\n", "\xE9\x01test\t\ning",
2121
'[]' => [],
2222
40D5 'res' => $g,
2323
'obj' => $foo,

0 commit comments

Comments
 (0)
0