1
0
mirror of https://github.com/google/nomulus synced 2026-01-09 15:43:52 +00:00
Files
nomulus/load-testing/src/main/java/google/registry/client/EppClientHandler.java
sarahcaseybot 0781010b16 Create a load testing EPP client (#2415)
* Create a load testing EPP client.

This code is mostly based off of what was used for a past EPP load testing client that can be found in Google3 at https://source.corp.google.com/piper///depot/google3/experimental/users/jianglai/proxy/java/google/registry/proxy/client/

I modified the old client to be open-source friendly and use Gradle.

For now, this only performs a login and logout command, I will further expand on this in later PRs to add other EPP commands so that we can truly load test the system.

* Small changes

* Remove unnecessary build dep

* Add gradle build tasks

* Small fixes

* Add an instances setUp and cleanUp script

* More modifications to instance setup scripts

* change to ubuntu instance

* Add comment to make ssh work
2024-05-23 21:37:34 +00:00

154 lines
5.5 KiB
Java

// Copyright 2024 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.client;
import static google.registry.client.EppClient.CHANNEL_NUMBER;
import static google.registry.client.EppClient.FORCE_TERMINATE;
import static google.registry.client.EppClient.INPUT_ITERATOR;
import static google.registry.client.EppClient.LOGGING_EXECUTOR;
import static google.registry.client.EppClient.LOGGING_LOCATION;
import static google.registry.client.EppClient.LOGGING_REQUEST_COMPLETE;
import static google.registry.client.EppClient.REQUEST_SENT;
import static google.registry.client.EppClient.RESPONSE_RECEIVED;
import static java.nio.file.StandardOpenOption.APPEND;
import com.google.common.flogger.FluentLogger;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.Channel;
import io.netty.channel.ChannelDuplexHandler;
import io.netty.channel.ChannelHandler.Sharable;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelPromise;
import io.netty.util.ReferenceCountUtil;
import io.netty.util.concurrent.Promise;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.time.ZoneOffset;
import java.time.ZonedDateTime;
/** Handler that sends EPP requests and receives EPP responses. */
@SuppressWarnings("FutureReturnValueIgnored")
@Sharable
public class EppClientHandler extends ChannelDuplexHandler {
private static final FluentLogger logger = FluentLogger.forEnclosingClass();
static class FileWriter implements Runnable {
private final Path loggingLocation;
private final byte[] contents;
private final ZonedDateTime time;
FileWriter(Path loggingLocation, byte[] contents, ZonedDateTime time) {
this.loggingLocation = loggingLocation;
this.contents = contents;
this.time = time;
}
@Override
public void run() {
try {
if (!Files.exists(loggingLocation)) {
Files.createFile(loggingLocation);
}
Files.writeString(loggingLocation, time.toString() + "\n", APPEND);
Files.write(loggingLocation, contents, APPEND);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
@Override
public void channelRegistered(ChannelHandlerContext ctx) throws Exception {
ZonedDateTime now = ZonedDateTime.now(ZoneOffset.UTC);
ctx.channel().attr(REQUEST_SENT).get().add(now);
ctx.channel().attr(LOGGING_REQUEST_COMPLETE).set(ctx.executor().newPromise());
super.channelRegistered(ctx);
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
Promise<Void> loggingCompletePromise = ctx.channel().attr(LOGGING_REQUEST_COMPLETE).get();
if (!loggingCompletePromise.isDone()) {
loggingCompletePromise.setSuccess(null);
}
logger.atWarning().withCause(cause).log(
"Connection %d closed.", ctx.channel().attr(CHANNEL_NUMBER).get());
}
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) {
ZonedDateTime now = ZonedDateTime.now(ZoneOffset.UTC);
Channel ch = ctx.channel();
ctx.channel().attr(RESPONSE_RECEIVED).get().add(now);
if (msg instanceof ByteBuf buffer) {
byte[] contents = new byte[buffer.readableBytes()];
buffer.readBytes(contents);
ReferenceCountUtil.release(buffer);
if (ch.attr(LOGGING_LOCATION).get() != null) {
ch.attr(LOGGING_EXECUTOR)
.get()
.submit(new FileWriter(ch.attr(LOGGING_LOCATION).get(), contents, now));
}
if (ch.attr(INPUT_ITERATOR).get().hasNext()) {
ch.writeAndFlush(ch.attr(INPUT_ITERATOR).get().next());
} else {
ch.attr(LOGGING_REQUEST_COMPLETE).get().setSuccess(null);
if (ch.attr(FORCE_TERMINATE).get()) {
ch.close();
}
}
}
}
@Override
public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) {
ZonedDateTime now = ZonedDateTime.now(ZoneOffset.UTC);
Channel ch = ctx.channel();
ctx.channel().attr(REQUEST_SENT).get().add(now);
if (msg instanceof byte[] outputBytes) {
ByteBuf buffer = Unpooled.buffer();
buffer.writeBytes(outputBytes);
ctx.write(buffer, promise);
if (ch.attr(LOGGING_LOCATION).get() != null) {
ch.attr(LOGGING_EXECUTOR)
.get()
.submit(new FileWriter(ch.attr(LOGGING_LOCATION).get(), outputBytes, now));
}
}
}
@Override
public void close(ChannelHandlerContext ctx, ChannelPromise promise) throws Exception {
Promise<Void> loggingCompletePromise = ctx.channel().attr(LOGGING_REQUEST_COMPLETE).get();
if (!loggingCompletePromise.isDone()) {
loggingCompletePromise.setSuccess(null);
}
super.close(ctx, promise);
}
@Override
public void channelInactive(ChannelHandlerContext ctx) throws Exception {
Promise<Void> loggingCompletePromise = ctx.channel().attr(LOGGING_REQUEST_COMPLETE).get();
if (!loggingCompletePromise.isDone()) {
loggingCompletePromise.setSuccess(null);
}
super.channelInactive(ctx);
}
}