1 // Copyright 2012 The Chromium Authors 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 #ifndef NET_WEBSOCKETS_WEBSOCKET_FRAME_H_ 6 #define NET_WEBSOCKETS_WEBSOCKET_FRAME_H_ 7 8 #include <stddef.h> 9 #include <stdint.h> 10 11 #include <array> 12 #include <memory> 13 #include <vector> 14 15 #include "base/containers/span.h" 16 #include "base/memory/raw_ptr_exclusion.h" 17 #include "base/memory/raw_span.h" 18 #include "base/memory/scoped_refptr.h" 19 #include "net/base/net_export.h" 20 21 namespace net { 22 23 // Represents a WebSocket frame header. 24 // 25 // Members of this class correspond to each element in WebSocket frame header 26 // (see http://tools.ietf.org/html/rfc6455#section-5.2). 27 struct NET_EXPORT WebSocketFrameHeader { 28 typedef int OpCode; 29 30 // Originally these constants were static const int, but to make it possible 31 // to use them in a switch statement they were changed to an enum. 32 enum OpCodeEnum { 33 kOpCodeContinuation = 0x0, 34 kOpCodeText = 0x1, 35 kOpCodeBinary = 0x2, 36 kOpCodeDataUnused3 = 0x3, 37 kOpCodeDataUnused4 = 0x4, 38 kOpCodeDataUnused5 = 0x5, 39 kOpCodeDataUnused6 = 0x6, 40 kOpCodeDataUnused7 = 0x7, 41 kOpCodeClose = 0x8, 42 kOpCodePing = 0x9, 43 kOpCodePong = 0xA, 44 kOpCodeControlUnusedB = 0xB, 45 kOpCodeControlUnusedC = 0xC, 46 kOpCodeControlUnusedD = 0xD, 47 kOpCodeControlUnusedE = 0xE, 48 kOpCodeControlUnusedF = 0xF, 49 }; 50 51 // Return true if |opcode| is one of the data opcodes known to this 52 // implementation. IsKnownDataOpCodeWebSocketFrameHeader53 static bool IsKnownDataOpCode(OpCode opcode) { 54 return opcode == kOpCodeContinuation || opcode == kOpCodeText || 55 opcode == kOpCodeBinary; 56 } 57 58 // Return true if |opcode| is one of the control opcodes known to this 59 // implementation. IsKnownControlOpCodeWebSocketFrameHeader60 static bool IsKnownControlOpCode(OpCode opcode) { 61 return opcode == kOpCodeClose || opcode == kOpCodePing || 62 opcode == kOpCodePong; 63 } 64 65 // Return true if |opcode| is one of the reserved data opcodes. IsReservedDataOpCodeWebSocketFrameHeader66 static bool IsReservedDataOpCode(OpCode opcode) { 67 return opcode == kOpCodeDataUnused3 || opcode == kOpCodeDataUnused4 || 68 opcode == kOpCodeDataUnused5 || opcode == kOpCodeDataUnused6 || 69 opcode == kOpCodeDataUnused7; 70 } 71 72 // Return true if |opcode| is one of the reserved control opcodes. IsReservedControlOpCodeWebSocketFrameHeader73 static bool IsReservedControlOpCode(OpCode opcode) { 74 return opcode == kOpCodeControlUnusedB || opcode == kOpCodeControlUnusedC || 75 opcode == kOpCodeControlUnusedD || opcode == kOpCodeControlUnusedE || 76 opcode == kOpCodeControlUnusedF; 77 } 78 79 // These values must be compile-time constants. 80 static constexpr size_t kBaseHeaderSize = 2; 81 static constexpr size_t kMaximumExtendedLengthSize = 8; 82 static constexpr size_t kMaskingKeyLength = 4; 83 84 // Contains four-byte data representing "masking key" of WebSocket frames. 85 struct WebSocketMaskingKey { 86 std::array<uint8_t, WebSocketFrameHeader::kMaskingKeyLength> key; 87 }; 88 89 // Constructor to avoid a lot of repetitive initialisation. WebSocketFrameHeaderWebSocketFrameHeader90 explicit WebSocketFrameHeader(OpCode opCode) : opcode(opCode) {} 91 92 WebSocketFrameHeader(const WebSocketFrameHeader&) = delete; 93 WebSocketFrameHeader& operator=(const WebSocketFrameHeader&) = delete; 94 95 // Create a clone of this object on the heap. 96 std::unique_ptr<WebSocketFrameHeader> Clone() const; 97 98 // Overwrite this object with the fields from |source|. 99 void CopyFrom(const WebSocketFrameHeader& source); 100 101 // Members below correspond to each item in WebSocket frame header. 102 // See <http://tools.ietf.org/html/rfc6455#section-5.2> for details. 103 bool final = false; 104 bool reserved1 = false; 105 bool reserved2 = false; 106 bool reserved3 = false; 107 OpCode opcode; 108 bool masked = false; 109 WebSocketMaskingKey masking_key = {}; 110 uint64_t payload_length = 0; 111 }; 112 113 // Contains an entire WebSocket frame including payload. This is used by APIs 114 // that are not concerned about retaining the original frame boundaries (because 115 // frames may need to be split in order for the data to fit in memory). 116 struct NET_EXPORT_PRIVATE WebSocketFrame { 117 // A frame must always have an opcode, so this parameter is compulsory. 118 explicit WebSocketFrame(WebSocketFrameHeader::OpCode opcode); 119 120 WebSocketFrame(const WebSocketFrame&) = delete; 121 WebSocketFrame& operator=(const WebSocketFrame&) = delete; 122 123 ~WebSocketFrame(); 124 125 // |header| is always present. 126 WebSocketFrameHeader header; 127 128 // |payload| is always unmasked even if the frame is masked. 129 // The lifetime of |payload| is not defined by WebSocketFrameChunk. It is the 130 // responsibility of the creator to ensure it remains valid for the lifetime 131 // of this object. This should be documented in the code that creates this 132 // object. 133 // TODO(crbug.com/40646382): Find more better way to clarify the life cycle. 134 // TODO(crbug.com/377222393): Remove `DanglingUntriaged`. 135 base::raw_span<const uint8_t, DanglingUntriaged> payload; 136 }; 137 138 // Structure describing one chunk of a WebSocket frame. 139 // 140 // The payload of a WebSocket frame may be divided into multiple chunks. 141 // You need to look at |final_chunk| member variable to detect the end of a 142 // series of chunk objects of a WebSocket frame. 143 // 144 // Frame dissection is necessary to handle frames that are too large to store in 145 // the browser memory without losing information about the frame boundaries. In 146 // practice, most code does not need to worry about the original frame 147 // boundaries and can use the WebSocketFrame type declared above. 148 // 149 // Users of this struct should treat WebSocket frames as a data stream; it's 150 // important to keep the frame data flowing, especially in the browser process. 151 // Users should not let the data stuck somewhere in the pipeline. 152 // 153 // This struct is used for reading WebSocket frame data (created by 154 // WebSocketFrameParser). To construct WebSocket frames, use functions below. 155 struct NET_EXPORT WebSocketFrameChunk { 156 WebSocketFrameChunk(); 157 158 WebSocketFrameChunk(const WebSocketFrameChunk&) = delete; 159 WebSocketFrameChunk& operator=(const WebSocketFrameChunk&) = delete; 160 161 ~WebSocketFrameChunk(); 162 163 // Non-null |header| is provided only if this chunk is the first part of 164 // a series of chunks. 165 std::unique_ptr<WebSocketFrameHeader> header; 166 167 // Indicates this part is the last chunk of a frame. 168 bool final_chunk = false; 169 170 // |payload| is always unmasked even if the frame is masked. |payload| might 171 // be empty in the first chunk. 172 // The lifetime of |payload| is not defined by WebSocketFrameChunk. It is the 173 // responsibility of the creator to ensure it remains valid for the lifetime 174 // of this object. This should be documented in the code that creates this 175 // object. 176 // TODO(crbug.com/40646382): Find more better way to clarify the life cycle. 177 // Using RAW_PTR_EXCLUSION here temporarily to prevent dangling pointer 178 // issues. 179 RAW_PTR_EXCLUSION base::span<char> payload; 180 }; 181 182 using WebSocketMaskingKey = WebSocketFrameHeader::WebSocketMaskingKey; 183 184 // Returns the size of WebSocket frame header. The size of WebSocket frame 185 // header varies from 2 bytes to 14 bytes depending on the payload length 186 // and maskedness. 187 NET_EXPORT size_t 188 GetWebSocketFrameHeaderSize(const WebSocketFrameHeader& header); 189 190 // Writes wire format of a WebSocket frame header into |output|, and returns 191 // the number of bytes written. 192 // 193 // WebSocket frame format is defined at: 194 // <http://tools.ietf.org/html/rfc6455#section-5.2>. This function writes 195 // everything but payload data in a WebSocket frame to |buffer|. 196 // 197 // If |header->masked| is true, |masking_key| must point to a valid 198 // WebSocketMaskingKey object containing the masking key for that frame 199 // (possibly generated by GenerateWebSocketMaskingKey() function below). 200 // Otherwise, |masking_key| must be NULL. 201 // 202 // |buffer| should have enough size to contain the frame header. 203 // GetWebSocketFrameHeaderSize() can be used to know the size of header 204 // beforehand. If the size of |buffer| is insufficient, this function returns 205 // ERR_INVALID_ARGUMENT and does not write any data to |buffer|. 206 NET_EXPORT int WriteWebSocketFrameHeader(const WebSocketFrameHeader& header, 207 const WebSocketMaskingKey* masking_key, 208 base::span<uint8_t> buffer); 209 210 // Generates a masking key suitable for use in a new WebSocket frame. 211 NET_EXPORT WebSocketMaskingKey GenerateWebSocketMaskingKey(); 212 213 // Masks WebSocket frame payload. 214 // 215 // A client must mask every WebSocket frame by XOR'ing the frame payload 216 // with four-byte random data (masking key). This function applies the masking 217 // to the given payload data. 218 // 219 // This function masks |data| with |masking_key|, assuming |data| is partial 220 // data starting from |frame_offset| bytes from the beginning of the payload 221 // data. 222 // 223 // Since frame masking is a reversible operation, this function can also be 224 // used for unmasking a WebSocket frame. 225 NET_EXPORT void MaskWebSocketFramePayload( 226 const WebSocketMaskingKey& masking_key, 227 uint64_t frame_offset, 228 base::span<uint8_t> data); 229 230 // Close frame parsing result structure. 231 struct ParseCloseFrameResult { 232 // Status code (always present). Set to `kWebSocketErrorNoStatusReceived` if 233 // no code was included in the close frame. 234 uint16_t code; 235 236 // Reason text (can be empty, but always present). 237 std::string_view reason; 238 239 // Error message if a protocol error occurred during parsing. 240 // This is optional and, when set, points to static storage. 241 std::optional<std::string_view> error = std::nullopt; 242 243 ParseCloseFrameResult(uint16_t close_code, 244 std::string_view close_reason, 245 std::optional<std::string_view> error = std::nullopt) codeParseCloseFrameResult246 : code(close_code), reason(close_reason), error(error) {} 247 }; 248 249 // Parses a WebSocket Close frame, extracts the status code and reason. 250 NET_EXPORT ParseCloseFrameResult 251 ParseCloseFrame(base::span<const char> payload); 252 253 } // namespace net 254 255 #endif // NET_WEBSOCKETS_WEBSOCKET_FRAME_H_ 256