diff --git a/lib/phoenix/endpoint.ex b/lib/phoenix/endpoint.ex index 0c7490f939..b76ebe7f6b 100644 --- a/lib/phoenix/endpoint.ex +++ b/lib/phoenix/endpoint.ex @@ -251,6 +251,9 @@ defmodule Phoenix.Endpoint do * for handling paths and URLs: `c:struct_url/0`, `c:url/0`, `c:path/1`, `c:static_url/0`,`c:static_path/1`, and `c:static_integrity/1` + * for gethering runtime information about the address and port the + endpoint is running on: `c:server_info/1` + * for broadcasting to channels: `c:broadcast/3`, `c:broadcast!/3`, `c:broadcast_from/4`, `c:broadcast_from!/4`, `c:local_broadcast/3`, and `c:local_broadcast_from/4` @@ -342,6 +345,15 @@ defmodule Phoenix.Endpoint do """ @callback host() :: String.t() + # Server information + + @doc """ + Returns the address and port that the server is running on + """ + @callback server_info(Plug.Conn.scheme()) :: + {:ok, {:inet.ip_address(), :inet.port_number()} | :inet.returned_non_ip_address()} + | {:error, term()} + # Channels @doc """ @@ -605,6 +617,11 @@ defmodule Phoenix.Endpoint do &Phoenix.Endpoint.Supervisor.static_lookup(&1, path) ) end + + @doc """ + Returns the address and port that the server is running on + """ + def server_info(scheme), do: config(:adapter).server_info(__MODULE__, scheme) end end diff --git a/lib/phoenix/endpoint/cowboy2_adapter.ex b/lib/phoenix/endpoint/cowboy2_adapter.ex index 84117a9c7a..2356e26bd1 100644 --- a/lib/phoenix/endpoint/cowboy2_adapter.ex +++ b/lib/phoenix/endpoint/cowboy2_adapter.ex @@ -80,7 +80,7 @@ defmodule Phoenix.Endpoint.Cowboy2Adapter do Application.ensure_all_started(:ssl) end - ref = Module.concat(endpoint, scheme |> Atom.to_string() |> String.upcase()) + ref = make_ref(endpoint, scheme) plug = if code_reloader? do @@ -137,4 +137,16 @@ defmodule Phoenix.Endpoint.Cowboy2Adapter do defp port_to_integer({:system, env_var}), do: port_to_integer(System.get_env(env_var)) 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 + + def server_info(endpoint, scheme) do + make_ref(endpoint, scheme) + |> :ranch.get_addr() + |> then(&{:ok, &1}) + rescue + e -> {:error, e.message} + end + + defp make_ref(endpoint, scheme) do + Module.concat(endpoint, scheme |> Atom.to_string() |> String.upcase()) + end end diff --git a/lib/phoenix/endpoint/supervisor.ex b/lib/phoenix/endpoint/supervisor.ex index 36bd2ef4fd..98d4f55c78 100644 --- a/lib/phoenix/endpoint/supervisor.ex +++ b/lib/phoenix/endpoint/supervisor.ex @@ -137,7 +137,7 @@ defmodule Phoenix.Endpoint.Supervisor do defp server_children(mod, config, server?) do cond do server? -> - adapter = config[:adapter] || Phoenix.Endpoint.Cowboy2Adapter + adapter = config[:adapter] adapter.child_specs(mod, config) config[:http] || config[:https] -> @@ -196,6 +196,7 @@ defmodule Phoenix.Endpoint.Supervisor do render_errors: [view: render_errors(module), accepts: ~w(html), layout: false], # Runtime config + adapter: Phoenix.Endpoint.Cowboy2Adapter, cache_static_manifest: nil, check_origin: true, http: false, diff --git a/test/phoenix/endpoint/endpoint_test.exs b/test/phoenix/endpoint/endpoint_test.exs index beb946a994..720fb6467a 100644 --- a/test/phoenix/endpoint/endpoint_test.exs +++ b/test/phoenix/endpoint/endpoint_test.exs @@ -227,6 +227,22 @@ defmodule Phoenix.Endpoint.EndpointTest do assert StaticEndpoint.static_path("/phoenix.png") =~ "/static/phoenix.png" end + @tag :capture_log + test "can find the running address and port for an endpoint" do + Application.put_env(:phoenix, __MODULE__.AddressEndpoint, + http: [ip: {127, 0, 0, 1}, port: 0], + server: true + ) + + defmodule AddressEndpoint do + use Phoenix.Endpoint, otp_app: :phoenix + end + + AddressEndpoint.start_link() + assert {:ok, {{127, 0, 0, 1}, port}} = AddressEndpoint.server_info(:http) + assert is_integer(port) + end + test "injects pubsub broadcast with configured server" do Endpoint.subscribe("sometopic") some = spawn fn -> :ok end