jetty-9 Added Trie for cached string lookup. HttpFields does not use StringMap

This commit is contained in:
Greg Wilkins 2012-12-07 12:24:48 +11:00
parent dc2850c898
commit a20d984d30
19 changed files with 1009 additions and 481 deletions

View File

@ -46,6 +46,7 @@ import org.eclipse.jetty.client.api.Destination;
import org.eclipse.jetty.client.api.ProxyConfiguration; import org.eclipse.jetty.client.api.ProxyConfiguration;
import org.eclipse.jetty.client.api.Request; import org.eclipse.jetty.client.api.Request;
import org.eclipse.jetty.client.api.Response; import org.eclipse.jetty.client.api.Response;
import org.eclipse.jetty.http.HttpField;
import org.eclipse.jetty.http.HttpFields; import org.eclipse.jetty.http.HttpFields;
import org.eclipse.jetty.http.HttpHeader; import org.eclipse.jetty.http.HttpHeader;
import org.eclipse.jetty.http.HttpMethod; import org.eclipse.jetty.http.HttpMethod;
@ -350,7 +351,7 @@ public class HttpClient extends ContainerLifeCycle
newRequest.method(oldRequest.getMethod()) newRequest.method(oldRequest.getMethod())
.version(oldRequest.getVersion()) .version(oldRequest.getVersion())
.content(oldRequest.getContent()); .content(oldRequest.getContent());
for (HttpFields.Field header : oldRequest.getHeaders()) for (HttpField header : oldRequest.getHeaders())
{ {
// We have a new URI, so skip the host header if present // We have a new URI, so skip the host header if present
if (HttpHeader.HOST == header.getHeader()) if (HttpHeader.HOST == header.getHeader())

View File

@ -0,0 +1,217 @@
//
// ========================================================================
// Copyright (c) 1995-2012 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.http;
import java.nio.ByteBuffer;
import org.eclipse.jetty.util.BufferUtil;
import org.eclipse.jetty.util.StringUtil;
import org.eclipse.jetty.util.Trie;
/* ------------------------------------------------------------ */
/** A HTTP Field
*/
public class HttpField
{
public final static Trie<HttpField> CACHE = new Trie<>();
static
{
CACHE.put(new HttpField(HttpHeader.CONNECTION,HttpHeaderValue.CLOSE));
CACHE.put(new HttpField(HttpHeader.CONNECTION,HttpHeaderValue.KEEP_ALIVE));
CACHE.put(new HttpField(HttpHeader.ACCEPT_ENCODING,"gzip, deflate"));
CACHE.put(new HttpField(HttpHeader.ACCEPT_LANGUAGE,"en-US,en;q=0.5"));
CACHE.put(new HttpField(HttpHeader.ACCEPT,"text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8"));
CACHE.put(new HttpField(HttpHeader.PRAGMA,"no-cache"));
CACHE.put(new HttpField(HttpHeader.CACHE_CONTROL,"private, no-cache, no-cache=Set-Cookie, proxy-revalidate"));
CACHE.put(new HttpField(HttpHeader.CACHE_CONTROL,"no-cache"));
CACHE.put(new HttpField(HttpHeader.CONTENT_LENGTH,"0"));
CACHE.put(new HttpField(HttpHeader.CONTENT_TYPE,"text/plain; charset=utf-8"));
CACHE.put(new HttpField(HttpHeader.CONTENT_TYPE,"application/x-www-form-urlencoded; charset=UTF-8"));
CACHE.put(new HttpField(HttpHeader.CONTENT_ENCODING,"gzip"));
CACHE.put(new HttpField(HttpHeader.CONTENT_ENCODING,"deflate"));
CACHE.put(new HttpField(HttpHeader.EXPIRES,"Fri, 01 Jan 1990 00:00:00 GMT"));
// Add headers with null values so HttpParser can avoid looking up name again for unknown values
CACHE.put(new HttpField(HttpHeader.CONNECTION,(String)null));
CACHE.put(new HttpField(HttpHeader.ACCEPT_ENCODING,(String)null));
CACHE.put(new HttpField(HttpHeader.ACCEPT_LANGUAGE,(String)null));
CACHE.put(new HttpField(HttpHeader.ACCEPT,(String)null));
CACHE.put(new HttpField(HttpHeader.PRAGMA,(String)null));
CACHE.put(new HttpField(HttpHeader.CONTENT_TYPE,(String)null));
CACHE.put(new HttpField(HttpHeader.CONTENT_LENGTH,(String)null));
CACHE.put(new HttpField(HttpHeader.CONTENT_ENCODING,(String)null));
CACHE.put(new HttpField(HttpHeader.EXPIRES,(String)null));
// TODO add common user agents
}
private final static byte[] __colon_space = new byte[] {':',' '};
private final HttpHeader _header;
private final String _name;
private final String _value;
public HttpField(HttpHeader header, String name, String value)
{
_header = header;
_name = name;
_value = value;
}
public HttpField(HttpHeader header, String value)
{
_header = header;
_name = _header.asString();
_value = value;
}
public HttpField(HttpHeader header, HttpHeaderValue value)
{
_header = header;
_name = _header.asString();
_value = value.asString();
}
public HttpField(String name, String value)
{
_header = HttpHeader.CACHE.get(name);
_name = name;
_value = value;
}
public HttpHeader getHeader()
{
return _header;
}
public String getName()
{
return _name;
}
public String getValue()
{
return _value;
}
public boolean contains(String value)
{
if (_value==null)
return false;
if (value.equalsIgnoreCase(_value))
return true;
String[] split = _value.split("\\s*,\\s*");
for (String s : split)
{
if (value.equalsIgnoreCase(s))
return true;
}
return false;
}
public int getIntValue()
{
return StringUtil.toInt(_value);
}
public long getLongValue()
{
return StringUtil.toLong(_value);
}
private byte[] toSanitisedName(String s)
{
byte[] bytes = s.getBytes(StringUtil.__ISO_8859_1_CHARSET);
for (int i=bytes.length;i-->0;)
{
switch(bytes[i])
{
case '\r':
case '\n':
case ':' :
bytes[i]=(byte)'?';
}
}
return bytes;
}
private byte[] toSanitisedValue(String s)
{
byte[] bytes = s.getBytes(StringUtil.__ISO_8859_1_CHARSET);
for (int i=bytes.length;i-->0;)
{
switch(bytes[i])
{
case '\r':
case '\n':
bytes[i]=(byte)'?';
}
}
return bytes;
}
public void putTo(ByteBuffer bufferInFillMode)
{
HttpHeader header = HttpHeader.CACHE.get(_name);
if (header!=null)
{
bufferInFillMode.put(header.getBytesColonSpace());
if (HttpHeaderValue.hasKnownValues(header))
{
HttpHeaderValue value=HttpHeaderValue.CACHE.get(_value);
if (value!=null)
bufferInFillMode.put(value.toBuffer());
else
bufferInFillMode.put(toSanitisedValue(_value));
}
else
bufferInFillMode.put(toSanitisedValue(_value));
}
else
{
bufferInFillMode.put(toSanitisedName(_name));
bufferInFillMode.put(__colon_space);
bufferInFillMode.put(toSanitisedValue(_value));
}
BufferUtil.putCRLF(bufferInFillMode);
}
public void putValueTo(ByteBuffer buffer)
{
buffer.put(toSanitisedValue(_value));
}
@Override
public String toString()
{
String v=getValue();
return getName() + ": " + (v==null?"":v.toString());
}
}

View File

@ -29,11 +29,13 @@ import java.util.Date;
import java.util.Enumeration; import java.util.Enumeration;
import java.util.GregorianCalendar; import java.util.GregorianCalendar;
import java.util.HashMap; import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator; import java.util.Iterator;
import java.util.List; import java.util.List;
import java.util.Locale; import java.util.Locale;
import java.util.Map; import java.util.Map;
import java.util.NoSuchElementException; import java.util.NoSuchElementException;
import java.util.Set;
import java.util.StringTokenizer; import java.util.StringTokenizer;
import java.util.TimeZone; import java.util.TimeZone;
@ -46,7 +48,6 @@ import org.eclipse.jetty.util.StringUtil;
import org.eclipse.jetty.util.log.Log; import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger; import org.eclipse.jetty.util.log.Logger;
// TODO Make this class inherit from oej.util.Fields
/** /**
* HTTP Fields. A collection of HTTP header and or Trailer fields. * HTTP Fields. A collection of HTTP header and or Trailer fields.
@ -55,7 +56,7 @@ import org.eclipse.jetty.util.log.Logger;
* single thread. * single thread.
* *
*/ */
public class HttpFields implements Iterable<HttpFields.Field> public class HttpFields implements Iterable<HttpField>
{ {
private static final Logger LOG = Log.getLogger(HttpFields.class); private static final Logger LOG = Log.getLogger(HttpFields.class);
public static final String __COOKIE_DELIM="\"\\\n\r\t\f\b%+ ;="; public static final String __COOKIE_DELIM="\"\\\n\r\t\f\b%+ ;=";
@ -274,9 +275,7 @@ public class HttpFields implements Iterable<HttpFields.Field>
public final static String __01Jan1970=formatDate(0); public final static String __01Jan1970=formatDate(0);
public final static ByteBuffer __01Jan1970_BUFFER=BufferUtil.toBuffer(__01Jan1970); public final static ByteBuffer __01Jan1970_BUFFER=BufferUtil.toBuffer(__01Jan1970);
public final static String __01Jan1970_COOKIE = formatCookieDate(0).trim(); public final static String __01Jan1970_COOKIE = formatCookieDate(0).trim();
private final static byte[] __colon_space = new byte[] {':',' '}; private final ArrayList<HttpField> _fields = new ArrayList<>(20);
private final ArrayList<Field> _fields = new ArrayList<>(20);
private final StringMap<Field> _names = new StringMap<>(true);
/** /**
* Constructor. * Constructor.
@ -291,11 +290,11 @@ public class HttpFields implements Iterable<HttpFields.Field>
*/ */
public Collection<String> getFieldNamesCollection() public Collection<String> getFieldNamesCollection()
{ {
final List<String> list = new ArrayList<>(_fields.size()); final Set<String> list = new HashSet<>(_fields.size());
for (Field f : _fields) for (HttpField f : _fields)
{ {
if (f!=null) if (f!=null)
list.add(f._name); list.add(f.getName());
} }
return list; return list;
} }
@ -306,21 +305,7 @@ public class HttpFields implements Iterable<HttpFields.Field>
*/ */
public Enumeration<String> getFieldNames() public Enumeration<String> getFieldNames()
{ {
final Enumeration<?> buffers = Collections.enumeration(_names.keySet()); return Collections.enumeration(getFieldNamesCollection());
return new Enumeration<String>()
{
@Override
public String nextElement()
{
return buffers.nextElement().toString();
}
@Override
public boolean hasMoreElements()
{
return buffers.hasMoreElements();
}
};
} }
public int size() public int size()
@ -333,30 +318,70 @@ public class HttpFields implements Iterable<HttpFields.Field>
* @return A Field value or null if the Field value has not been set * @return A Field value or null if the Field value has not been set
* *
*/ */
public Field getField(int i) public HttpField getField(int i)
{ {
return _fields.get(i); return _fields.get(i);
} }
@Override @Override
public Iterator<Field> iterator() public Iterator<HttpField> iterator()
{ {
return _fields.iterator(); return _fields.iterator();
} }
public Field getField(HttpHeader header) public HttpField getField(HttpHeader header)
{ {
return _names.get(header.toString()); for (int i=0;i<_fields.size();i++)
{
HttpField f=_fields.get(i);
if (f.getHeader()==header)
return f;
}
return null;
} }
public Field getField(String name) public HttpField getField(String name)
{ {
return _names.get(name); for (int i=0;i<_fields.size();i++)
{
HttpField f=_fields.get(i);
if (f.getName().equalsIgnoreCase(name))
return f;
}
return null;
} }
public boolean contains(HttpHeader header, String value)
{
for (int i=0;i<_fields.size();i++)
{
HttpField f=_fields.get(i);
if (f.getHeader()==header && f.contains(value))
return true;
}
return false;
}
public boolean contains(String name, String value)
{
for (int i=0;i<_fields.size();i++)
{
HttpField f=_fields.get(i);
if (f.getName().equalsIgnoreCase(name) && f.contains(value))
return true;
}
return false;
}
public boolean containsKey(String name) public boolean containsKey(String name)
{ {
return _names.containsKey(name); for (int i=0;i<_fields.size();i++)
{
HttpField f=_fields.get(i);
if (f.getName().equalsIgnoreCase(name))
return true;
}
return false;
} }
public String getStringField(HttpHeader header) public String getStringField(HttpHeader header)
@ -381,7 +406,7 @@ public class HttpFields implements Iterable<HttpFields.Field>
*/ */
public String getStringField(String name) public String getStringField(String name)
{ {
Field field = getField(name); HttpField field = getField(name);
return field==null?null:field.getValue(); return field==null?null:field.getValue();
} }
@ -394,17 +419,10 @@ public class HttpFields implements Iterable<HttpFields.Field>
*/ */
public Collection<String> getValuesCollection(String name) public Collection<String> getValuesCollection(String name)
{ {
Field field = getField(name);
if (field==null)
return null;
final List<String> list = new ArrayList<>(); final List<String> list = new ArrayList<>();
for (HttpField f : _fields)
while(field!=null) if (f.getName().equalsIgnoreCase(name))
{ list.add(f.getValue());
list.add(field.getValue());
field=field._next;
}
return list; return list;
} }
@ -414,34 +432,56 @@ public class HttpFields implements Iterable<HttpFields.Field>
* @return Enumeration of the values * @return Enumeration of the values
* @param name the case-insensitive field name * @param name the case-insensitive field name
*/ */
public Enumeration<String> getValues(String name) public Enumeration<String> getValues(final String name)
{ {
final Field field = getField(name); for (int i=0;i<_fields.size();i++)
if (field == null)
{ {
List<String> empty=Collections.emptyList(); final HttpField f = _fields.get(i);
return Collections.enumeration(empty);
if (f.getName().equalsIgnoreCase(name))
{
final int first=i;
return new Enumeration<String>()
{
HttpField field=f;
int i = first+1;
@Override
public boolean hasMoreElements()
{
if (field==null)
{
while (i<_fields.size())
{
field=_fields.get(i++);
if (field.getName().equalsIgnoreCase(name))
return true;
}
field=null;
return false;
}
return true;
}
@Override
public String nextElement() throws NoSuchElementException
{
if (hasMoreElements())
{
String value=field.getValue();
field=null;
return value;
}
throw new NoSuchElementException();
}
};
}
} }
return new Enumeration<String>() List<String> empty=Collections.emptyList();
{ return Collections.enumeration(empty);
Field f = field;
@Override
public boolean hasMoreElements()
{
return f != null;
}
@Override
public String nextElement() throws NoSuchElementException
{
if (f == null) throw new NoSuchElementException();
Field n = f;
f = f._next;
return n.getValue();
}
};
} }
/** /**
@ -459,7 +499,7 @@ public class HttpFields implements Iterable<HttpFields.Field>
if (e == null) if (e == null)
return null; return null;
return new Enumeration<String>() return new Enumeration<String>()
{ {
QuotedStringTokenizer tok = null; QuotedStringTokenizer tok = null;
@Override @Override
@ -484,7 +524,7 @@ public class HttpFields implements Iterable<HttpFields.Field>
if (next != null) next = next.trim(); if (next != null) next = next.trim();
return next; return next;
} }
}; };
} }
@ -501,9 +541,8 @@ public class HttpFields implements Iterable<HttpFields.Field>
return; return;
// new value; // new value;
Field field = new Field(name, value); HttpField field = new HttpField(name, value);
_fields.add(field); _fields.add(field);
_names.put(name, field);
} }
public void put(HttpHeader header, HttpHeaderValue value) public void put(HttpHeader header, HttpHeaderValue value)
@ -519,14 +558,13 @@ public class HttpFields implements Iterable<HttpFields.Field>
*/ */
public void put(HttpHeader header, String value) public void put(HttpHeader header, String value)
{ {
remove(header.toString()); remove(header);
if (value == null) if (value == null)
return; return;
// new value; // new value;
Field field = new Field(header, value); HttpField field = new HttpField(header, value);
_fields.add(field); _fields.add(field);
_names.put(header.toString(), field);
} }
/** /**
@ -557,23 +595,8 @@ public class HttpFields implements Iterable<HttpFields.Field>
if (value == null) if (value == null)
return; return;
Field field = _names.get(name); HttpField field = new HttpField(name, value);
Field last = null;
while (field != null)
{
last = field;
field = field._next;
}
// create the field
field = new Field(name, value);
_fields.add(field); _fields.add(field);
// look for chain to add too
if (last != null)
last._next = field;
else
_names.put(name, field);
} }
public void add(HttpHeader header, HttpHeaderValue value) throws IllegalArgumentException public void add(HttpHeader header, HttpHeaderValue value) throws IllegalArgumentException
@ -587,30 +610,14 @@ public class HttpFields implements Iterable<HttpFields.Field>
* *
* @param header the header * @param header the header
* @param value the value of the field. * @param value the value of the field.
* @exception IllegalArgumentException If the name is a single valued field and already has a * @exception IllegalArgumentException
* value.
*/ */
public void add(HttpHeader header, String value) throws IllegalArgumentException public void add(HttpHeader header, String value) throws IllegalArgumentException
{ {
if (value == null) throw new IllegalArgumentException("null value"); if (value == null) throw new IllegalArgumentException("null value");
Field field = _names.get(header.toString()); HttpField field = new HttpField(header, value);
Field last = null;
while (field != null)
{
last = field;
field = field._next;
}
// create the field
field = new Field(header, value);
_fields.add(field); _fields.add(field);
// look for chain to add too
if (last != null)
last._next = field;
else
_names.put(header.toString(), field);
} }
/** /**
@ -620,7 +627,13 @@ public class HttpFields implements Iterable<HttpFields.Field>
*/ */
public void remove(HttpHeader name) public void remove(HttpHeader name)
{ {
remove(name.toString()); Iterator<HttpField> i=_fields.iterator();
while (i.hasNext())
{
HttpField f=i.next();
if (f.getHeader()==name)
i.remove();
}
} }
/** /**
@ -630,11 +643,12 @@ public class HttpFields implements Iterable<HttpFields.Field>
*/ */
public void remove(String name) public void remove(String name)
{ {
Field field = _names.remove(name); Iterator<HttpField> i=_fields.iterator();
while (field != null) while (i.hasNext())
{ {
_fields.remove(field); HttpField f=i.next();
field = field._next; if (f.getName().equalsIgnoreCase(name))
i.remove();
} }
} }
@ -647,7 +661,7 @@ public class HttpFields implements Iterable<HttpFields.Field>
*/ */
public long getLongField(String name) throws NumberFormatException public long getLongField(String name) throws NumberFormatException
{ {
Field field = getField(name); HttpField field = getField(name);
return field==null?-1L:field.getLongValue(); return field==null?-1L:field.getLongValue();
} }
@ -659,11 +673,11 @@ public class HttpFields implements Iterable<HttpFields.Field>
*/ */
public long getDateField(String name) public long getDateField(String name)
{ {
Field field = getField(name); HttpField field = getField(name);
if (field == null) if (field == null)
return -1; return -1;
String val = valueParameters(field._value, null); String val = valueParameters(field.getValue(), null);
if (val == null) if (val == null)
return -1; return -1;
@ -840,29 +854,27 @@ public class HttpFields implements Iterable<HttpFields.Field>
name_value_params = buf.toString(); name_value_params = buf.toString();
// remove existing set-cookie of same name // remove existing set-cookie of same name
Field field = getField(HttpHeader.SET_COOKIE);
Field last=null;
while (field!=null)
{
String val = (field._value == null ? null : field._value.toString());
if (val!=null && val.startsWith(start))
{
//existing cookie has same name, does it also match domain and path?
if (((!hasDomain && !val.contains("Domain")) || (hasDomain && val.contains("Domain="+domain))) &&
((!hasPath && !val.contains("Path")) || (hasPath && val.contains("Path="+path))))
{
_fields.remove(field);
if (last==null)
_names.put(HttpHeader.SET_COOKIE.toString(),field._next);
else
last._next=field._next;
break;
}
}
last=field;
field=field._next;
}
Iterator<HttpField> i=_fields.iterator();
while (i.hasNext())
{
HttpField field=i.next();
if (field.getHeader()==HttpHeader.SET_COOKIE)
{
String val = (field.getValue() == null ? null : field.getValue().toString());
if (val!=null && val.startsWith(start))
{
//existing cookie has same name, does it also match domain and path?
if (((!hasDomain && !val.contains("Domain")) || (hasDomain && val.contains("Domain="+domain))) &&
((!hasPath && !val.contains("Path")) || (hasPath && val.contains("Path="+path))))
{
i.remove();
}
}
}
}
add(HttpHeader.SET_COOKIE.toString(), name_value_params); add(HttpHeader.SET_COOKIE.toString(), name_value_params);
// Expire responses with set-cookie headers so they do not get cached. // Expire responses with set-cookie headers so they do not get cached.
@ -871,7 +883,7 @@ public class HttpFields implements Iterable<HttpFields.Field>
public void putTo(ByteBuffer bufferInFillMode) throws IOException public void putTo(ByteBuffer bufferInFillMode) throws IOException
{ {
for (Field field : _fields) for (HttpField field : _fields)
{ {
if (field != null) if (field != null)
field.putTo(bufferInFillMode); field.putTo(bufferInFillMode);
@ -886,7 +898,7 @@ public class HttpFields implements Iterable<HttpFields.Field>
try try
{ {
StringBuilder buffer = new StringBuilder(); StringBuilder buffer = new StringBuilder();
for (Field field : _fields) for (HttpField field : _fields)
{ {
if (field != null) if (field != null)
{ {
@ -914,7 +926,6 @@ public class HttpFields implements Iterable<HttpFields.Field>
public void clear() public void clear()
{ {
_fields.clear(); _fields.clear();
_names.clear();
} }
/** /**
@ -1088,151 +1099,5 @@ public class HttpFields implements Iterable<HttpFields.Field>
return vl; return vl;
} }
public static final class Field
{
private final HttpHeader _header;
private final String _name;
private final String _value;
private Field _next;
private Field(HttpHeader header, String value)
{
_header = header;
_name = header.toString();
_value = value;
_next = null;
}
private Field(String name, String value)
{
_header = HttpHeader.CACHE.get(name);
_name = _header==null?name:_header.toString();
_value = value;
_next = null;
}
private byte[] toSanitisedName(String s)
{
byte[] bytes = s.getBytes(StringUtil.__ISO_8859_1_CHARSET);
for (int i=bytes.length;i-->0;)
{
switch(bytes[i])
{
case '\r':
case '\n':
case ':' :
bytes[i]=(byte)'?';
}
}
return bytes;
}
private byte[] toSanitisedValue(String s)
{
byte[] bytes = s.getBytes(StringUtil.__ISO_8859_1_CHARSET);
for (int i=bytes.length;i-->0;)
{
switch(bytes[i])
{
case '\r':
case '\n':
bytes[i]=(byte)'?';
}
}
return bytes;
}
public void putTo(ByteBuffer bufferInFillMode)
{
HttpHeader header = HttpHeader.CACHE.get(_name);
if (header!=null)
{
bufferInFillMode.put(header.getBytesColonSpace());
if (HttpHeaderValue.hasKnownValues(header))
{
HttpHeaderValue value=HttpHeaderValue.CACHE.get(_value);
if (value!=null)
bufferInFillMode.put(value.toBuffer());
else
bufferInFillMode.put(toSanitisedValue(_value));
}
else
bufferInFillMode.put(toSanitisedValue(_value));
}
else
{
bufferInFillMode.put(toSanitisedName(_name));
bufferInFillMode.put(__colon_space);
bufferInFillMode.put(toSanitisedValue(_value));
}
BufferUtil.putCRLF(bufferInFillMode);
}
public void putValueTo(ByteBuffer buffer)
{
buffer.put(toSanitisedValue(_value));
}
public HttpHeader getHeader()
{
return _header;
}
public String getName()
{
return _name;
}
public String getValue()
{
return _value;
}
public int getIntValue()
{
return StringUtil.toInt(_value);
}
public long getLongValue()
{
return StringUtil.toLong(_value);
}
@Override
public String toString()
{
return ("[" + getName() + "=" + _value + (_next == null ? "" : "->") + "]");
}
public boolean contains(String value)
{
if (_value==null)
return false;
if (value.equalsIgnoreCase(_value))
return true;
String[] split = _value.split("\\s*,\\s*");
for (String s : split)
{
if (value.equalsIgnoreCase(s))
return true;
}
if (_next!=null)
return _next.contains(value);
return false;
}
}
public boolean contains(HttpHeader header, String value)
{
Field field = getField(header);
if (field==null)
return false;
return field.contains(value);
}
} }

View File

@ -542,7 +542,7 @@ public class HttpGenerator
// default field values // default field values
boolean has_server = false; boolean has_server = false;
HttpFields.Field transfer_encoding=null; HttpField transfer_encoding=null;
boolean keep_alive=false; boolean keep_alive=false;
boolean close=false; boolean close=false;
boolean content_type=false; boolean content_type=false;
@ -551,11 +551,11 @@ public class HttpGenerator
// Generate fields // Generate fields
if (_info.getHttpFields() != null) if (_info.getHttpFields() != null)
{ {
for (HttpFields.Field field : _info.getHttpFields()) for (HttpField field : _info.getHttpFields())
{ {
HttpHeader name = field.getHeader(); HttpHeader h = field.getHeader();
switch (name==null?HttpHeader.UNKNOWN:name) switch (h==null?HttpHeader.UNKNOWN:h)
{ {
case CONTENT_LENGTH: case CONTENT_LENGTH:
// handle specially below // handle specially below
@ -666,11 +666,11 @@ public class HttpGenerator
} }
default: default:
if (name==null) if (h==null)
field.putTo(header); field.putTo(header);
else else
{ {
header.put(name.getBytesColonSpace()); header.put(h.getBytesColonSpace());
field.putValueTo(header); field.putValueTo(header);
header.put(CRLF); header.put(CRLF);
} }

View File

@ -19,11 +19,9 @@
package org.eclipse.jetty.http; package org.eclipse.jetty.http;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
import java.util.HashMap;
import java.util.Map;
import org.eclipse.jetty.util.StringMap;
import org.eclipse.jetty.util.StringUtil; import org.eclipse.jetty.util.StringUtil;
import org.eclipse.jetty.util.Trie;
public enum HttpHeader public enum HttpHeader
@ -114,7 +112,7 @@ public enum HttpHeader
/* ------------------------------------------------------------ */ /* ------------------------------------------------------------ */
public final static StringMap<HttpHeader> CACHE= new StringMap<HttpHeader>(true); public final static Trie<HttpHeader> CACHE= new Trie<HttpHeader>();
static static
{ {
for (HttpHeader header : HttpHeader.values()) for (HttpHeader header : HttpHeader.values())
@ -122,71 +120,6 @@ public enum HttpHeader
CACHE.put(header.toString(),header); CACHE.put(header.toString(),header);
} }
/* ------------------------------------------------------------ */
private final static HttpHeader[] __hashed= new HttpHeader[4096];
private final static int __maxHashed;
static
{
// This hash function has been picked to have no collisions for
// the known header values. This allows a very quick lookup.
int max=0;
Map<Integer,HttpHeader> hashes=new HashMap<>();
for (HttpHeader header : HttpHeader.values())
{
String s=header.asString();
max=Math.max(max,s.length());
int h=0;
for (char c:s.toCharArray())
h = 31*h + ((c>='a')?(c-'a'+'A'):c);
int hash=h%__hashed.length;
if (hash<0)hash=-hash;
if (hashes.containsKey(hash))
{
// This should not happen with known headers.
System.err.println("Duplicate hash "+header+" "+hashes.get(hash));
System.exit(1);
}
hashes.put(hash,header);
__hashed[hash]=header;
}
__maxHashed=max;
}
public static HttpHeader lookAheadGet(byte[] bytes, int position, int limit)
{
int h=0;
byte b=0;
limit=Math.min(position+__maxHashed,limit);
for (int i=position;i<limit;i++)
{
b=bytes[i];
if (b==':'||b==' ')
break;
h= 31*h+ ((b>='a')?(b-'a'+'A'):b);
}
if (b!=':'&&b!=' ')
return null;
int hash=h%__hashed.length;
if (hash<0)hash=-hash;
HttpHeader header=__hashed[hash];
if (header!=null)
{
String s=header.asString();
for (int i=s.length();i-->0;)
{
b=bytes[position+i];
char c=s.charAt(i);
if (c!=b && Character.toUpperCase(c)!=(b>='a'?(b-'a'+'A'):b))
return null;
}
}
return header;
}
private final String _string; private final String _string;
private final byte[] _bytes; private final byte[] _bytes;
private final byte[] _bytesColonSpace; private final byte[] _bytesColonSpace;

View File

@ -22,7 +22,7 @@ import java.nio.ByteBuffer;
import java.util.EnumSet; import java.util.EnumSet;
import org.eclipse.jetty.util.BufferUtil; import org.eclipse.jetty.util.BufferUtil;
import org.eclipse.jetty.util.StringMap; import org.eclipse.jetty.util.Trie;
/** /**
@ -44,7 +44,7 @@ public enum HttpHeaderValue
UNKNOWN("::UNKNOWN::"); UNKNOWN("::UNKNOWN::");
/* ------------------------------------------------------------ */ /* ------------------------------------------------------------ */
public final static StringMap<HttpHeaderValue> CACHE= new StringMap<HttpHeaderValue>(true); public final static Trie<HttpHeaderValue> CACHE= new Trie<HttpHeaderValue>();
static static
{ {
for (HttpHeaderValue value : HttpHeaderValue.values()) for (HttpHeaderValue value : HttpHeaderValue.values())

View File

@ -20,8 +20,8 @@ package org.eclipse.jetty.http;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
import org.eclipse.jetty.util.StringMap;
import org.eclipse.jetty.util.StringUtil; import org.eclipse.jetty.util.StringUtil;
import org.eclipse.jetty.util.Trie;
/* ------------------------------------------------------------------------------- */ /* ------------------------------------------------------------------------------- */
@ -109,11 +109,11 @@ public enum HttpMethod
{ {
if (buffer.hasArray()) if (buffer.hasArray())
return lookAheadGet(buffer.array(),buffer.arrayOffset()+buffer.position(),buffer.arrayOffset()+buffer.limit()); return lookAheadGet(buffer.array(),buffer.arrayOffset()+buffer.position(),buffer.arrayOffset()+buffer.limit());
return null; return CACHE.getBest(buffer,0,buffer.remaining());
} }
/* ------------------------------------------------------------ */ /* ------------------------------------------------------------ */
public final static StringMap<HttpMethod> CACHE= new StringMap<HttpMethod>(true); public final static Trie<HttpMethod> CACHE= new Trie<HttpMethod>();
static static
{ {
for (HttpMethod method : HttpMethod.values()) for (HttpMethod method : HttpMethod.values())

View File

@ -508,6 +508,86 @@ public class HttpParser
return return_from_parse; return return_from_parse;
} }
private boolean handleKnownHeaders(ByteBuffer buffer)
{
switch (_header)
{
case CONTENT_LENGTH:
if (_endOfContent != EndOfContent.CHUNKED_CONTENT)
{
try
{
_contentLength=Long.parseLong(_valueString);
}
catch(NumberFormatException e)
{
LOG.ignore(e);
badMessage(buffer,HttpStatus.BAD_REQUEST_400,"Bad Content-Length");
return true;
}
if (_contentLength <= 0)
_endOfContent=EndOfContent.NO_CONTENT;
else
_endOfContent=EndOfContent.CONTENT_LENGTH;
}
break;
case TRANSFER_ENCODING:
if (_value==HttpHeaderValue.CHUNKED)
_endOfContent=EndOfContent.CHUNKED_CONTENT;
else
{
if (_valueString.endsWith(HttpHeaderValue.CHUNKED.toString()))
_endOfContent=EndOfContent.CHUNKED_CONTENT;
else if (_valueString.indexOf(HttpHeaderValue.CHUNKED.toString()) >= 0)
{
badMessage(buffer,HttpStatus.BAD_REQUEST_400,"Bad chunking");
return true;
}
}
break;
case HOST:
_host=true;
String host=_valueString;
int port=0;
if (host==null || host.length()==0)
{
badMessage(buffer,HttpStatus.BAD_REQUEST_400,"Bad Host header");
return true;
}
loop: for (int i = host.length(); i-- > 0;)
{
char c2 = (char)(0xff & host.charAt(i));
switch (c2)
{
case ']':
break loop;
case ':':
try
{
port = StringUtil.toInt(host.substring(i+1));
}
catch (NumberFormatException e)
{
LOG.debug(e);
badMessage(buffer,HttpStatus.BAD_REQUEST_400,"Bad Host header");
return true;
}
host = host.substring(0,i);
break loop;
}
}
if (_requestHandler!=null)
_requestHandler.parsedHostHeader(host,port);
}
return false;
}
/* ------------------------------------------------------------------------------- */ /* ------------------------------------------------------------------------------- */
/* /*
* Parse the message headers and return true if the handler has signaled for a return * Parse the message headers and return true if the handler has signaled for a return
@ -545,8 +625,14 @@ public class HttpParser
case HttpTokens.TAB: case HttpTokens.TAB:
{ {
// header value without name - continuation? // header value without name - continuation?
_length=-1;
_string.setLength(0); _string.setLength(0);
if (_valueString!=null)
{
_string.append(_valueString);
_string.append(' ');
}
_length=_string.length();
_valueString=null;
setState(State.HEADER_VALUE); setState(State.HEADER_VALUE);
break; break;
} }
@ -557,82 +643,8 @@ public class HttpParser
if (_headerString!=null || _valueString!=null) if (_headerString!=null || _valueString!=null)
{ {
// Handle known headers // Handle known headers
if (_header!=null) if (_header!=null && handleKnownHeaders(buffer))
{ return true;
switch (_header)
{
case CONTENT_LENGTH:
if (_endOfContent != EndOfContent.CHUNKED_CONTENT)
{
try
{
_contentLength=Long.parseLong(_valueString);
}
catch(NumberFormatException e)
{
LOG.ignore(e);
badMessage(buffer,HttpStatus.BAD_REQUEST_400,"Bad Content-Length");
return true;
}
if (_contentLength <= 0)
_endOfContent=EndOfContent.NO_CONTENT;
else
_endOfContent=EndOfContent.CONTENT_LENGTH;
}
break;
case TRANSFER_ENCODING:
if (_value==HttpHeaderValue.CHUNKED)
_endOfContent=EndOfContent.CHUNKED_CONTENT;
else
{
if (_valueString.endsWith(HttpHeaderValue.CHUNKED.toString()))
_endOfContent=EndOfContent.CHUNKED_CONTENT;
else if (_valueString.indexOf(HttpHeaderValue.CHUNKED.toString()) >= 0)
{
badMessage(buffer,HttpStatus.BAD_REQUEST_400,"Bad chunking");
return true;
}
}
break;
case HOST:
_host=true;
String host=_valueString;
int port=0;
if (host==null || host.length()==0)
{
badMessage(buffer,HttpStatus.BAD_REQUEST_400,"Bad Host header");
return true;
}
loop: for (int i = host.length(); i-- > 0;)
{
char c2 = (char)(0xff & host.charAt(i));
switch (c2)
{
case ']':
break loop;
case ':':
try
{
port = StringUtil.toInt(host.substring(i+1));
}
catch (NumberFormatException e)
{
LOG.debug(e);
badMessage(buffer,HttpStatus.BAD_REQUEST_400,"Bad Host header");
return true;
}
host = host.substring(0,i);
break loop;
}
}
if (_requestHandler!=null)
_requestHandler.parsedHostHeader(host,port);
}
}
return_from_parse|=_handler.parsedHeader(_header, _headerString, _valueString); return_from_parse|=_handler.parsedHeader(_header, _headerString, _valueString);
} }
@ -704,14 +716,36 @@ public class HttpParser
{ {
if (buffer.remaining()>6 && buffer.hasArray()) if (buffer.remaining()>6 && buffer.hasArray())
{ {
// Try a look ahead for the known headers. // Try a look ahead for the known header name and value.
_header=HttpHeader.lookAheadGet(buffer.array(),buffer.arrayOffset()+buffer.position()-1,buffer.arrayOffset()+buffer.limit()); HttpField field=HttpField.CACHE.getBest(buffer.array(),buffer.arrayOffset()+buffer.position()-1,buffer.remaining()+1);
if (field!=null)
{
_header=field.getHeader();
_headerString=field.getName();
_valueString=field.getValue();
if (_valueString==null)
{
setState(State.HEADER_VALUE);
buffer.position(buffer.position()+_headerString.length()+1);
_string.setLength(0);
_length=0;
}
else
{
setState(State.HEADER_IN_VALUE);
buffer.position(buffer.position()+_headerString.length()+_valueString.length()+1);
}
break;
}
// Try a look ahead for the known header name.
_header=HttpHeader.CACHE.getBest(buffer.array(),buffer.arrayOffset()+buffer.position()-1,buffer.remaining()+1);
if (_header!=null) if (_header!=null)
{ {
_headerString=_header.asString(); _headerString=_header.asString();
buffer.position(buffer.position()+_headerString.length()); _string.setLength(0);
setState(buffer.get(buffer.position()-1)==':'?State.HEADER_VALUE:State.HEADER_NAME); setState(State.HEADER_IN_NAME);
buffer.position(buffer.position()+_headerString.length()-1);
break; break;
} }
} }
@ -733,8 +767,11 @@ public class HttpParser
case HttpTokens.CARRIAGE_RETURN: case HttpTokens.CARRIAGE_RETURN:
case HttpTokens.LINE_FEED: case HttpTokens.LINE_FEED:
consumeCRLF(ch,buffer); consumeCRLF(ch,buffer);
_headerString=takeLengthString(); if (_headerString==null)
_header=HttpHeader.CACHE.get(_headerString); {
_headerString=takeLengthString();
_header=HttpHeader.CACHE.get(_headerString);
}
setState(State.HEADER); setState(State.HEADER);
break; break;
@ -753,15 +790,6 @@ public class HttpParser
break; break;
default: default:
{ {
if (_header!=null)
{
_string.setLength(0);
_string.append(_header.asString());
_string.append(' ');
_length=_string.length();
_header=null;
_headerString=null;
}
_string.append((char)ch); _string.append((char)ch);
_length=_string.length(); _length=_string.length();
setState(State.HEADER_IN_NAME); setState(State.HEADER_IN_NAME);
@ -793,10 +821,26 @@ public class HttpParser
break; break;
case HttpTokens.SPACE: case HttpTokens.SPACE:
case HttpTokens.TAB: case HttpTokens.TAB:
if (_header!=null)
{
_string.setLength(0);
_string.append(_header.asString());
_length=_string.length();
_header=null;
_headerString=null;
}
setState(State.HEADER_NAME); setState(State.HEADER_NAME);
_string.append((char)ch); _string.append((char)ch);
break; break;
default: default:
if (_header!=null)
{
_string.setLength(0);
_string.append(_header.asString());
_length=_string.length();
_header=null;
_headerString=null;
}
_string.append((char)ch); _string.append((char)ch);
_length++; _length++;
} }
@ -849,13 +893,7 @@ public class HttpParser
consumeCRLF(ch,buffer); consumeCRLF(ch,buffer);
if (_length > 0) if (_length > 0)
{ {
if (_valueString!=null) if (HttpHeaderValue.hasKnownValues(_header))
{
// multi line value!
_value=null;
_valueString+=" "+takeString();
}
else if (HttpHeaderValue.hasKnownValues(_header))
{ {
_valueString=takeString(); _valueString=takeString();
_value=HttpHeaderValue.CACHE.get(_valueString); _value=HttpHeaderValue.CACHE.get(_valueString);
@ -871,10 +909,24 @@ public class HttpParser
break; break;
case HttpTokens.SPACE: case HttpTokens.SPACE:
case HttpTokens.TAB: case HttpTokens.TAB:
if (_valueString!=null)
{
_string.setLength(0);
_string.append(_valueString);
_length=_valueString.length();
_valueString=null;
}
_string.append((char)ch); _string.append((char)ch);
setState(State.HEADER_VALUE); setState(State.HEADER_VALUE);
break; break;
default: default:
if (_valueString!=null)
{
_string.setLength(0);
_string.append(_valueString);
_length=_valueString.length();
_valueString=null;
}
_string.append((char)ch); _string.append((char)ch);
_length++; _length++;
} }

View File

@ -21,7 +21,7 @@ package org.eclipse.jetty.http;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
import org.eclipse.jetty.util.BufferUtil; import org.eclipse.jetty.util.BufferUtil;
import org.eclipse.jetty.util.StringMap; import org.eclipse.jetty.util.Trie;
/* ------------------------------------------------------------------------------- */ /* ------------------------------------------------------------------------------- */
/** /**
@ -34,7 +34,7 @@ public enum HttpScheme
WSS("wss"); WSS("wss");
/* ------------------------------------------------------------ */ /* ------------------------------------------------------------ */
public final static StringMap<HttpScheme> CACHE= new StringMap<HttpScheme>(true); public final static Trie<HttpScheme> CACHE= new Trie<HttpScheme>();
static static
{ {
for (HttpScheme version : HttpScheme.values()) for (HttpScheme version : HttpScheme.values())

View File

@ -20,8 +20,8 @@ package org.eclipse.jetty.http;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
import org.eclipse.jetty.util.StringMap;
import org.eclipse.jetty.util.StringUtil; import org.eclipse.jetty.util.StringUtil;
import org.eclipse.jetty.util.Trie;
/* ------------------------------------------------------------------------------- */ /* ------------------------------------------------------------------------------- */
@ -33,7 +33,7 @@ public enum HttpVersion
HTTP_2_0("HTTP/2.0",20); HTTP_2_0("HTTP/2.0",20);
/* ------------------------------------------------------------ */ /* ------------------------------------------------------------ */
public final static StringMap<HttpVersion> CACHE= new StringMap<HttpVersion>(true); public final static Trie<HttpVersion> CACHE= new Trie<HttpVersion>();
static static
{ {
for (HttpVersion version : HttpVersion.values()) for (HttpVersion version : HttpVersion.values())

View File

@ -28,8 +28,8 @@ import java.util.MissingResourceException;
import java.util.ResourceBundle; import java.util.ResourceBundle;
import org.eclipse.jetty.util.BufferUtil; import org.eclipse.jetty.util.BufferUtil;
import org.eclipse.jetty.util.StringMap;
import org.eclipse.jetty.util.StringUtil; import org.eclipse.jetty.util.StringUtil;
import org.eclipse.jetty.util.Trie;
import org.eclipse.jetty.util.log.Log; import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger; import org.eclipse.jetty.util.log.Logger;
@ -109,8 +109,8 @@ public class MimeTypes
/* ------------------------------------------------------------ */ /* ------------------------------------------------------------ */
private static final Logger LOG = Log.getLogger(MimeTypes.class); private static final Logger LOG = Log.getLogger(MimeTypes.class);
public final static StringMap<MimeTypes.Type> CACHE= new StringMap<MimeTypes.Type>(true); public final static Trie<MimeTypes.Type> CACHE= new Trie<MimeTypes.Type>();
private final static StringMap<ByteBuffer> TYPES= new StringMap<ByteBuffer>(true); private final static Trie<ByteBuffer> TYPES= new Trie<ByteBuffer>();
private final static Map<String,String> __dftMimeMap = new HashMap<String,String>(); private final static Map<String,String> __dftMimeMap = new HashMap<String,String>();
private final static Map<String,String> __encodings = new HashMap<String,String>(); private final static Map<String,String> __encodings = new HashMap<String,String>();

View File

@ -514,8 +514,8 @@ public class HttpFieldsTest
for (int i=0;i<7;i++) for (int i=0;i<7;i++)
{ {
assertFalse(""+i,header.getField(""+i).contains("xyz")); assertFalse(""+i,header.contains(""+i,"xyz"));
assertEquals(""+i,i>=4,header.getField(""+i).contains("def")); assertEquals(""+i,i>=4,header.contains(""+i,"def"));
} }
} }
} }

View File

@ -23,6 +23,7 @@ import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue; import static org.junit.Assert.assertTrue;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
import java.util.Arrays;
import org.eclipse.jetty.http.HttpParser.State; import org.eclipse.jetty.http.HttpParser.State;
import org.eclipse.jetty.util.BufferUtil; import org.eclipse.jetty.util.BufferUtil;
@ -153,12 +154,15 @@ public class HttpParserTest
"Host: localhost\015\012" + "Host: localhost\015\012" +
"Header1: value1\015\012" + "Header1: value1\015\012" +
"Header 2 : value 2a \015\012" + "Header 2 : value 2a \015\012" +
" value 2b \015\012" + " value 2b \015\012" +
"Header3: \015\012" + "Header3: \015\012" +
"Header4 \015\012" + "Header4 \015\012" +
" value4\015\012" + " value4\015\012" +
"Server5 : notServer\015\012" + "Server5 : notServer\015\012" +
"Host Header: notHost\015\012" + "Host Header: notHost\015\012" +
"Connection: close\015\012" +
"Accept-Encoding: gzip, deflated\015\012" +
"Accept: unknown\015\012" +
"\015\012"); "\015\012");
Handler handler = new Handler(); Handler handler = new Handler();
HttpParser parser= new HttpParser((HttpParser.RequestHandler)handler); HttpParser parser= new HttpParser((HttpParser.RequestHandler)handler);
@ -181,7 +185,13 @@ public class HttpParserTest
assertEquals("notServer", _val[5]); assertEquals("notServer", _val[5]);
assertEquals("Host Header", _hdr[6]); assertEquals("Host Header", _hdr[6]);
assertEquals("notHost", _val[6]); assertEquals("notHost", _val[6]);
assertEquals(6, _h); assertEquals("Connection", _hdr[7]);
assertEquals("close", _val[7]);
assertEquals("Accept-Encoding", _hdr[8]);
assertEquals("gzip, deflated", _val[8]);
assertEquals("Accept", _hdr[9]);
assertEquals("unknown", _val[9]);
assertEquals(9, _h);
} }
@Test @Test
@ -736,8 +746,8 @@ public class HttpParserTest
{ {
request=true; request=true;
_h= -1; _h= -1;
_hdr= new String[9]; _hdr= new String[10];
_val= new String[9]; _val= new String[10];
_methodOrVersion= method; _methodOrVersion= method;
_uriOrStatus= uri; _uriOrStatus= uri;
_versionOrReason= version==null?null:version.asString(); _versionOrReason= version==null?null:version.asString();

View File

@ -44,6 +44,7 @@ import org.eclipse.jetty.client.api.Response;
import org.eclipse.jetty.client.api.Result; import org.eclipse.jetty.client.api.Result;
import org.eclipse.jetty.client.util.InputStreamContentProvider; import org.eclipse.jetty.client.util.InputStreamContentProvider;
import org.eclipse.jetty.client.util.TimedResponseListener; import org.eclipse.jetty.client.util.TimedResponseListener;
import org.eclipse.jetty.http.HttpField;
import org.eclipse.jetty.http.HttpFields; import org.eclipse.jetty.http.HttpFields;
import org.eclipse.jetty.http.HttpMethod; import org.eclipse.jetty.http.HttpMethod;
import org.eclipse.jetty.http.HttpVersion; import org.eclipse.jetty.http.HttpVersion;
@ -464,7 +465,7 @@ public class ProxyServlet extends HttpServlet
protected void onResponseHeaders(HttpServletRequest request, HttpServletResponse response, Response proxyResponse) protected void onResponseHeaders(HttpServletRequest request, HttpServletResponse response, Response proxyResponse)
{ {
for (HttpFields.Field field : proxyResponse.getHeaders()) for (HttpField field : proxyResponse.getHeaders())
{ {
String headerName = field.getName(); String headerName = field.getName();
String lowerHeaderName = headerName.toLowerCase(Locale.ENGLISH); String lowerHeaderName = headerName.toLowerCase(Locale.ENGLISH);

View File

@ -23,6 +23,7 @@ import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collection; import java.util.Collection;
import java.util.Collections; import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet; import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
@ -479,7 +480,7 @@ public class ConstraintSecurityHandler extends SecurityHandler implements Constr
Map<String, RoleInfo> mappings = _constraintMap.get(mapping.getPathSpec()); Map<String, RoleInfo> mappings = _constraintMap.get(mapping.getPathSpec());
if (mappings == null) if (mappings == null)
{ {
mappings = new StringMap<>(); mappings = new HashMap<String,RoleInfo>();
_constraintMap.put(mapping.getPathSpec(),mappings); _constraintMap.put(mapping.getPathSpec(),mappings);
} }
RoleInfo allMethodsRoleInfo = mappings.get(ALL_METHODS); RoleInfo allMethodsRoleInfo = mappings.get(ALL_METHODS);

View File

@ -25,6 +25,7 @@ import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException; import java.util.concurrent.TimeoutException;
import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicBoolean;
import org.eclipse.jetty.http.HttpField;
import org.eclipse.jetty.http.HttpFields; import org.eclipse.jetty.http.HttpFields;
import org.eclipse.jetty.http.HttpGenerator; import org.eclipse.jetty.http.HttpGenerator;
import org.eclipse.jetty.http.HttpStatus; import org.eclipse.jetty.http.HttpStatus;
@ -128,7 +129,7 @@ public class HttpTransportOverSPDY implements HttpTransport
{ {
for (int i = 0; i < fields.size(); ++i) for (int i = 0; i < fields.size(); ++i)
{ {
HttpFields.Field field = fields.getField(i); HttpField field = fields.getField(i);
String name = field.getName(); String name = field.getName();
String value = field.getValue(); String value = field.getValue();
headers.put(name, value); headers.put(name, value);

View File

@ -0,0 +1,334 @@
//
// ========================================================================
// Copyright (c) 1995-2012 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.util;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
public class Trie<V>
{
private static final int[] __lookup =
{ // 0 1 2 3 4 5 6 7 8 9 A B C D E F
/*0*/-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
/*1*/-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 30,
/*2*/31, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 26, -1, 27, -1, -1,
/*3*/-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 28, 29, -1, -1, -1, -1,
/*4*/-1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
/*5*/15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1,
/*6*/-1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
/*7*/15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1,
};
private static final int INDEX = 32;
private final Trie<V>[] _nextIndex;
private final List<Trie<V>> _nextOther=new ArrayList<>();
private final char _c;
private String _key;
private V _value;
public Trie()
{
_nextIndex = new Trie[INDEX];
_c=0;
}
private Trie(char c)
{
_nextIndex = new Trie[INDEX];
this._c=c;
}
public V put(V v)
{
return put(v.toString(),v);
}
public V put(String s, V v)
{
Trie<V> t = this;
int k;
int limit = s.length();
for(k=0; k < limit; k++)
{
char c=s.charAt(k);
int index=c>=0&&c<0x7f?__lookup[c]:-1;
if (index>=0)
{
if (t._nextIndex[index] == null)
t._nextIndex[index] = new Trie<V>(c);
t = t._nextIndex[index];
}
else
{
Trie<V> n=null;
for (int i=t._nextOther.size();i-->0;)
{
n=t._nextOther.get(i);
if (n._c==c)
break;
n=null;
}
if (n==null)
{
n=new Trie<V>(c);
t._nextOther.add(n);
}
t=n;
}
}
t._key=v==null?null:s;
V old=t._value;
t._value = v;
return old;
}
public V remove(String s)
{
V o=get(s);
put(s,null);
return o;
}
public V get(String s)
{
Trie<V> t = this;
int len = s.length();
for(int i=0; i < len; i++)
{
char c=s.charAt(i);
int index=c>=0&&c<0x7f?__lookup[c]:-1;
if (index>=0)
{
if (t._nextIndex[index] == null)
return null;
t = t._nextIndex[index];
}
else
{
Trie<V> n=null;
for (int j=t._nextOther.size();j-->0;)
{
n=t._nextOther.get(j);
if (n._c==c)
break;
n=null;
}
if (n==null)
return null;
t=n;
}
}
return t._value;
}
public V get(ByteBuffer b,int offset,int len)
{
Trie<V> t = this;
for(int i=0; i < len; i++)
{
byte c=b.get(offset+i);
int index=c>=0&&c<0x7f?__lookup[c]:-1;
if (index>=0)
{
if (t._nextIndex[index] == null)
return null;
t = t._nextIndex[index];
}
else
{
Trie<V> n=null;
for (int j=t._nextOther.size();j-->0;)
{
n=t._nextOther.get(j);
if (n._c==c)
break;
n=null;
}
if (n==null)
return null;
t=n;
}
}
return t._value;
}
public V getBest(byte[] b,int offset,int len)
{
Trie<V> t = this;
for(int i=0; i < len; i++)
{
byte c=b[offset+i];
int index=c>=0&&c<0x7f?__lookup[c]:-1;
if (index>=0)
{
if (t._nextIndex[index] == null)
return null;
t = t._nextIndex[index];
}
else
{
Trie<V> n=null;
for (int j=t._nextOther.size();j-->0;)
{
n=t._nextOther.get(j);
if (n._c==c)
break;
n=null;
}
if (n==null)
return null;
t=n;
}
// Is the next Trie is a match
if (t._key!=null)
{
// Recurse so we can remember this possibility
V best=t.getBest(b,offset+i+1,len-i-1);
if (best!=null)
return best;
return t._value;
}
}
return t._value;
}
public V getBest(ByteBuffer b,int offset,int len)
{
if (b.hasArray())
return getBest(b.array(),b.arrayOffset()+b.position()+offset,len);
return doGetBest(b,offset,len);
}
private V doGetBest(ByteBuffer b,int offset,int len)
{
Trie<V> t = this;
for(int i=0; i < len; i++)
{
byte c=b.get(offset+i);
int index=c>=0&&c<0x7f?__lookup[c]:-1;
if (index>=0)
{
if (t._nextIndex[index] == null)
return null;
t = t._nextIndex[index];
}
else
{
Trie<V> n=null;
for (int j=t._nextOther.size();j-->0;)
{
n=t._nextOther.get(j);
if (n._c==c)
break;
n=null;
}
if (n==null)
return null;
t=n;
}
// Is the next Trie is a match
if (t._key!=null)
{
// Recurse so we can remember this possibility
V best=t.getBest(b,offset+i+1,len-i-1);
if (best!=null)
return best;
return t._value;
}
}
return t._value;
}
@Override
public String toString()
{
StringBuilder buf = new StringBuilder();
toString(buf,this);
if (buf.length()==0)
return "{}";
buf.setCharAt(0,'{');
buf.append('}');
return buf.toString();
}
private static <V> void toString(Appendable out, Trie<V> t)
{
if (t != null)
{
if (t._value!=null)
{
try
{
out.append(',');
out.append(t._key);
out.append('=');
out.append(t._value.toString());
}
catch (IOException e)
{
throw new RuntimeException(e);
}
}
for(int i=0; i < INDEX; i++)
{
if (t._nextIndex[i] != null)
toString(out,t._nextIndex[i]);
}
for (int i=t._nextOther.size();i-->0;)
toString(out,t._nextOther.get(i));
}
}
public Set<String> keySet()
{
Set<String> keys = new HashSet<>();
keySet(keys,this);
return keys;
}
private static <V> void keySet(Set<String> set, Trie<V> t)
{
if (t != null)
{
if (t._key!=null)
set.add(t._key);
for(int i=0; i < INDEX; i++)
{
if (t._nextIndex[i] != null)
keySet(set,t._nextIndex[i]);
}
for (int i=t._nextOther.size();i-->0;)
keySet(set,t._nextOther.get(i));
}
}
}

View File

@ -0,0 +1,113 @@
//
// ========================================================================
// Copyright (c) 1995-2012 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.util;
import java.nio.ByteBuffer;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
public class TrieTest
{
Trie<Integer> trie = new Trie<>();
@Before
public void before()
{
trie.put("hello",1);
trie.put("He",2);
trie.put("HELL",3);
trie.put("wibble",4);
trie.put("Wobble",5);
trie.put("foo-bar",6);
trie.put("foo+bar",7);
trie.put("HELL4",8);
}
@Test
public void testGetString() throws Exception
{
Assert.assertEquals(1,trie.get("hello").intValue());
Assert.assertEquals(2,trie.get("He").intValue());
Assert.assertEquals(3,trie.get("HELL").intValue());
Assert.assertEquals(4,trie.get("wibble").intValue());
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(1,trie.get("Hello").intValue());
Assert.assertEquals(2,trie.get("HE").intValue());
Assert.assertEquals(3,trie.get("heLL").intValue());
Assert.assertEquals(4,trie.get("Wibble").intValue());
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(null,trie.get("Help"));
Assert.assertEquals(null,trie.get("Blah"));
}
@Test
public void testGetBuffer() throws Exception
{
Assert.assertEquals(1,trie.get(BufferUtil.toBuffer("xhellox"),1,5).intValue());
Assert.assertEquals(2,trie.get(BufferUtil.toBuffer("xhellox"),1,2).intValue());
Assert.assertEquals(3,trie.get(BufferUtil.toBuffer("xhellox"),1,4).intValue());
Assert.assertEquals(4,trie.get(BufferUtil.toBuffer("wibble"),0,6).intValue());
Assert.assertEquals(5,trie.get(BufferUtil.toBuffer("xWobble"),1,6).intValue());
Assert.assertEquals(6,trie.get(BufferUtil.toBuffer("xfoo-barx"),1,7).intValue());
Assert.assertEquals(7,trie.get(BufferUtil.toBuffer("xfoo+barx"),1,7).intValue());
Assert.assertEquals(1,trie.get(BufferUtil.toBuffer("xhellox"),1,5).intValue());
Assert.assertEquals(2,trie.get(BufferUtil.toBuffer("xHELLox"),1,2).intValue());
Assert.assertEquals(3,trie.get(BufferUtil.toBuffer("xhellox"),1,4).intValue());
Assert.assertEquals(4,trie.get(BufferUtil.toBuffer("Wibble"),0,6).intValue());
Assert.assertEquals(5,trie.get(BufferUtil.toBuffer("xwobble"),1,6).intValue());
Assert.assertEquals(6,trie.get(BufferUtil.toBuffer("xFOO-barx"),1,7).intValue());
Assert.assertEquals(7,trie.get(BufferUtil.toBuffer("xFOO+barx"),1,7).intValue());
Assert.assertEquals(null,trie.get(BufferUtil.toBuffer("xHelpx"),1,4));
Assert.assertEquals(null,trie.get(BufferUtil.toBuffer("xBlahx"),1,4));
}
@Test
public void testGetBest() throws Exception
{
Assert.assertEquals(1,trie.getBest(BufferUtil.toBuffer("xhelloxxxx"),1,10).intValue());
Assert.assertEquals(2,trie.getBest(BufferUtil.toBuffer("xhelxoxxxx"),1,10).intValue());
Assert.assertEquals(3,trie.getBest(BufferUtil.toBuffer("xhellxxxxx"),1,10).intValue());
Assert.assertEquals(6,trie.getBest(BufferUtil.toBuffer("xfoo-barxx"),1,10).intValue());
Assert.assertEquals(8,trie.getBest(BufferUtil.toBuffer("xhell4xxxx"),1,10).intValue());
Assert.assertEquals(1,trie.getBest(BufferUtil.toBuffer("xHELLOxxxx"),1,10).intValue());
Assert.assertEquals(2,trie.getBest(BufferUtil.toBuffer("xHELxoxxxx"),1,10).intValue());
Assert.assertEquals(3,trie.getBest(BufferUtil.toBuffer("xHELLxxxxx"),1,10).intValue());
Assert.assertEquals(6,trie.getBest(BufferUtil.toBuffer("xfoo-BARxx"),1,10).intValue());
Assert.assertEquals(8,trie.getBest(BufferUtil.toBuffer("xHELL4xxxx"),1,10).intValue());
ByteBuffer buffer = (ByteBuffer)BufferUtil.toBuffer("xhelloxxxxxxx").position(2);
Assert.assertEquals(1,trie.getBest(buffer,-1,10).intValue());
}
}

View File

@ -92,7 +92,7 @@ public class MuxAddHandler implements MuxAddServer
for (String headerName : request.getHeaders().keySet()) for (String headerName : request.getHeaders().keySet())
{ {
HttpHeader header = HttpHeader.lookAheadGet(headerName.getBytes(),0,headerName.length()); HttpHeader header = HttpHeader.CACHE.getBest(headerName.getBytes(),0,headerName.length());
for (String value : request.getHeaders().get(headerName)) for (String value : request.getHeaders().get(headerName))
{ {
httpChannel.parsedHeader(header,headerName,value); httpChannel.parsedHeader(header,headerName,value);