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