OLD | NEW |
(Empty) | |
| 1 // Copyright 2016 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 "websocket_blob_receiver.h" |
| 6 |
| 7 #include <stddef.h> |
| 8 #include <string.h> |
| 9 #include <ostream> |
| 10 #include <utility> |
| 11 |
| 12 #include "base/bind.h" |
| 13 #include "base/bind_helpers.h" |
| 14 #include "base/files/file_util.h" |
| 15 #include "base/guid.h" |
| 16 #include "base/logging.h" |
| 17 #include "base/numerics/safe_conversions.h" |
| 18 #include "base/single_thread_task_runner.h" |
| 19 #include "content/browser/loader/temporary_file_stream.h" |
| 20 #include "content/public/browser/browser_thread.h" |
| 21 #include "net/base/file_stream.h" |
| 22 #include "net/base/io_buffer.h" |
| 23 #include "net/base/net_errors.h" |
| 24 #include "storage/browser/blob/blob_data_builder.h" |
| 25 #include "storage/browser/blob/blob_storage_context.h" |
| 26 #include "storage/browser/blob/shareable_file_reference.h" |
| 27 |
| 28 namespace content { |
| 29 |
| 30 namespace { |
| 31 // Use a fixed buffer size for simplicity. |
| 32 const size_t kBufferSize = 32 * 1024 * 1024; |
| 33 } |
| 34 |
| 35 // Needed to make CHECK_EQ(), etc. work |
| 36 std::ostream& operator<<(std::ostream& os, WebSocketBlobReceiver::State state) { |
| 37 static const char* const kStateStrings[] = { |
| 38 "NONE", |
| 39 "CREATE_FILE", |
| 40 "CREATE_FILE_COMPLETE", |
| 41 "SEND_QUOTA", |
| 42 "WRITE", |
| 43 "WRITE_COMPLETE", |
| 44 "GET_INFO", |
| 45 "GET_INFO_COMPLETE", |
| 46 }; |
| 47 |
| 48 if (state < WebSocketBlobReceiver::State::NONE || |
| 49 state > WebSocketBlobReceiver::State::GET_INFO_COMPLETE) { |
| 50 return os << "Bad state (" << static_cast<int>(state) << ")"; |
| 51 } |
| 52 return os << kStateStrings[static_cast<int>(state)]; |
| 53 } |
| 54 |
| 55 // Helper object to call GetFileInfo() on the FILE thread |
| 56 class WebSocketBlobReceiver::FileInfoHelper { |
| 57 public: |
| 58 FileInfoHelper(const base::WeakPtr<WebSocketBlobReceiver>& owner) |
| 59 : owner_(owner) {} |
| 60 |
| 61 void GetFileInfo(const base::FilePath& path) { |
| 62 result_ = base::GetFileInfo(path, &info_); |
| 63 } |
| 64 |
| 65 void DidGetInfo() { |
| 66 if (owner_) |
| 67 owner_->DidGetInfo(result_, info_); |
| 68 } |
| 69 |
| 70 private: |
| 71 const base::WeakPtr<WebSocketBlobReceiver> owner_; |
| 72 bool result_ = false; |
| 73 base::File::Info info_; |
| 74 |
| 75 DISALLOW_COPY_AND_ASSIGN(FileInfoHelper); |
| 76 }; |
| 77 |
| 78 WebSocketBlobReceiver::WebSocketBlobReceiver( |
| 79 scoped_ptr<Client> client, |
| 80 storage::BlobStorageContext* blob_storage_context) |
| 81 : client_(std::move(client)), |
| 82 blob_storage_context_(blob_storage_context), |
| 83 io_buffer_(new net::DrainableIOBuffer(new net::IOBuffer(kBufferSize), |
| 84 kBufferSize)), |
| 85 pending_quota_(kBufferSize), |
| 86 weak_factory_(this) { |
| 87 DCHECK(blob_storage_context_); |
| 88 io_buffer_->SetOffset(kBufferSize); |
| 89 } |
| 90 |
| 91 WebSocketBlobReceiver::~WebSocketBlobReceiver() {} |
| 92 |
| 93 void WebSocketBlobReceiver::Start() { |
| 94 // This doesn't limit the space used by a single origin or take into account |
| 95 // incognito mode. TODO(ricea): Apply quota and take into account incognito |
| 96 // mode. |
| 97 next_state_ = State::CREATE_FILE; |
| 98 int rv = DoLoop(net::OK); |
| 99 DCHECK_EQ(net::ERR_IO_PENDING, rv); |
| 100 } |
| 101 |
| 102 int WebSocketBlobReceiver::AppendData(const std::vector<char>& data) { |
| 103 CHECK_LE(data.size(), kBufferSize); |
| 104 DCHECK_NE(next_state_, State::NONE); |
| 105 if (data.size() == 0) |
| 106 return net::OK; |
| 107 if (io_in_progress_) { |
| 108 pending_data_.insert(pending_data_.end(), data.begin(), data.end()); |
| 109 return net::ERR_IO_PENDING; |
| 110 } |
| 111 PrepareWrite(data); |
| 112 return DoLoop(net::OK); |
| 113 } |
| 114 |
| 115 int WebSocketBlobReceiver::Finish() { |
| 116 DCHECK(!finish_called_); |
| 117 finish_called_ = true; |
| 118 DCHECK_NE(next_state_, State::NONE); |
| 119 DCHECK_NE(next_state_, State::GET_INFO); |
| 120 DCHECK_NE(next_state_, State::GET_INFO_COMPLETE); |
| 121 if (io_in_progress_) |
| 122 return net::ERR_IO_PENDING; |
| 123 return DoLoop(net::OK); |
| 124 } |
| 125 |
| 126 int WebSocketBlobReceiver::DoLoop(int result) { |
| 127 int rv = result; |
| 128 do { |
| 129 State state = next_state_; |
| 130 next_state_ = State::NONE; |
| 131 switch (state) { |
| 132 case State::CREATE_FILE: |
| 133 DCHECK_EQ(net::OK, rv); |
| 134 rv = DoCreateFile(); |
| 135 break; |
| 136 |
| 137 case State::CREATE_FILE_COMPLETE: |
| 138 rv = DoCreateFileComplete(rv); |
| 139 break; |
| 140 |
| 141 case State::SEND_QUOTA: |
| 142 DCHECK_EQ(net::OK, rv); |
| 143 rv = DoSendQuota(); |
| 144 break; |
| 145 |
| 146 case State::WRITE: |
| 147 DCHECK_EQ(net::OK, rv); |
| 148 rv = DoWrite(); |
| 149 break; |
| 150 |
| 151 case State::WRITE_COMPLETE: |
| 152 rv = DoWriteComplete(rv); |
| 153 break; |
| 154 |
| 155 case State::GET_INFO: |
| 156 DCHECK_EQ(net::OK, rv); |
| 157 rv = DoGetInfo(); |
| 158 break; |
| 159 |
| 160 case State::GET_INFO_COMPLETE: |
| 161 rv = DoGetInfoComplete(rv); |
| 162 break; |
| 163 |
| 164 default: |
| 165 NOTREACHED(); |
| 166 } |
| 167 } while (rv != net::ERR_IO_PENDING && next_state_ != State::NONE); |
| 168 return rv; |
| 169 } |
| 170 |
| 171 void WebSocketBlobReceiver::DoLoopAsync(int result) { |
| 172 int rv = DoLoop(result); |
| 173 if (rv == net::ERR_IO_PENDING) |
| 174 return; |
| 175 if (rv < 0) |
| 176 client_->BlobFailed(rv); |
| 177 } |
| 178 |
| 179 int WebSocketBlobReceiver::DoCreateFile() { |
| 180 next_state_ = State::CREATE_FILE_COMPLETE; |
| 181 io_in_progress_ = true; |
| 182 CreateTemporaryFileStream( |
| 183 base::Bind(&WebSocketBlobReceiver::DidCreateTemporaryFileStream, |
| 184 weak_factory_.GetWeakPtr())); |
| 185 return net::ERR_IO_PENDING; |
| 186 } |
| 187 |
| 188 int WebSocketBlobReceiver::DoCreateFileComplete(int result) { |
| 189 io_in_progress_ = false; |
| 190 if (result < 0) |
| 191 return result; |
| 192 next_state_ = State::SEND_QUOTA; |
| 193 return result; |
| 194 } |
| 195 |
| 196 int WebSocketBlobReceiver::DoSendQuota() { |
| 197 next_state_ = State::WRITE; |
| 198 if (finish_called_) |
| 199 return net::OK; |
| 200 DCHECK_GT(pending_quota_, 0u); |
| 201 client_->AddFlowControlQuota(pending_quota_); |
| 202 pending_quota_ = 0u; |
| 203 return net::ERR_IO_PENDING; |
| 204 } |
| 205 |
| 206 int WebSocketBlobReceiver::DoWrite() { |
| 207 next_state_ = State::WRITE_COMPLETE; |
| 208 io_in_progress_ = true; |
| 209 if (io_buffer_->BytesRemaining() == 0) |
| 210 return net::OK; |
| 211 // This use of base::Unretained() is safe because |file_stream_| will cancel |
| 212 // writing on destruction, and its lifetime is tied to |
| 213 // |deletable_file_|. |deletable_file_| was created by this object, and |
| 214 // remains solely owned by this object until BlobCreated() is called. |
| 215 // BlobCreated() will not be called while |io_in_progress_| is true. |
| 216 return file_stream_->Write(io_buffer_.get(), io_buffer_->BytesRemaining(), |
| 217 base::Bind(&WebSocketBlobReceiver::OnWriteComplete, |
| 218 base::Unretained(this))); |
| 219 } |
| 220 |
| 221 int WebSocketBlobReceiver::DoWriteComplete(int result) { |
| 222 io_in_progress_ = false; |
| 223 if (result < 0) |
| 224 return result; |
| 225 if (result > 0) |
| 226 io_buffer_->DidConsume(result); |
| 227 if (io_buffer_->BytesRemaining() > 0) { |
| 228 next_state_ = State::WRITE; |
| 229 return net::OK; |
| 230 } |
| 231 if (!pending_data_.empty()) { |
| 232 next_state_ = State::WRITE; |
| 233 std::vector<char> pending_data; |
| 234 pending_data.swap(pending_data_); |
| 235 PrepareWrite(pending_data); |
| 236 return net::OK; |
| 237 } |
| 238 if (finish_called_) { |
| 239 next_state_ = State::GET_INFO; |
| 240 return net::OK; |
| 241 } |
| 242 DCHECK_GT(pending_quota_, 0u); |
| 243 next_state_ = State::SEND_QUOTA; |
| 244 return net::OK; |
| 245 } |
| 246 |
| 247 int WebSocketBlobReceiver::DoGetInfo() { |
| 248 next_state_ = State::GET_INFO_COMPLETE; |
| 249 scoped_refptr<base::SingleThreadTaskRunner> file_thread = |
| 250 BrowserThread::GetMessageLoopProxyForThread(BrowserThread::FILE); |
| 251 FileInfoHelper* file_info_helper = |
| 252 new FileInfoHelper(weak_factory_.GetWeakPtr()); |
| 253 file_thread->PostTaskAndReply( |
| 254 FROM_HERE, |
| 255 base::Bind(&FileInfoHelper::GetFileInfo, |
| 256 base::Unretained(file_info_helper), deletable_file_->path()), |
| 257 base::Bind(&FileInfoHelper::DidGetInfo, base::Owned(file_info_helper))); |
| 258 return net::ERR_IO_PENDING; |
| 259 } |
| 260 |
| 261 int WebSocketBlobReceiver::DoGetInfoComplete(int result) { |
| 262 return result; |
| 263 } |
| 264 |
| 265 void WebSocketBlobReceiver::OnWriteComplete(int result) { |
| 266 DoLoopAsync(result); |
| 267 } |
| 268 |
| 269 void WebSocketBlobReceiver::DidCreateTemporaryFileStream( |
| 270 base::File::Error error_code, |
| 271 scoped_ptr<net::FileStream> file_stream, |
| 272 storage::ShareableFileReference* deletable_file) { |
| 273 if (error_code == base::File::FILE_OK) { |
| 274 file_stream_.swap(file_stream); |
| 275 deletable_file_ = deletable_file; |
| 276 } |
| 277 DoLoopAsync(net::FileErrorToNetError(error_code)); |
| 278 } |
| 279 |
| 280 void WebSocketBlobReceiver::PrepareWrite(const std::vector<char>& data) { |
| 281 CHECK_LE(data.size(), kBufferSize); |
| 282 pending_quota_ += data.size(); |
| 283 io_buffer_->SetOffset(kBufferSize - data.size()); |
| 284 memcpy(io_buffer_->data(), data.data(), data.size()); |
| 285 } |
| 286 |
| 287 void WebSocketBlobReceiver::DidGetInfo(bool result, |
| 288 const base::File::Info& info) { |
| 289 if (result) { |
| 290 storage::BlobDataBuilder builder(base::GenerateGUID()); |
| 291 builder.AppendFile(deletable_file_->path(), UINT64_C(0), |
| 292 base::checked_cast<uint64_t>(info.size), |
| 293 info.last_modified); |
| 294 scoped_ptr<storage::BlobDataHandle> blob_data_handle( |
| 295 blob_storage_context_->AddFinishedBlob(builder)); |
| 296 if (!blob_data_handle) { |
| 297 DoLoopAsync(net::ERR_OUT_OF_MEMORY); |
| 298 return; |
| 299 } |
| 300 client_->BlobCreated(*blob_data_handle); |
| 301 } |
| 302 DoLoopAsync(result ? net::OK : net::ERR_FILE_NOT_FOUND); |
| 303 } |
| 304 |
| 305 } // namespace content |
OLD | NEW |