diff --git a/integration_test/mix.lock b/integration_test/mix.lock index 83ecfe3153..3276bfe7e3 100644 --- a/integration_test/mix.lock +++ b/integration_test/mix.lock @@ -47,4 +47,6 @@ "telemetry": {:hex, :telemetry, "1.1.0", "a589817034a27eab11144ad24d5c0f9fab1f58173274b1e9bae7074af9cbee51", [:rebar3], [], "hexpm", "b727b2a1f75614774cff2d7565b64d0dfa5bd52ba517f16543e6fc7efcc0df48"}, "telemetry_metrics": {:hex, :telemetry_metrics, "0.6.1", "315d9163a1d4660aedc3fee73f33f1d355dcc76c5c3ab3d59e76e3edf80eef1f", [:mix], [{:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "7be9e0871c41732c233be71e4be11b96e56177bf15dde64a8ac9ce72ac9834c6"}, "telemetry_poller": {:hex, :telemetry_poller, "1.0.0", "db91bb424e07f2bb6e73926fcafbfcbcb295f0193e0a00e825e589a0a47e8453", [:rebar3], [{:telemetry, "~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "b3a24eafd66c3f42da30fc3ca7dda1e9d546c12250a2d60d7b81d264fbec4f6e"}, + "websock": {:hex, :websock, "0.4.3", "184ac396bdcd3dfceb5b74c17d221af659dd559a95b1b92041ecb51c9b728093", [:mix], [], "hexpm", "5e4dd85f305f43fd3d3e25d70bec4a45228dfed60f0f3b072d8eddff335539cf"}, + "websock_adapter": {:hex, :websock_adapter, "0.4.3", "30dd07a9fabf704845baf0176b3e8508d3468539422ffe9ff4229fa39b6277c9", [:mix], [{:bandit, "~> 0.5.9", [hex: :bandit, repo: "hexpm", optional: true]}, {:plug, "~> 1.14.0", [hex: :plug, repo: "hexpm", optional: false]}, {:plug_cowboy, "~> 2.6", [hex: :plug_cowboy, repo: "hexpm", optional: true]}, {:websock, "~> 0.4.3", [hex: :websock, repo: "hexpm", optional: false]}], "hexpm", "304bf61322080bc0e6ea341263b63097f0c801af4638a86f264287ea8fd3a9ff"}, } diff --git a/lib/phoenix/endpoint/cowboy2_adapter.ex b/lib/phoenix/endpoint/cowboy2_adapter.ex index 3f324bfa31..84117a9c7a 100644 --- a/lib/phoenix/endpoint/cowboy2_adapter.ex +++ b/lib/phoenix/endpoint/cowboy2_adapter.ex @@ -137,26 +137,4 @@ 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 - - @doc false - def websocket_upgrade(conn, handler, state, opts) do - cowboy_opts = - opts - |> Enum.flat_map(fn - {:timeout, timeout} -> [idle_timeout: timeout] - {:compress, _} = opt -> [opt] - {:max_frame_size, _} = opt -> [opt] - _other -> [] - end) - |> Map.new() - - process_flags = - opts - |> Keyword.take([:fullsweep_after]) - |> Map.new() - - handler_args = {handler, process_flags, state} - upgrade_args = {Phoenix.Endpoint.Cowboy2Handler, handler_args, cowboy_opts} - Plug.Conn.upgrade_adapter(conn, :websocket, upgrade_args) - end end diff --git a/lib/phoenix/endpoint/cowboy2_handler.ex b/lib/phoenix/endpoint/cowboy2_handler.ex deleted file mode 100644 index c3c97852d1..0000000000 --- a/lib/phoenix/endpoint/cowboy2_handler.ex +++ /dev/null @@ -1,83 +0,0 @@ -defmodule Phoenix.Endpoint.Cowboy2Handler do - @moduledoc false - - @behaviour :cowboy_websocket - - # We never actually call this; it's just here to quell compiler warnings - @impl true - def init(req, state), do: {:cowboy_websocket, req, state} - - defp handle_reply(handler, {:ok, state}), do: {:ok, [handler | state]} - defp handle_reply(handler, {:push, data, state}), do: {:reply, data, [handler | state]} - - defp handle_reply(handler, {:reply, _status, data, state}), - do: {:reply, data, [handler | state]} - - defp handle_reply(handler, {:stop, _reason, state}), do: {:stop, [handler | state]} - - defp handle_control_frame(payload_with_opts, handler_state) do - [handler | state] = handler_state - reply = - if function_exported?(handler, :handle_control, 2) do - handler.handle_control(payload_with_opts, state) - else - {:ok, state} - end - - handle_reply(handler, reply) - end - - @impl true - def websocket_init({handler, process_flags, state}) do - for {key, value} <- process_flags do - :erlang.process_flag(key, value) - end - - {:ok, state} = handler.init(state) - {:ok, [handler | state]} - end - - @impl true - def websocket_handle({opcode, payload}, [handler | state]) when opcode in [:text, :binary] do - handle_reply(handler, handler.handle_in({payload, opcode: opcode}, state)) - end - - def websocket_handle({opcode, payload}, handler_state) when opcode in [:ping, :pong] do - handle_control_frame({payload, opcode: opcode}, handler_state) - end - - def websocket_handle(opcode, handler_state) when opcode in [:ping, :pong] do - handle_control_frame({nil, opcode: opcode}, handler_state) - end - - def websocket_handle(_other, handler_state) do - {:ok, handler_state} - end - - @impl true - def websocket_info(message, [handler | state]) do - handle_reply(handler, handler.handle_info(message, state)) - end - - @impl true - def terminate({:error, :closed}, _req, [handler | state]) do - handler.terminate(:closed, state) - end - - def terminate({:remote, :closed}, _req, [handler | state]) do - handler.terminate(:closed, state) - end - - def terminate({:remote, code, _}, _req, [handler | state]) - when code in 1000..1003 or code in 1005..1011 or code == 1015 do - handler.terminate(:closed, state) - end - - def terminate(:remote, _req, [handler | state]) do - handler.terminate(:closed, state) - end - - def terminate(reason, _req, [handler | state]) do - handler.terminate(reason, state) - end -end diff --git a/lib/phoenix/transports/websocket.ex b/lib/phoenix/transports/websocket.ex index 118949139d..16be35bb54 100644 --- a/lib/phoenix/transports/websocket.ex +++ b/lib/phoenix/transports/websocket.ex @@ -1,5 +1,18 @@ defmodule Phoenix.Transports.WebSocket do @moduledoc false + # + # How WebSockets Work In Phoenix + # + # WebSocket support in Phoenix is implemented on top of the `WebSockAdapter` library. Upgrade + # requests from clients originate as regular HTTP requests that get routed to this module via + # Plug. These requests are then upgraded to WebSocket connections via + # `WebSockAdapter.upgrade/4`, which takes as an argument the handler for a given socket endpoint + # as configured in the application's Endpoint. This handler module must implement the + # transport-agnostic `Phoenix.Socket.Transport` behaviour (this same behaviour is also used for + # other transports such as long polling). Because this behaviour is a superset of the `WebSock` + # behaviour, the `WebSock` library is able to use the callbacks in the `WebSock` behaviour to + # call this handler module directly for the rest of the WebSocket connection's lifetime. + # @behaviour Plug import Plug.Conn @@ -44,12 +57,8 @@ defmodule Phoenix.Transports.WebSocket do case handler.connect(config) do {:ok, arg} -> - upgrade = - conn.private[:phoenix_websocket_upgrade] || - (&Phoenix.Endpoint.Cowboy2Adapter.websocket_upgrade/4) - conn - |> upgrade.(handler, arg, opts) + |> WebSockAdapter.upgrade(handler, arg, opts) |> halt() :error -> diff --git a/mix.exs b/mix.exs index ca7923691f..2c832e7923 100644 --- a/mix.exs +++ b/mix.exs @@ -72,9 +72,9 @@ defmodule Phoenix.MixProject do {:plug_crypto, "~> 1.2"}, {:telemetry, "~> 0.4 or ~> 1.0"}, {:phoenix_pubsub, "~> 2.1"}, - {:phoenix_view, "~> 2.0", optional: true}, {:phoenix_template, "~> 1.0"}, + {:websock_adapter, "~> 0.4"}, # TODO drop castore when we require OTP 25+ {:castore, ">= 0.0.0"}, diff --git a/mix.lock b/mix.lock index 1082c3224d..4a71a6e939 100644 --- a/mix.lock +++ b/mix.lock @@ -34,4 +34,6 @@ "telemetry": {:hex, :telemetry, "1.1.0", "a589817034a27eab11144ad24d5c0f9fab1f58173274b1e9bae7074af9cbee51", [:rebar3], [], "hexpm", "b727b2a1f75614774cff2d7565b64d0dfa5bd52ba517f16543e6fc7efcc0df48"}, "telemetry_metrics": {:hex, :telemetry_metrics, "0.6.1", "315d9163a1d4660aedc3fee73f33f1d355dcc76c5c3ab3d59e76e3edf80eef1f", [:mix], [{:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "7be9e0871c41732c233be71e4be11b96e56177bf15dde64a8ac9ce72ac9834c6"}, "telemetry_poller": {:hex, :telemetry_poller, "1.0.0", "db91bb424e07f2bb6e73926fcafbfcbcb295f0193e0a00e825e589a0a47e8453", [:rebar3], [{:telemetry, "~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "b3a24eafd66c3f42da30fc3ca7dda1e9d546c12250a2d60d7b81d264fbec4f6e"}, + "websock": {:hex, :websock, "0.4.3", "184ac396bdcd3dfceb5b74c17d221af659dd559a95b1b92041ecb51c9b728093", [:mix], [], "hexpm", "5e4dd85f305f43fd3d3e25d70bec4a45228dfed60f0f3b072d8eddff335539cf"}, + "websock_adapter": {:hex, :websock_adapter, "0.4.3", "30dd07a9fabf704845baf0176b3e8508d3468539422ffe9ff4229fa39b6277c9", [:mix], [{:bandit, "~> 0.5.9", [hex: :bandit, repo: "hexpm", optional: true]}, {:plug, "~> 1.14.0", [hex: :plug, repo: "hexpm", optional: false]}, {:plug_cowboy, "~> 2.6", [hex: :plug_cowboy, repo: "hexpm", optional: true]}, {:websock, "~> 0.4.3", [hex: :websock, repo: "hexpm", optional: false]}], "hexpm", "304bf61322080bc0e6ea341263b63097f0c801af4638a86f264287ea8fd3a9ff"}, }