Repository: provectus/kafka-ui
Issue: Service is not working with Context Path
Labels: type/bug
scope/backend

**Describe the bug**
When we run the service with `SERVER_SERVLET_CONTEXT_PATH` set to something like "/kafkaui", the service can not start and all APIs return error.

Here is the logs of the system:
```
09:01:47.771 [kafka-admin-client-thread | adminclient-1] INFO  org.apache.zookeeper.ZooKeeper - Client environment:user.name=root
09:01:47.771 [kafka-admin-client-thread | adminclient-1] INFO  org.apache.zookeeper.ZooKeeper - Client environment:user.home=/root
09:01:47.772 [kafka-admin-client-thread | adminclient-1] INFO  org.apache.zookeeper.ZooKeeper - Client environment:user.dir=/
09:01:47.772 [kafka-admin-client-thread | adminclient-1] INFO  org.apache.zookeeper.ZooKeeper - Client environment:os.memory.free=55MB
09:01:47.772 [kafka-admin-client-thread | adminclient-1] INFO  org.apache.zookeeper.ZooKeeper - Client environment:os.memory.max=1979MB
09:01:47.772 [kafka-admin-client-thread | adminclient-1] INFO  org.apache.zookeeper.ZooKeeper - Client environment:os.memory.total=123MB
09:01:47.776 [kafka-admin-client-thread | adminclient-1] INFO  org.apache.zookeeper.ZooKeeper - Initiating client connection, connectString=cp-helm-charts-cp-zookeeper-headless.k
09:01:47.783 [kafka-admin-client-thread | adminclient-1] INFO  org.apache.zookeeper.common.X509Util - Setting -D jdk.tls.rejectClientInitiatedRenegotiation=true to disable client
09:01:47.789 [kafka-admin-client-thread | adminclient-1] INFO  org.apache.zookeeper.ClientCnxnSocket - jute.maxbuffer value is 4194304 Bytes
09:01:47.799 [kafka-admin-client-thread | adminclient-1] INFO  org.apache.zookeeper.ClientCnxn - zookeeper.request.timeout value is 0. feature enabled=
09:01:47.801 [kafka-admin-client-thread | adminclient-1] INFO  org.I0Itec.zkclient.ZkClient - Waiting for keeper state SyncConnected
09:01:47.818 [kafka-admin-client-thread | adminclient-1-SendThread(cp-helm-charts-cp-zookeeper-headless.kafka-cluster:2181)] INFO  org.apache.zookeeper.ClientCnxn - Opening socke
09:01:47.820 [kafka-admin-client-thread | adminclient-1-SendThread(cp-helm-charts-cp-zookeeper-headless.kafka-cluster:2181)] INFO  org.apache.zookeeper.ClientCnxn - Socket connec
09:01:47.833 [kafka-admin-client-thread | adminclient-1-SendThread(cp-helm-charts-cp-zookeeper-headless.kafka-cluster:2181)] INFO  org.apache.zookeeper.ClientCnxn - Session estab
09:01:47.837 [kafka-admin-client-thread | adminclient-1-EventThread] INFO  org.I0Itec.zkclient.ZkClient - zookeeper state changed (SyncConnected)
09:01:47.837 [kafka-admin-client-thread | adminclient-1] DEBUG com.provectus.kafka.ui.service.ZookeeperService - Start getting Zookeeper metrics for kafkaCluster: Production
09:01:52.274 [boundedElastic-1] DEBUG org.springframework.http.codec.json.Jackson2JsonEncoder - [4a56d3f9] Encoding [class ErrorResponse {
    code: 5000
    message: 404 NOT_FOUND
    timestamp: 1624438912139
    req (truncated)...]
09:01:53.314 [boundedElastic-1] DEBUG org.springframework.http.codec.json.Jackson2JsonEncoder - [5da6b63f] Encoding [class ErrorResponse {
    code: 5000
    message: 404 NOT_FOUND
    timestamp: 1624438913312
    req (truncated)...]
09:02:01.918 [boundedElastic-1] DEBUG org.springframework.http.codec.json.Jackson2JsonEncoder - [dcda94e7] Encoding [class ErrorResponse {
    code: 5000
    message: 404 NOT_FOUND
    timestamp: 1624438921918
    req (truncated)...]
09:02:03.309 [boundedElastic-1] DEBUG org.springframework.http.codec.json.Jackson2JsonEncoder - [864b2c34] Encoding [class ErrorResponse {
    code: 5000
    message: 404 NOT_FOUND
    timestamp: 1624438923308
    req (truncated)...]
```


Issue comments:
Hi, @sashati. Thanks for creating this issue. I just checked and it works fine with http://localhost:8080/kafkaui/. So you should include trailing slash. Agree that this is not convenient if you use subpath. We'll fix it the next release. 
Hi @germanosin. Thanks for the info. Then I close this issue and hopefully see this resolved soon
@sashati fix is in master, you could use image with tag master to get these updates.

Pull request review comments:

Patch that resolved the issue:
diff --git a/docker/kafka-ui-sasl.yaml b/docker/kafka-ui-sasl.yaml
index 87b1094be7f..1c0312f11a2 100644
--- a/docker/kafka-ui-sasl.yaml
+++ b/docker/kafka-ui-sasl.yaml
@@ -12,6 +12,7 @@ services:
       - kafka
     environment:
       KAFKA_CLUSTERS_0_NAME: local
+#      SERVER_SERVLET_CONTEXT_PATH: "/kafkaui"
       KAFKA_CLUSTERS_0_BOOTSTRAPSERVERS: kafka:9092
       KAFKA_CLUSTERS_0_ZOOKEEPER: zookeeper:2181
       KAFKA_CLUSTERS_0_PROPERTIES_SECURITY_PROTOCOL: SASL_PLAINTEXT
diff --git a/kafka-ui-api/src/main/java/com/provectus/kafka/ui/config/CustomWebFilter.java b/kafka-ui-api/src/main/java/com/provectus/kafka/ui/config/CustomWebFilter.java
index dfe9c4c13f1..5efee1db8de 100644
--- a/kafka-ui-api/src/main/java/com/provectus/kafka/ui/config/CustomWebFilter.java
+++ b/kafka-ui-api/src/main/java/com/provectus/kafka/ui/config/CustomWebFilter.java
@@ -1,5 +1,6 @@
 package com.provectus.kafka.ui.config;
 
+import java.util.Optional;
 import org.springframework.boot.autoconfigure.web.ServerProperties;
 import org.springframework.stereotype.Component;
 import org.springframework.web.server.ServerWebExchange;
@@ -22,13 +23,13 @@ public Mono<Void> filter(ServerWebExchange exchange, WebFilterChain chain) {
     String contextPath = serverProperties.getServlet().getContextPath() != null 
         ? serverProperties.getServlet().getContextPath() : "";
 
-    if (exchange.getRequest().getURI().getPath().equals(contextPath + "/")
-        || exchange.getRequest().getURI().getPath().startsWith(contextPath + "/ui")) {
+    final String path = exchange.getRequest().getURI().getPath().replaceAll("/$", "");
+    if (path.equals(contextPath) || path.startsWith(contextPath + "/ui")) {
       return chain.filter(
           exchange.mutate().request(exchange.getRequest().mutate().path("/index.html").build())
               .build()
       );
-    } else if (exchange.getRequest().getURI().getPath().startsWith(contextPath)) {
+    } else if (path.startsWith(contextPath)) {
       return chain.filter(
           exchange.mutate().request(exchange.getRequest().mutate().contextPath(contextPath).build())
               .build()


Test patch: