Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(284)

Side by Side Diff: net/http/http_cache_writers.cc

Issue 2886483002: Adds a new class HttpCache::Writers for multiple cache transactions reading from the network. (Closed)
Patch Set: rdsmith feedback Created 3 years, 7 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(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
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698