SplitHTTP (H2, QUIC H3)

v1.8.16+

Используется для загрузки с помощью HTTP-фрагментированной передачи, загрузка осуществляется с помощью нескольких HTTP POST-запросов.

Может использоваться через CDN, не поддерживающие WebSocket, но есть несколько требований:

  • CDN должен поддерживать HTTP-фрагментированную передачу и потоковые ответы без буферизации. Ядро будет отправлять различную информацию, чтобы сообщить CDN об этом, но CDN должна ее соблюдать. Если промежуточный узел не поддерживает потоковые ответы и зависает, этот транспорт, скорее всего, не будет работать.

Цель та же, что и у V2fly Meek, но благодаря использованию фрагментированной загрузки скорость загрузки выше, а скорость отдачи оптимизирована, но все еще очень ограничена, поэтому к HTTP-прокси предъявляются более высокие требования (см. выше).

SplitHTTP также принимает заголовок X-Forwarded-For.

SplitHttpObject

SplitHttpObject соответствует элементу splithttpSettings в конфигурации транспорта.

{
  "path": "/",
  "host": "xray.com",
  "headers": {
    "key": "value"
  },
  "scMaxEachPostBytes": 1000000,
  "scMaxConcurrentPosts": 100,
  "scMinPostsIntervalMs": 30,
  "noSSEHeader": false,
  "xPaddingBytes": "100-1000",
  "xmux": {
    "maxConcurrency": 0,
    "maxConnections": 0,
    "cMaxReuseTimes": 0,
    "cMaxLifetimeMs": 0
  }
}

path: string

Путь HTTP-протокола, используемый SplitHTTP. Значение по умолчанию — "/".

host: string

Хост, отправляемый в HTTP-запросе SplitHTTP. Значение по умолчанию пустое. Если значение на сервере пустое, значение хоста, отправляемое клиентом, не проверяется.

Если значение указано на сервере или в headers, оно будет сравниваться со значением хоста в запросе клиента.

Приоритет выбора хоста для отправки клиентом: host > headers > address.

headers: map {string: string}

Только для клиента. Пользовательские HTTP-заголовки. Пара «ключ-значение», где каждый ключ представляет собой имя HTTP-заголовка, а соответствующее значение — строка.

scMaxEachPostBytes: int | string

Максимальный размер блока выгрузки в байтах. Значение по умолчанию — 1000000 (1 МБ).

Размер, установленный на клиенте, должен быть меньше этого значения, иначе запрос POST, размер которого превышает значение, установленное на сервере, будет отклонен.

Это значение должно быть меньше максимального размера тела запроса, разрешенного CDN или другим обратным прокси-сервером HTTP, иначе будет выдаваться ошибка HTTP 413.

Также может быть строкой в формате "500000-1000000", и ядро будет случайным образом выбирать значение из этого диапазона для уменьшения цифрового следа.

scMaxConcurrentPosts: int | string

Максимальное количество одновременных запросов POST на одно соединение. Значение по умолчанию — 100.

Параллельная выгрузка также (и в основном) контролируется параметром scMinPostsIntervalMs, поэтому это значение является лишь страховкой.

Фактическое количество запросов, отправляемых клиентом, должно быть меньше, чем на сервере. (На практике, поскольку указанного выше ограничения трудно достичь, клиент может фактически установить значение, превышающее значение на сервере, но это не рекомендуется).

Также может быть строкой в формате "50-100", и ядро будет случайным образом выбирать значение из этого диапазона для уменьшения цифрового следа.

scMinPostsIntervalMs: int | string

Только для клиента. Минимальный интервал между запросами POST на выгрузку. Значение по умолчанию — 30.

Также может быть строкой в формате "10-50", и ядро будет случайным образом выбирать значение из этого диапазона для уменьшения цифрового следа.

noSSEHeader: bool

Только для сервера. Не отправлять заголовок ответа Content-Type: text/event-stream. Значение по умолчанию — false (то есть заголовок будет отправлен).

xPaddingBytes: int | string

Задает размер заполнения для запросов (исходящих) и ответов (входящих), используемый для уменьшения отпечатка запроса. Единица измерения: байты. Значение по умолчанию: "100-1000". При каждом запросе случайным образом выбирается число из этого диапазона. Тип: Int32Range

Значение -1 полностью отключает заполнение.

xmux: XmuxObject

XmuxObject

v24.9.19+

Только для клиента. Позволяет пользователю контролировать поведение многопоточности SplitHTTP в h2 и h3. Не используйте данную функцию вместе с mux.cool.

{
  "maxConcurrency": "16-32",
  "maxConnections": 0,
  "cMaxReuseTimes": "64-128",
  "cMaxLifetimeMs": 0
}

При установке всех значений в 0 или их отсутствии, ядро заполнит их значениями по умолчанию.

Объяснение терминов:

  • Потоки будут мультиплексироваться в физические соединения, например: Соединение 1 (Поток 1, Поток 2, Поток 3) Соединение 2 (Поток 4, Поток 5, Поток 6) ... и так далее. В других источниках вы можете встретить описание "соединение-подключение", это то же самое.
  • Все следующие поля имеют тип Int32Range:

maxConcurrency: int/string

Значение по умолчанию — 0 (неограниченно). Максимальное количество потоков, мультиплексируемых в одном соединении. Когда количество потоков в соединении достигает этого значения, ядро создает дополнительные соединения для размещения новых потоков, аналогично параметру concurrency в mux.cool.

maxConnections: int/string

Значение по умолчанию — 0 (неограниченно). Максимальное количество открытых соединений. Ядро будет активно открывать новые соединения для каждого потока до тех пор, пока не будет достигнуто это значение. Затем ядро начнет мультиплексировать потоки в уже установленные соединения. Конфликтует с maxConcurrency.

cMaxReuseTimes: int/string

Значение по умолчанию — 0 (неограниченно). Максимальное количество раз, которое соединение может быть использовано повторно. По достижении этого значения ядро больше не будет назначать потоки этому соединению, и оно будет разорвано после закрытия последнего внутреннего потока.

cMaxLifetimeMs: int/string

Значение по умолчанию — 0 (неограниченно). Максимальное время "жизни" соединения. По истечении этого времени ядро больше не будет назначать потоки этому соединению, и оно будет разорвано после закрытия последнего внутреннего потока.

Версия HTTP

Поведение клиента

По умолчанию клиент будет использовать http/1.1, если TLS не включен, и h2, если TLS включен.

Если TLS включен, можно указать конкретную версию HTTP (http/1.1, h2, h3) в массиве alpn в настройках TLS (работает только в том случае, если массив содержит только один элемент, если указано несколько элементов, будет использоваться поведение по умолчанию).

Поведение сервера

По умолчанию сервер будет прослушивать TCP-порт и обрабатывать трафик http/1.1 и h2.

Если TLS включен, можно указать h3 в массиве alpn в настройках TLS. В этом случае сервер будет прослушивать UDP-порт и обрабатывать трафик h3.

Советы

Поскольку этот протокол основан на стандартных HTTP-запросах, он нечувствителен к преобразованию версий HTTP, и различные промежуточные узлы могут преобразовывать версии HTTP.

Например, если вы хотите использовать h3 для подключения к Cloudflare, но Cloudflare не будет использовать h3 для обратного подключения, а будет использовать http/1.1 или h2, то на клиенте alpn должен быть установлен в h3, а на сервере — нет, поскольку запросы, отправляемые на сервер, не будут использовать h3.

Browser Dialer

При использовании HTTPS этот транспорт также поддерживает Browser Dialer.

Подробности протокола

Подробное обсуждение см. в #3412Открыть в новой вкладке и #3462Открыть в новой вкладке. Ниже приведено краткое описание и требования к совместимости реализации:

  1. Загрузка начинается с запроса GET /<UUID>. Сервер немедленно отвечает 200 OK и Transfer Encoding:chunked и сразу же отправляет двухбайтовую полезную нагрузку, чтобы заставить HTTP-промежуточные узлы сбросить заголовки.

На данный момент сервер отправляет следующие заголовки:

  • X-Accel-Buffering: no — отключает буферизацию.
  • Content-Type: text/event-stream — отключает буферизацию на некоторых промежуточных узлах, можно отключить с помощью опции "noSSEHeader".
  • Transfer-Encoding: chunked — кодировка передачи данных, используется только в HTTP/1.1.
  • Cache-Control: no-store — отключает любое возможное кэширование ответов.
  1. Выгрузка начинается с запроса POST /<UUID>/<seq>. seq работает аналогично порядковому номеру TCP, начиная с 0. Пакеты данных могут отправляться одновременно, сервер должен пересобрать данные в соответствии с порядковым номером. Порядковый номер не должен сбрасываться.

    Клиент может открывать запросы на выгрузку и загрузку в любом порядке, любой из них может инициировать сеанс, но соединение GET должно быть открыто в течение 30 секунд, иначе сеанс будет разорван.

  2. Запрос GET будет оставаться открытым до тех пор, пока соединение не будет разорвано. Как сервер, так и клиент могут закрыть соединение. Конкретное поведение зависит от версии HTTP.

Рекомендации:

  • Не ожидайте, что CDN будет правильно передавать все заголовки. Цель этого протокола — обойти CDN, которые не поддерживают WS, а такие CDN обычно ведут себя не очень хорошо.

  • Следует предполагать, что все HTTP-соединения не поддерживают потоковые запросы, поэтому размер каждого пакета, отправляемого по исходящему соединению, должен основываться на задержке, пропускной способности и ограничениях самого промежуточного узла (аналогично MTU и алгоритму Нейгла в TCP).