1

I'm trying to set up a WAMP server that can handle session data of individual clients. However this seems more troublesome than initially thought.

Crossbar config:

{
  "workers": [
    {
      "type": "router",
      "realms": [
        {
          "name": "default",
          "roles": [
            {
              "name": "anonymous",
              "permissions": [
                {
                  "uri": "*",
                  "call": true,
                  "register": true
                }
              ]
            }
          ]
        }
      ],
      "transports": [
        {
          "type": "websocket",
          "endpoint": {
            "type": "tcp",
            "port": 8080
          }
        }
      ],
      "components": [
        {
          "type": "class",
          "classname": "server.Server",
          "realm": "default",
          "role": "anonymous"
        }
      ]
    }
  ]
}

server.py:

The server registers two RPCs, one for appending data and one for returning a string of the data. The data is stored as self.data, but this is storing data for all sessions, not on a per client, per session basis. Once the session dies, the server should clean up the session data. Simply cleaning the list is no solution as simultaneous clients can access each others data. The id of the client is available in the append RPC (if the client discloses itself), however this seems fairly useless at this point.

from autobahn.twisted import wamp
from autobahn.wamp import types


class Server(wamp.ApplicationSession):
    def __init__(self, *args, **kwargs):
        wamp.ApplicationSession.__init__(self, *args, **kwargs)
        self.data = []

    def onJoin(self, details):
        def append(data, details):
            client_id = details.caller
            self.data.append(data)

        def get():
            return ''.join(self.data)

        options = types.RegisterOptions(details_arg='details')
        self.register(append, 'append', options=options)
        self.register(get, 'get')

client.py:

The client connects to the server, waits for the connection to open and executes RPCs. The client first appends 'a' and 'b' to the server's data, then the data is get and printed. The result should be ab as data should be stored per client, per session. Once the session dies, the data should be cleaned up.

import asyncio

from autobahn.asyncio import wamp
from autobahn.asyncio import websocket
from autobahn.wamp import types


class Client(wamp.ApplicationSession):
    def onOpen(self, protocol):
        protocol.session = self
        wamp.ApplicationSession.onOpen(self, protocol)


if __name__ == '__main__':
    session_factory = wamp.ApplicationSessionFactory()
    session_factory.session = Client
    transport_factory = websocket.WampWebSocketClientFactory(session_factory)

    loop = asyncio.get_event_loop()
    transport, protocol = loop.run_until_complete(
        asyncio.async(loop.create_connection(
            transport_factory, 'localhost', '8080',)))

    connected = asyncio.Event()

    @asyncio.coroutine
    def poll():
        session = getattr(protocol, 'session', None)
        if not session:
            yield from asyncio.sleep(1)
            asyncio.ensure_future(poll())
        else:
            connected.set()

    # Wait for session to open.
    asyncio.ensure_future(poll())
    loop.run_until_complete(connected.wait())

    # Client is connected, call RPCs.
    options = types.CallOptions(disclose_me=True)
    protocol.session.call('append', 'a', options=options)
    protocol.session.call('append', 'b', options=options)
    f = protocol.session.call('get', options=options)
    # Get stored data and print it.
    print(loop.run_until_complete(f))

Hope somebody can tell me how I can store data per client, per session in the server's memory.

siebz0r
  • 18,867
  • 14
  • 64
  • 107

1 Answers1

0

I managed to create a hack for this. It doesn't seem like the perfect solution, but it works for now.

from autobahn.twisted import wamp
from autobahn.wamp import types
from twisted.internet.defer import inlineCallbacks


class Server(wamp.ApplicationSession):
    def __init__(self, *args, **kwargs):
        wamp.ApplicationSession.__init__(self, *args, **kwargs)    
        self.sessions = {}

    def onJoin(self, details):
        def on_client_join(details):
            client_id = details['session']
            self.sessions[client_id] = {}

        def on_client_leave(client_id):
            self.sessions.pop(client_id)

        self.subscribe(on_client_join, 'wamp.session.on_join')
        self.subscribe(on_client_leave, 'wamp.session.on_leave')

        def get_session(details):
            return self.sessions[details.caller]

        @inlineCallbacks
        def append(data, details):
            session = yield self.call('get_session', details)
            d = session.setdefault('data', [])
            d.append(data)

        @inlineCallbacks
        def get(details):
            session = yield self.call('get_session', details)
            return ''.join(session['data'])

        reg_options = types.RegisterOptions(details_arg='details')
        self.register(get_session, 'get_session')
        self.register(append, 'append', options=reg_options)
        self.register(get, 'get', options=reg_options)

As session gets created when a client connects (on_client_join), and the session gets destroyed when a client disconnects (on_client_leave).

The server also needs permission to subscribe to the meta events. config.json:

...
"permissions": [
  {
    "uri": "*",
    "call": true,
    "register": true,
    "subscribe": true,
  }
]
....
siebz0r
  • 18,867
  • 14
  • 64
  • 107
  • Why do you consider this a hack? You need to use the meta-events since you can't count on clients notifying you of leaving (the connection could just drop). – gzost Oct 29 '15 at 11:57
  • @gzost Since I haven't read anything about sessions in the crossbar docs, I didn't know if this is how sessions should be implemented. I don't want people to assume this is production-ready code. – siebz0r Oct 30 '15 at 07:30
  • The session meta-events are intended for applications to be notified of what is happening regarding sessions, so I think this is intended usage. – gzost Oct 30 '15 at 12:01