@@ -115,7 +115,14 @@ defmodule Mint.WebSocket do
115
115
import Mint.HTTP , only: [ get_private: 2 , put_private: 3 , protocol: 1 ]
116
116
117
117
@ typedoc """
118
- An immutable data structure representing a WebSocket connection
118
+ An immutable data structure representing WebSocket state.
119
+
120
+ You will usually want to keep these around:
121
+
122
+ * The Mint connection
123
+ * The request reference for the WebSocket upgrade request
124
+ * This WebSocket data structure
125
+
119
126
"""
120
127
@ opaque t :: % __MODULE__ {
121
128
extensions: [ Extension . t ( ) ] ,
@@ -131,55 +138,52 @@ defmodule Mint.WebSocket do
131
138
@ type error :: Mint.Types . error ( ) | WebSocketError . t ( ) | UpgradeFailureError . t ( )
132
139
133
140
@ typedoc """
134
- Shorthand notations for control frames
141
+ Shorthand notations for control frames.
135
142
136
- * `:ping` - shorthand for `{:ping, ""}`
137
- * `:pong` - shorthand for `{:pong, ""}`
138
- * `:close` - shorthand for `{:close, nil, nil}`
143
+ * `:ping` - shorthand for `{:ping, ""}`
144
+ * `:pong` - shorthand for `{:pong, ""}`
145
+ * `:close` - shorthand for `{:close, nil, nil}`
139
146
140
147
These may be passed to `encode/2`. Frames decoded with `decode/2` are always
141
148
in `t:frame/0` format.
142
149
"""
143
150
@ type shorthand_frame :: :ping | :pong | :close
144
151
145
152
@ typedoc """
146
- A WebSocket frame
147
-
148
- * `{:binary, binary}` - a frame containing binary data. Binary frames
149
- can be used to send arbitrary binary data such as a PDF.
150
- * `{:text, text}` - a frame containing string data. Text frames must be
151
- valid utf8. Elixir has wonderful support for utf8: `String.valid?/1`
152
- can detect valid and invalid utf8.
153
- * `{:ping, binary}` - a control frame which the server should respond to
154
- with a pong. The binary data must be echoed in the pong response.
155
- * `{:pong, binary}` - a control frame which forms a reply to a ping frame.
156
- Pings and pongs may be used to check the a connection is alive or to
157
- estimate latency.
158
- * `{:close, code, reason}` - a control frame used to request that a connection
159
- be closed or to acknowledgee a close frame send by the server.
153
+ A WebSocket frame.
154
+
155
+ * `{:binary, binary}` - a frame containing binary data. Binary frames
156
+ can be used to send arbitrary binary data such as a PDF.
157
+ * `{:text, text}` - a frame containing string data. Text frames must be
158
+ valid utf8. Elixir has wonderful support for utf8: `String.valid?/1`
159
+ can detect valid and invalid utf8.
160
+ * `{:ping, binary}` - a control frame which the server should respond to
161
+ with a pong. The binary data must be echoed in the pong response.
162
+ * `{:pong, binary}` - a control frame which forms a reply to a ping frame.
163
+ Pings and pongs may be used to check the a connection is alive or to
164
+ estimate latency.
165
+ * `{:close, code, reason}` - a control frame used to request that a connection
166
+ be closed or to acknowledgee a close frame send by the server.
160
167
161
168
These may be passed to `encode/2` or returned from `decode/2`.
162
169
163
170
## Close frames
164
171
165
172
In order to close a WebSocket connection gracefully, either the client or
166
173
server sends a close frame. Then the other endpoint responds with a
167
- close with code `1_000` and then closes the TCP connection. This can be
168
- accomplished in Mint.WebSocket like so:
174
+ close with code `1_000` and then closes the TCP/TLS connection. This can be
175
+ accomplished in ` Mint.WebSocket` like so:
169
176
170
- ```elixir
171
- {:ok, websocket, data} = Mint.WebSocket.encode(websocket, :close)
172
- {:ok, conn} = Mint.WebSocket.stream_request_body(conn, ref, data)
177
+ {:ok, websocket, data} = Mint.WebSocket.encode(websocket, :close)
178
+ {:ok, conn} = Mint.WebSocket.stream_request_body(conn, ref, data)
173
179
174
- close_response = receive(do: (message -> message))
175
- {:ok, conn, [{:data, ^ref, data}]} = Mint.WebSocket.stream(conn, close_response)
176
- {:ok, websocket, [{:close, 1_000, ""}]} = Mint.WebSocket.decode(websocket, data)
180
+ close_response = receive(do: (message -> message))
181
+ {:ok, conn, [{:data, ^ref, data}]} = Mint.WebSocket.stream(conn, close_response)
182
+ {:ok, websocket, [{:close, 1_000, ""}]} = Mint.WebSocket.decode(websocket, data)
177
183
178
- Mint.HTTP.close(conn)
179
- ```
184
+ Mint.HTTP.close(conn)
180
185
181
- [rfc6455
182
- section 7.4.1](https://datatracker.ietf.org/doc/html/rfc6455#section-7.4.1)
186
+ [RFC6455 § 7.4.1](https://datatracker.ietf.org/doc/html/rfc6455#section-7.4.1)
183
187
documents codes which may be used in the `code` element.
184
188
"""
185
189
@ type frame ::
@@ -206,8 +210,8 @@ defmodule Mint.WebSocket do
206
210
207
211
## Options
208
212
209
- * `:extensions` - a list of extensions to negotiate. See the extensions
210
- section below.
213
+ * `:extensions` - a list of extensions to negotiate. See the extensions
214
+ section below.
211
215
212
216
## Extensions
213
217
@@ -220,27 +224,33 @@ defmodule Mint.WebSocket do
220
224
Extensions may be passed as a list of `Mint.WebSocket.Extension` structs
221
225
or with the following shorthand notations:
222
226
223
- * `module` - shorthand for `{module, []}`
224
- * `{module, params}` - shorthand for `{module, params, []}`
225
- * `{module, params, opts}` - a shorthand which is expanded to a
226
- `Mint.WebSocket.Extension` struct
227
+ * `module` - shorthand for `{module, []}`
228
+ * `{module, params}` - shorthand for `{module, params, []}`
229
+ * `{module, params, opts}` - a shorthand which is expanded to a
230
+ `Mint.WebSocket.Extension` struct
227
231
228
232
## Examples
229
233
230
- ```elixir
231
- {:ok, conn} = Mint.HTTP.connect(:http, "localhost", 9_000)
232
- {:ok, conn, ref} =
233
- Mint.WebSocket.upgrade(:ws, conn, "/", [], extensions: [Mint.WebSocket.PerMessageDeflate])
234
- # or provide params:
235
- {:ok, conn, ref} =
236
- Mint.WebSocket.upgrade(
237
- :ws,
238
- conn,
239
- "/",
240
- [],
241
- extensions: [{Mint.WebSocket.PerMessageDeflate, [:client_max_window_bits]]}]
242
- )
243
- ```
234
+ First, establish the Mint connection:
235
+
236
+ {:ok, conn} = Mint.HTTP.connect(:http, "localhost", 9_000)
237
+
238
+ Then, send the upgrade request (with an extension in this example):
239
+
240
+ {:ok, conn, ref} =
241
+ Mint.WebSocket.upgrade(:ws, conn, "/", [], extensions: [Mint.WebSocket.PerMessageDeflate])
242
+
243
+ Here's an example of providing extension parameters:
244
+
245
+ {:ok, conn, ref} =
246
+ Mint.WebSocket.upgrade(
247
+ :ws,
248
+ conn,
249
+ "/",
250
+ [],
251
+ extensions: [{Mint.WebSocket.PerMessageDeflate, [:client_max_window_bits]]}]
252
+ )
253
+
244
254
"""
245
255
@ spec upgrade (
246
256
scheme :: :ws | :wss ,
@@ -291,30 +301,36 @@ defmodule Mint.WebSocket do
291
301
292
302
@ doc """
293
303
Creates a new WebSocket data structure given the server's reply to the
294
- upgrade request
304
+ upgrade request.
305
+
306
+ `request_ref` should be the reference of the request made with `upgrade/5`.
307
+ `status` and `response_headers` should be the status code and headers
308
+ of the server's response to the upgrade request—see the example below.
309
+
310
+ The returned [WebSocket data structure](`t:t/0`) is used to encode and decode frames.
295
311
296
312
This function will setup any extensions accepted by the server using
297
313
the `c:Mint.WebSocket.Extension.init/2` callback.
298
314
299
315
## Options
300
316
301
- * `:mode` - (default: `:active`) either `:active` or `:passive`. This
302
- corresponds to the same option in `Mint.HTTP.connect/4`.
317
+ * `:mode` - (default: `:active`) either `:active` or `:passive`. This
318
+ corresponds to the same option in `Mint.HTTP.connect/4`.
303
319
304
320
## Examples
305
321
306
- ```elixir
307
- http_reply = receive(do: (message -> message))
308
- {:ok, conn, [{:status, ^ref, status}, {:headers, ^ref, headers}, {:done, ^ref}]} =
309
- Mint.WebSocket.stream(conn, http_reply)
322
+ http_reply = receive(do: (message -> message))
323
+
324
+ {:ok, conn, [{:status, ^ref, status}, {:headers, ^ref, headers}, {:done, ^ref}]} =
325
+ Mint.WebSocket.stream(conn, http_reply)
326
+
327
+ {:ok, conn, websocket} =
328
+ Mint.WebSocket.new(conn, ref, status, headers)
310
329
311
- {:ok, conn, websocket} =
312
- Mint.WebSocket.new(conn, ref, status, resp_headers)
313
- ```
314
330
"""
315
331
@ spec new (
316
332
Mint.HTTP . t ( ) ,
317
- reference ( ) ,
333
+ Mint.Types . request_ref ( ) ,
318
334
Mint.Types . status ( ) ,
319
335
Mint.Types . headers ( )
320
336
) ::
@@ -360,23 +376,26 @@ defmodule Mint.WebSocket do
360
376
361
377
@ doc """
362
378
A wrapper around `Mint.HTTP.stream/2` for streaming HTTP and WebSocket
363
- messages
379
+ messages.
364
380
365
- This function does not decode WebSocket frames. Instead, once a WebSocket
381
+ ** This function does not decode WebSocket frames** . Instead, once a WebSocket
366
382
connection has been established, decode any `{:data, request_ref, data}`
367
383
frames with `decode/2`.
368
384
369
- This function is a drop-in replacement for `Mint.HTTP.stream/2` which
385
+ This function is a drop-in replacement for `Mint.HTTP.stream/2`, which
370
386
enables streaming WebSocket data after the bootstrapping HTTP/1 connection
371
387
has concluded. It decodes both WebSocket and regular HTTP messages.
372
388
373
389
## Examples
374
390
375
391
message = receive(do: (message -> message))
392
+
376
393
{:ok, conn, [{:data, ^websocket_ref, data}]} =
377
394
Mint.WebSocket.stream(conn, message)
395
+
378
396
{:ok, websocket, [{:text, "hello world!"}]} =
379
397
Mint.WebSocket.decode(websocket, data)
398
+
380
399
"""
381
400
@ spec stream ( Mint.HTTP . t ( ) , term ( ) ) ::
382
401
{ :ok , Mint.HTTP . t ( ) , [ Mint.Types . response ( ) ] }
@@ -419,7 +438,7 @@ defmodule Mint.WebSocket do
419
438
end
420
439
421
440
@ doc """
422
- Receives data from the socket
441
+ Receives data from the socket.
423
442
424
443
This function is used instead of `stream/2` when the connection is
425
444
in `:passive` mode. You must pass the `mode: :passive` option to
@@ -431,8 +450,10 @@ defmodule Mint.WebSocket do
431
450
## Examples
432
451
433
452
{:ok, conn, [{:data, ^ref, data}]} = Mint.WebSocket.recv(conn, 0, 5_000)
453
+
434
454
{:ok, websocket, [{:text, "hello world!"}]} =
435
455
Mint.WebSocket.decode(websocket, data)
456
+
436
457
"""
437
458
@ spec recv ( Mint.HTTP . t ( ) , non_neg_integer ( ) , timeout ( ) ) ::
438
459
{ :ok , Mint.HTTP . t ( ) , [ Mint.Types . response ( ) ] }
@@ -460,12 +481,17 @@ defmodule Mint.WebSocket do
460
481
end
461
482
462
483
@ doc """
463
- Streams chunks of data on the connection
484
+ Streams chunks of data on the connection.
464
485
465
486
`stream_request_body/3` should be used to send encoded data on an
466
487
established WebSocket connection that has already been upgraded with
467
488
`upgrade/5`.
468
489
490
+ > #### Encoding {: .warning}
491
+ >
492
+ > This function doesn't perform any encoding. You should use `encode/2`
493
+ > to encode frames before sending them with `stream_request_body/3`.
494
+
469
495
This function is a wrapper around `Mint.HTTP.stream_request_body/3`. It
470
496
delegates to that function unless the `request_ref` belongs to an HTTP/1
471
497
WebSocket connection. When the request is an HTTP/1 WebSocket, this
@@ -479,6 +505,7 @@ defmodule Mint.WebSocket do
479
505
480
506
{:ok, websocket, data} = Mint.WebSocket.encode(websocket, {:text, "hello world!"})
481
507
{:ok, conn} = Mint.WebSocket.stream_request_body(conn, websocket_ref, data)
508
+
482
509
"""
483
510
@ spec stream_request_body (
484
511
Mint.HTTP . t ( ) ,
@@ -505,7 +532,7 @@ defmodule Mint.WebSocket do
505
532
end
506
533
507
534
@ doc """
508
- Encodes a frame into a binary
535
+ Encodes a frame into a binary.
509
536
510
537
The resulting binary may be sent with `stream_request_body/3`.
511
538
@@ -514,29 +541,27 @@ defmodule Mint.WebSocket do
514
541
515
542
## Examples
516
543
517
- ```elixir
518
- {:ok, websocket, data} = Mint.WebSocket.encode(websocket, {:text, "hello world"})
519
- {:ok, conn} = Mint.WebSocket.stream_request_body(conn, websocket_ref, data)
520
- ```
544
+ {:ok, websocket, data} = Mint.WebSocket.encode(websocket, {:text, "hello world"})
545
+ {:ok, conn} = Mint.WebSocket.stream_request_body(conn, websocket_ref, data)
546
+
521
547
"""
522
548
@ spec encode ( t ( ) , shorthand_frame ( ) | frame ( ) ) :: { :ok , t ( ) , binary ( ) } | { :error , t ( ) , any ( ) }
523
549
defdelegate encode ( websocket , frame ) , to: Frame
524
550
525
551
@ doc """
526
- Decodes a binary into a list of frames
552
+ Decodes a binary into a list of frames.
527
553
528
- The binary may received from the connection with `Mint.HTTP. stream/2`.
554
+ The binary may received from the connection with `stream/2`.
529
555
530
556
This function will invoke the `c:Mint.WebSocket.Extension.decode/2` callback
531
557
for any accepted extensions.
532
558
533
559
## Examples
534
560
535
- ```elixir
536
- message = receive(do: (message -> message))
537
- {:ok, conn, [{:data, ^ref, data}]} = Mint.HTTP.stream(conn, message)
538
- {:ok, websocket, frames} = Mint.WebSocket.decode(websocket, data)
539
- ```
561
+ message = receive(do: (message -> message))
562
+ {:ok, conn, [{:data, ^ref, data}]} = Mint.HTTP.stream(conn, message)
563
+ {:ok, websocket, frames} = Mint.WebSocket.decode(websocket, data)
564
+
540
565
"""
541
566
@ spec decode ( t ( ) , data :: binary ( ) ) ::
542
567
{ :ok , t ( ) , [ frame ( ) | { :error , term ( ) } ] } | { :error , t ( ) , any ( ) }
0 commit comments