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

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: Comments Created 7 years 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/public/browser/resource_controller.h"
16 #include "content/public/common/resource_response.h" 13 #include "content/public/common/resource_response.h"
17 #include "net/base/file_stream.h" 14 #include "net/base/file_stream.h"
18 #include "net/base/io_buffer.h" 15 #include "net/base/io_buffer.h"
19 #include "net/base/mime_sniffer.h" 16 #include "net/base/mime_sniffer.h"
20 #include "net/base/net_errors.h" 17 #include "net/base/net_errors.h"
21 #include "webkit/common/blob/shareable_file_reference.h" 18 #include "webkit/common/blob/shareable_file_reference.h"
22 19
23 using webkit_blob::ShareableFileReference; 20 using webkit_blob::ShareableFileReference;
24 21
25 namespace { 22 namespace {
(...skipping 20 matching lines...) Expand all
46 scoped_refptr<net::IOBuffer> backing_; 43 scoped_refptr<net::IOBuffer> backing_;
47 }; 44 };
48 45
49 } // namespace 46 } // namespace
50 47
51 namespace content { 48 namespace content {
52 49
53 static const int kInitialReadBufSize = 32768; 50 static const int kInitialReadBufSize = 32768;
54 static const int kMaxReadBufSize = 524288; 51 static const int kMaxReadBufSize = 524288;
55 52
53 // A separate object to manage the lifetime of the net::FileStream and the
54 // ShareableFileReference. This ensures that the file is closed before it is
55 // deleted and that |file_stream_->Close()| is not called until
56 // |file_stream_->Write()| completes.
mmenke 2013/12/04 20:55:48 Could we just make net::FileStream take a Sequence
davidben 2013/12/04 22:44:04 I thought about that, but I don't think it quite w
57 class RedirectToFileResourceHandler::Writer {
58 public:
59 Writer(RedirectToFileResourceHandler* handler,
60 scoped_ptr<net::FileStream> file_stream,
61 ShareableFileReference* deletable_file)
62 : handler_(handler),
63 file_stream_(file_stream.Pass()),
64 write_callback_pending_(false),
65 deletable_file_(deletable_file) {
66 }
67
68 bool write_callback_pending() const { return write_callback_pending_; }
69 const base::FilePath& path() const { return deletable_file_->path(); }
70
71 int Write(net::IOBuffer* buf, int buf_len) {
72 int result = file_stream_->Write(
73 buf, buf_len,
74 base::Bind(&Writer::DidWriteToFile, base::Unretained(this)));
75 if (result == net::ERR_IO_PENDING)
76 write_callback_pending_ = true;
77 return result;
78 }
79
80 void Orphan() {
81 handler_ = NULL;
82 if (!write_callback_pending_)
83 CloseAndDelete();
84 }
85
86 private:
87 void DidWriteToFile(int result) {
88 write_callback_pending_ = false;
89 if (handler_) {
90 handler_->DidWriteToFile(result);
91 } else {
92 CloseAndDelete();
93 }
94 }
95
96 void CloseAndDelete() {
97 int result = file_stream_->Close(base::Bind(&Writer::DidClose,
98 base::Unretained(this)));
99 if (result != net::ERR_IO_PENDING)
100 DidClose(result);
101 }
102
103 void DidClose(int result) {
104 delete this;
105 }
106
107 RedirectToFileResourceHandler* handler_;
108
109 scoped_ptr<net::FileStream> file_stream_;
110 bool write_callback_pending_;
111
112 // We create a ShareableFileReference that's deletable for the temp file
113 // created as a result of the download.
114 scoped_refptr<webkit_blob::ShareableFileReference> deletable_file_;
115
116 DISALLOW_COPY_AND_ASSIGN(Writer);
117 };
118
56 RedirectToFileResourceHandler::RedirectToFileResourceHandler( 119 RedirectToFileResourceHandler::RedirectToFileResourceHandler(
57 scoped_ptr<ResourceHandler> next_handler, 120 scoped_ptr<ResourceHandler> next_handler,
58 net::URLRequest* request, 121 net::URLRequest* request,
59 ResourceDispatcherHostImpl* host) 122 TemporaryFileStreamFactory* file_stream_factory)
60 : LayeredResourceHandler(request, next_handler.Pass()), 123 : LayeredResourceHandler(request, next_handler.Pass()),
61 weak_factory_(this), 124 file_stream_factory_(file_stream_factory),
62 host_(host),
63 buf_(new net::GrowableIOBuffer()), 125 buf_(new net::GrowableIOBuffer()),
64 buf_write_pending_(false), 126 buf_write_pending_(false),
65 write_cursor_(0), 127 write_cursor_(0),
66 write_callback_pending_(false),
67 next_buffer_size_(kInitialReadBufSize), 128 next_buffer_size_(kInitialReadBufSize),
68 did_defer_(false), 129 did_defer_(false),
69 completed_during_write_(false) { 130 completed_during_write_(false),
131 weak_factory_(this) {
70 } 132 }
71 133
72 RedirectToFileResourceHandler::~RedirectToFileResourceHandler() { 134 RedirectToFileResourceHandler::~RedirectToFileResourceHandler() {
135 // Orphan the writer to asynchronously close and release the temporary file.
136 if (writer_)
137 writer_.release()->Orphan();
73 } 138 }
74 139
75 bool RedirectToFileResourceHandler::OnResponseStarted( 140 bool RedirectToFileResourceHandler::OnResponseStarted(
76 int request_id, 141 int request_id,
77 ResourceResponse* response, 142 ResourceResponse* response,
78 bool* defer) { 143 bool* defer) {
79 if (response->head.error_code == net::OK || 144 if (response->head.error_code == net::OK ||
80 response->head.error_code == net::ERR_IO_PENDING) { 145 response->head.error_code == net::ERR_IO_PENDING) {
81 DCHECK(deletable_file_.get() && !deletable_file_->path().empty()); 146 DCHECK(writer_ && !writer_->path().empty());
82 response->head.download_file_path = deletable_file_->path(); 147 response->head.download_file_path = writer_->path();
83 } 148 }
84 return next_handler_->OnResponseStarted(request_id, response, defer); 149 return next_handler_->OnResponseStarted(request_id, response, defer);
85 } 150 }
86 151
87 bool RedirectToFileResourceHandler::OnWillStart(int request_id, 152 bool RedirectToFileResourceHandler::OnWillStart(int request_id,
88 const GURL& url, 153 const GURL& url,
89 bool* defer) { 154 bool* defer) {
90 if (!deletable_file_.get()) { 155 DCHECK(!writer_);
91 // Defer starting the request until we have created the temporary file. 156
92 // TODO(darin): This is sub-optimal. We should not delay starting the 157 // Defer starting the request until we have created the temporary file.
93 // network request like this. 158 // TODO(darin): This is sub-optimal. We should not delay starting the
94 did_defer_ = *defer = true; 159 // network request like this.
95 base::FileUtilProxy::CreateTemporary( 160 will_start_url_ = url;
96 BrowserThread::GetMessageLoopProxyForThread(BrowserThread::FILE).get(), 161 did_defer_ = *defer = true;
97 base::PLATFORM_FILE_ASYNC, 162 const ResourceRequestInfo* info = GetRequestInfo();
98 base::Bind(&RedirectToFileResourceHandler::DidCreateTemporaryFile, 163 file_stream_factory_->CreateTemporary(
99 weak_factory_.GetWeakPtr())); 164 info->GetChildID(), info->GetRequestID(),
100 return true; 165 base::Bind(&RedirectToFileResourceHandler::DidCreateTemporaryFile,
101 } 166 weak_factory_.GetWeakPtr()));
102 return next_handler_->OnWillStart(request_id, url, defer); 167 return true;
103 } 168 }
104 169
105 bool RedirectToFileResourceHandler::OnWillRead( 170 bool RedirectToFileResourceHandler::OnWillRead(
106 int request_id, 171 int request_id,
107 scoped_refptr<net::IOBuffer>* buf, 172 scoped_refptr<net::IOBuffer>* buf,
108 int* buf_size, 173 int* buf_size,
109 int min_size) { 174 int min_size) {
110 DCHECK_EQ(-1, min_size); 175 DCHECK_EQ(-1, min_size);
111 176
112 if (buf_->capacity() < next_buffer_size_) 177 if (buf_->capacity() < next_buffer_size_)
(...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after
144 } 209 }
145 210
146 return WriteMore(); 211 return WriteMore();
147 } 212 }
148 213
149 void RedirectToFileResourceHandler::OnResponseCompleted( 214 void RedirectToFileResourceHandler::OnResponseCompleted(
150 int request_id, 215 int request_id,
151 const net::URLRequestStatus& status, 216 const net::URLRequestStatus& status,
152 const std::string& security_info, 217 const std::string& security_info,
153 bool* defer) { 218 bool* defer) {
154 if (write_callback_pending_) { 219 if (writer_ && writer_->write_callback_pending()) {
155 completed_during_write_ = true; 220 completed_during_write_ = true;
156 completed_status_ = status; 221 completed_status_ = status;
157 completed_security_info_ = security_info; 222 completed_security_info_ = security_info;
158 *defer = true; 223 did_defer_ = *defer = true;
159 return; 224 return;
160 } 225 }
161 next_handler_->OnResponseCompleted(request_id, status, security_info, defer); 226 next_handler_->OnResponseCompleted(request_id, status, security_info, defer);
162 } 227 }
163 228
164 void RedirectToFileResourceHandler::DidCreateTemporaryFile( 229 void RedirectToFileResourceHandler::DidCreateTemporaryFile(
165 base::PlatformFileError /*error_code*/, 230 base::PlatformFileError error_code,
166 base::PassPlatformFile file_handle, 231 scoped_ptr<net::FileStream> file_stream,
167 const base::FilePath& file_path) { 232 scoped_refptr<ShareableFileReference> deletable_file) {
168 deletable_file_ = ShareableFileReference::GetOrCreate( 233 if (error_code != base::PLATFORM_FILE_OK) {
169 file_path, 234 controller()->CancelWithError(net::PlatformFileErrorToNetError(error_code));
170 ShareableFileReference::DELETE_ON_FINAL_RELEASE, 235 return;
171 BrowserThread::GetMessageLoopProxyForThread(BrowserThread::FILE).get()); 236 }
172 file_stream_.reset( 237
173 new net::FileStream(file_handle.ReleaseValue(), 238 writer_.reset(new Writer(this, file_stream.Pass(), deletable_file.get()));
174 base::PLATFORM_FILE_WRITE | base::PLATFORM_FILE_ASYNC, 239
175 NULL)); 240 // Resume the request.
176 const ResourceRequestInfo* info = GetRequestInfo(); 241 bool defer = false;
177 host_->RegisterDownloadedTempFile( 242 if (!next_handler_->OnWillStart(GetRequestID(), will_start_url_, &defer)) {
178 info->GetChildID(), info->GetRequestID(), deletable_file_.get()); 243 controller()->Cancel();
179 ResumeIfDeferred(); 244 } else if (!defer) {
mmenke 2013/12/04 20:55:48 Should probably have a test where defer is true.
davidben 2013/12/04 22:44:04 Done.
245 ResumeIfDeferred();
246 }
247 will_start_url_ = GURL();
180 } 248 }
181 249
182 void RedirectToFileResourceHandler::DidWriteToFile(int result) { 250 void RedirectToFileResourceHandler::DidWriteToFile(int result) {
183 write_callback_pending_ = false;
184 int request_id = GetRequestID(); 251 int request_id = GetRequestID();
185 252
186 bool failed = false; 253 bool failed = false;
187 if (result > 0) { 254 if (result > 0) {
188 next_handler_->OnDataDownloaded(request_id, result); 255 next_handler_->OnDataDownloaded(request_id, result);
189 write_cursor_ += result; 256 write_cursor_ += result;
190 failed = !WriteMore(); 257 failed = !WriteMore();
191 } else { 258 } else {
192 failed = true; 259 failed = true;
193 } 260 }
194 261
195 if (failed) { 262 if (failed) {
196 ResumeIfDeferred(); 263 // TODO(davidben): Recover the error code from WriteMore or |result|, as
197 } else if (completed_during_write_) { 264 // appropriate.
265 if (completed_during_write_ && completed_status_.is_success()) {
266 // If the request successfully completed mid-write, but the write failed,
267 // convert the status to a failure for downstream.
268 completed_status_.set_status(net::URLRequestStatus::CANCELED);
269 completed_status_.set_error(net::ERR_FAILED);
270 }
271 controller()->CancelWithError(net::ERR_FAILED);
272 }
273
274 if (completed_during_write_ && !writer_->write_callback_pending()) {
275 // Resume shutdown now that all data has been written to disk. Note that
276 // this should run even in the |failed| case above, otherwise a failed write
277 // leaves the handler stuck.
198 bool defer = false; 278 bool defer = false;
199 next_handler_->OnResponseCompleted(request_id, 279 next_handler_->OnResponseCompleted(request_id,
200 completed_status_, 280 completed_status_,
201 completed_security_info_, 281 completed_security_info_,
202 &defer); 282 &defer);
203 if (!defer) 283 if (!defer)
204 ResumeIfDeferred(); 284 ResumeIfDeferred();
205 } 285 }
206 } 286 }
207 287
208 bool RedirectToFileResourceHandler::WriteMore() { 288 bool RedirectToFileResourceHandler::WriteMore() {
209 DCHECK(file_stream_.get()); 289 DCHECK(writer_);
210 for (;;) { 290 for (;;) {
211 if (write_cursor_ == buf_->offset()) { 291 if (write_cursor_ == buf_->offset()) {
212 // We've caught up to the network load, but it may be in the process of 292 // We've caught up to the network load, but it may be in the process of
213 // appending more data to the buffer. 293 // appending more data to the buffer.
214 if (!buf_write_pending_) { 294 if (!buf_write_pending_) {
215 if (BufIsFull()) 295 if (BufIsFull())
216 ResumeIfDeferred(); 296 ResumeIfDeferred();
217 buf_->set_offset(0); 297 buf_->set_offset(0);
218 write_cursor_ = 0; 298 write_cursor_ = 0;
219 } 299 }
220 return true; 300 return true;
221 } 301 }
222 if (write_callback_pending_) 302 if (writer_->write_callback_pending())
223 return true; 303 return true;
224 DCHECK(write_cursor_ < buf_->offset()); 304 DCHECK(write_cursor_ < buf_->offset());
225 305
226 // Create a temporary buffer pointing to a subsection of the data buffer so 306 // Create a temporary buffer pointing to a subsection of the data buffer so
227 // that it can be passed to Write. This code makes some crazy scary 307 // that it can be passed to Write. This code makes some crazy scary
228 // assumptions about object lifetimes, thread sharing, and that buf_ will 308 // assumptions about object lifetimes, thread sharing, and that buf_ will
229 // not realloc durring the write due to how the state machine in this class 309 // not realloc durring the write due to how the state machine in this class
230 // works. 310 // works.
231 // Note that buf_ is also shared with the code that writes data into the 311 // Note that buf_ is also shared with the code that writes data into the
232 // cache, so modifying it can cause some pretty subtle race conditions: 312 // cache, so modifying it can cause some pretty subtle race conditions:
233 // https://code.google.com/p/chromium/issues/detail?id=152076 313 // https://code.google.com/p/chromium/issues/detail?id=152076
234 // We're using DependentIOBuffer instead of DrainableIOBuffer to dodge some 314 // We're using DependentIOBuffer instead of DrainableIOBuffer to dodge some
235 // of these issues, for the moment. 315 // of these issues, for the moment.
236 // TODO(ncbray) make this code less crazy scary. 316 // TODO(ncbray) make this code less crazy scary.
237 // Also note that Write may increase the refcount of "wrapped" deep in the 317 // Also note that Write may increase the refcount of "wrapped" deep in the
238 // bowels of its implementation, the use of scoped_refptr here is not 318 // bowels of its implementation, the use of scoped_refptr here is not
239 // spurious. 319 // spurious.
240 scoped_refptr<DependentIOBuffer> wrapped = new DependentIOBuffer( 320 scoped_refptr<DependentIOBuffer> wrapped = new DependentIOBuffer(
241 buf_.get(), buf_->StartOfBuffer() + write_cursor_); 321 buf_.get(), buf_->StartOfBuffer() + write_cursor_);
242 int write_len = buf_->offset() - write_cursor_; 322 int write_len = buf_->offset() - write_cursor_;
243 323
244 int rv = file_stream_->Write( 324 int rv = writer_->Write(wrapped.get(), write_len);
245 wrapped.get(), 325 if (rv == net::ERR_IO_PENDING)
246 write_len,
247 base::Bind(&RedirectToFileResourceHandler::DidWriteToFile,
248 base::Unretained(this)));
249 if (rv == net::ERR_IO_PENDING) {
250 write_callback_pending_ = true;
251 return true; 326 return true;
252 }
253 if (rv <= 0) 327 if (rv <= 0)
254 return false; 328 return false;
255 next_handler_->OnDataDownloaded(GetRequestID(), rv); 329 next_handler_->OnDataDownloaded(GetRequestID(), rv);
256 write_cursor_ += rv; 330 write_cursor_ += rv;
257 } 331 }
258 } 332 }
259 333
260 bool RedirectToFileResourceHandler::BufIsFull() const { 334 bool RedirectToFileResourceHandler::BufIsFull() const {
261 // This is a hack to workaround BufferedResourceHandler's inability to 335 // This is a hack to workaround BufferedResourceHandler's inability to
262 // deal with a ResourceHandler that returns a buffer size of less than 336 // deal with a ResourceHandler that returns a buffer size of less than
263 // 2 * net::kMaxBytesToSniff from its OnWillRead method. 337 // 2 * net::kMaxBytesToSniff from its OnWillRead method.
264 // TODO(darin): Fix this retardation! 338 // TODO(darin): Fix this retardation!
265 return buf_->RemainingCapacity() <= (2 * net::kMaxBytesToSniff); 339 return buf_->RemainingCapacity() <= (2 * net::kMaxBytesToSniff);
266 } 340 }
267 341
268 void RedirectToFileResourceHandler::ResumeIfDeferred() { 342 void RedirectToFileResourceHandler::ResumeIfDeferred() {
269 if (did_defer_) { 343 if (did_defer_) {
270 did_defer_ = false; 344 did_defer_ = false;
271 controller()->Resume(); 345 controller()->Resume();
272 } 346 }
273 } 347 }
274 348
275 } // namespace content 349 } // namespace content
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698