Pregunta

I am trying to dynamically create/destroy "websocket-capable" paths in CherryPy, using ws4py. Here is a full program that demonstrates the problem:

import cherrypy
from ws4py.server.cherrypyserver import WebSocketPlugin, WebSocketTool
from ws4py.websocket import EchoWebSocket, WebSocket

class Nothing(object):
    @cherrypy.expose
    def index(self):
        pass

class Root(object):
    @cherrypy.expose
    def index(self):
        return "Tweep tweep!"

    @cherrypy.expose
    def add(self, bird):
        # Try to create a new websocket-capable path.
        cherrypy.tree.mount(Nothing(), "/bird/" + bird, config={"": {"tools.websocket.on": True, "tools.websocket.handler_cls": EchoWebSocket}})

    @cherrypy.expose
    def remove(self, bird):
        # Remove a previously created websocket-capable path.
        del cherrypy.tree.apps["/bird/" + bird]

    @cherrypy.expose
    def other(self):
        pass

cherrypy.config.update({"server.socket_host": "localhost", "server.socket_port": 9000})
WebSocketPlugin(cherrypy.engine).subscribe()
cherrypy.tools.websocket = WebSocketTool()

cherrypy.quickstart(Root(), "/", config={"/other": {"tools.websocket.on": True,"tools.websocket.handler_cls": EchoWebSocket}})

This is as simple an example as I could construct: the Root class is placed as the main application, along with a ws4py config directive to allow for creation of websockets at ws://localhost:9000/other. The add() method creates a new application and mounts it at an appropriate path, to mimic the setup of the "/other" application.

After starting the server, I can do this in Chrome's JavaScript console:

> w = new WebSocket("ws://localhost:9000/other")
WebSocket {binaryType: "blob", extensions: "", protocol: "", onclose: null, onerror: null…}
> w.onmessage = function (d) { console.log(d.data); }
function (d) { console.log(d.data); }
> w.send("testing 1 2 3")
true
testing 1 2 3

Fantastic, it works!

Now, after visiting http://localhost:9000/add/eagle in my browser (to cause the creation of a new path), I get the following exchange in the console:

> w = new WebSocket("ws://localhost:9000/bird/eagle")
WebSocket {binaryType: "blob", extensions: "", protocol: "", onclose: null, onerror: null…}
WebSocket connection to 'ws://localhost:9000/bird/eagle' failed: Unexpected response code: 301

Hmm... why do I get a 301? Just to show the difference between "/bird/eagle" and some other path that I didn't "create" using the "add" path:

> w = new WebSocket("ws://localhost:9000/bird/pelican")
WebSocket {binaryType: "blob", extensions: "", protocol: "", onclose: null, onerror: null…}
WebSocket connection to 'ws://localhost:9000/bird/pelican' failed: Unexpected response code: 404

The 404 makes sense; there is no such path on the server. But why do I get a 301 after mounting a new app specifically for this websocket creation purpose? Why does it behave differently from the one set up at server start time (on path "/other")? And what might I do differently to accomplish this behavior that I'm after?

¿Fue útil?

Solución

Though I don't understand why I get that 301, I did figure out how to actually make this work the way I want it to. The trick seems to be that I can't install a websocket handler on the "index" path of a handler object. Instead, you need to do it on some other named path. My example program changed in just two places:

(1) The "Nothing" class gained a new method:

@cherrypy.expose
def ws(self):
    pass

(2) The line that mounts the dynamically created handler changes its mount point:

cherrypy.tree.mount(Nothing(), "/bird/" + bird, config={"/ws": {"tools.websocket.on": True, "tools.websocket.handler_cls": EchoWebSocket}})

Now things work very nicely. I can add a bird at the "add/" URL, then instantiate a websocket object from the console and communicate through it, and then finally I can remove the bird at the "remove/" URL.

Otros consejos

The 301 is a default behavior of CherryPy when a request hits an index() page handler indeed/ So /bird would redirect to /bird/ if the handler for it was index().

You can disable this behavior by disabling the trailing_slah tool for the class where the index is defined.

tools.trailing_slash.on: False

This ought to do the trick.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top