2022-09-25 21:36:30 +03:00
/*
2023-03-03 01:26:12 +03:00
* Copyright ( c ) 2022 - 2023 , Linus Groh < linusg @ serenityos . org >
2022-09-25 21:36:30 +03:00
*
* SPDX - License - Identifier : BSD - 2 - Clause
*/
2023-01-07 20:14:54 +03:00
# include <LibJS/Runtime/Completion.h>
2022-09-26 03:08:29 +03:00
# include <LibWeb/Bindings/Intrinsics.h>
2022-09-25 21:36:30 +03:00
# include <LibWeb/Bindings/MainThreadVM.h>
2024-04-27 03:09:58 +03:00
# include <LibWeb/Bindings/ResponsePrototype.h>
2024-02-11 09:48:56 +03:00
# include <LibWeb/DOMURL/DOMURL.h>
2022-09-25 21:36:30 +03:00
# include <LibWeb/Fetch/Enums.h>
# include <LibWeb/Fetch/Infrastructure/HTTP/Bodies.h>
# include <LibWeb/Fetch/Infrastructure/HTTP/Responses.h>
# include <LibWeb/Fetch/Infrastructure/HTTP/Statuses.h>
# include <LibWeb/Fetch/Response.h>
# include <LibWeb/HTML/Scripting/Environments.h>
# include <LibWeb/Infra/JSON.h>
namespace Web : : Fetch {
2023-11-19 21:47:52 +03:00
JS_DEFINE_ALLOCATOR ( Response ) ;
2022-10-30 04:52:07 +03:00
Response : : Response ( JS : : Realm & realm , JS : : NonnullGCPtr < Infrastructure : : Response > response )
2022-09-25 21:36:30 +03:00
: PlatformObject ( realm )
2022-10-30 04:52:07 +03:00
, m_response ( response )
2022-09-25 21:36:30 +03:00
{
}
Response : : ~ Response ( ) = default ;
2023-08-07 09:41:28 +03:00
void Response : : initialize ( JS : : Realm & realm )
2023-01-10 14:28:20 +03:00
{
2023-08-07 09:41:28 +03:00
Base : : initialize ( realm ) ;
2024-03-16 15:13:08 +03:00
WEB_SET_PROTOTYPE_FOR_INTERFACE ( Response ) ;
2023-01-10 14:28:20 +03:00
}
2022-09-25 21:36:30 +03:00
void Response : : visit_edges ( Cell : : Visitor & visitor )
{
Base : : visit_edges ( visitor ) ;
2022-10-30 04:52:07 +03:00
visitor . visit ( m_response ) ;
2022-09-25 21:36:30 +03:00
visitor . visit ( m_headers ) ;
}
// https://fetch.spec.whatwg.org/#concept-body-mime-type
// https://fetch.spec.whatwg.org/#ref-for-concept-header-extract-mime-type%E2%91%A7
2024-04-27 17:15:01 +03:00
Optional < MimeSniff : : MimeType > Response : : mime_type_impl ( ) const
2022-09-25 21:36:30 +03:00
{
// Objects including the Body interface mixin need to define an associated MIME type algorithm which takes no arguments and returns failure or a MIME type.
// A Response object’ s MIME type is to return the result of extracting a MIME type from its response’ s header list.
return m_response - > header_list ( ) - > extract_mime_type ( ) ;
}
// https://fetch.spec.whatwg.org/#concept-body-body
// https://fetch.spec.whatwg.org/#ref-for-concept-body-body%E2%91%A8
2023-08-18 20:38:13 +03:00
JS : : GCPtr < Infrastructure : : Body const > Response : : body_impl ( ) const
2022-09-25 21:36:30 +03:00
{
// Objects including the Body interface mixin have an associated body (null or a body).
// A Response object’ s body is its response’ s body.
2023-08-18 20:38:13 +03:00
return m_response - > body ( ) ? m_response - > body ( ) : nullptr ;
2022-09-25 21:36:30 +03:00
}
// https://fetch.spec.whatwg.org/#concept-body-body
// https://fetch.spec.whatwg.org/#ref-for-concept-body-body%E2%91%A8
2023-08-18 20:38:13 +03:00
JS : : GCPtr < Infrastructure : : Body > Response : : body_impl ( )
2022-09-25 21:36:30 +03:00
{
// Objects including the Body interface mixin have an associated body (null or a body).
// A Response object’ s body is its response’ s body.
2023-08-18 20:38:13 +03:00
return m_response - > body ( ) ? m_response - > body ( ) : nullptr ;
2022-09-25 21:36:30 +03:00
}
// https://fetch.spec.whatwg.org/#response-create
2023-08-13 14:05:26 +03:00
JS : : NonnullGCPtr < Response > Response : : create ( JS : : Realm & realm , JS : : NonnullGCPtr < Infrastructure : : Response > response , Headers : : Guard guard )
2022-09-25 21:36:30 +03:00
{
// 1. Let responseObject be a new Response object with realm.
// 2. Set responseObject’ s response to response.
2023-08-13 14:05:26 +03:00
auto response_object = realm . heap ( ) . allocate < Response > ( realm , realm , response ) ;
2022-09-25 21:36:30 +03:00
// 3. Set responseObject’ s headers to a new Headers object with realm, whose headers list is response’ s headers list and guard is guard.
2023-08-13 14:05:26 +03:00
response_object - > m_headers = realm . heap ( ) . allocate < Headers > ( realm , realm , response - > header_list ( ) ) ;
2022-09-25 21:36:30 +03:00
response_object - > m_headers - > set_guard ( guard ) ;
// 4. Return responseObject.
2022-12-14 20:40:33 +03:00
return response_object ;
2022-09-25 21:36:30 +03:00
}
// https://fetch.spec.whatwg.org/#initialize-a-response
WebIDL : : ExceptionOr < void > Response : : initialize_response ( ResponseInit const & init , Optional < Infrastructure : : BodyWithType > const & body )
{
// 1. If init["status"] is not in the range 200 to 599, inclusive, then throw a RangeError.
if ( init . status < 200 | | init . status > 599 )
return WebIDL : : SimpleException { WebIDL : : SimpleExceptionType : : RangeError , " Status must be in range 200-599 " sv } ;
// FIXME: 2. If init["statusText"] does not match the reason-phrase token production, then throw a TypeError.
// 3. Set response’ s response’ s status to init["status"].
m_response - > set_status ( init . status ) ;
// 4. Set response’ s response’ s status message to init["statusText"].
2024-04-27 17:34:37 +03:00
m_response - > set_status_message ( MUST ( ByteBuffer : : copy ( init . status_text . bytes ( ) ) ) ) ;
2022-09-25 21:36:30 +03:00
// 5. If init["headers"] exists, then fill response’ s headers with init["headers"].
if ( init . headers . has_value ( ) )
2022-10-30 20:50:04 +03:00
TRY ( m_headers - > fill ( * init . headers ) ) ;
2022-09-25 21:36:30 +03:00
// 6. If body was given, then:
if ( body . has_value ( ) ) {
// 1. If response’ s status is a null body status, then throw a TypeError.
if ( Infrastructure : : is_null_body_status ( m_response - > status ( ) ) )
return WebIDL : : SimpleException { WebIDL : : SimpleExceptionType : : TypeError , " Response with null body status cannot have a body " sv } ;
// 2. Set response’ s body to body’ s body.
m_response - > set_body ( body - > body ) ;
// 3. If body’ s type is non-null and response’ s header list does not contain `Content-Type`, then append (`Content-Type`, body’ s type) to response’ s header list.
2024-04-27 17:34:58 +03:00
if ( body - > type . has_value ( ) & & ! m_response - > header_list ( ) - > contains ( " Content-Type " sv . bytes ( ) ) ) {
2022-09-25 21:36:30 +03:00
auto header = Infrastructure : : Header {
2022-10-24 11:21:12 +03:00
. name = MUST ( ByteBuffer : : copy ( " Content-Type " sv . bytes ( ) ) ) ,
2024-04-27 17:34:37 +03:00
. value = MUST ( ByteBuffer : : copy ( body - > type - > span ( ) ) ) ,
2022-09-25 21:36:30 +03:00
} ;
2024-04-26 20:24:20 +03:00
m_response - > header_list ( ) - > append ( move ( header ) ) ;
2022-09-25 21:36:30 +03:00
}
}
return { } ;
}
// https://fetch.spec.whatwg.org/#dom-response
2022-09-26 03:08:29 +03:00
WebIDL : : ExceptionOr < JS : : NonnullGCPtr < Response > > Response : : construct_impl ( JS : : Realm & realm , Optional < BodyInit > const & body , ResponseInit const & init )
2022-09-25 21:36:30 +03:00
{
2022-10-30 04:52:07 +03:00
auto & vm = realm . vm ( ) ;
2022-09-25 21:36:30 +03:00
// Referred to as 'this' in the spec.
2023-08-13 14:05:26 +03:00
auto response_object = realm . heap ( ) . allocate < Response > ( realm , realm , Infrastructure : : Response : : create ( vm ) ) ;
2022-09-25 21:36:30 +03:00
// 1. Set this’ s response to a new response.
// NOTE: This is done at the beginning as the 'this' value Response object
// cannot exist with a null Infrastructure::Response.
// 2. Set this’ s headers to a new Headers object with this’ s relevant Realm, whose header list is this’ s response’ s header list and guard is "response".
2023-08-13 14:05:26 +03:00
response_object - > m_headers = realm . heap ( ) . allocate < Headers > ( realm , realm , response_object - > response ( ) - > header_list ( ) ) ;
2022-09-25 21:36:30 +03:00
response_object - > m_headers - > set_guard ( Headers : : Guard : : Response ) ;
// 3. Let bodyWithType be null.
Optional < Infrastructure : : BodyWithType > body_with_type ;
// 4. If body is non-null, then set bodyWithType to the result of extracting body.
if ( body . has_value ( ) )
body_with_type = TRY ( extract_body ( realm , * body ) ) ;
// 5. Perform initialize a response given this, init, and bodyWithType.
TRY ( response_object - > initialize_response ( init , body_with_type ) ) ;
return response_object ;
}
// https://fetch.spec.whatwg.org/#dom-response-error
2023-08-13 14:05:26 +03:00
JS : : NonnullGCPtr < Response > Response : : error ( JS : : VM & vm )
2022-09-25 21:36:30 +03:00
{
// The static error() method steps are to return the result of creating a Response object, given a new network error, "immutable", and this’ s relevant Realm.
// FIXME: How can we reliably get 'this', i.e. the object the function was called on, in IDL-defined functions?
2023-03-03 21:02:43 +03:00
return Response : : create ( * vm . current_realm ( ) , Infrastructure : : Response : : network_error ( vm , " Response created via `Response.error()` " sv ) , Headers : : Guard : : Immutable ) ;
2022-09-25 21:36:30 +03:00
}
// https://fetch.spec.whatwg.org/#dom-response-redirect
2023-03-03 01:26:12 +03:00
WebIDL : : ExceptionOr < JS : : NonnullGCPtr < Response > > Response : : redirect ( JS : : VM & vm , String const & url , u16 status )
2022-09-25 21:36:30 +03:00
{
2022-10-05 19:09:26 +03:00
auto & realm = * vm . current_realm ( ) ;
2022-09-25 21:36:30 +03:00
// 1. Let parsedURL be the result of parsing url with current settings object’ s API base URL.
auto api_base_url = HTML : : current_settings_object ( ) . api_base_url ( ) ;
2024-02-11 09:48:56 +03:00
auto parsed_url = DOMURL : : parse ( url , api_base_url ) ;
2022-09-25 21:36:30 +03:00
// 2. If parsedURL is failure, then throw a TypeError.
if ( ! parsed_url . is_valid ( ) )
return WebIDL : : SimpleException { WebIDL : : SimpleExceptionType : : TypeError , " Redirect URL is not valid " sv } ;
// 3. If status is not a redirect status, then throw a RangeError.
if ( ! Infrastructure : : is_redirect_status ( status ) )
return WebIDL : : SimpleException { WebIDL : : SimpleExceptionType : : RangeError , " Status must be one of 301, 302, 303, 307, or 308 " sv } ;
// 4. Let responseObject be the result of creating a Response object, given a new response, "immutable", and this’ s relevant Realm.
// FIXME: How can we reliably get 'this', i.e. the object the function was called on, in IDL-defined functions?
2023-08-13 14:05:26 +03:00
auto response_object = Response : : create ( realm , Infrastructure : : Response : : create ( vm ) , Headers : : Guard : : Immutable ) ;
2022-09-25 21:36:30 +03:00
// 5. Set responseObject’ s response’ s status to status.
2022-10-05 01:45:47 +03:00
response_object - > response ( ) - > set_status ( status ) ;
2022-09-25 21:36:30 +03:00
// 6. Let value be parsedURL, serialized and isomorphic encoded.
auto value = parsed_url . serialize ( ) ;
// 7. Append (`Location`, value) to responseObject’ s response’ s header list.
2024-04-26 20:24:20 +03:00
auto header = Infrastructure : : Header : : from_string_pair ( " Location " sv , value ) ;
response_object - > response ( ) - > header_list ( ) - > append ( move ( header ) ) ;
2022-09-25 21:36:30 +03:00
// 8. Return responseObject.
return response_object ;
}
// https://fetch.spec.whatwg.org/#dom-response-json
2022-10-05 19:09:26 +03:00
WebIDL : : ExceptionOr < JS : : NonnullGCPtr < Response > > Response : : json ( JS : : VM & vm , JS : : Value data , ResponseInit const & init )
2022-09-25 21:36:30 +03:00
{
auto & realm = * vm . current_realm ( ) ;
// 1. Let bytes the result of running serialize a JavaScript value to JSON bytes on data.
auto bytes = TRY ( Infra : : serialize_javascript_value_to_json_bytes ( vm , data ) ) ;
// 2. Let body be the result of extracting bytes.
auto [ body , _ ] = TRY ( extract_body ( realm , { bytes . bytes ( ) } ) ) ;
// 3. Let responseObject be the result of creating a Response object, given a new response, "response", and this’ s relevant Realm.
// FIXME: How can we reliably get 'this', i.e. the object the function was called on, in IDL-defined functions?
2023-08-13 14:05:26 +03:00
auto response_object = Response : : create ( realm , Infrastructure : : Response : : create ( vm ) , Headers : : Guard : : Response ) ;
2022-09-25 21:36:30 +03:00
// 4. Perform initialize a response given responseObject, init, and (body, "application/json").
auto body_with_type = Infrastructure : : BodyWithType {
2024-04-27 17:34:37 +03:00
. body = body ,
. type = MUST ( ByteBuffer : : copy ( " application/json " sv . bytes ( ) ) )
2022-09-25 21:36:30 +03:00
} ;
TRY ( response_object - > initialize_response ( init , move ( body_with_type ) ) ) ;
// 5. Return responseObject.
return response_object ;
}
// https://fetch.spec.whatwg.org/#dom-response-type
Bindings : : ResponseType Response : : type ( ) const
{
// The type getter steps are to return this’ s response’ s type.
return to_bindings_enum ( m_response - > type ( ) ) ;
}
// https://fetch.spec.whatwg.org/#dom-response-url
2024-04-27 17:34:37 +03:00
String Response : : url ( ) const
2022-09-25 21:36:30 +03:00
{
// The url getter steps are to return the empty string if this’ s response’ s URL is null; otherwise this’ s response’ s URL, serialized with exclude fragment set to true.
return ! m_response - > url ( ) . has_value ( )
2023-03-03 01:26:12 +03:00
? String { }
2024-04-27 17:34:37 +03:00
: MUST ( String : : from_byte_string ( m_response - > url ( ) - > serialize ( URL : : ExcludeFragment : : Yes ) ) ) ;
2022-09-25 21:36:30 +03:00
}
// https://fetch.spec.whatwg.org/#dom-response-redirected
bool Response : : redirected ( ) const
{
// The redirected getter steps are to return true if this’ s response’ s URL list has more than one item; otherwise false.
return m_response - > url_list ( ) . size ( ) > 1 ;
}
// https://fetch.spec.whatwg.org/#dom-response-status
u16 Response : : status ( ) const
{
// The status getter steps are to return this’ s response’ s status.
return m_response - > status ( ) ;
}
// https://fetch.spec.whatwg.org/#dom-response-ok
bool Response : : ok ( ) const
{
// The ok getter steps are to return true if this’ s response’ s status is an ok status; otherwise false.
return Infrastructure : : is_ok_status ( m_response - > status ( ) ) ;
}
// https://fetch.spec.whatwg.org/#dom-response-statustext
2024-04-27 17:34:37 +03:00
String Response : : status_text ( ) const
2022-09-25 21:36:30 +03:00
{
// The statusText getter steps are to return this’ s response’ s status message.
2024-04-27 17:34:37 +03:00
return MUST ( String : : from_utf8 ( m_response - > status_message ( ) ) ) ;
2022-09-25 21:36:30 +03:00
}
// https://fetch.spec.whatwg.org/#dom-response-headers
JS : : NonnullGCPtr < Headers > Response : : headers ( ) const
{
// The headers getter steps are to return this’ s headers.
return * m_headers ;
}
// https://fetch.spec.whatwg.org/#dom-response-clone
WebIDL : : ExceptionOr < JS : : NonnullGCPtr < Response > > Response : : clone ( ) const
{
2023-02-28 20:45:49 +03:00
auto & realm = this - > realm ( ) ;
2022-10-30 04:52:07 +03:00
2022-09-25 21:36:30 +03:00
// 1. If this is unusable, then throw a TypeError.
if ( is_unusable ( ) )
return WebIDL : : SimpleException { WebIDL : : SimpleExceptionType : : TypeError , " Response is unusable " sv } ;
// 2. Let clonedResponse be the result of cloning this’ s response.
2024-04-26 20:42:39 +03:00
auto cloned_response = m_response - > clone ( realm ) ;
2022-09-25 21:36:30 +03:00
// 3. Return the result of creating a Response object, given clonedResponse, this’ s headers’ s guard, and this’ s relevant Realm.
2023-08-13 14:05:26 +03:00
return Response : : create ( HTML : : relevant_realm ( * this ) , cloned_response , m_headers - > guard ( ) ) ;
2022-09-25 21:36:30 +03:00
}
}