From cc68de4b566e39bbe50530442945099105f2c48f Mon Sep 17 00:00:00 2001 From: Sebastian Rollen Date: Thu, 25 Jan 2024 21:33:16 -0500 Subject: [PATCH 1/4] use http-1.0.0 --- Cargo.toml | 5 +++-- http-cache-tower/Cargo.toml | 43 +++++++++++++++++++++++++++++++++++++ 2 files changed, 46 insertions(+), 2 deletions(-) create mode 100644 http-cache-tower/Cargo.toml diff --git a/Cargo.toml b/Cargo.toml index 08dfd83..917322b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,5 +6,6 @@ members = [ "http-cache-surf", "http-cache-quickcache", "http-cache-darkbird", - "http-cache-mokadeser" -] \ No newline at end of file + "http-cache-mokadeser", + "http-cache-tower" +] diff --git a/http-cache-tower/Cargo.toml b/http-cache-tower/Cargo.toml new file mode 100644 index 0000000..d91e617 --- /dev/null +++ b/http-cache-tower/Cargo.toml @@ -0,0 +1,43 @@ +[package] +name = "http-cache-tower" +version = "0.1.0" +description = "http-cache middleware implementation for tower" +authors = ["Christian Haynes <06chaynes@gmail.com>", "Kat Marchán "] +repository = "https://github.com/06chaynes/http-cache.git" +license = "MIT OR Apache-2.0" +readme = "README.md" +keywords = ["cache", "http", "middleware", "tower"] +categories = [ + "caching", + "web-programming::http-client" +] +edition = "2021" + +[dependencies] +futures-core = "0.3.23" +futures-util = { version = "0.3.23", default-features = false, features = [] } +http = "1.0.0" +http-body = "1.0.0" +pin-project-lite = "0.2.9" +tower-layer = "0.3.1" +tower-service = "0.3.2" +http-cache-semantics = "1.0.1" +serde = { version = "1.0.144", features = ["derive"] } +url = { version = "2.2.2", features = ["serde"] } +tower = { version = "0.4.13", features = ["util"] } + +[dependencies.http-cache] +path = "../http-cache" +version = "0.18.0" + +[dev-dependencies] +tokio = { version = "1.20.1", features = ["macros", "rt-multi-thread"] } + +[features] +default = ["manager-cacache"] +manager-cacache = ["http-cache/manager-cacache"] +manager-moka = ["http-cache/manager-moka"] + +[package.metadata.docs.rs] +all-features = true +rustdoc-args = ["--cfg", "docsrs"] From 222e0c8b2277ece88de037f71382bb93b4dbce1d Mon Sep 17 00:00:00 2001 From: Sebastian Rollen Date: Thu, 25 Jan 2024 21:33:23 -0500 Subject: [PATCH 2/4] clone request --- http-cache-tower/src/lib.rs | 151 ++++++++++++++++++++++++++++++++++++ 1 file changed, 151 insertions(+) create mode 100644 http-cache-tower/src/lib.rs diff --git a/http-cache-tower/src/lib.rs b/http-cache-tower/src/lib.rs new file mode 100644 index 0000000..f6692d5 --- /dev/null +++ b/http-cache-tower/src/lib.rs @@ -0,0 +1,151 @@ +use futures_core::ready; +use futures_util::future::Either; + +use std::{ + convert::TryFrom, + convert::TryInto, + future::Future, + mem, + pin::Pin, + str, + str::FromStr, + task::{Context, Poll}, +}; + +use http::{ + header::{HeaderName, CACHE_CONTROL}, + request, HeaderMap, HeaderValue, Method, Request, Response, StatusCode, + Uri, Version, +}; +use http_body::Body; +use http_cache::{BoxError, CacheManager, Result}; +use pin_project_lite::pin_project; +use tower::util::Oneshot; +use tower_layer::Layer; +use tower_service::Service; + +pub use http_cache::{CacheMode, CacheOptions, HttpCache, HttpResponse}; + +#[cfg(feature = "manager-cacache")] +#[cfg_attr(docsrs, doc(cfg(feature = "manager-cacache")))] +pub use http_cache::CACacheManager; + +#[cfg(feature = "manager-moka")] +#[cfg_attr(docsrs, doc(cfg(feature = "manager-moka")))] +pub use http_cache::{MokaCache, MokaCacheBuilder, MokaManager}; + +/// Wrapper for [`HttpCache`] +#[derive(Debug)] +pub struct Cache { + inner: S, + cache: HttpCache, +} + +impl Cache +where + T: Clone + CacheManager, +{ + /// Create a new [`Cache`]. + pub fn new(inner: S, cache: HttpCache) -> Self { + Self { inner, cache } + } + + /// Returns a new [`Layer`] that wraps services with a `Cache` middleware. + pub fn layer(&self) -> CacheLayer { + CacheLayer::new(self.cache.clone()) + } +} + +/// [`Layer`] with a [`Service`] to cache responses. +#[derive(Clone, Debug)] +pub struct CacheLayer { + cache: HttpCache, +} + +impl CacheLayer +where + T: CacheManager, +{ + /// Create a new [`CacheLayer`]. + pub fn new(cache: HttpCache) -> Self { + Self { cache } + } +} + +impl Layer for CacheLayer +where + S: Clone, + T: Clone + CacheManager, +{ + type Service = Cache; + + fn layer(&self, inner: S) -> Self::Service { + Cache { inner, cache: self.cache.clone() } + } +} + +impl Service> for Cache +where + S: Clone + Service, Response = Response>, + ReqBody: Body + Clone, + T: Clone + CacheManager, +{ + type Response = Response; + type Error = S::Error; + type Future = ResponseFuture; + + fn poll_ready( + &mut self, + cx: &mut Context<'_>, + ) -> Poll> { + self.inner.poll_ready(cx) + } + + fn call(&mut self, req: Request) -> Self::Future { + let service = self.inner.clone(); + let mut service = mem::replace(&mut self.inner, service); + ResponseFuture { + req: req.clone(), + future: Either::Left(service.call(req)), + service, + cache: self.cache.clone(), + } + } +} + +pin_project! { + /// Response future for [`Cache`]. + #[derive(Debug)] + pub struct ResponseFuture + where + S: Service>, + T: CacheManager, + { + #[pin] + future: Either>>, + service: S, + req: Request, + cache: HttpCache, + } +} + +impl Future for ResponseFuture +where + S: Service, Response = Response> + Clone, + ReqBody: Body + Clone, + T: CacheManager + Clone, +{ + type Output = std::result::Result, S::Error>; + + fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + let mut this = self.project(); + let request_parts = this.req.clone().into_parts().0; + // TODO: figure out how to run an async methods here + // let action = this + // .cache + // .before_request(&request_parts) + // .await; + let res = ready!(this.future.as_mut().poll(cx)?); + Poll::Ready(Ok(res)) + } +} From 3fe8c0688262d918a8449258b1f20f4d4ba47f07 Mon Sep 17 00:00:00 2001 From: Sebastian Rollen Date: Sun, 28 Jan 2024 20:20:55 -0500 Subject: [PATCH 3/4] manual future state machine --- http-cache-tower/src/lib.rs | 40 +++++++++++++++++++++++++++++-------- 1 file changed, 32 insertions(+), 8 deletions(-) diff --git a/http-cache-tower/src/lib.rs b/http-cache-tower/src/lib.rs index f6692d5..9c8ada9 100644 --- a/http-cache-tower/src/lib.rs +++ b/http-cache-tower/src/lib.rs @@ -109,6 +109,7 @@ where future: Either::Left(service.call(req)), service, cache: self.cache.clone(), + state: ResponseFutureState::Init, } } } @@ -126,6 +127,7 @@ pin_project! { service: S, req: Request, cache: HttpCache, + state: ResponseFutureState, } } @@ -139,13 +141,35 @@ where fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { let mut this = self.project(); - let request_parts = this.req.clone().into_parts().0; - // TODO: figure out how to run an async methods here - // let action = this - // .cache - // .before_request(&request_parts) - // .await; - let res = ready!(this.future.as_mut().poll(cx)?); - Poll::Ready(Ok(res)) + let mut curr_state = this.state; + match this.state.as_mut().project() { + ResponseFutureProj::Init => { + let request_parts = this.req.clone().into_parts().0; + let fut = this.cache.before_request(&request_parts); + curr_state.set(ResponseFutureState::BeforeRequest { fut }); + cx.waker().wake_by_ref(); + Poll::Pending + } + ResponseFutureProj::BeforeRequest { fut } => { + let res = ready!(fut.poll(cx)); + // TODO: What do we do with res here? + curr_state.set(ResponseFutureState::Inner); + cx.waker().wake_by_ref(); + Poll::Pending + } + ResponseFutureProj::Inner => this.future.as_mut().poll(cx), + } + } +} + +pin_project! { + #[project = ResponseFutureProj] + enum ResponseFutureState { + Init, + BeforeRequest { + #[pin] + fut: F + }, + Inner, } } From f2bca32e8b3c602138b64db40dc341ce460f7532 Mon Sep 17 00:00:00 2001 From: Christian Haynes <06chaynes@gmail.com> Date: Sun, 28 Jan 2024 20:49:04 -0500 Subject: [PATCH 4/4] updated http and http-cache-semantics --- http-cache-darkbird/Cargo.toml | 4 ++-- http-cache-mokadeser/Cargo.toml | 4 ++-- http-cache-quickcache/Cargo.toml | 4 ++-- http-cache-reqwest/Cargo.toml | 2 +- http-cache-surf/Cargo.toml | 4 ++-- http-cache-tower/Cargo.toml | 2 +- http-cache/Cargo.toml | 5 ++--- 7 files changed, 12 insertions(+), 13 deletions(-) diff --git a/http-cache-darkbird/Cargo.toml b/http-cache-darkbird/Cargo.toml index bbbe340..50cc898 100644 --- a/http-cache-darkbird/Cargo.toml +++ b/http-cache-darkbird/Cargo.toml @@ -18,7 +18,7 @@ rust-version = "1.67.1" [dependencies] async-trait = "0.1.72" darkbird = "6.1.8" -http-cache-semantics = "1.0.1" +http-cache-semantics = "2.0.1" serde = { version = "1.0.178", features = ["derive"] } thiserror = "1.0.44" @@ -28,7 +28,7 @@ version = "0.18.0" default-features = false [dev-dependencies] -http = "0.2.9" +http = "1.0.0" reqwest = { version = "0.11.18", default-features = false } reqwest-middleware = "0.2.2" tokio = { version = "1.29.1", features = [ "macros", "rt", "rt-multi-thread" ] } diff --git a/http-cache-mokadeser/Cargo.toml b/http-cache-mokadeser/Cargo.toml index d16add7..e863bd8 100644 --- a/http-cache-mokadeser/Cargo.toml +++ b/http-cache-mokadeser/Cargo.toml @@ -17,7 +17,7 @@ rust-version = "1.67.1" [dependencies] async-trait = "0.1.72" -http-cache-semantics = "1.0.1" +http-cache-semantics = "2.0.1" moka = { version = "0.12.0", features = ["future"]} [dependencies.http-cache] @@ -27,7 +27,7 @@ default-features = false features = ["bincode"] [dev-dependencies] -http = "0.2.9" +http = "1.0.0" reqwest = { version = "0.11.18", default-features = false } reqwest-middleware = "0.2.2" tokio = { version = "1.29.1", features = [ "macros", "rt", "rt-multi-thread" ] } diff --git a/http-cache-quickcache/Cargo.toml b/http-cache-quickcache/Cargo.toml index 93cd76e..df10039 100644 --- a/http-cache-quickcache/Cargo.toml +++ b/http-cache-quickcache/Cargo.toml @@ -18,7 +18,7 @@ rust-version = "1.67.1" [dependencies] async-trait = "0.1.72" bincode = "1.3.3" -http-cache-semantics = "1.0.1" +http-cache-semantics = "2.0.1" serde = { version = "1.0.178", features = ["derive"] } url = { version = "2.4.0", features = ["serde"] } quick_cache = "0.4.0" @@ -30,7 +30,7 @@ default-features = false features = ["bincode"] [dev-dependencies] -http = "0.2.9" +http = "1.0.0" reqwest = { version = "0.11.18", default-features = false } reqwest-middleware = "0.2.2" tokio = { version = "1.29.1", features = [ "macros", "rt", "rt-multi-thread" ] } diff --git a/http-cache-reqwest/Cargo.toml b/http-cache-reqwest/Cargo.toml index 66d001d..4fdcb3b 100644 --- a/http-cache-reqwest/Cargo.toml +++ b/http-cache-reqwest/Cargo.toml @@ -18,7 +18,7 @@ edition = "2021" anyhow = "1.0.72" async-trait = "0.1.72" http = "0.2.9" -http-cache-semantics = "1.0.1" +http-cache-semantics = "2.0.1" reqwest = { version = "0.11.18", default-features = false } reqwest-middleware = "0.2.2" serde = { version = "1.0.178", features = ["derive"] } diff --git a/http-cache-surf/Cargo.toml b/http-cache-surf/Cargo.toml index 73ccd40..706a342 100644 --- a/http-cache-surf/Cargo.toml +++ b/http-cache-surf/Cargo.toml @@ -17,8 +17,8 @@ edition = "2021" [dependencies] anyhow = "1.0.72" async-trait = "0.1.72" -http = "0.2.9" -http-cache-semantics = "1.0.1" +http = "1.0.0" +http-cache-semantics = "2.0.1" http-types = "2.12.0" serde = { version = "1.0.178", features = ["derive"] } surf = { version = "2.3.2", default-features = false } diff --git a/http-cache-tower/Cargo.toml b/http-cache-tower/Cargo.toml index d91e617..6e49447 100644 --- a/http-cache-tower/Cargo.toml +++ b/http-cache-tower/Cargo.toml @@ -21,7 +21,7 @@ http-body = "1.0.0" pin-project-lite = "0.2.9" tower-layer = "0.3.1" tower-service = "0.3.2" -http-cache-semantics = "1.0.1" +http-cache-semantics = "2.0.1" serde = { version = "1.0.144", features = ["derive"] } url = { version = "2.2.2", features = ["serde"] } tower = { version = "0.4.13", features = ["util"] } diff --git a/http-cache/Cargo.toml b/http-cache/Cargo.toml index ff62bae..e60de92 100644 --- a/http-cache/Cargo.toml +++ b/http-cache/Cargo.toml @@ -19,8 +19,8 @@ rust-version = "1.67.1" async-trait = "0.1.72" bincode = { version = "1.3.3", optional = true } cacache = { version = "12.0.0", default-features = false, features = ["mmap"], optional = true } -http = "0.2.9" -http-cache-semantics = "1.0.1" +http = "1.0.0" +http-cache-semantics = "2.0.1" http-types = { version = "2.12.0", default-features = false, optional = true } httpdate = "1.0.2" moka = { version = "0.12.0", features = ["future"], optional = true } @@ -30,7 +30,6 @@ url = { version = "2.4.0", features = ["serde"] } [dev-dependencies] async-attributes = "1.1.2" async-std = { version = "1.12.0" } -http-cache-semantics = "1.0.1" tokio = { version = "1.29.1", features = [ "macros", "rt", "rt-multi-thread" ] } [features]