update
This commit is contained in:
BIN
phoenix/deps/websock_adapter/.hex
Normal file
BIN
phoenix/deps/websock_adapter/.hex
Normal file
Binary file not shown.
68
phoenix/deps/websock_adapter/CHANGELOG.md
Normal file
68
phoenix/deps/websock_adapter/CHANGELOG.md
Normal file
@@ -0,0 +1,68 @@
|
||||
## 0.5.9 (4 Nov 2025)
|
||||
|
||||
### Enhancements
|
||||
|
||||
* Add support for deflate_options configuration (#21, thanks @studzien!)
|
||||
|
||||
## 0.5.8 (12 Nov 2024)
|
||||
|
||||
### Enhancements
|
||||
|
||||
* Improve handling of crashes during WebSock.init/1 calls (#20)
|
||||
|
||||
## 0.5.7 (9 Aug 2024)
|
||||
|
||||
### Enhancements
|
||||
|
||||
* Support use within Plug.Adapters.Test.Conn based tests
|
||||
|
||||
## 0.5.6 (25 Mar 2024)
|
||||
|
||||
### Enhancements
|
||||
|
||||
* Support Bandit 1.4+
|
||||
* Minor mix packaging improvements
|
||||
|
||||
## 0.5.5 (30 Oct 2023)
|
||||
|
||||
### Enhancements
|
||||
|
||||
* Add a `:max_heap_size` option (#15, thanks @v0idpwn!)
|
||||
* Validate client upgrade request at the time of upgrade (#14)
|
||||
|
||||
## 0.5.4 (15 Aug 2023)
|
||||
|
||||
### Enhancements
|
||||
|
||||
* Add ability to send preamble frames when closing a connection (#13)
|
||||
* Improve test coverage (#12)
|
||||
|
||||
## 0.5.3 (15 Jun 2023)
|
||||
|
||||
### Enhancements
|
||||
|
||||
* Support draining signals as used by Phoenix (#10)
|
||||
|
||||
## 0.5.2 (15 Jun 2023)
|
||||
|
||||
### Changes
|
||||
|
||||
* Allow the sending of some extra options to Cowboy (#9)
|
||||
|
||||
### Fixes
|
||||
|
||||
* Allow terminate/2 to be optional for Cowboy adapter (#8)
|
||||
* Allow nil detail reasons when closing connections in Cowboy
|
||||
|
||||
## 0.5.1 (24 Apr 2023)
|
||||
|
||||
### Changes
|
||||
|
||||
* Loosen optional dependency versioning on Bandit
|
||||
* Add examples to documentation, correct minor typos
|
||||
|
||||
## 0.5.0 (13 Mar 2023)
|
||||
|
||||
### Enhancements
|
||||
|
||||
* Add support for `:stop` return tuple to specify explicit close code & detail message
|
||||
21
phoenix/deps/websock_adapter/LICENSE
Normal file
21
phoenix/deps/websock_adapter/LICENSE
Normal file
@@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2022 Mat Trudel
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
102
phoenix/deps/websock_adapter/README.md
Normal file
102
phoenix/deps/websock_adapter/README.md
Normal file
@@ -0,0 +1,102 @@
|
||||
# WebSockAdapter
|
||||
|
||||
[](https://github.com/phoenixframework/websock_adapter/actions)
|
||||
[](https://hexdocs.pm/websock_adapter)
|
||||
[](https://hex.pm/packages/websock_adapter)
|
||||
|
||||
WebSockAdapter is a library of adapters from common Web Servers to the
|
||||
`WebSock` specification. WebSockAdapter currently supports
|
||||
[Bandit](https://github.com/mtrudel/bandit) and
|
||||
[Cowboy](https://github.com/ninenines/cowboy).
|
||||
|
||||
For details on the `WebSock` specification, consult the
|
||||
[WebSock](https://hexdocs.pm/websock) documentation.
|
||||
|
||||
## Usage
|
||||
|
||||
WebSockAdapter makes it easy to upgrade Plug connections to WebSock connections.
|
||||
Here's a simple example:
|
||||
|
||||
```elixir
|
||||
defmodule EchoServer do
|
||||
def init(args) do
|
||||
{:ok, []}
|
||||
end
|
||||
|
||||
def handle_in({"ping", [opcode: :text]}, state) do
|
||||
{:reply, :ok, {:text, "pong"}, state}
|
||||
end
|
||||
end
|
||||
|
||||
defmodule MyPlug do
|
||||
use Plug.Router
|
||||
|
||||
plug Plug.Logger
|
||||
plug :match
|
||||
plug :dispatch
|
||||
|
||||
get "/" do
|
||||
# Provide the user with some useful instructions to copy & paste into their inspector
|
||||
send_resp(conn, 200, """
|
||||
Use the JavaScript console to interact using websockets
|
||||
|
||||
sock = new WebSocket("ws://localhost:4000/websocket")
|
||||
sock.addEventListener("message", console.log)
|
||||
sock.addEventListener("open", () => sock.send("ping"))
|
||||
""")
|
||||
end
|
||||
|
||||
get "/websocket" do
|
||||
conn
|
||||
|> WebSockAdapter.upgrade(EchoServer, [], timeout: 60_000)
|
||||
|> halt()
|
||||
end
|
||||
|
||||
match _ do
|
||||
send_resp(conn, 404, "not found")
|
||||
end
|
||||
end
|
||||
```
|
||||
|
||||
This simple example illustrates many of the useful features of WebSock / WebSockAdapters:
|
||||
|
||||
* Implementing a WebSocket server is a single module, and looks & acts much like
|
||||
a GenServer does
|
||||
* It's easy to pass state from the `WebSockAdapter.upgrade/3`
|
||||
call & have it show up in your WebSock callbacks
|
||||
* Upgrades are handled as a plain Plug call. You are able to route requests to
|
||||
your upgrade endpoint using all of the power of the Plug API
|
||||
|
||||
If you're looking for more detail, Benjamin Milde has a [great blog
|
||||
post](https://kobrakai.de/kolumne/bare-websockets) that goes a bit deeper than
|
||||
the simple example above.
|
||||
|
||||
## Upgrade Validation
|
||||
|
||||
Since `0.5.5`, WebSockAdapter validates requests made via
|
||||
`WebSockAdapter.upgrade/3` (this was and continues to also be done by the
|
||||
underlying web server, but since the server's validation occurs after the
|
||||
`Plug.call/2` lifecycle completes it's difficult to meaningfully handle such
|
||||
errors). This validation examines the request for conformance to the clauses
|
||||
laid out in RFC6455§4.2, as well as RFC8441§5 for HTTP/2 connections. Requests
|
||||
which do not satisfy the requirements laid out in those specifications will
|
||||
result in a `WebSockAdapter.UpgradeError` being raised, containing
|
||||
details of the reason for the failure
|
||||
|
||||
## Installation
|
||||
|
||||
The websock_adapter package can be installed by adding `websock_adapter` to your list of dependencies in `mix.exs`:
|
||||
|
||||
```elixir
|
||||
def deps do
|
||||
[
|
||||
{:websock_adapter, "~> 0.5"}
|
||||
]
|
||||
end
|
||||
```
|
||||
|
||||
Documentation can be found at <https://hexdocs.pm/websock_adapter>.
|
||||
|
||||
## License
|
||||
|
||||
MIT
|
||||
36
phoenix/deps/websock_adapter/hex_metadata.config
Normal file
36
phoenix/deps/websock_adapter/hex_metadata.config
Normal file
@@ -0,0 +1,36 @@
|
||||
{<<"links">>,
|
||||
[{<<"GitHub">>,<<"https://github.com/phoenixframework/websock_adapter">>}]}.
|
||||
{<<"name">>,<<"websock_adapter">>}.
|
||||
{<<"version">>,<<"0.5.9">>}.
|
||||
{<<"description">>,<<"A set of WebSock adapters for common web servers">>}.
|
||||
{<<"elixir">>,<<"~> 1.9">>}.
|
||||
{<<"app">>,<<"websock_adapter">>}.
|
||||
{<<"files">>,
|
||||
[<<"lib">>,<<"lib/websock_adapter.ex">>,<<"lib/websock_adapter">>,
|
||||
<<"lib/websock_adapter/upgrade_error.ex">>,
|
||||
<<"lib/websock_adapter/upgrade_validation.ex">>,
|
||||
<<"lib/websock_adapter/cowboy_adapter.ex">>,<<"mix.exs">>,<<"README.md">>,
|
||||
<<"LICENSE">>,<<"CHANGELOG.md">>]}.
|
||||
{<<"licenses">>,[<<"MIT">>]}.
|
||||
{<<"requirements">>,
|
||||
[[{<<"name">>,<<"websock">>},
|
||||
{<<"app">>,<<"websock">>},
|
||||
{<<"optional">>,false},
|
||||
{<<"requirement">>,<<"~> 0.5">>},
|
||||
{<<"repository">>,<<"hexpm">>}],
|
||||
[{<<"name">>,<<"plug">>},
|
||||
{<<"app">>,<<"plug">>},
|
||||
{<<"optional">>,false},
|
||||
{<<"requirement">>,<<"~> 1.14">>},
|
||||
{<<"repository">>,<<"hexpm">>}],
|
||||
[{<<"name">>,<<"bandit">>},
|
||||
{<<"app">>,<<"bandit">>},
|
||||
{<<"optional">>,true},
|
||||
{<<"requirement">>,<<">= 0.6.0">>},
|
||||
{<<"repository">>,<<"hexpm">>}],
|
||||
[{<<"name">>,<<"plug_cowboy">>},
|
||||
{<<"app">>,<<"plug_cowboy">>},
|
||||
{<<"optional">>,true},
|
||||
{<<"requirement">>,<<"~> 2.6">>},
|
||||
{<<"repository">>,<<"hexpm">>}]]}.
|
||||
{<<"build_tools">>,[<<"mix">>]}.
|
||||
105
phoenix/deps/websock_adapter/lib/websock_adapter.ex
Normal file
105
phoenix/deps/websock_adapter/lib/websock_adapter.ex
Normal file
@@ -0,0 +1,105 @@
|
||||
defmodule WebSockAdapter do
|
||||
@moduledoc """
|
||||
Defines adapters to allow common Web Servers to serve applications via the `WebSock` API.
|
||||
Also provides a consistent upgrade facility to upgrade `Plug.Conn` requests to `WebSock`
|
||||
connections for supported servers.
|
||||
"""
|
||||
|
||||
@typedoc "The type of a supported connection option"
|
||||
@type connection_opt ::
|
||||
{:compress, boolean()}
|
||||
| {:timeout, timeout()}
|
||||
| {:max_frame_size, non_neg_integer()}
|
||||
| {:fullsweep_after, non_neg_integer()}
|
||||
| {:max_heap_size, :erlang.max_heap_size()}
|
||||
| {:validate_utf8, boolean()}
|
||||
| {:active_n, integer()}
|
||||
|
||||
@doc """
|
||||
Upgrades the provided `Plug.Conn` connection request to a `WebSock` connection using the
|
||||
provided `WebSock` compliant module as a handler.
|
||||
|
||||
This function returns the passed `conn` set to an `:upgraded` state. If `early_validate_upgrade`
|
||||
is set to true (as it is by default), the request is first examined to determine if it
|
||||
represents a valid WebSocket upgrade. If errors are discovered in the request, a
|
||||
`WebSockAdapter.UpgradeError` is raised containing information about the failure
|
||||
|
||||
The provided `state` value will be used as the argument for `c:WebSock.init/1` once the WebSocket
|
||||
connection has been successfully negotiated.
|
||||
|
||||
The `opts` keyword list argument allows a number of options to be set on the WebSocket
|
||||
connection. Not all options may be supported by the underlying HTTP server. Possible values are
|
||||
as follows:
|
||||
|
||||
* `early_validate_upgrade`: A boolean indicating whether or not WebSockAdapter should attempt to
|
||||
validate the WebSocket upgrade request before returning from this call. The underlying webserver
|
||||
may still perform its own validation during the actual upgrade process, but since this occurs
|
||||
after the `c:Plug.call/2` lifecycle it can be difficult to meaningfully handle failed upgrades.
|
||||
Having WebSockAdapter do its own checks as part of this call helps to alleviate this. Defaults
|
||||
to `true`
|
||||
* `timeout`: The number of milliseconds to wait after no client data is received before
|
||||
closing the connection. Defaults to `60_000`
|
||||
* `compress`: Whether or not to accept negotiation of a compression extension with the client.
|
||||
Defaults to `false`
|
||||
* `max_frame_size`: The maximum frame size to accept, in octets. If a frame size larger than this
|
||||
is received the connection will be closed. Defaults to `:infinity`
|
||||
* `fullsweep_after`: The maximum number of garbage collections before forcing a fullsweep of
|
||||
the WebSocket connection process. Setting this option requires OTP 24 or newer
|
||||
* `max_heap_size`: The maximum size of the websocket process heap in words, or a configuration
|
||||
map. See `:erlang.max_heap_size()` for more info
|
||||
* `validate_utf8`: Whether the server should verify that the payload of text and close frames is valid UTF-8.
|
||||
This is required by the protocol specification but in some cases it may be more interesting to disable it
|
||||
in order to save resources. Note that binary frames do not have this UTF-8 requirement and are what should be
|
||||
used under normal circumstances if necessary
|
||||
* `active_n`: (Cowboy only) The number of packets Cowboy will request from the socket at once. This can be used to tweak
|
||||
the performance of the server. Higher values reduce the number of times Cowboy need to request more
|
||||
packets from the port driver at the expense of potentially higher memory being used.
|
||||
This option does not apply to Websocket over HTTP/2
|
||||
* `deflate_options`: A keyword list of options to pass to the deflate library.
|
||||
See `Bandit` or `:cow_ws` documentation for more details
|
||||
"""
|
||||
@spec upgrade(Plug.Conn.t(), WebSock.impl(), WebSock.state(), [connection_opt()]) ::
|
||||
Plug.Conn.t()
|
||||
def upgrade(%{adapter: {adapter, _}} = conn, websock, state, opts) do
|
||||
# Do this first so we can identify unsupported adapters
|
||||
tuple = tuple_for(adapter, websock, state, opts)
|
||||
|
||||
if Keyword.get(opts, :early_validate_upgrade, true) do
|
||||
WebSockAdapter.UpgradeValidation.validate_upgrade!(conn)
|
||||
end
|
||||
|
||||
Plug.Conn.upgrade_adapter(conn, :websocket, tuple)
|
||||
end
|
||||
|
||||
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}
|
||||
defp tuple_for(Bandit.HTTP2.Adapter, websock, state, opts), do: {websock, state, opts}
|
||||
|
||||
defp tuple_for(Plug.Cowboy.Conn, websock, state, opts) do
|
||||
cowboy_opts =
|
||||
opts
|
||||
|> Enum.flat_map(fn
|
||||
{:timeout, timeout} -> [idle_timeout: timeout]
|
||||
{:compress, _} = opt -> [opt]
|
||||
{:max_frame_size, _} = opt -> [opt]
|
||||
{:validate_utf8, _} = opt -> [opt]
|
||||
{:active_n, _} = opt -> [opt]
|
||||
{:deflate_options, deflate_options} -> [deflate_opts: deflate_options]
|
||||
_other -> []
|
||||
end)
|
||||
|> Map.new()
|
||||
|
||||
process_flags =
|
||||
opts
|
||||
|> Keyword.take([:fullsweep_after, :max_heap_size])
|
||||
|> Map.new()
|
||||
|
||||
{WebSockAdapter.CowboyAdapter, {websock, process_flags, state}, cowboy_opts}
|
||||
end
|
||||
|
||||
defp tuple_for(Plug.Adapters.Test.Conn, websock, state, opts), do: {websock, state, opts}
|
||||
|
||||
defp tuple_for(adapter, _websock, _state, _opts),
|
||||
do: raise(ArgumentError, "Unknown adapter #{inspect(adapter)}")
|
||||
end
|
||||
@@ -0,0 +1,109 @@
|
||||
if Code.ensure_loaded?(:cowboy_websocket) do
|
||||
defmodule WebSockAdapter.CowboyAdapter do
|
||||
@moduledoc false
|
||||
|
||||
@behaviour :cowboy_websocket
|
||||
|
||||
# Cowboy never actually calls this implementation due to the way that Plug.Cowboy signals
|
||||
# upgrades; it's just here to quell compiler warnings
|
||||
@impl true
|
||||
def init(req, state), do: {:cowboy_websocket, req, state}
|
||||
|
||||
@impl true
|
||||
def websocket_init({handler, process_flags, state}) do
|
||||
for {key, value} <- process_flags do
|
||||
:erlang.process_flag(key, value)
|
||||
end
|
||||
|
||||
handler.init(state)
|
||||
|> handle_reply(handler)
|
||||
end
|
||||
|
||||
@impl true
|
||||
def websocket_handle({opcode, payload}, {handler, state}) when opcode in [:text, :binary] do
|
||||
handler.handle_in({payload, opcode: opcode}, state)
|
||||
|> handle_reply(handler)
|
||||
end
|
||||
|
||||
def websocket_handle({opcode, payload}, {handler, state}) when opcode in [:ping, :pong] do
|
||||
if function_exported?(handler, :handle_control, 2) do
|
||||
handler.handle_control({payload, opcode: opcode}, state)
|
||||
else
|
||||
{:ok, state}
|
||||
end
|
||||
|> handle_reply(handler)
|
||||
end
|
||||
|
||||
def websocket_handle(opcode, handler_state) when opcode in [:ping, :pong] do
|
||||
websocket_handle({opcode, nil}, handler_state)
|
||||
end
|
||||
|
||||
def websocket_handle(_other, handler_state) do
|
||||
{:ok, handler_state}
|
||||
end
|
||||
|
||||
@impl true
|
||||
def websocket_info(message, {handler, state}) do
|
||||
handler.handle_info(message, state)
|
||||
|> handle_reply(handler)
|
||||
end
|
||||
|
||||
@impl true
|
||||
def terminate({:remote, code, _}, _req, {handler, state})
|
||||
when code in 1000..1003 or code in 1005..1011 or code == 1015 do
|
||||
if function_exported?(handler, :terminate, 2) do
|
||||
handler.terminate(:remote, state)
|
||||
end
|
||||
end
|
||||
|
||||
def terminate({:remote, :closed}, _req, {handler, state}) do
|
||||
if function_exported?(handler, :terminate, 2) do
|
||||
handler.terminate(:closed, state)
|
||||
end
|
||||
end
|
||||
|
||||
def terminate(:stop, _req, {handler, state}) do
|
||||
if function_exported?(handler, :terminate, 2) do
|
||||
handler.terminate(:normal, state)
|
||||
end
|
||||
end
|
||||
|
||||
def terminate(reason, _req, {handler, state}) do
|
||||
if function_exported?(handler, :terminate, 2) do
|
||||
handler.terminate(reason, state)
|
||||
end
|
||||
end
|
||||
|
||||
# If websocket_init (or the handler's init) raises an error, state may still
|
||||
# be the 3-element tuple that we get at initialization time. Since we have
|
||||
# not yet initialized the websock at this point, just terminate and let
|
||||
# Cowboy do its usual logging
|
||||
def terminate(_reason, _req, {_handler, _process_flags, _state}), do: :ok
|
||||
|
||||
defp handle_reply({:ok, state}, handler), do: {:ok, {handler, state}}
|
||||
defp handle_reply({:push, data, state}, handler), do: {:reply, data, {handler, state}}
|
||||
|
||||
defp handle_reply({:reply, _status, data, state}, handler),
|
||||
do: {:reply, data, {handler, state}}
|
||||
|
||||
defp handle_reply({:stop, {:shutdown, :restart}, state}, handler),
|
||||
do: {:reply, {:close, _restart_code = 1012, <<>>}, {handler, state}}
|
||||
|
||||
defp handle_reply({:stop, _reason, state}, handler), do: {:stop, {handler, state}}
|
||||
|
||||
defp handle_reply({:stop, _reason, close_detail, state}, handler),
|
||||
do: {:reply, close_frame_for_detail(close_detail), {handler, state}}
|
||||
|
||||
defp handle_reply({:stop, _reason, close_detail, messages, state}, handler),
|
||||
do: {:reply, messages ++ [close_frame_for_detail(close_detail)], {handler, state}}
|
||||
|
||||
defp close_frame_for_detail(code) when is_integer(code),
|
||||
do: {:close, code, <<>>}
|
||||
|
||||
defp close_frame_for_detail({code, nil}) when is_integer(code),
|
||||
do: {:close, code, <<>>}
|
||||
|
||||
defp close_frame_for_detail({code, detail}) when is_integer(code) and is_binary(detail),
|
||||
do: {:close, code, detail}
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,3 @@
|
||||
defmodule WebSockAdapter.UpgradeError do
|
||||
defexception [:message]
|
||||
end
|
||||
@@ -0,0 +1,102 @@
|
||||
defmodule WebSockAdapter.UpgradeValidation do
|
||||
@moduledoc """
|
||||
Provides validation of WebSocket upgrade requests as described in RFC6455§4.2 & RFC8441§5
|
||||
|
||||
The `validate_upgrade/1` function is called internally by `WebSockAdapter.upgrade/4`; there is
|
||||
no need to call it yourself before attempting an upgrade (though doing so is harmless)
|
||||
"""
|
||||
|
||||
# credo:disable-for-this-file Credo.Check.Refactor.RedundantWithClauseResult
|
||||
# credo:disable-for-this-file Credo.Check.Design.AliasUsage
|
||||
|
||||
@doc """
|
||||
Validates that the request satisfies the requirements to issue a WebSocket upgrade response.
|
||||
|
||||
Validations are performed based on the clauses laid out in RFC6455§4.2 & RFC8441§5
|
||||
|
||||
This function does not actually perform an upgrade or change the connection in any way.
|
||||
Regardless of whether or not this function indicates a satisfactory connection, the
|
||||
underlying web server MAY still choose to not perform the upgrade (this scenario likely
|
||||
indicates a discrepancy between the validations done here and those done in the underlying web
|
||||
server & would merit further investigation)
|
||||
|
||||
Returns `:ok` if the connection satisfies the requirements for a WebSocket upgrade, and
|
||||
`{:error, reason}` if not
|
||||
"""
|
||||
@spec validate_upgrade(Plug.Conn.t()) :: :ok | {:error, String.t()}
|
||||
def validate_upgrade(conn) do
|
||||
case Plug.Conn.get_http_protocol(conn) do
|
||||
:"HTTP/1.1" -> validate_upgrade_http1(conn)
|
||||
:"HTTP/2" -> validate_upgrade_http2(conn)
|
||||
other -> {:error, "HTTP version #{other} unsupported"}
|
||||
end
|
||||
end
|
||||
|
||||
@doc """
|
||||
Raising variant of `validate_upgrade/1`.
|
||||
|
||||
Returns `:ok` if the connection satisfies the requirements for a WebSocket upgrade, and raises
|
||||
a `WebSockAdapter.UpgradeError` error if validation fails.
|
||||
"""
|
||||
@spec validate_upgrade!(Plug.Conn.t()) :: :ok
|
||||
def validate_upgrade!(conn) do
|
||||
case validate_upgrade(conn) do
|
||||
:ok -> :ok
|
||||
{:error, reason} -> raise WebSockAdapter.UpgradeError, reason
|
||||
end
|
||||
end
|
||||
|
||||
# Validate the conn per RFC6455§4.2.1
|
||||
defp validate_upgrade_http1(conn) do
|
||||
with :ok <- assert_method(conn, "GET"),
|
||||
:ok <- assert_header_nonempty(conn, "host"),
|
||||
:ok <- assert_header_contains(conn, "connection", "upgrade"),
|
||||
:ok <- assert_header_contains(conn, "upgrade", "websocket"),
|
||||
:ok <- assert_header_nonempty(conn, "sec-websocket-key"),
|
||||
:ok <- assert_header_equals(conn, "sec-websocket-version", "13") do
|
||||
:ok
|
||||
end
|
||||
end
|
||||
|
||||
# Validate the conn per RFC8441§5. Note that pseudo headers are not exposed
|
||||
# through Plug so we cannot test for those clauses here
|
||||
defp validate_upgrade_http2(conn) do
|
||||
with :ok <- assert_method(conn, "CONNECT"),
|
||||
:ok <- assert_header_equals(conn, "sec-websocket-version", "13") do
|
||||
:ok
|
||||
end
|
||||
end
|
||||
|
||||
defp assert_method(conn, verb) do
|
||||
case conn.method do
|
||||
^verb -> :ok
|
||||
other -> {:error, "HTTP method #{other} unsupported"}
|
||||
end
|
||||
end
|
||||
|
||||
defp assert_header_nonempty(conn, header) do
|
||||
case Plug.Conn.get_req_header(conn, header) do
|
||||
[] -> {:error, "'#{header}' header is absent"}
|
||||
_ -> :ok
|
||||
end
|
||||
end
|
||||
|
||||
defp assert_header_equals(conn, header, expected) do
|
||||
case Plug.Conn.get_req_header(conn, header) |> Enum.map(&String.downcase(&1, :ascii)) do
|
||||
[^expected] -> :ok
|
||||
value -> {:error, "'#{header}' header must equal '#{expected}', got #{inspect(value)}"}
|
||||
end
|
||||
end
|
||||
|
||||
defp assert_header_contains(conn, header, needle) do
|
||||
haystack = Plug.Conn.get_req_header(conn, header)
|
||||
|
||||
haystack
|
||||
|> Enum.flat_map(&Plug.Conn.Utils.list/1)
|
||||
|> Enum.any?(&(String.downcase(&1, :ascii) == needle))
|
||||
|> case do
|
||||
true -> :ok
|
||||
false -> {:error, "'#{header}' header must contain '#{needle}', got #{inspect(haystack)}"}
|
||||
end
|
||||
end
|
||||
end
|
||||
47
phoenix/deps/websock_adapter/mix.exs
Normal file
47
phoenix/deps/websock_adapter/mix.exs
Normal file
@@ -0,0 +1,47 @@
|
||||
defmodule WebSockAdapter.MixProject do
|
||||
use Mix.Project
|
||||
|
||||
def project do
|
||||
[
|
||||
app: :websock_adapter,
|
||||
version: "0.5.9",
|
||||
elixir: "~> 1.9",
|
||||
start_permanent: Mix.env() == :prod,
|
||||
deps: deps(),
|
||||
dialyzer: dialyzer(),
|
||||
name: "WebSockAdapter",
|
||||
description: "A set of WebSock adapters for common web servers",
|
||||
source_url: "https://github.com/phoenixframework/websock_adapter",
|
||||
package: [
|
||||
files: ["lib", "mix.exs", "README*", "LICENSE*", "CHANGELOG*"],
|
||||
maintainers: ["Mat Trudel"],
|
||||
licenses: ["MIT"],
|
||||
links: %{"GitHub" => "https://github.com/phoenixframework/websock_adapter"}
|
||||
]
|
||||
]
|
||||
end
|
||||
|
||||
def application do
|
||||
[]
|
||||
end
|
||||
|
||||
defp deps do
|
||||
[
|
||||
{:websock, "~> 0.5"},
|
||||
{:plug, "~> 1.14"},
|
||||
{:bandit, ">= 0.6.0", optional: true},
|
||||
{:plug_cowboy, "~> 2.6", optional: true},
|
||||
{:ex_doc, ">= 0.0.0", only: :dev, runtime: false},
|
||||
{:dialyxir, "~> 1.0", only: [:dev, :test], runtime: false},
|
||||
{:credo, "~> 1.0", only: [:dev, :test], runtime: false}
|
||||
]
|
||||
end
|
||||
|
||||
defp dialyzer do
|
||||
[
|
||||
plt_add_apps: [:cowboy],
|
||||
plt_core_path: "priv/plts",
|
||||
plt_file: {:no_warn, "priv/plts/dialyzer.plt"}
|
||||
]
|
||||
end
|
||||
end
|
||||
Reference in New Issue
Block a user