diff options
author | penghuang@chromium.org <penghuang@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-04-25 17:54:53 +0000 |
---|---|---|
committer | penghuang@chromium.org <penghuang@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-04-25 17:54:53 +0000 |
commit | 398dca8b08d277a02bf94320ee6ecfb507b264ec (patch) | |
tree | 5597cc76c87aeb7be89b7b57278a769bbff93aca | |
parent | 589907faa1f48738d58e1b9bad2c908ea20a58e3 (diff) | |
download | chromium_src-398dca8b08d277a02bf94320ee6ecfb507b264ec.zip chromium_src-398dca8b08d277a02bf94320ee6ecfb507b264ec.tar.gz chromium_src-398dca8b08d277a02bf94320ee6ecfb507b264ec.tar.bz2 |
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
18 files changed, 493 insertions, 259 deletions
diff --git a/chrome/browser/extensions/api/api_resource_event_notifier.cc b/chrome/browser/extensions/api/api_resource_event_notifier.cc index 6b09023..000ace5 100644 --- a/chrome/browser/extensions/api/api_resource_event_notifier.cc +++ b/chrome/browser/extensions/api/api_resource_event_notifier.cc @@ -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); } diff --git a/chrome/browser/extensions/api/api_resource_event_notifier.h b/chrome/browser/extensions/api/api_resource_event_notifier.h index eb16a01..4ef8e2e 100644 --- a/chrome/browser/extensions/api/api_resource_event_notifier.h +++ b/chrome/browser/extensions/api/api_resource_event_notifier.h @@ -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); diff --git a/chrome/browser/extensions/api/socket/socket.cc b/chrome/browser/extensions/api/socket/socket.cc index 60e57b7..37f8bab 100644 --- a/chrome/browser/extensions/api/socket/socket.cc +++ b/chrome/browser/extensions/api/socket/socket.cc @@ -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 diff --git a/chrome/browser/extensions/api/socket/socket.h b/chrome/browser/extensions/api/socket/socket.h index eb9d83e..d91a950 100644 --- a/chrome/browser/extensions/api/socket/socket.h +++ b/chrome/browser/extensions/api/socket/socket.h @@ -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_; diff --git a/chrome/browser/extensions/api/socket/socket_api.cc b/chrome/browser/extensions/api/socket/socket_api.cc index 979f42c..c8409aa 100644 --- a/chrome/browser/extensions/api/socket/socket_api.cc +++ b/chrome/browser/extensions/api/socket/socket_api.cc @@ -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 diff --git a/chrome/browser/extensions/api/socket/socket_api.h b/chrome/browser/extensions/api/socket/socket_api.h index a9efeaa..04f3038 100644 --- a/chrome/browser/extensions/api/socket/socket_api.h +++ b/chrome/browser/extensions/api/socket/socket_api.h @@ -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_ diff --git a/chrome/browser/extensions/api/socket/tcp_socket.cc b/chrome/browser/extensions/api/socket/tcp_socket.cc index 8989fd2..4e211bf 100644 --- a/chrome/browser/extensions/api/socket/tcp_socket.cc +++ b/chrome/browser/extensions/api/socket/tcp_socket.cc @@ -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); diff --git a/chrome/browser/extensions/api/socket/tcp_socket.h b/chrome/browser/extensions/api/socket/tcp_socket.h index 051abf4..55ead13 100644 --- a/chrome/browser/extensions/api/socket/tcp_socket.h +++ b/chrome/browser/extensions/api/socket/tcp_socket.h @@ -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_; diff --git a/chrome/browser/extensions/api/socket/tcp_socket_unittest.cc b/chrome/browser/extensions/api/socket/tcp_socket_unittest.cc index 1573fa5..3efc9c7 100644 --- a/chrome/browser/extensions/api/socket/tcp_socket_unittest.cc +++ b/chrome/browser/extensions/api/socket/tcp_socket_unittest.cc @@ -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(_, _, _)) diff --git a/chrome/browser/extensions/api/socket/udp_socket.cc b/chrome/browser/extensions/api/socket/udp_socket.cc index 467df66..7c72b42 100644 --- a/chrome/browser/extensions/api/socket/udp_socket.cc +++ b/chrome/browser/extensions/api/socket/udp_socket.cc @@ -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 diff --git a/chrome/browser/extensions/api/socket/udp_socket.h b/chrome/browser/extensions/api/socket/udp_socket.h index 4cffa98..c527e56 100644 --- a/chrome/browser/extensions/api/socket/udp_socket.h +++ b/chrome/browser/extensions/api/socket/udp_socket.h @@ -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 diff --git a/chrome/browser/extensions/api/socket/udp_socket_unittest.cc b/chrome/browser/extensions/api/socket/udp_socket_unittest.cc deleted file mode 100644 index b8c50a1..0000000 --- a/chrome/browser/extensions/api/socket/udp_socket_unittest.cc +++ /dev/null @@ -1,104 +0,0 @@ -// 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 diff --git a/chrome/chrome_tests.gypi b/chrome/chrome_tests.gypi index fcb41c1..88e5b39 100644 --- a/chrome/chrome_tests.gypi +++ b/chrome/chrome_tests.gypi @@ -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', diff --git a/chrome/common/extensions/api/experimental.socket.idl b/chrome/common/extensions/api/experimental.socket.idl index 7fdef7f..b42ecf7 100644 --- a/chrome/common/extensions/api/experimental.socket.idl +++ b/chrome/common/extensions/api/experimental.socket.idl @@ -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 { diff --git a/chrome/renderer/resources/extensions/experimental.socket_custom_bindings.js b/chrome/renderer/resources/extensions/experimental.socket_custom_bindings.js index 40011b2..80f016b 100644 --- a/chrome/renderer/resources/extensions/experimental.socket_custom_bindings.js +++ b/chrome/renderer/resources/extensions/experimental.socket_custom_bindings.js @@ -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. diff --git a/chrome/test/data/extensions/api_test/socket/api/background.js b/chrome/test/data/extensions/api_test/socket/api/background.js index 7eb9874..fef3347 100644 --- a/chrome/test/data/extensions/api_test/socket/api/background.js +++ b/chrome/test/data/extensions/api_test/socket/api/background.js @@ -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) { diff --git a/net/udp/udp_socket_libevent.h b/net/udp/udp_socket_libevent.h index 4317740..b6fe51f 100644 --- a/net/udp/udp_socket_libevent.h +++ b/net/udp/udp_socket_libevent.h @@ -1,4 +1,4 @@ -// 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, diff --git a/net/udp/udp_socket_win.h b/net/udp/udp_socket_win.h index d49f64d..e55b1f0 100644 --- a/net/udp/udp_socket_win.h +++ b/net/udp/udp_socket_win.h @@ -1,4 +1,4 @@ -// 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, |