Skip to content

Commit e691bef

Browse files
committed
Fix handling of codec exceptions without cause
Driver tries to extract cause of every `CodecException` received in `#exceptionCaught(Throwable)` netty callbacks. It did not handle cases when the cause is null, which resulted in an attempt to exceptionally complete a `CompletableFuture` with null. This commit fixes the problem by making handlers extract the exception cause only if it is defined and propagate the original error otherwise.
1 parent 1b8c4bb commit e691bef

File tree

4 files changed

+50
-8
lines changed

4 files changed

+50
-8
lines changed

driver/src/main/java/org/neo4j/driver/internal/async/HandshakeHandler.java

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -165,18 +165,23 @@ private static Throwable protocolNoSupportedByDriverError( int suggestedProtocol
165165

166166
private static Throwable transformError( Throwable error )
167167
{
168-
Throwable cause = error instanceof DecoderException ? error.getCause() : error;
169-
if ( cause instanceof ServiceUnavailableException )
168+
if ( error instanceof DecoderException && error.getCause() != null )
170169
{
171-
return cause;
170+
// unwrap the DecoderException if it has a cause
171+
error = error.getCause();
172172
}
173-
else if ( cause instanceof SSLHandshakeException )
173+
174+
if ( error instanceof ServiceUnavailableException )
175+
{
176+
return error;
177+
}
178+
else if ( error instanceof SSLHandshakeException )
174179
{
175-
return new SecurityException( "Failed to establish secured connection with the server", cause );
180+
return new SecurityException( "Failed to establish secured connection with the server", error );
176181
}
177182
else
178183
{
179-
return new ServiceUnavailableException( "Failed to establish connection with the server", cause );
184+
return new ServiceUnavailableException( "Failed to establish connection with the server", error );
180185
}
181186
}
182187
}

driver/src/main/java/org/neo4j/driver/internal/async/inbound/ChannelErrorHandler.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -103,9 +103,9 @@ private void fail( ChannelHandlerContext ctx, Throwable error )
103103

104104
private static Throwable transformError( Throwable error )
105105
{
106-
if ( error instanceof CodecException )
106+
if ( error instanceof CodecException && error.getCause() != null )
107107
{
108-
// unwrap exception from message encoder/decoder
108+
// unwrap the CodecException if it has a cause
109109
error = error.getCause();
110110
}
111111

driver/src/test/java/org/neo4j/driver/internal/async/ChannelErrorHandlerTest.java

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,18 @@ public void shouldHandleCodecException()
117117
assertFalse( channel.isOpen() );
118118
}
119119

120+
@Test
121+
public void shouldHandleCodecExceptionWithoutCause()
122+
{
123+
CodecException codecException = new CodecException( "Unable to encode or decode message" );
124+
channel.pipeline().fireExceptionCaught( codecException );
125+
126+
Throwable error = messageDispatcher.currentError();
127+
128+
assertEquals( codecException, error );
129+
assertFalse( channel.isOpen() );
130+
}
131+
120132
@Test
121133
public void shouldHandleIOException()
122134
{

driver/src/test/java/org/neo4j/driver/internal/async/HandshakeHandlerTest.java

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -187,6 +187,31 @@ public void shouldUnwrapDecoderException()
187187
assertNull( await( channel.closeFuture() ) );
188188
}
189189

190+
@Test
191+
public void shouldHandleDecoderExceptionWithoutCause()
192+
{
193+
ChannelPromise handshakeCompletedPromise = channel.newPromise();
194+
HandshakeHandler handler = newHandler( handshakeCompletedPromise );
195+
channel.pipeline().addLast( handler );
196+
197+
DecoderException decoderException = new DecoderException( "Unable to decode a message" );
198+
channel.pipeline().fireExceptionCaught( decoderException );
199+
200+
try
201+
{
202+
// promise should fail
203+
await( handshakeCompletedPromise );
204+
fail( "Exception expected" );
205+
}
206+
catch ( ServiceUnavailableException e )
207+
{
208+
assertEquals( decoderException, e.getCause() );
209+
}
210+
211+
// channel should be closed
212+
assertNull( await( channel.closeFuture() ) );
213+
}
214+
190215
@Test
191216
public void shouldTranslateSSLHandshakeException()
192217
{

0 commit comments

Comments
 (0)