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

Side by Side Diff: net/websockets/websocket_channel.cc

Issue 12764006: WebSocketChannel implementation (Closed) Base URL: http://git.chromium.org/chromium/src.git@web_socket_dispatcher
Patch Set: Add unit tests and fix bugs. Created 7 years, 5 months 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
OLDNEW
(Empty)
1 // Copyright 2013 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/websockets/websocket_channel.h"
6
7 #include <algorithm>
8
9 #include "base/basictypes.h" // for size_t
10 #include "base/bind.h"
11 #include "base/string_util.h"
12 #include "net/base/io_buffer.h"
13 #include "net/http/http_request_info.h"
14 #include "net/http/http_stream_factory.h"
15 #include "net/ssl/ssl_config_service.h"
16 #include "net/websockets/websocket_errors.h"
17 #include "net/websockets/websocket_event_interface.h"
18 #include "net/websockets/websocket_frame.h"
19 #include "net/websockets/websocket_mux.h"
20 #include "net/websockets/websocket_stream.h"
21
22 namespace net {
23
24 namespace {
25
26 const int kDefaultSendQuotaLowWaterMark = 1 << 16;
27 const int kDefaultSendQuotaHighWaterMark = 1 << 17;
28 const size_t kWebSocketErrorNetworkByteLength = 2;
tyoshino (SeeGerritForStatus) 2013/06/26 09:27:06 It's not always error. And the length of the field
Adam Rice 2013/06/27 17:29:49 Done.
29
30 union NetworkErrorCode {
31 char code_as_char[kWebSocketErrorNetworkByteLength];
32 unsigned short code_as_network_short;
33 };
34
35 // A subclass of IOBufferWithSize formed by concatenating two other subclasses
36 // of IOBufferWithSize.
37 class ConcatenatedIOBufferWithSize : public IOBufferWithSize {
38 public:
39 ConcatenatedIOBufferWithSize(const IOBufferWithSize* part1,
40 const IOBufferWithSize* part2)
tyoshino (SeeGerritForStatus) 2013/06/26 09:40:17 you don't need a new class. you can just have a ut
Adam Rice 2013/06/27 17:29:49 I must have been confused when I wrote this. Chang
41 : IOBufferWithSize(part1->size() + part2->size()) {
42 data_ = new char[size_];
43 std::copy(GetConstData(part1), GetConstData(part1) + part1->size(), data_);
44 std::copy(GetConstData(part2),
45 GetConstData(part2) + part2->size(),
46 data_ + part1->size());
47 }
48
49 private:
50 // We take const pointers as arguments, but IOBuffer doesn't actually have a
51 // const accessor for its data member. So we implement our own.
52 static const char* GetConstData(const IOBufferWithSize* iobuffer) {
53 return const_cast<IOBufferWithSize*>(iobuffer)->data();
54 }
55 };
56
57 } // namespace
58
59 struct WebSocketChannel::SendBuffer {
60 SendBuffer() : frames(), total_bytes(0) {}
61 ScopedVector<WebSocketFrameChunk> frames;
62 size_t total_bytes;
63 };
64
65 WebSocketChannel::WebSocketChannel(
66 const GURL& socket_url,
67 scoped_ptr<WebSocketEventInterface> event_interface)
68 : socket_url_(socket_url),
69 event_interface_(event_interface.Pass()),
70 currently_sending_(),
71 send_next_(),
72 read_frame_chunks_(),
73 current_frame_header_(),
74 incomplete_control_frame_body_(),
75 send_quota_low_water_mark_(kDefaultSendQuotaLowWaterMark),
76 send_quota_high_water_mark_(kDefaultSendQuotaHighWaterMark),
77 current_send_quota_(0),
78 state_(FRESHLY_CONSTRUCTED),
79 weak_factory_(this) {}
80
81 WebSocketChannel::~WebSocketChannel() {
82 // The stream may hold a pointer to read_frame_chunks_, and so it needs to be
83 // destroyed first.
84 stream_.reset();
85 }
86
87 void WebSocketChannel::SendAddChannelRequest(
88 const std::vector<std::string>& requested_protocols,
89 const GURL& origin,
90 URLRequestContext* url_request_context) {
91 // Delegate to the tested version.
92 SendAddChannelRequestWithFactory(
93 requested_protocols,
94 origin,
95 url_request_context,
96 base::Bind(&WebSocketStream::CreateAndConnectStream));
97 }
98
99 void WebSocketChannel::SendAddChannelRequestWithFactory(
100 const std::vector<std::string>& requested_protocols,
101 const GURL& origin,
102 URLRequestContext* url_request_context,
103 base::Callback<void(const GURL&,
104 const std::vector<std::string>&,
105 const GURL&,
106 URLRequestContext*,
107 const WebSocketStream::SuccessCallback&,
108 const WebSocketStream::FailureCallback&)> factory) {
109 DCHECK_EQ(FRESHLY_CONSTRUCTED, state_);
110 factory.Run(
111 socket_url_,
112 requested_protocols,
113 origin,
114 url_request_context,
115 base::Bind(&WebSocketChannel::OnConnectSuccess,
116 weak_factory_.GetWeakPtr()),
117 base::Bind(&WebSocketChannel::OnConnectFailure,
118 weak_factory_.GetWeakPtr()));
119 state_ = CONNECTING;
120 }
121
122 void WebSocketChannel::OnConnectSuccess(scoped_ptr<WebSocketStream> stream) {
123 DCHECK(stream);
124 DCHECK_EQ(CONNECTING, state_);
125 stream_ = stream.Pass();
126 event_interface_->OnAddChannelResponse(false, stream_->Protocol());
127 // TODO(ricea): Get flow control information from the WebSocketStream once we
128 // have a multiplexing WebSocketStream.
129 event_interface_->OnFlowControl(send_quota_high_water_mark_);
130 current_send_quota_ = send_quota_high_water_mark_;
131 state_ = CONNECTED;
132 ReadFrames();
133 }
134
135 void WebSocketChannel::OnConnectFailure(unsigned short web_socket_error) {
136 DCHECK_EQ(CONNECTING, state_);
137 event_interface_->OnAddChannelResponse(true, "");
138 state_ = CLOSED;
139 }
140
141 void WebSocketChannel::SendFrame(bool fin,
142 WebSocketFrameHeader::OpCode op_code,
143 const std::vector<char>& data) {
144 if (data.size() > INT_MAX) {
145 DCHECK_LE(data.size(), static_cast<size_t>(INT_MAX));
yhirano 2013/06/26 11:33:29 What is this DCHECK_LE?
Adam Rice 2013/06/27 17:29:49 It will cause a debug build to abort if the frame
yhirano 2013/06/28 06:49:17 How about NOTREACHED?
Adam Rice 2013/06/28 07:59:29 That is much better, thank you.
146 return;
147 }
148 if (stream_ == NULL) {
149 LOG(DFATAL) << "Got SendFrame without a connection established; "
150 << "misbehaving renderer? fin=" << fin << " op_code=" << op_code
151 << " data.size()=" << data.size();
152 return;
153 }
154 if (state_ == SEND_CLOSED || state_ == RECV_CLOSED || state_ == CLOSED) {
155 VLOG(1) << "SendFrame called in state " << state_
156 << ". This may be a bug, or a harmless race.";
157 return;
158 }
159 DCHECK_EQ(CONNECTED, state_);
160 CHECK_GE(current_send_quota_, 0); // Security-critical invariant
161 typedef std::vector<char>::size_type size_type;
162 if (data.size() > static_cast<size_type>(current_send_quota_)) {
163 FailChannel(kWebSocketMuxErrorSendQuotaViolation, "Send quota exceeded");
164 return;
165 }
166 if (!WebSocketFrameHeader::IsKnownDataOpCode(op_code)) {
167 LOG(DFATAL) << "Got SendFrame with bogus op_code " << op_code
168 << "; misbehaving renderer? fin=" << fin
169 << " data.size()=" << data.size();
170 return;
171 }
172 current_send_quota_ -= data.size();
173 // TODO(ricea): If current_send_quota_ has dropped below
174 // send_quota_low_water_mark_, we may want to consider increasing the "low
175 // water mark" and "high water mark", but only if we think we are not
176 // saturating the link to the WebSocket server.
177 // TODO(ricea): For kOpCodeText, do UTF-8 validation?
178 Send(fin, op_code, data);
179 }
180
181 void WebSocketChannel::Send(bool fin,
182 WebSocketFrameHeader::OpCode op_code,
183 const std::vector<char>& data) {
184 scoped_refptr<IOBufferWithSize> buffer(new IOBufferWithSize(data.size()));
185 std::copy(data.begin(), data.end(), buffer->data());
186 SendIOBufferWithSize(fin, op_code, buffer);
187 }
188
189 void WebSocketChannel::SendIOBufferWithSize(
190 bool fin,
191 WebSocketFrameHeader::OpCode op_code,
192 const scoped_refptr<IOBufferWithSize>& buffer) {
193 DCHECK(state_ == CONNECTED || state_ == RECV_CLOSED);
194 DCHECK(stream_);
195 scoped_ptr<WebSocketFrameHeader> header(new WebSocketFrameHeader(op_code));
196 header->final = fin;
197 header->payload_length = buffer->size();
198 scoped_ptr<WebSocketFrameChunk> chunk(new WebSocketFrameChunk());
199 chunk->header = header.Pass();
200 chunk->final_chunk = true;
201 chunk->data = buffer;
202 if (currently_sending_) {
203 // Either the link to the WebSocket server is saturated, or we are simply
204 // processing a batch of messages.
205 // TODO(ricea): We need to keep some statistics to work out which situation
206 // we are in and adjust quota appropriately.
207 if (!send_next_) {
208 send_next_.reset(new SendBuffer);
209 }
210 send_next_->frames.push_back(chunk.release());
211 send_next_->total_bytes += buffer->size();
212 } else {
213 currently_sending_.reset(new SendBuffer);
214 currently_sending_->frames.push_back(chunk.release());
215 currently_sending_->total_bytes += buffer->size();
216 WriteFrames();
217 }
218 }
219
220 void WebSocketChannel::WriteFrames() {
221 // This is safe because we own the WebSocketStream and destroying it cancels
222 // all callbacks.
223 int result = stream_->WriteFrames(
224 &(currently_sending_->frames),
225 base::Bind(&WebSocketChannel::OnWriteDone, base::Unretained(this)));
226 if (result != ERR_IO_PENDING) {
227 OnWriteDone(result);
228 }
229 }
230
231 void WebSocketChannel::OnWriteDone(int result) {
232 DCHECK(state_ != FRESHLY_CONSTRUCTED && state_ != CONNECTING);
233 DCHECK_NE(ERR_IO_PENDING, result);
234 DCHECK(currently_sending_);
235 switch (result) {
236 case OK:
237 if (current_send_quota_ < send_quota_low_water_mark_) {
yhirano 2013/06/26 11:33:29 I think this clause should be executed only if sen
Adam Rice 2013/06/27 17:29:49 You're probably right. I have moved it into the if
238 // TODO(ricea): Increase low_water_mark and high_water_mark if
239 // throughput is high, reduce them if throughput is low.
240 // Low water mark needs to be >= the bandwidth delay product *of the IPC
241 // channel*. Because factors like context-switch time, thread wake-up
242 // time, and bus speed come into play it is complex and probably needs
243 // to be determined empirically.
244 DCHECK_LE(send_quota_low_water_mark_, send_quota_high_water_mark_);
245 // TODO(ricea): Truncate quota by the quota specified by the remote
246 // server, if the protocol in use supports quota.
247 int fresh_quota = send_quota_high_water_mark_ - current_send_quota_;
248 event_interface_->OnFlowControl(fresh_quota);
249 current_send_quota_ += fresh_quota;
250 }
251 if (send_next_) {
252 currently_sending_ = send_next_.Pass();
253 WriteFrames();
254 } else {
255 currently_sending_.reset();
256 }
257 break;
258
259 // If a recoverable error condition existed, it would go here.
260
261 default:
262 DCHECK_LT(result, 0)
263 << "WriteFrames() should only return OK or ERR_ codes";
264 stream_->Close();
265 state_ = CLOSED;
266 event_interface_->OnDropChannel(kWebSocketErrorAbnormalClosure,
267 "Abnormal Closure");
268 break;
269 }
270 }
271
272 void WebSocketChannel::ReadFrames() {
273 // This use if base::Unretained is safe because we own the WebSocketStream,
274 // and any pending reads will be cancelled when it is destroyed.
275 int result = stream_->ReadFrames(
276 &read_frame_chunks_,
277 base::Bind(&WebSocketChannel::OnReadDone, base::Unretained(this)));
278 if (result != ERR_IO_PENDING) {
279 OnReadDone(result);
280 }
281 }
282
283 void WebSocketChannel::OnReadDone(int result) {
284 DCHECK(state_ != FRESHLY_CONSTRUCTED && state_ != CONNECTING);
285 DCHECK_NE(ERR_IO_PENDING, result);
286 switch (result) {
287 case OK:
288 // ReadFrames() must use ERR_CONNECTION_CLOSED for a closed connection
289 // with no data read, not an empty response.
290 DCHECK(!read_frame_chunks_.empty())
291 << "ReadFrames() returned OK, but nothing was read.";
292 for (size_t i = 0; i < read_frame_chunks_.size(); ++i) {
293 scoped_ptr<WebSocketFrameChunk> chunk(read_frame_chunks_[i]);
294 read_frame_chunks_[i] = NULL;
295 ProcessFrameChunk(chunk.Pass());
296 }
297 read_frame_chunks_.clear();
298 break;
tyoshino (SeeGerritForStatus) 2013/06/26 09:27:06 let's call ReadFrames() and return.
Adam Rice 2013/06/27 17:29:49 Done.
299
300 case ERR_CONNECTION_CLOSED: {
301 State old_state = state_;
302 state_ = CLOSED;
303 if (old_state != RECV_CLOSED && old_state != CLOSED) {
304 // We need to inform the render process of the unexpected closure.
305 event_interface_->OnDropChannel(kWebSocketErrorAbnormalClosure,
306 "Abnormal Closure");
307 }
308 break;
tyoshino (SeeGerritForStatus) 2013/06/26 09:27:06 let's return
Adam Rice 2013/06/27 17:29:49 Done.
309 }
310
311 default: {
312 DCHECK_LT(result, 0)
313 << "ReadFrames() should only return OK or ERR_ codes";
314 stream_->Close();
315 State old_state = state_;
316 state_ = CLOSED;
317 if (old_state != RECV_CLOSED && old_state != CLOSED) {
318 event_interface_->OnDropChannel(kWebSocketErrorAbnormalClosure,
319 "Abnormal Closure");
320 }
321 break;
tyoshino (SeeGerritForStatus) 2013/06/26 09:27:06 let's return
Adam Rice 2013/06/27 17:29:49 Done.
322 }
323 }
324 // We need to always keep a call to ReadFrames pending.
325 if (state_ != CLOSED) {
326 ReadFrames();
327 }
328 }
329
330 // TODO(ricea): This method is too long. Break it up.
331 void WebSocketChannel::ProcessFrameChunk(
332 scoped_ptr<WebSocketFrameChunk> chunk) {
333 scoped_ptr<WebSocketFrameHeader> frame_header;
334 frame_header.swap(current_frame_header_);
tyoshino (SeeGerritForStatus) 2013/06/26 09:27:06 Write the reason of swap.
Adam Rice 2013/06/27 17:29:49 I added a comment. I hope it helps.
335 bool first_chunk = false;
336 if (chunk->header) {
337 first_chunk = true;
338 frame_header.swap(chunk->header);
339 }
340 scoped_refptr<IOBufferWithSize> data_buffer;
341 data_buffer.swap(chunk->data);
342 if (WebSocketFrameHeader::IsKnownControlOpCode(frame_header->opcode)) {
343 if (chunk->final_chunk) {
344 if (incomplete_control_frame_body_) {
345 VLOG(2) << "Rejoining a split control frame, opcode "
346 << frame_header->opcode;
347 scoped_refptr<IOBufferWithSize> old_data_buffer;
348 old_data_buffer.swap(incomplete_control_frame_body_);
349 scoped_refptr<IOBufferWithSize> new_data_buffer;
350 new_data_buffer.swap(data_buffer);
351 data_buffer = new ConcatenatedIOBufferWithSize(old_data_buffer.get(),
352 new_data_buffer.get());
353 }
354 } else {
355 // TODO(ricea): Enforce a maximum size of 127 bytes on the control frames
356 // we accept.
357 VLOG(2) << "Encountered a split control frame, opcode "
358 << frame_header->opcode;
359 if (incomplete_control_frame_body_) {
360 // The really horrid case. We need to create a new IOBufferWithSize
361 // combining the new one and the old one. This should virtually never
362 // happen.
363 // TODO(ricea): This algorithm is O(N^2). Use a fixed 127-byte byffer
364 // instead.
365 VLOG(3) << "Hit the really horrid case";
366 scoped_refptr<IOBufferWithSize> old_body_;
tyoshino (SeeGerritForStatus) 2013/06/26 09:27:06 not member variable. remove last _
Adam Rice 2013/06/27 17:29:49 I have no idea why I put that _ there. Removed.
367 old_body_.swap(incomplete_control_frame_body_);
368 incomplete_control_frame_body_ = new ConcatenatedIOBufferWithSize(
369 old_body_.get(), data_buffer.get());
370 } else {
371 // The merely horrid case. Store the IOBufferWithSize to use when the
372 // rest of the control frame arrives.
373 incomplete_control_frame_body_.swap(data_buffer);
374 }
375 current_frame_header_.swap(frame_header);
376 return;
377 }
378 }
379
380 WebSocketFrameHeader::OpCode opcode = frame_header->opcode;
381 switch (frame_header->opcode) {
382 case WebSocketFrameHeader::kOpCodeText: // fall-thru
383 case WebSocketFrameHeader::kOpCodeBinary:
384 if (!first_chunk) {
385 opcode = WebSocketFrameHeader::kOpCodeContinuation;
386 }
387 // fall-thru
388 case WebSocketFrameHeader::kOpCodeContinuation: {
389 if (state_ == RECV_CLOSED) {
390 FailChannel(1002, "Data packet received after close");
391 return;
392 }
393 bool final = chunk->final_chunk && frame_header->final;
394 // TODO(ricea): Can this copy be eliminated?
395 char* data_begin = data_buffer->data();
396 char* data_end = data_begin + data_buffer->size();
397 std::vector<char> data(data_begin, data_end);
398 // TODO(ricea): Handle the (improbable) case when ReadFrames returns far
399 // more data at once than we want to send in a single IPC (in which case
400 // we need to buffer the data and return to the event loop with a
401 // callback to send the rest in 32K chunks).
402 event_interface_->OnSendFrame(final, opcode, data);
tyoshino (SeeGerritForStatus) 2013/06/26 09:27:06 why this is On"Send"?
Adam Rice 2013/06/27 17:29:49 Having received the message, we will send the IPC
tyoshino (SeeGerritForStatus) 2013/06/28 07:55:27 Yes. It's so common convention that OnSomething()
Adam Rice 2013/06/28 08:28:06 Done.
403 break;
404 }
405
406 case WebSocketFrameHeader::kOpCodePing:
407 VLOG(1) << "Got Ping of size " << data_buffer->size();
408 if (state_ == RECV_CLOSED) {
409 FailChannel(1002, "Ping received after Close");
410 return;
411 }
412 SendIOBufferWithSize(
413 true, WebSocketFrameHeader::kOpCodePong, data_buffer);
414 break;
415
416 case WebSocketFrameHeader::kOpCodePong:
417 VLOG(1) << "Got Pong of size " << data_buffer->size();
yhirano 2013/06/26 11:33:29 Can you write a TODO comment that describes how to
Adam Rice 2013/06/27 17:29:49 We don't need to anything with pong messages.
418 if (state_ == RECV_CLOSED) {
419 FailChannel(1002, "Pong received after Close");
420 return;
421 }
422 break;
423
424 case WebSocketFrameHeader::kOpCodeClose: {
425 unsigned short reason;
tyoshino (SeeGerritForStatus) 2013/06/26 09:27:06 initialize to some default value.
Adam Rice 2013/06/27 17:29:49 Done.
426 std::string reason_text;
427 ParseClose(*data_buffer, &reason, &reason_text);
428 // TODO(ricea): Find a way to safely log the message from the close
429 // message (escape control codes and so on).
430 VLOG(1) << "Got Close with reason " << reason;
431 switch (state_) {
432 case CONNECTED:
433 state_ = RECV_CLOSED;
434 SendClose(reason, reason_text);
435 event_interface_->OnDropChannel(reason, reason_text);
436 break;
437
438 case RECV_CLOSED:
439 FailChannel(1002, "Close received after Close");
440 break;
441
442 case SEND_CLOSED:
443 state_ = CLOSED;
444 event_interface_->OnDropChannel(reason, reason_text);
445 break;
446
447 default:
448 LOG(DFATAL) << "Got Close in unexpected state " << state_;
449 break;
450 }
451 break;
452 }
453
454 default:
455 NOTREACHED();
456 break;
457 }
458 if (!chunk->final_chunk) {
459 // Preserve the frame header for the next call.
460 current_frame_header_.swap(frame_header);
461 }
462 }
463
464 void WebSocketChannel::SendFlowControl(int64 quota) {
465 DCHECK_EQ(CONNECTED, state_);
466 // TODO(ricea): Add interface to WebSocketStream and implement.
467 // stream_->SendFlowControl(quota);
468 }
469
470 void WebSocketChannel::SendDropChannel(unsigned short reason,
471 const std::string& reason_text) {
472 if (state_ == SEND_CLOSED || state_ == CLOSED) {
473 VLOG(1) << "SendDropChannel called in state " << state_
474 << ". This may be a bug, or a harmless race.";
475 return;
476 }
477 DCHECK_EQ(CONNECTED, state_);
478 // TODO(ricea): Validate reason? Check that reason_text is valid UTF-8?
479 // TODO(ricea): There should probably be a timeout for the closing handshake.
480 SendClose(reason, reason_text);
481 }
482
483 void WebSocketChannel::FailChannel(unsigned short code,
484 const std::string& reason) {
485 // TODO(ricea): Logging.
486 State old_state = state_;
487 if (state_ == CONNECTED) {
488 SendClose(kWebSocketErrorGoingAway, "Internal Error");
489 }
490 if (old_state != RECV_CLOSED && old_state != CLOSED) {
491 event_interface_->OnDropChannel(code, reason);
492 }
493 }
494
495 void WebSocketChannel::SendClose(unsigned short code,
496 const std::string& reason) {
497 DCHECK(state_ == CONNECTED || state_ == RECV_CLOSED);
498 uint64 payload_length = kWebSocketErrorNetworkByteLength + reason.length();
499 std::vector<char> data(payload_length);
500 NetworkErrorCode network_code;
501 network_code.code_as_network_short = base::HostToNet16(code);
502 std::copy(network_code.code_as_char,
503 network_code.code_as_char + kWebSocketErrorNetworkByteLength,
504 data.begin());
tyoshino (SeeGerritForStatus) 2013/06/26 09:27:06 how about using net::WriteBigEndian?
Adam Rice 2013/06/27 17:29:49 Done.
505 std::copy(reason.begin(),
506 reason.end(),
507 data.begin() + kWebSocketErrorNetworkByteLength);
508 Send(true, WebSocketFrameHeader::kOpCodeClose, data);
509 // TODO(ricea): This is wrong.
510 state_ = state_ == CONNECTED ? SEND_CLOSED : CLOSED;
511 }
512
513 void WebSocketChannel::ParseClose(const IOBufferWithSize& buffer,
514 unsigned short* reason,
515 std::string* reason_text) {
516 const char* data = const_cast<IOBufferWithSize&>(buffer).data();
517 CHECK_GE(buffer.size(), 0); // Possibly security-critical invariant.
518 size_t size = static_cast<size_t>(buffer.size());
519 reason_text->clear();
520 if (size < kWebSocketErrorNetworkByteLength) {
tyoshino (SeeGerritForStatus) 2013/06/26 09:27:06 it works but i'd code this like if (size < kWebSo
Adam Rice 2013/06/27 17:29:49 Done.
521 if (size == 1) {
522 VLOG(1) << "Close frame with 1-byte payload received "
523 << "(the byte is " << std::hex << static_cast<int>(data[0])
524 << ")";
525 }
526 *reason = 1005;
yhirano 2013/06/26 11:33:29 Explaining what 1005 error is in a comment would b
Adam Rice 2013/06/27 17:29:49 I'm not sure why I wrote 1005 instead of just usin
527 return;
528 }
529 NetworkErrorCode network_code;
530 std::copy(
531 data, data + kWebSocketErrorNetworkByteLength, network_code.code_as_char);
532 unsigned short unchecked_reason =
533 base::NetToHost16(network_code.code_as_network_short);
tyoshino (SeeGerritForStatus) 2013/06/26 09:27:06 how about using net::ReadBigEndian?
Adam Rice 2013/06/27 17:29:49 Done.
534 if (unchecked_reason >=
535 static_cast<unsigned short>(kWebSocketNormalClosure) &&
536 unchecked_reason <=
537 static_cast<unsigned short>(kWebSocketErrorPrivateReservedMax)) {
538 *reason = unchecked_reason;
539 } else {
540 VLOG(1) << "Close frame contained reason code outside of the valid range: "
541 << unchecked_reason;
542 *reason = 1008;
543 }
544 std::string text(data + kWebSocketErrorNetworkByteLength, data + size);
545 // TODO(ricea): Is this check strict enough? In particular, check the
546 // "Security Considerations" from RFC3629.
547 if (IsStringUTF8(text)) {
548 using std::swap;
549 swap(*reason_text, text);
550 }
551 }
552
553 } // namespace net
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698