This is an automated email from the ASF dual-hosted git repository.

robertlazarski pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/axis-axis2-java-core.git

commit fc1b69cb76a23c0fa205005e0f3d38590f458a4a
Author: Zibran Khan <[email protected]>
AuthorDate: Sat Oct 24 12:24:56 2026 +0530

    dispatch on the request path only in the request uri dispatchers
---
 .../kernel/src/org/apache/axis2/util/Utils.java    | 41 ++++++++--------
 .../RequestURIBasedOperationDispatcherTest.java    | 28 +++++++++++
 .../RequestURIBasedServiceDispatcherTest.java      | 33 +++++++++++++
 .../apache/axis2/util/UtilsParseRequestTest.java   |  9 ++++
 .../axis2/webapp/RequestURIDispatchITCase.java     | 56 ++++++++++++++++++++++
 5 files changed, 148 insertions(+), 19 deletions(-)

diff --git a/modules/kernel/src/org/apache/axis2/util/Utils.java 
b/modules/kernel/src/org/apache/axis2/util/Utils.java
index 35efd3fc5f..6164016352 100644
--- a/modules/kernel/src/org/apache/axis2/util/Utils.java
+++ b/modules/kernel/src/org/apache/axis2/util/Utils.java
@@ -210,8 +210,13 @@ public class Utils {
         }
         String[] values = new String[2];
 
+        // The query string is not part of the path the container matched, so 
it
+        // takes no part in choosing the service or the operation. The service 
path
+        // is then the first match: a later one is a suffix the caller 
appended.
+        path = withoutQuery(path);
+
         // TODO. This is kind of brittle. Any service with the name /services 
would cause fun.
-        int index = path.lastIndexOf(servicePath);
+        int index = path.indexOf(servicePath);
         String service;
 
         if (-1 != index) {
@@ -220,12 +225,6 @@ public class Utils {
             if (path.length() > serviceStart + 1) {
                 service = path.substring(serviceStart + 1);
 
-                int queryIndex = service.indexOf('?');
-
-                if (queryIndex > 0) {
-                    service = service.substring(0, queryIndex);
-                }
-
                 int operationIndex = service.indexOf('/');
 
                 if (operationIndex > 0) {
@@ -265,7 +264,11 @@ public class Utils {
                servicePath = servicePath+"/";
         }
 
-        int index = path.lastIndexOf(servicePath);
+        // Only the path names the service: a query value or a repeated suffix 
that
+        // contains the service path must not win over the segment the 
container saw.
+        path = withoutQuery(path);
+
+        int index = path.indexOf(servicePath);
         String serviceOpPart = null;
 
         if (-1 != index) {
@@ -274,12 +277,6 @@ public class Utils {
             //get the string after services path
             if (path.length() > serviceStart) {
                 serviceOpPart = path.substring(serviceStart);
-
-                //remove everything after ?
-                int queryIndex = serviceOpPart.indexOf('?');
-                if (queryIndex > 0) {
-                    serviceOpPart = serviceOpPart.substring(0, queryIndex);
-                }
             }
         }
         return serviceOpPart;
@@ -299,6 +296,9 @@ public class Utils {
         if (path == null || serviceName == null) {
             return null;
         }
+        // The operation is read from the path only; a query value may 
legitimately
+        // contain the service name followed by a slash.
+        path = withoutQuery(path);
         int idx = path.lastIndexOf(serviceName + "/");
         String operationName = null;
         if (idx != -1) {
@@ -313,11 +313,6 @@ public class Utils {
         }
 
         if (operationName != null) {
-            //remove everyting after '?'
-            int queryIndex = operationName.indexOf('?');
-            if (queryIndex > 0) {
-                operationName = operationName.substring(0, queryIndex);
-            }
             //take the part upto / as the operation name
             if (operationName.indexOf("/") != -1) {
                 operationName = operationName.substring(0, 
operationName.indexOf("/"));
@@ -326,6 +321,14 @@ public class Utils {
         return operationName;
     }
 
+    /**
+     * Strips the query string from a request URL or URI, if it has one.
+     */
+    private static String withoutQuery(String path) {
+        int queryIndex = path.indexOf('?');
+        return queryIndex == -1 ? path : path.substring(0, queryIndex);
+    }
+
     public static ConfigurationContext getNewConfigurationContext(String 
repositry)
             throws Exception {
         final File file = new File(repositry);
diff --git 
a/modules/kernel/test/org/apache/axis2/dispatchers/RequestURIBasedOperationDispatcherTest.java
 
b/modules/kernel/test/org/apache/axis2/dispatchers/RequestURIBasedOperationDispatcherTest.java
index e5befef516..39a54f31a2 100644
--- 
a/modules/kernel/test/org/apache/axis2/dispatchers/RequestURIBasedOperationDispatcherTest.java
+++ 
b/modules/kernel/test/org/apache/axis2/dispatchers/RequestURIBasedOperationDispatcherTest.java
@@ -117,4 +117,32 @@ public class RequestURIBasedOperationDispatcherTest 
extends TestCase {
         assertEquals(operation4, messageContext.getAxisOperation());
     }
 
+    /**
+     * The operation comes from the request path alone, never from a query 
value
+     * that happens to contain the service name.
+     */
+    public void testOperationNotTakenFromQueryString() throws AxisFault {
+        AxisService service = new AxisService("Service1");
+        AxisOperation operation1 = new InOnlyAxisOperation(new 
QName("operation1"));
+        AxisOperation operation2 = new InOnlyAxisOperation(new 
QName("operation2"));
+        service.addOperation(operation1);
+        service.addOperation(operation2);
+        ConfigurationContext cc = 
ConfigurationContextFactory.createEmptyConfigurationContext();
+        cc.getAxisConfiguration().addService(service);
+        RequestURIBasedOperationDispatcher ruisd = new 
RequestURIBasedOperationDispatcher();
+
+        MessageContext messageContext = cc.createMessageContext();
+        messageContext.setTo(new EndpointReference(
+                
"/axis2/services/Service1/operation1?next=/axis2/services/Service1/operation2"));
+        messageContext.setAxisService(service);
+        ruisd.invoke(messageContext);
+        assertEquals(operation1, messageContext.getAxisOperation());
+
+        messageContext = cc.createMessageContext();
+        messageContext.setTo(new EndpointReference(
+                
"/axis2/services/Service1?next=/axis2/services/Service1/operation2"));
+        messageContext.setAxisService(service);
+        ruisd.invoke(messageContext);
+        assertNull(messageContext.getAxisOperation());
+    }
 }
diff --git 
a/modules/kernel/test/org/apache/axis2/dispatchers/RequestURIBasedServiceDispatcherTest.java
 
b/modules/kernel/test/org/apache/axis2/dispatchers/RequestURIBasedServiceDispatcherTest.java
index fad0957bd3..c9241fbf9e 100644
--- 
a/modules/kernel/test/org/apache/axis2/dispatchers/RequestURIBasedServiceDispatcherTest.java
+++ 
b/modules/kernel/test/org/apache/axis2/dispatchers/RequestURIBasedServiceDispatcherTest.java
@@ -158,4 +158,37 @@ public class RequestURIBasedServiceDispatcherTest extends 
TestCase {
         assertEquals(as4, messageContext.getAxisService());
     }
 
+    /**
+     * The service comes from the request path alone. A REST To address still
+     * carries the query string, and the servlet container matched its URL
+     * patterns against the path only, so a service path inside a query value 
or
+     * in a repeated path suffix must not select a different service.
+     */
+    public void testServiceNotTakenFromQueryStringOrRepeatedPath() throws 
AxisFault {
+        AxisService as1 = new AxisService("Service1");
+        AxisService as2 = new AxisService("Service2");
+        ConfigurationContext cc = 
ConfigurationContextFactory.createEmptyConfigurationContext();
+        AxisConfiguration ac = cc.getAxisConfiguration();
+        ac.addService(as1);
+        ac.addService(as2);
+        RequestURIBasedServiceDispatcher ruisd = new 
RequestURIBasedServiceDispatcher();
+
+        MessageContext messageContext = cc.createMessageContext();
+        messageContext.setTo(new EndpointReference(
+                
"/axis2/services/Service1?next=/axis2/services/Service2/operation"));
+        ruisd.invoke(messageContext);
+        assertEquals(as1, messageContext.getAxisService());
+
+        messageContext = cc.createMessageContext();
+        messageContext.setTo(new EndpointReference(
+                
"/axis2/services/NoSuchService?next=/axis2/services/Service2"));
+        ruisd.invoke(messageContext);
+        assertNull(messageContext.getAxisService());
+
+        messageContext = cc.createMessageContext();
+        messageContext.setTo(new EndpointReference(
+                "/axis2/services/Service1/axis2/services/Service2"));
+        ruisd.invoke(messageContext);
+        assertEquals(as1, messageContext.getAxisService());
+    }
 }
diff --git 
a/modules/kernel/test/org/apache/axis2/util/UtilsParseRequestTest.java 
b/modules/kernel/test/org/apache/axis2/util/UtilsParseRequestTest.java
index 8fdb1cd15a..cd1c9bf0de 100644
--- a/modules/kernel/test/org/apache/axis2/util/UtilsParseRequestTest.java
+++ b/modules/kernel/test/org/apache/axis2/util/UtilsParseRequestTest.java
@@ -80,4 +80,13 @@ public class UtilsParseRequestTest extends AbstractTestCase {
         assertEquals(operation, strings[1]);
     }
 
+    public void testServiceNotTakenFromQuery() throws Exception {
+        
assertParsesTo("http://localhost:8081/axis2/services/System?next=/axis2/services/Other/operation";,
+                       "System");
+    }
+
+    public void testOperationNotTakenFromQuery() throws Exception {
+        
assertParsesTo("http://localhost:8081/axis2/services/System/operation?next=/axis2/services/Other/other";,
+                       "System", "operation");
+    }
 }
diff --git 
a/systests/webapp-tests/src/test/java/org/apache/axis2/webapp/RequestURIDispatchITCase.java
 
b/systests/webapp-tests/src/test/java/org/apache/axis2/webapp/RequestURIDispatchITCase.java
new file mode 100644
index 0000000000..b2767ec32b
--- /dev/null
+++ 
b/systests/webapp-tests/src/test/java/org/apache/axis2/webapp/RequestURIDispatchITCase.java
@@ -0,0 +1,56 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.axis2.webapp;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+import java.net.URI;
+import java.net.http.HttpClient;
+import java.net.http.HttpRequest;
+import java.net.http.HttpResponse;
+
+import org.junit.Test;
+
+/**
+ * The service and operation a request is dispatched to come from the request
+ * path, which is what the container matched its URL patterns against. A 
service
+ * path carried in a query value, or appended after the real service name, must
+ * not select anything.
+ */
+public class RequestURIDispatchITCase {
+    private static String get(String pathAndQuery) throws Exception {
+        int port = Integer.getInteger("jetty.httpPort", 8080);
+        HttpRequest request = HttpRequest.newBuilder(
+                URI.create("http://localhost:"; + port + "/axis2" + 
pathAndQuery)).GET().build();
+        return HttpClient.newHttpClient()
+                .send(request, HttpResponse.BodyHandlers.ofString()).body();
+    }
+
+    @Test
+    public void testServiceIsTakenFromThePathOnly() throws Exception {
+        // Control: the operation answers when the path names it.
+        
assertThat(get("/services/Version/getVersion")).contains("getVersionResponse");
+        // The path names no deployed service; neither the query string nor a
+        // repeated service path after it may route the request to Version.
+        
assertThat(get("/services/NoSuchService?next=/axis2/services/Version/getVersion"))
+                .doesNotContain("getVersionResponse");
+        
assertThat(get("/services/NoSuchService/axis2/services/Version/getVersion"))
+                .doesNotContain("getVersionResponse");
+    }
+}

Reply via email to