def twebsocketflow(client_conn=True, server_conn=True, messages=True, err=None, handshake_flow=True): if client_conn is True: client_conn = tclient_conn() if server_conn is True: server_conn = tserver_conn() if handshake_flow is True: req = http.HTTPRequest("relative", "GET", "http", "example.com", "80", "/ws", "HTTP/1.1", headers=net_http.Headers( connection="upgrade", upgrade="websocket", sec_websocket_version="13", sec_websocket_key="1234", ), content=b'') resp = http.HTTPResponse( "HTTP/1.1", 101, reason=net_http.status_codes.RESPONSES.get(101), headers=net_http.Headers( connection='upgrade', upgrade='websocket', sec_websocket_accept=b'', ), content=b'', ) handshake_flow = http.HTTPFlow(client_conn, server_conn) handshake_flow.request = req handshake_flow.response = resp f = websocket.WebSocketFlow(client_conn, server_conn, handshake_flow) handshake_flow.metadata['websocket_flow'] = f if messages is True: messages = [ websocket.WebSocketMessage(websockets.OPCODE.BINARY, True, b"hello binary"), websocket.WebSocketMessage(websockets.OPCODE.TEXT, True, "hello text".encode()), websocket.WebSocketMessage(websockets.OPCODE.TEXT, False, "it's me".encode()), ] if err is True: err = terr() f.messages = messages f.error = err f.reply = controller.DummyReply() return f
def twebsocketflow(messages=True, err=None) -> http.HTTPFlow: flow = http.HTTPFlow(tclient_conn(), tserver_conn()) flow.request = http.Request( "example.com", 80, b"GET", b"http", b"example.com", b"/ws", b"HTTP/1.1", headers=http.Headers( connection="upgrade", upgrade="websocket", sec_websocket_version="13", sec_websocket_key="1234", ), content=b'', trailers=None, timestamp_start=946681200, timestamp_end=946681201, ) flow.response = http.Response( b"HTTP/1.1", 101, reason=b"Switching Protocols", headers=http.Headers( connection='upgrade', upgrade='websocket', sec_websocket_accept=b'', ), content=b'', trailers=None, timestamp_start=946681202, timestamp_end=946681203, ) flow.websocket = websocket.WebSocketData() if messages is True: flow.websocket.messages = [ websocket.WebSocketMessage(Opcode.BINARY, True, b"hello binary", 946681203), websocket.WebSocketMessage(Opcode.TEXT, True, b"hello text", 946681204), websocket.WebSocketMessage(Opcode.TEXT, False, b"it's me", 946681205), ] if err is True: flow.error = terr() flow.reply = controller.DummyReply() return flow
def test_text(self): txt = websocket.WebSocketMessage(Opcode.TEXT, True, b"foo") bin = websocket.WebSocketMessage(Opcode.BINARY, True, b"foo") assert txt.is_text assert txt.text == "foo" txt.text = "bar" assert txt.content == b"bar" assert not bin.is_text with pytest.raises(AttributeError, match="do not have a 'text' attribute."): _ = bin.text with pytest.raises(AttributeError, match="do not have a 'text' attribute."): bin.text = "bar"
def twebsocket(messages: bool = True) -> websocket.WebSocketData: ws = websocket.WebSocketData() if messages: ws.messages = [ websocket.WebSocketMessage(Opcode.BINARY, True, b"hello binary", 946681203), websocket.WebSocketMessage(Opcode.TEXT, True, b"hello text", 946681204), websocket.WebSocketMessage(Opcode.TEXT, False, b"it's me", 946681205), ] ws.close_reason = "Close Reason" ws.close_code = 1000 ws.closed_by_client = False ws.timestamp_end = 946681205 return ws
def test_basic(self): m = websocket.WebSocketMessage(Opcode.TEXT, True, b"foo") m.set_state(m.get_state()) assert m.content == b"foo" assert repr(m) == "'foo'" m.type = Opcode.BINARY assert repr(m) == "b'foo'" assert not m.killed m.kill() assert m.killed
def inject_websocket(self, flow: Flow, to_client: bool, message: bytes, is_text: bool = True): if not isinstance(flow, http.HTTPFlow) or not flow.websocket: ctx.log.warn("Cannot inject WebSocket messages into non-WebSocket flows.") msg = websocket.WebSocketMessage( Opcode.TEXT if is_text else Opcode.BINARY, not to_client, message ) event = WebSocketMessageInjected(flow, msg) try: self.inject_event(event) except ValueError as e: ctx.log.warn(str(e))
def relay_messages( self, event: events.ConnectionEvent) -> layer.CommandGenerator[None]: from_client = event.connection == self.context.client from_str = 'client' if from_client else 'server' if from_client: src_ws = self.client_ws dst_ws = self.server_ws else: src_ws = self.server_ws dst_ws = self.client_ws if isinstance(event, events.DataReceived): src_ws.receive_data(event.data) elif isinstance(event, events.ConnectionClosed): src_ws.receive_data(None) else: # pragma: no cover raise AssertionError(f"Unexpected event: {event}") for ws_event in src_ws.events(): if isinstance(ws_event, wsproto.events.Message): src_ws.frame_buf.append(ws_event.data) if ws_event.message_finished: if isinstance(ws_event, wsproto.events.TextMessage): frame_type = Opcode.TEXT content = "".join(src_ws.frame_buf) # type: ignore else: frame_type = Opcode.BINARY content = b"".join(src_ws.frame_buf) # type: ignore fragmentizer = Fragmentizer(src_ws.frame_buf) src_ws.frame_buf.clear() message = websocket.WebSocketMessage( frame_type, from_client, content) self.flow.messages.append(message) yield WebsocketMessageHook(self.flow) assert not message.killed # this is deprecated, instead we should have .content set to emptystr. for msg in fragmentizer(message.content): yield dst_ws.send2(msg) elif isinstance(ws_event, (wsproto.events.Ping, wsproto.events.Pong)): yield commands.Log( f"Received WebSocket {ws_event.__class__.__name__.lower()} from {from_str} " f"(payload: {bytes(ws_event.payload)!r})") yield dst_ws.send2(ws_event) elif isinstance(ws_event, wsproto.events.CloseConnection): self.flow.close_sender = from_str self.flow.close_code = ws_event.code self.flow.close_reason = ws_event.reason for ws in [self.server_ws, self.client_ws]: if ws.state in { ConnectionState.OPEN, ConnectionState.REMOTE_CLOSING }: # response == original event, so no need to differentiate here. yield ws.send2(ws_event) yield commands.CloseConnection(ws.conn) if ws_event.code in {1000, 1001, 1005}: yield WebsocketEndHook(self.flow) else: self.flow.error = flow.Error( f"WebSocket Error: {format_close_event(ws_event)}") yield WebsocketErrorHook(self.flow) self._handle_event = self.done else: # pragma: no cover raise AssertionError(f"Unexpected WebSocket event: {ws_event}")
def twebsocketflow(client_conn=True, server_conn=True, messages=True, err=None, handshake_flow=True): if client_conn is True: client_conn = tclient_conn() if server_conn is True: server_conn = tserver_conn() if handshake_flow is True: req = http.HTTPRequest( "example.com", 80, b"GET", b"http", b"example.com", b"/ws", b"HTTP/1.1", headers=net_http.Headers( connection="upgrade", upgrade="websocket", sec_websocket_version="13", sec_websocket_key="1234", ), content=b'', trailers=None, timestamp_start=946681200, timestamp_end=946681201, ) resp = http.HTTPResponse( b"HTTP/1.1", 101, reason=net_http.status_codes.RESPONSES.get(101), headers=net_http.Headers( connection='upgrade', upgrade='websocket', sec_websocket_accept=b'', ), content=b'', trailers=None, timestamp_start=946681202, timestamp_end=946681203, ) handshake_flow = http.HTTPFlow(client_conn, server_conn) handshake_flow.request = req handshake_flow.response = resp f = websocket.WebSocketFlow(client_conn, server_conn, handshake_flow) f.metadata['websocket_handshake'] = handshake_flow.id handshake_flow.metadata['websocket_flow'] = f.id handshake_flow.metadata['websocket'] = True if messages is True: messages = [ websocket.WebSocketMessage(Opcode.BINARY, True, b"hello binary"), websocket.WebSocketMessage(Opcode.TEXT, True, b"hello text"), websocket.WebSocketMessage(Opcode.TEXT, False, b"it's me"), ] if err is True: err = terr() f.messages = messages f.error = err f.reply = controller.DummyReply() return f
def relay_messages(self, event: events.Event) -> layer.CommandGenerator[None]: assert self.flow.websocket # satisfy type checker if isinstance(event, events.ConnectionEvent): from_client = event.connection == self.context.client elif isinstance(event, WebSocketMessageInjected): from_client = event.message.from_client else: raise AssertionError(f"Unexpected event: {event}") from_str = 'client' if from_client else 'server' if from_client: src_ws = self.client_ws dst_ws = self.server_ws else: src_ws = self.server_ws dst_ws = self.client_ws if isinstance(event, events.DataReceived): src_ws.receive_data(event.data) elif isinstance(event, events.ConnectionClosed): src_ws.receive_data(None) elif isinstance(event, WebSocketMessageInjected): fragmentizer = Fragmentizer([], event.message.type == Opcode.TEXT) src_ws._events.extend(fragmentizer(event.message.content)) else: # pragma: no cover raise AssertionError(f"Unexpected event: {event}") for ws_event in src_ws.events(): if isinstance(ws_event, wsproto.events.Message): is_text = isinstance(ws_event.data, str) if is_text: typ = Opcode.TEXT src_ws.frame_buf[-1] += ws_event.data.encode() else: typ = Opcode.BINARY src_ws.frame_buf[-1] += ws_event.data if ws_event.message_finished: content = b"".join(src_ws.frame_buf) fragmentizer = Fragmentizer(src_ws.frame_buf, is_text) src_ws.frame_buf = [b""] message = websocket.WebSocketMessage( typ, from_client, content) self.flow.websocket.messages.append(message) yield WebsocketMessageHook(self.flow) if not message.dropped: for msg in fragmentizer(message.content): yield dst_ws.send2(msg) elif ws_event.frame_finished: src_ws.frame_buf.append(b"") elif isinstance(ws_event, (wsproto.events.Ping, wsproto.events.Pong)): yield commands.Log( f"Received WebSocket {ws_event.__class__.__name__.lower()} from {from_str} " f"(payload: {bytes(ws_event.payload)!r})") yield dst_ws.send2(ws_event) elif isinstance(ws_event, wsproto.events.CloseConnection): self.flow.websocket.timestamp_end = time.time() self.flow.websocket.closed_by_client = from_client self.flow.websocket.close_code = ws_event.code self.flow.websocket.close_reason = ws_event.reason for ws in [self.server_ws, self.client_ws]: if ws.state in { ConnectionState.OPEN, ConnectionState.REMOTE_CLOSING }: # response == original event, so no need to differentiate here. yield ws.send2(ws_event) yield commands.CloseConnection(ws.conn) yield WebsocketEndHook(self.flow) self._handle_event = self.done else: # pragma: no cover raise AssertionError(f"Unexpected WebSocket event: {ws_event}")