diff options
Diffstat (limited to 'extensions/browser/api')
25 files changed, 5423 insertions, 6 deletions
diff --git a/extensions/browser/api/api_resource_manager.h b/extensions/browser/api/api_resource_manager.h index 0513c2c..39e9b8e 100644 --- a/extensions/browser/api/api_resource_manager.h +++ b/extensions/browser/api/api_resource_manager.h @@ -23,15 +23,16 @@ #include "extensions/common/extension.h" namespace extensions { + namespace api { class SerialEventDispatcher; +} + +namespace core_api { class TCPServerSocketEventDispatcher; class TCPSocketEventDispatcher; class UDPSocketEventDispatcher; } -} - -namespace extensions { // An ApiResourceManager manages the lifetime of a set of resources that // ApiFunctions use. Examples are sockets or USB connections. @@ -149,13 +150,17 @@ class ApiResourceManager : public BrowserContextKeyedAPI, } private: + // TODO(rockot): ApiResourceData could be moved out of ApiResourceManager and + // we could avoid maintaining a friends list here. friend class api::SerialEventDispatcher; - friend class api::TCPServerSocketEventDispatcher; - friend class api::TCPSocketEventDispatcher; - friend class api::UDPSocketEventDispatcher; + friend class core_api::TCPServerSocketEventDispatcher; + friend class core_api::TCPSocketEventDispatcher; + friend class core_api::UDPSocketEventDispatcher; friend class BrowserContextKeyedAPIFactory<ApiResourceManager<T> >; + // BrowserContextKeyedAPI implementation. static const char* service_name() { return T::service_name(); } + static const bool kServiceHasOwnInstanceInIncognito = true; static const bool kServiceIsNULLWhileTesting = true; diff --git a/extensions/browser/api/socket/socket.cc b/extensions/browser/api/socket/socket.cc new file mode 100644 index 0000000..afec38f --- /dev/null +++ b/extensions/browser/api/socket/socket.cc @@ -0,0 +1,152 @@ +// Copyright 2014 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 "extensions/browser/api/socket/socket.h" + +#include "base/bind.h" +#include "base/lazy_instance.h" +#include "extensions/browser/api/api_resource_manager.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" +#include "net/socket/socket.h" + +namespace extensions { + +const char kSocketTypeNotSupported[] = "Socket type does not support this API"; + +static base::LazyInstance< + BrowserContextKeyedAPIFactory<ApiResourceManager<Socket> > > g_factory = + LAZY_INSTANCE_INITIALIZER; + +// static +template <> +BrowserContextKeyedAPIFactory<ApiResourceManager<Socket> >* +ApiResourceManager<Socket>::GetFactoryInstance() { + return g_factory.Pointer(); +} + +Socket::Socket(const std::string& owner_extension_id) + : ApiResource(owner_extension_id), is_connected_(false) {} + +Socket::~Socket() { + // Derived destructors should make sure the socket has been closed. + DCHECK(!is_connected_); +} + +void Socket::Write(scoped_refptr<net::IOBuffer> io_buffer, + int byte_count, + const CompletionCallback& callback) { + DCHECK(!callback.is_null()); + write_queue_.push(WriteRequest(io_buffer, byte_count, callback)); + WriteData(); +} + +void Socket::WriteData() { + // IO is pending. + if (io_buffer_write_.get()) + return; + + WriteRequest& request = write_queue_.front(); + + DCHECK(request.byte_count >= request.bytes_written); + io_buffer_write_ = new net::WrappedIOBuffer(request.io_buffer->data() + + request.bytes_written); + int result = + WriteImpl(io_buffer_write_.get(), + request.byte_count - request.bytes_written, + base::Bind(&Socket::OnWriteComplete, base::Unretained(this))); + + if (result != net::ERR_IO_PENDING) + OnWriteComplete(result); +} + +void Socket::OnWriteComplete(int result) { + io_buffer_write_ = NULL; + + WriteRequest& request = write_queue_.front(); + + if (result >= 0) { + request.bytes_written += result; + if (request.bytes_written < request.byte_count) { + WriteData(); + return; + } + DCHECK(request.bytes_written == request.byte_count); + result = request.bytes_written; + } + + request.callback.Run(result); + write_queue_.pop(); + + if (!write_queue_.empty()) + WriteData(); +} + +bool Socket::SetKeepAlive(bool enable, int delay) { return false; } + +bool Socket::SetNoDelay(bool no_delay) { return false; } + +int Socket::Listen(const std::string& address, + int port, + int backlog, + std::string* error_msg) { + *error_msg = kSocketTypeNotSupported; + return net::ERR_FAILED; +} + +void Socket::Accept(const AcceptCompletionCallback& callback) { + callback.Run(net::ERR_FAILED, NULL); +} + +// 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; +} + +void Socket::IPEndPointToStringAndPort(const net::IPEndPoint& address, + std::string* ip_address_str, + int* port) { + DCHECK(ip_address_str); + DCHECK(port); + *ip_address_str = address.ToStringWithoutPort(); + if (ip_address_str->empty()) { + *port = 0; + } else { + *port = address.port(); + } +} + +Socket::WriteRequest::WriteRequest(scoped_refptr<net::IOBuffer> io_buffer, + int byte_count, + const CompletionCallback& callback) + : io_buffer(io_buffer), + byte_count(byte_count), + callback(callback), + bytes_written(0) {} + +Socket::WriteRequest::~WriteRequest() {} + +} // namespace extensions diff --git a/extensions/browser/api/socket/socket.h b/extensions/browser/api/socket/socket.h new file mode 100644 index 0000000..9ae45e7 --- /dev/null +++ b/extensions/browser/api/socket/socket.h @@ -0,0 +1,126 @@ +// Copyright 2014 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 EXTENSIONS_BROWSER_API_SOCKET_SOCKET_H_ +#define EXTENSIONS_BROWSER_API_SOCKET_SOCKET_H_ + +#include <queue> +#include <string> +#include <utility> + +#include "base/callback.h" +#include "base/memory/ref_counted.h" +#include "extensions/browser/api/api_resource.h" +#include "extensions/browser/api/api_resource_manager.h" +#include "net/base/completion_callback.h" +#include "net/base/io_buffer.h" +#include "net/base/ip_endpoint.h" +#include "net/socket/tcp_client_socket.h" + +namespace net { +class AddressList; +class IPEndPoint; +class Socket; +} + +namespace extensions { + +typedef base::Callback<void(int)> CompletionCallback; +typedef base::Callback<void(int, scoped_refptr<net::IOBuffer> io_buffer)> + ReadCompletionCallback; +typedef base::Callback< + void(int, scoped_refptr<net::IOBuffer> io_buffer, const std::string&, int)> + RecvFromCompletionCallback; +typedef base::Callback<void(int, net::TCPClientSocket*)> + AcceptCompletionCallback; + +// A Socket wraps a low-level socket and includes housekeeping information that +// we need to manage it in the context of an extension. +class Socket : public ApiResource { + public: + enum SocketType { TYPE_TCP, TYPE_UDP, }; + + virtual ~Socket(); + virtual void Connect(const std::string& address, + int port, + const CompletionCallback& callback) = 0; + virtual void Disconnect() = 0; + virtual int Bind(const std::string& address, int port) = 0; + + // The |callback| will be called with the number of bytes read into the + // buffer, or a negative number if an error occurred. + virtual void Read(int count, const ReadCompletionCallback& callback) = 0; + + // The |callback| will be called with |byte_count| or a negative number if an + // error occurred. + void Write(scoped_refptr<net::IOBuffer> io_buffer, + int byte_count, + const CompletionCallback& callback); + + virtual void RecvFrom(int count, + const RecvFromCompletionCallback& callback) = 0; + virtual void SendTo(scoped_refptr<net::IOBuffer> io_buffer, + int byte_count, + const std::string& address, + int port, + const CompletionCallback& callback) = 0; + + virtual bool SetKeepAlive(bool enable, int delay); + virtual bool SetNoDelay(bool no_delay); + virtual int Listen(const std::string& address, + int port, + int backlog, + std::string* error_msg); + virtual void Accept(const AcceptCompletionCallback& callback); + + virtual bool IsConnected() = 0; + + virtual bool GetPeerAddress(net::IPEndPoint* address) = 0; + virtual bool GetLocalAddress(net::IPEndPoint* address) = 0; + + virtual SocketType GetSocketType() const = 0; + + 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: + explicit Socket(const std::string& owner_extension_id_); + + void WriteData(); + virtual int WriteImpl(net::IOBuffer* io_buffer, + int io_buffer_size, + const net::CompletionCallback& callback) = 0; + virtual void OnWriteComplete(int result); + + const std::string address_; + bool is_connected_; + + private: + friend class ApiResourceManager<Socket>; + static const char* service_name() { return "SocketManager"; } + + struct WriteRequest { + WriteRequest(scoped_refptr<net::IOBuffer> io_buffer, + int byte_count, + const CompletionCallback& callback); + ~WriteRequest(); + scoped_refptr<net::IOBuffer> io_buffer; + int byte_count; + CompletionCallback callback; + int bytes_written; + }; + std::queue<WriteRequest> write_queue_; + scoped_refptr<net::IOBuffer> io_buffer_write_; +}; + +} // namespace extensions + +#endif // EXTENSIONS_BROWSER_API_SOCKET_SOCKET_H_ diff --git a/extensions/browser/api/socket/socket/OWNERS b/extensions/browser/api/socket/socket/OWNERS new file mode 100644 index 0000000..b0df7e7 --- /dev/null +++ b/extensions/browser/api/socket/socket/OWNERS @@ -0,0 +1,2 @@ +ikarienator@chromium.org +rpaquay@chromium.org diff --git a/extensions/browser/api/socket/socket_api.cc b/extensions/browser/api/socket/socket_api.cc new file mode 100644 index 0000000..34a5517 --- /dev/null +++ b/extensions/browser/api/socket/socket_api.cc @@ -0,0 +1,891 @@ +// Copyright 2014 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 "extensions/browser/api/socket/socket_api.h" + +#include <vector> + +#include "base/bind.h" +#include "base/containers/hash_tables.h" +#include "chrome/browser/extensions/api/dns/host_resolver_wrapper.h" +#include "chrome/common/extensions/permissions/socket_permission.h" +#include "content/public/browser/browser_context.h" +#include "content/public/browser/resource_context.h" +#include "extensions/browser/api/socket/socket.h" +#include "extensions/browser/api/socket/tcp_socket.h" +#include "extensions/browser/api/socket/udp_socket.h" +#include "extensions/browser/extension_system.h" +#include "extensions/common/extension.h" +#include "extensions/common/permissions/permissions_data.h" +#include "net/base/host_port_pair.h" +#include "net/base/io_buffer.h" +#include "net/base/ip_endpoint.h" +#include "net/base/net_errors.h" +#include "net/base/net_log.h" +#include "net/base/net_util.h" + +namespace extensions { + +using content::SocketPermissionRequest; + +const char kAddressKey[] = "address"; +const char kPortKey[] = "port"; +const char kBytesWrittenKey[] = "bytesWritten"; +const char kDataKey[] = "data"; +const char kResultCodeKey[] = "resultCode"; +const char kSocketIdKey[] = "socketId"; + +const char kSocketNotFoundError[] = "Socket not found"; +const char kDnsLookupFailedError[] = "DNS resolution failed"; +const char kPermissionError[] = "App does not have permission"; +const char kNetworkListError[] = "Network lookup failed or unsupported"; +const char kTCPSocketBindError[] = + "TCP socket does not support bind. For TCP server please use listen."; +const char kMulticastSocketTypeError[] = "Only UDP socket supports multicast."; +const char kWildcardAddress[] = "*"; +const int kWildcardPort = 0; + +SocketAsyncApiFunction::SocketAsyncApiFunction() {} + +SocketAsyncApiFunction::~SocketAsyncApiFunction() {} + +bool SocketAsyncApiFunction::PrePrepare() { + manager_ = CreateSocketResourceManager(); + return manager_->SetBrowserContext(browser_context()); +} + +bool SocketAsyncApiFunction::Respond() { return error_.empty(); } + +scoped_ptr<SocketResourceManagerInterface> +SocketAsyncApiFunction::CreateSocketResourceManager() { + return scoped_ptr<SocketResourceManagerInterface>( + new SocketResourceManager<Socket>()).Pass(); +} + +int SocketAsyncApiFunction::AddSocket(Socket* socket) { + return manager_->Add(socket); +} + +Socket* SocketAsyncApiFunction::GetSocket(int api_resource_id) { + return manager_->Get(extension_->id(), api_resource_id); +} + +base::hash_set<int>* SocketAsyncApiFunction::GetSocketIds() { + return manager_->GetResourceIds(extension_->id()); +} + +void SocketAsyncApiFunction::RemoveSocket(int api_resource_id) { + manager_->Remove(extension_->id(), api_resource_id); +} + +SocketExtensionWithDnsLookupFunction::SocketExtensionWithDnsLookupFunction() + : resource_context_(NULL), + request_handle_(new net::HostResolver::RequestHandle), + addresses_(new net::AddressList) {} + +SocketExtensionWithDnsLookupFunction::~SocketExtensionWithDnsLookupFunction() {} + +bool SocketExtensionWithDnsLookupFunction::PrePrepare() { + if (!SocketAsyncApiFunction::PrePrepare()) + return false; + resource_context_ = browser_context()->GetResourceContext(); + return resource_context_ != NULL; +} + +void SocketExtensionWithDnsLookupFunction::StartDnsLookup( + const std::string& hostname) { + net::HostResolver* host_resolver = + extensions::HostResolverWrapper::GetInstance()->GetHostResolver( + resource_context_->GetHostResolver()); + DCHECK(host_resolver); + + // Yes, we are passing zero as the port. There are some interesting but not + // presently relevant reasons why HostResolver asks for the port of the + // hostname you'd like to resolve, even though it doesn't use that value in + // determining its answer. + net::HostPortPair host_port_pair(hostname, 0); + + net::HostResolver::RequestInfo request_info(host_port_pair); + int resolve_result = host_resolver->Resolve( + request_info, + net::DEFAULT_PRIORITY, + addresses_.get(), + base::Bind(&SocketExtensionWithDnsLookupFunction::OnDnsLookup, this), + request_handle_.get(), + net::BoundNetLog()); + + if (resolve_result != net::ERR_IO_PENDING) + OnDnsLookup(resolve_result); +} + +void SocketExtensionWithDnsLookupFunction::OnDnsLookup(int resolve_result) { + if (resolve_result == net::OK) { + DCHECK(!addresses_->empty()); + resolved_address_ = addresses_->front().ToStringWithoutPort(); + } else { + error_ = kDnsLookupFailedError; + } + AfterDnsLookup(resolve_result); +} + +SocketCreateFunction::SocketCreateFunction() + : socket_type_(kSocketTypeInvalid) {} + +SocketCreateFunction::~SocketCreateFunction() {} + +bool SocketCreateFunction::Prepare() { + params_ = core_api::socket::Create::Params::Create(*args_); + EXTENSION_FUNCTION_VALIDATE(params_.get()); + + switch (params_->type) { + case extensions::core_api::socket::SOCKET_TYPE_TCP: + socket_type_ = kSocketTypeTCP; + break; + case extensions::core_api::socket::SOCKET_TYPE_UDP: + socket_type_ = kSocketTypeUDP; + break; + case extensions::core_api::socket::SOCKET_TYPE_NONE: + NOTREACHED(); + break; + } + + return true; +} + +void SocketCreateFunction::Work() { + Socket* socket = NULL; + if (socket_type_ == kSocketTypeTCP) { + socket = new TCPSocket(extension_->id()); + } else if (socket_type_ == kSocketTypeUDP) { + socket = new UDPSocket(extension_->id()); + } + DCHECK(socket); + + base::DictionaryValue* result = new base::DictionaryValue(); + result->SetInteger(kSocketIdKey, AddSocket(socket)); + SetResult(result); +} + +bool SocketDestroyFunction::Prepare() { + EXTENSION_FUNCTION_VALIDATE(args_->GetInteger(0, &socket_id_)); + return true; +} + +void SocketDestroyFunction::Work() { RemoveSocket(socket_id_); } + +SocketConnectFunction::SocketConnectFunction() + : socket_id_(0), hostname_(), port_(0), socket_(NULL) {} + +SocketConnectFunction::~SocketConnectFunction() {} + +bool SocketConnectFunction::Prepare() { + EXTENSION_FUNCTION_VALIDATE(args_->GetInteger(0, &socket_id_)); + EXTENSION_FUNCTION_VALIDATE(args_->GetString(1, &hostname_)); + EXTENSION_FUNCTION_VALIDATE(args_->GetInteger(2, &port_)); + return true; +} + +void SocketConnectFunction::AsyncWorkStart() { + socket_ = GetSocket(socket_id_); + if (!socket_) { + error_ = kSocketNotFoundError; + SetResult(new base::FundamentalValue(-1)); + AsyncWorkCompleted(); + return; + } + + SocketPermissionRequest::OperationType operation_type; + switch (socket_->GetSocketType()) { + case Socket::TYPE_TCP: + operation_type = SocketPermissionRequest::TCP_CONNECT; + break; + case Socket::TYPE_UDP: + operation_type = SocketPermissionRequest::UDP_SEND_TO; + break; + default: + NOTREACHED() << "Unknown socket type."; + operation_type = SocketPermissionRequest::NONE; + break; + } + + SocketPermission::CheckParam param(operation_type, hostname_, port_); + if (!PermissionsData::CheckAPIPermissionWithParam( + GetExtension(), APIPermission::kSocket, ¶m)) { + error_ = kPermissionError; + SetResult(new base::FundamentalValue(-1)); + AsyncWorkCompleted(); + return; + } + + StartDnsLookup(hostname_); +} + +void SocketConnectFunction::AfterDnsLookup(int lookup_result) { + if (lookup_result == net::OK) { + StartConnect(); + } else { + SetResult(new base::FundamentalValue(lookup_result)); + AsyncWorkCompleted(); + } +} + +void SocketConnectFunction::StartConnect() { + socket_->Connect(resolved_address_, + port_, + base::Bind(&SocketConnectFunction::OnConnect, this)); +} + +void SocketConnectFunction::OnConnect(int result) { + SetResult(new base::FundamentalValue(result)); + AsyncWorkCompleted(); +} + +bool SocketDisconnectFunction::Prepare() { + EXTENSION_FUNCTION_VALIDATE(args_->GetInteger(0, &socket_id_)); + return true; +} + +void SocketDisconnectFunction::Work() { + Socket* socket = GetSocket(socket_id_); + if (socket) + socket->Disconnect(); + else + error_ = kSocketNotFoundError; + SetResult(base::Value::CreateNullValue()); +} + +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 = GetSocket(socket_id_); + + if (!socket) { + error_ = kSocketNotFoundError; + SetResult(new base::FundamentalValue(result)); + return; + } + + if (socket->GetSocketType() == Socket::TYPE_UDP) { + SocketPermission::CheckParam param( + SocketPermissionRequest::UDP_BIND, address_, port_); + if (!PermissionsData::CheckAPIPermissionWithParam( + GetExtension(), APIPermission::kSocket, ¶m)) { + error_ = kPermissionError; + SetResult(new base::FundamentalValue(result)); + return; + } + } else if (socket->GetSocketType() == Socket::TYPE_TCP) { + error_ = kTCPSocketBindError; + SetResult(new base::FundamentalValue(result)); + return; + } + + result = socket->Bind(address_, port_); + SetResult(new base::FundamentalValue(result)); +} + +SocketListenFunction::SocketListenFunction() {} + +SocketListenFunction::~SocketListenFunction() {} + +bool SocketListenFunction::Prepare() { + params_ = core_api::socket::Listen::Params::Create(*args_); + EXTENSION_FUNCTION_VALIDATE(params_.get()); + return true; +} + +void SocketListenFunction::Work() { + int result = -1; + + Socket* socket = GetSocket(params_->socket_id); + if (socket) { + SocketPermission::CheckParam param( + SocketPermissionRequest::TCP_LISTEN, params_->address, params_->port); + if (!PermissionsData::CheckAPIPermissionWithParam( + GetExtension(), APIPermission::kSocket, ¶m)) { + error_ = kPermissionError; + SetResult(new base::FundamentalValue(result)); + return; + } + + result = + socket->Listen(params_->address, + params_->port, + params_->backlog.get() ? *params_->backlog.get() : 5, + &error_); + } else { + error_ = kSocketNotFoundError; + } + + SetResult(new base::FundamentalValue(result)); +} + +SocketAcceptFunction::SocketAcceptFunction() {} + +SocketAcceptFunction::~SocketAcceptFunction() {} + +bool SocketAcceptFunction::Prepare() { + params_ = core_api::socket::Accept::Params::Create(*args_); + EXTENSION_FUNCTION_VALIDATE(params_.get()); + return true; +} + +void SocketAcceptFunction::AsyncWorkStart() { + Socket* socket = GetSocket(params_->socket_id); + if (socket) { + socket->Accept(base::Bind(&SocketAcceptFunction::OnAccept, this)); + } else { + error_ = kSocketNotFoundError; + OnAccept(-1, NULL); + } +} + +void SocketAcceptFunction::OnAccept(int result_code, + net::TCPClientSocket* socket) { + base::DictionaryValue* result = new base::DictionaryValue(); + result->SetInteger(kResultCodeKey, result_code); + if (socket) { + Socket* client_socket = new TCPSocket(socket, extension_id(), true); + result->SetInteger(kSocketIdKey, AddSocket(client_socket)); + } + SetResult(result); + + AsyncWorkCompleted(); +} + +SocketReadFunction::SocketReadFunction() {} + +SocketReadFunction::~SocketReadFunction() {} + +bool SocketReadFunction::Prepare() { + params_ = core_api::socket::Read::Params::Create(*args_); + EXTENSION_FUNCTION_VALIDATE(params_.get()); + return true; +} + +void SocketReadFunction::AsyncWorkStart() { + Socket* socket = GetSocket(params_->socket_id); + if (!socket) { + error_ = kSocketNotFoundError; + OnCompleted(-1, NULL); + return; + } + + socket->Read(params_->buffer_size.get() ? *params_->buffer_size.get() : 4096, + base::Bind(&SocketReadFunction::OnCompleted, this)); +} + +void SocketReadFunction::OnCompleted(int bytes_read, + scoped_refptr<net::IOBuffer> io_buffer) { + base::DictionaryValue* result = new base::DictionaryValue(); + result->SetInteger(kResultCodeKey, bytes_read); + if (bytes_read > 0) { + result->Set(kDataKey, + base::BinaryValue::CreateWithCopiedBuffer(io_buffer->data(), + bytes_read)); + } else { + result->Set(kDataKey, new base::BinaryValue()); + } + SetResult(result); + + AsyncWorkCompleted(); +} + +SocketWriteFunction::SocketWriteFunction() + : socket_id_(0), io_buffer_(NULL), io_buffer_size_(0) {} + +SocketWriteFunction::~SocketWriteFunction() {} + +bool SocketWriteFunction::Prepare() { + EXTENSION_FUNCTION_VALIDATE(args_->GetInteger(0, &socket_id_)); + base::BinaryValue* data = NULL; + EXTENSION_FUNCTION_VALIDATE(args_->GetBinary(1, &data)); + + io_buffer_size_ = data->GetSize(); + io_buffer_ = new net::WrappedIOBuffer(data->GetBuffer()); + return true; +} + +void SocketWriteFunction::AsyncWorkStart() { + Socket* socket = GetSocket(socket_id_); + + if (!socket) { + error_ = kSocketNotFoundError; + OnCompleted(-1); + return; + } + + socket->Write(io_buffer_, + io_buffer_size_, + base::Bind(&SocketWriteFunction::OnCompleted, this)); +} + +void SocketWriteFunction::OnCompleted(int bytes_written) { + base::DictionaryValue* result = new base::DictionaryValue(); + result->SetInteger(kBytesWrittenKey, bytes_written); + SetResult(result); + + AsyncWorkCompleted(); +} + +SocketRecvFromFunction::SocketRecvFromFunction() {} + +SocketRecvFromFunction::~SocketRecvFromFunction() {} + +bool SocketRecvFromFunction::Prepare() { + params_ = core_api::socket::RecvFrom::Params::Create(*args_); + EXTENSION_FUNCTION_VALIDATE(params_.get()); + return true; +} + +void SocketRecvFromFunction::AsyncWorkStart() { + Socket* socket = GetSocket(params_->socket_id); + if (!socket) { + error_ = kSocketNotFoundError; + OnCompleted(-1, NULL, std::string(), 0); + return; + } + + socket->RecvFrom(params_->buffer_size.get() ? *params_->buffer_size : 4096, + base::Bind(&SocketRecvFromFunction::OnCompleted, this)); +} + +void SocketRecvFromFunction::OnCompleted(int bytes_read, + scoped_refptr<net::IOBuffer> io_buffer, + const std::string& address, + int port) { + base::DictionaryValue* result = new base::DictionaryValue(); + result->SetInteger(kResultCodeKey, bytes_read); + if (bytes_read > 0) { + result->Set(kDataKey, + base::BinaryValue::CreateWithCopiedBuffer(io_buffer->data(), + bytes_read)); + } else { + result->Set(kDataKey, new base::BinaryValue()); + } + result->SetString(kAddressKey, address); + result->SetInteger(kPortKey, port); + SetResult(result); + + AsyncWorkCompleted(); +} + +SocketSendToFunction::SocketSendToFunction() + : socket_id_(0), + io_buffer_(NULL), + io_buffer_size_(0), + port_(0), + socket_(NULL) {} + +SocketSendToFunction::~SocketSendToFunction() {} + +bool SocketSendToFunction::Prepare() { + EXTENSION_FUNCTION_VALIDATE(args_->GetInteger(0, &socket_id_)); + base::BinaryValue* data = NULL; + EXTENSION_FUNCTION_VALIDATE(args_->GetBinary(1, &data)); + EXTENSION_FUNCTION_VALIDATE(args_->GetString(2, &hostname_)); + EXTENSION_FUNCTION_VALIDATE(args_->GetInteger(3, &port_)); + + io_buffer_size_ = data->GetSize(); + io_buffer_ = new net::WrappedIOBuffer(data->GetBuffer()); + return true; +} + +void SocketSendToFunction::AsyncWorkStart() { + socket_ = GetSocket(socket_id_); + if (!socket_) { + error_ = kSocketNotFoundError; + SetResult(new base::FundamentalValue(-1)); + AsyncWorkCompleted(); + return; + } + + if (socket_->GetSocketType() == Socket::TYPE_UDP) { + SocketPermission::CheckParam param( + SocketPermissionRequest::UDP_SEND_TO, hostname_, port_); + if (!PermissionsData::CheckAPIPermissionWithParam( + GetExtension(), APIPermission::kSocket, ¶m)) { + error_ = kPermissionError; + SetResult(new base::FundamentalValue(-1)); + AsyncWorkCompleted(); + return; + } + } + + StartDnsLookup(hostname_); +} + +void SocketSendToFunction::AfterDnsLookup(int lookup_result) { + if (lookup_result == net::OK) { + StartSendTo(); + } else { + SetResult(new base::FundamentalValue(lookup_result)); + AsyncWorkCompleted(); + } +} + +void SocketSendToFunction::StartSendTo() { + socket_->SendTo(io_buffer_, + io_buffer_size_, + resolved_address_, + port_, + base::Bind(&SocketSendToFunction::OnCompleted, this)); +} + +void SocketSendToFunction::OnCompleted(int bytes_written) { + base::DictionaryValue* result = new base::DictionaryValue(); + result->SetInteger(kBytesWrittenKey, bytes_written); + SetResult(result); + + AsyncWorkCompleted(); +} + +SocketSetKeepAliveFunction::SocketSetKeepAliveFunction() {} + +SocketSetKeepAliveFunction::~SocketSetKeepAliveFunction() {} + +bool SocketSetKeepAliveFunction::Prepare() { + params_ = core_api::socket::SetKeepAlive::Params::Create(*args_); + EXTENSION_FUNCTION_VALIDATE(params_.get()); + return true; +} + +void SocketSetKeepAliveFunction::Work() { + bool result = false; + Socket* socket = GetSocket(params_->socket_id); + if (socket) { + int delay = 0; + if (params_->delay.get()) + delay = *params_->delay; + result = socket->SetKeepAlive(params_->enable, delay); + } else { + error_ = kSocketNotFoundError; + } + SetResult(new base::FundamentalValue(result)); +} + +SocketSetNoDelayFunction::SocketSetNoDelayFunction() {} + +SocketSetNoDelayFunction::~SocketSetNoDelayFunction() {} + +bool SocketSetNoDelayFunction::Prepare() { + params_ = core_api::socket::SetNoDelay::Params::Create(*args_); + EXTENSION_FUNCTION_VALIDATE(params_.get()); + return true; +} + +void SocketSetNoDelayFunction::Work() { + bool result = false; + Socket* socket = GetSocket(params_->socket_id); + if (socket) + result = socket->SetNoDelay(params_->no_delay); + else + error_ = kSocketNotFoundError; + SetResult(new base::FundamentalValue(result)); +} + +SocketGetInfoFunction::SocketGetInfoFunction() {} + +SocketGetInfoFunction::~SocketGetInfoFunction() {} + +bool SocketGetInfoFunction::Prepare() { + params_ = core_api::socket::GetInfo::Params::Create(*args_); + EXTENSION_FUNCTION_VALIDATE(params_.get()); + return true; +} + +void SocketGetInfoFunction::Work() { + Socket* socket = GetSocket(params_->socket_id); + if (!socket) { + error_ = kSocketNotFoundError; + return; + } + + core_api::socket::SocketInfo info; + // This represents what we know about the socket, and does not call through + // to the system. + if (socket->GetSocketType() == Socket::TYPE_TCP) + info.socket_type = extensions::core_api::socket::SOCKET_TYPE_TCP; + else + info.socket_type = extensions::core_api::socket::SOCKET_TYPE_UDP; + info.connected = socket->IsConnected(); + + // Grab the peer address as known by the OS. This and the call below will + // always succeed while the socket is connected, even if the socket has + // been remotely closed by the peer; only reading the socket will reveal + // that it should be closed locally. + net::IPEndPoint peerAddress; + if (socket->GetPeerAddress(&peerAddress)) { + info.peer_address.reset(new std::string(peerAddress.ToStringWithoutPort())); + info.peer_port.reset(new int(peerAddress.port())); + } + + // Grab the local address as known by the OS. + net::IPEndPoint localAddress; + if (socket->GetLocalAddress(&localAddress)) { + info.local_address.reset( + new std::string(localAddress.ToStringWithoutPort())); + info.local_port.reset(new int(localAddress.port())); + } + + SetResult(info.ToValue().release()); +} + +bool SocketGetNetworkListFunction::RunImpl() { + content::BrowserThread::PostTask( + content::BrowserThread::FILE, + FROM_HERE, + base::Bind(&SocketGetNetworkListFunction::GetNetworkListOnFileThread, + this)); + return true; +} + +void SocketGetNetworkListFunction::GetNetworkListOnFileThread() { + net::NetworkInterfaceList interface_list; + if (GetNetworkList(&interface_list, + net::INCLUDE_HOST_SCOPE_VIRTUAL_INTERFACES)) { + content::BrowserThread::PostTask( + content::BrowserThread::UI, + FROM_HERE, + base::Bind(&SocketGetNetworkListFunction::SendResponseOnUIThread, + this, + interface_list)); + return; + } + + content::BrowserThread::PostTask( + content::BrowserThread::UI, + FROM_HERE, + base::Bind(&SocketGetNetworkListFunction::HandleGetNetworkListError, + this)); +} + +void SocketGetNetworkListFunction::HandleGetNetworkListError() { + DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); + error_ = kNetworkListError; + SendResponse(false); +} + +void SocketGetNetworkListFunction::SendResponseOnUIThread( + const net::NetworkInterfaceList& interface_list) { + DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); + + std::vector<linked_ptr<core_api::socket::NetworkInterface> > create_arg; + create_arg.reserve(interface_list.size()); + for (net::NetworkInterfaceList::const_iterator i = interface_list.begin(); + i != interface_list.end(); + ++i) { + linked_ptr<core_api::socket::NetworkInterface> info = + make_linked_ptr(new core_api::socket::NetworkInterface); + info->name = i->name; + info->address = net::IPAddressToString(i->address); + info->prefix_length = i->network_prefix; + create_arg.push_back(info); + } + + results_ = core_api::socket::GetNetworkList::Results::Create(create_arg); + SendResponse(true); +} + +SocketJoinGroupFunction::SocketJoinGroupFunction() {} + +SocketJoinGroupFunction::~SocketJoinGroupFunction() {} + +bool SocketJoinGroupFunction::Prepare() { + params_ = core_api::socket::JoinGroup::Params::Create(*args_); + EXTENSION_FUNCTION_VALIDATE(params_.get()); + return true; +} + +void SocketJoinGroupFunction::Work() { + int result = -1; + Socket* socket = GetSocket(params_->socket_id); + if (!socket) { + error_ = kSocketNotFoundError; + SetResult(new base::FundamentalValue(result)); + return; + } + + if (socket->GetSocketType() != Socket::TYPE_UDP) { + error_ = kMulticastSocketTypeError; + SetResult(new base::FundamentalValue(result)); + return; + } + + SocketPermission::CheckParam param( + SocketPermissionRequest::UDP_MULTICAST_MEMBERSHIP, + kWildcardAddress, + kWildcardPort); + + if (!PermissionsData::CheckAPIPermissionWithParam( + GetExtension(), APIPermission::kSocket, ¶m)) { + error_ = kPermissionError; + SetResult(new base::FundamentalValue(result)); + return; + } + + result = static_cast<UDPSocket*>(socket)->JoinGroup(params_->address); + if (result != 0) { + error_ = net::ErrorToString(result); + } + SetResult(new base::FundamentalValue(result)); +} + +SocketLeaveGroupFunction::SocketLeaveGroupFunction() {} + +SocketLeaveGroupFunction::~SocketLeaveGroupFunction() {} + +bool SocketLeaveGroupFunction::Prepare() { + params_ = core_api::socket::LeaveGroup::Params::Create(*args_); + EXTENSION_FUNCTION_VALIDATE(params_.get()); + return true; +} + +void SocketLeaveGroupFunction::Work() { + int result = -1; + Socket* socket = GetSocket(params_->socket_id); + + if (!socket) { + error_ = kSocketNotFoundError; + SetResult(new base::FundamentalValue(result)); + return; + } + + if (socket->GetSocketType() != Socket::TYPE_UDP) { + error_ = kMulticastSocketTypeError; + SetResult(new base::FundamentalValue(result)); + return; + } + + SocketPermission::CheckParam param( + SocketPermissionRequest::UDP_MULTICAST_MEMBERSHIP, + kWildcardAddress, + kWildcardPort); + if (!PermissionsData::CheckAPIPermissionWithParam( + GetExtension(), APIPermission::kSocket, ¶m)) { + error_ = kPermissionError; + SetResult(new base::FundamentalValue(result)); + return; + } + + result = static_cast<UDPSocket*>(socket)->LeaveGroup(params_->address); + if (result != 0) + error_ = net::ErrorToString(result); + SetResult(new base::FundamentalValue(result)); +} + +SocketSetMulticastTimeToLiveFunction::SocketSetMulticastTimeToLiveFunction() {} + +SocketSetMulticastTimeToLiveFunction::~SocketSetMulticastTimeToLiveFunction() {} + +bool SocketSetMulticastTimeToLiveFunction::Prepare() { + params_ = core_api::socket::SetMulticastTimeToLive::Params::Create(*args_); + EXTENSION_FUNCTION_VALIDATE(params_.get()); + return true; +} +void SocketSetMulticastTimeToLiveFunction::Work() { + int result = -1; + Socket* socket = GetSocket(params_->socket_id); + if (!socket) { + error_ = kSocketNotFoundError; + SetResult(new base::FundamentalValue(result)); + return; + } + + if (socket->GetSocketType() != Socket::TYPE_UDP) { + error_ = kMulticastSocketTypeError; + SetResult(new base::FundamentalValue(result)); + return; + } + + result = + static_cast<UDPSocket*>(socket)->SetMulticastTimeToLive(params_->ttl); + if (result != 0) + error_ = net::ErrorToString(result); + SetResult(new base::FundamentalValue(result)); +} + +SocketSetMulticastLoopbackModeFunction:: + SocketSetMulticastLoopbackModeFunction() {} + +SocketSetMulticastLoopbackModeFunction:: + ~SocketSetMulticastLoopbackModeFunction() {} + +bool SocketSetMulticastLoopbackModeFunction::Prepare() { + params_ = core_api::socket::SetMulticastLoopbackMode::Params::Create(*args_); + EXTENSION_FUNCTION_VALIDATE(params_.get()); + return true; +} + +void SocketSetMulticastLoopbackModeFunction::Work() { + int result = -1; + Socket* socket = GetSocket(params_->socket_id); + if (!socket) { + error_ = kSocketNotFoundError; + SetResult(new base::FundamentalValue(result)); + return; + } + + if (socket->GetSocketType() != Socket::TYPE_UDP) { + error_ = kMulticastSocketTypeError; + SetResult(new base::FundamentalValue(result)); + return; + } + + result = static_cast<UDPSocket*>(socket) + ->SetMulticastLoopbackMode(params_->enabled); + if (result != 0) + error_ = net::ErrorToString(result); + SetResult(new base::FundamentalValue(result)); +} + +SocketGetJoinedGroupsFunction::SocketGetJoinedGroupsFunction() {} + +SocketGetJoinedGroupsFunction::~SocketGetJoinedGroupsFunction() {} + +bool SocketGetJoinedGroupsFunction::Prepare() { + params_ = core_api::socket::GetJoinedGroups::Params::Create(*args_); + EXTENSION_FUNCTION_VALIDATE(params_.get()); + return true; +} + +void SocketGetJoinedGroupsFunction::Work() { + int result = -1; + Socket* socket = GetSocket(params_->socket_id); + if (!socket) { + error_ = kSocketNotFoundError; + SetResult(new base::FundamentalValue(result)); + return; + } + + if (socket->GetSocketType() != Socket::TYPE_UDP) { + error_ = kMulticastSocketTypeError; + SetResult(new base::FundamentalValue(result)); + return; + } + + SocketPermission::CheckParam param( + SocketPermissionRequest::UDP_MULTICAST_MEMBERSHIP, + kWildcardAddress, + kWildcardPort); + if (!PermissionsData::CheckAPIPermissionWithParam( + GetExtension(), APIPermission::kSocket, ¶m)) { + error_ = kPermissionError; + SetResult(new base::FundamentalValue(result)); + return; + } + + base::ListValue* values = new base::ListValue(); + values->AppendStrings((std::vector<std::string>&)static_cast<UDPSocket*>( + socket)->GetJoinedGroups()); + SetResult(values); +} + +} // namespace extensions diff --git a/extensions/browser/api/socket/socket_api.h b/extensions/browser/api/socket/socket_api.h new file mode 100644 index 0000000..60bd765 --- /dev/null +++ b/extensions/browser/api/socket/socket_api.h @@ -0,0 +1,507 @@ +// Copyright 2014 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 EXTENSIONS_BROWSER_API_SOCKET_SOCKET_API_H_ +#define EXTENSIONS_BROWSER_API_SOCKET_SOCKET_API_H_ + +#include <string> + +#include "base/gtest_prod_util.h" +#include "base/memory/ref_counted.h" +#include "extensions/browser/api/api_resource_manager.h" +#include "extensions/browser/api/async_api_function.h" +#include "extensions/browser/extension_function.h" +#include "extensions/common/api/socket.h" +#include "net/base/address_list.h" +#include "net/dns/host_resolver.h" +#include "net/socket/tcp_client_socket.h" + +namespace content { +class BrowserContext; +class ResourceContext; +} + +namespace net { +class IOBuffer; +} + +namespace extensions { + +class Socket; + +// A simple interface to ApiResourceManager<Socket> or derived class. The goal +// of this interface is to allow Socket API functions to use distinct instances +// of ApiResourceManager<> depending on the type of socket (old version in +// "socket" namespace vs new version in "socket.xxx" namespaces). +class SocketResourceManagerInterface { + public: + virtual ~SocketResourceManagerInterface() {} + + virtual bool SetBrowserContext(content::BrowserContext* context) = 0; + virtual int Add(Socket* socket) = 0; + virtual Socket* Get(const std::string& extension_id, int api_resource_id) = 0; + virtual void Remove(const std::string& extension_id, int api_resource_id) = 0; + virtual base::hash_set<int>* GetResourceIds( + const std::string& extension_id) = 0; +}; + +// Implementation of SocketResourceManagerInterface using an +// ApiResourceManager<T> instance (where T derives from Socket). +template <typename T> +class SocketResourceManager : public SocketResourceManagerInterface { + public: + SocketResourceManager() : manager_(NULL) {} + + virtual bool SetBrowserContext(content::BrowserContext* context) OVERRIDE { + manager_ = ApiResourceManager<T>::Get(context); + 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<Socket>."; + return manager_ != NULL; + } + + virtual int Add(Socket* socket) OVERRIDE { + // Note: Cast needed here, because "T" may be a subclass of "Socket". + return manager_->Add(static_cast<T*>(socket)); + } + + virtual Socket* Get(const std::string& extension_id, + int api_resource_id) OVERRIDE { + return manager_->Get(extension_id, api_resource_id); + } + + virtual void Remove(const std::string& extension_id, + int api_resource_id) OVERRIDE { + manager_->Remove(extension_id, api_resource_id); + } + + virtual base::hash_set<int>* GetResourceIds(const std::string& extension_id) + OVERRIDE { + return manager_->GetResourceIds(extension_id); + } + + private: + ApiResourceManager<T>* manager_; +}; + +class SocketAsyncApiFunction : public AsyncApiFunction { + public: + SocketAsyncApiFunction(); + + protected: + virtual ~SocketAsyncApiFunction(); + + // AsyncApiFunction: + virtual bool PrePrepare() OVERRIDE; + virtual bool Respond() OVERRIDE; + + virtual scoped_ptr<SocketResourceManagerInterface> + CreateSocketResourceManager(); + + int AddSocket(Socket* socket); + Socket* GetSocket(int api_resource_id); + void RemoveSocket(int api_resource_id); + base::hash_set<int>* GetSocketIds(); + + private: + scoped_ptr<SocketResourceManagerInterface> manager_; +}; + +class SocketExtensionWithDnsLookupFunction : public SocketAsyncApiFunction { + protected: + SocketExtensionWithDnsLookupFunction(); + virtual ~SocketExtensionWithDnsLookupFunction(); + + // AsyncApiFunction: + virtual bool PrePrepare() OVERRIDE; + + void StartDnsLookup(const std::string& hostname); + virtual void AfterDnsLookup(int lookup_result) = 0; + + std::string resolved_address_; + + private: + void OnDnsLookup(int resolve_result); + + // Weak pointer to the resource context. + content::ResourceContext* resource_context_; + + scoped_ptr<net::HostResolver::RequestHandle> request_handle_; + scoped_ptr<net::AddressList> addresses_; +}; + +class SocketCreateFunction : public SocketAsyncApiFunction { + public: + DECLARE_EXTENSION_FUNCTION("socket.create", SOCKET_CREATE) + + SocketCreateFunction(); + + protected: + virtual ~SocketCreateFunction(); + + // AsyncApiFunction: + virtual bool Prepare() OVERRIDE; + virtual void Work() OVERRIDE; + + private: + FRIEND_TEST_ALL_PREFIXES(SocketUnitTest, Create); + enum SocketType { kSocketTypeInvalid = -1, kSocketTypeTCP, kSocketTypeUDP }; + + scoped_ptr<core_api::socket::Create::Params> params_; + SocketType socket_type_; +}; + +class SocketDestroyFunction : public SocketAsyncApiFunction { + public: + DECLARE_EXTENSION_FUNCTION("socket.destroy", SOCKET_DESTROY) + + protected: + virtual ~SocketDestroyFunction() {} + + // AsyncApiFunction: + virtual bool Prepare() OVERRIDE; + virtual void Work() OVERRIDE; + + private: + int socket_id_; +}; + +class SocketConnectFunction : public SocketExtensionWithDnsLookupFunction { + public: + DECLARE_EXTENSION_FUNCTION("socket.connect", SOCKET_CONNECT) + + SocketConnectFunction(); + + protected: + virtual ~SocketConnectFunction(); + + // AsyncApiFunction: + virtual bool Prepare() OVERRIDE; + virtual void AsyncWorkStart() OVERRIDE; + + // SocketExtensionWithDnsLookupFunction: + virtual void AfterDnsLookup(int lookup_result) OVERRIDE; + + private: + void StartConnect(); + void OnConnect(int result); + + int socket_id_; + std::string hostname_; + int port_; + Socket* socket_; +}; + +class SocketDisconnectFunction : public SocketAsyncApiFunction { + public: + DECLARE_EXTENSION_FUNCTION("socket.disconnect", SOCKET_DISCONNECT) + + protected: + virtual ~SocketDisconnectFunction() {} + + // AsyncApiFunction: + virtual bool Prepare() OVERRIDE; + virtual void Work() OVERRIDE; + + private: + int socket_id_; +}; + +class SocketBindFunction : public SocketAsyncApiFunction { + public: + DECLARE_EXTENSION_FUNCTION("socket.bind", SOCKET_BIND) + + protected: + virtual ~SocketBindFunction() {} + + // AsyncApiFunction: + virtual bool Prepare() OVERRIDE; + virtual void Work() OVERRIDE; + + private: + int socket_id_; + std::string address_; + int port_; +}; + +class SocketListenFunction : public SocketAsyncApiFunction { + public: + DECLARE_EXTENSION_FUNCTION("socket.listen", SOCKET_LISTEN) + + SocketListenFunction(); + + protected: + virtual ~SocketListenFunction(); + + // AsyncApiFunction: + virtual bool Prepare() OVERRIDE; + virtual void Work() OVERRIDE; + + private: + scoped_ptr<core_api::socket::Listen::Params> params_; +}; + +class SocketAcceptFunction : public SocketAsyncApiFunction { + public: + DECLARE_EXTENSION_FUNCTION("socket.accept", SOCKET_ACCEPT) + + SocketAcceptFunction(); + + protected: + virtual ~SocketAcceptFunction(); + + // AsyncApiFunction: + virtual bool Prepare() OVERRIDE; + virtual void AsyncWorkStart() OVERRIDE; + + private: + void OnAccept(int result_code, net::TCPClientSocket* socket); + + scoped_ptr<core_api::socket::Accept::Params> params_; +}; + +class SocketReadFunction : public SocketAsyncApiFunction { + public: + DECLARE_EXTENSION_FUNCTION("socket.read", SOCKET_READ) + + SocketReadFunction(); + + protected: + virtual ~SocketReadFunction(); + + // AsyncApiFunction: + virtual bool Prepare() OVERRIDE; + virtual void AsyncWorkStart() OVERRIDE; + void OnCompleted(int result, scoped_refptr<net::IOBuffer> io_buffer); + + private: + scoped_ptr<core_api::socket::Read::Params> params_; +}; + +class SocketWriteFunction : public SocketAsyncApiFunction { + public: + DECLARE_EXTENSION_FUNCTION("socket.write", SOCKET_WRITE) + + SocketWriteFunction(); + + protected: + virtual ~SocketWriteFunction(); + + // AsyncApiFunction: + virtual bool Prepare() OVERRIDE; + virtual void AsyncWorkStart() OVERRIDE; + void OnCompleted(int result); + + private: + int socket_id_; + scoped_refptr<net::IOBuffer> io_buffer_; + size_t io_buffer_size_; +}; + +class SocketRecvFromFunction : public SocketAsyncApiFunction { + public: + DECLARE_EXTENSION_FUNCTION("socket.recvFrom", SOCKET_RECVFROM) + + SocketRecvFromFunction(); + + protected: + virtual ~SocketRecvFromFunction(); + + // AsyncApiFunction + virtual bool Prepare() OVERRIDE; + virtual void AsyncWorkStart() OVERRIDE; + void OnCompleted(int result, + scoped_refptr<net::IOBuffer> io_buffer, + const std::string& address, + int port); + + private: + scoped_ptr<core_api::socket::RecvFrom::Params> params_; +}; + +class SocketSendToFunction : public SocketExtensionWithDnsLookupFunction { + public: + DECLARE_EXTENSION_FUNCTION("socket.sendTo", SOCKET_SENDTO) + + SocketSendToFunction(); + + protected: + virtual ~SocketSendToFunction(); + + // AsyncApiFunction: + virtual bool Prepare() OVERRIDE; + virtual void AsyncWorkStart() OVERRIDE; + void OnCompleted(int result); + + // SocketExtensionWithDnsLookupFunction: + virtual void AfterDnsLookup(int lookup_result) OVERRIDE; + + private: + void StartSendTo(); + + int socket_id_; + scoped_refptr<net::IOBuffer> io_buffer_; + size_t io_buffer_size_; + std::string hostname_; + int port_; + Socket* socket_; +}; + +class SocketSetKeepAliveFunction : public SocketAsyncApiFunction { + public: + DECLARE_EXTENSION_FUNCTION("socket.setKeepAlive", SOCKET_SETKEEPALIVE) + + SocketSetKeepAliveFunction(); + + protected: + virtual ~SocketSetKeepAliveFunction(); + + // AsyncApiFunction: + virtual bool Prepare() OVERRIDE; + virtual void Work() OVERRIDE; + + private: + scoped_ptr<core_api::socket::SetKeepAlive::Params> params_; +}; + +class SocketSetNoDelayFunction : public SocketAsyncApiFunction { + public: + DECLARE_EXTENSION_FUNCTION("socket.setNoDelay", SOCKET_SETNODELAY) + + SocketSetNoDelayFunction(); + + protected: + virtual ~SocketSetNoDelayFunction(); + + // AsyncApiFunction: + virtual bool Prepare() OVERRIDE; + virtual void Work() OVERRIDE; + + private: + scoped_ptr<core_api::socket::SetNoDelay::Params> params_; +}; + +class SocketGetInfoFunction : public SocketAsyncApiFunction { + public: + DECLARE_EXTENSION_FUNCTION("socket.getInfo", SOCKET_GETINFO) + + SocketGetInfoFunction(); + + protected: + virtual ~SocketGetInfoFunction(); + + // AsyncApiFunction: + virtual bool Prepare() OVERRIDE; + virtual void Work() OVERRIDE; + + private: + scoped_ptr<core_api::socket::GetInfo::Params> params_; +}; + +class SocketGetNetworkListFunction : public AsyncExtensionFunction { + public: + DECLARE_EXTENSION_FUNCTION("socket.getNetworkList", SOCKET_GETNETWORKLIST) + + protected: + virtual ~SocketGetNetworkListFunction() {} + virtual bool RunImpl() OVERRIDE; + + private: + void GetNetworkListOnFileThread(); + void HandleGetNetworkListError(); + void SendResponseOnUIThread(const net::NetworkInterfaceList& interface_list); +}; + +class SocketJoinGroupFunction : public SocketAsyncApiFunction { + public: + DECLARE_EXTENSION_FUNCTION("socket.joinGroup", SOCKET_MULTICAST_JOIN_GROUP) + + SocketJoinGroupFunction(); + + protected: + virtual ~SocketJoinGroupFunction(); + + // AsyncApiFunction + virtual bool Prepare() OVERRIDE; + virtual void Work() OVERRIDE; + + private: + scoped_ptr<core_api::socket::JoinGroup::Params> params_; +}; + +class SocketLeaveGroupFunction : public SocketAsyncApiFunction { + public: + DECLARE_EXTENSION_FUNCTION("socket.leaveGroup", SOCKET_MULTICAST_LEAVE_GROUP) + + SocketLeaveGroupFunction(); + + protected: + virtual ~SocketLeaveGroupFunction(); + + // AsyncApiFunction + virtual bool Prepare() OVERRIDE; + virtual void Work() OVERRIDE; + + private: + scoped_ptr<core_api::socket::LeaveGroup::Params> params_; +}; + +class SocketSetMulticastTimeToLiveFunction : public SocketAsyncApiFunction { + public: + DECLARE_EXTENSION_FUNCTION("socket.setMulticastTimeToLive", + SOCKET_MULTICAST_SET_TIME_TO_LIVE) + + SocketSetMulticastTimeToLiveFunction(); + + protected: + virtual ~SocketSetMulticastTimeToLiveFunction(); + + // AsyncApiFunction + virtual bool Prepare() OVERRIDE; + virtual void Work() OVERRIDE; + + private: + scoped_ptr<core_api::socket::SetMulticastTimeToLive::Params> params_; +}; + +class SocketSetMulticastLoopbackModeFunction : public SocketAsyncApiFunction { + public: + DECLARE_EXTENSION_FUNCTION("socket.setMulticastLoopbackMode", + SOCKET_MULTICAST_SET_LOOPBACK_MODE) + + SocketSetMulticastLoopbackModeFunction(); + + protected: + virtual ~SocketSetMulticastLoopbackModeFunction(); + + // AsyncApiFunction + virtual bool Prepare() OVERRIDE; + virtual void Work() OVERRIDE; + + private: + scoped_ptr<core_api::socket::SetMulticastLoopbackMode::Params> params_; +}; + +class SocketGetJoinedGroupsFunction : public SocketAsyncApiFunction { + public: + DECLARE_EXTENSION_FUNCTION("socket.getJoinedGroups", + SOCKET_MULTICAST_GET_JOINED_GROUPS) + + SocketGetJoinedGroupsFunction(); + + protected: + virtual ~SocketGetJoinedGroupsFunction(); + + // AsyncApiFunction + virtual bool Prepare() OVERRIDE; + virtual void Work() OVERRIDE; + + private: + scoped_ptr<core_api::socket::GetJoinedGroups::Params> params_; +}; +} // namespace extensions + +#endif // EXTENSIONS_BROWSER_API_SOCKET_SOCKET_API_H_ diff --git a/extensions/browser/api/socket/tcp_socket.cc b/extensions/browser/api/socket/tcp_socket.cc new file mode 100644 index 0000000..49f17dc --- /dev/null +++ b/extensions/browser/api/socket/tcp_socket.cc @@ -0,0 +1,329 @@ +// Copyright 2014 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 "extensions/browser/api/socket/tcp_socket.h" + +#include "extensions/browser/api/api_resource.h" +#include "net/base/address_list.h" +#include "net/base/ip_endpoint.h" +#include "net/base/net_errors.h" +#include "net/base/rand_callback.h" +#include "net/socket/tcp_client_socket.h" + +namespace extensions { + +const char kTCPSocketTypeInvalidError[] = + "Cannot call both connect and listen on the same socket."; +const char kSocketListenError[] = "Could not listen on the specified port."; + +static base::LazyInstance< + BrowserContextKeyedAPIFactory<ApiResourceManager<ResumableTCPSocket> > > + g_factory = LAZY_INSTANCE_INITIALIZER; + +// static +template <> +BrowserContextKeyedAPIFactory<ApiResourceManager<ResumableTCPSocket> >* +ApiResourceManager<ResumableTCPSocket>::GetFactoryInstance() { + return g_factory.Pointer(); +} + +static base::LazyInstance<BrowserContextKeyedAPIFactory< + ApiResourceManager<ResumableTCPServerSocket> > > g_server_factory = + LAZY_INSTANCE_INITIALIZER; + +// static +template <> +BrowserContextKeyedAPIFactory<ApiResourceManager<ResumableTCPServerSocket> >* +ApiResourceManager<ResumableTCPServerSocket>::GetFactoryInstance() { + return g_server_factory.Pointer(); +} + +TCPSocket::TCPSocket(const std::string& owner_extension_id) + : Socket(owner_extension_id), socket_mode_(UNKNOWN) {} + +TCPSocket::TCPSocket(net::TCPClientSocket* tcp_client_socket, + const std::string& owner_extension_id, + bool is_connected) + : Socket(owner_extension_id), + socket_(tcp_client_socket), + socket_mode_(CLIENT) { + this->is_connected_ = is_connected; +} + +TCPSocket::TCPSocket(net::TCPServerSocket* tcp_server_socket, + const std::string& owner_extension_id) + : Socket(owner_extension_id), + server_socket_(tcp_server_socket), + socket_mode_(SERVER) {} + +// static +TCPSocket* TCPSocket::CreateSocketForTesting( + net::TCPClientSocket* tcp_client_socket, + const std::string& owner_extension_id, + bool is_connected) { + return new TCPSocket(tcp_client_socket, owner_extension_id, is_connected); +} + +// static +TCPSocket* TCPSocket::CreateServerSocketForTesting( + net::TCPServerSocket* tcp_server_socket, + const std::string& owner_extension_id) { + return new TCPSocket(tcp_server_socket, owner_extension_id); +} + +TCPSocket::~TCPSocket() { Disconnect(); } + +void TCPSocket::Connect(const std::string& address, + int port, + const CompletionCallback& callback) { + DCHECK(!callback.is_null()); + + if (socket_mode_ == SERVER || !connect_callback_.is_null()) { + callback.Run(net::ERR_CONNECTION_FAILED); + return; + } + DCHECK(!server_socket_.get()); + socket_mode_ = CLIENT; + connect_callback_ = callback; + + int result = net::ERR_CONNECTION_FAILED; + do { + if (is_connected_) + break; + + net::AddressList address_list; + if (!StringAndPortToAddressList(address, port, &address_list)) { + result = net::ERR_ADDRESS_INVALID; + break; + } + + socket_.reset( + new net::TCPClientSocket(address_list, NULL, net::NetLog::Source())); + + connect_callback_ = callback; + result = socket_->Connect( + base::Bind(&TCPSocket::OnConnectComplete, base::Unretained(this))); + } while (false); + + if (result != net::ERR_IO_PENDING) + OnConnectComplete(result); +} + +void TCPSocket::Disconnect() { + is_connected_ = false; + if (socket_.get()) + socket_->Disconnect(); + server_socket_.reset(NULL); + connect_callback_.Reset(); + read_callback_.Reset(); + accept_callback_.Reset(); + accept_socket_.reset(NULL); +} + +int TCPSocket::Bind(const std::string& address, int port) { + return net::ERR_FAILED; +} + +void TCPSocket::Read(int count, const ReadCompletionCallback& callback) { + DCHECK(!callback.is_null()); + + if (socket_mode_ != CLIENT) { + callback.Run(net::ERR_FAILED, NULL); + return; + } + + if (!read_callback_.is_null()) { + callback.Run(net::ERR_IO_PENDING, NULL); + return; + } + + if (count < 0) { + callback.Run(net::ERR_INVALID_ARGUMENT, NULL); + return; + } + + if (!socket_.get() || !IsConnected()) { + callback.Run(net::ERR_SOCKET_NOT_CONNECTED, NULL); + return; + } + + read_callback_ = callback; + scoped_refptr<net::IOBuffer> io_buffer = new net::IOBuffer(count); + int result = socket_->Read( + io_buffer.get(), + count, + base::Bind( + &TCPSocket::OnReadComplete, base::Unretained(this), io_buffer)); + + if (result != net::ERR_IO_PENDING) + OnReadComplete(io_buffer, result); +} + +void TCPSocket::RecvFrom(int count, + const RecvFromCompletionCallback& callback) { + callback.Run(net::ERR_FAILED, NULL, NULL, 0); +} + +void TCPSocket::SendTo(scoped_refptr<net::IOBuffer> io_buffer, + int byte_count, + const std::string& address, + int port, + const CompletionCallback& callback) { + callback.Run(net::ERR_FAILED); +} + +bool TCPSocket::SetKeepAlive(bool enable, int delay) { + if (!socket_.get()) + return false; + return socket_->SetKeepAlive(enable, delay); +} + +bool TCPSocket::SetNoDelay(bool no_delay) { + if (!socket_.get()) + return false; + return socket_->SetNoDelay(no_delay); +} + +int TCPSocket::Listen(const std::string& address, + int port, + int backlog, + std::string* error_msg) { + if (socket_mode_ == CLIENT) { + *error_msg = kTCPSocketTypeInvalidError; + return net::ERR_NOT_IMPLEMENTED; + } + DCHECK(!socket_.get()); + socket_mode_ = SERVER; + + scoped_ptr<net::IPEndPoint> bind_address(new net::IPEndPoint()); + if (!StringAndPortToIPEndPoint(address, port, bind_address.get())) + return net::ERR_INVALID_ARGUMENT; + + if (!server_socket_.get()) { + server_socket_.reset(new net::TCPServerSocket(NULL, net::NetLog::Source())); + } + int result = server_socket_->Listen(*bind_address, backlog); + if (result) + *error_msg = kSocketListenError; + return result; +} + +void TCPSocket::Accept(const AcceptCompletionCallback& callback) { + if (socket_mode_ != SERVER || !server_socket_.get()) { + callback.Run(net::ERR_FAILED, NULL); + return; + } + + // Limits to only 1 blocked accept call. + if (!accept_callback_.is_null()) { + callback.Run(net::ERR_FAILED, NULL); + return; + } + + int result = server_socket_->Accept( + &accept_socket_, + base::Bind(&TCPSocket::OnAccept, base::Unretained(this))); + if (result == net::ERR_IO_PENDING) { + accept_callback_ = callback; + } else if (result == net::OK) { + accept_callback_ = callback; + this->OnAccept(result); + } else { + callback.Run(result, NULL); + } +} + +bool TCPSocket::IsConnected() { + RefreshConnectionStatus(); + return is_connected_; +} + +bool TCPSocket::GetPeerAddress(net::IPEndPoint* address) { + if (!socket_.get()) + return false; + return !socket_->GetPeerAddress(address); +} + +bool TCPSocket::GetLocalAddress(net::IPEndPoint* address) { + if (socket_.get()) { + return !socket_->GetLocalAddress(address); + } else if (server_socket_.get()) { + return !server_socket_->GetLocalAddress(address); + } else { + return false; + } +} + +Socket::SocketType TCPSocket::GetSocketType() const { return Socket::TYPE_TCP; } + +int TCPSocket::WriteImpl(net::IOBuffer* io_buffer, + int io_buffer_size, + const net::CompletionCallback& callback) { + if (socket_mode_ != CLIENT) + return net::ERR_FAILED; + else if (!socket_.get() || !IsConnected()) + return net::ERR_SOCKET_NOT_CONNECTED; + else + return socket_->Write(io_buffer, io_buffer_size, callback); +} + +void TCPSocket::RefreshConnectionStatus() { + if (!is_connected_) + return; + if (server_socket_) + return; + if (!socket_->IsConnected()) { + Disconnect(); + } +} + +void TCPSocket::OnConnectComplete(int result) { + DCHECK(!connect_callback_.is_null()); + DCHECK(!is_connected_); + is_connected_ = result == net::OK; + connect_callback_.Run(result); + connect_callback_.Reset(); +} + +void TCPSocket::OnReadComplete(scoped_refptr<net::IOBuffer> io_buffer, + int result) { + DCHECK(!read_callback_.is_null()); + read_callback_.Run(result, io_buffer); + read_callback_.Reset(); +} + +void TCPSocket::OnAccept(int result) { + DCHECK(!accept_callback_.is_null()); + if (result == net::OK && accept_socket_.get()) { + accept_callback_.Run( + result, static_cast<net::TCPClientSocket*>(accept_socket_.release())); + } else { + accept_callback_.Run(result, NULL); + } + accept_callback_.Reset(); +} + +ResumableTCPSocket::ResumableTCPSocket(const std::string& owner_extension_id) + : TCPSocket(owner_extension_id), + persistent_(false), + buffer_size_(0), + paused_(false) {} + +ResumableTCPSocket::ResumableTCPSocket(net::TCPClientSocket* tcp_client_socket, + const std::string& owner_extension_id, + bool is_connected) + : TCPSocket(tcp_client_socket, owner_extension_id, is_connected), + persistent_(false), + buffer_size_(0), + paused_(false) {} + +bool ResumableTCPSocket::IsPersistent() const { return persistent(); } + +ResumableTCPServerSocket::ResumableTCPServerSocket( + const std::string& owner_extension_id) + : TCPSocket(owner_extension_id), persistent_(false), paused_(false) {} + +bool ResumableTCPServerSocket::IsPersistent() const { return persistent(); } + +} // namespace extensions diff --git a/extensions/browser/api/socket/tcp_socket.h b/extensions/browser/api/socket/tcp_socket.h new file mode 100644 index 0000000..fbb8c54 --- /dev/null +++ b/extensions/browser/api/socket/tcp_socket.h @@ -0,0 +1,173 @@ +// Copyright 2014 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 EXTENSIONS_BROWSER_API_SOCKET_TCP_SOCKET_H_ +#define EXTENSIONS_BROWSER_API_SOCKET_TCP_SOCKET_H_ + +#include <string> + +#include "extensions/browser/api/socket/socket.h" + +// This looks like it should be forward-declarable, but it does some tricky +// moves that make it easier to just include it. +#include "net/socket/tcp_client_socket.h" +#include "net/socket/tcp_server_socket.h" + +namespace net { +class Socket; +} + +namespace extensions { + +class TCPSocket : public Socket { + public: + explicit TCPSocket(const std::string& owner_extension_id); + TCPSocket(net::TCPClientSocket* tcp_client_socket, + const std::string& owner_extension_id, + bool is_connected = false); + + virtual ~TCPSocket(); + + virtual void Connect(const std::string& address, + int port, + const CompletionCallback& callback) OVERRIDE; + virtual void Disconnect() OVERRIDE; + virtual int Bind(const std::string& address, int port) OVERRIDE; + virtual void Read(int count, const ReadCompletionCallback& callback) OVERRIDE; + virtual void RecvFrom(int count, + const RecvFromCompletionCallback& callback) OVERRIDE; + virtual void SendTo(scoped_refptr<net::IOBuffer> io_buffer, + int byte_count, + const std::string& address, + int port, + const CompletionCallback& callback) OVERRIDE; + virtual bool SetKeepAlive(bool enable, int delay) OVERRIDE; + virtual bool SetNoDelay(bool no_delay) OVERRIDE; + virtual int Listen(const std::string& address, + int port, + int backlog, + std::string* error_msg) OVERRIDE; + virtual void Accept(const AcceptCompletionCallback& callback) OVERRIDE; + + virtual bool IsConnected() OVERRIDE; + + virtual bool GetPeerAddress(net::IPEndPoint* address) OVERRIDE; + virtual bool GetLocalAddress(net::IPEndPoint* address) OVERRIDE; + virtual Socket::SocketType GetSocketType() const OVERRIDE; + + static TCPSocket* CreateSocketForTesting( + net::TCPClientSocket* tcp_client_socket, + const std::string& owner_extension_id, + bool is_connected = false); + static TCPSocket* CreateServerSocketForTesting( + net::TCPServerSocket* tcp_server_socket, + const std::string& owner_extension_id); + + protected: + virtual int WriteImpl(net::IOBuffer* io_buffer, + int io_buffer_size, + const net::CompletionCallback& callback) OVERRIDE; + + private: + void RefreshConnectionStatus(); + void OnConnectComplete(int result); + void OnReadComplete(scoped_refptr<net::IOBuffer> io_buffer, int result); + void OnAccept(int result); + + TCPSocket(net::TCPServerSocket* tcp_server_socket, + const std::string& owner_extension_id); + + scoped_ptr<net::TCPClientSocket> socket_; + scoped_ptr<net::TCPServerSocket> server_socket_; + + enum SocketMode { UNKNOWN = 0, CLIENT, SERVER, }; + SocketMode socket_mode_; + + CompletionCallback connect_callback_; + + ReadCompletionCallback read_callback_; + + scoped_ptr<net::StreamSocket> accept_socket_; + AcceptCompletionCallback accept_callback_; +}; + +// TCP Socket instances from the "sockets.tcp" namespace. These are regular +// socket objects with additional properties related to the behavior defined in +// the "sockets.tcp" namespace. +class ResumableTCPSocket : public TCPSocket { + public: + explicit ResumableTCPSocket(const std::string& owner_extension_id); + explicit ResumableTCPSocket(net::TCPClientSocket* tcp_client_socket, + const std::string& owner_extension_id, + bool is_connected); + + // Overriden from ApiResource + virtual bool IsPersistent() const OVERRIDE; + + const std::string& name() const { return name_; } + void set_name(const std::string& name) { name_ = name; } + + bool persistent() const { return persistent_; } + void set_persistent(bool persistent) { persistent_ = persistent; } + + int buffer_size() const { return buffer_size_; } + void set_buffer_size(int buffer_size) { buffer_size_ = buffer_size; } + + bool paused() const { return paused_; } + void set_paused(bool paused) { paused_ = paused; } + + private: + friend class ApiResourceManager<ResumableTCPSocket>; + static const char* service_name() { return "ResumableTCPSocketManager"; } + + // Application-defined string - see sockets_tcp.idl. + std::string name_; + // Flag indicating whether the socket is left open when the application is + // suspended - see sockets_tcp.idl. + bool persistent_; + // The size of the buffer used to receive data - see sockets_tcp.idl. + int buffer_size_; + // Flag indicating whether a connected socket blocks its peer from sending + // more data - see sockets_tcp.idl. + bool paused_; +}; + +// TCP Socket instances from the "sockets.tcpServer" namespace. These are +// regular socket objects with additional properties related to the behavior +// defined in the "sockets.tcpServer" namespace. +class ResumableTCPServerSocket : public TCPSocket { + public: + explicit ResumableTCPServerSocket(const std::string& owner_extension_id); + + // Overriden from ApiResource + virtual bool IsPersistent() const OVERRIDE; + + const std::string& name() const { return name_; } + void set_name(const std::string& name) { name_ = name; } + + bool persistent() const { return persistent_; } + void set_persistent(bool persistent) { persistent_ = persistent; } + + bool paused() const { return paused_; } + void set_paused(bool paused) { paused_ = paused; } + + private: + friend class ApiResourceManager<ResumableTCPServerSocket>; + static const char* service_name() { + return "ResumableTCPServerSocketManager"; + } + + // Application-defined string - see sockets_tcp_server.idl. + std::string name_; + // Flag indicating whether the socket is left open when the application is + // suspended - see sockets_tcp_server.idl. + bool persistent_; + // Flag indicating whether a connected socket blocks its peer from sending + // more data - see sockets_tcp_server.idl. + bool paused_; +}; + +} // namespace extensions + +#endif // EXTENSIONS_BROWSER_API_SOCKET_TCP_SOCKET_H_ diff --git a/extensions/browser/api/socket/udp_socket.cc b/extensions/browser/api/socket/udp_socket.cc new file mode 100644 index 0000000..8e36500 --- /dev/null +++ b/extensions/browser/api/socket/udp_socket.cc @@ -0,0 +1,296 @@ +// Copyright 2014 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 "extensions/browser/api/socket/udp_socket.h" + +#include <algorithm> + +#include "extensions/browser/api/api_resource.h" +#include "net/base/ip_endpoint.h" +#include "net/base/net_errors.h" +#include "net/udp/datagram_socket.h" +#include "net/udp/udp_client_socket.h" + +namespace extensions { + +static base::LazyInstance< + BrowserContextKeyedAPIFactory<ApiResourceManager<ResumableUDPSocket> > > + g_factory = LAZY_INSTANCE_INITIALIZER; + +// static +template <> +BrowserContextKeyedAPIFactory<ApiResourceManager<ResumableUDPSocket> >* +ApiResourceManager<ResumableUDPSocket>::GetFactoryInstance() { + return g_factory.Pointer(); +} + +UDPSocket::UDPSocket(const std::string& owner_extension_id) + : Socket(owner_extension_id), + socket_(net::DatagramSocket::DEFAULT_BIND, + net::RandIntCallback(), + NULL, + net::NetLog::Source()) {} + +UDPSocket::~UDPSocket() { Disconnect(); } + +void UDPSocket::Connect(const std::string& address, + int port, + const CompletionCallback& callback) { + int result = net::ERR_CONNECTION_FAILED; + do { + if (is_connected_) + break; + + net::IPEndPoint ip_end_point; + if (!StringAndPortToIPEndPoint(address, port, &ip_end_point)) { + result = net::ERR_ADDRESS_INVALID; + break; + } + + result = socket_.Connect(ip_end_point); + is_connected_ = (result == net::OK); + } while (false); + + callback.Run(result); +} + +int UDPSocket::Bind(const std::string& address, int port) { + if (IsBound()) + return net::ERR_CONNECTION_FAILED; + + net::IPEndPoint ip_end_point; + if (!StringAndPortToIPEndPoint(address, port, &ip_end_point)) + return net::ERR_INVALID_ARGUMENT; + + return socket_.Bind(ip_end_point); +} + +void UDPSocket::Disconnect() { + is_connected_ = false; + socket_.Close(); + read_callback_.Reset(); + recv_from_callback_.Reset(); + send_to_callback_.Reset(); + multicast_groups_.clear(); +} + +void UDPSocket::Read(int count, const ReadCompletionCallback& callback) { + DCHECK(!callback.is_null()); + + if (!read_callback_.is_null()) { + callback.Run(net::ERR_IO_PENDING, NULL); + return; + } else { + read_callback_ = callback; + } + + int result = net::ERR_FAILED; + scoped_refptr<net::IOBuffer> io_buffer; + do { + if (count < 0) { + result = net::ERR_INVALID_ARGUMENT; + break; + } + + if (!socket_.is_connected()) { + result = net::ERR_SOCKET_NOT_CONNECTED; + break; + } + + io_buffer = new net::IOBuffer(count); + result = socket_.Read( + io_buffer.get(), + count, + base::Bind( + &UDPSocket::OnReadComplete, base::Unretained(this), io_buffer)); + } while (false); + + if (result != net::ERR_IO_PENDING) + OnReadComplete(io_buffer, result); +} + +int UDPSocket::WriteImpl(net::IOBuffer* io_buffer, + int io_buffer_size, + const net::CompletionCallback& callback) { + if (!socket_.is_connected()) + return net::ERR_SOCKET_NOT_CONNECTED; + else + return socket_.Write(io_buffer, io_buffer_size, callback); +} + +void UDPSocket::RecvFrom(int count, + const RecvFromCompletionCallback& callback) { + DCHECK(!callback.is_null()); + + if (!recv_from_callback_.is_null()) { + callback.Run(net::ERR_IO_PENDING, NULL, std::string(), 0); + return; + } else { + recv_from_callback_ = callback; + } + + int result = net::ERR_FAILED; + scoped_refptr<net::IOBuffer> io_buffer; + scoped_refptr<IPEndPoint> address; + do { + if (count < 0) { + result = net::ERR_INVALID_ARGUMENT; + break; + } + + if (!socket_.is_connected()) { + result = net::ERR_SOCKET_NOT_CONNECTED; + break; + } + + io_buffer = new net::IOBuffer(count); + address = new IPEndPoint(); + result = socket_.RecvFrom(io_buffer.get(), + count, + &address->data, + base::Bind(&UDPSocket::OnRecvFromComplete, + base::Unretained(this), + io_buffer, + address)); + } while (false); + + if (result != net::ERR_IO_PENDING) + OnRecvFromComplete(io_buffer, address, result); +} + +void UDPSocket::SendTo(scoped_refptr<net::IOBuffer> io_buffer, + int byte_count, + const std::string& address, + int port, + const CompletionCallback& callback) { + DCHECK(!callback.is_null()); + + if (!send_to_callback_.is_null()) { + // TODO(penghuang): Put requests in a pending queue to support multiple + // sendTo calls. + callback.Run(net::ERR_IO_PENDING); + return; + } else { + send_to_callback_ = callback; + } + + int result = net::ERR_FAILED; + do { + net::IPEndPoint ip_end_point; + if (!StringAndPortToIPEndPoint(address, port, &ip_end_point)) { + result = net::ERR_ADDRESS_INVALID; + break; + } + + if (!socket_.is_connected()) { + result = net::ERR_SOCKET_NOT_CONNECTED; + break; + } + + result = socket_.SendTo( + io_buffer.get(), + byte_count, + ip_end_point, + base::Bind(&UDPSocket::OnSendToComplete, base::Unretained(this))); + } while (false); + + if (result != net::ERR_IO_PENDING) + OnSendToComplete(result); +} + +bool UDPSocket::IsConnected() { return is_connected_; } + +bool UDPSocket::GetPeerAddress(net::IPEndPoint* address) { + return !socket_.GetPeerAddress(address); +} + +bool UDPSocket::GetLocalAddress(net::IPEndPoint* address) { + return !socket_.GetLocalAddress(address); +} + +Socket::SocketType UDPSocket::GetSocketType() const { return Socket::TYPE_UDP; } + +void UDPSocket::OnReadComplete(scoped_refptr<net::IOBuffer> io_buffer, + int result) { + DCHECK(!read_callback_.is_null()); + read_callback_.Run(result, io_buffer); + read_callback_.Reset(); +} + +void UDPSocket::OnRecvFromComplete(scoped_refptr<net::IOBuffer> io_buffer, + scoped_refptr<IPEndPoint> address, + int result) { + DCHECK(!recv_from_callback_.is_null()); + std::string ip; + int port = 0; + if (result > 0 && address.get()) { + IPEndPointToStringAndPort(address->data, &ip, &port); + } + recv_from_callback_.Run(result, io_buffer, ip, port); + recv_from_callback_.Reset(); +} + +void UDPSocket::OnSendToComplete(int result) { + DCHECK(!send_to_callback_.is_null()); + send_to_callback_.Run(result); + send_to_callback_.Reset(); +} + +bool UDPSocket::IsBound() { return socket_.is_connected(); } + +int UDPSocket::JoinGroup(const std::string& address) { + net::IPAddressNumber ip; + if (!net::ParseIPLiteralToNumber(address, &ip)) + return net::ERR_ADDRESS_INVALID; + + std::string normalized_address = net::IPAddressToString(ip); + std::vector<std::string>::iterator find_result = std::find( + multicast_groups_.begin(), multicast_groups_.end(), normalized_address); + if (find_result != multicast_groups_.end()) + return net::ERR_ADDRESS_INVALID; + + int rv = socket_.JoinGroup(ip); + if (rv == 0) + multicast_groups_.push_back(normalized_address); + return rv; +} + +int UDPSocket::LeaveGroup(const std::string& address) { + net::IPAddressNumber ip; + if (!net::ParseIPLiteralToNumber(address, &ip)) + return net::ERR_ADDRESS_INVALID; + + std::string normalized_address = net::IPAddressToString(ip); + std::vector<std::string>::iterator find_result = std::find( + multicast_groups_.begin(), multicast_groups_.end(), normalized_address); + if (find_result == multicast_groups_.end()) + return net::ERR_ADDRESS_INVALID; + + int rv = socket_.LeaveGroup(ip); + if (rv == 0) + multicast_groups_.erase(find_result); + return rv; +} + +int UDPSocket::SetMulticastTimeToLive(int ttl) { + return socket_.SetMulticastTimeToLive(ttl); +} + +int UDPSocket::SetMulticastLoopbackMode(bool loopback) { + return socket_.SetMulticastLoopbackMode(loopback); +} + +const std::vector<std::string>& UDPSocket::GetJoinedGroups() const { + return multicast_groups_; +} + +ResumableUDPSocket::ResumableUDPSocket(const std::string& owner_extension_id) + : UDPSocket(owner_extension_id), + persistent_(false), + buffer_size_(0), + paused_(false) {} + +bool ResumableUDPSocket::IsPersistent() const { return persistent(); } + +} // namespace extensions diff --git a/extensions/browser/api/socket/udp_socket.h b/extensions/browser/api/socket/udp_socket.h new file mode 100644 index 0000000..7c1450f --- /dev/null +++ b/extensions/browser/api/socket/udp_socket.h @@ -0,0 +1,117 @@ +// Copyright 2014 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 EXTENSIONS_BROWSER_API_SOCKET_UDP_SOCKET_H_ +#define EXTENSIONS_BROWSER_API_SOCKET_UDP_SOCKET_H_ + +#include <string> +#include <vector> + +#include "extensions/browser/api/socket/socket.h" +#include "net/udp/udp_socket.h" + +namespace extensions { + +class UDPSocket : public Socket { + public: + explicit UDPSocket(const std::string& owner_extension_id); + virtual ~UDPSocket(); + + virtual void Connect(const std::string& address, + int port, + const CompletionCallback& callback) OVERRIDE; + virtual void Disconnect() OVERRIDE; + virtual int Bind(const std::string& address, int port) OVERRIDE; + virtual void Read(int count, const ReadCompletionCallback& callback) OVERRIDE; + virtual void RecvFrom(int count, + const RecvFromCompletionCallback& callback) OVERRIDE; + virtual void SendTo(scoped_refptr<net::IOBuffer> io_buffer, + int byte_count, + const std::string& address, + int port, + const CompletionCallback& callback) OVERRIDE; + + virtual bool IsConnected() OVERRIDE; + + virtual bool GetPeerAddress(net::IPEndPoint* address) OVERRIDE; + virtual bool GetLocalAddress(net::IPEndPoint* address) OVERRIDE; + virtual Socket::SocketType GetSocketType() const OVERRIDE; + + bool IsBound(); + + int JoinGroup(const std::string& address); + int LeaveGroup(const std::string& address); + + int SetMulticastTimeToLive(int ttl); + int SetMulticastLoopbackMode(bool loopback); + + const std::vector<std::string>& GetJoinedGroups() const; + + protected: + virtual int WriteImpl(net::IOBuffer* io_buffer, + int io_buffer_size, + const net::CompletionCallback& callback) OVERRIDE; + + private: + // Make net::IPEndPoint can be refcounted + typedef base::RefCountedData<net::IPEndPoint> IPEndPoint; + + void OnReadComplete(scoped_refptr<net::IOBuffer> io_buffer, int result); + void OnRecvFromComplete(scoped_refptr<net::IOBuffer> io_buffer, + scoped_refptr<IPEndPoint> address, + int result); + void OnSendToComplete(int result); + + net::UDPSocket socket_; + + ReadCompletionCallback read_callback_; + + RecvFromCompletionCallback recv_from_callback_; + + CompletionCallback send_to_callback_; + + std::vector<std::string> multicast_groups_; +}; + +// UDP Socket instances from the "sockets.udp" namespace. These are regular +// socket objects with additional properties related to the behavior defined in +// the "sockets.udp" namespace. +class ResumableUDPSocket : public UDPSocket { + public: + explicit ResumableUDPSocket(const std::string& owner_extension_id); + + // Overriden from ApiResource + virtual bool IsPersistent() const OVERRIDE; + + const std::string& name() const { return name_; } + void set_name(const std::string& name) { name_ = name; } + + bool persistent() const { return persistent_; } + void set_persistent(bool persistent) { persistent_ = persistent; } + + int buffer_size() const { return buffer_size_; } + void set_buffer_size(int buffer_size) { buffer_size_ = buffer_size; } + + bool paused() const { return paused_; } + void set_paused(bool paused) { paused_ = paused; } + + private: + friend class ApiResourceManager<ResumableUDPSocket>; + static const char* service_name() { return "ResumableUDPSocketManager"; } + + // Application-defined string - see sockets_udp.idl. + std::string name_; + // Flag indicating whether the socket is left open when the application is + // suspended - see sockets_udp.idl. + bool persistent_; + // The size of the buffer used to receive data - see sockets_udp.idl. + int buffer_size_; + // Flag indicating whether a connected socket blocks its peer from sending + // more data - see sockets_udp.idl. + bool paused_; +}; + +} // namespace extensions + +#endif // EXTENSIONS_BROWSER_API_SOCKET_UDP_SOCKET_H_ diff --git a/extensions/browser/api/sockets_tcp/sockets_tcp/OWNERS b/extensions/browser/api/sockets_tcp/sockets_tcp/OWNERS new file mode 100644 index 0000000..3e30c82 --- /dev/null +++ b/extensions/browser/api/sockets_tcp/sockets_tcp/OWNERS @@ -0,0 +1 @@ +rpaquay@chromium.org diff --git a/extensions/browser/api/sockets_tcp/sockets_tcp_api.cc b/extensions/browser/api/sockets_tcp/sockets_tcp_api.cc new file mode 100644 index 0000000..fabb950 --- /dev/null +++ b/extensions/browser/api/sockets_tcp/sockets_tcp_api.cc @@ -0,0 +1,445 @@ +// Copyright 2014 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 "extensions/browser/api/sockets_tcp/sockets_tcp_api.h" + +#include "chrome/common/extensions/api/sockets/sockets_manifest_data.h" +#include "content/public/common/socket_permission_request.h" +#include "extensions/browser/api/socket/tcp_socket.h" +#include "extensions/browser/api/sockets_tcp/tcp_socket_event_dispatcher.h" +#include "net/base/net_errors.h" + +using extensions::ResumableTCPSocket; +using extensions::core_api::sockets_tcp::SocketInfo; +using extensions::core_api::sockets_tcp::SocketProperties; + +namespace { + +const char kSocketNotFoundError[] = "Socket not found"; +const char kPermissionError[] = "Does not have permission"; + +linked_ptr<SocketInfo> CreateSocketInfo(int socket_id, + ResumableTCPSocket* socket) { + linked_ptr<SocketInfo> socket_info(new 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())); + } + socket_info->paused = socket->paused(); + socket_info->connected = socket->IsConnected(); + + // 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())); + } + + // Grab the peer address as known by the OS. This and the call below will + // always succeed while the socket is connected, even if the socket has + // been remotely closed by the peer; only reading the socket will reveal + // that it should be closed locally. + net::IPEndPoint peerAddress; + if (socket->GetPeerAddress(&peerAddress)) { + socket_info->peer_address.reset( + new std::string(peerAddress.ToStringWithoutPort())); + socket_info->peer_port.reset(new int(peerAddress.port())); + } + + return socket_info; +} + +void SetSocketProperties(ResumableTCPSocket* socket, + 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()) { + // buffer size is validated when issuing the actual Recv operation + // on the socket. + socket->set_buffer_size(*properties->buffer_size.get()); + } +} + +} // namespace + +namespace extensions { +namespace core_api { + +using content::SocketPermissionRequest; + +TCPSocketAsyncApiFunction::~TCPSocketAsyncApiFunction() {} + +scoped_ptr<SocketResourceManagerInterface> +TCPSocketAsyncApiFunction::CreateSocketResourceManager() { + return scoped_ptr<SocketResourceManagerInterface>( + new SocketResourceManager<ResumableTCPSocket>()).Pass(); +} + +ResumableTCPSocket* TCPSocketAsyncApiFunction::GetTcpSocket(int socket_id) { + return static_cast<ResumableTCPSocket*>(GetSocket(socket_id)); +} + +TCPSocketExtensionWithDnsLookupFunction:: + ~TCPSocketExtensionWithDnsLookupFunction() {} + +scoped_ptr<SocketResourceManagerInterface> +TCPSocketExtensionWithDnsLookupFunction::CreateSocketResourceManager() { + return scoped_ptr<SocketResourceManagerInterface>( + new SocketResourceManager<ResumableTCPSocket>()).Pass(); +} + +ResumableTCPSocket* TCPSocketExtensionWithDnsLookupFunction::GetTcpSocket( + int socket_id) { + return static_cast<ResumableTCPSocket*>(GetSocket(socket_id)); +} + +SocketsTcpCreateFunction::SocketsTcpCreateFunction() {} + +SocketsTcpCreateFunction::~SocketsTcpCreateFunction() {} + +bool SocketsTcpCreateFunction::Prepare() { + params_ = sockets_tcp::Create::Params::Create(*args_); + EXTENSION_FUNCTION_VALIDATE(params_.get()); + return true; +} + +void SocketsTcpCreateFunction::Work() { + ResumableTCPSocket* socket = new ResumableTCPSocket(extension_->id()); + + sockets_tcp::SocketProperties* properties = params_.get()->properties.get(); + if (properties) { + SetSocketProperties(socket, properties); + } + + sockets_tcp::CreateInfo create_info; + create_info.socket_id = AddSocket(socket); + results_ = sockets_tcp::Create::Results::Create(create_info); +} + +SocketsTcpUpdateFunction::SocketsTcpUpdateFunction() {} + +SocketsTcpUpdateFunction::~SocketsTcpUpdateFunction() {} + +bool SocketsTcpUpdateFunction::Prepare() { + params_ = sockets_tcp::Update::Params::Create(*args_); + EXTENSION_FUNCTION_VALIDATE(params_.get()); + return true; +} + +void SocketsTcpUpdateFunction::Work() { + ResumableTCPSocket* socket = GetTcpSocket(params_->socket_id); + if (!socket) { + error_ = kSocketNotFoundError; + return; + } + + SetSocketProperties(socket, ¶ms_.get()->properties); + results_ = sockets_tcp::Update::Results::Create(); +} + +SocketsTcpSetPausedFunction::SocketsTcpSetPausedFunction() + : socket_event_dispatcher_(NULL) {} + +SocketsTcpSetPausedFunction::~SocketsTcpSetPausedFunction() {} + +bool SocketsTcpSetPausedFunction::Prepare() { + params_ = core_api::sockets_tcp::SetPaused::Params::Create(*args_); + EXTENSION_FUNCTION_VALIDATE(params_.get()); + + socket_event_dispatcher_ = TCPSocketEventDispatcher::Get(browser_context()); + DCHECK(socket_event_dispatcher_) + << "There is no socket event dispatcher. " + "If this assertion is failing during a test, then it is likely that " + "TestExtensionSystem is failing to provide an instance of " + "TCPSocketEventDispatcher."; + return socket_event_dispatcher_ != NULL; +} + +void SocketsTcpSetPausedFunction::Work() { + ResumableTCPSocket* socket = GetTcpSocket(params_->socket_id); + if (!socket) { + error_ = kSocketNotFoundError; + return; + } + + if (socket->paused() != params_->paused) { + socket->set_paused(params_->paused); + if (socket->IsConnected() && !params_->paused) { + socket_event_dispatcher_->OnSocketResume(extension_->id(), + params_->socket_id); + } + } + + results_ = sockets_tcp::SetPaused::Results::Create(); +} + +SocketsTcpSetKeepAliveFunction::SocketsTcpSetKeepAliveFunction() {} + +SocketsTcpSetKeepAliveFunction::~SocketsTcpSetKeepAliveFunction() {} + +bool SocketsTcpSetKeepAliveFunction::Prepare() { + params_ = core_api::sockets_tcp::SetKeepAlive::Params::Create(*args_); + EXTENSION_FUNCTION_VALIDATE(params_.get()); + return true; +} + +void SocketsTcpSetKeepAliveFunction::Work() { + ResumableTCPSocket* socket = GetTcpSocket(params_->socket_id); + if (!socket) { + error_ = kSocketNotFoundError; + return; + } + + int delay = params_->delay ? *params_->delay.get() : 0; + + bool success = socket->SetKeepAlive(params_->enable, delay); + int net_result = (success ? net::OK : net::ERR_FAILED); + if (net_result != net::OK) + error_ = net::ErrorToString(net_result); + results_ = sockets_tcp::SetKeepAlive::Results::Create(net_result); +} + +SocketsTcpSetNoDelayFunction::SocketsTcpSetNoDelayFunction() {} + +SocketsTcpSetNoDelayFunction::~SocketsTcpSetNoDelayFunction() {} + +bool SocketsTcpSetNoDelayFunction::Prepare() { + params_ = core_api::sockets_tcp::SetNoDelay::Params::Create(*args_); + EXTENSION_FUNCTION_VALIDATE(params_.get()); + return true; +} + +void SocketsTcpSetNoDelayFunction::Work() { + ResumableTCPSocket* socket = GetTcpSocket(params_->socket_id); + if (!socket) { + error_ = kSocketNotFoundError; + return; + } + + bool success = socket->SetNoDelay(params_->no_delay); + int net_result = (success ? net::OK : net::ERR_FAILED); + if (net_result != net::OK) + error_ = net::ErrorToString(net_result); + results_ = sockets_tcp::SetNoDelay::Results::Create(net_result); +} + +SocketsTcpConnectFunction::SocketsTcpConnectFunction() + : socket_event_dispatcher_(NULL) {} + +SocketsTcpConnectFunction::~SocketsTcpConnectFunction() {} + +bool SocketsTcpConnectFunction::Prepare() { + params_ = sockets_tcp::Connect::Params::Create(*args_); + EXTENSION_FUNCTION_VALIDATE(params_.get()); + + socket_event_dispatcher_ = TCPSocketEventDispatcher::Get(browser_context()); + DCHECK(socket_event_dispatcher_) + << "There is no socket event dispatcher. " + "If this assertion is failing during a test, then it is likely that " + "TestExtensionSystem is failing to provide an instance of " + "TCPSocketEventDispatcher."; + return socket_event_dispatcher_ != NULL; +} + +void SocketsTcpConnectFunction::AsyncWorkStart() { + ResumableTCPSocket* socket = GetTcpSocket(params_->socket_id); + if (!socket) { + error_ = kSocketNotFoundError; + AsyncWorkCompleted(); + return; + } + + content::SocketPermissionRequest param(SocketPermissionRequest::TCP_CONNECT, + params_->peer_address, + params_->peer_port); + if (!SocketsManifestData::CheckRequest(GetExtension(), param)) { + error_ = kPermissionError; + AsyncWorkCompleted(); + return; + } + + StartDnsLookup(params_->peer_address); +} + +void SocketsTcpConnectFunction::AfterDnsLookup(int lookup_result) { + if (lookup_result == net::OK) { + StartConnect(); + } else { + OnCompleted(lookup_result); + } +} + +void SocketsTcpConnectFunction::StartConnect() { + ResumableTCPSocket* socket = GetTcpSocket(params_->socket_id); + if (!socket) { + error_ = kSocketNotFoundError; + AsyncWorkCompleted(); + return; + } + + socket->Connect(resolved_address_, + params_->peer_port, + base::Bind(&SocketsTcpConnectFunction::OnCompleted, this)); +} + +void SocketsTcpConnectFunction::OnCompleted(int net_result) { + if (net_result == net::OK) { + socket_event_dispatcher_->OnSocketConnect(extension_->id(), + params_->socket_id); + } + + if (net_result != net::OK) + error_ = net::ErrorToString(net_result); + results_ = sockets_tcp::Connect::Results::Create(net_result); + AsyncWorkCompleted(); +} + +SocketsTcpDisconnectFunction::SocketsTcpDisconnectFunction() {} + +SocketsTcpDisconnectFunction::~SocketsTcpDisconnectFunction() {} + +bool SocketsTcpDisconnectFunction::Prepare() { + params_ = sockets_tcp::Disconnect::Params::Create(*args_); + EXTENSION_FUNCTION_VALIDATE(params_.get()); + return true; +} + +void SocketsTcpDisconnectFunction::Work() { + ResumableTCPSocket* socket = GetTcpSocket(params_->socket_id); + if (!socket) { + error_ = kSocketNotFoundError; + return; + } + + socket->Disconnect(); + results_ = sockets_tcp::Disconnect::Results::Create(); +} + +SocketsTcpSendFunction::SocketsTcpSendFunction() : io_buffer_size_(0) {} + +SocketsTcpSendFunction::~SocketsTcpSendFunction() {} + +bool SocketsTcpSendFunction::Prepare() { + params_ = sockets_tcp::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 SocketsTcpSendFunction::AsyncWorkStart() { + ResumableTCPSocket* socket = GetTcpSocket(params_->socket_id); + if (!socket) { + error_ = kSocketNotFoundError; + AsyncWorkCompleted(); + return; + } + + socket->Write(io_buffer_, + io_buffer_size_, + base::Bind(&SocketsTcpSendFunction::OnCompleted, this)); +} + +void SocketsTcpSendFunction::OnCompleted(int net_result) { + if (net_result >= net::OK) { + SetSendResult(net::OK, net_result); + } else { + SetSendResult(net_result, -1); + } +} + +void SocketsTcpSendFunction::SetSendResult(int net_result, int bytes_sent) { + CHECK(net_result <= net::OK) << "Network status code must be <= net::OK"; + + sockets_tcp::SendInfo send_info; + send_info.result_code = net_result; + if (net_result == net::OK) { + send_info.bytes_sent.reset(new int(bytes_sent)); + } + + if (net_result != net::OK) + error_ = net::ErrorToString(net_result); + results_ = sockets_tcp::Send::Results::Create(send_info); + AsyncWorkCompleted(); +} + +SocketsTcpCloseFunction::SocketsTcpCloseFunction() {} + +SocketsTcpCloseFunction::~SocketsTcpCloseFunction() {} + +bool SocketsTcpCloseFunction::Prepare() { + params_ = sockets_tcp::Close::Params::Create(*args_); + EXTENSION_FUNCTION_VALIDATE(params_.get()); + return true; +} + +void SocketsTcpCloseFunction::Work() { + ResumableTCPSocket* socket = GetTcpSocket(params_->socket_id); + if (!socket) { + error_ = kSocketNotFoundError; + return; + } + + RemoveSocket(params_->socket_id); + results_ = sockets_tcp::Close::Results::Create(); +} + +SocketsTcpGetInfoFunction::SocketsTcpGetInfoFunction() {} + +SocketsTcpGetInfoFunction::~SocketsTcpGetInfoFunction() {} + +bool SocketsTcpGetInfoFunction::Prepare() { + params_ = sockets_tcp::GetInfo::Params::Create(*args_); + EXTENSION_FUNCTION_VALIDATE(params_.get()); + return true; +} + +void SocketsTcpGetInfoFunction::Work() { + ResumableTCPSocket* socket = GetTcpSocket(params_->socket_id); + if (!socket) { + error_ = kSocketNotFoundError; + return; + } + + linked_ptr<sockets_tcp::SocketInfo> socket_info = + CreateSocketInfo(params_->socket_id, socket); + results_ = sockets_tcp::GetInfo::Results::Create(*socket_info); +} + +SocketsTcpGetSocketsFunction::SocketsTcpGetSocketsFunction() {} + +SocketsTcpGetSocketsFunction::~SocketsTcpGetSocketsFunction() {} + +bool SocketsTcpGetSocketsFunction::Prepare() { return true; } + +void SocketsTcpGetSocketsFunction::Work() { + std::vector<linked_ptr<sockets_tcp::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; + ResumableTCPSocket* socket = GetTcpSocket(socket_id); + if (socket) { + socket_infos.push_back(CreateSocketInfo(socket_id, socket)); + } + } + } + results_ = sockets_tcp::GetSockets::Results::Create(socket_infos); +} + +} // namespace core_api +} // namespace extensions diff --git a/extensions/browser/api/sockets_tcp/sockets_tcp_api.h b/extensions/browser/api/sockets_tcp/sockets_tcp_api.h new file mode 100644 index 0000000..8195ca4 --- /dev/null +++ b/extensions/browser/api/sockets_tcp/sockets_tcp_api.h @@ -0,0 +1,244 @@ +// Copyright 2014 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 EXTENSIONS_BROWSER_API_SOCKETS_TCP_SOCKETS_TCP_API_H_ +#define EXTENSIONS_BROWSER_API_SOCKETS_TCP_SOCKETS_TCP_API_H_ + +#include "extensions/browser/api/socket/socket_api.h" +#include "extensions/common/api/sockets_tcp.h" + +namespace extensions { +class ResumableTCPSocket; +} + +namespace extensions { +namespace core_api { + +class TCPSocketEventDispatcher; + +class TCPSocketAsyncApiFunction : public SocketAsyncApiFunction { + protected: + virtual ~TCPSocketAsyncApiFunction(); + + virtual scoped_ptr<SocketResourceManagerInterface> + CreateSocketResourceManager() OVERRIDE; + + ResumableTCPSocket* GetTcpSocket(int socket_id); +}; + +class TCPSocketExtensionWithDnsLookupFunction + : public SocketExtensionWithDnsLookupFunction { + protected: + virtual ~TCPSocketExtensionWithDnsLookupFunction(); + + virtual scoped_ptr<SocketResourceManagerInterface> + CreateSocketResourceManager() OVERRIDE; + + ResumableTCPSocket* GetTcpSocket(int socket_id); +}; + +class SocketsTcpCreateFunction : public TCPSocketAsyncApiFunction { + public: + DECLARE_EXTENSION_FUNCTION("sockets.tcp.create", SOCKETS_TCP_CREATE) + + SocketsTcpCreateFunction(); + + protected: + virtual ~SocketsTcpCreateFunction(); + + // AsyncApiFunction: + virtual bool Prepare() OVERRIDE; + virtual void Work() OVERRIDE; + + private: + FRIEND_TEST_ALL_PREFIXES(SocketsTcpUnitTest, Create); + scoped_ptr<sockets_tcp::Create::Params> params_; +}; + +class SocketsTcpUpdateFunction : public TCPSocketAsyncApiFunction { + public: + DECLARE_EXTENSION_FUNCTION("sockets.tcp.update", SOCKETS_TCP_UPDATE) + + SocketsTcpUpdateFunction(); + + protected: + virtual ~SocketsTcpUpdateFunction(); + + // AsyncApiFunction: + virtual bool Prepare() OVERRIDE; + virtual void Work() OVERRIDE; + + private: + scoped_ptr<sockets_tcp::Update::Params> params_; +}; + +class SocketsTcpSetPausedFunction : public TCPSocketAsyncApiFunction { + public: + DECLARE_EXTENSION_FUNCTION("sockets.tcp.setPaused", SOCKETS_TCP_SETPAUSED) + + SocketsTcpSetPausedFunction(); + + protected: + virtual ~SocketsTcpSetPausedFunction(); + + // AsyncApiFunction + virtual bool Prepare() OVERRIDE; + virtual void Work() OVERRIDE; + + private: + scoped_ptr<sockets_tcp::SetPaused::Params> params_; + TCPSocketEventDispatcher* socket_event_dispatcher_; +}; + +class SocketsTcpSetKeepAliveFunction : public TCPSocketAsyncApiFunction { + public: + DECLARE_EXTENSION_FUNCTION("sockets.tcp.setKeepAlive", + SOCKETS_TCP_SETKEEPALIVE) + + SocketsTcpSetKeepAliveFunction(); + + protected: + virtual ~SocketsTcpSetKeepAliveFunction(); + + // AsyncApiFunction + virtual bool Prepare() OVERRIDE; + virtual void Work() OVERRIDE; + + private: + scoped_ptr<sockets_tcp::SetKeepAlive::Params> params_; +}; + +class SocketsTcpSetNoDelayFunction : public TCPSocketAsyncApiFunction { + public: + DECLARE_EXTENSION_FUNCTION("sockets.tcp.setNoDelay", SOCKETS_TCP_SETNODELAY) + + SocketsTcpSetNoDelayFunction(); + + protected: + virtual ~SocketsTcpSetNoDelayFunction(); + + // AsyncApiFunction + virtual bool Prepare() OVERRIDE; + virtual void Work() OVERRIDE; + + private: + scoped_ptr<sockets_tcp::SetNoDelay::Params> params_; +}; + +class SocketsTcpConnectFunction + : public TCPSocketExtensionWithDnsLookupFunction { + public: + DECLARE_EXTENSION_FUNCTION("sockets.tcp.connect", SOCKETS_TCP_CONNECT) + + SocketsTcpConnectFunction(); + + protected: + virtual ~SocketsTcpConnectFunction(); + + // AsyncApiFunction: + virtual bool Prepare() OVERRIDE; + virtual void AsyncWorkStart() OVERRIDE; + + // SocketExtensionWithDnsLookupFunction: + virtual void AfterDnsLookup(int lookup_result) OVERRIDE; + + private: + void StartConnect(); + void OnCompleted(int net_result); + + scoped_ptr<sockets_tcp::Connect::Params> params_; + TCPSocketEventDispatcher* socket_event_dispatcher_; +}; + +class SocketsTcpDisconnectFunction : public TCPSocketAsyncApiFunction { + public: + DECLARE_EXTENSION_FUNCTION("sockets.tcp.disconnect", SOCKETS_TCP_DISCONNECT) + + SocketsTcpDisconnectFunction(); + + protected: + virtual ~SocketsTcpDisconnectFunction(); + + // AsyncApiFunction: + virtual bool Prepare() OVERRIDE; + virtual void Work() OVERRIDE; + + private: + scoped_ptr<sockets_tcp::Disconnect::Params> params_; +}; + +class SocketsTcpSendFunction : public TCPSocketAsyncApiFunction { + public: + DECLARE_EXTENSION_FUNCTION("sockets.tcp.send", SOCKETS_TCP_SEND) + + SocketsTcpSendFunction(); + + protected: + virtual ~SocketsTcpSendFunction(); + + // AsyncApiFunction: + virtual bool Prepare() OVERRIDE; + virtual void AsyncWorkStart() OVERRIDE; + + private: + void OnCompleted(int net_result); + void SetSendResult(int net_result, int bytes_sent); + + scoped_ptr<sockets_tcp::Send::Params> params_; + scoped_refptr<net::IOBuffer> io_buffer_; + size_t io_buffer_size_; +}; + +class SocketsTcpCloseFunction : public TCPSocketAsyncApiFunction { + public: + DECLARE_EXTENSION_FUNCTION("sockets.tcp.close", SOCKETS_TCP_CLOSE) + + SocketsTcpCloseFunction(); + + protected: + virtual ~SocketsTcpCloseFunction(); + + // AsyncApiFunction: + virtual bool Prepare() OVERRIDE; + virtual void Work() OVERRIDE; + + private: + scoped_ptr<sockets_tcp::Close::Params> params_; +}; + +class SocketsTcpGetInfoFunction : public TCPSocketAsyncApiFunction { + public: + DECLARE_EXTENSION_FUNCTION("sockets.tcp.getInfo", SOCKETS_TCP_GETINFO) + + SocketsTcpGetInfoFunction(); + + protected: + virtual ~SocketsTcpGetInfoFunction(); + + // AsyncApiFunction: + virtual bool Prepare() OVERRIDE; + virtual void Work() OVERRIDE; + + private: + scoped_ptr<sockets_tcp::GetInfo::Params> params_; +}; + +class SocketsTcpGetSocketsFunction : public TCPSocketAsyncApiFunction { + public: + DECLARE_EXTENSION_FUNCTION("sockets.tcp.getSockets", SOCKETS_TCP_GETSOCKETS) + + SocketsTcpGetSocketsFunction(); + + protected: + virtual ~SocketsTcpGetSocketsFunction(); + + // AsyncApiFunction: + virtual bool Prepare() OVERRIDE; + virtual void Work() OVERRIDE; +}; + +} // namespace core_api +} // namespace extensions + +#endif // EXTENSIONS_BROWSER_API_SOCKETS_TCP_SOCKETS_TCP_API_H_ diff --git a/extensions/browser/api/sockets_tcp/tcp_socket_event_dispatcher.cc b/extensions/browser/api/sockets_tcp/tcp_socket_event_dispatcher.cc new file mode 100644 index 0000000..11d4cee --- /dev/null +++ b/extensions/browser/api/sockets_tcp/tcp_socket_event_dispatcher.cc @@ -0,0 +1,198 @@ +// Copyright 2014 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 "extensions/browser/api/sockets_tcp/tcp_socket_event_dispatcher.h" + +#include "extensions/browser/api/socket/tcp_socket.h" +#include "extensions/browser/event_router.h" +#include "extensions/browser/extension_system.h" +#include "extensions/browser/extensions_browser_client.h" +#include "net/base/net_errors.h" + +namespace { +int kDefaultBufferSize = 4096; +} + +namespace extensions { +namespace core_api { + +using content::BrowserThread; + +static base::LazyInstance< + BrowserContextKeyedAPIFactory<TCPSocketEventDispatcher> > g_factory = + LAZY_INSTANCE_INITIALIZER; + +// static +BrowserContextKeyedAPIFactory<TCPSocketEventDispatcher>* +TCPSocketEventDispatcher::GetFactoryInstance() { + return g_factory.Pointer(); +} + +// static +TCPSocketEventDispatcher* TCPSocketEventDispatcher::Get( + content::BrowserContext* context) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + + return BrowserContextKeyedAPIFactory<TCPSocketEventDispatcher>::Get(context); +} + +TCPSocketEventDispatcher::TCPSocketEventDispatcher( + content::BrowserContext* context) + : thread_id_(Socket::kThreadId), browser_context_(context) { + ApiResourceManager<ResumableTCPSocket>* manager = + ApiResourceManager<ResumableTCPSocket>::Get(browser_context_); + 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<ResumableTCPSocket>."; + sockets_ = manager->data_; +} + +TCPSocketEventDispatcher::~TCPSocketEventDispatcher() {} + +TCPSocketEventDispatcher::ReadParams::ReadParams() {} + +TCPSocketEventDispatcher::ReadParams::~ReadParams() {} + +void TCPSocketEventDispatcher::OnSocketConnect(const std::string& extension_id, + int socket_id) { + DCHECK(BrowserThread::CurrentlyOn(thread_id_)); + + StartSocketRead(extension_id, socket_id); +} + +void TCPSocketEventDispatcher::OnSocketResume(const std::string& extension_id, + int socket_id) { + DCHECK(BrowserThread::CurrentlyOn(thread_id_)); + + StartSocketRead(extension_id, socket_id); +} + +void TCPSocketEventDispatcher::StartSocketRead(const std::string& extension_id, + int socket_id) { + DCHECK(BrowserThread::CurrentlyOn(thread_id_)); + + ReadParams params; + params.thread_id = thread_id_; + params.browser_context_id = browser_context_; + params.extension_id = extension_id; + params.sockets = sockets_; + params.socket_id = socket_id; + + StartRead(params); +} + +// static +void TCPSocketEventDispatcher::StartRead(const ReadParams& params) { + DCHECK(BrowserThread::CurrentlyOn(params.thread_id)); + + ResumableTCPSocket* socket = + params.sockets->Get(params.extension_id, params.socket_id); + if (!socket) { + // This can happen if the socket is closed while our callback is active. + return; + } + DCHECK(params.extension_id == socket->owner_extension_id()) + << "Socket has wrong owner."; + + // Don't start another read if the socket has been paused. + if (socket->paused()) + return; + + int buffer_size = socket->buffer_size(); + if (buffer_size <= 0) + buffer_size = kDefaultBufferSize; + socket->Read(buffer_size, + base::Bind(&TCPSocketEventDispatcher::ReadCallback, params)); +} + +// static +void TCPSocketEventDispatcher::ReadCallback( + const ReadParams& params, + int bytes_read, + scoped_refptr<net::IOBuffer> io_buffer) { + DCHECK(BrowserThread::CurrentlyOn(params.thread_id)); + + // If |bytes_read| == 0, the connection has been closed by the peer. + // If |bytes_read| < 0, there was a network error, and |bytes_read| is a value + // from "net::ERR_". + + if (bytes_read == 0) { + bytes_read = net::ERR_CONNECTION_CLOSED; + } + + if (bytes_read > 0) { + // Dispatch "onReceive" event. + sockets_tcp::ReceiveInfo receive_info; + receive_info.socket_id = params.socket_id; + receive_info.data = std::string(io_buffer->data(), bytes_read); + scoped_ptr<base::ListValue> args = + sockets_tcp::OnReceive::Create(receive_info); + scoped_ptr<Event> event( + new Event(sockets_tcp::OnReceive::kEventName, args.Pass())); + PostEvent(params, 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. + BrowserThread::PostTask( + params.thread_id, + FROM_HERE, + base::Bind(&TCPSocketEventDispatcher::StartRead, params)); + } else if (bytes_read == net::ERR_IO_PENDING) { + // This happens when resuming a socket which already had an + // active "read" callback. + } else { + // Dispatch "onReceiveError" event but don't start another read to avoid + // potential infinite reads if we have a persistent network error. + sockets_tcp::ReceiveErrorInfo receive_error_info; + receive_error_info.socket_id = params.socket_id; + receive_error_info.result_code = bytes_read; + scoped_ptr<base::ListValue> args = + sockets_tcp::OnReceiveError::Create(receive_error_info); + scoped_ptr<Event> event( + new Event(sockets_tcp::OnReceiveError::kEventName, args.Pass())); + PostEvent(params, event.Pass()); + + // Since we got an error, the socket is now "paused" until the application + // "resumes" it. + ResumableTCPSocket* socket = + params.sockets->Get(params.extension_id, params.socket_id); + if (socket) { + socket->set_paused(true); + } + } +} + +// static +void TCPSocketEventDispatcher::PostEvent(const ReadParams& params, + scoped_ptr<Event> event) { + DCHECK(BrowserThread::CurrentlyOn(params.thread_id)); + + BrowserThread::PostTask(BrowserThread::UI, + FROM_HERE, + base::Bind(&DispatchEvent, + params.browser_context_id, + params.extension_id, + base::Passed(event.Pass()))); +} + +// static +void TCPSocketEventDispatcher::DispatchEvent(void* browser_context_id, + const std::string& extension_id, + scoped_ptr<Event> event) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + + content::BrowserContext* context = + reinterpret_cast<content::BrowserContext*>(browser_context_id); + if (!extensions::ExtensionsBrowserClient::Get()->IsValidContext(context)) + return; + + EventRouter* router = ExtensionSystem::Get(context)->event_router(); + if (router) + router->DispatchEventToExtension(extension_id, event.Pass()); +} + +} // namespace core_api +} // namespace extensions diff --git a/extensions/browser/api/sockets_tcp/tcp_socket_event_dispatcher.h b/extensions/browser/api/sockets_tcp/tcp_socket_event_dispatcher.h new file mode 100644 index 0000000..0e16690 --- /dev/null +++ b/extensions/browser/api/sockets_tcp/tcp_socket_event_dispatcher.h @@ -0,0 +1,94 @@ +// Copyright 2014 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 EXTENSIONS_BROWSER_API_SOCKETS_TCP_TCP_SOCKET_EVENT_DISPATCHER_H_ +#define EXTENSIONS_BROWSER_API_SOCKETS_TCP_TCP_SOCKET_EVENT_DISPATCHER_H_ + +#include "extensions/browser/api/api_resource_manager.h" +#include "extensions/browser/api/sockets_tcp/sockets_tcp_api.h" + +namespace content { +class BrowserContext; +} + +namespace extensions { +struct Event; +class ResumableTCPSocket; +} + +namespace extensions { +namespace core_api { + +// Dispatch events related to "sockets.tcp" sockets from callback on native +// socket instances. There is one instance per profile. +class TCPSocketEventDispatcher + : public BrowserContextKeyedAPI, + public base::SupportsWeakPtr<TCPSocketEventDispatcher> { + public: + explicit TCPSocketEventDispatcher(content::BrowserContext* context); + virtual ~TCPSocketEventDispatcher(); + + // Socket is active, start receving from it. + void OnSocketConnect(const std::string& extension_id, int socket_id); + + // Socket is active again, start receiving data from it. + void OnSocketResume(const std::string& extension_id, int socket_id); + + // BrowserContextKeyedAPI implementation. + static BrowserContextKeyedAPIFactory<TCPSocketEventDispatcher>* + GetFactoryInstance(); + + // Convenience method to get the SocketEventDispatcher for a profile. + static TCPSocketEventDispatcher* Get(content::BrowserContext* context); + + private: + typedef ApiResourceManager<ResumableTCPSocket>::ApiResourceData SocketData; + friend class BrowserContextKeyedAPIFactory<TCPSocketEventDispatcher>; + // BrowserContextKeyedAPI implementation. + static const char* service_name() { return "TCPSocketEventDispatcher"; } + static const bool kServiceHasOwnInstanceInIncognito = true; + static const bool kServiceIsNULLWhileTesting = true; + + // base::Bind supports methods with up to 6 parameters. ReadParams is used + // as a workaround that limitation for invoking StartReceive. + struct ReadParams { + ReadParams(); + ~ReadParams(); + + content::BrowserThread::ID thread_id; + void* browser_context_id; + std::string extension_id; + scoped_refptr<SocketData> sockets; + int socket_id; + }; + + // Start a receive and register a callback. + void StartSocketRead(const std::string& extension_id, int socket_id); + + // Start a receive and register a callback. + static void StartRead(const ReadParams& params); + + // Called when socket receive data. + static void ReadCallback(const ReadParams& params, + int bytes_read, + scoped_refptr<net::IOBuffer> io_buffer); + + // Post an extension event from IO to UI thread + static void PostEvent(const ReadParams& params, scoped_ptr<Event> event); + + // Dispatch an extension event on to EventRouter instance on UI thread. + static void DispatchEvent(void* browser_context_id, + const std::string& extension_id, + scoped_ptr<Event> event); + + // Usually IO thread (except for unit testing). + content::BrowserThread::ID thread_id_; + content::BrowserContext* const browser_context_; + scoped_refptr<SocketData> sockets_; +}; + +} // namespace core_api +} // namespace extensions + +#endif // EXTENSIONS_BROWSER_API_SOCKETS_TCP_TCP_SOCKET_EVENT_DISPATCHER_H_ diff --git a/extensions/browser/api/sockets_tcp_server/sockets_tcp_server/OWNERS b/extensions/browser/api/sockets_tcp_server/sockets_tcp_server/OWNERS new file mode 100644 index 0000000..3e30c82 --- /dev/null +++ b/extensions/browser/api/sockets_tcp_server/sockets_tcp_server/OWNERS @@ -0,0 +1 @@ +rpaquay@chromium.org diff --git a/extensions/browser/api/sockets_tcp_server/sockets_tcp_server_api.cc b/extensions/browser/api/sockets_tcp_server/sockets_tcp_server_api.cc new file mode 100644 index 0000000..6adc0fc --- /dev/null +++ b/extensions/browser/api/sockets_tcp_server/sockets_tcp_server_api.cc @@ -0,0 +1,298 @@ +// Copyright 2014 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 "extensions/browser/api/sockets_tcp_server/sockets_tcp_server_api.h" + +#include "chrome/common/extensions/api/sockets/sockets_manifest_data.h" +#include "chrome/common/extensions/permissions/socket_permission.h" +#include "content/public/common/socket_permission_request.h" +#include "extensions/browser/api/socket/tcp_socket.h" +#include "extensions/browser/api/sockets_tcp_server/tcp_server_socket_event_dispatcher.h" +#include "extensions/common/permissions/permissions_data.h" +#include "net/base/net_errors.h" + +using content::SocketPermissionRequest; +using extensions::ResumableTCPServerSocket; +using extensions::core_api::sockets_tcp_server::SocketInfo; +using extensions::core_api::sockets_tcp_server::SocketProperties; + +namespace { + +const char kSocketNotFoundError[] = "Socket not found"; +const char kPermissionError[] = "Does not have permission"; +const int kDefaultListenBacklog = SOMAXCONN; + +linked_ptr<SocketInfo> CreateSocketInfo(int socket_id, + ResumableTCPServerSocket* socket) { + linked_ptr<SocketInfo> socket_info(new 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(); + socket_info->paused = socket->paused(); + + // 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(ResumableTCPServerSocket* socket, + SocketProperties* properties) { + if (properties->name.get()) { + socket->set_name(*properties->name.get()); + } + if (properties->persistent.get()) { + socket->set_persistent(*properties->persistent.get()); + } +} + +} // namespace + +namespace extensions { +namespace core_api { + +TCPServerSocketAsyncApiFunction::~TCPServerSocketAsyncApiFunction() {} + +scoped_ptr<SocketResourceManagerInterface> +TCPServerSocketAsyncApiFunction::CreateSocketResourceManager() { + return scoped_ptr<SocketResourceManagerInterface>( + new SocketResourceManager<ResumableTCPServerSocket>()).Pass(); +} + +ResumableTCPServerSocket* TCPServerSocketAsyncApiFunction::GetTcpSocket( + int socket_id) { + return static_cast<ResumableTCPServerSocket*>(GetSocket(socket_id)); +} + +SocketsTcpServerCreateFunction::SocketsTcpServerCreateFunction() {} + +SocketsTcpServerCreateFunction::~SocketsTcpServerCreateFunction() {} + +bool SocketsTcpServerCreateFunction::Prepare() { + params_ = sockets_tcp_server::Create::Params::Create(*args_); + EXTENSION_FUNCTION_VALIDATE(params_.get()); + return true; +} + +void SocketsTcpServerCreateFunction::Work() { + ResumableTCPServerSocket* socket = + new ResumableTCPServerSocket(extension_->id()); + + sockets_tcp_server::SocketProperties* properties = + params_.get()->properties.get(); + if (properties) { + SetSocketProperties(socket, properties); + } + + sockets_tcp_server::CreateInfo create_info; + create_info.socket_id = AddSocket(socket); + results_ = sockets_tcp_server::Create::Results::Create(create_info); +} + +SocketsTcpServerUpdateFunction::SocketsTcpServerUpdateFunction() {} + +SocketsTcpServerUpdateFunction::~SocketsTcpServerUpdateFunction() {} + +bool SocketsTcpServerUpdateFunction::Prepare() { + params_ = sockets_tcp_server::Update::Params::Create(*args_); + EXTENSION_FUNCTION_VALIDATE(params_.get()); + return true; +} + +void SocketsTcpServerUpdateFunction::Work() { + ResumableTCPServerSocket* socket = GetTcpSocket(params_->socket_id); + if (!socket) { + error_ = kSocketNotFoundError; + return; + } + + SetSocketProperties(socket, ¶ms_.get()->properties); + results_ = sockets_tcp_server::Update::Results::Create(); +} + +SocketsTcpServerSetPausedFunction::SocketsTcpServerSetPausedFunction() + : socket_event_dispatcher_(NULL) {} + +SocketsTcpServerSetPausedFunction::~SocketsTcpServerSetPausedFunction() {} + +bool SocketsTcpServerSetPausedFunction::Prepare() { + params_ = core_api::sockets_tcp_server::SetPaused::Params::Create(*args_); + EXTENSION_FUNCTION_VALIDATE(params_.get()); + + socket_event_dispatcher_ = + TCPServerSocketEventDispatcher::Get(browser_context()); + DCHECK(socket_event_dispatcher_) + << "There is no socket event dispatcher. " + "If this assertion is failing during a test, then it is likely that " + "TestExtensionSystem is failing to provide an instance of " + "TCPServerSocketEventDispatcher."; + return socket_event_dispatcher_ != NULL; +} + +void SocketsTcpServerSetPausedFunction::Work() { + ResumableTCPServerSocket* socket = GetTcpSocket(params_->socket_id); + if (!socket) { + error_ = kSocketNotFoundError; + return; + } + + if (socket->paused() != params_->paused) { + socket->set_paused(params_->paused); + if (socket->IsConnected() && !params_->paused) { + socket_event_dispatcher_->OnServerSocketResume(extension_->id(), + params_->socket_id); + } + } + + results_ = sockets_tcp_server::SetPaused::Results::Create(); +} + +SocketsTcpServerListenFunction::SocketsTcpServerListenFunction() + : socket_event_dispatcher_(NULL) {} + +SocketsTcpServerListenFunction::~SocketsTcpServerListenFunction() {} + +bool SocketsTcpServerListenFunction::Prepare() { + params_ = core_api::sockets_tcp_server::Listen::Params::Create(*args_); + EXTENSION_FUNCTION_VALIDATE(params_.get()); + + socket_event_dispatcher_ = + TCPServerSocketEventDispatcher::Get(browser_context()); + DCHECK(socket_event_dispatcher_) + << "There is no socket event dispatcher. " + "If this assertion is failing during a test, then it is likely that " + "TestExtensionSystem is failing to provide an instance of " + "TCPServerSocketEventDispatcher."; + return socket_event_dispatcher_ != NULL; +} + +void SocketsTcpServerListenFunction::Work() { + ResumableTCPServerSocket* socket = GetTcpSocket(params_->socket_id); + if (!socket) { + error_ = kSocketNotFoundError; + return; + } + + SocketPermissionRequest param( + SocketPermissionRequest::TCP_LISTEN, params_->address, params_->port); + if (!SocketsManifestData::CheckRequest(GetExtension(), param)) { + error_ = kPermissionError; + return; + } + + int net_result = socket->Listen( + params_->address, + params_->port, + params_->backlog.get() ? *params_->backlog.get() : kDefaultListenBacklog, + &error_); + + if (net_result != net::OK) + error_ = net::ErrorToString(net_result); + + if (net_result == net::OK) { + socket_event_dispatcher_->OnServerSocketListen(extension_->id(), + params_->socket_id); + } + + results_ = sockets_tcp_server::Listen::Results::Create(net_result); +} + +SocketsTcpServerDisconnectFunction::SocketsTcpServerDisconnectFunction() {} + +SocketsTcpServerDisconnectFunction::~SocketsTcpServerDisconnectFunction() {} + +bool SocketsTcpServerDisconnectFunction::Prepare() { + params_ = sockets_tcp_server::Disconnect::Params::Create(*args_); + EXTENSION_FUNCTION_VALIDATE(params_.get()); + return true; +} + +void SocketsTcpServerDisconnectFunction::Work() { + ResumableTCPServerSocket* socket = GetTcpSocket(params_->socket_id); + if (!socket) { + error_ = kSocketNotFoundError; + return; + } + + socket->Disconnect(); + results_ = sockets_tcp_server::Disconnect::Results::Create(); +} + +SocketsTcpServerCloseFunction::SocketsTcpServerCloseFunction() {} + +SocketsTcpServerCloseFunction::~SocketsTcpServerCloseFunction() {} + +bool SocketsTcpServerCloseFunction::Prepare() { + params_ = sockets_tcp_server::Close::Params::Create(*args_); + EXTENSION_FUNCTION_VALIDATE(params_.get()); + return true; +} + +void SocketsTcpServerCloseFunction::Work() { + ResumableTCPServerSocket* socket = GetTcpSocket(params_->socket_id); + if (!socket) { + error_ = kSocketNotFoundError; + return; + } + + RemoveSocket(params_->socket_id); + results_ = sockets_tcp_server::Close::Results::Create(); +} + +SocketsTcpServerGetInfoFunction::SocketsTcpServerGetInfoFunction() {} + +SocketsTcpServerGetInfoFunction::~SocketsTcpServerGetInfoFunction() {} + +bool SocketsTcpServerGetInfoFunction::Prepare() { + params_ = sockets_tcp_server::GetInfo::Params::Create(*args_); + EXTENSION_FUNCTION_VALIDATE(params_.get()); + return true; +} + +void SocketsTcpServerGetInfoFunction::Work() { + ResumableTCPServerSocket* socket = GetTcpSocket(params_->socket_id); + if (!socket) { + error_ = kSocketNotFoundError; + return; + } + + linked_ptr<sockets_tcp_server::SocketInfo> socket_info = + CreateSocketInfo(params_->socket_id, socket); + results_ = sockets_tcp_server::GetInfo::Results::Create(*socket_info); +} + +SocketsTcpServerGetSocketsFunction::SocketsTcpServerGetSocketsFunction() {} + +SocketsTcpServerGetSocketsFunction::~SocketsTcpServerGetSocketsFunction() {} + +bool SocketsTcpServerGetSocketsFunction::Prepare() { return true; } + +void SocketsTcpServerGetSocketsFunction::Work() { + std::vector<linked_ptr<sockets_tcp_server::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; + ResumableTCPServerSocket* socket = GetTcpSocket(socket_id); + if (socket) { + socket_infos.push_back(CreateSocketInfo(socket_id, socket)); + } + } + } + results_ = sockets_tcp_server::GetSockets::Results::Create(socket_infos); +} + +} // namespace core_api +} // namespace extensions diff --git a/extensions/browser/api/sockets_tcp_server/sockets_tcp_server_api.h b/extensions/browser/api/sockets_tcp_server/sockets_tcp_server_api.h new file mode 100644 index 0000000..89d8cb8 --- /dev/null +++ b/extensions/browser/api/sockets_tcp_server/sockets_tcp_server_api.h @@ -0,0 +1,178 @@ +// Copyright 2014 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 EXTENSIONS_BROWSER_API_SOCKETS_TCP_SERVER_SOCKETS_TCP_SERVER_API_H_ +#define EXTENSIONS_BROWSER_API_SOCKETS_TCP_SERVER_SOCKETS_TCP_SERVER_API_H_ + +#include "extensions/browser/api/socket/socket_api.h" +#include "extensions/common/api/sockets_tcp_server.h" + +namespace extensions { +class ResumableTCPServerSocket; +} + +namespace extensions { +namespace core_api { + +class TCPServerSocketAsyncApiFunction : public SocketAsyncApiFunction { + protected: + virtual ~TCPServerSocketAsyncApiFunction(); + + virtual scoped_ptr<SocketResourceManagerInterface> + CreateSocketResourceManager() OVERRIDE; + + ResumableTCPServerSocket* GetTcpSocket(int socket_id); +}; + +class SocketsTcpServerCreateFunction : public TCPServerSocketAsyncApiFunction { + public: + DECLARE_EXTENSION_FUNCTION("sockets.tcpServer.create", + SOCKETS_TCP_SERVER_CREATE) + + SocketsTcpServerCreateFunction(); + + protected: + virtual ~SocketsTcpServerCreateFunction(); + + // AsyncApiFunction: + virtual bool Prepare() OVERRIDE; + virtual void Work() OVERRIDE; + + private: + FRIEND_TEST_ALL_PREFIXES(SocketsTcpServerUnitTest, Create); + scoped_ptr<sockets_tcp_server::Create::Params> params_; +}; + +class SocketsTcpServerUpdateFunction : public TCPServerSocketAsyncApiFunction { + public: + DECLARE_EXTENSION_FUNCTION("sockets.tcpServer.update", + SOCKETS_TCP_SERVER_UPDATE) + + SocketsTcpServerUpdateFunction(); + + protected: + virtual ~SocketsTcpServerUpdateFunction(); + + // AsyncApiFunction: + virtual bool Prepare() OVERRIDE; + virtual void Work() OVERRIDE; + + private: + scoped_ptr<sockets_tcp_server::Update::Params> params_; +}; + +class SocketsTcpServerSetPausedFunction + : public TCPServerSocketAsyncApiFunction { + public: + DECLARE_EXTENSION_FUNCTION("sockets.tcpServer.setPaused", + SOCKETS_TCP_SERVER_SETPAUSED) + + SocketsTcpServerSetPausedFunction(); + + protected: + virtual ~SocketsTcpServerSetPausedFunction(); + + // AsyncApiFunction + virtual bool Prepare() OVERRIDE; + virtual void Work() OVERRIDE; + + private: + scoped_ptr<sockets_tcp_server::SetPaused::Params> params_; + TCPServerSocketEventDispatcher* socket_event_dispatcher_; +}; + +class SocketsTcpServerListenFunction : public TCPServerSocketAsyncApiFunction { + public: + DECLARE_EXTENSION_FUNCTION("sockets.tcpServer.listen", + SOCKETS_TCP_SERVER_LISTEN) + + SocketsTcpServerListenFunction(); + + protected: + virtual ~SocketsTcpServerListenFunction(); + + // AsyncApiFunction: + virtual bool Prepare() OVERRIDE; + virtual void Work() OVERRIDE; + + private: + scoped_ptr<sockets_tcp_server::Listen::Params> params_; + TCPServerSocketEventDispatcher* socket_event_dispatcher_; +}; + +class SocketsTcpServerDisconnectFunction + : public TCPServerSocketAsyncApiFunction { + public: + DECLARE_EXTENSION_FUNCTION("sockets.tcpServer.disconnect", + SOCKETS_TCP_SERVER_DISCONNECT) + + SocketsTcpServerDisconnectFunction(); + + protected: + virtual ~SocketsTcpServerDisconnectFunction(); + + // AsyncApiFunction: + virtual bool Prepare() OVERRIDE; + virtual void Work() OVERRIDE; + + private: + scoped_ptr<sockets_tcp_server::Disconnect::Params> params_; +}; + +class SocketsTcpServerCloseFunction : public TCPServerSocketAsyncApiFunction { + public: + DECLARE_EXTENSION_FUNCTION("sockets.tcpServer.close", + SOCKETS_TCP_SERVER_CLOSE) + + SocketsTcpServerCloseFunction(); + + protected: + virtual ~SocketsTcpServerCloseFunction(); + + // AsyncApiFunction: + virtual bool Prepare() OVERRIDE; + virtual void Work() OVERRIDE; + + private: + scoped_ptr<sockets_tcp_server::Close::Params> params_; +}; + +class SocketsTcpServerGetInfoFunction : public TCPServerSocketAsyncApiFunction { + public: + DECLARE_EXTENSION_FUNCTION("sockets.tcpServer.getInfo", + SOCKETS_TCP_SERVER_GETINFO) + + SocketsTcpServerGetInfoFunction(); + + protected: + virtual ~SocketsTcpServerGetInfoFunction(); + + // AsyncApiFunction: + virtual bool Prepare() OVERRIDE; + virtual void Work() OVERRIDE; + + private: + scoped_ptr<sockets_tcp_server::GetInfo::Params> params_; +}; + +class SocketsTcpServerGetSocketsFunction + : public TCPServerSocketAsyncApiFunction { + public: + DECLARE_EXTENSION_FUNCTION("sockets.tcpServer.getSockets", + SOCKETS_TCP_SERVER_GETSOCKETS) + + SocketsTcpServerGetSocketsFunction(); + + protected: + virtual ~SocketsTcpServerGetSocketsFunction(); + + // AsyncApiFunction: + virtual bool Prepare() OVERRIDE; + virtual void Work() OVERRIDE; +}; + +} // namespace core_api +} // namespace extensions + +#endif // EXTENSIONS_BROWSER_API_SOCKETS_TCP_SERVER_SOCKETS_TCP_SERVER_API_H_ diff --git a/extensions/browser/api/sockets_tcp_server/tcp_server_socket_event_dispatcher.cc b/extensions/browser/api/sockets_tcp_server/tcp_server_socket_event_dispatcher.cc new file mode 100644 index 0000000..31eeeab --- /dev/null +++ b/extensions/browser/api/sockets_tcp_server/tcp_server_socket_event_dispatcher.cc @@ -0,0 +1,199 @@ +// Copyright 2014 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 "extensions/browser/api/sockets_tcp_server/tcp_server_socket_event_dispatcher.h" + +#include "extensions/browser/api/socket/tcp_socket.h" +#include "extensions/browser/event_router.h" +#include "extensions/browser/extension_system.h" +#include "extensions/browser/extensions_browser_client.h" +#include "net/base/net_errors.h" + +namespace extensions { +namespace core_api { + +using content::BrowserThread; + +static base::LazyInstance< + BrowserContextKeyedAPIFactory<TCPServerSocketEventDispatcher> > g_factory = + LAZY_INSTANCE_INITIALIZER; + +// static +BrowserContextKeyedAPIFactory<TCPServerSocketEventDispatcher>* +TCPServerSocketEventDispatcher::GetFactoryInstance() { + return g_factory.Pointer(); +} + +// static +TCPServerSocketEventDispatcher* TCPServerSocketEventDispatcher::Get( + content::BrowserContext* context) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + + return BrowserContextKeyedAPIFactory<TCPServerSocketEventDispatcher>::Get( + context); +} + +TCPServerSocketEventDispatcher::TCPServerSocketEventDispatcher( + content::BrowserContext* context) + : thread_id_(Socket::kThreadId), browser_context_(context) { + ApiResourceManager<ResumableTCPServerSocket>* server_manager = + ApiResourceManager<ResumableTCPServerSocket>::Get(browser_context_); + DCHECK(server_manager) + << "There is no server socket manager. " + "If this assertion is failing during a test, then it is likely that " + "TestExtensionSystem is failing to provide an instance of " + "ApiResourceManager<ResumableTCPServerSocket>."; + server_sockets_ = server_manager->data_; + + ApiResourceManager<ResumableTCPSocket>* client_manager = + ApiResourceManager<ResumableTCPSocket>::Get(browser_context_); + DCHECK(client_manager) + << "There is no client socket manager. " + "If this assertion is failing during a test, then it is likely that " + "TestExtensionSystem is failing to provide an instance of " + "ApiResourceManager<ResumableTCPSocket>."; + client_sockets_ = client_manager->data_; +} + +TCPServerSocketEventDispatcher::~TCPServerSocketEventDispatcher() {} + +TCPServerSocketEventDispatcher::AcceptParams::AcceptParams() {} + +TCPServerSocketEventDispatcher::AcceptParams::~AcceptParams() {} + +void TCPServerSocketEventDispatcher::OnServerSocketListen( + const std::string& extension_id, + int socket_id) { + DCHECK(BrowserThread::CurrentlyOn(thread_id_)); + + StartSocketAccept(extension_id, socket_id); +} + +void TCPServerSocketEventDispatcher::OnServerSocketResume( + const std::string& extension_id, + int socket_id) { + DCHECK(BrowserThread::CurrentlyOn(thread_id_)); + + StartSocketAccept(extension_id, socket_id); +} + +void TCPServerSocketEventDispatcher::StartSocketAccept( + const std::string& extension_id, + int socket_id) { + DCHECK(BrowserThread::CurrentlyOn(thread_id_)); + + AcceptParams params; + params.thread_id = thread_id_; + params.browser_context_id = browser_context_; + params.extension_id = extension_id; + params.server_sockets = server_sockets_; + params.client_sockets = client_sockets_; + params.socket_id = socket_id; + + StartAccept(params); +} + +// static +void TCPServerSocketEventDispatcher::StartAccept(const AcceptParams& params) { + DCHECK(BrowserThread::CurrentlyOn(params.thread_id)); + + ResumableTCPServerSocket* socket = + params.server_sockets->Get(params.extension_id, params.socket_id); + if (!socket) { + // This can happen if the socket is closed while our callback is active. + return; + } + DCHECK(params.extension_id == socket->owner_extension_id()) + << "Socket has wrong owner."; + + // Don't start another accept if the socket has been paused. + if (socket->paused()) + return; + + socket->Accept( + base::Bind(&TCPServerSocketEventDispatcher::AcceptCallback, params)); +} + +// static +void TCPServerSocketEventDispatcher::AcceptCallback( + const AcceptParams& params, + int result_code, + net::TCPClientSocket* socket) { + DCHECK(BrowserThread::CurrentlyOn(params.thread_id)); + + if (result_code >= 0) { + ResumableTCPSocket* client_socket = + new ResumableTCPSocket(socket, params.extension_id, true); + client_socket->set_paused(true); + int client_socket_id = params.client_sockets->Add(client_socket); + + // Dispatch "onAccept" event. + sockets_tcp_server::AcceptInfo accept_info; + accept_info.socket_id = params.socket_id; + accept_info.client_socket_id = client_socket_id; + scoped_ptr<base::ListValue> args = + sockets_tcp_server::OnAccept::Create(accept_info); + scoped_ptr<Event> event( + new Event(sockets_tcp_server::OnAccept::kEventName, args.Pass())); + PostEvent(params, event.Pass()); + + // Post a task to delay the "accept" until the socket is available, as + // calling StartAccept at this point would error with ERR_IO_PENDING. + BrowserThread::PostTask( + params.thread_id, + FROM_HERE, + base::Bind(&TCPServerSocketEventDispatcher::StartAccept, params)); + } else { + // Dispatch "onAcceptError" event but don't start another accept to avoid + // potential infinite "accepts" if we have a persistent network error. + sockets_tcp_server::AcceptErrorInfo accept_error_info; + accept_error_info.socket_id = params.socket_id; + accept_error_info.result_code = result_code; + scoped_ptr<base::ListValue> args = + sockets_tcp_server::OnAcceptError::Create(accept_error_info); + scoped_ptr<Event> event( + new Event(sockets_tcp_server::OnAcceptError::kEventName, args.Pass())); + PostEvent(params, event.Pass()); + + // Since we got an error, the socket is now "paused" until the application + // "resumes" it. + ResumableTCPServerSocket* socket = + params.server_sockets->Get(params.extension_id, params.socket_id); + if (socket) { + socket->set_paused(true); + } + } +} + +// static +void TCPServerSocketEventDispatcher::PostEvent(const AcceptParams& params, + scoped_ptr<Event> event) { + DCHECK(BrowserThread::CurrentlyOn(params.thread_id)); + + BrowserThread::PostTask(BrowserThread::UI, + FROM_HERE, + base::Bind(&DispatchEvent, + params.browser_context_id, + params.extension_id, + base::Passed(event.Pass()))); +} + +// static +void TCPServerSocketEventDispatcher::DispatchEvent( + void* browser_context_id, + const std::string& extension_id, + scoped_ptr<Event> event) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + + content::BrowserContext* context = + reinterpret_cast<content::BrowserContext*>(browser_context_id); + if (!extensions::ExtensionsBrowserClient::Get()->IsValidContext(context)) + return; + EventRouter* router = ExtensionSystem::Get(context)->event_router(); + if (router) + router->DispatchEventToExtension(extension_id, event.Pass()); +} + +} // namespace core_api +} // namespace extensions diff --git a/extensions/browser/api/sockets_tcp_server/tcp_server_socket_event_dispatcher.h b/extensions/browser/api/sockets_tcp_server/tcp_server_socket_event_dispatcher.h new file mode 100644 index 0000000..d4d604c --- /dev/null +++ b/extensions/browser/api/sockets_tcp_server/tcp_server_socket_event_dispatcher.h @@ -0,0 +1,100 @@ +// Copyright 2014 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 EXTENSIONS_BROWSER_API_SOCKETS_TCP_SERVER_TCP_SERVER_SOCKET_EVENT_DISPATCHER_H_ +#define EXTENSIONS_BROWSER_API_SOCKETS_TCP_SERVER_TCP_SERVER_SOCKET_EVENT_DISPATCHER_H_ + +#include "extensions/browser/api/api_resource_manager.h" +#include "extensions/browser/api/sockets_tcp/sockets_tcp_api.h" +#include "extensions/browser/api/sockets_tcp_server/sockets_tcp_server_api.h" + +namespace content { +class BrowserContext; +} + +namespace extensions { +struct Event; +class ResumableTCPSocket; +} + +namespace extensions { +namespace core_api { + +// Dispatch events related to "sockets.tcp" sockets from callback on native +// socket instances. There is one instance per profile. +class TCPServerSocketEventDispatcher + : public BrowserContextKeyedAPI, + public base::SupportsWeakPtr<TCPServerSocketEventDispatcher> { + public: + explicit TCPServerSocketEventDispatcher(content::BrowserContext* context); + virtual ~TCPServerSocketEventDispatcher(); + + // Server socket is active, start accepting connections from it. + void OnServerSocketListen(const std::string& extension_id, int socket_id); + + // Server socket is active again, start accepting connections from it. + void OnServerSocketResume(const std::string& extension_id, int socket_id); + + // BrowserContextKeyedAPI implementation. + static BrowserContextKeyedAPIFactory<TCPServerSocketEventDispatcher>* + GetFactoryInstance(); + + // Convenience method to get the SocketEventDispatcher for a profile. + static TCPServerSocketEventDispatcher* Get(content::BrowserContext* context); + + private: + typedef ApiResourceManager<ResumableTCPServerSocket>::ApiResourceData + ServerSocketData; + typedef ApiResourceManager<ResumableTCPSocket>::ApiResourceData + ClientSocketData; + friend class BrowserContextKeyedAPIFactory<TCPServerSocketEventDispatcher>; + // BrowserContextKeyedAPI implementation. + static const char* service_name() { return "TCPServerSocketEventDispatcher"; } + static const bool kServiceHasOwnInstanceInIncognito = true; + static const bool kServiceIsNULLWhileTesting = true; + + // base::Bind supports methods with up to 6 parameters. AcceptParams is used + // as a workaround that limitation for invoking StartAccept. + struct AcceptParams { + AcceptParams(); + ~AcceptParams(); + + content::BrowserThread::ID thread_id; + void* browser_context_id; + std::string extension_id; + scoped_refptr<ServerSocketData> server_sockets; + scoped_refptr<ClientSocketData> client_sockets; + int socket_id; + }; + + // Start an accept and register a callback. + void StartSocketAccept(const std::string& extension_id, int socket_id); + + // Start an accept and register a callback. + static void StartAccept(const AcceptParams& params); + + // Called when socket accepts a new connection. + static void AcceptCallback(const AcceptParams& params, + int result_code, + net::TCPClientSocket* socket); + + // Post an extension event from |thread_id| to UI thread + static void PostEvent(const AcceptParams& params, scoped_ptr<Event> event); + + // Dispatch an extension event on to EventRouter instance on UI thread. + static void DispatchEvent(void* browser_context_id, + const std::string& extension_id, + scoped_ptr<Event> event); + + // Usually IO thread (except for unit testing). + content::BrowserThread::ID thread_id_; + content::BrowserContext* const browser_context_; + scoped_refptr<ServerSocketData> server_sockets_; + scoped_refptr<ClientSocketData> client_sockets_; +}; + +} // namespace core_api +} // namespace extensions + +#endif // EXTENSIONS_BROWSER_API_SOCKETS_TCP_SERVER_TCP_SERVER_SOCKET_EVENT_DISPATCHER_H_ diff --git a/extensions/browser/api/sockets_udp/sockets_udp/OWNERS b/extensions/browser/api/sockets_udp/sockets_udp/OWNERS new file mode 100644 index 0000000..3e30c82 --- /dev/null +++ b/extensions/browser/api/sockets_udp/sockets_udp/OWNERS @@ -0,0 +1 @@ +rpaquay@chromium.org diff --git a/extensions/browser/api/sockets_udp/sockets_udp_api.cc b/extensions/browser/api/sockets_udp/sockets_udp_api.cc new file mode 100644 index 0000000..9ccb4a7 --- /dev/null +++ b/extensions/browser/api/sockets_udp/sockets_udp_api.cc @@ -0,0 +1,505 @@ +// Copyright 2014 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 "extensions/browser/api/sockets_udp/sockets_udp_api.h" + +#include "chrome/common/extensions/api/sockets/sockets_manifest_data.h" +#include "content/public/common/socket_permission_request.h" +#include "extensions/browser/api/socket/udp_socket.h" +#include "extensions/browser/api/sockets_udp/udp_socket_event_dispatcher.h" +#include "net/base/net_errors.h" + +namespace extensions { +namespace core_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())); + } + socket_info->paused = socket->paused(); + + // 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(); +} + +SocketsUdpSetPausedFunction::SocketsUdpSetPausedFunction() + : socket_event_dispatcher_(NULL) {} + +SocketsUdpSetPausedFunction::~SocketsUdpSetPausedFunction() {} + +bool SocketsUdpSetPausedFunction::Prepare() { + params_ = core_api::sockets_udp::SetPaused::Params::Create(*args_); + EXTENSION_FUNCTION_VALIDATE(params_.get()); + + socket_event_dispatcher_ = UDPSocketEventDispatcher::Get(browser_context()); + DCHECK(socket_event_dispatcher_) + << "There is no socket event dispatcher. " + "If this assertion is failing during a test, then it is likely that " + "TestExtensionSystem is failing to provide an instance of " + "UDPSocketEventDispatcher."; + return socket_event_dispatcher_ != NULL; +} + +void SocketsUdpSetPausedFunction::Work() { + ResumableUDPSocket* socket = GetUdpSocket(params_->socket_id); + if (!socket) { + error_ = kSocketNotFoundError; + return; + } + + if (socket->paused() != params_->paused) { + socket->set_paused(params_->paused); + if (socket->IsBound() && !params_->paused) { + socket_event_dispatcher_->OnSocketResume(extension_->id(), + params_->socket_id); + } + } + + results_ = sockets_udp::SetPaused::Results::Create(); +} + +SocketsUdpBindFunction::SocketsUdpBindFunction() + : socket_event_dispatcher_(NULL) {} + +SocketsUdpBindFunction::~SocketsUdpBindFunction() {} + +bool SocketsUdpBindFunction::Prepare() { + params_ = sockets_udp::Bind::Params::Create(*args_); + EXTENSION_FUNCTION_VALIDATE(params_.get()); + + socket_event_dispatcher_ = UDPSocketEventDispatcher::Get(browser_context()); + DCHECK(socket_event_dispatcher_) + << "There is no socket event dispatcher. " + "If this assertion is failing during a test, then it is likely that " + "TestExtensionSystem is failing to provide an instance of " + "UDPSocketEventDispatcher."; + return socket_event_dispatcher_ != NULL; +} + +void SocketsUdpBindFunction::Work() { + ResumableUDPSocket* socket = GetUdpSocket(params_->socket_id); + if (!socket) { + error_ = kSocketNotFoundError; + return; + } + + content::SocketPermissionRequest param( + SocketPermissionRequest::UDP_BIND, params_->address, params_->port); + if (!SocketsManifestData::CheckRequest(GetExtension(), param)) { + error_ = kPermissionError; + return; + } + + int net_result = socket->Bind(params_->address, params_->port); + if (net_result == net::OK) { + socket_event_dispatcher_->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; + } + + content::SocketPermissionRequest param( + SocketPermissionRequest::UDP_SEND_TO, params_->address, params_->port); + if (!SocketsManifestData::CheckRequest(GetExtension(), param)) { + 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_sent) { + CHECK(net_result <= net::OK) << "Network status code must be < 0"; + + sockets_udp::SendInfo send_info; + send_info.result_code = net_result; + if (net_result == net::OK) { + send_info.bytes_sent.reset(new int(bytes_sent)); + } + + 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; + } + + socket->Disconnect(); + 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; + } + + content::SocketPermissionRequest param( + SocketPermissionRequest::UDP_MULTICAST_MEMBERSHIP, + kWildcardAddress, + kWildcardPort); + if (!SocketsManifestData::CheckRequest(GetExtension(), param)) { + 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_ = core_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; + } + + content::SocketPermissionRequest param( + SocketPermissionRequest::UDP_MULTICAST_MEMBERSHIP, + kWildcardAddress, + kWildcardPort); + if (!SocketsManifestData::CheckRequest(GetExtension(), param)) { + 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_ = + core_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_ = + core_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_ = core_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; + } + + content::SocketPermissionRequest param( + SocketPermissionRequest::UDP_MULTICAST_MEMBERSHIP, + kWildcardAddress, + kWildcardPort); + if (!SocketsManifestData::CheckRequest(GetExtension(), param)) { + error_ = kPermissionError; + return; + } + + const std::vector<std::string>& groups = socket->GetJoinedGroups(); + results_ = sockets_udp::GetJoinedGroups::Results::Create(groups); +} + +} // namespace core_api +} // namespace extensions diff --git a/extensions/browser/api/sockets_udp/sockets_udp_api.h b/extensions/browser/api/sockets_udp/sockets_udp_api.h new file mode 100644 index 0000000..16e61d4 --- /dev/null +++ b/extensions/browser/api/sockets_udp/sockets_udp_api.h @@ -0,0 +1,279 @@ +// Copyright 2014 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 EXTENSIONS_BROWSER_API_SOCKETS_UDP_SOCKETS_UDP_API_H_ +#define EXTENSIONS_BROWSER_API_SOCKETS_UDP_SOCKETS_UDP_API_H_ + +#include "extensions/browser/api/socket/socket_api.h" +#include "extensions/common/api/sockets_udp.h" + +namespace extensions { +class ResumableUDPSocket; +} + +namespace extensions { +namespace core_api { + +class UDPSocketEventDispatcher; + +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 SocketsUdpSetPausedFunction : public UDPSocketAsyncApiFunction { + public: + DECLARE_EXTENSION_FUNCTION("sockets.udp.setPaused", SOCKETS_UDP_SETPAUSED) + + SocketsUdpSetPausedFunction(); + + protected: + virtual ~SocketsUdpSetPausedFunction(); + + // AsyncApiFunction + virtual bool Prepare() OVERRIDE; + virtual void Work() OVERRIDE; + + private: + scoped_ptr<sockets_udp::SetPaused::Params> params_; + UDPSocketEventDispatcher* socket_event_dispatcher_; +}; + +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_; + UDPSocketEventDispatcher* socket_event_dispatcher_; +}; + +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_sent); + + // 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 core_api +} // namespace extensions + +#endif // EXTENSIONS_BROWSER_API_SOCKETS_UDP_SOCKETS_UDP_API_H_ diff --git a/extensions/browser/api/sockets_udp/udp_socket_event_dispatcher.cc b/extensions/browser/api/sockets_udp/udp_socket_event_dispatcher.cc new file mode 100644 index 0000000..e985dfb --- /dev/null +++ b/extensions/browser/api/sockets_udp/udp_socket_event_dispatcher.cc @@ -0,0 +1,183 @@ +// Copyright 2014 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 "extensions/browser/api/sockets_udp/udp_socket_event_dispatcher.h" + +#include "extensions/browser/api/socket/udp_socket.h" +#include "extensions/browser/event_router.h" +#include "extensions/browser/extension_system.h" +#include "extensions/browser/extensions_browser_client.h" +#include "net/base/net_errors.h" + +namespace extensions { +namespace core_api { + +using content::BrowserThread; + +static base::LazyInstance< + BrowserContextKeyedAPIFactory<UDPSocketEventDispatcher> > g_factory = + LAZY_INSTANCE_INITIALIZER; + +// static +BrowserContextKeyedAPIFactory<UDPSocketEventDispatcher>* +UDPSocketEventDispatcher::GetFactoryInstance() { + return g_factory.Pointer(); +} + +// static +UDPSocketEventDispatcher* UDPSocketEventDispatcher::Get( + content::BrowserContext* context) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + + return BrowserContextKeyedAPIFactory<UDPSocketEventDispatcher>::Get(context); +} + +UDPSocketEventDispatcher::UDPSocketEventDispatcher( + content::BrowserContext* context) + : thread_id_(Socket::kThreadId), browser_context_(context) { + ApiResourceManager<ResumableUDPSocket>* manager = + ApiResourceManager<ResumableUDPSocket>::Get(browser_context_); + 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>."; + sockets_ = manager->data_; +} + +UDPSocketEventDispatcher::~UDPSocketEventDispatcher() {} + +UDPSocketEventDispatcher::ReceiveParams::ReceiveParams() {} + +UDPSocketEventDispatcher::ReceiveParams::~ReceiveParams() {} + +void UDPSocketEventDispatcher::OnSocketBind(const std::string& extension_id, + int socket_id) { + OnSocketResume(extension_id, socket_id); +} + +void UDPSocketEventDispatcher::OnSocketResume(const std::string& extension_id, + int socket_id) { + DCHECK(BrowserThread::CurrentlyOn(thread_id_)); + + ReceiveParams params; + params.thread_id = thread_id_; + params.browser_context_id = browser_context_; + params.extension_id = extension_id; + params.sockets = sockets_; + params.socket_id = socket_id; + + StartReceive(params); +} + +/* static */ +void UDPSocketEventDispatcher::StartReceive(const ReceiveParams& params) { + DCHECK(BrowserThread::CurrentlyOn(params.thread_id)); + + ResumableUDPSocket* socket = + params.sockets->Get(params.extension_id, params.socket_id); + if (socket == NULL) { + // This can happen if the socket is closed while our callback is active. + return; + } + DCHECK(params.extension_id == socket->owner_extension_id()) + << "Socket has wrong owner."; + + // Don't start another read if the socket has been paused. + if (socket->paused()) + return; + + int buffer_size = (socket->buffer_size() <= 0 ? 4096 : socket->buffer_size()); + socket->RecvFrom( + buffer_size, + base::Bind(&UDPSocketEventDispatcher::ReceiveCallback, params)); +} + +/* static */ +void UDPSocketEventDispatcher::ReceiveCallback( + const ReceiveParams& params, + int bytes_read, + scoped_refptr<net::IOBuffer> io_buffer, + const std::string& address, + int port) { + DCHECK(BrowserThread::CurrentlyOn(params.thread_id)); + + // If |bytes_read| == 0, the message contained no data. + // If |bytes_read| < 0, there was a network error, and |bytes_read| is a value + // from "net::ERR_". + + if (bytes_read >= 0) { + // Dispatch "onReceive" event. + sockets_udp::ReceiveInfo receive_info; + receive_info.socket_id = params.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())); + PostEvent(params, 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. + BrowserThread::PostTask( + params.thread_id, + FROM_HERE, + base::Bind(&UDPSocketEventDispatcher::StartReceive, params)); + } else if (bytes_read == net::ERR_IO_PENDING) { + // This happens when resuming a socket which already had an + // active "recv" callback. + } else { + // Dispatch "onReceiveError" event but don't start another read to avoid + // potential infinite reads if we have a persistent network error. + sockets_udp::ReceiveErrorInfo receive_error_info; + receive_error_info.socket_id = params.socket_id; + receive_error_info.result_code = 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())); + PostEvent(params, event.Pass()); + + // Since we got an error, the socket is now "paused" until the application + // "resumes" it. + ResumableUDPSocket* socket = + params.sockets->Get(params.extension_id, params.socket_id); + if (socket) { + socket->set_paused(true); + } + } +} + +/* static */ +void UDPSocketEventDispatcher::PostEvent(const ReceiveParams& params, + scoped_ptr<Event> event) { + DCHECK(BrowserThread::CurrentlyOn(params.thread_id)); + + BrowserThread::PostTask(BrowserThread::UI, + FROM_HERE, + base::Bind(&DispatchEvent, + params.browser_context_id, + params.extension_id, + base::Passed(event.Pass()))); +} + +/*static*/ +void UDPSocketEventDispatcher::DispatchEvent(void* browser_context_id, + const std::string& extension_id, + scoped_ptr<Event> event) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + + content::BrowserContext* context = + reinterpret_cast<content::BrowserContext*>(browser_context_id); + if (!extensions::ExtensionsBrowserClient::Get()->IsValidContext(context)) + return; + EventRouter* router = ExtensionSystem::Get(context)->event_router(); + if (router) + router->DispatchEventToExtension(extension_id, event.Pass()); +} + +} // namespace core_api +} // namespace extensions diff --git a/extensions/browser/api/sockets_udp/udp_socket_event_dispatcher.h b/extensions/browser/api/sockets_udp/udp_socket_event_dispatcher.h new file mode 100644 index 0000000..e42ecb4 --- /dev/null +++ b/extensions/browser/api/sockets_udp/udp_socket_event_dispatcher.h @@ -0,0 +1,93 @@ +// Copyright 2014 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 EXTENSIONS_BROWSER_API_SOCKETS_UDP_UDP_SOCKET_EVENT_DISPATCHER_H_ +#define EXTENSIONS_BROWSER_API_SOCKETS_UDP_UDP_SOCKET_EVENT_DISPATCHER_H_ + +#include "extensions/browser/api/api_resource_manager.h" +#include "extensions/browser/api/sockets_udp/sockets_udp_api.h" + +namespace content { +class BrowserContext; +} + +namespace extensions { +struct Event; +class ResumableUDPSocket; +} + +namespace extensions { +namespace core_api { + +// Dispatch events related to "sockets.udp" sockets from callback on native +// socket instances. There is one instance per profile. +class UDPSocketEventDispatcher + : public BrowserContextKeyedAPI, + public base::SupportsWeakPtr<UDPSocketEventDispatcher> { + public: + explicit UDPSocketEventDispatcher(content::BrowserContext* context); + virtual ~UDPSocketEventDispatcher(); + + // Socket is active, start receving from it. + void OnSocketBind(const std::string& extension_id, int socket_id); + + // Socket is active again, start receiving data from it. + void OnSocketResume(const std::string& extension_id, int socket_id); + + // BrowserContextKeyedAPI implementation. + static BrowserContextKeyedAPIFactory<UDPSocketEventDispatcher>* + GetFactoryInstance(); + + // Convenience method to get the SocketEventDispatcher for a profile. + static UDPSocketEventDispatcher* Get(content::BrowserContext* context); + + private: + typedef ApiResourceManager<ResumableUDPSocket>::ApiResourceData SocketData; + friend class BrowserContextKeyedAPIFactory<UDPSocketEventDispatcher>; + // BrowserContextKeyedAPI implementation. + static const char* service_name() { return "UDPSocketEventDispatcher"; } + static const bool kServiceHasOwnInstanceInIncognito = true; + static const bool kServiceIsNULLWhileTesting = true; + + // base::Bind supports methods with up to 6 parameters. ReceiveParams is used + // as a workaround that limitation for invoking StartReceive. + struct ReceiveParams { + ReceiveParams(); + ~ReceiveParams(); + + content::BrowserThread::ID thread_id; + void* browser_context_id; + std::string extension_id; + scoped_refptr<SocketData> sockets; + int socket_id; + }; + + // Start a receive and register a callback. + static void StartReceive(const ReceiveParams& params); + + // Called when socket receive data. + static void ReceiveCallback(const ReceiveParams& params, + int bytes_read, + scoped_refptr<net::IOBuffer> io_buffer, + const std::string& address, + int port); + + // Post an extension event from IO to UI thread + static void PostEvent(const ReceiveParams& params, scoped_ptr<Event> event); + + // Dispatch an extension event on to EventRouter instance on UI thread. + static void DispatchEvent(void* browser_context_id, + const std::string& extension_id, + scoped_ptr<Event> event); + + // Usually IO thread (except for unit testing). + content::BrowserThread::ID thread_id_; + content::BrowserContext* const browser_context_; + scoped_refptr<SocketData> sockets_; +}; + +} // namespace core_api +} // namespace extensions + +#endif // EXTENSIONS_BROWSER_API_SOCKETS_UDP_UDP_SOCKET_EVENT_DISPATCHER_H_ |