diff options
author | sergeyu@chromium.org <sergeyu@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-05-27 20:03:16 +0000 |
---|---|---|
committer | sergeyu@chromium.org <sergeyu@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-05-27 20:03:16 +0000 |
commit | a1b95f0eaba4d94ceb0711f2fd3245d8ecd444a0 (patch) | |
tree | 3f7208d4742fab73de02b90af827a3f689dae2a5 /net/socket/tcp_client_socket_libevent.cc | |
parent | 55775cc217406277d7591c22f5d7abec91773edc (diff) | |
download | chromium_src-a1b95f0eaba4d94ceb0711f2fd3245d8ecd444a0.zip chromium_src-a1b95f0eaba4d94ceb0711f2fd3245d8ecd444a0.tar.gz chromium_src-a1b95f0eaba4d94ceb0711f2fd3245d8ecd444a0.tar.bz2 |
Bind() methods for TCP sockets
BUG=80245
TEST=None
Review URL: http://codereview.chromium.org/7004055
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@87077 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'net/socket/tcp_client_socket_libevent.cc')
-rw-r--r-- | net/socket/tcp_client_socket_libevent.cc | 128 |
1 files changed, 94 insertions, 34 deletions
diff --git a/net/socket/tcp_client_socket_libevent.cc b/net/socket/tcp_client_socket_libevent.cc index 6e0cb1c..222ff94 100644 --- a/net/socket/tcp_client_socket_libevent.cc +++ b/net/socket/tcp_client_socket_libevent.cc @@ -70,6 +70,36 @@ void SetTCPKeepAlive(int fd) { #endif } +// Sets socket parameters. Returns the OS error code (or 0 on +// success). +int SetupSocket(int socket) { + if (SetNonBlocking(socket)) + return errno; + + // This mirrors the behaviour on Windows. See the comment in + // tcp_client_socket_win.cc after searching for "NODELAY". + DisableNagle(socket); // If DisableNagle fails, we don't care. + SetTCPKeepAlive(socket); + + return 0; +} + +// Creates a new socket and sets default parameters for it. Returns +// the OS error code (or 0 on success). +int CreateSocket(int family, int* socket) { + *socket = ::socket(family, SOCK_STREAM, IPPROTO_TCP); + if (*socket == kInvalidSocket) + return errno; + int error = SetupSocket(*socket); + if (error) { + if (HANDLE_EINTR(close(*socket)) < 0) + PLOG(ERROR) << "close"; + *socket = kInvalidSocket; + return error; + } + return 0; +} + int MapConnectError(int os_error) { switch (os_error) { case EACCES: @@ -100,6 +130,7 @@ TCPClientSocketLibevent::TCPClientSocketLibevent( net::NetLog* net_log, const net::NetLog::Source& source) : socket_(kInvalidSocket), + bound_socket_(kInvalidSocket), addresses_(addresses), current_ai_(NULL), read_watcher_(this), @@ -126,16 +157,53 @@ TCPClientSocketLibevent::~TCPClientSocketLibevent() { net_log_.EndEvent(NetLog::TYPE_SOCKET_ALIVE, NULL); } -void TCPClientSocketLibevent::AdoptSocket(int socket) { +int TCPClientSocketLibevent::AdoptSocket(int socket) { DCHECK_EQ(socket_, kInvalidSocket); + + int error = SetupSocket(socket); + if (error) + return MapSystemError(error); + socket_ = socket; - int error = SetupSocket(); - DCHECK_EQ(0, error); - // This is to make GetPeerAddress work. It's up to the test that is calling - // this function to ensure that address_ contains a reasonable address for - // this socket. (i.e. at least match IPv4 vs IPv6!). + + // This is to make GetPeerAddress() work. It's up to the caller ensure + // that |address_| contains a reasonable address for this + // socket. (i.e. at least match IPv4 vs IPv6!). current_ai_ = addresses_.head(); use_history_.set_was_ever_connected(); + + return OK; +} + +int TCPClientSocketLibevent::Bind(const IPEndPoint& address) { + if (current_ai_ != NULL || bind_address_.get()) { + // Cannot bind the socket if we are already bound connected or + // connecting. + return ERR_UNEXPECTED; + } + + sockaddr_storage addr_storage; + sockaddr* addr = reinterpret_cast<struct sockaddr*>(&addr_storage); + size_t addr_len = sizeof(addr_storage); + if (!address.ToSockAddr(addr, &addr_len)) + return ERR_INVALID_ARGUMENT; + + // Create |bound_socket_| and try to bound it to |address|. + int error = CreateSocket(address.GetFamily(), &bound_socket_); + if (error) + return MapSystemError(error); + + if (HANDLE_EINTR(bind(bound_socket_, addr, addr_len))) { + error = errno; + if (HANDLE_EINTR(close(bound_socket_)) < 0) + PLOG(ERROR) << "close"; + bound_socket_ = kInvalidSocket; + return MapSystemError(error); + } + + bind_address_.reset(new IPEndPoint(address)); + + return 0; } int TCPClientSocketLibevent::Connect(CompletionCallback* callback) { @@ -212,10 +280,26 @@ int TCPClientSocketLibevent::DoConnect() { next_connect_state_ = CONNECT_STATE_CONNECT_COMPLETE; - // Create a non-blocking socket. - connect_os_error_ = CreateSocket(current_ai_); - if (connect_os_error_) - return MapSystemError(connect_os_error_); + if (bound_socket_ != kInvalidSocket) { + DCHECK(bind_address_.get()); + socket_ = bound_socket_; + bound_socket_ = kInvalidSocket; + } else { + // Create a non-blocking socket. + connect_os_error_ = CreateSocket(current_ai_->ai_family, &socket_); + if (connect_os_error_) + return MapSystemError(connect_os_error_); + + if (bind_address_.get()) { + sockaddr_storage addr_storage; + sockaddr* addr = reinterpret_cast<struct sockaddr*>(&addr_storage); + size_t addr_len = sizeof(addr_storage); + if (!bind_address_->ToSockAddr(addr, &addr_len)) + return ERR_INVALID_ARGUMENT; + if (HANDLE_EINTR(bind(socket_, addr, addr_len))) + return MapSystemError(errno); + } + } // Connect the socket. if (!use_tcp_fastopen_) { @@ -460,30 +544,6 @@ bool TCPClientSocketLibevent::SetSendBufferSize(int32 size) { return rv == 0; } - -int TCPClientSocketLibevent::CreateSocket(const addrinfo* ai) { - socket_ = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol); - if (socket_ == kInvalidSocket) - return errno; - return SetupSocket(); -} - -int TCPClientSocketLibevent::SetupSocket() { - if (SetNonBlocking(socket_)) { - const int err = errno; - close(socket_); - socket_ = kInvalidSocket; - return err; - } - - // This mirrors the behaviour on Windows. See the comment in - // tcp_client_socket_win.cc after searching for "NODELAY". - DisableNagle(socket_); // If DisableNagle fails, we don't care. - SetTCPKeepAlive(socket_); - - return 0; -} - void TCPClientSocketLibevent::LogConnectCompletion(int net_error) { if (net_error == OK) UpdateConnectionTypeHistograms(CONNECTION_ANY); |