1414 */
1515final class HappyEyeBallsConnectionBuilder
1616{
17- const CONNECT_INTERVAL = 0.1 ;
18- const RESOLVE_WAIT = 0.5 ;
17+ /**
18+ * As long as we haven't connected yet keep popping an IP address of the connect queue until one of them
19+ * succeeds or they all fail. We will wait 100ms between connection attempts as per RFC.
20+ *
21+ * @link https://tools.ietf.org/html/rfc8305#section-5
22+ */
23+ const CONNECTION_ATTEMPT_DELAY = 0.1 ;
24+
25+ /**
26+ * Delay `A` lookup by 50ms sending out connection to IPv4 addresses when IPv6 records haven't
27+ * resolved yet as per RFC.
28+ *
29+ * @link https://tools.ietf.org/html/rfc8305#section-3
30+ */
31+ const RESOLUTION_DELAY = 0.05 ;
1932
2033 public $ loop ;
2134 public $ connector ;
@@ -29,7 +42,7 @@ final class HappyEyeBallsConnectionBuilder
2942 public $ resolverPromises = array ();
3043 public $ connectionPromises = array ();
3144 public $ connectQueue = array ();
32- public $ timer ;
45+ public $ nextAttemptTimer ;
3346 public $ parts ;
3447 public $ ipsCount = 0 ;
3548 public $ failureCount = 0 ;
@@ -58,40 +71,28 @@ public function connect()
5871
5972 $ that ->mixIpsIntoConnectQueue ($ ips );
6073
61- if ($ that ->timer instanceof TimerInterface) {
74+ if ($ that ->nextAttemptTimer instanceof TimerInterface) {
6275 return ;
6376 }
6477
6578 $ that ->check ($ resolve , $ reject );
6679 };
6780 };
6881
69- $ ipv4Deferred = null ;
70- $ that ->resolverPromises [Message::TYPE_AAAA ] = $ that ->resolve (Message::TYPE_AAAA , $ reject )->then ($ lookupResolve (Message::TYPE_AAAA ))->then (function () use (&$ ipv4Deferred ) {
71- if ($ ipv4Deferred instanceof Promise \Deferred) {
72- $ ipv4Deferred ->resolve ();
73- }
74- });
75- $ that ->resolverPromises [Message::TYPE_A ] = $ that ->resolve (Message::TYPE_A , $ reject )->then (function ($ ips ) use ($ that , &$ ipv4Deferred , &$ timer ) {
82+ $ that ->resolverPromises [Message::TYPE_AAAA ] = $ that ->resolve (Message::TYPE_AAAA , $ reject )->then ($ lookupResolve (Message::TYPE_AAAA ));
83+ $ that ->resolverPromises [Message::TYPE_A ] = $ that ->resolve (Message::TYPE_A , $ reject )->then (function ($ ips ) use ($ that , &$ timer ) {
84+ // happy path: IPv6 has resolved already, continue with IPv4 addresses
7685 if ($ that ->resolved [Message::TYPE_AAAA ] === true ) {
77- return Promise \resolve ( $ ips) ;
86+ return $ ips ;
7887 }
7988
80- /**
81- * Delay A lookup by 50ms sending out connection to IPv4 addresses when IPv6 records haven't
82- * resolved yet as per RFC.
83- *
84- * @link https://tools.ietf.org/html/rfc8305#section-3
85- */
86- $ ipv4Deferred = new Promise \Deferred ();
89+ // Otherwise delay processing IPv4 lookup until short timer passes or IPv6 resolves in the meantime
8790 $ deferred = new Promise \Deferred ();
88-
89- $ timer = $ that ->loop ->addTimer ($ that ::RESOLVE_WAIT , function () use ($ deferred , $ ips ) {
90- $ ipv4Deferred = null ;
91+ $ timer = $ that ->loop ->addTimer ($ that ::RESOLUTION_DELAY , function () use ($ deferred , $ ips ) {
9192 $ deferred ->resolve ($ ips );
9293 });
9394
94- $ ipv4Deferred -> promise () ->then (function () use ($ that , & $ timer , $ deferred , $ ips ) {
95+ $ that -> resolverPromises [Message:: TYPE_AAAA ] ->then (function () use ($ that , $ timer , $ deferred , $ ips ) {
9596 $ that ->loop ->cancelTimer ($ timer );
9697 $ deferred ->resolve ($ ips );
9798 });
@@ -124,7 +125,6 @@ public function resolve($type, $reject)
124125 }
125126
126127 if ($ that ->ipsCount === 0 ) {
127- $ that ->resolved = null ;
128128 $ that ->resolverPromises = null ;
129129 $ reject (new \RuntimeException ('Connection to ' . $ that ->uri . ' failed during DNS lookup: DNS error ' ));
130130 }
@@ -136,9 +136,9 @@ public function resolve($type, $reject)
136136 */
137137 public function check ($ resolve , $ reject )
138138 {
139- if (\count ($ this ->connectQueue ) === 0 && $ this ->resolved [Message::TYPE_A ] === true && $ this ->resolved [Message::TYPE_AAAA ] === true && $ this ->timer instanceof TimerInterface) {
140- $ this ->loop ->cancelTimer ($ this ->timer );
141- $ this ->timer = null ;
139+ if (\count ($ this ->connectQueue ) === 0 && $ this ->resolved [Message::TYPE_A ] === true && $ this ->resolved [Message::TYPE_AAAA ] === true && $ this ->nextAttemptTimer instanceof TimerInterface) {
140+ $ this ->loop ->cancelTimer ($ this ->nextAttemptTimer );
141+ $ this ->nextAttemptTimer = null ;
142142 }
143143
144144 if (\count ($ this ->connectQueue ) === 0 ) {
@@ -154,7 +154,7 @@ public function check($resolve, $reject)
154154 $ that ->cleanUp ();
155155
156156 $ resolve ($ connection );
157- }, function () use ($ that , $ ip , $ resolve , $ reject ) {
157+ }, function () use ($ that , $ ip , $ reject ) {
158158 unset($ that ->connectionPromises [$ ip ]);
159159
160160 $ that ->failureCount ++;
@@ -176,8 +176,8 @@ public function check($resolve, $reject)
176176 *
177177 * @link https://tools.ietf.org/html/rfc8305#section-5
178178 */
179- if ((\count ($ this ->connectQueue ) > 0 || ($ this ->resolved [Message::TYPE_A ] === false || $ this ->resolved [Message::TYPE_AAAA ] === false )) && $ this ->timer === null ) {
180- $ this ->timer = $ this ->loop ->addPeriodicTimer (self ::CONNECT_INTERVAL , function () use ($ that , $ resolve , $ reject ) {
179+ if ((\count ($ this ->connectQueue ) > 0 || ($ this ->resolved [Message::TYPE_A ] === false || $ this ->resolved [Message::TYPE_AAAA ] === false )) && $ this ->nextAttemptTimer === null ) {
180+ $ this ->nextAttemptTimer = $ this ->loop ->addPeriodicTimer (self ::CONNECTION_ATTEMPT_DELAY , function () use ($ that , $ resolve , $ reject ) {
181181 $ that ->check ($ resolve , $ reject );
182182 });
183183 }
@@ -238,23 +238,21 @@ public function attemptConnection($ip)
238238 */
239239 public function cleanUp ()
240240 {
241- /** @var CancellablePromiseInterface $promise */
242- foreach ($ this ->connectionPromises as $ index => $ connectionPromise ) {
241+ foreach ($ this ->connectionPromises as $ connectionPromise ) {
243242 if ($ connectionPromise instanceof CancellablePromiseInterface) {
244243 $ connectionPromise ->cancel ();
245244 }
246245 }
247246
248- /** @var CancellablePromiseInterface $promise */
249- foreach ($ this ->resolverPromises as $ index => $ resolverPromise ) {
247+ foreach ($ this ->resolverPromises as $ resolverPromise ) {
250248 if ($ resolverPromise instanceof CancellablePromiseInterface) {
251249 $ resolverPromise ->cancel ();
252250 }
253251 }
254252
255- if ($ this ->timer instanceof TimerInterface) {
256- $ this ->loop ->cancelTimer ($ this ->timer );
257- $ this ->timer = null ;
253+ if ($ this ->nextAttemptTimer instanceof TimerInterface) {
254+ $ this ->loop ->cancelTimer ($ this ->nextAttemptTimer );
255+ $ this ->nextAttemptTimer = null ;
258256 }
259257 }
260258
0 commit comments