Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(97)

Side by Side Diff: content/browser/loader/redirect_to_file_resource_handler.cc

Issue 82273002: Fix various issues in RedirectToFileResourceHandler. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: mmenke comments Created 6 years, 9 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "content/browser/loader/redirect_to_file_resource_handler.h" 5 #include "content/browser/loader/redirect_to_file_resource_handler.h"
6 6
7 #include "base/bind.h" 7 #include "base/bind.h"
8 #include "base/files/file_util_proxy.h"
9 #include "base/logging.h" 8 #include "base/logging.h"
10 #include "base/message_loop/message_loop_proxy.h"
11 #include "base/platform_file.h" 9 #include "base/platform_file.h"
12 #include "base/threading/thread_restrictions.h" 10 #include "base/threading/thread_restrictions.h"
13 #include "content/browser/loader/resource_dispatcher_host_impl.h"
14 #include "content/browser/loader/resource_request_info_impl.h" 11 #include "content/browser/loader/resource_request_info_impl.h"
15 #include "content/public/browser/browser_thread.h" 12 #include "content/browser/loader/temporary_file_stream.h"
13 #include "content/public/browser/resource_controller.h"
16 #include "content/public/common/resource_response.h" 14 #include "content/public/common/resource_response.h"
17 #include "net/base/file_stream.h" 15 #include "net/base/file_stream.h"
18 #include "net/base/io_buffer.h" 16 #include "net/base/io_buffer.h"
19 #include "net/base/mime_sniffer.h" 17 #include "net/base/mime_sniffer.h"
20 #include "net/base/net_errors.h" 18 #include "net/base/net_errors.h"
21 #include "webkit/common/blob/shareable_file_reference.h" 19 #include "webkit/common/blob/shareable_file_reference.h"
22 20
23 using webkit_blob::ShareableFileReference; 21 using webkit_blob::ShareableFileReference;
24 22
25 namespace { 23 namespace {
(...skipping 20 matching lines...) Expand all
46 scoped_refptr<net::IOBuffer> backing_; 44 scoped_refptr<net::IOBuffer> backing_;
47 }; 45 };
48 46
49 } // namespace 47 } // namespace
50 48
51 namespace content { 49 namespace content {
52 50
53 static const int kInitialReadBufSize = 32768; 51 static const int kInitialReadBufSize = 32768;
54 static const int kMaxReadBufSize = 524288; 52 static const int kMaxReadBufSize = 524288;
55 53
54 // A separate object to manage the lifetime of the net::FileStream and the
55 // ShareableFileReference. When the handler is destroyed, it asynchronously
56 // closes net::FileStream after all pending writes complete. Only after the
57 // stream is closed is the ShareableFileReference released, to ensure the
58 // temporary is not deleted before it is closed.
mmenke 2014/03/03 21:10:11 May be worth mentioning this lives on the same thr
davidben 2014/03/03 23:20:18 Done.
59 class RedirectToFileResourceHandler::Writer {
60 public:
61 Writer(RedirectToFileResourceHandler* handler,
62 scoped_ptr<net::FileStream> file_stream,
63 ShareableFileReference* deletable_file)
64 : handler_(handler),
65 file_stream_(file_stream.Pass()),
66 write_callback_pending_(false),
67 deletable_file_(deletable_file) {
68 DCHECK(!deletable_file_->path().empty());
69 }
70
71 bool write_callback_pending() const { return write_callback_pending_; }
mmenke 2014/03/03 21:10:11 Optional: is_writing maybe be clearer. If you pr
davidben 2014/03/03 23:20:18 Done.
72 const base::FilePath& path() const { return deletable_file_->path(); }
73
74 int Write(net::IOBuffer* buf, int buf_len) {
75 DCHECK(!write_callback_pending_);
76 DCHECK(handler_);
77 int result = file_stream_->Write(
78 buf, buf_len,
79 base::Bind(&Writer::DidWriteToFile, base::Unretained(this)));
80 if (result == net::ERR_IO_PENDING)
81 write_callback_pending_ = true;
82 return result;
83 }
84
85 void Close() {
86 handler_ = NULL;
87 if (!write_callback_pending_)
88 CloseAndDelete();
89 }
90
91 private:
92 // Only DidClose can delete this.
93 ~Writer() {
94 }
95
96 void DidWriteToFile(int result) {
97 DCHECK(write_callback_pending_);
98 write_callback_pending_ = false;
99 if (handler_) {
100 handler_->DidWriteToFile(result);
101 } else {
102 CloseAndDelete();
103 }
104 }
105
106 void CloseAndDelete() {
107 DCHECK(!write_callback_pending_);
108 int result = file_stream_->Close(base::Bind(&Writer::DidClose,
109 base::Unretained(this)));
110 if (result != net::ERR_IO_PENDING)
111 DidClose(result);
112 }
113
114 void DidClose(int result) {
115 delete this;
116 }
117
118 RedirectToFileResourceHandler* handler_;
119
120 scoped_ptr<net::FileStream> file_stream_;
121 bool write_callback_pending_;
122
123 // We create a ShareableFileReference that's deletable for the temp file
124 // created as a result of the download.
125 scoped_refptr<webkit_blob::ShareableFileReference> deletable_file_;
126
127 DISALLOW_COPY_AND_ASSIGN(Writer);
128 };
129
56 RedirectToFileResourceHandler::RedirectToFileResourceHandler( 130 RedirectToFileResourceHandler::RedirectToFileResourceHandler(
57 scoped_ptr<ResourceHandler> next_handler, 131 scoped_ptr<ResourceHandler> next_handler,
58 net::URLRequest* request, 132 net::URLRequest* request)
59 ResourceDispatcherHostImpl* host)
60 : LayeredResourceHandler(request, next_handler.Pass()), 133 : LayeredResourceHandler(request, next_handler.Pass()),
61 weak_factory_(this),
62 host_(host),
63 buf_(new net::GrowableIOBuffer()), 134 buf_(new net::GrowableIOBuffer()),
64 buf_write_pending_(false), 135 buf_write_pending_(false),
65 write_cursor_(0), 136 write_cursor_(0),
66 write_callback_pending_(false), 137 writer_(NULL),
67 next_buffer_size_(kInitialReadBufSize), 138 next_buffer_size_(kInitialReadBufSize),
68 did_defer_(false), 139 did_defer_(false),
69 completed_during_write_(false) { 140 completed_during_write_(false),
141 weak_factory_(this) {
70 } 142 }
71 143
72 RedirectToFileResourceHandler::~RedirectToFileResourceHandler() { 144 RedirectToFileResourceHandler::~RedirectToFileResourceHandler() {
145 // Orphan the writer to asynchronously close and release the temporary file.
146 if (writer_) {
147 writer_->Close();
148 writer_ = NULL;
mmenke 2014/03/03 21:10:11 Optional: Do we get anything out of NULLing this
davidben 2014/03/03 23:20:18 Not especially. Mostly just makes it clear that we
149 }
150 }
151
152 void RedirectToFileResourceHandler::
153 SetCreateTemporaryFileStreamFunctionForTesting(
154 const CreateTemporaryFileStreamFunction& create_temporary_file_stream) {
155 create_temporary_file_stream_ = create_temporary_file_stream;
73 } 156 }
74 157
75 bool RedirectToFileResourceHandler::OnResponseStarted( 158 bool RedirectToFileResourceHandler::OnResponseStarted(
76 int request_id, 159 int request_id,
77 ResourceResponse* response, 160 ResourceResponse* response,
78 bool* defer) { 161 bool* defer) {
79 if (response->head.error_code == net::OK || 162 if (response->head.error_code == net::OK ||
80 response->head.error_code == net::ERR_IO_PENDING) { 163 response->head.error_code == net::ERR_IO_PENDING) {
81 DCHECK(deletable_file_.get() && !deletable_file_->path().empty()); 164 DCHECK(writer_);
82 response->head.download_file_path = deletable_file_->path(); 165 response->head.download_file_path = writer_->path();
83 } 166 }
84 return next_handler_->OnResponseStarted(request_id, response, defer); 167 return next_handler_->OnResponseStarted(request_id, response, defer);
85 } 168 }
86 169
87 bool RedirectToFileResourceHandler::OnWillStart(int request_id, 170 bool RedirectToFileResourceHandler::OnWillStart(int request_id,
88 const GURL& url, 171 const GURL& url,
89 bool* defer) { 172 bool* defer) {
90 if (!deletable_file_.get()) { 173 DCHECK(!writer_);
91 // Defer starting the request until we have created the temporary file. 174
92 // TODO(darin): This is sub-optimal. We should not delay starting the 175 // Defer starting the request until we have created the temporary file.
93 // network request like this. 176 // TODO(darin): This is sub-optimal. We should not delay starting the
94 did_defer_ = *defer = true; 177 // network request like this.
95 base::FileUtilProxy::CreateTemporary( 178 will_start_url_ = url;
96 BrowserThread::GetMessageLoopProxyForThread(BrowserThread::FILE).get(), 179 did_defer_ = *defer = true;
97 base::PLATFORM_FILE_ASYNC, 180 if (create_temporary_file_stream_.is_null()) {
181 CreateTemporaryFileStream(
98 base::Bind(&RedirectToFileResourceHandler::DidCreateTemporaryFile, 182 base::Bind(&RedirectToFileResourceHandler::DidCreateTemporaryFile,
99 weak_factory_.GetWeakPtr())); 183 weak_factory_.GetWeakPtr()));
100 return true; 184 } else {
185 create_temporary_file_stream_.Run(
186 base::Bind(&RedirectToFileResourceHandler::DidCreateTemporaryFile,
187 weak_factory_.GetWeakPtr()));
101 } 188 }
102 return next_handler_->OnWillStart(request_id, url, defer); 189 return true;
103 } 190 }
104 191
105 bool RedirectToFileResourceHandler::OnWillRead( 192 bool RedirectToFileResourceHandler::OnWillRead(
106 int request_id, 193 int request_id,
107 scoped_refptr<net::IOBuffer>* buf, 194 scoped_refptr<net::IOBuffer>* buf,
108 int* buf_size, 195 int* buf_size,
109 int min_size) { 196 int min_size) {
110 DCHECK_EQ(-1, min_size); 197 DCHECK_EQ(-1, min_size);
111 198
112 if (buf_->capacity() < next_buffer_size_) 199 if (buf_->capacity() < next_buffer_size_)
(...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after
144 } 231 }
145 232
146 return WriteMore(); 233 return WriteMore();
147 } 234 }
148 235
149 void RedirectToFileResourceHandler::OnResponseCompleted( 236 void RedirectToFileResourceHandler::OnResponseCompleted(
150 int request_id, 237 int request_id,
151 const net::URLRequestStatus& status, 238 const net::URLRequestStatus& status,
152 const std::string& security_info, 239 const std::string& security_info,
153 bool* defer) { 240 bool* defer) {
154 if (write_callback_pending_) { 241 if (writer_ && writer_->write_callback_pending()) {
155 completed_during_write_ = true; 242 completed_during_write_ = true;
156 completed_status_ = status; 243 completed_status_ = status;
157 completed_security_info_ = security_info; 244 completed_security_info_ = security_info;
158 did_defer_ = true; 245 did_defer_ = true;
159 *defer = true; 246 *defer = true;
160 return; 247 return;
161 } 248 }
162 next_handler_->OnResponseCompleted(request_id, status, security_info, defer); 249 next_handler_->OnResponseCompleted(request_id, status, security_info, defer);
163 } 250 }
164 251
165 void RedirectToFileResourceHandler::DidCreateTemporaryFile( 252 void RedirectToFileResourceHandler::DidCreateTemporaryFile(
166 base::File::Error /*error_code*/, 253 base::File::Error error_code,
167 base::PassPlatformFile file_handle, 254 scoped_ptr<net::FileStream> file_stream,
168 const base::FilePath& file_path) { 255 ShareableFileReference* deletable_file) {
169 deletable_file_ = ShareableFileReference::GetOrCreate( 256 DCHECK(!writer_);
170 file_path, 257 if (error_code != base::File::FILE_OK) {
171 ShareableFileReference::DELETE_ON_FINAL_RELEASE, 258 controller()->CancelWithError(net::FileErrorToNetError(error_code));
mmenke 2014/03/03 21:10:11 optional: Should we set did_defer_ to false here?
davidben 2014/03/03 23:20:18 *shrug* I don't think it matters? I think we want
mmenke 2014/03/04 18:06:52 Only issue here is that you can defer handling of
172 BrowserThread::GetMessageLoopProxyForThread(BrowserThread::FILE).get()); 259 return;
173 file_stream_.reset( 260 }
174 new net::FileStream(file_handle.ReleaseValue(), 261
175 base::PLATFORM_FILE_WRITE | base::PLATFORM_FILE_ASYNC, 262 writer_ = new Writer(this, file_stream.Pass(), deletable_file);
176 NULL)); 263
177 const ResourceRequestInfo* info = GetRequestInfo(); 264 // Resume the request.
178 host_->RegisterDownloadedTempFile( 265 DCHECK(did_defer_);
179 info->GetChildID(), info->GetRequestID(), deletable_file_.get()); 266 bool defer = false;
180 ResumeIfDeferred(); 267 if (!next_handler_->OnWillStart(GetRequestID(), will_start_url_, &defer)) {
268 controller()->Cancel();
269 } else if (!defer) {
270 ResumeIfDeferred();
271 }
272 did_defer_ = false;
mmenke 2014/03/03 21:10:11 This still makes me nervous... Yes, if we call Re
davidben 2014/03/03 23:20:18 "This" being the did_defer_ outside of the else? M
mmenke 2014/03/04 18:06:52 Yea, "this" being setting did_defer_ to false afte
273 will_start_url_ = GURL();
181 } 274 }
182 275
183 void RedirectToFileResourceHandler::DidWriteToFile(int result) { 276 void RedirectToFileResourceHandler::DidWriteToFile(int result) {
184 write_callback_pending_ = false;
185 int request_id = GetRequestID(); 277 int request_id = GetRequestID();
186 278
187 bool failed = false; 279 bool failed = false;
188 if (result > 0) { 280 if (result > 0) {
189 next_handler_->OnDataDownloaded(request_id, result); 281 next_handler_->OnDataDownloaded(request_id, result);
190 write_cursor_ += result; 282 write_cursor_ += result;
191 failed = !WriteMore(); 283 failed = !WriteMore();
192 } else { 284 } else {
193 failed = true; 285 failed = true;
194 } 286 }
195 287
196 if (failed) { 288 if (failed) {
197 ResumeIfDeferred(); 289 DCHECK(!writer_->write_callback_pending());
198 } else if (completed_during_write_) { 290 // TODO(davidben): Recover the error code from WriteMore or |result|, as
291 // appropriate.
292 if (completed_during_write_ && completed_status_.is_success()) {
293 // If the request successfully completed mid-write, but the write failed,
294 // convert the status to a failure for downstream.
295 completed_status_.set_status(net::URLRequestStatus::CANCELED);
296 completed_status_.set_error(net::ERR_FAILED);
297 }
298 if (!completed_during_write_)
299 controller()->CancelWithError(net::ERR_FAILED);
300 }
301
302 if (completed_during_write_ && !writer_->write_callback_pending()) {
303 // Resume shutdown now that all data has been written to disk. Note that
304 // this should run even in the |failed| case above, otherwise a failed write
305 // leaves the handler stuck.
199 bool defer = false; 306 bool defer = false;
200 next_handler_->OnResponseCompleted(request_id, 307 next_handler_->OnResponseCompleted(request_id,
201 completed_status_, 308 completed_status_,
202 completed_security_info_, 309 completed_security_info_,
203 &defer); 310 &defer);
204 if (!defer) 311 if (!defer)
205 ResumeIfDeferred(); 312 ResumeIfDeferred();
313 did_defer_ = false;
mmenke 2014/03/03 21:10:11 Maybe put this in an else? We don't be deferring
davidben 2014/03/03 23:20:18 Done.
206 } 314 }
207 } 315 }
208 316
209 bool RedirectToFileResourceHandler::WriteMore() { 317 bool RedirectToFileResourceHandler::WriteMore() {
210 DCHECK(file_stream_.get()); 318 DCHECK(writer_);
211 for (;;) { 319 for (;;) {
212 if (write_cursor_ == buf_->offset()) { 320 if (write_cursor_ == buf_->offset()) {
213 // We've caught up to the network load, but it may be in the process of 321 // We've caught up to the network load, but it may be in the process of
214 // appending more data to the buffer. 322 // appending more data to the buffer.
215 if (!buf_write_pending_) { 323 if (!buf_write_pending_) {
216 if (BufIsFull()) 324 if (BufIsFull())
217 ResumeIfDeferred(); 325 ResumeIfDeferred();
218 buf_->set_offset(0); 326 buf_->set_offset(0);
219 write_cursor_ = 0; 327 write_cursor_ = 0;
220 } 328 }
221 return true; 329 return true;
222 } 330 }
223 if (write_callback_pending_) 331 if (writer_->write_callback_pending())
224 return true; 332 return true;
225 DCHECK(write_cursor_ < buf_->offset()); 333 DCHECK(write_cursor_ < buf_->offset());
226 334
227 // Create a temporary buffer pointing to a subsection of the data buffer so 335 // Create a temporary buffer pointing to a subsection of the data buffer so
228 // that it can be passed to Write. This code makes some crazy scary 336 // that it can be passed to Write. This code makes some crazy scary
229 // assumptions about object lifetimes, thread sharing, and that buf_ will 337 // assumptions about object lifetimes, thread sharing, and that buf_ will
230 // not realloc durring the write due to how the state machine in this class 338 // not realloc durring the write due to how the state machine in this class
231 // works. 339 // works.
232 // Note that buf_ is also shared with the code that writes data into the 340 // Note that buf_ is also shared with the code that writes data into the
233 // cache, so modifying it can cause some pretty subtle race conditions: 341 // cache, so modifying it can cause some pretty subtle race conditions:
234 // https://code.google.com/p/chromium/issues/detail?id=152076 342 // https://code.google.com/p/chromium/issues/detail?id=152076
235 // We're using DependentIOBuffer instead of DrainableIOBuffer to dodge some 343 // We're using DependentIOBuffer instead of DrainableIOBuffer to dodge some
236 // of these issues, for the moment. 344 // of these issues, for the moment.
237 // TODO(ncbray) make this code less crazy scary. 345 // TODO(ncbray) make this code less crazy scary.
238 // Also note that Write may increase the refcount of "wrapped" deep in the 346 // Also note that Write may increase the refcount of "wrapped" deep in the
239 // bowels of its implementation, the use of scoped_refptr here is not 347 // bowels of its implementation, the use of scoped_refptr here is not
240 // spurious. 348 // spurious.
241 scoped_refptr<DependentIOBuffer> wrapped = new DependentIOBuffer( 349 scoped_refptr<DependentIOBuffer> wrapped = new DependentIOBuffer(
242 buf_.get(), buf_->StartOfBuffer() + write_cursor_); 350 buf_.get(), buf_->StartOfBuffer() + write_cursor_);
243 int write_len = buf_->offset() - write_cursor_; 351 int write_len = buf_->offset() - write_cursor_;
244 352
245 int rv = file_stream_->Write( 353 int rv = writer_->Write(wrapped.get(), write_len);
246 wrapped.get(), 354 if (rv == net::ERR_IO_PENDING)
247 write_len,
248 base::Bind(&RedirectToFileResourceHandler::DidWriteToFile,
249 base::Unretained(this)));
250 if (rv == net::ERR_IO_PENDING) {
251 write_callback_pending_ = true;
252 return true; 355 return true;
253 }
254 if (rv <= 0) 356 if (rv <= 0)
255 return false; 357 return false;
256 next_handler_->OnDataDownloaded(GetRequestID(), rv); 358 next_handler_->OnDataDownloaded(GetRequestID(), rv);
257 write_cursor_ += rv; 359 write_cursor_ += rv;
258 } 360 }
259 } 361 }
260 362
261 bool RedirectToFileResourceHandler::BufIsFull() const { 363 bool RedirectToFileResourceHandler::BufIsFull() const {
262 // This is a hack to workaround BufferedResourceHandler's inability to 364 // This is a hack to workaround BufferedResourceHandler's inability to
263 // deal with a ResourceHandler that returns a buffer size of less than 365 // deal with a ResourceHandler that returns a buffer size of less than
264 // 2 * net::kMaxBytesToSniff from its OnWillRead method. 366 // 2 * net::kMaxBytesToSniff from its OnWillRead method.
265 // TODO(darin): Fix this retardation! 367 // TODO(darin): Fix this retardation!
266 return buf_->RemainingCapacity() <= (2 * net::kMaxBytesToSniff); 368 return buf_->RemainingCapacity() <= (2 * net::kMaxBytesToSniff);
267 } 369 }
268 370
269 void RedirectToFileResourceHandler::ResumeIfDeferred() { 371 void RedirectToFileResourceHandler::ResumeIfDeferred() {
270 if (did_defer_) { 372 if (did_defer_) {
271 did_defer_ = false; 373 did_defer_ = false;
272 controller()->Resume(); 374 controller()->Resume();
273 } 375 }
274 } 376 }
275 377
276 } // namespace content 378 } // namespace content
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698