Explicitly add python-numpy dependency to install-build-deps.
[chromium-blink-merge.git] / extensions / browser / api / cast_channel / cast_channel_api.cc
blob08e05d42746ccba4fd355d754855ad717e5c3471
1 // Copyright 2014 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.
5 #include "extensions/browser/api/cast_channel/cast_channel_api.h"
7 #include <limits>
8 #include <string>
10 #include "base/json/json_writer.h"
11 #include "base/lazy_instance.h"
12 #include "base/memory/scoped_ptr.h"
13 #include "base/strings/string_number_conversions.h"
14 #include "base/time/default_tick_clock.h"
15 #include "base/values.h"
16 #include "content/public/browser/browser_thread.h"
17 #include "extensions/browser/api/cast_channel/cast_auth_ica.h"
18 #include "extensions/browser/api/cast_channel/cast_message_util.h"
19 #include "extensions/browser/api/cast_channel/cast_socket.h"
20 #include "extensions/browser/api/cast_channel/logger.h"
21 #include "extensions/browser/event_router.h"
22 #include "extensions/common/api/cast_channel/cast_channel.pb.h"
23 #include "extensions/common/api/cast_channel/logging.pb.h"
24 #include "net/base/ip_endpoint.h"
25 #include "net/base/net_errors.h"
26 #include "net/base/net_util.h"
27 #include "url/gurl.h"
29 // Default timeout interval for connection setup.
30 // Used if not otherwise specified at ConnectInfo::timeout.
31 const int kDefaultConnectTimeoutMillis = 5000; // 5 seconds.
33 namespace extensions {
35 namespace Close = cast_channel::Close;
36 namespace OnError = cast_channel::OnError;
37 namespace OnMessage = cast_channel::OnMessage;
38 namespace Open = cast_channel::Open;
39 namespace Send = cast_channel::Send;
40 using cast_channel::CastMessage;
41 using cast_channel::CastSocket;
42 using cast_channel::ChannelAuthType;
43 using cast_channel::ChannelError;
44 using cast_channel::ChannelInfo;
45 using cast_channel::ConnectInfo;
46 using cast_channel::ErrorInfo;
47 using cast_channel::LastErrors;
48 using cast_channel::Logger;
49 using cast_channel::MessageInfo;
50 using cast_channel::ReadyState;
51 using content::BrowserThread;
53 namespace {
55 // T is an extension dictionary (MessageInfo or ChannelInfo)
56 template <class T>
57 std::string ParamToString(const T& info) {
58 scoped_ptr<base::DictionaryValue> dict = info.ToValue();
59 std::string out;
60 base::JSONWriter::Write(dict.get(), &out);
61 return out;
64 // Fills |channel_info| from the destination and state of |socket|.
65 void FillChannelInfo(const CastSocket& socket, ChannelInfo* channel_info) {
66 DCHECK(channel_info);
67 channel_info->channel_id = socket.id();
68 channel_info->url = socket.cast_url();
69 const net::IPEndPoint& ip_endpoint = socket.ip_endpoint();
70 channel_info->connect_info.ip_address = ip_endpoint.ToStringWithoutPort();
71 channel_info->connect_info.port = ip_endpoint.port();
72 channel_info->connect_info.auth = socket.channel_auth();
73 channel_info->ready_state = socket.ready_state();
74 channel_info->error_state = socket.error_state();
77 // Fills |error_info| from |error_state| and |last_errors|.
78 void FillErrorInfo(ChannelError error_state,
79 const LastErrors& last_errors,
80 ErrorInfo* error_info) {
81 error_info->error_state = error_state;
82 if (last_errors.event_type != cast_channel::proto::EVENT_TYPE_UNKNOWN)
83 error_info->event_type.reset(new int(last_errors.event_type));
84 if (last_errors.challenge_reply_error_type !=
85 cast_channel::proto::CHALLENGE_REPLY_ERROR_NONE) {
86 error_info->challenge_reply_error_type.reset(
87 new int(last_errors.challenge_reply_error_type));
89 if (last_errors.net_return_value <= 0)
90 error_info->net_return_value.reset(new int(last_errors.net_return_value));
91 if (last_errors.nss_error_code < 0)
92 error_info->nss_error_code.reset(new int(last_errors.nss_error_code));
95 bool IsValidConnectInfoPort(const ConnectInfo& connect_info) {
96 return connect_info.port > 0 && connect_info.port <
97 std::numeric_limits<uint16_t>::max();
100 bool IsValidConnectInfoAuth(const ConnectInfo& connect_info) {
101 return connect_info.auth == cast_channel::CHANNEL_AUTH_TYPE_SSL_VERIFIED ||
102 connect_info.auth == cast_channel::CHANNEL_AUTH_TYPE_SSL;
105 bool IsValidConnectInfoIpAddress(const ConnectInfo& connect_info) {
106 net::IPAddressNumber ip_address;
107 return net::ParseIPLiteralToNumber(connect_info.ip_address, &ip_address);
110 } // namespace
112 CastChannelAPI::CastChannelAPI(content::BrowserContext* context)
113 : browser_context_(context),
114 logger_(
115 new Logger(scoped_ptr<base::TickClock>(new base::DefaultTickClock),
116 base::TimeTicks::UnixEpoch())) {
117 DCHECK(browser_context_);
120 // static
121 CastChannelAPI* CastChannelAPI::Get(content::BrowserContext* context) {
122 return BrowserContextKeyedAPIFactory<CastChannelAPI>::Get(context);
125 scoped_refptr<Logger> CastChannelAPI::GetLogger() {
126 return logger_;
129 static base::LazyInstance<BrowserContextKeyedAPIFactory<CastChannelAPI> >
130 g_factory = LAZY_INSTANCE_INITIALIZER;
132 // static
133 BrowserContextKeyedAPIFactory<CastChannelAPI>*
134 CastChannelAPI::GetFactoryInstance() {
135 return g_factory.Pointer();
138 void CastChannelAPI::SetSocketForTest(scoped_ptr<CastSocket> socket_for_test) {
139 socket_for_test_ = socket_for_test.Pass();
142 scoped_ptr<CastSocket> CastChannelAPI::GetSocketForTest() {
143 return socket_for_test_.Pass();
146 content::BrowserContext* CastChannelAPI::GetBrowserContext() const {
147 return browser_context_;
150 CastChannelAPI::~CastChannelAPI() {}
152 CastChannelAsyncApiFunction::CastChannelAsyncApiFunction() : manager_(NULL) {
155 CastChannelAsyncApiFunction::~CastChannelAsyncApiFunction() { }
157 bool CastChannelAsyncApiFunction::PrePrepare() {
158 manager_ = ApiResourceManager<CastSocket>::Get(browser_context());
159 return true;
162 bool CastChannelAsyncApiFunction::Respond() {
163 return GetError().empty();
166 CastSocket* CastChannelAsyncApiFunction::GetSocketOrCompleteWithError(
167 int channel_id) {
168 CastSocket* socket = GetSocket(channel_id);
169 if (!socket) {
170 SetResultFromError(channel_id,
171 cast_channel::CHANNEL_ERROR_INVALID_CHANNEL_ID);
172 AsyncWorkCompleted();
174 return socket;
177 int CastChannelAsyncApiFunction::AddSocket(CastSocket* socket) {
178 DCHECK_CURRENTLY_ON(BrowserThread::IO);
179 DCHECK(socket);
180 DCHECK(manager_);
181 const int id = manager_->Add(socket);
182 socket->set_id(id);
183 return id;
186 void CastChannelAsyncApiFunction::RemoveSocket(int channel_id) {
187 DCHECK_CURRENTLY_ON(BrowserThread::IO);
188 DCHECK(manager_);
189 manager_->Remove(extension_->id(), channel_id);
192 void CastChannelAsyncApiFunction::SetResultFromSocket(
193 const CastSocket& socket) {
194 ChannelInfo channel_info;
195 FillChannelInfo(socket, &channel_info);
196 ChannelError error = socket.error_state();
197 if (error != cast_channel::CHANNEL_ERROR_NONE) {
198 SetError("Channel socket error = " + base::IntToString(error));
200 SetResultFromChannelInfo(channel_info);
203 void CastChannelAsyncApiFunction::SetResultFromError(int channel_id,
204 ChannelError error) {
205 ChannelInfo channel_info;
206 channel_info.channel_id = channel_id;
207 channel_info.url = "";
208 channel_info.ready_state = cast_channel::READY_STATE_CLOSED;
209 channel_info.error_state = error;
210 channel_info.connect_info.ip_address = "";
211 channel_info.connect_info.port = 0;
212 channel_info.connect_info.auth = cast_channel::CHANNEL_AUTH_TYPE_SSL;
213 SetResultFromChannelInfo(channel_info);
214 SetError("Channel error = " + base::IntToString(error));
217 CastSocket* CastChannelAsyncApiFunction::GetSocket(int channel_id) const {
218 DCHECK_CURRENTLY_ON(BrowserThread::IO);
219 DCHECK(manager_);
220 return manager_->Get(extension_->id(), channel_id);
223 void CastChannelAsyncApiFunction::SetResultFromChannelInfo(
224 const ChannelInfo& channel_info) {
225 DCHECK_CURRENTLY_ON(BrowserThread::IO);
226 SetResult(channel_info.ToValue().release());
229 CastChannelOpenFunction::CastChannelOpenFunction()
230 : new_channel_id_(0) { }
232 CastChannelOpenFunction::~CastChannelOpenFunction() { }
234 // TODO(mfoltz): Remove URL parsing when clients have converted to use
235 // ConnectInfo.
237 // Allowed schemes for Cast device URLs.
238 const char kCastInsecureScheme[] = "cast";
239 const char kCastSecureScheme[] = "casts";
241 bool CastChannelOpenFunction::ParseChannelUrl(const GURL& url,
242 ConnectInfo* connect_info) {
243 DCHECK(connect_info);
244 VLOG(2) << "ParseChannelUrl";
245 bool auth_required = false;
246 if (url.SchemeIs(kCastSecureScheme)) {
247 auth_required = true;
248 } else if (!url.SchemeIs(kCastInsecureScheme)) {
249 return false;
251 // TODO(mfoltz): Test for IPv6 addresses. Brackets or no brackets?
252 // TODO(mfoltz): Maybe enforce restriction to IPv4 private and IPv6
253 // link-local networks
254 const std::string& path = url.path();
255 // Shortest possible: //A:B
256 if (path.size() < 5) {
257 return false;
259 if (path.find("//") != 0) {
260 return false;
262 size_t colon = path.find_last_of(':');
263 if (colon == std::string::npos || colon < 3 || colon > path.size() - 2) {
264 return false;
266 const std::string& ip_address_str = path.substr(2, colon - 2);
267 const std::string& port_str = path.substr(colon + 1);
268 VLOG(2) << "IP: " << ip_address_str << " Port: " << port_str;
269 int port;
270 if (!base::StringToInt(port_str, &port))
271 return false;
272 connect_info->ip_address = ip_address_str;
273 connect_info->port = port;
274 connect_info->auth = auth_required ?
275 cast_channel::CHANNEL_AUTH_TYPE_SSL_VERIFIED :
276 cast_channel::CHANNEL_AUTH_TYPE_SSL;
277 return true;
280 net::IPEndPoint* CastChannelOpenFunction::ParseConnectInfo(
281 const ConnectInfo& connect_info) {
282 net::IPAddressNumber ip_address;
283 CHECK(net::ParseIPLiteralToNumber(connect_info.ip_address, &ip_address));
284 return new net::IPEndPoint(ip_address,
285 static_cast<uint16>(connect_info.port));
288 bool CastChannelOpenFunction::PrePrepare() {
289 api_ = CastChannelAPI::Get(browser_context());
290 return CastChannelAsyncApiFunction::PrePrepare();
293 bool CastChannelOpenFunction::Prepare() {
294 params_ = Open::Params::Create(*args_);
295 EXTENSION_FUNCTION_VALIDATE(params_.get());
296 // The connect_info parameter may be a string URL like cast:// or casts:// or
297 // a ConnectInfo object.
298 std::string cast_url;
299 switch (params_->connect_info->GetType()) {
300 case base::Value::TYPE_STRING:
301 CHECK(params_->connect_info->GetAsString(&cast_url));
302 connect_info_.reset(new ConnectInfo);
303 if (!ParseChannelUrl(GURL(cast_url), connect_info_.get())) {
304 connect_info_.reset();
305 SetError("Invalid connect_info (invalid Cast URL " + cast_url + ")");
307 break;
308 case base::Value::TYPE_DICTIONARY:
309 connect_info_ = ConnectInfo::FromValue(*(params_->connect_info));
310 if (!connect_info_.get()) {
311 SetError("connect_info.auth is required");
313 break;
314 default:
315 SetError("Invalid connect_info (unknown type)");
316 break;
318 if (!connect_info_.get()) {
319 return false;
321 if (!IsValidConnectInfoPort(*connect_info_)) {
322 SetError("Invalid connect_info (invalid port)");
323 } else if (!IsValidConnectInfoAuth(*connect_info_)) {
324 SetError("Invalid connect_info (invalid auth)");
325 } else if (!IsValidConnectInfoIpAddress(*connect_info_)) {
326 SetError("Invalid connect_info (invalid IP address)");
328 if (!GetError().empty()) {
329 return false;
331 channel_auth_ = connect_info_->auth;
332 ip_endpoint_.reset(ParseConnectInfo(*connect_info_));
333 return true;
336 void CastChannelOpenFunction::AsyncWorkStart() {
337 DCHECK(api_);
338 DCHECK(ip_endpoint_.get());
339 CastSocket* socket;
340 scoped_ptr<CastSocket> test_socket = api_->GetSocketForTest();
341 if (test_socket.get()) {
342 socket = test_socket.release();
343 } else {
344 socket = new cast_channel::CastSocketImpl(
345 extension_->id(), *ip_endpoint_, channel_auth_,
346 ExtensionsBrowserClient::Get()->GetNetLog(),
347 base::TimeDelta::FromMilliseconds(connect_info_->timeout.get()
348 ? *connect_info_->timeout
349 : kDefaultConnectTimeoutMillis),
350 api_->GetLogger());
352 new_channel_id_ = AddSocket(socket);
353 scoped_ptr<CastMessageHandler> delegate(new CastMessageHandler(api_, socket));
354 api_->GetLogger()->LogNewSocketEvent(*socket);
355 socket->Connect(delegate.Pass(),
356 base::Bind(&CastChannelOpenFunction::OnOpen, this));
359 void CastChannelOpenFunction::OnOpen(cast_channel::ChannelError result) {
360 DCHECK_CURRENTLY_ON(BrowserThread::IO);
361 VLOG(1) << "Connect finished, OnOpen invoked.";
362 CastSocket* socket = GetSocket(new_channel_id_);
363 if (!socket) {
364 SetResultFromError(new_channel_id_, result);
365 } else {
366 SetResultFromSocket(*socket);
368 AsyncWorkCompleted();
371 CastChannelSendFunction::CastChannelSendFunction() { }
373 CastChannelSendFunction::~CastChannelSendFunction() { }
375 bool CastChannelSendFunction::Prepare() {
376 params_ = Send::Params::Create(*args_);
377 EXTENSION_FUNCTION_VALIDATE(params_.get());
378 if (params_->message.namespace_.empty()) {
379 SetError("message_info.namespace_ is required");
380 return false;
382 if (params_->message.source_id.empty()) {
383 SetError("message_info.source_id is required");
384 return false;
386 if (params_->message.destination_id.empty()) {
387 SetError("message_info.destination_id is required");
388 return false;
390 switch (params_->message.data->GetType()) {
391 case base::Value::TYPE_STRING:
392 case base::Value::TYPE_BINARY:
393 break;
394 default:
395 SetError("Invalid type of message_info.data");
396 return false;
398 return true;
401 void CastChannelSendFunction::AsyncWorkStart() {
402 CastSocket* socket = GetSocket(params_->channel.channel_id);
403 if (!socket) {
404 SetResultFromError(params_->channel.channel_id,
405 cast_channel::CHANNEL_ERROR_INVALID_CHANNEL_ID);
406 AsyncWorkCompleted();
407 return;
409 CastMessage message_to_send;
410 if (!MessageInfoToCastMessage(params_->message, &message_to_send)) {
411 SetResultFromError(params_->channel.channel_id,
412 cast_channel::CHANNEL_ERROR_INVALID_MESSAGE);
413 AsyncWorkCompleted();
414 return;
416 socket->transport()->SendMessage(
417 message_to_send, base::Bind(&CastChannelSendFunction::OnSend, this));
420 void CastChannelSendFunction::OnSend(int result) {
421 DCHECK_CURRENTLY_ON(BrowserThread::IO);
422 int channel_id = params_->channel.channel_id;
423 CastSocket* socket = GetSocket(channel_id);
424 if (result < 0 || !socket) {
425 SetResultFromError(channel_id,
426 cast_channel::CHANNEL_ERROR_SOCKET_ERROR);
427 } else {
428 SetResultFromSocket(*socket);
430 AsyncWorkCompleted();
433 CastChannelCloseFunction::CastChannelCloseFunction() { }
435 CastChannelCloseFunction::~CastChannelCloseFunction() { }
437 bool CastChannelCloseFunction::Prepare() {
438 params_ = Close::Params::Create(*args_);
439 EXTENSION_FUNCTION_VALIDATE(params_.get());
440 return true;
443 void CastChannelCloseFunction::AsyncWorkStart() {
444 CastSocket* socket = GetSocket(params_->channel.channel_id);
445 if (!socket) {
446 SetResultFromError(params_->channel.channel_id,
447 cast_channel::CHANNEL_ERROR_INVALID_CHANNEL_ID);
448 AsyncWorkCompleted();
449 } else {
450 socket->Close(base::Bind(&CastChannelCloseFunction::OnClose, this));
454 void CastChannelCloseFunction::OnClose(int result) {
455 DCHECK_CURRENTLY_ON(BrowserThread::IO);
456 VLOG(1) << "CastChannelCloseFunction::OnClose result = " << result;
457 int channel_id = params_->channel.channel_id;
458 CastSocket* socket = GetSocket(channel_id);
459 if (result < 0 || !socket) {
460 SetResultFromError(channel_id,
461 cast_channel::CHANNEL_ERROR_SOCKET_ERROR);
462 } else {
463 SetResultFromSocket(*socket);
464 // This will delete |socket|.
465 RemoveSocket(channel_id);
467 AsyncWorkCompleted();
470 CastChannelGetLogsFunction::CastChannelGetLogsFunction() {
473 CastChannelGetLogsFunction::~CastChannelGetLogsFunction() {
476 bool CastChannelGetLogsFunction::PrePrepare() {
477 api_ = CastChannelAPI::Get(browser_context());
478 return CastChannelAsyncApiFunction::PrePrepare();
481 bool CastChannelGetLogsFunction::Prepare() {
482 return true;
485 void CastChannelGetLogsFunction::AsyncWorkStart() {
486 DCHECK(api_);
488 size_t length = 0;
489 scoped_ptr<char[]> out = api_->GetLogger()->GetLogs(&length);
490 if (out.get()) {
491 SetResult(new base::BinaryValue(out.Pass(), length));
492 } else {
493 SetError("Unable to get logs.");
496 api_->GetLogger()->Reset();
498 AsyncWorkCompleted();
501 CastChannelOpenFunction::CastMessageHandler::CastMessageHandler(
502 CastChannelAPI* api,
503 cast_channel::CastSocket* socket)
504 : api(api), socket(socket) {
505 DCHECK(api);
506 DCHECK(socket);
509 CastChannelOpenFunction::CastMessageHandler::~CastMessageHandler() {
512 void CastChannelOpenFunction::CastMessageHandler::OnError(
513 cast_channel::ChannelError error_state,
514 const cast_channel::LastErrors& last_errors) {
515 DCHECK_CURRENTLY_ON(BrowserThread::IO);
517 ChannelInfo channel_info;
518 FillChannelInfo(*socket, &channel_info);
519 channel_info.error_state = error_state;
520 ErrorInfo error_info;
521 FillErrorInfo(error_state, last_errors, &error_info);
523 scoped_ptr<base::ListValue> results =
524 OnError::Create(channel_info, error_info);
525 scoped_ptr<Event> event(new Event(OnError::kEventName, results.Pass()));
526 extensions::EventRouter::Get(api->GetBrowserContext())
527 ->DispatchEventToExtension(socket->owner_extension_id(), event.Pass());
530 void CastChannelOpenFunction::CastMessageHandler::OnMessage(
531 const CastMessage& message) {
532 DCHECK_CURRENTLY_ON(BrowserThread::IO);
534 MessageInfo message_info;
535 cast_channel::CastMessageToMessageInfo(message, &message_info);
536 ChannelInfo channel_info;
537 FillChannelInfo(*socket, &channel_info);
538 VLOG(1) << "Received message " << ParamToString(message_info)
539 << " on channel " << ParamToString(channel_info);
541 scoped_ptr<base::ListValue> results =
542 OnMessage::Create(channel_info, message_info);
543 scoped_ptr<Event> event(new Event(OnMessage::kEventName, results.Pass()));
544 extensions::EventRouter::Get(api->GetBrowserContext())
545 ->DispatchEventToExtension(socket->owner_extension_id(), event.Pass());
548 CastChannelSetAuthorityKeysFunction::CastChannelSetAuthorityKeysFunction() {
551 CastChannelSetAuthorityKeysFunction::~CastChannelSetAuthorityKeysFunction() {
554 bool CastChannelSetAuthorityKeysFunction::Prepare() {
555 params_ = cast_channel::SetAuthorityKeys::Params::Create(*args_);
556 EXTENSION_FUNCTION_VALIDATE(params_.get());
557 return true;
560 void CastChannelSetAuthorityKeysFunction::AsyncWorkStart() {
561 std::string& keys = params_->keys;
562 std::string& signature = params_->signature;
563 if (signature.empty() || keys.empty() ||
564 !cast_channel::SetTrustedCertificateAuthorities(keys, signature)) {
565 SetError("Unable to set authority keys.");
568 AsyncWorkCompleted();
571 } // namespace extensions