Various cleanups of Handler.insertHandler (#10792)

* Various cleanups of Handler.insertHandler
* Added missing call to relinkHandlers() in setHandler() after calling super.
* Moved call to relinkHandlers() in insertHandler(), as the various setSession|Security|ServletHandler() already call relinkHandlers().

Signed-off-by: Simone Bordet <simone.bordet@gmail.com>

---------

Signed-off-by: Simone Bordet <simone.bordet@gmail.com>
Co-authored-by: Simone Bordet <simone.bordet@gmail.com>
This commit is contained in:
Greg Wilkins 2023-11-13 23:13:51 +11:00 committed by GitHub
parent 2c35f5a17a
commit 49b344242b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 259 additions and 92 deletions

View File

@ -325,8 +325,8 @@ public interface Handler extends LifeCycle, Destroyable, Request.Handler
}
/**
* <p>Inserts the given {@code Handler} (and its chain of {@code Handler}s)
* in front of the child of this {@code Handler}.</p>
* <p>Inserts the given {@code Handler} (and possible chain of {@code Handler}s)
* between this {@code Handler} and its current {@link #getHandler() child}.
* <p>For example, if this {@code Handler} {@code A} has a child {@code B},
* inserting {@code Handler} {@code X} built as a chain {@code Handler}s
* {@code X-Y-Z} results in the structure {@code A-X-Y-Z-B}.</p>
@ -335,11 +335,7 @@ public interface Handler extends LifeCycle, Destroyable, Request.Handler
*/
default void insertHandler(Singleton handler)
{
Singleton tail = handler;
while (tail.getHandler() instanceof Wrapper)
{
tail = (Wrapper)tail.getHandler();
}
Singleton tail = handler.getTail();
if (tail.getHandler() != null)
throw new IllegalArgumentException("bad tail of inserted wrapper chain");

View File

@ -135,7 +135,7 @@ public class JaspiTest
JaspiAuthenticatorFactory jaspiAuthFactory = new JaspiAuthenticatorFactory();
ConstraintSecurityHandler security = new ConstraintSecurityHandler();
context.setHandler(security);
context.setSecurityHandler(security);
security.setAuthenticatorFactory(jaspiAuthFactory);
Constraint constraint = new Constraint.Builder()
@ -152,7 +152,7 @@ public class JaspiTest
other.setContextPath("/other");
other.addServlet(new TestServlet(), "/");
ConstraintSecurityHandler securityOther = new ConstraintSecurityHandler();
other.setHandler(securityOther);
other.setSecurityHandler(securityOther);
securityOther.setAuthenticatorFactory(jaspiAuthFactory);
securityOther.addConstraintMapping(mapping);

View File

@ -953,6 +953,7 @@ public class ServletContextHandler extends ContextHandler
if (handler != null)
LOG.warn("ServletContextHandler.setHandler should not be called directly. Use insertHandler or setSessionHandler etc.");
super.setHandler(handler);
relinkHandlers();
}
}
@ -1128,7 +1129,7 @@ public class ServletContextHandler extends ContextHandler
}
@Override
protected ServletContextRequest wrapRequest(Request request, Response response)
protected ContextRequest wrapRequest(Request request, Response response)
{
// Need to ask directly to the Context for the pathInContext, rather than using
// Request.getPathInContext(), as the request is not yet wrapped in this Context.
@ -1136,10 +1137,10 @@ public class ServletContextHandler extends ContextHandler
MatchedResource<ServletHandler.MappedServlet> matchedResource = _servletHandler.getMatchedServlet(decodedPathInContext);
if (matchedResource == null)
return null;
return wrapNoServlet(request, response);
ServletHandler.MappedServlet mappedServlet = matchedResource.getResource();
if (mappedServlet == null)
return null;
return wrapNoServlet(request, response);
// Get a servlet request, possibly from a cached version in the channel attributes.
Attributes cache = request.getComponents().getCache();
@ -1161,12 +1162,20 @@ public class ServletContextHandler extends ContextHandler
return servletContextRequest;
}
private ContextRequest wrapNoServlet(Request request, Response response)
{
Handler next = getServletHandler().getHandler();
if (next == null)
return null;
return super.wrapRequest(request, response);
}
@Override
protected ContextResponse wrapResponse(ContextRequest request, Response response)
{
if (request instanceof ServletContextRequest servletContextRequest)
return servletContextRequest.getServletContextResponse();
throw new IllegalArgumentException();
return super.wrapResponse(request, response);
}
@Override
@ -1662,26 +1671,17 @@ public class ServletContextHandler extends ContextHandler
setServletHandler((ServletHandler)handler);
else
{
// We cannot call super.insertHandler here, because it uses this.setHandler
// which sets the servletHandlers next handler.
// This is the same insert code, but uses super.setHandler, which sets this
// handler's next handler.
Singleton tail = handler.getTail();
if (tail.getHandler() != null)
throw new IllegalArgumentException("bad tail of inserted wrapper chain");
// Skip any injected handlers
Singleton h = this;
while (h.getHandler() instanceof Singleton wrapper)
{
if (wrapper instanceof SessionHandler ||
wrapper instanceof SecurityHandler ||
wrapper instanceof ServletHandler)
break;
h = wrapper;
}
Handler next = h.getHandler();
doSetHandler(h, handler);
doSetHandler(tail, next);
tail.setHandler(getHandler());
super.setHandler(handler);
relinkHandlers();
}
relinkHandlers();
}
/**

View File

@ -452,16 +452,21 @@ public class ServletHandler extends Handler.Wrapper
@Override
public boolean handle(Request request, Response response, Callback callback) throws Exception
{
// We will always have a ServletContextRequest as we must be within a ServletContextHandler
// We have a ServletContextRequest only when an enclosing ServletContextHandler matched a Servlet
ServletChannel servletChannel = Request.get(request, ServletContextRequest.class, ServletContextRequest::getServletChannel);
if (servletChannel != null)
{
if (LOG.isDebugEnabled())
LOG.debug("handle {} {} {} {} {}", this, servletChannel, request, response, callback);
if (LOG.isDebugEnabled())
LOG.debug("handle {} {} {} {}", this, request, response, callback);
// But request, response and/or callback may have been wrapped after the ServletContextHandler, so update the channel.
servletChannel.associate(request, response, callback);
servletChannel.handle();
return true;
}
// But request, response and/or callback may have been wrapped after the ServletContextHandler, so update the channel.
servletChannel.associate(request, response, callback);
servletChannel.handle();
return true;
// Otherwise, there is no matching servlet so we pass to our next handler (if any)
return super.handle(request, response, callback);
}
/**

View File

@ -386,7 +386,17 @@ public class SessionHandler extends AbstractSessionManager implements Handler.Si
@Override
public ManagedSession getManagedSession(Request request)
{
return Request.as(request, ServletContextRequest.class).getManagedSession();
ServletContextRequest servletContextRequest = Request.as(request, ServletContextRequest.class);
if (servletContextRequest != null)
return servletContextRequest.getManagedSession();
NonServletSessionRequest nonServletSessionRequest = Request.as(request, NonServletSessionRequest.class);
if (nonServletSessionRequest != null)
return nonServletSessionRequest.getManagedSession();
if (request.getSession(false) instanceof ManagedSession managedSession)
return managedSession;
return null;
}
/**
@ -674,21 +684,61 @@ public class SessionHandler extends AbstractSessionManager implements Handler.Si
if (next == null)
return false;
ServletContextRequest servletContextRequest = Request.as(request, ServletContextRequest.class);
addSessionStreamWrapper(request);
// find and set the session if one exists
RequestedSession requestedSession = resolveRequestedSessionId(request);
servletContextRequest.setRequestedSession(requestedSession);
ServletContextRequest servletContextRequest = Request.as(request, ServletContextRequest.class);
if (servletContextRequest == null)
request = new NonServletSessionRequest(request, response, requestedSession);
else
servletContextRequest.setRequestedSession(requestedSession);
// Handle changed ID or max-age refresh, but only if this is not a redispatched request
HttpCookie cookie = access(requestedSession.session(), request.getConnectionMetaData().isSecure());
if (cookie != null)
{
ServletContextResponse servletContextResponse = servletContextRequest.getServletContextResponse();
Response.putCookie(servletContextResponse, cookie);
}
Response.putCookie(response, cookie);
return next.handle(request, response, callback);
}
private class NonServletSessionRequest extends Request.Wrapper
{
private final Response _response;
private RequestedSession _session;
public NonServletSessionRequest(Request request, Response response, RequestedSession requestedSession)
{
super(request);
_response = response;
_session = requestedSession;
}
@Override
public Session getSession(boolean create)
{
ManagedSession session = _session.session();
if (session != null || !create)
return session;
newSession(getWrapped(), _session.sessionId(), ms ->
_session = new RequestedSession(ms, _session.sessionId(), true));
session = _session.session();
if (session == null)
throw new IllegalStateException("Create session failed");
HttpCookie cookie = getSessionCookie(session, isSecure());
if (cookie != null)
Response.replaceCookie(_response, cookie);
return session;
}
ManagedSession getManagedSession()
{
return _session.session();
}
}
}

View File

@ -33,8 +33,6 @@ import jakarta.servlet.ServletSecurityElement;
import jakarta.servlet.annotation.ServletSecurity.EmptyRoleSemantic;
import jakarta.servlet.annotation.ServletSecurity.TransportGuarantee;
import org.eclipse.jetty.ee10.servlet.ServletContextHandler;
import org.eclipse.jetty.ee10.servlet.security.ConstraintAware;
import org.eclipse.jetty.ee10.servlet.security.ConstraintMapping;
import org.eclipse.jetty.http.pathmap.MappedResource;
import org.eclipse.jetty.http.pathmap.MatchedResource;
import org.eclipse.jetty.http.pathmap.PathMappings;
@ -186,7 +184,7 @@ public class ConstraintSecurityHandler extends SecurityHandler implements Constr
}
/**
* Generate Constraints and ContraintMappings for the given url pattern and ServletSecurityElement
* Generate Constraints and ConstraintMappings for the given url pattern and ServletSecurityElement
*
* @param name the name
* @param pathSpec the path spec

View File

@ -71,6 +71,7 @@ import org.eclipse.jetty.http.HttpCookie;
import org.eclipse.jetty.http.HttpHeader;
import org.eclipse.jetty.http.HttpTester;
import org.eclipse.jetty.http.pathmap.MatchedResource;
import org.eclipse.jetty.io.Content;
import org.eclipse.jetty.logging.StacklessLogging;
import org.eclipse.jetty.security.Constraint;
import org.eclipse.jetty.security.SecurityHandler;
@ -104,6 +105,7 @@ import static org.hamcrest.Matchers.containsString;
import static org.hamcrest.Matchers.instanceOf;
import static org.hamcrest.Matchers.notNullValue;
import static org.hamcrest.Matchers.nullValue;
import static org.hamcrest.Matchers.sameInstance;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertNotEquals;
@ -1965,9 +1967,9 @@ public class ServletContextHandlerTest
}
@Test
public void testReplaceHandler() throws Exception
public void testInsertHandler() throws Exception
{
ServletContextHandler servletContextHandler = new ServletContextHandler();
ServletContextHandler servletContextHandler = new ServletContextHandler(ServletContextHandler.SESSIONS | ServletContextHandler.SECURITY);
ServletHolder sh = new ServletHolder(new TestServlet());
servletContextHandler.addServlet(sh, "/foo");
final AtomicBoolean contextInit = new AtomicBoolean(false);
@ -1975,7 +1977,6 @@ public class ServletContextHandlerTest
servletContextHandler.addEventListener(new ServletContextListener()
{
@Override
public void contextInitialized(ServletContextEvent sce)
{
@ -1990,15 +1991,26 @@ public class ServletContextHandlerTest
contextDestroy.set(true);
}
});
ServletHandler shandler = servletContextHandler.getServletHandler();
ResourceHandler rh = new ResourceHandler();
rh.setBaseResource(ResourceFactory.of(rh).newResource(Paths.get(".")));
servletContextHandler.insertHandler(rh);
assertEquals(shandler, servletContextHandler.getServletHandler());
assertEquals(rh, servletContextHandler.getHandler());
assertEquals(rh.getHandler(), shandler);
Handler last = new Handler.Abstract()
{
@Override
public boolean handle(Request request, Response response, Callback callback)
{
return false;
}
};
servletContextHandler.getServletHandler().setHandler(last);
assertThat(servletContextHandler.getHandler(), sameInstance(rh));
assertThat(rh.getHandler(), instanceOf(SessionHandler.class));
assertThat(servletContextHandler.getSessionHandler().getHandler(), instanceOf(SecurityHandler.class));
assertThat(servletContextHandler.getSecurityHandler().getHandler(), instanceOf(ServletHandler.class));
assertThat(servletContextHandler.getServletHandler().getHandler(), sameInstance(last));
_server.setHandler(servletContextHandler);
_server.start();
assertTrue(contextInit.get());
@ -2007,7 +2019,7 @@ public class ServletContextHandlerTest
}
@Test
public void testFallThrough() throws Exception
public void testFallThroughContextHandler() throws Exception
{
Handler.Sequence list = new Handler.Sequence();
_server.setHandler(list);
@ -2038,6 +2050,37 @@ public class ServletContextHandlerTest
assertThat(response, Matchers.containsString("404 Fell Through"));
}
@Test
public void testFallThroughServletHandler() throws Exception
{
ServletContextHandler root = new ServletContextHandler("/", ServletContextHandler.SESSIONS | ServletContextHandler.SECURITY);
_server.setHandler(root);
ServletHandler servlet = root.getServletHandler();
servlet.setEnsureDefaultServlet(false);
servlet.addServletWithMapping(HelloServlet.class, "/hello/*");
servlet.setHandler(new Handler.Abstract()
{
@Override
public boolean handle(Request request, Response response, Callback callback) throws Exception
{
Content.Sink.write(response, true, "Fell Through Handler: " + request.getSession(true).getId(), callback);
return true;
}
});
_server.start();
String response = _connector.getResponse("GET /hello HTTP/1.0\r\n\r\n");
assertThat(response, Matchers.containsString("200 OK"));
response = _connector.getResponse("GET /other HTTP/1.0\r\n\r\n");
assertThat(response, Matchers.containsString("200 OK"));
assertThat(response, Matchers.containsString("Set-Cookie: JSESSIONID="));
assertThat(response, Matchers.containsString("Fell Through Handler"));
}
/**
* Test behavior of new {@link org.eclipse.jetty.util.Decorator}, with
* new DecoratedObjectFactory class

View File

@ -285,6 +285,15 @@ public class ContextHandler extends ScopedHandler implements Attributes, Supplie
return _coreContextHandler;
}
/**
* Insert a handler between the {@link #getCoreContextHandler()} and this handler.
* @param coreHandler A core handler to insert
*/
public void insertHandler(org.eclipse.jetty.server.Handler.Singleton coreHandler)
{
getCoreContextHandler().insertHandler(coreHandler);
}
@Override
public void dump(Appendable out, String indent) throws IOException
{
@ -2607,6 +2616,8 @@ public class ContextHandler extends ScopedHandler implements Attributes, Supplie
@Override
public void insertHandler(Singleton handler)
{
// We cannot call super.insertHandler here, because it uses this.setHandler
// which gives a warning. This is the same code, but uses super.setHandler
Singleton tail = handler.getTail();
if (tail.getHandler() != null)
throw new IllegalArgumentException("bad tail of inserted wrapper chain");

View File

@ -81,6 +81,18 @@ public class HandlerWrapper extends AbstractHandlerContainer
updateBean(old, _handler, true);
}
/**
* Get the tail of a chain of {@link HandlerWrapper}s.
* @return The last {@link HandlerWrapper} in a chain of {@link HandlerWrapper}s
*/
public HandlerWrapper getTail()
{
HandlerWrapper tail = this;
while (tail.getHandler() instanceof HandlerWrapper handlerWrapper)
tail = handlerWrapper;
return tail;
}
/**
* Replace the current handler with another HandlerWrapper
* linked to the current handler.
@ -98,14 +110,7 @@ public class HandlerWrapper extends AbstractHandlerContainer
if (wrapper == null)
throw new IllegalArgumentException();
HandlerWrapper tail = wrapper;
while (tail.getHandler() instanceof HandlerWrapper)
{
tail = (HandlerWrapper)tail.getHandler();
}
if (tail.getHandler() != null)
throw new IllegalArgumentException("bad tail of inserted wrapper chain");
HandlerWrapper tail = wrapper.getTail();
Handler next = getHandler();
setHandler(wrapper);
tail.setHandler(next);

View File

@ -32,9 +32,12 @@ import org.eclipse.jetty.http.HttpTester;
import org.eclipse.jetty.http.MimeTypes;
import org.eclipse.jetty.logging.StacklessLogging;
import org.eclipse.jetty.server.Context;
import org.eclipse.jetty.server.Handler;
import org.eclipse.jetty.server.LocalConnector;
import org.eclipse.jetty.server.Response;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.server.handler.ContextHandlerCollection;
import org.eclipse.jetty.util.Callback;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeEach;
@ -48,6 +51,7 @@ import static org.hamcrest.Matchers.greaterThan;
import static org.hamcrest.Matchers.is;
import static org.hamcrest.Matchers.not;
import static org.hamcrest.Matchers.notNullValue;
import static org.hamcrest.Matchers.sameInstance;
public class ContextHandlerTest
{
@ -62,7 +66,6 @@ public class ContextHandlerTest
_connector = new LocalConnector(_server);
_server.addConnector(_connector);
_contextHandler = new ContextHandler();
_server.setHandler(_contextHandler);
}
@ -812,6 +815,54 @@ public class ContextHandlerTest
"Core exit http://0.0.0.0/"));
}
@Test
public void testInsertHandler() throws Exception
{
HelloHandler helloHandler = new HelloHandler();
_contextHandler.setHandler(helloHandler);
Handler.Wrapper coreHandler = new Handler.Wrapper()
{
@Override
public boolean handle(org.eclipse.jetty.server.Request request, Response response, Callback callback) throws Exception
{
response.getHeaders().put("Core", "Inserted");
return super.handle(request, response, callback);
}
};
_contextHandler.insertHandler(coreHandler);
HandlerWrapper nestedHandler = new HandlerWrapper()
{
@Override
public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
{
response.setHeader("Nested", "Inserted");
super.handle(target, baseRequest, request, response);
}
};
_contextHandler.insertHandler(nestedHandler);
assertThat(_contextHandler.getCoreContextHandler().getHandler(), sameInstance(coreHandler));
assertThat(coreHandler.getHandler().toString(), containsString("CoreToNestedHandler"));
assertThat(_contextHandler.getHandler(), sameInstance(nestedHandler));
assertThat(nestedHandler.getHandler(), sameInstance(helloHandler));
_server.start();
String rawResponse = _connector.getResponse("""
GET / HTTP/1.0
""");
HttpTester.Response response = HttpTester.parseResponse(rawResponse);
assertThat(response.getStatus(), is(200));
assertThat(response.getField(HttpHeader.CONTENT_LENGTH).getIntValue(), greaterThan(0));
assertThat(response.getContent(), containsString("Hello"));
assertThat(response.get("Core"), is("Inserted"));
assertThat(response.get("Nested"), is("Inserted"));
}
private static class TestErrorHandler extends ErrorHandler implements ErrorHandler.ErrorPageMapper
{
@Override

View File

@ -213,23 +213,6 @@ public class ServletContextHandler extends ContextHandler
return false;
}
@Override
public void setHandler(Handler handler)
{
if (handler instanceof SessionHandler)
setSessionHandler((SessionHandler)handler);
else if (handler instanceof SecurityHandler)
setSecurityHandler((SecurityHandler)handler);
else if (handler instanceof ServletHandler)
setServletHandler((ServletHandler)handler);
else
{
if (handler != null)
LOG.warn("ServletContextHandler.setHandler should not be called directly. Use insertHandler or setSessionHandler etc.");
super.setHandler(handler);
}
}
private void doSetHandler(HandlerWrapper wrapper, Handler handler)
{
if (wrapper == this)
@ -683,6 +666,19 @@ public class ServletContextHandler extends ContextHandler
relinkHandlers();
}
@Override
public void setHandler(Handler handler)
{
if (handler instanceof SessionHandler)
setSessionHandler((SessionHandler)handler);
else if (handler instanceof SecurityHandler)
setSecurityHandler((SecurityHandler)handler);
else if (handler instanceof ServletHandler)
setServletHandler((ServletHandler)handler);
else
getServletHandler().setHandler(handler);
}
/**
* Insert a HandlerWrapper before the first Session, Security or ServletHandler
* but after any other HandlerWrappers.
@ -707,6 +703,8 @@ public class ServletContextHandler extends ContextHandler
throw new IllegalArgumentException("bad tail of inserted wrapper chain");
// Skip any injected handlers
// This is not the best behavior, but is retained here for jetty-10/11 compatibility
// but is changed in ee10 and beyond
HandlerWrapper h = this;
while (h.getHandler() instanceof HandlerWrapper)
{

View File

@ -60,9 +60,9 @@ import jakarta.servlet.http.HttpSessionEvent;
import jakarta.servlet.http.HttpSessionIdListener;
import jakarta.servlet.http.HttpSessionListener;
import org.eclipse.jetty.ee9.nested.ContextHandler;
import org.eclipse.jetty.ee9.nested.Handler;
import org.eclipse.jetty.ee9.nested.HandlerWrapper;
import org.eclipse.jetty.ee9.nested.Request;
import org.eclipse.jetty.ee9.nested.ResourceHandler;
import org.eclipse.jetty.ee9.nested.Response;
import org.eclipse.jetty.ee9.nested.SessionHandler;
import org.eclipse.jetty.ee9.security.RoleInfo;
@ -90,6 +90,7 @@ import static org.hamcrest.Matchers.containsString;
import static org.hamcrest.Matchers.instanceOf;
import static org.hamcrest.Matchers.notNullValue;
import static org.hamcrest.Matchers.nullValue;
import static org.hamcrest.Matchers.sameInstance;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertNotNull;
@ -1827,9 +1828,10 @@ public class ServletContextHandlerTest
}
@Test
public void testReplaceHandler() throws Exception
public void testInsertHandler() throws Exception
{
ServletContextHandler servletContextHandler = new ServletContextHandler();
ServletContextHandler servletContextHandler = new ServletContextHandler(ServletContextHandler.SESSIONS | ServletContextHandler.SECURITY);
servletContextHandler.setBaseResourceAsString(".");
ServletHolder sh = new ServletHolder(new TestServlet());
servletContextHandler.addServlet(sh, "/foo");
final AtomicBoolean contextInit = new AtomicBoolean(false);
@ -1837,7 +1839,6 @@ public class ServletContextHandlerTest
servletContextHandler.addEventListener(new ServletContextListener()
{
@Override
public void contextInitialized(ServletContextEvent sce)
{
@ -1852,14 +1853,23 @@ public class ServletContextHandlerTest
contextDestroy.set(true);
}
});
ServletHandler shandler = servletContextHandler.getServletHandler();
ResourceHandler rh = new ResourceHandler();
org.eclipse.jetty.server.handler.ResourceHandler coreResourceHandler = new org.eclipse.jetty.server.handler.ResourceHandler();
HandlerWrapper nestedHandler = new HandlerWrapper();
Handler last = new HandlerWrapper();
servletContextHandler.insertHandler(coreResourceHandler);
servletContextHandler.insertHandler(nestedHandler);
servletContextHandler.setHandler(last);
assertThat(servletContextHandler.getCoreContextHandler().getHandler(), sameInstance(coreResourceHandler));
assertThat(coreResourceHandler.getHandler().toString(), containsString("CoreToNestedHandler@"));
assertThat(servletContextHandler.getHandler(), sameInstance(nestedHandler));
assertThat(nestedHandler.getHandler(), instanceOf(SessionHandler.class));
assertThat(servletContextHandler.getSessionHandler().getHandler(), instanceOf(SecurityHandler.class));
assertThat(servletContextHandler.getSecurityHandler().getHandler(), instanceOf(ServletHandler.class));
assertThat(servletContextHandler.getServletHandler().getHandler(), sameInstance(last));
servletContextHandler.insertHandler(rh);
assertEquals(shandler, servletContextHandler.getServletHandler());
assertEquals(rh, servletContextHandler.getHandler());
assertEquals(rh.getHandler(), shandler);
_server.setHandler(servletContextHandler);
_server.start();
assertTrue(contextInit.get());