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

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

Issue 1820233002: [WebSocket] Incoming close frame should not overtake data frames (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: rebase Created 4 years, 8 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
« no previous file with comments | « net/websockets/websocket_channel.h ('k') | net/websockets/websocket_channel_test.cc » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
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 <stddef.h> 7 #include <stddef.h>
8 #include <limits.h> // for INT_MAX 8 #include <limits.h> // for INT_MAX
9 9
10 #include <algorithm> 10 #include <algorithm>
(...skipping 408 matching lines...) Expand 10 before | Expand all | Expand 10 after
419 // TODO(ricea): If current_send_quota_ has dropped below 419 // TODO(ricea): If current_send_quota_ has dropped below
420 // send_quota_low_water_mark_, it might be good to increase the "low 420 // send_quota_low_water_mark_, it might be good to increase the "low
421 // water mark" and "high water mark", but only if the link to the WebSocket 421 // water mark" and "high water mark", but only if the link to the WebSocket
422 // server is not saturated. 422 // server is not saturated.
423 scoped_refptr<IOBuffer> buffer(new IOBuffer(data.size())); 423 scoped_refptr<IOBuffer> buffer(new IOBuffer(data.size()));
424 std::copy(data.begin(), data.end(), buffer->data()); 424 std::copy(data.begin(), data.end(), buffer->data());
425 return SendFrameFromIOBuffer(fin, op_code, buffer, data.size()); 425 return SendFrameFromIOBuffer(fin, op_code, buffer, data.size());
426 // |this| may have been deleted. 426 // |this| may have been deleted.
427 } 427 }
428 428
429 void WebSocketChannel::SendFlowControl(int64_t quota) { 429 ChannelState WebSocketChannel::SendFlowControl(int64_t quota) {
430 DCHECK(state_ == CONNECTING || state_ == CONNECTED || state_ == SEND_CLOSED || 430 DCHECK(state_ == CONNECTING || state_ == CONNECTED || state_ == SEND_CLOSED ||
431 state_ == CLOSE_WAIT); 431 state_ == CLOSE_WAIT);
432 // TODO(ricea): Kill the renderer if it tries to send us a negative quota 432 // TODO(ricea): Kill the renderer if it tries to send us a negative quota
433 // value or > INT_MAX. 433 // value or > INT_MAX.
434 DCHECK_GE(quota, 0); 434 DCHECK_GE(quota, 0);
435 DCHECK_LE(quota, INT_MAX); 435 DCHECK_LE(quota, INT_MAX);
436 if (!pending_received_frames_.empty()) { 436 if (!pending_received_frames_.empty()) {
437 DCHECK_EQ(0u, current_receive_quota_); 437 DCHECK_EQ(0u, current_receive_quota_);
438 } 438 }
439 while (!pending_received_frames_.empty() && quota > 0) { 439 while (!pending_received_frames_.empty() && quota > 0) {
440 PendingReceivedFrame& front = pending_received_frames_.front(); 440 PendingReceivedFrame& front = pending_received_frames_.front();
441 const uint64_t data_size = front.size() - front.offset(); 441 const uint64_t data_size = front.size() - front.offset();
442 const uint64_t bytes_to_send = 442 const uint64_t bytes_to_send =
443 std::min(base::checked_cast<uint64_t>(quota), data_size); 443 std::min(base::checked_cast<uint64_t>(quota), data_size);
444 const bool final = front.final() && data_size == bytes_to_send; 444 const bool final = front.final() && data_size == bytes_to_send;
445 const char* data = 445 const char* data =
446 front.data().get() ? front.data()->data() + front.offset() : NULL; 446 front.data().get() ? front.data()->data() + front.offset() : NULL;
447 DCHECK(!bytes_to_send || data) << "Non empty data should not be null."; 447 DCHECK(!bytes_to_send || data) << "Non empty data should not be null.";
448 const std::vector<char> data_vector(data, data + bytes_to_send); 448 const std::vector<char> data_vector(data, data + bytes_to_send);
449 DVLOG(3) << "Sending frame previously split due to quota to the " 449 DVLOG(3) << "Sending frame previously split due to quota to the "
450 << "renderer: quota=" << quota << " data_size=" << data_size 450 << "renderer: quota=" << quota << " data_size=" << data_size
451 << " bytes_to_send=" << bytes_to_send; 451 << " bytes_to_send=" << bytes_to_send;
452 if (event_interface_->OnDataFrame(final, front.opcode(), data_vector) == 452 if (event_interface_->OnDataFrame(final, front.opcode(), data_vector) ==
453 CHANNEL_DELETED) 453 CHANNEL_DELETED)
454 return; 454 return CHANNEL_DELETED;
455 if (bytes_to_send < data_size) { 455 if (bytes_to_send < data_size) {
456 front.DidConsume(bytes_to_send); 456 front.DidConsume(bytes_to_send);
457 front.ResetOpcode(); 457 front.ResetOpcode();
458 return; 458 return CHANNEL_ALIVE;
459 } 459 }
460 quota -= bytes_to_send; 460 quota -= bytes_to_send;
461 461
462 pending_received_frames_.pop(); 462 pending_received_frames_.pop();
463 } 463 }
464 if (pending_received_frames_.empty() && has_received_close_frame_) {
465 // We've been waiting for the client to consume the frames before
466 // responding to the closing handshake initiated by the server.
467 return RespondToClosingHandshake();
468 }
469
464 // If current_receive_quota_ == 0 then there is no pending ReadFrames() 470 // If current_receive_quota_ == 0 then there is no pending ReadFrames()
465 // operation. 471 // operation.
466 const bool start_read = 472 const bool start_read =
467 current_receive_quota_ == 0 && quota > 0 && 473 current_receive_quota_ == 0 && quota > 0 &&
468 (state_ == CONNECTED || state_ == SEND_CLOSED || state_ == CLOSE_WAIT); 474 (state_ == CONNECTED || state_ == SEND_CLOSED || state_ == CLOSE_WAIT);
469 current_receive_quota_ += quota; 475 current_receive_quota_ += quota;
470 if (start_read) 476 if (start_read)
471 ignore_result(ReadFrames()); 477 return ReadFrames();
472 // |this| may have been deleted. 478 return CHANNEL_ALIVE;
473 } 479 }
474 480
475 void WebSocketChannel::StartClosingHandshake(uint16_t code, 481 ChannelState WebSocketChannel::StartClosingHandshake(
476 const std::string& reason) { 482 uint16_t code,
483 const std::string& reason) {
477 if (InClosingState()) { 484 if (InClosingState()) {
478 // When the associated renderer process is killed while the channel is in 485 // When the associated renderer process is killed while the channel is in
479 // CLOSING state we reach here. 486 // CLOSING state we reach here.
480 DVLOG(1) << "StartClosingHandshake called in state " << state_ 487 DVLOG(1) << "StartClosingHandshake called in state " << state_
481 << ". This may be a bug, or a harmless race."; 488 << ". This may be a bug, or a harmless race.";
482 return; 489 return CHANNEL_ALIVE;
490 }
491 if (has_received_close_frame_) {
492 // We reach here if the client wants to start a closing handshake while
493 // the browser is waiting for the client to consume incoming data frames
494 // before responding to a closing handshake initiated by the server.
495 // As the client doesn't want the data frames any more, we can respond to
496 // the closing handshake initiated by the server.
497 return RespondToClosingHandshake();
483 } 498 }
484 if (state_ == CONNECTING) { 499 if (state_ == CONNECTING) {
485 // Abort the in-progress handshake and drop the connection immediately. 500 // Abort the in-progress handshake and drop the connection immediately.
486 stream_request_.reset(); 501 stream_request_.reset();
487 SetState(CLOSED); 502 SetState(CLOSED);
488 DoDropChannel(false, kWebSocketErrorAbnormalClosure, ""); 503 return DoDropChannel(false, kWebSocketErrorAbnormalClosure, "");
489 return;
490 } 504 }
491 if (state_ != CONNECTED) { 505 if (state_ != CONNECTED) {
492 NOTREACHED() << "StartClosingHandshake() called in state " << state_; 506 NOTREACHED() << "StartClosingHandshake() called in state " << state_;
493 return; 507 return CHANNEL_ALIVE;
494 } 508 }
495 509
496 DCHECK(!close_timer_.IsRunning()); 510 DCHECK(!close_timer_.IsRunning());
497 // This use of base::Unretained() is safe because we stop the timer in the 511 // This use of base::Unretained() is safe because we stop the timer in the
498 // destructor. 512 // destructor.
499 close_timer_.Start( 513 close_timer_.Start(
500 FROM_HERE, 514 FROM_HERE,
501 closing_handshake_timeout_, 515 closing_handshake_timeout_,
502 base::Bind(&WebSocketChannel::CloseTimeout, base::Unretained(this))); 516 base::Bind(&WebSocketChannel::CloseTimeout, base::Unretained(this)));
503 517
504 // Javascript actually only permits 1000 and 3000-4999, but the implementation 518 // Javascript actually only permits 1000 and 3000-4999, but the implementation
505 // itself may produce different codes. The length of |reason| is also checked 519 // itself may produce different codes. The length of |reason| is also checked
506 // by Javascript. 520 // by Javascript.
507 if (!IsStrictlyValidCloseStatusCode(code) || 521 if (!IsStrictlyValidCloseStatusCode(code) ||
508 reason.size() > kMaximumCloseReasonLength) { 522 reason.size() > kMaximumCloseReasonLength) {
509 // "InternalServerError" is actually used for errors from any endpoint, per 523 // "InternalServerError" is actually used for errors from any endpoint, per
510 // errata 3227 to RFC6455. If the renderer is sending us an invalid code or 524 // errata 3227 to RFC6455. If the renderer is sending us an invalid code or
511 // reason it must be malfunctioning in some way, and based on that we 525 // reason it must be malfunctioning in some way, and based on that we
512 // interpret this as an internal error. 526 // interpret this as an internal error.
513 if (SendClose(kWebSocketErrorInternalServerError, "") != CHANNEL_DELETED) { 527 if (SendClose(kWebSocketErrorInternalServerError, "") == CHANNEL_DELETED)
514 DCHECK_EQ(CONNECTED, state_); 528 return CHANNEL_DELETED;
515 SetState(SEND_CLOSED); 529 DCHECK_EQ(CONNECTED, state_);
516 } 530 SetState(SEND_CLOSED);
517 return; 531 return CHANNEL_ALIVE;
518 } 532 }
519 if (SendClose( 533 if (SendClose(
520 code, 534 code,
521 StreamingUtf8Validator::Validate(reason) ? reason : std::string()) == 535 StreamingUtf8Validator::Validate(reason) ? reason : std::string()) ==
522 CHANNEL_DELETED) 536 CHANNEL_DELETED)
523 return; 537 return CHANNEL_DELETED;
524 DCHECK_EQ(CONNECTED, state_); 538 DCHECK_EQ(CONNECTED, state_);
525 SetState(SEND_CLOSED); 539 SetState(SEND_CLOSED);
540 return CHANNEL_ALIVE;
526 } 541 }
527 542
528 void WebSocketChannel::SendAddChannelRequestForTesting( 543 void WebSocketChannel::SendAddChannelRequestForTesting(
529 const GURL& socket_url, 544 const GURL& socket_url,
530 const std::vector<std::string>& requested_subprotocols, 545 const std::vector<std::string>& requested_subprotocols,
531 const url::Origin& origin, 546 const url::Origin& origin,
532 const WebSocketStreamCreator& creator) { 547 const WebSocketStreamCreator& creator) {
533 SendAddChannelRequestWithSuppliedCreator( 548 SendAddChannelRequestWithSuppliedCreator(
534 socket_url, requested_subprotocols, origin, creator); 549 socket_url, requested_subprotocols, origin, creator);
535 } 550 }
(...skipping 302 matching lines...) Expand 10 before | Expand all | Expand 10 after
838 true, WebSocketFrameHeader::kOpCodePong, data_buffer, size); 853 true, WebSocketFrameHeader::kOpCodePong, data_buffer, size);
839 DVLOG(3) << "Ignored ping in state " << state_; 854 DVLOG(3) << "Ignored ping in state " << state_;
840 return CHANNEL_ALIVE; 855 return CHANNEL_ALIVE;
841 856
842 case WebSocketFrameHeader::kOpCodePong: 857 case WebSocketFrameHeader::kOpCodePong:
843 DVLOG(1) << "Got Pong of size " << size; 858 DVLOG(1) << "Got Pong of size " << size;
844 // There is no need to do anything with pong messages. 859 // There is no need to do anything with pong messages.
845 return CHANNEL_ALIVE; 860 return CHANNEL_ALIVE;
846 861
847 case WebSocketFrameHeader::kOpCodeClose: { 862 case WebSocketFrameHeader::kOpCodeClose: {
848 // TODO(ricea): If there is a message which is queued for transmission to
849 // the renderer, then the renderer should not receive an
850 // OnClosingHandshake or OnDropChannel IPC until the queued message has
851 // been completedly transmitted.
852 uint16_t code = kWebSocketNormalClosure; 863 uint16_t code = kWebSocketNormalClosure;
853 std::string reason; 864 std::string reason;
854 std::string message; 865 std::string message;
855 if (!ParseClose(data_buffer, size, &code, &reason, &message)) { 866 if (!ParseClose(data_buffer, size, &code, &reason, &message)) {
856 return FailChannel(message, code, reason); 867 return FailChannel(message, code, reason);
857 } 868 }
858 // TODO(ricea): Find a way to safely log the message from the close 869 // TODO(ricea): Find a way to safely log the message from the close
859 // message (escape control codes and so on). 870 // message (escape control codes and so on).
860 DVLOG(1) << "Got Close with code " << code; 871 DVLOG(1) << "Got Close with code " << code;
861 switch (state_) { 872 return HandleCloseFrame(code, reason);
862 case CONNECTED:
863 SetState(RECV_CLOSED);
864
865 if (SendClose(code, reason) == CHANNEL_DELETED)
866 return CHANNEL_DELETED;
867 DCHECK_EQ(RECV_CLOSED, state_);
868
869 SetState(CLOSE_WAIT);
870 DCHECK(!close_timer_.IsRunning());
871 // This use of base::Unretained() is safe because we stop the timer
872 // in the destructor.
873 close_timer_.Start(
874 FROM_HERE,
875 underlying_connection_close_timeout_,
876 base::Bind(
877 &WebSocketChannel::CloseTimeout, base::Unretained(this)));
878
879 if (event_interface_->OnClosingHandshake() == CHANNEL_DELETED)
880 return CHANNEL_DELETED;
881 has_received_close_frame_ = true;
882 received_close_code_ = code;
883 received_close_reason_ = reason;
884 break;
885
886 case SEND_CLOSED:
887 SetState(CLOSE_WAIT);
888 DCHECK(close_timer_.IsRunning());
889 close_timer_.Stop();
890 // This use of base::Unretained() is safe because we stop the timer
891 // in the destructor.
892 close_timer_.Start(
893 FROM_HERE,
894 underlying_connection_close_timeout_,
895 base::Bind(
896 &WebSocketChannel::CloseTimeout, base::Unretained(this)));
897
898 // From RFC6455 section 7.1.5: "Each endpoint
899 // will see the status code sent by the other end as _The WebSocket
900 // Connection Close Code_."
901 has_received_close_frame_ = true;
902 received_close_code_ = code;
903 received_close_reason_ = reason;
904 break;
905
906 default:
907 LOG(DFATAL) << "Got Close in unexpected state " << state_;
908 break;
909 }
910 return CHANNEL_ALIVE;
911 } 873 }
912 874
913 default: 875 default:
914 return FailChannel( 876 return FailChannel(
915 base::StringPrintf("Unrecognized frame opcode: %d", opcode), 877 base::StringPrintf("Unrecognized frame opcode: %d", opcode),
916 kWebSocketErrorProtocolError, 878 kWebSocketErrorProtocolError,
917 "Unknown opcode"); 879 "Unknown opcode");
918 } 880 }
919 } 881 }
920 882
921 ChannelState WebSocketChannel::HandleDataFrame( 883 ChannelState WebSocketChannel::HandleDataFrame(
922 WebSocketFrameHeader::OpCode opcode, 884 WebSocketFrameHeader::OpCode opcode,
923 bool final, 885 bool final,
924 const scoped_refptr<IOBuffer>& data_buffer, 886 const scoped_refptr<IOBuffer>& data_buffer,
925 uint64_t size) { 887 uint64_t size) {
926 if (state_ != CONNECTED) { 888 if (state_ != CONNECTED) {
927 DVLOG(3) << "Ignored data packet received in state " << state_; 889 DVLOG(3) << "Ignored data packet received in state " << state_;
928 return CHANNEL_ALIVE; 890 return CHANNEL_ALIVE;
929 } 891 }
892 if (has_received_close_frame_) {
893 DVLOG(3) << "Ignored data packet as we've received a close frame.";
894 return CHANNEL_ALIVE;
895 }
930 DCHECK(opcode == WebSocketFrameHeader::kOpCodeContinuation || 896 DCHECK(opcode == WebSocketFrameHeader::kOpCodeContinuation ||
931 opcode == WebSocketFrameHeader::kOpCodeText || 897 opcode == WebSocketFrameHeader::kOpCodeText ||
932 opcode == WebSocketFrameHeader::kOpCodeBinary); 898 opcode == WebSocketFrameHeader::kOpCodeBinary);
933 const bool got_continuation = 899 const bool got_continuation =
934 (opcode == WebSocketFrameHeader::kOpCodeContinuation); 900 (opcode == WebSocketFrameHeader::kOpCodeContinuation);
935 if (got_continuation != expecting_to_handle_continuation_) { 901 if (got_continuation != expecting_to_handle_continuation_) {
936 const std::string console_log = got_continuation 902 const std::string console_log = got_continuation
937 ? "Received unexpected continuation frame." 903 ? "Received unexpected continuation frame."
938 : "Received start of new message but previous message is unfinished."; 904 : "Received start of new message but previous message is unfinished.";
939 const std::string reason = got_continuation 905 const std::string reason = got_continuation
(...skipping 47 matching lines...) Expand 10 before | Expand all | Expand 10 after
987 // TODO(ricea): Can this copy be eliminated? 953 // TODO(ricea): Can this copy be eliminated?
988 const char* const data_begin = size ? data_buffer->data() : NULL; 954 const char* const data_begin = size ? data_buffer->data() : NULL;
989 const char* const data_end = data_begin + size; 955 const char* const data_end = data_begin + size;
990 const std::vector<char> data(data_begin, data_end); 956 const std::vector<char> data(data_begin, data_end);
991 current_receive_quota_ -= size; 957 current_receive_quota_ -= size;
992 958
993 // Sends the received frame to the renderer process. 959 // Sends the received frame to the renderer process.
994 return event_interface_->OnDataFrame(final, opcode_to_send, data); 960 return event_interface_->OnDataFrame(final, opcode_to_send, data);
995 } 961 }
996 962
963 ChannelState WebSocketChannel::HandleCloseFrame(uint16_t code,
964 const std::string& reason) {
965 DVLOG(1) << "Got Close with code " << code;
966 switch (state_) {
967 case CONNECTED:
968 has_received_close_frame_ = true;
969 received_close_code_ = code;
970 received_close_reason_ = reason;
971 if (!pending_received_frames_.empty()) {
972 // We have some data to be sent to the renderer before sending this
973 // frame.
974 return CHANNEL_ALIVE;
975 }
976 return RespondToClosingHandshake();
977
978 case SEND_CLOSED:
979 SetState(CLOSE_WAIT);
980 DCHECK(close_timer_.IsRunning());
981 close_timer_.Stop();
982 // This use of base::Unretained() is safe because we stop the timer
983 // in the destructor.
984 close_timer_.Start(
985 FROM_HERE, underlying_connection_close_timeout_,
986 base::Bind(&WebSocketChannel::CloseTimeout, base::Unretained(this)));
987
988 // From RFC6455 section 7.1.5: "Each endpoint
989 // will see the status code sent by the other end as _The WebSocket
990 // Connection Close Code_."
991 has_received_close_frame_ = true;
992 received_close_code_ = code;
993 received_close_reason_ = reason;
994 break;
995
996 default:
997 LOG(DFATAL) << "Got Close in unexpected state " << state_;
998 break;
999 }
1000 return CHANNEL_ALIVE;
1001 }
1002
1003 ChannelState WebSocketChannel::RespondToClosingHandshake() {
1004 DCHECK(has_received_close_frame_);
1005 DCHECK_EQ(CONNECTED, state_);
1006 SetState(RECV_CLOSED);
1007 if (SendClose(received_close_code_, received_close_reason_) ==
1008 CHANNEL_DELETED)
1009 return CHANNEL_DELETED;
1010 DCHECK_EQ(RECV_CLOSED, state_);
1011
1012 SetState(CLOSE_WAIT);
1013 DCHECK(!close_timer_.IsRunning());
1014 // This use of base::Unretained() is safe because we stop the timer
1015 // in the destructor.
1016 close_timer_.Start(
1017 FROM_HERE, underlying_connection_close_timeout_,
1018 base::Bind(&WebSocketChannel::CloseTimeout, base::Unretained(this)));
1019
1020 return event_interface_->OnClosingHandshake();
1021 }
1022
997 ChannelState WebSocketChannel::SendFrameFromIOBuffer( 1023 ChannelState WebSocketChannel::SendFrameFromIOBuffer(
998 bool fin, 1024 bool fin,
999 WebSocketFrameHeader::OpCode op_code, 1025 WebSocketFrameHeader::OpCode op_code,
1000 const scoped_refptr<IOBuffer>& buffer, 1026 const scoped_refptr<IOBuffer>& buffer,
1001 uint64_t size) { 1027 uint64_t size) {
1002 DCHECK(state_ == CONNECTED || state_ == RECV_CLOSED); 1028 DCHECK(state_ == CONNECTED || state_ == RECV_CLOSED);
1003 DCHECK(stream_); 1029 DCHECK(stream_);
1004 1030
1005 scoped_ptr<WebSocketFrame> frame(new WebSocketFrame(op_code)); 1031 scoped_ptr<WebSocketFrame> frame(new WebSocketFrame(op_code));
1006 WebSocketFrameHeader& header = frame->header; 1032 WebSocketFrameHeader& header = frame->header;
(...skipping 135 matching lines...) Expand 10 before | Expand all | Expand 10 after
1142 } 1168 }
1143 1169
1144 void WebSocketChannel::CloseTimeout() { 1170 void WebSocketChannel::CloseTimeout() {
1145 stream_->Close(); 1171 stream_->Close();
1146 SetState(CLOSED); 1172 SetState(CLOSED);
1147 DoDropChannel(false, kWebSocketErrorAbnormalClosure, ""); 1173 DoDropChannel(false, kWebSocketErrorAbnormalClosure, "");
1148 // |this| has been deleted. 1174 // |this| has been deleted.
1149 } 1175 }
1150 1176
1151 } // namespace net 1177 } // namespace net
OLDNEW
« no previous file with comments | « net/websockets/websocket_channel.h ('k') | net/websockets/websocket_channel_test.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698