Background: это было написано для "фронт-энд" сервиса, который должен был проксировать HTTP и WebSocket-запросы на несколько внутренних сервисов. Для того, чтобы переопределить то, как Phoenix обрабатывает вебсокеты, надо поменять [настройки HTTP сервера](https://hexdocs.pm/phoenix/Phoenix.Endpoint.html#module-runtime-configuration) (Cowboy), а именно [настройки роутинга ](https://ninenines.eu/docs/en/cowboy/2.5/manual/cowboy_router/) (`:dispatch`) — см. [`config.exs`](https://gist.github.com/smaximov/5307d6cead522384c0a7b045acdd8ef8#file-config-exs). Тут мы указываем, что для любого хоста: а) для пути `"/aaa/websocket"` вызывается хендлер [`API.Gateway.WSReverseProxy`](https://gist.github.com/smaximov/5307d6cead522384c0a7b045acdd8ef8#file-ws_reverse_proxy-ex), б) для любого другого пути вызывается дефолтный хэндлер Phoenix. [`API.Gateway.WSReverseProxy`](https://gist.github.com/smaximov/5307d6cead522384c0a7b045acdd8ef8#file-ws_reverse_proxy-ex) — это хэндлер Cowboy, для подробной информации о реализуемых колбэках см. поведение [`:cowboy_websocket`](https://ninenines.eu/docs/en/cowboy/2.5/manual/cowboy_websocket/). Логика его работы следующая. На каждое вебсокет-соединение создаётся два процесса: процесс самого хэндлера и процесс клиента, который ответственен за проксирование запроса, общаются они между через передачу сообщений. При открытии вебсокет-соединения вызывается колбэк `init/2`, в котором мы указываем, что хэндлер предназначен для обработки вебсокетов (см. возвращаемое значение). При этом мы откладываем обработку первичного запроса на соединение на момент создания клиента, складывая сам запрос в стейт. Это нужно потому, что на момент выполнения `init/2` процесс хэндлера ещё не создан, т.е. мы не знаем его PID, который понадобится для того, чтобы клиент мог возвращать запросы с бэкенд-сервиса браузеру через хэндлер. Затем вызывается `websocket_init/1`, который уже используется для инициализации созданного процесса хэндлера, тут мы создаём процесс клиента через [`API.Gateway.WSReverseProxy.ClientSupervisor`](https://gist.github.com/smaximov/5307d6cead522384c0a7b045acdd8ef8#file-client_supervisor-ex). Клиент [`API.Gateway.WSReverseProxy.Client`](https://gist.github.com/smaximov/5307d6cead522384c0a7b045acdd8ef8#file-client-ex) реализован на основе [Websockex](https://github.com/Azolo/websockex). При инициализации клиент определяет параметры соединения с помощью модуля, реализующего [`API.Gateway.WSReverseProxy.CallbackModule`](https://gist.github.com/smaximov/5307d6cead522384c0a7b045acdd8ef8#file-callback_module-ex) (для примера см. [`API.Gateway.WSReverseProxy.AAA`](https://gist.github.com/smaximov/5307d6cead522384c0a7b045acdd8ef8#file-aaa-ex)). Для проксирования HTTP-запросов используется модуль [`Plugs.ReverseProxy`](https://gist.github.com/smaximov/5307d6cead522384c0a7b045acdd8ef8#file-reverse_proxy-ex) через `forward` в роутере.