Skip to content

Commit

Permalink
Fix parsing of IPV6 URLs in client example
Browse files Browse the repository at this point in the history
Currently, when trying to run the client example with an IPV6 address
URL, such as by running:

    cargo run --example client https://[::1]:4433/Cargo.toml --host localhost

A "failed to lookup address information: Name or service not known"
error is raised. This is because `url.host_str()` is `"[::1]"`, which
is wrapped in brackets. These brackets, specified by by RFC 2732, are
part of the URL syntax, not the IP address syntax.

Although this code succeeds, because the standard library treats this
like a URL:

    use std::net::ToSocketAddrs;
    "[::1]:4433".to_socket_addrs()

This code does not:

    use std::net::Ipv6Addr;
    "[::1]".parse::<Ipv6Addr>()

As the stdlib expects to just receive "::1". Consequentially, this
does not succeed, counterintuitively:

    use std::net::ToSocketAddrs;
    ("[::1]", 4433).to_socket_addrs()

This code fixes the client example's URL parsing behavior by
stripping out such brackets in the same way as [is done in
tokio-tungstenite][1].

[1]: https://github.com/snapview/tokio-tungstenite/blob/052d085aff4708b924b92becaa83923b15045841/src/lib.rs#L404
  • Loading branch information
gretchenfrage authored and Ralith committed Jan 21, 2024
1 parent 2a1b614 commit 597b10b
Showing 1 changed file with 13 additions and 6 deletions.
19 changes: 13 additions & 6 deletions quinn/examples/client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,8 @@ fn main() {
#[tokio::main]
async fn run(options: Opt) -> Result<()> {
let url = options.url;
let remote = (url.host_str().unwrap(), url.port().unwrap_or(4433))
let url_host = strip_ipv6_brackets(url.host_str().unwrap());
let remote = (url_host, url.port().unwrap_or(4433))
.to_socket_addrs()?
.next()
.ok_or_else(|| anyhow!("couldn't resolve to an address"))?;
Expand Down Expand Up @@ -102,11 +103,7 @@ async fn run(options: Opt) -> Result<()> {
let request = format!("GET {}\r\n", url.path());
let start = Instant::now();
let rebind = options.rebind;
let host = options
.host
.as_ref()
.map_or_else(|| url.host_str(), |x| Some(x))
.ok_or_else(|| anyhow!("no hostname specified"))?;
let host = options.host.as_deref().unwrap_or(url_host);

eprintln!("connecting to {host} at {remote}");
let conn = endpoint
Expand Down Expand Up @@ -153,6 +150,16 @@ async fn run(options: Opt) -> Result<()> {
Ok(())
}

fn strip_ipv6_brackets(host: &str) -> &str {
// An ipv6 url looks like eg https://[::1]:4433/Cargo.toml, wherein the host [::1] is the
// ipv6 address ::1 wrapped in brackets, per RFC 2732. This strips those.
if host.starts_with('[') && host.ends_with(']') {
&host[1..host.len() - 1]
} else {
host
}
}

fn duration_secs(x: &Duration) -> f32 {
x.as_secs() as f32 + x.subsec_nanos() as f32 * 1e-9
}

0 comments on commit 597b10b

Please sign in to comment.