OkHttp за пределами базовых запросов: прокси, DNS и свои интерсепторы

Для большинства Android-приложений OkHttp начинается с простого вызова newCall(request).execute(). Однако клиент позволяет гораздо глубже управлять сетевым взаимодействием: выбирать прокси и конкретный тип подключения, задавать собственный DNS-резолвер, изменять цепочку интерсепторов и настраивать проверку сертификатов.
Ниже — обзор таких возможностей и связанных с ними ограничений. Некоторые приемы подходят для специальных сценариев и экспериментов, но требуют осторожности в продакшене: ошибка в сетевой конфигурации может привести к утечке данных, неожиданному расходу трафика или полной потере соединения.
Прокси и выбор сетевого подключения
Прокси можно задать непосредственно при создании OkHttpClient. Для этого используется объект Proxy с адресом и портом сервера. OkHttp поддерживает как HTTP-, так и SOCKS-прокси. Если прокси явно не указан, клиент ориентируется на системные настройки устройства.
Отдельно можно принудительно выбрать сеть, через которую будут выполняться запросы. На Android приложение способно запросить мобильную сеть через ConnectivityManager, дождаться события доступности и передать полученный socketFactory в новый экземпляр OkHttpClient. Это позволяет направить трафик через cellular-соединение даже в ситуации, когда одновременно доступен Wi-Fi.
Такой подход полезен для тестирования и специализированных приложений, но может неприятно удивить пользователя расходом мобильного трафика. Кроме того, на фактический маршрут могут влиять VPN, корпоративные политики и другие настройки операционной системы, поэтому результат стоит проверять на реальных устройствах.
Собственный DNS-резолвер
OkHttp позволяет заменить системный механизм разрешения доменных имен через интерфейс Dns. Например, для отдельных хостов можно возвращать заранее известные IP-адреса, а для остальных использовать стандартный системный DNS. При ошибке собственного резолвера также можно предусмотреть переход к системной реализации.
Жестко заданные адреса подходят для локальной разработки, тестовых стендов и некоторых инфраструктурных сценариев. В рабочем приложении такой список быстро устаревает: адреса меняются, серверы масштабируются, а балансировка может перестать работать. Поэтому при использовании локальных соответствий необходим механизм обновления и безопасного отката.
Как устроена цепочка OkHttp
Обычный вызов проходит через несколько встроенных интерсепторов. RetryAndFollowUpInterceptor отвечает за повторные попытки и редиректы, BridgeInterceptor связывает запрос с заголовками и CookieJar, CacheInterceptor работает с кэшем, ConnectInterceptor занимается DNS, TCP и TLS-соединением, а CallServerInterceptor отправляет запрос и принимает заголовки с телом ответа.
Иногда возникает задача заменить сам сетевой движок, например использовать Cronet вместо стандартного транспорта. Теоретически можно форкнуть OkHttp и изменить внутреннюю реализацию вызова. Более простой, но менее очевидный вариант — добавить собственный интерсептор в начало цепочки, выполнить запрос самостоятельно и не вызывать chain.proceed(). В этом случае последующие встроенные интерсепторы не будут запущены.
Вложенные запросы и собственная политика повторов
Интерсептор может использовать другой экземпляр OkHttpClient и выполнять внутри себя дополнительный запрос. Это позволяет, например, получить токен, проверить состояние сессии или запросить служебные данные перед основным вызовом. При этом вложенный запрос запускает собственную цепочку интерсепторов, поэтому важно заранее исключить рекурсию и неожиданные повторные обращения.
Свою retry policy также можно реализовать в интерсепторе: после неудачного ответа закрыть предыдущий Response и снова вызвать chain.proceed(). Число попыток, условия повтора и задержки следует определять явно. У OkHttp уже есть собственные механизмы повторов, поэтому дополнительная логика способна привести к избыточному числу запросов, особенно если она установлена одновременно в application- и network-интерсепторах.
Блокирующие операции и таймауты
Синхронный интерфейс Interceptor не является suspend-контекстом. Если перед запросом нужно дождаться действия пользователя или другого асинхронного события, разработчики иногда используют runBlocking. Это блокирует поток, на котором выполняется сетевой вызов, и при неосторожном применении может привести к нехватке потоков или зависанию интерфейса. Безопаснее заранее вынести такие действия за пределы сетевой цепочки.
Таймаут применяется ко всему вызову, включая время, потраченное на ретраи и дополнительные операции. В исходном материале приводится способ принудительно остановить внутренний таймер через объект тайм-аута вызова. Это низкоуровневое вмешательство во внутреннюю реализацию OkHttp, поэтому его нельзя считать штатным способом настройки клиента: оно может нарушить ожидаемое поведение отмены и привести к зависшим запросам.
Проверка SSL-сертификатов
Через собственный X509TrustManager можно реализовать дополнительные правила проверки TLS. Среди вариантов — разрешать только определенные сертификаты, блокировать сертификаты по SHA-256-хэшу или внедрять собственную проверку цепочки доверия. Такой механизм может использоваться как часть certificate pinning или для оперативной блокировки скомпрометированного сертификата.
Главный риск — случайно ослабить стандартную проверку и разрешить лишние сертификаты. Важно валидировать всю цепочку, а не только конечный сертификат, и отдельно проверять поведение отладочных и релизных сборок. Любые изменения SSL-логики должны проходить аудит безопасности и иметь возможность быстрого отката.
Итоги
OkHttp закрывает большинство повседневных задач и предоставляет достаточно точек расширения для нестандартных сценариев. При этом у библиотеки остаются ограничения: на момент описания она не поддерживает HTTP/3, не предлагает гибкий готовый механизм настройки retry policy и не поддерживает корутины непосредственно в интерсепторах.
Дополнительную сложность создает различие между application- и network-интерсепторами, а также отсутствие явного разделения на request- и response-интерсепторы, знакомого по некоторым другим сетевым движкам. Поэтому расширять OkHttp стоит постепенно: сначала использовать штатные настройки, затем добавлять изолированные эксперименты под feature flag и обязательно оставлять безопасный путь возврата к стандартному поведению.

