WebSocket handshake: заголовок Upgrade

WebSocket handshake: заголовок Upgrade

WebSocket — не просто HTTP с другим методом, это отдельный протокол. Но для его установки используется HTTP — хитрый трюк, который позволяет WebSocket'ам работать через те же порты (80/443) и с теми же прокси, что и обычный веб-трафик. Разберём, как HTTP-соединение «повышается» до WebSocket.

От HTTP к WebSocket через Upgrade

Клиент начинает с обычного HTTP-запроса, но добавляет специальные заголовки, означающие «я хочу переключиться на WebSocket»:

GET /chat HTTP/1.1
Host: example.com
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==
Sec-WebSocket-Version: 13

Это не REST-запрос и не запрос страницы. Это запрос на смену протокола. Метод всегда GET, URL — эндпоинт WebSocket-сервера.

Ключевые заголовки:

  • Upgrade: websocket — «хочу переключиться с HTTP на WebSocket».
  • Connection: Upgrade — обязательный спутник Upgrade, по правилам HTTP.
  • Sec-WebSocket-Key — случайная строка в base64 (16 байт). Нужна для доказательства, что сервер понимает WebSocket, а не случайный HTTP-сервер.
  • Sec-WebSocket-Version — версия протокола (всегда 13 в современных браузерах).

Ответ сервера: 101 Switching Protocols

Если сервер поддерживает WebSocket по этому URL, он отвечает не 200 OK, а специальным статусом:

HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=

101 Switching Protocols означает: «понял, переключаемся, дальше говорим на WebSocket». Важно: этот ответ не может быть сгенерирован обычным веб-приложением на Node.js/Python — его формирует сам WebSocket-сервер.

Заголовок Sec-WebSocket-Accept — доказательство того, что сервер действительно понял запрос. Он вычисляется по алгоритму:

Sec-WebSocket-Accept = base64(SHA1(Sec-WebSocket-Key + "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"))

Магическая строка 258EAFA5-E914-47DA-95CA-C5AB0DC85B11 — константа из спецификации RFC 6455. Она гарантирует, что сервер понимает именно WebSocket, а не какой-то другой Upgrade-протокол. Браузер проверяет этот хэш — и если не совпадает, соединение разрывается.

После handshake: WebSocket-фреймы

Как только сервер отправил 101, TCP-соединение больше не использует HTTP. Теперь данные передаются в виде WebSocket-фреймов — компактных бинарных пакетов:

WebSocket-фрейм:
┌─────┬─────┬──────────┬───────────┬──────────┐
│ FIN │opcode│ Masking │  Payload   │  Masking │
│ (1b)│ (4b) │ Key (4B)│ Length (7b)│  Key     │
└─────┴─────┴──────────┴───────────┴──────────┘
  + Payload Data (сами данные)

Фрейм содержит:

  • FIN — последний ли фрейм сообщения (сообщение может быть разбито на несколько фреймов).
  • Opcode — тип данных: text (UTF-8), binary (бинарные), ping, pong, close.
  • Masking key — ключ для XOR-маскировки. Все фреймы от клиента к серверу ОБЯЗАНЫ быть маскированы — это защита от атак через прокси-кэши.
  • Payload — сами данные.

Overhead WebSocket-фрейма — 2–10 байт (против ~400+ байт HTTP-заголовков). Именно это делает WebSocket эффективным для real-time.

Ping/Pong: keepalive для WebSocket

WebSocket-соединение может висеть открытым часами. Чтобы проверить, что клиент ещё жив (или что прокси не разорвал idle-соединение), используется механизм Ping/Pong:

Сервер:  Ping-фрейм (opcode 9)
Клиент:  Pong-фрейм (opcode 10)

Это встроено в протокол — не нужно слать кастомные сообщения «ты жив?». Большинство WebSocket-библиотек делают это автоматически.

Закрытие соединения

Закрыть WebSocket можно с любой стороны. Отправитель шлёт Close-фрейм (opcode 8) с кодом причины:

Клиент:  Close-фрейм (code=1000, "Normal Closure")
Сервер:  Close-фрейм (code=1000)

Коды закрытия стандартизованы:

  • 1000 — нормальное закрытие.
  • 1001 — клиент уходит (закрыл вкладку).
  • 1006 — соединение оборвалось без close-фрейма (сеть пропала).
  • 1011 — внутренняя ошибка сервера.

Проверь себя

  1. Какой HTTP-статус отвечает сервер при успешном WebSocket handshake?
  2. Зачем нужна магическая строка 258EAFA5-... в вычислении Sec-WebSocket-Accept?
  3. Зачем нужны Ping/Pong-фреймы?
<details> <summary>Ответы</summary>
  1. 101 Switching Protocols — не 200 OK. Это означает «переключаемся на другой протокол».
  2. Это гарантия, что сервер понимает именно WebSocket (RFC 6455), а не какой-то другой Upgrade-протокол. Без этой строки обычный HTTP-сервер мог бы случайно вычислить правильный хэш.
  3. Чтобы проверять, что соединение всё ещё активно. Прокси могут разрывать idle TCP-соединения, а ping/pong поддерживают активность на уровне протокола.
</details>

Что унести с урока

  • WebSocket начинается как HTTP GET с Upgrade: websocket → 101 Switching Protocols.
  • После handshake TCP-соединение переключается на WebSocket-фреймы (2–10 байт overhead).
  • Все фреймы от клиента маскируются XOR-ключом — защита от атак через прокси.
  • Ping/Pong поддерживают соединение активным, Close-фреймы корректно закрывают.

В следующем уроке разберём протоколы ws:// и wss:// — как WebSocket работает поверх TCP и TLS, и в чём отличие от HTTP.

Попробуйте интерактивную версию

Практические задачи, квизы и AI-наставник — бесплатный старт без карты

Перейти к практике