Merge remote-tracking branch 'origin/jetty-10.0.x' into jetty-10.0.x-3012-Compliance

This commit is contained in:
Greg Wilkins 2019-03-12 09:24:06 +11:00
commit 0ecbdec9d0
18 changed files with 140 additions and 352 deletions

View File

@ -179,7 +179,6 @@ To allow the greatest degree of flexibility in using JAAS with web applications,
Note that you don't ordinarily need to set these explicitly, as Jetty has defaults which will work in 99% of cases.
However, should you need to, you can configure:
* a policy for role-based authorization (Default: `org.eclipse.jetty.jaas.StrictRoleCheckPolicy`)
* a CallbackHandler (Default: `org.eclipse.jetty.jaas.callback.DefaultCallbackHandler`)
* a list of classnames for the Principal implementation that equate to a user role (Default: `org.eclipse.jetty.jaas.JAASRole`)
@ -190,9 +189,6 @@ Here's an example of setting each of these (to their default values):
<New class="org.eclipse.jetty.jaas.JAASLoginService">
<Set name="Name">Test JAAS Realm</Set>
<Set name="LoginModuleName">xyz</Set>
<Set name="RoleCheckPolicy">
<New class="org.eclipse.jetty.jaas.StrictRoleCheckPolicy"/>
</Set>
<Set name="CallbackHandlerClass">
org.eclipse.jetty.jaas.callback.DefaultCallbackHandler
</Set>
@ -204,16 +200,6 @@ Here's an example of setting each of these (to their default values):
</New>
----
===== RoleCheckPolicy
The `RoleCheckPolicy` must be an implementation of the `org.eclipse.jetty.jaas.RoleCheckPolicy` interface and its purpose is to help answer the question "is User X in Role Y" for role-based authorization requests.
The default implementation distributed with Jetty is the `org.eclipse.jetty.jaas.StrictRoleCheckPolicy`, which will assess a user as having a particular role if that role is at the top of the stack of roles that have been temporarily pushed onto the user.
If the user has no temporarily assigned roles, the role is amongst those configured for the user.
Roles can be temporarily assigned to a user programmatically by using the `pushRole(String rolename)` method of the `org.eclipse.jetty.jaas.JAASUserPrincipal` class.
For the majority of webapps, the default `StrictRoleCheckPolicy` will be quite adequate, however you may provide your own implementation and set it on your `JAASLoginService` instance.
===== CallbackHandler
A CallbackHandler is responsible for interfacing with the user to obtain usernames and credentials to be authenticated.

View File

@ -33,6 +33,20 @@ import static java.lang.Long.MAX_VALUE;
* <p>Subclasses should implement {@link #onTimeoutExpired()}.</p>
* <p>This implementation is optimised assuming that the timeout
* will mostly be cancelled and then reused with a similar value.</p>
* <p>This implementation has a {@link Timeout} holding the time
* at which the scheduled task should fire, and a linked list of
* {@link Wakeup}, each holding the actual scheduled task.</p>
* <p>Calling {@link #schedule(long, TimeUnit)} the first time will
* create a Timeout with an associated Wakeup and submit a task to
* the scheduler.
* Calling {@link #schedule(long, TimeUnit)} again with the same or
* a larger delay will cancel the previous Timeout, but keep the
* previous Wakeup without submitting a new task to the scheduler,
* therefore reducing the pressure on the scheduler and avoid it
* becomes a bottleneck.
* When the Wakeup task fires, it will see that the Timeout is now
* in the future and will attach a new Wakeup with the future time
* to the Timeout, and submit a scheduler task for the new Wakeup.</p>
*/
public abstract class CyclicTimeout implements Destroyable
{
@ -59,24 +73,24 @@ public abstract class CyclicTimeout implements Destroyable
}
/**
* Schedules a timeout, even if already set, cancelled or expired.
* <p>Schedules a timeout, even if already set, cancelled or expired.</p>
* <p>If a timeout is already set, it will be cancelled and replaced
* by the new one.</p>
*
* @param delay The period of time before the timeout expires.
* @param units The unit of time of the period.
* @return true if the timer was already set.
* @return true if the timeout was already set.
*/
public boolean schedule(long delay, TimeUnit units)
{
long now = System.nanoTime();
long new_timeout_at = now + units.toNanos(delay);
Wakeup new_wakeup = null;
boolean result;
Wakeup new_wakeup;
while (true)
{
Timeout timeout = _timeout.get();
new_wakeup = null;
result = timeout._at != MAX_VALUE;
// Is the current wakeup good to use? ie before our timeout time?
@ -114,14 +128,12 @@ public abstract class CyclicTimeout implements Destroyable
public boolean cancel()
{
boolean result;
Timeout timeout;
Timeout new_timeout;
while (true)
{
timeout = _timeout.get();
Timeout timeout = _timeout.get();
result = timeout._at != MAX_VALUE;
Wakeup wakeup = timeout._wakeup;
new_timeout = wakeup == null ? NOT_SET : new Timeout(MAX_VALUE, wakeup);
Timeout new_timeout = wakeup == null ? NOT_SET : new Timeout(MAX_VALUE, wakeup);
if (_timeout.compareAndSet(timeout, new_timeout))
break;
}
@ -166,7 +178,11 @@ public abstract class CyclicTimeout implements Destroyable
@Override
public String toString()
{
return String.format("%s@%x:%d,%s", getClass().getSimpleName(), hashCode(), _at, _wakeup);
return String.format("%s@%x:%dms,%s",
getClass().getSimpleName(),
hashCode(),
TimeUnit.NANOSECONDS.toMillis(_at - System.nanoTime()),
_wakeup);
}
}
@ -200,10 +216,9 @@ public abstract class CyclicTimeout implements Destroyable
@Override
public void run()
{
long now;
Wakeup new_wakeup;
boolean has_expired;
long now = System.nanoTime();
Wakeup new_wakeup = null;
boolean has_expired = false;
while (true)
{
Timeout timeout = _timeout.get();
@ -226,16 +241,12 @@ public abstract class CyclicTimeout implements Destroyable
// Not found, we become a noop.
return;
now = System.nanoTime();
new_wakeup = null;
has_expired = false;
Timeout new_timeout;
// We are in the wakeup list! So we have to act and we know our
// tail has not expired (else it would have removed us from the list).
// Remove ourselves (and any prior Wakeup) from the wakeup list.
wakeup = wakeup._next;
Timeout new_timeout;
if (timeout._at <= now)
{
// We have timed out!
@ -274,7 +285,11 @@ public abstract class CyclicTimeout implements Destroyable
@Override
public String toString()
{
return String.format("%s@%x:%d->%s", getClass().getSimpleName(), hashCode(), _at, _next);
return String.format("%s@%x:%dms->%s",
getClass().getSimpleName(),
hashCode(),
_at == MAX_VALUE ? _at : TimeUnit.NANOSECONDS.toMillis(_at - System.nanoTime()),
_next);
}
}
}

View File

@ -1,117 +0,0 @@
//
// ========================================================================
// Copyright (c) 1995-2019 Mort Bay Consulting Pty. Ltd.
// ------------------------------------------------------------------------
// All rights reserved. This program and the accompanying materials
// are made available under the terms of the Eclipse Public License v1.0
// and Apache License v2.0 which accompanies this distribution.
//
// The Eclipse Public License is available at
// http://www.eclipse.org/legal/epl-v10.html
//
// The Apache License v2.0 is available at
// http://www.opensource.org/licenses/apache2.0.php
//
// You may elect to redistribute this code under either of these licenses.
// ========================================================================
//
package org.eclipse.jetty.jaas;
import java.security.Principal;
import java.security.acl.Group;
import java.util.Enumeration;
import java.util.HashSet;
import java.util.Iterator;
public class JAASGroup implements Group
{
public static final String ROLES = "__roles__";
private String _name = null;
private HashSet<Principal> _members = null;
public JAASGroup(String n)
{
this._name = n;
this._members = new HashSet<Principal>();
}
/* ------------------------------------------------------------ */
@Override
public synchronized boolean addMember(Principal principal)
{
return _members.add(principal);
}
@Override
public synchronized boolean removeMember(Principal principal)
{
return _members.remove(principal);
}
@Override
public boolean isMember(Principal principal)
{
return _members.contains(principal);
}
@Override
public Enumeration<? extends Principal> members()
{
class MembersEnumeration implements Enumeration<Principal>
{
private Iterator<? extends Principal> itor;
public MembersEnumeration (Iterator<? extends Principal> itor)
{
this.itor = itor;
}
@Override
public boolean hasMoreElements ()
{
return this.itor.hasNext();
}
@Override
public Principal nextElement ()
{
return this.itor.next();
}
}
return new MembersEnumeration (_members.iterator());
}
@Override
public int hashCode()
{
return getName().hashCode();
}
@Override
public boolean equals(Object object)
{
if (! (object instanceof JAASGroup))
return false;
return ((JAASGroup)object).getName().equals(getName());
}
@Override
public String toString()
{
return getName();
}
@Override
public String getName()
{
return _name;
}
}

View File

@ -1,35 +0,0 @@
//
// ========================================================================
// Copyright (c) 1995-2019 Mort Bay Consulting Pty. Ltd.
// ------------------------------------------------------------------------
// All rights reserved. This program and the accompanying materials
// are made available under the terms of the Eclipse Public License v1.0
// and Apache License v2.0 which accompanies this distribution.
//
// The Eclipse Public License is available at
// http://www.eclipse.org/legal/epl-v10.html
//
// The Apache License v2.0 is available at
// http://www.opensource.org/licenses/apache2.0.php
//
// You may elect to redistribute this code under either of these licenses.
// ========================================================================
//
package org.eclipse.jetty.jaas;
import java.security.Principal;
import java.security.acl.Group;
public interface RoleCheckPolicy
{
/* ------------------------------------------------ */
/** Check if a role is either a runAsRole or in a set of roles
* @param roleName the role to check
* @param runAsRole a pushed role (can be null)
* @param roles a Group whose Principals are role names
* @return <code>true</code> if <code>role</code> equals <code>runAsRole</code> or is a member of <code>roles</code>.
*/
public boolean checkRole (String roleName, Principal runAsRole, Group roles);
}

View File

@ -1,63 +0,0 @@
//
// ========================================================================
// Copyright (c) 1995-2019 Mort Bay Consulting Pty. Ltd.
// ------------------------------------------------------------------------
// All rights reserved. This program and the accompanying materials
// are made available under the terms of the Eclipse Public License v1.0
// and Apache License v2.0 which accompanies this distribution.
//
// The Eclipse Public License is available at
// http://www.eclipse.org/legal/epl-v10.html
//
// The Apache License v2.0 is available at
// http://www.opensource.org/licenses/apache2.0.php
//
// You may elect to redistribute this code under either of these licenses.
// ========================================================================
//
package org.eclipse.jetty.jaas;
import java.security.Principal;
import java.security.acl.Group;
import java.util.Enumeration;
/* ---------------------------------------------------- */
/** StrictRoleCheckPolicy
* <p>Enforces that if a runAsRole is present, then the
* role to check must be the same as that runAsRole and
* the set of static roles is ignored.
*
*
*
*/
public class StrictRoleCheckPolicy implements RoleCheckPolicy
{
@Override
public boolean checkRole (String roleName, Principal runAsRole, Group roles)
{
//check if this user has had any temporary role pushed onto
//them. If so, then only check if the user has that role.
if (runAsRole != null)
{
return (roleName.equals(runAsRole.getName()));
}
else
{
if (roles == null)
return false;
Enumeration<? extends Principal> rolesEnum = roles.members();
boolean found = false;
while (rolesEnum.hasMoreElements() && !found)
{
Principal p = (Principal)rolesEnum.nextElement();
found = roleName.equals(p.getName());
}
return found;
}
}
}

View File

@ -29,6 +29,7 @@ import java.util.Optional;
import java.util.Set;
import java.util.concurrent.CompletableFuture;
import java.util.stream.Collectors;
import javax.websocket.CloseReason;
import javax.websocket.Decoder;
import javax.websocket.EndpointConfig;
@ -234,7 +235,7 @@ public class JavaxWebSocketFrameHandler implements FrameHandler
}
catch (Throwable cause)
{
Exception wse = new WebSocketException(endpointInstance.getClass().getName() + " OPEN method error: " + cause.getMessage(), cause);
Exception wse = new WebSocketException(endpointInstance.getClass().getSimpleName() + " OPEN method error: " + cause.getMessage(), cause);
callback.failed(wse);
futureSession.completeExceptionally(wse);
}
@ -283,12 +284,13 @@ public class JavaxWebSocketFrameHandler implements FrameHandler
closeHandle.invoke(closeReason);
}
callback.succeeded();
container.notifySessionListeners((listener) -> listener.onJavaxWebSocketSessionClosed(session));
}
catch (Throwable cause)
{
callback.failed(new WebSocketException(endpointInstance.getClass().getName() + " CLOSE method error: " + cause.getMessage(), cause));
callback.failed(new WebSocketException(endpointInstance.getClass().getSimpleName() + " CLOSE method error: " + cause.getMessage(), cause));
}
container.notifySessionListeners((listener) -> listener.onJavaxWebSocketSessionClosed(session));
}
@Override
@ -306,7 +308,7 @@ public class JavaxWebSocketFrameHandler implements FrameHandler
}
catch (Throwable t)
{
WebSocketException wsError = new WebSocketException(endpointInstance.getClass().getName() + " ERROR method error: " + cause.getMessage(), t);
WebSocketException wsError = new WebSocketException(endpointInstance.getClass().getSimpleName() + " ERROR method error: " + cause.getMessage(), t);
wsError.addSuppressed(cause);
callback.failed(wsError);
// TODO should futureSession be failed here?
@ -609,7 +611,7 @@ public class JavaxWebSocketFrameHandler implements FrameHandler
}
catch (Throwable cause)
{
throw new WebSocketException(endpointInstance.getClass().getName() + " PONG method error: " + cause.getMessage(), cause);
throw new WebSocketException(endpointInstance.getClass().getSimpleName() + " PONG method error: " + cause.getMessage(), cause);
}
}
callback.succeeded();

View File

@ -21,6 +21,7 @@ package org.eclipse.jetty.websocket.javax.common;
import java.util.Collections;
import java.util.Set;
import java.util.concurrent.CopyOnWriteArraySet;
import javax.websocket.Session;
import org.eclipse.jetty.util.component.AbstractLifeCycle;
@ -44,7 +45,7 @@ public class SessionTracker extends AbstractLifeCycle implements JavaxWebSocketS
@Override
public void onJavaxWebSocketSessionClosed(JavaxWebSocketSession session)
{
sessions.remove(sessions);
sessions.remove(session);
}
@Override

View File

@ -18,20 +18,6 @@
package org.eclipse.jetty.websocket.javax.tests.server;
import org.eclipse.jetty.util.BlockingArrayQueue;
import org.eclipse.jetty.websocket.core.Frame;
import org.eclipse.jetty.websocket.core.OpCode;
import org.eclipse.jetty.websocket.javax.tests.Fuzzer;
import org.eclipse.jetty.websocket.javax.tests.LocalServer;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;
import javax.websocket.OnMessage;
import javax.websocket.OnOpen;
import javax.websocket.Session;
import javax.websocket.server.ServerEndpoint;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
@ -39,9 +25,22 @@ import java.util.Collection;
import java.util.List;
import java.util.concurrent.TimeUnit;
import javax.websocket.OnMessage;
import javax.websocket.OnOpen;
import javax.websocket.Session;
import javax.websocket.server.ServerEndpoint;
import org.eclipse.jetty.util.BlockingArrayQueue;
import org.eclipse.jetty.websocket.core.Frame;
import org.eclipse.jetty.websocket.core.OpCode;
import org.eclipse.jetty.websocket.javax.tests.Fuzzer;
import org.eclipse.jetty.websocket.javax.tests.LocalServer;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertNotNull;
@Disabled("Replaced with ServerCloseTest.testOpenSessionCleanup()") // TODO: Remove once ServerCloseTest is fixed
public class SessionTrackingTest
{

View File

@ -33,6 +33,7 @@ import java.util.function.Consumer;
import org.eclipse.jetty.client.HttpClient;
import org.eclipse.jetty.io.ByteBufferPool;
import org.eclipse.jetty.io.Connection;
import org.eclipse.jetty.util.DecoratedObjectFactory;
import org.eclipse.jetty.util.component.ContainerLifeCycle;
import org.eclipse.jetty.util.log.Log;
@ -49,6 +50,7 @@ import org.eclipse.jetty.websocket.common.JettyWebSocketFrameHandlerFactory;
import org.eclipse.jetty.websocket.common.SessionTracker;
import org.eclipse.jetty.websocket.common.WebSocketContainer;
import org.eclipse.jetty.websocket.common.WebSocketSessionListener;
import org.eclipse.jetty.websocket.core.FrameHandler;
import org.eclipse.jetty.websocket.core.WebSocketExtensionRegistry;
import org.eclipse.jetty.websocket.core.client.UpgradeListener;
import org.eclipse.jetty.websocket.core.client.WebSocketCoreClient;
@ -61,23 +63,16 @@ public class WebSocketClient extends ContainerLifeCycle implements WebSocketPoli
private final JettyWebSocketFrameHandlerFactory frameHandlerFactory;
private final List<WebSocketSessionListener> sessionListeners = new CopyOnWriteArrayList<>();
private final SessionTracker sessionTracker = new SessionTracker();
private ClassLoader contextClassLoader;
private final FrameHandler.ConfigurationCustomizer configurationCustomizer = new FrameHandler.ConfigurationCustomizer();
private DecoratedObjectFactory objectFactory;
private WebSocketExtensionRegistry extensionRegistry;
private int inputBufferSize = 4 * 1024;
private int outputBufferSize = 4 * 1024;
private long maxBinaryMessageSize = 64 * 1024;
private long maxTextMessageSize = 64 * 1024;
/**
* Instantiate a WebSocketClient with defaults
*/
public WebSocketClient()
{
this(new WebSocketCoreClient());
this.coreClient.getHttpClient().setName("Jetty-WebSocketClient@" + hashCode());
// We created WebSocketCoreClient, let lifecycle be managed by us
addManaged(coreClient);
this(null);
}
/**
@ -87,19 +82,16 @@ public class WebSocketClient extends ContainerLifeCycle implements WebSocketPoli
*/
public WebSocketClient(HttpClient httpClient)
{
this(new WebSocketCoreClient(httpClient));
// We created WebSocketCoreClient, let lifecycle be managed by us
coreClient = new WebSocketCoreClient(httpClient, configurationCustomizer);
addManaged(coreClient);
}
private WebSocketClient(WebSocketCoreClient coreClient)
{
this.coreClient = coreClient;
this.contextClassLoader = this.getClass().getClassLoader();
this.objectFactory = new DecoratedObjectFactory();
this.extensionRegistry = new WebSocketExtensionRegistry();
this.frameHandlerFactory = new JettyWebSocketFrameHandlerFactory(this);
this.sessionListeners.add(sessionTracker);
if (httpClient == null)
coreClient.getHttpClient().setName("Jetty-WebSocketClient@" + hashCode());
objectFactory = new DecoratedObjectFactory();
extensionRegistry = new WebSocketExtensionRegistry();
frameHandlerFactory = new JettyWebSocketFrameHandlerFactory(this);
sessionListeners.add(sessionTracker);
addBean(sessionTracker);
}
@ -128,15 +120,19 @@ public class WebSocketClient extends ContainerLifeCycle implements WebSocketPoli
* @param websocket the websocket object
* @param toUri the websocket uri to connect to
* @param request the upgrade request information
* @param listener the upgrade listener
* @param upgradeListener the upgrade listener
* @return the future for the session, available on success of connect
* @throws IOException if unable to connect
*/
public CompletableFuture<Session> connect(Object websocket, URI toUri, UpgradeRequest request, UpgradeListener listener) throws IOException
public CompletableFuture<Session> connect(Object websocket, URI toUri, UpgradeRequest request, UpgradeListener upgradeListener) throws IOException
{
for (Connection.Listener listener : getBeans(Connection.Listener.class))
coreClient.addBean(listener);
JettyClientUpgradeRequest upgradeRequest = new JettyClientUpgradeRequest(this, coreClient, request, toUri, websocket);
if (listener != null)
upgradeRequest.addListener(listener);
if (upgradeListener != null)
upgradeRequest.addListener(upgradeListener);
coreClient.connect(upgradeRequest);
return upgradeRequest.getFutureSession();
}
@ -184,61 +180,61 @@ public class WebSocketClient extends ContainerLifeCycle implements WebSocketPoli
@Override
public Duration getIdleTimeout()
{
return Duration.ofMillis(getHttpClient().getIdleTimeout());
return configurationCustomizer.getIdleTimeout();
}
@Override
public int getInputBufferSize()
{
return this.inputBufferSize;
return configurationCustomizer.getInputBufferSize();
}
@Override
public int getOutputBufferSize()
{
return this.outputBufferSize;
return configurationCustomizer.getOutputBufferSize();
}
@Override
public long getMaxBinaryMessageSize()
{
return this.maxBinaryMessageSize;
return configurationCustomizer.getMaxBinaryMessageSize();
}
@Override
public long getMaxTextMessageSize()
{
return this.maxTextMessageSize;
return configurationCustomizer.getMaxTextMessageSize();
}
@Override
public void setIdleTimeout(Duration duration)
{
getHttpClient().setIdleTimeout(duration.toMillis());
configurationCustomizer.setIdleTimeout(duration);
}
@Override
public void setInputBufferSize(int size)
{
this.inputBufferSize = size;
configurationCustomizer.setInputBufferSize(size);
}
@Override
public void setOutputBufferSize(int size)
{
this.outputBufferSize = size;
configurationCustomizer.setOutputBufferSize(size);
}
@Override
public void setMaxBinaryMessageSize(long size)
{
this.maxBinaryMessageSize = size;
configurationCustomizer.setMaxBinaryMessageSize(size);
}
@Override
public void setMaxTextMessageSize(long size)
{
this.maxTextMessageSize = size;
configurationCustomizer.setMaxTextMessageSize(size);
}
public SocketAddress getBindAddress()

View File

@ -150,7 +150,7 @@ public class JettyWebSocketFrameHandler implements FrameHandler
}
catch (Throwable cause)
{
callback.failed(new WebSocketException(endpointInstance.getClass().getName() + " OPEN method error: " + cause.getMessage(), cause));
callback.failed(new WebSocketException(endpointInstance.getClass().getSimpleName() + " OPEN method error: " + cause.getMessage(), cause));
futureSession.completeExceptionally(cause);
}
}
@ -172,7 +172,7 @@ public class JettyWebSocketFrameHandler implements FrameHandler
}
catch (Throwable cause)
{
throw new WebSocketException(endpointInstance.getClass().getName() + " FRAME method error: " + cause.getMessage(), cause);
throw new WebSocketException(endpointInstance.getClass().getSimpleName() + " FRAME method error: " + cause.getMessage(), cause);
}
}
@ -219,7 +219,7 @@ public class JettyWebSocketFrameHandler implements FrameHandler
}
catch (Throwable t)
{
WebSocketException wsError = new WebSocketException(endpointInstance.getClass().getName() + " ERROR method error: " + cause.getMessage(), t);
WebSocketException wsError = new WebSocketException(endpointInstance.getClass().getSimpleName() + " ERROR method error: " + cause.getMessage(), t);
wsError.addSuppressed(cause);
callback.failed(wsError);
}
@ -228,7 +228,18 @@ public class JettyWebSocketFrameHandler implements FrameHandler
@Override
public void onClosed(CloseStatus closeStatus, Callback callback)
{
callback.succeeded();
try
{
if (closeHandle != null)
closeHandle.invoke(closeStatus.getCode(), closeStatus.getReason());
callback.succeeded();
}
catch (Throwable cause)
{
callback.failed(new WebSocketException(endpointInstance.getClass().getSimpleName() + " CLOSE method error: " + cause.getMessage(), cause));
}
container.notifySessionListeners((listener) -> listener.onWebSocketSessionClosed(session));
}
@ -262,18 +273,6 @@ public class JettyWebSocketFrameHandler implements FrameHandler
private void onCloseFrame(Frame frame, Callback callback)
{
if (closeHandle != null)
{
try
{
CloseStatus close = new CloseStatus(frame.getPayload());
closeHandle.invoke(close.getCode(), close.getReason());
}
catch (Throwable cause)
{
throw new WebSocketException(endpointInstance.getClass().getName() + " CLOSE method error: " + cause.getMessage(), cause);
}
}
callback.succeeded();
}
@ -296,7 +295,7 @@ public class JettyWebSocketFrameHandler implements FrameHandler
}
catch (Throwable cause)
{
throw new WebSocketException(endpointInstance.getClass().getName() + " PING method error: " + cause.getMessage(), cause);
throw new WebSocketException(endpointInstance.getClass().getSimpleName() + " PING method error: " + cause.getMessage(), cause);
}
}
else
@ -324,7 +323,7 @@ public class JettyWebSocketFrameHandler implements FrameHandler
}
catch (Throwable cause)
{
throw new WebSocketException(endpointInstance.getClass().getName() + " PONG method error: " + cause.getMessage(), cause);
throw new WebSocketException(endpointInstance.getClass().getSimpleName() + " PONG method error: " + cause.getMessage(), cause);
}
}
callback.succeeded();

View File

@ -19,10 +19,12 @@
package org.eclipse.jetty.websocket.common;
import java.util.Iterator;
import java.util.NoSuchElementException;
import java.util.concurrent.LinkedBlockingDeque;
import static org.eclipse.jetty.toolchain.test.matchers.RegexMatcher.matchesPattern;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.junit.jupiter.api.Assertions.fail;
public class EventQueue extends LinkedBlockingDeque<String>
{
@ -33,10 +35,18 @@ public class EventQueue extends LinkedBlockingDeque<String>
public void assertEvents(String... regexEvents)
{
Iterator<String> capturedIterator = iterator();
for (int i = 0; i < regexEvents.length; i++)
int i = 0;
try
{
assertThat("Event [" + i + "]", capturedIterator.next(), matchesPattern(regexEvents[i]));
Iterator<String> capturedIterator = iterator();
for (i = 0; i < regexEvents.length; i++)
{
assertThat("Event [" + i + "]", capturedIterator.next(), matchesPattern(regexEvents[i]));
}
}
catch (NoSuchElementException e)
{
fail("Event [" + (i) + "] not found: " + regexEvents[i]);
}
}
}

View File

@ -121,7 +121,7 @@ public class JettyWebSocketFrameHandlerTest
// Trigger Events
localEndpoint.onOpen(channel, Callback.NOOP);
localEndpoint.onFrame(new Frame(OpCode.TEXT).setPayload("Hello?").setFin(true), Callback.NOOP);
localEndpoint.onFrame(CloseStatus.toFrame(StatusCode.NORMAL, "Normal"), Callback.NOOP);
localEndpoint.onClosed(new CloseStatus(StatusCode.NORMAL, "Normal"), Callback.NOOP);
// Validate Events
socket.events.assertEvents(
@ -216,6 +216,7 @@ public class JettyWebSocketFrameHandlerTest
localEndpoint.onFrame(new Frame(OpCode.CONTINUATION).setPayload(" the ").setFin(false), Callback.NOOP);
localEndpoint.onFrame(new Frame(OpCode.CONTINUATION).setPayload("Pig").setFin(true), Callback.NOOP);
localEndpoint.onFrame(CloseStatus.toFrame(StatusCode.NORMAL), Callback.NOOP);
localEndpoint.onClosed(CloseStatus.NORMAL_STATUS, Callback.NOOP);
// Validate Events
socket.events.assertEvents(
@ -245,7 +246,7 @@ public class JettyWebSocketFrameHandlerTest
localEndpoint.onFrame(new Frame(OpCode.BINARY).setPayload("Save").setFin(false), Callback.NOOP);
localEndpoint.onFrame(new Frame(OpCode.CONTINUATION).setPayload(" the ").setFin(false), Callback.NOOP);
localEndpoint.onFrame(new Frame(OpCode.CONTINUATION).setPayload("Pig").setFin(true), Callback.NOOP);
localEndpoint.onFrame(CloseStatus.toFrame(StatusCode.NORMAL, "Normal"), Callback.NOOP);
localEndpoint.onClosed(new CloseStatus(StatusCode.NORMAL, "Normal"), Callback.NOOP);
// Validate Events
socket.events.assertEvents(
@ -323,7 +324,7 @@ public class JettyWebSocketFrameHandlerTest
localEndpoint.onFrame(new Frame(OpCode.CONTINUATION).setPayload(" the ").setFin(false), Callback.NOOP);
localEndpoint.onFrame(new Frame(OpCode.PONG).setPayload("You there?"), Callback.NOOP);
localEndpoint.onFrame(new Frame(OpCode.CONTINUATION).setPayload("Pig").setFin(true), Callback.NOOP);
localEndpoint.onFrame(CloseStatus.toFrame(StatusCode.NORMAL, "Normal"), Callback.NOOP);
localEndpoint.onClosed(new CloseStatus(StatusCode.NORMAL, "Normal"), Callback.NOOP);
// Validate Events
socket.events.assertEvents(

View File

@ -43,7 +43,6 @@ import org.eclipse.jetty.websocket.servlet.WebSocketServletFactory;
import org.eclipse.jetty.websocket.tests.CloseTrackingEndpoint;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;
import static org.hamcrest.Matchers.containsString;
@ -52,7 +51,6 @@ import static org.hamcrest.Matchers.is;
/**
* Tests for conditions due to bad networking.
*/
@Disabled("Needs triage")
public class BadNetworkTest
{
private Server server;

View File

@ -28,7 +28,6 @@ import java.util.Arrays;
import java.util.List;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import org.eclipse.jetty.io.EndPoint;
import org.eclipse.jetty.server.Server;
@ -39,12 +38,12 @@ import org.eclipse.jetty.servlet.ServletContextHandler;
import org.eclipse.jetty.servlet.ServletHolder;
import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger;
import org.eclipse.jetty.websocket.api.CloseException;
import org.eclipse.jetty.websocket.api.MessageTooLargeException;
import org.eclipse.jetty.websocket.api.Session;
import org.eclipse.jetty.websocket.api.StatusCode;
import org.eclipse.jetty.websocket.api.WebSocketFrameListener;
import org.eclipse.jetty.websocket.api.WebSocketListener;
import org.eclipse.jetty.websocket.api.WebSocketTimeoutException;
import org.eclipse.jetty.websocket.api.util.WSURI;
import org.eclipse.jetty.websocket.client.WebSocketClient;
import org.eclipse.jetty.websocket.core.CloseStatus;
@ -55,7 +54,6 @@ import org.eclipse.jetty.websocket.servlet.WebSocketServletFactory;
import org.eclipse.jetty.websocket.tests.CloseTrackingEndpoint;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;
import static java.nio.charset.StandardCharsets.UTF_8;
@ -68,7 +66,6 @@ import static org.hamcrest.Matchers.is;
import static org.hamcrest.Matchers.nullValue;
import static org.junit.jupiter.api.Assertions.assertTimeoutPreemptively;
@Disabled("Needs triage")
public class ClientCloseTest
{
private Server server;
@ -252,8 +249,8 @@ public class ClientCloseTest
// client reads -1 (EOF)
// client triggers close event on client ws-endpoint
clientSocket.assertReceivedCloseEvent(clientTimeout * 2,
is(StatusCode.SHUTDOWN),
containsString("timeout"));
is(StatusCode.ABNORMAL),
containsString("Channel Closed"));
}
clientSessionTracker.assertClosedProperly(client);
@ -285,12 +282,11 @@ public class ClientCloseTest
// client close should occur
clientSocket.assertReceivedCloseEvent(clientTimeout * 2,
is(StatusCode.SHUTDOWN),
containsString("timeout"));
containsString("Timeout"));
// client idle timeout triggers close event on client ws-endpoint
assertThat("OnError Latch", clientSocket.errorLatch.await(2, SECONDS), is(true));
assertThat("OnError", clientSocket.error.get(), instanceOf(CloseException.class));
assertThat("OnError.cause", clientSocket.error.get().getCause(), instanceOf(TimeoutException.class));
assertThat("OnError", clientSocket.error.get(), instanceOf(WebSocketTimeoutException.class));
}
clientSessionTracker.assertClosedProperly(client);
@ -330,7 +326,7 @@ public class ClientCloseTest
// clients disconnect
for (int i = 0; i < sessionCount; i++)
{
clientSockets.get(i).assertReceivedCloseEvent(timeout, is(StatusCode.ABNORMAL), containsString("Disconnected"));
clientSockets.get(i).assertReceivedCloseEvent(timeout, is(StatusCode.ABNORMAL), containsString("Channel Closed"));
}
// ensure all Sessions are gone. connections are gone. etc. (client and server)
@ -370,7 +366,7 @@ public class ClientCloseTest
// client triggers close event on client ws-endpoint
// assert - close code==1006 (abnormal)
clientSocket.assertReceivedCloseEvent(timeout, is(StatusCode.ABNORMAL), containsString("Eof"));
clientSocket.assertReceivedCloseEvent(timeout, is(StatusCode.ABNORMAL), containsString("Channel Closed"));
clientSessionTracker.assertClosedProperly(client);
}

View File

@ -46,7 +46,6 @@ import org.eclipse.jetty.websocket.tests.CloseTrackingEndpoint;
import org.eclipse.jetty.websocket.tests.EchoCreator;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;
import static org.hamcrest.MatcherAssert.assertThat;
@ -55,7 +54,6 @@ import static org.hamcrest.Matchers.is;
import static org.hamcrest.Matchers.notNullValue;
import static org.junit.jupiter.api.Assertions.assertTrue;
@Disabled("Needs triage")
public class ClientSessionsTest
{
private Server server;
@ -147,6 +145,8 @@ public class ClientSessionsTest
String received = cliSock.messageQueue.poll(5, TimeUnit.SECONDS);
assertThat("Message", received, containsString("Hello World!"));
sess.close(StatusCode.NORMAL, null);
}
cliSock.assertReceivedCloseEvent(30000, is(StatusCode.NORMAL));

View File

@ -36,13 +36,13 @@ import org.eclipse.jetty.websocket.api.util.WSURI;
import org.eclipse.jetty.websocket.client.ClientUpgradeRequest;
import org.eclipse.jetty.websocket.client.WebSocketClient;
import org.eclipse.jetty.websocket.common.WebSocketSessionImpl;
import org.eclipse.jetty.websocket.core.internal.WebSocketChannel;
import org.eclipse.jetty.websocket.server.JettyWebSocketServletContainerInitializer;
import org.eclipse.jetty.websocket.servlet.WebSocketServlet;
import org.eclipse.jetty.websocket.servlet.WebSocketServletFactory;
import org.eclipse.jetty.websocket.tests.CloseTrackingEndpoint;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;
import static java.util.concurrent.TimeUnit.SECONDS;
@ -55,7 +55,6 @@ import static org.hamcrest.Matchers.is;
/**
* Tests various close scenarios
*/
@Disabled("Needs triage")
public class ServerCloseTest
{
private WebSocketClient client;
@ -150,7 +149,7 @@ public class ServerCloseTest
// Verify that server socket got close event
AbstractCloseEndpoint serverEndpoint = serverEndpointCreator.pollLastCreated();
assertThat("Fast Close Latch", serverEndpoint.closeLatch.await(5, SECONDS), is(true));
assertThat("Fast Close.statusCode", serverEndpoint.closeStatusCode, is(StatusCode.ABNORMAL));
assertThat("Fast Close.statusCode", serverEndpoint.closeStatusCode, is(StatusCode.NORMAL));
}
finally
{
@ -174,7 +173,7 @@ public class ServerCloseTest
Future<Session> futSession = client.connect(clientEndpoint, wsUri, request);
Session session = null;
try(StacklessLogging ignore = new StacklessLogging(FastFailEndpoint.class, WebSocketSessionImpl.class))
try(StacklessLogging ignore = new StacklessLogging(WebSocketChannel.class))
{
session = futSession.get(5, SECONDS);
@ -221,12 +220,13 @@ public class ServerCloseTest
clientEndpoint.getEndPoint().close();
// Verify that client got close
clientEndpoint.assertReceivedCloseEvent(5000, is(StatusCode.ABNORMAL), containsString("Disconnected"));
clientEndpoint.assertReceivedCloseEvent(5000, is(StatusCode.ABNORMAL), containsString("Channel Closed"));
// Verify that server socket got close event
AbstractCloseEndpoint serverEndpoint = serverEndpointCreator.pollLastCreated();
serverEndpoint.assertReceivedCloseEvent(5000, is(StatusCode.ABNORMAL), containsString("Disconnected"));
} finally
serverEndpoint.assertReceivedCloseEvent(5000, is(StatusCode.ABNORMAL), containsString("Channel Closed"));
}
finally
{
close(session);
}

View File

@ -134,7 +134,7 @@ public class WebSocketChannelState
default:
if (_closeStatus == null || CloseStatus.isOrdinary(_closeStatus))
_closeStatus = CloseStatus.NO_CLOSE_STATUS;
_closeStatus = new CloseStatus(CloseStatus.NO_CLOSE, "Channel Closed");
_channelState = State.CLOSED;
return true;
}

View File

@ -2,7 +2,7 @@
<!DOCTYPE Configure PUBLIC "-//Mort Bay Consulting//DTD Configure//EN" "http://www.eclipse.org/jetty/configure_9_3.dtd">
<!-- =============================================================== -->
<!-- Configure the test-jndi webapp -->
<!-- Configure the test-jaas webapp -->
<!-- =============================================================== -->
<Configure id='wac' class="org.eclipse.jetty.webapp.WebAppContext">