google-nomulus/javatests/google/registry/proxy/ProtocolModuleTest.java
jianglai 3fc7271145 Move GCP proxy code to the old [] proxy's location
1. Moved code for the GCP proxy to where the [] proxy code used to live.
3. Corrected reference to the GCP proxy location.
4. Misc changes to make ErrorProne and various tools happy.

+diekmann to LGTM terraform whitelist change.

-------------
Created by MOE: https://github.com/google/moe
MOE_MIGRATED_REVID=213630560
2018-09-20 11:19:36 -04:00

291 lines
11 KiB
Java

// Copyright 2017 The Nomulus Authors. All Rights Reserved.
//
// Licensed 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 google.registry.proxy;
import static com.google.common.collect.ImmutableList.toImmutableList;
import static google.registry.proxy.ProxyConfig.Environment.LOCAL;
import static google.registry.proxy.ProxyConfig.getProxyConfig;
import com.google.common.base.Suppliers;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import com.google.common.util.concurrent.MoreExecutors;
import dagger.Component;
import dagger.Module;
import dagger.Provides;
import google.registry.proxy.EppProtocolModule.EppProtocol;
import google.registry.proxy.HealthCheckProtocolModule.HealthCheckProtocol;
import google.registry.proxy.HttpsRelayProtocolModule.HttpsRelayProtocol;
import google.registry.proxy.ProxyConfig.Environment;
import google.registry.proxy.WebWhoisProtocolsModule.HttpWhoisProtocol;
import google.registry.proxy.WhoisProtocolModule.WhoisProtocol;
import google.registry.proxy.handler.BackendMetricsHandler;
import google.registry.proxy.handler.ProxyProtocolHandler;
import google.registry.proxy.handler.QuotaHandler.EppQuotaHandler;
import google.registry.proxy.handler.QuotaHandler.WhoisQuotaHandler;
import google.registry.proxy.handler.RelayHandler.FullHttpRequestRelayHandler;
import google.registry.proxy.handler.RelayHandler.FullHttpResponseRelayHandler;
import google.registry.proxy.handler.SslClientInitializer;
import google.registry.proxy.handler.SslServerInitializer;
import google.registry.proxy.handler.WebWhoisRedirectHandler;
import google.registry.testing.FakeClock;
import google.registry.util.Clock;
import io.netty.channel.Channel;
import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.embedded.EmbeddedChannel;
import io.netty.handler.logging.LoggingHandler;
import io.netty.handler.ssl.SslProvider;
import io.netty.handler.timeout.ReadTimeoutHandler;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Supplier;
import javax.inject.Named;
import javax.inject.Provider;
import javax.inject.Singleton;
import org.junit.Before;
/**
* Base class for end-to-end tests of a {@link Protocol}.
*
* <p>The end-to-end tests ensures that the business logic that a {@link Protocol} defines are
* correctly performed by various handlers attached to its pipeline. Non-business essential handlers
* should be excluded.
*
* <p>Subclass should implement an no-arg constructor that calls constructors of this class,
* providing the method reference of the {@link TestComponent} method to call to obtain the list of
* {@link ChannelHandler} providers for the {@link Protocol} to test, and optionally a set of {@link
* ChannelHandler} classes to exclude from testing.
*/
public abstract class ProtocolModuleTest {
protected static final ProxyConfig PROXY_CONFIG = getProxyConfig(LOCAL);
protected TestComponent testComponent;
/**
* Default list of handler classes that are not of interest in end-to-end testing of the {@link
* Protocol}.
*/
private static final ImmutableSet<Class<? extends ChannelHandler>> DEFAULT_EXCLUDED_HANDLERS =
ImmutableSet.of(
// The PROXY protocol is only used when the proxy is behind the GCP load balancer. It is
// not part of any business logic.
ProxyProtocolHandler.class,
// SSL is part of the business logic for some protocol (EPP for example), but its
// impact is isolated. Including it makes tests much more complicated. It should be tested
// separately in its own unit tests.
SslClientInitializer.class,
SslServerInitializer.class,
// These two handlers provide essential functionalities for the proxy to operate, but they
// do not directly implement the business logic of a well-defined protocol. They should be
// tested separately in their respective unit tests.
FullHttpRequestRelayHandler.class,
FullHttpResponseRelayHandler.class,
// This handler is tested in its own unit tests. It is installed in web whois redirect
// protocols. The end-to-end tests for the rest of the handlers in its pipeline need to
// be able to emit incoming requests out of the channel for assertions. Therefore this
// handler is removed from the pipeline.
WebWhoisRedirectHandler.class,
// The rest are not part of business logic and do not need to be tested, obviously.
LoggingHandler.class,
// Metrics instrumentation is tested separately.
BackendMetricsHandler.class,
// Quota management is tested separately.
WhoisQuotaHandler.class,
EppQuotaHandler.class,
ReadTimeoutHandler.class);
protected EmbeddedChannel channel;
/**
* Method reference to the component method that exposes the list of handler providers for the
* specific {@link Protocol} in interest.
*/
protected final Function<TestComponent, ImmutableList<Provider<? extends ChannelHandler>>>
handlerProvidersMethod;
protected final ImmutableSet<Class<? extends ChannelHandler>> excludedHandlers;
protected ProtocolModuleTest(
Function<TestComponent, ImmutableList<Provider<? extends ChannelHandler>>>
handlerProvidersMethod,
ImmutableSet<Class<? extends ChannelHandler>> excludedHandlers) {
this.handlerProvidersMethod = handlerProvidersMethod;
this.excludedHandlers = excludedHandlers;
}
protected ProtocolModuleTest(
Function<TestComponent, ImmutableList<Provider<? extends ChannelHandler>>>
handlerProvidersMethod) {
this(handlerProvidersMethod, DEFAULT_EXCLUDED_HANDLERS);
}
/** Excludes handler providers that are not of interested for testing. */
private ImmutableList<Provider<? extends ChannelHandler>> excludeHandlerProvidersForTesting(
ImmutableList<Provider<? extends ChannelHandler>> handlerProviders) {
return handlerProviders
.stream()
.filter(handlerProvider -> !excludedHandlers.contains(handlerProvider.get().getClass()))
.collect(toImmutableList());
}
protected void initializeChannel(Consumer<Channel> initializer) {
channel =
new EmbeddedChannel(
new ChannelInitializer<Channel>() {
@Override
protected void initChannel(Channel ch) throws Exception {
initializer.accept(ch);
}
});
}
/** Adds handlers to the channel pipeline, excluding any one in {@link #excludedHandlers}. */
void addAllTestableHandlers(Channel ch) {
for (Provider<? extends ChannelHandler> handlerProvider :
excludeHandlerProvidersForTesting(handlerProvidersMethod.apply(testComponent))) {
ch.pipeline().addLast(handlerProvider.get());
}
}
static TestComponent makeTestComponent(FakeClock fakeClock) {
return DaggerProtocolModuleTest_TestComponent.builder()
.testModule(new TestModule(new FakeClock()))
.build();
}
@Before
public void setUp() throws Exception {
testComponent = makeTestComponent(new FakeClock());
initializeChannel(this::addAllTestableHandlers);
}
/**
* Component used to obtain the list of {@link ChannelHandler} providers for each {@link
* Protocol}.
*/
@Singleton
@Component(
modules = {
TestModule.class,
CertificateModule.class,
WhoisProtocolModule.class,
WebWhoisProtocolsModule.class,
EppProtocolModule.class,
HealthCheckProtocolModule.class,
HttpsRelayProtocolModule.class
})
interface TestComponent {
@WhoisProtocol
ImmutableList<Provider<? extends ChannelHandler>> whoisHandlers();
@EppProtocol
ImmutableList<Provider<? extends ChannelHandler>> eppHandlers();
@HealthCheckProtocol
ImmutableList<Provider<? extends ChannelHandler>> healthCheckHandlers();
@HttpsRelayProtocol
ImmutableList<Provider<? extends ChannelHandler>> httpsRelayHandlers();
@HttpWhoisProtocol
ImmutableList<Provider<? extends ChannelHandler>> httpWhoisHandlers();
}
/**
* Module that provides bindings used in tests.
*
* <p>Most of the binding provided in this module should be either a fake, or a {@link
* ChannelHandler} that is excluded, and annotated with {@code @Singleton}. This module acts as a
* replacement for {@link ProxyModule} used in production component. Providing a handler that is
* part of the business logic of a {@link Protocol} from this module is a sign that the binding
* should be provided in the respective {@code ProtocolModule} instead.
*/
@Module
static class TestModule {
/**
* A fake clock that is explicitly provided. Users can construct a module with a controller
* clock.
*/
private final FakeClock fakeClock;
TestModule(FakeClock fakeClock) {
this.fakeClock = fakeClock;
}
@Singleton
@Provides
static ProxyConfig provideProxyConfig() {
return getProxyConfig(LOCAL);
}
@Singleton
@Provides
static SslProvider provideSslProvider() {
return SslProvider.JDK;
}
@Singleton
@Provides
@Named("accessToken")
static Supplier<String> provideFakeAccessToken() {
return Suppliers.ofInstance("fake.test.token");
}
@Singleton
@Provides
static LoggingHandler provideLoggingHandler() {
return new LoggingHandler();
}
@Singleton
@Provides
Clock provideFakeClock() {
return fakeClock;
}
@Singleton
@Provides
static ExecutorService provideExecutorService() {
return MoreExecutors.newDirectExecutorService();
}
@Singleton
@Provides
static ScheduledExecutorService provideScheduledExecutorService() {
return Executors.newSingleThreadScheduledExecutor();
}
@Singleton
@Provides
static Environment provideEnvironment() {
return Environment.LOCAL;
}
// This method is only here to satisfy Dagger binding, but is never used. In test environment,
// it is the self-signed certificate and its key that end up being used.
@Singleton
@Provides
@Named("pemBytes")
static byte[] providePemBytes() {
return new byte[0];
}
}
}