Channels: WebsocketCommunicator does not split out query string

Created on 20 Feb 2018  路  4Comments  路  Source: django/channels

At the moment I'm trying to fix my test cases in channels 2 and found out, that the WebsocketComunicator doesn't consider any query parameters. Is it somehow possible to pass a query parameter into the scope? I implemented a token based authentication middleware which is extracting the token from a query string.

Testing a custom authentication middleware should be possible in Channels. At the moment it is not possible to write a test case for the custom authentication example from the documentation:
http://channels.readthedocs.io/en/latest/topics/authentication.html#custom-authentication

I'm using:

channels==2.0.2
channels-redis==2.0.3
daphne==2.0.3

Thanks for any help or advice.

bug exbeginner

All 4 comments

I faced the same issue lately when started moving the code to using channels==2.0 and here is the workaround I'm using right now:

from channels.testing import WebsocketCommunicator as WebsocketCommunicatorOrig

class WebsocketCommunicator(WebsocketCommunicatorOrig):
    """
    The original websocket communicator does not allow to pass query string to scope, so
    we write our own communicator that allows us to do exactly that (``query`` arg in constructor).
    """

    def __init__(self, application: AsyncConsumer, path: str, headers: Optional[List[str]] = None,
                 subprotocols: Optional[List[str]] = None, query: Optional[str] = None) -> None:
        self.scope = {
            'type': 'websocket',
            'path': path,
            'headers': headers or [],
            'subprotocols': subprotocols or [],
            'query_string': query or '',
        }
        # avoid calling super() here or the query string will be lost
        ApplicationCommunicator.__init__(self, application, self.scope)

This is pretty ugly since you have to avoid calling the channels.testing.WebsocketCommunicator's init. Modifying the scope after the super call will have no effect and there's no way to pass custom args to the scope in WebsocketCommunicator's init. But as long as the WebsocketCommunicator does nothing besides filling the scope, this should work.

Ideally, I'd see the channels.testing.WebsocketCommunicator accepting keyword args for the scope so one can pass whatever he needs to mock the switching of protocols, i.e.

class WebsocketCommunicator(ApplicationCommunicator):

    def __init__(self, application, path, **kwargs):
        headers = kwargs.pop('headers', [])
        subprotocols = kwargs.pop('subprotocols', [])
        self.scope = {
            "type": "websocket",
            "path": path,
            "headers": headers,
            "subprotocols": subprotocols,
            **kwargs
        }
        super().__init__(application, self.scope)

This way, passing of any custom args would be possible:

communicator = WebsocketCommunicator(app, '/endpoint', query_string='?spam=eggs&foo=bar')

@andrewgodwin If you find the suggestion useful, I will gladly submit a pull request for this.

@hoefling thanks for your reply. Maybe I will use something similar to make it working in the meantime till this got fixed.

You're right, it needs to split the query string out from the URL into the scope.

Fixed in #932.

Was this page helpful?
0 / 5 - 0 ratings