diff options
author | Ben Noordhuis <info@bnoordhuis.nl> | 2021-09-25 10:02:26 +0200 |
---|---|---|
committer | GitHub <noreply@github.com> | 2021-09-25 10:02:26 +0200 |
commit | 16ea39ee483ec8d256b0361485a7550e158b5fb1 (patch) | |
tree | ac4f1cbf6eaa0725fe9c8615c9899b06f878fa87 | |
parent | 1a6249c9717288e37873b692abac831f11c30f4e (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.rs | 31 |
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 { |