Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(153)

Side by Side Diff: net/server/web_socket_encoder.cc

Issue 769423005: Support WebSocket per-message deflate extension in http server. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Created 6 years ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « net/server/web_socket_encoder.h ('k') | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
1 // Copyright (c) 2014 The Chromium Authors. All rights reserved.
mmenke 2014/12/03 15:38:06 nit: Don't use (c) in new files.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "net/server/web_socket_encoder.h"
6
7 #include "base/logging.h"
8 #include "base/strings/string_number_conversions.h"
9 #include "base/strings/stringprintf.h"
10 #include "net/base/io_buffer.h"
11 #include "net/websockets/websocket_extension_parser.h"
12
13 namespace net {
14
15 const char WebSocketEncoder::kClientExtensions[] =
16 "Sec-WebSocket-Extensions: permessage-deflate; client_max_window_bits";
17
18 namespace {
19
20 // Constants for hybi-10 frame format.
21
22 typedef int OpCode;
23
24 const OpCode kOpCodeContinuation = 0x0;
25 const OpCode kOpCodeText = 0x1;
26 const OpCode kOpCodeBinary = 0x2;
27 const OpCode kOpCodeClose = 0x8;
28 const OpCode kOpCodePing = 0x9;
29 const OpCode kOpCodePong = 0xA;
30
31 const unsigned char kFinalBit = 0x80;
32 const unsigned char kReserved1Bit = 0x40;
33 const unsigned char kReserved2Bit = 0x20;
34 const unsigned char kReserved3Bit = 0x10;
35 const unsigned char kOpCodeMask = 0xF;
36 const unsigned char kMaskBit = 0x80;
37 const unsigned char kPayloadLengthMask = 0x7F;
38
39 const size_t kMaxSingleBytePayloadLength = 125;
40 const size_t kTwoBytePayloadLengthField = 126;
41 const size_t kEightBytePayloadLengthField = 127;
42 const size_t kMaskingKeyWidthInBytes = 4;
43
44 WebSocket::ParseResult DecodeFrameHybi17(
45 const base::StringPiece& frame,
46 bool client_frame,
47 int* bytes_consumed,
48 std::string* output,
49 bool* compressed) {
50 size_t data_length = frame.length();
51 if (data_length < 2)
52 return WebSocket::FRAME_INCOMPLETE;
53
54 const char* buffer_begin = const_cast<char*>(frame.data());
55 const char* p = buffer_begin;
56 const char* buffer_end = p + data_length;
57
58 unsigned char first_byte = *p++;
59 unsigned char second_byte = *p++;
60
61 bool final = (first_byte & kFinalBit) != 0;
62 bool reserved1 = (first_byte & kReserved1Bit) != 0;
63 bool reserved2 = (first_byte & kReserved2Bit) != 0;
64 bool reserved3 = (first_byte & kReserved3Bit) != 0;
65 int op_code = first_byte & kOpCodeMask;
66 bool masked = (second_byte & kMaskBit) != 0;
67 *compressed = reserved1;
68 if (!final || reserved2 || reserved3)
69 return WebSocket::FRAME_ERROR; // Only compression extension is supported.
70
71 bool closed = false;
72 switch (op_code) {
73 case kOpCodeClose:
74 closed = true;
75 break;
76 case kOpCodeText:
77 break;
78 case kOpCodeBinary: // We don't support binary frames yet.
79 case kOpCodeContinuation: // We don't support binary frames yet.
80 case kOpCodePing: // We don't support binary frames yet.
81 case kOpCodePong: // We don't support binary frames yet.
82 default:
83 return WebSocket::FRAME_ERROR;
84 }
85
86 if (client_frame && !masked) // In Hybi-17 spec client MUST mask his frame.
87 return WebSocket::FRAME_ERROR;
88
89 uint64 payload_length64 = second_byte & kPayloadLengthMask;
90 if (payload_length64 > kMaxSingleBytePayloadLength) {
91 int extended_payload_length_size;
92 if (payload_length64 == kTwoBytePayloadLengthField)
93 extended_payload_length_size = 2;
94 else {
95 DCHECK(payload_length64 == kEightBytePayloadLengthField);
96 extended_payload_length_size = 8;
97 }
98 if (buffer_end - p < extended_payload_length_size)
99 return WebSocket::FRAME_INCOMPLETE;
100 payload_length64 = 0;
101 for (int i = 0; i < extended_payload_length_size; ++i) {
102 payload_length64 <<= 8;
103 payload_length64 |= static_cast<unsigned char>(*p++);
104 }
105 }
106
107 size_t actual_masking_key_length = masked ? kMaskingKeyWidthInBytes : 0;
108 static const uint64 max_payload_length = 0x7FFFFFFFFFFFFFFFull;
109 static size_t max_length = std::numeric_limits<size_t>::max();
110 if (payload_length64 > max_payload_length ||
111 payload_length64 + actual_masking_key_length > max_length) {
112 // WebSocket frame length too large.
113 return WebSocket::FRAME_ERROR;
114 }
115 size_t payload_length = static_cast<size_t>(payload_length64);
116
117 size_t total_length = actual_masking_key_length + payload_length;
118 if (static_cast<size_t>(buffer_end - p) < total_length)
119 return WebSocket::FRAME_INCOMPLETE;
120
121 if (masked) {
122 output->resize(payload_length);
123 const char* masking_key = p;
124 char* payload = const_cast<char*>(p + kMaskingKeyWidthInBytes);
125 for (size_t i = 0; i < payload_length; ++i) // Unmask the payload.
126 (*output)[i] = payload[i] ^ masking_key[i % kMaskingKeyWidthInBytes];
127 } else {
128 output->assign(p, p + payload_length);
129 }
130
131 size_t pos = p + actual_masking_key_length + payload_length - buffer_begin;
132 *bytes_consumed = pos;
133 return closed ? WebSocket::FRAME_CLOSE : WebSocket::FRAME_OK;
134 }
135
136 void EncodeFrameHybi17(const std::string& message,
137 int masking_key,
138 bool compressed,
139 std::string* output) {
140 std::vector<char> frame;
141 OpCode op_code = kOpCodeText;
142 size_t data_length = message.length();
143
144 int reserved1 = compressed ? kReserved1Bit : 0;
145 frame.push_back(kFinalBit | op_code | reserved1);
146 char mask_key_bit = masking_key != 0 ? kMaskBit : 0;
147 if (data_length <= kMaxSingleBytePayloadLength)
148 frame.push_back(data_length | mask_key_bit);
149 else if (data_length <= 0xFFFF) {
150 frame.push_back(kTwoBytePayloadLengthField | mask_key_bit);
151 frame.push_back((data_length & 0xFF00) >> 8);
152 frame.push_back(data_length & 0xFF);
153 } else {
154 frame.push_back(kEightBytePayloadLengthField | mask_key_bit);
155 char extended_payload_length[8];
156 size_t remaining = data_length;
157 // Fill the length into extended_payload_length in the network byte order.
158 for (int i = 0; i < 8; ++i) {
159 extended_payload_length[7 - i] = remaining & 0xFF;
160 remaining >>= 8;
161 }
162 frame.insert(frame.end(),
163 extended_payload_length,
164 extended_payload_length + 8);
165 DCHECK(!remaining);
166 }
167
168 const char* data = const_cast<char*>(message.data());
169 if (masking_key != 0) {
170 const char* mask_bytes = reinterpret_cast<char*>(&masking_key);
171 frame.insert(frame.end(), mask_bytes, mask_bytes + 4);
172 for (size_t i = 0; i < data_length; ++i) // Mask the payload.
173 frame.push_back(data[i] ^ mask_bytes[i % kMaskingKeyWidthInBytes]);
174 } else {
175 frame.insert(frame.end(), data, data + data_length);
176 }
177 *output = std::string(&frame[0], frame.size());
178 }
179
180 } // anonymous namespace
181
182 //static
183 WebSocketEncoder* WebSocketEncoder::CreateServer(
184 const std::string& request_extensions,
185 std::string* response_extensions) {
186 bool deflate;
187 int client_window_bits;
188 int server_window_bits;
189 ParseExtensions(request_extensions,
190 &deflate, &client_window_bits, &server_window_bits);
191
192 if (deflate) {
193 *response_extensions = base::StringPrintf(
194 "permessage-deflate; server_max_window_bits=%d; "
195 "client_max_window_bits=%d",
196 server_window_bits,
197 client_window_bits);
198 return new WebSocketEncoder(
199 true /* is_server */, server_window_bits, client_window_bits);
200 } else {
201 *response_extensions = std::string();
202 return new WebSocketEncoder(true /* is_server */);
203 }
204 }
205
206 //static
207 WebSocketEncoder* WebSocketEncoder::CreateClient(
208 const std::string& response_extensions) {
209 bool deflate;
210 int client_window_bits;
211 int server_window_bits;
212 ParseExtensions(response_extensions,
213 &deflate, &client_window_bits, &server_window_bits);
214
215 if (deflate) {
216 return new WebSocketEncoder(
217 false /* is_server */, client_window_bits, server_window_bits);
218 } else {
219 return new WebSocketEncoder(false /* is_server */);
220 }
221 }
222
223 //static
224 void WebSocketEncoder::ParseExtensions(
225 const std::string& extensions,
226 bool *deflate, int *client_window_bits, int *server_window_bits) {
227 *deflate = false;
228 *client_window_bits = 15;
229 *server_window_bits = 15;
230
231 if (extensions.empty())
232 return;
233
234 WebSocketExtensionParser parser;
235 parser.Parse(extensions);
236 if (parser.has_error())
237 return;
238 if (parser.extension().name() != "permessage-deflate")
239 return;
240
241 const std::vector<WebSocketExtension::Parameter>& parameters =
242 parser.extension().parameters();
243 for (const auto& param : parameters) {
244 const std::string& name = param.name();
245 if (name == "client_max_window_bits" && param.HasValue()) {
246 int bits = 0;
247 if (base::StringToInt(param.value(), &bits) && bits >= 8 && bits <= 15)
248 *client_window_bits = bits;
249 }
250 if (name == "server_max_window_bits" && param.HasValue()) {
251 int bits = 0;
252 if (base::StringToInt(param.value(), &bits) && bits >= 8 && bits <= 15)
253 *server_window_bits = bits;
254 }
255 }
256 *deflate = true;
257 }
258
259 WebSocketEncoder::WebSocketEncoder(bool is_server)
260 : is_server_(is_server) {
261 }
262
263 WebSocketEncoder::WebSocketEncoder(
264 bool is_server, int deflate_bits, int inflate_bits)
265 : is_server_(is_server) {
266 deflater_.reset(
267 new WebSocketDeflater(WebSocketDeflater::TAKE_OVER_CONTEXT));
268 deflater_->Initialize(deflate_bits);
269
270 inflater_.reset(new WebSocketInflater(4 * 1024, 4 * 1024));
271 inflater_->Initialize(inflate_bits);
272 }
273
274 WebSocketEncoder::~WebSocketEncoder() {
275 }
276
277 WebSocket::ParseResult WebSocketEncoder::DecodeFrame(
278 const base::StringPiece& frame,
279 int* bytes_consumed,
280 std::string* output) {
281 bool compressed;
282 WebSocket::ParseResult result = DecodeFrameHybi17(
283 frame, is_server_, bytes_consumed, output, &compressed);
284 if (result == WebSocket::FRAME_OK && compressed)
285 result = Inflate(output);
286 return result;
287 }
288
289 void WebSocketEncoder::EncodeFrame(
290 const std::string& frame,
291 int masking_key,
292 std::string* output) {
293 std::string compressed;
294 WebSocket::ParseResult result = Deflate(frame, &compressed);
295 if (result == WebSocket::FRAME_OK)
296 EncodeFrameHybi17(compressed, masking_key, true, output);
297 else
298 EncodeFrameHybi17(frame, masking_key, false, output);
299 }
300
301 WebSocket::ParseResult WebSocketEncoder::Inflate(std::string* message) {
302 if (!inflater_)
303 return WebSocket::FRAME_ERROR;
304 if (!inflater_->AddBytes(message->data(), message->length())) {
305 inflater_->Finish();
306 return WebSocket::FRAME_ERROR;
307 }
308 if (!inflater_->Finish())
309 return WebSocket::FRAME_ERROR;
310 scoped_refptr<IOBufferWithSize> buffer =
311 inflater_->GetOutput(inflater_->CurrentOutputSize());
312 if (!buffer.get())
313 return WebSocket::FRAME_ERROR;
314 *message = std::string(buffer->data(), buffer->size());
315 return WebSocket::FRAME_OK;
316 }
317
318 WebSocket::ParseResult WebSocketEncoder::Deflate(
319 const std::string& message, std::string* output) {
320 if (!deflater_)
321 return WebSocket::FRAME_ERROR;
322 if (!deflater_->AddBytes(message.data(), message.length())) {
323 deflater_->Finish();
324 return WebSocket::FRAME_ERROR;
325 }
326 if (!deflater_->Finish())
327 return WebSocket::FRAME_ERROR;
328 scoped_refptr<IOBufferWithSize> buffer =
329 deflater_->GetOutput(deflater_->CurrentOutputSize());
330 if (!buffer.get())
331 return WebSocket::FRAME_ERROR;
332 *output = std::string(buffer->data(), buffer->size());
333 return WebSocket::FRAME_OK;
334 }
335
336 } // namespace net
OLDNEW
« no previous file with comments | « net/server/web_socket_encoder.h ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698