Current section
Files
Jump to
Current section
Files
gaoxy_websock_adapter
gaoxy.patch
gaoxy.patch
diff --git a/lib/websock_adapter.ex b/lib/websock_adapter.ex
index de367e6d0b242f5333ab69a802ef7987c5cd293e..71123c83e6a2527200a4de3d0b6f5672dfafd187 100644
--- a/lib/websock_adapter.ex
+++ b/lib/websock_adapter.ex
@@ -72,6 +72,7 @@ defmodule WebSockAdapter do
Plug.Conn.upgrade_adapter(conn, :websocket, tuple)
end
+ defp tuple_for(Gaoxy.PlugAdapter, websock, state, opts), do: {websock, state, opts}
defp tuple_for(Bandit.Adapter, websock, state, opts), do: {websock, state, opts}
# Support for adapters as specified prior to Bandit 1.4
defp tuple_for(Bandit.HTTP1.Adapter, websock, state, opts), do: {websock, state, opts}
diff --git a/lib/websock_adapter/upgrade_validation.ex b/lib/websock_adapter/upgrade_validation.ex
index 313310dff13ae0e321286027697753190db56cff..732c83e3d7f704dbb73e7301178dc836bd40b4df 100644
--- a/lib/websock_adapter/upgrade_validation.ex
+++ b/lib/websock_adapter/upgrade_validation.ex
@@ -24,6 +24,13 @@ defmodule WebSockAdapter.UpgradeValidation do
`{:error, reason}` if not
"""
@spec validate_upgrade(Plug.Conn.t()) :: :ok | {:error, String.t()}
+ def validate_upgrade(%Plug.Conn{
+ adapter: {Gaoxy.PlugAdapter, %{request: %{version: version} = request}}
+ })
+ when version in [:"HTTP/2", :"HTTP/3"] do
+ validate_gaoxy_extended(request)
+ end
+
def validate_upgrade(conn) do
case Plug.Conn.get_http_protocol(conn) do
:"HTTP/1.1" -> validate_upgrade_http1(conn)
@@ -32,6 +39,17 @@ defmodule WebSockAdapter.UpgradeValidation do
end
end
+ # Phoenix may see a temporary GET view. Gaoxy's immutable native metadata
+ # supplies the RFC 8441/9220 method/protocol without a reverse dependency.
+ defp validate_gaoxy_extended(%{method: "CONNECT", protocol: "websocket", headers: headers})
+ when is_list(headers) do
+ if for({"sec-websocket-version", value} <- headers, do: value) == ["13"],
+ do: :ok,
+ else: {:error, "'sec-websocket-version' header must equal '13'"}
+ end
+
+ defp validate_gaoxy_extended(_), do: {:error, "invalid native extended WebSocket request"}
+
@doc """
Raising variant of `validate_upgrade/1`.
diff --git a/test/gaoxy_dispatch_test.exs b/test/gaoxy_dispatch_test.exs
new file mode 100644
index 0000000000000000000000000000000000000000..6957c6b6ed6796b827dcf3cb85a0469d1def8c1b
--- /dev/null
+++ b/test/gaoxy_dispatch_test.exs
@@ -0,0 +1,78 @@
+# This isolated dependency test supplies the consumer's public adapter contract.
+defmodule Gaoxy.PlugAdapter do
+ def upgrade(:payload, :websocket, {handler, state, options}),
+ do: {:ok, {:native, handler, state, options}}
+
+ def upgrade(%{request: _} = payload, :websocket, {handler, state, options}),
+ do: {:ok, {:native, payload, handler, state, options}}
+
+ def get_http_protocol(%{request: %{version: version}}), do: version
+end
+
+defmodule WebSockAdapter.GaoxyDispatchTest do
+ use ExUnit.Case, async: true
+
+ test "dispatches the native Gaoxy adapter tuple" do
+ conn = %Plug.Conn{adapter: {Gaoxy.PlugAdapter, :payload}}
+ upgraded = WebSockAdapter.upgrade(conn, MyWebSock, :state, early_validate_upgrade: false)
+ assert upgraded.state == :upgraded
+ assert {Gaoxy.PlugAdapter, {:native, MyWebSock, :state, options}} = upgraded.adapter
+ assert options[:max_frame_size] == 10_000_000
+ end
+
+ test "Gaoxy H2 and H3 use immutable CONNECT metadata while Phoenix sees GET" do
+ for version <- [:"HTTP/2", :"HTTP/3"] do
+ request = %{
+ method: "CONNECT",
+ protocol: "websocket",
+ version: version,
+ headers: [{"sec-websocket-version", "13"}]
+ }
+
+ conn = %Plug.Conn{
+ method: "GET",
+ req_headers: request.headers,
+ adapter: {Gaoxy.PlugAdapter, %{request: request}}
+ }
+
+ result = WebSockAdapter.upgrade(conn, MyWebSock, :state, [])
+ assert result.state == :upgraded
+
+ assert {Gaoxy.PlugAdapter, {:native, %{request: ^request}, MyWebSock, :state, _}} =
+ result.adapter
+ end
+ end
+
+ test "apparent GET cannot replace missing or invalid native extended metadata" do
+ for request <- [
+ %{
+ method: "GET",
+ protocol: "websocket",
+ version: :"HTTP/2",
+ headers: [{"sec-websocket-version", "13"}]
+ },
+ %{
+ method: "CONNECT",
+ protocol: "other",
+ version: :"HTTP/3",
+ headers: [{"sec-websocket-version", "13"}]
+ },
+ %{
+ method: "CONNECT",
+ protocol: "websocket",
+ version: :"HTTP/3",
+ headers: [{"sec-websocket-version", "12"}]
+ }
+ ] do
+ conn = %Plug.Conn{
+ method: "GET",
+ req_headers: [{"sec-websocket-version", "13"}],
+ adapter: {Gaoxy.PlugAdapter, %{request: request}}
+ }
+
+ assert_raise WebSockAdapter.UpgradeError, fn ->
+ WebSockAdapter.upgrade(conn, MyWebSock, :state, [])
+ end
+ end
+ end
+end