Home | History | Annotate | Download | only in websockets
      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 <algorithm>
      8 
      9 #include "base/basictypes.h"  // for size_t
     10 #include "base/bind.h"
     11 #include "base/safe_numerics.h"
     12 #include "base/strings/string_util.h"
     13 #include "net/base/big_endian.h"
     14 #include "net/base/io_buffer.h"
     15 #include "net/base/net_log.h"
     16 #include "net/websockets/websocket_errors.h"
     17 #include "net/websockets/websocket_event_interface.h"
     18 #include "net/websockets/websocket_frame.h"
     19 #include "net/websockets/websocket_mux.h"
     20 #include "net/websockets/websocket_stream.h"
     21 
     22 namespace net {
     23 
     24 namespace {
     25 
     26 const int kDefaultSendQuotaLowWaterMark = 1 << 16;
     27 const int kDefaultSendQuotaHighWaterMark = 1 << 17;
     28 const size_t kWebSocketCloseCodeLength = 2;
     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 // Concatenate the data from two IOBufferWithSize objects into a single one.
     35 IOBufferWithSize* ConcatenateIOBuffers(
     36     const scoped_refptr<IOBufferWithSize>& part1,
     37     const scoped_refptr<IOBufferWithSize>& part2) {
     38   int newsize = part1->size() + part2->size();
     39   IOBufferWithSize* newbuffer = new IOBufferWithSize(newsize);
     40   std::copy(part1->data(), part1->data() + part1->size(), newbuffer->data());
     41   std::copy(part2->data(),
     42             part2->data() + part2->size(),
     43             newbuffer->data() + part1->size());
     44   return newbuffer;
     45 }
     46 
     47 }  // namespace
     48 
     49 // A class to encapsulate a set of frames and information about the size of
     50 // those frames.
     51 class WebSocketChannel::SendBuffer {
     52  public:
     53   SendBuffer() : total_bytes_(0) {}
     54 
     55   // Add a WebSocketFrameChunk to the buffer and increase total_bytes_.
     56   void AddFrame(scoped_ptr<WebSocketFrameChunk> chunk);
     57 
     58   // Return a pointer to the frames_ for write purposes.
     59   ScopedVector<WebSocketFrameChunk>* frames() { return &frames_; }
     60 
     61  private:
     62   // The frames_ that will be sent in the next call to WriteFrames().
     63   ScopedVector<WebSocketFrameChunk> frames_;
     64 
     65   // The total size of the buffers in |frames_|. This will be used to measure
     66   // the throughput of the link.
     67   // TODO(ricea): Measure the throughput of the link.
     68   size_t total_bytes_;
     69 };
     70 
     71 void WebSocketChannel::SendBuffer::AddFrame(
     72     scoped_ptr<WebSocketFrameChunk> chunk) {
     73   total_bytes_ += chunk->data->size();
     74   frames_.push_back(chunk.release());
     75 }
     76 
     77 // Implementation of WebSocketStream::ConnectDelegate that simply forwards the
     78 // calls on to the WebSocketChannel that created it.
     79 class WebSocketChannel::ConnectDelegate
     80     : public WebSocketStream::ConnectDelegate {
     81  public:
     82   explicit ConnectDelegate(WebSocketChannel* creator) : creator_(creator) {}
     83 
     84   virtual void OnSuccess(scoped_ptr<WebSocketStream> stream) OVERRIDE {
     85     creator_->OnConnectSuccess(stream.Pass());
     86   }
     87 
     88   virtual void OnFailure(uint16 websocket_error) OVERRIDE {
     89     creator_->OnConnectFailure(websocket_error);
     90   }
     91 
     92  private:
     93   // A pointer to the WebSocketChannel that created us. We do not need to worry
     94   // about this pointer being stale, because deleting WebSocketChannel cancels
     95   // the connect process, deleting this object and preventing its callbacks from
     96   // being called.
     97   WebSocketChannel* const creator_;
     98 
     99   DISALLOW_COPY_AND_ASSIGN(ConnectDelegate);
    100 };
    101 
    102 WebSocketChannel::WebSocketChannel(
    103     const GURL& socket_url,
    104     scoped_ptr<WebSocketEventInterface> event_interface)
    105     : socket_url_(socket_url),
    106       event_interface_(event_interface.Pass()),
    107       send_quota_low_water_mark_(kDefaultSendQuotaLowWaterMark),
    108       send_quota_high_water_mark_(kDefaultSendQuotaHighWaterMark),
    109       current_send_quota_(0),
    110       closing_code_(0),
    111       state_(FRESHLY_CONSTRUCTED) {}
    112 
    113 WebSocketChannel::~WebSocketChannel() {
    114   // The stream may hold a pointer to read_frame_chunks_, and so it needs to be
    115   // destroyed first.
    116   stream_.reset();
    117 }
    118 
    119 void WebSocketChannel::SendAddChannelRequest(
    120     const std::vector<std::string>& requested_subprotocols,
    121     const GURL& origin,
    122     URLRequestContext* url_request_context) {
    123   // Delegate to the tested version.
    124   SendAddChannelRequestWithFactory(
    125       requested_subprotocols,
    126       origin,
    127       url_request_context,
    128       base::Bind(&WebSocketStream::CreateAndConnectStream));
    129 }
    130 
    131 bool WebSocketChannel::InClosingState() const {
    132   // We intentionally do not support state RECV_CLOSED here, because it is only
    133   // used in one code path and should not leak into the code in general.
    134   DCHECK_NE(RECV_CLOSED, state_)
    135       << "InClosingState called with state_ == RECV_CLOSED";
    136   return state_ == SEND_CLOSED || state_ == CLOSE_WAIT || state_ == CLOSED;
    137 }
    138 
    139 void WebSocketChannel::SendFrame(bool fin,
    140                                  WebSocketFrameHeader::OpCode op_code,
    141                                  const std::vector<char>& data) {
    142   if (data.size() > INT_MAX) {
    143     NOTREACHED() << "Frame size sanity check failed";
    144     return;
    145   }
    146   if (stream_ == NULL) {
    147     LOG(DFATAL) << "Got SendFrame without a connection established; "
    148                 << "misbehaving renderer? fin=" << fin << " op_code=" << op_code
    149                 << " data.size()=" << data.size();
    150     return;
    151   }
    152   if (InClosingState()) {
    153     VLOG(1) << "SendFrame called in state " << state_
    154             << ". This may be a bug, or a harmless race.";
    155     return;
    156   }
    157   if (state_ != CONNECTED) {
    158     NOTREACHED() << "SendFrame() called in state " << state_;
    159     return;
    160   }
    161   if (data.size() > base::checked_numeric_cast<size_t>(current_send_quota_)) {
    162     FailChannel(SEND_GOING_AWAY,
    163                 kWebSocketMuxErrorSendQuotaViolation,
    164                 "Send quota exceeded");
    165     return;
    166   }
    167   if (!WebSocketFrameHeader::IsKnownDataOpCode(op_code)) {
    168     LOG(DFATAL) << "Got SendFrame with bogus op_code " << op_code
    169                 << "; misbehaving renderer? fin=" << fin
    170                 << " data.size()=" << data.size();
    171     return;
    172   }
    173   current_send_quota_ -= data.size();
    174   // TODO(ricea): If current_send_quota_ has dropped below
    175   // send_quota_low_water_mark_, we may want to consider increasing the "low
    176   // water mark" and "high water mark", but only if we think we are not
    177   // saturating the link to the WebSocket server.
    178   // TODO(ricea): For kOpCodeText, do UTF-8 validation?
    179   scoped_refptr<IOBufferWithSize> buffer(new IOBufferWithSize(data.size()));
    180   std::copy(data.begin(), data.end(), buffer->data());
    181   SendIOBufferWithSize(fin, op_code, buffer);
    182 }
    183 
    184 void WebSocketChannel::SendFlowControl(int64 quota) {
    185   DCHECK_EQ(CONNECTED, state_);
    186   // TODO(ricea): Add interface to WebSocketStream and implement.
    187   // stream_->SendFlowControl(quota);
    188 }
    189 
    190 void WebSocketChannel::StartClosingHandshake(uint16 code,
    191                                              const std::string& reason) {
    192   if (InClosingState()) {
    193     VLOG(1) << "StartClosingHandshake called in state " << state_
    194             << ". This may be a bug, or a harmless race.";
    195     return;
    196   }
    197   if (state_ != CONNECTED) {
    198     NOTREACHED() << "StartClosingHandshake() called in state " << state_;
    199     return;
    200   }
    201   // TODO(ricea): Validate |code|? Check that |reason| is valid UTF-8?
    202   // TODO(ricea): There should be a timeout for the closing handshake.
    203   SendClose(code, reason);  // Sets state_ to SEND_CLOSED
    204 }
    205 
    206 void WebSocketChannel::SendAddChannelRequestForTesting(
    207     const std::vector<std::string>& requested_subprotocols,
    208     const GURL& origin,
    209     URLRequestContext* url_request_context,
    210     const WebSocketStreamFactory& factory) {
    211   SendAddChannelRequestWithFactory(
    212       requested_subprotocols, origin, url_request_context, factory);
    213 }
    214 
    215 void WebSocketChannel::SendAddChannelRequestWithFactory(
    216     const std::vector<std::string>& requested_subprotocols,
    217     const GURL& origin,
    218     URLRequestContext* url_request_context,
    219     const WebSocketStreamFactory& factory) {
    220   DCHECK_EQ(FRESHLY_CONSTRUCTED, state_);
    221   scoped_ptr<WebSocketStream::ConnectDelegate> connect_delegate(
    222       new ConnectDelegate(this));
    223   stream_request_ = factory.Run(socket_url_,
    224                                 requested_subprotocols,
    225                                 origin,
    226                                 url_request_context,
    227                                 BoundNetLog(),
    228                                 connect_delegate.Pass());
    229   state_ = CONNECTING;
    230 }
    231 
    232 void WebSocketChannel::OnConnectSuccess(scoped_ptr<WebSocketStream> stream) {
    233   DCHECK(stream);
    234   DCHECK_EQ(CONNECTING, state_);
    235   stream_ = stream.Pass();
    236   state_ = CONNECTED;
    237   event_interface_->OnAddChannelResponse(false, stream_->GetSubProtocol());
    238 
    239   // TODO(ricea): Get flow control information from the WebSocketStream once we
    240   // have a multiplexing WebSocketStream.
    241   current_send_quota_ = send_quota_high_water_mark_;
    242   event_interface_->OnFlowControl(send_quota_high_water_mark_);
    243 
    244   // We don't need this any more.
    245   stream_request_.reset();
    246   ReadFrames();
    247 }
    248 
    249 void WebSocketChannel::OnConnectFailure(uint16 websocket_error) {
    250   DCHECK_EQ(CONNECTING, state_);
    251   state_ = CLOSED;
    252   stream_request_.reset();
    253   event_interface_->OnAddChannelResponse(true, "");
    254 }
    255 
    256 void WebSocketChannel::WriteFrames() {
    257   int result = OK;
    258   do {
    259     // This use of base::Unretained is safe because we own the WebSocketStream
    260     // and destroying it cancels all callbacks.
    261     result = stream_->WriteFrames(
    262         data_being_sent_->frames(),
    263         base::Bind(
    264             &WebSocketChannel::OnWriteDone, base::Unretained(this), false));
    265     if (result != ERR_IO_PENDING) {
    266       OnWriteDone(true, result);
    267     }
    268   } while (result == OK && data_being_sent_);
    269 }
    270 
    271 void WebSocketChannel::OnWriteDone(bool synchronous, int result) {
    272   DCHECK_NE(FRESHLY_CONSTRUCTED, state_);
    273   DCHECK_NE(CONNECTING, state_);
    274   DCHECK_NE(ERR_IO_PENDING, result);
    275   DCHECK(data_being_sent_);
    276   switch (result) {
    277     case OK:
    278       if (data_to_send_next_) {
    279         data_being_sent_ = data_to_send_next_.Pass();
    280         if (!synchronous) {
    281           WriteFrames();
    282         }
    283       } else {
    284         data_being_sent_.reset();
    285         if (current_send_quota_ < send_quota_low_water_mark_) {
    286           // TODO(ricea): Increase low_water_mark and high_water_mark if
    287           // throughput is high, reduce them if throughput is low.  Low water
    288           // mark needs to be >= the bandwidth delay product *of the IPC
    289           // channel*. Because factors like context-switch time, thread wake-up
    290           // time, and bus speed come into play it is complex and probably needs
    291           // to be determined empirically.
    292           DCHECK_LE(send_quota_low_water_mark_, send_quota_high_water_mark_);
    293           // TODO(ricea): Truncate quota by the quota specified by the remote
    294           // server, if the protocol in use supports quota.
    295           int fresh_quota = send_quota_high_water_mark_ - current_send_quota_;
    296           current_send_quota_ += fresh_quota;
    297           event_interface_->OnFlowControl(fresh_quota);
    298         }
    299       }
    300       return;
    301 
    302     // If a recoverable error condition existed, it would go here.
    303 
    304     default:
    305       DCHECK_LT(result, 0)
    306           << "WriteFrames() should only return OK or ERR_ codes";
    307       stream_->Close();
    308       if (state_ != CLOSED) {
    309         state_ = CLOSED;
    310         event_interface_->OnDropChannel(kWebSocketErrorAbnormalClosure,
    311                                         "Abnormal Closure");
    312       }
    313       return;
    314   }
    315 }
    316 
    317 void WebSocketChannel::ReadFrames() {
    318   int result = OK;
    319   do {
    320     // This use of base::Unretained is safe because we own the WebSocketStream,
    321     // and any pending reads will be cancelled when it is destroyed.
    322     result = stream_->ReadFrames(
    323         &read_frame_chunks_,
    324         base::Bind(
    325             &WebSocketChannel::OnReadDone, base::Unretained(this), false));
    326     if (result != ERR_IO_PENDING) {
    327       OnReadDone(true, result);
    328     }
    329   } while (result == OK && state_ != CLOSED);
    330 }
    331 
    332 void WebSocketChannel::OnReadDone(bool synchronous, int result) {
    333   DCHECK_NE(FRESHLY_CONSTRUCTED, state_);
    334   DCHECK_NE(CONNECTING, state_);
    335   DCHECK_NE(ERR_IO_PENDING, result);
    336   switch (result) {
    337     case OK:
    338       // ReadFrames() must use ERR_CONNECTION_CLOSED for a closed connection
    339       // with no data read, not an empty response.
    340       DCHECK(!read_frame_chunks_.empty())
    341           << "ReadFrames() returned OK, but nothing was read.";
    342       for (size_t i = 0; i < read_frame_chunks_.size(); ++i) {
    343         scoped_ptr<WebSocketFrameChunk> chunk(read_frame_chunks_[i]);
    344         read_frame_chunks_[i] = NULL;
    345         ProcessFrameChunk(chunk.Pass());
    346       }
    347       read_frame_chunks_.clear();
    348       // We need to always keep a call to ReadFrames pending.
    349       if (!synchronous && state_ != CLOSED) {
    350         ReadFrames();
    351       }
    352       return;
    353 
    354     default:
    355       DCHECK_LT(result, 0)
    356           << "ReadFrames() should only return OK or ERR_ codes";
    357       stream_->Close();
    358       if (state_ != CLOSED) {
    359         state_ = CLOSED;
    360         uint16 code = kWebSocketErrorAbnormalClosure;
    361         std::string reason = "Abnormal Closure";
    362         if (closing_code_ != 0) {
    363           code = closing_code_;
    364           reason = closing_reason_;
    365         }
    366         event_interface_->OnDropChannel(code, reason);
    367       }
    368       return;
    369   }
    370 }
    371 
    372 void WebSocketChannel::ProcessFrameChunk(
    373     scoped_ptr<WebSocketFrameChunk> chunk) {
    374   bool is_first_chunk = false;
    375   if (chunk->header) {
    376     DCHECK(current_frame_header_ == NULL)
    377         << "Received the header for a new frame without notification that "
    378         << "the previous frame was complete.";
    379     is_first_chunk = true;
    380     current_frame_header_.swap(chunk->header);
    381     if (current_frame_header_->masked) {
    382       // RFC6455 Section 5.1 "A client MUST close a connection if it detects a
    383       // masked frame."
    384       FailChannel(SEND_REAL_ERROR,
    385                   kWebSocketErrorProtocolError,
    386                   "Masked frame from server");
    387       return;
    388     }
    389   }
    390   if (!current_frame_header_) {
    391     // If we rejected the previous chunk as invalid, then we will have reset
    392     // current_frame_header_ to avoid using it. More chunks of the invalid frame
    393     // may still arrive, so this is not necessarily a bug on our side. However,
    394     // if this happens when state_ is CONNECTED, it is definitely a bug.
    395     DCHECK(state_ != CONNECTED) << "Unexpected header-less frame received "
    396                                 << "(final_chunk = " << chunk->final_chunk
    397                                 << ", data size = " << chunk->data->size()
    398                                 << ")";
    399     return;
    400   }
    401   scoped_refptr<IOBufferWithSize> data_buffer;
    402   data_buffer.swap(chunk->data);
    403   const bool is_final_chunk = chunk->final_chunk;
    404   chunk.reset();
    405   WebSocketFrameHeader::OpCode opcode = current_frame_header_->opcode;
    406   if (WebSocketFrameHeader::IsKnownControlOpCode(opcode)) {
    407     if (!current_frame_header_->final) {
    408       FailChannel(SEND_REAL_ERROR,
    409                   kWebSocketErrorProtocolError,
    410                   "Control message with FIN bit unset received");
    411       return;
    412     }
    413     if (current_frame_header_->payload_length > kMaxControlFramePayload) {
    414       FailChannel(SEND_REAL_ERROR,
    415                   kWebSocketErrorProtocolError,
    416                   "Control message has payload over 125 bytes");
    417       return;
    418     }
    419     if (!is_final_chunk) {
    420       VLOG(2) << "Encountered a split control frame, opcode " << opcode;
    421       if (incomplete_control_frame_body_) {
    422         // The really horrid case. We need to create a new IOBufferWithSize
    423         // combining the new one and the old one. This should virtually never
    424         // happen.
    425         // TODO(ricea): This algorithm is O(N^2). Use a fixed 127-byte buffer
    426         // instead.
    427         VLOG(3) << "Hit the really horrid case";
    428         incomplete_control_frame_body_ =
    429             ConcatenateIOBuffers(incomplete_control_frame_body_, data_buffer);
    430       } else {
    431         // The merely horrid case. Store the IOBufferWithSize to use when the
    432         // rest of the control frame arrives.
    433         incomplete_control_frame_body_.swap(data_buffer);
    434       }
    435       return;  // Handle when complete.
    436     }
    437     if (incomplete_control_frame_body_) {
    438       VLOG(2) << "Rejoining a split control frame, opcode " << opcode;
    439       data_buffer =
    440           ConcatenateIOBuffers(incomplete_control_frame_body_, data_buffer);
    441       incomplete_control_frame_body_ = NULL;  // Frame now complete.
    442     }
    443   }
    444 
    445   // Apply basic sanity checks to the |payload_length| field from the frame
    446   // header. We can only apply a strict check when we know we have the whole
    447   // frame in one chunk.
    448   DCHECK_GE(current_frame_header_->payload_length,
    449             base::checked_numeric_cast<uint64>(data_buffer->size()));
    450   DCHECK(!is_first_chunk || !is_final_chunk ||
    451          current_frame_header_->payload_length ==
    452              base::checked_numeric_cast<uint64>(data_buffer->size()));
    453 
    454   // Respond to the frame appropriately to its type.
    455   HandleFrame(opcode, is_first_chunk, is_final_chunk, data_buffer);
    456 
    457   if (is_final_chunk) {
    458     // Make sure we do not apply this frame header to any future chunks.
    459     current_frame_header_.reset();
    460   }
    461 }
    462 
    463 void WebSocketChannel::HandleFrame(
    464     const WebSocketFrameHeader::OpCode opcode,
    465     bool is_first_chunk,
    466     bool is_final_chunk,
    467     const scoped_refptr<IOBufferWithSize>& data_buffer) {
    468   DCHECK_NE(RECV_CLOSED, state_)
    469       << "HandleFrame() does not support being called re-entrantly from within "
    470          "SendClose()";
    471   if (state_ == CLOSED || state_ == CLOSE_WAIT) {
    472     DVLOG_IF(1, state_ == CLOSED) << "A frame was received while in the CLOSED "
    473                                      "state. This is possible after a channel "
    474                                      "failed, but should be very rare.";
    475     std::string frame_name;
    476     switch (opcode) {
    477       case WebSocketFrameHeader::kOpCodeText:    // fall-thru
    478       case WebSocketFrameHeader::kOpCodeBinary:  // fall-thru
    479       case WebSocketFrameHeader::kOpCodeContinuation:
    480         frame_name = "Data frame";
    481         break;
    482 
    483       case WebSocketFrameHeader::kOpCodePing:
    484         frame_name = "Ping";
    485         break;
    486 
    487       case WebSocketFrameHeader::kOpCodePong:
    488         frame_name = "Pong";
    489         break;
    490 
    491       case WebSocketFrameHeader::kOpCodeClose:
    492         frame_name = "Close";
    493         break;
    494 
    495       default:
    496         frame_name = "Unknown frame type";
    497         break;
    498     }
    499     // SEND_REAL_ERROR makes no difference here, as we won't send another Close
    500     // frame.
    501     FailChannel(SEND_REAL_ERROR,
    502                 kWebSocketErrorProtocolError,
    503                 frame_name + " received after close");
    504     return;
    505   }
    506   switch (opcode) {
    507     case WebSocketFrameHeader::kOpCodeText:    // fall-thru
    508     case WebSocketFrameHeader::kOpCodeBinary:  // fall-thru
    509     case WebSocketFrameHeader::kOpCodeContinuation:
    510       if (state_ == CONNECTED) {
    511         const bool final = is_final_chunk && current_frame_header_->final;
    512         // TODO(ricea): Need to fail the connection if UTF-8 is invalid
    513         // post-reassembly. Requires a streaming UTF-8 validator.
    514         // TODO(ricea): Can this copy be eliminated?
    515         const char* const data_begin = data_buffer->data();
    516         const char* const data_end = data_begin + data_buffer->size();
    517         const std::vector<char> data(data_begin, data_end);
    518         // TODO(ricea): Handle the (improbable) case when ReadFrames returns far
    519         // more data at once than we want to send in a single IPC (in which case
    520         // we need to buffer the data and return to the event loop with a
    521         // callback to send the rest in 32K chunks).
    522 
    523         // Send the received frame to the renderer process.
    524         event_interface_->OnDataFrame(
    525             final,
    526             is_first_chunk ? opcode : WebSocketFrameHeader::kOpCodeContinuation,
    527             data);
    528       } else {
    529         VLOG(3) << "Ignored data packet received in state " << state_;
    530       }
    531       return;
    532 
    533     case WebSocketFrameHeader::kOpCodePing:
    534       VLOG(1) << "Got Ping of size " << data_buffer->size();
    535       if (state_ == CONNECTED) {
    536         SendIOBufferWithSize(
    537             true, WebSocketFrameHeader::kOpCodePong, data_buffer);
    538       } else {
    539         VLOG(3) << "Ignored ping in state " << state_;
    540       }
    541       return;
    542 
    543     case WebSocketFrameHeader::kOpCodePong:
    544       VLOG(1) << "Got Pong of size " << data_buffer->size();
    545       // We do not need to do anything with pong messages.
    546       return;
    547 
    548     case WebSocketFrameHeader::kOpCodeClose: {
    549       uint16 code = kWebSocketNormalClosure;
    550       std::string reason;
    551       ParseClose(data_buffer, &code, &reason);
    552       // TODO(ricea): Find a way to safely log the message from the close
    553       // message (escape control codes and so on).
    554       VLOG(1) << "Got Close with code " << code;
    555       switch (state_) {
    556         case CONNECTED:
    557           state_ = RECV_CLOSED;
    558           SendClose(code, reason);  // Sets state_ to CLOSE_WAIT
    559           event_interface_->OnClosingHandshake();
    560           closing_code_ = code;
    561           closing_reason_ = reason;
    562           break;
    563 
    564         case SEND_CLOSED:
    565           state_ = CLOSE_WAIT;
    566           // From RFC6455 section 7.1.5: "Each endpoint
    567           // will see the status code sent by the other end as _The WebSocket
    568           // Connection Close Code_."
    569           closing_code_ = code;
    570           closing_reason_ = reason;
    571           break;
    572 
    573         default:
    574           LOG(DFATAL) << "Got Close in unexpected state " << state_;
    575           break;
    576       }
    577       return;
    578     }
    579 
    580     default:
    581       FailChannel(
    582           SEND_REAL_ERROR, kWebSocketErrorProtocolError, "Unknown opcode");
    583       return;
    584   }
    585 }
    586 
    587 void WebSocketChannel::SendIOBufferWithSize(
    588     bool fin,
    589     WebSocketFrameHeader::OpCode op_code,
    590     const scoped_refptr<IOBufferWithSize>& buffer) {
    591   DCHECK(state_ == CONNECTED || state_ == RECV_CLOSED);
    592   DCHECK(stream_);
    593   scoped_ptr<WebSocketFrameHeader> header(new WebSocketFrameHeader(op_code));
    594   header->final = fin;
    595   header->masked = true;
    596   header->payload_length = buffer->size();
    597   scoped_ptr<WebSocketFrameChunk> chunk(new WebSocketFrameChunk());
    598   chunk->header = header.Pass();
    599   chunk->final_chunk = true;
    600   chunk->data = buffer;
    601   if (data_being_sent_) {
    602     // Either the link to the WebSocket server is saturated, or we are simply
    603     // processing a batch of messages.
    604     // TODO(ricea): We need to keep some statistics to work out which situation
    605     // we are in and adjust quota appropriately.
    606     if (!data_to_send_next_)
    607       data_to_send_next_.reset(new SendBuffer);
    608     data_to_send_next_->AddFrame(chunk.Pass());
    609   } else {
    610     data_being_sent_.reset(new SendBuffer);
    611     data_being_sent_->AddFrame(chunk.Pass());
    612     WriteFrames();
    613   }
    614 }
    615 
    616 void WebSocketChannel::FailChannel(ExposeError expose,
    617                                    uint16 code,
    618                                    const std::string& reason) {
    619   DCHECK_NE(FRESHLY_CONSTRUCTED, state_);
    620   DCHECK_NE(CONNECTING, state_);
    621   // TODO(ricea): Logging.
    622   State old_state = state_;
    623   if (state_ == CONNECTED) {
    624     uint16 send_code = kWebSocketErrorGoingAway;
    625     std::string send_reason = "Internal Error";
    626     if (expose == SEND_REAL_ERROR) {
    627       send_code = code;
    628       send_reason = reason;
    629     }
    630     SendClose(send_code, send_reason);  // Sets state_ to SEND_CLOSED
    631   }
    632   // Careful study of RFC6455 section 7.1.7 and 7.1.1 indicates we should close
    633   // the connection ourselves without waiting for the closing handshake.
    634   stream_->Close();
    635   state_ = CLOSED;
    636 
    637   // We may be in the middle of processing several chunks. We should not re-use
    638   // the frame header.
    639   current_frame_header_.reset();
    640   if (old_state != CLOSED) {
    641     event_interface_->OnDropChannel(code, reason);
    642   }
    643 }
    644 
    645 void WebSocketChannel::SendClose(uint16 code, const std::string& reason) {
    646   DCHECK(state_ == CONNECTED || state_ == RECV_CLOSED);
    647   // TODO(ricea): Ensure reason.length() <= 123
    648   scoped_refptr<IOBufferWithSize> body;
    649   if (code == kWebSocketErrorNoStatusReceived) {
    650     // Special case: translate kWebSocketErrorNoStatusReceived into a Close
    651     // frame with no payload.
    652     body = new IOBufferWithSize(0);
    653   } else {
    654     const size_t payload_length = kWebSocketCloseCodeLength + reason.length();
    655     body = new IOBufferWithSize(payload_length);
    656     WriteBigEndian(body->data(), code);
    657     COMPILE_ASSERT(sizeof(code) == kWebSocketCloseCodeLength,
    658                    they_should_both_be_two);
    659     std::copy(
    660         reason.begin(), reason.end(), body->data() + kWebSocketCloseCodeLength);
    661   }
    662   SendIOBufferWithSize(true, WebSocketFrameHeader::kOpCodeClose, body);
    663   state_ = (state_ == CONNECTED) ? SEND_CLOSED : CLOSE_WAIT;
    664 }
    665 
    666 void WebSocketChannel::ParseClose(const scoped_refptr<IOBufferWithSize>& buffer,
    667                                   uint16* code,
    668                                   std::string* reason) {
    669   const char* data = buffer->data();
    670   size_t size = base::checked_numeric_cast<size_t>(buffer->size());
    671   reason->clear();
    672   if (size < kWebSocketCloseCodeLength) {
    673     *code = kWebSocketErrorNoStatusReceived;
    674     if (size != 0) {
    675       VLOG(1) << "Close frame with payload size " << size << " received "
    676               << "(the first byte is " << std::hex << static_cast<int>(data[0])
    677               << ")";
    678       return;
    679     }
    680     return;
    681   }
    682   uint16 unchecked_code = 0;
    683   ReadBigEndian(data, &unchecked_code);
    684   COMPILE_ASSERT(sizeof(unchecked_code) == kWebSocketCloseCodeLength,
    685                  they_should_both_be_two_bytes);
    686   if (unchecked_code >= static_cast<uint16>(kWebSocketNormalClosure) &&
    687       unchecked_code <=
    688           static_cast<uint16>(kWebSocketErrorPrivateReservedMax)) {
    689     *code = unchecked_code;
    690   } else {
    691     VLOG(1) << "Close frame contained code outside of the valid range: "
    692             << unchecked_code;
    693     *code = kWebSocketErrorAbnormalClosure;
    694   }
    695   std::string text(data + kWebSocketCloseCodeLength, data + size);
    696   // TODO(ricea): Is this check strict enough? In particular, check the
    697   // "Security Considerations" from RFC3629.
    698   if (IsStringUTF8(text)) {
    699     reason->swap(text);
    700   }
    701 }
    702 
    703 }  // namespace net
    704