1
0
Fork 0
mirror of https://github.com/actix/actix-web.git synced 2024-11-22 17:41:11 +00:00

expose h2c methods on HttpServer (#2999

* expose h2c methods on HttpServer

* update h2c docs
This commit is contained in:
Rob Ede 2023-04-06 03:11:28 +01:00 committed by GitHub
parent 97399e8c8c
commit e81dc768dc
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 77 additions and 13 deletions

View file

@ -4,6 +4,7 @@
### Added ### Added
- Add `HttpServer::{bind,listen}_auto_h2c()` method.
- Add `Resource::{get, post, etc...}` methods for more concisely adding routes that don't need additional guards. - Add `Resource::{get, post, etc...}` methods for more concisely adding routes that don't need additional guards.
### Changed ### Changed

View file

@ -4,8 +4,6 @@
//! For an example of extracting a client TLS certificate, see: //! For an example of extracting a client TLS certificate, see:
//! <https://github.com/actix/examples/tree/master/https-tls/rustls-client-cert> //! <https://github.com/actix/examples/tree/master/https-tls/rustls-client-cert>
#![allow(clippy::uninlined_format_args)]
use std::{any::Any, io, net::SocketAddr}; use std::{any::Any, io, net::SocketAddr};
use actix_web::{ use actix_web::{
@ -24,8 +22,7 @@ struct ConnectionInfo {
async fn route_whoami(req: HttpRequest) -> impl Responder { async fn route_whoami(req: HttpRequest) -> impl Responder {
match req.conn_data::<ConnectionInfo>() { match req.conn_data::<ConnectionInfo>() {
Some(info) => HttpResponse::Ok().body(format!( Some(info) => HttpResponse::Ok().body(format!(
"Here is some info about your connection:\n\n{:#?}", "Here is some info about your connection:\n\n{info:#?}",
info
)), )),
None => { None => {
HttpResponse::InternalServerError().body("Missing expected request extension data") HttpResponse::InternalServerError().body("Missing expected request extension data")
@ -54,8 +51,8 @@ async fn main() -> io::Result<()> {
HttpServer::new(|| App::new().default_service(web::to(route_whoami))) HttpServer::new(|| App::new().default_service(web::to(route_whoami)))
.on_connect(get_conn_info) .on_connect(get_conn_info)
.bind(bind)? .bind_auto_h2c(bind)?
.workers(1) .workers(2)
.run() .run()
.await .await
} }

View file

@ -41,10 +41,19 @@ struct Config {
/// ///
/// Create new HTTP server with application factory. /// Create new HTTP server with application factory.
/// ///
/// # HTTP/2 /// # Automatic HTTP Version Selection
/// Currently, HTTP/2 is only supported when using TLS (HTTPS). See `bind_rustls` or `bind_openssl`. ///
/// There are two ways to select the HTTP version of an incoming connection:
///
/// - One is to rely on the ALPN information that is provided when using a TLS (HTTPS); both
/// versions are supported automatically when using either of the `.bind_rustls()` or
/// `.bind_openssl()` methods.
/// - The other is to read the first few bytes of the TCP stream. This is the only viable approach
/// for supporting H2C, which allows the HTTP/2 protocol to work over plaintext connections. Use
/// the `.bind_auto_h2c()` method to enable this behavior.
/// ///
/// # Examples /// # Examples
///
/// ```no_run /// ```no_run
/// use actix_web::{web, App, HttpResponse, HttpServer}; /// use actix_web::{web, App, HttpResponse, HttpServer};
/// ///
@ -347,6 +356,18 @@ where
Ok(self) Ok(self)
} }
/// Resolves socket address(es) and binds server to created listener(s) for plaintext HTTP/1.x
/// or HTTP/2 connections.
pub fn bind_auto_h2c<A: net::ToSocketAddrs>(mut self, addrs: A) -> io::Result<Self> {
let sockets = bind_addrs(addrs, self.backlog)?;
for lst in sockets {
self = self.listen_auto_h2c(lst)?;
}
Ok(self)
}
/// Resolves socket address(es) and binds server to created listener(s) for TLS connections /// Resolves socket address(es) and binds server to created listener(s) for TLS connections
/// using Rustls. /// using Rustls.
/// ///
@ -406,13 +427,13 @@ where
self.builder = self.builder =
self.builder self.builder
.listen(format!("actix-web-service-{}", addr), lst, move || { .listen(format!("actix-web-service-{}", addr), lst, move || {
let c = cfg.lock().unwrap(); let cfg = cfg.lock().unwrap();
let host = c.host.clone().unwrap_or_else(|| format!("{}", addr)); let host = cfg.host.clone().unwrap_or_else(|| format!("{}", addr));
let mut svc = HttpService::build() let mut svc = HttpService::build()
.keep_alive(c.keep_alive) .keep_alive(cfg.keep_alive)
.client_request_timeout(c.client_request_timeout) .client_request_timeout(cfg.client_request_timeout)
.client_disconnect_timeout(c.client_disconnect_timeout) .client_disconnect_timeout(cfg.client_disconnect_timeout)
.local_addr(addr); .local_addr(addr);
if let Some(handler) = on_connect_fn.clone() { if let Some(handler) = on_connect_fn.clone() {
@ -430,6 +451,51 @@ where
})) }))
.tcp() .tcp()
})?; })?;
Ok(self)
}
/// Binds to existing listener for accepting incoming plaintext HTTP/1.x or HTTP/2 connections.
pub fn listen_auto_h2c(mut self, lst: net::TcpListener) -> io::Result<Self> {
let cfg = self.config.clone();
let factory = self.factory.clone();
let addr = lst.local_addr().unwrap();
self.sockets.push(Socket {
addr,
scheme: "http",
});
let on_connect_fn = self.on_connect_fn.clone();
self.builder =
self.builder
.listen(format!("actix-web-service-{}", addr), lst, move || {
let cfg = cfg.lock().unwrap();
let host = cfg.host.clone().unwrap_or_else(|| format!("{}", addr));
let mut svc = HttpService::build()
.keep_alive(cfg.keep_alive)
.client_request_timeout(cfg.client_request_timeout)
.client_disconnect_timeout(cfg.client_disconnect_timeout)
.local_addr(addr);
if let Some(handler) = on_connect_fn.clone() {
svc = svc.on_connect_ext(move |io: &_, ext: _| {
(handler)(io as &dyn Any, ext)
})
};
let fac = factory()
.into_factory()
.map_err(|err| err.into().error_response());
svc.finish(map_config(fac, move |_| {
AppConfig::new(false, host.clone(), addr)
}))
.tcp_auto_h2c()
})?;
Ok(self) Ok(self)
} }