diff --git a/jetty-jmh/src/main/java/org/eclipse/jetty/server/jmh/ForwardBenchmark.java b/jetty-jmh/src/main/java/org/eclipse/jetty/server/jmh/ForwardBenchmark.java new file mode 100644 index 00000000000..20660748b67 --- /dev/null +++ b/jetty-jmh/src/main/java/org/eclipse/jetty/server/jmh/ForwardBenchmark.java @@ -0,0 +1,196 @@ +// +// ======================================================================== +// Copyright (c) 1995-2019 Mort Bay Consulting Pty. Ltd. +// ------------------------------------------------------------------------ +// All rights reserved. This program and the accompanying materials +// are made available under the terms of the Eclipse Public License v1.0 +// and Apache License v2.0 which accompanies this distribution. +// +// The Eclipse Public License is available at +// http://www.eclipse.org/legal/epl-v10.html +// +// The Apache License v2.0 is available at +// http://www.opensource.org/licenses/apache2.0.php +// +// You may elect to redistribute this code under either of these licenses. +// ======================================================================== +// + +package org.eclipse.jetty.server.jmh; + +import java.io.IOException; +import java.lang.invoke.MethodHandle; +import java.lang.invoke.MethodHandles; +import java.lang.invoke.MethodType; +import java.util.Arrays; +import java.util.concurrent.ThreadLocalRandom; +import java.util.concurrent.TimeUnit; + +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.eclipse.jetty.http.HttpField; +import org.eclipse.jetty.http.HttpFields; +import org.eclipse.jetty.http.HttpHeader; +import org.eclipse.jetty.server.ForwardedRequestCustomizer; +import org.eclipse.jetty.server.HttpConfiguration; +import org.eclipse.jetty.server.HttpConnectionFactory; +import org.eclipse.jetty.server.LocalConnector; +import org.eclipse.jetty.server.Request; +import org.eclipse.jetty.server.Server; +import org.eclipse.jetty.server.handler.AbstractHandler; +import org.eclipse.jetty.util.ArrayTrie; +import org.eclipse.jetty.util.Trie; +import org.openjdk.jmh.annotations.Benchmark; +import org.openjdk.jmh.annotations.BenchmarkMode; +import org.openjdk.jmh.annotations.Measurement; +import org.openjdk.jmh.annotations.Mode; +import org.openjdk.jmh.annotations.Scope; +import org.openjdk.jmh.annotations.State; +import org.openjdk.jmh.annotations.Threads; +import org.openjdk.jmh.annotations.Warmup; +import org.openjdk.jmh.profile.GCProfiler; +import org.openjdk.jmh.runner.Runner; +import org.openjdk.jmh.runner.RunnerException; +import org.openjdk.jmh.runner.options.Options; +import org.openjdk.jmh.runner.options.OptionsBuilder; + +import static java.lang.invoke.MethodHandles.dropArguments; +import static java.lang.invoke.MethodHandles.foldArguments; +import static java.lang.invoke.MethodType.methodType; + + +@State(Scope.Benchmark) +@Threads(4) +@Warmup(iterations = 7, time = 500, timeUnit = TimeUnit.MILLISECONDS) +@Measurement(iterations = 7, time = 500, timeUnit = TimeUnit.MILLISECONDS) +public class ForwardBenchmark +{ + static HttpFields __fields = new HttpFields(); + static + { + Arrays.stream(new String[][] + { + {"accept", "*/*"}, + {"accept-encoding", "gzip, deflate, br"}, + {"accept-language", "en,en-AU;q=0.9,it;q=0.8"}, + {"content-length", "0"}, + {"content-type", "text/plain;charset=UTF-8"}, + {"cookie", "S=maestro=cH6W-eZ0JweknIgCwBxWD4FxQk647Uru:sso=HEiTd0qT5KdU7X6eL1m8snK1jNHwVJ9d"}, + {"origin", "https://www.google.com"}, + {"referer", "https://www.google.com/"}, + {"user-agent", "Mozilla/5.0"}, + {"x-client-data", "CLK1yQEIkLbJAQiltskBCKmdygEIoZ7KAQioo8oBCL+nygEI4qjKAQ=="}, + {"Forwarded", "for=192.0.2.43,for=198.51.100.17;by=203.0.113.60;proto=http;host=example.com"} + }).map(a->new HttpField(a[0],a[1])).forEach(__fields::add); + } + + public ForwardBenchmark() + { + } + + + + @Benchmark + @BenchmarkMode({ Mode.Throughput}) + public String testStringCompare() + { + String forwardedFor = null; + String forwardedHost = null; + String forwardedServer = null; + String forwarded = null; + + for (HttpField field : __fields) + { + String name = field.getName(); + if (HttpHeader.X_FORWARDED_FOR.asString().equalsIgnoreCase(name)) + forwardedFor = field.getValue(); + else if (HttpHeader.X_FORWARDED_HOST.asString().equalsIgnoreCase(name)) + forwardedHost = field.getValue(); + else if (HttpHeader.X_FORWARDED_SERVER.asString().equalsIgnoreCase(name)) + forwardedServer = field.getValue(); + else if (HttpHeader.FORWARDED.asString().equalsIgnoreCase(name)) + forwardedServer = field.getValue(); + } + + if (forwardedFor!=null) + return forwardedFor; + if (forwardedHost!=null) + return forwardedHost; + if (forwardedServer!=null) + return forwardedServer; + return forwarded; + } + + + + static class Forwarded + { + String host; + + public void getHost(HttpField field) + { + if (host==null) + host = field.getValue(); + } + } + + static Trie __handles = new ArrayTrie<>(1024); + static + { + MethodHandles.Lookup lookup = MethodHandles.lookup(); + MethodType type = methodType(Void.TYPE, HttpField.class); + + try + { + __handles.put(HttpHeader.X_FORWARDED_FOR.asString(), lookup.findVirtual(Forwarded.class, "getHost", type)); + __handles.put(HttpHeader.X_FORWARDED_HOST.asString(), lookup.findVirtual(Forwarded.class, "getHost", type)); + __handles.put(HttpHeader.X_FORWARDED_SERVER.asString(), lookup.findVirtual(Forwarded.class, "getHost", type)); + __handles.put(HttpHeader.FORWARDED.asString(), lookup.findVirtual(Forwarded.class, "getHost", type)); + } + catch (Exception e) + { + e.printStackTrace(); + } + } + + + @Benchmark + @BenchmarkMode({ Mode.Throughput}) + public String testTrieMethodHandle() + { + Forwarded forwarded = new Forwarded(); + + try + { + for (HttpField field : __fields) + { + MethodHandle handle = __handles.get(field.getName()); + if (handle != null) + handle.invoke(forwarded, field); + } + } + catch (Throwable e) + { + e.printStackTrace(); + } + + return forwarded.host; + } + + + public static void main(String[] args) throws RunnerException + { + Options opt = new OptionsBuilder() + .include(ForwardBenchmark.class.getSimpleName()) + .warmupIterations(20) + .measurementIterations(10) + .addProfiler(GCProfiler.class) + .forks(1) + .threads(100) + .build(); + + new Runner(opt).run(); + } +}