|
|
@@ -0,0 +1,121 @@
|
|
|
+package internal
|
|
|
+
|
|
|
+import (
|
|
|
+ "net"
|
|
|
+ "sync"
|
|
|
+ "time"
|
|
|
+ v2net "v2ray.com/core/common/net"
|
|
|
+ "v2ray.com/core/common/signal"
|
|
|
+)
|
|
|
+
|
|
|
+type ConnectionId struct {
|
|
|
+ Local v2net.Address
|
|
|
+ Remote v2net.Address
|
|
|
+ RemotePort v2net.Port
|
|
|
+}
|
|
|
+
|
|
|
+func NewConnectionId(source v2net.Address, dest v2net.Destination) ConnectionId {
|
|
|
+ return ConnectionId{
|
|
|
+ Local: source,
|
|
|
+ Remote: dest.Address,
|
|
|
+ RemotePort: dest.Port,
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+type ExpiringConnection struct {
|
|
|
+ conn net.Conn
|
|
|
+ expire time.Time
|
|
|
+}
|
|
|
+
|
|
|
+func (o *ExpiringConnection) Expired() bool {
|
|
|
+ return o.expire.Before(time.Now())
|
|
|
+}
|
|
|
+
|
|
|
+type Pool struct {
|
|
|
+ sync.Mutex
|
|
|
+ connsByDest map[ConnectionId][]*ExpiringConnection
|
|
|
+ cleanupOnce signal.Once
|
|
|
+}
|
|
|
+
|
|
|
+func NewConnectionPool() *Pool {
|
|
|
+ return &Pool{
|
|
|
+ connsByDest: make(map[ConnectionId][]*ExpiringConnection),
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+func (o *Pool) Get(id ConnectionId) net.Conn {
|
|
|
+ o.Lock()
|
|
|
+ defer o.Unlock()
|
|
|
+
|
|
|
+ list, found := o.connsByDest[id]
|
|
|
+ if !found {
|
|
|
+ return nil
|
|
|
+ }
|
|
|
+ connIdx := -1
|
|
|
+ for idx, conn := range list {
|
|
|
+ if !conn.Expired() {
|
|
|
+ connIdx = idx
|
|
|
+ break
|
|
|
+ }
|
|
|
+ }
|
|
|
+ if connIdx == -1 {
|
|
|
+ return nil
|
|
|
+ }
|
|
|
+ listLen := len(list)
|
|
|
+ conn := list[connIdx]
|
|
|
+ if connIdx != listLen-1 {
|
|
|
+ list[connIdx] = list[listLen-1]
|
|
|
+ }
|
|
|
+ list = list[:listLen-1]
|
|
|
+ o.connsByDest[id] = list
|
|
|
+ return conn.conn
|
|
|
+}
|
|
|
+
|
|
|
+func (o *Pool) Cleanup() {
|
|
|
+ defer o.cleanupOnce.Reset()
|
|
|
+
|
|
|
+ for len(o.connsByDest) > 0 {
|
|
|
+ time.Sleep(time.Second * 5)
|
|
|
+ expiredConns := make([]net.Conn, 0, 16)
|
|
|
+ o.Lock()
|
|
|
+ for dest, list := range o.connsByDest {
|
|
|
+ validConns := make([]*ExpiringConnection, 0, len(list))
|
|
|
+ for _, conn := range list {
|
|
|
+ if conn.Expired() {
|
|
|
+ expiredConns = append(expiredConns, conn.conn)
|
|
|
+ } else {
|
|
|
+ validConns = append(validConns, conn)
|
|
|
+ }
|
|
|
+ }
|
|
|
+ if len(validConns) != len(list) {
|
|
|
+ o.connsByDest[dest] = validConns
|
|
|
+ }
|
|
|
+ }
|
|
|
+ o.Unlock()
|
|
|
+ for _, conn := range expiredConns {
|
|
|
+ conn.Close()
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+func (o *Pool) Put(id ConnectionId, conn net.Conn) {
|
|
|
+ expiringConn := &ExpiringConnection{
|
|
|
+ conn: conn,
|
|
|
+ expire: time.Now().Add(time.Second * 4),
|
|
|
+ }
|
|
|
+
|
|
|
+ o.Lock()
|
|
|
+ defer o.Unlock()
|
|
|
+
|
|
|
+ list, found := o.connsByDest[id]
|
|
|
+ if !found {
|
|
|
+ list = []*ExpiringConnection{expiringConn}
|
|
|
+ } else {
|
|
|
+ list = append(list, expiringConn)
|
|
|
+ }
|
|
|
+ o.connsByDest[id] = list
|
|
|
+
|
|
|
+ o.cleanupOnce.Do(func() {
|
|
|
+ go o.Cleanup()
|
|
|
+ })
|
|
|
+}
|