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