xref: /aosp_15_r20/external/cronet/net/websockets/websocket_stream.cc (revision 6777b5387eb2ff775bb5750e3f5d96f37fb7352b)
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 #include "net/websockets/websocket_stream.h"
6 
7 #include <optional>
8 #include <ostream>
9 #include <utility>
10 
11 #include "base/check.h"
12 #include "base/check_op.h"
13 #include "base/functional/bind.h"
14 #include "base/location.h"
15 #include "base/logging.h"
16 #include "base/memory/raw_ptr.h"
17 #include "base/memory/weak_ptr.h"
18 #include "base/metrics/histogram_functions.h"
19 #include "base/notreached.h"
20 #include "base/strings/strcat.h"
21 #include "base/time/time.h"
22 #include "base/timer/timer.h"
23 #include "net/base/auth.h"
24 #include "net/base/isolation_info.h"
25 #include "net/base/load_flags.h"
26 #include "net/base/net_errors.h"
27 #include "net/base/request_priority.h"
28 #include "net/base/url_util.h"
29 #include "net/http/http_request_headers.h"
30 #include "net/http/http_response_headers.h"
31 #include "net/http/http_response_info.h"
32 #include "net/http/http_status_code.h"
33 #include "net/traffic_annotation/network_traffic_annotation.h"
34 #include "net/url_request/redirect_info.h"
35 #include "net/url_request/url_request.h"
36 #include "net/url_request/url_request_context.h"
37 #include "net/url_request/websocket_handshake_userdata_key.h"
38 #include "net/websockets/websocket_basic_handshake_stream.h"
39 #include "net/websockets/websocket_event_interface.h"
40 #include "net/websockets/websocket_handshake_constants.h"
41 #include "net/websockets/websocket_handshake_response_info.h"
42 #include "net/websockets/websocket_handshake_stream_base.h"
43 #include "net/websockets/websocket_handshake_stream_create_helper.h"
44 #include "net/websockets/websocket_http2_handshake_stream.h"
45 #include "net/websockets/websocket_http3_handshake_stream.h"
46 #include "url/gurl.h"
47 #include "url/origin.h"
48 
49 namespace net {
50 class SSLCertRequestInfo;
51 class SSLInfo;
52 class SiteForCookies;
53 
54 namespace {
55 
56 // The timeout duration of WebSocket handshake.
57 // It is defined as the same value as the TCP connection timeout value in
58 // net/socket/websocket_transport_client_socket_pool.cc to make it hard for
59 // JavaScript programs to recognize the timeout cause.
60 constexpr int kHandshakeTimeoutIntervalInSeconds = 240;
61 
62 class WebSocketStreamRequestImpl;
63 
64 class Delegate : public URLRequest::Delegate {
65  public:
Delegate(WebSocketStreamRequestImpl * owner)66   explicit Delegate(WebSocketStreamRequestImpl* owner) : owner_(owner) {}
67   ~Delegate() override = default;
68 
69   // Implementation of URLRequest::Delegate methods.
70   int OnConnected(URLRequest* request,
71                   const TransportInfo& info,
72                   CompletionOnceCallback callback) override;
73 
74   void OnReceivedRedirect(URLRequest* request,
75                           const RedirectInfo& redirect_info,
76                           bool* defer_redirect) override;
77 
78   void OnResponseStarted(URLRequest* request, int net_error) override;
79 
80   void OnAuthRequired(URLRequest* request,
81                       const AuthChallengeInfo& auth_info) override;
82 
83   void OnCertificateRequested(URLRequest* request,
84                               SSLCertRequestInfo* cert_request_info) override;
85 
86   void OnSSLCertificateError(URLRequest* request,
87                              int net_error,
88                              const SSLInfo& ssl_info,
89                              bool fatal) override;
90 
91   void OnReadCompleted(URLRequest* request, int bytes_read) override;
92 
93  private:
94   void OnAuthRequiredComplete(URLRequest* request,
95                               const AuthCredentials* auth_credentials);
96 
97   raw_ptr<WebSocketStreamRequestImpl> owner_;
98 };
99 
100 class WebSocketStreamRequestImpl : public WebSocketStreamRequestAPI {
101  public:
WebSocketStreamRequestImpl(const GURL & url,const std::vector<std::string> & requested_subprotocols,const URLRequestContext * context,const url::Origin & origin,const SiteForCookies & site_for_cookies,bool has_storage_access,const IsolationInfo & isolation_info,const HttpRequestHeaders & additional_headers,NetworkTrafficAnnotationTag traffic_annotation,std::unique_ptr<WebSocketStream::ConnectDelegate> connect_delegate,std::unique_ptr<WebSocketStreamRequestAPI> api_delegate)102   WebSocketStreamRequestImpl(
103       const GURL& url,
104       const std::vector<std::string>& requested_subprotocols,
105       const URLRequestContext* context,
106       const url::Origin& origin,
107       const SiteForCookies& site_for_cookies,
108       bool has_storage_access,
109       const IsolationInfo& isolation_info,
110       const HttpRequestHeaders& additional_headers,
111       NetworkTrafficAnnotationTag traffic_annotation,
112       std::unique_ptr<WebSocketStream::ConnectDelegate> connect_delegate,
113       std::unique_ptr<WebSocketStreamRequestAPI> api_delegate)
114       : delegate_(this),
115         connect_delegate_(std::move(connect_delegate)),
116         url_request_(context->CreateRequest(url,
117                                             DEFAULT_PRIORITY,
118                                             &delegate_,
119                                             traffic_annotation,
120                                             /*is_for_websockets=*/true)),
121         api_delegate_(std::move(api_delegate)) {
122     DCHECK_EQ(IsolationInfo::RequestType::kOther,
123               isolation_info.request_type());
124 
125     HttpRequestHeaders headers = additional_headers;
126     headers.SetHeader(websockets::kUpgrade, websockets::kWebSocketLowercase);
127     headers.SetHeader(HttpRequestHeaders::kConnection, websockets::kUpgrade);
128     headers.SetHeader(HttpRequestHeaders::kOrigin, origin.Serialize());
129     headers.SetHeader(websockets::kSecWebSocketVersion,
130                       websockets::kSupportedVersion);
131 
132     // Remove HTTP headers that are important to websocket connections: they
133     // will be added later.
134     headers.RemoveHeader(websockets::kSecWebSocketExtensions);
135     headers.RemoveHeader(websockets::kSecWebSocketKey);
136     headers.RemoveHeader(websockets::kSecWebSocketProtocol);
137 
138     url_request_->SetExtraRequestHeaders(headers);
139     url_request_->set_initiator(origin);
140     url_request_->set_site_for_cookies(site_for_cookies);
141     url_request_->set_has_storage_access(has_storage_access);
142     url_request_->set_isolation_info(isolation_info);
143 
144     auto create_helper = std::make_unique<WebSocketHandshakeStreamCreateHelper>(
145         connect_delegate_.get(), requested_subprotocols, this);
146     url_request_->SetUserData(kWebSocketHandshakeUserDataKey,
147                               std::move(create_helper));
148     url_request_->SetLoadFlags(LOAD_DISABLE_CACHE | LOAD_BYPASS_CACHE);
149     connect_delegate_->OnCreateRequest(url_request_.get());
150   }
151 
152   // Destroying this object destroys the URLRequest, which cancels the request
153   // and so terminates the handshake if it is incomplete.
~WebSocketStreamRequestImpl()154   ~WebSocketStreamRequestImpl() override {
155     if (ws_upgrade_success_) {
156       CHECK(url_request_);
157       // "Cancel" the request with an error code indicating the upgrade
158       // succeeded.
159       url_request_->CancelWithError(ERR_WS_UPGRADE);
160     }
161   }
162 
OnBasicHandshakeStreamCreated(WebSocketBasicHandshakeStream * handshake_stream)163   void OnBasicHandshakeStreamCreated(
164       WebSocketBasicHandshakeStream* handshake_stream) override {
165     if (api_delegate_) {
166       api_delegate_->OnBasicHandshakeStreamCreated(handshake_stream);
167     }
168     OnHandshakeStreamCreated(handshake_stream);
169   }
170 
OnHttp2HandshakeStreamCreated(WebSocketHttp2HandshakeStream * handshake_stream)171   void OnHttp2HandshakeStreamCreated(
172       WebSocketHttp2HandshakeStream* handshake_stream) override {
173     if (api_delegate_) {
174       api_delegate_->OnHttp2HandshakeStreamCreated(handshake_stream);
175     }
176     OnHandshakeStreamCreated(handshake_stream);
177   }
178 
OnHttp3HandshakeStreamCreated(WebSocketHttp3HandshakeStream * handshake_stream)179   void OnHttp3HandshakeStreamCreated(
180       WebSocketHttp3HandshakeStream* handshake_stream) override {
181     if (api_delegate_) {
182       api_delegate_->OnHttp3HandshakeStreamCreated(handshake_stream);
183     }
184     OnHandshakeStreamCreated(handshake_stream);
185   }
186 
OnFailure(const std::string & message,int net_error,std::optional<int> response_code)187   void OnFailure(const std::string& message,
188                  int net_error,
189                  std::optional<int> response_code) override {
190     if (api_delegate_)
191       api_delegate_->OnFailure(message, net_error, response_code);
192     failure_message_ = message;
193     failure_net_error_ = net_error;
194     failure_response_code_ = response_code;
195   }
196 
Start(std::unique_ptr<base::OneShotTimer> timer)197   void Start(std::unique_ptr<base::OneShotTimer> timer) {
198     DCHECK(timer);
199     base::TimeDelta timeout(base::Seconds(kHandshakeTimeoutIntervalInSeconds));
200     timer_ = std::move(timer);
201     timer_->Start(FROM_HERE, timeout,
202                   base::BindOnce(&WebSocketStreamRequestImpl::OnTimeout,
203                                  base::Unretained(this)));
204     url_request_->Start();
205   }
206 
PerformUpgrade()207   void PerformUpgrade() {
208     DCHECK(timer_);
209     DCHECK(connect_delegate_);
210 
211     timer_->Stop();
212 
213     if (!handshake_stream_) {
214       ReportFailureWithMessage(
215           "No handshake stream has been created or handshake stream is already "
216           "destroyed.",
217           ERR_FAILED, std::nullopt);
218       return;
219     }
220 
221     if (!handshake_stream_->CanReadFromStream()) {
222       ReportFailureWithMessage("Handshake stream is not readable.",
223                                ERR_CONNECTION_CLOSED, std::nullopt);
224       return;
225     }
226 
227     ws_upgrade_success_ = true;
228     WebSocketHandshakeStreamBase* handshake_stream = handshake_stream_.get();
229     handshake_stream_.reset();
230     auto handshake_response_info =
231         std::make_unique<WebSocketHandshakeResponseInfo>(
232             url_request_->url(), url_request_->response_headers(),
233             url_request_->GetResponseRemoteEndpoint(),
234             url_request_->response_time());
235     connect_delegate_->OnSuccess(handshake_stream->Upgrade(),
236                                  std::move(handshake_response_info));
237   }
238 
FailureMessageFromNetError(int net_error)239   std::string FailureMessageFromNetError(int net_error) {
240     if (net_error == ERR_TUNNEL_CONNECTION_FAILED) {
241       // This error is common and confusing, so special-case it.
242       // TODO(ricea): Include the HostPortPair of the selected proxy server in
243       // the error message. This is not currently possible because it isn't set
244       // in HttpResponseInfo when a ERR_TUNNEL_CONNECTION_FAILED error happens.
245       return "Establishing a tunnel via proxy server failed.";
246     } else {
247       return base::StrCat(
248           {"Error in connection establishment: ", ErrorToString(net_error)});
249     }
250   }
251 
ReportFailure(int net_error,std::optional<int> response_code)252   void ReportFailure(int net_error, std::optional<int> response_code) {
253     DCHECK(timer_);
254     timer_->Stop();
255     if (failure_message_.empty()) {
256       switch (net_error) {
257         case OK:
258         case ERR_IO_PENDING:
259           break;
260         case ERR_ABORTED:
261           failure_message_ = "WebSocket opening handshake was canceled";
262           break;
263         case ERR_TIMED_OUT:
264           failure_message_ = "WebSocket opening handshake timed out";
265           break;
266         default:
267           failure_message_ = FailureMessageFromNetError(net_error);
268           break;
269       }
270     }
271 
272     ReportFailureWithMessage(
273         failure_message_, failure_net_error_.value_or(net_error),
274         failure_response_code_ ? failure_response_code_ : response_code);
275   }
276 
ReportFailureWithMessage(const std::string & failure_message,int net_error,std::optional<int> response_code)277   void ReportFailureWithMessage(const std::string& failure_message,
278                                 int net_error,
279                                 std::optional<int> response_code) {
280     connect_delegate_->OnFailure(failure_message, net_error, response_code);
281   }
282 
connect_delegate() const283   WebSocketStream::ConnectDelegate* connect_delegate() const {
284     return connect_delegate_.get();
285   }
286 
OnTimeout()287   void OnTimeout() {
288     url_request_->CancelWithError(ERR_TIMED_OUT);
289   }
290 
291  private:
OnHandshakeStreamCreated(WebSocketHandshakeStreamBase * handshake_stream)292   void OnHandshakeStreamCreated(
293       WebSocketHandshakeStreamBase* handshake_stream) {
294     DCHECK(handshake_stream);
295 
296     handshake_stream_ = handshake_stream->GetWeakPtr();
297   }
298 
299   // |delegate_| needs to be declared before |url_request_| so that it gets
300   // initialised first and destroyed second.
301   Delegate delegate_;
302 
303   std::unique_ptr<WebSocketStream::ConnectDelegate> connect_delegate_;
304 
305   // Deleting the WebSocketStreamRequestImpl object deletes this URLRequest
306   // object, cancelling the whole connection. Must be destroyed before
307   // `delegate_`, since `url_request_` has a pointer to it, and before
308   // `connect_delegate_`, because there may be a pointer to it further down the
309   // stack.
310   const std::unique_ptr<URLRequest> url_request_;
311 
312   // This is owned by the caller of
313   // WebsocketHandshakeStreamCreateHelper::CreateBasicStream() or
314   // CreateHttp2Stream() or CreateHttp3Stream().  Both the stream and this
315   // object will be destroyed during the destruction of the URLRequest object
316   // associated with the handshake. This is only guaranteed to be a valid
317   // pointer if the handshake succeeded.
318   base::WeakPtr<WebSocketHandshakeStreamBase> handshake_stream_;
319 
320   // The failure information supplied by WebSocketBasicHandshakeStream, if any.
321   std::string failure_message_;
322   std::optional<int> failure_net_error_;
323   std::optional<int> failure_response_code_;
324 
325   // A timer for handshake timeout.
326   std::unique_ptr<base::OneShotTimer> timer_;
327 
328   // Set to true if the websocket upgrade succeeded.
329   bool ws_upgrade_success_ = false;
330 
331   // A delegate for On*HandshakeCreated and OnFailure calls.
332   std::unique_ptr<WebSocketStreamRequestAPI> api_delegate_;
333 };
334 
335 class SSLErrorCallbacks : public WebSocketEventInterface::SSLErrorCallbacks {
336  public:
SSLErrorCallbacks(URLRequest * url_request)337   explicit SSLErrorCallbacks(URLRequest* url_request)
338       : url_request_(url_request->GetWeakPtr()) {}
339 
CancelSSLRequest(int error,const SSLInfo * ssl_info)340   void CancelSSLRequest(int error, const SSLInfo* ssl_info) override {
341     if (!url_request_)
342       return;
343 
344     if (ssl_info) {
345       url_request_->CancelWithSSLError(error, *ssl_info);
346     } else {
347       url_request_->CancelWithError(error);
348     }
349   }
350 
ContinueSSLRequest()351   void ContinueSSLRequest() override {
352     if (url_request_)
353       url_request_->ContinueDespiteLastError();
354   }
355 
356  private:
357   base::WeakPtr<URLRequest> url_request_;
358 };
359 
OnConnected(URLRequest * request,const TransportInfo & info,CompletionOnceCallback callback)360 int Delegate::OnConnected(URLRequest* request,
361                           const TransportInfo& info,
362                           CompletionOnceCallback callback) {
363   owner_->connect_delegate()->OnURLRequestConnected(request, info);
364   return OK;
365 }
366 
OnReceivedRedirect(URLRequest * request,const RedirectInfo & redirect_info,bool * defer_redirect)367 void Delegate::OnReceivedRedirect(URLRequest* request,
368                                   const RedirectInfo& redirect_info,
369                                   bool* defer_redirect) {
370   // This code should never be reached for externally generated redirects,
371   // as WebSocketBasicHandshakeStream is responsible for filtering out
372   // all response codes besides 101, 401, and 407. As such, the URLRequest
373   // should never see a redirect sent over the network. However, internal
374   // redirects also result in this method being called, such as those
375   // caused by HSTS.
376   // Because it's security critical to prevent externally-generated
377   // redirects in WebSockets, perform additional checks to ensure this
378   // is only internal.
379   GURL::Replacements replacements;
380   replacements.SetSchemeStr("wss");
381   GURL expected_url = request->original_url().ReplaceComponents(replacements);
382   if (redirect_info.new_method != "GET" ||
383       redirect_info.new_url != expected_url) {
384     // This should not happen.
385     DLOG(FATAL) << "Unauthorized WebSocket redirect to "
386                 << redirect_info.new_method << " "
387                 << redirect_info.new_url.spec();
388     request->Cancel();
389   }
390 }
391 
OnResponseStarted(URLRequest * request,int net_error)392 void Delegate::OnResponseStarted(URLRequest* request, int net_error) {
393   DCHECK_NE(ERR_IO_PENDING, net_error);
394 
395   const bool is_http2 =
396       request->response_info().connection_info == HttpConnectionInfo::kHTTP2;
397 
398   // All error codes, including OK and ABORTED, as with
399   // Net.ErrorCodesForMainFrame4
400   base::UmaHistogramSparse("Net.WebSocket.ErrorCodes", -net_error);
401   if (is_http2) {
402     base::UmaHistogramSparse("Net.WebSocket.ErrorCodes.Http2", -net_error);
403   }
404   if (net::IsLocalhost(request->url())) {
405     base::UmaHistogramSparse("Net.WebSocket.ErrorCodes_Localhost", -net_error);
406   } else {
407     base::UmaHistogramSparse("Net.WebSocket.ErrorCodes_NotLocalhost",
408                              -net_error);
409   }
410 
411   if (net_error != OK) {
412     DVLOG(3) << "OnResponseStarted (request failed)";
413     owner_->ReportFailure(net_error, std::nullopt);
414     return;
415   }
416   const int response_code = request->GetResponseCode();
417   DVLOG(3) << "OnResponseStarted (response code " << response_code << ")";
418 
419   if (is_http2) {
420     if (response_code == HTTP_OK) {
421       owner_->PerformUpgrade();
422       return;
423     }
424 
425     owner_->ReportFailure(net_error, std::nullopt);
426     return;
427   }
428 
429   switch (response_code) {
430     case HTTP_SWITCHING_PROTOCOLS:
431       owner_->PerformUpgrade();
432       return;
433 
434     case HTTP_UNAUTHORIZED:
435       owner_->ReportFailureWithMessage(
436           "HTTP Authentication failed; no valid credentials available",
437           net_error, response_code);
438       return;
439 
440     case HTTP_PROXY_AUTHENTICATION_REQUIRED:
441       owner_->ReportFailureWithMessage("Proxy authentication failed", net_error,
442                                        response_code);
443       return;
444 
445     default:
446       owner_->ReportFailure(net_error, response_code);
447   }
448 }
449 
OnAuthRequired(URLRequest * request,const AuthChallengeInfo & auth_info)450 void Delegate::OnAuthRequired(URLRequest* request,
451                               const AuthChallengeInfo& auth_info) {
452   std::optional<AuthCredentials> credentials;
453   // This base::Unretained(this) relies on an assumption that |callback| can
454   // be called called during the opening handshake.
455   int rv = owner_->connect_delegate()->OnAuthRequired(
456       auth_info, request->response_headers(),
457       request->GetResponseRemoteEndpoint(),
458       base::BindOnce(&Delegate::OnAuthRequiredComplete, base::Unretained(this),
459                      request),
460       &credentials);
461   request->LogBlockedBy("WebSocketStream::Delegate::OnAuthRequired");
462   if (rv == ERR_IO_PENDING)
463     return;
464   if (rv != OK) {
465     request->LogUnblocked();
466     owner_->ReportFailure(rv, std::nullopt);
467     return;
468   }
469   OnAuthRequiredComplete(request, nullptr);
470 }
471 
OnAuthRequiredComplete(URLRequest * request,const AuthCredentials * credentials)472 void Delegate::OnAuthRequiredComplete(URLRequest* request,
473                                       const AuthCredentials* credentials) {
474   request->LogUnblocked();
475   if (!credentials) {
476     request->CancelAuth();
477     return;
478   }
479   request->SetAuth(*credentials);
480 }
481 
OnCertificateRequested(URLRequest * request,SSLCertRequestInfo * cert_request_info)482 void Delegate::OnCertificateRequested(URLRequest* request,
483                                       SSLCertRequestInfo* cert_request_info) {
484   // This method is called when a client certificate is requested, and the
485   // request context does not already contain a client certificate selection for
486   // the endpoint. In this case, a main frame resource request would pop-up UI
487   // to permit selection of a client certificate, but since WebSockets are
488   // sub-resources they should not pop-up UI and so there is nothing more we can
489   // do.
490   request->Cancel();
491 }
492 
OnSSLCertificateError(URLRequest * request,int net_error,const SSLInfo & ssl_info,bool fatal)493 void Delegate::OnSSLCertificateError(URLRequest* request,
494                                      int net_error,
495                                      const SSLInfo& ssl_info,
496                                      bool fatal) {
497   owner_->connect_delegate()->OnSSLCertificateError(
498       std::make_unique<SSLErrorCallbacks>(request), net_error, ssl_info, fatal);
499 }
500 
OnReadCompleted(URLRequest * request,int bytes_read)501 void Delegate::OnReadCompleted(URLRequest* request, int bytes_read) {
502   NOTREACHED();
503 }
504 
505 }  // namespace
506 
507 WebSocketStreamRequest::~WebSocketStreamRequest() = default;
508 
509 WebSocketStream::WebSocketStream() = default;
510 WebSocketStream::~WebSocketStream() = default;
511 
512 WebSocketStream::ConnectDelegate::~ConnectDelegate() = default;
513 
CreateAndConnectStream(const GURL & socket_url,const std::vector<std::string> & requested_subprotocols,const url::Origin & origin,const SiteForCookies & site_for_cookies,bool has_storage_access,const IsolationInfo & isolation_info,const HttpRequestHeaders & additional_headers,URLRequestContext * url_request_context,const NetLogWithSource & net_log,NetworkTrafficAnnotationTag traffic_annotation,std::unique_ptr<ConnectDelegate> connect_delegate)514 std::unique_ptr<WebSocketStreamRequest> WebSocketStream::CreateAndConnectStream(
515     const GURL& socket_url,
516     const std::vector<std::string>& requested_subprotocols,
517     const url::Origin& origin,
518     const SiteForCookies& site_for_cookies,
519     bool has_storage_access,
520     const IsolationInfo& isolation_info,
521     const HttpRequestHeaders& additional_headers,
522     URLRequestContext* url_request_context,
523     const NetLogWithSource& net_log,
524     NetworkTrafficAnnotationTag traffic_annotation,
525     std::unique_ptr<ConnectDelegate> connect_delegate) {
526   auto request = std::make_unique<WebSocketStreamRequestImpl>(
527       socket_url, requested_subprotocols, url_request_context, origin,
528       site_for_cookies, has_storage_access, isolation_info, additional_headers,
529       traffic_annotation, std::move(connect_delegate), nullptr);
530   request->Start(std::make_unique<base::OneShotTimer>());
531   return std::move(request);
532 }
533 
534 std::unique_ptr<WebSocketStreamRequest>
CreateAndConnectStreamForTesting(const GURL & socket_url,const std::vector<std::string> & requested_subprotocols,const url::Origin & origin,const SiteForCookies & site_for_cookies,bool has_storage_access,const IsolationInfo & isolation_info,const HttpRequestHeaders & additional_headers,URLRequestContext * url_request_context,const NetLogWithSource & net_log,NetworkTrafficAnnotationTag traffic_annotation,std::unique_ptr<WebSocketStream::ConnectDelegate> connect_delegate,std::unique_ptr<base::OneShotTimer> timer,std::unique_ptr<WebSocketStreamRequestAPI> api_delegate)535 WebSocketStream::CreateAndConnectStreamForTesting(
536     const GURL& socket_url,
537     const std::vector<std::string>& requested_subprotocols,
538     const url::Origin& origin,
539     const SiteForCookies& site_for_cookies,
540     bool has_storage_access,
541     const IsolationInfo& isolation_info,
542     const HttpRequestHeaders& additional_headers,
543     URLRequestContext* url_request_context,
544     const NetLogWithSource& net_log,
545     NetworkTrafficAnnotationTag traffic_annotation,
546     std::unique_ptr<WebSocketStream::ConnectDelegate> connect_delegate,
547     std::unique_ptr<base::OneShotTimer> timer,
548     std::unique_ptr<WebSocketStreamRequestAPI> api_delegate) {
549   auto request = std::make_unique<WebSocketStreamRequestImpl>(
550       socket_url, requested_subprotocols, url_request_context, origin,
551       site_for_cookies, has_storage_access, isolation_info, additional_headers,
552       traffic_annotation, std::move(connect_delegate), std::move(api_delegate));
553   request->Start(std::move(timer));
554   return std::move(request);
555 }
556 
557 }  // namespace net
558