diff --git a/index.bs b/index.bs
index 6b32a1c..9f4b8e7 100644
--- a/index.bs
+++ b/index.bs
@@ -281,7 +281,7 @@ It can have the following values:
close code=] and |reason| as [=the WebSocket connection close reason=].
: |socket|.url
- :: Returns the URL that was used to establish the WebSocket connection.
+ :: Returns the URL that was used to establish the WebSocket connection.
: |socket|.readyState
:: Returns the state of the WebSocket connection. It can have the values described above.
@@ -321,14 +321,8 @@ It can have the following values:
constructor steps are:
1. Let |baseURL| be [=this=]'s [=relevant settings object=]'s [=API base URL=].
- 1. Let |urlRecord| be the result of applying the [=URL parser=] to |url| with |baseURL|.
- 1. If |urlRecord| is failure, then throw a "{{SyntaxError}}" {{DOMException}}.
- 1. If |urlRecord|'s [=url/scheme=] is "`http`", then set |urlRecord|'s [=url/scheme=] to "`ws`".
- 1. Otherwise, if |urlRecord|'s [=url/scheme=] is "`https`", set |urlRecord|'s [=url/scheme=] to
- "`wss`".
- 1. If |urlRecord|'s [=scheme=] is not "[=ws=]
" or "[=wss=]
", then throw a
- "{{SyntaxError}}" {{DOMException}}.
- 1. If |urlRecord|'s [=fragment=] is non-null, then throw a "{{SyntaxError}}" {{DOMException}}.
+ 1. Let |urlRecord| be the result of [=getting a URL record=] given |url| and
+ |baseURL|.
1. If |protocols| is a string, set |protocols| to a sequence consisting of just that string.
1. If any of the values in |protocols| occur more than once or otherwise fail to match the
requirements for elements that comprise the value of
@@ -364,58 +358,10 @@ string. After [=the WebSocket connection is established=], its value might chang
The connection is already closing or is already closed. If it has not already, a - {{WebSocket/close}} event will eventually fire as described below. + 1. If |code| is the special value "missing", then set |code| to null. + 1. If |reason| is the special value "missing", then set |reason| to the empty string. + 1. [=Close the WebSocket=] with [=this=], |code|, and |reason|. - : If the WebSocket connection is not yet [=established=] [[!WSP]] - :: [=Fail the WebSocket connection=] and set [=this=]'s [=WebSocket/ready state=] to - {{WebSocket/CLOSING}} (2). [[!WSP]] - -
The [=fail the WebSocket connection=] algorithm invokes the [=close the - WebSocket connection=] algorithm, which then establishes that [=the WebSocket connection is - closed=], which fires the {{WebSocket/close}} event as described - below. - - : If the WebSocket closing handshake has not yet been started [[!WSP]] - :: [=Start the WebSocket closing handshake=] and set [=this=]'s [=WebSocket/ready state=] to - {{WebSocket/CLOSING}} (2). [[!WSP]] - - If neither |code| nor |reason| is present, the WebSocket Close message must not have a body. - -
The WebSocket Protocol erroneously states that the status code is required for the [=start the WebSocket closing handshake=] algorithm.
-
-
- If |code| is present, then the status code to use in the WebSocket Close
- message must be the integer given by |code|. [[!WSP]]
-
- If |reason| is also present, then |reasonBytes| must be provided in the Close message after the
- status code. [[!WSP]]
-
- The [=start the WebSocket closing handshake=] algorithm eventually invokes the
- [=close the WebSocket connection=] algorithm, which then establishes that [=the WebSocket
- connection is closed=], which fires the {{WebSocket/close}} event as
- described below.
-
- : Otherwise
- :: Set [=this=]'s [=WebSocket/ready state=] to {{WebSocket/CLOSING}} (2).
-
- [=The WebSocket closing handshake is started=], and will eventually invoke the
- [=close the WebSocket connection=] algorithm, which will establish that [=the WebSocket
- connection is closed=], and thus the {{WebSocket/close}} event will fire, as described below.
-
The {{WebSocket/close()}} method does not discard previously sent messages before @@ -547,7 +493,7 @@ that must be supported, as [=event handler IDL attributes=], by all objects impl -# Feedback from the protocol # {#feedback-from-the-protocol} +## Feedback from the protocol ## {#feedback-from-the-protocol} When [=the WebSocket connection is established=], the user agent must [=queue a task=] to run these steps: @@ -595,8 +541,8 @@ When [=a WebSocket message has been received=] with type |type| and data |data|, 1. [=Fire an event=] named message at the {{WebSocket}} object, using {{MessageEvent}}, with the {{MessageEvent/origin}} attribute initialized to the serialization of the {{WebSocket}} object's [=url=]'s [=origin=], and the - {{MessageEvent/data}} attribute initialized to |dataForEvent|. + serializer">serialization of the {{WebSocket}} object's [=internal-url|url=]'s [=origin=], + and the {{MessageEvent/data}} attribute initialized to |dataForEvent|.
User agents are encouraged to check if they can perform the above steps efficiently before they run the task, picking tasks from other [=task queues=] while they prepare the buffers @@ -688,23 +634,8 @@ The [=task source=] for all [=tasks=] queued in this se WebSocket task source. -# Ping and Pong frames # {#ping-and-pong-frames} - -The WebSocket protocol defines Ping and Pong frames that can be used for keep-alive, -heart-beats, network status probing, latency instrumentation, and so forth. These are not currently -exposed in the API. - -User agents may send ping and unsolicited pong frames as desired, for example in an attempt to -maintain local network NAT mappings, to detect failed connections, or to display latency metrics to -the user. User agents must not use pings or unsolicited pongs to aid the server; it is assumed that -servers will solicit pongs whenever appropriate for the server's needs. - - - -# The {{CloseEvent}} interface # {#the-closeevent-interface} +## The {{CloseEvent}} interface ## {#the-closeevent-interface} {{WebSocket}} objects use the {{CloseEvent}} interface for their {{WebSocket/close}} events: @@ -747,7 +678,7 @@ to. It represents the WebSocket connection close reason provided by the server. -# Garbage collection # {#garbage-collection} +## Garbage collection ## {#garbage-collection} A {{WebSocket}} object whose [=WebSocket/ready state=] was set to {{WebSocket/CONNECTING}} (0) as of the last time the [=event loop=] reached step 1 must not be garbage @@ -787,7 +718,7 @@ the following list: : If the WebSocket closing handshake has not yet been started [[!WSP]] :: [=Start the WebSocket closing handshake=], with the status code to use in the - WebSocket Close message being 1001. [[!WSP]] + WebSocket Close frame being 1001. [[!WSP]] : Otherwise :: Do nothing. @@ -795,6 +726,590 @@ the following list: + + +# The {{WebSocketStream}} interface # {#the-websocketstream-interface} + +The Web IDL definition for the {{WebSocketStream}} class is given as follows: + +
|socket| = new {{WebSocketStream/constructor(url, options)|WebSocketStream}}(|url|[, |options|])
+ :: Creates a new {{WebSocketStream}} object, immediately establishing the associated WebSocket
+ connection.
+
+ |url| is a string giving the URL over which the connection is established.
+ Only "`ws`", "`wss`", "`http`", and "`https`" schemes are allowed; others will cause a
+ "{{SyntaxError}}" {{DOMException}}. URLs with [=fragments=] will also cause such an exception.
+
+ The |options| argument is an object whose properties can be set as follows:
+
+ : {{WebSocketStreamOptions/protocols}}
+ :: An array of strings. If it is omitted, it is equivalent to an empty array. Each string in the
+ array is a subprotocol name. The connection will only be established if the server reports that
+ it has selected one of these subprotocols. The subprotocol names have to match the requirements
+ for elements that comprise the value of \``Sec-WebSocket-Protocol`\` fields as
+ defined by The WebSocket Protocol. [[!WSP]]
+
+ : {{WebSocketStreamOptions/signal}}
+ :: An {{AbortSignal}} that can be used to abort the handshake. After the handshake is complete,
+ the signal does nothing.
+
+ : |socket|.{{WebSocketStream/url}}
+ :: Returns the [=WebSocketStream/url|URL=] that was used to establish the WebSocket connection.
+
+ : |socket|.{{WebSocketStream/opened}}
+ :: Returns a {{Promise}} which resolves when the handshake successfully completes, or rejects if
+ the handshake fails. On success, it resolves to an object with the following properties:
+
+ : {{WebSocketOpenInfo/readable}}
+ :: A {{ReadableStream}} that can be used to read messages from the server. Each chunk read
+ corresponds to one message. Text messages will be read as strings; binary messages will be read
+ as {{Uint8Array}} objects.
+
+ The original {{WebSocket}} API supplied {{ArrayBuffer}} objects, but modern
+ practice is to prefer the {{Uint8Array}} type for binary data, particularly when using streams.
+ The underlying {{ArrayBuffer}} object can be recovered by accessing chunk.buffer
.
+
+ The stream can be closed by calling {{ReadableStream/cancel()}} on
+ {{WebSocketOpenInfo/readable}}. If the reason argument passed to {{ReadableStream/cancel()}} is a
+ {{WebSocketError}} object then {{WebSocketError/closeCode}} will be used as [=the WebSocket
+ connection close code=] and {{WebSocketError/reason}} will be used as [=the WebSocket connection
+ close reason=].
+
+ If no messages are read, or if messages are read slower than they are sent, then backpressure
+ will be applied and eventually the server will stop sending new messages.
+
+ : {{WebSocketOpenInfo/writable}}
+ :: A {{WritableStream}} that can be used to send messages to the server. Each chunk written will
+ be converted to one message. Strings will be sent as text messages; {{BufferSource}} chunks will
+ be sent as binary messages. Backpressure due to the network or server being unable to process
+ data fast enough will automatically be observed by piping. When using a [=writable stream
+ writer=], waiting for the {{WritableStreamDefaultWriter/ready|writer.ready}} promise will ensure
+ that backpressure is obeyed.
+
+ The WebSocket can be closed by calling {{WritableStream/close()}} on
+ {{WebSocketOpenInfo/writable}}.
+
+ The stream can also be closed by calling {{WritableStream/abort()}} on
+ {{WebSocketOpenInfo/writable}}. If the reason passed to {{WritableStream/abort()}} is a
+ {{WebSocketError}}, then it will be used to set [=the WebSocket connection close code=] and
+ [=the WebSocket connection close reason=] as with {{ReadableStream/cancel()}} above.
+
+ : {{WebSocketOpenInfo/extensions}}
+ :: The [=extensions in use=] for the connection.
+
+ : {{WebSocketOpenInfo/protocol}}
+ :: The [=subprotocol in use=] for the connection.
+
+ : |socket|.{{WebSocketStream/closed}}
+ :: A {{Promise}} which resolves when the connection is closed. If the connection did not close
+ [=cleanly=] then the promise is rejected with a {{WebSocketError}}. When the connection closes
+ [=cleanly=] the promise is fulfilled with an object with properties
+ {{WebSocketCloseInfo/closeCode}} and {{WebSocketCloseInfo/reason}}, giving [=the WebSocket
+ connection close code=] and [=the WebSocket connection close reason=] that were supplied by the
+ server.
+
+ : |socket|.{{WebSocketStream/close()}}
+ : |socket|.{{WebSocketStream/close()|close}}({ {{WebSocketCloseInfo/closeCode}}, {{WebSocketCloseInfo/reason}} })
+ :: Close the connection, optionally supplying an object with {{WebSocketCloseInfo/closeCode}} and
+ {{WebSocketCloseInfo/reason}} properties to indicate [=the WebSocket connection close code=] and
+ [=the WebSocket connection close reason=] that will be sent to the remote server. If the handshake
+ is still in progress, then it will be aborted and {{WebSocketCloseInfo/closeCode}} and
+ {{WebSocketCloseInfo/reason}} will be ignored.
+
new
+ WebSocketStream(|url|, |options|)
constructor steps are:
+
+ 1. Let |baseURL| be [=this=]'s [=relevant settings object=]'s [=API base URL=].
+ 1. Let |urlRecord| be the result of [=get a URL record|getting a URL record=] given |url| and
+ |baseURL|.
+ 1. Let |protocols| be |options|["{{WebSocketStreamOptions/protocols}}"] if it [=map/exists=],
+ otherwise an empty sequence.
+ 1. If any of the values in |protocols| occur more than once or otherwise fail to match the
+ requirements for elements that comprise the value of
+ \``Sec-WebSocket-Protocol`\` fields as defined by The WebSocket
+ Protocol, then throw a "{{SyntaxError}}" {{DOMException}}. [[!WSP]]
+ 1. Set [=this=]'s [=WebSocketStream/url=] to |urlRecord|.
+ 1. Set [=this=]'s [=WebSocketStream/opened promise=] and [=WebSocketStream/closed promise=] to new
+ promises.
+ 1. Apply backpressure to the WebSocket.
+
+ This means that messages won't be read until the application is ready for them. + + 1. If |options|["{{WebSocketStreamOptions/signal}}"] [=map/exists=], + 1. Let |signal| be |options|["{{WebSocketStreamOptions/signal}}"]. + 1. If |signal| is [=AbortSignal/aborted=], then reject [=this=]'s [=WebSocketStream/opened + promise=] and [=WebSocketStream/closed promise=] with |signal|'s [=abort reason=] and return. + 1. [=AbortSignal/add|Add the following abort steps=] to |signal|: + 1. If the WebSocket connection is not yet [=established=]: [[!WSP]] + 1. [=Fail the WebSocket connection=]. + 1. Set [=this=]'s [=WebSocketStream/ready state=] to {{WebSocket/CLOSING}}. + 1. [=Reject=] [=this=]'s [=WebSocketStream/opened promise=] and [=WebSocketStream/closed + promise=] with |signal|'s [=abort reason=]. + 1. Set [=this=]'s [=WebSocketStream/handshake aborted=] to true. + 1. Let |client| be [=this=]'s [=relevant settings object=]. + 1. Run this step [=in parallel=]: + 1. [=Establish a WebSocket connection=] given |urlRecord|, |protocols|, and |client|. [[!FETCH]] + +
If the [=establish a WebSocket connection=] algorithm fails, it triggers the + [=fail the WebSocket connection=] algorithm, which then invokes the [=close the WebSocket + connection=] algorithm. This establishes that [=the WebSocket connection is closed=], which + rejects the {{WebSocketStream/opened}} and {{WebSocketStream/closed}} promises. +
Applying backpressure will result in no new WebSocket messages being handled until + the backpressure is released. After an implementation-defined amount of data is buffered, the + client will stop reading from the underlying network connection resulting in network-layer + backpressure being applied. +
If any messages are queued, one will be handled immediately as a result. +
This means that backpressure will be applied when the user agent's buffers are + full. + + 1. If [=the WebSocket closing handshake is started|the closing handshake has not yet started=], + [=Send a WebSocket Message=] to |stream| comprised of |data| using |opcode|. + 1. [=Queue a global task=] on the [=WebSocket task source=] given |stream|'s [=relevant global + object=] to resolve |promise| with undefined. + 1. Return |promise|. +
A {{WebSocketError}} object constructed from JavaScript will always have a + [=WebSocketError/closeCode=] and [=WebSocketError/reason=] that JavaScript is permitted to set. + However, the {{WebSocketStream/closed}} promise might be rejected with a {{WebSocketError}} whose + [=WebSocketError/closeCode=] has a value coming from the server that JavaScript is not permitted to + set itself, such as 1001 "Going Away". +
|error| = new {{WebSocketError/constructor(message, init)|WebSocketError}}([|message|[,
+ |init|]])
+ :: Creates a new {{WebSocketError}} object.
+
+ |message| is a string which will be used to initialize the {{DOMException/message}} attribute of
+ the base class.
+
+ The |init| argument is an object whose properties can be set as follows:
+
+ : {{WebSocketCloseInfo/closeCode}}
+ :: A number, either 1000 or any integer in the range 3000 to 4999, inclusive. Any other
+ number will result in an "{{InvalidAccessError}}" {{DOMException}}. If a non-empty
+ {{WebSocketCloseInfo/reason}} is set, defaults to 1000, since there is no way to send a non-empty
+ reason without a close code.
+ : {{WebSocketCloseInfo/reason}}
+ :: A string. Must be 123 bytes or less when converted to [=UTF-8=]. A longer string will result in
+ a "{{SyntaxError}}" {{DOMException}} being thrown. Defaults to the empty string.
+
+ : |error|.{{WebSocketError/closeCode}}
+ :: Returns the [=the WebSocket connection close code=].
+
+ : |error|.{{WebSocketError/reason}}
+ :: Returns the [=the WebSocket connection close reason=].
+new
+WebSocketError(|message|, |init|)
constructor steps are:
+
+ 1. Set [=this=]'s [=DOMException/name=] to "WebSocketError
".
+ 1. Set [=this=]'s [=DOMException/message=] to |message|.
+ 1. Let |code| be |init|["{{WebSocketCloseInfo/closeCode}}"] if it [=map/exists=], or null
+ otherwise.
+ 1. Let |reason| be |init|["{{WebSocketCloseInfo/reason}}"] if it [=map/exists=], or the empty
+ string otherwise.
+ 1. [=Validate close code and reason=] with |code| and |reason|.
+ 1. If |reason| is non-empty, but |code| is null, then set |code| to 1000 ("Normal Closure").
+ 1. Set [=this=]'s [=WebSocketError/closeCode=] to |code|.
+ 1. Set [=this=]'s [=WebSocketError/reason=] to |reason|.
+[=ws=]
".
+ 1. Otherwise, if |urlRecord|'s [=url/scheme=] is "`https`", set |urlRecord|'s [=url/scheme=] to
+ "[=wss=]
".
+ 1. If |urlRecord|'s [=scheme=] is not "[=ws=]
" or "[=wss=]
", then throw a
+ "{{SyntaxError}}" {{DOMException}}.
+ 1. If |urlRecord|'s [=fragment=] is non-null, then throw a "{{SyntaxError}}" {{DOMException}}.
+ 1. Return |urlRecord|.
+The connection is already closing or is already closed. If it has not already, a + {{WebSocket/close}} event will eventually fire if |object| is a {{WebSocket}}, or the + {{WebSocketStream/closed}} promise will become settled if |object| is a {{WebSocketStream}}. + + : If the WebSocket connection is not yet [=established=] [[!WSP]] + :: [=Fail the WebSocket connection=] and set |object|'s [=WebSocket/ready state=] to + {{WebSocket/CLOSING}} (2). [[!WSP]] + +
The [=fail the WebSocket connection=] algorithm invokes the [=close the + WebSocket connection=] algorithm, which then establishes that [=the WebSocket connection is + closed=], which fires the {{WebSocket/close}} event if |object| is a {{WebSocket}}, or settles + the {{WebSocketStream/closed}} promise if |object| is a {{WebSocketStream}}. + + : If the WebSocket closing handshake has not yet been started [[!WSP]] + :: [=Start the WebSocket closing handshake=] and set |object|'s [=WebSocket/ready state=] to + {{WebSocket/CLOSING}} (2). [[!WSP]] + + If |code| is null and |reason| is the empty string, the WebSocket Close frame must not have a + body. + +
The WebSocket Protocol erroneously states that the status code is
+ required for the [=start the WebSocket closing handshake=]
+ algorithm.
+
+ if |reason| is non-empty but |code| is null, then set |code| to 1000 ("Normal Closure").
+
+ If |code| is set, then the status code to use in the WebSocket Close
+ frame must be the integer given by |code|. [[!WSP]]
+
+ If |reason| is non-empty, then |reason|, encoded as UTF-8, must be
+ provided in the Close frame after the status code. [[!WSP]]
+
+ The [=start the WebSocket closing handshake=] algorithm eventually invokes the
+ [=close the WebSocket connection=] algorithm, which then establishes that [=the WebSocket
+ connection is closed=], which fires the {{WebSocket/close}} event if |object| is a
+ {{WebSocket}}, or settles the {{WebSocketStream/closed}} promise if |object| is a
+ {{WebSocketStream}}.
+
+ : Otherwise
+ :: Set |object|'s [=WebSocket/ready state=] to {{WebSocket/CLOSING}} (2).
+
+ [=The WebSocket closing handshake is started=], and will eventually invoke the
+ [=close the WebSocket connection=] algorithm, which will establish that [=the WebSocket
+ connection is closed=], and thus the {{WebSocket/close}} event will fire or the
+ {{WebSocketStream/closed}} promise will resolve, depending on the type of |object|.
+