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

Unified 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 side-by-side diff with in-line comments
Download patch
Index: content/browser/renderer_host/websocket_blob_receiver.cc
diff --git a/content/browser/renderer_host/websocket_blob_receiver.cc b/content/browser/renderer_host/websocket_blob_receiver.cc
new file mode 100644
index 0000000000000000000000000000000000000000..c98cb8795d54b763094bf545b7d4e806f0777461
--- /dev/null
+++ b/content/browser/renderer_host/websocket_blob_receiver.cc
@@ -0,0 +1,322 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "websocket_blob_receiver.h"
+
+#include <stddef.h>
+#include <string.h>
+#include <ostream>
+#include <utility>
+
+#include "base/bind.h"
+#include "base/bind_helpers.h"
+#include "base/files/file_util.h"
+#include "base/guid.h"
+#include "base/location.h"
+#include "base/logging.h"
+#include "base/message_loop/message_loop.h"
+#include "base/numerics/safe_conversions.h"
+#include "base/single_thread_task_runner.h"
+#include "content/browser/loader/temporary_file_stream.h"
+#include "content/public/browser/browser_thread.h"
+#include "net/base/file_stream.h"
+#include "net/base/io_buffer.h"
+#include "net/base/net_errors.h"
+#include "storage/browser/blob/blob_data_builder.h"
+#include "storage/browser/blob/blob_storage_context.h"
+#include "storage/browser/blob/shareable_file_reference.h"
+
+namespace content {
+
+namespace {
+// This should be the same as the maximum receive quota provided by the
+// renderer, otherwise WebSocketHost will have to do unnecessary buffering.
+const size_t kBufferSize = 64 * 1024;
+}
+
+// Needed to make CHECK_EQ(), etc. work
+std::ostream& operator<<(std::ostream& os, WebSocketBlobReceiver::State state) {
+ static const char* const kStateStrings[] = {
+ "NONE",
+ "CREATE_FILE",
+ "CREATE_FILE_COMPLETE",
+ "SEND_QUOTA",
+ "WRITE",
+ "WRITE_COMPLETE",
+ "GET_INFO",
+ "GET_INFO_COMPLETE",
+ };
+
+ if (state < WebSocketBlobReceiver::State::NONE ||
+ state > WebSocketBlobReceiver::State::GET_INFO_COMPLETE) {
+ return os << "Bad state (" << static_cast<int>(state) << ")";
+ }
+ return os << kStateStrings[static_cast<int>(state)];
+}
+
+// Helper object to call GetFileInfo() on the FILE thread
+class WebSocketBlobReceiver::FileInfoHelper {
+ public:
+ FileInfoHelper(const base::WeakPtr<WebSocketBlobReceiver>& owner)
+ : owner_(owner) {}
+
+ void GetFileInfo(const base::FilePath& path) {
+ result_ = base::GetFileInfo(path, &info_);
+ }
+
+ void DidGetInfo() {
+ if (owner_)
+ owner_->DidGetInfo(result_, info_);
+ }
+
+ private:
+ const base::WeakPtr<WebSocketBlobReceiver> owner_;
+ bool result_ = false;
+ base::File::Info info_;
+
+ DISALLOW_COPY_AND_ASSIGN(FileInfoHelper);
+};
+
+WebSocketBlobReceiver::WebSocketBlobReceiver(
+ scoped_ptr<Client> client,
+ storage::BlobStorageContext* blob_storage_context)
+ : client_(std::move(client)),
+ blob_storage_context_(blob_storage_context),
+ io_buffer_(new net::DrainableIOBuffer(new net::IOBuffer(kBufferSize),
+ kBufferSize)),
+ pending_quota_(kBufferSize),
+ weak_factory_(this) {
+ DCHECK(blob_storage_context_);
+ io_buffer_->SetOffset(kBufferSize);
+}
+
+WebSocketBlobReceiver::~WebSocketBlobReceiver() {}
+
+void WebSocketBlobReceiver::Start() {
+ // This doesn't limit the space used by a single origin or take into account
+ // incognito mode. TODO(ricea): Apply quota and take into account incognito
+ // mode.
+ next_state_ = State::CREATE_FILE;
+ int rv = DoLoop(net::OK);
+ DCHECK_EQ(net::ERR_IO_PENDING, rv);
+}
+
+int WebSocketBlobReceiver::AppendData(const std::vector<char>& data) {
+ CHECK_LE(data.size(), kBufferSize);
+ DCHECK_NE(next_state_, State::NONE);
+ if (data.size() == 0)
+ return net::OK;
+ if (io_in_progress_) {
+ pending_data_.insert(pending_data_.end(), data.begin(), data.end());
+ return net::ERR_IO_PENDING;
+ }
+ PrepareWrite(data);
+ return DoLoop(net::OK);
+}
+
+int WebSocketBlobReceiver::Finish() {
+ DCHECK(!finish_called_);
+ finish_called_ = true;
+ DCHECK_NE(next_state_, State::NONE);
+ DCHECK_NE(next_state_, State::GET_INFO);
+ DCHECK_NE(next_state_, State::GET_INFO_COMPLETE);
+ if (io_in_progress_)
+ return net::ERR_IO_PENDING;
+ return DoLoop(net::OK);
+}
+
+int WebSocketBlobReceiver::DoLoop(int result) {
+ int rv = result;
+ do {
+ State state = next_state_;
+ next_state_ = State::NONE;
+ switch (state) {
+ case State::CREATE_FILE:
+ DCHECK_EQ(net::OK, rv);
+ rv = DoCreateFile();
+ break;
+
+ case State::CREATE_FILE_COMPLETE:
+ rv = DoCreateFileComplete(rv);
+ break;
+
+ case State::SEND_QUOTA:
+ DCHECK_EQ(net::OK, rv);
+ rv = DoSendQuota();
+ break;
+
+ case State::WRITE:
+ DCHECK_EQ(net::OK, rv);
+ rv = DoWrite();
+ break;
+
+ case State::WRITE_COMPLETE:
+ rv = DoWriteComplete(rv);
+ break;
+
+ case State::GET_INFO:
+ DCHECK_EQ(net::OK, rv);
+ rv = DoGetInfo();
+ break;
+
+ case State::GET_INFO_COMPLETE:
+ rv = DoGetInfoComplete(rv);
+ break;
+
+ default:
+ NOTREACHED();
+ }
+ } while (rv != net::ERR_IO_PENDING && next_state_ != State::NONE);
+ return rv;
+}
+
+void WebSocketBlobReceiver::DoLoopAsync(int result) {
+ int rv = DoLoop(result);
+ if (rv == net::ERR_IO_PENDING)
+ return;
+ if (rv < 0)
+ client_->BlobFailed(rv);
+}
+
+int WebSocketBlobReceiver::DoCreateFile() {
+ next_state_ = State::CREATE_FILE_COMPLETE;
+ io_in_progress_ = true;
+ CreateTemporaryFileStream(
+ base::Bind(&WebSocketBlobReceiver::DidCreateTemporaryFileStream,
+ weak_factory_.GetWeakPtr()));
+ return net::ERR_IO_PENDING;
+}
+
+int WebSocketBlobReceiver::DoCreateFileComplete(int result) {
+ io_in_progress_ = false;
+ if (result < 0)
+ return result;
+ next_state_ = State::SEND_QUOTA;
+ return result;
+}
+
+int WebSocketBlobReceiver::DoSendQuota() {
+ next_state_ = State::WRITE;
+ if (finish_called_)
+ return net::OK;
+ DCHECK_GT(pending_quota_, 0u);
+ base::MessageLoop::current()->PostTask(
+ FROM_HERE, base::Bind(&WebSocketBlobReceiver::DoSendQuotaAsync,
+ weak_factory_.GetWeakPtr(), pending_quota_));
+ pending_quota_ = 0u;
+ return net::ERR_IO_PENDING;
+}
+
+int WebSocketBlobReceiver::DoWrite() {
+ next_state_ = State::WRITE_COMPLETE;
+ io_in_progress_ = true;
+ if (io_buffer_->BytesRemaining() == 0)
+ return net::OK;
+ // This use of base::Unretained() is safe because |file_stream_| will cancel
+ // writing on destruction, and its lifetime is tied to
+ // |deletable_file_|. |deletable_file_| was created by this object, and
+ // remains solely owned by this object until BlobCreated() is called.
+ // BlobCreated() will not be called while |io_in_progress_| is true.
+ return file_stream_->Write(io_buffer_.get(), io_buffer_->BytesRemaining(),
+ base::Bind(&WebSocketBlobReceiver::OnWriteComplete,
+ base::Unretained(this)));
+}
+
+int WebSocketBlobReceiver::DoWriteComplete(int result) {
+ io_in_progress_ = false;
+ if (result < 0)
+ return result;
+ if (result > 0)
+ io_buffer_->DidConsume(result);
+ if (io_buffer_->BytesRemaining() > 0) {
+ next_state_ = State::WRITE;
+ return net::OK;
+ }
+ if (!pending_data_.empty()) {
+ next_state_ = State::WRITE;
+ std::vector<char> pending_data;
+ pending_data.swap(pending_data_);
+ PrepareWrite(pending_data);
+ return net::OK;
+ }
+ if (finish_called_) {
+ next_state_ = State::GET_INFO;
+ return net::OK;
+ }
+ DCHECK_GT(pending_quota_, 0u);
+ next_state_ = State::SEND_QUOTA;
+ return net::OK;
+}
+
+int WebSocketBlobReceiver::DoGetInfo() {
+ next_state_ = State::GET_INFO_COMPLETE;
+ scoped_refptr<base::SingleThreadTaskRunner> file_thread =
+ BrowserThread::GetMessageLoopProxyForThread(BrowserThread::FILE);
+ FileInfoHelper* file_info_helper =
+ new FileInfoHelper(weak_factory_.GetWeakPtr());
+ file_thread->PostTaskAndReply(
+ FROM_HERE,
+ base::Bind(&FileInfoHelper::GetFileInfo,
+ base::Unretained(file_info_helper), deletable_file_->path()),
+ base::Bind(&FileInfoHelper::DidGetInfo, base::Owned(file_info_helper)));
+ return net::ERR_IO_PENDING;
+}
+
+int WebSocketBlobReceiver::DoGetInfoComplete(int result) {
+ return result;
+}
+
+void WebSocketBlobReceiver::OnWriteComplete(int result) {
+ DoLoopAsync(result);
+ // |this| may be destroyed here.
+}
+
+void WebSocketBlobReceiver::DidCreateTemporaryFileStream(
+ base::File::Error error_code,
+ scoped_ptr<net::FileStream> file_stream,
+ storage::ShareableFileReference* deletable_file) {
+ if (error_code == base::File::FILE_OK) {
+ file_stream_.swap(file_stream);
+ deletable_file_ = deletable_file;
+ }
+ DoLoopAsync(net::FileErrorToNetError(error_code));
+ // |this| may be destroyed here.
+}
+
+void WebSocketBlobReceiver::PrepareWrite(const std::vector<char>& data) {
+ CHECK_LE(data.size(), kBufferSize);
+ pending_quota_ += data.size();
+ io_buffer_->SetOffset(kBufferSize - data.size());
+ memcpy(io_buffer_->data(), data.data(), data.size());
+}
+
+void WebSocketBlobReceiver::DidGetInfo(bool result,
+ const base::File::Info& info) {
+ if (result) {
+ storage::BlobDataBuilder builder(base::GenerateGUID());
+ builder.AppendFile(deletable_file_->path(), UINT64_C(0),
+ base::checked_cast<uint64_t>(info.size),
+ info.last_modified);
+ scoped_ptr<storage::BlobDataHandle> blob_data_handle(
+ blob_storage_context_->AddFinishedBlob(builder));
+ if (!blob_data_handle) {
+ DoLoopAsync(net::ERR_OUT_OF_MEMORY);
+ // |this| may be destroyed here.
+ return;
+ }
+ client_->BlobCreated(std::move(blob_data_handle), info.size);
+ // |this| may be destroyed here.
+ return;
+ }
+ DoLoopAsync(net::ERR_FILE_NOT_FOUND);
+ // |this| may be destroyed here.
+}
+
+// This is called asynchronously rather than from the DoLoop() because it may
+// destroy |this|.
+void WebSocketBlobReceiver::DoSendQuotaAsync(size_t quota) {
+ client_->AddFlowControlQuota(quota);
+}
+
+} // namespace content

Powered by Google App Engine
This is Rietveld 408576698