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