1
0

pipeline.go 3.2 KB

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