From 12dbc57a2dbd173873a47eb1e200ff2bb44803d6 Mon Sep 17 00:00:00 2001 From: Francesco Nigro Date: Sat, 16 Nov 2024 11:17:40 +0100 Subject: [PATCH] Improve HTTP validation for inlining and specialized types --- .../io/vertx/core/http/impl/HttpUtils.java | 209 ++++++++++++++---- .../io/vertx/benchmarks/BenchmarkBase.java | 3 - .../HeadersValidationBenchmark.java | 109 +++++++++ 3 files changed, 278 insertions(+), 43 deletions(-) create mode 100644 src/test/benchmarks/io/vertx/benchmarks/HeadersValidationBenchmark.java diff --git a/src/main/java/io/vertx/core/http/impl/HttpUtils.java b/src/main/java/io/vertx/core/http/impl/HttpUtils.java index 238341a1dcc..a18664c6164 100644 --- a/src/main/java/io/vertx/core/http/impl/HttpUtils.java +++ b/src/main/java/io/vertx/core/http/impl/HttpUtils.java @@ -863,67 +863,181 @@ public static void validateHeader(CharSequence name, Iterable 0x7f) { + throw new IllegalArgumentException("a header name cannot contain non-ASCII character: " + value); + } + if (!VALID_H_NAME_ASCII_CHARS[c & 0x7F]) { + throw new IllegalArgumentException("a header name cannot contain some prohibited characters, such as : " + value); + } + } + } + + private static void validateSequenceHeaderName(CharSequence value) { for (int i = 0; i < value.length(); i++) { final char c = value.charAt(i); // Check to see if the character is not an ASCII character, or invalid diff --git a/src/test/benchmarks/io/vertx/benchmarks/BenchmarkBase.java b/src/test/benchmarks/io/vertx/benchmarks/BenchmarkBase.java index eb38d6cfba0..bca07d9335f 100644 --- a/src/test/benchmarks/io/vertx/benchmarks/BenchmarkBase.java +++ b/src/test/benchmarks/io/vertx/benchmarks/BenchmarkBase.java @@ -29,9 +29,6 @@ @Threads(1) @BenchmarkMode(Mode.Throughput) @Fork(value = 1, jvmArgs = { - "-XX:+UseBiasedLocking", - "-XX:BiasedLockingStartupDelay=0", - "-XX:+AggressiveOpts", "-Djmh.executor=CUSTOM", "-Djmh.executor.class=io.vertx.core.impl.VertxExecutorService" }) diff --git a/src/test/benchmarks/io/vertx/benchmarks/HeadersValidationBenchmark.java b/src/test/benchmarks/io/vertx/benchmarks/HeadersValidationBenchmark.java new file mode 100644 index 00000000000..d801be1192e --- /dev/null +++ b/src/test/benchmarks/io/vertx/benchmarks/HeadersValidationBenchmark.java @@ -0,0 +1,109 @@ +package io.vertx.benchmarks; + +import java.util.concurrent.TimeUnit; +import java.util.function.Consumer; + +import org.openjdk.jmh.annotations.Benchmark; +import org.openjdk.jmh.annotations.CompilerControl; +import org.openjdk.jmh.annotations.Measurement; +import org.openjdk.jmh.annotations.Param; +import org.openjdk.jmh.annotations.Scope; +import org.openjdk.jmh.annotations.Setup; +import org.openjdk.jmh.annotations.State; +import org.openjdk.jmh.annotations.Warmup; + +import io.netty.handler.codec.DefaultHeaders; +import io.netty.handler.codec.http.DefaultHttpHeadersFactory; +import io.vertx.core.http.impl.HttpUtils; + +@State(Scope.Thread) +@Warmup(iterations = 10, time = 1, timeUnit = TimeUnit.SECONDS) +@Measurement(iterations = 5, time = 400, timeUnit = TimeUnit.MILLISECONDS) +public class HeadersValidationBenchmark extends BenchmarkBase { + + @Param({ "true", "false" }) + public boolean ascii; + + private CharSequence[] headerNames; + private CharSequence[] headerValues; + private static final DefaultHeaders.NameValidator nettyNameValidator = DefaultHttpHeadersFactory.headersFactory().getNameValidator(); + private static final DefaultHeaders.ValueValidator nettyValueValidator = DefaultHttpHeadersFactory.headersFactory().getValueValidator(); + private static final Consumer vertxNameValidator = HttpUtils::validateHeaderName; + private static final Consumer vertxValueValidator = HttpUtils::validateHeaderValue; + + @Setup + public void setup() { + headerNames = new CharSequence[4]; + headerValues = new CharSequence[4]; + headerNames[0] = io.vertx.core.http.HttpHeaders.CONTENT_TYPE; + headerValues[0] = io.vertx.core.http.HttpHeaders.createOptimized("text/plain"); + headerNames[1] = io.vertx.core.http.HttpHeaders.CONTENT_LENGTH; + headerValues[1] = io.vertx.core.http.HttpHeaders.createOptimized("20"); + headerNames[2] = io.vertx.core.http.HttpHeaders.SERVER; + headerValues[2] = io.vertx.core.http.HttpHeaders.createOptimized("vert.x"); + headerNames[3] = io.vertx.core.http.HttpHeaders.DATE; + headerValues[3] = io.vertx.core.http.HttpHeaders.createOptimized(HeadersUtils.DATE_FORMAT.format(new java.util.Date(0))); + if (!ascii) { + for (int i = 0; i < headerNames.length; i++) { + headerNames[i] = headerNames[i].toString(); + } + } + if (!ascii) { + for (int i = 0; i < headerValues.length; i++) { + headerValues[i] = headerValues[i].toString(); + } + } + } + + @Benchmark + public void validateNameNetty() { + for (CharSequence headerName : headerNames) { + nettyNameValidation(headerName); + } + } + + @CompilerControl(CompilerControl.Mode.DONT_INLINE) + private void nettyNameValidation(CharSequence headerName) { + nettyNameValidator.validateName(headerName); + } + + @Benchmark + public void validateNameVertx() { + for (CharSequence headerName : headerNames) { + vertxNameValidation(headerName); + } + } + + @CompilerControl(CompilerControl.Mode.DONT_INLINE) + private void vertxNameValidation(CharSequence headerName) { + vertxNameValidator.accept(headerName); + } + + + @Benchmark + public void validateValueNetty() { + for (CharSequence headerValue : headerValues) { + nettyValueValidation(headerValue); + } + } + + @CompilerControl(CompilerControl.Mode.DONT_INLINE) + private void nettyValueValidation(CharSequence headerValue) { + nettyValueValidator.validate(headerValue); + } + + @Benchmark + public void validateValueVertx() { + for (CharSequence headerValue : headerValues) { + vertxValueValidation(headerValue); + } + } + + @CompilerControl(CompilerControl.Mode.DONT_INLINE) + private void vertxValueValidation(CharSequence headerValue) { + vertxValueValidator.accept(headerValue); + } + + + +}