• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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