-
Notifications
You must be signed in to change notification settings - Fork 0
/
debug.go
112 lines (103 loc) · 2.76 KB
/
debug.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
package debug
import (
"bufio"
"fmt"
"net"
"reflect"
"github.com/ethereum/go-ethereum/log"
"github.com/status-im/status-go/api"
)
const (
// CLIPort is the CLI port.
CLIPort = "51515"
)
// Server provides a debug server receiving line based commands from
// a CLI via the debugging port and executing those on the Status API
// using reflection. The returned values will be rendered as
// string and returned to the CLI.
type Server struct {
commandSetValue reflect.Value
listener net.Listener
log log.Logger
}
// New creates a debug server using the passed Status API.
// It also starts the server.
func New(statusBackend *api.StatusBackend, port string) (*Server, error) {
listener, err := net.Listen("tcp", fmt.Sprintf(":%s", port)) // nolint
if err != nil {
return nil, err
}
s := Server{
commandSetValue: reflect.ValueOf(newCommandSet(statusBackend)),
listener: listener,
log: log.New("package", "status-go/cmd/statusd/debug.Server"),
}
go s.backend()
return &s, nil
}
// backend receives the commands and executes them on
// the Status API.
func (s *Server) backend() {
for {
conn, err := s.listener.Accept()
if err != nil {
s.log.Error("cannot establish debug connection", "error", err)
continue
}
go s.handleConnection(conn)
}
}
// handleConnection handles all commands of one connection.
func (s *Server) handleConnection(conn net.Conn) {
reader := bufio.NewReader(conn)
writer := bufio.NewWriter(conn)
defer func() {
if err := conn.Close(); err != nil {
s.log.Error("error while closing debug connection", "error", err)
}
}()
// Read, execute, and respond commands of a session.
for {
var (
replies []string
err error
)
command, err := s.readCommandLine(reader)
if err != nil {
replies = []string{fmt.Sprintf("cannot read command: %v", err)}
} else {
replies, err = command.execute(s.commandSetValue)
if err != nil {
replies = []string{fmt.Sprintf("cannot execute command: %v", err)}
}
}
err = s.writeReplies(writer, replies)
if err != nil {
s.log.Error("cannot write replies", "error", err)
return
}
}
}
// readCommandLine receives a command line via network and
// parses it into an executable command.
func (s *Server) readCommandLine(reader *bufio.Reader) (*command, error) {
commandLine, err := reader.ReadString('\n')
if err != nil {
return nil, err
}
return newCommand(commandLine)
}
// writeReplies sends the replies back to the CLI.
func (s *Server) writeReplies(writer *bufio.Writer, replies []string) error {
_, err := fmt.Fprintf(writer, "%d\n", len(replies))
if err != nil {
return err
}
for i, reply := range replies {
_, err = fmt.Fprintf(writer, "[%d] %s\n", i, reply)
if err != nil {
return err
}
}
return writer.Flush()
}