Skip to content

Commit a254a78

Browse files
authored
Improve code by adding some null checks (#3115)
1 parent 1448b2b commit a254a78

File tree

3 files changed

+41
-24
lines changed

3 files changed

+41
-24
lines changed

src/main/java/io/lettuce/core/protocol/CommandHandler.java

+25-20
Original file line numberDiff line numberDiff line change
@@ -82,9 +82,9 @@
8282
public class CommandHandler extends ChannelDuplexHandler implements HasQueuedCommands {
8383

8484
/**
85-
* When we encounter an unexpected IOException we look for these {@link Throwable#getMessage() messages} (because we have no
86-
* better way to distinguish) and log them at DEBUG rather than WARN, since they are generally caused by unclean client
87-
* disconnects rather than an actual problem.
85+
* When we encounter an unexpected {@link IOException} we look for these {@link Throwable#getMessage() messages} (because we
86+
* have no better way to distinguish) and log them at DEBUG rather than WARN, since they are generally caused by unclean
87+
* client disconnects rather than an actual problem.
8888
*/
8989
static final Set<String> SUPPRESS_IO_EXCEPTION_MESSAGES = LettuceSets.unmodifiableSet("Connection reset by peer",
9090
"Broken pipe", "Connection timed out");
@@ -123,7 +123,7 @@ public class CommandHandler extends ChannelDuplexHandler implements HasQueuedCom
123123

124124
private Channel channel;
125125

126-
private ByteBuf buffer;
126+
private ByteBuf readBuffer;
127127

128128
private boolean hasDecodeProgress;
129129

@@ -182,8 +182,8 @@ protected void setState(LifecycleState lifecycleState) {
182182
}
183183
}
184184

185-
void setBuffer(ByteBuf buffer) {
186-
this.buffer = buffer;
185+
void setReadBuffer(ByteBuf readBuffer) {
186+
this.readBuffer = readBuffer;
187187
}
188188

189189
@Override
@@ -222,7 +222,7 @@ public void channelRegistered(ChannelHandlerContext ctx) throws Exception {
222222

223223
setState(LifecycleState.REGISTERED);
224224

225-
buffer = ctx.alloc().buffer(8192 * 8);
225+
readBuffer = ctx.alloc().buffer(8192 * 8);
226226
rsm = new RedisStateMachine();
227227
ctx.fireChannelRegistered();
228228
}
@@ -242,10 +242,15 @@ public void channelUnregistered(ChannelHandlerContext ctx) throws Exception {
242242
ctx.fireChannelUnregistered();
243243
return;
244244
}
245-
246245
channel = null;
247-
buffer.release();
248-
rsm.close();
246+
247+
if (readBuffer != null) {
248+
readBuffer.release();
249+
}
250+
251+
if (rsm != null) {
252+
rsm.close();
253+
}
249254
rsm = null;
250255

251256
reset();
@@ -596,7 +601,7 @@ public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception
596601
}
597602

598603
try {
599-
if (buffer.refCnt() < 1) {
604+
if (readBuffer == null || readBuffer.refCnt() < 1) {
600605
logger.warn("{} Ignoring received data for closed or abandoned connection", logPrefix());
601606
return;
602607
}
@@ -610,10 +615,10 @@ public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception
610615
logger.trace("{} Buffer: {}", logPrefix(), input.toString(Charset.defaultCharset()).trim());
611616
}
612617

613-
buffer.touch("CommandHandler.read(…)");
614-
buffer.writeBytes(input);
618+
readBuffer.touch("CommandHandler.read(…)");
619+
readBuffer.writeBytes(input);
615620

616-
decode(ctx, buffer);
621+
decode(ctx, readBuffer);
617622
} finally {
618623
input.release();
619624
}
@@ -829,7 +834,7 @@ private boolean decode0(ChannelHandlerContext ctx, ByteBuf buffer, RedisCommand<
829834

830835
private boolean decode0(ChannelHandlerContext ctx, ByteBuf buffer, CommandOutput<?, ?, ?> pushOutput) {
831836

832-
if (!rsm.decode(buffer, pushOutput, ctx::fireExceptionCaught)) {
837+
if (rsm != null && !rsm.decode(buffer, pushOutput, ctx::fireExceptionCaught)) {
833838
return false;
834839
}
835840

@@ -852,11 +857,11 @@ private boolean decode0(ChannelHandlerContext ctx, ByteBuf buffer, CommandOutput
852857
}
853858

854859
protected boolean decode(ByteBuf buffer, CommandOutput<?, ?, ?> output) {
855-
return rsm.decode(buffer, output);
860+
return rsm != null && rsm.decode(buffer, output);
856861
}
857862

858863
protected boolean decode(ByteBuf buffer, RedisCommand<?, ?, ?> command, CommandOutput<?, ?, ?> output) {
859-
return rsm.decode(buffer, output, command::completeExceptionally);
864+
return rsm != null && rsm.decode(buffer, output, command::completeExceptionally);
860865
}
861866

862867
/**
@@ -918,7 +923,7 @@ private void onProtectedMode(String message) {
918923
* @param command
919924
*/
920925
protected void afterDecode(ChannelHandlerContext ctx, RedisCommand<?, ?, ?> command) {
921-
decodeBufferPolicy.afterCommandDecoded(buffer);
926+
decodeBufferPolicy.afterCommandDecoded(readBuffer);
922927
}
923928

924929
private void recordLatency(WithLatency withLatency, RedisCommand<?, ?, ?> command) {
@@ -960,8 +965,8 @@ private void resetInternals() {
960965
rsm.reset();
961966
}
962967

963-
if (buffer.refCnt() > 0) {
964-
buffer.clear();
968+
if (readBuffer != null && readBuffer.refCnt() > 0) {
969+
readBuffer.clear();
965970
}
966971
}
967972

src/main/java/io/lettuce/core/protocol/DecodeBufferPolicies.java

+2-2
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ public static DecodeBufferPolicy ratio(float bufferUsageRatio) {
6969

7070
/**
7171
* {@link DecodeBufferPolicy} that {@link ByteBuf#discardReadBytes() discards read bytes} after each decoding phase. This
72-
* strategy hast the most memory efficiency but also leads to more CPU pressure.
72+
* strategy has the most memory efficiency but also leads to more CPU pressure.
7373
*
7474
* @return the strategy object.
7575
*/
@@ -80,7 +80,7 @@ public static DecodeBufferPolicy always() {
8080
/**
8181
* {@link DecodeBufferPolicy} that {@link ByteBuf#discardSomeReadBytes() discards some read bytes} after each decoding
8282
* phase. This strategy might discard some, all, or none of read bytes depending on its internal implementation to reduce
83-
* overall memory bandwidth consumption at the cost of potentially additional memory consumption.
83+
* overall CPU bandwidth consumption at the cost of potentially additional memory consumption.
8484
*
8585
* @return the strategy object.
8686
*/

src/test/java/io/lettuce/core/protocol/CommandHandlerUnitTests.java

+14-2
Original file line numberDiff line numberDiff line change
@@ -530,7 +530,7 @@ void shouldNotDiscardReadBytes() throws Exception {
530530

531531
// set the command handler buffer capacity to 30, make it easy to test
532532
ByteBuf internalBuffer = context.alloc().buffer(30);
533-
sut.setBuffer(internalBuffer);
533+
sut.setReadBuffer(internalBuffer);
534534

535535
// mock a multi reply, which will reach the buffer usage ratio
536536
ByteBuf msg = context.alloc().buffer(100);
@@ -559,7 +559,7 @@ void shouldDiscardReadBytes() throws Exception {
559559

560560
// set the command handler buffer capacity to 30, make it easy to test
561561
ByteBuf internalBuffer = context.alloc().buffer(30);
562-
sut.setBuffer(internalBuffer);
562+
sut.setReadBuffer(internalBuffer);
563563

564564
// mock a multi reply, which will reach the buffer usage ratio
565565
ByteBuf msg = context.alloc().buffer(100);
@@ -638,4 +638,16 @@ void shouldHandleIncompleteResponses() throws Exception {
638638
assertThat(hmgetCommand.get()).hasSize(3);
639639
}
640640

641+
/**
642+
* @see <a href="https://github.com/redis/lettuce/issues/3087">Issue 3087</a>
643+
*/
644+
@Test
645+
void shouldHandleNullBuffers() throws Exception {
646+
sut.setReadBuffer(null);
647+
sut.channelRead(context, Unpooled.wrappedBuffer(("*4\r\n" + "$3\r\nONE\r\n" + "$4\r\n>TW").getBytes()));
648+
assertThat(stack).hasSize(0);
649+
650+
sut.channelUnregistered(context);
651+
}
652+
641653
}

0 commit comments

Comments
 (0)