def test_inject_message(ws_testdata): tctx, playbook, flow = ws_testdata assert (playbook << websocket.WebsocketStartHook(flow) >> reply() >> WebSocketMessageInjected( flow, WebSocketMessage(Opcode.TEXT, False, b"hello")) << websocket.WebsocketMessageHook(flow)) assert flow.websocket.messages[-1].content == b"hello" assert flow.websocket.messages[-1].from_client is False assert (playbook >> reply() << SendData(tctx.client, b"\x81\x05hello"))
def _handle_data_frame(self, frame, source_conn, other_conn, is_server): fb = self.server_frame_buffer if is_server else self.client_frame_buffer fb.append(frame) if frame.header.fin: payload = b''.join(f.payload for f in fb) original_chunk_sizes = [len(f.payload) for f in fb] message_type = fb[0].header.opcode compressed_message = fb[0].header.rsv1 fb.clear() websocket_message = WebSocketMessage(message_type, not is_server, payload) length = len(websocket_message.content) self.flow.messages.append(websocket_message) self.channel.ask("websocket_message", self.flow) def get_chunk(payload): if len(payload) == length: # message has the same length, we can reuse the same sizes pos = 0 for s in original_chunk_sizes: yield payload[pos:pos + s] pos += s else: # just re-chunk everything into 10kB frames chunk_size = 10240 chunks = range(0, len(payload), chunk_size) for i in chunks: yield payload[i:i + chunk_size] frms = [ websockets.Frame( payload=chunk, opcode=frame.header.opcode, mask=(False if is_server else 1), masking_key=(b'' if is_server else os.urandom(4))) for chunk in get_chunk(websocket_message.content) ] if len(frms) > 0: frms[-1].header.fin = True else: frms.append( websockets.Frame( fin=True, opcode=websockets.OPCODE.CONTINUE, mask=(False if is_server else 1), masking_key=(b'' if is_server else os.urandom(4)))) frms[0].header.opcode = message_type frms[0].header.rsv1 = compressed_message for frm in frms: other_conn.send(bytes(frm)) return True
def _handle_data_received(self, event, source_conn, other_conn, is_server): fb = self.server_frame_buffer if is_server else self.client_frame_buffer fb.append(event.data) if event.message_finished: original_chunk_sizes = [len(f) for f in fb] if isinstance(event, events.TextReceived): message_type = wsproto.frame_protocol.Opcode.TEXT payload = ''.join(fb) else: message_type = wsproto.frame_protocol.Opcode.BINARY payload = b''.join(fb) fb.clear() websocket_message = WebSocketMessage(message_type, not is_server, payload) length = len(websocket_message.content) self.flow.messages.append(websocket_message) self.channel.ask("websocket_message", self.flow) if not self.flow.stream: def get_chunk(payload): if len(payload) == length: # message has the same length, we can reuse the same sizes pos = 0 for s in original_chunk_sizes: yield (payload[pos:pos + s], True if pos + s == length else False) pos += s else: # just re-chunk everything into 4kB frames # header len = 4 bytes without masking key and 8 bytes with masking key chunk_size = 4092 if is_server else 4088 chunks = range(0, len(payload), chunk_size) for i in chunks: yield (payload[i:i + chunk_size], True if i + chunk_size >= len(payload) else False) for chunk, final in get_chunk(websocket_message.content): self.connections[other_conn].send_data(chunk, final) other_conn.send( self.connections[other_conn].bytes_to_send()) else: self.connections[other_conn].send_data(event.data, event.message_finished) other_conn.send(self.connections[other_conn].bytes_to_send()) elif self.flow.stream: self.connections[other_conn].send_data(event.data, event.message_finished) other_conn.send(self.connections[other_conn].bytes_to_send()) return True