def _handle_ping(self, event, source_conn, other_conn, is_server): # Use event.response to create the approprate Pong response data = self.connections[other_conn].send(Ping()) other_conn.send(data) data = self.connections[source_conn].send(event.response()) source_conn.send(data) self.log( "Ping Received from {}".format( "server" if is_server else "client"), "info", [strutils.bytes_to_escaped_str(bytes(event.payload))]) return True
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)
async def recv(self) -> bytes: """ Raises: TransportError """ data = bytearray() while True: if self.keepalive: with trio.move_on_after(self.keepalive) as cancel_scope: event = await self._next_ws_event() if cancel_scope.cancel_called: self.logger.debug("Sending keep alive ping") await self._net_send(Ping()) continue else: event = await self._next_ws_event() if isinstance(event, CloseConnection): self.logger.debug("Connection closed", code=event.code, reason=event.reason) try: await self._net_send(event.response()) except LocalProtocolError: # TODO: exception occurs when ws.state is already closed... pass raise TransportClosedByPeer("Peer has closed connection") elif isinstance(event, BytesMessage): # TODO: check that data doesn't go over MAX_BIN_LEN (1 MB) # Msgpack will refuse to unpack it so we should fail early on if that happens data += event.data if event.message_finished: return data elif isinstance(event, Ping): self.logger.debug("Received ping and sending pong") await self._net_send(event.response()) elif isinstance(event, Pong): # Nothing to do \o/ self.logger.debug("Received pong") else: self.logger.warning("Unexpected event", ws_event=event) raise TransportError(f"Unexpected event: {event}")
async def ping(self, payload: bytes = None): """Sends a WebSocket ping frame to the peer and waits for a pong. As each ping must be unique, the client internally keeps track of them. .. note:: A server is allowed to respond with a single pong to multiple pings. Therefore, a pong will wake up its corresponding ping as well as all previous in-flight pings. Parameters ---------- payload : Optional[bytes] The payload to send. If ``None``, a random 32-bit payload will be generated. Raises ------ :exc:`anysocks.exceptions.ConnectionClosed` If the connection is already closed. :exc:`ValueError` If ``payload`` is identical to another in-flight ping. """ if self._closed: raise ConnectionClosed(self._close_code, self._close_reason) if payload in self._pings: raise ValueError( 'Payload value {} is already in flight'.format(payload)) if payload is None: payload = struct.pack('!I', random.getrandbits(32)) event = anyio.Event() self._pings[payload] = event await self._send(Ping(payload=payload)) await event.wait()
def test_ping_pong(client_sends: bool) -> None: client = Connection(CLIENT) server = Connection(SERVER) if client_sends: local = client remote = server else: local = server remote = client payload = b"x" * 23 remote.receive_data(local.send(Ping(payload=payload))) event = next(remote.events()) assert isinstance(event, Ping) assert event.payload == payload local.receive_data(remote.send(event.response())) event = next(local.events()) assert isinstance(event, Pong) assert event.payload == payload
def test_send_invalid_event(): client = H11Handshake(CLIENT) with pytest.raises(LocalProtocolError): client.send(Ping())
def _handle_ping(self, event: Ping) -> None: self._client.sendall(self._ws.send(event.response()))
def ping(self, data: bytes = b'hello') -> None: if not isinstance(data, bytes): raise TypeError('data must be bytes') self._client.sendall(self._ws.send(Ping(data)))
def ping(self, data: bytes = b'hello') -> None: self._handshake_finished.get() if not isinstance(data, bytes): raise TypeError('data must be bytes') self._sock.sendall(self._ws.send(Ping(data)))
def _handle_ping(self, event: Ping) -> None: if EventType.PING in self._callbacks: self._callbacks[EventType.PING](event.payload) self._sock.sendall(self._ws.send(event.response()))
async def _handle_ping_event(self, event: Ping): logger.debug('%s pings with %r', self, event.payload) await self._send(event.response())
async def _send_pings(self) -> None: while not self.closed: await self._send_wsproto_event(Ping()) await self.context.sleep(self.config.websocket_ping_interval)
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)