]>
Commit | Line | Data |
---|---|---|
abe05a73 XL |
1 | // Copyright 2016 The rust-url developers. |
2 | // | |
3 | // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or | |
4 | // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license | |
5 | // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your | |
6 | // option. This file may not be copied, modified, or distributed | |
7 | // except according to those terms. | |
8 | ||
9 | //! Getters and setters for URL components implemented per https://url.spec.whatwg.org/#api | |
10 | //! | |
11 | //! Unless you need to be interoperable with web browsers, | |
12 | //! you probably want to use `Url` method instead. | |
13 | ||
e74abb32 XL |
14 | use parser::{default_port, Context, Input, Parser, SchemeType}; |
15 | use {idna, Host, ParseError, Position, Url}; | |
abe05a73 XL |
16 | |
17 | /// https://url.spec.whatwg.org/#dom-url-domaintoascii | |
18 | pub fn domain_to_ascii(domain: &str) -> String { | |
19 | match Host::parse(domain) { | |
20 | Ok(Host::Domain(domain)) => domain, | |
21 | _ => String::new(), | |
22 | } | |
23 | } | |
24 | ||
25 | /// https://url.spec.whatwg.org/#dom-url-domaintounicode | |
26 | pub fn domain_to_unicode(domain: &str) -> String { | |
27 | match Host::parse(domain) { | |
28 | Ok(Host::Domain(ref domain)) => { | |
29 | let (unicode, _errors) = idna::domain_to_unicode(domain); | |
30 | unicode | |
31 | } | |
32 | _ => String::new(), | |
33 | } | |
34 | } | |
35 | ||
36 | /// Getter for https://url.spec.whatwg.org/#dom-url-href | |
37 | pub fn href(url: &Url) -> &str { | |
38 | url.as_str() | |
39 | } | |
40 | ||
41 | /// Setter for https://url.spec.whatwg.org/#dom-url-href | |
42 | pub fn set_href(url: &mut Url, value: &str) -> Result<(), ParseError> { | |
43 | *url = Url::parse(value)?; | |
44 | Ok(()) | |
45 | } | |
46 | ||
47 | /// Getter for https://url.spec.whatwg.org/#dom-url-origin | |
48 | pub fn origin(url: &Url) -> String { | |
49 | url.origin().ascii_serialization() | |
50 | } | |
51 | ||
52 | /// Getter for https://url.spec.whatwg.org/#dom-url-protocol | |
53 | #[inline] | |
54 | pub fn protocol(url: &Url) -> &str { | |
55 | &url.as_str()[..url.scheme().len() + ":".len()] | |
56 | } | |
57 | ||
58 | /// Setter for https://url.spec.whatwg.org/#dom-url-protocol | |
59 | pub fn set_protocol(url: &mut Url, mut new_protocol: &str) -> Result<(), ()> { | |
60 | // The scheme state in the spec ignores everything after the first `:`, | |
61 | // but `set_scheme` errors if there is more. | |
62 | if let Some(position) = new_protocol.find(':') { | |
63 | new_protocol = &new_protocol[..position]; | |
64 | } | |
65 | url.set_scheme(new_protocol) | |
66 | } | |
67 | ||
68 | /// Getter for https://url.spec.whatwg.org/#dom-url-username | |
69 | #[inline] | |
70 | pub fn username(url: &Url) -> &str { | |
71 | url.username() | |
72 | } | |
73 | ||
74 | /// Setter for https://url.spec.whatwg.org/#dom-url-username | |
75 | pub fn set_username(url: &mut Url, new_username: &str) -> Result<(), ()> { | |
76 | url.set_username(new_username) | |
77 | } | |
78 | ||
79 | /// Getter for https://url.spec.whatwg.org/#dom-url-password | |
80 | #[inline] | |
81 | pub fn password(url: &Url) -> &str { | |
82 | url.password().unwrap_or("") | |
83 | } | |
84 | ||
85 | /// Setter for https://url.spec.whatwg.org/#dom-url-password | |
86 | pub fn set_password(url: &mut Url, new_password: &str) -> Result<(), ()> { | |
e74abb32 XL |
87 | url.set_password(if new_password.is_empty() { |
88 | None | |
89 | } else { | |
90 | Some(new_password) | |
91 | }) | |
abe05a73 XL |
92 | } |
93 | ||
94 | /// Getter for https://url.spec.whatwg.org/#dom-url-host | |
95 | #[inline] | |
96 | pub fn host(url: &Url) -> &str { | |
97 | &url[Position::BeforeHost..Position::AfterPort] | |
98 | } | |
99 | ||
100 | /// Setter for https://url.spec.whatwg.org/#dom-url-host | |
101 | pub fn set_host(url: &mut Url, new_host: &str) -> Result<(), ()> { | |
72b1a166 | 102 | // If context object’s url’s cannot-be-a-base-URL flag is set, then return. |
abe05a73 | 103 | if url.cannot_be_a_base() { |
e74abb32 | 104 | return Err(()); |
abe05a73 | 105 | } |
72b1a166 FG |
106 | // Host parsing rules are strict, |
107 | // We don't want to trim the input | |
108 | let input = Input::no_trim(new_host); | |
abe05a73 XL |
109 | let host; |
110 | let opt_port; | |
111 | { | |
112 | let scheme = url.scheme(); | |
72b1a166 FG |
113 | let scheme_type = SchemeType::from(scheme); |
114 | if let Ok((h, remaining)) = Parser::parse_host(input, scheme_type) { | |
115 | host = h; | |
116 | opt_port = if let Some(remaining) = remaining.split_prefix(':') { | |
117 | if remaining.is_empty() { | |
118 | None | |
119 | } else { | |
abe05a73 | 120 | Parser::parse_port(remaining, || default_port(scheme), Context::Setter) |
e74abb32 XL |
121 | .ok() |
122 | .map(|(port, _remaining)| port) | |
72b1a166 FG |
123 | } |
124 | } else { | |
125 | None | |
126 | }; | |
127 | } else { | |
128 | return Err(()); | |
129 | } | |
130 | } | |
131 | // Make sure we won't set an empty host to a url with a username or a port | |
132 | if host == Host::Domain("".to_string()) { | |
133 | if !username(&url).is_empty() { | |
134 | return Err(()); | |
135 | } | |
136 | if let Some(p) = opt_port { | |
137 | if let Some(_) = p { | |
138 | return Err(()); | |
abe05a73 | 139 | } |
72b1a166 FG |
140 | } |
141 | if url.port().is_some() { | |
142 | return Err(()); | |
abe05a73 XL |
143 | } |
144 | } | |
145 | url.set_host_internal(host, opt_port); | |
146 | Ok(()) | |
147 | } | |
148 | ||
149 | /// Getter for https://url.spec.whatwg.org/#dom-url-hostname | |
150 | #[inline] | |
151 | pub fn hostname(url: &Url) -> &str { | |
152 | url.host_str().unwrap_or("") | |
153 | } | |
154 | ||
155 | /// Setter for https://url.spec.whatwg.org/#dom-url-hostname | |
156 | pub fn set_hostname(url: &mut Url, new_hostname: &str) -> Result<(), ()> { | |
157 | if url.cannot_be_a_base() { | |
e74abb32 | 158 | return Err(()); |
abe05a73 | 159 | } |
72b1a166 FG |
160 | // Host parsing rules are strict we don't want to trim the input |
161 | let input = Input::no_trim(new_hostname); | |
162 | let scheme_type = SchemeType::from(url.scheme()); | |
163 | if let Ok((host, _remaining)) = Parser::parse_host(input, scheme_type) { | |
164 | if let Host::Domain(h) = &host { | |
165 | if h.is_empty() { | |
166 | // Empty host on special not file url | |
167 | if SchemeType::from(url.scheme()) == SchemeType::SpecialNotFile | |
168 | // Port with an empty host | |
169 | ||!port(&url).is_empty() | |
170 | // Empty host that includes credentials | |
171 | || !url.username().is_empty() | |
172 | || !url.password().unwrap_or(&"").is_empty() | |
173 | { | |
174 | return Err(()); | |
175 | } | |
176 | } | |
177 | } | |
abe05a73 XL |
178 | url.set_host_internal(host, None); |
179 | Ok(()) | |
180 | } else { | |
181 | Err(()) | |
182 | } | |
183 | } | |
184 | ||
185 | /// Getter for https://url.spec.whatwg.org/#dom-url-port | |
186 | #[inline] | |
187 | pub fn port(url: &Url) -> &str { | |
188 | &url[Position::BeforePort..Position::AfterPort] | |
189 | } | |
190 | ||
191 | /// Setter for https://url.spec.whatwg.org/#dom-url-port | |
192 | pub fn set_port(url: &mut Url, new_port: &str) -> Result<(), ()> { | |
193 | let result; | |
194 | { | |
195 | // has_host implies !cannot_be_a_base | |
196 | let scheme = url.scheme(); | |
2c00a5a8 | 197 | if !url.has_host() || url.host() == Some(Host::Domain("")) || scheme == "file" { |
e74abb32 | 198 | return Err(()); |
abe05a73 | 199 | } |
e74abb32 XL |
200 | result = Parser::parse_port( |
201 | Input::new(new_port), | |
202 | || default_port(scheme), | |
203 | Context::Setter, | |
204 | ) | |
abe05a73 XL |
205 | } |
206 | if let Ok((new_port, _remaining)) = result { | |
207 | url.set_port_internal(new_port); | |
208 | Ok(()) | |
209 | } else { | |
210 | Err(()) | |
211 | } | |
212 | } | |
213 | ||
214 | /// Getter for https://url.spec.whatwg.org/#dom-url-pathname | |
215 | #[inline] | |
216 | pub fn pathname(url: &Url) -> &str { | |
e74abb32 | 217 | url.path() |
abe05a73 XL |
218 | } |
219 | ||
220 | /// Setter for https://url.spec.whatwg.org/#dom-url-pathname | |
221 | pub fn set_pathname(url: &mut Url, new_pathname: &str) { | |
72b1a166 FG |
222 | if url.cannot_be_a_base() { |
223 | return; | |
224 | } | |
225 | if Some('/') == new_pathname.chars().nth(0) | |
226 | || (SchemeType::from(url.scheme()).is_special() | |
227 | // \ is a segment delimiter for 'special' URLs" | |
228 | && Some('\\') == new_pathname.chars().nth(0)) | |
229 | { | |
abe05a73 | 230 | url.set_path(new_pathname) |
72b1a166 FG |
231 | } else { |
232 | let mut path_to_set = String::from("/"); | |
233 | path_to_set.push_str(new_pathname); | |
234 | url.set_path(&path_to_set) | |
abe05a73 XL |
235 | } |
236 | } | |
237 | ||
238 | /// Getter for https://url.spec.whatwg.org/#dom-url-search | |
239 | pub fn search(url: &Url) -> &str { | |
240 | trim(&url[Position::AfterPath..Position::AfterQuery]) | |
241 | } | |
242 | ||
243 | /// Setter for https://url.spec.whatwg.org/#dom-url-search | |
244 | pub fn set_search(url: &mut Url, new_search: &str) { | |
245 | url.set_query(match new_search { | |
246 | "" => None, | |
247 | _ if new_search.starts_with('?') => Some(&new_search[1..]), | |
248 | _ => Some(new_search), | |
249 | }) | |
250 | } | |
251 | ||
252 | /// Getter for https://url.spec.whatwg.org/#dom-url-hash | |
253 | pub fn hash(url: &Url) -> &str { | |
254 | trim(&url[Position::AfterQuery..]) | |
255 | } | |
256 | ||
257 | /// Setter for https://url.spec.whatwg.org/#dom-url-hash | |
258 | pub fn set_hash(url: &mut Url, new_hash: &str) { | |
72b1a166 FG |
259 | url.set_fragment(match new_hash { |
260 | // If the given value is the empty string, | |
261 | // then set context object’s url’s fragment to null and return. | |
262 | "" => None, | |
263 | // Let input be the given value with a single leading U+0023 (#) removed, if any. | |
264 | _ if new_hash.starts_with('#') => Some(&new_hash[1..]), | |
265 | _ => Some(new_hash), | |
266 | }) | |
abe05a73 XL |
267 | } |
268 | ||
269 | fn trim(s: &str) -> &str { | |
270 | if s.len() == 1 { | |
271 | "" | |
272 | } else { | |
273 | s | |
274 | } | |
275 | } |