Commit d9e97076 authored by Maks Orlovich's avatar Maks Orlovich Committed by Commit Bot

jingle_glue: Prep CL to make migrating to NetworkService easier

This CL's contents are entirely:
cp chrome_async_socket.cc network_service_async_socket.cc
cp chrome_async_socket.h network_service_async_socket.h
cp chrome_async_socket_unittest.cc network_service_async_socket_unittest.cc

... done as a separate change so that the CL making the actual porting
changes gets a usable diff between net and NetworkService versions of code.
The "new" files are not included in compilation and this is meant to be
landed together with
https://chromium-review.googlesource.com/c/chromium/src/+/1232034

Bug: 875032
Change-Id: Idcdc85125c910662e642258adb71d58406752a64
Reviewed-on: https://chromium-review.googlesource.com/c/1237214Reviewed-by: default avatarNicolas Zea <zea@chromium.org>
Commit-Queue: Maks Orlovich <morlovich@chromium.org>
Cr-Commit-Position: refs/heads/master@{#604113}
parent c4720a76
// 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 "jingle/glue/chrome_async_socket.h"
#include <stddef.h>
#include <algorithm>
#include <cstdlib>
#include <cstring>
#include <utility>
#include "base/bind.h"
#include "base/compiler_specific.h"
#include "base/logging.h"
#include "base/threading/thread_task_runner_handle.h"
#include "jingle/glue/resolving_client_socket_factory.h"
#include "net/base/address_list.h"
#include "net/base/host_port_pair.h"
#include "net/base/io_buffer.h"
#include "net/socket/client_socket_handle.h"
#include "net/socket/ssl_client_socket.h"
#include "net/socket/tcp_client_socket.h"
#include "net/ssl/ssl_config_service.h"
#include "third_party/webrtc/rtc_base/socketaddress.h"
namespace jingle_glue {
ChromeAsyncSocket::ChromeAsyncSocket(
std::unique_ptr<ResolvingClientSocketFactory>
resolving_client_socket_factory,
size_t read_buf_size,
size_t write_buf_size,
const net::NetworkTrafficAnnotationTag& traffic_annotation)
: resolving_client_socket_factory_(
std::move(resolving_client_socket_factory)),
state_(STATE_CLOSED),
error_(ERROR_NONE),
net_error_(net::OK),
read_state_(IDLE),
read_buf_(base::MakeRefCounted<net::IOBufferWithSize>(read_buf_size)),
read_start_(0U),
read_end_(0U),
write_state_(IDLE),
write_buf_(base::MakeRefCounted<net::IOBufferWithSize>(write_buf_size)),
write_end_(0U),
traffic_annotation_(traffic_annotation),
weak_ptr_factory_(this) {
DCHECK(resolving_client_socket_factory_.get());
DCHECK_GT(read_buf_size, 0U);
DCHECK_GT(write_buf_size, 0U);
}
ChromeAsyncSocket::~ChromeAsyncSocket() {}
ChromeAsyncSocket::State ChromeAsyncSocket::state() {
return state_;
}
ChromeAsyncSocket::Error ChromeAsyncSocket::error() {
return error_;
}
int ChromeAsyncSocket::GetError() {
return net_error_;
}
bool ChromeAsyncSocket::IsOpen() const {
return (state_ == STATE_OPEN) || (state_ == STATE_TLS_OPEN);
}
void ChromeAsyncSocket::DoNonNetError(Error error) {
DCHECK_NE(error, ERROR_NONE);
DCHECK_NE(error, ERROR_WINSOCK);
error_ = error;
net_error_ = net::OK;
}
void ChromeAsyncSocket::DoNetError(net::Error net_error) {
error_ = ERROR_WINSOCK;
net_error_ = net_error;
}
void ChromeAsyncSocket::DoNetErrorFromStatus(int status) {
DCHECK_LT(status, net::OK);
DoNetError(static_cast<net::Error>(status));
}
// STATE_CLOSED -> STATE_CONNECTING
bool ChromeAsyncSocket::Connect(const rtc::SocketAddress& address) {
if (state_ != STATE_CLOSED) {
LOG(DFATAL) << "Connect() called on non-closed socket";
DoNonNetError(ERROR_WRONGSTATE);
return false;
}
if (address.hostname().empty() || address.port() == 0) {
DoNonNetError(ERROR_DNS);
return false;
}
DCHECK_EQ(state_, buzz::AsyncSocket::STATE_CLOSED);
DCHECK_EQ(read_state_, IDLE);
DCHECK_EQ(write_state_, IDLE);
state_ = STATE_CONNECTING;
DCHECK(!weak_ptr_factory_.HasWeakPtrs());
weak_ptr_factory_.InvalidateWeakPtrs();
net::HostPortPair dest_host_port_pair(address.hostname(), address.port());
transport_socket_ =
resolving_client_socket_factory_->CreateTransportClientSocket(
dest_host_port_pair);
int status = transport_socket_->Connect(
base::Bind(&ChromeAsyncSocket::ProcessConnectDone,
weak_ptr_factory_.GetWeakPtr()));
if (status != net::ERR_IO_PENDING) {
// We defer execution of ProcessConnectDone instead of calling it
// directly here as the caller may not expect an error/close to
// happen here. This is okay, as from the caller's point of view,
// the connect always happens asynchronously.
base::ThreadTaskRunnerHandle::Get()->PostTask(
FROM_HERE, base::Bind(&ChromeAsyncSocket::ProcessConnectDone,
weak_ptr_factory_.GetWeakPtr(), status));
}
return true;
}
// STATE_CONNECTING -> STATE_OPEN
// read_state_ == IDLE -> read_state_ == POSTED (via PostDoRead())
void ChromeAsyncSocket::ProcessConnectDone(int status) {
DCHECK_NE(status, net::ERR_IO_PENDING);
DCHECK_EQ(read_state_, IDLE);
DCHECK_EQ(write_state_, IDLE);
DCHECK_EQ(state_, STATE_CONNECTING);
if (status != net::OK) {
DoNetErrorFromStatus(status);
DoClose();
return;
}
state_ = STATE_OPEN;
PostDoRead();
// Write buffer should be empty.
DCHECK_EQ(write_end_, 0U);
SignalConnected();
}
// read_state_ == IDLE -> read_state_ == POSTED
void ChromeAsyncSocket::PostDoRead() {
DCHECK(IsOpen());
DCHECK_EQ(read_state_, IDLE);
DCHECK_EQ(read_start_, 0U);
DCHECK_EQ(read_end_, 0U);
base::ThreadTaskRunnerHandle::Get()->PostTask(
FROM_HERE,
base::Bind(&ChromeAsyncSocket::DoRead, weak_ptr_factory_.GetWeakPtr()));
read_state_ = POSTED;
}
// read_state_ == POSTED -> read_state_ == PENDING
void ChromeAsyncSocket::DoRead() {
DCHECK(IsOpen());
DCHECK_EQ(read_state_, POSTED);
DCHECK_EQ(read_start_, 0U);
DCHECK_EQ(read_end_, 0U);
// Once we call Read(), we cannot call StartTls() until the read
// finishes. This is okay, as StartTls() is called only from a read
// handler (i.e., after a read finishes and before another read is
// done).
int status =
transport_socket_->Read(
read_buf_.get(), read_buf_->size(),
base::Bind(&ChromeAsyncSocket::ProcessReadDone,
weak_ptr_factory_.GetWeakPtr()));
read_state_ = PENDING;
if (status != net::ERR_IO_PENDING) {
ProcessReadDone(status);
}
}
// read_state_ == PENDING -> read_state_ == IDLE
void ChromeAsyncSocket::ProcessReadDone(int status) {
DCHECK_NE(status, net::ERR_IO_PENDING);
DCHECK(IsOpen());
DCHECK_EQ(read_state_, PENDING);
DCHECK_EQ(read_start_, 0U);
DCHECK_EQ(read_end_, 0U);
read_state_ = IDLE;
if (status > 0) {
read_end_ = static_cast<size_t>(status);
SignalRead();
} else if (status == 0) {
// Other side closed the connection.
error_ = ERROR_NONE;
net_error_ = net::OK;
DoClose();
} else { // status < 0
DoNetErrorFromStatus(status);
DoClose();
}
}
// (maybe) read_state_ == IDLE -> read_state_ == POSTED (via
// PostDoRead())
bool ChromeAsyncSocket::Read(char* data, size_t len, size_t* len_read) {
if (!IsOpen() && (state_ != STATE_TLS_CONNECTING)) {
// Read() may be called on a closed socket if a previous read
// causes a socket close (e.g., client sends wrong password and
// server terminates connection).
//
// TODO(akalin): Fix handling of this on the libjingle side.
if (state_ != STATE_CLOSED) {
LOG(DFATAL) << "Read() called on non-open non-tls-connecting socket";
}
DoNonNetError(ERROR_WRONGSTATE);
return false;
}
DCHECK_LE(read_start_, read_end_);
if ((state_ == STATE_TLS_CONNECTING) || read_end_ == 0U) {
if (state_ == STATE_TLS_CONNECTING) {
DCHECK_EQ(read_state_, IDLE);
DCHECK_EQ(read_end_, 0U);
} else {
DCHECK_NE(read_state_, IDLE);
}
*len_read = 0;
return true;
}
DCHECK_EQ(read_state_, IDLE);
*len_read = std::min(len, read_end_ - read_start_);
DCHECK_GT(*len_read, 0U);
std::memcpy(data, read_buf_->data() + read_start_, *len_read);
read_start_ += *len_read;
if (read_start_ == read_end_) {
read_start_ = 0U;
read_end_ = 0U;
// We defer execution of DoRead() here for similar reasons as
// ProcessConnectDone().
PostDoRead();
}
return true;
}
// (maybe) write_state_ == IDLE -> write_state_ == POSTED (via
// PostDoWrite())
bool ChromeAsyncSocket::Write(const char* data, size_t len) {
if (!IsOpen() && (state_ != STATE_TLS_CONNECTING)) {
LOG(DFATAL) << "Write() called on non-open non-tls-connecting socket";
DoNonNetError(ERROR_WRONGSTATE);
return false;
}
// TODO(akalin): Avoid this check by modifying the interface to have
// a "ready for writing" signal.
if ((static_cast<size_t>(write_buf_->size()) - write_end_) < len) {
LOG(DFATAL) << "queueing " << len << " bytes would exceed the "
<< "max write buffer size = " << write_buf_->size()
<< " by " << (len - write_buf_->size()) << " bytes";
DoNetError(net::ERR_INSUFFICIENT_RESOURCES);
return false;
}
std::memcpy(write_buf_->data() + write_end_, data, len);
write_end_ += len;
// If we're TLS-connecting, the write buffer will get flushed once
// the TLS-connect finishes. Otherwise, start writing if we're not
// already writing and we have something to write.
if ((state_ != STATE_TLS_CONNECTING) &&
(write_state_ == IDLE) && (write_end_ > 0U)) {
// We defer execution of DoWrite() here for similar reasons as
// ProcessConnectDone().
PostDoWrite();
}
return true;
}
// write_state_ == IDLE -> write_state_ == POSTED
void ChromeAsyncSocket::PostDoWrite() {
DCHECK(IsOpen());
DCHECK_EQ(write_state_, IDLE);
DCHECK_GT(write_end_, 0U);
base::ThreadTaskRunnerHandle::Get()->PostTask(
FROM_HERE,
base::Bind(&ChromeAsyncSocket::DoWrite, weak_ptr_factory_.GetWeakPtr()));
write_state_ = POSTED;
}
// write_state_ == POSTED -> write_state_ == PENDING
void ChromeAsyncSocket::DoWrite() {
DCHECK(IsOpen());
DCHECK_EQ(write_state_, POSTED);
DCHECK_GT(write_end_, 0U);
// Once we call Write(), we cannot call StartTls() until the write
// finishes. This is okay, as StartTls() is called only after we
// have received a reply to a message we sent to the server and
// before we send the next message.
int status =
transport_socket_->Write(write_buf_.get(), write_end_,
base::Bind(&ChromeAsyncSocket::ProcessWriteDone,
weak_ptr_factory_.GetWeakPtr()),
traffic_annotation_);
write_state_ = PENDING;
if (status != net::ERR_IO_PENDING) {
ProcessWriteDone(status);
}
}
// write_state_ == PENDING -> write_state_ == IDLE or POSTED (the
// latter via PostDoWrite())
void ChromeAsyncSocket::ProcessWriteDone(int status) {
DCHECK_NE(status, net::ERR_IO_PENDING);
DCHECK(IsOpen());
DCHECK_EQ(write_state_, PENDING);
DCHECK_GT(write_end_, 0U);
write_state_ = IDLE;
if (status < net::OK) {
DoNetErrorFromStatus(status);
DoClose();
return;
}
size_t written = static_cast<size_t>(status);
if (written > write_end_) {
LOG(DFATAL) << "bytes written = " << written
<< " exceeds bytes requested = " << write_end_;
DoNetError(net::ERR_UNEXPECTED);
DoClose();
return;
}
// TODO(akalin): Figure out a better way to do this; perhaps a queue
// of DrainableIOBuffers. This'll also allow us to not have an
// artificial buffer size limit.
std::memmove(write_buf_->data(),
write_buf_->data() + written,
write_end_ - written);
write_end_ -= written;
if (write_end_ > 0U) {
PostDoWrite();
}
}
// * -> STATE_CLOSED
bool ChromeAsyncSocket::Close() {
DoClose();
return true;
}
// (not STATE_CLOSED) -> STATE_CLOSED
void ChromeAsyncSocket::DoClose() {
weak_ptr_factory_.InvalidateWeakPtrs();
if (transport_socket_.get()) {
transport_socket_->Disconnect();
}
transport_socket_.reset();
read_state_ = IDLE;
read_start_ = 0U;
read_end_ = 0U;
write_state_ = IDLE;
write_end_ = 0U;
if (state_ != STATE_CLOSED) {
state_ = STATE_CLOSED;
SignalClosed();
}
// Reset error variables after SignalClosed() so slots connected
// to it can read it.
error_ = ERROR_NONE;
net_error_ = net::OK;
}
// STATE_OPEN -> STATE_TLS_CONNECTING
bool ChromeAsyncSocket::StartTls(const std::string& domain_name) {
if ((state_ != STATE_OPEN) || (read_state_ == PENDING) ||
(write_state_ != IDLE)) {
LOG(DFATAL) << "StartTls() called in wrong state";
DoNonNetError(ERROR_WRONGSTATE);
return false;
}
state_ = STATE_TLS_CONNECTING;
read_state_ = IDLE;
read_start_ = 0U;
read_end_ = 0U;
DCHECK_EQ(write_end_, 0U);
// Clear out any posted DoRead() tasks.
weak_ptr_factory_.InvalidateWeakPtrs();
DCHECK(transport_socket_.get());
std::unique_ptr<net::ClientSocketHandle> socket_handle(
new net::ClientSocketHandle());
socket_handle->SetSocket(std::move(transport_socket_));
transport_socket_ = resolving_client_socket_factory_->CreateSSLClientSocket(
std::move(socket_handle), net::HostPortPair(domain_name, 443));
int status = transport_socket_->Connect(
base::Bind(&ChromeAsyncSocket::ProcessSSLConnectDone,
weak_ptr_factory_.GetWeakPtr()));
if (status != net::ERR_IO_PENDING) {
base::ThreadTaskRunnerHandle::Get()->PostTask(
FROM_HERE, base::Bind(&ChromeAsyncSocket::ProcessSSLConnectDone,
weak_ptr_factory_.GetWeakPtr(), status));
}
return true;
}
// STATE_TLS_CONNECTING -> STATE_TLS_OPEN
// read_state_ == IDLE -> read_state_ == POSTED (via PostDoRead())
// (maybe) write_state_ == IDLE -> write_state_ == POSTED (via
// PostDoWrite())
void ChromeAsyncSocket::ProcessSSLConnectDone(int status) {
DCHECK_NE(status, net::ERR_IO_PENDING);
DCHECK_EQ(state_, STATE_TLS_CONNECTING);
DCHECK_EQ(read_state_, IDLE);
DCHECK_EQ(read_start_, 0U);
DCHECK_EQ(read_end_, 0U);
DCHECK_EQ(write_state_, IDLE);
if (status != net::OK) {
DoNetErrorFromStatus(status);
DoClose();
return;
}
state_ = STATE_TLS_OPEN;
PostDoRead();
if (write_end_ > 0U) {
PostDoWrite();
}
SignalSSLConnected();
}
} // namespace jingle_glue
// 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.
//
// An implementation of buzz::AsyncSocket that uses Chrome sockets.
#ifndef JINGLE_GLUE_CHROME_ASYNC_SOCKET_H_
#define JINGLE_GLUE_CHROME_ASYNC_SOCKET_H_
#include <stddef.h>
#include <memory>
#include <string>
#include <vector>
#include "base/compiler_specific.h"
#include "base/macros.h"
#include "base/memory/ref_counted.h"
#include "base/memory/weak_ptr.h"
#include "net/base/completion_callback.h"
#include "net/base/net_errors.h"
#include "net/traffic_annotation/network_traffic_annotation.h"
#include "third_party/libjingle_xmpp/xmpp/asyncsocket.h"
namespace net {
class IOBufferWithSize;
class StreamSocket;
} // namespace net
namespace jingle_glue {
class ResolvingClientSocketFactory;
class ChromeAsyncSocket : public buzz::AsyncSocket {
public:
// Takes ownership of |resolving_client_socket_factory|.
ChromeAsyncSocket(std::unique_ptr<ResolvingClientSocketFactory>
resolving_client_socket_factory,
size_t read_buf_size,
size_t write_buf_size,
const net::NetworkTrafficAnnotationTag& traffic_annotation);
// Does not raise any signals.
~ChromeAsyncSocket() override;
// buzz::AsyncSocket implementation.
// The current state (see buzz::AsyncSocket::State; all but
// STATE_CLOSING is used).
State state() override;
// The last generated error. Errors are generated when the main
// functions below return false or when SignalClosed is raised due
// to an asynchronous error.
Error error() override;
// GetError() (which is of type net::Error) != net::OK only when
// error() == ERROR_WINSOCK.
int GetError() override;
// Tries to connect to the given address.
//
// If state() is not STATE_CLOSED, sets error to ERROR_WRONGSTATE
// and returns false.
//
// If |address| has an empty hostname or a zero port, sets error to
// ERROR_DNS and returns false. (We don't use the IP address even
// if it's present, as DNS resolution is done by
// |resolving_client_socket_factory_|. But it's perfectly fine if
// the hostname is a stringified IP address.)
//
// Otherwise, starts the connection process and returns true.
// SignalConnected will be raised when the connection is successful;
// otherwise, SignalClosed will be raised with a net error set.
bool Connect(const rtc::SocketAddress& address) override;
// Tries to read at most |len| bytes into |data|.
//
// If state() is not STATE_TLS_CONNECTING, STATE_OPEN, or
// STATE_TLS_OPEN, sets error to ERROR_WRONGSTATE and returns false.
//
// Otherwise, fills in |len_read| with the number of bytes read and
// returns true. If this is called when state() is
// STATE_TLS_CONNECTING, reads 0 bytes. (We have to handle this
// case because StartTls() is called during a slot connected to
// SignalRead after parsing the final non-TLS reply from the server
// [see XmppClient::Private::OnSocketRead()].)
bool Read(char* data, size_t len, size_t* len_read) override;
// Queues up |len| bytes of |data| for writing.
//
// If state() is not STATE_TLS_CONNECTING, STATE_OPEN, or
// STATE_TLS_OPEN, sets error to ERROR_WRONGSTATE and returns false.
//
// If the given data is too big for the internal write buffer, sets
// error to ERROR_WINSOCK/net::ERR_INSUFFICIENT_RESOURCES and
// returns false.
//
// Otherwise, queues up the data and returns true. If this is
// called when state() == STATE_TLS_CONNECTING, the data is will be
// sent only after the TLS connection succeeds. (See StartTls()
// below for why this happens.)
//
// Note that there's no guarantee that the data will actually be
// sent; however, it is guaranteed that the any data sent will be
// sent in FIFO order.
bool Write(const char* data, size_t len) override;
// If the socket is not already closed, closes the socket and raises
// SignalClosed. Always returns true.
bool Close() override;
// Tries to change to a TLS connection with the given domain name.
//
// If state() is not STATE_OPEN or there are pending reads or
// writes, sets error to ERROR_WRONGSTATE and returns false. (In
// practice, this means that StartTls() can only be called from a
// slot connected to SignalRead.)
//
// Otherwise, starts the TLS connection process and returns true.
// SignalSSLConnected will be raised when the connection is
// successful; otherwise, SignalClosed will be raised with a net
// error set.
bool StartTls(const std::string& domain_name) override;
// Signal behavior:
//
// SignalConnected: raised whenever the connect initiated by a call
// to Connect() is complete.
//
// SignalSSLConnected: raised whenever the connect initiated by a
// call to StartTls() is complete. Not actually used by
// XmppClient. (It just assumes that if SignalRead is raised after a
// call to StartTls(), the connection has been successfully
// upgraded.)
//
// SignalClosed: raised whenever the socket is closed, either due to
// an asynchronous error, the other side closing the connection, or
// when Close() is called.
//
// SignalRead: raised whenever the next call to Read() will succeed
// with a non-zero |len_read| (assuming nothing else happens in the
// meantime).
//
// SignalError: not used.
private:
enum AsyncIOState {
// An I/O op is not in progress.
IDLE,
// A function has been posted to do the I/O.
POSTED,
// An async I/O operation is pending.
PENDING,
};
bool IsOpen() const;
// Error functions.
void DoNonNetError(Error error);
void DoNetError(net::Error net_error);
void DoNetErrorFromStatus(int status);
// Connection functions.
void ProcessConnectDone(int status);
// Read loop functions.
void PostDoRead();
void DoRead();
void ProcessReadDone(int status);
// Write loop functions.
void PostDoWrite();
void DoWrite();
void ProcessWriteDone(int status);
// SSL/TLS connection functions.
void ProcessSSLConnectDone(int status);
// Close functions.
void DoClose();
std::unique_ptr<ResolvingClientSocketFactory>
resolving_client_socket_factory_;
// buzz::AsyncSocket state.
buzz::AsyncSocket::State state_;
buzz::AsyncSocket::Error error_;
net::Error net_error_;
// NULL iff state() == STATE_CLOSED.
std::unique_ptr<net::StreamSocket> transport_socket_;
// State for the read loop. |read_start_| <= |read_end_| <=
// |read_buf_->size()|. There's a read in flight (i.e.,
// |read_state_| != IDLE) iff |read_end_| == 0.
AsyncIOState read_state_;
scoped_refptr<net::IOBufferWithSize> read_buf_;
size_t read_start_, read_end_;
// State for the write loop. |write_end_| <= |write_buf_->size()|.
// There's a write in flight (i.e., |write_state_| != IDLE) iff
// |write_end_| > 0.
AsyncIOState write_state_;
scoped_refptr<net::IOBufferWithSize> write_buf_;
size_t write_end_;
// Network traffic annotation for downstream socket write. ChromeAsyncSocket
// is not reused, hence annotation can be added in constructor and used in all
// subsequent writes.
const net::NetworkTrafficAnnotationTag traffic_annotation_;
base::WeakPtrFactory<ChromeAsyncSocket> weak_ptr_factory_;
DISALLOW_COPY_AND_ASSIGN(ChromeAsyncSocket);
};
} // namespace jingle_glue
#endif // JINGLE_GLUE_CHROME_ASYNC_SOCKET_H_
// 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 "jingle/glue/chrome_async_socket.h"
#include <stddef.h>
#include <memory>
#include <string>
#include <utility>
#include "base/containers/circular_deque.h"
#include "base/logging.h"
#include "base/macros.h"
#include "base/message_loop/message_loop.h"
#include "base/message_loop/message_pump_default.h"
#include "base/run_loop.h"
#include "jingle/glue/resolving_client_socket_factory.h"
#include "net/base/address_list.h"
#include "net/base/ip_address.h"
#include "net/base/net_errors.h"
#include "net/cert/mock_cert_verifier.h"
#include "net/http/transport_security_state.h"
#include "net/log/net_log_source.h"
#include "net/socket/socket_test_util.h"
#include "net/socket/ssl_client_socket.h"
#include "net/ssl/ssl_config_service.h"
#include "net/traffic_annotation/network_traffic_annotation_test_helper.h"
#include "net/url_request/url_request_context_getter.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/webrtc/rtc_base/ipaddress.h"
#include "third_party/webrtc/rtc_base/socketaddress.h"
#include "third_party/webrtc/rtc_base/third_party/sigslot/sigslot.h"
namespace jingle_glue {
namespace {
// Data provider that handles reads/writes for ChromeAsyncSocket.
class AsyncSocketDataProvider : public net::SocketDataProvider {
public:
AsyncSocketDataProvider() : has_pending_read_(false) {}
~AsyncSocketDataProvider() override {
EXPECT_TRUE(writes_.empty());
EXPECT_TRUE(reads_.empty());
}
// If there's no read, sets the "has pending read" flag. Otherwise,
// pops the next read.
net::MockRead OnRead() override {
if (reads_.empty()) {
DCHECK(!has_pending_read_);
has_pending_read_ = true;
const net::MockRead pending_read(net::SYNCHRONOUS, net::ERR_IO_PENDING);
return pending_read;
}
net::MockRead mock_read = reads_.front();
reads_.pop_front();
return mock_read;
}
// Simply pops the next write and, if applicable, compares it to
// |data|.
net::MockWriteResult OnWrite(const std::string& data) override {
DCHECK(!writes_.empty());
net::MockWrite mock_write = writes_.front();
writes_.pop_front();
if (mock_write.result != net::OK) {
return net::MockWriteResult(mock_write.mode, mock_write.result);
}
std::string expected_data(mock_write.data, mock_write.data_len);
EXPECT_EQ(expected_data, data);
if (expected_data != data) {
return net::MockWriteResult(net::SYNCHRONOUS, net::ERR_UNEXPECTED);
}
return net::MockWriteResult(mock_write.mode, data.size());
}
// We ignore resets so we can pre-load the socket data provider with
// read/write events.
void Reset() override {}
// If there is a pending read, completes it with the given read.
// Otherwise, queues up the given read.
void AddRead(const net::MockRead& mock_read) {
DCHECK_NE(mock_read.result, net::ERR_IO_PENDING);
if (has_pending_read_) {
socket()->OnReadComplete(mock_read);
has_pending_read_ = false;
return;
}
reads_.push_back(mock_read);
}
// Simply queues up the given write.
void AddWrite(const net::MockWrite& mock_write) {
writes_.push_back(mock_write);
}
bool AllReadDataConsumed() const override {
return reads_.empty();
}
bool AllWriteDataConsumed() const override {
return writes_.empty();
}
private:
base::circular_deque<net::MockRead> reads_;
bool has_pending_read_;
base::circular_deque<net::MockWrite> writes_;
DISALLOW_COPY_AND_ASSIGN(AsyncSocketDataProvider);
};
class MockXmppClientSocketFactory : public ResolvingClientSocketFactory {
public:
MockXmppClientSocketFactory(
std::unique_ptr<net::ClientSocketFactory> mock_client_socket_factory,
const net::AddressList& address_list)
: mock_client_socket_factory_(std::move(mock_client_socket_factory)),
address_list_(address_list),
cert_verifier_(new net::MockCertVerifier),
transport_security_state_(new net::TransportSecurityState) {}
// ResolvingClientSocketFactory implementation.
std::unique_ptr<net::StreamSocket> CreateTransportClientSocket(
const net::HostPortPair& host_and_port) override {
return mock_client_socket_factory_->CreateTransportClientSocket(
address_list_, NULL, NULL, net::NetLogSource());
}
std::unique_ptr<net::SSLClientSocket> CreateSSLClientSocket(
std::unique_ptr<net::ClientSocketHandle> transport_socket,
const net::HostPortPair& host_and_port) override {
net::SSLClientSocketContext context;
context.cert_verifier = cert_verifier_.get();
context.transport_security_state = transport_security_state_.get();
return mock_client_socket_factory_->CreateSSLClientSocket(
std::move(transport_socket), host_and_port, ssl_config_, context);
}
private:
std::unique_ptr<net::ClientSocketFactory> mock_client_socket_factory_;
net::AddressList address_list_;
net::SSLConfig ssl_config_;
std::unique_ptr<net::CertVerifier> cert_verifier_;
std::unique_ptr<net::TransportSecurityState> transport_security_state_;
};
class ChromeAsyncSocketTest
: public testing::Test,
public sigslot::has_slots<> {
protected:
ChromeAsyncSocketTest()
: ssl_socket_data_provider_(net::ASYNC, net::OK),
addr_("localhost", 35) {
// GTest death tests by default execute in a fork()ed but not exec()ed
// process. On macOS, a CoreFoundation-backed MessageLoop will exit with a
// __THE_PROCESS_HAS_FORKED_AND_YOU_CANNOT_USE_THIS_COREFOUNDATION_FUNCTIONALITY___YOU_MUST_EXEC__
// when called. Use the threadsafe mode to avoid this problem.
testing::GTEST_FLAG(death_test_style) = "threadsafe";
}
~ChromeAsyncSocketTest() override {}
void SetUp() override {
std::unique_ptr<net::MockClientSocketFactory> mock_client_socket_factory(
new net::MockClientSocketFactory());
mock_client_socket_factory->AddSocketDataProvider(
&async_socket_data_provider_);
mock_client_socket_factory->AddSSLSocketDataProvider(
&ssl_socket_data_provider_);
// Fake DNS resolution for |addr_| and pass it to the factory.
const net::AddressList address_list = net::AddressList::CreateFromIPAddress(
net::IPAddress::IPv4Localhost(), addr_.port());
std::unique_ptr<MockXmppClientSocketFactory>
mock_xmpp_client_socket_factory(new MockXmppClientSocketFactory(
std::move(mock_client_socket_factory), address_list));
chrome_async_socket_.reset(
new ChromeAsyncSocket(std::move(mock_xmpp_client_socket_factory), 14,
20, TRAFFIC_ANNOTATION_FOR_TESTS)),
chrome_async_socket_->SignalConnected.connect(
this, &ChromeAsyncSocketTest::OnConnect);
chrome_async_socket_->SignalSSLConnected.connect(
this, &ChromeAsyncSocketTest::OnSSLConnect);
chrome_async_socket_->SignalClosed.connect(
this, &ChromeAsyncSocketTest::OnClose);
chrome_async_socket_->SignalRead.connect(
this, &ChromeAsyncSocketTest::OnRead);
chrome_async_socket_->SignalError.connect(
this, &ChromeAsyncSocketTest::OnError);
}
void TearDown() override {
// Run any tasks that we forgot to pump.
base::RunLoop().RunUntilIdle();
ExpectClosed();
ExpectNoSignal();
chrome_async_socket_.reset();
}
enum Signal {
SIGNAL_CONNECT,
SIGNAL_SSL_CONNECT,
SIGNAL_CLOSE,
SIGNAL_READ,
SIGNAL_ERROR,
};
// Helper struct that records the state at the time of a signal.
struct SignalSocketState {
SignalSocketState()
: signal(SIGNAL_ERROR),
state(ChromeAsyncSocket::STATE_CLOSED),
error(ChromeAsyncSocket::ERROR_NONE),
net_error(net::OK) {}
SignalSocketState(
Signal signal,
ChromeAsyncSocket::State state,
ChromeAsyncSocket::Error error,
net::Error net_error)
: signal(signal),
state(state),
error(error),
net_error(net_error) {}
bool IsEqual(const SignalSocketState& other) const {
return
(signal == other.signal) &&
(state == other.state) &&
(error == other.error) &&
(net_error == other.net_error);
}
static SignalSocketState FromAsyncSocket(
Signal signal,
buzz::AsyncSocket* async_socket) {
return SignalSocketState(signal,
async_socket->state(),
async_socket->error(),
static_cast<net::Error>(
async_socket->GetError()));
}
static SignalSocketState NoError(
Signal signal, buzz::AsyncSocket::State state) {
return SignalSocketState(signal, state,
buzz::AsyncSocket::ERROR_NONE,
net::OK);
}
Signal signal;
ChromeAsyncSocket::State state;
ChromeAsyncSocket::Error error;
net::Error net_error;
};
void CaptureSocketState(Signal signal) {
signal_socket_states_.push_back(
SignalSocketState::FromAsyncSocket(
signal, chrome_async_socket_.get()));
}
void OnConnect() {
CaptureSocketState(SIGNAL_CONNECT);
}
void OnSSLConnect() {
CaptureSocketState(SIGNAL_SSL_CONNECT);
}
void OnClose() {
CaptureSocketState(SIGNAL_CLOSE);
}
void OnRead() {
CaptureSocketState(SIGNAL_READ);
}
void OnError() {
ADD_FAILURE();
}
// State expect functions.
void ExpectState(ChromeAsyncSocket::State state,
ChromeAsyncSocket::Error error,
net::Error net_error) {
EXPECT_EQ(state, chrome_async_socket_->state());
EXPECT_EQ(error, chrome_async_socket_->error());
EXPECT_EQ(net_error, chrome_async_socket_->GetError());
}
void ExpectNonErrorState(ChromeAsyncSocket::State state) {
ExpectState(state, ChromeAsyncSocket::ERROR_NONE, net::OK);
}
void ExpectErrorState(ChromeAsyncSocket::State state,
ChromeAsyncSocket::Error error) {
ExpectState(state, error, net::OK);
}
void ExpectClosed() {
ExpectNonErrorState(ChromeAsyncSocket::STATE_CLOSED);
}
// Signal expect functions.
void ExpectNoSignal() {
if (!signal_socket_states_.empty()) {
ADD_FAILURE() << signal_socket_states_.front().signal;
}
}
void ExpectSignalSocketState(
SignalSocketState expected_signal_socket_state) {
if (signal_socket_states_.empty()) {
ADD_FAILURE() << expected_signal_socket_state.signal;
return;
}
EXPECT_TRUE(expected_signal_socket_state.IsEqual(
signal_socket_states_.front()))
<< signal_socket_states_.front().signal;
signal_socket_states_.pop_front();
}
void ExpectReadSignal() {
ExpectSignalSocketState(
SignalSocketState::NoError(
SIGNAL_READ, ChromeAsyncSocket::STATE_OPEN));
}
void ExpectSSLConnectSignal() {
ExpectSignalSocketState(
SignalSocketState::NoError(SIGNAL_SSL_CONNECT,
ChromeAsyncSocket::STATE_TLS_OPEN));
}
void ExpectSSLReadSignal() {
ExpectSignalSocketState(
SignalSocketState::NoError(
SIGNAL_READ, ChromeAsyncSocket::STATE_TLS_OPEN));
}
// Open/close utility functions.
void DoOpenClosed() {
ExpectClosed();
async_socket_data_provider_.set_connect_data(
net::MockConnect(net::SYNCHRONOUS, net::OK));
EXPECT_TRUE(chrome_async_socket_->Connect(addr_));
ExpectNonErrorState(ChromeAsyncSocket::STATE_CONNECTING);
base::RunLoop().RunUntilIdle();
// We may not necessarily be open; may have been other events
// queued up.
ExpectSignalSocketState(
SignalSocketState::NoError(
SIGNAL_CONNECT, ChromeAsyncSocket::STATE_OPEN));
}
void DoCloseOpened(SignalSocketState expected_signal_socket_state) {
// We may be in an error state, so just compare state().
EXPECT_EQ(ChromeAsyncSocket::STATE_OPEN, chrome_async_socket_->state());
EXPECT_TRUE(chrome_async_socket_->Close());
ExpectSignalSocketState(expected_signal_socket_state);
ExpectNonErrorState(ChromeAsyncSocket::STATE_CLOSED);
}
void DoCloseOpenedNoError() {
DoCloseOpened(
SignalSocketState::NoError(
SIGNAL_CLOSE, ChromeAsyncSocket::STATE_CLOSED));
}
void DoSSLOpenClosed() {
const char kDummyData[] = "dummy_data";
async_socket_data_provider_.AddRead(net::MockRead(kDummyData));
DoOpenClosed();
ExpectReadSignal();
EXPECT_EQ(kDummyData, DrainRead(1));
EXPECT_TRUE(chrome_async_socket_->StartTls("fakedomain.com"));
base::RunLoop().RunUntilIdle();
ExpectSSLConnectSignal();
ExpectNoSignal();
ExpectNonErrorState(ChromeAsyncSocket::STATE_TLS_OPEN);
}
void DoSSLCloseOpened(SignalSocketState expected_signal_socket_state) {
// We may be in an error state, so just compare state().
EXPECT_EQ(ChromeAsyncSocket::STATE_TLS_OPEN,
chrome_async_socket_->state());
EXPECT_TRUE(chrome_async_socket_->Close());
ExpectSignalSocketState(expected_signal_socket_state);
ExpectNonErrorState(ChromeAsyncSocket::STATE_CLOSED);
}
void DoSSLCloseOpenedNoError() {
DoSSLCloseOpened(
SignalSocketState::NoError(
SIGNAL_CLOSE, ChromeAsyncSocket::STATE_CLOSED));
}
// Read utility fucntions.
std::string DrainRead(size_t buf_size) {
std::string read;
std::unique_ptr<char[]> buf(new char[buf_size]);
size_t len_read;
while (true) {
bool success =
chrome_async_socket_->Read(buf.get(), buf_size, &len_read);
if (!success) {
ADD_FAILURE();
break;
}
if (len_read == 0U) {
break;
}
read.append(buf.get(), len_read);
}
return read;
}
// ChromeAsyncSocket expects a message loop.
base::MessageLoop message_loop_;
AsyncSocketDataProvider async_socket_data_provider_;
net::SSLSocketDataProvider ssl_socket_data_provider_;
std::unique_ptr<ChromeAsyncSocket> chrome_async_socket_;
base::circular_deque<SignalSocketState> signal_socket_states_;
const rtc::SocketAddress addr_;
private:
DISALLOW_COPY_AND_ASSIGN(ChromeAsyncSocketTest);
};
TEST_F(ChromeAsyncSocketTest, InitialState) {
ExpectClosed();
ExpectNoSignal();
}
TEST_F(ChromeAsyncSocketTest, EmptyClose) {
ExpectClosed();
EXPECT_TRUE(chrome_async_socket_->Close());
ExpectClosed();
}
TEST_F(ChromeAsyncSocketTest, ImmediateConnectAndClose) {
DoOpenClosed();
ExpectNonErrorState(ChromeAsyncSocket::STATE_OPEN);
DoCloseOpenedNoError();
}
// After this, no need to test immediate successful connect and
// Close() so thoroughly.
TEST_F(ChromeAsyncSocketTest, DoubleClose) {
DoOpenClosed();
EXPECT_TRUE(chrome_async_socket_->Close());
ExpectClosed();
ExpectSignalSocketState(
SignalSocketState::NoError(
SIGNAL_CLOSE, ChromeAsyncSocket::STATE_CLOSED));
EXPECT_TRUE(chrome_async_socket_->Close());
ExpectClosed();
}
TEST_F(ChromeAsyncSocketTest, NoHostnameConnect) {
rtc::IPAddress ip_address;
EXPECT_TRUE(rtc::IPFromString("127.0.0.1", &ip_address));
const rtc::SocketAddress no_hostname_addr(ip_address, addr_.port());
EXPECT_FALSE(chrome_async_socket_->Connect(no_hostname_addr));
ExpectErrorState(ChromeAsyncSocket::STATE_CLOSED,
ChromeAsyncSocket::ERROR_DNS);
EXPECT_TRUE(chrome_async_socket_->Close());
ExpectClosed();
}
TEST_F(ChromeAsyncSocketTest, ZeroPortConnect) {
const rtc::SocketAddress zero_port_addr(addr_.hostname(), 0);
EXPECT_FALSE(chrome_async_socket_->Connect(zero_port_addr));
ExpectErrorState(ChromeAsyncSocket::STATE_CLOSED,
ChromeAsyncSocket::ERROR_DNS);
EXPECT_TRUE(chrome_async_socket_->Close());
ExpectClosed();
}
TEST_F(ChromeAsyncSocketTest, DoubleConnect) {
EXPECT_DEBUG_DEATH({
DoOpenClosed();
EXPECT_FALSE(chrome_async_socket_->Connect(addr_));
ExpectErrorState(ChromeAsyncSocket::STATE_OPEN,
ChromeAsyncSocket::ERROR_WRONGSTATE);
DoCloseOpened(
SignalSocketState(SIGNAL_CLOSE,
ChromeAsyncSocket::STATE_CLOSED,
ChromeAsyncSocket::ERROR_WRONGSTATE,
net::OK));
}, "non-closed socket");
}
TEST_F(ChromeAsyncSocketTest, ImmediateConnectCloseBeforeRead) {
DoOpenClosed();
EXPECT_TRUE(chrome_async_socket_->Close());
ExpectClosed();
ExpectSignalSocketState(
SignalSocketState::NoError(
SIGNAL_CLOSE, ChromeAsyncSocket::STATE_CLOSED));
base::RunLoop().RunUntilIdle();
}
TEST_F(ChromeAsyncSocketTest, HangingConnect) {
EXPECT_TRUE(chrome_async_socket_->Connect(addr_));
ExpectNonErrorState(ChromeAsyncSocket::STATE_CONNECTING);
ExpectNoSignal();
EXPECT_TRUE(chrome_async_socket_->Close());
ExpectClosed();
ExpectSignalSocketState(
SignalSocketState::NoError(
SIGNAL_CLOSE, ChromeAsyncSocket::STATE_CLOSED));
}
TEST_F(ChromeAsyncSocketTest, PendingConnect) {
async_socket_data_provider_.set_connect_data(
net::MockConnect(net::ASYNC, net::OK));
EXPECT_TRUE(chrome_async_socket_->Connect(addr_));
ExpectNonErrorState(ChromeAsyncSocket::STATE_CONNECTING);
ExpectNoSignal();
base::RunLoop().RunUntilIdle();
ExpectNonErrorState(ChromeAsyncSocket::STATE_OPEN);
ExpectSignalSocketState(
SignalSocketState::NoError(
SIGNAL_CONNECT, ChromeAsyncSocket::STATE_OPEN));
ExpectNoSignal();
base::RunLoop().RunUntilIdle();
DoCloseOpenedNoError();
}
// After this no need to test successful pending connect so
// thoroughly.
TEST_F(ChromeAsyncSocketTest, PendingConnectCloseBeforeRead) {
async_socket_data_provider_.set_connect_data(
net::MockConnect(net::ASYNC, net::OK));
EXPECT_TRUE(chrome_async_socket_->Connect(addr_));
base::RunLoop().RunUntilIdle();
ExpectSignalSocketState(
SignalSocketState::NoError(
SIGNAL_CONNECT, ChromeAsyncSocket::STATE_OPEN));
DoCloseOpenedNoError();
base::RunLoop().RunUntilIdle();
}
TEST_F(ChromeAsyncSocketTest, PendingConnectError) {
async_socket_data_provider_.set_connect_data(
net::MockConnect(net::ASYNC, net::ERR_TIMED_OUT));
EXPECT_TRUE(chrome_async_socket_->Connect(addr_));
base::RunLoop().RunUntilIdle();
ExpectSignalSocketState(
SignalSocketState(
SIGNAL_CLOSE, ChromeAsyncSocket::STATE_CLOSED,
ChromeAsyncSocket::ERROR_WINSOCK, net::ERR_TIMED_OUT));
}
// After this we can assume Connect() and Close() work as expected.
TEST_F(ChromeAsyncSocketTest, EmptyRead) {
DoOpenClosed();
char buf[4096];
size_t len_read = 10000U;
EXPECT_TRUE(chrome_async_socket_->Read(buf, sizeof(buf), &len_read));
EXPECT_EQ(0U, len_read);
DoCloseOpenedNoError();
}
TEST_F(ChromeAsyncSocketTest, WrongRead) {
EXPECT_DEBUG_DEATH({
async_socket_data_provider_.set_connect_data(
net::MockConnect(net::ASYNC, net::OK));
EXPECT_TRUE(chrome_async_socket_->Connect(addr_));
ExpectNonErrorState(ChromeAsyncSocket::STATE_CONNECTING);
ExpectNoSignal();
char buf[4096];
size_t len_read;
EXPECT_FALSE(chrome_async_socket_->Read(buf, sizeof(buf), &len_read));
ExpectErrorState(ChromeAsyncSocket::STATE_CONNECTING,
ChromeAsyncSocket::ERROR_WRONGSTATE);
EXPECT_TRUE(chrome_async_socket_->Close());
ExpectSignalSocketState(
SignalSocketState(
SIGNAL_CLOSE, ChromeAsyncSocket::STATE_CLOSED,
ChromeAsyncSocket::ERROR_WRONGSTATE, net::OK));
}, "non-open");
}
TEST_F(ChromeAsyncSocketTest, WrongReadClosed) {
char buf[4096];
size_t len_read;
EXPECT_FALSE(chrome_async_socket_->Read(buf, sizeof(buf), &len_read));
ExpectErrorState(ChromeAsyncSocket::STATE_CLOSED,
ChromeAsyncSocket::ERROR_WRONGSTATE);
EXPECT_TRUE(chrome_async_socket_->Close());
}
const char kReadData[] = "mydatatoread";
TEST_F(ChromeAsyncSocketTest, Read) {
async_socket_data_provider_.AddRead(net::MockRead(kReadData));
DoOpenClosed();
ExpectReadSignal();
ExpectNoSignal();
EXPECT_EQ(kReadData, DrainRead(1));
base::RunLoop().RunUntilIdle();
DoCloseOpenedNoError();
}
TEST_F(ChromeAsyncSocketTest, ReadTwice) {
async_socket_data_provider_.AddRead(net::MockRead(kReadData));
DoOpenClosed();
ExpectReadSignal();
ExpectNoSignal();
EXPECT_EQ(kReadData, DrainRead(1));
base::RunLoop().RunUntilIdle();
const char kReadData2[] = "mydatatoread2";
async_socket_data_provider_.AddRead(net::MockRead(kReadData2));
ExpectReadSignal();
ExpectNoSignal();
EXPECT_EQ(kReadData2, DrainRead(1));
DoCloseOpenedNoError();
}
TEST_F(ChromeAsyncSocketTest, ReadError) {
async_socket_data_provider_.AddRead(net::MockRead(kReadData));
DoOpenClosed();
ExpectReadSignal();
ExpectNoSignal();
EXPECT_EQ(kReadData, DrainRead(1));
base::RunLoop().RunUntilIdle();
async_socket_data_provider_.AddRead(
net::MockRead(net::SYNCHRONOUS, net::ERR_TIMED_OUT));
ExpectSignalSocketState(
SignalSocketState(
SIGNAL_CLOSE, ChromeAsyncSocket::STATE_CLOSED,
ChromeAsyncSocket::ERROR_WINSOCK, net::ERR_TIMED_OUT));
}
TEST_F(ChromeAsyncSocketTest, ReadEmpty) {
async_socket_data_provider_.AddRead(net::MockRead(""));
DoOpenClosed();
ExpectSignalSocketState(
SignalSocketState::NoError(
SIGNAL_CLOSE, ChromeAsyncSocket::STATE_CLOSED));
}
TEST_F(ChromeAsyncSocketTest, PendingRead) {
DoOpenClosed();
ExpectNoSignal();
async_socket_data_provider_.AddRead(net::MockRead(kReadData));
ExpectSignalSocketState(
SignalSocketState::NoError(
SIGNAL_READ, ChromeAsyncSocket::STATE_OPEN));
ExpectNoSignal();
EXPECT_EQ(kReadData, DrainRead(1));
base::RunLoop().RunUntilIdle();
DoCloseOpenedNoError();
}
TEST_F(ChromeAsyncSocketTest, PendingEmptyRead) {
DoOpenClosed();
ExpectNoSignal();
async_socket_data_provider_.AddRead(net::MockRead(""));
ExpectSignalSocketState(
SignalSocketState::NoError(
SIGNAL_CLOSE, ChromeAsyncSocket::STATE_CLOSED));
}
TEST_F(ChromeAsyncSocketTest, PendingReadError) {
DoOpenClosed();
ExpectNoSignal();
async_socket_data_provider_.AddRead(
net::MockRead(net::ASYNC, net::ERR_TIMED_OUT));
ExpectSignalSocketState(
SignalSocketState(
SIGNAL_CLOSE, ChromeAsyncSocket::STATE_CLOSED,
ChromeAsyncSocket::ERROR_WINSOCK, net::ERR_TIMED_OUT));
}
// After this we can assume non-SSL Read() works as expected.
TEST_F(ChromeAsyncSocketTest, WrongWrite) {
EXPECT_DEBUG_DEATH({
std::string data("foo");
EXPECT_FALSE(chrome_async_socket_->Write(data.data(), data.size()));
ExpectErrorState(ChromeAsyncSocket::STATE_CLOSED,
ChromeAsyncSocket::ERROR_WRONGSTATE);
EXPECT_TRUE(chrome_async_socket_->Close());
}, "non-open");
}
const char kWriteData[] = "mydatatowrite";
TEST_F(ChromeAsyncSocketTest, SyncWrite) {
async_socket_data_provider_.AddWrite(
net::MockWrite(net::SYNCHRONOUS, kWriteData, 3));
async_socket_data_provider_.AddWrite(
net::MockWrite(net::SYNCHRONOUS, kWriteData + 3, 5));
async_socket_data_provider_.AddWrite(
net::MockWrite(net::SYNCHRONOUS,
kWriteData + 8, arraysize(kWriteData) - 8));
DoOpenClosed();
EXPECT_TRUE(chrome_async_socket_->Write(kWriteData, 3));
base::RunLoop().RunUntilIdle();
EXPECT_TRUE(chrome_async_socket_->Write(kWriteData + 3, 5));
base::RunLoop().RunUntilIdle();
EXPECT_TRUE(chrome_async_socket_->Write(kWriteData + 8,
arraysize(kWriteData) - 8));
base::RunLoop().RunUntilIdle();
ExpectNoSignal();
DoCloseOpenedNoError();
}
TEST_F(ChromeAsyncSocketTest, AsyncWrite) {
DoOpenClosed();
async_socket_data_provider_.AddWrite(
net::MockWrite(net::ASYNC, kWriteData, 3));
async_socket_data_provider_.AddWrite(
net::MockWrite(net::ASYNC, kWriteData + 3, 5));
async_socket_data_provider_.AddWrite(
net::MockWrite(net::ASYNC, kWriteData + 8, arraysize(kWriteData) - 8));
EXPECT_TRUE(chrome_async_socket_->Write(kWriteData, 3));
base::RunLoop().RunUntilIdle();
EXPECT_TRUE(chrome_async_socket_->Write(kWriteData + 3, 5));
base::RunLoop().RunUntilIdle();
EXPECT_TRUE(chrome_async_socket_->Write(kWriteData + 8,
arraysize(kWriteData) - 8));
base::RunLoop().RunUntilIdle();
ExpectNoSignal();
DoCloseOpenedNoError();
}
TEST_F(ChromeAsyncSocketTest, AsyncWriteError) {
DoOpenClosed();
async_socket_data_provider_.AddWrite(
net::MockWrite(net::ASYNC, kWriteData, 3));
async_socket_data_provider_.AddWrite(
net::MockWrite(net::ASYNC, kWriteData + 3, 5));
async_socket_data_provider_.AddWrite(
net::MockWrite(net::ASYNC, net::ERR_TIMED_OUT));
EXPECT_TRUE(chrome_async_socket_->Write(kWriteData, 3));
base::RunLoop().RunUntilIdle();
EXPECT_TRUE(chrome_async_socket_->Write(kWriteData + 3, 5));
base::RunLoop().RunUntilIdle();
EXPECT_TRUE(chrome_async_socket_->Write(kWriteData + 8,
arraysize(kWriteData) - 8));
base::RunLoop().RunUntilIdle();
ExpectSignalSocketState(
SignalSocketState(
SIGNAL_CLOSE, ChromeAsyncSocket::STATE_CLOSED,
ChromeAsyncSocket::ERROR_WINSOCK, net::ERR_TIMED_OUT));
}
TEST_F(ChromeAsyncSocketTest, LargeWrite) {
EXPECT_DEBUG_DEATH({
DoOpenClosed();
std::string large_data(100, 'x');
EXPECT_FALSE(chrome_async_socket_->Write(large_data.data(),
large_data.size()));
ExpectState(ChromeAsyncSocket::STATE_OPEN,
ChromeAsyncSocket::ERROR_WINSOCK,
net::ERR_INSUFFICIENT_RESOURCES);
DoCloseOpened(
SignalSocketState(
SIGNAL_CLOSE, ChromeAsyncSocket::STATE_CLOSED,
ChromeAsyncSocket::ERROR_WINSOCK,
net::ERR_INSUFFICIENT_RESOURCES));
}, "exceed the max write buffer");
}
TEST_F(ChromeAsyncSocketTest, LargeAccumulatedWrite) {
EXPECT_DEBUG_DEATH({
DoOpenClosed();
std::string data(15, 'x');
EXPECT_TRUE(chrome_async_socket_->Write(data.data(), data.size()));
EXPECT_FALSE(chrome_async_socket_->Write(data.data(), data.size()));
ExpectState(ChromeAsyncSocket::STATE_OPEN,
ChromeAsyncSocket::ERROR_WINSOCK,
net::ERR_INSUFFICIENT_RESOURCES);
DoCloseOpened(
SignalSocketState(
SIGNAL_CLOSE, ChromeAsyncSocket::STATE_CLOSED,
ChromeAsyncSocket::ERROR_WINSOCK,
net::ERR_INSUFFICIENT_RESOURCES));
}, "exceed the max write buffer");
}
// After this we can assume non-SSL I/O works as expected.
TEST_F(ChromeAsyncSocketTest, HangingSSLConnect) {
async_socket_data_provider_.AddRead(net::MockRead(kReadData));
DoOpenClosed();
ExpectReadSignal();
EXPECT_TRUE(chrome_async_socket_->StartTls("fakedomain.com"));
ExpectNoSignal();
ExpectNonErrorState(ChromeAsyncSocket::STATE_TLS_CONNECTING);
EXPECT_TRUE(chrome_async_socket_->Close());
ExpectSignalSocketState(
SignalSocketState::NoError(SIGNAL_CLOSE,
ChromeAsyncSocket::STATE_CLOSED));
ExpectNonErrorState(ChromeAsyncSocket::STATE_CLOSED);
}
TEST_F(ChromeAsyncSocketTest, ImmediateSSLConnect) {
async_socket_data_provider_.AddRead(net::MockRead(kReadData));
DoOpenClosed();
ExpectReadSignal();
EXPECT_TRUE(chrome_async_socket_->StartTls("fakedomain.com"));
base::RunLoop().RunUntilIdle();
ExpectSSLConnectSignal();
ExpectNoSignal();
ExpectNonErrorState(ChromeAsyncSocket::STATE_TLS_OPEN);
DoSSLCloseOpenedNoError();
}
TEST_F(ChromeAsyncSocketTest, DoubleSSLConnect) {
EXPECT_DEBUG_DEATH({
async_socket_data_provider_.AddRead(net::MockRead(kReadData));
DoOpenClosed();
ExpectReadSignal();
EXPECT_TRUE(chrome_async_socket_->StartTls("fakedomain.com"));
base::RunLoop().RunUntilIdle();
ExpectSSLConnectSignal();
ExpectNoSignal();
ExpectNonErrorState(ChromeAsyncSocket::STATE_TLS_OPEN);
EXPECT_FALSE(chrome_async_socket_->StartTls("fakedomain.com"));
DoSSLCloseOpened(
SignalSocketState(SIGNAL_CLOSE,
ChromeAsyncSocket::STATE_CLOSED,
ChromeAsyncSocket::ERROR_WRONGSTATE,
net::OK));
}, "wrong state");
}
TEST_F(ChromeAsyncSocketTest, FailedSSLConnect) {
ssl_socket_data_provider_.connect =
net::MockConnect(net::ASYNC, net::ERR_CERT_COMMON_NAME_INVALID),
async_socket_data_provider_.AddRead(net::MockRead(kReadData));
DoOpenClosed();
ExpectReadSignal();
EXPECT_TRUE(chrome_async_socket_->StartTls("fakedomain.com"));
base::RunLoop().RunUntilIdle();
ExpectSignalSocketState(
SignalSocketState(
SIGNAL_CLOSE, ChromeAsyncSocket::STATE_CLOSED,
ChromeAsyncSocket::ERROR_WINSOCK,
net::ERR_CERT_COMMON_NAME_INVALID));
EXPECT_TRUE(chrome_async_socket_->Close());
ExpectClosed();
}
TEST_F(ChromeAsyncSocketTest, ReadDuringSSLConnecting) {
async_socket_data_provider_.AddRead(net::MockRead(kReadData));
DoOpenClosed();
ExpectReadSignal();
EXPECT_EQ(kReadData, DrainRead(1));
EXPECT_TRUE(chrome_async_socket_->StartTls("fakedomain.com"));
ExpectNoSignal();
// Shouldn't do anything.
async_socket_data_provider_.AddRead(net::MockRead(kReadData));
char buf[4096];
size_t len_read = 10000U;
EXPECT_TRUE(chrome_async_socket_->Read(buf, sizeof(buf), &len_read));
EXPECT_EQ(0U, len_read);
base::RunLoop().RunUntilIdle();
ExpectSSLConnectSignal();
ExpectSSLReadSignal();
ExpectNoSignal();
ExpectNonErrorState(ChromeAsyncSocket::STATE_TLS_OPEN);
len_read = 10000U;
EXPECT_TRUE(chrome_async_socket_->Read(buf, sizeof(buf), &len_read));
EXPECT_EQ(kReadData, std::string(buf, len_read));
DoSSLCloseOpenedNoError();
}
TEST_F(ChromeAsyncSocketTest, WriteDuringSSLConnecting) {
async_socket_data_provider_.AddRead(net::MockRead(kReadData));
DoOpenClosed();
ExpectReadSignal();
EXPECT_TRUE(chrome_async_socket_->StartTls("fakedomain.com"));
ExpectNoSignal();
ExpectNonErrorState(ChromeAsyncSocket::STATE_TLS_CONNECTING);
async_socket_data_provider_.AddWrite(
net::MockWrite(net::ASYNC, kWriteData, 3));
// Shouldn't do anything.
EXPECT_TRUE(chrome_async_socket_->Write(kWriteData, 3));
// TODO(akalin): Figure out how to test that the write happens
// *after* the SSL connect.
base::RunLoop().RunUntilIdle();
ExpectSSLConnectSignal();
ExpectNoSignal();
base::RunLoop().RunUntilIdle();
DoSSLCloseOpenedNoError();
}
TEST_F(ChromeAsyncSocketTest, SSLConnectDuringPendingRead) {
EXPECT_DEBUG_DEATH({
DoOpenClosed();
EXPECT_FALSE(chrome_async_socket_->StartTls("fakedomain.com"));
DoCloseOpened(
SignalSocketState(SIGNAL_CLOSE,
ChromeAsyncSocket::STATE_CLOSED,
ChromeAsyncSocket::ERROR_WRONGSTATE,
net::OK));
}, "wrong state");
}
TEST_F(ChromeAsyncSocketTest, SSLConnectDuringPostedWrite) {
EXPECT_DEBUG_DEATH({
DoOpenClosed();
async_socket_data_provider_.AddWrite(
net::MockWrite(net::ASYNC, kWriteData, 3));
EXPECT_TRUE(chrome_async_socket_->Write(kWriteData, 3));
EXPECT_FALSE(chrome_async_socket_->StartTls("fakedomain.com"));
base::RunLoop().RunUntilIdle();
DoCloseOpened(
SignalSocketState(SIGNAL_CLOSE,
ChromeAsyncSocket::STATE_CLOSED,
ChromeAsyncSocket::ERROR_WRONGSTATE,
net::OK));
}, "wrong state");
}
// After this we can assume SSL connect works as expected.
TEST_F(ChromeAsyncSocketTest, SSLRead) {
DoSSLOpenClosed();
async_socket_data_provider_.AddRead(net::MockRead(kReadData));
base::RunLoop().RunUntilIdle();
ExpectSSLReadSignal();
ExpectNoSignal();
EXPECT_EQ(kReadData, DrainRead(1));
base::RunLoop().RunUntilIdle();
DoSSLCloseOpenedNoError();
}
TEST_F(ChromeAsyncSocketTest, SSLSyncWrite) {
async_socket_data_provider_.AddWrite(
net::MockWrite(net::SYNCHRONOUS, kWriteData, 3));
async_socket_data_provider_.AddWrite(
net::MockWrite(net::SYNCHRONOUS, kWriteData + 3, 5));
async_socket_data_provider_.AddWrite(
net::MockWrite(net::SYNCHRONOUS,
kWriteData + 8, arraysize(kWriteData) - 8));
DoSSLOpenClosed();
EXPECT_TRUE(chrome_async_socket_->Write(kWriteData, 3));
base::RunLoop().RunUntilIdle();
EXPECT_TRUE(chrome_async_socket_->Write(kWriteData + 3, 5));
base::RunLoop().RunUntilIdle();
EXPECT_TRUE(chrome_async_socket_->Write(kWriteData + 8,
arraysize(kWriteData) - 8));
base::RunLoop().RunUntilIdle();
ExpectNoSignal();
DoSSLCloseOpenedNoError();
}
TEST_F(ChromeAsyncSocketTest, SSLAsyncWrite) {
DoSSLOpenClosed();
async_socket_data_provider_.AddWrite(
net::MockWrite(net::ASYNC, kWriteData, 3));
async_socket_data_provider_.AddWrite(
net::MockWrite(net::ASYNC, kWriteData + 3, 5));
async_socket_data_provider_.AddWrite(
net::MockWrite(net::ASYNC, kWriteData + 8, arraysize(kWriteData) - 8));
EXPECT_TRUE(chrome_async_socket_->Write(kWriteData, 3));
base::RunLoop().RunUntilIdle();
EXPECT_TRUE(chrome_async_socket_->Write(kWriteData + 3, 5));
base::RunLoop().RunUntilIdle();
EXPECT_TRUE(chrome_async_socket_->Write(kWriteData + 8,
arraysize(kWriteData) - 8));
base::RunLoop().RunUntilIdle();
ExpectNoSignal();
DoSSLCloseOpenedNoError();
}
} // namespace
} // namespace jingle_glue
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment