Перейти к основному содержимому
Версия: 5.9.0

Сетевые настройки

Сетевые настройки подключения к серверу описываются моделью ChatNetworkConfig.

Инициализация:

Конструктор по умолчанию создаёт модель со стандартными настройками:

var chatNetworkConfig = ChatNetworkConfig()

Можно настраивать отдельные компоненты подключения:

  • httpConfig: Настройки REST API
  • wsConfig: Настройки WebSocket API
  • sslPinning: Настройки SSL‑пиннинга
Мутации после инициализации SDK не применяются

ChatNetworkConfigstruct, передаётся внутрь ChatConfig по значению. После вызова ChatCenterUISDK(chatConfig:) SDK работает со своей копией; изменения chatConfig.networkConfig или его вложенных полей со стороны интегратора не действуют. Все поля задавайте до передачи ChatConfig в конструктор SDK. Подробнее — Настройки SDK.

Настройки REST API (HTTPConfig)

Параметры HTTP(S)-подключения для REST API:

ПолеТипОбязательныйОписание
connectionTimeoutTimeInterval (сек)Нет (default 30)Таймаут бездействия REST-запроса: применяется как URLRequest.timeoutInterval и покрывает все фазы (установление соединения + ожидание ответа). Относится только к REST; для WebSocket см. WSConfig.connectionTimeout
downloadTimeoutTimeInterval (сек)Нет (default 30)Таймаут бездействия запроса скачивания файла: применяется как URLRequest.timeoutInterval (та же семантика, что у connectionTimeout)
uploadTimeoutTimeInterval (сек)Нет (default 120)Применяется одновременно как URLSessionConfiguration.timeoutIntervalForRequest (idle-таймаут) и timeoutIntervalForResource (общий лимит на загрузку ресурса). Это значит: для default 120 суммарное время выгрузки файла не должно превышать 120 сек — для больших файлов по медленному каналу требуется увеличить значение. Upload-сессия SDK также жёстко настроена с waitsForConnectivity = true: при отсутствии сети запрос ожидает её восстановления, а не падает с ошибкой немедленно (REST и download — наоборот, падают сразу). Общее время ожидания всё равно ограничено uploadTimeout

Настройки WebSocket-подключения (WSConfig)

Параметры WS(S)-подключения для real-time-сообщений:

ПолеТипОбязательныйОписание
connectionTimeoutTimeInterval (сек)Нет (default 30)Таймаут на WebSocket upgrade-запрос: значение применяется и как URLSessionConfiguration.timeoutIntervalForRequest, и как URLRequest.timeoutInterval upgrade-запроса
sendTimeoutTimeInterval (сек)Нет (default 20)Используется одновременно как: (1) интервал таймера, который проверяет статусы неотправленных сообщений (таймер работает пока открыт экран чата и активен диалог; при уходе в фон останавливается); (2) порог, после которого сообщение в статусе .sending помечается как .failed при отсутствии ответа сервера. Если сервер вернул ответ с ошибкой по WebSocket, сообщение становится .failed сразу, минуя таймер. Гарантия «не позже чем через 2 × sendTimeout» относится только к timer-пути

Настройки SSL пиннинга

Один сертификат — single point of failure

При использовании данного функционала не рекомендуется устанавливать только один доверенный сертификат. В случае его отзыва или истечения срока действия SDK перестанет подключаться к серверу. Используйте дополнительные резервные сертификаты и своевременно обновляйте их.

Параметры SSLPinningConfig:

ПолеТипОбязательныйОписание
allowUntrustedSSLCertificateBoolНет (default false)При true SDK принимает любой сертификат сервера без проверки подписи. Небезопасно для продакшена — см. :::danger ниже. Игнорируется, если trustedCertificates непуст — pinning имеет приоритет
trustedCertificates[ChatSSLCertificate]Нет (default [])Список доверенных сертификатов в формате DER. При его задании SDK полностью обходит SecTrustEvaluateWithError — игнорируются hostname, срок действия и проверка отозванности (OCSP/CRL). Проверяется только, совпадает ли публичный ключ любого сертификата из TLS-цепочки сервера (leaf, промежуточный или root) с одним из ключей списка (public key pinning). Это позволяет пинить как leaf-сертификат, так и intermediate/root CA — последнее переживает ротацию leaf без обновления приложения
Небезопасно для продакшена

allowUntrustedSSLCertificate = true имеет эффект только при пустом trustedCertificates — и в этом случае SDK принимает любой сертификат сервера, что делает приложение уязвимым к атакам man-in-the-middle (MITM). При непустом trustedCertificates флаг игнорируется (pinning имеет приоритет), поэтому MITM-уязвимости через него нет. Тем не менее, не оставляйте allowUntrustedSSLCertificate = true в боевых сборках: если в одной из конфигураций сборки trustedCertificates окажется пустым (например, не были найдены файлы через Bundle.main.url(forResource:)), приложение лишится защиты без явного сигнала.

trustedCertificates заменяет системную валидацию TLS проверкой публичного ключа — это работает и с обычными CA-подписанными, и с самоподписанными сертификатами, и с сертификатами с истёкшим сроком действия.

Сертификат передается в виде модели ChatSSLCertificate, в которой указывается локальный путь до сертификата:

var chatNetworkConfig = ChatNetworkConfig()
if let mainCert = Bundle.main.url(forResource: "main_cert", withExtension: "cer"),
let backupCert = Bundle.main.url(forResource: "backup_cert", withExtension: "cer") {
chatNetworkConfig.sslPinning.trustedCertificates = [ChatSSLCertificate(contentsOf: mainCert), ChatSSLCertificate(contentsOf: backupCert)]
}

Формат сертификата

ChatSSLCertificate(contentsOf:) ожидает DER-кодированное содержимое — SDK передаёт байты из файла напрямую в SecCertificateCreateWithData, расширение не проверяется (традиционно используются .cer или .der, но .crt или файл без расширения тоже сработают, если содержимое валидно). PEM-сертификаты (-----BEGIN CERTIFICATE-----…) не поддерживаются — сконвертируйте перед добавлением в bundle:

openssl x509 -in cert.pem -outform DER -out cert.cer

Добавьте получившийся .cer в bundle приложения (Build Phases → Copy Bundle Resources). Загрузка через Bundle.main.url(forResource:withExtension:) вернёт nil, если файла нет — как в примере выше, обработайте это явно: молчаливое отсутствие сертификата эквивалентно отключению пиннинга.

Битый или PEM-сертификат блокирует все соединения

Если файл по contentsOf: существует, но не является валидным DER (например, в bundle случайно положили PEM, либо файл повреждён), ChatSSLCertificate сохранит внутренний certificate = nil и не выбросит ошибку из конструктора. SDK пишет сообщение об ошибке в свой лог-поток (видно при ChatLoggerConfig(logLevel: .all) — см. Настройки логирования), но в коде интегратора признака отказа нет.

Эффект отличается от случая «пустой массив trustedCertificates»: массив с битым ChatSSLCertificate не пуст, поэтому SDK всё равно входит в режим pinning. Битые элементы пропускаются циклом и не блокируют валидные: если в массиве есть хотя бы один валидный ChatSSLCertificate с совпадающим публичным ключом, соединение установится. Если же все элементы битые — SDK не находит совпадений и отклоняет все TLS-соединения. Внешне это выглядит как «сеть не работает», а не «pinning выключен».

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

Диагностика SSL-ошибок

Если соединение не открывается после включения пиннинга:

СимптомВозможная причинаЧто проверить
Соединение обрывается на handshake (в логе SDK сообщение SSL CERTIFICATES Pinning error - server and pinned certificates are not equals)Ни один публичный ключ из TLS-цепочки сервера не совпадает с публичными ключами trustedCertificatesВключить ChatLoggerConfig(logLevel: .all) и сравнить hex-значения строк SSL Server public key: (все сертификаты цепочки сервера) и SSL Pinned certificate public key: (приложенные) — хотя бы одна пара должна совпадать. SDK логирует ключи в raw-формате (SecKeyCopyExternalRepresentation: PKCS #1 для RSA, X9.63 для EC), поэтому сторонние команды вроде openssl x509 -pubkey | openssl pkey -pubin -outform DER | openssl dgst -sha256 дадут другой хеш (SubjectPublicKeyInfo) — для сравнения с SDK-логом такие команды не подходят
Подключение работало, потом перестало (без обновления приложения)Серверный сертификат был ротирован, в приложении нет новогоСвязаться с поддержкой edna; временный fallback — резервный сертификат, если он был добавлен заранее
nil после Bundle.main.url(forResource:)Файл не попал в Copy Bundle Resources или неверно разбиты имя/расширение (forResource: "cert.cer" вместо forResource: "cert", withExtension: "cer")Проверить target → Build Phases → Copy Bundle Resources (файл должен быть в списке); проверить, что в Bundle.main.url(forResource:withExtension:) имя файла передано без расширения, а расширение — вторым аргументом
Ошибка только в Release-сборкеATS-конфигурация для домена различается между Debug и Release (например, NSAllowsArbitraryLoads: true стоит только в Debug)Проверить NSAppTransportSecurity в Info.plist обеих сборок; при правильных серверных хостах (HTTPS с действительными сертификатами) спец-настройки ATS не требуются

Включите подробное логирование SDK (ChatLoggerConfig(logLevel: .all)) — сообщения о SSL-handshake пишутся в общий поток логов и приходят делегату ChatCenterUISDKDelegate через метод chatCenterUI(chatCenter:didLog:).

Ротация сертификатов

Серверный сертификат имеет срок действия. Чтобы избежать blackout приложения при ротации:

  1. Запросите расписание у поддержки edna — за сколько до истечения вы получите новый сертификат.
  2. Всегда держите минимум 2 сертификата в trustedCertificates: текущий + резервный (next). Когда edna ротирует серт, новый уже окажется в trust-листе приложения.
  3. При выкатке версии приложения убедитесь, что в bundle включён новый сертификат, до того как старый перестанет работать на сервере.
  4. Не используйте allowUntrustedSSLCertificate = true в продакшене даже временно как обходной путь — это сводит на нет защиту от MITM на всё время фикса.

Связанные разделы