Index: chrome/browser/extensions/api/socket/tcp_socket.cc |
diff --git a/chrome/browser/extensions/api/socket/tcp_socket.cc b/chrome/browser/extensions/api/socket/tcp_socket.cc |
index 871567dbbd5f372bc2d850ef17f9362c7e682f7a..00287a54f4630743ab50ec4071d0a0076a81e129 100644 |
--- a/chrome/browser/extensions/api/socket/tcp_socket.cc |
+++ b/chrome/browser/extensions/api/socket/tcp_socket.cc |
@@ -15,14 +15,27 @@ |
namespace extensions { |
TCPSocket::TCPSocket(ApiResourceEventNotifier* event_notifier) |
- : Socket(event_notifier) { |
+ : Socket(event_notifier), |
+ is_client_socket_(false), |
+ is_server_socket_(false) { |
+} |
+ |
+TCPSocket::TCPSocket(ApiResourceEventNotifier* event_notifier, |
+ net::TCPClientSocket* client_socket) |
+ : Socket(event_notifier), |
+ socket_(client_socket), |
+ is_client_socket_(true), |
+ is_server_socket_(false) { |
+ this->is_connected_ = true; |
} |
// For testing. |
TCPSocket::TCPSocket(net::TCPClientSocket* tcp_client_socket, |
ApiResourceEventNotifier* event_notifier) |
: Socket(event_notifier), |
- socket_(tcp_client_socket) { |
+ socket_(tcp_client_socket), |
+ is_client_socket_(false), |
+ is_server_socket_(false) { |
} |
// static |
@@ -43,11 +56,15 @@ void TCPSocket::Connect(const std::string& address, |
const CompletionCallback& callback) { |
DCHECK(!callback.is_null()); |
- if (!connect_callback_.is_null()) { |
+ if (!connect_callback_.is_null() || is_server_socket_) { |
callback.Run(net::ERR_CONNECTION_FAILED); |
return; |
} |
+ DCHECK(server_socket_.get() == NULL); |
+ is_client_socket_ = true; |
connect_callback_ = callback; |
+ // TODO(justinlin): I think this should destroy any previously connected |
+ // socket, otherwise we might run into binding issues. |
int result = net::ERR_CONNECTION_FAILED; |
do { |
@@ -62,6 +79,15 @@ void TCPSocket::Connect(const std::string& address, |
socket_.reset(new net::TCPClientSocket(address_list, NULL, |
net::NetLog::Source())); |
+ |
+ if (bind_address_.get() != NULL) { |
+ if (socket_->Bind(*bind_address_) != 0) { |
+ socket_.reset(); |
+ OnConnectComplete(net::ERR_ADDRESS_IN_USE); |
+ return; |
+ }; |
+ } |
+ |
connect_callback_ = callback; |
result = socket_->Connect(base::Bind( |
&TCPSocket::OnConnectComplete, base::Unretained(this))); |
@@ -77,14 +103,28 @@ void TCPSocket::Disconnect() { |
} |
int TCPSocket::Bind(const std::string& address, int port) { |
- // TODO(penghuang): Supports bind for tcp? |
- return net::ERR_FAILED; |
+ // TODO(justinlin): Allow binding to multiple interfaces? |
+ if (bind_address_.get() != NULL) { |
+ return net::ERR_FAILED; |
+ } |
+ |
+ scoped_ptr<net::IPEndPoint> ip_end_point(new net::IPEndPoint()); |
+ if (!StringAndPortToIPEndPoint(address, port, ip_end_point.get())) |
+ return net::ERR_INVALID_ARGUMENT; |
+ |
+ bind_address_.swap(ip_end_point); |
+ return true; |
} |
void TCPSocket::Read(int count, |
const ReadCompletionCallback& callback) { |
DCHECK(!callback.is_null()); |
+ if (!is_client_socket_) { |
+ callback.Run(net::ERR_FAILED, NULL); |
+ return; |
+ } |
+ |
if (!read_callback_.is_null()) { |
callback.Run(net::ERR_IO_PENDING, NULL); |
return; |
@@ -140,6 +180,51 @@ bool TCPSocket::SetNoDelay(bool no_delay) { |
return socket_->SetNoDelay(no_delay); |
} |
+bool TCPSocket::Listen(int backlog) { |
+ if (is_client_socket_) { |
+ return false; |
miket_OOO
2012/08/31 23:18:01
An error message would be nice.
justinlin
2012/09/11 04:32:32
Done.
|
+ } |
+ DCHECK(socket_.get() == NULL); |
+ is_server_socket_ = true; |
+ |
+ if (bind_address_.get() == NULL) { |
+ return net::ERR_FAILED; |
+ } |
+ |
+ server_socket_.reset(new net::TCPServerSocket(NULL, |
+ net::NetLog::Source())); |
+ return server_socket_->Listen(*bind_address_, backlog); |
+} |
+ |
+void TCPSocket::Accept(const AcceptCompletionCallback &callback) { |
+ // This also makes it so you can't accept before listening, but that's |
+ // probably OK? |
+ if (!is_server_socket_ || server_socket_.get() == NULL) { |
+ callback.Run(net::ERR_FAILED, NULL); |
+ return; |
+ } |
+ |
+ // Limits to only 1 blocked accept call. |
miket_OOO
2012/08/31 23:18:01
Should this be a TODO to allow multiple? (I'm not
justinlin
2012/09/11 04:32:32
Yea, the number of connections held in the queue i
|
+ if (!accept_callback_.is_null()) { |
+ callback.Run(net::ERR_FAILED, NULL); |
+ return; |
+ } |
+ |
+ int result = server_socket_->Accept(&accept_socket_, base::Bind( |
+ &TCPSocket::OnAccept, base::Unretained(this))); |
+ if (result == net::ERR_IO_PENDING) { |
+ accept_callback_ = callback; |
+ return; |
+ } else if (result == net::OK) { |
+ accept_callback_ = callback; |
+ this->OnAccept(result); |
+ return; |
+ } else { |
+ callback.Run(result, NULL); |
+ return; |
+ } |
+} |
+ |
bool TCPSocket::IsTCPSocket() { |
return true; |
} |
@@ -151,16 +236,22 @@ bool TCPSocket::GetPeerAddress(net::IPEndPoint* address) { |
} |
bool TCPSocket::GetLocalAddress(net::IPEndPoint* address) { |
- if (!socket_.get()) |
+ if (socket_.get() != NULL) { |
+ return !socket_->GetLocalAddress(address); |
+ } else if (server_socket_.get() != NULL) { |
+ return !server_socket_->GetLocalAddress(address); |
+ } else { |
return false; |
- return !socket_->GetLocalAddress(address); |
+ } |
} |
int TCPSocket::WriteImpl(net::IOBuffer* io_buffer, |
int io_buffer_size, |
const net::CompletionCallback& callback) { |
- if (!socket_.get() || !socket_->IsConnected()) |
+ if (!is_client_socket_) { |
+ return net::ERR_FAILED; |
+ } else if (!socket_.get() || !socket_->IsConnected()) |
return net::ERR_SOCKET_NOT_CONNECTED; |
else |
return socket_->Write(io_buffer, io_buffer_size, callback); |
@@ -181,4 +272,17 @@ void TCPSocket::OnReadComplete(scoped_refptr<net::IOBuffer> io_buffer, |
read_callback_.Reset(); |
} |
+void TCPSocket::OnAccept(int result) { |
+ DCHECK(!accept_callback_.is_null()); |
+ if (result == net::OK) { |
+ DCHECK(accept_socket_.get() != NULL); |
+ |
+ accept_callback_.Run( |
+ result, static_cast<net::TCPClientSocket*>(accept_socket_.release())); |
+ } else { |
+ accept_callback_.Run(result, NULL); |
+ } |
+ accept_callback_.Reset(); |
+} |
+ |
} // namespace extensions |