summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBen Noordhuis <info@bnoordhuis.nl>2021-09-25 10:02:26 +0200
committerGitHub <noreply@github.com>2021-09-25 10:02:26 +0200
commit16ea39ee483ec8d256b0361485a7550e158b5fb1 (patch)
treeac4f1cbf6eaa0725fe9c8615c9899b06f878fa87
parent1a6249c9717288e37873b692abac831f11c30f4e (diff)
fix(ext/http): fortify "is websocket?" check (#12179)
Check for expected headers more rigorously and check that it's a HTTP/1.1 GET request. The logic mirrors what Deno Deploy and the tungstenite crate do. The presence of "Sec-Websocket-Version: 13" is now also enforced. I don't expect that to break anything: conforming clients already send it and tungstenite can't talk to older clients anyway. The new code is more efficient due to heap-allocating less and aligns more closely with the checks in ext/http/01_http.js now.
-rw-r--r--ext/http/lib.rs31
1 files changed, 19 insertions, 12 deletions
diff --git a/ext/http/lib.rs b/ext/http/lib.rs
index ba6d73d5c..9aef3d370 100644
--- a/ext/http/lib.rs
+++ b/ext/http/lib.rs
@@ -22,6 +22,10 @@ use deno_core::Resource;
use deno_core::ResourceId;
use deno_core::ZeroCopyBuf;
use hyper::body::HttpBody;
+use hyper::header::CONNECTION;
+use hyper::header::SEC_WEBSOCKET_KEY;
+use hyper::header::SEC_WEBSOCKET_VERSION;
+use hyper::header::UPGRADE;
use hyper::http;
use hyper::server::conn::Http;
use hyper::service::Service as HyperService;
@@ -312,20 +316,23 @@ fn req_headers(
}
fn is_websocket_request(req: &hyper::Request<hyper::Body>) -> bool {
- req_header_contains(req, hyper::header::CONNECTION, "upgrade")
- && req_header_contains(req, hyper::header::UPGRADE, "websocket")
+ req.version() == hyper::Version::HTTP_11
+ && req.method() == hyper::Method::GET
+ && req.headers().contains_key(&SEC_WEBSOCKET_KEY)
+ && header(req.headers(), &SEC_WEBSOCKET_VERSION) == b"13"
+ && header(req.headers(), &UPGRADE).eq_ignore_ascii_case(b"websocket")
+ && header(req.headers(), &CONNECTION)
+ .split(|c| *c == b' ' || *c == b',')
+ .any(|token| token.eq_ignore_ascii_case(b"upgrade"))
}
-fn req_header_contains(
- req: &hyper::Request<hyper::Body>,
- key: impl hyper::header::AsHeaderName,
- value: &str,
-) -> bool {
- req.headers().get_all(key).iter().any(|v| {
- v.to_str()
- .map(|s| s.to_lowercase().contains(value))
- .unwrap_or(false)
- })
+fn header<'a>(
+ h: &'a hyper::http::HeaderMap,
+ name: &hyper::header::HeaderName,
+) -> &'a [u8] {
+ h.get(name)
+ .map(hyper::header::HeaderValue::as_bytes)
+ .unwrap_or_default()
}
fn should_ignore_error(e: &AnyError) -> bool {