|  | // 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/network_service_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 "net/base/host_port_pair.h" | 
|  | #include "net/base/io_buffer.h" | 
|  |  | 
|  | namespace jingle_glue { | 
|  |  | 
|  | NetworkServiceAsyncSocket::NetworkServiceAsyncSocket( | 
|  | GetProxyResolvingSocketFactoryCallback get_socket_factory_callback, | 
|  | bool use_fake_tls_handshake, | 
|  | size_t read_buf_size, | 
|  | size_t write_buf_size, | 
|  | const net::NetworkTrafficAnnotationTag& traffic_annotation) | 
|  | : get_socket_factory_callback_(get_socket_factory_callback), | 
|  | socket_observer_binding_(this), | 
|  | use_fake_tls_handshake_(use_fake_tls_handshake), | 
|  | state_(STATE_CLOSED), | 
|  | error_(ERROR_NONE), | 
|  | net_error_(net::OK), | 
|  | read_state_(IDLE), | 
|  | read_buf_(read_buf_size), | 
|  | read_start_(0U), | 
|  | read_end_(0U), | 
|  | saw_error_on_read_pipe_(false), | 
|  | saw_error_on_write_pipe_(false), | 
|  | saw_read_error_on_socket_observer_pipe_(net::ERR_IO_PENDING), | 
|  | saw_write_error_on_socket_observer_pipe_(net::ERR_IO_PENDING), | 
|  | write_state_(IDLE), | 
|  | write_buf_(write_buf_size), | 
|  | write_end_(0U), | 
|  | traffic_annotation_(traffic_annotation) { | 
|  | DCHECK(get_socket_factory_callback_); | 
|  | DCHECK_GT(read_buf_size, 0U); | 
|  | DCHECK_GT(write_buf_size, 0U); | 
|  | } | 
|  |  | 
|  | NetworkServiceAsyncSocket::~NetworkServiceAsyncSocket() {} | 
|  |  | 
|  | NetworkServiceAsyncSocket::State NetworkServiceAsyncSocket::state() { | 
|  | return state_; | 
|  | } | 
|  |  | 
|  | NetworkServiceAsyncSocket::Error NetworkServiceAsyncSocket::error() { | 
|  | return error_; | 
|  | } | 
|  |  | 
|  | int NetworkServiceAsyncSocket::GetError() { | 
|  | return net_error_; | 
|  | } | 
|  |  | 
|  | bool NetworkServiceAsyncSocket::IsOpen() const { | 
|  | return (state_ == STATE_OPEN) || (state_ == STATE_TLS_OPEN); | 
|  | } | 
|  |  | 
|  | void NetworkServiceAsyncSocket::DoNonNetError(Error error) { | 
|  | DCHECK_NE(error, ERROR_NONE); | 
|  | DCHECK_NE(error, ERROR_WINSOCK); | 
|  | error_ = error; | 
|  | net_error_ = net::OK; | 
|  | } | 
|  |  | 
|  | void NetworkServiceAsyncSocket::DoNetError(net::Error net_error) { | 
|  | error_ = ERROR_WINSOCK; | 
|  | net_error_ = net_error; | 
|  | } | 
|  |  | 
|  | void NetworkServiceAsyncSocket::DoNetErrorFromStatus(int status) { | 
|  | DCHECK_LT(status, net::OK); | 
|  | DoNetError(static_cast<net::Error>(status)); | 
|  | } | 
|  |  | 
|  | void NetworkServiceAsyncSocket::ProcessSocketObserverError() { | 
|  | if (saw_read_error_on_socket_observer_pipe_ == net::ERR_IO_PENDING && | 
|  | saw_write_error_on_socket_observer_pipe_ == net::ERR_IO_PENDING) { | 
|  | // Haven't seen an error, and the socket observer pipe got broken. | 
|  | // This shouldn't normally happen, but as the trust level of network service | 
|  | // is lower than of browser process, it needs to be handled. | 
|  | DoNetError(net::ERR_FAILED); | 
|  | DoClose(); | 
|  | } | 
|  | // In case an error came in on the socket observer pipe, it will | 
|  | // get handled at time of read data pipe's closing. | 
|  | } | 
|  |  | 
|  | void NetworkServiceAsyncSocket::OnReadError(int32_t net_error) { | 
|  | // Ignore redundant error messages. | 
|  | if (saw_read_error_on_socket_observer_pipe_ != net::ERR_IO_PENDING) | 
|  | return; | 
|  |  | 
|  | // Sanitize any invalid error code, | 
|  | if (net_error > 0 || net_error == net::ERR_IO_PENDING) | 
|  | net_error = net::ERR_FAILED; | 
|  | if (saw_error_on_read_pipe_) { | 
|  | // Read pipe closure got delivered first, and so with the socket observer | 
|  | // notification here, both pipes got fully handled. | 
|  | ReportReadError(net_error); | 
|  | } else { | 
|  | // Read error notification on socket observer pipe got delivered first; | 
|  | // save error code for read pipe closure to deliver. | 
|  | saw_read_error_on_socket_observer_pipe_ = net_error; | 
|  | } | 
|  | } | 
|  |  | 
|  | void NetworkServiceAsyncSocket::OnWriteError(int32_t net_error) { | 
|  | // Ignore redundant error messages. | 
|  | if (saw_write_error_on_socket_observer_pipe_ != net::ERR_IO_PENDING) | 
|  | return; | 
|  |  | 
|  | // Sanitize any invalid error code, | 
|  | if (net_error >= 0 || net_error == net::ERR_IO_PENDING) | 
|  | net_error = net::ERR_FAILED; | 
|  | if (saw_error_on_write_pipe_) { | 
|  | // Write pipe closure got delivered first, and so with the socket observer | 
|  | // notification here, both pipes got fully handled. | 
|  | DoNetErrorFromStatus(net_error); | 
|  | DoClose(); | 
|  | } else { | 
|  | // Write error notification on socket observer pipe got delivered first; | 
|  | // save error code for write pipe closure to deliver. | 
|  | saw_write_error_on_socket_observer_pipe_ = net_error; | 
|  | } | 
|  | } | 
|  |  | 
|  | // STATE_CLOSED -> STATE_CONNECTING | 
|  |  | 
|  | bool NetworkServiceAsyncSocket::Connect(const net::HostPortPair& address) { | 
|  | if (state_ != STATE_CLOSED) { | 
|  | LOG(DFATAL) << "Connect() called on non-closed socket"; | 
|  | DoNonNetError(ERROR_WRONGSTATE); | 
|  | return false; | 
|  | } | 
|  | if (address.host().empty() || address.port() == 0) { | 
|  | DoNonNetError(ERROR_DNS); | 
|  | return false; | 
|  | } | 
|  |  | 
|  | DCHECK_EQ(state_, jingle_xmpp::AsyncSocket::STATE_CLOSED); | 
|  | DCHECK_EQ(read_state_, IDLE); | 
|  | DCHECK_EQ(write_state_, IDLE); | 
|  |  | 
|  | state_ = STATE_CONNECTING; | 
|  |  | 
|  | get_socket_factory_callback_.Run(mojo::MakeRequest(&socket_factory_)); | 
|  |  | 
|  | network::mojom::SocketObserverPtr socket_observer; | 
|  | network::mojom::SocketObserverRequest socket_observer_request = | 
|  | mojo::MakeRequest(&socket_observer); | 
|  | network::mojom::ProxyResolvingSocketOptionsPtr options = | 
|  | network::mojom::ProxyResolvingSocketOptions::New(); | 
|  | options->use_tls = false; | 
|  | options->fake_tls_handshake = use_fake_tls_handshake_; | 
|  | socket_factory_->CreateProxyResolvingSocket( | 
|  | GURL("https://" + address.ToString()), std::move(options), | 
|  | net::MutableNetworkTrafficAnnotationTag(traffic_annotation_), | 
|  | mojo::MakeRequest(&socket_), std::move(socket_observer), | 
|  | base::BindOnce(&NetworkServiceAsyncSocket::ProcessConnectDone, | 
|  | base::Unretained(this), | 
|  | std::move(socket_observer_request))); | 
|  | return true; | 
|  | } | 
|  |  | 
|  | // STATE_CONNECTING -> STATE_OPEN | 
|  | // read_state_ == IDLE -> read_state_ == WAITING (via WatchForReadReady()) | 
|  |  | 
|  | void NetworkServiceAsyncSocket::ProcessConnectDone( | 
|  | network::mojom::SocketObserverRequest socket_observer_request, | 
|  | int status, | 
|  | const base::Optional<net::IPEndPoint>& local_addr, | 
|  | const base::Optional<net::IPEndPoint>& peer_addr, | 
|  | mojo::ScopedDataPipeConsumerHandle receive_stream, | 
|  | mojo::ScopedDataPipeProducerHandle send_stream) { | 
|  | 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; | 
|  | ConnectPipes(std::move(receive_stream), std::move(send_stream)); | 
|  | BindSocketObserver(std::move(socket_observer_request)); | 
|  |  | 
|  | WatchForReadReady(); | 
|  | // Write buffer should be empty. | 
|  | DCHECK_EQ(write_end_, 0U); | 
|  | SignalConnected(); | 
|  | } | 
|  |  | 
|  | // read_state_ == IDLE -> read_state_ == WAITING | 
|  |  | 
|  | void NetworkServiceAsyncSocket::WatchForReadReady() { | 
|  | // Note that this never transitions to ProcessReadReady immediately; which | 
|  | // avoids potentially error-prone synchronous notifications from within | 
|  | // methods like Connect() and Read(). | 
|  | DCHECK(IsOpen()); | 
|  | DCHECK_EQ(read_state_, IDLE); | 
|  | 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). | 
|  | read_state_ = WAITING; | 
|  | read_watcher_->ArmOrNotify(); | 
|  | } | 
|  |  | 
|  | // read_state_ == WAITING -> read_state_ == IDLE | 
|  |  | 
|  | void NetworkServiceAsyncSocket::ProcessReadReady( | 
|  | MojoResult result, | 
|  | const mojo::HandleSignalsState& state) { | 
|  | DCHECK(IsOpen()); | 
|  | DCHECK_EQ(read_state_, WAITING); | 
|  | DCHECK_EQ(read_start_, 0U); | 
|  | DCHECK_EQ(read_end_, 0U); | 
|  | read_state_ = IDLE; | 
|  |  | 
|  | uint32_t num_bytes = read_buf_.size(); | 
|  | if (result == MOJO_RESULT_OK && !state.peer_closed()) { | 
|  | result = read_pipe_->ReadData(read_buf_.data(), &num_bytes, | 
|  | MOJO_READ_DATA_FLAG_NONE); | 
|  | if (result == MOJO_RESULT_SHOULD_WAIT) { | 
|  | WatchForReadReady(); | 
|  | return; | 
|  | } | 
|  | } | 
|  |  | 
|  | if (result != MOJO_RESULT_OK || !num_bytes || state.peer_closed()) { | 
|  | // The pipe is closed on any error, or EOF. | 
|  | if (saw_read_error_on_socket_observer_pipe_ != net::ERR_IO_PENDING) { | 
|  | // Already saw socket observer's notification, report result. | 
|  | ReportReadError(saw_read_error_on_socket_observer_pipe_); | 
|  | } else { | 
|  | // This got delivered before the error code from socket observer, let it | 
|  | // know it's responsible for reporting the error/EOF. | 
|  | saw_error_on_read_pipe_ = true; | 
|  | } | 
|  | return; | 
|  | } | 
|  |  | 
|  | read_end_ = num_bytes; | 
|  | SignalRead(); | 
|  | } | 
|  |  | 
|  | void NetworkServiceAsyncSocket::ReportReadError(int net_error) { | 
|  | if (net_error == 0) { | 
|  | // Other side closed the connection. | 
|  | error_ = ERROR_NONE; | 
|  | net_error_ = net::OK; | 
|  | } else { | 
|  | DoNetErrorFromStatus(net_error); | 
|  | } | 
|  | DoClose(); | 
|  | } | 
|  |  | 
|  | // (maybe) read_state_ == IDLE -> read_state_ == WAITING (via | 
|  | // WatchForReadReady()) | 
|  |  | 
|  | bool NetworkServiceAsyncSocket::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; | 
|  | WatchForReadReady(); | 
|  | } | 
|  | return true; | 
|  | } | 
|  |  | 
|  | // (maybe) write_state_ == IDLE -> write_state_ == WAITING (via | 
|  | // WatchForWriteReady()) | 
|  |  | 
|  | bool NetworkServiceAsyncSocket::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)) { | 
|  | WatchForWriteReady(); | 
|  | } | 
|  | return true; | 
|  | } | 
|  |  | 
|  | // write_state_ == IDLE -> write_state_ == WAITING | 
|  |  | 
|  | void NetworkServiceAsyncSocket::WatchForWriteReady() { | 
|  | // Note that this never transitions to ProcessWriteReady immediately; which | 
|  | // avoids potentially error-prone synchronous notifications from within | 
|  | // methods like Write(). | 
|  | DCHECK(IsOpen()); | 
|  | DCHECK_EQ(write_state_, IDLE); | 
|  | 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. | 
|  | write_state_ = WAITING; | 
|  | write_watcher_->ArmOrNotify(); | 
|  | } | 
|  |  | 
|  | // write_state_ == WAITING -> write_state_ == IDLE or WAITING (the | 
|  | // latter via WatchForWriteReady()) | 
|  |  | 
|  | void NetworkServiceAsyncSocket::ProcessWriteReady( | 
|  | MojoResult result, | 
|  | const mojo::HandleSignalsState& state) { | 
|  | DCHECK(IsOpen()); | 
|  | DCHECK_EQ(write_state_, WAITING); | 
|  | DCHECK_GT(write_end_, 0U); | 
|  | write_state_ = IDLE; | 
|  |  | 
|  | // Write errors are handled in ProcessWriteClosed. | 
|  | uint32_t written = write_end_; | 
|  | if (result == MOJO_RESULT_OK) { | 
|  | result = write_pipe_->WriteData(write_buf_.data(), &written, | 
|  | MOJO_WRITE_DATA_FLAG_NONE); | 
|  | } | 
|  |  | 
|  | if (result == MOJO_RESULT_SHOULD_WAIT) { | 
|  | WatchForWriteReady(); | 
|  | return; | 
|  | } | 
|  |  | 
|  | if (result != MOJO_RESULT_OK) { | 
|  | DCHECK(socket_observer_binding_.is_bound()); | 
|  | // Unlike with reads, as the pipe close notifier for writes is independent | 
|  | // and always armed, it can take care of all the errors. | 
|  | return; | 
|  | } | 
|  |  | 
|  | 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) { | 
|  | WatchForWriteReady(); | 
|  | } | 
|  | } | 
|  |  | 
|  | void NetworkServiceAsyncSocket::ProcessWriteClosed( | 
|  | MojoResult result, | 
|  | const mojo::HandleSignalsState& state) { | 
|  | DCHECK(state.peer_closed()); | 
|  |  | 
|  | // The pipe is closed on any error, or EOF. | 
|  | if (saw_write_error_on_socket_observer_pipe_ != net::ERR_IO_PENDING) { | 
|  | // Already saw socket observer's notification, report result. | 
|  | DoNetErrorFromStatus(saw_write_error_on_socket_observer_pipe_); | 
|  | DoClose(); | 
|  | } else { | 
|  | // This got delivered before the error code from socket observer, let it | 
|  | // know it's responsible for reporting the error/EOF. | 
|  | saw_error_on_write_pipe_ = true; | 
|  | } | 
|  | } | 
|  |  | 
|  | // * -> STATE_CLOSED | 
|  |  | 
|  | bool NetworkServiceAsyncSocket::Close() { | 
|  | DoClose(); | 
|  | return true; | 
|  | } | 
|  |  | 
|  | // (not STATE_CLOSED) -> STATE_CLOSED | 
|  |  | 
|  | void NetworkServiceAsyncSocket::DoClose() { | 
|  | // As this closes all the mojo pipes and destroys all the watchers it also | 
|  | // cancels all pending async operations. | 
|  | read_state_ = IDLE; | 
|  | read_start_ = 0U; | 
|  | read_end_ = 0U; | 
|  | read_pipe_.reset(); | 
|  | read_watcher_.reset(); | 
|  | saw_error_on_read_pipe_ = false; | 
|  | saw_error_on_write_pipe_ = false; | 
|  | saw_read_error_on_socket_observer_pipe_ = net::ERR_IO_PENDING; | 
|  | saw_write_error_on_socket_observer_pipe_ = net::ERR_IO_PENDING; | 
|  | write_state_ = IDLE; | 
|  | write_end_ = 0U; | 
|  | write_pipe_.reset(); | 
|  | write_watcher_.reset(); | 
|  | write_close_watcher_.reset(); | 
|  |  | 
|  | socket_ = nullptr; | 
|  | tls_socket_ = nullptr; | 
|  | socket_observer_binding_.Close(); | 
|  | socket_factory_ = nullptr; | 
|  |  | 
|  | 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 NetworkServiceAsyncSocket::StartTls(const std::string& domain_name) { | 
|  | DCHECK_EQ(IDLE, write_state_); | 
|  | if (state_ != STATE_OPEN) { | 
|  | 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); | 
|  |  | 
|  | read_watcher_ = nullptr; | 
|  | read_pipe_.reset(); | 
|  | write_watcher_ = nullptr; | 
|  | write_close_watcher_ = nullptr; | 
|  | write_pipe_.reset(); | 
|  | socket_observer_binding_.Close(); | 
|  | network::mojom::SocketObserverPtr socket_observer; | 
|  | network::mojom::SocketObserverRequest socket_observer_request = | 
|  | mojo::MakeRequest(&socket_observer); | 
|  |  | 
|  | socket_->UpgradeToTLS( | 
|  | net::HostPortPair(domain_name, 443), | 
|  | net::MutableNetworkTrafficAnnotationTag(traffic_annotation_), | 
|  | mojo::MakeRequest(&tls_socket_), std::move(socket_observer), | 
|  | base::BindOnce(&NetworkServiceAsyncSocket::ProcessSSLConnectDone, | 
|  | base::Unretained(this), | 
|  | std::move(socket_observer_request))); | 
|  | return true; | 
|  | } | 
|  |  | 
|  | // STATE_TLS_CONNECTING -> STATE_TLS_OPEN | 
|  | // read_state_ == IDLE -> read_state_ == WAITING (via WatchForReadReady()) | 
|  | // (maybe) write_state_ == IDLE -> write_state_ == WAITING (via | 
|  | // WatchForWriteReady()) | 
|  |  | 
|  | void NetworkServiceAsyncSocket::ProcessSSLConnectDone( | 
|  | network::mojom::SocketObserverRequest socket_observer_request, | 
|  | int status, | 
|  | mojo::ScopedDataPipeConsumerHandle receive_stream, | 
|  | mojo::ScopedDataPipeProducerHandle send_stream) { | 
|  | 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; | 
|  | ConnectPipes(std::move(receive_stream), std::move(send_stream)); | 
|  | BindSocketObserver(std::move(socket_observer_request)); | 
|  |  | 
|  | WatchForReadReady(); | 
|  | if (write_end_ > 0U) { | 
|  | WatchForWriteReady(); | 
|  | } | 
|  | SignalSSLConnected(); | 
|  | } | 
|  |  | 
|  | void NetworkServiceAsyncSocket::ConnectPipes( | 
|  | mojo::ScopedDataPipeConsumerHandle receive_stream, | 
|  | mojo::ScopedDataPipeProducerHandle send_stream) { | 
|  | read_pipe_ = std::move(receive_stream); | 
|  | read_watcher_ = std::make_unique<mojo::SimpleWatcher>( | 
|  | FROM_HERE, mojo::SimpleWatcher::ArmingPolicy::MANUAL); | 
|  | read_watcher_->Watch( | 
|  | read_pipe_.get(), | 
|  | MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED, | 
|  | MOJO_TRIGGER_CONDITION_SIGNALS_SATISFIED, | 
|  | base::BindRepeating(&NetworkServiceAsyncSocket::ProcessReadReady, | 
|  | base::Unretained(this))); | 
|  |  | 
|  | write_pipe_ = std::move(send_stream); | 
|  | write_watcher_ = std::make_unique<mojo::SimpleWatcher>( | 
|  | FROM_HERE, mojo::SimpleWatcher::ArmingPolicy::MANUAL); | 
|  | write_watcher_->Watch( | 
|  | write_pipe_.get(), MOJO_HANDLE_SIGNAL_WRITABLE, | 
|  | MOJO_TRIGGER_CONDITION_SIGNALS_SATISFIED, | 
|  | base::BindRepeating(&NetworkServiceAsyncSocket::ProcessWriteReady, | 
|  | base::Unretained(this))); | 
|  |  | 
|  | // Write pipe close gets a separate  watcher to look for signs of trouble | 
|  | // even when no write is pending. (Read doesn't need one since reads are | 
|  | // always watched for). | 
|  | write_close_watcher_ = std::make_unique<mojo::SimpleWatcher>( | 
|  | FROM_HERE, mojo::SimpleWatcher::ArmingPolicy::MANUAL); | 
|  | write_close_watcher_->Watch( | 
|  | write_pipe_.get(), MOJO_HANDLE_SIGNAL_PEER_CLOSED, | 
|  | MOJO_TRIGGER_CONDITION_SIGNALS_SATISFIED, | 
|  | base::BindRepeating(&NetworkServiceAsyncSocket::ProcessWriteClosed, | 
|  | base::Unretained(this))); | 
|  | write_close_watcher_->ArmOrNotify(); | 
|  | } | 
|  |  | 
|  | void NetworkServiceAsyncSocket::BindSocketObserver( | 
|  | network::mojom::SocketObserverRequest socket_observer_request) { | 
|  | socket_observer_binding_.Bind(std::move(socket_observer_request)); | 
|  | socket_observer_binding_.set_connection_error_handler( | 
|  | base::BindOnce(&NetworkServiceAsyncSocket::ProcessSocketObserverError, | 
|  | base::Unretained(this))); | 
|  | } | 
|  |  | 
|  | }  // namespace jingle_glue |