Serenity Operating System
at master 133 lines 6.0 kB view raw
1/* 2 * Copyright (c) 2022-2023, Linus Groh <linusg@serenityos.org> 3 * Copyright (c) 2022, Kenneth Myhra <kennethmyhra@serenityos.org> 4 * 5 * SPDX-License-Identifier: BSD-2-Clause 6 */ 7 8#include <LibJS/Runtime/Completion.h> 9#include <LibWeb/Fetch/BodyInit.h> 10#include <LibWeb/Fetch/Infrastructure/HTTP/Bodies.h> 11#include <LibWeb/URL/URLSearchParams.h> 12#include <LibWeb/WebIDL/AbstractOperations.h> 13#include <LibWeb/WebIDL/ExceptionOr.h> 14 15namespace Web::Fetch { 16 17// https://fetch.spec.whatwg.org/#bodyinit-safely-extract 18WebIDL::ExceptionOr<Infrastructure::BodyWithType> safely_extract_body(JS::Realm& realm, BodyInitOrReadableBytes const& object) 19{ 20 // 1. If object is a ReadableStream object, then: 21 if (auto const* stream = object.get_pointer<JS::Handle<Streams::ReadableStream>>()) { 22 // 1. Assert: object is neither disturbed nor locked. 23 VERIFY(!((*stream)->is_disturbed() || (*stream)->is_locked())); 24 } 25 26 // 2. Return the result of extracting object. 27 return extract_body(realm, object); 28} 29 30// https://fetch.spec.whatwg.org/#concept-bodyinit-extract 31WebIDL::ExceptionOr<Infrastructure::BodyWithType> extract_body(JS::Realm& realm, BodyInitOrReadableBytes const& object, bool keepalive) 32{ 33 auto& vm = realm.vm(); 34 35 // 1. Let stream be null. 36 JS::GCPtr<Streams::ReadableStream> stream; 37 38 // 2. If object is a ReadableStream object, then set stream to object. 39 if (auto const* stream_handle = object.get_pointer<JS::Handle<Streams::ReadableStream>>()) { 40 stream = const_cast<Streams::ReadableStream*>(stream_handle->cell()); 41 } 42 // 3. Otherwise, if object is a Blob object, set stream to the result of running object’s get stream. 43 else if (auto const* blob_handle = object.get_pointer<JS::Handle<FileAPI::Blob>>()) { 44 // FIXME: "set stream to the result of running object’s get stream" 45 (void)blob_handle; 46 stream = MUST_OR_THROW_OOM(realm.heap().allocate<Streams::ReadableStream>(realm, realm)); 47 } 48 // 4. Otherwise, set stream to a new ReadableStream object, and set up stream. 49 else { 50 // FIXME: "set up stream" 51 stream = MUST_OR_THROW_OOM(realm.heap().allocate<Streams::ReadableStream>(realm, realm)); 52 } 53 54 // 5. Assert: stream is a ReadableStream object. 55 VERIFY(stream); 56 57 // FIXME: 6. Let action be null. 58 59 // 7. Let source be null. 60 Infrastructure::Body::SourceType source {}; 61 62 // 8. Let length be null. 63 Optional<u64> length {}; 64 65 // 9. Let type be null. 66 Optional<ByteBuffer> type {}; 67 68 // 10. Switch on object. 69 // FIXME: Still need to support BufferSource and FormData 70 TRY(object.visit( 71 [&](JS::Handle<FileAPI::Blob> const& blob) -> WebIDL::ExceptionOr<void> { 72 // Set source to object. 73 source = blob; 74 // Set length to object’s size. 75 length = blob->size(); 76 // If object’s type attribute is not the empty byte sequence, set type to its value. 77 if (!blob->type().is_empty()) 78 type = TRY_OR_THROW_OOM(vm, ByteBuffer::copy(blob->type().bytes())); 79 return {}; 80 }, 81 [&](ReadonlyBytes bytes) -> WebIDL::ExceptionOr<void> { 82 // Set source to object. 83 source = TRY_OR_THROW_OOM(vm, ByteBuffer::copy(bytes)); 84 return {}; 85 }, 86 [&](JS::Handle<JS::Object> const& buffer_source) -> WebIDL::ExceptionOr<void> { 87 // Set source to a copy of the bytes held by object. 88 source = TRY_OR_THROW_OOM(vm, WebIDL::get_buffer_source_copy(*buffer_source.cell())); 89 return {}; 90 }, 91 [&](JS::Handle<URL::URLSearchParams> const& url_search_params) -> WebIDL::ExceptionOr<void> { 92 // Set source to the result of running the application/x-www-form-urlencoded serializer with object’s list. 93 auto search_params_bytes = TRY(url_search_params->to_string()).bytes(); 94 source = TRY_OR_THROW_OOM(vm, ByteBuffer::copy(search_params_bytes)); 95 // Set type to `application/x-www-form-urlencoded;charset=UTF-8`. 96 type = TRY_OR_THROW_OOM(vm, ByteBuffer::copy("application/x-www-form-urlencoded;charset=UTF-8"sv.bytes())); 97 return {}; 98 }, 99 [&](String const& scalar_value_string) -> WebIDL::ExceptionOr<void> { 100 // NOTE: AK::String is always UTF-8. 101 // Set source to the UTF-8 encoding of object. 102 source = TRY_OR_THROW_OOM(vm, ByteBuffer::copy(scalar_value_string.bytes())); 103 // Set type to `text/plain;charset=UTF-8`. 104 type = TRY_OR_THROW_OOM(vm, ByteBuffer::copy("text/plain;charset=UTF-8"sv.bytes())); 105 return {}; 106 }, 107 [&](JS::Handle<Streams::ReadableStream> const& stream) -> WebIDL::ExceptionOr<void> { 108 // If keepalive is true, then throw a TypeError. 109 if (keepalive) 110 return WebIDL::SimpleException { WebIDL::SimpleExceptionType::TypeError, "Cannot extract body from stream when keepalive is set"sv }; 111 112 // If object is disturbed or locked, then throw a TypeError. 113 if (stream->is_disturbed() || stream->is_locked()) 114 return WebIDL::SimpleException { WebIDL::SimpleExceptionType::TypeError, "Cannot extract body from disturbed or locked stream"sv }; 115 116 return {}; 117 })); 118 119 // FIXME: 11. If source is a byte sequence, then set action to a step that returns source and length to source’s length. 120 // For now, do it synchronously. 121 if (source.has<ByteBuffer>()) 122 length = source.get<ByteBuffer>().size(); 123 124 // FIXME: 12. If action is non-null, then run these steps in parallel: 125 126 // 13. Let body be a body whose stream is stream, source is source, and length is length. 127 auto body = Infrastructure::Body { JS::make_handle(*stream), move(source), move(length) }; 128 129 // 14. Return (body, type). 130 return Infrastructure::BodyWithType { .body = move(body), .type = move(type) }; 131} 132 133}