summaryrefslogtreecommitdiffstats
path: root/chrome/browser/extensions/api
diff options
context:
space:
mode:
authorrpaquay@chromium.org <rpaquay@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2013-10-18 03:49:15 +0000
committerrpaquay@chromium.org <rpaquay@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2013-10-18 03:49:15 +0000
commit96a0c067d816576735856452608a03bd7e914504 (patch)
tree513fe241364a307f7c83963d05f028055a66ab76 /chrome/browser/extensions/api
parentb1d1ea068f578e933054c8573e985402ca014319 (diff)
downloadchromium_src-96a0c067d816576735856452608a03bd7e914504.zip
chromium_src-96a0c067d816576735856452608a03bd7e914504.tar.gz
chromium_src-96a0c067d816576735856452608a03bd7e914504.tar.bz2
sockets.tcpServer API implementation.
Implement a new API for TCP *server* sockets. This CL is similar to sockets.tcp (see https://codereview.chromium.org/24684002) and follows the same design pattern. BUG=165273 BUG=173241 Review URL: https://codereview.chromium.org/27076004 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@229304 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome/browser/extensions/api')
-rw-r--r--chrome/browser/extensions/api/api_resource_manager.h2
-rw-r--r--chrome/browser/extensions/api/socket/tcp_socket.cc31
-rw-r--r--chrome/browser/extensions/api/socket/tcp_socket.h36
-rw-r--r--chrome/browser/extensions/api/sockets_tcp_server/sockets_tcp_server_api.cc298
-rw-r--r--chrome/browser/extensions/api/sockets_tcp_server/sockets_tcp_server_api.h179
-rw-r--r--chrome/browser/extensions/api/sockets_tcp_server/sockets_tcp_server_api_unittest.cc94
-rw-r--r--chrome/browser/extensions/api/sockets_tcp_server/sockets_tcp_server_apitest.cc111
-rw-r--r--chrome/browser/extensions/api/sockets_tcp_server/tcp_server_socket_event_dispatcher.cc198
-rw-r--r--chrome/browser/extensions/api/sockets_tcp_server/tcp_server_socket_event_dispatcher.h99
9 files changed, 1048 insertions, 0 deletions
diff --git a/chrome/browser/extensions/api/api_resource_manager.h b/chrome/browser/extensions/api/api_resource_manager.h
index d39c41b..28adefd 100644
--- a/chrome/browser/extensions/api/api_resource_manager.h
+++ b/chrome/browser/extensions/api/api_resource_manager.h
@@ -24,6 +24,7 @@
namespace extensions {
namespace api {
+class TCPServerSocketEventDispatcher;
class TCPSocketEventDispatcher;
class UDPSocketEventDispatcher;
}
@@ -153,6 +154,7 @@ class ApiResourceManager : public ProfileKeyedAPI,
}
private:
+ friend class api::TCPServerSocketEventDispatcher;
friend class api::TCPSocketEventDispatcher;
friend class api::UDPSocketEventDispatcher;
friend class ProfileKeyedAPIFactory<ApiResourceManager<T> >;
diff --git a/chrome/browser/extensions/api/socket/tcp_socket.cc b/chrome/browser/extensions/api/socket/tcp_socket.cc
index 8401dc26..4c0de16 100644
--- a/chrome/browser/extensions/api/socket/tcp_socket.cc
+++ b/chrome/browser/extensions/api/socket/tcp_socket.cc
@@ -28,6 +28,17 @@ ApiResourceManager<ResumableTCPSocket>::GetFactoryInstance() {
return &g_factory.Get();
}
+static base::LazyInstance<ProfileKeyedAPIFactory<
+ ApiResourceManager<ResumableTCPServerSocket> > >
+ g_server_factory = LAZY_INSTANCE_INITIALIZER;
+
+// static
+template <>
+ProfileKeyedAPIFactory<ApiResourceManager<ResumableTCPServerSocket> >*
+ApiResourceManager<ResumableTCPServerSocket>::GetFactoryInstance() {
+ return &g_server_factory.Get();
+}
+
TCPSocket::TCPSocket(const std::string& owner_extension_id)
: Socket(owner_extension_id),
socket_mode_(UNKNOWN) {
@@ -304,8 +315,28 @@ ResumableTCPSocket::ResumableTCPSocket(const std::string& owner_extension_id)
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::persistent() const {
return persistent_;
}
+ResumableTCPServerSocket::ResumableTCPServerSocket(
+ const std::string& owner_extension_id)
+ : TCPSocket(owner_extension_id),
+ persistent_(false),
+ paused_(false) {
+}
+
+bool ResumableTCPServerSocket::persistent() const {
+ return persistent_;
+}
+
} // namespace extensions
diff --git a/chrome/browser/extensions/api/socket/tcp_socket.h b/chrome/browser/extensions/api/socket/tcp_socket.h
index c684824..d5b4844 100644
--- a/chrome/browser/extensions/api/socket/tcp_socket.h
+++ b/chrome/browser/extensions/api/socket/tcp_socket.h
@@ -102,10 +102,14 @@ class TCPSocket : public Socket {
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);
const std::string& name() const { return name_; }
void set_name(const std::string& name) { name_ = name; }
+ // Overriden from ApiResource
virtual bool persistent() const OVERRIDE;
void set_persistent(bool persistent) { persistent_ = persistent; }
@@ -133,6 +137,38 @@ class ResumableTCPSocket : public TCPSocket {
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);
+
+ const std::string& name() const { return name_; }
+ void set_name(const std::string& name) { name_ = name; }
+
+ virtual bool persistent() const OVERRIDE;
+ 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 // CHROME_BROWSER_EXTENSIONS_API_SOCKET_TCP_SOCKET_H_
diff --git a/chrome/browser/extensions/api/sockets_tcp_server/sockets_tcp_server_api.cc b/chrome/browser/extensions/api/sockets_tcp_server/sockets_tcp_server_api.cc
new file mode 100644
index 0000000..0827e29
--- /dev/null
+++ b/chrome/browser/extensions/api/sockets_tcp_server/sockets_tcp_server_api.cc
@@ -0,0 +1,298 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/extensions/api/sockets_tcp_server/sockets_tcp_server_api.h"
+
+#include "chrome/browser/extensions/api/socket/tcp_socket.h"
+#include "chrome/browser/extensions/api/sockets_tcp_server/tcp_server_socket_event_dispatcher.h"
+#include "chrome/common/extensions/api/sockets/sockets_handler.h"
+#include "chrome/common/extensions/permissions/permissions_data.h"
+#include "chrome/common/extensions/permissions/socket_permission.h"
+#include "content/public/common/socket_permission_request.h"
+#include "net/base/net_errors.h"
+
+using content::SocketPermissionRequest;
+using extensions::ResumableTCPServerSocket;
+using extensions::api::sockets_tcp_server::SocketInfo;
+using extensions::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 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, &params_.get()->properties);
+ results_ = sockets_tcp_server::Update::Results::Create();
+}
+
+SocketsTcpServerSetPausedFunction::SocketsTcpServerSetPausedFunction()
+ : socket_event_dispatcher_(NULL) {}
+
+SocketsTcpServerSetPausedFunction::~SocketsTcpServerSetPausedFunction() {}
+
+bool SocketsTcpServerSetPausedFunction::Prepare() {
+ params_ = api::sockets_tcp_server::SetPaused::Params::Create(*args_);
+ EXTENSION_FUNCTION_VALIDATE(params_.get());
+
+ socket_event_dispatcher_ = TCPServerSocketEventDispatcher::Get(profile());
+ 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_ = api::sockets_tcp_server::Listen::Params::Create(*args_);
+ EXTENSION_FUNCTION_VALIDATE(params_.get());
+
+ socket_event_dispatcher_ = TCPServerSocketEventDispatcher::Get(profile());
+ 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 api
+} // namespace extensions
diff --git a/chrome/browser/extensions/api/sockets_tcp_server/sockets_tcp_server_api.h b/chrome/browser/extensions/api/sockets_tcp_server/sockets_tcp_server_api.h
new file mode 100644
index 0000000..439b857
--- /dev/null
+++ b/chrome/browser/extensions/api/sockets_tcp_server/sockets_tcp_server_api.h
@@ -0,0 +1,179 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_EXTENSIONS_API_SOCKETS_TCP_SERVER_SOCKETS_TCP_SERVER_API_H_
+#define CHROME_BROWSER_EXTENSIONS_API_SOCKETS_TCP_SERVER_SOCKETS_TCP_SERVER_API_H_
+
+#include "chrome/browser/extensions/api/socket/socket_api.h"
+#include "chrome/common/extensions/api/sockets_tcp_server.h"
+
+namespace extensions {
+class ResumableTCPServerSocket;
+}
+
+namespace extensions {
+namespace 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 api
+} // namespace extensions
+
+#endif // CHROME_BROWSER_EXTENSIONS_API_SOCKETS_TCP_SERVER_SOCKETS_TCP_SERVER_API_H_
diff --git a/chrome/browser/extensions/api/sockets_tcp_server/sockets_tcp_server_api_unittest.cc b/chrome/browser/extensions/api/sockets_tcp_server/sockets_tcp_server_api_unittest.cc
new file mode 100644
index 0000000..6ed0f9e
--- /dev/null
+++ b/chrome/browser/extensions/api/sockets_tcp_server/sockets_tcp_server_api_unittest.cc
@@ -0,0 +1,94 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/values.h"
+#include "chrome/browser/browser_process_impl.h"
+#include "chrome/browser/extensions/api/api_function.h"
+#include "chrome/browser/extensions/api/api_resource_manager.h"
+#include "chrome/browser/extensions/api/socket/socket.h"
+#include "chrome/browser/extensions/api/socket/tcp_socket.h"
+#include "chrome/browser/extensions/api/sockets_tcp_server/sockets_tcp_server_api.h"
+#include "chrome/browser/extensions/extension_function_test_utils.h"
+#include "chrome/browser/extensions/test_extension_system.h"
+#include "chrome/browser/profiles/profile_manager.h"
+#include "chrome/test/base/browser_with_test_window_test.h"
+#include "chrome/test/base/testing_browser_process.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace utils = extension_function_test_utils;
+
+namespace extensions {
+namespace api {
+
+static
+BrowserContextKeyedService* ApiResourceManagerTestFactory(
+ content::BrowserContext* profile) {
+ content::BrowserThread::ID id;
+ CHECK(content::BrowserThread::GetCurrentThreadIdentifier(&id));
+ return ApiResourceManager<ResumableTCPSocket>::
+ CreateApiResourceManagerForTest(static_cast<Profile*>(profile), id);
+}
+
+static
+BrowserContextKeyedService* ApiResourceManagerTestServerFactory(
+ content::BrowserContext* profile) {
+ content::BrowserThread::ID id;
+ CHECK(content::BrowserThread::GetCurrentThreadIdentifier(&id));
+ return ApiResourceManager<ResumableTCPServerSocket>::
+ CreateApiResourceManagerForTest(static_cast<Profile*>(profile), id);
+}
+
+class SocketsTcpServerUnitTest : public BrowserWithTestWindowTest {
+ public:
+ virtual void SetUp() {
+ BrowserWithTestWindowTest::SetUp();
+
+ ApiResourceManager<ResumableTCPSocket>::GetFactoryInstance()->
+ SetTestingFactoryAndUse(browser()->profile(),
+ ApiResourceManagerTestFactory);
+
+ ApiResourceManager<ResumableTCPServerSocket>::GetFactoryInstance()->
+ SetTestingFactoryAndUse(browser()->profile(),
+ ApiResourceManagerTestServerFactory);
+
+ extension_ = utils::CreateEmptyExtensionWithLocation(
+ extensions::Manifest::UNPACKED);
+ }
+
+ base::Value* RunFunctionWithExtension(
+ UIThreadExtensionFunction* function, const std::string& args) {
+ scoped_refptr<UIThreadExtensionFunction> delete_function(function);
+ function->set_extension(extension_.get());
+ return utils::RunFunctionAndReturnSingleResult(function, args, browser());
+ }
+
+ base::DictionaryValue* RunFunctionAndReturnDict(
+ UIThreadExtensionFunction* function, const std::string& args) {
+ base::Value* result = RunFunctionWithExtension(function, args);
+ return result ? utils::ToDictionary(result) : NULL;
+ }
+
+ protected:
+ scoped_refptr<extensions::Extension> extension_;
+};
+
+TEST_F(SocketsTcpServerUnitTest, Create) {
+ // Get BrowserThread
+ content::BrowserThread::ID id;
+ CHECK(content::BrowserThread::GetCurrentThreadIdentifier(&id));
+
+ // Create SocketCreateFunction and put it on BrowserThread
+ SocketsTcpServerCreateFunction *function =
+ new SocketsTcpServerCreateFunction();
+ function->set_work_thread_id(id);
+
+ // Run tests
+ scoped_ptr<base::DictionaryValue> result(RunFunctionAndReturnDict(
+ function, "[{\"persistent\": true, \"name\": \"foo\"}]"));
+ ASSERT_TRUE(result.get());
+}
+
+} // namespace api
+} // namespace extensions
diff --git a/chrome/browser/extensions/api/sockets_tcp_server/sockets_tcp_server_apitest.cc b/chrome/browser/extensions/api/sockets_tcp_server/sockets_tcp_server_apitest.cc
new file mode 100644
index 0000000..6e36a8c
--- /dev/null
+++ b/chrome/browser/extensions/api/sockets_tcp_server/sockets_tcp_server_apitest.cc
@@ -0,0 +1,111 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/memory/ref_counted.h"
+#include "base/path_service.h"
+#include "base/strings/stringprintf.h"
+#include "chrome/browser/extensions/api/dns/host_resolver_wrapper.h"
+#include "chrome/browser/extensions/api/dns/mock_host_resolver_creator.h"
+#include "chrome/browser/extensions/api/sockets_tcp_server/sockets_tcp_server_api.h"
+#include "chrome/browser/extensions/extension_apitest.h"
+#include "chrome/browser/extensions/extension_function_test_utils.h"
+#include "chrome/browser/extensions/extension_service.h"
+#include "chrome/browser/extensions/extension_test_message_listener.h"
+#include "chrome/browser/ui/browser.h"
+#include "chrome/browser/ui/extensions/application_launch.h"
+#include "chrome/common/chrome_paths.h"
+#include "chrome/test/base/in_process_browser_test.h"
+#include "chrome/test/base/ui_test_utils.h"
+#include "net/dns/mock_host_resolver.h"
+#include "net/test/spawned_test_server/spawned_test_server.h"
+
+using extensions::Extension;
+using extensions::api::SocketsTcpServerCreateFunction;
+
+namespace utils = extension_function_test_utils;
+
+namespace {
+
+// TODO(jschuh): Hanging plugin tests. crbug.com/244653
+#if defined(OS_WIN) && defined(ARCH_CPU_X86_64)
+#define MAYBE(x) DISABLED_##x
+#else
+#define MAYBE(x) x
+#endif
+
+const std::string kHostname = "127.0.0.1";
+const int kPort = 8888;
+
+class SocketsTcpServerApiTest : public ExtensionApiTest {
+ public:
+ SocketsTcpServerApiTest() : resolver_event_(true, false),
+ resolver_creator_(
+ new extensions::MockHostResolverCreator()) {
+ }
+
+ virtual void SetUpOnMainThread() OVERRIDE {
+ extensions::HostResolverWrapper::GetInstance()->SetHostResolverForTesting(
+ resolver_creator_->CreateMockHostResolver());
+ }
+
+ virtual void CleanUpOnMainThread() OVERRIDE {
+ extensions::HostResolverWrapper::GetInstance()->
+ SetHostResolverForTesting(NULL);
+ resolver_creator_->DeleteMockHostResolver();
+ }
+
+ private:
+ base::WaitableEvent resolver_event_;
+
+ // The MockHostResolver asserts that it's used on the same thread on which
+ // it's created, which is actually a stronger rule than its real counterpart.
+ // But that's fine; it's good practice.
+ scoped_refptr<extensions::MockHostResolverCreator> resolver_creator_;
+};
+
+} // namespace
+
+IN_PROC_BROWSER_TEST_F(SocketsTcpServerApiTest, SocketTCPCreateGood) {
+ scoped_refptr<SocketsTcpServerCreateFunction>
+ socket_create_function(new SocketsTcpServerCreateFunction());
+ scoped_refptr<Extension> empty_extension(utils::CreateEmptyExtension());
+
+ socket_create_function->set_extension(empty_extension.get());
+ socket_create_function->set_has_callback(true);
+
+ scoped_ptr<base::Value> result(utils::RunFunctionAndReturnSingleResult(
+ socket_create_function.get(), "[]", browser(), utils::NONE));
+ ASSERT_EQ(base::Value::TYPE_DICTIONARY, result->GetType());
+ base::DictionaryValue *value =
+ static_cast<base::DictionaryValue*>(result.get());
+ int socketId = -1;
+ EXPECT_TRUE(value->GetInteger("socketId", &socketId));
+ ASSERT_TRUE(socketId > 0);
+}
+
+IN_PROC_BROWSER_TEST_F(SocketsTcpServerApiTest, SocketTCPServerExtension) {
+ base::FilePath path = test_data_dir_.AppendASCII("sockets_tcp_server/api");
+ ResultCatcher catcher;
+ catcher.RestrictToProfile(browser()->profile());
+ ExtensionTestMessageListener listener("info_please", true);
+ ASSERT_TRUE(LoadExtension(path));
+ EXPECT_TRUE(listener.WaitUntilSatisfied());
+ listener.Reply(
+ base::StringPrintf("tcp_server:%s:%d", kHostname.c_str(), kPort));
+
+ EXPECT_TRUE(catcher.GetNextResult()) << catcher.message();
+}
+
+IN_PROC_BROWSER_TEST_F(SocketsTcpServerApiTest, SocketTCPServerUnbindOnUnload) {
+ base::FilePath path = test_data_dir_.AppendASCII("sockets_tcp_server/unload");
+ ResultCatcher catcher;
+ const Extension* extension = LoadExtension(path);
+ ASSERT_TRUE(extension);
+ EXPECT_TRUE(catcher.GetNextResult()) << catcher.message();
+
+ UnloadExtension(extension->id());
+
+ ASSERT_TRUE(LoadExtension(path)) << message_;
+ EXPECT_TRUE(catcher.GetNextResult()) << catcher.message();
+}
diff --git a/chrome/browser/extensions/api/sockets_tcp_server/tcp_server_socket_event_dispatcher.cc b/chrome/browser/extensions/api/sockets_tcp_server/tcp_server_socket_event_dispatcher.cc
new file mode 100644
index 0000000..d849e8c
--- /dev/null
+++ b/chrome/browser/extensions/api/sockets_tcp_server/tcp_server_socket_event_dispatcher.cc
@@ -0,0 +1,198 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/extensions/api/sockets_tcp_server/tcp_server_socket_event_dispatcher.h"
+
+#include "chrome/browser/browser_process.h"
+#include "chrome/browser/extensions/api/socket/tcp_socket.h"
+#include "chrome/browser/extensions/event_router.h"
+#include "chrome/browser/extensions/extension_system.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/profiles/profile_manager.h"
+#include "net/base/net_errors.h"
+
+namespace extensions {
+namespace api {
+
+using content::BrowserThread;
+
+static
+ base::LazyInstance<ProfileKeyedAPIFactory<TCPServerSocketEventDispatcher> >
+ g_factory = LAZY_INSTANCE_INITIALIZER;
+
+// static
+ProfileKeyedAPIFactory<TCPServerSocketEventDispatcher>*
+ TCPServerSocketEventDispatcher::GetFactoryInstance() {
+ return &g_factory.Get();
+}
+
+// static
+TCPServerSocketEventDispatcher* TCPServerSocketEventDispatcher::Get(
+ Profile* profile) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+
+ return ProfileKeyedAPIFactory<TCPServerSocketEventDispatcher>::GetForProfile(
+ profile);
+}
+
+TCPServerSocketEventDispatcher::TCPServerSocketEventDispatcher(Profile* profile)
+ : thread_id_(Socket::kThreadId),
+ profile_(profile) {
+ ApiResourceManager<ResumableTCPServerSocket>* server_manager =
+ ApiResourceManager<ResumableTCPServerSocket>::Get(profile);
+ 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(profile);
+ 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.profile_id = profile_;
+ 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.profile_id,
+ params.extension_id,
+ base::Passed(event.Pass())));
+}
+
+// static
+void TCPServerSocketEventDispatcher::DispatchEvent(
+ void* profile_id,
+ const std::string& extension_id,
+ scoped_ptr<Event> event) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+
+ Profile* profile = reinterpret_cast<Profile*>(profile_id);
+ if (!g_browser_process->profile_manager()->IsValidProfile(profile))
+ return;
+
+ EventRouter* router = ExtensionSystem::Get(profile)->event_router();
+ if (router)
+ router->DispatchEventToExtension(extension_id, event.Pass());
+}
+
+} // namespace api
+} // namespace extensions
diff --git a/chrome/browser/extensions/api/sockets_tcp_server/tcp_server_socket_event_dispatcher.h b/chrome/browser/extensions/api/sockets_tcp_server/tcp_server_socket_event_dispatcher.h
new file mode 100644
index 0000000..4d15bc3
--- /dev/null
+++ b/chrome/browser/extensions/api/sockets_tcp_server/tcp_server_socket_event_dispatcher.h
@@ -0,0 +1,99 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_EXTENSIONS_API_SOCKETS_TCP_SERVER_TCP_SERVER_SOCKET_EVENT_DISPATCHER_H_
+#define CHROME_BROWSER_EXTENSIONS_API_SOCKETS_TCP_SERVER_TCP_SERVER_SOCKET_EVENT_DISPATCHER_H_
+
+#include "chrome/browser/extensions/api/api_resource_manager.h"
+#include "chrome/browser/extensions/api/sockets_tcp/sockets_tcp_api.h"
+#include "chrome/browser/extensions/api/sockets_tcp_server/sockets_tcp_server_api.h"
+
+namespace extensions {
+struct Event;
+class ResumableTCPSocket;
+}
+
+namespace extensions {
+namespace api {
+
+// Dispatch events related to "sockets.tcp" sockets from callback on native
+// socket instances. There is one instance per profile.
+class TCPServerSocketEventDispatcher
+ : public ProfileKeyedAPI,
+ public base::SupportsWeakPtr<TCPServerSocketEventDispatcher> {
+ public:
+ explicit TCPServerSocketEventDispatcher(Profile* profile);
+ 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);
+
+ // ProfileKeyedAPI implementation.
+ static ProfileKeyedAPIFactory<TCPServerSocketEventDispatcher>*
+ GetFactoryInstance();
+
+ // Convenience method to get the SocketEventDispatcher for a profile.
+ static TCPServerSocketEventDispatcher* Get(Profile* profile);
+
+ private:
+ typedef ApiResourceManager<ResumableTCPServerSocket>::ApiResourceData
+ ServerSocketData;
+ typedef ApiResourceManager<ResumableTCPSocket>::ApiResourceData
+ ClientSocketData;
+ friend class ProfileKeyedAPIFactory<TCPServerSocketEventDispatcher>;
+ // ProfileKeyedAPI 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* profile_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* profile_id,
+ const std::string& extension_id,
+ scoped_ptr<Event> event);
+
+ // Usually IO thread (except for unit testing).
+ content::BrowserThread::ID thread_id_;
+ Profile* const profile_;
+ scoped_refptr<ServerSocketData> server_sockets_;
+ scoped_refptr<ClientSocketData> client_sockets_;
+};
+
+} // namespace api
+} // namespace extensions
+
+#endif // CHROME_BROWSER_EXTENSIONS_API_SOCKETS_TCP_SERVER_TCP_SERVER_SOCKET_EVENT_DISPATCHER_H_