Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit 802316a

Browse files
authoredFeb 17, 2022
refactor API to respect Mint's opaque structures (#19)
1 parent 8eac1b9 commit 802316a

File tree

13 files changed

+487
-255
lines changed

13 files changed

+487
-255
lines changed
 

‎CHANGELOG.md

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,47 @@ The format is based on [Keep a
66
Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to
77
[Semantic Versioning](https://semver.org/spec/v2.0.0.html).
88

9+
## UNRELEASED
10+
11+
This release is a breaking change from the 0.1.0 series. This update removes
12+
all instances where Mint.WebSocket would access opaque `t:Mint.HTTP.t/0` fields
13+
or call private functions within `Mint.HTTP1`, so now Mint.WebSocket should be
14+
more compatible with future changes to Mint.
15+
16+
#### Upgrade guide
17+
18+
First, add the `scheme` argument to calls to `Mint.WebSocket.upgrade/5`.
19+
For connections formed with `Mint.HTTP.connect(:http, ..)`, use the `:ws`
20+
scheme. For `Mint.HTTP.connect(:https, ..)`, use `:wss`.
21+
22+
23+
```diff
24+
- Mint.WebSocket.upgrade(conn, path, headers)
25+
+ Mint.WebSocket.upgrade(scheme, conn, path, headers)
26+
```
27+
28+
Then replace calls to `Mint.HTTP.stream/2` and/or `Mint.HTTP.recv/3` and
29+
`Mint.HTTP.stream_request_body/3` with the new `Mint.WebSocket` wrappers.
30+
This is safe to do even when these functions are being used to send and
31+
receive data in normal HTTP requests: the functionality only changes when
32+
the connection is an established HTTP/1 WebSocket.
33+
34+
### Added
35+
36+
- Added `Mint.WebSocket.stream/2` which wraps `Mint.HTTP.stream/2`
37+
- Added `Mint.WebSocket.recv/3` which wraps `Mint.HTTP.recv/3`
38+
- Added `Mint.WebSocket.stream_request_body/3` which wraps `Mint.HTTP.stream_request_body/3`
39+
40+
### Changed
41+
42+
- Changed function signature of `Mint.Websocket.upgrade/5` to accept the
43+
WebSocket's scheme (`:ws` or `:wss`) as the first argument
44+
- Added an optional `opts` argument to `Mint.WebSocket.new/5` to control
45+
active vs. passive mode on the socket
46+
- Restricted compatible Mint versions to `~> 1.4`
47+
- `Mint.WebSocket` now uses `Mint.HTTP.get_protocol/1` which was
48+
introduced in `1.4.0`.
49+
950
## 0.1.4 - 2021-07-06
1051

1152
### Fixed

‎README.md

Lines changed: 33 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -8,16 +8,45 @@
88

99
(Unofficial) HTTP/1 and HTTP/2 WebSocket support for Mint 🌱
1010

11+
## Usage
12+
13+
`Mint.WebSocket` works together with `Mint.HTTP` API. For example,
14+
this snippet shows sending and receiving a text frame of "hello world" to a
15+
WebSocket server which echos our frames:
16+
17+
```elixir
18+
# bootstrap
19+
{:ok, conn} = Mint.HTTP.connect(:http, "echo", 9000)
20+
21+
{:ok, conn, ref} = Mint.WebSocket.upgrade(:ws, conn, "/", [])
22+
23+
http_get_message = receive(do: (message -> message))
24+
{:ok, conn, [{:status, ^ref, status}, {:headers, ^ref, resp_headers}, {:done, ^ref}]} =
25+
Mint.WebSocket.stream(conn, http_get_message)
26+
27+
{:ok, conn, websocket} = Mint.WebSocket.new(conn, ref, status, resp_headers)
28+
29+
# send the hello world frame
30+
{:ok, websocket, data} = Mint.WebSocket.encode(websocket, {:text, "hello world"})
31+
{:ok, conn} = Mint.WebSocket.stream_request_body(conn, ref, data)
32+
33+
# receive the hello world reply frame
34+
hello_world_echo_message = receive(do: (message -> message))
35+
{:ok, conn, [{:data, ^ref, data}]} = Mint.WebSocket.stream(conn, hello_world_echo_message)
36+
{:ok, websocket, [{:text, "hello world"}]} = Mint.WebSocket.decode(websocket, data)
37+
```
38+
1139
## What is Mint?
1240

1341
Mint is a _functional_ HTTP/1 and HTTP/2 client library written in Elixir.
1442

1543
Why does it matter that it's functional? Isn't Elixir functional?
1644

1745
Existing WebSocket implementations like
18-
[`:gun`](https://github.com/ninenines/gun),
19-
[`:websocket_client`](https://github.com/jeremyong/websocket_client),
20-
or [`WebSockex`](https://github.com/Azolo/websockex) work by spawning and
46+
[`:gun`](https://github.com/ninenines/gun) /
47+
[`:websocket_client`](https://github.com/jeremyong/websocket_client) /
48+
[`Socket`](https://github.com/meh/elixir-socket) /
49+
[`WebSockex`](https://github.com/Azolo/websockex) work by spawning and
2150
passing messages among processes. This is a very convenient interface in
2251
Elixir and Erlang, but it does not allow the author much control over
2352
the WebSocket connection.
@@ -58,48 +87,6 @@ If `Mint.WebSocket.upgrade/4` returns
5887
Then the server does not support HTTP/2 WebSockets or does not have them
5988
enabled.
6089

61-
Support for HTTP/2 extended CONNECT was added to Mint in version `1.4.0`.
62-
If you need HTTP/2 support, make sure you require that version as a minimum.
63-
64-
```elixir
65-
# mix.exs
66-
def deps do
67-
[
68-
{:mint_web_socket, "~> 0.1"},
69-
{:mint, "~> 1.4"},
70-
# ..
71-
]
72-
end
73-
```
74-
75-
## Usage
76-
77-
`Mint.WebSocket` piggybacks much of the existing `Mint.HTTP` API. For example,
78-
this snippet shows sending and receiving a text frame of "hello world" to a
79-
WebSocket server which echos our frames:
80-
81-
```elixir
82-
# bootstrap
83-
{:ok, conn} = Mint.HTTP.connect(:http, "echo", 9000)
84-
85-
{:ok, conn, ref} = Mint.WebSocket.upgrade(conn, "/", [])
86-
87-
http_get_message = receive(do: (message -> message))
88-
{:ok, conn, [{:status, ^ref, status}, {:headers, ^ref, resp_headers}, {:done, ^ref}]} =
89-
Mint.HTTP.stream(conn, http_get_message)
90-
91-
{:ok, conn, websocket} = Mint.WebSocket.new(conn, ref, status, resp_headers)
92-
93-
# send the hello world frame
94-
{:ok, websocket, data} = Mint.WebSocket.encode(websocket, {:text, "hello world"})
95-
{:ok, conn} = Mint.HTTP.stream_request_body(conn, ref, data)
96-
97-
# receive the hello world reply frame
98-
hello_world_echo_message = receive(do: (message -> message))
99-
{:ok, conn, [{:data, ^ref, data}]} = Mint.HTTP.stream(conn, hello_world_echo_message)
100-
{:ok, websocket, [{:text, "hello world"}]} = Mint.WebSocket.decode(websocket, data)
101-
```
102-
10390
## Development workflow
10491

10592
Interested in developing `Mint.WebSocket`? The `docker-compose.yml` sets up
@@ -108,7 +95,7 @@ fuzzing server.
10895

10996
```
11097
(host)$ docker-compose up -d
111-
(host)$ docker-compose exec app /bin/bash
98+
(host)$ docker-compose exec app bash
11299
(app)$ mix deps.get
113100
(app)$ mix test
114101
(app)$ iex -S mix

‎coveralls.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,5 +6,5 @@
66
"terminal_options": {
77
"file_column_width": 60
88
},
9-
"skip_files": ["^deps", "^test/compare/"]
9+
"skip_files": ["^deps", "^test/compare/", "^test/fixtures/websocket_"]
1010
}

‎examples/echo.exs

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,31 +6,31 @@ require Logger
66
Logger.debug("Connected to https://echo.websocket.org:443")
77

88
Logger.debug("Upgrading to WebSocket protocol on /")
9-
{:ok, conn, ref} = Mint.WebSocket.upgrade(conn, "/", [])
9+
{:ok, conn, ref} = Mint.WebSocket.upgrade(:wss, conn, "/", [])
1010

1111
message = receive(do: (message -> message))
1212
{:ok, conn, [{:status, ^ref, status}, {:headers, ^ref, resp_headers}, {:done, ^ref}]} =
13-
Mint.HTTP.stream(conn, message)
13+
Mint.WebSocket.stream(conn, message)
1414
{:ok, conn, websocket} = Mint.WebSocket.new(conn, ref, status, resp_headers)
1515
Logger.debug("WebSocket established")
1616

1717
frame = {:text, "Rock it with Mint.WebSocket"}
1818
Logger.debug("Sending frame #{inspect(frame)}")
1919
{:ok, websocket, data} = Mint.WebSocket.encode(websocket, frame)
20-
{:ok, conn} = Mint.HTTP.stream_request_body(conn, ref, data)
20+
{:ok, conn} = Mint.WebSocket.stream_request_body(conn, ref, data)
2121

2222
message = receive(do: (message -> message))
23-
{:ok, conn, [{:data, ^ref, data}]} = Mint.HTTP.stream(conn, message)
23+
{:ok, conn, [{:data, ^ref, data}]} = Mint.WebSocket.stream(conn, message)
2424
{:ok, websocket, frames} = Mint.WebSocket.decode(websocket, data)
2525
Logger.debug("Received frames #{inspect(frames)}")
2626

2727
frame = :close
2828
Logger.debug("Sending frame #{inspect(frame)}")
2929
{:ok, websocket, data} = Mint.WebSocket.encode(websocket, frame)
30-
{:ok, conn} = Mint.HTTP.stream_request_body(conn, ref, data)
30+
{:ok, conn} = Mint.WebSocket.stream_request_body(conn, ref, data)
3131

3232
message = receive(do: (message -> message))
33-
{:ok, conn, [{:data, ^ref, data}]} = Mint.HTTP.stream(conn, message)
33+
{:ok, conn, [{:data, ^ref, data}]} = Mint.WebSocket.stream(conn, message)
3434
{:ok, websocket, frames} = Mint.WebSocket.decode(websocket, data)
3535
Logger.debug("Received frames #{inspect(frames)}")
3636

‎examples/phoenixchat_herokuapp.exs

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,33 +1,33 @@
1-
# N.B. this is a phoenix v1.3 server that sends pings periodically
1+
# this is a phoenix v1.3 server that sends pings periodically
22
# see https://phoenixchat.herokuapp.com for the in-browser version
33
{:ok, conn} = Mint.HTTP.connect(:https, "phoenixchat.herokuapp.com", 443)
44

5-
{:ok, conn, ref} = Mint.WebSocket.upgrade(conn, "/ws", [])
5+
{:ok, conn, ref} = Mint.WebSocket.upgrade(:wss, conn, "/ws", [])
66

77
http_get_message = receive(do: (message -> message))
88
{:ok, conn, [{:status, ^ref, status}, {:headers, ^ref, resp_headers}, {:done, ^ref}]} =
9-
Mint.HTTP.stream(conn, http_get_message)
9+
Mint.WebSocket.stream(conn, http_get_message)
1010
{:ok, conn, websocket} = Mint.WebSocket.new(conn, ref, status, resp_headers)
1111

1212
{:ok, websocket, data} = Mint.WebSocket.encode(websocket, {:text, ~s[{"topic":"rooms:lobby","event":"phx_join","payload":{},"ref":1}]})
13-
{:ok, conn} = Mint.HTTP.stream_request_body(conn, ref, data)
13+
{:ok, conn} = Mint.WebSocket.stream_request_body(conn, ref, data)
1414

1515
message = receive(do: (message -> message))
16-
{:ok, conn, [{:data, ^ref, data}]} = Mint.HTTP.stream(conn, message)
16+
{:ok, conn, [{:data, ^ref, data}]} = Mint.WebSocket.stream(conn, message)
1717
{:ok, websocket, messages} = Mint.WebSocket.decode(websocket, data)
1818
IO.inspect(messages)
1919

2020
message = receive(do: (message -> message))
21-
{:ok, conn, [{:data, ^ref, data}]} = Mint.HTTP.stream(conn, message)
21+
{:ok, conn, [{:data, ^ref, data}]} = Mint.WebSocket.stream(conn, message)
2222
{:ok, websocket, messages} = Mint.WebSocket.decode(websocket, data)
2323
IO.inspect(messages)
2424

2525
message = receive(do: (message -> message))
26-
{:ok, conn, [{:data, ^ref, data}]} = Mint.HTTP.stream(conn, message)
26+
{:ok, conn, [{:data, ^ref, data}]} = Mint.WebSocket.stream(conn, message)
2727
{:ok, websocket, messages} = Mint.WebSocket.decode(websocket, data)
2828
IO.inspect(messages)
2929

3030
message = receive(do: (message -> message))
31-
{:ok, _conn, [{:data, ^ref, data}]} = Mint.HTTP.stream(conn, message)
31+
{:ok, _conn, [{:data, ^ref, data}]} = Mint.WebSocket.stream(conn, message)
3232
{:ok, _websocket, messages} = Mint.WebSocket.decode(websocket, data)
3333
IO.inspect(messages)
There was a problem loading the remainder of the diff.

0 commit comments

Comments
 (0)
Please sign in to comment.