Skip to content

Commit b860f3e

Browse files
authored
Merge pull request #64 from ThouCheese/postgres-pool-rustls-example
Add an example of using a pooled bb8 connection with rustls
2 parents 3e220ce + 8ceb165 commit b860f3e

File tree

5 files changed

+94
-1
lines changed

5 files changed

+94
-1
lines changed

.github/workflows/ci.yml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,9 @@ jobs:
115115

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

119122
rustfmt_and_clippy:
120123
name: Check rustfmt style && run clippy

.gitignore

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
1-
/target
1+
**/target
22
Cargo.lock

Cargo.toml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,3 +51,9 @@ rustdoc-args = ["--cfg", "doc_cfg"]
5151

5252
[patch.crates-io]
5353
diesel = { git = "https://github.com/diesel-rs/diesel", rev = "b878fed" }
54+
55+
[workspace]
56+
members = [
57+
".",
58+
"examples/postgres/pooled-with-rustls"
59+
]
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
[package]
2+
name = "pooled-with-rustls"
3+
version = "0.1.0"
4+
edition = "2021"
5+
6+
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
7+
8+
[dependencies]
9+
diesel = { version = "2.0.2", default-features = false, features = ["postgres"] }
10+
diesel-async = { version = "0.2.0", path = "../../../", features = ["bb8", "postgres"] }
11+
futures-util = "0.3.21"
12+
rustls = "0.20.8"
13+
rustls-native-certs = "0.6.2"
14+
tokio = { version = "1.2.0", default-features = false, features = ["macros", "rt-multi-thread"] }
15+
tokio-postgres = "0.7.7"
16+
tokio-postgres-rustls = "0.9.0"
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
use diesel::{ConnectionError, ConnectionResult};
2+
use diesel_async::pooled_connection::bb8::Pool;
3+
use diesel_async::pooled_connection::AsyncDieselConnectionManager;
4+
use diesel_async::AsyncPgConnection;
5+
use futures_util::future::BoxFuture;
6+
use futures_util::FutureExt;
7+
use std::time::Duration;
8+
9+
#[tokio::main]
10+
async fn main() -> Result<(), Box<dyn std::error::Error>> {
11+
let db_url = std::env::var("DATABASE_URL").expect("Env var `DATABASE_URL` not set");
12+
13+
// First we have to construct a connection manager with our custom `establish_connection`
14+
// function
15+
let mgr = AsyncDieselConnectionManager::<AsyncPgConnection>::new_with_setup(
16+
db_url,
17+
establish_connection,
18+
);
19+
// From that connection we can then create a pool, here given with some example settings.
20+
//
21+
// This creates a TLS configuration that's equivalent to `libpq'` `sslmode=verify-full`, which
22+
// means this will check whether the provided certificate is valid for the given database host.
23+
//
24+
// `libpq` does not perform these checks by default (https://www.postgresql.org/docs/current/libpq-connect.html)
25+
// If you hit a TLS error while conneting to the database double check your certificates
26+
let pool = Pool::builder()
27+
.max_size(10)
28+
.min_idle(Some(5))
29+
.max_lifetime(Some(Duration::from_secs(60 * 60 * 24)))
30+
.idle_timeout(Some(Duration::from_secs(60 * 2)))
31+
.build(mgr)
32+
.await?;
33+
34+
// Now we can use our pool to run queries over a TLS-secured connection:
35+
let conn = pool.get().await?;
36+
let _ = conn;
37+
38+
Ok(())
39+
}
40+
41+
fn establish_connection(config: &str) -> BoxFuture<ConnectionResult<AsyncPgConnection>> {
42+
let fut = async {
43+
// We first set up the way we want rustls to work.
44+
let rustls_config = rustls::ClientConfig::builder()
45+
.with_safe_defaults()
46+
.with_root_certificates(root_certs())
47+
.with_no_client_auth();
48+
let tls = tokio_postgres_rustls::MakeRustlsConnect::new(rustls_config);
49+
let (client, conn) = tokio_postgres::connect(config, tls)
50+
.await
51+
.map_err(|e| ConnectionError::BadConnection(e.to_string()))?;
52+
tokio::spawn(async move {
53+
if let Err(e) = conn.await {
54+
eprintln!("Database connection: {e}");
55+
}
56+
});
57+
AsyncPgConnection::try_from(client).await
58+
};
59+
fut.boxed()
60+
}
61+
62+
fn root_certs() -> rustls::RootCertStore {
63+
let mut roots = rustls::RootCertStore::empty();
64+
let certs = rustls_native_certs::load_native_certs().expect("Certs not loadable!");
65+
let certs: Vec<_> = certs.into_iter().map(|cert| cert.0).collect();
66+
roots.add_parsable_certificates(&certs);
67+
roots
68+
}

0 commit comments

Comments
 (0)