Merge remote-tracking branch 'origin/jetty-9.4.x' into jetty-10.0.x

Signed-off-by: Greg Wilkins <gregw@webtide.com>
This commit is contained in:
Greg Wilkins 2020-04-28 10:29:41 +02:00
commit eacb79f4e7
10 changed files with 266 additions and 173 deletions

View File

@ -19,9 +19,8 @@
package org.eclipse.jetty.server; package org.eclipse.jetty.server;
import java.io.IOException; import java.io.IOException;
import java.util.Collections;
import java.util.Enumeration;
import java.util.HashSet; import java.util.HashSet;
import java.util.Set;
import javax.servlet.DispatcherType; import javax.servlet.DispatcherType;
import javax.servlet.RequestDispatcher; import javax.servlet.RequestDispatcher;
import javax.servlet.ServletException; import javax.servlet.ServletException;
@ -256,10 +255,8 @@ public class Dispatcher implements RequestDispatcher
return String.format("Dispatcher@0x%x{%s,%s}", hashCode(), _named, _uri); return String.format("Dispatcher@0x%x{%s,%s}", hashCode(), _named, _uri);
} }
private class ForwardAttributes implements Attributes private class ForwardAttributes extends Attributes.Wrapper
{ {
final Attributes _attr;
String _requestURI; String _requestURI;
String _contextPath; String _contextPath;
String _servletPath; String _servletPath;
@ -269,7 +266,7 @@ public class Dispatcher implements RequestDispatcher
ForwardAttributes(Attributes attributes) ForwardAttributes(Attributes attributes)
{ {
_attr = attributes; super(attributes);
} }
@Override @Override
@ -277,34 +274,37 @@ public class Dispatcher implements RequestDispatcher
{ {
if (Dispatcher.this._named == null) if (Dispatcher.this._named == null)
{ {
if (key.equals(FORWARD_PATH_INFO)) switch (key)
{
case FORWARD_PATH_INFO:
return _pathInfo; return _pathInfo;
if (key.equals(FORWARD_REQUEST_URI)) case FORWARD_REQUEST_URI:
return _requestURI; return _requestURI;
if (key.equals(FORWARD_SERVLET_PATH)) case FORWARD_SERVLET_PATH:
return _servletPath; return _servletPath;
if (key.equals(FORWARD_CONTEXT_PATH)) case FORWARD_CONTEXT_PATH:
return _contextPath; return _contextPath;
if (key.equals(FORWARD_QUERY_STRING)) case FORWARD_QUERY_STRING:
return _query; return _query;
if (key.equals(FORWARD_MAPPING)) case FORWARD_MAPPING:
return _mapping; return _mapping;
default:
break;
}
} }
if (key.startsWith(__INCLUDE_PREFIX)) if (key.startsWith(__INCLUDE_PREFIX))
return null; return null;
return _attr.getAttribute(key); return _attributes.getAttribute(key);
} }
@Override @Override
public Enumeration<String> getAttributeNames() public Set<String> getAttributeNameSet()
{ {
HashSet<String> set = new HashSet<>(); HashSet<String> set = new HashSet<>();
Enumeration<String> e = _attr.getAttributeNames(); for (String name : _attributes.getAttributeNameSet())
while (e.hasMoreElements())
{ {
String name = e.nextElement();
if (!name.startsWith(__INCLUDE_PREFIX) && if (!name.startsWith(__INCLUDE_PREFIX) &&
!name.startsWith(__FORWARD_PREFIX)) !name.startsWith(__FORWARD_PREFIX))
set.add(name); set.add(name);
@ -326,7 +326,7 @@ public class Dispatcher implements RequestDispatcher
set.remove(FORWARD_QUERY_STRING); set.remove(FORWARD_QUERY_STRING);
} }
return Collections.enumeration(set); return set;
} }
@Override @Override
@ -334,33 +334,43 @@ public class Dispatcher implements RequestDispatcher
{ {
if (_named == null && key.startsWith("javax.servlet.")) if (_named == null && key.startsWith("javax.servlet."))
{ {
if (key.equals(FORWARD_PATH_INFO)) switch (key)
{
case FORWARD_PATH_INFO:
_pathInfo = (String)value; _pathInfo = (String)value;
else if (key.equals(FORWARD_REQUEST_URI)) return;
case FORWARD_REQUEST_URI:
_requestURI = (String)value; _requestURI = (String)value;
else if (key.equals(FORWARD_SERVLET_PATH)) return;
case FORWARD_SERVLET_PATH:
_servletPath = (String)value; _servletPath = (String)value;
else if (key.equals(FORWARD_CONTEXT_PATH)) return;
case FORWARD_CONTEXT_PATH:
_contextPath = (String)value; _contextPath = (String)value;
else if (key.equals(FORWARD_QUERY_STRING)) return;
case FORWARD_QUERY_STRING:
_query = (String)value; _query = (String)value;
else if (key.equals(FORWARD_MAPPING)) return;
case FORWARD_MAPPING:
_mapping = (HttpServletMapping)value; _mapping = (HttpServletMapping)value;
else if (value == null) return;
_attr.removeAttribute(key); default:
if (value == null)
_attributes.removeAttribute(key);
else else
_attr.setAttribute(key, value); _attributes.setAttribute(key, value);
}
} }
else if (value == null) else if (value == null)
_attr.removeAttribute(key); _attributes.removeAttribute(key);
else else
_attr.setAttribute(key, value); _attributes.setAttribute(key, value);
} }
@Override @Override
public String toString() public String toString()
{ {
return "FORWARD+" + _attr.toString(); return "FORWARD+" + _attributes.toString();
} }
@Override @Override
@ -376,10 +386,8 @@ public class Dispatcher implements RequestDispatcher
} }
} }
private class IncludeAttributes implements Attributes private class IncludeAttributes extends Attributes.Wrapper
{ {
final Attributes _attr;
String _requestURI; String _requestURI;
String _contextPath; String _contextPath;
String _servletPath; String _servletPath;
@ -389,7 +397,7 @@ public class Dispatcher implements RequestDispatcher
IncludeAttributes(Attributes attributes) IncludeAttributes(Attributes attributes)
{ {
_attr = attributes; super(attributes);
} }
@Override @Override
@ -397,33 +405,36 @@ public class Dispatcher implements RequestDispatcher
{ {
if (Dispatcher.this._named == null) if (Dispatcher.this._named == null)
{ {
if (key.equals(INCLUDE_PATH_INFO)) switch (key)
{
case INCLUDE_PATH_INFO:
return _pathInfo; return _pathInfo;
if (key.equals(INCLUDE_SERVLET_PATH)) case INCLUDE_SERVLET_PATH:
return _servletPath; return _servletPath;
if (key.equals(INCLUDE_CONTEXT_PATH)) case INCLUDE_CONTEXT_PATH:
return _contextPath; return _contextPath;
if (key.equals(INCLUDE_QUERY_STRING)) case INCLUDE_QUERY_STRING:
return _query; return _query;
if (key.equals(INCLUDE_REQUEST_URI)) case INCLUDE_REQUEST_URI:
return _requestURI; return _requestURI;
if (key.equals(INCLUDE_MAPPING)) case INCLUDE_MAPPING:
return _mapping; return _mapping;
default:
break;
}
} }
else if (key.startsWith(__INCLUDE_PREFIX)) else if (key.startsWith(__INCLUDE_PREFIX))
return null; return null;
return _attr.getAttribute(key); return _attributes.getAttribute(key);
} }
@Override @Override
public Enumeration<String> getAttributeNames() public Set<String> getAttributeNameSet()
{ {
HashSet<String> set = new HashSet<>(); HashSet<String> set = new HashSet<>();
Enumeration<String> e = _attr.getAttributeNames(); for (String name : _attributes.getAttributeNameSet())
while (e.hasMoreElements())
{ {
String name = e.nextElement();
if (!name.startsWith(__INCLUDE_PREFIX)) if (!name.startsWith(__INCLUDE_PREFIX))
set.add(name); set.add(name);
} }
@ -444,7 +455,7 @@ public class Dispatcher implements RequestDispatcher
set.remove(INCLUDE_QUERY_STRING); set.remove(INCLUDE_QUERY_STRING);
} }
return Collections.enumeration(set); return set;
} }
@Override @Override
@ -452,33 +463,43 @@ public class Dispatcher implements RequestDispatcher
{ {
if (_named == null && key.startsWith("javax.servlet.")) if (_named == null && key.startsWith("javax.servlet."))
{ {
if (key.equals(INCLUDE_PATH_INFO)) switch (key)
{
case INCLUDE_PATH_INFO:
_pathInfo = (String)value; _pathInfo = (String)value;
else if (key.equals(INCLUDE_REQUEST_URI)) return;
case INCLUDE_REQUEST_URI:
_requestURI = (String)value; _requestURI = (String)value;
else if (key.equals(INCLUDE_SERVLET_PATH)) return;
case INCLUDE_SERVLET_PATH:
_servletPath = (String)value; _servletPath = (String)value;
else if (key.equals(INCLUDE_CONTEXT_PATH)) return;
case INCLUDE_CONTEXT_PATH:
_contextPath = (String)value; _contextPath = (String)value;
else if (key.equals(INCLUDE_QUERY_STRING)) return;
case INCLUDE_QUERY_STRING:
_query = (String)value; _query = (String)value;
else if (key.equals(INCLUDE_MAPPING)) return;
case INCLUDE_MAPPING:
_mapping = (HttpServletMapping)value; _mapping = (HttpServletMapping)value;
else if (value == null) return;
_attr.removeAttribute(key); default:
if (value == null)
_attributes.removeAttribute(key);
else else
_attr.setAttribute(key, value); _attributes.setAttribute(key, value);
}
} }
else if (value == null) else if (value == null)
_attr.removeAttribute(key); _attributes.removeAttribute(key);
else else
_attr.setAttribute(key, value); _attributes.setAttribute(key, value);
} }
@Override @Override
public String toString() public String toString()
{ {
return "INCLUDE+" + _attr.toString(); return "INCLUDE+" + _attributes.toString();
} }
@Override @Override

View File

@ -1808,8 +1808,14 @@ public class Request implements HttpServletRequest
_async = null; _async = null;
_asyncNotSupportedSource = null; _asyncNotSupportedSource = null;
_handled = false; _handled = false;
_attributes = Attributes.unwrap(_attributes);
if (_attributes != null) if (_attributes != null)
{
if (AttributesMap.class.equals(_attributes.getClass()))
_attributes.clearAttributes(); _attributes.clearAttributes();
else
_attributes = null;
}
_contentType = null; _contentType = null;
_characterEncoding = null; _characterEncoding = null;
_contextPath = null; _contextPath = null;

View File

@ -19,6 +19,8 @@
package org.eclipse.jetty.server; package org.eclipse.jetty.server;
import java.security.cert.X509Certificate; import java.security.cert.X509Certificate;
import java.util.HashSet;
import java.util.Set;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import javax.net.ssl.SSLContext; import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLEngine; import javax.net.ssl.SSLEngine;
@ -33,6 +35,8 @@ import org.eclipse.jetty.http.PreEncodedHttpField;
import org.eclipse.jetty.io.EndPoint; import org.eclipse.jetty.io.EndPoint;
import org.eclipse.jetty.io.ssl.SslConnection; import org.eclipse.jetty.io.ssl.SslConnection;
import org.eclipse.jetty.io.ssl.SslConnection.DecryptedEndPoint; import org.eclipse.jetty.io.ssl.SslConnection.DecryptedEndPoint;
import org.eclipse.jetty.util.Attributes;
import org.eclipse.jetty.util.StringUtil;
import org.eclipse.jetty.util.TypeUtil; import org.eclipse.jetty.util.TypeUtil;
import org.eclipse.jetty.util.annotation.Name; import org.eclipse.jetty.util.annotation.Name;
import org.eclipse.jetty.util.ssl.SniX509ExtendedKeyManager; import org.eclipse.jetty.util.ssl.SniX509ExtendedKeyManager;
@ -49,11 +53,10 @@ import org.slf4j.LoggerFactory;
public class SecureRequestCustomizer implements HttpConfiguration.Customizer public class SecureRequestCustomizer implements HttpConfiguration.Customizer
{ {
private static final Logger LOG = LoggerFactory.getLogger(SecureRequestCustomizer.class); private static final Logger LOG = LoggerFactory.getLogger(SecureRequestCustomizer.class);
public static final String JAVAX_SERVLET_REQUEST_X_509_CERTIFICATE = "javax.servlet.request.X509Certificate";
/** public static final String JAVAX_SERVLET_REQUEST_CIPHER_SUITE = "javax.servlet.request.cipher_suite";
* The name of the SSLSession attribute that will contain any cached information. public static final String JAVAX_SERVLET_REQUEST_KEY_SIZE = "javax.servlet.request.key_size";
*/ public static final String JAVAX_SERVLET_REQUEST_SSL_SESSION_ID = "javax.servlet.request.ssl_session_id";
public static final String CACHED_INFO_ATTR = CachedInfo.class.getName();
private String sslSessionAttribute = "org.eclipse.jetty.servlet.request.ssl_session"; private String sslSessionAttribute = "org.eclipse.jetty.servlet.request.ssl_session";
@ -245,61 +248,22 @@ public class SecureRequestCustomizer implements HttpConfiguration.Customizer
if (_sniHostCheck || _sniRequired) if (_sniHostCheck || _sniRequired)
{ {
String name = request.getServerName();
X509 x509 = (X509)sslSession.getValue(SniX509ExtendedKeyManager.SNI_X509); X509 x509 = (X509)sslSession.getValue(SniX509ExtendedKeyManager.SNI_X509);
if (LOG.isDebugEnabled()) if (LOG.isDebugEnabled())
LOG.debug("Host {} with SNI {}", name, x509); LOG.debug("Host {} with SNI {}", request.getServerName(), x509);
if (x509 == null) if (x509 == null)
{ {
if (_sniRequired) if (_sniRequired)
throw new BadMessageException(400, "SNI required"); throw new BadMessageException(400, "SNI required");
} }
else if (_sniHostCheck && !x509.matches(name)) else if (_sniHostCheck && !x509.matches(request.getServerName()))
{ {
throw new BadMessageException(400, "Host does not match SNI"); throw new BadMessageException(400, "Host does not match SNI");
} }
} }
try request.setAttributes(new SslAttributes(request, sslSession, request.getAttributes()));
{
String cipherSuite = sslSession.getCipherSuite();
Integer keySize;
X509Certificate[] certs;
String idStr;
CachedInfo cachedInfo = (CachedInfo)sslSession.getValue(CACHED_INFO_ATTR);
if (cachedInfo != null)
{
keySize = cachedInfo.getKeySize();
certs = cachedInfo.getCerts();
idStr = cachedInfo.getIdStr();
}
else
{
keySize = SslContextFactory.deduceKeyLength(cipherSuite);
certs = getCertChain(request, sslSession);
byte[] bytes = sslSession.getId();
idStr = TypeUtil.toHexString(bytes);
cachedInfo = new CachedInfo(keySize, certs, idStr);
sslSession.putValue(CACHED_INFO_ATTR, cachedInfo);
}
if (certs != null)
request.setAttribute("javax.servlet.request.X509Certificate", certs);
request.setAttribute("javax.servlet.request.cipher_suite", cipherSuite);
request.setAttribute("javax.servlet.request.key_size", keySize);
request.setAttribute("javax.servlet.request.ssl_session_id", idStr);
String sessionAttribute = getSslSessionAttribute();
if (sessionAttribute != null && !sessionAttribute.isEmpty())
request.setAttribute(sessionAttribute, sslSession);
}
catch (Exception e)
{
LOG.warn("Unable to customize request with encryption details", e);
}
} }
/** /**
@ -326,11 +290,9 @@ public class SecureRequestCustomizer implements HttpConfiguration.Customizer
if (sslConnectionFactory != null) if (sslConnectionFactory != null)
{ {
SslContextFactory sslContextFactory = sslConnectionFactory.getSslContextFactory(); SslContextFactory sslContextFactory = sslConnectionFactory.getSslContextFactory();
if (sslConnectionFactory != null) if (sslContextFactory != null)
{
return sslContextFactory.getX509CertChain(sslSession); return sslContextFactory.getX509CertChain(sslSession);
} }
}
// Fallback, either no SslConnectionFactory or no SslContextFactory instance found // Fallback, either no SslConnectionFactory or no SslContextFactory instance found
return SslContextFactory.getCertChain(sslSession); return SslContextFactory.getCertChain(sslSession);
@ -352,36 +314,66 @@ public class SecureRequestCustomizer implements HttpConfiguration.Customizer
return String.format("%s@%x", this.getClass().getSimpleName(), hashCode()); return String.format("%s@%x", this.getClass().getSimpleName(), hashCode());
} }
/** private class SslAttributes extends Attributes.Wrapper
* Simple bundle of information that is cached in the SSLSession. Stores the
* effective keySize and the client certificate chain.
*/
private static class CachedInfo
{ {
private final X509Certificate[] _certs; private final Request _request;
private final Integer _keySize; private final SSLSession _session;
private final String _idStr;
CachedInfo(Integer keySize, X509Certificate[] certs, String idStr) public SslAttributes(Request request, SSLSession sslSession, Attributes attributes)
{ {
this._keySize = keySize; super(attributes);
this._certs = certs; this._request = request;
this._idStr = idStr; this._session = sslSession;
} }
X509Certificate[] getCerts() @Override
public Object getAttribute(String name)
{ {
return _certs; Object value = _attributes.getAttribute(name);
if (value != null)
return value;
try
{
switch (name)
{
case JAVAX_SERVLET_REQUEST_X_509_CERTIFICATE:
return SecureRequestCustomizer.this.getCertChain(_request, _session);
case JAVAX_SERVLET_REQUEST_CIPHER_SUITE:
return _session.getCipherSuite();
case JAVAX_SERVLET_REQUEST_KEY_SIZE:
return SslContextFactory.deduceKeyLength(_session.getCipherSuite());
case JAVAX_SERVLET_REQUEST_SSL_SESSION_ID:
return TypeUtil.toHexString(_session.getId());
default:
String sessionAttribute = getSslSessionAttribute();
if (!StringUtil.isEmpty(sessionAttribute) && sessionAttribute.equals(name))
return _session;
}
}
catch (Exception e)
{
if (LOG.isDebugEnabled())
LOG.debug("Unable to get secure details ", e);
}
return null;
} }
Integer getKeySize() @Override
public Set<String> getAttributeNameSet()
{ {
return _keySize; Set<String> names = new HashSet<>(_attributes.getAttributeNameSet());
} names.add(JAVAX_SERVLET_REQUEST_X_509_CERTIFICATE);
names.add(JAVAX_SERVLET_REQUEST_CIPHER_SUITE);
String getIdStr() names.add(JAVAX_SERVLET_REQUEST_KEY_SIZE);
{ names.add(JAVAX_SERVLET_REQUEST_SSL_SESSION_ID);
return _idStr; String sessionAttribute = getSslSessionAttribute();
if (!StringUtil.isEmpty(sessionAttribute))
names.add(sessionAttribute);
return names;
} }
} }
} }

View File

@ -26,6 +26,7 @@ import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.Enumeration; import java.util.Enumeration;
import java.util.List; import java.util.List;
import java.util.Set;
import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import javax.servlet.ServletContext; import javax.servlet.ServletContext;
@ -684,6 +685,12 @@ public class Server extends HandlerWrapper implements Attributes
return _attributes.getAttributeNames(); return _attributes.getAttributeNames();
} }
@Override
public Set<String> getAttributeNameSet()
{
return _attributes.getAttributeNameSet();
}
@Override @Override
public void removeAttribute(String name) public void removeAttribute(String name)
{ {

View File

@ -502,6 +502,12 @@ public class ContextHandler extends ScopedHandler implements Attributes, Gracefu
return AttributesMap.getAttributeNamesCopy(_attributes); return AttributesMap.getAttributeNamesCopy(_attributes);
} }
@Override
public Set<String> getAttributeNameSet()
{
return _attributes.getAttributeNameSet();
}
/** /**
* @return Returns the attributes. * @return Returns the attributes.
*/ */

View File

@ -18,7 +18,6 @@
package org.eclipse.jetty.server.handler; package org.eclipse.jetty.server.handler;
import java.util.Enumeration;
import java.util.HashSet; import java.util.HashSet;
import java.util.Set; import java.util.Set;
import javax.servlet.ServletContextAttributeEvent; import javax.servlet.ServletContextAttributeEvent;
@ -77,10 +76,8 @@ public class ManagedAttributeListener implements ServletContextListener, Servlet
public void contextInitialized(ServletContextEvent event) public void contextInitialized(ServletContextEvent event)
{ {
// Update existing attributes // Update existing attributes
Enumeration<String> e = event.getServletContext().getAttributeNames(); for (String name : _context.getServletContext().getAttributeNameSet())
while (e.hasMoreElements())
{ {
String name = e.nextElement();
if (_managedAttributes.contains(name)) if (_managedAttributes.contains(name))
updateBean(name, null, event.getServletContext().getAttribute(name)); updateBean(name, null, event.getServletContext().getAttribute(name));
} }
@ -89,10 +86,8 @@ public class ManagedAttributeListener implements ServletContextListener, Servlet
@Override @Override
public void contextDestroyed(ServletContextEvent event) public void contextDestroyed(ServletContextEvent event)
{ {
Enumeration<String> e = _context.getServletContext().getAttributeNames(); for (String name : _context.getServletContext().getAttributeNameSet())
while (e.hasMoreElements())
{ {
String name = e.nextElement();
if (_managedAttributes.contains(name)) if (_managedAttributes.contains(name))
updateBean(name, event.getServletContext().getAttribute(name), null); updateBean(name, event.getServletContext().getAttribute(name), null);
} }

View File

@ -18,7 +18,6 @@
package org.eclipse.jetty.server.handler.jmx; package org.eclipse.jetty.server.handler.jmx;
import java.util.Enumeration;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
@ -42,10 +41,8 @@ public class ContextHandlerMBean extends AbstractHandlerMBean
{ {
Map<String, Object> map = new HashMap<String, Object>(); Map<String, Object> map = new HashMap<String, Object>();
Attributes attrs = ((ContextHandler)_managed).getAttributes(); Attributes attrs = ((ContextHandler)_managed).getAttributes();
Enumeration<String> en = attrs.getAttributeNames(); for (String name : attrs.getAttributeNameSet())
while (en.hasMoreElements())
{ {
String name = (String)en.nextElement();
Object value = attrs.getAttribute(name); Object value = attrs.getAttribute(name);
map.put(name, value); map.put(name, value);
} }

View File

@ -18,7 +18,9 @@
package org.eclipse.jetty.util; package org.eclipse.jetty.util;
import java.util.Collections;
import java.util.Enumeration; import java.util.Enumeration;
import java.util.Set;
/** /**
* Attributes. * Attributes.
@ -26,13 +28,72 @@ import java.util.Enumeration;
*/ */
public interface Attributes public interface Attributes
{ {
public void removeAttribute(String name); void removeAttribute(String name);
public void setAttribute(String name, Object attribute); void setAttribute(String name, Object attribute);
public Object getAttribute(String name); Object getAttribute(String name);
public Enumeration<String> getAttributeNames(); Set<String> getAttributeNameSet();
public void clearAttributes(); default Enumeration<String> getAttributeNames()
{
return Collections.enumeration(getAttributeNameSet());
}
void clearAttributes();
static Attributes unwrap(Attributes attributes)
{
while (attributes instanceof Wrapper)
{
attributes = ((Wrapper)attributes).getAttributes();
}
return attributes;
}
abstract class Wrapper implements Attributes
{
protected final Attributes _attributes;
public Wrapper(Attributes attributes)
{
_attributes = attributes;
}
public Attributes getAttributes()
{
return _attributes;
}
@Override
public void removeAttribute(String name)
{
_attributes.removeAttribute(name);
}
@Override
public void setAttribute(String name, Object attribute)
{
_attributes.setAttribute(name, attribute);
}
@Override
public Object getAttribute(String name)
{
return _attributes.getAttribute(name);
}
@Override
public Set<String> getAttributeNameSet()
{
return _attributes.getAttributeNameSet();
}
@Override
public void clearAttributes()
{
_attributes.clearAttributes();
}
}
} }

View File

@ -94,6 +94,7 @@ public class AttributesMap implements Attributes, Dumpable
return Collections.enumeration(getAttributeNameSet()); return Collections.enumeration(getAttributeNameSet());
} }
@Override
public Set<String> getAttributeNameSet() public Set<String> getAttributeNameSet()
{ {
return keySet(); return keySet();

View File

@ -23,6 +23,7 @@ import java.util.Collections;
import java.util.Enumeration; import java.util.Enumeration;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
import java.util.Set;
import org.eclipse.jetty.util.Attributes; import org.eclipse.jetty.util.Attributes;
@ -60,6 +61,12 @@ public class AttributeContainerMap extends ContainerLifeCycle implements Attribu
return Collections.enumeration(_map.keySet()); return Collections.enumeration(_map.keySet());
} }
@Override
public Set<String> getAttributeNameSet()
{
return _map.keySet();
}
@Override @Override
public synchronized void clearAttributes() public synchronized void clearAttributes()
{ {