• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2013 The Chromium Authors. All rights reserved.
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 <string>
8 #include <vector>
9 
10 #include "base/run_loop.h"
11 #include "net/base/net_errors.h"
12 #include "net/socket/client_socket_handle.h"
13 #include "net/socket/socket_test_util.h"
14 #include "net/url_request/url_request_test_util.h"
15 #include "net/websockets/websocket_basic_handshake_stream.h"
16 #include "net/websockets/websocket_handshake_stream_create_helper.h"
17 #include "net/websockets/websocket_test_util.h"
18 #include "testing/gtest/include/gtest/gtest.h"
19 #include "url/gurl.h"
20 
21 namespace net {
22 namespace {
23 
24 // A sub-class of WebSocketHandshakeStreamCreateHelper which always sets a
25 // deterministic key to use in the WebSocket handshake.
26 class DeterministicKeyWebSocketHandshakeStreamCreateHelper
27     : public WebSocketHandshakeStreamCreateHelper {
28  public:
DeterministicKeyWebSocketHandshakeStreamCreateHelper(const std::vector<std::string> & requested_subprotocols)29   DeterministicKeyWebSocketHandshakeStreamCreateHelper(
30       const std::vector<std::string>& requested_subprotocols)
31       : WebSocketHandshakeStreamCreateHelper(requested_subprotocols) {}
32 
CreateBasicStream(scoped_ptr<ClientSocketHandle> connection,bool using_proxy)33   virtual WebSocketHandshakeStreamBase* CreateBasicStream(
34       scoped_ptr<ClientSocketHandle> connection,
35       bool using_proxy) OVERRIDE {
36     WebSocketHandshakeStreamCreateHelper::CreateBasicStream(connection.Pass(),
37                                                             using_proxy);
38     // This will break in an obvious way if the type created by
39     // CreateBasicStream() changes.
40     static_cast<WebSocketBasicHandshakeStream*>(stream())
41         ->SetWebSocketKeyForTesting("dGhlIHNhbXBsZSBub25jZQ==");
42     return stream();
43   }
44 };
45 
46 class WebSocketStreamCreateTest : public ::testing::Test {
47  protected:
WebSocketStreamCreateTest()48   WebSocketStreamCreateTest() : websocket_error_(0) {}
49 
CreateAndConnectCustomResponse(const std::string & socket_url,const std::string & socket_path,const std::vector<std::string> & sub_protocols,const std::string & origin,const std::string & extra_request_headers,const std::string & response_body)50   void CreateAndConnectCustomResponse(
51       const std::string& socket_url,
52       const std::string& socket_path,
53       const std::vector<std::string>& sub_protocols,
54       const std::string& origin,
55       const std::string& extra_request_headers,
56       const std::string& response_body) {
57     url_request_context_host_.SetExpectations(
58         WebSocketStandardRequest(socket_path, origin, extra_request_headers),
59         response_body);
60     CreateAndConnectStream(socket_url, sub_protocols, origin);
61   }
62 
63   // |extra_request_headers| and |extra_response_headers| must end in "\r\n" or
64   // errors like "Unable to perform synchronous IO while stopped" will occur.
CreateAndConnectStandard(const std::string & socket_url,const std::string & socket_path,const std::vector<std::string> & sub_protocols,const std::string & origin,const std::string & extra_request_headers,const std::string & extra_response_headers)65   void CreateAndConnectStandard(const std::string& socket_url,
66                                 const std::string& socket_path,
67                                 const std::vector<std::string>& sub_protocols,
68                                 const std::string& origin,
69                                 const std::string& extra_request_headers,
70                                 const std::string& extra_response_headers) {
71     CreateAndConnectCustomResponse(
72         socket_url,
73         socket_path,
74         sub_protocols,
75         origin,
76         extra_request_headers,
77         WebSocketStandardResponse(extra_response_headers));
78   }
79 
CreateAndConnectRawExpectations(const std::string & socket_url,const std::vector<std::string> & sub_protocols,const std::string & origin,scoped_ptr<DeterministicSocketData> socket_data)80   void CreateAndConnectRawExpectations(
81       const std::string& socket_url,
82       const std::vector<std::string>& sub_protocols,
83       const std::string& origin,
84       scoped_ptr<DeterministicSocketData> socket_data) {
85     url_request_context_host_.SetRawExpectations(socket_data.Pass());
86     CreateAndConnectStream(socket_url, sub_protocols, origin);
87   }
88 
89   // A wrapper for CreateAndConnectStreamForTesting that knows about our default
90   // parameters.
CreateAndConnectStream(const std::string & socket_url,const std::vector<std::string> & sub_protocols,const std::string & origin)91   void CreateAndConnectStream(const std::string& socket_url,
92                               const std::vector<std::string>& sub_protocols,
93                               const std::string& origin) {
94     stream_request_ = ::net::CreateAndConnectStreamForTesting(
95         GURL(socket_url),
96         scoped_ptr<WebSocketHandshakeStreamCreateHelper>(
97             new DeterministicKeyWebSocketHandshakeStreamCreateHelper(
98                 sub_protocols)),
99         GURL(origin),
100         url_request_context_host_.GetURLRequestContext(),
101         BoundNetLog(),
102         scoped_ptr<WebSocketStream::ConnectDelegate>(
103             new TestConnectDelegate(this)));
104   }
105 
RunUntilIdle()106   static void RunUntilIdle() { base::RunLoop().RunUntilIdle(); }
107 
108   // A simple function to make the tests more readable. Creates an empty vector.
NoSubProtocols()109   static std::vector<std::string> NoSubProtocols() {
110     return std::vector<std::string>();
111   }
112 
error() const113   uint16 error() const { return websocket_error_; }
114 
115   class TestConnectDelegate : public WebSocketStream::ConnectDelegate {
116    public:
TestConnectDelegate(WebSocketStreamCreateTest * owner)117     TestConnectDelegate(WebSocketStreamCreateTest* owner) : owner_(owner) {}
118 
OnSuccess(scoped_ptr<WebSocketStream> stream)119     virtual void OnSuccess(scoped_ptr<WebSocketStream> stream) OVERRIDE {
120       stream.swap(owner_->stream_);
121     }
122 
OnFailure(uint16 websocket_error)123     virtual void OnFailure(uint16 websocket_error) OVERRIDE {
124       owner_->websocket_error_ = websocket_error;
125     }
126 
127    private:
128     WebSocketStreamCreateTest* owner_;
129   };
130 
131   WebSocketTestURLRequestContextHost url_request_context_host_;
132   scoped_ptr<WebSocketStreamRequest> stream_request_;
133   // Only set if the connection succeeded.
134   scoped_ptr<WebSocketStream> stream_;
135   // Only set if the connection failed. 0 otherwise.
136   uint16 websocket_error_;
137 };
138 
139 // Confirm that the basic case works as expected.
TEST_F(WebSocketStreamCreateTest,SimpleSuccess)140 TEST_F(WebSocketStreamCreateTest, SimpleSuccess) {
141   CreateAndConnectStandard(
142       "ws://localhost/", "/", NoSubProtocols(), "http://localhost/", "", "");
143   RunUntilIdle();
144   EXPECT_TRUE(stream_);
145 }
146 
147 // Confirm that the stream isn't established until the message loop runs.
TEST_F(WebSocketStreamCreateTest,NeedsToRunLoop)148 TEST_F(WebSocketStreamCreateTest, NeedsToRunLoop) {
149   CreateAndConnectStandard(
150       "ws://localhost/", "/", NoSubProtocols(), "http://localhost/", "", "");
151   EXPECT_FALSE(stream_);
152 }
153 
154 // Check the path is used.
TEST_F(WebSocketStreamCreateTest,PathIsUsed)155 TEST_F(WebSocketStreamCreateTest, PathIsUsed) {
156   CreateAndConnectStandard("ws://localhost/testing_path",
157                            "/testing_path",
158                            NoSubProtocols(),
159                            "http://localhost/",
160                            "",
161                            "");
162   RunUntilIdle();
163   EXPECT_TRUE(stream_);
164 }
165 
166 // Check that the origin is used.
TEST_F(WebSocketStreamCreateTest,OriginIsUsed)167 TEST_F(WebSocketStreamCreateTest, OriginIsUsed) {
168   CreateAndConnectStandard("ws://localhost/testing_path",
169                            "/testing_path",
170                            NoSubProtocols(),
171                            "http://google.com/",
172                            "",
173                            "");
174   RunUntilIdle();
175   EXPECT_TRUE(stream_);
176 }
177 
178 // Check that sub-protocols are sent and parsed.
TEST_F(WebSocketStreamCreateTest,SubProtocolIsUsed)179 TEST_F(WebSocketStreamCreateTest, SubProtocolIsUsed) {
180   std::vector<std::string> sub_protocols;
181   sub_protocols.push_back("chatv11.chromium.org");
182   sub_protocols.push_back("chatv20.chromium.org");
183   CreateAndConnectStandard("ws://localhost/testing_path",
184                            "/testing_path",
185                            sub_protocols,
186                            "http://google.com/",
187                            "Sec-WebSocket-Protocol: chatv11.chromium.org, "
188                            "chatv20.chromium.org\r\n",
189                            "Sec-WebSocket-Protocol: chatv20.chromium.org\r\n");
190   RunUntilIdle();
191   EXPECT_TRUE(stream_);
192   EXPECT_EQ("chatv20.chromium.org", stream_->GetSubProtocol());
193 }
194 
195 // Unsolicited sub-protocols are rejected.
TEST_F(WebSocketStreamCreateTest,UnsolicitedSubProtocol)196 TEST_F(WebSocketStreamCreateTest, UnsolicitedSubProtocol) {
197   CreateAndConnectStandard("ws://localhost/testing_path",
198                            "/testing_path",
199                            NoSubProtocols(),
200                            "http://google.com/",
201                            "",
202                            "Sec-WebSocket-Protocol: chatv20.chromium.org\r\n");
203   RunUntilIdle();
204   EXPECT_FALSE(stream_);
205   EXPECT_EQ(1006, error());
206 }
207 
208 // Missing sub-protocol response is rejected.
TEST_F(WebSocketStreamCreateTest,UnacceptedSubProtocol)209 TEST_F(WebSocketStreamCreateTest, UnacceptedSubProtocol) {
210   CreateAndConnectStandard("ws://localhost/testing_path",
211                            "/testing_path",
212                            std::vector<std::string>(1, "chat.example.com"),
213                            "http://localhost/",
214                            "Sec-WebSocket-Protocol: chat.example.com\r\n",
215                            "");
216   RunUntilIdle();
217   EXPECT_FALSE(stream_);
218   EXPECT_EQ(1006, error());
219 }
220 
221 // Only one sub-protocol can be accepted.
TEST_F(WebSocketStreamCreateTest,MultipleSubProtocolsInResponse)222 TEST_F(WebSocketStreamCreateTest, MultipleSubProtocolsInResponse) {
223   std::vector<std::string> sub_protocols;
224   sub_protocols.push_back("chatv11.chromium.org");
225   sub_protocols.push_back("chatv20.chromium.org");
226   CreateAndConnectStandard("ws://localhost/testing_path",
227                            "/testing_path",
228                            sub_protocols,
229                            "http://google.com/",
230                            "Sec-WebSocket-Protocol: chatv11.chromium.org, "
231                            "chatv20.chromium.org\r\n",
232                            "Sec-WebSocket-Protocol: chatv11.chromium.org, "
233                            "chatv20.chromium.org\r\n");
234   RunUntilIdle();
235   EXPECT_FALSE(stream_);
236   EXPECT_EQ(1006, error());
237 }
238 
239 // Unknown extension in the response is rejected
TEST_F(WebSocketStreamCreateTest,UnknownExtension)240 TEST_F(WebSocketStreamCreateTest, UnknownExtension) {
241   CreateAndConnectStandard("ws://localhost/testing_path",
242                            "/testing_path",
243                            NoSubProtocols(),
244                            "http://localhost/",
245                            "",
246                            "Sec-WebSocket-Extensions: x-unknown-extension\r\n");
247   RunUntilIdle();
248   EXPECT_FALSE(stream_);
249   EXPECT_EQ(1006, error());
250 }
251 
252 // Additional Sec-WebSocket-Accept headers should be rejected.
TEST_F(WebSocketStreamCreateTest,DoubleAccept)253 TEST_F(WebSocketStreamCreateTest, DoubleAccept) {
254   CreateAndConnectStandard(
255       "ws://localhost/",
256       "/",
257       NoSubProtocols(),
258       "http://localhost/",
259       "",
260       "Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=\r\n");
261   RunUntilIdle();
262   EXPECT_FALSE(stream_);
263   EXPECT_EQ(1006, error());
264 }
265 
266 // Response code 200 must be rejected.
TEST_F(WebSocketStreamCreateTest,InvalidStatusCode)267 TEST_F(WebSocketStreamCreateTest, InvalidStatusCode) {
268   static const char kInvalidStatusCodeResponse[] =
269       "HTTP/1.1 200 OK\r\n"
270       "Upgrade: websocket\r\n"
271       "Connection: Upgrade\r\n"
272       "Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=\r\n"
273       "\r\n";
274   CreateAndConnectCustomResponse("ws://localhost/",
275                                  "/",
276                                  NoSubProtocols(),
277                                  "http://localhost/",
278                                  "",
279                                  kInvalidStatusCodeResponse);
280   RunUntilIdle();
281   EXPECT_EQ(1006, error());
282 }
283 
284 // Redirects are not followed (according to the WHATWG WebSocket API, which
285 // overrides RFC6455 for browser applications).
TEST_F(WebSocketStreamCreateTest,RedirectsRejected)286 TEST_F(WebSocketStreamCreateTest, RedirectsRejected) {
287   static const char kRedirectResponse[] =
288       "HTTP/1.1 302 Moved Temporarily\r\n"
289       "Content-Type: text/html\r\n"
290       "Content-Length: 34\r\n"
291       "Connection: keep-alive\r\n"
292       "Location: ws://localhost/other\r\n"
293       "\r\n"
294       "<title>Moved</title><h1>Moved</h1>";
295   CreateAndConnectCustomResponse("ws://localhost/",
296                                  "/",
297                                  NoSubProtocols(),
298                                  "http://localhost/",
299                                  "",
300                                  kRedirectResponse);
301   RunUntilIdle();
302   EXPECT_EQ(1006, error());
303 }
304 
305 // Malformed responses should be rejected. HttpStreamParser will accept just
306 // about any garbage in the middle of the headers. To make it give up, the junk
307 // has to be at the start of the response. Even then, it just gets treated as an
308 // HTTP/0.9 response.
TEST_F(WebSocketStreamCreateTest,MalformedResponse)309 TEST_F(WebSocketStreamCreateTest, MalformedResponse) {
310   static const char kMalformedResponse[] =
311       "220 mx.google.com ESMTP\r\n"
312       "HTTP/1.1 101 OK\r\n"
313       "Upgrade: websocket\r\n"
314       "Connection: Upgrade\r\n"
315       "Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=\r\n"
316       "\r\n";
317   CreateAndConnectCustomResponse("ws://localhost/",
318                                  "/",
319                                  NoSubProtocols(),
320                                  "http://localhost/",
321                                  "",
322                                  kMalformedResponse);
323   RunUntilIdle();
324   EXPECT_EQ(1006, error());
325 }
326 
327 // Upgrade header must be present.
TEST_F(WebSocketStreamCreateTest,MissingUpgradeHeader)328 TEST_F(WebSocketStreamCreateTest, MissingUpgradeHeader) {
329   static const char kMissingUpgradeResponse[] =
330       "HTTP/1.1 101 Switching Protocols\r\n"
331       "Connection: Upgrade\r\n"
332       "Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=\r\n"
333       "\r\n";
334   CreateAndConnectCustomResponse("ws://localhost/",
335                                  "/",
336                                  NoSubProtocols(),
337                                  "http://localhost/",
338                                  "",
339                                  kMissingUpgradeResponse);
340   RunUntilIdle();
341   EXPECT_EQ(1006, error());
342 }
343 
344 // There must only be one upgrade header.
TEST_F(WebSocketStreamCreateTest,DoubleUpgradeHeader)345 TEST_F(WebSocketStreamCreateTest, DoubleUpgradeHeader) {
346   CreateAndConnectStandard(
347       "ws://localhost/",
348       "/",
349       NoSubProtocols(),
350       "http://localhost/",
351       "", "Upgrade: HTTP/2.0\r\n");
352   RunUntilIdle();
353   EXPECT_EQ(1006, error());
354 }
355 
356 // Connection header must be present.
TEST_F(WebSocketStreamCreateTest,MissingConnectionHeader)357 TEST_F(WebSocketStreamCreateTest, MissingConnectionHeader) {
358   static const char kMissingConnectionResponse[] =
359       "HTTP/1.1 101 Switching Protocols\r\n"
360       "Upgrade: websocket\r\n"
361       "Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=\r\n"
362       "\r\n";
363   CreateAndConnectCustomResponse("ws://localhost/",
364                                  "/",
365                                  NoSubProtocols(),
366                                  "http://localhost/",
367                                  "",
368                                  kMissingConnectionResponse);
369   RunUntilIdle();
370   EXPECT_EQ(1006, error());
371 }
372 
373 // Connection header is permitted to contain other tokens.
TEST_F(WebSocketStreamCreateTest,AdditionalTokenInConnectionHeader)374 TEST_F(WebSocketStreamCreateTest, AdditionalTokenInConnectionHeader) {
375   static const char kAdditionalConnectionTokenResponse[] =
376       "HTTP/1.1 101 Switching Protocols\r\n"
377       "Upgrade: websocket\r\n"
378       "Connection: Upgrade, Keep-Alive\r\n"
379       "Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=\r\n"
380       "\r\n";
381   CreateAndConnectCustomResponse("ws://localhost/",
382                                  "/",
383                                  NoSubProtocols(),
384                                  "http://localhost/",
385                                  "",
386                                  kAdditionalConnectionTokenResponse);
387   RunUntilIdle();
388   EXPECT_TRUE(stream_);
389 }
390 
391 // Sec-WebSocket-Accept header must be present.
TEST_F(WebSocketStreamCreateTest,MissingSecWebSocketAccept)392 TEST_F(WebSocketStreamCreateTest, MissingSecWebSocketAccept) {
393   static const char kMissingAcceptResponse[] =
394       "HTTP/1.1 101 Switching Protocols\r\n"
395       "Upgrade: websocket\r\n"
396       "Connection: Upgrade\r\n"
397       "\r\n";
398   CreateAndConnectCustomResponse("ws://localhost/",
399                                  "/",
400                                  NoSubProtocols(),
401                                  "http://localhost/",
402                                  "",
403                                  kMissingAcceptResponse);
404   RunUntilIdle();
405   EXPECT_EQ(1006, error());
406 }
407 
408 // Sec-WebSocket-Accept header must match the key that was sent.
TEST_F(WebSocketStreamCreateTest,WrongSecWebSocketAccept)409 TEST_F(WebSocketStreamCreateTest, WrongSecWebSocketAccept) {
410   static const char kIncorrectAcceptResponse[] =
411       "HTTP/1.1 101 Switching Protocols\r\n"
412       "Upgrade: websocket\r\n"
413       "Connection: Upgrade\r\n"
414       "Sec-WebSocket-Accept: x/byyPZ2tOFvJCGkkugcKvqhhPk=\r\n"
415       "\r\n";
416   CreateAndConnectCustomResponse("ws://localhost/",
417                                  "/",
418                                  NoSubProtocols(),
419                                  "http://localhost/",
420                                  "",
421                                  kIncorrectAcceptResponse);
422   RunUntilIdle();
423   EXPECT_EQ(1006, error());
424 }
425 
426 // Cancellation works.
TEST_F(WebSocketStreamCreateTest,Cancellation)427 TEST_F(WebSocketStreamCreateTest, Cancellation) {
428   CreateAndConnectStandard(
429       "ws://localhost/", "/", NoSubProtocols(), "http://localhost/", "", "");
430   stream_request_.reset();
431   RunUntilIdle();
432   EXPECT_FALSE(stream_);
433 }
434 
435 // Connect failure must look just like negotiation failure.
TEST_F(WebSocketStreamCreateTest,ConnectionFailure)436 TEST_F(WebSocketStreamCreateTest, ConnectionFailure) {
437   scoped_ptr<DeterministicSocketData> socket_data(
438       new DeterministicSocketData(NULL, 0, NULL, 0));
439   socket_data->set_connect_data(
440       MockConnect(SYNCHRONOUS, ERR_CONNECTION_REFUSED));
441   CreateAndConnectRawExpectations("ws://localhost/", NoSubProtocols(),
442                                   "http://localhost/", socket_data.Pass());
443   RunUntilIdle();
444   EXPECT_EQ(1006, error());
445 }
446 
447 // Connect timeout must look just like any other failure.
TEST_F(WebSocketStreamCreateTest,ConnectionTimeout)448 TEST_F(WebSocketStreamCreateTest, ConnectionTimeout) {
449   scoped_ptr<DeterministicSocketData> socket_data(
450       new DeterministicSocketData(NULL, 0, NULL, 0));
451   socket_data->set_connect_data(
452       MockConnect(ASYNC, ERR_CONNECTION_TIMED_OUT));
453   CreateAndConnectRawExpectations("ws://localhost/", NoSubProtocols(),
454                                   "http://localhost/", socket_data.Pass());
455   RunUntilIdle();
456   EXPECT_EQ(1006, error());
457 }
458 
459 // Cancellation during connect works.
TEST_F(WebSocketStreamCreateTest,CancellationDuringConnect)460 TEST_F(WebSocketStreamCreateTest, CancellationDuringConnect) {
461   scoped_ptr<DeterministicSocketData> socket_data(
462       new DeterministicSocketData(NULL, 0, NULL, 0));
463   socket_data->set_connect_data(MockConnect(SYNCHRONOUS, ERR_IO_PENDING));
464   CreateAndConnectRawExpectations("ws://localhost/",
465                                   NoSubProtocols(),
466                                   "http://localhost/",
467                                   socket_data.Pass());
468   stream_request_.reset();
469   RunUntilIdle();
470   EXPECT_FALSE(stream_);
471 }
472 
473 // Cancellation during write of the request headers works.
TEST_F(WebSocketStreamCreateTest,CancellationDuringWrite)474 TEST_F(WebSocketStreamCreateTest, CancellationDuringWrite) {
475   // We seem to need at least two operations in order to use SetStop().
476   MockWrite writes[] = {MockWrite(ASYNC, 0, "GET / HTTP/"),
477                         MockWrite(ASYNC, 1, "1.1\r\n")};
478   // We keep a copy of the pointer so that we can call RunFor() on it later.
479   DeterministicSocketData* socket_data(
480       new DeterministicSocketData(NULL, 0, writes, arraysize(writes)));
481   socket_data->set_connect_data(MockConnect(SYNCHRONOUS, OK));
482   socket_data->SetStop(1);
483   CreateAndConnectRawExpectations("ws://localhost/",
484                                   NoSubProtocols(),
485                                   "http://localhost/",
486                                   make_scoped_ptr(socket_data));
487   socket_data->Run();
488   stream_request_.reset();
489   RunUntilIdle();
490   EXPECT_FALSE(stream_);
491 }
492 
493 // Cancellation during read of the response headers works.
TEST_F(WebSocketStreamCreateTest,CancellationDuringRead)494 TEST_F(WebSocketStreamCreateTest, CancellationDuringRead) {
495   std::string request = WebSocketStandardRequest("/", "http://localhost/", "");
496   MockWrite writes[] = {MockWrite(ASYNC, 0, request.c_str())};
497   MockRead reads[] = {
498     MockRead(ASYNC, 1, "HTTP/1.1 101 Switching Protocols\r\nUpgr"),
499   };
500   DeterministicSocketData* socket_data(new DeterministicSocketData(
501       reads, arraysize(reads), writes, arraysize(writes)));
502   socket_data->set_connect_data(MockConnect(SYNCHRONOUS, OK));
503   socket_data->SetStop(1);
504   CreateAndConnectRawExpectations("ws://localhost/",
505                                   NoSubProtocols(),
506                                   "http://localhost/",
507                                   make_scoped_ptr(socket_data));
508   socket_data->Run();
509   stream_request_.reset();
510   RunUntilIdle();
511   EXPECT_FALSE(stream_);
512 }
513 
514 }  // namespace
515 }  // namespace net
516