blob: 2e9a76625d8d2cbc60d16a14851e6b7d6b8283fb [file] [log] [blame]
// 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 "modules/fetch/FormDataBytesConsumer.h"
#include "core/typed_arrays/DOMArrayBuffer.h"
#include "core/typed_arrays/DOMArrayBufferView.h"
#include "modules/fetch/BlobBytesConsumer.h"
#include "platform/blob/BlobData.h"
#include "platform/network/EncodedFormData.h"
#include "platform/wtf/Vector.h"
#include "platform/wtf/text/TextCodec.h"
#include "platform/wtf/text/TextEncoding.h"
#include "platform/wtf/text/WTFString.h"
namespace blink {
namespace {
bool IsSimple(const EncodedFormData* form_data) {
for (const auto& element : form_data->Elements()) {
if (element.type_ != FormDataElement::kData)
return false;
}
return true;
}
class SimpleFormDataBytesConsumer : public BytesConsumer {
public:
explicit SimpleFormDataBytesConsumer(PassRefPtr<EncodedFormData> form_data)
: form_data_(std::move(form_data)) {}
// BytesConsumer implementation
Result BeginRead(const char** buffer, size_t* available) override {
*buffer = nullptr;
*available = 0;
if (form_data_) {
form_data_->Flatten(flatten_form_data_);
form_data_ = nullptr;
DCHECK_EQ(flatten_form_data_offset_, 0u);
}
if (flatten_form_data_offset_ == flatten_form_data_.size())
return Result::kDone;
*buffer = flatten_form_data_.data() + flatten_form_data_offset_;
*available = flatten_form_data_.size() - flatten_form_data_offset_;
return Result::kOk;
}
Result EndRead(size_t read_size) override {
DCHECK(!form_data_);
DCHECK_LE(flatten_form_data_offset_ + read_size, flatten_form_data_.size());
flatten_form_data_offset_ += read_size;
if (flatten_form_data_offset_ == flatten_form_data_.size()) {
state_ = PublicState::kClosed;
return Result::kDone;
}
return Result::kOk;
}
PassRefPtr<BlobDataHandle> DrainAsBlobDataHandle(
BlobSizePolicy policy) override {
if (!form_data_)
return nullptr;
Vector<char> data;
form_data_->Flatten(data);
form_data_ = nullptr;
std::unique_ptr<BlobData> blob_data = BlobData::Create();
blob_data->AppendBytes(data.data(), data.size());
auto length = blob_data->length();
state_ = PublicState::kClosed;
return BlobDataHandle::Create(std::move(blob_data), length);
}
PassRefPtr<EncodedFormData> DrainAsFormData() override {
if (!form_data_)
return nullptr;
state_ = PublicState::kClosed;
return std::move(form_data_);
}
void SetClient(BytesConsumer::Client* client) override { DCHECK(client); }
void ClearClient() override {}
void Cancel() override {
state_ = PublicState::kClosed;
form_data_ = nullptr;
flatten_form_data_.clear();
flatten_form_data_offset_ = 0;
}
PublicState GetPublicState() const override { return state_; }
Error GetError() const override {
NOTREACHED();
return Error();
}
String DebugName() const override { return "SimpleFormDataBytesConsumer"; }
private:
// either one of |m_formData| and |m_flattenFormData| is usable at a time.
RefPtr<EncodedFormData> form_data_;
Vector<char> flatten_form_data_;
size_t flatten_form_data_offset_ = 0;
PublicState state_ = PublicState::kReadableOrWaiting;
};
class ComplexFormDataBytesConsumer final : public BytesConsumer {
public:
ComplexFormDataBytesConsumer(ExecutionContext* execution_context,
PassRefPtr<EncodedFormData> form_data,
BytesConsumer* consumer)
: form_data_(std::move(form_data)) {
if (consumer) {
// For testing.
blob_bytes_consumer_ = consumer;
return;
}
std::unique_ptr<BlobData> blob_data = BlobData::Create();
for (const auto& element : form_data_->Elements()) {
switch (element.type_) {
case FormDataElement::kData:
blob_data->AppendBytes(element.data_.data(), element.data_.size());
break;
case FormDataElement::kEncodedFile: {
auto file_length = element.file_length_;
if (file_length < 0) {
if (!GetFileSize(element.filename_, file_length)) {
form_data_ = nullptr;
blob_bytes_consumer_ = BytesConsumer::CreateErrored(
Error("Cannot determine a file size"));
return;
}
}
blob_data->AppendFile(element.filename_, element.file_start_,
file_length,
element.expected_file_modification_time_);
break;
}
case FormDataElement::kEncodedBlob:
if (element.optional_blob_data_handle_)
blob_data->AppendBlob(element.optional_blob_data_handle_, 0,
element.optional_blob_data_handle_->size());
break;
case FormDataElement::kEncodedFileSystemURL:
if (element.file_length_ < 0) {
form_data_ = nullptr;
blob_bytes_consumer_ = BytesConsumer::CreateErrored(
Error("Cannot determine a file size"));
return;
}
blob_data->AppendFileSystemURL(
element.file_system_url_, element.file_start_,
element.file_length_, element.expected_file_modification_time_);
break;
}
}
// Here we handle m_formData->boundary() as a C-style string. See
// FormDataEncoder::generateUniqueBoundaryString.
blob_data->SetContentType(AtomicString("multipart/form-data; boundary=") +
form_data_->Boundary().data());
auto size = blob_data->length();
blob_bytes_consumer_ = new BlobBytesConsumer(
execution_context, BlobDataHandle::Create(std::move(blob_data), size));
}
// BytesConsumer implementation
Result BeginRead(const char** buffer, size_t* available) override {
form_data_ = 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 blob_bytes_consumer_->BeginRead(buffer, available);
}
Result EndRead(size_t read_size) override {
return blob_bytes_consumer_->EndRead(read_size);
}
PassRefPtr<BlobDataHandle> DrainAsBlobDataHandle(
BlobSizePolicy policy) override {
RefPtr<BlobDataHandle> handle =
blob_bytes_consumer_->DrainAsBlobDataHandle(policy);
if (handle)
form_data_ = nullptr;
return handle;
}
PassRefPtr<EncodedFormData> DrainAsFormData() override {
if (!form_data_)
return nullptr;
blob_bytes_consumer_->Cancel();
return std::move(form_data_);
}
void SetClient(BytesConsumer::Client* client) override {
blob_bytes_consumer_->SetClient(client);
}
void ClearClient() override { blob_bytes_consumer_->ClearClient(); }
void Cancel() override {
form_data_ = nullptr;
blob_bytes_consumer_->Cancel();
}
PublicState GetPublicState() const override {
return blob_bytes_consumer_->GetPublicState();
}
Error GetError() const override { return blob_bytes_consumer_->GetError(); }
String DebugName() const override { return "ComplexFormDataBytesConsumer"; }
DEFINE_INLINE_TRACE() {
visitor->Trace(blob_bytes_consumer_);
BytesConsumer::Trace(visitor);
}
private:
RefPtr<EncodedFormData> form_data_;
Member<BytesConsumer> blob_bytes_consumer_;
};
} // namespace
FormDataBytesConsumer::FormDataBytesConsumer(const String& string)
: impl_(new SimpleFormDataBytesConsumer(EncodedFormData::Create(
UTF8Encoding().Encode(string, WTF::kEntitiesForUnencodables)))) {}
FormDataBytesConsumer::FormDataBytesConsumer(DOMArrayBuffer* buffer)
: FormDataBytesConsumer(buffer->Data(), buffer->ByteLength()) {}
FormDataBytesConsumer::FormDataBytesConsumer(DOMArrayBufferView* view)
: FormDataBytesConsumer(view->BaseAddress(), view->byteLength()) {}
FormDataBytesConsumer::FormDataBytesConsumer(const void* data, size_t size)
: impl_(new SimpleFormDataBytesConsumer(
EncodedFormData::Create(data, size))) {}
FormDataBytesConsumer::FormDataBytesConsumer(
ExecutionContext* execution_context,
PassRefPtr<EncodedFormData> form_data)
: FormDataBytesConsumer(execution_context, std::move(form_data), nullptr) {}
FormDataBytesConsumer::FormDataBytesConsumer(
ExecutionContext* execution_context,
PassRefPtr<EncodedFormData> form_data,
BytesConsumer* consumer)
: impl_(IsSimple(form_data.Get())
? static_cast<BytesConsumer*>(
new SimpleFormDataBytesConsumer(std::move(form_data)))
: static_cast<BytesConsumer*>(
new ComplexFormDataBytesConsumer(execution_context,
std::move(form_data),
consumer))) {}
} // namespace blink