| 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..85bf766854f88f5f86dae105499728ead2c29566 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());
|
| + 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,59 @@ 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()));
|
| + server_socket_->AllowAddressReuse();
|
| + 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 +250,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 +266,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 +289,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
|
|
|