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