| OLD | NEW |
| 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 Loading... |
| 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 Loading... |
| 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 Loading... |
| 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 Loading... |
| 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 Loading... |
| 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 Loading... |
| 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 |
| OLD | NEW |