14
14
*/
15
15
final class HappyEyeBallsConnectionBuilder
16
16
{
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 ;
19
32
20
33
public $ loop ;
21
34
public $ connector ;
@@ -29,7 +42,7 @@ final class HappyEyeBallsConnectionBuilder
29
42
public $ resolverPromises = array ();
30
43
public $ connectionPromises = array ();
31
44
public $ connectQueue = array ();
32
- public $ timer ;
45
+ public $ nextAttemptTimer ;
33
46
public $ parts ;
34
47
public $ ipsCount = 0 ;
35
48
public $ failureCount = 0 ;
@@ -58,40 +71,28 @@ public function connect()
58
71
59
72
$ that ->mixIpsIntoConnectQueue ($ ips );
60
73
61
- if ($ that ->timer instanceof TimerInterface) {
74
+ if ($ that ->nextAttemptTimer instanceof TimerInterface) {
62
75
return ;
63
76
}
64
77
65
78
$ that ->check ($ resolve , $ reject );
66
79
};
67
80
};
68
81
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
76
85
if ($ that ->resolved [Message::TYPE_AAAA ] === true ) {
77
- return Promise \resolve ( $ ips) ;
86
+ return $ ips ;
78
87
}
79
88
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
87
90
$ 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 ) {
91
92
$ deferred ->resolve ($ ips );
92
93
});
93
94
94
- $ ipv4Deferred -> promise () ->then (function () use ($ that , & $ timer , $ deferred , $ ips ) {
95
+ $ that -> resolverPromises [Message:: TYPE_AAAA ] ->then (function () use ($ that , $ timer , $ deferred , $ ips ) {
95
96
$ that ->loop ->cancelTimer ($ timer );
96
97
$ deferred ->resolve ($ ips );
97
98
});
@@ -124,7 +125,6 @@ public function resolve($type, $reject)
124
125
}
125
126
126
127
if ($ that ->ipsCount === 0 ) {
127
- $ that ->resolved = null ;
128
128
$ that ->resolverPromises = null ;
129
129
$ reject (new \RuntimeException ('Connection to ' . $ that ->uri . ' failed during DNS lookup: DNS error ' ));
130
130
}
@@ -136,9 +136,9 @@ public function resolve($type, $reject)
136
136
*/
137
137
public function check ($ resolve , $ reject )
138
138
{
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 ;
142
142
}
143
143
144
144
if (\count ($ this ->connectQueue ) === 0 ) {
@@ -154,7 +154,7 @@ public function check($resolve, $reject)
154
154
$ that ->cleanUp ();
155
155
156
156
$ resolve ($ connection );
157
- }, function () use ($ that , $ ip , $ resolve , $ reject ) {
157
+ }, function () use ($ that , $ ip , $ reject ) {
158
158
unset($ that ->connectionPromises [$ ip ]);
159
159
160
160
$ that ->failureCount ++;
@@ -176,8 +176,8 @@ public function check($resolve, $reject)
176
176
*
177
177
* @link https://tools.ietf.org/html/rfc8305#section-5
178
178
*/
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 ) {
181
181
$ that ->check ($ resolve , $ reject );
182
182
});
183
183
}
@@ -238,23 +238,21 @@ public function attemptConnection($ip)
238
238
*/
239
239
public function cleanUp ()
240
240
{
241
- /** @var CancellablePromiseInterface $promise */
242
- foreach ($ this ->connectionPromises as $ index => $ connectionPromise ) {
241
+ foreach ($ this ->connectionPromises as $ connectionPromise ) {
243
242
if ($ connectionPromise instanceof CancellablePromiseInterface) {
244
243
$ connectionPromise ->cancel ();
245
244
}
246
245
}
247
246
248
- /** @var CancellablePromiseInterface $promise */
249
- foreach ($ this ->resolverPromises as $ index => $ resolverPromise ) {
247
+ foreach ($ this ->resolverPromises as $ resolverPromise ) {
250
248
if ($ resolverPromise instanceof CancellablePromiseInterface) {
251
249
$ resolverPromise ->cancel ();
252
250
}
253
251
}
254
252
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 ;
258
256
}
259
257
}
260
258
0 commit comments