package session import ( "bufio" "errors" "fmt" "io" "net" "net/http" "runtime" "sort" "sync" "sync/atomic" "time" "github.com/xjasonlyu/tun2socks/common/queue" C "github.com/xjasonlyu/tun2socks/constant" ) const maxCompletedSessions = 100 var ( ServeAddr = "localhost:6001" ServePath = "/session/plain" ) type Server struct { *http.Server trafficUp int64 trafficDown int64 activeSessionMap sync.Map completedSessionQueue *queue.Queue } func NewServer() *Server { return &Server{ completedSessionQueue: queue.New(maxCompletedSessions), } } func (s *Server) handler(resp http.ResponseWriter, req *http.Request) { // Slice of active sessions var activeSessions []*Session s.activeSessionMap.Range(func(key, value interface{}) bool { activeSessions = append(activeSessions, value.(*Session)) return true }) // Slice of completed sessions var completedSessions []*Session for _, item := range s.completedSessionQueue.Copy() { if session, ok := item.(*Session); ok { completedSessions = append(completedSessions, session) } } tablePrint := func(w io.Writer, sessions []*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 | Network | Date | Duration | Client Addr | Target Addr | Upload | Download |
---|---|---|---|---|---|---|---|
%v | %v | %v | %v | %v | %v | %v | %v |
Statistics (%d)
", runtime.NumGoroutine()) _, _ = fmt.Fprintf(w, "Last Refresh Time | Uptime | CPU | MEM | Total | Upload | Download |
---|---|---|---|---|---|---|
%v | %v | %v | %v | %v | %v | %v |
Active sessions: %d
", len(activeSessions)) tablePrint(w, activeSessions) _, _ = fmt.Fprintf(w, "Recently completed sessions: %d
", len(completedSessions)) tablePrint(w, completedSessions) _, _ = fmt.Fprintf(w, "") _ = w.Flush() } func (s *Server) Start() error { _, port, err := net.SplitHostPort(ServeAddr) if port == "0" || port == "" || err != nil { return errors.New("address format error") } tcpAddr, err := net.ResolveTCPAddr("tcp", ServeAddr) if err != nil { return err } c, err := net.ListenTCP("tcp", tcpAddr) if err != nil { return err } mux := http.NewServeMux() mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { http.Redirect(w, r, ServePath, 301) }) mux.HandleFunc(ServePath, s.handler) s.Server = &http.Server{Addr: ServeAddr, Handler: mux} go func() { s.Serve(c) }() return nil } func (s *Server) Stop() error { return s.Close() } func (s *Server) AddSession(key interface{}, session *Session) { if session != nil { s.activeSessionMap.Store(key, session) } } func (s *Server) RemoveSession(key interface{}) { if item, ok := s.activeSessionMap.Load(key); ok { session := item.(*Session) // delete first s.activeSessionMap.Delete(key) // record up & down traffic atomic.AddInt64(&s.trafficUp, atomic.LoadInt64(&session.UploadBytes)) atomic.AddInt64(&s.trafficDown, atomic.LoadInt64(&session.DownloadBytes)) // move to completed sessions s.completedSessionQueue.Put(session) if s.completedSessionQueue.Len() > maxCompletedSessions { s.completedSessionQueue.Pop() } } }