pipeline.go 3.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166
  1. package pty
  2. import (
  3. "encoding/json"
  4. "github.com/0xJacky/Nginx-UI/internal/helper"
  5. "github.com/0xJacky/Nginx-UI/settings"
  6. "github.com/creack/pty"
  7. "github.com/gorilla/websocket"
  8. "github.com/pkg/errors"
  9. "github.com/uozi-tech/cosy/logger"
  10. "os"
  11. "os/exec"
  12. "time"
  13. "unicode/utf8"
  14. )
  15. type Pipeline struct {
  16. Pty *os.File
  17. cmd *exec.Cmd
  18. ws *websocket.Conn
  19. }
  20. type Message struct {
  21. Type MsgType
  22. Data json.RawMessage
  23. }
  24. const bufferSize = 2048
  25. func NewPipeLine(conn *websocket.Conn) (p *Pipeline, err error) {
  26. c := exec.Command(settings.TerminalSettings.StartCmd)
  27. ptmx, err := pty.StartWithSize(c, &pty.Winsize{Cols: 90, Rows: 60})
  28. if err != nil {
  29. return nil, errors.Wrap(err, "start pty error")
  30. }
  31. p = &Pipeline{
  32. Pty: ptmx,
  33. cmd: c,
  34. ws: conn,
  35. }
  36. return
  37. }
  38. func (p *Pipeline) ReadWsAndWritePty(errorChan chan error) {
  39. for {
  40. msgType, payload, err := p.ws.ReadMessage()
  41. if err != nil {
  42. if helper.IsUnexpectedWebsocketError(err) {
  43. errorChan <- errors.Wrap(err, "Error ReadWsAndWritePty unexpected close")
  44. }
  45. return
  46. }
  47. if msgType != websocket.TextMessage {
  48. errorChan <- errors.Errorf("Error ReadWsAndWritePty Invalid msgType: %v", msgType)
  49. return
  50. }
  51. var msg Message
  52. err = json.Unmarshal(payload, &msg)
  53. if err != nil {
  54. errorChan <- errors.Wrap(err, "Error ReadWsAndWritePty json.Unmarshal")
  55. return
  56. }
  57. switch msg.Type {
  58. case TypeData:
  59. var data string
  60. err = json.Unmarshal(msg.Data, &data)
  61. if err != nil {
  62. errorChan <- errors.Wrap(err, "Error ReadWsAndWritePty json.Unmarshal msg.Data")
  63. return
  64. }
  65. _, err = p.Pty.Write([]byte(data))
  66. if err != nil {
  67. errorChan <- errors.Wrap(err, "Error ReadWsAndWritePty write pty")
  68. return
  69. }
  70. case TypeResize:
  71. var win struct {
  72. Cols uint16
  73. Rows uint16
  74. }
  75. err = json.Unmarshal(msg.Data, &win)
  76. if err != nil {
  77. errorChan <- errors.Wrap(err, "Error ReadSktAndWritePty Invalid resize message")
  78. return
  79. }
  80. err = pty.Setsize(p.Pty, &pty.Winsize{Rows: win.Rows, Cols: win.Cols})
  81. if err != nil {
  82. errorChan <- errors.Wrap(err, "Error ReadSktAndWritePty set pty size")
  83. return
  84. }
  85. case TypePing:
  86. err = p.ws.WriteControl(websocket.PongMessage, []byte{}, time.Now().Add(time.Second))
  87. if err != nil {
  88. errorChan <- errors.Wrap(err, "Error ReadSktAndWritePty write pong")
  89. return
  90. }
  91. default:
  92. errorChan <- errors.Errorf("Error ReadWsAndWritePty unknown msg.Type %v", msg.Type)
  93. return
  94. }
  95. }
  96. }
  97. func (p *Pipeline) ReadPtyAndWriteWs(errorChan chan error) {
  98. buf := make([]byte, bufferSize)
  99. for {
  100. n, err := p.Pty.Read(buf)
  101. if err != nil {
  102. errorChan <- errors.Wrap(err, "Error ReadPtyAndWriteWs read pty")
  103. return
  104. }
  105. processedOutput := validString(string(buf[:n]))
  106. err = p.ws.WriteMessage(websocket.TextMessage, []byte(processedOutput))
  107. if err != nil {
  108. if helper.IsUnexpectedWebsocketError(err) {
  109. errorChan <- errors.Wrap(err, "Error ReadPtyAndWriteWs websocket write")
  110. }
  111. return
  112. }
  113. }
  114. }
  115. func (p *Pipeline) Close() {
  116. err := p.Pty.Close()
  117. if err != nil {
  118. logger.Error(err)
  119. }
  120. err = p.cmd.Process.Kill()
  121. if err != nil {
  122. logger.Error(err)
  123. }
  124. _, err = p.cmd.Process.Wait()
  125. if err != nil {
  126. logger.Error(err)
  127. }
  128. }
  129. func validString(s string) string {
  130. if !utf8.ValidString(s) {
  131. v := make([]rune, 0, len(s))
  132. for i, r := range s {
  133. if r == utf8.RuneError {
  134. _, size := utf8.DecodeRuneInString(s[i:])
  135. if size == 1 {
  136. continue
  137. }
  138. }
  139. v = append(v, r)
  140. }
  141. s = string(v)
  142. }
  143. return s
  144. }