Chromium Code Reviews| Index: net/socket/unix_domain_client_socket_posix.cc |
| diff --git a/net/socket/unix_domain_client_socket_posix.cc b/net/socket/unix_domain_client_socket_posix.cc |
| new file mode 100644 |
| index 0000000000000000000000000000000000000000..e75e6a28c6094ff48a60c805f021cc8d44812037 |
| --- /dev/null |
| +++ b/net/socket/unix_domain_client_socket_posix.cc |
| @@ -0,0 +1,318 @@ |
| +// Copyright 2014 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 "net/socket/unix_domain_client_socket_posix.h" |
| + |
| +#include <errno.h> |
| +#include <sys/socket.h> |
| +#include <unistd.h> |
| + |
| +#include "base/callback.h" |
| +#include "base/callback_helpers.h" |
| +#include "base/posix/eintr_wrapper.h" |
| +#include "net/base/io_buffer.h" |
| +#include "net/base/net_errors.h" |
| +#include "net/base/net_util.h" |
| +#include "net/socket/stream_socket.h" |
| + |
| +namespace net { |
| + |
| +UnixDomainClientSocket::UnixDomainClientSocket(const std::string& socket_path, |
| + bool use_abstract_namespace) |
| + : socket_path_(socket_path), |
| + use_abstract_namespace_(use_abstract_namespace), |
| + socket_fd_(kInvalidSocket), |
| + waiting_connect_(true), |
| + read_buf_len_(0), |
| + write_buf_len_(0) { |
| +} |
| + |
| +UnixDomainClientSocket::UnixDomainClientSocket(SocketDescriptor socket_fd) |
| + : use_abstract_namespace_(false), |
| + socket_fd_(socket_fd), |
| + waiting_connect_(false), |
| + read_buf_len_(0), |
| + write_buf_len_(0) { |
| +} |
| + |
| +UnixDomainClientSocket::~UnixDomainClientSocket() { |
| + Disconnect(); |
| +} |
| + |
| +// static |
| +bool UnixDomainClientSocket::FillAddress(const std::string& socket_path, |
| + bool use_abstract_namespace, |
| + sockaddr_un* socket_addr, |
| + socklen_t* addr_len) { |
| + size_t path_max = *addr_len - offsetof(struct sockaddr_un, sun_path); |
| + // Non abstract namespace pathname should be null-terminated. Abstract |
| + // namespace pathname must start with '\0'. So, the size is always greater |
| + // than socket_path size by 1. |
| + size_t path_size = socket_path.size() + 1; |
| + if (path_size > path_max) |
| + return false; |
| + |
| + memset(socket_addr, 0, *addr_len); |
| + socket_addr->sun_family = AF_UNIX; |
| + if (!use_abstract_namespace) { |
| + memcpy(socket_addr->sun_path, socket_path.c_str(), socket_path.size()); |
| + return true; |
| + } |
| + |
| +#if defined(OS_ANDROID) || defined(OS_LINUX) |
| + // Convert the path given into abstract socket name. It must start with |
| + // the '\0' character, so we are adding it. |addr_len| must specify the |
| + // length of the structure exactly, as potentially the socket name may |
| + // have '\0' characters embedded (although we don't support this). |
| + // Note that addr.sun_path is already zero initialized. |
| + memcpy(socket_addr->sun_path + 1, socket_path.c_str(), socket_path.size()); |
| + *addr_len = socket_path.size() + offsetof(struct sockaddr_un, sun_path) + 1; |
| + return true; |
| +#else |
| + return false; |
| +#endif |
| +} |
| + |
| +int UnixDomainClientSocket::Connect(const CompletionCallback& callback) { |
|
mmenke
2014/06/11 16:05:50
DCHECK(waiting_connect_)?
byungchul
2014/06/20 08:22:32
Done.
|
| + if (IsConnected()) |
| + return ERR_SOCKET_IS_CONNECTED; |
|
mmenke
2014/06/11 16:05:50
Should we DCHECK on this instead? (I know we don'
byungchul
2014/06/20 08:22:32
Done.
|
| + |
| + if (socket_path_.empty()) |
| + return ERR_ADDRESS_INVALID; |
| + |
| + DCHECK(!callback.is_null()); |
| + DCHECK(write_callback_.is_null()); |
|
mmenke
2014/06/11 16:05:50
Move these up before the IsConnected check?
byungchul
2014/06/20 08:22:32
Done.
|
| + |
| + int rv = DoConnect(); |
| + if (rv != ERR_IO_PENDING) { |
| + if (rv == OK) |
| + waiting_connect_ = false; |
| + return rv; |
| + } |
| + |
| + if (!base::MessageLoopForIO::current()->WatchFileDescriptor( |
| + socket_fd_, true, base::MessageLoopForIO::WATCH_WRITE, |
| + &write_socket_watcher_, this)) { |
| + PLOG(ERROR) << "WatchFileDescriptor failed on write"; |
| + return MapSystemError(errno); |
| + } |
| + |
| + write_callback_ = callback; |
| + return ERR_IO_PENDING; |
| +} |
| + |
| +int UnixDomainClientSocket::DoConnect() { |
| + sockaddr_un addr; |
| + socklen_t addr_len = sizeof(addr); |
| + if (!FillAddress(socket_path_, use_abstract_namespace_, &addr, &addr_len)) |
| + return ERR_ADDRESS_INVALID; |
| + |
| + if (socket_fd_ == kInvalidSocket) { |
| + SocketDescriptor s = CreatePlatformSocket(PF_UNIX, SOCK_STREAM, 0); |
| + if (s == kInvalidSocket) |
| + return errno ? MapSystemError(errno) : ERR_UNEXPECTED; |
| + |
| + if (SetNonBlocking(s)) { |
| + int rv = MapSystemError(errno); |
| + close(s); |
| + return rv; |
| + } |
| + |
| + socket_fd_ = s; |
| + } |
| + |
| + int rv = HANDLE_EINTR(connect(socket_fd_, |
| + reinterpret_cast<sockaddr*>(&addr), |
| + addr_len)); |
| + DCHECK_LE(rv, 0); |
| + return rv == 0 ? OK : |
| + errno == EINPROGRESS ? ERR_IO_PENDING : MapSystemError(errno); |
| +} |
| + |
| +void UnixDomainClientSocket::DidCompleteConnect() { |
| + // Get the error that connect() completed with. |
| + int os_error = 0; |
| + socklen_t len = sizeof(os_error); |
| + if (getsockopt(socket_fd_, SOL_SOCKET, SO_ERROR, &os_error, &len) < 0) |
| + os_error = errno; |
| + |
| + int rv = MapSystemError(os_error); |
| + if (rv == ERR_IO_PENDING) |
| + return; |
| + |
| + write_socket_watcher_.StopWatchingFileDescriptor(); |
| + waiting_connect_ = false; |
| + base::ResetAndReturn(&write_callback_).Run(rv); |
| +} |
| + |
| +void UnixDomainClientSocket::Disconnect() { |
| + if (socket_fd_ == kInvalidSocket) |
| + return; |
| + close(socket_fd_); |
| + socket_fd_ = kInvalidSocket; |
| +} |
| + |
| +bool UnixDomainClientSocket::IsConnected() const { |
| + return socket_fd_ != kInvalidSocket && !waiting_connect_; |
| +} |
| + |
| +bool UnixDomainClientSocket::IsConnectedAndIdle() const { |
| + return IsConnected(); |
| +} |
| + |
| +int UnixDomainClientSocket::GetPeerAddress(IPEndPoint* address) const { |
| + NOTIMPLEMENTED(); |
| + return ERR_NOT_IMPLEMENTED; |
| +} |
| + |
| +int UnixDomainClientSocket::GetLocalAddress(IPEndPoint* address) const { |
| + NOTIMPLEMENTED(); |
| + return ERR_NOT_IMPLEMENTED; |
| +} |
| + |
| +const BoundNetLog& UnixDomainClientSocket::NetLog() const { |
| + return netlog_; |
| +} |
| + |
| +void UnixDomainClientSocket::SetSubresourceSpeculation() { |
| +} |
| + |
| +void UnixDomainClientSocket::SetOmniboxSpeculation() { |
| +} |
| + |
| +bool UnixDomainClientSocket::WasEverUsed() const { |
| + return true; // We don't care. |
| +} |
| + |
| +bool UnixDomainClientSocket::UsingTCPFastOpen() const { |
| + return false; |
| +} |
| + |
| +bool UnixDomainClientSocket::WasNpnNegotiated() const { |
| + return false; |
| +} |
| + |
| +NextProto UnixDomainClientSocket::GetNegotiatedProtocol() const { |
| + return kProtoUnknown; |
| +} |
| + |
| +bool UnixDomainClientSocket::GetSSLInfo(SSLInfo* ssl_info) { |
| + return false; |
| +} |
| + |
| +int UnixDomainClientSocket::Read(IOBuffer* buf, int buf_len, |
| + const CompletionCallback& callback) { |
| + DCHECK(buf); |
| + DCHECK_LT(0, buf_len); |
| + DCHECK(!callback.is_null()); |
| + DCHECK(read_callback_.is_null()); |
| + |
| + if (!IsConnected()) |
| + return ERR_SOCKET_NOT_CONNECTED; |
| + |
| + int rv = DoRead(buf, buf_len); |
| + if (rv != ERR_IO_PENDING) |
| + return rv; |
| + |
| + if (!base::MessageLoopForIO::current()->WatchFileDescriptor( |
| + socket_fd_, true, base::MessageLoopForIO::WATCH_READ, |
| + &read_socket_watcher_, this)) { |
| + PLOG(ERROR) << "WatchFileDescriptor failed on read"; |
| + return MapSystemError(errno); |
| + } |
| + |
| + read_buf_ = buf; |
| + read_buf_len_ = buf_len; |
| + read_callback_ = callback; |
| + return ERR_IO_PENDING; |
| +} |
| + |
| +int UnixDomainClientSocket::DoRead(IOBuffer* buf, int buf_len) { |
| + int rv = HANDLE_EINTR(read(socket_fd_, buf->data(), buf_len)); |
| + return rv >= 0 ? rv : MapSystemError(errno); |
| +} |
| + |
| +void UnixDomainClientSocket::DidCompleteRead() { |
| + int rv = DoRead(read_buf_, read_buf_len_); |
| + if (rv == ERR_IO_PENDING) |
| + return; |
| + |
| + read_socket_watcher_.StopWatchingFileDescriptor(); |
| + read_buf_ = NULL; |
| + read_buf_len_ = 0; |
| + base::ResetAndReturn(&read_callback_).Run(rv); |
| +} |
| + |
| +int UnixDomainClientSocket::Write(IOBuffer* buf, int buf_len, |
| + const CompletionCallback& callback) { |
| + DCHECK(buf); |
| + DCHECK_LT(0, buf_len); |
| + DCHECK(!callback.is_null()); |
| + DCHECK(write_callback_.is_null()); |
| + |
| + if (!IsConnected()) |
| + return ERR_SOCKET_NOT_CONNECTED; |
| + |
| + int rv = DoWrite(buf, buf_len); |
| + if (rv != ERR_IO_PENDING) |
| + return rv; |
| + |
| + if (!base::MessageLoopForIO::current()->WatchFileDescriptor( |
| + socket_fd_, true, base::MessageLoopForIO::WATCH_WRITE, |
| + &write_socket_watcher_, this)) { |
| + PLOG(ERROR) << "WatchFileDescriptor failed on write"; |
| + return MapSystemError(errno); |
| + } |
| + |
| + write_buf_ = buf; |
| + write_buf_len_ = buf_len; |
| + write_callback_ = callback; |
| + return ERR_IO_PENDING; |
| +} |
| + |
| +int UnixDomainClientSocket::DoWrite(IOBuffer* buf, int buf_len) { |
| + int rv = HANDLE_EINTR(write(socket_fd_, buf->data(), buf_len)); |
| + return rv >= 0 ? rv : MapSystemError(errno); |
| +} |
| + |
| +void UnixDomainClientSocket::DidCompleteWrite() { |
|
mmenke
2014/06/11 16:05:50
This function seems to be misnamed (As is DidCompl
byungchul
2014/06/11 17:30:28
Per your request, will try to split this change in
mmenke
2014/06/11 17:53:27
Yea, makes sense.
Hrm... Since TCPSocketLibevent
byungchul
2014/06/20 08:22:32
Done. Please review https://codereview.chromium.or
|
| + int rv = DoWrite(write_buf_, write_buf_len_); |
| + if (rv == ERR_IO_PENDING) |
| + return; |
| + |
| + write_socket_watcher_.StopWatchingFileDescriptor(); |
| + write_buf_ = NULL; |
| + write_buf_len_ = 0; |
| + base::ResetAndReturn(&write_callback_).Run(rv); |
| +} |
| + |
| +int UnixDomainClientSocket::SetReceiveBufferSize(int32 size) { |
| + NOTIMPLEMENTED(); |
| + return ERR_NOT_IMPLEMENTED; |
| +} |
| + |
| +int UnixDomainClientSocket::SetSendBufferSize(int32 size) { |
| + NOTIMPLEMENTED(); |
| + return ERR_NOT_IMPLEMENTED; |
| +} |
| + |
| +void UnixDomainClientSocket::OnFileCanReadWithoutBlocking(int fd) { |
| + if (read_callback_.is_null()) { |
| + NOTREACHED(); |
| + } else { |
| + DidCompleteRead(); |
| + } |
| +} |
| + |
| +void UnixDomainClientSocket::OnFileCanWriteWithoutBlocking(int fd) { |
| + if (write_callback_.is_null()) { |
| + NOTREACHED(); |
|
mmenke
2014/06/11 16:05:50
Shouldn't be handling this case, should just have
byungchul
2014/06/20 08:22:32
Done.
|
| + } else if (waiting_connect_) { |
| + DidCompleteConnect(); |
| + } else { |
| + DidCompleteWrite(); |
| + } |
| +} |
| + |
| +} // namespace net |