Skip to content

Commit 755b231

Browse files
Martichourobjtede
andauthored
add MPTCP socket protocol (optional) (#466)
Co-authored-by: Rob Ede <robjtede@icloud.com>
1 parent 8d5d1db commit 755b231

File tree

4 files changed

+68
-5
lines changed

4 files changed

+68
-5
lines changed

actix-server/CHANGES.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
## Unreleased - 2023-xx-xx
44

5+
- Add support for MultiPath TCP (MPTCP) with `MpTcp` enum and `ServerBuilder::mptcp()` method.
56
- Minimum supported Rust version (MSRV) is now 1.65.
67

78
## 2.2.0 - 2022-12-21

actix-server/src/builder.rs

Lines changed: 39 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,13 +12,30 @@ use crate::{
1212
Server,
1313
};
1414

15+
/// Multipath TCP (MPTCP) preference.
16+
///
17+
/// Also see [`ServerBuilder::mptcp()`].
18+
#[derive(Debug, Clone)]
19+
pub enum MpTcp {
20+
/// MPTCP will not be used when binding sockets.
21+
Disabled,
22+
23+
/// MPTCP will be attempted when binding sockets. If errors occur, regular TCP will be
24+
/// attempted, too.
25+
TcpFallback,
26+
27+
/// MPTCP will be used when binding sockets (with no fallback).
28+
NoFallback,
29+
}
30+
1531
/// [Server] builder.
1632
pub struct ServerBuilder {
1733
pub(crate) threads: usize,
1834
pub(crate) token: usize,
1935
pub(crate) backlog: u32,
2036
pub(crate) factories: Vec<Box<dyn InternalServiceFactory>>,
2137
pub(crate) sockets: Vec<(usize, String, MioListener)>,
38+
pub(crate) mptcp: MpTcp,
2239
pub(crate) exit: bool,
2340
pub(crate) listen_os_signals: bool,
2441
pub(crate) cmd_tx: UnboundedSender<ServerCommand>,
@@ -43,6 +60,7 @@ impl ServerBuilder {
4360
factories: Vec::new(),
4461
sockets: Vec::new(),
4562
backlog: 2048,
63+
mptcp: MpTcp::Disabled,
4664
exit: false,
4765
listen_os_signals: true,
4866
cmd_tx,
@@ -96,6 +114,24 @@ impl ServerBuilder {
96114
self
97115
}
98116

117+
/// Sets MultiPath TCP (MPTCP) preference on bound sockets.
118+
///
119+
/// Multipath TCP (MPTCP) builds on top of TCP to improve connection redundancy and performance
120+
/// by sharing a network data stream across multiple underlying TCP sessions. See [mptcp.dev]
121+
/// for more info about MPTCP itself.
122+
///
123+
/// MPTCP is available on Linux kernel version 5.6 and higher. In addition, you'll also need to
124+
/// ensure the kernel option is enabled using `sysctl net.mptcp.enabled=1`.
125+
///
126+
/// This method will have no effect if called after a `bind()`.
127+
///
128+
/// [mptcp.dev]: https://www.mptcp.dev
129+
#[cfg(target_os = "linux")]
130+
pub fn mptcp(mut self, mptcp_enabled: MpTcp) -> Self {
131+
self.mptcp = mptcp_enabled;
132+
self
133+
}
134+
99135
/// Sets the maximum per-worker number of concurrent connections.
100136
///
101137
/// All socket listeners will stop accepting connections when this limit is reached for
@@ -144,7 +180,7 @@ impl ServerBuilder {
144180
U: ToSocketAddrs,
145181
N: AsRef<str>,
146182
{
147-
let sockets = bind_addr(addr, self.backlog)?;
183+
let sockets = bind_addr(addr, self.backlog, &self.mptcp)?;
148184

149185
trace!("binding server to: {:?}", &sockets);
150186

@@ -260,13 +296,14 @@ impl ServerBuilder {
260296
pub(super) fn bind_addr<S: ToSocketAddrs>(
261297
addr: S,
262298
backlog: u32,
299+
mptcp: &MpTcp,
263300
) -> io::Result<Vec<MioTcpListener>> {
264301
let mut opt_err = None;
265302
let mut success = false;
266303
let mut sockets = Vec::new();
267304

268305
for addr in addr.to_socket_addrs()? {
269-
match create_mio_tcp_listener(addr, backlog) {
306+
match create_mio_tcp_listener(addr, backlog, mptcp) {
270307
Ok(lst) => {
271308
success = true;
272309
sockets.push(lst);

actix-server/src/lib.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,10 @@ mod worker;
2121
#[doc(hidden)]
2222
pub use self::socket::FromStream;
2323
pub use self::{
24-
builder::ServerBuilder, handle::ServerHandle, server::Server, service::ServerServiceFactory,
24+
builder::{MpTcp, ServerBuilder},
25+
handle::ServerHandle,
26+
server::Server,
27+
service::ServerServiceFactory,
2528
test_server::TestServer,
2629
};
2730

actix-server/src/socket.rs

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@ pub(crate) use {
1111
mio::net::UnixListener as MioUnixListener, std::os::unix::net::UnixListener as StdUnixListener,
1212
};
1313

14+
use crate::builder::MpTcp;
15+
1416
pub(crate) enum MioListener {
1517
Tcp(MioTcpListener),
1618
#[cfg(unix)]
@@ -223,10 +225,30 @@ mod unix_impl {
223225
pub(crate) fn create_mio_tcp_listener(
224226
addr: StdSocketAddr,
225227
backlog: u32,
228+
mptcp: &MpTcp,
226229
) -> io::Result<MioTcpListener> {
227230
use socket2::{Domain, Protocol, Socket, Type};
228231

229-
let socket = Socket::new(Domain::for_address(addr), Type::STREAM, Some(Protocol::TCP))?;
232+
#[cfg(not(target_os = "linux"))]
233+
let protocol = Protocol::TCP;
234+
#[cfg(target_os = "linux")]
235+
let protocol = if matches!(mptcp, MpTcp::Disabled) {
236+
Protocol::TCP
237+
} else {
238+
Protocol::MPTCP
239+
};
240+
241+
let socket = match Socket::new(Domain::for_address(addr), Type::STREAM, Some(protocol)) {
242+
Ok(sock) => sock,
243+
244+
Err(err) if matches!(mptcp, MpTcp::TcpFallback) => {
245+
tracing::warn!("binding socket as MPTCP failed: {err}");
246+
tracing::warn!("falling back to TCP");
247+
Socket::new(Domain::for_address(addr), Type::STREAM, Some(Protocol::TCP))?
248+
}
249+
250+
Err(err) => return Err(err),
251+
};
230252

231253
socket.set_reuse_address(true)?;
232254
socket.set_nonblocking(true)?;
@@ -247,7 +269,7 @@ mod tests {
247269
assert_eq!(format!("{}", addr), "127.0.0.1:8080");
248270

249271
let addr: StdSocketAddr = "127.0.0.1:0".parse().unwrap();
250-
let lst = create_mio_tcp_listener(addr, 128).unwrap();
272+
let lst = create_mio_tcp_listener(addr, 128, &MpTcp::Disabled).unwrap();
251273
let lst = MioListener::Tcp(lst);
252274
assert!(format!("{:?}", lst).contains("TcpListener"));
253275
assert!(format!("{}", lst).contains("127.0.0.1"));

0 commit comments

Comments
 (0)