Add bind(), recvFrom(), sendTo() for UDP socket.


BUG=None
TEST=None

Review URL: http://codereview.chromium.org/10134008

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@133935 0039d316-1c4b-4281-b951-d872f2087c98
parent 589907fa
......@@ -31,6 +31,8 @@ const char kIsFinalEventKey[] = "isFinalEvent";
const char kResultCodeKey[] = "resultCode";
const char kDataKey[] = "data";
const char kAddressKey[] = "address";
const char kPortKey[] = "port";
APIResourceEventNotifier::APIResourceEventNotifier(
ExtensionEventRouter* router,
......@@ -51,7 +53,9 @@ void APIResourceEventNotifier::OnConnectComplete(int result_code) {
}
void APIResourceEventNotifier::OnDataRead(int result_code,
base::ListValue* data) {
base::ListValue* data,
const std::string& address,
int port) {
// Do we have a destination for this event? There will be one if a source id
// was injected by the request handler for the resource's create method in
// schema_generated_bindings.js, which will in turn be the case if the caller
......@@ -65,6 +69,8 @@ void APIResourceEventNotifier::OnDataRead(int result_code,
API_RESOURCE_EVENT_DATA_READ);
event->SetInteger(kResultCodeKey, result_code);
event->Set(kDataKey, data);
event->SetString(kAddressKey, address);
event->SetInteger(kPortKey, port);
DispatchEvent(event);
}
......
......@@ -53,7 +53,10 @@ class APIResourceEventNotifier
virtual void OnConnectComplete(int result_code);
// Takes ownership of data.
virtual void OnDataRead(int result_code, base::ListValue* data);
virtual void OnDataRead(int result_code,
base::ListValue* data,
const std::string& address,
int port);
virtual void OnWriteComplete(int result_code);
......
......@@ -6,6 +6,7 @@
#include "base/bind.h"
#include "chrome/browser/extensions/api/api_resource_event_notifier.h"
#include "net/base/address_list.h"
#include "net/base/io_buffer.h"
#include "net/base/ip_endpoint.h"
#include "net/base/net_errors.h"
......@@ -13,11 +14,8 @@
namespace extensions {
Socket::Socket(const std::string& address, int port,
APIResourceEventNotifier* event_notifier)
Socket::Socket(APIResourceEventNotifier* event_notifier)
: APIResource(APIResource::SocketResource, event_notifier),
address_(address),
port_(port),
is_connected_(false) {
}
......@@ -26,7 +24,9 @@ Socket::~Socket() {
DCHECK(!is_connected_);
}
void Socket::OnDataRead(scoped_refptr<net::IOBuffer> io_buffer, int result) {
void Socket::OnDataRead(scoped_refptr<net::IOBuffer> io_buffer,
net::IPEndPoint* address,
int result) {
// OnDataRead will take ownership of data_value.
ListValue* data_value = new ListValue();
if (result >= 0) {
......@@ -36,24 +36,59 @@ void Socket::OnDataRead(scoped_refptr<net::IOBuffer> io_buffer, int result) {
data_value->Set(i, Value::CreateIntegerValue(io_buffer_start[i]));
}
}
event_notifier()->OnDataRead(result, data_value);
std::string ip_address_str;
int port = 0;
if (address)
IPEndPointToStringAndPort(*address, &ip_address_str, &port);
event_notifier()->OnDataRead(result, data_value, ip_address_str, port);
}
void Socket::OnWriteComplete(int result) {
event_notifier()->OnWriteComplete(result);
}
int Socket::Read(scoped_refptr<net::IOBuffer> io_buffer, int io_buffer_len) {
return socket()->Read(
io_buffer.get(),
io_buffer_len,
base::Bind(&Socket::OnDataRead, base::Unretained(this), io_buffer));
// static
bool Socket::StringAndPortToIPEndPoint(const std::string& ip_address_str,
int port,
net::IPEndPoint* ip_end_point) {
DCHECK(ip_end_point);
net::IPAddressNumber ip_number;
if (!net::ParseIPLiteralToNumber(ip_address_str, &ip_number))
return false;
*ip_end_point = net::IPEndPoint(ip_number, port);
return true;
}
bool Socket::StringAndPortToAddressList(const std::string& ip_address_str,
int port,
net::AddressList* address_list) {
DCHECK(address_list);
net::IPAddressNumber ip_number;
if (!net::ParseIPLiteralToNumber(ip_address_str, &ip_number))
return false;
*address_list = net::AddressList::CreateFromIPAddress(ip_number, port);
return true;
}
int Socket::Write(scoped_refptr<net::IOBuffer> io_buffer, int byte_count) {
return socket()->Write(
io_buffer.get(), byte_count,
base::Bind(&Socket::OnWriteComplete, base::Unretained(this)));
void Socket::IPEndPointToStringAndPort(const net::IPEndPoint& address,
std::string* ip_address_str,
int* port) {
DCHECK(ip_address_str);
DCHECK(port);
struct sockaddr_storage addr;
size_t addr_len = sizeof(addr);
if (address.ToSockAddr(reinterpret_cast<struct sockaddr*>(&addr),
&addr_len)) {
*ip_address_str = net::NetAddressToString(
reinterpret_cast<struct sockaddr*>(&addr), addr_len);
*port = address.port();
} else {
*ip_address_str = "";
*port = 0;
}
}
} // namespace extensions
......@@ -13,6 +13,8 @@
#include "net/base/io_buffer.h"
namespace net {
class AddressList;
class IPEndPoint;
class Socket;
}
......@@ -24,31 +26,50 @@ class Socket : public APIResource {
public:
virtual ~Socket();
// Returns true iff the socket was able to properly initialize itself.
virtual bool IsValid() = 0;
// Returns net::OK if successful, or an error code otherwise.
virtual int Connect() = 0;
virtual int Connect(const std::string& address, int port) = 0;
virtual void Disconnect() = 0;
virtual int Bind(const std::string& address, int port) = 0;
// Returns the number of bytes read into the buffer, or a negative number if
// an error occurred.
virtual int Read(scoped_refptr<net::IOBuffer> io_buffer, int io_buffer_size);
virtual int Read(scoped_refptr<net::IOBuffer> io_buffer,
int io_buffer_size) = 0;
// Returns the number of bytes successfully written, or a negative error
// code. Note that ERR_IO_PENDING means that the operation blocked, in which
// case |event_notifier| (supplied at socket creation) will eventually be
// called with the final result (again, either a nonnegative number of bytes
// written, or a negative error).
virtual int Write(scoped_refptr<net::IOBuffer> io_buffer, int bytes);
virtual int Write(scoped_refptr<net::IOBuffer> io_buffer,
int byte_count) = 0;
virtual void OnDataRead(scoped_refptr<net::IOBuffer> io_buffer, int result);
virtual int RecvFrom(scoped_refptr<net::IOBuffer> io_buffer,
int io_buffer_size,
net::IPEndPoint *address) = 0;
virtual int SendTo(scoped_refptr<net::IOBuffer> io_buffer,
int byte_count,
const std::string& address,
int port) = 0;
virtual void OnDataRead(scoped_refptr<net::IOBuffer> io_buffer,
net::IPEndPoint *address,
int result);
virtual void OnWriteComplete(int result);
static bool StringAndPortToAddressList(const std::string& ip_address_str,
int port,
net::AddressList* address_list);
static bool StringAndPortToIPEndPoint(const std::string& ip_address_str,
int port,
net::IPEndPoint* ip_end_point);
static void IPEndPointToStringAndPort(const net::IPEndPoint& address,
std::string* ip_address_str,
int* port);
protected:
Socket(const std::string& address, int port,
APIResourceEventNotifier* event_notifier);
virtual net::Socket* socket() = 0;
explicit Socket(APIResourceEventNotifier* event_notifier);
const std::string address_;
int port_;
......
......@@ -11,9 +11,12 @@
#include "chrome/browser/extensions/api/socket/udp_socket.h"
#include "chrome/browser/extensions/extension_service.h"
#include "net/base/io_buffer.h"
#include "net/base/ip_endpoint.h"
namespace extensions {
const char kAddressKey[] = "address";
const char kPortKey[] = "port";
const char kBytesWrittenKey[] = "bytesWritten";
const char kDataKey[] = "data";
const char kResultCodeKey[] = "resultCode";
......@@ -29,14 +32,7 @@ SocketCreateFunction::SocketCreateFunction()
bool SocketCreateFunction::Prepare() {
std::string socket_type_string;
size_t argument_position = 0;
EXTENSION_FUNCTION_VALIDATE(args_->GetString(argument_position++,
&socket_type_string));
EXTENSION_FUNCTION_VALIDATE(args_->GetString(argument_position++,
&address_));
EXTENSION_FUNCTION_VALIDATE(args_->GetInteger(argument_position++,
&port_));
EXTENSION_FUNCTION_VALIDATE(args_->GetString(0, &socket_type_string));
if (socket_type_string == kTCPOption)
socket_type_ = kSocketTypeTCP;
else if (socket_type_string == kUDPOption)
......@@ -44,7 +40,7 @@ bool SocketCreateFunction::Prepare() {
else
return false;
src_id_ = ExtractSrcId(argument_position);
src_id_ = ExtractSrcId(1);
event_notifier_ = CreateEventNotifier(src_id_);
return true;
......@@ -53,12 +49,11 @@ bool SocketCreateFunction::Prepare() {
void SocketCreateFunction::Work() {
Socket* socket = NULL;
if (socket_type_ == kSocketTypeTCP) {
socket = new TCPSocket(address_, port_, event_notifier_);
socket = new TCPSocket(event_notifier_);
} else {
socket = new UDPSocket(address_, port_, event_notifier_);
socket = new UDPSocket(event_notifier_);
}
DCHECK(socket);
DCHECK(socket->IsValid());
DictionaryValue* result = new DictionaryValue();
......@@ -85,6 +80,8 @@ bool SocketDestroyFunction::Respond() {
bool SocketConnectFunction::Prepare() {
EXTENSION_FUNCTION_VALIDATE(args_->GetInteger(0, &socket_id_));
EXTENSION_FUNCTION_VALIDATE(args_->GetString(1, &address_));
EXTENSION_FUNCTION_VALIDATE(args_->GetInteger(2, &port_));
return true;
}
......@@ -92,7 +89,7 @@ void SocketConnectFunction::Work() {
int result = -1;
Socket* socket = controller()->GetSocket(socket_id_);
if (socket)
result = socket->Connect();
result = socket->Connect(address_, port_);
else
error_ = kSocketNotFoundError;
result_.reset(Value::CreateIntegerValue(result));
......@@ -120,6 +117,29 @@ bool SocketDisconnectFunction::Respond() {
return true;
}
bool SocketBindFunction::Prepare() {
EXTENSION_FUNCTION_VALIDATE(args_->GetInteger(0, &socket_id_));
EXTENSION_FUNCTION_VALIDATE(args_->GetString(1, &address_));
EXTENSION_FUNCTION_VALIDATE(args_->GetInteger(2, &port_));
return true;
}
void SocketBindFunction::Work() {
int result = -1;
Socket* socket = controller()->GetSocket(socket_id_);
if (socket)
result = socket->Bind(address_, port_);
else
error_ = kSocketNotFoundError;
result_.reset(Value::CreateIntegerValue(result));
}
bool SocketBindFunction::Respond() {
return true;
}
bool SocketReadFunction::Prepare() {
EXTENSION_FUNCTION_VALIDATE(args_->GetInteger(0, &socket_id_));
return true;
......@@ -204,4 +224,99 @@ bool SocketWriteFunction::Respond() {
return true;
}
bool SocketRecvFromFunction::Prepare() {
EXTENSION_FUNCTION_VALIDATE(args_->GetInteger(0, &socket_id_));
return true;
}
void SocketRecvFromFunction::Work() {
// TODO(miket): this is an arbitrary number. Can we come up with one that
// makes sense?
const int buffer_len = 2048;
scoped_refptr<net::IOBuffer> io_buffer(new net::IOBuffer(buffer_len));
Socket* socket = controller()->GetSocket(socket_id_);
int bytes_read = -1;
std::string ip_address_str;
int port = 0;
if (socket) {
bytes_read = socket->RecvFrom(io_buffer, buffer_len, &address_);
}
// TODO(miket): the buffer-to-array functionality appears twice, once here
// and once in socket.cc. When serial etc. is converted over, it'll appear
// there, too. What's a good single place for it to live? Keep in mind that
// this is short-term code, to be replaced with ArrayBuffer code.
DictionaryValue* result = new DictionaryValue();
ListValue* data_value = new ListValue();
if (bytes_read > 0) {
Socket::IPEndPointToStringAndPort(address_, &ip_address_str, &port);
size_t bytes_size = static_cast<size_t>(bytes_read);
const char* io_buffer_start = io_buffer->data();
for (size_t i = 0; i < bytes_size; ++i) {
data_value->Set(i, Value::CreateIntegerValue(io_buffer_start[i]));
}
}
result->SetInteger(kResultCodeKey, bytes_read);
result->Set(kDataKey, data_value);
result->SetString(kAddressKey, ip_address_str);
result->SetInteger(kPortKey, port);
result_.reset(result);
}
bool SocketRecvFromFunction::Respond() {
return true;
}
SocketSendToFunction::SocketSendToFunction()
: socket_id_(0),
io_buffer_(NULL) {
}
SocketSendToFunction::~SocketSendToFunction() {
}
bool SocketSendToFunction::Prepare() {
EXTENSION_FUNCTION_VALIDATE(args_->GetInteger(0, &socket_id_));
base::ListValue *data_list_value;
EXTENSION_FUNCTION_VALIDATE(args_->GetList(1, &data_list_value));
EXTENSION_FUNCTION_VALIDATE(args_->GetString(2, &address_));
EXTENSION_FUNCTION_VALIDATE(args_->GetInteger(3, &port_));
size_t size = data_list_value->GetSize();
if (size != 0) {
io_buffer_ = new net::IOBufferWithSize(size);
uint8* data_buffer =
reinterpret_cast<uint8*>(io_buffer_->data());
for (size_t i = 0; i < size; ++i) {
int int_value = -1;
data_list_value->GetInteger(i, &int_value);
DCHECK(int_value < 256);
DCHECK(int_value >= 0);
uint8 truncated_int = static_cast<uint8>(int_value);
*data_buffer++ = truncated_int;
}
}
return true;
}
void SocketSendToFunction::Work() {
int bytes_written = -1;
Socket* socket = controller()->GetSocket(socket_id_);
if (socket) {
bytes_written = socket->SendTo(io_buffer_, io_buffer_->size(), address_,
port_);
} else {
error_ = kSocketNotFoundError;
}
DictionaryValue* result = new DictionaryValue();
result->SetInteger(kBytesWrittenKey, bytes_written);
result_.reset(result);
}
bool SocketSendToFunction::Respond() {
return true;
}
} // namespace extensions
......@@ -9,6 +9,7 @@
#include "base/memory/ref_counted.h"
#include "chrome/browser/extensions/api/api_function.h"
#include "net/base/io_buffer.h"
#include "net/base/ip_endpoint.h"
#include <string>
......@@ -43,10 +44,8 @@ class SocketCreateFunction : public AsyncIOAPIFunction {
kSocketTypeUDP
};
int src_id_;
SocketType socket_type_;
std::string address_;
int port_;
int src_id_;
APIResourceEventNotifier* event_notifier_;
DECLARE_EXTENSION_FUNCTION_NAME("experimental.socket.create")
......@@ -72,6 +71,8 @@ class SocketConnectFunction : public AsyncIOAPIFunction {
private:
int socket_id_;
std::string address_;
int port_;
DECLARE_EXTENSION_FUNCTION_NAME("experimental.socket.connect")
};
......@@ -88,6 +89,20 @@ class SocketDisconnectFunction : public AsyncIOAPIFunction {
DECLARE_EXTENSION_FUNCTION_NAME("experimental.socket.disconnect")
};
class SocketBindFunction : public AsyncIOAPIFunction {
protected:
virtual bool Prepare() OVERRIDE;
virtual void Work() OVERRIDE;
virtual bool Respond() OVERRIDE;
private:
int socket_id_;
std::string address_;
int port_;
DECLARE_EXTENSION_FUNCTION_NAME("experimental.socket.bind")
};
class SocketReadFunction : public AsyncIOAPIFunction {
protected:
virtual bool Prepare() OVERRIDE;
......@@ -117,6 +132,38 @@ class SocketWriteFunction : public AsyncIOAPIFunction {
DECLARE_EXTENSION_FUNCTION_NAME("experimental.socket.write")
};
class SocketRecvFromFunction : public AsyncIOAPIFunction {
protected:
virtual bool Prepare() OVERRIDE;
virtual void Work() OVERRIDE;
virtual bool Respond() OVERRIDE;
private:
int socket_id_;
net::IPEndPoint address_;
DECLARE_EXTENSION_FUNCTION_NAME("experimental.socket.recvFrom")
};
class SocketSendToFunction : public AsyncIOAPIFunction {
public:
SocketSendToFunction();
virtual ~SocketSendToFunction();
protected:
virtual bool Prepare() OVERRIDE;
virtual void Work() OVERRIDE;
virtual bool Respond() OVERRIDE;
private:
int socket_id_;
scoped_refptr<net::IOBufferWithSize> io_buffer_;
std::string address_;
int port_;
DECLARE_EXTENSION_FUNCTION_NAME("experimental.socket.sendTo")
};
} // namespace extensions
#endif // CHROME_BROWSER_EXTENSIONS_API_SOCKET_SOCKET_API_H_
......@@ -14,32 +14,22 @@
namespace extensions {
TCPSocket::TCPSocket(const std::string& address, int port,
APIResourceEventNotifier* event_notifier)
: Socket(address, port, event_notifier) {
net::IPAddressNumber ip_number;
if (net::ParseIPLiteralToNumber(address, &ip_number)) {
net::AddressList address_list =
net::AddressList::CreateFromIPAddress(ip_number, port);
socket_.reset(new net::TCPClientSocket(address_list, NULL,
net::NetLog::Source()));
}
TCPSocket::TCPSocket(APIResourceEventNotifier* event_notifier)
: Socket(event_notifier) {
}
// For testing.
TCPSocket::TCPSocket(net::TCPClientSocket* tcp_client_socket,
const std::string& address, int port,
APIResourceEventNotifier* event_notifier)
: Socket(address, port, event_notifier),
: Socket(event_notifier),
socket_(tcp_client_socket) {
}
// static
TCPSocket* TCPSocket::CreateSocketForTesting(
net::TCPClientSocket* tcp_client_socket,
const std::string& address, int port,
APIResourceEventNotifier* event_notifier) {
return new TCPSocket(tcp_client_socket, address, port, event_notifier);
return new TCPSocket(tcp_client_socket, event_notifier);
}
TCPSocket::~TCPSocket() {
......@@ -48,18 +38,22 @@ TCPSocket::~TCPSocket() {
}
}
bool TCPSocket::IsValid() {
return socket_ != NULL;
}
int TCPSocket::Connect(const std::string& address, int port) {
if (is_connected_)
return net::ERR_CONNECTION_FAILED;
net::Socket* TCPSocket::socket() {
return socket_.get();
}
net::AddressList address_list;
if (!StringAndPortToAddressList(address, port, &address_list))
return net::ERR_INVALID_ARGUMENT;
socket_.reset(new net::TCPClientSocket(address_list, NULL,
net::NetLog::Source()));
int TCPSocket::Connect() {
int result = socket_->Connect(base::Bind(
&TCPSocket::OnConnect, base::Unretained(this)));
is_connected_ = result == net::OK;
if (result == net::OK) {
is_connected_ = true;
}
return result;
}
......@@ -68,6 +62,38 @@ void TCPSocket::Disconnect() {
socket_->Disconnect();
}
int TCPSocket::Bind(const std::string& address, int port) {
// TODO(penghuang): Supports bind for tcp?
return net::ERR_FAILED;
}
int TCPSocket::Read(scoped_refptr<net::IOBuffer> io_buffer, int io_buffer_len) {
return socket_->Read(
io_buffer.get(),
io_buffer_len,
base::Bind(&Socket::OnDataRead, base::Unretained(this), io_buffer,
static_cast<net::IPEndPoint*>(NULL)));
}
int TCPSocket::Write(scoped_refptr<net::IOBuffer> io_buffer, int byte_count) {
return socket_->Write(
io_buffer.get(), byte_count,
base::Bind(&Socket::OnWriteComplete, base::Unretained(this)));
}
int TCPSocket::RecvFrom(scoped_refptr<net::IOBuffer> io_buffer,
int io_buffer_len,
net::IPEndPoint* address) {
return net::ERR_FAILED;
}
int TCPSocket::SendTo(scoped_refptr<net::IOBuffer> io_buffer,
int byte_count,
const std::string& address,
int port) {
return net::ERR_FAILED;
}
void TCPSocket::OnConnect(int result) {
is_connected_ = result == net::OK;
event_notifier()->OnConnectComplete(result);
......
......@@ -24,28 +24,32 @@ class APIResourceEventNotifier;
class TCPSocket : public Socket {
public:
TCPSocket(const std::string& address, int port,
APIResourceEventNotifier* event_notifier);
explicit TCPSocket(APIResourceEventNotifier* event_notifier);
virtual ~TCPSocket();
virtual bool IsValid() OVERRIDE;
virtual int Connect() OVERRIDE;
virtual int Connect(const std::string& address, int port) OVERRIDE;
virtual void Disconnect() OVERRIDE;
virtual int Bind(const std::string& address, int port) OVERRIDE;
virtual int Read(scoped_refptr<net::IOBuffer> io_buffer,
int io_buffer_size) OVERRIDE;
virtual int Write(scoped_refptr<net::IOBuffer> io_buffer,
int bytes) OVERRIDE;
virtual int RecvFrom(scoped_refptr<net::IOBuffer> io_buffer,
int io_buffer_size,
net::IPEndPoint *address) OVERRIDE;
virtual int SendTo(scoped_refptr<net::IOBuffer> io_buffer,
int byte_count,
const std::string& address,
int port) OVERRIDE;
virtual void OnConnect(int result);
static TCPSocket* CreateSocketForTesting(
net::TCPClientSocket* tcp_client_socket,
const std::string& address, int port,
APIResourceEventNotifier* event_notifier);
protected:
virtual net::Socket* socket() OVERRIDE;
private:
TCPSocket(net::TCPClientSocket* tcp_client_socket,
const std::string& address, int port,
APIResourceEventNotifier* event_notifier);
scoped_ptr<net::TCPClientSocket> socket_;
......
......@@ -52,7 +52,7 @@ TEST(SocketTest, TestTCPSocketRead) {
APIResourceEventNotifier* notifier = new MockAPIResourceEventNotifier();
scoped_ptr<TCPSocket> socket(TCPSocket::CreateSocketForTesting(
tcp_client_socket, "1.2.3.4", 1, notifier));
tcp_client_socket, notifier));
EXPECT_CALL(*tcp_client_socket, Read(_, _, _))
.Times(1);
......@@ -68,7 +68,7 @@ TEST(SocketTest, TestTCPSocketWrite) {
APIResourceEventNotifier* notifier = new MockAPIResourceEventNotifier();
scoped_ptr<TCPSocket> socket(TCPSocket::CreateSocketForTesting(
tcp_client_socket, "1.2.3.4", 1, notifier));
tcp_client_socket, notifier));
EXPECT_CALL(*tcp_client_socket, Write(_, _, _))
.Times(1);
......@@ -84,7 +84,7 @@ TEST(SocketTest, TestTCPSocketBlockedWrite) {
MockAPIResourceEventNotifier* notifier = new MockAPIResourceEventNotifier();
scoped_ptr<TCPSocket> socket(TCPSocket::CreateSocketForTesting(
tcp_client_socket, "1.2.3.4", 1, notifier));
tcp_client_socket, notifier));
net::CompletionCallback callback;
EXPECT_CALL(*tcp_client_socket, Write(_, _, _))
......
......@@ -13,29 +13,12 @@
namespace extensions {
UDPSocket::UDPSocket(const std::string& address, int port,
APIResourceEventNotifier* event_notifier)
: Socket(address, port, event_notifier),
socket_(new net::UDPClientSocket(net::DatagramSocket::DEFAULT_BIND,
net::RandIntCallback(),
NULL,
net::NetLog::Source())) {
}
// For testing.
UDPSocket::UDPSocket(net::DatagramClientSocket* datagram_client_socket,
const std::string& address, int port,
APIResourceEventNotifier* event_notifier)
: Socket(address, port, event_notifier),
socket_(datagram_client_socket) {
}
// static
UDPSocket* UDPSocket::CreateSocketForTesting(
net::DatagramClientSocket* datagram_client_socket,
const std::string& address, int port,
APIResourceEventNotifier* event_notifier) {
return new UDPSocket(datagram_client_socket, address, port, event_notifier);
UDPSocket::UDPSocket(APIResourceEventNotifier* event_notifier)
: Socket(event_notifier),
socket_(new net::UDPSocket(net::DatagramSocket::DEFAULT_BIND,
net::RandIntCallback(),
NULL,
net::NetLog::Source())) {
}
UDPSocket::~UDPSocket() {
......@@ -44,21 +27,25 @@ UDPSocket::~UDPSocket() {
}
}
bool UDPSocket::IsValid() {
return socket_ != NULL;
}
int UDPSocket::Connect(const std::string& address, int port) {
if (is_connected_)
return net::ERR_CONNECTION_FAILED;
net::Socket* UDPSocket::socket() {
return socket_.get();
net::IPEndPoint ip_end_point;
if (!StringAndPortToIPEndPoint(address, port, &ip_end_point))
return net::ERR_INVALID_ARGUMENT;
int result = socket_->Connect(ip_end_point);
is_connected_ = (result == net::OK);
return result;
}
int UDPSocket::Connect() {
net::IPAddressNumber ip_number;
if (!net::ParseIPLiteralToNumber(address_, &ip_number))
int UDPSocket::Bind(const std::string& address, int port) {
net::IPEndPoint ip_end_point;
if (!StringAndPortToIPEndPoint(address, port, &ip_end_point))
return net::ERR_INVALID_ARGUMENT;
int result = socket_->Connect(net::IPEndPoint(ip_number, port_));
is_connected_ = result == net::OK;
return result;
return socket_->Bind(ip_end_point);
}
void UDPSocket::Disconnect() {
......@@ -66,4 +53,43 @@ void UDPSocket::Disconnect() {
socket_->Close();
}
int UDPSocket::Read(scoped_refptr<net::IOBuffer> io_buffer, int io_buffer_len) {
return socket_->Read(
io_buffer.get(),
io_buffer_len,
base::Bind(&Socket::OnDataRead, base::Unretained(this), io_buffer,
static_cast<net::IPEndPoint*>(NULL)));
}
int UDPSocket::Write(scoped_refptr<net::IOBuffer> io_buffer, int byte_count) {
return socket_->Write(
io_buffer.get(), byte_count,
base::Bind(&Socket::OnWriteComplete, base::Unretained(this)));
}
int UDPSocket::RecvFrom(scoped_refptr<net::IOBuffer> io_buffer,
int io_buffer_len,
net::IPEndPoint* address) {
return socket_->RecvFrom(
io_buffer.get(),
io_buffer_len,
address,
base::Bind(&Socket::OnDataRead, base::Unretained(this), io_buffer,
address));
}
int UDPSocket::SendTo(scoped_refptr<net::IOBuffer> io_buffer,
int byte_count,
const std::string& address,
int port) {
net::IPEndPoint ip_end_point;
if (!StringAndPortToIPEndPoint(address, port, &ip_end_point))
return net::ERR_INVALID_ARGUMENT;
return socket_->SendTo(
io_buffer.get(),
byte_count,
ip_end_point,
base::Bind(&Socket::OnWriteComplete, base::Unretained(this)));
}
} // namespace extensions
......@@ -9,7 +9,7 @@
#include <string>
#include "chrome/browser/extensions/api/socket/socket.h"
#include "net/udp/datagram_client_socket.h"
#include "net/udp/udp_socket.h"
namespace net {
class Socket;
......@@ -21,30 +21,26 @@ class APIResourceEventNotifier;
class UDPSocket : public Socket {
public:
UDPSocket(const std::string& address, int port,
APIResourceEventNotifier* event_notifier);
explicit UDPSocket(APIResourceEventNotifier* event_notifier);
virtual ~UDPSocket();
virtual bool IsValid() OVERRIDE;
virtual int Connect() OVERRIDE;
virtual int Connect(const std::string& address, int port) OVERRIDE;
virtual void Disconnect() OVERRIDE;
static UDPSocket* CreateSocketForTesting(
net::DatagramClientSocket* datagram_client_socket,
const std::string& address, int port,
APIResourceEventNotifier* event_notifier);
protected:
virtual net::Socket* socket() OVERRIDE;
virtual int Bind(const std::string& address, int port) OVERRIDE;
virtual int Read(scoped_refptr<net::IOBuffer> io_buffer,
int io_buffer_size) OVERRIDE;
virtual int Write(scoped_refptr<net::IOBuffer> io_buffer,
int bytes) OVERRIDE;
virtual int RecvFrom(scoped_refptr<net::IOBuffer> io_buffer,
int io_buffer_size,
net::IPEndPoint* address) OVERRIDE;
virtual int SendTo(scoped_refptr<net::IOBuffer> io_buffer,
int byte_count,
const std::string& address,
int port) OVERRIDE;
private:
// Special constructor for testing.
UDPSocket(net::DatagramClientSocket* datagram_client_socket,
const std::string& address, int port,
APIResourceEventNotifier* event_notifier);
scoped_ptr<net::DatagramClientSocket> socket_;
scoped_ptr<net::UDPSocket> socket_;
};
} // namespace extensions
......
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/extensions/api/socket/udp_socket.h"
#include "base/memory/scoped_ptr.h"
#include "chrome/browser/extensions/api/api_resource_event_notifier.h"
#include "net/base/completion_callback.h"
#include "net/base/io_buffer.h"
#include "net/base/net_errors.h"
#include "net/base/rand_callback.h"
#include "net/udp/udp_client_socket.h"
#include "testing/gmock/include/gmock/gmock.h"
using testing::_;
using testing::DoAll;
using testing::Return;
using testing::SaveArg;
namespace extensions {
class MockUDPSocket : public net::UDPClientSocket {
public:
MockUDPSocket()
: net::UDPClientSocket(net::DatagramSocket::DEFAULT_BIND,
net::RandIntCallback(),
NULL,
net::NetLog::Source()) {}
MOCK_METHOD3(Read, int(net::IOBuffer* buf, int buf_len,
const net::CompletionCallback& callback));
MOCK_METHOD3(Write, int(net::IOBuffer* buf, int buf_len,
const net::CompletionCallback& callback));
private:
DISALLOW_COPY_AND_ASSIGN(MockUDPSocket);
};
class MockAPIResourceEventNotifier : public APIResourceEventNotifier {
public:
MockAPIResourceEventNotifier() : APIResourceEventNotifier(NULL, NULL,
std::string(),
0, GURL()) {}
MOCK_METHOD2(OnReadComplete, void(int result_code,
const std::string& message));
MOCK_METHOD1(OnWriteComplete, void(int result_code));
};
TEST(SocketTest, TestUDPSocketRead) {
MockUDPSocket* udp_client_socket = new MockUDPSocket();
APIResourceEventNotifier* notifier = new MockAPIResourceEventNotifier();
scoped_ptr<UDPSocket> socket(UDPSocket::CreateSocketForTesting(
udp_client_socket, "1.2.3.4", 1, notifier));
EXPECT_CALL(*udp_client_socket, Read(_, _, _))
.Times(1);
scoped_refptr<net::IOBufferWithSize> io_buffer(
new net::IOBufferWithSize(512));
socket->Read(io_buffer.get(), io_buffer->size());
}
TEST(SocketTest, TestUDPSocketWrite) {
MockUDPSocket* udp_client_socket = new MockUDPSocket();
APIResourceEventNotifier* notifier = new MockAPIResourceEventNotifier();
scoped_ptr<UDPSocket> socket(UDPSocket::CreateSocketForTesting(
udp_client_socket, "1.2.3.4", 1, notifier));
EXPECT_CALL(*udp_client_socket, Write(_, _, _))
.Times(1);
scoped_refptr<net::IOBufferWithSize> io_buffer(
new net::IOBufferWithSize(512));
socket->Write(io_buffer.get(), io_buffer->size());
}
TEST(SocketTest, TestUDPSocketBlockedWrite) {
MockUDPSocket* udp_client_socket = new MockUDPSocket();
MockAPIResourceEventNotifier* notifier = new MockAPIResourceEventNotifier();
scoped_ptr<UDPSocket> socket(UDPSocket::CreateSocketForTesting(
udp_client_socket, "1.2.3.4", 1, notifier));
net::CompletionCallback callback;
EXPECT_CALL(*udp_client_socket, Write(_, _, _))
.Times(1)
.WillOnce(testing::DoAll(SaveArg<2>(&callback),
Return(net::ERR_IO_PENDING)));
scoped_refptr<net::IOBufferWithSize> io_buffer(new net::IOBufferWithSize(1));
ASSERT_EQ(net::ERR_IO_PENDING, socket->Write(io_buffer.get(),
io_buffer->size()));
// Good. Original call came back unable to complete. Now pretend the socket
// finished, and confirm that we passed the error back.
EXPECT_CALL(*notifier, OnWriteComplete(42))
.Times(1);
callback.Run(42);
}
} // namespace extensions
......@@ -1315,7 +1315,6 @@
'browser/extensions/api/proxy/proxy_api_helpers_unittest.cc',
'browser/extensions/api/serial/serial_connection_unittest.cc',
'browser/extensions/api/socket/tcp_socket_unittest.cc',
'browser/extensions/api/socket/udp_socket_unittest.cc',
'browser/extensions/api/web_navigation/web_navigation_unittest.cc',
'browser/extensions/api/web_request/web_request_api_unittest.cc',
'browser/extensions/api/web_request/web_request_time_tracker_unittest.cc',
......
......@@ -52,6 +52,8 @@
callback ConnectCallback = void (long result);
callback BindCallback = void (long result);
dictionary ReadInfo {
// The resultCode returned from the underlying read() call.
long resultCode;
......@@ -71,18 +73,27 @@
callback WriteCallback = void (WriteInfo writeInfo);
dictionary RecvFromInfo {
// The data received. Warning: will probably become a blob or other
// appropriate binary-friendly type.
// TODO(miket): [instanceOf=ArrayBuffer]object data;
long[] data;
DOMString address;
long port;
};
callback RecvFromCallback = void (RecvFromInfo recvFromInfo);
callback SendToCallback = void (long bytesWritten);
interface Functions {
// Creates a socket of the specified type that will connect to the specified
// remote machine.
// |type| : The type of socket to create. Must be <code>tcp</code> or
// <code>udp</code>.
// |address| : The address of the remote machine.
// |port| : The port of the remote machine.
// |options| : The socket options.
// |callback| : Called when the socket has been created.
static void create(DOMString type,
DOMString address,
long port,
optional CreateOptions options,
CreateCallback callback);
......@@ -90,13 +101,27 @@
// |socketId| : The socketId.
static void destroy(long socketId);
// Connects the socket to the remote machine. For UDP sockets,
// <code>connect</code> is a non-operation but is safe to call.
// Connects the socket to the remote machine.
// |socketId| : The socketId.
// |address| : The address of the remote machine.
// |port| : The port of the remote machine.
// |callback| : Called when the connection attempt is complete.
static void connect(long socketId,
DOMString address,
long port,
ConnectCallback callback);
// Binds the local address for UDP socket. Currently, it does not support
// TCP socket.
// |socketId| : The socketId.
// |address| : The address of the remote machine.
// |port| : The port of the remote machine.
// |callback| : Called when the connection attempt is complete.
static void bind(long socketId,
DOMString address,
long port,
BindCallback callback);
// Disconnects the socket. For UDP sockets, <code>disconnect</code> is a
// non-operation but is safe to call.
// |socketId| : The socketId.
......@@ -121,6 +146,29 @@
static void write(long socketId,
long[] data,
WriteCallback callback);
// Reads data from the given socket.
// |socketId| : The socketId.
// |callback| : Delivers data that was available to be read without
// blocking.
static void recvFrom(long socketId,
RecvFromCallback callback);
// Writes data on the given socket.
// |socketId| : The socketId.
// |data| : The data to write. Warning: will probably become a blob or other
// appropriate binary-friendly type.
// |address| : The address of the remote machine.
// |port| : The port of the remote machine.
// |callback| : Called when the first of any of the following happens: the
// write operation completes without blocking, the write operation blocked
// before completion (in which case onEvent() will eventually be called with
// a <code>writeComplete</code> event), or an error occurred.
static void sendTo(long socketId,
long[] data,
DOMString address,
long port,
SendToCallback callback);
};
interface Events {
......
......@@ -16,10 +16,10 @@
apiFunctions.setHandleRequest('create', function() {
var args = arguments;
if (args.length > 3 && args[3] && args[3].onEvent) {
if (args.length > 1 && args[1] && args[1].onEvent) {
var id = GetNextSocketEventId();
args[3].srcId = id;
chromeHidden.socket.handlers[id] = args[3].onEvent;
args[1].srcId = id;
chromeHidden.socket.handlers[id] = args[1].onEvent;
// Keep the page alive until the event finishes.
// Balanced in eventHandler.
......
......@@ -72,7 +72,7 @@ var testSocketCreation = function() {
chrome.test.succeed();
}
socket.create(protocol, address, port, {onEvent: function(e) {}}, onCreate);
socket.create(protocol, {onEvent: function(e) {}}, onCreate);
};
function onDataRead(readInfo) {
......@@ -91,19 +91,26 @@ function onDataRead(readInfo) {
// Blocked. Wait for onEvent.
}
function onWriteComplete(writeInfo) {
function onWriteOrSendToComplete(writeInfo) {
bytesWritten += writeInfo.bytesWritten;
if (bytesWritten == request.length) {
socket.read(socketId, onDataRead);
if (protocol == "tcp")
socket.read(socketId, onDataRead);
else
socket.recvFrom(socketId, onDataRead);
}
// Blocked. Wait for onEvent.
}
function onConnectComplete(connectResult) {
function onConnectOrBindComplete(connectResult) {
if (connectResult == 0) {
string2ArrayBuffer(request, function(arrayBuffer) {
var longs = arrayBufferToArrayOfLongs(arrayBuffer);
socket.write(socketId, longs, onWriteComplete);
if (protocol == "tcp")
socket.write(socketId, longs, onWriteOrSendToComplete);
else
socket.sendTo(socketId, longs, address, port,
onWriteOrSendToComplete);
});
}
// Blocked. Wait for onEvent.
......@@ -112,16 +119,19 @@ function onConnectComplete(connectResult) {
function onCreate(socketInfo) {
socketId = socketInfo.socketId;
chrome.test.assertTrue(socketId > 0, "failed to create socket");
socket.connect(socketId, onConnectComplete);
if (protocol == "tcp")
socket.connect(socketId, address, port, onConnectOrBindComplete);
else
socket.bind(socketId, "0.0.0.0", 0, onConnectOrBindComplete);
}
function onEvent(socketEvent) {
if (socketEvent.type == "connectComplete") {
onConnectComplete(socketEvent.resultCode);
onConnectOrBindComplete(socketEvent.resultCode);
} else if (socketEvent.type == "dataRead") {
onDataRead({resultCode: socketEvent.resultCode, data: socketEvent.data});
} else if (socketEvent.type == "writeComplete") {
onWriteComplete(socketEvent.resultCode);
onWriteOnSendToComplete(socketEvent.resultCode);
} else {
console.log("Received unhandled socketEvent of type " + socketEvent.type);
}
......@@ -143,7 +153,7 @@ var testSending = function() {
waitCount = 0;
setTimeout(waitForBlockingOperation, 1000);
socket.create(protocol, address, port, { onEvent: onEvent }, onCreate);
socket.create(protocol, {onEvent: onEvent}, onCreate);
};
var onMessageReply = function(message) {
......
// Copyright (c) 2011 The Chromium Authors. All rights reserved.
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
......@@ -12,6 +12,7 @@
#include "base/threading/non_thread_safe.h"
#include "net/base/address_list_net_log_param.h"
#include "net/base/completion_callback.h"
#include "net/base/net_export.h"
#include "net/base/rand_callback.h"
#include "net/base/io_buffer.h"
#include "net/base/ip_endpoint.h"
......@@ -20,7 +21,7 @@
namespace net {
class UDPSocketLibevent : public base::NonThreadSafe {
class NET_EXPORT UDPSocketLibevent : public base::NonThreadSafe {
public:
UDPSocketLibevent(DatagramSocket::BindType bind_type,
const RandIntCallback& rand_int_cb,
......
// Copyright (c) 2011 The Chromium Authors. All rights reserved.
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
......@@ -13,6 +13,7 @@
#include "base/threading/non_thread_safe.h"
#include "base/win/object_watcher.h"
#include "net/base/completion_callback.h"
#include "net/base/net_export.h"
#include "net/base/rand_callback.h"
#include "net/base/ip_endpoint.h"
#include "net/base/io_buffer.h"
......@@ -21,7 +22,7 @@
namespace net {
class UDPSocketWin : public base::NonThreadSafe {
class NET_EXPORT UDPSocketWin : NON_EXPORTED_BASE(public base::NonThreadSafe) {
public:
UDPSocketWin(DatagramSocket::BindType bind_type,
const RandIntCallback& rand_int_cb,
......
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