From 9854a591efa5a35e56a37fde226041fd570d515a Mon Sep 17 00:00:00 2001 From: Scallop Ye Date: Thu, 11 Apr 2024 20:03:12 +0800 Subject: [PATCH] Fix test fails and make tests available for all feature combinations --- .github/workflows/ci.yml | 12 +++---- src/builder/mod.rs | 8 ++--- src/normalizer.rs | 70 ++++++++++++++++++++++++++++++++++++++-- src/parser.rs | 7 +++- tests/normalize.rs | 20 ++++++++++-- tests/parse.rs | 9 +++++- tests/parse_ip.rs | 4 ++- tests/to_socket_addrs.rs | 2 ++ 8 files changed, 115 insertions(+), 17 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index e97e3404..48bb039f 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -16,9 +16,9 @@ jobs: uses: dtolnay/rust-toolchain@nightly - name: Test with default features run: cargo test - - name: Build with no features - run: cargo build --no-default-features - - name: Build with feature std - run: cargo build --no-default-features -F std - - name: Build with feature net - run: cargo build --no-default-features -F net + - name: Test with no features + run: cargo test --tests --no-default-features + - name: Test with feature std + run: cargo test --tests --no-default-features -F std + - name: Test with feature net + run: cargo test --tests --no-default-features -F net diff --git a/src/builder/mod.rs b/src/builder/mod.rs index 9ccad09a..cb1cf04e 100644 --- a/src/builder/mod.rs +++ b/src/builder/mod.rs @@ -8,7 +8,7 @@ use crate::{ encoder::{Fragment, Path, Query, Userinfo}, EStr, }, - internal::{AuthMeta, HostMeta, Meta}, + internal::{AuthMeta, Meta}, parser, Uri, }; use alloc::string::String; @@ -118,15 +118,15 @@ impl BuilderInner { #[cfg(feature = "net")] Host::Ipv4(addr) => { write!(self.buf, "{addr}").unwrap(); - auth_meta.host_meta = HostMeta::Ipv4(addr); + auth_meta.host_meta = crate::internal::HostMeta::Ipv4(addr); } #[cfg(feature = "net")] Host::Ipv6(addr) => { write!(self.buf, "[{addr}]").unwrap(); - auth_meta.host_meta = HostMeta::Ipv6(addr); + auth_meta.host_meta = crate::internal::HostMeta::Ipv6(addr); } Host::RegName(name) => { - auth_meta.host_meta = parser::reparse_reg_name(name.as_str().as_bytes()); + auth_meta.host_meta = parser::parse_v4_or_reg_name(name.as_str().as_bytes()); self.buf.push_str(name.as_str()); } _ => unreachable!(), diff --git a/src/normalizer.rs b/src/normalizer.rs index 3bd9c062..d2d54191 100644 --- a/src/normalizer.rs +++ b/src/normalizer.rs @@ -41,8 +41,15 @@ pub(crate) fn normalize(u: Uri<&str>) -> Uri { auth_meta.host_bounds.0 = buf.len() as _; match auth_meta.host_meta { // An IPv4 address is always canonical. - HostMeta::Ipv4(_) => buf.push_str(auth.host()), + HostMeta::Ipv4(..) => buf.push_str(auth.host()), + #[cfg(feature = "net")] HostMeta::Ipv6(addr) => write!(buf, "[{addr}]").unwrap(), + #[cfg(not(feature = "net"))] + HostMeta::Ipv6(..) => { + buf.push('['); + write_v6(&mut buf, parser::parse_v6(&auth.host().as_bytes()[1..])); + buf.push(']'); + } HostMeta::IpvFuture => { let start = buf.len(); buf.push_str(auth.host()); @@ -56,7 +63,7 @@ pub(crate) fn normalize(u: Uri<&str>) -> Uri { if buf.len() < start + host.len() { // Only reparse when the length is less than before. - auth_meta.host_meta = parser::reparse_reg_name(&buf.as_bytes()[start..]); + auth_meta.host_meta = parser::parse_v4_or_reg_name(&buf.as_bytes()[start..]); } } } @@ -123,3 +130,62 @@ fn normalize_estr(buf: &mut String, s: &str, to_lowercase: bool) { } } } + +// Taken from `std`. +#[cfg(not(feature = "net"))] +fn write_v6(buf: &mut String, segments: [u16; 8]) { + if let [0, 0, 0, 0, 0, 0xffff, ab, cd] = segments { + let [a, b] = ab.to_be_bytes(); + let [c, d] = cd.to_be_bytes(); + write!(buf, "::ffff:{}.{}.{}.{}", a, b, c, d).unwrap() + } else { + #[derive(Copy, Clone, Default)] + struct Span { + start: usize, + len: usize, + } + + // Find the inner 0 span + let zeroes = { + let mut longest = Span::default(); + let mut current = Span::default(); + + for (i, &segment) in segments.iter().enumerate() { + if segment == 0 { + if current.len == 0 { + current.start = i; + } + + current.len += 1; + + if current.len > longest.len { + longest = current; + } + } else { + current = Span::default(); + } + } + + longest + }; + + /// Write a colon-separated part of the address + #[inline] + fn write_subslice(buf: &mut String, chunk: &[u16]) { + if let Some((first, tail)) = chunk.split_first() { + write!(buf, "{:x}", first).unwrap(); + for segment in tail { + write!(buf, ":{:x}", segment).unwrap(); + } + } + } + + if zeroes.len > 1 { + write_subslice(buf, &segments[..zeroes.start]); + buf.push_str("::"); + write_subslice(buf, &segments[zeroes.start + zeroes.len..]); + } else { + write_subslice(buf, &segments); + } + } +} diff --git a/src/parser.rs b/src/parser.rs index b20a714e..4fd4c588 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -398,7 +398,7 @@ impl<'a> Reader<'a> { } } -pub(crate) fn reparse_reg_name(bytes: &[u8]) -> HostMeta { +pub(crate) fn parse_v4_or_reg_name(bytes: &[u8]) -> HostMeta { let mut reader = Reader::new(bytes); match reader.read_v4() { Some(_addr) if !reader.has_remaining() => HostMeta::Ipv4( @@ -409,6 +409,11 @@ pub(crate) fn reparse_reg_name(bytes: &[u8]) -> HostMeta { } } +#[cfg(not(feature = "net"))] +pub(crate) fn parse_v6(bytes: &[u8]) -> [u16; 8] { + Reader::new(bytes).read_v6().unwrap() +} + impl<'a> Parser<'a> { fn parse_from_scheme(&mut self) -> Result<()> { self.read(SCHEME)?; diff --git a/tests/normalize.rs b/tests/normalize.rs index 6023c566..55f67b4e 100644 --- a/tests/normalize.rs +++ b/tests/normalize.rs @@ -1,6 +1,10 @@ -use std::net::{Ipv4Addr, Ipv6Addr}; +#[cfg(feature = "net")] +use core::net::{Ipv4Addr, Ipv6Addr}; -use fluent_uri::{component::Host, Uri}; +#[cfg(feature = "net")] +use fluent_uri::component::Host; + +use fluent_uri::Uri; #[test] fn normalize() { @@ -72,6 +76,7 @@ fn normalize() { // Normal IPv4 address. let u = Uri::parse("//127.0.0.1").unwrap(); assert_eq!(u.normalize(), "//127.0.0.1"); + #[cfg(feature = "net")] assert_eq!( u.normalize().authority().unwrap().host_parsed(), Host::Ipv4(Ipv4Addr::LOCALHOST) @@ -80,6 +85,7 @@ fn normalize() { // Percent-encoded IPv4 address. let u = Uri::parse("//127.0.0.%31").unwrap(); assert_eq!(u.normalize(), "//127.0.0.1"); + #[cfg(feature = "net")] assert_eq!( u.normalize().authority().unwrap().host_parsed(), Host::Ipv4(Ipv4Addr::LOCALHOST) @@ -88,6 +94,7 @@ fn normalize() { // Normal IPv6 address. let u = Uri::parse("//[::1]").unwrap(); assert_eq!(u.normalize(), "//[::1]"); + #[cfg(feature = "net")] assert_eq!( u.normalize().authority().unwrap().host_parsed(), Host::Ipv6(Ipv6Addr::LOCALHOST) @@ -96,11 +103,20 @@ fn normalize() { // Verbose IPv6 address. let u = Uri::parse("//[0000:0000:0000::1]").unwrap(); assert_eq!(u.normalize(), "//[::1]"); + #[cfg(feature = "net")] assert_eq!( u.normalize().authority().unwrap().host_parsed(), Host::Ipv6(Ipv6Addr::LOCALHOST) ); + // IPv4-mapped IPv6 address. + let u = Uri::parse("//[0:0:0:0:0:ffff:192.0.2.1]").unwrap(); + assert_eq!(u.normalize(), "//[::ffff:192.0.2.1]"); + + // Deprecated IPv4-compatible IPv6 address. + let u = Uri::parse("//[::192.0.2.1]").unwrap(); + assert_eq!(u.normalize(), "//[::c000:201]"); + // IPvFuture address. let u = Uri::parse("//[v1FdE.AddR]").unwrap(); assert_eq!(u.normalize(), "//[v1fde.addr]"); diff --git a/tests/parse.rs b/tests/parse.rs index 82004a15..ab70b553 100644 --- a/tests/parse.rs +++ b/tests/parse.rs @@ -1,4 +1,5 @@ -use std::net::{Ipv4Addr, Ipv6Addr}; +#[cfg(feature = "net")] +use core::net::{Ipv4Addr, Ipv6Addr}; use fluent_uri::{component::Host, encoding::EStr, Uri}; @@ -47,6 +48,7 @@ fn parse_absolute() { assert_eq!(a.as_str(), "[2001:db8::7]"); assert_eq!(a.userinfo(), None); assert_eq!(a.host(), "[2001:db8::7]"); + #[cfg(feature = "net")] assert_eq!( a.host_parsed(), Host::Ipv6(Ipv6Addr::new(0x2001, 0xdb8, 0, 0, 0, 0, 0, 0x7)) @@ -83,6 +85,7 @@ fn parse_absolute() { assert_eq!(a.as_str(), "192.0.2.16:80"); assert_eq!(a.userinfo(), None); assert_eq!(a.host(), "192.0.2.16"); + #[cfg(feature = "net")] assert_eq!(a.host_parsed(), Host::Ipv4(Ipv4Addr::new(192, 0, 2, 16))); assert_eq!(a.port(), Some("80")); assert_eq!(u.path(), "/"); @@ -117,6 +120,7 @@ fn parse_absolute() { Some(EStr::new("cnn.example.com&story=breaking_news")) ); assert_eq!(a.host(), "10.0.0.1"); + #[cfg(feature = "net")] assert_eq!(a.host_parsed(), Host::Ipv4(Ipv4Addr::new(10, 0, 0, 1))); assert_eq!(a.port(), None); assert_eq!(u.path(), "/top_story.htm"); @@ -141,6 +145,7 @@ fn parse_absolute() { assert_eq!(a.as_str(), "127.0.0.1:"); assert_eq!(a.userinfo(), None); assert_eq!(a.host(), "127.0.0.1"); + #[cfg(feature = "net")] assert_eq!(a.host_parsed(), Host::Ipv4(Ipv4Addr::new(127, 0, 0, 1))); assert_eq!(a.port(), Some("")); assert_eq!(u.path(), "/"); @@ -153,6 +158,7 @@ fn parse_absolute() { assert_eq!(a.as_str(), "127.0.0.1:8080"); assert_eq!(a.userinfo(), None); assert_eq!(a.host(), "127.0.0.1"); + #[cfg(feature = "net")] assert_eq!(a.host_parsed(), Host::Ipv4(Ipv4Addr::new(127, 0, 0, 1))); assert_eq!(a.port(), Some("8080")); assert_eq!(u.path(), "/"); @@ -165,6 +171,7 @@ fn parse_absolute() { assert_eq!(a.as_str(), "127.0.0.1:80808"); assert_eq!(a.userinfo(), None); assert_eq!(a.host(), "127.0.0.1"); + #[cfg(feature = "net")] assert_eq!(a.host_parsed(), Host::Ipv4(Ipv4Addr::new(127, 0, 0, 1))); assert_eq!(a.port(), Some("80808")); assert_eq!(u.path(), "/"); diff --git a/tests/parse_ip.rs b/tests/parse_ip.rs index 078e8592..ab283474 100644 --- a/tests/parse_ip.rs +++ b/tests/parse_ip.rs @@ -1,4 +1,6 @@ -use std::net::{Ipv4Addr, Ipv6Addr}; +#![cfg(feature = "net")] + +use core::net::{Ipv4Addr, Ipv6Addr}; use fluent_uri::{component::Host, Uri}; diff --git a/tests/to_socket_addrs.rs b/tests/to_socket_addrs.rs index 4cffd6e9..315f329b 100644 --- a/tests/to_socket_addrs.rs +++ b/tests/to_socket_addrs.rs @@ -1,3 +1,5 @@ +#![cfg(all(feature = "net", feature = "std"))] + use std::net::{Ipv4Addr, Ipv6Addr, SocketAddrV4, SocketAddrV6}; use fluent_uri::Uri;