This commit is contained in:
2026-07-14 22:50:46 +04:00
parent 27143319e3
commit bd19e0682b
3116 changed files with 467189 additions and 0 deletions

BIN
phoenix/deps/websock/.hex Normal file

Binary file not shown.

View 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.

View File

@@ -0,0 +1,74 @@
# WebSock
[![Build Status](https://github.com/phoenixframework/websock/workflows/Elixir%20CI/badge.svg)](https://github.com/phoenixframework/websock/actions)
[![Docs](https://img.shields.io/badge/api-docs-green.svg?style=flat)](https://hexdocs.pm/websock)
[![Hex.pm](https://img.shields.io/hexpm/v/websock.svg?style=flat&color=blue)](https://hex.pm/packages/websock)
WebSock is a specification for apps to service WebSocket connections; you can
think of it as 'Plug for WebSockets'. WebSock abstracts WebSocket support from
servers such as [Bandit][] or [Cowboy][] and exposes a generic WebSocket API to
applications. WebSocket-aware applications such as Phoenix can then be hosted
within a supported web server simply by defining conformance to the `WebSock`
behaviour, in the same manner as how Plug conformance allows their HTTP aspects
to be hosted within an arbitrary web server.
<!-- MDOC -->
Defines the `WebSock` behaviour which describes the functions that
an application such as Phoenix must implement in order to be WebSock compliant;
it is roughly the equivalent of the `Plug` interface, but for WebSocket
connections. It is commonly used in conjunction with the [websock_adapter][]
package which defines concrete adapters on top of [Bandit][] and [Cowboy][];
the two packages are separate to allow for servers which directly expose
`WebSock` support to depend on just the behaviour. Users will almost always
want to depend on [websock_adapter][] instead of this package.
## WebSocket Lifecycle
WebSocket connections go through a well defined lifecycle mediated by `WebSock`
and `WebSock.Adapters`:
* **This step is outside the scope of the WebSock API**. A client will
attempt to Upgrade an HTTP connection to a WebSocket connection by passing
a specific set of headers in an HTTP request. An application may choose to
determine the feasibility of such an upgrade request however it pleases
* An application will then signal an upgrade to be performed by calling
`WebSockAdapter.upgrade/4`, passing in the `Plug.Conn` to upgrade, along with
the `WebSock` compliant handler module which will handle the connection once
it is upgraded
* The underlying server will then attempt to upgrade the HTTP connection to a WebSocket connection
* Assuming the WebSocket connection is successfully negotiated, WebSock will
call `c:WebSock.init/1` on the configured handler to allow the application to perform any necessary
tasks now that the WebSocket connection is live
* WebSock will call the configured handler's `c:WebSock.handle_in/2` callback
whenever data is received from the client
* WebSock will call the configured handler's `c:WebSock.handle_info/2` callback
whenever other processes send messages to the handler process
* The `WebSock` implementation can send data to the client by returning
a `{:push,...}` tuple from any of the above `handle_*` callbacks
* At any time, `c:WebSock.terminate/2` (if implemented) may be called to indicate a close, error or
timeout condition
[Cowboy]: https://github.com/ninenines/cowboy
[Bandit]: https://github.com/mtrudel/bandit/
[websock_adapter]: https://hex.pm/packages/websock_adapter
<!-- MDOC -->
For more information, consult the [docs](https://hexdocs.pm/websock).
## Installation
The websock package can be installed by adding `websock` to your list of dependencies in `mix.exs`:
```elixir
def deps do
[
{:websock, "~> 0.5"}
]
end
```
Documentation can be found at <https://hexdocs.pm/websock>.
## License
MIT

View File

@@ -0,0 +1,13 @@
{<<"app">>,<<"websock">>}.
{<<"build_tools">>,[<<"mix">>]}.
{<<"description">>,<<"A specification for WebSocket connections">>}.
{<<"elixir">>,<<"~> 1.9">>}.
{<<"files">>,
[<<"lib">>,<<"lib/websock.ex">>,<<"test">>,<<"test/test_helper.exs">>,
<<"mix.exs">>,<<"README.md">>,<<"LICENSE">>]}.
{<<"licenses">>,[<<"MIT">>]}.
{<<"links">>,
[{<<"GitHub">>,<<"https://github.com/phoenixframework/websock">>}]}.
{<<"name">>,<<"websock">>}.
{<<"requirements">>,[]}.
{<<"version">>,<<"0.5.3">>}.

View File

@@ -0,0 +1,128 @@
defmodule WebSock do
@external_resource Path.join([__DIR__, "../README.md"])
@moduledoc @external_resource
|> File.read!()
|> String.split("<!-- MDOC -->")
|> Enum.fetch!(1)
@typedoc "The type of an implementing module"
@type impl :: module()
@typedoc "The type of state passed into / returned from `WebSock` callbacks"
@type state :: term()
@typedoc "Possible data frame types"
@type data_opcode :: :text | :binary
@typedoc "Possible control frame types"
@type control_opcode :: :ping | :pong
@typedoc "All possible frame types"
@type opcode :: data_opcode() | control_opcode()
@typedoc "The structure of an outbound message"
@type message :: {opcode(), iodata() | nil}
@typedoc "Convenience type for one or many outbound messages"
@type messages :: message() | [message()]
@typedoc "The result as returned from init, handle_in, handle_control & handle_info calls"
@type handle_result ::
{:push, messages(), state()}
| {:reply, term(), messages(), state()}
| {:ok, state()}
| {:stop, {:shutdown, :restart} | term(), state()}
| {:stop, term(), close_detail(), state()}
| {:stop, term(), close_detail(), messages(), state()}
@typedoc "Details about why a connection was closed"
@type close_reason :: :normal | :remote | :shutdown | :timeout | {:error, term()}
@typedoc "Describes the data to send in a connection close frame"
@type close_detail :: integer() | {integer(), iodata() | nil}
@doc """
Called by WebSock after a WebSocket connection has been established (that is, after the server
has accepted the connection & the WebSocket handshake has been successfully completed).
Implementations can use this callback to perform tasks such as subscribing the client to any
relevant subscriptions within the application, or any other task which should be undertaken at
the time the connection is established
The return value from this callback is handled as described in `c:handle_in/2`
"""
@callback init(term()) :: handle_result()
@doc """
Called by WebSock when a frame is received from the client. WebSock will only call this function
once a complete frame has been received (that is, once any continuation frames have been
received).
The return value from this callback are processed as follows:
* `{:push, messages(), state()}`: The indicated message(s) are sent to the client. The
indicated state value is used to update the socket's current state
* `{:reply, term(), messages(), state()}`: The indicated message(s) are sent to the client. The
indicated state value is used to update the socket's current state. The second element of the
tuple has no semantic meaning in this context and is ignored. This return tuple is included
here solely for backwards compatibility with the `Phoenix.Socket.Transport` behaviour; it is in
all respects semantically identical to the `{:push, ...}` return value previously described
* `{:ok, state()}`: The indicated state value is used to update the socket's current state
* `{:stop, reason :: term(), state()}`: The connection will be closed based on the indicated
reason. If `reason` is `:normal`, `c:terminate/2` will be called with a `reason` value of
`:normal`. If the `reason` is `{:shutdown, :restart}`, the server is restarting and
the WebSocket adapter should close with the `1012` Service Restart code.
In all other cases, it will be called with `{:error, reason}`. Server
implementations should also use this value when determining how to close the connection with
the client
* `{:stop, reason :: term(), close_detail(), state()}`: Similar to the previous clause, but allows
for the explicit setting of either a plain close code or a close code with a body to be sent to
the client
* `{:stop, reason :: term(), close_detail(), messages(), state()}`: Similar to the previous clause, but allows
for the sending of one or more frames before sending the connection close frame to the client
"""
@callback handle_in({binary(), opcode: data_opcode()}, state()) :: handle_result()
@doc """
Called by WebSock when a ping or pong frame has been received from the client. Note that
implementations SHOULD NOT send a pong frame in response; this MUST be automatically done by the
web server before this callback has been called.
Despite the name of this callback, it is not called for connection close frames even though they
are technically control frames. WebSock will handle any received connection
close frames and issue calls to `c:terminate/2` as / if appropriate
This callback is optional
The return value from this callback is handled as described in `c:handle_in/2`
"""
@callback handle_control({binary(), opcode: control_opcode()}, state()) :: handle_result()
@doc """
Called by WebSock when the socket process receives a `c:GenServer.handle_info/2` call which was
not otherwise processed by the server implementation.
The return value from this callback is handled as described in `c:handle_in/2`
"""
@callback handle_info(term(), state()) :: handle_result()
@doc """
Called by WebSock when a connection is closed. `reason` may be one of the following:
* `:normal`: The local end shut down the connection normally, by returning a `{:stop, :normal,
state()}` tuple from one of the `WebSock.handle_*` callbacks
* `:remote`: The remote end shut down the connection
* `:shutdown`: The local server is being shut down
* `:timeout`: No data has been sent or received for more than the configured timeout duration
* `{:error, reason}`: An error occurred. This may be the result of error
handling in the local server, or the result of a `WebSock.handle_*` callback returning a `{:stop,
reason, state}` tuple where reason is any value other than `:normal`
This callback is optional
The return value of this callback is ignored
"""
@callback terminate(reason :: close_reason(), state()) :: any()
@optional_callbacks handle_control: 2, terminate: 2
end

View File

@@ -0,0 +1,46 @@
defmodule WebSock.MixProject do
use Mix.Project
def project do
[
app: :websock,
version: "0.5.3",
elixir: "~> 1.9",
start_permanent: Mix.env() == :prod,
deps: deps(),
dialyzer: dialyzer(),
name: "WebSock",
description: "A specification for WebSocket connections",
source_url: "https://github.com/phoenixframework/websock",
package: [
files: ["lib", "test", "mix.exs", "README*", "LICENSE*"],
maintainers: ["Mat Trudel"],
licenses: ["MIT"],
links: %{"GitHub" => "https://github.com/phoenixframework/websock"}
],
docs: [
extras: [
"README.md": [title: "README"],
"CHANGELOG.md": [title: "Changelog"]
],
main: "readme"
]
]
end
def application do
[]
end
defp deps do
[
{: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_core_path: "priv/plts", plt_file: {:no_warn, "priv/plts/dialyzer.plt"}]
end
end

View File

@@ -0,0 +1 @@
ExUnit.start()