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

Side by Side Diff: content/browser/renderer_host/websocket_blob_receiver.cc

Issue 1626453003: [OBSOLETE] Browser-side implementation of WebSocket Blob receive. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Add WebSocketHost::ReceiveQuotaMultiplexer Created 4 years, 10 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
OLDNEW
(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
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698