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 2edd0202963..44a083414e1 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 @@ -54,7 +54,6 @@ public class StompDecoder { private final Log logger = LogFactory.getLog(StompDecoder.class); - /** * Decodes one or more STOMP frames from the given {@code ByteBuffer} into a * list of {@link Message}s. If the input buffer contains any incplcontains partial STOMP frame content, or additional @@ -201,11 +200,41 @@ public class StompDecoder { } } - private String unescape(String input) { - return input.replaceAll("\\\\n", "\n") - .replaceAll("\\\\r", "\r") - .replaceAll("\\\\c", ":") - .replaceAll("\\\\\\\\", "\\\\"); + /** + * See STOMP Spec 1.2: + * "Value Encoding". + */ + private String unescape(String inString) { + + StringBuilder sb = new StringBuilder(); + int pos = 0; // position in the old string + int index = inString.indexOf("\\"); + + while (index >= 0) { + sb.append(inString.substring(pos, index)); + Character c = inString.charAt(index + 1); + if (c == 'r') { + sb.append('\r'); + } + else if (c == 'n') { + sb.append('\n'); + } + else if (c == 'c') { + sb.append(':'); + } + else if (c == '\\') { + sb.append('\\'); + } + else { + // should never happen + throw new StompConversionException("Illegal escape sequence at index " + index + ": " + inString); + } + pos = index + 2; + index = inString.indexOf("\\", pos); + } + + sb.append(inString.substring(pos)); + return sb.toString(); } private byte[] readPayload(ByteBuffer buffer, MultiValueMap headers) { diff --git a/spring-messaging/src/main/java/org/springframework/messaging/simp/stomp/StompEncoder.java b/spring-messaging/src/main/java/org/springframework/messaging/simp/stomp/StompEncoder.java index e7f94ddd9e9..0deb332361b 100644 --- a/spring-messaging/src/main/java/org/springframework/messaging/simp/stomp/StompEncoder.java +++ b/spring-messaging/src/main/java/org/springframework/messaging/simp/stomp/StompEncoder.java @@ -34,6 +34,7 @@ import org.springframework.messaging.simp.SimpMessageType; * An encoder for STOMP frames. * * @author Andy Wilkinson + * @author Rossen Stoyanchev * @since 4.0 */ public final class StompEncoder { @@ -95,42 +96,64 @@ public final class StompEncoder { else if (logger.isDebugEnabled()) { logger.debug("Encoded STOMP command=" + headers.getCommand() + " headers=" + stompHeaders); } + boolean escapeHeaders = shouldEscapeHeaders(headers); for (Entry> entry : stompHeaders.entrySet()) { - byte[] key = getUtf8BytesEscapingIfNecessary(entry.getKey(), headers); + byte[] key = toUtf8Bytes(entry.getKey(), escapeHeaders); for (String value : entry.getValue()) { output.write(key); output.write(COLON); - output.write(getUtf8BytesEscapingIfNecessary(value, headers)); + output.write(toUtf8Bytes(value, escapeHeaders)); output.write(LF); } } if ((headers.getCommand() == StompCommand.SEND) || (headers.getCommand() == StompCommand.MESSAGE) || (headers.getCommand() == StompCommand.ERROR)) { + int contentLength = message.getPayload().length; output.write("content-length:".getBytes(UTF8_CHARSET)); - output.write(Integer.toString(message.getPayload().length).getBytes(UTF8_CHARSET)); + output.write(Integer.toString(contentLength).getBytes(UTF8_CHARSET)); output.write(LF); } } - private void writeBody(Message message, DataOutputStream output) throws IOException { - output.write(message.getPayload()); + private boolean shouldEscapeHeaders(StompHeaderAccessor headers) { + return (headers.getCommand() != StompCommand.CONNECT && headers.getCommand() != StompCommand.CONNECTED); } - private byte[] getUtf8BytesEscapingIfNecessary(String input, StompHeaderAccessor headers) { - if (headers.getCommand() != StompCommand.CONNECT && headers.getCommand() != StompCommand.CONNECTED) { - return escape(input).getBytes(UTF8_CHARSET); - } - else { - return input.getBytes(UTF8_CHARSET); + private byte[] toUtf8Bytes(String input, boolean escape) { + input = escape ? escape(input) : input; + return input.getBytes(UTF8_CHARSET); + } + + /** + * See STOMP Spec 1.2: + * "Value Encoding". + */ + private String escape(String inString) { + StringBuilder sb = new StringBuilder(inString.length()); + for (int i = 0; i < inString.length(); i++) { + char c = inString.charAt(i); + if (c == '\\') { + sb.append("\\\\"); + } + else if (c == ':') { + sb.append("\\c"); + } + else if (c == '\n') { + sb.append("\\n"); + } + else if (c == '\r') { + sb.append("\\r"); + } + else { + sb.append(c); + } } + return sb.toString(); } - private String escape(String input) { - return input.replaceAll("\\\\", "\\\\\\\\") - .replaceAll(":", "\\\\c") - .replaceAll("\n", "\\\\n") - .replaceAll("\r", "\\\\r"); + private void writeBody(Message message, DataOutputStream output) throws IOException { + output.write(message.getPayload()); } }