-
-
Notifications
You must be signed in to change notification settings - Fork 4
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Apply timeout correctly during socket checkout and TLS handshakes
Fixes #145.
- Loading branch information
Showing
9 changed files
with
301 additions
and
51 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,52 @@ | ||
<?php | ||
|
||
namespace Amp\Artax\Internal; | ||
|
||
use Amp\CancellationToken; | ||
use Amp\CancellationTokenSource; | ||
|
||
/** @internal */ | ||
class CombinedCancellationToken implements CancellationToken { | ||
private $token; | ||
private $tokens = []; | ||
|
||
public function __construct(CancellationToken ...$tokens) { | ||
$tokenSource = new CancellationTokenSource; | ||
$this->token = $tokenSource->getToken(); | ||
|
||
foreach ($tokens as $token) { | ||
$id = $token->subscribe(function ($error) use ($tokenSource) { | ||
$tokenSource->cancel($error); | ||
}); | ||
|
||
$this->tokens[] = [$token, $id]; | ||
} | ||
} | ||
|
||
public function __destruct() { | ||
foreach ($this->tokens as list($token, $id)) { | ||
/** @var CancellationToken $token */ | ||
$token->unsubscribe($id); | ||
} | ||
} | ||
|
||
/** @inheritdoc */ | ||
public function subscribe(callable $callback): string { | ||
return $this->token->subscribe($callback); | ||
} | ||
|
||
/** @inheritdoc */ | ||
public function unsubscribe(string $id) { | ||
$this->token->unsubscribe($id); | ||
} | ||
|
||
/** @inheritdoc */ | ||
public function isRequested(): bool { | ||
return $this->token->isRequested(); | ||
} | ||
|
||
/** @inheritdoc */ | ||
public function throwIfRequested() { | ||
$this->token->throwIfRequested(); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,121 @@ | ||
<?php | ||
|
||
namespace Amp\Test\Artax; | ||
|
||
use Amp\Artax\Client; | ||
use Amp\Artax\DefaultClient; | ||
use Amp\Artax\HttpSocketPool; | ||
use Amp\Artax\Response; | ||
use Amp\Artax\TimeoutException; | ||
use Amp\CancellationToken; | ||
use Amp\Deferred; | ||
use Amp\Loop; | ||
use Amp\PHPUnit\TestCase; | ||
use Amp\Promise; | ||
use Amp\Socket; | ||
use Amp\Socket\ClientSocket; | ||
use function Amp\asyncCall; | ||
use function Amp\Promise\wait; | ||
|
||
class TimeoutTest extends TestCase { | ||
/** @var DefaultClient */ | ||
private $client; | ||
|
||
public function setUp() { | ||
$this->client = new DefaultClient; | ||
} | ||
|
||
public function testTimeoutDuringBody() { | ||
$server = Socket\listen("tcp://127.0.0.1:0"); | ||
|
||
asyncCall(function () use ($server) { | ||
/** @var Socket\ClientSocket $client */ | ||
while ($client = yield $server->accept()) { | ||
yield $client->write("HTTP/1.1 200 OK\r\nContent-Length: 2\r\n\r\n."); | ||
|
||
Loop::delay(3000, function () use ($client) { | ||
$client->close(); | ||
}); | ||
} | ||
}); | ||
|
||
try { | ||
$uri = "http://" . $server->getAddress() . "/"; | ||
|
||
$start = \microtime(true); | ||
$promise = $this->client->request($uri, [Client::OP_TRANSFER_TIMEOUT => 100]); | ||
|
||
/** @var Response $response */ | ||
$response = wait($promise); | ||
$this->expectException(TimeoutException::class); | ||
$this->expectExceptionMessage("Allowed transfer timeout exceeded: 100 ms"); | ||
wait($response->getBody()); | ||
} finally { | ||
$this->assertLessThan(0.6, \microtime(true) - $start); | ||
$server->close(); | ||
} | ||
} | ||
|
||
public function testTimeoutDuringConnect() { | ||
Loop::repeat(1000, function () { /* dummy watcher, because socket pool doesn't do anything */ }); | ||
|
||
$this->client = new DefaultClient(null, new HttpSocketPool(new class implements Socket\SocketPool { | ||
public function checkout(string $uri, CancellationToken $token = null): Promise { | ||
$deferred = new Deferred; | ||
|
||
if ($token) { | ||
$token->subscribe(function ($error) use ($deferred) { | ||
$deferred->fail($error); | ||
}); | ||
} | ||
|
||
return $deferred->promise(); // never resolve | ||
} | ||
|
||
public function checkin(ClientSocket $socket) { | ||
// ignore | ||
} | ||
|
||
public function clear(ClientSocket $socket) { | ||
// ignore | ||
} | ||
})); | ||
|
||
$this->expectException(TimeoutException::class); | ||
$this->expectExceptionMessage("Allowed transfer timeout exceeded: 100 ms"); | ||
|
||
$this->assertRunTimeLessThan(function () { | ||
wait($this->client->request("http://localhost:1337/", [Client::OP_TRANSFER_TIMEOUT => 100])); | ||
}, 600); | ||
} | ||
|
||
public function testTimeoutDuringTlsEnable() { | ||
$tlsContext = (new Socket\ServerTlsContext) | ||
->withDefaultCertificate(new Socket\Certificate(__DIR__ . "/tls/amphp.org.pem")); | ||
|
||
$server = Socket\listen("tcp://127.0.0.1:0", null, $tlsContext); | ||
|
||
asyncCall(function () use ($server) { | ||
/** @var Socket\ClientSocket $client */ | ||
while ($client = yield $server->accept()) { | ||
Loop::delay(3000, function () use ($client) { | ||
$client->close(); | ||
}); | ||
} | ||
}); | ||
|
||
try { | ||
$uri = "http://" . $server->getAddress() . "/"; | ||
|
||
$start = \microtime(true); | ||
$promise = $this->client->request($uri, [Client::OP_TRANSFER_TIMEOUT => 100]); | ||
|
||
$this->expectException(TimeoutException::class); | ||
$this->expectExceptionMessage("Allowed transfer timeout exceeded: 100 ms"); | ||
wait($promise); | ||
} finally { | ||
$this->assertLessThan(0.6, \microtime(true) - $start); | ||
$server->close(); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
-----BEGIN CERTIFICATE----- | ||
MIIDAjCCAeoCCQD9ibkUCk3KajANBgkqhkiG9w0BAQsFADBDMQswCQYDVQQGEwJE | ||
RTEQMA4GA1UECAwHR2VybWFueTEOMAwGA1UECgwFYW1waHAxEjAQBgNVBAMMCWFt | ||
cGhwLm9yZzAeFw0xNzEwMjMxMjI2NDFaFw0xODEwMjMxMjI2NDFaMEMxCzAJBgNV | ||
BAYTAkRFMRAwDgYDVQQIDAdHZXJtYW55MQ4wDAYDVQQKDAVhbXBocDESMBAGA1UE | ||
AwwJYW1waHAub3JnMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAvMq/ | ||
2ilgvZokr49wMYVpN/0hpd/992vMtVJ6sMr8LXHtP2iYCcS3oP1EfOUSKphEUBfS | ||
NbfkmQVkOysEo4+cadahqnBd7Ml++kBvwhHyP14uVp87jyZtCR/WL5BTyByweN07 | ||
sfdVyXSY9HsLBSr5vBtcgg9QHDiZfhouJDaWUwN80J6nK22IRT35i8bjORuqXm+4 | ||
XNEL1zO5mAUn3nJyaKs5gnwwwgJvGc46okJtoH/uWjFrI60LwchC9+yRgaTszznJ | ||
P7QcUtVczWdIW5dik0KesV/Wv18tCN6S4abnZVxc1tuShV+rVXedXVfDy291ws1w | ||
dKMNidp3uaIKnQof3QIDAQABMA0GCSqGSIb3DQEBCwUAA4IBAQBC9XCwPLOITsv/ | ||
M9LgcY0DZBMaoyJNhum7pxRXon7DG9k6awntyDh2h5b6oEGVFwBNMJYa0VYj6DZ+ | ||
TMT5Rqf7y2lgx/jNQUxQCzIRCerQK1bFWqYCQ8ZQbFpJTMmcJFrv++ST8R9Lz+11 | ||
eDinc304QFYo44fRZJcxrbCWIYV2kCR+2ivXxhXr2WL7egy8/XwVQawU0FyOB3ym | ||
mrIF5e8wkbst2FE7GGx87aUKqF4TubjiLg1RzuQwjEzG5C/hCXeSGhq3Zx9QfOXg | ||
xbFZwWxj9IxpuvM9gueIcFheFS7aquIjWe69jCNe46JgzjpO5pN38bC3a0t4ntVK | ||
OTwPPXAj | ||
-----END CERTIFICATE----- |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
-----BEGIN RSA PRIVATE KEY----- | ||
MIIEpAIBAAKCAQEAvMq/2ilgvZokr49wMYVpN/0hpd/992vMtVJ6sMr8LXHtP2iY | ||
CcS3oP1EfOUSKphEUBfSNbfkmQVkOysEo4+cadahqnBd7Ml++kBvwhHyP14uVp87 | ||
jyZtCR/WL5BTyByweN07sfdVyXSY9HsLBSr5vBtcgg9QHDiZfhouJDaWUwN80J6n | ||
K22IRT35i8bjORuqXm+4XNEL1zO5mAUn3nJyaKs5gnwwwgJvGc46okJtoH/uWjFr | ||
I60LwchC9+yRgaTszznJP7QcUtVczWdIW5dik0KesV/Wv18tCN6S4abnZVxc1tuS | ||
hV+rVXedXVfDy291ws1wdKMNidp3uaIKnQof3QIDAQABAoIBAC2n9pei1BNmOKMA | ||
VEiVk+mHXODJd5ijSEE9bhBdNnkjCRYBjGsoWKQlO6/ckfUdF8Aq0ppNG4pqBGBO | ||
ufN4IoJx9mzKedxuqjeI574OKwBqHVizb2riBxJi0aB1Dd3iGkdQcURLpUJv4SGW | ||
tAaO6xAzqb6GR4Bbq7sUR75YOShfz6ycP4Ri3xChkwtQ9mAjMjK2bNQweO6/5hGP | ||
c/GI889uSWGTA4lLrJZ/SIOUFFYSUzvvzEQ03pm3h4luqPWcCmPI6nUT+ytSdntK | ||
U4NtE/69Vgi0DH4f/x0gv6gGSwQ2f4ncA8/08rbfO0Z7gm/B/qB53Ek/G5EiAdCB | ||
MVT2mEECgYEA8waSN7WOdcEUE8pWH3tnLy9lO1i2Tpx/R53smJ6xgjsMyz3oxoMI | ||
UkQSL9bWy0jOVk3EDUaKxvNuME80WIcVZDfcqyEq+3vaQgfc2Lz2sQ8DGJN6pWml | ||
u2xZNrQa6fIvKbOzpLhINgCW6yFc8Yt/CCRxuv0TqzdXJ3dRmcGaXbECgYEAxt73 | ||
nOMSXXzeW5blFVNMtmZq6N9BgMiGp+9QV4oa4rNskjm3C6b+zHlHsisdMwR7WV9z | ||
Qk7DmjoSA0QzJwdvJd7RM4maZCiCF/szyAajzdWzFQRK1jt4AaQElIVzAGXLRYNV | ||
tdE24VBCTZOw5JQWHyy77spRnsLHfmoY+dwRU+0CgYBdLxLXq5TxT9RL9lRpg96/ | ||
t/Of+tcEc3tWUZaLuqWjotK4B4f+vfVt3c3/a+g2UDdV3kGjOX4y5NpGSchvfXRV | ||
VhoVwAUEIqxAj1U+Ac6xe2kbfkkTrduvbs8Sa6K6O3OcENx793Ewy68Sf1ts/qj5 | ||
zUeShEGLcA/KATVXuGVhMQKBgQCT/3K+qq4MLNU9y/oH+MnqJCYDz8HrmU+8wnhD | ||
7V88vtJZr+HtJgYRHWCh0zwTr44sYMBh5EPlDrpA/AwlS38H6948QHdJemv2dNeA | ||
UJCuqg5v4ZXgALm6XtmZvgZrkVXJEYhDmsMAwr3VBchrE1mzMZNaje5X/BSCm2qi | ||
54dtsQKBgQDQfQmh4FiGsjbXTd9ECUzdmB1Ts/1KubP4lvF4jxiSLXsQrgKRF2Vn | ||
nTWySTQhVjbpWZp5iDkXH9i6V+85wPyQPPNJS6abPFqLjFL1hbkEDQTGfikz4iuI | ||
ttDxbTSkle8ujWv1uU1cs7gMf9Ytk2K7PpWzKdiXowNkyATLje0TGQ== | ||
-----END RSA PRIVATE KEY----- |
Oops, something went wrong.