Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add MethodFilter::CONNECT #2961

Merged
merged 6 commits into from
Oct 6, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions axum-extra/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,10 @@ and this project adheres to [Semantic Versioning].

# Unreleased

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

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

# 0.10.0
Expand Down
23 changes: 23 additions & 0 deletions axum-extra/src/routing/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,19 @@ pub trait RouterExt<S>: sealed::Sealed {
T: SecondElementIs<P> + 'static,
P: TypedPath;

/// Add a typed `CONNECT` route to the router.
///
/// The path will be inferred from the first argument to the handler function which must
/// implement [`TypedPath`].
///
/// See [`TypedPath`] for more details and examples.
#[cfg(feature = "typed-routing")]
fn typed_connect<H, T, P>(self, handler: H) -> Self
SabrinaJewson marked this conversation as resolved.
Show resolved Hide resolved
where
H: axum::handler::Handler<T, S>,
T: SecondElementIs<P> + 'static,
P: TypedPath;

/// Add another route to the router with an additional "trailing slash redirect" route.
///
/// If you add a route _without_ a trailing slash, such as `/foo`, this method will also add a
Expand Down Expand Up @@ -255,6 +268,16 @@ where
self.route(P::PATH, axum::routing::trace(handler))
}

#[cfg(feature = "typed-routing")]
fn typed_connect<H, T, P>(self, handler: H) -> Self
where
H: axum::handler::Handler<T, S>,
T: SecondElementIs<P> + 'static,
P: TypedPath,
{
self.route(P::PATH, axum::routing::connect(handler))
}

#[track_caller]
fn route_with_tsr(mut self, path: &str, method_router: MethodRouter<S>) -> Self
where
Expand Down
12 changes: 10 additions & 2 deletions axum/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,16 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

# Unreleased

- **added:** Add support for WebSockets over HTTP/2 ([#2894]).
They can be enabled by changing `get(ws_endpoint)` handlers to `any(ws_endpoint)`
- **added:** Add `MethodFilter::CONNECT`, `routing::connect[_service]`
and `MethodRouter::connect[_service]` ([#2961])

[#2984]: https://github.com/tokio-rs/axum/pull/2984
[#2961]: https://github.com/tokio-rs/axum/pull/2961

# 0.8.0

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

[#2473]: https://github.com/tokio-rs/axum/pull/2473
[#2645]: https://github.com/tokio-rs/axum/pull/2645
Expand Down
34 changes: 30 additions & 4 deletions axum/src/routing/method_filter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,24 @@ use std::{
pub struct MethodFilter(u16);

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

fn try_from(m: Method) -> Result<Self, NoMatchingMethodFilter> {
match m {
Method::CONNECT => Ok(MethodFilter::CONNECT),
Method::DELETE => Ok(MethodFilter::DELETE),
Method::GET => Ok(MethodFilter::GET),
Method::HEAD => Ok(MethodFilter::HEAD),
Expand All @@ -90,6 +109,11 @@ mod tests {

#[test]
fn from_http_method() {
assert_eq!(
MethodFilter::try_from(Method::CONNECT).unwrap(),
MethodFilter::CONNECT
);

assert_eq!(
MethodFilter::try_from(Method::DELETE).unwrap(),
MethodFilter::DELETE
Expand Down Expand Up @@ -130,9 +154,11 @@ mod tests {
MethodFilter::TRACE
);

assert!(MethodFilter::try_from(http::Method::CONNECT)
.unwrap_err()
.to_string()
.contains("CONNECT"));
assert!(
MethodFilter::try_from(http::Method::from_bytes(b"CUSTOM").unwrap())
.unwrap_err()
.to_string()
.contains("CUSTOM")
);
}
}
79 changes: 78 additions & 1 deletion axum/src/routing/method_routing.rs
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,19 @@ macro_rules! top_level_service_fn {
);
};

(
$name:ident, CONNECT
) => {
top_level_service_fn!(
/// Route `CONNECT` requests to the given service.
///
/// See [`MethodFilter::CONNECT`] for when you'd want to use this,
/// and [`get_service`] for an example.
$name,
CONNECT
);
};

(
$name:ident, $method:ident
) => {
Expand Down Expand Up @@ -118,6 +131,19 @@ macro_rules! top_level_handler_fn {
);
};

(
$name:ident, CONNECT
) => {
top_level_handler_fn!(
/// Route `CONNECT` requests to the given handler.
///
/// See [`MethodFilter::CONNECT`] for when you'd want to use this,
/// and [`get`] for an example.
$name,
CONNECT
);
};

(
$name:ident, $method:ident
) => {
Expand Down Expand Up @@ -187,6 +213,19 @@ macro_rules! chained_service_fn {
);
};

(
$name:ident, CONNECT
) => {
chained_service_fn!(
/// Chain an additional service that will only accept `CONNECT` requests.
///
/// See [`MethodFilter::CONNECT`] for when you'd want to use this,
/// and [`MethodRouter::get_service`] for an example.
$name,
CONNECT
);
};

(
$name:ident, $method:ident
) => {
Expand Down Expand Up @@ -250,6 +289,19 @@ macro_rules! chained_handler_fn {
);
};

(
$name:ident, CONNECT
) => {
chained_handler_fn!(
/// Chain an additional handler that will only accept `CONNECT` requests.
///
/// See [`MethodFilter::CONNECT`] for when you'd want to use this,
/// and [`MethodRouter::get`] for an example.
$name,
CONNECT
);
};

(
$name:ident, $method:ident
) => {
Expand Down Expand Up @@ -279,6 +331,7 @@ macro_rules! chained_handler_fn {
};
}

top_level_service_fn!(connect_service, CONNECT);
top_level_service_fn!(delete_service, DELETE);
top_level_service_fn!(get_service, GET);
top_level_service_fn!(head_service, HEAD);
Expand Down Expand Up @@ -382,6 +435,7 @@ where
.skip_allow_header()
}

top_level_handler_fn!(connect, CONNECT);
top_level_handler_fn!(delete, DELETE);
top_level_handler_fn!(get, GET);
top_level_handler_fn!(head, HEAD);
Expand Down Expand Up @@ -498,6 +552,7 @@ pub struct MethodRouter<S = (), E = Infallible> {
post: MethodEndpoint<S, E>,
put: MethodEndpoint<S, E>,
trace: MethodEndpoint<S, E>,
connect: MethodEndpoint<S, E>,
fallback: Fallback<S, E>,
allow_header: AllowHeader,
}
Expand Down Expand Up @@ -539,6 +594,7 @@ impl<S, E> fmt::Debug for MethodRouter<S, E> {
.field("post", &self.post)
.field("put", &self.put)
.field("trace", &self.trace)
.field("connect", &self.connect)
.field("fallback", &self.fallback)
.field("allow_header", &self.allow_header)
.finish()
Expand Down Expand Up @@ -583,6 +639,7 @@ where
)
}

chained_handler_fn!(connect, CONNECT);
chained_handler_fn!(delete, DELETE);
chained_handler_fn!(get, GET);
chained_handler_fn!(head, HEAD);
Expand Down Expand Up @@ -690,6 +747,7 @@ where
post: MethodEndpoint::None,
put: MethodEndpoint::None,
trace: MethodEndpoint::None,
connect: MethodEndpoint::None,
allow_header: AllowHeader::None,
fallback: Fallback::Default(fallback),
}
Expand All @@ -706,6 +764,7 @@ where
post: self.post.with_state(&state),
put: self.put.with_state(&state),
trace: self.trace.with_state(&state),
connect: self.connect.with_state(&state),
allow_header: self.allow_header,
fallback: self.fallback.with_state(state),
}
Expand Down Expand Up @@ -854,9 +913,20 @@ where
&["DELETE"],
);

set_endpoint(
"CONNECT",
&mut self.options,
&endpoint,
filter,
MethodFilter::CONNECT,
&mut self.allow_header,
&["CONNECT"],
);

self
}

chained_service_fn!(connect_service, CONNECT);
chained_service_fn!(delete_service, DELETE);
chained_service_fn!(get_service, GET);
chained_service_fn!(head_service, HEAD);
Expand Down Expand Up @@ -900,6 +970,7 @@ where
post: self.post.map(layer_fn.clone()),
put: self.put.map(layer_fn.clone()),
trace: self.trace.map(layer_fn.clone()),
connect: self.connect.map(layer_fn.clone()),
fallback: self.fallback.map(layer_fn),
allow_header: self.allow_header,
}
Expand All @@ -924,6 +995,7 @@ where
&& self.post.is_none()
&& self.put.is_none()
&& self.trace.is_none()
&& self.connect.is_none()
{
panic!(
"Adding a route_layer before any routes is a no-op. \
Expand All @@ -944,7 +1016,8 @@ where
self.patch = self.patch.map(layer_fn.clone());
self.post = self.post.map(layer_fn.clone());
self.put = self.put.map(layer_fn.clone());
self.trace = self.trace.map(layer_fn);
self.trace = self.trace.map(layer_fn.clone());
self.connect = self.connect.map(layer_fn);

self
}
Expand Down Expand Up @@ -985,6 +1058,7 @@ where
self.post = merge_inner(path, "POST", self.post, other.post);
self.put = merge_inner(path, "PUT", self.put, other.put);
self.trace = merge_inner(path, "TRACE", self.trace, other.trace);
self.connect = merge_inner(path, "CONNECT", self.connect, other.connect);

self.fallback = self
.fallback
Expand Down Expand Up @@ -1058,6 +1132,7 @@ where
post,
put,
trace,
connect,
fallback,
allow_header,
} = self;
Expand All @@ -1071,6 +1146,7 @@ where
call!(req, method, PUT, put);
call!(req, method, DELETE, delete);
call!(req, method, TRACE, trace);
call!(req, method, CONNECT, connect);

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

Expand Down Expand Up @@ -1113,6 +1189,7 @@ impl<S, E> Clone for MethodRouter<S, E> {
post: self.post.clone(),
put: self.put.clone(),
trace: self.trace.clone(),
connect: self.connect.clone(),
fallback: self.fallback.clone(),
allow_header: self.allow_header.clone(),
}
Expand Down
6 changes: 3 additions & 3 deletions axum/src/routing/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,9 +40,9 @@ mod tests;
pub use self::{into_make_service::IntoMakeService, method_filter::MethodFilter, route::Route};

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

macro_rules! panic_on_err {
Expand Down