Chromium Code Reviews| 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 ecda98ba687d03cb8f1bc70347f2476fb4845063..e627a1697dac48a6d8c7f05ebb8d7848e8fdf755 100644 |
| --- a/chrome/browser/extensions/api/socket/tcp_socket.cc |
| +++ b/chrome/browser/extensions/api/socket/tcp_socket.cc |
| @@ -14,17 +14,32 @@ |
| namespace extensions { |
| +const char kTCPSocketTypeInvalidError[] = |
| + "Cannot call both connect and listen on the same socket."; |
| +const char kSocketListenError[] = "Could not listen on the specified port."; |
| +const char kSocketRebindError[] = "Rebinding a listening socket not supported"; |
| + |
| TCPSocket::TCPSocket(const std::string& owner_extension_id, |
| ApiResourceEventNotifier* event_notifier) |
| - : Socket(owner_extension_id, event_notifier) { |
| + : Socket(owner_extension_id, event_notifier), |
| + socket_mode_(UNKNOWN) { |
| } |
| -// For testing. |
| TCPSocket::TCPSocket(net::TCPClientSocket* tcp_client_socket, |
| const std::string& owner_extension_id, |
| - ApiResourceEventNotifier* event_notifier) |
| + ApiResourceEventNotifier* event_notifier, |
| + bool is_connected) |
| : Socket(owner_extension_id, event_notifier), |
| - socket_(tcp_client_socket) { |
| + socket_(tcp_client_socket), |
| + socket_mode_(CLIENT) { |
| + this->is_connected_ = is_connected; |
| +} |
| + |
| +TCPSocket::TCPSocket(net::TCPServerSocket* tcp_server_socket, |
| + const std::string& owner_extension_id) |
| + : Socket(owner_extension_id, NULL), |
| + server_socket_(tcp_server_socket), |
| + socket_mode_(SERVER) { |
| } |
| // static |
| @@ -35,10 +50,18 @@ TCPSocket* TCPSocket::CreateSocketForTesting( |
| return new TCPSocket(tcp_client_socket, owner_extension_id, event_notifier); |
| } |
| +// static |
| +TCPSocket* TCPSocket::CreateServerSocketForTesting( |
| + net::TCPServerSocket* tcp_server_socket, |
| + const std::string& owner_extension_id) { |
| + return new TCPSocket(tcp_server_socket, owner_extension_id); |
| +} |
| + |
| TCPSocket::~TCPSocket() { |
| if (is_connected_) { |
| Disconnect(); |
| } |
| + server_socket_.reset(NULL); |
| } |
| void TCPSocket::Connect(const std::string& address, |
| @@ -46,10 +69,12 @@ void TCPSocket::Connect(const std::string& address, |
| const CompletionCallback& callback) { |
| DCHECK(!callback.is_null()); |
| - if (!connect_callback_.is_null()) { |
| + if (socket_mode_ == SERVER || !connect_callback_.is_null()) { |
| callback.Run(net::ERR_CONNECTION_FAILED); |
| return; |
| } |
| + DCHECK(server_socket_.get() == NULL); |
|
Peng
2012/09/12 19:49:49
Done?
justinlin
2012/09/13 08:21:12
Done. Clicked this first one prematurely.
|
| + socket_mode_ = CLIENT; |
| connect_callback_ = callback; |
| int result = net::ERR_CONNECTION_FAILED; |
| @@ -65,6 +90,15 @@ void TCPSocket::Connect(const std::string& address, |
| socket_.reset(new net::TCPClientSocket(address_list, NULL, |
| net::NetLog::Source())); |
| + |
| + if (bind_address_.get()) { |
| + if (socket_->Bind(*bind_address_) != net::OK) { |
| + socket_.reset(); |
| + OnConnectComplete(net::ERR_ADDRESS_IN_USE); |
| + return; |
| + }; |
| + } |
| + |
| connect_callback_ = callback; |
| result = socket_->Connect(base::Bind( |
| &TCPSocket::OnConnectComplete, base::Unretained(this))); |
| @@ -80,14 +114,27 @@ void TCPSocket::Disconnect() { |
| } |
| int TCPSocket::Bind(const std::string& address, int port) { |
| - // TODO(penghuang): Supports bind for tcp? |
| - return net::ERR_FAILED; |
| + if (bind_address_.get()) { |
| + 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 net::OK; |
| } |
| void TCPSocket::Read(int count, |
| const ReadCompletionCallback& callback) { |
| DCHECK(!callback.is_null()); |
| + if (socket_mode_ != CLIENT) { |
| + callback.Run(net::ERR_FAILED, NULL); |
| + return; |
| + } |
| + |
| if (!read_callback_.is_null()) { |
| callback.Run(net::ERR_IO_PENDING, NULL); |
| return; |
| @@ -143,6 +190,58 @@ bool TCPSocket::SetNoDelay(bool no_delay) { |
| return socket_->SetNoDelay(no_delay); |
| } |
| +int TCPSocket::Listen(int backlog, std::string* error_msg) { |
| + if (socket_mode_ == CLIENT) { |
| + *error_msg = kTCPSocketTypeInvalidError; |
| + return net::ERR_NOT_IMPLEMENTED; |
| + } |
| + DCHECK(!socket_.get()); |
| + socket_mode_ = SERVER; |
| + |
| + if (!bind_address_.get()) { |
| + return net::ERR_FAILED; |
| + } |
| + |
| + if (server_socket_.get()) { |
| + *error_msg = kSocketRebindError; |
| + return net::ERR_ADDRESS_IN_USE; |
| + } |
| + server_socket_.reset(new net::TCPServerSocket(NULL, |
| + net::NetLog::Source())); |
| + int result = server_socket_->Listen(*bind_address_, backlog); |
| + if (result) { |
| + *error_msg = kSocketListenError; |
| + } |
| + return result; |
| +} |
| + |
| +void TCPSocket::Accept(const AcceptCompletionCallback &callback) { |
| + if (socket_mode_ != SERVER || !server_socket_.get()) { |
| + callback.Run(net::ERR_FAILED, NULL); |
| + return; |
| + } |
| + |
| + // Limits to only 1 blocked accept call. |
| + 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::GetPeerAddress(net::IPEndPoint* address) { |
| if (!socket_.get()) |
| return false; |
| @@ -150,9 +249,13 @@ bool TCPSocket::GetPeerAddress(net::IPEndPoint* address) { |
| } |
| bool TCPSocket::GetLocalAddress(net::IPEndPoint* address) { |
| - if (!socket_.get()) |
| + if (socket_.get()) { |
| + return !socket_->GetLocalAddress(address); |
| + } else if (server_socket_.get()) { |
| + return !server_socket_->GetLocalAddress(address); |
| + } else { |
| return false; |
| - return !socket_->GetLocalAddress(address); |
| + } |
| } |
| Socket::SocketType TCPSocket::GetSocketType() const { |
| @@ -162,7 +265,9 @@ Socket::SocketType TCPSocket::GetSocketType() const { |
| int TCPSocket::WriteImpl(net::IOBuffer* io_buffer, |
| int io_buffer_size, |
| const net::CompletionCallback& callback) { |
| - if (!socket_.get() || !socket_->IsConnected()) |
| + if (socket_mode_ != CLIENT) |
| + 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); |
| @@ -183,4 +288,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 |