Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to keep session alive when using async websockets?

I have this code to interact with a websocket api using async and websokets python libraries:

#!/usr/bin/env python3

import sys, json
import asyncio
from websockets import connect

class AsyncWebsocket():
    async def __aenter__(self):
        self._conn = connect('wss://ws.myws.com/v2')
        self.websocket = await self._conn.__aenter__()        
        return self

    async def __aexit__(self, *args, **kwargs):
        await self._conn.__aexit__(*args, **kwargs)

    async def send(self, message):
        await self.websocket.send(message)

    async def receive(self):
        return await self.websocket.recv()

class mtest():
    def __init__(self, api_token):
        self.aws        = AsyncWebsocket()
        self.loop       = asyncio.get_event_loop()
        self.api_token  = api_token

        self.authorize()

    def authorize(self):
        jdata = self.__async_exec({
                                    'authorize': self.api_token
                                  })

        try:
            print (jdata['email'])
            ret = True
        except:
            ret = False

        return ret

    def sendtest(self):

        jdata = self.__async_exec({
                                    "hello": 1
                                  })

        print (jdata)


    def __async_exec(self, jdata):
        try:
            ret = json.loads(self.loop.run_until_complete(self.__async_send_recieve(jdata)))
        except:
            ret = None

        return ret

    async def __async_send_recieve(self, jdata):
        async with self.aws:
            await self.aws.send(json.dumps(jdata))
            return await self.aws.receive()

So I have the following in main.py:

from webclass import *

a = mtest('12341234')
print (a.sendtest())

The problem is that it doesn't preserve the authorized session, so this is the output:

root@ubupc1:/home/dinocob# python3 test.py 
[email protected]
{'error': {'message': 'Please log in.', 'code': 'AuthorizationRequired'}}

As you see, the login call are working ok, but when calling and sending the hello in sendtest function, the session is not the same.

  • Where is destroyed the session?
  • How could I preserve it (without drastically modifying my class structure)?
like image 260
mllamazares Avatar asked Feb 06 '17 17:02

mllamazares


1 Answers

I think that the issue might be in the context_manager or the with statement.

async def __async_send_recieve(self, jdata):
    async with self.aws:
        await self.aws.send(json.dumps(jdata))
        return await self.aws.receive()

When you invoke 'with' the context should play out as following (with better exception handling and all the benefits of context managers, so you can picture the flow of __async_send_recieve as:

       self.aws.__aenter__()
       self.aws.send(data)
       self.aws.receive()
       self.aws.__aexit__()

To prove this theory, add a print statement into the __aenter__ and __aexit__ functions, and you should be able to better visualize the context manager flow.

The fix would be to re-authorize in every request. But I imagine what you want to do is have your test class to manage the context used to communicate with the remote server. (my async syntax might be a little wrong here, but conceptually with context managers):

class Mtest():
    def __init__(self, ...):
           ...

    def __enter__(self,):
        self.authorize()

    def __exit__(self):
        self.deauthorize()

    async def make_async_request(self, data):
        await self.aws.send(json.dumps(data))
        return await self.aws.receive()

with Mtest(api_key) as m:
    m.make_async_request({'test_data': 'dummy_test_data'})
    m.make_async_request({'more_data': 'more_mock_data'})
like image 141
Luke Exton Avatar answered Oct 05 '22 22:10

Luke Exton