diff --git a/spring-messaging/src/main/java/org/springframework/messaging/simp/stomp/StompDecoder.java b/spring-messaging/src/main/java/org/springframework/messaging/simp/stomp/StompDecoder.java index 60e5bb4ebb56..3c1b82ba8a73 100644 --- a/spring-messaging/src/main/java/org/springframework/messaging/simp/stomp/StompDecoder.java +++ b/spring-messaging/src/main/java/org/springframework/messaging/simp/stomp/StompDecoder.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2019 the original author or authors. + * Copyright 2002-2020 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -114,6 +114,10 @@ public List> decode(ByteBuffer byteBuffer, Message message = decodeMessage(byteBuffer, partialMessageHeaders); if (message != null) { messages.add(message); + skipEol(byteBuffer); + if (!byteBuffer.hasRemaining()) { + break; + } } else { break; @@ -128,7 +132,7 @@ public List> decode(ByteBuffer byteBuffer, @Nullable private Message decodeMessage(ByteBuffer byteBuffer, @Nullable MultiValueMap headers) { Message decodedMessage = null; - skipLeadingEol(byteBuffer); + skipEol(byteBuffer); // Explicit mark/reset access via Buffer base type for compatibility // with covariant return type on JDK 9's ByteBuffer... @@ -196,9 +200,10 @@ private void initHeaders(StompHeaderAccessor headerAccessor) { /** * Skip one ore more EOL characters at the start of the given ByteBuffer. - * Those are STOMP heartbeat frames. + * STOMP, section 2.1 says: "The NULL octet can be optionally followed by + * multiple EOLs." */ - protected void skipLeadingEol(ByteBuffer byteBuffer) { + protected void skipEol(ByteBuffer byteBuffer) { while (true) { if (!tryConsumeEndOfLine(byteBuffer)) { break; diff --git a/spring-messaging/src/test/java/org/springframework/messaging/simp/stomp/StompDecoderTests.java b/spring-messaging/src/test/java/org/springframework/messaging/simp/stomp/StompDecoderTests.java index 35734d0ea54d..439b157d2d6e 100644 --- a/spring-messaging/src/test/java/org/springframework/messaging/simp/stomp/StompDecoderTests.java +++ b/spring-messaging/src/test/java/org/springframework/messaging/simp/stomp/StompDecoderTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2019 the original author or authors. + * Copyright 2002-2020 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,7 +16,6 @@ package org.springframework.messaging.simp.stomp; -import java.io.UnsupportedEncodingException; import java.nio.ByteBuffer; import java.util.List; @@ -78,7 +77,7 @@ public void decodeFrameWithNoBody() { } @Test - public void decodeFrame() throws UnsupportedEncodingException { + public void decodeFrame() { Message frame = decode("SEND\ndestination:test\n\nThe body of the message\0"); StompHeaderAccessor headers = StompHeaderAccessor.wrap(frame); @@ -166,6 +165,17 @@ public void decodeFrameBodyNotAllowed() { decode("CONNECT\naccept-version:1.2\n\nThe body of the message\0")); } + @Test // gh-23713 + public void decodeFramesWithExtraNewLines() { + String frame1 = "SEND\ndestination:test\n\nbody\0\n\n\n"; + ByteBuffer buffer = ByteBuffer.wrap((frame1).getBytes()); + + final List> messages = decoder.decode(buffer); + + assertThat(messages.size()).isEqualTo(1); + assertThat(StompHeaderAccessor.wrap(messages.get(0)).getCommand()).isEqualTo(StompCommand.SEND); + } + @Test public void decodeMultipleFramesFromSameBuffer() { String frame1 = "SEND\ndestination:test\n\nThe body of the message\0"; @@ -179,9 +189,7 @@ public void decodeMultipleFramesFromSameBuffer() { assertThat(StompHeaderAccessor.wrap(messages.get(1)).getCommand()).isEqualTo(StompCommand.DISCONNECT); } - // SPR-13111 - - @Test + @Test // SPR-13111 public void decodeFrameWithHeaderWithEmptyValue() { String accept = "accept-version:1.1\n"; String valuelessKey = "key:\n";