Skip to content

Commit 822db3b

Browse files
Add MethodFilter::CONNECT (#2961)
1 parent 4b4dac4 commit 822db3b

File tree

6 files changed

+146
-10
lines changed

6 files changed

+146
-10
lines changed

axum-extra/CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,10 @@ and this project adheres to [Semantic Versioning].
77

88
# Unreleased
99

10+
- **added:** Add `RouterExt::typed_connect` ([#2961])
1011
- **added:** Add `json!` for easy construction of JSON responses ([#2962])
1112

13+
[#2961]: https://github.com/tokio-rs/axum/pull/2961
1214
[#2962]: https://github.com/tokio-rs/axum/pull/2962
1315

1416
# 0.10.0

axum-extra/src/routing/mod.rs

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,19 @@ pub trait RouterExt<S>: sealed::Sealed {
131131
T: SecondElementIs<P> + 'static,
132132
P: TypedPath;
133133

134+
/// Add a typed `CONNECT` route to the router.
135+
///
136+
/// The path will be inferred from the first argument to the handler function which must
137+
/// implement [`TypedPath`].
138+
///
139+
/// See [`TypedPath`] for more details and examples.
140+
#[cfg(feature = "typed-routing")]
141+
fn typed_connect<H, T, P>(self, handler: H) -> Self
142+
where
143+
H: axum::handler::Handler<T, S>,
144+
T: SecondElementIs<P> + 'static,
145+
P: TypedPath;
146+
134147
/// Add another route to the router with an additional "trailing slash redirect" route.
135148
///
136149
/// If you add a route _without_ a trailing slash, such as `/foo`, this method will also add a
@@ -255,6 +268,16 @@ where
255268
self.route(P::PATH, axum::routing::trace(handler))
256269
}
257270

271+
#[cfg(feature = "typed-routing")]
272+
fn typed_connect<H, T, P>(self, handler: H) -> Self
273+
where
274+
H: axum::handler::Handler<T, S>,
275+
T: SecondElementIs<P> + 'static,
276+
P: TypedPath,
277+
{
278+
self.route(P::PATH, axum::routing::connect(handler))
279+
}
280+
258281
#[track_caller]
259282
fn route_with_tsr(mut self, path: &str, method_router: MethodRouter<S>) -> Self
260283
where

axum/CHANGELOG.md

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,16 @@ All notable changes to this project will be documented in this file.
55
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
66
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
77

8+
# Unreleased
9+
10+
- **added:** Add support for WebSockets over HTTP/2 ([#2894]).
11+
They can be enabled by changing `get(ws_endpoint)` handlers to `any(ws_endpoint)`
12+
- **added:** Add `MethodFilter::CONNECT`, `routing::connect[_service]`
13+
and `MethodRouter::connect[_service]` ([#2961])
14+
15+
[#2984]: https://github.com/tokio-rs/axum/pull/2984
16+
[#2961]: https://github.com/tokio-rs/axum/pull/2961
17+
818
# 0.8.0
919

1020
## alpha.1
@@ -15,8 +25,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
1525
- **breaking:** Upgrade matchit to 0.8, changing the path parameter syntax from `/:single` and `/*many`
1626
to `/{single}` and `/{*many}`; the old syntax produces a panic to avoid silent change in behavior ([#2645])
1727
- **change:** Update minimum rust version to 1.75 ([#2943])
18-
- **added:** Add support WebSockets over HTTP/2.
19-
They can be enabled by changing `get(ws_endpoint)` handlers to `any(ws_endpoint)`.
2028

2129
[#2473]: https://github.com/tokio-rs/axum/pull/2473
2230
[#2645]: https://github.com/tokio-rs/axum/pull/2645

axum/src/routing/method_filter.rs

Lines changed: 30 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,24 @@ use std::{
99
pub struct MethodFilter(u16);
1010

1111
impl MethodFilter {
12+
/// Match `CONNECT` requests.
13+
///
14+
/// This is useful for implementing HTTP/2's [extended CONNECT method],
15+
/// in which the `:protocol` pseudoheader is read
16+
/// (using [`hyper::ext::Protocol`])
17+
/// and the connection upgraded to a bidirectional byte stream
18+
/// (using [`hyper::upgrade::on`]).
19+
///
20+
/// As seen in the [HTTP Upgrade Token Registry],
21+
/// common uses include WebSockets and proxying UDP or IP –
22+
/// though note that when using [`WebSocketUpgrade`]
23+
/// it's more useful to use [`any`](crate::routing::any)
24+
/// as HTTP/1.1 WebSockets need to support `GET`.
25+
///
26+
/// [extended CONNECT]: https://www.rfc-editor.org/rfc/rfc8441.html#section-4
27+
/// [HTTP Upgrade Token Registry]: https://www.iana.org/assignments/http-upgrade-tokens/http-upgrade-tokens.xhtml
28+
/// [`WebSocketUpgrade`]: crate::extract::WebSocketUpgrade
29+
pub const CONNECT: Self = Self::from_bits(0b0_0000_0001);
1230
/// Match `DELETE` requests.
1331
pub const DELETE: Self = Self::from_bits(0b0_0000_0010);
1432
/// Match `GET` requests.
@@ -71,6 +89,7 @@ impl TryFrom<Method> for MethodFilter {
7189

7290
fn try_from(m: Method) -> Result<Self, NoMatchingMethodFilter> {
7391
match m {
92+
Method::CONNECT => Ok(MethodFilter::CONNECT),
7493
Method::DELETE => Ok(MethodFilter::DELETE),
7594
Method::GET => Ok(MethodFilter::GET),
7695
Method::HEAD => Ok(MethodFilter::HEAD),
@@ -90,6 +109,11 @@ mod tests {
90109

91110
#[test]
92111
fn from_http_method() {
112+
assert_eq!(
113+
MethodFilter::try_from(Method::CONNECT).unwrap(),
114+
MethodFilter::CONNECT
115+
);
116+
93117
assert_eq!(
94118
MethodFilter::try_from(Method::DELETE).unwrap(),
95119
MethodFilter::DELETE
@@ -130,9 +154,11 @@ mod tests {
130154
MethodFilter::TRACE
131155
);
132156

133-
assert!(MethodFilter::try_from(http::Method::CONNECT)
134-
.unwrap_err()
135-
.to_string()
136-
.contains("CONNECT"));
157+
assert!(
158+
MethodFilter::try_from(http::Method::from_bytes(b"CUSTOM").unwrap())
159+
.unwrap_err()
160+
.to_string()
161+
.contains("CUSTOM")
162+
);
137163
}
138164
}

axum/src/routing/method_routing.rs

Lines changed: 78 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,19 @@ macro_rules! top_level_service_fn {
5959
);
6060
};
6161

62+
(
63+
$name:ident, CONNECT
64+
) => {
65+
top_level_service_fn!(
66+
/// Route `CONNECT` requests to the given service.
67+
///
68+
/// See [`MethodFilter::CONNECT`] for when you'd want to use this,
69+
/// and [`get_service`] for an example.
70+
$name,
71+
CONNECT
72+
);
73+
};
74+
6275
(
6376
$name:ident, $method:ident
6477
) => {
@@ -118,6 +131,19 @@ macro_rules! top_level_handler_fn {
118131
);
119132
};
120133

134+
(
135+
$name:ident, CONNECT
136+
) => {
137+
top_level_handler_fn!(
138+
/// Route `CONNECT` requests to the given handler.
139+
///
140+
/// See [`MethodFilter::CONNECT`] for when you'd want to use this,
141+
/// and [`get`] for an example.
142+
$name,
143+
CONNECT
144+
);
145+
};
146+
121147
(
122148
$name:ident, $method:ident
123149
) => {
@@ -187,6 +213,19 @@ macro_rules! chained_service_fn {
187213
);
188214
};
189215

216+
(
217+
$name:ident, CONNECT
218+
) => {
219+
chained_service_fn!(
220+
/// Chain an additional service that will only accept `CONNECT` requests.
221+
///
222+
/// See [`MethodFilter::CONNECT`] for when you'd want to use this,
223+
/// and [`MethodRouter::get_service`] for an example.
224+
$name,
225+
CONNECT
226+
);
227+
};
228+
190229
(
191230
$name:ident, $method:ident
192231
) => {
@@ -250,6 +289,19 @@ macro_rules! chained_handler_fn {
250289
);
251290
};
252291

292+
(
293+
$name:ident, CONNECT
294+
) => {
295+
chained_handler_fn!(
296+
/// Chain an additional handler that will only accept `CONNECT` requests.
297+
///
298+
/// See [`MethodFilter::CONNECT`] for when you'd want to use this,
299+
/// and [`MethodRouter::get`] for an example.
300+
$name,
301+
CONNECT
302+
);
303+
};
304+
253305
(
254306
$name:ident, $method:ident
255307
) => {
@@ -279,6 +331,7 @@ macro_rules! chained_handler_fn {
279331
};
280332
}
281333

334+
top_level_service_fn!(connect_service, CONNECT);
282335
top_level_service_fn!(delete_service, DELETE);
283336
top_level_service_fn!(get_service, GET);
284337
top_level_service_fn!(head_service, HEAD);
@@ -382,6 +435,7 @@ where
382435
.skip_allow_header()
383436
}
384437

438+
top_level_handler_fn!(connect, CONNECT);
385439
top_level_handler_fn!(delete, DELETE);
386440
top_level_handler_fn!(get, GET);
387441
top_level_handler_fn!(head, HEAD);
@@ -498,6 +552,7 @@ pub struct MethodRouter<S = (), E = Infallible> {
498552
post: MethodEndpoint<S, E>,
499553
put: MethodEndpoint<S, E>,
500554
trace: MethodEndpoint<S, E>,
555+
connect: MethodEndpoint<S, E>,
501556
fallback: Fallback<S, E>,
502557
allow_header: AllowHeader,
503558
}
@@ -539,6 +594,7 @@ impl<S, E> fmt::Debug for MethodRouter<S, E> {
539594
.field("post", &self.post)
540595
.field("put", &self.put)
541596
.field("trace", &self.trace)
597+
.field("connect", &self.connect)
542598
.field("fallback", &self.fallback)
543599
.field("allow_header", &self.allow_header)
544600
.finish()
@@ -583,6 +639,7 @@ where
583639
)
584640
}
585641

642+
chained_handler_fn!(connect, CONNECT);
586643
chained_handler_fn!(delete, DELETE);
587644
chained_handler_fn!(get, GET);
588645
chained_handler_fn!(head, HEAD);
@@ -690,6 +747,7 @@ where
690747
post: MethodEndpoint::None,
691748
put: MethodEndpoint::None,
692749
trace: MethodEndpoint::None,
750+
connect: MethodEndpoint::None,
693751
allow_header: AllowHeader::None,
694752
fallback: Fallback::Default(fallback),
695753
}
@@ -706,6 +764,7 @@ where
706764
post: self.post.with_state(&state),
707765
put: self.put.with_state(&state),
708766
trace: self.trace.with_state(&state),
767+
connect: self.connect.with_state(&state),
709768
allow_header: self.allow_header,
710769
fallback: self.fallback.with_state(state),
711770
}
@@ -854,9 +913,20 @@ where
854913
&["DELETE"],
855914
);
856915

916+
set_endpoint(
917+
"CONNECT",
918+
&mut self.options,
919+
&endpoint,
920+
filter,
921+
MethodFilter::CONNECT,
922+
&mut self.allow_header,
923+
&["CONNECT"],
924+
);
925+
857926
self
858927
}
859928

929+
chained_service_fn!(connect_service, CONNECT);
860930
chained_service_fn!(delete_service, DELETE);
861931
chained_service_fn!(get_service, GET);
862932
chained_service_fn!(head_service, HEAD);
@@ -900,6 +970,7 @@ where
900970
post: self.post.map(layer_fn.clone()),
901971
put: self.put.map(layer_fn.clone()),
902972
trace: self.trace.map(layer_fn.clone()),
973+
connect: self.connect.map(layer_fn.clone()),
903974
fallback: self.fallback.map(layer_fn),
904975
allow_header: self.allow_header,
905976
}
@@ -924,6 +995,7 @@ where
924995
&& self.post.is_none()
925996
&& self.put.is_none()
926997
&& self.trace.is_none()
998+
&& self.connect.is_none()
927999
{
9281000
panic!(
9291001
"Adding a route_layer before any routes is a no-op. \
@@ -944,7 +1016,8 @@ where
9441016
self.patch = self.patch.map(layer_fn.clone());
9451017
self.post = self.post.map(layer_fn.clone());
9461018
self.put = self.put.map(layer_fn.clone());
947-
self.trace = self.trace.map(layer_fn);
1019+
self.trace = self.trace.map(layer_fn.clone());
1020+
self.connect = self.connect.map(layer_fn);
9481021

9491022
self
9501023
}
@@ -985,6 +1058,7 @@ where
9851058
self.post = merge_inner(path, "POST", self.post, other.post);
9861059
self.put = merge_inner(path, "PUT", self.put, other.put);
9871060
self.trace = merge_inner(path, "TRACE", self.trace, other.trace);
1061+
self.connect = merge_inner(path, "CONNECT", self.connect, other.connect);
9881062

9891063
self.fallback = self
9901064
.fallback
@@ -1058,6 +1132,7 @@ where
10581132
post,
10591133
put,
10601134
trace,
1135+
connect,
10611136
fallback,
10621137
allow_header,
10631138
} = self;
@@ -1071,6 +1146,7 @@ where
10711146
call!(req, method, PUT, put);
10721147
call!(req, method, DELETE, delete);
10731148
call!(req, method, TRACE, trace);
1149+
call!(req, method, CONNECT, connect);
10741150

10751151
let future = fallback.clone().call_with_state(req, state);
10761152

@@ -1113,6 +1189,7 @@ impl<S, E> Clone for MethodRouter<S, E> {
11131189
post: self.post.clone(),
11141190
put: self.put.clone(),
11151191
trace: self.trace.clone(),
1192+
connect: self.connect.clone(),
11161193
fallback: self.fallback.clone(),
11171194
allow_header: self.allow_header.clone(),
11181195
}

axum/src/routing/mod.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -40,9 +40,9 @@ mod tests;
4040
pub use self::{into_make_service::IntoMakeService, method_filter::MethodFilter, route::Route};
4141

4242
pub use self::method_routing::{
43-
any, any_service, delete, delete_service, get, get_service, head, head_service, on, on_service,
44-
options, options_service, patch, patch_service, post, post_service, put, put_service, trace,
45-
trace_service, MethodRouter,
43+
any, any_service, connect, connect_service, delete, delete_service, get, get_service, head,
44+
head_service, on, on_service, options, options_service, patch, patch_service, post,
45+
post_service, put, put_service, trace, trace_service, MethodRouter,
4646
};
4747

4848
macro_rules! panic_on_err {

0 commit comments

Comments
 (0)