44
55use Clue \React \Block ;
66use React \EventLoop \Factory ;
7+ use React \Socket \ConnectionInterface ;
78use React \Socket \Connector ;
9+ use React \Socket \ConnectorInterface ;
810use React \Socket \TcpServer ;
911
1012class FunctionalConnectorTest extends TestCase
1113{
12- const TIMEOUT = 1.0 ;
14+ const TIMEOUT = 30.0 ;
15+
16+ private $ ipv4 ;
17+ private $ ipv6 ;
1318
1419 /** @test */
1520 public function connectionToTcpServerShouldSucceedWithLocalhost ()
@@ -29,4 +34,174 @@ public function connectionToTcpServerShouldSucceedWithLocalhost()
2934 $ connection ->close ();
3035 $ server ->close ();
3136 }
37+
38+ /**
39+ * @test
40+ * @group internet
41+ */
42+ public function connectionToRemoteTCP4n6ServerShouldResultInOurIP ()
43+ {
44+ $ loop = Factory::create ();
45+
46+ $ connector = new Connector ($ loop , array ('happy_eyeballs ' => true ));
47+
48+ $ ip = Block \await ($ this ->request ('dual.tlund.se ' , $ connector ), $ loop , self ::TIMEOUT );
49+
50+ $ this ->assertSame ($ ip , filter_var ($ ip , FILTER_VALIDATE_IP , FILTER_FLAG_IPV4 | FILTER_FLAG_IPV6 ), $ ip );
51+ }
52+
53+ /**
54+ * @test
55+ * @group internet
56+ */
57+ public function connectionToRemoteTCP4ServerShouldResultInOurIP ()
58+ {
59+ if ($ this ->ipv4 () === false ) {
60+ // IPv4 not supported on this system
61+ $ this ->assertFalse ($ this ->ipv4 ());
62+ return ;
63+ }
64+
65+ $ loop = Factory::create ();
66+
67+ $ connector = new Connector ($ loop , array ('happy_eyeballs ' => true ));
68+
69+ $ ip = Block \await ($ this ->request ('ipv4.tlund.se ' , $ connector ), $ loop , self ::TIMEOUT );
70+
71+ $ this ->assertSame ($ ip , filter_var ($ ip , FILTER_VALIDATE_IP , FILTER_FLAG_IPV4 ), $ ip );
72+ $ this ->assertFalse (filter_var ($ ip , FILTER_VALIDATE_IP , FILTER_FLAG_IPV6 ), $ ip );
73+ }
74+
75+ /**
76+ * @test
77+ * @group internet
78+ */
79+ public function connectionToRemoteTCP6ServerShouldResultInOurIP ()
80+ {
81+ if ($ this ->ipv6 () === false ) {
82+ // IPv6 not supported on this system
83+ $ this ->assertFalse ($ this ->ipv6 ());
84+ return ;
85+ }
86+
87+ $ loop = Factory::create ();
88+
89+ $ connector = new Connector ($ loop , array ('happy_eyeballs ' => true ));
90+
91+ $ ip = Block \await ($ this ->request ('ipv6.tlund.se ' , $ connector ), $ loop , self ::TIMEOUT );
92+
93+ $ this ->assertFalse (filter_var ($ ip , FILTER_VALIDATE_IP , FILTER_FLAG_IPV4 ), $ ip );
94+ $ this ->assertSame ($ ip , filter_var ($ ip , FILTER_VALIDATE_IP , FILTER_FLAG_IPV6 ), $ ip );
95+ }
96+
97+ /**
98+ * @test
99+ * @group internet
100+ *
101+ * @expectedException \RuntimeException
102+ * @expectedExceptionMessageRegExp /Connection to ipv6.tlund.se:80 failed/
103+ */
104+ public function tryingToConnectToAnIPv6OnlyHostWithOutHappyEyeBallsShouldResultInFailure ()
105+ {
106+ $ loop = Factory::create ();
107+
108+ $ connector = new Connector ($ loop , array ('happy_eyeballs ' => false ));
109+
110+ Block \await ($ this ->request ('ipv6.tlund.se ' , $ connector ), $ loop , self ::TIMEOUT );
111+ }
112+
113+ /**
114+ * @test
115+ * @group internet
116+ *
117+ * @expectedException \RuntimeException
118+ * @expectedExceptionMessageRegExp /Connection to tcp:\/\/193.15.228.195:80 failed:/
119+ */
120+ public function connectingDirectlyToAnIPv4AddressShouldFailWhenIPv4IsntAvailable ()
121+ {
122+ if ($ this ->ipv4 () === true ) {
123+ // IPv4 supported on this system
124+ throw new \RuntimeException ('Connection to tcp://193.15.228.195:80 failed: ' );
125+ }
126+
127+ $ loop = Factory::create ();
128+
129+ $ connector = new Connector ($ loop );
130+
131+ $ host = current (dns_get_record ('ipv4.tlund.se ' , DNS_A ));
132+ $ host = $ host ['ip ' ];
133+ Block \await ($ this ->request ($ host , $ connector ), $ loop , self ::TIMEOUT );
134+ }
135+
136+ /**
137+ * @test
138+ * @group internet
139+ *
140+ * @expectedException \RuntimeException
141+ * @expectedExceptionMessageRegExp /Connection to tcp:\/\/\[2a00:801:f::195\]:80 failed:/
142+ */
143+ public function connectingDirectlyToAnIPv6AddressShouldFailWhenIPv6IsntAvailable ()
144+ {
145+ if ($ this ->ipv6 () === true ) {
146+ // IPv6 supported on this system
147+ throw new \RuntimeException ('Connection to tcp://[2a00:801:f::195]:80 failed: ' );
148+ }
149+
150+ $ loop = Factory::create ();
151+
152+ $ connector = new Connector ($ loop );
153+
154+ $ host = current (dns_get_record ('ipv6.tlund.se ' , DNS_AAAA ));
155+ $ host = $ host ['ipv6 ' ];
156+ $ host = '[ ' . $ host . '] ' ;
157+ $ ip = Block \await ($ this ->request ($ host , $ connector ), $ loop , self ::TIMEOUT );
158+
159+ $ this ->assertFalse (filter_var ($ ip , FILTER_VALIDATE_IP , FILTER_FLAG_IPV4 ), $ ip );
160+ $ this ->assertSame ($ ip , filter_var ($ ip , FILTER_VALIDATE_IP , FILTER_FLAG_IPV6 ), $ ip );
161+ }
162+
163+ /**
164+ * @internal
165+ */
166+ public function parseIpFromPage ($ body )
167+ {
168+ $ ex = explode ('title="Look up on bgp.he.net"> ' , $ body );
169+ $ ex = explode ('< ' , $ ex [1 ]);
170+
171+ return $ ex [0 ];
172+ }
173+
174+ private function request ($ host , ConnectorInterface $ connector )
175+ {
176+ $ that = $ this ;
177+ return $ connector ->connect ($ host . ':80 ' )->then (function (ConnectionInterface $ connection ) use ($ host ) {
178+ $ connection ->write ("GET / HTTP/1.1 \r\nHost: " . $ host . "\r\n\r\n" );
179+
180+ return \React \Promise \Stream \buffer ($ connection );
181+ })->then (function ($ response ) use ($ that ) {
182+ return $ that ->parseIpFromPage ($ response );
183+ });
184+ }
185+
186+ private function ipv4 ()
187+ {
188+ if ($ this ->ipv4 !== null ) {
189+ return $ this ->ipv4 ;
190+ }
191+
192+ $ this ->ipv4 = !!@file_get_contents ('http://ipv4.tlund.se/ ' );
193+
194+ return $ this ->ipv4 ;
195+ }
196+
197+ private function ipv6 ()
198+ {
199+ if ($ this ->ipv6 !== null ) {
200+ return $ this ->ipv6 ;
201+ }
202+
203+ $ this ->ipv6 = !!@file_get_contents ('http://ipv6.tlund.se/ ' );
204+
205+ return $ this ->ipv6 ;
206+ }
32207}
0 commit comments