@@ -4,12 +4,99 @@ use std::cell::RefCell;
4
4
use std:: collections:: HashSet ;
5
5
use std:: str;
6
6
7
+ use url;
8
+
7
9
use cmd:: { cmd, pipe, Pipeline } ;
8
10
use types:: { RedisResult , Okay , Error , Value , Data , Nil , InternalIoError ,
9
- ToRedisArgs , FromRedisValue , from_redis_value} ;
11
+ ToRedisArgs , FromRedisValue , from_redis_value,
12
+ InvalidClientConfig } ;
10
13
use parser:: Parser ;
11
14
12
15
16
+ static DEFAULT_PORT : u16 = 6370 ;
17
+
18
+ fn redis_scheme_type_mapper ( scheme : & str ) -> url:: SchemeType {
19
+ match scheme {
20
+ "redis" => url:: RelativeScheme ( DEFAULT_PORT ) ,
21
+ _ => url:: NonRelativeScheme ,
22
+ }
23
+ }
24
+
25
+ /// This function takes a redis URL string and parses it into a URL
26
+ /// as used by rust-url. This is necessary as the default parser does
27
+ /// not understand how redis URLs function.
28
+ pub fn parse_redis_url ( input : & str ) -> url:: ParseResult < url:: Url > {
29
+ let mut parser = url:: UrlParser :: new ( ) ;
30
+ parser. scheme_type_mapper ( redis_scheme_type_mapper) ;
31
+ match parser. parse ( input) {
32
+ Ok ( result) => {
33
+ if result. scheme . as_slice ( ) != "redis" {
34
+ Err ( url:: InvalidScheme )
35
+ } else {
36
+ Ok ( result)
37
+ }
38
+ } ,
39
+ Err ( err) => Err ( err) ,
40
+ }
41
+ }
42
+
43
+
44
+ /// Holds the connection information that redis should use for connecting.
45
+ pub struct ConnectionInfo {
46
+ pub host : String ,
47
+ pub port : u16 ,
48
+ pub db : i64 ,
49
+ pub passwd : Option < String > ,
50
+ }
51
+
52
+ /// Converts an object into a connection info struct. This allows the
53
+ /// constructor of the client to accept connection information in a
54
+ /// range of different formats.
55
+ pub trait IntoConnectionInfo {
56
+ fn into_connection_info ( self ) -> RedisResult < ConnectionInfo > ;
57
+ }
58
+
59
+ impl IntoConnectionInfo for ConnectionInfo {
60
+ fn into_connection_info ( self ) -> RedisResult < ConnectionInfo > {
61
+ Ok ( self )
62
+ }
63
+ }
64
+
65
+ impl < ' a > IntoConnectionInfo for & ' a str {
66
+ fn into_connection_info ( self ) -> RedisResult < ConnectionInfo > {
67
+ match parse_redis_url ( self ) {
68
+ Ok ( u) => u. into_connection_info ( ) ,
69
+ Err ( _) => Err ( Error :: simple (
70
+ InvalidClientConfig , "Redis URL did not parse" ) ) ,
71
+ }
72
+ }
73
+ }
74
+
75
+ impl IntoConnectionInfo for url:: Url {
76
+ fn into_connection_info ( self ) -> RedisResult < ConnectionInfo > {
77
+ ensure ! ( self . scheme. as_slice( ) == "redis" , Err ( Error :: simple(
78
+ InvalidClientConfig , "URL provided is not a redis URL" ) ) ) ;
79
+
80
+ println ! ( "{}" , self ) ;
81
+
82
+ Ok ( ConnectionInfo {
83
+ host : unwrap_or ! ( self . serialize_host( ) ,
84
+ return Err ( Error :: simple( InvalidClientConfig ,
85
+ "Missing hostname" ) ) ) ,
86
+ port : self . port ( ) . unwrap_or ( DEFAULT_PORT ) ,
87
+ db : match self . serialize_path ( ) . unwrap_or ( "" . to_string ( ) )
88
+ . as_slice ( ) . trim_chars ( '/' ) {
89
+ "" => 0 ,
90
+ path => unwrap_or ! ( from_str:: <i64 >( path) ,
91
+ return Err ( Error :: simple( InvalidClientConfig ,
92
+ "Path is not a valid redis database number" ) ) )
93
+ } ,
94
+ passwd : self . password ( ) . and_then ( |pw| Some ( pw. to_string ( ) ) ) ,
95
+ } )
96
+ }
97
+ }
98
+
99
+
13
100
struct ActualConnection {
14
101
sock : TcpStream ,
15
102
}
@@ -60,11 +147,13 @@ impl ActualConnection {
60
147
}
61
148
62
149
63
- pub fn connect ( host : & str , port : u16 , db : i64 ) -> IoResult < Connection > {
64
- let con = try!( ActualConnection :: new ( host, port) ) ;
65
- let rv = Connection { con : RefCell :: new ( con) , db : db } ;
66
- if db != 0 {
67
- match cmd ( "SELECT" ) . arg ( db) . query :: < Value > ( & rv) {
150
+ pub fn connect ( connection_info : & ConnectionInfo ) -> IoResult < Connection > {
151
+ let con = try!( ActualConnection :: new (
152
+ connection_info. host [ ] , connection_info. port ) ) ;
153
+ let rv = Connection { con : RefCell :: new ( con) , db : connection_info. db } ;
154
+
155
+ if connection_info. db != 0 {
156
+ match cmd ( "SELECT" ) . arg ( connection_info. db ) . query :: < Value > ( & rv) {
68
157
Ok ( Okay ) => { }
69
158
_ => { return Err ( IoError {
70
159
kind : ConnectionFailed ,
@@ -73,12 +162,27 @@ pub fn connect(host: &str, port: u16, db: i64) -> IoResult<Connection> {
73
162
} ) ; }
74
163
}
75
164
}
165
+
166
+ match connection_info. passwd {
167
+ Some ( ref passwd) => {
168
+ match cmd ( "AUTH" ) . arg ( passwd[ ] ) . query :: < Value > ( & rv) {
169
+ Ok ( Okay ) => { }
170
+ _ => { return Err ( IoError {
171
+ kind : ConnectionFailed ,
172
+ desc : "Password authentication failed" ,
173
+ detail : None ,
174
+ } ) ; }
175
+ }
176
+ } ,
177
+ None => { } ,
178
+ }
179
+
76
180
Ok ( rv)
77
181
}
78
182
79
- pub fn connect_pubsub ( host : & str , port : u16 ) -> IoResult < PubSub > {
183
+ pub fn connect_pubsub ( connection_info : & ConnectionInfo ) -> IoResult < PubSub > {
80
184
Ok ( PubSub {
81
- con : try!( connect ( host , port , 0 ) ) ,
185
+ con : try!( connect ( connection_info ) ) ,
82
186
channels : HashSet :: new ( ) ,
83
187
pchannels : HashSet :: new ( ) ,
84
188
} )
0 commit comments