|  | @@ -11,6 +11,7 @@ import (
 | 
	
		
			
				|  |  |  	"v2ray.com/core/common"
 | 
	
		
			
				|  |  |  	"v2ray.com/core/common/buf"
 | 
	
		
			
				|  |  |  	"v2ray.com/core/common/net"
 | 
	
		
			
				|  |  | +	"v2ray.com/core/common/protocol"
 | 
	
		
			
				|  |  |  	"v2ray.com/core/proxy"
 | 
	
		
			
				|  |  |  	"v2ray.com/core/transport/ray"
 | 
	
		
			
				|  |  |  )
 | 
	
	
		
			
				|  | @@ -23,6 +24,8 @@ var (
 | 
	
		
			
				|  |  |  type DefaultDispatcher struct {
 | 
	
		
			
				|  |  |  	ohm    core.OutboundHandlerManager
 | 
	
		
			
				|  |  |  	router core.Router
 | 
	
		
			
				|  |  | +	policy core.PolicyManager
 | 
	
		
			
				|  |  | +	stats  core.StatManager
 | 
	
		
			
				|  |  |  }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  // NewDefaultDispatcher create a new DefaultDispatcher.
 | 
	
	
		
			
				|  | @@ -31,6 +34,8 @@ func NewDefaultDispatcher(ctx context.Context, config *Config) (*DefaultDispatch
 | 
	
		
			
				|  |  |  	d := &DefaultDispatcher{
 | 
	
		
			
				|  |  |  		ohm:    v.OutboundHandlerManager(),
 | 
	
		
			
				|  |  |  		router: v.Router(),
 | 
	
		
			
				|  |  | +		policy: v.PolicyManager(),
 | 
	
		
			
				|  |  | +		stats:  v.Stats(),
 | 
	
		
			
				|  |  |  	}
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  	if err := v.RegisterFeature((*core.Dispatcher)(nil), d); err != nil {
 | 
	
	
		
			
				|  | @@ -47,6 +52,10 @@ func (*DefaultDispatcher) Start() error {
 | 
	
		
			
				|  |  |  // Close implements app.Application.
 | 
	
		
			
				|  |  |  func (*DefaultDispatcher) Close() error { return nil }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | +func getStatsName(u *protocol.User) string {
 | 
	
		
			
				|  |  | +	return "user>traffic>" + u.Email
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |  // Dispatch implements core.Dispatcher.
 | 
	
		
			
				|  |  |  func (d *DefaultDispatcher) Dispatch(ctx context.Context, destination net.Destination) (ray.InboundRay, error) {
 | 
	
		
			
				|  |  |  	if !destination.IsValid() {
 | 
	
	
		
			
				|  | @@ -54,7 +63,26 @@ func (d *DefaultDispatcher) Dispatch(ctx context.Context, destination net.Destin
 | 
	
		
			
				|  |  |  	}
 | 
	
		
			
				|  |  |  	ctx = proxy.ContextWithTarget(ctx, destination)
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -	outbound := ray.New(ctx)
 | 
	
		
			
				|  |  | +	var rayOptions []ray.Option
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +	user := protocol.UserFromContext(ctx)
 | 
	
		
			
				|  |  | +	if user != nil && len(user.Email) > 0 {
 | 
	
		
			
				|  |  | +		name := getStatsName(user)
 | 
	
		
			
				|  |  | +		c, err := d.stats.RegisterCounter(name)
 | 
	
		
			
				|  |  | +		if err != nil {
 | 
	
		
			
				|  |  | +			c = d.stats.GetCounter(name)
 | 
	
		
			
				|  |  | +		}
 | 
	
		
			
				|  |  | +		if c == nil {
 | 
	
		
			
				|  |  | +			newError("failed to get stats counter ", name).AtWarning().WithContext(ctx).WriteToLog()
 | 
	
		
			
				|  |  | +		}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +		p := d.policy.ForLevel(user.Level)
 | 
	
		
			
				|  |  | +		if p.Stats.EnablePerUser {
 | 
	
		
			
				|  |  | +			rayOptions = append(rayOptions, ray.WithStatCounter(c))
 | 
	
		
			
				|  |  | +		}
 | 
	
		
			
				|  |  | +	}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +	outbound := ray.New(ctx, rayOptions...)
 | 
	
		
			
				|  |  |  	snifferList := proxyman.ProtocolSniffersFromContext(ctx)
 | 
	
		
			
				|  |  |  	if destination.Address.Family().IsDomain() || len(snifferList) == 0 {
 | 
	
		
			
				|  |  |  		go d.routedDispatch(ctx, outbound, destination)
 |