Skip to content

Commit d801685

Browse files
committed
Merge branch 'master' into post-state-validators
2 parents ce2c05b + 00fb79b commit d801685

File tree

11 files changed

+136
-126
lines changed

11 files changed

+136
-126
lines changed

CHANGELOG.md

+1
Original file line numberDiff line numberDiff line change
@@ -13,5 +13,6 @@ the [releases page](https://github.com/Consensys/teku/releases).
1313

1414
### Additions and Improvements
1515
- Improve block rewards calculation performance for `/eth/v3/validator/blocks/{slot}` block production beacon node API.
16+
- Updated Javalin to v.6 (used by rest-api and keymanager-api).
1617

1718
### Bug Fixes

data/beaconrestapi/src/test/java/tech/pegasys/teku/beaconrestapi/handlers/v1/events/StubContext.java

+43-6
Original file line numberDiff line numberDiff line change
@@ -13,16 +13,22 @@
1313

1414
package tech.pegasys.teku.beaconrestapi.handlers.v1.events;
1515

16+
import io.javalin.config.Key;
1617
import io.javalin.http.Context;
1718
import io.javalin.http.HandlerType;
1819
import io.javalin.http.HttpStatus;
20+
import io.javalin.json.JsonMapper;
21+
import io.javalin.plugin.ContextPlugin;
22+
import io.javalin.security.RouteRole;
1923
import jakarta.servlet.ServletOutputStream;
2024
import jakarta.servlet.http.HttpServletRequest;
2125
import jakarta.servlet.http.HttpServletResponse;
2226
import java.io.InputStream;
2327
import java.util.Map;
28+
import java.util.Set;
2429
import java.util.concurrent.CompletableFuture;
2530
import java.util.function.Supplier;
31+
import java.util.stream.Stream;
2632
import org.jetbrains.annotations.NotNull;
2733
import org.jetbrains.annotations.Nullable;
2834

@@ -36,12 +42,6 @@ public StubContext(final HttpServletRequest req, final HttpServletResponse res)
3642
this.res = res;
3743
}
3844

39-
@Override
40-
@SuppressWarnings("TypeParameterUnusedInFormals")
41-
public <T> T appAttribute(@NotNull final String s) {
42-
return null;
43-
}
44-
4545
@NotNull
4646
@Override
4747
public String endpointHandlerPath() {
@@ -107,4 +107,41 @@ public Context result(@NotNull final InputStream inputStream) {
107107
public InputStream resultInputStream() {
108108
return null;
109109
}
110+
111+
@Override
112+
public <T> T appData(@NotNull Key<T> key) {
113+
return null;
114+
}
115+
116+
@NotNull
117+
@Override
118+
public JsonMapper jsonMapper() {
119+
return null;
120+
}
121+
122+
@NotNull
123+
@Override
124+
public Context minSizeForCompression(int i) {
125+
return null;
126+
}
127+
128+
@NotNull
129+
@Override
130+
public Set<RouteRole> routeRoles() {
131+
return null;
132+
}
133+
134+
@NotNull
135+
@Override
136+
public Context skipRemainingHandlers() {
137+
return null;
138+
}
139+
140+
@Override
141+
public <T> T with(@NotNull Class<? extends ContextPlugin<?, T>> aClass) {
142+
return null;
143+
}
144+
145+
@Override
146+
public void writeJsonStream(@NotNull Stream<?> stream) {}
110147
}

gradle/versions.gradle

+2-2
Original file line numberDiff line numberDiff line change
@@ -27,8 +27,8 @@ dependencyManagement {
2727

2828
dependency 'info.picocli:picocli:4.7.5'
2929

30-
dependency 'io.javalin:javalin:5.6.3'
31-
dependency 'io.javalin:javalin-rendering:5.6.2'
30+
dependency 'io.javalin:javalin:6.1.0'
31+
dependency 'io.javalin:javalin-rendering:6.1.0'
3232

3333
dependency 'io.libp2p:jvm-libp2p:1.1.0-RELEASE'
3434
dependency 'tech.pegasys:jblst:0.3.11'

infrastructure/restapi/src/main/java/tech/pegasys/teku/infrastructure/restapi/AuthorizationManager.java infrastructure/restapi/src/main/java/tech/pegasys/teku/infrastructure/restapi/AuthorizationHandler.java

+6-12
Original file line numberDiff line numberDiff line change
@@ -17,26 +17,23 @@
1717

1818
import io.javalin.http.Context;
1919
import io.javalin.http.Handler;
20-
import io.javalin.security.AccessManager;
21-
import io.javalin.security.RouteRole;
2220
import java.io.File;
2321
import java.io.IOException;
2422
import java.net.URLEncoder;
2523
import java.nio.charset.StandardCharsets;
2624
import java.nio.file.Files;
2725
import java.nio.file.Path;
28-
import java.util.Set;
2926
import java.util.stream.Stream;
3027
import org.apache.logging.log4j.LogManager;
3128
import org.apache.logging.log4j.Logger;
3229
import tech.pegasys.teku.infrastructure.exceptions.InvalidConfigurationException;
3330

34-
public class AuthorizationManager implements AccessManager {
31+
public class AuthorizationHandler implements Handler {
3532
private static final Logger LOG = LogManager.getLogger();
3633
private static final String BEARER_PREFIX = "Bearer ";
3734
private final String bearer;
3835

39-
public AuthorizationManager(final Path path) {
36+
public AuthorizationHandler(final Path path) {
4037
bearer = readPasswordFile(path);
4138
}
4239

@@ -88,13 +85,12 @@ private void unauthorized(final Context ctx, final String message) {
8885
LOG.debug(message);
8986
ctx.json("{\n \"status\": 401, \n \"message\":\"Unauthorized\"\n}");
9087
ctx.status(SC_UNAUTHORIZED);
88+
ctx.skipRemainingHandlers();
9189
}
9290

9391
@Override
94-
public void manage(final Handler handler, final Context ctx, final Set<? extends RouteRole> set)
95-
throws Exception {
96-
if (ctx.matchedPath().equals("/swagger-docs")) {
97-
handler.handle(ctx);
92+
public void handle(final Context ctx) throws Exception {
93+
if (ctx.path().startsWith("/swagger-") || ctx.path().startsWith("/webjars")) {
9894
return;
9995
}
10096

@@ -108,11 +104,9 @@ public void manage(final Handler handler, final Context ctx, final Set<? extends
108104
unauthorized(ctx, "API Reject - authorization bearer missing from request header");
109105
return;
110106
}
107+
111108
if (!auth.substring(BEARER_PREFIX.length()).equals(bearer)) {
112109
unauthorized(ctx, "API Reject - Unauthorized");
113-
return;
114110
}
115-
116-
handler.handle(ctx);
117111
}
118112
}

infrastructure/restapi/src/main/java/tech/pegasys/teku/infrastructure/restapi/RestApi.java

+2-2
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ protected SafeFuture<?> doStart() {
6464
// throwing it here will terminate the process effectively.
6565
LOG.error("Failed to start Rest API", e);
6666
throw e;
67-
} else if (app.jettyServer() == null || !app.jettyServer().started) {
67+
} else if (app.jettyServer() == null || !app.jettyServer().started()) {
6868
// failing to create the jetty server or start the jetty server is fatal.
6969
throw new IllegalStateException("Rest API failed to start", e);
7070
} else {
@@ -95,7 +95,7 @@ private void checkAccessFile(final Path path) {
9595
throw new IllegalStateException("Failed to initialise access file for validator-api.");
9696
}
9797
}
98-
app.updateConfig(config -> config.accessManager(new AuthorizationManager(path)));
98+
app.beforeMatched(new AuthorizationHandler(path));
9999
}
100100

101101
@Override

infrastructure/restapi/src/main/java/tech/pegasys/teku/infrastructure/restapi/RestApiBuilder.java

+9-10
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
import static tech.pegasys.teku.infrastructure.json.types.CoreTypes.HTTP_ERROR_RESPONSE_TYPE;
2121

2222
import io.javalin.Javalin;
23+
import io.javalin.compression.CompressionStrategy;
2324
import io.javalin.config.JavalinConfig;
2425
import io.javalin.plugin.bundled.CorsPluginConfig;
2526
import io.javalin.util.JavalinLogger;
@@ -131,14 +132,15 @@ public RestApi build() {
131132
config -> {
132133
config.http.defaultContentType = "application/json";
133134
config.showJavalinBanner = false;
134-
config.compression.gzipOnly();
135135
// Work around a bug in Javalin where it decides whether to compress on each call to
136136
// write which could result in a mix of compressed and uncompressed content
137137
// and means it doesn't evaluate the length of the response correctly.
138-
config.pvt.compressionStrategy.setMinSizeForCompression(0);
138+
CompressionStrategy strategy = CompressionStrategy.GZIP;
139+
strategy.setDefaultMinSizeForCompression(0);
140+
config.http.customCompression(strategy);
139141
configureCors(config);
140142
swaggerBuilder.configureUI(config);
141-
config.jetty.server(this::createJettyServer);
143+
config.jetty.modifyServer(this::modifyJettyServer);
142144
});
143145

144146
if (!hostAllowlist.isEmpty()) {
@@ -182,16 +184,15 @@ private void addExceptionHandler(
182184
private void configureCors(final JavalinConfig config) {
183185
if (!corsAllowedOrigins.isEmpty()) {
184186
if (corsAllowedOrigins.contains("*")) {
185-
config.plugins.enableCors(cors -> cors.add(CorsPluginConfig::anyHost));
187+
config.bundledPlugins.enableCors(cors -> cors.addRule(CorsPluginConfig.CorsRule::anyHost));
186188
} else {
187-
config.plugins.enableCors(
188-
cors -> corsAllowedOrigins.forEach(origin -> cors.add(it -> it.allowHost(origin))));
189+
config.bundledPlugins.enableCors(
190+
cors -> corsAllowedOrigins.forEach(origin -> cors.addRule(it -> it.allowHost(origin))));
189191
}
190192
}
191193
}
192194

193-
private Server createJettyServer() {
194-
final Server server = new Server();
195+
private void modifyJettyServer(final Server server) {
195196
final ServerConnector connector;
196197
if (maybeKeystorePath.isPresent()) {
197198
connector = new ServerConnector(server, getSslContextFactory());
@@ -201,7 +202,6 @@ private Server createJettyServer() {
201202
connector.setPort(port);
202203
connector.setHost(listenAddress);
203204
server.setConnectors(new Connector[] {connector});
204-
205205
maxUrlLength.ifPresent(
206206
maxLength -> {
207207
LOG.debug("Setting Max URL length to {}", maxLength);
@@ -215,7 +215,6 @@ private Server createJettyServer() {
215215
}
216216
});
217217
JavalinLogger.startupInfo = false;
218-
return server;
219218
}
220219

221220
private SslContextFactory.Server getSslContextFactory() {

infrastructure/restapi/src/main/java/tech/pegasys/teku/infrastructure/restapi/SwaggerUIBuilder.java

+24-2
Original file line numberDiff line numberDiff line change
@@ -17,10 +17,15 @@
1717
import io.javalin.config.JavalinConfig;
1818
import io.javalin.http.Handler;
1919
import io.javalin.http.staticfiles.Location;
20+
import io.javalin.rendering.template.JavalinThymeleaf;
2021
import java.util.HashMap;
2122
import java.util.Map;
2223
import java.util.Optional;
2324
import java.util.Set;
25+
import org.thymeleaf.TemplateEngine;
26+
import org.thymeleaf.templatemode.TemplateMode;
27+
import org.thymeleaf.templateresolver.ClassLoaderTemplateResolver;
28+
import org.thymeleaf.templateresolver.ITemplateResolver;
2429
import tech.pegasys.teku.infrastructure.restapi.openapi.OpenApiDocBuilder;
2530

2631
public class SwaggerUIBuilder {
@@ -41,7 +46,7 @@ public class SwaggerUIBuilder {
4146

4247
private static final Handler INDEX =
4348
(ctx) -> {
44-
Map<String, Object> model = new HashMap<>();
49+
final Map<String, Object> model = new HashMap<>();
4550
model.put("title", "Teku REST API");
4651
model.put("basePath", SWAGGER_HOSTED_PATH);
4752
ctx.render("index.html", model);
@@ -71,7 +76,8 @@ public void configureUI(final JavalinConfig config) {
7176
SWAGGER_HOSTED_PATH + SWAGGER_INITIALIZER_JS,
7277
SWAGGER_UI_PATCHED + SWAGGER_INITIALIZER_JS,
7378
Location.CLASSPATH);
74-
ThymeleafConfigurator.enableThymeleafTemplates(SWAGGER_UI_PATCHED + "/");
79+
config.fileRenderer(createThymeleafRenderer(SWAGGER_UI_PATCHED + "/"));
80+
7581
config.spaRoot.addHandler(SWAGGER_UI_PATH, INDEX);
7682
}
7783

@@ -84,4 +90,20 @@ public Optional<String> configureDocs(
8490
app.get(SWAGGER_DOCS_PATH, ctx -> ctx.json(apiDocs));
8591
return Optional.of(apiDocs);
8692
}
93+
94+
private JavalinThymeleaf createThymeleafRenderer(final String templatePath) {
95+
final TemplateEngine templateEngine = new TemplateEngine();
96+
templateEngine.addTemplateResolver(templateResolver(templatePath));
97+
return new JavalinThymeleaf(templateEngine);
98+
}
99+
100+
private ITemplateResolver templateResolver(final String prefix) {
101+
final ClassLoaderTemplateResolver templateResolver =
102+
new ClassLoaderTemplateResolver(Thread.currentThread().getContextClassLoader());
103+
templateResolver.setTemplateMode(TemplateMode.HTML);
104+
templateResolver.setPrefix(prefix);
105+
templateResolver.setSuffix(".html");
106+
templateResolver.setCharacterEncoding("UTF-8");
107+
return templateResolver;
108+
}
87109
}

infrastructure/restapi/src/main/java/tech/pegasys/teku/infrastructure/restapi/ThymeleafConfigurator.java

-49
This file was deleted.

infrastructure/restapi/src/main/java/tech/pegasys/teku/infrastructure/restapi/endpoints/JavalinEndpointAdapter.java

+2-1
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,8 @@ private JavalinEndpointAdapter(final RestApiEndpoint endpoint) {
2727

2828
public static void addEndpoint(final Javalin app, final RestApiEndpoint endpoint) {
2929
final EndpointMetadata metadata = endpoint.getMetadata();
30-
app.addHandler(metadata.getMethod(), metadata.getPath(), new JavalinEndpointAdapter(endpoint));
30+
app.addHttpHandler(
31+
metadata.getMethod(), metadata.getPath(), new JavalinEndpointAdapter(endpoint));
3132
}
3233

3334
@Override

infrastructure/restapi/src/main/java/tech/pegasys/teku/infrastructure/restapi/endpoints/JavalinRestApiRequest.java

+4
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,10 @@ public <T> Optional<T> getOptionalRequestBody() throws JsonProcessingException {
7777
public JavalinRestApiRequest(final Context context, final EndpointMetadata metadata) {
7878
this.context = context;
7979
this.metadata = metadata;
80+
// Work around a bug in Javalin where it decides whether to compress on each call to
81+
// write which could result in a mix of compressed and uncompressed content
82+
// and means it doesn't evaluate the length of the response correctly.
83+
this.context.minSizeForCompression(0);
8084
this.pathParamMap = context.pathParamMap();
8185
this.queryParamMap = context.queryParamMap();
8286
this.headerMap = context.headerMap();

0 commit comments

Comments
 (0)