xref: /aosp_15_r20/external/cronet/net/websockets/websocket_stream_test.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 <algorithm>
8 #include <iterator>
9 #include <string>
10 #include <string_view>
11 #include <tuple>
12 #include <utility>
13 #include <vector>
14 
15 #include "base/check_op.h"
16 #include "base/containers/span.h"
17 #include "base/memory/weak_ptr.h"
18 #include "base/metrics/histogram_samples.h"
19 #include "base/run_loop.h"
20 #include "base/strings/string_util.h"
21 #include "base/strings/stringprintf.h"
22 #include "base/test/metrics/histogram_tester.h"
23 #include "base/test/scoped_feature_list.h"
24 #include "base/timer/mock_timer.h"
25 #include "base/timer/timer.h"
26 #include "net/base/auth.h"
27 #include "net/base/features.h"
28 #include "net/base/isolation_info.h"
29 #include "net/base/net_errors.h"
30 #include "net/base/request_priority.h"
31 #include "net/base/test_completion_callback.h"
32 #include "net/base/url_util.h"
33 #include "net/cookies/cookie_setting_override.h"
34 #include "net/cookies/site_for_cookies.h"
35 #include "net/http/http_network_session.h"
36 #include "net/http/http_request_headers.h"
37 #include "net/http/http_response_headers.h"
38 #include "net/log/net_log_with_source.h"
39 #include "net/socket/next_proto.h"
40 #include "net/socket/socket_test_util.h"
41 #include "net/spdy/spdy_test_util_common.h"
42 #include "net/ssl/ssl_info.h"
43 #include "net/test/cert_test_util.h"
44 #include "net/test/gtest_util.h"
45 #include "net/test/test_data_directory.h"
46 #include "net/third_party/quiche/src/quiche/spdy/core/http2_header_block.h"
47 #include "net/third_party/quiche/src/quiche/spdy/core/spdy_protocol.h"
48 #include "net/third_party/quiche/src/quiche/spdy/test_tools/spdy_test_utils.h"
49 #include "net/traffic_annotation/network_traffic_annotation_test_helper.h"
50 #include "net/url_request/url_request.h"
51 #include "net/url_request/url_request_context.h"
52 #include "net/url_request/url_request_test_util.h"
53 #include "net/websockets/websocket_frame.h"
54 #include "net/websockets/websocket_handshake_request_info.h"
55 #include "net/websockets/websocket_handshake_response_info.h"
56 #include "net/websockets/websocket_handshake_stream_base.h"
57 #include "net/websockets/websocket_stream_create_test_base.h"
58 #include "net/websockets/websocket_test_util.h"
59 #include "testing/gmock/include/gmock/gmock.h"
60 #include "testing/gtest/include/gtest/gtest.h"
61 #include "url/gurl.h"
62 #include "url/origin.h"
63 
64 using ::net::test::IsError;
65 using ::net::test::IsOk;
66 using ::testing::TestWithParam;
67 using ::testing::Values;
68 
69 namespace net {
70 namespace {
71 
72 enum HandshakeStreamType { BASIC_HANDSHAKE_STREAM, HTTP2_HANDSHAKE_STREAM };
73 
74 // Simple builder for a SequencedSocketData object to save repetitive code.
75 // It always sets the connect data to MockConnect(SYNCHRONOUS, OK), so it cannot
76 // be used in tests where the connect fails. In practice, those tests never have
77 // any read/write data and so can't benefit from it anyway.  The arrays are not
78 // copied. It is up to the caller to ensure they stay in scope until the test
79 // ends.
BuildSocketData(base::span<MockRead> reads,base::span<MockWrite> writes)80 std::unique_ptr<SequencedSocketData> BuildSocketData(
81     base::span<MockRead> reads,
82     base::span<MockWrite> writes) {
83   auto socket_data = std::make_unique<SequencedSocketData>(reads, writes);
84   socket_data->set_connect_data(MockConnect(SYNCHRONOUS, OK));
85   return socket_data;
86 }
87 
88 // Builder for a SequencedSocketData that expects nothing. This does not
89 // set the connect data, so the calling code must do that explicitly.
BuildNullSocketData()90 std::unique_ptr<SequencedSocketData> BuildNullSocketData() {
91   return std::make_unique<SequencedSocketData>();
92 }
93 
94 class MockWeakTimer : public base::MockOneShotTimer {
95  public:
96   MockWeakTimer() = default;
97 
AsWeakPtr()98   base::WeakPtr<MockWeakTimer> AsWeakPtr() {
99     return weak_ptr_factory_.GetWeakPtr();
100   }
101 
102  private:
103   base::WeakPtrFactory<MockWeakTimer> weak_ptr_factory_{this};
104 };
105 
106 constexpr char kOrigin[] = "http://www.example.org";
107 
Origin()108 static url::Origin Origin() {
109   return url::Origin::Create(GURL(kOrigin));
110 }
111 
SiteForCookies()112 static net::SiteForCookies SiteForCookies() {
113   return net::SiteForCookies::FromOrigin(Origin());
114 }
115 
CreateIsolationInfo()116 static IsolationInfo CreateIsolationInfo() {
117   url::Origin origin = Origin();
118   return IsolationInfo::Create(IsolationInfo::RequestType::kOther, origin,
119                                origin, SiteForCookies::FromOrigin(origin));
120 }
121 
122 class WebSocketStreamCreateTest
123     : public TestWithParam<std::tuple<HandshakeStreamType, bool>>,
124       public WebSocketStreamCreateTestBase {
125  protected:
WebSocketStreamCreateTest()126   WebSocketStreamCreateTest()
127       : stream_type_(std::get<HandshakeStreamType>(GetParam())),
128         spdy_util_(/*use_priority_header=*/true) {
129     // Make sure these tests all pass with connection partitioning enabled. The
130     // disabled case is less interesting, and is tested more directly at lower
131     // layers.
132     if (PriorityHeaderEnabled()) {
133       feature_list_.InitWithFeatures(
134           {features::kPartitionConnectionsByNetworkIsolationKey,
135            net::features::kPriorityHeader},
136           {});
137     } else {
138       feature_list_.InitWithFeatures(
139           {features::kPartitionConnectionsByNetworkIsolationKey},
140           {net::features::kPriorityHeader});
141     }
142   }
143 
~WebSocketStreamCreateTest()144   ~WebSocketStreamCreateTest() override {
145     // Permit any endpoint locks to be released.
146     stream_request_.reset();
147     stream_.reset();
148     base::RunLoop().RunUntilIdle();
149   }
150 
151   // Normally it's easier to use CreateAndConnectRawExpectations() instead. This
152   // method is only needed when multiple sockets are involved.
AddRawExpectations(std::unique_ptr<SequencedSocketData> socket_data)153   void AddRawExpectations(std::unique_ptr<SequencedSocketData> socket_data) {
154     url_request_context_host_.AddRawExpectations(std::move(socket_data));
155   }
156 
AddSSLData()157   void AddSSLData() {
158     auto ssl_data = std::make_unique<SSLSocketDataProvider>(ASYNC, OK);
159     ssl_data->ssl_info.cert =
160         ImportCertFromFile(GetTestCertsDirectory(), "wildcard.pem");
161     if (stream_type_ == HTTP2_HANDSHAKE_STREAM)
162       ssl_data->next_proto = kProtoHTTP2;
163     ASSERT_TRUE(ssl_data->ssl_info.cert.get());
164     url_request_context_host_.AddSSLSocketDataProvider(std::move(ssl_data));
165   }
166 
SetTimer(std::unique_ptr<base::OneShotTimer> timer)167   void SetTimer(std::unique_ptr<base::OneShotTimer> timer) {
168     timer_ = std::move(timer);
169   }
170 
SetAdditionalResponseData(std::string additional_data)171   void SetAdditionalResponseData(std::string additional_data) {
172     additional_data_ = std::move(additional_data);
173   }
174 
SetHttp2ResponseStatus(const char * const http2_response_status)175   void SetHttp2ResponseStatus(const char* const http2_response_status) {
176     http2_response_status_ = http2_response_status;
177   }
178 
SetResetWebSocketHttp2Stream(bool reset_websocket_http2_stream)179   void SetResetWebSocketHttp2Stream(bool reset_websocket_http2_stream) {
180     reset_websocket_http2_stream_ = reset_websocket_http2_stream;
181   }
182 
183   // Set up mock data and start websockets request, either for WebSocket
184   // upgraded from an HTTP/1 connection, or for a WebSocket request over HTTP/2.
CreateAndConnectStandard(std::string_view url,const std::vector<std::string> & sub_protocols,const WebSocketExtraHeaders & send_additional_request_headers,const WebSocketExtraHeaders & extra_request_headers,const WebSocketExtraHeaders & extra_response_headers,bool has_storage_access=false)185   void CreateAndConnectStandard(
186       std::string_view url,
187       const std::vector<std::string>& sub_protocols,
188       const WebSocketExtraHeaders& send_additional_request_headers,
189       const WebSocketExtraHeaders& extra_request_headers,
190       const WebSocketExtraHeaders& extra_response_headers,
191       bool has_storage_access = false) {
192     const GURL socket_url(url);
193     const std::string socket_host = GetHostAndOptionalPort(socket_url);
194     const std::string socket_path = socket_url.path();
195 
196     if (stream_type_ == BASIC_HANDSHAKE_STREAM) {
197       url_request_context_host_.SetExpectations(
198           WebSocketStandardRequest(socket_path, socket_host, Origin(),
199                                    send_additional_request_headers,
200                                    extra_request_headers),
201           WebSocketStandardResponse(
202               WebSocketExtraHeadersToString(extra_response_headers)) +
203               additional_data_);
204       CreateAndConnectStream(socket_url, sub_protocols, Origin(),
205                              SiteForCookies(), has_storage_access,
206                              CreateIsolationInfo(),
207                              WebSocketExtraHeadersToHttpRequestHeaders(
208                                  send_additional_request_headers),
209                              std::move(timer_));
210       return;
211     }
212 
213     DCHECK_EQ(stream_type_, HTTP2_HANDSHAKE_STREAM);
214 
215     // TODO(bnc): Find a way to clear
216     // spdy_session_pool.enable_sending_initial_data_ to avoid sending
217     // connection preface, initial settings, and window update.
218 
219     // HTTP/2 connection preface.
220     frames_.emplace_back(spdy::test::MakeSerializedFrame(
221         const_cast<char*>(spdy::kHttp2ConnectionHeaderPrefix),
222         spdy::kHttp2ConnectionHeaderPrefixSize));
223     AddWrite(&frames_.back());
224 
225     // Server advertises WebSockets over HTTP/2 support.
226     spdy::SettingsMap read_settings;
227     read_settings[spdy::SETTINGS_ENABLE_CONNECT_PROTOCOL] = 1;
228     frames_.push_back(spdy_util_.ConstructSpdySettings(read_settings));
229     AddRead(&frames_.back());
230 
231     // Initial SETTINGS frame.
232     spdy::SettingsMap write_settings;
233     write_settings[spdy::SETTINGS_HEADER_TABLE_SIZE] = kSpdyMaxHeaderTableSize;
234     write_settings[spdy::SETTINGS_INITIAL_WINDOW_SIZE] = 6 * 1024 * 1024;
235     write_settings[spdy::SETTINGS_MAX_HEADER_LIST_SIZE] =
236         kSpdyMaxHeaderListSize;
237     write_settings[spdy::SETTINGS_ENABLE_PUSH] = 0;
238     frames_.push_back(spdy_util_.ConstructSpdySettings(write_settings));
239     AddWrite(&frames_.back());
240 
241     // Initial window update frame.
242     frames_.push_back(spdy_util_.ConstructSpdyWindowUpdate(0, 0x00ef0001));
243     AddWrite(&frames_.back());
244 
245     // SETTINGS ACK sent as a response to server's SETTINGS frame.
246     frames_.push_back(spdy_util_.ConstructSpdySettingsAck());
247     AddWrite(&frames_.back());
248 
249     // First request.  This is necessary, because a WebSockets request currently
250     // does not open a new HTTP/2 connection, it only uses an existing one.
251     const char* const kExtraRequestHeaders[] = {
252         "user-agent",      "",        "accept-encoding", "gzip, deflate",
253         "accept-language", "en-us,fr"};
254     frames_.push_back(spdy_util_.ConstructSpdyGet(
255         kExtraRequestHeaders, std::size(kExtraRequestHeaders) / 2, 1,
256         DEFAULT_PRIORITY));
257     AddWrite(&frames_.back());
258 
259     // SETTINGS ACK frame sent by the server in response to the client's
260     // initial SETTINGS frame.
261     frames_.push_back(spdy_util_.ConstructSpdySettingsAck());
262     AddRead(&frames_.back());
263 
264     // Response headers to first request.
265     frames_.push_back(spdy_util_.ConstructSpdyGetReply(nullptr, 0, 1));
266     AddRead(&frames_.back());
267 
268     // Response body to first request.
269     frames_.push_back(spdy_util_.ConstructSpdyDataFrame(1, true));
270     AddRead(&frames_.back());
271 
272     // First request is closed.
273     spdy_util_.UpdateWithStreamDestruction(1);
274 
275     // WebSocket request.
276     spdy::Http2HeaderBlock request_headers = WebSocketHttp2Request(
277         socket_path, socket_host, kOrigin, extra_request_headers);
278     frames_.push_back(spdy_util_.ConstructSpdyHeaders(
279         3, std::move(request_headers), DEFAULT_PRIORITY, false));
280     AddWrite(&frames_.back());
281 
282     if (reset_websocket_http2_stream_) {
283       frames_.push_back(
284           spdy_util_.ConstructSpdyRstStream(3, spdy::ERROR_CODE_CANCEL));
285       AddRead(&frames_.back());
286     } else {
287       // Response to WebSocket request.
288       std::vector<std::string> extra_response_header_keys;
289       std::vector<const char*> extra_response_headers_vector;
290       for (const auto& extra_header : extra_response_headers) {
291         // Save a lowercase copy of the header key.
292         extra_response_header_keys.push_back(
293             base::ToLowerASCII(extra_header.first));
294         // Save a pointer to this lowercase copy.
295         extra_response_headers_vector.push_back(
296             extra_response_header_keys.back().c_str());
297         // Save a pointer to the original header value provided by the caller.
298         extra_response_headers_vector.push_back(extra_header.second.c_str());
299       }
300       frames_.push_back(spdy_util_.ConstructSpdyReplyError(
301           http2_response_status_, extra_response_headers_vector.data(),
302           extra_response_headers_vector.size() / 2, 3));
303       AddRead(&frames_.back());
304 
305       // WebSocket data received.
306       if (!additional_data_.empty()) {
307         frames_.push_back(
308             spdy_util_.ConstructSpdyDataFrame(3, additional_data_, true));
309         AddRead(&frames_.back());
310       }
311 
312       // Client cancels HTTP/2 stream when request is destroyed.
313       frames_.push_back(
314           spdy_util_.ConstructSpdyRstStream(3, spdy::ERROR_CODE_CANCEL));
315       AddWrite(&frames_.back());
316     }
317 
318     // EOF.
319     reads_.emplace_back(ASYNC, 0, sequence_number_++);
320 
321     auto socket_data = std::make_unique<SequencedSocketData>(reads_, writes_);
322     socket_data->set_connect_data(MockConnect(SYNCHRONOUS, OK));
323     AddRawExpectations(std::move(socket_data));
324 
325     // Send first request.  This makes sure server's
326     // spdy::SETTINGS_ENABLE_CONNECT_PROTOCOL advertisement is read.
327     URLRequestContext* context =
328         url_request_context_host_.GetURLRequestContext();
329     TestDelegate delegate;
330     std::unique_ptr<URLRequest> request = context->CreateRequest(
331         GURL("https://www.example.org/"), DEFAULT_PRIORITY, &delegate,
332         TRAFFIC_ANNOTATION_FOR_TESTS, /*is_for_websockets=*/false);
333     // The IsolationInfo has to match for a socket to be reused.
334     request->set_isolation_info(CreateIsolationInfo());
335     request->Start();
336     EXPECT_TRUE(request->is_pending());
337     delegate.RunUntilComplete();
338     EXPECT_FALSE(request->is_pending());
339 
340     CreateAndConnectStream(socket_url, sub_protocols, Origin(),
341                            SiteForCookies(), has_storage_access,
342                            CreateIsolationInfo(),
343                            WebSocketExtraHeadersToHttpRequestHeaders(
344                                send_additional_request_headers),
345                            std::move(timer_));
346   }
347 
348   // Like CreateAndConnectStandard(), but allow for arbitrary response body.
349   // Only for HTTP/1-based WebSockets.
CreateAndConnectCustomResponse(std::string_view url,const std::vector<std::string> & sub_protocols,const WebSocketExtraHeaders & send_additional_request_headers,const WebSocketExtraHeaders & extra_request_headers,const std::string & response_body,bool has_storage_access=false)350   void CreateAndConnectCustomResponse(
351       std::string_view url,
352       const std::vector<std::string>& sub_protocols,
353       const WebSocketExtraHeaders& send_additional_request_headers,
354       const WebSocketExtraHeaders& extra_request_headers,
355       const std::string& response_body,
356       bool has_storage_access = false) {
357     ASSERT_EQ(BASIC_HANDSHAKE_STREAM, stream_type_);
358 
359     const GURL socket_url(url);
360     const std::string socket_host = GetHostAndOptionalPort(socket_url);
361     const std::string socket_path = socket_url.path();
362 
363     url_request_context_host_.SetExpectations(
364         WebSocketStandardRequest(socket_path, socket_host, Origin(),
365                                  send_additional_request_headers,
366                                  extra_request_headers),
367         response_body);
368     CreateAndConnectStream(socket_url, sub_protocols, Origin(),
369                            SiteForCookies(), has_storage_access,
370                            CreateIsolationInfo(),
371                            WebSocketExtraHeadersToHttpRequestHeaders(
372                                send_additional_request_headers),
373                            nullptr);
374   }
375 
376   // Like CreateAndConnectStandard(), but take extra response headers as a
377   // string.  This can save space in case of a very large response.
378   // Only for HTTP/1-based WebSockets.
CreateAndConnectStringResponse(std::string_view url,const std::vector<std::string> & sub_protocols,const std::string & extra_response_headers,bool has_storage_access=false)379   void CreateAndConnectStringResponse(
380       std::string_view url,
381       const std::vector<std::string>& sub_protocols,
382       const std::string& extra_response_headers,
383       bool has_storage_access = false) {
384     ASSERT_EQ(BASIC_HANDSHAKE_STREAM, stream_type_);
385 
386     const GURL socket_url(url);
387     const std::string socket_host = GetHostAndOptionalPort(socket_url);
388     const std::string socket_path = socket_url.path();
389 
390     url_request_context_host_.SetExpectations(
391         WebSocketStandardRequest(socket_path, socket_host, Origin(),
392                                  /*send_additional_request_headers=*/{},
393                                  /*extra_headers=*/{}),
394         WebSocketStandardResponse(extra_response_headers));
395     CreateAndConnectStream(socket_url, sub_protocols, Origin(),
396                            SiteForCookies(), has_storage_access,
397                            CreateIsolationInfo(), HttpRequestHeaders(),
398                            nullptr);
399   }
400 
401   // Like CreateAndConnectStandard(), but take raw mock data.
CreateAndConnectRawExpectations(std::string_view url,const std::vector<std::string> & sub_protocols,const HttpRequestHeaders & additional_headers,std::unique_ptr<SequencedSocketData> socket_data,bool has_storage_access=false)402   void CreateAndConnectRawExpectations(
403       std::string_view url,
404       const std::vector<std::string>& sub_protocols,
405       const HttpRequestHeaders& additional_headers,
406       std::unique_ptr<SequencedSocketData> socket_data,
407       bool has_storage_access = false) {
408     ASSERT_EQ(BASIC_HANDSHAKE_STREAM, stream_type_);
409 
410     AddRawExpectations(std::move(socket_data));
411     CreateAndConnectStream(GURL(url), sub_protocols, Origin(), SiteForCookies(),
412                            has_storage_access, CreateIsolationInfo(),
413                            additional_headers, std::move(timer_));
414   }
415 
PriorityHeaderEnabled() const416   bool PriorityHeaderEnabled() const { return std::get<bool>(GetParam()); }
417 
418  private:
AddWrite(const spdy::SpdySerializedFrame * frame)419   void AddWrite(const spdy::SpdySerializedFrame* frame) {
420     writes_.emplace_back(ASYNC, frame->data(), frame->size(),
421                          sequence_number_++);
422   }
423 
AddRead(const spdy::SpdySerializedFrame * frame)424   void AddRead(const spdy::SpdySerializedFrame* frame) {
425     reads_.emplace_back(ASYNC, frame->data(), frame->size(),
426                         sequence_number_++);
427   }
428 
429  protected:
430   const HandshakeStreamType stream_type_;
431 
432  private:
433   base::test::ScopedFeatureList feature_list_;
434 
435   std::unique_ptr<base::OneShotTimer> timer_;
436   std::string additional_data_;
437   const char* http2_response_status_ = "200";
438   bool reset_websocket_http2_stream_ = false;
439   SpdyTestUtil spdy_util_;
440   NetLogWithSource log_;
441 
442   int sequence_number_ = 0;
443 
444   // Store mock HTTP/2 data.
445   std::vector<spdy::SpdySerializedFrame> frames_;
446 
447   // Store MockRead and MockWrite objects that have pointers to above data.
448   std::vector<MockRead> reads_;
449   std::vector<MockWrite> writes_;
450 };
451 
452 INSTANTIATE_TEST_SUITE_P(All,
453                          WebSocketStreamCreateTest,
454                          testing::Combine(Values(BASIC_HANDSHAKE_STREAM),
455                                           testing::Bool()));
456 
457 using WebSocketMultiProtocolStreamCreateTest = WebSocketStreamCreateTest;
458 
459 INSTANTIATE_TEST_SUITE_P(All,
460                          WebSocketMultiProtocolStreamCreateTest,
461                          testing::Combine(Values(BASIC_HANDSHAKE_STREAM,
462                                                  HTTP2_HANDSHAKE_STREAM),
463                                           testing::Bool()));
464 
465 // There are enough tests of the Sec-WebSocket-Extensions header that they
466 // deserve their own test fixture.
467 class WebSocketStreamCreateExtensionTest
468     : public WebSocketMultiProtocolStreamCreateTest {
469  protected:
470   // Performs a standard connect, with the value of the Sec-WebSocket-Extensions
471   // header in the response set to |extensions_header_value|. Runs the event
472   // loop to allow the connect to complete.
CreateAndConnectWithExtensions(const std::string & extensions_header_value)473   void CreateAndConnectWithExtensions(
474       const std::string& extensions_header_value) {
475     AddSSLData();
476     CreateAndConnectStandard(
477         "wss://www.example.org/testing_path", NoSubProtocols(), {}, {},
478         {{"Sec-WebSocket-Extensions", extensions_header_value}});
479     WaitUntilConnectDone();
480   }
481 };
482 
483 INSTANTIATE_TEST_SUITE_P(All,
484                          WebSocketStreamCreateExtensionTest,
485                          testing::Combine(Values(BASIC_HANDSHAKE_STREAM,
486                                                  HTTP2_HANDSHAKE_STREAM),
487                                           testing::Bool()));
488 
489 // Common code to construct expectations for authentication tests that receive
490 // the auth challenge on one connection and then create a second connection to
491 // send the authenticated request on.
492 class CommonAuthTestHelper {
493  public:
CommonAuthTestHelper()494   CommonAuthTestHelper() : reads_(), writes_() {}
495 
496   CommonAuthTestHelper(const CommonAuthTestHelper&) = delete;
497   CommonAuthTestHelper& operator=(const CommonAuthTestHelper&) = delete;
498 
BuildAuthSocketData(std::string response1,std::string request2,std::string response2)499   std::unique_ptr<SequencedSocketData> BuildAuthSocketData(
500       std::string response1,
501       std::string request2,
502       std::string response2) {
503     request1_ = WebSocketStandardRequest("/", "www.example.org", Origin(),
504                                          /*send_additional_request_headers=*/{},
505                                          /*extra_headers=*/{});
506     response1_ = std::move(response1);
507     request2_ = std::move(request2);
508     response2_ = std::move(response2);
509     writes_[0] = MockWrite(SYNCHRONOUS, 0, request1_.c_str());
510     reads_[0] = MockRead(SYNCHRONOUS, 1, response1_.c_str());
511     writes_[1] = MockWrite(SYNCHRONOUS, 2, request2_.c_str());
512     reads_[1] = MockRead(SYNCHRONOUS, 3, response2_.c_str());
513     reads_[2] = MockRead(SYNCHRONOUS, OK, 4);  // Close connection
514 
515     return BuildSocketData(reads_, writes_);
516   }
517 
518  private:
519   // These need to be object-scoped since they have to remain valid until all
520   // socket operations in the test are complete.
521   std::string request1_;
522   std::string request2_;
523   std::string response1_;
524   std::string response2_;
525   MockRead reads_[3];
526   MockWrite writes_[2];
527 };
528 
529 // Data and methods for BasicAuth tests.
530 class WebSocketStreamCreateBasicAuthTest : public WebSocketStreamCreateTest {
531  protected:
CreateAndConnectAuthHandshake(std::string_view url,std::string_view base64_user_pass,std::string_view response2)532   void CreateAndConnectAuthHandshake(std::string_view url,
533                                      std::string_view base64_user_pass,
534                                      std::string_view response2) {
535     CreateAndConnectRawExpectations(
536         url, NoSubProtocols(), HttpRequestHeaders(),
537         helper_.BuildAuthSocketData(kUnauthorizedResponse,
538                                     RequestExpectation(base64_user_pass),
539                                     std::string(response2)));
540   }
541 
RequestExpectation(std::string_view base64_user_pass)542   static std::string RequestExpectation(std::string_view base64_user_pass) {
543     // Copy base64_user_pass to a std::string in case it is not nul-terminated.
544     std::string base64_user_pass_string(base64_user_pass);
545     return base::StringPrintf(
546         "GET / HTTP/1.1\r\n"
547         "Host: www.example.org\r\n"
548         "Connection: Upgrade\r\n"
549         "Pragma: no-cache\r\n"
550         "Cache-Control: no-cache\r\n"
551         "Authorization: Basic %s\r\n"
552         "Upgrade: websocket\r\n"
553         "Origin: http://www.example.org\r\n"
554         "Sec-WebSocket-Version: 13\r\n"
555         "User-Agent: \r\n"
556         "Accept-Encoding: gzip, deflate\r\n"
557         "Accept-Language: en-us,fr\r\n"
558         "Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==\r\n"
559         "Sec-WebSocket-Extensions: permessage-deflate; "
560         "client_max_window_bits\r\n"
561         "\r\n",
562         base64_user_pass_string.c_str());
563   }
564 
565   static const char kUnauthorizedResponse[];
566 
567   CommonAuthTestHelper helper_;
568 };
569 
570 INSTANTIATE_TEST_SUITE_P(All,
571                          WebSocketStreamCreateBasicAuthTest,
572                          testing::Combine(Values(BASIC_HANDSHAKE_STREAM),
573                                           testing::Bool()));
574 
575 class WebSocketStreamCreateDigestAuthTest : public WebSocketStreamCreateTest {
576  protected:
577   static const char kUnauthorizedResponse[];
578   static const char kAuthorizedRequest[];
579 
580   CommonAuthTestHelper helper_;
581 };
582 
583 INSTANTIATE_TEST_SUITE_P(All,
584                          WebSocketStreamCreateDigestAuthTest,
585                          testing::Combine(Values(BASIC_HANDSHAKE_STREAM),
586                                           testing::Bool()));
587 
588 const char WebSocketStreamCreateBasicAuthTest::kUnauthorizedResponse[] =
589     "HTTP/1.1 401 Unauthorized\r\n"
590     "Content-Length: 0\r\n"
591     "WWW-Authenticate: Basic realm=\"camelot\"\r\n"
592     "\r\n";
593 
594 // These negotiation values are borrowed from
595 // http_auth_handler_digest_unittest.cc. Feel free to come up with new ones if
596 // you are bored. Only the weakest (no qop) variants of Digest authentication
597 // can be tested by this method, because the others involve random input.
598 const char WebSocketStreamCreateDigestAuthTest::kUnauthorizedResponse[] =
599     "HTTP/1.1 401 Unauthorized\r\n"
600     "Content-Length: 0\r\n"
601     "WWW-Authenticate: Digest realm=\"Oblivion\", nonce=\"nonce-value\"\r\n"
602     "\r\n";
603 
604 const char WebSocketStreamCreateDigestAuthTest::kAuthorizedRequest[] =
605     "GET / HTTP/1.1\r\n"
606     "Host: www.example.org\r\n"
607     "Connection: Upgrade\r\n"
608     "Pragma: no-cache\r\n"
609     "Cache-Control: no-cache\r\n"
610     "Authorization: Digest username=\"FooBar\", realm=\"Oblivion\", "
611     "nonce=\"nonce-value\", uri=\"/\", "
612     "response=\"f72ff54ebde2f928860f806ec04acd1b\"\r\n"
613     "Upgrade: websocket\r\n"
614     "Origin: http://www.example.org\r\n"
615     "Sec-WebSocket-Version: 13\r\n"
616     "User-Agent: \r\n"
617     "Accept-Encoding: gzip, deflate\r\n"
618     "Accept-Language: en-us,fr\r\n"
619     "Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==\r\n"
620     "Sec-WebSocket-Extensions: permessage-deflate; "
621     "client_max_window_bits\r\n"
622     "\r\n";
623 
624 // Confirm that the basic case works as expected.
TEST_P(WebSocketMultiProtocolStreamCreateTest,SimpleSuccess)625 TEST_P(WebSocketMultiProtocolStreamCreateTest, SimpleSuccess) {
626   base::HistogramTester histogram_tester;
627 
628   AddSSLData();
629   EXPECT_FALSE(url_request_);
630   CreateAndConnectStandard("wss://www.example.org/", NoSubProtocols(), {}, {},
631                            {});
632   EXPECT_FALSE(request_info_);
633   EXPECT_FALSE(response_info_);
634   EXPECT_TRUE(url_request_);
635   WaitUntilConnectDone();
636   EXPECT_FALSE(has_failed());
637   EXPECT_TRUE(stream_);
638   EXPECT_TRUE(request_info_);
639   EXPECT_TRUE(response_info_);
640 
641   // Histograms are only updated on stream request destruction.
642   stream_request_.reset();
643   stream_.reset();
644 
645   EXPECT_EQ(ERR_WS_UPGRADE,
646             url_request_context_host_.network_delegate().last_error());
647 
648   auto samples = histogram_tester.GetHistogramSamplesSinceCreation(
649       "Net.WebSocket.HandshakeResult2");
650   EXPECT_EQ(1, samples->TotalCount());
651   if (stream_type_ == BASIC_HANDSHAKE_STREAM) {
652     EXPECT_EQ(1,
653               samples->GetCount(static_cast<int>(
654                   WebSocketHandshakeStreamBase::HandshakeResult::CONNECTED)));
655   } else {
656     DCHECK_EQ(stream_type_, HTTP2_HANDSHAKE_STREAM);
657     EXPECT_EQ(
658         1,
659         samples->GetCount(static_cast<int>(
660             WebSocketHandshakeStreamBase::HandshakeResult::HTTP2_CONNECTED)));
661   }
662 }
663 
TEST_P(WebSocketStreamCreateTest,HandshakeInfo)664 TEST_P(WebSocketStreamCreateTest, HandshakeInfo) {
665   static constexpr char kResponse[] =
666       "HTTP/1.1 101 Switching Protocols\r\n"
667       "Upgrade: websocket\r\n"
668       "Connection: Upgrade\r\n"
669       "Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=\r\n"
670       "foo: bar, baz\r\n"
671       "hoge: fuga\r\n"
672       "hoge: piyo\r\n"
673       "\r\n";
674 
675   CreateAndConnectCustomResponse("ws://www.example.org/", NoSubProtocols(), {},
676                                  {}, kResponse);
677   EXPECT_FALSE(request_info_);
678   EXPECT_FALSE(response_info_);
679   WaitUntilConnectDone();
680   EXPECT_TRUE(stream_);
681   ASSERT_TRUE(request_info_);
682   ASSERT_TRUE(response_info_);
683   std::vector<HeaderKeyValuePair> request_headers =
684       RequestHeadersToVector(request_info_->headers);
685   // We examine the contents of request_info_ and response_info_
686   // mainly only in this test case.
687   EXPECT_EQ(GURL("ws://www.example.org/"), request_info_->url);
688   EXPECT_EQ(GURL("ws://www.example.org/"), response_info_->url);
689   EXPECT_EQ(101, response_info_->headers->response_code());
690   EXPECT_EQ("Switching Protocols", response_info_->headers->GetStatusText());
691   ASSERT_EQ(12u, request_headers.size());
692   EXPECT_EQ(HeaderKeyValuePair("Host", "www.example.org"), request_headers[0]);
693   EXPECT_EQ(HeaderKeyValuePair("Connection", "Upgrade"), request_headers[1]);
694   EXPECT_EQ(HeaderKeyValuePair("Pragma", "no-cache"), request_headers[2]);
695   EXPECT_EQ(HeaderKeyValuePair("Cache-Control", "no-cache"),
696             request_headers[3]);
697   EXPECT_EQ(HeaderKeyValuePair("Upgrade", "websocket"), request_headers[4]);
698   EXPECT_EQ(HeaderKeyValuePair("Origin", "http://www.example.org"),
699             request_headers[5]);
700   EXPECT_EQ(HeaderKeyValuePair("Sec-WebSocket-Version", "13"),
701             request_headers[6]);
702   EXPECT_EQ(HeaderKeyValuePair("User-Agent", ""), request_headers[7]);
703   EXPECT_EQ(HeaderKeyValuePair("Accept-Encoding", "gzip, deflate"),
704             request_headers[8]);
705   EXPECT_EQ(HeaderKeyValuePair("Accept-Language", "en-us,fr"),
706             request_headers[9]);
707   EXPECT_EQ("Sec-WebSocket-Key",  request_headers[10].first);
708   EXPECT_EQ(HeaderKeyValuePair("Sec-WebSocket-Extensions",
709                                "permessage-deflate; client_max_window_bits"),
710             request_headers[11]);
711 
712   std::vector<HeaderKeyValuePair> response_headers =
713       ResponseHeadersToVector(*response_info_->headers.get());
714   ASSERT_EQ(6u, response_headers.size());
715   // Sort the headers for ease of verification.
716   std::sort(response_headers.begin(), response_headers.end());
717 
718   EXPECT_EQ(HeaderKeyValuePair("Connection", "Upgrade"), response_headers[0]);
719   EXPECT_EQ("Sec-WebSocket-Accept", response_headers[1].first);
720   EXPECT_EQ(HeaderKeyValuePair("Upgrade", "websocket"), response_headers[2]);
721   EXPECT_EQ(HeaderKeyValuePair("foo", "bar, baz"), response_headers[3]);
722   EXPECT_EQ(HeaderKeyValuePair("hoge", "fuga"), response_headers[4]);
723   EXPECT_EQ(HeaderKeyValuePair("hoge", "piyo"), response_headers[5]);
724 }
725 
726 // Confirms that request headers are overriden/added after handshake
TEST_P(WebSocketStreamCreateTest,HandshakeOverrideHeaders)727 TEST_P(WebSocketStreamCreateTest, HandshakeOverrideHeaders) {
728   WebSocketExtraHeaders additional_headers(
729       {{"User-Agent", "OveRrIde"}, {"rAnDomHeader", "foobar"}});
730   CreateAndConnectStandard("ws://www.example.org/", NoSubProtocols(),
731                            additional_headers, additional_headers, {});
732   EXPECT_FALSE(request_info_);
733   EXPECT_FALSE(response_info_);
734   WaitUntilConnectDone();
735   EXPECT_FALSE(has_failed());
736   EXPECT_TRUE(stream_);
737   EXPECT_TRUE(request_info_);
738   EXPECT_TRUE(response_info_);
739 
740   std::vector<HeaderKeyValuePair> request_headers =
741       RequestHeadersToVector(request_info_->headers);
742   EXPECT_EQ(HeaderKeyValuePair("User-Agent", "OveRrIde"), request_headers[4]);
743   EXPECT_EQ(HeaderKeyValuePair("rAnDomHeader", "foobar"), request_headers[5]);
744 }
745 
TEST_P(WebSocketStreamCreateTest,OmitsHasStorageAccess)746 TEST_P(WebSocketStreamCreateTest, OmitsHasStorageAccess) {
747   CreateAndConnectStandard("ws://www.example.org/", NoSubProtocols(), {}, {},
748                            {}, /*has_storage_access=*/false);
749   WaitUntilConnectDone();
750 
751   EXPECT_THAT(
752       url_request_context_host_.network_delegate()
753           .cookie_setting_overrides_records(),
754       testing::ElementsAre(CookieSettingOverrides(), CookieSettingOverrides()));
755 }
756 
TEST_P(WebSocketStreamCreateTest,PlumbsHasStorageAccess)757 TEST_P(WebSocketStreamCreateTest, PlumbsHasStorageAccess) {
758   CreateAndConnectStandard("ws://www.example.org/", NoSubProtocols(), {}, {},
759                            {}, /*has_storage_access=*/true);
760   WaitUntilConnectDone();
761 
762   CookieSettingOverrides expected_overrides;
763   expected_overrides.Put(CookieSettingOverride::kStorageAccessGrantEligible);
764 
765   EXPECT_THAT(url_request_context_host_.network_delegate()
766                   .cookie_setting_overrides_records(),
767               testing::ElementsAre(expected_overrides, expected_overrides));
768 }
769 
770 // Confirm that the stream isn't established until the message loop runs.
TEST_P(WebSocketStreamCreateTest,NeedsToRunLoop)771 TEST_P(WebSocketStreamCreateTest, NeedsToRunLoop) {
772   CreateAndConnectStandard("ws://www.example.org/", NoSubProtocols(), {}, {},
773                            {});
774   EXPECT_FALSE(has_failed());
775   EXPECT_FALSE(stream_);
776 }
777 
778 // Check the path is used.
TEST_P(WebSocketMultiProtocolStreamCreateTest,PathIsUsed)779 TEST_P(WebSocketMultiProtocolStreamCreateTest, PathIsUsed) {
780   AddSSLData();
781   CreateAndConnectStandard("wss://www.example.org/testing_path",
782                            NoSubProtocols(), {}, {}, {});
783   WaitUntilConnectDone();
784   EXPECT_FALSE(has_failed());
785   EXPECT_TRUE(stream_);
786 }
787 
788 // Check that sub-protocols are sent and parsed.
TEST_P(WebSocketMultiProtocolStreamCreateTest,SubProtocolIsUsed)789 TEST_P(WebSocketMultiProtocolStreamCreateTest, SubProtocolIsUsed) {
790   AddSSLData();
791   std::vector<std::string> sub_protocols;
792   sub_protocols.push_back("chatv11.chromium.org");
793   sub_protocols.push_back("chatv20.chromium.org");
794   CreateAndConnectStandard(
795       "wss://www.example.org/testing_path", sub_protocols, {},
796       {{"Sec-WebSocket-Protocol",
797         "chatv11.chromium.org, chatv20.chromium.org"}},
798       {{"Sec-WebSocket-Protocol", "chatv20.chromium.org"}});
799   WaitUntilConnectDone();
800   ASSERT_TRUE(stream_);
801   EXPECT_FALSE(has_failed());
802   EXPECT_EQ("chatv20.chromium.org", stream_->GetSubProtocol());
803 }
804 
805 // Unsolicited sub-protocols are rejected.
TEST_P(WebSocketMultiProtocolStreamCreateTest,UnsolicitedSubProtocol)806 TEST_P(WebSocketMultiProtocolStreamCreateTest, UnsolicitedSubProtocol) {
807   base::HistogramTester histogram_tester;
808 
809   AddSSLData();
810   CreateAndConnectStandard(
811       "wss://www.example.org/testing_path", NoSubProtocols(), {}, {},
812       {{"Sec-WebSocket-Protocol", "chatv20.chromium.org"}});
813   WaitUntilConnectDone();
814   EXPECT_FALSE(stream_);
815   EXPECT_TRUE(has_failed());
816   EXPECT_EQ("Error during WebSocket handshake: "
817             "Response must not include 'Sec-WebSocket-Protocol' header "
818             "if not present in request: chatv20.chromium.org",
819             failure_message());
820   EXPECT_EQ(ERR_INVALID_RESPONSE,
821             url_request_context_host_.network_delegate().last_error());
822 
823   stream_request_.reset();
824 
825   auto samples = histogram_tester.GetHistogramSamplesSinceCreation(
826       "Net.WebSocket.HandshakeResult2");
827   EXPECT_EQ(1, samples->TotalCount());
828   if (stream_type_ == BASIC_HANDSHAKE_STREAM) {
829     EXPECT_EQ(
830         1,
831         samples->GetCount(static_cast<int>(
832             WebSocketHandshakeStreamBase::HandshakeResult::FAILED_SUBPROTO)));
833   } else {
834     DCHECK_EQ(stream_type_, HTTP2_HANDSHAKE_STREAM);
835     EXPECT_EQ(1, samples->GetCount(static_cast<int>(
836                      WebSocketHandshakeStreamBase::HandshakeResult::
837                          HTTP2_FAILED_SUBPROTO)));
838   }
839 }
840 
841 // Missing sub-protocol response is rejected.
TEST_P(WebSocketMultiProtocolStreamCreateTest,UnacceptedSubProtocol)842 TEST_P(WebSocketMultiProtocolStreamCreateTest, UnacceptedSubProtocol) {
843   AddSSLData();
844   std::vector<std::string> sub_protocols;
845   sub_protocols.push_back("chat.example.com");
846   CreateAndConnectStandard("wss://www.example.org/testing_path", sub_protocols,
847                            {}, {{"Sec-WebSocket-Protocol", "chat.example.com"}},
848                            {});
849   WaitUntilConnectDone();
850   EXPECT_FALSE(stream_);
851   EXPECT_TRUE(has_failed());
852   EXPECT_EQ("Error during WebSocket handshake: "
853             "Sent non-empty 'Sec-WebSocket-Protocol' header "
854             "but no response was received",
855             failure_message());
856 }
857 
858 // Only one sub-protocol can be accepted.
TEST_P(WebSocketMultiProtocolStreamCreateTest,MultipleSubProtocolsInResponse)859 TEST_P(WebSocketMultiProtocolStreamCreateTest, MultipleSubProtocolsInResponse) {
860   AddSSLData();
861   std::vector<std::string> sub_protocols;
862   sub_protocols.push_back("chatv11.chromium.org");
863   sub_protocols.push_back("chatv20.chromium.org");
864   CreateAndConnectStandard("wss://www.example.org/testing_path", sub_protocols,
865                            {},
866                            {{"Sec-WebSocket-Protocol",
867                              "chatv11.chromium.org, chatv20.chromium.org"}},
868                            {{"Sec-WebSocket-Protocol",
869                              "chatv11.chromium.org, chatv20.chromium.org"}});
870   WaitUntilConnectDone();
871   EXPECT_FALSE(stream_);
872   EXPECT_TRUE(has_failed());
873   EXPECT_EQ(
874       "Error during WebSocket handshake: "
875       "'Sec-WebSocket-Protocol' header must not appear "
876       "more than once in a response",
877       failure_message());
878 }
879 
880 // Unmatched sub-protocol should be rejected.
TEST_P(WebSocketMultiProtocolStreamCreateTest,UnmatchedSubProtocolInResponse)881 TEST_P(WebSocketMultiProtocolStreamCreateTest, UnmatchedSubProtocolInResponse) {
882   AddSSLData();
883   std::vector<std::string> sub_protocols;
884   sub_protocols.push_back("chatv11.chromium.org");
885   sub_protocols.push_back("chatv20.chromium.org");
886   CreateAndConnectStandard(
887       "wss://www.example.org/testing_path", sub_protocols, {},
888       {{"Sec-WebSocket-Protocol",
889         "chatv11.chromium.org, chatv20.chromium.org"}},
890       {{"Sec-WebSocket-Protocol", "chatv21.chromium.org"}});
891   WaitUntilConnectDone();
892   EXPECT_FALSE(stream_);
893   EXPECT_TRUE(has_failed());
894   EXPECT_EQ("Error during WebSocket handshake: "
895             "'Sec-WebSocket-Protocol' header value 'chatv21.chromium.org' "
896             "in response does not match any of sent values",
897             failure_message());
898 }
899 
900 // permessage-deflate extension basic success case.
TEST_P(WebSocketStreamCreateExtensionTest,PerMessageDeflateSuccess)901 TEST_P(WebSocketStreamCreateExtensionTest, PerMessageDeflateSuccess) {
902   CreateAndConnectWithExtensions("permessage-deflate");
903   EXPECT_TRUE(stream_);
904   EXPECT_FALSE(has_failed());
905 }
906 
907 // permessage-deflate extensions success with all parameters.
TEST_P(WebSocketStreamCreateExtensionTest,PerMessageDeflateParamsSuccess)908 TEST_P(WebSocketStreamCreateExtensionTest, PerMessageDeflateParamsSuccess) {
909   CreateAndConnectWithExtensions(
910       "permessage-deflate; client_no_context_takeover; "
911       "server_max_window_bits=11; client_max_window_bits=13; "
912       "server_no_context_takeover");
913   EXPECT_TRUE(stream_);
914   EXPECT_FALSE(has_failed());
915 }
916 
917 // Verify that incoming messages are actually decompressed with
918 // permessage-deflate enabled.
TEST_P(WebSocketStreamCreateExtensionTest,PerMessageDeflateInflates)919 TEST_P(WebSocketStreamCreateExtensionTest, PerMessageDeflateInflates) {
920   AddSSLData();
921   SetAdditionalResponseData(std::string(
922       "\xc1\x07"  // WebSocket header (FIN + RSV1, Text payload 7 bytes)
923       "\xf2\x48\xcd\xc9\xc9\x07\x00",  // "Hello" DEFLATE compressed
924       9));
925   CreateAndConnectStandard(
926       "wss://www.example.org/testing_path", NoSubProtocols(), {}, {},
927       {{"Sec-WebSocket-Extensions", "permessage-deflate"}});
928   WaitUntilConnectDone();
929 
930   ASSERT_TRUE(stream_);
931   std::vector<std::unique_ptr<WebSocketFrame>> frames;
932   TestCompletionCallback callback;
933   int rv = stream_->ReadFrames(&frames, callback.callback());
934   rv = callback.GetResult(rv);
935   ASSERT_THAT(rv, IsOk());
936   ASSERT_EQ(1U, frames.size());
937   ASSERT_EQ(5U, frames[0]->header.payload_length);
938   EXPECT_EQ(std::string("Hello"),
939             std::string(frames[0]->payload, frames[0]->header.payload_length));
940 }
941 
942 // Unknown extension in the response is rejected
TEST_P(WebSocketStreamCreateExtensionTest,UnknownExtension)943 TEST_P(WebSocketStreamCreateExtensionTest, UnknownExtension) {
944   CreateAndConnectWithExtensions("x-unknown-extension");
945   EXPECT_FALSE(stream_);
946   EXPECT_TRUE(has_failed());
947   EXPECT_EQ("Error during WebSocket handshake: "
948             "Found an unsupported extension 'x-unknown-extension' "
949             "in 'Sec-WebSocket-Extensions' header",
950             failure_message());
951 }
952 
953 // Malformed extensions are rejected (this file does not cover all possible
954 // parse failures, as the parser is covered thoroughly by its own unit tests).
TEST_P(WebSocketStreamCreateExtensionTest,MalformedExtension)955 TEST_P(WebSocketStreamCreateExtensionTest, MalformedExtension) {
956   CreateAndConnectWithExtensions(";");
957   EXPECT_FALSE(stream_);
958   EXPECT_TRUE(has_failed());
959   EXPECT_EQ(
960       "Error during WebSocket handshake: 'Sec-WebSocket-Extensions' header "
961       "value is rejected by the parser: ;",
962       failure_message());
963 }
964 
965 // The permessage-deflate extension may only be specified once.
TEST_P(WebSocketStreamCreateExtensionTest,OnlyOnePerMessageDeflateAllowed)966 TEST_P(WebSocketStreamCreateExtensionTest, OnlyOnePerMessageDeflateAllowed) {
967   base::HistogramTester histogram_tester;
968 
969   CreateAndConnectWithExtensions(
970       "permessage-deflate, permessage-deflate; client_max_window_bits=10");
971   EXPECT_FALSE(stream_);
972   EXPECT_TRUE(has_failed());
973   EXPECT_EQ(
974       "Error during WebSocket handshake: "
975       "Received duplicate permessage-deflate response",
976       failure_message());
977 
978   stream_request_.reset();
979 
980   auto samples = histogram_tester.GetHistogramSamplesSinceCreation(
981       "Net.WebSocket.HandshakeResult2");
982   EXPECT_EQ(1, samples->TotalCount());
983   if (stream_type_ == BASIC_HANDSHAKE_STREAM) {
984     EXPECT_EQ(
985         1,
986         samples->GetCount(static_cast<int>(
987             WebSocketHandshakeStreamBase::HandshakeResult::FAILED_EXTENSIONS)));
988   } else {
989     DCHECK_EQ(stream_type_, HTTP2_HANDSHAKE_STREAM);
990     EXPECT_EQ(1, samples->GetCount(static_cast<int>(
991                      WebSocketHandshakeStreamBase::HandshakeResult::
992                          HTTP2_FAILED_EXTENSIONS)));
993   }
994 }
995 
996 // client_max_window_bits must have an argument
TEST_P(WebSocketStreamCreateExtensionTest,NoMaxWindowBitsArgument)997 TEST_P(WebSocketStreamCreateExtensionTest, NoMaxWindowBitsArgument) {
998   CreateAndConnectWithExtensions("permessage-deflate; client_max_window_bits");
999   EXPECT_FALSE(stream_);
1000   EXPECT_TRUE(has_failed());
1001   EXPECT_EQ(
1002       "Error during WebSocket handshake: Error in permessage-deflate: "
1003       "client_max_window_bits must have value",
1004       failure_message());
1005 }
1006 
1007 // Other cases for permessage-deflate parameters are tested in
1008 // websocket_deflate_parameters_test.cc.
1009 
1010 // TODO(ricea): Check that WebSocketDeflateStream is initialised with the
1011 // arguments from the server. This is difficult because the data written to the
1012 // socket is randomly masked.
1013 
1014 // Additional Sec-WebSocket-Accept headers should be rejected.
TEST_P(WebSocketStreamCreateTest,DoubleAccept)1015 TEST_P(WebSocketStreamCreateTest, DoubleAccept) {
1016   CreateAndConnectStandard(
1017       "ws://www.example.org/", NoSubProtocols(), {}, {},
1018       {{"Sec-WebSocket-Accept", "s3pPLMBiTxaQ9kYGzzhZRbK+xOo="}});
1019   WaitUntilConnectDone();
1020   EXPECT_FALSE(stream_);
1021   EXPECT_TRUE(has_failed());
1022   EXPECT_EQ("Error during WebSocket handshake: "
1023             "'Sec-WebSocket-Accept' header must not appear "
1024             "more than once in a response",
1025             failure_message());
1026 }
1027 
1028 // When upgrading an HTTP/1 connection, response code 200 is invalid and must be
1029 // rejected.  Response code 101 means success.  On the other hand, when
1030 // requesting a WebSocket stream over HTTP/2, response code 101 is invalid and
1031 // must be rejected.  Response code 200 means success.
TEST_P(WebSocketMultiProtocolStreamCreateTest,InvalidStatusCode)1032 TEST_P(WebSocketMultiProtocolStreamCreateTest, InvalidStatusCode) {
1033   base::HistogramTester histogram_tester;
1034 
1035   AddSSLData();
1036   if (stream_type_ == BASIC_HANDSHAKE_STREAM) {
1037     static constexpr char kInvalidStatusCodeResponse[] =
1038         "HTTP/1.1 200 OK\r\n"
1039         "Upgrade: websocket\r\n"
1040         "Connection: Upgrade\r\n"
1041         "Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=\r\n"
1042         "\r\n";
1043     CreateAndConnectCustomResponse("wss://www.example.org/", NoSubProtocols(),
1044                                    {}, {}, kInvalidStatusCodeResponse);
1045   } else {
1046     DCHECK_EQ(stream_type_, HTTP2_HANDSHAKE_STREAM);
1047     SetHttp2ResponseStatus("101");
1048     CreateAndConnectStandard("wss://www.example.org/", NoSubProtocols(), {}, {},
1049                              {});
1050   }
1051 
1052   WaitUntilConnectDone();
1053   stream_request_.reset();
1054   EXPECT_TRUE(has_failed());
1055   auto samples = histogram_tester.GetHistogramSamplesSinceCreation(
1056       "Net.WebSocket.HandshakeResult2");
1057   EXPECT_EQ(1, samples->TotalCount());
1058 
1059   if (stream_type_ == BASIC_HANDSHAKE_STREAM) {
1060     EXPECT_EQ("Error during WebSocket handshake: Unexpected response code: 200",
1061               failure_message());
1062     EXPECT_EQ(failure_response_code(), 200);
1063     EXPECT_EQ(
1064         1, samples->GetCount(static_cast<int>(
1065                WebSocketHandshakeStreamBase::HandshakeResult::INVALID_STATUS)));
1066   } else {
1067     DCHECK_EQ(stream_type_, HTTP2_HANDSHAKE_STREAM);
1068     EXPECT_EQ("Error during WebSocket handshake: Unexpected response code: 101",
1069               failure_message());
1070     EXPECT_EQ(failure_response_code(), 101);
1071     EXPECT_EQ(1, samples->GetCount(static_cast<int>(
1072                      WebSocketHandshakeStreamBase::HandshakeResult::
1073                          HTTP2_INVALID_STATUS)));
1074   }
1075 }
1076 
1077 // Redirects are not followed (according to the WHATWG WebSocket API, which
1078 // overrides RFC6455 for browser applications).
TEST_P(WebSocketMultiProtocolStreamCreateTest,RedirectsRejected)1079 TEST_P(WebSocketMultiProtocolStreamCreateTest, RedirectsRejected) {
1080   AddSSLData();
1081   if (stream_type_ == BASIC_HANDSHAKE_STREAM) {
1082     static constexpr char kRedirectResponse[] =
1083         "HTTP/1.1 302 Moved Temporarily\r\n"
1084         "Content-Type: text/html\r\n"
1085         "Content-Length: 34\r\n"
1086         "Connection: keep-alive\r\n"
1087         "Location: wss://www.example.org/other\r\n"
1088         "\r\n"
1089         "<title>Moved</title><h1>Moved</h1>";
1090     CreateAndConnectCustomResponse("wss://www.example.org/", NoSubProtocols(),
1091                                    {}, {}, kRedirectResponse);
1092   } else {
1093     DCHECK_EQ(stream_type_, HTTP2_HANDSHAKE_STREAM);
1094     SetHttp2ResponseStatus("302");
1095     CreateAndConnectStandard("wss://www.example.org/", NoSubProtocols(), {}, {},
1096                              {});
1097   }
1098   WaitUntilConnectDone();
1099 
1100   EXPECT_TRUE(has_failed());
1101   EXPECT_EQ("Error during WebSocket handshake: Unexpected response code: 302",
1102             failure_message());
1103 }
1104 
1105 // Malformed responses should be rejected. HttpStreamParser will accept just
1106 // about any garbage in the middle of the headers. To make it give up, the junk
1107 // has to be at the start of the response. Even then, it just gets treated as an
1108 // HTTP/0.9 response.
TEST_P(WebSocketStreamCreateTest,MalformedResponse)1109 TEST_P(WebSocketStreamCreateTest, MalformedResponse) {
1110   static constexpr char kMalformedResponse[] =
1111       "220 mx.google.com ESMTP\r\n"
1112       "HTTP/1.1 101 OK\r\n"
1113       "Upgrade: websocket\r\n"
1114       "Connection: Upgrade\r\n"
1115       "Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=\r\n"
1116       "\r\n";
1117   CreateAndConnectCustomResponse("ws://www.example.org/", NoSubProtocols(), {},
1118                                  {}, kMalformedResponse);
1119   WaitUntilConnectDone();
1120   EXPECT_TRUE(has_failed());
1121   EXPECT_EQ("Error during WebSocket handshake: Invalid status line",
1122             failure_message());
1123 }
1124 
1125 // Upgrade header must be present.
TEST_P(WebSocketStreamCreateTest,MissingUpgradeHeader)1126 TEST_P(WebSocketStreamCreateTest, MissingUpgradeHeader) {
1127   base::HistogramTester histogram_tester;
1128 
1129   static constexpr char kMissingUpgradeResponse[] =
1130       "HTTP/1.1 101 Switching Protocols\r\n"
1131       "Connection: Upgrade\r\n"
1132       "Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=\r\n"
1133       "\r\n";
1134   CreateAndConnectCustomResponse("ws://www.example.org/", NoSubProtocols(), {},
1135                                  {}, kMissingUpgradeResponse);
1136   WaitUntilConnectDone();
1137   EXPECT_TRUE(has_failed());
1138   EXPECT_EQ("Error during WebSocket handshake: 'Upgrade' header is missing",
1139             failure_message());
1140 
1141   stream_request_.reset();
1142 
1143   auto samples = histogram_tester.GetHistogramSamplesSinceCreation(
1144       "Net.WebSocket.HandshakeResult2");
1145   EXPECT_EQ(1, samples->TotalCount());
1146   EXPECT_EQ(
1147       1, samples->GetCount(static_cast<int>(
1148              WebSocketHandshakeStreamBase::HandshakeResult::FAILED_UPGRADE)));
1149 }
1150 
1151 // There must only be one upgrade header.
TEST_P(WebSocketStreamCreateTest,DoubleUpgradeHeader)1152 TEST_P(WebSocketStreamCreateTest, DoubleUpgradeHeader) {
1153   CreateAndConnectStandard("ws://www.example.org/", NoSubProtocols(), {}, {},
1154                            {{"Upgrade", "HTTP/2.0"}});
1155   WaitUntilConnectDone();
1156   EXPECT_TRUE(has_failed());
1157   EXPECT_EQ("Error during WebSocket handshake: "
1158             "'Upgrade' header must not appear more than once in a response",
1159             failure_message());
1160 }
1161 
1162 // There must only be one correct upgrade header.
TEST_P(WebSocketStreamCreateTest,IncorrectUpgradeHeader)1163 TEST_P(WebSocketStreamCreateTest, IncorrectUpgradeHeader) {
1164   static constexpr char kMissingUpgradeResponse[] =
1165       "HTTP/1.1 101 Switching Protocols\r\n"
1166       "Connection: Upgrade\r\n"
1167       "Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=\r\n"
1168       "Upgrade: hogefuga\r\n"
1169       "\r\n";
1170   CreateAndConnectCustomResponse("ws://www.example.org/", NoSubProtocols(), {},
1171                                  {}, kMissingUpgradeResponse);
1172   WaitUntilConnectDone();
1173   EXPECT_TRUE(has_failed());
1174   EXPECT_EQ("Error during WebSocket handshake: "
1175             "'Upgrade' header value is not 'WebSocket': hogefuga",
1176             failure_message());
1177 }
1178 
1179 // Connection header must be present.
TEST_P(WebSocketStreamCreateTest,MissingConnectionHeader)1180 TEST_P(WebSocketStreamCreateTest, MissingConnectionHeader) {
1181   base::HistogramTester histogram_tester;
1182 
1183   static constexpr char kMissingConnectionResponse[] =
1184       "HTTP/1.1 101 Switching Protocols\r\n"
1185       "Upgrade: websocket\r\n"
1186       "Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=\r\n"
1187       "\r\n";
1188   CreateAndConnectCustomResponse("ws://www.example.org/", NoSubProtocols(), {},
1189                                  {}, kMissingConnectionResponse);
1190   WaitUntilConnectDone();
1191   EXPECT_TRUE(has_failed());
1192   EXPECT_EQ("Error during WebSocket handshake: "
1193             "'Connection' header is missing",
1194             failure_message());
1195 
1196   stream_request_.reset();
1197 
1198   auto samples = histogram_tester.GetHistogramSamplesSinceCreation(
1199       "Net.WebSocket.HandshakeResult2");
1200   EXPECT_EQ(1, samples->TotalCount());
1201   EXPECT_EQ(
1202       1,
1203       samples->GetCount(static_cast<int>(
1204           WebSocketHandshakeStreamBase::HandshakeResult::FAILED_CONNECTION)));
1205 }
1206 
1207 // Connection header must contain "Upgrade".
TEST_P(WebSocketStreamCreateTest,IncorrectConnectionHeader)1208 TEST_P(WebSocketStreamCreateTest, IncorrectConnectionHeader) {
1209   static constexpr char kMissingConnectionResponse[] =
1210       "HTTP/1.1 101 Switching Protocols\r\n"
1211       "Upgrade: websocket\r\n"
1212       "Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=\r\n"
1213       "Connection: hogefuga\r\n"
1214       "\r\n";
1215   CreateAndConnectCustomResponse("ws://www.example.org/", NoSubProtocols(), {},
1216                                  {}, kMissingConnectionResponse);
1217   WaitUntilConnectDone();
1218   EXPECT_TRUE(has_failed());
1219   EXPECT_EQ("Error during WebSocket handshake: "
1220             "'Connection' header value must contain 'Upgrade'",
1221             failure_message());
1222 }
1223 
1224 // Connection header is permitted to contain other tokens.
TEST_P(WebSocketStreamCreateTest,AdditionalTokenInConnectionHeader)1225 TEST_P(WebSocketStreamCreateTest, AdditionalTokenInConnectionHeader) {
1226   static constexpr char kAdditionalConnectionTokenResponse[] =
1227       "HTTP/1.1 101 Switching Protocols\r\n"
1228       "Upgrade: websocket\r\n"
1229       "Connection: Upgrade, Keep-Alive\r\n"
1230       "Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=\r\n"
1231       "\r\n";
1232   CreateAndConnectCustomResponse("ws://www.example.org/", NoSubProtocols(), {},
1233                                  {}, kAdditionalConnectionTokenResponse);
1234   WaitUntilConnectDone();
1235   EXPECT_FALSE(has_failed());
1236   EXPECT_TRUE(stream_);
1237 }
1238 
1239 // Sec-WebSocket-Accept header must be present.
TEST_P(WebSocketStreamCreateTest,MissingSecWebSocketAccept)1240 TEST_P(WebSocketStreamCreateTest, MissingSecWebSocketAccept) {
1241   base::HistogramTester histogram_tester;
1242 
1243   static constexpr char kMissingAcceptResponse[] =
1244       "HTTP/1.1 101 Switching Protocols\r\n"
1245       "Upgrade: websocket\r\n"
1246       "Connection: Upgrade\r\n"
1247       "\r\n";
1248   CreateAndConnectCustomResponse("ws://www.example.org/", NoSubProtocols(), {},
1249                                  {}, kMissingAcceptResponse);
1250   WaitUntilConnectDone();
1251   EXPECT_TRUE(has_failed());
1252   EXPECT_EQ("Error during WebSocket handshake: "
1253             "'Sec-WebSocket-Accept' header is missing",
1254             failure_message());
1255 
1256   stream_request_.reset();
1257 
1258   auto samples = histogram_tester.GetHistogramSamplesSinceCreation(
1259       "Net.WebSocket.HandshakeResult2");
1260   EXPECT_EQ(1, samples->TotalCount());
1261   EXPECT_EQ(1,
1262             samples->GetCount(static_cast<int>(
1263                 WebSocketHandshakeStreamBase::HandshakeResult::FAILED_ACCEPT)));
1264 }
1265 
1266 // Sec-WebSocket-Accept header must match the key that was sent.
TEST_P(WebSocketStreamCreateTest,WrongSecWebSocketAccept)1267 TEST_P(WebSocketStreamCreateTest, WrongSecWebSocketAccept) {
1268   static constexpr char kIncorrectAcceptResponse[] =
1269       "HTTP/1.1 101 Switching Protocols\r\n"
1270       "Upgrade: websocket\r\n"
1271       "Connection: Upgrade\r\n"
1272       "Sec-WebSocket-Accept: x/byyPZ2tOFvJCGkkugcKvqhhPk=\r\n"
1273       "\r\n";
1274   CreateAndConnectCustomResponse("ws://www.example.org/", NoSubProtocols(), {},
1275                                  {}, kIncorrectAcceptResponse);
1276   WaitUntilConnectDone();
1277   EXPECT_TRUE(has_failed());
1278   EXPECT_EQ("Error during WebSocket handshake: "
1279             "Incorrect 'Sec-WebSocket-Accept' header value",
1280             failure_message());
1281 }
1282 
1283 // Cancellation works.
TEST_P(WebSocketStreamCreateTest,Cancellation)1284 TEST_P(WebSocketStreamCreateTest, Cancellation) {
1285   CreateAndConnectStandard("ws://www.example.org/", NoSubProtocols(), {}, {},
1286                            {});
1287   stream_request_.reset();
1288   // WaitUntilConnectDone doesn't work in this case.
1289   base::RunLoop().RunUntilIdle();
1290   EXPECT_FALSE(has_failed());
1291   EXPECT_FALSE(stream_);
1292   EXPECT_FALSE(request_info_);
1293   EXPECT_FALSE(response_info_);
1294 }
1295 
1296 // Connect failure must look just like negotiation failure.
TEST_P(WebSocketStreamCreateTest,ConnectionFailure)1297 TEST_P(WebSocketStreamCreateTest, ConnectionFailure) {
1298   std::unique_ptr<SequencedSocketData> socket_data(BuildNullSocketData());
1299   socket_data->set_connect_data(
1300       MockConnect(SYNCHRONOUS, ERR_CONNECTION_REFUSED));
1301   CreateAndConnectRawExpectations("ws://www.example.org/", NoSubProtocols(),
1302                                   HttpRequestHeaders(), std::move(socket_data));
1303   WaitUntilConnectDone();
1304   EXPECT_TRUE(has_failed());
1305   EXPECT_EQ("Error in connection establishment: net::ERR_CONNECTION_REFUSED",
1306             failure_message());
1307   EXPECT_FALSE(request_info_);
1308   EXPECT_FALSE(response_info_);
1309 }
1310 
1311 // Connect timeout must look just like any other failure.
TEST_P(WebSocketStreamCreateTest,ConnectionTimeout)1312 TEST_P(WebSocketStreamCreateTest, ConnectionTimeout) {
1313   std::unique_ptr<SequencedSocketData> socket_data(BuildNullSocketData());
1314   socket_data->set_connect_data(
1315       MockConnect(ASYNC, ERR_CONNECTION_TIMED_OUT));
1316   CreateAndConnectRawExpectations("ws://www.example.org/", NoSubProtocols(),
1317                                   HttpRequestHeaders(), std::move(socket_data));
1318   WaitUntilConnectDone();
1319   EXPECT_TRUE(has_failed());
1320   EXPECT_EQ("Error in connection establishment: net::ERR_CONNECTION_TIMED_OUT",
1321             failure_message());
1322 }
1323 
1324 // The server doesn't respond to the opening handshake.
TEST_P(WebSocketStreamCreateTest,HandshakeTimeout)1325 TEST_P(WebSocketStreamCreateTest, HandshakeTimeout) {
1326   std::unique_ptr<SequencedSocketData> socket_data(BuildNullSocketData());
1327   socket_data->set_connect_data(MockConnect(SYNCHRONOUS, ERR_IO_PENDING));
1328   auto timer = std::make_unique<MockWeakTimer>();
1329   base::WeakPtr<MockWeakTimer> weak_timer = timer->AsWeakPtr();
1330   SetTimer(std::move(timer));
1331   CreateAndConnectRawExpectations("ws://www.example.org/", NoSubProtocols(),
1332                                   HttpRequestHeaders(), std::move(socket_data));
1333   EXPECT_FALSE(has_failed());
1334   ASSERT_TRUE(weak_timer.get());
1335   EXPECT_TRUE(weak_timer->IsRunning());
1336 
1337   weak_timer->Fire();
1338   WaitUntilConnectDone();
1339 
1340   EXPECT_TRUE(has_failed());
1341   EXPECT_EQ("WebSocket opening handshake timed out", failure_message());
1342   ASSERT_TRUE(weak_timer.get());
1343   EXPECT_FALSE(weak_timer->IsRunning());
1344 }
1345 
1346 // When the connection establishes the timer should be stopped.
TEST_P(WebSocketStreamCreateTest,HandshakeTimerOnSuccess)1347 TEST_P(WebSocketStreamCreateTest, HandshakeTimerOnSuccess) {
1348   auto timer = std::make_unique<MockWeakTimer>();
1349   base::WeakPtr<MockWeakTimer> weak_timer = timer->AsWeakPtr();
1350 
1351   SetTimer(std::move(timer));
1352   CreateAndConnectStandard("ws://www.example.org/", NoSubProtocols(), {}, {},
1353                            {});
1354   ASSERT_TRUE(weak_timer);
1355   EXPECT_TRUE(weak_timer->IsRunning());
1356 
1357   WaitUntilConnectDone();
1358   EXPECT_FALSE(has_failed());
1359   EXPECT_TRUE(stream_);
1360   ASSERT_TRUE(weak_timer);
1361   EXPECT_FALSE(weak_timer->IsRunning());
1362 }
1363 
1364 // When the connection fails the timer should be stopped.
TEST_P(WebSocketStreamCreateTest,HandshakeTimerOnFailure)1365 TEST_P(WebSocketStreamCreateTest, HandshakeTimerOnFailure) {
1366   std::unique_ptr<SequencedSocketData> socket_data(BuildNullSocketData());
1367   socket_data->set_connect_data(
1368       MockConnect(SYNCHRONOUS, ERR_CONNECTION_REFUSED));
1369   auto timer = std::make_unique<MockWeakTimer>();
1370   base::WeakPtr<MockWeakTimer> weak_timer = timer->AsWeakPtr();
1371   SetTimer(std::move(timer));
1372   CreateAndConnectRawExpectations("ws://www.example.org/", NoSubProtocols(),
1373                                   HttpRequestHeaders(), std::move(socket_data));
1374   ASSERT_TRUE(weak_timer.get());
1375   EXPECT_TRUE(weak_timer->IsRunning());
1376 
1377   WaitUntilConnectDone();
1378   EXPECT_TRUE(has_failed());
1379   EXPECT_EQ("Error in connection establishment: net::ERR_CONNECTION_REFUSED",
1380             failure_message());
1381   ASSERT_TRUE(weak_timer.get());
1382   EXPECT_FALSE(weak_timer->IsRunning());
1383 }
1384 
1385 // Cancellation during connect works.
TEST_P(WebSocketStreamCreateTest,CancellationDuringConnect)1386 TEST_P(WebSocketStreamCreateTest, CancellationDuringConnect) {
1387   std::unique_ptr<SequencedSocketData> socket_data(BuildNullSocketData());
1388   socket_data->set_connect_data(MockConnect(SYNCHRONOUS, ERR_IO_PENDING));
1389   CreateAndConnectRawExpectations("ws://www.example.org/", NoSubProtocols(),
1390                                   HttpRequestHeaders(), std::move(socket_data));
1391   stream_request_.reset();
1392   // WaitUntilConnectDone doesn't work in this case.
1393   base::RunLoop().RunUntilIdle();
1394   EXPECT_FALSE(has_failed());
1395   EXPECT_FALSE(stream_);
1396 }
1397 
1398 // Cancellation during write of the request headers works.
TEST_P(WebSocketStreamCreateTest,CancellationDuringWrite)1399 TEST_P(WebSocketStreamCreateTest, CancellationDuringWrite) {
1400   // First write never completes.
1401   MockWrite writes[] = {MockWrite(SYNCHRONOUS, ERR_IO_PENDING, 0)};
1402   auto socket_data =
1403       std::make_unique<SequencedSocketData>(base::span<MockRead>(), writes);
1404   auto* socket_data_ptr = socket_data.get();
1405   socket_data->set_connect_data(MockConnect(SYNCHRONOUS, OK));
1406   CreateAndConnectRawExpectations("ws://www.example.org/", NoSubProtocols(),
1407                                   HttpRequestHeaders(), std::move(socket_data));
1408   base::RunLoop().RunUntilIdle();
1409   EXPECT_TRUE(socket_data_ptr->AllWriteDataConsumed());
1410   stream_request_.reset();
1411   // WaitUntilConnectDone doesn't work in this case.
1412   base::RunLoop().RunUntilIdle();
1413   EXPECT_FALSE(has_failed());
1414   EXPECT_FALSE(stream_);
1415   EXPECT_TRUE(request_info_);
1416   EXPECT_FALSE(response_info_);
1417 }
1418 
1419 // Cancellation during read of the response headers works.
TEST_P(WebSocketStreamCreateTest,CancellationDuringRead)1420 TEST_P(WebSocketStreamCreateTest, CancellationDuringRead) {
1421   std::string request = WebSocketStandardRequest(
1422       "/", "www.example.org", Origin(), /*send_additional_request_headers=*/{},
1423       /*extra_headers=*/{});
1424   MockWrite writes[] = {MockWrite(ASYNC, 0, request.c_str())};
1425   MockRead reads[] = {
1426       MockRead(SYNCHRONOUS, ERR_IO_PENDING, 1),
1427   };
1428   std::unique_ptr<SequencedSocketData> socket_data(
1429       BuildSocketData(reads, writes));
1430   SequencedSocketData* socket_data_raw_ptr = socket_data.get();
1431   CreateAndConnectRawExpectations("ws://www.example.org/", NoSubProtocols(),
1432                                   HttpRequestHeaders(), std::move(socket_data));
1433   base::RunLoop().RunUntilIdle();
1434   EXPECT_TRUE(socket_data_raw_ptr->AllReadDataConsumed());
1435   stream_request_.reset();
1436   // WaitUntilConnectDone doesn't work in this case.
1437   base::RunLoop().RunUntilIdle();
1438   EXPECT_FALSE(has_failed());
1439   EXPECT_FALSE(stream_);
1440   EXPECT_TRUE(request_info_);
1441   EXPECT_FALSE(response_info_);
1442 }
1443 
1444 // Over-size response headers (> 256KB) should not cause a crash.  This is a
1445 // regression test for crbug.com/339456. It is based on the layout test
1446 // "cookie-flood.html".
TEST_P(WebSocketStreamCreateTest,VeryLargeResponseHeaders)1447 TEST_P(WebSocketStreamCreateTest, VeryLargeResponseHeaders) {
1448   base::HistogramTester histogram_tester;
1449 
1450   std::string set_cookie_headers;
1451   set_cookie_headers.reserve(24 * 20000);
1452   for (int i = 0; i < 20000; ++i) {
1453     set_cookie_headers += base::StringPrintf("Set-Cookie: ws-%d=1\r\n", i);
1454   }
1455   ASSERT_GT(set_cookie_headers.size(), 256U * 1024U);
1456   CreateAndConnectStringResponse("ws://www.example.org/", NoSubProtocols(),
1457                                  set_cookie_headers);
1458   WaitUntilConnectDone();
1459   EXPECT_TRUE(has_failed());
1460   EXPECT_FALSE(response_info_);
1461 
1462   stream_request_.reset();
1463 
1464   auto samples = histogram_tester.GetHistogramSamplesSinceCreation(
1465       "Net.WebSocket.HandshakeResult2");
1466   EXPECT_EQ(1, samples->TotalCount());
1467   EXPECT_EQ(1, samples->GetCount(static_cast<int>(
1468                    WebSocketHandshakeStreamBase::HandshakeResult::FAILED)));
1469 }
1470 
1471 // If the remote host closes the connection without sending headers, we should
1472 // log the console message "Connection closed before receiving a handshake
1473 // response".
TEST_P(WebSocketStreamCreateTest,NoResponse)1474 TEST_P(WebSocketStreamCreateTest, NoResponse) {
1475   base::HistogramTester histogram_tester;
1476 
1477   std::string request = WebSocketStandardRequest(
1478       "/", "www.example.org", Origin(), /*send_additional_request_headers=*/{},
1479       /*extra_headers=*/{});
1480   MockWrite writes[] = {MockWrite(ASYNC, request.data(), request.size(), 0)};
1481   MockRead reads[] = {MockRead(ASYNC, 0, 1)};
1482   std::unique_ptr<SequencedSocketData> socket_data(
1483       BuildSocketData(reads, writes));
1484   SequencedSocketData* socket_data_raw_ptr = socket_data.get();
1485   CreateAndConnectRawExpectations("ws://www.example.org/", NoSubProtocols(),
1486                                   HttpRequestHeaders(), std::move(socket_data));
1487   base::RunLoop().RunUntilIdle();
1488   EXPECT_TRUE(socket_data_raw_ptr->AllReadDataConsumed());
1489   EXPECT_TRUE(has_failed());
1490   EXPECT_FALSE(stream_);
1491   EXPECT_FALSE(response_info_);
1492   EXPECT_EQ("Connection closed before receiving a handshake response",
1493             failure_message());
1494 
1495   stream_request_.reset();
1496 
1497   auto samples = histogram_tester.GetHistogramSamplesSinceCreation(
1498       "Net.WebSocket.HandshakeResult2");
1499   EXPECT_EQ(1, samples->TotalCount());
1500   EXPECT_EQ(
1501       1, samples->GetCount(static_cast<int>(
1502              WebSocketHandshakeStreamBase::HandshakeResult::EMPTY_RESPONSE)));
1503 }
1504 
TEST_P(WebSocketStreamCreateTest,SelfSignedCertificateFailure)1505 TEST_P(WebSocketStreamCreateTest, SelfSignedCertificateFailure) {
1506   auto ssl_socket_data = std::make_unique<SSLSocketDataProvider>(
1507       ASYNC, ERR_CERT_AUTHORITY_INVALID);
1508   ssl_socket_data->ssl_info.cert =
1509       ImportCertFromFile(GetTestCertsDirectory(), "unittest.selfsigned.der");
1510   ASSERT_TRUE(ssl_socket_data->ssl_info.cert.get());
1511   url_request_context_host_.AddSSLSocketDataProvider(
1512       std::move(ssl_socket_data));
1513   std::unique_ptr<SequencedSocketData> raw_socket_data(BuildNullSocketData());
1514   CreateAndConnectRawExpectations("wss://www.example.org/", NoSubProtocols(),
1515                                   HttpRequestHeaders(),
1516                                   std::move(raw_socket_data));
1517   // WaitUntilConnectDone doesn't work in this case.
1518   base::RunLoop().RunUntilIdle();
1519   EXPECT_FALSE(has_failed());
1520   ASSERT_TRUE(ssl_error_callbacks_);
1521   ssl_error_callbacks_->CancelSSLRequest(ERR_CERT_AUTHORITY_INVALID,
1522                                          &ssl_info_);
1523   WaitUntilConnectDone();
1524   EXPECT_TRUE(has_failed());
1525 }
1526 
TEST_P(WebSocketStreamCreateTest,SelfSignedCertificateSuccess)1527 TEST_P(WebSocketStreamCreateTest, SelfSignedCertificateSuccess) {
1528   auto ssl_socket_data = std::make_unique<SSLSocketDataProvider>(
1529       ASYNC, ERR_CERT_AUTHORITY_INVALID);
1530   ssl_socket_data->ssl_info.cert =
1531       ImportCertFromFile(GetTestCertsDirectory(), "unittest.selfsigned.der");
1532   ASSERT_TRUE(ssl_socket_data->ssl_info.cert.get());
1533   url_request_context_host_.AddSSLSocketDataProvider(
1534       std::move(ssl_socket_data));
1535   url_request_context_host_.AddSSLSocketDataProvider(
1536       std::make_unique<SSLSocketDataProvider>(ASYNC, OK));
1537   AddRawExpectations(BuildNullSocketData());
1538   CreateAndConnectStandard("wss://www.example.org/", NoSubProtocols(), {}, {},
1539                            {});
1540   // WaitUntilConnectDone doesn't work in this case.
1541   base::RunLoop().RunUntilIdle();
1542   ASSERT_TRUE(ssl_error_callbacks_);
1543   ssl_error_callbacks_->ContinueSSLRequest();
1544   WaitUntilConnectDone();
1545   EXPECT_FALSE(has_failed());
1546   EXPECT_TRUE(stream_);
1547 }
1548 
1549 // If the server requests authorisation, but we have no credentials, the
1550 // connection should fail cleanly.
TEST_P(WebSocketStreamCreateBasicAuthTest,FailureNoCredentials)1551 TEST_P(WebSocketStreamCreateBasicAuthTest, FailureNoCredentials) {
1552   CreateAndConnectCustomResponse("ws://www.example.org/", NoSubProtocols(), {},
1553                                  {}, kUnauthorizedResponse);
1554   WaitUntilConnectDone();
1555   EXPECT_TRUE(has_failed());
1556   EXPECT_EQ("HTTP Authentication failed; no valid credentials available",
1557             failure_message());
1558   EXPECT_FALSE(response_info_);
1559 }
1560 
TEST_P(WebSocketStreamCreateBasicAuthTest,SuccessPasswordInUrl)1561 TEST_P(WebSocketStreamCreateBasicAuthTest, SuccessPasswordInUrl) {
1562   CreateAndConnectAuthHandshake("ws://foo:[email protected]/", "Zm9vOmJhcg==",
1563                                 WebSocketStandardResponse(std::string()));
1564   WaitUntilConnectDone();
1565   EXPECT_FALSE(has_failed());
1566   EXPECT_TRUE(stream_);
1567   ASSERT_TRUE(response_info_);
1568   EXPECT_EQ(101, response_info_->headers->response_code());
1569 }
1570 
TEST_P(WebSocketStreamCreateBasicAuthTest,FailureIncorrectPasswordInUrl)1571 TEST_P(WebSocketStreamCreateBasicAuthTest, FailureIncorrectPasswordInUrl) {
1572   CreateAndConnectAuthHandshake("ws://foo:[email protected]/",
1573                                 "Zm9vOmJheg==", kUnauthorizedResponse);
1574   WaitUntilConnectDone();
1575   EXPECT_TRUE(has_failed());
1576   EXPECT_FALSE(response_info_);
1577 }
1578 
TEST_P(WebSocketStreamCreateBasicAuthTest,SuccessfulConnectionReuse)1579 TEST_P(WebSocketStreamCreateBasicAuthTest, SuccessfulConnectionReuse) {
1580   std::string request1 = WebSocketStandardRequest(
1581       "/", "www.example.org", Origin(), /*send_additional_request_headers=*/{},
1582       /*extra_headers=*/{});
1583   std::string response1 = kUnauthorizedResponse;
1584   std::string request2 = WebSocketStandardRequest(
1585       "/", "www.example.org", Origin(),
1586       {{"Authorization", "Basic Zm9vOmJhcg=="}}, /*extra_headers=*/{});
1587   std::string response2 = WebSocketStandardResponse(std::string());
1588   MockWrite writes[] = {
1589       MockWrite(SYNCHRONOUS, 0, request1.c_str()),
1590       MockWrite(SYNCHRONOUS, 2, request2.c_str()),
1591   };
1592   MockRead reads[3] = {
1593       MockRead(SYNCHRONOUS, 1, response1.c_str()),
1594       MockRead(SYNCHRONOUS, 3, response2.c_str()),
1595       MockRead(SYNCHRONOUS, ERR_IO_PENDING, 4),
1596   };
1597   CreateAndConnectRawExpectations("ws://foo:[email protected]/",
1598                                   NoSubProtocols(), HttpRequestHeaders(),
1599                                   BuildSocketData(reads, writes));
1600   WaitUntilConnectDone();
1601   EXPECT_FALSE(has_failed());
1602   EXPECT_TRUE(stream_);
1603   ASSERT_TRUE(response_info_);
1604   EXPECT_EQ(101, response_info_->headers->response_code());
1605 }
1606 
TEST_P(WebSocketStreamCreateBasicAuthTest,OnAuthRequiredCancelAuth)1607 TEST_P(WebSocketStreamCreateBasicAuthTest, OnAuthRequiredCancelAuth) {
1608   CreateAndConnectCustomResponse("ws://www.example.org/", NoSubProtocols(), {},
1609                                  {}, kUnauthorizedResponse);
1610 
1611   EXPECT_FALSE(request_info_);
1612   EXPECT_FALSE(response_info_);
1613   on_auth_required_rv_ = ERR_IO_PENDING;
1614   WaitUntilOnAuthRequired();
1615 
1616   EXPECT_FALSE(stream_);
1617   EXPECT_FALSE(has_failed());
1618 
1619   std::move(on_auth_required_callback_).Run(nullptr);
1620   WaitUntilConnectDone();
1621   EXPECT_FALSE(stream_);
1622   EXPECT_TRUE(has_failed());
1623 }
1624 
TEST_P(WebSocketStreamCreateBasicAuthTest,OnAuthRequiredSetAuth)1625 TEST_P(WebSocketStreamCreateBasicAuthTest, OnAuthRequiredSetAuth) {
1626   CreateAndConnectRawExpectations(
1627       "ws://www.example.org/", NoSubProtocols(), HttpRequestHeaders(),
1628       helper_.BuildAuthSocketData(kUnauthorizedResponse,
1629                                   RequestExpectation("Zm9vOmJheg=="),
1630                                   WebSocketStandardResponse(std::string())));
1631 
1632   EXPECT_FALSE(request_info_);
1633   EXPECT_FALSE(response_info_);
1634   on_auth_required_rv_ = ERR_IO_PENDING;
1635   WaitUntilOnAuthRequired();
1636 
1637   EXPECT_FALSE(stream_);
1638   EXPECT_FALSE(has_failed());
1639 
1640   AuthCredentials credentials(u"foo", u"baz");
1641   std::move(on_auth_required_callback_).Run(&credentials);
1642 
1643   WaitUntilConnectDone();
1644   EXPECT_TRUE(stream_);
1645   EXPECT_FALSE(has_failed());
1646 }
1647 
1648 // Digest auth has the same connection semantics as Basic auth, so we can
1649 // generally assume that whatever works for Basic auth will also work for
1650 // Digest. There's just one test here, to confirm that it works at all.
TEST_P(WebSocketStreamCreateDigestAuthTest,DigestPasswordInUrl)1651 TEST_P(WebSocketStreamCreateDigestAuthTest, DigestPasswordInUrl) {
1652   CreateAndConnectRawExpectations(
1653       "ws://FooBar:[email protected]/", NoSubProtocols(),
1654       HttpRequestHeaders(),
1655       helper_.BuildAuthSocketData(kUnauthorizedResponse, kAuthorizedRequest,
1656                                   WebSocketStandardResponse(std::string())));
1657   WaitUntilConnectDone();
1658   EXPECT_FALSE(has_failed());
1659   EXPECT_TRUE(stream_);
1660   ASSERT_TRUE(response_info_);
1661   EXPECT_EQ(101, response_info_->headers->response_code());
1662 }
1663 
TEST_P(WebSocketMultiProtocolStreamCreateTest,Incomplete)1664 TEST_P(WebSocketMultiProtocolStreamCreateTest, Incomplete) {
1665   base::HistogramTester histogram_tester;
1666 
1667   AddSSLData();
1668   if (stream_type_ == BASIC_HANDSHAKE_STREAM) {
1669     std::string request = WebSocketStandardRequest(
1670         "/", "www.example.org", Origin(),
1671         /*send_additional_request_headers=*/{}, /*extra_headers=*/{});
1672     MockRead reads[] = {MockRead(ASYNC, ERR_IO_PENDING, 0)};
1673     MockWrite writes[] = {MockWrite(ASYNC, 1, request.c_str())};
1674     CreateAndConnectRawExpectations("wss://www.example.org/", NoSubProtocols(),
1675                                     HttpRequestHeaders(),
1676                                     BuildSocketData(reads, writes));
1677     base::RunLoop().RunUntilIdle();
1678     stream_request_.reset();
1679 
1680     auto samples = histogram_tester.GetHistogramSamplesSinceCreation(
1681         "Net.WebSocket.HandshakeResult2");
1682     EXPECT_EQ(1, samples->TotalCount());
1683     EXPECT_EQ(1,
1684               samples->GetCount(static_cast<int>(
1685                   WebSocketHandshakeStreamBase::HandshakeResult::INCOMPLETE)));
1686   } else {
1687     DCHECK_EQ(stream_type_, HTTP2_HANDSHAKE_STREAM);
1688     CreateAndConnectStandard("wss://www.example.org/", NoSubProtocols(), {}, {},
1689                              {});
1690     stream_request_.reset();
1691 
1692     auto samples = histogram_tester.GetHistogramSamplesSinceCreation(
1693         "Net.WebSocket.HandshakeResult2");
1694     EXPECT_EQ(1, samples->TotalCount());
1695     EXPECT_EQ(
1696         1,
1697         samples->GetCount(static_cast<int>(
1698             WebSocketHandshakeStreamBase::HandshakeResult::HTTP2_INCOMPLETE)));
1699   }
1700 }
1701 
TEST_P(WebSocketMultiProtocolStreamCreateTest,Http2StreamReset)1702 TEST_P(WebSocketMultiProtocolStreamCreateTest, Http2StreamReset) {
1703   AddSSLData();
1704 
1705   if (stream_type_ == BASIC_HANDSHAKE_STREAM) {
1706     // This is a dummy transaction to avoid crash in ~URLRequestContext().
1707     CreateAndConnectStandard("wss://www.example.org/", NoSubProtocols(), {}, {},
1708                              {});
1709   } else {
1710     DCHECK_EQ(stream_type_, HTTP2_HANDSHAKE_STREAM);
1711     base::HistogramTester histogram_tester;
1712 
1713     SetResetWebSocketHttp2Stream(true);
1714     CreateAndConnectStandard("wss://www.example.org/", NoSubProtocols(), {}, {},
1715                              {});
1716     base::RunLoop().RunUntilIdle();
1717     stream_request_.reset();
1718 
1719     EXPECT_TRUE(has_failed());
1720     EXPECT_EQ("Stream closed with error: net::ERR_HTTP2_PROTOCOL_ERROR",
1721               failure_message());
1722 
1723     auto samples = histogram_tester.GetHistogramSamplesSinceCreation(
1724         "Net.WebSocket.HandshakeResult2");
1725     EXPECT_EQ(1, samples->TotalCount());
1726     EXPECT_EQ(
1727         1, samples->GetCount(static_cast<int>(
1728                WebSocketHandshakeStreamBase::HandshakeResult::HTTP2_FAILED)));
1729   }
1730 }
1731 
TEST_P(WebSocketStreamCreateTest,HandleErrConnectionClosed)1732 TEST_P(WebSocketStreamCreateTest, HandleErrConnectionClosed) {
1733   base::HistogramTester histogram_tester;
1734 
1735   static constexpr char kTruncatedResponse[] =
1736       "HTTP/1.1 101 Switching Protocols\r\n"
1737       "Upgrade: websocket\r\n"
1738       "Connection: Upgrade\r\n"
1739       "Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=\r\n"
1740       "Cache-Control: no-sto";
1741 
1742   std::string request = WebSocketStandardRequest(
1743       "/", "www.example.org", Origin(), /*send_additional_request_headers=*/{},
1744       /*extra_headers=*/{});
1745   MockRead reads[] = {
1746       MockRead(SYNCHRONOUS, 1, kTruncatedResponse),
1747       MockRead(SYNCHRONOUS, ERR_CONNECTION_CLOSED, 2),
1748   };
1749   MockWrite writes[] = {MockWrite(SYNCHRONOUS, 0, request.c_str())};
1750   std::unique_ptr<SequencedSocketData> socket_data(
1751       BuildSocketData(reads, writes));
1752   socket_data->set_connect_data(MockConnect(SYNCHRONOUS, OK));
1753   CreateAndConnectRawExpectations("ws://www.example.org/", NoSubProtocols(),
1754                                   HttpRequestHeaders(), std::move(socket_data));
1755   WaitUntilConnectDone();
1756   EXPECT_TRUE(has_failed());
1757 
1758   stream_request_.reset();
1759 
1760   auto samples = histogram_tester.GetHistogramSamplesSinceCreation(
1761       "Net.WebSocket.HandshakeResult2");
1762   EXPECT_EQ(1, samples->TotalCount());
1763   EXPECT_EQ(1, samples->GetCount(static_cast<int>(
1764                    WebSocketHandshakeStreamBase::HandshakeResult::
1765                        FAILED_SWITCHING_PROTOCOLS)));
1766 }
1767 
TEST_P(WebSocketStreamCreateTest,HandleErrTunnelConnectionFailed)1768 TEST_P(WebSocketStreamCreateTest, HandleErrTunnelConnectionFailed) {
1769   static constexpr char kConnectRequest[] =
1770       "CONNECT www.example.org:80 HTTP/1.1\r\n"
1771       "Host: www.example.org:80\r\n"
1772       "Proxy-Connection: keep-alive\r\n"
1773       "\r\n";
1774 
1775   static constexpr char kProxyResponse[] =
1776       "HTTP/1.1 403 Forbidden\r\n"
1777       "Content-Type: text/html\r\n"
1778       "Content-Length: 9\r\n"
1779       "Connection: keep-alive\r\n"
1780       "\r\n"
1781       "Forbidden";
1782 
1783   MockRead reads[] = {MockRead(SYNCHRONOUS, 1, kProxyResponse)};
1784   MockWrite writes[] = {MockWrite(SYNCHRONOUS, 0, kConnectRequest)};
1785   std::unique_ptr<SequencedSocketData> socket_data(
1786       BuildSocketData(reads, writes));
1787   url_request_context_host_.SetProxyConfig("https=proxy:8000");
1788   CreateAndConnectRawExpectations("ws://www.example.org/", NoSubProtocols(),
1789                                   HttpRequestHeaders(), std::move(socket_data));
1790   WaitUntilConnectDone();
1791   EXPECT_TRUE(has_failed());
1792   EXPECT_EQ("Establishing a tunnel via proxy server failed.",
1793             failure_message());
1794 }
1795 
TEST_P(WebSocketStreamCreateTest,CancelSSLRequestAfterDelete)1796 TEST_P(WebSocketStreamCreateTest, CancelSSLRequestAfterDelete) {
1797   auto ssl_socket_data = std::make_unique<SSLSocketDataProvider>(
1798       ASYNC, ERR_CERT_AUTHORITY_INVALID);
1799   ssl_socket_data->ssl_info.cert =
1800       ImportCertFromFile(GetTestCertsDirectory(), "unittest.selfsigned.der");
1801   ASSERT_TRUE(ssl_socket_data->ssl_info.cert.get());
1802   url_request_context_host_.AddSSLSocketDataProvider(
1803       std::move(ssl_socket_data));
1804 
1805   MockRead reads[] = {MockRead(SYNCHRONOUS, ERR_CONNECTION_RESET, 0)};
1806   MockWrite writes[] = {MockWrite(SYNCHRONOUS, ERR_CONNECTION_RESET, 1)};
1807   std::unique_ptr<SequencedSocketData> raw_socket_data(
1808       BuildSocketData(reads, writes));
1809   CreateAndConnectRawExpectations("wss://www.example.org/", NoSubProtocols(),
1810                                   HttpRequestHeaders(),
1811                                   std::move(raw_socket_data));
1812   base::RunLoop().RunUntilIdle();
1813   EXPECT_FALSE(has_failed());
1814   ASSERT_TRUE(ssl_error_callbacks_);
1815   stream_request_.reset();
1816   ssl_error_callbacks_->CancelSSLRequest(ERR_CERT_AUTHORITY_INVALID,
1817                                          &ssl_info_);
1818 }
1819 
TEST_P(WebSocketStreamCreateTest,ContinueSSLRequestAfterDelete)1820 TEST_P(WebSocketStreamCreateTest, ContinueSSLRequestAfterDelete) {
1821   auto ssl_socket_data = std::make_unique<SSLSocketDataProvider>(
1822       ASYNC, ERR_CERT_AUTHORITY_INVALID);
1823   ssl_socket_data->ssl_info.cert =
1824       ImportCertFromFile(GetTestCertsDirectory(), "unittest.selfsigned.der");
1825   ASSERT_TRUE(ssl_socket_data->ssl_info.cert.get());
1826   url_request_context_host_.AddSSLSocketDataProvider(
1827       std::move(ssl_socket_data));
1828 
1829   MockRead reads[] = {MockRead(SYNCHRONOUS, ERR_CONNECTION_RESET, 0)};
1830   MockWrite writes[] = {MockWrite(SYNCHRONOUS, ERR_CONNECTION_RESET, 1)};
1831   std::unique_ptr<SequencedSocketData> raw_socket_data(
1832       BuildSocketData(reads, writes));
1833   CreateAndConnectRawExpectations("wss://www.example.org/", NoSubProtocols(),
1834                                   HttpRequestHeaders(),
1835                                   std::move(raw_socket_data));
1836   base::RunLoop().RunUntilIdle();
1837   EXPECT_FALSE(has_failed());
1838   ASSERT_TRUE(ssl_error_callbacks_);
1839   stream_request_.reset();
1840   ssl_error_callbacks_->ContinueSSLRequest();
1841 }
1842 
TEST_P(WebSocketStreamCreateTest,HandleConnectionCloseInFirstSegment)1843 TEST_P(WebSocketStreamCreateTest, HandleConnectionCloseInFirstSegment) {
1844   std::string request = WebSocketStandardRequest(
1845       "/", "www.example.org", Origin(), /*send_additional_request_headers=*/{},
1846       /*extra_headers=*/{});
1847 
1848   // The response headers are immediately followed by a close frame, length 11,
1849   // code 1013, reason "Try Again".
1850   std::string close_body = "\x03\xf5Try Again";
1851   std::string response = WebSocketStandardResponse(std::string()) + "\x88" +
1852                          static_cast<char>(close_body.size()) + close_body;
1853   MockRead reads[] = {
1854       MockRead(SYNCHRONOUS, response.data(), response.size(), 1),
1855       MockRead(SYNCHRONOUS, ERR_CONNECTION_CLOSED, 2),
1856   };
1857   MockWrite writes[] = {MockWrite(SYNCHRONOUS, 0, request.c_str())};
1858   std::unique_ptr<SequencedSocketData> socket_data(
1859       BuildSocketData(reads, writes));
1860   socket_data->set_connect_data(MockConnect(SYNCHRONOUS, OK));
1861   CreateAndConnectRawExpectations("ws://www.example.org/", NoSubProtocols(),
1862                                   HttpRequestHeaders(), std::move(socket_data));
1863   WaitUntilConnectDone();
1864   ASSERT_TRUE(stream_);
1865 
1866   std::vector<std::unique_ptr<WebSocketFrame>> frames;
1867   TestCompletionCallback callback1;
1868   int rv1 = stream_->ReadFrames(&frames, callback1.callback());
1869   rv1 = callback1.GetResult(rv1);
1870   ASSERT_THAT(rv1, IsOk());
1871   ASSERT_EQ(1U, frames.size());
1872   EXPECT_EQ(frames[0]->header.opcode, WebSocketFrameHeader::kOpCodeClose);
1873   EXPECT_TRUE(frames[0]->header.final);
1874   EXPECT_EQ(close_body,
1875             std::string(frames[0]->payload, frames[0]->header.payload_length));
1876 
1877   std::vector<std::unique_ptr<WebSocketFrame>> empty_frames;
1878   TestCompletionCallback callback2;
1879   int rv2 = stream_->ReadFrames(&empty_frames, callback2.callback());
1880   rv2 = callback2.GetResult(rv2);
1881   ASSERT_THAT(rv2, IsError(ERR_CONNECTION_CLOSED));
1882 }
1883 
1884 }  // namespace
1885 }  // namespace net
1886