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

Side by Side Diff: components/safe_browsing_db/v4_update_protocol_manager.cc

Issue 1727033003: v4_update_protocol_manager: Basic implementation with TODOs (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Fix comment. Created 4 years, 9 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 2016 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 "components/safe_browsing_db/v4_update_protocol_manager.h"
6
7 #include <utility>
8
9 #include "base/base64.h"
10 #include "base/macros.h"
11 #include "base/metrics/histogram_macros.h"
12 #include "base/timer/timer.h"
13 #include "components/safe_browsing_db/safebrowsing.pb.h"
14 #include "net/base/load_flags.h"
15 #include "net/http/http_response_headers.h"
16 #include "net/http/http_status_code.h"
17 #include "net/url_request/url_fetcher.h"
18 #include "net/url_request/url_request_context_getter.h"
19
20 using base::Time;
21 using base::TimeDelta;
22
23 namespace {
24
25 // Enumerate parsing failures for histogramming purposes. DO NOT CHANGE
26 // THE ORDERING OF THESE VALUES.
27 enum ParseResultType {
28 // Error parsing the protocol buffer from a string.
29 PARSE_FROM_STRING_ERROR = 0,
30
31 // No platform_type set in the response.
32 NO_PLATFORM_TYPE_ERROR = 1,
33
34 // No threat_entry_type set in the response.
35 NO_THREAT_ENTRY_TYPE_ERROR = 2,
36
37 // No threat_type set in the response.
38 NO_THREAT_TYPE_ERROR = 3,
39
40 // No state set in the response for one or more lists.
41 NO_STATE_ERROR = 4,
42
43 // Memory space for histograms is determined by the max. ALWAYS
44 // ADD NEW VALUES BEFORE THIS ONE.
45 PARSE_RESULT_TYPE_MAX = 5
46 };
47
48 // Record parsing errors of an update result.
49 void RecordParseUpdateResult(ParseResultType result_type) {
50 UMA_HISTOGRAM_ENUMERATION("SafeBrowsing.ParseV4UpdateResult", result_type,
51 PARSE_RESULT_TYPE_MAX);
52 }
53
54 } // namespace
55
56 namespace safe_browsing {
57
58 const char kUmaV4UpdateResponseMetricName[] =
Nathan Parker 2016/03/22 23:57:55 It's pretty standard to just put the metric name's
vakh (use Gerrit instead) 2016/03/24 22:34:24 Done.
59 "SafeBrowsing.V4UpdateHttpResponseOrErrorCode";
60
61 // The default V4UpdateProtocolManagerFactory.
62 class V4UpdateProtocolManagerFactoryImpl
63 : public V4UpdateProtocolManagerFactory {
64 public:
65 V4UpdateProtocolManagerFactoryImpl() {}
66 ~V4UpdateProtocolManagerFactoryImpl() override {}
67 V4UpdateProtocolManager* CreateProtocolManager(
68 net::URLRequestContextGetter* request_context_getter,
69 const V4ProtocolConfig& config) override {
70 return new V4UpdateProtocolManager(request_context_getter, config);
71 }
72
73 private:
74 DISALLOW_COPY_AND_ASSIGN(V4UpdateProtocolManagerFactoryImpl);
75 };
76
77 // V4UpdateProtocolManager implementation --------------------------------
78
79 // static
80 V4UpdateProtocolManagerFactory* V4UpdateProtocolManager::factory_ = NULL;
81
82 // static
83 V4UpdateProtocolManager* V4UpdateProtocolManager::Create(
84 net::URLRequestContextGetter* request_context_getter,
85 const V4ProtocolConfig& config) {
86 if (!factory_)
87 factory_ = new V4UpdateProtocolManagerFactoryImpl();
Nathan Parker 2016/03/22 23:57:55 Do you need this Impl class at all? Another way w
vakh (use Gerrit instead) 2016/03/24 22:34:24 This would be useful for testing using DI. The old
88 return factory_->CreateProtocolManager(request_context_getter, config);
89 }
90
91 void V4UpdateProtocolManager::ResetUpdateErrors() {
92 update_error_count_ = 0;
93 update_back_off_mult_ = 1;
94 }
95
96 V4UpdateProtocolManager::V4UpdateProtocolManager(
97 net::URLRequestContextGetter* request_context_getter,
98 const V4ProtocolConfig& config)
99 : update_error_count_(0),
100 update_back_off_mult_(1),
101 next_update_time_(Time::FromDoubleT(0)),
102 config_(config),
103 request_context_getter_(request_context_getter),
104 url_fetcher_id_(0),
105 update_request_pending_(false) {}
106
107 // static
108 void V4UpdateProtocolManager::RecordUpdateResult(
109 OperationResultType result_type) {
110 UMA_HISTOGRAM_ENUMERATION("SafeBrowsing.V4UpdateResult", result_type,
111 V4ProtocolManagerUtil::OPERATION_RESULT_TYPE_MAX);
112 }
113
114 V4UpdateProtocolManager::~V4UpdateProtocolManager() {
115 }
116
117 std::string V4UpdateProtocolManager::GetUpdateRequest(
118 const base::hash_set<const UpdateListIdentifier*>& lists_to_update,
119 const base::hash_map<const UpdateListIdentifier*, const std::string&>&
120 current_list_states) {
121 // Build the request. Client info and client states are not added to the
122 // request protocol buffer. Client info is passed as params in the url.
123 FetchThreatListUpdatesRequest request;
124 for (const UpdateListIdentifier* list_to_update : lists_to_update) {
125 ListUpdateRequest* list_update_request = request.add_list_update_requests();
126 list_update_request->set_platform_type(list_to_update->platform_type);
127 list_update_request->set_threat_entry_type(
128 list_to_update->threat_entry_type);
129 list_update_request->set_threat_type(list_to_update->threat_type);
130
131 // If the current state of the list is available, add that to the proto.
132 base::hash_map<const UpdateListIdentifier*,
133 const std::string&>::const_iterator list_iter =
134 current_list_states.find(list_to_update);
135 if (list_iter != current_list_states.end()) {
136 list_update_request->set_state(list_iter->second);
137 }
138 }
139
140 // Serialize and Base64 encode.
141 std::string req_data, req_base64;
142 request.SerializeToString(&req_data);
143 base::Base64Encode(req_data, &req_base64);
144
145 return req_base64;
146 }
147
148 bool V4UpdateProtocolManager::ParseUpdateResponse(
149 const std::string& data,
150 std::vector<ListUpdateResponse>* list_update_responses) {
151 FetchThreatListUpdatesResponse response;
152
153 if (!response.ParseFromString(data)) {
154 RecordParseUpdateResult(PARSE_FROM_STRING_ERROR);
155 return false;
156 }
157
158 if (response.has_minimum_wait_duration()) {
159 // Seconds resolution is good enough so we ignore the nanos field.
160 next_update_time_ =
Nathan Parker 2016/03/22 23:57:55 If it doesn't have a min wait duration, does next_
vakh (use Gerrit instead) 2016/03/24 22:34:24 Yes, we don't update it if the response doesn't co
Nathan Parker 2016/03/24 22:48:05 ok, so then assumption is that the caller to GetUp
vakh (use Gerrit instead) 2016/03/25 02:42:37 Not really. The class stores next_update_time_ whi
161 Time::Now() + base::TimeDelta::FromSeconds(
162 response.minimum_wait_duration().seconds());
163 }
164
165 // TODO(vakh): Do something useful with this response.
166 for (const ListUpdateResponse& list_update_response :
167 response.list_update_responses()) {
168 if (!list_update_response.has_platform_type()) {
169 RecordParseUpdateResult(NO_PLATFORM_TYPE_ERROR);
170 } else if (!list_update_response.has_threat_entry_type()) {
171 RecordParseUpdateResult(NO_THREAT_ENTRY_TYPE_ERROR);
172 } else if (!list_update_response.has_threat_type()) {
173 RecordParseUpdateResult(NO_THREAT_TYPE_ERROR);
174 } else if (!list_update_response.has_new_client_state()) {
175 RecordParseUpdateResult(NO_STATE_ERROR);
176 } else {
177 list_update_responses->push_back(list_update_response);
178 }
179 }
180 return true;
181 }
182
183 void V4UpdateProtocolManager::GetUpdates(
184 const base::hash_set<const UpdateListIdentifier*>& lists_to_update,
185 const base::hash_map<const UpdateListIdentifier*, const std::string&>&
186 current_list_states,
187 UpdateCallback callback) {
188 DCHECK(CalledOnValidThread());
189
190 // If an update request is already pending, return an empty result.
191 if (update_request_pending_) {
192 RecordUpdateResult(V4ProtocolManagerUtil::ALREADY_PENDING_ERROR);
193 std::vector<ListUpdateResponse> list_update_responses;
194 callback.Run(list_update_responses);
195 return;
196 }
197
198 // We need to wait the minimum waiting duration, and if we are in backoff,
199 // we need to check if we're past the next allowed time. If we are, we can
200 // proceed with the request. If not, we are required to return empty results.
201 if (Time::Now() <= next_update_time_) {
202 if (update_error_count_) {
203 RecordUpdateResult(V4ProtocolManagerUtil::BACKOFF_ERROR);
204 } else {
205 RecordUpdateResult(V4ProtocolManagerUtil::MIN_WAIT_DURATION_ERROR);
206 }
207 std::vector<ListUpdateResponse> list_update_responses;
208 callback.Run(list_update_responses);
209 return;
210 }
211
212 std::string req_base64 =
213 GetUpdateRequest(lists_to_update, current_list_states);
214 GURL update_url = GetUpdateUrl(req_base64);
215
216 net::URLFetcher* fetcher =
Nathan Parker 2016/03/22 23:57:55 nit: You could put this directly into fetcher_
vakh (use Gerrit instead) 2016/03/24 22:34:24 Done.
217 net::URLFetcher::Create(url_fetcher_id_++, update_url,
218 net::URLFetcher::GET, this)
219 .release();
220 update_request_pending_ = true;
221 callback_ = callback;
222 request_.reset(fetcher);
223
224 fetcher->SetLoadFlags(net::LOAD_DISABLE_CACHE);
225 fetcher->SetRequestContext(request_context_getter_.get());
226 fetcher->Start();
227 }
228
229 // net::URLFetcherDelegate implementation ----------------------------------
230
231 // SafeBrowsing request responses are handled here.
232 void V4UpdateProtocolManager::OnURLFetchComplete(
233 const net::URLFetcher* source) {
234 DCHECK(CalledOnValidThread());
235
236 int response_code = source->GetResponseCode();
237 net::URLRequestStatus status = source->GetStatus();
238 V4ProtocolManagerUtil::RecordHttpResponseOrErrorCode(
239 kUmaV4UpdateResponseMetricName, status, response_code);
240
241 std::vector<ListUpdateResponse> list_update_responses;
242 if (status.is_success() && response_code == net::HTTP_OK) {
243 RecordUpdateResult(V4ProtocolManagerUtil::STATUS_200);
244 ResetUpdateErrors();
245 std::string data;
246 source->GetResponseAsString(&data);
247 if (!ParseUpdateResponse(data, &list_update_responses)) {
248 list_update_responses.clear();
249 RecordUpdateResult(V4ProtocolManagerUtil::PARSE_ERROR);
250 }
251 } else {
252 HandleUpdateError(Time::Now());
253
254 DVLOG(1) << "SafeBrowsing GetEncodedUpdates request for: "
255 << source->GetURL() << " failed with error: " << status.error()
256 << " and response code: " << response_code;
257
258 if (status.status() == net::URLRequestStatus::FAILED) {
259 RecordUpdateResult(V4ProtocolManagerUtil::NETWORK_ERROR);
260 } else {
261 RecordUpdateResult(V4ProtocolManagerUtil::HTTP_ERROR);
262 }
263 }
264
265 // Invoke the callback with list_update_responses, even if there was a parse
266 // error or an error response code (in which case list_update_responses will
267 // be empty). The caller can't be blocked indefinitely.
268 callback_.Run(list_update_responses);
269
270 update_request_pending_ = false;
271 }
272
273 void V4UpdateProtocolManager::HandleUpdateError(const Time& now) {
274 DCHECK(CalledOnValidThread());
275 base::TimeDelta next = V4ProtocolManagerUtil::GetNextBackOffInterval(
276 &update_error_count_, &update_back_off_mult_);
277 next_update_time_ = now + next;
278 }
279
280 GURL V4UpdateProtocolManager::GetUpdateUrl(
281 const std::string& req_base64) const {
282 return V4ProtocolManagerUtil::GetRequestUrl(req_base64, "encodedUpdates",
283 config_);
284 }
285
286 } // namespace safe_browsing
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698