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(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() const { |
| 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 DCHECK_EQ(next_state_, State::NONE); |
| 163 next_state_ = State::CACHE_WRITE_TRUNCATED_RESPONSE; |
| 164 DoLoop(OK); |
| 165 } |
| 166 |
| 167 LoadState HttpCache::Writers::GetWriterLoadState() { |
| 168 return network_transaction_->GetLoadState(); |
| 169 } |
| 170 |
| 171 HttpCache::Writers::WaitingForRead::WaitingForRead( |
| 172 Transaction* cache_transaction, |
| 173 scoped_refptr<IOBuffer> buf, |
| 174 int len, |
| 175 const CompletionCallback& consumer_callback) |
| 176 : transaction(cache_transaction), |
| 177 read_buf(std::move(buf)), |
| 178 read_buf_len(len), |
| 179 write_len(0), |
| 180 callback(consumer_callback) { |
| 181 DCHECK(cache_transaction); |
| 182 DCHECK(buf); |
| 183 DCHECK_GT(len, 0); |
| 184 DCHECK(!consumer_callback.is_null()); |
| 185 } |
| 186 |
| 187 HttpCache::Writers::WaitingForRead::~WaitingForRead() {} |
| 188 HttpCache::Writers::WaitingForRead::WaitingForRead(const WaitingForRead&) = |
| 189 default; |
| 190 |
| 191 int HttpCache::Writers::DoLoop(int result) { |
| 192 DCHECK(next_state_ != State::NONE); |
| 193 int rv = result; |
| 194 do { |
| 195 State state = next_state_; |
| 196 next_state_ = State::NONE; |
| 197 switch (state) { |
| 198 case State::NETWORK_READ: |
| 199 DCHECK_EQ(OK, rv); |
| 200 rv = DoNetworkRead(); |
| 201 break; |
| 202 case State::NETWORK_READ_COMPLETE: |
| 203 rv = DoNetworkReadComplete(rv); |
| 204 break; |
| 205 case State::CACHE_WRITE_DATA: |
| 206 rv = DoCacheWriteData(rv); |
| 207 break; |
| 208 case State::CACHE_WRITE_DATA_COMPLETE: |
| 209 rv = DoCacheWriteDataComplete(rv); |
| 210 break; |
| 211 case State::CACHE_WRITE_TRUNCATED_RESPONSE: |
| 212 rv = DoCacheWriteTruncatedResponse(); |
| 213 break; |
| 214 case State::CACHE_WRITE_TRUNCATED_RESPONSE_COMPLETE: |
| 215 rv = DoCacheWriteTruncatedResponseComplete(rv); |
| 216 break; |
| 217 default: |
| 218 NOTREACHED() << "bad state"; |
| 219 rv = ERR_FAILED; |
| 220 break; |
| 221 } |
| 222 } while (next_state_ != State::NONE && rv != ERR_IO_PENDING); |
| 223 |
| 224 if (rv != ERR_IO_PENDING && !callback_.is_null()) { |
| 225 read_buf_ = NULL; // Release the buffer before invoking the callback. |
| 226 base::ResetAndReturn(&callback_).Run(rv); |
| 227 } |
| 228 return rv; |
| 229 } |
| 230 |
| 231 int HttpCache::Writers::DoNetworkRead() { |
| 232 next_state_ = State::NETWORK_READ_COMPLETE; |
| 233 return network_transaction_->Read(read_buf_.get(), io_buf_len_, io_callback_); |
| 234 } |
| 235 |
| 236 int HttpCache::Writers::DoNetworkReadComplete(int result) { |
| 237 if (result < 0) { |
| 238 OnNetworkReadFailure(result); |
| 239 return result; |
| 240 } |
| 241 |
| 242 next_state_ = State::CACHE_WRITE_DATA; |
| 243 return result; |
| 244 } |
| 245 |
| 246 void HttpCache::Writers::OnNetworkReadFailure(int result) { |
| 247 // At this point active_transaction_ may or may not be alive. |
| 248 // If no consumer, invoke entry processing here itself. |
| 249 if (!active_transaction_) |
| 250 cache_->DoneWithEntry(entry_, nullptr, true); |
| 251 } |
| 252 |
| 253 int HttpCache::Writers::DoCacheWriteData(int num_bytes) { |
| 254 next_state_ = State::CACHE_WRITE_DATA_COMPLETE; |
| 255 write_len_ = num_bytes; |
| 256 if (!num_bytes || network_read_only_) |
| 257 return num_bytes; |
| 258 |
| 259 int current_size = entry_->disk_entry->GetDataSize(kResponseContentIndex); |
| 260 return WriteToEntry(kResponseContentIndex, current_size, read_buf_.get(), |
| 261 num_bytes, io_callback_); |
| 262 } |
| 263 |
| 264 int HttpCache::Writers::WriteToEntry(int index, |
| 265 int offset, |
| 266 IOBuffer* data, |
| 267 int data_len, |
| 268 const CompletionCallback& callback) { |
| 269 int rv = 0; |
| 270 |
| 271 PartialData* partial = nullptr; |
| 272 // Transaction must be alive if this is a partial request. |
| 273 // Todo(shivanisha): When partial requests support parallel writing, this |
| 274 // assumption will not be true. |
| 275 if (active_transaction_) |
| 276 partial = active_transaction_->partial(); |
| 277 |
| 278 if (!partial || !data_len) { |
| 279 rv = entry_->disk_entry->WriteData(index, offset, data, data_len, callback, |
| 280 true); |
| 281 } else { |
| 282 rv = partial->CacheWrite(entry_->disk_entry, data, data_len, callback); |
| 283 } |
| 284 return rv; |
| 285 } |
| 286 |
| 287 int HttpCache::Writers::DoCacheWriteDataComplete(int result) { |
| 288 if (result != write_len_) { |
| 289 OnCacheWriteFailure(); |
| 290 |
| 291 // |active_transaction_| can continue reading from the network. |
| 292 result = write_len_; |
| 293 } else { |
| 294 OnDataReceived(result); |
| 295 } |
| 296 return result; |
| 297 } |
| 298 |
| 299 int HttpCache::Writers::DoCacheWriteTruncatedResponse() { |
| 300 next_state_ = State::CACHE_WRITE_TRUNCATED_RESPONSE_COMPLETE; |
| 301 return WriteResponseInfo(true); |
| 302 } |
| 303 |
| 304 int HttpCache::Writers::DoCacheWriteTruncatedResponseComplete(int result) { |
| 305 if (result != io_buf_len_) { |
| 306 DLOG(ERROR) << "failed to write response info to cache"; |
| 307 } |
| 308 truncated_ = true; |
| 309 return OK; |
| 310 } |
| 311 |
| 312 void HttpCache::Writers::OnDataReceived(int result) { |
| 313 if (result == 0 && !active_transaction_) { |
| 314 // Check if the response is actually completed or if not, attempt to mark |
| 315 // the entry as truncated. |
| 316 if (IsResponseCompleted()) { |
| 317 ProcessWaitingForReadTransactions(result); |
| 318 cache_->DoneWritingToEntry(entry_, true); |
| 319 } else { |
| 320 OnNetworkReadFailure(result); |
| 321 return; |
| 322 } |
| 323 } |
| 324 |
| 325 // Save the data in all the waiting transactions' read buffers. |
| 326 for (auto it = waiting_for_read_.begin(); it != waiting_for_read_.end(); |
| 327 it++) { |
| 328 it->write_len = std::min(it->read_buf_len, result); |
| 329 memcpy(it->read_buf->data(), read_buf_->data(), it->write_len); |
| 330 } |
| 331 |
| 332 // Notify waiting_for_read_. Tasks will be posted for all the |
| 333 // transactions. |
| 334 ProcessWaitingForReadTransactions(write_len_); |
| 335 |
| 336 if (result > 0) { // not the end of response |
| 337 active_transaction_ = nullptr; |
| 338 return; |
| 339 } |
| 340 DCHECK_EQ(result, 0); |
| 341 if (!active_transaction_) |
| 342 cache_->DoneWritingToEntry(entry_, true); |
| 343 } |
| 344 |
| 345 void HttpCache::Writers::OnCacheWriteFailure() { |
| 346 network_read_only_ = true; |
| 347 |
| 348 // Call the cache_ function here even if active_transaction_ is alive because |
| 349 // it wouldn't know if this was an error case, since it gets a positive result |
| 350 // back. This will in turn ProcessFailure. |
| 351 // TODO(shivanisha) : Also pass active_transaction_ and |
| 352 // ERR_CACHE_WRITE_FAILURE to DoneWritingToEntry on integration so that it can |
| 353 // be passed back in ProcessFailure. |
| 354 cache_->DoneWritingToEntry(entry_, false); |
| 355 } |
| 356 |
| 357 bool HttpCache::Writers::IsResponseCompleted() { |
| 358 int current_size = entry_->disk_entry->GetDataSize(kResponseContentIndex); |
| 359 const HttpResponseInfo* response_info = |
| 360 network_transaction_->GetResponseInfo(); |
| 361 int64_t content_length = response_info->headers->GetContentLength(); |
| 362 if ((content_length >= 0 && content_length <= current_size) || |
| 363 content_length < 0) |
| 364 return true; |
| 365 return false; |
| 366 } |
| 367 |
| 368 int HttpCache::Writers::WriteResponseInfo(bool truncated) { |
| 369 // When writing headers, we normally only write the non-transient headers. |
| 370 const HttpResponseInfo* response = network_transaction_->GetResponseInfo(); |
| 371 bool skip_transient_headers = true; |
| 372 scoped_refptr<PickledIOBuffer> data(new PickledIOBuffer()); |
| 373 response->Persist(data->pickle(), skip_transient_headers, truncated); |
| 374 data->Done(); |
| 375 io_buf_len_ = data->pickle()->size(); |
| 376 return entry_->disk_entry->WriteData(kResponseInfoIndex, 0, data.get(), |
| 377 io_buf_len_, io_callback_, truncated); |
| 378 } |
| 379 |
| 380 void HttpCache::Writers::ProcessWaitingForReadTransactions(int result) { |
| 381 for (auto it = waiting_for_read_.begin(); it != waiting_for_read_.end(); |
| 382 it++) { |
| 383 Transaction* transaction = it->transaction; |
| 384 if (result > 0) { // success |
| 385 result = it->write_len; |
| 386 } else { |
| 387 // If its response completion or failure, this transaction needs to be |
| 388 // removed. |
| 389 all_writers_.erase(transaction); |
| 390 } |
| 391 // Post task to notify transaction. |
| 392 base::ThreadTaskRunnerHandle::Get()->PostTask( |
| 393 FROM_HERE, base::Bind(it->callback, result)); |
| 394 } |
| 395 waiting_for_read_.clear(); |
| 396 } |
| 397 |
| 398 void HttpCache::Writers::SetIdleWritersFailState(int result) { |
| 399 // Since this is only for idle transactions, all waiting_for_read_ and |
| 400 // active_transaction_ should be empty. |
| 401 DCHECK(waiting_for_read_.empty()); |
| 402 DCHECK(!active_transaction_); |
| 403 for (auto* transaction : all_writers_) |
| 404 transaction->SetSharedWritingFailState(result); |
| 405 all_writers_.clear(); |
| 406 } |
| 407 |
| 408 RequestPriority HttpCache::Writers::GetCurrentHighestPriority() { |
| 409 RequestPriority priority = MINIMUM_PRIORITY; |
| 410 for (auto* transaction : all_writers_) |
| 411 priority = std::max(transaction->priority(), priority); |
| 412 return priority; |
| 413 } |
| 414 |
| 415 void HttpCache::Writers::OnIOComplete(int result) { |
| 416 DoLoop(result); |
| 417 } |
| 418 |
| 419 } // namespace net |
OLD | NEW |