| 
					
				 | 
			
			
				@@ -1,19 +1,137 @@ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 package mux 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-import "v2ray.com/core/common/net" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+import ( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	"context" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	"sync" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	"time" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	"v2ray.com/core/common/buf" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	"v2ray.com/core/common/signal" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	"v2ray.com/core/proxy" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	"v2ray.com/core/transport/ray" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 const ( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 	maxParallel = 8 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 	maxTotal    = 128 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 ) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-type mergerWorker struct { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+type clientSession struct { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	sync.Mutex 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	outboundRay    ray.OutboundRay 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	parent         *Client 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	id             uint16 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	uplinkClosed   bool 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	downlinkClosed bool 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+func (s *clientSession) checkAndRemove() { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	s.Lock() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	if s.uplinkClosed && s.downlinkClosed { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		s.parent.remove(s.id) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	s.Unlock() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+func (s *clientSession) closeUplink() { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	s.Lock() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	s.uplinkClosed = true 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	s.Unlock() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	s.checkAndRemove() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+func (s *clientSession) closeDownlink() { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	s.Lock() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	s.downlinkClosed = true 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	s.Unlock() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	s.checkAndRemove() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+type Client struct { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	access     sync.RWMutex 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	count      uint16 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	sessions   map[uint16]*clientSession 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	inboundRay ray.InboundRay 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+func (m *Client) IsFullyOccupied() bool { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	m.access.RLock() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	defer m.access.RUnlock() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	return len(m.sessions) >= maxParallel 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+func (m *Client) IsFullyUsed() bool { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	m.access.RLock() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	defer m.access.RUnlock() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	return m.count >= maxTotal 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-func (w *mergerWorker) isFull() bool { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-	return true 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+func (m *Client) remove(id uint16) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	m.access.Lock() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	delete(m.sessions, id) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	m.access.Unlock() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-type Merger struct { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-	sessions map[net.Destination]mergerWorker 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+func (m *Client) fetchInput(ctx context.Context, session *clientSession) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	dest, _ := proxy.TargetFromContext(ctx) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	writer := &muxWriter{ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		dest:   dest, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		id:     session.id, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		writer: m.inboundRay.InboundInput(), 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	_, timer := signal.CancelAfterInactivity(ctx, time.Minute*5) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	buf.PipeUntilEOF(timer, session.outboundRay.OutboundInput(), writer) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	writer.Close() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	session.closeUplink() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+func (m *Client) Dispatch(ctx context.Context, outboundRay ray.OutboundRay) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	m.access.Lock() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	defer m.access.Unlock() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	m.count++ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	id := m.count 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	session := &clientSession{ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		outboundRay: outboundRay, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		parent:      m, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		id:          id, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	m.sessions[id] = session 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	go m.fetchInput(ctx, session) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+func (m *Client) fetchOutput() { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	reader := NewReader(m.inboundRay.InboundOutput()) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	for { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		meta, err := reader.ReadMetadata() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		if err != nil { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+			break 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		m.access.RLock() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		session, found := m.sessions[meta.SessionID] 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		m.access.RUnlock() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		if found && meta.SessionStatus == SessionStatusEnd { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+			session.closeDownlink() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		if !meta.Option.Has(OptionData) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+			continue 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		for { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+			data, more, err := reader.Read() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+			if err != nil { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+				break 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+			} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+			if found { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+				if err := session.outboundRay.OutboundOutput().Write(data); err != nil { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+					break 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+				} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+			} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+			if !more { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+				break 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+			} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 } 
			 |