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> |
| 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(base::WeakPtr<HttpCache> cache, ActiveEntry* entry) |
| 15 : cache_(cache), entry_(entry), weak_factory_(this) { |
| 16 io_callback_ = |
| 17 base::Bind(&HttpCache::Writers::OnIOComplete, weak_factory_.GetWeakPtr()); |
| 18 } |
| 19 |
| 20 HttpCache::Writers::~Writers() {} |
| 21 |
| 22 int HttpCache::Writers::Read(scoped_refptr<IOBuffer> buf, |
| 23 int buf_len, |
| 24 const CompletionCallback& callback, |
| 25 Transaction* transaction) { |
| 26 DCHECK(buf); |
| 27 DCHECK_GT(buf_len, 0); |
| 28 DCHECK(!callback.is_null()); |
| 29 |
| 30 // If another transaction is already reading from the network, then this |
| 31 // transaction waits for the read to complete and gets its buffer filled |
| 32 // with the data returned from that read. |
| 33 if (active_transaction_) { |
| 34 WaitingForRead waiting_transaction(transaction, buf, buf_len, callback); |
| 35 waiting_for_read_.push_back(waiting_transaction); |
| 36 return ERR_IO_PENDING; |
| 37 } |
| 38 |
| 39 DCHECK_EQ(next_state_, State::NONE); |
| 40 DCHECK(callback_.is_null()); |
| 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 DCHECK(callback_.is_null()); |
| 50 callback_ = callback; |
| 51 } |
| 52 |
| 53 return rv; |
| 54 } |
| 55 |
| 56 bool HttpCache::Writers::StopCaching(Transaction* transaction) { |
| 57 // If this is the only transaction in Writers, then stopping will be |
| 58 // successful. If not, then we will not stop caching since there are |
| 59 // other consumers waiting to read from the cache. |
| 60 if (all_writers_.size() == 1) { |
| 61 DCHECK(all_writers_.count(transaction)); |
| 62 network_read_only_ = true; |
| 63 return true; |
| 64 } |
| 65 return false; |
| 66 } |
| 67 |
| 68 void HttpCache::Writers::AddTransaction(Transaction* transaction, |
| 69 HttpTransaction* network_transaction) { |
| 70 DCHECK(transaction); |
| 71 DCHECK(CanAddWriters()); |
| 72 |
| 73 auto return_val = all_writers_.insert(transaction); |
| 74 DCHECK_EQ(return_val.second, true); |
| 75 |
| 76 if (network_transaction) { |
| 77 network_transaction_ = |
| 78 base::WrapUnique<HttpTransaction>(network_transaction); |
| 79 } |
| 80 DCHECK(network_transaction_); |
| 81 |
| 82 priority_ = std::max(transaction->priority(), priority_); |
| 83 network_transaction_->SetPriority(priority_); |
| 84 } |
| 85 |
| 86 void HttpCache::Writers::RemoveTransaction(Transaction* transaction) { |
| 87 if (!transaction) |
| 88 return; |
| 89 |
| 90 // The transaction should be part of all_writers. |
| 91 auto it = all_writers_.find(transaction); |
| 92 DCHECK(it != all_writers_.end()); |
| 93 all_writers_.erase(transaction); |
| 94 PriorityChanged(); |
| 95 |
| 96 if (active_transaction_ == transaction) { |
| 97 active_transaction_ = nullptr; |
| 98 callback_.Reset(); |
| 99 return; |
| 100 } |
| 101 |
| 102 auto waiting_it = waiting_for_read_.begin(); |
| 103 for (; waiting_it != waiting_for_read_.end(); waiting_it++) { |
| 104 if (transaction == waiting_it->transaction) { |
| 105 waiting_for_read_.erase(waiting_it); |
| 106 // If a waiting transaction existed, there should have been an |
| 107 // active_transaction_. |
| 108 DCHECK(active_transaction_); |
| 109 return; |
| 110 } |
| 111 } |
| 112 } |
| 113 |
| 114 void HttpCache::Writers::RemoveAllTransactions() { |
| 115 all_writers_.clear(); |
| 116 } |
| 117 |
| 118 void HttpCache::Writers::PriorityChanged() { |
| 119 RequestPriority current_highest = getCurrentHighestPriority(); |
| 120 if (priority_ != current_highest) { |
| 121 network_transaction_->SetPriority(current_highest); |
| 122 priority_ = current_highest; |
| 123 } |
| 124 } |
| 125 |
| 126 void HttpCache::Writers::MoveIdleWritersToReaders() { |
| 127 // Should be invoked after |waiting_for_read_| transactions and |
| 128 // |active_transaction_| are processed so that all_writers_ only contains idle |
| 129 // writers. |
| 130 DCHECK(waiting_for_read_.empty()); |
| 131 DCHECK(!active_transaction_); |
| 132 for (auto* idle_writer : all_writers_) { |
| 133 entry_->readers.insert(idle_writer); |
| 134 } |
| 135 all_writers_.clear(); |
| 136 } |
| 137 |
| 138 bool HttpCache::Writers::CanAddWriters() { |
| 139 if (all_writers_.empty()) { |
| 140 DCHECK(!network_read_only_); |
| 141 return true; |
| 142 } |
| 143 |
| 144 return !is_exclusive_ && !network_read_only_; |
| 145 } |
| 146 |
| 147 void HttpCache::Writers::ProcessFailure(Transaction* transaction, int error) { |
| 148 DCHECK(!transaction || transaction == active_transaction_); |
| 149 |
| 150 // Notify waiting_for_read_ of the failure. Tasks will be posted for all the |
| 151 // transactions. |
| 152 ProcessWaitingForReadTransactions(error); |
| 153 |
| 154 // Idle readers should know to fail when Read is invoked by their consumers. |
| 155 SetIdleWritersFailState(error); |
| 156 DCHECK(all_writers_.empty()); |
| 157 } |
| 158 |
| 159 void HttpCache::Writers::TruncateEntry() { |
| 160 // TODO(shivanisha) On integration, see if the entry really needs to be |
| 161 // truncated on the lines of Transaction::AddTruncatedFlag and then proceed. |
| 162 next_state_ = State::CACHE_WRITE_TRUNCATED_RESPONSE; |
| 163 DoLoop(OK); |
| 164 } |
| 165 |
| 166 LoadState HttpCache::Writers::GetWriterLoadState() { |
| 167 return network_transaction_->GetLoadState(); |
| 168 } |
| 169 |
| 170 HttpCache::Writers::WaitingForRead::WaitingForRead( |
| 171 Transaction* cache_transaction, |
| 172 scoped_refptr<IOBuffer> buf, |
| 173 int len, |
| 174 const CompletionCallback& consumer_callback) |
| 175 : transaction(cache_transaction), |
| 176 read_buf(std::move(buf)), |
| 177 read_buf_len(len), |
| 178 write_len(0), |
| 179 callback(consumer_callback) { |
| 180 DCHECK(cache_transaction); |
| 181 DCHECK(buf); |
| 182 DCHECK_GT(len, 0); |
| 183 DCHECK(!consumer_callback.is_null()); |
| 184 } |
| 185 |
| 186 HttpCache::Writers::WaitingForRead::~WaitingForRead() {} |
| 187 HttpCache::Writers::WaitingForRead::WaitingForRead(const WaitingForRead&) = |
| 188 default; |
| 189 |
| 190 int HttpCache::Writers::DoLoop(int result) { |
| 191 DCHECK(next_state_ != State::NONE); |
| 192 int rv = result; |
| 193 do { |
| 194 State state = next_state_; |
| 195 next_state_ = State::NONE; |
| 196 switch (state) { |
| 197 case State::NETWORK_READ: |
| 198 DCHECK_EQ(OK, rv); |
| 199 rv = DoNetworkRead(); |
| 200 break; |
| 201 case State::NETWORK_READ_COMPLETE: |
| 202 rv = DoNetworkReadComplete(rv); |
| 203 break; |
| 204 case State::CACHE_WRITE_DATA: |
| 205 rv = DoCacheWriteData(rv); |
| 206 break; |
| 207 case State::CACHE_WRITE_DATA_COMPLETE: |
| 208 rv = DoCacheWriteDataComplete(rv); |
| 209 break; |
| 210 case State::CACHE_WRITE_TRUNCATED_RESPONSE: |
| 211 rv = DoCacheWriteTruncatedResponse(); |
| 212 break; |
| 213 case State::CACHE_WRITE_TRUNCATED_RESPONSE_COMPLETE: |
| 214 rv = DoCacheWriteTruncatedResponseComplete(rv); |
| 215 break; |
| 216 default: |
| 217 NOTREACHED() << "bad state"; |
| 218 rv = ERR_FAILED; |
| 219 break; |
| 220 } |
| 221 } while (next_state_ != State::NONE && rv != ERR_IO_PENDING); |
| 222 |
| 223 if (rv != ERR_IO_PENDING && !callback_.is_null()) { |
| 224 read_buf_ = NULL; // Release the buffer before invoking the callback. |
| 225 base::ResetAndReturn(&callback_).Run(rv); |
| 226 } |
| 227 return rv; |
| 228 } |
| 229 |
| 230 int HttpCache::Writers::DoNetworkRead() { |
| 231 next_state_ = State::NETWORK_READ_COMPLETE; |
| 232 return network_transaction_->Read(read_buf_.get(), io_buf_len_, io_callback_); |
| 233 } |
| 234 |
| 235 int HttpCache::Writers::DoNetworkReadComplete(int result) { |
| 236 if (result < 0) { |
| 237 OnNetworkReadFailure(result); |
| 238 return result; |
| 239 } |
| 240 |
| 241 next_state_ = State::CACHE_WRITE_DATA; |
| 242 return result; |
| 243 } |
| 244 |
| 245 void HttpCache::Writers::OnNetworkReadFailure(int result) { |
| 246 // At this point active_transaction_ may or may not be alive. |
| 247 // If no consumer, invoke entry processing here itself. |
| 248 if (!active_transaction_) |
| 249 cache_->DoneWithEntry(entry_, nullptr, true); |
| 250 } |
| 251 |
| 252 int HttpCache::Writers::DoCacheWriteData(int num_bytes) { |
| 253 next_state_ = State::CACHE_WRITE_DATA_COMPLETE; |
| 254 write_len_ = num_bytes; |
| 255 if (!num_bytes || network_read_only_) |
| 256 return num_bytes; |
| 257 |
| 258 int current_size = entry_->disk_entry->GetDataSize(kResponseContentIndex); |
| 259 return WriteToEntry(kResponseContentIndex, current_size, read_buf_.get(), |
| 260 num_bytes, io_callback_); |
| 261 } |
| 262 |
| 263 int HttpCache::Writers::WriteToEntry(int index, |
| 264 int offset, |
| 265 IOBuffer* data, |
| 266 int data_len, |
| 267 const CompletionCallback& callback) { |
| 268 int rv = 0; |
| 269 |
| 270 PartialData* partial = nullptr; |
| 271 // Transaction must be alive if this is a partial request. |
| 272 // Todo(shivanisha): When partial requests support parallel writing, this |
| 273 // assumption will not be true. |
| 274 if (active_transaction_) |
| 275 partial = active_transaction_->partial(); |
| 276 |
| 277 if (!partial || !data_len) { |
| 278 rv = entry_->disk_entry->WriteData(index, offset, data, data_len, callback, |
| 279 true); |
| 280 } else { |
| 281 rv = partial->CacheWrite(entry_->disk_entry, data, data_len, callback); |
| 282 } |
| 283 return rv; |
| 284 } |
| 285 |
| 286 int HttpCache::Writers::DoCacheWriteDataComplete(int result) { |
| 287 if (result != write_len_) { |
| 288 OnCacheWriteFailure(); |
| 289 |
| 290 // |active_transaction_| can continue reading from the network. |
| 291 result = write_len_; |
| 292 } else { |
| 293 OnDataReceived(result); |
| 294 } |
| 295 return result; |
| 296 } |
| 297 |
| 298 int HttpCache::Writers::DoCacheWriteTruncatedResponse() { |
| 299 next_state_ = State::CACHE_WRITE_TRUNCATED_RESPONSE_COMPLETE; |
| 300 return WriteResponseInfo(true); |
| 301 } |
| 302 |
| 303 int HttpCache::Writers::DoCacheWriteTruncatedResponseComplete(int result) { |
| 304 if (result != io_buf_len_) { |
| 305 DLOG(ERROR) << "failed to write response info to cache"; |
| 306 } |
| 307 truncated_ = true; |
| 308 return OK; |
| 309 } |
| 310 |
| 311 void HttpCache::Writers::OnDataReceived(int result) { |
| 312 if (result == 0 && !active_transaction_) { |
| 313 // Check if the response is actually completed or if not, attempt to mark |
| 314 // the entry as truncated. |
| 315 if (IsResponseCompleted()) { |
| 316 ProcessWaitingForReadTransactions(result); |
| 317 cache_->DoneWritingToEntry(entry_, true); |
| 318 } else { |
| 319 OnNetworkReadFailure(result); |
| 320 return; |
| 321 } |
| 322 } |
| 323 |
| 324 // Save the data in all the waiting transactions' read buffers. |
| 325 for (auto it = waiting_for_read_.begin(); it != waiting_for_read_.end(); |
| 326 it++) { |
| 327 it->write_len = std::min(it->read_buf_len, result); |
| 328 memcpy(it->read_buf->data(), read_buf_->data(), it->write_len); |
| 329 } |
| 330 |
| 331 // Notify waiting_for_read_. Tasks will be posted for all the |
| 332 // transactions. |
| 333 ProcessWaitingForReadTransactions(write_len_); |
| 334 |
| 335 if (result > 0) { // not the end of response |
| 336 active_transaction_ = nullptr; |
| 337 return; |
| 338 } |
| 339 DCHECK_EQ(result, 0); |
| 340 if (!active_transaction_) |
| 341 cache_->DoneWritingToEntry(entry_, true); |
| 342 } |
| 343 |
| 344 void HttpCache::Writers::OnCacheWriteFailure() { |
| 345 network_read_only_ = true; |
| 346 |
| 347 // Call the cache_ function here even if active_transaction_ is alive because |
| 348 // it wouldn't know if this was an error case, since it gets a positive result |
| 349 // back. This will in turn ProcessFailure. |
| 350 // TODO(shivanisha) : Also pass active_transaction_ and |
| 351 // ERR_CACHE_WRITE_FAILURE to DoneWritingToEntry on integration so that it can |
| 352 // be passed back in ProcessFailure. |
| 353 cache_->DoneWritingToEntry(entry_, false); |
| 354 } |
| 355 |
| 356 bool HttpCache::Writers::IsResponseCompleted() { |
| 357 int current_size = entry_->disk_entry->GetDataSize(kResponseContentIndex); |
| 358 const HttpResponseInfo* response_info = |
| 359 network_transaction_->GetResponseInfo(); |
| 360 int64_t content_length = response_info->headers->GetContentLength(); |
| 361 if ((content_length >= 0 && content_length <= current_size) || |
| 362 content_length < 0) |
| 363 return true; |
| 364 return false; |
| 365 } |
| 366 |
| 367 int HttpCache::Writers::WriteResponseInfo(bool truncated) { |
| 368 // When writing headers, we normally only write the non-transient headers. |
| 369 const HttpResponseInfo* response = network_transaction_->GetResponseInfo(); |
| 370 bool skip_transient_headers = true; |
| 371 scoped_refptr<PickledIOBuffer> data(new PickledIOBuffer()); |
| 372 response->Persist(data->pickle(), skip_transient_headers, truncated); |
| 373 data->Done(); |
| 374 io_buf_len_ = data->pickle()->size(); |
| 375 return entry_->disk_entry->WriteData(kResponseInfoIndex, 0, data.get(), |
| 376 io_buf_len_, io_callback_, truncated); |
| 377 } |
| 378 |
| 379 void HttpCache::Writers::ProcessWaitingForReadTransactions(int result) { |
| 380 for (auto it = waiting_for_read_.begin(); it != waiting_for_read_.end(); |
| 381 it++) { |
| 382 Transaction* transaction = it->transaction; |
| 383 if (result > 0) { // success |
| 384 result = it->write_len; |
| 385 } else { |
| 386 // If its response completion or failure, this transaction needs to be |
| 387 // removed. |
| 388 all_writers_.erase(transaction); |
| 389 } |
| 390 // Post task to notify transaction. |
| 391 base::ThreadTaskRunnerHandle::Get()->PostTask( |
| 392 FROM_HERE, base::Bind(it->callback, result)); |
| 393 } |
| 394 waiting_for_read_.clear(); |
| 395 } |
| 396 |
| 397 void HttpCache::Writers::SetIdleWritersFailState(int result) { |
| 398 // Since this is only for idle transactions, all waiting_for_read_ and |
| 399 // active_transaction_ should be empty. |
| 400 DCHECK(waiting_for_read_.empty()); |
| 401 DCHECK(!active_transaction_); |
| 402 for (auto* transaction : all_writers_) |
| 403 transaction->SetSharedWritingFailState(result); |
| 404 all_writers_.clear(); |
| 405 } |
| 406 |
| 407 RequestPriority HttpCache::Writers::getCurrentHighestPriority() { |
| 408 RequestPriority priority = MINIMUM_PRIORITY; |
| 409 for (auto* transaction : all_writers_) |
| 410 priority = std::max(transaction->priority(), priority); |
| 411 return priority; |
| 412 } |
| 413 |
| 414 void HttpCache::Writers::OnIOComplete(int result) { |
| 415 DoLoop(result); |
| 416 } |
| 417 |
| 418 } // namespace net |
OLD | NEW |