| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966 |
- <?php
- namespace React\Tests\Socket;
- use React\Promise\Promise;
- use React\Socket\HappyEyeBallsConnectionBuilder;
- use React\Dns\Model\Message;
- use React\Promise\Deferred;
- class HappyEyeBallsConnectionBuilderTest extends TestCase
- {
- public function testConnectWillResolveTwiceViaResolver()
- {
- $loop = $this->getMockBuilder('React\EventLoop\LoopInterface')->getMock();
- $loop->expects($this->never())->method('addTimer');
- $connector = $this->getMockBuilder('React\Socket\ConnectorInterface')->getMock();
- $connector->expects($this->never())->method('connect');
- $resolver = $this->getMockBuilder('React\Dns\Resolver\ResolverInterface')->getMock();
- $resolver->expects($this->exactly(2))->method('resolveAll')->withConsecutive(
- array('reactphp.org', Message::TYPE_AAAA),
- array('reactphp.org', Message::TYPE_A)
- )->willReturn(new Promise(function () { }));
- $uri = 'tcp://reactphp.org:80';
- $host = 'reactphp.org';
- $parts = parse_url($uri);
- $builder = new HappyEyeBallsConnectionBuilder($loop, $connector, $resolver, $uri, $host, $parts);
- $builder->connect();
- }
- public function testConnectWillRejectWhenBothDnsLookupsReject()
- {
- $loop = $this->getMockBuilder('React\EventLoop\LoopInterface')->getMock();
- $loop->expects($this->never())->method('addTimer');
- $connector = $this->getMockBuilder('React\Socket\ConnectorInterface')->getMock();
- $connector->expects($this->never())->method('connect');
- $resolver = $this->getMockBuilder('React\Dns\Resolver\ResolverInterface')->getMock();
- $resolver->expects($this->exactly(2))->method('resolveAll')->withConsecutive(
- array('reactphp.org', Message::TYPE_AAAA),
- array('reactphp.org', Message::TYPE_A)
- )->willReturn(new Promise(function () {
- throw new \RuntimeException('DNS lookup error');
- }));
- $uri = 'tcp://reactphp.org:80';
- $host = 'reactphp.org';
- $parts = parse_url($uri);
- $builder = new HappyEyeBallsConnectionBuilder($loop, $connector, $resolver, $uri, $host, $parts);
- $promise = $builder->connect();
- $exception = null;
- $promise->then(null, function ($e) use (&$exception) {
- $exception = $e;
- });
- $this->assertInstanceOf('RuntimeException', $exception);
- assert($exception instanceof \RuntimeException);
- $this->assertEquals('Connection to tcp://reactphp.org:80 failed during DNS lookup: DNS lookup error', $exception->getMessage());
- $this->assertEquals(0, $exception->getCode());
- $this->assertInstanceOf('RuntimeException', $exception->getPrevious());
- }
- public function testConnectWillRejectWhenBothDnsLookupsRejectWithDifferentMessages()
- {
- $loop = $this->getMockBuilder('React\EventLoop\LoopInterface')->getMock();
- $loop->expects($this->never())->method('addTimer');
- $connector = $this->getMockBuilder('React\Socket\ConnectorInterface')->getMock();
- $connector->expects($this->never())->method('connect');
- $deferred = new Deferred();
- $resolver = $this->getMockBuilder('React\Dns\Resolver\ResolverInterface')->getMock();
- $resolver->expects($this->exactly(2))->method('resolveAll')->withConsecutive(
- array('reactphp.org', Message::TYPE_AAAA),
- array('reactphp.org', Message::TYPE_A)
- )->willReturnOnConsecutiveCalls(
- $deferred->promise(),
- \React\Promise\reject(new \RuntimeException('DNS4 error'))
- );
- $uri = 'tcp://reactphp.org:80';
- $host = 'reactphp.org';
- $parts = parse_url($uri);
- $builder = new HappyEyeBallsConnectionBuilder($loop, $connector, $resolver, $uri, $host, $parts);
- $promise = $builder->connect();
- $deferred->reject(new \RuntimeException('DNS6 error'));
- $exception = null;
- $promise->then(null, function ($e) use (&$exception) {
- $exception = $e;
- });
- $this->assertInstanceOf('RuntimeException', $exception);
- assert($exception instanceof \RuntimeException);
- $this->assertEquals('Connection to tcp://reactphp.org:80 failed during DNS lookup. Last error for IPv6: DNS6 error. Previous error for IPv4: DNS4 error', $exception->getMessage());
- $this->assertEquals(0, $exception->getCode());
- $this->assertInstanceOf('RuntimeException', $exception->getPrevious());
- }
- public function testConnectWillStartDelayTimerWhenIpv4ResolvesAndIpv6IsPending()
- {
- $loop = $this->getMockBuilder('React\EventLoop\LoopInterface')->getMock();
- $loop->expects($this->once())->method('addTimer')->with(0.05, $this->anything());
- $loop->expects($this->never())->method('cancelTimer');
- $connector = $this->getMockBuilder('React\Socket\ConnectorInterface')->getMock();
- $connector->expects($this->never())->method('connect');
- $resolver = $this->getMockBuilder('React\Dns\Resolver\ResolverInterface')->getMock();
- $resolver->expects($this->exactly(2))->method('resolveAll')->withConsecutive(
- array('reactphp.org', Message::TYPE_AAAA),
- array('reactphp.org', Message::TYPE_A)
- )->willReturnOnConsecutiveCalls(
- new Promise(function () { }),
- \React\Promise\resolve(array('127.0.0.1'))
- );
- $uri = 'tcp://reactphp.org:80';
- $host = 'reactphp.org';
- $parts = parse_url($uri);
- $builder = new HappyEyeBallsConnectionBuilder($loop, $connector, $resolver, $uri, $host, $parts);
- $builder->connect();
- }
- public function testConnectWillStartConnectingWithAttemptTimerButWithoutResolutionTimerWhenIpv6ResolvesAndIpv4IsPending()
- {
- $loop = $this->getMockBuilder('React\EventLoop\LoopInterface')->getMock();
- $loop->expects($this->once())->method('addTimer')->with(0.1, $this->anything());
- $loop->expects($this->never())->method('cancelTimer');
- $connector = $this->getMockBuilder('React\Socket\ConnectorInterface')->getMock();
- $connector->expects($this->once())->method('connect')->with('tcp://[::1]:80?hostname=reactphp.org')->willReturn(new Promise(function () { }));
- $resolver = $this->getMockBuilder('React\Dns\Resolver\ResolverInterface')->getMock();
- $resolver->expects($this->exactly(2))->method('resolveAll')->withConsecutive(
- array('reactphp.org', Message::TYPE_AAAA),
- array('reactphp.org', Message::TYPE_A)
- )->willReturnOnConsecutiveCalls(
- \React\Promise\resolve(array('::1')),
- new Promise(function () { })
- );
- $uri = 'tcp://reactphp.org:80';
- $host = 'reactphp.org';
- $parts = parse_url($uri);
- $builder = new HappyEyeBallsConnectionBuilder($loop, $connector, $resolver, $uri, $host, $parts);
- $builder->connect();
- }
- public function testConnectWillStartConnectingAndWillStartNextConnectionWithNewAttemptTimerWhenNextAttemptTimerFiresWithIpv4StillPending()
- {
- $timer = null;
- $loop = $this->getMockBuilder('React\EventLoop\LoopInterface')->getMock();
- $loop->expects($this->exactly(2))->method('addTimer')->with(0.1, $this->callback(function ($cb) use (&$timer) {
- $timer = $cb;
- return true;
- }));
- $loop->expects($this->never())->method('cancelTimer');
- $connector = $this->getMockBuilder('React\Socket\ConnectorInterface')->getMock();
- $connector->expects($this->exactly(2))->method('connect')->willReturn(new Promise(function () { }));
- $resolver = $this->getMockBuilder('React\Dns\Resolver\ResolverInterface')->getMock();
- $resolver->expects($this->exactly(2))->method('resolveAll')->withConsecutive(
- array('reactphp.org', Message::TYPE_AAAA),
- array('reactphp.org', Message::TYPE_A)
- )->willReturnOnConsecutiveCalls(
- \React\Promise\resolve(array('::1', '::2')),
- new Promise(function () { })
- );
- $uri = 'tcp://reactphp.org:80';
- $host = 'reactphp.org';
- $parts = parse_url($uri);
- $builder = new HappyEyeBallsConnectionBuilder($loop, $connector, $resolver, $uri, $host, $parts);
- $builder->connect();
- $this->assertNotNull($timer);
- $timer();
- }
- public function testConnectWillStartConnectingAndWillDoNothingWhenNextAttemptTimerFiresWithNoOtherIps()
- {
- $timer = null;
- $loop = $this->getMockBuilder('React\EventLoop\LoopInterface')->getMock();
- $loop->expects($this->once())->method('addTimer')->with(0.1, $this->callback(function ($cb) use (&$timer) {
- $timer = $cb;
- return true;
- }));
- $loop->expects($this->never())->method('cancelTimer');
- $connector = $this->getMockBuilder('React\Socket\ConnectorInterface')->getMock();
- $connector->expects($this->once())->method('connect')->with('tcp://[::1]:80?hostname=reactphp.org')->willReturn(new Promise(function () { }));
- $resolver = $this->getMockBuilder('React\Dns\Resolver\ResolverInterface')->getMock();
- $resolver->expects($this->exactly(2))->method('resolveAll')->withConsecutive(
- array('reactphp.org', Message::TYPE_AAAA),
- array('reactphp.org', Message::TYPE_A)
- )->willReturnOnConsecutiveCalls(
- \React\Promise\resolve(array('::1')),
- new Promise(function () { })
- );
- $uri = 'tcp://reactphp.org:80';
- $host = 'reactphp.org';
- $parts = parse_url($uri);
- $builder = new HappyEyeBallsConnectionBuilder($loop, $connector, $resolver, $uri, $host, $parts);
- $builder->connect();
- $this->assertNotNull($timer);
- $timer();
- }
- public function testConnectWillStartConnectingWithAttemptTimerButWithoutResolutionTimerWhenIpv6ResolvesAndWillCancelAttemptTimerWhenIpv4Rejects()
- {
- $timer = $this->getMockBuilder('React\EventLoop\TimerInterface')->getMock();
- $loop = $this->getMockBuilder('React\EventLoop\LoopInterface')->getMock();
- $loop->expects($this->once())->method('addTimer')->with(0.1, $this->anything())->willReturn($timer);
- $loop->expects($this->once())->method('cancelTimer')->with($timer);
- $connector = $this->getMockBuilder('React\Socket\ConnectorInterface')->getMock();
- $connector->expects($this->once())->method('connect')->with('tcp://[::1]:80?hostname=reactphp.org')->willReturn(new Promise(function () { }));
- $deferred = new Deferred();
- $resolver = $this->getMockBuilder('React\Dns\Resolver\ResolverInterface')->getMock();
- $resolver->expects($this->exactly(2))->method('resolveAll')->withConsecutive(
- array('reactphp.org', Message::TYPE_AAAA),
- array('reactphp.org', Message::TYPE_A)
- )->willReturnOnConsecutiveCalls(
- \React\Promise\resolve(array('::1')),
- $deferred->promise()
- );
- $uri = 'tcp://reactphp.org:80';
- $host = 'reactphp.org';
- $parts = parse_url($uri);
- $builder = new HappyEyeBallsConnectionBuilder($loop, $connector, $resolver, $uri, $host, $parts);
- $builder->connect();
- $deferred->reject(new \RuntimeException());
- }
- public function testConnectWillStartConnectingWithAttemptTimerWhenIpv6AndIpv4ResolvesAndWillStartNextConnectionAttemptWithoutAttemptTimerImmediatelyWhenFirstConnectionAttemptFails()
- {
- $timer = $this->getMockBuilder('React\EventLoop\TimerInterface')->getMock();
- $loop = $this->getMockBuilder('React\EventLoop\LoopInterface')->getMock();
- $loop->expects($this->once())->method('addTimer')->with(0.1, $this->anything())->willReturn($timer);
- $loop->expects($this->once())->method('cancelTimer')->with($timer);
- $deferred = new Deferred();
- $connector = $this->getMockBuilder('React\Socket\ConnectorInterface')->getMock();
- $connector->expects($this->exactly(2))->method('connect')->withConsecutive(
- array('tcp://[::1]:80?hostname=reactphp.org'),
- array('tcp://127.0.0.1:80?hostname=reactphp.org')
- )->willReturnOnConsecutiveCalls(
- $deferred->promise(),
- new Promise(function () { })
- );
- $resolver = $this->getMockBuilder('React\Dns\Resolver\ResolverInterface')->getMock();
- $resolver->expects($this->exactly(2))->method('resolveAll')->withConsecutive(
- array('reactphp.org', Message::TYPE_AAAA),
- array('reactphp.org', Message::TYPE_A)
- )->willReturnOnConsecutiveCalls(
- \React\Promise\resolve(array('::1')),
- \React\Promise\resolve(array('127.0.0.1'))
- );
- $uri = 'tcp://reactphp.org:80';
- $host = 'reactphp.org';
- $parts = parse_url($uri);
- $builder = new HappyEyeBallsConnectionBuilder($loop, $connector, $resolver, $uri, $host, $parts);
- $builder->connect();
- $deferred->reject(new \RuntimeException());
- }
- public function testConnectWillStartConnectingWithAlternatingIPv6AndIPv4WhenResolverReturnsMultipleIPAdresses()
- {
- $timer = $this->getMockBuilder('React\EventLoop\TimerInterface')->getMock();
- $loop = $this->getMockBuilder('React\EventLoop\LoopInterface')->getMock();
- $loop->expects($this->once())->method('addTimer')->with(0.1, $this->anything())->willReturn($timer);
- $loop->expects($this->once())->method('cancelTimer')->with($timer);
- $deferred = new Deferred();
- $connector = $this->getMockBuilder('React\Socket\ConnectorInterface')->getMock();
- $connector->expects($this->exactly(4))->method('connect')->withConsecutive(
- array('tcp://[::1]:80?hostname=reactphp.org'),
- array('tcp://127.0.0.1:80?hostname=reactphp.org'),
- array('tcp://[::1]:80?hostname=reactphp.org'),
- array('tcp://127.0.0.1:80?hostname=reactphp.org')
- )->willReturnOnConsecutiveCalls(
- $deferred->promise(),
- $deferred->promise(),
- $deferred->promise(),
- new Promise(function () { })
- );
- $resolver = $this->getMockBuilder('React\Dns\Resolver\ResolverInterface')->getMock();
- $resolver->expects($this->exactly(2))->method('resolveAll')->withConsecutive(
- array('reactphp.org', Message::TYPE_AAAA),
- array('reactphp.org', Message::TYPE_A)
- )->willReturnOnConsecutiveCalls(
- \React\Promise\resolve(array('::1', '::1')),
- \React\Promise\resolve(array('127.0.0.1', '127.0.0.1'))
- );
- $uri = 'tcp://reactphp.org:80';
- $host = 'reactphp.org';
- $parts = parse_url($uri);
- $builder = new HappyEyeBallsConnectionBuilder($loop, $connector, $resolver, $uri, $host, $parts);
- $builder->connect();
- $deferred->reject(new \RuntimeException());
- }
- public function testConnectWillStartConnectingWithAttemptTimerWhenOnlyIpv6ResolvesAndWillStartNextConnectionAttemptWithoutAttemptTimerImmediatelyWhenFirstConnectionAttemptFails()
- {
- $timer = $this->getMockBuilder('React\EventLoop\TimerInterface')->getMock();
- $loop = $this->getMockBuilder('React\EventLoop\LoopInterface')->getMock();
- $loop->expects($this->once())->method('addTimer')->with(0.1, $this->anything())->willReturn($timer);
- $loop->expects($this->once())->method('cancelTimer')->with($timer);
- $connector = $this->getMockBuilder('React\Socket\ConnectorInterface')->getMock();
- $connector->expects($this->exactly(2))->method('connect')->withConsecutive(
- array('tcp://[::1]:80?hostname=reactphp.org'),
- array('tcp://[::1]:80?hostname=reactphp.org')
- )->willReturnOnConsecutiveCalls(
- \React\Promise\reject(new \RuntimeException()),
- new Promise(function () { })
- );
- $resolver = $this->getMockBuilder('React\Dns\Resolver\ResolverInterface')->getMock();
- $resolver->expects($this->exactly(2))->method('resolveAll')->withConsecutive(
- array('reactphp.org', Message::TYPE_AAAA),
- array('reactphp.org', Message::TYPE_A)
- )->willReturnOnConsecutiveCalls(
- \React\Promise\resolve(array('::1', '::1')),
- \React\Promise\reject(new \RuntimeException())
- );
- $uri = 'tcp://reactphp.org:80';
- $host = 'reactphp.org';
- $parts = parse_url($uri);
- $builder = new HappyEyeBallsConnectionBuilder($loop, $connector, $resolver, $uri, $host, $parts);
- $builder->connect();
- }
- public function testConnectWillStartConnectingAndWillStartNextConnectionWithoutNewAttemptTimerWhenNextAttemptTimerFiresAfterIpv4Rejected()
- {
- $timer = null;
- $loop = $this->getMockBuilder('React\EventLoop\LoopInterface')->getMock();
- $loop->expects($this->once())->method('addTimer')->with(0.1, $this->callback(function ($cb) use (&$timer) {
- $timer = $cb;
- return true;
- }));
- $loop->expects($this->never())->method('cancelTimer');
- $connector = $this->getMockBuilder('React\Socket\ConnectorInterface')->getMock();
- $connector->expects($this->exactly(2))->method('connect')->willReturn(new Promise(function () { }));
- $deferred = new Deferred();
- $resolver = $this->getMockBuilder('React\Dns\Resolver\ResolverInterface')->getMock();
- $resolver->expects($this->exactly(2))->method('resolveAll')->withConsecutive(
- array('reactphp.org', Message::TYPE_AAAA),
- array('reactphp.org', Message::TYPE_A)
- )->willReturnOnConsecutiveCalls(
- \React\Promise\resolve(array('::1', '::2')),
- $deferred->promise()
- );
- $uri = 'tcp://reactphp.org:80';
- $host = 'reactphp.org';
- $parts = parse_url($uri);
- $builder = new HappyEyeBallsConnectionBuilder($loop, $connector, $resolver, $uri, $host, $parts);
- $builder->connect();
- $deferred->reject(new \RuntimeException());
- $this->assertNotNull($timer);
- $timer();
- }
- public function testConnectWillStartAndCancelResolutionTimerAndStartAttemptTimerWhenIpv4ResolvesAndIpv6ResolvesAfterwardsAndStartConnectingToIpv6()
- {
- $timerDelay = $this->getMockBuilder('React\EventLoop\TimerInterface')->getMock();
- $timerAttempt = $this->getMockBuilder('React\EventLoop\TimerInterface')->getMock();
- $loop = $this->getMockBuilder('React\EventLoop\LoopInterface')->getMock();
- $loop->expects($this->exactly(2))->method('addTimer')->withConsecutive(
- array(
- 0.05,
- $this->anything()
- ),
- array(
- 0.1,
- $this->anything()
- )
- )->willReturnOnConsecutiveCalls($timerDelay, $timerAttempt);
- $loop->expects($this->once())->method('cancelTimer')->with($timerDelay);
- $connector = $this->getMockBuilder('React\Socket\ConnectorInterface')->getMock();
- $connector->expects($this->once())->method('connect')->with('tcp://[::1]:80?hostname=reactphp.org')->willReturn(new Promise(function () { }));
- $deferred = new Deferred();
- $resolver = $this->getMockBuilder('React\Dns\Resolver\ResolverInterface')->getMock();
- $resolver->expects($this->exactly(2))->method('resolveAll')->withConsecutive(
- array('reactphp.org', Message::TYPE_AAAA),
- array('reactphp.org', Message::TYPE_A)
- )->willReturnOnConsecutiveCalls(
- $deferred->promise(),
- \React\Promise\resolve(array('127.0.0.1'))
- );
- $uri = 'tcp://reactphp.org:80';
- $host = 'reactphp.org';
- $parts = parse_url($uri);
- $builder = new HappyEyeBallsConnectionBuilder($loop, $connector, $resolver, $uri, $host, $parts);
- $builder->connect();
- $deferred->resolve(array('::1'));
- }
- public function testConnectWillRejectWhenOnlyTcp6ConnectionRejectsAndCancelNextAttemptTimerImmediately()
- {
- $timer = $this->getMockBuilder('React\EventLoop\TimerInterface')->getMock();
- $loop = $this->getMockBuilder('React\EventLoop\LoopInterface')->getMock();
- $loop->expects($this->once())->method('addTimer')->with(0.1, $this->anything())->willReturn($timer);
- $loop->expects($this->once())->method('cancelTimer')->with($timer);
- $deferred = new Deferred();
- $connector = $this->getMockBuilder('React\Socket\ConnectorInterface')->getMock();
- $connector->expects($this->once())->method('connect')->with('tcp://[::1]:80?hostname=reactphp.org')->willReturn($deferred->promise());
- $resolver = $this->getMockBuilder('React\Dns\Resolver\ResolverInterface')->getMock();
- $resolver->expects($this->exactly(2))->method('resolveAll')->withConsecutive(
- array('reactphp.org', Message::TYPE_AAAA),
- array('reactphp.org', Message::TYPE_A)
- )->willReturnOnConsecutiveCalls(
- \React\Promise\resolve(array('::1')),
- \React\Promise\reject(new \RuntimeException('DNS failed'))
- );
- $uri = 'tcp://reactphp.org:80';
- $host = 'reactphp.org';
- $parts = parse_url($uri);
- $builder = new HappyEyeBallsConnectionBuilder($loop, $connector, $resolver, $uri, $host, $parts);
- $promise = $builder->connect();
- $deferred->reject(new \RuntimeException(
- 'Connection refused (ECONNREFUSED)',
- defined('SOCKET_ECONNREFUSED') ? SOCKET_ECONNREFUSED : 111
- ));
- $exception = null;
- $promise->then(null, function ($e) use (&$exception) {
- $exception = $e;
- });
- $this->assertInstanceOf('RuntimeException', $exception);
- assert($exception instanceof \RuntimeException);
- $this->assertEquals('Connection to tcp://reactphp.org:80 failed: Last error for IPv6: Connection refused (ECONNREFUSED). Previous error for IPv4: DNS failed', $exception->getMessage());
- $this->assertEquals(defined('SOCKET_ECONNREFUSED') ? SOCKET_ECONNREFUSED : 111, $exception->getCode());
- $this->assertInstanceOf('RuntimeException', $exception->getPrevious());
- }
- public function testConnectWillRejectWhenOnlyTcp4ConnectionRejectsAndWillNeverStartNextAttemptTimer()
- {
- $loop = $this->getMockBuilder('React\EventLoop\LoopInterface')->getMock();
- $loop->expects($this->never())->method('addTimer');
- $deferred = new Deferred();
- $connector = $this->getMockBuilder('React\Socket\ConnectorInterface')->getMock();
- $connector->expects($this->once())->method('connect')->with('tcp://127.0.0.1:80?hostname=reactphp.org')->willReturn($deferred->promise());
- $resolver = $this->getMockBuilder('React\Dns\Resolver\ResolverInterface')->getMock();
- $resolver->expects($this->exactly(2))->method('resolveAll')->withConsecutive(
- array('reactphp.org', Message::TYPE_AAAA),
- array('reactphp.org', Message::TYPE_A)
- )->willReturnOnConsecutiveCalls(
- \React\Promise\reject(new \RuntimeException('DNS failed')),
- \React\Promise\resolve(array('127.0.0.1'))
- );
- $uri = 'tcp://reactphp.org:80';
- $host = 'reactphp.org';
- $parts = parse_url($uri);
- $builder = new HappyEyeBallsConnectionBuilder($loop, $connector, $resolver, $uri, $host, $parts);
- $promise = $builder->connect();
- $deferred->reject(new \RuntimeException(
- 'Connection refused (ECONNREFUSED)',
- defined('SOCKET_ECONNREFUSED') ? SOCKET_ECONNREFUSED : 111
- ));
- $exception = null;
- $promise->then(null, function ($e) use (&$exception) {
- $exception = $e;
- });
- $this->assertInstanceOf('RuntimeException', $exception);
- assert($exception instanceof \RuntimeException);
- $this->assertEquals('Connection to tcp://reactphp.org:80 failed: Last error for IPv4: Connection refused (ECONNREFUSED). Previous error for IPv6: DNS failed', $exception->getMessage());
- $this->assertEquals(defined('SOCKET_ECONNREFUSED') ? SOCKET_ECONNREFUSED : 111, $exception->getCode());
- $this->assertInstanceOf('RuntimeException', $exception->getPrevious());
- }
- public function testConnectWillRejectWhenAllConnectionsRejectAndCancelNextAttemptTimerImmediately()
- {
- $timer = $this->getMockBuilder('React\EventLoop\TimerInterface')->getMock();
- $loop = $this->getMockBuilder('React\EventLoop\LoopInterface')->getMock();
- $loop->expects($this->once())->method('addTimer')->with(0.1, $this->anything())->willReturn($timer);
- $loop->expects($this->once())->method('cancelTimer')->with($timer);
- $deferred = new Deferred();
- $connector = $this->getMockBuilder('React\Socket\ConnectorInterface')->getMock();
- $connector->expects($this->exactly(2))->method('connect')->willReturn($deferred->promise());
- $resolver = $this->getMockBuilder('React\Dns\Resolver\ResolverInterface')->getMock();
- $resolver->expects($this->exactly(2))->method('resolveAll')->withConsecutive(
- array('reactphp.org', Message::TYPE_AAAA),
- array('reactphp.org', Message::TYPE_A)
- )->willReturnOnConsecutiveCalls(
- \React\Promise\resolve(array('::1')),
- \React\Promise\resolve(array('127.0.0.1'))
- );
- $uri = 'tcp://reactphp.org:80';
- $host = 'reactphp.org';
- $parts = parse_url($uri);
- $builder = new HappyEyeBallsConnectionBuilder($loop, $connector, $resolver, $uri, $host, $parts);
- $promise = $builder->connect();
- $deferred->reject(new \RuntimeException(
- 'Connection refused (ECONNREFUSED)',
- defined('SOCKET_ECONNREFUSED') ? SOCKET_ECONNREFUSED : 111
- ));
- $exception = null;
- $promise->then(null, function ($e) use (&$exception) {
- $exception = $e;
- });
- $this->assertInstanceOf('RuntimeException', $exception);
- assert($exception instanceof \RuntimeException);
- $this->assertEquals('Connection to tcp://reactphp.org:80 failed: Connection refused (ECONNREFUSED)', $exception->getMessage());
- $this->assertEquals(defined('SOCKET_ECONNREFUSED') ? SOCKET_ECONNREFUSED : 111, $exception->getCode());
- $this->assertInstanceOf('RuntimeException', $exception->getPrevious());
- }
- public function testConnectWillRejectWithMessageWithoutHostnameWhenAllConnectionsRejectAndCancelNextAttemptTimerImmediately()
- {
- $timer = $this->getMockBuilder('React\EventLoop\TimerInterface')->getMock();
- $loop = $this->getMockBuilder('React\EventLoop\LoopInterface')->getMock();
- $loop->expects($this->once())->method('addTimer')->with(0.1, $this->anything())->willReturn($timer);
- $loop->expects($this->once())->method('cancelTimer')->with($timer);
- $deferred = new Deferred();
- $connector = $this->getMockBuilder('React\Socket\ConnectorInterface')->getMock();
- $connector->expects($this->exactly(2))->method('connect')->willReturnOnConsecutiveCalls(
- $deferred->promise(),
- \React\Promise\reject(new \RuntimeException(
- 'Connection to tcp://127.0.0.1:80?hostname=localhost failed: Connection refused (ECONNREFUSED)',
- defined('SOCKET_ECONNREFUSED') ? SOCKET_ECONNREFUSED : 111
- ))
- );
- $resolver = $this->getMockBuilder('React\Dns\Resolver\ResolverInterface')->getMock();
- $resolver->expects($this->exactly(2))->method('resolveAll')->withConsecutive(
- array('localhost', Message::TYPE_AAAA),
- array('localhost', Message::TYPE_A)
- )->willReturnOnConsecutiveCalls(
- \React\Promise\resolve(array('::1')),
- \React\Promise\resolve(array('127.0.0.1'))
- );
- $uri = 'tcp://localhost:80';
- $host = 'localhost';
- $parts = parse_url($uri);
- $builder = new HappyEyeBallsConnectionBuilder($loop, $connector, $resolver, $uri, $host, $parts);
- $promise = $builder->connect();
- $deferred->reject(new \RuntimeException(
- 'Connection to tcp://[::1]:80?hostname=localhost failed: Connection refused (ECONNREFUSED)',
- defined('SOCKET_ECONNREFUSED') ? SOCKET_ECONNREFUSED : 111
- ));
- $exception = null;
- $promise->then(null, function ($e) use (&$exception) {
- $exception = $e;
- });
- $this->assertInstanceOf('RuntimeException', $exception);
- assert($exception instanceof \RuntimeException);
- $this->assertEquals('Connection to tcp://localhost:80 failed: Last error for IPv4: Connection to tcp://127.0.0.1:80 failed: Connection refused (ECONNREFUSED). Previous error for IPv6: Connection to tcp://[::1]:80 failed: Connection refused (ECONNREFUSED)', $exception->getMessage());
- $this->assertEquals(defined('SOCKET_ECONNREFUSED') ? SOCKET_ECONNREFUSED : 111, $exception->getCode());
- $this->assertInstanceOf('RuntimeException', $exception->getPrevious());
- }
- public function testCancelConnectWillRejectPromiseAndCancelBothDnsLookups()
- {
- $loop = $this->getMockBuilder('React\EventLoop\LoopInterface')->getMock();
- $loop->expects($this->never())->method('addTimer');
- $connector = $this->getMockBuilder('React\Socket\ConnectorInterface')->getMock();
- $connector->expects($this->never())->method('connect');
- $cancelled = 0;
- $resolver = $this->getMockBuilder('React\Dns\Resolver\ResolverInterface')->getMock();
- $resolver->expects($this->exactly(2))->method('resolveAll')->withConsecutive(
- array('reactphp.org', Message::TYPE_AAAA),
- array('reactphp.org', Message::TYPE_A)
- )->willReturnOnConsecutiveCalls(
- new Promise(function () { }, function () use (&$cancelled) {
- ++$cancelled;
- throw new \RuntimeException();
- }),
- new Promise(function () { }, function () use (&$cancelled) {
- ++$cancelled;
- throw new \RuntimeException();
- })
- );
- $uri = 'tcp://reactphp.org:80';
- $host = 'reactphp.org';
- $parts = parse_url($uri);
- $builder = new HappyEyeBallsConnectionBuilder($loop, $connector, $resolver, $uri, $host, $parts);
- $promise = $builder->connect();
- $promise->cancel();
- $this->assertEquals(2, $cancelled);
- $exception = null;
- $promise->then(null, function ($e) use (&$exception) {
- $exception = $e;
- });
- $this->assertInstanceOf('RuntimeException', $exception);
- assert($exception instanceof \RuntimeException);
- $this->assertEquals('Connection to tcp://reactphp.org:80 cancelled during DNS lookup (ECONNABORTED)', $exception->getMessage());
- $this->assertEquals(defined('SOCKET_ECONNABORTED') ? SOCKET_ECONNABORTED : 103, $exception->getCode());
- }
- public function testCancelConnectWillRejectPromiseAndCancelPendingIpv6LookupAndCancelDelayTimer()
- {
- $timer = $this->getMockBuilder('React\EventLoop\TimerInterface')->getMock();
- $loop = $this->getMockBuilder('React\EventLoop\LoopInterface')->getMock();
- $loop->expects($this->once())->method('addTimer')->willReturn($timer);
- $loop->expects($this->once())->method('cancelTimer')->with($timer);
- $connector = $this->getMockBuilder('React\Socket\ConnectorInterface')->getMock();
- $connector->expects($this->never())->method('connect');
- $resolver = $this->getMockBuilder('React\Dns\Resolver\ResolverInterface')->getMock();
- $resolver->expects($this->exactly(2))->method('resolveAll')->withConsecutive(
- array('reactphp.org', Message::TYPE_AAAA),
- array('reactphp.org', Message::TYPE_A)
- )->willReturnOnConsecutiveCalls(
- new Promise(function () { }, function () {
- throw new \RuntimeException('DNS cancelled');
- }),
- \React\Promise\resolve(array('127.0.0.1'))
- );
- $uri = 'tcp://reactphp.org:80';
- $host = 'reactphp.org';
- $parts = parse_url($uri);
- $builder = new HappyEyeBallsConnectionBuilder($loop, $connector, $resolver, $uri, $host, $parts);
- $promise = $builder->connect();
- $promise->cancel();
- $exception = null;
- $promise->then(null, function ($e) use (&$exception) {
- $exception = $e;
- });
- $this->assertInstanceOf('RuntimeException', $exception);
- assert($exception instanceof \RuntimeException);
- $this->assertEquals('Connection to tcp://reactphp.org:80 cancelled during DNS lookup (ECONNABORTED)', $exception->getMessage());
- $this->assertEquals(defined('SOCKET_ECONNABORTED') ? SOCKET_ECONNABORTED : 103, $exception->getCode());
- }
- public function testCancelConnectWillRejectPromiseAndCancelPendingIpv6ConnectionAttemptAndPendingIpv4LookupAndCancelAttemptTimer()
- {
- $timer = $this->getMockBuilder('React\EventLoop\TimerInterface')->getMock();
- $loop = $this->getMockBuilder('React\EventLoop\LoopInterface')->getMock();
- $loop->expects($this->once())->method('addTimer')->with(0.1, $this->anything())->willReturn($timer);
- $loop->expects($this->once())->method('cancelTimer')->with($timer);
- $cancelled = 0;
- $connector = $this->getMockBuilder('React\Socket\ConnectorInterface')->getMock();
- $connector->expects($this->once())->method('connect')->with('tcp://[::1]:80?hostname=reactphp.org')->willReturn(new Promise(function () { }, function () use (&$cancelled) {
- ++$cancelled;
- throw new \RuntimeException('Ignored message');
- }));
- $resolver = $this->getMockBuilder('React\Dns\Resolver\ResolverInterface')->getMock();
- $resolver->expects($this->exactly(2))->method('resolveAll')->withConsecutive(
- array('reactphp.org', Message::TYPE_AAAA),
- array('reactphp.org', Message::TYPE_A)
- )->willReturnOnConsecutiveCalls(
- \React\Promise\resolve(array('::1')),
- new Promise(function () { }, $this->expectCallableOnce())
- );
- $uri = 'tcp://reactphp.org:80';
- $host = 'reactphp.org';
- $parts = parse_url($uri);
- $builder = new HappyEyeBallsConnectionBuilder($loop, $connector, $resolver, $uri, $host, $parts);
- $promise = $builder->connect();
- $promise->cancel();
- $this->assertEquals(1, $cancelled);
- $exception = null;
- $promise->then(null, function ($e) use (&$exception) {
- $exception = $e;
- });
- $this->assertInstanceOf('RuntimeException', $exception);
- assert($exception instanceof \RuntimeException);
- $this->assertEquals('Connection to tcp://reactphp.org:80 cancelled (ECONNABORTED)', $exception->getMessage());
- $this->assertEquals(defined('SOCKET_ECONNABORTED') ? SOCKET_ECONNABORTED : 103, $exception->getCode());
- }
- public function testResolveWillReturnResolvedPromiseWithEmptyListWhenDnsResolverFails()
- {
- $loop = $this->getMockBuilder('React\EventLoop\LoopInterface')->getMock();
- $connector = $this->getMockBuilder('React\Socket\ConnectorInterface')->getMock();
- $resolver = $this->getMockBuilder('React\Dns\Resolver\ResolverInterface')->getMock();
- $resolver->expects($this->once())->method('resolveAll')->with('reactphp.org', Message::TYPE_A)->willReturn(\React\Promise\reject(new \RuntimeException()));
- $uri = 'tcp://reactphp.org:80';
- $host = 'reactphp.org';
- $parts = parse_url($uri);
- $builder = new HappyEyeBallsConnectionBuilder($loop, $connector, $resolver, $uri, $host, $parts);
- $promise = $builder->resolve(Message::TYPE_A, $this->expectCallableNever());
- $this->assertInstanceof('React\Promise\PromiseInterface', $promise);
- $promise->then($this->expectCallableOnceWith(array()), $this->expectCallableNever());
- }
- public function testAttemptConnectionWillConnectViaConnectorToGivenIpWithPortAndHostnameFromUriParts()
- {
- $loop = $this->getMockBuilder('React\EventLoop\LoopInterface')->getMock();
- $connector = $this->getMockBuilder('React\Socket\ConnectorInterface')->getMock();
- $connector->expects($this->once())->method('connect')->with('tcp://10.1.1.1:80?hostname=reactphp.org')->willReturn(new Promise(function () { }));
- $resolver = $this->getMockBuilder('React\Dns\Resolver\ResolverInterface')->getMock();
- $resolver->expects($this->never())->method('resolveAll');
- $uri = 'tcp://reactphp.org:80';
- $host = 'reactphp.org';
- $parts = parse_url($uri);
- $builder = new HappyEyeBallsConnectionBuilder($loop, $connector, $resolver, $uri, $host, $parts);
- $builder->attemptConnection('10.1.1.1');
- }
- public function testAttemptConnectionWillConnectViaConnectorToGivenIpv6WithAllUriParts()
- {
- $loop = $this->getMockBuilder('React\EventLoop\LoopInterface')->getMock();
- $connector = $this->getMockBuilder('React\Socket\ConnectorInterface')->getMock();
- $connector->expects($this->once())->method('connect')->with('tcp://[::1]:80/path?test=yes&hostname=reactphp.org#start')->willReturn(new Promise(function () { }));
- $resolver = $this->getMockBuilder('React\Dns\Resolver\ResolverInterface')->getMock();
- $resolver->expects($this->never())->method('resolveAll');
- $uri = 'tcp://reactphp.org:80/path?test=yes#start';
- $host = 'reactphp.org';
- $parts = parse_url($uri);
- $builder = new HappyEyeBallsConnectionBuilder($loop, $connector, $resolver, $uri, $host, $parts);
- $builder->attemptConnection('::1');
- }
- public function testCheckCallsRejectFunctionImmediateWithoutLeavingDanglingPromiseWhenConnectorRejectsImmediately()
- {
- $loop = $this->getMockBuilder('React\EventLoop\LoopInterface')->getMock();
- $connector = $this->getMockBuilder('React\Socket\ConnectorInterface')->getMock();
- $connector->expects($this->once())->method('connect')->with('tcp://[::1]:80/path?test=yes&hostname=reactphp.org#start')->willReturn(\React\Promise\reject(new \RuntimeException()));
- $resolver = $this->getMockBuilder('React\Dns\Resolver\ResolverInterface')->getMock();
- $resolver->expects($this->never())->method('resolveAll');
- $uri = 'tcp://reactphp.org:80/path?test=yes#start';
- $host = 'reactphp.org';
- $parts = parse_url($uri);
- $builder = new HappyEyeBallsConnectionBuilder($loop, $connector, $resolver, $uri, $host, $parts);
- $ref = new \ReflectionProperty($builder, 'connectQueue');
- $ref->setAccessible(true);
- $ref->setValue($builder, array('::1'));
- $builder->check($this->expectCallableNever(), function () { });
- $ref = new \ReflectionProperty($builder, 'connectionPromises');
- $ref->setAccessible(true);
- $promises = $ref->getValue($builder);
- $this->assertEquals(array(), $promises);
- }
- public function testCleanUpCancelsAllPendingConnectionAttempts()
- {
- $loop = $this->getMockBuilder('React\EventLoop\LoopInterface')->getMock();
- $connector = $this->getMockBuilder('React\Socket\ConnectorInterface')->getMock();
- $connector->expects($this->exactly(2))->method('connect')->with('tcp://[::1]:80/path?test=yes&hostname=reactphp.org#start')->willReturnOnConsecutiveCalls(
- new Promise(function () { }, $this->expectCallableOnce()),
- new Promise(function () { }, $this->expectCallableOnce())
- );
- $resolver = $this->getMockBuilder('React\Dns\Resolver\ResolverInterface')->getMock();
- $resolver->expects($this->never())->method('resolveAll');
- $uri = 'tcp://reactphp.org:80/path?test=yes#start';
- $host = 'reactphp.org';
- $parts = parse_url($uri);
- $builder = new HappyEyeBallsConnectionBuilder($loop, $connector, $resolver, $uri, $host, $parts);
- $ref = new \ReflectionProperty($builder, 'connectQueue');
- $ref->setAccessible(true);
- $ref->setValue($builder, array('::1', '::1'));
- $builder->check($this->expectCallableNever(), function () { });
- $builder->check($this->expectCallableNever(), function () { });
- $builder->cleanUp();
- }
- public function testCleanUpCancelsAllPendingConnectionAttemptsWithoutStartingNewAttemptsDueToCancellationRejection()
- {
- $loop = $this->getMockBuilder('React\EventLoop\LoopInterface')->getMock();
- $connector = $this->getMockBuilder('React\Socket\ConnectorInterface')->getMock();
- $connector->expects($this->once())->method('connect')->with('tcp://[::1]:80/path?test=yes&hostname=reactphp.org#start')->willReturn(new Promise(function () { }, function () {
- throw new \RuntimeException();
- }));
- $resolver = $this->getMockBuilder('React\Dns\Resolver\ResolverInterface')->getMock();
- $resolver->expects($this->never())->method('resolveAll');
- $uri = 'tcp://reactphp.org:80/path?test=yes#start';
- $host = 'reactphp.org';
- $parts = parse_url($uri);
- $builder = new HappyEyeBallsConnectionBuilder($loop, $connector, $resolver, $uri, $host, $parts);
- $ref = new \ReflectionProperty($builder, 'connectQueue');
- $ref->setAccessible(true);
- $ref->setValue($builder, array('::1', '::1'));
- $builder->check($this->expectCallableNever(), function () { });
- $builder->cleanUp();
- }
- public function testMixIpsIntoConnectQueueSometimesAssignsInOriginalOrder()
- {
- $loop = $this->getMockBuilder('React\EventLoop\LoopInterface')->getMock();
- $connector = $this->getMockBuilder('React\Socket\ConnectorInterface')->getMock();
- $resolver = $this->getMockBuilder('React\Dns\Resolver\ResolverInterface')->getMock();
- $uri = 'tcp://reactphp.org:80/path?test=yes#start';
- $host = 'reactphp.org';
- $parts = parse_url($uri);
- for ($i = 0; $i < 100; ++$i) {
- $builder = new HappyEyeBallsConnectionBuilder($loop, $connector, $resolver, $uri, $host, $parts);
- $builder->mixIpsIntoConnectQueue(array('::1', '::2'));
- $ref = new \ReflectionProperty($builder, 'connectQueue');
- $ref->setAccessible(true);
- $value = $ref->getValue($builder);
- if ($value === array('::1', '::2')) {
- break;
- }
- }
- $this->assertEquals(array('::1', '::2'), $value);
- }
- public function testMixIpsIntoConnectQueueSometimesAssignsInReverseOrder()
- {
- $loop = $this->getMockBuilder('React\EventLoop\LoopInterface')->getMock();
- $connector = $this->getMockBuilder('React\Socket\ConnectorInterface')->getMock();
- $resolver = $this->getMockBuilder('React\Dns\Resolver\ResolverInterface')->getMock();
- $uri = 'tcp://reactphp.org:80/path?test=yes#start';
- $host = 'reactphp.org';
- $parts = parse_url($uri);
- for ($i = 0; $i < 100; ++$i) {
- $builder = new HappyEyeBallsConnectionBuilder($loop, $connector, $resolver, $uri, $host, $parts);
- $builder->mixIpsIntoConnectQueue(array('::1', '::2'));
- $ref = new \ReflectionProperty($builder, 'connectQueue');
- $ref->setAccessible(true);
- $value = $ref->getValue($builder);
- if ($value === array('::2', '::1')) {
- break;
- }
- }
- $this->assertEquals(array('::2', '::1'), $value);
- }
- }
|