Adds socket.getInfo to the socket API

Allows clients to retrieve the state of a specified socket.

BUG=135464
TEST=


Review URL: https://chromiumcodereview.appspot.com/10790137

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@150190 0039d316-1c4b-4281-b951-d872f2087c98
parent 25938dab
...@@ -74,6 +74,10 @@ void Socket::OnWriteComplete(int result) { ...@@ -74,6 +74,10 @@ void Socket::OnWriteComplete(int result) {
WriteData(); WriteData();
} }
bool Socket::IsConnected() {
return is_connected_;
}
bool Socket::SetKeepAlive(bool enable, int delay) { bool Socket::SetKeepAlive(bool enable, int delay) {
return false; return false;
} }
......
...@@ -14,6 +14,7 @@ ...@@ -14,6 +14,7 @@
#include "chrome/browser/extensions/api/api_resource.h" #include "chrome/browser/extensions/api/api_resource.h"
#include "net/base/completion_callback.h" #include "net/base/completion_callback.h"
#include "net/base/io_buffer.h" #include "net/base/io_buffer.h"
#include "net/base/ip_endpoint.h"
namespace net { namespace net {
class AddressList; class AddressList;
...@@ -63,6 +64,12 @@ class Socket : public ApiResource { ...@@ -63,6 +64,12 @@ class Socket : public ApiResource {
virtual bool SetKeepAlive(bool enable, int delay); virtual bool SetKeepAlive(bool enable, int delay);
virtual bool SetNoDelay(bool no_delay); virtual bool SetNoDelay(bool no_delay);
bool IsConnected();
virtual bool IsTCPSocket() = 0;
virtual bool GetPeerAddress(net::IPEndPoint* address) = 0;
virtual bool GetLocalAddress(net::IPEndPoint* address) = 0;
static bool StringAndPortToAddressList(const std::string& ip_address_str, static bool StringAndPortToAddressList(const std::string& ip_address_str,
int port, int port,
net::AddressList* address_list); net::AddressList* address_list);
......
...@@ -453,4 +453,48 @@ void SocketSetNoDelayFunction::Work() { ...@@ -453,4 +453,48 @@ void SocketSetNoDelayFunction::Work() {
SetResult(Value::CreateBooleanValue(result)); SetResult(Value::CreateBooleanValue(result));
} }
SocketGetInfoFunction::SocketGetInfoFunction()
: params_(NULL) {}
SocketGetInfoFunction::~SocketGetInfoFunction() {}
bool SocketGetInfoFunction::Prepare() {
params_ = api::socket::GetInfo::Params::Create(*args_);
EXTENSION_FUNCTION_VALIDATE(params_.get());
return true;
}
void SocketGetInfoFunction::Work() {
api::socket::SocketInfo info;
Socket* socket = manager_->Get(params_->socket_id);
if (socket) {
// This represents what we know about the socket, and does not call through
// to the system.
info.socket_type = (socket->IsTCPSocket() ? kTCPOption : kUDPOption);
info.connected = socket->IsConnected();
// Grab the peer address as known by the OS. This and the call below will
// always succeed while the socket is connected, even if the socket has
// been remotely closed by the peer; only reading the socket will reveal
// that it should be closed locally.
net::IPEndPoint peerAddress;
if (socket->GetPeerAddress(&peerAddress)) {
info.peer_address.reset(
new std::string(peerAddress.ToStringWithoutPort()));
info.peer_port.reset(new int(peerAddress.port()));
}
// Grab the local address as known by the OS.
net::IPEndPoint localAddress;
if (socket->GetLocalAddress(&localAddress)) {
info.local_address.reset(
new std::string(localAddress.ToStringWithoutPort()));
info.local_port.reset(new int(localAddress.port()));
}
} else {
error_ = kSocketNotFoundError;
}
SetResult(info.ToValue().release());
}
} // namespace extensions } // namespace extensions
...@@ -279,6 +279,23 @@ class SocketSetNoDelayFunction : public SocketAsyncApiFunction { ...@@ -279,6 +279,23 @@ class SocketSetNoDelayFunction : public SocketAsyncApiFunction {
scoped_ptr<api::socket::SetNoDelay::Params> params_; scoped_ptr<api::socket::SetNoDelay::Params> params_;
}; };
class SocketGetInfoFunction : public SocketAsyncApiFunction {
public:
DECLARE_EXTENSION_FUNCTION_NAME("socket.getInfo");
SocketGetInfoFunction();
protected:
virtual ~SocketGetInfoFunction();
// AsyncApiFunction:
virtual bool Prepare() OVERRIDE;
virtual void Work() OVERRIDE;
private:
scoped_ptr<api::socket::GetInfo::Params> params_;
};
} // namespace extensions } // namespace extensions
#endif // CHROME_BROWSER_EXTENSIONS_API_SOCKET_SOCKET_API_H_ #endif // CHROME_BROWSER_EXTENSIONS_API_SOCKET_SOCKET_API_H_
...@@ -140,6 +140,23 @@ bool TCPSocket::SetNoDelay(bool no_delay) { ...@@ -140,6 +140,23 @@ bool TCPSocket::SetNoDelay(bool no_delay) {
return socket_->SetNoDelay(no_delay); return socket_->SetNoDelay(no_delay);
} }
bool TCPSocket::IsTCPSocket() {
return true;
}
bool TCPSocket::GetPeerAddress(net::IPEndPoint* address) {
if (!socket_.get())
return false;
return !socket_->GetPeerAddress(address);
}
bool TCPSocket::GetLocalAddress(net::IPEndPoint* address) {
if (!socket_.get())
return false;
return !socket_->GetLocalAddress(address);
}
int TCPSocket::WriteImpl(net::IOBuffer* io_buffer, int TCPSocket::WriteImpl(net::IOBuffer* io_buffer,
int io_buffer_size, int io_buffer_size,
const net::CompletionCallback& callback) { const net::CompletionCallback& callback) {
......
...@@ -42,6 +42,9 @@ class TCPSocket : public Socket { ...@@ -42,6 +42,9 @@ class TCPSocket : public Socket {
const CompletionCallback& callback) OVERRIDE; const CompletionCallback& callback) OVERRIDE;
virtual bool SetKeepAlive(bool enable, int delay) OVERRIDE; virtual bool SetKeepAlive(bool enable, int delay) OVERRIDE;
virtual bool SetNoDelay(bool no_delay) OVERRIDE; virtual bool SetNoDelay(bool no_delay) OVERRIDE;
virtual bool IsTCPSocket() OVERRIDE;
virtual bool GetPeerAddress(net::IPEndPoint* address) OVERRIDE;
virtual bool GetLocalAddress(net::IPEndPoint* address) OVERRIDE;
static TCPSocket* CreateSocketForTesting( static TCPSocket* CreateSocketForTesting(
net::TCPClientSocket* tcp_client_socket, net::TCPClientSocket* tcp_client_socket,
......
...@@ -185,6 +185,18 @@ void UDPSocket::SendTo(scoped_refptr<net::IOBuffer> io_buffer, ...@@ -185,6 +185,18 @@ void UDPSocket::SendTo(scoped_refptr<net::IOBuffer> io_buffer,
OnSendToComplete(result); OnSendToComplete(result);
} }
bool UDPSocket::IsTCPSocket() {
return false;
}
bool UDPSocket::GetPeerAddress(net::IPEndPoint* address) {
return !socket_.GetPeerAddress(address);
}
bool UDPSocket::GetLocalAddress(net::IPEndPoint* address) {
return !socket_.GetLocalAddress(address);
}
void UDPSocket::OnReadComplete(scoped_refptr<net::IOBuffer> io_buffer, void UDPSocket::OnReadComplete(scoped_refptr<net::IOBuffer> io_buffer,
int result) { int result) {
DCHECK(!read_callback_.is_null()); DCHECK(!read_callback_.is_null());
......
...@@ -33,6 +33,9 @@ class UDPSocket : public Socket { ...@@ -33,6 +33,9 @@ class UDPSocket : public Socket {
const std::string& address, const std::string& address,
int port, int port,
const CompletionCallback& callback) OVERRIDE; const CompletionCallback& callback) OVERRIDE;
virtual bool IsTCPSocket() OVERRIDE;
virtual bool GetPeerAddress(net::IPEndPoint* address) OVERRIDE;
virtual bool GetLocalAddress(net::IPEndPoint* address) OVERRIDE;
protected: protected:
virtual int WriteImpl(net::IOBuffer* io_buffer, virtual int WriteImpl(net::IOBuffer* io_buffer,
......
...@@ -53,6 +53,38 @@ namespace socket { ...@@ -53,6 +53,38 @@ namespace socket {
long port; long port;
}; };
dictionary SocketInfo {
// The type of the passed socket. This will be <code>tcp</code> or
// <code>udp</code>.
SocketType socketType;
// Whether or not the underlying socket is connected.
//
// For <code>tcp</code> sockets, this will remain true even if the remote
// peer has disconnected. Reading or writing to the socket may then result
// in an error, hinting that this socket should be disconnected via
// <code>disconnect()</code>.
//
// For <code>udp</code> sockets, this just represents whether a default
// remote address has been specified for reading and writing packets.
boolean connected;
// If the underlying socket is connected, contains the IPv4/6 address of
// the peer.
DOMString? peerAddress;
// If the underlying socket is connected, contains the port of the
// connected peer.
long? peerPort;
// If the underlying socket is bound or connected, contains its local
// IPv4/6 address.
DOMString? localAddress;
// If the underlying socket is bound or connected, contains its local port.
long? localPort;
};
callback RecvFromCallback = void (RecvFromInfo recvFromInfo); callback RecvFromCallback = void (RecvFromInfo recvFromInfo);
callback SendToCallback = void (WriteInfo writeInfo); callback SendToCallback = void (WriteInfo writeInfo);
...@@ -61,6 +93,8 @@ namespace socket { ...@@ -61,6 +93,8 @@ namespace socket {
callback SetNoDelayCallback = void (boolean result); callback SetNoDelayCallback = void (boolean result);
callback GetInfoCallback = void (SocketInfo result);
interface Functions { interface Functions {
// Creates a socket of the specified type that will connect to the specified // Creates a socket of the specified type that will connect to the specified
// remote machine. // remote machine.
...@@ -76,7 +110,10 @@ namespace socket { ...@@ -76,7 +110,10 @@ namespace socket {
// |socketId| : The socketId. // |socketId| : The socketId.
static void destroy(long socketId); static void destroy(long socketId);
// Connects the socket to the remote machine. // Connects the socket to the remote machine (for a <code>tcp</code>
// socket). For a <code>udp</code> socket, this sets the default address
// which packets are sent to and read from for <code>read()</code>
// and <code>write()</code> calls.
// |socketId| : The socketId. // |socketId| : The socketId.
// |hostname| : The hostname or IP address of the remote machine. // |hostname| : The hostname or IP address of the remote machine.
// |port| : The port of the remote machine. // |port| : The port of the remote machine.
...@@ -141,7 +178,7 @@ namespace socket { ...@@ -141,7 +178,7 @@ namespace socket {
long port, long port,
SendToCallback callback); SendToCallback callback);
// Enable/disable keep-alive functionality for a TCP connection. // Enables or disables the keep-alive functionality for a TCP connection.
// |socketId| : The socketId. // |socketId| : The socketId.
// |enable| : If true, enable keep-alive functionality. // |enable| : If true, enable keep-alive functionality.
// |delay| : Set the delay seconds between the last data packet received // |delay| : Set the delay seconds between the last data packet received
...@@ -152,13 +189,20 @@ namespace socket { ...@@ -152,13 +189,20 @@ namespace socket {
optional long delay, optional long delay,
SetKeepAliveCallback callback); SetKeepAliveCallback callback);
// Enable/disable Nagle algorithm. // Sets or clears <code>TCP_NODELAY</code> for a TCP connection. Nagle's
// algorithm will be disabled when <code>TCP_NODELAY</code> is set.
// |socketId| : The socketId. // |socketId| : The socketId.
// |noDelay| : If true, disable Nagle algorithm. // |noDelay| : If true, disables Nagle's algorithm.
// |callback| : Called when the setNoDelay attempt is complete. // |callback| : Called when the setNoDelay attempt is complete.
static void setNoDelay(long socketId, static void setNoDelay(long socketId,
boolean noDelay, boolean noDelay,
SetNoDelayCallback callback); SetNoDelayCallback callback);
// Retrieves the state of the given socket.
// |socketId| : The socketId.
// |callback| : Called when the state is available.
static void getInfo(long socketId,
GetInfoCallback callback);
}; };
}; };
...@@ -42,8 +42,16 @@ function arrayBuffer2String(buf, callback) { ...@@ -42,8 +42,16 @@ function arrayBuffer2String(buf, callback) {
} }
var testSocketCreation = function() { var testSocketCreation = function() {
function onCreate(socketInfo) { function onGetInfo(info) {
chrome.test.assertTrue(socketInfo.socketId > 0); chrome.test.assertEq(info.socketType, protocol);
chrome.test.assertFalse(info.connected);
if (info.peerAddress || info.peerPort) {
chrome.test.fail('Unconnected socket should not have peer');
}
if (info.localAddress || info.localPort) {
chrome.test.fail('Unconnected socket should not have local binding');
}
// TODO(miket): this doesn't work yet. It's possible this will become // TODO(miket): this doesn't work yet. It's possible this will become
// automatic, but either way we can't forget to clean up. // automatic, but either way we can't forget to clean up.
...@@ -53,9 +61,21 @@ var testSocketCreation = function() { ...@@ -53,9 +61,21 @@ var testSocketCreation = function() {
chrome.test.succeed(); chrome.test.succeed();
} }
function onCreate(socketInfo) {
chrome.test.assertTrue(socketInfo.socketId > 0);
// Obtaining socket information before a connect() call should be safe, but
// return empty values.
socket.getInfo(socketInfo.socketId, onGetInfo);
}
socket.create(protocol, {}, onCreate); socket.create(protocol, {}, onCreate);
}; };
var testGetInfo = function() {
};
function onDataRead(readInfo) { function onDataRead(readInfo) {
if (readInfo.resultCode > 0 || readInfo.data.byteLength > 0) { if (readInfo.resultCode > 0 || readInfo.data.byteLength > 0) {
chrome.test.assertEq(readInfo.resultCode, readInfo.data.byteLength); chrome.test.assertEq(readInfo.resultCode, readInfo.data.byteLength);
...@@ -103,11 +123,37 @@ function onSetNoDelay(result) { ...@@ -103,11 +123,37 @@ function onSetNoDelay(result) {
socket.setKeepAlive(socketId, true, 1000, onSetKeepAlive); socket.setKeepAlive(socketId, true, 1000, onSetKeepAlive);
} }
function onGetInfo(result) {
chrome.test.assertTrue(!!result.localAddress,
"Bound socket should always have local address");
chrome.test.assertTrue(!!result.localPort,
"Bound socket should always have local port");
chrome.test.assertEq(result.socketType, protocol, "Unexpected socketType");
if (protocol == "tcp") {
// NOTE: We're always called with 'localhost', but getInfo will only return
// IPs, not names.
chrome.test.assertEq(result.peerAddress, "127.0.0.1",
"Peer addresss should be the listen server");
chrome.test.assertEq(result.peerPort, port,
"Peer port should be the listen server");
chrome.test.assertTrue(result.connected, "Socket should be connected");
} else {
chrome.test.assertFalse(result.connected, "UDP socket was not connected");
chrome.test.assertTrue(!result.peerAddress,
"Unconnected UDP socket should not have peer address");
chrome.test.assertTrue(!result.peerPort,
"Unconnected UDP socket should not have peer port");
}
socket.setNoDelay(socketId, true, onSetNoDelay);
}
function onConnectOrBindComplete(result) { function onConnectOrBindComplete(result) {
chrome.test.assertEq(0, result, chrome.test.assertEq(0, result,
"Connect or bind failed with error " + result); "Connect or bind failed with error " + result);
if (result == 0) { if (result == 0) {
socket.setNoDelay(socketId, true, onSetNoDelay); socket.getInfo(socketId, onGetInfo);
} }
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment