1
0
mirror of https://github.com/fafhrd91/actix-web synced 2024-11-24 00:21:08 +01:00

prepare for actix-tls v3 beta 9 (#2456)

This commit is contained in:
Rob Ede 2021-11-22 15:37:23 +00:00 committed by GitHub
parent a2a42ec152
commit a172f5968d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 109 additions and 89 deletions

View File

@ -75,7 +75,7 @@ actix-rt = "2.3"
actix-server = "2.0.0-beta.9" actix-server = "2.0.0-beta.9"
actix-service = "2.0.0" actix-service = "2.0.0"
actix-utils = "3.0.0" actix-utils = "3.0.0"
actix-tls = { version = "3.0.0-beta.7", default-features = false, optional = true } actix-tls = { version = "3.0.0-beta.9", default-features = false, optional = true }
actix-http = "3.0.0-beta.12" actix-http = "3.0.0-beta.12"
actix-router = "0.5.0-beta.2" actix-router = "0.5.0-beta.2"

View File

@ -31,7 +31,7 @@ openssl = ["tls-openssl", "awc/openssl"]
[dependencies] [dependencies]
actix-service = "2.0.0" actix-service = "2.0.0"
actix-codec = "0.4.1" actix-codec = "0.4.1"
actix-tls = "3.0.0-beta.7" actix-tls = "3.0.0-beta.9"
actix-utils = "3.0.0" actix-utils = "3.0.0"
actix-rt = "2.2" actix-rt = "2.2"
actix-server = "2.0.0-beta.9" actix-server = "2.0.0-beta.9"

View File

@ -3,6 +3,7 @@
## Unreleased - 2021-xx-xx ## Unreleased - 2021-xx-xx
### Added ### Added
* `body::AnyBody::empty` for quickly creating an empty body. [#2446] * `body::AnyBody::empty` for quickly creating an empty body. [#2446]
* `body::AnyBody::none` for quickly creating a "none" body. [#2456]
* `impl Clone` for `body::AnyBody<S> where S: Clone`. [#2448] * `impl Clone` for `body::AnyBody<S> where S: Clone`. [#2448]
* `body::AnyBody::into_boxed` for quickly converting to a type-erased, boxed body type. [#2448] * `body::AnyBody::into_boxed` for quickly converting to a type-erased, boxed body type. [#2448]
@ -21,6 +22,7 @@
* `body::ResponseBody`; is function is replaced by the new `body::AnyBody` enum. [#2446] * `body::ResponseBody`; is function is replaced by the new `body::AnyBody` enum. [#2446]
[#2446]: https://github.com/actix/actix-web/pull/2446 [#2446]: https://github.com/actix/actix-web/pull/2446
[#2456]: https://github.com/actix/actix-web/pull/2456
## 3.0.0-beta.12 - 2021-11-15 ## 3.0.0-beta.12 - 2021-11-15

View File

@ -73,7 +73,7 @@ sha-1 = "0.9"
smallvec = "1.6.1" smallvec = "1.6.1"
# tls # tls
actix-tls = { version = "3.0.0-beta.7", default-features = false, optional = true } actix-tls = { version = "3.0.0-beta.9", default-features = false, optional = true }
# compression # compression
brotli2 = { version="0.3.2", optional = true } brotli2 = { version="0.3.2", optional = true }
@ -83,7 +83,7 @@ zstd = { version = "0.9", optional = true }
[dev-dependencies] [dev-dependencies]
actix-server = "2.0.0-beta.9" actix-server = "2.0.0-beta.9"
actix-http-test = { version = "3.0.0-beta.6", features = ["openssl"] } actix-http-test = { version = "3.0.0-beta.6", features = ["openssl"] }
actix-tls = { version = "3.0.0-beta.7", features = ["openssl"] } actix-tls = { version = "3.0.0-beta.9", features = ["openssl"] }
async-stream = "0.3" async-stream = "0.3"
criterion = { version = "0.3", features = ["html_reports"] } criterion = { version = "0.3", features = ["html_reports"] }
env_logger = "0.9" env_logger = "0.9"

View File

@ -32,9 +32,12 @@ pub enum AnyBody<B = BoxBody> {
} }
impl AnyBody { impl AnyBody {
// TODO: a None body constructor /// Constructs a "body" representing an empty response.
pub fn none() -> Self {
Self::None
}
/// Constructs a new, empty body. /// Constructs a new, 0-length body.
pub fn empty() -> Self { pub fn empty() -> Self {
Self::Bytes(Bytes::new()) Self::Bytes(Bytes::new())
} }

View File

@ -32,11 +32,11 @@ pub use self::sized_stream::SizedStream;
/// use bytes::Bytes; /// use bytes::Bytes;
/// ///
/// # async fn test_to_bytes() { /// # async fn test_to_bytes() {
/// let body = AnyBody::None; /// let body = AnyBody::none();
/// let bytes = to_bytes(body).await.unwrap(); /// let bytes = to_bytes(body).await.unwrap();
/// assert!(bytes.is_empty()); /// assert!(bytes.is_empty());
/// ///
/// let body = AnyBody::Bytes(Bytes::from_static(b"123")); /// let body = AnyBody::copy_from_slice(b"123");
/// let bytes = to_bytes(body).await.unwrap(); /// let bytes = to_bytes(body).await.unwrap();
/// assert_eq!(bytes, b"123"[..]); /// assert_eq!(bytes, b"123"[..]);
/// # } /// # }
@ -75,7 +75,7 @@ mod tests {
use actix_utils::future::poll_fn; use actix_utils::future::poll_fn;
use bytes::{Bytes, BytesMut}; use bytes::{Bytes, BytesMut};
use super::*; use super::{to_bytes, AnyBody as TestAnyBody, BodySize, MessageBody as _};
impl AnyBody { impl AnyBody {
pub(crate) fn get_ref(&self) -> &[u8] { pub(crate) fn get_ref(&self) -> &[u8] {
@ -87,13 +87,13 @@ mod tests {
} }
/// AnyBody alias because rustc does not (can not?) infer the default type parameter. /// AnyBody alias because rustc does not (can not?) infer the default type parameter.
type TestBody = AnyBody; type AnyBody = TestAnyBody;
#[actix_rt::test] #[actix_rt::test]
async fn test_static_str() { async fn test_static_str() {
assert_eq!(TestBody::from("").size(), BodySize::Sized(0)); assert_eq!(AnyBody::from("").size(), BodySize::Sized(0));
assert_eq!(TestBody::from("test").size(), BodySize::Sized(4)); assert_eq!(AnyBody::from("test").size(), BodySize::Sized(4));
assert_eq!(TestBody::from("test").get_ref(), b"test"); assert_eq!(AnyBody::from("test").get_ref(), b"test");
assert_eq!("test".size(), BodySize::Sized(4)); assert_eq!("test".size(), BodySize::Sized(4));
assert_eq!( assert_eq!(
@ -107,14 +107,14 @@ mod tests {
#[actix_rt::test] #[actix_rt::test]
async fn test_static_bytes() { async fn test_static_bytes() {
assert_eq!(TestBody::from(b"test".as_ref()).size(), BodySize::Sized(4)); assert_eq!(AnyBody::from(b"test".as_ref()).size(), BodySize::Sized(4));
assert_eq!(TestBody::from(b"test".as_ref()).get_ref(), b"test"); assert_eq!(AnyBody::from(b"test".as_ref()).get_ref(), b"test");
assert_eq!( assert_eq!(
TestBody::copy_from_slice(b"test".as_ref()).size(), AnyBody::copy_from_slice(b"test".as_ref()).size(),
BodySize::Sized(4) BodySize::Sized(4)
); );
assert_eq!( assert_eq!(
TestBody::copy_from_slice(b"test".as_ref()).get_ref(), AnyBody::copy_from_slice(b"test".as_ref()).get_ref(),
b"test" b"test"
); );
let sb = Bytes::from(&b"test"[..]); let sb = Bytes::from(&b"test"[..]);
@ -129,8 +129,8 @@ mod tests {
#[actix_rt::test] #[actix_rt::test]
async fn test_vec() { async fn test_vec() {
assert_eq!(TestBody::from(Vec::from("test")).size(), BodySize::Sized(4)); assert_eq!(AnyBody::from(Vec::from("test")).size(), BodySize::Sized(4));
assert_eq!(TestBody::from(Vec::from("test")).get_ref(), b"test"); assert_eq!(AnyBody::from(Vec::from("test")).get_ref(), b"test");
let test_vec = Vec::from("test"); let test_vec = Vec::from("test");
pin!(test_vec); pin!(test_vec);
@ -147,8 +147,8 @@ mod tests {
#[actix_rt::test] #[actix_rt::test]
async fn test_bytes() { async fn test_bytes() {
let b = Bytes::from("test"); let b = Bytes::from("test");
assert_eq!(TestBody::from(b.clone()).size(), BodySize::Sized(4)); assert_eq!(AnyBody::from(b.clone()).size(), BodySize::Sized(4));
assert_eq!(TestBody::from(b.clone()).get_ref(), b"test"); assert_eq!(AnyBody::from(b.clone()).get_ref(), b"test");
pin!(b); pin!(b);
assert_eq!(b.size(), BodySize::Sized(4)); assert_eq!(b.size(), BodySize::Sized(4));
@ -161,8 +161,8 @@ mod tests {
#[actix_rt::test] #[actix_rt::test]
async fn test_bytes_mut() { async fn test_bytes_mut() {
let b = BytesMut::from("test"); let b = BytesMut::from("test");
assert_eq!(TestBody::from(b.clone()).size(), BodySize::Sized(4)); assert_eq!(AnyBody::from(b.clone()).size(), BodySize::Sized(4));
assert_eq!(TestBody::from(b.clone()).get_ref(), b"test"); assert_eq!(AnyBody::from(b.clone()).get_ref(), b"test");
pin!(b); pin!(b);
assert_eq!(b.size(), BodySize::Sized(4)); assert_eq!(b.size(), BodySize::Sized(4));
@ -175,10 +175,10 @@ mod tests {
#[actix_rt::test] #[actix_rt::test]
async fn test_string() { async fn test_string() {
let b = "test".to_owned(); let b = "test".to_owned();
assert_eq!(TestBody::from(b.clone()).size(), BodySize::Sized(4)); assert_eq!(AnyBody::from(b.clone()).size(), BodySize::Sized(4));
assert_eq!(TestBody::from(b.clone()).get_ref(), b"test"); assert_eq!(AnyBody::from(b.clone()).get_ref(), b"test");
assert_eq!(TestBody::from(&b).size(), BodySize::Sized(4)); assert_eq!(AnyBody::from(&b).size(), BodySize::Sized(4));
assert_eq!(TestBody::from(&b).get_ref(), b"test"); assert_eq!(AnyBody::from(&b).get_ref(), b"test");
pin!(b); pin!(b);
assert_eq!(b.size(), BodySize::Sized(4)); assert_eq!(b.size(), BodySize::Sized(4));
@ -219,22 +219,22 @@ mod tests {
#[actix_rt::test] #[actix_rt::test]
async fn test_body_debug() { async fn test_body_debug() {
assert!(format!("{:?}", TestBody::None).contains("Body::None")); assert!(format!("{:?}", AnyBody::None).contains("Body::None"));
assert!(format!("{:?}", TestBody::from(Bytes::from_static(b"1"))).contains('1')); assert!(format!("{:?}", AnyBody::from(Bytes::from_static(b"1"))).contains('1'));
} }
#[actix_rt::test] #[actix_rt::test]
async fn test_serde_json() { async fn test_serde_json() {
use serde_json::{json, Value}; use serde_json::{json, Value};
assert_eq!( assert_eq!(
TestBody::from( AnyBody::from(
serde_json::to_vec(&Value::String("test".to_owned())).unwrap() serde_json::to_vec(&Value::String("test".to_owned())).unwrap()
) )
.size(), .size(),
BodySize::Sized(6) BodySize::Sized(6)
); );
assert_eq!( assert_eq!(
TestBody::from( AnyBody::from(
serde_json::to_vec(&json!({"test-key":"test-value"})).unwrap() serde_json::to_vec(&json!({"test-key":"test-value"})).unwrap()
) )
.size(), .size(),

View File

@ -102,7 +102,6 @@ where
mod openssl { mod openssl {
use super::*; use super::*;
use actix_service::ServiceFactoryExt;
use actix_tls::accept::{ use actix_tls::accept::{
openssl::{Acceptor, SslAcceptor, SslError, TlsStream}, openssl::{Acceptor, SslAcceptor, SslError, TlsStream},
TlsError, TlsError,
@ -133,7 +132,7 @@ mod openssl {
U::Error: fmt::Display + Into<Response<AnyBody>>, U::Error: fmt::Display + Into<Response<AnyBody>>,
U::InitError: fmt::Debug, U::InitError: fmt::Debug,
{ {
/// Create openssl based service /// Create OpenSSL based service.
pub fn openssl( pub fn openssl(
self, self,
acceptor: SslAcceptor, acceptor: SslAcceptor,
@ -145,11 +144,13 @@ mod openssl {
InitError = (), InitError = (),
> { > {
Acceptor::new(acceptor) Acceptor::new(acceptor)
.map_err(TlsError::Tls) .map_init_err(|_| {
.map_init_err(|_| panic!()) unreachable!("TLS acceptor service factory does not error on init")
.and_then(|io: TlsStream<TcpStream>| { })
.map_err(TlsError::into_service_error)
.map(|io: TlsStream<TcpStream>| {
let peer_addr = io.get_ref().peer_addr().ok(); let peer_addr = io.get_ref().peer_addr().ok();
ready(Ok((io, peer_addr))) (io, peer_addr)
}) })
.and_then(self.map_err(TlsError::Service)) .and_then(self.map_err(TlsError::Service))
} }
@ -158,16 +159,17 @@ mod openssl {
#[cfg(feature = "rustls")] #[cfg(feature = "rustls")]
mod rustls { mod rustls {
use super::*;
use std::io; use std::io;
use actix_service::ServiceFactoryExt; use actix_service::ServiceFactoryExt as _;
use actix_tls::accept::{ use actix_tls::accept::{
rustls::{Acceptor, ServerConfig, TlsStream}, rustls::{Acceptor, ServerConfig, TlsStream},
TlsError, TlsError,
}; };
use super::*;
impl<S, B, X, U> H1Service<TlsStream<TcpStream>, S, B, X, U> impl<S, B, X, U> H1Service<TlsStream<TcpStream>, S, B, X, U>
where where
S: ServiceFactory<Request, Config = ()>, S: ServiceFactory<Request, Config = ()>,
@ -193,7 +195,7 @@ mod rustls {
U::Error: fmt::Display + Into<Response<AnyBody>>, U::Error: fmt::Display + Into<Response<AnyBody>>,
U::InitError: fmt::Debug, U::InitError: fmt::Debug,
{ {
/// Create rustls based service /// Create Rustls based service.
pub fn rustls( pub fn rustls(
self, self,
config: ServerConfig, config: ServerConfig,
@ -205,11 +207,13 @@ mod rustls {
InitError = (), InitError = (),
> { > {
Acceptor::new(config) Acceptor::new(config)
.map_err(TlsError::Tls) .map_init_err(|_| {
.map_init_err(|_| panic!()) unreachable!("TLS acceptor service factory does not error on init")
.and_then(|io: TlsStream<TcpStream>| { })
.map_err(TlsError::into_service_error)
.map(|io: TlsStream<TcpStream>| {
let peer_addr = io.get_ref().0.peer_addr().ok(); let peer_addr = io.get_ref().0.peer_addr().ok();
ready(Ok((io, peer_addr))) (io, peer_addr)
}) })
.and_then(self.map_err(TlsError::Service)) .and_then(self.map_err(TlsError::Service))
} }

View File

@ -101,9 +101,11 @@ where
#[cfg(feature = "openssl")] #[cfg(feature = "openssl")]
mod openssl { mod openssl {
use actix_service::{fn_factory, fn_service, ServiceFactoryExt}; use actix_service::ServiceFactoryExt as _;
use actix_tls::accept::openssl::{Acceptor, SslAcceptor, SslError, TlsStream}; use actix_tls::accept::{
use actix_tls::accept::TlsError; openssl::{Acceptor, SslAcceptor, SslError, TlsStream},
TlsError,
};
use super::*; use super::*;
@ -118,7 +120,7 @@ mod openssl {
B: MessageBody + 'static, B: MessageBody + 'static,
B::Error: Into<Box<dyn StdError>>, B::Error: Into<Box<dyn StdError>>,
{ {
/// Create OpenSSL based service /// Create OpenSSL based service.
pub fn openssl( pub fn openssl(
self, self,
acceptor: SslAcceptor, acceptor: SslAcceptor,
@ -130,16 +132,14 @@ mod openssl {
InitError = S::InitError, InitError = S::InitError,
> { > {
Acceptor::new(acceptor) Acceptor::new(acceptor)
.map_err(TlsError::Tls) .map_init_err(|_| {
.map_init_err(|_| panic!()) unreachable!("TLS acceptor service factory does not error on init")
.and_then(fn_factory(|| { })
ready(Ok::<_, S::InitError>(fn_service( .map_err(TlsError::into_service_error)
|io: TlsStream<TcpStream>| { .map(|io: TlsStream<TcpStream>| {
let peer_addr = io.get_ref().peer_addr().ok(); let peer_addr = io.get_ref().peer_addr().ok();
ready(Ok((io, peer_addr))) (io, peer_addr)
}, })
)))
}))
.and_then(self.map_err(TlsError::Service)) .and_then(self.map_err(TlsError::Service))
} }
} }
@ -147,12 +147,16 @@ mod openssl {
#[cfg(feature = "rustls")] #[cfg(feature = "rustls")]
mod rustls { mod rustls {
use super::*;
use actix_service::ServiceFactoryExt;
use actix_tls::accept::rustls::{Acceptor, ServerConfig, TlsStream};
use actix_tls::accept::TlsError;
use std::io; use std::io;
use actix_service::ServiceFactoryExt as _;
use actix_tls::accept::{
rustls::{Acceptor, ServerConfig, TlsStream},
TlsError,
};
use super::*;
impl<S, B> H2Service<TlsStream<TcpStream>, S, B> impl<S, B> H2Service<TlsStream<TcpStream>, S, B>
where where
S: ServiceFactory<Request, Config = ()>, S: ServiceFactory<Request, Config = ()>,
@ -164,7 +168,7 @@ mod rustls {
B: MessageBody + 'static, B: MessageBody + 'static,
B::Error: Into<Box<dyn StdError>>, B::Error: Into<Box<dyn StdError>>,
{ {
/// Create Rustls based service /// Create Rustls based service.
pub fn rustls( pub fn rustls(
self, self,
mut config: ServerConfig, mut config: ServerConfig,
@ -180,16 +184,14 @@ mod rustls {
config.alpn_protocols = protos; config.alpn_protocols = protos;
Acceptor::new(config) Acceptor::new(config)
.map_err(TlsError::Tls) .map_init_err(|_| {
.map_init_err(|_| panic!()) unreachable!("TLS acceptor service factory does not error on init")
.and_then(fn_factory(|| { })
ready(Ok::<_, S::InitError>(fn_service( .map_err(TlsError::into_service_error)
|io: TlsStream<TcpStream>| { .map(|io: TlsStream<TcpStream>| {
let peer_addr = io.get_ref().0.peer_addr().ok(); let peer_addr = io.get_ref().0.peer_addr().ok();
ready(Ok((io, peer_addr))) (io, peer_addr)
}, })
)))
}))
.and_then(self.map_err(TlsError::Service)) .and_then(self.map_err(TlsError::Service))
} }
} }

View File

@ -195,9 +195,11 @@ where
#[cfg(feature = "openssl")] #[cfg(feature = "openssl")]
mod openssl { mod openssl {
use actix_service::ServiceFactoryExt; use actix_service::ServiceFactoryExt as _;
use actix_tls::accept::openssl::{Acceptor, SslAcceptor, SslError, TlsStream}; use actix_tls::accept::{
use actix_tls::accept::TlsError; openssl::{Acceptor, SslAcceptor, SslError, TlsStream},
TlsError,
};
use super::*; use super::*;
@ -227,7 +229,7 @@ mod openssl {
U::Error: fmt::Display + Into<Response<AnyBody>>, U::Error: fmt::Display + Into<Response<AnyBody>>,
U::InitError: fmt::Debug, U::InitError: fmt::Debug,
{ {
/// Create openssl based service /// Create OpenSSL based service.
pub fn openssl( pub fn openssl(
self, self,
acceptor: SslAcceptor, acceptor: SslAcceptor,
@ -239,9 +241,11 @@ mod openssl {
InitError = (), InitError = (),
> { > {
Acceptor::new(acceptor) Acceptor::new(acceptor)
.map_err(TlsError::Tls) .map_init_err(|_| {
.map_init_err(|_| panic!()) unreachable!("TLS acceptor service factory does not error on init")
.and_then(|io: TlsStream<TcpStream>| async { })
.map_err(TlsError::into_service_error)
.map(|io: TlsStream<TcpStream>| {
let proto = if let Some(protos) = io.ssl().selected_alpn_protocol() { let proto = if let Some(protos) = io.ssl().selected_alpn_protocol() {
if protos.windows(2).any(|window| window == b"h2") { if protos.windows(2).any(|window| window == b"h2") {
Protocol::Http2 Protocol::Http2
@ -251,8 +255,9 @@ mod openssl {
} else { } else {
Protocol::Http1 Protocol::Http1
}; };
let peer_addr = io.get_ref().peer_addr().ok(); let peer_addr = io.get_ref().peer_addr().ok();
Ok((io, proto, peer_addr)) (io, proto, peer_addr)
}) })
.and_then(self.map_err(TlsError::Service)) .and_then(self.map_err(TlsError::Service))
} }
@ -263,11 +268,13 @@ mod openssl {
mod rustls { mod rustls {
use std::io; use std::io;
use actix_tls::accept::rustls::{Acceptor, ServerConfig, TlsStream}; use actix_service::ServiceFactoryExt as _;
use actix_tls::accept::TlsError; use actix_tls::accept::{
rustls::{Acceptor, ServerConfig, TlsStream},
TlsError,
};
use super::*; use super::*;
use actix_service::ServiceFactoryExt;
impl<S, B, X, U> HttpService<TlsStream<TcpStream>, S, B, X, U> impl<S, B, X, U> HttpService<TlsStream<TcpStream>, S, B, X, U>
where where
@ -295,7 +302,7 @@ mod rustls {
U::Error: fmt::Display + Into<Response<AnyBody>>, U::Error: fmt::Display + Into<Response<AnyBody>>,
U::InitError: fmt::Debug, U::InitError: fmt::Debug,
{ {
/// Create rustls based service /// Create Rustls based service.
pub fn rustls( pub fn rustls(
self, self,
mut config: ServerConfig, mut config: ServerConfig,
@ -311,8 +318,10 @@ mod rustls {
config.alpn_protocols = protos; config.alpn_protocols = protos;
Acceptor::new(config) Acceptor::new(config)
.map_err(TlsError::Tls) .map_init_err(|_| {
.map_init_err(|_| panic!()) unreachable!("TLS acceptor service factory does not error on init")
})
.map_err(TlsError::into_service_error)
.and_then(|io: TlsStream<TcpStream>| async { .and_then(|io: TlsStream<TcpStream>| async {
let proto = if let Some(protos) = io.get_ref().1.alpn_protocol() { let proto = if let Some(protos) = io.get_ref().1.alpn_protocol() {
if protos.windows(2).any(|window| window == b"h2") { if protos.windows(2).any(|window| window == b"h2") {

View File

@ -57,7 +57,7 @@ actix-codec = "0.4.1"
actix-service = "2.0.0" actix-service = "2.0.0"
actix-http = "3.0.0-beta.12" actix-http = "3.0.0-beta.12"
actix-rt = { version = "2.1", default-features = false } actix-rt = { version = "2.1", default-features = false }
actix-tls = { version = "3.0.0-beta.7", features = ["connect"] } actix-tls = { version = "3.0.0-beta.9", features = ["connect"] }
actix-utils = "3.0.0" actix-utils = "3.0.0"
ahash = "0.7" ahash = "0.7"
@ -93,7 +93,7 @@ actix-http = { version = "3.0.0-beta.12", features = ["openssl"] }
actix-http-test = { version = "3.0.0-beta.6", features = ["openssl"] } actix-http-test = { version = "3.0.0-beta.6", features = ["openssl"] }
actix-utils = "3.0.0" actix-utils = "3.0.0"
actix-server = "2.0.0-beta.9" actix-server = "2.0.0-beta.9"
actix-tls = { version = "3.0.0-beta.7", features = ["openssl", "rustls"] } actix-tls = { version = "3.0.0-beta.9", features = ["openssl", "rustls"] }
actix-test = { version = "0.1.0-beta.6", features = ["openssl", "rustls"] } actix-test = { version = "0.1.0-beta.6", features = ["openssl", "rustls"] }
brotli2 = "0.3.2" brotli2 = "0.3.2"