| // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "net/spdy/spdy_session.h" |
| |
| #include "base/memory/scoped_ptr.h" |
| #include "net/base/io_buffer.h" |
| #include "net/base/ip_endpoint.h" |
| #include "net/base/net_log_unittest.h" |
| #include "net/base/request_priority.h" |
| #include "net/base/test_data_directory.h" |
| #include "net/base/test_data_stream.h" |
| #include "net/dns/host_cache.h" |
| #include "net/socket/next_proto.h" |
| #include "net/spdy/spdy_session_pool.h" |
| #include "net/spdy/spdy_session_test_util.h" |
| #include "net/spdy/spdy_stream.h" |
| #include "net/spdy/spdy_stream_test_util.h" |
| #include "net/spdy/spdy_test_util_common.h" |
| #include "net/spdy/spdy_test_util_spdy2.h" |
| #include "net/spdy/spdy_test_utils.h" |
| #include "net/test/cert_test_util.h" |
| #include "testing/platform_test.h" |
| |
| using namespace net::test_spdy2; |
| |
| namespace net { |
| |
| namespace { |
| |
| static const char kTestUrl[] = "http://www.example.org/"; |
| static const char kTestHost[] = "www.example.org"; |
| static const int kTestPort = 80; |
| |
| static int g_delta_seconds = 0; |
| base::TimeTicks TheNearFuture() { |
| return base::TimeTicks::Now() + base::TimeDelta::FromSeconds(g_delta_seconds); |
| } |
| |
| } // namespace |
| |
| class SpdySessionSpdy2Test : public PlatformTest { |
| protected: |
| SpdySessionSpdy2Test() |
| : session_deps_(kProtoSPDY2), |
| spdy_session_pool_(NULL), |
| test_url_(kTestUrl), |
| test_host_port_pair_(kTestHost, kTestPort), |
| pair_(test_host_port_pair_, ProxyServer::Direct()) { |
| } |
| |
| virtual void SetUp() { |
| g_delta_seconds = 0; |
| } |
| |
| void CreateDeterministicNetworkSession() { |
| http_session_ = |
| SpdySessionDependencies::SpdyCreateSessionDeterministic(&session_deps_); |
| spdy_session_pool_ = http_session_->spdy_session_pool(); |
| } |
| |
| void CreateNetworkSession() { |
| http_session_ = |
| SpdySessionDependencies::SpdyCreateSession(&session_deps_); |
| spdy_session_pool_ = http_session_->spdy_session_pool(); |
| } |
| |
| scoped_refptr<SpdySession> GetSession(const HostPortProxyPair& pair) { |
| EXPECT_FALSE(spdy_session_pool_->HasSession(pair)); |
| scoped_refptr<SpdySession> session = |
| spdy_session_pool_->Get(pair, BoundNetLog()); |
| EXPECT_TRUE(spdy_session_pool_->HasSession(pair)); |
| return session; |
| } |
| |
| // Creates an initialized session to |pair_|. |
| scoped_refptr<SpdySession> CreateInitializedSession() { |
| scoped_refptr<SpdySession> session = GetSession(pair_); |
| EXPECT_EQ( |
| OK, |
| InitializeSession( |
| http_session_.get(), session.get(), test_host_port_pair_)); |
| return session; |
| } |
| |
| net::Error InitializeSession(HttpNetworkSession* http_session, |
| SpdySession* session, |
| const HostPortPair& host_port_pair) { |
| transport_params_ = new TransportSocketParams( |
| host_port_pair, MEDIUM, false, false, OnHostResolutionCallback()); |
| |
| scoped_ptr<ClientSocketHandle> connection(new ClientSocketHandle); |
| EXPECT_EQ(OK, connection->Init(host_port_pair.ToString(), |
| transport_params_, MEDIUM, |
| CompletionCallback(), |
| http_session->GetTransportSocketPool( |
| HttpNetworkSession::NORMAL_SOCKET_POOL), |
| BoundNetLog())); |
| return session->InitializeWithSocket(connection.release(), false, OK); |
| } |
| |
| scoped_refptr<TransportSocketParams> transport_params_; |
| SpdySessionDependencies session_deps_; |
| scoped_refptr<HttpNetworkSession> http_session_; |
| SpdySessionPool* spdy_session_pool_; |
| GURL test_url_; |
| HostPortPair test_host_port_pair_; |
| HostPortProxyPair pair_; |
| }; |
| |
| TEST_F(SpdySessionSpdy2Test, GoAway) { |
| session_deps_.host_resolver->set_synchronous_mode(true); |
| |
| MockConnect connect_data(SYNCHRONOUS, OK); |
| scoped_ptr<SpdyFrame> goaway(ConstructSpdyGoAway(1)); |
| MockRead reads[] = { |
| CreateMockRead(*goaway, 2), |
| MockRead(ASYNC, 0, 3) // EOF |
| }; |
| scoped_ptr<SpdyFrame> req1(ConstructSpdyGet(NULL, 0, false, 1, MEDIUM)); |
| scoped_ptr<SpdyFrame> req2(ConstructSpdyGet(NULL, 0, false, 3, MEDIUM)); |
| MockWrite writes[] = { |
| CreateMockWrite(*req1, 0), |
| CreateMockWrite(*req2, 1), |
| }; |
| DeterministicSocketData data(reads, arraysize(reads), |
| writes, arraysize(writes)); |
| data.set_connect_data(connect_data); |
| session_deps_.deterministic_socket_factory->AddSocketDataProvider(&data); |
| |
| SSLSocketDataProvider ssl(SYNCHRONOUS, OK); |
| session_deps_.deterministic_socket_factory->AddSSLSocketDataProvider(&ssl); |
| |
| CreateDeterministicNetworkSession(); |
| |
| scoped_refptr<SpdySession> session = CreateInitializedSession(); |
| |
| EXPECT_EQ(2, session->GetProtocolVersion()); |
| |
| GURL url("http://www.google.com"); |
| base::WeakPtr<SpdyStream> spdy_stream1 = |
| CreateStreamSynchronously(session, url, MEDIUM, BoundNetLog()); |
| |
| base::WeakPtr<SpdyStream> spdy_stream2 = |
| CreateStreamSynchronously(session, url, MEDIUM, BoundNetLog()); |
| |
| scoped_ptr<SpdyHeaderBlock> headers(new SpdyHeaderBlock); |
| (*headers)["method"] = "GET"; |
| (*headers)["scheme"] = url.scheme(); |
| (*headers)["host"] = url.host(); |
| (*headers)["url"] = url.path(); |
| (*headers)["version"] = "HTTP/1.1"; |
| scoped_ptr<SpdyHeaderBlock> headers2(new SpdyHeaderBlock); |
| *headers2 = *headers; |
| |
| spdy_stream1->set_spdy_headers(headers.Pass()); |
| EXPECT_TRUE(spdy_stream1->HasUrl()); |
| |
| spdy_stream2->set_spdy_headers(headers2.Pass()); |
| EXPECT_TRUE(spdy_stream2->HasUrl()); |
| |
| spdy_stream1->SendRequest(false); |
| spdy_stream2->SendRequest(false); |
| data.RunFor(2); |
| |
| EXPECT_EQ(1u, spdy_stream1->stream_id()); |
| EXPECT_EQ(3u, spdy_stream2->stream_id()); |
| |
| EXPECT_TRUE(spdy_session_pool_->HasSession(pair_)); |
| |
| // Read and process the GOAWAY frame. |
| data.RunFor(1); |
| |
| EXPECT_FALSE(spdy_session_pool_->HasSession(pair_)); |
| |
| EXPECT_TRUE(session->IsStreamActive(1)); |
| EXPECT_FALSE(session->IsStreamActive(3)); |
| |
| scoped_refptr<SpdySession> session2 = GetSession(pair_); |
| |
| spdy_stream1->Close(); |
| EXPECT_EQ(NULL, spdy_stream1.get()); |
| |
| // Delete the first session. |
| session = NULL; |
| |
| // Delete the second session. |
| spdy_session_pool_->Remove(session2); |
| session2 = NULL; |
| EXPECT_EQ(NULL, spdy_stream2.get()); |
| } |
| |
| TEST_F(SpdySessionSpdy2Test, ClientPing) { |
| session_deps_.enable_ping = true; |
| session_deps_.host_resolver->set_synchronous_mode(true); |
| |
| MockConnect connect_data(SYNCHRONOUS, OK); |
| scoped_ptr<SpdyFrame> read_ping(ConstructSpdyPing(1)); |
| MockRead reads[] = { |
| CreateMockRead(*read_ping), |
| MockRead(SYNCHRONOUS, 0, 0) // EOF |
| }; |
| scoped_ptr<SpdyFrame> write_ping(ConstructSpdyPing(1)); |
| MockWrite writes[] = { |
| CreateMockWrite(*write_ping), |
| }; |
| StaticSocketDataProvider data( |
| reads, arraysize(reads), writes, arraysize(writes)); |
| data.set_connect_data(connect_data); |
| session_deps_.socket_factory->AddSocketDataProvider(&data); |
| |
| SSLSocketDataProvider ssl(SYNCHRONOUS, OK); |
| session_deps_.socket_factory->AddSSLSocketDataProvider(&ssl); |
| |
| CreateNetworkSession(); |
| |
| scoped_refptr<SpdySession> session = CreateInitializedSession(); |
| |
| base::WeakPtr<SpdyStream> spdy_stream1 = |
| CreateStreamSynchronously(session, test_url_, MEDIUM, BoundNetLog()); |
| ASSERT_TRUE(spdy_stream1.get() != NULL); |
| test::StreamDelegateSendImmediate delegate( |
| spdy_stream1, scoped_ptr<SpdyHeaderBlock>(), NULL); |
| spdy_stream1->SetDelegate(&delegate); |
| |
| base::TimeTicks before_ping_time = base::TimeTicks::Now(); |
| |
| session->set_connection_at_risk_of_loss_time( |
| base::TimeDelta::FromSeconds(-1)); |
| session->set_hung_interval(base::TimeDelta::FromMilliseconds(50)); |
| |
| session->SendPrefacePingIfNoneInFlight(); |
| |
| EXPECT_EQ(ERR_CONNECTION_CLOSED, delegate.WaitForClose()); |
| |
| session->CheckPingStatus(before_ping_time); |
| |
| EXPECT_EQ(0, session->pings_in_flight()); |
| EXPECT_GE(session->next_ping_id(), static_cast<uint32>(1)); |
| EXPECT_FALSE(session->check_ping_status_pending()); |
| EXPECT_GE(session->last_activity_time(), before_ping_time); |
| |
| EXPECT_FALSE(spdy_session_pool_->HasSession(pair_)); |
| |
| // Delete the first session. |
| session = NULL; |
| } |
| |
| TEST_F(SpdySessionSpdy2Test, ServerPing) { |
| session_deps_.host_resolver->set_synchronous_mode(true); |
| |
| MockConnect connect_data(SYNCHRONOUS, OK); |
| scoped_ptr<SpdyFrame> read_ping(ConstructSpdyPing(2)); |
| MockRead reads[] = { |
| CreateMockRead(*read_ping), |
| MockRead(SYNCHRONOUS, 0, 0) // EOF |
| }; |
| scoped_ptr<SpdyFrame> write_ping(ConstructSpdyPing(2)); |
| MockWrite writes[] = { |
| CreateMockWrite(*write_ping), |
| }; |
| StaticSocketDataProvider data( |
| reads, arraysize(reads), writes, arraysize(writes)); |
| data.set_connect_data(connect_data); |
| session_deps_.socket_factory->AddSocketDataProvider(&data); |
| |
| SSLSocketDataProvider ssl(SYNCHRONOUS, OK); |
| session_deps_.socket_factory->AddSSLSocketDataProvider(&ssl); |
| |
| CreateNetworkSession(); |
| |
| scoped_refptr<SpdySession> session = CreateInitializedSession(); |
| |
| base::WeakPtr<SpdyStream> spdy_stream1 = |
| CreateStreamSynchronously(session, test_url_, MEDIUM, BoundNetLog()); |
| ASSERT_TRUE(spdy_stream1.get() != NULL); |
| test::StreamDelegateSendImmediate delegate( |
| spdy_stream1, scoped_ptr<SpdyHeaderBlock>(), NULL); |
| spdy_stream1->SetDelegate(&delegate); |
| |
| // Flush the SpdySession::OnReadComplete() task. |
| MessageLoop::current()->RunUntilIdle(); |
| |
| EXPECT_FALSE(spdy_session_pool_->HasSession(pair_)); |
| |
| // Delete the session. |
| session = NULL; |
| EXPECT_EQ(NULL, spdy_stream1.get()); |
| } |
| |
| TEST_F(SpdySessionSpdy2Test, DeleteExpiredPushStreams) { |
| session_deps_.host_resolver->set_synchronous_mode(true); |
| |
| SSLSocketDataProvider ssl(SYNCHRONOUS, OK); |
| session_deps_.socket_factory->AddSSLSocketDataProvider(&ssl); |
| session_deps_.time_func = TheNearFuture; |
| |
| CreateNetworkSession(); |
| |
| scoped_refptr<SpdySession> session = GetSession(pair_); |
| |
| // Give the session a SPDY2 framer. |
| session->buffered_spdy_framer_.reset(new BufferedSpdyFramer(2, false)); |
| |
| // Create the associated stream and add to active streams. |
| scoped_ptr<SpdyHeaderBlock> request_headers(new SpdyHeaderBlock); |
| (*request_headers)["scheme"] = "http"; |
| (*request_headers)["host"] = "www.google.com"; |
| (*request_headers)["url"] = "/"; |
| |
| scoped_refptr<SpdyStream> stream( |
| new SpdyStream(session, std::string(), DEFAULT_PRIORITY, |
| kSpdyStreamInitialWindowSize, |
| kSpdyStreamInitialWindowSize, |
| false, session->net_log_)); |
| stream->set_spdy_headers(request_headers.Pass()); |
| session->ActivateStream(stream); |
| stream = NULL; |
| |
| SpdyHeaderBlock headers; |
| headers["url"] = "http://www.google.com/a.dat"; |
| session->OnSynStream(2, 1, 0, 0, true, false, headers); |
| |
| // Verify that there is one unclaimed push stream. |
| EXPECT_EQ(1u, session->num_unclaimed_pushed_streams()); |
| SpdySession::PushedStreamMap::iterator iter = |
| session->unclaimed_pushed_streams_.find("http://www.google.com/a.dat"); |
| EXPECT_TRUE(session->unclaimed_pushed_streams_.end() != iter); |
| |
| // Shift time. |
| g_delta_seconds = 301; |
| |
| headers["url"] = "http://www.google.com/b.dat"; |
| session->OnSynStream(4, 1, 0, 0, true, false, headers); |
| |
| // Verify that the second pushed stream evicted the first pushed stream. |
| EXPECT_EQ(1u, session->num_unclaimed_pushed_streams()); |
| iter = session->unclaimed_pushed_streams_.find("http://www.google.com/b.dat"); |
| EXPECT_TRUE(session->unclaimed_pushed_streams_.end() != iter); |
| |
| // Delete the session. |
| session = NULL; |
| } |
| |
| TEST_F(SpdySessionSpdy2Test, FailedPing) { |
| session_deps_.host_resolver->set_synchronous_mode(true); |
| |
| MockConnect connect_data(SYNCHRONOUS, OK); |
| scoped_ptr<SpdyFrame> read_ping(ConstructSpdyPing(1)); |
| MockRead reads[] = { |
| CreateMockRead(*read_ping), |
| MockRead(SYNCHRONOUS, 0, 0) // EOF |
| }; |
| scoped_ptr<SpdyFrame> write_ping(ConstructSpdyPing(1)); |
| MockWrite writes[] = { |
| CreateMockWrite(*write_ping), |
| }; |
| StaticSocketDataProvider data( |
| reads, arraysize(reads), writes, arraysize(writes)); |
| data.set_connect_data(connect_data); |
| session_deps_.socket_factory->AddSocketDataProvider(&data); |
| |
| SSLSocketDataProvider ssl(SYNCHRONOUS, OK); |
| session_deps_.socket_factory->AddSSLSocketDataProvider(&ssl); |
| |
| CreateNetworkSession(); |
| |
| scoped_refptr<SpdySession> session = CreateInitializedSession(); |
| |
| base::WeakPtr<SpdyStream> spdy_stream1 = |
| CreateStreamSynchronously(session, test_url_, MEDIUM, BoundNetLog()); |
| ASSERT_TRUE(spdy_stream1.get() != NULL); |
| test::StreamDelegateSendImmediate delegate( |
| spdy_stream1, scoped_ptr<SpdyHeaderBlock>(), NULL); |
| spdy_stream1->SetDelegate(&delegate); |
| |
| session->set_connection_at_risk_of_loss_time(base::TimeDelta::FromSeconds(0)); |
| session->set_hung_interval(base::TimeDelta::FromSeconds(0)); |
| |
| // Send a PING frame. |
| session->WritePingFrame(1); |
| EXPECT_LT(0, session->pings_in_flight()); |
| EXPECT_GE(session->next_ping_id(), static_cast<uint32>(1)); |
| EXPECT_TRUE(session->check_ping_status_pending()); |
| |
| // Assert session is not closed. |
| EXPECT_FALSE(session->IsClosed()); |
| EXPECT_LT(0u, session->num_active_streams() + session->num_created_streams()); |
| EXPECT_TRUE(spdy_session_pool_->HasSession(pair_)); |
| |
| // We set last time we have received any data in 1 sec less than now. |
| // CheckPingStatus will trigger timeout because hung interval is zero. |
| base::TimeTicks now = base::TimeTicks::Now(); |
| session->last_activity_time_ = now - base::TimeDelta::FromSeconds(1); |
| session->CheckPingStatus(now); |
| |
| EXPECT_TRUE(session->IsClosed()); |
| EXPECT_EQ(0u, session->num_active_streams()); |
| EXPECT_EQ(0u, session->num_unclaimed_pushed_streams()); |
| EXPECT_FALSE(spdy_session_pool_->HasSession(pair_)); |
| |
| // Delete the first session. |
| session = NULL; |
| EXPECT_EQ(NULL, spdy_stream1.get()); |
| } |
| |
| TEST_F(SpdySessionSpdy2Test, CloseIdleSessions) { |
| MockConnect connect_data(SYNCHRONOUS, OK); |
| MockRead reads[] = { |
| MockRead(ASYNC, 0, 0) // EOF |
| }; |
| |
| StaticSocketDataProvider data(reads, arraysize(reads), NULL, 0); |
| data.set_connect_data(connect_data); |
| session_deps_.socket_factory->AddSocketDataProvider(&data); |
| |
| CreateNetworkSession(); |
| |
| // Set up session 1 |
| const std::string kTestHost1("http://www.a.com"); |
| HostPortPair test_host_port_pair1(kTestHost1, 80); |
| HostPortProxyPair pair1(test_host_port_pair1, ProxyServer::Direct()); |
| scoped_refptr<SpdySession> session1 = GetSession(pair1); |
| EXPECT_EQ( |
| OK, |
| InitializeSession( |
| http_session_.get(), session1.get(), test_host_port_pair1)); |
| GURL url1(kTestHost1); |
| base::WeakPtr<SpdyStream> spdy_stream1 = |
| CreateStreamSynchronously(session1, url1, MEDIUM, BoundNetLog()); |
| ASSERT_TRUE(spdy_stream1.get() != NULL); |
| |
| // Set up session 2 |
| session_deps_.socket_factory->AddSocketDataProvider(&data); |
| const std::string kTestHost2("http://www.b.com"); |
| HostPortPair test_host_port_pair2(kTestHost2, 80); |
| HostPortProxyPair pair2(test_host_port_pair2, ProxyServer::Direct()); |
| scoped_refptr<SpdySession> session2 = GetSession(pair2); |
| EXPECT_EQ( |
| OK, |
| InitializeSession( |
| http_session_.get(), session2.get(), test_host_port_pair2)); |
| GURL url2(kTestHost2); |
| base::WeakPtr<SpdyStream> spdy_stream2 = |
| CreateStreamSynchronously(session2, url2, MEDIUM, BoundNetLog()); |
| ASSERT_TRUE(spdy_stream2.get() != NULL); |
| |
| // Set up session 3 |
| session_deps_.socket_factory->AddSocketDataProvider(&data); |
| const std::string kTestHost3("http://www.c.com"); |
| HostPortPair test_host_port_pair3(kTestHost3, 80); |
| HostPortProxyPair pair3(test_host_port_pair3, ProxyServer::Direct()); |
| scoped_refptr<SpdySession> session3 = GetSession(pair3); |
| EXPECT_EQ( |
| OK, |
| InitializeSession( |
| http_session_.get(), session3.get(), test_host_port_pair3)); |
| GURL url3(kTestHost3); |
| base::WeakPtr<SpdyStream> spdy_stream3 = |
| CreateStreamSynchronously(session3, url3, MEDIUM, BoundNetLog()); |
| ASSERT_TRUE(spdy_stream3.get() != NULL); |
| |
| // All sessions are active and not closed |
| EXPECT_TRUE(session1->is_active()); |
| EXPECT_FALSE(session1->IsClosed()); |
| EXPECT_TRUE(session2->is_active()); |
| EXPECT_FALSE(session2->IsClosed()); |
| EXPECT_TRUE(session3->is_active()); |
| EXPECT_FALSE(session3->IsClosed()); |
| |
| // Should not do anything, all are active |
| spdy_session_pool_->CloseIdleSessions(); |
| EXPECT_TRUE(session1->is_active()); |
| EXPECT_FALSE(session1->IsClosed()); |
| EXPECT_TRUE(session2->is_active()); |
| EXPECT_FALSE(session2->IsClosed()); |
| EXPECT_TRUE(session3->is_active()); |
| EXPECT_FALSE(session3->IsClosed()); |
| |
| // Make sessions 1 and 3 inactive, but keep them open. |
| // Session 2 still open and active |
| session1->CloseCreatedStream(spdy_stream1, OK); |
| EXPECT_EQ(NULL, spdy_stream1.get()); |
| session3->CloseCreatedStream(spdy_stream3, OK); |
| EXPECT_EQ(NULL, spdy_stream3.get()); |
| EXPECT_FALSE(session1->is_active()); |
| EXPECT_FALSE(session1->IsClosed()); |
| EXPECT_TRUE(session2->is_active()); |
| EXPECT_FALSE(session2->IsClosed()); |
| EXPECT_FALSE(session3->is_active()); |
| EXPECT_FALSE(session3->IsClosed()); |
| |
| // Should close session 1 and 3, 2 should be left open |
| spdy_session_pool_->CloseIdleSessions(); |
| EXPECT_FALSE(session1->is_active()); |
| EXPECT_TRUE(session1->IsClosed()); |
| EXPECT_TRUE(session2->is_active()); |
| EXPECT_FALSE(session2->IsClosed()); |
| EXPECT_FALSE(session3->is_active()); |
| EXPECT_TRUE(session3->IsClosed()); |
| |
| // Should not do anything |
| spdy_session_pool_->CloseIdleSessions(); |
| EXPECT_TRUE(session2->is_active()); |
| EXPECT_FALSE(session2->IsClosed()); |
| |
| // Make 2 not active |
| session2->CloseCreatedStream(spdy_stream2, OK); |
| EXPECT_EQ(NULL, spdy_stream2.get()); |
| EXPECT_FALSE(session2->is_active()); |
| EXPECT_FALSE(session2->IsClosed()); |
| |
| // This should close session 2 |
| spdy_session_pool_->CloseIdleSessions(); |
| EXPECT_FALSE(session2->is_active()); |
| EXPECT_TRUE(session2->IsClosed()); |
| } |
| |
| // Start with max concurrent streams set to 1. Request two streams. Receive a |
| // settings frame setting max concurrent streams to 2. Have the callback |
| // release the stream, which releases its reference (the last) to the session. |
| // Make sure nothing blows up. |
| // http://crbug.com/57331 |
| TEST_F(SpdySessionSpdy2Test, OnSettings) { |
| session_deps_.host_resolver->set_synchronous_mode(true); |
| |
| SettingsMap new_settings; |
| const SpdySettingsIds kSpdySettingsIds1 = SETTINGS_MAX_CONCURRENT_STREAMS; |
| const uint32 max_concurrent_streams = 2; |
| new_settings[kSpdySettingsIds1] = |
| SettingsFlagsAndValue(SETTINGS_FLAG_NONE, max_concurrent_streams); |
| |
| // Set up the socket so we read a SETTINGS frame that raises max concurrent |
| // streams to 2. |
| MockConnect connect_data(SYNCHRONOUS, OK); |
| scoped_ptr<SpdyFrame> settings_frame(ConstructSpdySettings(new_settings)); |
| MockRead reads[] = { |
| CreateMockRead(*settings_frame), |
| MockRead(SYNCHRONOUS, 0, 0) // EOF |
| }; |
| |
| StaticSocketDataProvider data(reads, arraysize(reads), NULL, 0); |
| data.set_connect_data(connect_data); |
| session_deps_.socket_factory->AddSocketDataProvider(&data); |
| |
| SSLSocketDataProvider ssl(SYNCHRONOUS, OK); |
| session_deps_.socket_factory->AddSSLSocketDataProvider(&ssl); |
| |
| CreateNetworkSession(); |
| |
| // Initialize the SpdySetting with 1 max concurrent streams. |
| spdy_session_pool_->http_server_properties()->SetSpdySetting( |
| test_host_port_pair_, |
| kSpdySettingsIds1, |
| SETTINGS_FLAG_PLEASE_PERSIST, |
| 1); |
| |
| scoped_refptr<SpdySession> session = CreateInitializedSession(); |
| |
| // Create 2 streams. First will succeed. Second will be pending. |
| base::WeakPtr<SpdyStream> spdy_stream1 = |
| CreateStreamSynchronously(session, test_url_, MEDIUM, BoundNetLog()); |
| ASSERT_TRUE(spdy_stream1.get() != NULL); |
| |
| StreamReleaserCallback stream_releaser; |
| SpdyStreamRequest request; |
| ASSERT_EQ(ERR_IO_PENDING, |
| request.StartRequest(session, test_url_, MEDIUM, |
| BoundNetLog(), |
| stream_releaser.MakeCallback(&request))); |
| |
| EXPECT_EQ(OK, stream_releaser.WaitForResult()); |
| } |
| |
| // Start with max concurrent streams set to 1 (that is persisted). Receive a |
| // settings frame setting max concurrent streams to 2 and which also clears the |
| // persisted data. Verify that persisted data is correct. |
| TEST_F(SpdySessionSpdy2Test, ClearSettings) { |
| session_deps_.host_resolver->set_synchronous_mode(true); |
| |
| SettingsMap new_settings; |
| const SpdySettingsIds kSpdySettingsIds1 = SETTINGS_MAX_CONCURRENT_STREAMS; |
| const uint32 max_concurrent_streams = 2; |
| new_settings[kSpdySettingsIds1] = |
| SettingsFlagsAndValue(SETTINGS_FLAG_NONE, max_concurrent_streams); |
| |
| // Set up the socket so we read a SETTINGS frame that raises max concurrent |
| // streams to 2 and clears previously persisted data. |
| MockConnect connect_data(SYNCHRONOUS, OK); |
| scoped_ptr<SpdyFrame> settings_frame(ConstructSpdySettings(new_settings)); |
| uint8 flags = SETTINGS_FLAG_CLEAR_PREVIOUSLY_PERSISTED_SETTINGS; |
| test::SetFrameFlags(settings_frame.get(), flags, kSpdyVersion3); |
| MockRead reads[] = { |
| CreateMockRead(*settings_frame), |
| MockRead(SYNCHRONOUS, 0, 0) // EOF |
| }; |
| |
| StaticSocketDataProvider data(reads, arraysize(reads), NULL, 0); |
| data.set_connect_data(connect_data); |
| session_deps_.socket_factory->AddSocketDataProvider(&data); |
| |
| SSLSocketDataProvider ssl(SYNCHRONOUS, OK); |
| session_deps_.socket_factory->AddSSLSocketDataProvider(&ssl); |
| |
| CreateNetworkSession(); |
| |
| // Initialize the SpdySetting with 1 max concurrent streams. |
| spdy_session_pool_->http_server_properties()->SetSpdySetting( |
| test_host_port_pair_, |
| kSpdySettingsIds1, |
| SETTINGS_FLAG_PLEASE_PERSIST, |
| 1); |
| |
| EXPECT_EQ(1u, spdy_session_pool_->http_server_properties()->GetSpdySettings( |
| test_host_port_pair_).size()); |
| |
| scoped_refptr<SpdySession> session = CreateInitializedSession(); |
| |
| // Create 2 streams. First will succeed. Second will be pending. |
| base::WeakPtr<SpdyStream> spdy_stream1 = |
| CreateStreamSynchronously(session, test_url_, MEDIUM, BoundNetLog()); |
| ASSERT_TRUE(spdy_stream1.get() != NULL); |
| |
| StreamReleaserCallback stream_releaser; |
| SpdyStreamRequest request; |
| ASSERT_EQ(ERR_IO_PENDING, |
| request.StartRequest(session, test_url_, MEDIUM, |
| BoundNetLog(), |
| stream_releaser.MakeCallback(&request))); |
| |
| EXPECT_EQ(OK, stream_releaser.WaitForResult()); |
| |
| // Make sure that persisted data is cleared. |
| EXPECT_EQ(0u, spdy_session_pool_->http_server_properties()->GetSpdySettings( |
| test_host_port_pair_).size()); |
| |
| // Make sure session's max_concurrent_streams is 2. |
| EXPECT_EQ(2u, session->max_concurrent_streams()); |
| |
| session = NULL; |
| } |
| |
| // Start with max concurrent streams set to 1. Request two streams. When the |
| // first completes, have the callback close itself, which should trigger the |
| // second stream creation. Then cancel that one immediately. Don't crash. |
| // http://crbug.com/63532 |
| TEST_F(SpdySessionSpdy2Test, CancelPendingCreateStream) { |
| session_deps_.host_resolver->set_synchronous_mode(true); |
| |
| MockRead reads[] = { |
| MockRead(SYNCHRONOUS, ERR_IO_PENDING) // Stall forever. |
| }; |
| |
| StaticSocketDataProvider data(reads, arraysize(reads), NULL, 0); |
| MockConnect connect_data(SYNCHRONOUS, OK); |
| |
| data.set_connect_data(connect_data); |
| session_deps_.socket_factory->AddSocketDataProvider(&data); |
| |
| SSLSocketDataProvider ssl(SYNCHRONOUS, OK); |
| session_deps_.socket_factory->AddSSLSocketDataProvider(&ssl); |
| |
| CreateNetworkSession(); |
| |
| // Initialize the SpdySetting with 1 max concurrent streams. |
| spdy_session_pool_->http_server_properties()->SetSpdySetting( |
| test_host_port_pair_, |
| SETTINGS_MAX_CONCURRENT_STREAMS, |
| SETTINGS_FLAG_PLEASE_PERSIST, |
| 1); |
| |
| scoped_refptr<SpdySession> session = CreateInitializedSession(); |
| |
| // Create 2 streams. First will succeed. Second will be pending. |
| base::WeakPtr<SpdyStream> spdy_stream1 = |
| CreateStreamSynchronously(session, test_url_, MEDIUM, BoundNetLog()); |
| ASSERT_TRUE(spdy_stream1.get() != NULL); |
| |
| // Use scoped_ptr to let us invalidate the memory when we want to, to trigger |
| // a valgrind error if the callback is invoked when it's not supposed to be. |
| scoped_ptr<TestCompletionCallback> callback(new TestCompletionCallback); |
| |
| SpdyStreamRequest request; |
| ASSERT_EQ(ERR_IO_PENDING, |
| request.StartRequest(session, test_url_, MEDIUM, |
| BoundNetLog(), |
| callback->callback())); |
| |
| // Release the first one, this will allow the second to be created. |
| spdy_stream1->Cancel(); |
| EXPECT_EQ(NULL, spdy_stream1.get()); |
| |
| request.CancelRequest(); |
| callback.reset(); |
| |
| // Should not crash when running the pending callback. |
| MessageLoop::current()->RunUntilIdle(); |
| } |
| |
| TEST_F(SpdySessionSpdy2Test, SendInitialSettingsOnNewSession) { |
| session_deps_.host_resolver->set_synchronous_mode(true); |
| |
| MockRead reads[] = { |
| MockRead(SYNCHRONOUS, ERR_IO_PENDING) // Stall forever. |
| }; |
| |
| SettingsMap settings; |
| const SpdySettingsIds kSpdySettingsIds1 = SETTINGS_MAX_CONCURRENT_STREAMS; |
| settings[kSpdySettingsIds1] = |
| SettingsFlagsAndValue(SETTINGS_FLAG_NONE, kMaxConcurrentPushedStreams); |
| MockConnect connect_data(SYNCHRONOUS, OK); |
| scoped_ptr<SpdyFrame> settings_frame(ConstructSpdySettings(settings)); |
| MockWrite writes[] = { |
| CreateMockWrite(*settings_frame), |
| }; |
| |
| StaticSocketDataProvider data( |
| reads, arraysize(reads), writes, arraysize(writes)); |
| data.set_connect_data(connect_data); |
| session_deps_.socket_factory->AddSocketDataProvider(&data); |
| |
| SSLSocketDataProvider ssl(SYNCHRONOUS, OK); |
| session_deps_.socket_factory->AddSSLSocketDataProvider(&ssl); |
| |
| CreateNetworkSession(); |
| |
| SpdySessionPoolPeer pool_peer(spdy_session_pool_); |
| pool_peer.EnableSendingInitialSettings(true); |
| |
| scoped_refptr<SpdySession> session = CreateInitializedSession(); |
| |
| MessageLoop::current()->RunUntilIdle(); |
| EXPECT_TRUE(data.at_write_eof()); |
| } |
| |
| TEST_F(SpdySessionSpdy2Test, SendSettingsOnNewSession) { |
| session_deps_.host_resolver->set_synchronous_mode(true); |
| |
| MockRead reads[] = { |
| MockRead(SYNCHRONOUS, ERR_IO_PENDING) // Stall forever. |
| }; |
| |
| // Create the bogus setting that we want to verify is sent out. |
| // Note that it will be marked as SETTINGS_FLAG_PERSISTED when sent out. But |
| // to persist it into the HttpServerProperties, we need to mark as |
| // SETTINGS_FLAG_PLEASE_PERSIST. |
| SettingsMap settings; |
| const SpdySettingsIds kSpdySettingsIds1 = SETTINGS_UPLOAD_BANDWIDTH; |
| const uint32 kBogusSettingValue = 0xCDCD; |
| settings[kSpdySettingsIds1] = |
| SettingsFlagsAndValue(SETTINGS_FLAG_PERSISTED, kBogusSettingValue); |
| MockConnect connect_data(SYNCHRONOUS, OK); |
| scoped_ptr<SpdyFrame> settings_frame(ConstructSpdySettings(settings)); |
| MockWrite writes[] = { |
| CreateMockWrite(*settings_frame), |
| }; |
| |
| StaticSocketDataProvider data( |
| reads, arraysize(reads), writes, arraysize(writes)); |
| data.set_connect_data(connect_data); |
| session_deps_.socket_factory->AddSocketDataProvider(&data); |
| |
| SSLSocketDataProvider ssl(SYNCHRONOUS, OK); |
| session_deps_.socket_factory->AddSSLSocketDataProvider(&ssl); |
| |
| CreateNetworkSession(); |
| |
| spdy_session_pool_->http_server_properties()->SetSpdySetting( |
| test_host_port_pair_, |
| kSpdySettingsIds1, |
| SETTINGS_FLAG_PLEASE_PERSIST, |
| kBogusSettingValue); |
| |
| scoped_refptr<SpdySession> session = CreateInitializedSession(); |
| |
| MessageLoop::current()->RunUntilIdle(); |
| EXPECT_TRUE(data.at_write_eof()); |
| } |
| |
| namespace { |
| |
| // Specifies the style for closing the connection. |
| enum SpdyPoolCloseSessionsType { |
| SPDY_POOL_CLOSE_SESSIONS_MANUALLY, |
| SPDY_POOL_CLOSE_CURRENT_SESSIONS, |
| SPDY_POOL_CLOSE_IDLE_SESSIONS, |
| }; |
| |
| // Initialize the SpdySession with socket. |
| void IPPoolingInitializedSession( |
| const std::string& group_name, |
| const scoped_refptr<TransportSocketParams>& transport_params, |
| HttpNetworkSession* http_session, |
| SpdySession* session) { |
| scoped_ptr<ClientSocketHandle> connection(new ClientSocketHandle); |
| EXPECT_EQ(OK, connection->Init(group_name, |
| transport_params, MEDIUM, CompletionCallback(), |
| http_session->GetTransportSocketPool( |
| HttpNetworkSession::NORMAL_SOCKET_POOL), |
| BoundNetLog())); |
| EXPECT_EQ(OK, session->InitializeWithSocket(connection.release(), false, OK)); |
| } |
| |
| // This test has three variants, one for each style of closing the connection. |
| // If |clean_via_close_current_sessions| is SPDY_POOL_CLOSE_SESSIONS_MANUALLY, |
| // the sessions are closed manually, calling SpdySessionPool::Remove() directly. |
| // If |clean_via_close_current_sessions| is SPDY_POOL_CLOSE_CURRENT_SESSIONS, |
| // sessions are closed with SpdySessionPool::CloseCurrentSessions(). |
| // If |clean_via_close_current_sessions| is SPDY_POOL_CLOSE_IDLE_SESSIONS, |
| // sessions are closed with SpdySessionPool::CloseIdleSessions(). |
| void IPPoolingTest(SpdyPoolCloseSessionsType close_sessions_type) { |
| const int kTestPort = 80; |
| struct TestHosts { |
| std::string url; |
| std::string name; |
| std::string iplist; |
| HostPortProxyPair pair; |
| AddressList addresses; |
| } test_hosts[] = { |
| { "http:://www.foo.com", |
| "www.foo.com", |
| "192.0.2.33,192.168.0.1,192.168.0.5" |
| }, |
| { "http://js.foo.com", |
| "js.foo.com", |
| "192.168.0.2,192.168.0.3,192.168.0.5,192.0.2.33" |
| }, |
| { "http://images.foo.com", |
| "images.foo.com", |
| "192.168.0.4,192.168.0.3" |
| }, |
| }; |
| |
| SpdySessionDependencies session_deps(kProtoSPDY2); |
| session_deps.host_resolver->set_synchronous_mode(true); |
| for (size_t i = 0; i < ARRAYSIZE_UNSAFE(test_hosts); i++) { |
| session_deps.host_resolver->rules()->AddIPLiteralRule( |
| test_hosts[i].name, test_hosts[i].iplist, std::string()); |
| |
| // This test requires that the HostResolver cache be populated. Normal |
| // code would have done this already, but we do it manually. |
| HostResolver::RequestInfo info(HostPortPair(test_hosts[i].name, kTestPort)); |
| session_deps.host_resolver->Resolve( |
| info, &test_hosts[i].addresses, CompletionCallback(), NULL, |
| BoundNetLog()); |
| |
| // Setup a HostPortProxyPair |
| test_hosts[i].pair = HostPortProxyPair( |
| HostPortPair(test_hosts[i].name, kTestPort), ProxyServer::Direct()); |
| } |
| |
| MockConnect connect_data(SYNCHRONOUS, OK); |
| MockRead reads[] = { |
| MockRead(SYNCHRONOUS, ERR_IO_PENDING) // Stall forever. |
| }; |
| |
| StaticSocketDataProvider data(reads, arraysize(reads), NULL, 0); |
| data.set_connect_data(connect_data); |
| session_deps.socket_factory->AddSocketDataProvider(&data); |
| |
| SSLSocketDataProvider ssl(SYNCHRONOUS, OK); |
| session_deps.socket_factory->AddSSLSocketDataProvider(&ssl); |
| |
| scoped_refptr<HttpNetworkSession> http_session( |
| SpdySessionDependencies::SpdyCreateSession(&session_deps)); |
| |
| // Setup the first session to the first host. |
| SpdySessionPool* spdy_session_pool(http_session->spdy_session_pool()); |
| EXPECT_FALSE(spdy_session_pool->HasSession(test_hosts[0].pair)); |
| scoped_refptr<SpdySession> session = |
| spdy_session_pool->Get(test_hosts[0].pair, BoundNetLog()); |
| EXPECT_TRUE(spdy_session_pool->HasSession(test_hosts[0].pair)); |
| |
| HostPortPair test_host_port_pair(test_hosts[0].name, kTestPort); |
| |
| // Initialize session for the first host. |
| scoped_refptr<TransportSocketParams> transport_params( |
| new TransportSocketParams(test_host_port_pair, |
| MEDIUM, |
| false, |
| false, |
| OnHostResolutionCallback())); |
| IPPoolingInitializedSession(test_host_port_pair.ToString(), |
| transport_params, http_session, session); |
| |
| // TODO(rtenneti): MockClientSocket::GetPeerAddress return's 0 as the port |
| // number. Fix it to return port 80 and then use GetPeerAddress to AddAlias. |
| SpdySessionPoolPeer pool_peer(spdy_session_pool); |
| pool_peer.AddAlias(test_hosts[0].addresses.front(), test_hosts[0].pair); |
| |
| // Flush the SpdySession::OnReadComplete() task. |
| MessageLoop::current()->RunUntilIdle(); |
| |
| // The third host has no overlap with the first, so it can't pool IPs. |
| EXPECT_FALSE(spdy_session_pool->HasSession(test_hosts[2].pair)); |
| |
| // The second host overlaps with the first, and should IP pool. |
| EXPECT_TRUE(spdy_session_pool->HasSession(test_hosts[1].pair)); |
| |
| // Verify that the second host, through a proxy, won't share the IP. |
| HostPortProxyPair proxy_pair(test_hosts[1].pair.first, |
| ProxyServer::FromPacString("HTTP http://proxy.foo.com/")); |
| EXPECT_FALSE(spdy_session_pool->HasSession(proxy_pair)); |
| |
| // Overlap between 2 and 3 does is not transitive to 1. |
| EXPECT_FALSE(spdy_session_pool->HasSession(test_hosts[2].pair)); |
| |
| // Create a new session to host 2. |
| scoped_refptr<SpdySession> session2 = |
| spdy_session_pool->Get(test_hosts[2].pair, BoundNetLog()); |
| |
| // Verify that we have sessions for everything. |
| EXPECT_TRUE(spdy_session_pool->HasSession(test_hosts[0].pair)); |
| EXPECT_TRUE(spdy_session_pool->HasSession(test_hosts[1].pair)); |
| EXPECT_TRUE(spdy_session_pool->HasSession(test_hosts[2].pair)); |
| |
| // Initialize session for host 2. |
| session_deps.socket_factory->AddSocketDataProvider(&data); |
| IPPoolingInitializedSession(test_hosts[2].pair.first.ToString(), |
| transport_params, http_session, session2); |
| |
| // Grab the session to host 1 and verify that it is the same session |
| // we got with host 0, and that is a different than host 2's session. |
| scoped_refptr<SpdySession> session1 = |
| spdy_session_pool->Get(test_hosts[1].pair, BoundNetLog()); |
| EXPECT_EQ(session.get(), session1.get()); |
| EXPECT_NE(session2.get(), session1.get()); |
| |
| // Initialize session for host 1. |
| session_deps.socket_factory->AddSocketDataProvider(&data); |
| IPPoolingInitializedSession(test_hosts[2].pair.first.ToString(), |
| transport_params, http_session, session2); |
| |
| // Remove the aliases and observe that we still have a session for host1. |
| pool_peer.RemoveAliases(test_hosts[0].pair); |
| pool_peer.RemoveAliases(test_hosts[1].pair); |
| EXPECT_TRUE(spdy_session_pool->HasSession(test_hosts[1].pair)); |
| |
| // Expire the host cache |
| session_deps.host_resolver->GetHostCache()->clear(); |
| EXPECT_TRUE(spdy_session_pool->HasSession(test_hosts[1].pair)); |
| |
| // Cleanup the sessions. |
| switch (close_sessions_type) { |
| case SPDY_POOL_CLOSE_SESSIONS_MANUALLY: |
| spdy_session_pool->Remove(session); |
| session = NULL; |
| spdy_session_pool->Remove(session2); |
| session2 = NULL; |
| break; |
| case SPDY_POOL_CLOSE_CURRENT_SESSIONS: |
| spdy_session_pool->CloseCurrentSessions(net::ERR_ABORTED); |
| break; |
| case SPDY_POOL_CLOSE_IDLE_SESSIONS: |
| GURL url(test_hosts[0].url); |
| base::WeakPtr<SpdyStream> spdy_stream = |
| CreateStreamSynchronously(session, url, MEDIUM, BoundNetLog()); |
| GURL url1(test_hosts[1].url); |
| base::WeakPtr<SpdyStream> spdy_stream1 = |
| CreateStreamSynchronously(session1, url1, MEDIUM, BoundNetLog()); |
| GURL url2(test_hosts[2].url); |
| base::WeakPtr<SpdyStream> spdy_stream2 = |
| CreateStreamSynchronously(session2, url2, MEDIUM, BoundNetLog()); |
| |
| // Close streams to make spdy_session and spdy_session1 inactive. |
| session->CloseCreatedStream(spdy_stream, OK); |
| EXPECT_EQ(NULL, spdy_stream.get()); |
| session1->CloseCreatedStream(spdy_stream1, OK); |
| EXPECT_EQ(NULL, spdy_stream1.get()); |
| |
| // Check spdy_session and spdy_session1 are not closed. |
| EXPECT_FALSE(session->is_active()); |
| EXPECT_FALSE(session->IsClosed()); |
| EXPECT_FALSE(session1->is_active()); |
| EXPECT_FALSE(session1->IsClosed()); |
| EXPECT_TRUE(session2->is_active()); |
| EXPECT_FALSE(session2->IsClosed()); |
| |
| // Test that calling CloseIdleSessions, does not cause a crash. |
| // http://crbug.com/181400 |
| spdy_session_pool->CloseIdleSessions(); |
| |
| // Verify spdy_session and spdy_session1 are closed. |
| EXPECT_FALSE(session->is_active()); |
| EXPECT_TRUE(session->IsClosed()); |
| EXPECT_FALSE(session1->is_active()); |
| EXPECT_TRUE(session1->IsClosed()); |
| EXPECT_TRUE(session2->is_active()); |
| EXPECT_FALSE(session2->IsClosed()); |
| |
| spdy_stream2->Cancel(); |
| EXPECT_EQ(NULL, spdy_stream2.get()); |
| spdy_session_pool->Remove(session2); |
| session2 = NULL; |
| break; |
| } |
| |
| // Verify that the map is all cleaned up. |
| EXPECT_FALSE(spdy_session_pool->HasSession(test_hosts[0].pair)); |
| EXPECT_FALSE(spdy_session_pool->HasSession(test_hosts[1].pair)); |
| EXPECT_FALSE(spdy_session_pool->HasSession(test_hosts[2].pair)); |
| } |
| |
| } // namespace |
| |
| TEST_F(SpdySessionSpdy2Test, IPPooling) { |
| IPPoolingTest(SPDY_POOL_CLOSE_SESSIONS_MANUALLY); |
| } |
| |
| TEST_F(SpdySessionSpdy2Test, IPPoolingCloseCurrentSessions) { |
| IPPoolingTest(SPDY_POOL_CLOSE_CURRENT_SESSIONS); |
| } |
| |
| TEST_F(SpdySessionSpdy2Test, IPPoolingCloseIdleSessions) { |
| IPPoolingTest(SPDY_POOL_CLOSE_IDLE_SESSIONS); |
| } |
| |
| TEST_F(SpdySessionSpdy2Test, ClearSettingsStorageOnIPAddressChanged) { |
| CreateNetworkSession(); |
| |
| HttpServerProperties* test_http_server_properties = |
| spdy_session_pool_->http_server_properties(); |
| SettingsFlagsAndValue flags_and_value1(SETTINGS_FLAG_PLEASE_PERSIST, 2); |
| test_http_server_properties->SetSpdySetting( |
| test_host_port_pair_, |
| SETTINGS_MAX_CONCURRENT_STREAMS, |
| SETTINGS_FLAG_PLEASE_PERSIST, |
| 2); |
| EXPECT_NE(0u, test_http_server_properties->GetSpdySettings( |
| test_host_port_pair_).size()); |
| spdy_session_pool_->OnIPAddressChanged(); |
| EXPECT_EQ(0u, test_http_server_properties->GetSpdySettings( |
| test_host_port_pair_).size()); |
| } |
| |
| TEST_F(SpdySessionSpdy2Test, CloseSessionOnError) { |
| session_deps_.host_resolver->set_synchronous_mode(true); |
| |
| MockConnect connect_data(SYNCHRONOUS, OK); |
| scoped_ptr<SpdyFrame> goaway(ConstructSpdyGoAway()); |
| MockRead reads[] = { |
| CreateMockRead(*goaway), |
| MockRead(SYNCHRONOUS, 0, 0) // EOF |
| }; |
| |
| StaticSocketDataProvider data(reads, arraysize(reads), NULL, 0); |
| data.set_connect_data(connect_data); |
| session_deps_.socket_factory->AddSocketDataProvider(&data); |
| |
| SSLSocketDataProvider ssl(SYNCHRONOUS, OK); |
| session_deps_.socket_factory->AddSSLSocketDataProvider(&ssl); |
| |
| CreateNetworkSession(); |
| |
| CapturingBoundNetLog log; |
| scoped_refptr<SpdySession> session = |
| spdy_session_pool_->Get(pair_, log.bound()); |
| EXPECT_TRUE(spdy_session_pool_->HasSession(pair_)); |
| |
| InitializeSession(http_session_.get(), session.get(), test_host_port_pair_); |
| |
| // Flush the SpdySession::OnReadComplete() task. |
| MessageLoop::current()->RunUntilIdle(); |
| |
| EXPECT_FALSE(spdy_session_pool_->HasSession(pair_)); |
| |
| // Check that the NetLog was filled reasonably. |
| net::CapturingNetLog::CapturedEntryList entries; |
| log.GetEntries(&entries); |
| EXPECT_LT(0u, entries.size()); |
| |
| // Check that we logged SPDY_SESSION_CLOSE correctly. |
| int pos = net::ExpectLogContainsSomewhere( |
| entries, 0, |
| net::NetLog::TYPE_SPDY_SESSION_CLOSE, |
| net::NetLog::PHASE_NONE); |
| |
| CapturingNetLog::CapturedEntry entry = entries[pos]; |
| int error_code = 0; |
| ASSERT_TRUE(entry.GetNetErrorCode(&error_code)); |
| EXPECT_EQ(ERR_CONNECTION_CLOSED, error_code); |
| } |
| |
| TEST_F(SpdySessionSpdy2Test, OutOfOrderSynStreams) { |
| // Construct the request. |
| MockConnect connect_data(SYNCHRONOUS, OK); |
| scoped_ptr<SpdyFrame> req1(ConstructSpdyGet(NULL, 0, false, 1, HIGHEST)); |
| scoped_ptr<SpdyFrame> req2(ConstructSpdyGet(NULL, 0, false, 3, LOWEST)); |
| MockWrite writes[] = { |
| CreateMockWrite(*req1, 2), |
| CreateMockWrite(*req2, 1), |
| }; |
| |
| scoped_ptr<SpdyFrame> resp1(ConstructSpdyGetSynReply(NULL, 0, 1)); |
| scoped_ptr<SpdyFrame> body1(ConstructSpdyBodyFrame(1, true)); |
| scoped_ptr<SpdyFrame> resp2(ConstructSpdyGetSynReply(NULL, 0, 3)); |
| scoped_ptr<SpdyFrame> body2(ConstructSpdyBodyFrame(3, true)); |
| MockRead reads[] = { |
| CreateMockRead(*resp1, 3), |
| CreateMockRead(*body1, 4), |
| CreateMockRead(*resp2, 5), |
| CreateMockRead(*body2, 6), |
| MockRead(ASYNC, 0, 7) // EOF |
| }; |
| |
| session_deps_.host_resolver->set_synchronous_mode(true); |
| |
| StaticSocketDataProvider data(reads, arraysize(reads), |
| writes, arraysize(writes)); |
| data.set_connect_data(connect_data); |
| session_deps_.socket_factory->AddSocketDataProvider(&data); |
| |
| SSLSocketDataProvider ssl(SYNCHRONOUS, OK); |
| session_deps_.socket_factory->AddSSLSocketDataProvider(&ssl); |
| |
| CreateNetworkSession(); |
| |
| scoped_refptr<SpdySession> session = CreateInitializedSession(); |
| |
| GURL url("http://www.google.com"); |
| |
| base::WeakPtr<SpdyStream> spdy_stream1 = |
| CreateStreamSynchronously(session, url, LOWEST, BoundNetLog()); |
| ASSERT_TRUE(spdy_stream1.get() != NULL); |
| EXPECT_EQ(0u, spdy_stream1->stream_id()); |
| test::StreamDelegateDoNothing delegate1(spdy_stream1); |
| spdy_stream1->SetDelegate(&delegate1); |
| |
| base::WeakPtr<SpdyStream> spdy_stream2 = |
| CreateStreamSynchronously(session, url, HIGHEST, BoundNetLog()); |
| ASSERT_TRUE(spdy_stream2.get() != NULL); |
| EXPECT_EQ(0u, spdy_stream2->stream_id()); |
| test::StreamDelegateDoNothing delegate2(spdy_stream2); |
| spdy_stream2->SetDelegate(&delegate2); |
| |
| scoped_ptr<SpdyHeaderBlock> headers(new SpdyHeaderBlock); |
| (*headers)["method"] = "GET"; |
| (*headers)["scheme"] = url.scheme(); |
| (*headers)["host"] = url.host(); |
| (*headers)["url"] = url.path(); |
| (*headers)["version"] = "HTTP/1.1"; |
| scoped_ptr<SpdyHeaderBlock> headers2(new SpdyHeaderBlock); |
| *headers2 = *headers; |
| |
| spdy_stream1->set_spdy_headers(headers.Pass()); |
| EXPECT_TRUE(spdy_stream1->HasUrl()); |
| |
| spdy_stream2->set_spdy_headers(headers2.Pass()); |
| EXPECT_TRUE(spdy_stream2->HasUrl()); |
| |
| spdy_stream1->SendRequest(false); |
| spdy_stream2->SendRequest(false); |
| MessageLoop::current()->RunUntilIdle(); |
| |
| EXPECT_EQ(NULL, spdy_stream1.get()); |
| EXPECT_EQ(NULL, spdy_stream2.get()); |
| EXPECT_EQ(3u, delegate1.stream_id()); |
| EXPECT_EQ(1u, delegate2.stream_id()); |
| } |
| |
| TEST_F(SpdySessionSpdy2Test, CancelStream) { |
| MockConnect connect_data(SYNCHRONOUS, OK); |
| // Request 1, at HIGHEST priority, will be cancelled before it writes data. |
| // Request 2, at LOWEST priority, will be a full request and will be id 1. |
| scoped_ptr<SpdyFrame> req2(ConstructSpdyGet(NULL, 0, false, 1, LOWEST)); |
| MockWrite writes[] = { |
| CreateMockWrite(*req2, 0), |
| }; |
| |
| scoped_ptr<SpdyFrame> resp2(ConstructSpdyGetSynReply(NULL, 0, 1)); |
| scoped_ptr<SpdyFrame> body2(ConstructSpdyBodyFrame(1, true)); |
| MockRead reads[] = { |
| CreateMockRead(*resp2, 1), |
| CreateMockRead(*body2, 2), |
| MockRead(ASYNC, 0, 3) // EOF |
| }; |
| |
| session_deps_.host_resolver->set_synchronous_mode(true); |
| |
| DeterministicSocketData data(reads, arraysize(reads), |
| writes, arraysize(writes)); |
| data.set_connect_data(connect_data); |
| session_deps_.deterministic_socket_factory->AddSocketDataProvider(&data); |
| |
| SSLSocketDataProvider ssl(SYNCHRONOUS, OK); |
| session_deps_.deterministic_socket_factory->AddSSLSocketDataProvider(&ssl); |
| |
| CreateDeterministicNetworkSession(); |
| |
| scoped_refptr<SpdySession> session = CreateInitializedSession(); |
| |
| GURL url1("http://www.google.com"); |
| base::WeakPtr<SpdyStream> spdy_stream1 = |
| CreateStreamSynchronously(session, url1, HIGHEST, BoundNetLog()); |
| ASSERT_TRUE(spdy_stream1.get() != NULL); |
| EXPECT_EQ(0u, spdy_stream1->stream_id()); |
| test::StreamDelegateDoNothing delegate1(spdy_stream1); |
| spdy_stream1->SetDelegate(&delegate1); |
| |
| GURL url2("http://www.google.com"); |
| base::WeakPtr<SpdyStream> spdy_stream2 = |
| CreateStreamSynchronously(session, url2, LOWEST, BoundNetLog()); |
| ASSERT_TRUE(spdy_stream2.get() != NULL); |
| EXPECT_EQ(0u, spdy_stream2->stream_id()); |
| test::StreamDelegateDoNothing delegate2(spdy_stream2); |
| spdy_stream2->SetDelegate(&delegate2); |
| |
| scoped_ptr<SpdyHeaderBlock> headers(new SpdyHeaderBlock); |
| (*headers)["method"] = "GET"; |
| (*headers)["scheme"] = url1.scheme(); |
| (*headers)["host"] = url1.host(); |
| (*headers)["url"] = url1.path(); |
| (*headers)["version"] = "HTTP/1.1"; |
| scoped_ptr<SpdyHeaderBlock> headers2(new SpdyHeaderBlock); |
| *headers2 = *headers; |
| |
| spdy_stream1->set_spdy_headers(headers.Pass()); |
| EXPECT_TRUE(spdy_stream1->HasUrl()); |
| |
| spdy_stream2->set_spdy_headers(headers2.Pass()); |
| EXPECT_TRUE(spdy_stream2->HasUrl()); |
| |
| spdy_stream1->SendRequest(false); |
| spdy_stream2->SendRequest(false); |
| |
| EXPECT_EQ(0u, spdy_stream1->stream_id()); |
| |
| spdy_stream1->Cancel(); |
| EXPECT_EQ(NULL, spdy_stream1.get()); |
| |
| EXPECT_EQ(0u, delegate1.stream_id()); |
| |
| data.RunFor(1); |
| |
| EXPECT_EQ(0u, delegate1.stream_id()); |
| EXPECT_EQ(1u, delegate2.stream_id()); |
| |
| spdy_stream2->Cancel(); |
| EXPECT_EQ(NULL, spdy_stream2.get()); |
| } |
| |
| TEST_F(SpdySessionSpdy2Test, CloseSessionWithTwoCreatedStreams) { |
| session_deps_.host_resolver->set_synchronous_mode(true); |
| |
| // Test that if a sesion is closed with two created streams pending, |
| // it does not crash. http://crbug.com/139518 |
| MockConnect connect_data(SYNCHRONOUS, OK); |
| |
| // No actual data will be sent. |
| MockWrite writes[] = { |
| MockWrite(ASYNC, 0, 1) // EOF |
| }; |
| |
| MockRead reads[] = { |
| MockRead(ASYNC, 0, 0) // EOF |
| }; |
| DeterministicSocketData data(reads, arraysize(reads), |
| writes, arraysize(writes)); |
| data.set_connect_data(connect_data); |
| session_deps_.deterministic_socket_factory->AddSocketDataProvider(&data); |
| |
| SSLSocketDataProvider ssl(SYNCHRONOUS, OK); |
| session_deps_.deterministic_socket_factory->AddSSLSocketDataProvider(&ssl); |
| |
| CreateDeterministicNetworkSession(); |
| |
| scoped_refptr<SpdySession> session = CreateInitializedSession(); |
| |
| GURL url1("http://www.google.com"); |
| base::WeakPtr<SpdyStream> spdy_stream1 = |
| CreateStreamSynchronously(session, url1, HIGHEST, BoundNetLog()); |
| ASSERT_TRUE(spdy_stream1.get() != NULL); |
| EXPECT_EQ(0u, spdy_stream1->stream_id()); |
| |
| GURL url2("http://www.google.com"); |
| base::WeakPtr<SpdyStream> spdy_stream2 = |
| CreateStreamSynchronously(session, url2, LOWEST, BoundNetLog()); |
| ASSERT_TRUE(spdy_stream2.get() != NULL); |
| EXPECT_EQ(0u, spdy_stream2->stream_id()); |
| |
| scoped_ptr<SpdyHeaderBlock> headers(new SpdyHeaderBlock); |
| (*headers)["method"] = "GET"; |
| (*headers)["scheme"] = url1.scheme(); |
| (*headers)["host"] = url1.host(); |
| (*headers)["url"] = url1.path(); |
| (*headers)["version"] = "HTTP/1.1"; |
| scoped_ptr<SpdyHeaderBlock> headers2(new SpdyHeaderBlock); |
| *headers2 = *headers; |
| |
| spdy_stream1->set_spdy_headers(headers.Pass()); |
| EXPECT_TRUE(spdy_stream1->HasUrl()); |
| test::StreamDelegateDoNothing delegate1(spdy_stream1); |
| spdy_stream1->SetDelegate(&delegate1); |
| |
| spdy_stream2->set_spdy_headers(headers2.Pass()); |
| EXPECT_TRUE(spdy_stream2->HasUrl()); |
| test::StreamDelegateDoNothing delegate2(spdy_stream2); |
| spdy_stream2->SetDelegate(&delegate2); |
| |
| spdy_stream1->SendRequest(false); |
| spdy_stream2->SendRequest(false); |
| |
| // Ensure that the streams have not yet been activated and assigned an id. |
| EXPECT_EQ(0u, spdy_stream1->stream_id()); |
| EXPECT_EQ(0u, spdy_stream2->stream_id()); |
| |
| // Ensure we don't crash while closing the session. |
| session->CloseSessionOnError(ERR_ABORTED, true, std::string()); |
| |
| EXPECT_EQ(NULL, spdy_stream1.get()); |
| EXPECT_EQ(NULL, spdy_stream2.get()); |
| |
| EXPECT_TRUE(delegate1.StreamIsClosed()); |
| EXPECT_TRUE(delegate2.StreamIsClosed()); |
| } |
| |
| TEST_F(SpdySessionSpdy2Test, VerifyDomainAuthentication) { |
| session_deps_.host_resolver->set_synchronous_mode(true); |
| |
| MockConnect connect_data(SYNCHRONOUS, OK); |
| |
| // No actual data will be sent. |
| MockWrite writes[] = { |
| MockWrite(ASYNC, 0, 1) // EOF |
| }; |
| |
| MockRead reads[] = { |
| MockRead(ASYNC, 0, 0) // EOF |
| }; |
| DeterministicSocketData data(reads, arraysize(reads), |
| writes, arraysize(writes)); |
| data.set_connect_data(connect_data); |
| session_deps_.deterministic_socket_factory->AddSocketDataProvider(&data); |
| |
| // Load a cert that is valid for: |
| // www.example.org |
| // mail.example.org |
| // www.example.com |
| base::FilePath certs_dir = GetTestCertsDirectory(); |
| scoped_refptr<X509Certificate> test_cert( |
| ImportCertFromFile(certs_dir, "spdy_pooling.pem")); |
| ASSERT_NE(static_cast<X509Certificate*>(NULL), test_cert); |
| |
| SSLSocketDataProvider ssl(SYNCHRONOUS, OK); |
| ssl.cert = test_cert; |
| session_deps_.deterministic_socket_factory->AddSSLSocketDataProvider(&ssl); |
| |
| CreateDeterministicNetworkSession(); |
| |
| scoped_refptr<SpdySession> session = GetSession(pair_); |
| |
| SSLConfig ssl_config; |
| scoped_refptr<TransportSocketParams> transport_params( |
| new TransportSocketParams(test_host_port_pair_, |
| MEDIUM, |
| false, |
| false, |
| OnHostResolutionCallback())); |
| scoped_refptr<SOCKSSocketParams> socks_params; |
| scoped_refptr<HttpProxySocketParams> http_proxy_params; |
| scoped_refptr<SSLSocketParams> ssl_params( |
| new SSLSocketParams(transport_params, |
| socks_params, |
| http_proxy_params, |
| ProxyServer::SCHEME_DIRECT, |
| test_host_port_pair_, |
| ssl_config, |
| 0, |
| false, |
| false)); |
| scoped_ptr<ClientSocketHandle> connection(new ClientSocketHandle); |
| EXPECT_EQ(OK, connection->Init(test_host_port_pair_.ToString(), |
| ssl_params, MEDIUM, CompletionCallback(), |
| http_session_->GetSSLSocketPool( |
| HttpNetworkSession::NORMAL_SOCKET_POOL), |
| BoundNetLog())); |
| |
| EXPECT_EQ(OK, session->InitializeWithSocket(connection.release(), false, OK)); |
| EXPECT_TRUE(session->VerifyDomainAuthentication("www.example.org")); |
| EXPECT_TRUE(session->VerifyDomainAuthentication("mail.example.org")); |
| EXPECT_TRUE(session->VerifyDomainAuthentication("mail.example.com")); |
| EXPECT_FALSE(session->VerifyDomainAuthentication("mail.google.com")); |
| } |
| |
| TEST_F(SpdySessionSpdy2Test, ConnectionPooledWithTlsChannelId) { |
| session_deps_.host_resolver->set_synchronous_mode(true); |
| |
| MockConnect connect_data(SYNCHRONOUS, OK); |
| |
| // No actual data will be sent. |
| MockWrite writes[] = { |
| MockWrite(ASYNC, 0, 1) // EOF |
| }; |
| |
| MockRead reads[] = { |
| MockRead(ASYNC, 0, 0) // EOF |
| }; |
| DeterministicSocketData data(reads, arraysize(reads), |
| writes, arraysize(writes)); |
| data.set_connect_data(connect_data); |
| session_deps_.deterministic_socket_factory->AddSocketDataProvider(&data); |
| |
| // Load a cert that is valid for: |
| // www.example.org |
| // mail.example.org |
| // www.example.com |
| base::FilePath certs_dir = GetTestCertsDirectory(); |
| scoped_refptr<X509Certificate> test_cert( |
| ImportCertFromFile(certs_dir, "spdy_pooling.pem")); |
| ASSERT_NE(static_cast<X509Certificate*>(NULL), test_cert); |
| |
| SSLSocketDataProvider ssl(SYNCHRONOUS, OK); |
| ssl.channel_id_sent = true; |
| ssl.cert = test_cert; |
| session_deps_.deterministic_socket_factory->AddSSLSocketDataProvider(&ssl); |
| |
| CreateDeterministicNetworkSession(); |
| |
| scoped_refptr<SpdySession> session = GetSession(pair_); |
| |
| SSLConfig ssl_config; |
| scoped_refptr<TransportSocketParams> transport_params( |
| new TransportSocketParams(test_host_port_pair_, |
| MEDIUM, |
| false, |
| false, |
| OnHostResolutionCallback())); |
| scoped_refptr<SOCKSSocketParams> socks_params; |
| scoped_refptr<HttpProxySocketParams> http_proxy_params; |
| scoped_refptr<SSLSocketParams> ssl_params( |
| new SSLSocketParams(transport_params, |
| socks_params, |
| http_proxy_params, |
| ProxyServer::SCHEME_DIRECT, |
| test_host_port_pair_, |
| ssl_config, |
| 0, |
| false, |
| false)); |
| scoped_ptr<ClientSocketHandle> connection(new ClientSocketHandle); |
| EXPECT_EQ(OK, connection->Init(test_host_port_pair_.ToString(), |
| ssl_params, MEDIUM, CompletionCallback(), |
| http_session_->GetSSLSocketPool( |
| HttpNetworkSession::NORMAL_SOCKET_POOL), |
| BoundNetLog())); |
| |
| EXPECT_EQ(OK, session->InitializeWithSocket(connection.release(), false, OK)); |
| EXPECT_TRUE(session->VerifyDomainAuthentication("www.example.org")); |
| EXPECT_TRUE(session->VerifyDomainAuthentication("mail.example.org")); |
| EXPECT_FALSE(session->VerifyDomainAuthentication("mail.example.com")); |
| EXPECT_FALSE(session->VerifyDomainAuthentication("mail.google.com")); |
| } |
| |
| TEST_F(SpdySessionSpdy2Test, CloseTwoStalledCreateStream) { |
| // TODO(rtenneti): Define a helper class/methods and move the common code in |
| // this file. |
| MockConnect connect_data(SYNCHRONOUS, OK); |
| |
| SettingsMap new_settings; |
| const SpdySettingsIds kSpdySettingsIds1 = SETTINGS_MAX_CONCURRENT_STREAMS; |
| const uint32 max_concurrent_streams = 1; |
| new_settings[kSpdySettingsIds1] = |
| SettingsFlagsAndValue(SETTINGS_FLAG_NONE, max_concurrent_streams); |
| |
| scoped_ptr<SpdyFrame> req1(ConstructSpdyGet(NULL, 0, false, 1, LOWEST)); |
| scoped_ptr<SpdyFrame> req2(ConstructSpdyGet(NULL, 0, false, 3, LOWEST)); |
| scoped_ptr<SpdyFrame> req3(ConstructSpdyGet(NULL, 0, false, 5, LOWEST)); |
| MockWrite writes[] = { |
| CreateMockWrite(*req1, 1), |
| CreateMockWrite(*req2, 4), |
| CreateMockWrite(*req3, 7), |
| }; |
| |
| // Set up the socket so we read a SETTINGS frame that sets max concurrent |
| // streams to 1. |
| scoped_ptr<SpdyFrame> settings_frame(ConstructSpdySettings(new_settings)); |
| |
| scoped_ptr<SpdyFrame> resp1(ConstructSpdyGetSynReply(NULL, 0, 1)); |
| scoped_ptr<SpdyFrame> body1(ConstructSpdyBodyFrame(1, true)); |
| |
| scoped_ptr<SpdyFrame> resp2(ConstructSpdyGetSynReply(NULL, 0, 3)); |
| scoped_ptr<SpdyFrame> body2(ConstructSpdyBodyFrame(3, true)); |
| |
| scoped_ptr<SpdyFrame> resp3(ConstructSpdyGetSynReply(NULL, 0, 5)); |
| scoped_ptr<SpdyFrame> body3(ConstructSpdyBodyFrame(5, true)); |
| |
| MockRead reads[] = { |
| CreateMockRead(*settings_frame), |
| CreateMockRead(*resp1, 2), |
| CreateMockRead(*body1, 3), |
| CreateMockRead(*resp2, 5), |
| CreateMockRead(*body2, 6), |
| CreateMockRead(*resp3, 8), |
| CreateMockRead(*body3, 9), |
| MockRead(ASYNC, 0, 10) // EOF |
| }; |
| |
| DeterministicSocketData data(reads, arraysize(reads), |
| writes, arraysize(writes)); |
| data.set_connect_data(connect_data); |
| session_deps_.deterministic_socket_factory->AddSocketDataProvider(&data); |
| |
| SSLSocketDataProvider ssl(SYNCHRONOUS, OK); |
| session_deps_.deterministic_socket_factory->AddSSLSocketDataProvider(&ssl); |
| |
| CreateDeterministicNetworkSession(); |
| |
| scoped_refptr<SpdySession> session = CreateInitializedSession(); |
| |
| // Read the settings frame. |
| data.RunFor(1); |
| |
| GURL url1("http://www.google.com"); |
| base::WeakPtr<SpdyStream> spdy_stream1 = |
| CreateStreamSynchronously(session, url1, LOWEST, BoundNetLog()); |
| ASSERT_TRUE(spdy_stream1.get() != NULL); |
| EXPECT_EQ(0u, spdy_stream1->stream_id()); |
| test::StreamDelegateDoNothing delegate1(spdy_stream1); |
| spdy_stream1->SetDelegate(&delegate1); |
| |
| TestCompletionCallback callback2; |
| GURL url2("http://www.google.com"); |
| SpdyStreamRequest request2; |
| ASSERT_EQ(ERR_IO_PENDING, |
| request2.StartRequest(session, url2, LOWEST, |
| BoundNetLog(), |
| callback2.callback())); |
| |
| TestCompletionCallback callback3; |
| GURL url3("http://www.google.com"); |
| SpdyStreamRequest request3; |
| ASSERT_EQ(ERR_IO_PENDING, |
| request3.StartRequest(session, url3, LOWEST, |
| BoundNetLog(), |
| callback3.callback())); |
| |
| EXPECT_EQ(1u, session->num_active_streams() + session->num_created_streams()); |
| EXPECT_EQ(2u, session->pending_create_stream_queues(LOWEST)); |
| |
| scoped_ptr<SpdyHeaderBlock> headers(new SpdyHeaderBlock); |
| (*headers)["method"] = "GET"; |
| (*headers)["scheme"] = url1.scheme(); |
| (*headers)["host"] = url1.host(); |
| (*headers)["url"] = url1.path(); |
| (*headers)["version"] = "HTTP/1.1"; |
| scoped_ptr<SpdyHeaderBlock> headers2(new SpdyHeaderBlock); |
| *headers2 = *headers; |
| scoped_ptr<SpdyHeaderBlock> headers3(new SpdyHeaderBlock); |
| *headers3 = *headers; |
| |
| spdy_stream1->set_spdy_headers(headers.Pass()); |
| EXPECT_TRUE(spdy_stream1->HasUrl()); |
| spdy_stream1->SendRequest(false); |
| |
| // Run until 1st stream is closed and 2nd one is opened. |
| EXPECT_EQ(0u, delegate1.stream_id()); |
| data.RunFor(3); |
| // Pump loop for SpdySession::ProcessPendingStreamRequests(). |
| MessageLoop::current()->RunUntilIdle(); |
| EXPECT_EQ(NULL, spdy_stream1.get()); |
| EXPECT_EQ(1u, delegate1.stream_id()); |
| EXPECT_EQ(2u, session->num_active_streams() + session->num_created_streams()); |
| EXPECT_EQ(0u, session->pending_create_stream_queues(LOWEST)); |
| |
| base::WeakPtr<SpdyStream> stream2 = request2.ReleaseStream(); |
| test::StreamDelegateDoNothing delegate2(stream2); |
| stream2->SetDelegate(&delegate2); |
| stream2->set_spdy_headers(headers2.Pass()); |
| EXPECT_TRUE(stream2->HasUrl()); |
| stream2->SendRequest(false); |
| |
| // Run until 2nd stream is closed. |
| EXPECT_EQ(0u, delegate2.stream_id()); |
| data.RunFor(3); |
| EXPECT_EQ(NULL, stream2.get()); |
| EXPECT_EQ(3u, delegate2.stream_id()); |
| EXPECT_EQ(1u, session->num_active_streams() + session->num_created_streams()); |
| EXPECT_EQ(0u, session->pending_create_stream_queues(LOWEST)); |
| |
| base::WeakPtr<SpdyStream> stream3 = request3.ReleaseStream(); |
| ASSERT_TRUE(stream3.get() != NULL); |
| test::StreamDelegateDoNothing delegate3(stream3); |
| stream3->SetDelegate(&delegate3); |
| stream3->set_spdy_headers(headers3.Pass()); |
| EXPECT_TRUE(stream3->HasUrl()); |
| stream3->SendRequest(false); |
| |
| EXPECT_EQ(0u, delegate3.stream_id()); |
| data.RunFor(4); |
| EXPECT_EQ(NULL, stream3.get()); |
| EXPECT_EQ(5u, delegate3.stream_id()); |
| EXPECT_EQ(0u, session->num_active_streams() + session->num_created_streams()); |
| EXPECT_EQ(0u, session->pending_create_stream_queues(LOWEST)); |
| } |
| |
| TEST_F(SpdySessionSpdy2Test, CancelTwoStalledCreateStream) { |
| session_deps_.host_resolver->set_synchronous_mode(true); |
| |
| MockRead reads[] = { |
| MockRead(SYNCHRONOUS, ERR_IO_PENDING) // Stall forever. |
| }; |
| |
| StaticSocketDataProvider data(reads, arraysize(reads), NULL, 0); |
| MockConnect connect_data(SYNCHRONOUS, OK); |
| |
| data.set_connect_data(connect_data); |
| session_deps_.socket_factory->AddSocketDataProvider(&data); |
| |
| SSLSocketDataProvider ssl(SYNCHRONOUS, OK); |
| session_deps_.socket_factory->AddSSLSocketDataProvider(&ssl); |
| |
| CreateNetworkSession(); |
| |
| // Initialize the SpdySetting with 1 max concurrent streams. |
| spdy_session_pool_->http_server_properties()->SetSpdySetting( |
| test_host_port_pair_, |
| SETTINGS_MAX_CONCURRENT_STREAMS, |
| SETTINGS_FLAG_PLEASE_PERSIST, |
| 1); |
| |
| scoped_refptr<SpdySession> session = CreateInitializedSession(); |
| |
| GURL url1("http://www.google.com"); |
| base::WeakPtr<SpdyStream> spdy_stream1 = |
| CreateStreamSynchronously(session, url1, LOWEST, BoundNetLog()); |
| ASSERT_TRUE(spdy_stream1.get() != NULL); |
| EXPECT_EQ(0u, spdy_stream1->stream_id()); |
| |
| TestCompletionCallback callback2; |
| GURL url2("http://www.google.com"); |
| SpdyStreamRequest request2; |
| ASSERT_EQ(ERR_IO_PENDING, |
| request2.StartRequest(session, url2, LOWEST, |
| BoundNetLog(), |
| callback2.callback())); |
| |
| TestCompletionCallback callback3; |
| GURL url3("http://www.google.com"); |
| SpdyStreamRequest request3; |
| ASSERT_EQ(ERR_IO_PENDING, |
| request3.StartRequest(session, url3, LOWEST, |
| BoundNetLog(), |
| callback3.callback())); |
| |
| EXPECT_EQ(1u, session->num_active_streams() + session->num_created_streams()); |
| EXPECT_EQ(2u, session->pending_create_stream_queues(LOWEST)); |
| |
| // Cancel the first stream, this will allow the second stream to be created. |
| EXPECT_TRUE(spdy_stream1.get() != NULL); |
| spdy_stream1->Cancel(); |
| EXPECT_EQ(NULL, spdy_stream1.get()); |
| |
| callback2.WaitForResult(); |
| EXPECT_EQ(2u, session->num_active_streams() + session->num_created_streams()); |
| EXPECT_EQ(0u, session->pending_create_stream_queues(LOWEST)); |
| |
| // Cancel the second stream, this will allow the third stream to be created. |
| base::WeakPtr<SpdyStream> spdy_stream2 = request2.ReleaseStream(); |
| spdy_stream2->Cancel(); |
| EXPECT_EQ(NULL, spdy_stream2.get()); |
| EXPECT_EQ(1u, session->num_active_streams() + session->num_created_streams()); |
| EXPECT_EQ(0u, session->pending_create_stream_queues(LOWEST)); |
| |
| // Cancel the third stream. |
| base::WeakPtr<SpdyStream> spdy_stream3 = request3.ReleaseStream(); |
| spdy_stream3->Cancel(); |
| EXPECT_EQ(NULL, spdy_stream3.get()); |
| EXPECT_EQ(0u, session->num_active_streams() + session->num_created_streams()); |
| EXPECT_EQ(0u, session->pending_create_stream_queues(LOWEST)); |
| } |
| |
| TEST_F(SpdySessionSpdy2Test, NeedsCredentials) { |
| MockConnect connect_data(SYNCHRONOUS, OK); |
| MockRead reads[] = { |
| MockRead(SYNCHRONOUS, ERR_IO_PENDING) // Stall forever. |
| }; |
| StaticSocketDataProvider data(reads, arraysize(reads), NULL, 0); |
| data.set_connect_data(connect_data); |
| session_deps_.socket_factory->AddSocketDataProvider(&data); |
| |
| SSLSocketDataProvider ssl(SYNCHRONOUS, OK); |
| ssl.channel_id_sent = true; |
| ssl.protocol_negotiated = kProtoSPDY2; |
| session_deps_.socket_factory->AddSSLSocketDataProvider(&ssl); |
| |
| CreateNetworkSession(); |
| |
| const std::string kTestHost("www.foo.com"); |
| const int kTestPort = 80; |
| HostPortPair test_host_port_pair(kTestHost, kTestPort); |
| HostPortProxyPair pair(test_host_port_pair, ProxyServer::Direct()); |
| |
| scoped_refptr<SpdySession> session = GetSession(pair); |
| |
| SSLConfig ssl_config; |
| scoped_refptr<TransportSocketParams> transport_params( |
| new TransportSocketParams(test_host_port_pair, |
| MEDIUM, |
| false, |
| false, |
| OnHostResolutionCallback())); |
| scoped_refptr<SOCKSSocketParams> socks_params; |
| scoped_refptr<HttpProxySocketParams> http_proxy_params; |
| scoped_refptr<SSLSocketParams> ssl_params( |
| new SSLSocketParams(transport_params, |
| socks_params, |
| http_proxy_params, |
| ProxyServer::SCHEME_DIRECT, |
| test_host_port_pair, |
| ssl_config, |
| 0, |
| false, |
| false)); |
| scoped_ptr<ClientSocketHandle> connection(new ClientSocketHandle); |
| EXPECT_EQ(OK, connection->Init(test_host_port_pair.ToString(), |
| ssl_params, MEDIUM, CompletionCallback(), |
| http_session_->GetSSLSocketPool( |
| HttpNetworkSession::NORMAL_SOCKET_POOL), |
| BoundNetLog())); |
| |
| EXPECT_EQ(OK, session->InitializeWithSocket(connection.release(), true, OK)); |
| |
| EXPECT_FALSE(session->NeedsCredentials()); |
| |
| // Flush the SpdySession::OnReadComplete() task. |
| MessageLoop::current()->RunUntilIdle(); |
| |
| spdy_session_pool_->Remove(session); |
| } |
| |
| // Test that SpdySession::DoRead reads data from the socket without yielding. |
| // This test makes 32k - 1 bytes of data available on the socket for reading. It |
| // then verifies that it has read all the available data without yielding. |
| TEST_F(SpdySessionSpdy2Test, ReadDataWithoutYielding) { |
| MockConnect connect_data(SYNCHRONOUS, OK); |
| BufferedSpdyFramer framer(2, false); |
| |
| scoped_ptr<SpdyFrame> req1(ConstructSpdyGet(NULL, 0, false, 1, MEDIUM)); |
| MockWrite writes[] = { |
| CreateMockWrite(*req1, 0), |
| }; |
| |
| // Build buffer of size kMaxReadBytes / 4 (-spdy_data_frame_size). |
| ASSERT_EQ(32 * 1024, kMaxReadBytes); |
| const int kPayloadSize = |
| kMaxReadBytes / 4 - framer.GetControlFrameHeaderSize(); |
| TestDataStream test_stream; |
| scoped_refptr<net::IOBuffer> payload(new net::IOBuffer(kPayloadSize)); |
| char* payload_data = payload->data(); |
| test_stream.GetBytes(payload_data, kPayloadSize); |
| |
| scoped_ptr<SpdyFrame> partial_data_frame( |
| framer.CreateDataFrame(1, payload_data, kPayloadSize, DATA_FLAG_NONE)); |
| scoped_ptr<SpdyFrame> finish_data_frame( |
| framer.CreateDataFrame(1, payload_data, kPayloadSize - 1, DATA_FLAG_FIN)); |
| |
| scoped_ptr<SpdyFrame> resp1(ConstructSpdyGetSynReply(NULL, 0, 1)); |
| |
| // Write 1 byte less than kMaxReadBytes to check that DoRead reads up to 32k |
| // bytes. |
| MockRead reads[] = { |
| CreateMockRead(*resp1, 1), |
| CreateMockRead(*partial_data_frame, 2), |
| CreateMockRead(*partial_data_frame, 3, SYNCHRONOUS), |
| CreateMockRead(*partial_data_frame, 4, SYNCHRONOUS), |
| CreateMockRead(*finish_data_frame, 5, SYNCHRONOUS), |
| MockRead(ASYNC, 0, 6) // EOF |
| }; |
| |
| // Create SpdySession and SpdyStream and send the request. |
| DeterministicSocketData data(reads, arraysize(reads), |
| writes, arraysize(writes)); |
| data.set_connect_data(connect_data); |
| session_deps_.host_resolver->set_synchronous_mode(true); |
| session_deps_.deterministic_socket_factory->AddSocketDataProvider(&data); |
| |
| SSLSocketDataProvider ssl(SYNCHRONOUS, OK); |
| session_deps_.deterministic_socket_factory->AddSSLSocketDataProvider(&ssl); |
| |
| CreateDeterministicNetworkSession(); |
| |
| scoped_refptr<SpdySession> session = CreateInitializedSession(); |
| |
| GURL url1("http://www.google.com"); |
| base::WeakPtr<SpdyStream> spdy_stream1 = |
| CreateStreamSynchronously(session, url1, MEDIUM, BoundNetLog()); |
| ASSERT_TRUE(spdy_stream1.get() != NULL); |
| EXPECT_EQ(0u, spdy_stream1->stream_id()); |
| test::StreamDelegateDoNothing delegate1(spdy_stream1); |
| spdy_stream1->SetDelegate(&delegate1); |
| |
| scoped_ptr<SpdyHeaderBlock> headers(new SpdyHeaderBlock); |
| (*headers)["method"] = "GET"; |
| (*headers)["scheme"] = url1.scheme(); |
| (*headers)["host"] = url1.host(); |
| (*headers)["url"] = url1.path(); |
| (*headers)["version"] = "HTTP/1.1"; |
| |
| spdy_stream1->set_spdy_headers(headers.Pass()); |
| EXPECT_TRUE(spdy_stream1->HasUrl()); |
| spdy_stream1->SendRequest(false); |
| |
| // Set up the TaskObserver to verify SpdySession::DoRead doesn't post a task. |
| SpdySessionTestTaskObserver observer("spdy_session.cc", "DoRead"); |
| |
| // Run until 1st read. |
| EXPECT_EQ(0u, delegate1.stream_id()); |
| data.RunFor(2); |
| EXPECT_EQ(1u, delegate1.stream_id()); |
| EXPECT_EQ(0u, observer.executed_count()); |
| |
| // Read all the data and verify SpdySession::DoRead has not posted a task. |
| data.RunFor(4); |
| EXPECT_EQ(NULL, spdy_stream1.get()); |
| |
| // Verify task observer's executed_count is zero, which indicates DoRead read |
| // all the available data. |
| EXPECT_EQ(0u, observer.executed_count()); |
| EXPECT_TRUE(data.at_write_eof()); |
| EXPECT_TRUE(data.at_read_eof()); |
| } |
| |
| // Test that SpdySession::DoRead yields while reading the data. This test makes |
| // 32k + 1 bytes of data available on the socket for reading. It then verifies |
| // that DoRead has yielded even though there is data available for it to read |
| // (i.e, socket()->Read didn't return ERR_IO_PENDING during socket reads). |
| TEST_F(SpdySessionSpdy2Test, TestYieldingDuringReadData) { |
| MockConnect connect_data(SYNCHRONOUS, OK); |
| BufferedSpdyFramer framer(2, false); |
| |
| scoped_ptr<SpdyFrame> req1(ConstructSpdyGet(NULL, 0, false, 1, MEDIUM)); |
| MockWrite writes[] = { |
| CreateMockWrite(*req1, 0), |
| }; |
| |
| // Build buffer of size kMaxReadBytes / 4 (-spdy_data_frame_size). |
| ASSERT_EQ(32 * 1024, kMaxReadBytes); |
| const int kPayloadSize = |
| kMaxReadBytes / 4 - framer.GetControlFrameHeaderSize(); |
| TestDataStream test_stream; |
| scoped_refptr<net::IOBuffer> payload(new net::IOBuffer(kPayloadSize)); |
| char* payload_data = payload->data(); |
| test_stream.GetBytes(payload_data, kPayloadSize); |
| |
| scoped_ptr<SpdyFrame> partial_data_frame( |
| framer.CreateDataFrame(1, payload_data, kPayloadSize, DATA_FLAG_NONE)); |
| scoped_ptr<SpdyFrame> finish_data_frame( |
| framer.CreateDataFrame(1, "h", 1, DATA_FLAG_FIN)); |
| |
| scoped_ptr<SpdyFrame> resp1(ConstructSpdyGetSynReply(NULL, 0, 1)); |
| |
| // Write 1 byte more than kMaxReadBytes to check that DoRead yields. |
| MockRead reads[] = { |
| CreateMockRead(*resp1, 1), |
| CreateMockRead(*partial_data_frame, 2), |
| CreateMockRead(*partial_data_frame, 3, SYNCHRONOUS), |
| CreateMockRead(*partial_data_frame, 4, SYNCHRONOUS), |
| CreateMockRead(*partial_data_frame, 5, SYNCHRONOUS), |
| CreateMockRead(*finish_data_frame, 6, SYNCHRONOUS), |
| MockRead(ASYNC, 0, 7) // EOF |
| }; |
| |
| // Create SpdySession and SpdyStream and send the request. |
| DeterministicSocketData data(reads, arraysize(reads), |
| writes, arraysize(writes)); |
| data.set_connect_data(connect_data); |
| session_deps_.host_resolver->set_synchronous_mode(true); |
| session_deps_.deterministic_socket_factory->AddSocketDataProvider(&data); |
| |
| SSLSocketDataProvider ssl(SYNCHRONOUS, OK); |
| session_deps_.deterministic_socket_factory->AddSSLSocketDataProvider(&ssl); |
| |
| CreateDeterministicNetworkSession(); |
| |
| scoped_refptr<SpdySession> session = CreateInitializedSession(); |
| |
| GURL url1("http://www.google.com"); |
| base::WeakPtr<SpdyStream> spdy_stream1 = |
| CreateStreamSynchronously(session, url1, MEDIUM, BoundNetLog()); |
| ASSERT_TRUE(spdy_stream1.get() != NULL); |
| EXPECT_EQ(0u, spdy_stream1->stream_id()); |
| test::StreamDelegateDoNothing delegate1(spdy_stream1); |
| spdy_stream1->SetDelegate(&delegate1); |
| |
| scoped_ptr<SpdyHeaderBlock> headers(new SpdyHeaderBlock); |
| (*headers)["method"] = "GET"; |
| (*headers)["scheme"] = url1.scheme(); |
| (*headers)["host"] = url1.host(); |
| (*headers)["url"] = url1.path(); |
| (*headers)["version"] = "HTTP/1.1"; |
| |
| spdy_stream1->set_spdy_headers(headers.Pass()); |
| EXPECT_TRUE(spdy_stream1->HasUrl()); |
| spdy_stream1->SendRequest(false); |
| |
| // Set up the TaskObserver to verify SpdySession::DoRead posts a task. |
| SpdySessionTestTaskObserver observer("spdy_session.cc", "DoRead"); |
| |
| // Run until 1st read. |
| EXPECT_EQ(0u, delegate1.stream_id()); |
| data.RunFor(2); |
| EXPECT_EQ(1u, delegate1.stream_id()); |
| EXPECT_EQ(0u, observer.executed_count()); |
| |
| // Read all the data and verify SpdySession::DoRead has posted a task. |
| data.RunFor(6); |
| EXPECT_EQ(NULL, spdy_stream1.get()); |
| |
| // Verify task observer's executed_count is 1, which indicates DoRead has |
| // posted only one task and thus yielded though there is data available for it |
| // to read. |
| EXPECT_EQ(1u, observer.executed_count()); |
| EXPECT_TRUE(data.at_write_eof()); |
| EXPECT_TRUE(data.at_read_eof()); |
| } |
| |
| // Test that SpdySession::DoRead() tests interactions of yielding + async, |
| // by doing the following MockReads. |
| // |
| // MockRead of SYNCHRONOUS 8K, SYNCHRONOUS 8K, SYNCHRONOUS 8K, SYNCHRONOUS 2K |
| // ASYNC 8K, SYNCHRONOUS 8K, SYNCHRONOUS 8K, SYNCHRONOUS 8K, SYNCHRONOUS 2K. |
| // |
| // The above reads 26K synchronously. Since that is less that 32K, we will |
| // attempt to read again. However, that DoRead() will return ERR_IO_PENDING |
| // (because of async read), so DoRead() will yield. When we come back, DoRead() |
| // will read the results from the async read, and rest of the data |
| // synchronously. |
| TEST_F(SpdySessionSpdy2Test, TestYieldingDuringAsyncReadData) { |
| MockConnect connect_data(SYNCHRONOUS, OK); |
| BufferedSpdyFramer framer(2, false); |
| |
| scoped_ptr<SpdyFrame> req1(ConstructSpdyGet(NULL, 0, false, 1, MEDIUM)); |
| MockWrite writes[] = { |
| CreateMockWrite(*req1, 0), |
| }; |
| |
| // Build buffer of size kMaxReadBytes / 4 (-spdy_data_frame_size). |
| ASSERT_EQ(32 * 1024, kMaxReadBytes); |
| TestDataStream test_stream; |
| const int kEightKPayloadSize = |
| kMaxReadBytes / 4 - framer.GetControlFrameHeaderSize(); |
| scoped_refptr<net::IOBuffer> eightk_payload( |
| new net::IOBuffer(kEightKPayloadSize)); |
| char* eightk_payload_data = eightk_payload->data(); |
| test_stream.GetBytes(eightk_payload_data, kEightKPayloadSize); |
| |
| // Build buffer of 2k size. |
| TestDataStream test_stream2; |
| const int kTwoKPayloadSize = kEightKPayloadSize - 6 * 1024; |
| scoped_refptr<net::IOBuffer> twok_payload( |
| new net::IOBuffer(kTwoKPayloadSize)); |
| char* twok_payload_data = twok_payload->data(); |
| test_stream2.GetBytes(twok_payload_data, kTwoKPayloadSize); |
| |
| scoped_ptr<SpdyFrame> eightk_data_frame(framer.CreateDataFrame( |
| 1, eightk_payload_data, kEightKPayloadSize, DATA_FLAG_NONE)); |
| scoped_ptr<SpdyFrame> twok_data_frame(framer.CreateDataFrame( |
| 1, twok_payload_data, kTwoKPayloadSize, DATA_FLAG_NONE)); |
| scoped_ptr<SpdyFrame> finish_data_frame(framer.CreateDataFrame( |
| 1, "h", 1, DATA_FLAG_FIN)); |
| |
| scoped_ptr<SpdyFrame> resp1(ConstructSpdyGetSynReply(NULL, 0, 1)); |
| |
| MockRead reads[] = { |
| CreateMockRead(*resp1, 1), |
| CreateMockRead(*eightk_data_frame, 2), |
| CreateMockRead(*eightk_data_frame, 3, SYNCHRONOUS), |
| CreateMockRead(*eightk_data_frame, 4, SYNCHRONOUS), |
| CreateMockRead(*twok_data_frame, 5, SYNCHRONOUS), |
| CreateMockRead(*eightk_data_frame, 6, ASYNC), |
| CreateMockRead(*eightk_data_frame, 7, SYNCHRONOUS), |
| CreateMockRead(*eightk_data_frame, 8, SYNCHRONOUS), |
| CreateMockRead(*eightk_data_frame, 9, SYNCHRONOUS), |
| CreateMockRead(*twok_data_frame, 10, SYNCHRONOUS), |
| CreateMockRead(*finish_data_frame, 11, SYNCHRONOUS), |
| MockRead(ASYNC, 0, 12) // EOF |
| }; |
| |
| // Create SpdySession and SpdyStream and send the request. |
| DeterministicSocketData data(reads, arraysize(reads), |
| writes, arraysize(writes)); |
| data.set_connect_data(connect_data); |
| session_deps_.host_resolver->set_synchronous_mode(true); |
| session_deps_.deterministic_socket_factory->AddSocketDataProvider(&data); |
| |
| SSLSocketDataProvider ssl(SYNCHRONOUS, OK); |
| session_deps_.deterministic_socket_factory->AddSSLSocketDataProvider(&ssl); |
| |
| CreateDeterministicNetworkSession(); |
| |
| scoped_refptr<SpdySession> session = CreateInitializedSession(); |
| |
| GURL url1("http://www.google.com"); |
| base::WeakPtr<SpdyStream> spdy_stream1 = |
| CreateStreamSynchronously(session, url1, MEDIUM, BoundNetLog()); |
| ASSERT_TRUE(spdy_stream1.get() != NULL); |
| EXPECT_EQ(0u, spdy_stream1->stream_id()); |
| test::StreamDelegateDoNothing delegate1(spdy_stream1); |
| spdy_stream1->SetDelegate(&delegate1); |
| |
| scoped_ptr<SpdyHeaderBlock> headers(new SpdyHeaderBlock); |
| (*headers)["method"] = "GET"; |
| (*headers)["scheme"] = url1.scheme(); |
| (*headers)["host"] = url1.host(); |
| (*headers)["url"] = url1.path(); |
| (*headers)["version"] = "HTTP/1.1"; |
| |
| spdy_stream1->set_spdy_headers(headers.Pass()); |
| EXPECT_TRUE(spdy_stream1->HasUrl()); |
| spdy_stream1->SendRequest(false); |
| |
| // Set up the TaskObserver to monitor SpdySession::DoRead posting of tasks. |
| SpdySessionTestTaskObserver observer("spdy_session.cc", "DoRead"); |
| |
| // Run until 1st read. |
| EXPECT_EQ(0u, delegate1.stream_id()); |
| data.RunFor(2); |
| EXPECT_EQ(1u, delegate1.stream_id()); |
| EXPECT_EQ(0u, observer.executed_count()); |
| |
| // Read all the data and verify SpdySession::DoRead has posted a task. |
| data.RunFor(12); |
| EXPECT_EQ(NULL, spdy_stream1.get()); |
| |
| // Verify task observer's executed_count is 1, which indicates DoRead has |
| // posted only one task and thus yielded though there is data available for |
| // it to read. |
| EXPECT_EQ(1u, observer.executed_count()); |
| EXPECT_TRUE(data.at_write_eof()); |
| EXPECT_TRUE(data.at_read_eof()); |
| } |
| |
| // Send a GoAway frame when SpdySession is in DoLoop. If scoped_refptr to |
| // <SpdySession> is deleted from SpdySession::DoLoop(), we get a crash because |
| // GoAway could delete the SpdySession from the SpdySessionPool and the last |
| // reference to SpdySession. |
| TEST_F(SpdySessionSpdy2Test, GoAwayWhileInDoLoop) { |
| MockConnect connect_data(SYNCHRONOUS, OK); |
| BufferedSpdyFramer framer(2, false); |
| |
| scoped_ptr<SpdyFrame> req1(ConstructSpdyGet(NULL, 0, false, 1, MEDIUM)); |
| MockWrite writes[] = { |
| CreateMockWrite(*req1, 0), |
| }; |
| |
| scoped_ptr<SpdyFrame> resp1(ConstructSpdyGetSynReply(NULL, 0, 1)); |
| scoped_ptr<SpdyFrame> body1(ConstructSpdyBodyFrame(1, true)); |
| scoped_ptr<SpdyFrame> goaway(ConstructSpdyGoAway()); |
| |
| MockRead reads[] = { |
| CreateMockRead(*resp1, 1), |
| CreateMockRead(*body1, 2), |
| CreateMockRead(*goaway, 3), |
| MockRead(ASYNC, 0, 4) // EOF |
| }; |
| |
| // Create SpdySession and SpdyStream and send the request. |
| DeterministicSocketData data(reads, arraysize(reads), |
| writes, arraysize(writes)); |
| data.set_connect_data(connect_data); |
| session_deps_.host_resolver->set_synchronous_mode(true); |
| session_deps_.deterministic_socket_factory->AddSocketDataProvider(&data); |
| |
| SSLSocketDataProvider ssl(SYNCHRONOUS, OK); |
| session_deps_.deterministic_socket_factory->AddSSLSocketDataProvider(&ssl); |
| |
| CreateDeterministicNetworkSession(); |
| |
| scoped_refptr<SpdySession> session = CreateInitializedSession(); |
| |
| GURL url1("http://www.google.com"); |
| base::WeakPtr<SpdyStream> spdy_stream1 = |
| CreateStreamSynchronously(session, url1, MEDIUM, BoundNetLog()); |
| session = NULL; |
| ASSERT_TRUE(spdy_stream1.get() != NULL); |
| EXPECT_EQ(0u, spdy_stream1->stream_id()); |
| |
| scoped_ptr<SpdyHeaderBlock> headers(new SpdyHeaderBlock); |
| (*headers)["method"] = "GET"; |
| (*headers)["scheme"] = url1.scheme(); |
| (*headers)["host"] = url1.host(); |
| (*headers)["url"] = url1.path(); |
| (*headers)["version"] = "HTTP/1.1"; |
| |
| spdy_stream1->set_spdy_headers(headers.Pass()); |
| EXPECT_TRUE(spdy_stream1->HasUrl()); |
| spdy_stream1->SendRequest(false); |
| |
| // Run until 1st read. |
| EXPECT_EQ(0u, spdy_stream1->stream_id()); |
| data.RunFor(1); |
| EXPECT_EQ(1u, spdy_stream1->stream_id()); |
| |
| // Only references to SpdySession are held by DoLoop and |
| // SpdySessionPool. If DoLoop doesn't hold the reference, we get a |
| // crash if SpdySession is deleted from the SpdySessionPool. |
| |
| // Run until GoAway. |
| data.RunFor(4); |
| EXPECT_EQ(NULL, spdy_stream1.get()); |
| EXPECT_TRUE(data.at_write_eof()); |
| EXPECT_TRUE(data.at_read_eof()); |
| } |
| |
| // Within this framework, a SpdySession should be initialized with |
| // flow control disabled and with protocol version 2. |
| TEST_F(SpdySessionSpdy2Test, ProtocolNegotiation) { |
| session_deps_.host_resolver->set_synchronous_mode(true); |
| |
| MockConnect connect_data(SYNCHRONOUS, OK); |
| MockRead reads[] = { |
| MockRead(SYNCHRONOUS, 0, 0) // EOF |
| }; |
| StaticSocketDataProvider data(reads, arraysize(reads), NULL, 0); |
| data.set_connect_data(connect_data); |
| session_deps_.socket_factory->AddSocketDataProvider(&data); |
| |
| CreateNetworkSession(); |
| scoped_refptr<SpdySession> session = GetSession(pair_); |
| |
| EXPECT_EQ(SpdySession::FLOW_CONTROL_NONE, session->flow_control_state()); |
| EXPECT_TRUE(session->buffered_spdy_framer_ == NULL); |
| EXPECT_EQ(0, session->session_send_window_size_); |
| EXPECT_EQ(0, session->session_recv_window_size_); |
| EXPECT_EQ(0, session->session_unacked_recv_window_bytes_); |
| |
| InitializeSession( |
| http_session_.get(), session.get(), test_host_port_pair_); |
| |
| EXPECT_EQ(SpdySession::FLOW_CONTROL_NONE, session->flow_control_state()); |
| EXPECT_EQ(kSpdyVersion2, session->buffered_spdy_framer_->protocol_version()); |
| EXPECT_EQ(0, session->session_send_window_size_); |
| EXPECT_EQ(0, session->session_recv_window_size_); |
| EXPECT_EQ(0, session->session_unacked_recv_window_bytes_); |
| } |
| |
| } // namespace net |