Revert 285173 "Removed InProcessBrowserTest::CleanUpOnMainThread()"
[chromium-blink-merge.git] / chrome / browser / extensions / api / cast_channel / cast_channel_api.cc
blobb4378ea0ac060e0d54c3e80295f20686c62641c0
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"
7 #include <limits>
8 #include <string>
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"
24 namespace extensions {
26 namespace Close = cast_channel::Close;
27 namespace OnError = cast_channel::OnError;
28 namespace OnMessage = cast_channel::OnMessage;
29 namespace Open = cast_channel::Open;
30 namespace Send = cast_channel::Send;
31 using cast_channel::CastSocket;
32 using cast_channel::ChannelAuthType;
33 using cast_channel::ChannelError;
34 using cast_channel::ChannelInfo;
35 using cast_channel::ConnectInfo;
36 using cast_channel::MessageInfo;
37 using cast_channel::ReadyState;
38 using content::BrowserThread;
40 namespace {
42 // T is an extension dictionary (MessageInfo or ChannelInfo)
43 template <class T>
44 std::string ParamToString(const T& info) {
45 scoped_ptr<base::DictionaryValue> dict = info.ToValue();
46 std::string out;
47 base::JSONWriter::Write(dict.get(), &out);
48 return out;
51 // Fills |channel_info| from the destination and state of |socket|.
52 void FillChannelInfo(const CastSocket& socket, ChannelInfo* channel_info) {
53 DCHECK(channel_info);
54 channel_info->channel_id = socket.id();
55 channel_info->url = socket.CastUrl();
56 const net::IPEndPoint& ip_endpoint = socket.ip_endpoint();
57 channel_info->connect_info.ip_address = ip_endpoint.ToStringWithoutPort();
58 channel_info->connect_info.port = ip_endpoint.port();
59 channel_info->connect_info.auth = socket.channel_auth();
60 channel_info->ready_state = socket.ready_state();
61 channel_info->error_state = socket.error_state();
64 bool IsValidConnectInfoPort(const ConnectInfo& connect_info) {
65 return connect_info.port > 0 && connect_info.port <
66 std::numeric_limits<unsigned short>::max();
69 bool IsValidConnectInfoAuth(const ConnectInfo& connect_info) {
70 return connect_info.auth == cast_channel::CHANNEL_AUTH_TYPE_SSL_VERIFIED ||
71 connect_info.auth == cast_channel::CHANNEL_AUTH_TYPE_SSL;
74 bool IsValidConnectInfoIpAddress(const ConnectInfo& connect_info) {
75 net::IPAddressNumber ip_address;
76 return net::ParseIPLiteralToNumber(connect_info.ip_address, &ip_address);
79 } // namespace
81 CastChannelAPI::CastChannelAPI(content::BrowserContext* context)
82 : browser_context_(context) {
83 DCHECK(browser_context_);
86 // static
87 CastChannelAPI* CastChannelAPI::Get(content::BrowserContext* context) {
88 return BrowserContextKeyedAPIFactory<CastChannelAPI>::Get(context);
91 static base::LazyInstance<BrowserContextKeyedAPIFactory<CastChannelAPI> >
92 g_factory = LAZY_INSTANCE_INITIALIZER;
94 // static
95 BrowserContextKeyedAPIFactory<CastChannelAPI>*
96 CastChannelAPI::GetFactoryInstance() {
97 return g_factory.Pointer();
100 scoped_ptr<CastSocket> CastChannelAPI::CreateCastSocket(
101 const std::string& extension_id, const net::IPEndPoint& ip_endpoint,
102 ChannelAuthType channel_auth) {
103 if (socket_for_test_.get()) {
104 return socket_for_test_.Pass();
105 } else {
106 return scoped_ptr<CastSocket>(
107 new CastSocket(extension_id, ip_endpoint, channel_auth, this,
108 g_browser_process->net_log()));
112 void CastChannelAPI::SetSocketForTest(scoped_ptr<CastSocket> socket_for_test) {
113 socket_for_test_ = socket_for_test.Pass();
116 void CastChannelAPI::OnError(const CastSocket* socket,
117 cast_channel::ChannelError error) {
118 DCHECK_CURRENTLY_ON(BrowserThread::IO);
119 ChannelInfo channel_info;
120 FillChannelInfo(*socket, &channel_info);
121 channel_info.error_state = error;
122 scoped_ptr<base::ListValue> results = OnError::Create(channel_info);
123 scoped_ptr<Event> event(new Event(OnError::kEventName, results.Pass()));
124 extensions::EventRouter::Get(browser_context_)
125 ->DispatchEventToExtension(socket->owner_extension_id(), event.Pass());
128 void CastChannelAPI::OnMessage(const CastSocket* socket,
129 const MessageInfo& message_info) {
130 DCHECK_CURRENTLY_ON(BrowserThread::IO);
131 ChannelInfo channel_info;
132 FillChannelInfo(*socket, &channel_info);
133 scoped_ptr<base::ListValue> results =
134 OnMessage::Create(channel_info, message_info);
135 VLOG(1) << "Sending message " << ParamToString(message_info)
136 << " to channel " << ParamToString(channel_info);
137 scoped_ptr<Event> event(new Event(OnMessage::kEventName, results.Pass()));
138 extensions::EventRouter::Get(browser_context_)
139 ->DispatchEventToExtension(socket->owner_extension_id(), event.Pass());
142 CastChannelAPI::~CastChannelAPI() {}
144 CastChannelAsyncApiFunction::CastChannelAsyncApiFunction()
145 : manager_(NULL), error_(cast_channel::CHANNEL_ERROR_NONE) { }
147 CastChannelAsyncApiFunction::~CastChannelAsyncApiFunction() { }
149 bool CastChannelAsyncApiFunction::PrePrepare() {
150 manager_ = ApiResourceManager<CastSocket>::Get(browser_context());
151 return true;
154 bool CastChannelAsyncApiFunction::Respond() {
155 return error_ != cast_channel::CHANNEL_ERROR_NONE;
158 CastSocket* CastChannelAsyncApiFunction::GetSocketOrCompleteWithError(
159 int channel_id) {
160 CastSocket* socket = GetSocket(channel_id);
161 if (!socket) {
162 SetResultFromError(cast_channel::CHANNEL_ERROR_INVALID_CHANNEL_ID);
163 AsyncWorkCompleted();
165 return socket;
168 int CastChannelAsyncApiFunction::AddSocket(CastSocket* socket) {
169 DCHECK_CURRENTLY_ON(BrowserThread::IO);
170 DCHECK(socket);
171 DCHECK(manager_);
172 const int id = manager_->Add(socket);
173 socket->set_id(id);
174 return id;
177 void CastChannelAsyncApiFunction::RemoveSocket(int channel_id) {
178 DCHECK_CURRENTLY_ON(BrowserThread::IO);
179 DCHECK(manager_);
180 manager_->Remove(extension_->id(), channel_id);
183 void CastChannelAsyncApiFunction::SetResultFromSocket(int channel_id) {
184 CastSocket* socket = GetSocket(channel_id);
185 DCHECK(socket);
186 ChannelInfo channel_info;
187 FillChannelInfo(*socket, &channel_info);
188 error_ = socket->error_state();
189 SetResultFromChannelInfo(channel_info);
192 void CastChannelAsyncApiFunction::SetResultFromError(ChannelError error) {
193 ChannelInfo channel_info;
194 channel_info.channel_id = -1;
195 channel_info.url = "";
196 channel_info.ready_state = cast_channel::READY_STATE_CLOSED;
197 channel_info.error_state = error;
198 SetResultFromChannelInfo(channel_info);
199 error_ = error;
202 CastSocket* CastChannelAsyncApiFunction::GetSocket(int channel_id) {
203 DCHECK_CURRENTLY_ON(BrowserThread::IO);
204 DCHECK(manager_);
205 return manager_->Get(extension_->id(), channel_id);
208 void CastChannelAsyncApiFunction::SetResultFromChannelInfo(
209 const ChannelInfo& channel_info) {
210 DCHECK_CURRENTLY_ON(BrowserThread::IO);
211 SetResult(channel_info.ToValue().release());
214 CastChannelOpenFunction::CastChannelOpenFunction()
215 : new_channel_id_(0) { }
217 CastChannelOpenFunction::~CastChannelOpenFunction() { }
219 // TODO(mfoltz): Remove URL parsing when clients have converted to use
220 // ConnectInfo.
222 // Allowed schemes for Cast device URLs.
223 const char kCastInsecureScheme[] = "cast";
224 const char kCastSecureScheme[] = "casts";
226 bool CastChannelOpenFunction::ParseChannelUrl(const GURL& url,
227 ConnectInfo* connect_info) {
228 DCHECK(connect_info);
229 VLOG(2) << "ParseChannelUrl";
230 bool auth_required = false;
231 if (url.SchemeIs(kCastSecureScheme)) {
232 auth_required = true;
233 } else if (!url.SchemeIs(kCastInsecureScheme)) {
234 return false;
236 // TODO(mfoltz): Test for IPv6 addresses. Brackets or no brackets?
237 // TODO(mfoltz): Maybe enforce restriction to IPv4 private and IPv6
238 // link-local networks
239 const std::string& path = url.path();
240 // Shortest possible: //A:B
241 if (path.size() < 5) {
242 return false;
244 if (path.find("//") != 0) {
245 return false;
247 size_t colon = path.find_last_of(':');
248 if (colon == std::string::npos || colon < 3 || colon > path.size() - 2) {
249 return false;
251 const std::string& ip_address_str = path.substr(2, colon - 2);
252 const std::string& port_str = path.substr(colon + 1);
253 VLOG(2) << "IP: " << ip_address_str << " Port: " << port_str;
254 int port;
255 if (!base::StringToInt(port_str, &port))
256 return false;
257 connect_info->ip_address = ip_address_str;
258 connect_info->port = port;
259 connect_info->auth = auth_required ?
260 cast_channel::CHANNEL_AUTH_TYPE_SSL_VERIFIED :
261 cast_channel::CHANNEL_AUTH_TYPE_SSL;
262 return true;
265 net::IPEndPoint* CastChannelOpenFunction::ParseConnectInfo(
266 const ConnectInfo& connect_info) {
267 net::IPAddressNumber ip_address;
268 CHECK(net::ParseIPLiteralToNumber(connect_info.ip_address, &ip_address));
269 return new net::IPEndPoint(ip_address, connect_info.port);
272 bool CastChannelOpenFunction::PrePrepare() {
273 api_ = CastChannelAPI::Get(browser_context());
274 return CastChannelAsyncApiFunction::PrePrepare();
277 bool CastChannelOpenFunction::Prepare() {
278 params_ = Open::Params::Create(*args_);
279 EXTENSION_FUNCTION_VALIDATE(params_.get());
280 // The connect_info parameter may be a string URL like cast:// or casts:// or
281 // a ConnectInfo object.
282 std::string cast_url;
283 switch (params_->connect_info->GetType()) {
284 case base::Value::TYPE_STRING:
285 CHECK(params_->connect_info->GetAsString(&cast_url));
286 connect_info_.reset(new ConnectInfo);
287 if (!ParseChannelUrl(GURL(cast_url), connect_info_.get())) {
288 connect_info_.reset();
289 SetError("Invalid connect_info (invalid Cast URL " + cast_url + ")");
291 break;
292 case base::Value::TYPE_DICTIONARY:
293 connect_info_ = ConnectInfo::FromValue(*(params_->connect_info));
294 if (!connect_info_.get()) {
295 SetError("connect_info.auth is required");
297 break;
298 default:
299 SetError("Invalid connect_info (unknown type)");
300 break;
302 if (!connect_info_.get()) {
303 return false;
305 if (!IsValidConnectInfoPort(*connect_info_)) {
306 SetError("Invalid connect_info (invalid port)");
307 } else if (!IsValidConnectInfoAuth(*connect_info_)) {
308 SetError("Invalid connect_info (invalid auth)");
309 } else if (!IsValidConnectInfoIpAddress(*connect_info_)) {
310 SetError("Invalid connect_info (invalid IP address)");
312 if (!GetError().empty()) {
313 return false;
315 channel_auth_ = connect_info_->auth;
316 ip_endpoint_.reset(ParseConnectInfo(*connect_info_));
317 return true;
320 void CastChannelOpenFunction::AsyncWorkStart() {
321 DCHECK(api_);
322 DCHECK(ip_endpoint_.get());
323 scoped_ptr<CastSocket> socket = api_->CreateCastSocket(
324 extension_->id(), *ip_endpoint_, channel_auth_);
325 new_channel_id_ = AddSocket(socket.release());
326 GetSocket(new_channel_id_)->Connect(
327 base::Bind(&CastChannelOpenFunction::OnOpen, this));
330 void CastChannelOpenFunction::OnOpen(int result) {
331 DCHECK_CURRENTLY_ON(BrowserThread::IO);
332 SetResultFromSocket(new_channel_id_);
333 AsyncWorkCompleted();
336 CastChannelSendFunction::CastChannelSendFunction() { }
338 CastChannelSendFunction::~CastChannelSendFunction() { }
340 bool CastChannelSendFunction::Prepare() {
341 params_ = Send::Params::Create(*args_);
342 EXTENSION_FUNCTION_VALIDATE(params_.get());
343 if (params_->message.namespace_.empty()) {
344 SetError("message_info.namespace_ is required");
345 return false;
347 if (params_->message.source_id.empty()) {
348 SetError("message_info.source_id is required");
349 return false;
351 if (params_->message.destination_id.empty()) {
352 SetError("message_info.destination_id is required");
353 return false;
355 switch (params_->message.data->GetType()) {
356 case base::Value::TYPE_STRING:
357 case base::Value::TYPE_BINARY:
358 break;
359 default:
360 SetError("Invalid type of message_info.data");
361 return false;
363 return true;
366 void CastChannelSendFunction::AsyncWorkStart() {
367 CastSocket* socket = GetSocketOrCompleteWithError(
368 params_->channel.channel_id);
369 if (socket)
370 socket->SendMessage(params_->message,
371 base::Bind(&CastChannelSendFunction::OnSend, this));
374 void CastChannelSendFunction::OnSend(int result) {
375 DCHECK_CURRENTLY_ON(BrowserThread::IO);
376 if (result < 0) {
377 SetResultFromError(cast_channel::CHANNEL_ERROR_SOCKET_ERROR);
378 } else {
379 SetResultFromSocket(params_->channel.channel_id);
381 AsyncWorkCompleted();
384 CastChannelCloseFunction::CastChannelCloseFunction() { }
386 CastChannelCloseFunction::~CastChannelCloseFunction() { }
388 bool CastChannelCloseFunction::Prepare() {
389 params_ = Close::Params::Create(*args_);
390 EXTENSION_FUNCTION_VALIDATE(params_.get());
391 return true;
394 void CastChannelCloseFunction::AsyncWorkStart() {
395 CastSocket* socket = GetSocketOrCompleteWithError(
396 params_->channel.channel_id);
397 if (socket)
398 socket->Close(base::Bind(&CastChannelCloseFunction::OnClose, this));
401 void CastChannelCloseFunction::OnClose(int result) {
402 DCHECK_CURRENTLY_ON(BrowserThread::IO);
403 VLOG(1) << "CastChannelCloseFunction::OnClose result = " << result;
404 if (result < 0) {
405 SetResultFromError(cast_channel::CHANNEL_ERROR_SOCKET_ERROR);
406 } else {
407 int channel_id = params_->channel.channel_id;
408 SetResultFromSocket(channel_id);
409 RemoveSocket(channel_id);
411 AsyncWorkCompleted();
414 } // namespace extensions