mirror of https://github.com/fafhrd91/actix-web
Add option to allow/disallow h1 half closures
The default is set to allow, thus reverting a change made in 3.11.1 Signed-off-by: Thales Fragoso <thales.fragoso@axiros.com>
This commit is contained in:
parent
3a3e286b35
commit
fa5fdcbfea
|
|
@ -2,7 +2,9 @@
|
||||||
|
|
||||||
## Unreleased
|
## Unreleased
|
||||||
|
|
||||||
- Properly wake Payload receivers when feeding errors or EOF
|
- Properly wake Payload receivers when feeding errors or EOF.
|
||||||
|
- Add `ServiceConfigBuilder` type to facilitate future configuration extensions.
|
||||||
|
- Add a configuration option to allow/disallow half closed connections in HTTP/1. This defaults to allow, reverting the change made in 3.11.1.
|
||||||
|
|
||||||
## 3.11.1
|
## 3.11.1
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -19,6 +19,7 @@ pub struct HttpServiceBuilder<T, S, X = ExpectHandler, U = UpgradeHandler> {
|
||||||
client_disconnect_timeout: Duration,
|
client_disconnect_timeout: Duration,
|
||||||
secure: bool,
|
secure: bool,
|
||||||
local_addr: Option<net::SocketAddr>,
|
local_addr: Option<net::SocketAddr>,
|
||||||
|
h1_allow_half_closed: bool,
|
||||||
expect: X,
|
expect: X,
|
||||||
upgrade: Option<U>,
|
upgrade: Option<U>,
|
||||||
on_connect_ext: Option<Rc<ConnectCallback<T>>>,
|
on_connect_ext: Option<Rc<ConnectCallback<T>>>,
|
||||||
|
|
@ -40,6 +41,7 @@ where
|
||||||
client_disconnect_timeout: Duration::ZERO,
|
client_disconnect_timeout: Duration::ZERO,
|
||||||
secure: false,
|
secure: false,
|
||||||
local_addr: None,
|
local_addr: None,
|
||||||
|
h1_allow_half_closed: true,
|
||||||
|
|
||||||
// dispatcher parts
|
// dispatcher parts
|
||||||
expect: ExpectHandler,
|
expect: ExpectHandler,
|
||||||
|
|
@ -124,6 +126,18 @@ where
|
||||||
self.client_disconnect_timeout(dur)
|
self.client_disconnect_timeout(dur)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Sets whether HTTP/1 connections should support half-closures.
|
||||||
|
///
|
||||||
|
/// Clients can choose to shutdown their writer-side of the connection after completing their
|
||||||
|
/// request and while waiting for the server response. Setting this to `false` will cause the
|
||||||
|
/// server to abort the connection handling as soon as it detects an EOF from the client.
|
||||||
|
///
|
||||||
|
/// The default behavior is to allow, i.e. `true`
|
||||||
|
pub fn h1_allow_half_closed(mut self, allow: bool) -> Self {
|
||||||
|
self.h1_allow_half_closed = allow;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
/// Provide service for `EXPECT: 100-Continue` support.
|
/// Provide service for `EXPECT: 100-Continue` support.
|
||||||
///
|
///
|
||||||
/// Service get called with request that contains `EXPECT` header.
|
/// Service get called with request that contains `EXPECT` header.
|
||||||
|
|
@ -142,6 +156,7 @@ where
|
||||||
client_disconnect_timeout: self.client_disconnect_timeout,
|
client_disconnect_timeout: self.client_disconnect_timeout,
|
||||||
secure: self.secure,
|
secure: self.secure,
|
||||||
local_addr: self.local_addr,
|
local_addr: self.local_addr,
|
||||||
|
h1_allow_half_closed: self.h1_allow_half_closed,
|
||||||
expect: expect.into_factory(),
|
expect: expect.into_factory(),
|
||||||
upgrade: self.upgrade,
|
upgrade: self.upgrade,
|
||||||
on_connect_ext: self.on_connect_ext,
|
on_connect_ext: self.on_connect_ext,
|
||||||
|
|
@ -166,6 +181,7 @@ where
|
||||||
client_disconnect_timeout: self.client_disconnect_timeout,
|
client_disconnect_timeout: self.client_disconnect_timeout,
|
||||||
secure: self.secure,
|
secure: self.secure,
|
||||||
local_addr: self.local_addr,
|
local_addr: self.local_addr,
|
||||||
|
h1_allow_half_closed: self.h1_allow_half_closed,
|
||||||
expect: self.expect,
|
expect: self.expect,
|
||||||
upgrade: Some(upgrade.into_factory()),
|
upgrade: Some(upgrade.into_factory()),
|
||||||
on_connect_ext: self.on_connect_ext,
|
on_connect_ext: self.on_connect_ext,
|
||||||
|
|
@ -201,6 +217,7 @@ where
|
||||||
.client_disconnect_timeout(self.client_disconnect_timeout)
|
.client_disconnect_timeout(self.client_disconnect_timeout)
|
||||||
.secure(self.secure)
|
.secure(self.secure)
|
||||||
.local_addr(self.local_addr)
|
.local_addr(self.local_addr)
|
||||||
|
.h1_allow_half_closed(self.h1_allow_half_closed)
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
H1Service::with_config(cfg, service.into_factory())
|
H1Service::with_config(cfg, service.into_factory())
|
||||||
|
|
@ -226,6 +243,7 @@ where
|
||||||
.client_disconnect_timeout(self.client_disconnect_timeout)
|
.client_disconnect_timeout(self.client_disconnect_timeout)
|
||||||
.secure(self.secure)
|
.secure(self.secure)
|
||||||
.local_addr(self.local_addr)
|
.local_addr(self.local_addr)
|
||||||
|
.h1_allow_half_closed(self.h1_allow_half_closed)
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
crate::h2::H2Service::with_config(cfg, service.into_factory())
|
crate::h2::H2Service::with_config(cfg, service.into_factory())
|
||||||
|
|
@ -248,6 +266,7 @@ where
|
||||||
.client_disconnect_timeout(self.client_disconnect_timeout)
|
.client_disconnect_timeout(self.client_disconnect_timeout)
|
||||||
.secure(self.secure)
|
.secure(self.secure)
|
||||||
.local_addr(self.local_addr)
|
.local_addr(self.local_addr)
|
||||||
|
.h1_allow_half_closed(self.h1_allow_half_closed)
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
HttpService::with_config(cfg, service.into_factory())
|
HttpService::with_config(cfg, service.into_factory())
|
||||||
|
|
|
||||||
|
|
@ -23,6 +23,7 @@ impl ServiceConfigBuilder {
|
||||||
/// - 0 seconds for the client shutdown timeout
|
/// - 0 seconds for the client shutdown timeout
|
||||||
/// - secure value of `false`
|
/// - secure value of `false`
|
||||||
/// - [`None`] for the local address setting
|
/// - [`None`] for the local address setting
|
||||||
|
/// - Allow for half closed HTTP/1 connections
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
Self::default()
|
Self::default()
|
||||||
}
|
}
|
||||||
|
|
@ -58,6 +59,16 @@ impl ServiceConfigBuilder {
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Sets whether HTTP/1 connections should support half-closures.
|
||||||
|
///
|
||||||
|
/// Clients can choose to shutdown their writer-side of the connection after completing their
|
||||||
|
/// request and while waiting for the server response. Setting this to `false` will cause the
|
||||||
|
/// server to abort the connection handling as soon as it detects an EOF from the client
|
||||||
|
pub fn h1_allow_half_closed(mut self, allow: bool) -> Self {
|
||||||
|
self.inner.h1_allow_half_closed = allow;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
/// Builds a [`ServiceConfig`] from this [`ServiceConfigBuilder`] instance
|
/// Builds a [`ServiceConfig`] from this [`ServiceConfigBuilder`] instance
|
||||||
pub fn build(self) -> ServiceConfig {
|
pub fn build(self) -> ServiceConfig {
|
||||||
ServiceConfig(Rc::new(self.inner))
|
ServiceConfig(Rc::new(self.inner))
|
||||||
|
|
@ -84,6 +95,7 @@ struct Inner {
|
||||||
secure: bool,
|
secure: bool,
|
||||||
local_addr: Option<SocketAddr>,
|
local_addr: Option<SocketAddr>,
|
||||||
date_service: DateService,
|
date_service: DateService,
|
||||||
|
h1_allow_half_closed: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for Inner {
|
impl Default for Inner {
|
||||||
|
|
@ -95,6 +107,7 @@ impl Default for Inner {
|
||||||
secure: false,
|
secure: false,
|
||||||
local_addr: None,
|
local_addr: None,
|
||||||
date_service: DateService::new(),
|
date_service: DateService::new(),
|
||||||
|
h1_allow_half_closed: true,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -121,6 +134,7 @@ impl ServiceConfig {
|
||||||
secure,
|
secure,
|
||||||
local_addr,
|
local_addr,
|
||||||
date_service: DateService::new(),
|
date_service: DateService::new(),
|
||||||
|
h1_allow_half_closed: true,
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -171,6 +185,15 @@ impl ServiceConfig {
|
||||||
(timeout != Duration::ZERO).then(|| self.now() + timeout)
|
(timeout != Duration::ZERO).then(|| self.now() + timeout)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Whether HTTP/1 connections should support half-closures.
|
||||||
|
///
|
||||||
|
/// Clients can choose to shutdown their writer-side of the connection after completing their
|
||||||
|
/// request and while waiting for the server response. If this configuration is `false`, the
|
||||||
|
/// server will abort the connection handling as soon as it detects an EOF from the client
|
||||||
|
pub fn h1_allow_half_closed(&self) -> bool {
|
||||||
|
self.0.h1_allow_half_closed
|
||||||
|
}
|
||||||
|
|
||||||
pub(crate) fn now(&self) -> Instant {
|
pub(crate) fn now(&self) -> Instant {
|
||||||
self.0.date_service.now()
|
self.0.date_service.now()
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1181,8 +1181,16 @@ where
|
||||||
let inner_p = inner.as_mut().project();
|
let inner_p = inner.as_mut().project();
|
||||||
let state_is_none = inner_p.state.is_none();
|
let state_is_none = inner_p.state.is_none();
|
||||||
|
|
||||||
// read half is closed; we do not process any responses
|
// If the read-half is closed, we start the shutdown procedure if either is
|
||||||
if inner_p.flags.contains(Flags::READ_DISCONNECT) {
|
// true:
|
||||||
|
//
|
||||||
|
// - state is [`State::None`], which means that we're done with request
|
||||||
|
// processing, so if the client closed its writer-side it means that it won't
|
||||||
|
// send more requests.
|
||||||
|
// - The user requested to not allow half-closures
|
||||||
|
if inner_p.flags.contains(Flags::READ_DISCONNECT)
|
||||||
|
&& (!inner_p.config.h1_allow_half_closed() || state_is_none)
|
||||||
|
{
|
||||||
trace!("read half closed; start shutdown");
|
trace!("read half closed; start shutdown");
|
||||||
inner_p.flags.insert(Flags::SHUTDOWN);
|
inner_p.flags.insert(Flags::SHUTDOWN);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -31,6 +31,7 @@ struct Config {
|
||||||
keep_alive: KeepAlive,
|
keep_alive: KeepAlive,
|
||||||
client_request_timeout: Duration,
|
client_request_timeout: Duration,
|
||||||
client_disconnect_timeout: Duration,
|
client_disconnect_timeout: Duration,
|
||||||
|
h1_allow_half_closed: bool,
|
||||||
#[allow(dead_code)] // only dead when no TLS features are enabled
|
#[allow(dead_code)] // only dead when no TLS features are enabled
|
||||||
tls_handshake_timeout: Option<Duration>,
|
tls_handshake_timeout: Option<Duration>,
|
||||||
}
|
}
|
||||||
|
|
@ -116,6 +117,7 @@ where
|
||||||
keep_alive: KeepAlive::default(),
|
keep_alive: KeepAlive::default(),
|
||||||
client_request_timeout: Duration::from_secs(5),
|
client_request_timeout: Duration::from_secs(5),
|
||||||
client_disconnect_timeout: Duration::from_secs(1),
|
client_disconnect_timeout: Duration::from_secs(1),
|
||||||
|
h1_allow_half_closed: true,
|
||||||
tls_handshake_timeout: None,
|
tls_handshake_timeout: None,
|
||||||
})),
|
})),
|
||||||
backlog: 1024,
|
backlog: 1024,
|
||||||
|
|
@ -257,6 +259,18 @@ where
|
||||||
self.client_disconnect_timeout(Duration::from_millis(dur))
|
self.client_disconnect_timeout(Duration::from_millis(dur))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Sets whether HTTP/1 connections should support half-closures.
|
||||||
|
///
|
||||||
|
/// Clients can choose to shutdown their writer-side of the connection after completing their
|
||||||
|
/// request and while waiting for the server response. Setting this to `false` will cause the
|
||||||
|
/// server to abort the connection handling as soon as it detects an EOF from the client.
|
||||||
|
///
|
||||||
|
/// The default behavior is to allow, i.e. `true`
|
||||||
|
pub fn h1_allow_half_closed(self, allow: bool) -> Self {
|
||||||
|
self.config.lock().unwrap().h1_allow_half_closed = allow;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
/// Sets function that will be called once before each connection is handled.
|
/// Sets function that will be called once before each connection is handled.
|
||||||
///
|
///
|
||||||
/// It will receive a `&std::any::Any`, which contains underlying connection type and an
|
/// It will receive a `&std::any::Any`, which contains underlying connection type and an
|
||||||
|
|
@ -558,6 +572,7 @@ where
|
||||||
.keep_alive(cfg.keep_alive)
|
.keep_alive(cfg.keep_alive)
|
||||||
.client_request_timeout(cfg.client_request_timeout)
|
.client_request_timeout(cfg.client_request_timeout)
|
||||||
.client_disconnect_timeout(cfg.client_disconnect_timeout)
|
.client_disconnect_timeout(cfg.client_disconnect_timeout)
|
||||||
|
.h1_allow_half_closed(cfg.h1_allow_half_closed)
|
||||||
.local_addr(addr);
|
.local_addr(addr);
|
||||||
|
|
||||||
if let Some(handler) = on_connect_fn.clone() {
|
if let Some(handler) = on_connect_fn.clone() {
|
||||||
|
|
@ -602,6 +617,7 @@ where
|
||||||
.keep_alive(cfg.keep_alive)
|
.keep_alive(cfg.keep_alive)
|
||||||
.client_request_timeout(cfg.client_request_timeout)
|
.client_request_timeout(cfg.client_request_timeout)
|
||||||
.client_disconnect_timeout(cfg.client_disconnect_timeout)
|
.client_disconnect_timeout(cfg.client_disconnect_timeout)
|
||||||
|
.h1_allow_half_closed(cfg.h1_allow_half_closed)
|
||||||
.local_addr(addr);
|
.local_addr(addr);
|
||||||
|
|
||||||
if let Some(handler) = on_connect_fn.clone() {
|
if let Some(handler) = on_connect_fn.clone() {
|
||||||
|
|
@ -677,6 +693,7 @@ where
|
||||||
let svc = HttpService::build()
|
let svc = HttpService::build()
|
||||||
.keep_alive(c.keep_alive)
|
.keep_alive(c.keep_alive)
|
||||||
.client_request_timeout(c.client_request_timeout)
|
.client_request_timeout(c.client_request_timeout)
|
||||||
|
.h1_allow_half_closed(c.h1_allow_half_closed)
|
||||||
.client_disconnect_timeout(c.client_disconnect_timeout);
|
.client_disconnect_timeout(c.client_disconnect_timeout);
|
||||||
|
|
||||||
let svc = if let Some(handler) = on_connect_fn.clone() {
|
let svc = if let Some(handler) = on_connect_fn.clone() {
|
||||||
|
|
@ -728,6 +745,7 @@ where
|
||||||
let svc = HttpService::build()
|
let svc = HttpService::build()
|
||||||
.keep_alive(c.keep_alive)
|
.keep_alive(c.keep_alive)
|
||||||
.client_request_timeout(c.client_request_timeout)
|
.client_request_timeout(c.client_request_timeout)
|
||||||
|
.h1_allow_half_closed(c.h1_allow_half_closed)
|
||||||
.client_disconnect_timeout(c.client_disconnect_timeout);
|
.client_disconnect_timeout(c.client_disconnect_timeout);
|
||||||
|
|
||||||
let svc = if let Some(handler) = on_connect_fn.clone() {
|
let svc = if let Some(handler) = on_connect_fn.clone() {
|
||||||
|
|
@ -794,6 +812,7 @@ where
|
||||||
let svc = HttpService::build()
|
let svc = HttpService::build()
|
||||||
.keep_alive(c.keep_alive)
|
.keep_alive(c.keep_alive)
|
||||||
.client_request_timeout(c.client_request_timeout)
|
.client_request_timeout(c.client_request_timeout)
|
||||||
|
.h1_allow_half_closed(c.h1_allow_half_closed)
|
||||||
.client_disconnect_timeout(c.client_disconnect_timeout);
|
.client_disconnect_timeout(c.client_disconnect_timeout);
|
||||||
|
|
||||||
let svc = if let Some(handler) = on_connect_fn.clone() {
|
let svc = if let Some(handler) = on_connect_fn.clone() {
|
||||||
|
|
@ -860,6 +879,7 @@ where
|
||||||
let svc = HttpService::build()
|
let svc = HttpService::build()
|
||||||
.keep_alive(c.keep_alive)
|
.keep_alive(c.keep_alive)
|
||||||
.client_request_timeout(c.client_request_timeout)
|
.client_request_timeout(c.client_request_timeout)
|
||||||
|
.h1_allow_half_closed(c.h1_allow_half_closed)
|
||||||
.client_disconnect_timeout(c.client_disconnect_timeout);
|
.client_disconnect_timeout(c.client_disconnect_timeout);
|
||||||
|
|
||||||
let svc = if let Some(handler) = on_connect_fn.clone() {
|
let svc = if let Some(handler) = on_connect_fn.clone() {
|
||||||
|
|
@ -927,6 +947,7 @@ where
|
||||||
.keep_alive(c.keep_alive)
|
.keep_alive(c.keep_alive)
|
||||||
.client_request_timeout(c.client_request_timeout)
|
.client_request_timeout(c.client_request_timeout)
|
||||||
.client_disconnect_timeout(c.client_disconnect_timeout)
|
.client_disconnect_timeout(c.client_disconnect_timeout)
|
||||||
|
.h1_allow_half_closed(c.h1_allow_half_closed)
|
||||||
.local_addr(addr);
|
.local_addr(addr);
|
||||||
|
|
||||||
let svc = if let Some(handler) = on_connect_fn.clone() {
|
let svc = if let Some(handler) = on_connect_fn.clone() {
|
||||||
|
|
@ -995,6 +1016,7 @@ where
|
||||||
.keep_alive(c.keep_alive)
|
.keep_alive(c.keep_alive)
|
||||||
.client_request_timeout(c.client_request_timeout)
|
.client_request_timeout(c.client_request_timeout)
|
||||||
.client_disconnect_timeout(c.client_disconnect_timeout)
|
.client_disconnect_timeout(c.client_disconnect_timeout)
|
||||||
|
.h1_allow_half_closed(c.h1_allow_half_closed)
|
||||||
.finish(map_config(fac, move |_| config.clone())),
|
.finish(map_config(fac, move |_| config.clone())),
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
|
|
@ -1036,6 +1058,7 @@ where
|
||||||
let mut svc = HttpService::build()
|
let mut svc = HttpService::build()
|
||||||
.keep_alive(c.keep_alive)
|
.keep_alive(c.keep_alive)
|
||||||
.client_request_timeout(c.client_request_timeout)
|
.client_request_timeout(c.client_request_timeout)
|
||||||
|
.h1_allow_half_closed(c.h1_allow_half_closed)
|
||||||
.client_disconnect_timeout(c.client_disconnect_timeout);
|
.client_disconnect_timeout(c.client_disconnect_timeout);
|
||||||
|
|
||||||
if let Some(handler) = on_connect_fn.clone() {
|
if let Some(handler) = on_connect_fn.clone() {
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue