• 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 <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