| OLD | NEW |
| (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 <limits.h> // for INT_MAX | |
| 8 | |
| 9 #include <algorithm> | |
| 10 #include <deque> | |
| 11 | |
| 12 #include "base/basictypes.h" // for size_t | |
| 13 #include "base/big_endian.h" | |
| 14 #include "base/bind.h" | |
| 15 #include "base/compiler_specific.h" | |
| 16 #include "base/memory/ref_counted.h" | |
| 17 #include "base/memory/weak_ptr.h" | |
| 18 #include "base/message_loop/message_loop.h" | |
| 19 #include "base/metrics/histogram.h" | |
| 20 #include "base/numerics/safe_conversions.h" | |
| 21 #include "base/stl_util.h" | |
| 22 #include "base/strings/stringprintf.h" | |
| 23 #include "base/time/time.h" | |
| 24 #include "net/base/io_buffer.h" | |
| 25 #include "net/base/net_log.h" | |
| 26 #include "net/http/http_request_headers.h" | |
| 27 #include "net/http/http_response_headers.h" | |
| 28 #include "net/http/http_util.h" | |
| 29 #include "net/websockets/websocket_errors.h" | |
| 30 #include "net/websockets/websocket_event_interface.h" | |
| 31 #include "net/websockets/websocket_frame.h" | |
| 32 #include "net/websockets/websocket_handshake_request_info.h" | |
| 33 #include "net/websockets/websocket_handshake_response_info.h" | |
| 34 #include "net/websockets/websocket_mux.h" | |
| 35 #include "net/websockets/websocket_stream.h" | |
| 36 #include "url/origin.h" | |
| 37 | |
| 38 namespace net { | |
| 39 | |
| 40 namespace { | |
| 41 | |
| 42 using base::StreamingUtf8Validator; | |
| 43 | |
| 44 const int kDefaultSendQuotaLowWaterMark = 1 << 16; | |
| 45 const int kDefaultSendQuotaHighWaterMark = 1 << 17; | |
| 46 const size_t kWebSocketCloseCodeLength = 2; | |
| 47 // Timeout for waiting for the server to acknowledge a closing handshake. | |
| 48 const int kClosingHandshakeTimeoutSeconds = 60; | |
| 49 // We wait for the server to close the underlying connection as recommended in | |
| 50 // https://tools.ietf.org/html/rfc6455#section-7.1.1 | |
| 51 // We don't use 2MSL since there're server implementations that don't follow | |
| 52 // the recommendation and wait for the client to close the underlying | |
| 53 // connection. It leads to unnecessarily long time before CloseEvent | |
| 54 // invocation. We want to avoid this rather than strictly following the spec | |
| 55 // recommendation. | |
| 56 const int kUnderlyingConnectionCloseTimeoutSeconds = 2; | |
| 57 | |
| 58 typedef WebSocketEventInterface::ChannelState ChannelState; | |
| 59 const ChannelState CHANNEL_ALIVE = WebSocketEventInterface::CHANNEL_ALIVE; | |
| 60 const ChannelState CHANNEL_DELETED = WebSocketEventInterface::CHANNEL_DELETED; | |
| 61 | |
| 62 // Maximum close reason length = max control frame payload - | |
| 63 // status code length | |
| 64 // = 125 - 2 | |
| 65 const size_t kMaximumCloseReasonLength = 125 - kWebSocketCloseCodeLength; | |
| 66 | |
| 67 // Check a close status code for strict compliance with RFC6455. This is only | |
| 68 // used for close codes received from a renderer that we are intending to send | |
| 69 // out over the network. See ParseClose() for the restrictions on incoming close | |
| 70 // codes. The |code| parameter is type int for convenience of implementation; | |
| 71 // the real type is uint16. Code 1005 is treated specially; it cannot be set | |
| 72 // explicitly by Javascript but the renderer uses it to indicate we should send | |
| 73 // a Close frame with no payload. | |
| 74 bool IsStrictlyValidCloseStatusCode(int code) { | |
| 75 static const int kInvalidRanges[] = { | |
| 76 // [BAD, OK) | |
| 77 0, 1000, // 1000 is the first valid code | |
| 78 1006, 1007, // 1006 MUST NOT be set. | |
| 79 1014, 3000, // 1014 unassigned; 1015 up to 2999 are reserved. | |
| 80 5000, 65536, // Codes above 5000 are invalid. | |
| 81 }; | |
| 82 const int* const kInvalidRangesEnd = | |
| 83 kInvalidRanges + arraysize(kInvalidRanges); | |
| 84 | |
| 85 DCHECK_GE(code, 0); | |
| 86 DCHECK_LT(code, 65536); | |
| 87 const int* upper = std::upper_bound(kInvalidRanges, kInvalidRangesEnd, code); | |
| 88 DCHECK_NE(kInvalidRangesEnd, upper); | |
| 89 DCHECK_GT(upper, kInvalidRanges); | |
| 90 DCHECK_GT(*upper, code); | |
| 91 DCHECK_LE(*(upper - 1), code); | |
| 92 return ((upper - kInvalidRanges) % 2) == 0; | |
| 93 } | |
| 94 | |
| 95 // Sets |name| to the name of the frame type for the given |opcode|. Note that | |
| 96 // for all of Text, Binary and Continuation opcode, this method returns | |
| 97 // "Data frame". | |
| 98 void GetFrameTypeForOpcode(WebSocketFrameHeader::OpCode opcode, | |
| 99 std::string* name) { | |
| 100 switch (opcode) { | |
| 101 case WebSocketFrameHeader::kOpCodeText: // fall-thru | |
| 102 case WebSocketFrameHeader::kOpCodeBinary: // fall-thru | |
| 103 case WebSocketFrameHeader::kOpCodeContinuation: | |
| 104 *name = "Data frame"; | |
| 105 break; | |
| 106 | |
| 107 case WebSocketFrameHeader::kOpCodePing: | |
| 108 *name = "Ping"; | |
| 109 break; | |
| 110 | |
| 111 case WebSocketFrameHeader::kOpCodePong: | |
| 112 *name = "Pong"; | |
| 113 break; | |
| 114 | |
| 115 case WebSocketFrameHeader::kOpCodeClose: | |
| 116 *name = "Close"; | |
| 117 break; | |
| 118 | |
| 119 default: | |
| 120 *name = "Unknown frame type"; | |
| 121 break; | |
| 122 } | |
| 123 | |
| 124 return; | |
| 125 } | |
| 126 | |
| 127 } // namespace | |
| 128 | |
| 129 // A class to encapsulate a set of frames and information about the size of | |
| 130 // those frames. | |
| 131 class WebSocketChannel::SendBuffer { | |
| 132 public: | |
| 133 SendBuffer() : total_bytes_(0) {} | |
| 134 | |
| 135 // Add a WebSocketFrame to the buffer and increase total_bytes_. | |
| 136 void AddFrame(scoped_ptr<WebSocketFrame> chunk); | |
| 137 | |
| 138 // Return a pointer to the frames_ for write purposes. | |
| 139 ScopedVector<WebSocketFrame>* frames() { return &frames_; } | |
| 140 | |
| 141 private: | |
| 142 // The frames_ that will be sent in the next call to WriteFrames(). | |
| 143 ScopedVector<WebSocketFrame> frames_; | |
| 144 | |
| 145 // The total size of the payload data in |frames_|. This will be used to | |
| 146 // measure the throughput of the link. | |
| 147 // TODO(ricea): Measure the throughput of the link. | |
| 148 uint64 total_bytes_; | |
| 149 }; | |
| 150 | |
| 151 void WebSocketChannel::SendBuffer::AddFrame(scoped_ptr<WebSocketFrame> frame) { | |
| 152 total_bytes_ += frame->header.payload_length; | |
| 153 frames_.push_back(frame.release()); | |
| 154 } | |
| 155 | |
| 156 // Implementation of WebSocketStream::ConnectDelegate that simply forwards the | |
| 157 // calls on to the WebSocketChannel that created it. | |
| 158 class WebSocketChannel::ConnectDelegate | |
| 159 : public WebSocketStream::ConnectDelegate { | |
| 160 public: | |
| 161 explicit ConnectDelegate(WebSocketChannel* creator) : creator_(creator) {} | |
| 162 | |
| 163 void OnSuccess(scoped_ptr<WebSocketStream> stream) override { | |
| 164 creator_->OnConnectSuccess(stream.Pass()); | |
| 165 // |this| may have been deleted. | |
| 166 } | |
| 167 | |
| 168 void OnFailure(const std::string& message) override { | |
| 169 creator_->OnConnectFailure(message); | |
| 170 // |this| has been deleted. | |
| 171 } | |
| 172 | |
| 173 void OnStartOpeningHandshake( | |
| 174 scoped_ptr<WebSocketHandshakeRequestInfo> request) override { | |
| 175 creator_->OnStartOpeningHandshake(request.Pass()); | |
| 176 } | |
| 177 | |
| 178 void OnFinishOpeningHandshake( | |
| 179 scoped_ptr<WebSocketHandshakeResponseInfo> response) override { | |
| 180 creator_->OnFinishOpeningHandshake(response.Pass()); | |
| 181 } | |
| 182 | |
| 183 void OnSSLCertificateError( | |
| 184 scoped_ptr<WebSocketEventInterface::SSLErrorCallbacks> | |
| 185 ssl_error_callbacks, | |
| 186 const SSLInfo& ssl_info, | |
| 187 bool fatal) override { | |
| 188 creator_->OnSSLCertificateError( | |
| 189 ssl_error_callbacks.Pass(), ssl_info, fatal); | |
| 190 } | |
| 191 | |
| 192 private: | |
| 193 // A pointer to the WebSocketChannel that created this object. There is no | |
| 194 // danger of this pointer being stale, because deleting the WebSocketChannel | |
| 195 // cancels the connect process, deleting this object and preventing its | |
| 196 // callbacks from being called. | |
| 197 WebSocketChannel* const creator_; | |
| 198 | |
| 199 DISALLOW_COPY_AND_ASSIGN(ConnectDelegate); | |
| 200 }; | |
| 201 | |
| 202 class WebSocketChannel::HandshakeNotificationSender | |
| 203 : public base::SupportsWeakPtr<HandshakeNotificationSender> { | |
| 204 public: | |
| 205 explicit HandshakeNotificationSender(WebSocketChannel* channel); | |
| 206 ~HandshakeNotificationSender(); | |
| 207 | |
| 208 static void Send(base::WeakPtr<HandshakeNotificationSender> sender); | |
| 209 | |
| 210 ChannelState SendImmediately(WebSocketEventInterface* event_interface); | |
| 211 | |
| 212 const WebSocketHandshakeRequestInfo* handshake_request_info() const { | |
| 213 return handshake_request_info_.get(); | |
| 214 } | |
| 215 | |
| 216 void set_handshake_request_info( | |
| 217 scoped_ptr<WebSocketHandshakeRequestInfo> request_info) { | |
| 218 handshake_request_info_ = request_info.Pass(); | |
| 219 } | |
| 220 | |
| 221 const WebSocketHandshakeResponseInfo* handshake_response_info() const { | |
| 222 return handshake_response_info_.get(); | |
| 223 } | |
| 224 | |
| 225 void set_handshake_response_info( | |
| 226 scoped_ptr<WebSocketHandshakeResponseInfo> response_info) { | |
| 227 handshake_response_info_ = response_info.Pass(); | |
| 228 } | |
| 229 | |
| 230 private: | |
| 231 WebSocketChannel* owner_; | |
| 232 scoped_ptr<WebSocketHandshakeRequestInfo> handshake_request_info_; | |
| 233 scoped_ptr<WebSocketHandshakeResponseInfo> handshake_response_info_; | |
| 234 }; | |
| 235 | |
| 236 WebSocketChannel::HandshakeNotificationSender::HandshakeNotificationSender( | |
| 237 WebSocketChannel* channel) | |
| 238 : owner_(channel) {} | |
| 239 | |
| 240 WebSocketChannel::HandshakeNotificationSender::~HandshakeNotificationSender() {} | |
| 241 | |
| 242 void WebSocketChannel::HandshakeNotificationSender::Send( | |
| 243 base::WeakPtr<HandshakeNotificationSender> sender) { | |
| 244 // Do nothing if |sender| is already destructed. | |
| 245 if (sender) { | |
| 246 WebSocketChannel* channel = sender->owner_; | |
| 247 sender->SendImmediately(channel->event_interface_.get()); | |
| 248 } | |
| 249 } | |
| 250 | |
| 251 ChannelState WebSocketChannel::HandshakeNotificationSender::SendImmediately( | |
| 252 WebSocketEventInterface* event_interface) { | |
| 253 | |
| 254 if (handshake_request_info_.get()) { | |
| 255 if (CHANNEL_DELETED == event_interface->OnStartOpeningHandshake( | |
| 256 handshake_request_info_.Pass())) | |
| 257 return CHANNEL_DELETED; | |
| 258 } | |
| 259 | |
| 260 if (handshake_response_info_.get()) { | |
| 261 if (CHANNEL_DELETED == event_interface->OnFinishOpeningHandshake( | |
| 262 handshake_response_info_.Pass())) | |
| 263 return CHANNEL_DELETED; | |
| 264 | |
| 265 // TODO(yhirano): We can release |this| to save memory because | |
| 266 // there will be no more opening handshake notification. | |
| 267 } | |
| 268 | |
| 269 return CHANNEL_ALIVE; | |
| 270 } | |
| 271 | |
| 272 WebSocketChannel::PendingReceivedFrame::PendingReceivedFrame( | |
| 273 bool final, | |
| 274 WebSocketFrameHeader::OpCode opcode, | |
| 275 const scoped_refptr<IOBuffer>& data, | |
| 276 uint64 offset, | |
| 277 uint64 size) | |
| 278 : final_(final), | |
| 279 opcode_(opcode), | |
| 280 data_(data), | |
| 281 offset_(offset), | |
| 282 size_(size) {} | |
| 283 | |
| 284 WebSocketChannel::PendingReceivedFrame::~PendingReceivedFrame() {} | |
| 285 | |
| 286 void WebSocketChannel::PendingReceivedFrame::ResetOpcode() { | |
| 287 DCHECK(WebSocketFrameHeader::IsKnownDataOpCode(opcode_)); | |
| 288 opcode_ = WebSocketFrameHeader::kOpCodeContinuation; | |
| 289 } | |
| 290 | |
| 291 void WebSocketChannel::PendingReceivedFrame::DidConsume(uint64 bytes) { | |
| 292 DCHECK_LE(offset_, size_); | |
| 293 DCHECK_LE(bytes, size_ - offset_); | |
| 294 offset_ += bytes; | |
| 295 } | |
| 296 | |
| 297 WebSocketChannel::WebSocketChannel( | |
| 298 scoped_ptr<WebSocketEventInterface> event_interface, | |
| 299 URLRequestContext* url_request_context) | |
| 300 : event_interface_(event_interface.Pass()), | |
| 301 url_request_context_(url_request_context), | |
| 302 send_quota_low_water_mark_(kDefaultSendQuotaLowWaterMark), | |
| 303 send_quota_high_water_mark_(kDefaultSendQuotaHighWaterMark), | |
| 304 current_send_quota_(0), | |
| 305 current_receive_quota_(0), | |
| 306 closing_handshake_timeout_(base::TimeDelta::FromSeconds( | |
| 307 kClosingHandshakeTimeoutSeconds)), | |
| 308 underlying_connection_close_timeout_(base::TimeDelta::FromSeconds( | |
| 309 kUnderlyingConnectionCloseTimeoutSeconds)), | |
| 310 has_received_close_frame_(false), | |
| 311 received_close_code_(0), | |
| 312 state_(FRESHLY_CONSTRUCTED), | |
| 313 notification_sender_(new HandshakeNotificationSender(this)), | |
| 314 sending_text_message_(false), | |
| 315 receiving_text_message_(false), | |
| 316 expecting_to_handle_continuation_(false), | |
| 317 initial_frame_forwarded_(false) {} | |
| 318 | |
| 319 WebSocketChannel::~WebSocketChannel() { | |
| 320 // The stream may hold a pointer to read_frames_, and so it needs to be | |
| 321 // destroyed first. | |
| 322 stream_.reset(); | |
| 323 // The timer may have a callback pointing back to us, so stop it just in case | |
| 324 // someone decides to run the event loop from their destructor. | |
| 325 close_timer_.Stop(); | |
| 326 } | |
| 327 | |
| 328 void WebSocketChannel::SendAddChannelRequest( | |
| 329 const GURL& socket_url, | |
| 330 const std::vector<std::string>& requested_subprotocols, | |
| 331 const url::Origin& origin) { | |
| 332 // Delegate to the tested version. | |
| 333 SendAddChannelRequestWithSuppliedCreator( | |
| 334 socket_url, | |
| 335 requested_subprotocols, | |
| 336 origin, | |
| 337 base::Bind(&WebSocketStream::CreateAndConnectStream)); | |
| 338 } | |
| 339 | |
| 340 void WebSocketChannel::SetState(State new_state) { | |
| 341 DCHECK_NE(state_, new_state); | |
| 342 | |
| 343 if (new_state == CONNECTED) | |
| 344 established_on_ = base::TimeTicks::Now(); | |
| 345 if (state_ == CONNECTED && !established_on_.is_null()) { | |
| 346 UMA_HISTOGRAM_LONG_TIMES( | |
| 347 "Net.WebSocket.Duration", base::TimeTicks::Now() - established_on_); | |
| 348 } | |
| 349 | |
| 350 state_ = new_state; | |
| 351 } | |
| 352 | |
| 353 bool WebSocketChannel::InClosingState() const { | |
| 354 // The state RECV_CLOSED is not supported here, because it is only used in one | |
| 355 // code path and should not leak into the code in general. | |
| 356 DCHECK_NE(RECV_CLOSED, state_) | |
| 357 << "InClosingState called with state_ == RECV_CLOSED"; | |
| 358 return state_ == SEND_CLOSED || state_ == CLOSE_WAIT || state_ == CLOSED; | |
| 359 } | |
| 360 | |
| 361 void WebSocketChannel::SendFrame(bool fin, | |
| 362 WebSocketFrameHeader::OpCode op_code, | |
| 363 const std::vector<char>& data) { | |
| 364 if (data.size() > INT_MAX) { | |
| 365 NOTREACHED() << "Frame size sanity check failed"; | |
| 366 return; | |
| 367 } | |
| 368 if (stream_ == NULL) { | |
| 369 LOG(DFATAL) << "Got SendFrame without a connection established; " | |
| 370 << "misbehaving renderer? fin=" << fin << " op_code=" << op_code | |
| 371 << " data.size()=" << data.size(); | |
| 372 return; | |
| 373 } | |
| 374 if (InClosingState()) { | |
| 375 DVLOG(1) << "SendFrame called in state " << state_ | |
| 376 << ". This may be a bug, or a harmless race."; | |
| 377 return; | |
| 378 } | |
| 379 if (state_ != CONNECTED) { | |
| 380 NOTREACHED() << "SendFrame() called in state " << state_; | |
| 381 return; | |
| 382 } | |
| 383 if (data.size() > base::checked_cast<size_t>(current_send_quota_)) { | |
| 384 // TODO(ricea): Kill renderer. | |
| 385 ignore_result( | |
| 386 FailChannel("Send quota exceeded", kWebSocketErrorGoingAway, "")); | |
| 387 // |this| has been deleted. | |
| 388 return; | |
| 389 } | |
| 390 if (!WebSocketFrameHeader::IsKnownDataOpCode(op_code)) { | |
| 391 LOG(DFATAL) << "Got SendFrame with bogus op_code " << op_code | |
| 392 << "; misbehaving renderer? fin=" << fin | |
| 393 << " data.size()=" << data.size(); | |
| 394 return; | |
| 395 } | |
| 396 if (op_code == WebSocketFrameHeader::kOpCodeText || | |
| 397 (op_code == WebSocketFrameHeader::kOpCodeContinuation && | |
| 398 sending_text_message_)) { | |
| 399 StreamingUtf8Validator::State state = | |
| 400 outgoing_utf8_validator_.AddBytes(vector_as_array(&data), data.size()); | |
| 401 if (state == StreamingUtf8Validator::INVALID || | |
| 402 (state == StreamingUtf8Validator::VALID_MIDPOINT && fin)) { | |
| 403 // TODO(ricea): Kill renderer. | |
| 404 ignore_result( | |
| 405 FailChannel("Browser sent a text frame containing invalid UTF-8", | |
| 406 kWebSocketErrorGoingAway, | |
| 407 "")); | |
| 408 // |this| has been deleted. | |
| 409 return; | |
| 410 } | |
| 411 sending_text_message_ = !fin; | |
| 412 DCHECK(!fin || state == StreamingUtf8Validator::VALID_ENDPOINT); | |
| 413 } | |
| 414 current_send_quota_ -= data.size(); | |
| 415 // TODO(ricea): If current_send_quota_ has dropped below | |
| 416 // send_quota_low_water_mark_, it might be good to increase the "low | |
| 417 // water mark" and "high water mark", but only if the link to the WebSocket | |
| 418 // server is not saturated. | |
| 419 scoped_refptr<IOBuffer> buffer(new IOBuffer(data.size())); | |
| 420 std::copy(data.begin(), data.end(), buffer->data()); | |
| 421 ignore_result(SendFrameFromIOBuffer(fin, op_code, buffer, data.size())); | |
| 422 // |this| may have been deleted. | |
| 423 } | |
| 424 | |
| 425 void WebSocketChannel::SendFlowControl(int64 quota) { | |
| 426 DCHECK(state_ == CONNECTING || state_ == CONNECTED || state_ == SEND_CLOSED || | |
| 427 state_ == CLOSE_WAIT); | |
| 428 // TODO(ricea): Kill the renderer if it tries to send us a negative quota | |
| 429 // value or > INT_MAX. | |
| 430 DCHECK_GE(quota, 0); | |
| 431 DCHECK_LE(quota, INT_MAX); | |
| 432 if (!pending_received_frames_.empty()) { | |
| 433 DCHECK_EQ(0u, current_receive_quota_); | |
| 434 } | |
| 435 while (!pending_received_frames_.empty() && quota > 0) { | |
| 436 PendingReceivedFrame& front = pending_received_frames_.front(); | |
| 437 const uint64 data_size = front.size() - front.offset(); | |
| 438 const uint64 bytes_to_send = | |
| 439 std::min(base::checked_cast<uint64>(quota), data_size); | |
| 440 const bool final = front.final() && data_size == bytes_to_send; | |
| 441 const char* data = | |
| 442 front.data().get() ? front.data()->data() + front.offset() : NULL; | |
| 443 DCHECK(!bytes_to_send || data) << "Non empty data should not be null."; | |
| 444 const std::vector<char> data_vector(data, data + bytes_to_send); | |
| 445 DVLOG(3) << "Sending frame previously split due to quota to the " | |
| 446 << "renderer: quota=" << quota << " data_size=" << data_size | |
| 447 << " bytes_to_send=" << bytes_to_send; | |
| 448 if (event_interface_->OnDataFrame(final, front.opcode(), data_vector) == | |
| 449 CHANNEL_DELETED) | |
| 450 return; | |
| 451 if (bytes_to_send < data_size) { | |
| 452 front.DidConsume(bytes_to_send); | |
| 453 front.ResetOpcode(); | |
| 454 return; | |
| 455 } | |
| 456 quota -= bytes_to_send; | |
| 457 | |
| 458 pending_received_frames_.pop(); | |
| 459 } | |
| 460 // If current_receive_quota_ == 0 then there is no pending ReadFrames() | |
| 461 // operation. | |
| 462 const bool start_read = | |
| 463 current_receive_quota_ == 0 && quota > 0 && | |
| 464 (state_ == CONNECTED || state_ == SEND_CLOSED || state_ == CLOSE_WAIT); | |
| 465 current_receive_quota_ += quota; | |
| 466 if (start_read) | |
| 467 ignore_result(ReadFrames()); | |
| 468 // |this| may have been deleted. | |
| 469 } | |
| 470 | |
| 471 void WebSocketChannel::StartClosingHandshake(uint16 code, | |
| 472 const std::string& reason) { | |
| 473 if (InClosingState()) { | |
| 474 // When the associated renderer process is killed while the channel is in | |
| 475 // CLOSING state we reach here. | |
| 476 DVLOG(1) << "StartClosingHandshake called in state " << state_ | |
| 477 << ". This may be a bug, or a harmless race."; | |
| 478 return; | |
| 479 } | |
| 480 if (state_ == CONNECTING) { | |
| 481 // Abort the in-progress handshake and drop the connection immediately. | |
| 482 stream_request_.reset(); | |
| 483 SetState(CLOSED); | |
| 484 DoDropChannel(false, kWebSocketErrorAbnormalClosure, ""); | |
| 485 return; | |
| 486 } | |
| 487 if (state_ != CONNECTED) { | |
| 488 NOTREACHED() << "StartClosingHandshake() called in state " << state_; | |
| 489 return; | |
| 490 } | |
| 491 | |
| 492 DCHECK(!close_timer_.IsRunning()); | |
| 493 // This use of base::Unretained() is safe because we stop the timer in the | |
| 494 // destructor. | |
| 495 close_timer_.Start( | |
| 496 FROM_HERE, | |
| 497 closing_handshake_timeout_, | |
| 498 base::Bind(&WebSocketChannel::CloseTimeout, base::Unretained(this))); | |
| 499 | |
| 500 // Javascript actually only permits 1000 and 3000-4999, but the implementation | |
| 501 // itself may produce different codes. The length of |reason| is also checked | |
| 502 // by Javascript. | |
| 503 if (!IsStrictlyValidCloseStatusCode(code) || | |
| 504 reason.size() > kMaximumCloseReasonLength) { | |
| 505 // "InternalServerError" is actually used for errors from any endpoint, per | |
| 506 // errata 3227 to RFC6455. If the renderer is sending us an invalid code or | |
| 507 // reason it must be malfunctioning in some way, and based on that we | |
| 508 // interpret this as an internal error. | |
| 509 if (SendClose(kWebSocketErrorInternalServerError, "") != CHANNEL_DELETED) { | |
| 510 DCHECK_EQ(CONNECTED, state_); | |
| 511 SetState(SEND_CLOSED); | |
| 512 } | |
| 513 return; | |
| 514 } | |
| 515 if (SendClose( | |
| 516 code, | |
| 517 StreamingUtf8Validator::Validate(reason) ? reason : std::string()) == | |
| 518 CHANNEL_DELETED) | |
| 519 return; | |
| 520 DCHECK_EQ(CONNECTED, state_); | |
| 521 SetState(SEND_CLOSED); | |
| 522 } | |
| 523 | |
| 524 void WebSocketChannel::SendAddChannelRequestForTesting( | |
| 525 const GURL& socket_url, | |
| 526 const std::vector<std::string>& requested_subprotocols, | |
| 527 const url::Origin& origin, | |
| 528 const WebSocketStreamCreator& creator) { | |
| 529 SendAddChannelRequestWithSuppliedCreator( | |
| 530 socket_url, requested_subprotocols, origin, creator); | |
| 531 } | |
| 532 | |
| 533 void WebSocketChannel::SetClosingHandshakeTimeoutForTesting( | |
| 534 base::TimeDelta delay) { | |
| 535 closing_handshake_timeout_ = delay; | |
| 536 } | |
| 537 | |
| 538 void WebSocketChannel::SetUnderlyingConnectionCloseTimeoutForTesting( | |
| 539 base::TimeDelta delay) { | |
| 540 underlying_connection_close_timeout_ = delay; | |
| 541 } | |
| 542 | |
| 543 void WebSocketChannel::SendAddChannelRequestWithSuppliedCreator( | |
| 544 const GURL& socket_url, | |
| 545 const std::vector<std::string>& requested_subprotocols, | |
| 546 const url::Origin& origin, | |
| 547 const WebSocketStreamCreator& creator) { | |
| 548 DCHECK_EQ(FRESHLY_CONSTRUCTED, state_); | |
| 549 if (!socket_url.SchemeIsWSOrWSS()) { | |
| 550 // TODO(ricea): Kill the renderer (this error should have been caught by | |
| 551 // Javascript). | |
| 552 ignore_result(event_interface_->OnAddChannelResponse(true, "", "")); | |
| 553 // |this| is deleted here. | |
| 554 return; | |
| 555 } | |
| 556 socket_url_ = socket_url; | |
| 557 scoped_ptr<WebSocketStream::ConnectDelegate> connect_delegate( | |
| 558 new ConnectDelegate(this)); | |
| 559 stream_request_ = creator.Run(socket_url_, | |
| 560 requested_subprotocols, | |
| 561 origin, | |
| 562 url_request_context_, | |
| 563 BoundNetLog(), | |
| 564 connect_delegate.Pass()); | |
| 565 SetState(CONNECTING); | |
| 566 } | |
| 567 | |
| 568 void WebSocketChannel::OnConnectSuccess(scoped_ptr<WebSocketStream> stream) { | |
| 569 DCHECK(stream); | |
| 570 DCHECK_EQ(CONNECTING, state_); | |
| 571 | |
| 572 stream_ = stream.Pass(); | |
| 573 | |
| 574 SetState(CONNECTED); | |
| 575 | |
| 576 if (event_interface_->OnAddChannelResponse( | |
| 577 false, stream_->GetSubProtocol(), stream_->GetExtensions()) == | |
| 578 CHANNEL_DELETED) | |
| 579 return; | |
| 580 | |
| 581 // TODO(ricea): Get flow control information from the WebSocketStream once we | |
| 582 // have a multiplexing WebSocketStream. | |
| 583 current_send_quota_ = send_quota_high_water_mark_; | |
| 584 if (event_interface_->OnFlowControl(send_quota_high_water_mark_) == | |
| 585 CHANNEL_DELETED) | |
| 586 return; | |
| 587 | |
| 588 // |stream_request_| is not used once the connection has succeeded. | |
| 589 stream_request_.reset(); | |
| 590 | |
| 591 ignore_result(ReadFrames()); | |
| 592 // |this| may have been deleted. | |
| 593 } | |
| 594 | |
| 595 void WebSocketChannel::OnConnectFailure(const std::string& message) { | |
| 596 DCHECK_EQ(CONNECTING, state_); | |
| 597 | |
| 598 // Copy the message before we delete its owner. | |
| 599 std::string message_copy = message; | |
| 600 | |
| 601 SetState(CLOSED); | |
| 602 stream_request_.reset(); | |
| 603 | |
| 604 if (CHANNEL_DELETED == | |
| 605 notification_sender_->SendImmediately(event_interface_.get())) { | |
| 606 // |this| has been deleted. | |
| 607 return; | |
| 608 } | |
| 609 ChannelState result = event_interface_->OnFailChannel(message_copy); | |
| 610 DCHECK_EQ(CHANNEL_DELETED, result); | |
| 611 // |this| has been deleted. | |
| 612 } | |
| 613 | |
| 614 void WebSocketChannel::OnSSLCertificateError( | |
| 615 scoped_ptr<WebSocketEventInterface::SSLErrorCallbacks> ssl_error_callbacks, | |
| 616 const SSLInfo& ssl_info, | |
| 617 bool fatal) { | |
| 618 ignore_result(event_interface_->OnSSLCertificateError( | |
| 619 ssl_error_callbacks.Pass(), socket_url_, ssl_info, fatal)); | |
| 620 } | |
| 621 | |
| 622 void WebSocketChannel::OnStartOpeningHandshake( | |
| 623 scoped_ptr<WebSocketHandshakeRequestInfo> request) { | |
| 624 DCHECK(!notification_sender_->handshake_request_info()); | |
| 625 | |
| 626 // Because it is hard to handle an IPC error synchronously is difficult, | |
| 627 // we asynchronously notify the information. | |
| 628 notification_sender_->set_handshake_request_info(request.Pass()); | |
| 629 ScheduleOpeningHandshakeNotification(); | |
| 630 } | |
| 631 | |
| 632 void WebSocketChannel::OnFinishOpeningHandshake( | |
| 633 scoped_ptr<WebSocketHandshakeResponseInfo> response) { | |
| 634 DCHECK(!notification_sender_->handshake_response_info()); | |
| 635 | |
| 636 // Because it is hard to handle an IPC error synchronously is difficult, | |
| 637 // we asynchronously notify the information. | |
| 638 notification_sender_->set_handshake_response_info(response.Pass()); | |
| 639 ScheduleOpeningHandshakeNotification(); | |
| 640 } | |
| 641 | |
| 642 void WebSocketChannel::ScheduleOpeningHandshakeNotification() { | |
| 643 base::MessageLoop::current()->PostTask( | |
| 644 FROM_HERE, | |
| 645 base::Bind(HandshakeNotificationSender::Send, | |
| 646 notification_sender_->AsWeakPtr())); | |
| 647 } | |
| 648 | |
| 649 ChannelState WebSocketChannel::WriteFrames() { | |
| 650 int result = OK; | |
| 651 do { | |
| 652 // This use of base::Unretained is safe because this object owns the | |
| 653 // WebSocketStream and destroying it cancels all callbacks. | |
| 654 result = stream_->WriteFrames( | |
| 655 data_being_sent_->frames(), | |
| 656 base::Bind(base::IgnoreResult(&WebSocketChannel::OnWriteDone), | |
| 657 base::Unretained(this), | |
| 658 false)); | |
| 659 if (result != ERR_IO_PENDING) { | |
| 660 if (OnWriteDone(true, result) == CHANNEL_DELETED) | |
| 661 return CHANNEL_DELETED; | |
| 662 // OnWriteDone() returns CHANNEL_DELETED on error. Here |state_| is | |
| 663 // guaranteed to be the same as before OnWriteDone() call. | |
| 664 } | |
| 665 } while (result == OK && data_being_sent_); | |
| 666 return CHANNEL_ALIVE; | |
| 667 } | |
| 668 | |
| 669 ChannelState WebSocketChannel::OnWriteDone(bool synchronous, int result) { | |
| 670 DCHECK_NE(FRESHLY_CONSTRUCTED, state_); | |
| 671 DCHECK_NE(CONNECTING, state_); | |
| 672 DCHECK_NE(ERR_IO_PENDING, result); | |
| 673 DCHECK(data_being_sent_); | |
| 674 switch (result) { | |
| 675 case OK: | |
| 676 if (data_to_send_next_) { | |
| 677 data_being_sent_ = data_to_send_next_.Pass(); | |
| 678 if (!synchronous) | |
| 679 return WriteFrames(); | |
| 680 } else { | |
| 681 data_being_sent_.reset(); | |
| 682 if (current_send_quota_ < send_quota_low_water_mark_) { | |
| 683 // TODO(ricea): Increase low_water_mark and high_water_mark if | |
| 684 // throughput is high, reduce them if throughput is low. Low water | |
| 685 // mark needs to be >= the bandwidth delay product *of the IPC | |
| 686 // channel*. Because factors like context-switch time, thread wake-up | |
| 687 // time, and bus speed come into play it is complex and probably needs | |
| 688 // to be determined empirically. | |
| 689 DCHECK_LE(send_quota_low_water_mark_, send_quota_high_water_mark_); | |
| 690 // TODO(ricea): Truncate quota by the quota specified by the remote | |
| 691 // server, if the protocol in use supports quota. | |
| 692 int fresh_quota = send_quota_high_water_mark_ - current_send_quota_; | |
| 693 current_send_quota_ += fresh_quota; | |
| 694 return event_interface_->OnFlowControl(fresh_quota); | |
| 695 } | |
| 696 } | |
| 697 return CHANNEL_ALIVE; | |
| 698 | |
| 699 // If a recoverable error condition existed, it would go here. | |
| 700 | |
| 701 default: | |
| 702 DCHECK_LT(result, 0) | |
| 703 << "WriteFrames() should only return OK or ERR_ codes"; | |
| 704 | |
| 705 stream_->Close(); | |
| 706 SetState(CLOSED); | |
| 707 return DoDropChannel(false, kWebSocketErrorAbnormalClosure, ""); | |
| 708 } | |
| 709 } | |
| 710 | |
| 711 ChannelState WebSocketChannel::ReadFrames() { | |
| 712 int result = OK; | |
| 713 while (result == OK && current_receive_quota_ > 0) { | |
| 714 // This use of base::Unretained is safe because this object owns the | |
| 715 // WebSocketStream, and any pending reads will be cancelled when it is | |
| 716 // destroyed. | |
| 717 result = stream_->ReadFrames( | |
| 718 &read_frames_, | |
| 719 base::Bind(base::IgnoreResult(&WebSocketChannel::OnReadDone), | |
| 720 base::Unretained(this), | |
| 721 false)); | |
| 722 if (result != ERR_IO_PENDING) { | |
| 723 if (OnReadDone(true, result) == CHANNEL_DELETED) | |
| 724 return CHANNEL_DELETED; | |
| 725 } | |
| 726 DCHECK_NE(CLOSED, state_); | |
| 727 } | |
| 728 return CHANNEL_ALIVE; | |
| 729 } | |
| 730 | |
| 731 ChannelState WebSocketChannel::OnReadDone(bool synchronous, int result) { | |
| 732 DCHECK_NE(FRESHLY_CONSTRUCTED, state_); | |
| 733 DCHECK_NE(CONNECTING, state_); | |
| 734 DCHECK_NE(ERR_IO_PENDING, result); | |
| 735 switch (result) { | |
| 736 case OK: | |
| 737 // ReadFrames() must use ERR_CONNECTION_CLOSED for a closed connection | |
| 738 // with no data read, not an empty response. | |
| 739 DCHECK(!read_frames_.empty()) | |
| 740 << "ReadFrames() returned OK, but nothing was read."; | |
| 741 for (size_t i = 0; i < read_frames_.size(); ++i) { | |
| 742 scoped_ptr<WebSocketFrame> frame(read_frames_[i]); | |
| 743 read_frames_[i] = NULL; | |
| 744 if (HandleFrame(frame.Pass()) == CHANNEL_DELETED) | |
| 745 return CHANNEL_DELETED; | |
| 746 } | |
| 747 read_frames_.clear(); | |
| 748 // There should always be a call to ReadFrames pending. | |
| 749 // TODO(ricea): Unless we are out of quota. | |
| 750 DCHECK_NE(CLOSED, state_); | |
| 751 if (!synchronous) | |
| 752 return ReadFrames(); | |
| 753 return CHANNEL_ALIVE; | |
| 754 | |
| 755 case ERR_WS_PROTOCOL_ERROR: | |
| 756 // This could be kWebSocketErrorProtocolError (specifically, non-minimal | |
| 757 // encoding of payload length) or kWebSocketErrorMessageTooBig, or an | |
| 758 // extension-specific error. | |
| 759 return FailChannel("Invalid frame header", | |
| 760 kWebSocketErrorProtocolError, | |
| 761 "WebSocket Protocol Error"); | |
| 762 | |
| 763 default: | |
| 764 DCHECK_LT(result, 0) | |
| 765 << "ReadFrames() should only return OK or ERR_ codes"; | |
| 766 | |
| 767 stream_->Close(); | |
| 768 SetState(CLOSED); | |
| 769 | |
| 770 uint16 code = kWebSocketErrorAbnormalClosure; | |
| 771 std::string reason = ""; | |
| 772 bool was_clean = false; | |
| 773 if (has_received_close_frame_) { | |
| 774 code = received_close_code_; | |
| 775 reason = received_close_reason_; | |
| 776 was_clean = (result == ERR_CONNECTION_CLOSED); | |
| 777 } | |
| 778 | |
| 779 return DoDropChannel(was_clean, code, reason); | |
| 780 } | |
| 781 } | |
| 782 | |
| 783 ChannelState WebSocketChannel::HandleFrame(scoped_ptr<WebSocketFrame> frame) { | |
| 784 if (frame->header.masked) { | |
| 785 // RFC6455 Section 5.1 "A client MUST close a connection if it detects a | |
| 786 // masked frame." | |
| 787 return FailChannel( | |
| 788 "A server must not mask any frames that it sends to the " | |
| 789 "client.", | |
| 790 kWebSocketErrorProtocolError, | |
| 791 "Masked frame from server"); | |
| 792 } | |
| 793 const WebSocketFrameHeader::OpCode opcode = frame->header.opcode; | |
| 794 DCHECK(!WebSocketFrameHeader::IsKnownControlOpCode(opcode) || | |
| 795 frame->header.final); | |
| 796 if (frame->header.reserved1 || frame->header.reserved2 || | |
| 797 frame->header.reserved3) { | |
| 798 return FailChannel(base::StringPrintf( | |
| 799 "One or more reserved bits are on: reserved1 = %d, " | |
| 800 "reserved2 = %d, reserved3 = %d", | |
| 801 static_cast<int>(frame->header.reserved1), | |
| 802 static_cast<int>(frame->header.reserved2), | |
| 803 static_cast<int>(frame->header.reserved3)), | |
| 804 kWebSocketErrorProtocolError, | |
| 805 "Invalid reserved bit"); | |
| 806 } | |
| 807 | |
| 808 // Respond to the frame appropriately to its type. | |
| 809 return HandleFrameByState( | |
| 810 opcode, frame->header.final, frame->data, frame->header.payload_length); | |
| 811 } | |
| 812 | |
| 813 ChannelState WebSocketChannel::HandleFrameByState( | |
| 814 const WebSocketFrameHeader::OpCode opcode, | |
| 815 bool final, | |
| 816 const scoped_refptr<IOBuffer>& data_buffer, | |
| 817 uint64 size) { | |
| 818 DCHECK_NE(RECV_CLOSED, state_) | |
| 819 << "HandleFrame() does not support being called re-entrantly from within " | |
| 820 "SendClose()"; | |
| 821 DCHECK_NE(CLOSED, state_); | |
| 822 if (state_ == CLOSE_WAIT) { | |
| 823 std::string frame_name; | |
| 824 GetFrameTypeForOpcode(opcode, &frame_name); | |
| 825 | |
| 826 // FailChannel() won't send another Close frame. | |
| 827 return FailChannel( | |
| 828 frame_name + " received after close", kWebSocketErrorProtocolError, ""); | |
| 829 } | |
| 830 switch (opcode) { | |
| 831 case WebSocketFrameHeader::kOpCodeText: // fall-thru | |
| 832 case WebSocketFrameHeader::kOpCodeBinary: | |
| 833 case WebSocketFrameHeader::kOpCodeContinuation: | |
| 834 return HandleDataFrame(opcode, final, data_buffer, size); | |
| 835 | |
| 836 case WebSocketFrameHeader::kOpCodePing: | |
| 837 DVLOG(1) << "Got Ping of size " << size; | |
| 838 if (state_ == CONNECTED) | |
| 839 return SendFrameFromIOBuffer( | |
| 840 true, WebSocketFrameHeader::kOpCodePong, data_buffer, size); | |
| 841 DVLOG(3) << "Ignored ping in state " << state_; | |
| 842 return CHANNEL_ALIVE; | |
| 843 | |
| 844 case WebSocketFrameHeader::kOpCodePong: | |
| 845 DVLOG(1) << "Got Pong of size " << size; | |
| 846 // There is no need to do anything with pong messages. | |
| 847 return CHANNEL_ALIVE; | |
| 848 | |
| 849 case WebSocketFrameHeader::kOpCodeClose: { | |
| 850 // TODO(ricea): If there is a message which is queued for transmission to | |
| 851 // the renderer, then the renderer should not receive an | |
| 852 // OnClosingHandshake or OnDropChannel IPC until the queued message has | |
| 853 // been completedly transmitted. | |
| 854 uint16 code = kWebSocketNormalClosure; | |
| 855 std::string reason; | |
| 856 std::string message; | |
| 857 if (!ParseClose(data_buffer, size, &code, &reason, &message)) { | |
| 858 return FailChannel(message, code, reason); | |
| 859 } | |
| 860 // TODO(ricea): Find a way to safely log the message from the close | |
| 861 // message (escape control codes and so on). | |
| 862 DVLOG(1) << "Got Close with code " << code; | |
| 863 switch (state_) { | |
| 864 case CONNECTED: | |
| 865 SetState(RECV_CLOSED); | |
| 866 | |
| 867 if (SendClose(code, reason) == CHANNEL_DELETED) | |
| 868 return CHANNEL_DELETED; | |
| 869 DCHECK_EQ(RECV_CLOSED, state_); | |
| 870 | |
| 871 SetState(CLOSE_WAIT); | |
| 872 DCHECK(!close_timer_.IsRunning()); | |
| 873 // This use of base::Unretained() is safe because we stop the timer | |
| 874 // in the destructor. | |
| 875 close_timer_.Start( | |
| 876 FROM_HERE, | |
| 877 underlying_connection_close_timeout_, | |
| 878 base::Bind( | |
| 879 &WebSocketChannel::CloseTimeout, base::Unretained(this))); | |
| 880 | |
| 881 if (event_interface_->OnClosingHandshake() == CHANNEL_DELETED) | |
| 882 return CHANNEL_DELETED; | |
| 883 has_received_close_frame_ = true; | |
| 884 received_close_code_ = code; | |
| 885 received_close_reason_ = reason; | |
| 886 break; | |
| 887 | |
| 888 case SEND_CLOSED: | |
| 889 SetState(CLOSE_WAIT); | |
| 890 DCHECK(close_timer_.IsRunning()); | |
| 891 close_timer_.Stop(); | |
| 892 // This use of base::Unretained() is safe because we stop the timer | |
| 893 // in the destructor. | |
| 894 close_timer_.Start( | |
| 895 FROM_HERE, | |
| 896 underlying_connection_close_timeout_, | |
| 897 base::Bind( | |
| 898 &WebSocketChannel::CloseTimeout, base::Unretained(this))); | |
| 899 | |
| 900 // From RFC6455 section 7.1.5: "Each endpoint | |
| 901 // will see the status code sent by the other end as _The WebSocket | |
| 902 // Connection Close Code_." | |
| 903 has_received_close_frame_ = true; | |
| 904 received_close_code_ = code; | |
| 905 received_close_reason_ = reason; | |
| 906 break; | |
| 907 | |
| 908 default: | |
| 909 LOG(DFATAL) << "Got Close in unexpected state " << state_; | |
| 910 break; | |
| 911 } | |
| 912 return CHANNEL_ALIVE; | |
| 913 } | |
| 914 | |
| 915 default: | |
| 916 return FailChannel( | |
| 917 base::StringPrintf("Unrecognized frame opcode: %d", opcode), | |
| 918 kWebSocketErrorProtocolError, | |
| 919 "Unknown opcode"); | |
| 920 } | |
| 921 } | |
| 922 | |
| 923 ChannelState WebSocketChannel::HandleDataFrame( | |
| 924 WebSocketFrameHeader::OpCode opcode, | |
| 925 bool final, | |
| 926 const scoped_refptr<IOBuffer>& data_buffer, | |
| 927 uint64 size) { | |
| 928 if (state_ != CONNECTED) { | |
| 929 DVLOG(3) << "Ignored data packet received in state " << state_; | |
| 930 return CHANNEL_ALIVE; | |
| 931 } | |
| 932 DCHECK(opcode == WebSocketFrameHeader::kOpCodeContinuation || | |
| 933 opcode == WebSocketFrameHeader::kOpCodeText || | |
| 934 opcode == WebSocketFrameHeader::kOpCodeBinary); | |
| 935 const bool got_continuation = | |
| 936 (opcode == WebSocketFrameHeader::kOpCodeContinuation); | |
| 937 if (got_continuation != expecting_to_handle_continuation_) { | |
| 938 const std::string console_log = got_continuation | |
| 939 ? "Received unexpected continuation frame." | |
| 940 : "Received start of new message but previous message is unfinished."; | |
| 941 const std::string reason = got_continuation | |
| 942 ? "Unexpected continuation" | |
| 943 : "Previous data frame unfinished"; | |
| 944 return FailChannel(console_log, kWebSocketErrorProtocolError, reason); | |
| 945 } | |
| 946 expecting_to_handle_continuation_ = !final; | |
| 947 WebSocketFrameHeader::OpCode opcode_to_send = opcode; | |
| 948 if (!initial_frame_forwarded_ && | |
| 949 opcode == WebSocketFrameHeader::kOpCodeContinuation) { | |
| 950 opcode_to_send = receiving_text_message_ | |
| 951 ? WebSocketFrameHeader::kOpCodeText | |
| 952 : WebSocketFrameHeader::kOpCodeBinary; | |
| 953 } | |
| 954 if (opcode == WebSocketFrameHeader::kOpCodeText || | |
| 955 (opcode == WebSocketFrameHeader::kOpCodeContinuation && | |
| 956 receiving_text_message_)) { | |
| 957 // This call is not redundant when size == 0 because it tells us what | |
| 958 // the current state is. | |
| 959 StreamingUtf8Validator::State state = incoming_utf8_validator_.AddBytes( | |
| 960 size ? data_buffer->data() : NULL, static_cast<size_t>(size)); | |
| 961 if (state == StreamingUtf8Validator::INVALID || | |
| 962 (state == StreamingUtf8Validator::VALID_MIDPOINT && final)) { | |
| 963 return FailChannel("Could not decode a text frame as UTF-8.", | |
| 964 kWebSocketErrorProtocolError, | |
| 965 "Invalid UTF-8 in text frame"); | |
| 966 } | |
| 967 receiving_text_message_ = !final; | |
| 968 DCHECK(!final || state == StreamingUtf8Validator::VALID_ENDPOINT); | |
| 969 } | |
| 970 if (size == 0U && !final) | |
| 971 return CHANNEL_ALIVE; | |
| 972 | |
| 973 initial_frame_forwarded_ = !final; | |
| 974 if (size > current_receive_quota_ || !pending_received_frames_.empty()) { | |
| 975 const bool no_quota = (current_receive_quota_ == 0); | |
| 976 DCHECK(no_quota || pending_received_frames_.empty()); | |
| 977 DVLOG(3) << "Queueing frame to renderer due to quota. quota=" | |
| 978 << current_receive_quota_ << " size=" << size; | |
| 979 WebSocketFrameHeader::OpCode opcode_to_queue = | |
| 980 no_quota ? opcode_to_send : WebSocketFrameHeader::kOpCodeContinuation; | |
| 981 pending_received_frames_.push(PendingReceivedFrame( | |
| 982 final, opcode_to_queue, data_buffer, current_receive_quota_, size)); | |
| 983 if (no_quota) | |
| 984 return CHANNEL_ALIVE; | |
| 985 size = current_receive_quota_; | |
| 986 final = false; | |
| 987 } | |
| 988 | |
| 989 // TODO(ricea): Can this copy be eliminated? | |
| 990 const char* const data_begin = size ? data_buffer->data() : NULL; | |
| 991 const char* const data_end = data_begin + size; | |
| 992 const std::vector<char> data(data_begin, data_end); | |
| 993 current_receive_quota_ -= size; | |
| 994 | |
| 995 // Sends the received frame to the renderer process. | |
| 996 return event_interface_->OnDataFrame(final, opcode_to_send, data); | |
| 997 } | |
| 998 | |
| 999 ChannelState WebSocketChannel::SendFrameFromIOBuffer( | |
| 1000 bool fin, | |
| 1001 WebSocketFrameHeader::OpCode op_code, | |
| 1002 const scoped_refptr<IOBuffer>& buffer, | |
| 1003 uint64 size) { | |
| 1004 DCHECK(state_ == CONNECTED || state_ == RECV_CLOSED); | |
| 1005 DCHECK(stream_); | |
| 1006 | |
| 1007 scoped_ptr<WebSocketFrame> frame(new WebSocketFrame(op_code)); | |
| 1008 WebSocketFrameHeader& header = frame->header; | |
| 1009 header.final = fin; | |
| 1010 header.masked = true; | |
| 1011 header.payload_length = size; | |
| 1012 frame->data = buffer; | |
| 1013 | |
| 1014 if (data_being_sent_) { | |
| 1015 // Either the link to the WebSocket server is saturated, or several messages | |
| 1016 // are being sent in a batch. | |
| 1017 // TODO(ricea): Keep some statistics to work out the situation and adjust | |
| 1018 // quota appropriately. | |
| 1019 if (!data_to_send_next_) | |
| 1020 data_to_send_next_.reset(new SendBuffer); | |
| 1021 data_to_send_next_->AddFrame(frame.Pass()); | |
| 1022 return CHANNEL_ALIVE; | |
| 1023 } | |
| 1024 | |
| 1025 data_being_sent_.reset(new SendBuffer); | |
| 1026 data_being_sent_->AddFrame(frame.Pass()); | |
| 1027 return WriteFrames(); | |
| 1028 } | |
| 1029 | |
| 1030 ChannelState WebSocketChannel::FailChannel(const std::string& message, | |
| 1031 uint16 code, | |
| 1032 const std::string& reason) { | |
| 1033 DCHECK_NE(FRESHLY_CONSTRUCTED, state_); | |
| 1034 DCHECK_NE(CONNECTING, state_); | |
| 1035 DCHECK_NE(CLOSED, state_); | |
| 1036 | |
| 1037 // TODO(ricea): Logging. | |
| 1038 if (state_ == CONNECTED) { | |
| 1039 if (SendClose(code, reason) == CHANNEL_DELETED) | |
| 1040 return CHANNEL_DELETED; | |
| 1041 } | |
| 1042 | |
| 1043 // Careful study of RFC6455 section 7.1.7 and 7.1.1 indicates the browser | |
| 1044 // should close the connection itself without waiting for the closing | |
| 1045 // handshake. | |
| 1046 stream_->Close(); | |
| 1047 SetState(CLOSED); | |
| 1048 ChannelState result = event_interface_->OnFailChannel(message); | |
| 1049 DCHECK_EQ(CHANNEL_DELETED, result); | |
| 1050 return result; | |
| 1051 } | |
| 1052 | |
| 1053 ChannelState WebSocketChannel::SendClose(uint16 code, | |
| 1054 const std::string& reason) { | |
| 1055 DCHECK(state_ == CONNECTED || state_ == RECV_CLOSED); | |
| 1056 DCHECK_LE(reason.size(), kMaximumCloseReasonLength); | |
| 1057 scoped_refptr<IOBuffer> body; | |
| 1058 uint64 size = 0; | |
| 1059 if (code == kWebSocketErrorNoStatusReceived) { | |
| 1060 // Special case: translate kWebSocketErrorNoStatusReceived into a Close | |
| 1061 // frame with no payload. | |
| 1062 DCHECK(reason.empty()); | |
| 1063 body = new IOBuffer(0); | |
| 1064 } else { | |
| 1065 const size_t payload_length = kWebSocketCloseCodeLength + reason.length(); | |
| 1066 body = new IOBuffer(payload_length); | |
| 1067 size = payload_length; | |
| 1068 base::WriteBigEndian(body->data(), code); | |
| 1069 static_assert(sizeof(code) == kWebSocketCloseCodeLength, | |
| 1070 "they should both be two"); | |
| 1071 std::copy( | |
| 1072 reason.begin(), reason.end(), body->data() + kWebSocketCloseCodeLength); | |
| 1073 } | |
| 1074 if (SendFrameFromIOBuffer( | |
| 1075 true, WebSocketFrameHeader::kOpCodeClose, body, size) == | |
| 1076 CHANNEL_DELETED) | |
| 1077 return CHANNEL_DELETED; | |
| 1078 return CHANNEL_ALIVE; | |
| 1079 } | |
| 1080 | |
| 1081 bool WebSocketChannel::ParseClose(const scoped_refptr<IOBuffer>& buffer, | |
| 1082 uint64 size, | |
| 1083 uint16* code, | |
| 1084 std::string* reason, | |
| 1085 std::string* message) { | |
| 1086 reason->clear(); | |
| 1087 if (size < kWebSocketCloseCodeLength) { | |
| 1088 if (size == 0U) { | |
| 1089 *code = kWebSocketErrorNoStatusReceived; | |
| 1090 return true; | |
| 1091 } | |
| 1092 | |
| 1093 DVLOG(1) << "Close frame with payload size " << size << " received " | |
| 1094 << "(the first byte is " << std::hex | |
| 1095 << static_cast<int>(buffer->data()[0]) << ")"; | |
| 1096 *code = kWebSocketErrorProtocolError; | |
| 1097 *message = | |
| 1098 "Received a broken close frame containing an invalid size body."; | |
| 1099 return false; | |
| 1100 } | |
| 1101 | |
| 1102 const char* data = buffer->data(); | |
| 1103 uint16 unchecked_code = 0; | |
| 1104 base::ReadBigEndian(data, &unchecked_code); | |
| 1105 static_assert(sizeof(unchecked_code) == kWebSocketCloseCodeLength, | |
| 1106 "they should both be two bytes"); | |
| 1107 | |
| 1108 switch (unchecked_code) { | |
| 1109 case kWebSocketErrorNoStatusReceived: | |
| 1110 case kWebSocketErrorAbnormalClosure: | |
| 1111 case kWebSocketErrorTlsHandshake: | |
| 1112 *code = kWebSocketErrorProtocolError; | |
| 1113 *message = | |
| 1114 "Received a broken close frame containing a reserved status code."; | |
| 1115 return false; | |
| 1116 | |
| 1117 default: | |
| 1118 *code = unchecked_code; | |
| 1119 break; | |
| 1120 } | |
| 1121 | |
| 1122 std::string text(data + kWebSocketCloseCodeLength, data + size); | |
| 1123 if (StreamingUtf8Validator::Validate(text)) { | |
| 1124 reason->swap(text); | |
| 1125 return true; | |
| 1126 } | |
| 1127 | |
| 1128 *code = kWebSocketErrorProtocolError; | |
| 1129 *reason = "Invalid UTF-8 in Close frame"; | |
| 1130 *message = "Received a broken close frame containing invalid UTF-8."; | |
| 1131 return false; | |
| 1132 } | |
| 1133 | |
| 1134 ChannelState WebSocketChannel::DoDropChannel(bool was_clean, | |
| 1135 uint16 code, | |
| 1136 const std::string& reason) { | |
| 1137 if (CHANNEL_DELETED == | |
| 1138 notification_sender_->SendImmediately(event_interface_.get())) | |
| 1139 return CHANNEL_DELETED; | |
| 1140 ChannelState result = | |
| 1141 event_interface_->OnDropChannel(was_clean, code, reason); | |
| 1142 DCHECK_EQ(CHANNEL_DELETED, result); | |
| 1143 return result; | |
| 1144 } | |
| 1145 | |
| 1146 void WebSocketChannel::CloseTimeout() { | |
| 1147 stream_->Close(); | |
| 1148 SetState(CLOSED); | |
| 1149 DoDropChannel(false, kWebSocketErrorAbnormalClosure, ""); | |
| 1150 // |this| has been deleted. | |
| 1151 } | |
| 1152 | |
| 1153 } // namespace net | |
| OLD | NEW |