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

Side by Side Diff: chrome/browser/extensions/api/cast_channel/cast_channel_api.cc

Issue 395333003: Extensions: Move cast_channel and hid APIs to extensions/. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: rebase Created 6 years, 4 months 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 2013 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 "chrome/browser/extensions/api/cast_channel/cast_channel_api.h"
6
7 #include <limits>
8 #include <string>
9
10 #include "base/json/json_writer.h"
11 #include "base/memory/scoped_ptr.h"
12 #include "base/strings/string_number_conversions.h"
13 #include "base/values.h"
14 #include "chrome/browser/browser_process.h"
15 #include "chrome/browser/extensions/api/cast_channel/cast_socket.h"
16 #include "chrome/browser/net/chrome_net_log.h"
17 #include "content/public/browser/browser_thread.h"
18 #include "extensions/browser/event_router.h"
19 #include "net/base/ip_endpoint.h"
20 #include "net/base/net_errors.h"
21 #include "net/base/net_util.h"
22 #include "url/gurl.h"
23
24 // Default timeout interval for connection setup.
25 // Used if not otherwise specified at ConnectInfo::timeout.
26 const int kDefaultConnectTimeoutMillis = 5000; // 5 seconds.
27
28 namespace extensions {
29
30 namespace Close = cast_channel::Close;
31 namespace OnError = cast_channel::OnError;
32 namespace OnMessage = cast_channel::OnMessage;
33 namespace Open = cast_channel::Open;
34 namespace Send = cast_channel::Send;
35 using cast_channel::CastSocket;
36 using cast_channel::ChannelAuthType;
37 using cast_channel::ChannelError;
38 using cast_channel::ChannelInfo;
39 using cast_channel::ConnectInfo;
40 using cast_channel::MessageInfo;
41 using cast_channel::ReadyState;
42 using content::BrowserThread;
43
44 namespace {
45
46 // T is an extension dictionary (MessageInfo or ChannelInfo)
47 template <class T>
48 std::string ParamToString(const T& info) {
49 scoped_ptr<base::DictionaryValue> dict = info.ToValue();
50 std::string out;
51 base::JSONWriter::Write(dict.get(), &out);
52 return out;
53 }
54
55 // Fills |channel_info| from the destination and state of |socket|.
56 void FillChannelInfo(const CastSocket& socket, ChannelInfo* channel_info) {
57 DCHECK(channel_info);
58 channel_info->channel_id = socket.id();
59 channel_info->url = socket.CastUrl();
60 const net::IPEndPoint& ip_endpoint = socket.ip_endpoint();
61 channel_info->connect_info.ip_address = ip_endpoint.ToStringWithoutPort();
62 channel_info->connect_info.port = ip_endpoint.port();
63 channel_info->connect_info.auth = socket.channel_auth();
64 channel_info->ready_state = socket.ready_state();
65 channel_info->error_state = socket.error_state();
66 }
67
68 bool IsValidConnectInfoPort(const ConnectInfo& connect_info) {
69 return connect_info.port > 0 && connect_info.port <
70 std::numeric_limits<unsigned short>::max();
71 }
72
73 bool IsValidConnectInfoAuth(const ConnectInfo& connect_info) {
74 return connect_info.auth == cast_channel::CHANNEL_AUTH_TYPE_SSL_VERIFIED ||
75 connect_info.auth == cast_channel::CHANNEL_AUTH_TYPE_SSL;
76 }
77
78 bool IsValidConnectInfoIpAddress(const ConnectInfo& connect_info) {
79 net::IPAddressNumber ip_address;
80 return net::ParseIPLiteralToNumber(connect_info.ip_address, &ip_address);
81 }
82
83 } // namespace
84
85 CastChannelAPI::CastChannelAPI(content::BrowserContext* context)
86 : browser_context_(context) {
87 DCHECK(browser_context_);
88 }
89
90 // static
91 CastChannelAPI* CastChannelAPI::Get(content::BrowserContext* context) {
92 return BrowserContextKeyedAPIFactory<CastChannelAPI>::Get(context);
93 }
94
95 static base::LazyInstance<BrowserContextKeyedAPIFactory<CastChannelAPI> >
96 g_factory = LAZY_INSTANCE_INITIALIZER;
97
98 // static
99 BrowserContextKeyedAPIFactory<CastChannelAPI>*
100 CastChannelAPI::GetFactoryInstance() {
101 return g_factory.Pointer();
102 }
103
104 scoped_ptr<CastSocket> CastChannelAPI::CreateCastSocket(
105 const std::string& extension_id, const net::IPEndPoint& ip_endpoint,
106 ChannelAuthType channel_auth, const base::TimeDelta& timeout) {
107 if (socket_for_test_.get()) {
108 return socket_for_test_.Pass();
109 } else {
110 return scoped_ptr<CastSocket>(
111 new CastSocket(extension_id, ip_endpoint, channel_auth, this,
112 g_browser_process->net_log(),
113 timeout));
114 }
115 }
116
117 void CastChannelAPI::SetSocketForTest(scoped_ptr<CastSocket> socket_for_test) {
118 socket_for_test_ = socket_for_test.Pass();
119 }
120
121 void CastChannelAPI::OnError(const CastSocket* socket,
122 cast_channel::ChannelError error) {
123 DCHECK_CURRENTLY_ON(BrowserThread::IO);
124 ChannelInfo channel_info;
125 FillChannelInfo(*socket, &channel_info);
126 channel_info.error_state = error;
127 scoped_ptr<base::ListValue> results = OnError::Create(channel_info);
128 scoped_ptr<Event> event(new Event(OnError::kEventName, results.Pass()));
129 extensions::EventRouter::Get(browser_context_)
130 ->DispatchEventToExtension(socket->owner_extension_id(), event.Pass());
131 }
132
133 void CastChannelAPI::OnMessage(const CastSocket* socket,
134 const MessageInfo& message_info) {
135 DCHECK_CURRENTLY_ON(BrowserThread::IO);
136 ChannelInfo channel_info;
137 FillChannelInfo(*socket, &channel_info);
138 scoped_ptr<base::ListValue> results =
139 OnMessage::Create(channel_info, message_info);
140 VLOG(1) << "Sending message " << ParamToString(message_info)
141 << " to channel " << ParamToString(channel_info);
142 scoped_ptr<Event> event(new Event(OnMessage::kEventName, results.Pass()));
143 extensions::EventRouter::Get(browser_context_)
144 ->DispatchEventToExtension(socket->owner_extension_id(), event.Pass());
145 }
146
147 CastChannelAPI::~CastChannelAPI() {}
148
149 CastChannelAsyncApiFunction::CastChannelAsyncApiFunction()
150 : manager_(NULL), error_(cast_channel::CHANNEL_ERROR_NONE) { }
151
152 CastChannelAsyncApiFunction::~CastChannelAsyncApiFunction() { }
153
154 bool CastChannelAsyncApiFunction::PrePrepare() {
155 manager_ = ApiResourceManager<CastSocket>::Get(browser_context());
156 return true;
157 }
158
159 bool CastChannelAsyncApiFunction::Respond() {
160 return error_ != cast_channel::CHANNEL_ERROR_NONE;
161 }
162
163 CastSocket* CastChannelAsyncApiFunction::GetSocketOrCompleteWithError(
164 int channel_id) {
165 CastSocket* socket = GetSocket(channel_id);
166 if (!socket) {
167 SetResultFromError(cast_channel::CHANNEL_ERROR_INVALID_CHANNEL_ID);
168 AsyncWorkCompleted();
169 }
170 return socket;
171 }
172
173 int CastChannelAsyncApiFunction::AddSocket(CastSocket* socket) {
174 DCHECK_CURRENTLY_ON(BrowserThread::IO);
175 DCHECK(socket);
176 DCHECK(manager_);
177 const int id = manager_->Add(socket);
178 socket->set_id(id);
179 return id;
180 }
181
182 void CastChannelAsyncApiFunction::RemoveSocket(int channel_id) {
183 DCHECK_CURRENTLY_ON(BrowserThread::IO);
184 DCHECK(manager_);
185 manager_->Remove(extension_->id(), channel_id);
186 }
187
188 void CastChannelAsyncApiFunction::SetResultFromSocket(int channel_id) {
189 CastSocket* socket = GetSocket(channel_id);
190 DCHECK(socket);
191 ChannelInfo channel_info;
192 FillChannelInfo(*socket, &channel_info);
193 error_ = socket->error_state();
194 SetResultFromChannelInfo(channel_info);
195 }
196
197 void CastChannelAsyncApiFunction::SetResultFromError(ChannelError error) {
198 ChannelInfo channel_info;
199 channel_info.channel_id = -1;
200 channel_info.url = "";
201 channel_info.ready_state = cast_channel::READY_STATE_CLOSED;
202 channel_info.error_state = error;
203 SetResultFromChannelInfo(channel_info);
204 error_ = error;
205 }
206
207 CastSocket* CastChannelAsyncApiFunction::GetSocket(int channel_id) {
208 DCHECK_CURRENTLY_ON(BrowserThread::IO);
209 DCHECK(manager_);
210 return manager_->Get(extension_->id(), channel_id);
211 }
212
213 void CastChannelAsyncApiFunction::SetResultFromChannelInfo(
214 const ChannelInfo& channel_info) {
215 DCHECK_CURRENTLY_ON(BrowserThread::IO);
216 SetResult(channel_info.ToValue().release());
217 }
218
219 CastChannelOpenFunction::CastChannelOpenFunction()
220 : new_channel_id_(0) { }
221
222 CastChannelOpenFunction::~CastChannelOpenFunction() { }
223
224 // TODO(mfoltz): Remove URL parsing when clients have converted to use
225 // ConnectInfo.
226
227 // Allowed schemes for Cast device URLs.
228 const char kCastInsecureScheme[] = "cast";
229 const char kCastSecureScheme[] = "casts";
230
231 bool CastChannelOpenFunction::ParseChannelUrl(const GURL& url,
232 ConnectInfo* connect_info) {
233 DCHECK(connect_info);
234 VLOG(2) << "ParseChannelUrl";
235 bool auth_required = false;
236 if (url.SchemeIs(kCastSecureScheme)) {
237 auth_required = true;
238 } else if (!url.SchemeIs(kCastInsecureScheme)) {
239 return false;
240 }
241 // TODO(mfoltz): Test for IPv6 addresses. Brackets or no brackets?
242 // TODO(mfoltz): Maybe enforce restriction to IPv4 private and IPv6
243 // link-local networks
244 const std::string& path = url.path();
245 // Shortest possible: //A:B
246 if (path.size() < 5) {
247 return false;
248 }
249 if (path.find("//") != 0) {
250 return false;
251 }
252 size_t colon = path.find_last_of(':');
253 if (colon == std::string::npos || colon < 3 || colon > path.size() - 2) {
254 return false;
255 }
256 const std::string& ip_address_str = path.substr(2, colon - 2);
257 const std::string& port_str = path.substr(colon + 1);
258 VLOG(2) << "IP: " << ip_address_str << " Port: " << port_str;
259 int port;
260 if (!base::StringToInt(port_str, &port))
261 return false;
262 connect_info->ip_address = ip_address_str;
263 connect_info->port = port;
264 connect_info->auth = auth_required ?
265 cast_channel::CHANNEL_AUTH_TYPE_SSL_VERIFIED :
266 cast_channel::CHANNEL_AUTH_TYPE_SSL;
267 return true;
268 }
269
270 net::IPEndPoint* CastChannelOpenFunction::ParseConnectInfo(
271 const ConnectInfo& connect_info) {
272 net::IPAddressNumber ip_address;
273 CHECK(net::ParseIPLiteralToNumber(connect_info.ip_address, &ip_address));
274 return new net::IPEndPoint(ip_address, connect_info.port);
275 }
276
277 bool CastChannelOpenFunction::PrePrepare() {
278 api_ = CastChannelAPI::Get(browser_context());
279 return CastChannelAsyncApiFunction::PrePrepare();
280 }
281
282 bool CastChannelOpenFunction::Prepare() {
283 params_ = Open::Params::Create(*args_);
284 EXTENSION_FUNCTION_VALIDATE(params_.get());
285 // The connect_info parameter may be a string URL like cast:// or casts:// or
286 // a ConnectInfo object.
287 std::string cast_url;
288 switch (params_->connect_info->GetType()) {
289 case base::Value::TYPE_STRING:
290 CHECK(params_->connect_info->GetAsString(&cast_url));
291 connect_info_.reset(new ConnectInfo);
292 if (!ParseChannelUrl(GURL(cast_url), connect_info_.get())) {
293 connect_info_.reset();
294 SetError("Invalid connect_info (invalid Cast URL " + cast_url + ")");
295 }
296 break;
297 case base::Value::TYPE_DICTIONARY:
298 connect_info_ = ConnectInfo::FromValue(*(params_->connect_info));
299 if (!connect_info_.get()) {
300 SetError("connect_info.auth is required");
301 }
302 break;
303 default:
304 SetError("Invalid connect_info (unknown type)");
305 break;
306 }
307 if (!connect_info_.get()) {
308 return false;
309 }
310 if (!IsValidConnectInfoPort(*connect_info_)) {
311 SetError("Invalid connect_info (invalid port)");
312 } else if (!IsValidConnectInfoAuth(*connect_info_)) {
313 SetError("Invalid connect_info (invalid auth)");
314 } else if (!IsValidConnectInfoIpAddress(*connect_info_)) {
315 SetError("Invalid connect_info (invalid IP address)");
316 }
317 if (!GetError().empty()) {
318 return false;
319 }
320 channel_auth_ = connect_info_->auth;
321 ip_endpoint_.reset(ParseConnectInfo(*connect_info_));
322 return true;
323 }
324
325 void CastChannelOpenFunction::AsyncWorkStart() {
326 DCHECK(api_);
327 DCHECK(ip_endpoint_.get());
328 scoped_ptr<CastSocket> socket = api_->CreateCastSocket(
329 extension_->id(),
330 *ip_endpoint_,
331 channel_auth_,
332 base::TimeDelta::FromMilliseconds(connect_info_->timeout.get()
333 ? *connect_info_->timeout
334 : kDefaultConnectTimeoutMillis));
335 new_channel_id_ = AddSocket(socket.release());
336 GetSocket(new_channel_id_)->Connect(
337 base::Bind(&CastChannelOpenFunction::OnOpen, this));
338 }
339
340 void CastChannelOpenFunction::OnOpen(int result) {
341 DCHECK_CURRENTLY_ON(BrowserThread::IO);
342 VLOG(1) << "Connect finished, OnOpen invoked.";
343 SetResultFromSocket(new_channel_id_);
344 AsyncWorkCompleted();
345 }
346
347 CastChannelSendFunction::CastChannelSendFunction() { }
348
349 CastChannelSendFunction::~CastChannelSendFunction() { }
350
351 bool CastChannelSendFunction::Prepare() {
352 params_ = Send::Params::Create(*args_);
353 EXTENSION_FUNCTION_VALIDATE(params_.get());
354 if (params_->message.namespace_.empty()) {
355 SetError("message_info.namespace_ is required");
356 return false;
357 }
358 if (params_->message.source_id.empty()) {
359 SetError("message_info.source_id is required");
360 return false;
361 }
362 if (params_->message.destination_id.empty()) {
363 SetError("message_info.destination_id is required");
364 return false;
365 }
366 switch (params_->message.data->GetType()) {
367 case base::Value::TYPE_STRING:
368 case base::Value::TYPE_BINARY:
369 break;
370 default:
371 SetError("Invalid type of message_info.data");
372 return false;
373 }
374 return true;
375 }
376
377 void CastChannelSendFunction::AsyncWorkStart() {
378 CastSocket* socket = GetSocketOrCompleteWithError(
379 params_->channel.channel_id);
380 if (socket)
381 socket->SendMessage(params_->message,
382 base::Bind(&CastChannelSendFunction::OnSend, this));
383 }
384
385 void CastChannelSendFunction::OnSend(int result) {
386 DCHECK_CURRENTLY_ON(BrowserThread::IO);
387 if (result < 0) {
388 SetResultFromError(cast_channel::CHANNEL_ERROR_SOCKET_ERROR);
389 } else {
390 SetResultFromSocket(params_->channel.channel_id);
391 }
392 AsyncWorkCompleted();
393 }
394
395 CastChannelCloseFunction::CastChannelCloseFunction() { }
396
397 CastChannelCloseFunction::~CastChannelCloseFunction() { }
398
399 bool CastChannelCloseFunction::Prepare() {
400 params_ = Close::Params::Create(*args_);
401 EXTENSION_FUNCTION_VALIDATE(params_.get());
402 return true;
403 }
404
405 void CastChannelCloseFunction::AsyncWorkStart() {
406 CastSocket* socket = GetSocketOrCompleteWithError(
407 params_->channel.channel_id);
408 if (socket)
409 socket->Close(base::Bind(&CastChannelCloseFunction::OnClose, this));
410 }
411
412 void CastChannelCloseFunction::OnClose(int result) {
413 DCHECK_CURRENTLY_ON(BrowserThread::IO);
414 VLOG(1) << "CastChannelCloseFunction::OnClose result = " << result;
415 if (result < 0) {
416 SetResultFromError(cast_channel::CHANNEL_ERROR_SOCKET_ERROR);
417 } else {
418 int channel_id = params_->channel.channel_id;
419 SetResultFromSocket(channel_id);
420 RemoveSocket(channel_id);
421 }
422 AsyncWorkCompleted();
423 }
424
425 } // namespace extensions
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698