1 // Copyright 2014 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/disk_cache_based_quic_server_info.h"
6
7 #include "base/bind.h"
8 #include "base/callback.h"
9 #include "base/logging.h"
10 #include "net/base/completion_callback.h"
11 #include "net/base/io_buffer.h"
12 #include "net/base/net_errors.h"
13 #include "net/http/http_cache.h"
14 #include "net/http/http_network_session.h"
15 #include "net/quic/quic_server_id.h"
16
17 namespace net {
18
19 // Some APIs inside disk_cache take a handle that the caller must keep alive
20 // until the API has finished its asynchronous execution.
21 //
22 // Unfortunately, DiskCacheBasedQuicServerInfo may be deleted before the
23 // operation completes causing a use-after-free.
24 //
25 // This data shim struct is meant to provide a location for the disk_cache
26 // APIs to write into even if the originating DiskCacheBasedQuicServerInfo
27 // object has been deleted. The lifetime for instances of this struct
28 // should be bound to the CompletionCallback that is passed to the disk_cache
29 // API. We do this by binding an instance of this struct to an unused
30 // parameter for OnIOComplete() using base::Owned().
31 //
32 // This is a hack. A better fix is to make it so that the disk_cache APIs
33 // take a Callback to a mutator for setting the output value rather than
34 // writing into a raw handle. Then the caller can just pass in a Callback
35 // bound to WeakPtr for itself. This callback would correctly "no-op" itself
36 // when the DiskCacheBasedQuicServerInfo object is deleted.
37 //
38 // TODO(ajwong): Change disk_cache's API to return results via Callback.
39 struct DiskCacheBasedQuicServerInfo::CacheOperationDataShim {
CacheOperationDataShimnet::DiskCacheBasedQuicServerInfo::CacheOperationDataShim40 CacheOperationDataShim() : backend(NULL), entry(NULL) {}
41
42 disk_cache::Backend* backend;
43 disk_cache::Entry* entry;
44 };
45
DiskCacheBasedQuicServerInfo(const QuicServerId & server_id,HttpCache * http_cache)46 DiskCacheBasedQuicServerInfo::DiskCacheBasedQuicServerInfo(
47 const QuicServerId& server_id,
48 HttpCache* http_cache)
49 : QuicServerInfo(server_id),
50 weak_factory_(this),
51 data_shim_(new CacheOperationDataShim()),
52 io_callback_(
53 base::Bind(&DiskCacheBasedQuicServerInfo::OnIOComplete,
54 weak_factory_.GetWeakPtr(),
55 base::Owned(data_shim_))), // Ownership assigned.
56 state_(GET_BACKEND),
57 ready_(false),
58 found_entry_(false),
59 server_id_(server_id),
60 http_cache_(http_cache),
61 backend_(NULL),
62 entry_(NULL) {
63 }
64
Start()65 void DiskCacheBasedQuicServerInfo::Start() {
66 DCHECK(CalledOnValidThread());
67 DCHECK_EQ(GET_BACKEND, state_);
68 DoLoop(OK);
69 }
70
WaitForDataReady(const CompletionCallback & callback)71 int DiskCacheBasedQuicServerInfo::WaitForDataReady(
72 const CompletionCallback& callback) {
73 DCHECK(CalledOnValidThread());
74 DCHECK_NE(GET_BACKEND, state_);
75
76 if (ready_)
77 return OK;
78
79 if (!callback.is_null()) {
80 // Prevent a new callback for WaitForDataReady overwriting an existing
81 // pending callback (|user_callback_|).
82 if (!user_callback_.is_null())
83 return ERR_INVALID_ARGUMENT;
84 user_callback_ = callback;
85 }
86
87 return ERR_IO_PENDING;
88 }
89
IsDataReady()90 bool DiskCacheBasedQuicServerInfo::IsDataReady() {
91 return ready_;
92 }
93
IsReadyToPersist()94 bool DiskCacheBasedQuicServerInfo::IsReadyToPersist() {
95 // The data can be persisted if it has been loaded from the disk cache
96 // and there are no pending writes.
97 return ready_ && new_data_.empty();
98 }
99
Persist()100 void DiskCacheBasedQuicServerInfo::Persist() {
101 DCHECK(CalledOnValidThread());
102 DCHECK_NE(GET_BACKEND, state_);
103
104 DCHECK(new_data_.empty());
105 CHECK(ready_);
106 DCHECK(user_callback_.is_null());
107 new_data_ = Serialize();
108
109 if (!backend_)
110 return;
111
112 state_ = CREATE_OR_OPEN;
113 DoLoop(OK);
114 }
115
~DiskCacheBasedQuicServerInfo()116 DiskCacheBasedQuicServerInfo::~DiskCacheBasedQuicServerInfo() {
117 DCHECK(user_callback_.is_null());
118 if (entry_)
119 entry_->Close();
120 }
121
key() const122 std::string DiskCacheBasedQuicServerInfo::key() const {
123 return "quicserverinfo:" + server_id_.ToString();
124 }
125
OnIOComplete(CacheOperationDataShim * unused,int rv)126 void DiskCacheBasedQuicServerInfo::OnIOComplete(CacheOperationDataShim* unused,
127 int rv) {
128 DCHECK_NE(NONE, state_);
129 rv = DoLoop(rv);
130 if (rv != ERR_IO_PENDING && !user_callback_.is_null()) {
131 CompletionCallback callback = user_callback_;
132 user_callback_.Reset();
133 callback.Run(rv);
134 }
135 }
136
DoLoop(int rv)137 int DiskCacheBasedQuicServerInfo::DoLoop(int rv) {
138 do {
139 switch (state_) {
140 case GET_BACKEND:
141 rv = DoGetBackend();
142 break;
143 case GET_BACKEND_COMPLETE:
144 rv = DoGetBackendComplete(rv);
145 break;
146 case OPEN:
147 rv = DoOpen();
148 break;
149 case OPEN_COMPLETE:
150 rv = DoOpenComplete(rv);
151 break;
152 case READ:
153 rv = DoRead();
154 break;
155 case READ_COMPLETE:
156 rv = DoReadComplete(rv);
157 break;
158 case WAIT_FOR_DATA_READY_DONE:
159 rv = DoWaitForDataReadyDone();
160 break;
161 case CREATE_OR_OPEN:
162 rv = DoCreateOrOpen();
163 break;
164 case CREATE_OR_OPEN_COMPLETE:
165 rv = DoCreateOrOpenComplete(rv);
166 break;
167 case WRITE:
168 rv = DoWrite();
169 break;
170 case WRITE_COMPLETE:
171 rv = DoWriteComplete(rv);
172 break;
173 case SET_DONE:
174 rv = DoSetDone();
175 break;
176 default:
177 rv = OK;
178 NOTREACHED();
179 }
180 } while (rv != ERR_IO_PENDING && state_ != NONE);
181
182 return rv;
183 }
184
DoGetBackendComplete(int rv)185 int DiskCacheBasedQuicServerInfo::DoGetBackendComplete(int rv) {
186 if (rv == OK) {
187 backend_ = data_shim_->backend;
188 state_ = OPEN;
189 } else {
190 state_ = WAIT_FOR_DATA_READY_DONE;
191 }
192 return OK;
193 }
194
DoOpenComplete(int rv)195 int DiskCacheBasedQuicServerInfo::DoOpenComplete(int rv) {
196 if (rv == OK) {
197 entry_ = data_shim_->entry;
198 state_ = READ;
199 found_entry_ = true;
200 } else {
201 state_ = WAIT_FOR_DATA_READY_DONE;
202 }
203
204 return OK;
205 }
206
DoReadComplete(int rv)207 int DiskCacheBasedQuicServerInfo::DoReadComplete(int rv) {
208 if (rv > 0)
209 data_.assign(read_buffer_->data(), rv);
210
211 state_ = WAIT_FOR_DATA_READY_DONE;
212 return OK;
213 }
214
DoWriteComplete(int rv)215 int DiskCacheBasedQuicServerInfo::DoWriteComplete(int rv) {
216 state_ = SET_DONE;
217 return OK;
218 }
219
DoCreateOrOpenComplete(int rv)220 int DiskCacheBasedQuicServerInfo::DoCreateOrOpenComplete(int rv) {
221 if (rv != OK) {
222 state_ = SET_DONE;
223 } else {
224 entry_ = data_shim_->entry;
225 state_ = WRITE;
226 }
227 return OK;
228 }
229
DoGetBackend()230 int DiskCacheBasedQuicServerInfo::DoGetBackend() {
231 state_ = GET_BACKEND_COMPLETE;
232 return http_cache_->GetBackend(&data_shim_->backend, io_callback_);
233 }
234
DoOpen()235 int DiskCacheBasedQuicServerInfo::DoOpen() {
236 state_ = OPEN_COMPLETE;
237 return backend_->OpenEntry(key(), &data_shim_->entry, io_callback_);
238 }
239
DoRead()240 int DiskCacheBasedQuicServerInfo::DoRead() {
241 const int32 size = entry_->GetDataSize(0 /* index */);
242 if (!size) {
243 state_ = WAIT_FOR_DATA_READY_DONE;
244 return OK;
245 }
246
247 read_buffer_ = new IOBuffer(size);
248 state_ = READ_COMPLETE;
249 return entry_->ReadData(
250 0 /* index */, 0 /* offset */, read_buffer_, size, io_callback_);
251 }
252
DoWrite()253 int DiskCacheBasedQuicServerInfo::DoWrite() {
254 write_buffer_ = new IOBuffer(new_data_.size());
255 memcpy(write_buffer_->data(), new_data_.data(), new_data_.size());
256 state_ = WRITE_COMPLETE;
257
258 return entry_->WriteData(
259 0 /* index */, 0 /* offset */, write_buffer_, new_data_.size(),
260 io_callback_, true /* truncate */);
261 }
262
DoCreateOrOpen()263 int DiskCacheBasedQuicServerInfo::DoCreateOrOpen() {
264 DCHECK(entry_ == NULL);
265 state_ = CREATE_OR_OPEN_COMPLETE;
266 if (found_entry_) {
267 return backend_->OpenEntry(key(), &data_shim_->entry, io_callback_);
268 }
269
270 return backend_->CreateEntry(key(), &data_shim_->entry, io_callback_);
271 }
272
DoWaitForDataReadyDone()273 int DiskCacheBasedQuicServerInfo::DoWaitForDataReadyDone() {
274 DCHECK(!ready_);
275 state_ = NONE;
276 ready_ = true;
277 // We close the entry because, if we shutdown before ::Persist is called,
278 // then we might leak a cache reference, which causes a DCHECK on shutdown.
279 if (entry_)
280 entry_->Close();
281 entry_ = NULL;
282 Parse(data_);
283 return OK;
284 }
285
DoSetDone()286 int DiskCacheBasedQuicServerInfo::DoSetDone() {
287 if (entry_)
288 entry_->Close();
289 entry_ = NULL;
290 new_data_.clear();
291 state_ = NONE;
292 return OK;
293 }
294
295 } // namespace net
296