Tests and a fix for ssl connections

Setting ssl options was broken because the memory for its content
was released immediately. We now keep ownership of everything until
the pool is dropped.
pull/4/head
Thijs Cadier 9 years ago
parent 96a051ef74
commit 98bb376969

1
.gitignore vendored

@ -2,3 +2,4 @@ target
Cargo.lock Cargo.lock
mongoc-sys/mongo-c-driver* mongoc-sys/mongo-c-driver*
mongoc-sys/src/bindings.rs mongoc-sys/src/bindings.rs
ssl_env_vars

@ -22,7 +22,10 @@ use super::read_prefs::ReadPrefs;
// with this root bool, there must be a better way. // with this root bool, there must be a better way.
pub struct ClientPool { pub struct ClientPool {
root_instance: bool, root_instance: bool,
uri: Uri, // Uri and SslOptions need to be present for the
// lifetime of this pool.
uri: Uri,
ssl_options: Option<SslOptions>,
inner: *mut bindings::mongoc_client_pool_t inner: *mut bindings::mongoc_client_pool_t
} }
@ -39,7 +42,7 @@ impl ClientPool {
pool_ptr pool_ptr
}; };
match ssl_options { match ssl_options {
Some(options) => { Some(ref options) => {
unsafe { unsafe {
bindings::mongoc_client_pool_set_ssl_opts( bindings::mongoc_client_pool_set_ssl_opts(
pool, pool,
@ -51,8 +54,9 @@ impl ClientPool {
}; };
ClientPool { ClientPool {
root_instance: true, root_instance: true,
uri: uri, // Become owner of uri so it doesn't go out of scope uri: uri,
inner: pool ssl_options: ssl_options,
inner: pool
} }
} }
@ -93,6 +97,7 @@ impl Clone for ClientPool {
ClientPool { ClientPool {
root_instance: false, root_instance: false,
uri: self.uri.clone(), uri: self.uri.clone(),
ssl_options: self.ssl_options.clone(),
inner: self.inner.clone() inner: self.inner.clone()
} }
} }
@ -111,11 +116,18 @@ impl Drop for ClientPool {
pub struct SslOptions { pub struct SslOptions {
inner: bindings::mongoc_ssl_opt_t, inner: bindings::mongoc_ssl_opt_t,
pem_file: Option<PathBuf>, // We need to store everything so both memory sticks around
pem_password: Option<String>, // for the C driver and we can clone this struct.
ca_file: Option<PathBuf>, pem_file: Option<PathBuf>,
ca_dir: Option<PathBuf>, _pem_file_cstring: Option<CString>,
crl_file: Option<PathBuf>, pem_password: Option<String>,
_pem_password_cstring: Option<CString>,
ca_file: Option<PathBuf>,
_ca_file_cstring: Option<CString>,
ca_dir: Option<PathBuf>,
_ca_dir_cstring: Option<CString>,
crl_file: Option<PathBuf>,
_crl_file_cstring: Option<CString>,
weak_cert_validation: bool weak_cert_validation: bool
} }
@ -128,37 +140,31 @@ impl SslOptions {
crl_file: Option<PathBuf>, crl_file: Option<PathBuf>,
weak_cert_validation: bool weak_cert_validation: bool
) -> io::Result<SslOptions> { ) -> io::Result<SslOptions> {
let pem_file_cstring = try!(Self::cstring_from_path(&pem_file));
let pem_password_cstring = Self::cstring_from_string(&pem_password);
let ca_file_cstring = try!(Self::cstring_from_path(&ca_file));
let ca_dir_cstring = try!(Self::cstring_from_path(&ca_dir));
let crl_file_cstring = try!(Self::cstring_from_path(&crl_file));
let ssl_options = bindings::mongoc_ssl_opt_t { let ssl_options = bindings::mongoc_ssl_opt_t {
pem_file: match pem_file { pem_file: match pem_file_cstring {
Some(ref f) => { Some(ref f) => f.as_ptr(),
try!(File::open(f.as_path()));
Self::path_ptr(f)
},
None => ptr::null() None => ptr::null()
}, },
pem_pwd: match pem_password { pem_pwd: match pem_password_cstring {
Some(ref password) => CString::new(password.clone()).unwrap().as_ptr(), Some(ref password) => password.as_ptr(),
None => ptr::null() None => ptr::null()
}, },
ca_file: match ca_file { ca_file: match ca_file_cstring {
Some(ref f) => { Some(ref f) => f.as_ptr(),
try!(File::open(f.as_path()));
Self::path_ptr(f)
},
None => ptr::null() None => ptr::null()
}, },
ca_dir: match ca_dir { ca_dir: match ca_dir_cstring {
Some(ref f) => { Some(ref f) => f.as_ptr(),
try!(File::open(f.as_path()));
Self::path_ptr(f)
},
None => ptr::null() None => ptr::null()
}, },
crl_file: match crl_file { crl_file: match crl_file_cstring {
Some(ref f) => { Some(ref f) => f.as_ptr(),
try!(File::open(f.as_path()));
Self::path_ptr(f)
},
None => ptr::null() None => ptr::null()
}, },
weak_cert_validation: weak_cert_validation as u8, weak_cert_validation: weak_cert_validation as u8,
@ -166,18 +172,36 @@ impl SslOptions {
}; };
Ok(SslOptions { Ok(SslOptions {
inner: ssl_options, inner: ssl_options,
pem_file: pem_file, pem_file: pem_file,
pem_password: pem_password, _pem_file_cstring: pem_file_cstring,
ca_file: ca_file, pem_password: pem_password,
ca_dir: ca_dir, _pem_password_cstring: pem_password_cstring,
crl_file: crl_file, ca_file: ca_file,
weak_cert_validation: weak_cert_validation _ca_file_cstring: ca_file_cstring,
ca_dir: ca_dir,
_ca_dir_cstring: ca_dir_cstring,
crl_file: crl_file,
_crl_file_cstring: crl_file_cstring,
weak_cert_validation: weak_cert_validation
}) })
} }
fn path_ptr(path: &PathBuf) -> *const i8 { fn cstring_from_path(path: &Option<PathBuf>) -> io::Result<Option<CString>> {
path.as_os_str().to_cstring().unwrap().as_ptr() match path {
&Some(ref p) => {
try!(File::open(p.as_path()));
Ok(Some(p.as_os_str().to_cstring().unwrap()))
},
&None => Ok(None)
}
}
fn cstring_from_string(path: &Option<String>) -> Option<CString> {
match path {
&Some(ref p) => Some(CString::new(p.clone()).unwrap()),
&None => None
}
} }
fn inner(&self) -> *const bindings::mongoc_ssl_opt_t { fn inner(&self) -> *const bindings::mongoc_ssl_opt_t {

@ -1,3 +1,4 @@
use std::env;
use std::path::PathBuf; use std::path::PathBuf;
use std::thread; use std::thread;
@ -55,7 +56,6 @@ fn test_get_server_status() {
#[test] #[test]
fn test_new_pool_with_ssl_options() { fn test_new_pool_with_ssl_options() {
// We currently have no way to test full operations
let uri = Uri::new("mongodb://localhost:27017/").unwrap(); let uri = Uri::new("mongodb://localhost:27017/").unwrap();
let ssl_options = SslOptions::new( let ssl_options = SslOptions::new(
Some(PathBuf::from("./README.md")), Some(PathBuf::from("./README.md")),
@ -80,3 +80,43 @@ fn test_ssl_options_nonexistent_file() {
false false
).is_err()); ).is_err());
} }
#[test]
fn test_ssl_connection_success() {
// This is currently tested on a private replica set, will be skipped if env vars are not set.
let uri = Uri::new(match env::var("MONGO_RUST_DRIVER_SSL_URI") { Ok(v) => v, Err(_) => return }).unwrap();
let pem_file = PathBuf::from(match env::var("MONGO_RUST_DRIVER_SSL_PEM_FILE") { Ok(v) => v, Err(_) => return });
let ca_file = PathBuf::from(match env::var("MONGO_RUST_DRIVER_SSL_CA_FILE") { Ok(v) => v, Err(_) => return });
let ssl_options = SslOptions::new(
Some(pem_file),
None,
Some(ca_file),
None,
None,
false
).unwrap();
let pool = ClientPool::new(uri, Some(ssl_options));
let client = pool.pop();
let database = client.get_database("admin");
let result = database.command_simple(doc! { "ping" => 1 }, None).unwrap();
assert!(result.contains_key("ok"));
}
#[test]
fn test_ssl_connection_failure() {
// This connection should fail since the private replica set uses certificate
// based auth that we're not setting. Test will be skipped if env var is not set.
let uri = Uri::new(match env::var("MONGO_RUST_DRIVER_SSL_URI") { Ok(v) => v, Err(_) => return }).unwrap();
let pool = ClientPool::new(uri, None);
let client = pool.pop();
let database = client.get_database("admin");
let result = database.command_simple(doc! { "ping" => 1 }, None);
assert!(result.is_err());
}

Loading…
Cancel
Save