Skip to content

Commit 1099d8b

Browse files
committed
Host handling.
1 parent 5e3640e commit 1099d8b

File tree

3 files changed

+95
-27
lines changed

3 files changed

+95
-27
lines changed

src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -692,7 +692,7 @@ impl Url {
692692
/// ```
693693
#[inline]
694694
pub fn cannot_be_a_base(&self) -> bool {
695-
!self.slice(self.path_start..).starts_with('/')
695+
!self.slice(self.scheme_end + 1..).starts_with('/')
696696
}
697697

698698
/// Return the username for this URL (typically the empty string)

src/parser.rs

Lines changed: 46 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -201,10 +201,16 @@ impl<'i> Input<'i> {
201201
Input::with_log(input, None)
202202
}
203203

204+
pub fn no_trim(input: &'i str) -> Self {
205+
Input {
206+
chars: input.chars(),
207+
}
208+
}
209+
204210
pub fn with_log(original_input: &'i str, vfn: Option<&dyn Fn(SyntaxViolation)>) -> Self {
205211
let input = original_input.trim_matches(c0_control_or_space);
206212
if let Some(vfn) = vfn {
207-
if input.len() < original_input.len() {
213+
if input.len() != original_input.len() {
208214
vfn(SyntaxViolation::C0SpaceIgnored)
209215
}
210216
if input.chars().any(|c| matches!(c, '\t' | '\n' | '\r')) {
@@ -912,6 +918,14 @@ impl<'a> Parser<'a> {
912918
let (host, remaining) = Parser::parse_host(input, scheme_type)?;
913919
write!(&mut self.serialization, "{}", host).unwrap();
914920
let host_end = to_u32(self.serialization.len())?;
921+
if remaining.starts_with(":") {
922+
// Port with an empty host
923+
if let Host::Domain(h) = &host {
924+
if h.is_empty() {
925+
return Err(ParseError::EmptyHost);
926+
}
927+
}
928+
}
915929
let (port, remaining) = if let Some(remaining) = remaining.split_prefix(':') {
916930
let scheme = || default_port(&self.serialization[..scheme_end as usize]);
917931
Parser::parse_port(remaining, scheme, self.context)?
@@ -978,10 +992,32 @@ impl<'a> Parser<'a> {
978992
Ok((host, input))
979993
}
980994

981-
pub(crate) fn parse_file_host<'i>(
995+
pub fn get_file_host<'i>(input: Input<'i>) -> ParseResult<(Host<String>, Input)> {
996+
let (_, host_str, remaining) = Parser::file_host(input)?;
997+
let host = Host::parse(&host_str)?;
998+
Ok((host, remaining))
999+
}
1000+
1001+
fn parse_file_host<'i>(
9821002
&mut self,
9831003
input: Input<'i>,
9841004
) -> ParseResult<(bool, HostInternal, Input<'i>)> {
1005+
let (has_host, host_str, remaining) = Parser::file_host(input)?;
1006+
let host = if host_str.is_empty() {
1007+
HostInternal::None
1008+
} else {
1009+
match Host::parse(&host_str)? {
1010+
Host::Domain(ref d) if d == "localhost" => HostInternal::None,
1011+
host => {
1012+
write!(&mut self.serialization, "{}", host).unwrap();
1013+
host.into()
1014+
}
1015+
}
1016+
};
1017+
Ok((has_host, host, remaining))
1018+
}
1019+
1020+
pub fn file_host<'i>(input: Input<'i>) -> ParseResult<(bool, String, Input<'i>)> {
9851021
// Undo the Input abstraction here to avoid allocating in the common case
9861022
// where the host part of the input does not contain any tab or newline
9871023
let input_str = input.chars.as_str();
@@ -1010,20 +1046,9 @@ impl<'a> Parser<'a> {
10101046
}
10111047
}
10121048
if is_windows_drive_letter(host_str) {
1013-
return Ok((false, HostInternal::None, input));
1049+
return Ok((false, "".to_string(), input));
10141050
}
1015-
let host = if host_str.is_empty() {
1016-
HostInternal::None
1017-
} else {
1018-
match Host::parse(host_str)? {
1019-
Host::Domain(ref d) if d == "localhost" => HostInternal::None,
1020-
host => {
1021-
write!(&mut self.serialization, "{}", host).unwrap();
1022-
host.into()
1023-
}
1024-
}
1025-
};
1026-
Ok((true, host, remaining))
1051+
Ok((true, host_str.to_string(), remaining))
10271052
}
10281053

10291054
pub fn parse_port<P>(
@@ -1448,6 +1473,12 @@ fn c0_control_or_space(ch: char) -> bool {
14481473
ch <= ' ' // U+0000 to U+0020
14491474
}
14501475

1476+
/// https://infra.spec.whatwg.org/#ascii-tab-or-newline
1477+
#[inline]
1478+
fn ascii_tab_or_new_line(ch: char) -> bool {
1479+
matches!(ch, '\t' | '\r' | '\n')
1480+
}
1481+
14511482
/// https://url.spec.whatwg.org/#ascii-alpha
14521483
#[inline]
14531484
pub fn ascii_alpha(ch: char) -> bool {

src/quirks.rs

Lines changed: 48 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@
1212
//! you probably want to use `Url` method instead.
1313
1414
use parser::{default_port, Context, Input, Parser, SchemeType};
15+
use std::cell::RefCell;
16+
use SyntaxViolation;
1517
use {idna, Host, ParseError, Position, Url};
1618

1719
/// https://url.spec.whatwg.org/#dom-url-domaintoascii
@@ -99,11 +101,23 @@ pub fn host(url: &Url) -> &str {
99101

100102
/// Setter for https://url.spec.whatwg.org/#dom-url-host
101103
pub fn set_host(url: &mut Url, new_host: &str) -> Result<(), ()> {
104+
// If context object’s url’s cannot-be-a-base-URL flag is set, then return.
105+
if url.cannot_be_a_base() {
106+
return Err(());
107+
}
108+
// Host parsing rules are strict,
109+
// We don't want to trim the input
110+
let input = Input::no_trim(new_host);
102111
let host;
103112
let opt_port;
104113
{
105114
let scheme = url.scheme();
106-
let result = Parser::parse_host(Input::new(new_host), SchemeType::from(scheme));
115+
let scheme_type = SchemeType::from(scheme);
116+
let result = if scheme_type == SchemeType::File {
117+
Parser::get_file_host(input)
118+
} else {
119+
Parser::parse_host(input, scheme_type)
120+
};
107121
match result {
108122
Ok((h, remaining)) => {
109123
host = h;
@@ -151,8 +165,30 @@ pub fn set_hostname(url: &mut Url, new_hostname: &str) -> Result<(), ()> {
151165
if url.cannot_be_a_base() {
152166
return Err(());
153167
}
154-
let result = Parser::parse_host(Input::new(new_hostname), SchemeType::from(url.scheme()));
168+
// Host parsing rules are strict,
169+
// We don't want to trim the input
170+
let input = Input::no_trim(new_hostname);
171+
let scheme_type = SchemeType::from(url.scheme());
172+
let result = if scheme_type == SchemeType::File {
173+
Parser::get_file_host(input)
174+
} else {
175+
Parser::parse_host(input, scheme_type)
176+
};
155177
if let Ok((host, _remaining)) = result {
178+
if let Host::Domain(h) = &host {
179+
if h.is_empty() {
180+
// Empty host on special url
181+
if SchemeType::from(url.scheme()).is_special()
182+
// Port with an empty host
183+
||!port(&url).is_empty()
184+
// Empty host with includes credentials
185+
|| !url.username().is_empty()
186+
|| !url.password().unwrap_or(&"").is_empty()
187+
{
188+
return Err(());
189+
}
190+
}
191+
}
156192
url.set_host_internal(host, None);
157193
Ok(())
158194
} else {
@@ -197,18 +233,19 @@ pub fn pathname(url: &Url) -> &str {
197233

198234
/// Setter for https://url.spec.whatwg.org/#dom-url-pathname
199235
pub fn set_pathname(url: &mut Url, new_pathname: &str) {
200-
if !url.cannot_be_a_base() && !new_pathname.is_empty() {
201-
if !SchemeType::from(url.scheme()).is_special()
236+
if url.cannot_be_a_base() {
237+
return;
238+
}
239+
if !SchemeType::from(url.scheme()).is_special()
202240
|| Some('/') == new_pathname.chars().nth(0)
203241
// \\ is a segment delimiter for 'special' URLs"
204242
|| Some('\\') == new_pathname.chars().nth(0)
205-
{
206-
url.set_path(new_pathname)
207-
} else {
208-
let mut path_to_set = String::from("/");
209-
path_to_set.push_str(new_pathname);
210-
url.set_path(&path_to_set)
211-
}
243+
{
244+
url.set_path(new_pathname)
245+
} else {
246+
let mut path_to_set = String::from("/");
247+
path_to_set.push_str(new_pathname);
248+
url.set_path(&path_to_set)
212249
}
213250
}
214251

0 commit comments

Comments
 (0)