github.com/sagernet/sing-box@v1.9.0-rc.20/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  	if messageLength == 0 {
    27  		return nil, nil
    28  	}
    29  	data := make([]byte, messageLength)
    30  	_, err = io.ReadFull(reader, data)
    31  	if err != nil {
    32  		return nil, err
    33  	}
    34  	return data, nil
    35  }
    36  
    37  func writeLog(writer io.Writer, message []byte) error {
    38  	err := binary.Write(writer, binary.BigEndian, uint8(0))
    39  	if err != nil {
    40  		return err
    41  	}
    42  	err = binary.Write(writer, binary.BigEndian, uint16(len(message)))
    43  	if err != nil {
    44  		return err
    45  	}
    46  	if len(message) > 0 {
    47  		_, err = writer.Write(message)
    48  	}
    49  	return err
    50  }
    51  
    52  func writeClearLog(writer io.Writer) error {
    53  	return binary.Write(writer, binary.BigEndian, uint8(1))
    54  }
    55  
    56  func (s *CommandServer) handleLogConn(conn net.Conn) error {
    57  	var savedLines []string
    58  	s.access.Lock()
    59  	savedLines = make([]string, 0, s.savedLines.Len())
    60  	for element := s.savedLines.Front(); element != nil; element = element.Next() {
    61  		savedLines = append(savedLines, element.Value)
    62  	}
    63  	s.access.Unlock()
    64  	subscription, done, err := s.observer.Subscribe()
    65  	if err != nil {
    66  		return err
    67  	}
    68  	defer s.observer.UnSubscribe(subscription)
    69  	for _, line := range savedLines {
    70  		err = writeLog(conn, []byte(line))
    71  		if err != nil {
    72  			return err
    73  		}
    74  	}
    75  	ctx := connKeepAlive(conn)
    76  	for {
    77  		select {
    78  		case <-ctx.Done():
    79  			return ctx.Err()
    80  		case message := <-subscription:
    81  			err = writeLog(conn, []byte(message))
    82  			if err != nil {
    83  				return err
    84  			}
    85  		case <-s.logReset:
    86  			err = writeClearLog(conn)
    87  			if err != nil {
    88  				return err
    89  			}
    90  		case <-done:
    91  			return nil
    92  		}
    93  	}
    94  }
    95  
    96  func (c *CommandClient) handleLogConn(conn net.Conn) {
    97  	for {
    98  		var messageType uint8
    99  		err := binary.Read(conn, binary.BigEndian, &messageType)
   100  		if err != nil {
   101  			c.handler.Disconnected(err.Error())
   102  			return
   103  		}
   104  		var message []byte
   105  		switch messageType {
   106  		case 0:
   107  			message, err = readLog(conn)
   108  			if err != nil {
   109  				c.handler.Disconnected(err.Error())
   110  				return
   111  			}
   112  			c.handler.WriteLog(string(message))
   113  		case 1:
   114  			c.handler.ClearLog()
   115  		}
   116  	}
   117  }
   118  
   119  func connKeepAlive(reader io.Reader) context.Context {
   120  	ctx, cancel := context.WithCancelCause(context.Background())
   121  	go func() {
   122  		for {
   123  			_, err := readLog(reader)
   124  			if err != nil {
   125  				cancel(err)
   126  				return
   127  			}
   128  		}
   129  	}()
   130  	return ctx
   131  }