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