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

Side by Side Diff: chrome/browser/supervised_user/experimental/supervised_user_async_url_checker.cc

Issue 2399823002: Extract the SafeSearch client to a separate directory (Closed)
Patch Set: Renamed to safe_search_api Created 4 years, 2 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 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.
4
5 #include "chrome/browser/supervised_user/experimental/supervised_user_async_url_ checker.h"
6
7 #include <string>
8 #include <utility>
9
10 #include "base/callback.h"
11 #include "base/json/json_reader.h"
12 #include "base/metrics/histogram_macros.h"
13 #include "base/stl_util.h"
14 #include "base/strings/string_piece.h"
15 #include "base/strings/string_util.h"
16 #include "base/strings/stringprintf.h"
17 #include "base/time/time.h"
18 #include "base/values.h"
19 #include "components/google/core/browser/google_util.h"
20 #include "google_apis/google_api_keys.h"
21 #include "net/base/escape.h"
22 #include "net/base/load_flags.h"
23 #include "net/url_request/url_fetcher.h"
24 #include "net/url_request/url_request_context.h"
25 #include "url/url_constants.h"
26
27 using net::URLFetcher;
28 using net::URLFetcherDelegate;
29 using net::URLRequestContextGetter;
30 using net::URLRequestStatus;
31
32 namespace {
33
34 const char kApiUrl[] = "https://safesearch.googleapis.com/v1:classify";
35 const char kDataContentType[] = "application/x-www-form-urlencoded";
36 const char kDataFormat[] = "key=%s&urls=%s";
37
38 const size_t kDefaultCacheSize = 1000;
39 const size_t kDefaultCacheTimeoutSeconds = 3600;
40
41 // Builds the POST data for SafeSearch API requests.
42 std::string BuildRequestData(const std::string& api_key, const GURL& url) {
43 std::string query = net::EscapeQueryParamValue(url.spec(), true);
44 return base::StringPrintf(kDataFormat, api_key.c_str(), query.c_str());
45 }
46
47 // Creates a URLFetcher to call the SafeSearch API for |url|.
48 std::unique_ptr<net::URLFetcher> CreateFetcher(URLFetcherDelegate* delegate,
49 URLRequestContextGetter* context,
50 const std::string& api_key,
51 const GURL& url) {
52 std::unique_ptr<net::URLFetcher> fetcher =
53 URLFetcher::Create(0, GURL(kApiUrl), URLFetcher::POST, delegate);
54 fetcher->SetUploadData(kDataContentType, BuildRequestData(api_key, url));
55 fetcher->SetRequestContext(context);
56 fetcher->SetLoadFlags(net::LOAD_DO_NOT_SEND_COOKIES |
57 net::LOAD_DO_NOT_SAVE_COOKIES);
58 return fetcher;
59 }
60
61 // Parses a SafeSearch API |response| and stores the result in |is_porn|.
62 // On errors, returns false and doesn't set |is_porn|.
63 bool ParseResponse(const std::string& response, bool* is_porn) {
64 std::unique_ptr<base::Value> value = base::JSONReader::Read(response);
65 const base::DictionaryValue* dict = nullptr;
66 if (!value || !value->GetAsDictionary(&dict)) {
67 DLOG(WARNING) << "ParseResponse failed to parse global dictionary";
68 return false;
69 }
70 const base::ListValue* classifications_list = nullptr;
71 if (!dict->GetList("classifications", &classifications_list)) {
72 DLOG(WARNING) << "ParseResponse failed to parse classifications list";
73 return false;
74 }
75 if (classifications_list->GetSize() != 1) {
76 DLOG(WARNING) << "ParseResponse expected exactly one result";
77 return false;
78 }
79 const base::DictionaryValue* classification_dict = nullptr;
80 if (!classifications_list->GetDictionary(0, &classification_dict)) {
81 DLOG(WARNING) << "ParseResponse failed to parse classification dict";
82 return false;
83 }
84 classification_dict->GetBoolean("pornography", is_porn);
85 return true;
86 }
87
88 } // namespace
89
90 struct SupervisedUserAsyncURLChecker::Check {
91 Check(const GURL& url,
92 std::unique_ptr<net::URLFetcher> fetcher,
93 const CheckCallback& callback);
94 ~Check();
95
96 GURL url;
97 std::unique_ptr<net::URLFetcher> fetcher;
98 std::vector<CheckCallback> callbacks;
99 base::TimeTicks start_time;
100 };
101
102 SupervisedUserAsyncURLChecker::Check::Check(
103 const GURL& url,
104 std::unique_ptr<net::URLFetcher> fetcher,
105 const CheckCallback& callback)
106 : url(url),
107 fetcher(std::move(fetcher)),
108 callbacks(1, callback),
109 start_time(base::TimeTicks::Now()) {}
110
111 SupervisedUserAsyncURLChecker::Check::~Check() {}
112
113 SupervisedUserAsyncURLChecker::CheckResult::CheckResult(
114 SupervisedUserURLFilter::FilteringBehavior behavior,
115 bool uncertain)
116 : behavior(behavior),
117 uncertain(uncertain),
118 timestamp(base::TimeTicks::Now()) {}
119
120 SupervisedUserAsyncURLChecker::SupervisedUserAsyncURLChecker(
121 URLRequestContextGetter* context)
122 : SupervisedUserAsyncURLChecker(context, kDefaultCacheSize) {}
123
124 SupervisedUserAsyncURLChecker::SupervisedUserAsyncURLChecker(
125 URLRequestContextGetter* context,
126 size_t cache_size)
127 : context_(context),
128 cache_(cache_size),
129 cache_timeout_(
130 base::TimeDelta::FromSeconds(kDefaultCacheTimeoutSeconds)) {}
131
132 SupervisedUserAsyncURLChecker::~SupervisedUserAsyncURLChecker() {}
133
134 bool SupervisedUserAsyncURLChecker::CheckURL(const GURL& url,
135 const CheckCallback& callback) {
136 // TODO(treib): Hack: For now, allow all Google URLs to save QPS. If we ever
137 // remove this, we should find a way to allow at least the NTP.
138 if (google_util::IsGoogleDomainUrl(url,
139 google_util::ALLOW_SUBDOMAIN,
140 google_util::ALLOW_NON_STANDARD_PORTS)) {
141 callback.Run(url, SupervisedUserURLFilter::ALLOW, false);
142 return true;
143 }
144 // TODO(treib): Hack: For now, allow all YouTube URLs since YouTube has its
145 // own Safety Mode anyway.
146 if (google_util::IsYoutubeDomainUrl(url,
147 google_util::ALLOW_SUBDOMAIN,
148 google_util::ALLOW_NON_STANDARD_PORTS)) {
149 callback.Run(url, SupervisedUserURLFilter::ALLOW, false);
150 return true;
151 }
152
153 auto cache_it = cache_.Get(url);
154 if (cache_it != cache_.end()) {
155 const CheckResult& result = cache_it->second;
156 base::TimeDelta age = base::TimeTicks::Now() - result.timestamp;
157 if (age < cache_timeout_) {
158 DVLOG(1) << "Cache hit! " << url.spec() << " is "
159 << (result.behavior == SupervisedUserURLFilter::BLOCK ? "NOT"
160 : "")
161 << " safe; certain: " << !result.uncertain;
162 callback.Run(url, result.behavior, result.uncertain);
163 return true;
164 }
165 DVLOG(1) << "Outdated cache entry for " << url.spec() << ", purging";
166 cache_.Erase(cache_it);
167 }
168
169 // See if we already have a check in progress for this URL.
170 for (Check* check : checks_in_progress_) {
171 if (check->url == url) {
172 DVLOG(1) << "Adding to pending check for " << url.spec();
173 check->callbacks.push_back(callback);
174 return false;
175 }
176 }
177
178 DVLOG(1) << "Checking URL " << url;
179 std::string api_key = google_apis::GetAPIKey();
180 std::unique_ptr<URLFetcher> fetcher(
181 CreateFetcher(this, context_, api_key, url));
182 fetcher->Start();
183 checks_in_progress_.push_back(new Check(url, std::move(fetcher), callback));
184 return false;
185 }
186
187 void SupervisedUserAsyncURLChecker::OnURLFetchComplete(
188 const net::URLFetcher* source) {
189 ScopedVector<Check>::iterator it = checks_in_progress_.begin();
190 while (it != checks_in_progress_.end()) {
191 if (source == (*it)->fetcher.get())
192 break;
193 ++it;
194 }
195 DCHECK(it != checks_in_progress_.end());
196 Check* check = *it;
197
198 const URLRequestStatus& status = source->GetStatus();
199 if (!status.is_success()) {
200 DLOG(WARNING) << "URL request failed! Letting through...";
201 for (size_t i = 0; i < check->callbacks.size(); i++)
202 check->callbacks[i].Run(check->url, SupervisedUserURLFilter::ALLOW, true);
203 checks_in_progress_.erase(it);
204 return;
205 }
206
207 std::string response_body;
208 source->GetResponseAsString(&response_body);
209 bool is_porn = false;
210 bool uncertain = !ParseResponse(response_body, &is_porn);
211 SupervisedUserURLFilter::FilteringBehavior behavior =
212 is_porn ? SupervisedUserURLFilter::BLOCK : SupervisedUserURLFilter::ALLOW;
213
214 UMA_HISTOGRAM_TIMES("ManagedUsers.SafeSitesDelay",
215 base::TimeTicks::Now() - check->start_time);
216
217 cache_.Put(check->url, CheckResult(behavior, uncertain));
218
219 for (size_t i = 0; i < check->callbacks.size(); i++)
220 check->callbacks[i].Run(check->url, behavior, uncertain);
221 checks_in_progress_.erase(it);
222 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698