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.