mirror of
https://github.com/google/nomulus.git
synced 2025-07-06 19:23:31 +02:00
Prober EPP components added (#202)
* Updated issues in rebasing * Minor style change on prober/build.gradle * Fixed warnings for java compilation * Fixed files to pass all style tests * Initial Commit. * Deleted unfinished features. Added ActionHandler and its Unit Tests. * Included prober subproject in settings.gradle * Added Protocol Class and its Basic Unit Tests * Added Changes Suggested by jianglai * Fixed Gitignore to take out AutoValue generated code * Removed AutoValue java files * Added gitignore within prober * Removed all generated java * Final Changes in .gitignore * Added Ssl and WebWhois Action Handlers and their unit tests in addition to the ProbingAction class * Fixed build.gradle changes requested * Removed Files irrelevant to current pull request * Minor fixes to ActionHandler, as responded in comments, removed package-info, and updated settings.gradle * Fully Updated ActionHandler (missing updated JavaDoc) * Added changed Protocol and both Inbound and Outbound Markers * Removed AutoVaue ignore clause from .gitignore * removed unneccessary dependencies in build.gradle * Fixed Javadoc and comments for ActionHandler * Fixed comments and JavaDoc on other files * EOL added * Removed Unnecessary Files * fixed .gradle files styles * Removed outbound message from ActionHandler's fields and renamed Marker Interfaces * Fixed javadoc for Marker Interfaced * Modified Comments on ActionHandler * Removed LocalAddress from Protocol * Fixed Travis Build Issues * Rebased to Master and added in modified Handlers and ProbingAction * Fixed changes suggested by CydeWeys * Added missing license headers and JavaDoc * Minor fix in NewChannelAction JavaDoc * Minor Style Fix * Full WebWhoIs Sequence Added * fixed build issues * Refactored by responses suggested by jianglai. * Updated build.gradle file * Modified license header dates * Updated WebWhois tests. * Refactored WebWhois to accomodate jianglai's suggested changes and modified tests to reflect this refactoring * SpotlessApply run to fix style issues * Added license header and newline where appropriate. * Javadoc style fix in tests and removed unused methods * Refactored ProbingAction to minimize number of unnecessary methods * Modified tests for WebWhois according to changes suggested by laijiang. * Removed TestProvider from TestUtils. * Rebased to Master and added in modified Handlers and ProbingAction * Fixed changes suggested by CydeWeys * Added missing license headers and JavaDoc * Minor fix in NewChannelAction JavaDoc * Minor Style Fix * Full WebWhoIs Sequence Added * fixed build issues * Refactored by responses suggested by jianglai. * Updated build.gradle file * Modified license header dates * Updated WebWhois tests. * Refactored WebWhois to accomodate jianglai's suggested changes and modified tests to reflect this refactoring * SpotlessApply run to fix style issues * Added license header and newline where appropriate. * Javadoc style fix in tests and removed unused methods * Refactored ProbingAction to minimize number of unnecessary methods * Modified tests for WebWhois according to changes suggested by laijiang. * Removed TestProvider from TestUtils. * Rebased to master * Updated issues in rebasing * Minor style change on prober/build.gradle * Fixed warnings for java compilation * Fixed files to pass all style tests * Minor syle fixes after succesful rebase onto master * Initial Commit. * Added Protocol Class and its Basic Unit Tests * Fixed Gitignore to take out AutoValue generated code * Final Changes in .gitignore * Minor fixes to ActionHandler, as responded in comments, removed package-info, and updated settings.gradle * Removed AutoVaue ignore clause from .gitignore * Rebased to Master and added in modified Handlers and ProbingAction * Full WebWhoIs Sequence Added * fixed build issues * Refactored by responses suggested by jianglai. * Added missing license headers and JavaDoc * Minor fix in NewChannelAction JavaDoc * Refactored WebWhois to accomodate jianglai's suggested changes and modified tests to reflect this refactoring * Full WebWhoIs Sequence Added * fixed build issues * Refactored by responses suggested by jianglai. * Modified license header dates * Refactored WebWhois to accomodate jianglai's suggested changes and modified tests to reflect this refactoring * SpotlessApply run to fix style issues * Added license header and newline where appropriate. * Javadoc style fix in tests and removed unused methods * Fixed files to pass all style tests * Fixed changes suggested by CydeWeys * Rebased to Master and added in modified Handlers and ProbingAction * Added missing license headers and JavaDoc * Minor fix in NewChannelAction JavaDoc * Minor Style Fix * Full WebWhoIs Sequence Added * Refactored by responses suggested by jianglai. * Updated build.gradle file * Modified license header dates * Updated WebWhois tests. * Refactored WebWhois to accomodate jianglai's suggested changes and modified tests to reflect this refactoring * SpotlessApply run to fix style issues * Added license header and newline where appropriate. * Javadoc style fix in tests and removed unused methods * Refactored ProbingAction to minimize number of unnecessary methods * Modified tests for WebWhois according to changes suggested by laijiang. * Removed TestProvider from TestUtils. * Rebased to Master and added in modified Handlers and ProbingAction * Fixed changes suggested by CydeWeys * Added missing license headers and JavaDoc * Minor fix in NewChannelAction JavaDoc * Minor Style Fix * Full WebWhoIs Sequence Added * fixed build issues * Refactored by responses suggested by jianglai. * Updated build.gradle file * Modified license header dates * Updated WebWhois tests. * Added license header and newline where appropriate. * Javadoc style fix in tests and removed unused methods * Refactored ProbingAction to minimize number of unnecessary methods * Modified tests for WebWhois according to changes suggested by laijiang. * Removed TestProvider from TestUtils. * Rebased to master * Updated issues in rebasing * Added circular linked list to utils * License Header added * Refactored probing sequence to be circular linked list iterator * Modified ProbingStep tests to reflect new ProbingStep structure. * Added circular linked list to utils * Added circular linked list to utils * License Header added * License Header added * Refactored probing sequence to be circular linked list iterator * Modified ProbingStep tests to reflect new ProbingStep structure. * Added missing license header to DefaultCircularLinkedListIterator * Fixed changes suggested by CydeWeys * Rebased to Master and added in modified Handlers and ProbingAction * Added missing license headers and JavaDoc * Minor fix in NewChannelAction JavaDoc * Minor Style Fix * Full WebWhoIs Sequence Added * fixed build issues * Refactored by responses suggested by jianglai. * Updated build.gradle file * Fixed max column length to be 100 * Rebased to Master and added in modified Handlers and ProbingAction * Modified license header dates * Updated WebWhois tests. * Refactored WebWhois to accomodate jianglai's suggested changes and modified tests to reflect this refactoring * Javadoc style fix in tests and removed unused methods * Refactored ProbingAction to minimize number of unnecessary methods * Modified tests for WebWhois according to changes suggested by laijiang. * Fixed changes suggested by CydeWeys * Added missing license headers and JavaDoc * Minor fix in NewChannelAction JavaDoc * Minor Style Fix * Full WebWhoIs Sequence Added * fixed build issues * Refactored by responses suggested by jianglai. * Updated build.gradle file * Modified license header dates * Updated WebWhois tests. * Refactored WebWhois to accomodate jianglai's suggested changes and modified tests to reflect this refactoring * SpotlessApply run to fix style issues * Added license header and newline where appropriate. * Javadoc style fix in tests and removed unused methods * Refactored ProbingAction to minimize number of unnecessary methods * Added circular linked list to utils * Added circular linked list to utils * License Header added * License Header added * Refactored probing sequence to be circular linked list iterator * Refactored probing sequence to be circular linked list iterator * Modified tests for WebWhois according to changes suggested by laijiang. * Removed TestProvider from TestUtils. * ProbingStepTest modified to have fewer unnecessary helper methods * Updated issues in rebasing * Fixed max column length to be 100 * Minor changes to pass style tests * Successful rebase onto finished web-whois branch * Removed need for TestTokens with Mockito mocks of Tokens * Fixed style issues in DefaultCircularLinkedListIterator and AbstractCircularLinkedListIterator * Modified CircularList according to changes suggested by jianglai. * Added Protocol Class and its Basic Unit Tests * Added Ssl and WebWhois Action Handlers and their unit tests in addition to the ProbingAction class * Fixed changes suggested by CydeWeys * Fixed changes suggested by CydeWeys * Rebased to Master and added in modified Handlers and ProbingAction * Rebased to Master and added in modified Handlers and ProbingAction * Rebased to Master and added in modified Handlers and ProbingAction * Rebased to Master and added in modified Handlers and ProbingAction * Rebased to Master and added in modified Handlers and ProbingAction * Added missing license headers and JavaDoc * Minor Style Fix * Minor Style Fix * Full WebWhoIs Sequence Added * Full WebWhoIs Sequence Added * Full WebWhoIs Sequence Added * Full WebWhoIs Sequence Added * fixed build issues * Refactored by responses suggested by jianglai. * Updated build.gradle file * Updated WebWhois tests. * Added Basic EPP structure * Added Basic EPP structure * Prober Updated tests * Prober Updated tests * Fully functioning EPP sequences with modified WebWhois base. * Fully functioning EPP sequences with modified WebWhois base. * Added Modified test server infrastructure. * Added Modified test server infrastructure. * Allowed ActionHandler to pass status to next hanlder in pipeline (to be MetricsHandler). * Allowed ActionHandler to pass status to next hanlder in pipeline (to be MetricsHandler). * Javadoc on EppRequestMessage added * Javadoc on EppRequestMessage added * Updated EppServer to properly send successful Check responses. * Updated EppServer to properly send successful Check responses. * Allowed for expected failures in EPP actions. * Refactored WebWhois to accomodate jianglai's suggested changes and modified tests to reflect this refactoring * Refactored WebWhois to accomodate jianglai's suggested changes and modified tests to reflect this refactoring * Refactored WebWhois to accomodate jianglai's suggested changes and modified tests to reflect this refactoring * Refactored WebWhois to accomodate jianglai's suggested changes and modified tests to reflect this refactoring * Fully rebased branch to prober-web-whois after refactoring * Added license header and newline where appropriate. * Javadoc style fix in tests and removed unused methods * Javadoc style fix in tests and removed unused methods * Modified tests for WebWhois according to changes suggested by laijiang. * Modified tests for WebWhois according to changes suggested by laijiang. * Removed TestProvider from TestUtils. * Rebased to master * Fixed max column length to be 100 * Fixed files to pass all style tests * Minor changes to pass style tests * Successful rebase onto circular-list * Epp Refactored to accomodate circular linked list PR * Modified construction of Epp Probing Sequences to reflect CircularList change * Renamed ProberModule provided Duration * Removed unnecessary ServerSideException file * Google-Java-Format run on all prober files * Style fix on ProbingSequence and its unit tests * Removed subclasses of EppRequestMessage and EppResponseMessage and fixed style and other minor issues * Style changes implemented as suggested by jianglai * Added style fixes suggested by mindhog
This commit is contained in:
parent
86fefa9a03
commit
57975898d5
66 changed files with 6089 additions and 1083 deletions
2
prober/.gitignore
vendored
2
prober/.gitignore
vendored
|
@ -1,2 +1,2 @@
|
|||
out/
|
||||
src/main/resources/google/registry/monitoring/blackbox/modules/secrets/
|
||||
src/main/resources/google/registry/monitoring/blackbox/modules/secrets/
|
|
@ -23,19 +23,16 @@ import google.registry.monitoring.blackbox.ProberModule.ProberComponent;
|
|||
*/
|
||||
public class Prober {
|
||||
|
||||
/**
|
||||
* Main Dagger Component
|
||||
*/
|
||||
private static ProberComponent proberComponent = DaggerProberModule_ProberComponent.builder()
|
||||
.build();
|
||||
|
||||
/** Main Dagger Component */
|
||||
private static ProberComponent proberComponent =
|
||||
DaggerProberModule_ProberComponent.builder().build();
|
||||
|
||||
public static void main(String[] args) {
|
||||
|
||||
//Obtains WebWhois Sequence provided by proberComponent
|
||||
// Obtains WebWhois Sequence provided by proberComponent
|
||||
ImmutableSet<ProbingSequence> sequences = ImmutableSet.copyOf(proberComponent.sequences());
|
||||
|
||||
//Tells Sequences to start running
|
||||
// Tells Sequences to start running
|
||||
for (ProbingSequence sequence : sequences) {
|
||||
sequence.start();
|
||||
}
|
||||
|
|
|
@ -17,6 +17,11 @@ package google.registry.monitoring.blackbox;
|
|||
import dagger.Component;
|
||||
import dagger.Module;
|
||||
import dagger.Provides;
|
||||
import google.registry.monitoring.blackbox.connection.ProbingAction;
|
||||
import google.registry.monitoring.blackbox.modules.CertificateModule;
|
||||
import google.registry.monitoring.blackbox.modules.EppModule;
|
||||
import google.registry.monitoring.blackbox.modules.WebWhoisModule;
|
||||
import io.netty.bootstrap.Bootstrap;
|
||||
import io.netty.channel.Channel;
|
||||
import io.netty.channel.EventLoopGroup;
|
||||
import io.netty.channel.nio.NioEventLoopGroup;
|
||||
|
@ -35,10 +40,8 @@ import org.joda.time.Duration;
|
|||
@Module
|
||||
public class ProberModule {
|
||||
|
||||
/**
|
||||
* Default {@link Duration} chosen to be time between each {@link ProbingAction} call.
|
||||
*/
|
||||
private static final Duration DEFAULT_DURATION = Duration.standardSeconds(4);
|
||||
/** Default {@link Duration} chosen to be time between each {@link ProbingAction} call. */
|
||||
private static final Duration DEFAULT_PROBER_INTERVAL = Duration.standardSeconds(4);
|
||||
|
||||
/**
|
||||
* {@link Provides} the {@link SslProvider} used by instances of {@link
|
||||
|
@ -51,9 +54,7 @@ public class ProberModule {
|
|||
return OpenSsl.isAvailable() ? SslProvider.OPENSSL : SslProvider.JDK;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@link Provides} one global {@link EventLoopGroup} shared by each {@link ProbingSequence}.
|
||||
*/
|
||||
/** {@link Provides} one global {@link EventLoopGroup} shared by each {@link ProbingSequence}. */
|
||||
@Provides
|
||||
@Singleton
|
||||
EventLoopGroup provideEventLoopGroup() {
|
||||
|
@ -71,27 +72,36 @@ public class ProberModule {
|
|||
}
|
||||
|
||||
/**
|
||||
* {@link Provides} above {@code DEFAULT_DURATION} for all provided {@link ProbingStep}s to use.
|
||||
* {@link Provides} above {@code DEFAULT_PROBER_INTERVAL} for all provided {@link ProbingStep}s to
|
||||
* use.
|
||||
*/
|
||||
@Provides
|
||||
@Singleton
|
||||
Duration provideDuration() {
|
||||
return DEFAULT_DURATION;
|
||||
Duration provideProbeInterval() {
|
||||
return DEFAULT_PROBER_INTERVAL;
|
||||
}
|
||||
|
||||
/**
|
||||
* Root level {@link Component} that provides each {@link ProbingSequence}.
|
||||
* {@link Provides} general {@link Bootstrap} for which a new instance is provided in any {@link
|
||||
* ProbingSequence}.
|
||||
*/
|
||||
@Provides
|
||||
Bootstrap provideBootstrap(EventLoopGroup eventLoopGroup) {
|
||||
return new Bootstrap().group(eventLoopGroup).channel(NioSocketChannel.class);
|
||||
}
|
||||
|
||||
/** Root level {@link Component} that provides each {@link ProbingSequence}. */
|
||||
@Singleton
|
||||
@Component(
|
||||
modules = {
|
||||
ProberModule.class,
|
||||
WebWhoisModule.class,
|
||||
ProberModule.class,
|
||||
WebWhoisModule.class,
|
||||
EppModule.class,
|
||||
CertificateModule.class
|
||||
})
|
||||
public interface ProberComponent {
|
||||
|
||||
//Standard WebWhois sequence
|
||||
// Standard WebWhois sequence
|
||||
Set<ProbingSequence> sequences();
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,316 +0,0 @@
|
|||
// Copyright 2019 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.monitoring.blackbox;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkArgument;
|
||||
import static com.google.common.base.Preconditions.checkNotNull;
|
||||
import static com.google.common.flogger.StackSize.SMALL;
|
||||
import static google.registry.monitoring.blackbox.Protocol.PROTOCOL_KEY;
|
||||
|
||||
import com.google.auto.value.AutoValue;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.flogger.FluentLogger;
|
||||
import google.registry.monitoring.blackbox.exceptions.UndeterminedStateException;
|
||||
import google.registry.monitoring.blackbox.handlers.ActionHandler;
|
||||
import google.registry.monitoring.blackbox.messages.OutboundMessageType;
|
||||
import io.netty.bootstrap.Bootstrap;
|
||||
import io.netty.channel.Channel;
|
||||
import io.netty.channel.ChannelFuture;
|
||||
import io.netty.channel.ChannelHandler;
|
||||
import io.netty.channel.ChannelInitializer;
|
||||
import io.netty.channel.ChannelPipeline;
|
||||
import io.netty.channel.ChannelPromise;
|
||||
import io.netty.channel.local.LocalAddress;
|
||||
import io.netty.util.AttributeKey;
|
||||
import io.netty.util.HashedWheelTimer;
|
||||
import io.netty.util.Timer;
|
||||
import java.net.InetAddress;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.net.SocketAddress;
|
||||
import java.net.UnknownHostException;
|
||||
import java.util.concurrent.Callable;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import javax.inject.Provider;
|
||||
import org.joda.time.Duration;
|
||||
|
||||
/**
|
||||
* AutoValue class that represents action generated by {@link ProbingStep}
|
||||
*
|
||||
* <p> Inherits from {@link Callable<ChannelFuture>}, as it has can be called
|
||||
* to perform its specified task, and return the {@link ChannelFuture} that will be informed when
|
||||
* the task has been completed </p>
|
||||
*
|
||||
* <p> Is an immutable class, as it is comprised of the tools necessary for making a specific type
|
||||
* of connection. It goes hand in hand with {@link Protocol}, which specifies the kind of overall
|
||||
* connection to be made. {@link Protocol} gives the outline and {@link ProbingAction} gives the
|
||||
* details of that connection.</p>
|
||||
*
|
||||
* <p> In its build, if there is no channel supplied, it will create a channel from the attributes
|
||||
* already supplied. Then, it only sends the {@link OutboundMessageType} down the pipeline when
|
||||
* informed that the connection is successful. If the channel is supplied, the connection future is
|
||||
* automatically set to successful.</p>
|
||||
*/
|
||||
|
||||
@AutoValue
|
||||
public abstract class ProbingAction implements Callable<ChannelFuture> {
|
||||
|
||||
/**
|
||||
* {@link AttributeKey} in channel that gives {@link ChannelFuture} that is set to success when
|
||||
* channel is active.
|
||||
*/
|
||||
public static final AttributeKey<ChannelFuture> CONNECTION_FUTURE_KEY = AttributeKey
|
||||
.valueOf("CONNECTION_FUTURE_KEY");
|
||||
/**
|
||||
* {@link AttributeKey} in channel that gives the information of the channel's host.
|
||||
*/
|
||||
public static final AttributeKey<String> REMOTE_ADDRESS_KEY = AttributeKey
|
||||
.valueOf("REMOTE_ADDRESS_KEY");
|
||||
private static final FluentLogger logger = FluentLogger.forEnclosingClass();
|
||||
/**
|
||||
* {@link Timer} that rate limits probing
|
||||
*/
|
||||
private static final Timer timer = new HashedWheelTimer();
|
||||
|
||||
public static Builder builder() {
|
||||
return new AutoValue_ProbingAction.Builder();
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds provided {@link ChannelHandler}s to the {@link ChannelPipeline} specified
|
||||
*
|
||||
* @param channelPipeline is pipeline associated with channel that we want to add handlers to
|
||||
* @param handlerProviders are a list of provider objects that give us the requisite handlers Adds
|
||||
* to the pipeline, the list of handlers in the order specified
|
||||
*/
|
||||
private static void addHandlers(
|
||||
ChannelPipeline channelPipeline,
|
||||
ImmutableList<Provider<? extends ChannelHandler>> handlerProviders) {
|
||||
for (Provider<? extends ChannelHandler> handlerProvider : handlerProviders) {
|
||||
channelPipeline.addLast(handlerProvider.get());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Actual {@link Duration} of this delay
|
||||
*/
|
||||
public abstract Duration delay();
|
||||
|
||||
/**
|
||||
* {@link OutboundMessageType} instance that we write and flush down pipeline to server
|
||||
*/
|
||||
public abstract OutboundMessageType outboundMessage();
|
||||
|
||||
/**
|
||||
* {@link Channel} object that either created by or passed into this {@link ProbingAction}
|
||||
* instance
|
||||
*/
|
||||
public abstract Channel channel();
|
||||
|
||||
/**
|
||||
* The {@link Protocol} instance that specifies type of connection
|
||||
*/
|
||||
public abstract Protocol protocol();
|
||||
|
||||
/**
|
||||
* The hostname of the remote host we have a connection or will make a connection to
|
||||
*/
|
||||
public abstract String host();
|
||||
|
||||
/**
|
||||
* Performs the work of the actual action.
|
||||
*
|
||||
* <p>First, checks if channel is active by setting a listener to perform the bulk of the work
|
||||
* when the connection future is successful.</p>
|
||||
*
|
||||
* <p>Once the connection is successful, we establish which of the handlers in the pipeline is
|
||||
* the {@link ActionHandler}.From that, we can obtain a future that is marked as a success when we
|
||||
* receive an expected response from the server.</p>
|
||||
*
|
||||
* <p>Next, we set a timer set to a specified delay. After the delay has passed, we send the
|
||||
* {@code outboundMessage} down the channel pipeline, and when we observe a success or failure, we
|
||||
* inform the {@link ProbingStep} of this.</p>
|
||||
*
|
||||
* @return {@link ChannelFuture} that denotes when the action has been successfully performed.
|
||||
*/
|
||||
@Override
|
||||
public ChannelFuture call() {
|
||||
//ChannelPromise that we return
|
||||
ChannelPromise finished = channel().newPromise();
|
||||
|
||||
//Ensures channel has been set up with connection future as an attribute
|
||||
checkNotNull(channel().attr(CONNECTION_FUTURE_KEY).get());
|
||||
|
||||
//When connection is established call super.call and set returned listener to success
|
||||
channel().attr(CONNECTION_FUTURE_KEY).get().addListener(
|
||||
(ChannelFuture connectionFuture) -> {
|
||||
if (connectionFuture.isSuccess()) {
|
||||
logger.atInfo().log(String
|
||||
.format("Successful connection to remote host: %s at port: %d", host(),
|
||||
protocol().port()));
|
||||
|
||||
ActionHandler actionHandler;
|
||||
try {
|
||||
actionHandler = channel().pipeline().get(ActionHandler.class);
|
||||
} catch (ClassCastException e) {
|
||||
//If we don't actually have an ActionHandler instance, we have an issue, and throw
|
||||
// an UndeterminedStateException
|
||||
logger.atSevere().withStackTrace(SMALL).log("ActionHandler not in Channel Pipeline");
|
||||
throw new UndeterminedStateException("No Action Handler found in pipeline");
|
||||
}
|
||||
ChannelFuture channelFuture = actionHandler.getFinishedFuture();
|
||||
|
||||
timer.newTimeout(timeout -> {
|
||||
// Write appropriate outboundMessage to pipeline
|
||||
ChannelFuture unusedFutureWriteAndFlush =
|
||||
channel().writeAndFlush(outboundMessage());
|
||||
channelFuture.addListeners(
|
||||
future -> {
|
||||
if (future.isSuccess()) {
|
||||
ChannelFuture unusedFuture = finished.setSuccess();
|
||||
} else {
|
||||
ChannelFuture unusedFuture = finished.setFailure(future.cause());
|
||||
}
|
||||
},
|
||||
//If we don't have a persistent connection, close the connection to this
|
||||
// channel
|
||||
future -> {
|
||||
if (!protocol().persistentConnection()) {
|
||||
|
||||
ChannelFuture closedFuture = channel().close();
|
||||
closedFuture.addListener(
|
||||
f -> {
|
||||
if (f.isSuccess()) {
|
||||
logger.atInfo()
|
||||
.log("Closed stale channel. Moving on to next ProbingStep");
|
||||
} else {
|
||||
logger.atWarning()
|
||||
.log(
|
||||
"Could not close channel. Stale connection still exists"
|
||||
+ ".");
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
);
|
||||
},
|
||||
delay().getStandardSeconds(),
|
||||
TimeUnit.SECONDS);
|
||||
} else {
|
||||
//if we receive a failure, log the failure, and close the channel
|
||||
logger.atSevere().withCause(connectionFuture.cause()).log(
|
||||
"Cannot connect to relay channel for %s channel: %s.",
|
||||
protocol().name(), this.channel());
|
||||
ChannelFuture unusedFuture = channel().close();
|
||||
}
|
||||
}
|
||||
);
|
||||
return finished;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final String toString() {
|
||||
return String.format(
|
||||
"ProbingAction with delay: %d\n"
|
||||
+ "outboundMessage: %s\n"
|
||||
+ "protocol: %s\n"
|
||||
+ "host: %s\n",
|
||||
delay().getStandardSeconds(),
|
||||
outboundMessage(),
|
||||
protocol(),
|
||||
host()
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@link AutoValue.Builder} that does work of creating connection when not already present.
|
||||
*/
|
||||
@AutoValue.Builder
|
||||
public abstract static class Builder {
|
||||
|
||||
private Bootstrap bootstrap;
|
||||
|
||||
public Builder setBootstrap(Bootstrap bootstrap) {
|
||||
this.bootstrap = bootstrap;
|
||||
return this;
|
||||
}
|
||||
|
||||
public abstract Builder setDelay(Duration value);
|
||||
|
||||
public abstract Builder setOutboundMessage(OutboundMessageType value);
|
||||
|
||||
public abstract Builder setProtocol(Protocol value);
|
||||
|
||||
public abstract Builder setHost(String value);
|
||||
|
||||
public abstract Builder setChannel(Channel channel);
|
||||
|
||||
abstract Protocol protocol();
|
||||
|
||||
abstract Channel channel();
|
||||
|
||||
abstract String host();
|
||||
|
||||
abstract ProbingAction autoBuild();
|
||||
|
||||
public ProbingAction build() {
|
||||
// Sets SocketAddress to bind to.
|
||||
SocketAddress address;
|
||||
try {
|
||||
InetAddress hostAddress = InetAddress.getByName(host());
|
||||
address = new InetSocketAddress(hostAddress, protocol().port());
|
||||
} catch (UnknownHostException e) {
|
||||
address = new LocalAddress(host());
|
||||
}
|
||||
|
||||
//Sets channel supplied or to be created.
|
||||
Channel channel;
|
||||
try {
|
||||
channel = channel();
|
||||
} catch (IllegalStateException e) {
|
||||
channel = null;
|
||||
}
|
||||
|
||||
checkArgument(channel == null ^ bootstrap == null,
|
||||
"One and only one of bootstrap and channel must be supplied.");
|
||||
//If a channel is supplied, nothing is needed to be done
|
||||
|
||||
//Otherwise, a Bootstrap must be supplied and be used for creating the channel
|
||||
if (channel == null) {
|
||||
bootstrap.handler(
|
||||
new ChannelInitializer<Channel>() {
|
||||
@Override
|
||||
protected void initChannel(Channel outboundChannel)
|
||||
throws Exception {
|
||||
//Uses Handlers from Protocol to fill pipeline
|
||||
addHandlers(outboundChannel.pipeline(), protocol().handlerProviders());
|
||||
}
|
||||
})
|
||||
.attr(PROTOCOL_KEY, protocol())
|
||||
.attr(REMOTE_ADDRESS_KEY, host());
|
||||
|
||||
logger.atInfo().log("Initialized bootstrap with channel Handlers");
|
||||
//ChannelFuture that performs action when connection is established
|
||||
ChannelFuture connectionFuture = bootstrap.connect(address);
|
||||
|
||||
setChannel(connectionFuture.channel());
|
||||
connectionFuture.channel().attr(CONNECTION_FUTURE_KEY).set(connectionFuture);
|
||||
}
|
||||
|
||||
//now we can actually build the ProbingAction
|
||||
return autoBuild();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -15,6 +15,7 @@
|
|||
package google.registry.monitoring.blackbox;
|
||||
|
||||
import com.google.common.flogger.FluentLogger;
|
||||
import google.registry.monitoring.blackbox.connection.ProbingAction;
|
||||
import google.registry.monitoring.blackbox.exceptions.UnrecoverableStateException;
|
||||
import google.registry.monitoring.blackbox.tokens.Token;
|
||||
import google.registry.util.CircularList;
|
||||
|
@ -26,29 +27,25 @@ import io.netty.channel.EventLoopGroup;
|
|||
/**
|
||||
* Represents Sequence of {@link ProbingStep}s that the Prober performs in order.
|
||||
*
|
||||
* <p>Inherits from {@link CircularList}, with element type of
|
||||
* {@link ProbingStep} as the manner in which the sequence is carried out is similar to the {@link
|
||||
* CircularList}. However, the {@link Builder} of {@link ProbingSequence} override
|
||||
* {@link CircularList.AbstractBuilder} allowing for more customized flows, that are looped, but
|
||||
* not necessarily entirely circular.
|
||||
* Example: first -> second -> third -> fourth -> second -> third -> fourth -> second -> ...
|
||||
* </p>
|
||||
* <p>Inherits from {@link CircularList}, with element type of {@link ProbingStep} as the manner in
|
||||
* which the sequence is carried out is similar to the {@link CircularList}. However, the {@link
|
||||
* Builder} of {@link ProbingSequence} override {@link CircularList.AbstractBuilder} allowing for
|
||||
* more customized flows, that are looped, but not necessarily entirely circular. Example: first ->
|
||||
* second -> third -> fourth -> second -> third -> fourth -> second -> ...
|
||||
*
|
||||
* <p>Created with {@link Builder} where we specify {@link EventLoopGroup}, {@link AbstractChannel}
|
||||
* class type, then sequentially add in the {@link ProbingStep.Builder}s in order and mark which one
|
||||
* is the first repeated step.</p>
|
||||
* is the first repeated step.
|
||||
*
|
||||
* <p>{@link ProbingSequence} implicitly points each {@link ProbingStep} to the next one, so once
|
||||
* the first one is activated with the requisite {@link Token}, the {@link ProbingStep}s do the rest
|
||||
* of the work.</p>
|
||||
* of the work.
|
||||
*/
|
||||
public class ProbingSequence extends CircularList<ProbingStep> {
|
||||
|
||||
private static final FluentLogger logger = FluentLogger.forEnclosingClass();
|
||||
|
||||
/**
|
||||
* Each {@link ProbingSequence} requires a start token to begin running.
|
||||
*/
|
||||
/** Each {@link ProbingSequence} requires a start token to begin running. */
|
||||
private Token startToken;
|
||||
|
||||
/**
|
||||
|
@ -57,38 +54,27 @@ public class ProbingSequence extends CircularList<ProbingStep> {
|
|||
*/
|
||||
private boolean lastStep = false;
|
||||
|
||||
/**
|
||||
* {@link ProbingSequence} object that represents first step in the sequence.
|
||||
*/
|
||||
/** {@link ProbingSequence} object that represents first step in the sequence. */
|
||||
private ProbingSequence first;
|
||||
|
||||
|
||||
/**
|
||||
* Standard constructor for {@link ProbingSequence} in the list that assigns value and token.
|
||||
*/
|
||||
/** Standard constructor for {@link ProbingSequence} in the list that assigns value and token. */
|
||||
private ProbingSequence(ProbingStep value, Token startToken) {
|
||||
super(value);
|
||||
this.startToken = startToken;
|
||||
}
|
||||
|
||||
/**
|
||||
* Method used in {@link Builder} to mark the last step in the sequence.
|
||||
*/
|
||||
/** Method used in {@link Builder} to mark the last step in the sequence. */
|
||||
private void markLast() {
|
||||
lastStep = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Obtains next {@link ProbingSequence} in sequence instead of next {@link CircularList}.
|
||||
*/
|
||||
/** Obtains next {@link ProbingSequence} in sequence. */
|
||||
@Override
|
||||
public ProbingSequence next() {
|
||||
return (ProbingSequence) super.next();
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts ProbingSequence by calling first {@code runStep} with {@code startToken}.
|
||||
*/
|
||||
/** Starts ProbingSequence by calling first {@code runStep} with {@code startToken}. */
|
||||
public void start() {
|
||||
runStep(startToken);
|
||||
}
|
||||
|
@ -97,16 +83,16 @@ public class ProbingSequence extends CircularList<ProbingStep> {
|
|||
* Generates new {@link ProbingAction} from {@link ProbingStep}, calls the action, then retrieves
|
||||
* the result of the action.
|
||||
*
|
||||
* @param token - used to generate the {@link ProbingAction} by calling {@code
|
||||
* get().generateAction}.
|
||||
*
|
||||
* <p>Calls {@code runNextStep} to have next {@link ProbingSequence} call {@code runStep}
|
||||
* with next token depending on if the current step is the last one in the sequence.
|
||||
* <p>Calls {@code runNextStep} to have next {@link ProbingSequence} call {@code runStep} with
|
||||
* next token depending on if the current step is the last one in the sequence.
|
||||
*
|
||||
* <p>If unable to generate the action, or the calling the action results in an immediate error,
|
||||
* we note an error. Otherwise, if the future marked as finished when the action is completed is
|
||||
* marked as a success, we note a success. Otherwise, if the cause of failure will either be a
|
||||
* failure or error. </p>
|
||||
* failure or error.
|
||||
*
|
||||
* @param token - used to generate the {@link ProbingAction} by calling {@code
|
||||
* get().generateAction}.
|
||||
*/
|
||||
private void runStep(Token token) {
|
||||
ProbingAction currentAction;
|
||||
|
@ -121,8 +107,7 @@ public class ProbingSequence extends CircularList<ProbingStep> {
|
|||
|
||||
} catch (UnrecoverableStateException e) {
|
||||
// On an UnrecoverableStateException, terminate the sequence.
|
||||
logger.atSevere().withCause(e).log(
|
||||
"Unrecoverable error in generating or calling action.");
|
||||
logger.atSevere().withCause(e).log("Unrecoverable error in generating or calling action.");
|
||||
return;
|
||||
|
||||
} catch (Exception e) {
|
||||
|
@ -134,32 +119,32 @@ public class ProbingSequence extends CircularList<ProbingStep> {
|
|||
return;
|
||||
}
|
||||
|
||||
future.addListener(f -> {
|
||||
if (f.isSuccess()) {
|
||||
// On a successful result, we log as a successful step, and note a success.
|
||||
logger.atInfo().log(String.format("Successfully completed Probing Step: %s", this));
|
||||
future.addListener(
|
||||
f -> {
|
||||
if (f.isSuccess()) {
|
||||
// On a successful result, we log as a successful step, and note a success.
|
||||
logger.atInfo().log(String.format("Successfully completed Probing Step: %s", this));
|
||||
|
||||
} else {
|
||||
// On a failed result, we log the failure and note either a failure or error.
|
||||
logger.atSevere().withCause(f.cause()).log("Did not result in future success");
|
||||
} else {
|
||||
// On a failed result, we log the failure and note either a failure or error.
|
||||
logger.atSevere().withCause(f.cause()).log("Did not result in future success");
|
||||
|
||||
// If not unrecoverable, we restart the sequence.
|
||||
if (!(f.cause() instanceof UnrecoverableStateException)) {
|
||||
restartSequence();
|
||||
}
|
||||
// Otherwise, we just terminate the full sequence.
|
||||
return;
|
||||
}
|
||||
// If not unrecoverable, we restart the sequence.
|
||||
if (!(f.cause() instanceof UnrecoverableStateException)) {
|
||||
restartSequence();
|
||||
}
|
||||
// Otherwise, we just terminate the full sequence.
|
||||
return;
|
||||
}
|
||||
|
||||
if (get().protocol().persistentConnection()) {
|
||||
// If the connection is persistent, we store the channel in the token.
|
||||
token.setChannel(currentAction.channel());
|
||||
}
|
||||
if (get().protocol().persistentConnection()) {
|
||||
// If the connection is persistent, we store the channel in the token.
|
||||
token.setChannel(currentAction.channel());
|
||||
}
|
||||
|
||||
//Calls next runStep
|
||||
runNextStep(token);
|
||||
|
||||
});
|
||||
// Calls next runStep
|
||||
runNextStep(token);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -204,9 +189,7 @@ public class ProbingSequence extends CircularList<ProbingStep> {
|
|||
this.startToken = startToken;
|
||||
}
|
||||
|
||||
/**
|
||||
* We take special note of the first repeated step.
|
||||
*/
|
||||
/** We take special note of the first repeated step. */
|
||||
public Builder markFirstRepeated() {
|
||||
firstRepeatedSequenceStep = current;
|
||||
return this;
|
||||
|
|
|
@ -15,6 +15,8 @@
|
|||
package google.registry.monitoring.blackbox;
|
||||
|
||||
import com.google.auto.value.AutoValue;
|
||||
import google.registry.monitoring.blackbox.connection.ProbingAction;
|
||||
import google.registry.monitoring.blackbox.connection.Protocol;
|
||||
import google.registry.monitoring.blackbox.exceptions.UndeterminedStateException;
|
||||
import google.registry.monitoring.blackbox.messages.OutboundMessageType;
|
||||
import google.registry.monitoring.blackbox.tokens.Token;
|
||||
|
@ -25,10 +27,10 @@ import org.joda.time.Duration;
|
|||
* {@link AutoValue} class that represents generator of actions performed at each step in {@link
|
||||
* ProbingSequence}.
|
||||
*
|
||||
* <p>Holds the unchanged components in a given step of the {@link ProbingSequence}, which are
|
||||
* the {@link OutboundMessageType}, {@link Protocol}, {@link Duration}, and {@link Bootstrap}
|
||||
* instances. It then modifies these components on each loop iteration with the consumed {@link
|
||||
* Token} and from that, generates a new {@link ProbingAction} to call.</p>
|
||||
* <p>Holds the unchanged components in a given step of the {@link ProbingSequence}, which are the
|
||||
* {@link OutboundMessageType}, {@link Protocol}, {@link Duration}, and {@link Bootstrap} instances.
|
||||
* It then modifies these components on each loop iteration with the consumed {@link Token} and from
|
||||
* that, generates a new {@link ProbingAction} to call.
|
||||
*/
|
||||
@AutoValue
|
||||
public abstract class ProbingStep {
|
||||
|
@ -37,14 +39,10 @@ public abstract class ProbingStep {
|
|||
return new AutoValue_ProbingStep.Builder();
|
||||
}
|
||||
|
||||
/**
|
||||
* Time delay duration between actions.
|
||||
*/
|
||||
/** Time delay duration between actions. */
|
||||
abstract Duration duration();
|
||||
|
||||
/**
|
||||
* {@link Protocol} type for this step.
|
||||
*/
|
||||
/** {@link Protocol} type for this step. */
|
||||
abstract Protocol protocol();
|
||||
|
||||
/**
|
||||
|
@ -63,11 +61,12 @@ public abstract class ProbingStep {
|
|||
*/
|
||||
public ProbingAction generateAction(Token token) throws UndeterminedStateException {
|
||||
OutboundMessageType message = token.modifyMessage(messageTemplate());
|
||||
ProbingAction.Builder probingActionBuilder = ProbingAction.builder()
|
||||
.setDelay(duration())
|
||||
.setProtocol(protocol())
|
||||
.setOutboundMessage(message)
|
||||
.setHost(token.host());
|
||||
ProbingAction.Builder probingActionBuilder =
|
||||
ProbingAction.builder()
|
||||
.setDelay(duration())
|
||||
.setProtocol(protocol())
|
||||
.setOutboundMessage(message)
|
||||
.setHost(token.host());
|
||||
|
||||
if (token.channel() != null) {
|
||||
probingActionBuilder.setChannel(token.channel());
|
||||
|
@ -80,15 +79,12 @@ public abstract class ProbingStep {
|
|||
|
||||
@Override
|
||||
public final String toString() {
|
||||
return String.format("ProbingStep with Protocol: %s\n"
|
||||
+ "OutboundMessage: %s\n",
|
||||
protocol(),
|
||||
messageTemplate().getClass().getName());
|
||||
return String.format(
|
||||
"ProbingStep with Protocol: %s\n" + "OutboundMessage: %s\n",
|
||||
protocol(), messageTemplate().getClass().getName());
|
||||
}
|
||||
|
||||
/**
|
||||
* Standard {@link AutoValue.Builder} for {@link ProbingStep}.
|
||||
*/
|
||||
/** Standard {@link AutoValue.Builder} for {@link ProbingStep}. */
|
||||
@AutoValue.Builder
|
||||
public abstract static class Builder {
|
||||
|
||||
|
@ -103,4 +99,3 @@ public abstract class ProbingStep {
|
|||
public abstract ProbingStep build();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,293 @@
|
|||
// Copyright 2019 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.monitoring.blackbox.connection;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkArgument;
|
||||
import static com.google.common.base.Preconditions.checkNotNull;
|
||||
import static com.google.common.flogger.StackSize.SMALL;
|
||||
import static google.registry.monitoring.blackbox.connection.Protocol.PROTOCOL_KEY;
|
||||
|
||||
import com.google.auto.value.AutoValue;
|
||||
import com.google.common.flogger.FluentLogger;
|
||||
import google.registry.monitoring.blackbox.ProbingStep;
|
||||
import google.registry.monitoring.blackbox.exceptions.UndeterminedStateException;
|
||||
import google.registry.monitoring.blackbox.handlers.ActionHandler;
|
||||
import google.registry.monitoring.blackbox.messages.OutboundMessageType;
|
||||
import io.netty.bootstrap.Bootstrap;
|
||||
import io.netty.channel.Channel;
|
||||
import io.netty.channel.ChannelFuture;
|
||||
import io.netty.channel.ChannelHandler;
|
||||
import io.netty.channel.ChannelInitializer;
|
||||
import io.netty.channel.ChannelPromise;
|
||||
import io.netty.channel.local.LocalAddress;
|
||||
import io.netty.util.AttributeKey;
|
||||
import io.netty.util.HashedWheelTimer;
|
||||
import io.netty.util.Timer;
|
||||
import java.net.InetAddress;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.net.SocketAddress;
|
||||
import java.net.UnknownHostException;
|
||||
import java.util.concurrent.Callable;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import javax.inject.Provider;
|
||||
import org.joda.time.Duration;
|
||||
|
||||
/**
|
||||
* AutoValue class that represents action generated by {@link ProbingStep}
|
||||
*
|
||||
* <p>Inherits from {@link Callable<ChannelFuture>}, as it has can be called to perform its
|
||||
* specified task, and return the {@link ChannelFuture} that will be informed when the task has been
|
||||
* completed
|
||||
*
|
||||
* <p>Is an immutable class, as it is comprised of the tools necessary for making a specific type of
|
||||
* connection. It goes hand in hand with {@link Protocol}, which specifies the kind of overall
|
||||
* connection to be made. {@link Protocol} gives the outline and {@link ProbingAction} gives the
|
||||
* details of that connection.
|
||||
*
|
||||
* <p>In its build, if there is no channel supplied, it will create a channel from the attributes
|
||||
* already supplied. Then, it only sends the {@link OutboundMessageType} down the pipeline when
|
||||
* informed that the connection is successful. If the channel is supplied, the connection future is
|
||||
* automatically set to successful.
|
||||
*/
|
||||
@AutoValue
|
||||
public abstract class ProbingAction implements Callable<ChannelFuture> {
|
||||
|
||||
private static final FluentLogger logger = FluentLogger.forEnclosingClass();
|
||||
|
||||
/**
|
||||
* {@link AttributeKey} in channel that gives {@link ChannelFuture} that is set to success when
|
||||
* channel is active.
|
||||
*/
|
||||
public static final AttributeKey<ChannelFuture> CONNECTION_FUTURE_KEY =
|
||||
AttributeKey.valueOf("CONNECTION_FUTURE_KEY");
|
||||
|
||||
/** {@link AttributeKey} in channel that gives the information of the channel's host. */
|
||||
public static final AttributeKey<String> REMOTE_ADDRESS_KEY =
|
||||
AttributeKey.valueOf("REMOTE_ADDRESS_KEY");
|
||||
|
||||
/** {@link Timer} that rate limits probing */
|
||||
private static final Timer timer = new HashedWheelTimer();
|
||||
|
||||
public static Builder builder() {
|
||||
return new AutoValue_ProbingAction.Builder();
|
||||
}
|
||||
|
||||
/** Actual {@link Duration} of this delay */
|
||||
public abstract Duration delay();
|
||||
|
||||
/** {@link OutboundMessageType} instance that we write and flush down pipeline to server */
|
||||
public abstract OutboundMessageType outboundMessage();
|
||||
|
||||
/**
|
||||
* {@link Channel} object that is either created by or passed into this {@link ProbingAction}
|
||||
* instance
|
||||
*/
|
||||
public abstract Channel channel();
|
||||
|
||||
/** The {@link Protocol} instance that specifies type of connection */
|
||||
public abstract Protocol protocol();
|
||||
|
||||
/** The hostname of the remote host we have a connection or will make a connection to */
|
||||
public abstract String host();
|
||||
|
||||
/**
|
||||
* Performs the work of the actual action.
|
||||
*
|
||||
* <p>First, checks if channel is active by setting a listener to perform the bulk of the work
|
||||
* when the connection future is successful.
|
||||
*
|
||||
* <p>Once the connection is successful, we establish which of the handlers in the pipeline is the
|
||||
* {@link ActionHandler}.From that, we can obtain a future that is marked as a success when we
|
||||
* receive an expected response from the server.
|
||||
*
|
||||
* <p>Next, we set a timer set to a specified delay. After the delay has passed, we send the
|
||||
* {@code outboundMessage} down the channel pipeline, and when we observe a success or failure, we
|
||||
* inform the {@link ProbingStep} of this.
|
||||
*
|
||||
* @return {@link ChannelFuture} that denotes when the action has been successfully performed.
|
||||
*/
|
||||
@Override
|
||||
public ChannelFuture call() {
|
||||
// ChannelPromise that we return
|
||||
ChannelPromise finished = channel().newPromise();
|
||||
|
||||
// Ensures channel has been set up with connection future as an attribute
|
||||
checkNotNull(channel().attr(CONNECTION_FUTURE_KEY).get());
|
||||
|
||||
// When connection is established call super.call and set returned listener to success
|
||||
channel()
|
||||
.attr(CONNECTION_FUTURE_KEY)
|
||||
.get()
|
||||
.addListener(
|
||||
(ChannelFuture connectionFuture) -> {
|
||||
if (connectionFuture.isSuccess()) {
|
||||
logger.atInfo().log(
|
||||
String.format(
|
||||
"Successful connection to remote host: %s at port: %d",
|
||||
host(), protocol().port()));
|
||||
|
||||
ActionHandler actionHandler;
|
||||
try {
|
||||
actionHandler = channel().pipeline().get(ActionHandler.class);
|
||||
} catch (ClassCastException e) {
|
||||
// If we don't actually have an ActionHandler instance, we have an issue, and
|
||||
// throw an UndeterminedStateException.
|
||||
logger.atSevere().withStackTrace(SMALL).log(
|
||||
"ActionHandler not in Channel Pipeline");
|
||||
throw new UndeterminedStateException("No Action Handler found in pipeline");
|
||||
}
|
||||
ChannelFuture channelFuture = actionHandler.getFinishedFuture();
|
||||
|
||||
timer.newTimeout(
|
||||
timeout -> {
|
||||
// Write appropriate outboundMessage to pipeline
|
||||
ChannelFuture unusedFutureWriteAndFlush =
|
||||
channel().writeAndFlush(outboundMessage());
|
||||
channelFuture.addListeners(
|
||||
future -> {
|
||||
if (future.isSuccess()) {
|
||||
ChannelFuture unusedFuture = finished.setSuccess();
|
||||
} else {
|
||||
ChannelFuture unusedFuture = finished.setFailure(future.cause());
|
||||
}
|
||||
},
|
||||
// If we don't have a persistent connection, close the connection to this
|
||||
// channel
|
||||
future -> {
|
||||
if (!protocol().persistentConnection()) {
|
||||
|
||||
ChannelFuture closedFuture = channel().close();
|
||||
closedFuture.addListener(
|
||||
f -> {
|
||||
if (f.isSuccess()) {
|
||||
logger.atInfo().log(
|
||||
"Closed stale channel. Moving on to next ProbingStep");
|
||||
} else {
|
||||
logger.atWarning().log(
|
||||
"Issue closing stale channel. Most likely already "
|
||||
+ "closed.");
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
},
|
||||
delay().getStandardSeconds(),
|
||||
TimeUnit.SECONDS);
|
||||
} else {
|
||||
// if we receive a failure, log the failure, and close the channel
|
||||
logger.atSevere().withCause(connectionFuture.cause()).log(
|
||||
"Cannot connect to relay channel for %s channel: %s.",
|
||||
protocol().name(), this.channel());
|
||||
ChannelFuture unusedFuture = channel().close();
|
||||
}
|
||||
});
|
||||
return finished;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final String toString() {
|
||||
return String.format(
|
||||
"ProbingAction with delay: %d\n"
|
||||
+ "outboundMessage: %s\n"
|
||||
+ "protocol: %s\n"
|
||||
+ "host: %s\n",
|
||||
delay().getStandardSeconds(), outboundMessage(), protocol(), host());
|
||||
}
|
||||
|
||||
/** {@link AutoValue.Builder} that does work of creating connection when not already present. */
|
||||
@AutoValue.Builder
|
||||
public abstract static class Builder {
|
||||
|
||||
private Bootstrap bootstrap;
|
||||
|
||||
public Builder setBootstrap(Bootstrap bootstrap) {
|
||||
this.bootstrap = bootstrap;
|
||||
return this;
|
||||
}
|
||||
|
||||
public abstract Builder setDelay(Duration value);
|
||||
|
||||
public abstract Builder setOutboundMessage(OutboundMessageType value);
|
||||
|
||||
public abstract Builder setProtocol(Protocol value);
|
||||
|
||||
public abstract Builder setHost(String value);
|
||||
|
||||
public abstract Builder setChannel(Channel channel);
|
||||
|
||||
abstract Protocol protocol();
|
||||
|
||||
abstract Channel channel();
|
||||
|
||||
abstract String host();
|
||||
|
||||
abstract ProbingAction autoBuild();
|
||||
|
||||
public ProbingAction build() {
|
||||
// Sets SocketAddress to bind to.
|
||||
SocketAddress address;
|
||||
try {
|
||||
InetAddress hostAddress = InetAddress.getByName(host());
|
||||
address = new InetSocketAddress(hostAddress, protocol().port());
|
||||
} catch (UnknownHostException e) {
|
||||
// If the supplied host isn't a valid one, we assume we are using a test host, meaning we
|
||||
// are using a LocalAddress as our SocketAddress. If this isn't the case, we will anyways
|
||||
// throw an error from now being able to connect to the LocalAddress.
|
||||
address = new LocalAddress(host());
|
||||
}
|
||||
|
||||
// Sets channel supplied or to be created.
|
||||
Channel channel;
|
||||
try {
|
||||
channel = channel();
|
||||
} catch (IllegalStateException e) {
|
||||
channel = null;
|
||||
}
|
||||
|
||||
checkArgument(
|
||||
channel == null ^ bootstrap == null,
|
||||
"One and only one of bootstrap and channel must be supplied.");
|
||||
// If a channel is supplied, nothing is needed to be done
|
||||
|
||||
// Otherwise, a Bootstrap must be supplied and be used for creating the channel
|
||||
if (channel == null) {
|
||||
bootstrap
|
||||
.handler(
|
||||
new ChannelInitializer<Channel>() {
|
||||
@Override
|
||||
protected void initChannel(Channel outboundChannel) throws Exception {
|
||||
// Uses Handlers from Protocol to fill pipeline in order of provided handlers.
|
||||
for (Provider<? extends ChannelHandler> handlerProvider :
|
||||
protocol().handlerProviders()) {
|
||||
outboundChannel.pipeline().addLast(handlerProvider.get());
|
||||
}
|
||||
}
|
||||
})
|
||||
.attr(PROTOCOL_KEY, protocol())
|
||||
.attr(REMOTE_ADDRESS_KEY, host());
|
||||
|
||||
logger.atInfo().log("Initialized bootstrap with channel Handlers");
|
||||
// ChannelFuture that performs action when connection is established
|
||||
ChannelFuture connectionFuture = bootstrap.connect(address);
|
||||
|
||||
setChannel(connectionFuture.channel());
|
||||
connectionFuture.channel().attr(CONNECTION_FUTURE_KEY).set(connectionFuture);
|
||||
}
|
||||
|
||||
// now we can actually build the ProbingAction
|
||||
return autoBuild();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -12,7 +12,7 @@
|
|||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package google.registry.monitoring.blackbox;
|
||||
package google.registry.monitoring.blackbox.connection;
|
||||
|
||||
import com.google.auto.value.AutoValue;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
|
@ -20,15 +20,11 @@ import io.netty.channel.ChannelHandler;
|
|||
import io.netty.util.AttributeKey;
|
||||
import javax.inject.Provider;
|
||||
|
||||
/**
|
||||
* {@link AutoValue} class that stores all unchanged variables necessary for type of connection.
|
||||
*/
|
||||
/** {@link AutoValue} class that stores all unchanged variables necessary for type of connection. */
|
||||
@AutoValue
|
||||
public abstract class Protocol {
|
||||
|
||||
/**
|
||||
* {@link AttributeKey} that lets channel reference {@link Protocol} that created it.
|
||||
*/
|
||||
/** {@link AttributeKey} that lets channel reference {@link Protocol} that created it. */
|
||||
public static final AttributeKey<Protocol> PROTOCOL_KEY = AttributeKey.valueOf("PROTOCOL_KEY");
|
||||
|
||||
public static Builder builder() {
|
||||
|
@ -39,30 +35,20 @@ public abstract class Protocol {
|
|||
|
||||
public abstract int port();
|
||||
|
||||
/**
|
||||
* The {@link ChannelHandler} providers to use for the protocol, in order.
|
||||
*/
|
||||
abstract ImmutableList<Provider<? extends ChannelHandler>> handlerProviders();
|
||||
/** The {@link ChannelHandler} providers to use for the protocol, in order. */
|
||||
public abstract ImmutableList<Provider<? extends ChannelHandler>> handlerProviders();
|
||||
|
||||
/**
|
||||
* Boolean that notes if connection associated with Protocol is persistent.
|
||||
*/
|
||||
abstract boolean persistentConnection();
|
||||
/** Boolean that notes if connection associated with Protocol is persistent. */
|
||||
public abstract boolean persistentConnection();
|
||||
|
||||
@Override
|
||||
public final String toString() {
|
||||
return String.format(
|
||||
"Protocol with name: %s, port: %d, providers: %s, and persistent connection: %s",
|
||||
name(),
|
||||
port(),
|
||||
handlerProviders(),
|
||||
persistentConnection()
|
||||
);
|
||||
name(), port(), handlerProviders(), persistentConnection());
|
||||
}
|
||||
|
||||
/**
|
||||
* Default {@link AutoValue.Builder} for {@link Protocol}.
|
||||
*/
|
||||
/** Default {@link AutoValue.Builder} for {@link Protocol}. */
|
||||
@AutoValue.Builder
|
||||
public abstract static class Builder {
|
||||
|
|
@ -0,0 +1,30 @@
|
|||
// Copyright 2019 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.monitoring.blackbox.exceptions;
|
||||
|
||||
/**
|
||||
* Subclass of {@link UndeterminedStateException} that represents all instances when the action
|
||||
* performed failed due to the fault of the Prober when the action is an EPP action.
|
||||
*/
|
||||
public class EppClientException extends UndeterminedStateException {
|
||||
|
||||
public EppClientException(String msg) {
|
||||
super(msg);
|
||||
}
|
||||
|
||||
public EppClientException(Throwable e) {
|
||||
super(e);
|
||||
}
|
||||
}
|
|
@ -14,9 +14,7 @@
|
|||
|
||||
package google.registry.monitoring.blackbox.exceptions;
|
||||
|
||||
/**
|
||||
* Base exception class for all instances when the status of the action performed is FAILURE.
|
||||
*/
|
||||
/** Base exception class for all instances when the status of the action performed is FAILURE. */
|
||||
public class FailureException extends Exception {
|
||||
|
||||
public FailureException(String msg) {
|
||||
|
|
|
@ -15,7 +15,7 @@
|
|||
package google.registry.monitoring.blackbox.handlers;
|
||||
|
||||
import com.google.common.flogger.FluentLogger;
|
||||
import google.registry.monitoring.blackbox.ProbingAction;
|
||||
import google.registry.monitoring.blackbox.connection.ProbingAction;
|
||||
import google.registry.monitoring.blackbox.exceptions.FailureException;
|
||||
import google.registry.monitoring.blackbox.exceptions.UndeterminedStateException;
|
||||
import google.registry.monitoring.blackbox.messages.InboundMessageType;
|
||||
|
@ -27,50 +27,40 @@ import io.netty.channel.SimpleChannelInboundHandler;
|
|||
/**
|
||||
* Superclass of all {@link io.netty.channel.ChannelHandler}s placed at end of channel pipeline
|
||||
*
|
||||
* <p> {@link ActionHandler} inherits from {@link SimpleChannelInboundHandler<InboundMessageType>},
|
||||
* as it should only be passed in messages that implement the {@link InboundMessageType}
|
||||
* interface.</p>
|
||||
* <p>{@link ActionHandler} inherits from {@link SimpleChannelInboundHandler<InboundMessageType>},
|
||||
* as it should only be passed in messages that implement the {@link InboundMessageType} interface.
|
||||
*
|
||||
* <p> The {@link ActionHandler} skeleton exists for a few main purposes. First, it returns a
|
||||
* {@link ChannelPromise}, which informs the {@link ProbingAction} in charge that a response has
|
||||
* been read. Second, with any exception thrown, the connection is closed, and the ProbingAction
|
||||
* governing this channel is informed of the error. If the error is an instance of a {@link
|
||||
* FailureException} {@code finished} is marked as a failure with cause {@link FailureException}. If
|
||||
* it is any other type of error, it is treated as an {@link UndeterminedStateException} and {@code
|
||||
* finished} set as a failure with the same cause as what caused the exception. Lastly, if no error
|
||||
* is thrown, we know the action completed as a success, and, as such, we mark {@code finished} as a
|
||||
* success.</p>
|
||||
* <p>The {@link ActionHandler} skeleton exists for a few main purposes. First, it returns a {@link
|
||||
* ChannelPromise}, which informs the {@link ProbingAction} in charge that a response has been read.
|
||||
* Second, with any exception thrown, the connection is closed, and the ProbingAction governing this
|
||||
* channel is informed of the error. If the error is an instance of a {@link FailureException}
|
||||
* {@code finished} is marked as a failure with cause {@link FailureException}. If it is any other
|
||||
* type of error, it is treated as an {@link UndeterminedStateException} and {@code finished} set as
|
||||
* a failure with the same cause as what caused the exception. Lastly, if no error is thrown, we
|
||||
* know the action completed as a success, and, as such, we mark {@code finished} as a success.
|
||||
*
|
||||
* <p>Subclasses specify further work to be done for specific kinds of channel pipelines. </p>
|
||||
* <p>Subclasses specify further work to be done for specific kinds of channel pipelines.
|
||||
*/
|
||||
public abstract class ActionHandler extends SimpleChannelInboundHandler<InboundMessageType> {
|
||||
|
||||
private static final FluentLogger logger = FluentLogger.forEnclosingClass();
|
||||
|
||||
/**
|
||||
* {@link ChannelPromise} that informs {@link ProbingAction} if response has been received.
|
||||
*/
|
||||
private ChannelPromise finished;
|
||||
/** {@link ChannelPromise} that informs {@link ProbingAction} if response has been received. */
|
||||
protected ChannelPromise finished;
|
||||
|
||||
/**
|
||||
* Returns initialized {@link ChannelPromise} to {@link ProbingAction}.
|
||||
*/
|
||||
/** Returns initialized {@link ChannelPromise} to {@link ProbingAction}. */
|
||||
public ChannelFuture getFinishedFuture() {
|
||||
return finished;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes {@link ChannelPromise}
|
||||
*/
|
||||
/** Initializes {@link ChannelPromise} */
|
||||
@Override
|
||||
public void handlerAdded(ChannelHandlerContext ctx) {
|
||||
//Once handler is added to channel pipeline, initialize channel and future for this handler
|
||||
// Once handler is added to channel pipeline, initialize channel and future for this handler
|
||||
finished = ctx.newPromise();
|
||||
}
|
||||
|
||||
/**
|
||||
* Marks {@link ChannelPromise} as success
|
||||
*/
|
||||
/** Marks {@link ChannelPromise} as success */
|
||||
@Override
|
||||
public void channelRead0(ChannelHandlerContext ctx, InboundMessageType inboundMessage)
|
||||
throws FailureException, UndeterminedStateException {
|
||||
|
@ -84,28 +74,28 @@ public abstract class ActionHandler extends SimpleChannelInboundHandler<InboundM
|
|||
*/
|
||||
@Override
|
||||
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
|
||||
logger.atWarning().withCause(cause).log(String.format(
|
||||
"Attempted Action was unsuccessful with channel: %s, having pipeline: %s",
|
||||
ctx.channel().toString(),
|
||||
ctx.channel().pipeline().toString()));
|
||||
logger.atWarning().withCause(cause).log(
|
||||
String.format(
|
||||
"Attempted Action was unsuccessful with channel: %s, having pipeline: %s",
|
||||
ctx.channel().toString(), ctx.channel().pipeline().toString()));
|
||||
|
||||
if (cause instanceof FailureException) {
|
||||
//On FailureException, we know the response is a failure.
|
||||
// On FailureException, we know the response is a failure.
|
||||
|
||||
//Since it wasn't a success, we still want to log to see what caused the FAILURE
|
||||
// Since it wasn't a success, we still want to log to see what caused the FAILURE
|
||||
logger.atInfo().log(cause.getMessage());
|
||||
|
||||
//As always, inform the ProbingStep that we successfully completed this action
|
||||
// As always, inform the ProbingStep that we successfully completed this action
|
||||
ChannelFuture unusedFuture = finished.setFailure(cause);
|
||||
|
||||
} else {
|
||||
//On UndeterminedStateException, we know the response type is an error.
|
||||
// On UndeterminedStateException, we know the response type is an error.
|
||||
|
||||
//Since it wasn't a success, we still log what caused the ERROR
|
||||
// Since it wasn't a success, we still log what caused the ERROR
|
||||
logger.atWarning().log(cause.getMessage());
|
||||
ChannelFuture unusedFuture = finished.setFailure(cause);
|
||||
|
||||
//As this was an ERROR in performing the action, we must close the channel
|
||||
// As this was an ERROR in performing the action, we must close the channel
|
||||
ChannelFuture closedFuture = ctx.channel().close();
|
||||
closedFuture.addListener(f -> logger.atInfo().log("Unsuccessful channel connection closed"));
|
||||
}
|
||||
|
|
|
@ -0,0 +1,54 @@
|
|||
// Copyright 2019 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.monitoring.blackbox.handlers;
|
||||
|
||||
import google.registry.monitoring.blackbox.exceptions.FailureException;
|
||||
import google.registry.monitoring.blackbox.exceptions.UndeterminedStateException;
|
||||
import google.registry.monitoring.blackbox.messages.EppResponseMessage;
|
||||
import google.registry.monitoring.blackbox.messages.InboundMessageType;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import javax.inject.Inject;
|
||||
|
||||
/**
|
||||
* Subclass of {@link ActionHandler} that deals with the Epp Sequence
|
||||
*
|
||||
* <p>Main purpose is to verify {@link EppResponseMessage} received is valid. If not it throws the
|
||||
* requisite error which is dealt with by the parent {@link ActionHandler}
|
||||
*/
|
||||
public class EppActionHandler extends ActionHandler {
|
||||
|
||||
@Inject
|
||||
public EppActionHandler() {}
|
||||
|
||||
/**
|
||||
* Decodes the received response to ensure that it is what we expect and resets future in case
|
||||
* {@link EppActionHandler} is reused.
|
||||
*
|
||||
* @throws FailureException if we receive a failed response from the server
|
||||
*/
|
||||
@Override
|
||||
public void channelRead0(ChannelHandlerContext ctx, InboundMessageType msg)
|
||||
throws FailureException, UndeterminedStateException {
|
||||
EppResponseMessage response = (EppResponseMessage) msg;
|
||||
|
||||
// Based on the expected response type, will throw ResponseFailure if we don't receive a
|
||||
// successful EPP response.
|
||||
response.verify();
|
||||
super.channelRead0(ctx, msg);
|
||||
|
||||
// Reset future as there is potential to reuse same ActionHandler for a different ProbingAction
|
||||
finished = ctx.channel().newPromise();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,83 @@
|
|||
// Copyright 2019 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.monitoring.blackbox.handlers;
|
||||
|
||||
import com.google.common.flogger.FluentLogger;
|
||||
import google.registry.monitoring.blackbox.exceptions.FailureException;
|
||||
import google.registry.monitoring.blackbox.messages.EppRequestMessage;
|
||||
import google.registry.monitoring.blackbox.messages.EppResponseMessage;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.channel.ChannelDuplexHandler;
|
||||
import io.netty.channel.ChannelFuture;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import io.netty.channel.ChannelPromise;
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Named;
|
||||
|
||||
/**
|
||||
* {@link io.netty.channel.ChannelHandler} that converts inbound {@link ByteBuf} to custom type
|
||||
* {@link EppResponseMessage} and similarly converts the outbound {@link EppRequestMessage} to a
|
||||
* {@link ByteBuf}. Always comes right before {@link EppActionHandler} in channel pipeline.
|
||||
*/
|
||||
public class EppMessageHandler extends ChannelDuplexHandler {
|
||||
|
||||
private static final FluentLogger logger = FluentLogger.forEnclosingClass();
|
||||
|
||||
/**
|
||||
* Corresponding {@link EppResponseMessage} that we expect to receive back from server.
|
||||
*
|
||||
* <p>We always expect the first response to be an {@link EppResponseMessage.Greeting}.
|
||||
*/
|
||||
private EppResponseMessage response;
|
||||
|
||||
@Inject
|
||||
public EppMessageHandler(@Named("greeting") EppResponseMessage greetingResponse) {
|
||||
response = greetingResponse;
|
||||
}
|
||||
|
||||
/** Performs conversion to {@link ByteBuf}. */
|
||||
@Override
|
||||
public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise)
|
||||
throws Exception {
|
||||
|
||||
// If the message is Hello, don't actually pass it on, just wait for server greeting.
|
||||
// otherwise, we store what we expect a successful response to be
|
||||
EppRequestMessage request = (EppRequestMessage) msg;
|
||||
response = request.getExpectedResponse();
|
||||
|
||||
if (!response.name().equals("greeting")) {
|
||||
// then we write the ByteBuf representation of the EPP message to the server
|
||||
ChannelFuture unusedFuture = ctx.write(request.bytes(), promise);
|
||||
}
|
||||
}
|
||||
|
||||
/** Performs conversion from {@link ByteBuf} */
|
||||
@Override
|
||||
public void channelRead(ChannelHandlerContext ctx, Object msg) throws FailureException {
|
||||
try {
|
||||
// attempt to get response document from ByteBuf
|
||||
ByteBuf buf = (ByteBuf) msg;
|
||||
response.getDocument(buf);
|
||||
logger.atInfo().log(response.toString());
|
||||
} catch (FailureException e) {
|
||||
|
||||
// otherwise we log that it was unsuccessful and throw the requisite error
|
||||
logger.atInfo().withCause(e).log("Failure in current step.");
|
||||
throw e;
|
||||
}
|
||||
// pass response to the ActionHandler in the pipeline
|
||||
ctx.fireChannelRead(response);
|
||||
}
|
||||
}
|
|
@ -15,12 +15,12 @@
|
|||
package google.registry.monitoring.blackbox.handlers;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkNotNull;
|
||||
import static google.registry.monitoring.blackbox.ProbingAction.REMOTE_ADDRESS_KEY;
|
||||
import static google.registry.monitoring.blackbox.Protocol.PROTOCOL_KEY;
|
||||
import static google.registry.monitoring.blackbox.connection.ProbingAction.REMOTE_ADDRESS_KEY;
|
||||
import static google.registry.monitoring.blackbox.connection.Protocol.PROTOCOL_KEY;
|
||||
|
||||
import com.google.common.annotations.VisibleForTesting;
|
||||
import com.google.common.flogger.FluentLogger;
|
||||
import google.registry.monitoring.blackbox.Protocol;
|
||||
import google.registry.monitoring.blackbox.connection.Protocol;
|
||||
import io.netty.channel.Channel;
|
||||
import io.netty.channel.ChannelHandler.Sharable;
|
||||
import io.netty.channel.ChannelInitializer;
|
||||
|
@ -38,8 +38,8 @@ import javax.net.ssl.SSLParameters;
|
|||
/**
|
||||
* Adds a client side SSL handler to the channel pipeline.
|
||||
*
|
||||
* <p> Code is close to unchanged from {@link SslClientInitializer}</p> in proxy, but is modified
|
||||
* for revised overall structure of connections, and to accomdate EPP connections </p>
|
||||
* <p>Code is close to unchanged from {@link SslClientInitializer} in proxy, but is modified for
|
||||
* revised overall structure of connections, and to accomdate EPP connections
|
||||
*
|
||||
* <p>This <b>must</b> be the first handler provided for any handler provider list, if it is
|
||||
* provided. The type parameter {@code C} is needed so that unit tests can construct this handler
|
||||
|
@ -56,16 +56,17 @@ public class SslClientInitializer<C extends Channel> extends ChannelInitializer<
|
|||
private final Supplier<PrivateKey> privateKeySupplier;
|
||||
private final Supplier<X509Certificate[]> certificateSupplier;
|
||||
|
||||
|
||||
public SslClientInitializer(SslProvider sslProvider) {
|
||||
// null uses the system default trust store.
|
||||
//Used for WebWhois, so we don't care about privateKey and certificates, setting them to null
|
||||
// Used for WebWhois, so we don't care about privateKey and certificates, setting them to null
|
||||
this(sslProvider, null, null, null);
|
||||
}
|
||||
|
||||
public SslClientInitializer(SslProvider sslProvider, Supplier<PrivateKey> privateKeySupplier,
|
||||
public SslClientInitializer(
|
||||
SslProvider sslProvider,
|
||||
Supplier<PrivateKey> privateKeySupplier,
|
||||
Supplier<X509Certificate[]> certificateSupplier) {
|
||||
//We use the default trust store here as well, setting trustCertificates to null
|
||||
// We use the default trust store here as well, setting trustCertificates to null
|
||||
this(sslProvider, null, privateKeySupplier, certificateSupplier);
|
||||
}
|
||||
|
||||
|
@ -92,20 +93,17 @@ public class SslClientInitializer<C extends Channel> extends ChannelInitializer<
|
|||
Protocol protocol = channel.attr(PROTOCOL_KEY).get();
|
||||
String host = channel.attr(REMOTE_ADDRESS_KEY).get();
|
||||
|
||||
//Builds SslHandler from Protocol, and based on if we require a privateKey and certificate
|
||||
// Builds SslHandler from Protocol, and based on if we require a privateKey and certificate
|
||||
checkNotNull(protocol, "Protocol is not set for channel: %s", channel);
|
||||
SslContextBuilder sslContextBuilder =
|
||||
SslContextBuilder.forClient()
|
||||
.sslProvider(sslProvider)
|
||||
.trustManager(trustedCertificates);
|
||||
SslContextBuilder.forClient().sslProvider(sslProvider).trustManager(trustedCertificates);
|
||||
if (privateKeySupplier != null && certificateSupplier != null) {
|
||||
sslContextBuilder = sslContextBuilder
|
||||
.keyManager(privateKeySupplier.get(), certificateSupplier.get());
|
||||
sslContextBuilder =
|
||||
sslContextBuilder.keyManager(privateKeySupplier.get(), certificateSupplier.get());
|
||||
}
|
||||
|
||||
SslHandler sslHandler = sslContextBuilder
|
||||
.build()
|
||||
.newHandler(channel.alloc(), host, protocol.port());
|
||||
SslHandler sslHandler =
|
||||
sslContextBuilder.build().newHandler(channel.alloc(), host, protocol.port());
|
||||
|
||||
// Enable hostname verification.
|
||||
SSLEngine sslEngine = sslHandler.engine();
|
||||
|
@ -116,4 +114,3 @@ public class SslClientInitializer<C extends Channel> extends ChannelInitializer<
|
|||
channel.pipeline().addLast(sslHandler);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -15,17 +15,17 @@
|
|||
package google.registry.monitoring.blackbox.handlers;
|
||||
|
||||
import com.google.common.flogger.FluentLogger;
|
||||
import google.registry.monitoring.blackbox.ProbingAction;
|
||||
import google.registry.monitoring.blackbox.Protocol;
|
||||
import google.registry.monitoring.blackbox.WebWhoisModule.HttpWhoisProtocol;
|
||||
import google.registry.monitoring.blackbox.WebWhoisModule.HttpsWhoisProtocol;
|
||||
import google.registry.monitoring.blackbox.WebWhoisModule.WebWhoisProtocol;
|
||||
import google.registry.monitoring.blackbox.connection.ProbingAction;
|
||||
import google.registry.monitoring.blackbox.connection.Protocol;
|
||||
import google.registry.monitoring.blackbox.exceptions.ConnectionException;
|
||||
import google.registry.monitoring.blackbox.exceptions.FailureException;
|
||||
import google.registry.monitoring.blackbox.exceptions.UndeterminedStateException;
|
||||
import google.registry.monitoring.blackbox.messages.HttpRequestMessage;
|
||||
import google.registry.monitoring.blackbox.messages.HttpResponseMessage;
|
||||
import google.registry.monitoring.blackbox.messages.InboundMessageType;
|
||||
import google.registry.monitoring.blackbox.modules.WebWhoisModule.HttpWhoisProtocol;
|
||||
import google.registry.monitoring.blackbox.modules.WebWhoisModule.HttpsWhoisProtocol;
|
||||
import google.registry.monitoring.blackbox.modules.WebWhoisModule.WebWhoisProtocol;
|
||||
import io.netty.bootstrap.Bootstrap;
|
||||
import io.netty.channel.ChannelFuture;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
|
@ -38,9 +38,9 @@ import org.joda.time.Duration;
|
|||
/**
|
||||
* Subclass of {@link ActionHandler} that deals with the WebWhois Sequence
|
||||
*
|
||||
* <p> Main purpose is to verify {@link HttpResponseMessage} received is valid. If the response
|
||||
* <p>Main purpose is to verify {@link HttpResponseMessage} received is valid. If the response
|
||||
* implies a redirection it follows the redirection until either an Error Response is received, or
|
||||
* {@link HttpResponseStatus.OK} is received</p>
|
||||
* {@link HttpResponseStatus.OK} is received
|
||||
*/
|
||||
public class WebWhoisActionHandler extends ActionHandler {
|
||||
|
||||
|
@ -48,24 +48,16 @@ public class WebWhoisActionHandler extends ActionHandler {
|
|||
|
||||
/** Dagger injected components necessary for redirect responses: */
|
||||
|
||||
/**
|
||||
* {@link Bootstrap} necessary for remaking connection on redirect response.
|
||||
*/
|
||||
/** {@link Bootstrap} necessary for remaking connection on redirect response. */
|
||||
private final Bootstrap bootstrap;
|
||||
|
||||
/**
|
||||
* {@link Protocol} for when redirected to http endpoint.
|
||||
*/
|
||||
/** {@link Protocol} for when redirected to http endpoint. */
|
||||
private final Protocol httpWhoisProtocol;
|
||||
|
||||
/**
|
||||
* {@link Protocol} for when redirected to https endpoint.
|
||||
*/
|
||||
/** {@link Protocol} for when redirected to https endpoint. */
|
||||
private final Protocol httpsWhoisProtocol;
|
||||
|
||||
/**
|
||||
* {@link HttpRequestMessage} that represents default GET message to be sent on redirect.
|
||||
*/
|
||||
/** {@link HttpRequestMessage} that represents default GET message to be sent on redirect. */
|
||||
private final HttpRequestMessage requestMessage;
|
||||
|
||||
@Inject
|
||||
|
@ -81,7 +73,6 @@ public class WebWhoisActionHandler extends ActionHandler {
|
|||
this.requestMessage = requestMessage;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* After receiving {@link HttpResponseMessage}, either notes success and marks future as finished,
|
||||
* notes an error in the received {@link URL} and throws a {@link ConnectionException}, received a
|
||||
|
@ -98,32 +89,33 @@ public class WebWhoisActionHandler extends ActionHandler {
|
|||
logger.atInfo().log("Received Successful HttpResponseStatus");
|
||||
logger.atInfo().log("Response Received: " + response);
|
||||
|
||||
//On success, we always pass message to ActionHandler's channelRead0 method.
|
||||
// On success, we always pass message to ActionHandler's channelRead0 method.
|
||||
super.channelRead0(ctx, msg);
|
||||
|
||||
} else if (response.status().equals(HttpResponseStatus.MOVED_PERMANENTLY)
|
||||
|| response.status().equals(HttpResponseStatus.FOUND)) {
|
||||
//TODO - Fix checker to better determine when we have encountered a redirection response.
|
||||
// TODO - Fix checker to better determine when we have encountered a redirection response.
|
||||
|
||||
//Obtain url to be redirected to
|
||||
// Obtain url to be redirected to
|
||||
URL url;
|
||||
try {
|
||||
url = new URL(response.headers().get("Location"));
|
||||
} catch (MalformedURLException e) {
|
||||
//in case of error, log it, and let ActionHandler's exceptionThrown method deal with it
|
||||
// in case of error, log it, and let ActionHandler's exceptionThrown method deal with it
|
||||
throw new FailureException(
|
||||
"Redirected Location was invalid. Given Location was: " + response.headers()
|
||||
.get("Location"));
|
||||
"Redirected Location was invalid. Given Location was: "
|
||||
+ response.headers().get("Location"));
|
||||
}
|
||||
//From url, extract new host, port, and path
|
||||
// From url, extract new host, port, and path
|
||||
String newHost = url.getHost();
|
||||
String newPath = url.getPath();
|
||||
|
||||
logger.atInfo().log(String
|
||||
.format("Redirected to %s with host: %s, port: %d, and path: %s", url, newHost,
|
||||
url.getDefaultPort(), newPath));
|
||||
logger.atInfo().log(
|
||||
String.format(
|
||||
"Redirected to %s with host: %s, port: %d, and path: %s",
|
||||
url, newHost, url.getDefaultPort(), newPath));
|
||||
|
||||
//Construct new Protocol to reflect redirected host, path, and port
|
||||
// Construct new Protocol to reflect redirected host, path, and port
|
||||
Protocol newProtocol;
|
||||
if (url.getProtocol().equals(httpWhoisProtocol.name())) {
|
||||
newProtocol = httpWhoisProtocol;
|
||||
|
@ -134,19 +126,21 @@ public class WebWhoisActionHandler extends ActionHandler {
|
|||
"Redirection Location port was invalid. Given protocol name was: " + url.getProtocol());
|
||||
}
|
||||
|
||||
//Obtain HttpRequestMessage with modified headers to reflect new host and path.
|
||||
// Obtain HttpRequestMessage with modified headers to reflect new host and path.
|
||||
HttpRequestMessage httpRequest = requestMessage.modifyMessage(newHost, newPath);
|
||||
|
||||
//Create new probingAction that takes in the new Protocol and HttpRequestMessage with no delay
|
||||
ProbingAction redirectedAction = ProbingAction.builder()
|
||||
.setBootstrap(bootstrap)
|
||||
.setProtocol(newProtocol)
|
||||
.setOutboundMessage(httpRequest)
|
||||
.setDelay(Duration.ZERO)
|
||||
.setHost(newHost)
|
||||
.build();
|
||||
// Create new probingAction that takes in the new Protocol and HttpRequestMessage with no
|
||||
// delay
|
||||
ProbingAction redirectedAction =
|
||||
ProbingAction.builder()
|
||||
.setBootstrap(bootstrap)
|
||||
.setProtocol(newProtocol)
|
||||
.setOutboundMessage(httpRequest)
|
||||
.setDelay(Duration.ZERO)
|
||||
.setHost(newHost)
|
||||
.build();
|
||||
|
||||
//close this channel as we no longer need it
|
||||
// close this channel as we no longer need it
|
||||
ChannelFuture future = ctx.close();
|
||||
future.addListener(
|
||||
f -> {
|
||||
|
@ -156,34 +150,29 @@ public class WebWhoisActionHandler extends ActionHandler {
|
|||
logger.atWarning().log("Channel was unsuccessfully closed.");
|
||||
}
|
||||
|
||||
//Once channel is closed, establish new connection to redirected host, and repeat
|
||||
// Once channel is closed, establish new connection to redirected host, and repeat
|
||||
// same actions
|
||||
ChannelFuture secondFuture = redirectedAction.call();
|
||||
|
||||
//Once we have a successful call, set original ChannelPromise as success to tell
|
||||
// Once we have a successful call, set original ChannelPromise as success to tell
|
||||
// ProbingStep we can move on
|
||||
secondFuture.addListener(f2 -> {
|
||||
if (f2.isSuccess()) {
|
||||
super.channelRead0(ctx, msg);
|
||||
} else {
|
||||
if (f2 instanceof FailureException) {
|
||||
throw new FailureException(f2.cause());
|
||||
} else {
|
||||
throw new UndeterminedStateException(f2.cause());
|
||||
}
|
||||
}
|
||||
|
||||
});
|
||||
}
|
||||
);
|
||||
secondFuture.addListener(
|
||||
f2 -> {
|
||||
if (f2.isSuccess()) {
|
||||
super.channelRead0(ctx, msg);
|
||||
} else {
|
||||
if (f2 instanceof FailureException) {
|
||||
throw new FailureException(f2.cause());
|
||||
} else {
|
||||
throw new UndeterminedStateException(f2.cause());
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
} else {
|
||||
//Add in metrics Handling that informs MetricsCollector the response was a FAILURE
|
||||
// Add in metrics Handling that informs MetricsCollector the response was a FAILURE
|
||||
logger.atWarning().log(String.format("Received unexpected response: %s", response.status()));
|
||||
throw new FailureException("Response received from remote site was: " + response.status());
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
|
|
@ -31,12 +31,9 @@ import javax.inject.Inject;
|
|||
public class WebWhoisMessageHandler extends ChannelDuplexHandler {
|
||||
|
||||
@Inject
|
||||
public WebWhoisMessageHandler() {
|
||||
}
|
||||
public WebWhoisMessageHandler() {}
|
||||
|
||||
/**
|
||||
* Retains {@link HttpRequestMessage} and calls super write method.
|
||||
*/
|
||||
/** Retains {@link HttpRequestMessage} and calls super write method. */
|
||||
@Override
|
||||
public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise)
|
||||
throws Exception {
|
||||
|
@ -45,7 +42,6 @@ public class WebWhoisMessageHandler extends ChannelDuplexHandler {
|
|||
super.write(ctx, request, promise);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Converts {@link FullHttpResponse} to {@link HttpResponseMessage}, so it is an {@link
|
||||
* InboundMessageType} instance.
|
||||
|
|
|
@ -0,0 +1,454 @@
|
|||
// Copyright 2019 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.monitoring.blackbox.messages;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkArgument;
|
||||
import static com.google.common.base.Strings.isNullOrEmpty;
|
||||
import static google.registry.util.ResourceUtils.readResourceBytes;
|
||||
|
||||
import com.google.common.annotations.VisibleForTesting;
|
||||
import com.google.common.flogger.FluentLogger;
|
||||
import google.registry.monitoring.blackbox.exceptions.EppClientException;
|
||||
import google.registry.monitoring.blackbox.exceptions.FailureException;
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.StringWriter;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.nio.charset.Charset;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import javax.annotation.Nullable;
|
||||
import javax.xml.XMLConstants;
|
||||
import javax.xml.namespace.NamespaceContext;
|
||||
import javax.xml.parsers.DocumentBuilder;
|
||||
import javax.xml.parsers.DocumentBuilderFactory;
|
||||
import javax.xml.parsers.ParserConfigurationException;
|
||||
import javax.xml.transform.Transformer;
|
||||
import javax.xml.transform.TransformerException;
|
||||
import javax.xml.transform.TransformerFactory;
|
||||
import javax.xml.transform.dom.DOMSource;
|
||||
import javax.xml.transform.stream.StreamResult;
|
||||
import javax.xml.transform.stream.StreamSource;
|
||||
import javax.xml.validation.Schema;
|
||||
import javax.xml.validation.SchemaFactory;
|
||||
import javax.xml.validation.Validator;
|
||||
import javax.xml.xpath.XPath;
|
||||
import javax.xml.xpath.XPathConstants;
|
||||
import javax.xml.xpath.XPathExpressionException;
|
||||
import javax.xml.xpath.XPathFactory;
|
||||
import org.w3c.dom.Document;
|
||||
import org.w3c.dom.Node;
|
||||
import org.w3c.dom.NodeList;
|
||||
import org.xml.sax.SAXException;
|
||||
|
||||
/**
|
||||
* Superclass of {@link EppRequestMessage} and {@link EppResponseMessage} that represents skeleton
|
||||
* of any kind of EPP message, whether inbound or outbound.
|
||||
*
|
||||
* <p>NOTE: Most static methods are copied over from
|
||||
* //java/com/google/domain/registry/monitoring/blackbox/EppHelper.java
|
||||
*
|
||||
* <p>Houses number of static methods for use of conversion between String and bytes to {@link
|
||||
* Document} type, which represents an XML Document, the type of which is used for EPP messages.
|
||||
*/
|
||||
public class EppMessage {
|
||||
|
||||
/** Key that allows for substitution of {@code domainName} to xml template. */
|
||||
public static final String DOMAIN_KEY = "//domainns:name";
|
||||
|
||||
/** Key that allows for substitution of epp user id to xml template. */
|
||||
public static final String CLIENT_ID_KEY = "//eppns:clID";
|
||||
|
||||
/** Key that allows for substitution of epp password to xml template. */
|
||||
public static final String CLIENT_PASSWORD_KEY = "//eppns:pw";
|
||||
|
||||
/** Key that allows for substitution of{@code clTrid} to xml template. */
|
||||
public static final String CLIENT_TRID_KEY = "//eppns:clTRID";
|
||||
|
||||
/** Key that allows for substitution of{@code svTrid} to xml template. */
|
||||
public static final String SERVER_TRID_KEY = "//eppns:svTRID";
|
||||
|
||||
/**
|
||||
* Expression that expresses a result code in the {@link EppResponseMessage} that means success.
|
||||
*/
|
||||
@VisibleForTesting
|
||||
public static final String XPASS_EXPRESSION =
|
||||
String.format("//eppns:result[@code>='%s'][@code<'%s']", 1000, 2000);
|
||||
/**
|
||||
* Expression that expresses a result code in the {@link EppResponseMessage} that means failure.
|
||||
*/
|
||||
@VisibleForTesting
|
||||
public static final String XFAIL_EXPRESSION =
|
||||
String.format("//eppns:result[@code>='%s'][@code<'%s']", 2000, 3000);
|
||||
// "Security" errors from RFC 5730, plus the error we get when we end
|
||||
// up no longer logged (see b/28196510).
|
||||
// 2002 "Command use error"
|
||||
// 2200 "Authentication error"
|
||||
// 2201 "Authorization error"
|
||||
// 2202 "Invalid authorization information"
|
||||
@VisibleForTesting
|
||||
static final String AUTHENTICATION_ERROR =
|
||||
"//eppns:epp/eppns:response/eppns:result[@code!='2002' and @code!='2200' "
|
||||
+ "and @code!='2201' and @code!='2202']";
|
||||
|
||||
static final String VALID_SLD_LABEL_REGEX;
|
||||
private static final FluentLogger logger = FluentLogger.forEnclosingClass();
|
||||
/**
|
||||
* Static variables necessary for static methods that serve as tools for {@link Document}
|
||||
* creation, conversion, and verification.
|
||||
*/
|
||||
private static final DocumentBuilderFactory docBuilderFactory =
|
||||
DocumentBuilderFactory.newInstance();
|
||||
|
||||
private static final XPath xpath;
|
||||
private static final Schema eppSchema;
|
||||
// As per RFC 1035 section 2.3.4 http://tools.ietf.org/html/rfc1035#page-10 and updated by
|
||||
// http://tools.ietf.org/html/rfc1123#page-13 which suggests a domain part length
|
||||
// of 255 SHOULD be supported, so we are permitting something close to that, but reserve
|
||||
// at least two characters for a TLD and a full stop (".") character.
|
||||
private static final int MAX_DOMAIN_PART_LENGTH = 252;
|
||||
private static final int MAX_SLD_DOMAIN_LABEL_LENGTH = 255;
|
||||
private static final String VALID_DOMAIN_PART_REGEX;
|
||||
private static final String VALID_TLD_PART_REGEX;
|
||||
/** Standard EPP header number of bytes (size of int). */
|
||||
protected static int HEADER_LENGTH = 4;
|
||||
|
||||
static {
|
||||
// tld label part may contain a dot and end with a dot, and must
|
||||
// start and end with 0-9 or a-zA-Z but may contain any number of
|
||||
// dashes (minus signs "-") in between (even consecutive)
|
||||
VALID_TLD_PART_REGEX =
|
||||
String.format(
|
||||
"\\p{Alnum}[-\\p{Alnum}]{0,%s}\\.{0,1}[-\\p{Alnum}]{0,%s}\\p{Alnum}",
|
||||
(MAX_DOMAIN_PART_LENGTH - 2), (MAX_DOMAIN_PART_LENGTH - 2));
|
||||
// domain label part ("left" of the tld) must start and end with 0-9 or a-zA-Z but
|
||||
// may contain any number of dashes (minus signs "-") in between (even consecutive)
|
||||
VALID_DOMAIN_PART_REGEX =
|
||||
String.format("\\p{Alnum}[-\\p{Alnum}]{0,%s}\\p{Alnum}", (MAX_DOMAIN_PART_LENGTH - 2));
|
||||
VALID_SLD_LABEL_REGEX =
|
||||
String.format(
|
||||
"(?=.{4,%s})%s\\.%s(\\.)*",
|
||||
MAX_SLD_DOMAIN_LABEL_LENGTH, VALID_DOMAIN_PART_REGEX, VALID_TLD_PART_REGEX);
|
||||
|
||||
xpath = XPathFactory.newInstance().newXPath();
|
||||
xpath.setNamespaceContext(new EppNamespaceContext());
|
||||
docBuilderFactory.setNamespaceAware(true);
|
||||
|
||||
String path = "./xsd/";
|
||||
StreamSource[] sources;
|
||||
try {
|
||||
sources =
|
||||
new StreamSource[] {
|
||||
new StreamSource(readResource(path + "eppcom.xsd")),
|
||||
new StreamSource(readResource(path + "epp.xsd")),
|
||||
new StreamSource(readResource(path + "host.xsd")),
|
||||
new StreamSource(readResource(path + "contact.xsd")),
|
||||
new StreamSource(readResource(path + "domain.xsd")),
|
||||
new StreamSource(readResource(path + "rgp.xsd")),
|
||||
new StreamSource(readResource(path + "mark.xsd")),
|
||||
new StreamSource(readResource(path + "dsig.xsd")),
|
||||
new StreamSource(readResource(path + "smd.xsd")),
|
||||
new StreamSource(readResource(path + "launch.xsd")),
|
||||
};
|
||||
SchemaFactory schemaFactory = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
|
||||
eppSchema = schemaFactory.newSchema(sources);
|
||||
} catch (SAXException | IOException e) {
|
||||
throw new ExceptionInInitializerError(e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@link Document} that represents the actual XML document sent inbound or outbound through
|
||||
* channel pipeline.
|
||||
*/
|
||||
protected Document message;
|
||||
|
||||
/** Helper method that reads resource existing in same package as {@link EppMessage} class. */
|
||||
private static InputStream readResource(String filename) throws IOException {
|
||||
return readResourceBytes(EppMessage.class, filename).openStream();
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate an EPP XML document against the set of XSD files that make up the EPP XML Schema.
|
||||
*
|
||||
* <p>Note that the document must have the namespace attributes set where expected (i.e. must be
|
||||
* built using a DocumentBuilder with namespace awareness set if using a DocumentBuilder).
|
||||
*
|
||||
* @param xml an XML Document to validate
|
||||
* @throws SAXException if the document is not valid
|
||||
*/
|
||||
public static void eppValidate(Document xml) throws SAXException, IOException {
|
||||
Validator schemaValidator = eppSchema.newValidator();
|
||||
schemaValidator.validate(new DOMSource(xml));
|
||||
}
|
||||
|
||||
/**
|
||||
* Verify an XML Document as an EPP Response using the provided XPath expressions.
|
||||
*
|
||||
* <p>This will first validate the document against the EPP schema, then run through the list of
|
||||
* xpath expressions -- so those need only look for specific EPP elements + values.
|
||||
*
|
||||
* @param xml the XML Document containing the EPP reponse to verify
|
||||
* @param expressions a list of XPath expressions to query the document with.
|
||||
* @param validate a boolean flag to control if schema validation occurs (useful for testing)
|
||||
* @throws IOException if InputStream throws one
|
||||
* @throws EppClientException if the EPP response cannot be read, parsed, or doesn't containing
|
||||
* matching data specified in expressions
|
||||
*/
|
||||
protected static void verifyEppResponse(Document xml, List<String> expressions, boolean validate)
|
||||
throws FailureException {
|
||||
if (validate) {
|
||||
try {
|
||||
eppValidate(xml);
|
||||
} catch (SAXException | IOException e) {
|
||||
throw new FailureException(e);
|
||||
}
|
||||
}
|
||||
try {
|
||||
for (String exp : expressions) {
|
||||
NodeList nodes = (NodeList) xpath.evaluate(exp, xml, XPathConstants.NODESET);
|
||||
if (nodes.getLength() == 0) {
|
||||
throw new FailureException("invalid EPP response. failed expression " + exp);
|
||||
}
|
||||
}
|
||||
} catch (XPathExpressionException e) {
|
||||
throw new FailureException(e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A helper method to extract a value from an element in an XML document.
|
||||
*
|
||||
* @return the text value for the element, or null is the element is not found
|
||||
*/
|
||||
public static String getElementValue(Document xml, String expression) {
|
||||
try {
|
||||
return (String) xpath.evaluate(expression, xml, XPathConstants.STRING);
|
||||
} catch (XPathExpressionException e) {
|
||||
logger.atSevere().withCause(e).log("Bad expression: %s", expression);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A helper method to transform an XML Document to a string. - e.g. a returned string might look
|
||||
* like the following for a Document with a root element of "foo" that has a child element of
|
||||
* "bar" which has text of "baz":<br>
|
||||
* {@code '<foo><bar>baz</bar></foo>'}
|
||||
*
|
||||
* @param xml the Document to transform
|
||||
* @return the resulting string or {@code null} if {@code xml} is {@code null}.
|
||||
* @throws EppClientException if the transform fails
|
||||
*/
|
||||
@Nullable
|
||||
public static String xmlDocToString(@Nullable Document xml) throws EppClientException {
|
||||
if (xml == null) {
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
Transformer transformer = TransformerFactory.newInstance().newTransformer();
|
||||
StreamResult result = new StreamResult(new StringWriter());
|
||||
DOMSource source = new DOMSource(xml);
|
||||
transformer.transform(source, result);
|
||||
return result.getWriter().toString();
|
||||
} catch (TransformerException e) {
|
||||
throw new EppClientException(e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A helper method to transform an XML Document to a byte array using the XML Encoding when
|
||||
* converting from a String (see xmlDocToString).
|
||||
*
|
||||
* @param xml the Document to transform
|
||||
* @return the resulting byte array.
|
||||
* @throws EppClientException if the transform fails
|
||||
*/
|
||||
public static byte[] xmlDocToByteArray(Document xml) throws EppClientException {
|
||||
try {
|
||||
Transformer transformer = TransformerFactory.newInstance().newTransformer();
|
||||
StreamResult result = new StreamResult(new StringWriter());
|
||||
DOMSource source = new DOMSource(xml);
|
||||
transformer.transform(source, result);
|
||||
String resultString = result.getWriter().toString();
|
||||
if (isNullOrEmpty(resultString)) {
|
||||
throw new EppClientException("unknown error converting Document to intermediate string");
|
||||
}
|
||||
String encoding = xml.getXmlEncoding();
|
||||
// this is actually not a problem since we can just use the default
|
||||
if (encoding == null) {
|
||||
encoding = Charset.defaultCharset().name();
|
||||
}
|
||||
return resultString.getBytes(encoding);
|
||||
} catch (TransformerException | UnsupportedEncodingException e) {
|
||||
throw new EppClientException(e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A helper method to transform an byte array to an XML {@link Document} using {@code
|
||||
* docBuilderFactory}
|
||||
*
|
||||
* @param responseBuffer the byte array to transform
|
||||
* @return the resulting Document
|
||||
* @throws EppClientException if the transform fails
|
||||
*/
|
||||
public static Document byteArrayToXmlDoc(byte[] responseBuffer) throws FailureException {
|
||||
Document xml;
|
||||
try {
|
||||
DocumentBuilder builder = docBuilderFactory.newDocumentBuilder();
|
||||
ByteArrayInputStream byteStream = new ByteArrayInputStream(responseBuffer);
|
||||
xml = builder.parse(byteStream);
|
||||
} catch (ParserConfigurationException | SAXException | IOException e) {
|
||||
throw new FailureException(e);
|
||||
}
|
||||
return xml;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads one of a set of EPP templates (included as resources in our jar) and finds nodes using an
|
||||
* xpath expression, then replaces the node value of the first child, returning the transformed
|
||||
* XML as a Document.
|
||||
*
|
||||
* <p>E.g. to replace the value "@@CLTRID@@" in the {@code <clTRID>} node with a client
|
||||
* transaction ID, use the mapping {@code <"//domainns:clTRID", "AAA-123-BBBB">} (or whatever the
|
||||
* ID is).
|
||||
*
|
||||
* @param template the relative (unqualified) name of the template file to use
|
||||
* @param replacements a map of strings to replace in the template keyed by the xpath expression
|
||||
* to use to find the nodes to operate on with the value being the text to use as the
|
||||
* replacement
|
||||
* @return the transformed EPP document
|
||||
* @throws IOException if the template cannot be read
|
||||
* @throws EppClientException if there are issues parsing the template or evaluating the xpath
|
||||
* expression, or if the resulting document is not valid EPP
|
||||
* @throws IllegalArgumentException if the xpath expression query yields anything other than an
|
||||
* Element node type
|
||||
*/
|
||||
public static Document getEppDocFromTemplate(String template, Map<String, String> replacements)
|
||||
throws IOException, EppClientException {
|
||||
Document xmlDoc;
|
||||
|
||||
try (InputStream is = readResource(template)) {
|
||||
DocumentBuilder builder = docBuilderFactory.newDocumentBuilder();
|
||||
xmlDoc = builder.parse(is);
|
||||
for (String key : replacements.keySet()) {
|
||||
NodeList nodes = (NodeList) xpath.evaluate(key, xmlDoc, XPathConstants.NODESET);
|
||||
for (int i = 0; i < nodes.getLength(); i++) {
|
||||
Node node = nodes.item(i);
|
||||
if (node.getNodeType() != Node.ELEMENT_NODE) {
|
||||
throw new IllegalArgumentException(
|
||||
String.format(
|
||||
"xpath expression (%s) must result in Element nodes, got %s",
|
||||
key, node.getNodeType()));
|
||||
}
|
||||
node.getFirstChild().setNodeValue(replacements.get(key));
|
||||
}
|
||||
}
|
||||
|
||||
eppValidate(xmlDoc);
|
||||
} catch (SAXException | XPathExpressionException | ParserConfigurationException e) {
|
||||
throw new EppClientException(e);
|
||||
}
|
||||
return xmlDoc;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public String getElementValue(String expression) {
|
||||
try {
|
||||
return (String) xpath.evaluate(expression, message, XPathConstants.STRING);
|
||||
} catch (XPathExpressionException e) {
|
||||
logger.atSevere().withCause(e).log("Bad expression: %s", expression);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
try {
|
||||
return xmlDocToString(message);
|
||||
} catch (EppClientException e) {
|
||||
return "No Message Found";
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements the {@link NamespaceContext} interface and adds an EPP namespace URI (prefix eppns).
|
||||
*/
|
||||
static class EppNamespaceContext implements NamespaceContext {
|
||||
|
||||
final Map<String, String> nsPrefixMap = new HashMap<>();
|
||||
final Map<String, Set<String>> nsUriMap = new HashMap<>();
|
||||
|
||||
public EppNamespaceContext() {
|
||||
try {
|
||||
addNamespace(XMLConstants.XML_NS_PREFIX, XMLConstants.XML_NS_URI);
|
||||
addNamespace(XMLConstants.XMLNS_ATTRIBUTE, XMLConstants.XMLNS_ATTRIBUTE_NS_URI);
|
||||
addNamespace("eppns", "urn:ietf:params:xml:ns:epp-1.0");
|
||||
addNamespace("contactns", "urn:ietf:params:xml:ns:contact-1.0");
|
||||
addNamespace("domainns", "urn:ietf:params:xml:ns:domain-1.0");
|
||||
addNamespace("hostns", "urn:ietf:params:xml:ns:host-1.0");
|
||||
addNamespace("launchns", "urn:ietf:params:xml:ns:launch-1.0");
|
||||
} catch (Exception e) {
|
||||
// this should never happen here but if it does, we just turn it into a runtime exception
|
||||
throw new IllegalArgumentException(e);
|
||||
}
|
||||
}
|
||||
|
||||
void addNamespace(String prefix, String namespaceURI) throws Exception {
|
||||
checkArgument(!isNullOrEmpty(prefix), "prefix");
|
||||
checkArgument(!isNullOrEmpty(namespaceURI), "namespaceURI");
|
||||
if (nsPrefixMap.containsKey(prefix)) {
|
||||
throw new RuntimeException("key already exists: " + prefix);
|
||||
}
|
||||
nsPrefixMap.put(prefix, namespaceURI);
|
||||
if (nsUriMap.containsKey(namespaceURI)) {
|
||||
nsUriMap.get(namespaceURI).add(prefix);
|
||||
} else {
|
||||
Set<String> prefixSet = new HashSet<>();
|
||||
prefixSet.add(prefix);
|
||||
nsUriMap.put(namespaceURI, prefixSet);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getNamespaceURI(String prefix) {
|
||||
checkArgument(!isNullOrEmpty(prefix), "prefix");
|
||||
return nsPrefixMap.getOrDefault(prefix, XMLConstants.NULL_NS_URI);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getPrefix(String namespaceURI) {
|
||||
checkArgument(!isNullOrEmpty(namespaceURI), "namespaceURI");
|
||||
return getPrefixes(namespaceURI).next();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterator<String> getPrefixes(String namespaceURI) {
|
||||
checkArgument(!isNullOrEmpty(namespaceURI), "namespaceURI");
|
||||
if (nsUriMap.containsKey(namespaceURI)) {
|
||||
return nsUriMap.get(namespaceURI).iterator();
|
||||
} else {
|
||||
return Collections.emptyIterator();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,132 @@
|
|||
// Copyright 2019 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.monitoring.blackbox.messages;
|
||||
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import google.registry.monitoring.blackbox.exceptions.EppClientException;
|
||||
import google.registry.monitoring.blackbox.exceptions.UndeterminedStateException;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.buffer.Unpooled;
|
||||
import java.io.IOException;
|
||||
import java.util.Map;
|
||||
import java.util.function.BiFunction;
|
||||
|
||||
/**
|
||||
* {@link EppMessage} subclass that implements {@link OutboundMessageType}, which represents an
|
||||
* outbound Epp message.
|
||||
*
|
||||
* <p>In modifying the {@code getReplacements} field of {@link EppRequestMessage} and the template,
|
||||
* we can represent the the 5 basic EPP commands we are attempting to probe. The original 5 are:
|
||||
* LOGIN, CREATE, CHECK, DELETE, LOGOUT.
|
||||
*
|
||||
* <p>In turn, we equivalently create 10 different EPP commands probed: Hello - checks for Greeting
|
||||
* response, Login expecting success, Login expecting Failure, Create expecting Success, Create
|
||||
* expecting Failure, Check that the domain exists, Check that the domain doesn't exist, Delete
|
||||
* expecting Success, Delete expecting Failure, and Logout expecting Success.
|
||||
*
|
||||
* <p>The main difference is that we added a hello command that simply waits for the server to send
|
||||
* a greeting, then moves on to the Login action.
|
||||
*
|
||||
* <p>Stores a clTRID and domainName which is modified each time the token calls {@code
|
||||
* modifyMessage}. These will also modify the EPP request sent to the server.
|
||||
*/
|
||||
public class EppRequestMessage extends EppMessage implements OutboundMessageType {
|
||||
|
||||
/** Corresponding {@link EppResponseMessage} that we expect to receive on a successful request. */
|
||||
private EppResponseMessage expectedResponse;
|
||||
|
||||
/** Filename for template of current request type. */
|
||||
private String template;
|
||||
|
||||
/**
|
||||
* {@link ImmutableMap} of replacements that is indicative of each type of {@link
|
||||
* EppRequestMessage}
|
||||
*/
|
||||
private BiFunction<String, String, Map<String, String>> getReplacements;
|
||||
|
||||
/**
|
||||
* Private constructor for {@link EppRequestMessage} that its subclasses use for instantiation.
|
||||
*/
|
||||
public EppRequestMessage(
|
||||
EppResponseMessage expectedResponse,
|
||||
String template,
|
||||
BiFunction<String, String, Map<String, String>> getReplacements) {
|
||||
|
||||
this.expectedResponse = expectedResponse;
|
||||
this.template = template;
|
||||
this.getReplacements = getReplacements;
|
||||
}
|
||||
|
||||
/**
|
||||
* From the input {@code clTrid} and {@code domainName}, modifies the template EPP XML document
|
||||
* and the {@code expectedResponse} to reflect new parameters.
|
||||
*
|
||||
* @param args - should always be two Strings: The first one is {@code clTrid} and the second one
|
||||
* is {@code domainName}.
|
||||
* @return the current {@link EppRequestMessage} instance.
|
||||
* @throws EppClientException - On the occasion that the prober can't appropriately modify the EPP
|
||||
* XML document, the blame falls on the prober, not the server, so it throws an {@link
|
||||
* EppClientException}, which is a subclass of the {@link UndeterminedStateException}.
|
||||
*/
|
||||
@Override
|
||||
public EppRequestMessage modifyMessage(String... args) throws EppClientException {
|
||||
// First argument should always be clTRID.
|
||||
String clTrid = args[0];
|
||||
|
||||
// Second argument should always be domainName.
|
||||
String domainName = args[1];
|
||||
|
||||
if (template != null) {
|
||||
// Checks if we are sending an actual EPP request to the server (if template is null, than
|
||||
// we just expect a response.
|
||||
try {
|
||||
message = getEppDocFromTemplate(template, getReplacements.apply(clTrid, domainName));
|
||||
} catch (IOException e) {
|
||||
throw new EppClientException(e);
|
||||
}
|
||||
}
|
||||
// Update the EppResponseMessage associated with this EppRequestMessage to reflect changed
|
||||
// parameters on this step.
|
||||
expectedResponse.updateInformation(clTrid, domainName);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts the current {@link org.w3c.dom.Document} message to a {@link ByteBuf} with the
|
||||
* requisite bytes
|
||||
*
|
||||
* @return the {@link ByteBuf} instance that stores the bytes representing the requisite EPP
|
||||
* Request
|
||||
* @throws EppClientException- On the occasion that the prober can't appropriately convert the EPP
|
||||
* XML document to a {@link ByteBuf}, the blame falls on the prober, not the server, so it
|
||||
* throws an {@link EppClientException}, which is a subclass of the {@link
|
||||
* UndeterminedStateException}.
|
||||
*/
|
||||
public ByteBuf bytes() throws EppClientException {
|
||||
// obtain byte array of our modified xml document
|
||||
byte[] bytestream = xmlDocToByteArray(message);
|
||||
|
||||
// Move bytes to a ByteBuf.
|
||||
ByteBuf buf = Unpooled.buffer(bytestream.length);
|
||||
buf.writeBytes(bytestream);
|
||||
|
||||
return buf;
|
||||
}
|
||||
|
||||
/** */
|
||||
public EppResponseMessage getExpectedResponse() {
|
||||
return expectedResponse;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,87 @@
|
|||
// Copyright 2019 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.monitoring.blackbox.messages;
|
||||
|
||||
import google.registry.monitoring.blackbox.exceptions.FailureException;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import java.util.List;
|
||||
import java.util.function.BiFunction;
|
||||
|
||||
/**
|
||||
* {@link EppMessage} subclass that implements {@link InboundMessageType}, which represents an
|
||||
* inbound EPP message and serves to verify the response received from the server.
|
||||
*
|
||||
* <p>There are 4 created types of this {@link EppRequestMessage}, which represent the expected
|
||||
* successful response types. Their names are: success, failure greeting, domainExists,
|
||||
* domainNotExists. Success implies a response declaring the command was completed successfully.
|
||||
* Failure implies a response declaring the command was not completed successfully. Greeting is the
|
||||
* standard initial response the server sends after a connection is established. DomainExists is a
|
||||
* response to a Check request saying the domain exists on the server. DomainNotExists is a response
|
||||
* that essentially says the opposite.
|
||||
*
|
||||
* <p>Stores an expected clTRID and domainName which are the ones used by the {@link
|
||||
* EppRequestMessage} pointing to this {@link EppRequestMessage}.
|
||||
*
|
||||
* <p>From the {@link ByteBuf} input, stores the corresponding {@link org.w3c.dom.Document}
|
||||
* represented and to be validated.
|
||||
*/
|
||||
public class EppResponseMessage extends EppMessage implements InboundMessageType {
|
||||
|
||||
/**
|
||||
* Specifies type of {@link EppResponseMessage}.
|
||||
*
|
||||
* <p>All possible names are: success, failure, domainExists, domainNotExists, greeting.
|
||||
*/
|
||||
private final String name;
|
||||
|
||||
/** Lambda expression that returns a checkList from input clTRID and domain Strings. */
|
||||
private final BiFunction<String, String, List<String>> getCheckList;
|
||||
|
||||
/** Domain name we expect to receive in current response. */
|
||||
private String expectedDomainName;
|
||||
|
||||
/** ClTRID we expect to receive in current response. */
|
||||
private String expectedClTrid;
|
||||
|
||||
/** Verifies that the response recorded is what we expect from the request sent. */
|
||||
public void verify() throws FailureException {
|
||||
verifyEppResponse(message, getCheckList.apply(expectedClTrid, expectedDomainName), true);
|
||||
}
|
||||
|
||||
/** Extracts {@link org.w3c.dom.Document} from the {@link ByteBuf} input. */
|
||||
public void getDocument(ByteBuf buf) throws FailureException {
|
||||
// Convert ByteBuf to byte array.
|
||||
byte[] response = new byte[buf.readableBytes()];
|
||||
buf.readBytes(response);
|
||||
|
||||
// Convert byte array to Document.
|
||||
message = byteArrayToXmlDoc(response);
|
||||
}
|
||||
|
||||
/** Updates {@code expectedClTrid} and {@code expectedDomainName} fields. */
|
||||
void updateInformation(String expectedClTrid, String expectedDomainName) {
|
||||
this.expectedClTrid = expectedClTrid;
|
||||
this.expectedDomainName = expectedDomainName;
|
||||
}
|
||||
|
||||
public EppResponseMessage(String name, BiFunction<String, String, List<String>> getCheckList) {
|
||||
this.name = name;
|
||||
this.getCheckList = getCheckList;
|
||||
}
|
||||
|
||||
public String name() {
|
||||
return name;
|
||||
}
|
||||
}
|
|
@ -25,9 +25,9 @@ import javax.inject.Inject;
|
|||
/**
|
||||
* {@link OutboundMessageType} subtype that acts identically to {@link DefaultFullHttpRequest}.
|
||||
*
|
||||
* <p>As it is an {@link OutboundMessageType} subtype, there is a {@code modifyMessage} method
|
||||
* that modifies the request to reflect the new host and optional path. We also implement a {@code
|
||||
* name} method, which returns a standard name and the current hostname.</p>
|
||||
* <p>As it is an {@link OutboundMessageType} subtype, there is a {@code modifyMessage} method that
|
||||
* modifies the request to reflect the new host and optional path. We also implement a {@code name}
|
||||
* method, which returns a standard name and the current hostname.
|
||||
*/
|
||||
public class HttpRequestMessage extends DefaultFullHttpRequest implements OutboundMessageType {
|
||||
|
||||
|
@ -40,15 +40,12 @@ public class HttpRequestMessage extends DefaultFullHttpRequest implements Outbou
|
|||
super(httpVersion, method, uri);
|
||||
}
|
||||
|
||||
private HttpRequestMessage(HttpVersion httpVersion, HttpMethod method, String uri,
|
||||
ByteBuf content) {
|
||||
private HttpRequestMessage(
|
||||
HttpVersion httpVersion, HttpMethod method, String uri, ByteBuf content) {
|
||||
super(httpVersion, method, uri, content);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Used for conversion from {@link FullHttpRequest} to {@link HttpRequestMessage}
|
||||
*/
|
||||
/** Used for conversion from {@link FullHttpRequest} to {@link HttpRequestMessage} */
|
||||
public HttpRequestMessage(FullHttpRequest request) {
|
||||
this(request.protocolVersion(), request.method(), request.uri(), request.content());
|
||||
request.headers().forEach((entry) -> headers().set(entry.getKey(), entry.getValue()));
|
||||
|
@ -60,9 +57,7 @@ public class HttpRequestMessage extends DefaultFullHttpRequest implements Outbou
|
|||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Modifies headers to reflect new host and new path if applicable.
|
||||
*/
|
||||
/** Modifies headers to reflect new host and new path if applicable. */
|
||||
@Override
|
||||
public HttpRequestMessage modifyMessage(String... args) throws IllegalArgumentException {
|
||||
if (args.length == 1 || args.length == 2) {
|
||||
|
@ -78,7 +73,8 @@ public class HttpRequestMessage extends DefaultFullHttpRequest implements Outbou
|
|||
String.format(
|
||||
"Wrong number of arguments present for modifying HttpRequestMessage."
|
||||
+ " Received %d arguments instead of 2. Received arguments: "
|
||||
+ Arrays.toString(args), args.length));
|
||||
+ Arrays.toString(args),
|
||||
args.length));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -86,5 +82,4 @@ public class HttpRequestMessage extends DefaultFullHttpRequest implements Outbou
|
|||
public String toString() {
|
||||
return String.format("Http(s) Request on: %s", headers().get("host"));
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -20,18 +20,14 @@ import io.netty.handler.codec.http.FullHttpResponse;
|
|||
import io.netty.handler.codec.http.HttpResponseStatus;
|
||||
import io.netty.handler.codec.http.HttpVersion;
|
||||
|
||||
/**
|
||||
* {@link InboundMessageType} subtype that acts identically to {@link DefaultFullHttpResponse}
|
||||
*/
|
||||
/** {@link InboundMessageType} subtype that acts identically to {@link DefaultFullHttpResponse} */
|
||||
public class HttpResponseMessage extends DefaultFullHttpResponse implements InboundMessageType {
|
||||
|
||||
private HttpResponseMessage(HttpVersion version, HttpResponseStatus status, ByteBuf content) {
|
||||
super(version, status, content);
|
||||
}
|
||||
|
||||
/**
|
||||
* Used for pipeline conversion from {@link FullHttpResponse} to {@link HttpResponseMessage}
|
||||
*/
|
||||
/** Used for pipeline conversion from {@link FullHttpResponse} to {@link HttpResponseMessage} */
|
||||
public HttpResponseMessage(FullHttpResponse response) {
|
||||
this(response.protocolVersion(), response.status(), response.content());
|
||||
|
||||
|
|
|
@ -18,6 +18,4 @@ package google.registry.monitoring.blackbox.messages;
|
|||
* Marker Interface that is implemented by all classes that serve as {@code inboundMessages} in
|
||||
* channel pipeline
|
||||
*/
|
||||
public interface InboundMessageType {
|
||||
|
||||
}
|
||||
public interface InboundMessageType {}
|
||||
|
|
|
@ -0,0 +1,125 @@
|
|||
// Copyright 2019 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.monitoring.blackbox.modules;
|
||||
|
||||
import static com.google.common.base.Suppliers.memoizeWithExpiration;
|
||||
import static google.registry.util.ResourceUtils.readResourceBytes;
|
||||
import static google.registry.util.ResourceUtils.readResourceUtf8;
|
||||
|
||||
import dagger.Module;
|
||||
import dagger.Provides;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.security.GeneralSecurityException;
|
||||
import java.security.KeyStore;
|
||||
import java.security.PrivateKey;
|
||||
import java.security.cert.X509Certificate;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.function.Supplier;
|
||||
import javax.inject.Provider;
|
||||
import javax.inject.Qualifier;
|
||||
import javax.inject.Singleton;
|
||||
import org.joda.time.Duration;
|
||||
|
||||
/**
|
||||
* Dagger module that provides bindings needed to inject server certificate chain and private key.
|
||||
*
|
||||
* <p>Currently the sandbox certificates and private key are stored as local secrets in a .p12 file,
|
||||
* however, in production, all certificates will be stored in a .pem file that is encrypted by Cloud
|
||||
* KMS. The .pem file can be generated by concatenating the .crt certificate files on the chain and
|
||||
* the .key private file.
|
||||
*
|
||||
* <p>The production certificates in the .pem file must be stored in order, where the next
|
||||
* certificate's subject is the previous certificate's issuer.
|
||||
*
|
||||
* @see <a href="https://cloud.google.com/kms/">Cloud Key Management Service</a>
|
||||
*/
|
||||
@Module
|
||||
public class CertificateModule {
|
||||
|
||||
/** {@link Qualifier} to identify components provided from Local Secrets. */
|
||||
// TODO - remove this qualifier and replace it with using KMS to retrieve private key and
|
||||
// certificate
|
||||
@Qualifier
|
||||
public @interface LocalSecrets {}
|
||||
|
||||
private static InputStream readResource(String filename) throws IOException {
|
||||
return readResourceBytes(CertificateModule.class, filename).openStream();
|
||||
}
|
||||
|
||||
@Provides
|
||||
@LocalSecrets
|
||||
static Duration provideCacheDuration() {
|
||||
return Duration.standardSeconds(2);
|
||||
}
|
||||
|
||||
@Singleton
|
||||
@Provides
|
||||
@LocalSecrets
|
||||
static String keystorePasswordProvider() {
|
||||
return readResourceUtf8(CertificateModule.class, "secrets/keystore_password.txt");
|
||||
}
|
||||
|
||||
@Provides
|
||||
@LocalSecrets
|
||||
static PrivateKey providePrivateKey(@LocalSecrets Provider<String> passwordProvider) {
|
||||
try {
|
||||
InputStream inStream = readResource("secrets/prober-client-tls-sandbox.p12");
|
||||
|
||||
KeyStore ks = KeyStore.getInstance("PKCS12");
|
||||
ks.load(inStream, passwordProvider.get().toCharArray());
|
||||
|
||||
String alias = ks.aliases().nextElement();
|
||||
return (PrivateKey) ks.getKey(alias, passwordProvider.get().toCharArray());
|
||||
} catch (IOException | GeneralSecurityException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Provides
|
||||
@LocalSecrets
|
||||
static X509Certificate[] provideCertificates(@LocalSecrets Provider<String> passwordProvider) {
|
||||
try {
|
||||
InputStream inStream = readResource("secrets/prober-client-tls-sandbox.p12");
|
||||
|
||||
KeyStore ks = KeyStore.getInstance("PKCS12");
|
||||
ks.load(inStream, passwordProvider.get().toCharArray());
|
||||
|
||||
String alias = ks.aliases().nextElement();
|
||||
return new X509Certificate[] {(X509Certificate) ks.getCertificate(alias)};
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Singleton
|
||||
@Provides
|
||||
@LocalSecrets
|
||||
static Supplier<PrivateKey> providePrivatekeySupplier(
|
||||
@LocalSecrets Provider<PrivateKey> privateKeyProvider, @LocalSecrets Duration duration) {
|
||||
return memoizeWithExpiration(
|
||||
privateKeyProvider::get, duration.getStandardSeconds(), TimeUnit.SECONDS);
|
||||
}
|
||||
|
||||
@Singleton
|
||||
@Provides
|
||||
@LocalSecrets
|
||||
static Supplier<X509Certificate[]> provideCertificatesSupplier(
|
||||
@LocalSecrets Provider<X509Certificate[]> certificatesProvider,
|
||||
@LocalSecrets Duration duration) {
|
||||
return memoizeWithExpiration(
|
||||
certificatesProvider::get, duration.getStandardSeconds(), TimeUnit.SECONDS);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,582 @@
|
|||
// Copyright 2019 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.monitoring.blackbox.modules;
|
||||
|
||||
import static google.registry.monitoring.blackbox.messages.EppRequestMessage.CLIENT_ID_KEY;
|
||||
import static google.registry.monitoring.blackbox.messages.EppRequestMessage.CLIENT_PASSWORD_KEY;
|
||||
import static google.registry.monitoring.blackbox.messages.EppRequestMessage.CLIENT_TRID_KEY;
|
||||
import static google.registry.monitoring.blackbox.messages.EppRequestMessage.DOMAIN_KEY;
|
||||
import static google.registry.util.ResourceUtils.readResourceUtf8;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import dagger.Module;
|
||||
import dagger.Provides;
|
||||
import dagger.multibindings.IntoSet;
|
||||
import google.registry.monitoring.blackbox.ProbingSequence;
|
||||
import google.registry.monitoring.blackbox.ProbingStep;
|
||||
import google.registry.monitoring.blackbox.connection.Protocol;
|
||||
import google.registry.monitoring.blackbox.handlers.EppActionHandler;
|
||||
import google.registry.monitoring.blackbox.handlers.EppMessageHandler;
|
||||
import google.registry.monitoring.blackbox.handlers.SslClientInitializer;
|
||||
import google.registry.monitoring.blackbox.messages.EppMessage;
|
||||
import google.registry.monitoring.blackbox.messages.EppRequestMessage;
|
||||
import google.registry.monitoring.blackbox.messages.EppResponseMessage;
|
||||
import google.registry.monitoring.blackbox.modules.CertificateModule.LocalSecrets;
|
||||
import google.registry.monitoring.blackbox.tokens.EppToken;
|
||||
import io.netty.bootstrap.Bootstrap;
|
||||
import io.netty.channel.ChannelHandler;
|
||||
import io.netty.channel.socket.nio.NioSocketChannel;
|
||||
import io.netty.handler.codec.LengthFieldBasedFrameDecoder;
|
||||
import io.netty.handler.codec.LengthFieldPrepender;
|
||||
import io.netty.handler.ssl.SslProvider;
|
||||
import java.security.PrivateKey;
|
||||
import java.security.cert.X509Certificate;
|
||||
import java.util.function.Supplier;
|
||||
import javax.inject.Named;
|
||||
import javax.inject.Provider;
|
||||
import javax.inject.Qualifier;
|
||||
import javax.inject.Singleton;
|
||||
import org.joda.time.Duration;
|
||||
|
||||
/**
|
||||
* A module that provides the components necessary for and the overall {@link ProbingSequence} to
|
||||
* probe EPP.
|
||||
*/
|
||||
@Module
|
||||
public class EppModule {
|
||||
|
||||
private static final int EPP_PORT = 700;
|
||||
private static final String EPP_PROTOCOL_NAME = "epp";
|
||||
|
||||
/** Maximum length of EPP messages received. Same as max length for HTTP messages, is 0.5 MB. */
|
||||
// TODO - replace with config specified maximum length.
|
||||
private static final int maximumMessageLengthBytes = 512 * 1024;
|
||||
|
||||
/** Standard EPP header length. */
|
||||
// TODO - replace with config specified header length (still 4).
|
||||
private static final int eppHeaderLengthBytes = 4;
|
||||
|
||||
/** Dagger provided {@link ProbingSequence} that probes EPP login and logout actions. */
|
||||
@Provides
|
||||
@Singleton
|
||||
@EppProtocol
|
||||
static Bootstrap provideEppBootstrap(Provider<Bootstrap> bootstrapProvider) {
|
||||
return bootstrapProvider.get();
|
||||
}
|
||||
|
||||
/**
|
||||
* Dagger provided {@link ProbingSequence} that probes EPP login, create, check, and delete
|
||||
* actions with a persistent connection.
|
||||
*/
|
||||
@Provides
|
||||
@Singleton
|
||||
@IntoSet
|
||||
static ProbingSequence provideEppLoginCreateCheckDeleteCheckProbingSequence(
|
||||
EppToken.Persistent token,
|
||||
@Named("hello") ProbingStep helloStep,
|
||||
@Named("loginSuccess") ProbingStep loginSuccessStep,
|
||||
@Named("createSuccess") ProbingStep createSuccessStep,
|
||||
@Named("checkExists") ProbingStep checkStepFirst,
|
||||
@Named("deleteSuccess") ProbingStep deleteSuccessStep,
|
||||
@Named("checkNotExists") ProbingStep checkStepSecond) {
|
||||
return new ProbingSequence.Builder(token)
|
||||
.add(helloStep)
|
||||
.add(loginSuccessStep)
|
||||
.add(createSuccessStep)
|
||||
.markFirstRepeated()
|
||||
.add(checkStepFirst)
|
||||
.add(deleteSuccessStep)
|
||||
.add(checkStepSecond)
|
||||
.build();
|
||||
}
|
||||
|
||||
/**
|
||||
* Dagger provided {@link ProbingSequence} that probes EPP login, create, check, delete, and
|
||||
* logout actions with a transient connection.
|
||||
*/
|
||||
@Provides
|
||||
@Singleton
|
||||
@IntoSet
|
||||
static ProbingSequence provideEppLoginCreateCheckDeleteCheckLogoutProbingSequence(
|
||||
EppToken.Transient token,
|
||||
@Named("hello") ProbingStep helloStep,
|
||||
@Named("loginSuccess") ProbingStep loginSuccessStep,
|
||||
@Named("createSuccess") ProbingStep createSuccessStep,
|
||||
@Named("checkExists") ProbingStep checkStepFirst,
|
||||
@Named("deleteSuccess") ProbingStep deleteSuccessStep,
|
||||
@Named("checkNotExists") ProbingStep checkStepSecond,
|
||||
@Named("logout") ProbingStep logoutStep) {
|
||||
return new ProbingSequence.Builder(token)
|
||||
.add(helloStep)
|
||||
.add(loginSuccessStep)
|
||||
.add(createSuccessStep)
|
||||
.add(checkStepFirst)
|
||||
.add(deleteSuccessStep)
|
||||
.add(checkStepSecond)
|
||||
.add(logoutStep)
|
||||
.build();
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides {@link ProbingStep} that establishes initial connection to EPP server.
|
||||
*
|
||||
* <p>Always necessary as first step for any EPP {@link ProbingSequence} and first repeated step
|
||||
* for any {@link ProbingSequence} that doesn't stay logged in (transient).
|
||||
*/
|
||||
@Provides
|
||||
@Named("hello")
|
||||
static ProbingStep provideEppHelloStep(
|
||||
@EppProtocol Protocol eppProtocol,
|
||||
Duration duration,
|
||||
@Named("hello") EppRequestMessage helloRequest,
|
||||
@EppProtocol Bootstrap bootstrap) {
|
||||
return ProbingStep.builder()
|
||||
.setProtocol(eppProtocol)
|
||||
.setDuration(duration)
|
||||
.setMessageTemplate(helloRequest)
|
||||
.setBootstrap(bootstrap)
|
||||
.build();
|
||||
}
|
||||
|
||||
/** {@link Provides} {@link ProbingStep} that logs into the EPP server. */
|
||||
@Provides
|
||||
@Named("loginSuccess")
|
||||
static ProbingStep provideEppLoginSuccessStep(
|
||||
@EppProtocol Protocol eppProtocol,
|
||||
Duration duration,
|
||||
@Named("loginSuccess") EppRequestMessage loginSuccessRequest,
|
||||
@EppProtocol Bootstrap bootstrap) {
|
||||
return ProbingStep.builder()
|
||||
.setProtocol(eppProtocol)
|
||||
.setDuration(duration)
|
||||
.setMessageTemplate(loginSuccessRequest)
|
||||
.setBootstrap(bootstrap)
|
||||
.build();
|
||||
}
|
||||
|
||||
/** {@link Provides} {@link ProbingStep} that creates a new domain on EPP server. */
|
||||
@Provides
|
||||
@Named("createSuccess")
|
||||
static ProbingStep provideEppCreateSuccessStep(
|
||||
@EppProtocol Protocol eppProtocol,
|
||||
Duration duration,
|
||||
@Named("createSuccess") EppRequestMessage createSuccessRequest,
|
||||
@EppProtocol Bootstrap bootstrap) {
|
||||
return ProbingStep.builder()
|
||||
.setProtocol(eppProtocol)
|
||||
.setDuration(duration)
|
||||
.setMessageTemplate(createSuccessRequest)
|
||||
.setBootstrap(bootstrap)
|
||||
.build();
|
||||
}
|
||||
|
||||
/** {@link Provides} {@link ProbingStep} that built, checks a domain exists on EPP server. */
|
||||
@Provides
|
||||
@Named("checkExists")
|
||||
static ProbingStep provideEppCheckExistsStep(
|
||||
@EppProtocol Protocol eppProtocol,
|
||||
Duration duration,
|
||||
@Named("checkExists") EppRequestMessage checkExistsRequest,
|
||||
@EppProtocol Bootstrap bootstrap) {
|
||||
return ProbingStep.builder()
|
||||
.setProtocol(eppProtocol)
|
||||
.setDuration(duration)
|
||||
.setMessageTemplate(checkExistsRequest)
|
||||
.setBootstrap(bootstrap)
|
||||
.build();
|
||||
}
|
||||
|
||||
/** {@link Provides} {@link ProbingStep} that checks a domain doesn't exist on EPP server. */
|
||||
@Provides
|
||||
@Named("checkNotExists")
|
||||
static ProbingStep provideEppCheckNotExistsStep(
|
||||
@EppProtocol Protocol eppProtocol,
|
||||
Duration duration,
|
||||
@Named("checkNotExists") EppRequestMessage checkNotExistsRequest,
|
||||
@EppProtocol Bootstrap bootstrap) {
|
||||
return ProbingStep.builder()
|
||||
.setProtocol(eppProtocol)
|
||||
.setDuration(duration)
|
||||
.setMessageTemplate(checkNotExistsRequest)
|
||||
.setBootstrap(bootstrap)
|
||||
.build();
|
||||
}
|
||||
|
||||
/** {@link Provides} {@link ProbingStep} that deletes a domain from EPP server. */
|
||||
@Provides
|
||||
@Named("deleteSuccess")
|
||||
static ProbingStep provideEppDeleteSuccessStep(
|
||||
@EppProtocol Protocol eppProtocol,
|
||||
Duration duration,
|
||||
@Named("deleteSuccess") EppRequestMessage deleteSuccessRequest,
|
||||
@EppProtocol Bootstrap bootstrap) {
|
||||
return ProbingStep.builder()
|
||||
.setProtocol(eppProtocol)
|
||||
.setDuration(duration)
|
||||
.setMessageTemplate(deleteSuccessRequest)
|
||||
.setBootstrap(bootstrap)
|
||||
.build();
|
||||
}
|
||||
|
||||
/** {@link Provides} {@link ProbingStep} that logs out of EPP server. */
|
||||
@Provides
|
||||
@Named("logout")
|
||||
static ProbingStep provideEppLogoutStep(
|
||||
@EppProtocol Protocol eppProtocol,
|
||||
Duration duration,
|
||||
@Named("logout") EppRequestMessage logoutRequest,
|
||||
@EppProtocol Bootstrap bootstrap) {
|
||||
return ProbingStep.builder()
|
||||
.setProtocol(eppProtocol)
|
||||
.setDuration(duration)
|
||||
.setMessageTemplate(logoutRequest)
|
||||
.setBootstrap(bootstrap)
|
||||
.build();
|
||||
}
|
||||
|
||||
/** {@link Provides} hello {@link EppRequestMessage} with only expected response of greeting. */
|
||||
@Provides
|
||||
@Named("hello")
|
||||
static EppRequestMessage provideHelloRequestMessage(
|
||||
@Named("greeting") EppResponseMessage greetingResponse) {
|
||||
|
||||
return new EppRequestMessage(greetingResponse, null, (a, b) -> ImmutableMap.of());
|
||||
}
|
||||
|
||||
/**
|
||||
* Set of all possible {@link EppRequestMessage}s paired with their expected {@link
|
||||
* EppResponseMessage}s.
|
||||
*/
|
||||
|
||||
/** {@link Provides} login {@link EppRequestMessage} with expected response of success. */
|
||||
@Provides
|
||||
@Named("loginSuccess")
|
||||
static EppRequestMessage provideLoginSuccessRequestMessage(
|
||||
@Named("success") EppResponseMessage successResponse,
|
||||
@Named("login") String loginTemplate,
|
||||
@Named("eppUserId") String userId,
|
||||
@Named("eppPassword") String userPassword) {
|
||||
return new EppRequestMessage(
|
||||
successResponse,
|
||||
loginTemplate,
|
||||
(clTrid, domain) ->
|
||||
ImmutableMap.of(
|
||||
CLIENT_TRID_KEY, clTrid,
|
||||
CLIENT_ID_KEY, userId,
|
||||
CLIENT_PASSWORD_KEY, userPassword));
|
||||
}
|
||||
|
||||
/** {@link Provides} login {@link EppRequestMessage} with expected response of failure. */
|
||||
@Provides
|
||||
@Named("loginFailure")
|
||||
static EppRequestMessage provideLoginFailureRequestMessage(
|
||||
@Named("failure") EppResponseMessage failureResponse,
|
||||
@Named("login") String loginTemplate,
|
||||
@Named("eppUserId") String userId,
|
||||
@Named("eppPassword") String userPassword) {
|
||||
return new EppRequestMessage(
|
||||
failureResponse,
|
||||
loginTemplate,
|
||||
(clTrid, domain) ->
|
||||
ImmutableMap.of(
|
||||
CLIENT_TRID_KEY, clTrid,
|
||||
CLIENT_ID_KEY, userId,
|
||||
CLIENT_PASSWORD_KEY, userPassword));
|
||||
}
|
||||
|
||||
/** {@link Provides} create {@link EppRequestMessage} with expected response of success. */
|
||||
@Provides
|
||||
@Named("createSuccess")
|
||||
static EppRequestMessage provideCreateSuccessRequestMessage(
|
||||
@Named("success") EppResponseMessage successResponse,
|
||||
@Named("create") String createTemplate) {
|
||||
return new EppRequestMessage(
|
||||
successResponse,
|
||||
createTemplate,
|
||||
(clTrid, domain) ->
|
||||
ImmutableMap.of(
|
||||
CLIENT_TRID_KEY, clTrid,
|
||||
DOMAIN_KEY, domain));
|
||||
}
|
||||
|
||||
/** {@link Provides} create {@link EppRequestMessage} with expected response of failure. */
|
||||
@Provides
|
||||
@Named("createFailure")
|
||||
static EppRequestMessage provideCreateFailureRequestMessage(
|
||||
@Named("failure") EppResponseMessage failureResponse,
|
||||
@Named("create") String createTemplate) {
|
||||
return new EppRequestMessage(
|
||||
failureResponse,
|
||||
createTemplate,
|
||||
(clTrid, domain) ->
|
||||
ImmutableMap.of(
|
||||
CLIENT_TRID_KEY, clTrid,
|
||||
DOMAIN_KEY, domain));
|
||||
}
|
||||
|
||||
/** {@link Provides} delete {@link EppRequestMessage} with expected response of success. */
|
||||
@Provides
|
||||
@Named("deleteSuccess")
|
||||
static EppRequestMessage provideDeleteSuccessRequestMessage(
|
||||
@Named("success") EppResponseMessage successResponse,
|
||||
@Named("delete") String deleteTemplate) {
|
||||
return new EppRequestMessage(
|
||||
successResponse,
|
||||
deleteTemplate,
|
||||
(clTrid, domain) ->
|
||||
ImmutableMap.of(
|
||||
CLIENT_TRID_KEY, clTrid,
|
||||
DOMAIN_KEY, domain));
|
||||
}
|
||||
|
||||
/** {@link Provides} delete {@link EppRequestMessage} with expected response of failure. */
|
||||
@Provides
|
||||
@Named("deleteFailure")
|
||||
static EppRequestMessage provideDeleteFailureRequestMessage(
|
||||
@Named("failure") EppResponseMessage failureResponse,
|
||||
@Named("delete") String deleteTemplate) {
|
||||
return new EppRequestMessage(
|
||||
failureResponse,
|
||||
deleteTemplate,
|
||||
(clTrid, domain) ->
|
||||
ImmutableMap.of(
|
||||
CLIENT_TRID_KEY, clTrid,
|
||||
DOMAIN_KEY, domain));
|
||||
}
|
||||
|
||||
/** {@link Provides} logout {@link EppRequestMessage} with expected response of success. */
|
||||
@Provides
|
||||
@Named("logout")
|
||||
static EppRequestMessage provideLogoutSuccessRequestMessage(
|
||||
@Named("success") EppResponseMessage successResponse,
|
||||
@Named("logout") String logoutTemplate) {
|
||||
return new EppRequestMessage(
|
||||
successResponse,
|
||||
logoutTemplate,
|
||||
(clTrid, domain) -> ImmutableMap.of(CLIENT_TRID_KEY, clTrid));
|
||||
}
|
||||
|
||||
/** {@link Provides} check {@link EppRequestMessage} with expected response of domainExists. */
|
||||
@Provides
|
||||
@Named("checkExists")
|
||||
static EppRequestMessage provideCheckExistsRequestMessage(
|
||||
@Named("domainExists") EppResponseMessage domainExistsResponse,
|
||||
@Named("check") String checkTemplate) {
|
||||
return new EppRequestMessage(
|
||||
domainExistsResponse,
|
||||
checkTemplate,
|
||||
(clTrid, domain) ->
|
||||
ImmutableMap.of(
|
||||
CLIENT_TRID_KEY, clTrid,
|
||||
DOMAIN_KEY, domain));
|
||||
}
|
||||
|
||||
/** {@link Provides} check {@link EppRequestMessage} with expected response of domainExists. */
|
||||
@Provides
|
||||
@Named("checkNotExists")
|
||||
static EppRequestMessage provideCheckNotExistsRequestMessage(
|
||||
@Named("domainNotExists") EppResponseMessage domainNotExistsResponse,
|
||||
@Named("check") String checkTemplate) {
|
||||
return new EppRequestMessage(
|
||||
domainNotExistsResponse,
|
||||
checkTemplate,
|
||||
(clTrid, domain) ->
|
||||
ImmutableMap.of(
|
||||
CLIENT_TRID_KEY, clTrid,
|
||||
DOMAIN_KEY, domain));
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Named("success")
|
||||
static EppResponseMessage provideSuccessResponse() {
|
||||
return new EppResponseMessage(
|
||||
"success",
|
||||
(clTrid, domain) ->
|
||||
ImmutableList.of(
|
||||
String.format("//eppns:clTRID[.='%s']", clTrid), EppMessage.XPASS_EXPRESSION));
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Named("failure")
|
||||
static EppResponseMessage provideFailureResponse() {
|
||||
return new EppResponseMessage(
|
||||
"failure",
|
||||
(clTrid, domain) ->
|
||||
ImmutableList.of(
|
||||
String.format("//eppns:clTRID[.='%s']", clTrid), EppMessage.XFAIL_EXPRESSION));
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Named("domainExists")
|
||||
static EppResponseMessage provideDomainExistsResponse() {
|
||||
return new EppResponseMessage(
|
||||
"domainExists",
|
||||
(clTrid, domain) ->
|
||||
ImmutableList.of(
|
||||
String.format("//eppns:clTRID[.='%s']", clTrid),
|
||||
String.format("//domainns:name[@avail='false'][.='%s']", domain),
|
||||
EppMessage.XPASS_EXPRESSION));
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Named("domainNotExists")
|
||||
static EppResponseMessage provideDomainNotExistsResponse() {
|
||||
return new EppResponseMessage(
|
||||
"domainNotExists",
|
||||
(clTrid, domain) ->
|
||||
ImmutableList.of(
|
||||
String.format("//eppns:clTRID[.='%s']", clTrid),
|
||||
String.format("//domainns:name[@avail='true'][.='%s']", domain),
|
||||
EppMessage.XPASS_EXPRESSION));
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Named("greeting")
|
||||
static EppResponseMessage provideGreetingResponse() {
|
||||
return new EppResponseMessage(
|
||||
"greeting", (clTrid, domain) -> ImmutableList.of("//eppns:greeting"));
|
||||
}
|
||||
|
||||
/** {@link Provides} filename of template for login EPP request. */
|
||||
@Provides
|
||||
@Named("login")
|
||||
static String provideLoginTemplate() {
|
||||
return "login.xml";
|
||||
}
|
||||
|
||||
/** {@link Provides} filename of template for create EPP request. */
|
||||
@Provides
|
||||
@Named("create")
|
||||
static String provideCreateTemplate() {
|
||||
return "create.xml";
|
||||
}
|
||||
|
||||
/** {@link Provides} filename of template for delete EPP request. */
|
||||
@Provides
|
||||
@Named("delete")
|
||||
static String provideDeleteTemplate() {
|
||||
return "delete.xml";
|
||||
}
|
||||
|
||||
/** {@link Provides} filename of template for logout EPP request. */
|
||||
@Provides
|
||||
@Named("logout")
|
||||
static String provideLogoutTemplate() {
|
||||
return "logout.xml";
|
||||
}
|
||||
|
||||
/** {@link Provides} filename of template for check EPP request. */
|
||||
@Provides
|
||||
@Named("check")
|
||||
static String provideCheckTemplate() {
|
||||
return "check.xml";
|
||||
}
|
||||
|
||||
/** {@link Provides} {@link Protocol} that represents an EPP connection. */
|
||||
@Singleton
|
||||
@Provides
|
||||
@EppProtocol
|
||||
static Protocol provideEppProtocol(
|
||||
@EppProtocol int eppPort,
|
||||
@EppProtocol ImmutableList<Provider<? extends ChannelHandler>> handlerProviders) {
|
||||
return Protocol.builder()
|
||||
.setName(EPP_PROTOCOL_NAME)
|
||||
.setPort(eppPort)
|
||||
.setHandlerProviders(handlerProviders)
|
||||
.setPersistentConnection(true)
|
||||
.build();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@link Provides} the list of providers of {@link ChannelHandler}s that are used for the EPP
|
||||
* Protocol.
|
||||
*/
|
||||
@Provides
|
||||
@EppProtocol
|
||||
static ImmutableList<Provider<? extends ChannelHandler>> provideEppHandlerProviders(
|
||||
@EppProtocol Provider<SslClientInitializer<NioSocketChannel>> sslClientInitializerProvider,
|
||||
Provider<LengthFieldBasedFrameDecoder> lengthFieldBasedFrameDecoderProvider,
|
||||
Provider<LengthFieldPrepender> lengthFieldPrependerProvider,
|
||||
Provider<EppMessageHandler> eppMessageHandlerProvider,
|
||||
Provider<EppActionHandler> eppActionHandlerProvider) {
|
||||
return ImmutableList.of(
|
||||
sslClientInitializerProvider,
|
||||
lengthFieldBasedFrameDecoderProvider,
|
||||
lengthFieldPrependerProvider,
|
||||
eppMessageHandlerProvider,
|
||||
eppActionHandlerProvider);
|
||||
}
|
||||
|
||||
@Provides
|
||||
static LengthFieldBasedFrameDecoder provideLengthFieldBasedFrameDecoder() {
|
||||
return new LengthFieldBasedFrameDecoder(
|
||||
maximumMessageLengthBytes,
|
||||
0,
|
||||
eppHeaderLengthBytes,
|
||||
-eppHeaderLengthBytes,
|
||||
eppHeaderLengthBytes);
|
||||
}
|
||||
|
||||
@Provides
|
||||
static LengthFieldPrepender provideLengthFieldPrepender() {
|
||||
return new LengthFieldPrepender(eppHeaderLengthBytes, true);
|
||||
}
|
||||
|
||||
/** {@link Provides} the {@link SslClientInitializer} used for the {@link EppProtocol}. */
|
||||
@Provides
|
||||
@EppProtocol
|
||||
static SslClientInitializer<NioSocketChannel> provideSslClientInitializer(
|
||||
SslProvider sslProvider,
|
||||
@LocalSecrets Supplier<PrivateKey> privateKeySupplier,
|
||||
@LocalSecrets Supplier<X509Certificate[]> certificatesSupplier) {
|
||||
|
||||
return new SslClientInitializer<>(sslProvider, privateKeySupplier, certificatesSupplier);
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Named("eppUserId")
|
||||
static String provideEppUserId() {
|
||||
return readResourceUtf8(EppModule.class, "secrets/user_id.txt");
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Named("eppPassword")
|
||||
static String provideEppPassphrase() {
|
||||
return readResourceUtf8(EppModule.class, "secrets/password.txt");
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Named("eppHost")
|
||||
static String provideEppHost() {
|
||||
return readResourceUtf8(EppModule.class, "secrets/epp_host.txt");
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Named("eppTld")
|
||||
static String provideTld() {
|
||||
return "oa-0.test";
|
||||
}
|
||||
|
||||
@Provides
|
||||
@EppProtocol
|
||||
static int provideEppPort() {
|
||||
return EPP_PORT;
|
||||
}
|
||||
|
||||
/** Dagger qualifier to provide EPP protocol related handlers and other bindings. */
|
||||
@Qualifier
|
||||
public @interface EppProtocol {}
|
||||
}
|
|
@ -12,12 +12,15 @@
|
|||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package google.registry.monitoring.blackbox;
|
||||
package google.registry.monitoring.blackbox.modules;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import dagger.Module;
|
||||
import dagger.Provides;
|
||||
import dagger.multibindings.IntoSet;
|
||||
import google.registry.monitoring.blackbox.ProbingSequence;
|
||||
import google.registry.monitoring.blackbox.ProbingStep;
|
||||
import google.registry.monitoring.blackbox.connection.Protocol;
|
||||
import google.registry.monitoring.blackbox.handlers.SslClientInitializer;
|
||||
import google.registry.monitoring.blackbox.handlers.WebWhoisActionHandler;
|
||||
import google.registry.monitoring.blackbox.handlers.WebWhoisMessageHandler;
|
||||
|
@ -38,7 +41,8 @@ import javax.inject.Singleton;
|
|||
import org.joda.time.Duration;
|
||||
|
||||
/**
|
||||
* A module that provides the {@link Protocol}s to send HTTP(S) web WHOIS requests.
|
||||
* A module that provides the components necessary for and the overall {@link ProbingSequence} to
|
||||
* probe WebWHOIS.
|
||||
*/
|
||||
@Module
|
||||
public class WebWhoisModule {
|
||||
|
@ -48,14 +52,10 @@ public class WebWhoisModule {
|
|||
private static final int HTTP_WHOIS_PORT = 80;
|
||||
private static final int HTTPS_WHOIS_PORT = 443;
|
||||
|
||||
/**
|
||||
* Standard length of messages used by Proxy. Equates to 0.5 MB.
|
||||
*/
|
||||
/** Standard length of messages used by Proxy. Equates to 0.5 MB. */
|
||||
private static final int maximumMessageLengthBytes = 512 * 1024;
|
||||
|
||||
/**
|
||||
* {@link Provides} only step used in WebWhois sequence.
|
||||
*/
|
||||
/** {@link Provides} only step used in WebWhois sequence. */
|
||||
@Provides
|
||||
@WebWhoisProtocol
|
||||
static ProbingStep provideWebWhoisStep(
|
||||
|
@ -72,9 +72,7 @@ public class WebWhoisModule {
|
|||
.build();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@link Provides} the {@link Protocol} that corresponds to http connection.
|
||||
*/
|
||||
/** {@link Provides} the {@link Protocol} that corresponds to http connection. */
|
||||
@Singleton
|
||||
@Provides
|
||||
@HttpWhoisProtocol
|
||||
|
@ -89,9 +87,7 @@ public class WebWhoisModule {
|
|||
.build();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@link Provides} the {@link Protocol} that corresponds to https connection.
|
||||
*/
|
||||
/** {@link Provides} the {@link Protocol} that corresponds to https connection. */
|
||||
@Singleton
|
||||
@Provides
|
||||
@HttpsWhoisProtocol
|
||||
|
@ -123,7 +119,6 @@ public class WebWhoisModule {
|
|||
messageHandlerProvider,
|
||||
webWhoisActionHandlerProvider);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@link Provides} the list of providers of {@link ChannelHandler}s that are used for https
|
||||
* protocol.
|
||||
|
@ -155,9 +150,7 @@ public class WebWhoisModule {
|
|||
return new HttpObjectAggregator(maxContentLength);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@link Provides} the {@link SslClientInitializer} used for the {@link HttpsWhoisProtocol}.
|
||||
*/
|
||||
/** {@link Provides} the {@link SslClientInitializer} used for the {@link HttpsWhoisProtocol}. */
|
||||
@Provides
|
||||
@HttpsWhoisProtocol
|
||||
static SslClientInitializer<NioSocketChannel> provideSslClientInitializer(
|
||||
|
@ -165,33 +158,23 @@ public class WebWhoisModule {
|
|||
return new SslClientInitializer<>(sslProvider);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@link Provides} the {@link Bootstrap} used by the WebWhois sequence.
|
||||
*/
|
||||
/** {@link Provides} the {@link Bootstrap} used by the WebWhois sequence. */
|
||||
@Singleton
|
||||
@Provides
|
||||
@WebWhoisProtocol
|
||||
static Bootstrap provideBootstrap(
|
||||
EventLoopGroup eventLoopGroup,
|
||||
Class<? extends Channel> channelClazz) {
|
||||
return new Bootstrap()
|
||||
.group(eventLoopGroup)
|
||||
.channel(channelClazz);
|
||||
EventLoopGroup eventLoopGroup, Class<? extends Channel> channelClazz) {
|
||||
return new Bootstrap().group(eventLoopGroup).channel(channelClazz);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@link Provides} standard WebWhois sequence.
|
||||
*/
|
||||
/** {@link Provides} standard WebWhois sequence. */
|
||||
@Provides
|
||||
@Singleton
|
||||
@IntoSet
|
||||
ProbingSequence provideWebWhoisSequence(
|
||||
@WebWhoisProtocol ProbingStep probingStep,
|
||||
WebWhoisToken webWhoisToken) {
|
||||
@WebWhoisProtocol ProbingStep probingStep, WebWhoisToken webWhoisToken) {
|
||||
|
||||
return new ProbingSequence.Builder(webWhoisToken)
|
||||
.add(probingStep)
|
||||
.build();
|
||||
return new ProbingSequence.Builder(webWhoisToken).add(probingStep).build();
|
||||
}
|
||||
|
||||
@Provides
|
||||
|
@ -200,16 +183,12 @@ public class WebWhoisModule {
|
|||
return maximumMessageLengthBytes;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@link Provides} the list of top level domains to be probed
|
||||
*/
|
||||
/** {@link Provides} the list of top level domains to be probed */
|
||||
@Singleton
|
||||
@Provides
|
||||
@WebWhoisProtocol
|
||||
CircularList<String> provideTopLevelDomains() {
|
||||
return new CircularList.Builder<String>()
|
||||
.add("how", "soy", "xn--q9jyb4c")
|
||||
.build();
|
||||
return new CircularList.Builder<String>().add("how", "soy", "xn--q9jyb4c").build();
|
||||
}
|
||||
|
||||
@Provides
|
||||
|
@ -224,28 +203,15 @@ public class WebWhoisModule {
|
|||
return HTTPS_WHOIS_PORT;
|
||||
}
|
||||
|
||||
/**
|
||||
* Dagger qualifier to provide HTTP whois protocol related handlers and other bindings.
|
||||
*/
|
||||
/** Dagger qualifier to provide HTTP whois protocol related handlers and other bindings. */
|
||||
@Qualifier
|
||||
public @interface HttpWhoisProtocol {
|
||||
public @interface HttpWhoisProtocol {}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Dagger qualifier to provide HTTPS whois protocol related handlers and other bindings.
|
||||
*/
|
||||
/** Dagger qualifier to provide HTTPS whois protocol related handlers and other bindings. */
|
||||
@Qualifier
|
||||
public @interface HttpsWhoisProtocol {
|
||||
public @interface HttpsWhoisProtocol {}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Dagger qualifier to provide any WebWhois related bindings.
|
||||
*/
|
||||
/** Dagger qualifier to provide any WebWhois related bindings. */
|
||||
@Qualifier
|
||||
public @interface WebWhoisProtocol {
|
||||
|
||||
}
|
||||
public @interface WebWhoisProtocol {}
|
||||
}
|
||||
|
|
@ -0,0 +1,138 @@
|
|||
// Copyright 2019 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.monitoring.blackbox.tokens;
|
||||
|
||||
import com.google.common.annotations.VisibleForTesting;
|
||||
import google.registry.monitoring.blackbox.exceptions.UndeterminedStateException;
|
||||
import google.registry.monitoring.blackbox.messages.EppRequestMessage;
|
||||
import google.registry.monitoring.blackbox.messages.OutboundMessageType;
|
||||
import io.netty.channel.Channel;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Named;
|
||||
|
||||
/** {@link Token} subtype that deals performs specified actions for the EPP sequence. */
|
||||
public abstract class EppToken extends Token {
|
||||
|
||||
/** Describes the maximum possible length of generated domain name. */
|
||||
private static final int MAX_DOMAIN_PART_LENGTH = 50;
|
||||
|
||||
/** Suffix added for differentiation between two TRID in case they have the same timestamp. */
|
||||
private static AtomicInteger clientIdSuffix = new AtomicInteger();
|
||||
|
||||
protected final String tld;
|
||||
private String host;
|
||||
private String currentDomainName;
|
||||
|
||||
/**
|
||||
* Always the constructor used to provide any {@link EppToken}, with {@code tld} and {@code host}
|
||||
* specified by Dagger.
|
||||
*/
|
||||
protected EppToken(String tld, String host) {
|
||||
this.tld = tld;
|
||||
this.host = host;
|
||||
currentDomainName = newDomainName(getNewTRID());
|
||||
}
|
||||
|
||||
/** Constructor used when passing on same {@link Channel} to next {@link Token}. */
|
||||
protected EppToken(String tld, String host, Channel channel) {
|
||||
this(tld, host);
|
||||
setChannel(channel);
|
||||
}
|
||||
|
||||
/** Modifies the message to reflect the new domain name and TRID */
|
||||
@Override
|
||||
public OutboundMessageType modifyMessage(OutboundMessageType originalMessage)
|
||||
throws UndeterminedStateException {
|
||||
return ((EppRequestMessage) originalMessage).modifyMessage(getNewTRID(), currentDomainName);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String host() {
|
||||
return host;
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
String getCurrentDomainName() {
|
||||
return currentDomainName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a unique string usable as an EPP client transaction ID.
|
||||
*
|
||||
* <p><b>Warning:</b> The prober cleanup servlet relies on the timestamp being in the third
|
||||
* position when splitting on dashes. Do not change this format without updating that code as
|
||||
* well.
|
||||
*/
|
||||
private String getNewTRID() {
|
||||
return String.format(
|
||||
"prober-%s-%d-%d",
|
||||
"localhost", System.currentTimeMillis(), clientIdSuffix.incrementAndGet());
|
||||
}
|
||||
|
||||
/** Return a fully qualified domain label to use, derived from the client transaction ID. */
|
||||
private String newDomainName(String clTrid) {
|
||||
String sld;
|
||||
// not sure if the local hostname will stick to RFC validity rules
|
||||
if (clTrid.length() > MAX_DOMAIN_PART_LENGTH) {
|
||||
sld = clTrid.substring(clTrid.length() - MAX_DOMAIN_PART_LENGTH);
|
||||
} else {
|
||||
sld = clTrid;
|
||||
}
|
||||
// insert top level domain here
|
||||
return String.format("%s.%s", sld, tld);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@link EppToken} Subclass that represents a token used in a transient sequence, meaning the
|
||||
* connection is remade on each new iteration of the {@link
|
||||
* google.registry.monitoring.blackbox.ProbingSequence}.
|
||||
*/
|
||||
public static class Transient extends EppToken {
|
||||
|
||||
@Inject
|
||||
public Transient(@Named("eppTld") String tld, @Named("eppHost") String host) {
|
||||
super(tld, host);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Token next() {
|
||||
return new Transient(tld, host());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@link EppToken} Subclass that represents a token used in a persistent sequence, meaning the
|
||||
* connection is maintained on each new iteration of the {@link
|
||||
* google.registry.monitoring.blackbox.ProbingSequence}.
|
||||
*/
|
||||
public static class Persistent extends EppToken {
|
||||
|
||||
@Inject
|
||||
public Persistent(@Named("eppTld") String tld, @Named("eppHost") String host) {
|
||||
super(tld, host);
|
||||
}
|
||||
|
||||
/** Constructor used on call to {@code next} to preserve channel. */
|
||||
private Persistent(String tld, String host, Channel channel) {
|
||||
super(tld, host, channel);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Token next() {
|
||||
return new Persistent(tld, host(), channel());
|
||||
}
|
||||
}
|
||||
}
|
|
@ -24,12 +24,12 @@ import io.netty.channel.Channel;
|
|||
* Superclass that represents information passed to each {@link ProbingStep} in a single loop of a
|
||||
* {@link ProbingSequence}.
|
||||
*
|
||||
* <p>Modifies the message passed in to reflect information relevant to a single loop
|
||||
* in a {@link ProbingSequence}. Additionally, passes on channel that remains unchanged within a
|
||||
* loop of the sequence.</p>
|
||||
* <p>Modifies the message passed in to reflect information relevant to a single loop in a {@link
|
||||
* ProbingSequence}. Additionally, passes on channel that remains unchanged within a loop of the
|
||||
* sequence.
|
||||
*
|
||||
* <p>Also obtains the next {@link Token} corresponding to the next iteration of a loop
|
||||
* in the sequence.</p>
|
||||
* <p>Also obtains the next {@link Token} corresponding to the next iteration of a loop in the
|
||||
* sequence.
|
||||
*/
|
||||
public abstract class Token {
|
||||
|
||||
|
@ -40,32 +40,22 @@ public abstract class Token {
|
|||
*/
|
||||
protected Channel channel;
|
||||
|
||||
/**
|
||||
* Obtains next {@link Token} for next loop in sequence.
|
||||
*/
|
||||
/** Obtains next {@link Token} for next loop in sequence. */
|
||||
public abstract Token next();
|
||||
|
||||
/**
|
||||
* String corresponding to host that is relevant for loop in sequence.
|
||||
*/
|
||||
/** String corresponding to host that is relevant for loop in sequence. */
|
||||
public abstract String host();
|
||||
|
||||
/**
|
||||
* Modifies the {@link OutboundMessageType} in the manner necessary for each loop
|
||||
*/
|
||||
/** Modifies the {@link OutboundMessageType} in the manner necessary for each loop */
|
||||
public abstract OutboundMessageType modifyMessage(OutboundMessageType messageType)
|
||||
throws UndeterminedStateException;
|
||||
|
||||
/**
|
||||
* Set method for {@code channel}
|
||||
*/
|
||||
/** Set method for {@code channel} */
|
||||
public void setChannel(Channel channel) {
|
||||
this.channel = channel;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get method for {@code channel}.
|
||||
*/
|
||||
/** Get method for {@code channel}. */
|
||||
public Channel channel() {
|
||||
return this.channel;
|
||||
}
|
||||
|
|
|
@ -15,29 +15,25 @@
|
|||
package google.registry.monitoring.blackbox.tokens;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import google.registry.monitoring.blackbox.WebWhoisModule.WebWhoisProtocol;
|
||||
import google.registry.monitoring.blackbox.exceptions.UndeterminedStateException;
|
||||
import google.registry.monitoring.blackbox.messages.OutboundMessageType;
|
||||
import google.registry.monitoring.blackbox.modules.WebWhoisModule.WebWhoisProtocol;
|
||||
import google.registry.util.CircularList;
|
||||
import javax.inject.Inject;
|
||||
|
||||
/**
|
||||
* {@link Token} subtype designed for WebWhois sequence.
|
||||
*
|
||||
* <p>Between loops of a WebWhois sequence the only thing changing is the tld we
|
||||
* are probing. As a result, we maintain the list of {@code topLevelDomains} and on each call to
|
||||
* next, have our index looking at the next {@code topLevelDomain}. </p>
|
||||
* <p>Between loops of a WebWhois sequence the only thing changing is the tld we are probing. As a
|
||||
* result, we maintain the list of {@code topLevelDomains} and on each call to next, have our index
|
||||
* looking at the next {@code topLevelDomain}.
|
||||
*/
|
||||
public class WebWhoisToken extends Token {
|
||||
|
||||
/**
|
||||
* For each top level domain (tld), we probe "prefix.tld".
|
||||
*/
|
||||
/** For each top level domain (tld), we probe "prefix.tld". */
|
||||
private static final String PREFIX = "whois.nic.";
|
||||
|
||||
/**
|
||||
* {@link ImmutableList} of all top level domains to be probed.
|
||||
*/
|
||||
/** {@link ImmutableList} of all top level domains to be probed. */
|
||||
private CircularList<String> topLevelDomainsList;
|
||||
|
||||
@Inject
|
||||
|
@ -46,18 +42,14 @@ public class WebWhoisToken extends Token {
|
|||
this.topLevelDomainsList = topLevelDomainsList;
|
||||
}
|
||||
|
||||
/**
|
||||
* Moves on to next top level domain in {@code topLevelDomainsList}.
|
||||
*/
|
||||
/** Moves on to next top level domain in {@code topLevelDomainsList}. */
|
||||
@Override
|
||||
public WebWhoisToken next() {
|
||||
topLevelDomainsList = topLevelDomainsList.next();
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Modifies message to reflect the new host coming from the new top level domain.
|
||||
*/
|
||||
/** Modifies message to reflect the new host coming from the new top level domain. */
|
||||
@Override
|
||||
public OutboundMessageType modifyMessage(OutboundMessageType original)
|
||||
throws UndeterminedStateException {
|
||||
|
@ -73,4 +65,3 @@ public class WebWhoisToken extends Token {
|
|||
return PREFIX + topLevelDomainsList.get();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,11 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<epp xmlns="urn:ietf:params:xml:ns:epp-1.0">
|
||||
<command>
|
||||
<check>
|
||||
<domain:check xmlns:domain="urn:ietf:params:xml:ns:domain-1.0">
|
||||
<domain:name>@@DOMAINNAME@@</domain:name>
|
||||
</domain:check>
|
||||
</check>
|
||||
<clTRID>@@CLTRID@@</clTRID>
|
||||
</command>
|
||||
</epp>
|
|
@ -0,0 +1,17 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<epp xmlns="urn:ietf:params:xml:ns:epp-1.0">
|
||||
<command>
|
||||
<check>
|
||||
<domain:check xmlns:domain="urn:ietf:params:xml:ns:domain-1.0">
|
||||
<domain:name>@@DOMAINNAME@@</domain:name>
|
||||
</domain:check>
|
||||
</check>
|
||||
<extension>
|
||||
<launch:check
|
||||
xmlns:launch="urn:ietf:params:xml:ns:launch-1.0" type="claims">
|
||||
<launch:phase>claims</launch:phase>
|
||||
</launch:check>
|
||||
</extension>
|
||||
<clTRID>@@CLTRID@@</clTRID>
|
||||
</command>
|
||||
</epp>
|
|
@ -0,0 +1,21 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<epp xmlns="urn:ietf:params:xml:ns:epp-1.0">
|
||||
<command>
|
||||
<create>
|
||||
<domain:create xmlns:domain="urn:ietf:params:xml:ns:domain-1.0">
|
||||
<domain:name>@@DOMAINNAME@@</domain:name>
|
||||
<domain:period unit="y">1</domain:period>
|
||||
<domain:ns>
|
||||
<domain:hostObj>ns.fake-domain.tld</domain:hostObj>
|
||||
</domain:ns>
|
||||
<domain:registrant>google-mon</domain:registrant>
|
||||
<domain:contact type="admin">google-mon</domain:contact>
|
||||
<domain:contact type="tech">google-mon</domain:contact>
|
||||
<domain:authInfo>
|
||||
<domain:pw>insecure</domain:pw>
|
||||
</domain:authInfo>
|
||||
</domain:create>
|
||||
</create>
|
||||
<clTRID>@@CLTRID@@</clTRID>
|
||||
</command>
|
||||
</epp>
|
|
@ -0,0 +1,11 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<epp xmlns="urn:ietf:params:xml:ns:epp-1.0">
|
||||
<command>
|
||||
<delete>
|
||||
<domain:delete xmlns:domain="urn:ietf:params:xml:ns:domain-1.0">
|
||||
<domain:name>@@DOMAINNAME@@</domain:name>
|
||||
</domain:delete>
|
||||
</delete>
|
||||
<clTRID>@@CLTRID@@</clTRID>
|
||||
</command>
|
||||
</epp>
|
|
@ -0,0 +1,19 @@
|
|||
<?xml version='1.0' encoding='UTF-8'?>
|
||||
<epp xmlns='urn:ietf:params:xml:ns:epp-1.0'>
|
||||
<response>
|
||||
<result code='1000'>
|
||||
<msg>Generic Message</msg>
|
||||
</result>
|
||||
<resData>
|
||||
<domain:chkData xmlns:domain='urn:ietf:params:xml:ns:domain-1.0'>
|
||||
<domain:cd>
|
||||
<domain:name avail='false'>@@DOMAINNAME@@</domain:name>
|
||||
</domain:cd>
|
||||
</domain:chkData>
|
||||
</resData>
|
||||
<trID>
|
||||
<clTRID>@@CLTRID@@</clTRID>
|
||||
<svTRID>@@SVTRID@@</svTRID>
|
||||
</trID>
|
||||
</response>
|
||||
</epp>
|
|
@ -0,0 +1,19 @@
|
|||
<?xml version='1.0' encoding='UTF-8'?>
|
||||
<epp xmlns='urn:ietf:params:xml:ns:epp-1.0'>
|
||||
<response>
|
||||
<result code='1000'>
|
||||
<msg>Generic Message</msg>
|
||||
</result>
|
||||
<resData>
|
||||
<domain:chkData xmlns:domain='urn:ietf:params:xml:ns:domain-1.0'>
|
||||
<domain:cd>
|
||||
<domain:name avail='true'>@@DOMAINNAME@@</domain:name>
|
||||
</domain:cd>
|
||||
</domain:chkData>
|
||||
</resData>
|
||||
<trID>
|
||||
<clTRID>@@CLTRID@@</clTRID>
|
||||
<svTRID>@@SVTRID@@</svTRID>
|
||||
</trID>
|
||||
</response>
|
||||
</epp>
|
|
@ -0,0 +1,12 @@
|
|||
<?xml version='1.0' encoding='UTF-8'?>
|
||||
<epp xmlns='urn:ietf:params:xml:ns:epp-1.0'>
|
||||
<response>
|
||||
<result code='2500'>
|
||||
<msg>Throwaway Message</msg>
|
||||
</result>
|
||||
<trID>
|
||||
<clTRID>@@CLTRID@@</clTRID>
|
||||
<svTRID>@@SVTRID@@</svTRID>
|
||||
</trID>
|
||||
</response>
|
||||
</epp>
|
|
@ -0,0 +1,21 @@
|
|||
<epp xmlns="urn:ietf:params:xml:ns:epp-1.0">
|
||||
<greeting>
|
||||
<svID>Test EPP Server</svID>
|
||||
<svDate>2000-06-08T22:00:00.0Z</svDate>
|
||||
<svcMenu>
|
||||
<version>1.0</version>
|
||||
<lang>en</lang>
|
||||
<objURI>urn:ietf:params:xml:ns:contact-1.0</objURI>
|
||||
<objURI>urn:ietf:params:xml:ns:domain-1.0</objURI>
|
||||
<objURI>urn:ietf:params:xml:ns:host-1.0</objURI>
|
||||
</svcMenu>
|
||||
<dcp>
|
||||
<access><all/></access>
|
||||
<statement>
|
||||
<purpose><admin/><prov/></purpose>
|
||||
<recipient><ours/></recipient>
|
||||
<retention><indefinite/></retention>
|
||||
</statement>
|
||||
</dcp>
|
||||
</greeting>
|
||||
</epp>
|
|
@ -0,0 +1,22 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<epp xmlns="urn:ietf:params:xml:ns:epp-1.0">
|
||||
<command>
|
||||
<login>
|
||||
<clID>@@CLID@@</clID>
|
||||
<pw>@@PWD@@</pw>
|
||||
<options>
|
||||
<version>1.0</version>
|
||||
<lang>en</lang>
|
||||
</options>
|
||||
<svcs>
|
||||
<objURI>urn:ietf:params:xml:ns:domain-1.0</objURI>
|
||||
<objURI>urn:ietf:params:xml:ns:contact-1.0</objURI>
|
||||
<objURI>urn:ietf:params:xml:ns:host-1.0</objURI>
|
||||
<svcExtension>
|
||||
<extURI>urn:ietf:params:xml:ns:launch-1.0</extURI>
|
||||
</svcExtension>
|
||||
</svcs>
|
||||
</login>
|
||||
<clTRID>@@CLTRID@@</clTRID>
|
||||
</command>
|
||||
</epp>
|
|
@ -0,0 +1,7 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<epp xmlns="urn:ietf:params:xml:ns:epp-1.0">
|
||||
<command>
|
||||
<logout/>
|
||||
<clTRID>ABC-12345</clTRID>
|
||||
</command>
|
||||
</epp>
|
|
@ -0,0 +1,12 @@
|
|||
<?xml version='1.0' encoding='UTF-8'?>
|
||||
<epp xmlns='urn:ietf:params:xml:ns:epp-1.0'>
|
||||
<response>
|
||||
<result code='1000'>
|
||||
<msg>Throwaway Message</msg>
|
||||
</result>
|
||||
<trID>
|
||||
<clTRID>@@CLTRID@@</clTRID>
|
||||
<svTRID>@@SVTRID@@</svTRID>
|
||||
</trID>
|
||||
</response>
|
||||
</epp>
|
|
@ -0,0 +1,389 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<schema targetNamespace="urn:ietf:params:xml:ns:contact-1.0"
|
||||
xmlns:contact="urn:ietf:params:xml:ns:contact-1.0"
|
||||
xmlns:epp="urn:ietf:params:xml:ns:epp-1.0"
|
||||
xmlns:eppcom="urn:ietf:params:xml:ns:eppcom-1.0"
|
||||
xmlns="http://www.w3.org/2001/XMLSchema"
|
||||
elementFormDefault="qualified">
|
||||
|
||||
<!--
|
||||
Import common element types.
|
||||
-->
|
||||
<import namespace="urn:ietf:params:xml:ns:eppcom-1.0"
|
||||
schemaLocation="eppcom.xsd"/>
|
||||
<import namespace="urn:ietf:params:xml:ns:epp-1.0"
|
||||
schemaLocation="epp.xsd"/>
|
||||
|
||||
<annotation>
|
||||
<documentation>
|
||||
Extensible Provisioning Protocol v1.0
|
||||
contact provisioning schema.
|
||||
</documentation>
|
||||
</annotation>
|
||||
|
||||
<!--
|
||||
Child elements found in EPP commands.
|
||||
-->
|
||||
<element name="check" type="contact:mIDType"/>
|
||||
<element name="create" type="contact:createType"/>
|
||||
<element name="delete" type="contact:sIDType"/>
|
||||
<element name="info" type="contact:authIDType"/>
|
||||
<element name="transfer" type="contact:authIDType"/>
|
||||
<element name="update" type="contact:updateType"/>
|
||||
|
||||
<!--
|
||||
Utility types.
|
||||
-->
|
||||
<simpleType name="ccType">
|
||||
<restriction base="token">
|
||||
<length value="2"/>
|
||||
</restriction>
|
||||
</simpleType>
|
||||
<complexType name="e164Type">
|
||||
<simpleContent>
|
||||
<extension base="contact:e164StringType">
|
||||
<attribute name="x" type="token"/>
|
||||
</extension>
|
||||
</simpleContent>
|
||||
</complexType>
|
||||
|
||||
<simpleType name="e164StringType">
|
||||
<restriction base="token">
|
||||
<pattern value="(\+[0-9]{1,3}\.[0-9]{1,14})?"/>
|
||||
<maxLength value="17"/>
|
||||
</restriction>
|
||||
</simpleType>
|
||||
|
||||
<simpleType name="pcType">
|
||||
<restriction base="token">
|
||||
<maxLength value="16"/>
|
||||
</restriction>
|
||||
</simpleType>
|
||||
|
||||
<simpleType name="postalLineType">
|
||||
<restriction base="normalizedString">
|
||||
<minLength value="1"/>
|
||||
<maxLength value="255"/>
|
||||
</restriction>
|
||||
</simpleType>
|
||||
|
||||
<simpleType name="optPostalLineType">
|
||||
<restriction base="normalizedString">
|
||||
<maxLength value="255"/>
|
||||
</restriction>
|
||||
</simpleType>
|
||||
|
||||
<!--
|
||||
Child elements of the <create> command.
|
||||
-->
|
||||
<complexType name="createType">
|
||||
<sequence>
|
||||
<element name="id" type="eppcom:clIDType"/>
|
||||
<element name="postalInfo" type="contact:postalInfoType"
|
||||
maxOccurs="2"/>
|
||||
<element name="voice" type="contact:e164Type"
|
||||
minOccurs="0"/>
|
||||
<element name="fax" type="contact:e164Type"
|
||||
minOccurs="0"/>
|
||||
<element name="email" type="eppcom:minTokenType"/>
|
||||
<element name="authInfo" type="contact:authInfoType"/>
|
||||
<element name="disclose" type="contact:discloseType"
|
||||
minOccurs="0"/>
|
||||
</sequence>
|
||||
</complexType>
|
||||
|
||||
<complexType name="postalInfoType">
|
||||
<sequence>
|
||||
<element name="name" type="contact:postalLineType"/>
|
||||
<element name="org" type="contact:optPostalLineType"
|
||||
minOccurs="0"/>
|
||||
<element name="addr" type="contact:addrType"/>
|
||||
</sequence>
|
||||
<attribute name="type" type="contact:postalInfoEnumType"
|
||||
use="required"/>
|
||||
</complexType>
|
||||
|
||||
<simpleType name="postalInfoEnumType">
|
||||
<restriction base="token">
|
||||
<enumeration value="loc"/>
|
||||
<enumeration value="int"/>
|
||||
</restriction>
|
||||
</simpleType>
|
||||
|
||||
<complexType name="addrType">
|
||||
<sequence>
|
||||
<element name="street" type="contact:optPostalLineType"
|
||||
minOccurs="0" maxOccurs="3"/>
|
||||
<element name="city" type="contact:postalLineType"/>
|
||||
<element name="sp" type="contact:optPostalLineType"
|
||||
minOccurs="0"/>
|
||||
<element name="pc" type="contact:pcType"
|
||||
minOccurs="0"/>
|
||||
<element name="cc" type="contact:ccType"/>
|
||||
</sequence>
|
||||
</complexType>
|
||||
|
||||
<complexType name="authInfoType">
|
||||
<choice>
|
||||
<element name="pw" type="eppcom:pwAuthInfoType"/>
|
||||
<element name="ext" type="eppcom:extAuthInfoType"/>
|
||||
</choice>
|
||||
</complexType>
|
||||
|
||||
<complexType name="discloseType">
|
||||
<sequence>
|
||||
<element name="name" type="contact:intLocType"
|
||||
minOccurs="0" maxOccurs="2"/>
|
||||
<element name="org" type="contact:intLocType"
|
||||
minOccurs="0" maxOccurs="2"/>
|
||||
<element name="addr" type="contact:intLocType"
|
||||
minOccurs="0" maxOccurs="2"/>
|
||||
<element name="voice" minOccurs="0"/>
|
||||
<element name="fax" minOccurs="0"/>
|
||||
<element name="email" minOccurs="0"/>
|
||||
</sequence>
|
||||
<attribute name="flag" type="boolean" use="required"/>
|
||||
</complexType>
|
||||
|
||||
<complexType name="intLocType">
|
||||
<attribute name="type" type="contact:postalInfoEnumType"
|
||||
use="required"/>
|
||||
</complexType>
|
||||
|
||||
<!--
|
||||
Child element of commands that require only an identifier.
|
||||
-->
|
||||
<complexType name="sIDType">
|
||||
<sequence>
|
||||
<element name="id" type="eppcom:clIDType"/>
|
||||
</sequence>
|
||||
</complexType>
|
||||
|
||||
<!--
|
||||
Child element of commands that accept multiple identifiers.
|
||||
-->
|
||||
<complexType name="mIDType">
|
||||
<sequence>
|
||||
<element name="id" type="eppcom:clIDType"
|
||||
maxOccurs="unbounded"/>
|
||||
</sequence>
|
||||
</complexType>
|
||||
|
||||
<!--
|
||||
Child elements of the <info> and <transfer> commands.
|
||||
-->
|
||||
<complexType name="authIDType">
|
||||
<sequence>
|
||||
<element name="id" type="eppcom:clIDType"/>
|
||||
<element name="authInfo" type="contact:authInfoType"
|
||||
minOccurs="0"/>
|
||||
</sequence>
|
||||
</complexType>
|
||||
|
||||
<!--
|
||||
Child elements of the <update> command.
|
||||
-->
|
||||
<complexType name="updateType">
|
||||
<sequence>
|
||||
<element name="id" type="eppcom:clIDType"/>
|
||||
<element name="add" type="contact:addRemType"
|
||||
minOccurs="0"/>
|
||||
<element name="rem" type="contact:addRemType"
|
||||
minOccurs="0"/>
|
||||
<element name="chg" type="contact:chgType"
|
||||
minOccurs="0"/>
|
||||
</sequence>
|
||||
</complexType>
|
||||
|
||||
<!--
|
||||
Data elements that can be added or removed.
|
||||
-->
|
||||
<complexType name="addRemType">
|
||||
<sequence>
|
||||
<element name="status" type="contact:statusType"
|
||||
maxOccurs="7"/>
|
||||
</sequence>
|
||||
</complexType>
|
||||
|
||||
<!--
|
||||
Data elements that can be changed.
|
||||
-->
|
||||
<complexType name="chgType">
|
||||
<sequence>
|
||||
<element name="postalInfo" type="contact:chgPostalInfoType"
|
||||
minOccurs="0" maxOccurs="2"/>
|
||||
<element name="voice" type="contact:e164Type"
|
||||
minOccurs="0"/>
|
||||
<element name="fax" type="contact:e164Type"
|
||||
minOccurs="0"/>
|
||||
<element name="email" type="eppcom:minTokenType"
|
||||
minOccurs="0"/>
|
||||
<element name="authInfo" type="contact:authInfoType"
|
||||
minOccurs="0"/>
|
||||
<element name="disclose" type="contact:discloseType"
|
||||
minOccurs="0"/>
|
||||
</sequence>
|
||||
</complexType>
|
||||
|
||||
<complexType name="chgPostalInfoType">
|
||||
<sequence>
|
||||
<element name="name" type="contact:postalLineType"
|
||||
minOccurs="0"/>
|
||||
<element name="org" type="contact:optPostalLineType"
|
||||
minOccurs="0"/>
|
||||
<element name="addr" type="contact:addrType"
|
||||
minOccurs="0"/>
|
||||
</sequence>
|
||||
<attribute name="type" type="contact:postalInfoEnumType"
|
||||
use="required"/>
|
||||
</complexType>
|
||||
|
||||
<!--
|
||||
Child response elements.
|
||||
-->
|
||||
<element name="chkData" type="contact:chkDataType"/>
|
||||
<element name="creData" type="contact:creDataType"/>
|
||||
<element name="infData" type="contact:infDataType"/>
|
||||
<element name="panData" type="contact:panDataType"/>
|
||||
<element name="trnData" type="contact:trnDataType"/>
|
||||
|
||||
<!--
|
||||
<check> response elements.
|
||||
-->
|
||||
<complexType name="chkDataType">
|
||||
<sequence>
|
||||
<element name="cd" type="contact:checkType"
|
||||
maxOccurs="unbounded"/>
|
||||
</sequence>
|
||||
</complexType>
|
||||
|
||||
<complexType name="checkType">
|
||||
<sequence>
|
||||
<element name="id" type="contact:checkIDType"/>
|
||||
<element name="reason" type="eppcom:reasonType"
|
||||
minOccurs="0"/>
|
||||
</sequence>
|
||||
</complexType>
|
||||
|
||||
<complexType name="checkIDType">
|
||||
<simpleContent>
|
||||
<extension base="eppcom:clIDType">
|
||||
<attribute name="avail" type="boolean"
|
||||
use="required"/>
|
||||
</extension>
|
||||
</simpleContent>
|
||||
</complexType>
|
||||
|
||||
<!--
|
||||
<create> response elements.
|
||||
-->
|
||||
<complexType name="creDataType">
|
||||
<sequence>
|
||||
<element name="id" type="eppcom:clIDType"/>
|
||||
<element name="crDate" type="dateTime"/>
|
||||
</sequence>
|
||||
</complexType>
|
||||
|
||||
<!--
|
||||
<info> response elements.
|
||||
-->
|
||||
<complexType name="infDataType">
|
||||
<sequence>
|
||||
<element name="id" type="eppcom:clIDType"/>
|
||||
<element name="roid" type="eppcom:roidType"/>
|
||||
<element name="status" type="contact:statusType"
|
||||
maxOccurs="7"/>
|
||||
<element name="postalInfo" type="contact:postalInfoType"
|
||||
maxOccurs="2"/>
|
||||
<element name="voice" type="contact:e164Type"
|
||||
minOccurs="0"/>
|
||||
<element name="fax" type="contact:e164Type"
|
||||
minOccurs="0"/>
|
||||
<element name="email" type="eppcom:minTokenType"/>
|
||||
<element name="clID" type="eppcom:clIDType"/>
|
||||
<element name="crID" type="eppcom:clIDType"/>
|
||||
<element name="crDate" type="dateTime"/>
|
||||
<element name="upID" type="eppcom:clIDType"
|
||||
minOccurs="0"/>
|
||||
<element name="upDate" type="dateTime"
|
||||
minOccurs="0"/>
|
||||
<element name="trDate" type="dateTime"
|
||||
minOccurs="0"/>
|
||||
<element name="authInfo" type="contact:authInfoType"
|
||||
minOccurs="0"/>
|
||||
<element name="disclose" type="contact:discloseType"
|
||||
minOccurs="0"/>
|
||||
</sequence>
|
||||
</complexType>
|
||||
|
||||
<!--
|
||||
Status is a combination of attributes and an optional human-readable
|
||||
message that may be expressed in languages other than English.
|
||||
-->
|
||||
<complexType name="statusType">
|
||||
<simpleContent>
|
||||
<extension base="normalizedString">
|
||||
<attribute name="s" type="contact:statusValueType"
|
||||
use="required"/>
|
||||
<attribute name="lang" type="language"
|
||||
default="en"/>
|
||||
</extension>
|
||||
</simpleContent>
|
||||
</complexType>
|
||||
|
||||
<simpleType name="statusValueType">
|
||||
<restriction base="token">
|
||||
<enumeration value="clientDeleteProhibited"/>
|
||||
<enumeration value="clientTransferProhibited"/>
|
||||
<enumeration value="clientUpdateProhibited"/>
|
||||
<enumeration value="linked"/>
|
||||
<enumeration value="ok"/>
|
||||
<enumeration value="pendingCreate"/>
|
||||
<enumeration value="pendingDelete"/>
|
||||
<enumeration value="pendingTransfer"/>
|
||||
<enumeration value="pendingUpdate"/>
|
||||
<enumeration value="serverDeleteProhibited"/>
|
||||
<enumeration value="serverTransferProhibited"/>
|
||||
<enumeration value="serverUpdateProhibited"/>
|
||||
</restriction>
|
||||
</simpleType>
|
||||
|
||||
<!--
|
||||
Pending action notification response elements.
|
||||
-->
|
||||
<complexType name="panDataType">
|
||||
<sequence>
|
||||
<element name="id" type="contact:paCLIDType"/>
|
||||
<element name="paTRID" type="epp:trIDType"/>
|
||||
<element name="paDate" type="dateTime"/>
|
||||
</sequence>
|
||||
</complexType>
|
||||
|
||||
<complexType name="paCLIDType">
|
||||
<simpleContent>
|
||||
<extension base="eppcom:clIDType">
|
||||
<attribute name="paResult" type="boolean"
|
||||
use="required"/>
|
||||
</extension>
|
||||
</simpleContent>
|
||||
</complexType>
|
||||
|
||||
<!--
|
||||
<transfer> response elements.
|
||||
-->
|
||||
<complexType name="trnDataType">
|
||||
<sequence>
|
||||
<element name="id" type="eppcom:clIDType"/>
|
||||
<element name="trStatus" type="eppcom:trStatusType"/>
|
||||
<element name="reID" type="eppcom:clIDType"/>
|
||||
<element name="reDate" type="dateTime"/>
|
||||
<element name="acID" type="eppcom:clIDType"/>
|
||||
<element name="acDate" type="dateTime"/>
|
||||
</sequence>
|
||||
</complexType>
|
||||
|
||||
<!--
|
||||
End of schema.
|
||||
-->
|
||||
</schema>
|
|
@ -0,0 +1,434 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<schema targetNamespace="urn:ietf:params:xml:ns:domain-1.0"
|
||||
xmlns:domain="urn:ietf:params:xml:ns:domain-1.0"
|
||||
xmlns:host="urn:ietf:params:xml:ns:host-1.0"
|
||||
xmlns:epp="urn:ietf:params:xml:ns:epp-1.0"
|
||||
xmlns:eppcom="urn:ietf:params:xml:ns:eppcom-1.0"
|
||||
xmlns="http://www.w3.org/2001/XMLSchema"
|
||||
elementFormDefault="qualified">
|
||||
|
||||
<!--
|
||||
Import common element types.
|
||||
-->
|
||||
<import namespace="urn:ietf:params:xml:ns:eppcom-1.0"
|
||||
schemaLocation="eppcom.xsd"/>
|
||||
<import namespace="urn:ietf:params:xml:ns:epp-1.0"
|
||||
schemaLocation="epp.xsd"/>
|
||||
<import namespace="urn:ietf:params:xml:ns:host-1.0"
|
||||
schemaLocation="host.xsd"/>
|
||||
|
||||
<annotation>
|
||||
<documentation>
|
||||
Extensible Provisioning Protocol v1.0
|
||||
domain provisioning schema.
|
||||
</documentation>
|
||||
</annotation>
|
||||
|
||||
<!--
|
||||
Child elements found in EPP commands.
|
||||
-->
|
||||
<element name="check" type="domain:mNameType"/>
|
||||
<element name="create" type="domain:createType"/>
|
||||
<element name="delete" type="domain:sNameType"/>
|
||||
<element name="info" type="domain:infoType"/>
|
||||
<element name="renew" type="domain:renewType"/>
|
||||
<element name="transfer" type="domain:transferType"/>
|
||||
<element name="update" type="domain:updateType"/>
|
||||
<!--
|
||||
Child elements of the <create> command.
|
||||
-->
|
||||
<complexType name="createType">
|
||||
<sequence>
|
||||
<element name="name" type="eppcom:labelType"/>
|
||||
<element name="period" type="domain:periodType"
|
||||
minOccurs="0"/>
|
||||
<element name="ns" type="domain:nsType"
|
||||
minOccurs="0"/>
|
||||
<element name="registrant" type="eppcom:clIDType"
|
||||
minOccurs="0"/>
|
||||
<element name="contact" type="domain:contactType"
|
||||
minOccurs="0" maxOccurs="unbounded"/>
|
||||
<element name="authInfo" type="domain:authInfoType"/>
|
||||
</sequence>
|
||||
</complexType>
|
||||
|
||||
<complexType name="periodType">
|
||||
<simpleContent>
|
||||
<extension base="domain:pLimitType">
|
||||
<attribute name="unit" type="domain:pUnitType"
|
||||
use="required"/>
|
||||
</extension>
|
||||
</simpleContent>
|
||||
</complexType>
|
||||
|
||||
<simpleType name="pLimitType">
|
||||
<restriction base="unsignedShort">
|
||||
<minInclusive value="1"/>
|
||||
<maxInclusive value="99"/>
|
||||
</restriction>
|
||||
</simpleType>
|
||||
|
||||
<simpleType name="pUnitType">
|
||||
<restriction base="token">
|
||||
<enumeration value="y"/>
|
||||
<enumeration value="m"/>
|
||||
</restriction>
|
||||
</simpleType>
|
||||
|
||||
<complexType name="nsType">
|
||||
<choice>
|
||||
<element name="hostObj" type="eppcom:labelType"
|
||||
maxOccurs="unbounded"/>
|
||||
<element name="hostAttr" type="domain:hostAttrType"
|
||||
maxOccurs="unbounded"/>
|
||||
</choice>
|
||||
</complexType>
|
||||
<!--
|
||||
Name servers are either host objects or attributes.
|
||||
-->
|
||||
|
||||
<complexType name="hostAttrType">
|
||||
<sequence>
|
||||
<element name="hostName" type="eppcom:labelType"/>
|
||||
<element name="hostAddr" type="host:addrType"
|
||||
minOccurs="0" maxOccurs="unbounded"/>
|
||||
</sequence>
|
||||
</complexType>
|
||||
<!--
|
||||
If attributes, addresses are optional and follow the
|
||||
structure defined in the host mapping.
|
||||
-->
|
||||
|
||||
<complexType name="contactType">
|
||||
<simpleContent>
|
||||
<extension base="eppcom:clIDType">
|
||||
<attribute name="type" type="domain:contactAttrType"/>
|
||||
</extension>
|
||||
</simpleContent>
|
||||
</complexType>
|
||||
|
||||
<simpleType name="contactAttrType">
|
||||
<restriction base="token">
|
||||
<enumeration value="admin"/>
|
||||
<enumeration value="billing"/>
|
||||
<enumeration value="tech"/>
|
||||
</restriction>
|
||||
</simpleType>
|
||||
|
||||
<complexType name="authInfoType">
|
||||
<choice>
|
||||
<element name="pw" type="eppcom:pwAuthInfoType"/>
|
||||
<element name="ext" type="eppcom:extAuthInfoType"/>
|
||||
</choice>
|
||||
</complexType>
|
||||
|
||||
<!--
|
||||
Child element of commands that require a single name.
|
||||
-->
|
||||
<complexType name="sNameType">
|
||||
<sequence>
|
||||
<element name="name" type="eppcom:labelType"/>
|
||||
</sequence>
|
||||
</complexType>
|
||||
<!--
|
||||
Child element of commands that accept multiple names.
|
||||
-->
|
||||
<complexType name="mNameType">
|
||||
<sequence>
|
||||
<element name="name" type="eppcom:labelType"
|
||||
maxOccurs="unbounded"/>
|
||||
</sequence>
|
||||
</complexType>
|
||||
<!--
|
||||
Child elements of the <info> command.
|
||||
-->
|
||||
<complexType name="infoType">
|
||||
<sequence>
|
||||
<element name="name" type="domain:infoNameType"/>
|
||||
<element name="authInfo" type="domain:authInfoType"
|
||||
minOccurs="0"/>
|
||||
</sequence>
|
||||
</complexType>
|
||||
|
||||
<complexType name="infoNameType">
|
||||
<simpleContent>
|
||||
<extension base = "eppcom:labelType">
|
||||
<attribute name="hosts" type="domain:hostsType"
|
||||
default="all"/>
|
||||
</extension>
|
||||
</simpleContent>
|
||||
</complexType>
|
||||
|
||||
<simpleType name="hostsType">
|
||||
<restriction base="token">
|
||||
<enumeration value="all"/>
|
||||
<enumeration value="del"/>
|
||||
<enumeration value="none"/>
|
||||
<enumeration value="sub"/>
|
||||
</restriction>
|
||||
</simpleType>
|
||||
|
||||
<!--
|
||||
Child elements of the <renew> command.
|
||||
-->
|
||||
<complexType name="renewType">
|
||||
<sequence>
|
||||
<element name="name" type="eppcom:labelType"/>
|
||||
<element name="curExpDate" type="date"/>
|
||||
<element name="period" type="domain:periodType"
|
||||
minOccurs="0"/>
|
||||
</sequence>
|
||||
</complexType>
|
||||
|
||||
<!--
|
||||
Child elements of the <transfer> command.
|
||||
-->
|
||||
<complexType name="transferType">
|
||||
<sequence>
|
||||
<element name="name" type="eppcom:labelType"/>
|
||||
<element name="period" type="domain:periodType"
|
||||
minOccurs="0"/>
|
||||
<element name="authInfo" type="domain:authInfoType"
|
||||
minOccurs="0"/>
|
||||
</sequence>
|
||||
</complexType>
|
||||
|
||||
<!--
|
||||
Child elements of the <update> command.
|
||||
-->
|
||||
<complexType name="updateType">
|
||||
<sequence>
|
||||
<element name="name" type="eppcom:labelType"/>
|
||||
<element name="add" type="domain:addRemType"
|
||||
minOccurs="0"/>
|
||||
<element name="rem" type="domain:addRemType"
|
||||
minOccurs="0"/>
|
||||
<element name="chg" type="domain:chgType"
|
||||
minOccurs="0"/>
|
||||
</sequence>
|
||||
</complexType>
|
||||
|
||||
<!--
|
||||
Data elements that can be added or removed.
|
||||
-->
|
||||
<complexType name="addRemType">
|
||||
<sequence>
|
||||
<element name="ns" type="domain:nsType"
|
||||
minOccurs="0"/>
|
||||
<element name="contact" type="domain:contactType"
|
||||
minOccurs="0" maxOccurs="unbounded"/>
|
||||
<element name="status" type="domain:statusType"
|
||||
minOccurs="0" maxOccurs="11"/>
|
||||
</sequence>
|
||||
</complexType>
|
||||
|
||||
<!--
|
||||
Data elements that can be changed.
|
||||
-->
|
||||
<complexType name="chgType">
|
||||
<sequence>
|
||||
<element name="registrant" type="domain:clIDChgType"
|
||||
minOccurs="0"/>
|
||||
<element name="authInfo" type="domain:authInfoChgType"
|
||||
minOccurs="0"/>
|
||||
</sequence>
|
||||
</complexType>
|
||||
|
||||
<!--
|
||||
Allow the registrant value to be nullified by changing the
|
||||
minLength restriction to "0".
|
||||
-->
|
||||
<simpleType name="clIDChgType">
|
||||
<restriction base="token">
|
||||
<minLength value="0"/>
|
||||
<maxLength value="16"/>
|
||||
</restriction>
|
||||
</simpleType>
|
||||
|
||||
<!--
|
||||
Allow the authInfo value to be nullified by including an
|
||||
empty element within the choice.
|
||||
-->
|
||||
<complexType name="authInfoChgType">
|
||||
<choice>
|
||||
<element name="pw" type="eppcom:pwAuthInfoType"/>
|
||||
<element name="ext" type="eppcom:extAuthInfoType"/>
|
||||
<element name="null"/>
|
||||
</choice>
|
||||
</complexType>
|
||||
|
||||
<!--
|
||||
Child response elements.
|
||||
-->
|
||||
<element name="chkData" type="domain:chkDataType"/>
|
||||
<element name="creData" type="domain:creDataType"/>
|
||||
<element name="infData" type="domain:infDataType"/>
|
||||
<element name="panData" type="domain:panDataType"/>
|
||||
<element name="renData" type="domain:renDataType"/>
|
||||
<element name="trnData" type="domain:trnDataType"/>
|
||||
|
||||
<!--
|
||||
<check> response elements.
|
||||
-->
|
||||
<complexType name="chkDataType">
|
||||
<sequence>
|
||||
<element name="cd" type="domain:checkType"
|
||||
maxOccurs="unbounded"/>
|
||||
</sequence>
|
||||
</complexType>
|
||||
|
||||
<complexType name="checkType">
|
||||
<sequence>
|
||||
<element name="name" type="domain:checkNameType"/>
|
||||
<element name="reason" type="eppcom:reasonType"
|
||||
minOccurs="0"/>
|
||||
</sequence>
|
||||
</complexType>
|
||||
|
||||
<complexType name="checkNameType">
|
||||
<simpleContent>
|
||||
<extension base="eppcom:labelType">
|
||||
<attribute name="avail" type="boolean"
|
||||
use="required"/>
|
||||
</extension>
|
||||
</simpleContent>
|
||||
</complexType>
|
||||
|
||||
<!--
|
||||
<create> response elements.
|
||||
-->
|
||||
<complexType name="creDataType">
|
||||
<sequence>
|
||||
<element name="name" type="eppcom:labelType"/>
|
||||
<element name="crDate" type="dateTime"/>
|
||||
<element name="exDate" type="dateTime"
|
||||
minOccurs="0"/>
|
||||
</sequence>
|
||||
</complexType>
|
||||
|
||||
<!--
|
||||
<info> response elements.
|
||||
-->
|
||||
|
||||
<complexType name="infDataType">
|
||||
<sequence>
|
||||
<element name="name" type="eppcom:labelType"/>
|
||||
<element name="roid" type="eppcom:roidType"/>
|
||||
<element name="status" type="domain:statusType"
|
||||
minOccurs="0" maxOccurs="11"/>
|
||||
<element name="registrant" type="eppcom:clIDType"
|
||||
minOccurs="0"/>
|
||||
<element name="contact" type="domain:contactType"
|
||||
minOccurs="0" maxOccurs="unbounded"/>
|
||||
<element name="ns" type="domain:nsType"
|
||||
minOccurs="0"/>
|
||||
<element name="host" type="eppcom:labelType"
|
||||
minOccurs="0" maxOccurs="unbounded"/>
|
||||
<element name="clID" type="eppcom:clIDType"/>
|
||||
<element name="crID" type="eppcom:clIDType"
|
||||
minOccurs="0"/>
|
||||
<element name="crDate" type="dateTime"
|
||||
minOccurs="0"/>
|
||||
<element name="upID" type="eppcom:clIDType"
|
||||
minOccurs="0"/>
|
||||
<element name="upDate" type="dateTime"
|
||||
minOccurs="0"/>
|
||||
<element name="exDate" type="dateTime"
|
||||
minOccurs="0"/>
|
||||
<element name="trDate" type="dateTime"
|
||||
minOccurs="0"/>
|
||||
<element name="authInfo" type="domain:authInfoType"
|
||||
minOccurs="0"/>
|
||||
</sequence>
|
||||
</complexType>
|
||||
|
||||
<!--
|
||||
Status is a combination of attributes and an optional
|
||||
human-readable message that may be expressed in languages other
|
||||
than English.
|
||||
-->
|
||||
<complexType name="statusType">
|
||||
<simpleContent>
|
||||
<extension base="normalizedString">
|
||||
<attribute name="s" type="domain:statusValueType"
|
||||
use="required"/>
|
||||
<attribute name="lang" type="language"
|
||||
default="en"/>
|
||||
</extension>
|
||||
</simpleContent>
|
||||
</complexType>
|
||||
|
||||
<simpleType name="statusValueType">
|
||||
<restriction base="token">
|
||||
<enumeration value="clientDeleteProhibited"/>
|
||||
<enumeration value="clientHold"/>
|
||||
<enumeration value="clientRenewProhibited"/>
|
||||
<enumeration value="clientTransferProhibited"/>
|
||||
<enumeration value="clientUpdateProhibited"/>
|
||||
<enumeration value="inactive"/>
|
||||
<enumeration value="ok"/>
|
||||
<enumeration value="pendingCreate"/>
|
||||
<enumeration value="pendingDelete"/>
|
||||
<enumeration value="pendingRenew"/>
|
||||
<enumeration value="pendingTransfer"/>
|
||||
<enumeration value="pendingUpdate"/>
|
||||
<enumeration value="serverDeleteProhibited"/>
|
||||
<enumeration value="serverHold"/>
|
||||
<enumeration value="serverRenewProhibited"/>
|
||||
<enumeration value="serverTransferProhibited"/>
|
||||
<enumeration value="serverUpdateProhibited"/>
|
||||
</restriction>
|
||||
</simpleType>
|
||||
|
||||
<!--
|
||||
Pending action notification response elements.
|
||||
-->
|
||||
<complexType name="panDataType">
|
||||
<sequence>
|
||||
<element name="name" type="domain:paNameType"/>
|
||||
<element name="paTRID" type="epp:trIDType"/>
|
||||
<element name="paDate" type="dateTime"/>
|
||||
</sequence>
|
||||
</complexType>
|
||||
|
||||
<complexType name="paNameType">
|
||||
<simpleContent>
|
||||
<extension base="eppcom:labelType">
|
||||
<attribute name="paResult" type="boolean"
|
||||
use="required"/>
|
||||
</extension>
|
||||
</simpleContent>
|
||||
</complexType>
|
||||
|
||||
<!--
|
||||
<renew> response elements.
|
||||
-->
|
||||
<complexType name="renDataType">
|
||||
<sequence>
|
||||
<element name="name" type="eppcom:labelType"/>
|
||||
<element name="exDate" type="dateTime"
|
||||
minOccurs="0"/>
|
||||
</sequence>
|
||||
</complexType>
|
||||
|
||||
<!--
|
||||
<transfer> response elements.
|
||||
-->
|
||||
<complexType name="trnDataType">
|
||||
<sequence>
|
||||
<element name="name" type="eppcom:labelType"/>
|
||||
<element name="trStatus" type="eppcom:trStatusType"/>
|
||||
<element name="reID" type="eppcom:clIDType"/>
|
||||
<element name="reDate" type="dateTime"/>
|
||||
<element name="acID" type="eppcom:clIDType"/>
|
||||
<element name="acDate" type="dateTime"/>
|
||||
<element name="exDate" type="dateTime"
|
||||
minOccurs="0"/>
|
||||
</sequence>
|
||||
</complexType>
|
||||
|
||||
<!--
|
||||
End of schema.
|
||||
-->
|
||||
</schema>
|
|
@ -0,0 +1,292 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!-- Schema extracted from http://www.w3.org/2000/09/xmldsig# -->
|
||||
<schema xmlns="http://www.w3.org/2001/XMLSchema"
|
||||
xmlns:ds="http://www.w3.org/2000/09/xmldsig#"
|
||||
targetNamespace="http://www.w3.org/2000/09/xmldsig#"
|
||||
version="0.1" elementFormDefault="qualified">
|
||||
|
||||
<!-- Basic Types Defined for Signatures -->
|
||||
|
||||
<simpleType name="CryptoBinary">
|
||||
<restriction base="base64Binary">
|
||||
</restriction>
|
||||
</simpleType>
|
||||
|
||||
<!-- Start Signature -->
|
||||
|
||||
<element name="Signature" type="ds:SignatureType"/>
|
||||
<complexType name="SignatureType">
|
||||
<sequence>
|
||||
<element ref="ds:SignedInfo"/>
|
||||
<element ref="ds:SignatureValue"/>
|
||||
<element ref="ds:KeyInfo" minOccurs="0"/>
|
||||
<element ref="ds:Object" minOccurs="0" maxOccurs="unbounded"/>
|
||||
</sequence>
|
||||
<attribute name="Id" type="ID" use="optional"/>
|
||||
</complexType>
|
||||
|
||||
<element name="SignatureValue" type="ds:SignatureValueType"/>
|
||||
<complexType name="SignatureValueType">
|
||||
<simpleContent>
|
||||
<extension base="base64Binary">
|
||||
<attribute name="Id" type="ID" use="optional"/>
|
||||
</extension>
|
||||
</simpleContent>
|
||||
</complexType>
|
||||
|
||||
<!-- Start SignedInfo -->
|
||||
|
||||
<element name="SignedInfo" type="ds:SignedInfoType"/>
|
||||
<complexType name="SignedInfoType">
|
||||
<sequence>
|
||||
<element ref="ds:CanonicalizationMethod"/>
|
||||
<element ref="ds:SignatureMethod"/>
|
||||
<element ref="ds:Reference" maxOccurs="unbounded"/>
|
||||
</sequence>
|
||||
<attribute name="Id" type="ID" use="optional"/>
|
||||
</complexType>
|
||||
|
||||
<element name="CanonicalizationMethod" type="ds:CanonicalizationMethodType"/>
|
||||
<complexType name="CanonicalizationMethodType" mixed="true">
|
||||
<sequence>
|
||||
<any namespace="##any" minOccurs="0" maxOccurs="unbounded"/>
|
||||
<!-- (0,unbounded) elements from (1,1) namespace -->
|
||||
</sequence>
|
||||
<attribute name="Algorithm" type="anyURI" use="required"/>
|
||||
</complexType>
|
||||
|
||||
<element name="SignatureMethod" type="ds:SignatureMethodType"/>
|
||||
<complexType name="SignatureMethodType" mixed="true">
|
||||
<sequence>
|
||||
<element name="HMACOutputLength" minOccurs="0" type="ds:HMACOutputLengthType"/>
|
||||
<any namespace="##other" minOccurs="0" maxOccurs="unbounded"/>
|
||||
<!-- (0,unbounded) elements from (1,1) external namespace -->
|
||||
</sequence>
|
||||
<attribute name="Algorithm" type="anyURI" use="required"/>
|
||||
</complexType>
|
||||
|
||||
<!-- Start Reference -->
|
||||
|
||||
<element name="Reference" type="ds:ReferenceType"/>
|
||||
<complexType name="ReferenceType">
|
||||
<sequence>
|
||||
<element ref="ds:Transforms" minOccurs="0"/>
|
||||
<element ref="ds:DigestMethod"/>
|
||||
<element ref="ds:DigestValue"/>
|
||||
</sequence>
|
||||
<attribute name="Id" type="ID" use="optional"/>
|
||||
<attribute name="URI" type="anyURI" use="optional"/>
|
||||
<attribute name="Type" type="anyURI" use="optional"/>
|
||||
</complexType>
|
||||
|
||||
<element name="Transforms" type="ds:TransformsType"/>
|
||||
<complexType name="TransformsType">
|
||||
<sequence>
|
||||
<element ref="ds:Transform" maxOccurs="unbounded"/>
|
||||
</sequence>
|
||||
</complexType>
|
||||
|
||||
<element name="Transform" type="ds:TransformType"/>
|
||||
<complexType name="TransformType" mixed="true">
|
||||
<choice minOccurs="0" maxOccurs="unbounded">
|
||||
<any namespace="##other" processContents="lax"/>
|
||||
<!-- (1,1) elements from (0,unbounded) namespaces -->
|
||||
<element name="XPath" type="string"/>
|
||||
</choice>
|
||||
<attribute name="Algorithm" type="anyURI" use="required"/>
|
||||
</complexType>
|
||||
|
||||
<!-- End Reference -->
|
||||
|
||||
<element name="DigestMethod" type="ds:DigestMethodType"/>
|
||||
<complexType name="DigestMethodType" mixed="true">
|
||||
<sequence>
|
||||
<any namespace="##other" processContents="lax" minOccurs="0" maxOccurs="unbounded"/>
|
||||
</sequence>
|
||||
<attribute name="Algorithm" type="anyURI" use="required"/>
|
||||
</complexType>
|
||||
|
||||
<element name="DigestValue" type="ds:DigestValueType"/>
|
||||
<simpleType name="DigestValueType">
|
||||
<restriction base="base64Binary"/>
|
||||
</simpleType>
|
||||
|
||||
<!-- End SignedInfo -->
|
||||
|
||||
<!-- Start KeyInfo -->
|
||||
|
||||
<element name="KeyInfo" type="ds:KeyInfoType"/>
|
||||
<complexType name="KeyInfoType" mixed="true">
|
||||
<choice maxOccurs="unbounded">
|
||||
<element ref="ds:KeyName"/>
|
||||
<element ref="ds:KeyValue"/>
|
||||
<element ref="ds:RetrievalMethod"/>
|
||||
<element ref="ds:X509Data"/>
|
||||
<element ref="ds:PGPData"/>
|
||||
<element ref="ds:SPKIData"/>
|
||||
<element ref="ds:MgmtData"/>
|
||||
<any processContents="lax" namespace="##other"/>
|
||||
<!-- (1,1) elements from (0,unbounded) namespaces -->
|
||||
</choice>
|
||||
<attribute name="Id" type="ID" use="optional"/>
|
||||
</complexType>
|
||||
|
||||
<element name="KeyName" type="string"/>
|
||||
<element name="MgmtData" type="string"/>
|
||||
|
||||
<element name="KeyValue" type="ds:KeyValueType"/>
|
||||
<complexType name="KeyValueType" mixed="true">
|
||||
<choice>
|
||||
<element ref="ds:DSAKeyValue"/>
|
||||
<element ref="ds:RSAKeyValue"/>
|
||||
<any namespace="##other" processContents="lax"/>
|
||||
</choice>
|
||||
</complexType>
|
||||
|
||||
<element name="RetrievalMethod" type="ds:RetrievalMethodType"/>
|
||||
<complexType name="RetrievalMethodType">
|
||||
<sequence>
|
||||
<element ref="ds:Transforms" minOccurs="0"/>
|
||||
</sequence>
|
||||
<attribute name="URI" type="anyURI"/>
|
||||
<attribute name="Type" type="anyURI" use="optional"/>
|
||||
</complexType>
|
||||
|
||||
<!-- Start X509Data -->
|
||||
|
||||
<element name="X509Data" type="ds:X509DataType"/>
|
||||
<complexType name="X509DataType">
|
||||
<sequence maxOccurs="unbounded">
|
||||
<choice>
|
||||
<element name="X509IssuerSerial" type="ds:X509IssuerSerialType"/>
|
||||
<element name="X509SKI" type="base64Binary"/>
|
||||
<element name="X509SubjectName" type="string"/>
|
||||
<element name="X509Certificate" type="base64Binary"/>
|
||||
<element name="X509CRL" type="base64Binary"/>
|
||||
<any namespace="##other" processContents="lax"/>
|
||||
</choice>
|
||||
</sequence>
|
||||
</complexType>
|
||||
|
||||
<complexType name="X509IssuerSerialType">
|
||||
<sequence>
|
||||
<element name="X509IssuerName" type="string"/>
|
||||
<element name="X509SerialNumber" type="integer"/>
|
||||
</sequence>
|
||||
</complexType>
|
||||
|
||||
<!-- End X509Data -->
|
||||
|
||||
<!-- Begin PGPData -->
|
||||
|
||||
<element name="PGPData" type="ds:PGPDataType"/>
|
||||
<complexType name="PGPDataType">
|
||||
<choice>
|
||||
<sequence>
|
||||
<element name="PGPKeyID" type="base64Binary"/>
|
||||
<element name="PGPKeyPacket" type="base64Binary" minOccurs="0"/>
|
||||
<any namespace="##other" processContents="lax" minOccurs="0"
|
||||
maxOccurs="unbounded"/>
|
||||
</sequence>
|
||||
<sequence>
|
||||
<element name="PGPKeyPacket" type="base64Binary"/>
|
||||
<any namespace="##other" processContents="lax" minOccurs="0"
|
||||
maxOccurs="unbounded"/>
|
||||
</sequence>
|
||||
</choice>
|
||||
</complexType>
|
||||
|
||||
<!-- End PGPData -->
|
||||
|
||||
<!-- Begin SPKIData -->
|
||||
|
||||
<element name="SPKIData" type="ds:SPKIDataType"/>
|
||||
<complexType name="SPKIDataType">
|
||||
<sequence maxOccurs="unbounded">
|
||||
<element name="SPKISexp" type="base64Binary"/>
|
||||
<any namespace="##other" processContents="lax" minOccurs="0"/>
|
||||
</sequence>
|
||||
</complexType>
|
||||
|
||||
<!-- End SPKIData -->
|
||||
|
||||
<!-- End KeyInfo -->
|
||||
|
||||
<!-- Start Object (Manifest, SignatureProperty) -->
|
||||
|
||||
<element name="Object" type="ds:ObjectType"/>
|
||||
<complexType name="ObjectType" mixed="true">
|
||||
<sequence minOccurs="0" maxOccurs="unbounded">
|
||||
<any namespace="##any" processContents="lax"/>
|
||||
</sequence>
|
||||
<attribute name="Id" type="ID" use="optional"/>
|
||||
<attribute name="MimeType" type="string" use="optional"/> <!-- add a grep facet -->
|
||||
<attribute name="Encoding" type="anyURI" use="optional"/>
|
||||
</complexType>
|
||||
|
||||
<element name="Manifest" type="ds:ManifestType"/>
|
||||
<complexType name="ManifestType">
|
||||
<sequence>
|
||||
<element ref="ds:Reference" maxOccurs="unbounded"/>
|
||||
</sequence>
|
||||
<attribute name="Id" type="ID" use="optional"/>
|
||||
</complexType>
|
||||
|
||||
<element name="SignatureProperties" type="ds:SignaturePropertiesType"/>
|
||||
<complexType name="SignaturePropertiesType">
|
||||
<sequence>
|
||||
<element ref="ds:SignatureProperty" maxOccurs="unbounded"/>
|
||||
</sequence>
|
||||
<attribute name="Id" type="ID" use="optional"/>
|
||||
</complexType>
|
||||
|
||||
<element name="SignatureProperty" type="ds:SignaturePropertyType"/>
|
||||
<complexType name="SignaturePropertyType" mixed="true">
|
||||
<choice maxOccurs="unbounded">
|
||||
<any namespace="##other" processContents="lax"/>
|
||||
<!-- (1,1) elements from (1,unbounded) namespaces -->
|
||||
</choice>
|
||||
<attribute name="Target" type="anyURI" use="required"/>
|
||||
<attribute name="Id" type="ID" use="optional"/>
|
||||
</complexType>
|
||||
|
||||
<!-- End Object (Manifest, SignatureProperty) -->
|
||||
|
||||
<!-- Start Algorithm Parameters -->
|
||||
|
||||
<simpleType name="HMACOutputLengthType">
|
||||
<restriction base="integer"/>
|
||||
</simpleType>
|
||||
|
||||
<!-- Start KeyValue Element-types -->
|
||||
|
||||
<element name="DSAKeyValue" type="ds:DSAKeyValueType"/>
|
||||
<complexType name="DSAKeyValueType">
|
||||
<sequence>
|
||||
<sequence minOccurs="0">
|
||||
<element name="P" type="ds:CryptoBinary"/>
|
||||
<element name="Q" type="ds:CryptoBinary"/>
|
||||
</sequence>
|
||||
<element name="G" type="ds:CryptoBinary" minOccurs="0"/>
|
||||
<element name="Y" type="ds:CryptoBinary"/>
|
||||
<element name="J" type="ds:CryptoBinary" minOccurs="0"/>
|
||||
<sequence minOccurs="0">
|
||||
<element name="Seed" type="ds:CryptoBinary"/>
|
||||
<element name="PgenCounter" type="ds:CryptoBinary"/>
|
||||
</sequence>
|
||||
</sequence>
|
||||
</complexType>
|
||||
|
||||
<element name="RSAKeyValue" type="ds:RSAKeyValueType"/>
|
||||
<complexType name="RSAKeyValueType">
|
||||
<sequence>
|
||||
<element name="Modulus" type="ds:CryptoBinary"/>
|
||||
<element name="Exponent" type="ds:CryptoBinary"/>
|
||||
</sequence>
|
||||
</complexType>
|
||||
|
||||
<!-- End KeyValue Element-types -->
|
||||
|
||||
<!-- End Signature -->
|
||||
|
||||
</schema>
|
|
@ -0,0 +1,445 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<schema targetNamespace="urn:ietf:params:xml:ns:epp-1.0"
|
||||
xmlns:epp="urn:ietf:params:xml:ns:epp-1.0"
|
||||
xmlns:eppcom="urn:ietf:params:xml:ns:eppcom-1.0"
|
||||
xmlns="http://www.w3.org/2001/XMLSchema"
|
||||
elementFormDefault="qualified">
|
||||
|
||||
<!--
|
||||
Import common element types.
|
||||
-->
|
||||
<import namespace="urn:ietf:params:xml:ns:eppcom-1.0"
|
||||
schemaLocation="eppcom.xsd"/>
|
||||
|
||||
<annotation>
|
||||
<documentation>
|
||||
Extensible Provisioning Protocol v1.0 schema.
|
||||
</documentation>
|
||||
</annotation>
|
||||
|
||||
<!--
|
||||
Every EPP XML instance must begin with this element.
|
||||
-->
|
||||
<element name="epp" type="epp:eppType"/>
|
||||
|
||||
<!--
|
||||
An EPP XML instance must contain a greeting, hello, command,
|
||||
response, or extension.
|
||||
-->
|
||||
<complexType name="eppType">
|
||||
<choice>
|
||||
<element name="greeting" type="epp:greetingType"/>
|
||||
<element name="hello"/>
|
||||
<element name="command" type="epp:commandType"/>
|
||||
<element name="response" type="epp:responseType"/>
|
||||
<element name="extension" type="epp:extAnyType"/>
|
||||
</choice>
|
||||
</complexType>
|
||||
|
||||
<!--
|
||||
A greeting is sent by a server in response to a client connection
|
||||
or <hello>.
|
||||
-->
|
||||
<complexType name="greetingType">
|
||||
<sequence>
|
||||
<element name="svID" type="epp:sIDType"/>
|
||||
<element name="svDate" type="dateTime"/>
|
||||
<element name="svcMenu" type="epp:svcMenuType"/>
|
||||
<element name="dcp" type="epp:dcpType"/>
|
||||
</sequence>
|
||||
</complexType>
|
||||
|
||||
<!--
|
||||
Server IDs are strings with minimum and maximum length restrictions.
|
||||
-->
|
||||
<simpleType name="sIDType">
|
||||
<restriction base="normalizedString">
|
||||
<minLength value="3"/>
|
||||
<maxLength value="64"/>
|
||||
</restriction>
|
||||
</simpleType>
|
||||
|
||||
<!--
|
||||
A server greeting identifies available object services.
|
||||
-->
|
||||
<complexType name="svcMenuType">
|
||||
<sequence>
|
||||
<element name="version" type="epp:versionType"
|
||||
maxOccurs="unbounded"/>
|
||||
<element name="lang" type="language"
|
||||
maxOccurs="unbounded"/>
|
||||
<element name="objURI" type="anyURI"
|
||||
maxOccurs="unbounded"/>
|
||||
<element name="svcExtension" type="epp:extURIType"
|
||||
minOccurs="0"/>
|
||||
</sequence>
|
||||
</complexType>
|
||||
|
||||
<!--
|
||||
Data Collection Policy types.
|
||||
-->
|
||||
<complexType name="dcpType">
|
||||
<sequence>
|
||||
<element name="access" type="epp:dcpAccessType"/>
|
||||
<element name="statement" type="epp:dcpStatementType"
|
||||
maxOccurs="unbounded"/>
|
||||
<element name="expiry" type="epp:dcpExpiryType"
|
||||
minOccurs="0"/>
|
||||
</sequence>
|
||||
</complexType>
|
||||
|
||||
<complexType name="dcpAccessType">
|
||||
<choice>
|
||||
<element name="all"/>
|
||||
<element name="none"/>
|
||||
<element name="null"/>
|
||||
<element name="other"/>
|
||||
<element name="personal"/>
|
||||
<element name="personalAndOther"/>
|
||||
</choice>
|
||||
</complexType>
|
||||
|
||||
<complexType name="dcpStatementType">
|
||||
<sequence>
|
||||
<element name="purpose" type="epp:dcpPurposeType"/>
|
||||
<element name="recipient" type="epp:dcpRecipientType"/>
|
||||
<element name="retention" type="epp:dcpRetentionType"/>
|
||||
</sequence>
|
||||
</complexType>
|
||||
|
||||
<complexType name="dcpPurposeType">
|
||||
<sequence>
|
||||
<element name="admin"
|
||||
minOccurs="0"/>
|
||||
<element name="contact"
|
||||
minOccurs="0"/>
|
||||
<element name="other"
|
||||
minOccurs="0"/>
|
||||
<element name="prov"
|
||||
minOccurs="0"/>
|
||||
</sequence>
|
||||
</complexType>
|
||||
|
||||
<complexType name="dcpRecipientType">
|
||||
<sequence>
|
||||
<element name="other"
|
||||
minOccurs="0"/>
|
||||
<element name="ours" type="epp:dcpOursType"
|
||||
minOccurs="0" maxOccurs="unbounded"/>
|
||||
<element name="public"
|
||||
minOccurs="0"/>
|
||||
<element name="same"
|
||||
minOccurs="0"/>
|
||||
<element name="unrelated"
|
||||
minOccurs="0"/>
|
||||
</sequence>
|
||||
</complexType>
|
||||
|
||||
<complexType name="dcpOursType">
|
||||
<sequence>
|
||||
<element name="recDesc" type="epp:dcpRecDescType"
|
||||
minOccurs="0"/>
|
||||
</sequence>
|
||||
</complexType>
|
||||
|
||||
<simpleType name="dcpRecDescType">
|
||||
<restriction base="token">
|
||||
<minLength value="1"/>
|
||||
<maxLength value="255"/>
|
||||
</restriction>
|
||||
</simpleType>
|
||||
|
||||
<complexType name="dcpRetentionType">
|
||||
<choice>
|
||||
<element name="business"/>
|
||||
<element name="indefinite"/>
|
||||
<element name="legal"/>
|
||||
<element name="none"/>
|
||||
<element name="stated"/>
|
||||
</choice>
|
||||
</complexType>
|
||||
|
||||
<complexType name="dcpExpiryType">
|
||||
<choice>
|
||||
<element name="absolute" type="dateTime"/>
|
||||
<element name="relative" type="duration"/>
|
||||
</choice>
|
||||
</complexType>
|
||||
|
||||
<!--
|
||||
Extension framework types.
|
||||
-->
|
||||
<complexType name="extAnyType">
|
||||
<sequence>
|
||||
<any namespace="##other"
|
||||
maxOccurs="unbounded"/>
|
||||
</sequence>
|
||||
</complexType>
|
||||
|
||||
<complexType name="extURIType">
|
||||
<sequence>
|
||||
<element name="extURI" type="anyURI"
|
||||
maxOccurs="unbounded"/>
|
||||
</sequence>
|
||||
</complexType>
|
||||
|
||||
<!--
|
||||
An EPP version number is a dotted pair of decimal numbers.
|
||||
-->
|
||||
<simpleType name="versionType">
|
||||
<restriction base="token">
|
||||
<pattern value="[1-9]+\.[0-9]+"/>
|
||||
<enumeration value="1.0"/>
|
||||
</restriction>
|
||||
</simpleType>
|
||||
|
||||
<!--
|
||||
Command types.
|
||||
-->
|
||||
<complexType name="commandType">
|
||||
<sequence>
|
||||
<choice>
|
||||
<element name="check" type="epp:readWriteType"/>
|
||||
<element name="create" type="epp:readWriteType"/>
|
||||
<element name="delete" type="epp:readWriteType"/>
|
||||
<element name="info" type="epp:readWriteType"/>
|
||||
<element name="login" type="epp:loginType"/>
|
||||
<element name="logout"/>
|
||||
<element name="poll" type="epp:pollType"/>
|
||||
<element name="renew" type="epp:readWriteType"/>
|
||||
<element name="transfer" type="epp:transferType"/>
|
||||
<element name="update" type="epp:readWriteType"/>
|
||||
</choice>
|
||||
<element name="extension" type="epp:extAnyType"
|
||||
minOccurs="0"/>
|
||||
<element name="clTRID" type="epp:trIDStringType"
|
||||
minOccurs="0"/>
|
||||
</sequence>
|
||||
</complexType>
|
||||
|
||||
<!--
|
||||
The <login> command.
|
||||
-->
|
||||
<complexType name="loginType">
|
||||
<sequence>
|
||||
<element name="clID" type="eppcom:clIDType"/>
|
||||
<element name="pw" type="epp:pwType"/>
|
||||
<element name="newPW" type="epp:pwType"
|
||||
minOccurs="0"/>
|
||||
<element name="options" type="epp:credsOptionsType"/>
|
||||
<element name="svcs" type="epp:loginSvcType"/>
|
||||
</sequence>
|
||||
</complexType>
|
||||
|
||||
<complexType name="credsOptionsType">
|
||||
<sequence>
|
||||
<element name="version" type="epp:versionType"/>
|
||||
<element name="lang" type="language"/>
|
||||
</sequence>
|
||||
</complexType>
|
||||
|
||||
<simpleType name="pwType">
|
||||
<restriction base="token">
|
||||
<minLength value="6"/>
|
||||
<maxLength value="16"/>
|
||||
</restriction>
|
||||
</simpleType>
|
||||
|
||||
<complexType name="loginSvcType">
|
||||
<sequence>
|
||||
<element name="objURI" type="anyURI"
|
||||
maxOccurs="unbounded"/>
|
||||
<element name="svcExtension" type="epp:extURIType"
|
||||
minOccurs="0"/>
|
||||
</sequence>
|
||||
</complexType>
|
||||
|
||||
<!--
|
||||
The <poll> command.
|
||||
-->
|
||||
<complexType name="pollType">
|
||||
<attribute name="op" type="epp:pollOpType"
|
||||
use="required"/>
|
||||
<attribute name="msgID" type="token"/>
|
||||
</complexType>
|
||||
|
||||
<simpleType name="pollOpType">
|
||||
<restriction base="token">
|
||||
<enumeration value="ack"/>
|
||||
<enumeration value="req"/>
|
||||
</restriction>
|
||||
</simpleType>
|
||||
|
||||
<!--
|
||||
The <transfer> command. This is object-specific, and uses attributes
|
||||
to identify the requested operation.
|
||||
-->
|
||||
<complexType name="transferType">
|
||||
<sequence>
|
||||
<any namespace="##other"/>
|
||||
</sequence>
|
||||
<attribute name="op" type="epp:transferOpType"
|
||||
use="required"/>
|
||||
</complexType>
|
||||
|
||||
<simpleType name="transferOpType">
|
||||
<restriction base="token">
|
||||
<enumeration value="approve"/>
|
||||
<enumeration value="cancel"/>
|
||||
<enumeration value="query"/>
|
||||
<enumeration value="reject"/>
|
||||
<enumeration value="request"/>
|
||||
</restriction>
|
||||
</simpleType>
|
||||
|
||||
<!--
|
||||
All other object-centric commands. EPP doesn't specify the syntax or
|
||||
semantics of object-centric command elements. The elements MUST be
|
||||
described in detail in another schema specific to the object.
|
||||
-->
|
||||
<complexType name="readWriteType">
|
||||
<sequence>
|
||||
<any namespace="##other"/>
|
||||
</sequence>
|
||||
</complexType>
|
||||
|
||||
<complexType name="trIDType">
|
||||
<sequence>
|
||||
<element name="clTRID" type="epp:trIDStringType"
|
||||
minOccurs="0"/>
|
||||
<element name="svTRID" type="epp:trIDStringType"/>
|
||||
</sequence>
|
||||
</complexType>
|
||||
|
||||
<simpleType name="trIDStringType">
|
||||
<restriction base="token">
|
||||
<minLength value="3"/>
|
||||
<maxLength value="64"/>
|
||||
</restriction>
|
||||
</simpleType>
|
||||
|
||||
<!--
|
||||
Response types.
|
||||
-->
|
||||
<complexType name="responseType">
|
||||
<sequence>
|
||||
<element name="result" type="epp:resultType"
|
||||
maxOccurs="unbounded"/>
|
||||
<element name="msgQ" type="epp:msgQType"
|
||||
minOccurs="0"/>
|
||||
|
||||
<element name="resData" type="epp:extAnyType"
|
||||
minOccurs="0"/>
|
||||
<element name="extension" type="epp:extAnyType"
|
||||
minOccurs="0"/>
|
||||
<element name="trID" type="epp:trIDType"/>
|
||||
</sequence>
|
||||
</complexType>
|
||||
|
||||
<complexType name="resultType">
|
||||
<sequence>
|
||||
<element name="msg" type="epp:msgType"/>
|
||||
<choice minOccurs="0" maxOccurs="unbounded">
|
||||
<element name="value" type="epp:errValueType"/>
|
||||
<element name="extValue" type="epp:extErrValueType"/>
|
||||
</choice>
|
||||
</sequence>
|
||||
<attribute name="code" type="epp:resultCodeType"
|
||||
use="required"/>
|
||||
</complexType>
|
||||
|
||||
<complexType name="errValueType" mixed="true">
|
||||
<sequence>
|
||||
<any namespace="##any" processContents="skip"/>
|
||||
</sequence>
|
||||
<anyAttribute namespace="##any" processContents="skip"/>
|
||||
</complexType>
|
||||
|
||||
<complexType name="extErrValueType">
|
||||
<sequence>
|
||||
<element name="value" type="epp:errValueType"/>
|
||||
<element name="reason" type="epp:msgType"/>
|
||||
</sequence>
|
||||
</complexType>
|
||||
|
||||
<complexType name="msgQType">
|
||||
<sequence>
|
||||
<element name="qDate" type="dateTime"
|
||||
minOccurs="0"/>
|
||||
<element name="msg" type="epp:mixedMsgType"
|
||||
minOccurs="0"/>
|
||||
</sequence>
|
||||
<attribute name="count" type="unsignedLong"
|
||||
use="required"/>
|
||||
<attribute name="id" type="eppcom:minTokenType"
|
||||
use="required"/>
|
||||
</complexType>
|
||||
|
||||
<complexType name="mixedMsgType" mixed="true">
|
||||
<sequence>
|
||||
<any processContents="skip"
|
||||
minOccurs="0" maxOccurs="unbounded"/>
|
||||
</sequence>
|
||||
<attribute name="lang" type="language"
|
||||
default="en"/>
|
||||
</complexType>
|
||||
|
||||
<!--
|
||||
Human-readable text may be expressed in languages other than English.
|
||||
-->
|
||||
<complexType name="msgType">
|
||||
<simpleContent>
|
||||
<extension base="normalizedString">
|
||||
<attribute name="lang" type="language"
|
||||
default="en"/>
|
||||
</extension>
|
||||
</simpleContent>
|
||||
</complexType>
|
||||
|
||||
<!--
|
||||
EPP result codes.
|
||||
-->
|
||||
<simpleType name="resultCodeType">
|
||||
<restriction base="unsignedShort">
|
||||
<enumeration value="1000"/>
|
||||
<enumeration value="1001"/>
|
||||
<enumeration value="1300"/>
|
||||
<enumeration value="1301"/>
|
||||
<enumeration value="1500"/>
|
||||
<enumeration value="2000"/>
|
||||
<enumeration value="2001"/>
|
||||
<enumeration value="2002"/>
|
||||
<enumeration value="2003"/>
|
||||
<enumeration value="2004"/>
|
||||
<enumeration value="2005"/>
|
||||
<enumeration value="2100"/>
|
||||
<enumeration value="2101"/>
|
||||
<enumeration value="2102"/>
|
||||
<enumeration value="2103"/>
|
||||
<enumeration value="2104"/>
|
||||
<enumeration value="2105"/>
|
||||
<enumeration value="2106"/>
|
||||
<enumeration value="2200"/>
|
||||
<enumeration value="2201"/>
|
||||
<enumeration value="2202"/>
|
||||
<enumeration value="2300"/>
|
||||
<enumeration value="2301"/>
|
||||
<enumeration value="2302"/>
|
||||
<enumeration value="2303"/>
|
||||
<enumeration value="2304"/>
|
||||
<enumeration value="2305"/>
|
||||
<enumeration value="2306"/>
|
||||
<enumeration value="2307"/>
|
||||
<enumeration value="2308"/>
|
||||
<enumeration value="2400"/>
|
||||
<enumeration value="2500"/>
|
||||
<enumeration value="2501"/>
|
||||
<enumeration value="2502"/>
|
||||
</restriction>
|
||||
</simpleType>
|
||||
|
||||
<!--
|
||||
End of schema.
|
||||
-->
|
||||
</schema>
|
|
@ -0,0 +1,107 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<schema targetNamespace="urn:ietf:params:xml:ns:eppcom-1.0"
|
||||
xmlns:eppcom="urn:ietf:params:xml:ns:eppcom-1.0"
|
||||
xmlns="http://www.w3.org/2001/XMLSchema"
|
||||
elementFormDefault="qualified">
|
||||
|
||||
<annotation>
|
||||
<documentation>
|
||||
Extensible Provisioning Protocol v1.0
|
||||
shared structures schema.
|
||||
</documentation>
|
||||
</annotation>
|
||||
|
||||
<!--
|
||||
Object authorization information types.
|
||||
-->
|
||||
<complexType name="pwAuthInfoType">
|
||||
<simpleContent>
|
||||
<extension base="normalizedString">
|
||||
<attribute name="roid" type="eppcom:roidType"/>
|
||||
</extension>
|
||||
</simpleContent>
|
||||
</complexType>
|
||||
|
||||
<complexType name="extAuthInfoType">
|
||||
<sequence>
|
||||
<any namespace="##other"/>
|
||||
</sequence>
|
||||
</complexType>
|
||||
|
||||
<!--
|
||||
<check> response types.
|
||||
-->
|
||||
<complexType name="reasonType">
|
||||
<simpleContent>
|
||||
<extension base="eppcom:reasonBaseType">
|
||||
<attribute name="lang" type="language"/>
|
||||
</extension>
|
||||
</simpleContent>
|
||||
</complexType>
|
||||
|
||||
<simpleType name="reasonBaseType">
|
||||
<restriction base="token">
|
||||
<minLength value="1"/>
|
||||
<maxLength value="32"/>
|
||||
</restriction>
|
||||
</simpleType>
|
||||
|
||||
<!--
|
||||
Abstract client and object identifier type.
|
||||
-->
|
||||
<simpleType name="clIDType">
|
||||
<restriction base="token">
|
||||
<minLength value="3"/>
|
||||
<maxLength value="16"/>
|
||||
</restriction>
|
||||
</simpleType>
|
||||
|
||||
<!--
|
||||
DNS label type.
|
||||
-->
|
||||
|
||||
<simpleType name="labelType">
|
||||
<restriction base="token">
|
||||
<minLength value="1"/>
|
||||
<maxLength value="255"/>
|
||||
</restriction>
|
||||
</simpleType>
|
||||
|
||||
<!--
|
||||
Non-empty token type.
|
||||
-->
|
||||
<simpleType name="minTokenType">
|
||||
<restriction base="token">
|
||||
<minLength value="1"/>
|
||||
</restriction>
|
||||
</simpleType>
|
||||
|
||||
<!--
|
||||
Repository Object IDentifier type.
|
||||
-->
|
||||
<simpleType name="roidType">
|
||||
<restriction base="token">
|
||||
<pattern value="(\w|_){1,80}-\w{1,8}"/>
|
||||
</restriction>
|
||||
</simpleType>
|
||||
|
||||
<!--
|
||||
Transfer status identifiers.
|
||||
-->
|
||||
|
||||
<simpleType name="trStatusType">
|
||||
<restriction base="token">
|
||||
<enumeration value="clientApproved"/>
|
||||
<enumeration value="clientCancelled"/>
|
||||
<enumeration value="clientRejected"/>
|
||||
<enumeration value="pending"/>
|
||||
<enumeration value="serverApproved"/>
|
||||
<enumeration value="serverCancelled"/>
|
||||
</restriction>
|
||||
</simpleType>
|
||||
|
||||
<!--
|
||||
End of schema.
|
||||
-->
|
||||
</schema>
|
|
@ -0,0 +1,242 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<schema targetNamespace="urn:ietf:params:xml:ns:host-1.0"
|
||||
xmlns:host="urn:ietf:params:xml:ns:host-1.0"
|
||||
xmlns:epp="urn:ietf:params:xml:ns:epp-1.0"
|
||||
xmlns:eppcom="urn:ietf:params:xml:ns:eppcom-1.0"
|
||||
xmlns="http://www.w3.org/2001/XMLSchema"
|
||||
elementFormDefault="qualified">
|
||||
|
||||
<!--
|
||||
Import common element types.
|
||||
-->
|
||||
<import namespace="urn:ietf:params:xml:ns:eppcom-1.0"
|
||||
schemaLocation="eppcom.xsd"/>
|
||||
<import namespace="urn:ietf:params:xml:ns:epp-1.0"
|
||||
schemaLocation="epp.xsd"/>
|
||||
|
||||
<annotation>
|
||||
<documentation>
|
||||
Extensible Provisioning Protocol v1.0
|
||||
host provisioning schema.
|
||||
</documentation>
|
||||
</annotation>
|
||||
|
||||
<!--
|
||||
Child elements found in EPP commands.
|
||||
-->
|
||||
<element name="check" type="host:mNameType"/>
|
||||
<element name="create" type="host:createType"/>
|
||||
<element name="delete" type="host:sNameType"/>
|
||||
<element name="info" type="host:sNameType"/>
|
||||
<element name="update" type="host:updateType"/>
|
||||
|
||||
<!--
|
||||
Child elements of the <create> command.
|
||||
-->
|
||||
<complexType name="createType">
|
||||
<sequence>
|
||||
<element name="name" type="eppcom:labelType"/>
|
||||
<element name="addr" type="host:addrType"
|
||||
minOccurs="0" maxOccurs="unbounded"/>
|
||||
</sequence>
|
||||
</complexType>
|
||||
|
||||
<complexType name="addrType">
|
||||
<simpleContent>
|
||||
<extension base="host:addrStringType">
|
||||
<attribute name="ip" type="host:ipType"
|
||||
default="v4"/>
|
||||
</extension>
|
||||
</simpleContent>
|
||||
</complexType>
|
||||
|
||||
<simpleType name="addrStringType">
|
||||
<restriction base="token">
|
||||
<minLength value="3"/>
|
||||
<maxLength value="45"/>
|
||||
</restriction>
|
||||
</simpleType>
|
||||
|
||||
<simpleType name="ipType">
|
||||
<restriction base="token">
|
||||
<enumeration value="v4"/>
|
||||
<enumeration value="v6"/>
|
||||
</restriction>
|
||||
</simpleType>
|
||||
|
||||
<!--
|
||||
Child elements of the <delete> and <info> commands.
|
||||
-->
|
||||
<complexType name="sNameType">
|
||||
<sequence>
|
||||
<element name="name" type="eppcom:labelType"/>
|
||||
</sequence>
|
||||
</complexType>
|
||||
|
||||
<!--
|
||||
Child element of commands that accept multiple names.
|
||||
-->
|
||||
<complexType name="mNameType">
|
||||
<sequence>
|
||||
<element name="name" type="eppcom:labelType"
|
||||
maxOccurs="unbounded"/>
|
||||
</sequence>
|
||||
</complexType>
|
||||
<!--
|
||||
Child elements of the <update> command.
|
||||
-->
|
||||
<complexType name="updateType">
|
||||
<sequence>
|
||||
<element name="name" type="eppcom:labelType"/>
|
||||
<element name="add" type="host:addRemType"
|
||||
minOccurs="0"/>
|
||||
<element name="rem" type="host:addRemType"
|
||||
minOccurs="0"/>
|
||||
<element name="chg" type="host:chgType"
|
||||
minOccurs="0"/>
|
||||
</sequence>
|
||||
</complexType>
|
||||
|
||||
<!--
|
||||
Data elements that can be added or removed.
|
||||
-->
|
||||
<complexType name="addRemType">
|
||||
<sequence>
|
||||
<element name="addr" type="host:addrType"
|
||||
minOccurs="0" maxOccurs="unbounded"/>
|
||||
<element name="status" type="host:statusType"
|
||||
minOccurs="0" maxOccurs="7"/>
|
||||
</sequence>
|
||||
</complexType>
|
||||
|
||||
<!--
|
||||
Data elements that can be changed.
|
||||
-->
|
||||
<complexType name="chgType">
|
||||
<sequence>
|
||||
<element name="name" type="eppcom:labelType"/>
|
||||
</sequence>
|
||||
</complexType>
|
||||
|
||||
<!--
|
||||
Child response elements.
|
||||
-->
|
||||
<element name="chkData" type="host:chkDataType"/>
|
||||
<element name="creData" type="host:creDataType"/>
|
||||
<element name="infData" type="host:infDataType"/>
|
||||
<element name="panData" type="host:panDataType"/>
|
||||
|
||||
<!--
|
||||
<check> response elements.
|
||||
-->
|
||||
<complexType name="chkDataType">
|
||||
<sequence>
|
||||
<element name="cd" type="host:checkType"
|
||||
maxOccurs="unbounded"/>
|
||||
</sequence>
|
||||
</complexType>
|
||||
|
||||
<complexType name="checkType">
|
||||
<sequence>
|
||||
<element name="name" type="host:checkNameType"/>
|
||||
<element name="reason" type="eppcom:reasonType"
|
||||
minOccurs="0"/>
|
||||
</sequence>
|
||||
</complexType>
|
||||
|
||||
<complexType name="checkNameType">
|
||||
<simpleContent>
|
||||
<extension base="eppcom:labelType">
|
||||
<attribute name="avail" type="boolean"
|
||||
use="required"/>
|
||||
</extension>
|
||||
</simpleContent>
|
||||
</complexType>
|
||||
|
||||
<!--
|
||||
<create> response elements.
|
||||
-->
|
||||
<complexType name="creDataType">
|
||||
<sequence>
|
||||
<element name="name" type="eppcom:labelType"/>
|
||||
<element name="crDate" type="dateTime"/>
|
||||
</sequence>
|
||||
</complexType>
|
||||
|
||||
<!--
|
||||
<info> response elements.
|
||||
-->
|
||||
<complexType name="infDataType">
|
||||
<sequence>
|
||||
<element name="name" type="eppcom:labelType"/>
|
||||
<element name="roid" type="eppcom:roidType"/>
|
||||
<element name="status" type="host:statusType"
|
||||
maxOccurs="7"/>
|
||||
<element name="addr" type="host:addrType"
|
||||
minOccurs="0" maxOccurs="unbounded"/>
|
||||
<element name="clID" type="eppcom:clIDType"/>
|
||||
<element name="crID" type="eppcom:clIDType"/>
|
||||
<element name="crDate" type="dateTime"/>
|
||||
<element name="upID" type="eppcom:clIDType"
|
||||
minOccurs="0"/>
|
||||
<element name="upDate" type="dateTime"
|
||||
minOccurs="0"/>
|
||||
<element name="trDate" type="dateTime"
|
||||
minOccurs="0"/>
|
||||
</sequence>
|
||||
</complexType>
|
||||
|
||||
<!--
|
||||
Status is a combination of attributes and an optional human-readable
|
||||
message that may be expressed in languages other than English.
|
||||
-->
|
||||
<complexType name="statusType">
|
||||
<simpleContent>
|
||||
<extension base="normalizedString">
|
||||
<attribute name="s" type="host:statusValueType"
|
||||
use="required"/>
|
||||
<attribute name="lang" type="language"
|
||||
default="en"/>
|
||||
</extension>
|
||||
</simpleContent>
|
||||
</complexType>
|
||||
|
||||
<simpleType name="statusValueType">
|
||||
<restriction base="token">
|
||||
<enumeration value="clientDeleteProhibited"/>
|
||||
<enumeration value="clientUpdateProhibited"/>
|
||||
<enumeration value="linked"/>
|
||||
<enumeration value="ok"/>
|
||||
<enumeration value="pendingCreate"/>
|
||||
<enumeration value="pendingDelete"/>
|
||||
<enumeration value="pendingTransfer"/>
|
||||
<enumeration value="pendingUpdate"/>
|
||||
<enumeration value="serverDeleteProhibited"/>
|
||||
<enumeration value="serverUpdateProhibited"/>
|
||||
</restriction>
|
||||
</simpleType>
|
||||
|
||||
<!--
|
||||
Pending action notification response elements.
|
||||
-->
|
||||
<complexType name="panDataType">
|
||||
<sequence>
|
||||
<element name="name" type="host:paNameType"/>
|
||||
<element name="paTRID" type="epp:trIDType"/>
|
||||
<element name="paDate" type="dateTime"/>
|
||||
</sequence>
|
||||
</complexType>
|
||||
<complexType name="paNameType">
|
||||
<simpleContent>
|
||||
<extension base="eppcom:labelType">
|
||||
<attribute name="paResult" type="boolean"
|
||||
use="required"/>
|
||||
</extension>
|
||||
</simpleContent>
|
||||
</complexType>
|
||||
|
||||
<!--
|
||||
End of schema.
|
||||
-->
|
||||
</schema>
|
|
@ -0,0 +1,246 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<schema targetNamespace="urn:ietf:params:xml:ns:launch-1.0" xmlns:launch="urn:ietf:params:xml:ns:launch-1.0"
|
||||
xmlns:eppcom="urn:ietf:params:xml:ns:eppcom-1.0" xmlns:mark="urn:ietf:params:xml:ns:mark-1.0"
|
||||
xmlns:smd="urn:ietf:params:xml:ns:signedMark-1.0" xmlns="http://www.w3.org/2001/XMLSchema"
|
||||
elementFormDefault="qualified">
|
||||
|
||||
<!-- Import common element types. -->
|
||||
<import namespace="urn:ietf:params:xml:ns:eppcom-1.0" schemaLocation="eppcom.xsd" />
|
||||
|
||||
<import namespace="urn:ietf:params:xml:ns:mark-1.0" schemaLocation="mark.xsd" />
|
||||
|
||||
<import namespace="urn:ietf:params:xml:ns:signedMark-1.0" schemaLocation="smd.xsd" />
|
||||
|
||||
<annotation>
|
||||
<documentation>
|
||||
Extensible Provisioning Protocol v1.0
|
||||
domain name extension
|
||||
schema
|
||||
for the launch phase processing.
|
||||
</documentation>
|
||||
</annotation>
|
||||
|
||||
<!-- Child elements found in EPP commands. -->
|
||||
<element name="check" type="launch:checkType" />
|
||||
<element name="info" type="launch:infoType" />
|
||||
<element name="create" type="launch:createType" />
|
||||
<element name="update" type="launch:idContainerType" />
|
||||
<element name="delete" type="launch:idContainerType" />
|
||||
|
||||
<!-- Common container of id (identifier) element -->
|
||||
<complexType name="idContainerType">
|
||||
<sequence>
|
||||
<element name="phase" type="launch:phaseType" />
|
||||
<element name="applicationID" type="launch:applicationIDType" />
|
||||
</sequence>
|
||||
</complexType>
|
||||
|
||||
<!-- Definition for application identifier -->
|
||||
<simpleType name="applicationIDType">
|
||||
<restriction base="token" />
|
||||
</simpleType>
|
||||
|
||||
<!-- Definition for launch phase. Name is an optional attribute used to extend
|
||||
the phase type. For example, when using the phase type value of &qt;custom>, the
|
||||
name can be used to specify the custom phase. -->
|
||||
<complexType name="phaseType">
|
||||
<simpleContent>
|
||||
<extension base="launch:phaseTypeValue">
|
||||
<attribute name="name" type="token" />
|
||||
</extension>
|
||||
</simpleContent>
|
||||
</complexType>
|
||||
|
||||
<!-- Enumeration of for launch phase values. -->
|
||||
<simpleType name="phaseTypeValue">
|
||||
<restriction base="token">
|
||||
<enumeration value="sunrise" />
|
||||
<enumeration value="landrush" />
|
||||
<enumeration value="claims" />
|
||||
<enumeration value="open" />
|
||||
<enumeration value="custom" />
|
||||
</restriction>
|
||||
</simpleType>
|
||||
|
||||
|
||||
<!-- Definition for the sunrise code -->
|
||||
<simpleType name="codeValue">
|
||||
<restriction base="token">
|
||||
<minLength value="1" />
|
||||
</restriction>
|
||||
</simpleType>
|
||||
|
||||
<complexType name="codeType">
|
||||
<simpleContent>
|
||||
<extension base="launch:codeValue">
|
||||
<attribute name="validatorID" type="launch:validatorIDType"
|
||||
use="optional" />
|
||||
</extension>
|
||||
</simpleContent>
|
||||
</complexType>
|
||||
|
||||
<!-- Definition for the notice identifier -->
|
||||
<simpleType name="noticeIDValue">
|
||||
<restriction base="token">
|
||||
<minLength value="1" />
|
||||
</restriction>
|
||||
</simpleType>
|
||||
|
||||
<complexType name="noticeIDType">
|
||||
<simpleContent>
|
||||
<extension base="launch:noticeIDValue">
|
||||
<attribute name="validatorID" type="launch:validatorIDType"
|
||||
use="optional" />
|
||||
</extension>
|
||||
</simpleContent>
|
||||
</complexType>
|
||||
|
||||
<!-- Definition for the validator identifier -->
|
||||
<simpleType name="validatorIDType">
|
||||
<restriction base="token">
|
||||
<minLength value="1" />
|
||||
</restriction>
|
||||
</simpleType>
|
||||
|
||||
<!-- Possible status values for sunrise application -->
|
||||
<simpleType name="statusValueType">
|
||||
<restriction base="token">
|
||||
<enumeration value="pendingValidation" />
|
||||
<enumeration value="validated" />
|
||||
<enumeration value="invalid" />
|
||||
<enumeration value="pendingAllocation" />
|
||||
<enumeration value="allocated" />
|
||||
<enumeration value="rejected" />
|
||||
<enumeration value="custom" />
|
||||
</restriction>
|
||||
</simpleType>
|
||||
|
||||
<!-- Status type definition -->
|
||||
<complexType name="statusType">
|
||||
<simpleContent>
|
||||
<extension base="normalizedString">
|
||||
<attribute name="s" type="launch:statusValueType" use="required" />
|
||||
<attribute name="lang" type="language" default="en" />
|
||||
<attribute name="name" type="token" />
|
||||
</extension>
|
||||
</simpleContent>
|
||||
</complexType>
|
||||
|
||||
<!-- codeMark Type that contains an optional code with mark information. -->
|
||||
<complexType name="codeMarkType">
|
||||
<sequence>
|
||||
<element name="code" type="launch:codeType" minOccurs="0" />
|
||||
<element ref="mark:abstractMark" minOccurs="0" />
|
||||
</sequence>
|
||||
</complexType>
|
||||
|
||||
<!-- Child elements for the create command -->
|
||||
<complexType name="createType">
|
||||
<sequence>
|
||||
<element name="phase" type="launch:phaseType" />
|
||||
<choice minOccurs="0">
|
||||
<element name="codeMark" type="launch:codeMarkType" maxOccurs="unbounded" />
|
||||
<element ref="smd:abstractSignedMark" maxOccurs="unbounded" />
|
||||
<element ref="smd:encodedSignedMark" maxOccurs="unbounded" />
|
||||
</choice>
|
||||
<element name="notice" minOccurs="0" type="launch:createNoticeType" />
|
||||
</sequence>
|
||||
<attribute name="type" type="launch:objectType" />
|
||||
</complexType>
|
||||
|
||||
<!-- Type of launch object -->
|
||||
<simpleType name="objectType">
|
||||
<restriction base="token">
|
||||
<enumeration value="application" />
|
||||
<enumeration value="registration" />
|
||||
</restriction>
|
||||
</simpleType>
|
||||
|
||||
|
||||
<!-- Child elements of the create notice element. -->
|
||||
<complexType name="createNoticeType">
|
||||
<sequence>
|
||||
<element name="noticeID" type="launch:noticeIDType" />
|
||||
<element name="notAfter" type="dateTime" />
|
||||
<element name="acceptedDate" type="dateTime" />
|
||||
</sequence>
|
||||
</complexType>
|
||||
|
||||
|
||||
<!-- Child elements of check (Claims Check Command). -->
|
||||
<complexType name="checkType">
|
||||
<sequence>
|
||||
<element name="phase" type="launch:phaseType" />
|
||||
</sequence>
|
||||
<attribute name="type" type="launch:checkFormType" default="claims" />
|
||||
</complexType>
|
||||
|
||||
|
||||
<!-- Type of check form (claims check or availability check) -->
|
||||
<simpleType name="checkFormType">
|
||||
<restriction base="token">
|
||||
<enumeration value="claims" />
|
||||
<enumeration value="avail" />
|
||||
</restriction>
|
||||
</simpleType>
|
||||
|
||||
|
||||
<!-- Child elements of info command. -->
|
||||
<complexType name="infoType">
|
||||
<sequence>
|
||||
<element name="phase" type="launch:phaseType" />
|
||||
<element name="applicationID" type="launch:applicationIDType"
|
||||
minOccurs="0" />
|
||||
</sequence>
|
||||
<attribute name="includeMark" type="boolean" default="false" />
|
||||
</complexType>
|
||||
|
||||
<!-- Child response elements. -->
|
||||
<element name="chkData" type="launch:chkDataType" />
|
||||
<element name="creData" type="launch:idContainerType" />
|
||||
<element name="infData" type="launch:infDataType" />
|
||||
|
||||
<!-- <check> response elements. -->
|
||||
<complexType name="chkDataType">
|
||||
<sequence>
|
||||
<element name="phase" type="launch:phaseType" />
|
||||
<element name="cd" type="launch:cdType" maxOccurs="unbounded" />
|
||||
</sequence>
|
||||
</complexType>
|
||||
|
||||
<complexType name="cdType">
|
||||
<sequence>
|
||||
<element name="name" type="launch:cdNameType" />
|
||||
<element name="claimKey" type="launch:claimKeyType" minOccurs="0" />
|
||||
</sequence>
|
||||
</complexType>
|
||||
|
||||
<complexType name="cdNameType">
|
||||
<simpleContent>
|
||||
<extension base="eppcom:labelType">
|
||||
<attribute name="exists" type="boolean" use="required" />
|
||||
</extension>
|
||||
</simpleContent>
|
||||
</complexType>
|
||||
|
||||
<complexType name="claimKeyType">
|
||||
<simpleContent>
|
||||
<extension base="token">
|
||||
<attribute name="validatorID" type="launch:validatorIDType"
|
||||
use="optional" />
|
||||
</extension>
|
||||
</simpleContent>
|
||||
</complexType>
|
||||
|
||||
<!-- <info> response elements -->
|
||||
<complexType name="infDataType">
|
||||
<sequence>
|
||||
<element name="phase" type="launch:phaseType" />
|
||||
<element name="applicationID" type="launch:applicationIDType"
|
||||
minOccurs="0" />
|
||||
<element name="status" type="launch:statusType" minOccurs="0" />
|
||||
<element ref="mark:abstractMark" minOccurs="0" maxOccurs="unbounded" />
|
||||
</sequence>
|
||||
</complexType>
|
||||
|
||||
</schema>
|
|
@ -0,0 +1,246 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!-- Schema extracted from http://tools.ietf.org/html/draft-lozano-tmch-smd -->
|
||||
<schema
|
||||
targetNamespace="urn:ietf:params:xml:ns:mark-1.0"
|
||||
xmlns:mark="urn:ietf:params:xml:ns:mark-1.0"
|
||||
xmlns="http://www.w3.org/2001/XMLSchema"
|
||||
elementFormDefault="qualified">
|
||||
|
||||
<annotation>
|
||||
<documentation>
|
||||
Schema for representing a Trademark, also referred to
|
||||
as Mark.
|
||||
</documentation>
|
||||
</annotation>
|
||||
|
||||
<!--
|
||||
Abstract mark for replacement via substitution.
|
||||
-->
|
||||
<element name="abstractMark" type="mark:abstractMarkType"
|
||||
abstract="true"/>
|
||||
|
||||
<!--
|
||||
<mark:mark> element definition
|
||||
-->
|
||||
<element name="mark" type="mark:markType"
|
||||
substitutionGroup="mark:abstractMark"/>
|
||||
|
||||
<!--
|
||||
Empty type for use in extending for a mark
|
||||
-->
|
||||
<complexType name="abstractMarkType"/>
|
||||
|
||||
<!--
|
||||
<mark:mark> child elements
|
||||
-->
|
||||
<complexType name="markType">
|
||||
<complexContent>
|
||||
<extension base="mark:abstractMarkType">
|
||||
<sequence>
|
||||
<element name="trademark" type="mark:trademarkType"
|
||||
minOccurs="0" maxOccurs="unbounded"/>
|
||||
<element name="treatyOrStatute"
|
||||
type="mark:treatyOrStatuteType" minOccurs="0"
|
||||
maxOccurs="unbounded"/>
|
||||
<element name="court" type="mark:courtType" minOccurs="0"
|
||||
maxOccurs="unbounded"/>
|
||||
</sequence>
|
||||
</extension>
|
||||
</complexContent>
|
||||
</complexType>
|
||||
|
||||
<complexType name="holderType">
|
||||
<sequence>
|
||||
<element name="name" type="token" minOccurs="0"/>
|
||||
<element name="org" type="token" minOccurs="0"/>
|
||||
<element name="addr" type="mark:addrType"/>
|
||||
<element name="voice" type="mark:e164Type" minOccurs="0"/>
|
||||
<element name="fax" type="mark:e164Type" minOccurs="0"/>
|
||||
<element name="email" type="mark:minTokenType" minOccurs="0"/>
|
||||
</sequence>
|
||||
<attribute name="entitlement" type="mark:entitlementType"/>
|
||||
</complexType>
|
||||
|
||||
<complexType name="contactType">
|
||||
<sequence>
|
||||
<element name="name" type="token"/>
|
||||
<element name="org" type="token" minOccurs="0"/>
|
||||
<element name="addr" type="mark:addrType"/>
|
||||
<element name="voice" type="mark:e164Type"/>
|
||||
<element name="fax" type="mark:e164Type" minOccurs="0"/>
|
||||
<element name="email" type="mark:minTokenType"/>
|
||||
</sequence>
|
||||
<attribute name="type" type="mark:contactTypeType"/>
|
||||
</complexType>
|
||||
|
||||
<complexType name="trademarkType">
|
||||
<sequence>
|
||||
<element name="id" type="mark:idType"/>
|
||||
<element name="markName" type="token"/>
|
||||
<element name="holder" type="mark:holderType"
|
||||
maxOccurs="unbounded" />
|
||||
<element name="contact" type="mark:contactType" minOccurs="0"
|
||||
maxOccurs="unbounded"/>
|
||||
<element name="jurisdiction" type="mark:ccType"/>
|
||||
<element name="class" type="integer" minOccurs="0"
|
||||
maxOccurs="unbounded"/>
|
||||
<element name="label" type="mark:labelType" minOccurs="0"
|
||||
maxOccurs="unbounded"/>
|
||||
<element name="goodsAndServices" type="token" />
|
||||
<element name="apId" type="token" minOccurs="0"/>
|
||||
<element name="apDate" type="dateTime" minOccurs="0"/>
|
||||
<element name="regNum" type="token"/>
|
||||
<element name="regDate" type="dateTime"/>
|
||||
<element name="exDate" type="dateTime" minOccurs="0"/>
|
||||
</sequence>
|
||||
</complexType>
|
||||
|
||||
<complexType name="treatyOrStatuteType">
|
||||
<sequence>
|
||||
<element name="id" type="mark:idType"/>
|
||||
<element name="markName" type="token"/>
|
||||
<element name="holder" type="mark:holderType"
|
||||
maxOccurs="unbounded" />
|
||||
<element name="contact" type="mark:contactType" minOccurs="0"
|
||||
maxOccurs="unbounded"/>
|
||||
<element name="protection" type="mark:protectionType"
|
||||
maxOccurs="unbounded"/>
|
||||
<element name="label" type="mark:labelType" minOccurs="0"
|
||||
maxOccurs="unbounded"/>
|
||||
<element name="goodsAndServices" type="token" />
|
||||
<element name="refNum" type="token"/>
|
||||
<element name="proDate" type="dateTime"/>
|
||||
<element name="title" type="token"/>
|
||||
<element name="execDate" type="dateTime"/>
|
||||
</sequence>
|
||||
</complexType>
|
||||
|
||||
<complexType name="courtType">
|
||||
<sequence>
|
||||
<element name="id" type="mark:idType"/>
|
||||
<element name="markName" type="token"/>
|
||||
<element name="holder" type="mark:holderType"
|
||||
maxOccurs="unbounded" />
|
||||
<element name="contact" type="mark:contactType" minOccurs="0"
|
||||
maxOccurs="unbounded"/>
|
||||
<element name="label" type="mark:labelType" minOccurs="0"
|
||||
maxOccurs="unbounded"/>
|
||||
<element name="goodsAndServices" type="token" />
|
||||
<element name="refNum" type="token"/>
|
||||
<element name="proDate" type="dateTime"/>
|
||||
<element name="cc" type="mark:ccType"/>
|
||||
<element name="region" type="token" minOccurs="0"
|
||||
maxOccurs="unbounded"/>
|
||||
<element name="courtName" type="token"/>
|
||||
</sequence>
|
||||
</complexType>
|
||||
|
||||
<!--
|
||||
Address (<mark:addr>) child elements
|
||||
-->
|
||||
<complexType name="addrType">
|
||||
<sequence>
|
||||
<element name="street" type="token" minOccurs="1" maxOccurs="3"/>
|
||||
<element name="city" type="token"/>
|
||||
<element name="sp" type="token" minOccurs="0"/>
|
||||
<element name="pc" type="mark:pcType" minOccurs="0"/>
|
||||
<element name="cc" type="mark:ccType"/>
|
||||
</sequence>
|
||||
</complexType>
|
||||
|
||||
<!--
|
||||
<mark:protection> child elements
|
||||
-->
|
||||
<complexType name="protectionType">
|
||||
<sequence>
|
||||
<element name="cc" type="mark:ccType"/>
|
||||
<element name="region" type="token" minOccurs="0"/>
|
||||
<element name="ruling" type="mark:ccType"
|
||||
minOccurs="0" maxOccurs="unbounded"/>
|
||||
</sequence>
|
||||
</complexType>
|
||||
|
||||
<!--
|
||||
Postal code definition
|
||||
-->
|
||||
<simpleType name="pcType">
|
||||
<restriction base="token">
|
||||
<maxLength value="16"/>
|
||||
</restriction>
|
||||
</simpleType>
|
||||
|
||||
<!--
|
||||
Country code definition
|
||||
-->
|
||||
<simpleType name="ccType">
|
||||
<restriction base="token">
|
||||
<length value="2"/>
|
||||
</restriction>
|
||||
</simpleType>
|
||||
|
||||
<!--
|
||||
Phone number with extension definition
|
||||
-->
|
||||
<complexType name="e164Type">
|
||||
<simpleContent>
|
||||
<extension base="mark:e164StringType">
|
||||
<attribute name="x" type="token"/>
|
||||
</extension>
|
||||
</simpleContent>
|
||||
</complexType>
|
||||
|
||||
<!--
|
||||
Phone number with extension definition
|
||||
-->
|
||||
<simpleType name="e164StringType">
|
||||
<restriction base="token">
|
||||
<pattern value="(\+[0-9]{1,3}\.[0-9]{1,14})?"/>
|
||||
<maxLength value="17"/>
|
||||
</restriction>
|
||||
</simpleType>
|
||||
|
||||
<!--
|
||||
Id type definition
|
||||
-->
|
||||
<simpleType name="idType">
|
||||
<restriction base="token">
|
||||
<pattern value="\d+-\d+"/>
|
||||
</restriction>
|
||||
</simpleType>
|
||||
|
||||
<!--
|
||||
DNS label type definition
|
||||
-->
|
||||
<simpleType name="labelType">
|
||||
<restriction base="token">
|
||||
<minLength value="1"/>
|
||||
<maxLength value="63"/>
|
||||
<pattern value="[a-zA-Z0-9]([a-zA-Z0-9\-]*[a-zA-Z0-9])?"/>
|
||||
</restriction>
|
||||
</simpleType>
|
||||
|
||||
<!--
|
||||
Type used for email addresses
|
||||
-->
|
||||
<simpleType name="minTokenType">
|
||||
<restriction base="token">
|
||||
<minLength value="1"/>
|
||||
</restriction>
|
||||
</simpleType>
|
||||
|
||||
<simpleType name="entitlementType">
|
||||
<restriction base="token">
|
||||
<enumeration value="owner"/>
|
||||
<enumeration value="assignee"/>
|
||||
<enumeration value="licensee"/>
|
||||
</restriction>
|
||||
</simpleType>
|
||||
|
||||
<simpleType name="contactTypeType">
|
||||
<restriction base="token">
|
||||
<enumeration value="owner"/>
|
||||
<enumeration value="agent"/>
|
||||
<enumeration value="thirdparty"/>
|
||||
</restriction>
|
||||
</simpleType>
|
||||
</schema>
|
|
@ -0,0 +1,133 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<schema targetNamespace="urn:ietf:params:xml:ns:rgp-1.0"
|
||||
xmlns:rgp="urn:ietf:params:xml:ns:rgp-1.0"
|
||||
xmlns="http://www.w3.org/2001/XMLSchema"
|
||||
elementFormDefault="qualified">
|
||||
|
||||
<annotation>
|
||||
<documentation>
|
||||
Extensible Provisioning Protocol v1.0
|
||||
domain name extension schema for registry grace period
|
||||
processing.
|
||||
</documentation>
|
||||
</annotation>
|
||||
|
||||
<!--
|
||||
Child elements found in EPP commands.
|
||||
-->
|
||||
<element name="update" type="rgp:updateType"/>
|
||||
|
||||
<!--
|
||||
Child elements of the <update> command for the
|
||||
redemption grace period.
|
||||
-->
|
||||
<complexType name="updateType">
|
||||
<sequence>
|
||||
<element name="restore" type="rgp:restoreType"/>
|
||||
</sequence>
|
||||
</complexType>
|
||||
|
||||
<complexType name="restoreType">
|
||||
<sequence>
|
||||
<element name="report" type="rgp:reportType"
|
||||
minOccurs="0"/>
|
||||
</sequence>
|
||||
<attribute name="op" type="rgp:rgpOpType" use="required"/>
|
||||
</complexType>
|
||||
|
||||
<!--
|
||||
New redemption grace period operations can be defined
|
||||
by adding to this enumeration.
|
||||
-->
|
||||
<simpleType name="rgpOpType">
|
||||
<restriction base="token">
|
||||
<enumeration value="request"/>
|
||||
<enumeration value="report"/>
|
||||
</restriction>
|
||||
</simpleType>
|
||||
|
||||
<complexType name="reportType">
|
||||
<sequence>
|
||||
<element name="preData" type="rgp:mixedType"/>
|
||||
<element name="postData" type="rgp:mixedType"/>
|
||||
<element name="delTime" type="dateTime"/>
|
||||
<element name="resTime" type="dateTime"/>
|
||||
<element name="resReason" type="rgp:reportTextType"/>
|
||||
<element name="statement" type="rgp:reportTextType"
|
||||
maxOccurs="2"/>
|
||||
<element name="other" type="rgp:mixedType"
|
||||
minOccurs="0"/>
|
||||
</sequence>
|
||||
</complexType>
|
||||
|
||||
<complexType name="mixedType">
|
||||
<complexContent mixed="true">
|
||||
<restriction base="anyType">
|
||||
<sequence>
|
||||
<any processContents="lax"
|
||||
minOccurs="0" maxOccurs="unbounded"/>
|
||||
</sequence>
|
||||
</restriction>
|
||||
</complexContent>
|
||||
</complexType>
|
||||
|
||||
<complexType name="reportTextType">
|
||||
<complexContent mixed="true">
|
||||
<restriction base="anyType">
|
||||
<sequence>
|
||||
<any processContents="lax"
|
||||
minOccurs="0" maxOccurs="unbounded"/>
|
||||
</sequence>
|
||||
<attribute name="lang" type="language" default="en"/>
|
||||
</restriction>
|
||||
</complexContent>
|
||||
</complexType>
|
||||
|
||||
<!--
|
||||
Child response elements.
|
||||
-->
|
||||
<element name="infData" type="rgp:respDataType"/>
|
||||
<element name="upData" type="rgp:respDataType"/>
|
||||
|
||||
<!--
|
||||
Response elements.
|
||||
-->
|
||||
<complexType name="respDataType">
|
||||
<sequence>
|
||||
<element name="rgpStatus" type="rgp:statusType"
|
||||
maxOccurs="unbounded"/>
|
||||
</sequence>
|
||||
</complexType>
|
||||
|
||||
<!--
|
||||
Status is a combination of attributes and an optional
|
||||
human-readable message that may be expressed in languages
|
||||
other than English.
|
||||
-->
|
||||
<complexType name="statusType">
|
||||
<simpleContent>
|
||||
<extension base="normalizedString">
|
||||
<attribute name="s" type="rgp:statusValueType"
|
||||
use="required"/>
|
||||
<attribute name="lang" type="language" default="en"/>
|
||||
</extension>
|
||||
</simpleContent>
|
||||
</complexType>
|
||||
|
||||
<simpleType name="statusValueType">
|
||||
<restriction base="token">
|
||||
<enumeration value="addPeriod"/>
|
||||
<enumeration value="autoRenewPeriod"/>
|
||||
<enumeration value="renewPeriod"/>
|
||||
<enumeration value="transferPeriod"/>
|
||||
<enumeration value="pendingDelete"/>
|
||||
<enumeration value="pendingRestore"/>
|
||||
<enumeration value="redemptionPeriod"/>
|
||||
</restriction>
|
||||
</simpleType>
|
||||
|
||||
<!--
|
||||
End of schema.
|
||||
-->
|
||||
</schema>
|
|
@ -0,0 +1,71 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!-- Schema extracted from http://tools.ietf.org/html/draft-lozano-tmch-smd -->
|
||||
<schema
|
||||
targetNamespace="urn:ietf:params:xml:ns:signedMark-1.0"
|
||||
xmlns:smd="urn:ietf:params:xml:ns:signedMark-1.0"
|
||||
xmlns:mark="urn:ietf:params:xml:ns:mark-1.0"
|
||||
xmlns:dsig="http://www.w3.org/2000/09/xmldsig#"
|
||||
xmlns="http://www.w3.org/2001/XMLSchema"
|
||||
elementFormDefault="qualified">
|
||||
|
||||
<annotation>
|
||||
<documentation>
|
||||
Schema for representing a Signed Trademark.
|
||||
</documentation>
|
||||
</annotation>
|
||||
|
||||
<import namespace="urn:ietf:params:xml:ns:mark-1.0"
|
||||
schemaLocation="mark.xsd" />
|
||||
<import namespace="http://www.w3.org/2000/09/xmldsig#"
|
||||
schemaLocation="dsig.xsd"/>
|
||||
|
||||
<!--
|
||||
Abstract signed mark for replacement via substitution.
|
||||
-->
|
||||
<element name="abstractSignedMark" type="smd:abstractSignedMarkType"
|
||||
abstract="true"/>
|
||||
|
||||
<!--
|
||||
Empty type for use in extending for a signed mark
|
||||
-->
|
||||
<complexType name="abstractSignedMarkType"/>
|
||||
|
||||
<element name="signedMark" type="smd:signedMarkType"
|
||||
substitutionGroup="smd:abstractSignedMark"/>
|
||||
|
||||
<element name="encodedSignedMark" type="smd:encodedSignedMarkType"/>
|
||||
|
||||
<complexType name="signedMarkType">
|
||||
<complexContent>
|
||||
<extension base="smd:abstractSignedMarkType">
|
||||
<sequence>
|
||||
<element name="id" type="mark:idType"/>
|
||||
<element name="issuerInfo" type="smd:issuerInfoType"/>
|
||||
<element name="notBefore" type="dateTime"/>
|
||||
<element name="notAfter" type="dateTime"/>
|
||||
<element ref="mark:abstractMark"/>
|
||||
<element ref="dsig:Signature"/>
|
||||
</sequence>
|
||||
<attribute name="id" type="ID" use="required"/>
|
||||
</extension>
|
||||
</complexContent>
|
||||
</complexType>
|
||||
|
||||
<complexType name="issuerInfoType">
|
||||
<sequence>
|
||||
<element name="org" type="token"/>
|
||||
<element name="email" type="mark:minTokenType"/>
|
||||
<element name="url" type="token" minOccurs="0"/>
|
||||
<element name="voice" type="mark:e164Type" minOccurs="0"/>
|
||||
</sequence>
|
||||
<attribute name="issuerID" type="token" use="required"/>
|
||||
</complexType>
|
||||
|
||||
<complexType name="encodedSignedMarkType">
|
||||
<simpleContent>
|
||||
<extension base="token">
|
||||
<attribute name="encoding" default="base64"/>
|
||||
</extension>
|
||||
</simpleContent>
|
||||
</complexType>
|
||||
</schema>
|
|
@ -22,6 +22,8 @@ import static org.mockito.Mockito.doThrow;
|
|||
import static org.mockito.Mockito.times;
|
||||
import static org.mockito.Mockito.verify;
|
||||
|
||||
import google.registry.monitoring.blackbox.connection.ProbingAction;
|
||||
import google.registry.monitoring.blackbox.connection.Protocol;
|
||||
import google.registry.monitoring.blackbox.exceptions.FailureException;
|
||||
import google.registry.monitoring.blackbox.exceptions.UndeterminedStateException;
|
||||
import google.registry.monitoring.blackbox.exceptions.UnrecoverableStateException;
|
||||
|
@ -38,22 +40,20 @@ import org.mockito.Mockito;
|
|||
/**
|
||||
* Unit Tests on {@link ProbingSequence}
|
||||
*
|
||||
* <p>First tests the construction of sequences and ensures the ordering is exactly how
|
||||
* we expect it to be.</p>
|
||||
* <p>First tests the construction of sequences and ensures the ordering is exactly how we expect it
|
||||
* to be.
|
||||
*
|
||||
* <p>Then tests the execution of each step, by ensuring the methods treatment of any kind
|
||||
* of response from the {@link ProbingStep}s or {@link ProbingAction}s is what is expected.</p>
|
||||
* <p>Then tests the execution of each step, by ensuring the methods treatment of any kind of
|
||||
* response from the {@link ProbingStep}s or {@link ProbingAction}s is what is expected.
|
||||
*
|
||||
* <p>On every test that runs the sequence, in order for the sequence to stop, we throw an
|
||||
* {@link UnrecoverableStateException}, using mocks of the steps or actions, as the sequences
|
||||
* are run using the main thread (with {@link EmbeddedChannel}).</p>
|
||||
* <p>On every test that runs the sequence, in order for the sequence to stop, we throw an {@link
|
||||
* UnrecoverableStateException}, using mocks of the steps or actions, as the sequences are run using
|
||||
* the main thread (with {@link EmbeddedChannel}).
|
||||
*/
|
||||
@RunWith(JUnit4.class)
|
||||
public class ProbingSequenceTest {
|
||||
|
||||
/**
|
||||
* Default mock {@link ProbingAction} returned when generating an action with a mockStep.
|
||||
*/
|
||||
/** Default mock {@link ProbingAction} returned when generating an action with a mockStep. */
|
||||
private ProbingAction mockAction = Mockito.mock(ProbingAction.class);
|
||||
|
||||
/**
|
||||
|
@ -62,10 +62,7 @@ public class ProbingSequenceTest {
|
|||
*/
|
||||
private ProbingStep mockStep = Mockito.mock(ProbingStep.class);
|
||||
|
||||
|
||||
/**
|
||||
* Default mock {@link Token} that is passed into each {@link ProbingSequence} tested.
|
||||
*/
|
||||
/** Default mock {@link Token} that is passed into each {@link ProbingSequence} tested. */
|
||||
private Token mockToken = Mockito.mock(Token.class);
|
||||
|
||||
/**
|
||||
|
@ -104,11 +101,12 @@ public class ProbingSequenceTest {
|
|||
ProbingStep secondStep = Mockito.mock(ProbingStep.class);
|
||||
ProbingStep thirdStep = Mockito.mock(ProbingStep.class);
|
||||
|
||||
ProbingSequence sequence = new ProbingSequence.Builder(mockToken)
|
||||
.add(firstStep)
|
||||
.add(secondStep)
|
||||
.add(thirdStep)
|
||||
.build();
|
||||
ProbingSequence sequence =
|
||||
new ProbingSequence.Builder(mockToken)
|
||||
.add(firstStep)
|
||||
.add(secondStep)
|
||||
.add(thirdStep)
|
||||
.build();
|
||||
|
||||
assertThat(sequence.get()).isEqualTo(firstStep);
|
||||
sequence = sequence.next();
|
||||
|
@ -128,12 +126,13 @@ public class ProbingSequenceTest {
|
|||
ProbingStep secondStep = Mockito.mock(ProbingStep.class);
|
||||
ProbingStep thirdStep = Mockito.mock(ProbingStep.class);
|
||||
|
||||
ProbingSequence sequence = new ProbingSequence.Builder(mockToken)
|
||||
.add(thirdStep)
|
||||
.add(secondStep)
|
||||
.markFirstRepeated()
|
||||
.add(firstStep)
|
||||
.build();
|
||||
ProbingSequence sequence =
|
||||
new ProbingSequence.Builder(mockToken)
|
||||
.add(thirdStep)
|
||||
.add(secondStep)
|
||||
.markFirstRepeated()
|
||||
.add(firstStep)
|
||||
.build();
|
||||
|
||||
assertThat(sequence.get()).isEqualTo(thirdStep);
|
||||
sequence = sequence.next();
|
||||
|
@ -145,47 +144,43 @@ public class ProbingSequenceTest {
|
|||
sequence = sequence.next();
|
||||
|
||||
assertThat(sequence.get()).isEqualTo(secondStep);
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRunStep_Success() throws UndeterminedStateException {
|
||||
//Always returns a succeeded future on call to mockAction.
|
||||
// Always returns a succeeded future on call to mockAction.
|
||||
doReturn(channel.newSucceededFuture()).when(mockAction).call();
|
||||
|
||||
// Has mockStep always return mockAction on call to generateAction
|
||||
// Has mockStep always return mockAction on call to generateAction.
|
||||
doReturn(mockAction).when(mockStep).generateAction(any(Token.class));
|
||||
|
||||
//Dummy step that server purpose of placeholder to test ability of ProbingSequence to move on.
|
||||
// Dummy step that server purpose of placeholder to test ability of ProbingSequence to move on.
|
||||
ProbingStep secondStep = Mockito.mock(ProbingStep.class);
|
||||
ProbingAction secondAction = Mockito.mock(ProbingAction.class);
|
||||
|
||||
doReturn(channel.newFailedFuture(new UnrecoverableStateException(""))).when(secondAction)
|
||||
doReturn(channel.newFailedFuture(new UnrecoverableStateException("")))
|
||||
.when(secondAction)
|
||||
.call();
|
||||
doReturn(secondAction).when(secondStep).generateAction(mockToken);
|
||||
|
||||
//Build testable sequence from mocked components.
|
||||
ProbingSequence sequence = new ProbingSequence.Builder(mockToken)
|
||||
.add(mockStep)
|
||||
.add(secondStep)
|
||||
.build();
|
||||
// Build testable sequence from mocked components.
|
||||
ProbingSequence sequence =
|
||||
new ProbingSequence.Builder(mockToken).add(mockStep).add(secondStep).build();
|
||||
|
||||
sequence.start();
|
||||
|
||||
// We expect to have only generated actions from mockStep once, and we expect to have called
|
||||
// this generated action only once, as when we move on to secondStep, it terminates the
|
||||
// sequence.
|
||||
verify(mockStep).generateAction(any(Token.class));
|
||||
verify(mockStep).generateAction(mockToken);
|
||||
verify(mockAction).call();
|
||||
|
||||
// Similarly, we expect to generate actions and call the action from the secondStep once, as
|
||||
// after calling it, the sequence should be terminated
|
||||
verify(secondStep).generateAction(any(Token.class));
|
||||
verify(secondStep).generateAction(mockToken);
|
||||
verify(secondAction).call();
|
||||
|
||||
//We should have modified the token's channel after the first, succeeded step.
|
||||
// We should have modified the token's channel after the first, succeeded step.
|
||||
assertThat(mockToken.channel()).isEqualTo(channel);
|
||||
}
|
||||
|
||||
|
@ -201,7 +196,7 @@ public class ProbingSequenceTest {
|
|||
ProbingStep secondStep = Mockito.mock(ProbingStep.class);
|
||||
ProbingAction secondAction = Mockito.mock(ProbingAction.class);
|
||||
|
||||
// Necessary for success of ProbingSequence runStep method as it calls get().protocol()
|
||||
// Necessary for success of ProbingSequence runStep method as it calls get().protocol().
|
||||
doReturn(mockProtocol).when(secondStep).protocol();
|
||||
|
||||
// We ensure that secondStep has necessary attributes to be successful step to pass on to
|
||||
|
@ -220,18 +215,15 @@ public class ProbingSequenceTest {
|
|||
doReturn(channel.newFailedFuture(new UnrecoverableStateException(""))).when(thirdAction).call();
|
||||
doReturn(thirdAction).when(mockStep).generateAction(secondToken);
|
||||
|
||||
//Build testable sequence from mocked components.
|
||||
ProbingSequence sequence = new ProbingSequence.Builder(mockToken)
|
||||
.add(mockStep)
|
||||
.add(secondStep)
|
||||
.build();
|
||||
// Build testable sequence from mocked components.
|
||||
ProbingSequence sequence =
|
||||
new ProbingSequence.Builder(mockToken).add(mockStep).add(secondStep).build();
|
||||
|
||||
sequence.start();
|
||||
|
||||
// We expect to have generated actions from mockStep twice (once for mockToken and once for
|
||||
// secondToken), and we expectto have called each generated action only once, as when we move
|
||||
// on to mockStep the second time, it will terminate the sequence after calling thirdAction.
|
||||
verify(mockStep, times(2)).generateAction(any(Token.class));
|
||||
verify(mockStep).generateAction(mockToken);
|
||||
verify(mockStep).generateAction(secondToken);
|
||||
verify(mockAction).call();
|
||||
|
@ -239,23 +231,22 @@ public class ProbingSequenceTest {
|
|||
|
||||
// Similarly, we expect to generate actions and call the action from the secondStep once, as
|
||||
// after calling it, we move on to mockStep again, which terminates the sequence.
|
||||
verify(secondStep).generateAction(any(Token.class));
|
||||
verify(secondStep).generateAction(mockToken);
|
||||
verify(secondAction).call();
|
||||
|
||||
//We should have modified the token's channel after the first, succeeded step.
|
||||
// We should have modified the token's channel after the first, succeeded step.
|
||||
assertThat(mockToken.channel()).isEqualTo(channel);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test for when we expect Failure within try catch block of generating and calling a
|
||||
* {@link ProbingAction}.
|
||||
* Test for when we expect Failure within try catch block of generating and calling a {@link
|
||||
* ProbingAction}.
|
||||
*
|
||||
* @throws UndeterminedStateException - necessary for having mock return anything on a call to
|
||||
* {@code generateAction}.
|
||||
* @throws UndeterminedStateException - necessary for having mock return anything on a call to
|
||||
* {@code generateAction}.
|
||||
*/
|
||||
private void testActionFailure() throws UndeterminedStateException {
|
||||
//Dummy step that server purpose of placeholder to test ability of ProbingSequence to move on.
|
||||
// Dummy step that server purpose of placeholder to test ability of ProbingSequence to move on.
|
||||
ProbingStep secondStep = Mockito.mock(ProbingStep.class);
|
||||
|
||||
// We create a second token that when used to generate an action throws an
|
||||
|
@ -264,18 +255,15 @@ public class ProbingSequenceTest {
|
|||
doReturn(secondToken).when(mockToken).next();
|
||||
doThrow(new UnrecoverableStateException("")).when(mockStep).generateAction(secondToken);
|
||||
|
||||
//Build testable sequence from mocked components.
|
||||
ProbingSequence sequence = new ProbingSequence.Builder(mockToken)
|
||||
.add(mockStep)
|
||||
.add(secondStep)
|
||||
.build();
|
||||
// Build testable sequence from mocked components.
|
||||
ProbingSequence sequence =
|
||||
new ProbingSequence.Builder(mockToken).add(mockStep).add(secondStep).build();
|
||||
|
||||
sequence.start();
|
||||
|
||||
// We expect that we have generated actions twice. First, when we actually test generateAction
|
||||
// with an actual call using mockToken, and second when we throw an
|
||||
// UnrecoverableStateException with secondToken.
|
||||
verify(mockStep, times(2)).generateAction(any(Token.class));
|
||||
verify(mockStep).generateAction(mockToken);
|
||||
verify(mockStep).generateAction(secondToken);
|
||||
|
||||
|
@ -296,17 +284,14 @@ public class ProbingSequenceTest {
|
|||
// Returns mock action on call to generate action for ProbingStep.
|
||||
doReturn(mockAction).when(mockStep).generateAction(mockToken);
|
||||
|
||||
//Tests generic behavior we expect when we fail in generating or calling an action.
|
||||
// Tests generic behavior we expect when we fail in generating or calling an action.
|
||||
testActionFailure();
|
||||
|
||||
// We only expect to have called this action once, as we only get it from one generateAction
|
||||
// call.
|
||||
verify(mockAction).call();
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testRunStep_FailureGenerating() throws UndeterminedStateException {
|
||||
// Create a mock first step that returns the dummy action when called to generate an action.
|
||||
|
|
|
@ -15,12 +15,14 @@
|
|||
package google.registry.monitoring.blackbox;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
import static google.registry.monitoring.blackbox.ProbingAction.CONNECTION_FUTURE_KEY;
|
||||
import static google.registry.monitoring.blackbox.connection.ProbingAction.CONNECTION_FUTURE_KEY;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.Mockito.doAnswer;
|
||||
import static org.mockito.Mockito.doReturn;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import google.registry.monitoring.blackbox.connection.ProbingAction;
|
||||
import google.registry.monitoring.blackbox.connection.Protocol;
|
||||
import google.registry.monitoring.blackbox.exceptions.UndeterminedStateException;
|
||||
import google.registry.monitoring.blackbox.handlers.ActionHandler;
|
||||
import google.registry.monitoring.blackbox.handlers.ConversionHandler;
|
||||
|
@ -48,31 +50,26 @@ import org.mockito.Mockito;
|
|||
*/
|
||||
public class ProbingStepTest {
|
||||
|
||||
/**
|
||||
* Basic Constants necessary for tests
|
||||
*/
|
||||
/** Basic Constants necessary for tests */
|
||||
private static final String ADDRESS_NAME = "TEST_ADDRESS";
|
||||
|
||||
private static final String PROTOCOL_NAME = "TEST_PROTOCOL";
|
||||
private static final int PROTOCOL_PORT = 0;
|
||||
private static final String TEST_MESSAGE = "TEST_MESSAGE";
|
||||
private static final String SECONDARY_TEST_MESSAGE = "SECONDARY_TEST_MESSAGE";
|
||||
private static final LocalAddress address = new LocalAddress(ADDRESS_NAME);
|
||||
private final EventLoopGroup eventLoopGroup = new NioEventLoopGroup(1);
|
||||
private final Bootstrap bootstrap = new Bootstrap()
|
||||
.group(eventLoopGroup)
|
||||
.channel(LocalChannel.class);
|
||||
/**
|
||||
* Used for testing how well probing step can create connection to blackbox server
|
||||
*/
|
||||
@Rule
|
||||
public NettyRule nettyRule = new NettyRule(eventLoopGroup);
|
||||
|
||||
private final Bootstrap bootstrap =
|
||||
new Bootstrap().group(eventLoopGroup).channel(LocalChannel.class);
|
||||
/** Used for testing how well probing step can create connection to blackbox server */
|
||||
@Rule public NettyRule nettyRule = new NettyRule(eventLoopGroup);
|
||||
|
||||
/**
|
||||
* The two main handlers we need in any test pipeline used that connects to {@link NettyRule's
|
||||
* server}
|
||||
**/
|
||||
*/
|
||||
private ActionHandler testHandler = new TestActionHandler();
|
||||
|
||||
private ChannelHandler conversionHandler = new ConversionHandler();
|
||||
|
||||
/**
|
||||
|
@ -92,12 +89,13 @@ public class ProbingStepTest {
|
|||
@Test
|
||||
public void testProbingActionGenerate_embeddedChannel() throws UndeterminedStateException {
|
||||
// Sets up Protocol to represent existing channel connection.
|
||||
Protocol testProtocol = Protocol.builder()
|
||||
.setHandlerProviders(ImmutableList.of(() -> conversionHandler, () -> testHandler))
|
||||
.setName(PROTOCOL_NAME)
|
||||
.setPort(PROTOCOL_PORT)
|
||||
.setPersistentConnection(true)
|
||||
.build();
|
||||
Protocol testProtocol =
|
||||
Protocol.builder()
|
||||
.setHandlerProviders(ImmutableList.of(() -> conversionHandler, () -> testHandler))
|
||||
.setName(PROTOCOL_NAME)
|
||||
.setPort(PROTOCOL_PORT)
|
||||
.setPersistentConnection(true)
|
||||
.build();
|
||||
|
||||
// Sets up an embedded channel to contain the two handlers we created already.
|
||||
EmbeddedChannel channel = new EmbeddedChannel(conversionHandler, testHandler);
|
||||
|
@ -109,12 +107,13 @@ public class ProbingStepTest {
|
|||
doReturn(channel).when(testToken).channel();
|
||||
|
||||
// Sets up generic {@link ProbingStep} that we are testing.
|
||||
ProbingStep testStep = ProbingStep.builder()
|
||||
.setMessageTemplate(new TestMessage(TEST_MESSAGE))
|
||||
.setBootstrap(bootstrap)
|
||||
.setDuration(Duration.ZERO)
|
||||
.setProtocol(testProtocol)
|
||||
.build();
|
||||
ProbingStep testStep =
|
||||
ProbingStep.builder()
|
||||
.setMessageTemplate(new TestMessage(TEST_MESSAGE))
|
||||
.setBootstrap(bootstrap)
|
||||
.setDuration(Duration.ZERO)
|
||||
.setProtocol(testProtocol)
|
||||
.build();
|
||||
|
||||
ProbingAction testAction = testStep.generateAction(testToken);
|
||||
|
||||
|
@ -123,27 +122,27 @@ public class ProbingStepTest {
|
|||
assertThat(testAction.outboundMessage().toString()).isEqualTo(SECONDARY_TEST_MESSAGE);
|
||||
assertThat(testAction.host()).isEqualTo(SECONDARY_TEST_MESSAGE);
|
||||
assertThat(testAction.protocol()).isEqualTo(testProtocol);
|
||||
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testProbingActionGenerate_newChannel() throws UndeterminedStateException {
|
||||
// Sets up Protocol for when we create a new channel.
|
||||
Protocol testProtocol = Protocol.builder()
|
||||
.setHandlerProviders(ImmutableList.of(() -> conversionHandler, () -> testHandler))
|
||||
.setName(PROTOCOL_NAME)
|
||||
.setPort(PROTOCOL_PORT)
|
||||
.setPersistentConnection(false)
|
||||
.build();
|
||||
Protocol testProtocol =
|
||||
Protocol.builder()
|
||||
.setHandlerProviders(ImmutableList.of(() -> conversionHandler, () -> testHandler))
|
||||
.setName(PROTOCOL_NAME)
|
||||
.setPort(PROTOCOL_PORT)
|
||||
.setPersistentConnection(false)
|
||||
.build();
|
||||
|
||||
// Sets up generic ProbingStep that we are testing.
|
||||
ProbingStep testStep = ProbingStep.builder()
|
||||
.setMessageTemplate(new TestMessage(TEST_MESSAGE))
|
||||
.setBootstrap(bootstrap)
|
||||
.setDuration(Duration.ZERO)
|
||||
.setProtocol(testProtocol)
|
||||
.build();
|
||||
ProbingStep testStep =
|
||||
ProbingStep.builder()
|
||||
.setMessageTemplate(new TestMessage(TEST_MESSAGE))
|
||||
.setBootstrap(bootstrap)
|
||||
.setDuration(Duration.ZERO)
|
||||
.setProtocol(testProtocol)
|
||||
.build();
|
||||
|
||||
// Sets up testToken to return arbitrary values, and no channel. Used when we create a new
|
||||
// channel.
|
||||
|
@ -162,7 +161,5 @@ public class ProbingStepTest {
|
|||
assertThat(testAction.outboundMessage().toString()).isEqualTo(ADDRESS_NAME);
|
||||
assertThat(testAction.host()).isEqualTo(ADDRESS_NAME);
|
||||
assertThat(testAction.protocol()).isEqualTo(testProtocol);
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -12,10 +12,10 @@
|
|||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package google.registry.monitoring.blackbox;
|
||||
package google.registry.monitoring.blackbox.connection;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
import static google.registry.monitoring.blackbox.ProbingAction.CONNECTION_FUTURE_KEY;
|
||||
import static google.registry.monitoring.blackbox.connection.ProbingAction.CONNECTION_FUTURE_KEY;
|
||||
import static java.nio.charset.StandardCharsets.US_ASCII;
|
||||
import static java.nio.charset.StandardCharsets.UTF_8;
|
||||
|
||||
|
@ -46,7 +46,7 @@ import org.junit.runners.JUnit4;
|
|||
* Unit tests for {@link ProbingAction} subtypes
|
||||
*
|
||||
* <p>Attempts to test how well each {@link ProbingAction} works with an {@link ActionHandler}
|
||||
* subtype when receiving to all possible types of responses</p>
|
||||
* subtype when receiving to all possible types of responses
|
||||
*/
|
||||
@RunWith(JUnit4.class)
|
||||
public class ProbingActionTest {
|
||||
|
@ -58,53 +58,55 @@ public class ProbingActionTest {
|
|||
private static final int TEST_PORT = 0;
|
||||
|
||||
private static final EventLoopGroup eventLoopGroup = new NioEventLoopGroup(1);
|
||||
/**
|
||||
* Used for testing how well probing step can create connection to blackbox server
|
||||
*/
|
||||
@Rule
|
||||
public NettyRule nettyRule = new NettyRule(eventLoopGroup);
|
||||
|
||||
/** Used for testing how well probing step can create connection to blackbox server */
|
||||
@Rule public NettyRule nettyRule = new NettyRule(eventLoopGroup);
|
||||
|
||||
/**
|
||||
* We use custom Test {@link ActionHandler} and {@link ConversionHandler} so test depends only on
|
||||
* {@link ProbingAction}
|
||||
*/
|
||||
private ActionHandler testHandler = new TestActionHandler();
|
||||
|
||||
private ChannelHandler conversionHandler = new ConversionHandler();
|
||||
|
||||
//TODO - Currently, this test fails to receive outbound messages from the embedded channel, which
|
||||
// TODO - Currently, this test fails to receive outbound messages from the embedded channel, which
|
||||
// we will fix in a later release.
|
||||
@Ignore
|
||||
@Test
|
||||
public void testSuccess_existingChannel() {
|
||||
//setup
|
||||
// setup
|
||||
EmbeddedChannel channel = new EmbeddedChannel(conversionHandler, testHandler);
|
||||
channel.attr(CONNECTION_FUTURE_KEY).set(channel.newSucceededFuture());
|
||||
|
||||
// Sets up a Protocol corresponding to when a connection exists.
|
||||
Protocol protocol = Protocol.builder()
|
||||
.setHandlerProviders(ImmutableList.of(() -> conversionHandler, () -> testHandler))
|
||||
.setName(PROTOCOL_NAME)
|
||||
.setPort(TEST_PORT)
|
||||
.setPersistentConnection(true)
|
||||
.build();
|
||||
Protocol protocol =
|
||||
Protocol.builder()
|
||||
.setHandlerProviders(ImmutableList.of(() -> conversionHandler, () -> testHandler))
|
||||
.setName(PROTOCOL_NAME)
|
||||
.setPort(TEST_PORT)
|
||||
.setPersistentConnection(true)
|
||||
.build();
|
||||
|
||||
// Sets up a ProbingAction that creates a channel using test specified attributes.
|
||||
ProbingAction action = ProbingAction.builder()
|
||||
.setChannel(channel)
|
||||
.setProtocol(protocol)
|
||||
.setDelay(Duration.ZERO)
|
||||
.setOutboundMessage(new TestMessage(TEST_MESSAGE))
|
||||
.setHost("")
|
||||
.build();
|
||||
ProbingAction action =
|
||||
ProbingAction.builder()
|
||||
.setChannel(channel)
|
||||
.setProtocol(protocol)
|
||||
.setDelay(Duration.ZERO)
|
||||
.setOutboundMessage(new TestMessage(TEST_MESSAGE))
|
||||
.setHost("")
|
||||
.build();
|
||||
|
||||
//tests main function of ProbingAction
|
||||
// tests main function of ProbingAction
|
||||
ChannelFuture future = action.call();
|
||||
|
||||
//Obtains the outboundMessage passed through pipeline after delay
|
||||
// Obtains the outboundMessage passed through pipeline after delay
|
||||
Object msg = null;
|
||||
while (msg == null) {
|
||||
msg = channel.readOutbound();
|
||||
}
|
||||
//tests the passed message is exactly what we expect
|
||||
// tests the passed message is exactly what we expect
|
||||
assertThat(msg).isInstanceOf(ByteBuf.class);
|
||||
String request = ((ByteBuf) msg).toString(UTF_8);
|
||||
assertThat(request).isEqualTo(TEST_MESSAGE);
|
||||
|
@ -112,7 +114,8 @@ public class ProbingActionTest {
|
|||
// Ensures that we haven't marked future as done until response is received.
|
||||
assertThat(future.isDone()).isFalse();
|
||||
|
||||
//after writing inbound, we should have a success
|
||||
// After writing inbound, we should have a success as we use an EmbeddedChannel, ensuring all
|
||||
// operations happen on the main thread - i.e. synchronously.
|
||||
channel.writeInbound(Unpooled.wrappedBuffer(SECONDARY_TEST_MESSAGE.getBytes(US_ASCII)));
|
||||
assertThat(future.isSuccess()).isTrue();
|
||||
|
||||
|
@ -121,42 +124,41 @@ public class ProbingActionTest {
|
|||
|
||||
@Test
|
||||
public void testSuccess_newChannel() throws Exception {
|
||||
//setup
|
||||
// setup
|
||||
|
||||
LocalAddress address = new LocalAddress(ADDRESS_NAME);
|
||||
Bootstrap bootstrap = new Bootstrap()
|
||||
.group(eventLoopGroup)
|
||||
.channel(LocalChannel.class);
|
||||
Bootstrap bootstrap = new Bootstrap().group(eventLoopGroup).channel(LocalChannel.class);
|
||||
|
||||
// Sets up a Protocol corresponding to when a new connection is created.
|
||||
Protocol protocol = Protocol.builder()
|
||||
.setHandlerProviders(ImmutableList.of(() -> conversionHandler, () -> testHandler))
|
||||
.setName(PROTOCOL_NAME)
|
||||
.setPort(TEST_PORT)
|
||||
.setPersistentConnection(false)
|
||||
.build();
|
||||
Protocol protocol =
|
||||
Protocol.builder()
|
||||
.setHandlerProviders(ImmutableList.of(() -> conversionHandler, () -> testHandler))
|
||||
.setName(PROTOCOL_NAME)
|
||||
.setPort(TEST_PORT)
|
||||
.setPersistentConnection(false)
|
||||
.build();
|
||||
|
||||
nettyRule.setUpServer(address);
|
||||
|
||||
// Sets up a ProbingAction with existing channel using test specified attributes.
|
||||
ProbingAction action = ProbingAction.builder()
|
||||
.setBootstrap(bootstrap)
|
||||
.setProtocol(protocol)
|
||||
.setDelay(Duration.ZERO)
|
||||
.setOutboundMessage(new TestMessage(TEST_MESSAGE))
|
||||
.setHost(ADDRESS_NAME)
|
||||
.build();
|
||||
ProbingAction action =
|
||||
ProbingAction.builder()
|
||||
.setBootstrap(bootstrap)
|
||||
.setProtocol(protocol)
|
||||
.setDelay(Duration.ZERO)
|
||||
.setOutboundMessage(new TestMessage(TEST_MESSAGE))
|
||||
.setHost(ADDRESS_NAME)
|
||||
.build();
|
||||
|
||||
//tests main function of ProbingAction
|
||||
// tests main function of ProbingAction
|
||||
ChannelFuture future = action.call();
|
||||
|
||||
//Tests to see if message is properly sent to remote server
|
||||
// Tests to see if message is properly sent to remote server
|
||||
nettyRule.assertReceivedMessage(TEST_MESSAGE);
|
||||
|
||||
future = future.syncUninterruptibly();
|
||||
//Tests to see that, since server responds, we have set future to true
|
||||
// Tests to see that, since server responds, we have set future to true
|
||||
assertThat(future.isSuccess()).isTrue();
|
||||
assertThat(((TestActionHandler) testHandler).getResponse().toString()).isEqualTo(TEST_MESSAGE);
|
||||
}
|
||||
}
|
||||
|
|
@ -31,24 +31,20 @@ import io.netty.channel.ChannelPromise;
|
|||
* {@link ChannelHandler} used in tests to convert {@link OutboundMessageType} to to {@link
|
||||
* ByteBuf}s and convert {@link ByteBuf}s to {@link InboundMessageType}
|
||||
*
|
||||
* <p>Specific type of {@link OutboundMessageType} and {@link InboundMessageType}
|
||||
* used for conversion is the {@link TestMessage} type.</p>
|
||||
* <p>Specific type of {@link OutboundMessageType} and {@link InboundMessageType} used for
|
||||
* conversion is the {@link TestMessage} type.
|
||||
*/
|
||||
public class ConversionHandler extends ChannelDuplexHandler {
|
||||
|
||||
/**
|
||||
* Handles inbound conversion
|
||||
*/
|
||||
/** Handles inbound conversion */
|
||||
@Override
|
||||
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
|
||||
public void channelRead(ChannelHandlerContext ctx, Object msg) {
|
||||
ByteBuf buf = (ByteBuf) msg;
|
||||
ctx.fireChannelRead(new TestMessage(buf.toString(UTF_8)));
|
||||
buf.release();
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles outbound conversion
|
||||
*/
|
||||
/** Handles outbound conversion */
|
||||
@Override
|
||||
public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise)
|
||||
throws Exception {
|
||||
|
@ -57,4 +53,3 @@ public class ConversionHandler extends ChannelDuplexHandler {
|
|||
super.write(ctx, buf, promise);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,240 @@
|
|||
// Copyright 2019 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.monitoring.blackbox.handlers;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
import static google.registry.testing.JUnitBackports.assertThrows;
|
||||
|
||||
import google.registry.monitoring.blackbox.exceptions.EppClientException;
|
||||
import google.registry.monitoring.blackbox.exceptions.FailureException;
|
||||
import google.registry.monitoring.blackbox.exceptions.UndeterminedStateException;
|
||||
import google.registry.monitoring.blackbox.messages.EppRequestMessage;
|
||||
import google.registry.monitoring.blackbox.messages.EppResponseMessage;
|
||||
import google.registry.monitoring.blackbox.util.EppUtils;
|
||||
import io.netty.channel.ChannelFuture;
|
||||
import io.netty.channel.ChannelHandler;
|
||||
import io.netty.channel.embedded.EmbeddedChannel;
|
||||
import java.io.IOException;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.junit.runners.Parameterized;
|
||||
import org.junit.runners.Parameterized.Parameter;
|
||||
import org.junit.runners.Parameterized.Parameters;
|
||||
import org.w3c.dom.Document;
|
||||
import org.xml.sax.SAXException;
|
||||
|
||||
/**
|
||||
* Unit tests for {@link EppActionHandler} and {@link EppMessageHandler} as well as integration
|
||||
* tests for both of them.
|
||||
*
|
||||
* <p>Attempts to test how well {@link EppActionHandler} works when responding to all possible types
|
||||
* of {@link EppResponseMessage}s with corresponding {@link EppRequestMessage} sent down channel
|
||||
* pipeline.
|
||||
*/
|
||||
@RunWith(Parameterized.class)
|
||||
public class EppActionHandlerTest {
|
||||
|
||||
private static final String USER_ID = "TEST_ID";
|
||||
private static final String USER_PASSWORD = "TEST_PASSWORD";
|
||||
private static final String USER_CLIENT_TRID = "prober-localhost-1234567891011-0";
|
||||
private static final String FAILURE_TRID = "TEST_FAILURE_TRID";
|
||||
private static final String DOMAIN_NAME = "TEST_DOMAIN_NAME.test";
|
||||
private static final String SERVER_ID = "TEST_SERVER_ID";
|
||||
|
||||
@Parameter(0)
|
||||
public EppRequestMessage message;
|
||||
|
||||
private EmbeddedChannel channel;
|
||||
private EppActionHandler actionHandler = new EppActionHandler();
|
||||
private EppMessageHandler messageHandler = new EppMessageHandler(EppUtils.getGreetingResponse());
|
||||
|
||||
// We test all relevant EPP actions
|
||||
@Parameters(name = "{0}")
|
||||
public static EppRequestMessage[] data() {
|
||||
return new EppRequestMessage[] {
|
||||
EppUtils.getHelloMessage(EppUtils.getGreetingResponse()),
|
||||
EppUtils.getLoginMessage(EppUtils.getSuccessResponse(), USER_ID, USER_PASSWORD),
|
||||
EppUtils.getCreateMessage(EppUtils.getSuccessResponse()),
|
||||
EppUtils.getCreateMessage(EppUtils.getFailureResponse()),
|
||||
EppUtils.getDeleteMessage(EppUtils.getSuccessResponse()),
|
||||
EppUtils.getDeleteMessage(EppUtils.getFailureResponse()),
|
||||
EppUtils.getLogoutMessage(EppUtils.getSuccessResponse()),
|
||||
EppUtils.getCheckMessage(EppUtils.getDomainExistsResponse()),
|
||||
EppUtils.getCheckMessage(EppUtils.getDomainNotExistsResponse())
|
||||
};
|
||||
}
|
||||
|
||||
/** Setup main three handlers to be used in pipeline. */
|
||||
@Before
|
||||
public void setup() throws EppClientException {
|
||||
message.modifyMessage(USER_CLIENT_TRID, DOMAIN_NAME);
|
||||
}
|
||||
|
||||
private void setupEmbeddedChannel(ChannelHandler... handlers) {
|
||||
channel = new EmbeddedChannel(handlers);
|
||||
}
|
||||
|
||||
private Document getResponse(EppResponseMessage response, boolean fail, String clTrid)
|
||||
throws IOException, EppClientException {
|
||||
if (response.name().equals("greeting")) {
|
||||
if (fail) {
|
||||
return EppUtils.getBasicResponse(true, clTrid, SERVER_ID);
|
||||
} else {
|
||||
return EppUtils.getGreeting();
|
||||
}
|
||||
} else if (response.name().equals("domainExists")) {
|
||||
return EppUtils.getDomainCheck(!fail, clTrid, SERVER_ID, DOMAIN_NAME);
|
||||
|
||||
} else if (response.name().equals("domainNotExists")) {
|
||||
return EppUtils.getDomainCheck(fail, clTrid, SERVER_ID, DOMAIN_NAME);
|
||||
|
||||
} else if (response.name().equals("success")) {
|
||||
return EppUtils.getBasicResponse(!fail, clTrid, SERVER_ID);
|
||||
|
||||
} else {
|
||||
return EppUtils.getBasicResponse(fail, clTrid, SERVER_ID);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBasicAction_Success_Embedded()
|
||||
throws SAXException, IOException, EppClientException, FailureException {
|
||||
// We simply use an embedded channel in this instance
|
||||
setupEmbeddedChannel(actionHandler);
|
||||
|
||||
ChannelFuture future = actionHandler.getFinishedFuture();
|
||||
|
||||
EppResponseMessage response = message.getExpectedResponse();
|
||||
|
||||
response.getDocument(
|
||||
EppUtils.docToByteBuf(getResponse(message.getExpectedResponse(), false, USER_CLIENT_TRID)));
|
||||
|
||||
channel.writeInbound(response);
|
||||
|
||||
ChannelFuture unusedFuture = future.syncUninterruptibly();
|
||||
|
||||
assertThat(future.isSuccess()).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBasicAction_FailCode_Embedded()
|
||||
throws SAXException, IOException, EppClientException, FailureException {
|
||||
// We simply use an embedded channel in this instance
|
||||
setupEmbeddedChannel(actionHandler);
|
||||
|
||||
ChannelFuture future = actionHandler.getFinishedFuture();
|
||||
|
||||
EppResponseMessage response = message.getExpectedResponse();
|
||||
|
||||
response.getDocument(
|
||||
EppUtils.docToByteBuf(getResponse(message.getExpectedResponse(), true, USER_CLIENT_TRID)));
|
||||
|
||||
channel.writeInbound(response);
|
||||
|
||||
assertThrows(
|
||||
FailureException.class,
|
||||
() -> {
|
||||
ChannelFuture unusedFuture = future.syncUninterruptibly();
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBasicAction_FailTRID_Embedded()
|
||||
throws SAXException, IOException, EppClientException, FailureException {
|
||||
// We simply use an embedded channel in this instance
|
||||
setupEmbeddedChannel(actionHandler);
|
||||
|
||||
ChannelFuture future = actionHandler.getFinishedFuture();
|
||||
|
||||
EppResponseMessage response = message.getExpectedResponse();
|
||||
|
||||
response.getDocument(
|
||||
EppUtils.docToByteBuf(getResponse(message.getExpectedResponse(), false, FAILURE_TRID)));
|
||||
|
||||
channel.writeInbound(response);
|
||||
|
||||
if (message.getExpectedResponse().name().equals("greeting")) {
|
||||
ChannelFuture unusedFuture = future.syncUninterruptibly();
|
||||
assertThat(future.isSuccess()).isTrue();
|
||||
} else {
|
||||
assertThrows(
|
||||
FailureException.class,
|
||||
() -> {
|
||||
ChannelFuture unusedFuture = future.syncUninterruptibly();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIntegratedAction_Success_Embedded()
|
||||
throws IOException, SAXException, UndeterminedStateException {
|
||||
// We simply use an embedded channel in this instance
|
||||
setupEmbeddedChannel(messageHandler, actionHandler);
|
||||
|
||||
ChannelFuture future = actionHandler.getFinishedFuture();
|
||||
channel.writeOutbound(message);
|
||||
|
||||
channel.writeInbound(
|
||||
EppUtils.docToByteBuf(getResponse(message.getExpectedResponse(), false, USER_CLIENT_TRID)));
|
||||
|
||||
ChannelFuture unusedFuture = future.syncUninterruptibly();
|
||||
|
||||
assertThat(future.isSuccess()).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIntegratedAction_FailCode_Embedded()
|
||||
throws IOException, SAXException, UndeterminedStateException {
|
||||
// We simply use an embedded channel in this instance
|
||||
setupEmbeddedChannel(messageHandler, actionHandler);
|
||||
|
||||
ChannelFuture future = actionHandler.getFinishedFuture();
|
||||
channel.writeOutbound(message);
|
||||
|
||||
channel.writeInbound(
|
||||
EppUtils.docToByteBuf(getResponse(message.getExpectedResponse(), true, USER_CLIENT_TRID)));
|
||||
|
||||
assertThrows(
|
||||
FailureException.class,
|
||||
() -> {
|
||||
ChannelFuture unusedFuture = future.syncUninterruptibly();
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIntegratedAction_FailTRID_Embedded()
|
||||
throws IOException, SAXException, UndeterminedStateException {
|
||||
// We simply use an embedded channel in this instance
|
||||
setupEmbeddedChannel(messageHandler, actionHandler);
|
||||
|
||||
ChannelFuture future = actionHandler.getFinishedFuture();
|
||||
channel.writeOutbound(message);
|
||||
|
||||
channel.writeInbound(
|
||||
EppUtils.docToByteBuf(getResponse(message.getExpectedResponse(), false, FAILURE_TRID)));
|
||||
|
||||
if (message.getExpectedResponse().name().equals("greeting")) {
|
||||
ChannelFuture unusedFuture = future.syncUninterruptibly();
|
||||
assertThat(future.isSuccess()).isTrue();
|
||||
} else {
|
||||
assertThrows(
|
||||
FailureException.class,
|
||||
() -> {
|
||||
ChannelFuture unusedFuture = future.syncUninterruptibly();
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
|
@ -16,8 +16,8 @@ package google.registry.monitoring.blackbox.handlers;
|
|||
|
||||
import static com.google.common.base.Preconditions.checkState;
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
import static google.registry.monitoring.blackbox.ProbingAction.REMOTE_ADDRESS_KEY;
|
||||
import static google.registry.monitoring.blackbox.Protocol.PROTOCOL_KEY;
|
||||
import static google.registry.monitoring.blackbox.connection.ProbingAction.REMOTE_ADDRESS_KEY;
|
||||
import static google.registry.monitoring.blackbox.connection.Protocol.PROTOCOL_KEY;
|
||||
import static google.registry.testing.JUnitBackports.assertThrows;
|
||||
import static java.nio.charset.StandardCharsets.US_ASCII;
|
||||
import static java.nio.charset.StandardCharsets.UTF_8;
|
||||
|
@ -25,9 +25,9 @@ import static java.nio.charset.StandardCharsets.UTF_8;
|
|||
import com.google.common.base.Throwables;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.truth.ThrowableSubject;
|
||||
import google.registry.monitoring.blackbox.ProbingActionTest;
|
||||
import google.registry.monitoring.blackbox.ProbingStepTest;
|
||||
import google.registry.monitoring.blackbox.Protocol;
|
||||
import google.registry.monitoring.blackbox.connection.ProbingActionTest;
|
||||
import google.registry.monitoring.blackbox.connection.Protocol;
|
||||
import google.registry.monitoring.blackbox.testservers.TestServer;
|
||||
import io.netty.bootstrap.Bootstrap;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
|
@ -51,18 +51,18 @@ import org.junit.rules.ExternalResource;
|
|||
/**
|
||||
* Helper for setting up and testing client / server connection with netty.
|
||||
*
|
||||
* <p>Code based on and almost identical to {@code NettyRule} in the proxy.
|
||||
* Used in {@link SslClientInitializerTest}, {@link ProbingActionTest}, and {@link ProbingStepTest}
|
||||
* </p>
|
||||
* <p>Code based on and almost identical to {@code NettyRule} in the proxy. Used in {@link
|
||||
* SslClientInitializerTest}, {@link ProbingActionTest}, and {@link ProbingStepTest}
|
||||
*/
|
||||
public final class NettyRule extends ExternalResource {
|
||||
|
||||
|
||||
private final EventLoopGroup eventLoopGroup;
|
||||
|
||||
// Handler attached to server's channel to record the request received.
|
||||
private EchoHandler echoHandler;
|
||||
// Handler attached to client's channel to record the response received.
|
||||
private DumpHandler dumpHandler;
|
||||
|
||||
private Channel channel;
|
||||
|
||||
// All I/O operations are done inside the single thread within this event loop group, which is
|
||||
|
@ -81,25 +81,20 @@ public final class NettyRule extends ExternalResource {
|
|||
channel.writeAndFlush(Unpooled.wrappedBuffer(data.getBytes(US_ASCII)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets up a server channel bound to the given local address.
|
||||
*/
|
||||
/** Sets up a server channel bound to the given local address. */
|
||||
public void setUpServer(LocalAddress localAddress, ChannelHandler... handlers) {
|
||||
checkState(echoHandler == null, "Can't call setUpServer twice");
|
||||
echoHandler = new EchoHandler();
|
||||
|
||||
new TestServer(eventLoopGroup, localAddress,
|
||||
new TestServer(
|
||||
eventLoopGroup,
|
||||
localAddress,
|
||||
ImmutableList.<ChannelHandler>builder().add(handlers).add(echoHandler).build());
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets up a client channel connecting to the give local address.
|
||||
*/
|
||||
/** Sets up a client channel connecting to the give local address. */
|
||||
void setUpClient(
|
||||
LocalAddress localAddress,
|
||||
Protocol protocol,
|
||||
String host,
|
||||
ChannelHandler handler) {
|
||||
LocalAddress localAddress, Protocol protocol, String host, ChannelHandler handler) {
|
||||
checkState(echoHandler != null, "Must call setUpServer before setUpClient");
|
||||
checkState(dumpHandler == null, "Can't call setUpClient twice");
|
||||
dumpHandler = new DumpHandler();
|
||||
|
@ -128,20 +123,17 @@ public final class NettyRule extends ExternalResource {
|
|||
checkState(channel != null, "Must call setUpClient to finish NettyRule setup");
|
||||
}
|
||||
|
||||
/**
|
||||
* Test that custom setup to send message to current server sends right message
|
||||
*/
|
||||
/** Test that custom setup to send message to current server sends right message */
|
||||
public void assertReceivedMessage(String message) throws Exception {
|
||||
assertThat(echoHandler.getRequestFuture().get()).isEqualTo(message);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Test that a message can go through, both inbound and outbound.
|
||||
*
|
||||
* <p>The client writes the message to the server, which echos it back and saves the string in
|
||||
* its promise. The client receives the echo and saves it in its promise. All these activities
|
||||
* happens in the I/O thread, and this call itself returns immediately.
|
||||
* <p>The client writes the message to the server, which echos it back and saves the string in its
|
||||
* promise. The client receives the echo and saves it in its promise. All these activities happens
|
||||
* in the I/O thread, and this call itself returns immediately.
|
||||
*/
|
||||
void assertThatMessagesWork() throws Exception {
|
||||
checkReady();
|
||||
|
@ -199,9 +191,7 @@ public final class NettyRule extends ExternalResource {
|
|||
ctx.writeAndFlush(msg).addListener(f -> requestFuture.complete(request));
|
||||
}
|
||||
|
||||
/**
|
||||
* Saves any inbound error as the cause of the promise failure.
|
||||
*/
|
||||
/** Saves any inbound error as the cause of the promise failure. */
|
||||
@Override
|
||||
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
|
||||
ChannelFuture unusedFuture =
|
||||
|
@ -209,9 +199,7 @@ public final class NettyRule extends ExternalResource {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A handler that dumps its inbound message to a promise that can be inspected later.
|
||||
*/
|
||||
/** A handler that dumps its inbound message to a promise that can be inspected later. */
|
||||
private static class DumpHandler extends ChannelInboundHandlerAdapter {
|
||||
|
||||
private final CompletableFuture<String> responseFuture = new CompletableFuture<>();
|
||||
|
@ -232,13 +220,10 @@ public final class NettyRule extends ExternalResource {
|
|||
responseFuture.complete(response);
|
||||
}
|
||||
|
||||
/**
|
||||
* Saves any inbound error into the failure cause of the promise.
|
||||
*/
|
||||
/** Saves any inbound error into the failure cause of the promise. */
|
||||
@Override
|
||||
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
|
||||
ctx.channel().closeFuture().addListener(f -> responseFuture.completeExceptionally(cause));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -15,14 +15,14 @@
|
|||
package google.registry.monitoring.blackbox.handlers;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
import static google.registry.monitoring.blackbox.ProbingAction.REMOTE_ADDRESS_KEY;
|
||||
import static google.registry.monitoring.blackbox.Protocol.PROTOCOL_KEY;
|
||||
import static google.registry.monitoring.blackbox.connection.ProbingAction.REMOTE_ADDRESS_KEY;
|
||||
import static google.registry.monitoring.blackbox.connection.Protocol.PROTOCOL_KEY;
|
||||
import static google.registry.monitoring.blackbox.handlers.SslInitializerTestUtils.getKeyPair;
|
||||
import static google.registry.monitoring.blackbox.handlers.SslInitializerTestUtils.setUpSslChannel;
|
||||
import static google.registry.monitoring.blackbox.handlers.SslInitializerTestUtils.signKeyPair;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import google.registry.monitoring.blackbox.Protocol;
|
||||
import google.registry.monitoring.blackbox.connection.Protocol;
|
||||
import io.netty.channel.ChannelHandler;
|
||||
import io.netty.channel.ChannelPipeline;
|
||||
import io.netty.channel.embedded.EmbeddedChannel;
|
||||
|
@ -62,39 +62,33 @@ import org.junit.runners.Parameterized.Parameters;
|
|||
@RunWith(Parameterized.class)
|
||||
public class SslClientInitializerTest {
|
||||
|
||||
/**
|
||||
* Fake host to test if the SSL engine gets the correct peer host.
|
||||
*/
|
||||
/** Fake host to test if the SSL engine gets the correct peer host. */
|
||||
private static final String SSL_HOST = "www.example.tld";
|
||||
|
||||
/**
|
||||
* Fake port to test if the SSL engine gets the correct peer port.
|
||||
*/
|
||||
/** Fake port to test if the SSL engine gets the correct peer port. */
|
||||
private static final int SSL_PORT = 12345;
|
||||
/**
|
||||
* Fake protocol saved in channel attribute.
|
||||
*/
|
||||
private static final Protocol PROTOCOL = Protocol.builder()
|
||||
.setName("ssl")
|
||||
.setPort(SSL_PORT)
|
||||
.setHandlerProviders(ImmutableList.of())
|
||||
.setPersistentConnection(false)
|
||||
.build();
|
||||
@Rule
|
||||
public NettyRule nettyRule = new NettyRule();
|
||||
/** Fake protocol saved in channel attribute. */
|
||||
private static final Protocol PROTOCOL =
|
||||
Protocol.builder()
|
||||
.setName("ssl")
|
||||
.setPort(SSL_PORT)
|
||||
.setHandlerProviders(ImmutableList.of())
|
||||
.setPersistentConnection(false)
|
||||
.build();
|
||||
|
||||
@Rule public NettyRule nettyRule = new NettyRule();
|
||||
|
||||
@Parameter(0)
|
||||
public SslProvider sslProvider;
|
||||
/**
|
||||
* Saves the SNI hostname received by the server, if sent by the client.
|
||||
*/
|
||||
/** Saves the SNI hostname received by the server, if sent by the client. */
|
||||
private String sniHostReceived;
|
||||
|
||||
// We do our best effort to test all available SSL providers.
|
||||
@Parameters(name = "{0}")
|
||||
public static SslProvider[] data() {
|
||||
return OpenSsl.isAvailable()
|
||||
? new SslProvider[]{SslProvider.JDK, SslProvider.OPENSSL}
|
||||
: new SslProvider[]{SslProvider.JDK};
|
||||
? new SslProvider[] {SslProvider.JDK, SslProvider.OPENSSL}
|
||||
: new SslProvider[] {SslProvider.JDK};
|
||||
}
|
||||
|
||||
private ChannelHandler getServerHandler(PrivateKey privateKey, X509Certificate certificate)
|
||||
|
@ -170,7 +164,7 @@ public class SslClientInitializerTest {
|
|||
|
||||
// Set up the client to trust the self signed cert used to sign the cert that server provides.
|
||||
SslClientInitializer<LocalChannel> sslClientInitializer =
|
||||
new SslClientInitializer<>(sslProvider, new X509Certificate[]{ssc.cert()});
|
||||
new SslClientInitializer<>(sslProvider, new X509Certificate[] {ssc.cert()});
|
||||
|
||||
nettyRule.setUpClient(localAddress, PROTOCOL, SSL_HOST, sslClientInitializer);
|
||||
|
||||
|
@ -199,7 +193,7 @@ public class SslClientInitializerTest {
|
|||
|
||||
// Set up the client to trust the self signed cert used to sign the cert that server provides.
|
||||
SslClientInitializer<LocalChannel> sslClientInitializer =
|
||||
new SslClientInitializer<>(sslProvider, new X509Certificate[]{ssc.cert()});
|
||||
new SslClientInitializer<>(sslProvider, new X509Certificate[] {ssc.cert()});
|
||||
|
||||
nettyRule.setUpClient(localAddress, PROTOCOL, SSL_HOST, sslClientInitializer);
|
||||
|
||||
|
@ -211,4 +205,3 @@ public class SslClientInitializerTest {
|
|||
assertThat(nettyRule.getChannel().isActive()).isFalse();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -33,9 +33,7 @@ import javax.security.auth.x500.X500Principal;
|
|||
import org.bouncycastle.jce.provider.BouncyCastleProvider;
|
||||
import org.bouncycastle.x509.X509V3CertificateGenerator;
|
||||
|
||||
/**
|
||||
* Utility class that provides methods used by {@link SslClientInitializerTest}
|
||||
*/
|
||||
/** Utility class that provides methods used by {@link SslClientInitializerTest} */
|
||||
public class SslInitializerTestUtils {
|
||||
|
||||
static {
|
||||
|
@ -74,10 +72,7 @@ public class SslInitializerTestUtils {
|
|||
* @param certs The certificate that the server should provide.
|
||||
* @return The SSL session in current channel, can be used for further validation.
|
||||
*/
|
||||
static SSLSession setUpSslChannel(
|
||||
Channel channel,
|
||||
X509Certificate... certs)
|
||||
throws Exception {
|
||||
static SSLSession setUpSslChannel(Channel channel, X509Certificate... certs) throws Exception {
|
||||
SslHandler sslHandler = channel.pipeline().get(SslHandler.class);
|
||||
// Wait till the handshake is complete.
|
||||
sslHandler.handshakeFuture().get();
|
||||
|
@ -92,4 +87,3 @@ public class SslInitializerTestUtils {
|
|||
return sslHandler.engine().getSession();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -37,6 +37,4 @@ public class TestActionHandler extends ActionHandler {
|
|||
public InboundMessageType getResponse() {
|
||||
return receivedMessage;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
|
|
@ -15,14 +15,14 @@
|
|||
package google.registry.monitoring.blackbox.handlers;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
import static google.registry.monitoring.blackbox.ProbingAction.CONNECTION_FUTURE_KEY;
|
||||
import static google.registry.monitoring.blackbox.Protocol.PROTOCOL_KEY;
|
||||
import static google.registry.monitoring.blackbox.TestUtils.makeHttpGetRequest;
|
||||
import static google.registry.monitoring.blackbox.TestUtils.makeHttpResponse;
|
||||
import static google.registry.monitoring.blackbox.TestUtils.makeRedirectResponse;
|
||||
import static google.registry.monitoring.blackbox.connection.ProbingAction.CONNECTION_FUTURE_KEY;
|
||||
import static google.registry.monitoring.blackbox.connection.Protocol.PROTOCOL_KEY;
|
||||
import static google.registry.monitoring.blackbox.util.WebWhoisUtils.makeHttpGetRequest;
|
||||
import static google.registry.monitoring.blackbox.util.WebWhoisUtils.makeHttpResponse;
|
||||
import static google.registry.monitoring.blackbox.util.WebWhoisUtils.makeRedirectResponse;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import google.registry.monitoring.blackbox.Protocol;
|
||||
import google.registry.monitoring.blackbox.connection.Protocol;
|
||||
import google.registry.monitoring.blackbox.exceptions.FailureException;
|
||||
import google.registry.monitoring.blackbox.messages.HttpRequestMessage;
|
||||
import google.registry.monitoring.blackbox.messages.HttpResponseMessage;
|
||||
|
@ -45,8 +45,8 @@ import org.junit.runners.JUnit4;
|
|||
/**
|
||||
* Unit tests for {@link WebWhoisActionHandler}.
|
||||
*
|
||||
* <p>Attempts to test how well {@link WebWhoisActionHandler} works
|
||||
* when responding to all possible types of responses </p>
|
||||
* <p>Attempts to test how well {@link WebWhoisActionHandler} works when responding to all possible
|
||||
* types of responses
|
||||
*/
|
||||
@RunWith(JUnit4.class)
|
||||
public class WebWhoisActionHandlerTest {
|
||||
|
@ -55,14 +55,14 @@ public class WebWhoisActionHandlerTest {
|
|||
private static final String HTTP_REDIRECT = "http://";
|
||||
private static final String TARGET_HOST = "whois.nic.tld";
|
||||
private static final String DUMMY_URL = "__WILL_NOT_WORK__";
|
||||
private final Protocol standardProtocol = Protocol.builder()
|
||||
.setHandlerProviders(ImmutableList.of(() -> new WebWhoisActionHandler(
|
||||
null, null, null, null)))
|
||||
.setName("http")
|
||||
.setPersistentConnection(false)
|
||||
.setPort(HTTP_PORT)
|
||||
.build();
|
||||
|
||||
private final Protocol standardProtocol =
|
||||
Protocol.builder()
|
||||
.setHandlerProviders(
|
||||
ImmutableList.of(() -> new WebWhoisActionHandler(null, null, null, null)))
|
||||
.setName("http")
|
||||
.setPersistentConnection(false)
|
||||
.setPort(HTTP_PORT)
|
||||
.build();
|
||||
|
||||
private EmbeddedChannel channel;
|
||||
private ActionHandler actionHandler;
|
||||
|
@ -70,10 +70,7 @@ public class WebWhoisActionHandlerTest {
|
|||
private Protocol initialProtocol;
|
||||
private HttpRequestMessage msg;
|
||||
|
||||
|
||||
/**
|
||||
* Creates default protocol with empty list of handlers and specified other inputs
|
||||
*/
|
||||
/** Creates default protocol with empty list of handlers and specified other inputs */
|
||||
private Protocol createProtocol(String name, int port, boolean persistentConnection) {
|
||||
return Protocol.builder()
|
||||
.setName(name)
|
||||
|
@ -83,22 +80,14 @@ public class WebWhoisActionHandlerTest {
|
|||
.build();
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes new WebWhoisActionHandler
|
||||
*/
|
||||
/** Initializes new WebWhoisActionHandler */
|
||||
private void setupActionHandler(Bootstrap bootstrap, HttpRequestMessage messageTemplate) {
|
||||
actionHandler = new WebWhoisActionHandler(
|
||||
bootstrap,
|
||||
standardProtocol,
|
||||
standardProtocol,
|
||||
messageTemplate
|
||||
);
|
||||
actionHandler =
|
||||
new WebWhoisActionHandler(bootstrap, standardProtocol, standardProtocol, messageTemplate);
|
||||
actionHandlerProvider = () -> actionHandler;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets up testing channel with requisite attributes
|
||||
*/
|
||||
/** Sets up testing channel with requisite attributes */
|
||||
private void setupChannel(Protocol protocol) {
|
||||
channel = new EmbeddedChannel(actionHandler);
|
||||
channel.attr(PROTOCOL_KEY).set(protocol);
|
||||
|
@ -106,43 +95,39 @@ public class WebWhoisActionHandlerTest {
|
|||
}
|
||||
|
||||
private Bootstrap makeBootstrap(EventLoopGroup group) {
|
||||
return new Bootstrap()
|
||||
.group(group)
|
||||
.channel(LocalChannel.class);
|
||||
return new Bootstrap().group(group).channel(LocalChannel.class);
|
||||
}
|
||||
|
||||
|
||||
private void setup(String hostName, Bootstrap bootstrap, boolean persistentConnection) {
|
||||
msg = new HttpRequestMessage(makeHttpGetRequest(hostName, ""));
|
||||
setupActionHandler(bootstrap, msg);
|
||||
initialProtocol = createProtocol("testProtocol", 0, persistentConnection);
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBasic_responseOk() {
|
||||
//setup
|
||||
// setup
|
||||
setup("", null, true);
|
||||
setupChannel(initialProtocol);
|
||||
|
||||
//stores future
|
||||
// stores future
|
||||
ChannelFuture future = actionHandler.getFinishedFuture();
|
||||
channel.writeOutbound(msg);
|
||||
|
||||
FullHttpResponse response = new HttpResponseMessage(makeHttpResponse(HttpResponseStatus.OK));
|
||||
|
||||
//assesses that future listener isn't triggered yet.
|
||||
// assesses that future listener isn't triggered yet.
|
||||
assertThat(future.isDone()).isFalse();
|
||||
|
||||
channel.writeInbound(response);
|
||||
|
||||
//assesses that we successfully received good response and protocol is unchanged
|
||||
// assesses that we successfully received good response and protocol is unchanged
|
||||
assertThat(future.isSuccess()).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBasic_responseFailure_badRequest() {
|
||||
//setup
|
||||
// setup
|
||||
setup("", null, false);
|
||||
setupChannel(initialProtocol);
|
||||
|
||||
|
@ -150,8 +135,8 @@ public class WebWhoisActionHandlerTest {
|
|||
ChannelFuture future = actionHandler.getFinishedFuture();
|
||||
channel.writeOutbound(msg);
|
||||
|
||||
FullHttpResponse response = new HttpResponseMessage(
|
||||
makeHttpResponse(HttpResponseStatus.BAD_REQUEST));
|
||||
FullHttpResponse response =
|
||||
new HttpResponseMessage(makeHttpResponse(HttpResponseStatus.BAD_REQUEST));
|
||||
|
||||
// Assesses that future listener isn't triggered yet.
|
||||
assertThat(future.isDone()).isFalse();
|
||||
|
@ -168,23 +153,24 @@ public class WebWhoisActionHandlerTest {
|
|||
|
||||
@Test
|
||||
public void testBasic_responseFailure_badURL() {
|
||||
//setup
|
||||
// setup
|
||||
setup("", null, false);
|
||||
setupChannel(initialProtocol);
|
||||
|
||||
//stores future
|
||||
// stores future
|
||||
ChannelFuture future = actionHandler.getFinishedFuture();
|
||||
channel.writeOutbound(msg);
|
||||
|
||||
FullHttpResponse response = new HttpResponseMessage(
|
||||
makeRedirectResponse(HttpResponseStatus.MOVED_PERMANENTLY, DUMMY_URL, true));
|
||||
FullHttpResponse response =
|
||||
new HttpResponseMessage(
|
||||
makeRedirectResponse(HttpResponseStatus.MOVED_PERMANENTLY, DUMMY_URL, true));
|
||||
|
||||
//assesses that future listener isn't triggered yet.
|
||||
// assesses that future listener isn't triggered yet.
|
||||
assertThat(future.isDone()).isFalse();
|
||||
|
||||
channel.writeInbound(response);
|
||||
|
||||
//assesses that listener is triggered, and event is success
|
||||
// assesses that listener is triggered, and event is success
|
||||
assertThat(future.isDone()).isTrue();
|
||||
assertThat(future.isSuccess()).isFalse();
|
||||
|
||||
|
@ -204,7 +190,7 @@ public class WebWhoisActionHandlerTest {
|
|||
// Initializes LocalAddress with unique String.
|
||||
LocalAddress address = new LocalAddress(TARGET_HOST);
|
||||
|
||||
//stores future
|
||||
// stores future
|
||||
ChannelFuture future = actionHandler.getFinishedFuture();
|
||||
channel.writeOutbound(msg);
|
||||
|
||||
|
@ -215,18 +201,19 @@ public class WebWhoisActionHandlerTest {
|
|||
TestServer.webWhoisServer(group, address, "", TARGET_HOST, path);
|
||||
|
||||
FullHttpResponse response =
|
||||
new HttpResponseMessage(makeRedirectResponse(HttpResponseStatus.MOVED_PERMANENTLY,
|
||||
HTTP_REDIRECT + TARGET_HOST + path, true));
|
||||
new HttpResponseMessage(
|
||||
makeRedirectResponse(
|
||||
HttpResponseStatus.MOVED_PERMANENTLY, HTTP_REDIRECT + TARGET_HOST + path, true));
|
||||
|
||||
//checks that future has not been set to successful or a failure
|
||||
// checks that future has not been set to successful or a failure
|
||||
assertThat(future.isDone()).isFalse();
|
||||
|
||||
channel.writeInbound(response);
|
||||
|
||||
//makes sure old channel is shut down when attempting redirection
|
||||
// makes sure old channel is shut down when attempting redirection
|
||||
assertThat(channel.isActive()).isFalse();
|
||||
|
||||
//assesses that we successfully received good response and protocol is unchanged
|
||||
// assesses that we successfully received good response and protocol is unchanged
|
||||
assertThat(future.syncUninterruptibly().isSuccess()).isTrue();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,223 @@
|
|||
// Copyright 2019 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.monitoring.blackbox.messages;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
import static google.registry.testing.JUnitBackports.assertThrows;
|
||||
import static java.nio.charset.StandardCharsets.UTF_8;
|
||||
|
||||
import google.registry.monitoring.blackbox.exceptions.EppClientException;
|
||||
import google.registry.monitoring.blackbox.exceptions.FailureException;
|
||||
import google.registry.monitoring.blackbox.util.EppUtils;
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import javax.xml.parsers.DocumentBuilder;
|
||||
import javax.xml.parsers.DocumentBuilderFactory;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.junit.runners.JUnit4;
|
||||
import org.w3c.dom.Document;
|
||||
import org.w3c.dom.Element;
|
||||
import org.xml.sax.SAXException;
|
||||
|
||||
@RunWith(JUnit4.class)
|
||||
public class EppMessageTest {
|
||||
|
||||
private Document xmlDoc = null;
|
||||
private Document greeting = null;
|
||||
private DocumentBuilder builder = null;
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
String xmlString =
|
||||
"<epp>"
|
||||
+ "<textAndAttr myAttr1='1'>text1</textAndAttr>"
|
||||
+ "<textNoAttr>text2</textNoAttr>"
|
||||
+ "<attrNoText myAttr2='2'/>"
|
||||
+ "<textAndAttrSplitRepeated>text3</textAndAttrSplitRepeated>"
|
||||
+ "<textAndAttrSplitRepeated myAttr3='3'/>"
|
||||
+ "</epp>";
|
||||
ByteArrayInputStream byteStream = new ByteArrayInputStream(xmlString.getBytes(UTF_8));
|
||||
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
|
||||
factory.setNamespaceAware(true);
|
||||
builder = factory.newDocumentBuilder();
|
||||
xmlDoc = builder.parse(byteStream);
|
||||
|
||||
greeting = EppUtils.getGreeting();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void xmlDocToStringSuccess() throws Exception {
|
||||
Document xml = builder.newDocument();
|
||||
Element doc = xml.createElement("doc");
|
||||
Element title = xml.createElement("title");
|
||||
title.setTextContent("test");
|
||||
Element meta = xml.createElement("meta");
|
||||
meta.setAttribute("version", "1.0");
|
||||
doc.appendChild(title);
|
||||
doc.appendChild(meta);
|
||||
xml.appendChild(doc);
|
||||
|
||||
// note that setting the version just ensures this will either be the same in the result,
|
||||
// or the result won't support the version and this will throw an exception.
|
||||
xml.setXmlVersion("1.0");
|
||||
// setting stand alone to true removes this from the processing instructions
|
||||
xml.setXmlStandalone(true);
|
||||
String expected =
|
||||
"<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
|
||||
+ "<doc><title>test</title><meta version=\"1.0\"/></doc>";
|
||||
String actual = EppMessage.xmlDocToString(xml);
|
||||
assertThat(actual).isEqualTo(expected);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void xmlDoctoByteArraySuccess() throws Exception {
|
||||
Document xml = builder.newDocument();
|
||||
Element doc = xml.createElement("doc");
|
||||
Element title = xml.createElement("title");
|
||||
title.setTextContent("test");
|
||||
Element meta = xml.createElement("meta");
|
||||
meta.setAttribute("version", "1.0");
|
||||
doc.appendChild(title);
|
||||
doc.appendChild(meta);
|
||||
xml.appendChild(doc);
|
||||
|
||||
// note that setting the version just ensures this will either be the same in the result,
|
||||
// or the result won't support the version and this will throw an exception.
|
||||
xml.setXmlVersion("1.0");
|
||||
// setting stand alone to true removes this from the processing instructions
|
||||
xml.setXmlStandalone(true);
|
||||
String expected =
|
||||
"<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
|
||||
+ "<doc><title>test</title><meta version=\"1.0\"/></doc>";
|
||||
byte[] actual = EppMessage.xmlDocToByteArray(xml);
|
||||
assertThat(actual).isEqualTo(expected.getBytes(UTF_8));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void eppValidateSuccess() throws Exception {
|
||||
EppMessage.eppValidate(greeting);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void eppValidateFail() {
|
||||
assertThrows(SAXException.class, () -> EppMessage.eppValidate(xmlDoc));
|
||||
}
|
||||
|
||||
/**
|
||||
* These test a "success" without validating a document. This is to make it easier to test the
|
||||
* XPath part of the verify in isolation. See EppClientConnectionTest for tests that verify an
|
||||
* actual (greeting) respoonse.
|
||||
*/
|
||||
@Test
|
||||
public void verifyResponseSuccess() throws Exception {
|
||||
ArrayList<String> list = new ArrayList<>();
|
||||
list.add("epp");
|
||||
list.add("//textAndAttr[@myAttr1='1'] | //textAndAttr[child::text()='text1']");
|
||||
list.add("//textAndAttr[child::text()='text1']");
|
||||
list.add("//textAndAttr/@myAttr1");
|
||||
list.add("//textAndAttrSplitRepeated[@myAttr3='3']");
|
||||
|
||||
EppMessage.verifyEppResponse(xmlDoc, list, false);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void verifyEppResponseSuccess() throws Exception {
|
||||
ArrayList<String> list = new ArrayList<>();
|
||||
list.add("*");
|
||||
list.add("/eppns:epp");
|
||||
list.add("/eppns:epp/eppns:greeting");
|
||||
list.add("//eppns:greeting");
|
||||
list.add("//eppns:svID");
|
||||
|
||||
EppMessage.verifyEppResponse(greeting, list, false);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void verifyResponseMissingTextFail() {
|
||||
ArrayList<String> list = new ArrayList<>();
|
||||
list.add("epp");
|
||||
list.add("//textAndAttr[child::text()='text2']");
|
||||
|
||||
assertThrows(FailureException.class, () -> EppMessage.verifyEppResponse(xmlDoc, list, false));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void verifyResponseMissingAttrFail() {
|
||||
ArrayList<String> list = new ArrayList<>();
|
||||
list.add("epp");
|
||||
list.add("//textAndAttr/@myAttr2");
|
||||
|
||||
assertThrows(FailureException.class, () -> EppMessage.verifyEppResponse(xmlDoc, list, false));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void verifyResponseSplitTextAttrFail() {
|
||||
ArrayList<String> list = new ArrayList<>();
|
||||
list.add("epp");
|
||||
list.add("//textAndAttrSplitRepeated[@myAttr3='3'][child::text()='text3']");
|
||||
|
||||
assertThrows(FailureException.class, () -> EppMessage.verifyEppResponse(xmlDoc, list, false));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getEppDocFromTemplateTest() throws Exception {
|
||||
Map<String, String> replaceMap = new HashMap<>();
|
||||
replaceMap.put("//eppns:expectedClTRID", "ABC-1234-CBA");
|
||||
replaceMap.put("//domainns:name", "foo");
|
||||
replaceMap.put("//domainns:pw", "bar");
|
||||
Document epp = EppMessage.getEppDocFromTemplate("create.xml", replaceMap);
|
||||
List<String> noList = Collections.emptyList();
|
||||
EppMessage.verifyEppResponse(epp, noList, true);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getEppDocFromTemplateTestFail() {
|
||||
Map<String, String> replaceMap = new HashMap<>();
|
||||
replaceMap.put("//eppns:create", "ABC-1234-CBA");
|
||||
replaceMap.put("//domainns:name", "foo");
|
||||
replaceMap.put("//domainns:pw", "bar");
|
||||
assertThrows(
|
||||
EppClientException.class, () -> EppMessage.getEppDocFromTemplate("create.xml", replaceMap));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void checkValidDomainName() {
|
||||
String domainName = "good.tld";
|
||||
assertThat(domainName.matches(EppMessage.VALID_SLD_LABEL_REGEX)).isTrue();
|
||||
domainName = "good.tld.";
|
||||
assertThat(domainName.matches(EppMessage.VALID_SLD_LABEL_REGEX)).isTrue();
|
||||
domainName = "g-o-o-d.tld.";
|
||||
assertThat(domainName.matches(EppMessage.VALID_SLD_LABEL_REGEX)).isTrue();
|
||||
domainName = "good.cc.tld";
|
||||
assertThat(domainName.matches(EppMessage.VALID_SLD_LABEL_REGEX)).isTrue();
|
||||
domainName = "good.cc.tld.";
|
||||
assertThat(domainName.matches(EppMessage.VALID_SLD_LABEL_REGEX)).isTrue();
|
||||
domainName = "too-short";
|
||||
assertThat(domainName.matches(EppMessage.VALID_SLD_LABEL_REGEX)).isFalse();
|
||||
domainName = "too-short.";
|
||||
assertThat(domainName.matches(EppMessage.VALID_SLD_LABEL_REGEX)).isFalse();
|
||||
// TODO(rgr): sync up how many dots is actually too many
|
||||
domainName = "too.many.dots.tld.";
|
||||
assertThat(domainName.matches(EppMessage.VALID_SLD_LABEL_REGEX)).isFalse();
|
||||
domainName = "too.many.dots.tld";
|
||||
assertThat(domainName.matches(EppMessage.VALID_SLD_LABEL_REGEX)).isFalse();
|
||||
}
|
||||
}
|
|
@ -14,17 +14,22 @@
|
|||
|
||||
package google.registry.monitoring.blackbox.testservers;
|
||||
|
||||
import static google.registry.monitoring.blackbox.TestUtils.makeHttpResponse;
|
||||
import static google.registry.monitoring.blackbox.TestUtils.makeRedirectResponse;
|
||||
import static google.registry.monitoring.blackbox.util.WebWhoisUtils.makeHttpResponse;
|
||||
import static google.registry.monitoring.blackbox.util.WebWhoisUtils.makeRedirectResponse;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import google.registry.monitoring.blackbox.exceptions.FailureException;
|
||||
import google.registry.monitoring.blackbox.messages.EppMessage;
|
||||
import google.registry.monitoring.blackbox.messages.HttpResponseMessage;
|
||||
import io.netty.bootstrap.ServerBootstrap;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.channel.ChannelDuplexHandler;
|
||||
import io.netty.channel.ChannelFuture;
|
||||
import io.netty.channel.ChannelHandler;
|
||||
import io.netty.channel.ChannelHandler.Sharable;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import io.netty.channel.ChannelInitializer;
|
||||
import io.netty.channel.ChannelPromise;
|
||||
import io.netty.channel.EventLoopGroup;
|
||||
import io.netty.channel.SimpleChannelInboundHandler;
|
||||
import io.netty.channel.local.LocalAddress;
|
||||
|
@ -34,6 +39,7 @@ import io.netty.channel.nio.NioEventLoopGroup;
|
|||
import io.netty.handler.codec.http.HttpRequest;
|
||||
import io.netty.handler.codec.http.HttpResponse;
|
||||
import io.netty.handler.codec.http.HttpResponseStatus;
|
||||
import org.w3c.dom.Document;
|
||||
|
||||
/**
|
||||
* Mock Server Superclass whose subclasses implement specific behaviors we expect blackbox server to
|
||||
|
@ -45,18 +51,21 @@ public class TestServer {
|
|||
this(new NioEventLoopGroup(1), localAddress, handlers);
|
||||
}
|
||||
|
||||
public TestServer(EventLoopGroup eventLoopGroup, LocalAddress localAddress,
|
||||
public TestServer(
|
||||
EventLoopGroup eventLoopGroup,
|
||||
LocalAddress localAddress,
|
||||
ImmutableList<? extends ChannelHandler> handlers) {
|
||||
//Creates ChannelInitializer with handlers specified
|
||||
ChannelInitializer<LocalChannel> serverInitializer = new ChannelInitializer<LocalChannel>() {
|
||||
@Override
|
||||
protected void initChannel(LocalChannel ch) {
|
||||
for (ChannelHandler handler : handlers) {
|
||||
ch.pipeline().addLast(handler);
|
||||
}
|
||||
}
|
||||
};
|
||||
//Sets up serverBootstrap with specified initializer, eventLoopGroup, and using
|
||||
// Creates ChannelInitializer with handlers specified
|
||||
ChannelInitializer<LocalChannel> serverInitializer =
|
||||
new ChannelInitializer<LocalChannel>() {
|
||||
@Override
|
||||
protected void initChannel(LocalChannel ch) {
|
||||
for (ChannelHandler handler : handlers) {
|
||||
ch.pipeline().addLast(handler);
|
||||
}
|
||||
}
|
||||
};
|
||||
// Sets up serverBootstrap with specified initializer, eventLoopGroup, and using
|
||||
// LocalServerChannel class
|
||||
ServerBootstrap serverBootstrap =
|
||||
new ServerBootstrap()
|
||||
|
@ -65,22 +74,27 @@ public class TestServer {
|
|||
.childHandler(serverInitializer);
|
||||
|
||||
ChannelFuture unusedFuture = serverBootstrap.bind(localAddress).syncUninterruptibly();
|
||||
|
||||
}
|
||||
|
||||
public static TestServer webWhoisServer(EventLoopGroup eventLoopGroup,
|
||||
LocalAddress localAddress, String redirectInput, String destinationInput,
|
||||
public static TestServer webWhoisServer(
|
||||
EventLoopGroup eventLoopGroup,
|
||||
LocalAddress localAddress,
|
||||
String redirectInput,
|
||||
String destinationInput,
|
||||
String destinationPath) {
|
||||
return new TestServer(
|
||||
eventLoopGroup,
|
||||
localAddress,
|
||||
ImmutableList.of(new RedirectHandler(redirectInput, destinationInput, destinationPath))
|
||||
);
|
||||
ImmutableList.of(new RedirectHandler(redirectInput, destinationInput, destinationPath)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Handler that will wither redirect client, give successful response, or give error messge
|
||||
*/
|
||||
public static TestServer eppServer(EventLoopGroup eventLoopGroup, LocalAddress localAddress) {
|
||||
// TODO - add LengthFieldBasedFrameDecoder to handlers in pipeline if necessary for future
|
||||
// tests.
|
||||
return new TestServer(eventLoopGroup, localAddress, ImmutableList.of(new EppHandler()));
|
||||
}
|
||||
|
||||
/** Handler that will wither redirect client, give successful response, or give error messge */
|
||||
@Sharable
|
||||
static class RedirectHandler extends SimpleChannelInboundHandler<HttpRequest> {
|
||||
|
||||
|
@ -90,9 +104,9 @@ public class TestServer {
|
|||
|
||||
/**
|
||||
* @param redirectInput - Server will send back redirect to {@code destinationInput} when
|
||||
* receiving a request with this host location
|
||||
* receiving a request with this host location
|
||||
* @param destinationInput - Server will send back an {@link HttpResponseStatus} OK response
|
||||
* when receiving a request with this host location
|
||||
* when receiving a request with this host location
|
||||
*/
|
||||
public RedirectHandler(String redirectInput, String destinationInput, String destinationPath) {
|
||||
this.redirectInput = redirectInput;
|
||||
|
@ -108,8 +122,9 @@ public class TestServer {
|
|||
public void channelRead0(ChannelHandlerContext ctx, HttpRequest request) {
|
||||
HttpResponse response;
|
||||
if (request.headers().get("host").equals(redirectInput)) {
|
||||
response = new HttpResponseMessage(
|
||||
makeRedirectResponse(HttpResponseStatus.MOVED_PERMANENTLY, destinationInput, true));
|
||||
response =
|
||||
new HttpResponseMessage(
|
||||
makeRedirectResponse(HttpResponseStatus.MOVED_PERMANENTLY, destinationInput, true));
|
||||
} else if (request.headers().get("host").equals(destinationInput)
|
||||
&& request.uri().equals(destinationPath)) {
|
||||
response = new HttpResponseMessage(makeHttpResponse(HttpResponseStatus.OK));
|
||||
|
@ -117,7 +132,36 @@ public class TestServer {
|
|||
response = new HttpResponseMessage(makeHttpResponse(HttpResponseStatus.BAD_REQUEST));
|
||||
}
|
||||
ChannelFuture unusedFuture = ctx.channel().writeAndFlush(response);
|
||||
}
|
||||
}
|
||||
|
||||
private static class EppHandler extends ChannelDuplexHandler {
|
||||
|
||||
Document doc;
|
||||
private ChannelPromise future;
|
||||
|
||||
@Override
|
||||
public void handlerAdded(ChannelHandlerContext ctx) {
|
||||
// TODO - pass EppMessage into future to easily read attributes of message.
|
||||
future = ctx.newPromise();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void channelRead(ChannelHandlerContext ctx, Object msg) {
|
||||
ByteBuf buf = (ByteBuf) msg;
|
||||
|
||||
byte[] messageBytes = new byte[buf.readableBytes()];
|
||||
buf.readBytes(messageBytes);
|
||||
|
||||
// TODO - Break ByteBuf into multiple pieces to test how well it works with
|
||||
// LengthFieldBasedFrameDecoder.
|
||||
|
||||
try {
|
||||
doc = EppMessage.byteArrayToXmlDoc(messageBytes);
|
||||
ChannelFuture unusedFuture = future.setSuccess();
|
||||
} catch (FailureException e) {
|
||||
ChannelFuture unusedFuture = future.setFailure(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,91 @@
|
|||
// Copyright 2019 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.monitoring.blackbox.tokens;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
|
||||
import google.registry.monitoring.blackbox.exceptions.UndeterminedStateException;
|
||||
import google.registry.monitoring.blackbox.messages.EppRequestMessage;
|
||||
import google.registry.monitoring.blackbox.util.EppUtils;
|
||||
import io.netty.channel.Channel;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.junit.runners.JUnit4;
|
||||
import org.mockito.Mockito;
|
||||
|
||||
/** Unit Tests for each {@link Token} subtype (just {@link WebWhoisToken} for now) */
|
||||
@RunWith(JUnit4.class)
|
||||
public class EppTokenTest {
|
||||
|
||||
private static String TEST_HOST = "host";
|
||||
private static String TEST_TLD = "tld";
|
||||
|
||||
private EppToken persistentEppToken = new EppToken.Persistent(TEST_TLD, TEST_HOST);
|
||||
private EppToken transientEppToken = new EppToken.Transient(TEST_TLD, TEST_HOST);
|
||||
|
||||
@Test
|
||||
public void testMessageModificationSuccess_PersistentToken() throws UndeterminedStateException {
|
||||
|
||||
EppRequestMessage originalMessage = EppUtils.getCreateMessage(EppUtils.getSuccessResponse());
|
||||
String domainName = persistentEppToken.getCurrentDomainName();
|
||||
String clTrid = domainName.substring(0, domainName.indexOf('.'));
|
||||
|
||||
EppRequestMessage modifiedMessage =
|
||||
(EppRequestMessage) persistentEppToken.modifyMessage(originalMessage);
|
||||
|
||||
// ensure element values are what they should be
|
||||
assertThat(modifiedMessage.getElementValue("//domainns:name")).isEqualTo(domainName);
|
||||
assertThat(modifiedMessage.getElementValue("//eppns:clTRID")).isNotEqualTo(clTrid);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMessageModificationSuccess_TransientToken() throws UndeterminedStateException {
|
||||
|
||||
EppRequestMessage originalMessage = EppUtils.getCreateMessage(EppUtils.getSuccessResponse());
|
||||
String domainName = transientEppToken.getCurrentDomainName();
|
||||
String clTrid = domainName.substring(0, domainName.indexOf('.'));
|
||||
|
||||
EppRequestMessage modifiedMessage =
|
||||
(EppRequestMessage) transientEppToken.modifyMessage(originalMessage);
|
||||
|
||||
// ensure element values are what they should be
|
||||
assertThat(modifiedMessage.getElementValue("//domainns:name")).isEqualTo(domainName);
|
||||
assertThat(modifiedMessage.getElementValue("//eppns:clTRID")).isNotEqualTo(clTrid);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNext_persistentToken() {
|
||||
String domainName = persistentEppToken.getCurrentDomainName();
|
||||
Channel mockChannel = Mockito.mock(Channel.class);
|
||||
persistentEppToken.setChannel(mockChannel);
|
||||
|
||||
EppToken nextToken = (EppToken) persistentEppToken.next();
|
||||
|
||||
assertThat(nextToken.getCurrentDomainName()).isNotEqualTo(domainName);
|
||||
assertThat(nextToken.channel()).isEqualTo(mockChannel);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNext_transientToken() {
|
||||
String domainName = transientEppToken.getCurrentDomainName();
|
||||
Channel mockChannel = Mockito.mock(Channel.class);
|
||||
transientEppToken.setChannel(mockChannel);
|
||||
|
||||
EppToken nextToken = (EppToken) transientEppToken.next();
|
||||
|
||||
assertThat(nextToken.getCurrentDomainName()).isNotEqualTo(domainName);
|
||||
assertThat(nextToken.channel()).isNull();
|
||||
}
|
||||
}
|
|
@ -23,9 +23,7 @@ import org.junit.Test;
|
|||
import org.junit.runner.RunWith;
|
||||
import org.junit.runners.JUnit4;
|
||||
|
||||
/**
|
||||
* Unit Tests for {@link WebWhoisToken}
|
||||
*/
|
||||
/** Unit Tests for {@link WebWhoisToken} */
|
||||
@RunWith(JUnit4.class)
|
||||
public class WebWhoisTokenTest {
|
||||
|
||||
|
@ -35,19 +33,17 @@ public class WebWhoisTokenTest {
|
|||
private static final String SECOND_TLD = "second_test";
|
||||
private static final String THIRD_TLD = "third_test";
|
||||
private final CircularList<String> testDomains =
|
||||
new CircularList.Builder<String>()
|
||||
.add(FIRST_TLD, SECOND_TLD, THIRD_TLD)
|
||||
.build();
|
||||
new CircularList.Builder<String>().add(FIRST_TLD, SECOND_TLD, THIRD_TLD).build();
|
||||
|
||||
public Token webToken = new WebWhoisToken(testDomains);
|
||||
|
||||
@Test
|
||||
public void testMessageModification() throws UndeterminedStateException {
|
||||
//creates Request message with header
|
||||
// creates Request message with header
|
||||
HttpRequestMessage message = new HttpRequestMessage();
|
||||
message.headers().set("host", HOST);
|
||||
|
||||
//attempts to use Token's method for modifying the method based on its stored host
|
||||
// attempts to use Token's method for modifying the method based on its stored host
|
||||
HttpRequestMessage secondMessage = (HttpRequestMessage) webToken.modifyMessage(message);
|
||||
assertThat(secondMessage.headers().get("host")).isEqualTo(PREFIX + FIRST_TLD);
|
||||
}
|
||||
|
@ -69,5 +65,4 @@ public class WebWhoisTokenTest {
|
|||
|
||||
assertThat(webToken.host()).isEqualTo(PREFIX + FIRST_TLD);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -0,0 +1,197 @@
|
|||
// Copyright 2019 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.monitoring.blackbox.util;
|
||||
|
||||
import static google.registry.monitoring.blackbox.messages.EppMessage.CLIENT_ID_KEY;
|
||||
import static google.registry.monitoring.blackbox.messages.EppMessage.CLIENT_PASSWORD_KEY;
|
||||
import static google.registry.monitoring.blackbox.messages.EppMessage.CLIENT_TRID_KEY;
|
||||
import static google.registry.monitoring.blackbox.messages.EppMessage.DOMAIN_KEY;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import google.registry.monitoring.blackbox.exceptions.EppClientException;
|
||||
import google.registry.monitoring.blackbox.messages.EppMessage;
|
||||
import google.registry.monitoring.blackbox.messages.EppRequestMessage;
|
||||
import google.registry.monitoring.blackbox.messages.EppResponseMessage;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.buffer.Unpooled;
|
||||
import java.io.IOException;
|
||||
import org.w3c.dom.Document;
|
||||
|
||||
/** Houses static utility functions for testing EPP components of Prober. */
|
||||
public class EppUtils {
|
||||
|
||||
/** Return a simple default greeting as a {@link Document}. */
|
||||
public static Document getGreeting() throws IOException, EppClientException {
|
||||
return EppMessage.getEppDocFromTemplate("greeting.xml", ImmutableMap.of());
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a basic response as a {@link Document}.
|
||||
*
|
||||
* @param success specifies if response shows a success or failure
|
||||
* @param clTrid the client transaction ID
|
||||
* @param svTrid the server transaction ID
|
||||
* @return the EPP basic response as a {@link Document}.
|
||||
*/
|
||||
public static Document getBasicResponse(boolean success, String clTrid, String svTrid)
|
||||
throws IOException, EppClientException {
|
||||
|
||||
String template = success ? "success_response.xml" : "failed_response.xml";
|
||||
|
||||
return EppMessage.getEppDocFromTemplate(
|
||||
template,
|
||||
ImmutableMap.of(
|
||||
EppRequestMessage.CLIENT_TRID_KEY, clTrid, EppMessage.SERVER_TRID_KEY, svTrid));
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a domain check response as a {@link Document}.
|
||||
*
|
||||
* @param exists specifies if response shows that domain name exists on server or doesn't
|
||||
* @param clTrid the client transaction ID
|
||||
* @param svTrid the server transaction ID
|
||||
* @param domain the domain the check success is for
|
||||
* @return the EPP check response as a {@link Document}.
|
||||
*/
|
||||
public static Document getDomainCheck(boolean exists, String clTrid, String svTrid, String domain)
|
||||
throws IOException, EppClientException {
|
||||
|
||||
String template = exists ? "domain_exists.xml" : "domain_not_exists.xml";
|
||||
|
||||
return EppMessage.getEppDocFromTemplate(
|
||||
template,
|
||||
ImmutableMap.of(
|
||||
EppRequestMessage.CLIENT_TRID_KEY, clTrid,
|
||||
EppMessage.SERVER_TRID_KEY, svTrid,
|
||||
EppMessage.DOMAIN_KEY, domain));
|
||||
}
|
||||
|
||||
/** Converts {@link Document} to {@link ByteBuf}. */
|
||||
public static ByteBuf docToByteBuf(Document message) throws EppClientException {
|
||||
byte[] bytestream = EppMessage.xmlDocToByteArray(message);
|
||||
|
||||
ByteBuf buf = Unpooled.buffer(bytestream.length);
|
||||
|
||||
buf.writeBytes(bytestream);
|
||||
|
||||
return buf;
|
||||
}
|
||||
|
||||
/** Returns standard hello request with supplied response. */
|
||||
public static EppRequestMessage getHelloMessage(EppResponseMessage greetingResponse) {
|
||||
return new EppRequestMessage(greetingResponse, null, (a, b) -> ImmutableMap.of());
|
||||
}
|
||||
|
||||
/** Returns standard login request with supplied userId, userPassword, and response. */
|
||||
public static EppRequestMessage getLoginMessage(
|
||||
EppResponseMessage response, String userId, String userPassword) {
|
||||
return new EppRequestMessage(
|
||||
response,
|
||||
"login.xml",
|
||||
(clTrid, domain) ->
|
||||
ImmutableMap.of(
|
||||
CLIENT_TRID_KEY, clTrid,
|
||||
CLIENT_ID_KEY, userId,
|
||||
CLIENT_PASSWORD_KEY, userPassword));
|
||||
}
|
||||
|
||||
/** Returns standard create request with supplied response. */
|
||||
public static EppRequestMessage getCreateMessage(EppResponseMessage response) {
|
||||
return new EppRequestMessage(
|
||||
response,
|
||||
"create.xml",
|
||||
(clTrid, domain) ->
|
||||
ImmutableMap.of(
|
||||
CLIENT_TRID_KEY, clTrid,
|
||||
DOMAIN_KEY, domain));
|
||||
}
|
||||
|
||||
/** Returns standard delete request with supplied response. */
|
||||
public static EppRequestMessage getDeleteMessage(EppResponseMessage response) {
|
||||
return new EppRequestMessage(
|
||||
response,
|
||||
"delete.xml",
|
||||
(clTrid, domain) ->
|
||||
ImmutableMap.of(
|
||||
CLIENT_TRID_KEY, clTrid,
|
||||
DOMAIN_KEY, domain));
|
||||
}
|
||||
|
||||
/** Returns standard logout request with supplied response. */
|
||||
public static EppRequestMessage getLogoutMessage(EppResponseMessage successResponse) {
|
||||
return new EppRequestMessage(
|
||||
successResponse,
|
||||
"logout.xml",
|
||||
(clTrid, domain) -> ImmutableMap.of(CLIENT_TRID_KEY, clTrid));
|
||||
}
|
||||
|
||||
/** Returns standard check request with supplied response. */
|
||||
public static EppRequestMessage getCheckMessage(EppResponseMessage response) {
|
||||
return new EppRequestMessage(
|
||||
response,
|
||||
"check.xml",
|
||||
(clTrid, domain) ->
|
||||
ImmutableMap.of(
|
||||
CLIENT_TRID_KEY, clTrid,
|
||||
DOMAIN_KEY, domain));
|
||||
}
|
||||
|
||||
/** Returns standard success response. */
|
||||
public static EppResponseMessage getSuccessResponse() {
|
||||
return new EppResponseMessage(
|
||||
"success",
|
||||
(clTrid, domain) ->
|
||||
ImmutableList.of(
|
||||
String.format("//eppns:clTRID[.='%s']", clTrid), EppMessage.XPASS_EXPRESSION));
|
||||
}
|
||||
|
||||
/** Returns standard failure response. */
|
||||
public static EppResponseMessage getFailureResponse() {
|
||||
return new EppResponseMessage(
|
||||
"failure",
|
||||
(clTrid, domain) ->
|
||||
ImmutableList.of(
|
||||
String.format("//eppns:clTRID[.='%s']", clTrid), EppMessage.XFAIL_EXPRESSION));
|
||||
}
|
||||
|
||||
/** Returns standard domainExists response. */
|
||||
public static EppResponseMessage getDomainExistsResponse() {
|
||||
return new EppResponseMessage(
|
||||
"domainExists",
|
||||
(clTrid, domain) ->
|
||||
ImmutableList.of(
|
||||
String.format("//eppns:clTRID[.='%s']", clTrid),
|
||||
String.format("//domainns:name[@avail='false'][.='%s']", domain),
|
||||
EppMessage.XPASS_EXPRESSION));
|
||||
}
|
||||
|
||||
/** Returns standard domainNotExists response. */
|
||||
public static EppResponseMessage getDomainNotExistsResponse() {
|
||||
return new EppResponseMessage(
|
||||
"domainNotExists",
|
||||
(clTrid, domain) ->
|
||||
ImmutableList.of(
|
||||
String.format("//eppns:clTRID[.='%s']", clTrid),
|
||||
String.format("//domainns:name[@avail='true'][.='%s']", domain),
|
||||
EppMessage.XPASS_EXPRESSION));
|
||||
}
|
||||
|
||||
/** Returns standard greeting response. */
|
||||
public static EppResponseMessage getGreetingResponse() {
|
||||
return new EppResponseMessage(
|
||||
"greeting", (clTrid, domain) -> ImmutableList.of("//eppns:greeting"));
|
||||
}
|
||||
}
|
|
@ -12,7 +12,7 @@
|
|||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package google.registry.monitoring.blackbox;
|
||||
package google.registry.monitoring.blackbox.util;
|
||||
|
||||
import static java.nio.charset.StandardCharsets.US_ASCII;
|
||||
|
||||
|
@ -26,10 +26,8 @@ import io.netty.handler.codec.http.HttpMethod;
|
|||
import io.netty.handler.codec.http.HttpResponseStatus;
|
||||
import io.netty.handler.codec.http.HttpVersion;
|
||||
|
||||
/**
|
||||
* Utility class for various helper methods used in testing.
|
||||
*/
|
||||
public class TestUtils {
|
||||
/** Houses static utility functions for testing WebWHOIS components of Prober. */
|
||||
public class WebWhoisUtils {
|
||||
|
||||
public static FullHttpRequest makeHttpGetRequest(String host, String path) {
|
||||
FullHttpRequest request =
|
||||
|
@ -51,9 +49,7 @@ public class TestUtils {
|
|||
return response;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates HttpResponse given status, redirection location, and other necessary inputs
|
||||
*/
|
||||
/** Creates HttpResponse given status, redirection location, and other necessary inputs */
|
||||
public static FullHttpResponse makeRedirectResponse(
|
||||
HttpResponseStatus status, String location, boolean keepAlive) {
|
||||
FullHttpResponse response = makeHttpResponse("", status);
|
||||
|
@ -67,4 +63,3 @@ public class TestUtils {
|
|||
return response;
|
||||
}
|
||||
}
|
||||
|
|
@ -15,69 +15,51 @@
|
|||
package google.registry.util;
|
||||
|
||||
/**
|
||||
* Class that stores value {@param <T>}, and points in circle to other {@link CircularList}
|
||||
* objects.
|
||||
* Class that stores value {@param <T>}, and points in circle to other {@link CircularList} objects.
|
||||
*
|
||||
* <p>In its construction, we create a sequence of {@link CircularList} objects, each storing an *
|
||||
* instance of T. They each point to each other in a circular manner, such that we can perform *
|
||||
* circular iteration on the elements. Once finished building, we return this first {@link *
|
||||
* CircularList} object in the sequence.
|
||||
*
|
||||
* @param <T> - Element type stored in the {@link CircularList}
|
||||
*
|
||||
* <p>In its construction, we create a sequence of {@link CircularList} objects, each storing an
|
||||
* instance of T. They each point to each other in a circular manner, such that we can perform
|
||||
* circular iteration on the elements. Once finished building, we return this first {@link
|
||||
* CircularList} object in the sequence.</p>
|
||||
*/
|
||||
public class CircularList<T> {
|
||||
|
||||
/**
|
||||
* T instance stored in current node of list.
|
||||
*/
|
||||
/** T instance stored in current node of list. */
|
||||
private final T value;
|
||||
|
||||
/**
|
||||
* Pointer to next node of list.
|
||||
*/
|
||||
/** Pointer to next node of list. */
|
||||
private CircularList<T> next;
|
||||
|
||||
/**
|
||||
* Standard constructor for {@link CircularList} that initializes its stored {@code value}.
|
||||
*/
|
||||
/** Standard constructor for {@link CircularList} that initializes its stored {@code value}. */
|
||||
protected CircularList(T value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Standard get method to retrieve {@code value}.
|
||||
*/
|
||||
/** Standard get method to retrieve {@code value}. */
|
||||
public T get() {
|
||||
return value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Standard method to obtain {@code next} node in list.
|
||||
*/
|
||||
/** Standard method to obtain {@code next} node in list. */
|
||||
public CircularList<T> next() {
|
||||
return next;
|
||||
}
|
||||
|
||||
/**
|
||||
* Setter method only used in builder to point one node to the next.
|
||||
*/
|
||||
/** Setter method only used in builder to point one node to the next. */
|
||||
public void setNext(CircularList<T> next) {
|
||||
this.next = next;
|
||||
}
|
||||
|
||||
/**
|
||||
* Default Builder to create a standard instance of a {@link CircularList}.
|
||||
*/
|
||||
/** Default Builder to create a standard instance of a {@link CircularList}. */
|
||||
public static class Builder<T> extends AbstractBuilder<T, CircularList<T>> {
|
||||
|
||||
/**
|
||||
* Simply calls on constructor to {@link CircularList} to create new instance.
|
||||
*/
|
||||
/** Simply calls on constructor to {@link CircularList} to create new instance. */
|
||||
@Override
|
||||
protected CircularList<T> create(T value) {
|
||||
return new CircularList<>(value);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -85,26 +67,20 @@ public class CircularList<T> {
|
|||
* create the full list.
|
||||
*
|
||||
* @param <T> - Matching element type of iterator
|
||||
*
|
||||
* <p>Supports adding in element at a time, adding an {@link Iterable}
|
||||
* of elements, and adding an variable number of elemetns.</p>
|
||||
*
|
||||
* <p>Sets first element added to {@code first}, and when built, points last element to the
|
||||
* {@code first} element.</p>
|
||||
* <p>Supports adding in element at a time, adding an {@link Iterable} of elements, and adding
|
||||
* an variable number of elemetns.
|
||||
* <p>Sets first element added to {@code first}, and when built, points last element to the
|
||||
* {@code first} element.
|
||||
*/
|
||||
public abstract static class AbstractBuilder<T, C extends CircularList<T>> {
|
||||
|
||||
protected C first;
|
||||
protected C current;
|
||||
|
||||
/**
|
||||
* Necessary to instantiate each {@code C} object from {@code value}.
|
||||
*/
|
||||
/** Necessary to instantiate each {@code C} object from {@code value}. */
|
||||
protected abstract C create(T value);
|
||||
|
||||
/**
|
||||
* Sets current {@code C} to element added and points previous {@code C} to this one.
|
||||
*/
|
||||
/** Sets current {@code C} to element added and points previous {@code C} to this one. */
|
||||
public AbstractBuilder<T, C> add(T value) {
|
||||
C c = create(value);
|
||||
if (current == null) {
|
||||
|
@ -116,9 +92,7 @@ public class CircularList<T> {
|
|||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Simply calls {@code addElement}, for each element in {@code elements}.
|
||||
*/
|
||||
/** Simply calls {@code addElement}, for each element in {@code elements}. */
|
||||
public AbstractBuilder<T, C> add(T... values) {
|
||||
for (T element : values) {
|
||||
add(element);
|
||||
|
@ -126,21 +100,16 @@ public class CircularList<T> {
|
|||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Simply calls {@code addElement}, for each element in {@code elements}.
|
||||
*/
|
||||
/** Simply calls {@code addElement}, for each element in {@code elements}. */
|
||||
public AbstractBuilder<T, C> add(Iterable<T> values) {
|
||||
values.forEach(this::add);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the next {@code C} of the list to be the first {@code C} in the list.
|
||||
*/
|
||||
/** Sets the next {@code C} of the list to be the first {@code C} in the list. */
|
||||
public C build() {
|
||||
current.setNext(first);
|
||||
return first;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue