Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix wrong handling of IPv6 addresses in URLs for PostgreSQL and MySQL #4051

Merged
merged 1 commit into from
Jul 24, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 19 additions & 2 deletions quaint/src/connector/mysql.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ use std::{
time::Duration,
};
use tokio::sync::Mutex;
use url::Url;
use url::{Host, Url};

/// The underlying MySQL driver. Only available with the `expose-drivers`
/// Cargo feature.
Expand Down Expand Up @@ -98,7 +98,18 @@ impl MysqlUrl {

/// The database host. If `socket` and `host` are not set, defaults to `localhost`.
pub fn host(&self) -> &str {
self.url.host_str().unwrap_or("localhost")
match (self.url.host(), self.url.host_str()) {
(Some(Host::Ipv6(_)), Some(host)) => {
// The `url` crate may return an IPv6 address in brackets, which must be stripped.
if host.starts_with('[') && host.ends_with(']') {
&host[1..host.len() - 1]
} else {
host
}
}
(_, Some(host)) => host,
_ => "localhost",
}
}

/// If set, connected to the database through a Unix socket.
Expand Down Expand Up @@ -604,6 +615,12 @@ mod tests {
assert!(!url.query_params.ssl_opts.accept_invalid_certs());
}

#[test]
fn should_parse_ipv6_host() {
let url = MysqlUrl::new(Url::parse("mysql://[2001:db8:1234::ffff]:5432/testdb").unwrap()).unwrap();
assert_eq!("2001:db8:1234::ffff", url.host());
}

#[test]
fn should_allow_changing_of_cache_size() {
let url = MysqlUrl::new(Url::parse("mysql:///root:root@localhost:3307/foo?statement_cache_size=420").unwrap())
Expand Down
26 changes: 20 additions & 6 deletions quaint/src/connector/postgres.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ use tokio_postgres::{
config::{ChannelBinding, SslMode},
Client, Config, Statement,
};
use url::Url;
use url::{Host, Url};

pub(crate) const DEFAULT_SCHEMA: &str = "public";

Expand Down Expand Up @@ -223,11 +223,19 @@ impl PostgresUrl {
///
/// If none of them are set, defaults to `localhost`.
pub fn host(&self) -> &str {
match (self.query_params.host.as_ref(), self.url.host_str()) {
(Some(host), _) => host.as_str(),
(None, Some("")) => "localhost",
(None, None) => "localhost",
(None, Some(host)) => host,
match (self.query_params.host.as_ref(), self.url.host_str(), self.url.host()) {
(Some(host), _, _) => host.as_str(),
(None, Some(""), _) => "localhost",
(None, None, _) => "localhost",
(None, Some(host), Some(Host::Ipv6(_))) => {
// The `url` crate may return an IPv6 address in brackets, which must be stripped.
if host.starts_with('[') && host.ends_with(']') {
&host[1..host.len() - 1]
} else {
host
}
}
(None, Some(host), _) => host,
}
}

Expand Down Expand Up @@ -1142,6 +1150,12 @@ mod tests {
assert_eq!("localhost", url.host());
}

#[test]
fn should_parse_ipv6_host() {
let url = PostgresUrl::new(Url::parse("postgresql://[2001:db8:1234::ffff]:5432/dbname").unwrap()).unwrap();
assert_eq!("2001:db8:1234::ffff", url.host());
}

#[test]
fn should_handle_options_field() {
let url = PostgresUrl::new(Url::parse("postgresql:///localhost:5432?options=--cluster%3Dmy_cluster").unwrap())
Expand Down