Search code examples
python-3.xopenshiftwsgicherrypyws4py

ws4py under cherrypy under WSGI: exception AttributeError: 'mod_wsgi.Input' object has no attribute 'rfile'


I am trying to implement websockets on a openshift.com server (which should support them).

openshift.com provides me a WSGI, so I embed my cherrypy to it, so that my wsgi.pyscript define an application object. Also, cherrypy has a websocket tool, as defined by ws4py.

This is a minimal cherrypy application that works under WSGI in OpenShift, and that should use websockets too!

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

# see http://tools.cherrypy.org/wiki/ModWSGI
cherrypy.config.update({'environment': 'embedded'}) 
if cherrypy.__version__.startswith('3.0') and cherrypy.engine.state == 0:
    cherrypy.engine.start(blocking=False)
    atexit.register(cherrypy.engine.stop)

class Root(object):
    def index(self): return 'I work!'
    def ws(self): print('THIS IS NEVER PRINTED :(')
    index.exposed=True
    ws.exposed=True

# registering the websocket
conf={'/ws':{'tools.websocket.on': True,'tools.websocket.handler_cls': EchoWebSocket}}
WebSocketPlugin(cherrypy.engine).subscribe()
cherrypy.tools.websocket = WebSocketTool()  

#show stacktraces in console (for some reason this is not default in cherrypy+WSGI)
logger = logging.getLogger()
logger.setLevel(logging.INFO)
stream = logging.StreamHandler()
stream.setLevel(logging.INFO)
logger.addHandler(stream)

application = cherrypy.Application(Root(), script_name='', config=conf)

Everything work wonderfully, except when I create a websocket ( connecting to ws://myserver:8000/ws ), this is the stacktrace I get:

 cherrypy/_cplogging.py, 214, HTTP Traceback (most recent call last):
   File "cherrypy/_cprequest.py", line 661, in respond
     self.hooks.run('before_request_body')
   File "cherrypy/_cprequest.py", line 114, in run
     raise exc
   File "cherrypy/_cprequest.py", line 104, in run
     hook()
   File "cherrypy/_cprequest.py", line 63, in __call__
     return self.callback(**self.kwargs)
   File "ws4py/server/cherrypyserver.py", line 200, in upgrade
     ws_conn = get_connection(request.rfile.rfile)
AttributeError: 'mod_wsgi.Input' object has no attribute 'rfile'

(I manually deleted the absolute path from the filenames) PS: I use python3.3, cherrypy==3.5.0, ws4py==0.3.4.

It is not clear to me:

  • if this is a lack of compatibility between cherrypy and ws4py when in an WSGI environment.
  • if it is a problem of ws4py when in a WSGI environment
  • if it is because Openshift websockets have a different port than the http one

PPS: this is a complete OpenShift project, that you can run and try this yourself: https://github.com/spocchio/wsgi-cherrypy-ws4py


Solution

  • I don't think it is possible at all. WSGI is a synchronous protocol (1, 2), WebSocket protocol is asynchronous. Wiki states that for a Python application interface OpenShift uses WSGI (3). Alas.

    However I've recently played with ws4py in pub/sub scenario and it works really well on top of CherryPy standard HTTP-server deployment. So it shouldn't be a problem on a generic virtual server with no application interface constraints.