Skip to content

Commit 0e591b8

Browse files
HypeMCfabpot
authored andcommitted
[HttpClient] Fix CurlHttpClient memory leak
1 parent 52ba6b1 commit 0e591b8

File tree

4 files changed

+34
-14
lines changed

4 files changed

+34
-14
lines changed

Response/CurlResponse.php

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,6 @@
1717
use Symfony\Component\HttpClient\Exception\TransportException;
1818
use Symfony\Component\HttpClient\Internal\ClientState;
1919
use Symfony\Component\HttpClient\Internal\CurlClientState;
20-
use Symfony\Contracts\HttpClient\Exception\HttpExceptionInterface;
2120
use Symfony\Contracts\HttpClient\ResponseInterface;
2221

2322
/**
@@ -205,14 +204,9 @@ public function __destruct()
205204
return; // Unused pushed response
206205
}
207206

208-
$e = null;
209207
$this->doDestruct();
210-
} catch (HttpExceptionInterface $e) {
211-
throw $e;
212208
} finally {
213-
if ($e ?? false) {
214-
throw $e;
215-
}
209+
$multi = clone $this->multi;
216210

217211
$this->close();
218212

@@ -221,6 +215,8 @@ public function __destruct()
221215
$this->multi->dnsCache->evictions = $this->multi->dnsCache->evictions ?: $this->multi->dnsCache->removals;
222216
$this->multi->dnsCache->removals = $this->multi->dnsCache->hostnames = [];
223217
}
218+
219+
$this->multi = $multi;
224220
}
225221
}
226222

Response/NativeResponse.php

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@
1616
use Symfony\Component\HttpClient\Exception\TransportException;
1717
use Symfony\Component\HttpClient\Internal\ClientState;
1818
use Symfony\Component\HttpClient\Internal\NativeClientState;
19-
use Symfony\Contracts\HttpClient\Exception\HttpExceptionInterface;
2019
use Symfony\Contracts\HttpClient\ResponseInterface;
2120

2221
/**
@@ -87,14 +86,9 @@ public function getInfo(string $type = null)
8786
public function __destruct()
8887
{
8988
try {
90-
$e = null;
9189
$this->doDestruct();
92-
} catch (HttpExceptionInterface $e) {
93-
throw $e;
9490
} finally {
95-
if ($e ?? false) {
96-
throw $e;
97-
}
91+
$multi = clone $this->multi;
9892

9993
$this->close();
10094

@@ -103,6 +97,8 @@ public function __destruct()
10397
$this->multi->responseCount = 0;
10498
$this->multi->dnsCache = [];
10599
}
100+
101+
$this->multi = $multi;
106102
}
107103
}
108104

Tests/HttpClientTestCase.php

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@
1313

1414
use Symfony\Component\HttpClient\Exception\ClientException;
1515
use Symfony\Component\HttpClient\Exception\TransportException;
16+
use Symfony\Component\HttpClient\Internal\ClientState;
17+
use Symfony\Contracts\HttpClient\Exception\RedirectionExceptionInterface;
1618
use Symfony\Contracts\HttpClient\Test\HttpClientTestCase as BaseHttpClientTestCase;
1719

1820
abstract class HttpClientTestCase extends BaseHttpClientTestCase
@@ -120,4 +122,26 @@ public function testTimeoutIsNotAFatalError()
120122
throw $e;
121123
}
122124
}
125+
126+
public function testHandleIsRemovedOnException()
127+
{
128+
$client = $this->getHttpClient(__FUNCTION__);
129+
130+
try {
131+
$client->request('GET', 'http://localhost:8057/304');
132+
$this->fail(RedirectionExceptionInterface::class.' expected');
133+
} catch (RedirectionExceptionInterface $e) {
134+
// The response content-type mustn't be json as that calls getContent
135+
// @see src/Symfony/Component/HttpClient/Exception/HttpExceptionTrait.php:58
136+
$this->assertStringNotContainsString('json', $e->getResponse()->getHeaders(false)['content-type'][0] ?? '');
137+
138+
$r = new \ReflectionProperty($client, 'multi');
139+
$r->setAccessible(true);
140+
/** @var ClientState $clientState */
141+
$clientState = $r->getValue($client);
142+
143+
$this->assertCount(0, $clientState->handlesActivity);
144+
$this->assertCount(0, $clientState->openHandles);
145+
}
146+
}
123147
}

Tests/MockHttpClientTest.php

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,10 @@ protected function getHttpClient(string $testCase): HttpClientInterface
119119
$this->markTestSkipped("MockHttpClient doesn't timeout on destruct");
120120
break;
121121

122+
case 'testHandleIsRemovedOnException':
123+
$this->markTestSkipped("MockHttpClient doesn't cache handles");
124+
break;
125+
122126
case 'testGetRequest':
123127
array_unshift($headers, 'HTTP/1.1 200 OK');
124128
$responses[] = new MockResponse($body, ['response_headers' => $headers]);

0 commit comments

Comments
 (0)