Skip to content

Commit

Permalink
dbeaver/pro#3807 jakarta websocket
Browse files Browse the repository at this point in the history
  • Loading branch information
alexander-skoblikov committed Dec 18, 2024
1 parent 48e0cda commit 5b10cdc
Show file tree
Hide file tree
Showing 16 changed files with 307 additions and 262 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -17,22 +17,33 @@
package io.cloudbeaver.model.session;

import jakarta.servlet.http.HttpServletRequest;
import org.jkiss.code.Nullable;

public class WebHttpRequestInfo {
public static final String USER_AGENT = "User-Agent";

@Nullable
private final String id;
@Nullable
private final Object locale;
@Nullable
private final String lastRemoteAddress;
@Nullable
private final String lastRemoteUserAgent;

public WebHttpRequestInfo(HttpServletRequest request) {
this.id = request.getSession().getId();
this.id = request.getSession() == null ? null : request.getSession().getId();
this.locale = request.getAttribute("locale");
this.lastRemoteAddress = request.getRemoteAddr();
this.lastRemoteUserAgent = request.getHeader("User-Agent");
this.lastRemoteUserAgent = request.getHeader(USER_AGENT);
}

public WebHttpRequestInfo(String id, Object locale, String lastRemoteAddress, String lastRemoteUserAgent) {
public WebHttpRequestInfo(
@Nullable String id,
@Nullable Object locale,
@Nullable String lastRemoteAddress,
@Nullable String lastRemoteUserAgent
) {
this.id = id;
this.locale = locale;
this.lastRemoteAddress = lastRemoteAddress;
Expand All @@ -43,14 +54,17 @@ public String getId() {
return id;
}

@Nullable
public Object getLocale() {
return locale;
}

@Nullable
public String getLastRemoteAddress() {
return lastRemoteAddress;
}

@Nullable
public String getLastRemoteUserAgent() {
return lastRemoteUserAgent;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@
import io.cloudbeaver.server.jobs.SessionStateJob;
import io.cloudbeaver.server.jobs.WebDataSourceMonitorJob;
import io.cloudbeaver.server.jobs.WebSessionMonitorJob;
import io.cloudbeaver.service.session.CBSessionManager;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.jkiss.code.NotNull;
Expand Down Expand Up @@ -84,6 +83,7 @@ protected synchronized void initialize() {
}

protected void scheduleServerJobs() {
super.scheduleServerJobs();
new WebSessionMonitorJob(this, application.getSessionManager())
.scheduleMonitor();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,24 +20,22 @@
import io.cloudbeaver.registry.WebServiceRegistry;
import io.cloudbeaver.server.CBApplication;
import io.cloudbeaver.server.CBConstants;
import io.cloudbeaver.server.WebApplication;
import io.cloudbeaver.server.graphql.GraphQLEndpoint;
import io.cloudbeaver.server.servlets.CBImageServlet;
import io.cloudbeaver.server.servlets.CBStaticServlet;
import io.cloudbeaver.server.servlets.WebStatusServlet;
import io.cloudbeaver.server.websockets.CBJettyWebSocketManager;
import io.cloudbeaver.server.websockets.CBEventsWebSocket;
import io.cloudbeaver.server.websockets.CBWebSocketServerConfigurator;
import io.cloudbeaver.service.DBWServiceBindingServlet;
import io.cloudbeaver.service.DBWServiceBindingWebSocket;
import jakarta.websocket.server.ServerEndpointConfig;
import org.eclipse.jetty.ee10.servlet.ErrorPageErrorHandler;
import org.eclipse.jetty.ee10.servlet.ServletContextHandler;
import org.eclipse.jetty.ee10.servlet.ServletHolder;
import org.eclipse.jetty.ee10.servlet.ServletMapping;
import org.eclipse.jetty.ee10.websocket.jakarta.server.config.JakartaWebSocketServletContainerInitializer;
import org.eclipse.jetty.server.*;
import org.eclipse.jetty.session.DefaultSessionCache;
import org.eclipse.jetty.session.DefaultSessionIdManager;
import org.eclipse.jetty.session.NullSessionDataStore;
import org.eclipse.jetty.util.resource.ResourceFactory;
import org.eclipse.jetty.websocket.server.WebSocketUpgradeHandler;
import org.eclipse.jetty.xml.XmlConfiguration;
import org.jkiss.code.NotNull;
import org.jkiss.code.Nullable;
Expand All @@ -48,7 +46,6 @@
import java.net.InetSocketAddress;
import java.nio.file.Files;
import java.nio.file.Path;
import java.time.Duration;
import java.util.Arrays;

public class CBJettyServer {
Expand Down Expand Up @@ -137,7 +134,7 @@ public void runServer() {
}

CBJettyWebSocketContext webSocketContext = new CBJettyWebSocketContext(server, servletContextHandler);
for (DBWServiceBindingWebSocket<CBApplication> wsb : WebServiceRegistry.getInstance()
for (DBWServiceBindingWebSocket wsb : WebServiceRegistry.getInstance()
.getWebServices(DBWServiceBindingWebSocket.class)
) {
if (wsb.isApplicable(this.application)) {
Expand All @@ -149,16 +146,16 @@ public void runServer() {
}
}

WebSocketUpgradeHandler webSocketHandler = WebSocketUpgradeHandler.from(server, servletContextHandler, (wsContainer) -> {
wsContainer.setIdleTimeout(Duration.ofMinutes(5));
// Add websockets
wsContainer.addMapping(
serverConfiguration.getServicesURI() + "ws",
new CBJettyWebSocketManager(this.application.getSessionManager())
);
}
);
servletContextHandler.insertHandler(webSocketHandler);
JakartaWebSocketServletContainerInitializer.configure(servletContextHandler, (context, container) -> {
// Add echo endpoint to server container
ServerEndpointConfig eventWsEnpoint = ServerEndpointConfig.Builder
.create(
CBEventsWebSocket.class,
serverConfiguration.getServicesURI() + "ws"
).configurator(new CBWebSocketServerConfigurator(application.getSessionManager()))
.build();
container.addEndpoint(eventWsEnpoint);
});

JettyUtils.initSessionManager(
this.application.getMaxSessionIdleTime(),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,6 @@
import io.cloudbeaver.registry.WebHandlerRegistry;
import io.cloudbeaver.registry.WebServletHandlerDescriptor;
import io.cloudbeaver.server.CBApplication;
import io.cloudbeaver.server.CBPlatform;
import jakarta.servlet.ServletException;
import jakarta.servlet.annotation.WebServlet;
import jakarta.servlet.http.HttpServletRequest;
Expand All @@ -49,7 +48,6 @@
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.ByteBuffer;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
Expand Down Expand Up @@ -227,7 +225,7 @@ private void patchStaticContentIfNeeded(HttpServletRequest request, HttpServletR
response.setHeader(HttpHeader.CACHE_CONTROL.toString(), "no-cache, no-store, must-revalidate");
response.setHeader(HttpHeader.CONTENT_TYPE.toString(), MimeTypes.TEXT_HTML);
response.setHeader(HttpHeader.EXPIRES.toString(), "0");
response.getOutputStream().write(ByteBuffer.wrap(indexBytes));
response.getOutputStream().write(indexBytes);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -22,22 +22,18 @@
import io.cloudbeaver.registry.WebHandlerRegistry;
import io.cloudbeaver.registry.WebSessionHandlerDescriptor;
import io.cloudbeaver.server.CBApplication;
import io.cloudbeaver.server.CBConstants;
import io.cloudbeaver.server.WebAppSessionManager;
import io.cloudbeaver.server.events.WSWebUtils;
import io.cloudbeaver.service.DBWSessionHandler;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.servlet.http.HttpSession;
import org.eclipse.jetty.server.Request;
import org.eclipse.jetty.server.Session;
import org.jkiss.code.NotNull;
import org.jkiss.code.Nullable;
import org.jkiss.dbeaver.DBException;
import org.jkiss.dbeaver.Log;
import org.jkiss.dbeaver.model.auth.SMAuthInfo;
import org.jkiss.dbeaver.model.security.user.SMAuthPermissions;
import org.jkiss.dbeaver.model.websocket.WSConstants;
import org.jkiss.dbeaver.model.websocket.event.WSUserDeletedEvent;
import org.jkiss.dbeaver.model.websocket.event.session.WSSessionStateEvent;
import org.jkiss.utils.CommonUtils;
Expand Down Expand Up @@ -163,15 +159,12 @@ public WebSession getWebSession(
* @return WebSession object or null, if session expired or invalid
*/
@Nullable
public WebSession getOrRestoreSession(@NotNull Request request) {
var sessionIdCookie = Request.getCookies(request).stream().filter(
c -> c.getName().equals(CBConstants.CB_SESSION_COOKIE_NAME)
).findAny().orElse(null);
if (sessionIdCookie == null) {
public WebSession getOrRestoreWebSession(@NotNull WebHttpRequestInfo requestInfo) {
final var sessionId = requestInfo.getId();
if (sessionId == null) {
log.debug("Http session is null. No Web Session returned");
return null;
}
var sessionId = sessionIdCookie.getValue();
WebSession webSession;
synchronized (sessionMap) {
if (sessionMap.containsKey(sessionId)) {
Expand All @@ -189,12 +182,7 @@ public WebSession getOrRestoreSession(@NotNull Request request) {
return null;
}

webSession = createWebSessionImpl(new WebHttpRequestInfo(
request.getId(),
request.getAttribute("locale"),
Request.getRemoteAddr(request),
request.getHeaders().get("User-Agent")
));
webSession = createWebSessionImpl(requestInfo);
restorePreviousUserSession(webSession, oldAuthInfo);

sessionMap.put(sessionId, webSession);
Expand Down Expand Up @@ -301,15 +289,18 @@ public Collection<BaseWebSession> getAllActiveSessions() {
}

@Nullable
public WebHeadlessSession getHeadlessSession(Request request, Session session, boolean create) throws DBException {
String smAccessToken = request.getHeaders().get(WSConstants.WS_AUTH_HEADER);
public WebHeadlessSession getHeadlessSession(
@Nullable String smAccessToken,
@NotNull WebHttpRequestInfo requestInfo,
boolean create
) throws DBException {
if (CommonUtils.isEmpty(smAccessToken)) {
return null;
}
synchronized (sessionMap) {
var tempCredProvider = new SMTokenCredentialProvider(smAccessToken);
SMAuthPermissions authPermissions = application.createSecurityController(tempCredProvider).getTokenPermissions();
var sessionId = session != null ? session.getId()
var sessionId = requestInfo.getId() != null ? requestInfo.getId()
: authPermissions.getSessionId();

var existSession = sessionMap.get(sessionId);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
package io.cloudbeaver.server;

import io.cloudbeaver.DBWConstants;
import io.cloudbeaver.server.websockets.WebSocketPingPongJob;
import org.eclipse.core.runtime.Plugin;
import org.jkiss.code.NotNull;
import org.jkiss.dbeaver.Log;
Expand Down Expand Up @@ -132,7 +133,9 @@ private void initTempFolder(@NotNull DBRProgressMonitor monitor) {
@NotNull

Check warning on line 133 in server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/server/BaseWebPlatform.java

View workflow job for this annotation

GitHub Actions / Server / Lint

[checkstyle] reported by reviewdog 🐶 Missing a Javadoc comment. Raw Output: /github/workspace/./server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/server/BaseWebPlatform.java:133:5: warning: Missing a Javadoc comment. (com.puppycrawl.tools.checkstyle.checks.javadoc.MissingJavadocMethodCheck)
public abstract WebApplication getApplication();

protected abstract void scheduleServerJobs();
protected void scheduleServerJobs() {
new WebSocketPingPongJob(WebAppUtils.getWebPlatform()).scheduleMonitor();
}

@Override
public synchronized void dispose() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,10 @@
import io.cloudbeaver.DBWebException;
import io.cloudbeaver.model.session.BaseWebSession;
import io.cloudbeaver.model.session.WebHeadlessSession;
import io.cloudbeaver.model.session.WebHttpRequestInfo;
import io.cloudbeaver.model.session.WebSession;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.eclipse.jetty.server.Request;
import org.eclipse.jetty.server.Session;
import org.jkiss.code.NotNull;
import org.jkiss.code.Nullable;
import org.jkiss.dbeaver.DBException;
Expand Down Expand Up @@ -56,9 +55,13 @@ WebSession getWebSession(

Collection<BaseWebSession> getAllActiveSessions();

Check warning on line 56 in server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/server/WebAppSessionManager.java

View workflow job for this annotation

GitHub Actions / Server / Lint

[checkstyle] reported by reviewdog 🐶 Missing a Javadoc comment. Raw Output: /github/workspace/./server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/server/WebAppSessionManager.java:56:5: warning: Missing a Javadoc comment. (com.puppycrawl.tools.checkstyle.checks.javadoc.MissingJavadocMethodCheck)

WebSession getOrRestoreSession(Request httpRequest);
WebSession getOrRestoreWebSession(WebHttpRequestInfo httpRequest);

Check warning on line 58 in server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/server/WebAppSessionManager.java

View workflow job for this annotation

GitHub Actions / Server / Lint

[checkstyle] reported by reviewdog 🐶 Missing a Javadoc comment. Raw Output: /github/workspace/./server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/server/WebAppSessionManager.java:58:5: warning: Missing a Javadoc comment. (com.puppycrawl.tools.checkstyle.checks.javadoc.MissingJavadocMethodCheck)

WebHeadlessSession getHeadlessSession(Request request, Session session, boolean create) throws DBException;
WebHeadlessSession getHeadlessSession(

Check warning on line 60 in server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/server/WebAppSessionManager.java

View workflow job for this annotation

GitHub Actions / Server / Lint

[checkstyle] reported by reviewdog 🐶 Missing a Javadoc comment. Raw Output: /github/workspace/./server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/server/WebAppSessionManager.java:60:5: warning: Missing a Javadoc comment. (com.puppycrawl.tools.checkstyle.checks.javadoc.MissingJavadocMethodCheck)
@Nullable String smAccessToken,
@NotNull WebHttpRequestInfo requestInfo,
boolean create
) throws DBException;

boolean touchSession(HttpServletRequest request, HttpServletResponse response) throws DBWebException;

Check warning on line 66 in server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/server/WebAppSessionManager.java

View workflow job for this annotation

GitHub Actions / Server / Lint

[checkstyle] reported by reviewdog 🐶 Missing a Javadoc comment. Raw Output: /github/workspace/./server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/server/WebAppSessionManager.java:66:5: warning: Missing a Javadoc comment. (com.puppycrawl.tools.checkstyle.checks.javadoc.MissingJavadocMethodCheck)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,36 +17,34 @@
package io.cloudbeaver.server.jetty;

import io.cloudbeaver.service.DBWWebSocketContext;
import jakarta.websocket.server.ServerEndpointConfig;
import org.eclipse.jetty.ee10.servlet.ServletContextHandler;
import org.eclipse.jetty.ee10.websocket.jakarta.server.config.JakartaWebSocketServletContainerInitializer;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.server.handler.ContextHandler;
import org.eclipse.jetty.websocket.api.Configurable;
import org.eclipse.jetty.websocket.server.WebSocketCreator;
import org.eclipse.jetty.websocket.server.WebSocketUpgradeHandler;
import org.jkiss.code.NotNull;

import java.util.ArrayList;
import java.util.List;
import java.util.function.Function;

public class CBJettyWebSocketContext implements DBWWebSocketContext {
private final List<String> mappings = new ArrayList<>();

private final Server server;
private final ContextHandler handler;
private final ServletContextHandler servletContextHandler;

public CBJettyWebSocketContext(@NotNull Server server, @NotNull ContextHandler handler) {
public CBJettyWebSocketContext(@NotNull Server server, @NotNull ServletContextHandler servletContextHandler) {
this.server = server;
this.handler = handler;
this.servletContextHandler = servletContextHandler;
}


@Override
public void addWebSocket(@NotNull String mapping, @NotNull Function<Configurable, WebSocketCreator> configurator) {
handler.insertHandler(WebSocketUpgradeHandler.from(
server,
handler,
container -> container.addMapping(mapping, configurator.apply(container))
));
mappings.add(mapping);
public void addWebSocket(@NotNull ServerEndpointConfig endpointConfig) {
// Add jakarta.websocket support
JakartaWebSocketServletContainerInitializer.configure(servletContextHandler, (context, container) -> {
container.addEndpoint(endpointConfig);
this.mappings.add(endpointConfig.getPath());
});
}

@NotNull
Expand Down
Loading

0 comments on commit 5b10cdc

Please sign in to comment.