mirror of
https://github.com/denoland/deno.git
synced 2024-12-24 08:09:08 -05:00
refactor(ext/http): fork fly_accept_encodings crate (#21586)
Blocks update to Hyper 1.0. It's a really small library, no need to pull it as a dependency. Blocker for #21583
This commit is contained in:
parent
8d269efbc2
commit
c481ff7d81
5 changed files with 221 additions and 15 deletions
13
Cargo.lock
generated
13
Cargo.lock
generated
|
@ -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",
|
||||
|
@ -2425,17 +2425,6 @@ dependencies = [
|
|||
"num-traits",
|
||||
]
|
||||
|
||||
[[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"
|
||||
|
|
|
@ -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
|
||||
|
|
214
ext/http/fly_accept_encoding.rs
Normal file
214
ext/http/fly_accept_encoding.rs
Normal file
|
@ -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));
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
Loading…
Reference in a new issue