socketclient.py 3.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130
  1. from __future__ import print_function
  2. # Adapted from https://github.com/benthor/remotty/blob/master/socketclient.py
  3. from select import select
  4. import sys
  5. import tty
  6. import fcntl
  7. import os
  8. import termios
  9. import threading
  10. import errno
  11. import logging
  12. log = logging.getLogger(__name__)
  13. class SocketClient:
  14. def __init__(self,
  15. socket_in=None,
  16. socket_out=None,
  17. socket_err=None,
  18. raw=True,
  19. ):
  20. self.socket_in = socket_in
  21. self.socket_out = socket_out
  22. self.socket_err = socket_err
  23. self.raw = raw
  24. self.stdin_fileno = sys.stdin.fileno()
  25. def __enter__(self):
  26. self.create()
  27. return self
  28. def __exit__(self, type, value, trace):
  29. self.destroy()
  30. def create(self):
  31. if os.isatty(sys.stdin.fileno()):
  32. self.settings = termios.tcgetattr(sys.stdin.fileno())
  33. else:
  34. self.settings = None
  35. if self.socket_in is not None:
  36. self.set_blocking(sys.stdin, False)
  37. self.set_blocking(sys.stdout, True)
  38. self.set_blocking(sys.stderr, True)
  39. if self.raw:
  40. tty.setraw(sys.stdin.fileno())
  41. def set_blocking(self, file, blocking):
  42. fd = file.fileno()
  43. flags = fcntl.fcntl(fd, fcntl.F_GETFL)
  44. flags = (flags & ~os.O_NONBLOCK) if blocking else (flags | os.O_NONBLOCK)
  45. fcntl.fcntl(fd, fcntl.F_SETFL, flags)
  46. def run(self):
  47. if self.socket_in is not None:
  48. self.start_background_thread(target=self.send_ws, args=(self.socket_in, sys.stdin))
  49. recv_threads = []
  50. if self.socket_out is not None:
  51. recv_threads.append(self.start_background_thread(target=self.recv_ws, args=(self.socket_out, sys.stdout)))
  52. if self.socket_err is not None:
  53. recv_threads.append(self.start_background_thread(target=self.recv_ws, args=(self.socket_err, sys.stderr)))
  54. for t in recv_threads:
  55. t.join()
  56. def start_background_thread(self, **kwargs):
  57. thread = threading.Thread(**kwargs)
  58. thread.daemon = True
  59. thread.start()
  60. return thread
  61. def recv_ws(self, socket, stream):
  62. try:
  63. while True:
  64. chunk = socket.recv()
  65. if chunk:
  66. stream.write(chunk)
  67. stream.flush()
  68. else:
  69. break
  70. except Exception as e:
  71. log.debug(e)
  72. def send_ws(self, socket, stream):
  73. while True:
  74. r, w, e = select([stream.fileno()], [], [])
  75. if r:
  76. chunk = stream.read(1)
  77. if chunk == '':
  78. socket.send_close()
  79. break
  80. else:
  81. try:
  82. socket.send(chunk)
  83. except Exception as e:
  84. if hasattr(e, 'errno') and e.errno == errno.EPIPE:
  85. break
  86. else:
  87. raise e
  88. def destroy(self):
  89. if self.settings is not None:
  90. termios.tcsetattr(self.stdin_fileno, termios.TCSADRAIN, self.settings)
  91. sys.stdout.flush()
  92. if __name__ == '__main__':
  93. import websocket
  94. if len(sys.argv) != 2:
  95. sys.stderr.write("Usage: python socketclient.py WEBSOCKET_URL\n")
  96. exit(1)
  97. url = sys.argv[1]
  98. socket = websocket.create_connection(url)
  99. print("connected\r")
  100. with SocketClient(socket, interactive=True) as client:
  101. client.run()