blob: 80d288dbab2f87fc860a9cce63372b177a1fbd82 [file] [log] [blame]
// Copyright 2013 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 "chrome/browser/extensions/api/cast_channel/cast_socket.h"
#include "chrome/browser/extensions/api/cast_channel/cast_channel.pb.h"
#include "chrome/browser/extensions/api/cast_channel/cast_message_util.h"
#include "net/base/address_list.h"
#include "net/base/capturing_net_log.h"
#include "net/base/io_buffer.h"
#include "net/base/net_errors.h"
#include "net/base/net_log.h"
#include "net/socket/socket_test_util.h"
#include "net/socket/ssl_client_socket.h"
#include "net/socket/tcp_client_socket.h"
#include "net/ssl/ssl_info.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
using ::testing::_;
using ::testing::A;
using ::testing::DoAll;
using ::testing::Return;
using ::testing::SaveArg;
namespace extensions {
namespace api {
namespace cast_channel {
class MockCastSocketDelegate : public CastSocket::Delegate {
public:
MOCK_METHOD2(OnError, void(const CastSocket* socket,
ChannelError error));
MOCK_METHOD2(OnMessage, void(const CastSocket* socket,
const MessageInfo& message));
};
class MockTCPClientSocket : public net::TCPClientSocket {
public:
explicit MockTCPClientSocket() :
TCPClientSocket(net::AddressList(), NULL, net::NetLog::Source()) { }
virtual ~MockTCPClientSocket() { }
MOCK_METHOD1(Connect, int(const net::CompletionCallback& callback));
MOCK_METHOD2(SetKeepAlive, bool(bool, int));
MOCK_METHOD1(SetNoDelay, bool(bool));
MOCK_METHOD3(Read, int(net::IOBuffer* buf, int buf_len,
const net::CompletionCallback& callback));
MOCK_METHOD3(Write, int(net::IOBuffer* buf, int buf_len,
const net::CompletionCallback& callback));
MOCK_METHOD0(Disconnect, void());
};
class MockSSLClientSocket : public net::MockClientSocket {
public:
MockSSLClientSocket() : MockClientSocket(net::BoundNetLog()) { }
virtual ~MockSSLClientSocket() { }
MOCK_METHOD1(Connect, int(const net::CompletionCallback& callback));
MOCK_METHOD3(Read, int(net::IOBuffer* buf, int buf_len,
const net::CompletionCallback& callback));
MOCK_METHOD3(Write, int(net::IOBuffer* buf, int buf_len,
const net::CompletionCallback& callback));
MOCK_METHOD0(Disconnect, void());
MOCK_CONST_METHOD0(WasEverUsed, bool());
MOCK_CONST_METHOD0(UsingTCPFastOpen, bool());
MOCK_CONST_METHOD0(WasNpnNegotiated, bool());
MOCK_METHOD1(GetSSLInfo, bool(net::SSLInfo*));
};
class CompleteHandler {
public:
CompleteHandler() {}
MOCK_METHOD1(OnCloseComplete, void(int result));
MOCK_METHOD1(OnConnectComplete, void(int result));
MOCK_METHOD1(OnWriteComplete, void(int result));
private:
DISALLOW_COPY_AND_ASSIGN(CompleteHandler);
};
class TestCastSocket : public CastSocket {
public:
static scoped_ptr<TestCastSocket> Create(
MockCastSocketDelegate* delegate) {
return scoped_ptr<TestCastSocket>(
new TestCastSocket(delegate, "cast://192.0.0.1:8009"));
}
static scoped_ptr<TestCastSocket> CreateSecure(
MockCastSocketDelegate* delegate) {
return scoped_ptr<TestCastSocket>(
new TestCastSocket(delegate, "casts://192.0.0.1:8009"));
}
explicit TestCastSocket(MockCastSocketDelegate* delegate,
const std::string& url) :
CastSocket("abcdefg", GURL(url), delegate,
&capturing_net_log_),
mock_tcp_socket_(new MockTCPClientSocket()),
mock_ssl_socket_(new MockSSLClientSocket()),
owns_tcp_socket_(true),
owns_ssl_socket_(true),
extract_cert_result_(true),
send_auth_challenge_result_(net::ERR_IO_PENDING),
read_auth_challenge_reply_result_(net::ERR_IO_PENDING),
challenge_reply_result_(true) {
}
virtual ~TestCastSocket() {
if (owns_tcp_socket_) {
DCHECK(mock_tcp_socket_);
delete mock_tcp_socket_;
}
if (owns_ssl_socket_) {
DCHECK(mock_ssl_socket_);
delete mock_ssl_socket_;
}
}
virtual void Close(const net::CompletionCallback& callback) OVERRIDE {
if (!owns_tcp_socket_)
mock_tcp_socket_ = NULL;
if (!owns_ssl_socket_)
mock_ssl_socket_ = NULL;
CastSocket::Close(callback);
}
void CreateNewSockets() {
owns_tcp_socket_ = true;
mock_tcp_socket_ = new MockTCPClientSocket();
owns_ssl_socket_ = true;
mock_ssl_socket_ = new MockSSLClientSocket();
}
void SetExtractCertResult(bool value) {
extract_cert_result_ = value;
}
void SetSendAuthChallengeResult(int result) {
send_auth_challenge_result_ = result;
}
void SetReadAuthChallengeReplyResult(int result) {
read_auth_challenge_reply_result_ = result;
}
void SetChallengeReplyResult(bool value) {
challenge_reply_result_ = value;
}
MockTCPClientSocket* mock_tcp_socket_;
MockSSLClientSocket* mock_ssl_socket_;
protected:
virtual scoped_ptr<net::TCPClientSocket> CreateTcpSocket() OVERRIDE {
owns_tcp_socket_ = false;
return scoped_ptr<net::TCPClientSocket>(mock_tcp_socket_);
}
virtual scoped_ptr<net::SSLClientSocket> CreateSslSocket() OVERRIDE {
owns_ssl_socket_ = false;
return scoped_ptr<net::SSLClientSocket>(mock_ssl_socket_);
}
virtual bool ExtractPeerCert(std::string* cert) OVERRIDE {
if (extract_cert_result_)
cert->assign("dummy_test_cert");
return extract_cert_result_;
}
virtual int SendAuthChallenge() OVERRIDE {
return send_auth_challenge_result_;
}
virtual int ReadAuthChallengeReply() OVERRIDE {
return read_auth_challenge_reply_result_;
}
virtual bool VerifyChallengeReply() OVERRIDE {
return challenge_reply_result_;
}
private:
net::CapturingNetLog capturing_net_log_;
// Whether this object or the parent owns |mock_tcp_socket_|.
bool owns_tcp_socket_;
// Whether this object or the parent owns |mock_ssl_socket_|.
bool owns_ssl_socket_;
// Simulated result of peer cert extraction.
bool extract_cert_result_;
// Simulated result to be returned by SendAuthChallenge.
int send_auth_challenge_result_;
// Simulated result to be returned by ReadAuthChallengeReply.
int read_auth_challenge_reply_result_;
// Simulated result of verifying challenge reply.
bool challenge_reply_result_;
};
class CastSocketTest : public testing::Test {
public:
CastSocketTest() {}
virtual ~CastSocketTest() {}
virtual void SetUp() OVERRIDE {
test_message_.namespace_ = "urn:test";
test_message_.source_id = "1";
test_message_.destination_id = "2";
test_message_.data.reset(new base::StringValue("Hello, World!"));
ASSERT_TRUE(MessageInfoToCastMessage(test_message_, &test_proto_));
}
virtual void TearDown() OVERRIDE {
EXPECT_CALL(handler_, OnCloseComplete(net::OK));
socket_->Close(base::Bind(&CompleteHandler::OnCloseComplete,
base::Unretained(&handler_)));
}
void CreateCastSocket() {
socket_ = TestCastSocket::Create(&mock_delegate_);
}
void CreateCastSocketSecure() {
socket_ = TestCastSocket::CreateSecure(&mock_delegate_);
}
// Sets an expectation that TCPClientSocket::Connect is called and
// returns |result| and stores the callback passed to it in |callback|.
void ExpectTcpConnect(net::CompletionCallback* callback, int result) {
EXPECT_CALL(mock_tcp_socket(), Connect(A<const net::CompletionCallback&>()))
.Times(1)
.WillOnce(DoAll(SaveArg<0>(callback), Return(result)));
EXPECT_CALL(mock_tcp_socket(), SetKeepAlive(_, _))
.WillOnce(Return(true));
}
// Same as ExpectTcpConnect but to return net::ERR_IO_PENDING.
void ExpectTcpConnectPending(net::CompletionCallback* callback) {
ExpectTcpConnect(callback, net::ERR_IO_PENDING);
}
// Sets an expectation that SSLClientSocket::Connect is called and
// returns |result| and stores the callback passed to it in |callback|.
void ExpectSslConnect(net::CompletionCallback* callback, int result) {
EXPECT_CALL(mock_ssl_socket(), Connect(A<const net::CompletionCallback&>()))
.Times(1)
.WillOnce(DoAll(SaveArg<0>(callback), Return(result)));
}
// Same as ExpectSslConnect but to return net::ERR_IO_PENDING.
void ExpectSslConnectPending(net::CompletionCallback* callback) {
ExpectSslConnect(callback, net::ERR_IO_PENDING);
}
// Sets an expectation that SSLClientSocket::Read is called |times| number
// of times and returns net::ERR_IO_PENDING.
void ExpectSslRead(int times) {
EXPECT_CALL(mock_ssl_socket(), Read(A<net::IOBuffer*>(),
A<int>(),
A<const net::CompletionCallback&>()))
.Times(times)
.WillOnce(Return(net::ERR_IO_PENDING));
}
// Sets up CastSocket::Connect to succeed.
// Connecting the socket also starts the read loop; we expect the call to
// Read(), but never fire the read callback.
void ConnectHelper() {
net::CompletionCallback connect_callback1;
net::CompletionCallback connect_callback2;
ExpectTcpConnect(&connect_callback1, net::OK);
ExpectSslConnect(&connect_callback2, net::OK);
EXPECT_CALL(handler_, OnConnectComplete(net::OK));
ExpectSslRead(1);
socket_->Connect(base::Bind(&CompleteHandler::OnConnectComplete,
base::Unretained(&handler_)));
EXPECT_EQ(cast_channel::READY_STATE_OPEN, socket_->ready_state());
EXPECT_EQ(cast_channel::CHANNEL_ERROR_NONE, socket_->error_state());
}
protected:
MockTCPClientSocket& mock_tcp_socket() {
MockTCPClientSocket* mock_socket = socket_->mock_tcp_socket_;
DCHECK(mock_socket);
return *mock_socket;
}
MockSSLClientSocket& mock_ssl_socket() {
MockSSLClientSocket* mock_socket = socket_->mock_ssl_socket_;
DCHECK(mock_socket);
return *mock_socket;
}
void CallOnChallengeEvent(int result) {
socket_->OnChallengeEvent(result);
}
MockCastSocketDelegate mock_delegate_;
scoped_ptr<TestCastSocket> socket_;
CompleteHandler handler_;
MessageInfo test_message_;
CastMessage test_proto_;
};
// Tests URL parsing and validation.
TEST_F(CastSocketTest, TestCastURLs) {
CreateCastSocket();
EXPECT_TRUE(socket_->ParseChannelUrl(GURL("cast://192.0.0.1:8009")));
EXPECT_FALSE(socket_->auth_required());
EXPECT_EQ(socket_->ip_endpoint_.ToString(), "192.0.0.1:8009");
EXPECT_TRUE(socket_->ParseChannelUrl(GURL("casts://192.0.0.1:12345")));
EXPECT_TRUE(socket_->auth_required());
EXPECT_EQ(socket_->ip_endpoint_.ToString(), "192.0.0.1:12345");
EXPECT_FALSE(socket_->ParseChannelUrl(GURL("http://192.0.0.1:12345")));
EXPECT_FALSE(socket_->ParseChannelUrl(GURL("cast:192.0.0.1:12345")));
EXPECT_FALSE(socket_->ParseChannelUrl(GURL("cast:///192.0.0.1:12345")));
EXPECT_FALSE(socket_->ParseChannelUrl(GURL("cast://:12345")));
EXPECT_FALSE(socket_->ParseChannelUrl(GURL("cast://abcd:8009")));
EXPECT_FALSE(socket_->ParseChannelUrl(GURL("cast://192.0.0.1:abcd")));
EXPECT_FALSE(socket_->ParseChannelUrl(GURL("")));
EXPECT_FALSE(socket_->ParseChannelUrl(GURL("foo")));
EXPECT_FALSE(socket_->ParseChannelUrl(GURL("cast:")));
EXPECT_FALSE(socket_->ParseChannelUrl(GURL("cast::")));
EXPECT_FALSE(socket_->ParseChannelUrl(GURL("cast://192.0.0.1")));
EXPECT_FALSE(socket_->ParseChannelUrl(GURL("cast://:")));
EXPECT_FALSE(socket_->ParseChannelUrl(GURL("cast://192.0.0.1:")));
}
// Tests connecting and closing the socket.
TEST_F(CastSocketTest, TestConnectAndClose) {
CreateCastSocket();
ConnectHelper();
EXPECT_CALL(handler_, OnCloseComplete(net::OK));
socket_->Close(base::Bind(&CompleteHandler::OnCloseComplete,
base::Unretained(&handler_)));
EXPECT_EQ(cast_channel::READY_STATE_CLOSED, socket_->ready_state());
EXPECT_EQ(cast_channel::CHANNEL_ERROR_NONE, socket_->error_state());
}
// Tests that the following connection flow works:
// - TCP connection succeeds (async)
// - SSL connection succeeds (async)
TEST_F(CastSocketTest, TestConnect) {
CreateCastSocket();
net::CompletionCallback connect_callback1;
net::CompletionCallback connect_callback2;
ExpectTcpConnectPending(&connect_callback1);
ExpectSslConnectPending(&connect_callback2);
EXPECT_CALL(handler_, OnConnectComplete(net::OK));
ExpectSslRead(1);
socket_->Connect(base::Bind(&CompleteHandler::OnConnectComplete,
base::Unretained(&handler_)));
connect_callback1.Run(net::OK);
connect_callback2.Run(net::OK);
EXPECT_EQ(cast_channel::READY_STATE_OPEN, socket_->ready_state());
EXPECT_EQ(cast_channel::CHANNEL_ERROR_NONE, socket_->error_state());
}
// Test that the following connection flow works:
// - TCP connection succeeds (async)
// - SSL connection fails with cert error (async)
// - Cert is extracted successfully
// - Second TCP connection succeeds (async)
// - Second SSL connection succeeds (async)
TEST_F(CastSocketTest, TestTwoStepConnect) {
CreateCastSocket();
// Expectations for the initial connect call
net::CompletionCallback tcp_connect_callback1;
net::CompletionCallback ssl_connect_callback1;
ExpectTcpConnectPending(&tcp_connect_callback1);
ExpectSslConnectPending(&ssl_connect_callback1);
// Start connect flow
socket_->Connect(base::Bind(&CompleteHandler::OnConnectComplete,
base::Unretained(&handler_)));
tcp_connect_callback1.Run(net::OK);
// Expectations for the second connect call
socket_->CreateNewSockets();
net::CompletionCallback tcp_connect_callback2;
net::CompletionCallback ssl_connect_callback2;
ExpectTcpConnectPending(&tcp_connect_callback2);
ExpectSslConnectPending(&ssl_connect_callback2);
EXPECT_CALL(handler_, OnConnectComplete(net::OK));
ExpectSslRead(1);
// Trigger callbacks for the first connect
ssl_connect_callback1.Run(net::ERR_CERT_AUTHORITY_INVALID);
// Trigger callbacks for the second connect
tcp_connect_callback2.Run(net::OK);
ssl_connect_callback2.Run(net::OK);
EXPECT_EQ(cast_channel::READY_STATE_OPEN, socket_->ready_state());
EXPECT_EQ(cast_channel::CHANNEL_ERROR_NONE, socket_->error_state());
}
// Test that the following connection flow works:
// - TCP connection succeeds (async)
// - SSL connection fails with cert error (async)
// - Cert is extracted successfully
// - Second TCP connection succeeds (async)
// - Second SSL connection fails (async)
// - The flow should NOT be tried again
TEST_F(CastSocketTest, TestMaxTwoConnectAttempts) {
CreateCastSocket();
net::CompletionCallback tcp_connect_callback1;
net::CompletionCallback ssl_connect_callback1;
// Expectations for the initial connect call
ExpectTcpConnectPending(&tcp_connect_callback1);
ExpectSslConnectPending(&ssl_connect_callback1);
// Start connect flow
socket_->Connect(base::Bind(&CompleteHandler::OnConnectComplete,
base::Unretained(&handler_)));
tcp_connect_callback1.Run(net::OK);
socket_->CreateNewSockets();
net::CompletionCallback tcp_connect_callback2;
net::CompletionCallback ssl_connect_callback2;
// Expectations for the second connect call
ExpectTcpConnectPending(&tcp_connect_callback2);
ExpectSslConnectPending(&ssl_connect_callback2);
EXPECT_CALL(handler_, OnConnectComplete(net::ERR_CERT_AUTHORITY_INVALID));
// Trigger callbacks for the first connect
ssl_connect_callback1.Run(net::ERR_CERT_AUTHORITY_INVALID);
// Trigger callbacks for the second connect
tcp_connect_callback2.Run(net::OK);
ssl_connect_callback2.Run(net::ERR_CERT_AUTHORITY_INVALID);
EXPECT_EQ(cast_channel::READY_STATE_CLOSED, socket_->ready_state());
EXPECT_EQ(cast_channel::CHANNEL_ERROR_CONNECT_ERROR, socket_->error_state());
}
// Test that when cert extraction fails the connection flow stops.
TEST_F(CastSocketTest, TestCertExtractionFailure) {
CreateCastSocket();
net::CompletionCallback connect_callback1;
net::CompletionCallback connect_callback2;
ExpectTcpConnectPending(&connect_callback1);
ExpectSslConnectPending(&connect_callback2);
socket_->Connect(base::Bind(&CompleteHandler::OnConnectComplete,
base::Unretained(&handler_)));
connect_callback1.Run(net::OK);
EXPECT_CALL(handler_, OnConnectComplete(net::ERR_CERT_AUTHORITY_INVALID));
// Set cert extraction to fail
socket_->SetExtractCertResult(false);
// Attempt to connect results in ERR_CERT_AUTHORTY_INVALID
connect_callback2.Run(net::ERR_CERT_AUTHORITY_INVALID);
EXPECT_EQ(cast_channel::READY_STATE_CLOSED, socket_->ready_state());
EXPECT_EQ(cast_channel::CHANNEL_ERROR_CONNECT_ERROR, socket_->error_state());
}
// Tests that the following connection flow works:
// - TCP connection succeeds (async)
// - SSL connection fails with cert error (async)
// - Cert is extracted successfully
// - Second TCP connection succeeds (async)
// - Second SSL connection succeeds (async)
// - Challenge request is sent (async)
// - Challenge response is received (async)
// - Credentials are verified successfuly
TEST_F(CastSocketTest, TestFullSecureConnectionFlowAsync) {
CreateCastSocketSecure();
net::CompletionCallback tcp_connect_callback1;
net::CompletionCallback ssl_connect_callback1;
// Expectations for the initial connect call
ExpectTcpConnectPending(&tcp_connect_callback1);
ExpectSslConnectPending(&ssl_connect_callback1);
// Start connect flow
socket_->Connect(base::Bind(&CompleteHandler::OnConnectComplete,
base::Unretained(&handler_)));
tcp_connect_callback1.Run(net::OK);
socket_->CreateNewSockets();
net::CompletionCallback tcp_connect_callback2;
net::CompletionCallback ssl_connect_callback2;
// Expectations for the second connect call
ExpectTcpConnectPending(&tcp_connect_callback2);
ExpectSslConnectPending(&ssl_connect_callback2);
EXPECT_CALL(handler_, OnConnectComplete(net::OK));
// Trigger callbacks for the first connect
ssl_connect_callback1.Run(net::ERR_CERT_AUTHORITY_INVALID);
// Trigger callbacks for the second connect
tcp_connect_callback2.Run(net::OK);
ssl_connect_callback2.Run(net::OK);
// Trigger callbacks for auth events.
CallOnChallengeEvent(net::OK); // Sent challenge
CallOnChallengeEvent(net::OK); // Received reply
EXPECT_EQ(cast_channel::READY_STATE_OPEN, socket_->ready_state());
EXPECT_EQ(cast_channel::CHANNEL_ERROR_NONE, socket_->error_state());
}
// Same as TestFullSecureConnectionFlowAsync, but operations are synchronous.
TEST_F(CastSocketTest, TestFullSecureConnectionFlowSync) {
CreateCastSocketSecure();
net::CompletionCallback tcp_connect_callback;
net::CompletionCallback ssl_connect_callback;
// Expectations for the connect calls
ExpectTcpConnect(&tcp_connect_callback, net::OK);
ExpectSslConnect(&ssl_connect_callback, net::OK);
EXPECT_CALL(handler_, OnConnectComplete(net::OK));
socket_->SetSendAuthChallengeResult(net::OK);
socket_->SetReadAuthChallengeReplyResult(net::OK);
// Start connect flow
socket_->Connect(base::Bind(&CompleteHandler::OnConnectComplete,
base::Unretained(&handler_)));
EXPECT_EQ(cast_channel::READY_STATE_OPEN, socket_->ready_state());
EXPECT_EQ(cast_channel::CHANNEL_ERROR_NONE, socket_->error_state());
}
// Tests writing a single message where the completion is signaled via
// callback.
TEST_F(CastSocketTest, TestWriteViaCallback) {
CreateCastSocket();
ConnectHelper();
net::CompletionCallback write_callback;
EXPECT_CALL(mock_ssl_socket(),
Write(A<net::IOBuffer*>(),
39,
A<const net::CompletionCallback&>()))
.Times(1)
.WillOnce(DoAll(SaveArg<2>(&write_callback),
Return(net::ERR_IO_PENDING)));
EXPECT_CALL(handler_, OnWriteComplete(39));
socket_->SendMessage(test_message_,
base::Bind(&CompleteHandler::OnWriteComplete,
base::Unretained(&handler_)));
write_callback.Run(39);
EXPECT_EQ(cast_channel::READY_STATE_OPEN, socket_->ready_state());
EXPECT_EQ(cast_channel::CHANNEL_ERROR_NONE, socket_->error_state());
}
// Tests writing a single message where the Write() returns directly.
TEST_F(CastSocketTest, TestWrite) {
CreateCastSocket();
ConnectHelper();
EXPECT_CALL(mock_ssl_socket(),
Write(A<net::IOBuffer*>(),
39,
A<const net::CompletionCallback&>()))
.Times(1)
.WillOnce(Return(39));
EXPECT_CALL(handler_, OnWriteComplete(39));
socket_->SendMessage(test_message_,
base::Bind(&CompleteHandler::OnWriteComplete,
base::Unretained(&handler_)));
EXPECT_EQ(cast_channel::READY_STATE_OPEN, socket_->ready_state());
EXPECT_EQ(cast_channel::CHANNEL_ERROR_NONE, socket_->error_state());
}
// Tests writing multiple messages.
TEST_F(CastSocketTest, TestWriteMany) {
CreateCastSocket();
ConnectHelper();
std::string messages[4];
messages[0] = "Hello, World!";
messages[1] = "Goodbye, World!";
messages[2] = "Hello, Sky!";
messages[3] = "Goodbye, Volcano!";
int sizes[4] = {39, 41, 37, 43};
MessageInfo message_info[4];
net::CompletionCallback write_callback;
for (int i = 0; i < 4; i++) {
EXPECT_CALL(mock_ssl_socket(),
Write(A<net::IOBuffer*>(),
sizes[i],
A<const net::CompletionCallback&>()))
.WillRepeatedly(DoAll(SaveArg<2>(&write_callback),
Return(net::ERR_IO_PENDING)));
EXPECT_CALL(handler_, OnWriteComplete(sizes[i]));
}
for (int i = 0; i < 4; i++) {
message_info[i].namespace_ = "urn:test";
message_info[i].source_id = "1";
message_info[i].destination_id = "2";
message_info[i].data.reset(new base::StringValue(messages[i]));
socket_->SendMessage(message_info[i],
base::Bind(&CompleteHandler::OnWriteComplete,
base::Unretained(&handler_)));
}
for (int i = 0; i < 4; i++) {
write_callback.Run(sizes[i]);
}
EXPECT_EQ(cast_channel::READY_STATE_OPEN, socket_->ready_state());
EXPECT_EQ(cast_channel::CHANNEL_ERROR_NONE, socket_->error_state());
}
// Tests error on writing.
TEST_F(CastSocketTest, TestWriteError) {
CreateCastSocket();
ConnectHelper();
net::CompletionCallback write_callback;
EXPECT_CALL(mock_ssl_socket(),
Write(A<net::IOBuffer*>(),
39,
A<const net::CompletionCallback&>()))
.Times(1)
.WillOnce(DoAll(SaveArg<2>(&write_callback),
Return(net::ERR_SOCKET_NOT_CONNECTED)));
EXPECT_CALL(handler_, OnWriteComplete(net::ERR_SOCKET_NOT_CONNECTED));
EXPECT_CALL(mock_delegate_,
OnError(socket_.get(), cast_channel::CHANNEL_ERROR_SOCKET_ERROR));
socket_->SendMessage(test_message_,
base::Bind(&CompleteHandler::OnWriteComplete,
base::Unretained(&handler_)));
EXPECT_EQ(cast_channel::READY_STATE_CLOSED, socket_->ready_state());
EXPECT_EQ(cast_channel::CHANNEL_ERROR_SOCKET_ERROR, socket_->error_state());
}
// Tests reading a single message.
TEST_F(CastSocketTest, TestRead) {
CreateCastSocket();
net::CompletionCallback connect_callback1;
net::CompletionCallback connect_callback2;
net::CompletionCallback read_callback;
std::string message_data;
ASSERT_TRUE(CastSocket::Serialize(test_proto_, &message_data));
ExpectTcpConnect(&connect_callback1, net::ERR_IO_PENDING);
ExpectSslConnect(&connect_callback2, net::ERR_IO_PENDING);
EXPECT_CALL(handler_, OnConnectComplete(net::OK));
EXPECT_CALL(mock_ssl_socket(), Read(A<net::IOBuffer*>(),
A<int>(),
A<const net::CompletionCallback&>()))
.Times(3)
.WillRepeatedly(DoAll(SaveArg<2>(&read_callback),
Return(net::ERR_IO_PENDING)));
// Expect the test message to be read and invoke the delegate.
EXPECT_CALL(mock_delegate_,
OnMessage(socket_.get(), A<const MessageInfo&>()));
// Connect the socket.
socket_->Connect(base::Bind(&CompleteHandler::OnConnectComplete,
base::Unretained(&handler_)));
connect_callback1.Run(net::OK);
connect_callback2.Run(net::OK);
// Put the test header and message into the io_buffers and invoke the read
// callbacks.
memcpy(socket_->header_read_buffer_->StartOfBuffer(),
message_data.c_str(), 4);
read_callback.Run(4);
memcpy(socket_->body_read_buffer_->StartOfBuffer(),
message_data.c_str() + 4, 35);
read_callback.Run(35);
EXPECT_EQ(cast_channel::READY_STATE_OPEN, socket_->ready_state());
EXPECT_EQ(cast_channel::CHANNEL_ERROR_NONE, socket_->error_state());
}
// Tests reading multiple messages.
TEST_F(CastSocketTest, TestReadMany) {
CreateCastSocket();
net::CompletionCallback connect_callback1;
net::CompletionCallback connect_callback2;
net::CompletionCallback read_callback;
std::string messages[4];
messages[0] = "Hello, World!";
messages[1] = "Goodbye, World!";
messages[2] = "Hello, Sky!";
messages[3] = "Goodbye, Volcano!";
int sizes[4] = {35, 37, 33, 39};
std::string message_data[4];
// Set up test data
for (int i = 0; i < 4; i++) {
test_proto_.set_payload_utf8(messages[i]);
ASSERT_TRUE(CastSocket::Serialize(test_proto_, &message_data[i]));
}
ExpectTcpConnect(&connect_callback1, net::ERR_IO_PENDING);
ExpectSslConnect(&connect_callback2, net::ERR_IO_PENDING);
EXPECT_CALL(handler_, OnConnectComplete(net::OK));
EXPECT_CALL(mock_ssl_socket(), Read(A<net::IOBuffer*>(),
A<int>(),
A<const net::CompletionCallback&>()))
.Times(9)
.WillRepeatedly(DoAll(SaveArg<2>(&read_callback),
Return(net::ERR_IO_PENDING)));
// Expect the test messages to be read and invoke the delegate.
EXPECT_CALL(mock_delegate_, OnMessage(socket_.get(),
A<const MessageInfo&>()))
.Times(4);
// Connect the socket.
socket_->Connect(base::Bind(&CompleteHandler::OnConnectComplete,
base::Unretained(&handler_)));
connect_callback1.Run(net::OK);
connect_callback2.Run(net::OK);
// Put the test headers and messages into the io_buffer and invoke the read
// callbacks.
for (int i = 0; i < 4; i++) {
memcpy(socket_->header_read_buffer_->StartOfBuffer(),
message_data[i].c_str(), 4);
read_callback.Run(4);
memcpy(socket_->body_read_buffer_->StartOfBuffer(),
message_data[i].c_str() + 4, sizes[i]);
read_callback.Run(sizes[i]);
}
EXPECT_EQ(cast_channel::READY_STATE_OPEN, socket_->ready_state());
EXPECT_EQ(cast_channel::CHANNEL_ERROR_NONE, socket_->error_state());
}
// Tests error on reading.
TEST_F(CastSocketTest, TestReadError) {
CreateCastSocket();
net::CompletionCallback connect_callback1;
net::CompletionCallback connect_callback2;
net::CompletionCallback read_callback;
ExpectTcpConnect(&connect_callback1, net::ERR_IO_PENDING);
ExpectSslConnect(&connect_callback2, net::ERR_IO_PENDING);
EXPECT_CALL(handler_, OnConnectComplete(net::OK));
EXPECT_CALL(mock_ssl_socket(), Read(A<net::IOBuffer*>(),
A<int>(),
A<const net::CompletionCallback&>()))
.WillOnce(DoAll(SaveArg<2>(&read_callback),
Return(net::ERR_IO_PENDING)));
EXPECT_CALL(mock_delegate_,
OnError(socket_.get(), cast_channel::CHANNEL_ERROR_SOCKET_ERROR));
// Connect the socket.
socket_->Connect(base::Bind(&CompleteHandler::OnConnectComplete,
base::Unretained(&handler_)));
connect_callback1.Run(net::OK);
connect_callback2.Run(net::OK);
// Cause an error.
read_callback.Run(net::ERR_SOCKET_NOT_CONNECTED);
EXPECT_EQ(cast_channel::READY_STATE_CLOSED, socket_->ready_state());
EXPECT_EQ(cast_channel::CHANNEL_ERROR_SOCKET_ERROR, socket_->error_state());
}
} // namespace cast_channel
} // namespace api
} // namespace extensions