21
21
import io .netty .bootstrap .Bootstrap ;
22
22
import io .netty .channel .Channel ;
23
23
import io .netty .channel .ChannelFuture ;
24
+ import io .netty .channel .ChannelPipeline ;
25
+ import io .netty .handler .ssl .SslHandler ;
24
26
import org .junit .After ;
25
27
import org .junit .Before ;
26
28
import org .junit .Rule ;
27
29
import org .junit .Test ;
30
+ import org .junit .rules .RuleChain ;
31
+ import org .junit .rules .Timeout ;
28
32
33
+ import java .io .IOException ;
29
34
import java .net .ConnectException ;
35
+ import java .net .ServerSocket ;
30
36
import java .util .concurrent .ExecutionException ;
31
37
import java .util .concurrent .TimeUnit ;
32
38
33
39
import org .neo4j .driver .internal .BoltServerAddress ;
34
40
import org .neo4j .driver .internal .ConnectionSettings ;
41
+ import org .neo4j .driver .internal .async .inbound .ConnectTimeoutHandler ;
35
42
import org .neo4j .driver .internal .security .SecurityPlan ;
36
43
import org .neo4j .driver .internal .util .FakeClock ;
37
44
import org .neo4j .driver .v1 .AuthToken ;
42
49
43
50
import static org .hamcrest .Matchers .instanceOf ;
44
51
import static org .hamcrest .Matchers .startsWith ;
52
+ import static org .junit .Assert .assertEquals ;
45
53
import static org .junit .Assert .assertFalse ;
54
+ import static org .junit .Assert .assertNotNull ;
46
55
import static org .junit .Assert .assertNull ;
47
56
import static org .junit .Assert .assertThat ;
48
57
import static org .junit .Assert .assertTrue ;
52
61
53
62
public class ChannelConnectorImplTest
54
63
{
64
+ private final TestNeo4j neo4j = new TestNeo4j ();
55
65
@ Rule
56
- public final TestNeo4j neo4j = new TestNeo4j ( );
66
+ public final RuleChain ruleChain = RuleChain . outerRule ( Timeout . seconds ( 20 ) ). around ( neo4j );
57
67
58
68
private Bootstrap bootstrap ;
59
69
60
70
@ Before
61
- public void setUp () throws Exception
71
+ public void setUp ()
62
72
{
63
73
bootstrap = BootstrapFactory .newBootstrap ( 1 );
64
74
}
65
75
66
76
@ After
67
- public void tearDown () throws Exception
77
+ public void tearDown ()
68
78
{
69
79
if ( bootstrap != null )
70
80
{
@@ -75,7 +85,7 @@ public void tearDown() throws Exception
75
85
@ Test
76
86
public void shouldConnect () throws Exception
77
87
{
78
- ChannelConnectorImpl connector = newConnector ( neo4j .authToken () );
88
+ ChannelConnector connector = newConnector ( neo4j .authToken () );
79
89
80
90
ChannelFuture channelFuture = connector .connect ( neo4j .address (), bootstrap );
81
91
assertTrue ( channelFuture .await ( 10 , TimeUnit .SECONDS ) );
@@ -85,10 +95,26 @@ public void shouldConnect() throws Exception
85
95
assertTrue ( channel .isActive () );
86
96
}
87
97
98
+ @ Test
99
+ public void shouldSetupHandlers () throws Exception
100
+ {
101
+ ChannelConnector connector = newConnector ( neo4j .authToken (), SecurityPlan .forAllCertificates (), 10_000 );
102
+
103
+ ChannelFuture channelFuture = connector .connect ( neo4j .address (), bootstrap );
104
+ assertTrue ( channelFuture .await ( 10 , TimeUnit .SECONDS ) );
105
+
106
+ Channel channel = channelFuture .channel ();
107
+ ChannelPipeline pipeline = channel .pipeline ();
108
+ assertTrue ( channel .isActive () );
109
+
110
+ assertNotNull ( pipeline .get ( SslHandler .class ) );
111
+ assertNull ( pipeline .get ( ConnectTimeoutHandler .class ) );
112
+ }
113
+
88
114
@ Test
89
115
public void shouldFailToConnectToWrongAddress () throws Exception
90
116
{
91
- ChannelConnectorImpl connector = newConnector ( neo4j .authToken () );
117
+ ChannelConnector connector = newConnector ( neo4j .authToken () );
92
118
93
119
ChannelFuture channelFuture = connector .connect ( new BoltServerAddress ( "wrong-localhost" ), bootstrap );
94
120
assertTrue ( channelFuture .await ( 10 , TimeUnit .SECONDS ) );
@@ -112,7 +138,7 @@ public void shouldFailToConnectToWrongAddress() throws Exception
112
138
public void shouldFailToConnectWithWrongCredentials () throws Exception
113
139
{
114
140
AuthToken authToken = AuthTokens .basic ( "neo4j" , "wrong-password" );
115
- ChannelConnectorImpl connector = newConnector ( authToken );
141
+ ChannelConnector connector = newConnector ( authToken );
116
142
117
143
ChannelFuture channelFuture = connector .connect ( neo4j .address (), bootstrap );
118
144
assertTrue ( channelFuture .await ( 10 , TimeUnit .SECONDS ) );
@@ -131,10 +157,10 @@ public void shouldFailToConnectWithWrongCredentials() throws Exception
131
157
assertFalse ( channel .isActive () );
132
158
}
133
159
134
- @ Test ( timeout = 10000 )
160
+ @ Test
135
161
public void shouldEnforceConnectTimeout () throws Exception
136
162
{
137
- ChannelConnectorImpl connector = newConnector ( neo4j .authToken (), 1000 );
163
+ ChannelConnector connector = newConnector ( neo4j .authToken (), 1000 );
138
164
139
165
// try connect to a non-routable ip address 10.0.0.0, it will never respond
140
166
ChannelFuture channelFuture = connector .connect ( new BoltServerAddress ( "10.0.0.0" ), bootstrap );
@@ -151,15 +177,55 @@ public void shouldEnforceConnectTimeout() throws Exception
151
177
}
152
178
}
153
179
180
+ @ Test
181
+ public void shouldFailWhenProtocolNegotiationTakesTooLong () throws Exception
182
+ {
183
+ // run without TLS so that Bolt handshake is the very first operation after connection is established
184
+ testReadTimeoutOnConnect ( SecurityPlan .insecure () );
185
+ }
186
+
187
+ @ Test
188
+ public void shouldFailWhenTLSHandshakeTakesTooLong () throws Exception
189
+ {
190
+ // run with TLS so that TLS handshake is the very first operation after connection is established
191
+ testReadTimeoutOnConnect ( SecurityPlan .forAllCertificates () );
192
+ }
193
+
194
+ private void testReadTimeoutOnConnect ( SecurityPlan securityPlan ) throws IOException
195
+ {
196
+ try ( ServerSocket server = new ServerSocket ( 0 ) ) // server that accepts connections but does not reply
197
+ {
198
+ int timeoutMillis = 1_000 ;
199
+ BoltServerAddress address = new BoltServerAddress ( "localhost" , server .getLocalPort () );
200
+ ChannelConnector connector = newConnector ( neo4j .authToken (), securityPlan , timeoutMillis );
201
+
202
+ ChannelFuture channelFuture = connector .connect ( address , bootstrap );
203
+ try
204
+ {
205
+ await ( channelFuture );
206
+ fail ( "Exception expected" );
207
+ }
208
+ catch ( ServiceUnavailableException e )
209
+ {
210
+ assertEquals ( e .getMessage (), "Unable to establish connection in " + timeoutMillis + "ms" );
211
+ }
212
+ }
213
+ }
214
+
154
215
private ChannelConnectorImpl newConnector ( AuthToken authToken ) throws Exception
155
216
{
156
217
return newConnector ( authToken , Integer .MAX_VALUE );
157
218
}
158
219
159
220
private ChannelConnectorImpl newConnector ( AuthToken authToken , int connectTimeoutMillis ) throws Exception
160
221
{
161
- ConnectionSettings settings = new ConnectionSettings ( authToken , 1000 );
162
- return new ChannelConnectorImpl ( settings , SecurityPlan .forAllCertificates (), DEV_NULL_LOGGING ,
163
- new FakeClock () );
222
+ return newConnector ( authToken , SecurityPlan .forAllCertificates (), connectTimeoutMillis );
223
+ }
224
+
225
+ private ChannelConnectorImpl newConnector ( AuthToken authToken , SecurityPlan securityPlan ,
226
+ int connectTimeoutMillis )
227
+ {
228
+ ConnectionSettings settings = new ConnectionSettings ( authToken , connectTimeoutMillis );
229
+ return new ChannelConnectorImpl ( settings , securityPlan , DEV_NULL_LOGGING , new FakeClock () );
164
230
}
165
231
}
0 commit comments