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
22namespace net {
23
24namespace {
25
26const int kDefaultSendQuotaLowWaterMark = 1 << 16;
27const int kDefaultSendQuotaHighWaterMark = 1 << 17;
28const size_t kWebSocketCloseCodeLength = 2;
29
30// This uses type uint64 to match the definition of
31// WebSocketFrameHeader::payload_length in websocket_frame.h.
32const uint64 kMaxControlFramePayload = 125;
33
34// Concatenate the data from two IOBufferWithSize objects into a single one.
35IOBufferWithSize* 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.
51class 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
71void 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.
79class 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
102WebSocketChannel::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
113WebSocketChannel::~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
119void 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
131bool 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
139void 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
184void WebSocketChannel::SendFlowControl(int64 quota) {
185  DCHECK_EQ(CONNECTED, state_);
186  // TODO(ricea): Add interface to WebSocketStream and implement.
187  // stream_->SendFlowControl(quota);
188}
189
190void 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
206void 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
215void 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
232void 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
249void WebSocketChannel::OnConnectFailure(uint16 websocket_error) {
250  DCHECK_EQ(CONNECTING, state_);
251  state_ = CLOSED;
252  stream_request_.reset();
253  event_interface_->OnAddChannelResponse(true, "");
254}
255
256void 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
271void 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
317void 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
332void 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
372void 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
463void 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
587void 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
616void 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
645void 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
666void 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