text:
package com.linecorp.armeria.server;

import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkState;
import static com.google.common.collect.ImmutableList.toImmutableList;
import static com.linecorp.armeria.server.ServerBuilder.decorate;
import static com.linecorp.armeria.server.ServerSslContextUtil.buildSslContext;
import static com.linecorp.armeria.server.ServerSslContextUtil.validateSslContext;
import static com.linecorp.armeria.server.ServiceConfig.validateMaxRequestLength;
import static com.linecorp.armeria.server.ServiceConfig.validateRequestTimeoutMillis;
import static com.linecorp.armeria.server.VirtualHost.HOSTNAME_WITH_NO_PORT_PATTERN;
import static com.linecorp.armeria.server.VirtualHost.ensureHostnamePatternMatchesDefaultHostname;
import static com.linecorp.armeria.server.VirtualHost.normalizeDefaultHostname;
import static com.linecorp.armeria.server.VirtualHost.normalizeHostnamePattern;
import static java.util.Objects.requireNonNull;

import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.IOError;
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Path;
import java.security.PrivateKey;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.time.Duration;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.concurrent.ScheduledExecutorService;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Supplier;
import java.util.stream.Stream;

import javax.net.ssl.KeyManagerFactory;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.google.common.base.MoreObjects;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableList.Builder;
import com.google.common.io.ByteStreams;
import com.google.common.net.HostAndPort;

import com.linecorp.armeria.common.CommonPools;
import com.linecorp.armeria.common.DependencyInjector;
import com.linecorp.armeria.common.Flags;
import com.linecorp.armeria.common.SuccessFunction;
import com.linecorp.armeria.common.TlsSetters;
import com.linecorp.armeria.common.annotation.Nullable;
import com.linecorp.armeria.common.annotation.UnstableApi;
import com.linecorp.armeria.common.util.BlockingTaskExecutor;
import com.linecorp.armeria.common.util.SystemInfo;
import com.linecorp.armeria.internal.common.util.SelfSignedCertificate;
import com.linecorp.armeria.internal.server.annotation.AnnotatedServiceExtensions;
import com.linecorp.armeria.server.annotation.ExceptionHandlerFunction;
import com.linecorp.armeria.server.annotation.RequestConverterFunction;
import com.linecorp.armeria.server.annotation.ResponseConverterFunction;
import com.linecorp.armeria.server.logging.AccessLogWriter;
import com.linecorp.armeria.server.logging.LoggingService;
import com.linecorp.armeria.server.metric.MetricCollectingService;

import io.netty.channel.EventLoopGroup;
import io.netty.handler.ssl.SslContext;
import io.netty.handler.ssl.SslContextBuilder;
import io.netty.util.ReferenceCountUtil;

/**
 * Builds a new {@link VirtualHost}.
 *
 *
This class can only be instantiated through the {@link ServerBuilder#defaultVirtualHost()} or
 * {@link ServerBuilder#virtualHost(String)} method of the {@link ServerBuilder}.
 *
 *
Call {@link #and()} method and return to {@link ServerBuilder}.
 *
 * @see ServerBuilder
 * @see Route
 */
public final class VirtualHostBuilder implements TlsSetters {

    private final ServerBuilder serverBuilder;
    private final boolean defaultVirtualHost;
    private final List<ServiceConfigSetters> serviceConfigSetters = new ArrayList<>();
    private final List<ShutdownSupport> shutdownSupports = new ArrayList<>();

    @Nullable
    private String defaultHostname;
    @Nullable
    private String hostnamePattern;
    private int port = -1;
    @Nullable
    private Supplier<SslContextBuilder> sslContextBuilderSupplier;
    @Nullable
    private Boolean tlsSelfSigned;
    @Nullable
    private SelfSignedCertificate selfSignedCertificate;
    private final List<Consumer<? super SslContextBuilder>> tlsCustomizers = new ArrayList<>();
    @Nullable
    private Boolean tlsAllowUnsafeCiphers;
    private final LinkedList<RouteDecoratingService> routeDecoratingServices = new LinkedList<>();
    @Nullable
    private Function<? super VirtualHost, ? extends Logger> accessLoggerMapper;

    @Nullable
    private RejectedRouteHandler rejectedRouteHandler;
    @Nullable
    private ServiceNaming defaultServiceNaming;
    @Nullable
    private Long requestTimeoutMillis;
    @Nullable
    private Long maxRequestLength;
    @Nullable
    private Boolean verboseResponses;
    @Nullable
    private AccessLogWriter accessLogWriter;
    @Nullable
    private AnnotatedServiceExtensions annotatedServiceExtensions;
    @Nullable
    private ScheduledExecutorService blockingTaskExecutor;
    @Nullable
    private SuccessFunction successFunction;
    @Nullable
    private Path multipartUploadsLocation;

    /**
     * Creates a new {@link VirtualHostBuilder}.
     *
     * @param serverBuilder the parent {@link ServerBuilder} to be returned by {@link #and()}
     * @param defaultVirtualHost tells whether this builder is the default virtual host builder or not
     */
    VirtualHostBuilder(ServerBuilder serverBuilder, boolean defaultVirtualHost) {
        this.serverBuilder = requireNonNull(serverBuilder, "serverBuilder");
        this.defaultVirtualHost = defaultVirtualHost;
    }

    /**
     * Creates a new {@link VirtualHostBuilder}.
     *
     * @param serverBuilder the parent {@link ServerBuilder} to be returned by {@link #and()}
     * @param port the port that this virtual host binds to
     */
    VirtualHostBuilder(ServerBuilder serverBuilder, int port) {
        this.serverBuilder = requireNonNull(serverBuilder, "serverBuilder");
        this.port = port;
        defaultVirtualHost = true;
    }

    /**
     * Returns the parent {@link ServerBuilder}.
     *
     * @return serverBuilder the parent {@link ServerBuilder}.
     */
    public ServerBuilder and() {
        return serverBuilder;
    }

    /**
     * Sets the default hostname of this {@link VirtualHost}.
     */
    public VirtualHostBuilder defaultHostname(String defaultHostname) {
        this.defaultHostname = normalizeDefaultHostname(defaultHostname);
        return this;
    }

    /**
     * Sets the hostname pattern of this {@link VirtualHost}.
     * If the hostname pattern contains a port number such {@code *.example.com:8080}, the returned virtual host
     * will be bound to the {@code 8080} port. Otherwise, the virtual host will allow all active ports.
     *
     * @throws UnsupportedOperationException if this is the default {@link VirtualHostBuilder}
     */
    public VirtualHostBuilder hostnamePattern(String hostnamePattern) {
        if (defaultVirtualHost) {
            throw new UnsupportedOperationException(
                    "Cannot set hostnamePattern for the default virtual host builder");
        }

        checkArgument(!hostnamePattern.isEmpty(), "hostnamePattern is empty.");

        final HostAndPort hostAndPort = HostAndPort.fromString(hostnamePattern);
        if (hostAndPort.hasPort()) {
            port = hostAndPort.getPort();
            checkArgument(port >= 1 && port <= 65535, "port: %s (expected: 1-65535)", port);
            hostnamePattern = hostAndPort.getHost();
        }

        final boolean validHostnamePattern;
        if (hostnamePattern.charAt(0) == '*') {
            validHostnamePattern =
                    hostnamePattern.length() >= 3 &&
                    hostnamePattern.charAt(1) == '.' &&
                    HOSTNAME_WITH_NO_PORT_PATTERN.matcher(hostnamePattern.substring(2)).matches();
        } else {
            validHostnamePattern = HOSTNAME_WITH_NO_PORT_PATTERN.matcher(hostnamePattern).matches();
        }

        checkArgument(validHostnamePattern,
                      "hostnamePattern: %s (expected: *.<hostname> or <hostname>)", hostnamePattern);

        this.hostnamePattern = normalizeHostnamePattern(hostnamePattern);
        return this;
    }

    @Override
    public VirtualHostBuilder tls(File keyCertChainFile, File keyFile) {
        return (VirtualHostBuilder) TlsSetters.super.tls(keyCertChainFile, keyFile);
    }

    @Override
    public VirtualHostBuilder tls(File keyCertChainFile, File keyFile, @Nullable String keyPassword) {
        requireNonNull(keyCertChainFile, "keyCertChainFile");
        requireNonNull(keyFile, "keyFile");
        return tls(() -> SslContextBuilder.forServer(keyCertChainFile, keyFile, keyPassword));
    }

    @Override
    public VirtualHostBuilder tls(InputStream keyCertChainInputStream, InputStream keyInputStream) {
        return (VirtualHostBuilder) TlsSetters.super.tls(keyCertChainInputStream, keyInputStream);
    }

    @Override
    public VirtualHostBuilder tls(InputStream keyCertChainInputStream, InputStream keyInputStream,
                                  @Nullable String keyPassword) {
        requireNonNull(keyCertChainInputStream, "keyCertChainInputStream");
        requireNonNull(keyInputStream, "keyInputStream");

        // Retrieve the content of the given streams so that they can be consumed more than once.
        final byte[] keyCertChain;
        final byte[] key;
        try {
            keyCertChain = ByteStreams.toByteArray(keyCertChainInputStream);
            key = ByteStreams.toByteArray(keyInputStream);
        } catch (IOException e) {
            throw new IOError(e);
        }

        return tls(() -> SslContextBuilder.forServer(new ByteArrayInputStream(keyCertChain),
                                                     new ByteArrayInputStream(key),
                                                     keyPassword));
    }

    @Override
    public VirtualHostBuilder tls(PrivateKey key, X509Certificate... keyCertChain) {
        return (VirtualHostBuilder) TlsSetters.super.tls(key, keyCertChain);
    }

    @Override
    public VirtualHostBuilder tls(PrivateKey key, Iterable<? extends X509Certificate> keyCertChain) {
        return (VirtualHostBuilder) TlsSetters.super.tls(key, keyCertChain);
    }

    @Override
    public VirtualHostBuilder tls(PrivateKey key, @Nullable String keyPassword,
                                  X509Certificate... keyCertChain) {
        return (VirtualHostBuilder) TlsSetters.super.tls(key, keyPassword, keyCertChain);
    }

    @Override
    public VirtualHostBuilder tls(PrivateKey key, @Nullable String keyPassword,
                                  Iterable<? extends X509Certificate> keyCertChain) {
        requireNonNull(key, "key");
        requireNonNull(keyCertChain, "keyCertChain");
        for (X509Certificate keyCert : keyCertChain) {
            requireNonNull(keyCert, "keyCertChain contains null.");
        }

        return tls(() -> SslContextBuilder.forServer(key, keyPassword, keyCertChain));
    }

    @Override
    public VirtualHostBuilder tls(KeyManagerFactory keyManagerFactory) {
        requireNonNull(keyManagerFactory, "keyManagerFactory");
        return tls(() -> SslContextBuilder.forServer(keyManagerFactory));
    }

    private VirtualHostBuilder tls(Supplier<SslContextBuilder> sslContextBuilderSupplier) {
        requireNonNull(sslContextBuilderSupplier, "sslContextBuilderSupplier");
        checkState(this.sslContextBuilderSupplier == null, "TLS has been configured already.");
        this.sslContextBuilderSupplier = sslContextBuilderSupplier;
        return this;
    }

    /**
     * Configures SSL or TLS of this {@link VirtualHost} with an auto-generated self-signed certificate.
     * Note: You should never use this in production but only for a testing purpose.
     *
     * @see #tlsCustomizer(Consumer)
     */
    public VirtualHostBuilder tlsSelfSigned() {
        tlsSelfSigned = true;
        return this;
    }

    /**
     * Configures SSL or TLS of this {@link VirtualHost} with an auto-generated self-signed certificate.
     * Note: You should never use this in production but only for a testing purpose.
     *
     * @see #tlsCustomizer(Consumer)
     */
    public VirtualHostBuilder tlsSelfSigned(boolean tlsSelfSigned) {
        this.tlsSelfSigned = tlsSelfSigned;
        return this;
    }

    @Override
    public VirtualHostBuilder tlsCustomizer(Consumer<? super SslContextBuilder> tlsCustomizer) {
        requireNonNull(tlsCustomizer, "tlsCustomizer");
        tlsCustomizers.add(tlsCustomizer);
        return this;
    }

    /**
     * Allows the bad cipher suites listed in
     * RFC7540 for TLS handshake.
     *
     *
Note that enabling this option increases the security risk of your connection.
     * Use it only when you must communicate with a legacy system that does not support
     * secure cipher suites.
     * See Section 9.2.2, RFC7540 for
     * more information. This option is disabled by default.
     *
     * @deprecated It's not recommended to enable this option. Use it only when you have no other way to
     *             communicate with an insecure peer than this.
     */
    @Deprecated
    public VirtualHostBuilder tlsAllowUnsafeCiphers() {
        return tlsAllowUnsafeCiphers(true);
    }

    /**
     * Allows the bad cipher suites listed in
     * RFC7540 for TLS handshake.
     *
     *
Note that enabling this option increases the security risk of your connection.
     * Use it only when you must communicate with a legacy system that does not support
     * secure cipher suites.
     * See Section 9.2.2, RFC7540 for
     * more information. This option is disabled by default.
     *
     * @param tlsAllowUnsafeCiphers Whether to allow the unsafe ciphers
     *
     * @deprecated It's not recommended to enable this option. Use it only when you have no other way to
     *             communicate with an insecure peer than this.
     */
    @Deprecated
    public VirtualHostBuilder tlsAllowUnsafeCiphers(boolean tlsAllowUnsafeCiphers) {
        this.tlsAllowUnsafeCiphers = tlsAllowUnsafeCiphers;
        return this;
    }

    /**
     * Configures an {@link HttpService} of the {@link VirtualHost} with the {@code customizer}.
     */
    public VirtualHostBuilder withRoute(Consumer<? super VirtualHostServiceBindingBuilder> customizer) {
        final VirtualHostServiceBindingBuilder builder = new VirtualHostServiceBindingBuilder(this);
        customizer.accept(builder);
        return this;
    }

    /**
     * Returns a {@link ServiceBindingBuilder} which is for binding an {@link HttpService} fluently.
     */
    public VirtualHostServiceBindingBuilder route() {
        return new VirtualHostServiceBindingBuilder(this);
    }

    /**
     * Returns a {@link VirtualHostDecoratingServiceBindingBuilder} which is for binding
     * a {@code decorator} fluently. The specified decorator(s) is/are executed in reverse order of
     * the insertion.
     */
    public VirtualHostDecoratingServiceBindingBuilder routeDecorator() {
        return new VirtualHostDecoratingServiceBindingBuilder(this);
    }

    /**
     * Binds the specified {@link HttpService} under the specified directory.
     * If the specified {@link HttpService} is an {@link HttpServiceWithRoutes}, the {@code pathPrefix} is added
     * to each {@link Route} of {@link HttpServiceWithRoutes#routes()}. For example, the
     * {@code serviceWithRoutes} in the following code will be bound to
     * ({@code "/foo/bar"}) and ({@code "/foo/baz"}):
     *
{@code
     * > HttpServiceWithRoutes serviceWithRoutes = new HttpServiceWithRoutes() {
     * >     @Override
     * >     public HttpResponse serve(ServiceRequestContext ctx, HttpRequest req) { ... }
     * >
     * >     @Override
     * >     public Set<Route> routes() {
     * >         return Set.of(Route.builder().path("/bar").build(),
     * >                       Route.builder().path("/baz").build());
     * >     }
     * > };
     * >
     * > Server.builder()
     * >       .serviceUnder("/foo", serviceWithRoutes)
     * >       .build();
     * }

     */
    public VirtualHostBuilder serviceUnder(String pathPrefix, HttpService service) {
        requireNonNull(pathPrefix, "pathPrefix");
        requireNonNull(service, "service");
        final HttpServiceWithRoutes serviceWithRoutes = service.as(HttpServiceWithRoutes.class);
        if (serviceWithRoutes != null) {
            serviceWithRoutes.routes().forEach(route -> {
                final ServiceConfigBuilder serviceConfigBuilder =
                        new ServiceConfigBuilder(route.withPrefix(pathPrefix), service);
                serviceConfigBuilder.addMappedRoute(route);
                addServiceConfigSetters(serviceConfigBuilder);
            });
        } else {
            service(Route.builder().pathPrefix(pathPrefix).build(), service);
        }
        return this;
    }

    /**
     * Binds the specified {@link HttpService} at the specified path pattern. e.g.
     *

     *
{@code /login} (no path parameters)

     *
{@code /users/{userId}} (curly-brace style)

     *
{@code /list/:productType/by/:ordering} (colon style)

     *
{@code exact:/foo/bar} (exact match)

     *
{@code prefix:/files} (prefix match)

     *
glob:/~*/downloads/** (glob pattern)

     *
{@code regex:^/files/(?<filePath>.*)$} (regular expression)

     *

     *
     * @throws IllegalArgumentException if the specified path pattern is invalid
     */
    public VirtualHostBuilder service(String pathPattern, HttpService service) {
        service(Route.builder().path(pathPattern).build(), service);
        return this;
    }

    /**
     * Binds the specified {@link HttpService} at the specified {@link Route}.
     */
    public VirtualHostBuilder service(Route route, HttpService service) {
        return addServiceConfigSetters(new ServiceConfigBuilder(route, service));
    }

    /**
     * Decorates and binds the specified {@link HttpServiceWithRoutes} at multiple {@link Route}s.
     *
     * @param serviceWithRoutes the {@link HttpServiceWithRoutes}.
     * @param decorators the decorator functions, which will be applied in the order specified.
     */
    public VirtualHostBuilder service(
            HttpServiceWithRoutes serviceWithRoutes,
            Iterable<? extends Function<? super HttpService, ? extends HttpService>> decorators) {
        requireNonNull(serviceWithRoutes, "serviceWithRoutes");
        requireNonNull(serviceWithRoutes.routes(), "serviceWithRoutes.routes()");
        requireNonNull(decorators, "decorators");

        final HttpService decorated = decorate(serviceWithRoutes, decorators);
        serviceWithRoutes.routes().forEach(route -> service(route, decorated));
        return this;
    }

    /**
     * Decorates and binds the specified {@link HttpServiceWithRoutes} at multiple {@link Route}s.
     *
     * @param serviceWithRoutes the {@link HttpServiceWithRoutes}.
     * @param decorators the decorator functions, which will be applied in the order specified.
     */
    @SafeVarargs
    public final VirtualHostBuilder service(
            HttpServiceWithRoutes serviceWithRoutes,
            Function<? super HttpService, ? extends HttpService>... decorators) {
        return service(serviceWithRoutes, ImmutableList.copyOf(requireNonNull(decorators, "decorators")));
    }

    /**
     * Binds the specified annotated service object under the path prefix {@code "/"}.
     */
    public VirtualHostBuilder annotatedService(Object service) {
        return annotatedService("/", service, Function.identity(), ImmutableList.of());
    }

    /**
     * Binds the specified annotated service object under the path prefix {@code "/"}.
     *
     * @param exceptionHandlersAndConverters the {@link ExceptionHandlerFunction}s,
     *                                       the {@link RequestConverterFunction}s and/or
     *                                       the {@link ResponseConverterFunction}s
     */
    public VirtualHostBuilder annotatedService(Object service,
                                               Object... exceptionHandlersAndConverters) {
        return annotatedService("/", service, Function.identity(),
                                ImmutableList.copyOf(requireNonNull(exceptionHandlersAndConverters,
                                                                    "exceptionHandlersAndConverters")));
    }

    /**
     * Binds the specified annotated service object under the path prefix {@code "/"}.
     *
     * @param exceptionHandlersAndConverters the {@link ExceptionHandlerFunction}s,
     *                                       the {@link RequestConverterFunction}s and/or
     *                                       the {@link ResponseConverterFunction}s
     */
    public VirtualHostBuilder annotatedService(
            Object service, Function<? super HttpService, ? extends HttpService> decorator,
            Object... exceptionHandlersAndConverters) {
        return annotatedService("/", service, decorator,
                                ImmutableList.copyOf(requireNonNull(exceptionHandlersAndConverters,
                                                                    "exceptionHandlersAndConverters")));
    }

    /**
     * Binds the specified annotated service object under the specified path prefix.
     */
    public VirtualHostBuilder annotatedService(String pathPrefix, Object service) {
        return annotatedService(pathPrefix, service, Function.identity(), ImmutableList.of());
    }

    /**
     * Binds the specified annotated service object under the specified path prefix.
     *
     * @param exceptionHandlersAndConverters the {@link ExceptionHandlerFunction}s,
     *                                       the {@link RequestConverterFunction}s and/or
     *                                       the {@link ResponseConverterFunction}s
     */
    public VirtualHostBuilder annotatedService(String pathPrefix, Object service,
                                               Object... exceptionHandlersAndConverters) {
        return annotatedService(pathPrefix, service, Function.identity(),
                                ImmutableList.copyOf(requireNonNull(exceptionHandlersAndConverters,
                                                                    "exceptionHandlersAndConverters")));
    }

    /**
     * Binds the specified annotated service object under the specified path prefix.
     *
     * @param exceptionHandlersAndConverters the {@link ExceptionHandlerFunction},
     *                                       {@link RequestConverterFunction} and/or
     *                                       {@link ResponseConverterFunction}
     */
    public VirtualHostBuilder annotatedService(String pathPrefix, Object service,
                                               Iterable<?> exceptionHandlersAndConverters) {
        return annotatedService(pathPrefix, service, Function.identity(),
                                requireNonNull(exceptionHandlersAndConverters,
                                               "exceptionHandlersAndConverters"));
    }

    /**
     * Binds the specified annotated service object under the specified path prefix.
     *
     * @param exceptionHandlersAndConverters the {@link ExceptionHandlerFunction}s,
     *                                       the {@link RequestConverterFunction}s and/or
     *                                       the {@link ResponseConverterFunction}s
     */
    public VirtualHostBuilder annotatedService(
            String pathPrefix, Object service, Function<? super HttpService, ? extends HttpService> decorator,
            Object... exceptionHandlersAndConverters) {
        return annotatedService(pathPrefix, service, decorator,
                                ImmutableList.copyOf(requireNonNull(exceptionHandlersAndConverters,
                                                                    "exceptionHandlersAndConverters")));
    }

    /**
     * Binds the specified annotated service object under the specified path prefix.
     *
     * @param exceptionHandlersAndConverters the {@link ExceptionHandlerFunction}s,
     *                                       the {@link RequestConverterFunction}s and/or
     *                                       the {@link ResponseConverterFunction}s
     */
    public VirtualHostBuilder annotatedService(String pathPrefix, Object service,
                                               Function<? super HttpService, ? extends HttpService> decorator,
                                               Iterable<?> exceptionHandlersAndConverters) {
        requireNonNull(pathPrefix, "pathPrefix");
        requireNonNull(service, "service");
        requireNonNull(decorator, "decorator");
        requireNonNull(exceptionHandlersAndConverters, "exceptionHandlersAndConverters");
        final AnnotatedServiceExtensions configurator =
                AnnotatedServiceExtensions
                        .ofExceptionHandlersAndConverters(exceptionHandlersAndConverters);
        return annotatedService(pathPrefix, service, decorator, configurator.exceptionHandlers(),
                                configurator.requestConverters(), configurator.responseConverters());
    }

    /**
     * Binds the specified annotated service object under the specified path prefix.
     *
     * @param exceptionHandlerFunctions the {@link ExceptionHandlerFunction}s
     * @param requestConverterFunctions the {@link RequestConverterFunction}s
     * @param responseConverterFunctions the {@link ResponseConverterFunction}s
     */
    public VirtualHostBuilder annotatedService(
            String pathPrefix, Object service, Function<? super HttpService, ? extends HttpService> decorator,
            Iterable<? extends ExceptionHandlerFunction> exceptionHandlerFunctions,
            Iterable<? extends RequestConverterFunction> requestConverterFunctions,
            Iterable<? extends ResponseConverterFunction> responseConverterFunctions) {
        requireNonNull(pathPrefix, "pathPrefix");
        requireNonNull(service, "service");
        requireNonNull(decorator, "decorator");
        requireNonNull(exceptionHandlerFunctions, "exceptionHandlerFunctions");
        requireNonNull(requestConverterFunctions, "requestConverterFunctions");
        requireNonNull(responseConverterFunctions, "responseConverterFunctions");
        return annotatedService().pathPrefix(pathPrefix)
                                 .decorator(decorator)
                                 .exceptionHandlers(exceptionHandlerFunctions)
                                 .requestConverters(requestConverterFunctions)
                                 .responseConverters(responseConverterFunctions)
                                 .build(service);
    }

    /**
     * Returns a new instance of {@link VirtualHostAnnotatedServiceBindingBuilder} to build
     * an annotated service fluently.
     */
    public VirtualHostAnnotatedServiceBindingBuilder annotatedService() {
        return new VirtualHostAnnotatedServiceBindingBuilder(this);
    }

    VirtualHostBuilder addServiceConfigSetters(ServiceConfigSetters serviceConfigSetters) {
        this.serviceConfigSetters.add(serviceConfigSetters);
        return this;
    }

    private List<ServiceConfigSetters> getServiceConfigSetters(
            @Nullable VirtualHostBuilder defaultVirtualHostBuilder) {
        final List<ServiceConfigSetters> serviceConfigSetters;
        if (defaultVirtualHostBuilder != null) {
            serviceConfigSetters = ImmutableList.<ServiceConfigSetters>builder()
                                                .addAll(this.serviceConfigSetters)
                                                .addAll(defaultVirtualHostBuilder.serviceConfigSetters)
                                                .build();
        } else {
            serviceConfigSetters = ImmutableList.copyOf(this.serviceConfigSetters);
        }
        return serviceConfigSetters;
    }

    VirtualHostBuilder addRouteDecoratingService(RouteDecoratingService routeDecoratingService) {
        if (Flags.useLegacyRouteDecoratorOrdering()) {
            // The first inserted decorator is applied first.
            routeDecoratingServices.addLast(routeDecoratingService);
        } else {
            // The last inserted decorator is applied first.
            routeDecoratingServices.addFirst(routeDecoratingService);
        }
        return this;
    }

    @Nullable
    private Function<? super HttpService, ? extends HttpService> getRouteDecoratingService(
            @Nullable VirtualHostBuilder defaultVirtualHostBuilder) {
        final List<RouteDecoratingService> routeDecoratingServices;
        if (defaultVirtualHostBuilder != null) {
            routeDecoratingServices = ImmutableList.<RouteDecoratingService>builder()
                                                   .addAll(this.routeDecoratingServices)
                                                   .addAll(defaultVirtualHostBuilder.routeDecoratingServices)
                                                   .build();
        } else {
            routeDecoratingServices = ImmutableList.copyOf(this.routeDecoratingServices);
        }

        if (!routeDecoratingServices.isEmpty()) {
            return RouteDecoratingService.newDecorator(
                    Routers.ofRouteDecoratingService(routeDecoratingServices));
        } else {
            return null;
        }
    }

    /**
     * Decorates all {@link HttpService}s with the specified {@code decorator}.
     * The specified decorator(s) is/are executed in reverse order of the insertion.
     *
     * @param decorator the {@link Function} that decorates {@link HttpService}s
     */
    public VirtualHostBuilder decorator(Function<? super HttpService, ? extends HttpService> decorator) {
        return decorator(Route.ofCatchAll(), decorator);
    }

    /**
     * Decorates all {@link HttpService}s with the specified {@link DecoratingHttpServiceFunction}.
     * The specified decorator(s) is/are executed in reverse order of the insertion.
     *
     * @param decoratingHttpServiceFunction the {@link DecoratingHttpServiceFunction} that decorates
     *                                      {@link HttpService}s
     */
    public VirtualHostBuilder decorator(
            DecoratingHttpServiceFunction decoratingHttpServiceFunction) {
        return decorator(Route.ofCatchAll(), decoratingHttpServiceFunction);
    }

    /**
     * Decorates {@link HttpService}s whose {@link Route} matches the specified {@code pathPattern}.
     * The specified decorator(s) is/are executed in reverse order of the insertion.
     *
     * @param decoratingHttpServiceFunction the {@link DecoratingHttpServiceFunction} that decorates
     *                                      {@link HttpService}s
     */
    public VirtualHostBuilder decorator(
            String pathPattern, DecoratingHttpServiceFunction decoratingHttpServiceFunction) {
        return decorator(Route.builder().path(pathPattern).build(), decoratingHttpServiceFunction);
    }

    /**
     * Decorates {@link HttpService}s whose {@link Route} matches the specified {@code pathPattern}.
     * The specified decorator(s) is/are executed in reverse order of the insertion.
     */
    public VirtualHostBuilder decorator(
            String pathPattern, Function<? super HttpService, ? extends HttpService> decorator) {
        return decorator(Route.builder().path(pathPattern).build(), decorator);
    }

    /**
     * Decorates {@link HttpService}s whose {@link Route} matches the specified {@link Route}.
     * The specified decorator(s) is/are executed in reverse order of the insertion.
     *
     * @param route the route being decorated
     * @param decorator the {@link Function} that decorates {@link HttpService}
     */
    public VirtualHostBuilder decorator(
            Route route, Function<? super HttpService, ? extends HttpService> decorator) {
        requireNonNull(route, "route");
        requireNonNull(decorator, "decorator");
        return addRouteDecoratingService(new RouteDecoratingService(route, decorator));
    }

    /**
     * Decorates {@link HttpService}s whose {@link Route} matches the specified {@link Route}.
     * The specified decorator(s) is/are executed in reverse order of the insertion.
     *
     * @param route the route being decorated
     * @param decoratingHttpServiceFunction the {@link DecoratingHttpServiceFunction} that decorates
     *                                      {@link HttpService}s
     */
    public VirtualHostBuilder decorator(
            Route route, DecoratingHttpServiceFunction decoratingHttpServiceFunction) {
        requireNonNull(decoratingHttpServiceFunction, "decoratingHttpServiceFunction");
        return decorator(route, delegate -> new FunctionalDecoratingHttpService(
                delegate, decoratingHttpServiceFunction));
    }

    /**
     * Decorates {@link HttpService}s under the specified directory.
     * The specified decorator(s) is/are executed in reverse order of the insertion.
     */
    public VirtualHostBuilder decoratorUnder(
            String prefix, Function<? super HttpService, ? extends HttpService> decorator) {
        return decorator(Route.builder().pathPrefix(prefix).build(), decorator);
    }

    /**
     * Decorates {@link HttpService}s under the specified directory.
     * The specified decorator(s) is/are executed in reverse order of the insertion.
     *
     * @param decoratingHttpServiceFunction the {@link DecoratingHttpServiceFunction} that decorates
     *                                      {@link HttpService}s
     */
    public VirtualHostBuilder decoratorUnder(
            String prefix, DecoratingHttpServiceFunction decoratingHttpServiceFunction) {
        return decorator(Route.builder().pathPrefix(prefix).build(), decoratingHttpServiceFunction);
    }

    /**
     * Sets the access logger mapper of this {@link VirtualHost}.
     */
    public VirtualHostBuilder accessLogger(Function<? super VirtualHost, ? extends Logger> mapper) {
        accessLoggerMapper = requireNonNull(mapper, "mapper");
        return this;
    }

    /**
     * Sets the {@link Logger} of this {@link VirtualHost}, which is used for writing access logs.
     */
    public VirtualHostBuilder accessLogger(Logger logger) {
        requireNonNull(logger, "logger");
        return accessLogger(host -> logger);
    }

    /**
     * Sets the {@link Logger} named {@code loggerName} of this {@link VirtualHost},
     * which is used for writing access logs.
     */
    public VirtualHostBuilder accessLogger(String loggerName) {
        requireNonNull(loggerName, "loggerName");
        return accessLogger(host -> LoggerFactory.getLogger(loggerName));
    }

    /**
     * Sets the {@link RejectedRouteHandler} which will be invoked when an attempt to bind
     * an {@link HttpService} at a certain {@link Route} is rejected. If not set,
     * the {@link RejectedRouteHandler} set via
     * {@link ServerBuilder#rejectedRouteHandler(RejectedRouteHandler)} is used.
     */
    public VirtualHostBuilder rejectedRouteHandler(RejectedRouteHandler handler) {
        rejectedRouteHandler = requireNonNull(handler, "handler");
        return this;
    }

    /**
     * Sets the timeout of a request. If not set, the value set via
     * {@link ServerBuilder#requestTimeoutMillis(long)} is used.
     *
     * @param requestTimeout the timeout. {@code 0} disables the timeout.
     */
    public VirtualHostBuilder requestTimeout(Duration requestTimeout) {
        return requestTimeoutMillis(requireNonNull(requestTimeout, "requestTimeout").toMillis());
    }

    /**
     * Sets the default naming rule for the name of services. If not set, the value set via
     * {@link ServerBuilder#defaultServiceNaming(ServiceNaming)} is used.
     */
    public VirtualHostBuilder defaultServiceNaming(ServiceNaming defaultServiceNaming) {
        this.defaultServiceNaming = requireNonNull(defaultServiceNaming);
        return this;
    }

    /**
     * Sets the timeout of a request in milliseconds. If not set, the value set via
     * {@link ServerBuilder#requestTimeoutMillis(long)} is used.
     *
     * @param requestTimeoutMillis the timeout in milliseconds. {@code 0} disables the timeout.
     */
    public VirtualHostBuilder requestTimeoutMillis(long requestTimeoutMillis) {
        this.requestTimeoutMillis = validateRequestTimeoutMillis(requestTimeoutMillis);
        return this;
    }

    /**
     * Sets the maximum allowed length of the content decoded at the session layer.
     * e.g. the content length of an HTTP request. If not set, the value set via
     * {@link ServerBuilder#maxRequestLength(long)} is used.
     *
     * @param maxRequestLength the maximum allowed length. {@code 0} disables the length limit.
     */
    public VirtualHostBuilder maxRequestLength(long maxRequestLength) {
        this.maxRequestLength = validateMaxRequestLength(maxRequestLength);
        return this;
    }

    /**
     * Sets whether the verbose response mode is enabled. When enabled, the server responses will contain
     * the exception type and its full stack trace, which may be useful for debugging while potentially
     * insecure. When disabled, the server responses will not expose such server-side details to the client.
     * If not set, the value set via {@link ServerBuilder#verboseResponses(boolean)} is used.
     */
    public VirtualHostBuilder verboseResponses(boolean verboseResponses) {
        this.verboseResponses = verboseResponses;
        return this;
    }

    /**
     * Sets the access log writer of this {@link VirtualHost}. If not set, the {@link AccessLogWriter} set via
     * {@link ServerBuilder#accessLogWriter(AccessLogWriter, boolean)} is used.
     *
     * @param shutdownOnStop whether to shut down the {@link AccessLogWriter} when the {@link Server} stops
     */
    public VirtualHostBuilder accessLogWriter(AccessLogWriter accessLogWriter, boolean shutdownOnStop) {
        requireNonNull(accessLogWriter, "accessLogWriter");
        if (this.accessLogWriter != null) {
            this.accessLogWriter = this.accessLogWriter.andThen(accessLogWriter);
        } else {
            this.accessLogWriter = accessLogWriter;
        }
        if (shutdownOnStop) {
            shutdownSupports.add(ShutdownSupport.of(accessLogWriter));
        }
        return this;
    }

    /**
     * Sets the {@link ScheduledExecutorService} dedicated to the execution of blocking tasks or invocations.
     * If not set, {@linkplain CommonPools#blockingTaskExecutor() the common pool} is used.
     *
     * @param shutdownOnStop whether to shut down the {@link ScheduledExecutorService} when the
     *                       {@link Server} stops
     */
    public VirtualHostBuilder blockingTaskExecutor(ScheduledExecutorService blockingTaskExecutor,
                                                   boolean shutdownOnStop) {
        this.blockingTaskExecutor = requireNonNull(blockingTaskExecutor, "blockingTaskExecutor");
        if (shutdownOnStop) {
            shutdownSupports.add(ShutdownSupport.of(blockingTaskExecutor));
        }
        return this;
    }

    /**
     * Uses a newly created {@link BlockingTaskExecutor} with the specified number of threads dedicated to
     * the execution of blocking tasks or invocations.
     * The worker {@link EventLoopGroup} will be shut down when the {@link Server} stops.
     *
     * @param numThreads the number of threads in the executor
     */
    public VirtualHostBuilder blockingTaskExecutor(int numThreads) {
        checkArgument(numThreads >= 0, "numThreads: %s (expected: >= 0)", numThreads);
        final BlockingTaskExecutor executor = BlockingTaskExecutor.builder()
                                                                  .numThreads(numThreads)
                                                                  .build();
        return blockingTaskExecutor(executor, true);
    }

    /**
     * Sets the {@link SuccessFunction} to define successful responses.
     * {@link MetricCollectingService} and {@link LoggingService} use this function.
     */
    @UnstableApi
    public VirtualHostBuilder successFunction(SuccessFunction successFunction) {
        this.successFunction = requireNonNull(successFunction, "successFunction");
        return this;
    }

    /**
     * Sets the {@link Path} for storing the files uploaded from
     * {@code multipart/form-data} requests.
     *
     * @param multipartUploadsLocation the path of the directory which stores the files.
     */
    public VirtualHostBuilder multipartUploadsLocation(Path multipartUploadsLocation) {
        this.multipartUploadsLocation = requireNonNull(multipartUploadsLocation, "multipartUploadsLocation");
        return this;
    }

    /**
     * Sets the {@link RequestConverterFunction}s, {@link ResponseConverterFunction}
     * and {@link ExceptionHandlerFunction}s for creating an {@link AnnotatedServiceExtensions}.
     *
     * @param requestConverterFunctions the {@link RequestConverterFunction}s
     * @param responseConverterFunctions the {@link ResponseConverterFunction}s
     * @param exceptionHandlerFunctions the {@link ExceptionHandlerFunction}s
     */
    public VirtualHostBuilder annotatedServiceExtensions(
            Iterable<? extends RequestConverterFunction> requestConverterFunctions,
            Iterable<? extends ResponseConverterFunction> responseConverterFunctions,
            Iterable<? extends ExceptionHandlerFunction> exceptionHandlerFunctions) {
        requireNonNull(requestConverterFunctions, "requestConverterFunctions");
        requireNonNull(responseConverterFunctions, "responseConverterFunctions");
        requireNonNull(exceptionHandlerFunctions, "exceptionHandlerFunctions");
        annotatedServiceExtensions =
                new AnnotatedServiceExtensions(ImmutableList.copyOf(requestConverterFunctions),
                                               ImmutableList.copyOf(responseConverterFunctions),
                                               ImmutableList.copyOf(exceptionHandlerFunctions));
        return this;
    }

    @Nullable
    AnnotatedServiceExtensions annotatedServiceExtensions() {
        return annotatedServiceExtensions;
    }

    /**
     * Returns a newly-created {@link VirtualHost} based on the properties of this builder and the services
     * added to this builder.
     */
    VirtualHost build(VirtualHostBuilder template, DependencyInjector dependencyInjector) {
        requireNonNull(template, "template");

        if (defaultHostname == null) {
            if (hostnamePattern != null) {
                defaultHostname = hostnamePattern.startsWith("*.") ? hostnamePattern.substring(2)
                                                                   : hostnamePattern;
            } else {
                defaultHostname = SystemInfo.hostname();
            }
        }

        if (hostnamePattern == null) {
            hostnamePattern = defaultVirtualHost ? "*" : "*." + defaultHostname;
        }

        ensureHostnamePatternMatchesDefaultHostname(hostnamePattern, defaultHostname);

        // Retrieve all settings as a local copy. Use default builder's properties if not set.
        final ServiceNaming defaultServiceNaming =
                this.defaultServiceNaming != null ?
                this.defaultServiceNaming : template.defaultServiceNaming;
        final long requestTimeoutMillis =
                this.requestTimeoutMillis != null ?
                this.requestTimeoutMillis : template.requestTimeoutMillis;
        final long maxRequestLength =
                this.maxRequestLength != null ?
                this.maxRequestLength : template.maxRequestLength;
        final boolean verboseResponses =
                this.verboseResponses != null ?
                this.verboseResponses : template.verboseResponses;
        final RejectedRouteHandler rejectedRouteHandler =
                this.rejectedRouteHandler != null ?
                this.rejectedRouteHandler : template.rejectedRouteHandler;

        final AccessLogWriter accessLogWriter;
        if (this.accessLogWriter != null) {
            accessLogWriter = this.accessLogWriter;
        } else {
            accessLogWriter = template.accessLogWriter != null ?
                              template.accessLogWriter : AccessLogWriter.disabled();
        }

        final Function<? super VirtualHost, ? extends Logger> accessLoggerMapper =
                this.accessLoggerMapper != null ?
                this.accessLoggerMapper : template.accessLoggerMapper;

        final AnnotatedServiceExtensions extensions =
                annotatedServiceExtensions != null ?
                annotatedServiceExtensions : template.annotatedServiceExtensions;

        final ScheduledExecutorService blockingTaskExecutor;
        if (this.blockingTaskExecutor != null) {
            blockingTaskExecutor = this.blockingTaskExecutor;
        } else {
            blockingTaskExecutor = template.blockingTaskExecutor;
        }

        final SuccessFunction successFunction;
        if (this.successFunction != null) {
            successFunction = this.successFunction;
        } else {
            successFunction = template.successFunction;
        }

        final Path multipartUploadsLocation =
                this.multipartUploadsLocation != null ?
                this.multipartUploadsLocation : template.multipartUploadsLocation;

        assert rejectedRouteHandler != null;
        assert accessLoggerMapper != null;
        assert extensions != null;
        assert blockingTaskExecutor != null;
        assert successFunction != null;
        assert multipartUploadsLocation != null;

        final List<ServiceConfig> serviceConfigs = getServiceConfigSetters(template)
                .stream()
                .flatMap(cfgSetters -> {
                    if (cfgSetters instanceof VirtualHostAnnotatedServiceBindingBuilder) {
                        return ((VirtualHostAnnotatedServiceBindingBuilder) cfgSetters)
                                .buildServiceConfigBuilder(extensions, dependencyInjector).stream();
                    } else if (cfgSetters instanceof AnnotatedServiceBindingBuilder) {
                        return ((AnnotatedServiceBindingBuilder) cfgSetters)
                                .buildServiceConfigBuilder(extensions, dependencyInjector).stream();
                    } else if (cfgSetters instanceof ServiceConfigBuilder) {
                        return Stream.of((ServiceConfigBuilder) cfgSetters);
                    } else {
                        // Should not reach here.
                        throw new Error("Unexpected service config setters type: " +
                                        cfgSetters.getClass().getSimpleName());
                    }
                }).map(cfgBuilder -> {
                    return cfgBuilder.build(defaultServiceNaming, requestTimeoutMillis, maxRequestLength,
                                            verboseResponses, accessLogWriter, blockingTaskExecutor,
                                            successFunction, multipartUploadsLocation);
                }).collect(toImmutableList());

        final ServiceConfig fallbackServiceConfig =
                new ServiceConfigBuilder(RouteBuilder.FALLBACK_ROUTE, FallbackService.INSTANCE)
                        .build(defaultServiceNaming, requestTimeoutMillis, maxRequestLength, verboseResponses,
                               accessLogWriter, blockingTaskExecutor, successFunction,
                               multipartUploadsLocation);

        SslContext sslContext = null;
        boolean releaseSslContextOnFailure = false;
        try {
            final boolean tlsAllowUnsafeCiphers =
                    this.tlsAllowUnsafeCiphers != null ?
                    this.tlsAllowUnsafeCiphers : template.tlsAllowUnsafeCiphers;

            // Whether the `SslContext` came (or was created) from this `VirtualHost`'s properties.
            boolean sslContextFromThis = false;

            // Build a new SslContext or use a user-specified one for backward compatibility.
            if (sslContextBuilderSupplier != null) {
                sslContext = buildSslContext(sslContextBuilderSupplier, tlsAllowUnsafeCiphers, tlsCustomizers);
                sslContextFromThis = true;
                releaseSslContextOnFailure = true;
            } else if (template.sslContextBuilderSupplier != null) {
                sslContext = buildSslContext(template.sslContextBuilderSupplier,
                                             tlsAllowUnsafeCiphers,
                                             template.tlsCustomizers);
                releaseSslContextOnFailure = true;
            }

            // Generate a self-signed certificate if necessary.
            if (sslContext == null) {
                final boolean tlsSelfSigned;
                final List<Consumer<? super SslContextBuilder>> tlsCustomizers;
                if (this.tlsSelfSigned != null) {
                    tlsSelfSigned = this.tlsSelfSigned;
                    tlsCustomizers = this.tlsCustomizers;
                    sslContextFromThis = true;
                } else {
                    tlsSelfSigned = template.tlsSelfSigned;
                    tlsCustomizers = template.tlsCustomizers;
                }

                if (tlsSelfSigned) {
                    final SelfSignedCertificate ssc;
                    try {
                        ssc = selfSignedCertificate();
                    } catch (Exception e) {
                        throw new RuntimeException("failed to create a self signed certificate", e);
                    }

                    sslContext = buildSslContext(() -> SslContextBuilder.forServer(ssc.certificate(),
                                                                                ssc.privateKey()),
                                                 tlsAllowUnsafeCiphers,
                                                 tlsCustomizers);
                    releaseSslContextOnFailure = true;
                }
            }

            // Reject if a user called `tlsCustomizer()` without `tls()` or `tlsSelfSigned()`.
            checkState(sslContextFromThis || tlsCustomizers.isEmpty(),
                       "Cannot call tlsCustomizer() without tls() or tlsSelfSigned()");

            // Validate the built `SslContext`.
            if (sslContext != null) {
                validateSslContext(sslContext);
                checkState(sslContext.isServer(), "sslContextBuilder built a client SSL context.");
            }

            final Builder<ShutdownSupport> builder = ImmutableList.builder();
            builder.addAll(shutdownSupports);
            builder.addAll(template.shutdownSupports);

            final VirtualHost virtualHost =
                    new VirtualHost(defaultHostname, hostnamePattern, port, sslContext,
                                    serviceConfigs, fallbackServiceConfig, rejectedRouteHandler,
                                    accessLoggerMapper, defaultServiceNaming, requestTimeoutMillis,
                                    maxRequestLength, verboseResponses, accessLogWriter,
                                    blockingTaskExecutor, builder.build());

            final Function<? super HttpService, ? extends HttpService> decorator =
                    getRouteDecoratingService(template);
            final VirtualHost decoratedVirtualHost = decorator != null ? virtualHost.decorate(decorator)
                                                                       : virtualHost;
            releaseSslContextOnFailure = false;
            return decoratedVirtualHost;
        } finally {
            if (releaseSslContextOnFailure) {
                ReferenceCountUtil.release(sslContext);
            }
        }
    }

    private SelfSignedCertificate selfSignedCertificate() throws CertificateException {
        if (selfSignedCertificate == null) {
            return selfSignedCertificate = new SelfSignedCertificate(defaultHostname);
        }
        return selfSignedCertificate;
    }

    int port() {
        return port;
    }

    boolean defaultVirtualHost() {
        return defaultVirtualHost;
    }

    @Override
    public String toString() {
        return MoreObjects.toStringHelper(this).omitNullValues()
                          .add("defaultHostname", defaultHostname)
                          .add("hostnamePattern", hostnamePattern)
                          .add("serviceConfigSetters", serviceConfigSetters)
                          .add("routeDecoratingServices", routeDecoratingServices)
                          .add("accessLoggerMapper", accessLoggerMapper)
                          .add("rejectedRouteHandler", rejectedRouteHandler)
                          .add("defaultServiceNaming", defaultServiceNaming)
                          .add("requestTimeoutMillis", requestTimeoutMillis)
                          .add("maxRequestLength", maxRequestLength)
                          .add("verboseResponses", verboseResponses)
                          .add("accessLogWriter", accessLogWriter)
                          .add("shutdownSupports", shutdownSupports)
                          .toString();
    }
}


text:
package com.couchbase.lite.internal.database;

/**
 * An exception that indicates there was an error with SQL parsing or execution.
 */
public class SQLException extends RuntimeException {
    public SQLException() {
    }

    public SQLException(String error) {
        super(error);
    }

    public SQLException(String error, Throwable cause) {
        super(error, cause);
    }
}

redpajama_set_name: RedPajamaGithub


text:
* Node.js

## Install dependencies
`npm install`

## Run all tests
`npm run test-all`

## Run  tests
`npm run test-people`

## Docker
If you want to run tests in docker.

`npm run test-all-with-docker"`