How to force WAMP Ticket Authentication? - python

I'm using the older Autobahn|Python subclassing API so that I can include ticket authentication for connecting to a WAMP bus. When I run all parts of the environment on my local machine, everything works fine and dandy - connection, challenge, authentication, publication of messages - but when I move over to my testing environment on an external server, the challenge for authentication doesn't seem to happen. My subclass looks like this:
class ClientSession(ApplicationSession):
def onConnect(self):
log("Session connected")
self.join(self.config.realm, [u"ticket"], ROLE_ID)
def onChallenge(self, challenge):
log("Authentication challenged")
if challenge.method == u"ticket":
return TICKET
#inlineCallbacks
def onJoin(session, details):
log("Connected to WAMP")
while True:
session.publish(u"topic", message)
yield(60)
if __name__ == "__main__":
RUNNER = ApplicationSession(url=WAMP_URL, realm=u"my-realm")
RUNNER.run(ClientSession)
When I run the full application on my computer, it goes through all of the steps perfectly fine, but when I run it on my test server that has a slightly different configuration, the only output that is logged is from the onConnect method. I need the authentication to be able to publish messages. Help!

Related

Python SocketIO, run socketio server in background

How can I start a SocketIO server and have it be listening in the background while I continue to execute code on the main thread?
Right now I am using the package python-socketio. I have also testedflask-socketio which using python-socketio anyway.
https://python-socketio.readthedocs.io/en/latest/server.html
What I've mostly tried is starting the server with sio.start_background_task.
For example:
class SocketIOServer(socketio.AsyncNamespace):
def __init__(self):
super().__init__()
self.sio = socketio.AsyncServer()
self.sio.register_namespace(self)
self.app = web.Application()
self.sio.attach(self.app)
self.task = None
def run(self):
web.run_app(self.app, port=8080)
def start(self):
self.task = self.sio.start_background_task(self.run)
I tried the above and multiple variations like using Flask, Tornado, etc.
To be more clear this is basically what I want to do:
if __name__ == '__main__':
# ...
# start app, e.g. -> web.run_app(self.app, port=8080)
# I want to continue to execute code here
I don't fully understand how everything is working, am I asking a stupid question?
The problem is as you described it. You do not fully understand how everything works.
The most important thing that you are missing is that the python-socketio package is not a web server. This package just implements the Socket.IO logic, but you have to add a web server through which your Socket.IO code is available.
From the code that you included in your question, it appears that you have selected to attach the Socket.IO code to a web application built with the aiohttp framework, and use its own web server. Correct?
Then the real question that you have is how to run the aiohttp web server in a non-blocking way.
And it runs out that aiohttp has information on this in their documentation: https://docs.aiohttp.org/en/stable/web_advanced.html#application-runners. The example they have does this:
runner = web.AppRunner(app)
await runner.setup()
site = web.TCPSite(runner, 'localhost', 8080)
await site.start() # <-- starts the server without blocking
# you can do whatever you want here!

Websocket connection between socket.io client and tornado python server

I'm trying to get websockets to work between two machines. One pc and one raspberry pi to be exact.
On the PC I'm using socket.io as a client to connect to the server on the raspberry pi.
With the following code I iniated the connection and try to send predefined data.
var socket = io.connect(ip + ':8080');
socket.send('volumes', { data: data });
On the raspberry pi, the websocket server looks like this:
from tornado import web, ioloop
from sockjs.tornado import SockJSRouter, SockJSConnection
class EchoConnection(SockJSConnection):
def on_message(self, msg):
self.send(msg)
def check_origin(self, origin):
return True
if __name__ == '__main__':
EchoRouter = SockJSRouter(EchoConnection, '/echo')
app = web.Application(EchoRouter.urls)
app.listen(8080)
ioloop.IOLoop.instance().start()
But the connection is never established. And I don't know why. In the server log I get:
WARNING:tornado.access:404 GET /socket.io/1/?t=1412865634790
(192.168.0.16) 9.01ms
And in the Inspector on the pc there is this error message:
XMLHttpRequest cannot load http://192.168.0.10:8080/socket.io/1/?t=1412865634790. Origin sp://793b6d4588ead99e1780e35b71d24d1b285328f8.hue is not allowed by Access-Control-Allow-Origin.
I am out of ideas and don't know what to do. Can you help me?
Thank you!
Well, the solution for your problem has to do with the internal design of the sockjs-tornado library more than with the socket.io library.
Basically, your problem has to do with cross origin request i.e. the html that is generating the request to the websocket server is not at the same origin as the websocket server. I can see from your code that you already identified the problem ( and you tried to solve it by redefining the method "check_origin") but you didnĀ“t find the proper way to do it, basically because within this library is not the SockJSConnection class the one that extends tornado WebSocketHandler and so redefining its "check_origin" is useless. If you dig a little bit into the code, you will see that there exists one class defined, namely SockJSWebSocketHandler that has a redefinition of such method itself, which relies on the tornado implementation if it returns true, but that also allows you to avoid that check using a setting parameter :
class SockJSWebSocketHandler(websocket.WebSocketHandler):
def check_origin(self, origin):
***
allow_origin = self.server.settings.get("websocket_allow_origin", "*")
if allow_origin == "*":
return True
So, to summarize, you just need to include the setting "websocket_allow_origin"="*" in the server settings and everything should work properly =D
if __name__ == '__main__':
EchoRouter = SockJSRouter(EchoConnection, '/echo', user_settings={"websocket_allow_origin":"*"})

Bottle equivalent of engine.restart()

I am trying to transfer from Cherrypy to Bottle & Gevent(server).
After I run:
application=bottle.default_app() #bottle
WSGIServer(('', port), application, spawn=None).serve_forever() #gevent
I want to restart the server just as if the reloader reloaded the server (but only when I tell the server to).
So I want to access a page with credential request and only after correct authentication will it restart.
Here is my functional example in Cherrypy:
#expose
def reloadMe(self, u=None, p=None):
if u=="username" and p=="password":
engine.restart()
raise HTTPRedirect('/')
More simply I am asking how do I reload this script so that my edits to the source file are implemented but only when I retrieve a "restart" page.
I literally only need the Bottlepy equivalent of
engine.restart() #cherrypy
Does no one know how to do this?
You can write a small shell script to restart gevent wsgi server.
then using this code, you can call the script.
#get('/restartmyserver')
def handler():
http_auth_data = bottle.request.auth() # returns a tuple (username,password) only basic auth.
if http_auth_data[0] == user and http_auth_data[1] == password:
os.system("your_shell_script_to_restart_gevent_wsgi")
bottle.redirect('/')
let me know if you need more info.

Websocket/event-source/... implementation to expose a two-way RPC to a python/django application

for a django application I'm working on, I need to implement a two ways RPC so :
the clients can call RPC methods from the platform and
the platform can call RPC methods from each client.
As the clients will mostly be behind NATs (which means no public IPs, and unpredictable weird firewalling policies), the platform to client way has to be initiated by the client.
I have a pretty good idea on how I can write this from scratch, I also think I can work something out of the publisher/subscriber model of twisted, but I've learned that there is always a best way to do it in python.
So I'm wondering what would be the best way to do it, that would also integrate the best to django. The code will have to be able to scope with hundreds of clients in short term, and (we hope) with thousands of clients in medium/long term.
So what library/implementation would you advice me to use ?
I'm mostly looking to starting points for RTFM !
websocket is a moving target, with new specifications from time to time. Brave developpers implements server side library, but few implements client side. The client for web socket is a web browser.
websocket is not the only way for a server to talk to a client, event source is a simple and pragmatic way to push information to a client. It's just a never ending page. Twitter fire hose use this tricks before its specification. The client open a http connection and waits for event. The connection is kept open, and reopen if there is some troubles (connection cut, something like that).
No timeout, you can send many events in one connection.
The difference between websocket and eventsource is simple. Websocket is bidirectionnal and hard to implement. Eventsource is unidirectionnal and simple to implement, both client and server side.
You can use eventsource as a zombie controller. Each client connects and reconnect to the master and wait for instruction. When instruction is received, the zombie acts and if needed can talk to its master, with a classical http connection, targeting the django app.
Eventsource keep the connection open, so you need an async server, like tornado. Django need a sync server, so, you need both, with a dispatcher, like nginx. Django or a cron like action talks to the async server, wich talks to the right zombie. Zombie talks to django, so, the async server doesn't need any peristance, it's just a hub with plugged zombies.
Gevent is able to handle such http server but there is no decent doc and examples for this point. It's a shame. I want a car, you give me a screw.
You can also use Tornado + Tornadio + Socket.io. That's what we are using right now for notifications, and the amount of code that you should write is not that much.
from tornadio2 import SocketConnection, TornadioRouter, SocketServer
class RouterConnection(SocketConnection):
__endpoints__ = {'/chat': ChatConnection,
'/ping': PingConnection,
'/notification' : NotificationConnection
}
def on_open(self, info):
print 'Router', repr(info)
MyRouter = TornadioRouter(RouterConnection)
# Create socket application
application = web.Application(
MyRouter.apply_routes([(r"/", IndexHandler),
(r"/socket.io.js", SocketIOHandler)]),
flash_policy_port = 843,
flash_policy_file = op.join(ROOT, 'flashpolicy.xml'),
socket_io_port = 3001,
template_path=os.path.join(os.path.dirname(__file__), "templates/notification")
)
class PingConnection(SocketConnection):
def on_open(self, info):
print 'Ping', repr(info)
def on_message(self, message):
now = dt.utcnow()
message['server'] = [now.hour, now.minute, now.second, now.microsecond / 1000]
self.send(message)
class ChatConnection(SocketConnection):
participants = set()
unique_id = 0
#classmethod
def get_username(cls):
cls.unique_id += 1
return 'User%d' % cls.unique_id
def on_open(self, info):
print 'Chat', repr(info)
# Give user unique ID
self.user_name = self.get_username()
self.participants.add(self)
def on_message(self, message):
pass
def on_close(self):
self.participants.remove(self)
def broadcast(self, msg):
for p in self.participants:
p.send(msg)
here is a really simple solution I could came up with :
import tornado.ioloop
import tornado.web
import time
class MainHandler(tornado.web.RequestHandler):
#tornado.web.asynchronous
def get(self):
self.set_header("Content-Type", "text/event-stream")
self.set_header("Cache-Control", "no-cache")
self.write("Hello, world")
self.flush()
for i in range(0, 5):
msg = "%d<br>" % i
self.write("%s\r\n" % msg) # content
self.flush()
time.sleep(5)
application = tornado.web.Application([
(r"/", MainHandler),
])
if __name__ == "__main__":
application.listen(8888)
tornado.ioloop.IOLoop.instance().start()
and
curl http://localhost:8888
gives output when it comes !
Now, I'll just have to implement the full event-source spec and some kind of data serialization between the server and the clients, but that's trivial. I'll post an URL to the lib I'll write here when it'll be done.
I've recently played with Django, Server-Sent Events and WebSocket, and I've wrote an article about it at http://curella.org/blog/2012/jul/17/django-push-using-server-sent-events-and-websocket/
Of course, this comes with the usual caveats that Django probably isn't the best fit for evented stuff, and both protocols are still drafts.

How to test xmpp/jabber on local machine?

I just started reading Oreilly's XMPP The Definitive Guide and for the hello world, they have this script:
def main():
bot = EchoBot("echobot#wonderland.lit/HelloWorld", "mypass")
bot.run()
class EchoBot(object):
def __init__(self, jid, password):
self.xmpp = sleekxmpp.ClientXMPP(jid, password)
self.xmpp.add_event_handler("session_start", self.handleXMPPConnected)
self.xmpp.add_event_handler("message", self.handleIncomingMessage)
def run(self):
self.xmpp.connect()
self.xmpp.process(threaded=False)
def handleXMPPConnected(self, event):
self.xmpp.sendPresence(pstatus="Send me a message")
def handleIncomingMessage(self, message):
self.xmpp.sendMessage(message["jid"], message["message"])
But it didn't say how to test and run this on my local machine. I'm really new to xmpp and a bit confused. Do I setup a local xmpp server or is there an existing one sitting around where I can test this?
Yes, you probably need to install your own server if you want to test it locally. Many servers have a one-click install system that should make it easy to install on your platform.
It should also work with an hosted XMPP account if you have one on a platform like Google Chat (which is XMPP), or any other on platforms like jabber.org.

Categories