mirror of
https://github.com/denoland/deno.git
synced 2025-09-25 11:49:11 +00:00
feat: add tcpBacklog
arg to Deno.listen{Tls}
and Deno.serve
(#30541)
This commit adds `tcpBacklog` argument to `Deno.listen`, `Deno.listenTls` and `Deno.serve` APIs. The argument specifies maximum number of pending connections in the listen queue, and by default is set to 511. Users that expect huge bursts of traffic can customize this option to a higher value. Ref https://github.com/denoland/deno/pull/30471 Closes https://github.com/denoland/deno/issues/30388
This commit is contained in:
parent
9dbcd025d6
commit
da1bf978f0
8 changed files with 49 additions and 15 deletions
12
cli/tsc/dts/lib.deno.ns.d.ts
vendored
12
cli/tsc/dts/lib.deno.ns.d.ts
vendored
|
@ -5157,6 +5157,18 @@ declare namespace Deno {
|
||||||
|
|
||||||
/** Sets `SO_REUSEPORT` on POSIX systems. */
|
/** Sets `SO_REUSEPORT` on POSIX systems. */
|
||||||
reusePort?: boolean;
|
reusePort?: boolean;
|
||||||
|
|
||||||
|
/** Maximum number of pending connections in the listen queue.
|
||||||
|
*
|
||||||
|
* This parameter controls how many incoming connections can be queued by the
|
||||||
|
* operating system while waiting for the application to accept them. If more
|
||||||
|
* connections arrive when the queue is full, they will be refused.
|
||||||
|
*
|
||||||
|
* The kernel may adjust this value (e.g., rounding up to the next power of 2
|
||||||
|
* plus 1). Different operating systems have different maximum limits.
|
||||||
|
*
|
||||||
|
* @default {511} */
|
||||||
|
tcpBacklog?: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
12
cli/tsc/dts/lib.deno_net.d.ts
vendored
12
cli/tsc/dts/lib.deno_net.d.ts
vendored
|
@ -191,6 +191,18 @@ declare namespace Deno {
|
||||||
*
|
*
|
||||||
* @default {"0.0.0.0"} */
|
* @default {"0.0.0.0"} */
|
||||||
hostname?: string;
|
hostname?: string;
|
||||||
|
|
||||||
|
/** Maximum number of pending connections in the listen queue.
|
||||||
|
*
|
||||||
|
* This parameter controls how many incoming connections can be queued by the
|
||||||
|
* operating system while waiting for the application to accept them. If more
|
||||||
|
* connections arrive when the queue is full, they will be refused.
|
||||||
|
*
|
||||||
|
* The kernel may adjust this value (e.g., rounding up to the next power of 2
|
||||||
|
* plus 1). Different operating systems have different maximum limits.
|
||||||
|
*
|
||||||
|
* @default {511} */
|
||||||
|
tcpBacklog?: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @category Network */
|
/** @category Network */
|
||||||
|
|
|
@ -936,6 +936,7 @@ function serveInner(options, handler) {
|
||||||
port: options.port ?? 8000,
|
port: options.port ?? 8000,
|
||||||
reusePort: options.reusePort ?? false,
|
reusePort: options.reusePort ?? false,
|
||||||
loadBalanced: options[kLoadBalanced] ?? false,
|
loadBalanced: options[kLoadBalanced] ?? false,
|
||||||
|
backlog: options.backlog,
|
||||||
};
|
};
|
||||||
|
|
||||||
if (options.certFile || options.keyFile) {
|
if (options.certFile || options.keyFile) {
|
||||||
|
|
|
@ -598,6 +598,7 @@ function listen(args) {
|
||||||
},
|
},
|
||||||
args.reusePort,
|
args.reusePort,
|
||||||
args.loadBalanced ?? false,
|
args.loadBalanced ?? false,
|
||||||
|
args.tcpBacklog ?? 511,
|
||||||
);
|
);
|
||||||
addr.transport = "tcp";
|
addr.transport = "tcp";
|
||||||
return new Listener(rid, addr, "tcp");
|
return new Listener(rid, addr, "tcp");
|
||||||
|
|
|
@ -162,6 +162,7 @@ function listenTls({
|
||||||
transport = "tcp",
|
transport = "tcp",
|
||||||
alpnProtocols = undefined,
|
alpnProtocols = undefined,
|
||||||
reusePort = false,
|
reusePort = false,
|
||||||
|
tcpBacklog = 511,
|
||||||
}) {
|
}) {
|
||||||
if (transport !== "tcp") {
|
if (transport !== "tcp") {
|
||||||
throw new TypeError(`Unsupported transport: '${transport}'`);
|
throw new TypeError(`Unsupported transport: '${transport}'`);
|
||||||
|
@ -176,7 +177,7 @@ function listenTls({
|
||||||
const keyPair = loadTlsKeyPair("Deno.listenTls", arguments[0]);
|
const keyPair = loadTlsKeyPair("Deno.listenTls", arguments[0]);
|
||||||
const { 0: rid, 1: localAddr } = op_net_listen_tls(
|
const { 0: rid, 1: localAddr } = op_net_listen_tls(
|
||||||
{ hostname, port },
|
{ hostname, port },
|
||||||
{ alpnProtocols, reusePort },
|
{ alpnProtocols, reusePort, tcpBacklog },
|
||||||
keyPair,
|
keyPair,
|
||||||
);
|
);
|
||||||
return new TlsListener(rid, localAddr);
|
return new TlsListener(rid, localAddr);
|
||||||
|
|
|
@ -574,6 +574,7 @@ pub fn op_net_listen_tcp<NP>(
|
||||||
#[serde] addr: IpAddr,
|
#[serde] addr: IpAddr,
|
||||||
reuse_port: bool,
|
reuse_port: bool,
|
||||||
load_balanced: bool,
|
load_balanced: bool,
|
||||||
|
tcp_backlog: i32,
|
||||||
) -> Result<(ResourceId, IpAddr), NetError>
|
) -> Result<(ResourceId, IpAddr), NetError>
|
||||||
where
|
where
|
||||||
NP: NetPermissions + 'static,
|
NP: NetPermissions + 'static,
|
||||||
|
@ -589,9 +590,9 @@ where
|
||||||
.ok_or_else(|| NetError::NoResolvedAddress)?;
|
.ok_or_else(|| NetError::NoResolvedAddress)?;
|
||||||
|
|
||||||
let listener = if load_balanced {
|
let listener = if load_balanced {
|
||||||
TcpListener::bind_load_balanced(addr)
|
TcpListener::bind_load_balanced(addr, tcp_backlog)
|
||||||
} else {
|
} else {
|
||||||
TcpListener::bind_direct(addr, reuse_port)
|
TcpListener::bind_direct(addr, reuse_port, tcp_backlog)
|
||||||
}?;
|
}?;
|
||||||
let local_addr = listener.local_addr()?;
|
let local_addr = listener.local_addr()?;
|
||||||
let listener_resource = NetworkListenerResource::new(listener);
|
let listener_resource = NetworkListenerResource::new(listener);
|
||||||
|
@ -1483,7 +1484,7 @@ mod tests {
|
||||||
let sockets = Arc::new(Mutex::new(vec![]));
|
let sockets = Arc::new(Mutex::new(vec![]));
|
||||||
let clone_addr = addr.clone();
|
let clone_addr = addr.clone();
|
||||||
let addr = addr.to_socket_addrs().unwrap().next().unwrap();
|
let addr = addr.to_socket_addrs().unwrap().next().unwrap();
|
||||||
let listener = TcpListener::bind_direct(addr, false).unwrap();
|
let listener = TcpListener::bind_direct(addr, false, 511).unwrap();
|
||||||
let accept_fut = listener.accept().boxed_local();
|
let accept_fut = listener.accept().boxed_local();
|
||||||
let store_fut = async move {
|
let store_fut = async move {
|
||||||
let socket = accept_fut.await.unwrap();
|
let socket = accept_fut.await.unwrap();
|
||||||
|
|
|
@ -514,6 +514,7 @@ pub struct ListenTlsArgs {
|
||||||
reuse_port: bool,
|
reuse_port: bool,
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
load_balanced: bool,
|
load_balanced: bool,
|
||||||
|
tcp_backlog: i32,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[op2(stack_trace)]
|
#[op2(stack_trace)]
|
||||||
|
@ -543,9 +544,9 @@ where
|
||||||
.ok_or(NetError::NoResolvedAddress)?;
|
.ok_or(NetError::NoResolvedAddress)?;
|
||||||
|
|
||||||
let tcp_listener = if args.load_balanced {
|
let tcp_listener = if args.load_balanced {
|
||||||
TcpListener::bind_load_balanced(bind_addr)
|
TcpListener::bind_load_balanced(bind_addr, args.tcp_backlog)
|
||||||
} else {
|
} else {
|
||||||
TcpListener::bind_direct(bind_addr, args.reuse_port)
|
TcpListener::bind_direct(bind_addr, args.reuse_port, args.tcp_backlog)
|
||||||
}?;
|
}?;
|
||||||
let local_addr = tcp_listener.local_addr()?;
|
let local_addr = tcp_listener.local_addr()?;
|
||||||
let alpn = args
|
let alpn = args
|
||||||
|
|
|
@ -31,8 +31,8 @@ pub struct TcpConnection {
|
||||||
|
|
||||||
impl TcpConnection {
|
impl TcpConnection {
|
||||||
/// Boot a load-balanced TCP connection
|
/// Boot a load-balanced TCP connection
|
||||||
pub fn start(key: SocketAddr) -> std::io::Result<Self> {
|
pub fn start(key: SocketAddr, backlog: i32) -> std::io::Result<Self> {
|
||||||
let listener = bind_socket_and_listen(key, false)?;
|
let listener = bind_socket_and_listen(key, false, backlog)?;
|
||||||
let sock = listener.into();
|
let sock = listener.into();
|
||||||
|
|
||||||
Ok(Self { sock, key })
|
Ok(Self { sock, key })
|
||||||
|
@ -78,11 +78,12 @@ impl TcpListener {
|
||||||
pub fn bind(
|
pub fn bind(
|
||||||
socket_addr: SocketAddr,
|
socket_addr: SocketAddr,
|
||||||
reuse_port: bool,
|
reuse_port: bool,
|
||||||
|
backlog: i32,
|
||||||
) -> std::io::Result<Self> {
|
) -> std::io::Result<Self> {
|
||||||
if REUSE_PORT_LOAD_BALANCES && reuse_port {
|
if REUSE_PORT_LOAD_BALANCES && reuse_port {
|
||||||
Self::bind_load_balanced(socket_addr)
|
Self::bind_load_balanced(socket_addr, backlog)
|
||||||
} else {
|
} else {
|
||||||
Self::bind_direct(socket_addr, reuse_port)
|
Self::bind_direct(socket_addr, reuse_port, backlog)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -91,9 +92,10 @@ impl TcpListener {
|
||||||
pub fn bind_direct(
|
pub fn bind_direct(
|
||||||
socket_addr: SocketAddr,
|
socket_addr: SocketAddr,
|
||||||
reuse_port: bool,
|
reuse_port: bool,
|
||||||
|
backlog: i32,
|
||||||
) -> std::io::Result<Self> {
|
) -> std::io::Result<Self> {
|
||||||
// We ignore `reuse_port` on platforms other than Linux to match the existing behaviour.
|
// We ignore `reuse_port` on platforms other than Linux to match the existing behaviour.
|
||||||
let listener = bind_socket_and_listen(socket_addr, reuse_port)?;
|
let listener = bind_socket_and_listen(socket_addr, reuse_port, backlog)?;
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
listener: Some(tokio::net::TcpListener::from_std(listener)?),
|
listener: Some(tokio::net::TcpListener::from_std(listener)?),
|
||||||
conn: None,
|
conn: None,
|
||||||
|
@ -101,7 +103,10 @@ impl TcpListener {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Bind to the port in a load-balanced manner.
|
/// Bind to the port in a load-balanced manner.
|
||||||
pub fn bind_load_balanced(socket_addr: SocketAddr) -> std::io::Result<Self> {
|
pub fn bind_load_balanced(
|
||||||
|
socket_addr: SocketAddr,
|
||||||
|
backlog: i32,
|
||||||
|
) -> std::io::Result<Self> {
|
||||||
let tcp = &mut CONNS.get_or_init(Default::default).lock().unwrap().tcp;
|
let tcp = &mut CONNS.get_or_init(Default::default).lock().unwrap().tcp;
|
||||||
if let Some(conn) = tcp.get(&socket_addr) {
|
if let Some(conn) = tcp.get(&socket_addr) {
|
||||||
let listener = Some(conn.listener()?);
|
let listener = Some(conn.listener()?);
|
||||||
|
@ -110,7 +115,7 @@ impl TcpListener {
|
||||||
conn: Some(conn.clone()),
|
conn: Some(conn.clone()),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
let conn = Arc::new(TcpConnection::start(socket_addr)?);
|
let conn = Arc::new(TcpConnection::start(socket_addr, backlog)?);
|
||||||
let listener = Some(conn.listener()?);
|
let listener = Some(conn.listener()?);
|
||||||
tcp.insert(socket_addr, conn.clone());
|
tcp.insert(socket_addr, conn.clone());
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
|
@ -151,6 +156,7 @@ impl Drop for TcpListener {
|
||||||
fn bind_socket_and_listen(
|
fn bind_socket_and_listen(
|
||||||
socket_addr: SocketAddr,
|
socket_addr: SocketAddr,
|
||||||
reuse_port: bool,
|
reuse_port: bool,
|
||||||
|
backlog: i32,
|
||||||
) -> Result<std::net::TcpListener, std::io::Error> {
|
) -> Result<std::net::TcpListener, std::io::Error> {
|
||||||
let socket = if socket_addr.is_ipv4() {
|
let socket = if socket_addr.is_ipv4() {
|
||||||
socket2::Socket::new(Domain::IPV4, Type::STREAM, Some(Protocol::TCP))?
|
socket2::Socket::new(Domain::IPV4, Type::STREAM, Some(Protocol::TCP))?
|
||||||
|
@ -170,8 +176,7 @@ fn bind_socket_and_listen(
|
||||||
socket.set_reuse_address(true)?;
|
socket.set_reuse_address(true)?;
|
||||||
socket.set_nonblocking(true)?;
|
socket.set_nonblocking(true)?;
|
||||||
socket.bind(&socket_addr.into())?;
|
socket.bind(&socket_addr.into())?;
|
||||||
// Kernel will round it up to the next power of 2 + 1.
|
socket.listen(backlog)?;
|
||||||
socket.listen(511)?;
|
|
||||||
let listener = socket.into();
|
let listener = socket.into();
|
||||||
Ok(listener)
|
Ok(listener)
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue