Search in sources :

Example 1 with BinaryMessage

use of org.springframework.web.socket.BinaryMessage in project spring-framework by spring-projects.

the class StompSubProtocolHandler method handleMessageFromClient.

/**
 * Handle incoming WebSocket messages from clients.
 */
@Override
public void handleMessageFromClient(WebSocketSession session, WebSocketMessage<?> webSocketMessage, MessageChannel outputChannel) {
    List<Message<byte[]>> messages;
    try {
        ByteBuffer byteBuffer;
        if (webSocketMessage instanceof TextMessage) {
            byteBuffer = ByteBuffer.wrap(((TextMessage) webSocketMessage).asBytes());
        } else if (webSocketMessage instanceof BinaryMessage) {
            byteBuffer = ((BinaryMessage) webSocketMessage).getPayload();
        } else {
            return;
        }
        BufferingStompDecoder decoder = this.decoders.get(session.getId());
        if (decoder == null) {
            if (!session.isOpen()) {
                logger.trace("Dropped inbound WebSocket message due to closed session");
                return;
            }
            throw new IllegalStateException("No decoder for session id '" + session.getId() + "'");
        }
        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() + ".");
            }
            return;
        }
    } catch (Throwable ex) {
        if (logger.isErrorEnabled()) {
            logger.error("Failed to parse " + webSocketMessage + " in session " + session.getId() + ". Sending STOMP ERROR to client.", ex);
        }
        handleError(session, ex, null);
        return;
    }
    for (Message<byte[]> message : messages) {
        StompHeaderAccessor headerAccessor = MessageHeaderAccessor.getAccessor(message, StompHeaderAccessor.class);
        Assert.state(headerAccessor != null, "No StompHeaderAccessor");
        StompCommand command = headerAccessor.getCommand();
        boolean isConnect = StompCommand.CONNECT.equals(command) || StompCommand.STOMP.equals(command);
        boolean sent = false;
        try {
            headerAccessor.setSessionId(session.getId());
            headerAccessor.setSessionAttributes(session.getAttributes());
            headerAccessor.setUser(getUser(session));
            if (isConnect) {
                headerAccessor.setUserChangeCallback(user -> {
                    if (user != null && user != session.getPrincipal()) {
                        this.stompAuthentications.put(session.getId(), user);
                    }
                });
            }
            headerAccessor.setHeader(SimpMessageHeaderAccessor.HEART_BEAT_HEADER, headerAccessor.getHeartbeat());
            if (!detectImmutableMessageInterceptor(outputChannel)) {
                headerAccessor.setImmutable();
            }
            if (logger.isTraceEnabled()) {
                logger.trace("From client: " + headerAccessor.getShortLogMessage(message.getPayload()));
            }
            if (isConnect) {
                this.stats.incrementConnectCount();
            } else if (StompCommand.DISCONNECT.equals(command)) {
                this.stats.incrementDisconnectCount();
            }
            try {
                SimpAttributesContextHolder.setAttributesFromMessage(message);
                sent = outputChannel.send(message);
                if (sent) {
                    if (this.eventPublisher != null) {
                        Principal user = getUser(session);
                        if (isConnect) {
                            publishEvent(this.eventPublisher, new SessionConnectEvent(this, message, user));
                        } else if (StompCommand.SUBSCRIBE.equals(command)) {
                            publishEvent(this.eventPublisher, new SessionSubscribeEvent(this, message, user));
                        } else if (StompCommand.UNSUBSCRIBE.equals(command)) {
                            publishEvent(this.eventPublisher, new SessionUnsubscribeEvent(this, message, user));
                        }
                    }
                }
            } finally {
                SimpAttributesContextHolder.resetAttributes();
            }
        } catch (Throwable ex) {
            if (logger.isDebugEnabled()) {
                logger.debug("Failed to send message to MessageChannel in session " + session.getId(), ex);
            } else if (logger.isErrorEnabled()) {
                // Skip unsent CONNECT messages (likely auth issues)
                if (!isConnect || sent) {
                    logger.error("Failed to send message to MessageChannel in session " + session.getId() + ":" + ex.getMessage());
                }
            }
            handleError(session, ex, message);
        }
    }
}
Also used : TextMessage(org.springframework.web.socket.TextMessage) Message(org.springframework.messaging.Message) WebSocketMessage(org.springframework.web.socket.WebSocketMessage) BinaryMessage(org.springframework.web.socket.BinaryMessage) StompHeaderAccessor(org.springframework.messaging.simp.stomp.StompHeaderAccessor) ByteBuffer(java.nio.ByteBuffer) StompCommand(org.springframework.messaging.simp.stomp.StompCommand) BinaryMessage(org.springframework.web.socket.BinaryMessage) BufferingStompDecoder(org.springframework.messaging.simp.stomp.BufferingStompDecoder) TextMessage(org.springframework.web.socket.TextMessage) Principal(java.security.Principal)

Example 2 with BinaryMessage

use of org.springframework.web.socket.BinaryMessage in project spring-framework by spring-projects.

the class StompSubProtocolHandler method sendToClient.

private void sendToClient(WebSocketSession session, StompHeaderAccessor stompAccessor, byte[] payload) {
    StompCommand command = stompAccessor.getCommand();
    try {
        byte[] bytes = this.stompEncoder.encode(stompAccessor.getMessageHeaders(), payload);
        boolean useBinary = (payload.length > 0 && !(session instanceof SockJsSession) && MimeTypeUtils.APPLICATION_OCTET_STREAM.isCompatibleWith(stompAccessor.getContentType()));
        if (useBinary) {
            session.sendMessage(new BinaryMessage(bytes));
        } else {
            session.sendMessage(new TextMessage(bytes));
        }
    } catch (SessionLimitExceededException ex) {
        // Bad session, just get out
        throw ex;
    } catch (Throwable ex) {
        // Could be part of normal workflow (e.g. browser tab closed)
        if (logger.isDebugEnabled()) {
            logger.debug("Failed to send WebSocket message to client in session " + session.getId(), ex);
        }
        command = StompCommand.ERROR;
    } finally {
        if (StompCommand.ERROR.equals(command)) {
            try {
                session.close(CloseStatus.PROTOCOL_ERROR);
            } catch (IOException ex) {
            // Ignore
            }
        }
    }
}
Also used : SockJsSession(org.springframework.web.socket.sockjs.transport.SockJsSession) SessionLimitExceededException(org.springframework.web.socket.handler.SessionLimitExceededException) IOException(java.io.IOException) StompCommand(org.springframework.messaging.simp.stomp.StompCommand) BinaryMessage(org.springframework.web.socket.BinaryMessage) TextMessage(org.springframework.web.socket.TextMessage)

Example 3 with BinaryMessage

use of org.springframework.web.socket.BinaryMessage in project spring-framework by spring-projects.

the class StompSubProtocolHandlerTests method handleMessageToClientWithBinaryWebSocketMessage.

// SPR-12475
@Test
public void handleMessageToClientWithBinaryWebSocketMessage() {
    StompHeaderAccessor headers = StompHeaderAccessor.create(StompCommand.MESSAGE);
    headers.setMessageId("mess0");
    headers.setSubscriptionId("sub0");
    headers.setContentType(MimeTypeUtils.APPLICATION_OCTET_STREAM);
    headers.setDestination("/queue/foo");
    // Non-empty payload
    byte[] payload = new byte[1];
    Message<byte[]> message = MessageBuilder.createMessage(payload, headers.getMessageHeaders());
    this.protocolHandler.handleMessageToClient(this.session, message);
    assertThat(this.session.getSentMessages().size()).isEqualTo(1);
    WebSocketMessage<?> webSocketMessage = this.session.getSentMessages().get(0);
    assertThat(webSocketMessage instanceof BinaryMessage).isTrue();
    // Empty payload
    payload = EMPTY_PAYLOAD;
    message = MessageBuilder.createMessage(payload, headers.getMessageHeaders());
    this.protocolHandler.handleMessageToClient(this.session, message);
    assertThat(this.session.getSentMessages().size()).isEqualTo(2);
    webSocketMessage = this.session.getSentMessages().get(1);
    assertThat(webSocketMessage instanceof TextMessage).isTrue();
}
Also used : StompHeaderAccessor(org.springframework.messaging.simp.stomp.StompHeaderAccessor) BinaryMessage(org.springframework.web.socket.BinaryMessage) TextMessage(org.springframework.web.socket.TextMessage) Test(org.junit.jupiter.api.Test)

Example 4 with BinaryMessage

use of org.springframework.web.socket.BinaryMessage in project spring-framework by spring-projects.

the class WebSocketStompClientTests method sendWebSocketBinary.

@Test
public void sendWebSocketBinary() throws Exception {
    StompHeaderAccessor accessor = StompHeaderAccessor.create(StompCommand.SEND);
    accessor.setDestination("/b");
    accessor.setContentType(MimeTypeUtils.APPLICATION_OCTET_STREAM);
    byte[] payload = "payload".getBytes(StandardCharsets.UTF_8);
    getTcpConnection().send(MessageBuilder.createMessage(payload, accessor.getMessageHeaders()));
    ArgumentCaptor<BinaryMessage> binaryMessageCaptor = ArgumentCaptor.forClass(BinaryMessage.class);
    verify(this.webSocketSession).sendMessage(binaryMessageCaptor.capture());
    BinaryMessage binaryMessage = binaryMessageCaptor.getValue();
    assertThat(binaryMessage).isNotNull();
    assertThat(new String(binaryMessage.getPayload().array(), StandardCharsets.UTF_8)).isEqualTo("SEND\ndestination:/b\ncontent-type:application/octet-stream\ncontent-length:7\n\npayload\0");
}
Also used : StompHeaderAccessor(org.springframework.messaging.simp.stomp.StompHeaderAccessor) BinaryMessage(org.springframework.web.socket.BinaryMessage) Test(org.junit.jupiter.api.Test)

Example 5 with BinaryMessage

use of org.springframework.web.socket.BinaryMessage in project spring-framework by spring-projects.

the class WebSocketStompClientTests method handleWebSocketMessageBinary.

@Test
@SuppressWarnings({ "unchecked", "rawtypes" })
public void handleWebSocketMessageBinary() throws Exception {
    String text = "SEND\na:alpha\n\nMessage payload\0";
    connect().handleMessage(this.webSocketSession, new BinaryMessage(text.getBytes(StandardCharsets.UTF_8)));
    ArgumentCaptor<Message> captor = ArgumentCaptor.forClass(Message.class);
    verify(this.stompSession).handleMessage(captor.capture());
    Message<byte[]> message = captor.getValue();
    assertThat(message).isNotNull();
    StompHeaderAccessor accessor = MessageHeaderAccessor.getAccessor(message, StompHeaderAccessor.class);
    StompHeaders headers = StompHeaders.readOnlyStompHeaders(accessor.toNativeHeaderMap());
    assertThat(accessor.getCommand()).isEqualTo(StompCommand.SEND);
    assertThat(headers.getFirst("a")).isEqualTo("alpha");
    assertThat(new String(message.getPayload(), StandardCharsets.UTF_8)).isEqualTo("Message payload");
}
Also used : TextMessage(org.springframework.web.socket.TextMessage) PongMessage(org.springframework.web.socket.PongMessage) Message(org.springframework.messaging.Message) BinaryMessage(org.springframework.web.socket.BinaryMessage) StompHeaderAccessor(org.springframework.messaging.simp.stomp.StompHeaderAccessor) StompHeaders(org.springframework.messaging.simp.stomp.StompHeaders) BinaryMessage(org.springframework.web.socket.BinaryMessage) Test(org.junit.jupiter.api.Test)

Aggregations

BinaryMessage (org.springframework.web.socket.BinaryMessage)5 StompHeaderAccessor (org.springframework.messaging.simp.stomp.StompHeaderAccessor)4 TextMessage (org.springframework.web.socket.TextMessage)4 Test (org.junit.jupiter.api.Test)3 Message (org.springframework.messaging.Message)2 StompCommand (org.springframework.messaging.simp.stomp.StompCommand)2 IOException (java.io.IOException)1 ByteBuffer (java.nio.ByteBuffer)1 Principal (java.security.Principal)1 BufferingStompDecoder (org.springframework.messaging.simp.stomp.BufferingStompDecoder)1 StompHeaders (org.springframework.messaging.simp.stomp.StompHeaders)1 PongMessage (org.springframework.web.socket.PongMessage)1 WebSocketMessage (org.springframework.web.socket.WebSocketMessage)1 SessionLimitExceededException (org.springframework.web.socket.handler.SessionLimitExceededException)1 SockJsSession (org.springframework.web.socket.sockjs.transport.SockJsSession)1