socket_io.go 3.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177
  1. // License: GPLv3 Copyright: 2022, Kovid Goyal, <kovid at kovidgoyal.net>
  2. package at
  3. import (
  4. "bytes"
  5. "errors"
  6. "fmt"
  7. "io"
  8. "net"
  9. "os"
  10. "strconv"
  11. "time"
  12. "kitty/tools/tui/loop"
  13. "kitty/tools/utils"
  14. "kitty/tools/wcswidth"
  15. )
  16. var _ = fmt.Print
  17. func write_all_to_conn(conn *net.Conn, data []byte) error {
  18. for len(data) > 0 {
  19. n, err := (*conn).Write(data)
  20. if err != nil && errors.Is(err, io.ErrShortWrite) {
  21. err = nil
  22. }
  23. if err != nil {
  24. return err
  25. }
  26. data = data[n:]
  27. }
  28. return nil
  29. }
  30. func write_many_to_conn(conn *net.Conn, datums ...[]byte) error {
  31. for len(datums) > 0 {
  32. err := write_all_to_conn(conn, datums[0])
  33. if err != nil {
  34. return err
  35. }
  36. datums = datums[1:]
  37. }
  38. return nil
  39. }
  40. func read_response_from_conn(conn *net.Conn, timeout time.Duration) (serialized_response []byte, err error) {
  41. p := wcswidth.EscapeCodeParser{}
  42. keep_going := true
  43. p.HandleDCS = func(data []byte) error {
  44. if bytes.HasPrefix(data, []byte("@kitty-cmd")) {
  45. serialized_response = data[len("@kitty-cmd"):]
  46. keep_going = false
  47. }
  48. return nil
  49. }
  50. buf := make([]byte, utils.DEFAULT_IO_BUFFER_SIZE)
  51. for keep_going {
  52. var n int
  53. (*conn).SetDeadline(time.Now().Add(timeout))
  54. n, err = (*conn).Read(buf)
  55. if err != nil {
  56. keep_going = false
  57. break
  58. }
  59. p.Parse(buf[:n])
  60. }
  61. return
  62. }
  63. const cmd_escape_code_prefix = "\x1bP@kitty-cmd"
  64. const cmd_escape_code_suffix = "\x1b\\"
  65. func run_stdin_echo_loop(conn *net.Conn, io_data *rc_io_data) (err error) {
  66. lp, err := loop.New(loop.NoAlternateScreen, loop.NoRestoreColors)
  67. if err != nil {
  68. return
  69. }
  70. lp.OnKeyEvent = func(event *loop.KeyEvent) error {
  71. event.Handled = true
  72. err = io_data.on_key_event(lp, event)
  73. if err != nil {
  74. if err == end_reading_from_stdin {
  75. lp.Quit(0)
  76. return nil
  77. }
  78. return err
  79. }
  80. chunk, err := io_data.next_chunk()
  81. if err != nil {
  82. if err == waiting_on_stdin {
  83. return nil
  84. }
  85. return err
  86. }
  87. err = write_many_to_conn(conn, []byte(cmd_escape_code_prefix), chunk, []byte(cmd_escape_code_suffix))
  88. if err != nil {
  89. return err
  90. }
  91. return nil
  92. }
  93. err = lp.Run()
  94. if err == nil {
  95. lp.KillIfSignalled()
  96. }
  97. return err
  98. }
  99. func simple_socket_io(conn *net.Conn, io_data *rc_io_data) (serialized_response []byte, err error) {
  100. const (
  101. BEFORE_FIRST_ESCAPE_CODE_SENT = iota
  102. SENDING
  103. )
  104. state := BEFORE_FIRST_ESCAPE_CODE_SENT
  105. wants_streaming := io_data.rc.Stream
  106. for {
  107. var chunk []byte
  108. chunk, err = io_data.next_chunk()
  109. if err != nil {
  110. if err == waiting_on_stdin {
  111. err := run_stdin_echo_loop(conn, io_data)
  112. return make([]byte, 0), err
  113. }
  114. return
  115. }
  116. if len(chunk) == 0 {
  117. break
  118. }
  119. err = write_many_to_conn(conn, []byte(cmd_escape_code_prefix), chunk, []byte(cmd_escape_code_suffix))
  120. if err != nil {
  121. return
  122. }
  123. if state == BEFORE_FIRST_ESCAPE_CODE_SENT {
  124. if wants_streaming {
  125. var streaming_response []byte
  126. streaming_response, err = read_response_from_conn(conn, io_data.timeout)
  127. if err != nil {
  128. return
  129. }
  130. if !is_stream_response(streaming_response) {
  131. err = fmt.Errorf("Did not receive expected streaming response")
  132. return
  133. }
  134. }
  135. state = SENDING
  136. }
  137. }
  138. if io_data.rc.NoResponse {
  139. return
  140. }
  141. return read_response_from_conn(conn, io_data.timeout)
  142. }
  143. func do_socket_io(io_data *rc_io_data) (serialized_response []byte, err error) {
  144. var conn net.Conn
  145. if global_options.to_network == "fd" {
  146. fd, _ := strconv.Atoi(global_options.to_address)
  147. if err != nil {
  148. return nil, err
  149. }
  150. f := os.NewFile(uintptr(fd), "fd:"+global_options.to_address)
  151. conn, err = net.FileConn(f)
  152. if err != nil {
  153. return nil, err
  154. }
  155. defer f.Close()
  156. } else {
  157. conn, err = net.Dial(global_options.to_network, global_options.to_address)
  158. if err != nil {
  159. return
  160. }
  161. }
  162. defer conn.Close()
  163. return simple_socket_io(&conn, io_data)
  164. }