client.go 4.9 KB

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