Skip to content

Commit

Permalink
Add Dialyxir and fix all warnings (#27)
Browse files Browse the repository at this point in the history
  • Loading branch information
rkallos authored Dec 9, 2022
1 parent 4fbd3f5 commit e76205e
Show file tree
Hide file tree
Showing 11 changed files with 100 additions and 30 deletions.
21 changes: 21 additions & 0 deletions .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ jobs:
lint: true
coverage: true
report: true
dialyzer: true
- erlang: "24.3"
elixir: "1.12"
- erlang: "23.3.1"
Expand Down Expand Up @@ -65,6 +66,26 @@ jobs:
run: mix compile --warnings-as-errors
if: ${{ matrix.lint }}

- name: Restore cached PLTs
uses: actions/cache@v3
id: plt_cache
with:
key: |
${{ runner.os }}-${{ matrix.elixir }}-${{ matrix.erlang }}-plt
restore-keys: |
${{ runner.os }}-${{ matrix.elixir }}-${{ matrix.erlang }}-plt
path: |
priv/plts
# Create PLTs if no cached PLTs were found
- name: Create PLTs
if: steps.plt_cache.outputs.cache-hit != 'true'
run: MIX_ENV=test mix dialyzer --plt

- name: Run dialyzer
run: MIX_ENV=test mix dialyzer
if: ${{ matrix.dialyzer }}

- name: Run tests with coverage
run: mix coveralls.github
if: ${{ matrix.coverage }}
Expand Down
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -25,3 +25,7 @@ mint_web_socket-*.tar

# Temporary files for e.g. tests
/tmp

# Dialyzer PLTs
/priv/plts/*.plt
/priv/plts/*.plt.hash
16 changes: 8 additions & 8 deletions lib/mint/web_socket.ex
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,6 @@ defmodule Mint.WebSocket do

alias __MODULE__.{Utils, Extension, Frame}
alias Mint.{WebSocketError, WebSocket.UpgradeFailureError}
alias Mint.{HTTP1, HTTP2}
import Mint.HTTP, only: [get_private: 2, put_private: 3, protocol: 1]

@typedoc """
Expand Down Expand Up @@ -267,11 +266,12 @@ defmodule Mint.WebSocket do

headers = Utils.headers({:http1, nonce}, extensions) ++ headers

Mint.HTTP1.request(conn, "GET", path, headers, nil)
Mint.HTTP.request(conn, "GET", path, headers, nil)
end

@dialyzer {:no_opaque, do_upgrade: 6}
defp do_upgrade(scheme, :http2, conn, path, headers, opts) do
if HTTP2.get_server_setting(conn, :enable_connect_protocol) == true do
if Mint.HTTP2.get_server_setting(conn, :enable_connect_protocol) == true do
extensions = get_extensions(opts)
conn = put_private(conn, :extensions, extensions)

Expand Down Expand Up @@ -395,23 +395,23 @@ defmodule Mint.WebSocket do
# we take manual control of the :gen_tcp and :ssl messages in HTTP/1 because
# we have taken over the transport
defp stream_http1(conn, request_ref, message) do
socket = HTTP1.get_socket(conn)
socket = Mint.HTTP.get_socket(conn)
tag = if get_private(conn, :scheme) == :ws, do: :tcp, else: :ssl

case message do
{^tag, ^socket, data} ->
reset_mode(conn, [{:data, request_ref, data}])

_ ->
HTTP1.stream(conn, message)
Mint.HTTP.stream(conn, message)
end
end

defp reset_mode(conn, responses) do
module = if get_private(conn, :scheme) == :ws, do: :inet, else: :ssl

with :active <- get_private(conn, :mode),
{:error, reason} <- module.setopts(HTTP1.get_socket(conn), active: :once) do
{:error, reason} <- module.setopts(Mint.HTTP.get_socket(conn), active: :once) do
{:error, conn, %Mint.TransportError{reason: reason}, responses}
else
_ -> {:ok, conn, responses}
Expand Down Expand Up @@ -448,7 +448,7 @@ defmodule Mint.WebSocket do

defp recv_http1(conn, request_ref, byte_count, timeout) do
module = if get_private(conn, :scheme) == :ws, do: :gen_tcp, else: :ssl
socket = HTTP1.get_socket(conn)
socket = Mint.HTTP.get_socket(conn)

case module.recv(socket, byte_count, timeout) do
{:ok, data} ->
Expand Down Expand Up @@ -498,7 +498,7 @@ defmodule Mint.WebSocket do
defp stream_request_body_http1(conn, data) do
transport = if get_private(conn, :scheme) == :ws, do: :gen_tcp, else: :ssl

case transport.send(Mint.HTTP1.get_socket(conn), data) do
case transport.send(Mint.HTTP.get_socket(conn), data) do
:ok -> {:ok, conn}
{:error, reason} -> {:error, conn, %Mint.TransportError{reason: reason}}
end
Expand Down
4 changes: 2 additions & 2 deletions lib/mint/web_socket/extension.ex
Original file line number Diff line number Diff line change
Expand Up @@ -156,7 +156,7 @@ defmodule Mint.WebSocket.Extension do
defstruct [:name, :module, :state, opts: [], params: []]

@doc false
@spec encode(tuple(), [t()]) :: {:ok, tuple(), [t()]} | {:error, term()}
@spec encode(tuple(), [t()]) :: {tuple(), [t()]} | no_return()
def encode(frame, extensions) do
encode_all_extensions(frame, extensions, [])
end
Expand All @@ -180,7 +180,7 @@ defmodule Mint.WebSocket.Extension do
end

@doc false
@spec decode(tuple(), [t()]) :: {:ok, tuple(), [t()]} | {:error, term()}
@spec decode(tuple(), [t()]) :: {tuple(), [t()]} | {{:error, term()}, [t()]}
def decode(frame, extensions) do
decode_all_extensions(frame, extensions, [])
end
Expand Down
39 changes: 37 additions & 2 deletions lib/mint/web_socket/frame.ex
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,39 @@ defmodule Mint.WebSocket.Frame do
defrecord :ping, shared
defrecord :pong, shared

@typep continuation_frame() ::
record(:continuation,
reserved: <<_::3>>,
mask: binary(),
data: binary(),
fin?: boolean()
)
@type text_frame() ::
record(:text, reserved: <<_::3>>, mask: binary(), data: binary(), fin?: boolean())
@type binary_frame() ::
record(:binary, reserved: <<_::3>>, mask: binary(), data: binary(), fin?: boolean())
@type close_frame() ::
record(:close,
reserved: <<_::3>>,
mask: binary(),
data: binary(),
fin?: boolean(),
code: binary(),
reason: binary()
)
@type ping_frame() ::
record(:ping, reserved: <<_::3>>, mask: binary(), data: binary(), fin?: boolean())
@type pong_frame() ::
record(:pong, reserved: <<_::3>>, mask: binary(), data: binary(), fin?: boolean())

@type frame_record() ::
continuation_frame()
| text_frame()
| binary_frame()
| close_frame()
| ping_frame()
| pong_frame()

defguard is_control(frame)
when is_tuple(frame) and
(elem(frame, 0) == :close or elem(frame, 0) == :ping or elem(frame, 0) == :pong)
Expand Down Expand Up @@ -60,6 +93,9 @@ defmodule Mint.WebSocket.Frame do

def new_mask, do: :crypto.strong_rand_bytes(4)

@spec encode(Mint.WebSocket.t(), Mint.WebSocket.frame()) ::
{:ok, Mint.WebSocket.t(), bitstring()}
| {:error, Mint.WebSocket.t(), WebSocketError.t()}
def encode(websocket, frame) when is_friendly_frame(frame) do
{frame, extensions} =
frame
Expand All @@ -74,7 +110,7 @@ defmodule Mint.WebSocket.Frame do
:throw, {:mint, reason} -> {:error, websocket, reason}
end

@spec encode_to_binary(tuple()) :: binary()
@spec encode_to_binary(frame_record()) :: bitstring()
defp encode_to_binary(frame) do
payload = payload(frame)
mask = mask(frame)
Expand Down Expand Up @@ -340,7 +376,6 @@ defmodule Mint.WebSocket.Frame do
# translate from user-friendly tuple into record defined in this module
# (and the reverse)
@spec translate(Mint.WebSocket.frame() | Mint.WebSocket.shorthand_frame()) :: tuple()
@spec translate(tuple) :: Mint.WebSocket.frame()
for opcode <- Map.keys(@opcodes) do
def translate(unquote(opcode)(reserved: <<reserved::bitstring>>))
when reserved != <<0::size(3)>> do
Expand Down
4 changes: 4 additions & 0 deletions lib/mint/web_socket/upgrade_failure_error.ex
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,10 @@ defmodule Mint.WebSocket.UpgradeFailureError do
An error representing a failure to upgrade protocols from HTTP to WebSocket
"""

@type t() :: %__MODULE__{
status_code: Mint.Types.status(),
headers: [Mint.Types.headers()]
}
defexception [:status_code, :headers]

def message(%__MODULE__{} = error) do
Expand Down
7 changes: 6 additions & 1 deletion mix.exs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,10 @@ defmodule MintWebSocket.MixProject do
"coveralls.github": :test,
docs: :dev
],
dialyzer: [
plt_local_path: "priv/plts",
plt_add_apps: [:mix]
],
package: package(),
description: description(),
source_url: @source_url,
Expand All @@ -41,7 +45,8 @@ defmodule MintWebSocket.MixProject do
{:jason, ">= 0.0.0", only: [:dev, :test]},
{:cowboy, "~> 2.9", only: [:test]},
{:gun, "== 2.0.0-rc.2", only: [:test]},
{:excoveralls, "~> 0.14", only: [:test]}
{:excoveralls, "~> 0.14", only: [:test]},
{:dialyxir, "~> 1.2", only: [:dev, :test], runtime: false}
]
end

Expand Down
2 changes: 2 additions & 0 deletions mix.lock
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,9 @@
"certifi": {:hex, :certifi, "2.9.0", "6f2a475689dd47f19fb74334859d460a2dc4e3252a3324bd2111b8f0429e7e21", [:rebar3], [], "hexpm", "266da46bdb06d6c6d35fde799bcb28d36d985d424ad7c08b5bb48f5b5cdd4641"},
"cowboy": {:hex, :cowboy, "2.9.0", "865dd8b6607e14cf03282e10e934023a1bd8be6f6bacf921a7e2a96d800cd452", [:make, :rebar3], [{:cowlib, "2.11.0", [hex: :cowlib, repo: "hexpm", optional: false]}, {:ranch, "1.8.0", [hex: :ranch, repo: "hexpm", optional: false]}], "hexpm", "2c729f934b4e1aa149aff882f57c6372c15399a20d54f65c8d67bef583021bde"},
"cowlib": {:hex, :cowlib, "2.11.0", "0b9ff9c346629256c42ebe1eeb769a83c6cb771a6ee5960bd110ab0b9b872063", [:make, :rebar3], [], "hexpm", "2b3e9da0b21c4565751a6d4901c20d1b4cc25cbb7fd50d91d2ab6dd287bc86a9"},
"dialyxir": {:hex, :dialyxir, "1.2.0", "58344b3e87c2e7095304c81a9ae65cb68b613e28340690dfe1a5597fd08dec37", [:mix], [{:erlex, ">= 0.2.6", [hex: :erlex, repo: "hexpm", optional: false]}], "hexpm", "61072136427a851674cab81762be4dbeae7679f85b1272b6d25c3a839aff8463"},
"earmark_parser": {:hex, :earmark_parser, "1.4.25", "2024618731c55ebfcc5439d756852ec4e85978a39d0d58593763924d9a15916f", [:mix], [], "hexpm", "56749c5e1c59447f7b7a23ddb235e4b3defe276afc220a6227237f3efe83f51e"},
"erlex": {:hex, :erlex, "0.2.6", "c7987d15e899c7a2f34f5420d2a2ea0d659682c06ac607572df55a43753aa12e", [:mix], [], "hexpm", "2ed2e25711feb44d52b17d2780eabf998452f6efda104877a3881c2f8c0c0c75"},
"ex_doc": {:hex, :ex_doc, "0.28.4", "001a0ea6beac2f810f1abc3dbf4b123e9593eaa5f00dd13ded024eae7c523298", [:mix], [{:earmark_parser, "~> 1.4.19", [hex: :earmark_parser, repo: "hexpm", optional: false]}, {:makeup_elixir, "~> 0.14", [hex: :makeup_elixir, repo: "hexpm", optional: false]}, {:makeup_erlang, "~> 0.1", [hex: :makeup_erlang, repo: "hexpm", optional: false]}], "hexpm", "bf85d003dd34911d89c8ddb8bda1a958af3471a274a4c2150a9c01c78ac3f8ed"},
"excoveralls": {:hex, :excoveralls, "0.14.4", "295498f1ae47bdc6dce59af9a585c381e1aefc63298d48172efaaa90c3d251db", [:mix], [{:hackney, "~> 1.16", [hex: :hackney, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "e3ab02f2df4c1c7a519728a6f0a747e71d7d6e846020aae338173619217931c1"},
"gun": {:hex, :gun, "2.0.0-rc.2", "7c489a32dedccb77b6e82d1f3c5a7dadfbfa004ec14e322cdb5e579c438632d2", [:make, :rebar3], [{:cowlib, "2.11.0", [hex: :cowlib, repo: "hexpm", optional: false]}], "hexpm", "6b9d1eae146410d727140dbf8b404b9631302ecc2066d1d12f22097ad7d254fc"},
Expand Down
22 changes: 14 additions & 8 deletions test/fixtures/autobahn_client.ex
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ defmodule AutobahnClient do
defstruct [:conn, :websocket, :ref, messages: [], next: :cont, sent_close?: false, buffer: <<>>]

defguardp is_close_frame(frame)
when frame == :close or (is_tuple(frame) and elem(frame, 0) == :close)
when is_tuple(frame) and elem(frame, 0) == :close

def get_case_count do
%{messages: [{:text, count} | _]} = connect("/getCaseCount") |> decode_buffer()
Expand Down Expand Up @@ -149,18 +149,24 @@ defmodule AutobahnClient do
def send(state, frame) do
Logger.debug("Sending #{inspect(frame, printable_limit: 30)}")

with {:ok, %Mint.WebSocket{} = websocket, data} <-
Mint.WebSocket.encode(state.websocket, frame),
{:ok, conn} <- Mint.WebSocket.stream_request_body(state.conn, state.ref, data) do
Logger.debug("Sent.")
%__MODULE__{state | conn: conn, websocket: websocket, sent_close?: is_close_frame(frame)}
else
{:error, %Mint.WebSocket{} = websocket, reason} ->
case Mint.WebSocket.encode(state.websocket, frame) do
{:ok, websocket, data} ->
do_send(put_in(state.websocket, websocket), frame, data)

{:error, websocket, reason} ->
Logger.debug(
"Could not send frame #{inspect(frame, printable_limit: 30)} because #{inspect(reason)}, sending close..."
)

send(put_in(state.websocket, websocket), {:close, 1002, ""})
end
end

defp do_send(state, frame, data) do
case Mint.WebSocket.stream_request_body(state.conn, state.ref, data) do
{:ok, conn} ->
Logger.debug("Sent.")
%__MODULE__{state | conn: conn, sent_close?: is_close_frame(frame)}

{:error, conn, %Mint.TransportError{reason: :closed}} ->
Logger.debug(
Expand Down
8 changes: 1 addition & 7 deletions test/fixtures/test_server.ex
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,7 @@ defmodule TestServer do
A supervisor for the WebsocketHandler
"""

use Supervisor

def start_link(_opts) do
def start() do
dispatch =
:cowboy_router.compile([
{:_,
Expand All @@ -21,9 +19,5 @@ defmodule TestServer do
env: %{dispatch: dispatch},
enable_connect_protocol: true
})

Supervisor.start_link([], strategy: :one_for_one)
end

def init(_opts), do: {:ok, nil}
end
3 changes: 1 addition & 2 deletions test/mint/web_socket_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,7 @@ defmodule Mint.WebSocketTest do
alias Mint.{HTTP1, HTTP2, WebSocket, WebSocket.UpgradeFailureError}

setup_all do
# a cowboy test server used by the HTTP/2 tests
start_supervised!(TestServer)
TestServer.start()
:ok
end

Expand Down

0 comments on commit e76205e

Please sign in to comment.