|  | @@ -12,27 +12,16 @@ import (
 | 
	
		
			
				|  |  |  	"v2ray.com/core/proxy"
 | 
	
		
			
				|  |  |  )
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -type workerWithContext struct {
 | 
	
		
			
				|  |  | -	ctx    context.Context
 | 
	
		
			
				|  |  | -	cancel context.CancelFunc
 | 
	
		
			
				|  |  | -	worker worker
 | 
	
		
			
				|  |  | -}
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -func (w *workerWithContext) Close() {
 | 
	
		
			
				|  |  | -	w.cancel()
 | 
	
		
			
				|  |  | -	w.worker.Close()
 | 
	
		
			
				|  |  | -}
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  |  type DynamicInboundHandler struct {
 | 
	
		
			
				|  |  | -	sync.Mutex
 | 
	
		
			
				|  |  |  	tag            string
 | 
	
		
			
				|  |  |  	ctx            context.Context
 | 
	
		
			
				|  |  |  	cancel         context.CancelFunc
 | 
	
		
			
				|  |  |  	proxyConfig    interface{}
 | 
	
		
			
				|  |  |  	receiverConfig *proxyman.ReceiverConfig
 | 
	
		
			
				|  |  | +	portMutex      sync.Mutex
 | 
	
		
			
				|  |  |  	portsInUse     map[v2net.Port]bool
 | 
	
		
			
				|  |  | -	worker         []*workerWithContext
 | 
	
		
			
				|  |  | -	worker2Recycle []*workerWithContext
 | 
	
		
			
				|  |  | +	workerMutex    sync.RWMutex
 | 
	
		
			
				|  |  | +	worker         []worker
 | 
	
		
			
				|  |  |  	lastRefresh    time.Time
 | 
	
		
			
				|  |  |  }
 | 
	
		
			
				|  |  |  
 | 
	
	
		
			
				|  | @@ -53,8 +42,9 @@ func NewDynamicInboundHandler(ctx context.Context, tag string, receiverConfig *p
 | 
	
		
			
				|  |  |  func (h *DynamicInboundHandler) allocatePort() v2net.Port {
 | 
	
		
			
				|  |  |  	from := int(h.receiverConfig.PortRange.From)
 | 
	
		
			
				|  |  |  	delta := int(h.receiverConfig.PortRange.To) - from + 1
 | 
	
		
			
				|  |  | -	h.Lock()
 | 
	
		
			
				|  |  | -	defer h.Unlock()
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +	h.portMutex.Lock()
 | 
	
		
			
				|  |  | +	defer h.portMutex.Unlock()
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  	for {
 | 
	
		
			
				|  |  |  		r := dice.Roll(delta)
 | 
	
	
		
			
				|  | @@ -67,30 +57,34 @@ func (h *DynamicInboundHandler) allocatePort() v2net.Port {
 | 
	
		
			
				|  |  |  	}
 | 
	
		
			
				|  |  |  }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -func (h *DynamicInboundHandler) refresh() error {
 | 
	
		
			
				|  |  | -	h.lastRefresh = time.Now()
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -	ports2Del := make([]v2net.Port, 0, 16)
 | 
	
		
			
				|  |  | -	for _, worker := range h.worker2Recycle {
 | 
	
		
			
				|  |  | +func (h *DynamicInboundHandler) waitAnyCloseWorkers(ctx context.Context, cancel context.CancelFunc, workers []worker, duration time.Duration) {
 | 
	
		
			
				|  |  | +	time.Sleep(duration)
 | 
	
		
			
				|  |  | +	cancel()
 | 
	
		
			
				|  |  | +	ports2Del := make([]v2net.Port, len(workers))
 | 
	
		
			
				|  |  | +	for idx, worker := range workers {
 | 
	
		
			
				|  |  | +		ports2Del[idx] = worker.Port()
 | 
	
		
			
				|  |  |  		worker.Close()
 | 
	
		
			
				|  |  | -		ports2Del = append(ports2Del, worker.worker.Port())
 | 
	
		
			
				|  |  |  	}
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -	h.Lock()
 | 
	
		
			
				|  |  | +	h.portMutex.Lock()
 | 
	
		
			
				|  |  |  	for _, port := range ports2Del {
 | 
	
		
			
				|  |  |  		delete(h.portsInUse, port)
 | 
	
		
			
				|  |  |  	}
 | 
	
		
			
				|  |  | -	h.Unlock()
 | 
	
		
			
				|  |  | +	h.portMutex.Unlock()
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +func (h *DynamicInboundHandler) refresh() error {
 | 
	
		
			
				|  |  | +	h.lastRefresh = time.Now()
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -	h.worker2Recycle, h.worker = h.worker, h.worker2Recycle[:0]
 | 
	
		
			
				|  |  | +	timeout := time.Minute * time.Duration(h.receiverConfig.AllocationStrategy.GetRefreshValue())
 | 
	
		
			
				|  |  | +	ctx, cancel := context.WithTimeout(h.ctx, timeout)
 | 
	
		
			
				|  |  | +	workers := make([]worker, 0, h.receiverConfig.AllocationStrategy.GetConcurrencyValue())
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  	address := h.receiverConfig.Listen.AsAddress()
 | 
	
		
			
				|  |  |  	if address == nil {
 | 
	
		
			
				|  |  |  		address = v2net.AnyIP
 | 
	
		
			
				|  |  |  	}
 | 
	
		
			
				|  |  |  	for i := uint32(0); i < h.receiverConfig.AllocationStrategy.GetConcurrencyValue(); i++ {
 | 
	
		
			
				|  |  | -		ctx, cancel := context.WithCancel(h.ctx)
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  |  		port := h.allocatePort()
 | 
	
		
			
				|  |  |  		p, err := proxy.CreateInboundHandler(ctx, h.proxyConfig)
 | 
	
		
			
				|  |  |  		if err != nil {
 | 
	
	
		
			
				|  | @@ -109,13 +103,10 @@ func (h *DynamicInboundHandler) refresh() error {
 | 
	
		
			
				|  |  |  				allowPassiveConn: h.receiverConfig.AllowPassiveConnection,
 | 
	
		
			
				|  |  |  			}
 | 
	
		
			
				|  |  |  			if err := worker.Start(); err != nil {
 | 
	
		
			
				|  |  | -				return err
 | 
	
		
			
				|  |  | +				log.Warning("Proxyman:InboundHandler: Failed to create TCP worker: ", err)
 | 
	
		
			
				|  |  | +				continue
 | 
	
		
			
				|  |  |  			}
 | 
	
		
			
				|  |  | -			h.worker = append(h.worker, &workerWithContext{
 | 
	
		
			
				|  |  | -				ctx:    ctx,
 | 
	
		
			
				|  |  | -				cancel: cancel,
 | 
	
		
			
				|  |  | -				worker: worker,
 | 
	
		
			
				|  |  | -			})
 | 
	
		
			
				|  |  | +			workers = append(workers, worker)
 | 
	
		
			
				|  |  |  		}
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  		if nl.HasNetwork(v2net.Network_UDP) {
 | 
	
	
		
			
				|  | @@ -127,16 +118,19 @@ func (h *DynamicInboundHandler) refresh() error {
 | 
	
		
			
				|  |  |  				recvOrigDest: h.receiverConfig.ReceiveOriginalDestination,
 | 
	
		
			
				|  |  |  			}
 | 
	
		
			
				|  |  |  			if err := worker.Start(); err != nil {
 | 
	
		
			
				|  |  | -				return err
 | 
	
		
			
				|  |  | +				log.Warning("Proxyman:InboundHandler: Failed to create UDP worker: ", err)
 | 
	
		
			
				|  |  | +				continue
 | 
	
		
			
				|  |  |  			}
 | 
	
		
			
				|  |  | -			h.worker = append(h.worker, &workerWithContext{
 | 
	
		
			
				|  |  | -				ctx:    ctx,
 | 
	
		
			
				|  |  | -				cancel: cancel,
 | 
	
		
			
				|  |  | -				worker: worker,
 | 
	
		
			
				|  |  | -			})
 | 
	
		
			
				|  |  | +			workers = append(workers, worker)
 | 
	
		
			
				|  |  |  		}
 | 
	
		
			
				|  |  |  	}
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | +	h.workerMutex.Lock()
 | 
	
		
			
				|  |  | +	h.worker = workers
 | 
	
		
			
				|  |  | +	h.workerMutex.Unlock()
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +	go h.waitAnyCloseWorkers(ctx, cancel, workers, timeout)
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |  	return nil
 | 
	
		
			
				|  |  |  }
 | 
	
		
			
				|  |  |  
 | 
	
	
		
			
				|  | @@ -162,7 +156,10 @@ func (h *DynamicInboundHandler) Close() {
 | 
	
		
			
				|  |  |  }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  func (h *DynamicInboundHandler) GetRandomInboundProxy() (proxy.Inbound, v2net.Port, int) {
 | 
	
		
			
				|  |  | +	h.workerMutex.RLock()
 | 
	
		
			
				|  |  | +	defer h.workerMutex.RUnlock()
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |  	w := h.worker[dice.Roll(len(h.worker))]
 | 
	
		
			
				|  |  |  	expire := h.receiverConfig.AllocationStrategy.GetRefreshValue() - uint32(time.Since(h.lastRefresh)/time.Minute)
 | 
	
		
			
				|  |  | -	return w.worker.Proxy(), w.worker.Port(), int(expire)
 | 
	
		
			
				|  |  | +	return w.Proxy(), w.Port(), int(expire)
 | 
	
		
			
				|  |  |  }
 |