0
0
Fork 0
mirror of https://github.com/denoland/deno.git synced 2024-10-29 08:58:01 -04:00

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.
This commit is contained in:
Ben Noordhuis 2021-09-25 10:02:26 +02:00 committed by GitHub
parent 1a6249c971
commit 16ea39ee48
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23

View file

@ -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 {