Заголовки фреймов

Фрейм STOMP состоит из команды, набора заголовков, пустой строки-разделителя и тела сообщения. Заголовки занимают ключевую роль в протоколе, поскольку именно через них передаётся вся метаинформация: маршрутизация, идентификация подписок, управление подтверждениями доставки и параметры контента.

Каждый заголовок в STOMP представляет собой строку вида:

ключ:значение

Между ключом и двоеточием пробел не является обязательным, но допускается. После двоеточия следует значение, которое интерпретируется как строка.

Набор заголовков располагается последовательно, по одному на строку. Завершение списка заголовков обозначается пустой строкой (LF LF), после которой начинается тело фрейма.

Ключевые свойства:

  • каждый заголовок — отдельная строка
  • формат строго текстовый
  • кодировка UTF-8
  • порядок заголовков сохраняется и может иметь значение для некоторых брокеров
  • пустые строки внутри заголовков недопустимы

Роль заголовков в жизненном цикле фрейма

Заголовки определяют поведение фрейма на всех этапах обработки:

  • маршрутизация сообщения к destination
  • управление подписками (id, ack)
  • подтверждение доставки (ack/nack механика)
  • управление ответами (receipt)
  • описание содержимого (content-type, content-length)
  • корреляция сообщений между запросом и ответом

Фактически STOMP рассматривает заголовки как единственный механизм расширения протокола без изменения его базовой структуры.

Обязательные и командно-зависимые заголовки

Разные команды требуют разных наборов заголовков.

CONNECT / STOMP

Для установления соединения используются заголовки:

  • accept-version — поддерживаемые версии протокола (например, 1.2)
  • host — виртуальный хост брокера
  • login / passcode — аутентификация (если используется)
  • heart-beat — параметры heartbeat

Пример:

CONNECT
accept-version:1.2
host:broker.example.com
heart-beat:10000,10000

SEND

Отправка сообщения требует указания маршрута:

  • destination — адрес назначения (очередь или топик)
  • content-type — тип содержимого (например, application/json)
  • content-length — длина тела сообщения в байтах (рекомендуется для бинарных данных)
  • persistent — флаг сохранения (если поддерживается брокером)

Пример:

SEND
destination:/queue/orders
content-type:application/json
content-length:45

SUBSCRIBE

Подписка на сообщения использует набор управляющих заголовков:

  • destination — канал подписки
  • id — уникальный идентификатор подписки
  • ack — режим подтверждения (auto, client, client-individual)

Пример:

SUBSCRIBE
destination:/topic/news
id:sub-001
ack:client-individual

MESSAGE

Фреймы доставки сообщений содержат метаданные брокера:

  • subscription — идентификатор подписки
  • message-id — уникальный ID сообщения
  • destination — исходный канал
  • content-type — тип данных
  • ack — иногда добавляется брокером для управления подтверждением

Пример:

MESSAGE
subscription:sub-001
message-id:msg-12345
destination:/topic/news
content-type:application/json

Заголовок destination

destination является ключевым механизмом маршрутизации.

Он определяет логический адрес доставки сообщения. Формат зависит от брокера, но чаще всего используется URI-подобная структура:

  • /queue/name — очередь
  • /topic/name — топик
  • /exchange/name — обменник (в системах типа RabbitMQ)

Особенности:

  • строковое значение
  • чувствителен к точному совпадению
  • не интерпретируется клиентом, только брокером

Заголовок id

Используется для идентификации подписки или сущности внутри соединения.

Применяется в:

  • SUBSCRIBE (идентификатор подписки)
  • UNSUBSCRIBE (снятие подписки)
  • ACK / NACK (связь с подпиской)

Особенность заключается в том, что id должен быть уникален в рамках одного соединения.

Заголовок ack

Определяет стратегию подтверждения получения сообщений:

  • auto — автоматическое подтверждение сразу после доставки
  • client — подтверждение вручную через ACK
  • client-individual — подтверждение каждого сообщения отдельно

Этот заголовок влияет на надёжность доставки и контроль обработки сообщений на клиенте.

Заголовок receipt

Механизм подтверждения выполнения команд брокером.

Если заголовок receipt присутствует, брокер обязан вернуть фрейм RECEIPT:

receipt:message-xyz

Это используется для операций:

  • SEND
  • SUBSCRIBE
  • UNSUBSCRIBE
  • DISCONNECT

Позволяет отслеживать гарантированное выполнение команды.

Заголовки content-type и content-length

content-type

Определяет формат тела сообщения:

  • text/plain
  • application/json
  • application/xml

Влияет на интерпретацию данных получателем, но не на сам протокол.

content-length

Указывает точное количество байт тела фрейма.

Критически важен при:

  • передаче бинарных данных
  • наличии символов LF в теле
  • потоковой обработке сообщений

При отсутствии может использоваться разделитель null-байта, но это менее надёжно.

Специальные заголовки протокола STOMP 1.2

heart-beat

Формат:

heart-beat:cx,cy

Где:

  • cx — интервал отправки клиентом
  • cy — интервал ожидания от сервера

Используется для поддержания соединения и обнаружения разрыва канала.

version negotiation (accept-version)

accept-version:1.0,1.1,1.2

Позволяет согласовать версию протокола между клиентом и брокером.

Правила экранирования заголовков

STOMP накладывает ограничения на допустимые символы в заголовках.

Запрещены:

  • символ новой строки
  • символ двоеточия в ключе
  • управляющие символы NULL

Для некоторых символов используется экранирование:

  • — перевод строки
  • возврат каретки
  • : — двоеточие в значениях
  •  — обратный слэш

Это особенно важно при использовании JSON внутри заголовков или сложных маршрутов.

Повторяющиеся заголовки

Протокол допускает повторение одного и того же ключа, однако поведение зависит от реализации брокера:

  • некоторые объединяют значения
  • некоторые используют последнее значение
  • некоторые игнорируют дубликаты

Поэтому в прикладной разработке рекомендуется избегать повторяющихся ключей.

Взаимосвязь заголовков и тела фрейма

Заголовки определяют, как интерпретируется тело:

  • content-type влияет на парсинг
  • content-length определяет границы тела
  • destination определяет маршрут доставки
  • ack определяет поведение подтверждения после обработки тела

Ошибки в заголовках приводят к:

  • разрыву соединения
  • игнорированию сообщения
  • некорректной маршрутизации
  • невозможности подтверждения доставки

Использование заголовков в STOMP.js

В STOMP.js заголовки формируются как обычные JavaScript-объекты:

client.publish({
  destination: "/queue/orders",
  headers: {
    "content-type": "application/json",
    "receipt": "msg-1"
  },
  body: JSON.stringify({ id: 10 })
});

При подписке:

client.subscribe("/topic/news", (message) => {
  console.log(message.headers);
});

Каждое сообщение в STOMP.js предоставляет доступ к headers как к словарю строк, где значения всегда приходят в виде строкового представления.

Семантическая нагрузка заголовков

Заголовки формируют уровень абстракции над WebSocket, превращая поток байтов в структурированную систему сообщений. Через них реализуются:

  • маршрутизация без знания транспортного уровня
  • управление состоянием подписок
  • подтверждение доставки на уровне приложения
  • расширяемость без изменения протокола

Именно за счёт заголовков STOMP остаётся минималистичным, но функционально расширяемым протоколом поверх WebSocket или TCP.