diff --git a/adapter/outbound/anytls.go b/adapter/outbound/anytls.go index 482623ee..66722261 100644 --- a/adapter/outbound/anytls.go +++ b/adapter/outbound/anytls.go @@ -42,6 +42,7 @@ type AnyTLSOption struct { UDP bool `proxy:"udp,omitempty"` IdleSessionCheckInterval int `proxy:"idle-session-check-interval,omitempty"` IdleSessionTimeout int `proxy:"idle-session-timeout,omitempty"` + MinIdleSession int `proxy:"min-idle-session,omitempty"` } func (t *AnyTLS) DialContext(ctx context.Context, metadata *C.Metadata, opts ...dialer.Option) (_ C.Conn, err error) { @@ -98,6 +99,7 @@ func NewAnyTLS(option AnyTLSOption) (*AnyTLS, error) { Dialer: singDialer, IdleSessionCheckInterval: time.Duration(option.IdleSessionCheckInterval) * time.Second, IdleSessionTimeout: time.Duration(option.IdleSessionTimeout) * time.Second, + MinIdleSession: option.MinIdleSession, } tlsConfig := &vmess.TLSConfig{ Host: option.SNI, diff --git a/docs/config.yaml b/docs/config.yaml index db66a2b3..934cf091 100644 --- a/docs/config.yaml +++ b/docs/config.yaml @@ -874,6 +874,7 @@ proxies: # socks5 udp: true # idle-session-check-interval: 30 # seconds # idle-session-timeout: 30 # seconds + # min-idle-session: 0 # sni: "example.com" # alpn: # - h2 diff --git a/transport/anytls/client.go b/transport/anytls/client.go index 19776df9..ea99b438 100644 --- a/transport/anytls/client.go +++ b/transport/anytls/client.go @@ -22,19 +22,19 @@ type ClientConfig struct { Password string IdleSessionCheckInterval time.Duration IdleSessionTimeout time.Duration + MinIdleSession int Server M.Socksaddr Dialer N.Dialer TLSConfig *vmess.TLSConfig } type Client struct { - passwordSha256 []byte - tlsConfig *vmess.TLSConfig - clientFingerprint string - dialer N.Dialer - server M.Socksaddr - sessionClient *session.Client - padding atomic.TypedValue[*padding.PaddingFactory] + passwordSha256 []byte + tlsConfig *vmess.TLSConfig + dialer N.Dialer + server M.Socksaddr + sessionClient *session.Client + padding atomic.TypedValue[*padding.PaddingFactory] } func NewClient(ctx context.Context, config ClientConfig) *Client { @@ -47,7 +47,7 @@ func NewClient(ctx context.Context, config ClientConfig) *Client { } // Initialize the padding state of this client padding.UpdatePaddingScheme(padding.DefaultPaddingScheme, &c.padding) - c.sessionClient = session.NewClient(ctx, c.CreateOutboundTLSConnection, &c.padding, config.IdleSessionCheckInterval, config.IdleSessionTimeout) + c.sessionClient = session.NewClient(ctx, c.CreateOutboundTLSConnection, &c.padding, config.IdleSessionCheckInterval, config.IdleSessionTimeout, config.MinIdleSession) return c } diff --git a/transport/anytls/session/client.go b/transport/anytls/session/client.go index 2312a6ff..58adbf94 100644 --- a/transport/anytls/session/client.go +++ b/transport/anytls/session/client.go @@ -28,13 +28,15 @@ type Client struct { padding *atomic.TypedValue[*padding.PaddingFactory] idleSessionTimeout time.Duration + minIdleSession int } -func NewClient(ctx context.Context, dialOut func(ctx context.Context) (net.Conn, error), _padding *atomic.TypedValue[*padding.PaddingFactory], idleSessionCheckInterval, idleSessionTimeout time.Duration) *Client { +func NewClient(ctx context.Context, dialOut func(ctx context.Context) (net.Conn, error), _padding *atomic.TypedValue[*padding.PaddingFactory], idleSessionCheckInterval, idleSessionTimeout time.Duration, minIdleSession int) *Client { c := &Client{ dialOut: dialOut, padding: _padding, idleSessionTimeout: idleSessionTimeout, + minIdleSession: minIdleSession, } if idleSessionCheckInterval <= time.Second*5 { idleSessionCheckInterval = time.Second * 30 @@ -138,17 +140,30 @@ func (c *Client) idleCleanup() { } func (c *Client) idleCleanupExpTime(expTime time.Time) { - var sessionToRemove = make([]*Session, 0) + sessionToRemove := make([]*Session, 0, c.idleSession.Len()) c.idleSessionLock.Lock() it := c.idleSession.Iterate() + + activeCount := 0 for it.IsNotEnd() { session := it.Value() - if session.idleSince.Before(expTime) { - sessionToRemove = append(sessionToRemove, session) - c.idleSession.Remove(it.Key()) - } + key := it.Key() it.MoveToNext() + + if !session.idleSince.Before(expTime) { + activeCount++ + continue + } + + if activeCount < c.minIdleSession { + session.idleSince = time.Now() + activeCount++ + continue + } + + sessionToRemove = append(sessionToRemove, session) + c.idleSession.Remove(key) } c.idleSessionLock.Unlock()