@@ -59,6 +59,10 @@ const TLS_INSECURE: &str = "tlsinsecure";
5959const TLS_ALLOW_INVALID_CERTIFICATES : & str = "tlsallowinvalidcertificates" ;
6060#[ cfg( feature = "openssl-tls" ) ]
6161const TLS_ALLOW_INVALID_HOSTNAMES : & str = "tlsallowinvalidhostnames" ;
62+ const PROXY_HOST : & str = "proxyhost" ;
63+ const PROXY_PORT : & str = "proxyport" ;
64+ const PROXY_USERNAME : & str = "proxyusername" ;
65+ const PROXY_PASSWORD : & str = "proxypassword" ;
6266const URI_OPTIONS : & [ & str ] = & [
6367 "appname" ,
6468 "authmechanism" ,
@@ -75,10 +79,10 @@ const URI_OPTIONS: &[&str] = &[
7579 "maxpoolsize" ,
7680 "minpoolsize" ,
7781 "maxconnecting" ,
78- "proxyHost" ,
79- "proxyPort" ,
80- "proxyUsername" ,
81- "proxyPassword" ,
82+ PROXY_HOST ,
83+ PROXY_PORT ,
84+ PROXY_USERNAME ,
85+ PROXY_PASSWORD ,
8286 "readconcernlevel" ,
8387 "readpreference" ,
8488 "readpreferencetags" ,
@@ -993,6 +997,14 @@ pub struct ConnectionString {
993997 /// Overrides the default "mongodb" service name for SRV lookup in both discovery and polling
994998 pub srv_service_name : Option < String > ,
995999
1000+ pub proxy_host : Option < String > ,
1001+
1002+ pub proxy_port : Option < u16 > ,
1003+
1004+ pub proxy_username : Option < String > ,
1005+
1006+ pub proxy_password : Option < String > ,
1007+
9961008 #[ serde( serialize_with = "serde_util::serialize_duration_option_as_int_millis" ) ]
9971009 wait_queue_timeout : Option < Duration > ,
9981010 tls_insecure : Option < bool > ,
@@ -1333,6 +1345,42 @@ impl ClientOptions {
13331345 }
13341346 }
13351347
1348+ #[ cfg( feature = "socks5-proxy" ) ]
1349+ {
1350+ if self . proxy_host . is_some ( ) {
1351+ if self
1352+ . hosts
1353+ . iter ( )
1354+ . any ( |address| !matches ! ( address, ServerAddress :: Tcp { .. } ) )
1355+ {
1356+ return Err ( Error :: invalid_argument (
1357+ "cannot specify a non-TCP address when connected to a proxy host" ,
1358+ ) ) ;
1359+ }
1360+
1361+ if self . proxy_username . is_some ( ) != self . proxy_password . is_some ( ) {
1362+ return Err ( Error :: invalid_argument (
1363+ "proxy username and proxy password must both be specified or unset" ,
1364+ ) ) ;
1365+ }
1366+ } else {
1367+ let error = |option : & str | {
1368+ Error :: invalid_argument ( format ! (
1369+ "cannot specify a proxy {option} without a proxy host"
1370+ ) )
1371+ } ;
1372+ if self . proxy_port . is_some ( ) {
1373+ return Err ( error ( "port" ) ) ;
1374+ }
1375+ if self . proxy_username . is_some ( ) {
1376+ return Err ( error ( "username" ) ) ;
1377+ }
1378+ if self . proxy_password . is_some ( ) {
1379+ return Err ( error ( "password" ) ) ;
1380+ }
1381+ }
1382+ }
1383+
13361384 Ok ( ( ) )
13371385 }
13381386
@@ -1720,6 +1768,10 @@ impl ConnectionString {
17201768 srv_service_name: _,
17211769 wait_queue_timeout,
17221770 tls_insecure,
1771+ proxy_host,
1772+ proxy_port,
1773+ proxy_username,
1774+ proxy_password,
17231775 #[ cfg ( test) ]
17241776 original_uri: _,
17251777 } = self ;
@@ -2021,6 +2073,22 @@ impl ConnectionString {
20212073 opts. push_str ( & format ! ( "&srvMaxHosts={srv_max_hosts}" ) ) ;
20222074 }
20232075
2076+ if let Some ( proxy_host) = proxy_host {
2077+ opts. push_str ( & format ! ( "&proxyHost={proxy_host}" ) ) ;
2078+ }
2079+
2080+ if let Some ( proxy_port) = proxy_port {
2081+ opts. push_str ( & format ! ( "&proxyPort={proxy_port}" ) ) ;
2082+ }
2083+
2084+ if let Some ( proxy_username) = proxy_username {
2085+ opts. push_str ( & format ! ( "&proxyUsername={proxy_username}" ) ) ;
2086+ }
2087+
2088+ if let Some ( proxy_password) = proxy_password {
2089+ opts. push_str ( & format ! ( "&proxyPassword={proxy_password}" ) ) ;
2090+ }
2091+
20242092 if !opts. is_empty ( ) {
20252093 opts. replace_range ( 0 ..1 , "?" ) ; // mark start of options
20262094 res. push_str ( & opts) ;
@@ -2599,6 +2667,24 @@ impl ConnectionString {
25992667
26002668 parts. zlib_compression = Some ( i) ;
26012669 }
2670+ #[ cfg( feature = "socks5-proxy" ) ]
2671+ PROXY_HOST => self . proxy_host = Some ( value. to_string ( ) ) ,
2672+ #[ cfg( feature = "socks5-proxy" ) ]
2673+ PROXY_PORT => {
2674+ let port = u16:: from_str ( value)
2675+ . map_err ( |_| Error :: invalid_argument ( format ! ( "invalid proxy port: {value}" ) ) ) ?;
2676+ self . proxy_port = Some ( port) ;
2677+ }
2678+ #[ cfg( feature = "socks5-proxy" ) ]
2679+ PROXY_USERNAME => self . proxy_username = Some ( value. to_string ( ) ) ,
2680+ #[ cfg( feature = "socks5-proxy" ) ]
2681+ PROXY_PASSWORD => self . proxy_password = Some ( value. to_string ( ) ) ,
2682+ #[ cfg( not( feature = "socks5-proxy" ) ) ]
2683+ PROXY_HOST | PROXY_PORT | PROXY_USERNAME | PROXY_PASSWORD => {
2684+ return Err ( Error :: invalid_argument ( format ! (
2685+ "cannot specify {key} if socks5-proxy feature is not enabled"
2686+ ) ) ) ;
2687+ }
26022688
26032689 other => {
26042690 let ( jaro_winkler, option) = URI_OPTIONS . iter ( ) . fold ( ( 0.0 , "" ) , |acc, option| {
0 commit comments