// 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_proxy_client_socket.h" #include #include "base/bind.h" #include "base/bind_helpers.h" #include "base/macros.h" #include "base/run_loop.h" #include "base/strings/string_piece.h" #include "base/strings/utf_string_conversions.h" #include "net/base/address_list.h" #include "net/base/test_completion_callback.h" #include "net/base/winsock_init.h" #include "net/dns/mock_host_resolver.h" #include "net/http/http_response_headers.h" #include "net/http/http_response_info.h" #include "net/log/net_log_event_type.h" #include "net/log/net_log_source.h" #include "net/log/test_net_log.h" #include "net/log/test_net_log_entry.h" #include "net/log/test_net_log_util.h" #include "net/socket/client_socket_factory.h" #include "net/socket/socket_tag.h" #include "net/socket/socket_test_util.h" #include "net/socket/tcp_client_socket.h" #include "net/spdy/buffered_spdy_framer.h" #include "net/spdy/spdy_http_utils.h" #include "net/spdy/spdy_session_pool.h" #include "net/spdy/spdy_test_util_common.h" #include "net/test/cert_test_util.h" #include "net/test/gtest_util.h" #include "net/test/test_data_directory.h" #include "net/test/test_with_scoped_task_environment.h" #include "net/third_party/quiche/src/spdy/core/spdy_protocol.h" #include "net/traffic_annotation/network_traffic_annotation_test_helper.h" #include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" #include "testing/platform_test.h" using net::test::IsError; using net::test::IsOk; //----------------------------------------------------------------------------- namespace { static const char kRequestUrl[] = "https://www.google.com/"; static const char kOriginHost[] = "www.google.com"; static const int kOriginPort = 443; static const char kOriginHostPort[] = "www.google.com:443"; static const char kProxyUrl[] = "https://myproxy:6121/"; static const char kProxyHost[] = "myproxy"; static const int kProxyPort = 6121; static const char kUserAgent[] = "Mozilla/1.0"; static const int kStreamId = 1; static const char kMsg1[] = "\0hello!\xff"; static const int kLen1 = 8; static const char kMsg2[] = "\0a2345678\0"; static const int kLen2 = 10; static const char kMsg3[] = "bye!"; static const int kLen3 = 4; static const char kMsg33[] = "bye!bye!"; static const int kLen33 = kLen3 + kLen3; static const char kMsg333[] = "bye!bye!bye!"; static const int kLen333 = kLen3 + kLen3 + kLen3; static const char kRedirectUrl[] = "https://example.com/"; } // anonymous namespace namespace net { class SpdyProxyClientSocketTest : public PlatformTest, public WithScopedTaskEnvironment, public ::testing::WithParamInterface { public: SpdyProxyClientSocketTest(); ~SpdyProxyClientSocketTest() override; void TearDown() override; protected: void Initialize(base::span reads, base::span writes); void PopulateConnectRequestIR(spdy::SpdyHeaderBlock* syn_ir); void PopulateConnectReplyIR(spdy::SpdyHeaderBlock* block, const char* status); spdy::SpdySerializedFrame ConstructConnectRequestFrame( RequestPriority priority); spdy::SpdySerializedFrame ConstructConnectAuthRequestFrame(); spdy::SpdySerializedFrame ConstructConnectReplyFrame(); spdy::SpdySerializedFrame ConstructConnectAuthReplyFrame(); spdy::SpdySerializedFrame ConstructConnectRedirectReplyFrame(); spdy::SpdySerializedFrame ConstructConnectErrorReplyFrame(); spdy::SpdySerializedFrame ConstructBodyFrame(const char* data, int length); scoped_refptr CreateBuffer(const char* data, int size); void AssertConnectSucceeds(); void AssertConnectFails(int result); void AssertConnectionEstablished(); void AssertSyncReadEquals(const char* data, int len); void AssertSyncReadEOF(); void AssertAsyncReadEquals(const char* data, int len); void AssertReadStarts(const char* data, int len); void AssertReadReturns(const char* data, int len); void AssertAsyncWriteSucceeds(const char* data, int len); void AssertWriteReturns(const char* data, int len, int rv); void AssertWriteLength(int len); void AddAuthToCache() { const base::string16 kFoo(base::ASCIIToUTF16("foo")); const base::string16 kBar(base::ASCIIToUTF16("bar")); session_->http_auth_cache()->Add(GURL(kProxyUrl), "MyRealm1", HttpAuth::AUTH_SCHEME_BASIC, "Basic realm=MyRealm1", AuthCredentials(kFoo, kBar), "/"); } void ResumeAndRun() { // Run until the pause, if the provider isn't paused yet. data_->RunUntilPaused(); data_->Resume(); base::RunLoop().RunUntilIdle(); } void CloseSpdySession(Error error, const std::string& description) { spdy_session_->CloseSessionOnError(error, description); } // Whether to use net::Socket::ReadIfReady() instead of net::Socket::Read(). bool use_read_if_ready() const { return GetParam(); } SpdyTestUtil spdy_util_; std::unique_ptr sock_; TestCompletionCallback read_callback_; TestCompletionCallback write_callback_; std::unique_ptr data_; BoundTestNetLog net_log_; private: scoped_refptr read_buf_; SpdySessionDependencies session_deps_; std::unique_ptr session_; MockConnect connect_data_; base::WeakPtr spdy_session_; std::string user_agent_; GURL url_; HostPortPair proxy_host_port_; HostPortPair endpoint_host_port_pair_; ProxyServer proxy_; SpdySessionKey endpoint_spdy_session_key_; DISALLOW_COPY_AND_ASSIGN(SpdyProxyClientSocketTest); }; SpdyProxyClientSocketTest::SpdyProxyClientSocketTest() : read_buf_(NULL), connect_data_(SYNCHRONOUS, OK), user_agent_(kUserAgent), url_(kRequestUrl), proxy_host_port_(kProxyHost, kProxyPort), endpoint_host_port_pair_(kOriginHost, kOriginPort), proxy_(ProxyServer::SCHEME_HTTPS, proxy_host_port_), endpoint_spdy_session_key_(endpoint_host_port_pair_, proxy_, PRIVACY_MODE_DISABLED, SpdySessionKey::IsProxySession::kFalse, SocketTag()) { session_deps_.net_log = net_log_.bound().net_log(); } SpdyProxyClientSocketTest::~SpdyProxyClientSocketTest() { if (data_) { EXPECT_TRUE(data_->AllWriteDataConsumed()); EXPECT_TRUE(data_->AllReadDataConsumed()); } } void SpdyProxyClientSocketTest::TearDown() { if (session_) session_->spdy_session_pool()->CloseAllSessions(); // Empty the current queue. base::RunLoop().RunUntilIdle(); PlatformTest::TearDown(); } void SpdyProxyClientSocketTest::Initialize(base::span reads, base::span writes) { data_ = std::make_unique(reads, writes); data_->set_connect_data(connect_data_); session_deps_.socket_factory->AddSocketDataProvider(data_.get()); SSLSocketDataProvider ssl(SYNCHRONOUS, OK); ssl.ssl_info.cert = ImportCertFromFile(GetTestCertsDirectory(), "spdy_pooling.pem"); ASSERT_TRUE(ssl.ssl_info.cert); session_deps_.socket_factory->AddSSLSocketDataProvider(&ssl); session_ = SpdySessionDependencies::SpdyCreateSession(&session_deps_); // Creates the SPDY session and stream. spdy_session_ = CreateSpdySession(session_.get(), endpoint_spdy_session_key_, NetLogWithSource()); base::WeakPtr spdy_stream( CreateStreamSynchronously( SPDY_BIDIRECTIONAL_STREAM, spdy_session_, url_, LOWEST, net_log_.bound())); ASSERT_TRUE(spdy_stream.get() != NULL); // Create the SpdyProxyClientSocket. sock_ = std::make_unique( spdy_stream, user_agent_, endpoint_host_port_pair_, net_log_.bound(), new HttpAuthController( HttpAuth::AUTH_PROXY, GURL("https://" + proxy_host_port_.ToString()), session_->http_auth_cache(), session_->http_auth_handler_factory())); } scoped_refptr SpdyProxyClientSocketTest::CreateBuffer( const char* data, int size) { scoped_refptr buf = base::MakeRefCounted(size); memcpy(buf->data(), data, size); return buf; } void SpdyProxyClientSocketTest::AssertConnectSucceeds() { ASSERT_THAT(sock_->Connect(read_callback_.callback()), IsError(ERR_IO_PENDING)); ASSERT_THAT(read_callback_.WaitForResult(), IsOk()); } void SpdyProxyClientSocketTest::AssertConnectFails(int result) { ASSERT_THAT(sock_->Connect(read_callback_.callback()), IsError(ERR_IO_PENDING)); ASSERT_EQ(result, read_callback_.WaitForResult()); } void SpdyProxyClientSocketTest::AssertConnectionEstablished() { const HttpResponseInfo* response = sock_->GetConnectResponseInfo(); ASSERT_TRUE(response != NULL); ASSERT_EQ(200, response->headers->response_code()); } void SpdyProxyClientSocketTest::AssertSyncReadEquals(const char* data, int len) { scoped_refptr buf = base::MakeRefCounted(len); if (use_read_if_ready()) { ASSERT_EQ(len, sock_->ReadIfReady(buf.get(), len, CompletionOnceCallback())); } else { ASSERT_EQ(len, sock_->Read(buf.get(), len, CompletionOnceCallback())); } ASSERT_EQ(std::string(data, len), std::string(buf->data(), len)); ASSERT_TRUE(sock_->IsConnected()); } void SpdyProxyClientSocketTest::AssertSyncReadEOF() { if (use_read_if_ready()) { ASSERT_EQ(0, sock_->ReadIfReady(nullptr, 1, read_callback_.callback())); } else { ASSERT_EQ(0, sock_->Read(nullptr, 1, read_callback_.callback())); } } void SpdyProxyClientSocketTest::AssertAsyncReadEquals(const char* data, int len) { // Issue the read, which will be completed asynchronously scoped_refptr buf = base::MakeRefCounted(len); if (use_read_if_ready()) { ASSERT_EQ(ERR_IO_PENDING, sock_->ReadIfReady(buf.get(), len, read_callback_.callback())); } else { ASSERT_EQ(ERR_IO_PENDING, sock_->Read(buf.get(), len, read_callback_.callback())); } EXPECT_TRUE(sock_->IsConnected()); ResumeAndRun(); if (use_read_if_ready()) { EXPECT_EQ(OK, read_callback_.WaitForResult()); ASSERT_EQ(len, sock_->ReadIfReady(buf.get(), len, read_callback_.callback())); } else { EXPECT_EQ(len, read_callback_.WaitForResult()); } EXPECT_TRUE(sock_->IsConnected()); ASSERT_EQ(std::string(data, len), std::string(buf->data(), len)); } void SpdyProxyClientSocketTest::AssertReadStarts(const char* data, int len) { // Issue the read, which will be completed asynchronously. read_buf_ = base::MakeRefCounted(len); if (use_read_if_ready()) { ASSERT_EQ(ERR_IO_PENDING, sock_->ReadIfReady(read_buf_.get(), len, read_callback_.callback())); } else { ASSERT_EQ(ERR_IO_PENDING, sock_->Read(read_buf_.get(), len, read_callback_.callback())); } EXPECT_TRUE(sock_->IsConnected()); } void SpdyProxyClientSocketTest::AssertReadReturns(const char* data, int len) { EXPECT_TRUE(sock_->IsConnected()); // Now the read will return if (use_read_if_ready()) { EXPECT_EQ(OK, read_callback_.WaitForResult()); ASSERT_EQ(len, sock_->ReadIfReady(read_buf_.get(), len, read_callback_.callback())); } else { EXPECT_EQ(len, read_callback_.WaitForResult()); } ASSERT_EQ(std::string(data, len), std::string(read_buf_->data(), len)); } void SpdyProxyClientSocketTest::AssertAsyncWriteSucceeds(const char* data, int len) { AssertWriteReturns(data, len, ERR_IO_PENDING); AssertWriteLength(len); } void SpdyProxyClientSocketTest::AssertWriteReturns(const char* data, int len, int rv) { scoped_refptr buf(CreateBuffer(data, len)); EXPECT_EQ(rv, sock_->Write(buf.get(), buf->size(), write_callback_.callback(), TRAFFIC_ANNOTATION_FOR_TESTS)); } void SpdyProxyClientSocketTest::AssertWriteLength(int len) { EXPECT_EQ(len, write_callback_.WaitForResult()); } void SpdyProxyClientSocketTest::PopulateConnectRequestIR( spdy::SpdyHeaderBlock* block) { (*block)[spdy::kHttp2MethodHeader] = "CONNECT"; (*block)[spdy::kHttp2AuthorityHeader] = kOriginHostPort; (*block)["user-agent"] = kUserAgent; } void SpdyProxyClientSocketTest::PopulateConnectReplyIR( spdy::SpdyHeaderBlock* block, const char* status) { (*block)[spdy::kHttp2StatusHeader] = status; } // Constructs a standard SPDY HEADERS frame for a CONNECT request. spdy::SpdySerializedFrame SpdyProxyClientSocketTest::ConstructConnectRequestFrame( RequestPriority priority = LOWEST) { spdy::SpdyHeaderBlock block; PopulateConnectRequestIR(&block); return spdy_util_.ConstructSpdyHeaders(kStreamId, std::move(block), priority, false); } // Constructs a SPDY HEADERS frame for a CONNECT request which includes // Proxy-Authorization headers. spdy::SpdySerializedFrame SpdyProxyClientSocketTest::ConstructConnectAuthRequestFrame() { spdy::SpdyHeaderBlock block; PopulateConnectRequestIR(&block); block["proxy-authorization"] = "Basic Zm9vOmJhcg=="; return spdy_util_.ConstructSpdyHeaders(kStreamId, std::move(block), LOWEST, false); } // Constructs a standard SPDY HEADERS frame to match the SPDY CONNECT. spdy::SpdySerializedFrame SpdyProxyClientSocketTest::ConstructConnectReplyFrame() { spdy::SpdyHeaderBlock block; PopulateConnectReplyIR(&block, "200"); return spdy_util_.ConstructSpdyReply(kStreamId, std::move(block)); } // Constructs a standard SPDY HEADERS frame to match the SPDY CONNECT, // including Proxy-Authenticate headers. spdy::SpdySerializedFrame SpdyProxyClientSocketTest::ConstructConnectAuthReplyFrame() { spdy::SpdyHeaderBlock block; PopulateConnectReplyIR(&block, "407"); block["proxy-authenticate"] = "Basic realm=\"MyRealm1\""; return spdy_util_.ConstructSpdyReply(kStreamId, std::move(block)); } // Constructs a SPDY HEADERS frame with an HTTP 302 redirect. spdy::SpdySerializedFrame SpdyProxyClientSocketTest::ConstructConnectRedirectReplyFrame() { spdy::SpdyHeaderBlock block; PopulateConnectReplyIR(&block, "302"); block["location"] = kRedirectUrl; block["set-cookie"] = "foo=bar"; return spdy_util_.ConstructSpdyReply(kStreamId, std::move(block)); } // Constructs a SPDY HEADERS frame with an HTTP 500 error. spdy::SpdySerializedFrame SpdyProxyClientSocketTest::ConstructConnectErrorReplyFrame() { spdy::SpdyHeaderBlock block; PopulateConnectReplyIR(&block, "500"); return spdy_util_.ConstructSpdyReply(kStreamId, std::move(block)); } spdy::SpdySerializedFrame SpdyProxyClientSocketTest::ConstructBodyFrame( const char* data, int length) { return spdy_util_.ConstructSpdyDataFrame(kStreamId, base::StringPiece(data, length), /*fin=*/false); } // ----------- Connect INSTANTIATE_TEST_CASE_P(/* no prefix */, SpdyProxyClientSocketTest, ::testing::Bool()); TEST_P(SpdyProxyClientSocketTest, ConnectSendsCorrectRequest) { spdy::SpdySerializedFrame conn(ConstructConnectRequestFrame()); MockWrite writes[] = { CreateMockWrite(conn, 0, SYNCHRONOUS), }; spdy::SpdySerializedFrame resp(ConstructConnectReplyFrame()); MockRead reads[] = { CreateMockRead(resp, 1, ASYNC), MockRead(SYNCHRONOUS, ERR_IO_PENDING, 2), }; Initialize(reads, writes); ASSERT_FALSE(sock_->IsConnected()); AssertConnectSucceeds(); AssertConnectionEstablished(); } TEST_P(SpdyProxyClientSocketTest, ConnectWithAuthRequested) { spdy::SpdySerializedFrame conn(ConstructConnectRequestFrame()); MockWrite writes[] = { CreateMockWrite(conn, 0, SYNCHRONOUS), }; spdy::SpdySerializedFrame resp(ConstructConnectAuthReplyFrame()); MockRead reads[] = { CreateMockRead(resp, 1, ASYNC), MockRead(SYNCHRONOUS, ERR_IO_PENDING, 2), }; Initialize(reads, writes); AssertConnectFails(ERR_PROXY_AUTH_REQUESTED); const HttpResponseInfo* response = sock_->GetConnectResponseInfo(); ASSERT_TRUE(response != NULL); ASSERT_EQ(407, response->headers->response_code()); } TEST_P(SpdyProxyClientSocketTest, ConnectWithAuthCredentials) { spdy::SpdySerializedFrame conn(ConstructConnectAuthRequestFrame()); MockWrite writes[] = { CreateMockWrite(conn, 0, SYNCHRONOUS), }; spdy::SpdySerializedFrame resp(ConstructConnectReplyFrame()); MockRead reads[] = { CreateMockRead(resp, 1, ASYNC), MockRead(SYNCHRONOUS, ERR_IO_PENDING, 2), }; Initialize(reads, writes); AddAuthToCache(); AssertConnectSucceeds(); AssertConnectionEstablished(); } TEST_P(SpdyProxyClientSocketTest, ConnectRedirects) { spdy::SpdySerializedFrame conn(ConstructConnectRequestFrame()); spdy::SpdySerializedFrame rst( spdy_util_.ConstructSpdyRstStream(1, spdy::ERROR_CODE_CANCEL)); MockWrite writes[] = { CreateMockWrite(conn, 0, SYNCHRONOUS), CreateMockWrite(rst, 3), }; spdy::SpdySerializedFrame resp(ConstructConnectRedirectReplyFrame()); MockRead reads[] = { CreateMockRead(resp, 1, ASYNC), MockRead(SYNCHRONOUS, ERR_IO_PENDING, 2), }; Initialize(reads, writes); AssertConnectFails(ERR_HTTPS_PROXY_TUNNEL_RESPONSE); const HttpResponseInfo* response = sock_->GetConnectResponseInfo(); ASSERT_TRUE(response != NULL); const HttpResponseHeaders* headers = response->headers.get(); ASSERT_EQ(302, headers->response_code()); ASSERT_FALSE(headers->HasHeader("set-cookie")); ASSERT_TRUE(headers->HasHeaderValue("content-length", "0")); std::string location; ASSERT_TRUE(headers->IsRedirect(&location)); ASSERT_EQ(location, kRedirectUrl); // Let the RST_STREAM write while |rst| is in-scope. base::RunLoop().RunUntilIdle(); } TEST_P(SpdyProxyClientSocketTest, ConnectFails) { spdy::SpdySerializedFrame conn(ConstructConnectRequestFrame()); MockWrite writes[] = { CreateMockWrite(conn, 0, SYNCHRONOUS), }; spdy::SpdySerializedFrame resp(ConstructConnectReplyFrame()); MockRead reads[] = { MockRead(ASYNC, 0, 1), // EOF }; Initialize(reads, writes); ASSERT_FALSE(sock_->IsConnected()); AssertConnectFails(ERR_CONNECTION_CLOSED); ASSERT_FALSE(sock_->IsConnected()); } TEST_P(SpdyProxyClientSocketTest, SetStreamPriority) { spdy::SpdySerializedFrame conn(ConstructConnectRequestFrame(HIGHEST)); MockWrite writes[] = { CreateMockWrite(conn, 0, SYNCHRONOUS), }; spdy::SpdySerializedFrame resp(ConstructConnectReplyFrame()); MockRead reads[] = { CreateMockRead(resp, 1, ASYNC), MockRead(SYNCHRONOUS, ERR_IO_PENDING, 2), }; Initialize(reads, writes); sock_->SetStreamPriority(HIGHEST); AssertConnectSucceeds(); } // ----------- WasEverUsed TEST_P(SpdyProxyClientSocketTest, WasEverUsedReturnsCorrectValues) { spdy::SpdySerializedFrame conn(ConstructConnectRequestFrame()); spdy::SpdySerializedFrame rst( spdy_util_.ConstructSpdyRstStream(1, spdy::ERROR_CODE_CANCEL)); MockWrite writes[] = { CreateMockWrite(conn, 0, SYNCHRONOUS), CreateMockWrite(rst, 3), }; spdy::SpdySerializedFrame resp(ConstructConnectReplyFrame()); MockRead reads[] = { CreateMockRead(resp, 1, ASYNC), MockRead(SYNCHRONOUS, ERR_IO_PENDING, 2), }; Initialize(reads, writes); EXPECT_FALSE(sock_->WasEverUsed()); AssertConnectSucceeds(); EXPECT_TRUE(sock_->WasEverUsed()); sock_->Disconnect(); EXPECT_TRUE(sock_->WasEverUsed()); // Let the RST_STREAM write while |rst| is in-scope. base::RunLoop().RunUntilIdle(); } // ----------- GetPeerAddress TEST_P(SpdyProxyClientSocketTest, GetPeerAddressReturnsCorrectValues) { spdy::SpdySerializedFrame conn(ConstructConnectRequestFrame()); MockWrite writes[] = { CreateMockWrite(conn, 0, SYNCHRONOUS), }; spdy::SpdySerializedFrame resp(ConstructConnectReplyFrame()); MockRead reads[] = { CreateMockRead(resp, 1, ASYNC), MockRead(ASYNC, ERR_IO_PENDING, 2), MockRead(ASYNC, 0, 3), // EOF }; Initialize(reads, writes); IPEndPoint addr; EXPECT_THAT(sock_->GetPeerAddress(&addr), IsError(ERR_SOCKET_NOT_CONNECTED)); AssertConnectSucceeds(); EXPECT_TRUE(sock_->IsConnected()); EXPECT_THAT(sock_->GetPeerAddress(&addr), IsOk()); ResumeAndRun(); EXPECT_FALSE(sock_->IsConnected()); EXPECT_THAT(sock_->GetPeerAddress(&addr), IsError(ERR_SOCKET_NOT_CONNECTED)); sock_->Disconnect(); EXPECT_THAT(sock_->GetPeerAddress(&addr), IsError(ERR_SOCKET_NOT_CONNECTED)); } // ----------- Write TEST_P(SpdyProxyClientSocketTest, WriteSendsDataInDataFrame) { spdy::SpdySerializedFrame conn(ConstructConnectRequestFrame()); spdy::SpdySerializedFrame msg1(ConstructBodyFrame(kMsg1, kLen1)); spdy::SpdySerializedFrame msg2(ConstructBodyFrame(kMsg2, kLen2)); MockWrite writes[] = { CreateMockWrite(conn, 0, SYNCHRONOUS), CreateMockWrite(msg1, 3, SYNCHRONOUS), CreateMockWrite(msg2, 4, SYNCHRONOUS), }; spdy::SpdySerializedFrame resp(ConstructConnectReplyFrame()); MockRead reads[] = { CreateMockRead(resp, 1, ASYNC), MockRead(SYNCHRONOUS, ERR_IO_PENDING, 2), }; Initialize(reads, writes); AssertConnectSucceeds(); AssertAsyncWriteSucceeds(kMsg1, kLen1); AssertAsyncWriteSucceeds(kMsg2, kLen2); } TEST_P(SpdyProxyClientSocketTest, WriteSplitsLargeDataIntoMultipleFrames) { std::string chunk_data(kMaxSpdyFrameChunkSize, 'x'); spdy::SpdySerializedFrame conn(ConstructConnectRequestFrame()); spdy::SpdySerializedFrame chunk( ConstructBodyFrame(chunk_data.data(), chunk_data.length())); MockWrite writes[] = {CreateMockWrite(conn, 0, SYNCHRONOUS), CreateMockWrite(chunk, 3, SYNCHRONOUS), CreateMockWrite(chunk, 4, SYNCHRONOUS), CreateMockWrite(chunk, 5, SYNCHRONOUS)}; spdy::SpdySerializedFrame resp(ConstructConnectReplyFrame()); MockRead reads[] = { CreateMockRead(resp, 1, ASYNC), MockRead(SYNCHRONOUS, ERR_IO_PENDING, 2), }; Initialize(reads, writes); AssertConnectSucceeds(); std::string big_data(kMaxSpdyFrameChunkSize * 3, 'x'); scoped_refptr buf(CreateBuffer(big_data.data(), big_data.length())); EXPECT_EQ(ERR_IO_PENDING, sock_->Write(buf.get(), buf->size(), write_callback_.callback(), TRAFFIC_ANNOTATION_FOR_TESTS)); EXPECT_EQ(buf->size(), write_callback_.WaitForResult()); } // ----------- Read TEST_P(SpdyProxyClientSocketTest, ReadReadsDataInDataFrame) { spdy::SpdySerializedFrame conn(ConstructConnectRequestFrame()); MockWrite writes[] = { CreateMockWrite(conn, 0, SYNCHRONOUS), }; spdy::SpdySerializedFrame resp(ConstructConnectReplyFrame()); spdy::SpdySerializedFrame msg1(ConstructBodyFrame(kMsg1, kLen1)); MockRead reads[] = { CreateMockRead(resp, 1, ASYNC), MockRead(ASYNC, ERR_IO_PENDING, 2), CreateMockRead(msg1, 3, ASYNC), MockRead(SYNCHRONOUS, ERR_IO_PENDING, 4), }; Initialize(reads, writes); AssertConnectSucceeds(); // SpdySession consumes the next read and sends it to sock_ to be buffered. ResumeAndRun(); AssertSyncReadEquals(kMsg1, kLen1); } TEST_P(SpdyProxyClientSocketTest, ReadDataFromBufferedFrames) { spdy::SpdySerializedFrame conn(ConstructConnectRequestFrame()); MockWrite writes[] = { CreateMockWrite(conn, 0, SYNCHRONOUS), }; spdy::SpdySerializedFrame resp(ConstructConnectReplyFrame()); spdy::SpdySerializedFrame msg1(ConstructBodyFrame(kMsg1, kLen1)); spdy::SpdySerializedFrame msg2(ConstructBodyFrame(kMsg2, kLen2)); MockRead reads[] = { CreateMockRead(resp, 1, ASYNC), MockRead(ASYNC, ERR_IO_PENDING, 2), CreateMockRead(msg1, 3, ASYNC), MockRead(ASYNC, ERR_IO_PENDING, 4), CreateMockRead(msg2, 5, ASYNC), MockRead(SYNCHRONOUS, ERR_IO_PENDING, 6), }; Initialize(reads, writes); AssertConnectSucceeds(); // SpdySession consumes the next read and sends it to sock_ to be buffered. ResumeAndRun(); AssertSyncReadEquals(kMsg1, kLen1); // SpdySession consumes the next read and sends it to sock_ to be buffered. ResumeAndRun(); AssertSyncReadEquals(kMsg2, kLen2); } TEST_P(SpdyProxyClientSocketTest, ReadDataMultipleBufferedFrames) { spdy::SpdySerializedFrame conn(ConstructConnectRequestFrame()); MockWrite writes[] = { CreateMockWrite(conn, 0, SYNCHRONOUS), }; spdy::SpdySerializedFrame resp(ConstructConnectReplyFrame()); spdy::SpdySerializedFrame msg1(ConstructBodyFrame(kMsg1, kLen1)); spdy::SpdySerializedFrame msg2(ConstructBodyFrame(kMsg2, kLen2)); MockRead reads[] = { CreateMockRead(resp, 1, ASYNC), MockRead(ASYNC, ERR_IO_PENDING, 2), CreateMockRead(msg1, 3, ASYNC), CreateMockRead(msg2, 4, ASYNC), MockRead(SYNCHRONOUS, ERR_IO_PENDING, 5), }; Initialize(reads, writes); AssertConnectSucceeds(); // SpdySession consumes the next two reads and sends then to sock_ to be // buffered. ResumeAndRun(); AssertSyncReadEquals(kMsg1, kLen1); AssertSyncReadEquals(kMsg2, kLen2); } TEST_P(SpdyProxyClientSocketTest, LargeReadWillMergeDataFromDifferentFrames) { spdy::SpdySerializedFrame conn(ConstructConnectRequestFrame()); MockWrite writes[] = { CreateMockWrite(conn, 0, SYNCHRONOUS), }; spdy::SpdySerializedFrame resp(ConstructConnectReplyFrame()); spdy::SpdySerializedFrame msg1(ConstructBodyFrame(kMsg1, kLen1)); spdy::SpdySerializedFrame msg3(ConstructBodyFrame(kMsg3, kLen3)); MockRead reads[] = { CreateMockRead(resp, 1, ASYNC), MockRead(ASYNC, ERR_IO_PENDING, 2), CreateMockRead(msg3, 3, ASYNC), CreateMockRead(msg3, 4, ASYNC), MockRead(SYNCHRONOUS, ERR_IO_PENDING, 5), }; Initialize(reads, writes); AssertConnectSucceeds(); // SpdySession consumes the next two reads and sends then to sock_ to be // buffered. ResumeAndRun(); // The payload from two data frames, each with kMsg3 will be combined // together into a single read(). AssertSyncReadEquals(kMsg33, kLen33); } TEST_P(SpdyProxyClientSocketTest, MultipleShortReadsThenMoreRead) { spdy::SpdySerializedFrame conn(ConstructConnectRequestFrame()); MockWrite writes[] = { CreateMockWrite(conn, 0, SYNCHRONOUS), }; spdy::SpdySerializedFrame resp(ConstructConnectReplyFrame()); spdy::SpdySerializedFrame msg1(ConstructBodyFrame(kMsg1, kLen1)); spdy::SpdySerializedFrame msg3(ConstructBodyFrame(kMsg3, kLen3)); spdy::SpdySerializedFrame msg2(ConstructBodyFrame(kMsg2, kLen2)); MockRead reads[] = { CreateMockRead(resp, 1, ASYNC), MockRead(ASYNC, ERR_IO_PENDING, 2), CreateMockRead(msg1, 3, ASYNC), CreateMockRead(msg3, 4, ASYNC), CreateMockRead(msg3, 5, ASYNC), CreateMockRead(msg2, 6, ASYNC), MockRead(SYNCHRONOUS, ERR_IO_PENDING, 7), }; Initialize(reads, writes); AssertConnectSucceeds(); // SpdySession consumes the next four reads and sends then to sock_ to be // buffered. ResumeAndRun(); AssertSyncReadEquals(kMsg1, kLen1); // The payload from two data frames, each with kMsg3 will be combined // together into a single read(). AssertSyncReadEquals(kMsg33, kLen33); AssertSyncReadEquals(kMsg2, kLen2); } TEST_P(SpdyProxyClientSocketTest, ReadWillSplitDataFromLargeFrame) { spdy::SpdySerializedFrame conn(ConstructConnectRequestFrame()); MockWrite writes[] = { CreateMockWrite(conn, 0, SYNCHRONOUS), }; spdy::SpdySerializedFrame resp(ConstructConnectReplyFrame()); spdy::SpdySerializedFrame msg1(ConstructBodyFrame(kMsg1, kLen1)); spdy::SpdySerializedFrame msg33(ConstructBodyFrame(kMsg33, kLen33)); spdy::SpdySerializedFrame msg2(ConstructBodyFrame(kMsg2, kLen2)); MockRead reads[] = { CreateMockRead(resp, 1, ASYNC), MockRead(ASYNC, ERR_IO_PENDING, 2), CreateMockRead(msg1, 3, ASYNC), CreateMockRead(msg33, 4, ASYNC), MockRead(SYNCHRONOUS, ERR_IO_PENDING, 5), }; Initialize(reads, writes); AssertConnectSucceeds(); // SpdySession consumes the next two reads and sends then to sock_ to be // buffered. ResumeAndRun(); AssertSyncReadEquals(kMsg1, kLen1); // The payload from the single large data frame will be read across // two different reads. AssertSyncReadEquals(kMsg3, kLen3); AssertSyncReadEquals(kMsg3, kLen3); } TEST_P(SpdyProxyClientSocketTest, MultipleReadsFromSameLargeFrame) { spdy::SpdySerializedFrame conn(ConstructConnectRequestFrame()); MockWrite writes[] = { CreateMockWrite(conn, 0, SYNCHRONOUS), }; spdy::SpdySerializedFrame resp(ConstructConnectReplyFrame()); spdy::SpdySerializedFrame msg333(ConstructBodyFrame(kMsg333, kLen333)); MockRead reads[] = { CreateMockRead(resp, 1, ASYNC), MockRead(ASYNC, ERR_IO_PENDING, 2), CreateMockRead(msg333, 3, ASYNC), MockRead(SYNCHRONOUS, ERR_IO_PENDING, 4), }; Initialize(reads, writes); AssertConnectSucceeds(); // SpdySession consumes the next read and sends it to sock_ to be buffered. ResumeAndRun(); // The payload from the single large data frame will be read across // two different reads. AssertSyncReadEquals(kMsg33, kLen33); // Now attempt to do a read of more data than remains buffered AssertSyncReadEquals(kMsg3, kLen3); ASSERT_TRUE(sock_->IsConnected()); } TEST_P(SpdyProxyClientSocketTest, ReadAuthResponseBody) { spdy::SpdySerializedFrame conn(ConstructConnectRequestFrame()); MockWrite writes[] = { CreateMockWrite(conn, 0, SYNCHRONOUS), }; spdy::SpdySerializedFrame resp(ConstructConnectAuthReplyFrame()); spdy::SpdySerializedFrame msg1(ConstructBodyFrame(kMsg1, kLen1)); spdy::SpdySerializedFrame msg2(ConstructBodyFrame(kMsg2, kLen2)); MockRead reads[] = { CreateMockRead(resp, 1, ASYNC), MockRead(ASYNC, ERR_IO_PENDING, 2), CreateMockRead(msg1, 3, ASYNC), CreateMockRead(msg2, 4, ASYNC), MockRead(SYNCHRONOUS, ERR_IO_PENDING, 5), }; Initialize(reads, writes); AssertConnectFails(ERR_PROXY_AUTH_REQUESTED); // SpdySession consumes the next two reads and sends then to sock_ to be // buffered. ResumeAndRun(); AssertSyncReadEquals(kMsg1, kLen1); AssertSyncReadEquals(kMsg2, kLen2); } TEST_P(SpdyProxyClientSocketTest, ReadErrorResponseBody) { spdy::SpdySerializedFrame conn(ConstructConnectRequestFrame()); MockWrite writes[] = { CreateMockWrite(conn, 0, SYNCHRONOUS), }; spdy::SpdySerializedFrame resp(ConstructConnectErrorReplyFrame()); spdy::SpdySerializedFrame msg1(ConstructBodyFrame(kMsg1, kLen1)); spdy::SpdySerializedFrame msg2(ConstructBodyFrame(kMsg2, kLen2)); MockRead reads[] = { CreateMockRead(resp, 1, ASYNC), CreateMockRead(msg1, 2, SYNCHRONOUS), CreateMockRead(msg2, 3, SYNCHRONOUS), MockRead(SYNCHRONOUS, 0, 4), // EOF }; Initialize(reads, writes); AssertConnectFails(ERR_TUNNEL_CONNECTION_FAILED); } TEST_P(SpdyProxyClientSocketTest, SocketDestroyedWhenReadIsPending) { spdy::SpdySerializedFrame conn(ConstructConnectRequestFrame()); MockWrite writes[] = {CreateMockWrite(conn, 0, SYNCHRONOUS)}; spdy::SpdySerializedFrame resp(ConstructConnectReplyFrame()); spdy::SpdySerializedFrame msg1(ConstructBodyFrame(kMsg1, kLen1)); MockRead reads[] = { CreateMockRead(resp, 1, ASYNC), CreateMockRead(msg1, 2, ASYNC), MockRead(SYNCHRONOUS, ERR_IO_PENDING, 3), }; Initialize(reads, writes); AssertConnectSucceeds(); // Make Read()/ReadIfReady() pending. AssertReadStarts(kMsg1, kLen1); // Destroying socket. sock_ = nullptr; // Read data is not consumed. EXPECT_TRUE(data_->AllWriteDataConsumed()); EXPECT_FALSE(data_->AllReadDataConsumed()); // Reset |data_| so the test destructor doesn't check it. data_ = nullptr; } // ----------- Reads and Writes TEST_P(SpdyProxyClientSocketTest, AsyncReadAroundWrite) { spdy::SpdySerializedFrame conn(ConstructConnectRequestFrame()); spdy::SpdySerializedFrame msg2(ConstructBodyFrame(kMsg2, kLen2)); MockWrite writes[] = { CreateMockWrite(conn, 0, SYNCHRONOUS), CreateMockWrite(msg2, 4, SYNCHRONOUS), }; spdy::SpdySerializedFrame resp(ConstructConnectReplyFrame()); spdy::SpdySerializedFrame msg1(ConstructBodyFrame(kMsg1, kLen1)); spdy::SpdySerializedFrame msg3(ConstructBodyFrame(kMsg3, kLen3)); MockRead reads[] = { CreateMockRead(resp, 1, ASYNC), MockRead(ASYNC, ERR_IO_PENDING, 2), CreateMockRead(msg1, 3, ASYNC), // sync read MockRead(ASYNC, ERR_IO_PENDING, 5), CreateMockRead(msg3, 6, ASYNC), // async read MockRead(SYNCHRONOUS, ERR_IO_PENDING, 7), }; Initialize(reads, writes); AssertConnectSucceeds(); ResumeAndRun(); AssertSyncReadEquals(kMsg1, kLen1); AssertReadStarts(kMsg3, kLen3); // Read should block until after the write succeeds. AssertAsyncWriteSucceeds(kMsg2, kLen2); // Advances past paused read. ASSERT_FALSE(read_callback_.have_result()); ResumeAndRun(); // Now the read will return. AssertReadReturns(kMsg3, kLen3); } TEST_P(SpdyProxyClientSocketTest, AsyncWriteAroundReads) { spdy::SpdySerializedFrame conn(ConstructConnectRequestFrame()); spdy::SpdySerializedFrame msg2(ConstructBodyFrame(kMsg2, kLen2)); MockWrite writes[] = { CreateMockWrite(conn, 0, SYNCHRONOUS), MockWrite(ASYNC, ERR_IO_PENDING, 7), CreateMockWrite(msg2, 8, ASYNC), }; spdy::SpdySerializedFrame resp(ConstructConnectReplyFrame()); spdy::SpdySerializedFrame msg1(ConstructBodyFrame(kMsg1, kLen1)); spdy::SpdySerializedFrame msg3(ConstructBodyFrame(kMsg3, kLen3)); MockRead reads[] = { CreateMockRead(resp, 1, ASYNC), MockRead(ASYNC, ERR_IO_PENDING, 2), CreateMockRead(msg1, 3, ASYNC), MockRead(ASYNC, ERR_IO_PENDING, 4), CreateMockRead(msg3, 5, ASYNC), MockRead(SYNCHRONOUS, ERR_IO_PENDING, 6), }; Initialize(reads, writes); AssertConnectSucceeds(); ResumeAndRun(); AssertSyncReadEquals(kMsg1, kLen1); // Write should block until the read completes AssertWriteReturns(kMsg2, kLen2, ERR_IO_PENDING); AssertAsyncReadEquals(kMsg3, kLen3); ASSERT_FALSE(write_callback_.have_result()); // Now the write will complete ResumeAndRun(); AssertWriteLength(kLen2); } // ----------- Reading/Writing on Closed socket // Reading from an already closed socket should return 0 TEST_P(SpdyProxyClientSocketTest, ReadOnClosedSocketReturnsZero) { spdy::SpdySerializedFrame conn(ConstructConnectRequestFrame()); MockWrite writes[] = { CreateMockWrite(conn, 0, SYNCHRONOUS), }; spdy::SpdySerializedFrame resp(ConstructConnectReplyFrame()); MockRead reads[] = { CreateMockRead(resp, 1, ASYNC), MockRead(ASYNC, ERR_IO_PENDING, 2), MockRead(ASYNC, 0, 3), // EOF }; Initialize(reads, writes); AssertConnectSucceeds(); ResumeAndRun(); ASSERT_FALSE(sock_->IsConnected()); AssertSyncReadEOF(); AssertSyncReadEOF(); AssertSyncReadEOF(); ASSERT_FALSE(sock_->IsConnectedAndIdle()); } // Read pending when socket is closed should return 0 TEST_P(SpdyProxyClientSocketTest, PendingReadOnCloseReturnsZero) { spdy::SpdySerializedFrame conn(ConstructConnectRequestFrame()); MockWrite writes[] = { CreateMockWrite(conn, 0, SYNCHRONOUS), }; spdy::SpdySerializedFrame resp(ConstructConnectReplyFrame()); MockRead reads[] = { CreateMockRead(resp, 1, ASYNC), MockRead(ASYNC, ERR_IO_PENDING, 2), MockRead(ASYNC, 0, 3), // EOF }; Initialize(reads, writes); AssertConnectSucceeds(); AssertReadStarts(kMsg1, kLen1); ResumeAndRun(); ASSERT_EQ(0, read_callback_.WaitForResult()); } // Reading from a disconnected socket is an error TEST_P(SpdyProxyClientSocketTest, ReadOnDisconnectSocketReturnsNotConnected) { spdy::SpdySerializedFrame conn(ConstructConnectRequestFrame()); spdy::SpdySerializedFrame rst( spdy_util_.ConstructSpdyRstStream(1, spdy::ERROR_CODE_CANCEL)); MockWrite writes[] = { CreateMockWrite(conn, 0, SYNCHRONOUS), CreateMockWrite(rst, 3), }; spdy::SpdySerializedFrame resp(ConstructConnectReplyFrame()); MockRead reads[] = { CreateMockRead(resp, 1, ASYNC), MockRead(SYNCHRONOUS, ERR_IO_PENDING, 2), }; Initialize(reads, writes); AssertConnectSucceeds(); sock_->Disconnect(); if (use_read_if_ready()) { ASSERT_EQ(ERR_SOCKET_NOT_CONNECTED, sock_->ReadIfReady(NULL, 1, CompletionOnceCallback())); } else { ASSERT_EQ(ERR_SOCKET_NOT_CONNECTED, sock_->Read(NULL, 1, CompletionOnceCallback())); } // Let the RST_STREAM write while |rst| is in-scope. base::RunLoop().RunUntilIdle(); } // Reading buffered data from an already closed socket should return // buffered data, then 0. TEST_P(SpdyProxyClientSocketTest, ReadOnClosedSocketReturnsBufferedData) { spdy::SpdySerializedFrame conn(ConstructConnectRequestFrame()); MockWrite writes[] = { CreateMockWrite(conn, 0, SYNCHRONOUS), }; spdy::SpdySerializedFrame resp(ConstructConnectReplyFrame()); spdy::SpdySerializedFrame msg1(ConstructBodyFrame(kMsg1, kLen1)); MockRead reads[] = { CreateMockRead(resp, 1, ASYNC), MockRead(ASYNC, ERR_IO_PENDING, 2), CreateMockRead(msg1, 3, ASYNC), MockRead(ASYNC, 0, 4), // EOF }; Initialize(reads, writes); AssertConnectSucceeds(); ResumeAndRun(); ASSERT_FALSE(sock_->IsConnected()); scoped_refptr buf = base::MakeRefCounted(kLen1); ASSERT_EQ(kLen1, sock_->Read(buf.get(), kLen1, CompletionOnceCallback())); ASSERT_EQ(std::string(kMsg1, kLen1), std::string(buf->data(), kLen1)); ASSERT_EQ(0, sock_->Read(NULL, 1, CompletionOnceCallback())); ASSERT_EQ(0, sock_->Read(NULL, 1, CompletionOnceCallback())); sock_->Disconnect(); ASSERT_EQ(ERR_SOCKET_NOT_CONNECTED, sock_->Read(NULL, 1, CompletionOnceCallback())); } // Calling Write() on a closed socket is an error TEST_P(SpdyProxyClientSocketTest, WriteOnClosedStream) { spdy::SpdySerializedFrame conn(ConstructConnectRequestFrame()); MockWrite writes[] = { CreateMockWrite(conn, 0, SYNCHRONOUS), }; spdy::SpdySerializedFrame resp(ConstructConnectReplyFrame()); spdy::SpdySerializedFrame msg1(ConstructBodyFrame(kMsg1, kLen1)); MockRead reads[] = { CreateMockRead(resp, 1, ASYNC), MockRead(ASYNC, ERR_IO_PENDING, 2), MockRead(ASYNC, 0, 3), // EOF }; Initialize(reads, writes); AssertConnectSucceeds(); // Read EOF which will close the stream. ResumeAndRun(); scoped_refptr buf(CreateBuffer(kMsg1, kLen1)); EXPECT_EQ(ERR_SOCKET_NOT_CONNECTED, sock_->Write(buf.get(), buf->size(), CompletionOnceCallback(), TRAFFIC_ANNOTATION_FOR_TESTS)); } // Calling Write() on a disconnected socket is an error. TEST_P(SpdyProxyClientSocketTest, WriteOnDisconnectedSocket) { spdy::SpdySerializedFrame conn(ConstructConnectRequestFrame()); spdy::SpdySerializedFrame rst( spdy_util_.ConstructSpdyRstStream(1, spdy::ERROR_CODE_CANCEL)); MockWrite writes[] = { CreateMockWrite(conn, 0, SYNCHRONOUS), CreateMockWrite(rst, 3), }; spdy::SpdySerializedFrame resp(ConstructConnectReplyFrame()); spdy::SpdySerializedFrame msg1(ConstructBodyFrame(kMsg1, kLen1)); MockRead reads[] = { CreateMockRead(resp, 1, ASYNC), MockRead(SYNCHRONOUS, ERR_IO_PENDING, 2), }; Initialize(reads, writes); AssertConnectSucceeds(); sock_->Disconnect(); scoped_refptr buf(CreateBuffer(kMsg1, kLen1)); EXPECT_EQ(ERR_SOCKET_NOT_CONNECTED, sock_->Write(buf.get(), buf->size(), CompletionOnceCallback(), TRAFFIC_ANNOTATION_FOR_TESTS)); // Let the RST_STREAM write while |rst| is in-scope. base::RunLoop().RunUntilIdle(); } // If the socket is closed with a pending Write(), the callback // should be called with ERR_CONNECTION_CLOSED. TEST_P(SpdyProxyClientSocketTest, WritePendingOnClose) { spdy::SpdySerializedFrame conn(ConstructConnectRequestFrame()); MockWrite writes[] = { CreateMockWrite(conn, 0, SYNCHRONOUS), MockWrite(SYNCHRONOUS, ERR_IO_PENDING, 3), }; spdy::SpdySerializedFrame resp(ConstructConnectReplyFrame()); MockRead reads[] = { CreateMockRead(resp, 1, ASYNC), MockRead(SYNCHRONOUS, ERR_IO_PENDING, 2), }; Initialize(reads, writes); AssertConnectSucceeds(); EXPECT_TRUE(sock_->IsConnected()); scoped_refptr buf(CreateBuffer(kMsg1, kLen1)); EXPECT_EQ(ERR_IO_PENDING, sock_->Write(buf.get(), buf->size(), write_callback_.callback(), TRAFFIC_ANNOTATION_FOR_TESTS)); // Make sure the write actually starts. base::RunLoop().RunUntilIdle(); CloseSpdySession(ERR_ABORTED, std::string()); EXPECT_THAT(write_callback_.WaitForResult(), IsError(ERR_CONNECTION_CLOSED)); } // If the socket is Disconnected with a pending Write(), the callback // should not be called. TEST_P(SpdyProxyClientSocketTest, DisconnectWithWritePending) { spdy::SpdySerializedFrame conn(ConstructConnectRequestFrame()); spdy::SpdySerializedFrame rst( spdy_util_.ConstructSpdyRstStream(1, spdy::ERROR_CODE_CANCEL)); MockWrite writes[] = { CreateMockWrite(conn, 0, SYNCHRONOUS), CreateMockWrite(rst, 3), }; spdy::SpdySerializedFrame resp(ConstructConnectReplyFrame()); MockRead reads[] = { CreateMockRead(resp, 1, ASYNC), MockRead(SYNCHRONOUS, ERR_IO_PENDING, 2), }; Initialize(reads, writes); AssertConnectSucceeds(); EXPECT_TRUE(sock_->IsConnected()); scoped_refptr buf(CreateBuffer(kMsg1, kLen1)); EXPECT_EQ(ERR_IO_PENDING, sock_->Write(buf.get(), buf->size(), write_callback_.callback(), TRAFFIC_ANNOTATION_FOR_TESTS)); sock_->Disconnect(); EXPECT_FALSE(sock_->IsConnected()); EXPECT_FALSE(write_callback_.have_result()); // Let the RST_STREAM write while |rst| is in-scope. base::RunLoop().RunUntilIdle(); } // If the socket is Disconnected with a pending Read(), the callback // should not be called. TEST_P(SpdyProxyClientSocketTest, DisconnectWithReadPending) { spdy::SpdySerializedFrame conn(ConstructConnectRequestFrame()); spdy::SpdySerializedFrame rst( spdy_util_.ConstructSpdyRstStream(1, spdy::ERROR_CODE_CANCEL)); MockWrite writes[] = { CreateMockWrite(conn, 0, SYNCHRONOUS), CreateMockWrite(rst, 3), }; spdy::SpdySerializedFrame resp(ConstructConnectReplyFrame()); MockRead reads[] = { CreateMockRead(resp, 1, ASYNC), MockRead(SYNCHRONOUS, ERR_IO_PENDING, 2), }; Initialize(reads, writes); AssertConnectSucceeds(); EXPECT_TRUE(sock_->IsConnected()); scoped_refptr buf = base::MakeRefCounted(kLen1); ASSERT_EQ(ERR_IO_PENDING, sock_->Read(buf.get(), kLen1, read_callback_.callback())); sock_->Disconnect(); EXPECT_FALSE(sock_->IsConnected()); EXPECT_FALSE(read_callback_.have_result()); // Let the RST_STREAM write while |rst| is in-scope. base::RunLoop().RunUntilIdle(); } // If the socket is Reset when both a read and write are pending, // both should be called back. TEST_P(SpdyProxyClientSocketTest, RstWithReadAndWritePending) { spdy::SpdySerializedFrame conn(ConstructConnectRequestFrame()); MockWrite writes[] = { CreateMockWrite(conn, 0, SYNCHRONOUS), }; spdy::SpdySerializedFrame resp(ConstructConnectReplyFrame()); spdy::SpdySerializedFrame rst( spdy_util_.ConstructSpdyRstStream(1, spdy::ERROR_CODE_CANCEL)); MockRead reads[] = { CreateMockRead(resp, 1, ASYNC), MockRead(ASYNC, ERR_IO_PENDING, 2), CreateMockRead(rst, 3, ASYNC), MockRead(ASYNC, 0, 4) // EOF }; Initialize(reads, writes); AssertConnectSucceeds(); EXPECT_TRUE(sock_->IsConnected()); scoped_refptr read_buf = base::MakeRefCounted(kLen1); ASSERT_EQ(ERR_IO_PENDING, sock_->Read(read_buf.get(), kLen1, read_callback_.callback())); scoped_refptr write_buf(CreateBuffer(kMsg1, kLen1)); EXPECT_EQ(ERR_IO_PENDING, sock_->Write(write_buf.get(), write_buf->size(), write_callback_.callback(), TRAFFIC_ANNOTATION_FOR_TESTS)); ResumeAndRun(); EXPECT_TRUE(sock_.get()); EXPECT_TRUE(read_callback_.have_result()); EXPECT_TRUE(write_callback_.have_result()); // Let the RST_STREAM write while |rst| is in-scope. base::RunLoop().RunUntilIdle(); } // Makes sure the proxy client socket's source gets the expected NetLog events // and only the expected NetLog events (No SpdySession events). TEST_P(SpdyProxyClientSocketTest, NetLog) { spdy::SpdySerializedFrame conn(ConstructConnectRequestFrame()); spdy::SpdySerializedFrame rst( spdy_util_.ConstructSpdyRstStream(1, spdy::ERROR_CODE_CANCEL)); MockWrite writes[] = { CreateMockWrite(conn, 0, SYNCHRONOUS), CreateMockWrite(rst, 5), }; spdy::SpdySerializedFrame resp(ConstructConnectReplyFrame()); spdy::SpdySerializedFrame msg1(ConstructBodyFrame(kMsg1, kLen1)); MockRead reads[] = { CreateMockRead(resp, 1, ASYNC), MockRead(ASYNC, ERR_IO_PENDING, 2), CreateMockRead(msg1, 3, ASYNC), MockRead(SYNCHRONOUS, ERR_IO_PENDING, 4), }; Initialize(reads, writes); AssertConnectSucceeds(); // SpdySession consumes the next read and sends it to sock_ to be buffered. ResumeAndRun(); AssertSyncReadEquals(kMsg1, kLen1); NetLogSource sock_source = sock_->NetLog().source(); sock_.reset(); TestNetLogEntry::List entry_list; net_log_.GetEntriesForSource(sock_source, &entry_list); ASSERT_EQ(entry_list.size(), 10u); EXPECT_TRUE( LogContainsBeginEvent(entry_list, 0, NetLogEventType::SOCKET_ALIVE)); EXPECT_TRUE(LogContainsEvent(entry_list, 1, NetLogEventType::HTTP2_PROXY_CLIENT_SESSION, NetLogEventPhase::NONE)); EXPECT_TRUE(LogContainsBeginEvent( entry_list, 2, NetLogEventType::HTTP_TRANSACTION_TUNNEL_SEND_REQUEST)); EXPECT_TRUE(LogContainsEvent( entry_list, 3, NetLogEventType::HTTP_TRANSACTION_SEND_TUNNEL_HEADERS, NetLogEventPhase::NONE)); EXPECT_TRUE(LogContainsEndEvent( entry_list, 4, NetLogEventType::HTTP_TRANSACTION_TUNNEL_SEND_REQUEST)); EXPECT_TRUE(LogContainsBeginEvent( entry_list, 5, NetLogEventType::HTTP_TRANSACTION_TUNNEL_READ_HEADERS)); EXPECT_TRUE(LogContainsEvent( entry_list, 6, NetLogEventType::HTTP_TRANSACTION_READ_TUNNEL_RESPONSE_HEADERS, NetLogEventPhase::NONE)); EXPECT_TRUE(LogContainsEndEvent( entry_list, 7, NetLogEventType::HTTP_TRANSACTION_TUNNEL_READ_HEADERS)); EXPECT_TRUE(LogContainsEvent(entry_list, 8, NetLogEventType::SOCKET_BYTES_RECEIVED, NetLogEventPhase::NONE)); EXPECT_TRUE( LogContainsEndEvent(entry_list, 9, NetLogEventType::SOCKET_ALIVE)); // Let the RST_STREAM write while |rst| is in-scope. base::RunLoop().RunUntilIdle(); } // A helper class that will delete |sock| when the callback is invoked. class DeleteSockCallback : public TestCompletionCallbackBase { public: explicit DeleteSockCallback(std::unique_ptr* sock) : sock_(sock) {} ~DeleteSockCallback() override = default; CompletionOnceCallback callback() { return base::BindOnce(&DeleteSockCallback::OnComplete, base::Unretained(this)); } private: void OnComplete(int result) { sock_->reset(NULL); SetResult(result); } std::unique_ptr* sock_; DISALLOW_COPY_AND_ASSIGN(DeleteSockCallback); }; // If the socket is Reset when both a read and write are pending, and the // read callback causes the socket to be deleted, the write callback should // not be called. TEST_P(SpdyProxyClientSocketTest, RstWithReadAndWritePendingDelete) { spdy::SpdySerializedFrame conn(ConstructConnectRequestFrame()); MockWrite writes[] = { CreateMockWrite(conn, 0, SYNCHRONOUS), }; spdy::SpdySerializedFrame resp(ConstructConnectReplyFrame()); spdy::SpdySerializedFrame rst( spdy_util_.ConstructSpdyRstStream(1, spdy::ERROR_CODE_CANCEL)); MockRead reads[] = { CreateMockRead(resp, 1, ASYNC), MockRead(ASYNC, ERR_IO_PENDING, 2), CreateMockRead(rst, 3, ASYNC), MockRead(SYNCHRONOUS, ERR_IO_PENDING, 4), }; Initialize(reads, writes); AssertConnectSucceeds(); EXPECT_TRUE(sock_->IsConnected()); DeleteSockCallback read_callback(&sock_); scoped_refptr read_buf = base::MakeRefCounted(kLen1); ASSERT_EQ(ERR_IO_PENDING, sock_->Read(read_buf.get(), kLen1, read_callback.callback())); scoped_refptr write_buf(CreateBuffer(kMsg1, kLen1)); EXPECT_EQ(ERR_IO_PENDING, sock_->Write(write_buf.get(), write_buf->size(), write_callback_.callback(), TRAFFIC_ANNOTATION_FOR_TESTS)); ResumeAndRun(); EXPECT_FALSE(sock_.get()); EXPECT_TRUE(read_callback.have_result()); EXPECT_FALSE(write_callback_.have_result()); // Let the RST_STREAM write while |rst| is in-scope. base::RunLoop().RunUntilIdle(); } // ----------- Canceling a ReadIfReady TEST_P(SpdyProxyClientSocketTest, CancelReadIfReady) { // Not relevant if not ReadIfReady(). if (!use_read_if_ready()) return; spdy::SpdySerializedFrame conn(ConstructConnectRequestFrame()); MockWrite writes[] = {CreateMockWrite(conn, 0, SYNCHRONOUS)}; spdy::SpdySerializedFrame resp(ConstructConnectReplyFrame()); spdy::SpdySerializedFrame msg1(ConstructBodyFrame(kMsg1, kLen1)); spdy::SpdySerializedFrame msg3(ConstructBodyFrame(kMsg3, kLen3)); MockRead reads[] = { CreateMockRead(resp, 1, ASYNC), CreateMockRead(msg1, 2, ASYNC), CreateMockRead(msg3, 3, ASYNC), MockRead(SYNCHRONOUS, ERR_IO_PENDING, 4), }; Initialize(reads, writes); AssertConnectSucceeds(); AssertReadStarts(kMsg1, kLen1); EXPECT_EQ(OK, sock_->CancelReadIfReady()); // Perform ReadIfReady again should succeed after cancelation. AssertReadStarts(kMsg1, kLen1); AssertReadReturns(kMsg1, kLen1); AssertReadStarts(kMsg3, kLen3); AssertReadReturns(kMsg3, kLen3); // Canceling ReadIfReady() when none is in progress is an no-op. EXPECT_EQ(OK, sock_->CancelReadIfReady()); } } // namespace net