From 2b43a0dadc6617f94c25d0c4ac0155af0e4c32c4 Mon Sep 17 00:00:00 2001 From: Jason Axelson Date: Tue, 1 Feb 2022 08:29:54 +0000 Subject: [PATCH 1/4] Allow runtime configuration of MasterProxy By not starting up an application, and instead having the user define a proxy module to add to their supervision tree (similar to how a Phoenix Endpoint is added to the user's supervision tree) it becomes easier for the user to define the backends for MasterProxy at runtime. In particular I need this for using MasterProxy with SiteEncrypt since SiteEncrypt generates its configuration at runtime. --- CHANGELOG.md | 26 +++++++ README.md | 22 +++++- lib/master_proxy/application.ex | 54 -------------- lib/master_proxy/proxy.ex | 120 ++++++++++++++++++++++++++++++++ mix.exs | 3 +- 5 files changed, 168 insertions(+), 57 deletions(-) delete mode 100644 lib/master_proxy/application.ex create mode 100644 lib/master_proxy/proxy.ex diff --git a/CHANGELOG.md b/CHANGELOG.md index dfcda1d..c9fe98c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,32 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). ## [Unreleased] +The flexibility of MasterProxy has been increased and it is now possible to +generate configuration at runtime during application startup. + +Breaking change: You must create a `MyApp.Proxy` module that calls `use +MasterProxy`. This allows configuration to be generated at runtime which is +important for usage with SiteEncrypt along with other setups. + +Example module: + +```elixir +defmodule MyApp.Proxy do + use MasterProxy.Proxy +end +``` + +The proxy must then be explicitly started as part of your application +supervision tree. Proxies can be added to the supervision tree as follows +(usually in `MyApp.Application`): + +```elixir +children = [ + # ... other children + MyApp.Proxy, +] +``` + ## 0.1.4 - 2022-01-21 ### Added - Add server and domain options [#16](https://github.com/jesseshieh/master_proxy/pull/16) diff --git a/README.md b/README.md index 924cfc4..fa0218d 100644 --- a/README.md +++ b/README.md @@ -20,7 +20,10 @@ def deps do end ``` -Configure rules for routing requests by adding something like this in your configuration (i.e. `config/config.exs`). +Configure rules for routing requests by adding configuration (i.e. +`config/config.exs`). Backend configuration is optional and can be replaced by +the `merge_config/2` callback of your proxy module (more info below) if you need +to generate configuration at runtime. ```elixir config :master_proxy, @@ -47,6 +50,23 @@ config :master_proxy, See [Configuration Examples](#module-configuration-examples) for more. +Then create the proxy module and add it to your application startup (often in `MyApp.Application`): + +module: +``` elixir +defmodule MyApp.Proxy do + use MasterProxy.Proxy +end +``` + +Proxies must be explicitly started as part of your application supervision tree. +Proxies can be added to the supervision tree as follows (usually in `MyApp.Application`): + + children = [ + # ... other children + MyApp.Proxy, + ] + To avoid the platform routing requests directly to your Web apps' Endpoints, and thus bypassing the Endpoint on which MasterProxy is running, you can configure your other Web apps' Endpoints to not start a server in your production config. ```elixir diff --git a/lib/master_proxy/application.ex b/lib/master_proxy/application.ex deleted file mode 100644 index b6557ab..0000000 --- a/lib/master_proxy/application.ex +++ /dev/null @@ -1,54 +0,0 @@ -defmodule MasterProxy.Application do - @moduledoc false - use Application - require Logger - - def start(_type, _args) do - children = children(server?()) - - opts = [strategy: :one_for_one, name: MasterProxy.Supervisor] - Supervisor.start_link(children, opts) - end - - defp children(false), do: [] - - defp children(true) do - Enum.reduce([:http, :https], [], fn scheme, result -> - case Application.get_env(:master_proxy, scheme) do - nil -> - # no config for this scheme, that's ok, just skip - result - - scheme_opts -> - port = :proplists.get_value(:port, scheme_opts) - dispatch = [{:_, [{:_, MasterProxy.Cowboy2Handler, {nil, nil}}]}] - - opts = - [ - port: port_to_integer(port), - dispatch: dispatch - ] ++ :proplists.delete(:port, scheme_opts) - - Logger.info("[master_proxy] Listening on #{scheme} with options: #{inspect(opts)}") - - [{Plug.Cowboy, scheme: scheme, plug: {nil, nil}, options: opts} | result] - end - end) - end - - defp server?() do - # the server will be started in following situations: - # + enable `server: true` option for master_proxy (by default) - # + run `iex -S mix phx.server` - # + run `mix phx.server` - Application.get_env(:phoenix, :serve_endpoints, false) || - Application.get_env(:master_proxy, :server, true) - end - - # :undefined is what :proplist.get_value returns - defp port_to_integer(:undefined), - do: raise("port is missing from the master_proxy configuration") - - defp port_to_integer(port) when is_binary(port), do: String.to_integer(port) - defp port_to_integer(port) when is_integer(port), do: port -end diff --git a/lib/master_proxy/proxy.ex b/lib/master_proxy/proxy.ex new file mode 100644 index 0000000..f99ba70 --- /dev/null +++ b/lib/master_proxy/proxy.ex @@ -0,0 +1,120 @@ +defmodule MasterProxy.Proxy do + @moduledoc """ + Defines a proxy + + Allows defining an http and/or https proxy. + + Basic example: + + defmodule MyApp.Proxy do + use MasterProxy.Proxy + end + + Example with [SiteEncrypt](https://hex.pm/packages/site_encrypt): + + defmodule MyApp.Proxy do + use MasterProxy.Proxy + + @impl MasterProxy.Proxy + def merge_config(:https, opts) do + Config.Reader.merge(opts, SiteEncrypt.https_keys(MyAppWeb.Endpoint)) + end + + def merge_config(_, opts), do: opts + end + """ + + @type scheme :: :http | :https + + @doc """ + Overriding this callback allows the configuration from the application + environment to be modified at runtime. + + Receives configuration from application environment. By default the + application environment configuration is used. + """ + @callback merge_config(scheme(), keyword()) :: keyword() + + @optional_callbacks merge_config: 2 + + require Logger + + defmacro __using__(_opts) do + quote do + use Supervisor + + @behaviour MasterProxy.Proxy + + def start_link(opts \\ []) do + Supervisor.start_link(__MODULE__, opts, name: __MODULE__) + end + + @impl Supervisor + def init(callback_module) do + backends = Application.fetch_env!(:master_proxy, :backends) + + children = + if MasterProxy.Proxy.server?() do + MasterProxy.Proxy.spec([backends: backends, callback_module: __MODULE__], __MODULE__) + else + [] + end + + Supervisor.init(children, strategy: :one_for_one) + end + + def merge_config(_scheme, opts), do: opts + + defoverridable merge_config: 2 + end + end + + # Builds the spec for each Plug.Cowboy process + @doc false + def spec(handler_opts, callback_module) do + Enum.reduce([:http, :https], [], fn scheme, result -> + case Application.get_env(:master_proxy, scheme) do + nil -> + # no config for this scheme, that's ok, just skip + result + + scheme_opts -> + opts = build_opts(scheme, scheme_opts, handler_opts, callback_module) + + Logger.info("[master_proxy] Listening on #{scheme} with options: #{inspect(opts)}") + + [{Plug.Cowboy, scheme: scheme, plug: {nil, nil}, options: opts} | result] + end + end) + end + + defp build_opts(scheme, scheme_opts, handler_opts, callback_module) do + port = :proplists.get_value(:port, scheme_opts) + dispatch = [{:_, [{:_, MasterProxy.Cowboy2Handler, {nil, handler_opts}}]}] + + opts = + callback_module.merge_config(scheme, + port: port_to_integer(port), + dispatch: dispatch + ) + + opts ++ :proplists.delete(:port, scheme_opts) + end + + @doc false + def server?() do + # the server will be started in following situations: + # + enable `server: true` option for master_proxy (by default) + # + run `iex -S mix phx.server` + # + run `mix phx.server` + Application.get_env(:phoenix, :serve_endpoints, false) || + Application.get_env(:master_proxy, :server, true) + end + + # :undefined is what :proplist.get_value returns + defp port_to_integer(:undefined), + do: raise("port is missing from the master_proxy configuration") + + defp port_to_integer(port) when is_binary(port), do: String.to_integer(port) + defp port_to_integer(port) when is_integer(port), do: port +end diff --git a/mix.exs b/mix.exs index 090bcfc..8fbf7a9 100644 --- a/mix.exs +++ b/mix.exs @@ -22,8 +22,7 @@ defmodule MasterProxy.MixProject do # Run "mix help compile.app" to learn about applications. def application do [ - extra_applications: [:logger], - mod: {MasterProxy.Application, []} + extra_applications: [:logger] ] end From 5af51e81631ddcf8e85eb459891581f87f9c012b Mon Sep 17 00:00:00 2001 From: Jason Axelson Date: Wed, 2 Feb 2022 10:54:32 +0000 Subject: [PATCH 2/4] Fix tests Also allow a plain `mix format` to work by adding :stream_data to dev env --- mix.exs | 2 +- test/master_proxy/websocket_socket_test.exs | 7 ++++++- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/mix.exs b/mix.exs index 8fbf7a9..cb75f1a 100644 --- a/mix.exs +++ b/mix.exs @@ -46,7 +46,7 @@ defmodule MasterProxy.MixProject do {:ex_doc, ">= 0.0.0", only: :dev}, # test - {:stream_data, "~> 0.4", only: :test}, + {:stream_data, "~> 0.4", only: [:dev, :test]}, {:jason, "~> 1.0", only: :test}, {:websocket_client, git: "https://github.com/jeremyong/websocket_client.git", only: :test} ] diff --git a/test/master_proxy/websocket_socket_test.exs b/test/master_proxy/websocket_socket_test.exs index ed63d77..130ca52 100644 --- a/test/master_proxy/websocket_socket_test.exs +++ b/test/master_proxy/websocket_socket_test.exs @@ -8,7 +8,7 @@ defmodule MasterProxy.Integration.WebSocketTest do alias __MODULE__.Endpoint @moduletag :capture_log - @port 5907 + @port Application.fetch_env!(:master_proxy, :http)[:port] @path "ws://127.0.0.1:#{@port}/ws/websocket" # TODO: how does this work? when I try to configure @@ -23,6 +23,10 @@ defmodule MasterProxy.Integration.WebSocketTest do server: true ) + defmodule MyApp.Proxy do + use MasterProxy.Proxy + end + defmodule UserSocket do @behaviour Phoenix.Socket.Transport @@ -78,6 +82,7 @@ defmodule MasterProxy.Integration.WebSocketTest do # This needs to start so Phoenix.Config is initialized # among other things capture_log(fn -> Endpoint.start_link() end) + start_supervised!(MyApp.Proxy) :ok end From 79f72cf98c1d316705ab7230c5a4431c881aabe5 Mon Sep 17 00:00:00 2001 From: Jason Axelson Date: Sat, 15 Oct 2022 13:33:33 -1000 Subject: [PATCH 3/4] Update docs and add `backends/0` callback --- CHANGELOG.md | 8 +++ README.md | 83 ++++++++++++++++++++--------- lib/master_proxy/cowboy2_handler.ex | 5 +- lib/master_proxy/proxy.ex | 52 +++++++++++++++--- 4 files changed, 112 insertions(+), 36 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c9fe98c..242e745 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -31,6 +31,14 @@ children = [ ] ``` +You can also move your `backends` configuration into your `MyApp.Proxy` module +instead of inside application configuration. This change is in line with the +["Avoid application +configuration"](https://hexdocs.pm/elixir/1.13/library-guidelines.html#avoid-application-configuration) +library guideline. + +See `MasterProxy.Proxy` docs for details about the new module. + ## 0.1.4 - 2022-01-21 ### Added - Add server and domain options [#16](https://github.com/jesseshieh/master_proxy/pull/16) diff --git a/README.md b/README.md index fa0218d..e5046e6 100644 --- a/README.md +++ b/README.md @@ -26,28 +26,16 @@ the `merge_config/2` callback of your proxy module (more info below) if you need to generate configuration at runtime. ```elixir -config :master_proxy, +config :master_proxy, # any Cowboy options are allowed http: [:inet6, port: 4080], - https: [:inet6, port: 4443], - backends: [ - %{ - domain: "my-cool-app.com", - phoenix_endpoint: MyCoolAppWeb.Endpoint - }, - %{ - domain: "members.my-cool-app.com", - phoenix_endpoint: MyAppMembersWeb.Endpoint - }, - %{ - verb: ~r/get/i, - path: ~r{^/master-proxy-plug-test$}, - plug: MasterProxy.Plug.Test, - opts: [1, 2, 3] - } - ] + https: [:inet6, port: 4443] ``` +Note: backends can also be configured via configuration, but configuring the +backends via your proxy module (see the `use MasterProxy.Proxy` example below) +is recommended. + See [Configuration Examples](#module-configuration-examples) for more. Then create the proxy module and add it to your application startup (often in `MyApp.Application`): @@ -56,6 +44,26 @@ module: ``` elixir defmodule MyApp.Proxy do use MasterProxy.Proxy + + @impl MasterProxy.Proxy + def backends do + [ + %{ + domain: "my-cool-app.com", + phoenix_endpoint: MyCoolAppWeb.Endpoint + }, + %{ + domain: "members.my-cool-app.com", + phoenix_endpoint: MyAppMembersWeb.Endpoint + }, + %{ + verb: ~r/get/i, + path: ~r{^/master-proxy-plug-test$}, + plug: MasterProxy.Plug.Test, + opts: [1, 2, 3] + } + ] + end end ``` @@ -82,6 +90,7 @@ config :my_app_web, MyAppWeb.Endpoint, - `:https` - the configuration for the HTTPS server. It accepts all options as defined by [Plug.Cowboy](https://hexdocs.pm/plug_cowboy/). - `:server` - `true` by default. If you are running application with `mix phx.server`, this option is ignored, and the server will always be started. - `:backends` - the rule for routing requests. See [Configuration Examples](#configuration-examples) for more. + - `:domain` - `:verb` - `:host` - `:path` @@ -95,6 +104,36 @@ config :my_app_web, MyAppWeb.Endpoint, ### Route requests to apps based on hostname ```elixir +defmodule MyApp.Proxy do + use MasterProxy.Proxy + + @impl MasterProxy.Proxy + def backends do + [ + %{ + host: ~r{^app-name\.gigalixirapp\.com$}, + phoenix_endpoint: MyAppWeb.Endpoint + }, + %{ + host: ~r{^www\.example\.com$}, + phoenix_endpoint: MyAppWeb.Endpoint + }, + %{ + host: ~r{^api\.example\.com$}, + phoenix_endpoint: MyAppApiWeb.Endpoint + }, + %{ + host: ~r{^members\.example\.com$}, + phoenix_endpoint: MyAppMembersWeb.Endpoint + } + ] + end +end +``` + +### Configuration via application config + +``` elixir config :master_proxy, http: [port: 80], backends: [ @@ -105,14 +144,6 @@ config :master_proxy, %{ host: ~r{^www\.example\.com$}, phoenix_endpoint: MyAppWeb.Endpoint - }, - %{ - host: ~r{^api\.example\.com$}, - phoenix_endpoint: MyAppApiWeb.Endpoint - }, - %{ - host: ~r{^members\.example\.com$}, - phoenix_endpoint: MyAppMembersWeb.Endpoint } ] ``` diff --git a/lib/master_proxy/cowboy2_handler.ex b/lib/master_proxy/cowboy2_handler.ex index c47800c..d2aea7f 100644 --- a/lib/master_proxy/cowboy2_handler.ex +++ b/lib/master_proxy/cowboy2_handler.ex @@ -19,13 +19,12 @@ defmodule MasterProxy.Cowboy2Handler do # endpoint and opts are not passed in because they # are dynamically chosen - def init(req, {_endpoint, _opts}) do + def init(req, {_endpoint, opts}) do log_request("MasterProxy.Cowboy2Handler called with req: #{inspect(req)}") conn = connection().conn(req) - # extract this and pass in as a param somehow - backends = Application.get_env(:master_proxy, :backends) + backends = Keyword.get(opts, :backends) backend = choose_backend(conn, backends) log_request("Backend chosen: #{inspect(backend)}") diff --git a/lib/master_proxy/proxy.ex b/lib/master_proxy/proxy.ex index f99ba70..e34d71b 100644 --- a/lib/master_proxy/proxy.ex +++ b/lib/master_proxy/proxy.ex @@ -27,15 +27,38 @@ defmodule MasterProxy.Proxy do @type scheme :: :http | :https @doc """ + Merge cowboy config + Overriding this callback allows the configuration from the application environment to be modified at runtime. - - Receives configuration from application environment. By default the - application environment configuration is used. """ @callback merge_config(scheme(), keyword()) :: keyword() - @optional_callbacks merge_config: 2 + @doc """ + Specify the backends to pass requests to at startup time. (Optional) + + Overriding this callback allows for setting the backends to be matched at + runtime when the proxy is starting up. + + Example: + + @impl MasterProxy.Proxy + def backends do + [ + %{ + domain: "https://myapp1.com", + phoenix_endpoint: MyApp1Web.Endpoint + }, + %{ + domain: "https://myapp2.com", + phoenix_endpoint: MyApp2Web.Endpoint + } + ] + end + """ + @callback backends :: list(map()) + + @optional_callbacks merge_config: 2, backends: 0 require Logger @@ -50,8 +73,8 @@ defmodule MasterProxy.Proxy do end @impl Supervisor - def init(callback_module) do - backends = Application.fetch_env!(:master_proxy, :backends) + def init(_opts) do + backends = __MODULE__.backends() children = if MasterProxy.Proxy.server?() do @@ -64,8 +87,23 @@ defmodule MasterProxy.Proxy do end def merge_config(_scheme, opts), do: opts + def backends, do: MasterProxy.Proxy.default_fetch_backends() - defoverridable merge_config: 2 + defoverridable merge_config: 2, backends: 0 + end + end + + @doc false + def default_fetch_backends do + case Application.fetch_env(:master_proxy, :backends) do + {:ok, backends} -> + backends + + :error -> + Logger.warn( + "No backends specified. Either configure :master_proxy, :backends or define a " <> + "`backend/0` function in your `Proxy` module." + ) end end From fbce9ca1956d5d0243db2ef4fd0045e3fc9666ac Mon Sep 17 00:00:00 2001 From: Jason Axelson Date: Sat, 15 Oct 2022 18:37:36 -1000 Subject: [PATCH 4/4] My best stab at fixing the tests --- mix.exs | 2 +- mix.lock | 3 +-- .../cowboy2_handler_phoenix_endpoint_test.exs | 5 ++-- .../cowboy2_handler_plug_test.exs | 26 +++++++++++-------- test/master_proxy/websocket_socket_test.exs | 22 +++++++++------- test/support/websocket_client.ex | 6 ++++- 6 files changed, 38 insertions(+), 26 deletions(-) diff --git a/mix.exs b/mix.exs index cb75f1a..262187d 100644 --- a/mix.exs +++ b/mix.exs @@ -48,7 +48,7 @@ defmodule MasterProxy.MixProject do # test {:stream_data, "~> 0.4", only: [:dev, :test]}, {:jason, "~> 1.0", only: :test}, - {:websocket_client, git: "https://github.com/jeremyong/websocket_client.git", only: :test} + {:websocket_client, "~> 1.5", only: :test} ] end end diff --git a/mix.lock b/mix.lock index 47152e7..301af29 100644 --- a/mix.lock +++ b/mix.lock @@ -2,7 +2,6 @@ "cowboy": {:hex, :cowboy, "2.9.0", "865dd8b6607e14cf03282e10e934023a1bd8be6f6bacf921a7e2a96d800cd452", [:make, :rebar3], [{:cowlib, "2.11.0", [hex: :cowlib, repo: "hexpm", optional: false]}, {:ranch, "1.8.0", [hex: :ranch, repo: "hexpm", optional: false]}], "hexpm", "2c729f934b4e1aa149aff882f57c6372c15399a20d54f65c8d67bef583021bde"}, "cowboy_telemetry": {:hex, :cowboy_telemetry, "0.4.0", "f239f68b588efa7707abce16a84d0d2acf3a0f50571f8bb7f56a15865aae820c", [:rebar3], [{:cowboy, "~> 2.7", [hex: :cowboy, repo: "hexpm", optional: false]}, {:telemetry, "~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "7d98bac1ee4565d31b62d59f8823dfd8356a169e7fcbb83831b8a5397404c9de"}, "cowlib": {:hex, :cowlib, "2.11.0", "0b9ff9c346629256c42ebe1eeb769a83c6cb771a6ee5960bd110ab0b9b872063", [:make, :rebar3], [], "hexpm", "2b3e9da0b21c4565751a6d4901c20d1b4cc25cbb7fd50d91d2ab6dd287bc86a9"}, - "earmark": {:hex, :earmark, "1.3.0", "17f0c38eaafb4800f746b457313af4b2442a8c2405b49c645768680f900be603", [:mix], [], "hexpm", "f8b8820099caf0d5e72ae6482d2b0da96f213cbbe2b5b2191a37966e119eaa27"}, "earmark_parser": {:hex, :earmark_parser, "1.4.19", "de0d033d5ff9fc396a24eadc2fcf2afa3d120841eb3f1004d138cbf9273210e8", [:mix], [], "hexpm", "527ab6630b5c75c3a3960b75844c314ec305c76d9899bb30f71cb85952a9dc45"}, "ex_doc": {:hex, :ex_doc, "0.27.3", "d09ed7ab590b71123959d9017f6715b54a448d76b43cf909eb0b2e5a78a977b2", [:mix], [{:earmark_parser, "~> 1.4.19", [hex: :earmark_parser, repo: "hexpm", optional: false]}, {:makeup_elixir, "~> 0.14", [hex: :makeup_elixir, repo: "hexpm", optional: false]}, {:makeup_erlang, "~> 0.1", [hex: :makeup_erlang, repo: "hexpm", optional: false]}], "hexpm", "ee60b329d08195039bfeb25231a208749be4f2274eae42ce38f9be0538a2f2e6"}, "jason": {:hex, :jason, "1.3.0", "fa6b82a934feb176263ad2df0dbd91bf633d4a46ebfdffea0c8ae82953714946", [:mix], [{:decimal, "~> 1.0 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm", "53fc1f51255390e0ec7e50f9cb41e751c260d065dcba2bf0d08dc51a4002c2ac"}, @@ -20,5 +19,5 @@ "ranch": {:hex, :ranch, "1.8.0", "8c7a100a139fd57f17327b6413e4167ac559fbc04ca7448e9be9057311597a1d", [:make, :rebar3], [], "hexpm", "49fbcfd3682fab1f5d109351b61257676da1a2fdbe295904176d5e521a2ddfe5"}, "stream_data": {:hex, :stream_data, "0.5.0", "b27641e58941685c75b353577dc602c9d2c12292dd84babf506c2033cd97893e", [:mix], [], "hexpm", "012bd2eec069ada4db3411f9115ccafa38540a3c78c4c0349f151fc761b9e271"}, "telemetry": {:hex, :telemetry, "1.0.0", "0f453a102cdf13d506b7c0ab158324c337c41f1cc7548f0bc0e130bbf0ae9452", [:rebar3], [], "hexpm", "73bc09fa59b4a0284efb4624335583c528e07ec9ae76aca96ea0673850aec57a"}, - "websocket_client": {:git, "https://github.com/jeremyong/websocket_client.git", "9a6f65d05ebf2725d62fb19262b21f1805a59fbf", []}, + "websocket_client": {:hex, :websocket_client, "1.5.0", "e825f23c51a867681a222148ed5200cc4a12e4fb5ff0b0b35963e916e2b5766b", [:rebar3], [], "hexpm", "2b9b201cc5c82b9d4e6966ad8e605832eab8f4ddb39f57ac62f34cb208b68de9"}, } diff --git a/test/master_proxy/cowboy2_handler_phoenix_endpoint_test.exs b/test/master_proxy/cowboy2_handler_phoenix_endpoint_test.exs index f3e73ce..98acaa4 100644 --- a/test/master_proxy/cowboy2_handler_phoenix_endpoint_test.exs +++ b/test/master_proxy/cowboy2_handler_phoenix_endpoint_test.exs @@ -34,11 +34,12 @@ defmodule MasterProxy.Cowboy2HandlerPhoenixEndpointTest do # |> MasterProxy.Plug.call(MasterProxy.Plug.init(opts)) backends = [%{host: backend_host, phoenix_endpoint: MasterProxy.Test.Endpoint}] - Application.put_env(:master_proxy, :backends, backends) # these are the required params.. req = build_req("http", "GET", conn_host, "/") - {:ok, _req, {_handler, _opts}} = MasterProxy.Cowboy2Handler.init(req, {nil, nil}) + + {:ok, _req, {_handler, _opts}} = + MasterProxy.Cowboy2Handler.init(req, {nil, backends: backends}) my_pid = self() stream_id = 1 diff --git a/test/master_proxy/cowboy2_handler_plug_test.exs b/test/master_proxy/cowboy2_handler_plug_test.exs index 546c196..3cfa1b2 100644 --- a/test/master_proxy/cowboy2_handler_plug_test.exs +++ b/test/master_proxy/cowboy2_handler_plug_test.exs @@ -22,11 +22,12 @@ defmodule MasterProxy.Cowboy2HandlerPlugTest do defp matches_domain?(backend_domain, conn_domain) do backends = [%{domain: backend_domain, plug: MasterProxy.Plug.Test}] - Application.put_env(:master_proxy, :backends, backends) # these are the required params.. req = build_req("http", "GET", conn_domain, "/") - {:ok, _req, {_handler, _opts}} = MasterProxy.Cowboy2Handler.init(req, {nil, nil}) + + {:ok, _req, {_handler, _opts}} = + MasterProxy.Cowboy2Handler.init(req, {nil, backends: backends}) my_pid = self() stream_id = 1 @@ -42,11 +43,12 @@ defmodule MasterProxy.Cowboy2HandlerPlugTest do defp matches_host?(backend_host, conn_host) do backends = [%{host: backend_host, plug: MasterProxy.Plug.Test}] - Application.put_env(:master_proxy, :backends, backends) # these are the required params.. req = build_req("http", "GET", conn_host, "/") - {:ok, _req, {_handler, _opts}} = MasterProxy.Cowboy2Handler.init(req, {nil, nil}) + + {:ok, _req, {_handler, _opts}} = + MasterProxy.Cowboy2Handler.init(req, {nil, backends: backends}) my_pid = self() stream_id = 1 @@ -62,11 +64,12 @@ defmodule MasterProxy.Cowboy2HandlerPlugTest do defp matches_path?(backend_path, conn_path) do backends = [%{path: backend_path, plug: MasterProxy.Plug.Test}] - Application.put_env(:master_proxy, :backends, backends) # these are the required params.. req = build_req("http", "GET", "localhost", conn_path) - {:ok, _req, {_handler, _opts}} = MasterProxy.Cowboy2Handler.init(req, {nil, nil}) + + {:ok, _req, {_handler, _opts}} = + MasterProxy.Cowboy2Handler.init(req, {nil, backends: backends}) my_pid = self() stream_id = 1 @@ -82,11 +85,12 @@ defmodule MasterProxy.Cowboy2HandlerPlugTest do defp matches_verb?(backend_verb, conn_verb) do backends = [%{verb: backend_verb, plug: MasterProxy.Plug.Test}] - Application.put_env(:master_proxy, :backends, backends) # these are the required params.. req = build_req("http", conn_verb, "localhost", "/") - {:ok, _req, {_handler, _opts}} = MasterProxy.Cowboy2Handler.init(req, {nil, nil}) + + {:ok, _req, {_handler, _opts}} = + MasterProxy.Cowboy2Handler.init(req, {nil, backends: backends}) my_pid = self() stream_id = 1 @@ -111,11 +115,11 @@ defmodule MasterProxy.Cowboy2HandlerPlugTest do } ] - Application.put_env(:master_proxy, :backends, backends) - # these are the required params.. req = build_req("http", conn_verb, conn_host, conn_path) - {:ok, _req, {_handler, _opts}} = MasterProxy.Cowboy2Handler.init(req, {nil, nil}) + + {:ok, _req, {_handler, _opts}} = + MasterProxy.Cowboy2Handler.init(req, {nil, backends: backends}) my_pid = self() stream_id = 1 diff --git a/test/master_proxy/websocket_socket_test.exs b/test/master_proxy/websocket_socket_test.exs index 130ca52..4714267 100644 --- a/test/master_proxy/websocket_socket_test.exs +++ b/test/master_proxy/websocket_socket_test.exs @@ -8,7 +8,7 @@ defmodule MasterProxy.Integration.WebSocketTest do alias __MODULE__.Endpoint @moduletag :capture_log - @port Application.fetch_env!(:master_proxy, :http)[:port] + @port Application.compile_env!(:master_proxy, [:http, :port]) @path "ws://127.0.0.1:#{@port}/ws/websocket" # TODO: how does this work? when I try to configure @@ -25,14 +25,23 @@ defmodule MasterProxy.Integration.WebSocketTest do defmodule MyApp.Proxy do use MasterProxy.Proxy + + @impl MasterProxy.Proxy + def backends do + # matches everything and proxies over to the Endpoint here + [%{phoenix_endpoint: Endpoint}] + end end defmodule UserSocket do @behaviour Phoenix.Socket.Transport - def child_spec(opts) do - :value = Keyword.fetch!(opts, :custom) - Supervisor.child_spec({Task, fn -> :ok end}, []) + def child_spec(_opts) do + %{id: UserSocket, start: {Task, :start_link, [fn -> :ok end]}, restart: :transient} + end + + def start_link(opts \\ []) do + GenServer.start_link(__MODULE__, opts, name: __MODULE__) end def connect(map) do @@ -74,11 +83,6 @@ defmodule MasterProxy.Integration.WebSocketTest do end setup_all do - # capture_log(fn -> MasterProxy.Application.start(nil, nil) end) - # MasterProxy.Application.start(nil, nil) - # matches everything and proxies over to the Endpoint here - backends = [%{phoenix_endpoint: Endpoint}] - Application.put_env(:master_proxy, :backends, backends) # This needs to start so Phoenix.Config is initialized # among other things capture_log(fn -> Endpoint.start_link() end) diff --git a/test/support/websocket_client.ex b/test/support/websocket_client.ex index 62c2d09..731363b 100644 --- a/test/support/websocket_client.ex +++ b/test/support/websocket_client.ex @@ -12,7 +12,7 @@ defmodule MasterProxy.Integration.WebsocketClient do :ssl.start() :websocket_client.start_link( - String.to_charlist(url), + url, __MODULE__, [sender, serializer], extra_headers: headers @@ -62,6 +62,10 @@ defmodule MasterProxy.Integration.WebsocketClient do end @doc false + def init([sender, serializer]) do + {:ok, %{sender: sender, join_ref: 1, ref: 0, serializer: serializer}} + end + def init([sender, serializer], _conn_state) do {:ok, %{sender: sender, join_ref: 1, ref: 0, serializer: serializer}} end