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

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: Chunked inflate 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 2014 The Chromium Authors. All rights reserved.
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 const int kInflaterChunkSize = 16 * 1024;
21
22 // Constants for hybi-10 frame format.
23
24 typedef int OpCode;
25
26 const OpCode kOpCodeContinuation = 0x0;
27 const OpCode kOpCodeText = 0x1;
28 const OpCode kOpCodeBinary = 0x2;
29 const OpCode kOpCodeClose = 0x8;
30 const OpCode kOpCodePing = 0x9;
31 const OpCode kOpCodePong = 0xA;
32
33 const unsigned char kFinalBit = 0x80;
34 const unsigned char kReserved1Bit = 0x40;
35 const unsigned char kReserved2Bit = 0x20;
36 const unsigned char kReserved3Bit = 0x10;
37 const unsigned char kOpCodeMask = 0xF;
38 const unsigned char kMaskBit = 0x80;
39 const unsigned char kPayloadLengthMask = 0x7F;
40
41 const size_t kMaxSingleBytePayloadLength = 125;
42 const size_t kTwoBytePayloadLengthField = 126;
43 const size_t kEightBytePayloadLengthField = 127;
44 const size_t kMaskingKeyWidthInBytes = 4;
45
46 WebSocket::ParseResult DecodeFrameHybi17(
47 const base::StringPiece& frame,
48 bool client_frame,
49 int* bytes_consumed,
50 std::string* output,
51 bool* compressed) {
52 size_t data_length = frame.length();
53 if (data_length < 2)
54 return WebSocket::FRAME_INCOMPLETE;
55
56 const char* buffer_begin = const_cast<char*>(frame.data());
57 const char* p = buffer_begin;
58 const char* buffer_end = p + data_length;
59
60 unsigned char first_byte = *p++;
61 unsigned char second_byte = *p++;
62
63 bool final = (first_byte & kFinalBit) != 0;
64 bool reserved1 = (first_byte & kReserved1Bit) != 0;
65 bool reserved2 = (first_byte & kReserved2Bit) != 0;
66 bool reserved3 = (first_byte & kReserved3Bit) != 0;
67 int op_code = first_byte & kOpCodeMask;
68 bool masked = (second_byte & kMaskBit) != 0;
69 *compressed = reserved1;
70 if (!final || reserved2 || reserved3)
71 return WebSocket::FRAME_ERROR; // Only compression extension is supported.
72
73 bool closed = false;
74 switch (op_code) {
75 case kOpCodeClose:
76 closed = true;
77 break;
78 case kOpCodeText:
79 break;
80 case kOpCodeBinary: // We don't support binary frames yet.
81 case kOpCodeContinuation: // We don't support binary frames yet.
82 case kOpCodePing: // We don't support binary frames yet.
83 case kOpCodePong: // We don't support binary frames yet.
84 default:
85 return WebSocket::FRAME_ERROR;
86 }
87
88 if (client_frame && !masked) // In Hybi-17 spec client MUST mask his frame.
89 return WebSocket::FRAME_ERROR;
90
91 uint64 payload_length64 = second_byte & kPayloadLengthMask;
92 if (payload_length64 > kMaxSingleBytePayloadLength) {
93 int extended_payload_length_size;
94 if (payload_length64 == kTwoBytePayloadLengthField)
95 extended_payload_length_size = 2;
96 else {
97 DCHECK(payload_length64 == kEightBytePayloadLengthField);
98 extended_payload_length_size = 8;
99 }
100 if (buffer_end - p < extended_payload_length_size)
101 return WebSocket::FRAME_INCOMPLETE;
102 payload_length64 = 0;
103 for (int i = 0; i < extended_payload_length_size; ++i) {
104 payload_length64 <<= 8;
105 payload_length64 |= static_cast<unsigned char>(*p++);
106 }
107 }
108
109 size_t actual_masking_key_length = masked ? kMaskingKeyWidthInBytes : 0;
110 static const uint64 max_payload_length = 0x7FFFFFFFFFFFFFFFull;
111 static size_t max_length = std::numeric_limits<size_t>::max();
112 if (payload_length64 > max_payload_length ||
113 payload_length64 + actual_masking_key_length > max_length) {
114 // WebSocket frame length too large.
115 return WebSocket::FRAME_ERROR;
116 }
117 size_t payload_length = static_cast<size_t>(payload_length64);
118
119 size_t total_length = actual_masking_key_length + payload_length;
120 if (static_cast<size_t>(buffer_end - p) < total_length)
121 return WebSocket::FRAME_INCOMPLETE;
122
123 if (masked) {
124 output->resize(payload_length);
125 const char* masking_key = p;
126 char* payload = const_cast<char*>(p + kMaskingKeyWidthInBytes);
127 for (size_t i = 0; i < payload_length; ++i) // Unmask the payload.
128 (*output)[i] = payload[i] ^ masking_key[i % kMaskingKeyWidthInBytes];
129 } else {
130 output->assign(p, p + payload_length);
131 }
132
133 size_t pos = p + actual_masking_key_length + payload_length - buffer_begin;
134 *bytes_consumed = pos;
135 return closed ? WebSocket::FRAME_CLOSE : WebSocket::FRAME_OK;
136 }
137
138 void EncodeFrameHybi17(const std::string& message,
139 int masking_key,
140 bool compressed,
141 std::string* output) {
142 std::vector<char> frame;
143 OpCode op_code = kOpCodeText;
144 size_t data_length = message.length();
145
146 int reserved1 = compressed ? kReserved1Bit : 0;
147 frame.push_back(kFinalBit | op_code | reserved1);
148 char mask_key_bit = masking_key != 0 ? kMaskBit : 0;
149 if (data_length <= kMaxSingleBytePayloadLength)
150 frame.push_back(data_length | mask_key_bit);
151 else if (data_length <= 0xFFFF) {
152 frame.push_back(kTwoBytePayloadLengthField | mask_key_bit);
153 frame.push_back((data_length & 0xFF00) >> 8);
154 frame.push_back(data_length & 0xFF);
155 } else {
156 frame.push_back(kEightBytePayloadLengthField | mask_key_bit);
157 char extended_payload_length[8];
158 size_t remaining = data_length;
159 // Fill the length into extended_payload_length in the network byte order.
160 for (int i = 0; i < 8; ++i) {
161 extended_payload_length[7 - i] = remaining & 0xFF;
162 remaining >>= 8;
163 }
164 frame.insert(frame.end(),
165 extended_payload_length,
166 extended_payload_length + 8);
167 DCHECK(!remaining);
168 }
169
170 const char* data = const_cast<char*>(message.data());
171 if (masking_key != 0) {
172 const char* mask_bytes = reinterpret_cast<char*>(&masking_key);
173 frame.insert(frame.end(), mask_bytes, mask_bytes + 4);
174 for (size_t i = 0; i < data_length; ++i) // Mask the payload.
175 frame.push_back(data[i] ^ mask_bytes[i % kMaskingKeyWidthInBytes]);
176 } else {
177 frame.insert(frame.end(), data, data + data_length);
178 }
179 *output = std::string(&frame[0], frame.size());
180 }
181
182 } // anonymous namespace
183
184 //static
185 WebSocketEncoder* WebSocketEncoder::CreateServer(
186 const std::string& request_extensions,
187 std::string* response_extensions) {
188 bool deflate;
189 int client_window_bits;
190 int server_window_bits;
191 ParseExtensions(request_extensions,
192 &deflate, &client_window_bits, &server_window_bits);
193
194 if (deflate) {
195 *response_extensions = base::StringPrintf(
196 "permessage-deflate; server_max_window_bits=%d; "
197 "client_max_window_bits=%d",
198 server_window_bits,
199 client_window_bits);
200 return new WebSocketEncoder(
201 true /* is_server */, server_window_bits, client_window_bits);
202 } else {
203 *response_extensions = std::string();
204 return new WebSocketEncoder(true /* is_server */);
205 }
206 }
207
208 //static
209 WebSocketEncoder* WebSocketEncoder::CreateClient(
210 const std::string& response_extensions) {
211 bool deflate;
212 int client_window_bits;
213 int server_window_bits;
214 ParseExtensions(response_extensions,
215 &deflate, &client_window_bits, &server_window_bits);
216
217 if (deflate) {
218 return new WebSocketEncoder(
219 false /* is_server */, client_window_bits, server_window_bits);
220 } else {
221 return new WebSocketEncoder(false /* is_server */);
222 }
223 }
224
225 //static
226 void WebSocketEncoder::ParseExtensions(
227 const std::string& extensions,
228 bool *deflate, int *client_window_bits, int *server_window_bits) {
229 *deflate = false;
230 *client_window_bits = 15;
231 *server_window_bits = 15;
232
233 if (extensions.empty())
234 return;
235
236 WebSocketExtensionParser parser;
vkuzkokov 2014/12/03 18:34:48 Comment in WebSocketExtensionParser says "This par
dgozman 2014/12/03 21:34:56 There is only one extension now. Let's fix the pro
dgozman 2014/12/04 15:17:44 Added TODO.
237 parser.Parse(extensions);
238 if (parser.has_error())
239 return;
240 if (parser.extension().name() != "permessage-deflate")
241 return;
242
243 const std::vector<WebSocketExtension::Parameter>& parameters =
244 parser.extension().parameters();
245 for (const auto& param : parameters) {
246 const std::string& name = param.name();
247 if (name == "client_max_window_bits" && param.HasValue()) {
248 int bits = 0;
249 if (base::StringToInt(param.value(), &bits) && bits >= 8 && bits <= 15)
250 *client_window_bits = bits;
251 }
252 if (name == "server_max_window_bits" && param.HasValue()) {
253 int bits = 0;
254 if (base::StringToInt(param.value(), &bits) && bits >= 8 && bits <= 15)
255 *server_window_bits = bits;
256 }
257 }
258 *deflate = true;
259 }
260
261 WebSocketEncoder::WebSocketEncoder(bool is_server)
262 : is_server_(is_server) {
263 }
264
265 WebSocketEncoder::WebSocketEncoder(
266 bool is_server, int deflate_bits, int inflate_bits)
267 : is_server_(is_server) {
268 deflater_.reset(
269 new WebSocketDeflater(WebSocketDeflater::TAKE_OVER_CONTEXT));
270 deflater_->Initialize(deflate_bits);
271
272 inflater_.reset(
273 new WebSocketInflater(kInflaterChunkSize, kInflaterChunkSize));
274 inflater_->Initialize(inflate_bits);
275 }
276
277 WebSocketEncoder::~WebSocketEncoder() {
278 }
279
280 WebSocket::ParseResult WebSocketEncoder::DecodeFrame(
281 const base::StringPiece& frame,
282 int* bytes_consumed,
283 std::string* output) {
284 bool compressed;
285 WebSocket::ParseResult result = DecodeFrameHybi17(
286 frame, is_server_, bytes_consumed, output, &compressed);
287 if (result == WebSocket::FRAME_OK && compressed)
288 result = Inflate(output);
289 return result;
290 }
291
292 void WebSocketEncoder::EncodeFrame(
293 const std::string& frame,
294 int masking_key,
295 std::string* output) {
296 std::string compressed;
297 WebSocket::ParseResult result = Deflate(frame, &compressed);
298 if (result == WebSocket::FRAME_OK)
299 EncodeFrameHybi17(compressed, masking_key, true, output);
300 else
301 EncodeFrameHybi17(frame, masking_key, false, output);
pfeldman 2014/12/04 14:17:27 I wonder if this should fail in case deflate faile
dgozman 2014/12/04 15:17:44 Not really. We can send non-compressed frame just
302 }
303
304 WebSocket::ParseResult WebSocketEncoder::Inflate(std::string* message) {
pfeldman 2014/12/04 14:17:27 Should be boolean - it is not about source frame p
dgozman 2014/12/04 15:17:44 Done.
305 if (!inflater_)
306 return WebSocket::FRAME_ERROR;
307
308 const char* data = message->data();
309 int length = message->length();
310 while (length > 0) {
311 int length_to_add = std::min(length, kInflaterChunkSize);
312 if (!inflater_->AddBytes(data, length_to_add))
313 return WebSocket::FRAME_ERROR;
314 data += length_to_add;
315 length -= length_to_add;
316 }
317
318 if (!inflater_->Finish())
319 return WebSocket::FRAME_ERROR;
320
321 std::vector<char> output;
322 while (inflater_->CurrentOutputSize() > 0) {
323 scoped_refptr<IOBufferWithSize> chunk =
324 inflater_->GetOutput(inflater_->CurrentOutputSize());
325 if (!chunk.get())
326 return WebSocket::FRAME_ERROR;
327 output.insert(output.end(), chunk->data(), chunk->data() + chunk->size());
328 }
329
330 *message = std::string(&output[0], output.size());
331 return WebSocket::FRAME_OK;
332 }
333
334 WebSocket::ParseResult WebSocketEncoder::Deflate(
pfeldman 2014/12/04 14:17:27 ditto
dgozman 2014/12/04 15:17:44 Done.
335 const std::string& message, std::string* output) {
336 if (!deflater_)
337 return WebSocket::FRAME_ERROR;
338 if (!deflater_->AddBytes(message.data(), message.length())) {
339 deflater_->Finish();
340 return WebSocket::FRAME_ERROR;
341 }
342 if (!deflater_->Finish())
343 return WebSocket::FRAME_ERROR;
344 scoped_refptr<IOBufferWithSize> buffer =
345 deflater_->GetOutput(deflater_->CurrentOutputSize());
346 if (!buffer.get())
347 return WebSocket::FRAME_ERROR;
348 *output = std::string(buffer->data(), buffer->size());
349 return WebSocket::FRAME_OK;
350 }
351
352 } // 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