mirror of
https://github.com/go-gost/x.git
synced 2026-10-20 00:17:32 +00:00
Disable Nagle on the underlying TCP socket for relay+ws, relay+wss, and relay+tls listeners/dialers. Small latency-sensitive datagrams (e.g. WireGuard UDP over a relay tunnel) were being coalesced by Nagle until a peer ACK returned, adding up to one RTT per datagram and inflating round-trip latency (go-gost/gost#858). wstunnel sets TCP_NODELAY on its tunnel socket by default and keeps latency flat; gost did not. Add ApplyNoDelay helper and WrapNoDelayListener (mirrors ApplyKeepalive), applied on the raw *net.TCPConn before the proxyproto/tls wrapper chain.
65 lines
1.8 KiB
Go
65 lines
1.8 KiB
Go
package net
|
|
|
|
import "net"
|
|
|
|
// WrapKeepaliveListener wraps ln so that TCP keepalive config is applied to
|
|
// every accepted *net.TCPConn before it enters the middleware wrapper chain.
|
|
func WrapKeepaliveListener(ln net.Listener, cfg net.KeepAliveConfig) net.Listener {
|
|
return &keepaliveListener{Listener: ln, cfg: cfg}
|
|
}
|
|
|
|
// ApplyKeepalive applies cfg to conn if it is a *net.TCPConn.
|
|
func ApplyKeepalive(conn net.Conn, cfg net.KeepAliveConfig) {
|
|
if tc, ok := conn.(*net.TCPConn); ok {
|
|
tc.SetKeepAliveConfig(cfg)
|
|
}
|
|
}
|
|
|
|
// ApplyNoDelay disables Nagle's algorithm on conn if it is a *net.TCPConn.
|
|
//
|
|
// gost tunnels carry small, latency-sensitive datagrams (e.g. WireGuard UDP
|
|
// over relay+ws/wss). Without this, Nagle coalesces each frame until a peer
|
|
// ACK returns, adding up to one RTT per datagram — the root cause of the
|
|
// latency regression reported in go-gost/gost#858. Match wstunnel, which sets
|
|
// TCP_NODELAY on its tunnel socket by default.
|
|
func ApplyNoDelay(conn net.Conn) {
|
|
if tc, ok := conn.(*net.TCPConn); ok {
|
|
tc.SetNoDelay(true)
|
|
}
|
|
}
|
|
|
|
// WrapNoDelayListener wraps ln so that TCP_NODELAY is applied to every accepted
|
|
// *net.TCPConn before it enters the middleware wrapper chain.
|
|
func WrapNoDelayListener(ln net.Listener) net.Listener {
|
|
return &noDelayListener{Listener: ln}
|
|
}
|
|
|
|
type noDelayListener struct {
|
|
net.Listener
|
|
}
|
|
|
|
func (l *noDelayListener) Accept() (net.Conn, error) {
|
|
conn, err := l.Listener.Accept()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
ApplyNoDelay(conn)
|
|
return conn, nil
|
|
}
|
|
|
|
type keepaliveListener struct {
|
|
net.Listener
|
|
cfg net.KeepAliveConfig
|
|
}
|
|
|
|
func (l *keepaliveListener) Accept() (net.Conn, error) {
|
|
conn, err := l.Listener.Accept()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if tc, ok := conn.(*net.TCPConn); ok {
|
|
tc.SetKeepAliveConfig(l.cfg)
|
|
}
|
|
return conn, nil
|
|
}
|