33namespace React \Tests \Socket ;
44
55use React \Promise ;
6+ use React \Promise \Deferred ;
67use React \Socket \SecureConnector ;
78
89class SecureConnectorTest extends TestCase
@@ -51,16 +52,33 @@ public function testConnectionToInvalidSchemeWillReject()
5152
5253 public function testCancelDuringTcpConnectionCancelsTcpConnection ()
5354 {
54- $ pending = new Promise \Promise (function () { }, function () { throw new \Exception (); });
55+ $ pending = new Promise \Promise (function () { }, $ this ->expectCallableOnce ());
56+ $ this ->tcp ->expects ($ this ->once ())->method ('connect ' )->with ('example.com:80 ' )->willReturn ($ pending );
57+
58+ $ promise = $ this ->connector ->connect ('example.com:80 ' );
59+ $ promise ->cancel ();
60+ }
61+
62+ /**
63+ * @expectedException RuntimeException
64+ * @expectedExceptionMessage Connection cancelled
65+ */
66+ public function testCancelDuringTcpConnectionCancelsTcpConnectionAndRejectsWithTcpRejection ()
67+ {
68+ $ pending = new Promise \Promise (function () { }, function () { throw new \RuntimeException ('Connection cancelled ' ); });
5569 $ this ->tcp ->expects ($ this ->once ())->method ('connect ' )->with ($ this ->equalTo ('example.com:80 ' ))->will ($ this ->returnValue ($ pending ));
5670
5771 $ promise = $ this ->connector ->connect ('example.com:80 ' );
5872 $ promise ->cancel ();
5973
60- $ promise -> then ( $ this ->expectCallableNever (), $ this -> expectCallableOnce () );
74+ $ this ->throwRejection ( $ promise );
6175 }
6276
63- public function testConnectionWillBeClosedAndRejectedIfConnectioIsNoStream ()
77+ /**
78+ * @expectedException UnexpectedValueException
79+ * @expectedExceptionMessage Base connector does not use internal Connection class exposing stream resource
80+ */
81+ public function testConnectionWillBeClosedAndRejectedIfConnectionIsNoStream ()
6482 {
6583 $ connection = $ this ->getMockBuilder ('React\Socket\ConnectionInterface ' )->getMock ();
6684 $ connection ->expects ($ this ->once ())->method ('close ' );
@@ -69,6 +87,133 @@ public function testConnectionWillBeClosedAndRejectedIfConnectioIsNoStream()
6987
7088 $ promise = $ this ->connector ->connect ('example.com:80 ' );
7189
72- $ promise ->then ($ this ->expectCallableNever (), $ this ->expectCallableOnce ());
90+ $ this ->throwRejection ($ promise );
91+ }
92+
93+ public function testStreamEncryptionWillBeEnabledAfterConnecting ()
94+ {
95+ $ connection = $ this ->getMockBuilder ('React\Socket\Connection ' )->disableOriginalConstructor ()->getMock ();
96+
97+ $ encryption = $ this ->getMockBuilder ('React\Socket\StreamEncryption ' )->disableOriginalConstructor ()->getMock ();
98+ $ encryption ->expects ($ this ->once ())->method ('enable ' )->with ($ connection )->willReturn (new \React \Promise \Promise (function () { }));
99+
100+ $ ref = new \ReflectionProperty ($ this ->connector , 'streamEncryption ' );
101+ $ ref ->setAccessible (true );
102+ $ ref ->setValue ($ this ->connector , $ encryption );
103+
104+ $ pending = new Promise \Promise (function () { }, function () { throw new \RuntimeException ('Connection cancelled ' ); });
105+ $ this ->tcp ->expects ($ this ->once ())->method ('connect ' )->with ($ this ->equalTo ('example.com:80 ' ))->willReturn (Promise \resolve ($ connection ));
106+
107+ $ promise = $ this ->connector ->connect ('example.com:80 ' );
108+ }
109+
110+ public function testConnectionWillBeRejectedIfStreamEncryptionFailsAndClosesConnection ()
111+ {
112+ $ connection = $ this ->getMockBuilder ('React\Socket\Connection ' )->disableOriginalConstructor ()->getMock ();
113+ $ connection ->expects ($ this ->once ())->method ('close ' );
114+
115+ $ encryption = $ this ->getMockBuilder ('React\Socket\StreamEncryption ' )->disableOriginalConstructor ()->getMock ();
116+ $ encryption ->expects ($ this ->once ())->method ('enable ' )->willReturn (Promise \reject (new \RuntimeException ('TLS error ' , 123 )));
117+
118+ $ ref = new \ReflectionProperty ($ this ->connector , 'streamEncryption ' );
119+ $ ref ->setAccessible (true );
120+ $ ref ->setValue ($ this ->connector , $ encryption );
121+
122+ $ pending = new Promise \Promise (function () { }, function () { throw new \RuntimeException ('Connection cancelled ' ); });
123+ $ this ->tcp ->expects ($ this ->once ())->method ('connect ' )->with ($ this ->equalTo ('example.com:80 ' ))->willReturn (Promise \resolve ($ connection ));
124+
125+ $ promise = $ this ->connector ->connect ('example.com:80 ' );
126+
127+ try {
128+ $ this ->throwRejection ($ promise );
129+ } catch (\RuntimeException $ e ) {
130+ $ this ->assertEquals ('Connection to example.com:80 failed during TLS handshake: TLS error ' , $ e ->getMessage ());
131+ $ this ->assertEquals (123 , $ e ->getCode ());
132+ $ this ->assertNull ($ e ->getPrevious ());
133+ }
134+ }
135+
136+ /**
137+ * @expectedException RuntimeException
138+ * @expectedExceptionMessage Connection to example.com:80 cancelled during TLS handshake
139+ */
140+ public function testCancelDuringStreamEncryptionCancelsEncryptionAndClosesConnection ()
141+ {
142+ $ connection = $ this ->getMockBuilder ('React\Socket\Connection ' )->disableOriginalConstructor ()->getMock ();
143+ $ connection ->expects ($ this ->once ())->method ('close ' );
144+
145+ $ pending = new Promise \Promise (function () { }, function () {
146+ throw new \Exception ('Ignored ' );
147+ });
148+ $ encryption = $ this ->getMockBuilder ('React\Socket\StreamEncryption ' )->disableOriginalConstructor ()->getMock ();
149+ $ encryption ->expects ($ this ->once ())->method ('enable ' )->willReturn ($ pending );
150+
151+ $ ref = new \ReflectionProperty ($ this ->connector , 'streamEncryption ' );
152+ $ ref ->setAccessible (true );
153+ $ ref ->setValue ($ this ->connector , $ encryption );
154+
155+ $ this ->tcp ->expects ($ this ->once ())->method ('connect ' )->with ($ this ->equalTo ('example.com:80 ' ))->willReturn (Promise \resolve ($ connection ));
156+
157+ $ promise = $ this ->connector ->connect ('example.com:80 ' );
158+ $ promise ->cancel ();
159+
160+ $ this ->throwRejection ($ promise );
161+ }
162+
163+ public function testRejectionDuringConnectionShouldNotCreateAnyGarbageReferences ()
164+ {
165+ if (class_exists ('React\Promise\When ' )) {
166+ $ this ->markTestSkipped ('Not supported on legacy Promise v1 API ' );
167+ }
168+
169+ gc_collect_cycles ();
170+
171+ $ tcp = new Deferred ();
172+ $ this ->tcp ->expects ($ this ->once ())->method ('connect ' )->willReturn ($ tcp ->promise ());
173+
174+ $ promise = $ this ->connector ->connect ('example.com:80 ' );
175+ $ tcp ->reject (new \RuntimeException ());
176+ unset($ promise , $ tcp );
177+
178+ $ this ->assertEquals (0 , gc_collect_cycles ());
179+ }
180+
181+ public function testRejectionDuringTlsHandshakeShouldNotCreateAnyGarbageReferences ()
182+ {
183+ if (class_exists ('React\Promise\When ' )) {
184+ $ this ->markTestSkipped ('Not supported on legacy Promise v1 API ' );
185+ }
186+
187+ gc_collect_cycles ();
188+
189+ $ connection = $ this ->getMockBuilder ('React\Socket\Connection ' )->disableOriginalConstructor ()->getMock ();
190+
191+ $ tcp = new Deferred ();
192+ $ this ->tcp ->expects ($ this ->once ())->method ('connect ' )->willReturn ($ tcp ->promise ());
193+
194+ $ tls = new Deferred ();
195+ $ encryption = $ this ->getMockBuilder ('React\Socket\StreamEncryption ' )->disableOriginalConstructor ()->getMock ();
196+ $ encryption ->expects ($ this ->once ())->method ('enable ' )->willReturn ($ tls ->promise ());
197+
198+ $ ref = new \ReflectionProperty ($ this ->connector , 'streamEncryption ' );
199+ $ ref ->setAccessible (true );
200+ $ ref ->setValue ($ this ->connector , $ encryption );
201+
202+ $ promise = $ this ->connector ->connect ('example.com:80 ' );
203+ $ tcp ->resolve ($ connection );
204+ $ tls ->reject (new \RuntimeException ());
205+ unset($ promise , $ tcp , $ tls );
206+
207+ $ this ->assertEquals (0 , gc_collect_cycles ());
208+ }
209+
210+ private function throwRejection ($ promise )
211+ {
212+ $ ex = null ;
213+ $ promise ->then (null , function ($ e ) use (&$ ex ) {
214+ $ ex = $ e ;
215+ });
216+
217+ throw $ ex ;
73218 }
74219}
0 commit comments