Skip to content

Add an example of using a pooled bb8 connection with rustls #64

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Mar 20, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,9 @@ jobs:

- name: Test diesel_async
run: cargo +${{ matrix.rust }} test --manifest-path Cargo.toml --no-default-features --features "${{ matrix.backend }} deadpool bb8 mobc"
- name: Run examples
if: matrix.backend == 'postgres'
run: cargo +${{ matrix.rust }} check --manifest-path examples/postgres/pooled-with-rustls/Cargo.toml

rustfmt_and_clippy:
name: Check rustfmt style && run clippy
Expand Down
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
/target
**/target
Cargo.lock
6 changes: 6 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -51,3 +51,9 @@ rustdoc-args = ["--cfg", "doc_cfg"]

[patch.crates-io]
diesel = { git = "https://github.com/diesel-rs/diesel", rev = "b878fed" }

[workspace]
members = [
".",
"examples/postgres/pooled-with-rustls"
]
16 changes: 16 additions & 0 deletions examples/postgres/pooled-with-rustls/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
[package]
name = "pooled-with-rustls"
version = "0.1.0"
edition = "2021"

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
diesel = { version = "2.0.2", default-features = false, features = ["postgres"] }
diesel-async = { version = "0.2.0", path = "../../../", features = ["bb8", "postgres"] }
futures-util = "0.3.21"
rustls = "0.20.8"
rustls-native-certs = "0.6.2"
tokio = { version = "1.2.0", default-features = false, features = ["macros", "rt-multi-thread"] }
tokio-postgres = "0.7.7"
tokio-postgres-rustls = "0.9.0"
68 changes: 68 additions & 0 deletions examples/postgres/pooled-with-rustls/src/main.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
use diesel::{ConnectionError, ConnectionResult};
use diesel_async::pooled_connection::bb8::Pool;
use diesel_async::pooled_connection::AsyncDieselConnectionManager;
use diesel_async::AsyncPgConnection;
use futures_util::future::BoxFuture;
use futures_util::FutureExt;
use std::time::Duration;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let db_url = std::env::var("DATABASE_URL").expect("Env var `DATABASE_URL` not set");

// First we have to construct a connection manager with our custom `establish_connection`
// function
let mgr = AsyncDieselConnectionManager::<AsyncPgConnection>::new_with_setup(
db_url,
establish_connection,
);
// From that connection we can then create a pool, here given with some example settings.
//
// This creates a TLS configuration that's equivalent to `libpq'` `sslmode=verify-full`, which
// means this will check whether the provided certificate is valid for the given database host.
//
// `libpq` does not perform these checks by default (https://www.postgresql.org/docs/current/libpq-connect.html)
// If you hit a TLS error while conneting to the database double check your certificates
let pool = Pool::builder()
.max_size(10)
.min_idle(Some(5))
.max_lifetime(Some(Duration::from_secs(60 * 60 * 24)))
.idle_timeout(Some(Duration::from_secs(60 * 2)))
.build(mgr)
.await?;

// Now we can use our pool to run queries over a TLS-secured connection:
let conn = pool.get().await?;
let _ = conn;

Ok(())
}

fn establish_connection(config: &str) -> BoxFuture<ConnectionResult<AsyncPgConnection>> {
let fut = async {
// We first set up the way we want rustls to work.
let rustls_config = rustls::ClientConfig::builder()
.with_safe_defaults()
.with_root_certificates(root_certs())
.with_no_client_auth();
let tls = tokio_postgres_rustls::MakeRustlsConnect::new(rustls_config);
let (client, conn) = tokio_postgres::connect(config, tls)
.await
.map_err(|e| ConnectionError::BadConnection(e.to_string()))?;
tokio::spawn(async move {
if let Err(e) = conn.await {
eprintln!("Database connection: {e}");
}
});
AsyncPgConnection::try_from(client).await
};
fut.boxed()
}

fn root_certs() -> rustls::RootCertStore {
let mut roots = rustls::RootCertStore::empty();
let certs = rustls_native_certs::load_native_certs().expect("Certs not loadable!");
let certs: Vec<_> = certs.into_iter().map(|cert| cert.0).collect();
roots.add_parsable_certificates(&certs);
roots
}