Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "chrome/browser/safe_browsing/download_protection_service.h" | 5 #include "chrome/browser/safe_browsing/download_protection_service.h" |
| 6 | 6 |
| 7 #include "base/bind.h" | 7 #include "base/bind.h" |
| 8 #include "base/memory/scoped_ptr.h" | 8 #include "base/memory/scoped_ptr.h" |
| 9 #include "base/metrics/histogram.h" | 9 #include "base/metrics/histogram.h" |
| 10 #include "base/stl_util.h" | 10 #include "base/stl_util.h" |
| 11 #include "base/string_util.h" | |
| 11 #include "chrome/browser/safe_browsing/safe_browsing_service.h" | 12 #include "chrome/browser/safe_browsing/safe_browsing_service.h" |
| 13 #include "chrome/browser/safe_browsing/signature_util.h" | |
| 12 #include "chrome/common/net/http_return.h" | 14 #include "chrome/common/net/http_return.h" |
| 13 #include "chrome/common/safe_browsing/csd.pb.h" | 15 #include "chrome/common/safe_browsing/csd.pb.h" |
| 14 #include "content/browser/browser_thread.h" | 16 #include "content/browser/browser_thread.h" |
| 17 #include "content/browser/download/download_item.h" | |
| 18 #include "content/public/common/url_fetcher_delegate.h" | |
| 15 #include "content/common/net/url_fetcher.h" | 19 #include "content/common/net/url_fetcher.h" |
| 16 #include "net/base/load_flags.h" | 20 #include "net/base/load_flags.h" |
| 17 #include "net/url_request/url_request_context_getter.h" | 21 #include "net/url_request/url_request_context_getter.h" |
| 18 #include "net/url_request/url_request_status.h" | 22 #include "net/url_request/url_request_status.h" |
| 19 | 23 |
| 20 namespace safe_browsing { | 24 namespace safe_browsing { |
| 21 | 25 |
| 22 const char DownloadProtectionService::kDownloadRequestUrl[] = | 26 const char DownloadProtectionService::kDownloadRequestUrl[] = |
| 23 "https://sb-ssl.google.com/safebrowsing/clientreport/download"; | 27 "https://sb-ssl.google.com/safebrowsing/clientreport/download"; |
| 24 | 28 |
| 29 namespace { | |
| 30 bool IsBinaryFile(const FilePath& file) { | |
| 31 return (file.MatchesExtension(FILE_PATH_LITERAL(".exe")) || | |
| 32 file.MatchesExtension(FILE_PATH_LITERAL(".cab")) || | |
| 33 file.MatchesExtension(FILE_PATH_LITERAL(".msi"))); | |
| 34 } | |
| 35 } // namespace | |
| 36 | |
| 25 DownloadProtectionService::DownloadInfo::DownloadInfo() | 37 DownloadProtectionService::DownloadInfo::DownloadInfo() |
| 26 : total_bytes(0), user_initiated(false) {} | 38 : total_bytes(0), user_initiated(false) {} |
| 27 | 39 |
| 28 DownloadProtectionService::DownloadInfo::~DownloadInfo() {} | 40 DownloadProtectionService::DownloadInfo::~DownloadInfo() {} |
| 29 | 41 |
| 30 DownloadProtectionService::DownloadProtectionService( | 42 // static |
| 31 SafeBrowsingService* sb_service, | 43 DownloadProtectionService::DownloadInfo |
| 32 net::URLRequestContextGetter* request_context_getter) | 44 DownloadProtectionService::DownloadInfo::FromDownloadItem( |
| 33 : sb_service_(sb_service), | 45 const DownloadItem& item) { |
| 34 request_context_getter_(request_context_getter), | 46 DownloadInfo download_info; |
| 35 enabled_(false) {} | 47 download_info.local_file = item.full_path(); |
| 36 | 48 download_info.download_url_chain = item.url_chain(); |
| 37 DownloadProtectionService::~DownloadProtectionService() { | 49 download_info.referrer_url = item.referrer_url(); |
| 38 STLDeleteContainerPairFirstPointers(download_requests_.begin(), | 50 // TODO(bryner): Fill in the hash (we shouldn't compute it again) |
| 39 download_requests_.end()); | 51 download_info.total_bytes = item.total_bytes(); |
| 40 download_requests_.clear(); | 52 // TODO(bryner): Populate user_initiated |
| 53 return download_info; | |
| 41 } | 54 } |
| 42 | 55 |
| 43 void DownloadProtectionService::SetEnabled(bool enabled) { | 56 class DownloadProtectionService::CheckClientDownloadRequest |
| 44 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | 57 : public base::RefCountedThreadSafe< |
| 45 BrowserThread::PostTask( | 58 DownloadProtectionService::CheckClientDownloadRequest, |
| 46 BrowserThread::IO, | 59 BrowserThread::DeleteOnUIThread>, |
| 47 FROM_HERE, | 60 public content::URLFetcherDelegate { |
| 48 base::Bind(&DownloadProtectionService::SetEnabledOnIOThread, | 61 public: |
| 49 this, enabled)); | 62 CheckClientDownloadRequest(const DownloadInfo& info, |
| 50 } | 63 const CheckDownloadCallback& callback, |
| 64 DownloadProtectionService* service, | |
| 65 SafeBrowsingService* sb_service) | |
| 66 : info_(info), | |
| 67 callback_(callback), | |
| 68 service_(service), | |
| 69 sb_service_(sb_service), | |
| 70 pingback_enabled_(service_->enabled()), | |
| 71 canceled_(false) { | |
| 72 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
| 73 } | |
| 51 | 74 |
| 52 void DownloadProtectionService::SetEnabledOnIOThread(bool enabled) { | 75 void Start() { |
| 53 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); | 76 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| 54 if (enabled == enabled_) { | 77 // TODO(noelutz): implement some cache to make sure we don't issue the same |
| 55 return; | 78 // request over and over again if a user downloads the same binary multiple |
| 56 } | 79 // times. |
| 57 enabled_ = enabled; | 80 if (info_.download_url_chain.empty()) { |
| 58 if (!enabled_) { | 81 RecordStats(REASON_INVALID_URL); |
| 59 for (std::map<const URLFetcher*, CheckDownloadCallback>::iterator it = | 82 PostFinishTask(SAFE); |
| 60 download_requests_.begin(); | |
| 61 it != download_requests_.end(); ++it) { | |
| 62 it->second.Run(SAFE); | |
| 63 } | |
| 64 STLDeleteContainerPairFirstPointers(download_requests_.begin(), | |
| 65 download_requests_.end()); | |
| 66 download_requests_.clear(); | |
| 67 } | |
| 68 } | |
| 69 | |
| 70 void DownloadProtectionService::OnURLFetchComplete(const URLFetcher* source) { | |
| 71 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); | |
| 72 scoped_ptr<const URLFetcher> s(source); // will delete the URLFetcher object. | |
| 73 if (download_requests_.find(source) != download_requests_.end()) { | |
| 74 CheckDownloadCallback callback = download_requests_[source]; | |
| 75 download_requests_.erase(source); | |
| 76 if (!enabled_) { | |
| 77 // SafeBrowsing got disabled. We can't do anything. Note: the request | |
| 78 // object will be deleted. | |
| 79 RecordStats(REASON_SB_DISABLED); | |
| 80 return; | 83 return; |
| 81 } | 84 } |
| 85 const GURL& final_url = info_.download_url_chain.back(); | |
| 86 if (!final_url.is_valid() || final_url.is_empty() || | |
| 87 !final_url.SchemeIs("http")) { | |
| 88 RecordStats(REASON_INVALID_URL); | |
| 89 PostFinishTask(SAFE); | |
| 90 return; // For now we only support HTTP download URLs. | |
| 91 } | |
| 92 | |
| 93 if (!IsBinaryFile(info_.local_file)) { | |
| 94 RecordStats(REASON_NOT_BINARY_FILE); | |
| 95 PostFinishTask(SAFE); | |
| 96 return; | |
| 97 } | |
| 98 | |
| 99 // Compute features from the file contents. Note that we record histograms | |
| 100 // based on the result, so this runs regardless of whether the pingbacks | |
| 101 // are enabled. Since we do blocking I/O, this happens on the file thread. | |
| 102 BrowserThread::PostTask( | |
| 103 BrowserThread::FILE, | |
| 104 FROM_HERE, | |
| 105 base::Bind(&CheckClientDownloadRequest::ExtractFileFeatures, this)); | |
| 106 } | |
| 107 | |
| 108 // Canceling a request will cause us to always report the result as SAFE. | |
| 109 void Cancel() { | |
| 110 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
| 111 canceled_ = true; | |
| 112 } | |
| 113 | |
| 114 // From the content::URLFetcher interface. | |
| 115 virtual void OnURLFetchComplete(const URLFetcher* source) OVERRIDE { | |
| 116 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
| 117 DCHECK_EQ(source, fetcher_.get()); | |
| 118 VLOG(2) << "Received a response for URL: " | |
| 119 << info_.download_url_chain.back() << ": success=" | |
| 120 << source->status().is_success() << " response_code=" | |
| 121 << source->response_code(); | |
| 82 DownloadCheckResultReason reason = REASON_MAX; | 122 DownloadCheckResultReason reason = REASON_MAX; |
| 83 reason = REASON_SERVER_PING_FAILED; | 123 reason = REASON_SERVER_PING_FAILED; |
| 84 if (source->status().is_success() && | 124 if (source->status().is_success() && |
| 85 RC_REQUEST_OK == source->response_code()) { | 125 RC_REQUEST_OK == source->response_code()) { |
| 86 std::string data; | 126 std::string data; |
| 87 source->GetResponseAsString(&data); | 127 source->GetResponseAsString(&data); |
| 88 if (data.size() > 0) { | 128 if (data.size() > 0) { |
| 89 // For now no matter what we'll always say the download is safe. | 129 // For now no matter what we'll always say the download is safe. |
| 90 // TODO(noelutz): Parse the response body to see exactly what's going | 130 // TODO(noelutz): Parse the response body to see exactly what's going |
| 91 // on. | 131 // on. |
| 92 reason = REASON_INVALID_RESPONSE_PROTO; | 132 reason = REASON_INVALID_RESPONSE_PROTO; |
| 93 } | 133 } |
| 94 } | 134 } |
| 135 | |
| 136 if (reason != REASON_MAX) { | |
| 137 RecordStats(reason); | |
| 138 } | |
| 139 FinishRequest(SAFE); | |
| 140 } | |
| 141 | |
| 142 private: | |
| 143 friend struct BrowserThread::DeleteOnThread<BrowserThread::UI>; | |
| 144 friend class DeleteTask<CheckClientDownloadRequest>; | |
| 145 | |
| 146 virtual ~CheckClientDownloadRequest() { | |
| 147 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
| 148 } | |
| 149 | |
| 150 void ExtractFileFeatures() { | |
| 151 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); | |
| 152 bool is_signed; | |
| 153 if (safe_browsing::signature_util::IsSigned(info_.local_file)) { | |
| 154 VLOG(2) << "Downloaded a signed binary: " << info_.local_file.value(); | |
| 155 is_signed = true; | |
| 156 } else { | |
| 157 VLOG(2) << "Downloaded an unsigned binary: " << info_.local_file.value(); | |
| 158 is_signed = false; | |
| 159 } | |
| 160 UMA_HISTOGRAM_BOOLEAN("SBClientDownload.SignedBinaryDownload", is_signed); | |
| 161 | |
| 162 // TODO(noelutz): DownloadInfo should also contain the IP address of every | |
| 163 // URL in the redirect chain. We also should check whether the download | |
| 164 // URL is hosted on the internal network. | |
| 165 BrowserThread::PostTask( | |
| 166 BrowserThread::IO, | |
| 167 FROM_HERE, | |
| 168 base::Bind(&CheckClientDownloadRequest::CheckWhitelists, this)); | |
| 169 } | |
| 170 | |
| 171 void CheckWhitelists() { | |
| 172 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); | |
| 173 DownloadCheckResultReason reason = REASON_MAX; | |
| 174 if (!pingback_enabled_ || !sb_service_.get()) { | |
| 175 reason = REASON_SB_DISABLED; | |
| 176 } else { | |
| 177 for (size_t i = 0; i < info_.download_url_chain.size(); ++i) { | |
| 178 const GURL& url = info_.download_url_chain[i]; | |
| 179 if (url.is_valid() && sb_service_->MatchDownloadWhitelistUrl(url)) { | |
| 180 reason = REASON_WHITELISTED_URL; | |
| 181 break; | |
| 182 } | |
| 183 } | |
| 184 if (info_.referrer_url.is_valid() && | |
| 185 sb_service_->MatchDownloadWhitelistUrl(info_.referrer_url)) { | |
| 186 reason = REASON_WHITELISTED_REFERRER; | |
| 187 } | |
| 188 } | |
| 189 if (reason != REASON_MAX) { | |
| 190 RecordStats(reason); | |
| 191 PostFinishTask(SAFE); | |
| 192 return; | |
| 193 } | |
| 194 | |
| 195 // TODO(noelutz): check signature and CA against whitelist. | |
| 196 | |
| 197 // The URLFetcher is owned by the UI thread, so post a message to | |
| 198 // start the pigback. | |
| 95 BrowserThread::PostTask( | 199 BrowserThread::PostTask( |
| 96 BrowserThread::UI, | 200 BrowserThread::UI, |
| 97 FROM_HERE, | 201 FROM_HERE, |
| 98 base::Bind(&DownloadProtectionService::EndCheckClientDownload, | 202 base::Bind(&CheckClientDownloadRequest::SendRequest, this)); |
| 99 this, SAFE, reason, callback)); | 203 } |
| 100 } else { | 204 |
| 101 NOTREACHED(); | 205 void SendRequest() { |
| 206 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
| 207 ClientDownloadRequest request; | |
| 208 request.set_url(info_.download_url_chain.back().spec()); | |
| 209 request.mutable_digests()->set_sha256(info_.sha256_hash); | |
| 210 request.set_length(info_.total_bytes); | |
| 211 for (size_t i = 0; i < info_.download_url_chain.size(); ++i) { | |
| 212 ClientDownloadRequest::Resource* resource = request.add_resources(); | |
| 213 resource->set_url(info_.download_url_chain[i].spec()); | |
| 214 if (i == info_.download_url_chain.size() - 1) { | |
| 215 // The last URL in the chain is the download URL. | |
| 216 resource->set_type(ClientDownloadRequest::DOWNLOAD_URL); | |
| 217 resource->set_referrer(info_.referrer_url.spec()); | |
| 218 } else { | |
| 219 resource->set_type(ClientDownloadRequest::DOWNLOAD_REDIRECT); | |
| 220 } | |
| 221 // TODO(noelutz): fill out the remote IP addresses. | |
| 222 } | |
| 223 request.set_user_initiated(info_.user_initiated); | |
| 224 std::string request_data; | |
| 225 if (!request.SerializeToString(&request_data)) { | |
| 226 RecordStats(REASON_INVALID_REQUEST_PROTO); | |
| 227 FinishRequest(SAFE); | |
| 228 return; | |
| 229 } | |
| 230 | |
| 231 VLOG(2) << "Sending a request for URL: " | |
| 232 << info_.download_url_chain.back(); | |
| 233 fetcher_.reset(URLFetcher::Create(0 /* ID used for testing */, | |
| 234 GURL(kDownloadRequestUrl), | |
| 235 URLFetcher::POST, | |
| 236 this)); | |
| 237 fetcher_->set_load_flags(net::LOAD_DISABLE_CACHE); | |
| 238 fetcher_->set_request_context(service_->request_context_getter_.get()); | |
|
noelutz
2011/10/25 21:43:50
Check if the request is cancelled before accessing
Brian Ryner
2011/10/25 22:03:07
Done.
| |
| 239 fetcher_->set_upload_data("application/octet-stream", request_data); | |
| 240 fetcher_->Start(); | |
| 241 } | |
| 242 | |
| 243 void PostFinishTask(DownloadCheckResult result) { | |
| 244 BrowserThread::PostTask( | |
| 245 BrowserThread::UI, | |
| 246 FROM_HERE, | |
| 247 base::Bind(&CheckClientDownloadRequest::FinishRequest, this, result)); | |
| 248 } | |
| 249 | |
| 250 void FinishRequest(DownloadCheckResult result) { | |
| 251 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
| 252 callback_.Run(canceled_ ? SAFE : result); | |
| 253 service_->RequestFinished(this); | |
|
noelutz
2011/10/25 21:43:50
Same here. I'm pretty sure it's possible for serv
Brian Ryner
2011/10/25 22:03:07
Ah, I guess you mean in the ShutDown() case? Done
noelutz
2011/10/25 22:13:42
Yeah the ShutDown case.
| |
| 254 } | |
| 255 | |
| 256 void RecordStats(DownloadCheckResultReason reason) { | |
| 257 UMA_HISTOGRAM_ENUMERATION("SBClientDownload.CheckDownloadStats", | |
| 258 reason, | |
| 259 REASON_MAX); | |
| 260 } | |
| 261 | |
| 262 DownloadInfo info_; | |
| 263 CheckDownloadCallback callback_; | |
| 264 DownloadProtectionService* service_; | |
| 265 scoped_refptr<SafeBrowsingService> sb_service_; | |
| 266 bool pingback_enabled_; | |
| 267 bool canceled_; | |
| 268 scoped_ptr<URLFetcher> fetcher_; | |
| 269 | |
| 270 DISALLOW_COPY_AND_ASSIGN(CheckClientDownloadRequest); | |
| 271 }; | |
| 272 | |
| 273 DownloadProtectionService::DownloadProtectionService( | |
| 274 SafeBrowsingService* sb_service, | |
| 275 net::URLRequestContextGetter* request_context_getter) | |
| 276 : sb_service_(sb_service), | |
| 277 request_context_getter_(request_context_getter), | |
| 278 enabled_(false) {} | |
| 279 | |
| 280 DownloadProtectionService::~DownloadProtectionService() { | |
| 281 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
| 282 // The SafeBrowsingService owns this class, and any pending request will | |
| 283 // have an outstanding reference to the SafeBrowsingService. Therefore, | |
| 284 // at this point there must not be any active requests. | |
| 285 DCHECK(download_requests_.empty()); | |
|
noelutz
2011/10/25 21:43:50
I think you need to cancel requests here.
Brian Ryner
2011/10/25 22:03:07
So normally if we cancel a request, we still rely
noelutz
2011/10/25 22:13:42
Not necessarily but I believe there is a bug here.
| |
| 286 } | |
| 287 | |
| 288 void DownloadProtectionService::SetEnabled(bool enabled) { | |
| 289 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
| 290 if (enabled == enabled_) { | |
| 291 return; | |
| 292 } | |
| 293 enabled_ = enabled; | |
| 294 if (!enabled_) { | |
| 295 for (std::set<scoped_refptr<CheckClientDownloadRequest> >::iterator it = | |
| 296 download_requests_.begin(); | |
| 297 it != download_requests_.end(); ++it) { | |
| 298 (*it)->Cancel(); | |
| 299 } | |
| 102 } | 300 } |
| 103 } | 301 } |
| 104 | 302 |
| 105 bool DownloadProtectionService::CheckClientDownload( | 303 void DownloadProtectionService::CheckClientDownload( |
| 106 const DownloadInfo& info, | 304 const DownloadProtectionService::DownloadInfo& info, |
| 107 const CheckDownloadCallback& callback) { | 305 const CheckDownloadCallback& callback) { |
| 108 // TODO(noelutz): implement some cache to make sure we don't issue the same | 306 scoped_refptr<CheckClientDownloadRequest> request( |
| 109 // request over and over again if a user downloads the same binary multiple | 307 new CheckClientDownloadRequest(info, callback, this, sb_service_)); |
| 110 // times. | 308 download_requests_.insert(request); |
| 111 if (info.download_url_chain.empty()) { | 309 request->Start(); |
| 112 RecordStats(REASON_INVALID_URL); | |
| 113 return true; | |
| 114 } | |
| 115 const GURL& final_url = info.download_url_chain.back(); | |
| 116 if (!final_url.is_valid() || final_url.is_empty() || | |
| 117 !final_url.SchemeIs("http")) { | |
| 118 RecordStats(REASON_INVALID_URL); | |
| 119 return true; // For now we only support HTTP download URLs. | |
| 120 } | |
| 121 // TODO(noelutz): DownloadInfo should also contain the IP address of every | |
| 122 // URL in the redirect chain. We also should check whether the download URL | |
| 123 // is hosted on the internal network. | |
| 124 BrowserThread::PostTask( | |
| 125 BrowserThread::IO, | |
| 126 FROM_HERE, | |
| 127 base::Bind(&DownloadProtectionService::StartCheckClientDownload, | |
| 128 this, info, callback)); | |
| 129 return false; | |
| 130 } | 310 } |
| 131 | 311 |
| 132 void DownloadProtectionService::StartCheckClientDownload( | 312 void DownloadProtectionService::RequestFinished( |
| 133 const DownloadInfo& info, | 313 CheckClientDownloadRequest* request) { |
| 134 const CheckDownloadCallback& callback) { | 314 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| 135 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); | 315 std::set<scoped_refptr<CheckClientDownloadRequest> >::iterator it = |
| 136 if (!enabled_ || !sb_service_.get()) { | 316 download_requests_.find(request); |
| 137 // This is a hard fail. We won't even call the callback in this case. | 317 DCHECK(it != download_requests_.end()); |
| 138 RecordStats(REASON_SB_DISABLED); | 318 download_requests_.erase(*it); |
| 139 return; | |
| 140 } | |
| 141 DownloadCheckResultReason reason = REASON_MAX; | |
| 142 for (size_t i = 0; i < info.download_url_chain.size(); ++i) { | |
| 143 if (sb_service_->MatchDownloadWhitelistUrl(info.download_url_chain[i])) { | |
| 144 reason = REASON_WHITELISTED_URL; | |
| 145 break; | |
| 146 } | |
| 147 } | |
| 148 if (sb_service_->MatchDownloadWhitelistUrl(info.referrer_url)) { | |
| 149 reason = REASON_WHITELISTED_REFERRER; | |
| 150 } | |
| 151 // TODO(noelutz): check signature and CA against whitelist. | |
| 152 | |
| 153 ClientDownloadRequest request; | |
| 154 request.set_url(info.download_url_chain.back().spec()); | |
| 155 request.mutable_digests()->set_sha256(info.sha256_hash); | |
| 156 request.set_length(info.total_bytes); | |
| 157 for (size_t i = 0; i < info.download_url_chain.size(); ++i) { | |
| 158 ClientDownloadRequest::Resource* resource = request.add_resources(); | |
| 159 resource->set_url(info.download_url_chain[i].spec()); | |
| 160 if (i == info.download_url_chain.size() - 1) { | |
| 161 // The last URL in the chain is the download URL. | |
| 162 resource->set_type(ClientDownloadRequest::DOWNLOAD_URL); | |
| 163 resource->set_referrer(info.referrer_url.spec()); | |
| 164 } else { | |
| 165 resource->set_type(ClientDownloadRequest::DOWNLOAD_REDIRECT); | |
| 166 } | |
| 167 // TODO(noelutz): fill out the remote IP addresses. | |
| 168 } | |
| 169 request.set_user_initiated(info.user_initiated); | |
| 170 std::string request_data; | |
| 171 if (!request.SerializeToString(&request_data)) { | |
| 172 reason = REASON_INVALID_REQUEST_PROTO; | |
| 173 } | |
| 174 | |
| 175 if (reason != REASON_MAX) { | |
| 176 // We stop here because the download is considered safe. | |
| 177 BrowserThread::PostTask( | |
| 178 BrowserThread::UI, | |
| 179 FROM_HERE, | |
| 180 base::Bind(&DownloadProtectionService::EndCheckClientDownload, | |
| 181 this, SAFE, reason, callback)); | |
| 182 return; | |
| 183 } | |
| 184 | |
| 185 URLFetcher* fetcher = URLFetcher::Create(0 /* ID used for testing */, | |
| 186 GURL(kDownloadRequestUrl), | |
| 187 URLFetcher::POST, | |
| 188 this); | |
| 189 download_requests_[fetcher] = callback; | |
| 190 fetcher->set_load_flags(net::LOAD_DISABLE_CACHE); | |
| 191 fetcher->set_request_context(request_context_getter_.get()); | |
| 192 fetcher->set_upload_data("application/octet-stream", request_data); | |
| 193 fetcher->Start(); | |
| 194 } | 319 } |
| 195 | 320 |
| 196 void DownloadProtectionService::EndCheckClientDownload( | |
| 197 DownloadCheckResult result, | |
| 198 DownloadCheckResultReason reason, | |
| 199 const CheckDownloadCallback& callback) { | |
| 200 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
| 201 RecordStats(reason); | |
| 202 callback.Run(result); | |
| 203 } | |
| 204 | |
| 205 void DownloadProtectionService::RecordStats(DownloadCheckResultReason reason) { | |
| 206 UMA_HISTOGRAM_ENUMERATION("SBClientDownload.CheckDownloadStats", | |
| 207 reason, | |
| 208 REASON_MAX); | |
| 209 } | |
| 210 } // namespace safe_browsing | 321 } // namespace safe_browsing |
| OLD | NEW |