2018-12-28 18:55:09 +00:00
|
|
|
use std::collections::HashMap;
|
2018-12-14 05:03:47 +00:00
|
|
|
use std::iter;
|
2018-12-28 18:51:30 +00:00
|
|
|
#[cfg(all(feature = "runtime", unix))]
|
|
|
|
use std::path::{Path, PathBuf};
|
2018-12-14 05:03:47 +00:00
|
|
|
use std::str::{self, FromStr};
|
2018-12-28 19:16:38 +00:00
|
|
|
use std::sync::Arc;
|
2018-12-28 18:51:30 +00:00
|
|
|
#[cfg(feature = "runtime")]
|
|
|
|
use std::time::Duration;
|
2018-11-27 06:45:14 +00:00
|
|
|
use tokio_io::{AsyncRead, AsyncWrite};
|
|
|
|
|
2018-12-18 05:25:21 +00:00
|
|
|
#[cfg(feature = "runtime")]
|
|
|
|
use crate::proto::ConnectFuture;
|
2018-12-17 05:30:52 +00:00
|
|
|
use crate::proto::HandshakeFuture;
|
2018-12-18 05:25:21 +00:00
|
|
|
#[cfg(feature = "runtime")]
|
2018-12-19 05:39:05 +00:00
|
|
|
use crate::{Connect, MakeTlsMode, Socket};
|
2018-12-17 05:30:52 +00:00
|
|
|
use crate::{Error, Handshake, TlsMode};
|
2018-11-27 06:45:14 +00:00
|
|
|
|
2018-12-28 18:51:30 +00:00
|
|
|
#[cfg(feature = "runtime")]
|
|
|
|
#[derive(Debug, Clone, PartialEq)]
|
|
|
|
pub(crate) enum Host {
|
|
|
|
Tcp(String),
|
|
|
|
#[cfg(unix)]
|
|
|
|
Unix(PathBuf),
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Debug, Clone, PartialEq)]
|
2018-12-28 19:16:38 +00:00
|
|
|
pub(crate) struct Inner {
|
2018-12-28 18:51:30 +00:00
|
|
|
pub(crate) params: HashMap<String, String>,
|
|
|
|
pub(crate) password: Option<Vec<u8>>,
|
|
|
|
#[cfg(feature = "runtime")]
|
|
|
|
pub(crate) host: Vec<Host>,
|
|
|
|
#[cfg(feature = "runtime")]
|
|
|
|
pub(crate) port: Vec<u16>,
|
|
|
|
#[cfg(feature = "runtime")]
|
|
|
|
pub(crate) connect_timeout: Option<Duration>,
|
2018-11-27 06:45:14 +00:00
|
|
|
}
|
|
|
|
|
2018-12-28 19:16:38 +00:00
|
|
|
#[derive(Debug, Clone, PartialEq)]
|
|
|
|
pub struct Builder(pub(crate) Arc<Inner>);
|
|
|
|
|
2018-12-10 05:23:31 +00:00
|
|
|
impl Default for Builder {
|
|
|
|
fn default() -> Builder {
|
|
|
|
Builder::new()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-11-27 06:45:14 +00:00
|
|
|
impl Builder {
|
|
|
|
pub fn new() -> Builder {
|
|
|
|
let mut params = HashMap::new();
|
|
|
|
params.insert("client_encoding".to_string(), "UTF8".to_string());
|
|
|
|
params.insert("timezone".to_string(), "GMT".to_string());
|
|
|
|
|
2018-12-28 19:16:38 +00:00
|
|
|
Builder(Arc::new(Inner {
|
2018-12-28 18:51:30 +00:00
|
|
|
params,
|
|
|
|
password: None,
|
|
|
|
#[cfg(feature = "runtime")]
|
|
|
|
host: vec![],
|
|
|
|
#[cfg(feature = "runtime")]
|
|
|
|
port: vec![],
|
|
|
|
#[cfg(feature = "runtime")]
|
|
|
|
connect_timeout: None,
|
2018-12-28 19:16:38 +00:00
|
|
|
}))
|
2018-12-28 18:51:30 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#[cfg(feature = "runtime")]
|
|
|
|
pub fn host(&mut self, host: &str) -> &mut Builder {
|
|
|
|
#[cfg(unix)]
|
|
|
|
{
|
|
|
|
if host.starts_with('/') {
|
2018-12-28 19:33:27 +00:00
|
|
|
return self.host_path(host);
|
2018-12-28 18:51:30 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-12-28 19:16:38 +00:00
|
|
|
Arc::make_mut(&mut self.0)
|
|
|
|
.host
|
|
|
|
.push(Host::Tcp(host.to_string()));
|
2018-12-28 18:51:30 +00:00
|
|
|
self
|
|
|
|
}
|
|
|
|
|
|
|
|
#[cfg(all(feature = "runtime", unix))]
|
|
|
|
pub fn host_path<T>(&mut self, host: T) -> &mut Builder
|
|
|
|
where
|
|
|
|
T: AsRef<Path>,
|
|
|
|
{
|
2018-12-28 19:16:38 +00:00
|
|
|
Arc::make_mut(&mut self.0)
|
|
|
|
.host
|
|
|
|
.push(Host::Unix(host.as_ref().to_path_buf()));
|
2018-12-28 18:51:30 +00:00
|
|
|
self
|
2018-11-27 06:45:14 +00:00
|
|
|
}
|
|
|
|
|
2018-12-28 18:51:30 +00:00
|
|
|
#[cfg(feature = "runtime")]
|
|
|
|
pub fn port(&mut self, port: u16) -> &mut Builder {
|
2018-12-28 19:16:38 +00:00
|
|
|
Arc::make_mut(&mut self.0).port.push(port);
|
2018-12-28 18:51:30 +00:00
|
|
|
self
|
2018-11-27 06:45:14 +00:00
|
|
|
}
|
|
|
|
|
2018-12-28 18:51:30 +00:00
|
|
|
#[cfg(feature = "runtime")]
|
|
|
|
pub fn connect_timeout(&mut self, connect_timeout: Duration) -> &mut Builder {
|
2018-12-28 19:16:38 +00:00
|
|
|
Arc::make_mut(&mut self.0).connect_timeout = Some(connect_timeout);
|
2018-12-28 18:51:30 +00:00
|
|
|
self
|
2018-11-27 06:45:14 +00:00
|
|
|
}
|
|
|
|
|
2018-12-28 18:51:30 +00:00
|
|
|
pub fn password<T>(&mut self, password: T) -> &mut Builder
|
|
|
|
where
|
|
|
|
T: AsRef<[u8]>,
|
|
|
|
{
|
2018-12-28 19:16:38 +00:00
|
|
|
Arc::make_mut(&mut self.0).password = Some(password.as_ref().to_vec());
|
2018-12-28 18:51:30 +00:00
|
|
|
self
|
2018-11-27 06:45:14 +00:00
|
|
|
}
|
|
|
|
|
2018-12-14 05:03:47 +00:00
|
|
|
pub fn param(&mut self, key: &str, value: &str) -> &mut Builder {
|
2018-12-28 19:16:38 +00:00
|
|
|
Arc::make_mut(&mut self.0)
|
|
|
|
.params
|
|
|
|
.insert(key.to_string(), value.to_string());
|
2018-11-27 06:45:14 +00:00
|
|
|
self
|
|
|
|
}
|
|
|
|
|
2018-12-17 05:30:52 +00:00
|
|
|
pub fn handshake<S, T>(&self, stream: S, tls_mode: T) -> Handshake<S, T>
|
2018-11-27 06:45:14 +00:00
|
|
|
where
|
|
|
|
S: AsyncRead + AsyncWrite,
|
|
|
|
T: TlsMode<S>,
|
|
|
|
{
|
2018-12-28 18:51:30 +00:00
|
|
|
Handshake(HandshakeFuture::new(stream, tls_mode, self.clone()))
|
2018-12-14 05:03:47 +00:00
|
|
|
}
|
2018-12-18 05:25:21 +00:00
|
|
|
|
|
|
|
#[cfg(feature = "runtime")]
|
2018-12-19 05:39:05 +00:00
|
|
|
pub fn connect<T>(&self, make_tls_mode: T) -> Connect<T>
|
2018-12-18 05:25:21 +00:00
|
|
|
where
|
2018-12-19 05:39:05 +00:00
|
|
|
T: MakeTlsMode<Socket>,
|
2018-12-18 05:25:21 +00:00
|
|
|
{
|
2018-12-28 18:51:30 +00:00
|
|
|
Connect(ConnectFuture::new(make_tls_mode, self.clone()))
|
2018-12-18 05:25:21 +00:00
|
|
|
}
|
2018-12-14 05:03:47 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
impl FromStr for Builder {
|
|
|
|
type Err = Error;
|
|
|
|
|
|
|
|
fn from_str(s: &str) -> Result<Builder, Error> {
|
|
|
|
let mut parser = Parser::new(s);
|
|
|
|
let mut builder = Builder::new();
|
|
|
|
|
|
|
|
while let Some((key, value)) = parser.parameter()? {
|
2018-12-28 18:51:30 +00:00
|
|
|
match key {
|
|
|
|
"password" => {
|
|
|
|
builder.password(value);
|
|
|
|
}
|
|
|
|
#[cfg(feature = "runtime")]
|
|
|
|
"host" => {
|
|
|
|
for host in value.split(',') {
|
|
|
|
builder.host(host);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#[cfg(feature = "runtime")]
|
|
|
|
"port" => {
|
|
|
|
for port in value.split(',') {
|
|
|
|
let port = if port.is_empty() {
|
|
|
|
5432
|
|
|
|
} else {
|
|
|
|
port.parse().map_err(Error::invalid_port)?
|
|
|
|
};
|
|
|
|
builder.port(port);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#[cfg(feature = "runtime")]
|
|
|
|
"connect_timeout" => {
|
|
|
|
let timeout = value
|
|
|
|
.parse::<i64>()
|
|
|
|
.map_err(Error::invalid_connect_timeout)?;
|
|
|
|
if timeout > 0 {
|
|
|
|
builder.connect_timeout(Duration::from_secs(timeout as u64));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
key => {
|
|
|
|
builder.param(key, &value);
|
|
|
|
}
|
|
|
|
}
|
2018-12-14 05:03:47 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
Ok(builder)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
struct Parser<'a> {
|
|
|
|
s: &'a str,
|
|
|
|
it: iter::Peekable<str::CharIndices<'a>>,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl<'a> Parser<'a> {
|
|
|
|
fn new(s: &'a str) -> Parser<'a> {
|
|
|
|
Parser {
|
|
|
|
s,
|
|
|
|
it: s.char_indices().peekable(),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn skip_ws(&mut self) {
|
2018-12-17 02:11:52 +00:00
|
|
|
self.take_while(|c| c.is_whitespace());
|
2018-12-14 05:03:47 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
fn take_while<F>(&mut self, f: F) -> &'a str
|
|
|
|
where
|
|
|
|
F: Fn(char) -> bool,
|
|
|
|
{
|
|
|
|
let start = match self.it.peek() {
|
|
|
|
Some(&(i, _)) => i,
|
|
|
|
None => return "",
|
|
|
|
};
|
|
|
|
|
|
|
|
loop {
|
|
|
|
match self.it.peek() {
|
|
|
|
Some(&(_, c)) if f(c) => {
|
|
|
|
self.it.next();
|
|
|
|
}
|
|
|
|
Some(&(i, _)) => return &self.s[start..i],
|
|
|
|
None => return &self.s[start..],
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn eat(&mut self, target: char) -> Result<(), Error> {
|
|
|
|
match self.it.next() {
|
|
|
|
Some((_, c)) if c == target => Ok(()),
|
|
|
|
Some((i, c)) => {
|
|
|
|
let m = format!(
|
|
|
|
"unexpected character at byte {}: expected `{}` but got `{}`",
|
|
|
|
i, target, c
|
|
|
|
);
|
|
|
|
Err(Error::connection_syntax(m.into()))
|
|
|
|
}
|
|
|
|
None => Err(Error::connection_syntax("unexpected EOF".into())),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn eat_if(&mut self, target: char) -> bool {
|
|
|
|
match self.it.peek() {
|
|
|
|
Some(&(_, c)) if c == target => {
|
|
|
|
self.it.next();
|
|
|
|
true
|
|
|
|
}
|
|
|
|
_ => false,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn keyword(&mut self) -> Option<&'a str> {
|
|
|
|
let s = self.take_while(|c| match c {
|
2018-12-17 02:11:52 +00:00
|
|
|
c if c.is_whitespace() => false,
|
|
|
|
'=' => false,
|
2018-12-14 05:03:47 +00:00
|
|
|
_ => true,
|
|
|
|
});
|
|
|
|
|
|
|
|
if s.is_empty() {
|
|
|
|
None
|
|
|
|
} else {
|
|
|
|
Some(s)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-12-17 02:11:52 +00:00
|
|
|
fn value(&mut self) -> Result<String, Error> {
|
|
|
|
let value = if self.eat_if('\'') {
|
|
|
|
let value = self.quoted_value()?;
|
2018-12-14 05:03:47 +00:00
|
|
|
self.eat('\'')?;
|
2018-12-17 02:11:52 +00:00
|
|
|
value
|
2018-12-14 05:03:47 +00:00
|
|
|
} else {
|
2018-12-17 02:11:52 +00:00
|
|
|
self.simple_value()?
|
2018-12-14 05:03:47 +00:00
|
|
|
};
|
|
|
|
|
2018-12-17 02:11:52 +00:00
|
|
|
Ok(value)
|
2018-12-14 05:03:47 +00:00
|
|
|
}
|
|
|
|
|
2018-12-17 02:11:52 +00:00
|
|
|
fn simple_value(&mut self) -> Result<String, Error> {
|
|
|
|
let mut value = String::new();
|
|
|
|
|
|
|
|
while let Some(&(_, c)) = self.it.peek() {
|
|
|
|
if c.is_whitespace() {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
self.it.next();
|
|
|
|
if c == '\\' {
|
|
|
|
if let Some((_, c2)) = self.it.next() {
|
|
|
|
value.push(c2);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
value.push(c);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if value.is_empty() {
|
|
|
|
return Err(Error::connection_syntax("unexpected EOF".into()));
|
2018-12-14 05:03:47 +00:00
|
|
|
}
|
|
|
|
|
2018-12-17 02:11:52 +00:00
|
|
|
Ok(value)
|
|
|
|
}
|
|
|
|
|
|
|
|
fn quoted_value(&mut self) -> Result<String, Error> {
|
|
|
|
let mut value = String::new();
|
|
|
|
|
|
|
|
while let Some(&(_, c)) = self.it.peek() {
|
|
|
|
if c == '\'' {
|
|
|
|
return Ok(value);
|
|
|
|
}
|
|
|
|
|
|
|
|
self.it.next();
|
|
|
|
if c == '\\' {
|
|
|
|
if let Some((_, c2)) = self.it.next() {
|
|
|
|
value.push(c2);
|
2018-12-14 05:03:47 +00:00
|
|
|
}
|
|
|
|
} else {
|
2018-12-17 02:11:52 +00:00
|
|
|
value.push(c);
|
|
|
|
}
|
2018-12-14 05:03:47 +00:00
|
|
|
}
|
|
|
|
|
2018-12-17 02:11:52 +00:00
|
|
|
Err(Error::connection_syntax(
|
|
|
|
"unterminated quoted connection parameter value".into(),
|
|
|
|
))
|
2018-12-14 05:03:47 +00:00
|
|
|
}
|
|
|
|
|
2018-12-17 02:11:52 +00:00
|
|
|
fn parameter(&mut self) -> Result<Option<(&'a str, String)>, Error> {
|
2018-12-14 05:03:47 +00:00
|
|
|
self.skip_ws();
|
|
|
|
let keyword = match self.keyword() {
|
|
|
|
Some(keyword) => keyword,
|
|
|
|
None => return Ok(None),
|
|
|
|
};
|
|
|
|
self.skip_ws();
|
|
|
|
self.eat('=')?;
|
|
|
|
self.skip_ws();
|
|
|
|
let value = self.value()?;
|
|
|
|
|
|
|
|
Ok(Some((keyword, value)))
|
2018-11-27 06:45:14 +00:00
|
|
|
}
|
|
|
|
}
|