summaryrefslogtreecommitdiffstats
path: root/net/socket/tcp_client_socket_libevent.cc
diff options
context:
space:
mode:
authorsergeyu@chromium.org <sergeyu@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2011-05-27 20:03:16 +0000
committersergeyu@chromium.org <sergeyu@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2011-05-27 20:03:16 +0000
commita1b95f0eaba4d94ceb0711f2fd3245d8ecd444a0 (patch)
tree3f7208d4742fab73de02b90af827a3f689dae2a5 /net/socket/tcp_client_socket_libevent.cc
parent55775cc217406277d7591c22f5d7abec91773edc (diff)
downloadchromium_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.cc128
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);