Skip to content

Commit 8ff349a

Browse files
committed
Improve memory consumption for failed connection attempts
1 parent e8e53d6 commit 8ff349a

File tree

3 files changed

+147
-5
lines changed

3 files changed

+147
-5
lines changed

composer.json

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,8 @@
99
"react/dns": "^0.4.13",
1010
"react/event-loop": "^1.0 || ^0.5 || ^0.4 || ^0.3.5",
1111
"react/stream": "^1.0 || ^0.7.1",
12-
"react/promise": "^2.1 || ^1.2",
13-
"react/promise-timer": "~1.0"
12+
"react/promise": "2.x-dev as 2.6.0 || ^2.6.0 || ^1.2.1",
13+
"react/promise-timer": "dev-garbage || ^1.3.0"
1414
},
1515
"require-dev": {
1616
"clue/block-react": "^1.2",
@@ -25,5 +25,11 @@
2525
"psr-4": {
2626
"React\\Tests\\Socket\\": "tests"
2727
}
28-
}
28+
},
29+
"repositories": [
30+
{
31+
"type": "vcs",
32+
"url": "https://github.com/clue-labs/promise-timer"
33+
}
34+
]
2935
}

src/TcpConnector.php

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -112,7 +112,7 @@ private function waitForStreamOnce($stream)
112112
$resolve(new Connection($stream, $loop));
113113
}
114114
});
115-
}, function ($resolve, $reject, $progress) use ($loop, $stream) {
115+
}, function () use ($loop, $stream) {
116116
$loop->removeWriteStream($stream);
117117
fclose($stream);
118118

@@ -123,7 +123,6 @@ private function waitForStreamOnce($stream)
123123
}
124124
// @codeCoverageIgnoreEnd
125125

126-
$resolve = $reject = $progress = null;
127126
throw new RuntimeException('Cancelled while waiting for TCP/IP connection to be established');
128127
});
129128
}

tests/IntegrationTest.php

Lines changed: 137 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,143 @@ public function testConnectingFailsIfDnsUsesInvalidResolver()
114114
Block\await($connector->connect('google.com:80'), $loop, self::TIMEOUT);
115115
}
116116

117+
public function testCancellingPendingConnectionShouldNotCreateAnyGarbageReferences()
118+
{
119+
if (class_exists('React\Promise\When')) {
120+
$this->markTestSkipped('Not supported on legacy Promise v1 API');
121+
}
122+
123+
$loop = Factory::create();
124+
$connector = new Connector($loop, array('timeout' => false));
125+
126+
gc_collect_cycles();
127+
$promise = $connector->connect('8.8.8.8:80');
128+
$promise->cancel();
129+
unset($promise);
130+
131+
$this->assertEquals(0, gc_collect_cycles());
132+
}
133+
134+
public function testWaitingForRejectedConnectionShouldNotCreateAnyGarbageReferences()
135+
{
136+
if (class_exists('React\Promise\When')) {
137+
$this->markTestSkipped('Not supported on legacy Promise v1 API');
138+
}
139+
140+
$loop = Factory::create();
141+
$connector = new Connector($loop, array('timeout' => false));
142+
143+
gc_collect_cycles();
144+
145+
$wait = true;
146+
$promise = $connector->connect('127.0.0.1:1')->then(
147+
null,
148+
function ($e) use (&$wait) {
149+
$wait = false;
150+
throw $e;
151+
}
152+
);
153+
154+
// run loop for short period to ensure we detect connection refused error
155+
Block\sleep(0.01, $loop);
156+
if ($wait) {
157+
Block\sleep(0.2, $loop);
158+
if ($wait) {
159+
$this->fail('Connection attempt did not fail');
160+
}
161+
}
162+
unset($promise);
163+
164+
$this->assertEquals(0, gc_collect_cycles());
165+
}
166+
167+
public function testWaitingForConnectionTimeoutShouldNotCreateAnyGarbageReferences()
168+
{
169+
if (class_exists('React\Promise\When')) {
170+
$this->markTestSkipped('Not supported on legacy Promise v1 API');
171+
}
172+
173+
$loop = Factory::create();
174+
$connector = new Connector($loop, array('timeout' => 0.001));
175+
176+
gc_collect_cycles();
177+
178+
$wait = true;
179+
$promise = $connector->connect('google.com:80')->then(
180+
null,
181+
function ($e) use (&$wait) {
182+
$wait = false;
183+
throw $e;
184+
}
185+
);
186+
187+
// run loop for short period to ensure we detect connection timeout error
188+
Block\sleep(0.01, $loop);
189+
if ($wait) {
190+
Block\sleep(0.2, $loop);
191+
if ($wait) {
192+
$this->fail('Connection attempt did not fail');
193+
}
194+
}
195+
unset($promise);
196+
197+
$this->assertEquals(0, gc_collect_cycles());
198+
}
199+
200+
public function testWaitingForInvalidDnsConnectionShouldNotCreateAnyGarbageReferences()
201+
{
202+
if (class_exists('React\Promise\When')) {
203+
$this->markTestSkipped('Not supported on legacy Promise v1 API');
204+
}
205+
206+
$loop = Factory::create();
207+
$connector = new Connector($loop, array('timeout' => false));
208+
209+
gc_collect_cycles();
210+
211+
$wait = true;
212+
$promise = $connector->connect('example.invalid:80')->then(
213+
null,
214+
function ($e) use (&$wait) {
215+
$wait = false;
216+
throw $e;
217+
}
218+
);
219+
220+
// run loop for short period to ensure we detect DNS error
221+
Block\sleep(0.01, $loop);
222+
if ($wait) {
223+
Block\sleep(0.2, $loop);
224+
if ($wait) {
225+
$this->fail('Connection attempt did not fail');
226+
}
227+
}
228+
unset($promise);
229+
230+
$this->assertEquals(0, gc_collect_cycles());
231+
}
232+
233+
public function testWaitingForSuccessfullyClosedConnectionShouldNotCreateAnyGarbageReferences()
234+
{
235+
if (class_exists('React\Promise\When')) {
236+
$this->markTestSkipped('Not supported on legacy Promise v1 API');
237+
}
238+
239+
$loop = Factory::create();
240+
$connector = new Connector($loop, array('timeout' => false));
241+
242+
gc_collect_cycles();
243+
$promise = $connector->connect('google.com:80')->then(
244+
function ($conn) {
245+
$conn->close();
246+
}
247+
);
248+
Block\await($promise, $loop, self::TIMEOUT);
249+
unset($promise);
250+
251+
$this->assertEquals(0, gc_collect_cycles());
252+
}
253+
117254
public function testConnectingFailsIfTimeoutIsTooSmall()
118255
{
119256
if (!function_exists('stream_socket_enable_crypto')) {

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy