Search code examples
pythonwebsockettwistedautobahntwisted.web

How to access Autobahn WebSocketResource from Twisted Root Resource?


I have been searching for many examples to demonstrate how to access a Autobahn Twisted WebSocketResource , but can't seem to find examples that show this.

I understand from this example Autobahn Twisted WebSocketResource example that you instantiate a WebSocketServerFactory, set the websocket protocol and then use WebSocketResource(factory) to create the websocket resource. Once you have the websocket resource, this can be added on the main Twisted Web resource path before creating the Site instance like so:

class WebSocketProtocol(WebSocketServerProtocol):

    def onConnect(self, request):
        print("WebSocket connection request: {}".format(request))

    def onMessage(self, payload, isBinary):
        self.sendMessage(payload, isBinary)

class HttpResource(resource.Resource):
    isLeaf = True

    def render_GET(self, request):
        return "<html><h1>Hello World!</h1></html>"

factory = WebSocketServerFactory(u"ws://127.0.0.1:8000")
factory.protocol = WebSocketProtocol
ws_resource = WebSocketResource(factory)

root = HttpResource()
root.putChild(b"ws", ws_resource)

site = Site(root)

So my understanding is that all requests on ws://127.0.0.1:8000/ws will be routed to the websocket resource. However the /ws resource doesn't appear to be discovered by the browser. The GET requests work fine, but the websocket requests do not.

As far as the websocket requests goes, here is the flow of events that I believe should fix this issue (I am just not sure how to implement them):

  1. Browser sends a GET request with Upgrade to websocket in the header.
  2. render_GET method on HttpResource needs to recognize this in the request and set the response code to 101 and/or locate the ws resource to handle data communication.

How do you switch from the root resource to a child resource so that the websocket can handle websocket requests?

My initial thought was to use the getChild method on the root resource to check for ws. If name is ws then return the websocket resource. I also read here: Twisted Web (isLeaf) that the isLeaf attribute under the root resource class HttpResource cannot be present or you can't access the children on the root resource.

Any help would be great. Thank you so much in advance for any help you can provide.

Cheers!

Brian


Solution

  • After some time reading about Autobahn and Twisted, I have arrived at a snippet of code that works. Autobahn's onConnect method handles the request and peaks into the header if need be.

    class WebSocketProtocol(WebSocketServerProtocol):
    
        def onConnect(self, request):
            custom_header = {}
    
            if request.headers['sec-websocket-key']:
                custom_header['sec-websocket-protocol'] = 'graphql-ws'
            return (None, custom_header)
    
        def onMessage(self, payload, isBinary):
            self.sendMessage(payload, isBinary)
    
    class HttpResource(Resource):
        isLeaf = True
    
        def render_GET(self, request):
            return "<html><h1>Hello World!</h1></html>"
    
    factory = WebSocketServerFactory()
    factory.protocol = WebSocketProtocol
    ws_resource = WebSocketResource(factory)
    
    root = Resource()
    root.putChild("", HttpResource())
    root.putChild(b"ws", ws_resource)
    
    site = Site(root)
    reactor.listenTCP(8000, site)
    
    reactor.run()