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 #ifndef NET_WEBSOCKETS_WEBSOCKET_CHANNEL_H_
      6 #define NET_WEBSOCKETS_WEBSOCKET_CHANNEL_H_
      7 
      8 #include <string>
      9 #include <vector>
     10 
     11 #include "base/basictypes.h"
     12 #include "base/callback.h"
     13 #include "base/memory/scoped_ptr.h"
     14 #include "base/memory/scoped_vector.h"
     15 #include "net/base/net_export.h"
     16 #include "net/websockets/websocket_frame.h"
     17 #include "net/websockets/websocket_stream.h"
     18 #include "url/gurl.h"
     19 
     20 namespace net {
     21 
     22 class URLRequestContext;
     23 class WebSocketEventInterface;
     24 
     25 // Transport-independent implementation of WebSockets. Implements protocol
     26 // semantics that do not depend on the underlying transport. Provides the
     27 // interface to the content layer. Some WebSocket concepts are used here without
     28 // definition; please see the RFC at http://tools.ietf.org/html/rfc6455 for
     29 // clarification.
     30 class NET_EXPORT WebSocketChannel {
     31  public:
     32   // The type of a WebSocketStream factory callback. Must match the signature of
     33   // WebSocketStream::CreateAndConnectStream().
     34   typedef base::Callback<scoped_ptr<WebSocketStreamRequest>(
     35       const GURL&,
     36       const std::vector<std::string>&,
     37       const GURL&,
     38       URLRequestContext*,
     39       const BoundNetLog&,
     40       scoped_ptr<WebSocketStream::ConnectDelegate>)> WebSocketStreamFactory;
     41 
     42   // Creates a new WebSocketChannel with the specified parameters.
     43   // SendAddChannelRequest() must be called immediately afterwards to start the
     44   // connection process.
     45   WebSocketChannel(const GURL& socket_url,
     46                    scoped_ptr<WebSocketEventInterface> event_interface);
     47   virtual ~WebSocketChannel();
     48 
     49   // Starts the connection process.
     50   void SendAddChannelRequest(
     51       const std::vector<std::string>& requested_protocols,
     52       const GURL& origin,
     53       URLRequestContext* url_request_context);
     54 
     55   // Sends a data frame to the remote side. The frame should usually be no
     56   // larger than 32KB to prevent the time required to copy the buffers from from
     57   // unduly delaying other tasks that need to run on the IO thread. This method
     58   // has a hard limit of 2GB. It is the responsibility of the caller to ensure
     59   // that they have sufficient send quota to send this data, otherwise the
     60   // connection will be closed without sending. |fin| indicates the last frame
     61   // in a message, equivalent to "FIN" as specified in section 5.2 of
     62   // RFC6455. |data| is the "Payload Data". If |op_code| is kOpCodeText, or it
     63   // is kOpCodeContinuation and the type the message is Text, then |data| must
     64   // be a chunk of a valid UTF-8 message, however there is no requirement for
     65   // |data| to be split on character boundaries.
     66   void SendFrame(bool fin,
     67                  WebSocketFrameHeader::OpCode op_code,
     68                  const std::vector<char>& data);
     69 
     70   // Sends |quota| units of flow control to the remote side. If the underlying
     71   // transport has a concept of |quota|, then it permits the remote server to
     72   // send up to |quota| units of data.
     73   void SendFlowControl(int64 quota);
     74 
     75   // Start the closing handshake for a client-initiated shutdown of the
     76   // connection. There is no API to close the connection without a closing
     77   // handshake, but destroying the WebSocketChannel object while connected will
     78   // effectively do that. |code| must be in the range 1000-4999. |reason| should
     79   // be a valid UTF-8 string or empty.
     80   //
     81   // This does *not* trigger the event OnClosingHandshake(). The caller should
     82   // assume that the closing handshake has started and perform the equivalent
     83   // processing to OnClosingHandshake() if necessary.
     84   void StartClosingHandshake(uint16 code, const std::string& reason);
     85 
     86   // Starts the connection process, using a specified factory function rather
     87   // than the default. This is exposed for testing.
     88   void SendAddChannelRequestForTesting(
     89       const std::vector<std::string>& requested_protocols,
     90       const GURL& origin,
     91       URLRequestContext* url_request_context,
     92       const WebSocketStreamFactory& factory);
     93 
     94  private:
     95   // We have a simple linear progression of states from FRESHLY_CONSTRUCTED to
     96   // CLOSED, except that the SEND_CLOSED and RECV_CLOSED states may be skipped
     97   // in case of error.
     98   enum State {
     99     FRESHLY_CONSTRUCTED,
    100     CONNECTING,
    101     CONNECTED,
    102     SEND_CLOSED,  // We have sent a Close frame but not received a Close frame.
    103     RECV_CLOSED,  // Used briefly between receiving a Close frame and sending
    104                   // the response. Once we have responded, the state changes
    105                   // to CLOSED.
    106     CLOSE_WAIT,   // The Closing Handshake has completed, but the remote server
    107                   // has not yet closed the connection.
    108     CLOSED,       // The Closing Handshake has completed and the connection
    109                   // has been closed; or the connection is failed.
    110   };
    111 
    112   // When failing a channel, we may or may not want to send the real reason for
    113   // failing to the remote server. This enum is used by FailChannel() to
    114   // choose.
    115   enum ExposeError {
    116     SEND_REAL_ERROR,
    117     SEND_GOING_AWAY,
    118   };
    119 
    120   // Our implementation of WebSocketStream::ConnectDelegate. We do not inherit
    121   // from WebSocketStream::ConnectDelegate directly to avoid cluttering our
    122   // public interface with the implementation of those methods, and because the
    123   // lifetime of a WebSocketChannel is longer than the lifetime of the
    124   // connection process.
    125   class ConnectDelegate;
    126 
    127   // Starts the connection progress, using a specified factory function.
    128   void SendAddChannelRequestWithFactory(
    129       const std::vector<std::string>& requested_protocols,
    130       const GURL& origin,
    131       URLRequestContext* url_request_context,
    132       const WebSocketStreamFactory& factory);
    133 
    134   // Success callback from WebSocketStream::CreateAndConnectStream(). Reports
    135   // success to the event interface.
    136   void OnConnectSuccess(scoped_ptr<WebSocketStream> stream);
    137 
    138   // Failure callback from WebSocketStream::CreateAndConnectStream(). Reports
    139   // failure to the event interface.
    140   void OnConnectFailure(uint16 websocket_error);
    141 
    142   // Returns true if state_ is SEND_CLOSED, CLOSE_WAIT or CLOSED.
    143   bool InClosingState() const;
    144 
    145   // Calls WebSocketStream::WriteFrames() with the appropriate arguments
    146   void WriteFrames();
    147 
    148   // Callback from WebSocketStream::WriteFrames. Sends pending data or adjusts
    149   // the send quota of the renderer channel as appropriate. |result| is a net
    150   // error code, usually OK. If |synchronous| is true, then OnWriteDone() is
    151   // being called from within the WriteFrames() loop and does not need to call
    152   // WriteFrames() itself.
    153   void OnWriteDone(bool synchronous, int result);
    154 
    155   // Calls WebSocketStream::ReadFrames() with the appropriate arguments.
    156   void ReadFrames();
    157 
    158   // Callback from WebSocketStream::ReadFrames. Handles any errors and processes
    159   // the returned chunks appropriately to their type. |result| is a net error
    160   // code. If |synchronous| is true, then OnReadDone() is being called from
    161   // within the ReadFrames() loop and does not need to call ReadFrames() itself.
    162   void OnReadDone(bool synchronous, int result);
    163 
    164   // Processes a single chunk that has been read from the stream.
    165   void ProcessFrameChunk(scoped_ptr<WebSocketFrameChunk> chunk);
    166 
    167   // Handle a frame that we have received enough of to process. May call
    168   // event_interface_ methods, send responses to the server, and change the
    169   // value of state_.
    170   void HandleFrame(const WebSocketFrameHeader::OpCode opcode,
    171                    bool is_first_chunk,
    172                    bool is_final_chunk,
    173                    const scoped_refptr<IOBufferWithSize>& data_buffer);
    174 
    175   // Low-level method to send a single frame. Used for both data and control
    176   // frames. Either sends the frame immediately or buffers it to be scheduled
    177   // when the current write finishes. |fin| and |op_code| are defined as for
    178   // SendFrame() above, except that |op_code| may also be a control frame
    179   // opcode.
    180   void SendIOBufferWithSize(bool fin,
    181                             WebSocketFrameHeader::OpCode op_code,
    182                             const scoped_refptr<IOBufferWithSize>& buffer);
    183 
    184   // Perform the "Fail the WebSocket Connection" operation as defined in
    185   // RFC6455. The supplied code and reason are sent back to the renderer in an
    186   // OnDropChannel message. If state_ is CONNECTED then a Close message is sent
    187   // to the remote host. If |expose| is SEND_REAL_ERROR then the remote host is
    188   // given the same status code we gave the renderer; otherwise it is sent a
    189   // fixed "Going Away" code.  Resets current_frame_header_, closes the
    190   // stream_, and sets state_ to CLOSED.
    191   void FailChannel(ExposeError expose, uint16 code, const std::string& reason);
    192 
    193   // Sends a Close frame to Start the WebSocket Closing Handshake, or to respond
    194   // to a Close frame from the server. As a special case, setting |code| to
    195   // kWebSocketErrorNoStatusReceived will create a Close frame with no payload;
    196   // this is symmetric with the behaviour of ParseClose.
    197   void SendClose(uint16 code, const std::string& reason);
    198 
    199   // Parses a Close frame. If no status code is supplied, then |code| is set to
    200   // 1005 (No status code) with empty |reason|. If the supplied code is
    201   // outside the valid range, then 1002 (Protocol error) is set instead. If the
    202   // reason text is not valid UTF-8, then |reason| is set to an empty string
    203   // instead.
    204   void ParseClose(const scoped_refptr<IOBufferWithSize>& buffer,
    205                   uint16* code,
    206                   std::string* reason);
    207 
    208   // The URL to which we connect.
    209   const GURL socket_url_;
    210 
    211   // The object receiving events.
    212   const scoped_ptr<WebSocketEventInterface> event_interface_;
    213 
    214   // The WebSocketStream to which we are sending/receiving data.
    215   scoped_ptr<WebSocketStream> stream_;
    216 
    217   // A data structure containing a vector of frames to be sent and the total
    218   // number of bytes contained in the vector.
    219   class SendBuffer;
    220   // Data that is currently pending write, or NULL if no write is pending.
    221   scoped_ptr<SendBuffer> data_being_sent_;
    222   // Data that is queued up to write after the current write completes.
    223   // Only non-NULL when such data actually exists.
    224   scoped_ptr<SendBuffer> data_to_send_next_;
    225 
    226   // Destination for the current call to WebSocketStream::ReadFrames
    227   ScopedVector<WebSocketFrameChunk> read_frame_chunks_;
    228   // Frame header for the frame currently being received. Only non-NULL while we
    229   // are processing the frame. If the frame arrives in multiple chunks, can
    230   // remain non-NULL while we wait for additional chunks to arrive. If the
    231   // header of the frame was invalid, this is set to NULL, the channel is
    232   // failed, and subsequent chunks of the same frame will be ignored.
    233   scoped_ptr<WebSocketFrameHeader> current_frame_header_;
    234   // Handle to an in-progress WebSocketStream creation request. Only non-NULL
    235   // during the connection process.
    236   scoped_ptr<WebSocketStreamRequest> stream_request_;
    237   // Although it will almost never happen in practice, we can be passed an
    238   // incomplete control frame, in which case we need to keep the data around
    239   // long enough to reassemble it. This variable will be NULL the rest of the
    240   // time.
    241   scoped_refptr<IOBufferWithSize> incomplete_control_frame_body_;
    242   // The point at which we give the renderer a quota refresh (quota units).
    243   // "quota units" are currently bytes. TODO(ricea): Update the definition of
    244   // quota units when necessary.
    245   int send_quota_low_water_mark_;
    246   // The amount which we refresh the quota to when it reaches the
    247   // low_water_mark (quota units).
    248   int send_quota_high_water_mark_;
    249   // The current amount of quota that the renderer has available for sending
    250   // on this logical channel (quota units).
    251   int current_send_quota_;
    252 
    253   // Storage for the status code and reason from the time we receive the Close
    254   // frame until the connection is closed and we can call OnDropChannel().
    255   uint16 closing_code_;
    256   std::string closing_reason_;
    257 
    258   // The current state of the channel. Mainly used for sanity checking, but also
    259   // used to track the close state.
    260   State state_;
    261 
    262   DISALLOW_COPY_AND_ASSIGN(WebSocketChannel);
    263 };
    264 
    265 }  // namespace net
    266 
    267 #endif  // NET_WEBSOCKETS_WEBSOCKET_CHANNEL_H_
    268