diff --git a/core/xray.go b/core/xray.go index a70507068..794ce8fff 100644 --- a/core/xray.go +++ b/core/xray.go @@ -92,6 +92,8 @@ type Instance struct { // Instance state func (server *Instance) IsRunning() bool { + server.statusLock.Lock() + defer server.statusLock.Unlock() return server.running } @@ -320,7 +322,7 @@ func (s *Instance) RequireFeatures(callback interface{}, optional bool) error { // AddFeature registers a feature into current Instance. func (s *Instance) AddFeature(feature features.Feature) error { - if s.running { + if s.IsRunning() { if err := feature.Start(); err != nil { errors.LogInfoInner(s.ctx, err, "failed to start feature") } diff --git a/transport/internet/hysteria/dialer.go b/transport/internet/hysteria/dialer.go index 4b774c353..6ff19c237 100644 --- a/transport/internet/hysteria/dialer.go +++ b/transport/internet/hysteria/dialer.go @@ -17,6 +17,7 @@ import ( "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/core" "github.com/xtls/xray-core/transport/internet" "github.com/xtls/xray-core/transport/internet/finalmask" "github.com/xtls/xray-core/transport/internet/hysteria/congestion" @@ -28,6 +29,8 @@ import ( type client struct { sync.Mutex + instance *core.Instance + forced bool dest net.Destination config *Config tlsConfig *gotls.Config @@ -64,11 +67,14 @@ func (c *client) close() { } func (c *client) dial(ctx context.Context) error { - status := c.status() - if status == StatusActive { - return nil + if c.forced { + return errors.New("client is closed") } - if status == StatusInactive { + + switch c.status() { + case StatusActive: + return nil + case StatusInactive: c.close() } @@ -90,7 +96,7 @@ func (c *client) dial(ctx context.Context) error { ChromeParrot: !quicParams.DisableChromeParrot, EnableDatagrams: true, MaxDatagramFrameSize: MaxDatagramFrameSize, - OmitMaxDatagramFrameSize: time.Now().After(time.Date(2026, 9, 1, 0, 0, 0, 0, time.UTC)), + OmitMaxDatagramFrameSize: true, DisablePathManager: true, } if quicParams.InitStreamReceiveWindow == 0 { @@ -256,9 +262,12 @@ func (c *client) udp(ctx context.Context) (stat.Connection, error) { return c.udpSM.udp() } -func (c *client) clean() { +func (c *client) clean(force bool) { c.Lock() - if c.status() == StatusInactive { + if force { + c.forced = true + } + if status := c.status(); force && status != StatusNull || status == StatusInactive { c.close() } c.Unlock() @@ -277,11 +286,23 @@ type clientManager struct { func (m *clientManager) clean() { ticker := time.NewTicker(idleCleanupInterval) for range ticker.C { + var forced []dialerConf + m.RLock() - for _, c := range m.m { - c.clean() + for k, c := range m.m { + force := c.instance != nil && !c.instance.IsRunning() + c.clean(force) + if force { + forced = append(forced, k) + } } m.RUnlock() + + for i := range forced { + m.Lock() + delete(m.m, forced[i]) + m.Unlock() + } } } @@ -306,15 +327,18 @@ func Dial(ctx context.Context, dest net.Destination, streamSettings *internet.Me go manager.clean() }) + dialerConfKey := dialerConf{dest, streamSettings} + manager.RLock() - c := manager.m[dialerConf{dest, streamSettings}] + c := manager.m[dialerConfKey] manager.RUnlock() if c == nil { manager.Lock() - c = manager.m[dialerConf{dest, streamSettings}] + c = manager.m[dialerConfKey] if c == nil { c = &client{ + instance: core.FromContext(ctx), dest: dest, config: streamSettings.ProtocolSettings.(*Config), tlsConfig: tlsConfig.GetTLSConfig(tls.WithDestination(dest)), @@ -322,7 +346,7 @@ func Dial(ctx context.Context, dest net.Destination, streamSettings *internet.Me finalMask: streamSettings.FinalMask, quicParams: streamSettings.QuicParams, } - manager.m[dialerConf{dest, streamSettings}] = c + manager.m[dialerConfKey] = c } manager.Unlock() }