diff --git a/axum/CHANGELOG.md b/axum/CHANGELOG.md index a510d282..fa52e5b6 100644 --- a/axum/CHANGELOG.md +++ b/axum/CHANGELOG.md @@ -16,6 +16,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 `/foo/`). That is no longer supported and such requests will now be sent to the fallback. Consider using `axum_extra::routing::RouterExt::route_with_tsr` if you want the old behavior ([#1119]) +- **fixed:** If `WebSocketUpgrade` cannot upgrade the connection it will return a + `WebSocketUpgradeRejection::ConnectionNotUpgradable` rejection ([#1135]) +- **changed:** `WebSocketUpgradeRejection` has a new variant `ConnectionNotUpgradable` + variant ([#1135]) - **added** Implement `TryFrom` for `MethodFilter` and use new `NoMatchingMethodFilter` error in case of failure ([#1130]) - **added:** Support running extractors from `middleware::from_fn` functions ([#1088]) - **added:** Added `debug_handler` which is an attribute macro that improves @@ -27,6 +31,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 [#1102]: https://github.com/tokio-rs/axum/pull/1102 [#1119]: https://github.com/tokio-rs/axum/pull/1119 [#1130]: https://github.com/tokio-rs/axum/pull/1130 +[#1135]: https://github.com/tokio-rs/axum/pull/1135 [#924]: https://github.com/tokio-rs/axum/pull/924 # 0.5.10 (28. June, 2022) diff --git a/axum/src/extract/ws.rs b/axum/src/extract/ws.rs index 7ac75ab6..5d8f6005 100644 --- a/axum/src/extract/ws.rs +++ b/axum/src/extract/ws.rs @@ -267,14 +267,15 @@ where return Err(InvalidWebSocketVersionHeader.into()); } - let sec_websocket_key = - if let Some(key) = req.headers_mut().remove(header::SEC_WEBSOCKET_KEY) { - key - } else { - return Err(WebSocketKeyHeaderMissing.into()); - }; + let sec_websocket_key = req + .headers_mut() + .remove(header::SEC_WEBSOCKET_KEY) + .ok_or(WebSocketKeyHeaderMissing)?; - let on_upgrade = req.extensions_mut().remove::().unwrap(); + let on_upgrade = req + .extensions_mut() + .remove::() + .ok_or(ConnectionNotUpgradable)?; let sec_websocket_protocol = req.headers().get(header::SEC_WEBSOCKET_PROTOCOL).cloned(); @@ -564,6 +565,20 @@ pub mod rejection { pub struct WebSocketKeyHeaderMissing; } + define_rejection! { + #[status = UPGRADE_REQUIRED] + #[body = "WebSocket request couldn't be upgraded since no upgrade state was present"] + /// Rejection type for [`WebSocketUpgrade`](super::WebSocketUpgrade). + /// + /// This rejection is returned if the connection cannot be upgraded for example if the + /// request is HTTP/1.0. + /// + /// See [MDN] for more details about connection upgrades. + /// + /// [MDN]: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Upgrade + pub struct ConnectionNotUpgradable; + } + composite_rejection! { /// Rejection used for [`WebSocketUpgrade`](super::WebSocketUpgrade). /// @@ -575,6 +590,7 @@ pub mod rejection { InvalidUpgradeHeader, InvalidWebSocketVersionHeader, WebSocketKeyHeaderMissing, + ConnectionNotUpgradable, } } } @@ -640,3 +656,37 @@ pub mod close_code { /// action. pub const AGAIN: u16 = 1013; } + +#[cfg(test)] +mod tests { + use super::*; + use crate::{body::Body, routing::get}; + use http::{Request, Version}; + use tower::ServiceExt; + + #[tokio::test] + async fn rejects_http_1_0_requests() { + let svc = get(|ws: Result| { + let rejection = ws.unwrap_err(); + assert!(matches!( + rejection, + WebSocketUpgradeRejection::ConnectionNotUpgradable(_) + )); + std::future::ready(()) + }); + + let req = Request::builder() + .version(Version::HTTP_10) + .method(Method::GET) + .header("upgrade", "websocket") + .header("connection", "Upgrade") + .header("sec-websocket-key", "6D69KGBOr4Re+Nj6zx9aQA==") + .header("sec-websocket-version", "13") + .body(Body::empty()) + .unwrap(); + + let res = svc.oneshot(req).await.unwrap(); + + assert_eq!(res.status(), StatusCode::OK); + } +}