Page Menu
Home
Phorge
Search
Configure Global Search
Log In
Files
F3050093
request.c
No One
Temporary
Actions
Download File
Edit File
Delete File
View Transforms
Subscribe
Flag For Later
Award Token
Size
44 KB
Referenced Files
None
Subscribers
None
request.c
View Options
#include
<stdlib.h>
#include
<string.h>
#include
<strings.h>
#include
<ctype.h>
#include
<stdio.h>
#include
"ant.h"
#include
"errors.h"
#include
"runtime.h"
#include
"internal.h"
#include
"common.h"
#include
"descriptors.h"
#include
"modules/blob.h"
#include
"modules/buffer.h"
#include
"modules/assert.h"
#include
"modules/abort.h"
#include
"modules/formdata.h"
#include
"modules/headers.h"
#include
"modules/multipart.h"
#include
"modules/request.h"
#include
"modules/symbol.h"
#include
"modules/url.h"
#include
"modules/json.h"
#include
"streams/pipes.h"
#include
"streams/readable.h"
ant_value_t
g_request_proto
=
0
;
static
request_data_t
*
get_data
(
ant_value_t
obj
)
{
ant_value_t
slot
=
js_get_slot
(
obj
,
SLOT_DATA
);
if
(
vtype
(
slot
)
!=
T_NUM
)
return
NULL
;
return
(
request_data_t
*
)(
uintptr_t
)(
size_t
)
js_getnum
(
slot
);
}
request_data_t
*
request_get_data
(
ant_value_t
obj
)
{
return
get_data
(
obj
);
}
ant_value_t
request_get_headers
(
ant_value_t
obj
)
{
return
js_get_slot
(
obj
,
SLOT_REQUEST_HEADERS
);
}
ant_value_t
request_get_signal
(
ant_t
*
js
,
ant_value_t
obj
)
{
ant_value_t
signal
=
js_get_slot
(
obj
,
SLOT_REQUEST_SIGNAL
);
if
(
vtype
(
signal
)
!=
T_UNDEF
)
return
signal
;
signal
=
abort_signal_create_dependent
(
js
,
js_mkundef
());
if
(
is_err
(
signal
))
return
signal
;
ant_value_t
abort_reason
=
js_get_slot
(
obj
,
SLOT_REQUEST_ABORT_REASON
);
if
(
vtype
(
abort_reason
)
!=
T_UNDEF
)
signal_do_abort
(
js
,
signal
,
abort_reason
);
js_set_slot_wb
(
js
,
obj
,
SLOT_REQUEST_SIGNAL
,
signal
);
return
signal
;
}
static
void
data_free
(
request_data_t
*
d
)
{
if
(
!
d
)
return
;
free
(
d
->
method
);
url_state_clear
(
&
d
->
url
);
free
(
d
->
referrer
);
free
(
d
->
referrer_policy
);
free
(
d
->
mode
);
free
(
d
->
credentials
);
free
(
d
->
cache
);
free
(
d
->
redirect
);
free
(
d
->
integrity
);
free
(
d
->
body_data
);
free
(
d
->
body_type
);
free
(
d
);
}
static
request_data_t
*
data_new_with
(
const
char
*
method
,
const
char
*
mode
)
{
request_data_t
*
d
=
calloc
(
1
,
sizeof
(
request_data_t
));
if
(
!
d
)
return
NULL
;
d
->
method
=
strdup
(
method
?
method
:
"GET"
);
d
->
referrer
=
strdup
(
"client"
);
d
->
referrer_policy
=
strdup
(
""
);
d
->
mode
=
strdup
(
mode
);
d
->
credentials
=
strdup
(
"same-origin"
);
d
->
cache
=
strdup
(
"default"
);
d
->
redirect
=
strdup
(
"follow"
);
d
->
integrity
=
strdup
(
""
);
if
(
!
d
->
method
||
!
d
->
referrer
||
!
d
->
referrer_policy
||
!
d
->
mode
||
!
d
->
credentials
||
!
d
->
cache
||
!
d
->
redirect
||
!
d
->
integrity
)
{
data_free
(
d
);
return
NULL
;
}
return
d
;
}
static
request_data_t
*
data_new
(
void
)
{
return
data_new_with
(
"GET"
,
"cors"
);
}
static
request_data_t
*
data_new_server
(
const
char
*
method
)
{
return
data_new_with
(
method
,
"same-origin"
);
}
static
ant_value_t
request_create_object
(
ant_t
*
js
,
request_data_t
*
req
,
ant_value_t
headers_obj
,
bool
create_signal
)
{
ant_value_t
obj
=
js_mkobj
(
js
);
ant_value_t
hdrs
=
is_object_type
(
headers_obj
)
?
headers_obj
:
headers_create_empty
(
js
);
js_set_proto_init
(
obj
,
g_request_proto
);
js_set_slot
(
obj
,
SLOT_BRAND
,
js_mknum
(
BRAND_REQUEST
));
js_set_slot
(
obj
,
SLOT_DATA
,
ANT_PTR
(
req
));
headers_set_guard
(
hdrs
,
strcmp
(
req
->
mode
,
"no-cors"
)
==
0
?
HEADERS_GUARD_REQUEST_NO_CORS
:
HEADERS_GUARD_REQUEST
);
headers_apply_guard
(
hdrs
);
js_set_slot_wb
(
js
,
obj
,
SLOT_REQUEST_HEADERS
,
hdrs
);
js_set_slot
(
obj
,
SLOT_REQUEST_ABORT_REASON
,
js_mkundef
());
js_set_slot_wb
(
js
,
obj
,
SLOT_REQUEST_SIGNAL
,
create_signal
?
abort_signal_create_dependent
(
js
,
js_mkundef
())
:
js_mkundef
()
);
return
obj
;
}
static
char
*
request_build_server_base_url
(
const
char
*
host
,
const
char
*
server_hostname
,
int
server_port
)
{
const
char
*
authority
=
(
host
&&
host
[
0
])
?
host
:
server_hostname
;
size_t
authority_len
=
authority
?
strlen
(
authority
)
:
0
;
bool
include_port
=
(
!
host
||
!
host
[
0
])
&&
server_port
>
0
&&
server_port
!=
80
;
size_t
cap
=
sizeof
(
"http://"
)
-
1
+
authority_len
+
1
+
16
+
1
;
char
*
base
=
malloc
(
cap
);
int
written
=
0
;
if
(
!
base
)
return
NULL
;
if
(
include_port
)
written
=
snprintf
(
base
,
cap
,
"http://%s:%d/"
,
authority
?
authority
:
""
,
server_port
);
else
written
=
snprintf
(
base
,
cap
,
"http://%s/"
,
authority
?
authority
:
""
);
if
(
written
<
0
||
(
size_t
)
written
>=
cap
)
{
free
(
base
);
return
NULL
;
}
return
base
;
}
static
int
request_parse_server_url
(
const
char
*
target
,
bool
absolute_target
,
const
char
*
host
,
const
char
*
server_hostname
,
int
server_port
,
url_state_t
*
out
)
{
char
*
base
=
NULL
;
int
rc
=
0
;
if
(
absolute_target
)
return
parse_url_to_state
(
target
,
NULL
,
out
);
base
=
request_build_server_base_url
(
host
,
server_hostname
,
server_port
);
if
(
!
base
)
return
-1
;
rc
=
parse_url_to_state
(
target
,
base
,
out
);
free
(
base
);
return
rc
;
}
static
request_data_t
*
data_dup
(
const
request_data_t
*
src
)
{
request_data_t
*
d
=
calloc
(
1
,
sizeof
(
request_data_t
));
if
(
!
d
)
return
NULL
;
#define DUP_STR(f) do { d->f = src->f ? strdup(src->f) : NULL; } while(0)
DUP_STR
(
method
);
DUP_STR
(
referrer
);
DUP_STR
(
referrer_policy
);
DUP_STR
(
mode
);
DUP_STR
(
credentials
);
DUP_STR
(
cache
);
DUP_STR
(
redirect
);
DUP_STR
(
integrity
);
DUP_STR
(
body_type
);
#undef DUP_STR
url_state_t
*
su
=
(
url_state_t
*
)
&
src
->
url
;
url_state_t
*
du
=
&
d
->
url
;
#define DUP_US(f) do { du->f = su->f ? strdup(su->f) : NULL; } while(0)
DUP_US
(
protocol
);
DUP_US
(
username
);
DUP_US
(
password
);
DUP_US
(
hostname
);
DUP_US
(
port
);
DUP_US
(
pathname
);
DUP_US
(
search
);
DUP_US
(
hash
);
#undef DUP_US
d
->
keepalive
=
src
->
keepalive
;
d
->
reload_navigation
=
src
->
reload_navigation
;
d
->
history_navigation
=
src
->
history_navigation
;
d
->
has_body
=
src
->
has_body
;
d
->
body_is_stream
=
src
->
body_is_stream
;
d
->
body_used
=
src
->
body_used
;
d
->
body_size
=
src
->
body_size
;
if
(
src
->
body_data
&&
src
->
body_size
>
0
)
{
d
->
body_data
=
malloc
(
src
->
body_size
);
if
(
!
d
->
body_data
)
{
data_free
(
d
);
return
NULL
;
}
memcpy
(
d
->
body_data
,
src
->
body_data
,
src
->
body_size
);
}
return
d
;
}
static
bool
is_token_char
(
unsigned
char
c
)
{
if
(
c
==
0
||
c
>
127
)
return
false
;
static
const
char
*
delimiters
=
"(),/:;<=>?@[
\\
]{}
\"
"
;
return
c
>
32
&&
!
strchr
(
delimiters
,
(
char
)
c
);
}
static
bool
is_valid_method
(
const
char
*
m
)
{
if
(
!
m
||
!*
m
)
return
false
;
for
(
const
unsigned
char
*
p
=
(
const
unsigned
char
*
)
m
;
*
p
;
p
++
)
if
(
!
is_token_char
(
*
p
))
return
false
;
return
true
;
}
static
bool
is_forbidden_method
(
const
char
*
m
)
{
return
strcasecmp
(
m
,
"CONNECT"
)
==
0
||
strcasecmp
(
m
,
"TRACE"
)
==
0
||
strcasecmp
(
m
,
"TRACK"
)
==
0
;
}
static
bool
is_cors_safelisted_method
(
const
char
*
m
)
{
return
strcasecmp
(
m
,
"GET"
)
==
0
||
strcasecmp
(
m
,
"HEAD"
)
==
0
||
strcasecmp
(
m
,
"POST"
)
==
0
;
}
static
void
normalize_method
(
char
*
m
)
{
static
const
char
*
norm
[]
=
{
"DELETE"
,
"GET"
,
"HEAD"
,
"OPTIONS"
,
"POST"
,
"PUT"
};
for
(
int
i
=
0
;
i
<
6
;
i
++
)
{
if
(
strcasecmp
(
m
,
norm
[
i
])
==
0
)
{
strcpy
(
m
,
norm
[
i
]);
return
;
}}
}
static
ant_value_t
request_rejection_reason
(
ant_t
*
js
,
ant_value_t
value
)
{
if
(
!
is_err
(
value
))
return
value
;
ant_value_t
reason
=
js
->
thrown_exists
?
js
->
thrown_value
:
value
;
js
->
thrown_exists
=
false
;
js
->
thrown_value
=
js_mkundef
();
js
->
thrown_stack
=
js_mkundef
();
return
reason
;
}
static
const
char
*
request_effective_body_type
(
ant_t
*
js
,
ant_value_t
req_obj
,
request_data_t
*
d
)
{
ant_value_t
headers
=
js_get_slot
(
req_obj
,
SLOT_REQUEST_HEADERS
);
if
(
!
headers_is_headers
(
headers
))
return
d
?
d
->
body_type
:
NULL
;
ant_value_t
ct
=
headers_get_value
(
js
,
headers
,
"content-type"
);
if
(
vtype
(
ct
)
==
T_STR
)
return
js_getstr
(
js
,
ct
,
NULL
);
return
d
?
d
->
body_type
:
NULL
;
}
static
bool
copy_body_bytes
(
ant_t
*
js
,
const
uint8_t
*
src
,
size_t
src_len
,
uint8_t
**
out_data
,
size_t
*
out_size
,
ant_value_t
*
err_out
)
{
uint8_t
*
buf
=
NULL
;
*
out_data
=
NULL
;
*
out_size
=
0
;
if
(
src_len
==
0
)
return
true
;
buf
=
malloc
(
src_len
);
if
(
!
buf
)
{
*
err_out
=
js_mkerr
(
js
,
"out of memory"
);
return
false
;
}
memcpy
(
buf
,
src
,
src_len
);
*
out_data
=
buf
;
*
out_size
=
src_len
;
return
true
;
}
static
bool
extract_buffer_source_body
(
ant_t
*
js
,
ant_value_t
body_val
,
uint8_t
**
out_data
,
size_t
*
out_size
,
ant_value_t
*
err_out
)
{
const
uint8_t
*
src
=
NULL
;
size_t
src_len
=
0
;
if
(
!
((
vtype
(
body_val
)
==
T_TYPEDARRAY
||
vtype
(
body_val
)
==
T_OBJ
)
&&
buffer_source_get_bytes
(
js
,
body_val
,
&
src
,
&
src_len
))
)
return
false
;
return
copy_body_bytes
(
js
,
src
,
src_len
,
out_data
,
out_size
,
err_out
);
}
static
bool
extract_stream_body
(
ant_t
*
js
,
ant_value_t
body_val
,
ant_value_t
*
out_stream
,
ant_value_t
*
err_out
)
{
if
(
!
rs_is_stream
(
body_val
))
return
false
;
if
(
rs_stream_unusable
(
body_val
))
{
*
err_out
=
js_mkerr_typed
(
js
,
JS_ERR_TYPE
,
"body stream is disturbed or locked"
);
return
false
;
}
*
out_stream
=
body_val
;
return
true
;
}
static
bool
extract_blob_body
(
ant_t
*
js
,
ant_value_t
body_val
,
uint8_t
**
out_data
,
size_t
*
out_size
,
char
**
out_type
,
ant_value_t
*
err_out
)
{
blob_data_t
*
bd
=
blob_is_blob
(
js
,
body_val
)
?
blob_get_data
(
body_val
)
:
NULL
;
if
(
!
bd
)
return
false
;
if
(
!
copy_body_bytes
(
js
,
bd
->
data
,
bd
->
size
,
out_data
,
out_size
,
err_out
))
return
false
;
if
(
bd
->
type
&&
bd
->
type
[
0
])
*
out_type
=
strdup
(
bd
->
type
);
return
true
;
}
static
bool
extract_urlsearchparams_body
(
ant_t
*
js
,
ant_value_t
body_val
,
uint8_t
**
out_data
,
size_t
*
out_size
,
char
**
out_type
)
{
char
*
serialized
=
NULL
;
if
(
!
usp_is_urlsearchparams
(
js
,
body_val
))
return
false
;
serialized
=
usp_serialize
(
js
,
body_val
);
if
(
serialized
)
{
*
out_data
=
(
uint8_t
*
)
serialized
;
*
out_size
=
strlen
(
serialized
);
*
out_type
=
strdup
(
"application/x-www-form-urlencoded;charset=UTF-8"
);
}
return
true
;
}
static
bool
extract_formdata_body
(
ant_t
*
js
,
ant_value_t
body_val
,
uint8_t
**
out_data
,
size_t
*
out_size
,
char
**
out_type
,
ant_value_t
*
err_out
)
{
char
*
boundary
=
NULL
;
char
*
content_type
=
NULL
;
size_t
mp_size
=
0
;
uint8_t
*
mp
=
NULL
;
if
(
!
formdata_is_formdata
(
js
,
body_val
))
return
false
;
mp
=
formdata_serialize_multipart
(
js
,
body_val
,
&
mp_size
,
&
boundary
);
if
(
!
mp
)
{
*
err_out
=
js_mkerr
(
js
,
"out of memory"
);
return
false
;
}
if
(
mp_size
>
0
)
*
out_data
=
mp
;
else
free
(
mp
);
*
out_size
=
mp_size
;
if
(
boundary
)
{
size_t
ct_len
=
snprintf
(
NULL
,
0
,
"multipart/form-data; boundary=%s"
,
boundary
);
content_type
=
malloc
(
ct_len
+
1
);
if
(
!
content_type
)
{
free
(
boundary
);
if
(
mp_size
>
0
)
free
(
mp
);
*
out_data
=
NULL
;
*
out_size
=
0
;
*
err_out
=
js_mkerr
(
js
,
"out of memory"
);
return
false
;
}
snprintf
(
content_type
,
ct_len
+
1
,
"multipart/form-data; boundary=%s"
,
boundary
);
free
(
boundary
);
*
out_type
=
content_type
;
}
return
true
;
}
static
bool
extract_string_body
(
ant_t
*
js
,
ant_value_t
body_val
,
uint8_t
**
out_data
,
size_t
*
out_size
,
char
**
out_type
,
ant_value_t
*
err_out
)
{
size_t
len
=
0
;
const
char
*
s
=
NULL
;
if
(
vtype
(
body_val
)
!=
T_STR
)
{
body_val
=
js_tostring_val
(
js
,
body_val
);
if
(
is_err
(
body_val
))
{
*
err_out
=
body_val
;
return
false
;
}}
s
=
js_getstr
(
js
,
body_val
,
&
len
);
if
(
!
copy_body_bytes
(
js
,
(
const
uint8_t
*
)
s
,
len
,
out_data
,
out_size
,
err_out
))
return
false
;
*
out_type
=
strdup
(
"text/plain;charset=UTF-8"
);
return
true
;
}
static
bool
extract_body
(
ant_t
*
js
,
ant_value_t
body_val
,
uint8_t
**
out_data
,
size_t
*
out_size
,
char
**
out_type
,
ant_value_t
*
out_stream
,
ant_value_t
*
err_out
)
{
*
out_data
=
NULL
;
*
out_size
=
0
;
*
out_type
=
NULL
;
*
out_stream
=
js_mkundef
();
*
err_out
=
js_mkundef
();
if
(
vtype
(
body_val
)
==
T_NULL
||
vtype
(
body_val
)
==
T_UNDEF
)
return
true
;
if
(
extract_buffer_source_body
(
js
,
body_val
,
out_data
,
out_size
,
err_out
))
return
true
;
if
(
vtype
(
body_val
)
==
T_OBJ
&&
rs_is_stream
(
body_val
))
return
extract_stream_body
(
js
,
body_val
,
out_stream
,
err_out
);
if
(
vtype
(
body_val
)
==
T_OBJ
&&
extract_blob_body
(
js
,
body_val
,
out_data
,
out_size
,
out_type
,
err_out
))
return
true
;
if
(
vtype
(
body_val
)
==
T_OBJ
&&
extract_urlsearchparams_body
(
js
,
body_val
,
out_data
,
out_size
,
out_type
))
return
true
;
if
(
vtype
(
body_val
)
==
T_OBJ
&&
extract_formdata_body
(
js
,
body_val
,
out_data
,
out_size
,
out_type
,
err_out
))
return
true
;
return
extract_string_body
(
js
,
body_val
,
out_data
,
out_size
,
out_type
,
err_out
);
}
enum
{
BODY_TEXT
=
0
,
BODY_JSON
,
BODY_ARRAYBUFFER
,
BODY_BLOB
,
BODY_BYTES
,
BODY_FORMDATA
};
static
void
resolve_body_promise
(
ant_t
*
js
,
ant_value_t
promise
,
const
uint8_t
*
data
,
size_t
size
,
const
char
*
body_type
,
int
mode
,
bool
has_body
)
{
switch
(
mode
)
{
case
BODY_TEXT
:
{
ant_value_t
str
=
(
data
&&
size
>
0
)
?
js_mkstr
(
js
,
(
const
char
*
)
data
,
size
)
:
js_mkstr
(
js
,
""
,
0
);
js_resolve_promise
(
js
,
promise
,
str
);
break
;
}
case
BODY_JSON
:
{
ant_value_t
str
=
(
data
&&
size
>
0
)
?
js_mkstr
(
js
,
(
const
char
*
)
data
,
size
)
:
js_mkstr
(
js
,
""
,
0
);
ant_value_t
parsed
=
json_parse_value
(
js
,
str
);
if
(
is_err
(
parsed
))
js_reject_promise
(
js
,
promise
,
request_rejection_reason
(
js
,
parsed
));
else
js_resolve_promise
(
js
,
promise
,
parsed
);
break
;
}
case
BODY_ARRAYBUFFER
:
{
ArrayBufferData
*
ab
=
create_array_buffer_data
(
size
);
if
(
!
ab
)
{
js_reject_promise
(
js
,
promise
,
js_mkerr
(
js
,
"out of memory"
));
break
;
}
if
(
data
&&
size
>
0
)
memcpy
(
ab
->
data
,
data
,
size
);
js_resolve_promise
(
js
,
promise
,
create_arraybuffer_obj
(
js
,
ab
));
break
;
}
case
BODY_BLOB
:
{
const
char
*
type
=
body_type
?
body_type
:
""
;
js_resolve_promise
(
js
,
promise
,
blob_create
(
js
,
data
,
size
,
type
));
break
;
}
case
BODY_BYTES
:
{
ArrayBufferData
*
ab
=
create_array_buffer_data
(
size
);
if
(
!
ab
)
{
js_reject_promise
(
js
,
promise
,
js_mkerr
(
js
,
"out of memory"
));
break
;
}
if
(
data
&&
size
>
0
)
memcpy
(
ab
->
data
,
data
,
size
);
js_resolve_promise
(
js
,
promise
,
create_typed_array
(
js
,
TYPED_ARRAY_UINT8
,
ab
,
0
,
size
,
"Uint8Array"
));
break
;
}
case
BODY_FORMDATA
:
{
ant_value_t
fd
=
formdata_parse_body
(
js
,
data
,
size
,
body_type
,
has_body
);
if
(
is_err
(
fd
))
js_reject_promise
(
js
,
promise
,
request_rejection_reason
(
js
,
fd
));
else
js_resolve_promise
(
js
,
promise
,
fd
);
break
;
}}
}
static
uint8_t
*
concat_chunks
(
ant_t
*
js
,
ant_value_t
chunks
,
size_t
*
out_size
)
{
ant_offset_t
n
=
js_arr_len
(
js
,
chunks
);
size_t
total
=
0
;
for
(
ant_offset_t
i
=
0
;
i
<
n
;
i
++
)
{
ant_value_t
chunk
=
js_arr_get
(
js
,
chunks
,
i
);
if
(
vtype
(
chunk
)
==
T_TYPEDARRAY
)
{
TypedArrayData
*
ta
=
(
TypedArrayData
*
)
js_gettypedarray
(
chunk
);
if
(
ta
&&
ta
->
buffer
&&
!
ta
->
buffer
->
is_detached
)
total
+=
ta
->
byte_length
;
}}
uint8_t
*
buf
=
total
>
0
?
malloc
(
total
)
:
NULL
;
if
(
total
>
0
&&
!
buf
)
return
NULL
;
size_t
pos
=
0
;
for
(
ant_offset_t
i
=
0
;
i
<
n
;
i
++
)
{
ant_value_t
chunk
=
js_arr_get
(
js
,
chunks
,
i
);
if
(
vtype
(
chunk
)
==
T_TYPEDARRAY
)
{
TypedArrayData
*
ta
=
(
TypedArrayData
*
)
js_gettypedarray
(
chunk
);
if
(
ta
&&
ta
->
buffer
&&
!
ta
->
buffer
->
is_detached
&&
ta
->
byte_length
>
0
)
{
memcpy
(
buf
+
pos
,
ta
->
buffer
->
data
+
ta
->
byte_offset
,
ta
->
byte_length
);
pos
+=
ta
->
byte_length
;
}}}
*
out_size
=
pos
;
return
buf
;
}
static
ant_value_t
stream_body_read
(
ant_t
*
js
,
ant_value_t
*
args
,
int
nargs
);
static
ant_value_t
stream_body_rejected
(
ant_t
*
js
,
ant_value_t
*
args
,
int
nargs
)
{
ant_value_t
state
=
js_get_slot
(
js
->
current_func
,
SLOT_DATA
);
ant_value_t
promise
=
js_get
(
js
,
state
,
"promise"
);
ant_value_t
reason
=
(
nargs
>
0
)
?
args
[
0
]
:
js_mkundef
();
js_reject_promise
(
js
,
promise
,
reason
);
return
js_mkundef
();
}
static
void
stream_schedule_next_read
(
ant_t
*
js
,
ant_value_t
state
,
ant_value_t
read_fn
,
ant_value_t
reader
)
{
ant_value_t
next_p
=
rs_default_reader_read
(
js
,
reader
);
ant_value_t
fulfill
=
js_heavy_mkfun
(
js
,
stream_body_read
,
state
);
ant_value_t
reject
=
js_heavy_mkfun
(
js
,
stream_body_rejected
,
state
);
ant_value_t
then_result
=
js_promise_then
(
js
,
next_p
,
fulfill
,
reject
);
promise_mark_handled
(
then_result
);
}
static
ant_value_t
stream_body_read
(
ant_t
*
js
,
ant_value_t
*
args
,
int
nargs
)
{
ant_value_t
state
=
js_get_slot
(
js
->
current_func
,
SLOT_DATA
);
ant_value_t
result
=
(
nargs
>
0
)
?
args
[
0
]
:
js_mkundef
();
ant_value_t
promise
=
js_get
(
js
,
state
,
"promise"
);
ant_value_t
reader
=
js_get
(
js
,
state
,
"reader"
);
ant_value_t
chunks
=
js_get
(
js
,
state
,
"chunks"
);
int
mode
=
(
int
)
js_getnum
(
js_get
(
js
,
state
,
"mode"
));
ant_value_t
done_val
=
js_get
(
js
,
result
,
"done"
);
ant_value_t
value
=
js_get
(
js
,
result
,
"value"
);
if
(
vtype
(
done_val
)
==
T_BOOL
&&
done_val
==
js_true
)
{
size_t
size
=
0
;
uint8_t
*
data
=
concat_chunks
(
js
,
chunks
,
&
size
);
ant_value_t
type_v
=
js_get
(
js
,
state
,
"type"
);
const
char
*
body_type
=
(
vtype
(
type_v
)
==
T_STR
)
?
js_getstr
(
js
,
type_v
,
NULL
)
:
NULL
;
resolve_body_promise
(
js
,
promise
,
data
,
size
,
body_type
,
mode
,
true
);
free
(
data
);
return
js_mkundef
();
}
if
(
vtype
(
value
)
!=
T_UNDEF
&&
vtype
(
value
)
!=
T_NULL
)
js_arr_push
(
js
,
chunks
,
value
);
stream_schedule_next_read
(
js
,
state
,
js_mkundef
(),
reader
);
return
js_mkundef
();
}
static
ant_value_t
consume_body_from_stream
(
ant_t
*
js
,
ant_value_t
stream
,
ant_value_t
promise
,
int
mode
,
const
char
*
body_type
)
{
ant_value_t
reader_args
[
1
]
=
{
stream
};
ant_value_t
saved
=
js
->
new_target
;
js
->
new_target
=
g_reader_proto
;
ant_value_t
reader
=
js_rs_reader_ctor
(
js
,
reader_args
,
1
);
js
->
new_target
=
saved
;
if
(
is_err
(
reader
))
{
js_reject_promise
(
js
,
promise
,
reader
);
return
promise
;
}
ant_value_t
state
=
js_mkobj
(
js
);
js_set
(
js
,
state
,
"promise"
,
promise
);
js_set
(
js
,
state
,
"reader"
,
reader
);
js_set
(
js
,
state
,
"chunks"
,
js_mkarr
(
js
));
js_set
(
js
,
state
,
"mode"
,
js_mknum
(
mode
));
js_set
(
js
,
state
,
"type"
,
body_type
?
js_mkstr
(
js
,
body_type
,
strlen
(
body_type
))
:
js_mkundef
());
stream_schedule_next_read
(
js
,
state
,
js_mkundef
(),
reader
);
return
promise
;
}
static
ant_value_t
consume_body
(
ant_t
*
js
,
int
mode
)
{
ant_value_t
this
=
js_getthis
(
js
);
request_data_t
*
d
=
get_data
(
this
);
ant_value_t
promise
=
js_mkpromise
(
js
);
if
(
!
d
)
{
js_reject_promise
(
js
,
promise
,
request_rejection_reason
(
js
,
js_mkerr_typed
(
js
,
JS_ERR_TYPE
,
"Invalid Request object"
)));
return
promise
;
}
if
(
!
d
->
has_body
)
{
resolve_body_promise
(
js
,
promise
,
NULL
,
0
,
request_effective_body_type
(
js
,
this
,
d
),
mode
,
false
);
return
promise
;
}
if
(
d
->
body_used
)
{
js_reject_promise
(
js
,
promise
,
request_rejection_reason
(
js
,
js_mkerr_typed
(
js
,
JS_ERR_TYPE
,
"body stream is disturbed or locked"
)));
return
promise
;
}
d
->
body_used
=
true
;
ant_value_t
stream
=
js_get_slot
(
this
,
SLOT_REQUEST_BODY_STREAM
);
if
(
rs_is_stream
(
stream
)
&&
d
->
body_is_stream
)
return
consume_body_from_stream
(
js
,
stream
,
promise
,
mode
,
request_effective_body_type
(
js
,
this
,
d
));
resolve_body_promise
(
js
,
promise
,
d
->
body_data
,
d
->
body_size
,
request_effective_body_type
(
js
,
this
,
d
),
mode
,
true
);
return
promise
;
}
static
ant_value_t
js_req_text
(
ant_t
*
js
,
ant_value_t
*
args
,
int
nargs
)
{
return
consume_body
(
js
,
BODY_TEXT
);
}
static
ant_value_t
js_req_json
(
ant_t
*
js
,
ant_value_t
*
args
,
int
nargs
)
{
return
consume_body
(
js
,
BODY_JSON
);
}
static
ant_value_t
js_req_array_buffer
(
ant_t
*
js
,
ant_value_t
*
args
,
int
nargs
)
{
return
consume_body
(
js
,
BODY_ARRAYBUFFER
);
}
static
ant_value_t
js_req_blob
(
ant_t
*
js
,
ant_value_t
*
args
,
int
nargs
)
{
return
consume_body
(
js
,
BODY_BLOB
);
}
static
ant_value_t
js_req_bytes
(
ant_t
*
js
,
ant_value_t
*
args
,
int
nargs
)
{
return
consume_body
(
js
,
BODY_BYTES
);
}
static
ant_value_t
js_req_form_data
(
ant_t
*
js
,
ant_value_t
*
args
,
int
nargs
)
{
return
consume_body
(
js
,
BODY_FORMDATA
);
}
static
ant_value_t
request_set_extracted_body
(
ant_t
*
js
,
ant_value_t
req_obj
,
ant_value_t
headers
,
request_data_t
*
req
,
uint8_t
*
body_data
,
size_t
body_size
,
char
*
body_type
,
ant_value_t
body_stream
,
bool
duplex_provided
)
{
free
(
req
->
body_data
);
free
(
req
->
body_type
);
req
->
body_data
=
body_data
;
req
->
body_size
=
body_size
;
req
->
body_type
=
body_type
;
req
->
body_is_stream
=
rs_is_stream
(
body_stream
);
req
->
has_body
=
true
;
if
(
!
req
->
body_is_stream
)
{
if
(
body_type
&&
body_type
[
0
])
headers_append_if_missing
(
headers
,
"content-type"
,
body_type
);
return
js_mkundef
();
}
if
(
req
->
keepalive
)
{
return
js_mkerr_typed
(
js
,
JS_ERR_TYPE
,
"Failed to construct 'Request': keepalive cannot be used with a ReadableStream body"
);
}
if
(
!
duplex_provided
)
{
return
js_mkerr_typed
(
js
,
JS_ERR_TYPE
,
"Failed to construct 'Request': duplex must be provided for a ReadableStream body"
);
}
js_set_slot_wb
(
js
,
req_obj
,
SLOT_REQUEST_BODY_STREAM
,
body_stream
);
if
(
body_type
&&
body_type
[
0
])
headers_append_if_missing
(
headers
,
"content-type"
,
body_type
);
return
js_mkundef
();
}
static
void
request_clear_body
(
ant_t
*
js
,
ant_value_t
req_obj
,
request_data_t
*
req
)
{
free
(
req
->
body_data
);
free
(
req
->
body_type
);
req
->
body_data
=
NULL
;
req
->
body_size
=
0
;
req
->
body_type
=
NULL
;
req
->
body_is_stream
=
false
;
req
->
has_body
=
false
;
js_set_slot_wb
(
js
,
req_obj
,
SLOT_REQUEST_BODY_STREAM
,
js_mkundef
());
}
static
ant_value_t
request_copy_source_body
(
ant_t
*
js
,
ant_value_t
req_obj
,
ant_value_t
input
,
request_data_t
*
req
,
request_data_t
*
src
)
{
ant_value_t
src_stream
=
js_get_slot
(
input
,
SLOT_REQUEST_BODY_STREAM
);
if
(
src
->
body_used
)
{
return
js_mkerr_typed
(
js
,
JS_ERR_TYPE
,
"Cannot construct Request with unusable body"
);
}
if
(
rs_is_stream
(
src_stream
)
&&
rs_stream_unusable
(
src_stream
))
{
return
js_mkerr_typed
(
js
,
JS_ERR_TYPE
,
"body stream is disturbed or locked"
);
}
if
(
!
src
->
body_is_stream
)
{
req
->
body_data
=
malloc
(
src
->
body_size
);
if
(
src
->
body_size
>
0
&&
!
req
->
body_data
)
return
js_mkerr
(
js
,
"out of memory"
);
if
(
src
->
body_size
>
0
)
memcpy
(
req
->
body_data
,
src
->
body_data
,
src
->
body_size
);
req
->
body_size
=
src
->
body_size
;
req
->
body_type
=
src
->
body_type
?
strdup
(
src
->
body_type
)
:
NULL
;
req
->
body_is_stream
=
false
;
req
->
has_body
=
true
;
return
js_mkundef
();
}
if
(
!
rs_is_stream
(
src_stream
))
return
js_mkundef
();
ant_value_t
branches
=
readable_stream_tee
(
js
,
src_stream
);
if
(
is_err
(
branches
))
return
branches
;
if
(
vtype
(
branches
)
!=
T_ARR
)
{
return
js_mkerr_typed
(
js
,
JS_ERR_TYPE
,
"Failed to construct 'Request': tee() did not return branches"
);
}
js_set_slot_wb
(
js
,
req_obj
,
SLOT_REQUEST_BODY_STREAM
,
js_arr_get
(
js
,
branches
,
1
));
req
->
body_is_stream
=
true
;
req
->
has_body
=
true
;
return
js_mkundef
();
}
#define REQ_GETTER_START(name) \
static ant_value_t js_req_get_##name(ant_t *js, ant_value_t *args, int nargs) { \
ant_value_t this = js_getthis(js); \
request_data_t *d = get_data(this); \
if (!d) return js_mkundef();
#define REQ_GETTER_END }
REQ_GETTER_START
(
method
)
return
js_mkstr
(
js
,
d
->
method
,
strlen
(
d
->
method
));
REQ_GETTER_END
REQ_GETTER_START
(
url
)
char
*
href
=
build_href
(
&
d
->
url
);
if
(
!
href
)
return
js_mkstr
(
js
,
""
,
0
);
ant_value_t
ret
=
js_mkstr
(
js
,
href
,
strlen
(
href
));
free
(
href
);
return
ret
;
REQ_GETTER_END
REQ_GETTER_START
(
headers
)
return
js_get_slot
(
this
,
SLOT_REQUEST_HEADERS
);
REQ_GETTER_END
REQ_GETTER_START
(
destination
)
(
void
)
d
;
return
js_mkstr
(
js
,
""
,
0
);
REQ_GETTER_END
REQ_GETTER_START
(
referrer
)
if
(
!
d
->
referrer
||
strcmp
(
d
->
referrer
,
"no-referrer"
)
==
0
)
return
js_mkstr
(
js
,
""
,
0
);
if
(
strcmp
(
d
->
referrer
,
"client"
)
==
0
)
return
js_mkstr
(
js
,
"about:client"
,
12
);
return
js_mkstr
(
js
,
d
->
referrer
,
strlen
(
d
->
referrer
));
REQ_GETTER_END
REQ_GETTER_START
(
referrer_policy
)
const
char
*
p
=
d
->
referrer_policy
?
d
->
referrer_policy
:
""
;
return
js_mkstr
(
js
,
p
,
strlen
(
p
));
REQ_GETTER_END
REQ_GETTER_START
(
mode
)
return
js_mkstr
(
js
,
d
->
mode
,
strlen
(
d
->
mode
));
REQ_GETTER_END
REQ_GETTER_START
(
credentials
)
return
js_mkstr
(
js
,
d
->
credentials
,
strlen
(
d
->
credentials
));
REQ_GETTER_END
REQ_GETTER_START
(
cache
)
return
js_mkstr
(
js
,
d
->
cache
,
strlen
(
d
->
cache
));
REQ_GETTER_END
REQ_GETTER_START
(
redirect
)
return
js_mkstr
(
js
,
d
->
redirect
,
strlen
(
d
->
redirect
));
REQ_GETTER_END
REQ_GETTER_START
(
integrity
)
const
char
*
ig
=
d
->
integrity
?
d
->
integrity
:
""
;
return
js_mkstr
(
js
,
ig
,
strlen
(
ig
));
REQ_GETTER_END
REQ_GETTER_START
(
keepalive
)
return
js_bool
(
d
->
keepalive
);
REQ_GETTER_END
REQ_GETTER_START
(
is_reload_navigation
)
return
js_bool
(
d
->
reload_navigation
);
REQ_GETTER_END
REQ_GETTER_START
(
is_history_navigation
)
return
js_bool
(
d
->
history_navigation
);
REQ_GETTER_END
REQ_GETTER_START
(
signal
)
return
request_get_signal
(
js
,
this
);
REQ_GETTER_END
REQ_GETTER_START
(
duplex
)
return
js_mkstr
(
js
,
"half"
,
4
);
REQ_GETTER_END
static
ant_value_t
req_body_pull
(
ant_t
*
js
,
ant_value_t
*
args
,
int
nargs
)
{
ant_value_t
req_obj
=
js_get_slot
(
js
->
current_func
,
SLOT_DATA
);
request_data_t
*
d
=
get_data
(
req_obj
);
ant_value_t
ctrl
=
(
nargs
>
0
)
?
args
[
0
]
:
js_mkundef
();
if
(
d
&&
d
->
body_data
&&
d
->
body_size
>
0
)
{
ArrayBufferData
*
ab
=
create_array_buffer_data
(
d
->
body_size
);
if
(
ab
)
{
memcpy
(
ab
->
data
,
d
->
body_data
,
d
->
body_size
);
rs_controller_enqueue
(
js
,
ctrl
,
create_typed_array
(
js
,
TYPED_ARRAY_UINT8
,
ab
,
0
,
d
->
body_size
,
"Uint8Array"
));
}}
rs_controller_close
(
js
,
ctrl
);
return
js_mkundef
();
}
REQ_GETTER_START
(
body
)
if
(
!
d
->
has_body
)
return
js_mknull
();
ant_value_t
stored_stream
=
js_get_slot
(
this
,
SLOT_REQUEST_BODY_STREAM
);
if
(
rs_is_stream
(
stored_stream
))
return
stored_stream
;
if
(
d
->
body_used
)
return
js_mknull
();
ant_value_t
pull
=
js_heavy_mkfun
(
js
,
req_body_pull
,
this
);
ant_value_t
stream
=
rs_create_stream
(
js
,
pull
,
js_mkundef
(),
1.0
);
if
(
!
is_err
(
stream
))
js_set_slot_wb
(
js
,
this
,
SLOT_REQUEST_BODY_STREAM
,
stream
);
return
stream
;
REQ_GETTER_END
REQ_GETTER_START
(
body_used
)
return
js_bool
(
d
->
body_used
);
REQ_GETTER_END
#undef REQ_GETTER_START
#undef REQ_GETTER_END
static
ant_value_t
js_request_clone
(
ant_t
*
js
,
ant_value_t
*
args
,
int
nargs
)
{
ant_value_t
this
=
js_getthis
(
js
);
request_data_t
*
d
=
get_data
(
this
);
if
(
!
d
)
return
js_mkerr_typed
(
js
,
JS_ERR_TYPE
,
"Invalid Request object"
);
if
(
d
->
body_used
)
return
js_mkerr_typed
(
js
,
JS_ERR_TYPE
,
"Cannot clone a Request whose body is unusable"
);
request_data_t
*
nd
=
data_dup
(
d
);
if
(
!
nd
)
return
js_mkerr
(
js
,
"out of memory"
);
ant_value_t
src_headers
=
js_get_slot
(
this
,
SLOT_REQUEST_HEADERS
);
ant_value_t
src_signal
=
request_get_signal
(
js
,
this
);
ant_value_t
new_headers
=
headers_create_empty
(
js
);
if
(
is_err
(
new_headers
))
{
data_free
(
nd
);
return
new_headers
;
}
headers_copy_from
(
js
,
new_headers
,
src_headers
);
headers_set_guard
(
new_headers
,
strcmp
(
nd
->
mode
,
"no-cors"
)
==
0
?
HEADERS_GUARD_REQUEST_NO_CORS
:
HEADERS_GUARD_REQUEST
);
headers_apply_guard
(
new_headers
);
ant_value_t
new_signal
=
abort_signal_create_dependent
(
js
,
src_signal
);
if
(
is_err
(
new_signal
))
{
data_free
(
nd
);
return
new_signal
;
}
ant_value_t
obj
=
js_mkobj
(
js
);
js_set_proto_init
(
obj
,
g_request_proto
);
js_set_slot
(
obj
,
SLOT_BRAND
,
js_mknum
(
BRAND_REQUEST
));
js_set_slot
(
obj
,
SLOT_DATA
,
ANT_PTR
(
nd
));
js_set_slot_wb
(
js
,
obj
,
SLOT_REQUEST_HEADERS
,
new_headers
);
js_set_slot
(
obj
,
SLOT_REQUEST_ABORT_REASON
,
js_mkundef
());
js_set_slot_wb
(
js
,
obj
,
SLOT_REQUEST_SIGNAL
,
new_signal
);
ant_value_t
src_stream
=
js_get_slot
(
this
,
SLOT_REQUEST_BODY_STREAM
);
if
(
rs_is_stream
(
src_stream
))
{
ant_value_t
branches
=
readable_stream_tee
(
js
,
src_stream
);
if
(
!
is_err
(
branches
)
&&
vtype
(
branches
)
==
T_ARR
)
{
ant_value_t
b1
=
js_arr_get
(
js
,
branches
,
0
);
ant_value_t
b2
=
js_arr_get
(
js
,
branches
,
1
);
js_set_slot_wb
(
js
,
this
,
SLOT_REQUEST_BODY_STREAM
,
b1
);
js_set_slot_wb
(
js
,
obj
,
SLOT_REQUEST_BODY_STREAM
,
b2
);
}}
return
obj
;
}
static
const
char
*
init_str
(
ant_t
*
js
,
ant_value_t
init
,
const
char
*
key
,
size_t
klen
,
ant_value_t
*
err_out
)
{
ant_value_t
v
=
js_get
(
js
,
init
,
key
);
if
(
vtype
(
v
)
==
T_UNDEF
)
return
NULL
;
if
(
vtype
(
v
)
!=
T_STR
)
{
v
=
js_tostring_val
(
js
,
v
);
if
(
is_err
(
v
))
{
*
err_out
=
v
;
return
NULL
;
}
}
return
js_getstr
(
js
,
v
,
NULL
);
}
static
ant_value_t
request_new_from_input
(
ant_t
*
js
,
ant_value_t
input
,
request_data_t
**
out_req
,
request_data_t
**
out_src
,
ant_value_t
*
out_input_signal
)
{
request_data_t
*
req
=
NULL
;
request_data_t
*
src
=
NULL
;
*
out_req
=
NULL
;
*
out_src
=
NULL
;
*
out_input_signal
=
js_mkundef
();
if
(
vtype
(
input
)
==
T_OBJ
&&
js_check_brand
(
input
,
BRAND_REQUEST
)
)
src
=
get_data
(
input
);
if
(
!
src
)
{
size_t
ulen
=
0
;
const
char
*
url_str
=
NULL
;
url_state_t
parsed
=
{
0
};
if
(
vtype
(
input
)
!=
T_STR
)
{
input
=
js_tostring_val
(
js
,
input
);
if
(
is_err
(
input
))
return
input
;
}
url_str
=
js_getstr
(
js
,
input
,
&
ulen
);
if
(
parse_url_to_state
(
url_str
,
NULL
,
&
parsed
)
!=
0
)
{
url_state_clear
(
&
parsed
);
return
js_mkerr_typed
(
js
,
JS_ERR_TYPE
,
"Failed to construct 'Request': Invalid URL"
);
}
if
((
parsed
.
username
&&
parsed
.
username
[
0
])
||
(
parsed
.
password
&&
parsed
.
password
[
0
]))
{
url_state_clear
(
&
parsed
);
return
js_mkerr_typed
(
js
,
JS_ERR_TYPE
,
"Failed to construct 'Request': URL includes credentials"
);
}
req
=
data_new
();
if
(
!
req
)
{
url_state_clear
(
&
parsed
);
return
js_mkerr
(
js
,
"out of memory"
);
}
req
->
url
=
parsed
;
}
else
{
req
=
data_dup
(
src
);
if
(
!
req
)
return
js_mkerr
(
js
,
"out of memory"
);
req
->
body_used
=
false
;
*
out_input_signal
=
request_get_signal
(
js
,
input
);
}
*
out_req
=
req
;
*
out_src
=
src
;
return
js_mkundef
();
}
static
ant_value_t
request_apply_init_options
(
ant_t
*
js
,
ant_value_t
init
,
request_data_t
*
req
,
ant_value_t
*
input_signal
)
{
ant_value_t
err
=
js_mkundef
();
ant_value_t
win
=
js_get
(
js
,
init
,
"window"
);
const
char
*
ref
=
NULL
;
const
char
*
rp
=
NULL
;
const
char
*
mode_val
=
NULL
;
const
char
*
cred
=
NULL
;
const
char
*
cache_val
=
NULL
;
const
char
*
redir
=
NULL
;
const
char
*
integ
=
NULL
;
const
char
*
method_val
=
NULL
;
if
(
vtype
(
win
)
!=
T_UNDEF
&&
vtype
(
win
)
!=
T_NULL
)
{
return
js_mkerr_typed
(
js
,
JS_ERR_TYPE
,
"Failed to construct 'Request': 'window' must be null"
);
}
if
(
strcmp
(
req
->
mode
,
"navigate"
)
==
0
)
{
free
(
req
->
mode
);
req
->
mode
=
strdup
(
"same-origin"
);
}
req
->
reload_navigation
=
false
;
req
->
history_navigation
=
false
;
free
(
req
->
referrer
);
req
->
referrer
=
strdup
(
"client"
);
free
(
req
->
referrer_policy
);
req
->
referrer_policy
=
strdup
(
""
);
ref
=
init_str
(
js
,
init
,
"referrer"
,
8
,
&
err
);
if
(
is_err
(
err
))
return
err
;
if
(
ref
)
{
if
(
ref
[
0
]
==
'\0'
)
{
free
(
req
->
referrer
);
req
->
referrer
=
strdup
(
"no-referrer"
);
}
else
{
url_state_t
rs
=
{
0
};
if
(
parse_url_to_state
(
ref
,
NULL
,
&
rs
)
!=
0
)
{
url_state_clear
(
&
rs
);
return
js_mkerr_typed
(
js
,
JS_ERR_TYPE
,
"Failed to construct 'Request': Invalid referrer URL"
);
}
free
(
req
->
referrer
);
req
->
referrer
=
build_href
(
&
rs
);
url_state_clear
(
&
rs
);
if
(
!
req
->
referrer
)
req
->
referrer
=
strdup
(
"client"
);
}}
rp
=
init_str
(
js
,
init
,
"referrerPolicy"
,
14
,
&
err
);
if
(
is_err
(
err
))
return
err
;
if
(
rp
)
{
free
(
req
->
referrer_policy
);
req
->
referrer_policy
=
strdup
(
rp
);
}
mode_val
=
init_str
(
js
,
init
,
"mode"
,
4
,
&
err
);
if
(
is_err
(
err
))
return
err
;
if
(
mode_val
)
{
if
(
strcmp
(
mode_val
,
"navigate"
)
==
0
)
{
return
js_mkerr_typed
(
js
,
JS_ERR_TYPE
,
"Failed to construct 'Request': mode 'navigate' is not allowed"
);
}
free
(
req
->
mode
);
req
->
mode
=
strdup
(
mode_val
);
}
cred
=
init_str
(
js
,
init
,
"credentials"
,
11
,
&
err
);
if
(
is_err
(
err
))
return
err
;
if
(
cred
)
{
free
(
req
->
credentials
);
req
->
credentials
=
strdup
(
cred
);
}
cache_val
=
init_str
(
js
,
init
,
"cache"
,
5
,
&
err
);
if
(
is_err
(
err
))
return
err
;
if
(
cache_val
)
{
free
(
req
->
cache
);
req
->
cache
=
strdup
(
cache_val
);
if
(
strcmp
(
req
->
cache
,
"only-if-cached"
)
==
0
&&
strcmp
(
req
->
mode
,
"same-origin"
)
!=
0
)
return
js_mkerr_typed
(
js
,
JS_ERR_TYPE
,
"Failed to construct 'Request': cache mode 'only-if-cached' requires mode 'same-origin'"
);
}
redir
=
init_str
(
js
,
init
,
"redirect"
,
8
,
&
err
);
if
(
is_err
(
err
))
return
err
;
if
(
redir
)
{
free
(
req
->
redirect
);
req
->
redirect
=
strdup
(
redir
);
}
integ
=
init_str
(
js
,
init
,
"integrity"
,
9
,
&
err
);
if
(
is_err
(
err
))
return
err
;
if
(
integ
)
{
free
(
req
->
integrity
);
req
->
integrity
=
strdup
(
integ
);
}
ant_value_t
ka
=
js_get
(
js
,
init
,
"keepalive"
);
if
(
vtype
(
ka
)
!=
T_UNDEF
)
req
->
keepalive
=
js_truthy
(
js
,
ka
);
method_val
=
init_str
(
js
,
init
,
"method"
,
6
,
&
err
);
if
(
is_err
(
err
))
return
err
;
if
(
method_val
)
{
if
(
!
is_valid_method
(
method_val
))
{
return
js_mkerr_typed
(
js
,
JS_ERR_TYPE
,
"Failed to construct 'Request': Invalid method"
);
}
if
(
is_forbidden_method
(
method_val
))
{
return
js_mkerr_typed
(
js
,
JS_ERR_TYPE
,
"Failed to construct 'Request': Forbidden method"
);
}
free
(
req
->
method
);
req
->
method
=
strdup
(
method_val
);
normalize_method
(
req
->
method
);
}
ant_value_t
sig_val
=
js_get
(
js
,
init
,
"signal"
);
if
(
vtype
(
sig_val
)
==
T_UNDEF
)
return
js_mkundef
();
if
(
vtype
(
sig_val
)
==
T_NULL
)
{
*
input_signal
=
js_mkundef
();
return
js_mkundef
();
}
if
(
abort_signal_is_signal
(
sig_val
))
{
*
input_signal
=
sig_val
;
return
js_mkundef
();
}
return
js_mkerr_typed
(
js
,
JS_ERR_TYPE
,
"Failed to construct 'Request': signal must be an AbortSignal"
);
}
static
ant_value_t
request_create_ctor_headers
(
ant_t
*
js
,
ant_value_t
input
)
{
ant_value_t
headers
=
headers_create_empty
(
js
);
if
(
is_err
(
headers
))
return
headers
;
if
(
vtype
(
input
)
!=
T_OBJ
)
return
headers
;
ant_value_t
src_hdrs
=
js_get_slot
(
input
,
SLOT_REQUEST_HEADERS
);
headers_copy_from
(
js
,
headers
,
src_hdrs
);
return
headers
;
}
static
ant_value_t
request_apply_init_headers
(
ant_t
*
js
,
ant_value_t
init
,
ant_value_t
headers
)
{
ant_value_t
init_headers
=
js_get
(
js
,
init
,
"headers"
);
if
(
vtype
(
init_headers
)
==
T_UNDEF
)
return
headers
;
return
headers_create_from_init
(
js
,
init_headers
);
}
static
ant_value_t
request_parse_duplex
(
ant_t
*
js
,
ant_value_t
init
,
bool
*
out_duplex_provided
)
{
ant_value_t
duplex_val
=
js_get
(
js
,
init
,
"duplex"
);
ant_value_t
duplex_str_v
=
duplex_val
;
const
char
*
duplex_str
=
NULL
;
*
out_duplex_provided
=
vtype
(
duplex_val
)
!=
T_UNDEF
;
if
(
!*
out_duplex_provided
)
return
js_mkundef
();
if
(
vtype
(
duplex_str_v
)
!=
T_STR
)
{
duplex_str_v
=
js_tostring_val
(
js
,
duplex_str_v
);
if
(
is_err
(
duplex_str_v
))
return
duplex_str_v
;
}
duplex_str
=
js_getstr
(
js
,
duplex_str_v
,
NULL
);
if
(
duplex_str
&&
strcmp
(
duplex_str
,
"half"
)
==
0
)
return
js_mkundef
();
return
js_mkerr_typed
(
js
,
JS_ERR_TYPE
,
"Failed to construct 'Request': duplex must be 'half'"
);
}
static
ant_value_t
request_apply_ctor_body
(
ant_t
*
js
,
ant_value_t
req_obj
,
ant_value_t
input
,
ant_value_t
init
,
bool
init_provided
,
bool
duplex_provided
,
request_data_t
*
req
,
request_data_t
*
src
,
ant_value_t
headers
)
{
if
(
init_provided
)
{
ant_value_t
body_val
=
js_get
(
js
,
init
,
"body"
);
bool
init_body_present
=
vtype
(
body_val
)
!=
T_UNDEF
;
bool
input_body_present
=
src
&&
src
->
has_body
;
bool
effective_body_present
=
(
init_body_present
&&
vtype
(
body_val
)
!=
T_NULL
)
||
(
input_body_present
&&
(
!
init_body_present
||
vtype
(
body_val
)
==
T_NULL
));
if
((
strcmp
(
req
->
method
,
"GET"
)
==
0
||
strcmp
(
req
->
method
,
"HEAD"
)
==
0
)
&&
effective_body_present
)
{
return
js_mkerr_typed
(
js
,
JS_ERR_TYPE
,
"Failed to construct 'Request': Request with GET/HEAD method cannot have body"
);
}
if
(
vtype
(
body_val
)
==
T_UNDEF
)
return
js_mkundef
();
if
(
vtype
(
body_val
)
==
T_NULL
)
{
request_clear_body
(
js
,
req_obj
,
req
);
return
js_mkundef
();
}
request_data_t
*
init_req
=
get_data
(
init
);
ant_value_t
body_err
=
js_mkundef
();
ant_value_t
body_stream
=
js_mkundef
();
uint8_t
*
bd
=
NULL
;
size_t
bs
=
0
;
char
*
bt
=
NULL
;
if
(
init_req
&&
!
init_req
->
body_used
&&
!
init_req
->
body_is_stream
&&
init_req
->
has_body
)
{
bd
=
malloc
(
init_req
->
body_size
);
if
(
init_req
->
body_size
>
0
&&
!
bd
)
return
js_mkerr
(
js
,
"out of memory"
);
if
(
init_req
->
body_size
>
0
)
memcpy
(
bd
,
init_req
->
body_data
,
init_req
->
body_size
);
bs
=
init_req
->
body_size
;
bt
=
init_req
->
body_type
?
strdup
(
init_req
->
body_type
)
:
NULL
;
}
else
if
(
!
extract_body
(
js
,
body_val
,
&
bd
,
&
bs
,
&
bt
,
&
body_stream
,
&
body_err
))
return
is_err
(
body_err
)
?
body_err
:
js_mkerr
(
js
,
"Failed to extract body"
);
return
request_set_extracted_body
(
js
,
req_obj
,
headers
,
req
,
bd
,
bs
,
bt
,
body_stream
,
duplex_provided
);
}
if
(
!
src
)
return
js_mkundef
();
return
request_copy_source_body
(
js
,
req_obj
,
input
,
req
,
src
);
}
static
ant_value_t
js_request_ctor
(
ant_t
*
js
,
ant_value_t
*
args
,
int
nargs
)
{
ant_value_t
init
=
(
nargs
>=
2
&&
vtype
(
args
[
1
])
!=
T_UNDEF
)
?
args
[
1
]
:
js_mkundef
();
if
(
vtype
(
js
->
new_target
)
==
T_UNDEF
)
return
js_mkerr_typed
(
js
,
JS_ERR_TYPE
,
"Request constructor requires 'new'"
);
if
(
nargs
<
1
)
return
js_mkerr_typed
(
js
,
JS_ERR_TYPE
,
"Request constructor requires at least 1 argument"
);
ant_value_t
input
=
args
[
0
];
ant_value_t
obj
=
0
;
ant_value_t
proto
=
0
;
bool
init_provided
=
false
;
request_data_t
*
req
=
NULL
;
request_data_t
*
src
=
NULL
;
ant_value_t
input_signal
=
js_mkundef
();
ant_value_t
step
=
js_mkundef
();
ant_value_t
signal
=
0
;
ant_value_t
headers
=
0
;
bool
duplex_provided
=
false
;
init_provided
=
(
vtype
(
init
)
==
T_OBJ
||
vtype
(
init
)
==
T_ARR
);
step
=
request_new_from_input
(
js
,
input
,
&
req
,
&
src
,
&
input_signal
);
if
(
is_err
(
step
))
return
step
;
if
(
init_provided
)
{
step
=
request_apply_init_options
(
js
,
init
,
req
,
&
input_signal
);
if
(
is_err
(
step
))
{
data_free
(
req
);
return
step
;
}
}
if
(
strcmp
(
req
->
mode
,
"no-cors"
)
==
0
&&
!
is_cors_safelisted_method
(
req
->
method
)
)
{
data_free
(
req
);
return
js_mkerr_typed
(
js
,
JS_ERR_TYPE
,
"Failed to construct 'Request': method must be one of GET, HEAD, POST for no-cors mode"
);
}
obj
=
js_mkobj
(
js
);
proto
=
js_instance_proto_from_new_target
(
js
,
g_request_proto
);
if
(
is_object_type
(
proto
))
js_set_proto_init
(
obj
,
proto
);
else
js_set_proto_init
(
obj
,
g_request_proto
);
js_set_slot
(
obj
,
SLOT_BRAND
,
js_mknum
(
BRAND_REQUEST
));
js_set_slot
(
obj
,
SLOT_DATA
,
ANT_PTR
(
req
));
js_set_slot
(
obj
,
SLOT_REQUEST_ABORT_REASON
,
js_mkundef
());
signal
=
abort_signal_create_dependent
(
js
,
input_signal
);
if
(
is_err
(
signal
))
{
data_free
(
req
);
return
signal
;
}
js_set_slot_wb
(
js
,
obj
,
SLOT_REQUEST_SIGNAL
,
signal
);
headers
=
request_create_ctor_headers
(
js
,
input
);
if
(
is_err
(
headers
))
{
data_free
(
req
);
return
headers
;
}
if
(
init_provided
)
{
headers
=
request_apply_init_headers
(
js
,
init
,
headers
);
if
(
is_err
(
headers
))
{
data_free
(
req
);
return
headers
;
}
}
headers_set_guard
(
headers
,
strcmp
(
req
->
mode
,
"no-cors"
)
==
0
?
HEADERS_GUARD_REQUEST_NO_CORS
:
HEADERS_GUARD_REQUEST
);
headers_apply_guard
(
headers
);
js_set_slot_wb
(
js
,
obj
,
SLOT_REQUEST_HEADERS
,
headers
);
if
(
init_provided
)
{
step
=
request_parse_duplex
(
js
,
init
,
&
duplex_provided
);
if
(
is_err
(
step
))
{
data_free
(
req
);
return
step
;
}
}
step
=
request_apply_ctor_body
(
js
,
obj
,
input
,
init
,
init_provided
,
duplex_provided
,
req
,
src
,
headers
);
if
(
is_err
(
step
))
{
data_free
(
req
);
return
step
;
}
if
(
src
&&
src
->
has_body
&&
!
src
->
body_used
)
src
->
body_used
=
true
;
return
obj
;
}
ant_value_t
request_create_from_input_init
(
ant_t
*
js
,
ant_value_t
input
,
ant_value_t
init
)
{
bool
init_provided
=
(
vtype
(
init
)
==
T_OBJ
||
vtype
(
init
)
==
T_ARR
);
request_data_t
*
req
=
NULL
;
request_data_t
*
src
=
NULL
;
ant_value_t
input_signal
=
js_mkundef
();
ant_value_t
step
=
js_mkundef
();
ant_value_t
obj
=
0
;
ant_value_t
signal
=
0
;
ant_value_t
headers
=
0
;
bool
duplex_provided
=
false
;
step
=
request_new_from_input
(
js
,
input
,
&
req
,
&
src
,
&
input_signal
);
if
(
is_err
(
step
))
return
step
;
if
(
init_provided
)
{
step
=
request_apply_init_options
(
js
,
init
,
req
,
&
input_signal
);
if
(
is_err
(
step
))
{
data_free
(
req
);
return
step
;
}
}
if
(
strcmp
(
req
->
mode
,
"no-cors"
)
==
0
&&
!
is_cors_safelisted_method
(
req
->
method
)
)
{
data_free
(
req
);
return
js_mkerr_typed
(
js
,
JS_ERR_TYPE
,
"Failed to construct 'Request': method must be one of GET, HEAD, POST for no-cors mode"
);
}
obj
=
js_mkobj
(
js
);
js_set_proto_init
(
obj
,
g_request_proto
);
js_set_slot
(
obj
,
SLOT_BRAND
,
js_mknum
(
BRAND_REQUEST
));
js_set_slot
(
obj
,
SLOT_DATA
,
ANT_PTR
(
req
));
js_set_slot
(
obj
,
SLOT_REQUEST_ABORT_REASON
,
js_mkundef
());
signal
=
abort_signal_create_dependent
(
js
,
input_signal
);
if
(
is_err
(
signal
))
{
data_free
(
req
);
return
signal
;
}
js_set_slot_wb
(
js
,
obj
,
SLOT_REQUEST_SIGNAL
,
signal
);
headers
=
request_create_ctor_headers
(
js
,
input
);
if
(
is_err
(
headers
))
{
data_free
(
req
);
return
headers
;
}
if
(
init_provided
)
{
headers
=
request_apply_init_headers
(
js
,
init
,
headers
);
if
(
is_err
(
headers
))
{
data_free
(
req
);
return
headers
;
}
}
headers_set_guard
(
headers
,
strcmp
(
req
->
mode
,
"no-cors"
)
==
0
?
HEADERS_GUARD_REQUEST_NO_CORS
:
HEADERS_GUARD_REQUEST
);
headers_apply_guard
(
headers
);
js_set_slot_wb
(
js
,
obj
,
SLOT_REQUEST_HEADERS
,
headers
);
if
(
init_provided
)
{
step
=
request_parse_duplex
(
js
,
init
,
&
duplex_provided
);
if
(
is_err
(
step
))
{
data_free
(
req
);
return
step
;
}
}
step
=
request_apply_ctor_body
(
js
,
obj
,
input
,
init
,
init_provided
,
duplex_provided
,
req
,
src
,
headers
);
if
(
is_err
(
step
))
{
data_free
(
req
);
return
step
;
}
if
(
src
&&
src
->
has_body
&&
!
src
->
body_used
)
src
->
body_used
=
true
;
return
obj
;
}
ant_value_t
request_create
(
ant_t
*
js
,
const
char
*
method
,
const
char
*
url
,
ant_value_t
headers_obj
,
const
uint8_t
*
body
,
size_t
body_len
,
const
char
*
body_type
)
{
request_data_t
*
req
=
data_new
();
if
(
!
req
)
return
js_mkerr
(
js
,
"out of memory"
);
free
(
req
->
method
);
req
->
method
=
strdup
(
method
?
method
:
"GET"
);
free
(
req
->
mode
);
req
->
mode
=
strdup
(
"same-origin"
);
url_state_t
parsed
=
{
0
};
if
(
url
&&
parse_url_to_state
(
url
,
NULL
,
&
parsed
)
==
0
)
req
->
url
=
parsed
;
else
url_state_clear
(
&
parsed
);
if
(
body
)
req
->
has_body
=
true
;
if
(
body
&&
body_len
>
0
)
{
req
->
body_data
=
malloc
(
body_len
);
if
(
!
req
->
body_data
)
{
data_free
(
req
);
return
js_mkerr
(
js
,
"out of memory"
);
}
memcpy
(
req
->
body_data
,
body
,
body_len
);
req
->
body_size
=
body_len
;
req
->
body_type
=
body_type
?
strdup
(
body_type
)
:
NULL
;
}
req
->
body_is_stream
=
false
;
return
request_create_object
(
js
,
req
,
headers_obj
,
true
);
}
ant_value_t
request_create_server
(
ant_t
*
js
,
const
char
*
method
,
const
char
*
target
,
bool
absolute_target
,
const
char
*
host
,
const
char
*
server_hostname
,
int
server_port
,
ant_value_t
headers_obj
,
const
uint8_t
*
body
,
size_t
body_len
,
const
char
*
body_type
)
{
request_data_t
*
req
=
data_new_server
(
method
);
if
(
!
req
)
return
js_mkerr
(
js
,
"out of memory"
);
if
(
target
&&
request_parse_server_url
(
target
,
absolute_target
,
host
,
server_hostname
,
server_port
,
&
req
->
url
)
!=
0
)
url_state_clear
(
&
req
->
url
);
if
(
body
)
req
->
has_body
=
true
;
if
(
body
&&
body_len
>
0
)
{
req
->
body_data
=
malloc
(
body_len
);
if
(
!
req
->
body_data
)
{
data_free
(
req
);
return
js_mkerr
(
js
,
"out of memory"
);
}
memcpy
(
req
->
body_data
,
body
,
body_len
);
req
->
body_size
=
body_len
;
req
->
body_type
=
body_type
?
strdup
(
body_type
)
:
NULL
;
}
req
->
body_is_stream
=
false
;
return
request_create_object
(
js
,
req
,
headers_obj
,
false
);
}
void
init_request_module
(
void
)
{
ant_t
*
js
=
rt
->
js
;
ant_value_t
g
=
js_glob
(
js
);
g_request_proto
=
js_mkobj
(
js
);
js_set
(
js
,
g_request_proto
,
"text"
,
js_mkfun
(
js_req_text
));
js_set
(
js
,
g_request_proto
,
"json"
,
js_mkfun
(
js_req_json
));
js_set
(
js
,
g_request_proto
,
"arrayBuffer"
,
js_mkfun
(
js_req_array_buffer
));
js_set
(
js
,
g_request_proto
,
"blob"
,
js_mkfun
(
js_req_blob
));
js_set
(
js
,
g_request_proto
,
"formData"
,
js_mkfun
(
js_req_form_data
));
js_set
(
js
,
g_request_proto
,
"bytes"
,
js_mkfun
(
js_req_bytes
));
js_set
(
js
,
g_request_proto
,
"clone"
,
js_mkfun
(
js_request_clone
));
#define GETTER(prop, fn) \
js_set_getter_desc(js, g_request_proto, prop, sizeof(prop)-1, js_mkfun(js_req_get_##fn), JS_DESC_C)
GETTER
(
"method"
,
method
);
GETTER
(
"url"
,
url
);
GETTER
(
"headers"
,
headers
);
GETTER
(
"destination"
,
destination
);
GETTER
(
"referrer"
,
referrer
);
GETTER
(
"referrerPolicy"
,
referrer_policy
);
GETTER
(
"mode"
,
mode
);
GETTER
(
"credentials"
,
credentials
);
GETTER
(
"cache"
,
cache
);
GETTER
(
"redirect"
,
redirect
);
GETTER
(
"integrity"
,
integrity
);
GETTER
(
"keepalive"
,
keepalive
);
GETTER
(
"isReloadNavigation"
,
is_reload_navigation
);
GETTER
(
"isHistoryNavigation"
,
is_history_navigation
);
GETTER
(
"signal"
,
signal
);
GETTER
(
"duplex"
,
duplex
);
GETTER
(
"body"
,
body
);
GETTER
(
"bodyUsed"
,
body_used
);
#undef GETTER
js_set_sym
(
js
,
g_request_proto
,
get_toStringTag_sym
(),
js_mkstr
(
js
,
"Request"
,
7
));
ant_value_t
ctor
=
js_make_ctor
(
js
,
js_request_ctor
,
g_request_proto
,
"Request"
,
7
);
js_set
(
js
,
g
,
"Request"
,
ctor
);
js_set_descriptor
(
js
,
g
,
"Request"
,
7
,
JS_DESC_W
|
JS_DESC_C
);
}
File Metadata
Details
Attached
Mime Type
text/x-c
Expires
Fri, Apr 3, 2:29 PM (2 d)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
521445
Default Alt Text
request.c (44 KB)
Attached To
Mode
rANT Ant
Attached
Detach File
Event Timeline
Log In to Comment