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
17namespace 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.
39struct DiskCacheBasedQuicServerInfo::CacheOperationDataShim {
40  CacheOperationDataShim() : backend(NULL), entry(NULL) {}
41
42  disk_cache::Backend* backend;
43  disk_cache::Entry* entry;
44};
45
46DiskCacheBasedQuicServerInfo::DiskCacheBasedQuicServerInfo(
47    const QuicServerId& server_id,
48    HttpCache* http_cache)
49    : QuicServerInfo(server_id),
50      data_shim_(new CacheOperationDataShim()),
51      state_(GET_BACKEND),
52      ready_(false),
53      found_entry_(false),
54      server_id_(server_id),
55      http_cache_(http_cache),
56      backend_(NULL),
57      entry_(NULL),
58      weak_factory_(this) {
59      io_callback_ =
60          base::Bind(&DiskCacheBasedQuicServerInfo::OnIOComplete,
61                     weak_factory_.GetWeakPtr(),
62                     base::Owned(data_shim_));  // Ownership assigned.
63}
64
65void DiskCacheBasedQuicServerInfo::Start() {
66  DCHECK(CalledOnValidThread());
67  DCHECK_EQ(GET_BACKEND, state_);
68  DoLoop(OK);
69}
70
71int 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
90bool DiskCacheBasedQuicServerInfo::IsDataReady() {
91  return ready_;
92}
93
94bool 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
100void 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
116DiskCacheBasedQuicServerInfo::~DiskCacheBasedQuicServerInfo() {
117  DCHECK(user_callback_.is_null());
118  if (entry_)
119    entry_->Close();
120}
121
122std::string DiskCacheBasedQuicServerInfo::key() const {
123  return "quicserverinfo:" + server_id_.ToString();
124}
125
126void 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
137int 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
185int 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
195int 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
207int 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
215int DiskCacheBasedQuicServerInfo::DoWriteComplete(int rv) {
216  // Keep the entry open for future writes.
217  new_data_.clear();
218  state_ = NONE;
219  return OK;
220}
221
222int DiskCacheBasedQuicServerInfo::DoCreateOrOpenComplete(int rv) {
223  if (rv != OK) {
224    state_ = SET_DONE;
225  } else {
226    if (!entry_)
227      entry_ = data_shim_->entry;
228    state_ = WRITE;
229  }
230  return OK;
231}
232
233int DiskCacheBasedQuicServerInfo::DoGetBackend() {
234  state_ = GET_BACKEND_COMPLETE;
235  return http_cache_->GetBackend(&data_shim_->backend, io_callback_);
236}
237
238int DiskCacheBasedQuicServerInfo::DoOpen() {
239  state_ = OPEN_COMPLETE;
240  return backend_->OpenEntry(key(), &data_shim_->entry, io_callback_);
241}
242
243int DiskCacheBasedQuicServerInfo::DoRead() {
244  const int32 size = entry_->GetDataSize(0 /* index */);
245  if (!size) {
246    state_ = WAIT_FOR_DATA_READY_DONE;
247    return OK;
248  }
249
250  read_buffer_ = new IOBuffer(size);
251  state_ = READ_COMPLETE;
252  return entry_->ReadData(
253      0 /* index */, 0 /* offset */, read_buffer_.get(), size, io_callback_);
254}
255
256int DiskCacheBasedQuicServerInfo::DoWrite() {
257  write_buffer_ = new IOBuffer(new_data_.size());
258  memcpy(write_buffer_->data(), new_data_.data(), new_data_.size());
259  state_ = WRITE_COMPLETE;
260
261  return entry_->WriteData(0 /* index */,
262                           0 /* offset */,
263                           write_buffer_.get(),
264                           new_data_.size(),
265                           io_callback_,
266                           true /* truncate */);
267}
268
269int DiskCacheBasedQuicServerInfo::DoCreateOrOpen() {
270  state_ = CREATE_OR_OPEN_COMPLETE;
271  if (entry_)
272    return OK;
273
274  if (found_entry_) {
275    return backend_->OpenEntry(key(), &data_shim_->entry, io_callback_);
276  }
277
278  return backend_->CreateEntry(key(), &data_shim_->entry, io_callback_);
279}
280
281int DiskCacheBasedQuicServerInfo::DoWaitForDataReadyDone() {
282  DCHECK(!ready_);
283  state_ = NONE;
284  ready_ = true;
285  // We close the entry because, if we shutdown before ::Persist is called,
286  // then we might leak a cache reference, which causes a DCHECK on shutdown.
287  if (entry_)
288    entry_->Close();
289  entry_ = NULL;
290  Parse(data_);
291  return OK;
292}
293
294int DiskCacheBasedQuicServerInfo::DoSetDone() {
295  if (entry_)
296    entry_->Close();
297  entry_ = NULL;
298  new_data_.clear();
299  state_ = NONE;
300  return OK;
301}
302
303}  // namespace net
304