diff --git a/src/html5ever/url.rs b/src/html5ever/url.rs
index 6c2699e8..406ea829 100644
--- a/src/html5ever/url.rs
+++ b/src/html5ever/url.rs
@@ -272,6 +272,15 @@ pub unsafe extern "C" fn url_set_port(url: *mut Url, port: u16) -> i32 {
}
}
+#[no_mangle]
+pub unsafe extern "C" fn url_set_port_to_null(url: *mut Url) -> i32 {
+ let url = unsafe { &mut *url };
+ match url.set_port(None) {
+ Ok(()) => 0,
+ Err(()) => -1,
+ }
+}
+
#[no_mangle]
pub unsafe extern "C" fn url_get_port(url: *const Url) -> i32 {
let url = unsafe { &*url };
@@ -281,8 +290,9 @@ pub unsafe extern "C" fn url_get_port(url: *const Url) -> i32 {
}
}
+/// WHATWG `hostname` setter: sets the host without touching the port.
#[no_mangle]
-pub unsafe extern "C" fn url_set_host(url: *mut Url, ptr: *const c_uchar, len: usize) -> i32 {
+pub unsafe extern "C" fn url_set_hostname(url: *mut Url, ptr: *const c_uchar, len: usize) -> i32 {
let url = unsafe { &mut *url };
let slice = match str_from(ptr, len) {
@@ -296,8 +306,10 @@ pub unsafe extern "C" fn url_set_host(url: *mut Url, ptr: *const c_uchar, len: u
}
}
+/// WHATWG `hostname` getter: the host without the port. Borrows into the Url's
+/// buffer; returns -1 (and writes an empty slice) when there is no host.
#[no_mangle]
-pub unsafe extern "C" fn url_get_host(
+pub unsafe extern "C" fn url_get_hostname(
url: *const Url,
out_ptr: *mut *const c_uchar,
out_len: *mut usize,
@@ -317,6 +329,76 @@ pub unsafe extern "C" fn url_get_host(
}
}
+#[no_mangle]
+pub unsafe extern "C" fn url_get_host(
+ url: *const Url,
+ out_ptr: *mut *const c_uchar,
+ out_len: *mut usize,
+) -> i32 {
+ let url = unsafe { &*url };
+ if url.host_str().is_none() {
+ unsafe {
+ *out_len = 0;
+ }
+ return -1;
+ }
+
+ let host = &url[url::Position::BeforeHost..url::Position::AfterPort];
+ unsafe {
+ *out_ptr = host.as_ptr();
+ *out_len = host.len();
+ }
+ 0
+}
+
+/// rust-url's `set_host` discards any trailing `:port`, so we split it off
+/// ourselves (respecting IPv6 `[...]` literals) and apply the port separately.
+#[no_mangle]
+pub unsafe extern "C" fn url_set_host(url: *mut Url, ptr: *const c_uchar, len: usize) -> i32 {
+ let url = unsafe { &mut *url };
+ let input = match str_from(ptr, len) {
+ Some(s) => s,
+ None => return -1,
+ };
+
+ // Find the port separator ':', but only outside an IPv6 [...] literal.
+ let colon = if input.starts_with('[') {
+ input
+ .find(']')
+ .and_then(|b| input[b..].find(':').map(|i| b + i))
+ } else {
+ input.find(':')
+ };
+
+ let Some(i) = colon else {
+ // No port given: set the host only, leaving any existing port untouched.
+ return if url.set_host(Some(input)).is_ok() {
+ 0
+ } else {
+ -1
+ };
+ };
+
+ let (host, port_str) = (&input[..i], &input[i + 1..]);
+
+ // Validate the port up-front so we never apply the host and then fail.
+ let new_port: Option = if port_str.is_empty() {
+ None // "host:" clears the port
+ } else {
+ match port_str.parse::() {
+ Ok(p) => Some(p),
+ Err(_) => return -1,
+ }
+ };
+
+ if url.set_host(Some(host)).is_err() {
+ return -1;
+ }
+ // set_port only errors on cannot-be-a-base, already ruled out by set_host.
+ let _ = url.set_port(new_port);
+ 0
+}
+
#[no_mangle]
pub unsafe extern "C" fn url_set_scheme(url: *mut Url, ptr: *const c_uchar, len: usize) -> i32 {
let url = unsafe { &mut *url };
@@ -408,6 +490,12 @@ pub unsafe extern "C" fn url_set_fragment(url: *mut Url, ptr: *const c_uchar, le
0
}
+#[no_mangle]
+pub unsafe extern "C" fn url_set_fragment_to_null(url: *mut Url) {
+ let url = unsafe { &mut *url };
+ url.set_fragment(None);
+}
+
#[no_mangle]
pub unsafe extern "C" fn url_get_fragment(
url: *const Url,
diff --git a/src/sys/url.zig b/src/sys/url.zig
index e74a511d..851fa2a4 100644
--- a/src/sys/url.zig
+++ b/src/sys/url.zig
@@ -56,12 +56,27 @@ pub extern "c" fn url_free(url: *Url) void;
pub extern "c" fn url_can_parse(ptr: [*]const u8, len: usize) bool;
pub extern "c" fn url_can_parse_with_base(base_ptr: [*]const u8, base_len: usize, ptr: [*]const u8, len: usize) bool;
pub extern "c" fn url_to_string(url: *const Url, out_ptr: *[*]const u8, out_len: *usize) void;
+pub extern "c" fn url_set_hostname(url: *Url, ptr: [*]const u8, len: usize) i32;
+pub extern "c" fn url_get_hostname(url: *const Url, out_ptr: *[*]const u8, out_len: *usize) i32;
pub extern "c" fn url_set_host(url: *Url, ptr: [*]const u8, len: usize) i32;
-pub extern "c" fn url_get_host(url: *const Url, out_ptr: *[*]const u8, out_len: *usize) void;
+pub extern "c" fn url_get_host(url: *const Url, out_ptr: *[*]const u8, out_len: *usize) i32;
/// This function allocates on Rust-side.
pub extern "c" fn url_get_origin(url: *const Url) OwnedString;
pub extern "c" fn free_owned_string(owned: OwnedString) void;
pub extern "c" fn url_set_port(url: *Url, port: u16) i32;
+/// Sets the port to null.
+pub extern "c" fn url_set_port_to_null(url: *Url) i32;
+pub extern "c" fn url_set_username(url: *Url, ptr: [*]const u8, len: usize) i32;
+pub extern "c" fn url_get_username(url: *const Url, out_ptr: *[*]const u8, out_len: *usize) void;
+pub extern "c" fn url_set_password(url: *Url, ptr: [*]const u8, len: usize) i32;
+pub extern "c" fn url_get_password(url: *const Url, out_ptr: *[*]const u8, out_len: *usize) i32;
+pub extern "c" fn url_set_path(url: *Url, ptr: [*]const u8, len: usize) i32;
+pub extern "c" fn url_get_path(url: *const Url, out_ptr: *[*]const u8, out_len: *usize) void;
+pub extern "c" fn url_set_scheme(url: *Url, ptr: [*]const u8, len: usize) i32;
+pub extern "c" fn url_get_scheme(url: *const Url, out_ptr: *[*]const u8, out_len: *usize) void;
+pub extern "c" fn url_set_fragment(url: *Url, ptr: [*]const u8, len: usize) i32;
+pub extern "c" fn url_set_fragment_to_null(url: *Url) void;
+pub extern "c" fn url_get_fragment(url: *const Url, out_ptr: *[*]const u8, out_len: *usize) i32;
extern "c" fn url_get_port(url: *const Url) i32;
pub inline fn urlGetPort(url: *const Url) ?u16 {