413154 ContextHandlerCollection defers virtual host handling to ContextHandler

This commit is contained in:
Greg Wilkins 2013-07-22 22:09:42 +10:00
parent 8946b4946b
commit e4ef8ee1f4
8 changed files with 354 additions and 280 deletions

View File

@ -884,8 +884,10 @@ public class ContextHandler extends ScopedHandler implements Attributes, Gracefu
String vhost = normalizeHostname(baseRequest.getServerName());
boolean match = false;
boolean connectorName = false;
boolean connectorMatch = false;
loop: for (String contextVhost:_vhosts)
for (String contextVhost:_vhosts)
{
if (contextVhost == null || contextVhost.length()==0)
continue;
@ -895,21 +897,21 @@ public class ContextHandler extends ScopedHandler implements Attributes, Gracefu
case '*':
if (contextVhost.startsWith("*."))
// wildcard only at the beginning, and only for one additional subdomain level
match = contextVhost.regionMatches(true,2,vhost,vhost.indexOf(".") + 1,contextVhost.length() - 2);
match = match || contextVhost.regionMatches(true,2,vhost,vhost.indexOf(".") + 1,contextVhost.length() - 2);
break;
case '@':
connectorName=true;
String name=baseRequest.getHttpChannel().getConnector().getName();
match=name!=null && contextVhost.length()==name.length()+1 && contextVhost.endsWith(name);
boolean m=name!=null && contextVhost.length()==name.length()+1 && contextVhost.endsWith(name);
match = match || m;
connectorMatch = connectorMatch || m;
break;
default:
match = contextVhost.equalsIgnoreCase(vhost);
match = match || contextVhost.equalsIgnoreCase(vhost);
}
if (match)
break loop;
}
if (!match)
if (!match || connectorName && !connectorMatch)
return false;
}
@ -1287,8 +1289,26 @@ public class ContextHandler extends ScopedHandler implements Attributes, Gracefu
*/
public void setContextPath(String contextPath)
{
if (contextPath != null && contextPath.length() > 1 && contextPath.endsWith("/"))
throw new IllegalArgumentException("ends with /");
if (contextPath == null)
throw new IllegalArgumentException("null contextPath");
if (contextPath.endsWith("/*"))
{
LOG.warn(this+" contextPath ends with /*");
contextPath=contextPath.substring(0,contextPath.length()-2);
}
else if (contextPath.endsWith("/"))
{
LOG.warn(this+" contextPath ends with /");
contextPath=contextPath.substring(0,contextPath.length()-1);
}
if (contextPath.length()==0)
{
LOG.warn("Empty contextPath");
contextPath="/";
}
_contextPath = contextPath;
if (getServer() != null && (getServer().isStarting() || getServer().isStarted()))

View File

@ -19,19 +19,19 @@
package org.eclipse.jetty.server.handler;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.Arrays;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.eclipse.jetty.http.PathMap;
import org.eclipse.jetty.server.Handler;
import org.eclipse.jetty.server.HandlerContainer;
import org.eclipse.jetty.server.HttpChannelState;
import org.eclipse.jetty.server.Request;
import org.eclipse.jetty.util.LazyList;
import org.eclipse.jetty.util.ArrayTernaryTrie;
import org.eclipse.jetty.util.ArrayUtil;
import org.eclipse.jetty.util.Trie;
import org.eclipse.jetty.util.annotation.ManagedObject;
import org.eclipse.jetty.util.annotation.ManagedOperation;
import org.eclipse.jetty.util.log.Log;
@ -53,7 +53,7 @@ public class ContextHandlerCollection extends HandlerCollection
{
private static final Logger LOG = Log.getLogger(ContextHandlerCollection.class);
private volatile PathMap<Object> _contextMap;
private volatile Trie<ContextHandler[]> _contexts;
private Class<? extends ContextHandler> _contextClass = ContextHandler.class;
/* ------------------------------------------------------------ */
@ -70,90 +70,71 @@ public class ContextHandlerCollection extends HandlerCollection
@ManagedOperation("update the mapping of context path to context")
public void mapContexts()
{
PathMap<Object> contextMap = new PathMap<Object>();
Handler[] branches = getHandlers();
for (int b=0;branches!=null && b<branches.length;b++)
int capacity=512;
// Loop until we have a big enough trie to hold all the context paths
Trie<ContextHandler[]> trie;
loop: while(true)
{
Handler[] handlers=null;
trie=new ArrayTernaryTrie<>(false,capacity);
if (branches[b] instanceof ContextHandler)
Handler[] branches = getHandlers();
// loop over each group of contexts
for (int b=0;branches!=null && b<branches.length;b++)
{
handlers = new Handler[]{ branches[b] };
}
else if (branches[b] instanceof HandlerContainer)
{
handlers = ((HandlerContainer)branches[b]).getChildHandlersByClass(ContextHandler.class);
}
else
continue;
Handler[] handlers=null;
for (int i=0;i<handlers.length;i++)
{
ContextHandler handler=(ContextHandler)handlers[i];
String contextPath=handler.getContextPath();
if (contextPath==null || contextPath.indexOf(',')>=0 || contextPath.startsWith("*"))
throw new IllegalArgumentException ("Illegal context spec:"+contextPath);
if(!contextPath.startsWith("/"))
contextPath='/'+contextPath;
if (contextPath.length()>1)
if (branches[b] instanceof ContextHandler)
{
if (contextPath.endsWith("/"))
contextPath+="*";
else if (!contextPath.endsWith("/*"))
contextPath+="/*";
handlers = new Handler[]{ branches[b] };
}
Object contexts=contextMap.get(contextPath);
String[] vhosts=handler.getVirtualHosts();
if (vhosts!=null && vhosts.length>0)
else if (branches[b] instanceof HandlerContainer)
{
Map<String, Object> hosts;
if (contexts instanceof Map)
hosts=(Map<String, Object>)contexts;
else
{
hosts=new HashMap<String, Object>();
hosts.put("*",contexts);
contextMap.put(contextPath, hosts);
}
for (int j=0;j<vhosts.length;j++)
{
String vhost=vhosts[j];
contexts=hosts.get(vhost);
contexts=LazyList.add(contexts,branches[b]);
hosts.put(vhost,contexts);
}
}
else if (contexts instanceof Map)
{
Map<String, Object> hosts=(Map<String, Object>)contexts;
contexts=hosts.get("*");
contexts= LazyList.add(contexts, branches[b]);
hosts.put("*",contexts);
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++)
{
contexts= LazyList.add(contexts, branches[b]);
contextMap.put(contextPath, contexts);
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;
}
// Sort the contexts so those with virtual hosts are considered before those without
for (String ctx : trie.keySet())
{
ContextHandler[] contexts=trie.get(ctx);
ContextHandler[] sorted=new ContextHandler[contexts.length];
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);
}
_contextMap=contextMap;
//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[])
@ -161,7 +142,7 @@ public class ContextHandlerCollection extends HandlerCollection
@Override
public void setHandlers(Handler[] handlers)
{
_contextMap=null;
_contexts=null;
super.setHandlers(handlers);
if (isStarted())
mapContexts();
@ -199,67 +180,31 @@ public class ContextHandlerCollection extends HandlerCollection
}
// data structure which maps a request to a context; first-best match wins
// { context path =>
// { virtual host => context }
// { context path => [ context ] }
// }
PathMap<Object> map = _contextMap;
if (map!=null && target!=null && target.startsWith("/"))
if (target.startsWith("/"))
{
// first, get all contexts matched by context path
Object contexts = map.getLazyMatches(target);
int limit = target.length()-1;
for (int i=0; i<LazyList.size(contexts); i++)
{
// then, match against the virtualhost of each context
Map.Entry entry = (Map.Entry)LazyList.get(contexts, i);
Object list = entry.getValue();
while (limit>=0)
{
// Get best match
ContextHandler[] contexts = _contexts.getBest(target,1,limit);
if (contexts==null)
break;
if (list instanceof Map)
{
Map hosts = (Map)list;
String host = normalizeHostname(request.getServerName());
// explicitly-defined virtual hosts, most specific
list=hosts.get(host);
for (int j=0; j<LazyList.size(list); j++)
{
Handler handler = (Handler)LazyList.get(list,j);
handler.handle(target,baseRequest, request, response);
if (baseRequest.isHandled())
return;
}
// wildcard for one level of names
list=hosts.get("*."+host.substring(host.indexOf(".")+1));
for (int j=0; j<LazyList.size(list); j++)
{
Handler handler = (Handler)LazyList.get(list,j);
handler.handle(target,baseRequest, request, response);
if (baseRequest.isHandled())
return;
}
// no virtualhosts defined for the context, least specific
// will handle any request that does not match to a specific virtual host above
list=hosts.get("*");
for (int j=0; j<LazyList.size(list); j++)
{
Handler handler = (Handler)LazyList.get(list,j);
handler.handle(target,baseRequest, request, response);
if (baseRequest.isHandled())
return;
}
}
else
{
for (int j=0; j<LazyList.size(list); j++)
{
Handler handler = (Handler)LazyList.get(list,j);
handler.handle(target,baseRequest, request, response);
if (baseRequest.isHandled())
return;
}
}
int l=contexts[0].getContextPath().length();
if (l==1 || target.length()==l || target.charAt(l)=='/')
{
for (ContextHandler handler : contexts)
{
handler.handle(target,baseRequest, request, response);
if (baseRequest.isHandled())
return;
}
}
limit=l-2;
}
}
else
@ -320,16 +265,5 @@ public class ContextHandlerCollection extends HandlerCollection
_contextClass = contextClass;
}
/* ------------------------------------------------------------ */
private String normalizeHostname( String host )
{
if ( host == null )
return null;
if ( host.endsWith( "." ) )
return host.substring( 0, host.length() -1);
return host;
}
}

View File

@ -23,6 +23,8 @@ import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import java.io.IOException;
import java.util.Arrays;
import java.util.Collections;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
@ -32,60 +34,121 @@ import org.eclipse.jetty.server.Connector;
import org.eclipse.jetty.server.LocalConnector;
import org.eclipse.jetty.server.Request;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.util.ArrayUtil;
import org.hamcrest.Matchers;
import org.junit.Assert;
import org.junit.Test;
public class ContextHandlerCollectionTest
{
@Test
public void testVirtualHostNormalization() throws Exception
public void testVirtualHosts() throws Exception
{
Server server = new Server();
LocalConnector connector = new LocalConnector(server);
LocalConnector connector0 = new LocalConnector(server);
LocalConnector connector1 = new LocalConnector(server);
connector1.setName("connector1");
server.setConnectors(new Connector[]
{ connector });
{ connector0,connector1});
ContextHandler contextA = new ContextHandler("/");
ContextHandler contextA = new ContextHandler("/ctx");
contextA.setVirtualHosts(new String[]
{ "www.example.com" });
IsHandledHandler handlerA = new IsHandledHandler();
{ "www.example.com", "alias.example.com" });
IsHandledHandler handlerA = new IsHandledHandler("A");
contextA.setHandler(handlerA);
contextA.setAllowNullPathInfo(true);
ContextHandler contextB = new ContextHandler("/");
IsHandledHandler handlerB = new IsHandledHandler();
ContextHandler contextB = new ContextHandler("/ctx");
IsHandledHandler handlerB = new IsHandledHandler("B");
contextB.setHandler(handlerB);
contextB.setVirtualHosts(new String[]
{ "www.example2.com." });
{ "*.other.com" , "@connector1"});
ContextHandler contextC = new ContextHandler("/");
IsHandledHandler handlerC = new IsHandledHandler();
ContextHandler contextC = new ContextHandler("/ctx");
IsHandledHandler handlerC = new IsHandledHandler("C");
contextC.setHandler(handlerC);
ContextHandlerCollection c = new ContextHandlerCollection();
ContextHandler contextD = new ContextHandler("/");
IsHandledHandler handlerD = new IsHandledHandler("D");
contextD.setHandler(handlerD);
ContextHandler contextE = new ContextHandler("/ctx/foo");
IsHandledHandler handlerE = new IsHandledHandler("E");
contextE.setHandler(handlerE);
ContextHandler contextF = new ContextHandler("/ctxlong");
IsHandledHandler handlerF = new IsHandledHandler("F");
contextF.setHandler(handlerF);
ContextHandlerCollection c = new ContextHandlerCollection();
c.addHandler(contextA);
c.addHandler(contextB);
c.addHandler(contextC);
HandlerList list = new HandlerList();
list.addHandler(contextD);
list.addHandler(contextE);
list.addHandler(contextF);
c.addHandler(list);
server.setHandler(c);
try
{
server.start();
connector.getResponses("GET / HTTP/1.0\n" + "Host: www.example.com.\n\n");
Object[][] tests = new Object[][] {
{connector0,"www.example.com.", "/ctx", handlerA},
{connector0,"www.example.com.", "/ctx/", handlerA},
{connector0,"www.example.com.", "/ctx/info", handlerA},
{connector0,"www.example.com", "/ctx/info", handlerA},
{connector0,"alias.example.com", "/ctx/info", handlerA},
{connector1,"www.example.com.", "/ctx/info", handlerA},
{connector1,"www.example.com", "/ctx/info", handlerA},
{connector1,"alias.example.com", "/ctx/info", handlerA},
assertTrue(handlerA.isHandled());
assertFalse(handlerB.isHandled());
assertFalse(handlerC.isHandled());
{connector1,"www.other.com", "/ctx", null},
{connector1,"www.other.com", "/ctx/", handlerB},
{connector1,"www.other.com", "/ctx/info", handlerB},
{connector0,"www.other.com", "/ctx/info", handlerC},
{connector0,"www.example.com", "/ctxinfo", handlerD},
{connector1,"unknown.com", "/unknown", handlerD},
{connector0,"alias.example.com", "/ctx/foo/info", handlerE},
{connector0,"alias.example.com", "/ctxlong/info", handlerF},
};
for (int i=0;i<tests.length;i++)
{
Object[] test=tests[i];
LocalConnector connector = (LocalConnector)test[0];
String host=(String)test[1];
String uri=(String)test[2];
IsHandledHandler handler = (IsHandledHandler)test[3];
handlerA.reset();
handlerB.reset();
handlerC.reset();
handlerA.reset();
handlerB.reset();
handlerC.reset();
handlerD.reset();
handlerE.reset();
handlerF.reset();
connector.getResponses("GET / HTTP/1.0\n" + "Host: www.example2.com\n\n");
assertFalse(handlerA.isHandled());
assertTrue(handlerB.isHandled());
assertFalse(handlerC.isHandled());
// System.err.printf("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");
if (handler==null)
{
Assert.assertThat(response,Matchers.containsString(" 302 "));
}
else if (!handler.isHandled())
{
System.err.printf("FAILED %d %s@%s --> %s | %s%n",i,uri,host,connector.getName(),handler);
System.err.println(response);
Assert.fail();
}
}
}
finally
@ -103,7 +166,7 @@ public class ContextHandlerCollectionTest
ContextHandler context = new ContextHandler("/");
IsHandledHandler handler = new IsHandledHandler();
IsHandledHandler handler = new IsHandledHandler("H");
context.setHandler(handler);
ContextHandlerCollection c = new ContextHandlerCollection();
@ -149,7 +212,10 @@ public class ContextHandlerCollectionTest
for(String host : requestHosts)
{
connector.getResponses("GET / HTTP/1.0\n" + "Host: "+host+"\nConnection:close\n\n");
// System.err.printf("host=%s in %s%n",host,contextHosts==null?Collections.emptyList():Arrays.asList(contextHosts));
String response=connector.getResponses("GET / HTTP/1.0\n" + "Host: "+host+"\nConnection:close\n\n");
// System.err.println(response);
if(succeed)
assertTrue("'"+host+"' should have been handled.",handler.isHandled());
else
@ -166,17 +232,17 @@ public class ContextHandlerCollectionTest
Server server = new Server();
ContextHandler contextA = new ContextHandler("/a");
IsHandledHandler handlerA = new IsHandledHandler();
IsHandledHandler handlerA = new IsHandledHandler("A");
contextA.setHandler(handlerA);
ContextHandler contextB = new ContextHandler("/b");
IsHandledHandler handlerB = new IsHandledHandler();
IsHandledHandler handlerB = new IsHandledHandler("B");
HandlerWrapper wrapperB = new HandlerWrapper();
wrapperB.setHandler(handlerB);
contextB.setHandler(wrapperB);
ContextHandler contextC = new ContextHandler("/c");
IsHandledHandler handlerC = new IsHandledHandler();
IsHandledHandler handlerC = new IsHandledHandler("C");
contextC.setHandler(handlerC);
ContextHandlerCollection collection = new ContextHandlerCollection();
@ -202,22 +268,36 @@ public class ContextHandlerCollectionTest
private static final class IsHandledHandler extends AbstractHandler
{
private boolean handled;
private final String name;
public IsHandledHandler(String string)
{
name=string;
}
public boolean isHandled()
{
return handled;
}
@Override
public void handle(String s, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
{
baseRequest.setHandled(true);
this.handled = true;
response.getWriter().print(name);
}
public void reset()
{
handled = false;
}
@Override
public String toString()
{
return name;
}
}

View File

@ -118,12 +118,10 @@ public class ArrayTernaryTrie<V> extends AbstractTrie<V>
@Override
public boolean put(String s, V v)
{
int last=EQ;
int t=_tree[last];
int k;
int t=0;
int limit = s.length();
int node=0;
for(k=0; k < limit; k++)
int last=0;
for(int k=0; k < limit; k++)
{
char c=s.charAt(k);
if(isCaseInsensitive() && c<128)
@ -131,49 +129,66 @@ public class ArrayTernaryTrie<V> extends AbstractTrie<V>
while (true)
{
if (t==0)
int row=ROW_SIZE*t;
// Do we need to create the new row?
if (t==_rows)
{
node=t=++_rows;
_rows++;
if (_rows>=_key.length)
{
_rows--;
return false;
}
int row=ROW_SIZE*t;
_tree[row]=c;
_tree[last]=(char)t;
last=row+EQ;
}
int row=ROW_SIZE*t;
char n=_tree[row];
int diff=n-c;
if (diff==0)
{
node=t;
t=_tree[last=(row+EQ)];
break;
}
if (diff<0)
else if (diff<0)
t=_tree[last=(row+LO)];
else
t=_tree[last=(row+HI)];
// do we need a new row?
if (t==0)
{
t=_rows;
_tree[last]=(char)t;
}
if (diff==0)
break;
}
}
_key[node]=v==null?null:s;
_value[node] = v;
// Do we need to create the new row?
if (t==_rows)
{
_rows++;
if (_rows>=_key.length)
{
_rows--;
return false;
}
}
// Put the key and value
_key[t]=v==null?null:s;
_value[t] = v;
return true;
}
/* ------------------------------------------------------------ */
@Override
public V get(String s,int offset, int length)
public V get(String s,int offset, int len)
{
int t = _tree[EQ];
int len = length;
int i=0;
while(i<len)
int t = 0;
for(int i=0; i < len;)
{
char c=s.charAt(offset+i++);
if(isCaseInsensitive() && c<128)
@ -187,8 +202,6 @@ public class ArrayTernaryTrie<V> extends AbstractTrie<V>
if (diff==0)
{
if (i==len)
return (V)_value[t];
t=_tree[row+EQ];
if (t==0)
return null;
@ -201,19 +214,17 @@ public class ArrayTernaryTrie<V> extends AbstractTrie<V>
}
}
return null;
return (V)_value[t];
}
@Override
public V get(ByteBuffer b, int offset, int length)
public V get(ByteBuffer b, int offset, int len)
{
int t = _tree[EQ];
int len = length;
int i=0;
int t = 0;
offset+=b.position();
while(i<len)
for(int i=0; i < len;)
{
byte c=(byte)(b.get(offset+i++)&0x7f);
if(isCaseInsensitive())
@ -227,8 +238,6 @@ public class ArrayTernaryTrie<V> extends AbstractTrie<V>
if (diff==0)
{
if (i==len)
return (V)_value[t];
t=_tree[row+EQ];
if (t==0)
return null;
@ -240,35 +249,35 @@ public class ArrayTernaryTrie<V> extends AbstractTrie<V>
return null;
}
}
return null;
return (V)_value[t];
}
/* ------------------------------------------------------------ */
@Override
public V getBest(String s)
{
return getBest(_tree[EQ],s,0,s.length());
return getBest(0,s,0,s.length());
}
/* ------------------------------------------------------------ */
@Override
public V getBest(String s, int offset, int length)
{
return getBest(_tree[EQ],s,offset,length);
return getBest(0,s,offset,length);
}
/* ------------------------------------------------------------ */
private V getBest(int t,String s,int offset,int len)
{
int node=0;
for(int i=0; t!=0 && i<len; i++)
int node=t;
loop: for(int i=0; i<len; i++)
{
char c=s.charAt(offset+i);
if(isCaseInsensitive() && c<128)
c=StringUtil.lowercases[c];
while (t!=0)
while (true)
{
int row = ROW_SIZE*t;
char n=_tree[row];
@ -276,25 +285,27 @@ public class ArrayTernaryTrie<V> extends AbstractTrie<V>
if (diff==0)
{
node=t;
t=_tree[row+EQ];
if (t==0)
break loop;
// if this node is a match, recurse to remember
if (_key[node]!=null)
if (_key[t]!=null)
{
node=t;
V best=getBest(t,s,offset+i+1,len-i-1);
if (best!=null)
return best;
return (V)_value[node];
}
break;
}
t=_tree[row+hilo(diff)];
if (t==0)
break loop;
}
}
return null;
return (V)_value[node];
}
@ -303,21 +314,21 @@ public class ArrayTernaryTrie<V> extends AbstractTrie<V>
public V getBest(ByteBuffer b, int offset, int len)
{
if (b.hasArray())
return getBest(_tree[EQ],b.array(),b.arrayOffset()+b.position()+offset,len);
return getBest(_tree[EQ],b,offset,len);
return getBest(0,b.array(),b.arrayOffset()+b.position()+offset,len);
return getBest(0,b,offset,len);
}
/* ------------------------------------------------------------ */
private V getBest(int t,byte[] b, int offset, int len)
{
int node=0;
for(int i=0; t!=0 && i<len; i++)
int node=t;
loop: for(int i=0; i<len; i++)
{
byte c=(byte)(b[offset+i]&0x7f);
if(isCaseInsensitive())
c=(byte)StringUtil.lowercases[c];
while (t!=0)
while (true)
{
int row = ROW_SIZE*t;
char n=_tree[row];
@ -325,40 +336,42 @@ public class ArrayTernaryTrie<V> extends AbstractTrie<V>
if (diff==0)
{
node=t;
t=_tree[row+EQ];
if (t==0)
break loop;
// if this node is a match, recurse to remember
if (_key[node]!=null)
if (_key[t]!=null)
{
node=t;
V best=getBest(t,b,offset+i+1,len-i-1);
if (best!=null)
return best;
return (V)_value[node];
}
break;
}
t=_tree[row+hilo(diff)];
if (t==0)
break loop;
}
}
return null;
return (V)_value[node];
}
/* ------------------------------------------------------------ */
private V getBest(int t,ByteBuffer b, int offset, int len)
{
int node=0;
int node=t;
int o= offset+b.position();
for(int i=0; t!=0 && i<len; i++)
loop: for(int i=0; i<len; i++)
{
byte c=(byte)(b.get(o+i)&0x7f);
if(isCaseInsensitive())
c=(byte)StringUtil.lowercases[c];
while (t!=0)
while (true)
{
int row = ROW_SIZE*t;
char n=_tree[row];
@ -366,32 +379,34 @@ public class ArrayTernaryTrie<V> extends AbstractTrie<V>
if (diff==0)
{
node=t;
t=_tree[row+EQ];
if (t==0)
break loop;
// if this node is a match, recurse to remember
if (_key[node]!=null)
if (_key[t]!=null)
{
node=t;
V best=getBest(t,b,offset+i+1,len-i-1);
if (best!=null)
return best;
return (V)_value[node];
}
break;
}
t=_tree[row+hilo(diff)];
if (t==0)
break loop;
}
}
return null;
return (V)_value[node];
}
@Override
public String toString()
{
StringBuilder buf = new StringBuilder();
for (int r=1;r<=_rows;r++)
for (int r=0;r<=_rows;r++)
{
if (_key[r]!=null && _value[r]!=null)
{
@ -416,7 +431,7 @@ public class ArrayTernaryTrie<V> extends AbstractTrie<V>
{
Set<String> keys = new HashSet<>();
for (int r=1;r<=_rows;r++)
for (int r=0;r<=_rows;r++)
{
if (_key[r]!=null && _value[r]!=null)
keys.add(_key[r]);
@ -439,10 +454,10 @@ public class ArrayTernaryTrie<V> extends AbstractTrie<V>
public void dump()
{
for (int r=0;r<=_rows;r++)
for (int r=0;r<_rows;r++)
{
char c=_tree[r*ROW_SIZE+0];
System.err.printf("%4d [%s,%d,%d,%d] %s:%s%n",
System.err.printf("%4d [%s,%d,%d,%d] '%s':%s%n",
r,
(c<' '||c>127)?(""+(int)c):"'"+c+"'",
(int)_tree[r*ROW_SIZE+LO],

View File

@ -249,18 +249,20 @@ public class ArrayTrie<V> extends AbstractTrie<V>
if (index>=0)
{
int idx=t*ROW_SIZE+index;
t=_rowIndex[idx];
if (t==0)
return null;
int nt=_rowIndex[idx];
if (nt==0)
break;
t=nt;
}
else
{
char[] big = _bigIndex==null?null:_bigIndex[t];
if (big==null)
return null;
t=big[c];
if (t==0)
return null;
int nt=big[c];
if (nt==0)
break;
t=nt;
}
// Is the next Trie is a match
@ -286,18 +288,20 @@ public class ArrayTrie<V> extends AbstractTrie<V>
if (index>=0)
{
int idx=t*ROW_SIZE+index;
t=_rowIndex[idx];
if (t==0)
return null;
int nt=_rowIndex[idx];
if (nt==0)
break;
t=nt;
}
else
{
char[] big = _bigIndex==null?null:_bigIndex[t];
if (big==null)
return null;
t=big[c];
if (t==0)
return null;
int nt=big[c];
if (nt==0)
break;
t=nt;
}
// Is the next Trie is a match
@ -307,7 +311,7 @@ public class ArrayTrie<V> extends AbstractTrie<V>
V best=getBest(t,b,offset+i+1,len-i-1);
if (best!=null)
return best;
return (V)_value[t];
break;
}
}
return (V)_value[t];
@ -323,18 +327,20 @@ public class ArrayTrie<V> extends AbstractTrie<V>
if (index>=0)
{
int idx=t*ROW_SIZE+index;
t=_rowIndex[idx];
if (t==0)
return null;
int nt=_rowIndex[idx];
if (nt==0)
break;
t=nt;
}
else
{
char[] big = _bigIndex==null?null:_bigIndex[t];
if (big==null)
return null;
t=big[c];
if (t==0)
return null;
int nt=big[c];
if (nt==0)
break;
t=nt;
}
// Is the next Trie is a match
@ -344,7 +350,7 @@ public class ArrayTrie<V> extends AbstractTrie<V>
V best=getBest(t,b,offset+i+1,len-i-1);
if (best!=null)
return best;
return (V)_value[t];
break;
}
}
return (V)_value[t];

View File

@ -70,9 +70,8 @@ public class TreeTrie<V> extends AbstractTrie<V>
public boolean put(String s, V v)
{
TreeTrie<V> t = this;
int k;
int limit = s.length();
for(k=0; k < limit; k++)
for(int k=0; k < limit; k++)
{
char c=s.charAt(k);
@ -102,7 +101,6 @@ public class TreeTrie<V> extends AbstractTrie<V>
}
}
t._key=v==null?null:s;
V old=t._value;
t._value = v;
return true;
}
@ -182,7 +180,7 @@ public class TreeTrie<V> extends AbstractTrie<V>
if (index>=0)
{
if (t._nextIndex[index] == null)
return null;
break;
t = t._nextIndex[index];
}
else
@ -196,7 +194,7 @@ public class TreeTrie<V> extends AbstractTrie<V>
n=null;
}
if (n==null)
return null;
break;
t=n;
}
@ -207,7 +205,7 @@ public class TreeTrie<V> extends AbstractTrie<V>
V best=t.getBest(b,offset+i+1,len-i-1);
if (best!=null)
return best;
return t._value;
break;
}
}
return t._value;
@ -240,7 +238,7 @@ public class TreeTrie<V> extends AbstractTrie<V>
if (index>=0)
{
if (t._nextIndex[index] == null)
return null;
break;
t = t._nextIndex[index];
}
else
@ -254,7 +252,7 @@ public class TreeTrie<V> extends AbstractTrie<V>
n=null;
}
if (n==null)
return null;
break;
t=n;
}
@ -265,7 +263,7 @@ public class TreeTrie<V> extends AbstractTrie<V>
V best=t.getBest(b,offset+i+1,len-i-1);
if (best!=null)
return best;
return t._value;
break;
}
}
return t._value;

View File

@ -23,6 +23,7 @@ import java.nio.ByteBuffer;
import java.util.Arrays;
import java.util.Collection;
import org.hamcrest.Matchers;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
@ -62,6 +63,21 @@ public class TrieTest
trie.put("foo-bar",6);
trie.put("foo+bar",7);
trie.put("HELL4",8);
trie.put("",9);
}
@Test
public void testKeySet() throws Exception
{
Assert.assertTrue(trie.keySet().contains("hello"));
Assert.assertTrue(trie.keySet().contains("He"));
Assert.assertTrue(trie.keySet().contains("HELL"));
Assert.assertTrue(trie.keySet().contains("wibble"));
Assert.assertTrue(trie.keySet().contains("Wobble"));
Assert.assertTrue(trie.keySet().contains("foo-bar"));
Assert.assertTrue(trie.keySet().contains("foo+bar"));
Assert.assertTrue(trie.keySet().contains("HELL4"));
Assert.assertTrue(trie.keySet().contains(""));
}
@Test
@ -82,6 +98,8 @@ public class TrieTest
Assert.assertEquals(5,trie.get("wobble").intValue());
Assert.assertEquals(6,trie.get("Foo-bar").intValue());
Assert.assertEquals(7,trie.get("FOO+bar").intValue());
Assert.assertEquals(8,trie.get("HELL4").intValue());
Assert.assertEquals(9,trie.get("").intValue());
Assert.assertEquals(null,trie.get("helloworld"));
Assert.assertEquals(null,trie.get("Help"));
@ -151,6 +169,7 @@ public class TrieTest
Assert.assertEquals(3,trie.getBest(StringUtil.getUtf8Bytes("xHELLxxxxx"),1,8).intValue());
Assert.assertEquals(6,trie.getBest(StringUtil.getUtf8Bytes("xfoo-BARxx"),1,8).intValue());
Assert.assertEquals(8,trie.getBest(StringUtil.getUtf8Bytes("xHELL4xxxx"),1,8).intValue());
Assert.assertEquals(9,trie.getBest(StringUtil.getUtf8Bytes("xZZZZZxxxx"),1,8).intValue());
}
@Test
@ -167,6 +186,7 @@ public class TrieTest
Assert.assertEquals(3,trie.getBest(BufferUtil.toBuffer("xHELLxxxxx"),1,8).intValue());
Assert.assertEquals(6,trie.getBest(BufferUtil.toBuffer("xfoo-BARxx"),1,8).intValue());
Assert.assertEquals(8,trie.getBest(BufferUtil.toBuffer("xHELL4xxxx"),1,8).intValue());
Assert.assertEquals(9,trie.getBest(BufferUtil.toBuffer("xZZZZZxxxx"),1,8).intValue());
ByteBuffer buffer = (ByteBuffer)BufferUtil.toBuffer("xhelloxxxxxxx").position(2);
Assert.assertEquals(1,trie.getBest(buffer,-1,10).intValue());
@ -186,6 +206,7 @@ public class TrieTest
Assert.assertEquals(3,trie.getBest(BufferUtil.toDirectBuffer("xHELLxxxxx"),1,8).intValue());
Assert.assertEquals(6,trie.getBest(BufferUtil.toDirectBuffer("xfoo-BARxx"),1,8).intValue());
Assert.assertEquals(8,trie.getBest(BufferUtil.toDirectBuffer("xHELL4xxxx"),1,8).intValue());
Assert.assertEquals(9,trie.getBest(BufferUtil.toDirectBuffer("xZZZZZxxxx"),1,8).intValue());
ByteBuffer buffer = (ByteBuffer)BufferUtil.toDirectBuffer("xhelloxxxxxxx").position(2);
Assert.assertEquals(1,trie.getBest(buffer,-1,10).intValue());

View File

@ -57,12 +57,12 @@ public abstract class AbstractNewSessionTest
@Test
public void testNewSession() throws Exception
{
String contextPath = "";
String servletMapping = "/server";
int scavengePeriod = 3;
AbstractTestServer server = createServer(0, 1, scavengePeriod);
ServletContextHandler context = server.addContext(contextPath);
ServletContextHandler context = server.addContext("/");
context.addServlet(TestServlet.class, servletMapping);
String contextPath = "";
try
{