LjhAUMEM
2026-05-02 20:27:27 +08:00
committed by GitHub
parent 52cf9ef5d6
commit 1d62941bd2
20 changed files with 845 additions and 1171 deletions
+43 -69
View File
@@ -5,11 +5,11 @@ import (
gotls "crypto/tls"
"fmt"
"io"
"math/rand"
"net/http"
"net/http/httptrace"
"net/url"
reflect "reflect"
"runtime"
"strconv"
"sync"
"sync/atomic"
@@ -21,6 +21,7 @@ import (
"github.com/xtls/xray-core/common/buf"
"github.com/xtls/xray-core/common/errors"
"github.com/xtls/xray-core/common/net"
"github.com/xtls/xray-core/common/net/cnc"
"github.com/xtls/xray-core/common/signal/done"
"github.com/xtls/xray-core/common/uuid"
"github.com/xtls/xray-core/transport/internet"
@@ -173,7 +174,7 @@ func createHTTPClient(dest net.Destination, streamSettings *internet.MemoryStrea
MaxIdleTimeout: time.Duration(quicParams.MaxIdleTimeout) * time.Second,
KeepAlivePeriod: time.Duration(quicParams.KeepAlivePeriod) * time.Second,
MaxIncomingStreams: quicParams.MaxIncomingStreams,
DisablePathMTUDiscovery: quicParams.DisablePathMtuDiscovery,
DisablePathMTUDiscovery: quicParams.DisablePathMtuDiscovery || (runtime.GOOS != "linux" && runtime.GOOS != "windows" && runtime.GOOS != "darwin"),
}
if quicParams.MaxIdleTimeout == 0 {
quicConfig.MaxIdleTimeout = net.ConnIdleTimeout
@@ -194,110 +195,83 @@ func createHTTPClient(dest net.Destination, streamSettings *internet.MemoryStrea
QUICConfig: quicConfig,
TLSClientConfig: gotlsConfig,
Dial: func(ctx context.Context, addr string, tlsCfg *gotls.Config, cfg *quic.Config) (*quic.Conn, error) {
udphopDialer := func(addr *net.UDPAddr) (net.PacketConn, error) {
udpHopDialer := func(addr *net.UDPAddr) (net.PacketConn, error) {
conn, err := internet.DialSystem(ctx, net.UDPDestination(net.IPAddress(addr.IP), net.Port(addr.Port)), streamSettings.SocketSettings)
if err != nil {
errors.LogDebug(context.Background(), "skip hop: failed to dial to dest")
conn.Close()
return nil, errors.New()
errors.LogInfoInner(context.Background(), err, "skip hop: failed to dial to dest")
return nil, errors.New("failed to dial to dest").Base(err)
}
var udpConn net.PacketConn
var pktConn net.PacketConn
switch c := conn.(type) {
case *internet.PacketConnWrapper:
udpConn = c.PacketConn
pktConn = c.PacketConn
case *net.UDPConn:
udpConn = c
pktConn = c
default:
errors.LogDebug(context.Background(), "skip hop: udphop requires being at the outermost level ", reflect.TypeOf(c))
errors.LogInfo(context.Background(), "skip hop: invalid conn ", reflect.TypeOf(c))
conn.Close()
return nil, errors.New()
return nil, errors.New("invalid conn ", reflect.TypeOf(c))
}
return udpConn, nil
return pktConn, nil
}
var index int
if len(quicParams.UdpHop.Ports) > 0 {
index = rand.Intn(len(quicParams.UdpHop.Ports))
dest.Port = net.Port(quicParams.UdpHop.Ports[index])
}
conn, err := internet.DialSystem(ctx, dest, streamSettings.SocketSettings)
var pktConn net.PacketConn
var udpAddr *net.UDPAddr
var err error
udpAddr, err = net.ResolveUDPAddr("udp", dest.NetAddr())
if err != nil {
return nil, err
}
var udpConn net.PacketConn
var udpAddr *net.UDPAddr
switch c := conn.(type) {
case *internet.PacketConnWrapper:
udpConn = c.PacketConn
udpAddr, err = net.ResolveUDPAddr("udp", c.Dest.String())
if err != nil {
conn.Close()
return nil, err
}
case *net.UDPConn:
udpConn = c
udpAddr, err = net.ResolveUDPAddr("udp", c.RemoteAddr().String())
if err != nil {
conn.Close()
return nil, err
}
default:
udpConn = &internet.FakePacketConn{Conn: c}
udpAddr, err = net.ResolveUDPAddr("udp", c.RemoteAddr().String())
if err != nil {
conn.Close()
return nil, err
}
if len(quicParams.UdpHop.Ports) > 0 {
conn.Close()
return nil, errors.New("udphop requires being at the outermost level ", reflect.TypeOf(c))
}
}
if len(quicParams.UdpHop.Ports) > 0 {
addr := &udphop.UDPHopAddr{
IP: udpAddr.IP,
Ports: quicParams.UdpHop.Ports,
}
udpConn, err = udphop.NewUDPHopPacketConn(addr, index, quicParams.UdpHop.IntervalMin, quicParams.UdpHop.IntervalMax, udphopDialer, udpConn)
pktConn, err = udphop.NewUDPHopPacketConn(udphop.ToAddrs(udpAddr.IP, quicParams.UdpHop.Ports), time.Duration(quicParams.UdpHop.IntervalMin)*time.Second, time.Duration(quicParams.UdpHop.IntervalMax)*time.Second, udpHopDialer)
if err != nil {
conn.Close()
return nil, errors.New("udphop err").Base(err)
return nil, err
}
} else {
conn, err := internet.DialSystem(ctx, dest, streamSettings.SocketSettings)
if err != nil {
return nil, err
}
switch c := conn.(type) {
case *internet.PacketConnWrapper:
pktConn = c.PacketConn
case *net.UDPConn:
pktConn = c
case *cnc.Connection:
pktConn = &internet.FakePacketConn{Conn: c}
default:
panic(reflect.TypeOf(c))
}
}
if streamSettings.UdpmaskManager != nil {
udpConn, err = streamSettings.UdpmaskManager.WrapPacketConnClient(udpConn)
newConn, err := streamSettings.UdpmaskManager.WrapPacketConnClient(pktConn)
if err != nil {
conn.Close()
pktConn.Close()
return nil, errors.New("mask err").Base(err)
}
pktConn = newConn
}
quicConn, err := quic.DialEarly(ctx, udpConn, udpAddr, tlsCfg, cfg)
conn, err := quic.DialEarly(ctx, pktConn, udpAddr, tlsCfg, cfg)
if err != nil {
return nil, err
}
switch quicParams.Congestion {
case "force-brutal":
errors.LogDebug(context.Background(), quicConn.RemoteAddr(), " ", "congestion brutal bytes per second ", quicParams.BrutalUp)
congestion.UseBrutal(quicConn, quicParams.BrutalUp)
case "reno":
errors.LogDebug(context.Background(), quicConn.RemoteAddr(), " ", "congestion reno")
case "", "bbr":
congestion.UseBBR(conn, bbr.Profile(quicParams.BbrProfile))
case "force-brutal":
congestion.UseBrutal(conn, quicParams.BrutalUp)
default:
errors.LogDebug(context.Background(), quicConn.RemoteAddr(), " ", "congestion bbr ", quicParams.BbrProfile)
congestion.UseBBR(quicConn, bbr.Profile(quicParams.BbrProfile))
panic(quicParams.Congestion)
}
return quicConn, nil
return conn, nil
},
}
} else if httpVersion == "2" {
+41 -30
View File
@@ -8,6 +8,7 @@ import (
"fmt"
"io"
"net/http"
"runtime"
"slices"
"strconv"
"strings"
@@ -440,7 +441,7 @@ type Listener struct {
server http.Server
h3server *http3.Server
listener net.Listener
h3listener *quic.EarlyListener
h3listener Qface
config *Config
addConn internet.ConnHandler
isH3 bool
@@ -487,12 +488,12 @@ func ListenXH(ctx context.Context, address net.Address, port net.Port, streamSet
return nil, errors.New("failed to listen UDP for XHTTP/3 on ", address, ":", port).Base(err)
}
if streamSettings.UdpmaskManager != nil {
pktConn, err := streamSettings.UdpmaskManager.WrapPacketConnServer(Conn)
newConn, err := streamSettings.UdpmaskManager.WrapPacketConnServer(Conn)
if err != nil {
Conn.Close()
return nil, errors.New("mask err").Base(err)
}
Conn = pktConn
Conn = newConn
}
quicParams := streamSettings.QuicParams
@@ -510,13 +511,17 @@ func ListenXH(ctx context.Context, address net.Address, port net.Port, streamSet
MaxConnectionReceiveWindow: quicParams.MaxConnReceiveWindow,
MaxIdleTimeout: time.Duration(quicParams.MaxIdleTimeout) * time.Second,
MaxIncomingStreams: quicParams.MaxIncomingStreams,
DisablePathMTUDiscovery: quicParams.DisablePathMtuDiscovery,
DisablePathMTUDiscovery: quicParams.DisablePathMtuDiscovery || (runtime.GOOS != "linux" && runtime.GOOS != "windows" && runtime.GOOS != "darwin"),
}
l.h3listener, err = quic.ListenEarly(Conn, tlsConfig, quicConfig)
if err != nil {
return nil, errors.New("failed to listen QUIC for XHTTP/3 on ", address, ":", port).Base(err)
}
l.h3listener = &QListener{
Qface: l.h3listener,
quicParams: quicParams,
}
errors.LogInfo(ctx, "listening QUIC for XHTTP/3 on ", address, ":", port)
handler.localAddr = l.h3listener.Addr()
@@ -525,30 +530,8 @@ func ListenXH(ctx context.Context, address net.Address, port net.Port, streamSet
Handler: handler,
}
go func() {
for {
conn, err := l.h3listener.Accept(context.Background())
if err != nil {
errors.LogInfoInner(ctx, err, "XHTTP/3 listener closed")
return
}
switch quicParams.Congestion {
case "force-brutal":
errors.LogDebug(context.Background(), conn.RemoteAddr(), " ", "congestion brutal bytes per second ", quicParams.BrutalUp)
congestion.UseBrutal(conn, quicParams.BrutalUp)
case "reno":
errors.LogDebug(context.Background(), conn.RemoteAddr(), " ", "congestion reno")
default:
errors.LogDebug(context.Background(), conn.RemoteAddr(), " ", "congestion bbr ", quicParams.BbrProfile)
congestion.UseBBR(conn, bbr.Profile(quicParams.BbrProfile))
}
go func() {
if err := l.h3server.ServeQUICConn(conn); err != nil {
errors.LogDebugInner(ctx, err, "XHTTP/3 connection ended")
}
_ = conn.CloseWithError(0, "")
}()
if err := l.h3server.ServeListener(l.h3listener); err != nil {
errors.LogErrorInner(ctx, err, "failed to serve HTTP/3 for XHTTP/3")
}
}()
} else { // tcp
@@ -614,10 +597,8 @@ func (ln *Listener) Addr() net.Addr {
func (ln *Listener) Close() error {
if ln.h3server != nil {
if err := ln.h3server.Close(); err != nil {
_ = ln.h3listener.Close()
return err
}
return ln.h3listener.Close()
} else if ln.listener != nil {
return ln.listener.Close()
}
@@ -633,3 +614,33 @@ func getTLSConfig(streamSettings *internet.MemoryStreamConfig) *gotls.Config {
func init() {
common.Must(internet.RegisterTransportListener(protocolName, ListenXH))
}
type Qface interface {
Accept(ctx context.Context) (*quic.Conn, error)
Addr() net.Addr
Close() error
}
var _ Qface = (*quic.EarlyListener)(nil)
type QListener struct {
Qface
quicParams *internet.QuicParams
}
func (l *QListener) Accept(ctx context.Context) (*quic.Conn, error) {
conn, err := l.Qface.Accept(ctx)
if err != nil {
return nil, err
}
switch l.quicParams.Congestion {
case "reno":
case "", "bbr":
congestion.UseBBR(conn, bbr.Profile(l.quicParams.BbrProfile))
case "force-brutal":
congestion.UseBrutal(conn, l.quicParams.BrutalUp)
default:
panic(l.quicParams.Congestion)
}
return conn, nil
}