Merge pull request #4917 from eclipse/jetty-10.0.x-4903-WebSocketPrivateEndpoints

Issue #4903 - give better errors for non public javax.websocket endpoints (jetty-10)
This commit is contained in:
Lachlan 2020-05-29 13:59:54 +10:00 committed by GitHub
commit 0f3a5823cc
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 211 additions and 38 deletions

View File

@ -72,7 +72,8 @@ public final class ContainerDefaultConfigurator extends Configurator
}
catch (Exception e)
{
InstantiationException instantiationException = new InstantiationException();
String errorMsg = String.format("%s: %s", e.getClass().getName(), e.getMessage());
InstantiationException instantiationException = new InstantiationException(errorMsg);
instantiationException.initCause(e);
throw instantiationException;
}

View File

@ -24,6 +24,7 @@ import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import javax.websocket.Extension;
import javax.websocket.Extension.Parameter;
import javax.websocket.server.ServerEndpointConfig;
@ -70,7 +71,7 @@ public class JavaxWebSocketCreator implements WebSocketCreator
// per upgrade request.
ServerEndpointConfig config = new ServerEndpointConfigWrapper(baseConfig)
{
Map<String, Object> userProperties = new HashMap<>(baseConfig.getUserProperties());
final Map<String, Object> userProperties = new HashMap<>(baseConfig.getUserProperties());
@Override
public Map<String, Object> getUserProperties()
@ -183,15 +184,13 @@ public class JavaxWebSocketCreator implements WebSocketCreator
return false;
JavaxWebSocketCreator that = (JavaxWebSocketCreator)o;
return baseConfig != null ? baseConfig.equals(that.baseConfig) : that.baseConfig == null;
return Objects.equals(baseConfig, that.baseConfig);
}
@Override
public int hashCode()
{
int result = (baseConfig != null ? baseConfig.hashCode() : 0);
return result;
return (baseConfig != null ? baseConfig.hashCode() : 0);
}
@Override

View File

@ -36,10 +36,10 @@ import org.eclipse.jetty.util.annotation.ManagedObject;
import org.eclipse.jetty.util.component.LifeCycle;
import org.eclipse.jetty.websocket.core.WebSocketComponents;
import org.eclipse.jetty.websocket.core.client.WebSocketCoreClient;
import org.eclipse.jetty.websocket.core.exception.WebSocketException;
import org.eclipse.jetty.websocket.core.server.WebSocketServerComponents;
import org.eclipse.jetty.websocket.javax.client.internal.JavaxWebSocketClientContainer;
import org.eclipse.jetty.websocket.javax.server.config.JavaxWebSocketServletContainerInitializer;
import org.eclipse.jetty.websocket.util.ReflectUtils;
import org.eclipse.jetty.websocket.util.server.internal.WebSocketMapping;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@ -160,6 +160,7 @@ public class JavaxWebSocketServerContainer extends JavaxWebSocketClientContainer
return frameHandlerFactory;
}
@Override
public void addEndpoint(Class<?> endpointClass) throws DeploymentException
{
if (endpointClass == null)
@ -169,19 +170,12 @@ public class JavaxWebSocketServerContainer extends JavaxWebSocketClientContainer
if (isStarted() || isStarting())
{
try
{
ServerEndpoint anno = endpointClass.getAnnotation(ServerEndpoint.class);
if (anno == null)
throw new DeploymentException(String.format("Class must be @%s annotated: %s", ServerEndpoint.class.getName(), endpointClass.getName()));
ServerEndpoint anno = endpointClass.getAnnotation(ServerEndpoint.class);
if (anno == null)
throw new DeploymentException(String.format("Class must be @%s annotated: %s", ServerEndpoint.class.getName(), endpointClass.getName()));
ServerEndpointConfig config = new AnnotatedServerEndpointConfig(this, endpointClass, anno);
addEndpointMapping(config);
}
catch (WebSocketException e)
{
throw new DeploymentException("Unable to deploy: " + endpointClass.getName(), e);
}
ServerEndpointConfig config = new AnnotatedServerEndpointConfig(this, endpointClass, anno);
addEndpointMapping(config);
}
else
{
@ -199,23 +193,16 @@ public class JavaxWebSocketServerContainer extends JavaxWebSocketClientContainer
if (isStarted() || isStarting())
{
// If we have annotations merge the annotated ServerEndpointConfig with the provided one.
Class<?> endpointClass = providedConfig.getEndpointClass();
try
{
// If we have annotations merge the annotated ServerEndpointConfig with the provided one.
ServerEndpoint anno = endpointClass.getAnnotation(ServerEndpoint.class);
ServerEndpointConfig config = (anno == null) ? providedConfig
: new AnnotatedServerEndpointConfig(this, endpointClass, anno, providedConfig);
ServerEndpoint anno = endpointClass.getAnnotation(ServerEndpoint.class);
ServerEndpointConfig config = (anno == null) ? providedConfig
: new AnnotatedServerEndpointConfig(this, endpointClass, anno, providedConfig);
if (LOG.isDebugEnabled())
LOG.debug("addEndpoint({}) path={} endpoint={}", config, config.getPath(), endpointClass);
if (LOG.isDebugEnabled())
LOG.debug("addEndpoint({}) path={} endpoint={}", config, config.getPath(), endpointClass);
addEndpointMapping(config);
}
catch (WebSocketException e)
{
throw new DeploymentException("Unable to deploy: " + endpointClass.getName(), e);
}
addEndpointMapping(config);
}
else
{
@ -225,14 +212,23 @@ public class JavaxWebSocketServerContainer extends JavaxWebSocketClientContainer
}
}
private void addEndpointMapping(ServerEndpointConfig config) throws WebSocketException
private void addEndpointMapping(ServerEndpointConfig config) throws DeploymentException
{
frameHandlerFactory.getMetadata(config.getEndpointClass(), config);
if (!ReflectUtils.isDefaultConstructable(config.getEndpointClass()))
throw new DeploymentException("Cannot access default constructor for the Endpoint class");
JavaxWebSocketCreator creator = new JavaxWebSocketCreator(this, config, getExtensionRegistry());
try
{
frameHandlerFactory.getMetadata(config.getEndpointClass(), config);
JavaxWebSocketCreator creator = new JavaxWebSocketCreator(this, config, getExtensionRegistry());
PathSpec pathSpec = new UriTemplatePathSpec(config.getPath());
webSocketMapping.addMapping(pathSpec, creator, frameHandlerFactory, defaultCustomizer);
}
catch (Throwable t)
{
throw new DeploymentException("Unable to deploy: " + config.getEndpointClass().getName(), t);
}
PathSpec pathSpec = new UriTemplatePathSpec(config.getPath());
webSocketMapping.addMapping(pathSpec, creator, frameHandlerFactory, defaultCustomizer);
}
@Override

View File

@ -0,0 +1,177 @@
//
// ========================================================================
// Copyright (c) 1995-2020 Mort Bay Consulting Pty Ltd and others.
//
// This program and the accompanying materials are made available under
// the terms of the Eclipse Public License 2.0 which is available at
// https://www.eclipse.org/legal/epl-2.0
//
// This Source Code may also be made available under the following
// Secondary Licenses when the conditions for such availability set
// forth in the Eclipse Public License, v. 2.0 are satisfied:
// the Apache License v2.0 which is available at
// https://www.apache.org/licenses/LICENSE-2.0
//
// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
// ========================================================================
//
package org.eclipse.jetty.websocket.javax.tests;
import javax.websocket.ContainerProvider;
import javax.websocket.DeploymentException;
import javax.websocket.Endpoint;
import javax.websocket.EndpointConfig;
import javax.websocket.MessageHandler;
import javax.websocket.OnMessage;
import javax.websocket.Session;
import javax.websocket.WebSocketContainer;
import javax.websocket.server.ServerContainer;
import javax.websocket.server.ServerEndpoint;
import javax.websocket.server.ServerEndpointConfig;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.server.ServerConnector;
import org.eclipse.jetty.servlet.ServletContextHandler;
import org.eclipse.jetty.util.component.LifeCycle;
import org.eclipse.jetty.websocket.javax.server.config.JavaxWebSocketServletContainerInitializer;
import org.eclipse.jetty.websocket.util.InvalidSignatureException;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.containsString;
import static org.hamcrest.Matchers.instanceOf;
import static org.junit.jupiter.api.Assertions.assertThrows;
public class PrivateEndpointTest
{
private Server server;
private WebSocketContainer client;
private ServletContextHandler contextHandler;
@BeforeEach
public void before()
{
server = new Server();
ServerConnector connector = new ServerConnector(server);
server.addConnector(connector);
contextHandler = new ServletContextHandler();
contextHandler.setContextPath("/");
server.setHandler(contextHandler);
client = ContainerProvider.getWebSocketContainer();
}
@AfterEach
public void after() throws Exception
{
LifeCycle.stop(client);
server.stop();
}
public interface CheckedConsumer<T>
{
void accept(T t) throws DeploymentException;
}
public void start(CheckedConsumer<ServerContainer> containerConsumer) throws Exception
{
JavaxWebSocketServletContainerInitializer.configure(contextHandler, (context, container) -> containerConsumer.accept(container));
server.start();
}
private static class ServerSocket extends Endpoint implements MessageHandler.Whole<String>
{
@Override
public void onOpen(Session session, EndpointConfig config)
{
session.addMessageHandler(this);
}
@Override
public void onMessage(String message)
{
}
}
@SuppressWarnings("InnerClassMayBeStatic")
public class ServerSocketNonStatic extends Endpoint implements MessageHandler.Whole<String>
{
@Override
public void onOpen(Session session, EndpointConfig config)
{
session.addMessageHandler(this);
}
@Override
public void onMessage(String message)
{
}
}
@ServerEndpoint("/annotated")
private static class AnnotatedServerSocket
{
@OnMessage
public void onMessage(String message)
{
}
}
@ServerEndpoint("/annotatedMethod")
public static class AnnotatedServerMethod
{
@OnMessage
private void onMessage(String message)
{
}
}
@Test
public void testEndpoint()
{
RuntimeException error = assertThrows(RuntimeException.class, () ->
start(container -> container.addEndpoint(ServerEndpointConfig.Builder.create(ServerSocket.class, "/").build())));
assertThat(error.getCause(), instanceOf(DeploymentException.class));
DeploymentException deploymentException = (DeploymentException)error.getCause();
assertThat(deploymentException.getMessage(), containsString("Cannot access default constructor for the Endpoint class"));
}
@Test
public void testInnerEndpoint()
{
RuntimeException error = assertThrows(RuntimeException.class, () ->
start(container -> container.addEndpoint(ServerEndpointConfig.Builder.create(ServerSocketNonStatic.class, "/").build())));
assertThat(error.getCause(), instanceOf(DeploymentException.class));
DeploymentException deploymentException = (DeploymentException)error.getCause();
assertThat(deploymentException.getMessage(), containsString("Cannot access default constructor for the Endpoint class"));
}
@Test
public void testAnnotatedEndpoint()
{
RuntimeException error = assertThrows(RuntimeException.class, () ->
start(container -> container.addEndpoint(AnnotatedServerSocket.class)));
assertThat(error.getCause(), instanceOf(DeploymentException.class));
DeploymentException deploymentException = (DeploymentException)error.getCause();
assertThat(deploymentException.getMessage(), containsString("Cannot access default constructor for the Endpoint class"));
}
@Test
public void testAnnotatedMethod()
{
RuntimeException error = assertThrows(RuntimeException.class, () ->
start(container -> container.addEndpoint(AnnotatedServerMethod.class)));
assertThat(error.getCause(), instanceOf(DeploymentException.class));
DeploymentException deploymentException = (DeploymentException)error.getCause();
assertThat(deploymentException.getCause(), instanceOf(InvalidSignatureException.class));
InvalidSignatureException cause = (InvalidSignatureException)deploymentException.getCause();
assertThat(cause.getMessage(), containsString("@OnMessage method must be public"));
}
}