Home | History | Annotate | Download | only in spdy
      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/spdy/spdy_websocket_stream.h"
      6 
      7 #include <string>
      8 #include <vector>
      9 
     10 #include "base/bind.h"
     11 #include "base/bind_helpers.h"
     12 #include "net/base/completion_callback.h"
     13 #include "net/proxy/proxy_server.h"
     14 #include "net/socket/next_proto.h"
     15 #include "net/socket/ssl_client_socket.h"
     16 #include "net/spdy/spdy_http_utils.h"
     17 #include "net/spdy/spdy_protocol.h"
     18 #include "net/spdy/spdy_session.h"
     19 #include "net/spdy/spdy_websocket_test_util.h"
     20 #include "testing/gtest/include/gtest/gtest.h"
     21 
     22 namespace net {
     23 
     24 namespace {
     25 
     26 struct SpdyWebSocketStreamEvent {
     27   enum EventType {
     28     EVENT_CREATED,
     29     EVENT_SENT_HEADERS,
     30     EVENT_RECEIVED_HEADER,
     31     EVENT_SENT_DATA,
     32     EVENT_RECEIVED_DATA,
     33     EVENT_CLOSE,
     34   };
     35   SpdyWebSocketStreamEvent(EventType type,
     36                            const SpdyHeaderBlock& headers,
     37                            int result,
     38                            const std::string& data)
     39       : event_type(type),
     40         headers(headers),
     41         result(result),
     42         data(data) {}
     43 
     44   EventType event_type;
     45   SpdyHeaderBlock headers;
     46   int result;
     47   std::string data;
     48 };
     49 
     50 class SpdyWebSocketStreamEventRecorder : public SpdyWebSocketStream::Delegate {
     51  public:
     52   explicit SpdyWebSocketStreamEventRecorder(const CompletionCallback& callback)
     53       : callback_(callback) {}
     54   virtual ~SpdyWebSocketStreamEventRecorder() {}
     55 
     56   typedef base::Callback<void(SpdyWebSocketStreamEvent*)> StreamEventCallback;
     57 
     58   void SetOnCreated(const StreamEventCallback& callback) {
     59     on_created_ = callback;
     60   }
     61   void SetOnSentHeaders(const StreamEventCallback& callback) {
     62     on_sent_headers_ = callback;
     63   }
     64   void SetOnReceivedHeader(const StreamEventCallback& callback) {
     65     on_received_header_ = callback;
     66   }
     67   void SetOnSentData(const StreamEventCallback& callback) {
     68     on_sent_data_ = callback;
     69   }
     70   void SetOnReceivedData(const StreamEventCallback& callback) {
     71     on_received_data_ = callback;
     72   }
     73   void SetOnClose(const StreamEventCallback& callback) {
     74     on_close_ = callback;
     75   }
     76 
     77   virtual void OnCreatedSpdyStream(int result) OVERRIDE {
     78     events_.push_back(
     79         SpdyWebSocketStreamEvent(SpdyWebSocketStreamEvent::EVENT_CREATED,
     80                                  SpdyHeaderBlock(),
     81                                  result,
     82                                  std::string()));
     83     if (!on_created_.is_null())
     84       on_created_.Run(&events_.back());
     85   }
     86   virtual void OnSentSpdyHeaders() OVERRIDE {
     87     events_.push_back(
     88         SpdyWebSocketStreamEvent(SpdyWebSocketStreamEvent::EVENT_SENT_HEADERS,
     89                                  SpdyHeaderBlock(),
     90                                  OK,
     91                                  std::string()));
     92     if (!on_sent_data_.is_null())
     93       on_sent_data_.Run(&events_.back());
     94   }
     95   virtual void OnSpdyResponseHeadersUpdated(
     96       const SpdyHeaderBlock& response_headers) OVERRIDE {
     97     events_.push_back(
     98         SpdyWebSocketStreamEvent(
     99             SpdyWebSocketStreamEvent::EVENT_RECEIVED_HEADER,
    100             response_headers,
    101             OK,
    102             std::string()));
    103     if (!on_received_header_.is_null())
    104       on_received_header_.Run(&events_.back());
    105   }
    106   virtual void OnSentSpdyData(size_t bytes_sent) OVERRIDE {
    107     events_.push_back(
    108         SpdyWebSocketStreamEvent(
    109             SpdyWebSocketStreamEvent::EVENT_SENT_DATA,
    110             SpdyHeaderBlock(),
    111             static_cast<int>(bytes_sent),
    112             std::string()));
    113     if (!on_sent_data_.is_null())
    114       on_sent_data_.Run(&events_.back());
    115   }
    116   virtual void OnReceivedSpdyData(scoped_ptr<SpdyBuffer> buffer) OVERRIDE {
    117     std::string buffer_data;
    118     size_t buffer_len = 0;
    119     if (buffer) {
    120       buffer_len = buffer->GetRemainingSize();
    121       buffer_data.append(buffer->GetRemainingData(), buffer_len);
    122     }
    123     events_.push_back(
    124         SpdyWebSocketStreamEvent(
    125             SpdyWebSocketStreamEvent::EVENT_RECEIVED_DATA,
    126             SpdyHeaderBlock(),
    127             buffer_len,
    128             buffer_data));
    129     if (!on_received_data_.is_null())
    130       on_received_data_.Run(&events_.back());
    131   }
    132   virtual void OnCloseSpdyStream() OVERRIDE {
    133     events_.push_back(
    134         SpdyWebSocketStreamEvent(
    135             SpdyWebSocketStreamEvent::EVENT_CLOSE,
    136             SpdyHeaderBlock(),
    137             OK,
    138             std::string()));
    139     if (!on_close_.is_null())
    140       on_close_.Run(&events_.back());
    141     if (!callback_.is_null())
    142       callback_.Run(OK);
    143   }
    144 
    145   const std::vector<SpdyWebSocketStreamEvent>& GetSeenEvents() const {
    146     return events_;
    147   }
    148 
    149  private:
    150   std::vector<SpdyWebSocketStreamEvent> events_;
    151   StreamEventCallback on_created_;
    152   StreamEventCallback on_sent_headers_;
    153   StreamEventCallback on_received_header_;
    154   StreamEventCallback on_sent_data_;
    155   StreamEventCallback on_received_data_;
    156   StreamEventCallback on_close_;
    157   CompletionCallback callback_;
    158 
    159   DISALLOW_COPY_AND_ASSIGN(SpdyWebSocketStreamEventRecorder);
    160 };
    161 
    162 }  // namespace
    163 
    164 class SpdyWebSocketStreamTest
    165     : public ::testing::Test,
    166       public ::testing::WithParamInterface<NextProto> {
    167  public:
    168   OrderedSocketData* data() { return data_.get(); }
    169 
    170   void DoSendHelloFrame(SpdyWebSocketStreamEvent* event) {
    171     // Record the actual stream_id.
    172     created_stream_id_ = websocket_stream_->stream_->stream_id();
    173     websocket_stream_->SendData(kMessageFrame, kMessageFrameLength);
    174   }
    175 
    176   void DoSendClosingFrame(SpdyWebSocketStreamEvent* event) {
    177     websocket_stream_->SendData(kClosingFrame, kClosingFrameLength);
    178   }
    179 
    180   void DoClose(SpdyWebSocketStreamEvent* event) {
    181     websocket_stream_->Close();
    182   }
    183 
    184   void DoSync(SpdyWebSocketStreamEvent* event) {
    185     sync_callback_.callback().Run(OK);
    186   }
    187 
    188  protected:
    189   SpdyWebSocketStreamTest()
    190       : spdy_util_(GetParam()),
    191         spdy_settings_id_to_set_(SETTINGS_MAX_CONCURRENT_STREAMS),
    192         spdy_settings_flags_to_set_(SETTINGS_FLAG_PLEASE_PERSIST),
    193         spdy_settings_value_to_set_(1),
    194         session_deps_(GetParam()),
    195         stream_id_(0),
    196         created_stream_id_(0) {}
    197   virtual ~SpdyWebSocketStreamTest() {}
    198 
    199   virtual void SetUp() {
    200     host_port_pair_.set_host("example.com");
    201     host_port_pair_.set_port(80);
    202     spdy_session_key_ = SpdySessionKey(host_port_pair_,
    203                                        ProxyServer::Direct(),
    204                                        kPrivacyModeDisabled);
    205 
    206     spdy_settings_to_send_[spdy_settings_id_to_set_] =
    207         SettingsFlagsAndValue(
    208             SETTINGS_FLAG_PERSISTED, spdy_settings_value_to_set_);
    209   }
    210 
    211   virtual void TearDown() {
    212     base::MessageLoop::current()->RunUntilIdle();
    213   }
    214 
    215   void Prepare(SpdyStreamId stream_id) {
    216     stream_id_ = stream_id;
    217 
    218     request_frame_.reset(spdy_util_.ConstructSpdyWebSocketSynStream(
    219         stream_id_,
    220         "/echo",
    221         "example.com",
    222         "http://example.com/wsdemo"));
    223 
    224     response_frame_.reset(
    225         spdy_util_.ConstructSpdyWebSocketSynReply(stream_id_));
    226 
    227     message_frame_.reset(spdy_util_.ConstructSpdyWebSocketDataFrame(
    228         kMessageFrame,
    229         kMessageFrameLength,
    230         stream_id_,
    231         false));
    232 
    233     closing_frame_.reset(spdy_util_.ConstructSpdyWebSocketDataFrame(
    234         kClosingFrame,
    235         kClosingFrameLength,
    236         stream_id_,
    237         false));
    238   }
    239 
    240   void InitSession(MockRead* reads, size_t reads_count,
    241                    MockWrite* writes, size_t writes_count) {
    242     data_.reset(new OrderedSocketData(reads, reads_count,
    243                                       writes, writes_count));
    244     session_deps_.socket_factory->AddSocketDataProvider(data_.get());
    245     http_session_ = SpdySessionDependencies::SpdyCreateSession(&session_deps_);
    246     session_ = CreateInsecureSpdySession(
    247         http_session_, spdy_session_key_, BoundNetLog());
    248   }
    249 
    250   void SendRequest() {
    251     scoped_ptr<SpdyHeaderBlock> headers(new SpdyHeaderBlock);
    252     spdy_util_.SetHeader("path", "/echo", headers.get());
    253     spdy_util_.SetHeader("host", "example.com", headers.get());
    254     spdy_util_.SetHeader("version", "WebSocket/13", headers.get());
    255     spdy_util_.SetHeader("scheme", "ws", headers.get());
    256     spdy_util_.SetHeader("origin", "http://example.com/wsdemo", headers.get());
    257     websocket_stream_->SendRequest(headers.Pass());
    258   }
    259 
    260   SpdyWebSocketTestUtil spdy_util_;
    261   SpdySettingsIds spdy_settings_id_to_set_;
    262   SpdySettingsFlags spdy_settings_flags_to_set_;
    263   uint32 spdy_settings_value_to_set_;
    264   SettingsMap spdy_settings_to_send_;
    265   SpdySessionDependencies session_deps_;
    266   scoped_ptr<OrderedSocketData> data_;
    267   scoped_refptr<HttpNetworkSession> http_session_;
    268   base::WeakPtr<SpdySession> session_;
    269   scoped_ptr<SpdyWebSocketStream> websocket_stream_;
    270   SpdyStreamId stream_id_;
    271   SpdyStreamId created_stream_id_;
    272   scoped_ptr<SpdyFrame> request_frame_;
    273   scoped_ptr<SpdyFrame> response_frame_;
    274   scoped_ptr<SpdyFrame> message_frame_;
    275   scoped_ptr<SpdyFrame> closing_frame_;
    276   HostPortPair host_port_pair_;
    277   SpdySessionKey spdy_session_key_;
    278   TestCompletionCallback completion_callback_;
    279   TestCompletionCallback sync_callback_;
    280 
    281   static const char kMessageFrame[];
    282   static const char kClosingFrame[];
    283   static const size_t kMessageFrameLength;
    284   static const size_t kClosingFrameLength;
    285 };
    286 
    287 INSTANTIATE_TEST_CASE_P(
    288     NextProto,
    289     SpdyWebSocketStreamTest,
    290     testing::Values(kProtoSPDY2, kProtoSPDY3, kProtoSPDY31, kProtoSPDY4a2,
    291                     kProtoHTTP2Draft04));
    292 
    293 // TODO(toyoshim): Replace old framing data to new one, then use HEADERS and
    294 // data frames.
    295 const char SpdyWebSocketStreamTest::kMessageFrame[] = "\x81\x05hello";
    296 const char SpdyWebSocketStreamTest::kClosingFrame[] = "\x88\0";
    297 const size_t SpdyWebSocketStreamTest::kMessageFrameLength =
    298     arraysize(SpdyWebSocketStreamTest::kMessageFrame) - 1;
    299 const size_t SpdyWebSocketStreamTest::kClosingFrameLength =
    300     arraysize(SpdyWebSocketStreamTest::kClosingFrame) - 1;
    301 
    302 TEST_P(SpdyWebSocketStreamTest, Basic) {
    303   Prepare(1);
    304   MockWrite writes[] = {
    305     CreateMockWrite(*request_frame_.get(), 1),
    306     CreateMockWrite(*message_frame_.get(), 3),
    307     CreateMockWrite(*closing_frame_.get(), 5)
    308   };
    309 
    310   MockRead reads[] = {
    311     CreateMockRead(*response_frame_.get(), 2),
    312     CreateMockRead(*message_frame_.get(), 4),
    313     // Skip sequence 6 to notify closing has been sent.
    314     CreateMockRead(*closing_frame_.get(), 7),
    315     MockRead(SYNCHRONOUS, 0, 8)  // EOF cause OnCloseSpdyStream event.
    316   };
    317 
    318   InitSession(reads, arraysize(reads), writes, arraysize(writes));
    319 
    320   SpdyWebSocketStreamEventRecorder delegate(completion_callback_.callback());
    321   delegate.SetOnReceivedHeader(
    322       base::Bind(&SpdyWebSocketStreamTest::DoSendHelloFrame,
    323                  base::Unretained(this)));
    324   delegate.SetOnReceivedData(
    325       base::Bind(&SpdyWebSocketStreamTest::DoSendClosingFrame,
    326                  base::Unretained(this)));
    327 
    328   websocket_stream_.reset(new SpdyWebSocketStream(session_, &delegate));
    329 
    330   BoundNetLog net_log;
    331   GURL url("ws://example.com/echo");
    332   ASSERT_EQ(OK, websocket_stream_->InitializeStream(url, HIGHEST, net_log));
    333 
    334   ASSERT_TRUE(websocket_stream_->stream_.get());
    335 
    336   SendRequest();
    337 
    338   completion_callback_.WaitForResult();
    339 
    340   EXPECT_EQ(stream_id_, created_stream_id_);
    341 
    342   websocket_stream_.reset();
    343 
    344   const std::vector<SpdyWebSocketStreamEvent>& events =
    345       delegate.GetSeenEvents();
    346   ASSERT_EQ(7U, events.size());
    347 
    348   EXPECT_EQ(SpdyWebSocketStreamEvent::EVENT_SENT_HEADERS,
    349             events[0].event_type);
    350   EXPECT_EQ(OK, events[0].result);
    351   EXPECT_EQ(SpdyWebSocketStreamEvent::EVENT_RECEIVED_HEADER,
    352             events[1].event_type);
    353   EXPECT_EQ(OK, events[1].result);
    354   EXPECT_EQ(SpdyWebSocketStreamEvent::EVENT_SENT_DATA,
    355             events[2].event_type);
    356   EXPECT_EQ(static_cast<int>(kMessageFrameLength), events[2].result);
    357   EXPECT_EQ(SpdyWebSocketStreamEvent::EVENT_RECEIVED_DATA,
    358             events[3].event_type);
    359   EXPECT_EQ(static_cast<int>(kMessageFrameLength), events[3].result);
    360   EXPECT_EQ(SpdyWebSocketStreamEvent::EVENT_SENT_DATA,
    361             events[4].event_type);
    362   EXPECT_EQ(static_cast<int>(kClosingFrameLength), events[4].result);
    363   EXPECT_EQ(SpdyWebSocketStreamEvent::EVENT_RECEIVED_DATA,
    364             events[5].event_type);
    365   EXPECT_EQ(static_cast<int>(kClosingFrameLength), events[5].result);
    366   EXPECT_EQ(SpdyWebSocketStreamEvent::EVENT_CLOSE,
    367             events[6].event_type);
    368   EXPECT_EQ(OK, events[6].result);
    369 
    370   // EOF close SPDY session.
    371   EXPECT_FALSE(
    372       HasSpdySession(http_session_->spdy_session_pool(), spdy_session_key_));
    373   EXPECT_TRUE(data()->at_read_eof());
    374   EXPECT_TRUE(data()->at_write_eof());
    375 }
    376 
    377 TEST_P(SpdyWebSocketStreamTest, DestructionBeforeClose) {
    378   Prepare(1);
    379   MockWrite writes[] = {
    380     CreateMockWrite(*request_frame_.get(), 1),
    381     CreateMockWrite(*message_frame_.get(), 3)
    382   };
    383 
    384   MockRead reads[] = {
    385     CreateMockRead(*response_frame_.get(), 2),
    386     CreateMockRead(*message_frame_.get(), 4),
    387     MockRead(ASYNC, ERR_IO_PENDING, 5)
    388   };
    389 
    390   InitSession(reads, arraysize(reads), writes, arraysize(writes));
    391 
    392   SpdyWebSocketStreamEventRecorder delegate(completion_callback_.callback());
    393   delegate.SetOnReceivedHeader(
    394       base::Bind(&SpdyWebSocketStreamTest::DoSendHelloFrame,
    395                  base::Unretained(this)));
    396   delegate.SetOnReceivedData(
    397       base::Bind(&SpdyWebSocketStreamTest::DoSync,
    398                  base::Unretained(this)));
    399 
    400   websocket_stream_.reset(new SpdyWebSocketStream(session_, &delegate));
    401 
    402   BoundNetLog net_log;
    403   GURL url("ws://example.com/echo");
    404   ASSERT_EQ(OK, websocket_stream_->InitializeStream(url, HIGHEST, net_log));
    405 
    406   SendRequest();
    407 
    408   sync_callback_.WaitForResult();
    409 
    410   // WebSocketStream destruction remove its SPDY stream from the session.
    411   EXPECT_TRUE(session_->IsStreamActive(stream_id_));
    412   websocket_stream_.reset();
    413   EXPECT_FALSE(session_->IsStreamActive(stream_id_));
    414 
    415   const std::vector<SpdyWebSocketStreamEvent>& events =
    416       delegate.GetSeenEvents();
    417   ASSERT_GE(4U, events.size());
    418 
    419   EXPECT_EQ(SpdyWebSocketStreamEvent::EVENT_SENT_HEADERS,
    420             events[0].event_type);
    421   EXPECT_EQ(OK, events[0].result);
    422   EXPECT_EQ(SpdyWebSocketStreamEvent::EVENT_RECEIVED_HEADER,
    423             events[1].event_type);
    424   EXPECT_EQ(OK, events[1].result);
    425   EXPECT_EQ(SpdyWebSocketStreamEvent::EVENT_SENT_DATA,
    426             events[2].event_type);
    427   EXPECT_EQ(static_cast<int>(kMessageFrameLength), events[2].result);
    428   EXPECT_EQ(SpdyWebSocketStreamEvent::EVENT_RECEIVED_DATA,
    429             events[3].event_type);
    430   EXPECT_EQ(static_cast<int>(kMessageFrameLength), events[3].result);
    431 
    432   EXPECT_TRUE(
    433       HasSpdySession(http_session_->spdy_session_pool(), spdy_session_key_));
    434   EXPECT_TRUE(data()->at_read_eof());
    435   EXPECT_TRUE(data()->at_write_eof());
    436 }
    437 
    438 TEST_P(SpdyWebSocketStreamTest, DestructionAfterExplicitClose) {
    439   Prepare(1);
    440   MockWrite writes[] = {
    441     CreateMockWrite(*request_frame_.get(), 1),
    442     CreateMockWrite(*message_frame_.get(), 3),
    443     CreateMockWrite(*closing_frame_.get(), 5)
    444   };
    445 
    446   MockRead reads[] = {
    447     CreateMockRead(*response_frame_.get(), 2),
    448     CreateMockRead(*message_frame_.get(), 4),
    449     MockRead(ASYNC, ERR_IO_PENDING, 6)
    450   };
    451 
    452   InitSession(reads, arraysize(reads), writes, arraysize(writes));
    453 
    454   SpdyWebSocketStreamEventRecorder delegate(completion_callback_.callback());
    455   delegate.SetOnReceivedHeader(
    456       base::Bind(&SpdyWebSocketStreamTest::DoSendHelloFrame,
    457                  base::Unretained(this)));
    458   delegate.SetOnReceivedData(
    459       base::Bind(&SpdyWebSocketStreamTest::DoClose,
    460                  base::Unretained(this)));
    461 
    462   websocket_stream_.reset(new SpdyWebSocketStream(session_, &delegate));
    463 
    464   BoundNetLog net_log;
    465   GURL url("ws://example.com/echo");
    466   ASSERT_EQ(OK, websocket_stream_->InitializeStream(url, HIGHEST, net_log));
    467 
    468   SendRequest();
    469 
    470   completion_callback_.WaitForResult();
    471 
    472   // SPDY stream has already been removed from the session by Close().
    473   EXPECT_FALSE(session_->IsStreamActive(stream_id_));
    474   websocket_stream_.reset();
    475 
    476   const std::vector<SpdyWebSocketStreamEvent>& events =
    477       delegate.GetSeenEvents();
    478   ASSERT_EQ(5U, events.size());
    479 
    480   EXPECT_EQ(SpdyWebSocketStreamEvent::EVENT_SENT_HEADERS,
    481             events[0].event_type);
    482   EXPECT_EQ(OK, events[0].result);
    483   EXPECT_EQ(SpdyWebSocketStreamEvent::EVENT_RECEIVED_HEADER,
    484             events[1].event_type);
    485   EXPECT_EQ(OK, events[1].result);
    486   EXPECT_EQ(SpdyWebSocketStreamEvent::EVENT_SENT_DATA,
    487             events[2].event_type);
    488   EXPECT_EQ(static_cast<int>(kMessageFrameLength), events[2].result);
    489   EXPECT_EQ(SpdyWebSocketStreamEvent::EVENT_RECEIVED_DATA,
    490             events[3].event_type);
    491   EXPECT_EQ(static_cast<int>(kMessageFrameLength), events[3].result);
    492   EXPECT_EQ(SpdyWebSocketStreamEvent::EVENT_CLOSE, events[4].event_type);
    493 
    494   EXPECT_TRUE(
    495       HasSpdySession(http_session_->spdy_session_pool(), spdy_session_key_));
    496 }
    497 
    498 TEST_P(SpdyWebSocketStreamTest, IOPending) {
    499   Prepare(1);
    500   scoped_ptr<SpdyFrame> settings_frame(
    501       spdy_util_.ConstructSpdySettings(spdy_settings_to_send_));
    502   MockWrite writes[] = {
    503     CreateMockWrite(*request_frame_.get(), 1),
    504     CreateMockWrite(*message_frame_.get(), 3),
    505     CreateMockWrite(*closing_frame_.get(), 5)
    506   };
    507 
    508   MockRead reads[] = {
    509     CreateMockRead(*settings_frame.get(), 0),
    510     CreateMockRead(*response_frame_.get(), 2),
    511     CreateMockRead(*message_frame_.get(), 4),
    512     CreateMockRead(*closing_frame_.get(), 6),
    513     MockRead(SYNCHRONOUS, 0, 7)  // EOF cause OnCloseSpdyStream event.
    514   };
    515 
    516   DeterministicSocketData data(reads, arraysize(reads),
    517                                writes, arraysize(writes));
    518   session_deps_.deterministic_socket_factory->AddSocketDataProvider(&data);
    519   http_session_ =
    520       SpdySessionDependencies::SpdyCreateSessionDeterministic(&session_deps_);
    521 
    522   session_ = CreateInsecureSpdySession(
    523       http_session_, spdy_session_key_, BoundNetLog());
    524 
    525   // Create a dummy WebSocketStream which cause ERR_IO_PENDING to another
    526   // WebSocketStream under test.
    527   SpdyWebSocketStreamEventRecorder block_delegate((CompletionCallback()));
    528 
    529   scoped_ptr<SpdyWebSocketStream> block_stream(
    530       new SpdyWebSocketStream(session_, &block_delegate));
    531   BoundNetLog block_net_log;
    532   GURL block_url("ws://example.com/block");
    533   ASSERT_EQ(OK,
    534             block_stream->InitializeStream(block_url, HIGHEST, block_net_log));
    535 
    536   data.RunFor(1);
    537 
    538   // Create a WebSocketStream under test.
    539   SpdyWebSocketStreamEventRecorder delegate(completion_callback_.callback());
    540   delegate.SetOnCreated(
    541       base::Bind(&SpdyWebSocketStreamTest::DoSync,
    542                  base::Unretained(this)));
    543   delegate.SetOnReceivedHeader(
    544       base::Bind(&SpdyWebSocketStreamTest::DoSendHelloFrame,
    545                  base::Unretained(this)));
    546   delegate.SetOnReceivedData(
    547       base::Bind(&SpdyWebSocketStreamTest::DoSendClosingFrame,
    548                  base::Unretained(this)));
    549 
    550   websocket_stream_.reset(new SpdyWebSocketStream(session_, &delegate));
    551   BoundNetLog net_log;
    552   GURL url("ws://example.com/echo");
    553   ASSERT_EQ(ERR_IO_PENDING, websocket_stream_->InitializeStream(
    554       url, HIGHEST, net_log));
    555 
    556   // Delete the fist stream to allow create the second stream.
    557   block_stream.reset();
    558   ASSERT_EQ(OK, sync_callback_.WaitForResult());
    559 
    560   SendRequest();
    561 
    562   data.RunFor(7);
    563   completion_callback_.WaitForResult();
    564 
    565   websocket_stream_.reset();
    566 
    567   const std::vector<SpdyWebSocketStreamEvent>& block_events =
    568       block_delegate.GetSeenEvents();
    569   ASSERT_EQ(0U, block_events.size());
    570 
    571   const std::vector<SpdyWebSocketStreamEvent>& events =
    572       delegate.GetSeenEvents();
    573   ASSERT_EQ(8U, events.size());
    574   EXPECT_EQ(SpdyWebSocketStreamEvent::EVENT_CREATED,
    575             events[0].event_type);
    576   EXPECT_EQ(0, events[0].result);
    577   EXPECT_EQ(SpdyWebSocketStreamEvent::EVENT_SENT_HEADERS,
    578             events[1].event_type);
    579   EXPECT_EQ(OK, events[1].result);
    580   EXPECT_EQ(SpdyWebSocketStreamEvent::EVENT_RECEIVED_HEADER,
    581             events[2].event_type);
    582   EXPECT_EQ(OK, events[2].result);
    583   EXPECT_EQ(SpdyWebSocketStreamEvent::EVENT_SENT_DATA,
    584             events[3].event_type);
    585   EXPECT_EQ(static_cast<int>(kMessageFrameLength), events[3].result);
    586   EXPECT_EQ(SpdyWebSocketStreamEvent::EVENT_RECEIVED_DATA,
    587             events[4].event_type);
    588   EXPECT_EQ(static_cast<int>(kMessageFrameLength), events[4].result);
    589   EXPECT_EQ(SpdyWebSocketStreamEvent::EVENT_SENT_DATA,
    590             events[5].event_type);
    591   EXPECT_EQ(static_cast<int>(kClosingFrameLength), events[5].result);
    592   EXPECT_EQ(SpdyWebSocketStreamEvent::EVENT_RECEIVED_DATA,
    593             events[6].event_type);
    594   EXPECT_EQ(static_cast<int>(kClosingFrameLength), events[6].result);
    595   EXPECT_EQ(SpdyWebSocketStreamEvent::EVENT_CLOSE,
    596             events[7].event_type);
    597   EXPECT_EQ(OK, events[7].result);
    598 
    599   // EOF close SPDY session.
    600   EXPECT_FALSE(
    601       HasSpdySession(http_session_->spdy_session_pool(), spdy_session_key_));
    602   EXPECT_TRUE(data.at_read_eof());
    603   EXPECT_TRUE(data.at_write_eof());
    604 }
    605 
    606 }  // namespace net
    607