Chromium Code Reviews
|
| OLD | NEW |
|---|---|
| (Empty) | |
| 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 | |
| 3 // found in the LICENSE file. | |
| 4 // | |
| 5 // Implementation of the MalwareDetails class. | |
| 6 | |
| 7 #include "chrome/browser/safe_browsing/malware_details.h" | |
| 8 | |
| 9 #include "base/callback.h" | |
| 10 #include "base/lazy_instance.h" | |
| 11 #include "base/md5.h" | |
| 12 #include "chrome/browser/net/chrome_url_request_context.h" | |
| 13 #include "chrome/browser/profiles/profile.h" | |
| 14 #include "chrome/browser/safe_browsing/malware_details_cache.h" | |
| 15 #include "chrome/browser/safe_browsing/safe_browsing_service.h" | |
| 16 #include "chrome/browser/safe_browsing/report.pb.h" | |
| 17 #include "chrome/common/net/url_request_context_getter.h" | |
| 18 #include "content/browser/browser_thread.h" | |
| 19 #include "net/base/io_buffer.h" | |
| 20 #include "net/disk_cache/disk_cache.h" | |
| 21 #include "net/http/http_cache.h" | |
| 22 #include "net/http/http_response_headers.h" | |
| 23 #include "net/http/http_response_info.h" | |
| 24 | |
| 25 using safe_browsing::ClientMalwareReportRequest; | |
| 26 | |
| 27 // Only send small files for now, a better strategy would use the size | |
| 28 // of the whole report and the user's bandwidth. | |
| 29 static const int kMaxBodySize = 1024; | |
|
noelutz
2011/04/06 20:34:19
Is this in bytes or KB?
panayiotis
2011/04/07 20:08:32
Done.
| |
| 30 | |
| 31 MalwareDetailsCacheCollector::MalwareDetailsCacheCollector() | |
| 32 : cache_state_(STATE_NONE), | |
| 33 ALLOW_THIS_IN_INITIALIZER_LIST( | |
| 34 cache_callback_(this , &MalwareDetailsCacheCollector::CacheLoop)), | |
| 35 ALLOW_THIS_IN_INITIALIZER_LIST( | |
| 36 entry_callback_( | |
| 37 new net::CancelableCompletionCallback< | |
| 38 MalwareDetailsCacheCollector>( | |
| 39 this, &MalwareDetailsCacheCollector::CacheLoop))) { | |
| 40 } | |
| 41 | |
| 42 MalwareDetailsCacheCollector::~MalwareDetailsCacheCollector() { | |
| 43 } | |
| 44 | |
| 45 // We modify |resources| and |result|, so it must remain alive. | |
|
noelutz
2011/04/06 20:34:19
Consider moving this comment to the header file.
panayiotis
2011/04/07 20:08:32
Done.
| |
| 46 void MalwareDetailsCacheCollector::StartCacheCollection( | |
| 47 URLRequestContextGetter* request_context_getter, | |
| 48 safe_browsing::ResourceMap* resources, | |
| 49 int* result, | |
| 50 Task* callback) { | |
| 51 // Start the data collection from the HTTP cache. | |
| 52 DVLOG(1) << "Getting disk_cache data for all urls..."; | |
| 53 request_context_getter_ = request_context_getter; | |
| 54 resources_ = resources; | |
| 55 resources_it_ = resources_->begin(); | |
| 56 cache_state_ = STATE_OPEN_CACHE; | |
| 57 result_ = result; | |
| 58 callback_ = callback; | |
| 59 | |
| 60 AddRef(); // The cache_callback_ calls are asynchronous and not refcounted. | |
| 61 | |
| 62 // Post a task in the message loop, so the callers don't need to | |
| 63 // check if we call their callback immediately. | |
| 64 BrowserThread::PostTask( | |
| 65 BrowserThread::IO, FROM_HERE, | |
| 66 NewRunnableMethod( | |
| 67 this, &MalwareDetailsCacheCollector::CacheLoop, net::OK)); | |
| 68 } | |
| 69 | |
| 70 bool MalwareDetailsCacheCollector::InProgress() { | |
| 71 return cache_state_ != STATE_NONE; | |
|
noelutz
2011/04/06 20:34:19
I assume this is also always called on the IO thre
panayiotis
2011/04/07 20:08:32
Done.
| |
| 72 } | |
| 73 | |
| 74 // The IO thread loop. Called once the DOM details are callected, | |
| 75 // and then is passed to all the cache lookups as a callback. | |
| 76 void MalwareDetailsCacheCollector::CacheLoop(int result) { | |
| 77 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); | |
| 78 DCHECK(cache_state_ != STATE_NONE); | |
| 79 | |
| 80 int rv = result; | |
| 81 do { | |
| 82 DVLOG(1) << "cache_state_:" << cache_state_; | |
| 83 CacheState state = cache_state_; | |
| 84 cache_state_ = STATE_NONE; | |
| 85 switch (state) { | |
| 86 case STATE_OPEN_CACHE: | |
| 87 rv = DoOpenCache(); | |
| 88 break; | |
| 89 case STATE_OPEN_CACHE_COMPLETE: | |
| 90 rv = DoOpenCacheComplete(rv); | |
| 91 break; | |
| 92 case STATE_OPEN_ENTRY: | |
| 93 rv = DoOpenEntry(); | |
| 94 break; | |
| 95 case STATE_OPEN_ENTRY_COMPLETE: | |
| 96 rv = DoOpenEntryComplete(rv); | |
| 97 break; | |
| 98 case STATE_READ_RESPONSE: | |
| 99 rv = DoReadResponse(); | |
| 100 break; | |
| 101 case STATE_READ_RESPONSE_COMPLETE: | |
| 102 rv = DoReadResponseComplete(rv); | |
| 103 break; | |
| 104 case STATE_READ_DATA: | |
| 105 rv = DoReadData(); | |
| 106 break; | |
| 107 case STATE_READ_DATA_COMPLETE: | |
| 108 rv = DoReadDataComplete(rv); | |
| 109 break; | |
| 110 default: | |
| 111 NOTREACHED() << "Bad state:" << state; | |
| 112 rv = net::ERR_FAILED; | |
| 113 break; | |
| 114 } | |
| 115 } while (rv != net::ERR_IO_PENDING && cache_state_ != STATE_NONE); | |
|
noelutz
2011/04/06 20:34:19
I'm wondering if it's OK to do all these operation
panayiotis
2011/04/07 20:08:32
Done.
| |
| 116 | |
| 117 if (rv != net::ERR_IO_PENDING) { | |
| 118 DVLOG(1) << "CacheIO done."; | |
| 119 DoCacheDone(rv); | |
| 120 } | |
| 121 } | |
| 122 | |
| 123 net::URLRequestContext* MalwareDetailsCacheCollector::GetURLRequestContext() { | |
| 124 if (!request_context_getter_) { | |
| 125 DVLOG(1) << "Missing request context getter"; | |
| 126 return NULL; | |
| 127 } | |
| 128 return request_context_getter_->GetURLRequestContext(); | |
| 129 } | |
| 130 | |
| 131 int MalwareDetailsCacheCollector::DoOpenCache() { | |
| 132 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); | |
| 133 cache_state_ = STATE_OPEN_CACHE_COMPLETE; | |
| 134 | |
| 135 net::URLRequestContext* context = GetURLRequestContext(); | |
| 136 if (!context) { | |
| 137 LOG(ERROR) << "Could not get URLRequestContext"; | |
|
noelutz
2011/04/06 20:34:19
Is there a particular reason you LOG(ERROR) here b
panayiotis
2011/04/07 20:08:32
Done.
| |
| 138 cache_state_ = STATE_NONE; | |
| 139 return net::ERR_FAILED; | |
| 140 } | |
| 141 | |
| 142 if (!context->http_transaction_factory()) { | |
| 143 DVLOG(1) << "Could not get HTTP transaction factory"; | |
| 144 cache_state_ = STATE_NONE; | |
| 145 return net::ERR_FAILED; | |
| 146 } | |
| 147 | |
| 148 net::HttpCache* http_cache = | |
| 149 context->http_transaction_factory()->GetCache(); | |
| 150 if (!http_cache) { | |
| 151 DVLOG(1) << "Could not get http cache"; | |
| 152 cache_state_ = STATE_NONE; | |
| 153 return net::ERR_FAILED; | |
| 154 } | |
| 155 | |
| 156 return http_cache->GetBackend(&cache_, &cache_callback_); | |
| 157 } | |
| 158 | |
| 159 int MalwareDetailsCacheCollector::DoOpenCacheComplete(int result) { | |
| 160 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); | |
| 161 cache_state_ = STATE_OPEN_ENTRY; | |
| 162 | |
| 163 if (result == net::ERR_FAILED || !cache_) { | |
| 164 LOG(ERROR) << "Could not get disk cache"; | |
| 165 cache_state_ = STATE_NONE; | |
| 166 return net::ERR_FAILED; | |
| 167 } | |
| 168 | |
| 169 return net::OK; | |
| 170 } | |
| 171 | |
| 172 int MalwareDetailsCacheCollector::DoOpenEntry() { | |
| 173 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); | |
| 174 cache_state_ = STATE_OPEN_ENTRY_COMPLETE; | |
| 175 | |
| 176 if (resources_it_ == resources_->end()) { | |
| 177 cache_state_ = STATE_NONE; | |
| 178 return net::OK; | |
| 179 } | |
| 180 | |
| 181 entry_ = NULL; // Reset the entry. | |
| 182 return cache_->OpenEntry(resources_it_->first, &entry_, &cache_callback_); | |
|
noelutz
2011/04/06 20:34:19
Maybe mention what resources_it_ refers to?
panayiotis
2011/04/07 20:08:32
Done.
| |
| 183 } | |
| 184 | |
| 185 int MalwareDetailsCacheCollector::DoOpenEntryComplete(int rv) { | |
| 186 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); | |
| 187 cache_state_ = STATE_READ_RESPONSE; | |
| 188 return net::OK; | |
|
noelutz
2011/04/06 20:34:19
I think there might be a bug here. I think OpenEn
panayiotis
2011/04/07 20:08:32
Done.
| |
| 189 } | |
| 190 | |
| 191 int MalwareDetailsCacheCollector::DoReadResponse() { | |
| 192 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); | |
| 193 cache_state_ = STATE_READ_RESPONSE_COMPLETE; | |
| 194 | |
| 195 if (!entry_) { // No entry in the cache for this url. | |
|
noelutz
2011/04/06 20:34:19
Is that how you handle errors mentioned above? Ar
panayiotis
2011/04/07 20:08:32
Done.
| |
| 196 return net::OK; | |
| 197 } | |
| 198 | |
| 199 // net::HttpCache::kResponseInfoIndex = 0. | |
| 200 buf_len_ = entry_->GetDataSize(0); | |
|
noelutz
2011/04/06 20:34:19
why not use the constant then?
panayiotis
2011/04/07 20:08:32
It's not public.
| |
| 201 if (!buf_len_) { | |
| 202 DVLOG(1) << "Empty response"; | |
| 203 return buf_len_; | |
|
noelutz
2011/04/06 20:34:19
This is confusing. You always return net::somethi
panayiotis
2011/04/07 20:08:32
well, now we return net::ERR_PENDING
but DoReadRe
| |
| 204 } | |
| 205 | |
| 206 buf_ = new net::IOBuffer(buf_len_); | |
| 207 return entry_->ReadData(0, 0, buf_, buf_len_, entry_callback_); | |
| 208 } | |
| 209 | |
| 210 int MalwareDetailsCacheCollector::DoReadResponseComplete(int result) { | |
| 211 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); | |
| 212 cache_state_ = STATE_READ_DATA; | |
| 213 | |
| 214 if (!entry_ || !result || result != buf_len_) { | |
| 215 DVLOG(1) << "No entry"; | |
| 216 } else { | |
| 217 linked_ptr<ClientMalwareReportRequest::Resource> | |
| 218 pb_resource = resources_it_->second; | |
| 219 net::HttpResponseInfo response; | |
| 220 bool truncated; | |
| 221 if (net::HttpCache::ParseResponseInfo(buf_->data(), buf_len_, &response, | |
| 222 &truncated)) { | |
| 223 if (!truncated) { | |
| 224 if (response.headers) { | |
| 225 ClientMalwareReportRequest::HTTPResponse* pb_response = | |
| 226 pb_resource->mutable_response(); | |
| 227 pb_response->mutable_firstline()->set_code( | |
| 228 response.headers->response_code()); | |
| 229 void* iter = NULL; | |
| 230 std::string name, value; | |
| 231 while (response.headers->EnumerateHeaderLines(&iter, &name, &value)) { | |
| 232 // Strip any Set-Cookie headers. | |
| 233 if (LowerCaseEqualsASCII(name, "set-cookie")) { | |
| 234 continue; | |
| 235 } | |
| 236 ClientMalwareReportRequest::HTTPHeader* pb_header = | |
| 237 pb_response->add_headers(); | |
| 238 pb_header->set_name(name); | |
| 239 pb_header->set_value(value); | |
| 240 } | |
| 241 } else { | |
| 242 DVLOG(1) << "Missing response headers."; | |
| 243 } | |
| 244 } else { | |
| 245 DVLOG(1) << "Response truncated"; | |
| 246 } | |
| 247 } else { | |
| 248 LOG(ERROR) << "Could not parse HTTP cache entry"; | |
| 249 } | |
| 250 } | |
| 251 | |
| 252 return net::OK; | |
| 253 } | |
| 254 | |
| 255 int MalwareDetailsCacheCollector::DoReadData() { | |
| 256 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); | |
| 257 cache_state_ = STATE_READ_DATA_COMPLETE; | |
|
noelutz
2011/04/06 20:34:19
skip this stage too if entry_ wasn't set?
panayiotis
2011/04/07 20:08:32
Done.
| |
| 258 | |
| 259 if (!entry_) { // No entry in the cache for this url. | |
| 260 return net::OK; | |
| 261 } | |
| 262 | |
| 263 // net::HttpCache::kResponseContentIndex = 1. | |
| 264 buf_len_ = entry_->GetDataSize(1); | |
|
noelutz
2011/04/06 20:34:19
why not use the contant here too?
panayiotis
2011/04/07 20:08:32
same reason as above, not public.
| |
| 265 if (!buf_len_) { | |
| 266 DVLOG(1) << "Empty data"; | |
| 267 return buf_len_; | |
| 268 } | |
| 269 | |
| 270 buf_ = new net::IOBuffer(buf_len_); | |
| 271 return entry_->ReadData(1, 0, buf_, buf_len_, entry_callback_); | |
|
noelutz
2011/04/06 20:34:19
also use the contant here?
panayiotis
2011/04/07 20:08:32
same
| |
| 272 } | |
| 273 | |
| 274 int MalwareDetailsCacheCollector::DoReadDataComplete(int result) { | |
| 275 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); | |
| 276 cache_state_ = STATE_OPEN_ENTRY; | |
| 277 | |
| 278 if (entry_ && result && result == buf_len_) { | |
| 279 linked_ptr<ClientMalwareReportRequest::Resource> | |
| 280 pb_resource = resources_it_->second; | |
| 281 ClientMalwareReportRequest::HTTPResponse* pb_response = | |
| 282 pb_resource->mutable_response(); | |
| 283 if (buf_len_ <= kMaxBodySize) { // Only send small bodies for now. | |
|
noelutz
2011/04/06 20:34:19
you know in the previous stage (DoReadData) that y
panayiotis
2011/04/07 20:08:32
Done.
noelutz
2011/04/11 20:32:10
I don't think this is done. Is there any reason n
panayiotis
2011/04/11 21:42:16
Oh sorry, I missed this. I actually do read the re
noelutz
2011/04/11 21:49:15
sounds good. i missed the digest part.
| |
| 284 pb_response->set_body(buf_->data(), buf_len_); | |
| 285 } | |
| 286 pb_response->set_bodylength(buf_len_); | |
| 287 MD5Digest digest; | |
| 288 MD5Sum(buf_->data(), buf_len_, &digest); | |
| 289 pb_response->set_bodydigest(MD5DigestToBase16(digest)); | |
| 290 entry_->Close(); | |
|
noelutz
2011/04/06 20:34:19
If you do what I mentioned above make sure to clos
panayiotis
2011/04/07 20:08:32
Done.
| |
| 291 } | |
| 292 | |
| 293 ++resources_it_; // Advance to the next entry. | |
|
noelutz
2011/04/06 20:34:19
A few things here:
- Before you go read the next
panayiotis
2011/04/07 20:08:32
setting buf_ = NULL now. I verified this releases
| |
| 294 return net::OK; | |
| 295 } | |
| 296 | |
| 297 void MalwareDetailsCacheCollector::DoCacheDone(int result) { | |
| 298 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); | |
| 299 *result_ = result; | |
| 300 BrowserThread::PostTask(BrowserThread::IO, FROM_HERE, callback_); | |
| 301 | |
| 302 Release(); | |
| 303 // We are now deleted. | |
| 304 } | |
| OLD | NEW |