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

CB-4686 add new client event for updating session info #2462

Merged
Show file tree
Hide file tree
Changes from 11 commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
6c4056d
CB-4810 add new client event for updating session info
yagudin10 Mar 8, 2024
7caa874
CB-4810 update request parameters on creating web session
yagudin10 Mar 12, 2024
1cf0d9e
CB-4810 deprecate update session gql functions
yagudin10 Mar 12, 2024
7738c58
Merge branch 'devel' into CB-4686-migrate-from-scheduled-touch-sessio…
Mar 14, 2024
754639a
CB-4810 fix tests
yagudin10 Mar 15, 2024
df951b2
CB-4810 add params to session state event
yagudin10 Mar 15, 2024
dbd8d10
Merge branch 'devel' into CB-4686-migrate-from-scheduled-touch-sessio…
Mar 15, 2024
4a2eb8c
CB-4810 add params to gql schema
yagudin10 Mar 15, 2024
ad51772
CB-4686 feat: migrates touch session from REST to WebSockets
Mar 16, 2024
2871d56
Merge branch 'devel' into CB-4686-migrate-from-scheduled-touch-sessio…
Mar 18, 2024
1f6ba77
CB-4686 fix: do not open websocket if session is expired
Mar 18, 2024
8b6d9c9
Revert "CB-4686 fix: do not open websocket if session is expired"
Mar 19, 2024
f7f3124
CB-4686 setData uses performUpdate for the SessionResource
Mar 19, 2024
7aeffff
CB-4686 unsubscribes from the websocket when session expired event ar…
Mar 19, 2024
9b57c04
Revert "CB-4686 unsubscribes from the websocket when session expired …
Mar 19, 2024
3204665
Merge branch 'devel' into CB-4686-migrate-from-scheduled-touch-sessio…
yagudin10 Mar 19, 2024
ea0833a
Merge branch 'devel' into CB-4686-migrate-from-scheduled-touch-sessio…
Mar 19, 2024
f3c65a1
CB-4810 rename client event
yagudin10 Mar 19, 2024
09d7a0d
CB-4686 pr fixes
Mar 19, 2024
d0566d9
Merge branch 'devel' into CB-4686-migrate-from-scheduled-touch-sessio…
Wroud Mar 20, 2024
474c7b4
Merge branch 'devel' into CB-4686-migrate-from-scheduled-touch-sessio…
Mar 20, 2024
0530eb4
CB-4686 makes onDataOutdated execution manual
Mar 20, 2024
d247b23
CB-4686 fix: onDataOutdated.execute executes in the end of the perfor…
Mar 21, 2024
02782b7
Merge branch 'devel' into CB-4686-migrate-from-scheduled-touch-sessio…
mr-anton-t Mar 25, 2024
38ec679
Merge branch 'devel' into CB-4686-migrate-from-scheduled-touch-sessio…
yagudin10 Mar 25, 2024
e617a99
Merge branch 'devel' into CB-4686-migrate-from-scheduled-touch-sessio…
dariamarutkina Mar 26, 2024
b22cdd6
Merge branch 'devel' into CB-4686-migrate-from-scheduled-touch-sessio…
dariamarutkina Mar 26, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -125,20 +125,21 @@ public class WebSession extends BaseWebSession
private final Map<String, DBWSessionHandler> sessionHandlers;

public WebSession(
@NotNull HttpSession httpSession,
@NotNull HttpServletRequest request,
@NotNull WebAuthApplication application,
@NotNull Map<String, DBWSessionHandler> sessionHandlers
) throws DBException {
super(httpSession.getId(), application);
super(request.getSession().getId(), application);
this.lastAccessTime = this.createTime;
setLocale(CommonUtils.toString(httpSession.getAttribute(ATTR_LOCALE), this.locale));
setLocale(CommonUtils.toString(request.getSession().getAttribute(ATTR_LOCALE), this.locale));
this.sessionHandlers = sessionHandlers;
//force authorization of anonymous session to avoid access error,
//because before authorization could be called by any request,
//but now 'updateInfo' is called only in special requests,
//and the order of requests is not guaranteed.
//look at CB-4747
refreshSessionAuth();
updateSessionParameters(request);
}

@Nullable
Expand Down Expand Up @@ -525,17 +526,10 @@ public List<WebServerMessage> getSessionMessages() {
}
}

public synchronized void updateInfo(
HttpServletRequest request,
HttpServletResponse response
) throws DBWebException {
public synchronized void updateInfo(boolean isOldHttpSessionUsed) {
log.debug("Update session lifetime " + getSessionId() + " for user " + getUserId());
touchSession();
HttpSession httpSession = request.getSession();
this.lastRemoteAddr = request.getRemoteAddr();
this.lastRemoteUserAgent = request.getHeader("User-Agent");
this.cacheExpired = false;
if (!httpSession.isNew()) {
if (isOldHttpSessionUsed) {
try {
// Persist session
if (!isAuthorizedInSecurityManager()) {
Expand All @@ -555,6 +549,12 @@ public synchronized void updateInfo(
}
}

public synchronized void updateSessionParameters(HttpServletRequest request) {
this.lastRemoteAddr = request.getRemoteAddr();
this.lastRemoteUserAgent = request.getHeader("User-Agent");
this.cacheExpired = false;
}

@Association
public List<WebConnectionInfo> getConnections() {
synchronized (connections) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -566,9 +566,9 @@ extend type Mutation {
closeSession: Boolean

# Refreshes session on server and returns its state
touchSession: Boolean @deprecated(reason: "use updateSession instead")
touchSession: Boolean @deprecated(reason: "use events to update session")
# Refreshes session on server and returns session state
updateSession: SessionInfo! @since(version: "24.0.0")
updateSession: SessionInfo! @since(version: "24.0.0") @deprecated(reason: "use events to update session")

# Refresh session connection list
refreshSessionConnections: Boolean
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,8 @@ enum CBServerEventId {
enum CBClientEventId {
cb_client_topic_subscribe,
cb_client_topic_unsubscribe,
cb_client_projects_active
cb_client_projects_active,
cb_client_touch_session
}

# Client subscribes on topic to receive only related events
Expand Down Expand Up @@ -114,8 +115,12 @@ type WSSocketConnectedEvent implements CBServerEvent {
type WSSessionStateEvent implements CBServerEvent {
id: CBServerEventId!
topicId: CBEventTopic
lastAccessTime: Int!
remainingTime: Int!
isValid: Boolean
isCacheExpired: Boolean
locale: String!
actionParameters: Object
Wroud marked this conversation as resolved.
Show resolved Hide resolved
}

# Session expired event
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@

import io.cloudbeaver.DBWebException;
import io.cloudbeaver.model.session.BaseWebSession;
import io.cloudbeaver.model.session.WebSession;
import io.cloudbeaver.websocket.CBWebSessionEventHandler;
import org.eclipse.jetty.websocket.api.Session;
import org.eclipse.jetty.websocket.api.WriteCallback;
Expand Down Expand Up @@ -76,6 +77,12 @@ public void onWebSocketText(String message) {
this.webSession.getEventsFilter().setSubscribedProjects(projectEvent.getProjectIds());
break;
}
case TOUCH_SESSION: {
if (webSession instanceof WebSession session) {
session.updateInfo(true);
}
break;
}
default:
var e = new DBWebException("Unknown websocket client event: " + clientEvent.getId());
log.error(e.getMessage(), e);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
import org.jkiss.dbeaver.DBException;
import org.jkiss.dbeaver.Log;
import org.jkiss.dbeaver.model.security.exception.SMAccessTokenExpiredException;
import org.jkiss.dbeaver.runtime.DBWorkbench;

import java.nio.ByteBuffer;
import java.nio.charset.StandardCharsets;
Expand All @@ -54,6 +55,7 @@ public Object createWebSocket(@NotNull JettyServerUpgradeRequest request, JettyS
var httpRequest = request.getHttpServletRequest();
var webSession = webSessionManager.getOrRestoreSession(httpRequest);
if (webSession != null) {
webSession.updateSessionParameters(httpRequest);
// web client session
return createNewEventsWebSocket(webSession);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,7 @@ WebSession openSession(
@WebAction(authRequired = false)
boolean touchSession(@NotNull HttpServletRequest request, @NotNull HttpServletResponse servletResponse) throws DBWebException;

@Deprecated
@WebAction(authRequired = false)
WebSession updateSession(@NotNull HttpServletRequest request, @NotNull HttpServletResponse response)
throws DBWebException;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -282,11 +282,13 @@ public boolean closeSession(HttpServletRequest request) throws DBWebException {
}

@Override
@Deprecated
public boolean touchSession(@NotNull HttpServletRequest request, @NotNull HttpServletResponse response) throws DBWebException {
return CBPlatform.getInstance().getSessionManager().touchSession(request, response);
}

@Override
@Deprecated
public WebSession updateSession(@NotNull HttpServletRequest request, @NotNull HttpServletResponse response)
throws DBWebException {
WebSessionManager sessionManager = CBPlatform.getInstance().getSessionManager();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -79,10 +79,12 @@ protected CBApplication getApplication() {
return application;
}

@Deprecated
public boolean touchSession(@NotNull HttpServletRequest request,
@NotNull HttpServletResponse response) throws DBWebException {
WebSession webSession = getWebSession(request, response, false);
webSession.updateInfo(request, response);
webSession.updateSessionParameters(request);
webSession.updateInfo(!request.getSession().isNew());
return true;
}

Expand All @@ -105,14 +107,14 @@ public WebSession getWebSession(
var baseWebSession = sessionMap.get(sessionId);
if (baseWebSession == null && CBApplication.getInstance().isConfigurationMode()) {
try {
webSession = createWebSessionImpl(httpSession);
webSession = createWebSessionImpl(request);
} catch (DBException e) {
throw new DBWebException("Failed to create web session", e);
}
sessionMap.put(sessionId, webSession);
} else if (baseWebSession == null) {
try {
webSession = createWebSessionImpl(httpSession);
webSession = createWebSessionImpl(request);
} catch (DBException e) {
throw new DBWebException("Failed to create web session", e);
}
Expand Down Expand Up @@ -174,7 +176,7 @@ public WebSession getOrRestoreSession(@NotNull HttpServletRequest request) {
return null;
}

webSession = createWebSessionImpl(httpSession);
webSession = createWebSessionImpl(request);
restorePreviousUserSession(webSession, oldAuthInfo);

sessionMap.put(sessionId, webSession);
Expand Down Expand Up @@ -208,8 +210,8 @@ private void restorePreviousUserSession(
}

@NotNull
protected WebSession createWebSessionImpl(@NotNull HttpSession httpSession) throws DBException {
return new WebSession(httpSession, application, getSessionHandlers());
protected WebSession createWebSessionImpl(@NotNull HttpServletRequest request) throws DBException {
return new WebSession(request, application, getSessionHandlers());
}

@NotNull
Expand Down Expand Up @@ -330,7 +332,13 @@ public void sendSessionsStates() {
})
.forEach(session -> {
try {
session.addSessionEvent(new WSSessionStateEvent(session.getRemainingTime(), session.isValid()));
session.addSessionEvent(new WSSessionStateEvent(
session.getLastAccessTimeMillis(),
session.getRemainingTime(),
session.isValid(),
((WebSession) session).isCacheExpired(),
((WebSession) session).getLocale(),
((WebSession) session).getActionParameters()));
} catch (Exception e) {
log.error("Failed to refresh session state: " + session.getSessionId(), e);
}
Expand Down
13 changes: 6 additions & 7 deletions webapp/packages/core-root/src/SessionActivityService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,22 +16,21 @@ export const SESSION_TOUCH_TIME_PERIOD = 1000 * 60;
export class SessionActivityService extends Dependency {
private touchSessionTimer: ReturnType<typeof setTimeout> | null = null;

constructor(private readonly clientActivityService: ClientActivityService, private readonly sessionResource: SessionResource) {
constructor(
private readonly clientActivityService: ClientActivityService,
private readonly sessionResource: SessionResource,
) {
super();
this.notifyClientActivity = this.notifyClientActivity.bind(this);
this.clientActivityService.onActiveStateChange.addHandler(this.notifyClientActivity);
}

private async notifyClientActivity() {
private notifyClientActivity() {
if (this.touchSessionTimer || !this.clientActivityService.isActive) {
return;
}

try {
await this.sessionResource.updateSession();
} catch (e) {
console.error('Session update error', e);
}
this.sessionResource.updateSession();

this.touchSessionTimer = setTimeout(() => {
if (this.touchSessionTimer) {
Expand Down
26 changes: 24 additions & 2 deletions webapp/packages/core-root/src/SessionEventSource.ts
Original file line number Diff line number Diff line change
Expand Up @@ -76,10 +76,12 @@ export class SessionEventSource implements IServerEventEmitter<ISessionEvent, IS

this.openSubject.subscribe(() => {
this.onInit.execute();
this.disconnected = false;
Wroud marked this conversation as resolved.
Show resolved Hide resolved
});

this.closeSubject.subscribe(event => {
console.info(`Websocket closed: ${event.reason}`);
this.disconnect();
});

this.eventsSubject = merge(this.oldEventsSubject, this.subject);
Expand Down Expand Up @@ -120,8 +122,8 @@ export class SessionEventSource implements IServerEventEmitter<ISessionEvent, IS
multiplex<T = ISessionEvent>(topicId: SessionEventTopic, mapTo: (event: ISessionEvent) => T = e => e as T): Observable<T> {
return merge(
this.subject.multiplex(
() => ({ id: ClientEventId.CbClientTopicSubscribe, topicId } as ITopicSubEvent),
() => ({ id: ClientEventId.CbClientTopicUnsubscribe, topicId } as ITopicSubEvent),
() => ({ id: ClientEventId.CbClientTopicSubscribe, topicId }) as ITopicSubEvent,
() => ({ id: ClientEventId.CbClientTopicUnsubscribe, topicId }) as ITopicSubEvent,
event => event.topicId === topicId,
),
this.oldEventsSubject,
Expand All @@ -135,6 +137,26 @@ export class SessionEventSource implements IServerEventEmitter<ISessionEvent, IS

disconnect() {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

https://rxjs.dev/api/webSocket/webSocket - the rxjs websocket is tried to reconnect cause there was some subscriptions

this.disconnected = true;

if (!this.subject.closed) {
this.subject.unsubscribe();
}

if (!this.closeSubject.closed) {
this.closeSubject.unsubscribe();
}

if (!this.openSubject.closed) {
this.openSubject.unsubscribe();
}

if (!this.oldEventsSubject.closed) {
this.oldEventsSubject.unsubscribe();
}

if (!this.errorSubject.closed) {
this.errorSubject.unsubscribe();
}
Wroud marked this conversation as resolved.
Show resolved Hide resolved
}

private handleErrors() {
Expand Down
8 changes: 6 additions & 2 deletions webapp/packages/core-root/src/SessionInfoEventHandler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,10 @@
* you may not use this file except in compliance with the License.
*/
import { injectable } from '@cloudbeaver/core-di';
import type { WsSessionStateEvent as ISessionStateEvent } from '@cloudbeaver/core-sdk';
import { type CbClientEvent, CbEventTopic, type WsSessionStateEvent as ISessionStateEvent } from '@cloudbeaver/core-sdk';

import { TopicEventHandler } from './ServerEventEmitter/TopicEventHandler';
import { ISessionEvent, SessionEventId, SessionEventSource, SessionEventTopic } from './SessionEventSource';
import { ClientEventId, ISessionEvent, SessionEventId, SessionEventSource, SessionEventTopic } from './SessionEventSource';

export { type ISessionStateEvent };

Expand All @@ -19,6 +19,10 @@ export class SessionInfoEventHandler extends TopicEventHandler<ISessionStateEven
super(SessionEventTopic.CbSession, sessionEventSource);
}

updateSession(): void {
this.emit<CbClientEvent>({ id: ClientEventId.CbClientTouchSession, topicId: CbEventTopic.CbSession });
}

map(event: any): ISessionStateEvent {
return event;
}
Expand Down
Loading
Loading