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 |