Reduce header cache memory usage on non persistent requests (#6494)
Delay creating a header cache until a second request on a parser. Refactored cache code into subclass Signed-off-by: Greg Wilkins <gregw@webtide.com>
This commit is contained in:
parent
259f9af9c9
commit
8945a58ffc
|
@ -15,6 +15,7 @@ package org.eclipse.jetty.http;
|
|||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.ArrayList;
|
||||
import java.util.EnumSet;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
|
@ -217,6 +218,7 @@ public class HttpParser
|
|||
private final int _maxHeaderBytes;
|
||||
private final HttpCompliance _complianceMode;
|
||||
private final Utf8StringBuilder _uri = new Utf8StringBuilder(INITIAL_URI_LENGTH);
|
||||
private final FieldCache _fieldCache = new FieldCache();
|
||||
private HttpField _field;
|
||||
private HttpHeader _header;
|
||||
private String _headerString;
|
||||
|
@ -241,11 +243,8 @@ public class HttpParser
|
|||
private boolean _headResponse;
|
||||
private boolean _cr;
|
||||
private ByteBuffer _contentChunk;
|
||||
private Index.Mutable<HttpField> _fieldCache;
|
||||
private int _length;
|
||||
private final StringBuilder _string = new StringBuilder();
|
||||
private int _headerCacheSize = 1024;
|
||||
private boolean _headerCacheCaseSensitive;
|
||||
|
||||
private static HttpCompliance compliance()
|
||||
{
|
||||
|
@ -304,22 +303,22 @@ public class HttpParser
|
|||
|
||||
public int getHeaderCacheSize()
|
||||
{
|
||||
return _headerCacheSize;
|
||||
return _fieldCache.getCapacity();
|
||||
}
|
||||
|
||||
public void setHeaderCacheSize(int headerCacheSize)
|
||||
{
|
||||
_headerCacheSize = headerCacheSize;
|
||||
_fieldCache.setCapacity(headerCacheSize);
|
||||
}
|
||||
|
||||
public boolean isHeaderCacheCaseSensitive()
|
||||
{
|
||||
return _headerCacheCaseSensitive;
|
||||
return _fieldCache.isCaseSensitive();
|
||||
}
|
||||
|
||||
public void setHeaderCacheCaseSensitive(boolean headerCacheCaseSensitive)
|
||||
{
|
||||
_headerCacheCaseSensitive = headerCacheCaseSensitive;
|
||||
_fieldCache.setCaseSensitive(headerCacheCaseSensitive);
|
||||
}
|
||||
|
||||
protected void checkViolation(Violation violation) throws BadMessageException
|
||||
|
@ -746,6 +745,7 @@ public class HttpParser
|
|||
break;
|
||||
|
||||
case LF:
|
||||
_fieldCache.prepare();
|
||||
setState(State.HEADER);
|
||||
_responseHandler.startResponse(_version, _responseStatus, null);
|
||||
break;
|
||||
|
@ -851,6 +851,7 @@ public class HttpParser
|
|||
case LF:
|
||||
if (_responseHandler != null)
|
||||
{
|
||||
_fieldCache.prepare();
|
||||
setState(State.HEADER);
|
||||
_responseHandler.startResponse(_version, _responseStatus, null);
|
||||
}
|
||||
|
@ -881,7 +882,7 @@ public class HttpParser
|
|||
_version = HttpVersion.CACHE.get(takeString());
|
||||
}
|
||||
checkVersion();
|
||||
|
||||
_fieldCache.prepare();
|
||||
setState(State.HEADER);
|
||||
|
||||
_requestHandler.startRequest(_methodString, _uri.toString(), _version);
|
||||
|
@ -905,6 +906,7 @@ public class HttpParser
|
|||
{
|
||||
case LF:
|
||||
String reason = takeString();
|
||||
_fieldCache.prepare();
|
||||
setState(State.HEADER);
|
||||
_responseHandler.startResponse(_version, _responseStatus, reason);
|
||||
continue;
|
||||
|
@ -1026,7 +1028,7 @@ public class HttpParser
|
|||
_field = new HostPortHttpField(_header,
|
||||
CASE_SENSITIVE_FIELD_NAME.isAllowedBy(_complianceMode) ? _headerString : _header.asString(),
|
||||
_valueString);
|
||||
addToFieldCache = true;
|
||||
addToFieldCache = _fieldCache.isEnabled();
|
||||
}
|
||||
break;
|
||||
|
||||
|
@ -1035,7 +1037,7 @@ public class HttpParser
|
|||
if (_field == null)
|
||||
_field = new HttpField(_header, caseInsensitiveHeader(_headerString, _header.asString()), _valueString);
|
||||
if (getHeaderCacheSize() > 0 && _field.contains(HttpHeaderValue.CLOSE.asString()))
|
||||
_fieldCache = NO_CACHE;
|
||||
_fieldCache.setCapacity(-1);
|
||||
break;
|
||||
|
||||
case AUTHORIZATION:
|
||||
|
@ -1046,7 +1048,7 @@ public class HttpParser
|
|||
case COOKIE:
|
||||
case CACHE_CONTROL:
|
||||
case USER_AGENT:
|
||||
addToFieldCache = _field == null;
|
||||
addToFieldCache = _field == null && _fieldCache.cacheable(_header, _valueString);
|
||||
break;
|
||||
|
||||
default:
|
||||
|
@ -1054,18 +1056,12 @@ public class HttpParser
|
|||
}
|
||||
|
||||
// Cache field?
|
||||
if (addToFieldCache && _header != null && _valueString != null)
|
||||
if (addToFieldCache)
|
||||
{
|
||||
if (_fieldCache == null)
|
||||
_fieldCache = Index.buildCaseSensitiveMutableVisibleAsciiAlphabet(getHeaderCacheSize());
|
||||
|
||||
if (_field == null)
|
||||
_field = new HttpField(_header, caseInsensitiveHeader(_headerString, _header.asString()), _valueString);
|
||||
if (_field.getValue().length() < getHeaderCacheSize() && !_fieldCache.put(_field))
|
||||
{
|
||||
_fieldCache.clear();
|
||||
_fieldCache.put(_field);
|
||||
}
|
||||
|
||||
_fieldCache.add(_field);
|
||||
}
|
||||
}
|
||||
_handler.parsedHeader(_field != null ? _field : new HttpField(_header, _headerString, _valueString));
|
||||
|
@ -1244,7 +1240,7 @@ public class HttpParser
|
|||
if (buffer.hasRemaining())
|
||||
{
|
||||
// Try a look ahead for the known header name and value.
|
||||
HttpField cachedField = _fieldCache == null ? null : _fieldCache.getBest(buffer, -1, buffer.remaining());
|
||||
HttpField cachedField = _fieldCache.getBest(buffer, -1, buffer.remaining());
|
||||
if (cachedField == null)
|
||||
cachedField = CACHE.getBest(buffer, -1, buffer.remaining());
|
||||
|
||||
|
@ -1911,7 +1907,8 @@ public class HttpParser
|
|||
|
||||
public Index<HttpField> getFieldCache()
|
||||
{
|
||||
return _fieldCache;
|
||||
_fieldCache.prepare();
|
||||
return _fieldCache.getCache();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -2007,4 +2004,87 @@ public class HttpParser
|
|||
LOG.debug(String.format("Illegal character %s in state=%s for buffer %s", token, state, BufferUtil.toDetailString(buffer)));
|
||||
}
|
||||
}
|
||||
|
||||
private static class FieldCache
|
||||
{
|
||||
private int _size = 1024;
|
||||
private Index.Mutable<HttpField> _cache;
|
||||
private List<HttpField> _cacheableFields;
|
||||
private boolean _caseSensitive;
|
||||
|
||||
public int getCapacity()
|
||||
{
|
||||
return _size;
|
||||
}
|
||||
|
||||
public void setCapacity(int size)
|
||||
{
|
||||
_size = size;
|
||||
if (_size <= 0)
|
||||
_cache = NO_CACHE;
|
||||
else
|
||||
_cache = null;
|
||||
}
|
||||
|
||||
public boolean isCaseSensitive()
|
||||
{
|
||||
return _caseSensitive;
|
||||
}
|
||||
|
||||
public void setCaseSensitive(boolean caseSensitive)
|
||||
{
|
||||
_caseSensitive = caseSensitive;
|
||||
}
|
||||
|
||||
public boolean isEnabled()
|
||||
{
|
||||
return _cache != NO_CACHE;
|
||||
}
|
||||
|
||||
public Index<HttpField> getCache()
|
||||
{
|
||||
return _cache;
|
||||
}
|
||||
|
||||
public HttpField getBest(ByteBuffer buffer, int i, int remaining)
|
||||
{
|
||||
Index.Mutable<HttpField> cache = _cache;
|
||||
return cache == null ? null : _cache.getBest(buffer, i, remaining);
|
||||
}
|
||||
|
||||
public void add(HttpField field)
|
||||
{
|
||||
if (_cache == null)
|
||||
{
|
||||
if (_cacheableFields == null)
|
||||
_cacheableFields = new ArrayList<>();
|
||||
_cacheableFields.add(field);
|
||||
}
|
||||
else if (!_cache.put(field))
|
||||
{
|
||||
_cache.clear();
|
||||
_cache.put(field);
|
||||
}
|
||||
}
|
||||
|
||||
public boolean cacheable(HttpHeader header, String valueString)
|
||||
{
|
||||
return isEnabled() && header != null && valueString.length() <= _size;
|
||||
}
|
||||
|
||||
private void prepare()
|
||||
{
|
||||
if (_cache == null && _cacheableFields != null)
|
||||
{
|
||||
_cache = Index.buildMutableVisibleAsciiAlphabet(_caseSensitive, _size);
|
||||
for (HttpField f : _cacheableFields)
|
||||
{
|
||||
if (!_cache.put(f))
|
||||
break;
|
||||
}
|
||||
_cacheableFields.clear();
|
||||
_cacheableFields = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -259,13 +259,13 @@ public interface Index<V>
|
|||
* @param <V> The type of the index
|
||||
* @return A case sensitive mutable Index tacking visible ASCII alphabet to a max capacity.
|
||||
*/
|
||||
static <V> Mutable<V> buildCaseSensitiveMutableVisibleAsciiAlphabet(int maxCapacity)
|
||||
static <V> Mutable<V> buildMutableVisibleAsciiAlphabet(boolean caseSensitive, int maxCapacity)
|
||||
{
|
||||
if (maxCapacity < 0 || maxCapacity > ArrayTrie.MAX_CAPACITY)
|
||||
return new TreeTrie<>(true);
|
||||
return new TreeTrie<>(caseSensitive);
|
||||
if (maxCapacity == 0)
|
||||
return EmptyTrie.instance(true);
|
||||
return new ArrayTrie<>(true, maxCapacity);
|
||||
return EmptyTrie.instance(caseSensitive);
|
||||
return new ArrayTrie<>(caseSensitive, maxCapacity);
|
||||
}
|
||||
|
||||
static <V> Index<V> empty(boolean caseSensitive)
|
||||
|
|
Loading…
Reference in New Issue