
Since we don't currently have streams, we didn't set length anywhere. However, this is required by XHR's reliance on Fetch to get the total number of bytes for the progress events.
133 lines
5.9 KiB
C++
133 lines
5.9 KiB
C++
/*
|
||
* Copyright (c) 2022-2023, Linus Groh <linusg@serenityos.org>
|
||
* Copyright (c) 2022, Kenneth Myhra <kennethmyhra@serenityos.org>
|
||
*
|
||
* SPDX-License-Identifier: BSD-2-Clause
|
||
*/
|
||
|
||
#include <LibJS/Runtime/Completion.h>
|
||
#include <LibWeb/Fetch/BodyInit.h>
|
||
#include <LibWeb/Fetch/Infrastructure/HTTP/Bodies.h>
|
||
#include <LibWeb/URL/URLSearchParams.h>
|
||
#include <LibWeb/WebIDL/AbstractOperations.h>
|
||
#include <LibWeb/WebIDL/ExceptionOr.h>
|
||
|
||
namespace Web::Fetch {
|
||
|
||
// https://fetch.spec.whatwg.org/#bodyinit-safely-extract
|
||
WebIDL::ExceptionOr<Infrastructure::BodyWithType> safely_extract_body(JS::Realm& realm, BodyInitOrReadableBytes const& object)
|
||
{
|
||
// 1. If object is a ReadableStream object, then:
|
||
if (auto const* stream = object.get_pointer<JS::Handle<Streams::ReadableStream>>()) {
|
||
// 1. Assert: object is neither disturbed nor locked.
|
||
VERIFY(!((*stream)->is_disturbed() || (*stream)->is_locked()));
|
||
}
|
||
|
||
// 2. Return the result of extracting object.
|
||
return extract_body(realm, object);
|
||
}
|
||
|
||
// https://fetch.spec.whatwg.org/#concept-bodyinit-extract
|
||
WebIDL::ExceptionOr<Infrastructure::BodyWithType> extract_body(JS::Realm& realm, BodyInitOrReadableBytes const& object, bool keepalive)
|
||
{
|
||
auto& vm = realm.vm();
|
||
|
||
// 1. Let stream be null.
|
||
JS::GCPtr<Streams::ReadableStream> stream;
|
||
|
||
// 2. If object is a ReadableStream object, then set stream to object.
|
||
if (auto const* stream_handle = object.get_pointer<JS::Handle<Streams::ReadableStream>>()) {
|
||
stream = const_cast<Streams::ReadableStream*>(stream_handle->cell());
|
||
}
|
||
// 3. Otherwise, if object is a Blob object, set stream to the result of running object’s get stream.
|
||
else if (auto const* blob_handle = object.get_pointer<JS::Handle<FileAPI::Blob>>()) {
|
||
// FIXME: "set stream to the result of running object’s get stream"
|
||
(void)blob_handle;
|
||
stream = MUST_OR_THROW_OOM(realm.heap().allocate<Streams::ReadableStream>(realm, realm));
|
||
}
|
||
// 4. Otherwise, set stream to a new ReadableStream object, and set up stream.
|
||
else {
|
||
// FIXME: "set up stream"
|
||
stream = MUST_OR_THROW_OOM(realm.heap().allocate<Streams::ReadableStream>(realm, realm));
|
||
}
|
||
|
||
// 5. Assert: stream is a ReadableStream object.
|
||
VERIFY(stream);
|
||
|
||
// FIXME: 6. Let action be null.
|
||
|
||
// 7. Let source be null.
|
||
Infrastructure::Body::SourceType source {};
|
||
|
||
// 8. Let length be null.
|
||
Optional<u64> length {};
|
||
|
||
// 9. Let type be null.
|
||
Optional<ByteBuffer> type {};
|
||
|
||
// 10. Switch on object.
|
||
// FIXME: Still need to support BufferSource and FormData
|
||
TRY(object.visit(
|
||
[&](JS::Handle<FileAPI::Blob> const& blob) -> WebIDL::ExceptionOr<void> {
|
||
// Set source to object.
|
||
source = blob;
|
||
// Set length to object’s size.
|
||
length = blob->size();
|
||
// If object’s type attribute is not the empty byte sequence, set type to its value.
|
||
if (!blob->type().is_empty())
|
||
type = TRY_OR_THROW_OOM(vm, ByteBuffer::copy(blob->type().bytes()));
|
||
return {};
|
||
},
|
||
[&](ReadonlyBytes bytes) -> WebIDL::ExceptionOr<void> {
|
||
// Set source to object.
|
||
source = TRY_OR_THROW_OOM(vm, ByteBuffer::copy(bytes));
|
||
return {};
|
||
},
|
||
[&](JS::Handle<JS::Object> const& buffer_source) -> WebIDL::ExceptionOr<void> {
|
||
// Set source to a copy of the bytes held by object.
|
||
source = TRY_OR_THROW_OOM(vm, WebIDL::get_buffer_source_copy(*buffer_source.cell()));
|
||
return {};
|
||
},
|
||
[&](JS::Handle<URL::URLSearchParams> const& url_search_params) -> WebIDL::ExceptionOr<void> {
|
||
// Set source to the result of running the application/x-www-form-urlencoded serializer with object’s list.
|
||
auto search_params_bytes = TRY(url_search_params->to_string()).bytes();
|
||
source = TRY_OR_THROW_OOM(vm, ByteBuffer::copy(search_params_bytes));
|
||
// Set type to `application/x-www-form-urlencoded;charset=UTF-8`.
|
||
type = TRY_OR_THROW_OOM(vm, ByteBuffer::copy("application/x-www-form-urlencoded;charset=UTF-8"sv.bytes()));
|
||
return {};
|
||
},
|
||
[&](String const& scalar_value_string) -> WebIDL::ExceptionOr<void> {
|
||
// NOTE: AK::String is always UTF-8.
|
||
// Set source to the UTF-8 encoding of object.
|
||
source = TRY_OR_THROW_OOM(vm, ByteBuffer::copy(scalar_value_string.bytes()));
|
||
// Set type to `text/plain;charset=UTF-8`.
|
||
type = TRY_OR_THROW_OOM(vm, ByteBuffer::copy("text/plain;charset=UTF-8"sv.bytes()));
|
||
return {};
|
||
},
|
||
[&](JS::Handle<Streams::ReadableStream> const& stream) -> WebIDL::ExceptionOr<void> {
|
||
// If keepalive is true, then throw a TypeError.
|
||
if (keepalive)
|
||
return WebIDL::SimpleException { WebIDL::SimpleExceptionType::TypeError, "Cannot extract body from stream when keepalive is set"sv };
|
||
|
||
// If object is disturbed or locked, then throw a TypeError.
|
||
if (stream->is_disturbed() || stream->is_locked())
|
||
return WebIDL::SimpleException { WebIDL::SimpleExceptionType::TypeError, "Cannot extract body from disturbed or locked stream"sv };
|
||
|
||
return {};
|
||
}));
|
||
|
||
// FIXME: 11. If source is a byte sequence, then set action to a step that returns source and length to source’s length.
|
||
// For now, do it synchronously.
|
||
if (source.has<ByteBuffer>())
|
||
length = source.get<ByteBuffer>().size();
|
||
|
||
// FIXME: 12. If action is non-null, then run these steps in parallel:
|
||
|
||
// 13. Let body be a body whose stream is stream, source is source, and length is length.
|
||
auto body = Infrastructure::Body { JS::make_handle(*stream), move(source), move(length) };
|
||
|
||
// 14. Return (body, type).
|
||
return Infrastructure::BodyWithType { .body = move(body), .type = move(type) };
|
||
}
|
||
|
||
}
|