Проект "Unity Proxy"
Продолжаю рассказывать про наши интересные реализованные проекты. В этот раз представляю вашему вниманию проект 2018го года, из категории «самый полный Пэ» — за такое беремся только мы.
Полный Пэ
Такие проекты это причина, по которой я до сих пор беру заказы с открытого рынка — с бирж фриланса, потому что они позволяют просраться даже опытным профессионалам.
Если вы зазвездились и начали считать себя настоящим гением, гуру и профи в одном лице — описываемая жесть отлично поставит вас на место.
Да и вообще подобные проекты неплохо бодрят и не позволяют вконец отупеть от очередного корпоративного формошлепства.
Начнем с описания этого интересного проекта.
У заказчика были два веселых гуся готовых приложения для iOS и Android, созданных на базе Unity, которые с помощью технологий дополненной реальности позволяли встраивать трехмерные объекты в видеопоток с камеры телефона.
В работе эта неведомая хтонь выглядела как-то так:
Все работало бы замечательно и дальше, если бы не родной РКН, который в тот год объявил джихад и кровную месть Телеграму и в попытках его заблокировать порубил половину тогдашнего рунета.
Если кто забыл, в 2018 м постоянно падали банковские приложения, хостинги, онлайн-казиносервисы — пока сотрудники РКН изучали как работают CDN-серверы и геораспределенные зеркала, они раз за разом блочили абсолютно левые сервисы, просто оказавшиеся соседями по провайдеру или подсети.
Замечательное приложение с дополненной реальностью тоже задело банхаммером РКН:
под раздачу попали сервера Vuforia, которые использовались для определения точки привязки объекта в видеопотоке.
Обратите внимание на картинку с текстурой камней в видео выше — это и есть та самая точка привязки, используемая для позиционирования.
Суммируя получалось следующее:
Проект на Unity, который использует Vuforia SDK, из которого собираются два мобильных приложения Android и iOS, которые делают вызовы к удаленному закрытому серверу (точнее к нескольким) для своей работы.
Вся логика взаимодействия с серверами Vuforia является закрытой, реализация частично нативная.
И вот вся эта радость в один момент была заблокирована РКН.
Перенастроить адреса серверов нельзя (они зашиты в библиотеку), сделать эмулятор — времени не хватит.
А задача очень простая (как обычно):
Примерно как у Бетмена, разве что в небо прожектором не светили.
Ну что, взялись бы за такой проект?
Изучение
Первым делом было развернуто тестовое приложение с Vuforia на чистом Android, собрано и развернуто на реальном смартфоне — на эмуляторе оно ввиду своей специфики не работало.
Затем через отладчик началось изучение протокола взаимодействия:
Отладка показала самое важное:
протокол взаимодействия между приложением и серверами Vuforia основан на https и json
А забравшись внутрь нативной части, увидел что используются стандартные системные классы для работы с http-протоколом:
Эти два факта в сумме и дали возможное решение:
программная установка прокси-сервера для системных вызовов ДО начала работы Vuforia.
Технически, примерно такой код надо вызывать до момента запуска Vuforia:
String url = "http://www.google.com/",
proxy = "proxy.mydomain.com",
port = "8080";
URL server = new URL(url);
Properties systemProperties = System.getProperties();
systemProperties.setProperty("http.proxyHost",proxy);
systemProperties.setProperty("http.proxyPort",port);Сказано — сделано, вот так выглядел первый работающий прототип, с проксированием вызовов к серверам Vuforia из тестового приложения на Android:
реализовать подобное для iOS-версии и затем выдать готовое решение уже на Unity.
Плюс реализовать всю обвязку по настройке, чтобы не зашивать адрес прокси в код приложения
Оборудование
Отдельной подставой в тот момент был выход из строя сразу двоих моих рабочих ноутбуков, еще и по серьезным причинам, требующим длительного ремонта.
Пока они были в ремонте, завершать проект пришлось на полных «дровах»:
i5 и 8Гб памяти (всего), на которых помимо основной ОС крутилась виртуальная машина с MacOS, средой разработки XCode, Unity и эмулятором iOS.
Если кто вдруг не знает, то для нормальной разработки под iOS на XCode рекомендуется 32Гб памяти, разумеется на реальном устройстве.
А ведь был еще Android и Unity.
Было весело, зато с тех времен остался вот этот эпичный скриншот, которым я ныне пугаю современных разработчиков:
Как только получилось пропихнуть прокси, была запущена специальная отладочная утилита, позволяющая в реальном времени отслеживать все проходящие через прокси запросы, называется AnyProxy.
Вот так выглядел первый запуск тестового приложения через управляемый прокси:
Тут сразу стало видно проблему:
запросы к домену dp.qcarsdk.com не проходят уже со стороны прокси
Как раз из-за угара РКН в тот год.
Вот так все выглядело целиком в динамике, запущенное тестовое приложение в эмуляторе и отображение запросов прокси:
У AnyProxy была встроенная возможность каскада, когда запрос к прокси отправляется не напрямую а в следующий прокси.
Именно это я и использовал, подключив каскадом локальный прокси, запускаемый Tor Browser.
Чем наконец обеспечил работоспособность (обратите внимание на трехмерный чайник):
Следующим этапом началась разработка модуля для Unity, в рамках которого уже решалась задача с проксированием запросов для iOS версии.
Принцип включения прокси был аналогичен Android-версии:
вызов специального системного метода для установки прокси для всех вызовов HTTP/HTTPS
Unity позволяет добавлять в проект части кода на Objective-C для iOS и Java — для Android, хотя основую логику (включая интерфейс настройки) пришлось писать на C# — основной язык Unity.
Вот так выглядит нативная часть на Objective-C, непосредственно устанавливающая прокси:
extern "C" int DoSetupUpdate(const char* paramHost,const int paramPort) {
NSString* proxyHost = CreateNSString(paramHost);
NSNumber* proxyPort = [NSNumber numberWithInt: paramPort];
NSLog(@"update proxy for IOS %@ : %@",proxyHost,proxyPort);
[LLHTTPProxyURLProtocol setProxyParams: proxyHost pPort:proxyPort];
}Класс LLHTTPProxyURLProtocol был взят отсюда.
Вот так выглядело связывание с C# (Mono) в Unity:
[DllImport ("__Internal")]
private static extern int DoSetupUpdate (string paramHost,int paramPort);Ну и общая логика установки прокси со стороны модуля Unity:
/// <summary>
/// метод обновляет настройки прокси,
/// логика работы отличается от начальной установки прокси
/// </summary>
/// <param name="ps">новые параметры прокси</param>
public void Setup(ProxySettings ps)
{
ps.SaveToPlayerPrefs();
if (Application.platform == RuntimePlatform.Android) {
SetupForAndroid (ps);
} else if (Application.platform == RuntimePlatform.IPhonePlayer) {
try {
if (!ps.ProxyEnabled) // если использование прокси-сервера отключено
{ // сбрасываем настройки прокси
ps.ProxyHost = null;
ps.ProxyPort = 0;
}
// эта функция лишь обновит настройку без установки обработчика протокола
DoSetupUpdate (ps.ProxyHost, ps.ProxyPort);
} catch (Exception e) {
// могут быть ошибки сигнатур нативных методов или имен классов
Debug.LogError (e.Message);
}
} else {
Environment.SetEnvironmentVariable("HTTP_PROXY", ps.ProxyHost + ":" + ps.ProxyPort,
EnvironmentVariableTarget.Machine);
Environment.SetEnvironmentVariable("HTTPS_PROXY", ps.ProxyHost + ":" + ps.ProxyPort,
EnvironmentVariableTarget.Machine);
}
}Вот так готовый модуль для Unity выглядел в работе:
А так выглядела трассировка работы Android-приложения на реальном смартфоне:
Наконец пришло время подключить готовый модуль Unity в реальные приложения заказчика и проверить в работе:
Не забыли мы и про интерфейс для настройки:
Вот так это выглядело непосредственно в среде разработки (в Unity модули умеют сами себя настраивать):
Эпилог
Вот такие дела, сложное предварительное изучение вопроса с отладчиком в руках, прототипирование и сама разработка на трех серьезных языках:
C# (Mono), Java (Android) и Objective-C (iOS).
Теперь представьте, если бы я следуя современным подходам в разработке начал ныть про «специализацию», что «один разработчик не может разбираться сразу во всем» и про прочее современное говно.
Да да, именно там где вы подумали.
За кадром еще осталась настройка PUSH-уведомлений для обновления настроек прокси без пересборки приложений, что было отдельной эпопеей.
На все ушло примерно полтора месяца, но думаю вы уже успели заметить — судя по времени на скриншотах эта работа сильно отличалась от лепки стандартных CRUDов в теплом офисе.
Также из наработок этого замечательного проекта родилась аж целая система управления наборами прокси, но это уже другая история, для другой статьи: