From 565587f5dda85d050e263758a28f73a775ed899e Mon Sep 17 00:00:00 2001 From: Guus der Kinderen Date: Thu, 14 Nov 2024 16:06:36 +0100 Subject: [PATCH] OF-2717: Allow plugins to register/unregister web context The embedded webserver (Jetty) that powers the HTTP-Bind (BOSH) and Websocket endpoints can be used by plugins, for them to add web content of their own. To do so, a plugin can dynamically add a 'handler' when it gets loaded in Openfire (that handler should be removed again when the plugin gets unloaded). Due to changes to the Jetty API (which got updated from 10 to 12, using EE8), this mechanism was broken. Prior to the upgrade, a `org.eclipse.jetty.server.handler.HandlerList` was used to maintain the collection of plugin-provided contexts. After upgrading to Jetty 12, this was replaced with a `org.eclipse.jetty.server.handler.ContextHandlerCollection`. This was part of https://github.com/igniterealtime/Openfire/pull/2600/commits/99fe5f98554a547a57f59ed6c3b1c65f47c5f6c6#diff-55709739e79c9b3141bb106d4e4cf428d2bc54e46acf59450c3d9e8cf988385b The methods used by plugins to add and remove 'handlers' were not modified in that change. The 'handler' passed to them remained of type `org.eclipse.jetty.server.Handler`. This compiles fine, but seems to be a problem. When plugins (which are stand-alone projects with their own source code repositories) get updated to use Jetty 12 instead of 10, their representation of their web-context typically switches from `org.eclipse.jetty.webapp.WebAppContext` to the Jetty 12 EE8 `org.eclipse.jetty.ee8.webapp.WebAppContext`. The former is compatible with the old implementation in Openfire, but the latter is not. The old methods to register and unregister can still be used when the 'core' context handler of the new Jetty 12 EE8 WebAppContext is used (or when the plugin does not use a WebAppContext at all, but a different type of Handler). The core handler can be obtained through `org.eclipse.jetty.ee8.nested.ContextHandler#get`. As it is expected that many/most applications use a WebAppContext, this commit adds new methods to register and unregister (Jetty 12 EE8) web contexts directly (without the need to obtain their 'core' handler). --- .../openfire/http/HttpBindManager.java | 43 +++++++++++++++++-- 1 file changed, 40 insertions(+), 3 deletions(-) diff --git a/xmppserver/src/main/java/org/jivesoftware/openfire/http/HttpBindManager.java b/xmppserver/src/main/java/org/jivesoftware/openfire/http/HttpBindManager.java index 7e2287ad30..4ca9a9d5f2 100644 --- a/xmppserver/src/main/java/org/jivesoftware/openfire/http/HttpBindManager.java +++ b/xmppserver/src/main/java/org/jivesoftware/openfire/http/HttpBindManager.java @@ -19,9 +19,18 @@ import org.apache.tomcat.InstanceManager; import org.apache.tomcat.SimpleInstanceManager; import org.eclipse.jetty.ee8.apache.jsp.JettyJasperInitializer; +import org.eclipse.jetty.ee8.nested.ContextHandler; import org.eclipse.jetty.http.HttpHeader; import org.eclipse.jetty.http.HttpMethod; -import org.eclipse.jetty.server.*; +import org.eclipse.jetty.server.Connector; +import org.eclipse.jetty.server.ForwardedRequestCustomizer; +import org.eclipse.jetty.server.Handler; +import org.eclipse.jetty.server.HttpConfiguration; +import org.eclipse.jetty.server.HttpConnectionFactory; +import org.eclipse.jetty.server.SecureRequestCustomizer; +import org.eclipse.jetty.server.Server; +import org.eclipse.jetty.server.ServerConnector; +import org.eclipse.jetty.server.SslConnectionFactory; import org.eclipse.jetty.server.handler.ContextHandlerCollection; import org.eclipse.jetty.server.Handler.Sequence; import org.eclipse.jetty.server.handler.gzip.GzipHandler; @@ -45,6 +54,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import javax.annotation.Nonnull; import javax.servlet.DispatcherType; import java.io.File; import java.time.Duration; @@ -756,13 +766,40 @@ protected ServletContextHandler createStaticContentHandler() return null; } + /** + * Adds a Jetty handler to be added to the embedded web server that is used to expose Openfire's public + * web-bindings (eg: BOSH / HTTP-bind and websocket). + * + * @param handler The handler (cannot be null). + */ + public void addJettyHandler(@Nonnull final ContextHandler handler) + { + if ( handler == null ) + { + throw new IllegalArgumentException( "Argument 'handler' cannot be null." ); + } + + addJettyHandler(handler.get()); + } + + /** + * Removes a Jetty handler to be added to the embedded web server that is used to expose Openfire's public + * web-bindings (eg: BOSH / HTTP-bind and websocket). + * + * @param handler The handler (should not be null). + */ + public void removeJettyHandler(@Nonnull final ContextHandler handler) + { + removeJettyHandler(handler.get()); + } + /** * Adds a Jetty handler to be added to the embedded web server that is used to expose BOSH (HTTP-bind) * functionality. * * @param handler The handler (cannot be null). */ - public void addJettyHandler( Handler handler ) + public void addJettyHandler(@Nonnull final Handler handler ) { if ( handler == null ) { @@ -793,7 +830,7 @@ public void addJettyHandler( Handler handler ) * * @param handler The handler (should not be null). */ - public void removeJettyHandler( Handler handler ) + public void removeJettyHandler(@Nonnull final Handler handler ) { extensionHandlers.removeHandler( handler ); if ( handler.isStarted() )