mirror of
https://github.com/tokio-rs/axum.git
synced 2026-08-29 00:00:18 +02:00
Fix websockets failing on Firefox (#76)
Axum expected the `Connection` header to be _exactly_ `upgrade`. Turns out thats a bit too strict as this didn't work in Firefox. Turns out `Connection` just has to contain `upgrade`. At least that is what [warp does](https://github.com/seanmonstar/warp/blob/master/src/filters/ws.rs#L46).
This commit is contained in:
@@ -16,6 +16,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||||||
- Improve documentation for routing ([#71](https://github.com/tokio-rs/axum/pull/71))
|
- Improve documentation for routing ([#71](https://github.com/tokio-rs/axum/pull/71))
|
||||||
- Clarify required response body type when routing to `tower::Service`s ([#69](https://github.com/tokio-rs/axum/pull/69))
|
- Clarify required response body type when routing to `tower::Service`s ([#69](https://github.com/tokio-rs/axum/pull/69))
|
||||||
- Add `axum::body::box_body` to converting an `http_body::Body` to `axum::body::BoxBody` ([#69](https://github.com/tokio-rs/axum/pull/69))
|
- Add `axum::body::box_body` to converting an `http_body::Body` to `axum::body::BoxBody` ([#69](https://github.com/tokio-rs/axum/pull/69))
|
||||||
|
- Fix WebSockets failing on Firefox ([#76](https://github.com/tokio-rs/axum/pull/76))
|
||||||
|
|
||||||
## Breaking changes
|
## Breaking changes
|
||||||
|
|
||||||
|
|||||||
+20
-12
@@ -236,29 +236,21 @@ where
|
|||||||
return response(StatusCode::NOT_FOUND, "Request method must be `GET`");
|
return response(StatusCode::NOT_FOUND, "Request method must be `GET`");
|
||||||
}
|
}
|
||||||
|
|
||||||
if !header_eq(
|
if !header_contains(&req, header::CONNECTION, "upgrade") {
|
||||||
&req,
|
|
||||||
header::CONNECTION,
|
|
||||||
HeaderValue::from_static("upgrade"),
|
|
||||||
) {
|
|
||||||
return response(
|
return response(
|
||||||
StatusCode::BAD_REQUEST,
|
StatusCode::BAD_REQUEST,
|
||||||
"Connection header did not include 'upgrade'",
|
"Connection header did not include 'upgrade'",
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if !header_eq(&req, header::UPGRADE, HeaderValue::from_static("websocket")) {
|
if !header_eq(&req, header::UPGRADE, "websocket") {
|
||||||
return response(
|
return response(
|
||||||
StatusCode::BAD_REQUEST,
|
StatusCode::BAD_REQUEST,
|
||||||
"`Upgrade` header did not include 'websocket'",
|
"`Upgrade` header did not include 'websocket'",
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if !header_eq(
|
if !header_eq(&req, header::SEC_WEBSOCKET_VERSION, "13") {
|
||||||
&req,
|
|
||||||
header::SEC_WEBSOCKET_VERSION,
|
|
||||||
HeaderValue::from_static("13"),
|
|
||||||
) {
|
|
||||||
return response(
|
return response(
|
||||||
StatusCode::BAD_REQUEST,
|
StatusCode::BAD_REQUEST,
|
||||||
"`Sec-Websocket-Version` header did not include '13'",
|
"`Sec-Websocket-Version` header did not include '13'",
|
||||||
@@ -320,6 +312,8 @@ where
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn response<E>(status: StatusCode, body: &'static str) -> Result<Response<BoxBody>, E> {
|
fn response<E>(status: StatusCode, body: &'static str) -> Result<Response<BoxBody>, E> {
|
||||||
|
dbg!((status, body));
|
||||||
|
|
||||||
let res = Response::builder()
|
let res = Response::builder()
|
||||||
.status(status)
|
.status(status)
|
||||||
.body(box_body(Full::from(body)))
|
.body(box_body(Full::from(body)))
|
||||||
@@ -327,7 +321,7 @@ fn response<E>(status: StatusCode, body: &'static str) -> Result<Response<BoxBod
|
|||||||
Ok(res)
|
Ok(res)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn header_eq<B>(req: &Request<B>, key: HeaderName, value: HeaderValue) -> bool {
|
fn header_eq<B>(req: &Request<B>, key: HeaderName, value: &'static str) -> bool {
|
||||||
if let Some(header) = req.headers().get(&key) {
|
if let Some(header) = req.headers().get(&key) {
|
||||||
header.as_bytes().eq_ignore_ascii_case(value.as_bytes())
|
header.as_bytes().eq_ignore_ascii_case(value.as_bytes())
|
||||||
} else {
|
} else {
|
||||||
@@ -335,6 +329,20 @@ fn header_eq<B>(req: &Request<B>, key: HeaderName, value: HeaderValue) -> bool {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn header_contains<B>(req: &Request<B>, key: HeaderName, value: &'static str) -> bool {
|
||||||
|
let header = if let Some(header) = req.headers().get(&key) {
|
||||||
|
header
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
|
||||||
|
if let Ok(header) = std::str::from_utf8(header.as_bytes()) {
|
||||||
|
header.to_ascii_lowercase().contains(value)
|
||||||
|
} else {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn sign(key: &[u8]) -> HeaderValue {
|
fn sign(key: &[u8]) -> HeaderValue {
|
||||||
let mut sha1 = Sha1::default();
|
let mut sha1 = Sha1::default();
|
||||||
sha1.update(key);
|
sha1.update(key);
|
||||||
|
|||||||
Reference in New Issue
Block a user