1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package io.netty.handler.codec.http2;
17
18 import io.netty.handler.codec.http.DefaultHttpHeaders;
19 import io.netty.handler.codec.http.DefaultHttpRequest;
20 import io.netty.handler.codec.http.HttpHeaderNames;
21 import io.netty.handler.codec.http.HttpHeaders;
22 import io.netty.handler.codec.http.HttpMethod;
23 import io.netty.handler.codec.http.HttpRequest;
24 import io.netty.handler.codec.http.HttpScheme;
25 import io.netty.handler.codec.http.HttpUtil;
26 import io.netty.handler.codec.http.HttpVersion;
27 import io.netty.microbench.util.AbstractMicrobenchmark;
28 import io.netty.util.AsciiString;
29 import org.openjdk.jmh.annotations.Benchmark;
30 import org.openjdk.jmh.annotations.BenchmarkMode;
31 import org.openjdk.jmh.annotations.Measurement;
32 import org.openjdk.jmh.annotations.Mode;
33 import org.openjdk.jmh.annotations.OutputTimeUnit;
34 import org.openjdk.jmh.annotations.Param;
35 import org.openjdk.jmh.annotations.Scope;
36 import org.openjdk.jmh.annotations.Setup;
37 import org.openjdk.jmh.annotations.State;
38 import org.openjdk.jmh.annotations.Warmup;
39 import org.openjdk.jmh.infra.Blackhole;
40
41 import java.net.URI;
42 import java.util.concurrent.TimeUnit;
43
44 import static io.netty.util.internal.StringUtil.isNullOrEmpty;
45
46 @State(Scope.Benchmark)
47 @Warmup(iterations = 5, time = 200, timeUnit = TimeUnit.MILLISECONDS)
48 @Measurement(iterations = 5, time = 200, timeUnit = TimeUnit.MILLISECONDS)
49 @BenchmarkMode(Mode.AverageTime)
50 @OutputTimeUnit(TimeUnit.NANOSECONDS)
51 public class Http2RequestTargetConversionBenchmark extends AbstractMicrobenchmark {
52
53 @Param
54 public RequestTargetType requestTargetType;
55
56 private HttpRequest request;
57
58 @Setup
59 public void setup() {
60 request = new DefaultHttpRequest(
61 HttpVersion.HTTP_1_1,
62 HttpMethod.GET,
63 requestTargetType.requestTarget,
64 new DefaultHttpHeaders(),
65 false);
66 request.headers().set(HttpConversionUtil.ExtensionHeaderNames.SCHEME.text(), HttpScheme.HTTP.name());
67 }
68
69 @Benchmark
70 public void newConversion(Blackhole bh) {
71 bh.consume(HttpConversionUtil.toHttp2Headers(request, false));
72 }
73
74 @Benchmark
75 public void oldUriConversion(Blackhole bh) {
76 bh.consume(oldToHttp2Headers(request));
77 }
78
79 public enum RequestTargetType {
80 ORIGIN("/orders/123/items?expand=details"),
81 ABSOLUTE("http://example.com/orders/123/items?expand=details#section"),
82 ABSOLUTE_NO_PATH("http://example.com?next=/home#section"),
83 ABSOLUTE_NO_AUTHORITY("http://?x=1#frag"),
84 SCHEME_ONLY_ABSOLUTE_PATH("http:/orders/123/items?expand=details");
85
86 final String requestTarget;
87
88 RequestTargetType(String requestTarget) {
89 this.requestTarget = requestTarget;
90 }
91 }
92
93 private static Http2Headers oldToHttp2Headers(final HttpRequest request) {
94 HttpHeaders inHeaders = request.headers();
95 Http2Headers out = new DefaultHttp2Headers(false, inHeaders.size());
96 String host = inHeaders.getAsString(HttpHeaderNames.HOST);
97 if (HttpUtil.isOriginForm(request.uri()) || HttpUtil.isAsteriskForm(request.uri())) {
98 out.path(new AsciiString(request.uri()));
99 oldSetHttp2Scheme(inHeaders, URI.create(""), out);
100 } else {
101 URI requestTargetUri = URI.create(request.uri());
102 out.path(oldToHttp2Path(requestTargetUri));
103 host = isNullOrEmpty(host) ? requestTargetUri.getAuthority() : host;
104 oldSetHttp2Scheme(inHeaders, requestTargetUri, out);
105 }
106 HttpConversionUtil.setHttp2Authority(host, out);
107 out.method(request.method().asciiName());
108 HttpConversionUtil.toHttp2Headers(inHeaders, out);
109 return out;
110 }
111
112 private static AsciiString oldToHttp2Path(final URI uri) {
113 StringBuilder pathBuilder = new StringBuilder();
114 if (!isNullOrEmpty(uri.getRawPath())) {
115 pathBuilder.append(uri.getRawPath());
116 }
117 if (!isNullOrEmpty(uri.getRawQuery())) {
118 pathBuilder.append('?');
119 pathBuilder.append(uri.getRawQuery());
120 }
121 if (!isNullOrEmpty(uri.getRawFragment())) {
122 pathBuilder.append('#');
123 pathBuilder.append(uri.getRawFragment());
124 }
125 String path = pathBuilder.toString();
126 return path.isEmpty() ? new AsciiString("/") : new AsciiString(path);
127 }
128
129 private static void oldSetHttp2Scheme(final HttpHeaders in, final URI uri, final Http2Headers out) {
130 String value = uri.getScheme();
131 if (!isNullOrEmpty(value)) {
132 out.scheme(new AsciiString(value));
133 return;
134 }
135
136 CharSequence cValue = in.get(HttpConversionUtil.ExtensionHeaderNames.SCHEME.text());
137 if (cValue != null) {
138 out.scheme(AsciiString.of(cValue));
139 return;
140 }
141
142 if (uri.getPort() == HttpScheme.HTTPS.port()) {
143 out.scheme(HttpScheme.HTTPS.name());
144 } else if (uri.getPort() == HttpScheme.HTTP.port()) {
145 out.scheme(HttpScheme.HTTP.name());
146 } else {
147 throw new IllegalArgumentException(
148 ":scheme must be specified. see https://tools.ietf.org/html/rfc7540#section-8.1.2.3");
149 }
150 }
151 }