Skip to content

Commit

Permalink
Merge pull request #3520 from petrberan/RESTEASY-3266
Browse files Browse the repository at this point in the history
[RESTEASY-3266] RestEasy JSAPI Servlet does not work with root "/" or empty @ApplicationPath("")
  • Loading branch information
jamezp committed Apr 18, 2023
2 parents a80647d + dc090b2 commit 2d62b0d
Show file tree
Hide file tree
Showing 5 changed files with 177 additions and 19 deletions.
Expand Up @@ -47,9 +47,9 @@ public void writeJavaScript(String base, HttpServletRequest req, HttpServletResp

for (Map.Entry<String, ServiceRegistry> entry : serviceRegistries.entrySet()) {
String uri = base;
if (entry.getKey() != null)
uri += entry.getKey();

if (entry.getKey() != null) {
uri = resolveDoubleSlash(uri, entry.getKey(), true);
}
StringWriter stringWriter = new StringWriter();
PrintWriter writer = new PrintWriter(new BufferedWriter(stringWriter));
writeJavaScript(uri, writer, entry.getValue());
Expand Down Expand Up @@ -98,7 +98,7 @@ public void writeJavaScript(String uri, PrintWriter writer,
LogMessages.LOGGER.debug(Messages.MESSAGES.restApiUrl(uri));
writer.println("REST.apiURL = '" + uri + "';");
Set<String> declaredPrefixes = new HashSet<String>();
printService(writer, serviceRegistry, declaredPrefixes);
printService(writer, serviceRegistry, declaredPrefixes, uri);

}

Expand All @@ -124,7 +124,7 @@ private void generateEtag(ServiceRegistry serviceRegistry, StringBuilder etagBui
}

private void printService(PrintWriter writer,
ServiceRegistry serviceRegistry, Set<String> declaredPrefixes) {
ServiceRegistry serviceRegistry, Set<String> declaredPrefixes, String uri) {

for (MethodMetaData methodMetaData : serviceRegistry.getMethodMetaData()) {
LogMessages.LOGGER.debug(Messages.MESSAGES.path(methodMetaData.getUri()));
Expand All @@ -133,11 +133,11 @@ private void printService(PrintWriter writer,
declarePrefix(writer, declaringPrefix, declaredPrefixes);

for (String httpMethod : methodMetaData.getHttpMethods()) {
print(writer, httpMethod, methodMetaData);
print(writer, httpMethod, methodMetaData, uri);
}
}
for (ServiceRegistry subService : serviceRegistry.getLocators())
printService(writer, subService, declaredPrefixes);
printService(writer, subService, declaredPrefixes, uri);
}

private void declarePrefix(PrintWriter writer, String declaringPrefix, Set<String> declaredPrefixes) {
Expand Down Expand Up @@ -166,20 +166,21 @@ private void copyResource(String name, PrintWriter writer)
}

private void print(PrintWriter writer, String httpMethod,
MethodMetaData methodMetaData) {
String uri = methodMetaData.getUri();
writer.println("// " + httpMethod + " " + uri);
MethodMetaData methodMetaData, String uri) {
String methodUri = methodMetaData.getUri();
writer.println("// " + httpMethod + " " + methodUri);
writer
.println(methodMetaData.getFunctionName() + " = function(_params){");
writer.println(" var params = _params ? _params : {};");
writer.println(" var request = new REST.Request();");
writer.println(" request.setMethod('" + httpMethod + "');");
writer
.println(" var uri = params.$apiURL ? params.$apiURL : REST.apiURL;");
if (uri.contains("{")) {
printURIParams(uri, writer);
if (methodUri.contains("{")) {
printURIParams(methodUri, writer);
} else {
writer.println(" uri += '" + uri + "';");
String resolvedMethodUri = resolveDoubleSlash(uri, methodUri, false);
writer.println(" uri += '" + resolvedMethodUri + "';");
}
printOtherParams(methodMetaData, writer);
writer.println(" request.setURI(uri);");
Expand Down Expand Up @@ -284,4 +285,47 @@ private void printURIParams(String uri, PrintWriter writer) {
if (i < replacedCurlyURI.length())
writer.println(" uri += '" + replacedCurlyURI.substring(i) + "';");
}

/**
* Resolves the case of {@code uri} ending with "/" and {@code uriToAppend} starting with "/" so that the combination
* of both ends up with only one "/" instead of "//".
*
* <br>
* <br>
* Example:
* http://localhost:8080/Uri//UriToAppend -> http://localhost:8080/Uri/UriToAppend
*
* @param uri the base URI with the possible extra "/" at the end.
* @param uriToAppend the URI being appended to the base one, with possible extra "/" at the beginning.
* @param appendToUri true if the {@code uriToAppend} should be appended to the {@code uri}, false if the
* {@code uriToAppend} should have the "/" removed without appending it to the {@code uri}.
* @return The {@code uriToAppend} appended to the {@code uri} if the {@code appendToUri} is true, the {@code uriToAppend}
* without the extra "/" at the beginning if the {@code appendToUri} is false. In both cases there are no two "//"
* between the {@code uri} and the {@code uriToAppend}.
*/
private String resolveDoubleSlash(String uri, String uriToAppend, boolean appendToUri) {
if (uri.endsWith("/") && uriToAppend.startsWith("/")) {
// Remove the extra "/" at the beginning of uriToAppend
if (uriToAppend.length() > 1) {
if (appendToUri) {
return uri.concat(uriToAppend.substring(1));
} else {
return uriToAppend.substring(1);
}
}
// Edge case if the uriToAppend is just "/"
else {
if (appendToUri) {
return uri;
}
return "";
}
}

// If the
if (appendToUri) {
return uri.concat(uriToAppend);
}
return uriToAppend;
}
}
Expand Up @@ -5,11 +5,14 @@
import jakarta.ws.rs.core.Response;

import org.jboss.arquillian.container.test.api.Deployment;
import org.jboss.arquillian.container.test.api.OperateOnDeployment;
import org.jboss.arquillian.container.test.api.RunAsClient;
import org.jboss.arquillian.junit.Arquillian;
import org.jboss.logging.Logger;
import org.jboss.resteasy.client.jaxrs.ResteasyClient;
import org.jboss.resteasy.spi.HttpResponseCodes;
import org.jboss.resteasy.test.jsapi.resource.CustomResource;
import org.jboss.resteasy.test.jsapi.resource.RootApplication;
import org.jboss.resteasy.utils.PortProviderUtil;
import org.jboss.resteasy.utils.TestUtil;
import org.jboss.shrinkwrap.api.Archive;
Expand All @@ -29,16 +32,27 @@
@RunAsClient
public class JSAPIGetBasicJsapiHandlingScriptTest {

private static final String JSAPI = "jsapi";
private static final String DOUBLESLASH = "doubleSlash";

static ResteasyClient client;
protected static final Logger logger = Logger.getLogger(JSAPIGetBasicJsapiHandlingScriptTest.class.getName());

@Deployment
public static Archive<?> deploy() {
WebArchive war = TestUtil.prepareArchive(JSAPIGetBasicJsapiHandlingScriptTest.class.getSimpleName());
@Deployment(name = JSAPI)
public static Archive<?> deployJSAPI() {
WebArchive war = TestUtil.prepareArchive(JSAPI);
war.addAsWebInfResource(JSAPIGetBasicJsapiHandlingScriptTest.class.getPackage(), "web.xml", "web.xml");
return TestUtil.finishContainerPrepare(war, null, (Class<?>[]) null);
}

@Deployment(name = DOUBLESLASH)
public static Archive<?> deployDoubleSlash() {
WebArchive war = TestUtil.prepareArchive(DOUBLESLASH);
war.addClasses(CustomResource.class, RootApplication.class);
war.addAsWebInfResource(JSAPIGetBasicJsapiHandlingScriptTest.class.getPackage(), "web_double_slash.xml", "web.xml");
return TestUtil.finishContainerPrepare(war, null, (Class<?>[]) null);
}

@Before
public void init() {
client = (ResteasyClient) ClientBuilder.newClient();
Expand All @@ -49,8 +63,8 @@ public void after() throws Exception {
client.close();
}

private String generateURL(String path) {
return PortProviderUtil.generateURL(path, JSAPIGetBasicJsapiHandlingScriptTest.class.getSimpleName());
private String generateURL(String path, String deploymentName) {
return PortProviderUtil.generateURL(path, deploymentName);
}

/**
Expand All @@ -59,13 +73,33 @@ private String generateURL(String path) {
* @tpSince RESTEasy 3.0.16
*/
@Test
@OperateOnDeployment(JSAPI)
public void testGetJsapiHeaderScript() throws Exception {
WebTarget target = client.target(generateURL("/rest-js"));
WebTarget target = client.target(generateURL("/rest-js", JSAPI));
Response response = target.request().get();
Assert.assertEquals(HttpResponseCodes.SC_OK, response.getStatus());
String responseString = response.readEntity(String.class);
logger.info(responseString);
Assert.assertTrue("Basic javascript generated by jsapi doesn't contain expected code",
responseString.contains("REST.Request = function() {"));
}

/**
* @tpTestDetails Tests whether the {@code request.setURI(uri)} would contain "//" in case the combination of
* {@code Path} and {@code ApplicationPath} would produce "//" or if the extra "/" was removed
* @tpSince RESTEasy 6.3.0
*/
@Test
@OperateOnDeployment(DOUBLESLASH)
public void testDoubleSlashInURI() {
WebTarget target = client.target(generateURL("/rest-js", DOUBLESLASH));
Response response = target.request().get();
Assert.assertEquals(HttpResponseCodes.SC_OK, response.getStatus());
String responseString = response.readEntity(String.class);
logger.info(responseString);
Assert.assertTrue("Basic javascript generated by jsapi doesn't contain expected line",
responseString.contains("8080/doubleSlash/rootApplication/"));
Assert.assertTrue("Basic javascript generated by jsapi doesn't contain expected line",
responseString.contains("uri += 'path';"));
}
}
@@ -0,0 +1,34 @@
/*
* JBoss, Home of Professional Open Source.
*
* Copyright 2023 Red Hat, Inc., and individual contributors
* as indicated by the @author tags.
*
* Licensed 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.jboss.resteasy.test.jsapi.resource;

import jakarta.ws.rs.GET;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.Produces;

@Path("/path/")
public class CustomResource {

@GET
@Produces("application/xml")
public String get() {
return "Hello world!";
}
}
@@ -0,0 +1,28 @@
/*
* JBoss, Home of Professional Open Source.
*
* Copyright 2023 Red Hat, Inc., and individual contributors
* as indicated by the @author tags.
*
* Licensed 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.jboss.resteasy.test.jsapi.resource;

import jakarta.ws.rs.ApplicationPath;
import jakarta.ws.rs.core.Application;

@ApplicationPath("/rootApplication/")
public class RootApplication extends Application {

}
@@ -0,0 +1,18 @@
<!DOCTYPE web-app PUBLIC
"-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
"http://java.sun.com/dtd/web-app_2_3.dtd" >

<web-app>
<display-name>Archetype Created Web Application</display-name>

<servlet>
<servlet-name>Resteasy JSAPI</servlet-name>
<servlet-class>org.jboss.resteasy.jsapi.JSAPIServlet</servlet-class>
</servlet>

<servlet-mapping>
<servlet-name>Resteasy JSAPI</servlet-name>
<url-pattern>/rest-js</url-pattern>
</servlet-mapping>

</web-app>

0 comments on commit 2d62b0d

Please sign in to comment.