1
0
mirror of https://github.com/fafhrd91/actix-web synced 2025-06-26 15:07:42 +02:00

awc: support http2 over plain tcp with feature flag (#2439)

Co-authored-by: Rob Ede <robjtede@icloud.com>
This commit is contained in:
fakeshadow
2021-11-23 02:16:56 +08:00
committed by GitHub
parent a172f5968d
commit e7987e7429
6 changed files with 136 additions and 27 deletions

View File

@ -67,9 +67,9 @@ impl Connector<()> {
> + Clone,
> {
Connector {
ssl: Self::build_ssl(vec![b"h2".to_vec(), b"http/1.1".to_vec()]),
connector: new_connector(resolver::resolver()),
config: ConnectorConfig::default(),
ssl: Self::build_ssl(vec![b"h2".to_vec(), b"http/1.1".to_vec()]),
}
}
@ -189,7 +189,7 @@ where
http::Version::HTTP_11 => vec![b"http/1.1".to_vec()],
http::Version::HTTP_2 => vec![b"h2".to_vec(), b"http/1.1".to_vec()],
_ => {
unimplemented!("actix-http:client: supported versions http/1.1, http/2")
unimplemented!("actix-http client only supports versions http/1.1 & http/2")
}
};
self.ssl = Connector::build_ssl(versions);
@ -279,7 +279,63 @@ where
};
let tls_service = match self.ssl {
SslConnector::None => None,
SslConnector::None => {
#[cfg(not(feature = "dangerous-h2c"))]
{
None
}
#[cfg(feature = "dangerous-h2c")]
{
use std::{
future::{ready, Ready},
io,
};
use actix_tls::connect::Connection;
impl IntoConnectionIo for TcpConnection<Uri, Box<dyn ConnectionIo>> {
fn into_connection_io(self) -> (Box<dyn ConnectionIo>, Protocol) {
let io = self.into_parts().0;
(io, Protocol::Http2)
}
}
/// With the `dangerous-h2c` feature enabled, this connector uses a no-op TLS
/// connection service that passes through plain TCP as a TLS connection.
///
/// The protocol version of this fake TLS connection is set to be HTTP/2.
#[derive(Clone)]
struct NoOpTlsConnectorService;
impl<T, U> Service<Connection<T, U>> for NoOpTlsConnectorService
where
U: ActixStream + 'static,
{
type Response = Connection<T, Box<dyn ConnectionIo>>;
type Error = io::Error;
type Future = Ready<Result<Self::Response, Self::Error>>;
actix_service::always_ready!();
fn call(&self, connection: Connection<T, U>) -> Self::Future {
let (io, connection) = connection.replace_io(());
let (_, connection) = connection.replace_io(Box::new(io) as _);
ready(Ok(connection))
}
}
let handshake_timeout = self.config.handshake_timeout;
let tls_service = TlsConnectorService {
tcp_service: tcp_service_inner,
tls_service: NoOpTlsConnectorService,
timeout: handshake_timeout,
};
Some(actix_service::boxed::rc_service(tls_service))
}
}
#[cfg(feature = "openssl")]
SslConnector::Openssl(tls) => {
const H2: &[u8] = b"h2";
@ -760,3 +816,42 @@ mod resolver {
})
}
}
#[cfg(feature = "dangerous-h2c")]
#[cfg(test)]
mod tests {
use std::convert::Infallible;
use actix_http::{HttpService, Request, Response, Version};
use actix_http_test::test_server;
use actix_service::ServiceFactoryExt as _;
use super::*;
use crate::Client;
#[actix_rt::test]
async fn h2c_connector() {
let mut srv = test_server(|| {
HttpService::build()
.h2(|_req: Request| async { Ok::<_, Infallible>(Response::ok()) })
.tcp()
.map_err(|_| ())
})
.await;
let connector = Connector {
connector: new_connector(resolver::resolver()),
config: ConnectorConfig::default(),
ssl: SslConnector::None,
};
let client = Client::builder().connector(connector).finish();
let request = client.get(srv.surl("/")).send();
let response = request.await.unwrap();
assert!(response.status().is_success());
assert_eq!(response.version(), Version::HTTP_2);
srv.stop().await;
}
}

View File

@ -115,10 +115,10 @@ impl ClientRequest {
&self.head.method
}
#[doc(hidden)]
/// Set HTTP version of this request.
///
/// By default requests's HTTP version depends on network stream
#[doc(hidden)]
#[inline]
pub fn version(mut self, version: Version) -> Self {
self.head.version = version;