From 70ce071c595edecde1e2d6ada01f88a0d68e4d86 Mon Sep 17 00:00:00 2001 From: Viktor Kirst Date: Thu, 16 Jul 2026 14:21:09 +0400 Subject: [PATCH] dbeaver/pro#9768 Fix long-polling session cookie after session ID rotation --- .../websockets/CBEventsLongPolling.java | 12 +++++-- .../CBEventsLongPollingServlet.java | 34 +++++++++---------- 2 files changed, 26 insertions(+), 20 deletions(-) diff --git a/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/server/websockets/CBEventsLongPolling.java b/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/server/websockets/CBEventsLongPolling.java index 9f147524a7b..3548922d7c4 100644 --- a/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/server/websockets/CBEventsLongPolling.java +++ b/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/server/websockets/CBEventsLongPolling.java @@ -1,6 +1,6 @@ /* * DBeaver - Universal Database Manager - * Copyright (C) 2010-2024 DBeaver Corp and others + * Copyright (C) 2010-2026 DBeaver Corp and others * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -44,12 +44,17 @@ public class CBEventsLongPolling implements CBWebSessionEventHandler { private final BlockingQueue queue = new LinkedBlockingQueue<>(QUEUE_CAPACITY); private final CBClientEventProcessor processor; private volatile long lastPoll; + private final CBEventsLongPollingServlet servlet; - public CBEventsLongPolling(@NotNull BaseWebSession webSession) { + public CBEventsLongPolling( + @NotNull BaseWebSession webSession, + @NotNull CBEventsLongPollingServlet owner + ) { this.webSession = webSession; this.lastPoll = System.currentTimeMillis(); this.webSession.addEventHandler(this); this.processor = new CBClientEventProcessor(this.webSession); + this.servlet = owner; } @NotNull @@ -90,8 +95,10 @@ public List pollEvents(long timeoutSec) throws InterruptedException { @Override public void migrateToSession(@NotNull BaseWebSession newSession) { + String oldSid = this.webSession.getSessionId(); this.webSession = newSession; this.processor.setWebSession(newSession); + servlet.rekeySession(oldSid, newSession.getSessionId()); } @Override @@ -112,6 +119,7 @@ public void close() { queue.clear(); } + @NotNull @Override public String toString() { return "CBEventsLongPolling{" + diff --git a/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/server/websockets/CBEventsLongPollingServlet.java b/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/server/websockets/CBEventsLongPollingServlet.java index 700a2cad8c9..fdc5eaf5769 100644 --- a/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/server/websockets/CBEventsLongPollingServlet.java +++ b/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/server/websockets/CBEventsLongPollingServlet.java @@ -1,6 +1,6 @@ /* * DBeaver - Universal Database Manager - * Copyright (C) 2010-2025 DBeaver Corp and others + * Copyright (C) 2010-2026 DBeaver Corp and others * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -83,7 +83,7 @@ protected void doGet(@NotNull HttpServletRequest req, @NotNull HttpServletRespon return; } - CBEventsLongPolling ps = getOrCreatePollSession(ws); + CBEventsLongPolling ps = getOrCreatePollSession(ws, resp); ps.onPoll(); List events = ps.pollEvents(POLL_TIMEOUT_SEC); @@ -111,7 +111,7 @@ protected void doPost(@NotNull HttpServletRequest req, @NotNull HttpServletRespo return; } - CBEventsLongPolling ps = getOrCreatePollSession(ws); + CBEventsLongPolling ps = getOrCreatePollSession(ws, resp); ps.onUserActivity(); String json = new String(req.getInputStream().readAllBytes(), StandardCharsets.UTF_8); @@ -163,18 +163,7 @@ protected BaseWebSession resolveSessionOrSendError( ) { try { - BaseWebSession ws = resolveSession(req); - - resp.setHeader(WSConstants.WS_SESSION_HEADER, ws.getSessionId()); - ServletAppUtils.addResponseCookie( - req, - resp, - CBConstants.CB_SESSION_COOKIE_NAME, - ws.getSessionId(), - -1 - ); - - return ws; + return resolveSession(req); } catch (SMException e) { log.debug("LP request rejected: access token expired"); sendError(resp, HttpConstants.CODE_TOKEN_EXPIRED, e.getMessage()); @@ -214,7 +203,7 @@ protected BaseWebSession resolveSession(@NotNull HttpServletRequest req) throws } @NotNull - private CBEventsLongPolling getOrCreatePollSession(@NotNull BaseWebSession ws) { + private CBEventsLongPolling getOrCreatePollSession(@NotNull BaseWebSession ws, @NotNull HttpServletResponse resp) { final String sid = ws.getSessionId(); return sessions.compute(sid, (key, existing) -> { @@ -222,9 +211,10 @@ private CBEventsLongPolling getOrCreatePollSession(@NotNull BaseWebSession ws) { return existing; } - CBEventsLongPolling ps = new CBEventsLongPolling(ws); + CBEventsLongPolling ps = new CBEventsLongPolling(ws, this); ps.handleWebSessionEvent(new WSSocketConnectedEvent(ws.getApplication().getApplicationRunId())); + resp.setHeader(WSConstants.WS_SESSION_HEADER, sid); log.debug("HTTP Long-Poll channel opened for session " + sid); return ps; @@ -253,7 +243,8 @@ public static void sendError( @NotNull protected static WebHttpRequestInfo createRequestInfo( - @Nullable String sessionId, @NotNull HttpServletRequest req + @Nullable String sessionId, + @NotNull HttpServletRequest req ) { return new WebHttpRequestInfo( sessionId, @@ -263,6 +254,13 @@ protected static WebHttpRequestInfo createRequestInfo( ); } + void rekeySession(@NotNull String oldSid, @NotNull String newSid) { + CBEventsLongPolling moved = sessions.get(oldSid); + if (moved != null) { + sessions.put(newSid, moved); + } + } + /** * Resolves a {@link BaseWebSession} from an incoming HTTP request. *