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

Unified Diff: third_party/WebKit/Source/modules/fetch/FormDataBytesConsumer.cpp

Issue 2710033003: Add unknown file size handling in ComplexFormDataBytesConsumer (Closed)
Patch Set: fix Created 3 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: third_party/WebKit/Source/modules/fetch/FormDataBytesConsumer.cpp
diff --git a/third_party/WebKit/Source/modules/fetch/FormDataBytesConsumer.cpp b/third_party/WebKit/Source/modules/fetch/FormDataBytesConsumer.cpp
index 0e36d2d7df54c02992f8a87ea94caded250b6b30..96de7535cf8d3025ae9f20e975789d4cd6fbf273 100644
--- a/third_party/WebKit/Source/modules/fetch/FormDataBytesConsumer.cpp
+++ b/third_party/WebKit/Source/modules/fetch/FormDataBytesConsumer.cpp
@@ -4,8 +4,13 @@
#include "modules/fetch/FormDataBytesConsumer.h"
+#include "bindings/core/v8/ExceptionState.h"
#include "core/dom/DOMArrayBuffer.h"
#include "core/dom/DOMArrayBufferView.h"
+#include "core/dom/ExecutionContext.h"
+#include "core/dom/TaskRunnerHelper.h"
+#include "core/fileapi/Blob.h"
+#include "core/fileapi/File.h"
#include "modules/fetch/BlobBytesConsumer.h"
#include "platform/blob/BlobData.h"
#include "platform/network/EncodedFormData.h"
@@ -18,6 +23,8 @@ namespace blink {
namespace {
+using BytesConsumerFactory = FormDataBytesConsumer::BytesConsumerFactory;
+
bool isSimple(const EncodedFormData* formData) {
for (const auto& element : formData->elements()) {
if (element.m_type != FormDataElement::data)
@@ -101,96 +108,204 @@ class SimpleFormDataBytesConsumer : public BytesConsumer {
};
class ComplexFormDataBytesConsumer final : public BytesConsumer {
+ enum class State {
+ Clean,
+ Reading,
+ Closed,
+ Errored,
+ };
+
public:
ComplexFormDataBytesConsumer(ExecutionContext* executionContext,
PassRefPtr<EncodedFormData> formData,
- BytesConsumer* consumer)
- : m_formData(formData) {
- if (consumer) {
- // For testing.
- m_blobBytesConsumer = consumer;
- return;
- }
+ std::unique_ptr<BytesConsumerFactory> factory)
+ : m_formData(formData),
+ m_executionContext(executionContext),
+ m_factoryForTesting(std::move(factory)) {}
- std::unique_ptr<BlobData> blobData = BlobData::create();
- for (const auto& element : m_formData->elements()) {
- switch (element.m_type) {
- case FormDataElement::data:
- blobData->appendBytes(element.m_data.data(), element.m_data.size());
- break;
- case FormDataElement::encodedFile:
- blobData->appendFile(element.m_filename, element.m_fileStart,
- element.m_fileLength,
dmurph 2017/02/24 19:49:24 Why not just check if the file size is -1, and if
yhirano 2017/03/02 04:05:04 Fixed.
- element.m_expectedFileModificationTime);
- break;
- case FormDataElement::encodedBlob:
- if (element.m_optionalBlobDataHandle)
- blobData->appendBlob(element.m_optionalBlobDataHandle, 0,
- element.m_optionalBlobDataHandle->size());
- break;
- case FormDataElement::encodedFileSystemURL:
- blobData->appendFileSystemURL(
- element.m_fileSystemURL, element.m_fileStart,
- element.m_fileLength, element.m_expectedFileModificationTime);
- break;
+ // BytesConsumer implementation
+ Result beginRead(const char** buffer, size_t* available) override {
+ *buffer = nullptr;
+ *available = 0;
+ if (m_state == State::Closed)
+ return Result::Done;
+ if (m_state == State::Errored)
+ return Result::Error;
+ if (m_state == State::Clean)
+ m_state = State::Reading;
+
+ if (m_blobBytesConsumer) {
+ auto r = m_blobBytesConsumer->beginRead(buffer, available);
+ if (r == Result::Error) {
+ m_state = State::Errored;
+ return r;
}
+ if (r == Result::Done) {
+ ++m_elementIndex;
+ m_blobBytesConsumer = nullptr;
+ if (m_elementIndex == m_formData->elements().size()) {
+ m_state = State::Closed;
+ return Result::Done;
+ }
+ return beginRead(buffer, available);
+ }
+ return r;
+ }
+ const auto& element = m_formData->elements()[m_elementIndex];
+ if (element.m_type == FormDataElement::data) {
+ DCHECK_LE(m_offset, element.m_data.size());
+ *buffer = &element.m_data[m_offset];
+ *available = element.m_data.size() - m_offset;
+ return Result::Ok;
}
- // Here we handle m_formData->boundary() as a C-style string. See
- // FormDataEncoder::generateUniqueBoundaryString.
- blobData->setContentType(AtomicString("multipart/form-data; boundary=") +
- m_formData->boundary().data());
- auto size = blobData->length();
- m_blobBytesConsumer = new BlobBytesConsumer(
- executionContext, BlobDataHandle::create(std::move(blobData), size));
- }
- // BytesConsumer implementation
- Result beginRead(const char** buffer, size_t* available) override {
- m_formData = nullptr;
- // Delegate the operation to the underlying consumer. This relies on
- // the fact that we appropriately notify the draining information to
- // the underlying consumer.
- return m_blobBytesConsumer->beginRead(buffer, available);
+ RefPtr<BlobDataHandle> blobDataHandle = nullptr;
+ const String type = "application/octet-stream";
+ switch (element.m_type) {
+ case FormDataElement::data:
+ NOTREACHED();
+ break;
+ case FormDataElement::encodedBlob:
+ if (element.m_optionalBlobDataHandle)
+ blobDataHandle = element.m_optionalBlobDataHandle;
+ else
+ blobDataHandle = BlobDataHandle::create(element.m_blobUUID, type, -1);
+ break;
+ case FormDataElement::encodedFile: {
+ NonThrowableExceptionState es;
+ Blob* file = File::create(element.m_filename);
+ if (element.m_fileStart > 0) {
+ file =
+ file->slice(element.m_fileStart,
+ element.m_fileStart + element.m_fileLength, type, es);
+ }
+ blobDataHandle = file->blobDataHandle();
+ break;
+ }
+ case FormDataElement::encodedFileSystemURL: {
+ FileMetadata metadata;
+ metadata.length = element.m_fileStart + element.m_fileLength;
+ NonThrowableExceptionState es;
+ Blob* file = File::createForFileSystemFile(
+ element.m_fileSystemURL, metadata, File::IsUserVisible);
+ if (element.m_fileStart > 0) {
+ file =
+ file->slice(element.m_fileStart,
+ element.m_fileStart + element.m_fileLength, type, es);
+ }
+ blobDataHandle = file->blobDataHandle();
+ break;
+ }
+ }
+ if (m_factoryForTesting) {
+ m_blobBytesConsumer =
+ (*m_factoryForTesting)(m_executionContext, std::move(blobDataHandle));
+ } else {
+ m_blobBytesConsumer =
+ new BlobBytesConsumer(m_executionContext, std::move(blobDataHandle));
+ }
+ if (m_client)
+ m_blobBytesConsumer->setClient(m_client);
+ return beginRead(buffer, available);
}
Result endRead(size_t readSize) override {
- return m_blobBytesConsumer->endRead(readSize);
- }
- PassRefPtr<BlobDataHandle> drainAsBlobDataHandle(
- BlobSizePolicy policy) override {
- RefPtr<BlobDataHandle> handle =
- m_blobBytesConsumer->drainAsBlobDataHandle(policy);
- if (handle)
- m_formData = nullptr;
- return handle.release();
+ DCHECK_EQ(m_state, State::Reading);
+ const auto& element = m_formData->elements()[m_elementIndex];
+ if (element.m_type == FormDataElement::data) {
+ DCHECK_LE(m_offset + readSize, element.m_data.size());
+ m_offset += readSize;
+ if (m_offset == element.m_data.size()) {
+ m_offset = 0;
+ ++m_elementIndex;
+ }
+ } else {
+ DCHECK(m_blobBytesConsumer);
+ auto r = m_blobBytesConsumer->endRead(readSize);
+ if (r == Result::Error) {
+ m_state = State::Errored;
+ return r;
+ }
+ if (r == Result::Done) {
+ m_blobBytesConsumer = nullptr;
+ ++m_elementIndex;
+ }
+ }
+ if (m_elementIndex == m_formData->elements().size()) {
+ m_state = State::Closed;
+ return Result::Done;
+ }
+ return Result::Ok;
}
PassRefPtr<EncodedFormData> drainAsFormData() override {
- if (!m_formData)
+ if (m_state != State::Clean)
return nullptr;
- m_blobBytesConsumer->cancel();
+ DCHECK(!m_blobBytesConsumer);
+ DCHECK(m_formData);
+ m_state = State::Closed;
return m_formData.release();
}
void setClient(BytesConsumer::Client* client) override {
- m_blobBytesConsumer->setClient(client);
+ DCHECK(client);
+ m_client = client;
+ if (m_blobBytesConsumer) {
+ m_blobBytesConsumer->setClient(client);
+ } else if (getPublicState() == PublicState::ReadableOrWaiting) {
+ TaskRunnerHelper::get(TaskType::Networking, m_executionContext)
+ ->postTask(BLINK_FROM_HERE,
+ WTF::bind(&BytesConsumer::Client::onStateChange,
+ wrapPersistent(m_client.get())));
+ }
+ }
+ void clearClient() override {
+ m_client = nullptr;
+ if (m_blobBytesConsumer)
+ m_blobBytesConsumer->clearClient();
}
- void clearClient() override { m_blobBytesConsumer->clearClient(); }
void cancel() override {
+ if (m_state == State::Closed || m_state == State::Errored)
+ return;
m_formData = nullptr;
- m_blobBytesConsumer->cancel();
+ m_client = nullptr;
+ m_state = State::Closed;
+ if (m_blobBytesConsumer)
+ m_blobBytesConsumer->cancel();
}
PublicState getPublicState() const override {
- return m_blobBytesConsumer->getPublicState();
+ switch (m_state) {
+ case State::Clean:
+ case State::Reading:
+ return PublicState::ReadableOrWaiting;
+ case State::Closed:
+ return PublicState::Closed;
+ case State::Errored:
+ return PublicState::Errored;
+ }
+ NOTREACHED();
+ return PublicState::Errored;
+ }
+ Error getError() const override {
+ if (m_blobBytesConsumer)
+ return m_blobBytesConsumer->getError();
+ return Error();
}
- Error getError() const override { return m_blobBytesConsumer->getError(); }
String debugName() const override { return "ComplexFormDataBytesConsumer"; }
DEFINE_INLINE_TRACE() {
visitor->trace(m_blobBytesConsumer);
+ visitor->trace(m_client);
+ visitor->trace(m_executionContext);
BytesConsumer::trace(visitor);
}
private:
+ unsigned m_elementIndex = 0;
+ size_t m_offset = 0;
+ State m_state = State::Clean;
RefPtr<EncodedFormData> m_formData;
Member<BytesConsumer> m_blobBytesConsumer;
+ Member<BytesConsumer::Client> m_client;
+ Member<ExecutionContext> m_executionContext;
+ std::unique_ptr<BytesConsumerFactory> m_factoryForTesting;
};
} // namespace
@@ -217,13 +332,13 @@ FormDataBytesConsumer::FormDataBytesConsumer(
FormDataBytesConsumer::FormDataBytesConsumer(
ExecutionContext* executionContext,
PassRefPtr<EncodedFormData> formData,
- BytesConsumer* consumer)
+ std::unique_ptr<BytesConsumerFactory> factory)
: m_impl(isSimple(formData.get())
? static_cast<BytesConsumer*>(
new SimpleFormDataBytesConsumer(std::move(formData)))
: static_cast<BytesConsumer*>(
new ComplexFormDataBytesConsumer(executionContext,
std::move(formData),
- consumer))) {}
+ std::move(factory)))) {}
} // namespace blink

Powered by Google App Engine
This is Rietveld 408576698