Skip to content

Commit 594830b

Browse files
committed
Closer-to-proper error handling for connect
1 parent c73e1e9 commit 594830b

File tree

1 file changed

+85
-32
lines changed

1 file changed

+85
-32
lines changed

src/lib.rs

Lines changed: 85 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ extern mod extra;
22

33
use extra::digest::Digest;
44
use extra::md5::Md5;
5-
use extra::url::Url;
5+
use extra::url::{UserInfo, Url};
66
use std::cell::Cell;
77
use std::rt::io::io_error;
88
use std::rt::io::net::ip::SocketAddr;
@@ -26,26 +26,68 @@ impl Drop for PostgresConnection {
2626
}
2727
}
2828

29+
#[deriving(ToStr)]
30+
pub struct PostgresError;
31+
32+
#[deriving(ToStr)]
33+
pub enum PostgresConnectError {
34+
InvalidUrl,
35+
MissingUser,
36+
DbError(PostgresError),
37+
MissingPassword,
38+
UnsupportedAuthentication
39+
}
40+
2941
impl PostgresConnection {
3042
pub fn connect(url: &str) -> PostgresConnection {
31-
let url: Url = FromStr::from_str(url).unwrap();
43+
match PostgresConnection::try_connect(url) {
44+
Ok(conn) => conn,
45+
Err(err) => fail!("Failed to connect: %s", err.to_str())
46+
}
47+
}
48+
49+
pub fn try_connect(url: &str) -> Result<PostgresConnection,
50+
PostgresConnectError> {
51+
let Url {
52+
host,
53+
port,
54+
user,
55+
path,
56+
query: args,
57+
_
58+
}: Url = match FromStr::from_str(url) {
59+
Some(url) => url,
60+
None => return Err(InvalidUrl)
61+
};
62+
let user = match user {
63+
Some(user) => user,
64+
None => return Err(MissingUser)
65+
};
66+
let mut args = args;
67+
68+
let socket_url = fmt!("%s:%s", host, port.unwrap_or_default(~"5432"));
69+
let addr: SocketAddr = match FromStr::from_str(socket_url) {
70+
Some(addr) => addr,
71+
None => return Err(InvalidUrl)
72+
};
3273
33-
let socket_url = fmt!("%s:%s", url.host,
34-
url.port.get_ref().as_slice());
35-
let addr: SocketAddr = FromStr::from_str(socket_url).unwrap();
3674
let conn = PostgresConnection {
75+
// Need to figure out what to do about unwrap here
3776
stream: Cell::new(TcpStream::connect(addr).unwrap()),
3877
next_stmt_id: Cell::new(0)
3978
};
4079
41-
let mut args = url.query.clone();
42-
args.push((~"user", url.user.get_ref().user.clone()));
43-
if !url.path.is_empty() {
44-
args.push((~"database", url.path.clone()));
80+
// we have to clone here since we need the user again for auth
81+
args.push((~"user", user.user.clone()));
82+
if !path.is_empty() {
83+
args.push((~"database", path));
4584
}
4685
conn.write_message(&StartupMessage(args.as_slice()));
4786
48-
conn.handle_auth(&url);
87+
match conn.handle_auth(user) {
88+
Some(err) => return Err(err),
89+
None => ()
90+
}
4991
5092
loop {
5193
match conn.read_message() {
@@ -57,7 +99,7 @@ impl PostgresConnection {
5799
}
58100
}
59101
60-
conn
102+
Ok(conn)
61103
}
62104
63105
fn write_message(&self, message: &FrontendMessage) {
@@ -72,28 +114,39 @@ impl PostgresConnection {
72114
}
73115
}
74116
75-
fn handle_auth(&self, url: &Url) {
76-
loop {
77-
match self.read_message() {
78-
AuthenticationOk => break,
79-
AuthenticationCleartextPassword => {
80-
let pass = url.user.get_ref().pass.get_ref().as_slice();
81-
self.write_message(&PasswordMessage(pass));
82-
}
83-
AuthenticationMD5Password(nonce) => {
84-
let input = url.user.get_ref().pass.get_ref().as_slice() +
85-
url.user.get_ref().user.as_slice();
86-
let mut md5 = Md5::new();
87-
md5.input_str(input);
88-
let output = md5.result_str();
89-
md5.reset();
90-
md5.input_str(output);
91-
md5.input(nonce);
92-
let output = "md5" + md5.result_str();
93-
self.write_message(&PasswordMessage(output.as_slice()));
94-
}
95-
resp => fail!("Bad response: %?", resp.to_str())
117+
fn handle_auth(&self, user: UserInfo) -> Option<PostgresConnectError> {
118+
match self.read_message() {
119+
AuthenticationOk => return None,
120+
AuthenticationCleartextPassword => {
121+
let pass = match user.pass {
122+
Some(pass) => pass,
123+
None => return Some(MissingPassword)
124+
};
125+
self.write_message(&PasswordMessage(pass));
126+
}
127+
AuthenticationMD5Password(nonce) => {
128+
let UserInfo { user, pass } = user;
129+
let pass = match pass {
130+
Some(pass) => pass,
131+
None => return Some(MissingPassword)
132+
};
133+
let input = pass + user;
134+
let mut md5 = Md5::new();
135+
md5.input_str(input);
136+
let output = md5.result_str();
137+
md5.reset();
138+
md5.input_str(output);
139+
md5.input(nonce);
140+
let output = "md5" + md5.result_str();
141+
self.write_message(&PasswordMessage(output.as_slice()));
96142
}
143+
resp => fail!("Bad response: %?", resp.to_str())
144+
}
145+
146+
match self.read_message() {
147+
AuthenticationOk => None,
148+
ErrorResponse(*) => Some(DbError(PostgresError)),
149+
resp => fail!("Bad response: %?", resp.to_str())
97150
}
98151
}
99152

0 commit comments

Comments
 (0)