@@ -2,7 +2,7 @@ extern mod extra;
2
2
3
3
use extra:: digest:: Digest ;
4
4
use extra:: md5:: Md5 ;
5
- use extra:: url:: Url ;
5
+ use extra:: url:: { UserInfo , Url } ;
6
6
use std:: cell:: Cell ;
7
7
use std:: rt:: io:: io_error;
8
8
use std:: rt:: io:: net:: ip:: SocketAddr ;
@@ -26,26 +26,68 @@ impl Drop for PostgresConnection {
26
26
}
27
27
}
28
28
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
+
29
41
impl PostgresConnection {
30
42
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
+ };
32
73
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 ( ) ;
36
74
let conn = PostgresConnection {
75
+ // Need to figure out what to do about unwrap here
37
76
stream: Cell::new(TcpStream::connect(addr).unwrap()),
38
77
next_stmt_id: Cell::new(0)
39
78
};
40
79
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));
45
84
}
46
85
conn.write_message(&StartupMessage(args.as_slice()));
47
86
48
- conn. handle_auth ( & url) ;
87
+ match conn.handle_auth(user) {
88
+ Some(err) => return Err(err),
89
+ None => ()
90
+ }
49
91
50
92
loop {
51
93
match conn.read_message() {
@@ -57,7 +99,7 @@ impl PostgresConnection {
57
99
}
58
100
}
59
101
60
- conn
102
+ Ok( conn)
61
103
}
62
104
63
105
fn write_message(&self, message: &FrontendMessage) {
@@ -72,28 +114,39 @@ impl PostgresConnection {
72
114
}
73
115
}
74
116
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()));
96
142
}
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())
97
150
}
98
151
}
99
152
0 commit comments