Skip to content

Commit

Permalink
Merge pull request #4810 from eclipse/jetty-10.0.x-LinkageErrorInvest…
Browse files Browse the repository at this point in the history
…igation

Issue #4800 - fix WebSocket LinkageError and invalid PathParam type handling
  • Loading branch information
lachlan-roberts authored May 4, 2020
2 parents 267039f + 1ad600e commit 07551ba
Show file tree
Hide file tree
Showing 18 changed files with 531 additions and 205 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -40,11 +40,14 @@
import org.eclipse.jetty.util.annotation.ManagedObject;
import org.eclipse.jetty.websocket.core.WebSocketComponents;
import org.eclipse.jetty.websocket.core.client.WebSocketCoreClient;
import org.eclipse.jetty.websocket.core.exception.UpgradeException;
import org.eclipse.jetty.websocket.core.exception.WebSocketTimeoutException;
import org.eclipse.jetty.websocket.javax.common.ConfiguredEndpoint;
import org.eclipse.jetty.websocket.javax.common.JavaxWebSocketContainer;
import org.eclipse.jetty.websocket.javax.common.JavaxWebSocketExtensionConfig;
import org.eclipse.jetty.websocket.javax.common.JavaxWebSocketFrameHandler;
import org.eclipse.jetty.websocket.javax.common.JavaxWebSocketFrameHandlerFactory;
import org.eclipse.jetty.websocket.util.InvalidWebSocketException;

/**
* Container for Client use of the javax.websocket API.
Expand Down Expand Up @@ -131,7 +134,7 @@ private CompletableFuture<Session> connect(JavaxClientUpgradeRequest upgradeRequ
{
if (error != null)
{
futureSession.completeExceptionally(error);
futureSession.completeExceptionally(convertCause(error));
return;
}

Expand All @@ -147,6 +150,18 @@ private CompletableFuture<Session> connect(JavaxClientUpgradeRequest upgradeRequ
return futureSession;
}

public static Throwable convertCause(Throwable error)
{
if (error instanceof UpgradeException ||
error instanceof WebSocketTimeoutException)
return new IOException(error);

if (error instanceof InvalidWebSocketException)
return new DeploymentException(error.getMessage(), error);

return error;
}

private Session connect(ConfiguredEndpoint configuredEndpoint, URI destURI) throws IOException
{
Objects.requireNonNull(configuredEndpoint, "WebSocket configured endpoint cannot be null");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
import java.lang.reflect.Method;
import javax.websocket.server.PathParam;

import org.eclipse.jetty.websocket.util.InvalidSignatureException;
import org.eclipse.jetty.websocket.util.InvokerUtils;

/**
Expand All @@ -40,11 +41,30 @@ public InvokerUtils.Arg getParamArg(Method method, Class<?> paramType, int idx)
{
if (anno.annotationType().equals(PathParam.class))
{
validateType(paramType);
PathParam pathParam = (PathParam)anno;
return new InvokerUtils.Arg(paramType, pathParam.value());
}
}
}
return new InvokerUtils.Arg(paramType);
}

/**
* The JSR356 rules for @PathParam only support
* String, Primitive Types (and their Boxed version)
*/
public static void validateType(Class<?> type)
{
if (!String.class.isAssignableFrom(type) &&
!Integer.TYPE.isAssignableFrom(type) &&
!Long.TYPE.isAssignableFrom(type) &&
!Short.TYPE.isAssignableFrom(type) &&
!Float.TYPE.isAssignableFrom(type) &&
!Double.TYPE.isAssignableFrom(type) &&
!Boolean.TYPE.isAssignableFrom(type) &&
!Character.TYPE.isAssignableFrom(type) &&
!Byte.TYPE.isAssignableFrom(type))
throw new InvalidSignatureException("Unsupported PathParam Type: " + type);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -50,112 +50,121 @@
public class WSServer extends LocalServer implements LocalFuzzer.Provider
{
private static final Logger LOG = LoggerFactory.getLogger(WSServer.class);
private final Path contextDir;
private final String contextPath;
private ContextHandlerCollection contexts;
private Path webinf;
private Path classesDir;
private final Path testDir;
private ContextHandlerCollection contexts = new ContextHandlerCollection();

public WSServer(File testdir, String contextName)
public WSServer(Path testDir)
{
this(testdir.toPath(), contextName);
this.testDir = testDir;
}

public WSServer(Path testdir, String contextName)
public WebApp createWebApp(String contextName)
{
this.contextDir = testdir.resolve(contextName);
this.contextPath = "/" + contextName;
FS.ensureEmpty(contextDir);
return new WebApp(contextName);
}

public void copyClass(Class<?> clazz) throws Exception
{
ClassLoader cl = Thread.currentThread().getContextClassLoader();
String endpointPath = TypeUtil.toClassReference(clazz);
URL classUrl = cl.getResource(endpointPath);
assertThat("Class URL for: " + clazz, classUrl, notNullValue());
Path destFile = classesDir.resolve(endpointPath);
FS.ensureDirExists(destFile.getParent());
File srcFile = new File(classUrl.toURI());
IO.copy(srcFile, destFile.toFile());
}

public void copyEndpoint(Class<?> endpointClass) throws Exception
@Override
protected Handler createRootHandler(Server server)
{
copyClass(endpointClass);
return contexts;
}

public void copyLib(Class<?> clazz, String jarFileName) throws URISyntaxException, IOException
public class WebApp
{
webinf = contextDir.resolve("WEB-INF");
FS.ensureDirExists(webinf);
Path libDir = webinf.resolve("lib");
FS.ensureDirExists(libDir);
Path jarFile = libDir.resolve(jarFileName);
private final WebAppContext context;
private final Path contextDir;
private final Path webInf;
private final Path classesDir;
private final Path libDir;

URL codeSourceURL = clazz.getProtectionDomain().getCodeSource().getLocation();
assertThat("Class CodeSource URL is file scheme", codeSourceURL.getProtocol(), is("file"));
private WebApp(String contextName)
{
// Ensure context directory.
contextDir = testDir.resolve(contextName);
FS.ensureEmpty(contextDir);

// Ensure WEB-INF directories.
webInf = contextDir.resolve("WEB-INF");
FS.ensureDirExists(webInf);
classesDir = webInf.resolve("classes");
FS.ensureDirExists(classesDir);
libDir = webInf.resolve("lib");
FS.ensureDirExists(libDir);

// Configure the WebAppContext.
context = new WebAppContext();
context.setContextPath("/" + contextName);
context.setBaseResource(new PathResource(contextDir));
context.setAttribute("org.eclipse.jetty.websocket.javax", Boolean.TRUE);
context.addConfiguration(new JavaxWebSocketConfiguration());
}

File sourceCodeSourceFile = new File(codeSourceURL.toURI());
if (sourceCodeSourceFile.isDirectory())
public WebAppContext getWebAppContext()
{
LOG.info("Creating " + jarFile + " from " + sourceCodeSourceFile);
JAR.create(sourceCodeSourceFile, jarFile.toFile());
return context;
}
else

public String getContextPath()
{
LOG.info("Copying " + sourceCodeSourceFile + " to " + jarFile);
IO.copy(sourceCodeSourceFile, jarFile.toFile());
return context.getContextPath();
}
}

public void copyWebInf(String testResourceName) throws IOException
{
webinf = contextDir.resolve("WEB-INF");
FS.ensureDirExists(webinf);
classesDir = webinf.resolve("classes");
FS.ensureDirExists(classesDir);
Path webxml = webinf.resolve("web.xml");
File testWebXml = MavenTestingUtils.getTestResourceFile(testResourceName);
IO.copy(testWebXml, webxml.toFile());
}
public Path getContextDir()
{
return contextDir;
}

public WebAppContext createWebAppContext() throws IOException
{
WebAppContext context = new WebAppContext();
context.setContextPath(this.contextPath);
context.setBaseResource(new PathResource(this.contextDir));
context.setAttribute("org.eclipse.jetty.websocket.javax", Boolean.TRUE);
context.addConfiguration(new JavaxWebSocketConfiguration());
return context;
}
public void createWebInf() throws IOException
{
copyWebInf("empty-web.xml");
}

public void createWebInf() throws IOException
{
copyWebInf("empty-web.xml");
}
public void copyWebInf(String testResourceName) throws IOException
{
File testWebXml = MavenTestingUtils.getTestResourceFile(testResourceName);
Path webXml = webInf.resolve("web.xml");
IO.copy(testWebXml, webXml.toFile());
}

public void deployWebapp(WebAppContext webapp) throws Exception
{
contexts.addHandler(webapp);
contexts.manage(webapp);
webapp.setThrowUnavailableOnStartupException(true);
webapp.start();
if (LOG.isDebugEnabled())
public void copyClass(Class<?> clazz) throws Exception
{
LOG.debug("{}", webapp.dump());
ClassLoader cl = Thread.currentThread().getContextClassLoader();
String endpointPath = TypeUtil.toClassReference(clazz);
URL classUrl = cl.getResource(endpointPath);
assertThat("Class URL for: " + clazz, classUrl, notNullValue());
Path destFile = classesDir.resolve(endpointPath);
FS.ensureDirExists(destFile.getParent());
File srcFile = new File(classUrl.toURI());
IO.copy(srcFile, destFile.toFile());
}
}

public Path getWebAppDir()
{
return this.contextDir;
}
public void copyLib(Class<?> clazz, String jarFileName) throws URISyntaxException, IOException
{
Path jarFile = libDir.resolve(jarFileName);

URL codeSourceURL = clazz.getProtectionDomain().getCodeSource().getLocation();
assertThat("Class CodeSource URL is file scheme", codeSourceURL.getProtocol(), is("file"));

File sourceCodeSourceFile = new File(codeSourceURL.toURI());
if (sourceCodeSourceFile.isDirectory())
{
LOG.info("Creating " + jarFile + " from " + sourceCodeSourceFile);
JAR.create(sourceCodeSourceFile, jarFile.toFile());
}
else
{
LOG.info("Copying " + sourceCodeSourceFile + " to " + jarFile);
IO.copy(sourceCodeSourceFile, jarFile.toFile());
}
}

@Override
protected Handler createRootHandler(Server server) throws Exception
{
contexts = new ContextHandlerCollection();
return contexts;
public void deploy()
{
contexts.addHandler(context);
contexts.manage(context);
context.setThrowUnavailableOnStartupException(true);
if (LOG.isDebugEnabled())
LOG.debug("{}", context.dump());
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@
import org.eclipse.jetty.servlet.FilterHolder;
import org.eclipse.jetty.toolchain.test.jupiter.WorkDir;
import org.eclipse.jetty.toolchain.test.jupiter.WorkDirExtension;
import org.eclipse.jetty.webapp.WebAppContext;
import org.eclipse.jetty.websocket.core.CloseStatus;
import org.eclipse.jetty.websocket.core.Frame;
import org.eclipse.jetty.websocket.core.OpCode;
Expand All @@ -50,22 +49,21 @@ public class AltFilterTest
@Test
public void testEcho() throws Exception
{
WSServer wsb = new WSServer(testdir.getPath(), "app");
wsb.copyWebInf("alt-filter-web.xml");
WSServer wsb = new WSServer(testdir.getPath());
WSServer.WebApp app = wsb.createWebApp("app");
app.copyWebInf("alt-filter-web.xml");
// the endpoint (extends javax.websocket.Endpoint)
wsb.copyClass(BasicEchoSocket.class);
app.copyClass(BasicEchoSocket.class);
app.deploy();

try
{
wsb.start();

WebAppContext webapp = wsb.createWebAppContext();
wsb.deployWebapp(webapp);

FilterHolder filterWebXml = webapp.getServletHandler().getFilter("wsuf-test");
FilterHolder filterWebXml = app.getWebAppContext().getServletHandler().getFilter("wsuf-test");
assertThat("Filter[wsuf-test]", filterWebXml, notNullValue());

FilterHolder filterSCI = webapp.getServletHandler().getFilter("Jetty_WebSocketUpgradeFilter");
FilterHolder filterSCI = app.getWebAppContext().getServletHandler().getFilter("Jetty_WebSocketUpgradeFilter");
assertThat("Filter[Jetty_WebSocketUpgradeFilter]", filterSCI, nullValue());

List<Frame> send = new ArrayList<>();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,6 @@
import javax.websocket.server.ServerEndpoint;

import org.eclipse.jetty.toolchain.test.MavenTestingUtils;
import org.eclipse.jetty.webapp.WebAppContext;
import org.eclipse.jetty.websocket.javax.tests.EventSocket;
import org.eclipse.jetty.websocket.javax.tests.WSServer;
import org.junit.jupiter.api.AfterEach;
Expand Down Expand Up @@ -61,12 +60,13 @@ public void onOpen()
public void startServer() throws Exception
{
Path testdir = MavenTestingUtils.getTargetTestingPath(ContainerProviderServerTest.class.getName());
server = new WSServer(testdir, "app");
server.createWebInf();
server.copyEndpoint(MySocket.class);
server = new WSServer(testdir);
WSServer.WebApp app = server.createWebApp("app");
app.createWebInf();
app.copyClass(MySocket.class);
app.deploy();

server.start();
WebAppContext webapp = server.createWebAppContext();
server.deployWebapp(webapp);
}

@AfterEach
Expand Down
Loading

0 comments on commit 07551ba

Please sign in to comment.