From 7f9be07e8dcf20ca9b6254d87dd0448fbf711e97 Mon Sep 17 00:00:00 2001 From: Quentin Gliech Date: Mon, 21 Nov 2022 18:45:53 +0100 Subject: [PATCH] Move the BoxCloneSyncService to mas-http --- crates/http/src/client.rs | 10 +- crates/http/src/layers/client.rs | 28 ++++-- crates/http/src/lib.rs | 2 + crates/http/src/service.rs | 104 +++++++++++++++++++++ crates/oidc-client/src/http_service/mod.rs | 94 +------------------ 5 files changed, 133 insertions(+), 105 deletions(-) create mode 100644 crates/http/src/service.rs diff --git a/crates/http/src/client.rs b/crates/http/src/client.rs index 4c3b79c4..a8e89aff 100644 --- a/crates/http/src/client.rs +++ b/crates/http/src/client.rs @@ -27,8 +27,8 @@ use hyper::{ use hyper_rustls::{HttpsConnector, HttpsConnectorBuilder}; use thiserror::Error; use tower::{ - util::{BoxCloneService, MapErrLayer, MapResponseLayer}, - Layer, Service, ServiceExt, + util::{MapErrLayer, MapResponseLayer}, + Layer, Service, }; use crate::{ @@ -36,7 +36,7 @@ use crate::{ client::{ClientLayer, ClientResponse}, otel::{TraceDns, TraceLayer}, }, - BoxError, + BoxCloneSyncService, BoxError, }; #[cfg(all(not(feature = "webpki-roots"), not(feature = "native-roots")))] @@ -237,7 +237,7 @@ where pub async fn client( operation: &'static str, ) -> Result< - BoxCloneService, Response>, ClientError>, + BoxCloneSyncService, Response>, ClientError>, ClientInitError, > where @@ -254,7 +254,7 @@ where }), ClientLayer::new(operation), ); - let client = layer.layer(client).boxed_clone(); + let client = BoxCloneSyncService::new(layer.layer(client)); Ok(client) } diff --git a/crates/http/src/layers/client.rs b/crates/http/src/layers/client.rs index c6082d9a..89ff5b8a 100644 --- a/crates/http/src/layers/client.rs +++ b/crates/http/src/layers/client.rs @@ -16,17 +16,18 @@ use std::{marker::PhantomData, time::Duration}; use http::{header::USER_AGENT, HeaderValue, Request, Response}; use tower::{ - limit::ConcurrencyLimitLayer, timeout::TimeoutLayer, util::BoxCloneService, Layer, Service, - ServiceExt, + limit::{ConcurrencyLimit, ConcurrencyLimitLayer}, + timeout::{Timeout, TimeoutLayer}, + Layer, Service, }; use tower_http::{ - decompression::{DecompressionBody, DecompressionLayer}, - follow_redirect::FollowRedirectLayer, - set_header::SetRequestHeaderLayer, + decompression::{Decompression, DecompressionBody, DecompressionLayer}, + follow_redirect::{FollowRedirect, FollowRedirectLayer}, + set_header::{SetRequestHeader, SetRequestHeaderLayer}, }; use super::otel::TraceLayer; -use crate::BoxError; +use crate::{otel::TraceHttpClient, BoxError}; static MAS_USER_AGENT: HeaderValue = HeaderValue::from_static("matrix-authentication-service/0.0.1"); @@ -47,17 +48,27 @@ impl ClientLayer { } } +#[allow(dead_code)] pub type ClientResponse = Response>; impl Layer for ClientLayer where - S: Service, Response = Response, Error = E> + Clone + Send + 'static, + S: Service, Response = Response, Error = E> + + Clone + + Send + + Sync + + 'static, ReqBody: http_body::Body + Default + Send + 'static, ResBody: http_body::Body + Sync + Send + 'static, S::Future: Send + 'static, E: Into, { - type Service = BoxCloneService, ClientResponse, BoxError>; + type Service = Decompression< + SetRequestHeader< + TraceHttpClient>>>>, + HeaderValue, + >, + >; fn layer(&self, inner: S) -> Self::Service { // Note that most layers here just forward the error type. Two notables @@ -78,6 +89,5 @@ where TimeoutLayer::new(Duration::from_secs(10)), ) .layer(inner) - .boxed_clone() } } diff --git a/crates/http/src/lib.rs b/crates/http/src/lib.rs index 642e56f8..3b251725 100644 --- a/crates/http/src/lib.rs +++ b/crates/http/src/lib.rs @@ -28,6 +28,7 @@ mod client; mod ext; mod layers; +mod service; #[cfg(feature = "client")] pub use self::client::{client, make_traced_connector, make_untraced_client, ClientInitError}; @@ -44,6 +45,7 @@ pub use self::{ otel, server::ServerLayer, }, + service::{BoxCloneSyncService, HttpService}, }; pub(crate) type BoxError = Box; diff --git a/crates/http/src/service.rs b/crates/http/src/service.rs new file mode 100644 index 00000000..a05649d5 --- /dev/null +++ b/crates/http/src/service.rs @@ -0,0 +1,104 @@ +// Copyright 2022 The Matrix.org Foundation C.I.C. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use std::{ + fmt, + task::{Context, Poll}, +}; + +use bytes::Bytes; +use futures_util::future::BoxFuture; +use tower::{BoxError, Service, ServiceExt}; + +/// Type for the underlying HTTP service. +/// +/// Allows implementors to use different libraries that provide a [`Service`] +/// that implements [`Clone`] + [`Send`] + [`Sync`]. +pub type HttpService = BoxCloneSyncService, http::Response, BoxError>; + +impl fmt::Debug for HttpService { + fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt.debug_struct("HttpService").finish() + } +} + +/// A [`Clone`] + [`Send`] + [`Sync`] boxed [`Service`]. +/// +/// [`BoxCloneSyncService`] turns a service into a trait object, allowing the +/// response future type to be dynamic, and allowing the service to be cloned. +#[allow(clippy::type_complexity)] +pub struct BoxCloneSyncService( + Box< + dyn CloneSyncService>>, + >, +); + +impl BoxCloneSyncService { + /// Create a new `BoxCloneSyncService`. + pub fn new(inner: S) -> Self + where + S: Service + Clone + Send + Sync + 'static, + S::Future: Send + 'static, + { + let inner = inner.map_future(|f| Box::pin(f) as _); + Self(Box::new(inner)) + } +} + +impl Service for BoxCloneSyncService { + type Response = U; + type Error = E; + type Future = BoxFuture<'static, Result>; + + #[inline] + fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll> { + self.0.poll_ready(cx) + } + + #[inline] + fn call(&mut self, request: T) -> Self::Future { + self.0.call(request) + } +} + +impl Clone for BoxCloneSyncService { + fn clone(&self) -> Self { + Self(self.0.clone_sync_box()) + } +} + +trait CloneSyncService: Service + Send + Sync { + fn clone_sync_box( + &self, + ) -> Box< + dyn CloneSyncService< + R, + Response = Self::Response, + Error = Self::Error, + Future = Self::Future, + >, + >; +} + +impl CloneSyncService for T +where + T: Service + Send + Sync + Clone + 'static, +{ + fn clone_sync_box( + &self, + ) -> Box> + { + Box::new(self.clone()) + } +} diff --git a/crates/oidc-client/src/http_service/mod.rs b/crates/oidc-client/src/http_service/mod.rs index f896f720..4051f5b5 100644 --- a/crates/oidc-client/src/http_service/mod.rs +++ b/crates/oidc-client/src/http_service/mod.rs @@ -12,98 +12,10 @@ // See the License for the specific language governing permissions and // limitations under the License. -//! Traits to implement to provide a custom HTTP service for `Client`. - -use std::{ - fmt, - task::{Context, Poll}, -}; - -use bytes::Bytes; -use futures::future::BoxFuture; -use tower::{BoxError, Service, ServiceExt}; +//! Reexports of traits to implement to provide a custom HTTP service for +//! `Client`. #[cfg(feature = "hyper")] pub mod hyper; -/// Type for the underlying HTTP service. -/// -/// Allows implementors to use different libraries that provide a [`Service`] -/// that implements [`Clone`] + [`Send`] + [`Sync`]. -pub type HttpService = BoxCloneSyncService, http::Response, BoxError>; - -impl fmt::Debug for HttpService { - fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { - fmt.debug_struct("HttpService").finish() - } -} - -/// A [`Clone`] + [`Send`] + [`Sync`] boxed [`Service`]. -/// -/// [`BoxCloneSyncService`] turns a service into a trait object, allowing the -/// response future type to be dynamic, and allowing the service to be cloned. -#[allow(clippy::type_complexity)] -pub struct BoxCloneSyncService( - Box< - dyn CloneSyncService>>, - >, -); - -impl BoxCloneSyncService { - /// Create a new `BoxCloneSyncService`. - pub fn new(inner: S) -> Self - where - S: Service + Clone + Send + Sync + 'static, - S::Future: Send + 'static, - { - let inner = inner.map_future(|f| Box::pin(f) as _); - Self(Box::new(inner)) - } -} - -impl Service for BoxCloneSyncService { - type Response = U; - type Error = E; - type Future = BoxFuture<'static, Result>; - - #[inline] - fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll> { - self.0.poll_ready(cx) - } - - #[inline] - fn call(&mut self, request: T) -> Self::Future { - self.0.call(request) - } -} - -impl Clone for BoxCloneSyncService { - fn clone(&self) -> Self { - Self(self.0.clone_sync_box()) - } -} - -trait CloneSyncService: Service + Send + Sync { - fn clone_sync_box( - &self, - ) -> Box< - dyn CloneSyncService< - R, - Response = Self::Response, - Error = Self::Error, - Future = Self::Future, - >, - >; -} - -impl CloneSyncService for T -where - T: Service + Send + Sync + Clone + 'static, -{ - fn clone_sync_box( - &self, - ) -> Box> - { - Box::new(self.clone()) - } -} +pub use mas_http::{BoxCloneSyncService, HttpService};