This specification does not currently reflect the way Named Web Sockets currently work at https://github.com/namedwebsockets/networkwebsockets!

This specification defines a bootstrap mechanism for creating, binding and connecting WebSocket peers together on a local machine or on a local network that share the same service name. Named WebSockets can be created from both web applications and native applications to create communications bridges between and among themselves.

This specification introduces the LocalWebSocket and NetworkWebSocket interfaces and the algorithms required to create, bind, advertise and discover other WebSocket peers on the local machine and the local network.

By enabling different peers on local machines and local networks to discover and connect with each other we enable new sharing channels to be established. The following diagram illustrates some of the high-level sharing enabled by Named WebSockets.

Implementors need to be aware that this specification is extremely unstable. Implementors who are not taking part in the discussions will find the specification changing out from under them in incompatible ways. Vendors interested in implementing this specification before it eventually reaches the Candidate Recommendation phase should subscribe to the repository on GitHub and take part in the discussions.

Usage examples

This section shows how developers can make use of the various features of this specification.

Creating local machine broadcast channels

The following shows how a web page can create a local machine broadcast channel.

var ws = new LocalWebSocket("com.example.bootstrapservice");

The returned object implements the WebSocket interface and thus can be used as a normal WebSocket object. Sending and receiving directly on the returned object broadcasts messages to all local machine peers.

ws.addEventListener('open', function(evt) {
	ws.send("New broadcast message sent on local service 'com.example.bootstrapservice'");
}, false);

ws.addEventListener('message', function(evt) {
	console.log("Broadcast message received on local service 'com.example.bootstrapservice'");
}, false);

Whenever another peer registers on the local machine that shares the same service name (i.e. "com.example.bootstrapservice") and an entangled websocket connection has been established between these peers, then a connect event containing a WebSocket connection toward the newly discovered peer will be fired on the root object.

ws.addEventListener('connect', function(evt) {
	console.log("New peer detected on local service 'com.example.bootstrapservice'");

	var peerWebSocket = evt.target;

	/* Use peerWebSocket object for direct p2p communication with newly established WebSocket peer */
}, false);

Creating local network broadcast channels

The following shows how a web page can create a local network broadcast channel.

var ws = new NetworkWebSocket("com.example.bootstrapservice");

The returned object implements the WebSocket interface and thus can be used as a normal WebSocket object. Sending and receiving directly on the returned object broadcasts messages to all local network peers.

ws.addEventListener('open', function(evt) {
	ws.send("New broadcast message sent on network service 'com.example.bootstrapservice'");
}, false);

ws.addEventListener('message', function(evt) {
	console.log("Broadcast message received on network service 'com.example.bootstrapservice'");
}, false);

Whenever another peer registers on the local network or the local machine (created via the NetworkWebSocket interface) that shares the same service name (i.e. "com.example.bootstrapservice") and an entangled websocket connection has been established between these peers, then a connect event containing a WebSocket connection toward the newly discovered peer will be fired on the root object.

ws.addEventListener('connect', function(evt) {
	console.log("New peer detected on network service 'com.example.bootstrapservice'");

	var peerWebSocket = evt.target;

	/* Use peerWebSocket object for direct p2p communication with newly established WebSocket peer */
}, false);

Use cases and requirements

Named WebSockets are useful in a variety of collaborative local device and local network scenarios:

Conformance

Requirements phrased in the imperative as part of algorithms (such as "strip any leading space characters" or "return false and abort these steps") are to be interpreted with the meaning of the key word ("must", "should", "may", etc) used in introducing the algorithm.

Some conformance requirements are phrased as requirements on attributes, methods or objects. Such requirements are to be interpreted as requirements on user agents.

Conformance requirements phrased as algorithms or specific steps MAY be implemented in any manner, so long as the end result is equivalent. (In particular, the algorithms defined in this specification are intended to be easy to follow, and not intended to be performant.)

The only conformance class defined by this specification is a user agent.

User agents MAY impose implementation-specific limits on otherwise unconstrained inputs, e.g. to prevent denial of service attacks, to guard against running out of memory, or to work around platform-specific limitations.

When support for a feature is disabled (e.g. as an emergency measure to mitigate a security problem, or to aid in development, or for performance reasons), user agents MUST act as if they had no support for the feature whatsoever, and as if the feature was not mentioned in this specification. For example, if a particular feature is accessed via an attribute in a Web IDL interface, the attribute itself would be omitted from the objects that implement that interface - leaving the attribute on the object but making it return null or throw an exception is insufficient.

Dependencies

This specification relies on other underlying specifications:

The WebSocket API
The interfaces introduced in this specification are required to implement the WebSocket API. [[!websockets]]

Terminology

The construction "a Foo object", where Foo is actually an interface, is sometimes used instead of the more accurate "an object implementing the interface Foo".

The term DOM is used to refer to the API set made available to scripts in Web applications, and does not necessarily imply the existence of an actual Document object or of any other Node objects as defined in the DOM Core specifications. [[!DOM4]]

An IDL attribute is said to be getting when its value is being retrieved (e.g. by author script), and is said to be setting when a new value is assigned to it.

A valid channel name is a string used to federate peers that contains from 3 to 128 Unicode characters in the ranges U+002D to U+002E, U+0030 to U+0039, U+0041 to U+005A, U+005F, U+0061 to U+007A.

The local multicast address is a UDP socket bound to both the IPv4 loopback address 127.0.0.1:5352 and the IPv6 loopback address [::1]:5352.

The network multicast address is a UDP socket bound to both the IPv4 address 224.0.0.251:5353 and the IPv6 address [FF02::FB]:5353 (also known as the standard [[!MDNS]] listener endpoints).

A user agent MUST listen for new Named WebSocket DNS-SD [[!DNS-SD]] broadcasts from other user agents on both the local multicast address and the network multicast address. Additionally, a user agent MUST managing the advertising of locally-created Named WebSockets objects via [[!DNS-SD]] from a random locally-bound port toward either the local multicast address or the network multicast address according to the algorithms defined in this specification.

All implementations MUST use the SO_REUSEPORT and SO_REUSEADDR options so all user agents are able to bind to the same local multicast address and network multicast address. Additional guidelines to avoid conflicts between multiple mDNS responders running on the same machine are provided and MUST be followed by implementations from Considerations for Multiple Responders on the Same Machine. [[!MDNS]]

A local WebSocket URL is a standard WebSocket URL with a scheme of ws, a host of localhost, a port and a path component that identifies a single peer registered or discovered on the local multicast address.

A network WebSocket URL is a standard WebSocket URL with a scheme of ws, a host corresponding to a resolvable host name on the local network, a port and a path component that identifes a single peer registered or discovered on the network multicast address.

The list of active local WebSockets is a dynamic list that contains active LocalWebSocket objects created by the user agent.

The list of active network WebSockets is a dynamic list that contains active NetworkWebSocket objects created by the user agent.

Each Named WebSocket object (a LocalWebSocket object or a NetworkWebSocket object) has its own peer list that tracks WebSocket connections from itself to remote Named WebSocket peers.

The local broadcast list is a dynamic map of channel name to zero or more peer WebSocket connections. When a new Named WebSocket service is detected on the local multicast address then it will be added to this list.

The network broadcast list is a dynamic map of channel name to zero or more peer WebSocket connections. When a new Named WebSocket service is detected on the network multicast address then it will be added to this list.

The LocalWebSocket interface

[Constructor(DOMString channel)]
interface LocalWebSocket : WebSocket {
  // peer establishment
           attribute EventHandler onconnect;
}

The LocalWebSocket(channel) constructor takes one argument, channel, that specifies the local machine service name to which to connect.

When the LocalWebSocket() constructor is invoked, the UA must run these steps:

  1. If channel is not a valid channel name then throw a SyntaxError exception and abort these steps.
  2. Let peer id be a new random globally-unique identifer for the new Local Named WebSocket peer.
  3. Let url be a new local WebSocket URL, setting its path component to the string "/" followed by channel, followed by "/", followed by peer id, followed by "/", followed by a remote peer id placeholder "%s" (e.g. /myservicename/fa4c372c-f557-11e3-981d-b2227cce2b54/%s).

    User agents MAY define the path component as they wish provided the generated path is globally unique and a remote peer id placeholder ("%s") is included in the resulting path (i.e. /fa4c372c-f557-11e3-981d-b2227cce2b54/myservicename?%s or /services?id=fa4c372c-f557-11e3-981d-b2227cce2b54&peerId=%s&serviceName=myservicename are also valid path components).

  4. Let new local WebSocket be a new WebSocket object - representing a new Local WebSocket peer - passing in url as the first and only argument to the WebSocket API constructor. [[!websockets]]
  5. Return new local WebSocket and run the remaining steps asynchronously (without blocking scripts).
  6. Add new local WebSocket to the list of active local WebSockets.
  7. Let service records be a collection of the following DNS records corresponding to the new local WebSocket to form a [[!DNS-SD]] advertisement.
    1. Create a new DNS PTR record and set its value to the string channel, followed by "[", followed by peer id, followed by "]", followed by "._ws._tcp.local.". e.g.
      _ws._tcp.local.  10  IN  PTR  myservicename[fa4c372c...]._ws._tcp.local.
    2. Create a new DNS SRV record and set its port value to the newly created local WebSocket URL's port component and its host value to the newly created local WebSocket URL's host value. e.g.
      myservicename[fa4c372c...]._ws._tcp.local. 10 IN SRV 10 1 32541 localhost
    3. Create a new DNS TXT record and set its value to the string "path=" followed by the newly created local WebSocket URL's path component. e.g.
      myservicename[fa4c372c...]._ws._tcp.local. 10 IN TXT "path=/myservicename/fa4c372c.../%s"
  8. Search and connect other local-machine-based Named WebSocket peers with the same valid channel name.
  9. Advertise service records to other user agents via the local multicast address and respond with service records when an mDNS query is received at the local multicast address from another user agent for _ws._tcp.local. services according to the algorithms defined in [[!MDNS]].

The LocalWebSocket constructor must be visible when the script's global object is either a Window object or an object implementing the WorkerUtils interface.

The NetworkWebSocket interface

[Constructor(DOMString channel)]
interface NetworkWebSocket : WebSocket {
  // peer establishment
           attribute EventHandler onconnect;
}

The NetworkWebSocket(channel) constructor takes one argument, channel, that specifies the local network service name to which to connect.

When the NetworkWebSocket() constructor is invoked, the UA must run these steps:

  1. If channel is not a valid channel name then throw a SyntaxError exception and abort these steps.
  2. Let peer id be a new random globally-unique identifer for the new Network Named WebSocket peer.
  3. Let url be a new network WebSocket URL, setting its path component to the string "/" followed by channel, followed by "/", followed by peer id, followed by "/", followed by a remote peer id placeholder "%s" (e.g. /myservicename/j343h93x/%s).

    User agents MAY define the path component as they wish provided the generated path is globally unique and a remote peer id placeholder ("%s") is included in the resulting path (i.e. /j343h93x/myservicename?%s or /services?id=j343h93x&peerId=%s&serviceName=myservicename are also valid path components).

  4. Let new network WebSocket be a new WebSocket object - representing a new Network WebSocket peer - passing in url as the first and only argument to the WebSocket API constructor. [[!websockets]]
  5. Return new network WebSocket and run the remaining steps asynchronously (without blocking scripts).
  6. Add new network WebSocket to the list of active network WebSockets.
  7. Let service records be a collection of the following DNS records corresponding to the new network WebSocket to form a [[!DNS-SD]] advertisement.
    1. Create a new DNS PTR record and set its value to the string channel, followed by "[", followed by peer id, followed by "]", followed by "._ws._tcp.local.". e.g.
      _ws._tcp.local.  10  IN  PTR  myservicename[j343h93x]._ws._tcp.local.
    2. Create a new DNS SRV record and set its port value to the newly created network WebSocket URL's port component and its host value to the newly created network WebSocket URL's host value. e.g.
      myservicename[j343h93x]._ws._tcp.local. 10 IN SRV 10 1 23956 MyComputer.local.
    3. Create a new DNS TXT record and set its value to the string "path=" followed by the newly created network WebSocket URL's path component. e.g.
      myservicename[j343h93x]._ws._tcp.local. 10 IN TXT "path=/myservicename/j343h93x/%s"
    4. Create a new DNS A record and set its value to the resolved IPv4 address of the current machine's host. e.g.
      MyComputer.local.  10  IN  A  10.112.0.223
    5. Create a new DNS AAAA record and set its value to the resolved IPv6 address of the current machine's host. e.g.
      MyComputer.local.  10  IN  AAAA  fe80::7aca:39ff:feb4:42c1
  8. Search and connect other local-network-based Named WebSocket peers with the same valid channel name.
  9. Advertise service records to other user agents via the network multicast address module and respond with service records when an mDNS query is received at the network multicast address from another user agent for _ws._tcp.local. services according to the algorithms defined in [[!MDNS]].

The NetworkWebSocket constructor must be visible when the script's global object is either a Window object or an object implementing the WorkerUtils interface.

Peer management

Example call flow

@TBD

Peer discovery

A user agent MUST actively search for new Named WebSocket peers via both the local multicast address and the network multicast address.

When a Named WebSocket peer is discovered as a result of running an mDNS Query for _ws._tcp.local. services against either the local multicast address or the network multicast address then a user agent MUST run the following steps:

  1. Let service name be the the result of running the rule to obtain an advertised Named WebSocket service name against the discovered DNS records.
  2. Let remote peer id be the the result of running the rule to obtain an advertised Named WebSocket service id against the discovered DNS records.
  3. Let service URL be a WebSocket URL from the result of running the rule to obtain an advertised Named WebSocket service URL against the discovered DNS records.
  4. If the discovered DNS records were obtained via the local multicast address then let active WebSocket connections be the list of active local WebSockets. Otherwise, let active WebSocket connections be the list of active network WebSockets.
  5. For each active WebSocket in active WebSocket connections do the following:
    1. If service name does not match the service name used to create active WebSocket then abort any remaining sub-steps and continue at the next available active WebSocket.
    2. If remote peer id is already bound in the active WebSocket's peer list then abort any remaining sub-steps and continue at the next available active WebSocket.
    3. Let peering URL be the string service URL, replacing the remote peer id placeholder (i.e. "%s") in the service URL with the peer id of the active WebSocket.
    4. Let client WebSocket be a new WebSocket object with a URL of peering URL.
    5. Add client WebSocket to active WebSocket's peer list.
    6. Let broadcast WebSocket be a new WebSocket object with a URL of service URL.
    7. Add broadcast WebSocket to the appropriate broadcast list against the resolved service name.
    8. Let new peer connection event be a simple event with the event type connect, a target of client WebSocket, which does not bubble, is not cancelable, and has no default action.
    9. Fire new peer connection event at active WebSocket.

A user agent MUST listen for new Named WebSocket broadcasts from other agents on both the local multicast address and the network multicast address using standard [[!MDNS]] techniques.

The rule to obtain an advertised Named WebSocket service name is as follows:

  1. Let service name be the result of obtaining the service name component from the DNS PTR record.
  2. Return service name.

The rule to obtain an advertised Named WebSocket service id is as follows:

  1. Let service id be the result of obtaining the service id component from the DNS PTR record.
  2. Return service id.

The rule to obtain an advertised Named WebSocket service URL is as follows:

  1. Let host be the result of obtaining the host name component from the DNS SRV record.
  2. Let port be the result of obtaining the port component from the DNS SRV record.
  3. Let path be the result of parsing the DNS TXT record and obtaining the path parameter. If no DNS TXT record is provided or the path parameter is empty then let path be an empty string.
  4. Let service URL be the string "ws://", followed by host, followed by ":", followed by port, followed by path.
  5. Return service URL.

Peer binding

The user agent MUST allow remote peers to establish WebSocket connections based on the services that it currently manages.

When a new incoming WebSocket connection is established from a remote peer that has independently discovered a peer service running on the current user agent then the user agent MUST run the following steps.

  1. Let server WebSocket be a new WebSocket object representing the new incoming WebSocket connection.
  2. Let service name be the valid channel name obtained from the URL used to establish server WebSocket.
  3. Parse local peer id from the URL used to establish server WebSocket.
  4. Parse remote peer id from the URL used to establish server WebSocket.
  5. If server WebSocket was established via discovery from the local multicast address then let active WebSocket connections be the list of active local WebSockets. Otherwise, let active WebSocket connections be the list of active network WebSockets.
  6. For each active WebSocket in the active WebSocket connections do the following:
    1. If service name does not match the service name used to create active WebSocket then abort any remaining sub-steps and continue at the next available active WebSocket.
    2. If a remote peer id placeholder (i.e. "%s") is present in service URL then add the WebSocket connection object to the appropriate broadcast list against the resolved service name and abort any remaining steps.
    3. If active WebSocket's peer id does not match local peer id then abort any remaining sub-steps and continue at the next available active WebSocket.
    4. If remote peer id is already bound in active WebSocket's peer list then abort any remaining steps.
    5. Add server WebSocket to active WebSocket's peer list.
    6. Let new peer connection event be a simple event with the event type connect, a target of server WebSocket, which does not bubble, is not cancelable, and has no default action.
    7. Fire new peer connection event at active WebSocket.

Message routing

When a new Named WebSocket is created via the LocalWebSocket() or NetworkWebSocket() constructors the resulting object MUST act as a broadcast channel toward all peer WebSocket connections contained in either the local broadcast list or the network broadcast list, as appropriate, that share the same valid channel name. Thus, if send() is called on a Named WebSocket object then the user agent MUST relay the sent message toward all matching broadcast WebSocket connections in the appropriate broadcast list. All broadcast WebSocket connections other than the sender MUST receive the sent message, by emitting a standard WebSocket message event against the broadcast WebSocket object (either a LocalWebSocket or NetworkWebSocket object).