An Extensible Messaging and Presence Protocol (XMPP) Subprotocol for WebSocket
Extracted elements (28)
Server discovery uses Web-host Metadata (RFC 6415) via HTTPS rather than DNS SRV records because web browsers and WebSocket-capable applications typically cannot perform DNS SRV lookups. This is functionally equivalent to DNS SRV from a security standpoint.
The <open/> and <close/> elements were introduced (instead of reusing <stream:stream/>) because the parsable XML document framing restriction in Section 3.3.3 requires each WebSocket message to be a complete standalone XML document; a wrapping stream element would span multiple messages and cannot be self-contained.
The WebSocket binding was designed to replace BOSH (HTTP long-polling) for browser-based XMPP clients because BOSH suffers from high transport overhead and known issues with long polling. WebSocket provides bidirectional communication effectively emulating TCP at the application layer.
XML declarations are NOT RECOMMENDED in each WebSocket frame because WebSocket messages are already mandated to be UTF-8 encoded, so declarations would only increase framing overhead without providing additional information.
If stream management resumption (XEP-0198) was negotiated, the server SHOULD consider the XMPP session still alive for a period after an abrupt WebSocket disconnection, enabling session resumption.
Stream features and errors sent over WebSocket must include explicit namespace declarations (e.g., xmlns:stream='http://etherx.jabber.org/streams') since there is no parent stream:stream element providing the namespace context as in RFC 6120.
The WebSocket binding uses <open/>/<close/> elements instead of <stream:stream/> tags, and frames each XML element as an independent parsable document rather than using streaming XML as in the TCP binding (RFC 6120). This framing difference does not affect XMPP security properties.
Clients MUST NOT accept see-other-uri suggested endpoints with a lower security context (e.g., moving from a 'wss://' endpoint to 'ws://' or 'http://') to prevent downgrade attacks.
Clients MUST NOT multiplex XMPP streams over the same WebSocket connection.
Data frame messages in the XMPP subprotocol MUST be of the text type and contain UTF-8 encoded data.
During the WebSocket handshake, the client MUST include 'xmpp' in the 'Sec-WebSocket-Protocol' header, and the server's reply MUST also contain 'xmpp' in its own 'Sec-WebSocket-Protocol' header for an XMPP subprotocol connection to be established.
Each stream frame MUST be parsable as a standalone XML document, complete with all relevant namespace and language declarations. The first character of each frame MUST be a '<' character.
Every XMPP stanza or other XML element (including stream open and close headers) sent directly over the XML stream MUST be sent in its own WebSocket message frame.
If a client receives a handshake response that does not include 'xmpp' in the 'Sec-WebSocket-Protocol' header, the client MUST close the WebSocket connection.
The first message sent after the WebSocket opening handshake MUST be from the initiating entity and MUST be an <open/> element qualified by the 'urn:ietf:params:xml:ns:xmpp-framing' namespace.
The XML stream headers (<open/> and <close/> elements) MUST be qualified by the namespace 'urn:ietf:params:xml:ns:xmpp-framing'. If violated, the receiving entity MUST close the stream with an <invalid-namespace> error.
TLS MUST be enabled at the WebSocket layer using secure WebSocket connections via the 'wss' URI scheme; it cannot be used at the XMPP subprotocol layer. A server MUST NOT advertise TLS as a stream feature, and a client MUST ignore any advertised TLS stream feature.
When a stream restart is mandated, the client MUST send a new stream <open/> element and MUST NOT send a closing <close/> element.
Whitespace keepalives MUST NOT be used in the XMPP subprotocol because each message is required to start with a '<' character.
The <close/> element, qualified by 'urn:ietf:params:xml:ns:xmpp-framing', serves as the stream closing header replacing the TCP binding's </stream:stream> tag. It optionally carries a 'see-other-uri' attribute for redirecting clients to a different endpoint.
The <open/> element, qualified by 'urn:ietf:params:xml:ns:xmpp-framing', serves as the stream opening header replacing the TCP binding's <stream:stream/> element. It carries the same attributes (from, id, to, version, xml:lang) as defined in RFC 6120.
IANA has registered a URN sub-namespace 'urn:ietf:params:xml:ns:xmpp-framing' for framing of XMPP streams over WebSocket, as defined by RFC 7395.
IANA has registered the WebSocket XMPP subprotocol in the 'WebSocket Subprotocol Name Registry' with subprotocol identifier 'xmpp' and common name 'WebSocket Transport for the Extensible Messaging and Presence Protocol (XMPP)'.
Browser-based applications cannot inspect and verify at the application layer the certificate used for the WebSocket connection. In multi-tenant situations where XMPP service domain differs from WebSocket origin, the host-meta process SHOULD be used to delegate trust, provided the host-meta exchange occurs over secure HTTP.
Since application-level TLS cannot be used, operators must serve Web-host Metadata over HTTPS only, configure WebSocket endpoints to use 'wss' only, and deploy certificates that properly identify both the XMPP service domain and WebSocket connection endpoint.
When presented with a new endpoint via 'see-other-uri', clients MUST NOT accept it if the security context is lower than the current connection, preventing downgrade attacks from wss:// to ws://.
Connection lifecycle: WebSocket handshake → stream initiation (<open/> exchange) → stanza exchange → stream close (<close/> exchange) → WebSocket closing handshake. If WebSocket closes without prior XMPP stream close, the XMPP stream is implicitly closed.
Each XMPP stream frame is a WebSocket text message containing a complete, standalone XML document (UTF-8 encoded) starting with '<'. One top-level XMPP element per WebSocket message; no shared stream:stream wrapper element.