1 #!/usr/bin/env python 2 3 import argparse 4 import code 5 import sys 6 import threading 7 import time 8 9 import six 10 from six.moves.urllib.parse import urlparse 11 12 import websocket 13 14 try: 15 import readline 16 except ImportError: 17 pass 18 19 20 def get_encoding(): 21 encoding = getattr(sys.stdin, "encoding", "") 22 if not encoding: 23 return "utf-8" 24 else: 25 return encoding.lower() 26 27 28 OPCODE_DATA = (websocket.ABNF.OPCODE_TEXT, websocket.ABNF.OPCODE_BINARY) 29 ENCODING = get_encoding() 30 31 32 class VAction(argparse.Action): 33 34 def __call__(self, parser, args, values, option_string=None): 35 if values is None: 36 values = "1" 37 try: 38 values = int(values) 39 except ValueError: 40 values = values.count("v") + 1 41 setattr(args, self.dest, values) 42 43 44 def parse_args(): 45 parser = argparse.ArgumentParser(description="WebSocket Simple Dump Tool") 46 parser.add_argument("url", metavar="ws_url", 47 help="websocket url. ex. ws://echo.websocket.org/") 48 parser.add_argument("-p", "--proxy", 49 help="proxy url. ex. http://127.0.0.1:8080") 50 parser.add_argument("-v", "--verbose", default=0, nargs='?', action=VAction, 51 dest="verbose", 52 help="set verbose mode. If set to 1, show opcode. " 53 "If set to 2, enable to trace websocket module") 54 parser.add_argument("-n", "--nocert", action='store_true', 55 help="Ignore invalid SSL cert") 56 parser.add_argument("-r", "--raw", action="store_true", 57 help="raw output") 58 parser.add_argument("-s", "--subprotocols", nargs='*', 59 help="Set subprotocols") 60 parser.add_argument("-o", "--origin", 61 help="Set origin") 62 parser.add_argument("--eof-wait", default=0, type=int, 63 help="wait time(second) after 'EOF' received.") 64 parser.add_argument("-t", "--text", 65 help="Send initial text") 66 parser.add_argument("--timings", action="store_true", 67 help="Print timings in seconds") 68 parser.add_argument("--headers", 69 help="Set custom headers. Use ',' as separator") 70 71 return parser.parse_args() 72 73 74 class RawInput: 75 76 def raw_input(self, prompt): 77 if six.PY3: 78 line = input(prompt) 79 else: 80 line = raw_input(prompt) 81 82 if ENCODING and ENCODING != "utf-8" and not isinstance(line, six.text_type): 83 line = line.decode(ENCODING).encode("utf-8") 84 elif isinstance(line, six.text_type): 85 line = line.encode("utf-8") 86 87 return line 88 89 90 class InteractiveConsole(RawInput, code.InteractiveConsole): 91 92 def write(self, data): 93 sys.stdout.write("\033[2K\033[E") 94 # sys.stdout.write("\n") 95 sys.stdout.write("\033[34m< " + data + "\033[39m") 96 sys.stdout.write("\n> ") 97 sys.stdout.flush() 98 99 def read(self): 100 return self.raw_input("> ") 101 102 103 class NonInteractive(RawInput): 104 105 def write(self, data): 106 sys.stdout.write(data) 107 sys.stdout.write("\n") 108 sys.stdout.flush() 109 110 def read(self): 111 return self.raw_input("") 112 113 114 def main(): 115 start_time = time.time() 116 args = parse_args() 117 if args.verbose > 1: 118 websocket.enableTrace(True) 119 options = {} 120 if args.proxy: 121 p = urlparse(args.proxy) 122 options["http_proxy_host"] = p.hostname 123 options["http_proxy_port"] = p.port 124 if args.origin: 125 options["origin"] = args.origin 126 if args.subprotocols: 127 options["subprotocols"] = args.subprotocols 128 opts = {} 129 if args.nocert: 130 opts = {"cert_reqs": websocket.ssl.CERT_NONE, "check_hostname": False} 131 if args.headers: 132 options['header'] = map(str.strip, args.headers.split(',')) 133 ws = websocket.create_connection(args.url, sslopt=opts, **options) 134 if args.raw: 135 console = NonInteractive() 136 else: 137 console = InteractiveConsole() 138 print("Press Ctrl+C to quit") 139 140 def recv(): 141 try: 142 frame = ws.recv_frame() 143 except websocket.WebSocketException: 144 return websocket.ABNF.OPCODE_CLOSE, None 145 if not frame: 146 raise websocket.WebSocketException("Not a valid frame %s" % frame) 147 elif frame.opcode in OPCODE_DATA: 148 return frame.opcode, frame.data 149 elif frame.opcode == websocket.ABNF.OPCODE_CLOSE: 150 ws.send_close() 151 return frame.opcode, None 152 elif frame.opcode == websocket.ABNF.OPCODE_PING: 153 ws.pong(frame.data) 154 return frame.opcode, frame.data 155 156 return frame.opcode, frame.data 157 158 def recv_ws(): 159 while True: 160 opcode, data = recv() 161 msg = None 162 if six.PY3 and opcode == websocket.ABNF.OPCODE_TEXT and isinstance(data, bytes): 163 data = str(data, "utf-8") 164 if not args.verbose and opcode in OPCODE_DATA: 165 msg = data 166 elif args.verbose: 167 msg = "%s: %s" % (websocket.ABNF.OPCODE_MAP.get(opcode), data) 168 169 if msg is not None: 170 if args.timings: 171 console.write(str(time.time() - start_time) + ": " + msg) 172 else: 173 console.write(msg) 174 175 if opcode == websocket.ABNF.OPCODE_CLOSE: 176 break 177 178 thread = threading.Thread(target=recv_ws) 179 thread.daemon = True 180 thread.start() 181 182 if args.text: 183 ws.send(args.text) 184 185 while True: 186 try: 187 message = console.read() 188 ws.send(message) 189 except KeyboardInterrupt: 190 return 191 except EOFError: 192 time.sleep(args.eof_wait) 193 return 194 195 196 if __name__ == "__main__": 197 try: 198 main() 199 except Exception as e: 200 print(e) 201