Explorar o código

Store the default port number in URLs.

Simon Sapin %!s(int64=12) %!d(string=hai) anos
pai
achega
43419c0b84
Modificáronse 4 ficheiros con 62 adicións e 27 borrados
  1. 29 0
      src/lib.rs
  2. 21 22
      src/parser.rs
  3. 3 1
      src/tests.rs
  4. 9 4
      src/urlutils.rs

+ 29 - 0
src/lib.rs

@@ -236,6 +236,10 @@ pub struct RelativeSchemeData {
     /// `None` for file-like schemes, or to indicate the default port number.
     pub port: Option<u16>,
 
+    /// The default port number for the URL’s scheme.
+    /// `None` for file-like schemes.
+    pub default_port: Option<u16>,
+
     /// The path of the URL, as vector of pecent-encoded strings.
     ///
     /// Percent encoded strings are within the ASCII range.
@@ -393,6 +397,16 @@ pub enum SchemeType {
     FileLikeRelativeScheme,
 }
 
+
+impl SchemeType {
+    pub fn default_port(&self) -> Option<u16> {
+        match self {
+            &RelativeScheme(default_port) => Some(default_port),
+            _ => None,
+        }
+    }
+}
+
 /// http://url.spec.whatwg.org/#relative-scheme
 pub fn whatwg_scheme_type_mapper(scheme: &str) -> SchemeType {
     match scheme {
@@ -458,6 +472,7 @@ impl Url {
                 username: "".to_string(),
                 password: None,
                 port: None,
+                default_port: None,
                 host: Domain("".to_string()),
                 path: path,
             }),
@@ -639,6 +654,13 @@ impl Url {
         self.relative_scheme_data_mut().map(|scheme_data| &mut scheme_data.port)
     }
 
+    /// If the URL is in a *relative scheme* that is not a file-like,
+    /// return its port number, even if it is the default.
+    #[inline]
+    pub fn port_or_default(&self) -> Option<u16> {
+        self.relative_scheme_data().and_then(|scheme_data| scheme_data.port_or_default())
+    }
+
     /// If the URL is in a *relative scheme*, return its path components.
     #[inline]
     pub fn path<'a>(&'a self) -> Option<&'a [String]> {
@@ -785,6 +807,13 @@ impl RelativeSchemeData {
         }
     }
 
+    /// Return the port number of the URL, even if it is the default.
+    /// Return `None` for file-like URLs.
+    #[inline]
+    pub fn port_or_default(&self) -> Option<u16> {
+        self.port.or(self.default_port)
+    }
+
     /// Serialize the path as a string.
     ///
     /// The returned string starts with a "/" slash, and components are separated by slashes.

+ 21 - 22
src/parser.rs

@@ -127,7 +127,7 @@ pub fn parse_url(input: &str, parser: &UrlParser) -> ParseResult<Url> {
                 // FIXME: Should not have to use a made-up base URL.
                 _ => parse_relative_url(remaining, scheme, scheme_type, &RelativeSchemeData {
                     username: String::new(), password: None, host: Domain(String::new()),
-                    port: None, path: Vec::new()
+                    port: None, default_port: None, path: Vec::new()
                 }, &None, parser)
             }
         },
@@ -182,11 +182,13 @@ fn parse_absolute_url<'a>(scheme: String, scheme_type: SchemeType,
     // Authority state
     let (username, password, remaining) = try!(parse_userinfo(remaining, parser));
     // Host state
-    let (host, port, remaining) = try!(parse_host(remaining, scheme_type, parser));
+    let (host, port, default_port, remaining) = try!(parse_host(remaining, scheme_type, parser));
     let (path, remaining) = try!(parse_path_start(
         remaining, UrlParserContext, scheme_type, parser));
     let scheme_data = RelativeSchemeData(RelativeSchemeData {
-        username: username, password: password, host: host, port: port, path: path });
+        username: username, password: password,
+        host: host, port: port, default_port: default_port,
+        path: path });
     let (query, fragment) = try!(parse_query_and_fragment(remaining, parser));
     Ok(Url { scheme: scheme, scheme_data: scheme_data, query: query, fragment: fragment })
 }
@@ -224,7 +226,7 @@ fn parse_relative_url<'a>(input: &'a str, scheme: String, scheme_type: SchemeTyp
                         remaining, UrlParserContext, scheme_type, parser));
                     let scheme_data = RelativeSchemeData(RelativeSchemeData {
                         username: String::new(), password: None,
-                        host: host, port: None, path: path
+                        host: host, port: None, default_port: None, path: path
                     });
                     let (query, fragment) = try!(parse_query_and_fragment(remaining, parser));
                     Ok(Url { scheme: scheme, scheme_data: scheme_data,
@@ -239,7 +241,7 @@ fn parse_relative_url<'a>(input: &'a str, scheme: String, scheme_type: SchemeTyp
                 let scheme_data = RelativeSchemeData(if scheme_type == FileLikeRelativeScheme {
                     RelativeSchemeData {
                         username: String::new(), password: None, host:
-                        Domain(String::new()), port: None, path: path
+                        Domain(String::new()), port: None, default_port: None, path: path
                     }
                 } else {
                     RelativeSchemeData {
@@ -247,6 +249,7 @@ fn parse_relative_url<'a>(input: &'a str, scheme: String, scheme_type: SchemeTyp
                         password: base.password.clone(),
                         host: base.host.clone(),
                         port: base.port.clone(),
+                        default_port: base.default_port.clone(),
                         path: path
                     }
                 });
@@ -281,6 +284,7 @@ fn parse_relative_url<'a>(input: &'a str, scheme: String, scheme_type: SchemeTyp
                     username: String::new(), password: None,
                     host: Domain(String::new()),
                     port: None,
+                    default_port: None,
                     path: path
                 }), remaining)
             } else {
@@ -293,6 +297,7 @@ fn parse_relative_url<'a>(input: &'a str, scheme: String, scheme_type: SchemeTyp
                     password: base.password.clone(),
                     host: base.host.clone(),
                     port: base.port.clone(),
+                    default_port: base.default_port.clone(),
                     path: path
                 }), remaining)
             };
@@ -372,14 +377,14 @@ fn parse_password(input: &str, parser: &UrlParser) -> ParseResult<String> {
 
 
 pub fn parse_host<'a>(input: &'a str, scheme_type: SchemeType, parser: &UrlParser)
-                          -> ParseResult<(Host, Option<u16>, &'a str)> {
+                          -> ParseResult<(Host, Option<u16>, Option<u16>, &'a str)> {
     let (host, remaining) = try!(parse_hostname(input, parser));
-    let (port, remaining) = if remaining.starts_with(":") {
+    let (port, default_port, remaining) = if remaining.starts_with(":") {
         try!(parse_port(remaining.slice_from(1), scheme_type, parser))
     } else {
-        (None, remaining)
+        (None, scheme_type.default_port(), remaining)
     };
-    Ok((host, port, remaining))
+    Ok((host, port, default_port, remaining))
 }
 
 
@@ -415,7 +420,7 @@ pub fn parse_hostname<'a>(input: &'a str, parser: &UrlParser)
 
 
 pub fn parse_port<'a>(input: &'a str, scheme_type: SchemeType, parser: &UrlParser)
-                      -> ParseResult<(Option<u16>, &'a str)> {
+                      -> ParseResult<(Option<u16>, Option<u16>, &'a str)> {
     let mut port = 0;
     let mut has_any_digit = false;
     let mut end = input.len();
@@ -436,18 +441,12 @@ pub fn parse_port<'a>(input: &'a str, scheme_type: SchemeType, parser: &UrlParse
             _ => return Err(InvalidPort)
         }
     }
-    let port = port as u16;
-    let port = if has_any_digit {
-        match scheme_type {
-            RelativeScheme(default_port) => {
-                if port == default_port { None } else { Some(port) }
-            }
-            _ => Some(port as u16),  // Can only happen when UrlUtils is misused
-        }
-    } else {
-        None
-    };
-    return Ok((port, input.slice_from(end)))
+    let default_port = scheme_type.default_port();
+    let mut port = Some(port as u16);
+    if !has_any_digit || port == default_port {
+        port = None;
+    }
+    return Ok((port, default_port, input.slice_from(end)))
 }
 
 

+ 3 - 1
src/tests.rs

@@ -69,7 +69,9 @@ fn url_parsing() {
 
         assert_eq!(Some(scheme), expected_scheme);
         match scheme_data {
-            RelativeSchemeData(RelativeSchemeData { username, password, host, port, path }) => {
+            RelativeSchemeData(RelativeSchemeData {
+                username, password, host, port, default_port: _, path,
+            }) => {
                 assert_eq!(username, expected_username);
                 assert_eq!(password, expected_password);
                 let host = host.serialize();

+ 9 - 4
src/urlutils.rs

@@ -78,12 +78,15 @@ impl<'a> UrlUtils for UrlUtilsWrapper<'a> {
     /// `URLUtils.host` setter
     fn set_host_and_port(&mut self, input: &str) -> ParseResult<()> {
         match self.url.scheme_data {
-            RelativeSchemeData(RelativeSchemeData { ref mut host, ref mut port, .. }) => {
+            RelativeSchemeData(RelativeSchemeData {
+                ref mut host, ref mut port, ref mut default_port, ..
+            }) => {
                 let scheme_type = self.parser.get_scheme_type(self.url.scheme.as_slice());
-                let (new_host, new_port, _) = try!(::parser::parse_host(
+                let (new_host, new_port, new_default_port, _) = try!(::parser::parse_host(
                     input, scheme_type, self.parser));
                 *host = new_host;
                 *port = new_port;
+                *default_port = new_default_port;
                 Ok(())
             },
             NonRelativeSchemeData(_) => Err(CannotSetNonRelativeScheme("host/port"))
@@ -105,13 +108,15 @@ impl<'a> UrlUtils for UrlUtilsWrapper<'a> {
     /// `URLUtils.port` setter
     fn set_port(&mut self, input: &str) -> ParseResult<()> {
         match self.url.scheme_data {
-            RelativeSchemeData(RelativeSchemeData { ref mut port, .. }) => {
+            RelativeSchemeData(RelativeSchemeData { ref mut port, ref mut default_port, .. }) => {
                 let scheme_type = self.parser.get_scheme_type(self.url.scheme.as_slice());
                 if scheme_type == FileLikeRelativeScheme {
                     return Err(CannotSetFileScheme("port"));
                 }
-                let (new_port, _) = try!(::parser::parse_port(input, scheme_type, self.parser));
+                let (new_port, new_default_port, _) = try!(::parser::parse_port(
+                    input, scheme_type, self.parser));
                 *port = new_port;
+                *default_port = new_default_port;
                 Ok(())
             },
             NonRelativeSchemeData(_) => Err(CannotSetNonRelativeScheme("port"))