| 
					
				 | 
			
			
				@@ -42,7 +42,7 @@ func (s *session) closeUplink() { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 	allDone = s.uplinkClosed && s.downlinkClosed 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 	s.Unlock() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 	if allDone { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-		go s.parent.remove(s.id) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		s.parent.remove(s.id) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 	} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
	
		
			
				| 
					
				 | 
			
			
				@@ -53,7 +53,7 @@ func (s *session) closeDownlink() { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 	allDone = s.uplinkClosed && s.downlinkClosed 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 	s.Unlock() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 	if allDone { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-		go s.parent.remove(s.id) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		s.parent.remove(s.id) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 	} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
	
		
			
				| 
					
				 | 
			
			
				@@ -109,13 +109,14 @@ func (m *ClientManager) onClientFinish() { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 type Client struct { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-	access     sync.RWMutex 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-	count      uint16 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-	sessions   map[uint16]*session 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-	inboundRay ray.InboundRay 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-	ctx        context.Context 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-	cancel     context.CancelFunc 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-	manager    *ClientManager 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	access         sync.RWMutex 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	count          uint16 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	sessions       map[uint16]*session 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	inboundRay     ray.InboundRay 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	ctx            context.Context 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	cancel         context.CancelFunc 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	manager        *ClientManager 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	session2Remove chan uint16 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 var muxCoolDestination = net.TCPDestination(net.DomainAddress("v1.mux.cool"), net.Port(9527)) 
			 | 
		
	
	
		
			
				| 
					
				 | 
			
			
				@@ -126,27 +127,24 @@ func NewClient(p proxy.Outbound, dialer proxy.Dialer, m *ClientManager) (*Client 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 	pipe := ray.NewRay(ctx) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 	go p.Process(ctx, pipe, dialer) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 	c := &Client{ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-		sessions:   make(map[uint16]*session, 256), 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-		inboundRay: pipe, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-		ctx:        ctx, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-		cancel:     cancel, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-		manager:    m, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-		count:      0, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		sessions:       make(map[uint16]*session, 256), 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		inboundRay:     pipe, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		ctx:            ctx, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		cancel:         cancel, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		manager:        m, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		count:          0, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		session2Remove: make(chan uint16, 16), 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 	} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 	go c.fetchOutput() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	go c.monitor() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 	return c, nil 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 func (m *Client) remove(id uint16) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-	m.access.Lock() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-	defer m.access.Unlock() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				- 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-	delete(m.sessions, id) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				- 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-	if len(m.sessions) == 0 { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-		m.cancel() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-		m.inboundRay.InboundInput().Close() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-		go m.manager.onClientFinish() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	select { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	case m.session2Remove <- id: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	default: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		// Probably not gonna happen. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 	} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
	
		
			
				| 
					
				 | 
			
			
				@@ -159,6 +157,31 @@ func (m *Client) Closed() bool { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 	} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+func (m *Client) monitor() { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	for { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		select { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		case <-m.ctx.Done(): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+			m.cleanup() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+			return 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		case id := <-m.session2Remove: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+			m.access.Lock() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+			delete(m.sessions, id) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+			m.access.Unlock() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+func (m *Client) cleanup() { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	m.access.Lock() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	defer m.access.Unlock() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	for _, s := range m.sessions { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		s.closeUplink() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		s.closeDownlink() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		s.output.CloseError() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 func fetchInput(ctx context.Context, s *session, output buf.Writer) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 	dest, _ := proxy.TargetFromContext(ctx) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 	writer := &Writer{ 
			 | 
		
	
	
		
			
				| 
					
				 | 
			
			
				@@ -242,6 +265,8 @@ func pipe(reader *Reader, writer buf.Writer) error { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 func (m *Client) fetchOutput() { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	defer m.cancel() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 	reader := NewReader(m.inboundRay.InboundOutput()) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 	for { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 		meta, err := reader.ReadMetadata() 
			 | 
		
	
	
		
			
				| 
					
				 | 
			
			
				@@ -271,15 +296,6 @@ func (m *Client) fetchOutput() { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 			break 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 		} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 	} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				- 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-	// Close all downlinks 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-	m.access.RLock() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-	for _, s := range m.sessions { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-		s.closeUplink() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-		s.closeDownlink() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-		s.output.CloseError() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-	} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-	m.access.RUnlock() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 type Server struct { 
			 |