Skip to content

Commit 137c931

Browse files
authored
feat(layer): 分离请求和响应头移除功能 (#27)
1 parent 2f98ffe commit 137c931

File tree

8 files changed

+264
-24
lines changed

8 files changed

+264
-24
lines changed

Cargo.lock

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

crates/layer/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ serde = { workspace = true, features = ["derive"] }
1919
tower = { workspace = true, features = ["limit"] }
2020
tower-http = { workspace = true, features = ["trace", "set-header", "timeout", "cors"] }
2121
tracing = { workspace = true }
22+
pin-project-lite = { workspace = true }
2223

2324
[dev-dependencies]
2425
serde_yaml = { workspace = true }
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
mod remove_request_header;
2+
mod remove_response_header;
3+
4+
use http::{HeaderName, Request, Response};
5+
pub use remove_request_header::{
6+
MakeRemoveRequestHeaderRouteLayer, RemoveRequestHeader, RemoveRequestHeaderLayer,
7+
};
8+
pub use remove_response_header::{
9+
MakeRemoveResponseHeaderRouteLayer, RemoveResponseHeader, RemoveResponseHeaderLayer,
10+
ResponseFuture,
11+
};
12+
13+
pub trait Removable {
14+
fn remove(&mut self, name: &HeaderName);
15+
}
16+
17+
impl<ResBody> Removable for Response<ResBody> {
18+
fn remove(&mut self, name: &HeaderName) {
19+
let headers = self.headers_mut();
20+
if headers.contains_key(name) {
21+
headers.remove(name);
22+
}
23+
}
24+
}
25+
26+
impl<ReqBody> Removable for Request<ReqBody> {
27+
fn remove(&mut self, name: &HeaderName) {
28+
let headers = self.headers_mut();
29+
if headers.contains_key(name) {
30+
headers.remove(name);
31+
}
32+
}
33+
}

crates/layer/src/remove_header.rs renamed to crates/layer/src/remove_header/remove_request_header.rs

Lines changed: 21 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,64 +1,64 @@
11
use std::{str::FromStr, sync::Arc};
22

33
use crate::make::MakeRouteLayer;
4-
use http::{HeaderName, Request};
4+
use crate::remove_header::Removable;
5+
use http::HeaderName;
56
use satex_core::component::Args;
67
use satex_core::{component::Configurable, Error};
78
use satex_macro::make;
89
use tower::{Layer, Service};
910

10-
#[make(kind = RemoveHeader)]
11-
pub struct MakeRemoveHeaderRouteLayer {
11+
#[make(kind = RemoveRequestHeader)]
12+
pub struct MakeRemoveRequestHeaderRouteLayer {
1213
name: String,
1314
}
1415

15-
impl MakeRouteLayer for MakeRemoveHeaderRouteLayer {
16-
type Layer = RemoveHeaderRouteLayer;
16+
impl MakeRouteLayer for MakeRemoveRequestHeaderRouteLayer {
17+
type Layer = RemoveRequestHeaderLayer;
1718

1819
fn make(&self, args: Args) -> Result<Self::Layer, Error> {
1920
Config::with_args(args).and_then(|config| {
2021
HeaderName::from_str(&config.name)
2122
.map_err(Error::new)
22-
.map(|name| RemoveHeaderRouteLayer {
23-
name: Arc::new(name),
24-
})
23+
.map(RemoveRequestHeaderLayer::new)
2524
})
2625
}
2726
}
2827

2928
#[derive(Debug, Clone)]
30-
pub struct RemoveHeaderRouteLayer {
29+
pub struct RemoveRequestHeaderLayer {
3130
name: Arc<HeaderName>,
3231
}
3332

34-
impl RemoveHeaderRouteLayer {
35-
pub fn new(name: &str) -> Self {
33+
impl RemoveRequestHeaderLayer {
34+
pub fn new(name: HeaderName) -> Self {
3635
Self {
37-
name: Arc::new(HeaderName::from_str(name).unwrap()),
36+
name: Arc::new(name),
3837
}
3938
}
4039
}
4140

42-
impl<S> Layer<S> for RemoveHeaderRouteLayer {
43-
type Service = RemoveHeader<S>;
41+
impl<S> Layer<S> for RemoveRequestHeaderLayer {
42+
type Service = RemoveRequestHeader<S>;
4443

4544
fn layer(&self, inner: S) -> Self::Service {
46-
RemoveHeader {
45+
RemoveRequestHeader {
4746
name: self.name.clone(),
4847
inner,
4948
}
5049
}
5150
}
5251

5352
#[derive(Debug, Clone)]
54-
pub struct RemoveHeader<S> {
53+
pub struct RemoveRequestHeader<S> {
5554
inner: S,
5655
name: Arc<HeaderName>,
5756
}
5857

59-
impl<S, ReqBody> Service<Request<ReqBody>> for RemoveHeader<S>
58+
impl<S, Req> Service<Req> for RemoveRequestHeader<S>
6059
where
61-
S: Service<Request<ReqBody>>,
60+
S: Service<Req>,
61+
Req: Removable,
6262
{
6363
type Response = S::Response;
6464

@@ -73,9 +73,8 @@ where
7373
self.inner.poll_ready(cx)
7474
}
7575

76-
fn call(&mut self, mut request: Request<ReqBody>) -> Self::Future {
77-
let headers = request.headers_mut();
78-
headers.remove(self.name.as_ref());
79-
self.inner.call(request)
76+
fn call(&mut self, mut req: Req) -> Self::Future {
77+
req.remove(self.name.as_ref());
78+
self.inner.call(req)
8079
}
8180
}
Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,124 @@
1+
use crate::make::MakeRouteLayer;
2+
use crate::remove_header::Removable;
3+
use http::HeaderName;
4+
use pin_project_lite::pin_project;
5+
use satex_core::component::Args;
6+
use satex_core::{component::Configurable, Error};
7+
use satex_macro::make;
8+
use std::pin::Pin;
9+
use std::task::{ready, Context, Poll};
10+
use std::{str::FromStr, sync::Arc};
11+
use tower::{Layer, Service};
12+
13+
#[make(kind = RemoveResponseHeader)]
14+
pub struct MakeRemoveResponseHeaderRouteLayer {
15+
name: String,
16+
}
17+
18+
impl MakeRouteLayer for MakeRemoveResponseHeaderRouteLayer {
19+
type Layer = RemoveResponseHeaderLayer;
20+
21+
fn make(&self, args: Args) -> Result<Self::Layer, Error> {
22+
Config::with_args(args).and_then(|config| {
23+
HeaderName::from_str(&config.name)
24+
.map_err(Error::new)
25+
.map(RemoveResponseHeaderLayer::new)
26+
})
27+
}
28+
}
29+
30+
#[derive(Debug, Clone)]
31+
pub struct RemoveResponseHeaderLayer {
32+
name: Arc<HeaderName>,
33+
}
34+
35+
impl RemoveResponseHeaderLayer {
36+
pub fn new(name: HeaderName) -> Self {
37+
Self {
38+
name: Arc::new(name),
39+
}
40+
}
41+
}
42+
43+
impl<S> Layer<S> for RemoveResponseHeaderLayer {
44+
type Service = RemoveResponseHeader<S>;
45+
46+
fn layer(&self, inner: S) -> Self::Service {
47+
RemoveResponseHeader {
48+
name: self.name.clone(),
49+
inner,
50+
}
51+
}
52+
}
53+
54+
#[derive(Debug, Clone)]
55+
pub struct RemoveResponseHeader<S> {
56+
inner: S,
57+
name: Arc<HeaderName>,
58+
}
59+
60+
impl<S, Req, Res> Service<Req> for RemoveResponseHeader<S>
61+
where
62+
S: Service<Req, Response=Res>,
63+
Res: Removable,
64+
{
65+
type Response = S::Response;
66+
67+
type Error = S::Error;
68+
69+
type Future = ResponseFuture<S::Future>;
70+
71+
fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
72+
self.inner.poll_ready(cx)
73+
}
74+
75+
fn call(&mut self, request: Req) -> Self::Future {
76+
let name = self.name.clone();
77+
ResponseFuture::new(name, self.inner.call(request))
78+
}
79+
}
80+
81+
pin_project! {
82+
#[project = ResponseFutureProj]
83+
#[project_replace = MapProjReplace]
84+
pub enum ResponseFuture<F> {
85+
Incomplete {
86+
name: Arc<HeaderName>,
87+
#[pin]
88+
future: F,
89+
},
90+
Complete,
91+
}
92+
}
93+
94+
impl<F> ResponseFuture<F> {
95+
pub fn new(name: Arc<HeaderName>, future: F) -> Self {
96+
Self::Incomplete { name, future }
97+
}
98+
}
99+
100+
impl<F, Res, E> Future for ResponseFuture<F>
101+
where
102+
F: Future<Output=Result<Res, E>>,
103+
Res: Removable,
104+
{
105+
type Output = F::Output;
106+
107+
fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
108+
match self.as_mut().project() {
109+
ResponseFutureProj::Incomplete { future, .. } => match ready!(future.poll(cx)) {
110+
Ok(mut res) => match self.project_replace(ResponseFuture::Complete) {
111+
MapProjReplace::Incomplete { name, .. } => {
112+
res.remove(name.as_ref());
113+
Poll::Ready(Ok(res))
114+
}
115+
MapProjReplace::Complete => unreachable!(),
116+
},
117+
Err(e) => Poll::Ready(Err(e)),
118+
},
119+
ResponseFutureProj::Complete { .. } => {
120+
panic!("polled after completion")
121+
}
122+
}
123+
}
124+
}
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
mod identify;
2+
3+
use crate::identify::Identify;
4+
use http::header::HOST;
5+
use http::{HeaderValue, Request};
6+
use satex_core::body::Body;
7+
use satex_core::component::Args;
8+
use satex_layer::make::MakeRouteLayer;
9+
use satex_layer::remove_header::{MakeRemoveRequestHeaderRouteLayer, RemoveRequestHeaderLayer};
10+
use tower::{Layer, Service};
11+
12+
async fn _layer(layer: RemoveRequestHeaderLayer) {
13+
let mut service = layer.layer(Identify::new(|request: Request<Body>| {
14+
!request.headers().contains_key(HOST)
15+
}));
16+
let mut request = Request::builder().uri("/").body(Body::empty()).unwrap();
17+
request
18+
.headers_mut()
19+
.insert(HOST, HeaderValue::from_static("127.0.0.1:3000"));
20+
21+
let result = service.call(request).await;
22+
assert!(result.is_ok());
23+
}
24+
25+
#[tokio::test]
26+
async fn make_with_shortcut() {
27+
let args = Args::shortcut("host");
28+
let layer = MakeRemoveRequestHeaderRouteLayer.make(args).unwrap();
29+
_layer(layer).await;
30+
}
31+
32+
#[tokio::test]
33+
async fn make_with_full() {
34+
let yaml = "name: host";
35+
let value = serde_yaml::from_str(yaml).unwrap();
36+
let args = Args::full(&value);
37+
let layer = MakeRemoveRequestHeaderRouteLayer.make(args).unwrap();
38+
_layer(layer).await;
39+
}
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
use http::header::CONTENT_TYPE;
2+
use http::{HeaderValue, Request, Response};
3+
use satex_core::body::Body;
4+
use satex_core::component::Args;
5+
use satex_layer::make::MakeRouteLayer;
6+
use satex_layer::remove_header::{
7+
MakeRemoveResponseHeaderRouteLayer, RemoveResponseHeaderLayer,
8+
};
9+
use std::convert::Infallible;
10+
use tower::{service_fn, Layer, Service};
11+
12+
async fn _layer(layer: RemoveResponseHeaderLayer) {
13+
let mut service = layer.layer(service_fn(|_| async move {
14+
let mut response = Response::new(Body::empty());
15+
response
16+
.headers_mut()
17+
.insert(CONTENT_TYPE, HeaderValue::from_static("application/json"));
18+
Ok::<_, Infallible>(response)
19+
}));
20+
let request = Request::new(Body::empty());
21+
22+
let response = service.call(request).await.unwrap();
23+
assert!(!response.headers().contains_key(CONTENT_TYPE))
24+
}
25+
26+
#[tokio::test]
27+
async fn make_with_shortcut() {
28+
let args = Args::shortcut("content-type");
29+
let layer = MakeRemoveResponseHeaderRouteLayer.make(args).unwrap();
30+
_layer(layer).await;
31+
}
32+
33+
#[tokio::test]
34+
async fn make_with_full() {
35+
let yaml = "name: content-type";
36+
let value = serde_yaml::from_str(yaml).unwrap();
37+
let args = Args::full(&value);
38+
let layer = MakeRemoveResponseHeaderRouteLayer.make(args).unwrap();
39+
_layer(layer).await;
40+
}

src/registry/layer.rs

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,9 @@ use satex_core::BoxError;
66
use satex_layer::concurrency_limit::MakeConcurrencyLimitRouteLayer;
77
use satex_layer::cors::MakeCorsRouteLayer;
88
use satex_layer::make::{ArcMakeRouteLayer, MakeRouteLayer};
9-
use satex_layer::remove_header::MakeRemoveHeaderRouteLayer;
9+
use satex_layer::remove_header::{
10+
MakeRemoveRequestHeaderRouteLayer, MakeRemoveResponseHeaderRouteLayer,
11+
};
1012
use satex_layer::set_header::{MakeSetRequestHeaderRouteLayer, MakeSetResponseHeaderRouteLayer};
1113
use satex_layer::set_method::MakeSetMethodRouteLayer;
1214
use satex_layer::set_prefix::MakeSetPrefixRouteLayer;
@@ -34,7 +36,8 @@ impl MakeRouteLayerRegistry {
3436
MakeSetMethodRouteLayer,
3537
MakeSetRequestHeaderRouteLayer,
3638
MakeSetResponseHeaderRouteLayer,
37-
MakeRemoveHeaderRouteLayer,
39+
MakeRemoveRequestHeaderRouteLayer,
40+
MakeRemoveResponseHeaderRouteLayer,
3841
MakeStripPrefixRouteLayer,
3942
MakeTraceRouteLayer,
4043
MakeTimeoutRouteLayer,

0 commit comments

Comments
 (0)