def _handle_message(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.TextMessage): 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 and not websocket_message.killed: 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): data = self.connections[other_conn].send( Message(data=chunk, message_finished=final)) other_conn.send(data) if self.flow.stream: data = self.connections[other_conn].send( Message(data=event.data, message_finished=event.message_finished)) other_conn.send(data) return True
def handle_todo_post_save(sender, instance, created, **kwargs): if not hasattr(sender, 'APP_PORT'): return conn = socket.socket(socket.AF_INET, socket.SOCK_STREAM) conn.connect(('localhost', int(sender.APP_PORT))) ws = WSConnection(ConnectionType.CLIENT) print("hello") net_send( ws.send(Request( host=f"localhost:{sender.APP_PORT}", target=f"ws/todos")), conn ) net_recv(ws, conn) handle_events(ws) net_send(ws.send(Message(data=str(instance.pk))), conn) net_recv(ws, conn) handle_events(ws) net_send(ws.send(CloseConnection(code=1000)), conn) net_recv(ws, conn) conn.shutdown(socket.SHUT_WR) net_recv(ws, conn) del sender.APP_PORT
def _send_data(self, data: AnyStr) -> None: if isinstance(data, str): io_object = io.StringIO(data) else: io_object = io.BytesIO(data) with io_object as f: chunk = f.read(self.buffer_size) while chunk: if len(chunk) < self.buffer_size: self._client.sendall( self._ws.send(Message(data, message_finished=True))) break else: self._client.sendall( self._ws.send(Message(data, message_finished=False))) chunk = f.read(self.buffer_size)
def send(self, data): if not self.connected: raise ConnectionClosed() if isinstance(data, bytes): out_data = self.ws.send(Message(data=data)) else: out_data = self.ws.send(TextMessage(data=str(data))) self.sock.send(out_data)
def _inject_messages(self, endpoint, message_queue): while True: try: payload = message_queue.get_nowait() data = self.connections[endpoint].send(Message(data=payload, message_finished=True)) endpoint.send(data) except queue.Empty: break
def websocket(request): # The underlying socket must be provided by the server. Gunicorn and # Werkzeug's dev server are known to support this. stream = request.environ.get("werkzeug.socket") if stream is None: stream = request.environ.get("gunicorn.socket") if stream is None: raise InternalServerError() # Initialize the wsproto connection. Need to recreate the request # data that was read by the WSGI server already. ws = WSConnection(ConnectionType.SERVER) in_data = b"GET %s HTTP/1.1\r\n" % request.path.encode("utf8") for header, value in request.headers.items(): in_data += f"{header}: {value}\r\n".encode() in_data += b"\r\n" ws.receive_data(in_data) running = True while True: out_data = b"" for event in ws.events(): if isinstance(event, WSRequest): out_data += ws.send(AcceptConnection()) elif isinstance(event, CloseConnection): out_data += ws.send(event.response()) running = False elif isinstance(event, Ping): out_data += ws.send(event.response()) elif isinstance(event, TextMessage): # echo the incoming message back to the client if event.data == "quit": out_data += ws.send( CloseConnection(CloseReason.NORMAL_CLOSURE, "bye") ) running = False else: out_data += ws.send(Message(data=event.data)) if out_data: stream.send(out_data) if not running: break in_data = stream.recv(4096) ws.receive_data(in_data) # The connection will be closed at this point, but WSGI still # requires a response. return Response("", status=204)
def handle_connection(stream: socket.socket) -> None: """ Handle a connection. The server operates a request/response cycle, so it performs a synchronous loop: 1) Read data from network into wsproto 2) Get new events and handle them 3) Send data from wsproto to network :param stream: a socket stream """ ws = WSConnection(ConnectionType.SERVER) running = True while running: # 1) Read data from network in_data = stream.recv(RECEIVE_BYTES) print("Received {} bytes".format(len(in_data))) ws.receive_data(in_data) # 2) Get new events and handle them out_data = b"" for event in ws.events(): if isinstance(event, Request): # Negotiate new WebSocket connection print("Accepting WebSocket upgrade") out_data += ws.send(AcceptConnection()) elif isinstance(event, CloseConnection): # Print log message and break out print("Connection closed: code={} reason={}".format( event.code, event.reason)) out_data += ws.send(event.response()) running = False elif isinstance(event, TextMessage): # Reverse text and send it back to wsproto print("Received request and sending response") out_data += ws.send(Message(data=event.data[::-1])) elif isinstance(event, Ping): # wsproto handles ping events for you by placing a pong frame in # the outgoing buffer. You should not call pong() unless you want to # send an unsolicited pong frame. print("Received ping and sending pong") out_data += ws.send(event.response()) else: print(f"Unknown event: {event!r}") # 4) Send data from wsproto to network print("Sending {} bytes".format(len(out_data))) stream.send(out_data)
def wsproto_demo(host, port): ''' Demonstrate wsproto: 0) Open TCP connection 1) Negotiate WebSocket opening handshake 2) Send a message and display response 3) Send ping and display pong 4) Negotiate WebSocket closing handshake :param stream: a socket stream ''' # 0) Open TCP connection print('Connecting to {}:{}'.format(host, port)) conn = socket.socket(socket.AF_INET, socket.SOCK_STREAM) conn.connect((host, port)) # 1) Negotiate WebSocket opening handshake print('Opening WebSocket') ws = WSConnection(ConnectionType.CLIENT) # Because this is a client WebSocket, we need to initiate the connection # handshake by sending a Request event. net_send(ws.send(Request(host=host, target='server')), conn) net_recv(ws, conn) handle_events(ws) # 2) Send a message and display response message = "wsproto is great" print('Sending message: {}'.format(message)) net_send(ws.send(Message(data=message)), conn) net_recv(ws, conn) handle_events(ws) # 3) Send ping and display pong payload = b"table tennis" print('Sending ping: {}'.format(payload)) net_send(ws.send(Ping(payload=payload)), conn) net_recv(ws, conn) handle_events(ws) # 4) Negotiate WebSocket closing handshake print('Closing WebSocket') net_send(ws.send(CloseConnection(code=1000, reason='sample reason')), conn) # After sending the closing frame, we won't get any more events. The server # should send a reply and then close the connection, so we need to receive # twice: net_recv(ws, conn) conn.shutdown(socket.SHUT_WR) net_recv(ws, conn)
def test_send_message(client_sends, final): client = Connection(CLIENT) server = Connection(SERVER) if client_sends: local = client remote = server else: local = server remote = client data = b"x" * 23 remote.receive_data(local.send(Message(data=data, message_finished=final))) event = next(remote.events()) assert isinstance(event, BytesMessage) assert event.data == data assert event.message_finished is final
def new_conn(sock: socket.socket) -> None: global count print("test_server.py received connection {}".format(count)) count += 1 ws = WSConnection(SERVER) closed = False while not closed: try: data: Optional[bytes] = sock.recv(65535) except socket.error: data = None ws.receive_data(data or None) outgoing_data = b"" for event in ws.events(): if isinstance(event, Request): outgoing_data += ws.send( AcceptConnection(extensions=[PerMessageDeflate()])) elif isinstance(event, Message): outgoing_data += ws.send( Message(data=event.data, message_finished=event.message_finished)) elif isinstance(event, Ping): outgoing_data += ws.send(event.response()) elif isinstance(event, CloseConnection): closed = True if ws.state is not ConnectionState.CLOSED: outgoing_data += ws.send(event.response()) if not data: closed = True try: sock.sendall(outgoing_data) except socket.error: closed = True sock.close()
def handle_connection(stream): ''' Handle a connection. The server operates a request/response cycle, so it performs a synchronous loop: 1) Read data from network into wsproto 2) Get next wsproto event 3) Handle event 4) Send data from wsproto to network :param stream: a socket stream ''' ws = WSConnection(ConnectionType.SERVER) # events is a generator that yields websocket event objects. Usually you # would say `for event in ws.events()`, but the synchronous nature of this # server requires us to use next(event) instead so that we can interleave # the network I/O. events = ws.events() running = True while running: # 1) Read data from network in_data = stream.recv(RECEIVE_BYTES) print('Received {} bytes'.format(len(in_data))) ws.receive_data(in_data) # 2) Get next wsproto event try: event = next(events) except StopIteration: print('Client connection dropped unexpectedly') return # 3) Handle event if isinstance(event, Request): # Negotiate new WebSocket connection print('Accepting WebSocket upgrade') out_data = ws.send(AcceptConnection()) elif isinstance(event, CloseConnection): # Print log message and break out print('Connection closed: code={}/{} reason={}'.format( event.code.value, event.code.name, event.reason)) out_data = ws.send(event.response()) running = False elif isinstance(event, TextMessage): # Reverse text and send it back to wsproto print('Received request and sending response') out_data = ws.send(Message(data=event.data[::-1])) elif isinstance(event, Ping): # wsproto handles ping events for you by placing a pong frame in # the outgoing buffer. You should not call pong() unless you want to # send an unsolicited pong frame. print('Received ping and sending pong') out_data = ws.send(event.response()) else: print('Unknown event: {!r}'.format(event)) # 4) Send data from wsproto to network print('Sending {} bytes'.format(len(out_data))) stream.send(out_data)
async def send(self, data: AnyStr) -> None: self.server.data_received(self.connection.send(Message(data=data))) await asyncio.sleep(0) # Allow the server to respond
async def send(self, data): if self.closed: raise WebsocketClosedError() await self.outgoing.put(Message(data=data))
host=uri.netloc, target='%s?%s' % (uri.path, uri.query), extensions=[PerMessageDeflate()], )) ) closed = False while not closed: try: data = sock.recv(65535) except CONNECTION_EXCEPTIONS: data = None connection.receive_data(data or None) out_data = b"" for event in connection.events(): if isinstance(event, Message): out_data += connection.send(Message(data=event.data, message_finished=event.message_finished)) elif isinstance(event, Ping): out_data += connection.send(event.response()) elif isinstance(event, CloseConnection): closed = True out_data += connection.send(event.response()) # else: # print("??", event) if out_data is None: break try: sock.sendall(out_data) except CONNECTION_EXCEPTIONS: closed = True break
async def send(self, data: AnyStr) -> None: await self.client_stream.send_all( self.connection.send(Message(data=data))) await trio.sleep(0) # Allow the server to respond
target='%s?%s' % (uri.path, uri.query), extensions=[PerMessageDeflate()], ))) closed = False while not closed: try: data = sock.recv(65535) except CONNECTION_EXCEPTIONS: data = None connection.receive_data(data or None) out_data = b"" for event in connection.events(): if isinstance(event, Message): out_data += connection.send( Message(data=event.data, message_finished=event.message_finished)) elif isinstance(event, Ping): out_data += connection.send(event.response()) elif isinstance(event, CloseConnection): closed = True out_data += connection.send(event.response()) # else: # print("??", event) if out_data is None: break try: sock.sendall(out_data) except CONNECTION_EXCEPTIONS: closed = True break
def wsproto_demo(host, port): ''' Demonstrate wsproto: 0) Open TCP connection 1) Negotiate WebSocket opening handshake 2) Send a message and display response 3) Send ping and display pong 4) Negotiate WebSocket closing handshake :param stream: a socket stream ''' # 0) Open TCP connection print('Connecting to {}:{}'.format(host, port)) conn = socket.socket(socket.AF_INET, socket.SOCK_STREAM) conn.connect((host, port)) # 1) Negotiate WebSocket opening handshake print('Opening WebSocket') ws = WSConnection(ConnectionType.CLIENT) net_send(ws.send(Request(host=host, target='server')), conn) net_recv(ws, conn) # events is a generator that yields websocket event objects. Usually you # would say `for event in ws.events()`, but the synchronous nature of this # client requires us to use next(event) instead so that we can interleave # the network I/O. It will raise StopIteration when it runs out of events # (i.e. needs more network data), but since this script is synchronous, we # will explicitly resume the generator whenever we have new network data. events = ws.events() # Because this is a client WebSocket, wsproto has automatically queued up # a handshake, and we need to send it and wait for a response. event = next(events) if isinstance(event, AcceptConnection): print('WebSocket negotiation complete') else: raise Exception('Expected AcceptConnection event!') # 2) Send a message and display response message = "wsproto is great" print('Sending message: {}'.format(message)) net_send(ws.send(Message(data=message)), conn) net_recv(ws, conn) event = next(events) if isinstance(event, TextMessage): print('Received message: {}'.format(event.data)) else: raise Exception('Expected TextMessage event!') # 3) Send ping and display pong payload = b"table tennis" print('Sending ping: {}'.format(payload)) net_send(ws.send(Ping(payload=payload)), conn) net_recv(ws, conn) event = next(events) if isinstance(event, Pong): print('Received pong: {}'.format(event.payload)) else: raise Exception('Expected Pong event!') # 4) Negotiate WebSocket closing handshake print('Closing WebSocket') net_send(ws.send(CloseConnection(code=1000, reason='sample reason')), conn) # After sending the closing frame, we won't get any more events. The server # should send a reply and then close the connection, so we need to receive # twice: net_recv(ws, conn) conn.shutdown(socket.SHUT_WR) net_recv(ws, conn)