/*
* Copyright 2015 LINE Corporation
*
* LINE Corporation licenses this file to you under the Apache License,
* version 2.0 (the "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
package com.linecorp.armeria.client;
import static com.linecorp.armeria.client.SessionOption.ADDRESS_RESOLVER_GROUP;
import static com.linecorp.armeria.client.SessionOption.CONNECT_TIMEOUT;
import static com.linecorp.armeria.client.SessionOption.EVENT_LOOP_GROUP;
import static com.linecorp.armeria.client.SessionOption.IDLE_TIMEOUT;
import static com.linecorp.armeria.client.SessionOption.POOL_HANDLER_DECORATOR;
import static com.linecorp.armeria.client.SessionOption.TRUST_MANAGER_FACTORY;
import static com.linecorp.armeria.client.SessionOption.USE_HTTP1_PIPELINING;
import static com.linecorp.armeria.client.SessionOption.USE_HTTP2_PREFACE;
import static java.util.Objects.requireNonNull;
import java.net.InetSocketAddress;
import java.time.Duration;
import java.util.Map;
import java.util.Optional;
import java.util.function.Function;
import javax.net.ssl.TrustManagerFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.linecorp.armeria.client.pool.KeyedChannelPoolHandler;
import com.linecorp.armeria.client.pool.PoolKey;
import com.linecorp.armeria.common.util.AbstractOptions;
import io.netty.channel.EventLoopGroup;
import io.netty.resolver.AddressResolverGroup;
/**
* A set of {@link SessionOption}s and their respective values.
*/
public final class SessionOptions extends AbstractOptions {
private static final Logger logger = LoggerFactory.getLogger(SessionOptions.class);
private static final Duration DEFAULT_CONNECTION_TIMEOUT = Duration.ofMillis(3200);
private static final Duration DEFAULT_IDLE_TIMEOUT = Duration.ofSeconds(10);
private static final Boolean DEFAULT_USE_HTTP2_PREFACE =
"true".equals(System.getProperty("com.linecorp.armeria.defaultUseHttp2Preface", "false"));
private static final Boolean DEFAULT_USE_HTTP1_PIPELINING =
"true".equals(System.getProperty("com.linecorp.armeria.defaultUseHttp1Pipelining", "true"));
static {
logger.info("defaultUseHttp2Preface: {}", DEFAULT_USE_HTTP2_PREFACE);
}
private static final SessionOptionValue<?>[] DEFAULT_OPTION_VALUES = {
CONNECT_TIMEOUT.newValue(DEFAULT_CONNECTION_TIMEOUT),
IDLE_TIMEOUT.newValue(DEFAULT_IDLE_TIMEOUT),
USE_HTTP2_PREFACE.newValue(DEFAULT_USE_HTTP2_PREFACE)
};
/**
* The default {@link SessionOptions}.
*/
public static final SessionOptions DEFAULT = new SessionOptions(DEFAULT_OPTION_VALUES);
/**
* Creates a new {@link SessionOptions} with the specified {@link SessionOptionValue}s.
*/
public static SessionOptions of(SessionOptionValue<?>... options) {
return new SessionOptions(DEFAULT, options);
}
/**
* Returns the {@link SessionOptions} with the specified {@link SessionOptionValue}s.
*/
public static SessionOptions of(Iterable<SessionOptionValue<?>> options) {
return new SessionOptions(DEFAULT, options);
}
/**
* Merges the specified {@link SessionOptions} and {@link SessionOptionValue}s.
*
* @return the merged {@link SessionOptions}
*/
public static SessionOptions of(SessionOptions baseOptions, SessionOptionValue<?>... options) {
// TODO(trustin): Reduce the cost of creating a derived SessionOptions.
requireNonNull(baseOptions, "baseOptions");
requireNonNull(options, "options");
if (options.length == 0) {
return baseOptions;
}
return new SessionOptions(baseOptions, options);
}
/**
* Merges the specified {@link SessionOptions} and {@link SessionOptionValue}s.
*
* @return the merged {@link SessionOptions}
*/
public static SessionOptions of(SessionOptions baseOptions, Iterable<SessionOptionValue<?>> options) {
// TODO(trustin): Reduce the cost of creating a derived SessionOptions.
requireNonNull(baseOptions, "baseOptions");
requireNonNull(options, "options");
return new SessionOptions(baseOptions, options);
}
/**
* Merges the specified two {@link SessionOptions} into one.
*
* @return the merged {@link SessionOptions}
*/
public static SessionOptions of(SessionOptions baseOptions, SessionOptions options) {
// TODO(trustin): Reduce the cost of creating a derived SessionOptions.
requireNonNull(baseOptions, "baseOptions");
requireNonNull(options, "options");
return new SessionOptions(baseOptions, options);
}
private static <T> SessionOptionValue<T> validateValue(SessionOptionValue<T> optionValue) {
requireNonNull(optionValue, "value");
SessionOption<?> option = optionValue.option();
T value = optionValue.value();
if (option == CONNECT_TIMEOUT) {
validateConnectionTimeout((Duration) value);
} else if (option == IDLE_TIMEOUT) {
validateIdleTimeout((Duration) value);
}
return optionValue;
}
private static Duration validateConnectionTimeout(Duration connectionTimeout) {
requireNonNull(connectionTimeout, "connectionTimeout");
if (connectionTimeout.isNegative() || connectionTimeout.isZero()) {
throw new IllegalArgumentException(
"connectTimeout: " + connectionTimeout + " (expected: > 0)");
}
return connectionTimeout;
}
private static Duration validateIdleTimeout(Duration idleTimeout) {
requireNonNull(idleTimeout, "idleTimeout");
if (idleTimeout.isNegative()) {
throw new IllegalArgumentException(
"idleTimeout: " + idleTimeout + " (expected: >= 0)");
}
return idleTimeout;
}
private SessionOptions(SessionOptionValue<?>... options) {
super(SessionOptions::validateValue, options);
}
private SessionOptions(SessionOptions baseOptions, SessionOptionValue<?>... options) {
super(SessionOptions::validateValue, baseOptions, options);
}
private SessionOptions(
SessionOptions baseOptions, Iterable<SessionOptionValue<?>> options) {
super(SessionOptions::validateValue, baseOptions, options);
}
private SessionOptions(SessionOptions clientOptions, SessionOptions options) {
super(clientOptions, options);
}
/**
* Returns the value of the specified {@link SessionOption}.
*
* @return the value of the {@link SessionOption}, or
* {@link Optional#empty()} if the default value of the specified {@link SessionOption} is
* not available
*/
public <T> Optional<T> get(SessionOption<T> option) {
return get0(option);
}
/**
* Returns the value of the specified {@link SessionOption}.
*
* @return the value of the {@link SessionOption}, or
* {@code defaultValue} if the specified {@link SessionOption} is not set.
*/
public <T> T getOrElse(SessionOption<T> option, T defaultValue) {
return getOrElse0(option, defaultValue);
}
/**
* Converts this {@link SessionOptions} to a {@link Map}.
*/
public Map<SessionOption<Object>, SessionOptionValue<Object>> asMap() {
return asMap0();
}
/**
* Returns the {@link SessionOption#CONNECT_TIMEOUT} value.
*/
public Duration connectTimeout() {
return getOrElse(CONNECT_TIMEOUT, DEFAULT_CONNECTION_TIMEOUT);
}
/**
* Returns the {@link SessionOption#CONNECT_TIMEOUT} value as milliseconds.
*/
public long connectTimeoutMillis() {
return connectTimeout().toMillis();
}
/**
* Returns the {@link SessionOption#EVENT_LOOP_GROUP} if non-default was specified.
*/
public Optional<EventLoopGroup> eventLoopGroup() {
return get(EVENT_LOOP_GROUP);
}
/**
* Returns the {@link SessionOption#TRUST_MANAGER_FACTORY} if non-default was specified.
*/
public Optional<TrustManagerFactory> trustManagerFactory() {
return get(TRUST_MANAGER_FACTORY);
}
/**
* Returns the {@link SessionOption#ADDRESS_RESOLVER_GROUP} if non-default was specified.
*/
public Optional<AddressResolverGroup<InetSocketAddress>> addressResolverGroup() {
final Optional<AddressResolverGroup<? extends InetSocketAddress>> value = get(ADDRESS_RESOLVER_GROUP);
@SuppressWarnings("unchecked")
final Optional<AddressResolverGroup<InetSocketAddress>> castValue =
(Optional<AddressResolverGroup<InetSocketAddress>>) (Optional<?>) value;
return castValue;
}
/**
* Returns the {@link SessionOption#IDLE_TIMEOUT} value.
*/
public Duration idleTimeout() {
return getOrElse(IDLE_TIMEOUT, DEFAULT_IDLE_TIMEOUT);
}
/**
* Returns the {@link SessionOption#IDLE_TIMEOUT} value as milliseconds.
*/
public long idleTimeoutMillis() {
return idleTimeout().toMillis();
}
/**
* Returns the {@link SessionOption#POOL_HANDLER_DECORATOR}.
*/
public Function<KeyedChannelPoolHandler<PoolKey>, KeyedChannelPoolHandler<PoolKey>> poolHandlerDecorator() {
return getOrElse(POOL_HANDLER_DECORATOR, Function.identity());
}
/**
* Returns whether {@link SessionOption#USE_HTTP2_PREFACE} is enabled or not.
*/
public boolean useHttp2Preface() {
return getOrElse(USE_HTTP2_PREFACE, DEFAULT_USE_HTTP2_PREFACE);
}
/**
* Returns whether {@link SessionOption#USE_HTTP1_PIPELINING} is enabled or not.
*/
public boolean useHttp1Pipelining() {
return getOrElse(USE_HTTP1_PIPELINING, DEFAULT_USE_HTTP1_PIPELINING);
}
}