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

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: Changes in response to review comments. 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/big_endian.h"
13 #include "net/base/io_buffer.h"
14 #include "net/http/http_request_info.h"
15 #include "net/http/http_stream_factory.h"
16 #include "net/ssl/ssl_config_service.h"
17 #include "net/websockets/websocket_errors.h"
18 #include "net/websockets/websocket_event_interface.h"
19 #include "net/websockets/websocket_frame.h"
20 #include "net/websockets/websocket_mux.h"
21 #include "net/websockets/websocket_stream.h"
22
23 namespace net {
24
25 namespace {
26
27 const int kDefaultSendQuotaLowWaterMark = 1 << 16;
28 const int kDefaultSendQuotaHighWaterMark = 1 << 17;
29 const size_t kWebSocketCloseCodeLength = 2;
30
31 // IOBuffer is too hardcore to offer a const accessor. Make our own.
32 const char* GetConstData(const IOBuffer* iobuffer) {
33 return const_cast<IOBuffer*>(iobuffer)->data();
34 }
35
36 // Concatenate the data from two IOBufferWithSize objects into a single one.
37 IOBufferWithSize* ConcatenateIOBuffers(const IOBufferWithSize* part1,
38 const IOBufferWithSize* part2) {
39 int newsize = part1->size() + part2->size();
40 IOBufferWithSize* newbuffer = new IOBufferWithSize(newsize);
41 std::copy(GetConstData(part1),
42 GetConstData(part1) + part1->size(),
43 newbuffer->data());
44 std::copy(GetConstData(part2),
45 GetConstData(part2) + part2->size(),
46 newbuffer->data() + part1->size());
47 return newbuffer;
48 }
49
50 } // namespace
51
52 struct WebSocketChannel::SendBuffer {
53 SendBuffer() : frames(), total_bytes(0) {}
yhirano 2013/06/28 06:49:17 nit: You don't need frames().
Adam Rice 2013/06/28 07:59:29 I have a habit of initialising all the member vara
54 ScopedVector<WebSocketFrameChunk> frames;
55 size_t total_bytes;
56 };
57
58 // Implementation of WebSocketStream::ConnectDelegate that simply forwards the
59 // calls on to the WebSocketChannel that created it.
60 class WebSocketChannel::ConnectDelegate
61 : public WebSocketStream::ConnectDelegate {
62 public:
63 ConnectDelegate(WebSocketChannel* creator) : creator_(creator) {}
64
65 virtual void OnSuccess(scoped_ptr<WebSocketStream> stream) OVERRIDE {
66 creator_->OnConnectSuccess(stream.Pass());
67 }
68
69 virtual void OnFailure(unsigned short websocket_error) OVERRIDE {
70 creator_->OnConnectFailure(websocket_error);
71 }
72
73 private:
74 // A pointer to the WebSocketChannel that created us. We do not need to worry
75 // about this pointer being stale, because deleting WebSocketChannel cancels
76 // the connect process, deleting this object and preventing its callbacks from
77 // being called.
78 WebSocketChannel* const creator_;
79
80 DISALLOW_COPY_AND_ASSIGN(ConnectDelegate);
81 };
82
83 WebSocketChannel::WebSocketChannel(
84 const GURL& socket_url,
85 scoped_ptr<WebSocketEventInterface> event_interface)
86 : socket_url_(socket_url),
87 event_interface_(event_interface.Pass()),
88 currently_sending_(),
89 send_next_(),
90 read_frame_chunks_(),
91 current_frame_header_(),
92 incomplete_control_frame_body_(),
yhirano 2013/06/28 06:49:17 nit: You don't need call a constructor if it doesn
Adam Rice 2013/06/28 07:59:29 I removed the redundant initialisers. See my comme
93 send_quota_low_water_mark_(kDefaultSendQuotaLowWaterMark),
94 send_quota_high_water_mark_(kDefaultSendQuotaHighWaterMark),
95 current_send_quota_(0),
96 state_(FRESHLY_CONSTRUCTED),
97 weak_factory_(this) {}
98
99 WebSocketChannel::~WebSocketChannel() {
100 // The stream may hold a pointer to read_frame_chunks_, and so it needs to be
101 // destroyed first.
102 stream_.reset();
103 }
104
105 void WebSocketChannel::SendAddChannelRequest(
106 const std::vector<std::string>& requested_subprotocols,
107 const GURL& origin,
108 URLRequestContext* url_request_context) {
109 // Delegate to the tested version.
110 SendAddChannelRequestWithFactory(
111 requested_subprotocols,
112 origin,
113 url_request_context,
114 base::Bind(&WebSocketStream::CreateAndConnectStream));
115 }
116
117 void WebSocketChannel::SendAddChannelRequestWithFactory(
118 const std::vector<std::string>& requested_subprotocols,
119 const GURL& origin,
120 URLRequestContext* url_request_context,
121 base::Callback<scoped_ptr<WebSocketStreamRequest>(
122 const GURL&,
123 const std::vector<std::string>&,
124 const GURL&,
125 URLRequestContext*,
126 const BoundNetLog&,
127 scoped_ptr<WebSocketStream::ConnectDelegate>)> factory) {
128 DCHECK_EQ(FRESHLY_CONSTRUCTED, state_);
129 scoped_ptr<WebSocketStream::ConnectDelegate> connect_delegate(
130 new WebSocketChannel::ConnectDelegate(this));
131 stream_request_ = factory.Run(socket_url_,
132 requested_subprotocols,
133 origin,
134 url_request_context,
135 BoundNetLog(),
136 connect_delegate.Pass());
137 state_ = CONNECTING;
138 }
139
140 void WebSocketChannel::OnConnectSuccess(scoped_ptr<WebSocketStream> stream) {
141 DCHECK(stream);
142 DCHECK_EQ(CONNECTING, state_);
143 stream_ = stream.Pass();
144 event_interface_->OnAddChannelResponse(false, stream_->GetSubProtocol());
145 // TODO(ricea): Get flow control information from the WebSocketStream once we
146 // have a multiplexing WebSocketStream.
147 event_interface_->OnFlowControl(send_quota_high_water_mark_);
148 current_send_quota_ = send_quota_high_water_mark_;
149 state_ = CONNECTED;
150 // We don't need this any more.
151 stream_request_.reset();
152 ReadFrames();
153 }
154
155 void WebSocketChannel::OnConnectFailure(unsigned short web_socket_error) {
156 DCHECK_EQ(CONNECTING, state_);
157 event_interface_->OnAddChannelResponse(true, "");
158 stream_request_.reset();
159 state_ = CLOSED;
160 }
161
162 void WebSocketChannel::SendFrame(bool fin,
163 WebSocketFrameHeader::OpCode op_code,
164 const std::vector<char>& data) {
165 if (data.size() > INT_MAX) {
tyoshino (SeeGerritForStatus) 2013/06/28 07:55:27 > -> >= ? what is this check for?
Adam Rice 2013/06/28 08:28:06 Mostly just paranoia. The networking code uses int
tyoshino (SeeGerritForStatus) 2013/06/28 08:30:51 ok. it's fine for now. please write a comment that
Adam Rice 2013/06/28 14:20:48 I updated the comment.
166 DCHECK_LE(data.size(), static_cast<size_t>(INT_MAX));
167 return;
168 }
169 if (stream_ == NULL) {
170 LOG(DFATAL) << "Got SendFrame without a connection established; "
171 << "misbehaving renderer? fin=" << fin << " op_code=" << op_code
172 << " data.size()=" << data.size();
173 return;
174 }
175 if (state_ == SEND_CLOSED || state_ == RECV_CLOSED || state_ == CLOSED) {
176 VLOG(1) << "SendFrame called in state " << state_
177 << ". This may be a bug, or a harmless race.";
178 return;
179 }
180 DCHECK_EQ(CONNECTED, state_);
181 CHECK_GE(current_send_quota_, 0); // Security-critical invariant
182 typedef std::vector<char>::size_type size_type;
tyoshino (SeeGerritForStatus) 2013/06/28 07:55:27 don't worry about size_type being different from s
Adam Rice 2013/06/28 08:28:06 Done.
183 if (data.size() > static_cast<size_type>(current_send_quota_)) {
184 FailChannel(SEND_INTERNAL_ERROR,
185 kWebSocketMuxErrorSendQuotaViolation,
186 "Send quota exceeded");
187 return;
188 }
189 if (!WebSocketFrameHeader::IsKnownDataOpCode(op_code)) {
190 LOG(DFATAL) << "Got SendFrame with bogus op_code " << op_code
191 << "; misbehaving renderer? fin=" << fin
192 << " data.size()=" << data.size();
193 return;
194 }
195 current_send_quota_ -= data.size();
196 // TODO(ricea): If current_send_quota_ has dropped below
197 // send_quota_low_water_mark_, we may want to consider increasing the "low
198 // water mark" and "high water mark", but only if we think we are not
199 // saturating the link to the WebSocket server.
200 // TODO(ricea): For kOpCodeText, do UTF-8 validation?
201 Send(fin, op_code, data);
202 }
203
204 void WebSocketChannel::Send(bool fin,
205 WebSocketFrameHeader::OpCode op_code,
206 const std::vector<char>& data) {
207 scoped_refptr<IOBufferWithSize> buffer(new IOBufferWithSize(data.size()));
208 std::copy(data.begin(), data.end(), buffer->data());
209 SendIOBufferWithSize(fin, op_code, buffer);
210 }
211
212 void WebSocketChannel::SendIOBufferWithSize(
213 bool fin,
214 WebSocketFrameHeader::OpCode op_code,
215 const scoped_refptr<IOBufferWithSize>& buffer) {
216 DCHECK(state_ == CONNECTED || state_ == RECV_CLOSED);
217 DCHECK(stream_);
218 scoped_ptr<WebSocketFrameHeader> header(new WebSocketFrameHeader(op_code));
219 header->final = fin;
220 header->masked = true;
221 header->payload_length = buffer->size();
222 scoped_ptr<WebSocketFrameChunk> chunk(new WebSocketFrameChunk());
223 chunk->header = header.Pass();
224 chunk->final_chunk = true;
225 chunk->data = buffer;
226 if (currently_sending_) {
227 // Either the link to the WebSocket server is saturated, or we are simply
228 // processing a batch of messages.
229 // TODO(ricea): We need to keep some statistics to work out which situation
230 // we are in and adjust quota appropriately.
231 if (!send_next_) {
232 send_next_.reset(new SendBuffer);
233 }
234 send_next_->frames.push_back(chunk.release());
235 send_next_->total_bytes += buffer->size();
236 } else {
237 currently_sending_.reset(new SendBuffer);
238 currently_sending_->frames.push_back(chunk.release());
239 currently_sending_->total_bytes += buffer->size();
240 WriteFrames();
241 }
242 }
243
244 void WebSocketChannel::WriteFrames() {
245 // This is safe because we own the WebSocketStream and destroying it cancels
246 // all callbacks.
247 int result = stream_->WriteFrames(
248 &(currently_sending_->frames),
249 base::Bind(&WebSocketChannel::OnWriteDone, base::Unretained(this)));
250 if (result != ERR_IO_PENDING) {
251 OnWriteDone(result);
252 }
253 }
254
255 void WebSocketChannel::OnWriteDone(int result) {
256 DCHECK(state_ != FRESHLY_CONSTRUCTED && state_ != CONNECTING);
257 DCHECK_NE(ERR_IO_PENDING, result);
258 DCHECK(currently_sending_);
259 switch (result) {
260 case OK:
261 if (send_next_) {
262 currently_sending_ = send_next_.Pass();
263 WriteFrames();
264 } else {
265 currently_sending_.reset();
266 if (current_send_quota_ < send_quota_low_water_mark_) {
267 // TODO(ricea): Increase low_water_mark and high_water_mark if
268 // throughput is high, reduce them if throughput is low. Low water
269 // mark needs to be >= the bandwidth delay product *of the IPC
270 // channel*. Because factors like context-switch time, thread wake-up
271 // time, and bus speed come into play it is complex and probably needs
272 // to be determined empirically.
273 DCHECK_LE(send_quota_low_water_mark_, send_quota_high_water_mark_);
274 // TODO(ricea): Truncate quota by the quota specified by the remote
275 // server, if the protocol in use supports quota.
276 int fresh_quota = send_quota_high_water_mark_ - current_send_quota_;
277 event_interface_->OnFlowControl(fresh_quota);
278 current_send_quota_ += fresh_quota;
279 }
280 }
281 break;
282
283 // If a recoverable error condition existed, it would go here.
284
285 default:
286 DCHECK_LT(result, 0)
287 << "WriteFrames() should only return OK or ERR_ codes";
288 stream_->Close();
289 state_ = CLOSED;
290 event_interface_->OnDropChannel(kWebSocketErrorAbnormalClosure,
291 "Abnormal Closure");
292 break;
293 }
294 }
295
296 void WebSocketChannel::ReadFrames() {
297 // This use if base::Unretained is safe because we own the WebSocketStream,
298 // and any pending reads will be cancelled when it is destroyed.
299 int result = stream_->ReadFrames(
300 &read_frame_chunks_,
301 base::Bind(&WebSocketChannel::OnReadDone, base::Unretained(this)));
302 if (result != ERR_IO_PENDING) {
303 OnReadDone(result);
304 }
305 }
306
307 void WebSocketChannel::OnReadDone(int result) {
308 DCHECK(state_ != FRESHLY_CONSTRUCTED && state_ != CONNECTING);
309 DCHECK_NE(ERR_IO_PENDING, result);
310 switch (result) {
311 case OK:
312 // ReadFrames() must use ERR_CONNECTION_CLOSED for a closed connection
313 // with no data read, not an empty response.
314 DCHECK(!read_frame_chunks_.empty())
315 << "ReadFrames() returned OK, but nothing was read.";
316 for (size_t i = 0; i < read_frame_chunks_.size(); ++i) {
317 scoped_ptr<WebSocketFrameChunk> chunk(read_frame_chunks_[i]);
yhirano 2013/06/28 06:49:17 What happens when ProcessFrameChunk fails (i.e. Fa
Adam Rice 2013/06/28 07:59:29 It is important that is doesn't make any differenc
318 read_frame_chunks_[i] = NULL;
319 ProcessFrameChunk(chunk.Pass());
320 }
321 read_frame_chunks_.clear();
322 // We need to always keep a call to ReadFrames pending.
323 ReadFrames();
324 return;
325
326 case ERR_CONNECTION_CLOSED: {
327 State old_state = state_;
328 state_ = CLOSED;
329 if (old_state != RECV_CLOSED && old_state != CLOSED) {
330 // We need to inform the render process of the unexpected closure.
331 event_interface_->OnDropChannel(kWebSocketErrorAbnormalClosure,
332 "Abnormal Closure");
333 }
334 return;
335 }
336
337 default: {
338 DCHECK_LT(result, 0)
339 << "ReadFrames() should only return OK or ERR_ codes";
340 stream_->Close();
341 State old_state = state_;
342 state_ = CLOSED;
343 if (old_state != RECV_CLOSED && old_state != CLOSED) {
344 event_interface_->OnDropChannel(kWebSocketErrorAbnormalClosure,
345 "Abnormal Closure");
346 }
347 return;
348 }
349 }
350 }
351
352 // TODO(ricea): This method is too long. Break it up.
353 void WebSocketChannel::ProcessFrameChunk(
354 scoped_ptr<WebSocketFrameChunk> chunk) {
355 // frame_header stores the header for this frame, either saved from a previous
356 // chunk or from this chunk if it includes a header.
357 scoped_ptr<WebSocketFrameHeader> frame_header;
358 // Borrow the value of current_frame_header_. At the end of the function we
359 // will put it back if it is still valid, or replace it with the header from
360 // the new chunk.
361 frame_header.swap(current_frame_header_);
362 bool first_chunk = false;
363 if (chunk->header) {
364 first_chunk = true;
365 frame_header.swap(chunk->header);
366 if (frame_header->masked) {
367 // RFC6455 Section 5.1 "A client MUST close a connection if it detects a
368 // masked frame."
369 FailChannel(SEND_REAL_ERROR,
370 kWebSocketErrorProtocolError,
371 "Masked frame from server");
372 return;
373 }
374 }
yhirano 2013/06/28 06:49:17 optional: DCHECK(frame_header) here?
Adam Rice 2013/06/28 07:59:29 I made it return instead, since this can legitimat
375 scoped_refptr<IOBufferWithSize> data_buffer;
376 data_buffer.swap(chunk->data);
377 if (WebSocketFrameHeader::IsKnownControlOpCode(frame_header->opcode)) {
378 if (chunk->final_chunk) {
379 if (incomplete_control_frame_body_) {
380 VLOG(2) << "Rejoining a split control frame, opcode "
381 << frame_header->opcode;
382 scoped_refptr<IOBufferWithSize> old_data_buffer;
383 old_data_buffer.swap(incomplete_control_frame_body_);
384 scoped_refptr<IOBufferWithSize> new_data_buffer;
385 new_data_buffer.swap(data_buffer);
386 data_buffer =
387 ConcatenateIOBuffers(old_data_buffer.get(), new_data_buffer.get());
388 }
389 } else {
390 // TODO(ricea): Enforce a maximum size of 125 bytes on the control frames
391 // we accept.
392 VLOG(2) << "Encountered a split control frame, opcode "
393 << frame_header->opcode;
394 if (incomplete_control_frame_body_) {
395 // The really horrid case. We need to create a new IOBufferWithSize
396 // combining the new one and the old one. This should virtually never
397 // happen.
398 // TODO(ricea): This algorithm is O(N^2). Use a fixed 127-byte byffer
399 // instead.
400 VLOG(3) << "Hit the really horrid case";
401 scoped_refptr<IOBufferWithSize> old_body;
402 old_body.swap(incomplete_control_frame_body_);
403 incomplete_control_frame_body_ =
404 ConcatenateIOBuffers(old_body.get(), data_buffer.get());
405 } else {
406 // The merely horrid case. Store the IOBufferWithSize to use when the
407 // rest of the control frame arrives.
408 incomplete_control_frame_body_.swap(data_buffer);
409 }
410 current_frame_header_.swap(frame_header);
411 return;
412 }
413 }
414
415 WebSocketFrameHeader::OpCode opcode = frame_header->opcode;
416 switch (frame_header->opcode) {
417 case WebSocketFrameHeader::kOpCodeText: // fall-thru
418 case WebSocketFrameHeader::kOpCodeBinary:
419 if (!first_chunk) {
420 opcode = WebSocketFrameHeader::kOpCodeContinuation;
421 }
422 // fall-thru
423 case WebSocketFrameHeader::kOpCodeContinuation:
424 if (state_ == RECV_CLOSED) {
425 FailChannel(SEND_REAL_ERROR,
426 kWebSocketErrorProtocolError,
427 "Data packet received after close");
428 return;
429 } else if (state_ == CONNECTED) {
430 const bool final = chunk->final_chunk && frame_header->final;
431 // TODO(ricea): Can this copy be eliminated?
432 const char* const data_begin = data_buffer->data();
433 const char* const data_end = data_begin + data_buffer->size();
434 const std::vector<char> data(data_begin, data_end);
435 // TODO(ricea): Handle the (improbable) case when ReadFrames returns far
436 // more data at once than we want to send in a single IPC (in which case
437 // we need to buffer the data and return to the event loop with a
438 // callback to send the rest in 32K chunks).
439
440 // Send the received frame to the renderer process.
441 event_interface_->OnSendFrame(final, opcode, data);
442 }
443 break;
444
445 case WebSocketFrameHeader::kOpCodePing:
446 VLOG(1) << "Got Ping of size " << data_buffer->size();
447 if (state_ == RECV_CLOSED) {
448 FailChannel(SEND_REAL_ERROR,
449 kWebSocketErrorProtocolError,
450 "Ping received after Close");
451 return;
452 } else if (state_ == CONNECTED) {
453 SendIOBufferWithSize(
454 true, WebSocketFrameHeader::kOpCodePong, data_buffer);
455 }
456 break;
457
458 case WebSocketFrameHeader::kOpCodePong:
459 VLOG(1) << "Got Pong of size " << data_buffer->size();
460 if (state_ == RECV_CLOSED) {
461 FailChannel(SEND_REAL_ERROR,
462 kWebSocketErrorProtocolError,
463 "Pong received after Close");
464 return;
465 }
466 // We do not need to do anything with pong messages.
467 break;
468
469 case WebSocketFrameHeader::kOpCodeClose: {
470 unsigned short reason = kWebSocketNormalClosure;
471 std::string reason_text;
472 ParseClose(*data_buffer, &reason, &reason_text);
473 // TODO(ricea): Find a way to safely log the message from the close
474 // message (escape control codes and so on).
475 VLOG(1) << "Got Close with reason " << reason;
476 switch (state_) {
477 case CONNECTED:
478 state_ = RECV_CLOSED;
479 SendClose(reason, reason_text);
480 event_interface_->OnDropChannel(reason, reason_text);
481 break;
482
483 case RECV_CLOSED:
484 FailChannel(SEND_REAL_ERROR,
485 kWebSocketErrorProtocolError,
486 "Close received after Close");
487 break;
488
489 case SEND_CLOSED:
490 state_ = CLOSED;
491 event_interface_->OnDropChannel(reason, reason_text);
492 break;
493
494 default:
495 LOG(DFATAL) << "Got Close in unexpected state " << state_;
496 break;
497 }
498 break;
499 }
500
501 default:
502 FailChannel(SEND_REAL_ERROR,
503 kWebSocketErrorProtocolError,
504 "Unknown opcode");
505
506 break;
507 }
508 if (!chunk->final_chunk) {
509 // Preserve the frame header for the next call.
510 current_frame_header_.swap(frame_header);
511 }
512 }
513
514 void WebSocketChannel::SendFlowControl(int64 quota) {
515 DCHECK_EQ(CONNECTED, state_);
516 // TODO(ricea): Add interface to WebSocketStream and implement.
517 // stream_->SendFlowControl(quota);
518 }
519
520 void WebSocketChannel::SendDropChannel(unsigned short reason,
521 const std::string& reason_text) {
522 if (state_ == SEND_CLOSED || state_ == CLOSED) {
523 VLOG(1) << "SendDropChannel called in state " << state_
524 << ". This may be a bug, or a harmless race.";
525 return;
526 }
527 DCHECK_EQ(CONNECTED, state_);
528 // TODO(ricea): Validate reason? Check that reason_text is valid UTF-8?
529 // TODO(ricea): There should probably be a timeout for the closing handshake.
530 SendClose(reason, reason_text);
531 }
532
533 void WebSocketChannel::FailChannel(ExposeError expose,
534 unsigned short code,
535 const std::string& reason) {
536 // TODO(ricea): Logging.
537 State old_state = state_;
538 if (state_ == CONNECTED) {
539 unsigned short send_code = kWebSocketErrorGoingAway;
540 std::string send_reason = "Internal Error";
541 if (expose == SEND_REAL_ERROR) {
542 send_code = code;
543 send_reason = reason;
544 }
545 SendClose(send_code, send_reason);
546 }
547 if (old_state != RECV_CLOSED && old_state != CLOSED) {
548 event_interface_->OnDropChannel(code, reason);
549 }
550 }
551
552 void WebSocketChannel::SendClose(unsigned short code,
553 const std::string& reason) {
554 DCHECK(state_ == CONNECTED || state_ == RECV_CLOSED);
555 uint64 payload_length = kWebSocketCloseCodeLength + reason.length();
556 std::vector<char> data(payload_length);
557 DCHECK(payload_length > 0);
558 WriteBigEndian(&data[0], code);
559 COMPILE_ASSERT(sizeof(code) == kWebSocketCloseCodeLength,
560 they_should_both_be_two);
561 std::copy(reason.begin(),
562 reason.end(),
563 data.begin() + kWebSocketCloseCodeLength);
564 Send(true, WebSocketFrameHeader::kOpCodeClose, data);
565 state_ = state_ == CONNECTED ? SEND_CLOSED : CLOSED;
566 }
567
568 void WebSocketChannel::ParseClose(const IOBufferWithSize& buffer,
569 unsigned short* reason,
570 std::string* reason_text) {
571 const char* data = const_cast<IOBufferWithSize&>(buffer).data();
572 CHECK_GE(buffer.size(), 0); // Possibly security-critical invariant.
573 size_t size = static_cast<size_t>(buffer.size());
574 reason_text->clear();
575 if (size < kWebSocketCloseCodeLength) {
576 *reason = kWebSocketErrorNoStatusReceived;
577 if (size != 0) {
578 VLOG(1) << "Close frame with payload size " << size << " received "
579 << "(the first byte is " << std::hex << static_cast<int>(data[0])
580 << ")";
581 return;
582 }
583 return;
584 }
585 unsigned short unchecked_reason = 0;
586 ReadBigEndian(data, &unchecked_reason);
587 COMPILE_ASSERT(sizeof(unchecked_reason) == kWebSocketCloseCodeLength,
588 they_should_both_be_two_bytes);
589 if (unchecked_reason >=
590 static_cast<unsigned short>(kWebSocketNormalClosure) &&
591 unchecked_reason <=
592 static_cast<unsigned short>(kWebSocketErrorPrivateReservedMax)) {
593 *reason = unchecked_reason;
594 } else {
595 VLOG(1) << "Close frame contained reason code outside of the valid range: "
596 << unchecked_reason;
597 *reason = kWebSocketErrorProtocolError;
598 }
599 std::string text(data + kWebSocketCloseCodeLength, data + size);
600 // TODO(ricea): Is this check strict enough? In particular, check the
601 // "Security Considerations" from RFC3629.
602 if (IsStringUTF8(text)) {
603 using std::swap;
604 swap(*reason_text, text);
605 }
606 }
607
608 } // namespace net
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698