From 1a3ef5b23fdf4a4619fa9b5d3f24cff6f8008085 Mon Sep 17 00:00:00 2001 From: Matthew Pomes Date: Sat, 10 Aug 2024 13:02:41 -0500 Subject: [PATCH 01/11] Update docs to make sense --- core/lib/src/serde/msgpack.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/core/lib/src/serde/msgpack.rs b/core/lib/src/serde/msgpack.rs index 5e70fee1b1..bf758b8fe1 100644 --- a/core/lib/src/serde/msgpack.rs +++ b/core/lib/src/serde/msgpack.rs @@ -321,7 +321,8 @@ pub fn from_slice<'a, T>(v: &'a [u8]) -> Result /// /// The compact representation represents structs as arrays. /// -/// **_Always_ use [`Compact`] to serialize compact MessagePack response data.** +/// **_Always_ use [`Compact`] to serialize MessagePack response data in a +/// compact format.** /// /// # Example /// From 87f80714fa9c7edf802b3d23422d7f808f165819 Mon Sep 17 00:00:00 2001 From: Sergio Benitez Date: Wed, 14 Aug 2024 14:57:22 -0700 Subject: [PATCH 02/11] Update 'diesel-async' in 'db_pools' to 0.5. Also modifies the `databases` example so that it makes use of the new ability to run migrations in diesel-async v0.5. To accomplish this, `diesel_async::async_connection_wrapper::AsyncConnectionWrapper` is exported from `rocket_db_pools::diesel` and used in the `diesel_mysql` portion of the `databases` example. The URL for the MySQL version of the database example is now `/mysql` instead of `/diesel-async`. --- contrib/db_pools/lib/Cargo.toml | 18 +++----- contrib/db_pools/lib/src/diesel.rs | 3 ++ contrib/db_pools/lib/src/pool.rs | 53 +++-------------------- contrib/sync_db_pools/lib/src/poolable.rs | 2 +- examples/databases/README.md | 2 +- examples/databases/src/diesel_mysql.rs | 31 +++++++++++-- 6 files changed, 45 insertions(+), 64 deletions(-) diff --git a/contrib/db_pools/lib/Cargo.toml b/contrib/db_pools/lib/Cargo.toml index abbc50e61c..1835acb771 100644 --- a/contrib/db_pools/lib/Cargo.toml +++ b/contrib/db_pools/lib/Cargo.toml @@ -26,8 +26,8 @@ sqlx_postgres = ["sqlx", "sqlx/postgres", "log"] sqlx_sqlite = ["sqlx", "sqlx/sqlite", "log"] sqlx_macros = ["sqlx/macros"] # diesel features -diesel_postgres = ["diesel-async/postgres", "diesel-async/deadpool", "diesel", "deadpool_09"] -diesel_mysql = ["diesel-async/mysql", "diesel-async/deadpool", "diesel", "deadpool_09"] +diesel_postgres = ["diesel-async/postgres", "diesel-async/deadpool", "deadpool", "diesel"] +diesel_mysql = ["diesel-async/mysql", "diesel-async/deadpool", "deadpool", "diesel"] # implicit features: mongodb [dependencies.rocket] @@ -39,9 +39,8 @@ default-features = false path = "../codegen" version = "0.1.0" -[dependencies.deadpool_09] -package = "deadpool" -version = "0.9.5" +[dependencies.deadpool] +version = "0.12.1" default-features = false features = ["rt_tokio_1", "managed"] optional = true @@ -52,12 +51,6 @@ default-features = false features = ["rt_tokio_1"] optional = true -[dependencies.deadpool] -version = "0.12.1" -default-features = false -features = ["rt_tokio_1", "managed"] -optional = true - [dependencies.deadpool-redis] version = "0.15" default-features = false @@ -71,8 +64,9 @@ features = ["compat-3-0-0", "rustls-tls"] optional = true [dependencies.diesel-async] -version = "0.4.1" +version = "0.5.0" default-features = false +features = ["async-connection-wrapper"] optional = true [dependencies.diesel] diff --git a/contrib/db_pools/lib/src/diesel.rs b/contrib/db_pools/lib/src/diesel.rs index c7229eab43..9d4d935d9d 100644 --- a/contrib/db_pools/lib/src/diesel.rs +++ b/contrib/db_pools/lib/src/diesel.rs @@ -84,6 +84,9 @@ pub use diesel_async::pg; #[doc(inline)] pub use diesel_async::pooled_connection::deadpool::Pool; +#[doc(inline)] +pub use diesel_async::async_connection_wrapper::AsyncConnectionWrapper; + #[doc(inline)] #[cfg(feature = "diesel_mysql")] pub use diesel_async::AsyncMysqlConnection; diff --git a/contrib/db_pools/lib/src/pool.rs b/contrib/db_pools/lib/src/pool.rs index 73219a6d5e..b37fecced4 100644 --- a/contrib/db_pools/lib/src/pool.rs +++ b/contrib/db_pools/lib/src/pool.rs @@ -157,6 +157,9 @@ mod deadpool_postgres { use deadpool::{Runtime, managed::{Manager, Pool, PoolError, Object}}; use super::{Duration, Error, Config, Figment}; + #[cfg(feature = "diesel")] + use diesel_async::pooled_connection::AsyncDieselConnectionManager; + pub trait DeadManager: Manager + Sized + Send + Sync + 'static { fn new(config: &Config) -> Result; } @@ -175,50 +178,6 @@ mod deadpool_postgres { } } - #[rocket::async_trait] - impl>> crate::Pool for Pool - where M::Type: Send, C: Send + Sync + 'static, M::Error: std::error::Error - { - type Error = Error>; - - type Connection = C; - - async fn init(figment: &Figment) -> Result { - let config: Config = figment.extract()?; - let manager = M::new(&config).map_err(|e| Error::Init(e.into()))?; - - Pool::builder(manager) - .max_size(config.max_connections) - .wait_timeout(Some(Duration::from_secs(config.connect_timeout))) - .create_timeout(Some(Duration::from_secs(config.connect_timeout))) - .recycle_timeout(config.idle_timeout.map(Duration::from_secs)) - .runtime(Runtime::Tokio1) - .build() - .map_err(|_| Error::Init(PoolError::NoRuntimeSpecified)) - } - - async fn get(&self) -> Result { - self.get().await.map_err(Error::Get) - } - - async fn close(&self) { - >::close(self) - } - } -} - -// TODO: Remove when new release of diesel-async with deadpool 0.10 is out. -#[cfg(all(feature = "deadpool_09", any(feature = "diesel_postgres", feature = "diesel_mysql")))] -mod deadpool_old { - use deadpool_09::{managed::{Manager, Pool, PoolError, Object, BuildError}, Runtime}; - use diesel_async::pooled_connection::AsyncDieselConnectionManager; - - use super::{Duration, Error, Config, Figment}; - - pub trait DeadManager: Manager + Sized + Send + Sync + 'static { - fn new(config: &Config) -> Result; - } - #[cfg(feature = "diesel_postgres")] impl DeadManager for AsyncDieselConnectionManager { fn new(config: &Config) -> Result { @@ -237,13 +196,13 @@ mod deadpool_old { impl>> crate::Pool for Pool where M::Type: Send, C: Send + Sync + 'static, M::Error: std::error::Error { - type Error = Error, PoolError>; + type Error = Error>; type Connection = C; async fn init(figment: &Figment) -> Result { let config: Config = figment.extract()?; - let manager = M::new(&config).map_err(|e| Error::Init(BuildError::Backend(e)))?; + let manager = M::new(&config).map_err(|e| Error::Init(e.into()))?; Pool::builder(manager) .max_size(config.max_connections) @@ -252,7 +211,7 @@ mod deadpool_old { .recycle_timeout(config.idle_timeout.map(Duration::from_secs)) .runtime(Runtime::Tokio1) .build() - .map_err(Error::Init) + .map_err(|_| Error::Init(PoolError::NoRuntimeSpecified)) } async fn get(&self) -> Result { diff --git a/contrib/sync_db_pools/lib/src/poolable.rs b/contrib/sync_db_pools/lib/src/poolable.rs index 0451de60bb..ebe50994e4 100644 --- a/contrib/sync_db_pools/lib/src/poolable.rs +++ b/contrib/sync_db_pools/lib/src/poolable.rs @@ -130,7 +130,7 @@ impl Poolable for diesel::SqliteConnection { fn on_acquire(&self, conn: &mut SqliteConnection) -> Result<(), Error> { conn.batch_execute("\ PRAGMA journal_mode = WAL;\ - PRAGMA busy_timeout = 1000;\ + PRAGMA busy_timeout = 5000;\ PRAGMA foreign_keys = ON;\ ").map_err(Error::QueryError)?; diff --git a/examples/databases/README.md b/examples/databases/README.md index f9505bf6af..15eafc1f92 100644 --- a/examples/databases/README.md +++ b/examples/databases/README.md @@ -14,7 +14,7 @@ This example implements a JSON-based HTTP API for a "blog" using several databas * `sqlx` (`/sqlx`, `sqlx.rs`) * `rusqlite` (`/rusqlite`, `rusqlite.rs`) * `diesel` (sqlite) (`/diesel`, `diesel_sqlite.rs`) - * `diesel-async` (mysql) (`/diesel-async`, `diesel_mysql.rs`) + * `diesel-async` (mysql) (`/mysql`, `diesel_mysql.rs`) The exposed API is succinctly described as follows, with [`httpie`](https://httpie.io/) CLI examples: diff --git a/examples/databases/src/diesel_mysql.rs b/examples/databases/src/diesel_mysql.rs index 2d5a9a5665..0cfa3f17fa 100644 --- a/examples/databases/src/diesel_mysql.rs +++ b/examples/databases/src/diesel_mysql.rs @@ -1,9 +1,10 @@ +use rocket::{Rocket, Build}; use rocket::fairing::AdHoc; use rocket::response::{Debug, status::Created}; use rocket::serde::{Serialize, Deserialize, json::Json}; use rocket_db_pools::{Database, Connection}; -use rocket_db_pools::diesel::{MysqlPool, prelude::*}; +use rocket_db_pools::diesel::{prelude::*, MysqlPool}; type Result> = std::result::Result; @@ -34,7 +35,7 @@ diesel::table! { #[post("/", data = "")] async fn create(mut db: Connection, mut post: Json) -> Result>> { - diesel::sql_function!(fn last_insert_id() -> BigInt); + diesel::define_sql_function!(fn last_insert_id() -> BigInt); let post = db.transaction(|mut conn| Box::pin(async move { diesel::insert_into(posts::table) @@ -89,9 +90,33 @@ async fn destroy(mut db: Connection) -> Result<()> { Ok(()) } +async fn run_migrations(rocket: Rocket) -> Rocket { + use rocket_db_pools::diesel::AsyncConnectionWrapper; + use diesel_migrations::{embed_migrations, EmbeddedMigrations, MigrationHarness}; + + const MIGRATIONS: EmbeddedMigrations = embed_migrations!("db/diesel/mysql-migrations"); + + let conn = Db::fetch(&rocket) + .expect("database is attached") + .get().await + .unwrap_or_else(|e| { + span_error!("failed to connect to MySQL database" => error!("{e}")); + panic!("aborting launch"); + }); + + // `run_pending_migrations` blocks, so it must be run in `spawn_blocking` + rocket::tokio::task::spawn_blocking(move || { + let mut conn: AsyncConnectionWrapper<_> = conn.into(); + conn.run_pending_migrations(MIGRATIONS).expect("diesel migrations"); + }).await.expect("diesel migrations"); + + rocket +} + pub fn stage() -> AdHoc { AdHoc::on_ignite("Diesel MySQL Stage", |rocket| async { rocket.attach(Db::init()) - .mount("/diesel-async", routes![list, read, create, delete, destroy]) + .attach(AdHoc::on_ignite("Diesel Migrations", run_migrations)) + .mount("/mysql", routes![list, read, create, delete, destroy]) }) } From 628cc2a2f616cdd45e8a57bf46bd3ab3be2c7843 Mon Sep 17 00:00:00 2001 From: Sergio Benitez Date: Wed, 14 Aug 2024 15:02:18 -0700 Subject: [PATCH 03/11] Update 'deadpool-{postgres,redis}' to 0.14, 0.16. --- contrib/db_pools/lib/Cargo.toml | 4 ++-- contrib/db_pools/lib/src/lib.rs | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/contrib/db_pools/lib/Cargo.toml b/contrib/db_pools/lib/Cargo.toml index 1835acb771..f006fd122c 100644 --- a/contrib/db_pools/lib/Cargo.toml +++ b/contrib/db_pools/lib/Cargo.toml @@ -46,13 +46,13 @@ features = ["rt_tokio_1", "managed"] optional = true [dependencies.deadpool-postgres] -version = "0.13.2" +version = "0.14" default-features = false features = ["rt_tokio_1"] optional = true [dependencies.deadpool-redis] -version = "0.15" +version = "0.16" default-features = false features = ["rt_tokio_1"] optional = true diff --git a/contrib/db_pools/lib/src/lib.rs b/contrib/db_pools/lib/src/lib.rs index a5f71bc483..0ec23bb470 100644 --- a/contrib/db_pools/lib/src/lib.rs +++ b/contrib/db_pools/lib/src/lib.rs @@ -103,12 +103,12 @@ //! Drivers have a varying degree of support for graceful shutdown, affected by //! the `Type::init()` fairing on Rocket shutdown. //! -//! ## `deadpool` (v0.13) +//! ## `deadpool` (v0.12) //! //! | Database | Feature | [`Pool`] Type | [`Connection`] Deref | //! |----------|-----------------------------|-----------------------------|--------------------------------------| -//! | Postgres | `deadpool_postgres` (v0.13) | [`deadpool_postgres::Pool`] | [`deadpool_postgres::ClientWrapper`] | -//! | Redis | `deadpool_redis` (v0.15) | [`deadpool_redis::Pool`] | [`deadpool_redis::Connection`] | +//! | Postgres | `deadpool_postgres` (v0.14) | [`deadpool_postgres::Pool`] | [`deadpool_postgres::ClientWrapper`] | +//! | Redis | `deadpool_redis` (v0.16) | [`deadpool_redis::Pool`] | [`deadpool_redis::Connection`] | //! //! On shutdown, new connections are denied. Shutdown _does not_ wait for //! connections to be returned. From 39c90481e700274da057d371a1da60bcc968aedc Mon Sep 17 00:00:00 2001 From: Sergio Benitez Date: Wed, 14 Aug 2024 15:13:45 -0700 Subject: [PATCH 04/11] Update 'sqlx' to 0.8, 'rusqlite' to 0.31. --- contrib/db_pools/lib/Cargo.toml | 2 +- contrib/sync_db_pools/lib/Cargo.toml | 4 ++-- contrib/sync_db_pools/lib/src/lib.rs | 2 +- ...72f427c8d3911ef4327afd9516143806981e11f8e34d069c14472.json | 2 +- ...52a981b10707fe2e1ceb0bad0e473701e51ef21ecb2973c76b4df.json | 2 +- ...64f6b383e854f8bc2770d89cfaf9859d0bfca78b2ca24627675b7.json | 2 +- examples/databases/Cargo.toml | 2 +- 7 files changed, 8 insertions(+), 8 deletions(-) diff --git a/contrib/db_pools/lib/Cargo.toml b/contrib/db_pools/lib/Cargo.toml index f006fd122c..2654139914 100644 --- a/contrib/db_pools/lib/Cargo.toml +++ b/contrib/db_pools/lib/Cargo.toml @@ -75,7 +75,7 @@ default-features = false optional = true [dependencies.sqlx] -version = "0.7" +version = "0.8" default-features = false features = ["runtime-tokio-rustls"] optional = true diff --git a/contrib/sync_db_pools/lib/Cargo.toml b/contrib/sync_db_pools/lib/Cargo.toml index 5d17119c85..7d2a027ff4 100644 --- a/contrib/sync_db_pools/lib/Cargo.toml +++ b/contrib/sync_db_pools/lib/Cargo.toml @@ -31,8 +31,8 @@ diesel = { version = "2.0.0", default-features = false, optional = true } postgres = { version = "0.19", optional = true } r2d2_postgres = { version = "0.18", optional = true } -rusqlite = { version = "0.29.0", optional = true } -r2d2_sqlite = { version = "0.22.0", optional = true } +rusqlite = { version = "0.31.0", optional = true } +r2d2_sqlite = { version = "0.24.0", optional = true } memcache = { version = "0.15.2", optional = true } r2d2-memcache = { version = "0.6", optional = true } diff --git a/contrib/sync_db_pools/lib/src/lib.rs b/contrib/sync_db_pools/lib/src/lib.rs index 6fafac0c43..e68ed58165 100644 --- a/contrib/sync_db_pools/lib/src/lib.rs +++ b/contrib/sync_db_pools/lib/src/lib.rs @@ -317,7 +317,7 @@ //! | Postgres | [Diesel] | `2` | [`diesel::PgConnection`] | `diesel_postgres_pool` | //! | MySQL | [Diesel] | `2` | [`diesel::MysqlConnection`] | `diesel_mysql_pool` | //! | Postgres | [Rust-Postgres] | `0.19` | [`postgres::Client`] | `postgres_pool` | -//! | Sqlite | [`Rusqlite`] | `0.27` | [`rusqlite::Connection`] | `sqlite_pool` | +//! | Sqlite | [`Rusqlite`] | `0.31` | [`rusqlite::Connection`] | `sqlite_pool` | //! | Memcache | [`memcache`] | `0.15` | [`memcache::Client`] | `memcache_pool` | //! //! [Diesel]: https://diesel.rs diff --git a/examples/databases/.sqlx/query-11e3096becb72f427c8d3911ef4327afd9516143806981e11f8e34d069c14472.json b/examples/databases/.sqlx/query-11e3096becb72f427c8d3911ef4327afd9516143806981e11f8e34d069c14472.json index 247b40e2fd..9a480c0867 100644 --- a/examples/databases/.sqlx/query-11e3096becb72f427c8d3911ef4327afd9516143806981e11f8e34d069c14472.json +++ b/examples/databases/.sqlx/query-11e3096becb72f427c8d3911ef4327afd9516143806981e11f8e34d069c14472.json @@ -6,7 +6,7 @@ { "name": "id", "ordinal": 0, - "type_info": "Int64" + "type_info": "Integer" }, { "name": "title", diff --git a/examples/databases/.sqlx/query-4415c35941e52a981b10707fe2e1ceb0bad0e473701e51ef21ecb2973c76b4df.json b/examples/databases/.sqlx/query-4415c35941e52a981b10707fe2e1ceb0bad0e473701e51ef21ecb2973c76b4df.json index 99a73650ca..986241dc05 100644 --- a/examples/databases/.sqlx/query-4415c35941e52a981b10707fe2e1ceb0bad0e473701e51ef21ecb2973c76b4df.json +++ b/examples/databases/.sqlx/query-4415c35941e52a981b10707fe2e1ceb0bad0e473701e51ef21ecb2973c76b4df.json @@ -6,7 +6,7 @@ { "name": "id", "ordinal": 0, - "type_info": "Int64" + "type_info": "Integer" } ], "parameters": { diff --git a/examples/databases/.sqlx/query-bea4ef6e25064f6b383e854f8bc2770d89cfaf9859d0bfca78b2ca24627675b7.json b/examples/databases/.sqlx/query-bea4ef6e25064f6b383e854f8bc2770d89cfaf9859d0bfca78b2ca24627675b7.json index 7aad158e46..9a668bab3c 100644 --- a/examples/databases/.sqlx/query-bea4ef6e25064f6b383e854f8bc2770d89cfaf9859d0bfca78b2ca24627675b7.json +++ b/examples/databases/.sqlx/query-bea4ef6e25064f6b383e854f8bc2770d89cfaf9859d0bfca78b2ca24627675b7.json @@ -6,7 +6,7 @@ { "name": "id", "ordinal": 0, - "type_info": "Int64" + "type_info": "Integer" } ], "parameters": { diff --git a/examples/databases/Cargo.toml b/examples/databases/Cargo.toml index 2ed4fa5adc..935c8da18a 100644 --- a/examples/databases/Cargo.toml +++ b/examples/databases/Cargo.toml @@ -11,7 +11,7 @@ diesel = { version = "2", features = ["returning_clauses_for_sqlite_3_35"] } diesel_migrations = "2" [dependencies.sqlx] -version = "0.7.0" +version = "0.8.0" default-features = false features = ["macros", "migrate"] From 6484c621327ed6d8421addf807165c395b2f74c0 Mon Sep 17 00:00:00 2001 From: Sergio Benitez Date: Wed, 14 Aug 2024 15:44:01 -0700 Subject: [PATCH 05/11] Update 'memcache' to 0.17. We no longer depend on 'r2d2-memcache', which was woefully out of date, and instead implement our own r2d2 connection manager for memcache. --- contrib/sync_db_pools/lib/Cargo.toml | 5 +- contrib/sync_db_pools/lib/src/lib.rs | 7 +- contrib/sync_db_pools/lib/src/poolable.rs | 68 +++++++++++++++----- contrib/sync_db_pools/lib/tests/databases.rs | 10 +++ 4 files changed, 66 insertions(+), 24 deletions(-) diff --git a/contrib/sync_db_pools/lib/Cargo.toml b/contrib/sync_db_pools/lib/Cargo.toml index 7d2a027ff4..765ece2ea6 100644 --- a/contrib/sync_db_pools/lib/Cargo.toml +++ b/contrib/sync_db_pools/lib/Cargo.toml @@ -19,7 +19,7 @@ diesel_postgres_pool = ["diesel/postgres", "diesel/r2d2"] diesel_mysql_pool = ["diesel/mysql", "diesel/r2d2"] sqlite_pool = ["rusqlite", "r2d2_sqlite"] postgres_pool = ["postgres", "r2d2_postgres"] -memcache_pool = ["memcache", "r2d2-memcache"] +memcache_pool = ["memcache"] [dependencies] r2d2 = "0.8" @@ -34,8 +34,7 @@ r2d2_postgres = { version = "0.18", optional = true } rusqlite = { version = "0.31.0", optional = true } r2d2_sqlite = { version = "0.24.0", optional = true } -memcache = { version = "0.15.2", optional = true } -r2d2-memcache = { version = "0.6", optional = true } +memcache = { version = "0.17.2", optional = true } [dependencies.rocket_sync_db_pools_codegen] version = "0.1.0" diff --git a/contrib/sync_db_pools/lib/src/lib.rs b/contrib/sync_db_pools/lib/src/lib.rs index e68ed58165..7c35e3b0ba 100644 --- a/contrib/sync_db_pools/lib/src/lib.rs +++ b/contrib/sync_db_pools/lib/src/lib.rs @@ -318,7 +318,7 @@ //! | MySQL | [Diesel] | `2` | [`diesel::MysqlConnection`] | `diesel_mysql_pool` | //! | Postgres | [Rust-Postgres] | `0.19` | [`postgres::Client`] | `postgres_pool` | //! | Sqlite | [`Rusqlite`] | `0.31` | [`rusqlite::Connection`] | `sqlite_pool` | -//! | Memcache | [`memcache`] | `0.15` | [`memcache::Client`] | `memcache_pool` | +//! | Memcache | [`memcache`] | `0.17` | [`memcache::Client`] | `memcache_pool` | //! //! [Diesel]: https://diesel.rs //! [`diesel::SqliteConnection`]: https://docs.rs/diesel/2/diesel/sqlite/struct.SqliteConnection.html @@ -327,10 +327,10 @@ //! [Rust-Postgres]: https://github.com/sfackler/rust-postgres //! [`postgres::Client`]: https://docs.rs/postgres/0.19/postgres/struct.Client.html //! [`Rusqlite`]: https://github.com/jgallagher/rusqlite -//! [`rusqlite::Connection`]: https://docs.rs/rusqlite/0.27/rusqlite/struct.Connection.html +//! [`rusqlite::Connection`]: https://docs.rs/rusqlite/0.31/rusqlite/struct.Connection.html //! [`diesel::PgConnection`]: http://docs.diesel.rs/diesel/pg/struct.PgConnection.html //! [`memcache`]: https://github.com/aisk/rust-memcache -//! [`memcache::Client`]: https://docs.rs/memcache/0.15/memcache/struct.Client.html +//! [`memcache::Client`]: https://docs.rs/memcache/0.17/memcache/struct.Client.html //! //! The above table lists all the supported database adapters in this library. //! In order to use particular `Poolable` type that's included in this library, @@ -372,7 +372,6 @@ pub use diesel; #[cfg(feature = "sqlite_pool")] pub use r2d2_sqlite; #[cfg(feature = "memcache_pool")] pub use memcache; -#[cfg(feature = "memcache_pool")] pub use r2d2_memcache; pub use r2d2; diff --git a/contrib/sync_db_pools/lib/src/poolable.rs b/contrib/sync_db_pools/lib/src/poolable.rs index ebe50994e4..8700f6b91c 100644 --- a/contrib/sync_db_pools/lib/src/poolable.rs +++ b/contrib/sync_db_pools/lib/src/poolable.rs @@ -13,11 +13,12 @@ use crate::{Config, Error}; /// /// Implementations of `Poolable` are provided for the following types: /// -/// * `diesel::MysqlConnection` -/// * `diesel::PgConnection` -/// * `diesel::SqliteConnection` -/// * `postgres::Connection` -/// * `rusqlite::Connection` +/// * [`diesel::MysqlConnection`](diesel::MysqlConnection) +/// * [`diesel::PgConnection`](diesel::PgConnection) +/// * [`diesel::SqliteConnection`](diesel::SqliteConnection) +/// * [`postgres::Client`](postgres::Client) +/// * [`rusqlite::Connection`](rusqlite::Connection) +/// * [`memcache::Client`](memcache::Client) /// /// # Implementation Guide /// @@ -263,19 +264,52 @@ impl Poolable for rusqlite::Connection { } #[cfg(feature = "memcache_pool")] -impl Poolable for memcache::Client { - type Manager = r2d2_memcache::MemcacheConnectionManager; - // Unused, but we might want it in the future without a breaking change. - type Error = memcache::MemcacheError; +mod memcache_pool { + use memcache::{Client, Connectable, MemcacheError}; - fn pool(db_name: &str, rocket: &Rocket) -> PoolResult { - let config = Config::from(db_name, rocket)?; - let manager = r2d2_memcache::MemcacheConnectionManager::new(&*config.url); - let pool = r2d2::Pool::builder() - .max_size(config.pool_size) - .connection_timeout(Duration::from_secs(config.timeout as u64)) - .build(manager)?; + use super::*; - Ok(pool) + #[derive(Debug)] + pub struct ConnectionManager { + urls: Vec, + } + + impl ConnectionManager { + pub fn new(target: C) -> Self { + Self { urls: target.get_urls(), } + } + } + + impl r2d2::ManageConnection for ConnectionManager { + type Connection = Client; + type Error = MemcacheError; + + fn connect(&self) -> Result { + Client::connect(self.urls.clone()) + } + + fn is_valid(&self, connection: &mut Client) -> Result<(), MemcacheError> { + connection.version().map(|_| ()) + } + + fn has_broken(&self, _connection: &mut Client) -> bool { + false + } + } + + impl super::Poolable for memcache::Client { + type Manager = ConnectionManager; + type Error = MemcacheError; + + fn pool(db_name: &str, rocket: &Rocket) -> PoolResult { + let config = Config::from(db_name, rocket)?; + let manager = ConnectionManager::new(&*config.url); + let pool = r2d2::Pool::builder() + .max_size(config.pool_size) + .connection_timeout(Duration::from_secs(config.timeout as u64)) + .build(manager)?; + + Ok(pool) + } } } diff --git a/contrib/sync_db_pools/lib/tests/databases.rs b/contrib/sync_db_pools/lib/tests/databases.rs index ae72a6c2c7..9a58c791a6 100644 --- a/contrib/sync_db_pools/lib/tests/databases.rs +++ b/contrib/sync_db_pools/lib/tests/databases.rs @@ -11,6 +11,16 @@ mod databases_tests { struct PrimaryDb(diesel::PgConnection); } +#[cfg(feature = "memcache_pool")] +mod memcache_pool_tests { + #![allow(dead_code)] + + use rocket_sync_db_pools::database; + + #[database("test_db")] + struct MemcacheDb(memcache::Client); +} + #[cfg(test)] #[cfg(all(feature = "sqlite_pool"))] mod rusqlite_integration_test { From e41b5f469e3133e9ee45212c0d50c4f582c859d0 Mon Sep 17 00:00:00 2001 From: Sergio Benitez Date: Thu, 15 Aug 2024 02:18:54 -0700 Subject: [PATCH 06/11] Fix Windows CI: update vcpkg dependency bundle. --- .github/workflows/ci.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 9db1bafdda..293cf447a2 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -76,13 +76,14 @@ jobs: - name: Install Native Dependencies (Windows) if: matrix.platform.name == 'Windows' run: | - curl -fsLS -o vcpkg.7z https://blob.rocket.rs/vcpkg-2019-07-05.7z + curl -fsLS -o vcpkg.7z https://blob.rocket.rs/vcpkg-2024-08-16.7z 7z x vcpkg.7z -y -bb0 xcopy .\vcpkg $env:VCPKG_INSTALLATION_ROOT /s /e /h /y /q vcpkg integrate install echo "VCPKGRS_DYNAMIC=1" >> "$env:GITHUB_ENV" echo "VCPKG_ROOT=$env:VCPKG_INSTALLATION_ROOT" >> "$env:GITHUB_ENV" echo "$env:VCPKG_INSTALLATION_ROOT\installed\x64-windows\lib" >> "$env:GITHUB_PATH" + echo "MYSQLCLIENT_VERSION=8.0.39" >> "$env:GITHUB_ENV" - name: Install NASM (Windows) if: matrix.platform.name == 'Windows' From 2825e46a340a067922380f15f43c04874ea9e2f4 Mon Sep 17 00:00:00 2001 From: Sergio Benitez Date: Fri, 16 Aug 2024 05:30:45 -0700 Subject: [PATCH 07/11] Update 'tokio-tungstenite' to 0.23. --- contrib/ws/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contrib/ws/Cargo.toml b/contrib/ws/Cargo.toml index 417f28fc7d..079b36322d 100644 --- a/contrib/ws/Cargo.toml +++ b/contrib/ws/Cargo.toml @@ -20,7 +20,7 @@ default = ["tungstenite"] tungstenite = ["tokio-tungstenite"] [dependencies] -tokio-tungstenite = { version = "0.21", optional = true } +tokio-tungstenite = { version = "0.23", optional = true } [dependencies.rocket] version = "0.6.0-dev" From faa0543c3d7b67b16691ab21c8b90fa7d0922564 Mon Sep 17 00:00:00 2001 From: Sergio Benitez Date: Fri, 16 Aug 2024 15:47:04 -0700 Subject: [PATCH 08/11] Pin 's2n-quic-h3' git dependency to a commit. --- core/lib/Cargo.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/core/lib/Cargo.toml b/core/lib/Cargo.toml index 004115a681..1dec918711 100644 --- a/core/lib/Cargo.toml +++ b/core/lib/Cargo.toml @@ -128,6 +128,7 @@ optional = true [dependencies.s2n-quic-h3] git = "https://github.com/SergioBenitez/s2n-quic-h3.git" +rev = "865fd25" optional = true [target.'cfg(unix)'.dependencies] From 51d4ed439442046223405bf0e6d3fb707855b0bf Mon Sep 17 00:00:00 2001 From: Sergio Benitez Date: Fri, 16 Aug 2024 16:09:54 -0700 Subject: [PATCH 09/11] Update 'h3' and 's2n_quic' dependencies. --- core/lib/Cargo.toml | 4 ++-- core/lib/src/listener/quic.rs | 14 +++++++------- examples/tls/Rocket.toml | 3 +++ examples/tls/src/redirector.rs | 33 ++++++++++----------------------- 4 files changed, 22 insertions(+), 32 deletions(-) diff --git a/core/lib/Cargo.toml b/core/lib/Cargo.toml index 1dec918711..10eb5f17bd 100644 --- a/core/lib/Cargo.toml +++ b/core/lib/Cargo.toml @@ -33,7 +33,7 @@ uuid = ["uuid_", "rocket_http/uuid"] tls = ["rustls", "tokio-rustls", "rustls-pemfile"] mtls = ["tls", "x509-parser"] tokio-macros = ["tokio/macros"] -trace = ["tracing-subscriber", "tinyvec", "thread_local", "rustls?/logging", "tokio-rustls?/logging", "multer/log"] +trace = ["tracing-subscriber", "tinyvec", "thread_local", "rustls?/logging", "tokio-rustls?/logging", "multer/log", "s2n-quic-h3?/tracing"] [dependencies] # Optional serialization dependencies. @@ -128,7 +128,7 @@ optional = true [dependencies.s2n-quic-h3] git = "https://github.com/SergioBenitez/s2n-quic-h3.git" -rev = "865fd25" +rev = "7aa3be0" optional = true [target.'cfg(unix)'.dependencies] diff --git a/core/lib/src/listener/quic.rs b/core/lib/src/listener/quic.rs index dd94fd054f..a51ae13eed 100644 --- a/core/lib/src/listener/quic.rs +++ b/core/lib/src/listener/quic.rs @@ -48,10 +48,10 @@ pub struct QuicListener { tls: TlsConfig, } -pub struct H3Stream(H3Conn); +pub struct H3Stream(H3Conn, quic::connection::Result); pub struct H3Connection { - pub(crate) handle: quic::connection::Handle, + pub(crate) remote: quic::connection::Result, pub(crate) parts: http::request::Parts, pub(crate) tx: QuicTx, pub(crate) rx: QuicRx, @@ -104,9 +104,10 @@ impl QuicListener { } pub async fn connect(&self, accept: quic::Connection) -> io::Result { + let remote = accept.remote_addr(); let quic_conn = quic_h3::Connection::new(accept); let conn = H3Conn::new(quic_conn).await.map_err(io::Error::other)?; - Ok(H3Stream(conn)) + Ok(H3Stream(conn, remote)) } pub fn endpoint(&self) -> io::Result { @@ -116,7 +117,7 @@ impl QuicListener { impl H3Stream { pub async fn accept(&mut self) -> io::Result> { - let handle = self.0.inner.conn.handle().clone(); + let remote = self.1.clone(); let ((parts, _), (tx, rx)) = match self.0.accept().await { Ok(Some((req, stream))) => (req.into_parts(), stream.split()), Ok(None) => return Ok(None), @@ -129,7 +130,7 @@ impl H3Stream { } }; - Ok(Some(H3Connection { handle, parts, tx: QuicTx(tx), rx: QuicRx(rx) })) + Ok(Some(H3Connection { remote, parts, tx: QuicTx(tx), rx: QuicRx(rx) })) } } @@ -158,8 +159,7 @@ impl QuicTx { // FIXME: Expose certificates when possible. impl H3Connection { pub fn endpoint(&self) -> io::Result { - let addr = self.handle.remote_addr()?; - Ok(Endpoint::Quic(addr).assume_tls()) + Ok(Endpoint::Quic(self.remote?).assume_tls()) } } diff --git a/examples/tls/Rocket.toml b/examples/tls/Rocket.toml index 36f2f158d5..4b94b79d93 100644 --- a/examples/tls/Rocket.toml +++ b/examples/tls/Rocket.toml @@ -5,6 +5,9 @@ # directly for your browser to show connections as secure. You should NEVER use # these certificate/key pairs. They are here for DEMONSTRATION PURPOSES ONLY. +[default] +log_format = "compact" + [default.tls] certs = "private/rsa_sha256_cert.pem" key = "private/rsa_sha256_key.pem" diff --git a/examples/tls/src/redirector.rs b/examples/tls/src/redirector.rs index 2e4a5ce320..ae08185aa1 100644 --- a/examples/tls/src/redirector.rs +++ b/examples/tls/src/redirector.rs @@ -3,7 +3,7 @@ use std::net::SocketAddr; use rocket::http::Status; -use rocket::tracing::Level; +use rocket::tracing::{self, Instrument}; use rocket::{route, Error, Request, Data, Route, Orbit, Rocket, Ignite}; use rocket::fairing::{Fairing, Info, Kind}; use rocket::response::Redirect; @@ -45,16 +45,13 @@ impl Redirector { pub async fn try_launch(self, config: Config) -> Result, Error> { use rocket::http::Method::*; - rocket::span_info!("HTTP -> HTTPS Redirector" => { - info!(from = self.0, to = config.tls_addr.port(), "redirecting"); - }); - // Build a vector of routes to `redirect` on `` for each method. let redirects = [Get, Put, Post, Delete, Options, Head, Trace, Connect, Patch] .into_iter() .map(|m| Route::new(m, "/", Self::redirect)) .collect::>(); + info!(from = self.0, to = config.tls_addr.port(), "redirecting"); let addr = SocketAddr::new(config.tls_addr.ip(), self.0); rocket::custom(&config.server) .manage(config) @@ -73,35 +70,25 @@ impl Fairing for Redirector { } } + #[tracing::instrument(name = "HTTP -> HTTPS Redirector", skip_all)] async fn on_liftoff(&self, rocket: &Rocket) { let Some(tls_addr) = rocket.endpoints().find_map(|e| e.tls()?.tcp()) else { - rocket::span_warn!("HTTP -> HTTPS Redirector" => { - warn!("Main instance is not being served over TLS/TCP.\n\ - Redirector refusing to start."); - }); + warn!("Main instance is not being served over TLS/TCP.\n\ + Redirector refusing to start."); return; }; - let config = Config { - tls_addr, - server: rocket::Config { - log_level: Some(Level::ERROR), - ..rocket.config().clone() - }, - }; - let this = *self; let shutdown = rocket.shutdown(); + let span = tracing::info_span!("HTTP -> HTTPS Redirector"); + let config = Config { tls_addr, server: rocket.config().clone() }; rocket::tokio::spawn(async move { if let Err(e) = this.try_launch(config).await { - span_error!("HTTP -> HTTPS Redirector", "failed to start" => { - e.trace_error(); - info!("shutting down main instance"); - }); - + e.trace_error(); + info!("shutting down main instance"); shutdown.notify(); } - }); + }.instrument(span)); } } From 4c71ed6ee3747ff97ca7ff9cd8abefac7636fe0b Mon Sep 17 00:00:00 2001 From: lobsterwise Date: Fri, 16 Aug 2024 22:47:26 -0400 Subject: [PATCH 10/11] Fix support for HTTP extension methods. --- core/lib/src/request/request.rs | 7 +++--- testbench/src/client.rs | 6 +++-- testbench/src/servers/http_extensions.rs | 29 ++++++++++++++++++++++++ testbench/src/servers/mod.rs | 1 + 4 files changed, 38 insertions(+), 5 deletions(-) create mode 100644 testbench/src/servers/http_extensions.rs diff --git a/core/lib/src/request/request.rs b/core/lib/src/request/request.rs index 93912383de..e99e721d3b 100644 --- a/core/lib/src/request/request.rs +++ b/core/lib/src/request/request.rs @@ -2,6 +2,7 @@ use std::{io, fmt}; use std::ops::RangeFrom; use std::sync::{Arc, atomic::Ordering}; use std::borrow::Cow; +use std::str::FromStr; use std::future::Future; use std::net::IpAddr; @@ -1086,7 +1087,7 @@ impl<'r> Request<'r> { // Keep track of parsing errors; emit a `BadRequest` if any exist. let mut errors = vec![]; - // Ensure that the method is known. TODO: Allow made-up methods? + // Ensure that the method is known. let method = match hyper.method { hyper::Method::GET => Method::Get, hyper::Method::PUT => Method::Put, @@ -1097,10 +1098,10 @@ impl<'r> Request<'r> { hyper::Method::TRACE => Method::Trace, hyper::Method::CONNECT => Method::Connect, hyper::Method::PATCH => Method::Patch, - _ => { + ref ext => Method::from_str(ext.as_str()).unwrap_or_else(|_| { errors.push(RequestError::BadMethod(hyper.method.clone())); Method::Get - } + }), }; // TODO: Keep around not just the path/query, but the rest, if there? diff --git a/testbench/src/client.rs b/testbench/src/client.rs index 953b2f907a..607ba9fa11 100644 --- a/testbench/src/client.rs +++ b/testbench/src/client.rs @@ -26,7 +26,9 @@ impl Client { .connect_timeout(Duration::from_secs(5)) } - pub fn request(&self, server: &Server, method: Method, url: &str) -> Result { + pub fn request(&self, server: &Server, method: M, url: &str) -> Result + where M: AsRef + { let uri = match Uri::parse_any(url).map_err(|e| e.into_owned())? { Uri::Origin(uri) => { let proto = if server.tls { "https" } else { "http" }; @@ -45,7 +47,7 @@ impl Client { uri => return Err(Error::InvalidUri(uri.into_owned())), }; - let method = reqwest::Method::from_str(method.as_str()).unwrap(); + let method = reqwest::Method::from_str(method.as_ref()).unwrap(); Ok(self.client.request(method, uri.to_string())) } diff --git a/testbench/src/servers/http_extensions.rs b/testbench/src/servers/http_extensions.rs new file mode 100644 index 0000000000..42b990c76e --- /dev/null +++ b/testbench/src/servers/http_extensions.rs @@ -0,0 +1,29 @@ +//! Test that HTTP method extensions unlike POST or GET work. + +use crate::prelude::*; + +use rocket::http::Method; + +#[route(PROPFIND, uri = "/")] +fn route() -> &'static str { + "Hello, World!" +} + +pub fn test_http_extensions() -> Result<()> { + let server = spawn! { + Rocket::default().mount("/", routes![route]) + }?; + + let client = Client::default(); + let response = client.request(&server, Method::PropFind, "/")?.send()?; + assert_eq!(response.status(), 200); + assert_eq!(response.text()?, "Hello, World!"); + + // Make sure that verbs outside of extensions are marked as errors + let res = client.request(&server, "BAKEMEACOOKIE", "/")?.send()?; + assert_eq!(res.status(), 400); + + Ok(()) +} + +register!(test_http_extensions); diff --git a/testbench/src/servers/mod.rs b/testbench/src/servers/mod.rs index 30fd6ff97c..d05aa00c69 100644 --- a/testbench/src/servers/mod.rs +++ b/testbench/src/servers/mod.rs @@ -1,5 +1,6 @@ pub mod ignite_failure; pub mod bind; +pub mod http_extensions; pub mod infinite_stream; pub mod tls_resolver; pub mod mtls; From ef1cfa0965a8a1ed67a68c35897b6dbb020a1224 Mon Sep 17 00:00:00 2001 From: Sergio Benitez Date: Sat, 17 Aug 2024 04:25:15 -0700 Subject: [PATCH 11/11] Impl 'TryFrom<&str>' for 'Method'. Also implements 'From' for 'ParseMethodError'. --- core/http/src/method.rs | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/core/http/src/method.rs b/core/http/src/method.rs index cf6129ea5b..2b273181b2 100644 --- a/core/http/src/method.rs +++ b/core/http/src/method.rs @@ -316,6 +316,12 @@ pub struct ParseMethodError; impl std::error::Error for ParseMethodError { } +impl From for ParseMethodError { + fn from(infallible: std::convert::Infallible) -> Self { + match infallible {} + } +} + impl fmt::Display for ParseMethodError { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.write_str("invalid HTTP method") @@ -331,6 +337,14 @@ impl FromStr for Method { } } +impl TryFrom<&str> for Method { + type Error = ParseMethodError; + + fn try_from(s: &str) -> Result { + Self::try_from(s.as_bytes()) + } +} + impl AsRef for Method { fn as_ref(&self) -> &str { self.as_str()