Spring STOMP Неполный кадр

Я создал веб-сокет, используя STOMP внутри Spring. Конечная точка работает как шарм при использовании с библиотеками javascript, однако, когда я использую любое из простых расширений google chrome для веб-сокетов (например, простой клиент веб-сокета, умный клиент веб-сокета, клиент веб-сокета), spring выдает сообщение «Неполное сообщение содержимого кадра STOMP». код, я смог увидеть, что причина этого в том, что я не могу вставить нулевой символ / u0000 с помощью любого из этих инструментов. Я предполагаю, что все фреймворки java-скриптов делают это по умолчанию. Кто-то нашел обходной путь для этого, поэтому что я могу использовать любой клиент веб-сокета с Spring STOMP?

Код Stomp находится здесь: https://github.com/spring-projects/spring-framework/blob/master/spring-messaging/src/main/java/org/springframework/messaging/simp/stomp/StompDecoder.java

В [в настоящее время] строках 308-320 существует приведенный ниже код. Этот метод возвращает null, поскольку byteBuffer.remaining не превышает длину содержимого (оба равны 0). Существует исключение StompSubProtocolHandler, которое срабатывает впоследствии. Я попытался изучить все обработчики и перехватчики, но, похоже, нет способа перехватывать вещи на этом уровне, не переписывая почти все. Я хотел просто ввести "\0" в полезную нагрузку...

if (contentLength != null && contentLength >= 0) {
        if (byteBuffer.remaining() > contentLength) {
            byte[] payload = new byte[contentLength];
            byteBuffer.get(payload);
            if (byteBuffer.get() != 0) {
                throw new StompConversionException("Frame must be terminated with a null octet");
            }
            return payload;
        }
        else {
            return null;
        }
    }

person GSUgambit    schedule 19.11.2017    source источник


Ответы (1)


У меня была точно такая же проблема, я тестировал с клиентом веб-сокета.

Чтобы иметь возможность протестировать STOMP вручную в моей локальной среде, я настроил контекст Spring. Таким образом, мне не нужно добавлять нулевой символ на стороне клиента. Он автоматически добавляется, если он не существует.

Для этого в классе AbstractWebSocketMessageBrokerConfigurer я добавил:

@Override
public void configureWebSocketTransport(WebSocketTransportRegistration registration) {
    registration.addDecoratorFactory(new WebSocketHandlerDecoratorFactory() {
        @Override
        public WebSocketHandler decorate(WebSocketHandler webSocketHandler) {
            return new EmaWebSocketHandlerDecorator(webSocketHandler);
        }
    });
}

Декоратор автоматически добавляет возврат каретки, когда нет тела запроса (например, команда подключения).

/**
 * Extension of the {@link WebSocketHandlerDecorator websocket handler decorator} that allows to manually test the
 * STOMP protocol.
 *
 * @author Sebastien Gerard
 */
public class EmaWebSocketHandlerDecorator extends WebSocketHandlerDecorator {

    private static final Logger logger = LoggerFactory.getLogger(EmaWebSocketHandlerDecorator.class);

    public EmaWebSocketHandlerDecorator(WebSocketHandler webSocketHandler) {
        super(webSocketHandler);
    }

    @Override
    public void handleMessage(WebSocketSession session, WebSocketMessage<?> message) throws Exception {
        super.handleMessage(session, updateBodyIfNeeded(message));
    }

    /**
     * Updates the content of the specified message. The message is updated only if it is
     * a {@link TextMessage text message} and if does not contain the <tt>null</tt> character at the end. If
     * carriage returns are missing (when the command does not need a body) there are also added.
     */
    private WebSocketMessage<?> updateBodyIfNeeded(WebSocketMessage<?> message) {
        if (!(message instanceof TextMessage) || ((TextMessage) message).getPayload().endsWith("\u0000")) {
            return message;
        }

        String payload = ((TextMessage) message).getPayload();

        final Optional<StompCommand> stompCommand = getStompCommand(payload);

        if (!stompCommand.isPresent()) {
            return message;
        }

        if (!stompCommand.get().isBodyAllowed() && !payload.endsWith("\n\n")) {
            if (payload.endsWith("\n")) {
                payload += "\n";
            } else {
                payload += "\n\n";
            }
        }

        payload += "\u0000";

        return new TextMessage(payload);
    }

    /**
     * Returns the {@link StompCommand STOMP command} associated to the specified payload.
     */
    private Optional<StompCommand> getStompCommand(String payload) {
        final int firstCarriageReturn = payload.indexOf('\n');

        if (firstCarriageReturn < 0) {
            return Optional.empty();
        }

        try {
            return Optional.of(
                    StompCommand.valueOf(payload.substring(0, firstCarriageReturn))
            );
        } catch (IllegalArgumentException e) {
            logger.trace("Error while parsing STOMP command.", e);

            return Optional.empty();
        }
    }
}

Теперь я могу выполнять такие запросы, как:

CONNECT
accept-version:1.2
host:localhost
content-length:0


SEND
destination:/queue/com.X.notification-subscription
content-type:text/plain
reply-to:/temp-queue/notification

hello world :)

Надеюсь это поможет.

S.

person sebge2    schedule 06.04.2018