• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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