January 8

Проект "Unity Proxy"

Продолжаю рассказывать про наши интересные реализованные проекты. В этот раз представляю вашему вниманию проект 2018го года, из категории «самый полный Пэ» — за такое беремся только мы.

Так выглядела отладка, правда весело?

Полный Пэ

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

Если вы зазвездились и начали считать себя настоящим гением, гуру и профи в одном лице — описываемая жесть отлично поставит вас на место.

Да и вообще подобные проекты неплохо бодрят и не позволяют вконец отупеть от очередного корпоративного формошлепства.

Начнем с описания этого интересного проекта.

У заказчика были два веселых гуся готовых приложения для iOS и Android, созданных на базе Unity, которые с помощью технологий дополненной реальности позволяли встраивать трехмерные объекты в видеопоток с камеры телефона.

В работе эта неведомая хтонь выглядела как-то так:

Все работало бы замечательно и дальше, если бы не родной РКН, который в тот год объявил джихад и кровную месть Телеграму и в попытках его заблокировать порубил половину тогдашнего рунета.

Если кто забыл, в 2018 м постоянно падали банковские приложения, хостинги, онлайн-казино сервисы — пока сотрудники РКН изучали как работают CDN-серверы и геораспределенные зеркала, они раз за разом блочили абсолютно левые сервисы, просто оказавшиеся соседями по провайдеру или подсети.

Замечательное приложение с дополненной реальностью тоже задело банхаммером РКН:

под раздачу попали сервера Vuforia, которые использовались для определения точки привязки объекта в видеопотоке.

Обратите внимание на картинку с текстурой камней в видео выше — это и есть та самая точка привязки, используемая для позиционирования.

Суммируя получалось следующее:

Проект на Unity, который использует Vuforia SDK, из которого собираются два мобильных приложения Android и iOS, которые делают вызовы к удаленному закрытому серверу (точнее к нескольким) для своей работы.

Вся логика взаимодействия с серверами Vuforia является закрытой, реализация частично нативная.

И вот вся эта радость в один момент была заблокирована РКН.

Перенастроить адреса серверов нельзя (они зашиты в библиотеку), сделать эмулятор — времени не хватит.

А задача очень простая (как обычно):

придти и все исправить.

Примерно как у Бетмена, разве что в небо прожектором не светили.

Ну что, взялись бы за такой проект?

Изучение

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

Затем через отладчик началось изучение протокола взаимодействия:

Так выглядела регистрация сессии на сервере Vuforia

Отладка показала самое важное:

протокол взаимодействия между приложением и серверами 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.

Плюс реализовать всю обвязку по настройке, чтобы не зашивать адрес прокси в код приложения

Попытка запуска Vuforia в эмуляторе iOS - стало понятно что нужно реальное устройство.

Оборудование

Отдельной подставой в тот момент был выход из строя сразу двоих моих рабочих ноутбуков, еще и по серьезным причинам, требующим длительного ремонта.

Пока они были в ремонте, завершать проект пришлось на полных «дровах»:

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ов в теплом офисе.

Также из наработок этого замечательного проекта родилась аж целая система управления наборами прокси, но это уже другая история, для другой статьи: