Channels: Sent non-empty 'Sec-WebSocket-Protocol' error on Chrome only

Created on 21 Oct 2019  路  11Comments  路  Source: django/channels

Everything is working fine on Firefox/IE, but on Chrome I keep getting the "Sent non-empty 'Sec-WebSocket-Protocol' header but no response was receive" error. Any ideas on why this is happening and how to fix it?

js

var roomid = 'roomname'
var endpoint = 'ws://'+ window.location.host + window.location.pathname
var socket = new WebSocket(endpoint, roomid)

consumers.py
class ChatConsumer(AsyncConsumer):
async def websocket_connect(self, event):
chat_room = str(self.scope['subprotocols']).replace('[','').replace(']','').replace("'",'')
self.chat_room = chat_room
await self.channel_layer.group_add(
chat_room,
self.channel_name)
await self.send({"type": "websocket.accept"}

(Using windows 10, localhost)

Most helpful comment

ok, I figured it out.

On the frontend, this is how I create a websocket:

const websocket = new WebSocket('ws://127.0.0.1:8000/ws/', ['Token', 'user_secret_token'])

This is my consumer:

class MyConsumer(JsonWebsocketConsumer):
    def connect(self):
        self.room_group_name = 'example_room'

        # Join room group
        async_to_sync(self.channel_layer.group_add)(self.room_group_name, self.channel_name)

        # incorrect
        # self.accept()

        # correct
        self.accept('Token')

@jayalen86 @alex-yobota hope this helps you! :)

All 11 comments

There's this StackOverflow post https://stackoverflow.com/questions/34198566/websocket-error-during-websocket-handshake-sent-non-empty-sec-websocket-proto

Daphne should handle that. so...

Are you able to put together a minimal project the recreates you issue? If so I can try to have a look...

Thanks for the response. Being that I got it working in IE and Firefox, I reckon the issue has to do with connecting via local host in chrome. I'm going to go live in a few days and see what's what, but if the issue persists, I will definitely take you up on your offer. Thanks, friend!

OK, I'll close this now. Happy to re-open if we can pin it down to an issue on Channels.

Hello. I'm having a similar issue. Apparently, a common hack for authenticating websockets with token authentication is to put the token in the subprotocol argument of the websocket connection. I've done this, with the following AuthMiddleware:

@database_sync_to_async
def validate_token(token):
    auth = TokenAuthentication()
    return auth.authenticate_credentials(token.encode("utf-8"))


@database_sync_to_async
def close_connections():
    close_old_connections()


class WebsocketTokenAuthMiddleware:
    def __init__(self, inner):
        self.inner = inner

    def __call__(self, scope):
        close_connections()
        try:
            token = scope["subprotocols"][0]
            scope["user"] = validate_token(token)
            return self.inner(scope)
        except Exception:
            pass

This works fine on Firefox, but gives the same error as @jayalen86 was having on Chrome. This is running locally, via Daphne.

Short of some information as to _why_ this doesn't work on Chrome, I don't think there's anything we can say.

Happy to re-open if there's something specific that we can do.

I am having the same problem in Chrome, but not in Firefox and not in Safari.

Looks like Chrome expects to receive the response header sec-websocket-accept from the server.
https://stackoverflow.com/a/28046874
https://stackoverflow.com/questions/42357140/django-channels-how-to-respond-to-a-websocket-open-request-with-a-subprotocol/46582687

I found this issue where sending a response header is mentioned https://github.com/django/channels/issues/928#issuecomment-368180935

await self.send({"type": "websocket.accept", "subprotocol": "Sec-Websocket-Accept"})

but it doesn't seem to work. How can you send the response header sec-websocket-accept?

I hope this helps us figure where the problem is :)

Is there anything in the Autobahn docs or tests about this?

@carltongibson I haven鈥檛 checked those

I'm wondering if it shouldn't be handled at that level. Seems a bit low-level for us? 馃

I agree. Not all applications use that protocol so it shouldn鈥檛 be handled by the channels package in my opinion as well.

I still haven鈥檛 figured out how to send the response header Chrome expects to receive in order to accept the connection on the client side. Any help?

ok, I figured it out.

On the frontend, this is how I create a websocket:

const websocket = new WebSocket('ws://127.0.0.1:8000/ws/', ['Token', 'user_secret_token'])

This is my consumer:

class MyConsumer(JsonWebsocketConsumer):
    def connect(self):
        self.room_group_name = 'example_room'

        # Join room group
        async_to_sync(self.channel_layer.group_add)(self.room_group_name, self.channel_name)

        # incorrect
        # self.accept()

        # correct
        self.accept('Token')

@jayalen86 @alex-yobota hope this helps you! :)

Was this page helpful?
0 / 5 - 0 ratings