RFC 6455
The WebSocket Protocol
RFC 6455: The WebSocket Protocol ๐ฆ
Okay bestie, the title block first! ๐ This one is from the Internet Engineering Task Force (IETF), dated December 2011. The authors are I. Fette (Google, Inc.) and A. Melnikov (Isode Ltd.). It's on the Standards Track and carries ISSN 2070-1721.
Abstract
okay so the WebSocket Protocol gives you two-way communication between a client running untrusted code in a controlled environment and a remote host that has opted in to hearing from that code ๐ The security model is the origin-based one that web browsers already use. The protocol is an opening handshake followed by basic message framing, layered over TCP. The goal is a way for browser apps that need two-way chatter with servers to do it without opening a pile of HTTP connections (like with XMLHttpRequest or <iframe>s and long polling) ๐
Status of This Memo
This is an Internet Standards Track document. โจ
This document is a product of the Internet Engineering Task Force (IETF). It represents the consensus of the IETF community. It has had public review and has been approved for publication by the Internet Engineering Steering Group (IESG). More info on Internet Standards is in Section 2 of RFC 5741 ๐
Info about the current status of this document, any errata, and how to send feedback on it lives at http://www.rfc-editor.org/info/rfc6455 ๐ธ
Copyright Notice
Copyright (c) 2011 IETF Trust and the persons identified as the document authors. All rights reserved. ๐
This document is subject to BCP 78 and the IETF Trust's Legal Provisions Relating to IETF Documents (http://trustee.ietf.org/license-info) in effect on the date of publication of this document. Please review these documents carefully, because they describe your rights and restrictions with respect to this document. Code Components extracted from this document must include Simplified BSD License text as described in Section 4.e of the Trust Legal Provisions, and they are provided without warranty as described in the Simplified BSD License. ๐ซถ
Table of Contents ๐ง
1. Introduction . . . . . . . . . . . . . . . . . . . . . . . . . 4
1.1. Background . . . . . . . . . . . . . . . . . . . . . . . . 4
1.2. Protocol Overview . . . . . . . . . . . . . . . . . . . . 5
1.3. Opening Handshake . . . . . . . . . . . . . . . . . . . . 6
1.4. Closing Handshake . . . . . . . . . . . . . . . . . . . . 9
1.5. Design Philosophy . . . . . . . . . . . . . . . . . . . . 9
1.6. Security Model . . . . . . . . . . . . . . . . . . . . . . 10
1.7. Relationship to TCP and HTTP . . . . . . . . . . . . . . . 11
1.8. Establishing a Connection . . . . . . . . . . . . . . . . 11
1.9. Subprotocols Using the WebSocket Protocol . . . . . . . . 12
2. Conformance Requirements . . . . . . . . . . . . . . . . . . . 12
2.1. Terminology and Other Conventions . . . . . . . . . . . . 13
3. WebSocket URIs . . . . . . . . . . . . . . . . . . . . . . . . 14
4. Opening Handshake . . . . . . . . . . . . . . . . . . . . . . 14
4.1. Client Requirements . . . . . . . . . . . . . . . . . . . 14
4.2. Server-Side Requirements . . . . . . . . . . . . . . . . . 20
4.2.1. Reading the Client's Opening Handshake . . . . . . . . 21
4.2.2. Sending the Server's Opening Handshake . . . . . . . . 22
4.3. Collected ABNF for New Header Fields Used in Handshake . . 25
4.4. Supporting Multiple Versions of WebSocket Protocol . . . . 26
5. Data Framing . . . . . . . . . . . . . . . . . . . . . . . . . 27
5.1. Overview . . . . . . . . . . . . . . . . . . . . . . . . . 27
5.2. Base Framing Protocol . . . . . . . . . . . . . . . . . . 28
5.3. Client-to-Server Masking . . . . . . . . . . . . . . . . . 32
5.4. Fragmentation . . . . . . . . . . . . . . . . . . . . . . 33
5.5. Control Frames . . . . . . . . . . . . . . . . . . . . . . 36
5.5.1. Close . . . . . . . . . . . . . . . . . . . . . . . . 36
5.5.2. Ping . . . . . . . . . . . . . . . . . . . . . . . . . 37
5.5.3. Pong . . . . . . . . . . . . . . . . . . . . . . . . . 37
5.6. Data Frames . . . . . . . . . . . . . . . . . . . . . . . 38
5.7. Examples . . . . . . . . . . . . . . . . . . . . . . . . . 38
5.8. Extensibility . . . . . . . . . . . . . . . . . . . . . . 39
6. Sending and Receiving Data . . . . . . . . . . . . . . . . . . 39
6.1. Sending Data . . . . . . . . . . . . . . . . . . . . . . . 39
6.2. Receiving Data . . . . . . . . . . . . . . . . . . . . . . 40
7. Closing the Connection . . . . . . . . . . . . . . . . . . . . 41
7.1. Definitions . . . . . . . . . . . . . . . . . . . . . . . 41
7.1.1. Close the WebSocket Connection . . . . . . . . . . . . 41
7.1.2. Start the WebSocket Closing Handshake . . . . . . . . 42
7.1.3. The WebSocket Closing Handshake is Started . . . . . . 42
7.1.4. The WebSocket Connection is Closed . . . . . . . . . . 42
7.1.5. The WebSocket Connection Close Code . . . . . . . . . 42
7.1.6. The WebSocket Connection Close Reason . . . . . . . . 43
7.1.7. Fail the WebSocket Connection . . . . . . . . . . . . 43
7.2. Abnormal Closures . . . . . . . . . . . . . . . . . . . . 44
7.2.1. Client-Initiated Closure . . . . . . . . . . . . . . . 44
7.2.2. Server-Initiated Closure . . . . . . . . . . . . . . . 44
7.2.3. Recovering from Abnormal Closure . . . . . . . . . . . 44
7.3. Normal Closure of Connections . . . . . . . . . . . . . . 45
7.4. Status Codes . . . . . . . . . . . . . . . . . . . . . . . 45
7.4.1. Defined Status Codes . . . . . . . . . . . . . . . . . 45
7.4.2. Reserved Status Code Ranges . . . . . . . . . . . . . 47
8. Error Handling . . . . . . . . . . . . . . . . . . . . . . . . 48
8.1. Handling Errors in UTF-8-Encoded Data . . . . . . . . . . 48
9. Extensions . . . . . . . . . . . . . . . . . . . . . . . . . . 48
9.1. Negotiating Extensions . . . . . . . . . . . . . . . . . . 48
9.2. Known Extensions . . . . . . . . . . . . . . . . . . . . . 50
10. Security Considerations . . . . . . . . . . . . . . . . . . . 50
10.1. Non-Browser Clients . . . . . . . . . . . . . . . . . . . 50
10.2. Origin Considerations . . . . . . . . . . . . . . . . . . 50
10.3. Attacks On Infrastructure (Masking) . . . . . . . . . . . 51
10.4. Implementation-Specific Limits . . . . . . . . . . . . . . 52
10.5. WebSocket Client Authentication . . . . . . . . . . . . . 53
10.6. Connection Confidentiality and Integrity . . . . . . . . . 53
10.7. Handling of Invalid Data . . . . . . . . . . . . . . . . . 53
10.8. Use of SHA-1 by the WebSocket Handshake . . . . . . . . . 54
11. IANA Considerations . . . . . . . . . . . . . . . . . . . . . 54
11.1. Registration of New URI Schemes . . . . . . . . . . . . . 54
11.1.1. Registration of "ws" Scheme . . . . . . . . . . . . . 54
11.1.2. Registration of "wss" Scheme . . . . . . . . . . . . . 55
11.2. Registration of the "WebSocket" HTTP Upgrade Keyword . . . 56
11.3. Registration of New HTTP Header Fields . . . . . . . . . . 57
11.3.1. Sec-WebSocket-Key . . . . . . . . . . . . . . . . . . 57
11.3.2. Sec-WebSocket-Extensions . . . . . . . . . . . . . . . 58
11.3.3. Sec-WebSocket-Accept . . . . . . . . . . . . . . . . . 58
11.3.4. Sec-WebSocket-Protocol . . . . . . . . . . . . . . . . 59
11.3.5. Sec-WebSocket-Version . . . . . . . . . . . . . . . . 60
11.4. WebSocket Extension Name Registry . . . . . . . . . . . . 61
11.5. WebSocket Subprotocol Name Registry . . . . . . . . . . . 61
11.6. WebSocket Version Number Registry . . . . . . . . . . . . 62
11.7. WebSocket Close Code Number Registry . . . . . . . . . . . 64
11.8. WebSocket Opcode Registry . . . . . . . . . . . . . . . . 65
11.9. WebSocket Framing Header Bits Registry . . . . . . . . . . 66
12. Using the WebSocket Protocol from Other Specifications . . . . 66
13. Acknowledgements . . . . . . . . . . . . . . . . . . . . . . . 67
14. References . . . . . . . . . . . . . . . . . . . . . . . . . . 68
14.1. Normative References . . . . . . . . . . . . . . . . . . . 68
14.2. Informative References . . . . . . . . . . . . . . . . . . 69
1. Introduction โจ
1.1. Background ๐
This section is non-normative.
Historically, if you wanted a web app where the client and server could both talk whenever they wanted (think instant messaging and gaming apps), you had to basically abuse HTTP. You'd keep polling the server for updates, and send your upstream notifications as separate HTTP calls [RFC6202]. Iconic behaviour, not ๐
This gives you a bunch of problems:
- The server is forced to use a number of different underlying TCP connections for each client: one for sending information to the client, and a new one for each incoming message.
- The wire protocol has a high overhead, with each client-to-server message carrying an HTTP header.
- The client-side script is forced to maintain a mapping from the outgoing connections to the incoming connection so it can track replies.
A simpler solution would be to use a single TCP connection for traffic in both directions ๐ฆ That's what the WebSocket Protocol gives you. Paired with the WebSocket API [WSAPI], it's an alternative to HTTP polling for two-way communication from a web page to a remote server. Literally the main character move ๐
The same technique works for a whole bunch of web apps: games, stock tickers, multiuser apps with simultaneous editing, user interfaces that expose server-side services in real time, and so on. ๐
The WebSocket Protocol is designed to supersede existing bidirectional communication technologies that use HTTP as a transport layer. The point is to benefit from existing infrastructure (proxies, filtering, authentication). Those older technologies were trade-offs between efficiency and reliability, because HTTP was not originally meant for bidirectional communication (see [RFC6202] for more on that) ๐ฅน WebSocket tries to hit the goals of those HTTP-based techniques while working inside the existing HTTP infrastructure. So it's designed to work over HTTP ports 80 and 443, and to support HTTP proxies and intermediaries, even if that means some complexity specific to today's environment. โจ
However, the design doesn't limit WebSocket to HTTP. Future implementations could use a simpler handshake over a dedicated port without reinventing the entire protocol. This last point is a big deal, because the traffic patterns of interactive messaging don't closely match standard HTTP traffic, and they can induce unusual loads on some components ๐
1.2. Protocol Overview โจ
_This section is non-normative._ ๐
Okay so the protocol has two parts: a handshake and then the data transfer. ๐
The client's handshake looks like this:
GET /chat HTTP/1.1
Host: server.example.com
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==
Origin: http://example.com
Sec-WebSocket-Protocol: chat, superchat
Sec-WebSocket-Version: 13
And the server's handshake looks like this:
HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=
Sec-WebSocket-Protocol: chat
The first line from the client follows the Request-Line format, and the first line from the server follows the Status-Line format. Both Request-Line and Status-Line are defined in [RFC2616], so you know where to look. ๐ง
After that first line, both sides send an unordered set of header fields. What those header fields mean is spelled out in Section 4 of this document. There can be extra header fields too, like cookies [RFC6265]. The format and parsing of headers is as defined in [RFC2616]. ๐
Once the client and server have both sent their handshakes, and the handshake worked out, the data transfer part kicks off. This is a two-way channel where each side can send data whenever it wants, totally independently of the other. Main character energy, literally. ๐
After a successful handshake, clients and servers pass data back and forth in units this specification calls "messages". On the wire, a message is made of one or more frames. A WebSocket message doesn't have to line up with any particular network layer framing, because an intermediary is allowed to coalesce or split a fragmented message. ๐ฆ
Every frame has a type, and every frame in the same message carries the same type of data. Broadly speaking, there are types for textual data (which gets interpreted as UTF-8 [RFC3629] text), types for binary data (what it means is up to the application), and types for control frames. Control frames aren't meant to carry data for the app. They're for protocol-level signaling, like telling the other side the connection should be closed. ๐ This version of the protocol defines six frame types and leaves ten reserved for future use. Iconic restraint, honestly. โจ
1.3. Opening Handshake ๐ธ
_This section is non-normative._ ๐
The opening handshake is designed to play nicely with HTTP-based server software and intermediaries. That way a single port can serve both regular HTTP clients and WebSocket clients talking to the same server. So the WebSocket client's handshake is an HTTP Upgrade request, like this:
GET /chat HTTP/1.1
Host: server.example.com
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==
Origin: http://example.com
Sec-WebSocket-Protocol: chat, superchat
Sec-WebSocket-Version: 13
Following [RFC2616], header fields in the handshake can be sent by the client in any order, so the order you receive them in doesn't matter. No cap, that's a relief. ๐
The Request-URI of the GET method [RFC2616] is used to identify the endpoint of the WebSocket connection. This lets multiple domains share one IP address, and it lets one server host multiple WebSocket endpoints. ๐
The client puts the hostname in the Host header field of its handshake as per [RFC2616]. That way both the client and the server can check that they agree on which host is in use. ๐ซถ
Extra header fields are used to pick options in the WebSocket Protocol. The typical options in this version are the subprotocol selector (Sec-WebSocket-Protocol), the list of extensions the client supports (Sec-WebSocket-Extensions), the Origin header field, and so on. The Sec-WebSocket-Protocol request-header field says which subprotocols (application-level protocols layered over the WebSocket Protocol) the client finds acceptable. The server picks one of them, or none, and echoes that value back in its handshake to show which one it picked. ๐
Sec-WebSocket-Protocol: chat
The Origin header field [RFC6454] is there to protect against unauthorized cross-origin use of a WebSocket server by scripts running the WebSocket API in a web browser. The server gets told which script origin started the WebSocket connection request. If the server doesn't want to accept connections from that origin, it can reject the connection by sending an appropriate HTTP error code. Browsers send this header field. Non-browser clients can send it too, if it makes sense for them. ๐ฅ
Next up, the server has to prove to the client that it actually received the client's WebSocket handshake. That way it won't accept connections that aren't WebSocket connections in the first place. This stops an attacker from tricking a WebSocket server into doing something by sending it carefully crafted packets via XMLHttpRequest [XMLHttpRequest] or a form submission. Not on my watch. ๐
To prove it got the handshake, the server takes two pieces of information and combines them into a response. The first piece comes from the Sec-WebSocket-Key header field in the client handshake:
Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==
For this header field, the server takes the value (as it appears in the header field, for example the base64-encoded [RFC4648] version, minus any leading and trailing whitespace) and concatenates it with the Globally Unique Identifier (GUID, [RFC4122]) "258EAFA5-E914-47DA-95CA-C5AB0DC85B11" in string form. That GUID is unlikely to be used by network endpoints that don't understand the WebSocket Protocol. The server then takes a SHA-1 hash (160 bits) [FIPS.180-3] of that concatenation, base64-encodes it (see Section 4 of [RFC4648]), and sends the result back in its handshake. ๐ง
Let's make it concrete. In the example above, the Sec-WebSocket-Key header field had the value "dGhlIHNhbXBsZSBub25jZQ==". So the server concatenates the string "258EAFA5-E914-47DA-95CA-C5AB0DC85B11" onto it, which gives "dGhlIHNhbXBsZSBub25jZQ==258EAFA5-E914-47DA-95CA-C5AB0DC85B11". Then it takes the SHA-1 hash of that, which comes out as 0xb3 0x7a 0x4f 0x2c 0xc0 0x62 0x4f 0x16 0x90 0xf6 0x46 0x06 0xcf 0x38 0x59 0x45 0xb2 0xbe 0xc4 0xea. That value gets base64-encoded (see Section 4 of [RFC4648]) to give "s3pPLMBiTxaQ9kYGzzhZRbK+xOo=". This value is what gets echoed back in the Sec-WebSocket-Accept header field. Math with a plot twist ๐คโจ
The server's handshake is way simpler than the client's. The first line is an HTTP Status-Line with status code 101:
HTTP/1.1 101 Switching Protocols
Any status code other than 101 means the WebSocket handshake hasn't completed, and the semantics of HTTP still apply. The headers come after the status code. ๐
The Connection and Upgrade header fields complete the HTTP Upgrade. The Sec-WebSocket-Accept header field shows whether the server is willing to accept the connection. If it's present, this header field must include a hash of the client's nonce sent in Sec-WebSocket-Key, along with the predefined GUID. Any other value must not be taken as the server accepting the connection. Be picky about it, bestie. ๐ซ
HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=
For scripted pages, the WebSocket client checks these fields. If the Sec-WebSocket-Accept value doesn't match the expected value, if the header field is missing, or if the HTTP status code isn't 101, the connection won't be established, and no WebSocket frames get sent. Period. ๐ซ
There can also be option fields in the response. In this version the main one is Sec-WebSocket-Protocol, which tells the client which subprotocol the server picked. WebSocket clients verify that the server included one of the values the client specified in its handshake. A server that speaks more than one subprotocol has to pick one based on the client's handshake and then put it in its own handshake. ๐
Sec-WebSocket-Protocol: chat
The server can also set cookie-related option fields to _set_ cookies, as described in [RFC6265]. ๐ช
1.4. Closing Handshake ๐ฆ
_This section is non-normative._ ๐
The closing handshake is way simpler than the opening one. Lowkey a relief. ๐ฎโ๐จ
Either peer can send a control frame, with data containing a specified control sequence, to start the closing handshake (detailed in Section 5.5.1). When the other peer gets that frame, it sends a Close frame back in response, as long as it hasn't already sent one. When the first peer receives _that_ control frame, it closes the connection, safe in the knowledge that no more data is coming. Peak closure energy. ๐ธ
After a peer sends a control frame saying the connection should be closed, it doesn't send any more data. After a peer receives a control frame saying the connection should be closed, it throws away any further data it receives. ๐๏ธ
It's safe for both peers to start this handshake at the same time. ๐ซถ
The closing handshake is meant to complement the TCP closing handshake (FIN/ACK), because the TCP closing handshake isn't always reliable end-to-end. That's especially true when there are intercepting proxies and other intermediaries in the way. ๐ง
By sending a Close frame and then waiting for a Close frame in response, certain cases are avoided where data could be lost for no reason. For instance, on some platforms, if a socket is closed while there's data in the receive queue, a RST packet is sent. That causes recv() to fail for the party that got the RST, even if there was data waiting to be read. Bestie, that's a whole trauma arc, and the closing handshake exists so we never have to live it. ๐ญ๐
1.5. Design Philosophy โจ
_This section is non-normative._
Okay so the WebSocket Protocol is built on one big principle: minimal framing ๐ . The only framing that exists is there to make the protocol frame-based instead of stream-based ๐ and to tell Unicode text frames apart from binary frames. Anything fancier is supposed to be layered on top by the application layer, the same way metadata gets layered on top of TCP by the application layer (HTTP is the classic example) ๐ง.
Conceptually, WebSocket is really just a layer on top of TCP that does the following:
- adds a web origin-based security model for browsers ๐
- adds an addressing and protocol naming mechanism to support multiple services on one port and multiple host names on one IP address ๐
- layers a framing mechanism on top of TCP to get back to the IP packet mechanism that TCP is built on, but without length limits ๐ฆ
- includes an additional closing handshake in-band that is designed to work in the presence of proxies and other intermediaries ๐ซถ
Other than that, WebSocket adds nothing. Lowkey that's the whole point: it's basically raw TCP exposed to script, as close as the Web allows ๐ฆ. It's also designed so its servers can share a port with HTTP servers, because the handshake is a valid HTTP Upgrade request. Iconic, right? ๐ You could conceptually use other protocols for client-server messaging, but WebSockets are meant to be a relatively simple protocol that coexists with HTTP and the HTTP infrastructure already out there (like proxies). It stays as close to TCP as is safe for that infrastructure, given the security considerations, and adds a few targeted things to keep simple things simple, like message semantics โจ.
The protocol is meant to be extensible, and future versions will likely add stuff like multiplexing ๐.
1.6. Security Model
_This section is non-normative._
The WebSocket Protocol uses the same origin model that web browsers use, to control which web pages can contact a WebSocket server when the protocol is used from a web page ๐ธ๏ธ. But if a dedicated client uses the protocol directly (so not from a web page in a browser), the origin model isn't really useful, because that client can send any arbitrary origin string ๐.
This protocol is meant to fail to connect to servers of pre-existing protocols like SMTP [RFC5321] and HTTP, while still letting HTTP servers opt-in to supporting it if they want ๐ง. The way it does this is with a strict and elaborate handshake, plus by limiting how much data can be stuffed into the connection before the handshake is finished (so the server can't be influenced too much) ๐ก๏ธ.
It's also meant to fail to connect when data from other protocols, especially HTTP, gets sent to a WebSocket server. For example, that could happen if an HTML "form" got submitted to one ๐. This is mainly handled by requiring the server to prove it read the handshake, which it can only do if the handshake has the right parts, and only a WebSocket client can send those ๐. Also, at the time of writing this spec, fields starting with Sec- can't be set by an attacker from a web browser using only HTML and JavaScript APIs like XMLHttpRequest [XMLHttpRequest] ๐.
1.7. Relationship to TCP and HTTP
_This section is non-normative._
The WebSocket Protocol is its own independent TCP-based protocol ๐ง. Its only relationship to HTTP is that HTTP servers interpret its handshake as an Upgrade request.
By default, WebSocket uses port 80 for regular connections and port 443 for connections tunneled over Transport Layer Security (TLS) [RFC2818] ๐.
1.8. Establishing a Connection
_This section is non-normative._
When a connection is made to a port shared by an HTTP server (which is super likely on ports 80 and 443 ๐ญ), the connection will look to the HTTP server like a regular GET request with an Upgrade offer. In simple setups with one IP address and one server handling all traffic for a single hostname, this might be a practical way to deploy WebSocket-based systems ๐. In more elaborate setups (think load balancers and multiple servers), it's probably easier to manage a dedicated set of hosts just for WebSocket connections, separate from the HTTP servers ๐ ๏ธ.
At the time of writing this spec, it should be noted that connections on ports 80 and 443 have very different success rates. Connections on port 443 are significantly more likely to succeed, though that may change with time ๐. Not me noticing this as the main character of the internet ๐ .
1.9. Subprotocols Using the WebSocket Protocol
_This section is non-normative._
The client can ask the server to use a specific subprotocol by including the Sec-WebSocket-Protocol field in its handshake ๐. If one is specified, the server needs to include the same field, plus one of the selected subprotocol values, in its response for the connection to be established ๐ค.
These subprotocol names should be registered as per Section 11.5. To avoid collisions, it is recommended to use names that contain the ASCII version of the domain name of the subprotocol's originator ๐ชช. For example, if Example Corporation made a Chat subprotocol for lots of servers around the Web, they could name it "chat.example.com". If the Example Organization made a competing one called "chat.example.org", then both could be implemented by servers at the same time, and the server would pick which one to use based on what the client sent ๐ฏโโ๏ธ. Ate and left no crumbs ๐
.
Subprotocols can be versioned in backward-incompatible ways by changing the subprotocol name, e.g., going from "bookings.example.net" to "v2.bookings.example.net". WebSocket clients would treat those as completely separate subprotocols ๐ฆ. Backward-compatible versioning is possible by reusing the same subprotocol string and carefully designing the subprotocol so it can stretch and extend ๐ธ.
2. Conformance Requirements โจ
All diagrams, examples, and notes in this specification are non-normative, as are all sections explicitly marked non-normative. Everything else in this specification is normative ๐.
The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in [RFC2119]. These are the official capital-letter power words, so read them carefully ๐ฅ.
Requirements written in the imperative as part of algorithms (like "strip any leading space characters" or "return false and abort these steps") are interpreted with the force of the key word ("MUST", "SHOULD", "MAY", etc.) used when introducing the algorithm ๐งฎ.
Conformance requirements phrased as algorithms or specific steps MAY be implemented in any manner, as long as the end result is equivalent. (The algorithms here are meant to be easy to follow, not fast ๐ข.)
2.1. Terminology and Other Conventions
_ASCII_ shall mean the character-encoding scheme defined in [ANSI.X3-4.1986]. (Lowercase "shall," not the loud capital SHALL, so it's just a plain old word here ๐ค.)
This document references UTF-8 values and uses UTF-8 notational formats as defined in STD 63 [RFC3629] ๐ค.
Key terms like named algorithms or definitions are marked like _this_ โจ.
Names of header fields or variables are marked like |this| ๐ท๏ธ.
Variable values are marked like /this/ ๐ฏ.
This document references the procedure to _Fail the WebSocket Connection_. That procedure is defined in Section 7.1.7 ๐ฅ.
_Converting a string to ASCII lowercase_ means replacing every character in the range U+0041 to U+005A (so LATIN CAPITAL LETTER A to LATIN CAPITAL LETTER Z) with the matching character in the range U+0061 to U+007A (LATIN SMALL LETTER A to LATIN SMALL LETTER Z) ๐ก.
Comparing two strings in an _ASCII case-insensitive_ way means comparing them exactly, code point for code point, except that the characters in the range U+0041 to U+005A (LATIN CAPITAL LETTER A to LATIN CAPITAL LETTER Z) and the matching characters in the range U+0061 to U+007A (LATIN SMALL LETTER A to LATIN SMALL LETTER Z) are also considered to match ๐ซ .
The term "URI" is used in this document as defined in [RFC3986] ๐.
When an implementation is required to _send_ data as part of the WebSocket Protocol, the implementation MAY delay the actual transmission for as long as it likes. For example, it can buffer data so it sends fewer IP packets ๐ฆโจ.
Note that this document uses both [RFC5234] and [RFC2616] variants of ABNF in different sections ๐ง .
3. WebSocket URIs ๐ฆ
This specification defines two URI schemes, using the ABNF syntax defined in RFC 5234 [RFC5234], along with terminology and ABNF productions defined by the URI specification, RFC 3986 [RFC3986] ๐.
ws-URI = "ws:" "//" host [ ":" port ] path [ "?" query ]
wss-URI = "wss:" "//" host [ ":" port ] path [ "?" query ]
host = <host, defined in [RFC3986], Section 3.2.2>
port = <port, defined in [RFC3986], Section 3.2.3>
path = <path-abempty, defined in [RFC3986], Section 3.3>
query = <query, defined in [RFC3986], Section 3.4>
The port component is OPTIONAL. The default for "ws" is port 80, and the default for "wss" is port 443 ๐.
The URI is called "secure" (and it's said that "the secure flag is set") if the scheme component matches "wss" case-insensitively ๐.
The "resource-name" (also known as /resource name/ in Section 4.1) can be built by concatenating the following:
"/"if the path component is empty- the path component
"?"if the query component is non-empty- the query component
Fragment identifiers are meaningless in the context of WebSocket URIs and MUST NOT be used on these URIs. As with any URI scheme, the character "#", when it's not marking the start of a fragment, MUST be escaped as %23. Bestie, don't be the person who forgets the escape ๐๐.
4. Opening Handshake ๐ค
4.1. Client Requirements
To _Establish a WebSocket Connection_, a client opens a connection and sends a handshake as defined in this section. A connection is defined to start in a CONNECTING state ๐ช. A client needs to supply a /host/, /port/, /resource name/, and a /secure/ flag. Those are the components of a WebSocket URI from Section 3, along with a list of /protocols/ and /extensions/ to be used ๐งฉ. Also, if the client is a web browser, it supplies /origin/ ๐.
Clients running in controlled environments, like browsers on mobile handsets tied to specific carriers, MAY offload the management of the connection to another agent on the network. In that case, the client, for the purposes of this specification, is considered to include both the handset software and any such agents ๐ฑโจ.
Okay so bestie, this is where the client actually does the thing ๐ When it's told to Establish a WebSocket Connection with a host, port, resource name, and secure flag, plus a list of protocols and extensions and an origin if it's a web browser, it MUST open a connection, send an opening handshake, and read the server's handshake back ๐ฆ The exact rules for opening the connection, what goes in the opening handshake, and how the server's reply gets read are below โจ In the following text we'll use terms from Section 3, like "host" and "secure flag", which are defined there ๐
- The components of the WebSocket URI passed into this algorithm (host, port, resource name, and secure flag) MUST be valid according to the specification of WebSocket URIs in Section 3. If any of the components are invalid, the client MUST Fail the WebSocket Connection and abort these steps. ๐
- If the client already has a WebSocket connection to the remote host (IP address) identified by host and port port pair, even if the remote host is known by another name, the client MUST wait until that connection has been established or for that connection to have failed. There MUST be no more than one connection in a CONNECTING state. If multiple connections to the same IP address are attempted simultaneously, the client MUST serialize them so that there is no more than one connection at a time running through the following steps. ๐ซถ
If the client cannot determine the IP address of the remote host (for example, because all communication is being done through a proxy server that performs DNS queries itself), then the client MUST assume for the purposes of this step that each host name refers to a distinct remote host, and instead the client SHOULD limit the total number of simultaneous pending connections to a reasonably low number (e.g., the client might allow simultaneous pending connections to a.example.com and b.example.com, but if thirty simultaneous connections to a single host are requested, that may not be allowed). For example, in a web browser context, the client needs to consider the number of tabs the user has open in setting a limit to the number of simultaneous pending connections. ๐ธ
NOTE: This makes it harder for a script to perform a denial-of-service attack by just opening a large number of WebSocket connections to a remote host. A server can further reduce the load on itself when attacked by pausing before closing the connection, as that will reduce the rate at which the client reconnects. ๐
NOTE: There is no limit to the number of established WebSocket connections a client can have with a single remote host. Servers can refuse to accept connections from hosts/IP addresses with an excessive number of existing connections or disconnect resource-hogging connections when suffering high load. ๐
- Proxy Usage: If the client is configured to use a proxy when using the WebSocket Protocol to connect to host host and port port, then the client SHOULD connect to that proxy and ask it to open a TCP connection to the host given by host and the port given by port. ๐ง
EXAMPLE: For example, if the client uses an HTTP proxy for all traffic, then if it was to try to connect to port 80 on server example.com, it might send the following lines to the proxy server:
CONNECT example.com:80 HTTP/1.1
Host: example.com
If there was a password, the connection might look like:
CONNECT example.com:80 HTTP/1.1
Host: example.com
Proxy-authorization: Basic ZWRuYW1vZGU6bm9jYXBlcyE=
If the client is not configured to use a proxy, then a direct TCP connection SHOULD be opened to the host given by host and the port given by port. ๐
NOTE: Implementations that do not expose explicit UI for selecting a proxy for WebSocket connections separate from other proxies are encouraged to use a SOCKS5 [RFC1928] proxy for WebSocket connections, if available, or failing that, to prefer the proxy configured for HTTPS connections over the proxy configured for HTTP connections. ๐
For the purpose of proxy autoconfiguration scripts, the URI to pass the function MUST be constructed from host, port, resource name, and the secure flag using the definition of a WebSocket URI as given in Section 3. โจ
NOTE: The WebSocket Protocol can be identified in proxy autoconfiguration scripts from the scheme ("ws" for unencrypted connections and "wss" for encrypted connections). ๐
- If the connection could not be opened, either because a direct connection failed or because any proxy used returned an error, then the client MUST Fail the WebSocket Connection and abort the connection attempt. ๐ญ
- If secure is true, the client MUST perform a TLS handshake over the connection after opening the connection and before sending the handshake data [RFC2818]. If this fails (e.g., the server's certificate could not be verified), then the client MUST Fail the WebSocket Connection and abort the connection. Otherwise, all further communication on this channel MUST run through the encrypted tunnel [RFC5246]. ๐
Clients MUST use the Server Name Indication extension in the TLS handshake [RFC6066]. ๐
Once a connection to the server has been established (including a connection via a proxy or over a TLS-encrypted tunnel), the client MUST send an opening handshake to the server. The handshake consists of an HTTP Upgrade request, along with a list of required and optional header fields. The requirements for this handshake are as follows. ๐ฆ
- The handshake MUST be a valid HTTP request as specified by [RFC2616]. ๐
- The method of the request MUST be GET, and the HTTP version MUST be at least 1.1.
For example, if the WebSocket URI is "ws://example.com/chat", the first line sent should be GET /chat HTTP/1.1. ๐ธ
- The "Request-URI" part of the request MUST match the resource name defined in Section 3 (a relative URI) or be an absolute http/https URI that, when parsed, has a resource name, host, and port that match the corresponding ws/wss URI. ๐ง
- The request MUST contain a
Hostheader field whose value contains host plus optionally ":" followed by port (when not using the default port). ๐ - The request MUST contain an
Upgradeheader field whose value MUST include the "websocket" keyword. โญ - The request MUST contain a
Connectionheader field whose value MUST include the "Upgrade" token. ๐ - The request MUST include a header field with the name
Sec-WebSocket-Key. The value of this header field MUST be a nonce consisting of a randomly selected 16-byte value that has been base64-encoded (see Section 4 of [RFC4648]). The nonce MUST be selected randomly for each connection. ๐ฅ
NOTE: As an example, if the randomly selected value was the sequence of bytes 0x01 0x02 0x03 0x04 0x05 0x06 0x07 0x08 0x09 0x0a 0x0b 0x0c 0x0d 0x0e 0x0f 0x10, the value of the header field would be "AQIDBAUGBwgJCgsMDQ4PEC==" ๐ง
- The request MUST include a header field with the name
Origin[RFC6454] if the request is coming from a browser client. If the connection is from a non-browser client, the request MAY include this header field if the semantics of that client match the use-case described here for browser clients. The value of this header field is the ASCII serialization of origin of the context in which the code establishing the connection is running. See [RFC6454] for the details of how this header field value is constructed. ๐
As an example, if code downloaded from www.example.com attempts to establish a connection to ww2.example.com, the value of the header field would be "http://www.example.com". โจ
- The request MUST include a header field with the name
Sec-WebSocket-Version. The value of this header field MUST be 13. ๐
NOTE: Although draft versions of this document (-09, -10, -11, and -12) were posted (they were mostly comprised of editorial changes and clarifications and not changes to the wire protocol), values 9, 10, 11, and 12 were not used as valid values for Sec-WebSocket-Version. These values were reserved in the IANA registry but were not and will not be used. ๐
- The request MAY include a header field with the name
Sec-WebSocket-Protocol. If present, this value indicates one or more comma-separated subprotocol the client wishes to speak, ordered by preference. The elements that comprise this value MUST be non-empty strings with characters in the range U+0021 to U+007E not including separator characters as defined in [RFC2616] and MUST all be unique strings. The ABNF for the value of this header field is1#token, where the definitions of constructs and rules are as given in [RFC2616]. ๐ - The request MAY include a header field with the name
Sec-WebSocket-Extensions. If present, this value indicates the protocol-level extension(s) the client wishes to speak. The interpretation and format of this header field is described in Section 9.1. ๐งฉ - The request MAY include any other header fields, for example, cookies [RFC6265] and/or authentication-related header fields such as the
Authorizationheader field [RFC2616], which are processed according to documents that define them. ๐ช
Once the client's opening handshake has been sent, the client MUST wait for a response from the server before sending any further data. The client MUST validate the server's response as follows: ๐
- If the status code received from the server is not 101, the client handles the response per HTTP [RFC2616] procedures. In particular, the client might perform authentication if it receives a 401 status code; the server might redirect the client using a 3xx status code (but clients are not required to follow them), etc. Otherwise, proceed as follows. ๐
- If the response lacks an
Upgradeheader field or theUpgradeheader field contains a value that is not an ASCII case-insensitive match for the value "websocket", the client MUST Fail the WebSocket Connection. ๐ซ - If the response lacks a
Connectionheader field or theConnectionheader field doesn't contain a token that is an ASCII case-insensitive match for the value "Upgrade", the client MUST Fail the WebSocket Connection. ๐ซ - If the response lacks a
Sec-WebSocket-Acceptheader field or theSec-WebSocket-Acceptcontains a value other than the base64-encoded SHA-1 of the concatenation of theSec-WebSocket-Key(as a string, not base64-decoded) with the string "258EAFA5-E914-47DA-95CA-C5AB0DC85B11" but ignoring any leading and trailing whitespace, the client MUST Fail the WebSocket Connection. ๐ซ
That's the whole client side of the handshake, bestie! ๐๐
- Okay bestie, if the server's response has a
Sec-WebSocket-Extensionsheader that says it's using an extension the client never asked for (the server crashed the party uninvited ๐), the client MUST Fail the WebSocket Connection. (How to parse that header to figure out which extensions were requested is covered in Section 9.1 ๐ฆ.) - Same energy for
Sec-WebSocket-Protocol. If the response includes that header and it says the server wants a subprotocol the client didn't request, the client MUST Fail the WebSocket Connection ๐ซ.
If the server's response doesn't meet the server's handshake requirements from this section and from Section 4.2.2, the client MUST Fail the WebSocket Connection ๐ . No exceptions, no drama ๐ โโ๏ธ.
Also, according to [RFC2616], all header field names in both HTTP requests and HTTP responses are case-insensitive. So Upgrade, upgrade, and UPGRADE are all the same field to us ๐.
If the server's response passes all the checks above, it is said that The WebSocket Connection is Established โจ๐, and the WebSocket Connection is in the OPEN state. Two things get locked in at this point:
- Extensions In Use is a string that can be empty. Its value is whatever the server sent in
Sec-WebSocket-Extensions, ornullif that header wasn't in the server's handshake ๐งฉ. - Subprotocol In Use is the value of
Sec-WebSocket-Protocolfrom the server's handshake, ornullif that header wasn't there ๐.
And if any header fields in the server's handshake say that cookies should be set (as defined by [RFC6265]), those cookies are called Cookies Set During the Server's Opening Handshake ๐ช. Iconic naming, honestly ๐ญ.
4.2. Server-Side Requirements ๐ง
Servers MAY offload management of the connection to other agents on the network, like load balancers and reverse proxies ๐ธ. When that happens, the "server" for this spec means every piece of server-side infrastructure, from the first device that terminates the TCP connection all the way to the server that processes requests and sends responses. Main character behavior, all of it ๐.
EXAMPLE: A data center might have one machine that answers WebSocket requests with the proper handshake, then hands the connection off to a different machine that actually handles the data frames. For this specification, the "server" is both computers together ๐.
4.2.1. Reading the Client's Opening Handshake ๐
When a client starts a WebSocket connection, it sends its part of the opening handshake ๐ค. The server must parse at least part of that handshake so it knows what it needs to build the server's part.
Here's what the client's opening handshake has to include. If the server, while reading it, finds that the client didn't send a handshake matching this description (remember, per [RFC2616], the order of header fields doesn't matter ๐), including but not limited to any violations of the ABNF grammar specified for the handshake components, the server MUST stop processing the client's handshake and return an HTTP response with an appropriate error code, such as 400 Bad Request ๐จ. Rude, but necessary ๐.
- An HTTP/1.1 or higher GET request, including a "Request-URI" [RFC2616] that should be interpreted as a resource name defined in Section 3 (or an absolute HTTP/HTTPS URI containing the resource name) ๐.
- A
Hostheader field containing the server's authority ๐ . - An
Upgradeheader field containing the value "websocket", treated as an ASCII case-insensitive value ๐ฆ. - A
Connectionheader field that includes the token "Upgrade", treated as an ASCII case-insensitive value ๐ซถ. - A
Sec-WebSocket-Keyheader field with a base64-encoded value (see Section 4 of [RFC4648]) that, once decoded, is 16 bytes long ๐. - A
Sec-WebSocket-Versionheader field with a value of 13 ๐ข. - Optionally, an
Originheader field. Every browser client sends this one โจ. A connection attempt that doesn't have this header SHOULD NOT be interpreted as coming from a browser client ๐. - Optionally, a
Sec-WebSocket-Protocolheader field with a list of values showing which protocols the client would like to speak, ordered by preference ๐ฌ. - Optionally, a
Sec-WebSocket-Extensionsheader field with a list of values showing which extensions the client would like to speak. How this header gets interpreted is covered in Section 9.1 ๐งฉ. - Optionally, other header fields, like the ones used to send cookies or request authentication from a server ๐. Unknown header fields are ignored, as per [RFC2616] ๐.
4.2.2. Sending the Server's Opening Handshake โจ
Okay bestie, this is the part where the server finally says "yes, I'm in" ๐. When a client establishes a WebSocket connection to a server, the server MUST complete the following steps to accept the connection and send the server's opening handshake.
- If the connection is happening on an HTTPS (HTTP-over-TLS) port, perform a TLS handshake over the connection. If this fails (e.g., the client indicated a host name in the extended client hello "server_name" extension that the server does not host), then close the connection; otherwise, all further communication for the connection (including the server's handshake) MUST run through the encrypted tunnel [RFC5246]. ๐
- The server can perform additional client authentication, for example, by returning a 401 status code with the corresponding
WWW-Authenticateheader field as described in [RFC2616]. ๐ - The server MAY redirect the client using a 3xx status code [RFC2616]. Note that this step can happen together with, before, or after the optional authentication step described above. ๐ฆ
- Establish the following information: ๐ง
- /origin/ The
Originheader field in the client's handshake indicates the origin of the script establishing the connection. The origin is serialized to ASCII and converted to lowercase. The server MAY use this information as part of a determination of whether to accept the incoming connection. If the server does not validate the origin, it will accept connections from anywhere. If the server does not wish to accept this connection, it MUST return an appropriate HTTP error code (e.g., 403 Forbidden) and abort the WebSocket handshake described in this section. For more detail, refer to Section 10. ๐ - /key/ The
Sec-WebSocket-Keyheader field in the client's handshake includes a base64-encoded value that, if decoded, is 16 bytes in length. This (encoded) value is used in the creation of the server's handshake to indicate an acceptance of the connection. It is not necessary for the server to base64-decode theSec-WebSocket-Keyvalue. ๐ - /version/ The
Sec-WebSocket-Versionheader field in the client's handshake includes the version of the WebSocket Protocol with which the client is attempting to communicate. If this version does not match a version understood by the server, the server MUST abort the WebSocket handshake described in this section and instead send an appropriate HTTP error code (such as 426 Upgrade Required) and aSec-WebSocket-Versionheader field indicating the version(s) the server is capable of understanding. ๐ - /resource name/ An identifier for the service provided by the server. If the server provides multiple services, then the value should be derived from the resource name given in the client's handshake in the "Request-URI" [RFC2616] of the GET method. If the requested service is not available, the server MUST send an appropriate HTTP error code (such as 404 Not Found) and abort the WebSocket handshake. ๐
- /subprotocol/ Either a single value representing the subprotocol the server is ready to use or null. The value chosen MUST be derived from the client's handshake, specifically by selecting one of the values from the
Sec-WebSocket-Protocolfield that the server is willing to use for this connection (if any). If the client's handshake did not contain such a header field or if the server does not agree to any of the client's requested subprotocols, the only acceptable value is null. The absence of such a field is equivalent to the null value (meaning that if the server does not wish to agree to one of the suggested subprotocols, it MUST NOT send back aSec-WebSocket-Protocolheader field in its response). The empty string is not the same as the null value for these purposes and is not a legal value for this field. The ABNF for the value of this header field is (token), where the definitions of constructs and rules are as given in [RFC2616]. ๐ - /extensions/ A (possibly empty) list representing the protocol-level extensions the server is ready to use. If the server supports multiple extensions, then the value MUST be derived from the client's handshake, specifically by selecting one or more of the values from the
Sec-WebSocket-Extensionsfield. The absence of such a field is equivalent to the null value. The empty string is not the same as the null value for these purposes. Extensions not listed by the client MUST NOT be listed. The method by which these values should be selected and interpreted is discussed in Section 9.1. โจ
- /origin/ The
- If the server chooses to accept the incoming connection, it MUST reply with a valid HTTP response indicating the following. ๐
- A Status-Line with a 101 response code as per RFC 2616 [RFC2616]. Such a response could look like "HTTP/1.1 101 Switching Protocols".
- An
Upgradeheader field with value "websocket" as per RFC 2616 [RFC2616]. - A
Connectionheader field with value "Upgrade". - A
Sec-WebSocket-Acceptheader field. The value of this header field is constructed by concatenating /key/, defined above in step 4 in Section 4.2.2, with the string "258EAFA5-E914-47DA-95CA-C5AB0DC85B11", taking the SHA-1 hash of this concatenated value to obtain a 20-byte value and base64-encoding (see Section 4 of [RFC4648]) this 20-byte hash. The ABNF [RFC2616] of this header field is defined as follows:
Sec-WebSocket-Accept = base64-value-non-empty
base64-value-non-empty = (1*base64-data [ base64-padding ]) |
base64-padding
base64-data = 4base64-character
base64-padding = (2base64-character "==") |
(3base64-character "=")
base64-character = ALPHA | DIGIT | "+" | "/"
- Optionally, a
Sec-WebSocket-Protocolheader field, with a value /subprotocol/ as defined in step 4 in Section 4.2.2. - Optionally, a
Sec-WebSocket-Extensionsheader field, with a value /extensions/ as defined in step 4 in Section 4.2.2. If multiple extensions are to be used, they can all be listed in a singleSec-WebSocket-Extensionsheader field or split between multiple instances of theSec-WebSocket-Extensionsheader field.
NOTE: As an example, if the value of theSec-WebSocket-Keyheader field in the client's handshake were "dGhlIHNhbXBsZSBub25jZQ==", the server would append the string "258EAFA5-E914-47DA-95CA-C5AB0DC85B11" to form the string "dGhlIHNhbXBsZSBub25jZQ==258EAFA5-E914-47DA-95CA-C5AB0DC85B11". The server would then take the SHA-1 hash of this string, giving the value0xb3 0x7a 0x4f 0x2c 0xc0 0x62 0x4f 0x16 0x90 0xf6 0x46 0x06 0xcf 0x38 0x59 0x45 0xb2 0xbe 0xc4 0xea. This value is then base64-encoded, to give the value "s3pPLMBiTxaQ9kYGzzhZRbK+xOo=", which would be returned in theSec-WebSocket-Acceptheader field. ๐ง
This completes the server's handshake. If the server finishes these steps without aborting the WebSocket handshake, the server considers the WebSocket connection to be established and that the WebSocket connection is in the OPEN state. At this point, the server may begin sending (and receiving) data. Bestie, the handshake is DONE and the connection is live! ๐
4.3. Collected ABNF for New Header Fields Used in Handshake
This section is using ABNF syntax/rules from Section 2.1 of [RFC2616], including the "implied *LWS rule". ๐
Note that the following ABNF conventions are used in this section. Some names of the rules correspond to names of the corresponding header fields. Such rules express values of the corresponding header fields, for example, the Sec-WebSocket-Key ABNF rule describes syntax of the Sec-WebSocket-Key header field value. ABNF rules with the "-Client" suffix in the name are only used in requests sent by the client to the server; ABNF rules with the "-Server" suffix in the name are only used in responses sent by the server to the client. For example, the ABNF rule Sec-WebSocket-Protocol-Client describes syntax of the Sec-WebSocket-Protocol header field value sent by the client to the server. ๐ง
The following new header fields can be sent during the handshake from the client to the server:
Sec-WebSocket-Key = base64-value-non-empty
Sec-WebSocket-Extensions = extension-list
Sec-WebSocket-Protocol-Client = 1#token
Sec-WebSocket-Version-Client = version
base64-value-non-empty = (1*base64-data [ base64-padding ]) |
base64-padding
base64-data = 4base64-character
base64-padding = (2base64-character "==") |
(3base64-character "=")
base64-character = ALPHA | DIGIT | "+" | "/"
extension-list = 1#extension
extension = extension-token *( ";" extension-param )
extension-token = registered-token
registered-token = token
extension-param = token [ "=" (token | quoted-string) ]
; When using the quoted-string syntax variant, the value
; after quoted-string unescaping MUST conform to the
; 'token' ABNF.
NZDIGIT = "1" | "2" | "3" | "4" | "5" | "6" |
"7" | "8" | "9"
version = DIGIT | (NZDIGIT DIGIT) |
("1" DIGIT DIGIT) | ("2" DIGIT DIGIT)
; Limited to 0-255 range, with no leading zeros
The following new header fields can be sent during the handshake from the server to the client:
Sec-WebSocket-Extensions = extension-list
Sec-WebSocket-Accept = base64-value-non-empty
Sec-WebSocket-Protocol-Server = token
Sec-WebSocket-Version-Server = 1#version
4.4. Supporting Multiple Versions of WebSocket Protocol ๐
okay so this section is basically the guide for how clients and servers can handle more than one version of the WebSocket Protocol, and it's giving very "be flexible, bestie" ๐
so the version advertisement thing (that's the Sec-WebSocket-Version header field) lets a client ask for whatever version it prefers first ๐ฆ and it doesn't have to be the newest one it knows. if the server supports that version and the handshake is otherwise valid, the server accepts it, easy. but if the server doesn't support the requested version, it MUST respond with a Sec-WebSocket-Version header field (or multiple Sec-WebSocket-Version header fields) listing every version it's willing to use. then if the client supports one of those, it can just redo the handshake with a new version value. iconic negotiation behaviour honestly ๐
here's an example of that negotiation in action:
GET /chat HTTP/1.1
Host: server.example.com
Upgrade: websocket
Connection: Upgrade
...
Sec-WebSocket-Version: 25
and the server might reply like this:
HTTP/1.1 400 Bad Request
...
Sec-WebSocket-Version: 13, 8, 7
the last reply could also look like this, which is the same info just split across two header fields ๐ธ
HTTP/1.1 400 Bad Request
...
Sec-WebSocket-Version: 13
Sec-WebSocket-Version: 8, 7
and then the client repeats the handshake for version 13:
GET /chat HTTP/1.1
Host: server.example.com
Upgrade: websocket
Connection: Upgrade
...
Sec-WebSocket-Version: 13
5. Data Framing โจ
5.1. Overview ๐
okay bestie, this is where the actual data starts moving ๐ in the WebSocket Protocol, everything gets sent as a sequence of frames. and here's the lowkey important bit: to avoid confusing network intermediaries (like intercepting proxies) and for security reasons that get more detail in Section 10.3, a client MUST mask all frames that it sends to the server (see Section 5.3 for the deets). (fun fact, masking happens whether or not the WebSocket Protocol is running over TLS ๐)
the rules are strict on purpose: a server MUST close the connection if it gets a frame that isn't masked. in that case the server MAY send a Close frame with status code 1002 (protocol error), which is defined in Section 7.4.1. and a server MUST NOT mask any frames it sends to the client. a client MUST close the connection if it detects a masked frame, and in that case it MAY use status code 1002 (protocol error) as defined in Section 7.4.1. (these rules might get relaxed in a future specification, so keep an eye out ๐)
the base framing protocol defines a frame type with an opcode, a payload length, and designated spots for "Extension data" and "Application data". together those make up the "Payload data" ๐ง and some bits and opcodes are reserved for future expansion of the protocol, so they're just chilling for later โจ
finally, a data frame MAY be sent by either the client or the server at any point after the opening handshake is complete, and before that endpoint has sent a Close frame (Section 5.5.1). periodt ๐
5.2. Base Framing Protocol ๐
okay so this section is the wire format for the data transfer part, and it's literally the blueprint for every single frame that goes across a WebSocket ๐ The ABNF [RFC5234] below spells it all out. Heads up: unlike the rest of the doc, this ABNF works on groups of bits, not characters. Each group's length is in a comment, and when it's on the wire the most significant bit is the leftmost one in the ABNF ๐ง
Here's the big-picture diagram. If the figure and the ABNF ever disagree, the figure wins, so trust the picture first โจ
0 1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-------+-+-------------+-------------------------------+
|F|R|R|R| opcode|M| Payload len | Extended payload length |
|I|S|S|S| (4) |A| (7) | (16/64) |
|N|V|V|V| |S| | (if payload len==126/127) |
| |1|2|3| |K| | |
+-+-+-+-+-------+-+-------------+ - - - - - - - - - - - - - - - +
| Extended payload length continued, if payload len == 127 |
+ - - - - - - - - - - - - - - - +-------------------------------+
| |Masking-key, if MASK set to 1 |
+-------------------------------+-------------------------------+
| Masking-key (continued) | Payload Data |
+-------------------------------- - - - - - - - - - - - - - - - +
: Payload Data continued ... :
+ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +
| Payload Data continued ... |
+---------------------------------------------------------------+
Now let's go through each field ๐
FIN: 1 bit
this one tells you if this is the final fragment in a message ๐ฆ The first fragment MAY also be the final fragment, so a message can totally be just one frame, no cap.
RSV1, RSV2, RSV3: 1 bit each
these are reserved bits and they MUST be 0 unless an extension is negotiated that gives non-zero values a meaning ๐ If you get a nonzero value and none of the negotiated extensions explains it, the receiving endpoint MUST Fail the WebSocket Connection. no vibes, just shutting it down ๐
Opcode: 4 bits
the opcode tells you how to read the "Payload data" ๐ If you receive an opcode nobody knows, the receiving endpoint MUST Fail the WebSocket Connection. Here's what the values mean:
%x0denotes a continuation frame%x1denotes a text frame%x2denotes a binary frame%x3-7are reserved for further non-control frames%x8denotes a connection close%x9denotes a ping%xAdenotes a pong%xB-Fare reserved for further control frames
Mask: 1 bit
this is the bit that says whether the "Payload data" is masked ๐ If it's set to 1, there's a masking key sitting in masking-key, and you use it to unmask the "Payload data" as per Section 5.3. Every frame going from client to server has this bit set to 1, so that part's basically iconic and consistent ๐
Payload length: 7 bits, 7+16 bits, or 7+64 bits
this is the length of the "Payload data" in bytes, and it's a little choose-your-own-adventure ๐ง If it's 0-125, that number is the payload length. If it's 126, the next 2 bytes, read as a 16-bit unsigned integer, are the length. If it's 127, the next 8 bytes, read as a 64-bit unsigned integer, are the length (and the most significant bit MUST be 0). Multibyte lengths go in network byte order ๐ธ Also, in every case you MUST use the minimal number of bytes to encode the length. So a 124-byte string can't be sent as 126, 0, 124, because that's just extra and not it ๐ โโ๏ธ The payload length is the length of the "Extension data" plus the length of the "Application data". The "Extension data" can be zero length, and then the payload length is just the "Application data" length ๐
Masking-key: 0 or 4 bytes
every frame the client sends to the server gets masked with a 32-bit value sitting right inside the frame โจ This field is there if the mask bit is 1 and gone if the mask bit is 0. Check out Section 5.3 for the deets on client-to-server masking ๐ซถ
Payload data: (x+y) bytes
the "Payload data" is just "Extension data" glued together with "Application data" ๐ง
Extension data: x bytes
this is 0 bytes unless an extension was negotiated. Any extension MUST specify the length of the "Extension data", or how that length gets calculated, and how the extension use MUST be negotiated during the opening handshake ๐ฅ When it's there, the "Extension data" counts toward the total payload length ๐
Application data: y bytes
this is the arbitrary stuff you actually care about, and it takes up whatever's left of the frame after the "Extension data". Its length is the payload length minus the length of the "Extension data" ๐
okay now the formal part ๐ค The base framing protocol is defined by the ABNF [RFC5234] below. It's super important to remember this data is binary, NOT ASCII text. So a 1-bit field with values %x0 / %x1 is one single bit that's 0 or 1, not a whole byte standing in for the characters "0" or "1". Same deal for the 4-bit fields: they're 4 bits, not an ASCII character or a full byte ๐ [RFC5234] doesn't pick a character encoding, and it says: "Rules resolve into a string of terminal values, sometimes called characters. In ABNF, a character is merely a non-negative integer. In certain contexts, a specific mapping (encoding) of values into a character set (such as ASCII) will be specified." So here, the encoding is binary, where each terminal value gets encoded in however many bits its field needs, which changes from field to field ๐ฆ
ws-frame = frame-fin ; 1 bit in length
frame-rsv1 ; 1 bit in length
frame-rsv2 ; 1 bit in length
frame-rsv3 ; 1 bit in length
frame-opcode ; 4 bits in length
frame-masked ; 1 bit in length
frame-payload-length ; either 7, 7+16,
; or 7+64 bits in
; length
[ frame-masking-key ] ; 32 bits in length
frame-payload-data ; n*8 bits in
; length, where
; n >= 0
frame-fin = %x0 ; more frames of this message follow
/ %x1 ; final frame of this message
; 1 bit in length
frame-rsv1 = %x0 / %x1
; 1 bit in length, MUST be 0 unless
; negotiated otherwise
frame-rsv2 = %x0 / %x1
; 1 bit in length, MUST be 0 unless
; negotiated otherwise
frame-rsv3 = %x0 / %x1
; 1 bit in length, MUST be 0 unless
; negotiated otherwise
frame-opcode = frame-opcode-non-control /
frame-opcode-control /
frame-opcode-cont
frame-opcode-cont = %x0 ; frame continuation
frame-opcode-non-control= %x1 ; text frame
/ %x2 ; binary frame
/ %x3-7
; 4 bits in length,
; reserved for further non-control frames
frame-opcode-control = %x8 ; connection close
/ %x9 ; ping
/ %xA ; pong
/ %xB-F ; reserved for further control
; frames
; 4 bits in length
frame-masked = %x0
; frame is not masked, no frame-masking-key
/ %x1
; frame is masked, frame-masking-key present
; 1 bit in length
frame-payload-length = ( %x00-7D )
/ ( %x7E frame-payload-length-16 )
/ ( %x7F frame-payload-length-63 )
; 7, 7+16, or 7+64 bits in length,
; respectively
frame-payload-length-16 = %x0000-FFFF ; 16 bits in length
frame-payload-length-63 = %x0000000000000000-7FFFFFFFFFFFFFFF
; 64 bits in length
frame-masking-key = 4( %x00-FF )
; present only if frame-masked is 1
; 32 bits in length
frame-payload-data = (frame-masked-extension-data
frame-masked-application-data)
; when frame-masked is 1
/ (frame-unmasked-extension-data
frame-unmasked-application-data)
; when frame-masked is 0
frame-masked-extension-data = *( %x00-FF )
; reserved for future extensibility
; n*8 bits in length, where n >= 0
frame-masked-application-data = *( %x00-FF )
; n*8 bits in length, where n >= 0
frame-unmasked-extension-data = *( %x00-FF )
; reserved for future extensibility
; n*8 bits in length, where n >= 0
frame-unmasked-application-data = *( %x00-FF )
; n*8 bits in length, where n >= 0
5.3. Client-to-Server Masking ๐
okay so a masked frame MUST have frame-masked set to 1 ๐, as defined in Section 5.2. bestie, that bit is the whole "yes this frame is masked" flag. ๐
the masking key lives entirely inside the frame (it's frame-masking-key from Section 5.2) and it's used to mask the "Payload data" from that same section, which covers both "Extension data" and "Application data" ๐ซถ
the client picks the masking key at random, a 32-bit value. when it prepares a masked frame it MUST pick a fresh masking key from the set of allowed 32-bit values ๐ฒ. and the key has to be unpredictable, so it MUST be derived from a strong source of entropy, and the key for a given frame MUST NOT make it simple for a server/proxy to predict the key for the next frame ๐ฎ. this matters SO much because if attackers could predict the bytes that show up on the wire, they could pick the exact bytes they want to appear ๐ญ that's the whole reason. [RFC4086] is the place to read about what counts as a suitable entropy source for security-sensitive stuff ๐
masking doesn't change the length of the "Payload data" at all ๐. to turn masked data into unmasked data (or the other way around) you run this algorithm, and it's the same no matter which direction you're going. like, the exact same steps mask it AND unmask it โจ
each octet i of the transformed data (transformed-octet-i) is the XOR of octet i of the original data (original-octet-i) with the masking key octet at index i modulo 4 (masking-key-octet-j) ๐งฎ
j = i MOD 4
transformed-octet-i = original-octet-i XOR masking-key-octet-j
and the payload length in the framing (frame-payload-length) does not include the length of the masking key. it's just the length of the "Payload data", so the number of bytes that come after the masking key ๐ธ
5.4. Fragmentation ๐งฉ
okay the main reason fragmentation exists is so you can start sending a message when you don't know how big it's gonna be, without having to buffer the whole thing first ๐ฆ. if messages couldn't be fragmented, an endpoint would have to hold the entire message and count its length before sending the very first byte, which is so inconvenient ๐ฉ with fragmentation, a server or intermediary can pick a reasonable buffer size and write out a fragment to the network whenever that buffer fills up ๐
there's a second use-case too, which is multiplexing ๐ฆ. you don't want one big message on one logical channel hogging the whole output channel, so the multiplexing needs to be free to chop the message into smaller fragments and share the output nicely. (fyi the multiplexing extension isn't described in this document, so don't go looking for it here ๐)
unless an extension says otherwise, frames have no semantic meaning โจ. an intermediary might coalesce and/or split frames if no extensions were negotiated by the client and the server. or if some were negotiated, but the intermediary understood all of them and knows how to coalesce and/or split frames with those extensions around ๐ง . the big takeaway: without extensions, senders and receivers must not depend on the presence of specific frame boundaries ๐ซ
here are the fragmentation rules, bestie ๐
- an unfragmented message is a single frame with the FIN bit set (Section 5.2) and an opcode other than
0. - a fragmented message is a single frame with the FIN bit clear and an opcode other than
0, followed by zero or more frames with the FIN bit clear and the opcode set to0, and then ended by a single frame with the FIN bit set and an opcode of0. conceptually, a fragmented message is the same as one bigger message whose payload is the fragments' payloads glued together in order ๐งต. however, with extensions this might not hold, because the extension decides how the "Extension data" should be read. like, "Extension data" might only show up at the start of the first fragment and apply to all the rest, or each fragment might carry its own "Extension data" that only applies to that one fragment ๐ . without any "Extension data", here's how it works:
EXAMPLE: For a text message sent as three fragments, the first fragment would have an opcode of 0x1 and a FIN bit clear, the second fragment would have an opcode of 0x0 and a FIN bit clear, and the third fragment would have an opcode of 0x0 and a FIN bit that is set. ๐
- control frames (see Section 5.5) MAY be injected in the middle of a fragmented message ๐ซ. but control frames themselves MUST NOT be fragmented ๐ซ.
- message fragments MUST be delivered to the recipient in the order the sender sent them ๐ฌ.
- the fragments of one message MUST NOT be interleaved with the fragments of another message, unless an extension has been negotiated that can make sense of that interleaving ๐ โโ๏ธ.
- an endpoint MUST be capable of handling control frames in the middle of a fragmented message ๐ซถ.
- a sender MAY create fragments of any size for non-control messages ๐.
- clients and servers MUST support receiving both fragmented and unfragmented messages ๐.
- control frames can't be fragmented, so an intermediary MUST NOT attempt to change the fragmentation of a control frame ๐.
- an intermediary MUST NOT change the fragmentation of a message if any reserved bit values are used and the intermediary doesn't know what those values mean ๐.
- an intermediary MUST NOT change the fragmentation of any message on a connection where extensions have been negotiated, if the intermediary doesn't understand those extensions' semantics. likewise, an intermediary that didn't see the WebSocket handshake (and wasn't told what was in it) that set up the WebSocket connection MUST NOT change the fragmentation of any message on that connection ๐.
- because of all these rules, every fragment of a message is the same type, as set by the first fragment's opcode ๐. since control frames can't be fragmented, the type for all fragments in a message MUST be either text, binary, or one of the reserved opcodes โจ.
NOTE: if control frames couldn't be interjected, the latency of a ping would be really long if it got stuck behind a large message ๐ญ. that's why there's a requirement to handle control frames in the middle of a fragmented message ๐โโ๏ธ.
IMPLEMENTATION NOTE: without any extension, a receiver doesn't have to buffer the whole frame before processing it ๐. for example, if a streaming API is used, part of a frame can be handed to the application right away. BUT, heads up, this assumption might not hold for every future WebSocket extension ๐.
5.5. Control Frames ๐
control frames are identified by opcodes where the most significant bit of the opcode is 1 ๐. the control opcodes defined right now are 0x8 (Close), 0x9 (Ping), and 0xA (Pong) ๐
. opcodes 0xB-0xF are reserved for control frames that haven't been defined yet ๐ธ.
control frames are how the endpoints communicate state about the WebSocket ๐ก. and yes, they can be interjected in the middle of a fragmented message โญ.
all control frames MUST have a payload length of 125 bytes or less, and they MUST NOT be fragmented ๐ โโ๏ธ.
5.5.1. Close ๐ช
the Close frame has an opcode of 0x8 ๐ค.
the Close frame MAY contain a body (the "Application data" portion of the frame) that gives a reason for closing ๐. examples are an endpoint shutting down, an endpoint receiving a frame that's too large, or an endpoint receiving a frame that doesn't match the format it expects ๐ต. if there is a body, the first two bytes of the body MUST be a 2-byte unsigned integer (in network byte order) representing a status code with value /code/ defined in Section 7.4 ๐ข. after that 2-byte integer, the body MAY contain UTF-8-encoded data with value /reason/. the spec doesn't define how to interpret that part ๐คทโโ๏ธ. it's not necessarily human readable, but it can be handy for debugging or for passing info to the script that opened the connection ๐งโ๐ป. since it's not guaranteed to be human readable, clients MUST NOT show it to end users ๐.
Close frames sent from client to server must be masked as per Section 5.3 ๐.
the application MUST NOT send any more data frames after it sends a Close frame ๐.
if an endpoint receives a Close frame and didn't send one before, the endpoint MUST send a Close frame in response ๐ซ. (when it's responding, the endpoint typically echoes back the status code it got ๐ช.) it SHOULD do this as soon as practical โฐ. an endpoint MAY hold off sending its Close frame until its current message is finished sending. for example, if most of a fragmented message is already out, the endpoint MAY send the remaining fragments before the Close frame ๐งฉ. HOWEVER, there's no guarantee that an endpoint that already sent a Close frame will keep processing data ๐ฌ.
after both sending and receiving a Close message, an endpoint considers the WebSocket connection closed and MUST close the underlying TCP connection ๐. the server MUST close the underlying TCP connection immediately. the client SHOULD wait for the server to close the connection, but it MAY close the connection at any time after it has sent and received a Close message, for example if it hasn't gotten a TCP Close from the server within a reasonable amount of time โณ.
if a client and server both send a Close message at the same time, both endpoints will have sent and received one. they should consider the WebSocket connection closed and close the underlying TCP connection ๐ค.
5.5.2. Ping ๐
the Ping frame has an opcode of 0x9 ๐.
a Ping frame MAY include "Application data" ๐ฌ.
when an endpoint receives a Ping frame, it MUST send a Pong frame in response, unless it already received a Close frame ๐. it SHOULD respond with a Pong frame as soon as is practical ๐โโ๏ธ. Pong frames are discussed in Section 5.5.3 ๐ฆ.
an endpoint MAY send a Ping frame any time after the connection is established and before the connection is closed ๐ฐ๏ธ.
NOTE: a Ping frame can serve either as a keepalive or as a way to check that the remote endpoint is still responsive ๐.
5.5.3. Pong ๐
the Pong frame has an opcode of 0xA โจ.
Section 5.5.2 details requirements that apply to both Ping and Pong frames ๐.
a Pong frame sent in response to a Ping frame MUST have identical "Application data" to what was in the message body of the Ping frame it's replying to ๐ช.
if an endpoint receives a Ping frame and has not yet sent Pong frame(s) in response to previous Ping frame(s), the endpoint MAY choose to send a Pong frame only for the most recently processed Ping frame ๐ฏ.
a Pong frame MAY be sent unsolicited ๐ฃ. this works as a one-way heartbeat ๐. a response to an unsolicited Pong frame is not expected ๐.
5.6. Data Frames ๐
okay so data frames are the non-control ones, and you can spot them because the most significant bit of their opcode is 0 ๐
Right now the defined ones are 0x1 (Text) and 0x2 (Binary). Opcodes 0x3-0x7 are saved for future non-control frames that haven't been defined yet ๐ฎ
Data frames carry application-layer and/or extension-layer data, and the opcode decides how that data should be read:
- Text: the "Payload data" is text encoded as UTF-8. Heads up, one text frame might only hold part of a UTF-8 sequence, but the whole message MUST contain valid UTF-8 ๐ Invalid UTF-8 in reassembled messages gets handled the way Section 8.1 explains.
- Binary: the "Payload data" is arbitrary binary data, and what it means is totally up to the application layer ๐คทโโ๏ธ
5.7. Examples โจ
Here are some examples, because seeing the bytes is so much easier ๐ฅน
A single-frame unmasked text message
0x81 0x05 0x48 0x65 0x6c 0x6c 0x6f (contains "Hello")
A single-frame masked text message
0x81 0x85 0x37 0xfa 0x21 0x3d 0x7f 0x9f 0x4d 0x51 0x58 (contains "Hello")
A fragmented unmasked text message
0x01 0x03 0x48 0x65 0x6c (contains "Hel")
0x80 0x02 0x6c 0x6f (contains "lo")
Unmasked Ping request and masked Ping response
0x89 0x05 0x48 0x65 0x6c 0x6c 0x6f (contains a body of "Hello",
but the contents of the body are arbitrary)
0x8a 0x85 0x37 0xfa 0x21 0x3d 0x7f 0x9f 0x4d 0x51 0x58
(contains a body of "Hello", matching the body of the ping)
256 bytes binary message in a single unmasked frame
0x82 0x7E 0x0100 [256 bytes of binary data]
64KiB binary message in a single unmasked frame
0x82 0x7F 0x0000000000010000 [65536 bytes of binary data]
5.8. Extensibility ๐ง
The protocol is built so extensions can bolt on new capabilities to the base protocol ๐ The endpoints of a connection MUST negotiate the use of any extensions during the opening handshake. This spec also hands extensions a bunch of stuff to play with: opcodes 0x3 through 0x7 and 0xB through 0xF, the "Extension data" field, and the frame-rsv1, frame-rsv2, and frame-rsv3 bits of the frame header. Negotiation gets its own deep dive in Section 9.1. Here are some anticipated uses, and yes, this list is neither complete nor prescriptive ๐ซถ
- "Extension data" may be placed in the "Payload data" before the "Application data".
- Reserved bits can be allocated for per-frame needs.
- Reserved opcode values can be defined.
- Reserved bits can be allocated to the opcode field if more opcode values are needed.
- A reserved bit or an "extension" opcode can be defined that allocates additional bits out of the "Payload data" to define larger opcodes or more per-frame bits.
6. Sending and Receiving Data ๐ฆ
6.1. Sending Data
To Send a WebSocket Message comprising of data over a WebSocket connection, an endpoint MUST go through the following steps ๐ฌ
- The endpoint MUST ensure the WebSocket connection is in the OPEN state (cf. Sections 4.1 and 4.2.2.) If at any point the state of the WebSocket connection changes, the endpoint MUST abort the following steps.
- An endpoint MUST encapsulate the data in a WebSocket frame as defined in Section 5.2. If the data to be sent is large, or if the data is not available in its entirety at the point the endpoint wants to start sending it, the endpoint MAY instead encapsulate the data in a series of frames as defined in Section 5.4.
- The opcode (
frame-opcode) of the first frame containing the data MUST be set to the appropriate value from Section 5.2 for data that the recipient will interpret as text or binary data. - The FIN bit (
frame-fin) of the last frame containing the data MUST be set to1as defined in Section 5.2. - If the data is being sent by the client, the frame(s) MUST be masked as defined in Section 5.3.
- If any extensions (Section 9) have been negotiated for the WebSocket connection, extra considerations may apply, based on how those extensions are defined.
- The frame(s) that have been formed MUST be transmitted over the underlying network connection.
6.2. Receiving Data
To receive WebSocket data, an endpoint listens on the underlying network connection. Incoming data MUST be parsed as WebSocket frames as defined in Section 5.2. If a control frame (Section 5.5) comes in, the frame MUST be handled as Section 5.5 defines. When a data frame (Section 5.6) arrives, the endpoint MUST note the type of the data, based on the opcode (frame-opcode) from Section 5.2. The "Application data" from that frame is the data of the message. If the frame is a complete, unfragmented message (Section 5.4), A WebSocket Message Has Been Received with type type and data data. If the frame is part of a fragmented message, the "Application data" from the later data frames gets concatenated to build the data. Once the last fragment arrives, flagged by the FIN bit (frame-fin), A WebSocket Message Has Been Received with data data (the concatenated "Application data" of the fragments) and type type (taken from the first frame of the fragmented message). Subsequent data frames MUST be interpreted as belonging to a new WebSocket message ๐
Extensions (Section 9) MAY change how data gets read, and that includes what counts as a message boundary ๐ On top of adding "Extension data" before the "Application data" in a payload, extensions MAY also modify the "Application data" (for example, by compressing it).
A server MUST remove masking for data frames received from a client, as described in Section 5.3 ๐
7. Closing the Connection ๐
7.1. Definitions
7.1.1. Close the WebSocket Connection
To Close the WebSocket Connection, an endpoint closes the underlying TCP connection. An endpoint SHOULD use a method that cleanly closes the TCP connection, plus the TLS session if there is one, and throws away any trailing bytes it might have received. An endpoint MAY close the connection by any means available when it's necessary, such as when it's under attack ๐จ
Here's the lowkey important bit about who closes first ๐ง In most normal cases, the server SHOULD be the one to close the underlying TCP connection first, so that the server ends up holding the TIME_WAIT state instead of the client. That matters because a client stuck in TIME_WAIT can't re-open the connection for 2 maximum segment lifetimes (2MSL). A server in TIME_WAIT doesn't get the same hit, since a TIME_WAIT connection gets reopened right away when a new SYN with a higher seq number shows up. In abnormal cases, like when the client hasn't received a TCP Close from the server after a reasonable amount of time, a client MAY initiate the TCP Close. So, when a server is told to Close the WebSocket Connection, it SHOULD start a TCP Close immediately, and when a client is told to do the same, it SHOULD wait for a TCP Close from the server โณ
For a clean closure in C with Berkeley sockets, the move is: call shutdown() with SHUT_WR on the socket, keep calling recv() until it returns 0 (which tells you the peer has also done an orderly shutdown), and then call close() on the socket ๐งผ
7.1.2. Start the WebSocket Closing Handshake
To Start the WebSocket Closing Handshake with a status code (Section 7.4) /code/ and an optional close reason (Section 7.1.6) /reason/, an endpoint MUST send a Close control frame, as described in Section 5.5.1, with its status code set to /code/ and its close reason set to /reason/. Once an endpoint has both sent and received a Close control frame, that endpoint SHOULD Close the WebSocket Connection as defined in Section 7.1.1 ๐
7.1.3. The WebSocket Closing Handshake is Started
Once an endpoint either sends or receives a Close control frame, The WebSocket Closing Handshake is Started, and the WebSocket connection is in the CLOSING state ๐ฆ
7.1.4. The WebSocket Connection is Closed
When the underlying TCP connection is closed, The WebSocket Connection is Closed, and the WebSocket connection is in the CLOSED state ๐ If the TCP connection got closed after the WebSocket closing handshake was completed, the WebSocket connection is said to have been closed cleanly โจ
If the WebSocket connection could never be established, The WebSocket Connection is Closed still applies, but it's not cleanly ๐
7.1.5. The WebSocket Connection Close Code
As defined in Sections 5.5.1 and 7.4, a Close control frame can carry a status code that gives a reason for the closure. Either endpoint can start closing the WebSocket connection, and they might do it at the same time ๐ฏ The WebSocket Connection Close Code is the status code (Section 7.4) in the first Close control frame that the application implementing this protocol receives. If that Close control frame has no status code, The WebSocket Connection Close Code is considered to be 1005. If The WebSocket Connection is Closed and the endpoint never received a Close control frame (which could happen if the underlying transport connection drops), The WebSocket Connection Close Code is considered to be 1006 ๐
NOTE: Two endpoints may not agree on the value of The WebSocket Connection Close Code. For example, say the remote endpoint sent a Close frame, but the local application hasn't read the data holding that Close frame out of its socket's receive buffer yet. If the local application then decides on its own to close the connection and sends a Close frame, both endpoints will have sent and received a Close frame, and neither will send another one. Each endpoint will see the status code the other one sent as The WebSocket Connection Close Code. So the two endpoints might end up disagreeing on it, if both Start the WebSocket Closing Handshake on their own at roughly the same time ๐ซฃ
7.1.6. The WebSocket Connection Close Reason
As defined in Sections 5.5.1 and 7.4, a Close control frame can carry a status code that gives a reason for the closure, followed by UTF-8-encoded data. What that data means is left up to the endpoints, and this protocol doesn't define it ๐ Either endpoint can start closing, and that can happen at the same time too. The WebSocket Connection Close Reason is the UTF-8-encoded data after the status code (Section 7.4) in the first Close control frame the application implementing this protocol receives. If that Close control frame has no such data, The WebSocket Connection Close Reason is the empty string ๐ธ
NOTE: Same logic as Section 7.1.5 applies here, so two endpoints may not agree on The WebSocket Connection Close Reason either ๐ง
7.1.7. Fail the WebSocket Connection
Sometimes an algorithm or spec is like "nope, we're done here" and tells an endpoint to Fail the WebSocket Connection ๐. To do that, the client MUST Close the WebSocket Connection, and it MAY tell the user about the problem in an appropriate way (which is especially helpful for developers ๐งโ๐ปโจ). The server does the same thing: it MUST Close the WebSocket Connection too, and it SHOULD log the problem ๐.
If The WebSocket Connection is Established before the endpoint hits the point where it has to Fail the WebSocket Connection, the endpoint SHOULD send a Close frame with an appropriate status code (Section 7.4) first, and only then Close the WebSocket Connection ๐. An endpoint MAY skip the Close frame if it thinks the other side probably can't receive and process it, because of whatever error made the connection fail in the first place ๐. An endpoint MUST NOT keep trying to process data from the remote endpoint after it's been told to Fail the WebSocket Connection, and that includes a Close frame sent back in response ๐ซ.
Unless the stuff above says otherwise, or the application layer specifies it (like a script using the WebSocket API), clients SHOULD NOT close the connection ๐ โโ๏ธ.
7.2. Abnormal Closures
7.2.1. Client-Initiated Closure
Certain algorithms, especially during the opening handshake, require the client to Fail the WebSocket Connection ๐ค. To do so, the client MUST Fail the WebSocket Connection as defined in Section 7.1.7.
If the underlying transport layer connection is unexpectedly lost at any point, the client MUST Fail the WebSocket Connection ๐.
Unless the stuff above says otherwise, or the application layer specifies it (like a script using the WebSocket API), clients SHOULD NOT close the connection ๐ โโ๏ธ.
7.2.2. Server-Initiated Closure
Certain algorithms require or recommend that the server Abort the WebSocket Connection during the opening handshake ๐ช. To do so, the server MUST simply Close the WebSocket Connection (Section 7.1.1).
7.2.3. Recovering from Abnormal Closure
Abnormal closures can happen for lots of reasons ๐คทโโ๏ธ. Sometimes it's just a transient error, so reconnecting might give you a good connection and everything goes back to normal โจ. But sometimes it's a nontransient problem. If every deployed client has an abnormal closure and immediately, nonstop tries to reconnect, the server can end up dealing with what's basically a denial-of-service attack from a huge crowd of clients all piling on ๐ฑ. The end result could be that the service can't recover in a timely way, or that recovery gets way harder.
To prevent this, clients SHOULD use some form of backoff when trying to reconnect after abnormal closures, as described in this section ๐.
The first reconnect attempt SHOULD be delayed by a random amount of time โณ. The client gets to decide how that random delay is picked. A value chosen randomly between 0 and 5 seconds is a reasonable starting delay, though clients MAY choose a different interval to pick a delay length from, based on implementation experience and the particular application ๐ง .
If the first reconnect attempt fails, the subsequent attempts SHOULD be delayed by increasingly longer amounts of time, using a method such as truncated binary exponential backoff ๐.
7.3. Normal Closure of Connections
Servers MAY close the WebSocket connection whenever they want ๐ค๏ธ. Clients SHOULD NOT close the WebSocket connection arbitrarily ๐ . Either way, an endpoint starts a closure by following the procedures to Start the WebSocket Closing Handshake (Section 7.1.2).
7.4. Status Codes
When closing an established connection (like when sending a Close frame after the opening handshake has completed), an endpoint MAY indicate a reason for the closure ๐. What an endpoint makes of that reason, and what action it should take because of it, is left undefined by this spec ๐คทโโ๏ธ. This spec defines a set of predefined status codes and says which ranges extensions, frameworks, and end applications may use. The status code and any textual message are both optional parts of a Close frame ๐.
7.4.1. Defined Status Codes
Endpoints MAY use the following predefined status codes when sending a Close frame ๐ .
1000: 1000 indicates a normal closure, meaning the purpose the connection was established for has been fulfilled ๐.1001: 1001 indicates that an endpoint is "going away", like a server going down or a browser navigating away from a page ๐.1002: 1002 indicates that an endpoint is terminating the connection due to a protocol error ๐ฅ.1003: 1003 indicates that an endpoint is terminating the connection because it has received a type of data it cannot accept (e.g., an endpoint that understands only text data MAY send this if it receives a binary message) ๐ โโ๏ธ.1004: Reserved. The specific meaning might be defined in the future ๐ฎ.1005: 1005 is a reserved value and MUST NOT be set as a status code in a Close control frame by an endpoint. It is designated for use in applications expecting a status code to indicate that no status code was actually present ๐ซ.1006: 1006 is a reserved value and MUST NOT be set as a status code in a Close control frame by an endpoint. It is designated for use in applications expecting a status code to indicate that the connection was closed abnormally, e.g., without sending or receiving a Close control frame ๐.1007: 1007 indicates that an endpoint is terminating the connection because it has received data within a message that was not consistent with the type of the message (e.g., non-UTF-8 [RFC3629] data within a text message) ๐ค.1008: 1008 indicates that an endpoint is terminating the connection because it has received a message that violates its policy. This is a generic status code that can be returned when there is no other more suitable status code (e.g.,1003or1009) or if there is a need to hide specific details about the policy ๐.1009: 1009 indicates that an endpoint is terminating the connection because it has received a message that is too big for it to process ๐ฆ.1010: 1010 indicates that an endpoint (client) is terminating the connection because it expected the server to negotiate one or more extensions, but the server didn't return them in the response message of the WebSocket handshake. The list of extensions that are needed SHOULD appear in the reason part of the Close frame. Note that this status code is not used by the server, because it can fail the WebSocket handshake instead ๐งฉ.1011: 1011 indicates that a server is terminating the connection because it encountered an unexpected condition that prevented it from fulfilling the request ๐ซ .1015: 1015 is a reserved value and MUST NOT be set as a status code in a Close control frame by an endpoint. It is designated for use in applications expecting a status code to indicate that the connection was closed due to a failure to perform a TLS handshake (e.g., the server certificate can't be verified) ๐.
7.4.2. Reserved Status Code Ranges
0-999: Status codes in the range 0-999 are not used. ๐ซฅ1000-2999: Status codes in this range are reserved for definition by this protocol, its future revisions, and extensions specified in a permanent and readily available public specification ๐.3000-3999: Status codes in this range are reserved for use by libraries, frameworks, and applications. These status codes are registered directly with IANA. The interpretation of these codes is undefined by this protocol ๐๏ธ.4000-4999: Status codes in this range are reserved for private use and thus can't be registered. Such codes can be used by prior agreements between WebSocket applications. The interpretation of these codes is undefined by this protocol ๐คซ.
8. Error Handling ๐จ
8.1. Handling Errors in UTF-8-Encoded Data
When an endpoint is supposed to interpret a byte stream as UTF-8 but finds that the byte stream is not actually a valid UTF-8 stream, that endpoint MUST Fail the WebSocket Connection ๐ค. This rule applies both during the opening handshake and during subsequent data exchange ๐ค.
9. Extensions โจ
WebSocket clients MAY request extensions to this specification, and WebSocket servers MAY accept some or all of the extensions the client requested ๐. A server MUST NOT respond with any extension that the client did not request ๐ . If extension parameters are included in negotiations between the client and the server, those parameters MUST be chosen in accordance with the specification of the extension they apply to ๐.
9.1. Negotiating Extensions โจ
okay bestie, so when a client wants extensions it sends a Sec-WebSocket-Extensions header field ๐
. It follows the normal HTTP header field rules (see [RFC2616], Section 4.2), and its value is spelled out in the ABNF below, which uses [RFC2616] ABNF, including the "implied *LWS rule". If either side gets a value during negotiation that doesn't match this ABNF, the recipient of that malformed data MUST immediately Fail the WebSocket Connection ๐
Sec-WebSocket-Extensions = extension-list
extension-list = 1#extension
extension = extension-token *( ";" extension-param )
extension-token = registered-token
registered-token = token
extension-param = token [ "=" (token | quoted-string) ]
;When using the quoted-string syntax variant, the value
;after quoted-string unescaping MUST conform to the
;'token' ABNF.
Note that, like other HTTP header fields, this header field MAY be split or combined across multiple lines. Ergo, these two are equivalent ๐ง
Sec-WebSocket-Extensions: foo
Sec-WebSocket-Extensions: bar; baz=2
which is exactly the same as
Sec-WebSocket-Extensions: foo, bar; baz=2
Any extension-token you use MUST be a registered token (see Section 11.4) ๐ The parameters for any given extension MUST be defined for that extension. Also, the client is only offering to use the advertised extensions, so it MUST NOT use them unless the server says it wants to use the extension. Not me waiting for a yes before I start ๐ญ
Order matters here ๐ธ Any interactions between multiple extensions MAY be defined in the documents that define the extensions. If nobody defines that, the client's list counts as a preference order, with the first options being most preferable. The extensions the server lists in its response are the ones actually in use for the connection. If the extensions modify the data and/or framing, the order of operations should be assumed to be the same as the order the extensions are listed in the server's response in the opening handshake.
For example, say there are two extensions, "foo" and "bar", and the server sends Sec-WebSocket-Extensions with the value "foo, bar". Then the operations on the data happen as bar(foo(data)), whether those changes are to the data itself (like compression) or to the framing that may "stack" ๐ฅ
Non-normative examples of acceptable extension header fields (long lines are folded so they're easier to read) ๐
Sec-WebSocket-Extensions: deflate-stream
Sec-WebSocket-Extensions: mux; max-channels=4; flow-control,
deflate-stream
Sec-WebSocket-Extensions: private-extension
A server accepts one or more extensions by including a Sec-WebSocket-Extensions header field containing one or more of the extensions the client requested. What any extension parameter means, and what counts as a valid server response to a client's requested set of parameters, will be defined by each extension itself ๐ฆ
9.2. Known Extensions ๐
Extensions are how implementations opt in to extra protocol features ๐ This document doesn't define any extension, but implementations MAY use extensions that are defined separately โจ
10. Security Considerations ๐ก๏ธ
This section covers some security considerations for the WebSocket Protocol ๐ The specific ones live in the subsections of this section, so let's get into it bestie ๐
10.1. Non-Browser Clients ๐ค
The WebSocket Protocol protects against malicious JavaScript running inside a trusted app like a web browser, for example by checking the Origin header field (more on that below). See Section 1.6 for extra details. Those assumptions don't hold up when the client is more capable than a browser ๐ฌ
This protocol is meant to be used by scripts on web pages, but hosts can also use it directly. Those hosts are acting on their own behalf, so they can send fake Origin header fields and mislead the server ๐ So servers should be careful about assuming they're talking directly to scripts from known origins, and they must consider that they might be accessed in unexpected ways. Most importantly, a server should not trust that any input is valid ๐ซ
EXAMPLE: If the server uses input as part of SQL queries, all input text should be escaped before it's passed to the SQL server, lest the server be susceptible to SQL injection ๐ Not the vibe!
10.2. Origin Considerations ๐ธ
Servers that aren't meant to process input from every web page, but only from certain sites, SHOULD verify that the Origin field is an origin they expect. If the origin is unacceptable to the server, then it SHOULD respond to the WebSocket handshake with a reply containing the HTTP 403 Forbidden status code ๐ช
The Origin header field protects against attacks where the untrusted party is usually the author of a JavaScript application running inside a trusted client. The client itself can contact the server and, through the Origin header field, decide whether to give those communication privileges to the JavaScript app ๐ The goal isn't to stop non-browsers from connecting. It's to make sure trusted browsers controlled by possibly malicious JavaScript can't fake a WebSocket handshake ๐ซถ
10.3. Attacks On Infrastructure (Masking) ๐ญ
Endpoints aren't the only ones that get targeted via WebSockets. Other parts of web infrastructure, like proxies, can be attacked too ๐ญ
While this protocol was being developed, an experiment showed a class of attacks on proxies that led to poisoning of caching proxies out in the wild [TALKING] ๐ The general shape of the attack: connect to a server under the "attacker's" control, do an UPGRADE on the HTTP connection the way the WebSocket Protocol does to set up a connection, and then send data over that UPGRADEd connection that looks like a GET request for a specific known resource (in a real attack, probably something like a widely deployed hit-tracking script or a resource on an ad network). The remote server replies with something that looks like a response to that fake GET, and a nonzero percentage of deployed intermediaries cache that response, which poisons the cache. So if a user could be talked into visiting an attacker's website, the attacker could poison the cache for that user and for other users behind the same cache, run malicious script on other origins, and break the web security model ๐จ
To avoid these attacks on deployed intermediaries, it isn't enough to put non-HTTP framing in front of application data, because you can't exhaustively find and test every nonconformant intermediary to check that it won't skip that framing and act wrongly on the payload. So the defense is to mask all data going from the client to the server. That way the remote script (the attacker) has no control over how the data looks on the wire, and can't build a message that an intermediary might misread as an HTTP request ๐ก๏ธ
Clients MUST choose a new masking key for each frame, using an algorithm that end applications providing the data can't predict. For example, each masking key could come from a cryptographically strong random number generator ๐ฒ If the same key gets reused, or there's a decipherable pattern for choosing the next key, the attacker can send a message that looks like an HTTP request once masked. (They take the message they want to appear on the wire, mask it with the next masking key, and then the masking key effectively unmasks the data when the client applies it. Galaxy brain attack, but still no thanks ๐ )
It's also necessary that once a frame's transmission from a client has begun, the frame's payload (the application-supplied data) must not be modifiable by the application. Otherwise, an attacker could send a long frame whose first data was a known value (like all zeros), work out the masking key from the first part they receive, and then change the rest of the frame's data so it looks like an HTTP request once masked. (This is basically the same problem as using a known or predictable masking key, just sneakier ๐ฅท) If more data needs to be sent, or data that's already queued gets changed, that new or changed data must go in a new frame, and therefore with a new masking key. In short: once a frame starts transmitting, its contents must not be modifiable by the remote script (application) ๐
The threat model we're protecting against is a client sending data that looks like an HTTP request. So the channel that needs masking is the data going from the client to the server. The server-to-client data can be made to look like a response, but to pull that off the client would also have to be able to forge a request. So it wasn't deemed necessary to mask data in both directions, and data from the server to the client is not masked ๐โโ๏ธ
Even with masking in place, non-compliant HTTP proxies are still vulnerable to this type of poisoning attack from clients and servers that don't apply masking ๐ซ
10.4. Implementation-Specific Limits ๐งฑ
Implementations that have their own implementation- and/or platform-specific limits on frame size, or on total message size after reassembly from multiple frames, MUST protect themselves against going over those limits. (For example, a malicious endpoint can try to exhaust its peer's memory or mount a denial-of-service attack, either by sending one giant frame, like one of size 2**60, or by sending a long stream of small frames that are part of a fragmented message ๐ฅ) An implementation like this SHOULD impose a limit on frame sizes and on total message size after reassembly from multiple frames ๐ง
10.5. WebSocket Client Authentication ๐ชช
This protocol doesn't prescribe any particular way for servers to authenticate clients during the WebSocket handshake ๐ The WebSocket server can use any client authentication mechanism that's available to a generic HTTP server, like cookies, HTTP authentication, or TLS authentication ๐ช
10.6. Connection Confidentiality and Integrity ๐
Okay bestie, so connection confidentiality and integrity come from running WebSocket over TLS, which is what the wss URIs are for โจ. WebSocket implementations MUST support TLS and SHOULD employ it when they're talking to their peers ๐ซถ.
With TLS connections, how much protection you actually get depends a LOT on how strong the algorithms are that get negotiated during the TLS handshake ๐ . Some TLS cipher mechanisms don't even provide connection confidentiality, which is so not it ๐. So to get reasonable protection, clients should use only Strong TLS algorithms ๐. "Web Security Context: User Interface Guidelines" [W3C.REC-wsc-ui-20100812] talks about what counts as Strong TLS algorithms ๐. [RFC5246] gives extra guidance in Appendix A.5 and Appendix D.3 ๐ธ.
10.7. Handling of Invalid Data ๐ โโ๏ธ
Incoming data MUST always be validated by both clients and servers, no exceptions ๐ซ. If an endpoint ever gets data it doesn't understand, or data that breaks the criteria it uses to decide whether input is safe, or it sees an opening handshake that doesn't match what it was expecting (like a wrong path or origin in the client request), the endpoint MAY drop the TCP connection ๐. If the invalid data showed up after a successful WebSocket handshake, the endpoint SHOULD send a Close frame with an appropriate status code (Section 7.4) before moving on to _Close the WebSocket Connection_ ๐ฆ. Using a Close frame with a good status code can really help with figuring out what went wrong ๐ต๏ธ. If the invalid data is sent during the WebSocket handshake, the server SHOULD return an appropriate HTTP [RFC2616] status code ๐งพ.
A super common class of security problems happens when text data gets sent in the wrong encoding ๐ญ. This protocol says that messages with a Text data type (as opposed to Binary or other types) contain UTF-8-encoded data ๐. The length is still indicated, and applications using this protocol should use the length to figure out where the frame actually ends, but sending data in an improper encoding can still break the assumptions that apps built on top of this protocol make. That can lead to anything from misread data to lost data or potential security bugs ๐.
10.8. Use of SHA-1 by the WebSocket Handshake ๐ง
The WebSocket handshake described here doesn't depend on any security properties of SHA-1, like collision resistance or resistance to the second pre-image attack (as described in [RFC4270]) ๐ฆ.
11. IANA Considerations ๐
11.1. Registration of New URI Schemes ๐
11.1.1. Registration of "ws" Scheme
A ws URI identifies a WebSocket server and resource name ๐.
- URI scheme name:
ws - Status: Permanent
- URI scheme syntax: Using the ABNF [RFC5234] syntax and ABNF terminals from the URI specification [RFC3986]:
"ws:" "//" authority path-abempty [ "?" query ]
The <path-abempty> and <query> [RFC3986] components form the resource name sent to the server to identify the kind of service desired. Other components have the meanings described in [RFC3986] โจ.
- URI scheme semantics: The only operation for this scheme is to open a connection using the WebSocket Protocol ๐.
- Encoding considerations: Characters in the host component that are excluded by the syntax defined above MUST be converted from Unicode to ASCII as specified in [RFC3987] or its replacement. For the purposes of scheme-based normalization, Internationalized Domain Name (IDN) forms of the host component and their conversions to punycode are considered equivalent (see Section 5.3.3 of [RFC3987]) ๐.
Characters in other components that are excluded by the syntax defined above MUST be converted from Unicode to ASCII by first encoding the characters as UTF-8 and then replacing the corresponding bytes using their percent-encoded form, as defined in the URI [RFC3986] and Internationalized Resource Identifier (IRI) [RFC3987] specifications ๐ซง.
- Applications/protocols that use this URI scheme name: WebSocket Protocol
- Interoperability considerations: Use of WebSocket requires use of HTTP version 1.1 or higher ๐.
- Security considerations: See "Security Considerations" section ๐ฅ.
- Contact:
HYBI WG <hybi@ietf.org> - Author/Change controller:
IETF <iesg@ietf.org> - References: RFC 6455
11.1.2. Registration of "wss" Scheme
A wss URI identifies a WebSocket server and resource name, and it says that traffic over that connection is protected via TLS ๐. That includes the usual TLS perks like data confidentiality and integrity and endpoint authentication ๐.
- URI scheme name:
wss - Status: Permanent
- URI scheme syntax: Using the ABNF [RFC5234] syntax and ABNF terminals from the URI specification [RFC3986]:
"wss:" "//" authority path-abempty [ "?" query ]
The <path-abempty> and <query> components form the resource name sent to the server to identify the kind of service desired. Other components have the meanings described in [RFC3986] ๐.
- URI scheme semantics: The only operation for this scheme is to open a connection using the WebSocket Protocol, encrypted using TLS ๐.
- Encoding considerations: Characters in the host component that are excluded by the syntax defined above MUST be converted from Unicode to ASCII as specified in [RFC3987] or its replacement. For the purposes of scheme-based normalization, IDN forms of the host component and their conversions to punycode are considered equivalent (see Section 5.3.3 of [RFC3987]) ๐ธ.
Characters in other components that are excluded by the syntax defined above MUST be converted from Unicode to ASCII by first encoding the characters as UTF-8 and then replacing the corresponding bytes using their percent-encoded form, as defined in the URI [RFC3986] and IRI [RFC3987] specifications ๐ .
- Applications/protocols that use this URI scheme name: WebSocket Protocol over TLS
- Interoperability considerations: Use of WebSocket requires use of HTTP version 1.1 or higher ๐ฆ.
- Security considerations: See "Security Considerations" section ๐.
- Contact:
HYBI WG <hybi@ietf.org> - Author/Change controller:
IETF <iesg@ietf.org> - References: RFC 6455
11.2. Registration of the "WebSocket" HTTP Upgrade Keyword ๐
This section defines a keyword registered in the HTTP Upgrade Tokens Registry as per RFC 2817 [RFC2817] โจ.
- Name of token: WebSocket
- Author/Change controller:
IETF <iesg@ietf.org> - Contact:
HYBI <hybi@ietf.org> - References: RFC 6455
11.3. Registration of New HTTP Header Fields ๐
11.3.1. Sec-WebSocket-Key
This section describes a header field registered in the Permanent Message Header Field Names registry [RFC3864] ๐.
- Header field name:
Sec-WebSocket-Key - Applicable protocol: http
- Status: standard
- Author/Change controller: IETF
- Specification document(s): RFC 6455
- Related information: This header field is only used for WebSocket opening handshake.
The Sec-WebSocket-Key header field is used in the WebSocket opening handshake. It is sent from the client to the server to provide part of the information the server uses to prove that it received a valid WebSocket opening handshake. This helps make sure the server doesn't accept connections from non-WebSocket clients (like HTTP clients) that are being abused to send data to unsuspecting WebSocket servers ๐ก๏ธ.
The Sec-WebSocket-Key header field MUST NOT appear more than once in an HTTP request ๐
โโ๏ธ.
11.3.2. Sec-WebSocket-Extensions
This section describes a header field for registration in the Permanent Message Header Field Names registry [RFC3864] ๐ง.
- Header field name:
Sec-WebSocket-Extensions - Applicable protocol: http
- Status: standard
- Author/Change controller: IETF
- Specification document(s): RFC 6455
- Related information: This header field is only used for WebSocket opening handshake.
The Sec-WebSocket-Extensions header field is used in the WebSocket opening handshake. It is initially sent from the client to the server, and then subsequently sent from the server to the client, to agree on a set of protocol-level extensions to use for the duration of the connection ๐.
The Sec-WebSocket-Extensions header field MAY appear multiple times in an HTTP request (which is logically the same as a single Sec-WebSocket-Extensions header field that contains all values). However, the Sec-WebSocket-Extensions header field MUST NOT appear more than once in an HTTP response ๐ฐ.
11.3.3. Sec-WebSocket-Accept
This section describes a header field registered in the Permanent Message Header Field Names registry [RFC3864] ๐.
- Header field name:
Sec-WebSocket-Accept - Applicable protocol: http
- Status: standard
- Author/Change controller: IETF
- Specification document(s): RFC 6455
- Related information: This header field is only used for the WebSocket opening handshake.
The Sec-WebSocket-Accept header field is used in the WebSocket opening handshake. It is sent from the server to the client to confirm that the server is willing to initiate the WebSocket connection ๐ซถ.
The Sec-WebSocket-Accept header MUST NOT appear more than once in an HTTP response ๐.
11.3.4. Sec-WebSocket-Protocol
This section describes a header field registered in the Permanent Message Header Field Names registry [RFC3864] ๐.
- Header field name:
Sec-WebSocket-Protocol - Applicable protocol: http
- Status: standard
- Author/Change controller: IETF
- Specification document(s): RFC 6455
- Related information: This header field is only used for the WebSocket opening handshake.
The Sec-WebSocket-Protocol header field is used in the WebSocket opening handshake. It is sent from the client to the server and back from the server to the client to confirm the subprotocol of the connection. This lets scripts both select a subprotocol and be sure that the server agreed to serve that subprotocol โจ.
The Sec-WebSocket-Protocol header field MAY appear multiple times in an HTTP request (which is logically the same as a single Sec-WebSocket-Protocol header field that contains all values). However, the Sec-WebSocket-Protocol header field MUST NOT appear more than once in an HTTP response ๐.
11.3.5. Sec-WebSocket-Version ๐
Okay so this header is registered in the Permanent Message Header Field Names registry [RFC3864], and it's giving very official vibes ๐
- Header field name:
Sec-WebSocket-Version - Applicable protocol: http
- Status: standard
- Author/Change controller: IETF
- Specification document(s): RFC 6455
- Related information: This header field is only used for the WebSocket opening handshake.
Bestie, this is the header the client uses to say which WebSocket protocol version it's speaking ๐ฆ That way the server can read the opening handshake correctly and understand the data coming after it. If the server can't handle that data safely, it can close the connection โจ The server also sends Sec-WebSocket-Version back to the client when the handshake fails because the version the client asked for isn't one the server knows. In that case, the header lists the version(s) the server does support. So it's like a little "here's what I speak" note ๐ธ
Also, higher version numbers are not guaranteed to be backward compatible with lower ones. Don't assume they'll get along just because the numbers go up ๐
The Sec-WebSocket-Version header field MAY appear multiple times in an HTTP response, and that's logically the same as one header carrying all the values together ๐ But in an HTTP request, Sec-WebSocket-Version MUST NOT appear more than once. Keep it to one! ๐ซ
11.4. WebSocket Extension Name Registry โจ
So this section creates a brand new IANA registry for WebSocket Extension names, to be used with the WebSocket Protocol. It follows the principles in RFC 5226 [RFC5226] ๐ง
IANA keeps track of the following info for each entry:
- Extension Identifier: The identifier of the extension, as it will be used in the
Sec-WebSocket-Extensionsheader field registered in Section 11.3.2 of this specification. The value must conform to the requirements for an extension-token, as defined in Section 9.1 of this specification. - Extension Common Name: The name of the extension, as people generally refer to it.
- Extension Definition: A reference to the document where the extension being used with the WebSocket Protocol is defined.
- Known Incompatible Extensions: A list of extension identifiers that this extension is known to be incompatible with. Yikes ๐
WebSocket Extension names are subject to the "First Come First Served" IANA registration policy [RFC5226]. First come, first served, babe ๐
There are no initial values in this registry. It's a clean slate ๐ซง
11.5. WebSocket Subprotocol Name Registry ๐
This section creates a new IANA registry for WebSocket Subprotocol names, to be used with the WebSocket Protocol. It also follows the principles in RFC 5226 [RFC5226] ๐
IANA keeps track of the following info:
- Subprotocol Identifier: The identifier of the subprotocol, as it will be used in the
Sec-WebSocket-Protocolheader field registered in Section 11.3.4 of this specification. The value must conform to the requirements given in item 10 of Section 4.1 of this specification. In other words, the value must be a token, as defined by RFC 2616 [RFC2616] ๐ - Subprotocol Common Name: The name of the subprotocol, as people generally refer to it.
- Subprotocol Definition: A reference to the document where the subprotocol being used with the WebSocket Protocol is defined.
WebSocket Subprotocol names are subject to the "First Come First Served" IANA registration policy [RFC5226]. Same energy as the extension names, no notes ๐ซถ
11.6. WebSocket Version Number Registry ๐ฆ
This section creates a new IANA registry for WebSocket Version Numbers, to be used with the WebSocket Protocol. It follows the principles in RFC 5226 [RFC5226] ๐ธ
IANA keeps track of the following info:
- Version Number: The version number to be used in
Sec-WebSocket-Versionis specified in Section 4.1 of this specification. The value must be a non-negative integer in the range between 0 and 255 (inclusive). - Reference: The RFC requesting a new version number, or a draft name with a version number (more on that below).
- Status: Either "Interim" or "Standard." More on this below too ๐
Every version number is designated as either "Interim" or "Standard," okay?
A "Standard" version number is documented in an RFC and identifies a major, stable version of the WebSocket protocol, like the one defined by this RFC ๐ "Standard" version numbers are subject to the "IETF Review" IANA registration policy [RFC5226].
An "Interim" version number is documented in an Internet-Draft. Its job is to help implementers identify and interoperate with deployed versions of the WebSocket protocol, including versions that were developed before this RFC was published ๐ซถ "Interim" version numbers are subject to the "Expert Review" IANA registration policy [RFC5226]. The initial Designated Experts are the chairs of the HYBI Working Group. If the working group closes, the Area Directors for the IETF Applications Area take over as the initial Designated Experts ๐ง
IANA has added initial values to the registry as follows:
+--------+-----------------------------------------+----------+
|Version | Reference | Status |
| Number | | |
+--------+-----------------------------------------+----------+
| 0 + draft-ietf-hybi-thewebsocketprotocol-00 | Interim |
+--------+-----------------------------------------+----------+
| 1 + draft-ietf-hybi-thewebsocketprotocol-01 | Interim |
+--------+-----------------------------------------+----------+
| 2 + draft-ietf-hybi-thewebsocketprotocol-02 | Interim |
+--------+-----------------------------------------+----------+
| 3 + draft-ietf-hybi-thewebsocketprotocol-03 | Interim |
+--------+-----------------------------------------+----------+
| 4 + draft-ietf-hybi-thewebsocketprotocol-04 | Interim |
+--------+-----------------------------------------+----------+
| 5 + draft-ietf-hybi-thewebsocketprotocol-05 | Interim |
+--------+-----------------------------------------+----------+
| 6 + draft-ietf-hybi-thewebsocketprotocol-06 | Interim |
+--------+-----------------------------------------+----------+
| 7 + draft-ietf-hybi-thewebsocketprotocol-07 | Interim |
+--------+-----------------------------------------+----------+
| 8 + draft-ietf-hybi-thewebsocketprotocol-08 | Interim |
+--------+-----------------------------------------+----------+
| 9 + Reserved | |
+--------+-----------------------------------------+----------+
| 10 + Reserved | |
+--------+-----------------------------------------+----------+
| 11 + Reserved | |
+--------+-----------------------------------------+----------+
| 12 + Reserved | |
+--------+-----------------------------------------+----------+
| 13 + RFC 6455 | Standard |
+--------+-----------------------------------------+----------+
11.7. WebSocket Close Code Number Registry ๐
This section creates a new IANA registry for WebSocket Connection Close Code Numbers, following the principles in RFC 5226 [RFC5226] ๐
IANA keeps track of the following info:
- Status Code: The Status Code denotes a reason for a WebSocket connection closure, as described in Section 7.4 of this document. It's an integer between 1000 and 4999 (inclusive) ๐ข
- Meaning: What the status code means. Each status code has to have a unique meaning, no duplicates allowed ๐ซ
- Contact: A contact for the entity reserving the status code.
- Reference: The stable document that requests the status codes and defines what they mean. This is required for status codes in the range 1000-2999, and recommended for status codes in the range 3000-3999.
Close code numbers have different registration requirements depending on their range ๐ Requests for status codes for use by this protocol and its future versions or extensions are subject to any one of these IANA registration policies: "Standards Action," "Specification Required" (which implies "Designated Expert"), or "IESG Review." These should be granted in the range 1000-2999.
Requests for status codes for use by libraries, frameworks, and applications are subject to the "First Come First Served" IANA registration policy. These should be granted in the range 3000-3999.
The range 4000-4999 is designated for Private Use ๐
Requests should say whether they're asking for status codes for the WebSocket Protocol (or a future version of it), for extensions, or for libraries/frameworks/applications. Be clear about it, bestie ๐
IANA has added initial values to the registry as follows:
|Status Code | Meaning | Contact | Reference |
-+------------+-----------------+---------------+-----------|
| 1000 | Normal Closure | hybi@ietf.org | RFC 6455 |
-+------------+-----------------+---------------+-----------|
| 1001 | Going Away | hybi@ietf.org | RFC 6455 |
-+------------+-----------------+---------------+-----------|
| 1002 | Protocol error | hybi@ietf.org | RFC 6455 |
-+------------+-----------------+---------------+-----------|
| 1003 | Unsupported Data| hybi@ietf.org | RFC 6455 |
-+------------+-----------------+---------------+-----------|
| 1004 | ---Reserved---- | hybi@ietf.org | RFC 6455 |
-+------------+-----------------+---------------+-----------|
| 1005 | No Status Rcvd | hybi@ietf.org | RFC 6455 |
-+------------+-----------------+---------------+-----------|
| 1006 | Abnormal Closure| hybi@ietf.org | RFC 6455 |
-+------------+-----------------+---------------+-----------|
| 1007 | Invalid frame | hybi@ietf.org | RFC 6455 |
| | payload data | | |
-+------------+-----------------+---------------+-----------|
| 1008 | Policy Violation| hybi@ietf.org | RFC 6455 |
-+------------+-----------------+---------------+-----------|
| 1009 | Message Too Big | hybi@ietf.org | RFC 6455 |
-+------------+-----------------+---------------+-----------|
| 1010 | Mandatory Ext. | hybi@ietf.org | RFC 6455 |
-+------------+-----------------+---------------+-----------|
| 1011 | Internal Server | hybi@ietf.org | RFC 6455 |
| | Error | | |
-+------------+-----------------+---------------+-----------|
| 1015 | TLS handshake | hybi@ietf.org | RFC 6455 |
-+------------+-----------------+---------------+-----------|
11.8. WebSocket Opcode Registry ๐
This spec sets up a brand-new IANA registry for WebSocket Opcodes, following the principles in RFC 5226 [RFC5226]. โจ
IANA keeps the following info for this registry:
- Opcode: the frame type of a WebSocket frame, as defined in Section 5.2. It's an integer from 0 to 15, inclusive. ๐ง
- Meaning: what the opcode value means. ๐
- Reference: the spec that asked for the opcode. ๐
WebSocket Opcode numbers are handled under the "Standards Action" IANA registration policy [RFC5226]. ๐
IANA already added these initial values to the registry: ๐
|Opcode | Meaning | Reference |
-+--------+-------------------------------------+-----------|
| 0 | Continuation Frame | RFC 6455 |
-+--------+-------------------------------------+-----------|
| 1 | Text Frame | RFC 6455 |
-+--------+-------------------------------------+-----------|
| 2 | Binary Frame | RFC 6455 |
-+--------+-------------------------------------+-----------|
| 8 | Connection Close Frame | RFC 6455 |
-+--------+-------------------------------------+-----------|
| 9 | Ping Frame | RFC 6455 |
-+--------+-------------------------------------+-----------|
| 10 | Pong Frame | RFC 6455 |
-+--------+-------------------------------------+-----------|
11.9. WebSocket Framing Header Bits Registry ๐ฆ
Same energy here: this spec creates another new IANA registry, for WebSocket Framing Header Bits, following the principles in RFC 5226 [RFC5226]. This one controls the assignment of the bits marked RSV1, RSV2, and RSV3 in Section 5.2. ๐
Those bits are reserved for future versions or extensions of this spec. ๐ฎ
WebSocket Framing Header Bits assignments are handled under the "Standards Action" IANA registration policy [RFC5226]. ๐ซถ
12. Using the WebSocket Protocol from Other Specifications ๐งฉ
The WebSocket Protocol is meant to be used by other specs as a generic way to do dynamic, author-defined content, like in a spec that defines a scripted API. ๐
So a spec like that first needs to Establish a WebSocket Connection, and it gives that algorithm:
- The destination, made up of a host and a port. ๐
- A resource name, which lets multiple services be identified at one host and port. ๐๏ธ
- A secure flag, which is true if the connection is going to be encrypted and false otherwise. ๐
- An ASCII serialization of an origin [RFC6454] that's being made responsible for the connection. ๐โโ๏ธ
- Optionally, a string naming a protocol to be layered over the WebSocket connection. ๐ฐ
The host, port, resource name, and secure flag are usually pulled out of a URI using the steps to parse a WebSocket URI's components. Those steps fail if the URI doesn't specify a WebSocket. ๐ซ
If the connection ever needs to close at any point, the spec has to use the Close the WebSocket Connection algorithm (Section 7.1.1). ๐
Section 7.1.4 defines when The WebSocket Connection is Closed. ๐
While a connection is open, the spec will need to handle the cases where A WebSocket Message Has Been Received (Section 6.2). ๐ฉ
To send some data data to an open connection, the spec needs to Send a WebSocket Message (Section 6.1). ๐
13. Acknowledgements ๐ธ
Huge shoutout to Ian Hickson, the original author and editor of this protocol, no notes. ๐ The initial design of this spec benefited from lots of people in the WHATWG and the WHATWG mailing list. Contributions to that spec aren't tracked by section, but there's a list of everyone who contributed in the WHATWG HTML specification at http://whatwg.org/html5. ๐ฅน
Special thanks also to John Tamplin, who wrote a significant amount of text for the "Data Framing" section. โ๏ธ
Special thanks also to Adam Barth, who wrote a significant amount of text and did background research for the "Data Masking" section. ๐ฌ
Special thanks to Lisa Dusseault for the Apps Area review (and for helping start this work), Richard Barnes for the Gen-Art review, and Magnus Westerlund for the Transport Area Review. ๐ Special thanks to the HYBI WG chairs, past and present, who tirelessly worked behind the scenes to move this toward completion: Joe Hildebrand, Salvatore Loreto, and Gabriel Montenegro. And last but not least, special thank you to the responsible Area Director, Peter Saint-Andre. ๐โจ
Thank you to the following people who joined discussions on the HYBI WG mailing list and contributed ideas and/or gave detailed reviews (the list is likely incomplete): Greg Wilkins, John Tamplin, Willy Tarreau, Maciej Stachowiak, Jamie Lokier, Scott Ferguson, Bjoern Hoehrmann, Julian Reschke, Dave Cridland, Andy Green, Eric Rescorla, Inaki Baz Castillo, Martin Thomson, Roberto Peon, Patrick McManus, Zhong Yu, Bruce Atherton, Takeshi Yoshino, Martin J. Duerst, James Graham, Simon Pieters, Roy T. Fielding, Mykyta Yevstifeyev, Len Holgate, Paul Colomiets, Piotr Kulaga, Brian Raymor, Jan Koehler, Joonas Lehtolahti, Sylvain Hellegouarch, Stephen Farrell, Sean Turner, Pete Resnick, Peter Thorson, Joe Mason, John Fallows, and Alexander Philippou. ๐ People listed above didn't necessarily endorse the end result of this work, so no pressure on them lol. ๐
14. References ๐
14.1. Normative References ๐
[ANSI.X3-4.1986]
American National Standards Institute, "Coded Character
Set - 7-bit American Standard Code for Information
Interchange", ANSI X3.4, 1986.
[FIPS.180-3]
National Institute of Standards and Technology, "Secure
Hash Standard", FIPS PUB 180-3, October 2008,
<http://csrc.nist.gov/publications/fips/fips180-3/
fips180-3_final.pdf>.
[RFC1928] Leech, M., Ganis, M., Lee, Y., Kuris, R., Koblas, D., and
L. Jones, "SOCKS Protocol Version 5", RFC 1928,
March 1996.
[RFC2119] Bradner, S., "Key words for use in RFCs to Indicate
Requirement Levels", BCP 14, RFC 2119, March 1997.
[RFC2616] Fielding, R., Gettys, J., Mogul, J., Frystyk, H.,
Masinter, L., Leach, P., and T. Berners-Lee, "Hypertext
Transfer Protocol -- HTTP/1.1", RFC 2616, June 1999.
[RFC2817] Khare, R. and S. Lawrence, "Upgrading to TLS Within
HTTP/1.1", RFC 2817, May 2000.
[RFC2818] Rescorla, E., "HTTP Over TLS", RFC 2818, May 2000.
[RFC3629] Yergeau, F., "UTF-8, a transformation format of ISO
10646", STD 63, RFC 3629, November 2003.
[RFC3864] Klyne, G., Nottingham, M., and J. Mogul, "Registration
Procedures for Message Header Fields", BCP 90, RFC 3864,
September 2004.
[RFC3986] Berners-Lee, T., Fielding, R., and L. Masinter, "Uniform
Resource Identifier (URI): Generic Syntax", STD 66,
RFC 3986, January 2005.
[RFC3987] Duerst, M. and M. Suignard, "Internationalized Resource
Identifiers (IRIs)", RFC 3987, January 2005.
[RFC4086] Eastlake, D., Schiller, J., and S. Crocker, "Randomness
Requirements for Security", BCP 106, RFC 4086, June 2005.
[RFC4648] Josefsson, S., "The Base16, Base32, and Base64 Data
Encodings", RFC 4648, October 2006.
[RFC5226] Narten, T. and H. Alvestrand, "Guidelines for Writing an
IANA Considerations Section in RFCs", BCP 26, RFC 5226,
May 2008.
[RFC5234] Crocker, D. and P. Overell, "Augmented BNF for Syntax
Specifications: ABNF", STD 68, RFC 5234, January 2008.
[RFC5246] Dierks, T. and E. Rescorla, "The Transport Layer Security
(TLS) Protocol Version 1.2", RFC 5246, August 2008.
[RFC6066] Eastlake, D., "Transport Layer Security (TLS) Extensions:
Extension Definitions", RFC 6066, January 2011.
[RFC6454] Barth, A., "The Web Origin Concept", RFC 6454,
December 2011.
14.2. Informative References ๐ก
[RFC4122] Leach, P., Mealling, M., and R. Salz, "A Universally
Unique IDentifier (UUID) URN Namespace", RFC 4122,
July 2005.
[RFC4270] Hoffman, P. and B. Schneier, "Attacks on Cryptographic
Hashes in Internet Protocols", RFC 4270, November 2005.
[RFC5321] Klensin, J., "Simple Mail Transfer Protocol", RFC 5321,
October 2008.
[RFC6202] Loreto, S., Saint-Andre, P., Salsano, S., and G. Wilkins,
"Known Issues and Best Practices for the Use of Long
Polling and Streaming in Bidirectional HTTP", RFC 6202,
April 2011.
[RFC6265] Barth, A., "HTTP State Management Mechanism", RFC 6265,
April 2011.
[TALKING] Huang, L-S., Chen, E., Barth, A., Rescorla, E., and C.
Jackson, "Talking to Yourself for Fun and Profit", 2010,
<http://w2spconf.com/2011/papers/websocket.pdf>.
[W3C.REC-wsc-ui-20100812]
Roessler, T. and A. Saldhana, "Web Security Context: User
Interface Guidelines", World Wide Web Consortium
Recommendation REC-wsc-ui-20100812, August 2010,
<http://www.w3.org/TR/2010/REC-wsc-ui-20100812/>.
Latest version available at
<http://www.w3.org/TR/wsc-ui/>.
[WSAPI] Hickson, I., "The WebSocket API", W3C Working Draft WD-
websockets-20110929, September 2011,
<http://www.w3.org/TR/2011/WD-websockets-20110929/>.
Latest version available at
<http://www.w3.org/TR/websockets/>.
[XMLHttpRequest]
van Kesteren, A., Ed., "XMLHttpRequest", W3C Candidate
Recommendation CR-XMLHttpRequest-20100803, August 2010,
<http://www.w3.org/TR/2010/CR-XMLHttpRequest-20100803/>.
Latest version available at
<http://www.w3.org/TR/XMLHttpRequest/>.
Authors' Addresses ๐
Ian Fette
Google, Inc.
EMail: ifette+ietf@google.com
URI: http://www.ianfette.com/
Alexey Melnikov
Isode Ltd.
5 Castle Business Village
36 Station Road
Hampton, Middlesex TW12 2BX
UK
EMail: Alexey.Melnikov@isode.com
That's the whole protocol, bestie!! ๐๐ง๐
๐ฆ where this page comes from
- ietf: rfc6455 2011-12
- ๐ girlified by claude-haiku-5-5 on : a language model's rewrite of the original, under its terms