HTTP-запрос подписки
Реализация: SubscriptionFetcher в SharXCore. Тип запроса — GET через URLSession.shared.data(for:), таймаут 60 секунд, HTTPS ожидается (но не принуждается; TLS выполняет OC).
Заголовки запроса
Все заголовки, которые клиент присылает панели:
| Заголовок | Значение | Кто задаёт | Обязателен? |
|---|---|---|---|
Accept |
*/* |
SubscriptionFetcher.fetchDetails |
да |
User-Agent |
SharXConnect/<version>/<platform>/<build> |
SubscriptionFetcher.defaultUserAgent |
да |
x-hwid |
Стабильный ID устройства (hex, 32 симв.) | SubscriptionDeviceHeaders |
да |
x-device-os |
iOS | macOS | Unknown |
SubscriptionDeviceHeaders |
да |
x-ver-os |
Версия ОС (18.2, 26.0.1) |
SubscriptionDeviceHeaders |
да |
x-device-model |
Маркетинговое имя (iPhone 14 Pro Max, iPad Pro 11-inch (M4)), на симуляторе — из SIMULATOR_MODEL_IDENTIFIER; если идентификатор не опознан — сам hw.machine |
SubscriptionDeviceHeaders.deviceModelName |
да |
X-SharX-Refresh-Planned-Seconds |
Секунды до следующего автообновления | SubscriptionRefreshService.refresh |
только если на подписке включено автообновление |
Accept: */*
Клиент намеренно не просит text/html: многие панели при таком Accept отдают SPA вместо сырого списка узлов. Если тело начинается с <!DOCTYPE / <html, клиент отвергает ответ ошибкой receivedMarkupInsteadOfSubscription.
User-Agent
Формат: SharXConnect/<shortVersion>/<platform>/<build>.
<shortVersion>—CFBundleShortVersionString(fallback1.0).<platform>—ios|macos|tvos|watchos|apple.<build>—CFBundleVersion(fallback1).
Пример: SharXConnect/1.0/ios/42.
Рекомендация для панелей: подставляйте шаблон по префиксу SharXConnect/, не по полной строке — версии и билды меняются с релизами.
x-hwid и HWID-заголовки
Стандартное поле панелей с HWID-лимитом (Remnawave и совместимые). Идентификатор создаётся один раз и хранится в Keychain; сохраняется при переустановке приложения и шэрится между таргетами (main app, PacketTunnel, виджеты) через Access Group.
Документация Remnawave: HWID device limit.
Ответ панели при превышении лимита. Remnawave 2.7.5+ возвращает HTTP 200, но ставит заголовок x-hwid-max-devices-reached: true (и x-hwid-limit: true для обратной совместимости с v2RayTun). SubscriptionFetcher распознаёт это до кода ответа и бросает SubscriptionFetchError.deviceLimitReached, а UI показывает локализованное сообщение с подсказкой удалить одно из устройств в личном кабинете панели или попросить администратора увеличить лимит. REST-эндпоинт самой панели при попытке добавить новое устройство возвращает код ошибки A099 («User hwid device limit reached», HTTP 400) — SharX этим эндпоинтом не пользуется, но в логах панели эта ошибка коррелирует с теми же флагами.
X-SharX-Refresh-Planned-Seconds
Оповещение панели о предполагаемом интервале до следующего запроса (в секундах). Помогает панели планировать rate-limit. Значение совпадает с entity.effectiveAutoRefreshIntervalSeconds. Заголовок не отправляется, если пользователь выключил автообновление на подписке.
Заголовки ответа
Клиент читает заголовки без учёта регистра имён и извлекает значения, перечисленные в SubscriptionBodyDirectives.knownKeys. Тот же набор ключей одновременно поддерживается как #key: value-директивы в начале тела подписки — см. subscription-body-and-routing.md.
Приоритет: если один и тот же ключ пришёл и в HTTP-заголовке, и в body-директиве — побеждает HTTP-заголовок (он более авторитетный для клиента; SubscriptionRefreshService.refresh делает merged = body.merging(headers) { $1 }).
Панель → клиент: тема интерфейса (sharx-color-scheme, sharx-accent-palette)
Панель может передать в приложение светлую/тёмную схему и акцентную палитру при каждом успешном обновлении подписки:
- В заголовках ответа (имена регистронезависимы):
sharx-color-scheme,sharx-accent-palette. - Либо в начале тела как директивы:
#sharx-color-scheme: …/#sharx-accent-palette: …(те же правила, что у остальных#key: value— см. subscription-body-and-routing.md).
Допустимые значения (в нижнем регистре; сравнение без учёта регистра):
sharx-color-scheme:automatic,light,dark.sharx-accent-palette:system,aurora,midnight,sakura,sunset,graphite,matrix,crimson.
Они записываются в UserDefaults.standard под ключами sharxColorSchemePreference и sharxAccentPalette (как @AppStorage в Theme.swift), интерфейс подхватывает при следующем отображении. Невалидное значение игнорируется. Если ключа нет, клиент не сбрасывает эту настройку (остаётся прежний выбор пользователя или прошлое значение с панели).
Для inline-подписки (вставка share-body без http) ведущие строки #sharx-… тоже разбираются и применяются.
Реализация: SubscriptionPanelThemeDirectives.applyFromMergedDirectives.
Типичные ошибки
| Ошибка | Когда |
|---|---|
SubscriptionFetchError.invalidURL |
sourceURLString не парсится как URL |
SubscriptionFetchError.httpFailure(code) |
HTTP-статус ≠ 2xx |
SubscriptionFetchError.emptyResponseBody |
HTTP 200 и 0 байт тела (пустой шаблон на панели, Fallback без XRAY_BASE64 и т.п.) |
SubscriptionFetchError.deviceLimitReached |
Remnawave прислал x-hwid-max-devices-reached: true или x-hwid-limit: true — превышен лимит HWID |
SubscriptionFetchError.network(error) |
Транспорт: таймаут, DNS, нет сети и т.д. |
SubscriptionParseError.receivedMarkupInsteadOfSubscription |
Тело начинается с HTML (<!DOCTYPE, <html) |