This specification defines an API that allows web applications to talk to servers and devices that have their own protocols incompatible with those available on the web.

This is a work in progress. All contributions welcome.

{{TCPSocket}} interface

      [Exposed=(Window,Worker), SecureContext]
      interface TCPSocket {
        constructor(DOMString remoteAddress,
                    unsigned short remotePort,
                    optional TCPSocketOptions options = {});

        readonly attribute Promise<TCPSocketOpenInfo> opened;
        readonly attribute Promise<undefined> closed;

        Promise<undefined> close();
      };
    

Methods on this interface typically complete asynchronously, queuing work on the TCPSocket task source.

Instances of {{TCPSocket}} are created with the internal slots described in the following table:

Internal slot Initial value Description (non-normative)
[[\readable]] `null` A {{ReadableStream}} that receives data from the socket
[[\writable]] `null` A {{WritableStream}} that transmits data to the socket
[[\openedPromise]] `new Promise` A {{Promise}} used to wait for the socket to be opened. Corresponds to the {{TCPSocket/opened}} member.
[[\closedPromise]] `new Promise` A {{Promise}} used to wait for the socket to close or error. Corresponds to the {{TCPSocket/closed}} member.

constructor() method

The {{TCPSocket/constructor()}} steps are:
  1. If [=this=]'s [=relevant global object=]'s [=associated Document=] is not [=allowed to use=] the [=policy-controlled feature=] named "[=policy-controlled feature/direct-sockets=]", throw a "{{NotAllowedError}}" {{DOMException}}.
  2. If |options|["{{TCPSocketOptions/keepAliveDelay}}"] is less than 1,000, throw a {{TypeError}}.
  3. If |options|["{{TCPSocketOptions/sendBufferSize}}"] is equal to 0, throw a {{TypeError}}.
  4. If |options|["{{TCPSocketOptions/receiveBufferSize}}"] is equal to 0, throw a {{TypeError}}.
  5. Perform the following steps [=in parallel=].
    1. Invoke the operating system to open a TCP socket using the given |remoteAddress| and |remotePort| and the connection parameters (or their defaults) specified in |options|.
    2. If this fails for any reason, [=queue a global task=] on the [=relevant global object=] of [=this=] using the [=TCPSocket task source=] to run the following steps:
      1. [=Reject=] the {{TCPSocket/[[openedPromise]]}} with a "{{NetworkError}}" {{DOMException}}.
      2. [=Reject=] the {{TCPSocket/[[closedPromise]]}} with a "{{NetworkError}}" {{DOMException}}.
    3. On success, [=queue a global task=] on the [=relevant global object=] of [=this=] using the [=TCPSocket task source=] to run the following steps:
      1. [=initialize TCPSocket readable stream|Initialize=] {{TCPSocket/[[readable]]}}.
      2. [=initialize TCPSocket writable stream|Initialize=] {{TCPSocket/[[writable]]}}.
      3. Let |openInfo:TCPSocketOpenInfo| be a new {{TCPSocketOpenInfo}}.
      4. Set |openInfo|["{{TCPSocketOpenInfo/readable}}"] to [=this=].{{TCPSocket/[[readable]]}}.
      5. Set |openInfo|["{{TCPSocketOpenInfo/writable}}"] to [=this=].{{TCPSocket/[[writable]]}}.
      6. Populate the remaining fields of |openInfo| using the information provided by the operating system: |openInfo|["{{TCPSocketOpenInfo/remoteAddress}}"], |openInfo|["{{TCPSocketOpenInfo/remotePort}}"], |openInfo|["{{TCPSocketOpenInfo/localAddress}}"] and |openInfo|["{{TCPSocketOpenInfo/localPort}}"].
      7. [=Resolve=] [=this=].{{TCPSocket/[[openedPromise]]}} with |openInfo|.

TCPSocketOptions dictionary

          enum SocketDnsQueryType {
            "ipv4",
            "ipv6"
          };

          dictionary TCPSocketOptions {
            [EnforceRange] unsigned long sendBufferSize;
            [EnforceRange] unsigned long receiveBufferSize;

            boolean noDelay = false;
            [EnforceRange] unsigned long keepAliveDelay;

            SocketDnsQueryType dnsQueryType;
          };
        
sendBufferSize member
The requested send buffer size, in bytes. If not specified, then platform-specific default value will be used.
receiveBufferSize member
The requested receive buffer size, in bytes. If not specified, then platform-specific default value will be used.
noDelay member
Enables the `TCP_NODELAY` option, disabling Nagle's algorithm.
No-Delay is disabled by default.
keepAliveDelay member
If specified, enables TCP Keep-Alive by setting `SO_KEEPALIVE` option on the socket to `true`. The way the actual delay is set is platform-specific:
  1. On Linux & ChromeOS `keepAliveDelay` is applied to `TCP_KEEPIDLE` and `TCP_KEEPINTVL`;
  2. On MacOS `keepAliveDelay` affects `TCP_KEEPALIVE`;
  3. On Windows `keepAliveDelay` is replicated to `keepalivetime` and `keepaliveinterval` of `SIO_KEEPALIVE_VALS`.
Keep-Alive is disabled by default.
dnsQueryType member
Indicates whether IPv4 or IPv6 record should be returned during DNS lookup. If omitted, the OS will select the record type(s) to be queried automatically depending on IPv4/IPv6 settings and reachability.

{{TCPSocket/[[readable]]}} attribute (internal)

The steps to initialize the TCPSocket readable stream are:
  1. Let |stream:ReadableStream| be a [=new=] {{ReadableStream}}.
  2. Let |pullAlgorithm| be the following steps:
    1. Let |desiredSize| be the [=ReadableStream/desired size to fill up to the high water mark=] for [=this=].{{TCPSocket/[[readable]]}}.
    2. If [=this=].{{TCPSocket/[[readable]]}}'s [=ReadableStream/current BYOB request view=] is non-null, then set |desiredSize| to [=this=].{{TCPSocket/[[readable]]}}'s [=ReadableStream/current BYOB request view=]'s [=BufferSource/byte length=].
    3. Run the following steps in parallel:
      1. Invoke the operating system to read up to |desiredSize| bytes from the socket, placing the result in the [=byte sequence=] |bytes|.
      2. [=Queue a global task=] on the [=relevant global object=] of [=this=] using the [=TCPSocket task source=] to run the following steps:
        1. If the connection was closed gracefully, run the following steps:
          1. Invoke [=ReadableStream/close=] on [=this=].{{TCPSocket/[[readable]]}}.
          2. Invoke the steps to [=handle closing the TCPSocket readable stream=].
          This is triggered by the peer sending a packet with the FIN flag set and is typically indicated by the operating system returning 0 bytes when asked for more data from the socket.
        2. If no errors were encountered, then:
          1. If [=this=].{{TCPSocket/[[readable]]}}'s [=ReadableStream/current BYOB request view=] is non-null, then [=ArrayBufferView/write=] |bytes| into [=this=].{{TCPSocket/[[readable]]}}'s [=ReadableStream/current BYOB request view=], and set |view| to [=this=].{{TCPSocket/[[readable]]}}'s [=ReadableStream/current BYOB request view=].
          2. Otherwise, set |view| to the result of [=ArrayBufferView/create|creating=] a {{Uint8Array}} from |bytes| in [=this=]'s [=relevant Realm=].
          3. [=ReadableStream/Enqueue=] |view| into [=this=].{{TCPSocket/[[readable]]}}.
        3. If a network or operating system error was encountered, invoke [=ReadableStream/error=] on [=this=].{{TCPSocket/[[readable]]}} with a "{{NetworkError}}" {{DOMException}} and invoke the steps to [=handle closing the TCPSocket readable stream=].
    4. Return [=a promise resolved with=] `undefined`.
  3. Let |cancelAlgorithm| be the following steps:
    1. Invoke the steps to [=handle closing the TCPSocket readable stream=].
    2. Return [=a promise resolved with=] `undefined`.
  4. [=ReadableStream/Set up with byte reading support=] |stream| with [=ReadableStream/set up with byte reading support/pullAlgorithm=] set to |pullAlgorithm|, [=ReadableStream/set up with byte reading support/cancelAlgorithm=] set to |cancelAlgorithm|, and [=ReadableStream/set up with byte reading support/highWaterMark=] set to an implementation-defined value.
  5. Set [=this=].{{TCPSocket/[[readable]]}} to |stream|.
To handle closing the TCPSocket readable stream perform the following steps:
  1. If [=this=].{{TCPSocket/[[writable]]}} is active, abort these steps.
  2. Run the following steps [=in parallel=].
    1. Invoke the operating system to close the socket.
    2. [=Queue a global task=] on the [=relevant global object=] of [=this=] using the [=TCPSocket task source=] to run the following steps:
      • If [=this=].{{TCPSocket/[[writable]]}} is errored, [=reject=] [=this=].{{TCPSocket/[[closedPromise]]}} with [=this=].{{TCPSocket/[[writable]]}}.`[[storedPromise]]`.
      • Otherwise, if [=this=].{{TCPSocket/[[readable]]}} is errored, [=reject=] [=this=].{{TCPSocket/[[closedPromise]]}} with [=this=].{{TCPSocket/[[readable]]}}.`[[storedPromise]]`.
      • Otherwise, [=resolve=] [=this=].{{TCPSocket/[[closedPromise]]}} with `undefined`.

{{TCPSocket/[[writable]]}} attribute (internal)

The steps to initialize the TCPSocket writable stream are:
  1. Let |stream:WritableStream| be a [=new=] {{WritableStream}}.
  2. Let |signal:AbortSignal| be |stream|'s [=WritableStream/signal=].
  3. Let |writeAlgorithm| be the following steps, given |chunk|:
    1. Let |promise:Promise| be [=a new promise=].
    2. Assert: |signal| is not [=AbortSignal/aborted=].
    3. If |chunk| cannot be [=converted to an IDL value=] of type {{BufferSource}}, reject |promise| with a {{TypeError}} and return |promise|. Otherwise, save the result of the conversion in |source:BufferSource|.
    4. [=Get a copy of the buffer source=] |source| and save the result in |bytes|.
    5. [=In parallel=], run the following steps:
      1. Invoke the operating system to write |bytes| to the socket.
        The operating system may return from this operation once |bytes| has been queued for transmission rather than after it has been transmitted.
      2. [=Queue a global task=] on the [=relevant global object=] of [=this=] using the [=TCPSocket task source=] to run the following steps:
        1. If the chunk was successfully written, [=resolve=] |promise| with `undefined`.
          [[STREAMS]] specifies that |writeAlgorithm| will only be invoked after the {{Promise}} returned by a previous invocation of this algorithm has resolved. For efficiency an implementation is allowed to resolve this {{Promise}} early in order to coalesce multiple chunks waiting in the {{WritableStream}}'s internal queue into a single request to the operating system.
        2. If a network or operating system error was encountered:
          1. [=Reject=] |promise| with a "{{NetworkError}}" {{DOMException}}.
          2. Invoke the steps to [=handle closing the TCPSocket writable stream=].
        3. If |signal| is [=AbortSignal/aborted=], [=reject=] |promise| with |signal|'s [=AbortSignal/abort reason=].
    6. Return |promise|.
  4. Let |abortAlgorithm| be the following steps:
    1. Let |promise| be [=a new promise=].
    2. Run the following steps [=in parallel=]:
      1. Invoke the operating system to shutdown the socket for writing.
      2. [=Queue a global task=] on the [=relevant global object=] of [=this=] using the [=TCPSocket task source=] to run the following steps:
        1. Invoke the steps to [=handle closing the TCPSocket writable stream=].
        2. [=Resolve=] |promise| with `undefined`.
    3. Return |promise|.
  5. Let |closeAlgorithm| be the following steps:
    1. Let |promise| be [=a new promise=].
    2. Run the following steps [=in parallel=].
      1. Invoke the operating system to shutdown the socket for writing.
      2. [=Queue a global task=] on the [=relevant global object=] of [=this=] using the [=TCPSocket task source=] to run the following steps:
        1. Invoke the steps to [=handle closing the TCPSocket writable stream=].
        2. If |signal| is [=AbortSignal/aborted=], [=reject=] |promise| with |signal|'s [=AbortSignal/abort reason=].
        3. [=Resolve=] |promise| with `undefined`.
    3. Return |promise|.
  6. [=WritableStream/Set up=] |stream| with [=WritableStream/set up/writeAlgorithm=] set to |writeAlgorithm|, [=WritableStream/set up/abortAlgorithm=] set to |abortAlgorithm|, [=WritableStream/set up/closeAlgorithm=] set to |closeAlgorithm|, [=WritableStream/set up/highWaterMark=] set to an implementation-defined value.
  7. [=AbortSignal/Add=] the following abort steps to |signal|:
    1. Cause any invocation of the operating system to write to the socket to return as soon as possible no matter how much data has been written.
  8. Set [=this=].{{TCPSocket/[[writable]]}} to |stream|.
To handle closing the TCPSocket writable stream perform the following steps:
  1. If [=this=].{{TCPSocket/[[readable]]}} is active, abort these steps.
  2. Run the following steps [=in parallel=].
    1. Invoke the operating system to close the socket.
    2. [=Queue a global task=] on the [=relevant global object=] of [=this=] using the [=TCPSocket task source=] to run the following steps:
      • If [=this=].{{TCPSocket/[[readable]]}} is errored, [=reject=] [=this=].{{TCPSocket/[[closedPromise]]}} with [=this=].{{TCPSocket/[[readable]]}}.`[[storedPromise]]`.
      • Otherwise, if [=this=].{{TCPSocket/[[writable]]}} is errored, [=reject=] [=this=].{{TCPSocket/[[closedPromise]]}} with [=this=].{{TCPSocket/[[writable]]}}.`[[storedPromise]]`.
      • Otherwise, [=resolve=] [=this=].{{TCPSocket/[[closedPromise]]}} with `undefined`.

opened attribute

When called, returns the [=this=].{{TCPSocket/[[openedPromise]]}}.

TCPSocketOpenInfo dictionary

          dictionary TCPSocketOpenInfo {
            ReadableStream readable;
            WritableStream writable;

            DOMString remoteAddress;
            unsigned short remotePort;

            DOMString localAddress;
            unsigned short localPort;
          };
        
readable member
The readable side of the socket. Set to {{TCPSocket/[[readable]]}}.
writable member
The writable side of the socket. Set to {{TCPSocket/[[writable]]}}.
remoteAddress member
Resolved remote IP address that the socket is connected to.
remotePort member
Remote port that the socket is connected to.
localAddress member
Local IP address that the socket is bound to.
localPort member
Local port that the socket is bound to.

closed attribute

When called, returns the [=this=].{{TCPSocket/[[closedPromise]]}}.

close() method

The {{TCPSocket/close()}} method steps are:
  1. If [=this=].{{TCPSocket/[[openedPromise]]}} is rejected or not yet resolved, [=reject=] with "{{InvalidStateError}}" {{DOMException}}.
  2. If [=this=].{{TCPSocket/[[closedPromise]]}} is settled, return [=this=].{{TCPSocket/[[closedPromise]]}}.
  3. If [=this=].{{TCPSocket/[[readable]]}} or [=this=].{{TCPSocket/[[writable]]}} are locked, [=reject=] with "{{InvalidStateError}}" {{DOMException}}.
  4. Let |cancelPromise:Promise| be the result of invoking [=ReadableStream/cancel=] on [=this=].{{TCPSocket/[[readable]]}}.
  5. Set |cancelPromise|.[[\PromiseIsHandled]] to true.
  6. Let |abortPromise:Promise| be the result of invoking [=WritableStream/abort=] on [=this=].{{TCPSocket/[[writable]]}}.
  7. Set |abortPromise|.[[\PromiseIsHandled]] to true.
  8. Return [=this=].{{TCPSocket/[[closedPromise]]}}.

{{UDPSocket}} interface

      [Exposed=(Window,Worker), SecureContext]
      interface UDPSocket {
        constructor(optional UDPSocketOptions options = {});

        readonly attribute Promise<UDPSocketOpenInfo> opened;
        readonly attribute Promise<undefined> closed;

        Promise<undefined> close();
      };
    
Methods on this interface typically complete asynchronously, queuing work on the UDPSocket task source.

Instances of {{UDPSocket}} are created with the internal slots described in the following table:

Internal slot Initial value Description (non-normative)
[[\readable]] `null` A {{ReadableStream}} that receives data from the socket
[[\writable]] `null` A {{WritableStream}} that transmits data to the socket
[[\openedPromise]] `new Promise` A {{Promise}} used to wait for the socket to be opened. Corresponds to the {{UDPSocket/opened}} member.
[[\closedPromise]] `new Promise` A {{Promise}} used to wait for the socket to close or error. Corresponds to the {{UDPSocket/closed}} member.

constructor() method

{{UDPSocket}} can operate in either {{UDPSocket/connected}} or {{UDPSocket/bound}} mode which is decided based on the provided set of constructor options. The {{UDPSocket/constructor()}} steps are:
  1. If [=this=]'s [=relevant global object=]'s [=associated Document=] is not [=allowed to use=] the [=policy-controlled feature=] named "[=policy-controlled feature/direct-sockets=]", throw a "{{NotAllowedError}}" {{DOMException}}.
  2. If only one of |options|["{{UDPSocketOptions/remoteAddress}}"] and |options|["{{UDPSocketOptions/remotePort}}"] is specified, throw a {{TypeError}}.
  3. Alternatively, if both |options|["{{UDPSocketOptions/remoteAddress}}"] and |options|["{{UDPSocketOptions/remotePort}}"] are specified, assume {{UDPSocket/connected}} {{UDPSocket/mode}}.
  4. If |options|["{{UDPSocketOptions/localPort}}"] is equal to 0 or specified without |options|["{{UDPSocketOptions/localAddress}}"], throw a {{TypeError}}.
  5. If |options|["{{UDPSocketOptions/localAddress}}"] is specified:
    1. If {{UDPSocket/connected}} {{UDPSocket/mode}} was previously inferred, throw a {{TypeError}}.
    2. If |options|["{{UDPSocketOptions/localAddress}}"] is not a valid IP address, throw a {{TypeError}}.
    3. Assume {{UDPSocket/bound}} {{UDPSocket/mode}}.
  6. If no {{UDPSocket/mode}} has been inferred at this point, throw a {{TypeError}}.
  7. If |options|["{{UDPSocketOptions/dnsQueryType}}"] is specified in {{UDPSocket/bound}} {{UDPSocket/mode}}, throw a {{TypeError}}.
  8. If |options|["{{UDPSocketOptions/ipv6Only}}"] is specified:
    1. If inferred {{UDPSocket/mode}} is {{UDPSocket/connected}}, throw a {{TypeError}}.
    2. If |options|["{{UDPSocketOptions/localAddress}}"] is not equal to the IPv6 unspecified address (`::`), throw a {{TypeError}}.
  9. If |options|["{{UDPSocketOptions/sendBufferSize}}"] is equal to 0, throw a {{TypeError}}.
  10. If |options|["{{UDPSocketOptions/receiveBufferSize}}"] is equal to 0, throw a {{TypeError}}.
  11. Perform the following steps [=in parallel=].
    1. Invoke the operating system to open a UDP socket using the inferred {{UDPSocket/mode}} and the parameters (or their defaults) specified in |options|.
    2. If this fails for any reason, [=queue a global task=] on the [=relevant global object=] of [=this=] using the [=UDPSocket task source=] to run the following steps:
      1. [=Reject=] the {{UDPSocket/[[openedPromise]]}} with a "{{NetworkError}}" {{DOMException}}.
      2. [=Reject=] the {{UDPSocket/[[closedPromise]]}} with a "{{NetworkError}}" {{DOMException}}.
    3. On success, [=queue a global task=] on the [=relevant global object=] of [=this=] using the [=UDPSocket task source=] to run the following steps:
      1. [=initialize UDPSocket readable stream|Initialize=] {{UDPSocket/[[readable]]}}.
      2. [=initialize UDPSocket writable stream|Initialize=] {{UDPSocket/[[writable]]}}.
      3. Let |openInfo:UDPSocketOpenInfo| be a new {{UDPSocketOpenInfo}}.
      4. Set |openInfo|["{{UDPSocketOpenInfo/readable}}"] to [=this=].{{UDPSocket/[[readable]]}}.
      5. Set |openInfo|["{{UDPSocketOpenInfo/writable}}"] to [=this=].{{UDPSocket/[[writable]]}}.
      6. Populate the remaining fields of |openInfo| using the information provided by the operating system:
        • For {{UDPSocket/bound}} {{UDPSocket/mode}}, populate only |openInfo|["{{UDPSocketOpenInfo/localAddress}}"] and |openInfo|["{{UDPSocketOpenInfo/localPort}}"].
        • For {{UDPSocket/connected}} {{UDPSocket/mode}}, populate |openInfo|["{{UDPSocketOpenInfo/localAddress}}"] and |openInfo|["{{UDPSocketOpenInfo/localPort}}"] as well as |openInfo|["{{UDPSocketOpenInfo/remoteAddress}}"] and |openInfo|["{{UDPSocketOpenInfo/remotePort}}"].
      7. Resolve [=this=].{{UDPSocket/[[openedPromise]]}} with |openInfo|.

UDPSocketOptions dictionary

          dictionary UDPSocketOptions {
            DOMString remoteAddress;
            [EnforceRange] unsigned short remotePort;

            DOMString localAddress;
            [EnforceRange] unsigned short localPort;

            [EnforceRange] unsigned long sendBufferSize;
            [EnforceRange] unsigned long receiveBufferSize;

            SocketDnsQueryType dnsQueryType;
            boolean ipv6Only;
          };
        
remoteAddress member
The remote IP address to connect the socket to.
remotePort member
The remote port to connect the socket to.
localAddress member
The local IP address to bind the socket to.
localPort member
The local port to bind the socket to. Leave this field empty to let the OS pick one on its own.
sendBufferSize member
The requested send buffer size, in bytes. If not specified, then platform-specific default value will be used.
receiveBufferSize member
The requested receive buffer size, in bytes. If not specified, then platform-specific default value will be used.
dnsQueryType member
Indicates whether IPv4 or IPv6 record should be returned during DNS lookup. If omitted, the OS will select the record type(s) to be queried automatically depending on IPv4/IPv6 settings and reachability.
This field can only be supplied in {{UDPSocket/connected}} {{UDPSocket/mode}}.
ipv6Only member
Enables or disables `IPV6_V6ONLY` to either restrict connections to IPv6 only or allow both IPv4/IPv6 connections.
This field can only be supplied in {{UDPSocket/bound}} {{UDPSocket/mode}} when `localAddress` is equal to the IPv6 unspecified address (`::`).
Leave this field empty to retain default platform-defined behavior (`true` on Windows and `false` on Posix).

UDPMessage dictionary

{{UDPSocket/[[readable]]}} and {{UDPSocket/[[writable]]}} streams operate on {{UDPMessage}} objects.
          dictionary UDPMessage {
            BufferSource data;
            DOMString remoteAddress;
            unsigned short remotePort;
            SocketDnsQueryType dnsQueryType;
          };
        
data member
The user message represented as {{BufferSource}}.
For {{UDPSocket/[[readable]]}} the underlying type is always {{Uint8Array}}.
remoteAddress member
The remote address where the message came from or where it should be send to.
  • In {{UDPSocket/connected}} {{UDPSocket/mode}} this field is always unspecified; attempting to set it while writing will throw (see {{UDPSocket/[[writable]]}}).
  • In {{UDPSocket/bound}} {{UDPSocket/mode}} this field represents the remote host that this packet came from for {{UDPSocket/[[readable]]}} or instructs the socket about the destination host for {{UDPSocket/[[writable]]}}.
remotePort member
The remote port where the message came from or where it should be sent to.
  • In {{UDPSocket/connected}} {{UDPSocket/mode}} this field is always unspecified; attempting to set it while writing will throw (see {{UDPSocket/[[writable]]}}).
  • In {{UDPSocket/bound}} {{UDPSocket/mode}} this field represents the remote port that this packet came from for {{UDPSocket/[[readable]]}} or instructs the socket about the destination port for {{UDPSocket/[[writable]]}}.
dnsQueryType member
Indicates whether IPv4 or IPv6 record should be returned during DNS lookup. If omitted, the OS will select the record type(s) to be queried automatically depending on IPv4/IPv6 settings and reachability.
This field is always unset for {{UDPMessage}} instances received from {{UDPSocket/[[readable]]}} stream regardless of socket's operating {{UDPSocket/mode}}. For {{UDPSocket/[[writable]]}} this can only be specified in {{UDPSocket/bound}} {{UDPSocket/mode}}; attempting to set the field in {{UDPSocket/connected}} {{UDPSocket/mode}} will throw.

{{UDPSocket/[[readable]]}} attribute (internal)

The steps to initialize the UDPSocket readable stream are:
  1. Let |stream:ReadableStream| be a [=new=] {{ReadableStream}}.
  2. Let |pullAlgorithm| be the following steps:
    1. Let |desiredSize| be the [=ReadableStream/desired size to fill up to the high water mark=] for [=this=].{{UDPSocket/[[readable]]}}.
    2. Run the following steps in parallel:
      1. Invoke the operating system to provide up to |desiredSize| UDP packets from the socket.
      2. [=Queue a global task=] on the [=relevant global object=] of [=this=] using the [=UDPSocket task source=] to run the following steps for each received packet:
        1. If no errors were encountered, for each packet received run the following steps:
          1. Let |bytes| be a [=byte sequence=] containing the packet payload.
          2. Let |buffer| be a [=new=] {{ArrayBuffer}} created from |bytes|.
          3. Let |chunk| be a [=new=] {{Uint8Array}} view over |buffer|, who's length is the length of |bytes|.
          4. Let |message:UDPMessage| be a new {{UDPMessage}}.
          5. Set |message|["{{UDPMessage/data}}"] to |chunk|.
          6. If the socket is operating in {{UDPSocket/bound}} {{UDPSocket/mode}}:
            1. Set |message|["{{UDPMessage/remoteAddress}}"] to the source address of the packet.
            2. Set |message|["{{UDPMessage/remotePort}}"] to the source port of the packet.
          7. Invoke [=ReadableStream/enqueue=] on [=this=].{{UDPSocket/[[readable]]}} with |message|.
        2. If a network or operating system error was encountered, invoke [=ReadableStream/error=] on [=this=].{{UDPSocket/[[readable]]}} with a "{{NetworkError}}" {{DOMException}}, discard other packets and invoke the steps to [=handle closing the UDPSocket readable stream=].
    3. Return [=a promise resolved with=] `undefined`.
  3. Let |cancelAlgorithm| be the following steps:
    1. Invoke the steps to [=handle closing the UDPSocket readable stream=].
    2. Return [=a promise resolved with=] `undefined`.
  4. [=ReadableStream/Set up=] |stream| with [=ReadableStream/set up/pullAlgorithm=] set to |pullAlgorithm|, [=ReadableStream/set up/cancelAlgorithm=] set to |cancelAlgorithm|, [=ReadableStream/set up/highWaterMark=] [=ReadableStream/Set up=] |stream| with set to an implementation-defined value.
  5. Set [=this=].{{UDPSocket/[[readable]]}} to |stream|.
To handle closing the UDPSocket readable stream perform the following steps:
  1. If [=this=].{{UDPSocket/[[writable]]}} is active, abort these steps.
  2. Run the following steps [=in parallel=].
    1. Invoke the operating system to close the socket.
    2. [=Queue a global task=] on the [=relevant global object=] of [=this=] using the [=UDPSocket task source=] to run the following steps:
      • If [=this=].{{UDPSocket/[[writable]]}} is errored, [=reject=] [=this=].{{UDPSocket/[[closedPromise]]}} with [=this=].{{UDPSocket/[[writable]]}}.`[[storedPromise]]`.
      • Otherwise, if [=this=].{{UDPSocket/[[readable]]}} is errored, [=reject=] [=this=].{{UDPSocket/[[closedPromise]]}} with [=this=].{{UDPSocket/[[readable]]}}.`[[storedPromise]]`.
      • Otherwise, [=resolve=] [=this=].{{UDPSocket/[[closedPromise]]}} with `undefined`.

{{UDPSocket/[[writable]]}} attribute (internal)

The steps to initialize the UDPSocket writable stream are:
  1. Let |stream:WritableStream| be a [=new=] {{WritableStream}}.
  2. Let |signal:AbortSignal| be |stream|'s [=WritableStream/signal=].
  3. Let |writeAlgorithm| be the following steps, given |chunk|:
    1. Let |promise:Promise| be [=a new promise=].
    2. Assert: |signal| is not [=AbortSignal/aborted=].
    3. Let |message:UDPMessage| be a new {{UDPMessage}}.
    4. If |chunk| cannot be [=converted to an IDL value=] of type {{UDPMessage}}, reject |promise| with a {{TypeError}} and return |promise|. Otherwise, save the result of the conversion in |message|.
    5. If either |message|["{{UDPMessage/remoteAddress}}"], |message|["{{UDPMessage/remotePort}}"] or |message|["{{UDPMessage/dnsQueryType}}"] is specified in {{UDPSocket/connected}} {{UDPSocket/mode}}, reject |promise| with a {{TypeError}} and return |promise|.
    6. If either |message|["{{UDPMessage/remoteAddress}}"] or |message|["{{UDPMessage/remotePort}}"] is not specified in {{UDPSocket/bound}} {{UDPSocket/mode}}, reject |promise| with a {{TypeError}} and return |promise|.
    7. [=Get a copy of the buffer source=] |message|["{{UDPMessage/data}}"] and save the result in |bytes|.
    8. [=In parallel=], run the following steps:
      1. Invoke the operating system to send |bytes| to the socket.
        • In {{UDPSocket/connected}} {{UDPSocket/mode}} the data is routed to the address/port specified upon construction.
        • In {{UDPSocket/bound}} {{UDPSocket/mode}} the data is routed to |message|["{{UDPMessage/remoteAddress}}"] and |message|["{{UDPMessage/remotePort}}"]. |message|["{{UDPMessage/dnsQueryType}}"] can be optionally supplied to control whether DNS resolution routine returns an IPv4 or an IPv6 record.
        The operating system may return from this operation once |bytes| has been queued for transmission rather than after it has been transmitted.
      2. [=Queue a global task=] on the [=relevant global object=] of [=this=] using the [=UDPSocket task source=] to run the following steps:
        1. If the data was successfully written, [=resolve=] |promise| with `undefined`.
        2. If a network or operating system error was encountered:
          1. [=Reject=] |promise| with a "{{NetworkError}}" {{DOMException}}.
          2. Invoke the steps to [=handle closing the UDPSocket writable stream=].
        3. If |signal| is [=AbortSignal/aborted=], [=reject=] |promise| with |signal|'s [=AbortSignal/abort reason=].
    9. Return |promise|.
  4. Let |abortAlgorithm| be the following steps:
    1. Invoke the steps to [=handle closing the UDPSocket writable stream=].
    2. Return [=a promise resolved with=] `undefined`.
  5. Let |closeAlgorithm| be the following steps:
    1. Invoke the steps to [=handle closing the UDPSocket writable stream=].
    2. Return [=a promise resolved with=] `undefined`.
  6. [=WritableStream/Set up=] |stream| with [=WritableStream/set up/writeAlgorithm=] set to |writeAlgorithm|, [=WritableStream/set up/abortAlgorithm=] set to |abortAlgorithm|, [=WritableStream/set up/closeAlgorithm=] set to |closeAlgorithm|, [=WritableStream/set up/highWaterMark=] set to an implementation-defined value.
  7. [=AbortSignal/Add=] the following abort steps to |signal|:
    1. Cause any invocation of the operating system to write to the socket to return as soon as possible no matter how much data has been written.
  8. Set [=this=].{{UDPSocket/[[writable]]}} to |stream|.
To handle closing the UDPSocket writable stream perform the following steps:
  1. If [=this=].{{UDPSocket/[[readable]]}} is active, abort these steps.
  2. Run the following steps [=in parallel=].
    1. Invoke the operating system to close the socket.
    2. [=Queue a global task=] on the [=relevant global object=] of [=this=] using the [=UDPSocket task source=] to run the following steps:
      • If [=this=].{{UDPSocket/[[readable]]}} is errored, [=reject=] [=this=].{{UDPSocket/[[closedPromise]]}} with [=this=].{{UDPSocket/[[readable]]}}.`[[storedPromise]]`.
      • Otherwise, if [=this=].{{UDPSocket/[[writable]]}} is errored, [=reject=] [=this=].{{UDPSocket/[[closedPromise]]}} with [=this=].{{UDPSocket/[[writable]]}}.`[[storedPromise]]`.
      • Otherwise, [=resolve=] [=this=].{{UDPSocket/[[closedPromise]]}} with `undefined`.

opened attribute

When called, returns the [=this=].{{UDPSocket/[[openedPromise]]}}.

UDPSocketOpenInfo dictionary

          dictionary UDPSocketOpenInfo {
            ReadableStream readable;
            WritableStream writable;

            DOMString remoteAddress;
            unsigned short remotePort;

            DOMString localAddress;
            unsigned short localPort;
          };
        
readable member
The readable side of the socket. Set to {{UDPSocket/[[readable]]}}.
writable member
The writable side of the socket. Set to {{UDPSocket/[[writable]]}}.
remoteAddress member
Resolved remote IP address that the socket is communicating with.
remotePort member
Remote port that the socket is communicating with.
localAddress member
Local IP address that the socket is bound to.
localPort member
Local port that the socket is bound to.

closed attribute

When called, returns the [=this=].{{UDPSocket/[[closedPromise]]}}.

close() method

The {{UDPSocket/close()}} method steps are:
  1. If [=this=].{{UDPSocket/[[openedPromise]]}} is rejected or not yet resolved, [=reject=] with "{{InvalidStateError}}" {{DOMException}}.
  2. If [=this=].{{UDPSocket/[[closedPromise]]}} is settled, return [=this=].{{UDPSocket/[[closedPromise]]}}.
  3. If [=this=].{{UDPSocket/[[readable]]}} or [=this=].{{UDPSocket/[[writable]]}} are locked, [=reject=] with "{{InvalidStateError}}" {{DOMException}}.
  4. Let |cancelPromise:Promise| be the result of invoking [=ReadableStream/cancel=] on [=this=].{{UDPSocket/[[readable]]}}.
  5. Set |cancelPromise|.[[\PromiseIsHandled]] as handled.
  6. Let |abortPromise:Promise| be the result of invoking [=WritableStream/abort=] on [=this=].{{UDPSocket/[[writable]]}}.
  7. Set |abortPromise|.[[\PromiseIsHandled]] as handled.
  8. Return [=this=].{{UDPSocket/[[closedPromise]]}}.

{{TCPServerSocket}} interface

      [Exposed=(Window,Worker), SecureContext]
      interface TCPServerSocket {
        constructor(DOMString localAddress,
                    optional TCPServerSocketOptions options = {});

        readonly attribute Promise<TCPServerSocketOpenInfo> opened;
        readonly attribute Promise<undefined> closed;

        Promise<undefined> close();
      };
    

Methods on this interface typically complete asynchronously, queuing work on the TCPServerSocket task source.

Instances of {{TCPServerSocket}} are created with the internal slots described in the following table:

Internal slot Initial value Description (non-normative)
[[\readable]] `null` A {{ReadableStream}} used to accept incoming connections.
[[\openedPromise]] `new Promise` A {{Promise}} used to wait for the socket to be opened. Corresponds to the {{TCPServerSocket/opened}} member.
[[\closedPromise]] `new Promise` A {{Promise}} used to wait for the socket to close or error. Corresponds to the {{TCPServerSocket/closed}} member.

constructor() method

The {{TCPServerSocket/constructor()}} steps are:
  1. If [=this=]'s [=relevant global object=]'s [=associated Document=] is not [=allowed to use=] the [=policy-controlled feature=] named "[=policy-controlled feature/direct-sockets=]", throw a "{{NotAllowedError}}" {{DOMException}}.
  2. If |localAddress| is not a valid IP address, throw a {{TypeError}}.
  3. If |options|["{{TCPServerSocketOptions/localPort}}"] is equal to 0, throw a {{TypeError}}.
  4. If |options|["{{TCPServerSocketOptions/backlog}}"] is equal to 0, throw a {{TypeError}}.
  5. If |options|["{{TCPServerSocketOptions/ipv6Only}}"] is true but `localAddress` is not equal to the IPv6 unspecified address (`::`), throw a {{TypeError}}.
  6. Perform the following steps [=in parallel=].
    1. Invoke the operating system to open a TCP server socket using the given |localAddress| and the connection parameters (or their defaults) specified in |options|.
    2. If this fails for any reason, [=queue a global task=] on the [=relevant global object=] of [=this=] using the [=TCPServerSocket task source=] to run the following steps:
      1. [=Reject=] the {{TCPServerSocket/[[openedPromise]]}} with a "{{NetworkError}}" {{DOMException}}.
      2. [=Reject=] the {{TCPServerSocket/[[closedPromise]]}} with a "{{NetworkError}}" {{DOMException}}.
    3. On success, [=queue a global task=] on the [=relevant global object=] of [=this=] using the [=TCPServerSocket task source=] to run the following steps:
      1. [=initialize TCPServerSocket readable stream|Initialize=] {{TCPServerSocket/[[readable]]}}.
      2. Let |openInfo:TCPServerSocketOpenInfo| be a new {{TCPServerSocketOpenInfo}}.
      3. Set |openInfo|["{{TCPServerSocketOpenInfo/readable}}"] to [=this=].{{TCPServerSocket/[[readable]]}}.
      4. Populate the remaining fields of |openInfo| using the information provided by the operating system: |openInfo|["{{TCPServerSocketOpenInfo/localAddress}}"] and |openInfo|["{{TCPServerSocketOpenInfo/localPort}}"].
      5. [=Resolve=] [=this=].{{TCPServerSocket/[[openedPromise]]}} with |openInfo|.

TCPServerSocketOptions dictionary

          dictionary TCPServerSocketOptions {
            [EnforceRange] unsigned short localPort;
            [EnforceRange] unsigned long backlog;

            boolean ipv6Only;
          };
        
localPort member
The port to open the socket on.
Leave this field empty to let the OS pick one on its own.
backlog member
The size of the OS accept queue.
Leave this field empty to let the OS pick a reasonable platform-specific default.
ipv6Only member
Enables or disables `IPV6_V6ONLY` to either restrict connections to IPv6 only or allow both IPv4/IPv6 connections.
This field can only be supplied when `localAddress` is equal to the IPv6 unspecified address (`::`).
Leave this field empty to retain default platform-defined behavior (`true` on Windows and `false` on Posix).

{{TCPServerSocket/[[readable]]}} attribute (internal)

The steps to initialize the TCPServerSocket readable stream are:
  1. Let |stream:ReadableStream| be a [=new=] {{ReadableStream}}.
  2. Let |pullAlgorithm| be the following steps:
    1. Let |desiredSize| be the desired size of [=this=].{{TCPServerSocket/[[readable]]}}'s internal queue.
    2. Run the following steps in parallel:
      1. Invoke the operating system to accept up to |desiredSize| incoming connections.
      2. [=Queue a global task=] on the [=relevant global object=] of [=this=] using the [=TCPServerSocket task source=] to run the following steps:
        1. If the connection was closed gracefully, run the following steps:
          1. Invoke [=ReadableStream/close=] on [=this=].{{TCPServerSocket/[[readable]]}}.
          2. Invoke the steps to [=handle closing the TCPServerSocket readable stream=].
        2. If no errors were encountered run the following steps:
          1. Let |tcpSocket| be a [=new=] {{TCPSocket}} accepted by the operating system.
          2. Invoke [=ReadableStream/enqueue=] on [=this=].{{TCPServerSocket/[[readable]]}} with |tcpSocket|.
        3. If a network or operating system error was encountered, invoke [=ReadableStream/error=] on [=this=].{{TCPServerSocket/[[readable]]}} with a "{{NetworkError}}" {{DOMException}} and invoke the steps to [=handle closing the TCPServerSocket readable stream=].
    3. Return [=a promise resolved with=] `undefined`.
  3. Let |cancelAlgorithm| be the following steps:
    1. Invoke the steps to [=handle closing the TCPServerSocket readable stream=].
    2. Return [=a promise resolved with=] `undefined`.
  4. [=ReadableStream/Set up=] |stream| with pullAlgorithm set to |pullAlgorithm|, cancelAlgorithm set to |cancelAlgorithm|, highWaterMark set to an implementation-defined value.
  5. Set [=this=].{{TCPServerSocket/[[readable]]}} to |stream|.
To handle closing the TCPServerSocket readable stream run the following steps [=in parallel=]:
  1. Invoke the operating system to close the socket.
  2. [=Queue a global task=] on the [=relevant global object=] of [=this=] using the [=TCPServerSocket task source=] to [=resolve=] [=this=].{{TCPServerSocket/[[closedPromise]]}} with `undefined`.

opened attribute

When called, returns the [=this=].{{TCPServerSocket/[[openedPromise]]}}.

TCPServerSocketOpenInfo dictionary

          dictionary TCPServerSocketOpenInfo {
            ReadableStream readable;

            DOMString localAddress;
            unsigned short localPort;
          };
        
readable member
The readable side of the socket. Set to {{TCPServerSocket/[[readable]]}}.
localAddress member
Local IP address that the socket is bound to.
localPort member
Local port that the socket is bound to.

closed attribute

When called, returns the [=this=].{{TCPServerSocket/[[closedPromise]]}}.

close() method

The {{TCPServerSocket/close()}} method steps are:
  1. If [=this=].{{TCPServerSocket/[[openedPromise]]}} is rejected or not yet resolved, [=reject=] with "{{InvalidStateError}}" {{DOMException}}.
  2. If [=this=].{{TCPServerSocket/[[closedPromise]]}} is settled, return [=this=].{{TCPServerSocket/[[closedPromise]]}}.
  3. If [=this=].{{TCPServerSocket/[[readable]]}} is locked, [=reject=] with "{{InvalidStateError}}" {{DOMException}}.
  4. Let |cancelPromise:Promise| be the result of invoking [=ReadableStream/cancel=] on [=this=].{{TCPServerSocket/[[readable]]}}.
  5. Set |cancelPromise|.[[\PromiseIsHandled]] to true.
  6. Return [=this=].{{TCPServerSocket/[[closedPromise]]}}.

Integrations

Permissions Policy

This specification defines a feature that controls whether {{TCPSocket}} and {{UDPSocket}} classes may be created.

The feature name for this feature is "direct-sockets"`.

The default allowlist for this feature is `'self'`.

A document’s permission policy determines whether a `new TCPSocket(...)`, `new UDPSocket(...)` or `new TCPServerSocket(...)` call rejects with a {{"NotAllowedError"}} {{DOMException}}.

Security and privacy considerations