Skip to content

Commit 71a8e8f

Browse files
fix(socket): set environ when restoring websocket session (#2689)
Hi, I encountered this behavior when using `cl.context.session.environ` to get the HTTP headers. It seemed like the headers were outdated when I used them - and after going through the `connect` function it seemed like `environ` is not passed to the `restore_existing_session` function, which means that the updated socket won't necessarily have the updated environment variables. If there's an intentional reason for this behavior that I'm missing, I'd appreciate some context :) And one more question - I didn't dive deeply into the authentication flow, but do you think I should also update the user when restoring the session? for now I left it unchanged. Thanks for the help! <!-- This is an auto-generated description by cubic. --> --- ## Summary by cubic Restored websocket sessions now receive the current request environ so headers and context stay up to date after reconnects. This prevents stale header usage in session logic. - **Bug Fixes** - Added an environ parameter to restore_existing_session and set session.environ during restore. - Passed environ when restoring a session and updated tests to assert environ propagation and not-found behavior. <sup>Written for commit 0b840fe. Summary will update automatically on new commits.</sup> <!-- End of auto-generated description by cubic. --> --------- Co-authored-by: Josh Hayes <[email protected]>
1 parent 85a4f87 commit 71a8e8f

File tree

3 files changed

+14
-10
lines changed

3 files changed

+14
-10
lines changed

backend/chainlit/socket.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -38,12 +38,13 @@ class WebSocketSessionAuth(TypedDict):
3838
threadId: str | None
3939

4040

41-
def restore_existing_session(sid, session_id, emit_fn, emit_call_fn):
41+
def restore_existing_session(sid, session_id, emit_fn, emit_call_fn, environ):
4242
"""Restore a session from the sessionId provided by the client."""
4343
if session := WebsocketSession.get_by_id(session_id):
4444
session.restore(new_socket_id=sid)
4545
session.emit = emit_fn
4646
session.emit_call = emit_call_fn
47+
session.environ = environ
4748
return True
4849
return False
4950

@@ -149,7 +150,7 @@ def emit_call_fn(event: Literal["ask", "call_fn"], data, timeout):
149150
return sio.call(event, data, timeout=timeout, to=sid)
150151

151152
session_id = auth["sessionId"]
152-
if restore_existing_session(sid, session_id, emit_fn, emit_call_fn):
153+
if restore_existing_session(sid, session_id, emit_fn, emit_call_fn, environ):
153154
return True
154155

155156
user_env_string = auth.get("userEnv", None)

backend/tests/test_socket.py

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -118,25 +118,29 @@ def test_restore_existing_session_success(self):
118118
mock_session = Mock(spec=WebsocketSession)
119119
emit_fn = Mock()
120120
emit_call_fn = Mock()
121+
environ = {"HTTP_COOKIE": "token=token"}
121122

122123
with patch.object(WebsocketSession, "get_by_id") as mock_get:
123124
mock_get.return_value = mock_session
124125

125126
result = restore_existing_session(
126-
"new_sid", "session_123", emit_fn, emit_call_fn
127+
"new_sid", "session_123", emit_fn, emit_call_fn, environ
127128
)
128129

129130
assert result is True
130131
mock_session.restore.assert_called_once_with(new_socket_id="new_sid")
131132
assert mock_session.emit == emit_fn
132133
assert mock_session.emit_call == emit_call_fn
134+
assert mock_session.environ == environ
133135

134136
def test_restore_existing_session_not_found(self):
135137
"""Test when session is not found."""
136138
with patch.object(WebsocketSession, "get_by_id") as mock_get:
137139
mock_get.return_value = None
138140

139-
result = restore_existing_session("new_sid", "session_123", Mock(), Mock())
141+
result = restore_existing_session(
142+
"new_sid", "session_123", Mock(), Mock(), {"HTTP_COOKIE": "token=token"}
143+
)
140144

141145
assert result is False
142146

@@ -407,7 +411,7 @@ def test_restore_existing_session_with_none_session_id(self):
407411
with patch.object(WebsocketSession, "get_by_id") as mock_get:
408412
mock_get.return_value = None
409413

410-
result = restore_existing_session(None, None, Mock(), Mock())
414+
result = restore_existing_session(None, None, Mock(), Mock(), None)
411415

412416
assert result is False
413417

cypress/e2e/header_auth/spec.cy.ts

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -15,11 +15,6 @@ describe('Header auth', () => {
1515
req.headers['test-header'] = 'test header value';
1616
req.reply();
1717
}).as('auth');
18-
19-
// Only intercept /user _after_ we're logged in.
20-
cy.wait('@auth').then(() => {
21-
cy.intercept('GET', '/user').as('user');
22-
});
2318
};
2419

2520
beforeEach(() => {
@@ -55,6 +50,10 @@ describe('Header auth', () => {
5550
shouldBeLoggedIn();
5651

5752
it('should request and have access to /user', () => {
53+
// Only intercept /user _after_ we're logged in.
54+
cy.wait('@auth').then(() => {
55+
cy.intercept('GET', '/user').as('user');
56+
});
5857
cy.wait('@user').then((interception) => {
5958
expect(interception.response, 'Intercepted response').to.satisfy(
6059
() => true

0 commit comments

Comments
 (0)