blob: 2b2883d82439e60f1b9f1e02e7323753a1dc8b5c [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 "third_party/blink/renderer/core/fetch/form_data_bytes_consumer.h"
#include "third_party/blink/public/platform/platform.h"
#include "third_party/blink/public/platform/task_type.h"
#include "third_party/blink/renderer/core/execution_context/execution_context.h"
#include "third_party/blink/renderer/core/fetch/blob_bytes_consumer.h"
#include "third_party/blink/renderer/core/typed_arrays/dom_array_buffer.h"
#include "third_party/blink/renderer/core/typed_arrays/dom_array_buffer_view.h"
#include "third_party/blink/renderer/platform/blob/blob_data.h"
#include "third_party/blink/renderer/platform/file_metadata.h"
#include "third_party/blink/renderer/platform/loader/fetch/data_pipe_bytes_consumer.h"
#include "third_party/blink/renderer/platform/network/encoded_form_data.h"
#include "third_party/blink/renderer/platform/network/form_data_encoder.h"
#include "third_party/blink/renderer/platform/wtf/functional.h"
#include "third_party/blink/renderer/platform/wtf/text/text_codec.h"
#include "third_party/blink/renderer/platform/wtf/text/text_encoding.h"
#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
#include "third_party/blink/renderer/platform/wtf/vector.h"
namespace blink {
namespace {
enum class FormDataType {
// Has only data elements.
kSimple,
// Can have data, file, and blob elements (no data pipes).
kComplex,
// Can have data and data pipe elements (no files and blobs).
kDataPipeAndDataOnly
};
FormDataType GetType(const EncodedFormData* form_data) {
FormDataType type = FormDataType::kSimple;
for (const auto& element : form_data->Elements()) {
switch (element.type_) {
case FormDataElement::kData:
break;
case FormDataElement::kEncodedFile:
case FormDataElement::kEncodedBlob:
DCHECK_NE(type, FormDataType::kDataPipeAndDataOnly);
type = FormDataType::kComplex;
break;
case FormDataElement::kDataPipe:
DCHECK_NE(type, FormDataType::kComplex);
type = FormDataType::kDataPipeAndDataOnly;
break;
}
}
return type;
}
class SimpleFormDataBytesConsumer : public BytesConsumer {
public:
explicit SimpleFormDataBytesConsumer(scoped_refptr<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;
}
scoped_refptr<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);
}
scoped_refptr<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 |form_data_| and |flatten_form_data_| is usable at a time.
scoped_refptr<EncodedFormData> form_data_;
Vector<char> flatten_form_data_;
size_t flatten_form_data_offset_ = 0;
PublicState state_ = PublicState::kReadableOrWaiting;
};
class DataPipeAndDataBytesConsumer final : public BytesConsumer {
public:
DataPipeAndDataBytesConsumer(ExecutionContext* execution_context,
EncodedFormData* form_data)
: execution_context_(execution_context) {
// Make a copy in case |form_data| will mutate while we read it. Copy()
// works fine; we don't need to DeepCopy() the data and data pipe getter:
// data is just a Vector<char> and data pipe getter can be shared.
form_data_ = form_data->Copy();
form_data_->SetBoundary(FormDataEncoder::GenerateUniqueBoundaryString());
iter_ = form_data_->MutableElements().begin();
}
Result BeginRead(const char** buffer, size_t* available) override {
*buffer = nullptr;
*available = 0;
if (state_ == PublicState::kClosed)
return Result::kDone;
if (state_ == PublicState::kErrored)
return Result::kError;
if (iter_ == form_data_->MutableElements().end()) {
Close();
return Result::kDone;
}
// Currently reading bytes.
if (iter_->type_ == FormDataElement::kData) {
// Create the bytes consumer if there isn't one yet.
if (!simple_consumer_) {
scoped_refptr<EncodedFormData> simple_data =
EncodedFormData::Create(iter_->data_);
simple_consumer_ = MakeGarbageCollected<SimpleFormDataBytesConsumer>(
std::move(simple_data));
if (client_)
simple_consumer_->SetClient(client_);
}
// Read from the bytes consumer.
Result result = simple_consumer_->BeginRead(buffer, available);
if (result == Result::kError) {
SetError();
return Result::kError;
}
// If done, continue to the next element.
if (result == Result::kDone) {
simple_consumer_ = nullptr;
++iter_;
return BeginRead(buffer, available);
}
return result;
}
// Currently reading a data pipe.
if (iter_->type_ == FormDataElement::kDataPipe) {
// Create the data pipe consumer if there isn't one yet.
if (!data_pipe_consumer_) {
network::mojom::blink::DataPipeGetterPtr* data_pipe_getter =
iter_->data_pipe_getter_->GetPtr();
mojo::ScopedDataPipeProducerHandle pipe_producer_handle;
mojo::ScopedDataPipeConsumerHandle pipe_consumer_handle;
MojoResult rv = mojo::CreateDataPipe(nullptr, &pipe_producer_handle,
&pipe_consumer_handle);
if (rv != MOJO_RESULT_OK) {
return Result::kError;
}
(*data_pipe_getter)
->Read(
std::move(pipe_producer_handle),
WTF::Bind(&DataPipeAndDataBytesConsumer::DataPipeGetterCallback,
WrapWeakPersistent(this)));
DataPipeBytesConsumer::CompletionNotifier* completion_notifier =
nullptr;
data_pipe_consumer_ = MakeGarbageCollected<DataPipeBytesConsumer>(
execution_context_->GetTaskRunner(TaskType::kNetworking),
std::move(pipe_consumer_handle), &completion_notifier);
completion_notifier_ = completion_notifier;
if (client_)
data_pipe_consumer_->SetClient(client_);
}
// Read from the data pipe consumer.
Result result = data_pipe_consumer_->BeginRead(buffer, available);
if (result == Result::kError) {
SetError();
return Result::kError;
}
if (result == Result::kDone) {
// We're done. Move on to the next element.
data_pipe_consumer_ = nullptr;
completion_notifier_ = nullptr;
++iter_;
return BeginRead(buffer, available);
}
return result;
}
NOTREACHED() << "Invalid type: " << iter_->type_;
return Result::kError;
}
Result EndRead(size_t read_size) override {
if (state_ == PublicState::kClosed)
return Result::kDone;
if (state_ == PublicState::kErrored)
return Result::kError;
if (simple_consumer_) {
Result result = simple_consumer_->EndRead(read_size);
if (result == Result::kError) {
SetError();
return Result::kError;
}
// Even if this consumer is done, there may still be more elements, so
// return Ok.
DCHECK(result == Result::kOk || result == Result::kDone);
return Result::kOk;
}
if (data_pipe_consumer_) {
Result result = data_pipe_consumer_->EndRead(read_size);
if (result == Result::kError) {
SetError();
return Result::kError;
}
// Even if this consumer is done, there may still be more elements, so
// return Ok.
DCHECK(result == Result::kOk || result == Result::kDone);
return Result::kOk;
}
NOTREACHED() << "No consumer. BeginRead() was not called?";
return Result::kError;
}
scoped_refptr<EncodedFormData> DrainAsFormData() override {
if (state_ == PublicState::kClosed || state_ == PublicState::kErrored)
return nullptr;
// According to the DrainAsFormData() contract, we can only return bytes
// that haven't already been read. So if reading has already started,
// give up and return null.
if (simple_consumer_ || data_pipe_consumer_)
return nullptr;
Close();
return std::move(form_data_);
}
void SetClient(Client* client) override {
DCHECK(!client_);
DCHECK(client);
client_ = client;
if (simple_consumer_)
simple_consumer_->SetClient(client_);
else if (data_pipe_consumer_)
data_pipe_consumer_->SetClient(client_);
}
void ClearClient() override {
client_ = nullptr;
if (simple_consumer_)
simple_consumer_->ClearClient();
else if (data_pipe_consumer_)
data_pipe_consumer_->ClearClient();
}
void Cancel() override {
if (state_ == PublicState::kClosed || state_ == PublicState::kErrored)
return;
if (simple_consumer_)
simple_consumer_->Cancel();
else if (data_pipe_consumer_)
data_pipe_consumer_->Cancel();
Close();
}
PublicState GetPublicState() const override { return state_; }
Error GetError() const override {
DCHECK_EQ(state_, PublicState::kErrored);
return error_;
}
String DebugName() const override { return "DataPipeAndDataBytesConsumer"; }
void Trace(blink::Visitor* visitor) override {
visitor->Trace(execution_context_);
visitor->Trace(client_);
visitor->Trace(simple_consumer_);
visitor->Trace(data_pipe_consumer_);
visitor->Trace(completion_notifier_);
BytesConsumer::Trace(visitor);
}
private:
void DataPipeGetterCallback(int32_t status, uint64_t size) {
switch (state_) {
case PublicState::kErrored:
// The error should have already been propagated to the notifier.
DCHECK(!completion_notifier_);
DCHECK(!data_pipe_consumer_);
return;
case PublicState::kClosed:
// The data_pipe_consumer_ should already be cleaned up.
DCHECK(!completion_notifier_);
DCHECK(!data_pipe_consumer_);
return;
case PublicState::kReadableOrWaiting:
break;
}
DCHECK(completion_notifier_);
if (status != 0) {
// 0 is net::OK.
completion_notifier_->SignalError(Error("error"));
} else {
completion_notifier_->SignalComplete();
}
}
void Close() {
if (state_ == PublicState::kClosed)
return;
DCHECK_EQ(state_, PublicState::kReadableOrWaiting);
state_ = PublicState::kClosed;
ClearClient();
simple_consumer_ = nullptr;
if (data_pipe_consumer_) {
data_pipe_consumer_->Cancel();
data_pipe_consumer_ = nullptr;
completion_notifier_ = nullptr;
}
}
void SetError() {
if (state_ == PublicState::kErrored)
return;
DCHECK_EQ(state_, PublicState::kReadableOrWaiting);
state_ = PublicState::kErrored;
error_ = Error("error");
ClearClient();
simple_consumer_ = nullptr;
if (completion_notifier_) {
completion_notifier_->SignalError(error_);
completion_notifier_ = nullptr;
data_pipe_consumer_ = nullptr;
}
}
Member<ExecutionContext> execution_context_;
PublicState state_ = PublicState::kReadableOrWaiting;
scoped_refptr<EncodedFormData> form_data_;
Vector<FormDataElement>::iterator iter_;
Error error_;
Member<BytesConsumer::Client> client_;
Member<SimpleFormDataBytesConsumer> simple_consumer_;
Member<DataPipeBytesConsumer> data_pipe_consumer_;
Member<DataPipeBytesConsumer::CompletionNotifier> completion_notifier_;
};
class ComplexFormDataBytesConsumer final : public BytesConsumer {
public:
ComplexFormDataBytesConsumer(ExecutionContext* execution_context,
scoped_refptr<EncodedFormData> form_data,
BytesConsumer* consumer_for_testing)
: form_data_(std::move(form_data)) {
if (consumer_for_testing) {
blob_bytes_consumer_ = consumer_for_testing;
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::kDataPipe:
NOTREACHED() << "This consumer can't handle data pipes.";
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_ = MakeGarbageCollected<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);
}
scoped_refptr<BlobDataHandle> DrainAsBlobDataHandle(
BlobSizePolicy policy) override {
scoped_refptr<BlobDataHandle> handle =
blob_bytes_consumer_->DrainAsBlobDataHandle(policy);
if (handle)
form_data_ = nullptr;
return handle;
}
scoped_refptr<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"; }
void Trace(blink::Visitor* visitor) override {
visitor->Trace(blob_bytes_consumer_);
BytesConsumer::Trace(visitor);
}
private:
scoped_refptr<EncodedFormData> form_data_;
TraceWrapperMember<BytesConsumer> blob_bytes_consumer_;
};
} // namespace
FormDataBytesConsumer::FormDataBytesConsumer(const String& string)
: impl_(MakeGarbageCollected<SimpleFormDataBytesConsumer>(
EncodedFormData::Create(
UTF8Encoding().Encode(string, WTF::kNoUnencodables)))) {}
FormDataBytesConsumer::FormDataBytesConsumer(DOMArrayBuffer* buffer)
: FormDataBytesConsumer(buffer->Data(), buffer->ByteLength()) {}
FormDataBytesConsumer::FormDataBytesConsumer(DOMArrayBufferView* view)
: FormDataBytesConsumer(view->BaseAddress(), view->byteLength()) {}
FormDataBytesConsumer::FormDataBytesConsumer(const void* data, wtf_size_t size)
: impl_(MakeGarbageCollected<SimpleFormDataBytesConsumer>(
EncodedFormData::Create(data, size))) {}
FormDataBytesConsumer::FormDataBytesConsumer(
ExecutionContext* execution_context,
scoped_refptr<EncodedFormData> form_data)
: FormDataBytesConsumer(execution_context, std::move(form_data), nullptr) {}
FormDataBytesConsumer::FormDataBytesConsumer(
ExecutionContext* execution_context,
scoped_refptr<EncodedFormData> form_data,
BytesConsumer* consumer_for_testing)
: impl_(GetImpl(execution_context,
std::move(form_data),
consumer_for_testing)) {}
// static
BytesConsumer* FormDataBytesConsumer::GetImpl(
ExecutionContext* execution_context,
scoped_refptr<EncodedFormData> form_data,
BytesConsumer* consumer_for_testing) {
DCHECK(form_data);
switch (GetType(form_data.get())) {
case FormDataType::kSimple:
return MakeGarbageCollected<SimpleFormDataBytesConsumer>(
std::move(form_data));
case FormDataType::kComplex:
return MakeGarbageCollected<ComplexFormDataBytesConsumer>(
execution_context, std::move(form_data), consumer_for_testing);
case FormDataType::kDataPipeAndDataOnly:
return MakeGarbageCollected<DataPipeAndDataBytesConsumer>(
execution_context, form_data.get());
}
return nullptr;
}
} // namespace blink