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"
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/keep_alive_delegate.h"
21 #include "extensions/browser/api/cast_channel/logger.h"
22 #include "extensions/browser/event_router.h"
23 #include "extensions/common/api/cast_channel/cast_channel.pb.h"
24 #include "extensions/common/api/cast_channel/logging.pb.h"
25 #include "net/base/ip_endpoint.h"
26 #include "net/base/net_errors.h"
27 #include "net/base/net_util.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::CastDeviceCapability
;
41 using cast_channel::CastMessage
;
42 using cast_channel::CastSocket
;
43 using cast_channel::ChannelAuthType
;
44 using cast_channel::ChannelError
;
45 using cast_channel::ChannelInfo
;
46 using cast_channel::ConnectInfo
;
47 using cast_channel::ErrorInfo
;
48 using cast_channel::LastErrors
;
49 using cast_channel::Logger
;
50 using cast_channel::MessageInfo
;
51 using cast_channel::ReadyState
;
52 using content::BrowserThread
;
56 // T is an extension dictionary (MessageInfo or ChannelInfo)
58 std::string
ParamToString(const T
& info
) {
59 scoped_ptr
<base::DictionaryValue
> dict
= info
.ToValue();
61 base::JSONWriter::Write(*dict
, &out
);
65 // Fills |channel_info| from the destination and state of |socket|.
66 void FillChannelInfo(const CastSocket
& socket
, ChannelInfo
* channel_info
) {
68 channel_info
->channel_id
= socket
.id();
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();
75 channel_info
->keep_alive
= socket
.keep_alive();
78 // Fills |error_info| from |error_state| and |last_errors|.
79 void FillErrorInfo(ChannelError error_state
,
80 const LastErrors
& last_errors
,
81 ErrorInfo
* error_info
) {
82 error_info
->error_state
= error_state
;
83 if (last_errors
.event_type
!= cast_channel::proto::EVENT_TYPE_UNKNOWN
)
84 error_info
->event_type
.reset(new int(last_errors
.event_type
));
85 if (last_errors
.challenge_reply_error_type
!=
86 cast_channel::proto::CHALLENGE_REPLY_ERROR_NONE
) {
87 error_info
->challenge_reply_error_type
.reset(
88 new int(last_errors
.challenge_reply_error_type
));
90 if (last_errors
.net_return_value
<= 0)
91 error_info
->net_return_value
.reset(new int(last_errors
.net_return_value
));
92 if (last_errors
.nss_error_code
< 0)
93 error_info
->nss_error_code
.reset(new int(last_errors
.nss_error_code
));
96 bool IsValidConnectInfoPort(const ConnectInfo
& connect_info
) {
97 return connect_info
.port
> 0 && connect_info
.port
<
98 std::numeric_limits
<uint16_t>::max();
101 bool IsValidConnectInfoIpAddress(const ConnectInfo
& connect_info
) {
102 net::IPAddressNumber ip_address
;
103 return net::ParseIPLiteralToNumber(connect_info
.ip_address
, &ip_address
);
108 CastChannelAPI::CastChannelAPI(content::BrowserContext
* context
)
109 : browser_context_(context
),
111 new Logger(scoped_ptr
<base::TickClock
>(new base::DefaultTickClock
),
112 base::TimeTicks::UnixEpoch())) {
113 DCHECK(browser_context_
);
117 CastChannelAPI
* CastChannelAPI::Get(content::BrowserContext
* context
) {
118 return BrowserContextKeyedAPIFactory
<CastChannelAPI
>::Get(context
);
121 scoped_refptr
<Logger
> CastChannelAPI::GetLogger() {
125 void CastChannelAPI::SendEvent(const std::string
& extension_id
,
126 scoped_ptr
<Event
> event
) {
127 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
128 EventRouter
* event_router
= EventRouter::Get(GetBrowserContext());
130 event_router
->DispatchEventToExtension(extension_id
, event
.Pass());
134 static base::LazyInstance
<BrowserContextKeyedAPIFactory
<CastChannelAPI
> >
135 g_factory
= LAZY_INSTANCE_INITIALIZER
;
138 BrowserContextKeyedAPIFactory
<CastChannelAPI
>*
139 CastChannelAPI::GetFactoryInstance() {
140 return g_factory
.Pointer();
143 void CastChannelAPI::SetSocketForTest(scoped_ptr
<CastSocket
> socket_for_test
) {
144 socket_for_test_
= socket_for_test
.Pass();
147 scoped_ptr
<CastSocket
> CastChannelAPI::GetSocketForTest() {
148 return socket_for_test_
.Pass();
151 content::BrowserContext
* CastChannelAPI::GetBrowserContext() const {
152 return browser_context_
;
155 void CastChannelAPI::SetPingTimeoutTimerForTest(scoped_ptr
<base::Timer
> timer
) {
156 injected_timeout_timer_
= timer
.Pass();
159 scoped_ptr
<base::Timer
> CastChannelAPI::GetInjectedTimeoutTimerForTest() {
160 return injected_timeout_timer_
.Pass();
163 CastChannelAPI::~CastChannelAPI() {}
165 CastChannelAsyncApiFunction::CastChannelAsyncApiFunction() {
168 CastChannelAsyncApiFunction::~CastChannelAsyncApiFunction() { }
170 bool CastChannelAsyncApiFunction::PrePrepare() {
171 DCHECK(ApiResourceManager
<CastSocket
>::Get(browser_context()));
172 sockets_
= ApiResourceManager
<CastSocket
>::Get(browser_context())->data_
;
177 bool CastChannelAsyncApiFunction::Respond() {
178 return GetError().empty();
181 CastSocket
* CastChannelAsyncApiFunction::GetSocketOrCompleteWithError(
183 CastSocket
* socket
= GetSocket(channel_id
);
185 SetResultFromError(channel_id
,
186 cast_channel::CHANNEL_ERROR_INVALID_CHANNEL_ID
);
187 AsyncWorkCompleted();
192 int CastChannelAsyncApiFunction::AddSocket(CastSocket
* socket
) {
193 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
195 const int id
= sockets_
->Add(socket
);
200 void CastChannelAsyncApiFunction::RemoveSocket(int channel_id
) {
201 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
202 sockets_
->Remove(extension_
->id(), channel_id
);
205 void CastChannelAsyncApiFunction::SetResultFromSocket(
206 const CastSocket
& socket
) {
207 ChannelInfo channel_info
;
208 FillChannelInfo(socket
, &channel_info
);
209 ChannelError error
= socket
.error_state();
210 if (error
!= cast_channel::CHANNEL_ERROR_NONE
) {
211 SetError("Channel socket error = " + base::IntToString(error
));
213 SetResultFromChannelInfo(channel_info
);
216 void CastChannelAsyncApiFunction::SetResultFromError(int channel_id
,
217 ChannelError error
) {
218 ChannelInfo channel_info
;
219 channel_info
.channel_id
= channel_id
;
220 channel_info
.ready_state
= cast_channel::READY_STATE_CLOSED
;
221 channel_info
.error_state
= error
;
222 channel_info
.connect_info
.ip_address
= "";
223 channel_info
.connect_info
.port
= 0;
224 channel_info
.connect_info
.auth
= cast_channel::CHANNEL_AUTH_TYPE_SSL
;
225 SetResultFromChannelInfo(channel_info
);
226 SetError("Channel error = " + base::IntToString(error
));
229 CastSocket
* CastChannelAsyncApiFunction::GetSocket(int channel_id
) const {
230 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
231 return sockets_
->Get(extension_
->id(), channel_id
);
234 void CastChannelAsyncApiFunction::SetResultFromChannelInfo(
235 const ChannelInfo
& channel_info
) {
236 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
237 SetResult(channel_info
.ToValue().release());
240 CastChannelOpenFunction::CastChannelOpenFunction()
241 : new_channel_id_(0) {
244 CastChannelOpenFunction::~CastChannelOpenFunction() { }
246 net::IPEndPoint
* CastChannelOpenFunction::ParseConnectInfo(
247 const ConnectInfo
& connect_info
) {
248 net::IPAddressNumber ip_address
;
249 CHECK(net::ParseIPLiteralToNumber(connect_info
.ip_address
, &ip_address
));
250 return new net::IPEndPoint(ip_address
,
251 static_cast<uint16
>(connect_info
.port
));
254 bool CastChannelOpenFunction::PrePrepare() {
255 api_
= CastChannelAPI::Get(browser_context());
256 return CastChannelAsyncApiFunction::PrePrepare();
259 bool CastChannelOpenFunction::Prepare() {
260 params_
= Open::Params::Create(*args_
);
261 EXTENSION_FUNCTION_VALIDATE(params_
.get());
262 const ConnectInfo
& connect_info
= params_
->connect_info
;
263 if (!IsValidConnectInfoPort(connect_info
)) {
264 SetError("Invalid connect_info (invalid port)");
265 } else if (!IsValidConnectInfoIpAddress(connect_info
)) {
266 SetError("Invalid connect_info (invalid IP address)");
268 // Parse timeout parameters if they are set.
269 if (connect_info
.liveness_timeout
.get()) {
270 liveness_timeout_
= base::TimeDelta::FromMilliseconds(
271 *connect_info
.liveness_timeout
.get());
273 if (connect_info
.ping_interval
.get()) {
275 base::TimeDelta::FromMilliseconds(*connect_info
.ping_interval
.get());
278 // Validate timeout parameters.
279 if (liveness_timeout_
< base::TimeDelta() ||
280 ping_interval_
< base::TimeDelta()) {
281 SetError("livenessTimeout and pingInterval must be greater than 0.");
282 } else if ((liveness_timeout_
> base::TimeDelta()) !=
283 (ping_interval_
> base::TimeDelta())) {
284 SetError("livenessTimeout and pingInterval must be set together.");
285 } else if (liveness_timeout_
< ping_interval_
) {
286 SetError("livenessTimeout must be longer than pingTimeout.");
290 if (!GetError().empty()) {
294 channel_auth_
= connect_info
.auth
;
295 ip_endpoint_
.reset(ParseConnectInfo(connect_info
));
299 void CastChannelOpenFunction::AsyncWorkStart() {
301 DCHECK(ip_endpoint_
.get());
302 const ConnectInfo
& connect_info
= params_
->connect_info
;
304 scoped_ptr
<CastSocket
> test_socket
= api_
->GetSocketForTest();
305 if (test_socket
.get()) {
306 socket
= test_socket
.release();
308 socket
= new cast_channel::CastSocketImpl(
309 extension_
->id(), *ip_endpoint_
, channel_auth_
,
310 ExtensionsBrowserClient::Get()->GetNetLog(),
311 base::TimeDelta::FromMilliseconds(connect_info
.timeout
.get()
312 ? *connect_info
.timeout
.get()
313 : kDefaultConnectTimeoutMillis
),
314 liveness_timeout_
> base::TimeDelta(), api_
->GetLogger(),
315 connect_info
.capabilities
.get() ? *connect_info
.capabilities
.get()
316 : CastDeviceCapability::NONE
);
318 new_channel_id_
= AddSocket(socket
);
319 api_
->GetLogger()->LogNewSocketEvent(*socket
);
321 // Construct read delegates.
322 scoped_ptr
<api::cast_channel::CastTransport::Delegate
> delegate(
323 make_scoped_ptr(new CastMessageHandler(
324 base::Bind(&CastChannelAPI::SendEvent
, api_
->AsWeakPtr()), socket
,
325 api_
->GetLogger())));
326 if (socket
->keep_alive()) {
327 // Wrap read delegate in a KeepAliveDelegate for timeout handling.
328 api::cast_channel::KeepAliveDelegate
* keep_alive
=
329 new api::cast_channel::KeepAliveDelegate(
330 socket
, api_
->GetLogger(), delegate
.Pass(), ping_interval_
,
332 scoped_ptr
<base::Timer
> injected_timer
=
333 api_
->GetInjectedTimeoutTimerForTest();
334 if (injected_timer
) {
335 keep_alive
->SetTimersForTest(
336 make_scoped_ptr(new base::Timer(false, false)),
337 injected_timer
.Pass());
339 delegate
.reset(keep_alive
);
342 api_
->GetLogger()->LogNewSocketEvent(*socket
);
343 socket
->Connect(delegate
.Pass(),
344 base::Bind(&CastChannelOpenFunction::OnOpen
, this));
347 void CastChannelOpenFunction::OnOpen(cast_channel::ChannelError result
) {
348 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
349 VLOG(1) << "Connect finished, OnOpen invoked.";
350 CastSocket
* socket
= GetSocket(new_channel_id_
);
352 SetResultFromError(new_channel_id_
, result
);
354 SetResultFromSocket(*socket
);
356 AsyncWorkCompleted();
359 CastChannelSendFunction::CastChannelSendFunction() { }
361 CastChannelSendFunction::~CastChannelSendFunction() { }
363 bool CastChannelSendFunction::Prepare() {
364 params_
= Send::Params::Create(*args_
);
365 EXTENSION_FUNCTION_VALIDATE(params_
.get());
366 if (params_
->message
.namespace_
.empty()) {
367 SetError("message_info.namespace_ is required");
370 if (params_
->message
.source_id
.empty()) {
371 SetError("message_info.source_id is required");
374 if (params_
->message
.destination_id
.empty()) {
375 SetError("message_info.destination_id is required");
378 switch (params_
->message
.data
->GetType()) {
379 case base::Value::TYPE_STRING
:
380 case base::Value::TYPE_BINARY
:
383 SetError("Invalid type of message_info.data");
389 void CastChannelSendFunction::AsyncWorkStart() {
390 CastSocket
* socket
= GetSocket(params_
->channel
.channel_id
);
392 SetResultFromError(params_
->channel
.channel_id
,
393 cast_channel::CHANNEL_ERROR_INVALID_CHANNEL_ID
);
394 AsyncWorkCompleted();
397 CastMessage message_to_send
;
398 if (!MessageInfoToCastMessage(params_
->message
, &message_to_send
)) {
399 SetResultFromError(params_
->channel
.channel_id
,
400 cast_channel::CHANNEL_ERROR_INVALID_MESSAGE
);
401 AsyncWorkCompleted();
404 socket
->transport()->SendMessage(
405 message_to_send
, base::Bind(&CastChannelSendFunction::OnSend
, this));
408 void CastChannelSendFunction::OnSend(int result
) {
409 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
410 int channel_id
= params_
->channel
.channel_id
;
411 CastSocket
* socket
= GetSocket(channel_id
);
412 if (result
< 0 || !socket
) {
413 SetResultFromError(channel_id
,
414 cast_channel::CHANNEL_ERROR_SOCKET_ERROR
);
416 SetResultFromSocket(*socket
);
418 AsyncWorkCompleted();
421 CastChannelCloseFunction::CastChannelCloseFunction() { }
423 CastChannelCloseFunction::~CastChannelCloseFunction() { }
425 bool CastChannelCloseFunction::Prepare() {
426 params_
= Close::Params::Create(*args_
);
427 EXTENSION_FUNCTION_VALIDATE(params_
.get());
431 void CastChannelCloseFunction::AsyncWorkStart() {
432 CastSocket
* socket
= GetSocket(params_
->channel
.channel_id
);
434 SetResultFromError(params_
->channel
.channel_id
,
435 cast_channel::CHANNEL_ERROR_INVALID_CHANNEL_ID
);
436 AsyncWorkCompleted();
438 socket
->Close(base::Bind(&CastChannelCloseFunction::OnClose
, this));
442 void CastChannelCloseFunction::OnClose(int result
) {
443 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
444 VLOG(1) << "CastChannelCloseFunction::OnClose result = " << result
;
445 int channel_id
= params_
->channel
.channel_id
;
446 CastSocket
* socket
= GetSocket(channel_id
);
447 if (result
< 0 || !socket
) {
448 SetResultFromError(channel_id
,
449 cast_channel::CHANNEL_ERROR_SOCKET_ERROR
);
451 SetResultFromSocket(*socket
);
452 // This will delete |socket|.
453 RemoveSocket(channel_id
);
455 AsyncWorkCompleted();
458 CastChannelGetLogsFunction::CastChannelGetLogsFunction() {
461 CastChannelGetLogsFunction::~CastChannelGetLogsFunction() {
464 bool CastChannelGetLogsFunction::PrePrepare() {
465 api_
= CastChannelAPI::Get(browser_context());
466 return CastChannelAsyncApiFunction::PrePrepare();
469 bool CastChannelGetLogsFunction::Prepare() {
473 void CastChannelGetLogsFunction::AsyncWorkStart() {
477 scoped_ptr
<char[]> out
= api_
->GetLogger()->GetLogs(&length
);
479 SetResult(new base::BinaryValue(out
.Pass(), length
));
481 SetError("Unable to get logs.");
484 api_
->GetLogger()->Reset();
486 AsyncWorkCompleted();
489 CastChannelOpenFunction::CastMessageHandler::CastMessageHandler(
490 const EventDispatchCallback
& ui_dispatch_cb
,
491 cast_channel::CastSocket
* socket
,
492 scoped_refptr
<Logger
> logger
)
493 : ui_dispatch_cb_(ui_dispatch_cb
), socket_(socket
), logger_(logger
) {
498 CastChannelOpenFunction::CastMessageHandler::~CastMessageHandler() {
501 void CastChannelOpenFunction::CastMessageHandler::OnError(
502 cast_channel::ChannelError error_state
) {
503 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
505 ChannelInfo channel_info
;
506 FillChannelInfo(*socket_
, &channel_info
);
507 channel_info
.error_state
= error_state
;
508 ErrorInfo error_info
;
509 FillErrorInfo(error_state
, logger_
->GetLastErrors(socket_
->id()),
512 scoped_ptr
<base::ListValue
> results
=
513 OnError::Create(channel_info
, error_info
);
514 scoped_ptr
<Event
> event(new Event(events::CAST_CHANNEL_ON_ERROR
,
515 OnError::kEventName
, results
.Pass()));
516 BrowserThread::PostTask(
517 BrowserThread::UI
, FROM_HERE
,
518 base::Bind(ui_dispatch_cb_
, socket_
->owner_extension_id(),
519 base::Passed(event
.Pass())));
522 void CastChannelOpenFunction::CastMessageHandler::OnMessage(
523 const CastMessage
& message
) {
524 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
526 MessageInfo message_info
;
527 cast_channel::CastMessageToMessageInfo(message
, &message_info
);
528 ChannelInfo channel_info
;
529 FillChannelInfo(*socket_
, &channel_info
);
530 VLOG(1) << "Received message " << ParamToString(message_info
)
531 << " on channel " << ParamToString(channel_info
);
533 scoped_ptr
<base::ListValue
> results
=
534 OnMessage::Create(channel_info
, message_info
);
535 scoped_ptr
<Event
> event(new Event(events::CAST_CHANNEL_ON_MESSAGE
,
536 OnMessage::kEventName
, results
.Pass()));
537 BrowserThread::PostTask(
538 BrowserThread::UI
, FROM_HERE
,
539 base::Bind(ui_dispatch_cb_
, socket_
->owner_extension_id(),
540 base::Passed(event
.Pass())));
543 void CastChannelOpenFunction::CastMessageHandler::Start() {
546 CastChannelSetAuthorityKeysFunction::CastChannelSetAuthorityKeysFunction() {
549 CastChannelSetAuthorityKeysFunction::~CastChannelSetAuthorityKeysFunction() {
552 bool CastChannelSetAuthorityKeysFunction::Prepare() {
553 params_
= cast_channel::SetAuthorityKeys::Params::Create(*args_
);
554 EXTENSION_FUNCTION_VALIDATE(params_
.get());
558 void CastChannelSetAuthorityKeysFunction::AsyncWorkStart() {
559 std::string
& keys
= params_
->keys
;
560 std::string
& signature
= params_
->signature
;
561 if (signature
.empty() || keys
.empty() ||
562 !cast_channel::SetTrustedCertificateAuthorities(keys
, signature
)) {
563 SetError("Unable to set authority keys.");
566 AsyncWorkCompleted();
569 } // namespace extensions