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/1237214
Reviewed-by: Nicolas Zea <zea@chromium.org>
Commit-Queue: Maks Orlovich <morlovich@chromium.org>
Cr-Commit-Position: refs/heads/master@{#604113}diff --git a/jingle/glue/network_service_async_socket.cc b/jingle/glue/network_service_async_socket.cc
new file mode 100644
index 0000000..ebe8aec8
--- /dev/null
+++ b/jingle/glue/network_service_async_socket.cc
@@ -0,0 +1,441 @@
+// 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
diff --git a/jingle/glue/network_service_async_socket.h b/jingle/glue/network_service_async_socket.h
new file mode 100644
index 0000000..1612357
--- /dev/null
+++ b/jingle/glue/network_service_async_socket.h
@@ -0,0 +1,220 @@
+// 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_
diff --git a/jingle/glue/network_service_async_socket_unittest.cc b/jingle/glue/network_service_async_socket_unittest.cc
new file mode 100644
index 0000000..65b9d9a7
--- /dev/null
+++ b/jingle/glue/network_service_async_socket_unittest.cc
@@ -0,0 +1,1100 @@
+// 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