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

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

Issue 23604044: Replace WebSocketFrameChunk with WebSocketFrame (Closed) Base URL: http://git.chromium.org/chromium/src.git@master
Patch Set: Fix partial control frame after headers case Created 7 years, 3 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
1 // Copyright 2013 The Chromium Authors. All rights reserved. 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 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "net/websockets/websocket_channel.h" 5 #include "net/websockets/websocket_channel.h"
6 6
7 #include <algorithm> 7 #include <algorithm>
8 8
9 #include "base/basictypes.h" // for size_t 9 #include "base/basictypes.h" // for size_t
10 #include "base/bind.h" 10 #include "base/bind.h"
11 #include "base/safe_numerics.h" 11 #include "base/safe_numerics.h"
12 #include "base/strings/string_util.h" 12 #include "base/strings/string_util.h"
13 #include "net/base/big_endian.h" 13 #include "net/base/big_endian.h"
14 #include "net/base/io_buffer.h" 14 #include "net/base/io_buffer.h"
15 #include "net/base/net_log.h" 15 #include "net/base/net_log.h"
16 #include "net/websockets/websocket_errors.h" 16 #include "net/websockets/websocket_errors.h"
17 #include "net/websockets/websocket_event_interface.h" 17 #include "net/websockets/websocket_event_interface.h"
18 #include "net/websockets/websocket_frame.h" 18 #include "net/websockets/websocket_frame.h"
19 #include "net/websockets/websocket_mux.h" 19 #include "net/websockets/websocket_mux.h"
20 #include "net/websockets/websocket_stream.h" 20 #include "net/websockets/websocket_stream.h"
21 21
22 namespace net { 22 namespace net {
23 23
24 namespace { 24 namespace {
25 25
26 const int kDefaultSendQuotaLowWaterMark = 1 << 16; 26 const int kDefaultSendQuotaLowWaterMark = 1 << 16;
27 const int kDefaultSendQuotaHighWaterMark = 1 << 17; 27 const int kDefaultSendQuotaHighWaterMark = 1 << 17;
28 const size_t kWebSocketCloseCodeLength = 2; 28 const size_t kWebSocketCloseCodeLength = 2;
29 29
30 // This uses type uint64 to match the definition of
31 // WebSocketFrameHeader::payload_length in websocket_frame.h.
32 const uint64 kMaxControlFramePayload = 125;
33
34 } // namespace 30 } // namespace
35 31
36 // A class to encapsulate a set of frames and information about the size of 32 // A class to encapsulate a set of frames and information about the size of
37 // those frames. 33 // those frames.
38 class WebSocketChannel::SendBuffer { 34 class WebSocketChannel::SendBuffer {
39 public: 35 public:
40 SendBuffer() : total_bytes_(0) {} 36 SendBuffer() : total_bytes_(0) {}
41 37
42 // Add a WebSocketFrameChunk to the buffer and increase total_bytes_. 38 // Add a WebSocketFrame to the buffer and increase total_bytes_.
43 void AddFrame(scoped_ptr<WebSocketFrameChunk> chunk); 39 void AddFrame(scoped_ptr<WebSocketFrame> chunk);
44 40
45 // Return a pointer to the frames_ for write purposes. 41 // Return a pointer to the frames_ for write purposes.
46 ScopedVector<WebSocketFrameChunk>* frames() { return &frames_; } 42 ScopedVector<WebSocketFrame>* frames() { return &frames_; }
47 43
48 private: 44 private:
49 // The frames_ that will be sent in the next call to WriteFrames(). 45 // The frames_ that will be sent in the next call to WriteFrames().
50 ScopedVector<WebSocketFrameChunk> frames_; 46 ScopedVector<WebSocketFrame> frames_;
51 47
52 // The total size of the buffers in |frames_|. This will be used to measure 48 // The total size of the payload data in |frames_|. This will be used to
53 // the throughput of the link. 49 // measure the throughput of the link.
54 // TODO(ricea): Measure the throughput of the link. 50 // TODO(ricea): Measure the throughput of the link.
55 size_t total_bytes_; 51 size_t total_bytes_;
56 }; 52 };
57 53
58 void WebSocketChannel::SendBuffer::AddFrame( 54 void WebSocketChannel::SendBuffer::AddFrame(scoped_ptr<WebSocketFrame> frame) {
59 scoped_ptr<WebSocketFrameChunk> chunk) { 55 total_bytes_ += frame->header.payload_length;
60 total_bytes_ += chunk->data->size(); 56 frames_.push_back(frame.release());
61 frames_.push_back(chunk.release());
62 } 57 }
63 58
64 // Implementation of WebSocketStream::ConnectDelegate that simply forwards the 59 // Implementation of WebSocketStream::ConnectDelegate that simply forwards the
65 // calls on to the WebSocketChannel that created it. 60 // calls on to the WebSocketChannel that created it.
66 class WebSocketChannel::ConnectDelegate 61 class WebSocketChannel::ConnectDelegate
67 : public WebSocketStream::ConnectDelegate { 62 : public WebSocketStream::ConnectDelegate {
68 public: 63 public:
69 explicit ConnectDelegate(WebSocketChannel* creator) : creator_(creator) {} 64 explicit ConnectDelegate(WebSocketChannel* creator) : creator_(creator) {}
70 65
71 virtual void OnSuccess(scoped_ptr<WebSocketStream> stream) OVERRIDE { 66 virtual void OnSuccess(scoped_ptr<WebSocketStream> stream) OVERRIDE {
(...skipping 19 matching lines...) Expand all
91 scoped_ptr<WebSocketEventInterface> event_interface) 86 scoped_ptr<WebSocketEventInterface> event_interface)
92 : socket_url_(socket_url), 87 : socket_url_(socket_url),
93 event_interface_(event_interface.Pass()), 88 event_interface_(event_interface.Pass()),
94 send_quota_low_water_mark_(kDefaultSendQuotaLowWaterMark), 89 send_quota_low_water_mark_(kDefaultSendQuotaLowWaterMark),
95 send_quota_high_water_mark_(kDefaultSendQuotaHighWaterMark), 90 send_quota_high_water_mark_(kDefaultSendQuotaHighWaterMark),
96 current_send_quota_(0), 91 current_send_quota_(0),
97 closing_code_(0), 92 closing_code_(0),
98 state_(FRESHLY_CONSTRUCTED) {} 93 state_(FRESHLY_CONSTRUCTED) {}
99 94
100 WebSocketChannel::~WebSocketChannel() { 95 WebSocketChannel::~WebSocketChannel() {
101 // The stream may hold a pointer to read_frame_chunks_, and so it needs to be 96 // The stream may hold a pointer to read_frames_, and so it needs to be
102 // destroyed first. 97 // destroyed first.
103 stream_.reset(); 98 stream_.reset();
104 } 99 }
105 100
106 void WebSocketChannel::SendAddChannelRequest( 101 void WebSocketChannel::SendAddChannelRequest(
107 const std::vector<std::string>& requested_subprotocols, 102 const std::vector<std::string>& requested_subprotocols,
108 const GURL& origin, 103 const GURL& origin,
109 URLRequestContext* url_request_context) { 104 URLRequestContext* url_request_context) {
110 // Delegate to the tested version. 105 // Delegate to the tested version.
111 SendAddChannelRequestWithFactory( 106 SendAddChannelRequestWithFactory(
(...skipping 44 matching lines...) Expand 10 before | Expand all | Expand 10 after
156 << "; misbehaving renderer? fin=" << fin 151 << "; misbehaving renderer? fin=" << fin
157 << " data.size()=" << data.size(); 152 << " data.size()=" << data.size();
158 return; 153 return;
159 } 154 }
160 current_send_quota_ -= data.size(); 155 current_send_quota_ -= data.size();
161 // TODO(ricea): If current_send_quota_ has dropped below 156 // TODO(ricea): If current_send_quota_ has dropped below
162 // send_quota_low_water_mark_, it might be good to increase the "low 157 // send_quota_low_water_mark_, it might be good to increase the "low
163 // water mark" and "high water mark", but only if the link to the WebSocket 158 // water mark" and "high water mark", but only if the link to the WebSocket
164 // server is not saturated. 159 // server is not saturated.
165 // TODO(ricea): For kOpCodeText, do UTF-8 validation? 160 // TODO(ricea): For kOpCodeText, do UTF-8 validation?
166 scoped_refptr<IOBufferWithSize> buffer(new IOBufferWithSize(data.size())); 161 scoped_refptr<IOBuffer> buffer(new IOBuffer(data.size()));
167 std::copy(data.begin(), data.end(), buffer->data()); 162 std::copy(data.begin(), data.end(), buffer->data());
168 SendIOBufferWithSize(fin, op_code, buffer); 163 SendIOBuffer(fin, op_code, buffer, data.size());
169 } 164 }
170 165
171 void WebSocketChannel::SendFlowControl(int64 quota) { 166 void WebSocketChannel::SendFlowControl(int64 quota) {
172 DCHECK_EQ(CONNECTED, state_); 167 DCHECK_EQ(CONNECTED, state_);
173 // TODO(ricea): Add interface to WebSocketStream and implement. 168 // TODO(ricea): Add interface to WebSocketStream and implement.
174 // stream_->SendFlowControl(quota); 169 // stream_->SendFlowControl(quota);
175 } 170 }
176 171
177 void WebSocketChannel::StartClosingHandshake(uint16 code, 172 void WebSocketChannel::StartClosingHandshake(uint16 code,
178 const std::string& reason) { 173 const std::string& reason) {
(...skipping 122 matching lines...) Expand 10 before | Expand all | Expand 10 after
301 } 296 }
302 } 297 }
303 298
304 void WebSocketChannel::ReadFrames() { 299 void WebSocketChannel::ReadFrames() {
305 int result = OK; 300 int result = OK;
306 do { 301 do {
307 // This use of base::Unretained is safe because this object owns the 302 // This use of base::Unretained is safe because this object owns the
308 // WebSocketStream, and any pending reads will be cancelled when it is 303 // WebSocketStream, and any pending reads will be cancelled when it is
309 // destroyed. 304 // destroyed.
310 result = stream_->ReadFrames( 305 result = stream_->ReadFrames(
311 &read_frame_chunks_, 306 &read_frames_,
312 base::Bind( 307 base::Bind(
313 &WebSocketChannel::OnReadDone, base::Unretained(this), false)); 308 &WebSocketChannel::OnReadDone, base::Unretained(this), false));
314 if (result != ERR_IO_PENDING) { 309 if (result != ERR_IO_PENDING) {
315 OnReadDone(true, result); 310 OnReadDone(true, result);
316 } 311 }
317 } while (result == OK && state_ != CLOSED); 312 } while (result == OK && state_ != CLOSED);
318 } 313 }
319 314
320 void WebSocketChannel::OnReadDone(bool synchronous, int result) { 315 void WebSocketChannel::OnReadDone(bool synchronous, int result) {
321 DCHECK_NE(FRESHLY_CONSTRUCTED, state_); 316 DCHECK_NE(FRESHLY_CONSTRUCTED, state_);
322 DCHECK_NE(CONNECTING, state_); 317 DCHECK_NE(CONNECTING, state_);
323 DCHECK_NE(ERR_IO_PENDING, result); 318 DCHECK_NE(ERR_IO_PENDING, result);
324 switch (result) { 319 switch (result) {
325 case OK: 320 case OK:
326 // ReadFrames() must use ERR_CONNECTION_CLOSED for a closed connection 321 // ReadFrames() must use ERR_CONNECTION_CLOSED for a closed connection
327 // with no data read, not an empty response. 322 // with no data read, not an empty response.
328 DCHECK(!read_frame_chunks_.empty()) 323 DCHECK(!read_frames_.empty())
329 << "ReadFrames() returned OK, but nothing was read."; 324 << "ReadFrames() returned OK, but nothing was read.";
330 for (size_t i = 0; i < read_frame_chunks_.size(); ++i) { 325 for (size_t i = 0; i < read_frames_.size(); ++i) {
331 scoped_ptr<WebSocketFrameChunk> chunk(read_frame_chunks_[i]); 326 scoped_ptr<WebSocketFrame> frame(read_frames_[i]);
332 read_frame_chunks_[i] = NULL; 327 read_frames_[i] = NULL;
333 ProcessFrameChunk(chunk.Pass()); 328 ProcessFrame(frame.Pass());
334 } 329 }
335 read_frame_chunks_.clear(); 330 read_frames_.clear();
336 // There should always be a call to ReadFrames pending. 331 // There should always be a call to ReadFrames pending.
332 // TODO(ricea): Unless we are out of quota.
337 if (!synchronous && state_ != CLOSED) { 333 if (!synchronous && state_ != CLOSED) {
338 ReadFrames(); 334 ReadFrames();
339 } 335 }
340 return; 336 return;
341 337
342 default: 338 default:
343 DCHECK_LT(result, 0) 339 DCHECK_LT(result, 0)
344 << "ReadFrames() should only return OK or ERR_ codes"; 340 << "ReadFrames() should only return OK or ERR_ codes";
345 stream_->Close(); 341 stream_->Close();
346 if (state_ != CLOSED) { 342 if (state_ != CLOSED) {
347 state_ = CLOSED; 343 state_ = CLOSED;
348 uint16 code = kWebSocketErrorAbnormalClosure; 344 uint16 code = kWebSocketErrorAbnormalClosure;
349 std::string reason = "Abnormal Closure"; 345 std::string reason = "Abnormal Closure";
350 if (closing_code_ != 0) { 346 if (closing_code_ != 0) {
351 code = closing_code_; 347 code = closing_code_;
352 reason = closing_reason_; 348 reason = closing_reason_;
353 } 349 }
354 event_interface_->OnDropChannel(code, reason); 350 event_interface_->OnDropChannel(code, reason);
355 } 351 }
356 return; 352 return;
357 } 353 }
358 } 354 }
359 355
360 void WebSocketChannel::ProcessFrameChunk( 356 void WebSocketChannel::ProcessFrame(scoped_ptr<WebSocketFrame> frame) {
361 scoped_ptr<WebSocketFrameChunk> chunk) { 357 if (frame->header.masked) {
362 bool is_first_chunk = false; 358 // RFC6455 Section 5.1 "A client MUST close a connection if it detects a
363 if (chunk->header) { 359 // masked frame."
364 DCHECK(current_frame_header_ == NULL) 360 FailChannel(SEND_REAL_ERROR,
365 << "Received the header for a new frame without notification that " 361 kWebSocketErrorProtocolError,
366 << "the previous frame was complete."; 362 "Masked frame from server");
367 is_first_chunk = true;
368 current_frame_header_.swap(chunk->header);
369 if (current_frame_header_->masked) {
370 // RFC6455 Section 5.1 "A client MUST close a connection if it detects a
371 // masked frame."
372 FailChannel(SEND_REAL_ERROR,
373 kWebSocketErrorProtocolError,
374 "Masked frame from server");
375 return;
376 }
377 }
378 if (!current_frame_header_) {
379 // If this channel rejected the previous chunk as invalid, then it will have
380 // reset |current_frame_header_| and closed the channel. More chunks of the
381 // invalid frame may still arrive, and it is not necessarily a bug for that
382 // to happen. However, if this happens when state_ is CONNECTED, it is
383 // definitely a bug.
384 DCHECK(state_ != CONNECTED) << "Unexpected header-less frame received "
385 << "(final_chunk = " << chunk->final_chunk
386 << ", data size = " << chunk->data->size()
387 << ")";
388 return; 363 return;
389 } 364 }
390 scoped_refptr<IOBufferWithSize> data_buffer; 365 scoped_refptr<IOBuffer> data_buffer;
391 data_buffer.swap(chunk->data); 366 data_buffer.swap(frame->data);
392 const bool is_final_chunk = chunk->final_chunk; 367 const WebSocketFrameHeader::OpCode opcode = frame->header.opcode;
393 chunk.reset(); 368 if (WebSocketFrameHeader::IsKnownControlOpCode(opcode) &&
394 const WebSocketFrameHeader::OpCode opcode = current_frame_header_->opcode; 369 !frame->header.final) {
395 if (WebSocketFrameHeader::IsKnownControlOpCode(opcode)) { 370 FailChannel(SEND_REAL_ERROR,
396 if (!current_frame_header_->final) { 371 kWebSocketErrorProtocolError,
397 FailChannel(SEND_REAL_ERROR, 372 "Control message with FIN bit unset received");
398 kWebSocketErrorProtocolError, 373 return;
399 "Control message with FIN bit unset received");
400 return;
401 }
402 if (current_frame_header_->payload_length > kMaxControlFramePayload) {
403 FailChannel(SEND_REAL_ERROR,
404 kWebSocketErrorProtocolError,
405 "Control message has payload over 125 bytes");
406 return;
407 }
408 if (!is_final_chunk) {
409 VLOG(2) << "Encountered a split control frame, opcode " << opcode;
410 if (incomplete_control_frame_body_) {
411 VLOG(3) << "Appending to an existing split control frame.";
412 AddToIncompleteControlFrameBody(data_buffer);
413 } else {
414 VLOG(3) << "Creating new storage for an incomplete control frame.";
415 incomplete_control_frame_body_ = new GrowableIOBuffer();
416 // This method checks for oversize control frames above, so as long as
417 // the frame parser is working correctly, this won't overflow. If a bug
418 // does cause it to overflow, it will CHECK() in
419 // AddToIncompleteControlFrameBody() without writing outside the buffer.
420 incomplete_control_frame_body_->SetCapacity(kMaxControlFramePayload);
421 AddToIncompleteControlFrameBody(data_buffer);
422 }
423 return; // Handle when complete.
424 }
425 if (incomplete_control_frame_body_) {
426 VLOG(2) << "Rejoining a split control frame, opcode " << opcode;
427 AddToIncompleteControlFrameBody(data_buffer);
428 const int body_size = incomplete_control_frame_body_->offset();
429 data_buffer = new IOBufferWithSize(body_size);
430 memcpy(data_buffer->data(),
431 incomplete_control_frame_body_->StartOfBuffer(),
432 body_size);
433 incomplete_control_frame_body_ = NULL; // Frame now complete.
434 }
435 } 374 }
436 375
437 // Apply basic sanity checks to the |payload_length| field from the frame
438 // header. A check for exact equality can only be used when the whole frame
439 // arrives in one chunk.
440 DCHECK_GE(current_frame_header_->payload_length,
441 base::checked_numeric_cast<uint64>(data_buffer->size()));
442 DCHECK(!is_first_chunk || !is_final_chunk ||
443 current_frame_header_->payload_length ==
444 base::checked_numeric_cast<uint64>(data_buffer->size()));
445
446 // Respond to the frame appropriately to its type. 376 // Respond to the frame appropriately to its type.
447 HandleFrame(opcode, is_first_chunk, is_final_chunk, data_buffer); 377 HandleFrame(
448 378 opcode, frame->header.final, data_buffer, frame->header.payload_length);
449 if (is_final_chunk) {
450 // Make sure that this frame header is not applied to any future chunks.
451 current_frame_header_.reset();
452 }
453 } 379 }
454 380
455 void WebSocketChannel::AddToIncompleteControlFrameBody( 381 void WebSocketChannel::HandleFrame(const WebSocketFrameHeader::OpCode opcode,
456 const scoped_refptr<IOBufferWithSize>& data_buffer) { 382 bool final,
457 const int new_offset = 383 const scoped_refptr<IOBuffer>& data_buffer,
458 incomplete_control_frame_body_->offset() + data_buffer->size(); 384 size_t size) {
459 CHECK_GE(incomplete_control_frame_body_->capacity(), new_offset)
460 << "Control frame body larger than frame header indicates; frame parser "
461 "bug?";
462 memcpy(incomplete_control_frame_body_->data(),
463 data_buffer->data(),
464 data_buffer->size());
465 incomplete_control_frame_body_->set_offset(new_offset);
466 }
467
468 void WebSocketChannel::HandleFrame(
469 const WebSocketFrameHeader::OpCode opcode,
470 bool is_first_chunk,
471 bool is_final_chunk,
472 const scoped_refptr<IOBufferWithSize>& data_buffer) {
473 DCHECK_NE(RECV_CLOSED, state_) 385 DCHECK_NE(RECV_CLOSED, state_)
474 << "HandleFrame() does not support being called re-entrantly from within " 386 << "HandleFrame() does not support being called re-entrantly from within "
475 "SendClose()"; 387 "SendClose()";
476 if (state_ == CLOSED || state_ == CLOSE_WAIT) { 388 if (state_ == CLOSED || state_ == CLOSE_WAIT) {
477 DVLOG_IF(1, state_ == CLOSED) << "A frame was received while in the CLOSED " 389 DVLOG_IF(1, state_ == CLOSED) << "A frame was received while in the CLOSED "
478 "state. This is possible after a channel " 390 "state. This is possible after a channel "
479 "failed, but should be very rare."; 391 "failed, but should be very rare.";
480 std::string frame_name; 392 std::string frame_name;
481 switch (opcode) { 393 switch (opcode) {
482 case WebSocketFrameHeader::kOpCodeText: // fall-thru 394 case WebSocketFrameHeader::kOpCodeText: // fall-thru
(...skipping 23 matching lines...) Expand all
506 FailChannel(SEND_REAL_ERROR, 418 FailChannel(SEND_REAL_ERROR,
507 kWebSocketErrorProtocolError, 419 kWebSocketErrorProtocolError,
508 frame_name + " received after close"); 420 frame_name + " received after close");
509 return; 421 return;
510 } 422 }
511 switch (opcode) { 423 switch (opcode) {
512 case WebSocketFrameHeader::kOpCodeText: // fall-thru 424 case WebSocketFrameHeader::kOpCodeText: // fall-thru
513 case WebSocketFrameHeader::kOpCodeBinary: // fall-thru 425 case WebSocketFrameHeader::kOpCodeBinary: // fall-thru
514 case WebSocketFrameHeader::kOpCodeContinuation: 426 case WebSocketFrameHeader::kOpCodeContinuation:
515 if (state_ == CONNECTED) { 427 if (state_ == CONNECTED) {
516 const bool final = is_final_chunk && current_frame_header_->final;
517 // TODO(ricea): Need to fail the connection if UTF-8 is invalid 428 // TODO(ricea): Need to fail the connection if UTF-8 is invalid
518 // post-reassembly. Requires a streaming UTF-8 validator. 429 // post-reassembly. Requires a streaming UTF-8 validator.
519 // TODO(ricea): Can this copy be eliminated? 430 // TODO(ricea): Can this copy be eliminated?
520 const char* const data_begin = data_buffer->data(); 431 const char* const data_begin = data_buffer->data();
521 const char* const data_end = data_begin + data_buffer->size(); 432 const char* const data_end = data_begin + size;
522 const std::vector<char> data(data_begin, data_end); 433 const std::vector<char> data(data_begin, data_end);
523 // TODO(ricea): Handle the case when ReadFrames returns far 434 // TODO(ricea): Handle the case when ReadFrames returns far
524 // more data at once than should be sent in a single IPC. This needs to 435 // more data at once than should be sent in a single IPC. This needs to
525 // be handled carefully, as an overloaded IO thread is one possible 436 // be handled carefully, as an overloaded IO thread is one possible
526 // cause of receiving very large chunks. 437 // cause of receiving very large chunks.
527 438
528 // Sends the received frame to the renderer process. 439 // Sends the received frame to the renderer process.
529 event_interface_->OnDataFrame( 440 event_interface_->OnDataFrame(final, opcode, data);
530 final,
531 is_first_chunk ? opcode : WebSocketFrameHeader::kOpCodeContinuation,
532 data);
533 } else { 441 } else {
534 VLOG(3) << "Ignored data packet received in state " << state_; 442 VLOG(3) << "Ignored data packet received in state " << state_;
535 } 443 }
536 return; 444 return;
537 445
538 case WebSocketFrameHeader::kOpCodePing: 446 case WebSocketFrameHeader::kOpCodePing:
539 VLOG(1) << "Got Ping of size " << data_buffer->size(); 447 VLOG(1) << "Got Ping of size " << size;
540 if (state_ == CONNECTED) { 448 if (state_ == CONNECTED) {
541 SendIOBufferWithSize( 449 SendIOBuffer(
542 true, WebSocketFrameHeader::kOpCodePong, data_buffer); 450 true, WebSocketFrameHeader::kOpCodePong, data_buffer, size);
543 } else { 451 } else {
544 VLOG(3) << "Ignored ping in state " << state_; 452 VLOG(3) << "Ignored ping in state " << state_;
545 } 453 }
546 return; 454 return;
547 455
548 case WebSocketFrameHeader::kOpCodePong: 456 case WebSocketFrameHeader::kOpCodePong:
549 VLOG(1) << "Got Pong of size " << data_buffer->size(); 457 VLOG(1) << "Got Pong of size " << size;
550 // There is no need to do anything with pong messages. 458 // There is no need to do anything with pong messages.
551 return; 459 return;
552 460
553 case WebSocketFrameHeader::kOpCodeClose: { 461 case WebSocketFrameHeader::kOpCodeClose: {
554 uint16 code = kWebSocketNormalClosure; 462 uint16 code = kWebSocketNormalClosure;
555 std::string reason; 463 std::string reason;
556 ParseClose(data_buffer, &code, &reason); 464 ParseClose(data_buffer, size, &code, &reason);
557 // TODO(ricea): Find a way to safely log the message from the close 465 // TODO(ricea): Find a way to safely log the message from the close
558 // message (escape control codes and so on). 466 // message (escape control codes and so on).
559 VLOG(1) << "Got Close with code " << code; 467 VLOG(1) << "Got Close with code " << code;
560 switch (state_) { 468 switch (state_) {
561 case CONNECTED: 469 case CONNECTED:
562 state_ = RECV_CLOSED; 470 state_ = RECV_CLOSED;
563 SendClose(code, reason); // Sets state_ to CLOSE_WAIT 471 SendClose(code, reason); // Sets state_ to CLOSE_WAIT
564 event_interface_->OnClosingHandshake(); 472 event_interface_->OnClosingHandshake();
565 closing_code_ = code; 473 closing_code_ = code;
566 closing_reason_ = reason; 474 closing_reason_ = reason;
(...skipping 15 matching lines...) Expand all
582 return; 490 return;
583 } 491 }
584 492
585 default: 493 default:
586 FailChannel( 494 FailChannel(
587 SEND_REAL_ERROR, kWebSocketErrorProtocolError, "Unknown opcode"); 495 SEND_REAL_ERROR, kWebSocketErrorProtocolError, "Unknown opcode");
588 return; 496 return;
589 } 497 }
590 } 498 }
591 499
592 void WebSocketChannel::SendIOBufferWithSize( 500 void WebSocketChannel::SendIOBuffer(bool fin,
593 bool fin, 501 WebSocketFrameHeader::OpCode op_code,
594 WebSocketFrameHeader::OpCode op_code, 502 const scoped_refptr<IOBuffer>& buffer,
595 const scoped_refptr<IOBufferWithSize>& buffer) { 503 size_t size) {
596 DCHECK(state_ == CONNECTED || state_ == RECV_CLOSED); 504 DCHECK(state_ == CONNECTED || state_ == RECV_CLOSED);
597 DCHECK(stream_); 505 DCHECK(stream_);
598 scoped_ptr<WebSocketFrameHeader> header(new WebSocketFrameHeader(op_code)); 506 scoped_ptr<WebSocketFrame> frame(new WebSocketFrame(op_code));
599 header->final = fin; 507 WebSocketFrameHeader& header = frame->header;
600 header->masked = true; 508 header.final = fin;
601 header->payload_length = buffer->size(); 509 header.masked = true;
602 scoped_ptr<WebSocketFrameChunk> chunk(new WebSocketFrameChunk()); 510 header.payload_length = size;
603 chunk->header = header.Pass(); 511 frame->data = buffer;
604 chunk->final_chunk = true;
605 chunk->data = buffer;
606 if (data_being_sent_) { 512 if (data_being_sent_) {
607 // Either the link to the WebSocket server is saturated, or several messages 513 // Either the link to the WebSocket server is saturated, or several messages
608 // are being sent in a batch. 514 // are being sent in a batch.
609 // TODO(ricea): Keep some statistics to work out the situation and adjust 515 // TODO(ricea): Keep some statistics to work out the situation and adjust
610 // quota appropriately. 516 // quota appropriately.
611 if (!data_to_send_next_) 517 if (!data_to_send_next_)
612 data_to_send_next_.reset(new SendBuffer); 518 data_to_send_next_.reset(new SendBuffer);
613 data_to_send_next_->AddFrame(chunk.Pass()); 519 data_to_send_next_->AddFrame(frame.Pass());
614 } else { 520 } else {
615 data_being_sent_.reset(new SendBuffer); 521 data_being_sent_.reset(new SendBuffer);
616 data_being_sent_->AddFrame(chunk.Pass()); 522 data_being_sent_->AddFrame(frame.Pass());
617 WriteFrames(); 523 WriteFrames();
618 } 524 }
619 } 525 }
620 526
621 void WebSocketChannel::FailChannel(ExposeError expose, 527 void WebSocketChannel::FailChannel(ExposeError expose,
622 uint16 code, 528 uint16 code,
623 const std::string& reason) { 529 const std::string& reason) {
624 DCHECK_NE(FRESHLY_CONSTRUCTED, state_); 530 DCHECK_NE(FRESHLY_CONSTRUCTED, state_);
625 DCHECK_NE(CONNECTING, state_); 531 DCHECK_NE(CONNECTING, state_);
626 // TODO(ricea): Logging. 532 // TODO(ricea): Logging.
627 State old_state = state_; 533 State old_state = state_;
628 if (state_ == CONNECTED) { 534 if (state_ == CONNECTED) {
629 uint16 send_code = kWebSocketErrorGoingAway; 535 uint16 send_code = kWebSocketErrorGoingAway;
630 std::string send_reason = "Internal Error"; 536 std::string send_reason = "Internal Error";
631 if (expose == SEND_REAL_ERROR) { 537 if (expose == SEND_REAL_ERROR) {
632 send_code = code; 538 send_code = code;
633 send_reason = reason; 539 send_reason = reason;
634 } 540 }
635 SendClose(send_code, send_reason); // Sets state_ to SEND_CLOSED 541 SendClose(send_code, send_reason); // Sets state_ to SEND_CLOSED
636 } 542 }
637 // Careful study of RFC6455 section 7.1.7 and 7.1.1 indicates the browser 543 // Careful study of RFC6455 section 7.1.7 and 7.1.1 indicates the browser
638 // should close the connection itself without waiting for the closing 544 // should close the connection itself without waiting for the closing
639 // handshake. 545 // handshake.
640 stream_->Close(); 546 stream_->Close();
641 state_ = CLOSED; 547 state_ = CLOSED;
642 548
643 // The channel may be in the middle of processing several chunks. It should
644 // not use this frame header for subsequent chunks.
645 current_frame_header_.reset();
646 if (old_state != CLOSED) { 549 if (old_state != CLOSED) {
647 event_interface_->OnDropChannel(code, reason); 550 event_interface_->OnDropChannel(code, reason);
648 } 551 }
649 } 552 }
650 553
651 void WebSocketChannel::SendClose(uint16 code, const std::string& reason) { 554 void WebSocketChannel::SendClose(uint16 code, const std::string& reason) {
652 DCHECK(state_ == CONNECTED || state_ == RECV_CLOSED); 555 DCHECK(state_ == CONNECTED || state_ == RECV_CLOSED);
653 // TODO(ricea): Ensure reason.length() <= 123 556 // TODO(ricea): Ensure reason.length() <= 123
654 scoped_refptr<IOBufferWithSize> body; 557 scoped_refptr<IOBuffer> body;
558 size_t size = 0;
655 if (code == kWebSocketErrorNoStatusReceived) { 559 if (code == kWebSocketErrorNoStatusReceived) {
656 // Special case: translate kWebSocketErrorNoStatusReceived into a Close 560 // Special case: translate kWebSocketErrorNoStatusReceived into a Close
657 // frame with no payload. 561 // frame with no payload.
658 body = new IOBufferWithSize(0); 562 body = new IOBuffer(0);
659 } else { 563 } else {
660 const size_t payload_length = kWebSocketCloseCodeLength + reason.length(); 564 const size_t payload_length = kWebSocketCloseCodeLength + reason.length();
661 body = new IOBufferWithSize(payload_length); 565 body = new IOBuffer(payload_length);
566 size = payload_length;
662 WriteBigEndian(body->data(), code); 567 WriteBigEndian(body->data(), code);
663 COMPILE_ASSERT(sizeof(code) == kWebSocketCloseCodeLength, 568 COMPILE_ASSERT(sizeof(code) == kWebSocketCloseCodeLength,
664 they_should_both_be_two); 569 they_should_both_be_two);
665 std::copy( 570 std::copy(
666 reason.begin(), reason.end(), body->data() + kWebSocketCloseCodeLength); 571 reason.begin(), reason.end(), body->data() + kWebSocketCloseCodeLength);
667 } 572 }
668 SendIOBufferWithSize(true, WebSocketFrameHeader::kOpCodeClose, body); 573 SendIOBuffer(true, WebSocketFrameHeader::kOpCodeClose, body, size);
669 state_ = (state_ == CONNECTED) ? SEND_CLOSED : CLOSE_WAIT; 574 state_ = (state_ == CONNECTED) ? SEND_CLOSED : CLOSE_WAIT;
670 } 575 }
671 576
672 void WebSocketChannel::ParseClose(const scoped_refptr<IOBufferWithSize>& buffer, 577 void WebSocketChannel::ParseClose(const scoped_refptr<IOBuffer>& buffer,
578 size_t size,
673 uint16* code, 579 uint16* code,
674 std::string* reason) { 580 std::string* reason) {
675 const char* data = buffer->data(); 581 const char* data = buffer->data();
676 size_t size = base::checked_numeric_cast<size_t>(buffer->size());
677 reason->clear(); 582 reason->clear();
678 if (size < kWebSocketCloseCodeLength) { 583 if (size < kWebSocketCloseCodeLength) {
679 *code = kWebSocketErrorNoStatusReceived; 584 *code = kWebSocketErrorNoStatusReceived;
680 if (size != 0) { 585 if (size != 0) {
681 VLOG(1) << "Close frame with payload size " << size << " received " 586 VLOG(1) << "Close frame with payload size " << size << " received "
682 << "(the first byte is " << std::hex << static_cast<int>(data[0]) 587 << "(the first byte is " << std::hex << static_cast<int>(data[0])
683 << ")"; 588 << ")";
684 return; 589 return;
685 } 590 }
686 return; 591 return;
(...skipping 13 matching lines...) Expand all
700 } 605 }
701 std::string text(data + kWebSocketCloseCodeLength, data + size); 606 std::string text(data + kWebSocketCloseCodeLength, data + size);
702 // TODO(ricea): Is this check strict enough? In particular, check the 607 // TODO(ricea): Is this check strict enough? In particular, check the
703 // "Security Considerations" from RFC3629. 608 // "Security Considerations" from RFC3629.
704 if (IsStringUTF8(text)) { 609 if (IsStringUTF8(text)) {
705 reason->swap(text); 610 reason->swap(text);
706 } 611 }
707 } 612 }
708 613
709 } // namespace net 614 } // namespace net
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698