Support non-ASCII chars in postgres credentials
The `url` crate returns username and password portions of a URL as
percent encoded ASCII strings. In order to use them for the Postgres
connection, we need to decode them.
use crate::error::Error;
use crate::postgres::PgConnectOptions;
+use percent_encoding::percent_decode_str;
use std::str::FromStr;
use url::Url;
@@ -21,11 +22,19 @@ impl FromStr for PgConnectOptions {
let username = url.username();
if !username.is_empty() {
- options = options.username(username);
+ options = options.username(
+ &*percent_decode_str(username)
+ .decode_utf8()
+ .map_err(Error::config)?,
+ );
}
if let Some(password) = url.password() {
- options = options.password(password);
+ options = options.password(
+ &*percent_decode_str(password)
+ .decode_utf8()
+ .map_err(Error::config)?,
+ );
}
let path = url.path().trim_start_matches('/');
@@ -80,3 +89,19 @@ fn it_parses_host_correctly_from_parameter() {
assert_eq!(None, opts.socket);
assert_eq!("google.database.com", &opts.host);
}
+
+#[test]
+fn it_parses_username_with_at_sign_correctly() {
+ let uri = "postgres://user@hostname:password@hostname:5432/database";
+ let opts = PgConnectOptions::from_str(uri).unwrap();
+
+ assert_eq!("user@hostname", &opts.username);
+}
+
+#[test]
+fn it_parses_password_with_non_ascii_chars_correctly() {
+ let uri = "postgres://username:p@ssw0rd@hostname:5432/database";
+ let opts = PgConnectOptions::from_str(uri).unwrap();
+
+ assert_eq!(Some("p@ssw0rd".into()), opts.password);
+}
