summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Cargo.lock13
-rw-r--r--ext/http/Cargo.toml2
-rw-r--r--ext/http/fly_accept_encoding.rs214
-rw-r--r--ext/http/http_next.rs4
-rw-r--r--ext/http/lib.rs3
5 files changed, 221 insertions, 15 deletions
diff --git a/Cargo.lock b/Cargo.lock
index aa5e84d50..7f86e52ec 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -1336,12 +1336,12 @@ dependencies = [
"deno_net",
"deno_websocket",
"flate2",
- "fly-accept-encoding",
"http",
"http-body-util",
"httparse",
"hyper 0.14.27",
"hyper 1.0.0-rc.4",
+ "itertools",
"memmem",
"mime",
"once_cell",
@@ -2426,17 +2426,6 @@ dependencies = [
]
[[package]]
-name = "fly-accept-encoding"
-version = "0.2.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a3afa7516fdcfd8e5e93a938f8fec857785ced190a1f62d842d1fe1ffbe22ba8"
-dependencies = [
- "http",
- "itertools",
- "thiserror",
-]
-
-[[package]]
name = "fnv"
version = "1.0.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
diff --git a/ext/http/Cargo.toml b/ext/http/Cargo.toml
index 8426b9e14..fe4ed0c97 100644
--- a/ext/http/Cargo.toml
+++ b/ext/http/Cargo.toml
@@ -31,11 +31,11 @@ deno_core.workspace = true
deno_net.workspace = true
deno_websocket.workspace = true
flate2.workspace = true
-fly-accept-encoding = "0.2.0"
http.workspace = true
httparse.workspace = true
hyper = { workspace = true, features = ["server", "stream", "http1", "http2", "runtime"] }
hyper1 = { package = "hyper", features = ["full"], version = "=1.0.0-rc.4" }
+itertools = "0.10"
memmem.workspace = true
mime = "0.3.16"
once_cell.workspace = true
diff --git a/ext/http/fly_accept_encoding.rs b/ext/http/fly_accept_encoding.rs
new file mode 100644
index 000000000..f3de9bee4
--- /dev/null
+++ b/ext/http/fly_accept_encoding.rs
@@ -0,0 +1,214 @@
+// Copyright 2018 Yoshua Wuyts. All rights reserved. MIT license.
+// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
+// Forked from https://github.com/superfly/accept-encoding/blob/1cded757ec7ff3916e5bfe7441db76cdc48170dc/
+// Forked to support both http 0.3 and http 1.0 crates.
+
+use http::header::HeaderMap;
+use http::header::ACCEPT_ENCODING;
+use itertools::Itertools;
+
+/// A list enumerating the categories of errors in this crate.
+///
+/// This list is intended to grow over time and it is not recommended to
+/// exhaustively match against it.
+///
+/// It is used with the [`Error`] struct.
+///
+/// [`Error`]: std.struct.Error.html
+#[derive(Debug, thiserror::Error)]
+pub enum EncodingError {
+ /// Invalid header encoding.
+ #[error("Invalid header encoding.")]
+ InvalidEncoding,
+ /// The encoding scheme is unknown.
+ #[error("Unknown encoding scheme.")]
+ UnknownEncoding,
+}
+
+/// Encodings to use.
+#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)]
+pub enum Encoding {
+ /// The Gzip encoding.
+ Gzip,
+ /// The Deflate encoding.
+ Deflate,
+ /// The Brotli encoding.
+ Brotli,
+ /// The Zstd encoding.
+ Zstd,
+ /// No encoding.
+ Identity,
+}
+
+impl Encoding {
+ /// Parses a given string into its corresponding encoding.
+ fn parse(s: &str) -> Result<Option<Encoding>, EncodingError> {
+ match s {
+ "gzip" => Ok(Some(Encoding::Gzip)),
+ "deflate" => Ok(Some(Encoding::Deflate)),
+ "br" => Ok(Some(Encoding::Brotli)),
+ "zstd" => Ok(Some(Encoding::Zstd)),
+ "identity" => Ok(Some(Encoding::Identity)),
+ "*" => Ok(None),
+ _ => Err(EncodingError::UnknownEncoding),
+ }
+ }
+}
+
+/// Select the encoding with the largest qval or the first with qval ~= 1
+pub fn preferred(
+ encodings: impl Iterator<Item = Result<(Option<Encoding>, f32), EncodingError>>,
+) -> Result<Option<Encoding>, EncodingError> {
+ let mut preferred_encoding = None;
+ let mut max_qval = 0.0;
+
+ for r in encodings {
+ let (encoding, qval) = r?;
+ if (qval - 1.0f32).abs() < 0.01 {
+ return Ok(encoding);
+ } else if qval > max_qval {
+ preferred_encoding = encoding;
+ max_qval = qval;
+ }
+ }
+
+ Ok(preferred_encoding)
+}
+
+/// Parse a set of HTTP headers into an iterator containing tuples of options containing encodings and their corresponding q-values.
+pub fn encodings_iter(
+ headers: &HeaderMap,
+) -> impl Iterator<Item = Result<(Option<Encoding>, f32), EncodingError>> + '_ {
+ headers
+ .get_all(ACCEPT_ENCODING)
+ .iter()
+ .map(|hval| hval.to_str().map_err(|_| EncodingError::InvalidEncoding))
+ .map_ok(|s| s.split(',').map(str::trim))
+ .flatten_ok()
+ .filter_map_ok(|v| {
+ let (e, q) = match v.split_once(";q=") {
+ Some((e, q)) => (e, q),
+ None => return Some(Ok((Encoding::parse(v).ok()?, 1.0f32))),
+ };
+ let encoding = Encoding::parse(e).ok()?; // ignore unknown encodings
+ let qval = match q.parse() {
+ Ok(f) if f > 1.0 => return Some(Err(EncodingError::InvalidEncoding)), // q-values over 1 are unacceptable,
+ Ok(f) => f,
+ Err(_) => return Some(Err(EncodingError::InvalidEncoding)),
+ };
+ Some(Ok((encoding, qval)))
+ })
+ .map(|r| r?) // flatten Result<Result<...
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+ use http::HeaderValue;
+
+ fn encodings(
+ headers: &HeaderMap,
+ ) -> Result<Vec<(Option<Encoding>, f32)>, EncodingError> {
+ encodings_iter(headers).collect()
+ }
+
+ fn parse(headers: &HeaderMap) -> Result<Option<Encoding>, EncodingError> {
+ preferred(encodings_iter(headers))
+ }
+
+ #[test]
+ fn single_encoding() {
+ let mut headers = HeaderMap::new();
+ headers.insert(ACCEPT_ENCODING, HeaderValue::from_str("gzip").unwrap());
+
+ let encoding = parse(&headers).unwrap().unwrap();
+ assert_eq!(encoding, Encoding::Gzip);
+ }
+
+ #[test]
+ fn multiple_encodings() {
+ let mut headers = HeaderMap::new();
+ headers.insert(
+ ACCEPT_ENCODING,
+ HeaderValue::from_str("gzip, deflate, br").unwrap(),
+ );
+
+ let encoding = parse(&headers).unwrap().unwrap();
+ assert_eq!(encoding, Encoding::Gzip);
+ }
+
+ #[test]
+ fn single_encoding_with_qval() {
+ let mut headers = HeaderMap::new();
+ headers.insert(
+ ACCEPT_ENCODING,
+ HeaderValue::from_str("deflate;q=1.0").unwrap(),
+ );
+
+ let encoding = parse(&headers).unwrap().unwrap();
+ assert_eq!(encoding, Encoding::Deflate);
+ }
+
+ #[test]
+ fn multiple_encodings_with_qval_1() {
+ let mut headers = HeaderMap::new();
+ headers.insert(
+ ACCEPT_ENCODING,
+ HeaderValue::from_str("deflate, gzip;q=1.0, *;q=0.5").unwrap(),
+ );
+
+ let encoding = parse(&headers).unwrap().unwrap();
+ assert_eq!(encoding, Encoding::Deflate);
+ }
+
+ #[test]
+ fn multiple_encodings_with_qval_2() {
+ let mut headers = HeaderMap::new();
+ headers.insert(
+ ACCEPT_ENCODING,
+ HeaderValue::from_str("gzip;q=0.5, deflate;q=1.0, *;q=0.5").unwrap(),
+ );
+
+ let encoding = parse(&headers).unwrap().unwrap();
+ assert_eq!(encoding, Encoding::Deflate);
+ }
+
+ #[test]
+ fn multiple_encodings_with_qval_3() {
+ let mut headers = HeaderMap::new();
+ headers.insert(
+ ACCEPT_ENCODING,
+ HeaderValue::from_str("gzip;q=0.5, deflate;q=0.75, *;q=1.0").unwrap(),
+ );
+
+ let encoding = parse(&headers).unwrap();
+ assert!(encoding.is_none());
+ }
+
+ #[test]
+ fn list_encodings() {
+ let mut headers = HeaderMap::new();
+ headers.insert(
+ ACCEPT_ENCODING,
+ HeaderValue::from_str("zstd;q=1.0, deflate;q=0.8, br;q=0.9").unwrap(),
+ );
+
+ let encodings = encodings(&headers).unwrap();
+ assert_eq!(encodings[0], (Some(Encoding::Zstd), 1.0));
+ assert_eq!(encodings[1], (Some(Encoding::Deflate), 0.8));
+ assert_eq!(encodings[2], (Some(Encoding::Brotli), 0.9));
+ }
+
+ #[test]
+ fn list_encodings_ignore_unknown() {
+ let mut headers = HeaderMap::new();
+ headers.insert(
+ ACCEPT_ENCODING,
+ HeaderValue::from_str("zstd;q=1.0, unknown;q=0.8, br;q=0.9").unwrap(),
+ );
+
+ let encodings = encodings(&headers).unwrap();
+ assert_eq!(encodings[0], (Some(Encoding::Zstd), 1.0));
+ assert_eq!(encodings[1], (Some(Encoding::Brotli), 0.9));
+ }
+}
diff --git a/ext/http/http_next.rs b/ext/http/http_next.rs
index 217ae1c39..c1cb2df66 100644
--- a/ext/http/http_next.rs
+++ b/ext/http/http_next.rs
@@ -44,7 +44,6 @@ use deno_core::ResourceId;
use deno_net::ops_tls::TlsStream;
use deno_net::raw::NetworkStream;
use deno_websocket::ws_create_server_stream;
-use fly_accept_encoding::Encoding;
use http::header::ACCEPT_ENCODING;
use http::header::CACHE_CONTROL;
use http::header::CONTENT_ENCODING;
@@ -72,6 +71,9 @@ use std::pin::Pin;
use std::ptr::null;
use std::rc::Rc;
+use super::fly_accept_encoding;
+use fly_accept_encoding::Encoding;
+
use tokio::io::AsyncReadExt;
use tokio::io::AsyncWriteExt;
diff --git a/ext/http/lib.rs b/ext/http/lib.rs
index af0f0f0c9..f4fb55062 100644
--- a/ext/http/lib.rs
+++ b/ext/http/lib.rs
@@ -41,7 +41,6 @@ use deno_net::raw::NetworkStream;
use deno_websocket::ws_create_server_stream;
use flate2::write::GzEncoder;
use flate2::Compression;
-use fly_accept_encoding::Encoding;
use hyper::body::Bytes;
use hyper::body::HttpBody;
use hyper::body::SizeHint;
@@ -79,6 +78,7 @@ use crate::reader_stream::ExternallyAbortableReaderStream;
use crate::reader_stream::ShutdownHandle;
pub mod compressible;
+mod fly_accept_encoding;
mod http_next;
mod hyper_util_tokioio;
mod network_buffered_stream;
@@ -89,6 +89,7 @@ mod response_body;
mod service;
mod websocket_upgrade;
+use fly_accept_encoding::Encoding;
pub use request_properties::DefaultHttpPropertyExtractor;
pub use request_properties::HttpConnectionProperties;
pub use request_properties::HttpListenProperties;