Can't get websocket connection working with libsoup

Hi,

Im trying to get a websocket connection to a echo server working, it seems i can connect fine, but then my message never goes out

from gi.repository import Soup
from gi.repository import GLib

TEST_URL = 'wss://echo.websocket.org'

print(Soup.get_major_version(),
      Soup.get_minor_version(),
      Soup.get_micro_version())

def connect():
    session = Soup.Session()
    session.add_feature(Soup.Logger.new(Soup.LoggerLogLevel.BODY, -1))
    message = Soup.Message.new('GET', TEST_URL)
    message.connect('network-event', print)
    message.connect('starting', print)
    session.websocket_connect_async(message,
                                    None,
                                    None,
                                    None,
                                    _on_connect,
                                    None)

def _on_connect(session, result, user_data):
    con = session.websocket_connect_finish(result)
    con.connect('message', _on_message)
    con.connect('closed', _on_closed)
    con.connect('closing', _on_closing)
    con.connect('error', _on_error)
    con.connect('pong', _on_pong)
    print('Trying to send message')
    con.send_text('test')

def _on_message(websocket_connection, type, message):
    print('MESSAGE')
    bytes_ = message.get_data()
    string = bytes_.decode()
    print(string)

def _on_error(websocket_connection, error):
    print('ERROR')
    print(error)

def _on_closed(websocket_connection):
    print('CLOSED')

def _on_closing(websocket_connection):
    print('CLOSING')

def _on_pong(websocket_connection, message):
    print('Pong')
    bytes_ = message.get_data()
    string = bytes_.decode()
    print(string)

connect()
GLib.MainLoop().run()

my logging output looks like this

User:~/projects/tests$ python3 websocket.py
websocket.py:1: PyGIWarning: Soup was imported without specifying a version first. Use gi.require_version('Soup', '2.4') before import to ensure that the right version gets loaded.
  from gi.repository import Soup
2 68 2
<Soup.Message object at 0x7f597e4e7d20 (SoupMessage at 0x23d28a0)> <enum G_SOCKET_CLIENT_RESOLVING of type Gio.SocketClientEvent> None
<Soup.Message object at 0x7f597e4e7d20 (SoupMessage at 0x23d28a0)> <enum G_SOCKET_CLIENT_RESOLVED of type Gio.SocketClientEvent> None
<Soup.Message object at 0x7f597e4e7d20 (SoupMessage at 0x23d28a0)> <enum G_SOCKET_CLIENT_CONNECTING of type Gio.SocketClientEvent> <Gio.TcpConnection object at 0x7f597a34aaf0 (GTcpConnection at 0x2712860)>
<Soup.Message object at 0x7f597e4e7d20 (SoupMessage at 0x23d28a0)> <enum G_SOCKET_CLIENT_RESOLVING of type Gio.SocketClientEvent> None
<Soup.Message object at 0x7f597e4e7d20 (SoupMessage at 0x23d28a0)> <enum G_SOCKET_CLIENT_CONNECTED of type Gio.SocketClientEvent> <Gio.TcpConnection object at 0x7f597a34aaf0 (GTcpConnection at 0x2712860)>
<Soup.Message object at 0x7f597e4e7d20 (SoupMessage at 0x23d28a0)> <enum G_SOCKET_CLIENT_TLS_HANDSHAKING of type Gio.SocketClientEvent> <__gi__.GTlsClientConnectionGnutls object at 0x7f597a3520a0 (GTlsClientConnectionGnutls at 0x27202e0)>
<Soup.Message object at 0x7f597e4e7d20 (SoupMessage at 0x23d28a0)> <enum G_SOCKET_CLIENT_TLS_HANDSHAKED of type Gio.SocketClientEvent> <__gi__.GTlsClientConnectionGnutls object at 0x7f597a3520a0 (GTlsClientConnectionGnutls at 0x27202e0)>
<Soup.Message object at 0x7f597e4e7d20 (SoupMessage at 0x23d28a0)> <enum G_SOCKET_CLIENT_COMPLETE of type Gio.SocketClientEvent> <__gi__.GTlsClientConnectionGnutls object at 0x7f597a3520a0 (GTlsClientConnectionGnutls at 0x27202e0)>
<Soup.Message object at 0x7f597e4e7d20 (SoupMessage at 0x23d28a0)>
> GET  HTTP/1.1
> Soup-Debug-Timestamp: 1577787514
> Soup-Debug: SoupSession 1 (0x23c1100), SoupMessage 1 (0x23d28a0), SoupSocket 1 (0x27120e0)
> Host: echo.websocket.org
> Upgrade: websocket
> Connection: Upgrade
> Sec-WebSocket-Key: awpDu/YRflST/3IGgpzCIA==
> Sec-WebSocket-Version: 13
> Accept-Encoding: gzip, deflate
  
< HTTP/1.1 101 Web Socket Protocol Handshake
< Soup-Debug-Timestamp: 1577787515
< Soup-Debug: SoupMessage 1 (0x23d28a0)
< Connection: Upgrade
< Date: Tue, 31 Dec 2019 10:14:47 GMT
< Sec-WebSocket-Accept: T5QWZRI+y69Zn5tLZZnUj2rO2Vg=
< Server: Kaazing Gateway
< Upgrade: websocket

Trying to send message
1 Like

Hi,

I just did some websocket programming using GNU C.

http://git.savannah.nongnu.org/cgit/gsequencer.git/tree/ags/test/audio/osc/ags_functional_osc_xmlrpc_server_test.c?h=3.0.x&id=e1f44dc4b6a1ec1819d2eddf419962738aeebc1b#n393

Might be it helps …

Are you sure you can use the SoupWebsocketConnection::message() signal? I could imagine that it is a low-level function to handle Websocket protocol.

If you search for the term message you are going to find:

After a successful handshake, clients and servers transfer data back
and forth in conceptual units referred to in this specification as
“messages”. On the wire, a message is composed of one or more
frames. The WebSocket message does not necessarily correspond to a
particular network layer framing, as a fragmented message may be
coalesced or split by an intermediary.

regards, Joël

Sorry, I am not aware of Python … Are you sure the main loop is running as you do:

con.send_text('test')

Might be you want to do following prior doing any callback.

g_main_context_push_thread_default(g_main_context_default());

Hi thanks for your response,

Yes the mainloop is running, otherwise resolving the domain, completing a TLS handshake and finally my async callback getting called, would not work.

How else would you get informed about a incoming message if not via the SoupWebsocketConnection::message signal?

Im not a websocket expert but my last impression was it behaves like normal sockets, means not every message i send out gets or needs a response like with traditional HTTP.
So using stuff like response-body of the Soup.Message object seem wrong to me.

Hi,

Try this:

def _on_connect(session, result, user_data):
    con = session.websocket_connect_finish(result)
    time.sleep(1)
    con.connect('message', _on_message)
    con.connect('closed', _on_closed)
    con.connect('closing', _on_closing)
    con.connect('error', _on_error)
    con.connect('pong', _on_pong)
    stream = con.get_io_stream()
    stream_out = stream.get_output_stream()
    stream_in = stream.get_input_stream()
    print('Trying to send message')
    print('WebSocket rocks')
#    con.send_text('WebSocket rocks');                                                                                                                                                                                                                                                                                                                     
    stream_out.write(b'\x81\x0fWebSocket rocks\x00')
    i = 0
    while i < 10:
        response = stream_in.read_bytes(1024, None)
        print(str(response.get_data()))
        time.sleep(1)
        i += 1

it works!

There seems to be a magic byte sequence at the beginning …

\x81\x0f

You can do con.send_text(), too.

The bytes shown are probably opcodes from Websocket protocol.

regards, Joël

Hi,

Your example does not really use Soup to do it though.

Soup is meant to abstract input/output stream handling from the user, and also abstract any protocol needs like adding magic byte sequences.

See how Soup Tests do it

Ok i solved it myself

The problem is that i dont keep a reference to the SoupWebsocketConnection object. So after the the async connect callback is finished, the Object gets garbage collected, and hence no callbacks fire.

1 Like

Hi,

You can’t do event driven programming with multi-threaded setup.

I would expect from Libsoup that I am able to read/write to Websocket like any other socket :wink:

If you did UDP/TCP sockets before you would know, this is how it works. But do further reading about.

I just have different needs.

@lovetox you are right, I am not sure why I was using GIO instead of:

  soup_websocket_connection_send_text(websocket_connection,
                                      buffer);

This topic was automatically closed 14 days after the last reply. New replies are no longer allowed.