Merge remote-tracking branch 'origin/master' into jetty-http2

Conflicts:
	jetty-server/src/main/java/org/eclipse/jetty/server/HttpConnection.java
This commit is contained in:
Greg Wilkins 2014-07-23 16:38:15 +10:00
commit ecb3ab7e5b
13 changed files with 449 additions and 133 deletions

View File

@ -82,6 +82,21 @@ public class JspcMojo extends AbstractMojo
public static final String PRECOMPILED_FLAG = "org.eclipse.jetty.jsp.precompiled"; public static final String PRECOMPILED_FLAG = "org.eclipse.jetty.jsp.precompiled";
/**
* JettyJspC
*
* Add some extra setters to standard JspC class to help configure it
* for running in maven.
*/
public static class JettyJspC extends JspC
{
public void setClassLoader (ClassLoader loader)
{
this.loader = loader;
}
}
/** /**
* Whether or not to include dependencies on the plugin's classpath with <scope>provided</scope> * Whether or not to include dependencies on the plugin's classpath with <scope>provided</scope>
* Use WITH CAUTION as you may wind up with duplicate jars/classes. * Use WITH CAUTION as you may wind up with duplicate jars/classes.
@ -219,7 +234,7 @@ public class JspcMojo extends AbstractMojo
* *
* @parameter * @parameter
*/ */
private JspC jspc; private JettyJspC jspc;
@ -287,18 +302,21 @@ public class JspcMojo extends AbstractMojo
webAppClassPath.append(System.getProperty("path.separator")); webAppClassPath.append(System.getProperty("path.separator"));
} }
Thread.currentThread().setContextClassLoader(webAppClassLoader); //Interpose a fake classloader as the webapp class loader. This is because the Apache JspC class
//uses a TldScanner which ignores jars outside of the WEB-INF/lib path on the webapp classloader.
//It will, however, look at all jars on the parents of the webapp classloader.
URLClassLoader fakeWebAppClassLoader = new URLClassLoader(new URL[0], webAppClassLoader);
Thread.currentThread().setContextClassLoader(fakeWebAppClassLoader);
if (jspc == null) if (jspc == null)
jspc = new JspC(); jspc = new JettyJspC();
jspc.setWebXmlFragment(webXmlFragment); jspc.setWebXmlFragment(webXmlFragment);
jspc.setUriroot(webAppSourceDirectory); jspc.setUriroot(webAppSourceDirectory);
jspc.setOutputDir(generatedClasses); jspc.setOutputDir(generatedClasses);
jspc.setClassPath(sysClassPath+System.getProperty("path.separator")+webAppClassPath.toString()); jspc.setClassPath(sysClassPath+System.getProperty("path.separator")+webAppClassPath.toString());
jspc.setClassLoader(fakeWebAppClassLoader);
jspc.setCompile(true); jspc.setCompile(true);
//jspc.setSystemClassPath(sysClassPath);
// JspC#setExtensions() does not exist, so // JspC#setExtensions() does not exist, so
// always set concrete list of files that will be processed. // always set concrete list of files that will be processed.

View File

@ -32,6 +32,7 @@ import java.util.Set;
import java.util.concurrent.Executor; import java.util.concurrent.Executor;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException; import java.util.concurrent.TimeoutException;
import javax.servlet.AsyncContext; import javax.servlet.AsyncContext;
import javax.servlet.ServletConfig; import javax.servlet.ServletConfig;
import javax.servlet.ServletContext; import javax.servlet.ServletContext;
@ -51,6 +52,7 @@ import org.eclipse.jetty.http.HttpField;
import org.eclipse.jetty.http.HttpHeader; import org.eclipse.jetty.http.HttpHeader;
import org.eclipse.jetty.http.HttpHeaderValue; import org.eclipse.jetty.http.HttpHeaderValue;
import org.eclipse.jetty.http.HttpVersion; import org.eclipse.jetty.http.HttpVersion;
import org.eclipse.jetty.server.HttpConnection;
import org.eclipse.jetty.util.Callback; import org.eclipse.jetty.util.Callback;
import org.eclipse.jetty.util.HttpCookieStore; import org.eclipse.jetty.util.HttpCookieStore;
import org.eclipse.jetty.util.log.Log; import org.eclipse.jetty.util.log.Log;
@ -558,7 +560,15 @@ public class ProxyServlet extends HttpServlet
_log.debug(getRequestId(request) + " proxying failed", failure); _log.debug(getRequestId(request) + " proxying failed", failure);
if (response.isCommitted()) if (response.isCommitted())
{ {
request.setAttribute("org.eclipse.jetty.server.Response.failure", failure); // Use Jetty specific behavior to close connection
try
{
response.sendError(-1);
}
catch (IOException e)
{
getServletContext().log("close failed", e);
}
AsyncContext asyncContext = request.getAsyncContext(); AsyncContext asyncContext = request.getAsyncContext();
asyncContext.complete(); asyncContext.complete();
} }

View File

@ -358,21 +358,8 @@ public class HttpChannel implements Runnable
} }
else else
{ {
// There is no way in the Servlet API to directly close a connection, // Complete generating the response
// so we rely on applications to pass this attribute to signal they _response.closeOutput();
// want to hard close the connection, without even closing the output.
Object failure = _request.getAttribute("org.eclipse.jetty.server.Response.failure");
if (failure != null)
{
if (LOG.isDebugEnabled())
LOG.debug("Explicit response failure", failure);
failed();
}
else
{
// Complete generating the response
_response.closeOutput();
}
} }
} }
catch(EofException|ClosedChannelException e) catch(EofException|ClosedChannelException e)
@ -604,10 +591,11 @@ public class HttpChannel implements Runnable
/** /**
* If a write or similar to this channel fails this method should be called. The standard implementation * If a write or similar to this channel fails this method should be called. The standard implementation
* of {@link #failed()} is a noop. But the different implementations of HttpChannel might want to take actions. * is to call {@link HttpTransport#abort()}
*/ */
public void failed() public void abort()
{ {
_transport.abort();
} }
private class CommitCallback implements Callback private class CommitCallback implements Callback

View File

@ -347,10 +347,10 @@ class HttpChannelOverHttp extends HttpChannel implements HttpParser.RequestHandl
} }
@Override @Override
public void failed() public void abort()
{ {
_httpConnection._generator.setPersistent(false); super.abort();
getEndPoint().shutdownOutput(); _generator.setPersistent(false);
} }
@Override @Override

View File

@ -450,6 +450,7 @@ public class HttpConnection extends AbstractConnection implements Runnable, Http
_sendCallback.iterate(); _sendCallback.iterate();
} }
super.abort();
private class SendCallback extends IteratingCallback private class SendCallback extends IteratingCallback
{ {
private ResponseInfo _info; private ResponseInfo _info;

View File

@ -157,7 +157,7 @@ public class HttpOutput extends ServletOutputStream implements Runnable
catch(IOException e) catch(IOException e)
{ {
LOG.debug(e); LOG.debug(e);
_channel.failed(); _channel.abort();
} }
releaseBuffer(); releaseBuffer();
return; return;
@ -192,7 +192,7 @@ public class HttpOutput extends ServletOutputStream implements Runnable
catch(IOException e) catch(IOException e)
{ {
LOG.debug(e); LOG.debug(e);
_channel.failed(); _channel.abort();
} }
releaseBuffer(); releaseBuffer();
return; return;

View File

@ -158,6 +158,8 @@ public class LocalConnector extends AbstractConnector
private LocalEndPoint executeRequest(ByteBuffer rawRequest) private LocalEndPoint executeRequest(ByteBuffer rawRequest)
{ {
if (!isStarted())
throw new IllegalStateException("!STARTED");
LocalEndPoint endp = new LocalEndPoint(); LocalEndPoint endp = new LocalEndPoint();
endp.setInput(rawRequest); endp.setInput(rawRequest);
_connects.add(endp); _connects.add(endp);

View File

@ -541,10 +541,7 @@ public class Response implements HttpServletResponse
@Override @Override
public void sendError(int sc) throws IOException public void sendError(int sc) throws IOException
{ {
if (sc == 102) sendError(sc, null);
sendProcessing();
else
sendError(sc, null);
} }
@Override @Override
@ -553,6 +550,17 @@ public class Response implements HttpServletResponse
if (isIncluding()) if (isIncluding())
return; return;
switch(code)
{
case -1:
_channel.abort();
return;
case 102:
sendProcessing();
return;
default:
}
if (isCommitted()) if (isCommitted())
LOG.warn("Committed before "+code+" "+message); LOG.warn("Committed before "+code+" "+message);

View File

@ -870,27 +870,8 @@ public class ContextHandler extends ScopedHandler implements Attributes, Gracefu
_scontext.clearAttributes(); _scontext.clearAttributes();
} }
/* ------------------------------------------------------------ */ public boolean checkVirtualHost(final Request baseRequest)
/*
* @see org.eclipse.jetty.server.Handler#handle(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse)
*/
public boolean checkContext(final String target, final Request baseRequest, final HttpServletResponse response) throws IOException
{ {
DispatcherType dispatch = baseRequest.getDispatcherType();
switch (_availability)
{
case SHUTDOWN:
case UNAVAILABLE:
baseRequest.setHandled(true);
response.sendError(HttpServletResponse.SC_SERVICE_UNAVAILABLE);
return false;
default:
if ((DispatcherType.REQUEST.equals(dispatch) && baseRequest.isHandled()))
return false;
}
// Check the vhosts
if (_vhosts != null && _vhosts.length > 0) if (_vhosts != null && _vhosts.length > 0)
{ {
String vhost = normalizeHostname(baseRequest.getServerName()); String vhost = normalizeHostname(baseRequest.getServerName());
@ -926,27 +907,61 @@ public class ContextHandler extends ScopedHandler implements Attributes, Gracefu
if (!match || connectorName && !connectorMatch) if (!match || connectorName && !connectorMatch)
return false; return false;
} }
return true;
}
public boolean checkContextPath(String uri)
{
// Are we not the root context? // Are we not the root context?
if (_contextPath.length() > 1) if (_contextPath.length() > 1)
{ {
// reject requests that are not for us // reject requests that are not for us
if (!target.startsWith(_contextPath)) if (!uri.startsWith(_contextPath))
return false; return false;
if (target.length() > _contextPath.length() && target.charAt(_contextPath.length()) != '/') if (uri.length() > _contextPath.length() && uri.charAt(_contextPath.length()) != '/')
return false; return false;
}
return true;
}
// redirect null path infos /* ------------------------------------------------------------ */
if (!_allowNullPathInfo && _contextPath.length() == target.length()) /*
{ * @see org.eclipse.jetty.server.Handler#handle(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse)
// context request must end with / */
public boolean checkContext(final String target, final Request baseRequest, final HttpServletResponse response) throws IOException
{
DispatcherType dispatch = baseRequest.getDispatcherType();
// Check the vhosts
if (!checkVirtualHost(baseRequest))
return false;
if (!checkContextPath(target))
return false;
// Are we not the root context?
// redirect null path infos
if (!_allowNullPathInfo && _contextPath.length() == target.length() && _contextPath.length()>1)
{
// context request must end with /
baseRequest.setHandled(true);
if (baseRequest.getQueryString() != null)
response.sendRedirect(URIUtil.addPaths(baseRequest.getRequestURI(),URIUtil.SLASH) + "?" + baseRequest.getQueryString());
else
response.sendRedirect(URIUtil.addPaths(baseRequest.getRequestURI(),URIUtil.SLASH));
return false;
}
switch (_availability)
{
case SHUTDOWN:
case UNAVAILABLE:
baseRequest.setHandled(true); baseRequest.setHandled(true);
if (baseRequest.getQueryString() != null) response.sendError(HttpServletResponse.SC_SERVICE_UNAVAILABLE);
response.sendRedirect(URIUtil.addPaths(baseRequest.getRequestURI(),URIUtil.SLASH) + "?" + baseRequest.getQueryString());
else
response.sendRedirect(URIUtil.addPaths(baseRequest.getRequestURI(),URIUtil.SLASH));
return false; return false;
} default:
if ((DispatcherType.REQUEST.equals(dispatch) && baseRequest.isHandled()))
return false;
} }
return true; return true;

View File

@ -19,6 +19,15 @@
package org.eclipse.jetty.server.handler; package org.eclipse.jetty.server.handler;
import java.io.IOException; import java.io.IOException;
import java.lang.reflect.Array;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import javax.servlet.ServletException; import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequest;
@ -52,7 +61,8 @@ public class ContextHandlerCollection extends HandlerCollection
{ {
private static final Logger LOG = Log.getLogger(ContextHandlerCollection.class); private static final Logger LOG = Log.getLogger(ContextHandlerCollection.class);
private volatile Trie<ContextHandler[]> _contexts; private final ConcurrentMap<ContextHandler,Handler> _contextBranches = new ConcurrentHashMap<>();
private volatile Trie<Map.Entry<String,Branch[]>> _pathBranches;
private Class<? extends ContextHandler> _contextClass = ContextHandler.class; private Class<? extends ContextHandler> _contextClass = ContextHandler.class;
/* ------------------------------------------------------------ */ /* ------------------------------------------------------------ */
@ -69,71 +79,70 @@ public class ContextHandlerCollection extends HandlerCollection
@ManagedOperation("update the mapping of context path to context") @ManagedOperation("update the mapping of context path to context")
public void mapContexts() public void mapContexts()
{ {
int capacity=512; _contextBranches.clear();
if (getHandlers()==null)
{
_pathBranches=new ArrayTernaryTrie<>(false,16);
return;
}
// Create map of contextPath to handler Branch
Map<String,Branch[]> map = new HashMap<>();
for (Handler handler:getHandlers())
{
Branch branch=new Branch(handler);
for (String contextPath : branch.getContextPaths())
{
Branch[] branches=map.get(contextPath);
map.put(contextPath, ArrayUtil.addToArray(branches, branch, Branch.class));
}
for (ContextHandler context : branch.getContextHandlers())
_contextBranches.putIfAbsent(context, branch.getHandler());
}
// Sort the branches so those with virtual hosts are considered before those without
for (Map.Entry<String,Branch[]> entry: map.entrySet())
{
Branch[] branches=entry.getValue();
Branch[] sorted=new Branch[branches.length];
int i=0;
for (Branch branch:branches)
if (branch.hasVirtualHost())
sorted[i++]=branch;
for (Branch branch:branches)
if (!branch.hasVirtualHost())
sorted[i++]=branch;
entry.setValue(sorted);
}
// Loop until we have a big enough trie to hold all the context paths // Loop until we have a big enough trie to hold all the context paths
Trie<ContextHandler[]> trie; int capacity=512;
Trie<Map.Entry<String,Branch[]>> trie;
loop: while(true) loop: while(true)
{ {
trie=new ArrayTernaryTrie<>(false,capacity); trie=new ArrayTernaryTrie<>(false,capacity);
for (Map.Entry<String,Branch[]> entry: map.entrySet())
Handler[] branches = getHandlers();
// loop over each group of contexts
for (int b=0;branches!=null && b<branches.length;b++)
{ {
Handler[] handlers=null; if (!trie.put(entry.getKey().substring(1),entry))
if (branches[b] instanceof ContextHandler)
{ {
handlers = new Handler[]{ branches[b] }; capacity+=512;
} continue loop;
else if (branches[b] instanceof HandlerContainer)
{
handlers = ((HandlerContainer)branches[b]).getChildHandlersByClass(ContextHandler.class);
}
else
continue;
// for each context handler in a group
for (int i=0;handlers!=null && i<handlers.length;i++)
{
ContextHandler handler=(ContextHandler)handlers[i];
String contextPath=handler.getContextPath().substring(1);
ContextHandler[] contexts=trie.get(contextPath);
if (!trie.put(contextPath,ArrayUtil.addToArray(contexts,handler,ContextHandler.class)))
{
capacity+=512;
continue loop;
}
} }
} }
break loop;
break;
} }
// Sort the contexts so those with virtual hosts are considered before those without
for (String ctx : trie.keySet()) if (LOG.isDebugEnabled())
{ {
ContextHandler[] contexts=trie.get(ctx); for (String ctx : trie.keySet())
ContextHandler[] sorted=new ContextHandler[contexts.length]; LOG.debug("{}->{}",ctx,Arrays.asList(trie.get(ctx).getValue()));
int i=0;
for (ContextHandler handler:contexts)
if (handler.getVirtualHosts()!=null && handler.getVirtualHosts().length>0)
sorted[i++]=handler;
for (ContextHandler handler:contexts)
if (handler.getVirtualHosts()==null || handler.getVirtualHosts().length==0)
sorted[i++]=handler;
trie.put(ctx,sorted);
} }
_pathBranches=trie;
//for (String ctx : trie.keySet())
// System.err.printf("'%s'->%s%n",ctx,Arrays.asList(trie.get(ctx)));
_contexts=trie;
} }
/* ------------------------------------------------------------ */ /* ------------------------------------------------------------ */
/* /*
* @see org.eclipse.jetty.server.server.handler.HandlerCollection#setHandlers(org.eclipse.jetty.server.server.Handler[]) * @see org.eclipse.jetty.server.server.handler.HandlerCollection#setHandlers(org.eclipse.jetty.server.server.Handler[])
@ -172,7 +181,12 @@ public class ContextHandlerCollection extends HandlerCollection
ContextHandler context=async.getContextHandler(); ContextHandler context=async.getContextHandler();
if (context!=null) if (context!=null)
{ {
context.handle(target,baseRequest,request, response); Handler branch = _contextBranches.get(context);
if (branch==null)
context.handle(target,baseRequest,request, response);
else
branch.handle(target, baseRequest, request, response);
return; return;
} }
} }
@ -187,16 +201,18 @@ public class ContextHandlerCollection extends HandlerCollection
while (limit>=0) while (limit>=0)
{ {
// Get best match // Get best match
ContextHandler[] contexts = _contexts.getBest(target,1,limit); Map.Entry<String,Branch[]> branches = _pathBranches.getBest(target,1,limit);
if (contexts==null)
if (branches==null)
break; break;
int l=contexts[0].getContextPath().length(); int l=branches.getKey().length();
if (l==1 || target.length()==l || target.charAt(l)=='/') if (l==1 || target.length()==l || target.charAt(l)=='/')
{ {
for (ContextHandler handler : contexts) for (Branch branch : branches.getValue())
{ {
handler.handle(target,baseRequest, request, response); branch.getHandler().handle(target,baseRequest, request, response);
if (baseRequest.isHandled()) if (baseRequest.isHandled())
return; return;
} }
@ -217,7 +233,6 @@ public class ContextHandlerCollection extends HandlerCollection
} }
} }
/* ------------------------------------------------------------ */ /* ------------------------------------------------------------ */
/** Add a context handler. /** Add a context handler.
* @param contextPath The context path to add * @param contextPath The context path to add
@ -263,5 +278,64 @@ public class ContextHandlerCollection extends HandlerCollection
_contextClass = contextClass; _contextClass = contextClass;
} }
/* ------------------------------------------------------------ */
/* ------------------------------------------------------------ */
/* ------------------------------------------------------------ */
private final static class Branch
{
private final Handler _handler;
private final ContextHandler[] _contexts;
Branch(Handler handler)
{
_handler=handler;
if (handler instanceof ContextHandler)
{
_contexts = new ContextHandler[]{(ContextHandler)handler};
}
else if (handler instanceof HandlerContainer)
{
Handler[] contexts=((HandlerContainer)handler).getChildHandlersByClass(ContextHandler.class);
_contexts = new ContextHandler[contexts.length];
System.arraycopy(contexts, 0, _contexts, 0, contexts.length);
}
else
_contexts = new ContextHandler[0];
}
Set<String> getContextPaths()
{
Set<String> set = new HashSet<String>();
for (ContextHandler context:_contexts)
set.add(context.getContextPath());
return set;
}
boolean hasVirtualHost()
{
for (ContextHandler context:_contexts)
if (context.getVirtualHosts()!=null && context.getVirtualHosts().length>0)
return true;
return false;
}
ContextHandler[] getContextHandlers()
{
return _contexts;
}
Handler getHandler()
{
return _handler;
}
@Override
public String toString()
{
return String.format("{%s,%s}",_handler,Arrays.asList(_contexts));
}
}
} }

View File

@ -19,6 +19,7 @@
package org.eclipse.jetty.server.handler; package org.eclipse.jetty.server.handler;
import java.io.IOException; import java.io.IOException;
import java.util.Arrays;
import java.util.List; import java.util.List;
import javax.servlet.ServletException; import javax.servlet.ServletException;
@ -185,4 +186,12 @@ public class HandlerCollection extends AbstractHandlerContainer
child.destroy(); child.destroy();
super.destroy(); super.destroy();
} }
/* ------------------------------------------------------------ */
@Override
public String toString()
{
Handler[] handlers=getHandlers();
return super.toString()+(handlers==null?"[]":Arrays.asList(getHandlers()).toString());
}
} }

View File

@ -18,17 +18,24 @@
package org.eclipse.jetty.server.handler; package org.eclipse.jetty.server.handler;
import static org.hamcrest.Matchers.containsString;
import static org.hamcrest.Matchers.endsWith;
import static org.hamcrest.Matchers.not;
import static org.hamcrest.Matchers.startsWith;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.assertTrue; import static org.junit.Assert.assertTrue;
import java.io.IOException; import java.io.IOException;
import javax.servlet.AsyncContext;
import javax.servlet.ServletException; import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpServletResponse;
import org.eclipse.jetty.server.Connector; import org.eclipse.jetty.server.Connector;
import org.eclipse.jetty.server.Handler;
import org.eclipse.jetty.server.LocalConnector; import org.eclipse.jetty.server.LocalConnector;
import org.eclipse.jetty.server.Request; import org.eclipse.jetty.server.Request;
import org.eclipse.jetty.server.Server; import org.eclipse.jetty.server.Server;
@ -84,9 +91,9 @@ public class ContextHandlerCollectionTest
c.addHandler(contextC); c.addHandler(contextC);
HandlerList list = new HandlerList(); HandlerList list = new HandlerList();
list.addHandler(contextD);
list.addHandler(contextE); list.addHandler(contextE);
list.addHandler(contextF); list.addHandler(contextF);
list.addHandler(contextD);
c.addHandler(list); c.addHandler(list);
server.setHandler(c); server.setHandler(c);
@ -132,18 +139,22 @@ public class ContextHandlerCollectionTest
handlerE.reset(); handlerE.reset();
handlerF.reset(); handlerF.reset();
// System.err.printf("test %d %s@%s --> %s | %s%n",i,uri,host,connector.getName(),handler); String t = String.format("test %d %s@%s --> %s | %s%n",i,uri,host,connector.getName(),handler);
String response = connector.getResponses("GET "+uri+" HTTP/1.0\nHost: "+host+"\n\n"); String response = connector.getResponses("GET "+uri+" HTTP/1.0\nHost: "+host+"\n\n");
if (handler==null) if (handler==null)
{ {
Assert.assertThat(response,Matchers.containsString(" 302 ")); Assert.assertThat(t,response,Matchers.containsString(" 302 "));
} }
else if (!handler.isHandled()) else
{ {
System.err.printf("FAILED %d %s@%s --> %s | %s%n",i,uri,host,connector.getName(),handler); assertThat(t,response,endsWith(handler.toString()));
System.err.println(response); if (!handler.isHandled())
Assert.fail(); {
System.err.printf("FAILED %s",t);
System.err.println(response);
Assert.fail();
}
} }
} }
@ -261,6 +272,146 @@ public class ContextHandlerCollectionTest
} }
@Test
public void testWrappedContext() throws Exception
{
Server server = new Server();
LocalConnector connector = new LocalConnector(server);
server.setConnectors(new Connector[] { connector });
ContextHandler root = new ContextHandler("/");
root.setHandler(new IsHandledHandler("root"));
ContextHandler left = new ContextHandler("/left");
left.setHandler(new IsHandledHandler("left"));
HandlerList centre = new HandlerList();
ContextHandler centreLeft = new ContextHandler("/leftcentre");
centreLeft.setHandler(new IsHandledHandler("left of centre"));
ContextHandler centreRight = new ContextHandler("/rightcentre");
centreRight.setHandler(new IsHandledHandler("right of centre"));
centre.setHandlers(new Handler[]{centreLeft,new WrappedHandler(centreRight)});
ContextHandler right = new ContextHandler("/right");
right.setHandler(new IsHandledHandler("right"));
ContextHandlerCollection contexts = new ContextHandlerCollection();
contexts.setHandlers(new Handler[]{root,left,centre,new WrappedHandler(right)});
server.setHandler(contexts);
server.start();
String response=connector.getResponses("GET / HTTP/1.0\r\n\r\n");
assertThat(response, startsWith("HTTP/1.1 200 OK"));
assertThat(response, endsWith("root"));
assertThat(response, not(containsString("Wrapped: TRUE")));
response=connector.getResponses("GET /foobar/info HTTP/1.0\r\n\r\n");
assertThat(response, startsWith("HTTP/1.1 200 OK"));
assertThat(response, endsWith("root"));
assertThat(response, not(containsString("Wrapped: TRUE")));
response=connector.getResponses("GET /left/info HTTP/1.0\r\n\r\n");
assertThat(response, startsWith("HTTP/1.1 200 OK"));
assertThat(response, endsWith("left"));
assertThat(response, not(containsString("Wrapped: TRUE")));
response=connector.getResponses("GET /leftcentre/info HTTP/1.0\r\n\r\n");
assertThat(response, startsWith("HTTP/1.1 200 OK"));
assertThat(response, endsWith("left of centre"));
assertThat(response, not(containsString("Wrapped: TRUE")));
response=connector.getResponses("GET /rightcentre/info HTTP/1.0\r\n\r\n");
assertThat(response, startsWith("HTTP/1.1 200 OK"));
assertThat(response, endsWith("right of centre"));
assertThat(response, containsString("Wrapped: TRUE"));
response=connector.getResponses("GET /right/info HTTP/1.0\r\n\r\n");
assertThat(response, startsWith("HTTP/1.1 200 OK"));
assertThat(response, endsWith("right"));
assertThat(response, containsString("Wrapped: TRUE"));
}
@Test
public void testAsyncWrappedContext() throws Exception
{
Server server = new Server();
LocalConnector connector = new LocalConnector(server);
server.setConnectors(new Connector[] { connector });
ContextHandler root = new ContextHandler("/");
root.setHandler(new AsyncHandler("root"));
ContextHandler left = new ContextHandler("/left");
left.setHandler(new AsyncHandler("left"));
HandlerList centre = new HandlerList();
ContextHandler centreLeft = new ContextHandler("/leftcentre");
centreLeft.setHandler(new AsyncHandler("left of centre"));
ContextHandler centreRight = new ContextHandler("/rightcentre");
centreRight.setHandler(new AsyncHandler("right of centre"));
centre.setHandlers(new Handler[]{centreLeft,new WrappedHandler(centreRight)});
ContextHandler right = new ContextHandler("/right");
right.setHandler(new AsyncHandler("right"));
ContextHandlerCollection contexts = new ContextHandlerCollection();
contexts.setHandlers(new Handler[]{root,left,centre,new WrappedHandler(right)});
server.setHandler(contexts);
server.start();
String response=connector.getResponses("GET / HTTP/1.0\r\n\r\n");
assertThat(response, startsWith("HTTP/1.1 200 OK"));
assertThat(response, endsWith("root"));
assertThat(response, not(containsString("Wrapped: TRUE")));
response=connector.getResponses("GET /foobar/info HTTP/1.0\r\n\r\n");
assertThat(response, startsWith("HTTP/1.1 200 OK"));
assertThat(response, endsWith("root"));
assertThat(response, not(containsString("Wrapped: TRUE")));
response=connector.getResponses("GET /left/info HTTP/1.0\r\n\r\n");
assertThat(response, startsWith("HTTP/1.1 200 OK"));
assertThat(response, endsWith("left"));
assertThat(response, not(containsString("Wrapped: TRUE")));
response=connector.getResponses("GET /leftcentre/info HTTP/1.0\r\n\r\n");
assertThat(response, startsWith("HTTP/1.1 200 OK"));
assertThat(response, endsWith("left of centre"));
assertThat(response, not(containsString("Wrapped: TRUE")));
response=connector.getResponses("GET /rightcentre/info HTTP/1.0\r\n\r\n");
assertThat(response, startsWith("HTTP/1.1 200 OK"));
assertThat(response, endsWith("right of centre"));
assertThat(response, containsString("Wrapped: ASYNC"));
response=connector.getResponses("GET /right/info HTTP/1.0\r\n\r\n");
assertThat(response, startsWith("HTTP/1.1 200 OK"));
assertThat(response, endsWith("right"));
assertThat(response, containsString("Wrapped: ASYNC"));
}
private static final class WrappedHandler extends HandlerWrapper
{
WrappedHandler(Handler handler)
{
setHandler(handler);
}
@Override
public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
{
if (response.containsHeader("Wrapped"))
response.setHeader("Wrapped", "ASYNC");
else
response.setHeader("Wrapped", "TRUE");
super.handle(target, baseRequest, request, response);
}
}
private static final class IsHandledHandler extends AbstractHandler private static final class IsHandledHandler extends AbstractHandler
{ {
@ -299,4 +450,40 @@ public class ContextHandlerCollectionTest
private static final class AsyncHandler extends AbstractHandler
{
private final String name;
public AsyncHandler(String string)
{
name=string;
}
@Override
public void handle(String s, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
{
baseRequest.setHandled(true);
String n = (String)baseRequest.getAttribute("async");
if (n==null)
{
AsyncContext async=baseRequest.startAsync();
async.setTimeout(1000);
baseRequest.setAttribute("async", name);
async.dispatch();
}
else
{
response.getWriter().print(n);
}
}
@Override
public String toString()
{
return name;
}
}
} }

View File

@ -252,6 +252,8 @@ public class DecoderReaderTest
server.stop(); server.stop();
} }
// TODO analyse and fix
@Ignore
@Test @Test
public void testSingleQuotes() throws Exception public void testSingleQuotes() throws Exception
{ {
@ -269,6 +271,8 @@ public class DecoderReaderTest
Assert.assertThat("Quotes Count",quotes.quotes.size(),is(3)); Assert.assertThat("Quotes Count",quotes.quotes.size(),is(3));
} }
// TODO analyse and fix
@Ignore
@Test @Test
@Ignore ("Quotes appear to be able to arrive in any order?") @Ignore ("Quotes appear to be able to arrive in any order?")
public void testTwoQuotes() throws Exception public void testTwoQuotes() throws Exception