1f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)// Copyright 2013 The Chromium Authors. All rights reserved.
2f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)// Use of this source code is governed by a BSD-style license that can be
3f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)// found in the LICENSE file.
4f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)
5f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)#include "content/browser/loader/detachable_resource_handler.h"
6f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)
7f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)#include "base/logging.h"
8f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)#include "base/time/time.h"
9f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)#include "content/browser/loader/resource_request_info_impl.h"
10f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)#include "net/base/io_buffer.h"
11f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)#include "net/base/net_errors.h"
121320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci#include "net/url_request/url_request.h"
13f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)#include "net/url_request/url_request_status.h"
14f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)
15f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)namespace {
16f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)// This matches the maximum allocation size of AsyncResourceHandler.
17f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)const int kReadBufSize = 32 * 1024;
180529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch}
19f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)
20f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)namespace content {
21f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)
22f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)DetachableResourceHandler::DetachableResourceHandler(
23f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    net::URLRequest* request,
24f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    base::TimeDelta cancel_delay,
25f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    scoped_ptr<ResourceHandler> next_handler)
26f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    : ResourceHandler(request),
27f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)      next_handler_(next_handler.Pass()),
28f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)      cancel_delay_(cancel_delay),
29f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)      is_deferred_(false),
300529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch      is_finished_(false) {
31f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  GetRequestInfo()->set_detachable_handler(this);
32f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)}
33f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)
34f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)DetachableResourceHandler::~DetachableResourceHandler() {
35f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  // Cleanup back-pointer stored on the request info.
36f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  GetRequestInfo()->set_detachable_handler(NULL);
37f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)}
38f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)
39f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)void DetachableResourceHandler::Detach() {
40f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  if (is_detached())
41f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    return;
42f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)
43f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  if (!is_finished_) {
44f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    // Simulate a cancel on the next handler before destroying it.
45f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    net::URLRequestStatus status(net::URLRequestStatus::CANCELED,
46f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)                                 net::ERR_ABORTED);
47f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    bool defer_ignored = false;
48cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)    next_handler_->OnResponseCompleted(status, std::string(), &defer_ignored);
49f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    DCHECK(!defer_ignored);
50f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    // If |next_handler_| were to defer its shutdown in OnResponseCompleted,
51f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    // this would destroy it anyway. Fortunately, AsyncResourceHandler never
52f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    // does this anyway, so DCHECK it. BufferedResourceHandler and RVH shutdown
53f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    // already ignore deferred ResourceHandler shutdown, but
54f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    // DetachableResourceHandler and the detach-on-renderer-cancel logic
55f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    // introduces a case where this occurs when the renderer cancels a resource.
56f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  }
57f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  // A OnWillRead / OnReadCompleted pair may still be in progress, but
58f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  // OnWillRead passes back a scoped_refptr, so downstream handler's buffer will
59f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  // survive long enough to complete that read. From there, future reads will
60f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  // drain into |read_buffer_|. (If |next_handler_| is an AsyncResourceHandler,
61f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  // the net::IOBuffer takes a reference to the ResourceBuffer which owns the
62f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  // shared memory.)
63f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  next_handler_.reset();
64f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)
65f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  // Time the request out if it takes too long.
66f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  detached_timer_.reset(new base::OneShotTimer<DetachableResourceHandler>());
67f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  detached_timer_->Start(
680529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch      FROM_HERE, cancel_delay_, this, &DetachableResourceHandler::Cancel);
69f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)
70f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  // Resume if necessary. The request may have been deferred, say, waiting on a
71f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  // full buffer in AsyncResourceHandler. Now that it has been detached, resume
72f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  // and drain it.
731320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  if (is_deferred_) {
741320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    // The nested ResourceHandler may have logged that it's blocking the
751320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    // request.  Log it as no longer doing so, to avoid a DCHECK on resume.
761320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    request()->LogUnblocked();
77f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    Resume();
781320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  }
79f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)}
80f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)
81f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)void DetachableResourceHandler::SetController(ResourceController* controller) {
82f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  ResourceHandler::SetController(controller);
83f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)
84f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  // Intercept the ResourceController for downstream handlers to keep track of
85f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  // whether the request is deferred.
86f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  if (next_handler_)
87f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    next_handler_->SetController(this);
88f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)}
89f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)
90cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)bool DetachableResourceHandler::OnUploadProgress(uint64 position, uint64 size) {
91f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  if (!next_handler_)
92f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    return true;
93f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)
94cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  return next_handler_->OnUploadProgress(position, size);
95f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)}
96f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)
976e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)bool DetachableResourceHandler::OnRequestRedirected(
986e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)    const net::RedirectInfo& redirect_info,
996e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)    ResourceResponse* response,
1006e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)    bool* defer) {
101f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  DCHECK(!is_deferred_);
102f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)
103f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  if (!next_handler_)
104f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    return true;
105f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)
1066e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)  bool ret = next_handler_->OnRequestRedirected(
1076e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)      redirect_info, response, &is_deferred_);
108f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  *defer = is_deferred_;
109f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  return ret;
110f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)}
111f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)
112cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)bool DetachableResourceHandler::OnResponseStarted(ResourceResponse* response,
113f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)                                                  bool* defer) {
114f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  DCHECK(!is_deferred_);
115f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)
116f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  if (!next_handler_)
117f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    return true;
118f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)
119f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  bool ret =
120cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)      next_handler_->OnResponseStarted(response, &is_deferred_);
121f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  *defer = is_deferred_;
122f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  return ret;
123f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)}
124f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)
125cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)bool DetachableResourceHandler::OnWillStart(const GURL& url, bool* defer) {
126f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  DCHECK(!is_deferred_);
127f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)
128f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  if (!next_handler_)
129f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    return true;
130f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)
131cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  bool ret = next_handler_->OnWillStart(url, &is_deferred_);
132f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  *defer = is_deferred_;
133f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  return ret;
134f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)}
135f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)
136cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)bool DetachableResourceHandler::OnBeforeNetworkStart(const GURL& url,
1375d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                                                     bool* defer) {
1385d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  DCHECK(!is_deferred_);
1395d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
1405d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  if (!next_handler_)
1415d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    return true;
1425d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
1435d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  bool ret =
144cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)      next_handler_->OnBeforeNetworkStart(url, &is_deferred_);
1455d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  *defer = is_deferred_;
1465d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  return ret;
1475d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)}
1485d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
149cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)bool DetachableResourceHandler::OnWillRead(scoped_refptr<net::IOBuffer>* buf,
150f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)                                           int* buf_size,
151f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)                                           int min_size) {
152f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  if (!next_handler_) {
153f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    DCHECK_EQ(-1, min_size);
1541320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    if (!read_buffer_.get())
155f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)      read_buffer_ = new net::IOBuffer(kReadBufSize);
156f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    *buf = read_buffer_;
157f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    *buf_size = kReadBufSize;
158f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    return true;
159f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  }
160f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)
161cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  return next_handler_->OnWillRead(buf, buf_size, min_size);
162f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)}
163f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)
164cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)bool DetachableResourceHandler::OnReadCompleted(int bytes_read, bool* defer) {
165f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  DCHECK(!is_deferred_);
166f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)
167f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  if (!next_handler_)
168f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    return true;
169f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)
170f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  bool ret =
171cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)      next_handler_->OnReadCompleted(bytes_read, &is_deferred_);
172f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  *defer = is_deferred_;
173f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  return ret;
174f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)}
175f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)
176f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)void DetachableResourceHandler::OnResponseCompleted(
177f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    const net::URLRequestStatus& status,
178f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    const std::string& security_info,
179f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    bool* defer) {
180f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  // No DCHECK(!is_deferred_) as the request may have been cancelled while
181f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  // deferred.
182f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)
183f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  if (!next_handler_)
184f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    return;
185f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)
186f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  is_finished_ = true;
187f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)
188cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  next_handler_->OnResponseCompleted(status, security_info, &is_deferred_);
189f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  *defer = is_deferred_;
190f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)}
191f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)
192cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)void DetachableResourceHandler::OnDataDownloaded(int bytes_downloaded) {
193f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  if (!next_handler_)
194f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    return;
195f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)
196cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  next_handler_->OnDataDownloaded(bytes_downloaded);
197f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)}
198f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)
199f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)void DetachableResourceHandler::Resume() {
200f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  DCHECK(is_deferred_);
201f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  is_deferred_ = false;
202f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  controller()->Resume();
203f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)}
204f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)
205f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)void DetachableResourceHandler::Cancel() {
206f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  controller()->Cancel();
207f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)}
208f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)
209f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)void DetachableResourceHandler::CancelAndIgnore() {
210f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  controller()->CancelAndIgnore();
211f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)}
212f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)
213f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)void DetachableResourceHandler::CancelWithError(int error_code) {
214f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  controller()->CancelWithError(error_code);
215f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)}
216f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)
217f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)}  // namespace content
218