diff options
author | rpaquay@chromium.org <rpaquay@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-08-16 02:05:26 +0000 |
---|---|---|
committer | rpaquay@chromium.org <rpaquay@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-08-16 02:05:26 +0000 |
commit | 64a3996dc779333627f34e4e46be2a23b9539551 (patch) | |
tree | 8e1e0829056207efdea2c70258f33d2d92bc0e2e /chrome/browser/extensions/api/sockets_udp | |
parent | 6a078da35f5fcf238013a837ab33e4a16e00d0a8 (diff) | |
download | chromium_src-64a3996dc779333627f34e4e46be2a23b9539551.zip chromium_src-64a3996dc779333627f34e4e46be2a23b9539551.tar.gz chromium_src-64a3996dc779333627f34e4e46be2a23b9539551.tar.bz2 |
Implement v2 API of udp socket.
(Note this issue is a replacement for issue #19260002 that will not accept additional patch sets due to a corruption bug: https://code.google.com/p/chromium/issues/detail?id=107101).
One notable change vs socket v1 is the use of event handlers for receiving data instead of using callbacks with the "recvFrom" function. This allow server apps to go to "suspend" mode when inactive (see https://docs.google.com/document/d/1qGytoYz6K0xYnOR6oM2tpxC0ET8bbb8XdTFMq4K9jTU/edit?usp=sharing), as well as improve performance (#packets/sec) (see https://docs.google.com/spreadsheet/ccc?key=0Ar6WDZ-sS7b5dEp1ckJGQjZEVGlFN3A1U1BVQUdQb2c&usp=sharing).
Also implement the "close_on_suspend" behavior wrt to lifetime: By default, sockets are closed when the packaged app process dies (unload or suspend). When "close_on_suspend" is 'false', sockets survive "suspend" events, and thus can be re-used across process re-activation. This is useful for background server type apps.
BUG=165273
BUG=173241
Review URL: https://chromiumcodereview.appspot.com/22650003
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@217912 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome/browser/extensions/api/sockets_udp')
6 files changed, 1124 insertions, 0 deletions
diff --git a/chrome/browser/extensions/api/sockets_udp/sockets_udp_api.cc b/chrome/browser/extensions/api/sockets_udp/sockets_udp_api.cc new file mode 100644 index 0000000..c7a6e81 --- /dev/null +++ b/chrome/browser/extensions/api/sockets_udp/sockets_udp_api.cc @@ -0,0 +1,471 @@ +// Copyright 2013 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/sockets_udp/sockets_udp_api.h" + +#include "chrome/browser/extensions/api/socket/udp_socket.h" +#include "chrome/browser/extensions/api/sockets_udp/udp_socket_event_dispatcher.h" +#include "chrome/common/extensions/permissions/permissions_data.h" +#include "chrome/common/extensions/permissions/socket_permission.h" +#include "content/public/common/socket_permission_request.h" +#include "net/base/net_errors.h" + +namespace extensions { +namespace api { + +using content::SocketPermissionRequest; + +const char kSocketNotFoundError[] = "Socket not found"; +const char kPermissionError[] = "App does not have permission"; +const char kWildcardAddress[] = "*"; +const int kWildcardPort = 0; + +UDPSocketAsyncApiFunction::~UDPSocketAsyncApiFunction() {} + +scoped_ptr<SocketResourceManagerInterface> + UDPSocketAsyncApiFunction::CreateSocketResourceManager() { + return scoped_ptr<SocketResourceManagerInterface>( + new SocketResourceManager<ResumableUDPSocket>()).Pass(); +} + +ResumableUDPSocket* UDPSocketAsyncApiFunction::GetUdpSocket(int socket_id) { + return static_cast<ResumableUDPSocket*>(GetSocket(socket_id)); +} + +UDPSocketExtensionWithDnsLookupFunction:: + ~UDPSocketExtensionWithDnsLookupFunction() {} + +scoped_ptr<SocketResourceManagerInterface> + UDPSocketExtensionWithDnsLookupFunction::CreateSocketResourceManager() { + return scoped_ptr<SocketResourceManagerInterface>( + new SocketResourceManager<ResumableUDPSocket>()).Pass(); +} + +ResumableUDPSocket* UDPSocketExtensionWithDnsLookupFunction::GetUdpSocket( + int socket_id) { + return static_cast<ResumableUDPSocket*>(GetSocket(socket_id)); +} + +linked_ptr<sockets_udp::SocketInfo> CreateSocketInfo( + int socket_id, + ResumableUDPSocket* socket) { + linked_ptr<sockets_udp::SocketInfo> socket_info( + new sockets_udp::SocketInfo()); + // This represents what we know about the socket, and does not call through + // to the system. + socket_info->socket_id = socket_id; + if (!socket->name().empty()) { + socket_info->name.reset(new std::string(socket->name())); + } + socket_info->persistent = socket->persistent(); + if (socket->buffer_size() > 0) { + socket_info->buffer_size.reset(new int(socket->buffer_size())); + } + + // Grab the local address as known by the OS. + net::IPEndPoint localAddress; + if (socket->GetLocalAddress(&localAddress)) { + socket_info->local_address.reset( + new std::string(localAddress.ToStringWithoutPort())); + socket_info->local_port.reset(new int(localAddress.port())); + } + + return socket_info; +} + +void SetSocketProperties(ResumableUDPSocket* socket, + sockets_udp::SocketProperties* properties) { + if (properties->name.get()) { + socket->set_name(*properties->name.get()); + } + if (properties->persistent.get()) { + socket->set_persistent(*properties->persistent.get()); + } + if (properties->buffer_size.get()) { + socket->set_buffer_size(*properties->buffer_size.get()); + } +} + +SocketsUdpCreateFunction::SocketsUdpCreateFunction() {} + +SocketsUdpCreateFunction::~SocketsUdpCreateFunction() {} + +bool SocketsUdpCreateFunction::Prepare() { + params_ = sockets_udp::Create::Params::Create(*args_); + EXTENSION_FUNCTION_VALIDATE(params_.get()); + return true; +} + +void SocketsUdpCreateFunction::Work() { + ResumableUDPSocket* socket = new ResumableUDPSocket(extension_->id()); + + sockets_udp::SocketProperties* properties = params_.get()->properties.get(); + if (properties) { + SetSocketProperties(socket, properties); + } + + sockets_udp::CreateInfo create_info; + create_info.socket_id = AddSocket(socket); + results_ = sockets_udp::Create::Results::Create(create_info); +} + +SocketsUdpUpdateFunction::SocketsUdpUpdateFunction() {} + +SocketsUdpUpdateFunction::~SocketsUdpUpdateFunction() {} + +bool SocketsUdpUpdateFunction::Prepare() { + params_ = sockets_udp::Update::Params::Create(*args_); + EXTENSION_FUNCTION_VALIDATE(params_.get()); + return true; +} + +void SocketsUdpUpdateFunction::Work() { + ResumableUDPSocket* socket = GetUdpSocket(params_->socket_id); + if (!socket) { + error_ = kSocketNotFoundError; + return; + } + + SetSocketProperties(socket, ¶ms_.get()->properties); + results_ = sockets_udp::Update::Results::Create(); +} + +SocketsUdpBindFunction::SocketsUdpBindFunction() {} + +SocketsUdpBindFunction::~SocketsUdpBindFunction() {} + +bool SocketsUdpBindFunction::Prepare() { + params_ = sockets_udp::Bind::Params::Create(*args_); + EXTENSION_FUNCTION_VALIDATE(params_.get()); + return true; +} + +void SocketsUdpBindFunction::Work() { + ResumableUDPSocket* socket = GetUdpSocket(params_->socket_id); + if (!socket) { + error_ = kSocketNotFoundError; + return; + } + + SocketPermission::CheckParam param( + SocketPermissionRequest::UDP_BIND, params_->address, params_->port); + if (!PermissionsData::CheckAPIPermissionWithParam( + GetExtension(), + APIPermission::kSocket, + ¶m)) { + error_ = kPermissionError; + return; + } + + int net_result = socket->Bind(params_->address, params_->port); + if (net_result == net::OK) { + UDPSocketEventDispatcher::Get(profile())->OnSocketBind(extension_->id(), + params_->socket_id); + } + + if (net_result != net::OK) + error_ = net::ErrorToString(net_result); + results_ = sockets_udp::Bind::Results::Create(net_result); +} + +SocketsUdpSendFunction::SocketsUdpSendFunction() + : io_buffer_size_(0) {} + +SocketsUdpSendFunction::~SocketsUdpSendFunction() {} + +bool SocketsUdpSendFunction::Prepare() { + params_ = sockets_udp::Send::Params::Create(*args_); + EXTENSION_FUNCTION_VALIDATE(params_.get()); + io_buffer_size_ = params_->data.size(); + io_buffer_ = new net::WrappedIOBuffer(params_->data.data()); + return true; +} + +void SocketsUdpSendFunction::AsyncWorkStart() { + ResumableUDPSocket* socket = GetUdpSocket(params_->socket_id); + if (!socket) { + error_ = kSocketNotFoundError; + AsyncWorkCompleted(); + return; + } + + SocketPermission::CheckParam param( + SocketPermissionRequest::UDP_SEND_TO, + params_->address, + params_->port); + if (!PermissionsData::CheckAPIPermissionWithParam( + GetExtension(), + APIPermission::kSocket, + ¶m)) { + error_ = kPermissionError; + AsyncWorkCompleted(); + return; + } + + StartDnsLookup(params_->address); +} + +void SocketsUdpSendFunction::AfterDnsLookup(int lookup_result) { + if (lookup_result == net::OK) { + StartSendTo(); + } else { + SetSendResult(lookup_result, -1); + } +} + +void SocketsUdpSendFunction::StartSendTo() { + ResumableUDPSocket* socket = GetUdpSocket(params_->socket_id); + if (!socket) { + error_ = kSocketNotFoundError; + AsyncWorkCompleted(); + return; + } + + socket->SendTo(io_buffer_, io_buffer_size_, resolved_address_, params_->port, + base::Bind(&SocketsUdpSendFunction::OnCompleted, this)); +} + +void SocketsUdpSendFunction::OnCompleted(int net_result) { + if (net_result >= net::OK) { + SetSendResult(net::OK, net_result); + } else { + SetSendResult(net_result, -1); + } +} + +void SocketsUdpSendFunction::SetSendResult(int net_result, int bytes_written) { + CHECK(net_result <= net::OK) << "Network status code must be < 0"; + + sockets_udp::SendInfo send_info; + send_info.result = net_result; + if (net_result == net::OK) { + send_info.bytes_written.reset(new int(bytes_written)); + } + + if (net_result != net::OK) + error_ = net::ErrorToString(net_result); + results_ = sockets_udp::Send::Results::Create(send_info); + AsyncWorkCompleted(); +} + +SocketsUdpCloseFunction::SocketsUdpCloseFunction() {} + +SocketsUdpCloseFunction::~SocketsUdpCloseFunction() {} + +bool SocketsUdpCloseFunction::Prepare() { + params_ = sockets_udp::Close::Params::Create(*args_); + EXTENSION_FUNCTION_VALIDATE(params_.get()); + return true; +} + +void SocketsUdpCloseFunction::Work() { + ResumableUDPSocket* socket = GetUdpSocket(params_->socket_id); + if (!socket) { + error_ = kSocketNotFoundError; + return; + } + + RemoveSocket(params_->socket_id); + results_ = sockets_udp::Close::Results::Create(); +} + +SocketsUdpGetInfoFunction::SocketsUdpGetInfoFunction() {} + +SocketsUdpGetInfoFunction::~SocketsUdpGetInfoFunction() {} + +bool SocketsUdpGetInfoFunction::Prepare() { + params_ = sockets_udp::GetInfo::Params::Create(*args_); + EXTENSION_FUNCTION_VALIDATE(params_.get()); + return true; +} + +void SocketsUdpGetInfoFunction::Work() { + ResumableUDPSocket* socket = GetUdpSocket(params_->socket_id); + if (!socket) { + error_ = kSocketNotFoundError; + return; + } + + linked_ptr<sockets_udp::SocketInfo> socket_info = + CreateSocketInfo(params_->socket_id, socket); + results_ = sockets_udp::GetInfo::Results::Create(*socket_info); +} + +SocketsUdpGetSocketsFunction::SocketsUdpGetSocketsFunction() {} + +SocketsUdpGetSocketsFunction::~SocketsUdpGetSocketsFunction() {} + +bool SocketsUdpGetSocketsFunction::Prepare() { + return true; +} + +void SocketsUdpGetSocketsFunction::Work() { + std::vector<linked_ptr<sockets_udp::SocketInfo> > socket_infos; + base::hash_set<int>* resource_ids = GetSocketIds(); + if (resource_ids != NULL) { + for (base::hash_set<int>::iterator it = resource_ids->begin(); + it != resource_ids->end(); ++it) { + int socket_id = *it; + ResumableUDPSocket* socket = GetUdpSocket(socket_id); + if (socket) { + socket_infos.push_back(CreateSocketInfo(socket_id, socket)); + } + } + } + results_ = sockets_udp::GetSockets::Results::Create(socket_infos); +} + +SocketsUdpJoinGroupFunction::SocketsUdpJoinGroupFunction() {} + +SocketsUdpJoinGroupFunction::~SocketsUdpJoinGroupFunction() {} + +bool SocketsUdpJoinGroupFunction::Prepare() { + params_ = sockets_udp::JoinGroup::Params::Create(*args_); + EXTENSION_FUNCTION_VALIDATE(params_.get()); + return true; +} + +void SocketsUdpJoinGroupFunction::Work() { + ResumableUDPSocket* socket = GetUdpSocket(params_->socket_id); + if (!socket) { + error_ = kSocketNotFoundError; + return; + } + + SocketPermission::CheckParam param( + SocketPermissionRequest::UDP_MULTICAST_MEMBERSHIP, + kWildcardAddress, + kWildcardPort); + + if (!PermissionsData::CheckAPIPermissionWithParam( + GetExtension(), APIPermission::kSocket, ¶m)) { + error_ = kPermissionError; + return; + } + + int net_result = socket->JoinGroup(params_->address); + if (net_result != net::OK) + error_ = net::ErrorToString(net_result); + results_ = sockets_udp::JoinGroup::Results::Create(net_result); +} + +SocketsUdpLeaveGroupFunction::SocketsUdpLeaveGroupFunction() {} + +SocketsUdpLeaveGroupFunction::~SocketsUdpLeaveGroupFunction() {} + +bool SocketsUdpLeaveGroupFunction::Prepare() { + params_ = api::sockets_udp::LeaveGroup::Params::Create(*args_); + EXTENSION_FUNCTION_VALIDATE(params_.get()); + return true; +} + +void SocketsUdpLeaveGroupFunction::Work() { + ResumableUDPSocket* socket = GetUdpSocket(params_->socket_id); + if (!socket) { + error_ = kSocketNotFoundError; + return; + } + + SocketPermission::CheckParam param( + SocketPermissionRequest::UDP_MULTICAST_MEMBERSHIP, + kWildcardAddress, + kWildcardPort); + if (!PermissionsData::CheckAPIPermissionWithParam(GetExtension(), + APIPermission::kSocket, + ¶m)) { + error_ = kPermissionError; + return; + } + + int net_result = socket->LeaveGroup(params_->address); + if (net_result != net::OK) + error_ = net::ErrorToString(net_result); + results_ = sockets_udp::LeaveGroup::Results::Create(net_result); +} + +SocketsUdpSetMulticastTimeToLiveFunction:: + SocketsUdpSetMulticastTimeToLiveFunction() {} + +SocketsUdpSetMulticastTimeToLiveFunction:: + ~SocketsUdpSetMulticastTimeToLiveFunction() {} + +bool SocketsUdpSetMulticastTimeToLiveFunction::Prepare() { + params_ = api::sockets_udp::SetMulticastTimeToLive::Params::Create(*args_); + EXTENSION_FUNCTION_VALIDATE(params_.get()); + return true; +} + +void SocketsUdpSetMulticastTimeToLiveFunction::Work() { + ResumableUDPSocket* socket = GetUdpSocket(params_->socket_id); + if (!socket) { + error_ = kSocketNotFoundError; + return; + } + + int net_result = socket->SetMulticastTimeToLive(params_->ttl); + if (net_result != net::OK) + error_ = net::ErrorToString(net_result); + results_ = sockets_udp::SetMulticastTimeToLive::Results::Create(net_result); +} + +SocketsUdpSetMulticastLoopbackModeFunction:: + SocketsUdpSetMulticastLoopbackModeFunction() {} + +SocketsUdpSetMulticastLoopbackModeFunction:: + ~SocketsUdpSetMulticastLoopbackModeFunction() {} + +bool SocketsUdpSetMulticastLoopbackModeFunction::Prepare() { + params_ = api::sockets_udp::SetMulticastLoopbackMode::Params::Create(*args_); + EXTENSION_FUNCTION_VALIDATE(params_.get()); + return true; +} + +void SocketsUdpSetMulticastLoopbackModeFunction::Work() { + ResumableUDPSocket* socket = GetUdpSocket(params_->socket_id); + if (!socket) { + error_ = kSocketNotFoundError; + return; + } + + int net_result = socket->SetMulticastLoopbackMode(params_->enabled); + if (net_result != net::OK) + error_ = net::ErrorToString(net_result); + results_ = sockets_udp::SetMulticastLoopbackMode::Results::Create(net_result); +} + +SocketsUdpGetJoinedGroupsFunction::SocketsUdpGetJoinedGroupsFunction() {} + +SocketsUdpGetJoinedGroupsFunction::~SocketsUdpGetJoinedGroupsFunction() {} + +bool SocketsUdpGetJoinedGroupsFunction::Prepare() { + params_ = api::sockets_udp::GetJoinedGroups::Params::Create(*args_); + EXTENSION_FUNCTION_VALIDATE(params_.get()); + return true; +} + +void SocketsUdpGetJoinedGroupsFunction::Work() { + ResumableUDPSocket* socket = GetUdpSocket(params_->socket_id); + if (!socket) { + error_ = kSocketNotFoundError; + return; + } + + SocketPermission::CheckParam param( + SocketPermissionRequest::UDP_MULTICAST_MEMBERSHIP, + kWildcardAddress, + kWildcardPort); + if (!PermissionsData::CheckAPIPermissionWithParam( + GetExtension(), + APIPermission::kSocket, + ¶m)) { + error_ = kPermissionError; + return; + } + + const std::vector<std::string>& groups = socket->GetJoinedGroups(); + results_ = sockets_udp::GetJoinedGroups::Results::Create(groups); +} + +} // namespace api +} // namespace extensions diff --git a/chrome/browser/extensions/api/sockets_udp/sockets_udp_api.h b/chrome/browser/extensions/api/sockets_udp/sockets_udp_api.h new file mode 100644 index 0000000..b971c4f --- /dev/null +++ b/chrome/browser/extensions/api/sockets_udp/sockets_udp_api.h @@ -0,0 +1,258 @@ +// Copyright 2013 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. + +#ifndef CHROME_BROWSER_EXTENSIONS_API_SOCKETS_UDP_SOCKETS_UDP_API_H_ +#define CHROME_BROWSER_EXTENSIONS_API_SOCKETS_UDP_SOCKETS_UDP_API_H_ + +#include "chrome/browser/extensions/api/socket/socket_api.h" +#include "chrome/common/extensions/api/sockets_udp.h" + +namespace extensions { +class ResumableUDPSocket; +} + +namespace extensions { +namespace api { + +class UDPSocketAsyncApiFunction : public SocketAsyncApiFunction { + protected: + virtual ~UDPSocketAsyncApiFunction(); + + virtual scoped_ptr<SocketResourceManagerInterface> + CreateSocketResourceManager() OVERRIDE; + + ResumableUDPSocket* GetUdpSocket(int socket_id); +}; + +class UDPSocketExtensionWithDnsLookupFunction + : public SocketExtensionWithDnsLookupFunction { + protected: + virtual ~UDPSocketExtensionWithDnsLookupFunction(); + + virtual scoped_ptr<SocketResourceManagerInterface> + CreateSocketResourceManager() OVERRIDE; + + ResumableUDPSocket* GetUdpSocket(int socket_id); +}; + +class SocketsUdpCreateFunction : public UDPSocketAsyncApiFunction { + public: + DECLARE_EXTENSION_FUNCTION("sockets.udp.create", SOCKETS_UDP_CREATE) + + SocketsUdpCreateFunction(); + + protected: + virtual ~SocketsUdpCreateFunction(); + + // AsyncApiFunction: + virtual bool Prepare() OVERRIDE; + virtual void Work() OVERRIDE; + + private: + FRIEND_TEST_ALL_PREFIXES(SocketsUdpUnitTest, Create); + scoped_ptr<sockets_udp::Create::Params> params_; +}; + +class SocketsUdpUpdateFunction : public UDPSocketAsyncApiFunction { + public: + DECLARE_EXTENSION_FUNCTION("sockets.udp.update", SOCKETS_UDP_UPDATE) + + SocketsUdpUpdateFunction(); + + protected: + virtual ~SocketsUdpUpdateFunction(); + + // AsyncApiFunction: + virtual bool Prepare() OVERRIDE; + virtual void Work() OVERRIDE; + + private: + scoped_ptr<sockets_udp::Update::Params> params_; +}; + +class SocketsUdpBindFunction : public UDPSocketAsyncApiFunction { + public: + DECLARE_EXTENSION_FUNCTION("sockets.udp.bind", SOCKETS_UDP_BIND) + + SocketsUdpBindFunction(); + + protected: + virtual ~SocketsUdpBindFunction(); + + // AsyncApiFunction: + virtual bool Prepare() OVERRIDE; + virtual void Work() OVERRIDE; + + private: + scoped_ptr<sockets_udp::Bind::Params> params_; +}; + +class SocketsUdpSendFunction : public UDPSocketExtensionWithDnsLookupFunction { + public: + DECLARE_EXTENSION_FUNCTION("sockets.udp.send", SOCKETS_UDP_SEND) + + SocketsUdpSendFunction(); + + protected: + virtual ~SocketsUdpSendFunction(); + + // AsyncApiFunction: + virtual bool Prepare() OVERRIDE; + virtual void AsyncWorkStart() OVERRIDE; + void OnCompleted(int net_result); + void SetSendResult(int net_result, int bytes_written); + + // SocketExtensionWithDnsLookupFunction: + virtual void AfterDnsLookup(int lookup_result) OVERRIDE; + + private: + void StartSendTo(); + + scoped_ptr<sockets_udp::Send::Params> params_; + scoped_refptr<net::IOBuffer> io_buffer_; + size_t io_buffer_size_; +}; + +class SocketsUdpCloseFunction : public UDPSocketAsyncApiFunction { + public: + DECLARE_EXTENSION_FUNCTION("sockets.udp.close", SOCKETS_UDP_CLOSE) + + SocketsUdpCloseFunction(); + + protected: + virtual ~SocketsUdpCloseFunction(); + + // AsyncApiFunction: + virtual bool Prepare() OVERRIDE; + virtual void Work() OVERRIDE; + + private: + scoped_ptr<sockets_udp::Close::Params> params_; +}; + +class SocketsUdpGetInfoFunction : public UDPSocketAsyncApiFunction { + public: + DECLARE_EXTENSION_FUNCTION("sockets.udp.getInfo", SOCKETS_UDP_GETINFO) + + SocketsUdpGetInfoFunction(); + + protected: + virtual ~SocketsUdpGetInfoFunction(); + + // AsyncApiFunction: + virtual bool Prepare() OVERRIDE; + virtual void Work() OVERRIDE; + + private: + scoped_ptr<sockets_udp::GetInfo::Params> params_; +}; + +class SocketsUdpGetSocketsFunction : public UDPSocketAsyncApiFunction { + public: + DECLARE_EXTENSION_FUNCTION("sockets.udp.getSockets", SOCKETS_UDP_GETSOCKETS) + + SocketsUdpGetSocketsFunction(); + + protected: + virtual ~SocketsUdpGetSocketsFunction(); + + // AsyncApiFunction: + virtual bool Prepare() OVERRIDE; + virtual void Work() OVERRIDE; +}; + +class SocketsUdpJoinGroupFunction : public UDPSocketAsyncApiFunction { + public: + DECLARE_EXTENSION_FUNCTION("sockets.udp.joinGroup", SOCKETS_UDP_JOINGROUP) + + SocketsUdpJoinGroupFunction(); + + protected: + virtual ~SocketsUdpJoinGroupFunction(); + + // AsyncApiFunction + virtual bool Prepare() OVERRIDE; + virtual void Work() OVERRIDE; + + private: + scoped_ptr<sockets_udp::JoinGroup::Params> params_; +}; + +class SocketsUdpLeaveGroupFunction : public UDPSocketAsyncApiFunction { + public: + DECLARE_EXTENSION_FUNCTION("sockets.udp.leaveGroup", SOCKETS_UDP_LEAVEGROUP) + + SocketsUdpLeaveGroupFunction(); + + protected: + virtual ~SocketsUdpLeaveGroupFunction(); + + // AsyncApiFunction + virtual bool Prepare() OVERRIDE; + virtual void Work() OVERRIDE; + + private: + scoped_ptr<sockets_udp::LeaveGroup::Params> params_; +}; + +class SocketsUdpSetMulticastTimeToLiveFunction + : public UDPSocketAsyncApiFunction { + public: + DECLARE_EXTENSION_FUNCTION("sockets.udp.setMulticastTimeToLive", + SOCKETS_UDP_SETMULTICASTTIMETOLIVE) + + SocketsUdpSetMulticastTimeToLiveFunction(); + + protected: + virtual ~SocketsUdpSetMulticastTimeToLiveFunction(); + + // AsyncApiFunction + virtual bool Prepare() OVERRIDE; + virtual void Work() OVERRIDE; + + private: + scoped_ptr<sockets_udp::SetMulticastTimeToLive::Params> params_; +}; + +class SocketsUdpSetMulticastLoopbackModeFunction + : public UDPSocketAsyncApiFunction { + public: + DECLARE_EXTENSION_FUNCTION("sockets.udp.setMulticastLoopbackMode", + SOCKETS_UDP_SETMULTICASTLOOPBACKMODE) + + SocketsUdpSetMulticastLoopbackModeFunction(); + + protected: + virtual ~SocketsUdpSetMulticastLoopbackModeFunction(); + + // AsyncApiFunction + virtual bool Prepare() OVERRIDE; + virtual void Work() OVERRIDE; + + private: + scoped_ptr<sockets_udp::SetMulticastLoopbackMode::Params> params_; +}; + +class SocketsUdpGetJoinedGroupsFunction : public UDPSocketAsyncApiFunction { + public: + DECLARE_EXTENSION_FUNCTION("sockets.udp.getJoinedGroups", + SOCKETS_UDP_GETJOINEDGROUPS) + + SocketsUdpGetJoinedGroupsFunction(); + + protected: + virtual ~SocketsUdpGetJoinedGroupsFunction(); + + // AsyncApiFunction + virtual bool Prepare() OVERRIDE; + virtual void Work() OVERRIDE; + + private: + scoped_ptr<sockets_udp::GetJoinedGroups::Params> params_; +}; + +} // namespace api +} // namespace extensions + +#endif // CHROME_BROWSER_EXTENSIONS_API_SOCKETS_UDP_SOCKETS_UDP_API_H_ diff --git a/chrome/browser/extensions/api/sockets_udp/sockets_udp_api_unittest.cc b/chrome/browser/extensions/api/sockets_udp/sockets_udp_api_unittest.cc new file mode 100644 index 0000000..08d3c83 --- /dev/null +++ b/chrome/browser/extensions/api/sockets_udp/sockets_udp_api_unittest.cc @@ -0,0 +1,79 @@ +// Copyright 2013 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 "base/values.h" +#include "chrome/browser/browser_process_impl.h" +#include "chrome/browser/extensions/api/api_function.h" +#include "chrome/browser/extensions/api/api_resource_manager.h" +#include "chrome/browser/extensions/api/socket/socket.h" +#include "chrome/browser/extensions/api/socket/udp_socket.h" +#include "chrome/browser/extensions/api/sockets_udp/sockets_udp_api.h" +#include "chrome/browser/extensions/extension_function_test_utils.h" +#include "chrome/browser/extensions/test_extension_system.h" +#include "chrome/browser/profiles/profile_manager.h" +#include "chrome/test/base/browser_with_test_window_test.h" +#include "chrome/test/base/testing_browser_process.h" +#include "testing/gmock/include/gmock/gmock.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace utils = extension_function_test_utils; + +namespace extensions { +namespace api { + +BrowserContextKeyedService* ApiResourceManagerTestFactory( + content::BrowserContext* profile) { + content::BrowserThread::ID id; + CHECK(content::BrowserThread::GetCurrentThreadIdentifier(&id)); + return ApiResourceManager<ResumableUDPSocket>:: + CreateApiResourceManagerForTest(static_cast<Profile*>(profile), id); +} + +class SocketsUdpUnitTest : public BrowserWithTestWindowTest { + public: + virtual void SetUp() { + BrowserWithTestWindowTest::SetUp(); + + ApiResourceManager<ResumableUDPSocket>::GetFactoryInstance()-> + SetTestingFactoryAndUse(browser()->profile(), + ApiResourceManagerTestFactory); + + extension_ = utils::CreateEmptyExtensionWithLocation( + extensions::Manifest::UNPACKED); + } + + base::Value* RunFunctionWithExtension( + UIThreadExtensionFunction* function, const std::string& args) { + scoped_refptr<UIThreadExtensionFunction> delete_function(function); + function->set_extension(extension_.get()); + return utils::RunFunctionAndReturnSingleResult(function, args, browser()); + } + + base::DictionaryValue* RunFunctionAndReturnDict( + UIThreadExtensionFunction* function, const std::string& args) { + base::Value* result = RunFunctionWithExtension(function, args); + return result ? utils::ToDictionary(result) : NULL; + } + + protected: + scoped_refptr<extensions::Extension> extension_; +}; + +TEST_F(SocketsUdpUnitTest, Create) { + // Get BrowserThread + content::BrowserThread::ID id; + CHECK(content::BrowserThread::GetCurrentThreadIdentifier(&id)); + + // Create SocketCreateFunction and put it on BrowserThread + SocketsUdpCreateFunction *function = new SocketsUdpCreateFunction(); + function->set_work_thread_id(id); + + // Run tests + scoped_ptr<base::DictionaryValue> result(RunFunctionAndReturnDict( + function, "[{\"resumable\": true, \"name\": \"foo\"}]")); + ASSERT_TRUE(result.get()); +} + +} // namespace api +} // namespace extensions diff --git a/chrome/browser/extensions/api/sockets_udp/sockets_udp_apitest.cc b/chrome/browser/extensions/api/sockets_udp/sockets_udp_apitest.cc new file mode 100644 index 0000000..943bbf1 --- /dev/null +++ b/chrome/browser/extensions/api/sockets_udp/sockets_udp_apitest.cc @@ -0,0 +1,125 @@ +// Copyright 2013 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 "base/memory/ref_counted.h" +#include "base/path_service.h" +#include "base/strings/stringprintf.h" +#include "chrome/browser/extensions/api/dns/host_resolver_wrapper.h" +#include "chrome/browser/extensions/api/dns/mock_host_resolver_creator.h" +#include "chrome/browser/extensions/api/sockets_udp/sockets_udp_api.h" +#include "chrome/browser/extensions/extension_apitest.h" +#include "chrome/browser/extensions/extension_function_test_utils.h" +#include "chrome/browser/extensions/extension_service.h" +#include "chrome/browser/extensions/extension_test_message_listener.h" +#include "chrome/browser/ui/browser.h" +#include "chrome/browser/ui/extensions/application_launch.h" +#include "chrome/common/chrome_paths.h" +#include "chrome/common/chrome_switches.h" +#include "chrome/test/base/in_process_browser_test.h" +#include "chrome/test/base/ui_test_utils.h" +#include "net/dns/mock_host_resolver.h" +#include "net/test/spawned_test_server/spawned_test_server.h" + +using extensions::Extension; + +namespace utils = extension_function_test_utils; + +namespace { + +// TODO(jschuh): Hanging plugin tests. crbug.com/244653 +#if defined(OS_WIN) && defined(ARCH_CPU_X86_64) +#define MAYBE(x) DISABLED_##x +#else +#define MAYBE(x) x +#endif + +const std::string kHostname = "127.0.0.1"; +const int kPort = 8888; + +class SocketsUdpApiTest : public ExtensionApiTest { + public: + SocketsUdpApiTest() : resolver_event_(true, false), + resolver_creator_( + new extensions::MockHostResolverCreator()) { + } + + virtual void SetUpOnMainThread() OVERRIDE { + extensions::HostResolverWrapper::GetInstance()->SetHostResolverForTesting( + resolver_creator_->CreateMockHostResolver()); + } + + virtual void CleanUpOnMainThread() OVERRIDE { + extensions::HostResolverWrapper::GetInstance()-> + SetHostResolverForTesting(NULL); + resolver_creator_->DeleteMockHostResolver(); + } + + private: + base::WaitableEvent resolver_event_; + + // The MockHostResolver asserts that it's used on the same thread on which + // it's created, which is actually a stronger rule than its real counterpart. + // But that's fine; it's good practice. + scoped_refptr<extensions::MockHostResolverCreator> resolver_creator_; +}; + +} // namespace + +IN_PROC_BROWSER_TEST_F(SocketsUdpApiTest, SocketsUdpCreateGood) { + scoped_refptr<extensions::api::SocketsUdpCreateFunction> + socket_create_function(new extensions::api::SocketsUdpCreateFunction()); + scoped_refptr<Extension> empty_extension(utils::CreateEmptyExtension()); + + socket_create_function->set_extension(empty_extension.get()); + socket_create_function->set_has_callback(true); + + scoped_ptr<base::Value> result(utils::RunFunctionAndReturnSingleResult( + socket_create_function.get(), "[]", browser(), utils::NONE)); + ASSERT_EQ(base::Value::TYPE_DICTIONARY, result->GetType()); + base::DictionaryValue *value = + static_cast<base::DictionaryValue*>(result.get()); + int socketId = -1; + EXPECT_TRUE(value->GetInteger("socketId", &socketId)); + EXPECT_TRUE(socketId > 0); +} + +IN_PROC_BROWSER_TEST_F(SocketsUdpApiTest, SocketsUdpExtension) { + scoped_ptr<net::SpawnedTestServer> test_server( + new net::SpawnedTestServer( + net::SpawnedTestServer::TYPE_UDP_ECHO, + net::SpawnedTestServer::kLocalhost, + base::FilePath(FILE_PATH_LITERAL("net/data")))); + EXPECT_TRUE(test_server->Start()); + + net::HostPortPair host_port_pair = test_server->host_port_pair(); + int port = host_port_pair.port(); + ASSERT_TRUE(port > 0); + + // Test that sendTo() is properly resolving hostnames. + host_port_pair.set_host("LOCALhost"); + + ResultCatcher catcher; + catcher.RestrictToProfile(browser()->profile()); + + ExtensionTestMessageListener listener("info_please", true); + + ASSERT_TRUE(LoadExtension(test_data_dir_.AppendASCII("sockets_udp/api"))); + EXPECT_TRUE(listener.WaitUntilSatisfied()); + listener.Reply( + base::StringPrintf("udp:%s:%d", host_port_pair.host().c_str(), port)); + + EXPECT_TRUE(catcher.GetNextResult()) << catcher.message(); +} + +IN_PROC_BROWSER_TEST_F(SocketsUdpApiTest, SocketsUdpMulticast) { + ResultCatcher catcher; + catcher.RestrictToProfile(browser()->profile()); + ExtensionTestMessageListener listener("info_please", true); + ASSERT_TRUE(LoadExtension(test_data_dir_.AppendASCII("sockets_udp/api"))); + EXPECT_TRUE(listener.WaitUntilSatisfied()); + listener.Reply( + base::StringPrintf("multicast:%s:%d", kHostname.c_str(), kPort)); + + EXPECT_TRUE(catcher.GetNextResult()) << catcher.message(); +} diff --git a/chrome/browser/extensions/api/sockets_udp/udp_socket_event_dispatcher.cc b/chrome/browser/extensions/api/sockets_udp/udp_socket_event_dispatcher.cc new file mode 100644 index 0000000..2331417 --- /dev/null +++ b/chrome/browser/extensions/api/sockets_udp/udp_socket_event_dispatcher.cc @@ -0,0 +1,125 @@ +// Copyright 2013 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/sockets_udp/udp_socket_event_dispatcher.h" + +#include "chrome/browser/extensions/api/socket/udp_socket.h" +#include "chrome/browser/extensions/event_router.h" +#include "chrome/browser/extensions/extension_system.h" +#include "net/base/net_errors.h" + +namespace extensions { +namespace api { + +static base::LazyInstance<ProfileKeyedAPIFactory<UDPSocketEventDispatcher> > +g_factory = LAZY_INSTANCE_INITIALIZER; + +// static +ProfileKeyedAPIFactory<UDPSocketEventDispatcher>* + UDPSocketEventDispatcher::GetFactoryInstance() { + return &g_factory.Get(); +} + +// static +UDPSocketEventDispatcher* UDPSocketEventDispatcher::Get(Profile* profile) { + return ProfileKeyedAPIFactory<UDPSocketEventDispatcher>::GetForProfile( + profile); +} + +UDPSocketEventDispatcher::UDPSocketEventDispatcher(Profile* profile) + : thread_id_(Socket::kThreadId), + profile_(profile) { +} + +UDPSocketEventDispatcher::~UDPSocketEventDispatcher() { +} + +ResumableUDPSocket* UDPSocketEventDispatcher::GetUdpSocket( + const std::string& extension_id, + int socket_id) { + DCHECK(content::BrowserThread::CurrentlyOn(thread_id_)); + + ApiResourceManager<ResumableUDPSocket>* manager = + ApiResourceManager<ResumableUDPSocket>::Get(profile_); + DCHECK(manager) << "There is no socket manager. " + "If this assertion is failing during a test, then it is likely that " + "TestExtensionSystem is failing to provide an instance of " + "ApiResourceManager<ResumableUDPSocket>."; + + return manager->Get(extension_id, socket_id); +} + +void UDPSocketEventDispatcher::OnSocketBind(const std::string& extension_id, + int socket_id) { + DCHECK(content::BrowserThread::CurrentlyOn(thread_id_)); + StartReceive(extension_id, socket_id); +} + +void UDPSocketEventDispatcher::StartReceive(const std::string& extension_id, + int socket_id) { + DCHECK(content::BrowserThread::CurrentlyOn(thread_id_)); + ResumableUDPSocket* socket = GetUdpSocket(extension_id, socket_id); + if (socket == NULL) { + // This can happen if the socket is closed while our callback is active. + return; + } + DCHECK(extension_id == socket->owner_extension_id()) + << "Socket has wrong owner."; + + int buffer_size = (socket->buffer_size() <= 0 ? 4096 : socket->buffer_size()); + socket->RecvFrom(buffer_size, + base::Bind(&UDPSocketEventDispatcher::ReceiveCallback, + AsWeakPtr(), + extension_id, + socket_id)); +} + +void UDPSocketEventDispatcher::ReceiveCallback( + const std::string& extension_id, + const int socket_id, + int bytes_read, + scoped_refptr<net::IOBuffer> io_buffer, + const std::string& address, + int port) { + DCHECK(content::BrowserThread::CurrentlyOn(thread_id_)); + + // Note: if "bytes_read" < 0, there was a network error, and "bytes_read" is + // a value from "net::ERR_". + + if (bytes_read >= 0) { + // Dispatch event. + sockets_udp::ReceiveInfo receive_info; + receive_info.socket_id = socket_id; + receive_info.data = std::string(io_buffer->data(), bytes_read); + receive_info.remote_address = address; + receive_info.remote_port = port; + scoped_ptr<base::ListValue> args = + sockets_udp::OnReceive::Create(receive_info); + scoped_ptr<Event> event( + new Event(sockets_udp::OnReceive::kEventName, args.Pass())); + ExtensionSystem::Get(profile_)->event_router()->DispatchEventToExtension( + extension_id, event.Pass()); + + // Post a task to delay the read until the socket is available, as + // calling StartReceive at this point would error with ERR_IO_PENDING. + content::BrowserThread::PostTask(thread_id_, FROM_HERE, + base::Bind(&UDPSocketEventDispatcher::StartReceive, + AsWeakPtr(), extension_id, socket_id)); + } else { + // Dispatch event but don't start another read to avoid infinite read if + // we have a persistent network error. + sockets_udp::ReceiveErrorInfo receive_error_info; + receive_error_info.socket_id = socket_id; + receive_error_info.result = bytes_read; + scoped_ptr<base::ListValue> args = + sockets_udp::OnReceiveError::Create(receive_error_info); + scoped_ptr<Event> event( + new Event(sockets_udp::OnReceiveError::kEventName, args.Pass())); + ExtensionSystem::Get(profile_)->event_router()->DispatchEventToExtension( + extension_id, event.Pass()); + } +} + +} // namespace api +} // namespace extensions diff --git a/chrome/browser/extensions/api/sockets_udp/udp_socket_event_dispatcher.h b/chrome/browser/extensions/api/sockets_udp/udp_socket_event_dispatcher.h new file mode 100644 index 0000000..c5672a9 --- /dev/null +++ b/chrome/browser/extensions/api/sockets_udp/udp_socket_event_dispatcher.h @@ -0,0 +1,66 @@ +// Copyright 2013 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. + +#ifndef CHROME_BROWSER_EXTENSIONS_API_SOCKETS_UDP_UDP_SOCKET_EVENT_DISPATCHER_H_ +#define CHROME_BROWSER_EXTENSIONS_API_SOCKETS_UDP_UDP_SOCKET_EVENT_DISPATCHER_H_ + +#include "chrome/browser/extensions/api/sockets_udp/sockets_udp_api.h" + +namespace extensions { +class ResumableUDPSocket; +} + +namespace extensions { +namespace api { + +// Dispatch events related to "sockets.udp" sockets from callback on native +// socket instances. There is one instance per profile. +class UDPSocketEventDispatcher + : public ProfileKeyedAPI, + public base::SupportsWeakPtr<UDPSocketEventDispatcher> { + public: + explicit UDPSocketEventDispatcher(Profile* profile); + virtual ~UDPSocketEventDispatcher(); + + // Socket is active, start receving from it. + void OnSocketBind(const std::string& extension_id, int socket_id); + + // ProfileKeyedAPI implementation. + static ProfileKeyedAPIFactory<UDPSocketEventDispatcher>* GetFactoryInstance(); + + // Convenience method to get the SocketEventDispatcher for a profile. + static UDPSocketEventDispatcher* Get(Profile* profile); + + private: + friend class ProfileKeyedAPIFactory<UDPSocketEventDispatcher>; + + ResumableUDPSocket* GetUdpSocket(const std::string& extension_id, + int socket_id); + + // Start a receive and register a callback. + void StartReceive(const std::string& extension_id, int socket_id); + + // Called when socket receive data. + void ReceiveCallback(const std::string& extension_id, + const int socket_id, + int bytes_read, + scoped_refptr<net::IOBuffer> io_buffer, + const std::string& address, + int port); + + // ProfileKeyedAPI implementation. + static const char* service_name() { + return "UDPSocketEventDispatcher"; + } + static const bool kServiceHasOwnInstanceInIncognito = true; + + // Usually IO thread (except for unit testing). + content::BrowserThread::ID thread_id_; + Profile* const profile_; +}; + +} // namespace api +} // namespace extensions + +#endif // CHROME_BROWSER_EXTENSIONS_API_SOCKETS_UDP_UDP_SOCKET_EVENT_DISPATCHER_H_ |