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

Side by Side Diff: chrome/browser/media/router/discovery/dial/device_description_service.cc

Issue 2701633002: [Media Router] Add DialMediaSinkService and DeviceDescriptionService (Closed)
Patch Set: resolve code review comments from Mark cont Created 3 years, 8 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
OLDNEW
(Empty)
1 // Copyright 2017 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/media/router/discovery/dial/device_description_service. h"
6
7 #include "base/strings/string_util.h"
8 #include "chrome/browser/media/router/discovery/dial/device_description_fetcher. h"
9 #include "chrome/browser/media/router/discovery/dial/safe_dial_device_descriptio n_parser.h"
10 #include "net/base/ip_address.h"
11 #include "url/gurl.h"
12
13 namespace {
14
15 // How long to cache a device description.
16 // TODO: Use value from chrome.dial when http://crbug.com/165289 is fixed.
mark a. foltz 2017/04/12 00:17:17 I'm going to close this as WontFix.
zhaobin 2017/04/18 06:58:26 Done.
17 const int kDeviceDescriptionCacheTimeMillis = 30 * 60 * 1000;
18
19 // Replaces "<element_name>content</element_name>" with
20 // "<element_name>***</element_name>"
21 void Scrub(const std::string& element_name, std::string& xml_text) {
imcheng 2017/04/12 19:33:50 nit: |xml_text| should be passed in as a pointer.
zhaobin 2017/04/18 06:58:25 Done.
22 size_t pos = xml_text.find("<" + element_name + ">");
23 size_t end_pos = xml_text.find("</" + element_name + ">");
24 size_t start_pos = pos + element_name.length() + 2;
mark a. foltz 2017/04/12 00:17:17 What happens if pos == std::string::npos?
zhaobin 2017/04/18 06:58:26 Done.
25
26 if (pos != std::string::npos && end_pos > start_pos)
27 xml_text.replace(start_pos, end_pos - start_pos, "***");
28 }
29
30 // Removes unique identifiers from the device description.
31 // |xml_text|: xmlDoc The device description.
32 // Returns false if <UDN> or <serialNumber> field does not exist in |xml_text|.
mark a. foltz 2017/04/12 00:17:17 This needs to be updated
zhaobin 2017/04/18 06:58:25 Done.
33 std::string ScrubDeviceDescriptionXml(const std::string& xml_text) {
34 std::string scrubbed_xml(xml_text);
35 Scrub("UDN", scrubbed_xml);
36 Scrub("serialNumber", scrubbed_xml);
37 return scrubbed_xml;
38 }
39
40 // Validates whether a URL is valid for fetching device descriptions.
41 // |descriptionUrl|: Device description URL.
42 // Returns true if descriptionUrl is valid.
43 static bool IsValidDeviceDescriptionUrl(const GURL& device_description_url) {
imcheng 2017/04/12 19:33:50 Functions in anonymous namespace do not need to be
zhaobin 2017/04/18 06:58:25 Done.
44 if (!device_description_url.SchemeIs(url::kHttpScheme))
45 return false;
46
47 net::IPAddress address;
48 if (!net::ParseURLHostnameToAddress(device_description_url.host(), &address))
mark a. foltz 2017/04/12 00:17:17 This check could go earlier in DialService::ParseR
zhaobin 2017/04/18 06:58:26 Done.
49 return false;
50
51 return address.IsReserved();
52 }
53
54 static bool IsValidAppUrl(const GURL& app_url, const std::string& ip_address) {
55 return app_url.SchemeIs(url::kHttpScheme) && app_url.host() == ip_address;
56 }
57
58 // Checks mandatory fields.
mark a. foltz 2017/04/12 00:17:17 Please document meaning of return value.
zhaobin 2017/04/18 06:58:25 Done.
59 static std::string Validate(
imcheng 2017/04/12 19:33:50 Maybe ValidateParsedDeviceDescription to be more c
zhaobin 2017/04/18 06:58:25 Done.
60 const std::string& expected_ip_address,
61 const media_router::ParsedDialDeviceDescription& description_data) {
62 if (description_data.unique_id.empty()) {
63 return "Missing uniqueId";
imcheng 2017/04/12 19:33:50 IMO, enum is slightly preferable to strings for in
zhaobin 2017/04/18 06:58:25 Done.
64 }
65 if (description_data.friendly_name.empty()) {
66 return "Missing friendlyName";
67 }
68 if (!description_data.app_url.is_valid()) {
69 return "Missing appUrl";
70 }
71 if (!IsValidAppUrl(description_data.app_url, expected_ip_address)) {
72 return "Invalid appUrl";
73 }
74 return "";
75 }
76
77 static std::string ToString(
mark a. foltz 2017/04/12 00:17:17 If this is only use with D*LOG statements, use #if
imcheng 2017/04/12 19:33:50 Naming suggestion: CachedDeviceDescriptionToString
zhaobin 2017/04/18 06:58:25 Done.
zhaobin 2017/04/18 06:58:26 Done.
78 const media_router::CachedParsedDialDeviceDescription& cached_data) {
79 std::stringstream ss;
80 ss << "CachedDialDeviceDescription [unique_id]: "
81 << cached_data.description_data.unique_id
82 << " [friendly_name]: " << cached_data.description_data.friendly_name
83 << " [model_name]: " << cached_data.description_data.model_name
84 << " [device_type]: " << cached_data.description_data.device_type
85 << " [app_url]: " << cached_data.description_data.app_url
86 << " [fetch_time_millis]: " << cached_data.fetch_time_millis
87 << " [expire_time_millis]: " << cached_data.expire_time_millis
88 << " [config_id]: ";
mark a. foltz 2017/04/12 00:17:17 Nit: Maybe only include this if config_id is set.
zhaobin 2017/04/18 06:58:26 Done.
89
90 if (cached_data.config_id)
91 ss << cached_data.config_id.value();
92
93 return ss.str();
94 }
95
96 } // namespace
97
98 namespace media_router {
99
100 DeviceDescriptionService::DeviceDescriptionService(
101 DeviceDescriptionParseSuccessCallback success_cb,
102 DeviceDescriptionParseErrorCallback error_cb)
103 : success_cb_(success_cb), error_cb_(error_cb) {}
104
105 DeviceDescriptionService::~DeviceDescriptionService() = default;
106
107 void DeviceDescriptionService::GetDeviceDescriptions(
108 const std::vector<DialDeviceData>& devices,
imcheng 2017/04/12 19:33:50 Is the idea that this gets called with the full de
zhaobin 2017/04/18 06:58:26 Done.
109 net::URLRequestContextGetter* request_context) {
110 DCHECK(thread_checker_.CalledOnValidThread());
111
112 std::map<std::string, std::unique_ptr<DeviceDescriptionFetcher>>
113 existing_fetcher_map;
114 for (const auto& device_data : devices) {
115 // Keep fetcher if it is for |device_data.label()| and has the same Url.
mark a. foltz 2017/04/12 00:17:17 nit: URL
zhaobin 2017/04/18 06:58:25 Done.
116 const auto& it = device_description_fetcher_map_.find(device_data.label());
117 if (it != device_description_fetcher_map_.end() &&
118 it->second->device_description_url() ==
119 device_data.device_description_url()) {
120 existing_fetcher_map.insert(
121 std::make_pair(device_data.label(), std::move(it->second)));
122 }
123 }
124
125 // Remove all out dated fetcheres.
mark a. foltz 2017/04/12 00:17:17 typo in fetchers
zhaobin 2017/04/18 06:58:25 Done.
126 device_description_fetcher_map_ = std::move(existing_fetcher_map);
mark a. foltz 2017/04/12 00:17:17 Will this delete all fetchers whose labels are not
zhaobin 2017/04/18 06:58:26 Seems to be safe. net::URLFetcher says "You may ca
127
128 for (const auto& device_data : devices) {
129 CachedParsedDialDeviceDescription description_data;
130 if (CheckAndUpdateCache(device_data, &description_data)) {
131 // Get device description from cache.
132 success_cb_.Run(device_data, description_data.description_data);
133 continue;
134 }
135
136 FetchDeviceDescription(device_data, request_context);
137 }
138 }
139
140 void DeviceDescriptionService::FetchDeviceDescription(
141 const DialDeviceData& device_data,
142 net::URLRequestContextGetter* request_context) {
143 DCHECK(thread_checker_.CalledOnValidThread());
144
145 if (!IsValidDeviceDescriptionUrl(device_data.device_description_url())) {
146 std::string error_message = "Invalid device description URL: " +
147 device_data.device_description_url().spec();
148 OnDeviceDescriptionFetchError(device_data, error_message);
149 return;
150 }
151
152 auto device_description_fetcher = base::MakeUnique<DeviceDescriptionFetcher>(
153 device_data.device_description_url(), request_context,
154 base::BindOnce(
155 &DeviceDescriptionService::OnDeviceDescriptionFetchComplete,
156 base::Unretained(this), device_data),
157 base::BindOnce(&DeviceDescriptionService::OnDeviceDescriptionFetchError,
158 base::Unretained(this), device_data));
159
160 device_description_fetcher->Start();
161 device_description_fetcher_map_.insert(std::make_pair(
162 device_data.label(), std::move(device_description_fetcher)));
163 }
164
165 bool DeviceDescriptionService::CheckAndUpdateCache(
166 const DialDeviceData& device_data,
167 CachedParsedDialDeviceDescription* out_description) {
168 const auto& it = description_map_.find(device_data.label());
169 if (it == description_map_.end())
170 return false;
171
172 // If the entry's configId does not match, or it has expired, remove it.
mark a. foltz 2017/04/12 00:17:17 nit: config_id
zhaobin 2017/04/18 06:58:25 Done.
173 if (it->second.config_id != device_data.config_id() ||
174 base::Time::Now() >= it->second.expire_time_millis) {
imcheng 2017/04/12 19:33:50 General comment: for tests you might find it usefu
zhaobin 2017/04/18 06:58:25 Done.
175 DVLOG(2) << "Removing invalid entry " << it->first;
176 description_map_.erase(it);
mark a. foltz 2017/04/12 00:17:17 It looks like there will be some expired cached en
zhaobin 2017/04/18 06:58:26 On 2017/04/12 00:17:17, mark a. foltz wrote: > It
177 return false;
178 }
179
180 // Entry is valid.
181 (*out_description) = it->second;
mark a. foltz 2017/04/12 00:17:17 It it necessary to make a copy here, or could Chec
zhaobin 2017/04/18 06:58:26 Done.
182 return true;
183 }
184
185 void DeviceDescriptionService::OnParsedDeviceDescription(
186 const DialDeviceData& device_data,
187 const GURL& app_url,
188 chrome::mojom::DialDeviceDescriptionPtr parsed_device_description) {
189 DCHECK(thread_checker_.CalledOnValidThread());
190
191 if (!parsed_device_description) {
192 error_cb_.Run(device_data.label(),
193 "Failed to parse device description xml");
194 return;
195 }
196
197 ParsedDialDeviceDescription description_data;
198 description_data.unique_id = parsed_device_description->unique_id;
199 description_data.friendly_name = parsed_device_description->friendly_name;
200 description_data.model_name = parsed_device_description->model_name;
201 description_data.device_type = parsed_device_description->device_type;
202 description_data.app_url = app_url;
203
204 std::string error =
205 Validate(device_data.device_description_url().host(), description_data);
206
207 if (error.empty()) {
mark a. foltz 2017/04/12 00:17:17 Nit: I would reverse the two cases of the if() and
zhaobin 2017/04/18 06:58:26 Done.
208 CachedParsedDialDeviceDescription cached_description_data;
209 cached_description_data.fetch_time_millis = base::Time::Now();
210 cached_description_data.expire_time_millis =
211 cached_description_data.fetch_time_millis +
212 base::TimeDelta::FromMilliseconds(kDeviceDescriptionCacheTimeMillis);
213 cached_description_data.config_id = device_data.config_id();
214 cached_description_data.description_data = description_data;
215
216 DVLOG(2) << "Got device description for " << device_data.label();
mark a. foltz 2017/04/12 00:17:17 Nit: These can be combined into one statement.
zhaobin 2017/04/18 06:58:26 Done.
217 DVLOG(2) << "... device description was: "
218 << ToString(cached_description_data);
219
220 // Stick it in the cache if the description has a config (version) id.
221 // NOTE: We could cache descriptions without a config id and rely on the
222 // timeout to eventually update the cache.
mark a. foltz 2017/04/12 00:17:17 This would be fine with me. The only piece of dat
zhaobin 2017/04/18 06:58:26 Acknowledged.
223 if (cached_description_data.config_id) {
224 DVLOG(2) << "Caching device description for " << device_data.label();
225 description_map_.insert(
mark a. foltz 2017/04/12 00:17:17 Can we set a maximum size on the number of cached
zhaobin 2017/04/18 06:58:25 Done.
226 std::make_pair(device_data.label(), cached_description_data));
227 }
228
229 success_cb_.Run(device_data, description_data);
230 } else {
231 DLOG(WARNING) << "Device description failed to validate: " << error;
232 error_cb_.Run(device_data.label(), "Failed to process fetch result");
233 }
234 }
235
236 void DeviceDescriptionService::OnDeviceDescriptionFetchComplete(
237 const DialDeviceData& device_data,
238 const DialDeviceDescriptionData& description_data) {
239 DCHECK(thread_checker_.CalledOnValidThread());
240
241 scoped_refptr<SafeDialDeviceDescriptionParser> parser(
mark a. foltz 2017/04/12 00:17:17 What keeps the parser alive after calling Start()?
zhaobin 2017/04/18 06:58:26 It is bound insdie parser->start() (https://codere
242 new SafeDialDeviceDescriptionParser());
mark a. foltz 2017/04/12 00:17:17 Could this potentially spawn multiple utility proc
zhaobin 2017/04/18 06:58:25 Done.
243
244 parser->Start(description_data.device_description,
245 base::Bind(&DeviceDescriptionService::OnParsedDeviceDescription,
246 base::Unretained(this), device_data,
mark a. foltz 2017/04/12 00:17:17 What prevents the callback from being run after |t
zhaobin 2017/04/18 06:58:26 (I think) |this| is owned by DialMediaSinkService,
247 description_data.app_url));
248
249 std::string xml_logging =
imcheng 2017/04/12 19:33:50 Similar to above, we want to guard this with #ifnd
zhaobin 2017/04/18 06:58:25 Done.
250 ScrubDeviceDescriptionXml(description_data.device_description);
251 DVLOG(2) << "Device description: " << xml_logging;
252
253 device_description_fetcher_map_.erase(device_data.label());
254 }
255
256 void DeviceDescriptionService::OnDeviceDescriptionFetchError(
257 const DialDeviceData& device_data,
258 const std::string& error_message) {
259 DCHECK(thread_checker_.CalledOnValidThread());
260
261 DVLOG(2) << "OnDeviceDescriptionFetchError [label]: " << device_data.label();
262
263 error_cb_.Run(device_data.label(), error_message);
264 device_description_fetcher_map_.erase(device_data.label());
265 }
266
267 CachedParsedDialDeviceDescription::CachedParsedDialDeviceDescription() =
268 default;
269 CachedParsedDialDeviceDescription::CachedParsedDialDeviceDescription(
270 const CachedParsedDialDeviceDescription& other) = default;
271 CachedParsedDialDeviceDescription::~CachedParsedDialDeviceDescription() =
272 default;
273
274 } // namespace media_router
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698