2022-09-22 02:04:06 +03:00
/*
2023-03-03 01:26:12 +03:00
* Copyright ( c ) 2022 - 2023 , Linus Groh < linusg @ serenityos . org >
2024-05-14 23:06:03 +03:00
* Copyright ( c ) 2022 - 2024 , Kenneth Myhra < kennethmyhra @ serenityos . org >
2022-09-22 02:04:06 +03:00
*
* SPDX - License - Identifier : BSD - 2 - Clause
*/
2023-01-07 20:14:54 +03:00
# include <LibJS/Runtime/Completion.h>
2024-05-14 23:06:03 +03:00
# include <LibJS/Runtime/TypedArray.h>
2024-02-11 09:48:56 +03:00
# include <LibWeb/DOMURL/URLSearchParams.h>
2022-09-22 02:04:06 +03:00
# include <LibWeb/Fetch/BodyInit.h>
# include <LibWeb/Fetch/Infrastructure/HTTP/Bodies.h>
2024-01-25 22:40:55 +03:00
# include <LibWeb/FileAPI/Blob.h>
2023-04-02 23:30:56 +03:00
# include <LibWeb/HTML/FormControlInfrastructure.h>
2024-01-25 22:40:55 +03:00
# include <LibWeb/HTML/Scripting/TemporaryExecutionContext.h>
2024-05-14 23:06:03 +03:00
# include <LibWeb/Platform/EventLoopPlugin.h>
2024-01-25 22:40:55 +03:00
# include <LibWeb/Streams/AbstractOperations.h>
2022-09-24 18:14:37 +03:00
# include <LibWeb/WebIDL/AbstractOperations.h>
2023-11-23 10:07:25 +03:00
# include <LibWeb/WebIDL/Buffers.h>
2022-09-25 19:03:42 +03:00
# include <LibWeb/WebIDL/ExceptionOr.h>
2023-04-02 23:30:56 +03:00
# include <LibWeb/XHR/FormData.h>
2022-09-22 02:04:06 +03:00
namespace Web : : Fetch {
2022-10-13 20:11:42 +03:00
// https://fetch.spec.whatwg.org/#bodyinit-safely-extract
2022-11-10 01:37:07 +03:00
WebIDL : : ExceptionOr < Infrastructure : : BodyWithType > safely_extract_body ( JS : : Realm & realm , BodyInitOrReadableBytes const & object )
2022-10-13 20:11:42 +03:00
{
// 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 ) ;
}
2022-09-22 02:04:06 +03:00
// https://fetch.spec.whatwg.org/#concept-bodyinit-extract
2022-11-10 01:37:07 +03:00
WebIDL : : ExceptionOr < Infrastructure : : BodyWithType > extract_body ( JS : : Realm & realm , BodyInitOrReadableBytes const & object , bool keepalive )
2022-09-22 02:04:06 +03:00
{
2024-01-25 22:40:55 +03:00
HTML : : TemporaryExecutionContext execution_context { Bindings : : host_defined_environment_settings_object ( realm ) , HTML : : TemporaryExecutionContext : : CallbacksEnabled : : Yes } ;
2023-01-07 20:14:54 +03:00
auto & vm = realm . vm ( ) ;
2022-10-30 17:37:38 +03:00
// 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 > > ( ) ) {
2024-04-30 01:01:44 +03:00
stream = blob_handle - > cell ( ) - > get_stream ( ) ;
2022-09-22 19:30:54 +03:00
}
2024-01-25 22:40:55 +03:00
// 4. Otherwise, set stream to a new ReadableStream object, and set up stream with byte reading support.
2022-10-30 17:37:38 +03:00
else {
2023-08-13 14:05:26 +03:00
stream = realm . heap ( ) . allocate < Streams : : ReadableStream > ( realm , realm ) ;
2024-04-30 01:01:44 +03:00
Streams : : set_up_readable_stream_controller_with_byte_reading_support ( * stream ) ;
2022-10-30 17:37:38 +03:00
}
// 5. Assert: stream is a ReadableStream object.
VERIFY ( stream ) ;
2022-09-22 19:30:54 +03:00
2024-05-14 23:06:03 +03:00
// 6. Let action be null.
Function < ByteBuffer ( ) > action ;
2022-10-30 17:37:38 +03:00
// 7. Let source be null.
2022-09-22 02:04:06 +03:00
Infrastructure : : Body : : SourceType source { } ;
2022-10-30 17:37:38 +03:00
// 8. Let length be null.
2022-09-22 02:04:06 +03:00
Optional < u64 > length { } ;
2022-10-30 17:37:38 +03:00
// 9. Let type be null.
2022-09-22 02:04:06 +03:00
Optional < ByteBuffer > type { } ;
2022-10-30 17:37:38 +03:00
// 10. Switch on object.
2022-09-22 19:30:54 +03:00
TRY ( object . visit (
2022-09-25 19:03:42 +03:00
[ & ] ( JS : : Handle < FileAPI : : Blob > const & blob ) - > WebIDL : : ExceptionOr < void > {
2022-09-22 02:04:06 +03:00
// 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 ( ) )
2024-04-27 17:18:45 +03:00
type = MUST ( ByteBuffer : : copy ( blob - > type ( ) . bytes ( ) ) ) ;
2022-09-22 02:04:06 +03:00
return { } ;
} ,
2022-09-25 21:15:35 +03:00
[ & ] ( ReadonlyBytes bytes ) - > WebIDL : : ExceptionOr < void > {
// Set source to object.
2024-04-27 17:18:45 +03:00
source = MUST ( ByteBuffer : : copy ( bytes ) ) ;
2022-09-25 21:15:35 +03:00
return { } ;
} ,
2023-11-23 10:07:25 +03:00
[ & ] ( JS : : Handle < WebIDL : : BufferSource > const & buffer_source ) - > WebIDL : : ExceptionOr < void > {
2022-09-22 02:04:06 +03:00
// Set source to a copy of the bytes held by object.
2024-04-27 17:18:45 +03:00
source = MUST ( WebIDL : : get_buffer_source_copy ( * buffer_source - > raw_object ( ) ) ) ;
2022-09-22 02:04:06 +03:00
return { } ;
} ,
2023-04-02 23:30:56 +03:00
[ & ] ( JS : : Handle < XHR : : FormData > const & form_data ) - > WebIDL : : ExceptionOr < void > {
// Set action to this step: run the multipart/form-data encoding algorithm, with object’ s entry list and UTF-8.
2024-04-27 17:18:45 +03:00
auto serialized_form_data = MUST ( HTML : : serialize_to_multipart_form_data ( form_data - > entry_list ( ) ) ) ;
2023-04-02 23:30:56 +03:00
// Set source to object.
source = serialized_form_data . serialized_data ;
// FIXME: Set length to unclear, see html/6424 for improving this.
// Set type to `multipart/form-data; boundary=`, followed by the multipart/form-data boundary string generated by the multipart/form-data encoding algorithm.
2024-04-27 17:18:45 +03:00
type = MUST ( ByteBuffer : : copy ( MUST ( String : : formatted ( " multipart/form-data; boundary={} " sv , serialized_form_data . boundary ) ) . bytes ( ) ) ) ;
2023-04-02 23:30:56 +03:00
return { } ;
} ,
2024-02-11 09:48:56 +03:00
[ & ] ( JS : : Handle < DOMURL : : URLSearchParams > const & url_search_params ) - > WebIDL : : ExceptionOr < void > {
2022-09-22 02:04:06 +03:00
// Set source to the result of running the application/x-www-form-urlencoded serializer with object’ s list.
2024-04-03 22:54:22 +03:00
auto search_params_string = TRY ( url_search_params - > to_string ( ) ) ;
2024-04-27 17:18:45 +03:00
source = MUST ( ByteBuffer : : copy ( search_params_string . bytes ( ) ) ) ;
2022-09-22 02:04:06 +03:00
// Set type to `application/x-www-form-urlencoded;charset=UTF-8`.
2024-04-27 17:18:45 +03:00
type = MUST ( ByteBuffer : : copy ( " application/x-www-form-urlencoded;charset=UTF-8 " sv . bytes ( ) ) ) ;
2022-09-22 02:04:06 +03:00
return { } ;
} ,
2023-03-03 01:26:12 +03:00
[ & ] ( String const & scalar_value_string ) - > WebIDL : : ExceptionOr < void > {
// NOTE: AK::String is always UTF-8.
2022-09-22 02:04:06 +03:00
// Set source to the UTF-8 encoding of object.
2024-04-27 17:18:45 +03:00
source = MUST ( ByteBuffer : : copy ( scalar_value_string . bytes ( ) ) ) ;
2022-09-22 02:04:06 +03:00
// Set type to `text/plain;charset=UTF-8`.
2024-04-27 17:18:45 +03:00
type = MUST ( ByteBuffer : : copy ( " text/plain;charset=UTF-8 " sv . bytes ( ) ) ) ;
2022-09-22 19:30:54 +03:00
return { } ;
} ,
2022-09-25 19:03:42 +03:00
[ & ] ( JS : : Handle < Streams : : ReadableStream > const & stream ) - > WebIDL : : ExceptionOr < void > {
2022-09-22 19:30:54 +03:00
// If keepalive is true, then throw a TypeError.
if ( keepalive )
2022-10-28 01:18:16 +03:00
return WebIDL : : SimpleException { WebIDL : : SimpleExceptionType : : TypeError , " Cannot extract body from stream when keepalive is set " sv } ;
2022-09-22 19:30:54 +03:00
// If object is disturbed or locked, then throw a TypeError.
if ( stream - > is_disturbed ( ) | | stream - > is_locked ( ) )
2022-10-28 01:18:16 +03:00
return WebIDL : : SimpleException { WebIDL : : SimpleExceptionType : : TypeError , " Cannot extract body from disturbed or locked stream " sv } ;
2022-09-22 19:30:54 +03:00
2022-09-22 02:04:06 +03:00
return { } ;
} ) ) ;
2024-05-14 23:06:03 +03:00
// 11. If source is a byte sequence, then set action to a step that returns source and length to source’ s length.
2023-02-28 21:50:42 +03:00
// For now, do it synchronously.
2024-05-14 23:06:03 +03:00
if ( source . has < ByteBuffer > ( ) ) {
action = [ source = MUST ( ByteBuffer : : copy ( source . get < ByteBuffer > ( ) ) ) ] ( ) mutable {
return move ( source ) ;
} ;
2023-02-28 21:50:42 +03:00
length = source . get < ByteBuffer > ( ) . size ( ) ;
2024-05-14 23:06:03 +03:00
}
// 12. If action is non-null, then run these steps in parallel:
if ( action ) {
Platform : : EventLoopPlugin : : the ( ) . deferred_invoke ( [ & realm , stream , action = move ( action ) ] {
HTML : : TemporaryExecutionContext execution_context { Bindings : : host_defined_environment_settings_object ( realm ) , HTML : : TemporaryExecutionContext : : CallbacksEnabled : : Yes } ;
// 1. Run action.
auto bytes = action ( ) ;
2023-02-28 21:50:42 +03:00
2024-05-14 23:06:03 +03:00
// Whenever one or more bytes are available and stream is not errored, enqueue the result of creating a
// Uint8Array from the available bytes into stream.
if ( ! bytes . is_empty ( ) & & ! stream - > is_errored ( ) ) {
auto array_buffer = JS : : ArrayBuffer : : create ( stream - > realm ( ) , move ( bytes ) ) ;
auto chunk = JS : : Uint8Array : : create ( stream - > realm ( ) , array_buffer - > byte_length ( ) , * array_buffer ) ;
Streams : : readable_stream_enqueue ( * stream - > controller ( ) , chunk ) . release_value_but_fixme_should_propagate_errors ( ) ;
}
// When running action is done, close stream.
stream - > close ( ) ;
} ) ;
}
2022-10-30 17:37:38 +03:00
// 13. Let body be a body whose stream is stream, source is source, and length is length.
2023-08-18 20:38:13 +03:00
auto body = Infrastructure : : Body : : create ( vm , * stream , move ( source ) , move ( length ) ) ;
2022-09-22 02:04:06 +03:00
2022-10-30 17:37:38 +03:00
// 14. Return (body, type).
2022-09-22 02:04:06 +03:00
return Infrastructure : : BodyWithType { . body = move ( body ) , . type = move ( type ) } ;
}
}