github.com/inazumav/sing-box@v0.0.0-20230926072359-ab51429a14f1/experimental/libbox/command_log.go (about) 1 package libbox 2 3 import ( 4 "context" 5 "encoding/binary" 6 "io" 7 "net" 8 ) 9 10 func (s *CommandServer) WriteMessage(message string) { 11 s.subscriber.Emit(message) 12 s.access.Lock() 13 s.savedLines.PushBack(message) 14 if s.savedLines.Len() > s.maxLines { 15 s.savedLines.Remove(s.savedLines.Front()) 16 } 17 s.access.Unlock() 18 } 19 20 func readLog(reader io.Reader) ([]byte, error) { 21 var messageLength uint16 22 err := binary.Read(reader, binary.BigEndian, &messageLength) 23 if err != nil { 24 return nil, err 25 } 26 data := make([]byte, messageLength) 27 _, err = io.ReadFull(reader, data) 28 if err != nil { 29 return nil, err 30 } 31 return data, nil 32 } 33 34 func writeLog(writer io.Writer, message []byte) error { 35 err := binary.Write(writer, binary.BigEndian, uint16(len(message))) 36 if err != nil { 37 return err 38 } 39 _, err = writer.Write(message) 40 return err 41 } 42 43 func (s *CommandServer) handleLogConn(conn net.Conn) error { 44 var savedLines []string 45 s.access.Lock() 46 savedLines = make([]string, 0, s.savedLines.Len()) 47 for element := s.savedLines.Front(); element != nil; element = element.Next() { 48 savedLines = append(savedLines, element.Value) 49 } 50 s.access.Unlock() 51 subscription, done, err := s.observer.Subscribe() 52 if err != nil { 53 return err 54 } 55 defer s.observer.UnSubscribe(subscription) 56 for _, line := range savedLines { 57 err = writeLog(conn, []byte(line)) 58 if err != nil { 59 return err 60 } 61 } 62 ctx := connKeepAlive(conn) 63 for { 64 select { 65 case <-ctx.Done(): 66 return ctx.Err() 67 case message := <-subscription: 68 err = writeLog(conn, []byte(message)) 69 if err != nil { 70 return err 71 } 72 case <-done: 73 return nil 74 } 75 } 76 } 77 78 func (c *CommandClient) handleLogConn(conn net.Conn) { 79 for { 80 message, err := readLog(conn) 81 if err != nil { 82 c.handler.Disconnected(err.Error()) 83 return 84 } 85 c.handler.WriteLog(string(message)) 86 } 87 } 88 89 func connKeepAlive(reader io.Reader) context.Context { 90 ctx, cancel := context.WithCancelCause(context.Background()) 91 go func() { 92 for { 93 _, err := readLog(reader) 94 if err != nil { 95 cancel(err) 96 return 97 } 98 } 99 }() 100 return ctx 101 }