Example #1
0
 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
Example #2
0
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)
Example #3
0
    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}")
Example #4
0
    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()
Example #5
0
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
Example #6
0
def test_send_invalid_event():
    client = H11Handshake(CLIENT)
    with pytest.raises(LocalProtocolError):
        client.send(Ping())
Example #7
0
 def _handle_ping(self, event: Ping) -> None:
     self._client.sendall(self._ws.send(event.response()))
Example #8
0
    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)))
Example #9
0
    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)))
Example #10
0
 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()))
Example #11
0
 async def _handle_ping_event(self, event: Ping):
     logger.debug('%s pings with %r', self, event.payload)
     await self._send(event.response())
Example #12
0
 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)
Example #13
0
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)