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

Side by Side 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 unified diff | Download patch | Annotate | Revision Log
OLDNEW
(Empty)
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "remoting/host/host_status_service.h"
6
7 #include "base/json/json_reader.h"
8 #include "base/json/json_writer.h"
9 #include "base/stl_util.h"
10 #include "base/string_number_conversions.h"
11 #include "base/string_util.h"
12 #include "base/stringize_macros.h"
13 #include "base/values.h"
14 #include "net/base/ip_endpoint.h"
15 #include "net/base/net_util.h"
16 #include "remoting/host/websocket_connection.h"
17 #include "remoting/host/websocket_listener.h"
18
19 namespace remoting {
20
21 namespace {
22
23 // HostStatusService uses the first port available in the following range.
24 const int kPortRangeMin = 12810;
25 const int kPortRangeMax = 12820;
26
27 const char kChromeExtensionUrlSchemePrefix[] = "chrome-extension://";
28
29 #if defined(NDEBUG)
30 const char* const kAllowedWebApplicationIds[] = {
31 "gbchcmhmhahfdphkhkmpfmihenigjmpp", // Chrome Remote Desktop
32 "kgngmbheleoaphbjbaiobfdepmghbfah", // Pre-release Chrome Remote Desktop
33 "odkaodonbgfohohmklejpjiejmcipmib", // Dogfood Chrome Remote Desktop
34 "ojoimpklfciegopdfgeenehpalipignm", // Chromoting canary
35 };
36 #endif
37
38 // All messages we expect should be smaller than 64k.
39 const uint kMaximumIncomingMessageSize = 65536;
40
41 } // namespace
42
43 class HostStatusService::Connection : public WebsocketConnection::Delegate {
44 public:
45 // |service| owns Connection connection objects and must outlive them.
46 Connection(HostStatusService* service,
47 scoped_ptr<WebsocketConnection> websocket);
48 virtual ~Connection();
49
50 // WebsocketConnection::Delegate interface.
51 virtual void OnWebsocketMessage(const std::string& message) OVERRIDE;
52 virtual void OnWebsocketClosed() OVERRIDE;
53
54 private:
55 // Sends message with the specified |method| and |data|.
56 void SendMessage(const std::string& method,
57 scoped_ptr<base::DictionaryValue> data);
58
59 // Closes the connection and destroys this object.
60 void Close();
61
62 HostStatusService* service_;
63 scoped_ptr<WebsocketConnection> websocket_;
64
65 DISALLOW_COPY_AND_ASSIGN(Connection);
66 };
67
68 HostStatusService::Connection::Connection(
69 HostStatusService* service,
70 scoped_ptr<WebsocketConnection> websocket)
71 : service_(service),
72 websocket_(websocket.Pass()) {
73 websocket_->Accept(this);
74 websocket_->set_maximum_message_size(kMaximumIncomingMessageSize);
75 }
76
77 HostStatusService::Connection::~Connection() {
78 }
79
80 void HostStatusService::Connection::OnWebsocketMessage(
81 const std::string& message) {
82 scoped_ptr<base::Value> json(
83 base::JSONReader::Read(message, base::JSON_ALLOW_TRAILING_COMMAS));
84
85 // Verify that we've received a valid JSON dictionary and extract |method| and
86 // |data| fields from it.
87 base::DictionaryValue* message_dict = NULL;
88 std::string method;
89 base::DictionaryValue* data = NULL;
90 if (!json.get() ||
91 !json->GetAsDictionary(&message_dict) ||
92 !message_dict->GetString("method", &method) ||
93 !message_dict->GetDictionary("data", &data)) {
94 LOG(ERROR) << "Received invalid message: " << message;
95 Close();
96 return;
97 }
98
99 if (method == "getHostStatus") {
100 SendMessage("hostStatus", service_->GetStatusMessage());
101 } else {
102 LOG(ERROR) << "Received message with unknown method: " << message;
103 scoped_ptr<base::DictionaryValue> response(new base::DictionaryValue());
104 response->SetString("method", method);
105 SendMessage("unsupportedMethod", response.Pass());
106 return;
107 }
108 }
109
110 void HostStatusService::Connection::OnWebsocketClosed() {
111 Close();
112 }
113
114 void HostStatusService::Connection::SendMessage(
115 const std::string& method,
116 scoped_ptr<base::DictionaryValue> data) {
117 scoped_ptr<base::DictionaryValue> message(new base::DictionaryValue());
118 message->SetString("method", method);
119 message->Set("data", data.release());
120
121 std::string message_json;
122 base::JSONWriter::Write(message.get(), &message_json);
123 websocket_->SendText(message_json);
124 }
125
126 void HostStatusService::Connection::Close() {
127 websocket_.reset();
128 service_->OnConnectionClosed(this);
129 }
130
131 HostStatusService::HostStatusService()
132 : started_(false) {
133 // TODO(sergeyu): Do we need to listen on IPv6 port too?
134 char ip[] = {127, 0, 0, 1};
135 net::IPAddressNumber localhost(ip, ip + sizeof(ip));
136 for (int port = kPortRangeMin; port < kPortRangeMax; ++port) {
137 net::IPEndPoint endpoint(localhost, port);
138 // base::Unretained is safe because we own |websocket_listener_|.
139 if (websocket_listener_.Listen(
140 endpoint,
141 base::Bind(&HostStatusService::OnNewConnection,
142 base::Unretained(this)))) {
143 service_host_name_ = "localhost:" + base::UintToString(port);
144 LOG(INFO) << "Listening for WebSocket connections on localhost:" << port;
145 break;
146 }
147 }
148 }
149
150 HostStatusService::~HostStatusService() {
151 STLDeleteElements(&connections_);
152 }
153
154 void HostStatusService::SetHostIsUp(const std::string& host_id) {
155 started_ = true;
156 host_id_ = host_id;
157 }
158 void HostStatusService::SetHostIsDown() {
159 started_ = false;
160 host_id_.clear();
161 }
162
163 // static
164 bool HostStatusService::IsAllowedOrigin(const std::string& origin) {
165 #ifndef NDEBUG
166 // Allow all chrome extensions in Debug builds.
167 return StartsWithASCII(origin, kChromeExtensionUrlSchemePrefix, false);
168 #else
169 // For Release builds allow only specific set of clients.
170 //
171 // TODO(sergeyu): Allow whitelisting of origins different from the ones in
172 // kAllowedWebApplicationIds (e.g. specify them in the host config).
173 std::string prefix(kChromeExtensionUrlSchemePrefix);
174 for (int i = 0; i < arraysize(kAllowedWebApplicationIds)) {
175 if (origin == prefix + kAllowedWebApplicationIds[i]) {
176 return true;
177 }
178 }
179 return false;
180 #endif
181 }
182
183 void HostStatusService::OnNewConnection(
184 scoped_ptr<WebsocketConnection> connection) {
185 if (connection->request_host() != service_host_name_) {
186 LOG(ERROR) << "Received connection for invalid host: "
187 << connection->request_host()
188 << ". Expected " << service_host_name_;
189 connection->Reject();
190 return;
191 }
192
193 if (connection->request_path() != "/remoting_host_status") {
194 LOG(ERROR) << "Received connection for unknown path: "
195 << connection->request_path();
196 connection->Reject();
197 return;
198 }
199
200 if (!IsAllowedOrigin(connection->origin())) {
201 LOG(ERROR) << "Rejecting connection from unknown origin: "
202 << connection->origin();
203 connection->Reject();
204 return;
205 }
206
207 // Accept connection.
208 connections_.insert(new Connection(this, connection.Pass()));
209 }
210
211 void HostStatusService::OnConnectionClosed(Connection* connection) {
212 connections_.erase(connection);
213 delete connection;
214 }
215
216 scoped_ptr<base::DictionaryValue> HostStatusService::GetStatusMessage() {
217 scoped_ptr<base::DictionaryValue> result(new base::DictionaryValue());
218 result->SetString("state", started_ ? "STARTED" : "STOPPED");
219 result->SetString("version", STRINGIZE(VERSION));
220 result->SetString("hostId", host_id_);
221 return result.Pass();
222 }
223
224 } // namespace remoting
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698