Skip to content
Open
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
96 changes: 94 additions & 2 deletions core/src/main/java/tech/pegasys/web3signer/core/Runner.java
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,8 @@
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;

import io.netty.handler.codec.http.HttpHeaderValues;
import io.vertx.core.Handler;
Expand Down Expand Up @@ -79,11 +81,16 @@
public static final String UPCHECK_PATH = "/upcheck";

private static final Logger LOG = LogManager.getLogger();
private static final long HTTP_SHUTDOWN_TIMEOUT_SECONDS = 20;

protected final BaseConfig baseConfig;

private HealthCheckHandler healthCheckHandler;
private final List<Closeable> closeables = new ArrayList<>();
private final Object httpShutdownMonitor = new Object();
private final AtomicInteger inFlightHttpRequests = new AtomicInteger();
private final AtomicBoolean shuttingDown = new AtomicBoolean(false);
private volatile HttpServer httpServer;

protected Runner(final BaseConfig baseConfig) {
this.baseConfig = baseConfig;
Expand Down Expand Up @@ -179,7 +186,7 @@

populateRouter(context);

final HttpServer httpServer = createServerAndWait(vertx, router);
httpServer = createServerAndWait(vertx, router);
final String tlsStatus = baseConfig.getTlsOptions().isPresent() ? "enabled" : "disabled";
LOG.info(
"Web3Signer has started with TLS {}, and ready to handle signing requests on {}:{}",
Expand All @@ -204,6 +211,84 @@
}
}

private void gracefulHttpShutdown() {
shuttingDown.set(true);
waitForInFlightRequestsToComplete();

final CountDownLatch latch = new CountDownLatch(1);
httpServer.close(res -> latch.countDown());
try {
latch.await(HTTP_SHUTDOWN_TIMEOUT_SECONDS + 5, TimeUnit.SECONDS);

Check warning on line 221 in core/src/main/java/tech/pegasys/web3signer/core/Runner.java

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Do something with the "boolean" value returned by "await".

See more on https://sonarcloud.io/project/issues?id=ConsenSys_web3signer&issues=AZ9c26XX7Ck9GhP6XbjA&open=AZ9c26XX7Ck9GhP6XbjA&pullRequest=1210
} catch (final InterruptedException e) {

Check warning on line 222 in core/src/main/java/tech/pegasys/web3signer/core/Runner.java

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Replace "e" with an unnamed pattern.

See more on https://sonarcloud.io/project/issues?id=ConsenSys_web3signer&issues=AZ9c26XX7Ck9GhP6XbjB&open=AZ9c26XX7Ck9GhP6XbjB&pullRequest=1210
Thread.currentThread().interrupt();
LOG.warn("Interrupted while waiting for HTTP server to drain connections");
}
}

private void waitForInFlightRequestsToComplete() {
final long deadlineNanos =
System.nanoTime() + TimeUnit.SECONDS.toNanos(HTTP_SHUTDOWN_TIMEOUT_SECONDS);
synchronized (httpShutdownMonitor) {
while (inFlightHttpRequests.get() > 0) {
final long remainingNanos = deadlineNanos - System.nanoTime();
if (remainingNanos <= 0) {
LOG.warn(
"Timed out waiting for {} in-flight HTTP request(s) to complete before shutdown",
inFlightHttpRequests.get());
return;
}
try {
TimeUnit.NANOSECONDS.timedWait(httpShutdownMonitor, remainingNanos);
} catch (final InterruptedException e) {

Check warning on line 242 in core/src/main/java/tech/pegasys/web3signer/core/Runner.java

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Replace "e" with an unnamed pattern.

See more on https://sonarcloud.io/project/issues?id=ConsenSys_web3signer&issues=AZ9c26XX7Ck9GhP6XbjC&open=AZ9c26XX7Ck9GhP6XbjC&pullRequest=1210
Thread.currentThread().interrupt();
LOG.warn("Interrupted while waiting for in-flight HTTP requests to complete");
return;
}
}
}
}
Comment thread
cursor[bot] marked this conversation as resolved.

private void decrementInFlightRequestCount() {
final int remainingRequests =
inFlightHttpRequests.updateAndGet(current -> Math.max(0, current - 1));
if (remainingRequests == 0) {
synchronized (httpShutdownMonitor) {
httpShutdownMonitor.notifyAll();
}
}
}

private Handler<HttpServerRequest> trackInFlightRequests(
final Handler<HttpServerRequest> requestHandler) {
return request -> {
if (shuttingDown.get()) {
request.response().setStatusCode(503).end();
return;
}
Comment thread
cursor[bot] marked this conversation as resolved.
inFlightHttpRequests.incrementAndGet();

final AtomicBoolean requestCompleted = new AtomicBoolean(false);
final Runnable completeRequest =
() -> {
if (requestCompleted.compareAndSet(false, true)) {
decrementInFlightRequestCount();
}
};

request.exceptionHandler(error -> completeRequest.run());
request.response().exceptionHandler(error -> completeRequest.run());
request.response().closeHandler(unused -> completeRequest.run());
request.response().endHandler(unused -> completeRequest.run());

try {
requestHandler.handle(request);
} catch (final RuntimeException | Error e) {

Check warning on line 285 in core/src/main/java/tech/pegasys/web3signer/core/Runner.java

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Replace "e" with an unnamed pattern.

See more on https://sonarcloud.io/project/issues?id=ConsenSys_web3signer&issues=AZ9c26XX7Ck9GhP6XbjD&open=AZ9c26XX7Ck9GhP6XbjD&pullRequest=1210
completeRequest.run();
throw e;
}
};
}

private void shutdownVertx(final Vertx vertx) {
final CountDownLatch vertxShutdownLatch = new CountDownLatch(1);
vertx.close((res) -> vertxShutdownLatch.countDown());
Expand Down Expand Up @@ -283,10 +368,10 @@
.setReuseAddress(true)
.setReusePort(true);
final HttpServerOptions tlsServerOptions = applyConfigTlsSettingsTo(serverOptions);
final HttpServer httpServer = vertx.createHttpServer(tlsServerOptions);

Check warning on line 371 in core/src/main/java/tech/pegasys/web3signer/core/Runner.java

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Rename "httpServer" which hides the field declared at line 93.

See more on https://sonarcloud.io/project/issues?id=ConsenSys_web3signer&issues=AZ9c26XX7Ck9GhP6XbjE&open=AZ9c26XX7Ck9GhP6XbjE&pullRequest=1210
final CompletableFuture<Void> serverRunningFuture = new CompletableFuture<>();
httpServer
.requestHandler(requestHandler)
.requestHandler(trackInFlightRequests(requestHandler))
.listen(
result -> {
if (result.succeeded()) {
Expand Down Expand Up @@ -408,6 +493,13 @@

@Override
public void close() throws Exception {
if (httpServer != null) {
try {
gracefulHttpShutdown();
} catch (final Exception e) {
LOG.error("Failed to gracefully shut down HTTP server", e);
}
}
for (Closeable closeable : closeables) {
try {
closeable.close();
Expand Down
Loading
Loading