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.
5 #include "chrome/browser/extensions/api/cast_channel/cast_channel_api.h"
9 #include "base/json/json_writer.h"
10 #include "base/memory/scoped_ptr.h"
11 #include "base/strings/string_number_conversions.h"
12 #include "base/values.h"
13 #include "chrome/browser/browser_process.h"
14 #include "chrome/browser/extensions/api/cast_channel/cast_socket.h"
15 #include "chrome/browser/net/chrome_net_log.h"
16 #include "content/public/browser/browser_thread.h"
17 #include "extensions/browser/event_router.h"
18 #include "net/base/ip_endpoint.h"
19 #include "net/base/net_errors.h"
20 #include "net/base/net_util.h"
23 namespace extensions
{
25 namespace Close
= cast_channel::Close
;
26 namespace OnError
= cast_channel::OnError
;
27 namespace OnMessage
= cast_channel::OnMessage
;
28 namespace Open
= cast_channel::Open
;
29 namespace Send
= cast_channel::Send
;
30 using cast_channel::CastSocket
;
31 using cast_channel::ChannelAuthType
;
32 using cast_channel::ChannelError
;
33 using cast_channel::ChannelInfo
;
34 using cast_channel::ConnectInfo
;
35 using cast_channel::MessageInfo
;
36 using cast_channel::ReadyState
;
37 using content::BrowserThread
;
41 // T is an extension dictionary (MessageInfo or ChannelInfo)
43 std::string
ParamToString(const T
& info
) {
44 scoped_ptr
<base::DictionaryValue
> dict
= info
.ToValue();
46 base::JSONWriter::Write(dict
.get(), &out
);
50 // Fills |channel_info| from the destination and state of |socket|.
51 void FillChannelInfo(const CastSocket
& socket
, ChannelInfo
* channel_info
) {
53 channel_info
->channel_id
= socket
.id();
54 channel_info
->url
= socket
.CastUrl();
55 const net::IPEndPoint
& ip_endpoint
= socket
.ip_endpoint();
56 channel_info
->connect_info
.ip_address
= ip_endpoint
.ToStringWithoutPort();
57 channel_info
->connect_info
.port
= ip_endpoint
.port();
58 channel_info
->connect_info
.auth
= socket
.channel_auth();
59 channel_info
->ready_state
= socket
.ready_state();
60 channel_info
->error_state
= socket
.error_state();
65 CastChannelAPI::CastChannelAPI(content::BrowserContext
* context
)
66 : browser_context_(context
) {
67 DCHECK(browser_context_
);
71 CastChannelAPI
* CastChannelAPI::Get(content::BrowserContext
* context
) {
72 return BrowserContextKeyedAPIFactory
<CastChannelAPI
>::Get(context
);
75 static base::LazyInstance
<BrowserContextKeyedAPIFactory
<CastChannelAPI
> >
76 g_factory
= LAZY_INSTANCE_INITIALIZER
;
79 BrowserContextKeyedAPIFactory
<CastChannelAPI
>*
80 CastChannelAPI::GetFactoryInstance() {
81 return g_factory
.Pointer();
84 scoped_ptr
<CastSocket
> CastChannelAPI::CreateCastSocket(
85 const std::string
& extension_id
, const net::IPEndPoint
& ip_endpoint
,
86 ChannelAuthType channel_auth
) {
87 if (socket_for_test_
.get()) {
88 return socket_for_test_
.Pass();
90 return scoped_ptr
<CastSocket
>(
91 new CastSocket(extension_id
, ip_endpoint
, channel_auth
, this,
92 g_browser_process
->net_log()));
96 void CastChannelAPI::SetSocketForTest(scoped_ptr
<CastSocket
> socket_for_test
) {
97 socket_for_test_
= socket_for_test
.Pass();
100 void CastChannelAPI::OnError(const CastSocket
* socket
,
101 cast_channel::ChannelError error
) {
102 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
103 ChannelInfo channel_info
;
104 FillChannelInfo(*socket
, &channel_info
);
105 channel_info
.error_state
= error
;
106 scoped_ptr
<base::ListValue
> results
= OnError::Create(channel_info
);
107 scoped_ptr
<Event
> event(new Event(OnError::kEventName
, results
.Pass()));
108 extensions::EventRouter::Get(browser_context_
)
109 ->DispatchEventToExtension(socket
->owner_extension_id(), event
.Pass());
112 void CastChannelAPI::OnMessage(const CastSocket
* socket
,
113 const MessageInfo
& message_info
) {
114 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
115 ChannelInfo channel_info
;
116 FillChannelInfo(*socket
, &channel_info
);
117 scoped_ptr
<base::ListValue
> results
=
118 OnMessage::Create(channel_info
, message_info
);
119 VLOG(1) << "Sending message " << ParamToString(message_info
)
120 << " to channel " << ParamToString(channel_info
);
121 scoped_ptr
<Event
> event(new Event(OnMessage::kEventName
, results
.Pass()));
122 extensions::EventRouter::Get(browser_context_
)
123 ->DispatchEventToExtension(socket
->owner_extension_id(), event
.Pass());
126 CastChannelAPI::~CastChannelAPI() {}
128 CastChannelAsyncApiFunction::CastChannelAsyncApiFunction()
129 : manager_(NULL
), error_(cast_channel::CHANNEL_ERROR_NONE
) { }
131 CastChannelAsyncApiFunction::~CastChannelAsyncApiFunction() { }
133 bool CastChannelAsyncApiFunction::PrePrepare() {
134 manager_
= ApiResourceManager
<CastSocket
>::Get(browser_context());
138 bool CastChannelAsyncApiFunction::Respond() {
139 return error_
!= cast_channel::CHANNEL_ERROR_NONE
;
142 CastSocket
* CastChannelAsyncApiFunction::GetSocketOrCompleteWithError(
144 CastSocket
* socket
= GetSocket(channel_id
);
146 SetResultFromError(cast_channel::CHANNEL_ERROR_INVALID_CHANNEL_ID
);
147 AsyncWorkCompleted();
152 int CastChannelAsyncApiFunction::AddSocket(CastSocket
* socket
) {
153 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
156 const int id
= manager_
->Add(socket
);
161 void CastChannelAsyncApiFunction::RemoveSocket(int channel_id
) {
162 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
164 manager_
->Remove(extension_
->id(), channel_id
);
167 void CastChannelAsyncApiFunction::SetResultFromSocket(int channel_id
) {
168 CastSocket
* socket
= GetSocket(channel_id
);
170 ChannelInfo channel_info
;
171 FillChannelInfo(*socket
, &channel_info
);
172 error_
= socket
->error_state();
173 SetResultFromChannelInfo(channel_info
);
176 void CastChannelAsyncApiFunction::SetResultFromError(ChannelError error
) {
177 ChannelInfo channel_info
;
178 channel_info
.channel_id
= -1;
179 channel_info
.url
= "";
180 channel_info
.ready_state
= cast_channel::READY_STATE_CLOSED
;
181 channel_info
.error_state
= error
;
182 SetResultFromChannelInfo(channel_info
);
186 CastSocket
* CastChannelAsyncApiFunction::GetSocket(int channel_id
) {
187 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
189 return manager_
->Get(extension_
->id(), channel_id
);
192 void CastChannelAsyncApiFunction::SetResultFromChannelInfo(
193 const ChannelInfo
& channel_info
) {
194 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
195 SetResult(channel_info
.ToValue().release());
198 CastChannelOpenFunction::CastChannelOpenFunction()
199 : new_channel_id_(0) { }
201 CastChannelOpenFunction::~CastChannelOpenFunction() { }
203 // TODO(mfoltz): Remove URL parsing when clients have converted to use
206 // Allowed schemes for Cast device URLs.
207 const char kCastInsecureScheme
[] = "cast";
208 const char kCastSecureScheme
[] = "casts";
210 bool CastChannelOpenFunction::ParseChannelUrl(const GURL
& url
,
211 ConnectInfo
* connect_info
) {
212 DCHECK(connect_info
);
213 VLOG(2) << "ParseChannelUrl";
214 bool auth_required
= false;
215 if (url
.SchemeIs(kCastSecureScheme
)) {
216 auth_required
= true;
217 } else if (!url
.SchemeIs(kCastInsecureScheme
)) {
220 // TODO(mfoltz): Test for IPv6 addresses. Brackets or no brackets?
221 // TODO(mfoltz): Maybe enforce restriction to IPv4 private and IPv6
222 // link-local networks
223 const std::string
& path
= url
.path();
224 // Shortest possible: //A:B
225 if (path
.size() < 5) {
228 if (path
.find("//") != 0) {
231 size_t colon
= path
.find_last_of(':');
232 if (colon
== std::string::npos
|| colon
< 3 || colon
> path
.size() - 2) {
235 const std::string
& ip_address_str
= path
.substr(2, colon
- 2);
236 const std::string
& port_str
= path
.substr(colon
+ 1);
237 VLOG(2) << "IP: " << ip_address_str
<< " Port: " << port_str
;
239 if (!base::StringToInt(port_str
, &port
))
241 connect_info
->ip_address
= ip_address_str
;
242 connect_info
->port
= port
;
243 connect_info
->auth
= auth_required
?
244 cast_channel::CHANNEL_AUTH_TYPE_SSL_VERIFIED
:
245 cast_channel::CHANNEL_AUTH_TYPE_SSL
;
249 net::IPEndPoint
* CastChannelOpenFunction::ParseConnectInfo(
250 const ConnectInfo
& connect_info
) {
251 net::IPAddressNumber ip_address
;
252 if (!net::ParseIPLiteralToNumber(connect_info
.ip_address
, &ip_address
)) {
255 if (connect_info
.port
< 0 || connect_info
.port
>
256 std::numeric_limits
<unsigned short>::max()) {
259 if (connect_info
.auth
!= cast_channel::CHANNEL_AUTH_TYPE_SSL_VERIFIED
&&
260 connect_info
.auth
!= cast_channel::CHANNEL_AUTH_TYPE_SSL
) {
263 return new net::IPEndPoint(ip_address
, connect_info
.port
);
266 bool CastChannelOpenFunction::PrePrepare() {
267 api_
= CastChannelAPI::Get(browser_context());
268 return CastChannelAsyncApiFunction::PrePrepare();
271 bool CastChannelOpenFunction::Prepare() {
272 params_
= Open::Params::Create(*args_
);
273 EXTENSION_FUNCTION_VALIDATE(params_
.get());
274 // The connect_info parameter may be a string URL like cast:// or casts:// or
275 // a ConnectInfo object.
276 std::string cast_url
;
277 switch (params_
->connect_info
->GetType()) {
278 case base::Value::TYPE_STRING
:
279 CHECK(params_
->connect_info
->GetAsString(&cast_url
));
280 connect_info_
.reset(new ConnectInfo
);
281 if (!ParseChannelUrl(GURL(cast_url
), connect_info_
.get())) {
282 connect_info_
.reset();
285 case base::Value::TYPE_DICTIONARY
:
286 connect_info_
= ConnectInfo::FromValue(*(params_
->connect_info
));
291 if (connect_info_
.get()) {
292 channel_auth_
= connect_info_
->auth
;
293 ip_endpoint_
.reset(ParseConnectInfo(*connect_info_
));
294 return ip_endpoint_
.get() != NULL
;
299 void CastChannelOpenFunction::AsyncWorkStart() {
301 DCHECK(ip_endpoint_
.get());
302 scoped_ptr
<CastSocket
> socket
= api_
->CreateCastSocket(
303 extension_
->id(), *ip_endpoint_
, channel_auth_
);
304 new_channel_id_
= AddSocket(socket
.release());
305 GetSocket(new_channel_id_
)->Connect(
306 base::Bind(&CastChannelOpenFunction::OnOpen
, this));
309 void CastChannelOpenFunction::OnOpen(int result
) {
310 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
311 SetResultFromSocket(new_channel_id_
);
312 AsyncWorkCompleted();
315 CastChannelSendFunction::CastChannelSendFunction() { }
317 CastChannelSendFunction::~CastChannelSendFunction() { }
319 bool CastChannelSendFunction::Prepare() {
320 params_
= Send::Params::Create(*args_
);
321 EXTENSION_FUNCTION_VALIDATE(params_
.get());
325 void CastChannelSendFunction::AsyncWorkStart() {
326 CastSocket
* socket
= GetSocketOrCompleteWithError(
327 params_
->channel
.channel_id
);
329 socket
->SendMessage(params_
->message
,
330 base::Bind(&CastChannelSendFunction::OnSend
, this));
333 void CastChannelSendFunction::OnSend(int result
) {
334 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
336 SetResultFromError(cast_channel::CHANNEL_ERROR_SOCKET_ERROR
);
338 SetResultFromSocket(params_
->channel
.channel_id
);
340 AsyncWorkCompleted();
343 CastChannelCloseFunction::CastChannelCloseFunction() { }
345 CastChannelCloseFunction::~CastChannelCloseFunction() { }
347 bool CastChannelCloseFunction::Prepare() {
348 params_
= Close::Params::Create(*args_
);
349 EXTENSION_FUNCTION_VALIDATE(params_
.get());
353 void CastChannelCloseFunction::AsyncWorkStart() {
354 CastSocket
* socket
= GetSocketOrCompleteWithError(
355 params_
->channel
.channel_id
);
357 socket
->Close(base::Bind(&CastChannelCloseFunction::OnClose
, this));
360 void CastChannelCloseFunction::OnClose(int result
) {
361 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
362 VLOG(1) << "CastChannelCloseFunction::OnClose result = " << result
;
364 SetResultFromError(cast_channel::CHANNEL_ERROR_SOCKET_ERROR
);
366 int channel_id
= params_
->channel
.channel_id
;
367 SetResultFromSocket(channel_id
);
368 RemoveSocket(channel_id
);
370 AsyncWorkCompleted();
373 } // namespace extensions