protected static OptionMap getOptions(ModelNode properties) { final OptionMap optionMap; if (properties.isDefined() && properties.asInt() > 0) { OptionMap.Builder builder = OptionMap.builder(); final ClassLoader loader = SecurityActions.getClassLoader(ConnectorResource.class); for (Property property : properties.asPropertyList()) { String name = property.getName(); if (!name.contains(".")) { name = "org.xnio.Options." + name; } final Option option = Option.fromString(name, loader); builder.set( option, option.parseValue(property.getValue().get(CommonAttributes.VALUE).asString(), loader)); } optionMap = builder.getMap(); } else { optionMap = OptionMap.EMPTY; } return optionMap; }
/** {@inheritDoc} */ @Override public <T> T getOption(final Option<T> option) throws IOException { if (option == Options.SSL_CLIENT_AUTH_MODE) { return option.cast( engine.getNeedClientAuth() ? SslClientAuthMode.REQUIRED : engine.getWantClientAuth() ? SslClientAuthMode.REQUESTED : SslClientAuthMode.NOT_REQUESTED); } else { return option == Options.SECURE ? (T) Boolean.TRUE : delegate.getOption(option); } }
/** {@inheritDoc} */ @Override public <T> T setOption(final Option<T> option, final T value) throws IllegalArgumentException, IOException { if (option == Options.SSL_CLIENT_AUTH_MODE) { try { return option.cast( engine.getNeedClientAuth() ? SslClientAuthMode.REQUIRED : engine.getWantClientAuth() ? SslClientAuthMode.REQUESTED : SslClientAuthMode.NOT_REQUESTED); } finally { engine.setNeedClientAuth(value == SslClientAuthMode.REQUIRED); engine.setWantClientAuth(value == SslClientAuthMode.REQUESTED); } } else if (option == Options.SECURE) { throw new IllegalArgumentException(); } else { return delegate.setOption(option, value); } }
/** @author Stuart Douglas */ public class UndertowOptions { /** The maximum size in bytes of a http request header. */ public static final Option<Integer> MAX_HEADER_SIZE = Option.simple(UndertowOptions.class, "MAX_HEADER_SIZE", Integer.class); /** The default size we allow for the HTTP header. */ public static final int DEFAULT_MAX_HEADER_SIZE = 50 * 1024; /** The default maximum size of the HTTP entity body. */ public static final Option<Long> MAX_ENTITY_SIZE = Option.simple(UndertowOptions.class, "MAX_ENTITY_SIZE", Long.class); /** We do not have a default upload limit */ public static final long DEFAULT_MAX_ENTITY_SIZE = -1; /** If we should buffer pipelined requests. Defaults to false. */ public static final Option<Boolean> BUFFER_PIPELINED_DATA = Option.simple(UndertowOptions.class, "BUFFER_PIPELINED_DATA", Boolean.class); /** The idle timeout in milliseconds after which the channel will be closed. */ public static final Option<Long> IDLE_TIMEOUT = Option.simple(UndertowOptions.class, "IDLE_TIMEOUT", Long.class); /** * The maximum number of parameters that will be parsed. This is used to protect against hash * vulnerabilities. * * <p>This applies to both query parameters, and to POST data, but is not cumulative (i.e. you can * potentially have max parameters * 2 total parameters). * * <p>Defaults to 1000 */ public static final Option<Integer> MAX_PARAMETERS = Option.simple(UndertowOptions.class, "MAX_PARAMETERS", Integer.class); /** * The maximum number of headers that will be parsed. This is used to protect against hash * vulnerabilities. * * <p>Defaults to 200 */ public static final Option<Integer> MAX_HEADERS = Option.simple(UndertowOptions.class, "MAX_HEADERS", Integer.class); /** * The maximum number of cookies that will be parsed. This is used to protect against hash * vulnerabilities. * * <p>Defaults to 200 */ public static final Option<Integer> MAX_COOKIES = Option.simple(UndertowOptions.class, "MAX_COOKIES", Integer.class); /** * If a request comes in with encoded / characters (i.e. %2F), will these be decoded. * * <p>This can cause security problems if a front end proxy does not perform the same decoding, * and as a result this is disabled by default. * * <p>Defaults to false * * @see http://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2007-0450 */ public static final Option<Boolean> ALLOW_ENCODED_SLASH = Option.simple(UndertowOptions.class, "ALLOW_ENCODED_SLASH", Boolean.class); /** * If this is true then the parser will decode the URL and query parameters using the selected * character encoding (UTF-8 by default). If this is false they will not be decoded. This will * allow a later handler to decode them into whatever charset is desired. * * <p>Defaults to true. */ public static final Option<Boolean> DECODE_URL = Option.simple(UndertowOptions.class, "DECODE_URL", Boolean.class); /** * If this is true then the parser will decode the URL and query parameters using the selected * character encoding (UTF-8 by default). If this is false they will not be decoded. This will * allow a later handler to decode them into whatever charset is desired. * * <p>Defaults to true. */ public static final Option<String> URL_CHARSET = Option.simple(UndertowOptions.class, "URL_CHARSET", String.class); /** * If this is true then a Connection: keep-alive header will be added to responses, even when it * is not strictly required by the specification. * * <p>Defaults to true */ public static final Option<Boolean> ALWAYS_SET_KEEP_ALIVE = Option.simple(UndertowOptions.class, "ALWAYS_SET_KEEP_ALIVE", Boolean.class); /** * If this is true then a Date header will be added to all responses. The HTTP spec says this * header should be added to all responses, unless the server does not have an accurate clock. * * <p>Defaults to true */ public static final Option<Boolean> ALWAYS_SET_DATE = Option.simple(UndertowOptions.class, "ALWAYS_SET_DATE", Boolean.class); /** * Maximum size of a buffered request, in bytes * * <p>Requests are not usually buffered, the most common case is when performing SSL renegotiation * for a POST request, and the post data must be fully buffered in order to perform the * renegotiation. * * <p>Defaults to 16384. */ public static final Option<Integer> MAX_BUFFERED_REQUEST_SIZE = Option.simple(UndertowOptions.class, "MAX_BUFFERED_REQUEST_SIZE", Integer.class); /** * If this is true then Undertow will record the request start time, to allow for request time to * be logged * * <p>This has a small but measurable performance impact * * <p>default is false */ public static final Option<Boolean> RECORD_REQUEST_START_TIME = Option.simple(UndertowOptions.class, "RECORD_REQUEST_START_TIME", Boolean.class); /** * If this is true then Undertow will allow non-escaped equals characters in unquoted cookie * values. * * <p>Unquoted cookie values may not contain equals characters. If present the value ends before * the equals sign. The remainder of the cookie value will be dropped. * * <p>default is false */ public static final Option<Boolean> ALLOW_EQUALS_IN_COOKIE_VALUE = Option.simple(UndertowOptions.class, "ALLOW_EQUALS_IN_COOKIE_VALUE", Boolean.class); private UndertowOptions() {} }
/** @author Stuart Douglas */ class UndertowSslConnection extends SslConnection { private static final Set<Option<?>> SUPPORTED_OPTIONS = Option.setBuilder().add(Options.SECURE, Options.SSL_CLIENT_AUTH_MODE).create(); private final StreamConnection delegate; private final SslConduit sslConduit; private final ChannelListener.SimpleSetter<SslConnection> handshakeSetter = new ChannelListener.SimpleSetter<>(); private final SSLEngine engine; /** * Construct a new instance. * * @param delegate the underlying connection */ UndertowSslConnection(StreamConnection delegate, SSLEngine engine, ByteBufferPool bufferPool) { super(delegate.getIoThread()); this.delegate = delegate; this.engine = engine; sslConduit = new SslConduit(this, delegate, engine, bufferPool, new HandshakeCallback()); setSourceConduit(sslConduit); setSinkConduit(sslConduit); } @Override public void startHandshake() throws IOException { sslConduit.startHandshake(); } @Override public SSLSession getSslSession() { return sslConduit.getSslSession(); } @Override public ChannelListener.Setter<? extends SslConnection> getHandshakeSetter() { return handshakeSetter; } @Override protected void notifyWriteClosed() { sslConduit.notifyWriteClosed(); } @Override protected void notifyReadClosed() { sslConduit.notifyReadClosed(); } @Override public SocketAddress getPeerAddress() { return delegate.getPeerAddress(); } @Override public SocketAddress getLocalAddress() { return delegate.getLocalAddress(); } public SSLEngine getSSLEngine() { return sslConduit.getSSLEngine(); } SslConduit getSslConduit() { return sslConduit; } /** {@inheritDoc} */ @Override public <T> T setOption(final Option<T> option, final T value) throws IllegalArgumentException, IOException { if (option == Options.SSL_CLIENT_AUTH_MODE) { try { return option.cast( engine.getNeedClientAuth() ? SslClientAuthMode.REQUIRED : engine.getWantClientAuth() ? SslClientAuthMode.REQUESTED : SslClientAuthMode.NOT_REQUESTED); } finally { engine.setNeedClientAuth(value == SslClientAuthMode.REQUIRED); engine.setWantClientAuth(value == SslClientAuthMode.REQUESTED); } } else if (option == Options.SECURE) { throw new IllegalArgumentException(); } else { return delegate.setOption(option, value); } } /** {@inheritDoc} */ @Override public <T> T getOption(final Option<T> option) throws IOException { if (option == Options.SSL_CLIENT_AUTH_MODE) { return option.cast( engine.getNeedClientAuth() ? SslClientAuthMode.REQUIRED : engine.getWantClientAuth() ? SslClientAuthMode.REQUESTED : SslClientAuthMode.NOT_REQUESTED); } else { return option == Options.SECURE ? (T) Boolean.TRUE : delegate.getOption(option); } } /** {@inheritDoc} */ @Override public boolean supportsOption(final Option<?> option) { return SUPPORTED_OPTIONS.contains(option) || delegate.supportsOption(option); } @Override protected boolean readClosed() { return super.readClosed(); } @Override protected boolean writeClosed() { return super.writeClosed(); } protected void closeAction() { sslConduit.close(); } private final class HandshakeCallback implements Runnable { @Override public void run() { final ChannelListener<? super SslConnection> listener = handshakeSetter.get(); if (listener == null) { return; } ChannelListeners.<SslConnection>invokeChannelListener(UndertowSslConnection.this, listener); } } }