|
|
|
@ -21,6 +21,7 @@ import java.io.DataOutputStream; |
|
|
|
import java.io.IOException; |
|
|
|
import java.io.IOException; |
|
|
|
import java.nio.charset.StandardCharsets; |
|
|
|
import java.nio.charset.StandardCharsets; |
|
|
|
import java.util.Collections; |
|
|
|
import java.util.Collections; |
|
|
|
|
|
|
|
import java.util.LinkedHashMap; |
|
|
|
import java.util.List; |
|
|
|
import java.util.List; |
|
|
|
import java.util.Map; |
|
|
|
import java.util.Map; |
|
|
|
import java.util.Map.Entry; |
|
|
|
import java.util.Map.Entry; |
|
|
|
@ -50,6 +51,16 @@ public class StompEncoder { |
|
|
|
|
|
|
|
|
|
|
|
private static final Log logger = LogFactory.getLog(StompEncoder.class); |
|
|
|
private static final Log logger = LogFactory.getLog(StompEncoder.class); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private static final int HEADER_KEY_CACHE_LIMIT = 32; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@SuppressWarnings("serial") |
|
|
|
|
|
|
|
private final Map<String, byte[]> headerKeyCache = |
|
|
|
|
|
|
|
new LinkedHashMap<String, byte[]>(HEADER_KEY_CACHE_LIMIT, 0.75f, true) { |
|
|
|
|
|
|
|
@Override |
|
|
|
|
|
|
|
protected boolean removeEldestEntry(Map.Entry<String, byte[]> eldest) { |
|
|
|
|
|
|
|
return size() > HEADER_KEY_CACHE_LIMIT; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
/** |
|
|
|
* Encodes the given STOMP {@code message} into a {@code byte[]} |
|
|
|
* Encodes the given STOMP {@code message} into a {@code byte[]} |
|
|
|
@ -130,11 +141,11 @@ public class StompEncoder { |
|
|
|
values = Collections.singletonList(StompHeaderAccessor.getPasscode(headers)); |
|
|
|
values = Collections.singletonList(StompHeaderAccessor.getPasscode(headers)); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
byte[] encodedKey = encodeHeaderString(entry.getKey(), shouldEscape); |
|
|
|
byte[] encodedKey = encodeHeaderKey(entry.getKey(), shouldEscape); |
|
|
|
for (String value : values) { |
|
|
|
for (String value : values) { |
|
|
|
output.write(encodedKey); |
|
|
|
output.write(encodedKey); |
|
|
|
output.write(COLON); |
|
|
|
output.write(COLON); |
|
|
|
output.write(encodeHeaderString(value, shouldEscape)); |
|
|
|
output.write(encodeHeaderValue(value, shouldEscape)); |
|
|
|
output.write(LF); |
|
|
|
output.write(LF); |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
@ -147,9 +158,23 @@ public class StompEncoder { |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
private byte[] encodeHeaderString(String input, boolean escape) { |
|
|
|
private byte[] encodeHeaderKey(String input, boolean escape) { |
|
|
|
|
|
|
|
String inputToUse = (escape ? escape(input) : input); |
|
|
|
|
|
|
|
if (headerKeyCache.containsKey(inputToUse)) { |
|
|
|
|
|
|
|
return headerKeyCache.get(inputToUse); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
byte[] bytes = encodeHeaderString(inputToUse); |
|
|
|
|
|
|
|
headerKeyCache.put(inputToUse, bytes); |
|
|
|
|
|
|
|
return bytes; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private byte[] encodeHeaderValue(String input, boolean escape) { |
|
|
|
String inputToUse = (escape ? escape(input) : input); |
|
|
|
String inputToUse = (escape ? escape(input) : input); |
|
|
|
return inputToUse.getBytes(StandardCharsets.UTF_8); |
|
|
|
return encodeHeaderString(inputToUse); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private byte[] encodeHeaderString(String input) { |
|
|
|
|
|
|
|
return input.getBytes(StandardCharsets.UTF_8); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
/** |
|
|
|
@ -157,26 +182,38 @@ public class StompEncoder { |
|
|
|
* <a href="http://stomp.github.io/stomp-specification-1.2.html#Value_Encoding">"Value Encoding"</a>. |
|
|
|
* <a href="http://stomp.github.io/stomp-specification-1.2.html#Value_Encoding">"Value Encoding"</a>. |
|
|
|
*/ |
|
|
|
*/ |
|
|
|
private String escape(String inString) { |
|
|
|
private String escape(String inString) { |
|
|
|
StringBuilder sb = new StringBuilder(inString.length()); |
|
|
|
StringBuilder sb = null; |
|
|
|
for (int i = 0; i < inString.length(); i++) { |
|
|
|
for (int i = 0; i < inString.length(); i++) { |
|
|
|
char c = inString.charAt(i); |
|
|
|
char c = inString.charAt(i); |
|
|
|
if (c == '\\') { |
|
|
|
if (c == '\\') { |
|
|
|
|
|
|
|
sb = getStringBuilder(sb, inString, i); |
|
|
|
sb.append("\\\\"); |
|
|
|
sb.append("\\\\"); |
|
|
|
} |
|
|
|
} |
|
|
|
else if (c == ':') { |
|
|
|
else if (c == ':') { |
|
|
|
|
|
|
|
sb = getStringBuilder(sb, inString, i); |
|
|
|
sb.append("\\c"); |
|
|
|
sb.append("\\c"); |
|
|
|
} |
|
|
|
} |
|
|
|
else if (c == '\n') { |
|
|
|
else if (c == '\n') { |
|
|
|
sb.append("\\n"); |
|
|
|
sb = getStringBuilder(sb, inString, i); |
|
|
|
|
|
|
|
sb.append("\\n"); |
|
|
|
} |
|
|
|
} |
|
|
|
else if (c == '\r') { |
|
|
|
else if (c == '\r') { |
|
|
|
|
|
|
|
sb = getStringBuilder(sb, inString, i); |
|
|
|
sb.append("\\r"); |
|
|
|
sb.append("\\r"); |
|
|
|
} |
|
|
|
} |
|
|
|
else { |
|
|
|
else if (sb != null){ |
|
|
|
sb.append(c); |
|
|
|
sb.append(c); |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
return sb.toString(); |
|
|
|
return (sb != null ? sb.toString() : inString); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private StringBuilder getStringBuilder(StringBuilder sb, String inString, int i) { |
|
|
|
|
|
|
|
if (sb == null) { |
|
|
|
|
|
|
|
sb = new StringBuilder(inString.length()); |
|
|
|
|
|
|
|
sb.append(inString.substring(0, i)); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
return sb; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
private void writeBody(byte[] payload, DataOutputStream output) throws IOException { |
|
|
|
private void writeBody(byte[] payload, DataOutputStream output) throws IOException { |
|
|
|
|