From 7d5553238e6725e2e25665259148cb9b7ff6fb63 Mon Sep 17 00:00:00 2001 From: Halil Durak Date: Mon, 22 Jun 2026 13:09:23 +0300 Subject: [PATCH] `URL`: changes on host(name) setter, introduce `clean_hostname_input` --- src/html5ever/url.rs | 45 +++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 42 insertions(+), 3 deletions(-) diff --git a/src/html5ever/url.rs b/src/html5ever/url.rs index b587efb98..2d5f60e07 100644 --- a/src/html5ever/url.rs +++ b/src/html5ever/url.rs @@ -335,17 +335,40 @@ pub unsafe extern "C" fn url_get_port(url: *const Url) -> i32 { } } +fn clean_hostname_input(url: &Url, raw: &str) -> String { + let special = matches!( + url.scheme(), + "http" | "https" | "ws" | "wss" | "ftp" | "file" + ); + let mut out = String::with_capacity(raw.len()); + let mut in_brackets = false; + for c in raw.chars() { + match c { + '\t' | '\n' | '\r' => continue, + '[' => in_brackets = true, + ']' => in_brackets = false, + '/' | '?' | '#' => break, + '\\' if special => break, + ':' if !in_brackets => break, + _ => {} + } + out.push(c); + } + out +} + /// WHATWG `hostname` setter: sets the host without touching the port. #[no_mangle] 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) { + let raw = match str_from(ptr, len) { Some(s) => s, None => return -1, }; + let host = clean_hostname_input(url, raw); - match url.set_host(Some(slice)) { + match url.set_host(Some(host.as_str())) { Ok(()) => 0, Err(_) => -1, } @@ -406,6 +429,18 @@ pub unsafe extern "C" fn url_set_host(url: *mut Url, ptr: *const c_uchar, len: u None => return -1, }; + // The WHATWG host setter parses in "host state", which stops at the first + // '/', '?', '#' (and '\' for special schemes); the remainder is ignored + // rather than making the whole value invalid. + let special = matches!( + url.scheme(), + "http" | "https" | "ws" | "wss" | "ftp" | "file" + ); + let end = input + .find(|c| c == '/' || c == '?' || c == '#' || (special && c == '\\')) + .unwrap_or(input.len()); + let input = &input[..end]; + // Find the port separator ':', but only outside an IPv6 [...] literal. let colon = if input.starts_with('[') { input @@ -430,7 +465,11 @@ pub unsafe extern "C" fn url_set_host(url: *mut Url, ptr: *const c_uchar, len: u // the WHATWG host setter ignores: set the host only, leaving any existing // port untouched (matches Chrome and Firefox). if port_str.is_empty() { - return if url.set_host(Some(host)).is_ok() { 0 } else { -1 }; + return if url.set_host(Some(host)).is_ok() { + 0 + } else { + -1 + }; } // Validate the port up-front so we never apply the host and then fail.