Hello,
I’m trying to run a websocket client alongside the python cmd library, basically taking input from the user and converting it to a websocket message. The problem is both libraries run an event loop.
I tried using crochet with the “@run_in_reactor” feature but it’s not working. It seems like it might be a problem with my client protocol class but it looks complete based on the docs I’ve been working from. Reactor starts as expected and the client successfully connects to the server but when I try to call sendMessage, I get the following error:
Unhandled Error
Traceback (most recent call last):
File "/Library/Developer/CommandLineTools/Library/Frameworks/Python3.framework/Versions/3.7/lib/python3.7/threading.py", line 865, in run
self._target(*self._args, **self._kwargs)
File "/Users/tomd/havoc/lib/python3.7/site-packages/crochet/_eventloop.py", line 412, in <lambda>
target=lambda: self._reactor.run(installSignalHandlers=False),
File "/Users/tomd/havoc/lib/python3.7/site-packages/twisted/internet/base.py", line 1283, in run
self.mainLoop()
File "/Users/tomd/havoc/lib/python3.7/site-packages/twisted/internet/base.py", line 1292, in mainLoop
self.runUntilCurrent()
--- <exception caught here> ---
File "/Users/tomd/havoc/lib/python3.7/site-packages/twisted/internet/base.py", line 886, in runUntilCurrent
f(*a, **kw)
File "./havoc.py", line 62, in sendTask
self.sendMessage(payload)
File "/Users/tomd/havoc/lib/python3.7/site-packages/autobahn/websocket/protocol.py", line 2215, in sendMessage
if self.state != WebSocketProtocol.STATE_OPEN:
builtins.AttributeError: 'HavocClientProtocol' object has no attribute 'state'
My imports:
from cmd import Cmd
from crochet import setup, run_in_reactor, retrieve_result, TimeoutError
# Setup crochet before importing twisted
setup()
from twisted.internet import reactor, ssl
from twisted.python import log
from autobahn.twisted.websocket import WebSocketClientFactory, \
WebSocketClientProtocol, \
connectWS
This is my ClientProtocol and ClientFactory:
class HavocClientProtocol(WebSocketClientProtocol):
def __init__(self):
super(HavocClientProtocol, self).__init__()
def onConnect(self, response):
print("Connected to havocops.io")
def onMessage(self, payload, isBinary):
if not isBinary:
print('Message received: {}'.format(payload.decode('utf8')))
def sendTask(self, payload):
payload = json.dumps(payload, ensure_ascii = False).encode('utf8')
self.sendMessage(payload)
class HavocClientFactory(WebSocketClientFactory):
def __init__(self):
super(HavocClientFactory, self).__init__()
def buildFactory(self, uri, headers):
factory = WebSocketClientFactory(uri, headers=headers)
factory.protocol = HavocClientProtocol
return factory
And this is how I’m calling it:
if __name__ == '__main__':
@run_in_reactor
def start_connectWS():
headers = {'api_key': api_key, 'secret_key': secret_key}
f = HavocClientFactory()
connectStatement = f.buildFactory(uri, headers)
if connectStatement.isSecure:
contextFactory = ssl.ClientContextFactory()
else:
contextFactory = None
connectWS(connectStatement, contextFactory)
start_connectWS()
If I’m on the wrong path, what is the recommended approach to background the websocket client and call sendMessage from another class? If it seems like I don’t know what the hell I’m doing, it’s because I don’t! Any guidance would be greatly appreciated.
Thanks,
Tom