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 3 // found in the LICENSE file. 4 5 #include "storage/browser/fileapi/file_writer_delegate.h" 6 7 #include "base/bind.h" 8 #include "base/callback.h" 9 #include "base/files/file_util_proxy.h" 10 #include "base/message_loop/message_loop.h" 11 #include "base/message_loop/message_loop_proxy.h" 12 #include "base/sequenced_task_runner.h" 13 #include "base/threading/thread_restrictions.h" 14 #include "net/base/net_errors.h" 15 #include "storage/browser/fileapi/file_stream_writer.h" 16 #include "storage/browser/fileapi/file_system_context.h" 17 #include "storage/common/fileapi/file_system_util.h" 18 19 namespace storage { 20 21 static const int kReadBufSize = 32768; 22 23 FileWriterDelegate::FileWriterDelegate( 24 scoped_ptr<FileStreamWriter> file_stream_writer, 25 FlushPolicy flush_policy) 26 : file_stream_writer_(file_stream_writer.Pass()), 27 writing_started_(false), 28 flush_policy_(flush_policy), 29 bytes_written_backlog_(0), 30 bytes_written_(0), 31 bytes_read_(0), 32 io_buffer_(new net::IOBufferWithSize(kReadBufSize)), 33 weak_factory_(this) { 34 } 35 36 FileWriterDelegate::~FileWriterDelegate() { 37 } 38 39 void FileWriterDelegate::Start(scoped_ptr<net::URLRequest> request, 40 const DelegateWriteCallback& write_callback) { 41 write_callback_ = write_callback; 42 request_ = request.Pass(); 43 request_->Start(); 44 } 45 46 void FileWriterDelegate::Cancel() { 47 if (request_) { 48 // This halts any callbacks on this delegate. 49 request_->set_delegate(NULL); 50 request_->Cancel(); 51 } 52 53 const int status = file_stream_writer_->Cancel( 54 base::Bind(&FileWriterDelegate::OnWriteCancelled, 55 weak_factory_.GetWeakPtr())); 56 // Return true to finish immediately if we have no pending writes. 57 // Otherwise we'll do the final cleanup in the Cancel callback. 58 if (status != net::ERR_IO_PENDING) { 59 write_callback_.Run(base::File::FILE_ERROR_ABORT, 0, 60 GetCompletionStatusOnError()); 61 } 62 } 63 64 void FileWriterDelegate::OnReceivedRedirect( 65 net::URLRequest* request, 66 const net::RedirectInfo& redirect_info, 67 bool* defer_redirect) { 68 NOTREACHED(); 69 OnError(base::File::FILE_ERROR_SECURITY); 70 } 71 72 void FileWriterDelegate::OnAuthRequired(net::URLRequest* request, 73 net::AuthChallengeInfo* auth_info) { 74 NOTREACHED(); 75 OnError(base::File::FILE_ERROR_SECURITY); 76 } 77 78 void FileWriterDelegate::OnCertificateRequested( 79 net::URLRequest* request, 80 net::SSLCertRequestInfo* cert_request_info) { 81 NOTREACHED(); 82 OnError(base::File::FILE_ERROR_SECURITY); 83 } 84 85 void FileWriterDelegate::OnSSLCertificateError(net::URLRequest* request, 86 const net::SSLInfo& ssl_info, 87 bool fatal) { 88 NOTREACHED(); 89 OnError(base::File::FILE_ERROR_SECURITY); 90 } 91 92 void FileWriterDelegate::OnResponseStarted(net::URLRequest* request) { 93 DCHECK_EQ(request_.get(), request); 94 if (!request->status().is_success() || request->GetResponseCode() != 200) { 95 OnError(base::File::FILE_ERROR_FAILED); 96 return; 97 } 98 Read(); 99 } 100 101 void FileWriterDelegate::OnReadCompleted(net::URLRequest* request, 102 int bytes_read) { 103 DCHECK_EQ(request_.get(), request); 104 if (!request->status().is_success()) { 105 OnError(base::File::FILE_ERROR_FAILED); 106 return; 107 } 108 OnDataReceived(bytes_read); 109 } 110 111 void FileWriterDelegate::Read() { 112 bytes_written_ = 0; 113 bytes_read_ = 0; 114 if (request_->Read(io_buffer_.get(), io_buffer_->size(), &bytes_read_)) { 115 base::MessageLoop::current()->PostTask( 116 FROM_HERE, 117 base::Bind(&FileWriterDelegate::OnDataReceived, 118 weak_factory_.GetWeakPtr(), bytes_read_)); 119 } else if (!request_->status().is_io_pending()) { 120 OnError(base::File::FILE_ERROR_FAILED); 121 } 122 } 123 124 void FileWriterDelegate::OnDataReceived(int bytes_read) { 125 bytes_read_ = bytes_read; 126 if (!bytes_read_) { // We're done. 127 OnProgress(0, true); 128 } else { 129 // This could easily be optimized to rotate between a pool of buffers, so 130 // that we could read and write at the same time. It's not yet clear that 131 // it's necessary. 132 cursor_ = new net::DrainableIOBuffer(io_buffer_.get(), bytes_read_); 133 Write(); 134 } 135 } 136 137 void FileWriterDelegate::Write() { 138 writing_started_ = true; 139 int64 bytes_to_write = bytes_read_ - bytes_written_; 140 int write_response = 141 file_stream_writer_->Write(cursor_.get(), 142 static_cast<int>(bytes_to_write), 143 base::Bind(&FileWriterDelegate::OnDataWritten, 144 weak_factory_.GetWeakPtr())); 145 if (write_response > 0) { 146 base::MessageLoop::current()->PostTask( 147 FROM_HERE, 148 base::Bind(&FileWriterDelegate::OnDataWritten, 149 weak_factory_.GetWeakPtr(), write_response)); 150 } else if (net::ERR_IO_PENDING != write_response) { 151 OnError(NetErrorToFileError(write_response)); 152 } 153 } 154 155 void FileWriterDelegate::OnDataWritten(int write_response) { 156 if (write_response > 0) { 157 OnProgress(write_response, false); 158 cursor_->DidConsume(write_response); 159 bytes_written_ += write_response; 160 if (bytes_written_ == bytes_read_) 161 Read(); 162 else 163 Write(); 164 } else { 165 OnError(NetErrorToFileError(write_response)); 166 } 167 } 168 169 FileWriterDelegate::WriteProgressStatus 170 FileWriterDelegate::GetCompletionStatusOnError() const { 171 return writing_started_ ? ERROR_WRITE_STARTED : ERROR_WRITE_NOT_STARTED; 172 } 173 174 void FileWriterDelegate::OnError(base::File::Error error) { 175 if (request_) { 176 request_->set_delegate(NULL); 177 request_->Cancel(); 178 } 179 180 if (writing_started_) 181 MaybeFlushForCompletion(error, 0, ERROR_WRITE_STARTED); 182 else 183 write_callback_.Run(error, 0, ERROR_WRITE_NOT_STARTED); 184 } 185 186 void FileWriterDelegate::OnProgress(int bytes_written, bool done) { 187 DCHECK(bytes_written + bytes_written_backlog_ >= bytes_written_backlog_); 188 static const int kMinProgressDelayMS = 200; 189 base::Time currentTime = base::Time::Now(); 190 if (done || last_progress_event_time_.is_null() || 191 (currentTime - last_progress_event_time_).InMilliseconds() > 192 kMinProgressDelayMS) { 193 bytes_written += bytes_written_backlog_; 194 last_progress_event_time_ = currentTime; 195 bytes_written_backlog_ = 0; 196 197 if (done) { 198 MaybeFlushForCompletion(base::File::FILE_OK, bytes_written, 199 SUCCESS_COMPLETED); 200 } else { 201 write_callback_.Run(base::File::FILE_OK, bytes_written, 202 SUCCESS_IO_PENDING); 203 } 204 return; 205 } 206 bytes_written_backlog_ += bytes_written; 207 } 208 209 void FileWriterDelegate::OnWriteCancelled(int status) { 210 write_callback_.Run(base::File::FILE_ERROR_ABORT, 0, 211 GetCompletionStatusOnError()); 212 } 213 214 void FileWriterDelegate::MaybeFlushForCompletion( 215 base::File::Error error, 216 int bytes_written, 217 WriteProgressStatus progress_status) { 218 if (flush_policy_ == NO_FLUSH_ON_COMPLETION) { 219 write_callback_.Run(error, bytes_written, progress_status); 220 return; 221 } 222 DCHECK_EQ(FLUSH_ON_COMPLETION, flush_policy_); 223 224 int flush_error = file_stream_writer_->Flush( 225 base::Bind(&FileWriterDelegate::OnFlushed, weak_factory_.GetWeakPtr(), 226 error, bytes_written, progress_status)); 227 if (flush_error != net::ERR_IO_PENDING) 228 OnFlushed(error, bytes_written, progress_status, flush_error); 229 } 230 231 void FileWriterDelegate::OnFlushed(base::File::Error error, 232 int bytes_written, 233 WriteProgressStatus progress_status, 234 int flush_error) { 235 if (error == base::File::FILE_OK && flush_error != net::OK) { 236 // If the Flush introduced an error, overwrite the status. 237 // Otherwise, keep the original error status. 238 error = NetErrorToFileError(flush_error); 239 progress_status = GetCompletionStatusOnError(); 240 } 241 write_callback_.Run(error, bytes_written, progress_status); 242 } 243 244 } // namespace storage 245