Chromium Code Reviews| OLD | NEW |
|---|---|
| (Empty) | |
| 1 // Copyright (c) 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 "net/http/http_cache_writers.h" | |
| 6 | |
| 7 #include <algorithm> | |
| 8 #include <utility> | |
|
jkarlin
2017/06/28 15:50:09
newline between groups of includes
shivanisha
2017/06/28 19:38:19
Added
| |
| 9 #include "net/disk_cache/disk_cache.h" | |
| 10 #include "net/http/http_cache_transaction.h" | |
| 11 | |
| 12 namespace net { | |
| 13 | |
| 14 HttpCache::Writers::Writers(disk_cache::Entry* disk_entry) | |
| 15 : disk_entry_(disk_entry), weak_factory_(this) {} | |
| 16 | |
| 17 HttpCache::Writers::~Writers() {} | |
| 18 | |
| 19 int HttpCache::Writers::Read(scoped_refptr<IOBuffer> buf, | |
| 20 int buf_len, | |
| 21 const CompletionCallback& callback, | |
| 22 Transaction* transaction) { | |
| 23 DCHECK(buf); | |
|
jkarlin
2017/06/28 15:50:09
include file missing for DCHECK
shivanisha
2017/06/28 19:38:19
Added logging.h
| |
| 24 DCHECK_GT(buf_len, 0); | |
| 25 DCHECK(!callback.is_null()); | |
| 26 DCHECK(transaction); | |
| 27 | |
| 28 // If another transaction invoked a Read which is currently ongoing, then | |
| 29 // this transaction waits for the read to complete and gets its buffer filled | |
| 30 // with the data returned from that read. | |
| 31 if (next_state_ != State::NONE) { | |
| 32 WaitingForRead waiting_transaction(transaction, buf, buf_len, callback); | |
| 33 waiting_for_read_.push_back(waiting_transaction); | |
| 34 return ERR_IO_PENDING; | |
|
jkarlin
2017/06/28 15:50:09
need include file for ERR_IO_PENDING
shivanisha
2017/06/28 19:38:19
Added
| |
| 35 } | |
| 36 | |
| 37 DCHECK_EQ(next_state_, State::NONE); | |
| 38 DCHECK(callback_.is_null()); | |
| 39 DCHECK_EQ(nullptr, active_transaction_); | |
| 40 DCHECK(IsPresent(transaction)); | |
| 41 active_transaction_ = transaction; | |
| 42 | |
| 43 read_buf_ = std::move(buf); | |
| 44 io_buf_len_ = buf_len; | |
| 45 next_state_ = State::NETWORK_READ; | |
| 46 | |
| 47 int rv = DoLoop(OK); | |
| 48 if (rv == ERR_IO_PENDING) | |
| 49 callback_ = callback; | |
| 50 | |
| 51 return rv; | |
| 52 } | |
| 53 | |
| 54 bool HttpCache::Writers::StopCaching(Transaction* transaction) { | |
| 55 // If this is the only transaction in Writers, then stopping will be | |
| 56 // successful. If not, then we will not stop caching since there are | |
| 57 // other consumers waiting to read from the cache. | |
| 58 if (all_writers_.size() == 1) { | |
| 59 DCHECK(all_writers_.count(transaction)); | |
| 60 network_read_only_ = true; | |
| 61 return true; | |
| 62 } | |
| 63 return false; | |
| 64 } | |
| 65 | |
| 66 void HttpCache::Writers::AddTransaction( | |
| 67 Transaction* transaction, | |
| 68 std::unique_ptr<HttpTransaction> network_transaction, | |
| 69 bool is_exclusive) { | |
| 70 DCHECK(transaction); | |
| 71 DCHECK(CanAddWriters()); | |
| 72 DCHECK(network_transaction_ || network_transaction); | |
| 73 | |
| 74 std::pair<TransactionSet::iterator, bool> return_val = | |
| 75 all_writers_.insert(transaction); | |
| 76 DCHECK_EQ(return_val.second, true); | |
| 77 | |
| 78 if (is_exclusive) { | |
| 79 DCHECK_EQ(1u, all_writers_.size()); | |
| 80 is_exclusive_ = true; | |
| 81 } | |
| 82 | |
| 83 if (network_transaction) { | |
| 84 DCHECK(!network_transaction_); | |
| 85 network_transaction_ = std::move(network_transaction); | |
| 86 } | |
| 87 | |
| 88 priority_ = std::max(transaction->priority(), priority_); | |
| 89 network_transaction_->SetPriority(priority_); | |
| 90 } | |
| 91 | |
| 92 void HttpCache::Writers::RemoveTransaction(Transaction* transaction) { | |
| 93 if (!transaction) | |
|
jkarlin
2017/06/28 15:50:09
Huh, do we really expect to have nullptr here?
shivanisha
2017/06/28 19:38:19
Yes, it will be clearer in the integration CL. But
| |
| 94 return; | |
| 95 | |
| 96 // The transaction should be part of all_writers. | |
| 97 auto it = all_writers_.find(transaction); | |
| 98 DCHECK(it != all_writers_.end()); | |
|
jkarlin
2017/06/28 15:50:09
DCHECK_NE
shivanisha
2017/06/28 19:38:19
Gives a compile time error due to iterators being
jkarlin
2017/06/30 18:20:38
Good point, we don't actually need to print out th
| |
| 99 all_writers_.erase(transaction); | |
|
jkarlin
2017/06/28 15:50:09
Better to erase the iterator.
shivanisha
2017/06/28 19:38:20
done
| |
| 100 if (all_writers_.empty() && next_state_ == State::NONE) | |
| 101 ResetStateForEmptyWriters(); | |
| 102 else | |
| 103 UpdatePriority(); | |
| 104 | |
| 105 if (active_transaction_ == transaction) { | |
| 106 active_transaction_ = nullptr; | |
| 107 callback_.Reset(); | |
| 108 return; | |
| 109 } | |
| 110 | |
| 111 auto waiting_it = waiting_for_read_.begin(); | |
| 112 for (; waiting_it != waiting_for_read_.end(); waiting_it++) { | |
| 113 if (transaction == waiting_it->transaction) { | |
| 114 waiting_for_read_.erase(waiting_it); | |
| 115 // If a waiting transaction existed, there should have been an | |
| 116 // active_transaction_. | |
| 117 DCHECK(active_transaction_); | |
| 118 return; | |
| 119 } | |
| 120 } | |
| 121 } | |
| 122 | |
| 123 void HttpCache::Writers::UpdatePriority() { | |
| 124 // Get the current highest priority. | |
| 125 RequestPriority current_highest = MINIMUM_PRIORITY; | |
| 126 for (auto* transaction : all_writers_) | |
| 127 current_highest = std::max(transaction->priority(), current_highest); | |
| 128 | |
| 129 if (priority_ != current_highest) { | |
| 130 network_transaction_->SetPriority(current_highest); | |
| 131 priority_ = current_highest; | |
| 132 } | |
| 133 } | |
| 134 | |
| 135 bool HttpCache::Writers::ContainsOnlyIdleWriters() const { | |
| 136 return waiting_for_read_.empty() && !active_transaction_; | |
| 137 } | |
| 138 | |
| 139 HttpCache::TransactionSet HttpCache::Writers::RemoveAllIdleWriters() { | |
| 140 // Should be invoked after |waiting_for_read_| transactions and | |
| 141 // |active_transaction_| are processed so that all_writers_ only contains idle | |
| 142 // writers. | |
| 143 DCHECK(ContainsOnlyIdleWriters()); | |
| 144 | |
| 145 TransactionSet idle_writers; | |
| 146 idle_writers.insert(all_writers_.begin(), all_writers_.end()); | |
| 147 all_writers_.clear(); | |
| 148 ResetStateForEmptyWriters(); | |
| 149 return idle_writers; | |
| 150 } | |
| 151 | |
| 152 bool HttpCache::Writers::CanAddWriters() { | |
| 153 if (all_writers_.empty()) | |
| 154 return true; | |
| 155 | |
| 156 return !is_exclusive_ && !network_read_only_; | |
| 157 } | |
| 158 | |
| 159 void HttpCache::Writers::ProcessFailure(Transaction* transaction, int error) { | |
| 160 DCHECK(!transaction || transaction == active_transaction_); | |
| 161 | |
| 162 // Notify waiting_for_read_ of the failure. Tasks will be posted for all the | |
| 163 // transactions. | |
| 164 ProcessWaitingForReadTransactions(error); | |
| 165 | |
| 166 // Idle readers should fail when Read is invoked on them. | |
| 167 SetIdleWritersFailState(error); | |
| 168 | |
| 169 if (all_writers_.empty()) | |
| 170 ResetStateForEmptyWriters(); | |
| 171 } | |
| 172 | |
| 173 void HttpCache::Writers::TruncateEntry() { | |
| 174 // TODO(shivanisha) On integration, see if the entry really needs to be | |
| 175 // truncated on the lines of Transaction::AddTruncatedFlag and then proceed. | |
| 176 DCHECK_EQ(next_state_, State::NONE); | |
| 177 next_state_ = State::CACHE_WRITE_TRUNCATED_RESPONSE; | |
| 178 DoLoop(OK); | |
| 179 } | |
| 180 | |
| 181 LoadState HttpCache::Writers::GetWriterLoadState() { | |
| 182 DCHECK(network_transaction_); | |
| 183 return network_transaction_->GetLoadState(); | |
| 184 } | |
| 185 | |
| 186 HttpCache::Writers::WaitingForRead::WaitingForRead( | |
| 187 Transaction* cache_transaction, | |
| 188 scoped_refptr<IOBuffer> buf, | |
| 189 int len, | |
| 190 const CompletionCallback& consumer_callback) | |
| 191 : transaction(cache_transaction), | |
| 192 read_buf(std::move(buf)), | |
| 193 read_buf_len(len), | |
| 194 write_len(0), | |
| 195 callback(consumer_callback) { | |
| 196 DCHECK(cache_transaction); | |
| 197 DCHECK(read_buf); | |
| 198 DCHECK_GT(len, 0); | |
| 199 DCHECK(!consumer_callback.is_null()); | |
| 200 } | |
| 201 | |
| 202 HttpCache::Writers::WaitingForRead::~WaitingForRead() {} | |
| 203 HttpCache::Writers::WaitingForRead::WaitingForRead(const WaitingForRead&) = | |
| 204 default; | |
| 205 | |
| 206 int HttpCache::Writers::DoLoop(int result) { | |
| 207 DCHECK(next_state_ != State::NONE); | |
|
jkarlin
2017/06/28 15:50:09
DCHECK_NE
shivanisha
2017/06/28 19:38:20
done
| |
| 208 int rv = result; | |
| 209 do { | |
| 210 State state = next_state_; | |
| 211 next_state_ = State::NONE; | |
|
jkarlin
2017/06/28 15:50:09
Can you use the same STATE::INVALID as we do in HT
shivanisha
2017/06/28 19:38:19
done
| |
| 212 switch (state) { | |
| 213 case State::NETWORK_READ: | |
| 214 DCHECK_EQ(OK, rv); | |
| 215 rv = DoNetworkRead(); | |
| 216 break; | |
| 217 case State::NETWORK_READ_COMPLETE: | |
| 218 rv = DoNetworkReadComplete(rv); | |
| 219 break; | |
| 220 case State::CACHE_WRITE_DATA: | |
| 221 rv = DoCacheWriteData(rv); | |
| 222 break; | |
| 223 case State::CACHE_WRITE_DATA_COMPLETE: | |
| 224 rv = DoCacheWriteDataComplete(rv); | |
| 225 break; | |
| 226 case State::CACHE_WRITE_TRUNCATED_RESPONSE: | |
| 227 rv = DoCacheWriteTruncatedResponse(); | |
| 228 break; | |
| 229 case State::CACHE_WRITE_TRUNCATED_RESPONSE_COMPLETE: | |
| 230 rv = DoCacheWriteTruncatedResponseComplete(rv); | |
| 231 break; | |
| 232 default: | |
|
jkarlin
2017/06/28 15:50:08
Better not to have a default case in this switch s
shivanisha
2017/06/28 19:38:19
done
| |
| 233 NOTREACHED() << "bad state"; | |
| 234 rv = ERR_FAILED; | |
| 235 break; | |
| 236 } | |
| 237 } while (next_state_ != State::NONE && rv != ERR_IO_PENDING); | |
| 238 | |
| 239 if (rv != ERR_IO_PENDING && !callback_.is_null()) { | |
| 240 read_buf_ = NULL; | |
| 241 base::ResetAndReturn(&callback_).Run(rv); | |
| 242 } | |
| 243 return rv; | |
| 244 } | |
| 245 | |
| 246 int HttpCache::Writers::DoNetworkRead() { | |
| 247 next_state_ = State::NETWORK_READ_COMPLETE; | |
| 248 CompletionCallback io_callback = | |
| 249 base::Bind(&HttpCache::Writers::OnIOComplete, weak_factory_.GetWeakPtr()); | |
| 250 return network_transaction_->Read(read_buf_.get(), io_buf_len_, io_callback); | |
| 251 } | |
| 252 | |
| 253 int HttpCache::Writers::DoNetworkReadComplete(int result) { | |
| 254 if (result < 0) { | |
| 255 OnNetworkReadFailure(result); | |
| 256 return result; | |
| 257 } | |
| 258 | |
| 259 next_state_ = State::CACHE_WRITE_DATA; | |
| 260 return result; | |
| 261 } | |
| 262 | |
| 263 void HttpCache::Writers::OnNetworkReadFailure(int result) { | |
| 264 ProcessFailure(active_transaction_, result); | |
| 265 | |
| 266 active_transaction_ = nullptr; | |
| 267 | |
| 268 // TODO(shivanisha): Invoke DoneWithEntry here while | |
| 269 // integrating this class with HttpCache. That will also invoke truncation of | |
| 270 // the entry. | |
| 271 } | |
| 272 | |
| 273 int HttpCache::Writers::DoCacheWriteData(int num_bytes) { | |
| 274 next_state_ = State::CACHE_WRITE_DATA_COMPLETE; | |
| 275 write_len_ = num_bytes; | |
| 276 if (!num_bytes || network_read_only_) | |
| 277 return num_bytes; | |
| 278 | |
| 279 int current_size = disk_entry_->GetDataSize(kResponseContentIndex); | |
| 280 CompletionCallback io_callback = | |
| 281 base::Bind(&HttpCache::Writers::OnIOComplete, weak_factory_.GetWeakPtr()); | |
| 282 | |
| 283 int rv = 0; | |
| 284 | |
| 285 PartialData* partial = nullptr; | |
| 286 // The active transaction must be alive if this is a partial request, as | |
| 287 // partial requests are exclusive and hence will always be the active | |
| 288 // transaction. | |
| 289 // Todo(shivanisha): When partial requests support parallel writing, this | |
|
jkarlin
2017/06/28 15:50:09
s/Todo/TODO
shivanisha
2017/06/28 19:38:19
done
| |
| 290 // assumption will not be true. | |
| 291 if (active_transaction_) | |
| 292 partial = active_transaction_->partial(); | |
| 293 | |
| 294 if (!partial) { | |
| 295 rv = disk_entry_->WriteData(kResponseContentIndex, current_size, | |
| 296 read_buf_.get(), num_bytes, io_callback, true); | |
| 297 } else { | |
| 298 rv = partial->CacheWrite(disk_entry_, read_buf_.get(), num_bytes, | |
| 299 io_callback); | |
| 300 } | |
| 301 return rv; | |
| 302 } | |
| 303 | |
| 304 int HttpCache::Writers::DoCacheWriteDataComplete(int result) { | |
| 305 if (result != write_len_) { | |
| 306 OnCacheWriteFailure(); | |
| 307 | |
| 308 // |active_transaction_| can continue reading from the network. | |
| 309 result = write_len_; | |
| 310 } else { | |
| 311 OnDataReceived(result); | |
| 312 } | |
| 313 return result; | |
| 314 } | |
| 315 | |
| 316 int HttpCache::Writers::DoCacheWriteTruncatedResponse() { | |
| 317 next_state_ = State::CACHE_WRITE_TRUNCATED_RESPONSE_COMPLETE; | |
| 318 const HttpResponseInfo* response = network_transaction_->GetResponseInfo(); | |
| 319 scoped_refptr<PickledIOBuffer> data(new PickledIOBuffer()); | |
| 320 response->Persist(data->pickle(), true /* skip_transient_headers*/, true); | |
| 321 data->Done(); | |
| 322 io_buf_len_ = data->pickle()->size(); | |
| 323 CompletionCallback io_callback = | |
| 324 base::Bind(&HttpCache::Writers::OnIOComplete, weak_factory_.GetWeakPtr()); | |
| 325 return disk_entry_->WriteData(kResponseInfoIndex, 0, data.get(), io_buf_len_, | |
| 326 io_callback, true); | |
| 327 } | |
| 328 | |
| 329 int HttpCache::Writers::DoCacheWriteTruncatedResponseComplete(int result) { | |
| 330 if (result != io_buf_len_) { | |
| 331 DLOG(ERROR) << "failed to write response info to cache"; | |
| 332 | |
| 333 // TODO(shivanisha): Invoke DoneWritingToEntry so that this entry is doomed. | |
| 334 } | |
| 335 truncated_ = true; | |
| 336 return OK; | |
| 337 } | |
| 338 | |
| 339 void HttpCache::Writers::OnDataReceived(int result) { | |
| 340 if (result == 0) { | |
| 341 // Check if the response is actually completed or if not, attempt to mark | |
| 342 // the entry as truncated in OnNetworkReadFailure. | |
| 343 int current_size = disk_entry_->GetDataSize(kResponseContentIndex); | |
| 344 const HttpResponseInfo* response_info = | |
| 345 network_transaction_->GetResponseInfo(); | |
| 346 int64_t content_length = response_info->headers->GetContentLength(); | |
| 347 if (content_length >= 0 && content_length > current_size) { | |
| 348 OnNetworkReadFailure(result); | |
| 349 return; | |
| 350 } | |
| 351 // TODO(shivanisha) Invoke cache_->DoneWritingToEntry() with success after | |
| 352 // integration with HttpCache layer. | |
| 353 } | |
| 354 | |
| 355 // Notify waiting_for_read_. Tasks will be posted for all the | |
| 356 // transactions. | |
| 357 ProcessWaitingForReadTransactions(write_len_); | |
| 358 | |
| 359 active_transaction_ = nullptr; | |
| 360 | |
| 361 if (all_writers_.empty()) | |
| 362 ResetStateForEmptyWriters(); | |
| 363 } | |
| 364 | |
| 365 void HttpCache::Writers::OnCacheWriteFailure() { | |
|
jkarlin
2017/06/30 18:20:39
This is interesting. We actually expect the cache
shivanisha
2017/07/05 18:55:42
As discussed f2f, since only around 0.003% of simp
jkarlin
2017/07/06 18:50:03
Acknowledged.
| |
| 366 DLOG(ERROR) << "failed to write response data to cache"; | |
| 367 | |
| 368 // Now writers will only be reading from the network. | |
| 369 network_read_only_ = true; | |
| 370 | |
| 371 ProcessFailure(active_transaction_, ERR_CACHE_WRITE_FAILURE); | |
| 372 | |
| 373 active_transaction_ = nullptr; | |
| 374 | |
| 375 // Call the cache_ function here even if |active_transaction_| is alive | |
| 376 // because it wouldn't know if this was an error case, since it gets a | |
| 377 // positive result back. | |
| 378 // TODO(shivanisha) : Invoke DoneWritingToEntry on integration. Since the | |
| 379 // active_transaction_ continues to read from the network, invoke | |
| 380 // DoneWritingToEntry with nullptr as transaction so that it is not removed | |
| 381 // from |this|. | |
| 382 } | |
| 383 | |
| 384 void HttpCache::Writers::ProcessWaitingForReadTransactions(int result) { | |
| 385 for (auto& waiting : waiting_for_read_) { | |
| 386 Transaction* transaction = waiting.transaction; | |
| 387 int callback_result = result; | |
| 388 | |
| 389 if (result >= 0) { // success | |
| 390 // Save the data in the waiting transaction's read buffer. | |
| 391 waiting.write_len = std::min(waiting.read_buf_len, result); | |
| 392 memcpy(waiting.read_buf->data(), read_buf_->data(), waiting.write_len); | |
| 393 callback_result = waiting.write_len; | |
| 394 } | |
| 395 | |
| 396 // If its response completion or failure, this transaction needs to be | |
| 397 // removed. | |
| 398 if (result <= 0) | |
| 399 all_writers_.erase(transaction); | |
| 400 | |
| 401 // Post task to notify transaction. | |
| 402 base::ThreadTaskRunnerHandle::Get()->PostTask( | |
| 403 FROM_HERE, base::Bind(waiting.callback, callback_result)); | |
| 404 } | |
| 405 | |
| 406 waiting_for_read_.clear(); | |
| 407 } | |
| 408 | |
| 409 void HttpCache::Writers::SetIdleWritersFailState(int result) { | |
| 410 // Since this is only for idle transactions, waiting_for_read_ | |
| 411 // should be empty. | |
| 412 DCHECK(waiting_for_read_.empty()); | |
| 413 for (auto* transaction : all_writers_) { | |
| 414 if (transaction == active_transaction_) | |
| 415 continue; | |
| 416 transaction->SetSharedWritingFailState(result); | |
|
jkarlin
2017/06/28 15:50:08
I'm not sure that this new method is necessary. Wh
shivanisha
2017/06/28 19:38:19
That would need the HC::T::Read() to invoke Writer
jkarlin
2017/06/30 18:20:38
Just to make sure, but the idea is that HC::T::Rea
shivanisha
2017/07/05 18:55:42
There are multiple scenarios in HC::T::Read() to d
jkarlin
2017/07/06 18:50:04
This will fail because the transaction isn't part
jkarlin
2017/07/06 21:17:49
Ignore my comments above, I meant to just post the
| |
| 417 all_writers_.erase(transaction); | |
| 418 } | |
| 419 } | |
| 420 | |
| 421 void HttpCache::Writers::ResetStateForEmptyWriters() { | |
| 422 DCHECK(all_writers_.empty()); | |
| 423 network_read_only_ = false; | |
| 424 network_transaction_.reset(); | |
| 425 } | |
| 426 | |
| 427 void HttpCache::Writers::OnIOComplete(int result) { | |
| 428 DoLoop(result); | |
| 429 } | |
| 430 | |
| 431 } // namespace net | |
| OLD | NEW |