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