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  }