35constexpr std::uint8_t
kText = 0x1;
38constexpr std::uint8_t
kPing = 0x9;
39constexpr std::uint8_t
kPong = 0xa;
42 output->push_back(
static_cast<char>((
value >> 8U) & 0xffU));
43 output->push_back(
static_cast<char>(
value & 0xffU));
47 for (
int shift = 56; shift >= 0; shift -= 8) {
48 output->push_back(
static_cast<char>((
value >> shift) & 0xffU));
68 std::uint32_t word = 0;
69 std::memcpy(&word, mask,
sizeof(word));
71 for (; index +
sizeof(word) <= length; index +=
sizeof(word)) {
72 std::uint32_t chunk = 0;
73 std::memcpy(&chunk,
data + index,
sizeof(chunk));
75 std::memcpy(
data + index, &chunk,
sizeof(chunk));
77 for (; index < length; ++index) {
78 data[index] ^= mask[index % 4];
83 return static_cast<std::uint16_t
>(
84 (
static_cast<std::uint16_t
>(
static_cast<unsigned char>(input[
offset]))
86 static_cast<std::uint16_t
>(
87 static_cast<unsigned char>(input[
offset + 1])));
91 std::uint64_t result = 0;
92 for (
size_t index = 0; index <
sizeof(result); ++index) {
93 result = (result << 8U) | static_cast<unsigned char>(input[
offset + index]);
100 Framing(
Role role,
size_t maximum)
101 : role_(role), max_message_size_(maximum) {}
103 struct ParsedActions {
104 std::vector<std::string> messages;
105 std::vector<std::string> pongs;
106 std::optional<std::string> close;
109 absl::Status ParseFrames(ParsedActions* actions) {
111 while (input_.size() - consumed >= 2) {
112 const auto first =
static_cast<unsigned char>(input_[consumed]);
113 const auto second =
static_cast<unsigned char>(input_[consumed + 1]);
114 const bool final = (first & 0x80U) != 0;
115 const std::uint8_t opcode = first & 0x0fU;
116 if ((first & 0x70U) != 0) {
117 return absl::InvalidArgumentError(
"WebSocket RSV bits are not zero");
119 const bool masked = (second & 0x80U) != 0;
121 return absl::InvalidArgumentError(
123 ?
"WebSocket client frame is not masked"
124 :
"WebSocket server frame is unexpectedly masked");
127 std::uint64_t payload_size = second & 0x7fU;
128 size_t header_size = 2;
129 if (payload_size == 126) {
130 if (input_.size() - consumed < 4) {
135 }
else if (payload_size == 127) {
136 if (input_.size() - consumed < 10) {
140 if ((payload_size & (std::uint64_t{1} << 63U)) != 0) {
141 return absl::InvalidArgumentError(
"WebSocket length is invalid");
145 if (payload_size > max_message_size_ ||
146 payload_size > std::numeric_limits<size_t>::max()) {
147 return absl::OutOfRangeError(
148 "WebSocket frame exceeds max_message_size");
150 const bool control = opcode >=
kClose;
151 if (control && (!
final || payload_size > 125)) {
152 return absl::InvalidArgumentError(
153 "WebSocket control frame must be final and at most 125 bytes");
155 const size_t mask_size = masked ? 4 : 0;
156 const size_t full_size =
157 header_size + mask_size +
static_cast<size_t>(payload_size);
158 if (input_.size() - consumed < full_size) {
162 const size_t mask_offset = consumed + header_size;
163 const size_t payload_offset = mask_offset + mask_size;
166 char mask[4] = {0, 0, 0, 0};
168 std::memcpy(mask, input_.data() + mask_offset,
sizeof(mask));
172 if (consumed == 0 && payload_offset + payload_size == input_.size()) {
177 payload.erase(0, payload_offset);
181 input_.substr(payload_offset,
static_cast<size_t>(payload_size));
189 consumed = taken ? 0 : consumed + full_size;
191 if (opcode ==
kPing) {
192 actions->pongs.push_back(std::move(
payload));
193 }
else if (opcode ==
kPong) {
195 }
else if (opcode ==
kClose) {
197 return absl::InvalidArgumentError(
198 "WebSocket close code is truncated");
200 actions->close = std::move(
payload);
202 }
else if (opcode ==
kText) {
203 return absl::InvalidArgumentError(
204 "A11 binary channel received a text WebSocket frame");
205 }
else if (opcode ==
kBinary) {
206 if (fragment_opcode_.has_value()) {
207 return absl::InvalidArgumentError(
208 "WebSocket data frame interrupted a fragmented message");
211 actions->messages.push_back(std::move(
payload));
213 fragment_opcode_ = opcode;
214 fragmented_ = std::move(
payload);
217 if (!fragment_opcode_.has_value()) {
218 return absl::InvalidArgumentError(
219 "WebSocket continuation has no initial frame");
221 if (fragmented_.size() +
payload.size() > max_message_size_) {
222 return absl::OutOfRangeError(
223 "Fragmented WebSocket message exceeds max_message_size");
227 actions->messages.push_back(std::move(fragmented_));
229 fragment_opcode_.reset();
232 return absl::InvalidArgumentError(
"WebSocket opcode is unsupported");
236 input_.erase(0, consumed);
238 return absl::OkStatus();
241 std::string WriteFrame(std::uint8_t opcode, std::string_view
payload,
242 std::uint32_t masking_key) {
245 frame.reserve(
payload.size() + 14);
246 frame.push_back(
static_cast<char>(0x80U | opcode));
247 const std::uint8_t mask_flag = masked ? 0x80U : 0;
249 frame.push_back(
static_cast<char>(mask_flag |
payload.size()));
250 }
else if (
payload.size() <= std::numeric_limits<std::uint16_t>::max()) {
251 frame.push_back(
static_cast<char>(mask_flag | 126U));
254 frame.push_back(
static_cast<char>(mask_flag | 127U));
258 const auto key = masking_key;
259 const char mask[4] = {
260 static_cast<char>((key >> 24U) & 0xffU),
261 static_cast<char>((key >> 16U) & 0xffU),
262 static_cast<char>((key >> 8U) & 0xffU),
263 static_cast<char>(key & 0xffU),
265 frame.append(mask,
sizeof(mask));
266 const size_t body = frame.size();
276 const size_t max_message_size_;
278 std::optional<std::uint8_t> fragment_opcode_;
279 std::string fragmented_;