| 
					
				 | 
			
			
				@@ -57,6 +57,52 @@ func (s *session) closeDownlink() { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 	s.checkAndRemove() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+type ClientManager struct { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	access  sync.Mutex 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	clients []*Client 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	proxy   proxy.Outbound 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	dialer  proxy.Dialer 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+func NewClientManager(p proxy.Outbound, d proxy.Dialer) *ClientManager { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	return &ClientManager{ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		proxy:  p, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		dialer: d, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+func (m *ClientManager) Dispatch(ctx context.Context, outboundRay ray.OutboundRay) error { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	m.access.Lock() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	defer m.access.Unlock() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	for _, client := range m.clients { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		if client.Dispatch(ctx, outboundRay) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+			return nil 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	client, err := NewClient(m.proxy, m.dialer, m) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	if err != nil { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		return err 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	m.clients = append(m.clients, client) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	client.Dispatch(ctx, outboundRay) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	return nil 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+func (m *ClientManager) onClientFinish() { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	m.access.Lock() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	defer m.access.Unlock() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	nActive := 0 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	for idx, client := range m.clients { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		if nActive != idx && !client.Closed() { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+			m.clients[nActive] = client 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	m.clients = m.clients[:nActive] 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 type Client struct { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 	access     sync.RWMutex 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 	count      uint16 
			 | 
		
	
	
		
			
				| 
					
				 | 
			
			
				@@ -64,11 +110,12 @@ type Client struct { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 	inboundRay ray.InboundRay 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 	ctx        context.Context 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 	cancel     context.CancelFunc 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	manager    *ClientManager 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 var muxCoolDestination = net.TCPDestination(net.DomainAddress("v1.mux.cool"), net.Port(9527)) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-func NewClient(p proxy.Outbound, dialer proxy.Dialer) (*Client, error) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+func NewClient(p proxy.Outbound, dialer proxy.Dialer, m *ClientManager) (*Client, error) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 	ctx, cancel := context.WithCancel(context.Background()) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 	ctx = proxy.ContextWithTarget(ctx, muxCoolDestination) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 	pipe := ray.NewRay(ctx) 
			 | 
		
	
	
		
			
				| 
					
				 | 
			
			
				@@ -82,6 +129,7 @@ func NewClient(p proxy.Outbound, dialer proxy.Dialer) (*Client, error) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 		inboundRay: pipe, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 		ctx:        ctx, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 		cancel:     cancel, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		manager:    m, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 	}, nil 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
	
		
			
				| 
					
				 | 
			
			
				@@ -101,6 +149,7 @@ func (m *Client) remove(id uint16) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 	if len(m.sessions) == 0 { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 		m.cancel() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 		m.inboundRay.InboundInput().Close() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		go m.manager.onClientFinish() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 	} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
	
		
			
				| 
					
				 | 
			
			
				@@ -121,7 +170,10 @@ func (m *Client) fetchInput(ctx context.Context, s *session) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 		writer: m.inboundRay.InboundInput(), 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 	} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 	_, timer := signal.CancelAfterInactivity(ctx, time.Minute*5) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-	buf.PipeUntilEOF(timer, s.input, writer) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	if err := buf.PipeUntilEOF(timer, s.input, writer); err != nil { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		log.Info("Proxyman|Mux|Client: Failed to fetch all input: ", err) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 	writer.Close() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 	s.closeUplink() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 } 
			 |