sing-box/experimental/libbox/command_log.go
2023-10-03 09:22:29 +08:00

132 lines
2.6 KiB
Go

package libbox
import (
"context"
"encoding/binary"
"io"
"net"
)
func (s *CommandServer) WriteMessage(message string) {
s.subscriber.Emit(message)
s.access.Lock()
s.savedLines.PushBack(message)
if s.savedLines.Len() > s.maxLines {
s.savedLines.Remove(s.savedLines.Front())
}
s.access.Unlock()
}
func readLog(reader io.Reader) ([]byte, error) {
var messageLength uint16
err := binary.Read(reader, binary.BigEndian, &messageLength)
if err != nil {
return nil, err
}
if messageLength == 0 {
return nil, nil
}
data := make([]byte, messageLength)
_, err = io.ReadFull(reader, data)
if err != nil {
return nil, err
}
return data, nil
}
func writeLog(writer io.Writer, message []byte) error {
err := binary.Write(writer, binary.BigEndian, uint8(0))
if err != nil {
return err
}
err = binary.Write(writer, binary.BigEndian, uint16(len(message)))
if err != nil {
return err
}
if len(message) > 0 {
_, err = writer.Write(message)
}
return err
}
func writeClearLog(writer io.Writer) error {
return binary.Write(writer, binary.BigEndian, uint8(1))
}
func (s *CommandServer) handleLogConn(conn net.Conn) error {
var savedLines []string
s.access.Lock()
savedLines = make([]string, 0, s.savedLines.Len())
for element := s.savedLines.Front(); element != nil; element = element.Next() {
savedLines = append(savedLines, element.Value)
}
s.access.Unlock()
subscription, done, err := s.observer.Subscribe()
if err != nil {
return err
}
defer s.observer.UnSubscribe(subscription)
for _, line := range savedLines {
err = writeLog(conn, []byte(line))
if err != nil {
return err
}
}
ctx := connKeepAlive(conn)
for {
select {
case <-ctx.Done():
return ctx.Err()
case message := <-subscription:
err = writeLog(conn, []byte(message))
if err != nil {
return err
}
case <-s.logReset:
err = writeClearLog(conn)
if err != nil {
return err
}
case <-done:
return nil
}
}
}
func (c *CommandClient) handleLogConn(conn net.Conn) {
for {
var messageType uint8
err := binary.Read(conn, binary.BigEndian, &messageType)
if err != nil {
c.handler.Disconnected(err.Error())
return
}
var message []byte
switch messageType {
case 0:
message, err = readLog(conn)
if err != nil {
c.handler.Disconnected(err.Error())
return
}
c.handler.WriteLog(string(message))
case 1:
c.handler.ClearLog()
}
}
}
func connKeepAlive(reader io.Reader) context.Context {
ctx, cancel := context.WithCancelCause(context.Background())
go func() {
for {
_, err := readLog(reader)
if err != nil {
cancel(err)
return
}
}
}()
return ctx
}