@Test public void basic() throws InterruptedException { BufferingStompDecoder stompDecoder = new BufferingStompDecoder(STOMP_DECODER, 128); String chunk = "SEND\na:alpha\n\nMessage body\0"; List<Message<byte[]>> messages = stompDecoder.decode(toByteBuffer(chunk)); assertEquals(1, messages.size()); assertEquals("Message body", new String(messages.get(0).getPayload())); assertEquals(0, stompDecoder.getBufferSize()); assertNull(stompDecoder.getExpectedContentLength()); }
/** * Decodes one or more STOMP frames from the given {@code ByteBuffer} into a * list of {@link Message Messages}. * <p>If there was enough data to parse a "content-length" header, then the * value is used to determine how much more data is needed before a new * attempt to decode is made. * <p>If there was not enough data to parse the "content-length", or if there * is "content-length" header, every subsequent call to decode attempts to * parse again with all available data. Therefore the presence of a "content-length" * header helps to optimize the decoding of large messages. * @param newBuffer a buffer containing new data to decode * @return decoded messages or an empty list * @throws StompConversionException raised in case of decoding issues */ public List<Message<byte[]>> decode(ByteBuffer newBuffer) { this.chunks.add(newBuffer); checkBufferLimits(); Integer contentLength = this.expectedContentLength; if (contentLength != null && getBufferSize() < contentLength) { return Collections.emptyList(); } ByteBuffer bufferToDecode = assembleChunksAndReset(); MultiValueMap<String, String> headers = new LinkedMultiValueMap<>(); List<Message<byte[]>> messages = this.stompDecoder.decode(bufferToDecode, headers); if (bufferToDecode.hasRemaining()) { this.chunks.add(bufferToDecode); this.expectedContentLength = StompHeaderAccessor.getContentLength(headers); } return messages; }
public List<Message<byte[]>> decode(WebSocketMessage<?> webSocketMessage) { List<Message<byte[]>> result = Collections.emptyList(); ByteBuffer byteBuffer; if (webSocketMessage instanceof TextMessage) { byteBuffer = ByteBuffer.wrap(((TextMessage) webSocketMessage).asBytes()); } else if (webSocketMessage instanceof BinaryMessage) { byteBuffer = ((BinaryMessage) webSocketMessage).getPayload(); } else { return result; } result = this.bufferingDecoder.decode(byteBuffer); if (result.isEmpty()) { if (logger.isTraceEnabled()) { logger.trace("Incomplete STOMP frame content received, bufferSize=" + this.bufferingDecoder.getBufferSize() + ", bufferSizeLimit=" + this.bufferingDecoder.getBufferSizeLimit() + "."); } } return result; }
@Test(expected = StompConversionException.class) public void invalidEscapeSequenceWithSingleSlashAtEndOfHeaderValue() { BufferingStompDecoder stompDecoder = new BufferingStompDecoder(STOMP_DECODER, 128); String payload = "SEND\na:alpha\\\n\nMessage body\0"; stompDecoder.decode(toByteBuffer(payload)); }
private ByteBuffer assembleChunksAndReset() { ByteBuffer result; if (this.chunks.size() == 1) { result = this.chunks.remove(); } else { result = ByteBuffer.allocate(getBufferSize()); for (ByteBuffer partial : this.chunks) { result.put(partial); } result.flip(); } this.chunks.clear(); this.expectedContentLength = null; return result; }
public StompWebSocketMessageCodec(int messageSizeLimit) { this.bufferingDecoder = new BufferingStompDecoder(DECODER, messageSizeLimit); }
private void checkBufferLimits() { Integer contentLength = this.expectedContentLength; if (contentLength != null && contentLength > this.bufferSizeLimit) { throw new StompConversionException( "STOMP 'content-length' header value " + this.expectedContentLength + " exceeds configured buffer size limit " + this.bufferSizeLimit); } if (getBufferSize() > this.bufferSizeLimit) { throw new StompConversionException("The configured STOMP buffer size limit of " + this.bufferSizeLimit + " bytes has been exceeded"); } }
@Test(expected = StompConversionException.class) public void bufferSizeLimit() { BufferingStompDecoder stompDecoder = new BufferingStompDecoder(STOMP_DECODER, 10); String payload = "SEND\na:alpha\n\nMessage body"; stompDecoder.decode(toByteBuffer(payload)); }
@Override public void afterSessionStarted(WebSocketSession session, MessageChannel outputChannel) { if (session.getTextMessageSizeLimit() < MINIMUM_WEBSOCKET_MESSAGE_SIZE) { session.setTextMessageSizeLimit(MINIMUM_WEBSOCKET_MESSAGE_SIZE); } this.decoders.put(session.getId(), new BufferingStompDecoder(this.stompDecoder, getMessageSizeLimit())); }
@Test public void twoMessagesInOneChunk() throws InterruptedException { BufferingStompDecoder stompDecoder = new BufferingStompDecoder(STOMP_DECODER, 128); String chunk = "SEND\na:alpha\n\nPayload1\0" + "SEND\na:alpha\n\nPayload2\0"; List<Message<byte[]>> messages = stompDecoder.decode(toByteBuffer(chunk)); assertEquals(2, messages.size()); assertEquals("Payload1", new String(messages.get(0).getPayload())); assertEquals("Payload2", new String(messages.get(1).getPayload())); assertEquals(0, stompDecoder.getBufferSize()); assertNull(stompDecoder.getExpectedContentLength()); }
messages = decoder.decode(byteBuffer); if (messages.isEmpty()) { if (logger.isTraceEnabled()) { logger.trace("Incomplete STOMP frame content received in session " + session + ", bufferSize=" + decoder.getBufferSize() + ", bufferSizeLimit=" + decoder.getBufferSizeLimit() + ".");
/** * Decodes one or more STOMP frames from the given {@code ByteBuffer} into a * list of {@link Message Messages}. * <p>If there was enough data to parse a "content-length" header, then the * value is used to determine how much more data is needed before a new * attempt to decode is made. * <p>If there was not enough data to parse the "content-length", or if there * is "content-length" header, every subsequent call to decode attempts to * parse again with all available data. Therefore the presence of a "content-length" * header helps to optimize the decoding of large messages. * @param newBuffer a buffer containing new data to decode * @return decoded messages or an empty list * @throws StompConversionException raised in case of decoding issues */ public List<Message<byte[]>> decode(ByteBuffer newBuffer) { this.chunks.add(newBuffer); checkBufferLimits(); Integer contentLength = this.expectedContentLength; if (contentLength != null && getBufferSize() < contentLength) { return Collections.emptyList(); } ByteBuffer bufferToDecode = assembleChunksAndReset(); MultiValueMap<String, String> headers = new LinkedMultiValueMap<>(); List<Message<byte[]>> messages = this.stompDecoder.decode(bufferToDecode, headers); if (bufferToDecode.hasRemaining()) { this.chunks.add(bufferToDecode); this.expectedContentLength = StompHeaderAccessor.getContentLength(headers); } return messages; }
private ByteBuffer assembleChunksAndReset() { ByteBuffer result; if (this.chunks.size() == 1) { result = this.chunks.remove(); } else { result = ByteBuffer.allocate(getBufferSize()); for (ByteBuffer partial : this.chunks) { result.put(partial); } result.flip(); } this.chunks.clear(); this.expectedContentLength = null; return result; }
@Test(expected = StompConversionException.class) public void invalidEscapeSequence() { BufferingStompDecoder stompDecoder = new BufferingStompDecoder(STOMP_DECODER, 128); String payload = "SEND\na:alpha\\x\\n\nMessage body\0"; stompDecoder.decode(toByteBuffer(payload)); }
public StompWebSocketMessageCodec(int messageSizeLimit) { this.bufferingDecoder = new BufferingStompDecoder(DECODER, messageSizeLimit); }
@Test public void oneMessageInTwoChunks() throws InterruptedException { BufferingStompDecoder stompDecoder = new BufferingStompDecoder(STOMP_DECODER, 128); String chunk1 = "SEND\na:alpha\n\nMessage"; String chunk2 = " body\0"; List<Message<byte[]>> messages = stompDecoder.decode(toByteBuffer(chunk1)); assertEquals(Collections.<Message<byte[]>>emptyList(), messages); messages = stompDecoder.decode(toByteBuffer(chunk2)); assertEquals(1, messages.size()); assertEquals("Message body", new String(messages.get(0).getPayload())); assertEquals(0, stompDecoder.getBufferSize()); assertNull(stompDecoder.getExpectedContentLength()); }
public List<Message<byte[]>> decode(WebSocketMessage<?> webSocketMessage) { List<Message<byte[]>> result = Collections.emptyList(); ByteBuffer byteBuffer; if (webSocketMessage instanceof TextMessage) { byteBuffer = ByteBuffer.wrap(((TextMessage) webSocketMessage).asBytes()); } else if (webSocketMessage instanceof BinaryMessage) { byteBuffer = ((BinaryMessage) webSocketMessage).getPayload(); } else { return result; } result = this.bufferingDecoder.decode(byteBuffer); if (result.isEmpty()) { if (logger.isTraceEnabled()) { logger.trace("Incomplete STOMP frame content received, bufferSize=" + this.bufferingDecoder.getBufferSize() + ", bufferSizeLimit=" + this.bufferingDecoder.getBufferSizeLimit() + "."); } } return result; }
/** * Decodes one or more STOMP frames from the given {@code ByteBuffer} into a * list of {@link Message Messages}. * <p>If there was enough data to parse a "content-length" header, then the * value is used to determine how much more data is needed before a new * attempt to decode is made. * <p>If there was not enough data to parse the "content-length", or if there * is "content-length" header, every subsequent call to decode attempts to * parse again with all available data. Therefore the presence of a "content-length" * header helps to optimize the decoding of large messages. * @param newBuffer a buffer containing new data to decode * @return decoded messages or an empty list * @throws StompConversionException raised in case of decoding issues */ public List<Message<byte[]>> decode(ByteBuffer newBuffer) { this.chunks.add(newBuffer); checkBufferLimits(); Integer contentLength = this.expectedContentLength; if (contentLength != null && getBufferSize() < contentLength) { return Collections.emptyList(); } ByteBuffer bufferToDecode = assembleChunksAndReset(); MultiValueMap<String, String> headers = new LinkedMultiValueMap<>(); List<Message<byte[]>> messages = this.stompDecoder.decode(bufferToDecode, headers); if (bufferToDecode.hasRemaining()) { this.chunks.add(bufferToDecode); this.expectedContentLength = StompHeaderAccessor.getContentLength(headers); } return messages; }
private void checkBufferLimits() { Integer contentLength = this.expectedContentLength; if (contentLength != null && contentLength > this.bufferSizeLimit) { throw new StompConversionException( "STOMP 'content-length' header value " + this.expectedContentLength + " exceeds configured buffer size limit " + this.bufferSizeLimit); } if (getBufferSize() > this.bufferSizeLimit) { throw new StompConversionException("The configured STOMP buffer size limit of " + this.bufferSizeLimit + " bytes has been exceeded"); } }
@Test public void incompleteHeaderWithPartialEscapeSequence() throws Exception { BufferingStompDecoder stompDecoder = new BufferingStompDecoder(STOMP_DECODER, 128); String chunk = "SEND\na:long\\"; List<Message<byte[]>> messages = stompDecoder.decode(toByteBuffer(chunk)); assertEquals(0, messages.size()); }