blob: a7be45400a71252ab8be455555e0724c94db589c [file] [log] [blame]
// Copyright 2018 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/websockets/websocket_basic_stream_adapters.h"
#include <utility>
#include "base/bind.h"
#include "base/memory/scoped_refptr.h"
#include "base/memory/weak_ptr.h"
#include "base/optional.h"
#include "base/run_loop.h"
#include "base/strings/string_piece.h"
#include "net/base/host_port_pair.h"
#include "net/base/io_buffer.h"
#include "net/base/net_errors.h"
#include "net/base/privacy_mode.h"
#include "net/base/proxy_server.h"
#include "net/base/test_completion_callback.h"
#include "net/dns/mock_host_resolver.h"
#include "net/http/http_network_session.h"
#include "net/log/net_log_with_source.h"
#include "net/socket/client_socket_handle.h"
#include "net/socket/connect_job.h"
#include "net/socket/socket_tag.h"
#include "net/socket/socket_test_util.h"
#include "net/socket/ssl_client_socket.h"
#include "net/socket/transport_client_socket_pool.h"
#include "net/socket/websocket_endpoint_lock_manager.h"
#include "net/spdy/spdy_session.h"
#include "net/spdy/spdy_session_key.h"
#include "net/spdy/spdy_test_util_common.h"
#include "net/ssl/ssl_config.h"
#include "net/ssl/ssl_info.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_task_environment.h"
#include "net/traffic_annotation/network_traffic_annotation_test_helper.h"
#include "net/websockets/websocket_test_util.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
using testing::Test;
using testing::StrictMock;
using testing::_;
namespace net {
namespace test {
class WebSocketClientSocketHandleAdapterTest : public TestWithTaskEnvironment {
protected:
WebSocketClientSocketHandleAdapterTest()
: host_port_pair_("www.example.org", 443),
network_session_(
SpdySessionDependencies::SpdyCreateSession(&session_deps_)),
websocket_endpoint_lock_manager_(
network_session_->websocket_endpoint_lock_manager()) {}
~WebSocketClientSocketHandleAdapterTest() override = default;
bool InitClientSocketHandle(ClientSocketHandle* connection) {
scoped_refptr<ClientSocketPool::SocketParams> socks_params =
base::MakeRefCounted<ClientSocketPool::SocketParams>(
std::make_unique<SSLConfig>() /* ssl_config_for_origin */,
nullptr /* ssl_config_for_proxy */);
TestCompletionCallback callback;
int rv = connection->Init(
ClientSocketPool::GroupId(
host_port_pair_, ClientSocketPool::SocketType::kSsl,
PrivacyMode::PRIVACY_MODE_DISABLED, NetworkIsolationKey(),
false /* disable_secure_dns */),
socks_params, TRAFFIC_ANNOTATION_FOR_TESTS /* proxy_annotation_tag */,
MEDIUM, SocketTag(), ClientSocketPool::RespectLimits::ENABLED,
callback.callback(), ClientSocketPool::ProxyAuthCallback(),
network_session_->GetSocketPool(HttpNetworkSession::NORMAL_SOCKET_POOL,
ProxyServer::Direct()),
NetLogWithSource());
rv = callback.GetResult(rv);
return rv == OK;
}
const HostPortPair host_port_pair_;
SpdySessionDependencies session_deps_;
std::unique_ptr<HttpNetworkSession> network_session_;
WebSocketEndpointLockManager* websocket_endpoint_lock_manager_;
};
TEST_F(WebSocketClientSocketHandleAdapterTest, Uninitialized) {
auto connection = std::make_unique<ClientSocketHandle>();
WebSocketClientSocketHandleAdapter adapter(std::move(connection));
EXPECT_FALSE(adapter.is_initialized());
}
TEST_F(WebSocketClientSocketHandleAdapterTest, IsInitialized) {
StaticSocketDataProvider data;
session_deps_.socket_factory->AddSocketDataProvider(&data);
SSLSocketDataProvider ssl_socket_data(ASYNC, OK);
session_deps_.socket_factory->AddSSLSocketDataProvider(&ssl_socket_data);
auto connection = std::make_unique<ClientSocketHandle>();
ClientSocketHandle* const connection_ptr = connection.get();
WebSocketClientSocketHandleAdapter adapter(std::move(connection));
EXPECT_FALSE(adapter.is_initialized());
EXPECT_TRUE(InitClientSocketHandle(connection_ptr));
EXPECT_TRUE(adapter.is_initialized());
}
TEST_F(WebSocketClientSocketHandleAdapterTest, Disconnect) {
StaticSocketDataProvider data;
session_deps_.socket_factory->AddSocketDataProvider(&data);
SSLSocketDataProvider ssl_socket_data(ASYNC, OK);
session_deps_.socket_factory->AddSSLSocketDataProvider(&ssl_socket_data);
auto connection = std::make_unique<ClientSocketHandle>();
EXPECT_TRUE(InitClientSocketHandle(connection.get()));
StreamSocket* const socket = connection->socket();
WebSocketClientSocketHandleAdapter adapter(std::move(connection));
EXPECT_TRUE(adapter.is_initialized());
EXPECT_TRUE(socket->IsConnected());
adapter.Disconnect();
EXPECT_FALSE(socket->IsConnected());
}
TEST_F(WebSocketClientSocketHandleAdapterTest, Read) {
MockRead reads[] = {MockRead(SYNCHRONOUS, "foo"), MockRead("bar")};
StaticSocketDataProvider data(reads, base::span<MockWrite>());
session_deps_.socket_factory->AddSocketDataProvider(&data);
SSLSocketDataProvider ssl_socket_data(ASYNC, OK);
session_deps_.socket_factory->AddSSLSocketDataProvider(&ssl_socket_data);
auto connection = std::make_unique<ClientSocketHandle>();
EXPECT_TRUE(InitClientSocketHandle(connection.get()));
WebSocketClientSocketHandleAdapter adapter(std::move(connection));
EXPECT_TRUE(adapter.is_initialized());
// Buffer larger than each MockRead.
const int kReadBufSize = 1024;
auto read_buf = base::MakeRefCounted<IOBuffer>(kReadBufSize);
int rv = adapter.Read(read_buf.get(), kReadBufSize, CompletionOnceCallback());
ASSERT_EQ(3, rv);
EXPECT_EQ("foo", base::StringPiece(read_buf->data(), rv));
TestCompletionCallback callback;
rv = adapter.Read(read_buf.get(), kReadBufSize, callback.callback());
EXPECT_THAT(rv, IsError(ERR_IO_PENDING));
rv = callback.WaitForResult();
ASSERT_EQ(3, rv);
EXPECT_EQ("bar", base::StringPiece(read_buf->data(), rv));
EXPECT_TRUE(data.AllReadDataConsumed());
EXPECT_TRUE(data.AllWriteDataConsumed());
}
TEST_F(WebSocketClientSocketHandleAdapterTest, ReadIntoSmallBuffer) {
MockRead reads[] = {MockRead(SYNCHRONOUS, "foo"), MockRead("bar")};
StaticSocketDataProvider data(reads, base::span<MockWrite>());
session_deps_.socket_factory->AddSocketDataProvider(&data);
SSLSocketDataProvider ssl_socket_data(ASYNC, OK);
session_deps_.socket_factory->AddSSLSocketDataProvider(&ssl_socket_data);
auto connection = std::make_unique<ClientSocketHandle>();
EXPECT_TRUE(InitClientSocketHandle(connection.get()));
WebSocketClientSocketHandleAdapter adapter(std::move(connection));
EXPECT_TRUE(adapter.is_initialized());
// Buffer smaller than each MockRead.
const int kReadBufSize = 2;
auto read_buf = base::MakeRefCounted<IOBuffer>(kReadBufSize);
int rv = adapter.Read(read_buf.get(), kReadBufSize, CompletionOnceCallback());
ASSERT_EQ(2, rv);
EXPECT_EQ("fo", base::StringPiece(read_buf->data(), rv));
rv = adapter.Read(read_buf.get(), kReadBufSize, CompletionOnceCallback());
ASSERT_EQ(1, rv);
EXPECT_EQ("o", base::StringPiece(read_buf->data(), rv));
TestCompletionCallback callback1;
rv = adapter.Read(read_buf.get(), kReadBufSize, callback1.callback());
EXPECT_THAT(rv, IsError(ERR_IO_PENDING));
rv = callback1.WaitForResult();
ASSERT_EQ(2, rv);
EXPECT_EQ("ba", base::StringPiece(read_buf->data(), rv));
rv = adapter.Read(read_buf.get(), kReadBufSize, CompletionOnceCallback());
ASSERT_EQ(1, rv);
EXPECT_EQ("r", base::StringPiece(read_buf->data(), rv));
EXPECT_TRUE(data.AllReadDataConsumed());
EXPECT_TRUE(data.AllWriteDataConsumed());
}
TEST_F(WebSocketClientSocketHandleAdapterTest, Write) {
MockWrite writes[] = {MockWrite(SYNCHRONOUS, "foo"), MockWrite("bar")};
StaticSocketDataProvider data(base::span<MockRead>(), writes);
session_deps_.socket_factory->AddSocketDataProvider(&data);
SSLSocketDataProvider ssl_socket_data(ASYNC, OK);
session_deps_.socket_factory->AddSSLSocketDataProvider(&ssl_socket_data);
auto connection = std::make_unique<ClientSocketHandle>();
EXPECT_TRUE(InitClientSocketHandle(connection.get()));
WebSocketClientSocketHandleAdapter adapter(std::move(connection));
EXPECT_TRUE(adapter.is_initialized());
auto write_buf1 = base::MakeRefCounted<StringIOBuffer>("foo");
int rv =
adapter.Write(write_buf1.get(), write_buf1->size(),
CompletionOnceCallback(), TRAFFIC_ANNOTATION_FOR_TESTS);
ASSERT_EQ(3, rv);
auto write_buf2 = base::MakeRefCounted<StringIOBuffer>("bar");
TestCompletionCallback callback;
rv = adapter.Write(write_buf2.get(), write_buf2->size(), callback.callback(),
TRAFFIC_ANNOTATION_FOR_TESTS);
EXPECT_THAT(rv, IsError(ERR_IO_PENDING));
rv = callback.WaitForResult();
ASSERT_EQ(3, rv);
EXPECT_TRUE(data.AllReadDataConsumed());
EXPECT_TRUE(data.AllWriteDataConsumed());
}
// Test that if both Read() and Write() returns asynchronously,
// the two callbacks are handled correctly.
TEST_F(WebSocketClientSocketHandleAdapterTest, AsyncReadAndWrite) {
MockRead reads[] = {MockRead("foobar")};
MockWrite writes[] = {MockWrite("baz")};
StaticSocketDataProvider data(reads, writes);
session_deps_.socket_factory->AddSocketDataProvider(&data);
SSLSocketDataProvider ssl_socket_data(ASYNC, OK);
session_deps_.socket_factory->AddSSLSocketDataProvider(&ssl_socket_data);
auto connection = std::make_unique<ClientSocketHandle>();
EXPECT_TRUE(InitClientSocketHandle(connection.get()));
WebSocketClientSocketHandleAdapter adapter(std::move(connection));
EXPECT_TRUE(adapter.is_initialized());
const int kReadBufSize = 1024;
auto read_buf = base::MakeRefCounted<IOBuffer>(kReadBufSize);
TestCompletionCallback read_callback;
int rv = adapter.Read(read_buf.get(), kReadBufSize, read_callback.callback());
EXPECT_THAT(rv, IsError(ERR_IO_PENDING));
auto write_buf = base::MakeRefCounted<StringIOBuffer>("baz");
TestCompletionCallback write_callback;
rv = adapter.Write(write_buf.get(), write_buf->size(),
write_callback.callback(), TRAFFIC_ANNOTATION_FOR_TESTS);
EXPECT_THAT(rv, IsError(ERR_IO_PENDING));
rv = read_callback.WaitForResult();
ASSERT_EQ(6, rv);
EXPECT_EQ("foobar", base::StringPiece(read_buf->data(), rv));
rv = write_callback.WaitForResult();
ASSERT_EQ(3, rv);
EXPECT_TRUE(data.AllReadDataConsumed());
EXPECT_TRUE(data.AllWriteDataConsumed());
}
class MockDelegate : public WebSocketSpdyStreamAdapter::Delegate {
public:
~MockDelegate() override = default;
MOCK_METHOD0(OnHeadersSent, void());
MOCK_METHOD1(OnHeadersReceived, void(const spdy::SpdyHeaderBlock&));
MOCK_METHOD1(OnClose, void(int));
};
class WebSocketSpdyStreamAdapterTest : public TestWithTaskEnvironment {
protected:
WebSocketSpdyStreamAdapterTest()
: url_("wss://www.example.org/"),
key_(HostPortPair::FromURL(url_),
ProxyServer::Direct(),
PRIVACY_MODE_DISABLED,
SpdySessionKey::IsProxySession::kFalse,
SocketTag(),
NetworkIsolationKey(),
false /* disable_secure_dns */),
session_(SpdySessionDependencies::SpdyCreateSession(&session_deps_)),
ssl_(SYNCHRONOUS, OK) {}
~WebSocketSpdyStreamAdapterTest() override = default;
static spdy::SpdyHeaderBlock RequestHeaders() {
return WebSocketHttp2Request("/", "www.example.org:443",
"http://www.example.org", {});
}
static spdy::SpdyHeaderBlock ResponseHeaders() {
return WebSocketHttp2Response({});
}
void AddSocketData(SocketDataProvider* data) {
session_deps_.socket_factory->AddSocketDataProvider(data);
}
void AddSSLSocketData() {
ssl_.ssl_info.cert =
ImportCertFromFile(GetTestCertsDirectory(), "wildcard.pem");
ASSERT_TRUE(ssl_.ssl_info.cert);
session_deps_.socket_factory->AddSSLSocketDataProvider(&ssl_);
}
base::WeakPtr<SpdySession> CreateSpdySession() {
return ::net::CreateSpdySession(session_.get(), key_, NetLogWithSource());
}
base::WeakPtr<SpdyStream> CreateSpdyStream(
base::WeakPtr<SpdySession> session) {
return CreateStreamSynchronously(SPDY_BIDIRECTIONAL_STREAM, session, url_,
LOWEST, NetLogWithSource());
}
SpdyTestUtil spdy_util_;
StrictMock<MockDelegate> mock_delegate_;
private:
const GURL url_;
const SpdySessionKey key_;
SpdySessionDependencies session_deps_;
std::unique_ptr<HttpNetworkSession> session_;
SSLSocketDataProvider ssl_;
};
TEST_F(WebSocketSpdyStreamAdapterTest, Disconnect) {
MockRead reads[] = {MockRead(ASYNC, ERR_IO_PENDING, 0),
MockRead(ASYNC, 0, 1)};
SequencedSocketData data(reads, base::span<MockWrite>());
AddSocketData(&data);
AddSSLSocketData();
base::WeakPtr<SpdySession> session = CreateSpdySession();
base::WeakPtr<SpdyStream> stream = CreateSpdyStream(session);
WebSocketSpdyStreamAdapter adapter(stream, &mock_delegate_,
NetLogWithSource());
EXPECT_TRUE(adapter.is_initialized());
base::RunLoop().RunUntilIdle();
EXPECT_TRUE(stream);
adapter.Disconnect();
EXPECT_FALSE(stream);
// Read EOF.
EXPECT_TRUE(session);
data.Resume();
base::RunLoop().RunUntilIdle();
EXPECT_FALSE(session);
EXPECT_TRUE(data.AllReadDataConsumed());
EXPECT_TRUE(data.AllWriteDataConsumed());
}
TEST_F(WebSocketSpdyStreamAdapterTest, SendRequestHeadersThenDisconnect) {
MockRead reads[] = {MockRead(ASYNC, ERR_IO_PENDING, 0),
MockRead(ASYNC, 0, 3)};
spdy::SpdySerializedFrame headers(spdy_util_.ConstructSpdyHeaders(
1, RequestHeaders(), DEFAULT_PRIORITY, false));
spdy::SpdySerializedFrame rst(
spdy_util_.ConstructSpdyRstStream(1, spdy::ERROR_CODE_CANCEL));
MockWrite writes[] = {CreateMockWrite(headers, 1), CreateMockWrite(rst, 2)};
SequencedSocketData data(reads, writes);
AddSocketData(&data);
AddSSLSocketData();
base::WeakPtr<SpdySession> session = CreateSpdySession();
base::WeakPtr<SpdyStream> stream = CreateSpdyStream(session);
WebSocketSpdyStreamAdapter adapter(stream, &mock_delegate_,
NetLogWithSource());
EXPECT_TRUE(adapter.is_initialized());
int rv = stream->SendRequestHeaders(RequestHeaders(), MORE_DATA_TO_SEND);
EXPECT_THAT(rv, IsError(ERR_IO_PENDING));
// First read is a pause and it has lower sequence number than first write.
// Therefore writing headers does not complete while |data| is paused.
base::RunLoop().RunUntilIdle();
// Reset the stream before writing completes.
// OnHeadersSent() will never be called.
EXPECT_TRUE(stream);
adapter.Disconnect();
EXPECT_FALSE(stream);
// Resume |data|, finish writing headers, and read EOF.
EXPECT_TRUE(session);
data.Resume();
base::RunLoop().RunUntilIdle();
EXPECT_FALSE(session);
EXPECT_TRUE(data.AllReadDataConsumed());
EXPECT_TRUE(data.AllWriteDataConsumed());
}
TEST_F(WebSocketSpdyStreamAdapterTest, OnHeadersSentThenDisconnect) {
MockRead reads[] = {MockRead(ASYNC, 0, 2)};
spdy::SpdySerializedFrame headers(spdy_util_.ConstructSpdyHeaders(
1, RequestHeaders(), DEFAULT_PRIORITY, false));
spdy::SpdySerializedFrame rst(
spdy_util_.ConstructSpdyRstStream(1, spdy::ERROR_CODE_CANCEL));
MockWrite writes[] = {CreateMockWrite(headers, 0), CreateMockWrite(rst, 1)};
SequencedSocketData data(reads, writes);
AddSocketData(&data);
AddSSLSocketData();
EXPECT_CALL(mock_delegate_, OnHeadersSent());
base::WeakPtr<SpdySession> session = CreateSpdySession();
base::WeakPtr<SpdyStream> stream = CreateSpdyStream(session);
WebSocketSpdyStreamAdapter adapter(stream, &mock_delegate_,
NetLogWithSource());
EXPECT_TRUE(adapter.is_initialized());
int rv = stream->SendRequestHeaders(RequestHeaders(), MORE_DATA_TO_SEND);
EXPECT_THAT(rv, IsError(ERR_IO_PENDING));
// Finish asynchronous write of headers. This calls OnHeadersSent().
base::RunLoop().RunUntilIdle();
EXPECT_TRUE(stream);
adapter.Disconnect();
EXPECT_FALSE(stream);
// Read EOF.
EXPECT_TRUE(session);
base::RunLoop().RunUntilIdle();
EXPECT_FALSE(session);
EXPECT_TRUE(data.AllReadDataConsumed());
EXPECT_TRUE(data.AllWriteDataConsumed());
}
TEST_F(WebSocketSpdyStreamAdapterTest, OnHeadersReceivedThenDisconnect) {
spdy::SpdySerializedFrame response_headers(
spdy_util_.ConstructSpdyResponseHeaders(1, ResponseHeaders(), false));
MockRead reads[] = {CreateMockRead(response_headers, 1),
MockRead(ASYNC, 0, 3)};
spdy::SpdySerializedFrame request_headers(spdy_util_.ConstructSpdyHeaders(
1, RequestHeaders(), DEFAULT_PRIORITY, false));
spdy::SpdySerializedFrame rst(
spdy_util_.ConstructSpdyRstStream(1, spdy::ERROR_CODE_CANCEL));
MockWrite writes[] = {CreateMockWrite(request_headers, 0),
CreateMockWrite(rst, 2)};
SequencedSocketData data(reads, writes);
AddSocketData(&data);
AddSSLSocketData();
EXPECT_CALL(mock_delegate_, OnHeadersSent());
EXPECT_CALL(mock_delegate_, OnHeadersReceived(_));
base::WeakPtr<SpdySession> session = CreateSpdySession();
base::WeakPtr<SpdyStream> stream = CreateSpdyStream(session);
WebSocketSpdyStreamAdapter adapter(stream, &mock_delegate_,
NetLogWithSource());
EXPECT_TRUE(adapter.is_initialized());
int rv = stream->SendRequestHeaders(RequestHeaders(), MORE_DATA_TO_SEND);
EXPECT_THAT(rv, IsError(ERR_IO_PENDING));
base::RunLoop().RunUntilIdle();
EXPECT_TRUE(stream);
adapter.Disconnect();
EXPECT_FALSE(stream);
// Read EOF.
EXPECT_TRUE(session);
base::RunLoop().RunUntilIdle();
EXPECT_FALSE(session);
EXPECT_TRUE(data.AllReadDataConsumed());
EXPECT_TRUE(data.AllWriteDataConsumed());
}
TEST_F(WebSocketSpdyStreamAdapterTest, ServerClosesConnection) {
MockRead reads[] = {MockRead(ASYNC, 0, 0)};
SequencedSocketData data(reads, base::span<MockWrite>());
AddSocketData(&data);
AddSSLSocketData();
EXPECT_CALL(mock_delegate_, OnClose(ERR_CONNECTION_CLOSED));
base::WeakPtr<SpdySession> session = CreateSpdySession();
base::WeakPtr<SpdyStream> stream = CreateSpdyStream(session);
WebSocketSpdyStreamAdapter adapter(stream, &mock_delegate_,
NetLogWithSource());
EXPECT_TRUE(adapter.is_initialized());
EXPECT_TRUE(session);
EXPECT_TRUE(stream);
base::RunLoop().RunUntilIdle();
EXPECT_FALSE(session);
EXPECT_FALSE(stream);
EXPECT_TRUE(data.AllReadDataConsumed());
EXPECT_TRUE(data.AllWriteDataConsumed());
}
TEST_F(WebSocketSpdyStreamAdapterTest,
SendRequestHeadersThenServerClosesConnection) {
MockRead reads[] = {MockRead(ASYNC, 0, 1)};
spdy::SpdySerializedFrame headers(spdy_util_.ConstructSpdyHeaders(
1, RequestHeaders(), DEFAULT_PRIORITY, false));
MockWrite writes[] = {CreateMockWrite(headers, 0)};
SequencedSocketData data(reads, writes);
AddSocketData(&data);
AddSSLSocketData();
EXPECT_CALL(mock_delegate_, OnHeadersSent());
EXPECT_CALL(mock_delegate_, OnClose(ERR_CONNECTION_CLOSED));
base::WeakPtr<SpdySession> session = CreateSpdySession();
base::WeakPtr<SpdyStream> stream = CreateSpdyStream(session);
WebSocketSpdyStreamAdapter adapter(stream, &mock_delegate_,
NetLogWithSource());
EXPECT_TRUE(adapter.is_initialized());
int rv = stream->SendRequestHeaders(RequestHeaders(), MORE_DATA_TO_SEND);
EXPECT_THAT(rv, IsError(ERR_IO_PENDING));
EXPECT_TRUE(session);
EXPECT_TRUE(stream);
base::RunLoop().RunUntilIdle();
EXPECT_FALSE(session);
EXPECT_FALSE(stream);
EXPECT_TRUE(data.AllReadDataConsumed());
EXPECT_TRUE(data.AllWriteDataConsumed());
}
TEST_F(WebSocketSpdyStreamAdapterTest,
OnHeadersReceivedThenServerClosesConnection) {
spdy::SpdySerializedFrame response_headers(
spdy_util_.ConstructSpdyResponseHeaders(1, ResponseHeaders(), false));
MockRead reads[] = {CreateMockRead(response_headers, 1),
MockRead(ASYNC, 0, 2)};
spdy::SpdySerializedFrame request_headers(spdy_util_.ConstructSpdyHeaders(
1, RequestHeaders(), DEFAULT_PRIORITY, false));
MockWrite writes[] = {CreateMockWrite(request_headers, 0)};
SequencedSocketData data(reads, writes);
AddSocketData(&data);
AddSSLSocketData();
EXPECT_CALL(mock_delegate_, OnHeadersSent());
EXPECT_CALL(mock_delegate_, OnHeadersReceived(_));
EXPECT_CALL(mock_delegate_, OnClose(ERR_CONNECTION_CLOSED));
base::WeakPtr<SpdySession> session = CreateSpdySession();
base::WeakPtr<SpdyStream> stream = CreateSpdyStream(session);
WebSocketSpdyStreamAdapter adapter(stream, &mock_delegate_,
NetLogWithSource());
EXPECT_TRUE(adapter.is_initialized());
int rv = stream->SendRequestHeaders(RequestHeaders(), MORE_DATA_TO_SEND);
EXPECT_THAT(rv, IsError(ERR_IO_PENDING));
EXPECT_TRUE(session);
EXPECT_TRUE(stream);
base::RunLoop().RunUntilIdle();
EXPECT_FALSE(session);
EXPECT_FALSE(stream);
EXPECT_TRUE(data.AllReadDataConsumed());
EXPECT_TRUE(data.AllWriteDataConsumed());
}
TEST_F(WebSocketSpdyStreamAdapterTest, DetachDelegate) {
spdy::SpdySerializedFrame response_headers(
spdy_util_.ConstructSpdyResponseHeaders(1, ResponseHeaders(), false));
MockRead reads[] = {CreateMockRead(response_headers, 1),
MockRead(ASYNC, 0, 2)};
spdy::SpdySerializedFrame request_headers(spdy_util_.ConstructSpdyHeaders(
1, RequestHeaders(), DEFAULT_PRIORITY, false));
MockWrite writes[] = {CreateMockWrite(request_headers, 0)};
SequencedSocketData data(reads, writes);
AddSocketData(&data);
AddSSLSocketData();
base::WeakPtr<SpdySession> session = CreateSpdySession();
base::WeakPtr<SpdyStream> stream = CreateSpdyStream(session);
WebSocketSpdyStreamAdapter adapter(stream, &mock_delegate_,
NetLogWithSource());
EXPECT_TRUE(adapter.is_initialized());
// No Delegate methods shall be called after this.
adapter.DetachDelegate();
int rv = stream->SendRequestHeaders(RequestHeaders(), MORE_DATA_TO_SEND);
EXPECT_THAT(rv, IsError(ERR_IO_PENDING));
EXPECT_TRUE(session);
EXPECT_TRUE(stream);
base::RunLoop().RunUntilIdle();
EXPECT_FALSE(session);
EXPECT_FALSE(stream);
EXPECT_TRUE(data.AllReadDataConsumed());
EXPECT_TRUE(data.AllWriteDataConsumed());
}
TEST_F(WebSocketSpdyStreamAdapterTest, Read) {
spdy::SpdySerializedFrame response_headers(
spdy_util_.ConstructSpdyResponseHeaders(1, ResponseHeaders(), false));
// First read is the same size as the buffer, next is smaller, last is larger.
spdy::SpdySerializedFrame data_frame1(
spdy_util_.ConstructSpdyDataFrame(1, "foo", false));
spdy::SpdySerializedFrame data_frame2(
spdy_util_.ConstructSpdyDataFrame(1, "ba", false));
spdy::SpdySerializedFrame data_frame3(
spdy_util_.ConstructSpdyDataFrame(1, "rbaz", true));
MockRead reads[] = {CreateMockRead(response_headers, 1),
CreateMockRead(data_frame1, 2),
CreateMockRead(data_frame2, 3),
CreateMockRead(data_frame3, 4), MockRead(ASYNC, 0, 5)};
spdy::SpdySerializedFrame request_headers(spdy_util_.ConstructSpdyHeaders(
1, RequestHeaders(), DEFAULT_PRIORITY, false));
MockWrite writes[] = {CreateMockWrite(request_headers, 0)};
SequencedSocketData data(reads, writes);
AddSocketData(&data);
AddSSLSocketData();
EXPECT_CALL(mock_delegate_, OnHeadersSent());
EXPECT_CALL(mock_delegate_, OnHeadersReceived(_));
base::WeakPtr<SpdySession> session = CreateSpdySession();
base::WeakPtr<SpdyStream> stream = CreateSpdyStream(session);
WebSocketSpdyStreamAdapter adapter(stream, &mock_delegate_,
NetLogWithSource());
EXPECT_TRUE(adapter.is_initialized());
int rv = stream->SendRequestHeaders(RequestHeaders(), MORE_DATA_TO_SEND);
EXPECT_THAT(rv, IsError(ERR_IO_PENDING));
const int kReadBufSize = 3;
auto read_buf = base::MakeRefCounted<IOBuffer>(kReadBufSize);
TestCompletionCallback callback;
rv = adapter.Read(read_buf.get(), kReadBufSize, callback.callback());
EXPECT_THAT(rv, IsError(ERR_IO_PENDING));
rv = callback.WaitForResult();
ASSERT_EQ(3, rv);
EXPECT_EQ("foo", base::StringPiece(read_buf->data(), rv));
// Read EOF to destroy the connection and the stream.
// This calls SpdySession::Delegate::OnClose().
EXPECT_TRUE(session);
EXPECT_TRUE(stream);
base::RunLoop().RunUntilIdle();
EXPECT_FALSE(session);
EXPECT_FALSE(stream);
// Two socket reads are concatenated by WebSocketSpdyStreamAdapter.
rv = adapter.Read(read_buf.get(), kReadBufSize, CompletionOnceCallback());
ASSERT_EQ(3, rv);
EXPECT_EQ("bar", base::StringPiece(read_buf->data(), rv));
rv = adapter.Read(read_buf.get(), kReadBufSize, CompletionOnceCallback());
ASSERT_EQ(3, rv);
EXPECT_EQ("baz", base::StringPiece(read_buf->data(), rv));
// Even though connection and stream are already closed,
// WebSocketSpdyStreamAdapter::Delegate::OnClose() is only called after all
// buffered data are read.
EXPECT_CALL(mock_delegate_, OnClose(ERR_CONNECTION_CLOSED));
base::RunLoop().RunUntilIdle();
EXPECT_TRUE(data.AllReadDataConsumed());
EXPECT_TRUE(data.AllWriteDataConsumed());
}
TEST_F(WebSocketSpdyStreamAdapterTest, CallDelegateOnCloseShouldNotCrash) {
spdy::SpdySerializedFrame response_headers(
spdy_util_.ConstructSpdyResponseHeaders(1, ResponseHeaders(), false));
spdy::SpdySerializedFrame data_frame1(
spdy_util_.ConstructSpdyDataFrame(1, "foo", false));
spdy::SpdySerializedFrame data_frame2(
spdy_util_.ConstructSpdyDataFrame(1, "bar", false));
spdy::SpdySerializedFrame rst(
spdy_util_.ConstructSpdyRstStream(1, spdy::ERROR_CODE_CANCEL));
MockRead reads[] = {CreateMockRead(response_headers, 1),
CreateMockRead(data_frame1, 2),
CreateMockRead(data_frame2, 3), CreateMockRead(rst, 4),
MockRead(ASYNC, 0, 5)};
spdy::SpdySerializedFrame request_headers(spdy_util_.ConstructSpdyHeaders(
1, RequestHeaders(), DEFAULT_PRIORITY, false));
MockWrite writes[] = {CreateMockWrite(request_headers, 0)};
SequencedSocketData data(reads, writes);
AddSocketData(&data);
AddSSLSocketData();
EXPECT_CALL(mock_delegate_, OnHeadersSent());
EXPECT_CALL(mock_delegate_, OnHeadersReceived(_));
base::WeakPtr<SpdySession> session = CreateSpdySession();
base::WeakPtr<SpdyStream> stream = CreateSpdyStream(session);
WebSocketSpdyStreamAdapter adapter(stream, &mock_delegate_,
NetLogWithSource());
EXPECT_TRUE(adapter.is_initialized());
int rv = stream->SendRequestHeaders(RequestHeaders(), MORE_DATA_TO_SEND);
EXPECT_THAT(rv, IsError(ERR_IO_PENDING));
// Buffer larger than each MockRead.
const int kReadBufSize = 1024;
auto read_buf = base::MakeRefCounted<IOBuffer>(kReadBufSize);
TestCompletionCallback callback;
rv = adapter.Read(read_buf.get(), kReadBufSize, callback.callback());
EXPECT_THAT(rv, IsError(ERR_IO_PENDING));
rv = callback.WaitForResult();
ASSERT_EQ(3, rv);
EXPECT_EQ("foo", base::StringPiece(read_buf->data(), rv));
// Read RST_STREAM to destroy the stream.
// This calls SpdySession::Delegate::OnClose().
EXPECT_TRUE(session);
EXPECT_TRUE(stream);
base::RunLoop().RunUntilIdle();
EXPECT_FALSE(session);
EXPECT_FALSE(stream);
// Read remaining buffered data. This will PostTask CallDelegateOnClose().
rv = adapter.Read(read_buf.get(), kReadBufSize, CompletionOnceCallback());
ASSERT_EQ(3, rv);
EXPECT_EQ("bar", base::StringPiece(read_buf->data(), rv));
adapter.DetachDelegate();
// Run CallDelegateOnClose(), which should not crash
// even if |delegate_| is null.
base::RunLoop().RunUntilIdle();
EXPECT_TRUE(data.AllReadDataConsumed());
EXPECT_TRUE(data.AllWriteDataConsumed());
}
TEST_F(WebSocketSpdyStreamAdapterTest, Write) {
spdy::SpdySerializedFrame response_headers(
spdy_util_.ConstructSpdyResponseHeaders(1, ResponseHeaders(), false));
MockRead reads[] = {CreateMockRead(response_headers, 1),
MockRead(ASYNC, 0, 3)};
spdy::SpdySerializedFrame request_headers(spdy_util_.ConstructSpdyHeaders(
1, RequestHeaders(), DEFAULT_PRIORITY, false));
spdy::SpdySerializedFrame data_frame(
spdy_util_.ConstructSpdyDataFrame(1, "foo", false));
MockWrite writes[] = {CreateMockWrite(request_headers, 0),
CreateMockWrite(data_frame, 2)};
SequencedSocketData data(reads, writes);
AddSocketData(&data);
AddSSLSocketData();
base::WeakPtr<SpdySession> session = CreateSpdySession();
base::WeakPtr<SpdyStream> stream = CreateSpdyStream(session);
WebSocketSpdyStreamAdapter adapter(stream, nullptr, NetLogWithSource());
EXPECT_TRUE(adapter.is_initialized());
int rv = stream->SendRequestHeaders(RequestHeaders(), MORE_DATA_TO_SEND);
EXPECT_THAT(rv, IsError(ERR_IO_PENDING));
base::RunLoop().RunUntilIdle();
auto write_buf = base::MakeRefCounted<StringIOBuffer>("foo");
TestCompletionCallback callback;
rv = adapter.Write(write_buf.get(), write_buf->size(), callback.callback(),
TRAFFIC_ANNOTATION_FOR_TESTS);
EXPECT_THAT(rv, IsError(ERR_IO_PENDING));
rv = callback.WaitForResult();
ASSERT_EQ(3, rv);
// Read EOF.
base::RunLoop().RunUntilIdle();
EXPECT_TRUE(data.AllReadDataConsumed());
EXPECT_TRUE(data.AllWriteDataConsumed());
}
// Test that if both Read() and Write() returns asynchronously,
// the two callbacks are handled correctly.
TEST_F(WebSocketSpdyStreamAdapterTest, AsyncReadAndWrite) {
spdy::SpdySerializedFrame response_headers(
spdy_util_.ConstructSpdyResponseHeaders(1, ResponseHeaders(), false));
spdy::SpdySerializedFrame read_data_frame(
spdy_util_.ConstructSpdyDataFrame(1, "foobar", true));
MockRead reads[] = {CreateMockRead(response_headers, 1),
CreateMockRead(read_data_frame, 3),
MockRead(ASYNC, 0, 4)};
spdy::SpdySerializedFrame request_headers(spdy_util_.ConstructSpdyHeaders(
1, RequestHeaders(), DEFAULT_PRIORITY, false));
spdy::SpdySerializedFrame write_data_frame(
spdy_util_.ConstructSpdyDataFrame(1, "baz", false));
MockWrite writes[] = {CreateMockWrite(request_headers, 0),
CreateMockWrite(write_data_frame, 2)};
SequencedSocketData data(reads, writes);
AddSocketData(&data);
AddSSLSocketData();
base::WeakPtr<SpdySession> session = CreateSpdySession();
base::WeakPtr<SpdyStream> stream = CreateSpdyStream(session);
WebSocketSpdyStreamAdapter adapter(stream, nullptr, NetLogWithSource());
EXPECT_TRUE(adapter.is_initialized());
int rv = stream->SendRequestHeaders(RequestHeaders(), MORE_DATA_TO_SEND);
EXPECT_THAT(rv, IsError(ERR_IO_PENDING));
base::RunLoop().RunUntilIdle();
const int kReadBufSize = 1024;
auto read_buf = base::MakeRefCounted<IOBuffer>(kReadBufSize);
TestCompletionCallback read_callback;
rv = adapter.Read(read_buf.get(), kReadBufSize, read_callback.callback());
EXPECT_THAT(rv, IsError(ERR_IO_PENDING));
auto write_buf = base::MakeRefCounted<StringIOBuffer>("baz");
TestCompletionCallback write_callback;
rv = adapter.Write(write_buf.get(), write_buf->size(),
write_callback.callback(), TRAFFIC_ANNOTATION_FOR_TESTS);
EXPECT_THAT(rv, IsError(ERR_IO_PENDING));
rv = read_callback.WaitForResult();
ASSERT_EQ(6, rv);
EXPECT_EQ("foobar", base::StringPiece(read_buf->data(), rv));
rv = write_callback.WaitForResult();
ASSERT_EQ(3, rv);
// Read EOF.
base::RunLoop().RunUntilIdle();
EXPECT_TRUE(data.AllReadDataConsumed());
EXPECT_TRUE(data.AllWriteDataConsumed());
}
// A helper class that will delete |adapter| when the callback is invoked.
class KillerCallback : public TestCompletionCallbackBase {
public:
explicit KillerCallback(std::unique_ptr<WebSocketSpdyStreamAdapter> adapter)
: adapter_(std::move(adapter)) {}
~KillerCallback() override = default;
CompletionOnceCallback callback() {
return base::BindOnce(&KillerCallback::OnComplete, base::Unretained(this));
}
private:
void OnComplete(int result) {
adapter_.reset();
SetResult(result);
}
std::unique_ptr<WebSocketSpdyStreamAdapter> adapter_;
};
TEST_F(WebSocketSpdyStreamAdapterTest, ReadCallbackDestroysAdapter) {
spdy::SpdySerializedFrame response_headers(
spdy_util_.ConstructSpdyResponseHeaders(1, ResponseHeaders(), false));
MockRead reads[] = {CreateMockRead(response_headers, 1),
MockRead(ASYNC, ERR_IO_PENDING, 2),
MockRead(ASYNC, 0, 3)};
spdy::SpdySerializedFrame request_headers(spdy_util_.ConstructSpdyHeaders(
1, RequestHeaders(), DEFAULT_PRIORITY, false));
MockWrite writes[] = {CreateMockWrite(request_headers, 0)};
SequencedSocketData data(reads, writes);
AddSocketData(&data);
AddSSLSocketData();
EXPECT_CALL(mock_delegate_, OnHeadersSent());
EXPECT_CALL(mock_delegate_, OnHeadersReceived(_));
base::WeakPtr<SpdySession> session = CreateSpdySession();
base::WeakPtr<SpdyStream> stream = CreateSpdyStream(session);
auto adapter = std::make_unique<WebSocketSpdyStreamAdapter>(
stream, &mock_delegate_, NetLogWithSource());
EXPECT_TRUE(adapter->is_initialized());
int rv = stream->SendRequestHeaders(RequestHeaders(), MORE_DATA_TO_SEND);
EXPECT_THAT(rv, IsError(ERR_IO_PENDING));
// Send headers.
base::RunLoop().RunUntilIdle();
WebSocketSpdyStreamAdapter* adapter_raw = adapter.get();
KillerCallback callback(std::move(adapter));
const int kReadBufSize = 1024;
auto read_buf = base::MakeRefCounted<IOBuffer>(kReadBufSize);
rv = adapter_raw->Read(read_buf.get(), kReadBufSize, callback.callback());
EXPECT_THAT(rv, IsError(ERR_IO_PENDING));
// Read EOF while read is pending. WebSocketSpdyStreamAdapter::OnClose()
// should not crash if read callback destroys |adapter|.
data.Resume();
rv = callback.WaitForResult();
EXPECT_THAT(rv, IsError(ERR_CONNECTION_CLOSED));
base::RunLoop().RunUntilIdle();
EXPECT_FALSE(session);
EXPECT_FALSE(stream);
EXPECT_TRUE(data.AllReadDataConsumed());
EXPECT_TRUE(data.AllWriteDataConsumed());
}
TEST_F(WebSocketSpdyStreamAdapterTest, WriteCallbackDestroysAdapter) {
spdy::SpdySerializedFrame response_headers(
spdy_util_.ConstructSpdyResponseHeaders(1, ResponseHeaders(), false));
MockRead reads[] = {CreateMockRead(response_headers, 1),
MockRead(ASYNC, ERR_IO_PENDING, 2),
MockRead(ASYNC, 0, 3)};
spdy::SpdySerializedFrame request_headers(spdy_util_.ConstructSpdyHeaders(
1, RequestHeaders(), DEFAULT_PRIORITY, false));
MockWrite writes[] = {CreateMockWrite(request_headers, 0)};
SequencedSocketData data(reads, writes);
AddSocketData(&data);
AddSSLSocketData();
EXPECT_CALL(mock_delegate_, OnHeadersSent());
EXPECT_CALL(mock_delegate_, OnHeadersReceived(_));
base::WeakPtr<SpdySession> session = CreateSpdySession();
base::WeakPtr<SpdyStream> stream = CreateSpdyStream(session);
auto adapter = std::make_unique<WebSocketSpdyStreamAdapter>(
stream, &mock_delegate_, NetLogWithSource());
EXPECT_TRUE(adapter->is_initialized());
int rv = stream->SendRequestHeaders(RequestHeaders(), MORE_DATA_TO_SEND);
EXPECT_THAT(rv, IsError(ERR_IO_PENDING));
// Send headers.
base::RunLoop().RunUntilIdle();
WebSocketSpdyStreamAdapter* adapter_raw = adapter.get();
KillerCallback callback(std::move(adapter));
auto write_buf = base::MakeRefCounted<StringIOBuffer>("foo");
rv = adapter_raw->Write(write_buf.get(), write_buf->size(),
callback.callback(), TRAFFIC_ANNOTATION_FOR_TESTS);
EXPECT_THAT(rv, IsError(ERR_IO_PENDING));
// Read EOF while write is pending. WebSocketSpdyStreamAdapter::OnClose()
// should not crash if write callback destroys |adapter|.
data.Resume();
rv = callback.WaitForResult();
EXPECT_THAT(rv, IsError(ERR_CONNECTION_CLOSED));
base::RunLoop().RunUntilIdle();
EXPECT_FALSE(session);
EXPECT_FALSE(stream);
EXPECT_TRUE(data.AllReadDataConsumed());
EXPECT_TRUE(data.AllWriteDataConsumed());
}
} // namespace test
} // namespace net