123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222 |
- package v2rayapi
- import (
- "context"
- "net"
- "regexp"
- "runtime"
- "strings"
- "sync"
- "time"
- "github.com/sagernet/sing-box/adapter"
- "github.com/sagernet/sing-box/option"
- "github.com/sagernet/sing/common/atomic"
- "github.com/sagernet/sing/common/bufio"
- E "github.com/sagernet/sing/common/exceptions"
- N "github.com/sagernet/sing/common/network"
- )
- func init() {
- StatsService_ServiceDesc.ServiceName = "v2ray.core.app.stats.command.StatsService"
- }
- var (
- _ adapter.ConnectionTracker = (*StatsService)(nil)
- _ StatsServiceServer = (*StatsService)(nil)
- )
- type StatsService struct {
- createdAt time.Time
- inbounds map[string]bool
- outbounds map[string]bool
- users map[string]bool
- access sync.Mutex
- counters map[string]*atomic.Int64
- }
- func NewStatsService(options option.V2RayStatsServiceOptions) *StatsService {
- if !options.Enabled {
- return nil
- }
- inbounds := make(map[string]bool)
- outbounds := make(map[string]bool)
- users := make(map[string]bool)
- for _, inbound := range options.Inbounds {
- inbounds[inbound] = true
- }
- for _, outbound := range options.Outbounds {
- outbounds[outbound] = true
- }
- for _, user := range options.Users {
- users[user] = true
- }
- return &StatsService{
- createdAt: time.Now(),
- inbounds: inbounds,
- outbounds: outbounds,
- users: users,
- counters: make(map[string]*atomic.Int64),
- }
- }
- func (s *StatsService) RoutedConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext, matchedRule adapter.Rule, matchOutbound adapter.Outbound) net.Conn {
- inbound := metadata.Inbound
- user := metadata.User
- outbound := matchOutbound.Tag()
- var readCounter []*atomic.Int64
- var writeCounter []*atomic.Int64
- countInbound := inbound != "" && s.inbounds[inbound]
- countOutbound := outbound != "" && s.outbounds[outbound]
- countUser := user != "" && s.users[user]
- if !countInbound && !countOutbound && !countUser {
- return conn
- }
- s.access.Lock()
- if countInbound {
- readCounter = append(readCounter, s.loadOrCreateCounter("inbound>>>"+inbound+">>>traffic>>>uplink"))
- writeCounter = append(writeCounter, s.loadOrCreateCounter("inbound>>>"+inbound+">>>traffic>>>downlink"))
- }
- if countOutbound {
- readCounter = append(readCounter, s.loadOrCreateCounter("outbound>>>"+outbound+">>>traffic>>>uplink"))
- writeCounter = append(writeCounter, s.loadOrCreateCounter("outbound>>>"+outbound+">>>traffic>>>downlink"))
- }
- if countUser {
- readCounter = append(readCounter, s.loadOrCreateCounter("user>>>"+user+">>>traffic>>>uplink"))
- writeCounter = append(writeCounter, s.loadOrCreateCounter("user>>>"+user+">>>traffic>>>downlink"))
- }
- s.access.Unlock()
- return bufio.NewInt64CounterConn(conn, readCounter, writeCounter)
- }
- func (s *StatsService) RoutedPacketConnection(ctx context.Context, conn N.PacketConn, metadata adapter.InboundContext, matchedRule adapter.Rule, matchOutbound adapter.Outbound) N.PacketConn {
- inbound := metadata.Inbound
- user := metadata.User
- outbound := matchOutbound.Tag()
- var readCounter []*atomic.Int64
- var writeCounter []*atomic.Int64
- countInbound := inbound != "" && s.inbounds[inbound]
- countOutbound := outbound != "" && s.outbounds[outbound]
- countUser := user != "" && s.users[user]
- if !countInbound && !countOutbound && !countUser {
- return conn
- }
- s.access.Lock()
- if countInbound {
- readCounter = append(readCounter, s.loadOrCreateCounter("inbound>>>"+inbound+">>>traffic>>>uplink"))
- writeCounter = append(writeCounter, s.loadOrCreateCounter("inbound>>>"+inbound+">>>traffic>>>downlink"))
- }
- if countOutbound {
- readCounter = append(readCounter, s.loadOrCreateCounter("outbound>>>"+outbound+">>>traffic>>>uplink"))
- writeCounter = append(writeCounter, s.loadOrCreateCounter("outbound>>>"+outbound+">>>traffic>>>downlink"))
- }
- if countUser {
- readCounter = append(readCounter, s.loadOrCreateCounter("user>>>"+user+">>>traffic>>>uplink"))
- writeCounter = append(writeCounter, s.loadOrCreateCounter("user>>>"+user+">>>traffic>>>downlink"))
- }
- s.access.Unlock()
- return bufio.NewInt64CounterPacketConn(conn, readCounter, writeCounter)
- }
- func (s *StatsService) GetStats(ctx context.Context, request *GetStatsRequest) (*GetStatsResponse, error) {
- s.access.Lock()
- counter, loaded := s.counters[request.Name]
- s.access.Unlock()
- if !loaded {
- return nil, E.New(request.Name, " not found.")
- }
- var value int64
- if request.Reset_ {
- value = counter.Swap(0)
- } else {
- value = counter.Load()
- }
- return &GetStatsResponse{Stat: &Stat{Name: request.Name, Value: value}}, nil
- }
- func (s *StatsService) QueryStats(ctx context.Context, request *QueryStatsRequest) (*QueryStatsResponse, error) {
- var response QueryStatsResponse
- s.access.Lock()
- defer s.access.Unlock()
- if len(request.Patterns) == 0 {
- for name, counter := range s.counters {
- var value int64
- if request.Reset_ {
- value = counter.Swap(0)
- } else {
- value = counter.Load()
- }
- response.Stat = append(response.Stat, &Stat{Name: name, Value: value})
- }
- } else if request.Regexp {
- matchers := make([]*regexp.Regexp, 0, len(request.Patterns))
- for _, pattern := range request.Patterns {
- matcher, err := regexp.Compile(pattern)
- if err != nil {
- return nil, err
- }
- matchers = append(matchers, matcher)
- }
- for name, counter := range s.counters {
- for _, matcher := range matchers {
- if matcher.MatchString(name) {
- var value int64
- if request.Reset_ {
- value = counter.Swap(0)
- } else {
- value = counter.Load()
- }
- response.Stat = append(response.Stat, &Stat{Name: name, Value: value})
- }
- }
- }
- } else {
- for name, counter := range s.counters {
- for _, matcher := range request.Patterns {
- if strings.Contains(name, matcher) {
- var value int64
- if request.Reset_ {
- value = counter.Swap(0)
- } else {
- value = counter.Load()
- }
- response.Stat = append(response.Stat, &Stat{Name: name, Value: value})
- }
- }
- }
- }
- return &response, nil
- }
- func (s *StatsService) GetSysStats(ctx context.Context, request *SysStatsRequest) (*SysStatsResponse, error) {
- var rtm runtime.MemStats
- runtime.ReadMemStats(&rtm)
- response := &SysStatsResponse{
- Uptime: uint32(time.Now().Sub(s.createdAt).Seconds()),
- NumGoroutine: uint32(runtime.NumGoroutine()),
- Alloc: rtm.Alloc,
- TotalAlloc: rtm.TotalAlloc,
- Sys: rtm.Sys,
- Mallocs: rtm.Mallocs,
- Frees: rtm.Frees,
- LiveObjects: rtm.Mallocs - rtm.Frees,
- NumGC: rtm.NumGC,
- PauseTotalNs: rtm.PauseTotalNs,
- }
- return response, nil
- }
- func (s *StatsService) mustEmbedUnimplementedStatsServiceServer() {
- }
- //nolint:staticcheck
- func (s *StatsService) loadOrCreateCounter(name string) *atomic.Int64 {
- counter, loaded := s.counters[name]
- if loaded {
- return counter
- }
- counter = &atomic.Int64{}
- s.counters[name] = counter
- return counter
- }
|