Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

WebSocket disconnects and does not return the response when authentication enabled #812

Open
1 of 3 tasks
mikitakandratsiuk opened this issue Jun 27, 2023 · 6 comments
Open
1 of 3 tasks
Labels
bug Something isn't working

Comments

@mikitakandratsiuk
Copy link

Please provide us with the following information:

This issue is a: (mark with an x)

  • bug report -> please search issues before submitting
  • documentation issue or request
  • regression (a behavior that used to work and stopped in a new release)

Issue description

WebSocket disconnects and does not return the response when authentication enabled.

I have a sample Web Socket echo server mikitakandratsiuk/sample-websocket written in Python which appends all submitted messages on the page. It works fine in the ACA when authentication is disabled.

When the authentication is enabled (Azure AD), the WebSocket connection is being established, but when submitting a message the response is not returned (i.e. message is not appended on the page) and connection is closed with the exception on the server side (see below).

ERROR:    Exception in ASGI application
Traceback (most recent call last):
  File "/usr/local/lib/python3.10/site-packages/uvicorn/protocols/websockets/websockets_impl.py", line 254, in run_asgi
    result = await self.app(self.scope, self.asgi_receive, self.asgi_send)
  File "/usr/local/lib/python3.10/site-packages/uvicorn/middleware/proxy_headers.py", line 78, in __call__
    return await self.app(scope, receive, send)
  File "/usr/local/lib/python3.10/site-packages/fastapi/applications.py", line 284, in __call__
    await super().__call__(scope, receive, send)
  File "/usr/local/lib/python3.10/site-packages/starlette/applications.py", line 122, in __call__
    await self.middleware_stack(scope, receive, send)
  File "/usr/local/lib/python3.10/site-packages/starlette/middleware/errors.py", line 149, in __call__
    await self.app(scope, receive, send)
  File "/usr/local/lib/python3.10/site-packages/starlette/middleware/httpsredirect.py", line 19, in __call__
    await self.app(scope, receive, send)
  File "/usr/local/lib/python3.10/site-packages/starlette/middleware/exceptions.py", line 79, in __call__
    raise exc
  File "/usr/local/lib/python3.10/site-packages/starlette/middleware/exceptions.py", line 68, in __call__
    await self.app(scope, receive, sender)
  File "/usr/local/lib/python3.10/site-packages/fastapi/middleware/asyncexitstack.py", line 20, in __call__
    raise e
  File "/usr/local/lib/python3.10/site-packages/fastapi/middleware/asyncexitstack.py", line 17, in __call__
    await self.app(scope, receive, send)
  File "/usr/local/lib/python3.10/site-packages/starlette/routing.py", line 718, in __call__
    await route.handle(scope, receive, send)
  File "/usr/local/lib/python3.10/site-packages/starlette/routing.py", line 341, in handle
    await self.app(scope, receive, send)
  File "/usr/local/lib/python3.10/site-packages/starlette/routing.py", line 82, in app
    await func(session)
  File "/usr/local/lib/python3.10/site-packages/fastapi/routing.py", line 292, in app
    await dependant.call(**values)
  File "/app/app.py", line 27, in websocket_endpoint
    data = await websocket.receive_text()
  File "/usr/local/lib/python3.10/site-packages/starlette/websockets.py", line 113, in receive_text
    self._raise_on_disconnect(message)
  File "/usr/local/lib/python3.10/site-packages/starlette/websockets.py", line 105, in _raise_on_disconnect
    raise WebSocketDisconnect(message["code"])
starlette.websockets.WebSocketDisconnect: 1002

Steps to reproduce

  1. Deploy a websocket enabled Container App from this sample code (used Azure Portal) with Ingress enabled.
  2. Test the WebSocket connection by pointing the browser to the container app: https://**********.germanywestcentral.azurecontainerapps.io/ws. It opens a WebSocket connection. When submit a text message into the form, it is sent to WebSocket and response is appended on the page.
  3. Add an authentication provider to the Container App (used Azure AD), and enable it (used Azure Portal).
  4. Go to the /ws page again. In the browser console log note that WebSocket connection is established. Submit a text message. Note that response is not returned from the WebSocket and not appended on the page. Additionally note the disconnect exception in the Container App logs.

Expected behavior [What you expected to happen.]
Application with WebSocket and enabled authentication works fine (submitted messages are returned and appended on the page), same as it works when the authentication is disabled.

Actual behavior [What actually happened.]
WebSocket disconnects when the message is submitted.

Screenshots
authentication-issue

Additional context

All services are spined up using Azure Portal.

Additionally, I have followed the issue #549. I tried to run jmalloc/echo-server, and it works fine with and without authentication. However, I'm not sure it is the same problem.

@ahmelsayed ahmelsayed added the investigating currently looking into the issue label Jun 27, 2023
@ahmelsayed
Copy link
Member

Thanks for the repro @mikitakandratsiuk.

I'm still looking at it, so I don't have much of an update. But I can repro it and setting uvicorn log_level to trace shows that the client -> server message is reaching it, but it gets 1002 ProtocolError when it's trying to write back

DEBUG:    < GET /ws HTTP/1.1
DEBUG:    < cache-control: no-cache
DEBUG:    < pragma: no-cache
DEBUG:    < connection: Upgrade
DEBUG:    < upgrade: websocket
DEBUG:    < sec-websocket-version: 13
[...]
DEBUG:    > HTTP/1.1 101 Switching Protocols
DEBUG:    > Upgrade: websocket
DEBUG:    > Connection: Upgrade
DEBUG:    > server: uvicorn
INFO:     connection open
DEBUG:    = connection is OPEN

DEBUG:    < TEXT 'Hello' [5 bytes]
TRACE:    ...:0 - ASGI [5] Receive {'type': 'websocket.receive', 'text': '<5 chars>'}
TRACE:    ...:0 - ASGI [5] Send {'type': 'websocket.send', 'bytes': '<26 bytes>'}
DEBUG:    > TEXT 'web socket response: Hello' [26 bytes]
DEBUG:    < CLOSE 1002 (protocol error) [2 bytes]
DEBUG:    = connection is CLOSING
DEBUG:    > CLOSE 1002 (protocol error) [2 bytes]
DEBUG:    x half-closing TCP connection

TRACE:    127.0.0.1:50996 - WebSocket connection lost
DEBUG:    = connection is CLOSED
TRACE:    ...:0 - ASGI [5] Receive {'type': 'websocket.disconnect', 'code': 1002}
TRACE:    ...:0 - ASGI [5] Raised exception
ERROR:    Exception in ASGI application 
[...]

I can also see from the edge loadbalancer that the upstream disconnected. so it's definitely something isolated inside the auth service that's rejecting the write from the python app. I've contacted the owners of the auth service as I don't see any helpful logs from there. Will update when I have an update

@mikitakandratsiuk
Copy link
Author

Hi @ahmelsayed,
do you have any news or updates about the issue? Thanks!

@fnavarrete83
Copy link

Hi @ahmelsayed,
also interested in a solution to this issue with the websockets on authentication. Any news?

@ahmelsayed
Copy link
Member

ahmelsayed commented Jul 11, 2023

Sorry for the delay.

It seems that the problem is in the auth proxy not supporting compression; websocket's Sec-WebSocket-Extensions: permessage-deflate header which chrome/firefox set by default. I think the go server doesn't do compression by default either.

For a workaround you can disable compression in uvicorn by setting ws_per_message_deflate=False

e.g:

if __name__ == "__main__":
    uvicorn.run(app, host="0.0.0.0", port=8000, ws_per_message_deflate=False)

@mikitakandratsiuk
Copy link
Author

Hi @ahmelsayed,
this workaround worked and I can proceed with it. Thanks a lot!

Although, in my case it required some downstream workarounds, because I use third-party libraries and don't have direct access to uvicorn.Config object. So I'm wondering if there are plans to resolve the issue in a long-term?

Thanks again for the support!

@ahmelsayed ahmelsayed added bug Something isn't working and removed investigating currently looking into the issue labels Jul 13, 2023
@ahmelsayed
Copy link
Member

Yes, I think it should ideally just work. So will mark it as bug for now

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working
Projects
None yet
Development

No branches or pull requests

3 participants