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