| 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 |
| 338 case ERR_WS_PROTOCOL_ERROR: |
| 339 FailChannel(SEND_REAL_ERROR, |
| 340 kWebSocketErrorProtocolError, |
| 341 "WebSocket Protocol Error"); |
| 342 return; |
| 343 |
| 342 default: | 344 default: |
| 343 DCHECK_LT(result, 0) | 345 DCHECK_LT(result, 0) |
| 344 << "ReadFrames() should only return OK or ERR_ codes"; | 346 << "ReadFrames() should only return OK or ERR_ codes"; |
| 345 stream_->Close(); | 347 stream_->Close(); |
| 346 if (state_ != CLOSED) { | 348 if (state_ != CLOSED) { |
| 347 state_ = CLOSED; | 349 state_ = CLOSED; |
| 348 uint16 code = kWebSocketErrorAbnormalClosure; | 350 uint16 code = kWebSocketErrorAbnormalClosure; |
| 349 std::string reason = "Abnormal Closure"; | 351 std::string reason = "Abnormal Closure"; |
| 350 if (closing_code_ != 0) { | 352 if (closing_code_ != 0) { |
| 351 code = closing_code_; | 353 code = closing_code_; |
| 352 reason = closing_reason_; | 354 reason = closing_reason_; |
| 353 } | 355 } |
| 354 event_interface_->OnDropChannel(code, reason); | 356 event_interface_->OnDropChannel(code, reason); |
| 355 } | 357 } |
| 356 return; | 358 return; |
| 357 } | 359 } |
| 358 } | 360 } |
| 359 | 361 |
| 360 void WebSocketChannel::ProcessFrameChunk( | 362 void WebSocketChannel::ProcessFrame(scoped_ptr<WebSocketFrame> frame) { |
| 361 scoped_ptr<WebSocketFrameChunk> chunk) { | 363 if (frame->header.masked) { |
| 362 bool is_first_chunk = false; | 364 // RFC6455 Section 5.1 "A client MUST close a connection if it detects a |
| 363 if (chunk->header) { | 365 // masked frame." |
| 364 DCHECK(current_frame_header_ == NULL) | 366 FailChannel(SEND_REAL_ERROR, |
| 365 << "Received the header for a new frame without notification that " | 367 kWebSocketErrorProtocolError, |
| 366 << "the previous frame was complete."; | 368 "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; | 369 return; |
| 389 } | 370 } |
| 390 scoped_refptr<IOBufferWithSize> data_buffer; | 371 const WebSocketFrameHeader::OpCode opcode = frame->header.opcode; |
| 391 data_buffer.swap(chunk->data); | 372 if (WebSocketFrameHeader::IsKnownControlOpCode(opcode) && |
| 392 const bool is_final_chunk = chunk->final_chunk; | 373 !frame->header.final) { |
| 393 chunk.reset(); | 374 FailChannel(SEND_REAL_ERROR, |
| 394 const WebSocketFrameHeader::OpCode opcode = current_frame_header_->opcode; | 375 kWebSocketErrorProtocolError, |
| 395 if (WebSocketFrameHeader::IsKnownControlOpCode(opcode)) { | 376 "Control message with FIN bit unset received"); |
| 396 if (!current_frame_header_->final) { | 377 return; |
| 397 FailChannel(SEND_REAL_ERROR, | |
| 398 kWebSocketErrorProtocolError, | |
| 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 } | 378 } |
| 436 | 379 |
| 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. | 380 // Respond to the frame appropriately to its type. |
| 447 HandleFrame(opcode, is_first_chunk, is_final_chunk, data_buffer); | 381 HandleFrame( |
| 448 | 382 opcode, frame->header.final, frame->data, 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 } | 383 } |
| 454 | 384 |
| 455 void WebSocketChannel::AddToIncompleteControlFrameBody( | 385 void WebSocketChannel::HandleFrame(const WebSocketFrameHeader::OpCode opcode, |
| 456 const scoped_refptr<IOBufferWithSize>& data_buffer) { | 386 bool final, |
| 457 const int new_offset = | 387 const scoped_refptr<IOBuffer>& data_buffer, |
| 458 incomplete_control_frame_body_->offset() + data_buffer->size(); | 388 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_) | 389 DCHECK_NE(RECV_CLOSED, state_) |
| 474 << "HandleFrame() does not support being called re-entrantly from within " | 390 << "HandleFrame() does not support being called re-entrantly from within " |
| 475 "SendClose()"; | 391 "SendClose()"; |
| 476 if (state_ == CLOSED || state_ == CLOSE_WAIT) { | 392 if (state_ == CLOSED || state_ == CLOSE_WAIT) { |
| 477 DVLOG_IF(1, state_ == CLOSED) << "A frame was received while in the CLOSED " | 393 DVLOG_IF(1, state_ == CLOSED) << "A frame was received while in the CLOSED " |
| 478 "state. This is possible after a channel " | 394 "state. This is possible after a channel " |
| 479 "failed, but should be very rare."; | 395 "failed, but should be very rare."; |
| 480 std::string frame_name; | 396 std::string frame_name; |
| 481 switch (opcode) { | 397 switch (opcode) { |
| 482 case WebSocketFrameHeader::kOpCodeText: // fall-thru | 398 case WebSocketFrameHeader::kOpCodeText: // fall-thru |
| (...skipping 23 matching lines...) Expand all Loading... |
| 506 FailChannel(SEND_REAL_ERROR, | 422 FailChannel(SEND_REAL_ERROR, |
| 507 kWebSocketErrorProtocolError, | 423 kWebSocketErrorProtocolError, |
| 508 frame_name + " received after close"); | 424 frame_name + " received after close"); |
| 509 return; | 425 return; |
| 510 } | 426 } |
| 511 switch (opcode) { | 427 switch (opcode) { |
| 512 case WebSocketFrameHeader::kOpCodeText: // fall-thru | 428 case WebSocketFrameHeader::kOpCodeText: // fall-thru |
| 513 case WebSocketFrameHeader::kOpCodeBinary: // fall-thru | 429 case WebSocketFrameHeader::kOpCodeBinary: // fall-thru |
| 514 case WebSocketFrameHeader::kOpCodeContinuation: | 430 case WebSocketFrameHeader::kOpCodeContinuation: |
| 515 if (state_ == CONNECTED) { | 431 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 | 432 // TODO(ricea): Need to fail the connection if UTF-8 is invalid |
| 518 // post-reassembly. Requires a streaming UTF-8 validator. | 433 // post-reassembly. Requires a streaming UTF-8 validator. |
| 519 // TODO(ricea): Can this copy be eliminated? | 434 // TODO(ricea): Can this copy be eliminated? |
| 520 const char* const data_begin = data_buffer->data(); | 435 const char* const data_begin = data_buffer->data(); |
| 521 const char* const data_end = data_begin + data_buffer->size(); | 436 const char* const data_end = data_begin + size; |
| 522 const std::vector<char> data(data_begin, data_end); | 437 const std::vector<char> data(data_begin, data_end); |
| 523 // TODO(ricea): Handle the case when ReadFrames returns far | 438 // 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 | 439 // 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 | 440 // be handled carefully, as an overloaded IO thread is one possible |
| 526 // cause of receiving very large chunks. | 441 // cause of receiving very large chunks. |
| 527 | 442 |
| 528 // Sends the received frame to the renderer process. | 443 // Sends the received frame to the renderer process. |
| 529 event_interface_->OnDataFrame( | 444 event_interface_->OnDataFrame(final, opcode, data); |
| 530 final, | |
| 531 is_first_chunk ? opcode : WebSocketFrameHeader::kOpCodeContinuation, | |
| 532 data); | |
| 533 } else { | 445 } else { |
| 534 VLOG(3) << "Ignored data packet received in state " << state_; | 446 VLOG(3) << "Ignored data packet received in state " << state_; |
| 535 } | 447 } |
| 536 return; | 448 return; |
| 537 | 449 |
| 538 case WebSocketFrameHeader::kOpCodePing: | 450 case WebSocketFrameHeader::kOpCodePing: |
| 539 VLOG(1) << "Got Ping of size " << data_buffer->size(); | 451 VLOG(1) << "Got Ping of size " << size; |
| 540 if (state_ == CONNECTED) { | 452 if (state_ == CONNECTED) { |
| 541 SendIOBufferWithSize( | 453 SendIOBuffer( |
| 542 true, WebSocketFrameHeader::kOpCodePong, data_buffer); | 454 true, WebSocketFrameHeader::kOpCodePong, data_buffer, size); |
| 543 } else { | 455 } else { |
| 544 VLOG(3) << "Ignored ping in state " << state_; | 456 VLOG(3) << "Ignored ping in state " << state_; |
| 545 } | 457 } |
| 546 return; | 458 return; |
| 547 | 459 |
| 548 case WebSocketFrameHeader::kOpCodePong: | 460 case WebSocketFrameHeader::kOpCodePong: |
| 549 VLOG(1) << "Got Pong of size " << data_buffer->size(); | 461 VLOG(1) << "Got Pong of size " << size; |
| 550 // There is no need to do anything with pong messages. | 462 // There is no need to do anything with pong messages. |
| 551 return; | 463 return; |
| 552 | 464 |
| 553 case WebSocketFrameHeader::kOpCodeClose: { | 465 case WebSocketFrameHeader::kOpCodeClose: { |
| 554 uint16 code = kWebSocketNormalClosure; | 466 uint16 code = kWebSocketNormalClosure; |
| 555 std::string reason; | 467 std::string reason; |
| 556 ParseClose(data_buffer, &code, &reason); | 468 ParseClose(data_buffer, size, &code, &reason); |
| 557 // TODO(ricea): Find a way to safely log the message from the close | 469 // TODO(ricea): Find a way to safely log the message from the close |
| 558 // message (escape control codes and so on). | 470 // message (escape control codes and so on). |
| 559 VLOG(1) << "Got Close with code " << code; | 471 VLOG(1) << "Got Close with code " << code; |
| 560 switch (state_) { | 472 switch (state_) { |
| 561 case CONNECTED: | 473 case CONNECTED: |
| 562 state_ = RECV_CLOSED; | 474 state_ = RECV_CLOSED; |
| 563 SendClose(code, reason); // Sets state_ to CLOSE_WAIT | 475 SendClose(code, reason); // Sets state_ to CLOSE_WAIT |
| 564 event_interface_->OnClosingHandshake(); | 476 event_interface_->OnClosingHandshake(); |
| 565 closing_code_ = code; | 477 closing_code_ = code; |
| 566 closing_reason_ = reason; | 478 closing_reason_ = reason; |
| (...skipping 15 matching lines...) Expand all Loading... |
| 582 return; | 494 return; |
| 583 } | 495 } |
| 584 | 496 |
| 585 default: | 497 default: |
| 586 FailChannel( | 498 FailChannel( |
| 587 SEND_REAL_ERROR, kWebSocketErrorProtocolError, "Unknown opcode"); | 499 SEND_REAL_ERROR, kWebSocketErrorProtocolError, "Unknown opcode"); |
| 588 return; | 500 return; |
| 589 } | 501 } |
| 590 } | 502 } |
| 591 | 503 |
| 592 void WebSocketChannel::SendIOBufferWithSize( | 504 void WebSocketChannel::SendIOBuffer(bool fin, |
| 593 bool fin, | 505 WebSocketFrameHeader::OpCode op_code, |
| 594 WebSocketFrameHeader::OpCode op_code, | 506 const scoped_refptr<IOBuffer>& buffer, |
| 595 const scoped_refptr<IOBufferWithSize>& buffer) { | 507 size_t size) { |
| 596 DCHECK(state_ == CONNECTED || state_ == RECV_CLOSED); | 508 DCHECK(state_ == CONNECTED || state_ == RECV_CLOSED); |
| 597 DCHECK(stream_); | 509 DCHECK(stream_); |
| 598 scoped_ptr<WebSocketFrameHeader> header(new WebSocketFrameHeader(op_code)); | 510 scoped_ptr<WebSocketFrame> frame(new WebSocketFrame(op_code)); |
| 599 header->final = fin; | 511 WebSocketFrameHeader& header = frame->header; |
| 600 header->masked = true; | 512 header.final = fin; |
| 601 header->payload_length = buffer->size(); | 513 header.masked = true; |
| 602 scoped_ptr<WebSocketFrameChunk> chunk(new WebSocketFrameChunk()); | 514 header.payload_length = size; |
| 603 chunk->header = header.Pass(); | 515 frame->data = buffer; |
| 604 chunk->final_chunk = true; | |
| 605 chunk->data = buffer; | |
| 606 if (data_being_sent_) { | 516 if (data_being_sent_) { |
| 607 // Either the link to the WebSocket server is saturated, or several messages | 517 // Either the link to the WebSocket server is saturated, or several messages |
| 608 // are being sent in a batch. | 518 // are being sent in a batch. |
| 609 // TODO(ricea): Keep some statistics to work out the situation and adjust | 519 // TODO(ricea): Keep some statistics to work out the situation and adjust |
| 610 // quota appropriately. | 520 // quota appropriately. |
| 611 if (!data_to_send_next_) | 521 if (!data_to_send_next_) |
| 612 data_to_send_next_.reset(new SendBuffer); | 522 data_to_send_next_.reset(new SendBuffer); |
| 613 data_to_send_next_->AddFrame(chunk.Pass()); | 523 data_to_send_next_->AddFrame(frame.Pass()); |
| 614 } else { | 524 } else { |
| 615 data_being_sent_.reset(new SendBuffer); | 525 data_being_sent_.reset(new SendBuffer); |
| 616 data_being_sent_->AddFrame(chunk.Pass()); | 526 data_being_sent_->AddFrame(frame.Pass()); |
| 617 WriteFrames(); | 527 WriteFrames(); |
| 618 } | 528 } |
| 619 } | 529 } |
| 620 | 530 |
| 621 void WebSocketChannel::FailChannel(ExposeError expose, | 531 void WebSocketChannel::FailChannel(ExposeError expose, |
| 622 uint16 code, | 532 uint16 code, |
| 623 const std::string& reason) { | 533 const std::string& reason) { |
| 624 DCHECK_NE(FRESHLY_CONSTRUCTED, state_); | 534 DCHECK_NE(FRESHLY_CONSTRUCTED, state_); |
| 625 DCHECK_NE(CONNECTING, state_); | 535 DCHECK_NE(CONNECTING, state_); |
| 626 // TODO(ricea): Logging. | 536 // TODO(ricea): Logging. |
| 627 State old_state = state_; | 537 State old_state = state_; |
| 628 if (state_ == CONNECTED) { | 538 if (state_ == CONNECTED) { |
| 629 uint16 send_code = kWebSocketErrorGoingAway; | 539 uint16 send_code = kWebSocketErrorGoingAway; |
| 630 std::string send_reason = "Internal Error"; | 540 std::string send_reason = "Internal Error"; |
| 631 if (expose == SEND_REAL_ERROR) { | 541 if (expose == SEND_REAL_ERROR) { |
| 632 send_code = code; | 542 send_code = code; |
| 633 send_reason = reason; | 543 send_reason = reason; |
| 634 } | 544 } |
| 635 SendClose(send_code, send_reason); // Sets state_ to SEND_CLOSED | 545 SendClose(send_code, send_reason); // Sets state_ to SEND_CLOSED |
| 636 } | 546 } |
| 637 // Careful study of RFC6455 section 7.1.7 and 7.1.1 indicates the browser | 547 // 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 | 548 // should close the connection itself without waiting for the closing |
| 639 // handshake. | 549 // handshake. |
| 640 stream_->Close(); | 550 stream_->Close(); |
| 641 state_ = CLOSED; | 551 state_ = CLOSED; |
| 642 | 552 |
| 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) { | 553 if (old_state != CLOSED) { |
| 647 event_interface_->OnDropChannel(code, reason); | 554 event_interface_->OnDropChannel(code, reason); |
| 648 } | 555 } |
| 649 } | 556 } |
| 650 | 557 |
| 651 void WebSocketChannel::SendClose(uint16 code, const std::string& reason) { | 558 void WebSocketChannel::SendClose(uint16 code, const std::string& reason) { |
| 652 DCHECK(state_ == CONNECTED || state_ == RECV_CLOSED); | 559 DCHECK(state_ == CONNECTED || state_ == RECV_CLOSED); |
| 653 // TODO(ricea): Ensure reason.length() <= 123 | 560 // TODO(ricea): Ensure reason.length() <= 123 |
| 654 scoped_refptr<IOBufferWithSize> body; | 561 scoped_refptr<IOBuffer> body; |
| 562 size_t size = 0; |
| 655 if (code == kWebSocketErrorNoStatusReceived) { | 563 if (code == kWebSocketErrorNoStatusReceived) { |
| 656 // Special case: translate kWebSocketErrorNoStatusReceived into a Close | 564 // Special case: translate kWebSocketErrorNoStatusReceived into a Close |
| 657 // frame with no payload. | 565 // frame with no payload. |
| 658 body = new IOBufferWithSize(0); | 566 body = new IOBuffer(0); |
| 659 } else { | 567 } else { |
| 660 const size_t payload_length = kWebSocketCloseCodeLength + reason.length(); | 568 const size_t payload_length = kWebSocketCloseCodeLength + reason.length(); |
| 661 body = new IOBufferWithSize(payload_length); | 569 body = new IOBuffer(payload_length); |
| 570 size = payload_length; |
| 662 WriteBigEndian(body->data(), code); | 571 WriteBigEndian(body->data(), code); |
| 663 COMPILE_ASSERT(sizeof(code) == kWebSocketCloseCodeLength, | 572 COMPILE_ASSERT(sizeof(code) == kWebSocketCloseCodeLength, |
| 664 they_should_both_be_two); | 573 they_should_both_be_two); |
| 665 std::copy( | 574 std::copy( |
| 666 reason.begin(), reason.end(), body->data() + kWebSocketCloseCodeLength); | 575 reason.begin(), reason.end(), body->data() + kWebSocketCloseCodeLength); |
| 667 } | 576 } |
| 668 SendIOBufferWithSize(true, WebSocketFrameHeader::kOpCodeClose, body); | 577 SendIOBuffer(true, WebSocketFrameHeader::kOpCodeClose, body, size); |
| 669 state_ = (state_ == CONNECTED) ? SEND_CLOSED : CLOSE_WAIT; | 578 state_ = (state_ == CONNECTED) ? SEND_CLOSED : CLOSE_WAIT; |
| 670 } | 579 } |
| 671 | 580 |
| 672 void WebSocketChannel::ParseClose(const scoped_refptr<IOBufferWithSize>& buffer, | 581 void WebSocketChannel::ParseClose(const scoped_refptr<IOBuffer>& buffer, |
| 582 size_t size, |
| 673 uint16* code, | 583 uint16* code, |
| 674 std::string* reason) { | 584 std::string* reason) { |
| 675 const char* data = buffer->data(); | 585 const char* data = buffer->data(); |
| 676 size_t size = base::checked_numeric_cast<size_t>(buffer->size()); | |
| 677 reason->clear(); | 586 reason->clear(); |
| 678 if (size < kWebSocketCloseCodeLength) { | 587 if (size < kWebSocketCloseCodeLength) { |
| 679 *code = kWebSocketErrorNoStatusReceived; | 588 *code = kWebSocketErrorNoStatusReceived; |
| 680 if (size != 0) { | 589 if (size != 0) { |
| 681 VLOG(1) << "Close frame with payload size " << size << " received " | 590 VLOG(1) << "Close frame with payload size " << size << " received " |
| 682 << "(the first byte is " << std::hex << static_cast<int>(data[0]) | 591 << "(the first byte is " << std::hex << static_cast<int>(data[0]) |
| 683 << ")"; | 592 << ")"; |
| 684 return; | 593 return; |
| 685 } | 594 } |
| 686 return; | 595 return; |
| (...skipping 13 matching lines...) Expand all Loading... |
| 700 } | 609 } |
| 701 std::string text(data + kWebSocketCloseCodeLength, data + size); | 610 std::string text(data + kWebSocketCloseCodeLength, data + size); |
| 702 // TODO(ricea): Is this check strict enough? In particular, check the | 611 // TODO(ricea): Is this check strict enough? In particular, check the |
| 703 // "Security Considerations" from RFC3629. | 612 // "Security Considerations" from RFC3629. |
| 704 if (IsStringUTF8(text)) { | 613 if (IsStringUTF8(text)) { |
| 705 reason->swap(text); | 614 reason->swap(text); |
| 706 } | 615 } |
| 707 } | 616 } |
| 708 | 617 |
| 709 } // namespace net | 618 } // namespace net |
| OLD | NEW |