package session import ( "bufio" "fmt" "io" "net/http" "sort" "sync" "sync/atomic" "time" "golang.org/x/text/language" "golang.org/x/text/message" "github.com/xjasonlyu/tun2socks/common/log" "github.com/xjasonlyu/tun2socks/common/stats" ) const maxCompletedSessions = 100 var ActiveConnections *int64 var ( StatsAddr = "localhost:6001" StatsPath = "/stats/session/plain" StatsVersion = "" ) type simpleSessionStater struct { mux sync.Mutex sessions sync.Map completedSessions []stats.Session server *http.Server } func NewSimpleSessionStater() stats.SessionStater { return &simpleSessionStater{} } func (s *simpleSessionStater) Start() error { log.Infof("Start session stater") sessionStatsHandler := func(resp http.ResponseWriter, req *http.Request) { // Make a snapshot. var sessions []stats.Session s.sessions.Range(func(key, value interface{}) bool { sess := value.(*stats.Session) sessions = append(sessions, *sess) return true }) p := message.NewPrinter(language.English) tablePrint := func(w io.Writer, sessions []stats.Session) { // Sort by session start time. sort.Slice(sessions, func(i, j int) bool { return sessions[i].SessionStart.Sub(sessions[j].SessionStart) < 0 }) _, _ = fmt.Fprintf(w, "
Process Name | Network | Duration | Dialer Addr | Client Addr | Target Addr | Upload Bytes | Download Bytes |
%v | %v | %v | %v | %v | %v | %v | %v |
Active sessions %d (%d)
", len(sessions), atomic.LoadInt64(ActiveConnections)) tablePrint(w, sessions) _, _ = fmt.Fprintf(w, "Recently completed sessions %d
", len(s.completedSessions)) tablePrint(w, s.completedSessions) _, _ = fmt.Fprintf(w, "") _ = w.Flush() } mux := http.NewServeMux() mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { http.Redirect(w, r, StatsPath, 301) }) mux.HandleFunc(StatsPath, sessionStatsHandler) s.server = &http.Server{Addr: StatsAddr, Handler: mux} go s.server.ListenAndServe() return nil } func (s *simpleSessionStater) Stop() error { log.Infof("Stop session stater") return s.server.Close() } func (s *simpleSessionStater) AddSession(key interface{}, session *stats.Session) { s.sessions.Store(key, session) } func (s *simpleSessionStater) GetSession(key interface{}) *stats.Session { if sess, ok := s.sessions.Load(key); ok { return sess.(*stats.Session) } return nil } func (s *simpleSessionStater) RemoveSession(key interface{}) { if sess, ok := s.sessions.Load(key); ok { // move to completed sessions s.mux.Lock() s.completedSessions = append(s.completedSessions, *(sess.(*stats.Session))) if len(s.completedSessions) > maxCompletedSessions { s.completedSessions = s.completedSessions[1:] } s.mux.Unlock() // delete s.sessions.Delete(key) } }