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);
   }
 }
Exemple #4
0
/** @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);
    }
  }
}