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"); + } +}
