You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@knox.apache.org by pz...@apache.org on 2024/08/21 22:26:14 UTC
(knox) 01/01: KNOX-3058: Avoid 404 When Topology Is Being Redeployed (#929)
This is an automated email from the ASF dual-hosted git repository.
pzampino pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/knox.git
commit 0358054bcbcf643d7348d91891b48e67fedabf1b
Author: Phil Zampino <pz...@apache.org>
AuthorDate: Wed Aug 21 18:11:15 2024 -0400
KNOX-3058: Avoid 404 When Topology Is Being Redeployed (#929)
---
.../org/apache/knox/gateway/GatewayServer.java | 71 +++++++++++++++++++++-
1 file changed, 70 insertions(+), 1 deletion(-)
diff --git a/gateway-server/src/main/java/org/apache/knox/gateway/GatewayServer.java b/gateway-server/src/main/java/org/apache/knox/gateway/GatewayServer.java
index 4e8c83458..2d6fd3a2a 100644
--- a/gateway-server/src/main/java/org/apache/knox/gateway/GatewayServer.java
+++ b/gateway-server/src/main/java/org/apache/knox/gateway/GatewayServer.java
@@ -62,11 +62,13 @@ import org.eclipse.jetty.server.Handler;
import org.eclipse.jetty.server.HttpConfiguration;
import org.eclipse.jetty.server.HttpConnectionFactory;
import org.eclipse.jetty.server.NetworkConnector;
+import org.eclipse.jetty.server.Request;
import org.eclipse.jetty.server.SecureRequestCustomizer;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.server.ServerConnector;
import org.eclipse.jetty.server.handler.ContextHandler;
import org.eclipse.jetty.server.handler.ContextHandlerCollection;
+import org.eclipse.jetty.server.handler.ErrorHandler;
import org.eclipse.jetty.server.handler.HandlerCollection;
import org.eclipse.jetty.server.handler.RequestLogHandler;
import org.eclipse.jetty.util.ssl.SslContextFactory;
@@ -84,6 +86,8 @@ import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;
import javax.servlet.SessionCookieConfig;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.TransformerException;
import java.io.File;
@@ -148,6 +152,8 @@ public class GatewayServer {
private AtomicBoolean stopped = new AtomicBoolean(false);
private GatewayStatusService gatewayStatusService;
+ private final Set<String> inactiveTopologies = new HashSet<>();
+
public static void main( String[] args ) {
try {
logSysProps();
@@ -717,6 +723,9 @@ public class GatewayServer {
jetty.setAttribute(ContextHandler.MAX_FORM_KEYS_KEY, config.getJettyMaxFormKeys());
log.setMaxFormKeys(config.getJettyMaxFormKeys());
+ // Add a handler for the 404 responses when a topology is being redeployed (i.e., is inactive)
+ jetty.setErrorHandler(new Http404ErrorHandler(this, config.getGatewayPath()));
+
/* topologyName is null because all topology listen on this port */
List<Connector> connectors = createConnector( jetty, config, config.getGatewayPort(), null);
for (Connector connector : connectors) {
@@ -915,13 +924,21 @@ public class GatewayServer {
}
private synchronized void internalActivateTopology( Topology topology, File topoDir ) {
- log.activatingTopology( topology.getName() );
+ final String name = topology.getName();
+
+ // Add the topology to the inactive set until it has been activated
+ addInactiveTopology(name);
+
+ log.activatingTopology(name);
File[] files = topoDir.listFiles( new RegexFilenameFilter( "%.*" ) );
if( files != null ) {
for( File file : files ) {
internalActivateArchive( topology, file );
}
}
+
+ // Remove the topology from the inactive set
+ removeInactiveTopology(name);
}
private synchronized void internalActivateArchive( Topology topology, File warDir ) {
@@ -962,10 +979,32 @@ public class GatewayServer {
});
}
+ private void addInactiveTopology(final String topologyName) {
+ synchronized (inactiveTopologies) {
+ inactiveTopologies.add(topologyName);
+ }
+ }
+
+ private void removeInactiveTopology(final String topologyName) {
+ synchronized (inactiveTopologies) {
+ inactiveTopologies.remove(topologyName);
+ }
+ }
+
+ private boolean isInactiveTopology(final String topologyName) {
+ boolean result = false;
+ synchronized (inactiveTopologies) {
+ result = inactiveTopologies.contains(topologyName);
+ }
+ return result;
+ }
+
private synchronized void internalDeactivateTopology( Topology topology ) {
log.deactivatingTopology( topology.getName() );
+ addInactiveTopology(topology.getName());
+
String topoName = topology.getName();
String topoPath = "/" + Urls.trimLeadingAndTrailingSlashJoin( config.getGatewayPath(), topoName );
String topoPathSlash = topoPath + "/";
@@ -1026,6 +1065,10 @@ public class GatewayServer {
auditor.audit(Action.UNDEPLOY, topology.getName(), ResourceType.TOPOLOGY,
ActionOutcome.UNAVAILABLE);
internalDeactivateTopology( topology );
+
+ // Since it is being deleted, then no longer consider it as only inactive
+ removeInactiveTopology(topology.getName());
+
for( File file : files ) {
log.deletingDeployment( file.getAbsolutePath() );
FileUtils.deleteQuietly( file );
@@ -1164,4 +1207,30 @@ public class GatewayServer {
return Long.compare(rightTime, leftTime);
}
}
+
+ /**
+ * Jetty ErrorHandler extension for handling 404 responses when topologies are inactive.
+ */
+ private static class Http404ErrorHandler extends ErrorHandler {
+ private final GatewayServer gs;
+ private final String gatewayPath;
+
+ Http404ErrorHandler(final GatewayServer gs, final String gatewayPath) {
+ this.gs = gs;
+ this.gatewayPath = gatewayPath;
+ }
+
+ @Override
+ public void doError(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException {
+ final int gatewayPrefixLength = ("/" + gatewayPath + "/").length();
+ String pathInfo = baseRequest.getPathInfo();
+ String topologyName = pathInfo.substring(gatewayPrefixLength, pathInfo.indexOf('/', gatewayPrefixLength));
+ if (gs.isInactiveTopology(topologyName) && (response.getStatus() == HttpServletResponse.SC_NOT_FOUND)) {
+ request.setAttribute("javax.servlet.error.message", "Service Unavailable"); // The default ErrorHandler references this attribute
+ response.setStatus(HttpServletResponse.SC_SERVICE_UNAVAILABLE);
+ }
+ super.doError(target, baseRequest, request, response);
+ }
+ }
+
}