Merge remote-tracking branch 'eclipse/jetty-10.0.x' into jetty-10.0.x-3298-completableFutures
This commit is contained in:
commit
684dcd1693
|
@ -23,7 +23,7 @@ A basic adapter for managing the Session object on the WebSocketListener.
|
|||
|
||||
[source, java, subs="{sub-order}"]
|
||||
----
|
||||
include::{SRCDIR}/jetty-websocket/websocket-common/src/test/java/examples/echo/AdapterEchoSocket.java[]
|
||||
include::{SRCDIR}/jetty-websocket/jetty-websocket-common/src/test/java/org/eclipse/jetty/websocket/common/endpoints/adapters/AdapterEchoSocket.java[]
|
||||
----
|
||||
|
||||
This is a convenience class to make using the WebSocketListener easier, and provides some useful methods to check the state of the Session.
|
||||
|
|
|
@ -24,7 +24,7 @@ provided by the Jetty WebSocket API.
|
|||
|
||||
[source, java, subs="{sub-order}"]
|
||||
----
|
||||
include::{SRCDIR}/jetty-websocket/websocket-common/src/test/java/examples/echo/AnnotatedEchoSocket.java[]
|
||||
include::{SRCDIR}/jetty-websocket/jetty-websocket-common/src/test/java/org/eclipse/jetty/websocket/common/endpoints/adapters/AnnotatedEchoSocket.java[]
|
||||
----
|
||||
|
||||
The above example is a simple WebSocket echo endpoint that will echo back any TEXT messages it receives.
|
||||
|
|
|
@ -23,7 +23,7 @@ The basic form of a WebSocket using the link:{JDURL}/org/eclipse/jetty/websocket
|
|||
|
||||
[source, java, subs="{sub-order}"]
|
||||
----
|
||||
include::{SRCDIR}/jetty-websocket/websocket-common/src/test/java/examples/echo/ListenerEchoSocket.java[]
|
||||
include::{SRCDIR}/jetty-websocket/jetty-websocket-common/src/test/java/org/eclipse/jetty/websocket/common/endpoints/adapters/ListenerEchoSocket.java[]
|
||||
----
|
||||
|
||||
This is by far the most basic and best performing (speed and memory wise) WebSocket implementation you can create.
|
||||
|
|
|
@ -38,14 +38,14 @@ To use the WebSocketClient you will need to hook up a WebSocket object instance
|
|||
|
||||
[source, java, subs="{sub-order}"]
|
||||
----
|
||||
include::{SRCDIR}/jetty-websocket/websocket-client/src/test/java/examples/SimpleEchoClient.java[]
|
||||
include::{SRCDIR}/jetty-websocket/jetty-websocket-client/src/test/java/examples/SimpleEchoClient.java[]
|
||||
----
|
||||
|
||||
The above example connects to a remote WebSocket server and hands off a SimpleEchoSocket to perform the logic on the websocket once connected, waiting for the socket to register that it has closed.
|
||||
|
||||
[source, java, subs="{sub-order}"]
|
||||
----
|
||||
include::{SRCDIR}/jetty-websocket/websocket-client/src/test/java/examples/SimpleEchoSocket.java[]
|
||||
include::{SRCDIR}/jetty-websocket/jetty-websocket-client/src/test/java/examples/SimpleEchoSocket.java[]
|
||||
----
|
||||
|
||||
When the SimpleEchoSocket connects, it sends 2 Text messages and then closes the socket.
|
||||
|
|
|
@ -31,7 +31,7 @@ To wire up your WebSocket to a specific path via the WebSocketServlet, you will
|
|||
|
||||
[source, java, subs="{sub-order}"]
|
||||
----
|
||||
include::{SRCDIR}/jetty-websocket/websocket-servlet/src/test/java/examples/MyEchoServlet.java[]
|
||||
include::{SRCDIR}/jetty-websocket/websocket-tests/src/test/java/org/eclipse/jetty/websocket/tests/examples/MyEchoServlet.java[]
|
||||
----
|
||||
|
||||
This example will create a Servlet mapped via the http://docs.oracle.com/javaee/6/api/javax/servlet/annotation/WebServlet.html[@WebServlet] annotation to the Servlet path spec of `"/echo"` (or you can do this manually in the `WEB-INF/web.xml` of your web application) which will create MyEchoSocket instances when encountering HTTP Upgrade requests.
|
||||
|
@ -57,7 +57,7 @@ If you have a more complicated creation scenario, you might want to provide your
|
|||
|
||||
[source, java, subs="{sub-order}"]
|
||||
----
|
||||
include::{SRCDIR}/jetty-websocket/websocket-servlet/src/test/java/examples/MyAdvancedEchoCreator.java[]
|
||||
include::{SRCDIR}/jetty-websocket/websocket-tests/src/test/java/org/eclipse/jetty/websocket/tests/examples/MyAdvancedEchoCreator.java[]
|
||||
----
|
||||
|
||||
Here we show a WebSocketCreator that will utilize the http://tools.ietf.org/html/rfc6455#section-1.9[WebSocket subprotocol] information from request to determine what WebSocket type should be
|
||||
|
@ -65,7 +65,7 @@ created.
|
|||
|
||||
[source, java, subs="{sub-order}"]
|
||||
----
|
||||
include::{SRCDIR}/jetty-websocket/websocket-servlet/src/test/java/examples/MyAdvancedEchoServlet.java[]
|
||||
include::{SRCDIR}/jetty-websocket/websocket-tests/src/test/java/org/eclipse/jetty/websocket/tests/examples/MyAdvancedEchoServlet.java[]
|
||||
----
|
||||
|
||||
When you want a custom WebSocketCreator, use link:{JDURL}/org/eclipse/jetty/websocket/servlet/WebSocketServletFactory.html#setCreator(org.eclipse.jetty.websocket.servlet.WebSocketCreator)[`WebSocketServletFactory.setCreator(WebSocketCreator creator)`] and the WebSocketServletFactory will use your creator for all incoming Upgrade requests on this servlet.
|
||||
|
|
|
@ -73,20 +73,20 @@
|
|||
javax.servlet.jsp.jstl.fmt;version="1.2";resolution:=optional,
|
||||
javax.servlet.jsp.jstl.sql;version="1.2";resolution:=optional,
|
||||
javax.servlet.jsp.jstl.tlv;version="1.2";resolution:=optional,
|
||||
org.apache.el;version="[8.0.23,9)";resolution:=optional,
|
||||
org.apache.el.lang;version="[8.0.23,9)";resolution:=optional,
|
||||
org.apache.el.stream;version="[8.0.23,9)";resolution:=optional,
|
||||
org.apache.el.util;version="[8.0.23,9)";resolution:=optional,
|
||||
org.apache.el.parser;version="[8.0.23,9)";resolution:=optional,
|
||||
org.apache.jasper;version="[8.0.23,9)";resolution:=optional,
|
||||
org.apache.jasper.compiler;version="[8.0.23,9)";resolution:=optional,
|
||||
org.apache.jasper.compiler.tagplugin;version="[8.0.23,9)";resolution:=optional,
|
||||
org.apache.jasper.runtime;version="[8.0.23,9)";resolution:=optional,
|
||||
org.apache.jasper.security;version="[8.0.23,9)";resolution:=optional,
|
||||
org.apache.jasper.servlet;version="[8.0.23,9)";resolution:=optional,
|
||||
org.apache.jasper.tagplugins.jstl;version="[8.0.23,9)";resolution:=optional,
|
||||
org.apache.jasper.util;version="[8.0.23,9)";resolution:=optional,
|
||||
org.apache.jasper.xmlparser;version="[8.0.23,9)";resolution:=optional,
|
||||
org.apache.el;version="[8.0.23,10)";resolution:=optional,
|
||||
org.apache.el.lang;version="[8.0.23,10)";resolution:=optional,
|
||||
org.apache.el.stream;version="[8.0.23,10)";resolution:=optional,
|
||||
org.apache.el.util;version="[8.0.23,10)";resolution:=optional,
|
||||
org.apache.el.parser;version="[8.0.23,10)";resolution:=optional,
|
||||
org.apache.jasper;version="[8.0.23,10)";resolution:=optional,
|
||||
org.apache.jasper.compiler;version="[8.0.23,10)";resolution:=optional,
|
||||
org.apache.jasper.compiler.tagplugin;version="[8.0.23,10)";resolution:=optional,
|
||||
org.apache.jasper.runtime;version="[8.0.23,10)";resolution:=optional,
|
||||
org.apache.jasper.security;version="[8.0.23,10)";resolution:=optional,
|
||||
org.apache.jasper.servlet;version="[8.0.23,10)";resolution:=optional,
|
||||
org.apache.jasper.tagplugins.jstl;version="[8.0.23,10)";resolution:=optional,
|
||||
org.apache.jasper.util;version="[8.0.23,10)";resolution:=optional,
|
||||
org.apache.jasper.xmlparser;version="[8.0.23,10)";resolution:=optional,
|
||||
org.apache.taglibs.standard;version="1.2";resolution:=optional,
|
||||
org.apache.taglibs.standard.extra.spath;version="1.2";resolution:=optional,
|
||||
org.apache.taglibs.standard.functions;version="1.2";resolution:=optional,
|
||||
|
@ -110,7 +110,7 @@
|
|||
org.apache.taglibs.standard.tag.rt.xml;version="1.2";resolution:=optional,
|
||||
org.apache.taglibs.standard.tei;version="1.2";resolution:=optional,
|
||||
org.apache.taglibs.standard.tlv;version="1.2";resolution:=optional,
|
||||
org.apache.tomcat;version="[8.0.23,9)";resolution:=optional,
|
||||
org.apache.tomcat;version="[8.0.23,10)";resolution:=optional,
|
||||
org.eclipse.jetty.jsp;version="[$(version;===;${parsedVersion.osgiVersion}),$(version;==+;${parsedVersion.osgiVersion}))";resolution:=optional,
|
||||
org.osgi.*,
|
||||
org.xml.*;resolution:=optional,
|
||||
|
|
|
@ -616,12 +616,10 @@ public abstract class AbstractSessionCache extends ContainerLifeCycle implements
|
|||
{
|
||||
//get the session, if its not in memory, this will load it
|
||||
Session session = get(id);
|
||||
|
||||
|
||||
//Always delete it from the backing data store
|
||||
if (_sessionDataStore != null)
|
||||
{
|
||||
|
||||
boolean dsdel = _sessionDataStore.delete(id);
|
||||
if (LOG.isDebugEnabled()) LOG.debug("Session {} deleted in session data store {}",id, dsdel);
|
||||
}
|
||||
|
@ -635,10 +633,6 @@ public abstract class AbstractSessionCache extends ContainerLifeCycle implements
|
|||
return doDelete(id);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @see org.eclipse.jetty.server.session.SessionCache#checkExpiration(Set)
|
||||
|
|
|
@ -116,7 +116,6 @@ public class Session implements SessionHandler.SessionIf
|
|||
public class SessionInactivityTimer
|
||||
{
|
||||
protected final CyclicTimeout _timer;
|
||||
protected long _msec = -1;
|
||||
|
||||
public SessionInactivityTimer()
|
||||
{
|
||||
|
@ -127,31 +126,45 @@ public class Session implements SessionHandler.SessionIf
|
|||
{
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("Timer expired for session {}", getId());
|
||||
getSessionHandler().sessionInactivityTimerExpired(Session.this);
|
||||
long now = System.currentTimeMillis();
|
||||
//handle what to do with the session after the timer expired
|
||||
getSessionHandler().sessionInactivityTimerExpired(Session.this, now);
|
||||
try (Lock lock = Session.this.lock())
|
||||
{
|
||||
//grab the lock and check what happened to the session: if it didn't get evicted and
|
||||
//it hasn't expired, we need to reset the timer
|
||||
if (Session.this.isResident() && Session.this.getRequests() <= 0 && Session.this.isValid() && !Session.this.isExpiredAt(now))
|
||||
{
|
||||
//session wasn't expired or evicted, we need to reset the timer
|
||||
SessionInactivityTimer.this.schedule(Session.this.calculateInactivityTimeout(now));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param ms the timeout to set; -1 means that the timer will not be
|
||||
* scheduled
|
||||
* For backward api compatibility only.
|
||||
* @see #schedule(long)
|
||||
*/
|
||||
public void setTimeout(long ms)
|
||||
@Deprecated
|
||||
public void schedule ()
|
||||
{
|
||||
_msec = ms;
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("Session {} timer={}ms", getId(), ms);
|
||||
schedule(calculateInactivityTimeout(System.currentTimeMillis()));
|
||||
}
|
||||
|
||||
public void schedule()
|
||||
/**
|
||||
* @param time the timeout to set; -1 means that the timer will not be
|
||||
* scheduled
|
||||
*/
|
||||
public void schedule (long time)
|
||||
{
|
||||
if (_msec > 0)
|
||||
if (time >= 0)
|
||||
{
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("(Re)starting timer for session {} at {}ms", getId(), _msec);
|
||||
_timer.schedule(_msec, TimeUnit.MILLISECONDS);
|
||||
LOG.debug("(Re)starting timer for session {} at {}ms", getId(), time);
|
||||
_timer.schedule(time, TimeUnit.MILLISECONDS);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -279,9 +292,15 @@ public class Session implements SessionHandler.SessionIf
|
|||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("Session {} complete, active requests={}", getId(), _requests);
|
||||
|
||||
// start the inactivity timer
|
||||
// start the inactivity timer if necessary
|
||||
if (_requests == 0)
|
||||
_sessionInactivityTimer.schedule();
|
||||
{
|
||||
//update the expiry time to take account of the time all requests spent inside of the
|
||||
//session.
|
||||
long now = System.currentTimeMillis();
|
||||
_sessionData.calcAndSetExpiry(now);
|
||||
_sessionInactivityTimer.schedule(calculateInactivityTimeout(now));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -513,7 +532,7 @@ public class Session implements SessionHandler.SessionIf
|
|||
_sessionData.setMaxInactiveMs((long) secs * 1000L);
|
||||
_sessionData.calcAndSetExpiry();
|
||||
_sessionData.setDirty(true);
|
||||
updateInactivityTimer();
|
||||
|
||||
if (LOG.isDebugEnabled())
|
||||
{
|
||||
if (secs <= 0)
|
||||
|
@ -524,14 +543,29 @@ public class Session implements SessionHandler.SessionIf
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the inactivity timer to the smaller of the session maxInactivity (ie
|
||||
* session-timeout from web.xml), or the inactive eviction time.
|
||||
*/
|
||||
public void updateInactivityTimer()
|
||||
|
||||
@Deprecated
|
||||
public void updateInactivityTimer()
|
||||
{
|
||||
//for backward api compatibility only
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate what the session timer setting should be based on:
|
||||
* the time remaining before the session expires
|
||||
* and any idle eviction time configured.
|
||||
* The timer value will be the lesser of the above.
|
||||
*
|
||||
* @param now the time at which to calculate remaining expiry
|
||||
* @return the time remaining before expiry or inactivity timeout
|
||||
*/
|
||||
public long calculateInactivityTimeout (long now)
|
||||
{
|
||||
long time = 0;
|
||||
|
||||
try (Lock lock = _lock.lock())
|
||||
{
|
||||
long remaining = _sessionData.getExpiry() - now;
|
||||
long maxInactive = _sessionData.getMaxInactiveMs();
|
||||
int evictionPolicy = getSessionHandler().getSessionCache().getEvictionPolicy();
|
||||
|
||||
|
@ -541,7 +575,7 @@ public class Session implements SessionHandler.SessionIf
|
|||
if (evictionPolicy < SessionCache.EVICT_ON_INACTIVITY)
|
||||
{
|
||||
// we do not want to evict inactive sessions
|
||||
_sessionInactivityTimer.setTimeout(-1);
|
||||
time = -1;
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("Session {} is immortal && no inactivity eviction", getId());
|
||||
}
|
||||
|
@ -549,7 +583,7 @@ public class Session implements SessionHandler.SessionIf
|
|||
{
|
||||
// sessions are immortal but we want to evict after
|
||||
// inactivity
|
||||
_sessionInactivityTimer.setTimeout(TimeUnit.SECONDS.toMillis(evictionPolicy));
|
||||
time = TimeUnit.SECONDS.toMillis(evictionPolicy);
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("Session {} is immortal; evict after {} sec inactivity", getId(), evictionPolicy);
|
||||
}
|
||||
|
@ -559,31 +593,33 @@ public class Session implements SessionHandler.SessionIf
|
|||
// sessions are not immortal
|
||||
if (evictionPolicy == SessionCache.NEVER_EVICT)
|
||||
{
|
||||
// timeout is just the maxInactive setting
|
||||
_sessionInactivityTimer.setTimeout(_sessionData.getMaxInactiveMs());
|
||||
// timeout is the time remaining until its expiry
|
||||
time = (remaining > 0 ? remaining : 0);
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("Session {} no eviction", getId());
|
||||
}
|
||||
else if (evictionPolicy == SessionCache.EVICT_ON_SESSION_EXIT)
|
||||
{
|
||||
// session will not remain in the cache, so no timeout
|
||||
_sessionInactivityTimer.setTimeout(-1);
|
||||
time = -1;
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("Session {} evict on exit", getId());
|
||||
}
|
||||
else
|
||||
{
|
||||
// want to evict on idle: timer is lesser of the session's
|
||||
// maxInactive and eviction timeout
|
||||
_sessionInactivityTimer.setTimeout(Math.min(maxInactive, TimeUnit.SECONDS.toMillis(evictionPolicy)));
|
||||
// expiration remaining and the time to evict
|
||||
time = (remaining > 0 ? (Math.min(maxInactive, TimeUnit.SECONDS.toMillis(evictionPolicy))) : 0);
|
||||
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("Session {} timer set to lesser of maxInactive={} and inactivityEvict={}", getId(), maxInactive, evictionPolicy);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return time;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @see javax.servlet.http.HttpSession#getMaxInactiveInterval()
|
||||
*/
|
||||
|
@ -963,16 +999,6 @@ public class Session implements SessionHandler.SessionIf
|
|||
return _lock.lock();
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------- */
|
||||
/**
|
||||
* Grab the lock on the session if it isn't locked already
|
||||
*
|
||||
* @return the lock
|
||||
*/
|
||||
public Lock lockIfNotHeld()
|
||||
{
|
||||
return _lock.lock();
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------- */
|
||||
/**
|
||||
|
@ -1132,13 +1158,14 @@ public class Session implements SessionHandler.SessionIf
|
|||
}
|
||||
|
||||
/* ------------------------------------------------------------- */
|
||||
/**
|
||||
* @param resident
|
||||
*/
|
||||
public void setResident(boolean resident)
|
||||
{
|
||||
_resident = resident;
|
||||
|
||||
if (_resident)
|
||||
updateInactivityTimer();
|
||||
else
|
||||
if (!_resident)
|
||||
_sessionInactivityTimer.destroy();
|
||||
}
|
||||
|
||||
|
|
|
@ -1372,6 +1372,17 @@ public class SessionHandler extends ScopedHandler
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @see #sessionInactivityTimerExpired(Session, long)
|
||||
*/
|
||||
@Deprecated
|
||||
public void sessionInactivityTimerExpired (Session session)
|
||||
{
|
||||
//for backwards compilation compatibility only
|
||||
sessionInactivityTimerExpired(session, System.currentTimeMillis());
|
||||
}
|
||||
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/**
|
||||
* Each session has a timer that is configured to go off
|
||||
|
@ -1379,20 +1390,28 @@ public class SessionHandler extends ScopedHandler
|
|||
* configurable amount of time, or the session itself
|
||||
* has passed its expiry.
|
||||
*
|
||||
* If it has passed its expiry, then we will mark it for
|
||||
* scavenging by next run of the HouseKeeper; if it has
|
||||
* been idle longer than the configured eviction period,
|
||||
* we evict from the cache.
|
||||
*
|
||||
* If none of the above are true, then the System timer
|
||||
* is inconsistent and the caller of this method will
|
||||
* need to reset the timer.
|
||||
*
|
||||
* @param session the session
|
||||
* @param now the time at which to check for expiry
|
||||
*/
|
||||
public void sessionInactivityTimerExpired (Session session)
|
||||
public void sessionInactivityTimerExpired (Session session, long now)
|
||||
{
|
||||
if (session == null)
|
||||
return;
|
||||
|
||||
|
||||
//check if the session is:
|
||||
//1. valid
|
||||
//2. expired
|
||||
//3. idle
|
||||
boolean expired = false;
|
||||
try (Lock lock = session.lockIfNotHeld())
|
||||
try (Lock lock = session.lock())
|
||||
{
|
||||
if (session.getRequests() > 0)
|
||||
return; //session can't expire or be idle if there is a request in it
|
||||
|
@ -1402,27 +1421,27 @@ public class SessionHandler extends ScopedHandler
|
|||
|
||||
if (!session.isValid())
|
||||
return; //do nothing, session is no longer valid
|
||||
|
||||
if (session.isExpiredAt(System.currentTimeMillis()) && session.getRequests() <=0)
|
||||
expired = true;
|
||||
}
|
||||
|
||||
if (expired)
|
||||
{
|
||||
//instead of expiring the session directly here, accumulate a list of
|
||||
//session ids that need to be expired. This is an efficiency measure: as
|
||||
//the expiration involves the SessionDataStore doing a delete, it is
|
||||
//most efficient if it can be done as a bulk operation to eg reduce
|
||||
//roundtrips to the persistent store. Only do this if the HouseKeeper that
|
||||
//does the scavenging is configured to actually scavenge
|
||||
if (_sessionIdManager.getSessionHouseKeeper() != null && _sessionIdManager.getSessionHouseKeeper().getIntervalSec() > 0)
|
||||
if (session.isExpiredAt(now))
|
||||
{
|
||||
_candidateSessionIdsForExpiry.add(session.getId());
|
||||
if (LOG.isDebugEnabled())LOG.debug("Session {} is candidate for expiry", session.getId());
|
||||
//instead of expiring the session directly here, accumulate a list of
|
||||
//session ids that need to be expired. This is an efficiency measure: as
|
||||
//the expiration involves the SessionDataStore doing a delete, it is
|
||||
//most efficient if it can be done as a bulk operation to eg reduce
|
||||
//roundtrips to the persistent store. Only do this if the HouseKeeper that
|
||||
//does the scavenging is configured to actually scavenge
|
||||
if (_sessionIdManager.getSessionHouseKeeper() != null && _sessionIdManager.getSessionHouseKeeper().getIntervalSec() > 0)
|
||||
{
|
||||
_candidateSessionIdsForExpiry.add(session.getId());
|
||||
if (LOG.isDebugEnabled())LOG.debug("Session {} is candidate for expiry", session.getId());
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
//possibly evict the session
|
||||
_sessionCache.checkInactiveSession(session);
|
||||
}
|
||||
}
|
||||
else
|
||||
_sessionCache.checkInactiveSession(session); //if inactivity eviction is enabled the session will be deleted from the cache
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -29,7 +29,6 @@ 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;
|
||||
|
@ -566,7 +565,10 @@ public class JavaxWebSocketFrameHandler implements FrameHandler
|
|||
{
|
||||
// No message sink is active
|
||||
if (activeMessageSink == null)
|
||||
{
|
||||
callback.succeeded();
|
||||
return;
|
||||
}
|
||||
|
||||
// Accept the payload into the message sink
|
||||
activeMessageSink.accept(frame, callback);
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
package org.eclipse.jetty.websocket.javax.server;
|
||||
|
||||
import org.eclipse.jetty.client.HttpClient;
|
||||
import org.eclipse.jetty.http.pathmap.PathSpec;
|
||||
import org.eclipse.jetty.http.pathmap.UriTemplatePathSpec;
|
||||
import org.eclipse.jetty.io.ByteBufferPool;
|
||||
import org.eclipse.jetty.server.handler.ContextHandler;
|
||||
|
@ -29,10 +30,11 @@ import org.eclipse.jetty.util.component.LifeCycle;
|
|||
import org.eclipse.jetty.util.log.Log;
|
||||
import org.eclipse.jetty.util.log.Logger;
|
||||
import org.eclipse.jetty.websocket.core.FrameHandler;
|
||||
import org.eclipse.jetty.websocket.core.WebSocketComponents;
|
||||
import org.eclipse.jetty.websocket.core.WebSocketException;
|
||||
import org.eclipse.jetty.websocket.core.WebSocketExtensionRegistry;
|
||||
import org.eclipse.jetty.websocket.core.client.WebSocketCoreClient;
|
||||
import org.eclipse.jetty.websocket.javax.client.JavaxWebSocketClientContainer;
|
||||
import org.eclipse.jetty.websocket.javax.common.InvalidWebSocketException;
|
||||
import org.eclipse.jetty.websocket.javax.server.internal.AnnotatedServerEndpointConfig;
|
||||
import org.eclipse.jetty.websocket.javax.server.internal.JavaxWebSocketCreator;
|
||||
import org.eclipse.jetty.websocket.javax.server.internal.UndefinedServerEndpointConfig;
|
||||
|
@ -80,7 +82,7 @@ public class JavaxWebSocketServerContainer
|
|||
return (javax.websocket.WebSocketContainer)handler.getServletContext().getAttribute("javax.websocket.server.ServerContainer");
|
||||
}
|
||||
|
||||
public static JavaxWebSocketServerContainer ensureContainer(ServletContext servletContext) throws ServletException
|
||||
public static JavaxWebSocketServerContainer ensureContainer(ServletContext servletContext)
|
||||
{
|
||||
ContextHandler contextHandler = ServletContextHandler.getServletContextHandler(servletContext, "Javax Websocket");
|
||||
if (contextHandler.getServer() == null)
|
||||
|
@ -106,7 +108,9 @@ public class JavaxWebSocketServerContainer
|
|||
|
||||
// Create the Jetty ServerContainer implementation
|
||||
container = new JavaxWebSocketServerContainer(
|
||||
WebSocketMapping.ensureMapping(servletContext), httpClient, executor);
|
||||
WebSocketMapping.ensureMapping(servletContext, WebSocketMapping.DEFAULT_KEY),
|
||||
WebSocketComponents.ensureWebSocketComponents(servletContext),
|
||||
httpClient, executor);
|
||||
contextHandler.addManaged(container);
|
||||
contextHandler.addLifeCycleListener(container);
|
||||
}
|
||||
|
@ -116,6 +120,7 @@ public class JavaxWebSocketServerContainer
|
|||
}
|
||||
|
||||
private final WebSocketMapping webSocketMapping;
|
||||
private final WebSocketComponents webSocketComponents;
|
||||
private final JavaxWebSocketServerFrameHandlerFactory frameHandlerFactory;
|
||||
private final Executor executor;
|
||||
private final FrameHandler.ConfigurationCustomizer customizer = new FrameHandler.ConfigurationCustomizer();
|
||||
|
@ -123,12 +128,19 @@ public class JavaxWebSocketServerContainer
|
|||
private List<Class<?>> deferredEndpointClasses;
|
||||
private List<ServerEndpointConfig> deferredEndpointConfigs;
|
||||
|
||||
|
||||
public JavaxWebSocketServerContainer(WebSocketMapping webSocketMapping, HttpClient httpClient, Executor executor)
|
||||
{
|
||||
this(webSocketMapping, new WebSocketComponents(), httpClient, executor);
|
||||
}
|
||||
|
||||
/**
|
||||
* Main entry point for {@link JavaxWebSocketServletContainerInitializer}.
|
||||
* @param webSocketMapping the {@link WebSocketMapping} that this container belongs to
|
||||
* @param httpClient the {@link HttpClient} instance to use
|
||||
* @param webSocketComponents the {@link WebSocketComponents} instance to use
|
||||
* @param httpClient the {@link HttpClient} instance to use
|
||||
*/
|
||||
public JavaxWebSocketServerContainer(WebSocketMapping webSocketMapping, HttpClient httpClient, Executor executor)
|
||||
public JavaxWebSocketServerContainer(WebSocketMapping webSocketMapping, WebSocketComponents webSocketComponents, HttpClient httpClient, Executor executor)
|
||||
{
|
||||
super(() ->
|
||||
{
|
||||
|
@ -138,6 +150,7 @@ public class JavaxWebSocketServerContainer
|
|||
return client;
|
||||
});
|
||||
this.webSocketMapping = webSocketMapping;
|
||||
this.webSocketComponents = webSocketComponents;
|
||||
this.executor = executor;
|
||||
this.frameHandlerFactory = new JavaxWebSocketServerFrameHandlerFactory(this);
|
||||
}
|
||||
|
@ -158,7 +171,7 @@ public class JavaxWebSocketServerContainer
|
|||
@Override
|
||||
public ByteBufferPool getBufferPool()
|
||||
{
|
||||
return this.webSocketMapping.getBufferPool();
|
||||
return webSocketComponents.getBufferPool();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -170,7 +183,7 @@ public class JavaxWebSocketServerContainer
|
|||
@Override
|
||||
public WebSocketExtensionRegistry getExtensionRegistry()
|
||||
{
|
||||
return this.webSocketMapping.getExtensionRegistry();
|
||||
return webSocketComponents.getExtensionRegistry();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -182,7 +195,7 @@ public class JavaxWebSocketServerContainer
|
|||
@Override
|
||||
public DecoratedObjectFactory getObjectFactory()
|
||||
{
|
||||
return this.webSocketMapping.getObjectFactory();
|
||||
return webSocketComponents.getObjectFactory();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -225,7 +238,7 @@ public class JavaxWebSocketServerContainer
|
|||
ServerEndpointConfig config = new AnnotatedServerEndpointConfig(this, endpointClass, anno);
|
||||
addEndpointMapping(config);
|
||||
}
|
||||
catch (InvalidWebSocketException e)
|
||||
catch (WebSocketException e)
|
||||
{
|
||||
throw new DeploymentException("Unable to deploy: " + endpointClass.getName(), e);
|
||||
}
|
||||
|
@ -252,7 +265,15 @@ public class JavaxWebSocketServerContainer
|
|||
{
|
||||
LOG.debug("addEndpoint({}) path={} endpoint={}", config, config.getPath(), config.getEndpointClass());
|
||||
}
|
||||
addEndpointMapping(config);
|
||||
|
||||
try
|
||||
{
|
||||
addEndpointMapping(config);
|
||||
}
|
||||
catch (WebSocketException e)
|
||||
{
|
||||
throw new DeploymentException("Unable to deploy: " + config.getEndpointClass().getName(), e);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -264,15 +285,15 @@ public class JavaxWebSocketServerContainer
|
|||
}
|
||||
}
|
||||
|
||||
private void addEndpointMapping(ServerEndpointConfig config)
|
||||
private void addEndpointMapping(ServerEndpointConfig config) throws WebSocketException
|
||||
{
|
||||
frameHandlerFactory.getMetadata(config.getEndpointClass(), config);
|
||||
|
||||
JavaxWebSocketCreator creator = new JavaxWebSocketCreator(this, config, this.webSocketMapping
|
||||
JavaxWebSocketCreator creator = new JavaxWebSocketCreator(this, config, webSocketComponents
|
||||
.getExtensionRegistry());
|
||||
|
||||
this.webSocketMapping
|
||||
.addMapping(new UriTemplatePathSpec(config.getPath()), creator, frameHandlerFactory, customizer);
|
||||
PathSpec pathSpec = new UriTemplatePathSpec(config.getPath());
|
||||
webSocketMapping.addMapping(pathSpec, creator, frameHandlerFactory, customizer);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -20,7 +20,6 @@ package org.eclipse.jetty.websocket.javax.server;
|
|||
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
import javax.servlet.ServletContainerInitializer;
|
||||
import javax.servlet.ServletContext;
|
||||
import javax.servlet.ServletException;
|
||||
|
@ -37,6 +36,7 @@ import org.eclipse.jetty.util.TypeUtil;
|
|||
import org.eclipse.jetty.util.log.Log;
|
||||
import org.eclipse.jetty.util.log.Logger;
|
||||
import org.eclipse.jetty.util.thread.ThreadClassLoaderScope;
|
||||
import org.eclipse.jetty.websocket.core.WebSocketComponents;
|
||||
import org.eclipse.jetty.websocket.servlet.WebSocketMapping;
|
||||
import org.eclipse.jetty.websocket.servlet.WebSocketUpgradeFilter;
|
||||
|
||||
|
@ -54,69 +54,61 @@ public class JavaxWebSocketServletContainerInitializer implements ServletContain
|
|||
*
|
||||
* @param context the context to search
|
||||
* @param keyName the key name
|
||||
* @param defValue the default value, if the value is not specified in the context
|
||||
* @return the value for the feature key
|
||||
* @return the value for the feature key, otherwise null if key is not set in context
|
||||
*/
|
||||
public static Boolean isEnabledViaContext(ServletContext context, String keyName, Boolean defValue)
|
||||
private static Boolean isEnabledViaContext(ServletContext context, String keyName)
|
||||
{
|
||||
// Try context parameters first
|
||||
String cp = context.getInitParameter(keyName);
|
||||
|
||||
if (cp != null)
|
||||
{
|
||||
if (TypeUtil.isTrue(cp))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if (TypeUtil.isFalse(cp))
|
||||
{
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
return defValue;
|
||||
}
|
||||
|
||||
// Next, try attribute on context
|
||||
Object enable = context.getAttribute(keyName);
|
||||
|
||||
if (enable != null)
|
||||
{
|
||||
if (TypeUtil.isTrue(enable))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if (TypeUtil.isFalse(enable))
|
||||
{
|
||||
else
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return defValue;
|
||||
return null;
|
||||
}
|
||||
|
||||
public static JavaxWebSocketServerContainer configureContext(ServletContextHandler context)
|
||||
throws ServletException
|
||||
{
|
||||
WebSocketMapping mapping = WebSocketMapping.ensureMapping(context.getServletContext());
|
||||
FilterHolder upgradeFilter = WebSocketUpgradeFilter.ensureFilter(context.getServletContext());
|
||||
WebSocketComponents components = WebSocketComponents.ensureWebSocketComponents(context.getServletContext());
|
||||
FilterHolder filterHolder = WebSocketUpgradeFilter.ensureFilter(context.getServletContext());
|
||||
WebSocketMapping mapping = WebSocketMapping.ensureMapping(context.getServletContext(), WebSocketMapping.DEFAULT_KEY);
|
||||
JavaxWebSocketServerContainer container = JavaxWebSocketServerContainer.ensureContainer(context.getServletContext());
|
||||
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("configureContext {} {} {}",mapping,upgradeFilter,container);
|
||||
LOG.debug("configureContext {} {} {} {}", mapping, components, filterHolder, container);
|
||||
|
||||
return container;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStartup(Set<Class<?>> c, ServletContext context) throws ServletException
|
||||
{
|
||||
Boolean dft = isEnabledViaContext(context, DEPRECATED_ENABLE_KEY, null);
|
||||
if (dft==null)
|
||||
dft = Boolean.TRUE;
|
||||
else
|
||||
Boolean enableKey = isEnabledViaContext(context, ENABLE_KEY);
|
||||
Boolean deprecatedEnabledKey = isEnabledViaContext(context, DEPRECATED_ENABLE_KEY);
|
||||
if (deprecatedEnabledKey != null)
|
||||
LOG.warn("Deprecated parameter used: " + DEPRECATED_ENABLE_KEY);
|
||||
|
||||
if (!isEnabledViaContext(context, ENABLE_KEY, dft))
|
||||
boolean websocketEnabled = true;
|
||||
if (enableKey != null)
|
||||
websocketEnabled = enableKey;
|
||||
else if (deprecatedEnabledKey != null)
|
||||
websocketEnabled = deprecatedEnabledKey;
|
||||
|
||||
if (!websocketEnabled)
|
||||
{
|
||||
LOG.info("Javax Websocket is disabled by configuration for context {}", context.getContextPath());
|
||||
return;
|
||||
|
|
|
@ -168,8 +168,7 @@ public class JavaxWebSocketCreator implements WebSocketCreator
|
|||
}
|
||||
catch (InstantiationException e)
|
||||
{
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("Unable to create websocket: " + config.getEndpointClass().getName(), e);
|
||||
LOG.warn("Unable to create websocket: " + config.getEndpointClass().getName(), e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -18,6 +18,20 @@
|
|||
|
||||
package org.eclipse.jetty.websocket.javax.tests.server;
|
||||
|
||||
import java.net.URI;
|
||||
import java.util.concurrent.Future;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import javax.servlet.ServletContextEvent;
|
||||
import javax.servlet.ServletContextListener;
|
||||
import javax.websocket.DeploymentException;
|
||||
import javax.websocket.EndpointConfig;
|
||||
import javax.websocket.MessageHandler;
|
||||
import javax.websocket.OnOpen;
|
||||
import javax.websocket.Session;
|
||||
import javax.websocket.server.ServerEndpoint;
|
||||
import javax.websocket.server.ServerEndpointConfig;
|
||||
|
||||
import org.eclipse.jetty.toolchain.test.jupiter.WorkDir;
|
||||
import org.eclipse.jetty.toolchain.test.jupiter.WorkDirExtension;
|
||||
import org.eclipse.jetty.util.Callback;
|
||||
|
@ -34,19 +48,6 @@ import org.eclipse.jetty.websocket.javax.tests.framehandlers.FrameHandlerTracker
|
|||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
|
||||
import javax.servlet.ServletContextEvent;
|
||||
import javax.servlet.ServletContextListener;
|
||||
import javax.websocket.DeploymentException;
|
||||
import javax.websocket.EndpointConfig;
|
||||
import javax.websocket.MessageHandler;
|
||||
import javax.websocket.OnOpen;
|
||||
import javax.websocket.Session;
|
||||
import javax.websocket.server.ServerEndpoint;
|
||||
import javax.websocket.server.ServerEndpointConfig;
|
||||
import java.net.URI;
|
||||
import java.util.concurrent.Future;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import static org.hamcrest.MatcherAssert.assertThat;
|
||||
import static org.hamcrest.Matchers.is;
|
||||
|
||||
|
@ -62,6 +63,7 @@ public class EndpointViaConfigTest
|
|||
@ServerEndpoint("/echo")
|
||||
public static class BasicEchoEndpoint extends WSEventTracker implements MessageHandler.Whole<String>
|
||||
{
|
||||
@Override
|
||||
public void onMessage(String msg)
|
||||
{
|
||||
super.onWsText(msg);
|
||||
|
|
|
@ -18,25 +18,17 @@
|
|||
|
||||
package org.eclipse.jetty.websocket.server;
|
||||
|
||||
import org.eclipse.jetty.server.handler.ContextHandler;
|
||||
import org.eclipse.jetty.servlet.FilterHolder;
|
||||
import java.util.Collections;
|
||||
import java.util.Set;
|
||||
import javax.servlet.ServletContainerInitializer;
|
||||
import javax.servlet.ServletContext;
|
||||
import javax.servlet.ServletException;
|
||||
|
||||
import org.eclipse.jetty.servlet.ServletContextHandler;
|
||||
import org.eclipse.jetty.util.component.AbstractLifeCycle;
|
||||
import org.eclipse.jetty.util.log.Log;
|
||||
import org.eclipse.jetty.util.log.Logger;
|
||||
import org.eclipse.jetty.util.thread.QueuedThreadPool;
|
||||
import org.eclipse.jetty.websocket.servlet.FrameHandlerFactory;
|
||||
import org.eclipse.jetty.websocket.servlet.WebSocketMapping;
|
||||
import org.eclipse.jetty.websocket.servlet.WebSocketUpgradeFilter;
|
||||
|
||||
import javax.servlet.ServletContainerInitializer;
|
||||
import javax.servlet.ServletContext;
|
||||
import javax.servlet.ServletException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.Executor;
|
||||
import org.eclipse.jetty.websocket.core.WebSocketComponents;
|
||||
|
||||
/**
|
||||
* ServletContext configuration for Jetty Native WebSockets API.
|
||||
|
@ -80,11 +72,10 @@ public class JettyWebSocketServletContainerInitializer implements ServletContain
|
|||
@Override
|
||||
public void onStartup(Set<Class<?>> c, ServletContext servletContext) throws ServletException
|
||||
{
|
||||
WebSocketMapping mapping = WebSocketMapping.ensureMapping(servletContext);
|
||||
FilterHolder upgradeFilter = WebSocketUpgradeFilter.ensureFilter(servletContext);
|
||||
WebSocketComponents components = WebSocketComponents.ensureWebSocketComponents(servletContext);
|
||||
JettyServerFrameHandlerFactory factory = JettyServerFrameHandlerFactory.ensureFactory(servletContext);
|
||||
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("onStartup {} {} {}",mapping, upgradeFilter, factory);
|
||||
LOG.debug("onStartup {} {}", components, factory);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -25,7 +25,6 @@ import java.time.Duration;
|
|||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import javax.servlet.RequestDispatcher;
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
@ -148,10 +147,10 @@ public class BrowserDebugTool
|
|||
factory.addMapping(new ServletPathSpec("/"), new BrowserSocketCreator());
|
||||
|
||||
// Set the timeout
|
||||
factory.setDefaultIdleTimeout(Duration.ofSeconds(30));
|
||||
factory.setIdleTimeout(Duration.ofSeconds(30));
|
||||
|
||||
// Set top end message size
|
||||
factory.setDefaultMaxTextMessageSize(15 * 1024 * 1024);
|
||||
factory.setMaxTextMessageSize(15 * 1024 * 1024);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -0,0 +1,79 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
|
||||
<parent>
|
||||
<groupId>org.eclipse.jetty.websocket</groupId>
|
||||
<artifactId>websocket-parent</artifactId>
|
||||
<version>10.0.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<artifactId>jetty-websocket-tests</artifactId>
|
||||
<name>Jetty :: Websocket :: org.eclipse.jetty.websocket :: Tests</name>
|
||||
|
||||
<properties>
|
||||
<bundle-symbolic-name>${project.groupId}.jetty.tests</bundle-symbolic-name>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.eclipse.jetty.websocket</groupId>
|
||||
<artifactId>jetty-websocket-api</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.eclipse.jetty.websocket</groupId>
|
||||
<artifactId>jetty-websocket-client</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.eclipse.jetty.websocket</groupId>
|
||||
<artifactId>jetty-websocket-server</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.eclipse.jetty.toolchain</groupId>
|
||||
<artifactId>jetty-test-helper</artifactId>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-enforcer-plugin</artifactId>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>ban-java-servlet-api</id>
|
||||
<goals>
|
||||
<goal>enforce</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<rules>
|
||||
<bannedDependencies>
|
||||
<includes>
|
||||
<include>javax.servlet</include>
|
||||
<include>servletapi</include>
|
||||
<include>org.eclipse.jetty.orbit:javax.servlet</include>
|
||||
<include>org.mortbay.jetty:servlet-api</include>
|
||||
<include>jetty:servlet-api</include>
|
||||
</includes>
|
||||
</bannedDependencies>
|
||||
</rules>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-deploy-plugin</artifactId>
|
||||
<configuration>
|
||||
<skip>true</skip>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</project>
|
||||
|
|
@ -0,0 +1,146 @@
|
|||
//
|
||||
// ========================================================================
|
||||
// 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.websocket.tests;
|
||||
|
||||
import java.net.URI;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import org.eclipse.jetty.server.Server;
|
||||
import org.eclipse.jetty.server.ServerConnector;
|
||||
import org.eclipse.jetty.servlet.ServletContextHandler;
|
||||
import org.eclipse.jetty.websocket.api.Session;
|
||||
import org.eclipse.jetty.websocket.api.annotations.OnWebSocketClose;
|
||||
import org.eclipse.jetty.websocket.api.annotations.OnWebSocketConnect;
|
||||
import org.eclipse.jetty.websocket.api.annotations.OnWebSocketError;
|
||||
import org.eclipse.jetty.websocket.api.annotations.OnWebSocketMessage;
|
||||
import org.eclipse.jetty.websocket.api.annotations.WebSocket;
|
||||
import org.eclipse.jetty.websocket.client.WebSocketClient;
|
||||
import org.eclipse.jetty.websocket.server.JettyWebSocketServletContainerInitializer;
|
||||
import org.eclipse.jetty.websocket.servlet.WebSocketServlet;
|
||||
import org.eclipse.jetty.websocket.servlet.WebSocketServletFactory;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
public class JettyWebsocketTest
|
||||
{
|
||||
|
||||
@WebSocket
|
||||
public static class EventSocket
|
||||
{
|
||||
CountDownLatch closed = new CountDownLatch(1);
|
||||
|
||||
String behavior;
|
||||
|
||||
@OnWebSocketConnect
|
||||
public void onOpen(Session sess)
|
||||
{
|
||||
behavior = sess.getPolicy().getBehavior().name();
|
||||
System.err.println(toString() + " Socket Connected: " + sess);
|
||||
}
|
||||
|
||||
@OnWebSocketMessage
|
||||
public void onMessage(String message)
|
||||
{
|
||||
System.err.println(toString() + " Received TEXT message: " + message);
|
||||
}
|
||||
|
||||
@OnWebSocketClose
|
||||
public void onClose(int statusCode, String reason)
|
||||
{
|
||||
System.err.println(toString() + " Socket Closed: " + statusCode + ":" + reason);
|
||||
closed.countDown();
|
||||
}
|
||||
|
||||
@OnWebSocketError
|
||||
public void onError(Throwable cause)
|
||||
{
|
||||
cause.printStackTrace(System.err);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString()
|
||||
{
|
||||
return String.format("[%s@%s]", behavior, Integer.toHexString(hashCode()));
|
||||
}
|
||||
}
|
||||
|
||||
public static class MyWebSocketServlet extends WebSocketServlet
|
||||
{
|
||||
@Override
|
||||
public void configure(WebSocketServletFactory factory)
|
||||
{
|
||||
factory.addMapping("/",(req, resp)->new EventSocket());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void test() throws Exception
|
||||
{
|
||||
Server server = new Server();
|
||||
ServerConnector connector = new ServerConnector(server);
|
||||
connector.setPort(8080);
|
||||
server.addConnector(connector);
|
||||
|
||||
ServletContextHandler contextHandler = new ServletContextHandler(ServletContextHandler.SESSIONS);
|
||||
contextHandler.setContextPath("/");
|
||||
server.setHandler(contextHandler);
|
||||
|
||||
contextHandler.addServlet(MyWebSocketServlet.class, "/testPath1");
|
||||
contextHandler.addServlet(MyWebSocketServlet.class, "/testPath2");
|
||||
|
||||
try
|
||||
{
|
||||
JettyWebSocketServletContainerInitializer.configure(contextHandler);
|
||||
server.start();
|
||||
|
||||
WebSocketClient client = new WebSocketClient();
|
||||
client.start();
|
||||
|
||||
URI uri = URI.create("ws://localhost:8080/testPath1");
|
||||
EventSocket socket = new EventSocket();
|
||||
CompletableFuture<Session> connect = client.connect(socket, uri);
|
||||
try(Session session = connect.get(5, TimeUnit.SECONDS))
|
||||
{
|
||||
session.getRemote().sendString("hello world");
|
||||
}
|
||||
assertTrue(socket.closed.await(10, TimeUnit.SECONDS));
|
||||
|
||||
|
||||
uri = URI.create("ws://localhost:8080/testPath2");
|
||||
socket = new EventSocket();
|
||||
connect = client.connect(socket, uri);
|
||||
try(Session session = connect.get(5, TimeUnit.SECONDS))
|
||||
{
|
||||
session.getRemote().sendString("hello world");
|
||||
}
|
||||
assertTrue(socket.closed.await(10, TimeUnit.SECONDS));
|
||||
|
||||
|
||||
server.stop();
|
||||
}
|
||||
catch (Throwable t)
|
||||
{
|
||||
t.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,226 @@
|
|||
//
|
||||
// ========================================================================
|
||||
// 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.websocket.tests;
|
||||
|
||||
import java.net.URI;
|
||||
import java.util.concurrent.ArrayBlockingQueue;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import org.eclipse.jetty.client.api.AuthenticationStore;
|
||||
import org.eclipse.jetty.client.util.BasicAuthentication;
|
||||
import org.eclipse.jetty.security.ConstraintMapping;
|
||||
import org.eclipse.jetty.security.ConstraintSecurityHandler;
|
||||
import org.eclipse.jetty.security.HashLoginService;
|
||||
import org.eclipse.jetty.security.SecurityHandler;
|
||||
import org.eclipse.jetty.security.UserStore;
|
||||
import org.eclipse.jetty.security.authentication.BasicAuthenticator;
|
||||
import org.eclipse.jetty.server.Server;
|
||||
import org.eclipse.jetty.server.ServerConnector;
|
||||
import org.eclipse.jetty.servlet.ServletContextHandler;
|
||||
import org.eclipse.jetty.util.security.Constraint;
|
||||
import org.eclipse.jetty.util.security.Credential;
|
||||
import org.eclipse.jetty.websocket.api.Session;
|
||||
import org.eclipse.jetty.websocket.api.UpgradeRequest;
|
||||
import org.eclipse.jetty.websocket.api.annotations.OnWebSocketClose;
|
||||
import org.eclipse.jetty.websocket.api.annotations.OnWebSocketConnect;
|
||||
import org.eclipse.jetty.websocket.api.annotations.OnWebSocketError;
|
||||
import org.eclipse.jetty.websocket.api.annotations.OnWebSocketMessage;
|
||||
import org.eclipse.jetty.websocket.api.annotations.WebSocket;
|
||||
import org.eclipse.jetty.websocket.client.ClientUpgradeRequest;
|
||||
import org.eclipse.jetty.websocket.client.WebSocketClient;
|
||||
import org.eclipse.jetty.websocket.server.JettyWebSocketServletContainerInitializer;
|
||||
import org.eclipse.jetty.websocket.tests.examples.MyAdvancedEchoServlet;
|
||||
import org.eclipse.jetty.websocket.tests.examples.MyAuthedServlet;
|
||||
import org.eclipse.jetty.websocket.tests.examples.MyEchoServlet;
|
||||
import org.junit.jupiter.api.AfterAll;
|
||||
import org.junit.jupiter.api.BeforeAll;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import static org.hamcrest.MatcherAssert.assertThat;
|
||||
import static org.hamcrest.Matchers.is;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
public class WebSocketServletExamplesTest
|
||||
{
|
||||
|
||||
@WebSocket
|
||||
public static class ClientSocket
|
||||
{
|
||||
CountDownLatch closed = new CountDownLatch(1);
|
||||
ArrayBlockingQueue<String> messageQueue = new ArrayBlockingQueue<>(2);
|
||||
|
||||
@OnWebSocketConnect
|
||||
public void onOpen(Session sess)
|
||||
{
|
||||
System.err.println("ClientSocket Connected: " + sess);
|
||||
}
|
||||
|
||||
@OnWebSocketMessage
|
||||
public void onMessage(String message)
|
||||
{
|
||||
messageQueue.offer(message);
|
||||
System.err.println("Received TEXT message: " + message);
|
||||
}
|
||||
|
||||
@OnWebSocketClose
|
||||
public void onClose(int statusCode, String reason)
|
||||
{
|
||||
System.err.println("ClientSocket Closed: " + statusCode + ":" + reason);
|
||||
closed.countDown();
|
||||
}
|
||||
|
||||
@OnWebSocketError
|
||||
public void onError(Throwable cause)
|
||||
{
|
||||
cause.printStackTrace(System.err);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static Server _server;
|
||||
static ServletContextHandler _context;
|
||||
|
||||
@BeforeAll
|
||||
public static void setup() throws Exception
|
||||
{
|
||||
_server = new Server();
|
||||
ServerConnector connector = new ServerConnector(_server);
|
||||
connector.setPort(8080);
|
||||
_server.addConnector(connector);
|
||||
|
||||
_context = new ServletContextHandler(ServletContextHandler.SESSIONS);
|
||||
_context.setContextPath("/");
|
||||
_context.setSecurityHandler(getSecurityHandler("user", "password", "testRealm"));
|
||||
_server.setHandler(_context);
|
||||
|
||||
_context.addServlet(MyEchoServlet.class, "/echo");
|
||||
_context.addServlet(MyAdvancedEchoServlet.class, "/advancedEcho");
|
||||
_context.addServlet(MyAuthedServlet.class, "/authed");
|
||||
|
||||
JettyWebSocketServletContainerInitializer.configure(_context);
|
||||
_server.start();
|
||||
}
|
||||
|
||||
@AfterAll
|
||||
public static void stop() throws Exception
|
||||
{
|
||||
_server.stop();
|
||||
}
|
||||
|
||||
private static SecurityHandler getSecurityHandler(String username, String password, String realm) {
|
||||
|
||||
HashLoginService loginService = new HashLoginService();
|
||||
UserStore userStore = new UserStore();
|
||||
userStore.addUser(username, Credential.getCredential(password), new String[] {"websocket"});
|
||||
loginService.setUserStore(userStore);
|
||||
loginService.setName(realm);
|
||||
|
||||
Constraint constraint = new Constraint();
|
||||
constraint.setName("auth");
|
||||
constraint.setAuthenticate(true);
|
||||
constraint.setRoles(new String[]{"**"});
|
||||
|
||||
ConstraintMapping mapping = new ConstraintMapping();
|
||||
mapping.setPathSpec("/authed/*");
|
||||
mapping.setConstraint(constraint);
|
||||
|
||||
ConstraintSecurityHandler security = new ConstraintSecurityHandler();
|
||||
security.addConstraintMapping(mapping);
|
||||
security.setAuthenticator(new BasicAuthenticator());
|
||||
security.setLoginService(loginService);
|
||||
|
||||
return security;
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testEchoServlet() throws Exception
|
||||
{
|
||||
WebSocketClient client = new WebSocketClient();
|
||||
client.start();
|
||||
|
||||
URI uri = URI.create("ws://localhost:8080/echo");
|
||||
ClientSocket socket = new ClientSocket();
|
||||
CompletableFuture<Session> connect = client.connect(socket, uri);
|
||||
try (Session session = connect.get(5, TimeUnit.SECONDS))
|
||||
{
|
||||
String message = "hello world";
|
||||
session.getRemote().sendString(message);
|
||||
|
||||
String response = socket.messageQueue.poll(5, TimeUnit.SECONDS);
|
||||
assertThat(response, is(message));
|
||||
}
|
||||
|
||||
assertTrue(socket.closed.await(10, TimeUnit.SECONDS));
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testAdvancedEchoServlet() throws Exception
|
||||
{
|
||||
WebSocketClient client = new WebSocketClient();
|
||||
client.start();
|
||||
|
||||
URI uri = URI.create("ws://localhost:8080/advancedEcho");
|
||||
ClientSocket socket = new ClientSocket();
|
||||
|
||||
UpgradeRequest upgradeRequest = new ClientUpgradeRequest();
|
||||
upgradeRequest.setSubProtocols("text");
|
||||
CompletableFuture<Session> connect = client.connect(socket, uri, upgradeRequest);
|
||||
try (Session session = connect.get(5, TimeUnit.SECONDS))
|
||||
{
|
||||
String message = "hello world";
|
||||
session.getRemote().sendString(message);
|
||||
|
||||
String response = socket.messageQueue.poll(5, TimeUnit.SECONDS);
|
||||
assertThat(response, is(message));
|
||||
}
|
||||
|
||||
assertTrue(socket.closed.await(10, TimeUnit.SECONDS));
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testAuthedServlet() throws Exception
|
||||
{
|
||||
WebSocketClient client = new WebSocketClient();
|
||||
client.start();
|
||||
AuthenticationStore authenticationStore = client.getHttpClient().getAuthenticationStore();
|
||||
|
||||
URI uri = URI.create("ws://localhost:8080/authed");
|
||||
|
||||
BasicAuthentication basicAuthentication = new BasicAuthentication(uri, "testRealm", "user", "password");
|
||||
authenticationStore.addAuthentication(basicAuthentication);
|
||||
|
||||
ClientSocket socket = new ClientSocket();
|
||||
CompletableFuture<Session> connect = client.connect(socket, uri);
|
||||
try (Session session = connect.get(5, TimeUnit.SECONDS))
|
||||
{
|
||||
String message = "hello world";
|
||||
session.getRemote().sendString(message);
|
||||
|
||||
String response = socket.messageQueue.poll(5, TimeUnit.SECONDS);
|
||||
assertThat(response, is(message));
|
||||
}
|
||||
|
||||
assertTrue(socket.closed.await(10, TimeUnit.SECONDS));
|
||||
}
|
||||
}
|
|
@ -0,0 +1,57 @@
|
|||
//
|
||||
// ========================================================================
|
||||
// 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.websocket.tests.examples;
|
||||
|
||||
import org.eclipse.jetty.websocket.servlet.ServletUpgradeRequest;
|
||||
import org.eclipse.jetty.websocket.servlet.ServletUpgradeResponse;
|
||||
import org.eclipse.jetty.websocket.servlet.WebSocketCreator;
|
||||
|
||||
public class MyAdvancedEchoCreator implements WebSocketCreator
|
||||
{
|
||||
private MyBinaryEchoSocket binaryEcho;
|
||||
private MyEchoSocket textEcho;
|
||||
|
||||
public MyAdvancedEchoCreator()
|
||||
{
|
||||
// Create the reusable sockets
|
||||
this.binaryEcho = new MyBinaryEchoSocket();
|
||||
this.textEcho = new MyEchoSocket();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object createWebSocket(ServletUpgradeRequest req, ServletUpgradeResponse resp)
|
||||
{
|
||||
for (String subprotocol : req.getSubProtocols())
|
||||
{
|
||||
if ("binary".equals(subprotocol))
|
||||
{
|
||||
resp.setAcceptedSubProtocol(subprotocol);
|
||||
return binaryEcho;
|
||||
}
|
||||
if ("text".equals(subprotocol))
|
||||
{
|
||||
resp.setAcceptedSubProtocol(subprotocol);
|
||||
return textEcho;
|
||||
}
|
||||
}
|
||||
|
||||
// No valid subprotocol in request, ignore the request
|
||||
return null;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,41 @@
|
|||
//
|
||||
// ========================================================================
|
||||
// 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.websocket.tests.examples;
|
||||
|
||||
import java.time.Duration;
|
||||
|
||||
import javax.servlet.annotation.WebServlet;
|
||||
|
||||
import org.eclipse.jetty.websocket.servlet.WebSocketServlet;
|
||||
import org.eclipse.jetty.websocket.servlet.WebSocketServletFactory;
|
||||
|
||||
@SuppressWarnings("serial")
|
||||
@WebServlet(name = "MyAdvanced Echo WebSocket Servlet", urlPatterns = { "/advecho" })
|
||||
public class MyAdvancedEchoServlet extends WebSocketServlet
|
||||
{
|
||||
@Override
|
||||
public void configure(WebSocketServletFactory factory)
|
||||
{
|
||||
// set a 10 second timeout
|
||||
factory.setIdleTimeout(Duration.ofSeconds(10));
|
||||
|
||||
// set a custom WebSocket creator
|
||||
factory.setCreator(new MyAdvancedEchoCreator());
|
||||
}
|
||||
}
|
|
@ -0,0 +1,60 @@
|
|||
//
|
||||
// ========================================================================
|
||||
// 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.websocket.tests.examples;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.security.Principal;
|
||||
|
||||
import org.eclipse.jetty.websocket.servlet.ServletUpgradeRequest;
|
||||
import org.eclipse.jetty.websocket.servlet.ServletUpgradeResponse;
|
||||
import org.eclipse.jetty.websocket.servlet.WebSocketCreator;
|
||||
|
||||
public class MyAuthedCreator implements WebSocketCreator
|
||||
{
|
||||
@Override
|
||||
public Object createWebSocket(ServletUpgradeRequest req, ServletUpgradeResponse resp)
|
||||
{
|
||||
try
|
||||
{
|
||||
// Is Authenticated?
|
||||
Principal principal = req.getUserPrincipal();
|
||||
if (principal == null)
|
||||
{
|
||||
resp.sendForbidden("Not authenticated yet");
|
||||
return null;
|
||||
}
|
||||
|
||||
// Is Authorized?
|
||||
if (!req.isUserInRole("websocket"))
|
||||
{
|
||||
resp.sendForbidden("Not authenticated yet");
|
||||
return null;
|
||||
}
|
||||
|
||||
// Return websocket
|
||||
return new MyEchoSocket();
|
||||
}
|
||||
catch (IOException e)
|
||||
{
|
||||
e.printStackTrace(System.err);
|
||||
}
|
||||
// no websocket
|
||||
return null;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,32 @@
|
|||
//
|
||||
// ========================================================================
|
||||
// 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.websocket.tests.examples;
|
||||
|
||||
import org.eclipse.jetty.websocket.servlet.WebSocketServlet;
|
||||
import org.eclipse.jetty.websocket.servlet.WebSocketServletFactory;
|
||||
|
||||
@SuppressWarnings("serial")
|
||||
public class MyAuthedServlet extends WebSocketServlet
|
||||
{
|
||||
@Override
|
||||
public void configure(WebSocketServletFactory factory)
|
||||
{
|
||||
factory.setCreator(new MyAuthedCreator());
|
||||
}
|
||||
}
|
|
@ -0,0 +1,39 @@
|
|||
//
|
||||
// ========================================================================
|
||||
// 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.websocket.tests.examples;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
import org.eclipse.jetty.websocket.api.Session;
|
||||
import org.eclipse.jetty.websocket.api.annotations.OnWebSocketMessage;
|
||||
import org.eclipse.jetty.websocket.api.annotations.WebSocket;
|
||||
|
||||
/**
|
||||
* Echo BINARY messages
|
||||
*/
|
||||
@WebSocket
|
||||
public class MyBinaryEchoSocket
|
||||
{
|
||||
@OnWebSocketMessage
|
||||
public void onWebSocketText(Session session, byte buf[], int offset, int len)
|
||||
{
|
||||
// Echo message back, asynchronously
|
||||
session.getRemote().sendBytes(ByteBuffer.wrap(buf,offset,len), null);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,41 @@
|
|||
//
|
||||
// ========================================================================
|
||||
// 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.websocket.tests.examples;
|
||||
|
||||
import java.time.Duration;
|
||||
|
||||
import javax.servlet.annotation.WebServlet;
|
||||
|
||||
import org.eclipse.jetty.websocket.servlet.WebSocketServlet;
|
||||
import org.eclipse.jetty.websocket.servlet.WebSocketServletFactory;
|
||||
|
||||
@SuppressWarnings("serial")
|
||||
@WebServlet(name = "MyEcho WebSocket Servlet", urlPatterns = { "/echo" })
|
||||
public class MyEchoServlet extends WebSocketServlet
|
||||
{
|
||||
@Override
|
||||
public void configure(WebSocketServletFactory factory)
|
||||
{
|
||||
// set a 10 second timeout
|
||||
factory.setIdleTimeout(Duration.ofSeconds(10));
|
||||
|
||||
// register MyEchoSocket as the WebSocket to create on Upgrade
|
||||
factory.register(MyEchoSocket.class);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,37 @@
|
|||
//
|
||||
// ========================================================================
|
||||
// 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.websocket.tests.examples;
|
||||
|
||||
import org.eclipse.jetty.websocket.api.Session;
|
||||
import org.eclipse.jetty.websocket.api.annotations.OnWebSocketMessage;
|
||||
import org.eclipse.jetty.websocket.api.annotations.WebSocket;
|
||||
|
||||
/**
|
||||
* Example WebSocket, simple echo
|
||||
*/
|
||||
@WebSocket
|
||||
public class MyEchoSocket
|
||||
{
|
||||
@OnWebSocketMessage
|
||||
public void onWebSocketText(Session session, String message)
|
||||
{
|
||||
// Echo message back, asynchronously
|
||||
session.getRemote().sendString(message, null);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,43 @@
|
|||
#
|
||||
#
|
||||
# ========================================================================
|
||||
# Copyright (c) 1995-2017 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.
|
||||
# ========================================================================
|
||||
#
|
||||
#
|
||||
# org.eclipse.jetty.util.log.class=org.eclipse.jetty.util.log.Slf4jLog
|
||||
org.eclipse.jetty.util.log.class=org.eclipse.jetty.util.log.StdErrLog
|
||||
org.eclipse.jetty.LEVEL=WARN
|
||||
# org.eclipse.jetty.util.log.stderr.LONG=true
|
||||
# org.eclipse.jetty.server.AbstractConnector.LEVEL=DEBUG
|
||||
# org.eclipse.jetty.io.WriteFlusher.LEVEL=DEBUG
|
||||
# org.eclipse.jetty.io.FillInterest.LEVEL=DEBUG
|
||||
# org.eclipse.jetty.client.LEVEL=DEBUG
|
||||
# org.eclipse.jetty.io.LEVEL=DEBUG
|
||||
# org.eclipse.jetty.io.ManagedSelector.LEVEL=INFO
|
||||
# org.eclipse.jetty.websocket.LEVEL=DEBUG
|
||||
# org.eclipse.jetty.websocket.core.internal.WebSocketChannel.LEVEL=DEBUG
|
||||
# org.eclipse.jetty.websocket.jsr356.tests.LEVEL=DEBUG
|
||||
# org.eclipse.jetty.websocket.LEVEL=INFO
|
||||
# org.eclipse.jetty.websocket.jsr356.messages.LEVEL=DEBUG
|
||||
# org.eclipse.jetty.websocket.tests.LEVEL=DEBUG
|
||||
# org.eclipse.jetty.websocket.tests.client.LEVEL=DEBUG
|
||||
# org.eclipse.jetty.websocket.tests.client.jsr356.LEVEL=DEBUG
|
||||
# org.eclipse.jetty.websocket.tests.server.LEVEL=DEBUG
|
||||
# org.eclipse.jetty.websocket.tests.server.jsr356.LEVEL=DEBUG
|
||||
### Showing any unintended (ignored) errors from CompletionCallback
|
||||
# org.eclipse.jetty.websocket.common.CompletionCallback.LEVEL=ALL
|
||||
### Disabling intentional error out of RFCSocket
|
||||
org.eclipse.jetty.websocket.tests.server.RFCSocket.LEVEL=OFF
|
|
@ -22,6 +22,7 @@
|
|||
<module>jetty-websocket-common</module>
|
||||
<module>jetty-websocket-client</module>
|
||||
<module>jetty-websocket-server</module>
|
||||
<module>jetty-websocket-tests</module>
|
||||
<!-- Javax WebSocket Implementation -->
|
||||
<module>javax-websocket-common</module>
|
||||
<module>javax-websocket-client</module>
|
||||
|
|
|
@ -0,0 +1,83 @@
|
|||
//
|
||||
// ========================================================================
|
||||
// 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.websocket.core;
|
||||
|
||||
import javax.servlet.ServletContext;
|
||||
|
||||
import org.eclipse.jetty.io.ByteBufferPool;
|
||||
import org.eclipse.jetty.io.MappedByteBufferPool;
|
||||
import org.eclipse.jetty.server.handler.ContextHandler;
|
||||
import org.eclipse.jetty.util.DecoratedObjectFactory;
|
||||
|
||||
/**
|
||||
* A collection of components which are the resources needed for websockets such as
|
||||
* {@link ByteBufferPool}, {@link WebSocketExtensionRegistry}, and {@link DecoratedObjectFactory}.
|
||||
*
|
||||
* These components should be accessed through {@link WebSocketComponents#ensureWebSocketComponents} so that
|
||||
* the instance can be shared by being stored as a bean on the ContextHandler.
|
||||
*/
|
||||
public class WebSocketComponents
|
||||
{
|
||||
public static WebSocketComponents ensureWebSocketComponents(ServletContext servletContext)
|
||||
{
|
||||
ContextHandler contextHandler = ContextHandler.getContextHandler(servletContext);
|
||||
|
||||
// Ensure a mapping exists
|
||||
WebSocketComponents components = contextHandler.getBean(WebSocketComponents.class);
|
||||
if (components == null)
|
||||
{
|
||||
components = new WebSocketComponents();
|
||||
contextHandler.addBean(components);
|
||||
}
|
||||
|
||||
return components;
|
||||
}
|
||||
|
||||
public WebSocketComponents()
|
||||
{
|
||||
this(new WebSocketExtensionRegistry(), new DecoratedObjectFactory(), new MappedByteBufferPool());
|
||||
}
|
||||
|
||||
public WebSocketComponents(WebSocketExtensionRegistry extensionRegistry, DecoratedObjectFactory objectFactory, ByteBufferPool bufferPool)
|
||||
{
|
||||
this.extensionRegistry = extensionRegistry;
|
||||
this.objectFactory = objectFactory;
|
||||
this.bufferPool = bufferPool;
|
||||
}
|
||||
|
||||
private DecoratedObjectFactory objectFactory;
|
||||
private WebSocketExtensionRegistry extensionRegistry;
|
||||
private ByteBufferPool bufferPool;
|
||||
|
||||
|
||||
public ByteBufferPool getBufferPool()
|
||||
{
|
||||
return bufferPool;
|
||||
}
|
||||
|
||||
public WebSocketExtensionRegistry getExtensionRegistry()
|
||||
{
|
||||
return extensionRegistry;
|
||||
}
|
||||
|
||||
public DecoratedObjectFactory getObjectFactory()
|
||||
{
|
||||
return objectFactory;
|
||||
}
|
||||
}
|
|
@ -31,6 +31,12 @@
|
|||
<artifactId>jetty-test-helper</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.eclipse.jetty.websocket</groupId>
|
||||
<artifactId>jetty-websocket-api</artifactId>
|
||||
<version>${project.version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
|
|
|
@ -23,7 +23,6 @@ import java.net.URISyntaxException;
|
|||
import java.util.function.Consumer;
|
||||
|
||||
import javax.servlet.ServletContext;
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
|
@ -33,17 +32,15 @@ import org.eclipse.jetty.http.pathmap.PathSpec;
|
|||
import org.eclipse.jetty.http.pathmap.RegexPathSpec;
|
||||
import org.eclipse.jetty.http.pathmap.ServletPathSpec;
|
||||
import org.eclipse.jetty.http.pathmap.UriTemplatePathSpec;
|
||||
import org.eclipse.jetty.io.ByteBufferPool;
|
||||
import org.eclipse.jetty.io.MappedByteBufferPool;
|
||||
import org.eclipse.jetty.io.RuntimeIOException;
|
||||
import org.eclipse.jetty.server.handler.ContextHandler;
|
||||
import org.eclipse.jetty.util.DecoratedObjectFactory;
|
||||
import org.eclipse.jetty.util.component.Dumpable;
|
||||
import org.eclipse.jetty.util.component.LifeCycle;
|
||||
import org.eclipse.jetty.util.log.Log;
|
||||
import org.eclipse.jetty.util.log.Logger;
|
||||
import org.eclipse.jetty.websocket.core.FrameHandler;
|
||||
import org.eclipse.jetty.websocket.core.WebSocketExtensionRegistry;
|
||||
import org.eclipse.jetty.websocket.core.WebSocketComponents;
|
||||
import org.eclipse.jetty.websocket.core.WebSocketException;
|
||||
import org.eclipse.jetty.websocket.core.server.Handshaker;
|
||||
import org.eclipse.jetty.websocket.core.server.Negotiation;
|
||||
import org.eclipse.jetty.websocket.core.server.WebSocketNegotiator;
|
||||
|
@ -64,41 +61,42 @@ public class WebSocketMapping implements Dumpable, LifeCycle.Listener
|
|||
{
|
||||
private static final Logger LOG = Log.getLogger(WebSocketMapping.class);
|
||||
|
||||
public static WebSocketMapping ensureMapping(ServletContext servletContext) throws ServletException
|
||||
public static WebSocketMapping ensureMapping(ServletContext servletContext, String mappingKey)
|
||||
{
|
||||
ContextHandler contextHandler = ContextHandler.getContextHandler(servletContext);
|
||||
|
||||
// Ensure a mapping exists
|
||||
WebSocketMapping mapping = contextHandler.getBean(WebSocketMapping.class);
|
||||
if (mapping == null)
|
||||
Object mappingObject = contextHandler.getAttribute(mappingKey);
|
||||
if (mappingObject!=null)
|
||||
{
|
||||
mapping = new WebSocketMapping();
|
||||
mapping.setContextClassLoader(servletContext.getClassLoader());
|
||||
contextHandler.addBean(mapping);
|
||||
contextHandler.addLifeCycleListener(mapping);
|
||||
if (WebSocketMapping.class.isInstance(mappingObject))
|
||||
return (WebSocketMapping)mappingObject;
|
||||
else
|
||||
throw new IllegalStateException(
|
||||
String.format("ContextHandler attribute %s is not of type WebSocketMapping: {%s}",
|
||||
mappingKey, mappingObject.toString()));
|
||||
}
|
||||
else
|
||||
{
|
||||
WebSocketMapping mapping = new WebSocketMapping(WebSocketComponents.ensureWebSocketComponents(servletContext));
|
||||
contextHandler.setAttribute(mappingKey, mapping);
|
||||
return mapping;
|
||||
}
|
||||
|
||||
return mapping;
|
||||
}
|
||||
|
||||
private final PathMappings<Negotiator> mappings = new PathMappings<>();
|
||||
private final Handshaker handshaker = Handshaker.newInstance();
|
||||
public static final String DEFAULT_KEY = "org.eclipse.jetty.websocket.servlet.WebSocketMapping";
|
||||
|
||||
private DecoratedObjectFactory objectFactory;
|
||||
private ClassLoader contextClassLoader;
|
||||
private WebSocketExtensionRegistry extensionRegistry;
|
||||
private ByteBufferPool bufferPool;
|
||||
private final PathMappings<Negotiator> mappings = new PathMappings<>();
|
||||
private final WebSocketComponents components;
|
||||
private final Handshaker handshaker = Handshaker.newInstance();
|
||||
|
||||
public WebSocketMapping()
|
||||
{
|
||||
this(new WebSocketExtensionRegistry(), new DecoratedObjectFactory(), new MappedByteBufferPool());
|
||||
this(new WebSocketComponents());
|
||||
}
|
||||
|
||||
public WebSocketMapping(WebSocketExtensionRegistry extensionRegistry, DecoratedObjectFactory objectFactory, ByteBufferPool bufferPool)
|
||||
public WebSocketMapping(WebSocketComponents components)
|
||||
{
|
||||
this.extensionRegistry = extensionRegistry;
|
||||
this.objectFactory = objectFactory;
|
||||
this.bufferPool = bufferPool;
|
||||
this.components = components;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -127,10 +125,12 @@ public class WebSocketMapping implements Dumpable, LifeCycle.Listener
|
|||
* @param customizer the customizer to use to customize the WebSocket session.
|
||||
*/
|
||||
public void addMapping(PathSpec pathSpec, WebSocketCreator creator, FrameHandlerFactory factory, FrameHandler.Customizer customizer)
|
||||
throws WebSocketException
|
||||
{
|
||||
// Handling for response forbidden (and similar paths)
|
||||
// no creation, sorry
|
||||
// No factory worked!
|
||||
// TODO evaluate why this can't be done
|
||||
//if (getMapping(pathSpec) != null)
|
||||
// throw new WebSocketException("Duplicate WebSocket Mapping for PathSpec");
|
||||
|
||||
mappings.put(pathSpec, new Negotiator(creator, factory, customizer));
|
||||
}
|
||||
|
||||
|
@ -157,31 +157,6 @@ public class WebSocketMapping implements Dumpable, LifeCycle.Listener
|
|||
Dumpable.dumpObjects(out, indent, this, mappings);
|
||||
}
|
||||
|
||||
public ByteBufferPool getBufferPool()
|
||||
{
|
||||
return bufferPool;
|
||||
}
|
||||
|
||||
public void setContextClassLoader(ClassLoader classLoader)
|
||||
{
|
||||
this.contextClassLoader = classLoader;
|
||||
}
|
||||
|
||||
public ClassLoader getContextClassloader()
|
||||
{
|
||||
return contextClassLoader;
|
||||
}
|
||||
|
||||
public WebSocketExtensionRegistry getExtensionRegistry()
|
||||
{
|
||||
return this.extensionRegistry;
|
||||
}
|
||||
|
||||
public DecoratedObjectFactory getObjectFactory()
|
||||
{
|
||||
return this.objectFactory;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the matching {@link MappedResource} for the provided target.
|
||||
*
|
||||
|
@ -280,9 +255,7 @@ public class WebSocketMapping implements Dumpable, LifeCycle.Listener
|
|||
|
||||
public Negotiator(WebSocketCreator creator, FrameHandlerFactory factory, FrameHandler.Customizer customizer)
|
||||
{
|
||||
super(WebSocketMapping.this.getExtensionRegistry(), WebSocketMapping.this.getObjectFactory(),
|
||||
WebSocketMapping.this.getBufferPool(),
|
||||
customizer);
|
||||
super(components.getExtensionRegistry(), components.getObjectFactory(), components.getBufferPool(), customizer);
|
||||
this.creator = creator;
|
||||
this.factory = factory;
|
||||
}
|
||||
|
@ -296,10 +269,16 @@ public class WebSocketMapping implements Dumpable, LifeCycle.Listener
|
|||
@Override
|
||||
public FrameHandler negotiate(Negotiation negotiation)
|
||||
{
|
||||
ServletContext servletContext = negotiation.getRequest().getServletContext();
|
||||
if (servletContext == null)
|
||||
throw new IllegalStateException("null servletContext from request");
|
||||
|
||||
ClassLoader loader = servletContext.getClassLoader();
|
||||
ClassLoader old = Thread.currentThread().getContextClassLoader();
|
||||
|
||||
try
|
||||
{
|
||||
Thread.currentThread().setContextClassLoader(getContextClassloader());
|
||||
Thread.currentThread().setContextClassLoader(loader);
|
||||
|
||||
ServletUpgradeRequest upgradeRequest = new ServletUpgradeRequest(negotiation);
|
||||
ServletUpgradeResponse upgradeResponse = new ServletUpgradeResponse(negotiation);
|
||||
|
|
|
@ -19,8 +19,8 @@
|
|||
package org.eclipse.jetty.websocket.servlet;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.time.Duration;
|
||||
|
||||
import javax.servlet.ServletContext;
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.http.HttpServlet;
|
||||
|
@ -33,6 +33,7 @@ import org.eclipse.jetty.servlet.ServletContextHandler;
|
|||
import org.eclipse.jetty.util.log.Log;
|
||||
import org.eclipse.jetty.util.log.Logger;
|
||||
import org.eclipse.jetty.websocket.core.FrameHandler;
|
||||
import org.eclipse.jetty.websocket.core.WebSocketComponents;
|
||||
import org.eclipse.jetty.websocket.core.WebSocketExtensionRegistry;
|
||||
|
||||
/**
|
||||
|
@ -94,6 +95,7 @@ public abstract class WebSocketServlet extends HttpServlet
|
|||
private final CustomizedWebSocketServletFactory customizer = new CustomizedWebSocketServletFactory();
|
||||
|
||||
private WebSocketMapping mapping;
|
||||
private WebSocketComponents components;
|
||||
|
||||
/**
|
||||
* Configure the WebSocketServletFactory for this servlet instance by setting default
|
||||
|
@ -111,7 +113,8 @@ public abstract class WebSocketServlet extends HttpServlet
|
|||
{
|
||||
ServletContext servletContext = getServletContext();
|
||||
|
||||
mapping = WebSocketMapping.ensureMapping(servletContext);
|
||||
components = WebSocketComponents.ensureWebSocketComponents(servletContext);
|
||||
mapping = new WebSocketMapping(components);
|
||||
|
||||
String max = getInitParameter("maxIdleTime");
|
||||
if (max != null)
|
||||
|
@ -176,79 +179,7 @@ public abstract class WebSocketServlet extends HttpServlet
|
|||
{
|
||||
public WebSocketExtensionRegistry getExtensionRegistry()
|
||||
{
|
||||
return mapping.getExtensionRegistry();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Duration getDefaultIdleTimeout()
|
||||
{
|
||||
return getIdleTimeout();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setDefaultIdleTimeout(Duration duration)
|
||||
{
|
||||
setIdleTimeout(duration);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getDefaultInputBufferSize()
|
||||
{
|
||||
return getInputBufferSize();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setDefaultInputBufferSize(int bufferSize)
|
||||
{
|
||||
setInputBufferSize(bufferSize);
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getDefaultMaxAllowedFrameSize()
|
||||
{
|
||||
return getMaxFrameSize();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setDefaultMaxAllowedFrameSize(long maxFrameSize)
|
||||
{
|
||||
setMaxFrameSize(maxFrameSize);
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getDefaultMaxBinaryMessageSize()
|
||||
{
|
||||
return getMaxBinaryMessageSize();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setDefaultMaxBinaryMessageSize(long size)
|
||||
{
|
||||
setMaxBinaryMessageSize(size);
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getDefaultMaxTextMessageSize()
|
||||
{
|
||||
return getMaxTextMessageSize();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setDefaultMaxTextMessageSize(long size)
|
||||
{
|
||||
setMaxTextMessageSize(size);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getDefaultOutputBufferSize()
|
||||
{
|
||||
return getOutputBufferSize();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setDefaultOutputBufferSize(int bufferSize)
|
||||
{
|
||||
setOutputBufferSize(bufferSize);
|
||||
return components.getExtensionRegistry();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -271,6 +202,41 @@ public abstract class WebSocketServlet extends HttpServlet
|
|||
mapping.addMapping(pathSpec, creator, frameHandlerFactory, this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void register(Class<?> endpointClass)
|
||||
{
|
||||
Constructor<?> constructor;
|
||||
try
|
||||
{
|
||||
constructor = endpointClass.getDeclaredConstructor(null);
|
||||
}
|
||||
catch (NoSuchMethodException e)
|
||||
{
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
|
||||
WebSocketCreator creator = (req, resp) ->
|
||||
{
|
||||
try
|
||||
{
|
||||
return constructor.newInstance();
|
||||
}
|
||||
catch (Throwable t)
|
||||
{
|
||||
t.printStackTrace();
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
addMapping("/", creator);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setCreator(WebSocketCreator creator)
|
||||
{
|
||||
addMapping("/", creator);
|
||||
}
|
||||
|
||||
@Override
|
||||
public WebSocketCreator getMapping(PathSpec pathSpec)
|
||||
{
|
||||
|
|
|
@ -18,39 +18,39 @@
|
|||
|
||||
package org.eclipse.jetty.websocket.servlet;
|
||||
|
||||
import java.time.Duration;
|
||||
|
||||
import org.eclipse.jetty.http.pathmap.PathSpec;
|
||||
import org.eclipse.jetty.websocket.core.WebSocketExtensionRegistry;
|
||||
|
||||
import java.time.Duration;
|
||||
|
||||
public interface WebSocketServletFactory
|
||||
{
|
||||
|
||||
WebSocketExtensionRegistry getExtensionRegistry();
|
||||
|
||||
Duration getDefaultIdleTimeout();
|
||||
Duration getIdleTimeout();
|
||||
|
||||
void setDefaultIdleTimeout(Duration duration);
|
||||
void setIdleTimeout(Duration duration);
|
||||
|
||||
int getDefaultInputBufferSize();
|
||||
int getInputBufferSize();
|
||||
|
||||
void setDefaultInputBufferSize(int bufferSize);
|
||||
void setInputBufferSize(int bufferSize);
|
||||
|
||||
long getDefaultMaxAllowedFrameSize();
|
||||
long getMaxFrameSize();
|
||||
|
||||
void setDefaultMaxAllowedFrameSize(long maxFrameSize);
|
||||
void setMaxFrameSize(long maxFrameSize);
|
||||
|
||||
long getDefaultMaxBinaryMessageSize();
|
||||
long getMaxBinaryMessageSize();
|
||||
|
||||
void setDefaultMaxBinaryMessageSize(long bufferSize);
|
||||
void setMaxBinaryMessageSize(long bufferSize);
|
||||
|
||||
long getDefaultMaxTextMessageSize();
|
||||
long getMaxTextMessageSize();
|
||||
|
||||
void setDefaultMaxTextMessageSize(long bufferSize);
|
||||
void setMaxTextMessageSize(long bufferSize);
|
||||
|
||||
int getDefaultOutputBufferSize();
|
||||
int getOutputBufferSize();
|
||||
|
||||
void setDefaultOutputBufferSize(int bufferSize);
|
||||
void setOutputBufferSize(int bufferSize);
|
||||
|
||||
boolean isAutoFragment();
|
||||
|
||||
|
@ -72,6 +72,20 @@ public interface WebSocketServletFactory
|
|||
*/
|
||||
void addMapping(PathSpec pathSpec, WebSocketCreator creator);
|
||||
|
||||
/**
|
||||
* Add a WebSocket mapping at PathSpec "/" for a creator which creates the endpointClass
|
||||
*
|
||||
* @param endpointClass the WebSocket class to use
|
||||
*/
|
||||
void register(Class<?> endpointClass);
|
||||
|
||||
/**
|
||||
* Add a WebSocket mapping at PathSpec "/" for a creator
|
||||
*
|
||||
* @param creator the WebSocketCreator to use
|
||||
*/
|
||||
void setCreator(WebSocketCreator creator);
|
||||
|
||||
/**
|
||||
* Returns the creator for the given path spec.
|
||||
*
|
||||
|
|
|
@ -21,7 +21,6 @@ package org.eclipse.jetty.websocket.servlet;
|
|||
import java.io.IOException;
|
||||
import java.time.Duration;
|
||||
import java.util.EnumSet;
|
||||
|
||||
import javax.servlet.DispatcherType;
|
||||
import javax.servlet.Filter;
|
||||
import javax.servlet.FilterChain;
|
||||
|
@ -33,7 +32,6 @@ import javax.servlet.ServletResponse;
|
|||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.eclipse.jetty.http.pathmap.PathSpec;
|
||||
import org.eclipse.jetty.server.handler.ContextHandler;
|
||||
import org.eclipse.jetty.servlet.FilterHolder;
|
||||
import org.eclipse.jetty.servlet.ServletHandler;
|
||||
|
@ -43,8 +41,7 @@ import org.eclipse.jetty.util.component.Dumpable;
|
|||
import org.eclipse.jetty.util.log.Log;
|
||||
import org.eclipse.jetty.util.log.Logger;
|
||||
import org.eclipse.jetty.websocket.core.FrameHandler;
|
||||
import org.eclipse.jetty.websocket.core.server.Handshaker;
|
||||
import org.eclipse.jetty.websocket.core.server.WebSocketNegotiator;
|
||||
import org.eclipse.jetty.websocket.core.WebSocketComponents;
|
||||
|
||||
/**
|
||||
* Inline Servlet Filter to capture WebSocket upgrade requests.
|
||||
|
@ -78,38 +75,46 @@ public class WebSocketUpgradeFilter implements Filter, Dumpable
|
|||
{
|
||||
private static final Logger LOG = Log.getLogger(WebSocketUpgradeFilter.class);
|
||||
|
||||
public static FilterHolder ensureFilter(ServletContext servletContext) throws ServletException
|
||||
|
||||
private static FilterHolder getFilter(ServletContext servletContext)
|
||||
{
|
||||
ServletHandler servletHandler = ContextHandler.getContextHandler(servletContext).getChildHandlerByClass(ServletHandler.class);
|
||||
|
||||
for (FilterHolder holder : servletHandler.getFilters())
|
||||
{
|
||||
if (holder.getClassName().equals(WebSocketUpgradeFilter.class.getName()))
|
||||
return holder;
|
||||
if (holder.getHeldClass()!=null && WebSocketUpgradeFilter.class.isAssignableFrom(holder.getHeldClass()))
|
||||
if (holder.getInitParameter(MAPPING_INIT_PARAM) != null)
|
||||
return holder;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public static FilterHolder ensureFilter(ServletContext servletContext)
|
||||
{
|
||||
FilterHolder existingFilter = WebSocketUpgradeFilter.getFilter(servletContext);
|
||||
if (existingFilter != null)
|
||||
return existingFilter;
|
||||
|
||||
String name = "WebSocketUpgradeFilter";
|
||||
String pathSpec = "/*";
|
||||
EnumSet<DispatcherType> dispatcherTypes = EnumSet.of(DispatcherType.REQUEST);
|
||||
|
||||
FilterHolder holder = new FilterHolder(new WebSocketUpgradeFilter());
|
||||
holder.setName(name);
|
||||
holder.setInitParameter(MAPPING_INIT_PARAM, WebSocketMapping.DEFAULT_KEY);
|
||||
|
||||
holder.setAsyncSupported(true);
|
||||
ServletHandler servletHandler = ContextHandler.getContextHandler(servletContext).getChildHandlerByClass(ServletHandler.class);
|
||||
servletHandler.addFilterWithMapping(holder, pathSpec, dispatcherTypes);
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("Adding {} mapped to {} in {}", holder, pathSpec, servletContext);
|
||||
return holder;
|
||||
}
|
||||
|
||||
public final static String MAPPING_INIT_PARAM = "org.eclipse.jetty.websocket.servlet.WebSocketMapping.key";
|
||||
|
||||
private final FrameHandler.ConfigurationCustomizer defaultCustomizer = new FrameHandler.ConfigurationCustomizer();
|
||||
private WebSocketMapping mapping;
|
||||
|
||||
public WebSocketUpgradeFilter()
|
||||
{
|
||||
}
|
||||
|
||||
@Override
|
||||
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException
|
||||
{
|
||||
|
@ -151,7 +156,12 @@ public class WebSocketUpgradeFilter implements Filter, Dumpable
|
|||
public void init(FilterConfig config) throws ServletException
|
||||
{
|
||||
final ServletContext context = config.getServletContext();
|
||||
mapping = WebSocketMapping.ensureMapping(context);
|
||||
|
||||
String mappingKey = config.getInitParameter(MAPPING_INIT_PARAM);
|
||||
if (mappingKey != null)
|
||||
mapping = WebSocketMapping.ensureMapping(context, mappingKey);
|
||||
else
|
||||
mapping = new WebSocketMapping(WebSocketComponents.ensureWebSocketComponents(context));
|
||||
|
||||
String max = config.getInitParameter("maxIdleTime");
|
||||
if (max != null)
|
||||
|
|
|
@ -18,6 +18,19 @@
|
|||
|
||||
package org.eclipse.jetty.websocket.servlet.internal;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.security.Principal;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Enumeration;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.TreeMap;
|
||||
|
||||
import javax.servlet.AsyncContext;
|
||||
import javax.servlet.DispatcherType;
|
||||
import javax.servlet.RequestDispatcher;
|
||||
|
@ -31,18 +44,10 @@ import javax.servlet.http.HttpServletResponse;
|
|||
import javax.servlet.http.HttpSession;
|
||||
import javax.servlet.http.HttpUpgradeHandler;
|
||||
import javax.servlet.http.Part;
|
||||
import java.io.BufferedReader;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.security.Principal;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Enumeration;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.TreeMap;
|
||||
|
||||
import org.eclipse.jetty.server.Authentication;
|
||||
import org.eclipse.jetty.server.Request;
|
||||
import org.eclipse.jetty.server.UserIdentity;
|
||||
|
||||
/**
|
||||
* An immutable, feature limited, HttpServletRequest that will not be recycled by Jetty.
|
||||
|
@ -67,6 +72,8 @@ public class UpgradeHttpServletRequest implements HttpServletRequest
|
|||
private final Cookie[] cookies;
|
||||
private final String remoteUser;
|
||||
private final Principal principal;
|
||||
private final Authentication authentication;
|
||||
private final UserIdentity.Scope scope;
|
||||
|
||||
private final Map<String, List<String>> headers = new TreeMap<>(String.CASE_INSENSITIVE_ORDER);
|
||||
private final Map<String, String[]> parameters = new TreeMap<>(String.CASE_INSENSITIVE_ORDER);
|
||||
|
@ -104,6 +111,8 @@ public class UpgradeHttpServletRequest implements HttpServletRequest
|
|||
|
||||
remoteUser = httpRequest.getRemoteUser();
|
||||
principal = httpRequest.getUserPrincipal();
|
||||
authentication = Request.getBaseRequest(httpRequest).getAuthentication();
|
||||
scope = Request.getBaseRequest(httpRequest).getUserIdentityScope();
|
||||
|
||||
Enumeration<String> headerNames = httpRequest.getHeaderNames();
|
||||
while (headerNames.hasMoreElements())
|
||||
|
@ -220,7 +229,10 @@ public class UpgradeHttpServletRequest implements HttpServletRequest
|
|||
@Override
|
||||
public boolean isUserInRole(String role)
|
||||
{
|
||||
throw new UnsupportedOperationException(UNSUPPORTED_WITH_WEBSOCKET_UPGRADE);
|
||||
if (authentication instanceof Authentication.User)
|
||||
return ((Authentication.User)authentication).isUserInRole(scope, role);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
2
pom.xml
2
pom.xml
|
@ -50,7 +50,7 @@
|
|||
<jetty-test-policy.version>1.2</jetty-test-policy.version>
|
||||
<servlet.api.version>4.0.1</servlet.api.version>
|
||||
<servlet.schema.version>4.0.3</servlet.schema.version>
|
||||
<jsp.version>8.5.35.1</jsp.version>
|
||||
<jsp.version>9.0.14.1</jsp.version>
|
||||
<!-- default values are unsupported, but required to be defined for reactor sanity reasons -->
|
||||
<alpn.version>undefined</alpn.version>
|
||||
<conscrypt.version>1.4.1</conscrypt.version>
|
||||
|
|
Loading…
Reference in New Issue