1 // Copyright 2013 The Chromium Authors 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 <stdint.h> 9 10 #include <memory> 11 #include <string> 12 #include <vector> 13 14 #include "base/containers/queue.h" 15 #include "base/functional/callback.h" 16 #include "base/i18n/streaming_utf8_validator.h" 17 #include "base/memory/raw_ptr.h" 18 #include "base/memory/scoped_refptr.h" 19 #include "base/time/time.h" 20 #include "base/timer/timer.h" 21 #include "net/base/net_export.h" 22 #include "net/websockets/websocket_event_interface.h" 23 #include "net/websockets/websocket_frame.h" 24 #include "net/websockets/websocket_stream.h" 25 #include "third_party/abseil-cpp/absl/types/optional.h" 26 #include "url/gurl.h" 27 28 namespace url { 29 class Origin; 30 } // namespace url 31 32 namespace net { 33 34 class HttpRequestHeaders; 35 class IOBuffer; 36 class IPEndPoint; 37 class NetLogWithSource; 38 class IsolationInfo; 39 class SiteForCookies; 40 class URLRequest; 41 class URLRequestContext; 42 struct WebSocketHandshakeRequestInfo; 43 struct WebSocketHandshakeResponseInfo; 44 struct NetworkTrafficAnnotationTag; 45 46 // Transport-independent implementation of WebSockets. Implements protocol 47 // semantics that do not depend on the underlying transport. Provides the 48 // interface to the content layer. Some WebSocket concepts are used here without 49 // definition; please see the RFC at http://tools.ietf.org/html/rfc6455 for 50 // clarification. 51 class NET_EXPORT WebSocketChannel { 52 public: 53 // The type of a WebSocketStream creator callback. Must match the signature of 54 // WebSocketStream::CreateAndConnectStream(). 55 typedef base::OnceCallback<std::unique_ptr<WebSocketStreamRequest>( 56 const GURL&, 57 const std::vector<std::string>&, 58 const url::Origin&, 59 const SiteForCookies&, 60 const IsolationInfo&, 61 const HttpRequestHeaders&, 62 URLRequestContext*, 63 const NetLogWithSource&, 64 NetworkTrafficAnnotationTag, 65 std::unique_ptr<WebSocketStream::ConnectDelegate>)> 66 WebSocketStreamRequestCreationCallback; 67 68 // Methods which return a value of type ChannelState may delete |this|. If the 69 // return value is CHANNEL_DELETED, then the caller must return without making 70 // any further access to member variables or methods. 71 enum ChannelState { CHANNEL_ALIVE, CHANNEL_DELETED }; 72 73 // Creates a new WebSocketChannel in an idle state. 74 // SendAddChannelRequest() must be called immediately afterwards to start the 75 // connection process. 76 WebSocketChannel(std::unique_ptr<WebSocketEventInterface> event_interface, 77 URLRequestContext* url_request_context); 78 79 WebSocketChannel(const WebSocketChannel&) = delete; 80 WebSocketChannel& operator=(const WebSocketChannel&) = delete; 81 82 virtual ~WebSocketChannel(); 83 84 // Starts the connection process. 85 void SendAddChannelRequest( 86 const GURL& socket_url, 87 const std::vector<std::string>& requested_protocols, 88 const url::Origin& origin, 89 const SiteForCookies& site_for_cookies, 90 const IsolationInfo& isolation_info, 91 const HttpRequestHeaders& additional_headers, 92 NetworkTrafficAnnotationTag traffic_annotation); 93 94 // Sends a data frame to the remote side. It is the responsibility of the 95 // caller to ensure that they have sufficient send quota to send this data, 96 // otherwise the connection will be closed without sending. |fin| indicates 97 // the last frame in a message, equivalent to "FIN" as specified in section 98 // 5.2 of RFC6455. |buffer->data()| is the "Payload Data". If |op_code| is 99 // kOpCodeText, or it is kOpCodeContinuation and the type the message is 100 // Text, then |buffer->data()| must be a chunk of a valid UTF-8 message, 101 // however there is no requirement for |buffer->data()| to be split on 102 // character boundaries. Calling SendFrame may result in synchronous calls to 103 // |event_interface_| which may result in this object being deleted. In that 104 // case, the return value will be CHANNEL_DELETED. 105 [[nodiscard]] ChannelState SendFrame(bool fin, 106 WebSocketFrameHeader::OpCode op_code, 107 scoped_refptr<IOBuffer> buffer, 108 size_t buffer_size); 109 110 // Calls WebSocketStream::ReadFrames() with the appropriate arguments. Stops 111 // calling ReadFrames if no writable buffer in dataframe or WebSocketStream 112 // starts async read. 113 [[nodiscard]] ChannelState ReadFrames(); 114 115 // Starts the closing handshake for a client-initiated shutdown of the 116 // connection. There is no API to close the connection without a closing 117 // handshake, but destroying the WebSocketChannel object while connected will 118 // effectively do that. |code| must be in the range 1000-4999. |reason| should 119 // be a valid UTF-8 string or empty. 120 // 121 // Calling this function may result in synchronous calls to |event_interface_| 122 // which may result in this object being deleted. In that case, the return 123 // value will be CHANNEL_DELETED. 124 [[nodiscard]] ChannelState StartClosingHandshake(uint16_t code, 125 const std::string& reason); 126 127 // Starts the connection process, using a specified creator callback rather 128 // than the default. This is exposed for testing. 129 void SendAddChannelRequestForTesting( 130 const GURL& socket_url, 131 const std::vector<std::string>& requested_protocols, 132 const url::Origin& origin, 133 const SiteForCookies& site_for_cookies, 134 const IsolationInfo& isolation_info, 135 const HttpRequestHeaders& additional_headers, 136 NetworkTrafficAnnotationTag traffic_annotation, 137 WebSocketStreamRequestCreationCallback callback); 138 139 // The default timout for the closing handshake is a sensible value (see 140 // kClosingHandshakeTimeoutSeconds in websocket_channel.cc). However, we can 141 // set it to a very small value for testing purposes. 142 void SetClosingHandshakeTimeoutForTesting(base::TimeDelta delay); 143 144 // The default timout for the underlying connection close is a sensible value 145 // (see kUnderlyingConnectionCloseTimeoutSeconds in websocket_channel.cc). 146 // However, we can set it to a very small value for testing purposes. 147 void SetUnderlyingConnectionCloseTimeoutForTesting(base::TimeDelta delay); 148 149 // Called when the stream starts the WebSocket Opening Handshake. 150 // This method is public for testing. 151 void OnStartOpeningHandshake( 152 std::unique_ptr<WebSocketHandshakeRequestInfo> request); 153 154 private: 155 // The object passes through a linear progression of states from 156 // FRESHLY_CONSTRUCTED to CLOSED, except that the SEND_CLOSED and RECV_CLOSED 157 // states may be skipped in case of error. 158 enum State { 159 FRESHLY_CONSTRUCTED, 160 CONNECTING, 161 CONNECTED, 162 SEND_CLOSED, // A Close frame has been sent but not received. 163 RECV_CLOSED, // Used briefly between receiving a Close frame and sending 164 // the response. Once the response is sent, the state changes 165 // to CLOSED. 166 CLOSE_WAIT, // The Closing Handshake has completed, but the remote server 167 // has not yet closed the connection. 168 CLOSED, // The Closing Handshake has completed and the connection 169 // has been closed; or the connection is failed. 170 }; 171 172 // Implementation of WebSocketStream::ConnectDelegate for 173 // WebSocketChannel. WebSocketChannel does not inherit from 174 // WebSocketStream::ConnectDelegate directly to avoid cluttering the public 175 // interface with the implementation of those methods, and because the 176 // lifetime of a WebSocketChannel is longer than the lifetime of the 177 // connection process. 178 class ConnectDelegate; 179 180 // Starts the connection process, using the supplied stream request creation 181 // callback. 182 void SendAddChannelRequestWithSuppliedCallback( 183 const GURL& socket_url, 184 const std::vector<std::string>& requested_protocols, 185 const url::Origin& origin, 186 const SiteForCookies& site_for_cookies, 187 const IsolationInfo& isolation_info, 188 const HttpRequestHeaders& additional_headers, 189 NetworkTrafficAnnotationTag traffic_annotation, 190 WebSocketStreamRequestCreationCallback callback); 191 192 // Called when a URLRequest is created for handshaking. 193 void OnCreateURLRequest(URLRequest* request); 194 195 // Success callback from WebSocketStream::CreateAndConnectStream(). Reports 196 // success to the event interface. May delete |this|. 197 void OnConnectSuccess( 198 std::unique_ptr<WebSocketStream> stream, 199 std::unique_ptr<WebSocketHandshakeResponseInfo> response); 200 201 // Failure callback from WebSocketStream::CreateAndConnectStream(). Reports 202 // failure to the event interface. May delete |this|. 203 void OnConnectFailure(const std::string& message, 204 int net_error, 205 absl::optional<int> response_code); 206 207 // SSL certificate error callback from 208 // WebSocketStream::CreateAndConnectStream(). Forwards the request to the 209 // event interface. 210 void OnSSLCertificateError( 211 std::unique_ptr<WebSocketEventInterface::SSLErrorCallbacks> 212 ssl_error_callbacks, 213 int net_error, 214 const SSLInfo& ssl_info, 215 bool fatal); 216 217 // Authentication request from WebSocketStream::CreateAndConnectStream(). 218 // Forwards the request to the event interface. 219 int OnAuthRequired(const AuthChallengeInfo& auth_info, 220 scoped_refptr<HttpResponseHeaders> response_headers, 221 const IPEndPoint& remote_endpoint, 222 base::OnceCallback<void(const AuthCredentials*)> callback, 223 absl::optional<AuthCredentials>* credentials); 224 225 // Sets |state_| to |new_state| and updates UMA if necessary. 226 void SetState(State new_state); 227 228 // Returns true if state_ is SEND_CLOSED, CLOSE_WAIT or CLOSED. 229 bool InClosingState() const; 230 231 // Calls WebSocketStream::WriteFrames() with the appropriate arguments 232 [[nodiscard]] ChannelState WriteFrames(); 233 234 // Callback from WebSocketStream::WriteFrames. Sends pending data or adjusts 235 // the send quota of the renderer channel as appropriate. |result| is a net 236 // error code, usually OK. If |synchronous| is true, then OnWriteDone() is 237 // being called from within the WriteFrames() loop and does not need to call 238 // WriteFrames() itself. 239 [[nodiscard]] ChannelState OnWriteDone(bool synchronous, int result); 240 241 // Callback from WebSocketStream::ReadFrames. Handles any errors and processes 242 // the returned chunks appropriately to their type. |result| is a net error 243 // code. If |synchronous| is true, then OnReadDone() is being called from 244 // within the ReadFrames() loop and does not need to call ReadFrames() itself. 245 [[nodiscard]] ChannelState OnReadDone(bool synchronous, int result); 246 247 // Handles a single frame that the object has received enough of to process. 248 // May call |event_interface_| methods, send responses to the server, and 249 // change the value of |state_|. 250 // 251 // This method performs sanity checks on the frame that are needed regardless 252 // of the current state. Then, calls the HandleFrameByState() method below 253 // which performs the appropriate action(s) depending on the current state. 254 [[nodiscard]] ChannelState HandleFrame(std::unique_ptr<WebSocketFrame> frame); 255 256 // Handles a single frame depending on the current state. It's used by the 257 // HandleFrame() method. 258 [[nodiscard]] ChannelState HandleFrameByState( 259 const WebSocketFrameHeader::OpCode opcode, 260 bool final, 261 base::span<const char> payload); 262 263 // Forwards a received data frame to the renderer, if connected. If 264 // |expecting_continuation| is not equal to |expecting_to_read_continuation_|, 265 // will fail the channel. Also checks the UTF-8 validity of text frames. 266 [[nodiscard]] ChannelState HandleDataFrame( 267 WebSocketFrameHeader::OpCode opcode, 268 bool final, 269 base::span<const char> payload); 270 271 // Handles an incoming close frame with |code| and |reason|. 272 [[nodiscard]] ChannelState HandleCloseFrame(uint16_t code, 273 const std::string& reason); 274 275 // Responds to a closing handshake initiated by the server. 276 [[nodiscard]] ChannelState RespondToClosingHandshake(); 277 278 // Low-level method to send a single frame. Used for both data and control 279 // frames. Either sends the frame immediately or buffers it to be scheduled 280 // when the current write finishes. |fin| and |op_code| are defined as for 281 // SendFrame() above, except that |op_code| may also be a control frame 282 // opcode. 283 [[nodiscard]] ChannelState SendFrameInternal( 284 bool fin, 285 WebSocketFrameHeader::OpCode op_code, 286 scoped_refptr<IOBuffer> buffer, 287 uint64_t buffer_size); 288 289 // Performs the "Fail the WebSocket Connection" operation as defined in 290 // RFC6455. A NotifyFailure message is sent to the renderer with |message|. 291 // The renderer will log the message to the console but not expose it to 292 // Javascript. Javascript will see a Close code of AbnormalClosure (1006) with 293 // an empty reason string. If state_ is CONNECTED then a Close message is sent 294 // to the remote host containing the supplied |code| and |reason|. If the 295 // stream is open, closes it and sets state_ to CLOSED. This function deletes 296 // |this|. 297 void FailChannel(const std::string& message, 298 uint16_t code, 299 const std::string& reason); 300 301 // Sends a Close frame to Start the WebSocket Closing Handshake, or to respond 302 // to a Close frame from the server. As a special case, setting |code| to 303 // kWebSocketErrorNoStatusReceived will create a Close frame with no payload; 304 // this is symmetric with the behaviour of ParseClose. 305 [[nodiscard]] ChannelState SendClose(uint16_t code, 306 const std::string& reason); 307 308 // Parses a Close frame payload. If no status code is supplied, then |code| is 309 // set to 1005 (No status code) with empty |reason|. If the reason text is not 310 // valid UTF-8, then |reason| is set to an empty string. If the payload size 311 // is 1, or the supplied code is not permitted to be sent over the network, 312 // then false is returned and |message| is set to an appropriate console 313 // message. 314 bool ParseClose(base::span<const char> payload, 315 uint16_t* code, 316 std::string* reason, 317 std::string* message); 318 319 // Drop this channel. 320 // If there are pending opening handshake notifications, notify them 321 // before dropping. This function deletes |this|. 322 void DoDropChannel(bool was_clean, uint16_t code, const std::string& reason); 323 324 // Called if the closing handshake times out. Closes the connection and 325 // informs the |event_interface_| if appropriate. 326 void CloseTimeout(); 327 328 // The URL of the remote server. 329 GURL socket_url_; 330 331 // The object receiving events. 332 const std::unique_ptr<WebSocketEventInterface> event_interface_; 333 334 // The URLRequestContext to pass to the WebSocketStream creator. 335 const raw_ptr<URLRequestContext> url_request_context_; 336 337 // The WebSocketStream on which to send and receive data. 338 std::unique_ptr<WebSocketStream> stream_; 339 340 // A data structure containing a vector of frames to be sent and the total 341 // number of bytes contained in the vector. 342 class SendBuffer; 343 // Data that is currently pending write, or NULL if no write is pending. 344 std::unique_ptr<SendBuffer> data_being_sent_; 345 // Data that is queued up to write after the current write completes. 346 // Only non-NULL when such data actually exists. 347 std::unique_ptr<SendBuffer> data_to_send_next_; 348 349 // Destination for the current call to WebSocketStream::ReadFrames 350 std::vector<std::unique_ptr<WebSocketFrame>> read_frames_; 351 352 // Handle to an in-progress WebSocketStream creation request. Only non-NULL 353 // during the connection process. 354 std::unique_ptr<WebSocketStreamRequest> stream_request_; 355 356 // Timer for the closing handshake. 357 base::OneShotTimer close_timer_; 358 359 // Timeout for the closing handshake. 360 base::TimeDelta closing_handshake_timeout_; 361 362 // Timeout for the underlying connection close after completion of closing 363 // handshake. 364 base::TimeDelta underlying_connection_close_timeout_; 365 366 // Storage for the status code and reason from the time the Close frame 367 // arrives until the connection is closed and they are passed to 368 // OnDropChannel(). 369 bool has_received_close_frame_ = false; 370 uint16_t received_close_code_ = 0; 371 std::string received_close_reason_; 372 373 // The current state of the channel. Mainly used for sanity checking, but also 374 // used to track the close state. 375 State state_ = FRESHLY_CONSTRUCTED; 376 377 // UTF-8 validator for outgoing Text messages. 378 base::StreamingUtf8Validator outgoing_utf8_validator_; 379 bool sending_text_message_ = false; 380 381 // UTF-8 validator for incoming Text messages. 382 base::StreamingUtf8Validator incoming_utf8_validator_; 383 bool receiving_text_message_ = false; 384 385 // True if we are in the middle of receiving a message. 386 bool expecting_to_handle_continuation_ = false; 387 388 // True if we have already sent the type (Text or Binary) of the current 389 // message to the renderer. This can be false if the message is empty so far. 390 bool initial_frame_forwarded_ = false; 391 392 // True if we're waiting for OnReadDone() callback. 393 bool is_reading_ = false; 394 }; 395 396 } // namespace net 397 398 #endif // NET_WEBSOCKETS_WEBSOCKET_CHANNEL_H_ 399