client.go 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230
  1. package cli
  2. import (
  3. "bytes"
  4. "context"
  5. "encoding/json"
  6. "errors"
  7. "fmt"
  8. "git.nspix.com/golang/micro/helper/net/ip"
  9. byte2 "git.nspix.com/golang/micro/helper/pool/byte"
  10. "git.nspix.com/golang/micro/helper/unsafestr"
  11. "github.com/peterh/liner"
  12. "net"
  13. "os"
  14. "runtime"
  15. "strings"
  16. "time"
  17. )
  18. type Client struct {
  19. appName string
  20. conn net.Conn
  21. context context.Context
  22. readyChan chan struct{}
  23. completerChan chan *Frame
  24. responseChan chan *Frame
  25. }
  26. func (client *Client) writePack(packet *Frame) (err error) {
  27. if packet.Timestamp == 0 {
  28. packet.Timestamp = time.Now().Unix()
  29. }
  30. err = writeFrame(client.conn, packet)
  31. return
  32. }
  33. func (client *Client) stdout(args ...interface{}) {
  34. fmt.Fprint(os.Stdout, args...)
  35. }
  36. func (client *Client) rdyLoop(c chan error) {
  37. var (
  38. err error
  39. buf []byte
  40. )
  41. buf = byte2.Get(MaxReadBufferLength)
  42. defer func() {
  43. byte2.Put(buf)
  44. }()
  45. for {
  46. packet := &Frame{}
  47. if packet, err = readFrame(client.conn); err != nil {
  48. c <- err
  49. break
  50. }
  51. switch packet.Type {
  52. case PacketTypeCompleter:
  53. client.completerChan <- packet
  54. case PacketTypeEcho:
  55. vs := make(map[string]string)
  56. if err = json.Unmarshal(packet.Data, &vs); err == nil {
  57. time.Sleep(time.Millisecond * 200)
  58. client.stdout("\u001B[2K")
  59. client.stdout("\u001B[0G")
  60. client.appName = vs["name"]
  61. client.stdout(fmt.Sprintf("Welcome to the %s monitor.", vs["name"]), EOL)
  62. client.stdout(fmt.Sprintf("Server Version: %s, os: %s, ip: %s", vs["version"], runtime.GOOS, ip.InternalIP()), EOL)
  63. client.stdout(fmt.Sprintf("Last login: %s from %s@%s", vs["login_at"], vs["client_addr"], vs["cid"]), EOL)
  64. client.stdout("Type 'help;' for help. Type 'cls' to clear the current input statement.", EOL)
  65. select {
  66. case client.readyChan <- struct{}{}:
  67. default:
  68. }
  69. }
  70. case PacketTypeData:
  71. client.responseChan <- packet
  72. }
  73. }
  74. select {
  75. case c <- err:
  76. case <-client.context.Done():
  77. }
  78. }
  79. func (client *Client) interactive(c chan error) {
  80. var (
  81. err error
  82. line string
  83. state *liner.State
  84. )
  85. state = liner.NewLiner()
  86. state.SetCompleter(client.CompleterHandleFunc)
  87. select {
  88. case <-client.readyChan:
  89. case <-client.context.Done():
  90. return
  91. }
  92. defer func() {
  93. _ = state.Close()
  94. c <- err
  95. }()
  96. for {
  97. if line, err = state.Prompt(client.appName + "> "); err != nil {
  98. break
  99. }
  100. line = strings.TrimSpace(line)
  101. if line == "" {
  102. continue
  103. }
  104. if strings.ToLower(line) == "exit" || strings.ToLower(line) == "quit" {
  105. client.stdout("Bye Bye", EOL)
  106. return
  107. }
  108. if strings.ToLower(line) == "clear" || strings.ToLower(line) == "cls" {
  109. client.stdout("\033[2J")
  110. continue
  111. }
  112. if err = client.writePack(&Frame{Type: PacketTypeData, Data: []byte(line)}); err != nil {
  113. break
  114. }
  115. state.AppendHistory(line)
  116. select {
  117. case <-client.context.Done():
  118. err = client.context.Err()
  119. return
  120. case <-time.After(time.Second * 30):
  121. client.stdout("io timeout", EOL)
  122. case res, ok := <-client.responseChan:
  123. if !ok {
  124. break
  125. }
  126. if res.Error != "" {
  127. client.stdout(res.Error, EOL)
  128. } else {
  129. client.stdout(string(bytes.Trim(res.Data, "\r\n")), EOL)
  130. }
  131. }
  132. }
  133. }
  134. func (client *Client) CompleterHandleFunc(str string) (ss []string) {
  135. var err error
  136. if err = client.writePack(&Frame{
  137. Type: PacketTypeCompleter,
  138. Data: unsafestr.StringToBytes(str),
  139. }); err != nil {
  140. return
  141. }
  142. select {
  143. case <-time.After(time.Second * 5):
  144. return nil
  145. case <-client.context.Done():
  146. return nil
  147. case resp, ok := <-client.completerChan:
  148. if ok {
  149. ss = make([]string, 0)
  150. err = json.Unmarshal(resp.Data, &ss)
  151. }
  152. }
  153. return
  154. }
  155. func (client *Client) Run() (err error) {
  156. var (
  157. errChan chan error
  158. )
  159. errChan = make(chan error)
  160. defer func() {
  161. _ = client.conn.Close()
  162. }()
  163. if err = client.writePack(&Frame{
  164. Type: PacketTypeEcho,
  165. }); err != nil {
  166. return
  167. }
  168. client.stdout("connecting")
  169. go client.rdyLoop(errChan)
  170. go client.interactive(errChan)
  171. err = <-errChan
  172. return
  173. }
  174. func ExecuteContext(ctx context.Context, conn net.Conn, cmd string) (s string, err error) {
  175. if ctx == nil {
  176. ctx = context.Background()
  177. }
  178. cli := &Client{
  179. conn: conn,
  180. context: ctx,
  181. readyChan: make(chan struct{}),
  182. completerChan: make(chan *Frame),
  183. responseChan: make(chan *Frame, 1),
  184. }
  185. if err = cli.writePack(&Frame{
  186. Type: PacketTypeEcho,
  187. }); err != nil {
  188. return
  189. }
  190. frame := &Frame{}
  191. if frame, err = readFrame(conn); err != nil {
  192. return
  193. }
  194. if err = cli.writePack(&Frame{Type: PacketTypeData, Data: []byte(cmd)}); err != nil {
  195. return
  196. }
  197. if frame, err = readFrame(conn); err != nil {
  198. return
  199. }
  200. if frame.Error != "" {
  201. err = errors.New(frame.Error)
  202. } else {
  203. s = string(frame.Data)
  204. }
  205. return
  206. }
  207. func OpenInteractive(ctx context.Context, conn net.Conn) (err error) {
  208. if ctx == nil {
  209. ctx = context.Background()
  210. }
  211. cli := &Client{
  212. conn: conn,
  213. context: ctx,
  214. readyChan: make(chan struct{}),
  215. completerChan: make(chan *Frame),
  216. responseChan: make(chan *Frame, 1),
  217. }
  218. err = cli.Run()
  219. return
  220. }