| // Copyright 2018 Google Inc. All Rights Reserved. |
| // |
| // Licensed under the Apache License, Version 2.0 (the "License"); |
| // you may not use this file except in compliance with the License. |
| // You may obtain a copy of the License at |
| // |
| // http://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, software |
| // distributed under the License is distributed on an "AS IS" BASIS, |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| // See the License for the specific language governing permissions and |
| // limitations under the License. |
| |
| #include "net/socket/tcp_socket_starboard.h" |
| |
| #include "base/callback_helpers.h" |
| #include "net/base/net_errors.h" |
| #include "net/base/network_activity_monitor.h" |
| #include "net/socket/socket_net_log_params.h" |
| #include "net/socket/socket_options.h" |
| |
| namespace net { |
| |
| TCPSocketStarboard::TCPSocketStarboard( |
| std::unique_ptr<SocketPerformanceWatcher> socket_performance_watcher, |
| NetLog* net_log, const NetLogSource& source) |
| : socket_performance_watcher_(std::move(socket_performance_watcher)), |
| socket_(kSbSocketInvalid), |
| family_(ADDRESS_FAMILY_UNSPECIFIED), |
| logging_multiple_connect_attempts_(false), |
| net_log_(NetLogWithSource::Make(net_log, NetLogSourceType::SOCKET)), |
| listening_(false), waiting_connect_(false) { |
| net_log_.BeginEvent( |
| NetLogEventType::SOCKET_ALIVE, source.ToEventParametersCallback()); |
| } |
| |
| TCPSocketStarboard::~TCPSocketStarboard() { |
| net_log_.EndEvent(NetLogEventType::SOCKET_ALIVE); |
| Close(); |
| } |
| |
| int TCPSocketStarboard::Open(AddressFamily family) { |
| DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); |
| DCHECK(!SbSocketIsValid(socket_)); |
| socket_ = SbSocketCreate(ConvertAddressFamily(family), kSbSocketProtocolTcp); |
| if (!SbSocketIsValid(socket_)) { |
| return MapLastSystemError(); |
| } |
| |
| family_ = family; |
| return OK; |
| } |
| |
| int TCPSocketStarboard::AdoptConnectedSocket(SocketDescriptor socket, |
| const IPEndPoint& peer_address) { |
| DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); |
| DCHECK(!SbSocketIsValid(socket_)); |
| |
| SbSocketAddress storage; |
| if (!peer_address.ToSbSocketAddress(&storage) && |
| !(peer_address == IPEndPoint())) { |
| return ERR_ADDRESS_INVALID; |
| } |
| |
| socket_ = socket; |
| peer_address_.reset(new IPEndPoint(peer_address)); |
| return OK; |
| } |
| |
| int TCPSocketStarboard::AdoptUnconnectedSocket(SocketDescriptor socket) { |
| DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); |
| DCHECK(!SbSocketIsValid(socket_)); |
| |
| socket_ = socket; |
| return OK; |
| } |
| |
| int TCPSocketStarboard::Bind(const IPEndPoint& address) { |
| DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); |
| DCHECK(SbSocketIsValid(socket_)); |
| // Must have already called Open() on this socket with the same address |
| // family as is being passed in here. |
| DCHECK_EQ(family_, address.GetFamily()); |
| |
| SbSocketAddress storage; |
| if (!address.ToSbSocketAddress(&storage)) { |
| return ERR_ADDRESS_INVALID; |
| } |
| |
| SbSocketError error = SbSocketBind(socket_, &storage); |
| if (error != kSbSocketOk) { |
| DLOG(ERROR) << "SbSocketBind() returned an error"; |
| return MapLastSocketError(socket_); |
| } |
| |
| local_address_.reset(new IPEndPoint(address)); |
| |
| return OK; |
| } |
| |
| int TCPSocketStarboard::Listen(int backlog) { |
| DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); |
| DCHECK_GT(backlog, 0); |
| DCHECK(SbSocketIsValid(socket_)); |
| DCHECK(local_address_) << "Must have previously called Bind()."; |
| |
| SbSocketError error = SbSocketListen(socket_); |
| if (error != kSbSocketOk) { |
| DLOG(ERROR) << "SbSocketListen() returned an error"; |
| int rv = MapLastSocketError(socket_); |
| Close(); |
| return rv; |
| } |
| |
| listening_ = true; |
| |
| return OK; |
| } |
| |
| int TCPSocketStarboard::Accept(std::unique_ptr<TCPSocketStarboard>* socket, |
| IPEndPoint* address, |
| CompletionOnceCallback callback) { |
| DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); |
| DCHECK(socket); |
| DCHECK(!callback.is_null()); |
| DCHECK(accept_callback_.is_null()); |
| |
| net_log_.BeginEvent(NetLogEventType::TCP_ACCEPT); |
| |
| int result = AcceptInternal(socket, address); |
| |
| if (result == ERR_IO_PENDING) { |
| if (!base::MessageLoopForIO::current()->Watch( |
| socket_, true, base::MessageLoopCurrentForIO::WATCH_READ, |
| &socket_watcher_, this)) { |
| DLOG(ERROR) << "WatchSocket failed on read"; |
| return MapLastSocketError(socket_); |
| } |
| |
| accept_socket_ = socket; |
| accept_address_ = address; |
| accept_callback_ = std::move(callback); |
| } |
| |
| return result; |
| } |
| |
| int TCPSocketStarboard::SetDefaultOptionsForServer() { |
| DCHECK(SbSocketIsValid(socket_)); |
| if (!SbSocketSetReuseAddress(socket_, true)) { |
| return MapLastSocketError(socket_); |
| } |
| return OK; |
| } |
| |
| void TCPSocketStarboard::SetDefaultOptionsForClient() { |
| SbSocketSetTcpNoDelay(socket_, true); |
| |
| const SbTime kTCPKeepAliveDuration = 45 * kSbTimeSecond; |
| SbSocketSetTcpKeepAlive(socket_, true, kTCPKeepAliveDuration); |
| |
| SbSocketSetTcpWindowScaling(socket_, true); |
| |
| if (SB_NETWORK_RECEIVE_BUFFER_SIZE != 0) { |
| SbSocketSetReceiveBufferSize(socket_, SB_NETWORK_RECEIVE_BUFFER_SIZE); |
| } |
| } |
| |
| int TCPSocketStarboard::AcceptInternal( |
| std::unique_ptr<TCPSocketStarboard>* socket, |
| IPEndPoint* address) { |
| SbSocket new_socket = SbSocketAccept(socket_); |
| if (!SbSocketIsValid(new_socket)) { |
| int net_error = MapLastSocketError(socket_); |
| if (net_error != ERR_IO_PENDING) { |
| net_log_.EndEventWithNetErrorCode(NetLogEventType::TCP_ACCEPT, net_error); |
| } |
| return net_error; |
| } |
| |
| SbSocketAddress sb_address; |
| if (!SbSocketGetLocalAddress(new_socket, &sb_address)) { |
| SbSocketDestroy(new_socket); |
| int net_error = ERR_FAILED; |
| net_log_.EndEventWithNetErrorCode(NetLogEventType::TCP_ACCEPT, net_error); |
| return net_error; |
| } |
| |
| IPEndPoint ip_end_point; |
| if (!ip_end_point.FromSbSocketAddress(&sb_address)) { |
| SbSocketDestroy(new_socket); |
| int net_error = ERR_ADDRESS_INVALID; |
| net_log_.EndEventWithNetErrorCode(NetLogEventType::TCP_ACCEPT, net_error); |
| return net_error; |
| } |
| |
| std::unique_ptr<TCPSocketStarboard> tcp_socket(new TCPSocketStarboard( |
| nullptr, net_log_.net_log(), net_log_.source())); |
| int adopt_result = tcp_socket->AdoptConnectedSocket(new_socket, ip_end_point); |
| if (adopt_result != OK) { |
| if (!SbSocketDestroy(new_socket)) { |
| DPLOG(ERROR) << "SbSocketDestroy"; |
| } |
| net_log_.EndEventWithNetErrorCode( |
| NetLogEventType::TCP_ACCEPT, adopt_result); |
| return adopt_result; |
| } |
| |
| *socket = std::move(tcp_socket); |
| *address = ip_end_point; |
| net_log_.EndEvent(NetLogEventType::TCP_ACCEPT, |
| CreateNetLogIPEndPointCallback(&ip_end_point)); |
| return OK; |
| } |
| |
| void TCPSocketStarboard::Close() { |
| DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); |
| if (SbSocketIsValid(socket_)) { |
| StopWatchingAndCleanUp(); |
| |
| net_log_.AddEvent(NetLogEventType::SOCKET_CLOSED); |
| |
| if (!SbSocketDestroy(socket_)) { |
| DPLOG(ERROR) << "SbSocketDestroy"; |
| } |
| socket_ = kSbSocketInvalid; |
| } |
| |
| listening_ = false; |
| } |
| |
| void TCPSocketStarboard::StopWatchingAndCleanUp() { |
| bool ok = socket_watcher_.StopWatchingSocket(); |
| DCHECK(ok); |
| |
| if (!accept_callback_.is_null()) { |
| accept_socket_ = nullptr; |
| accept_callback_.Reset(); |
| } |
| |
| if (!read_callback_.is_null()) { |
| read_buf_ = nullptr; |
| read_buf_len_ = 0; |
| read_if_ready_callback_.Reset(); |
| read_callback_.Reset(); |
| } |
| |
| read_if_ready_callback_.Reset(); |
| |
| if (!write_callback_.is_null()) { |
| write_buf_ = nullptr; |
| write_buf_len_ = 0; |
| write_callback_.Reset(); |
| } |
| |
| waiting_connect_ = false; |
| peer_address_.reset(); |
| } |
| |
| void TCPSocketStarboard::OnSocketReadyToRead(SbSocket socket) { |
| DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); |
| |
| if (accept_pending()) { |
| int result = AcceptInternal(accept_socket_, accept_address_); |
| if (result != ERR_IO_PENDING) { |
| accept_socket_ = nullptr; |
| CompletionOnceCallback callback = std::move(accept_callback_); |
| accept_callback_.Reset(); |
| std::move(callback).Run(result); |
| } |
| } else if (read_pending()) { |
| DidCompleteRead(); |
| } |
| } |
| |
| void TCPSocketStarboard::OnSocketReadyToWrite(SbSocket socket) { |
| DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); |
| |
| if (listening_) { |
| NOTREACHED(); |
| } else { |
| // is this a connection callback? |
| if (connect_pending()) { |
| DidCompleteConnect(); |
| } else if (write_pending()) { |
| DidCompleteWrite(); |
| } |
| } |
| } |
| |
| int TCPSocketStarboard::Connect(const IPEndPoint& address, |
| CompletionOnceCallback callback) { |
| DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); |
| DCHECK(SbSocketIsValid(socket_)); |
| DCHECK(!peer_address_); |
| DCHECK(!connect_pending()); |
| |
| if (!logging_multiple_connect_attempts_) { |
| LogConnectBegin(AddressList(address)); |
| } |
| |
| net_log_.BeginEvent(NetLogEventType::TCP_CONNECT_ATTEMPT, |
| CreateNetLogIPEndPointCallback(&address)); |
| |
| SbSocketAddress storage; |
| if (!address.ToSbSocketAddress(&storage)) { |
| return ERR_ADDRESS_INVALID; |
| } |
| |
| peer_address_.reset(new IPEndPoint(address)); |
| |
| bool success = SbSocketConnect(socket_, &storage); |
| DCHECK_EQ(true, success); |
| |
| int rv = MapLastSocketError(socket_); |
| if (rv != ERR_IO_PENDING) { |
| return HandleConnectCompleted(rv); |
| } |
| |
| waiting_connect_ = true; |
| write_callback_ = std::move(callback); |
| |
| // When it is ready to write, it will have connected. |
| base::MessageLoopForIO::current()->Watch( |
| socket_, true, base::MessageLoopCurrentForIO::WATCH_WRITE, |
| &socket_watcher_, this); |
| |
| return ERR_IO_PENDING; |
| } |
| |
| int TCPSocketStarboard::HandleConnectCompleted(int rv) { |
| // Log the end of this attempt (and any OS error it threw). |
| if (rv != OK) { |
| net_log_.EndEvent(NetLogEventType::TCP_CONNECT_ATTEMPT, |
| NetLog::IntCallback("mapped_error", rv)); |
| } else { |
| net_log_.EndEvent(NetLogEventType::TCP_CONNECT_ATTEMPT); |
| } |
| |
| // Give a more specific error when the user is offline. |
| if (!logging_multiple_connect_attempts_) |
| LogConnectEnd(rv); |
| |
| return rv; |
| } |
| |
| void TCPSocketStarboard::DidCompleteConnect() { |
| DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); |
| int rv = SbSocketIsConnected(socket_) ? OK : ERR_FAILED; |
| |
| waiting_connect_ = false; |
| CompletionOnceCallback callback = std::move(write_callback_); |
| write_callback_.Reset(); |
| ClearWatcherIfOperationsNotPending(); |
| std::move(callback).Run(HandleConnectCompleted(rv)); |
| } |
| |
| void TCPSocketStarboard::StartLoggingMultipleConnectAttempts( |
| const AddressList& addresses) { |
| if (!logging_multiple_connect_attempts_) { |
| logging_multiple_connect_attempts_ = true; |
| LogConnectBegin(addresses); |
| } else { |
| NOTREACHED(); |
| } |
| } |
| |
| bool TCPSocketStarboard::IsConnected() const { |
| DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); |
| |
| return SbSocketIsConnected(socket_); |
| } |
| |
| bool TCPSocketStarboard::IsConnectedAndIdle() const { |
| DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); |
| |
| return SbSocketIsConnectedAndIdle(socket_); |
| } |
| |
| void TCPSocketStarboard::EndLoggingMultipleConnectAttempts(int net_error) { |
| if (logging_multiple_connect_attempts_) { |
| LogConnectEnd(net_error); |
| logging_multiple_connect_attempts_ = false; |
| } else { |
| NOTREACHED(); |
| } |
| } |
| |
| void TCPSocketStarboard::LogConnectBegin(const AddressList& addresses) const { |
| net_log_.BeginEvent(NetLogEventType::TCP_CONNECT, |
| addresses.CreateNetLogCallback()); |
| } |
| |
| void TCPSocketStarboard::LogConnectEnd(int net_error) { |
| if (net_error != OK) { |
| net_log_.EndEventWithNetErrorCode(NetLogEventType::TCP_CONNECT, net_error); |
| return; |
| } |
| |
| net_log_.EndEvent(NetLogEventType::TCP_CONNECT); |
| } |
| |
| int TCPSocketStarboard::Read(IOBuffer* buf, |
| int buf_len, |
| CompletionOnceCallback callback) { |
| DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); |
| DCHECK(SbSocketIsValid(socket_)); |
| DCHECK(!connect_pending()); |
| DCHECK_GT(buf_len, 0); |
| |
| int rv = ReadIfReady( |
| buf, buf_len, |
| base::Bind(&TCPSocketStarboard::RetryRead, base::Unretained(this))); |
| |
| if (rv != ERR_IO_PENDING) { |
| return rv; |
| } |
| |
| // We'll callback when there is more data. |
| read_buf_ = buf; |
| read_buf_len_ = buf_len; |
| read_callback_ = std::move(callback); |
| return rv; |
| } |
| |
| int TCPSocketStarboard::ReadIfReady(IOBuffer* buf, |
| int buf_len, |
| CompletionOnceCallback callback) { |
| DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); |
| DCHECK(SbSocketIsValid(socket_)); |
| DCHECK(read_if_ready_callback_.is_null()); |
| |
| int rv = DoRead(buf, buf_len); |
| |
| if (rv != ERR_IO_PENDING) { |
| return rv; |
| } |
| |
| read_if_ready_callback_ = std::move(callback); |
| base::MessageLoopForIO::current()->Watch( |
| socket_, true, base::MessageLoopCurrentForIO::WATCH_READ, |
| &socket_watcher_, this); |
| |
| return rv; |
| } |
| |
| int TCPSocketStarboard::CancelReadIfReady() { |
| DCHECK(read_if_ready_callback_); |
| |
| bool ok = socket_watcher_.StopWatchingSocket(); |
| DCHECK(ok); |
| |
| read_if_ready_callback_.Reset(); |
| return net::OK; |
| } |
| |
| void TCPSocketStarboard::RetryRead(int rv) { |
| DCHECK(read_callback_); |
| DCHECK(read_buf_); |
| DCHECK_LT(0, read_buf_len_); |
| |
| if (rv == OK) { |
| rv = ReadIfReady( |
| read_buf_, read_buf_len_, |
| base::Bind(&TCPSocketStarboard::RetryRead, base::Unretained(this))); |
| if (rv == ERR_IO_PENDING) |
| return; |
| } |
| read_buf_ = nullptr; |
| read_buf_len_ = 0; |
| base::ResetAndReturn(&read_callback_).Run(rv); |
| } |
| |
| int TCPSocketStarboard::DoRead(IOBuffer* buf, int buf_len) { |
| int bytes_read = SbSocketReceiveFrom(socket_, buf->data(), buf_len, NULL); |
| |
| if (bytes_read >= 0) { |
| net_log_.AddByteTransferEvent( |
| NetLogEventType::SOCKET_BYTES_RECEIVED, bytes_read, buf->data()); |
| NetworkActivityMonitor::GetInstance()->IncrementBytesReceived(bytes_read); |
| |
| return bytes_read; |
| } else { |
| // If |bytes_read| < 0, some kind of error occurred. |
| SbSocketError starboard_error = SbSocketGetLastError(socket_); |
| int rv = MapSocketError(starboard_error); |
| net_log_.AddEvent(NetLogEventType::SOCKET_READ_ERROR, |
| CreateNetLogSocketErrorCallback(rv, starboard_error)); |
| if (rv != ERR_IO_PENDING) { |
| DLOG(ERROR) << __FUNCTION__ << "[" << this << "]: Error: " << rv; |
| } |
| return rv; |
| } |
| } |
| |
| void TCPSocketStarboard::DidCompleteRead() { |
| DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); |
| |
| CompletionOnceCallback callback = std::move(read_if_ready_callback_); |
| read_if_ready_callback_.Reset(); |
| |
| ClearWatcherIfOperationsNotPending(); |
| std::move(callback).Run(OK); |
| } |
| |
| int TCPSocketStarboard::Write( |
| IOBuffer* buf, |
| int buf_len, |
| CompletionOnceCallback callback, |
| const NetworkTrafficAnnotationTag& traffic_annotation) { |
| DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); |
| DCHECK(SbSocketIsValid(socket_)); |
| DCHECK(!connect_pending()); |
| DCHECK(!write_pending()); |
| DCHECK_GT(buf_len, 0); |
| |
| int rv = DoWrite(buf, buf_len); |
| |
| if (rv == ERR_IO_PENDING) { |
| // We are just blocked, so let's set up the callback. |
| write_buf_ = buf; |
| write_buf_len_ = buf_len; |
| write_callback_ = std::move(callback); |
| base::MessageLoopForIO::current()->Watch( |
| socket_, true, base::MessageLoopCurrentForIO::WATCH_WRITE, |
| &socket_watcher_, this); |
| } |
| |
| return rv; |
| } |
| |
| int TCPSocketStarboard::DoWrite(IOBuffer* buf, int buf_len) { |
| int bytes_sent = SbSocketSendTo(socket_, buf->data(), buf_len, NULL); |
| |
| if (bytes_sent >= 0) { |
| net_log_.AddByteTransferEvent( |
| NetLogEventType::SOCKET_BYTES_SENT, bytes_sent, buf->data()); |
| NetworkActivityMonitor::GetInstance()->IncrementBytesSent(bytes_sent); |
| |
| return bytes_sent; |
| } else { |
| SbSocketError starboard_error = SbSocketGetLastError(socket_); |
| int rv = MapSocketError(starboard_error); |
| net_log_.AddEvent(NetLogEventType::SOCKET_WRITE_ERROR, |
| CreateNetLogSocketErrorCallback(rv, starboard_error)); |
| if (rv != ERR_IO_PENDING) { |
| DLOG(ERROR) << __FUNCTION__ << "[" << this << "]: Error: " << rv; |
| } |
| |
| return rv; |
| } |
| } |
| |
| void TCPSocketStarboard::DidCompleteWrite() { |
| DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); |
| DCHECK(write_pending()); |
| |
| int rv = DoWrite(write_buf_, write_buf_len_); |
| |
| if (rv != ERR_IO_PENDING) { |
| write_buf_ = nullptr; |
| write_buf_len_ = 0; |
| |
| CompletionOnceCallback callback = std::move(write_callback_); |
| write_callback_.Reset(); |
| |
| ClearWatcherIfOperationsNotPending(); |
| std::move(callback).Run(rv); |
| } |
| } |
| |
| bool TCPSocketStarboard::SetReceiveBufferSize(int32_t size) { |
| return SetSocketReceiveBufferSize(socket_, size); |
| } |
| |
| bool TCPSocketStarboard::SetSendBufferSize(int32_t size) { |
| return SetSocketSendBufferSize(socket_, size); |
| } |
| |
| bool TCPSocketStarboard::SetKeepAlive(bool enable, int delay) { |
| return SbSocketSetTcpKeepAlive(socket_, enable, delay); |
| } |
| |
| bool TCPSocketStarboard::SetNoDelay(bool no_delay) { |
| return SetTCPNoDelay(socket_, no_delay); |
| } |
| |
| bool TCPSocketStarboard::GetEstimatedRoundTripTime( |
| base::TimeDelta* out_rtt) const { |
| DCHECK(out_rtt); |
| // Intentionally commented to suppress tons of warnings. |
| // net calls this all the time. |
| // NOTIMPLEMENTED(); |
| return false; |
| } |
| |
| int TCPSocketStarboard::GetLocalAddress(IPEndPoint* address) const { |
| DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); |
| DCHECK(address); |
| |
| SbSocketAddress sb_address; |
| if (!SbSocketGetLocalAddress(socket_, &sb_address)) { |
| SbSocketError starboard_error = SbSocketGetLastError(socket_); |
| return MapSocketError(starboard_error); |
| } |
| |
| if (!address->FromSbSocketAddress(&sb_address)) { |
| return ERR_ADDRESS_INVALID; |
| } |
| |
| return OK; |
| } |
| |
| int TCPSocketStarboard::GetPeerAddress(IPEndPoint* address) const { |
| DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); |
| DCHECK(address); |
| if (!IsConnected()) |
| return ERR_SOCKET_NOT_CONNECTED; |
| *address = *peer_address_; |
| return OK; |
| } |
| |
| void TCPSocketStarboard::DetachFromThread() { |
| DETACH_FROM_THREAD(thread_checker_); |
| } |
| |
| SocketDescriptor TCPSocketStarboard::ReleaseSocketDescriptorForTesting() { |
| SocketDescriptor socket_descriptor = socket_; |
| socket_ = kSbSocketInvalid; |
| Close(); |
| return socket_descriptor; |
| } |
| |
| SocketDescriptor TCPSocketStarboard::SocketDescriptorForTesting() const { |
| return socket_; |
| } |
| |
| void TCPSocketStarboard::ApplySocketTag(const SocketTag& tag) { |
| if (SbSocketIsValid(socket_) && tag != tag_) { |
| tag.Apply(socket_); |
| } |
| tag_ = tag; |
| } |
| |
| void TCPSocketStarboard::ClearWatcherIfOperationsNotPending() { |
| if (!read_pending() && !write_pending() && !accept_pending() && |
| !connect_pending()) { |
| bool ok = socket_watcher_.StopWatchingSocket(); |
| DCHECK(ok); |
| } |
| } |
| |
| } // namespace net |