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

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

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

Powered by Google App Engine
This is Rietveld 408576698