Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(365)

Unified Diff: remoting/host/host_status_service.cc

Issue 11362267: Add status service for remoting host. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Created 8 years, 1 month ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « remoting/host/host_status_service.h ('k') | remoting/host/remoting_me2me_host.cc » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: remoting/host/host_status_service.cc
diff --git a/remoting/host/host_status_service.cc b/remoting/host/host_status_service.cc
new file mode 100644
index 0000000000000000000000000000000000000000..3141308f19587a484d7f9de2512bde4555e81a05
--- /dev/null
+++ b/remoting/host/host_status_service.cc
@@ -0,0 +1,224 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "remoting/host/host_status_service.h"
+
+#include "base/json/json_reader.h"
+#include "base/json/json_writer.h"
+#include "base/stl_util.h"
+#include "base/string_number_conversions.h"
+#include "base/string_util.h"
+#include "base/stringize_macros.h"
+#include "base/values.h"
+#include "net/base/ip_endpoint.h"
+#include "net/base/net_util.h"
+#include "remoting/host/websocket_connection.h"
+#include "remoting/host/websocket_listener.h"
+
+namespace remoting {
+
+namespace {
+
+// HostStatusService uses the first port available in the following range.
+const int kPortRangeMin = 12810;
+const int kPortRangeMax = 12820;
+
+const char kChromeExtensionUrlSchemePrefix[] = "chrome-extension://";
+
+#if defined(NDEBUG)
+const char* const kAllowedWebApplicationIds[] = {
+ "gbchcmhmhahfdphkhkmpfmihenigjmpp", // Chrome Remote Desktop
+ "kgngmbheleoaphbjbaiobfdepmghbfah", // Pre-release Chrome Remote Desktop
+ "odkaodonbgfohohmklejpjiejmcipmib", // Dogfood Chrome Remote Desktop
+ "ojoimpklfciegopdfgeenehpalipignm", // Chromoting canary
+};
+#endif
+
+// All messages we expect should be smaller than 64k.
+const uint64 kMaximumIncomingMessageSize = 65536;
+
+} // namespace
+
+class HostStatusService::Connection : public WebSocketConnection::Delegate {
+ public:
+ // |service| owns Connection connection objects and must outlive them.
+ Connection(HostStatusService* service,
+ scoped_ptr<WebSocketConnection> websocket);
+ virtual ~Connection();
+
+ // WebSocketConnection::Delegate interface.
+ virtual void OnWebSocketMessage(const std::string& message) OVERRIDE;
+ virtual void OnWebSocketClosed() OVERRIDE;
+
+ private:
+ // Sends message with the specified |method| and |data|.
+ void SendMessage(const std::string& method,
+ scoped_ptr<base::DictionaryValue> data);
+
+ // Closes the connection and destroys this object.
+ void Close();
+
+ HostStatusService* service_;
+ scoped_ptr<WebSocketConnection> websocket_;
+
+ DISALLOW_COPY_AND_ASSIGN(Connection);
+};
+
+HostStatusService::Connection::Connection(
+ HostStatusService* service,
+ scoped_ptr<WebSocketConnection> websocket)
+ : service_(service),
+ websocket_(websocket.Pass()) {
+ websocket_->Accept(this);
+ websocket_->set_maximum_message_size(kMaximumIncomingMessageSize);
+}
+
+HostStatusService::Connection::~Connection() {
+}
+
+void HostStatusService::Connection::OnWebSocketMessage(
+ const std::string& message) {
+ scoped_ptr<base::Value> json(
+ base::JSONReader::Read(message, base::JSON_ALLOW_TRAILING_COMMAS));
+
+ // Verify that we've received a valid JSON dictionary and extract |method| and
+ // |data| fields from it.
+ base::DictionaryValue* message_dict = NULL;
+ std::string method;
+ base::DictionaryValue* data = NULL;
+ if (!json.get() ||
+ !json->GetAsDictionary(&message_dict) ||
+ !message_dict->GetString("method", &method) ||
+ !message_dict->GetDictionary("data", &data)) {
+ LOG(ERROR) << "Received invalid message: " << message;
+ Close();
+ return;
+ }
+
+ if (method == "getHostStatus") {
+ SendMessage("hostStatus", service_->GetStatusMessage());
+ } else {
+ LOG(ERROR) << "Received message with unknown method: " << message;
+ scoped_ptr<base::DictionaryValue> response(new base::DictionaryValue());
+ response->SetString("method", method);
+ SendMessage("unsupportedMethod", response.Pass());
+ return;
+ }
+}
+
+void HostStatusService::Connection::OnWebSocketClosed() {
+ Close();
+}
+
+void HostStatusService::Connection::SendMessage(
+ const std::string& method,
+ scoped_ptr<base::DictionaryValue> data) {
+ scoped_ptr<base::DictionaryValue> message(new base::DictionaryValue());
+ message->SetString("method", method);
+ message->Set("data", data.release());
+
+ std::string message_json;
+ base::JSONWriter::Write(message.get(), &message_json);
+ websocket_->SendText(message_json);
+}
+
+void HostStatusService::Connection::Close() {
+ websocket_.reset();
+ service_->OnConnectionClosed(this);
+}
+
+HostStatusService::HostStatusService()
+ : started_(false) {
+ // TODO(sergeyu): Do we need to listen on IPv6 port too?
+ char ip[] = {127, 0, 0, 1};
+ net::IPAddressNumber localhost(ip, ip + sizeof(ip));
+ for (int port = kPortRangeMin; port < kPortRangeMax; ++port) {
+ net::IPEndPoint endpoint(localhost, port);
+ // base::Unretained is safe because we own |websocket_listener_|.
+ if (websocket_listener_.Listen(
+ endpoint,
+ base::Bind(&HostStatusService::OnNewConnection,
+ base::Unretained(this)))) {
+ service_host_name_ = "localhost:" + base::UintToString(port);
+ LOG(INFO) << "Listening for WebSocket connections on localhost:" << port;
+ break;
+ }
+ }
+}
+
+HostStatusService::~HostStatusService() {
+ STLDeleteElements(&connections_);
+}
+
+void HostStatusService::SetHostIsUp(const std::string& host_id) {
+ started_ = true;
+ host_id_ = host_id;
+}
+void HostStatusService::SetHostIsDown() {
+ started_ = false;
+ host_id_.clear();
+}
+
+// static
+bool HostStatusService::IsAllowedOrigin(const std::string& origin) {
+#ifndef NDEBUG
+ // Allow all chrome extensions in Debug builds.
+ return StartsWithASCII(origin, kChromeExtensionUrlSchemePrefix, false);
+#else
+ // For Release builds allow only specific set of clients.
+ //
+ // TODO(sergeyu): Allow whitelisting of origins different from the ones in
+ // kAllowedWebApplicationIds (e.g. specify them in the host config).
+ std::string prefix(kChromeExtensionUrlSchemePrefix);
+ for (size_t i = 0; i < arraysize(kAllowedWebApplicationIds); ++i) {
+ if (origin == prefix + kAllowedWebApplicationIds[i]) {
+ return true;
+ }
+ }
+ return false;
+#endif
+}
+
+void HostStatusService::OnNewConnection(
+ scoped_ptr<WebSocketConnection> connection) {
+ if (connection->request_host() != service_host_name_) {
+ LOG(ERROR) << "Received connection for invalid host: "
+ << connection->request_host()
+ << ". Expected " << service_host_name_;
+ connection->Reject();
+ return;
+ }
+
+ if (connection->request_path() != "/remoting_host_status") {
+ LOG(ERROR) << "Received connection for unknown path: "
+ << connection->request_path();
+ connection->Reject();
+ return;
+ }
+
+ if (!IsAllowedOrigin(connection->origin())) {
+ LOG(ERROR) << "Rejecting connection from unknown origin: "
+ << connection->origin();
+ connection->Reject();
+ return;
+ }
+
+ // Accept connection.
+ connections_.insert(new Connection(this, connection.Pass()));
+}
+
+void HostStatusService::OnConnectionClosed(Connection* connection) {
+ connections_.erase(connection);
+ delete connection;
+}
+
+scoped_ptr<base::DictionaryValue> HostStatusService::GetStatusMessage() {
+ scoped_ptr<base::DictionaryValue> result(new base::DictionaryValue());
+ result->SetString("state", started_ ? "STARTED" : "STOPPED");
+ result->SetString("version", STRINGIZE(VERSION));
+ result->SetString("hostId", host_id_);
+ return result.Pass();
+}
+
+} // namespace remoting
« no previous file with comments | « remoting/host/host_status_service.h ('k') | remoting/host/remoting_me2me_host.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698