1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32 package io.netty.handler.codec.http2;
33
34 import io.netty.handler.codec.http.HttpHeaderNames;
35 import io.netty.handler.codec.http.HttpMethod;
36 import io.netty.handler.codec.http.HttpResponseStatus;
37 import io.netty.handler.codec.http2.Http2Headers.PseudoHeaderName;
38 import io.netty.util.AsciiString;
39 import io.netty.util.internal.PlatformDependent;
40
41 import java.util.Arrays;
42 import java.util.List;
43
44 import static io.netty.handler.codec.http2.HpackUtil.equalsVariableTime;
45
46 final class HpackStaticTable {
47
48 static final int NOT_FOUND = -1;
49
50
51
52 private static final List<HpackHeaderField> STATIC_TABLE = Arrays.asList(
53 newEmptyPseudoHeaderField(PseudoHeaderName.AUTHORITY),
54 newPseudoHeaderMethodField(HttpMethod.GET),
55 newPseudoHeaderMethodField(HttpMethod.POST),
56 newPseudoHeaderField(PseudoHeaderName.PATH, "/"),
57 newPseudoHeaderField(PseudoHeaderName.PATH, "/index.html"),
58 newPseudoHeaderField(PseudoHeaderName.SCHEME, "http"),
59 newPseudoHeaderField(PseudoHeaderName.SCHEME, "https"),
60 newPseudoHeaderField(PseudoHeaderName.STATUS, HttpResponseStatus.OK.codeAsText()),
61 newPseudoHeaderField(PseudoHeaderName.STATUS, HttpResponseStatus.NO_CONTENT.codeAsText()),
62 newPseudoHeaderField(PseudoHeaderName.STATUS, HttpResponseStatus.PARTIAL_CONTENT.codeAsText()),
63 newPseudoHeaderField(PseudoHeaderName.STATUS, HttpResponseStatus.NOT_MODIFIED.codeAsText()),
64 newPseudoHeaderField(PseudoHeaderName.STATUS, HttpResponseStatus.BAD_REQUEST.codeAsText()),
65 newPseudoHeaderField(PseudoHeaderName.STATUS, HttpResponseStatus.NOT_FOUND.codeAsText()),
66 newPseudoHeaderField(PseudoHeaderName.STATUS, HttpResponseStatus.INTERNAL_SERVER_ERROR.codeAsText()),
67 newEmptyHeaderField(HttpHeaderNames.ACCEPT_CHARSET),
68 newHeaderField(HttpHeaderNames.ACCEPT_ENCODING, "gzip, deflate"),
69 newEmptyHeaderField(HttpHeaderNames.ACCEPT_LANGUAGE),
70 newEmptyHeaderField(HttpHeaderNames.ACCEPT_RANGES),
71 newEmptyHeaderField(HttpHeaderNames.ACCEPT),
72 newEmptyHeaderField(HttpHeaderNames.ACCESS_CONTROL_ALLOW_ORIGIN),
73 newEmptyHeaderField(HttpHeaderNames.AGE),
74 newEmptyHeaderField(HttpHeaderNames.ALLOW),
75 newEmptyHeaderField(HttpHeaderNames.AUTHORIZATION),
76 newEmptyHeaderField(HttpHeaderNames.CACHE_CONTROL),
77 newEmptyHeaderField(HttpHeaderNames.CONTENT_DISPOSITION),
78 newEmptyHeaderField(HttpHeaderNames.CONTENT_ENCODING),
79 newEmptyHeaderField(HttpHeaderNames.CONTENT_LANGUAGE),
80 newEmptyHeaderField(HttpHeaderNames.CONTENT_LENGTH),
81 newEmptyHeaderField(HttpHeaderNames.CONTENT_LOCATION),
82 newEmptyHeaderField(HttpHeaderNames.CONTENT_RANGE),
83 newEmptyHeaderField(HttpHeaderNames.CONTENT_TYPE),
84 newEmptyHeaderField(HttpHeaderNames.COOKIE),
85 newEmptyHeaderField(HttpHeaderNames.DATE),
86 newEmptyHeaderField(HttpHeaderNames.ETAG),
87 newEmptyHeaderField(HttpHeaderNames.EXPECT),
88 newEmptyHeaderField(HttpHeaderNames.EXPIRES),
89 newEmptyHeaderField(HttpHeaderNames.FROM),
90 newEmptyHeaderField(HttpHeaderNames.HOST),
91 newEmptyHeaderField(HttpHeaderNames.IF_MATCH),
92 newEmptyHeaderField(HttpHeaderNames.IF_MODIFIED_SINCE),
93 newEmptyHeaderField(HttpHeaderNames.IF_NONE_MATCH),
94 newEmptyHeaderField(HttpHeaderNames.IF_RANGE),
95 newEmptyHeaderField(HttpHeaderNames.IF_UNMODIFIED_SINCE),
96 newEmptyHeaderField(HttpHeaderNames.LAST_MODIFIED),
97 newEmptyHeaderField("link"),
98 newEmptyHeaderField(HttpHeaderNames.LOCATION),
99 newEmptyHeaderField(HttpHeaderNames.MAX_FORWARDS),
100 newEmptyHeaderField(HttpHeaderNames.PROXY_AUTHENTICATE),
101 newEmptyHeaderField(HttpHeaderNames.PROXY_AUTHORIZATION),
102 newEmptyHeaderField(HttpHeaderNames.RANGE),
103 newEmptyHeaderField(HttpHeaderNames.REFERER),
104 newEmptyHeaderField("refresh"),
105 newEmptyHeaderField(HttpHeaderNames.RETRY_AFTER),
106 newEmptyHeaderField(HttpHeaderNames.SERVER),
107 newEmptyHeaderField(HttpHeaderNames.SET_COOKIE),
108 newEmptyHeaderField("strict-transport-security"),
109 newEmptyHeaderField(HttpHeaderNames.TRANSFER_ENCODING),
110 newEmptyHeaderField(HttpHeaderNames.USER_AGENT),
111 newEmptyHeaderField(HttpHeaderNames.VARY),
112 newEmptyHeaderField(HttpHeaderNames.VIA),
113 newEmptyHeaderField(HttpHeaderNames.WWW_AUTHENTICATE)
114 );
115
116 private static HpackHeaderField newEmptyHeaderField(AsciiString name) {
117 return new HpackHeaderField(name, AsciiString.EMPTY_STRING);
118 }
119
120 private static HpackHeaderField newEmptyHeaderField(String name) {
121 return new HpackHeaderField(AsciiString.cached(name), AsciiString.EMPTY_STRING);
122 }
123
124 private static HpackHeaderField newHeaderField(AsciiString name, String value) {
125 return new HpackHeaderField(name, AsciiString.cached(value));
126 }
127
128 private static HpackHeaderField newPseudoHeaderMethodField(HttpMethod method) {
129 return new HpackHeaderField(PseudoHeaderName.METHOD.value(), method.asciiName());
130 }
131
132 private static HpackHeaderField newPseudoHeaderField(PseudoHeaderName name, AsciiString value) {
133 return new HpackHeaderField(name.value(), value);
134 }
135
136 private static HpackHeaderField newPseudoHeaderField(PseudoHeaderName name, String value) {
137 return new HpackHeaderField(name.value(), AsciiString.cached(value));
138 }
139
140 private static HpackHeaderField newEmptyPseudoHeaderField(PseudoHeaderName name) {
141 return new HpackHeaderField(name.value(), AsciiString.EMPTY_STRING);
142 }
143
144
145 private static final int HEADER_NAMES_TABLE_SIZE = 1 << 9;
146
147 private static final int HEADER_NAMES_TABLE_SHIFT = PlatformDependent.BIG_ENDIAN_NATIVE_ORDER ? 22 : 18;
148
149
150 private static final HeaderNameIndex[] HEADER_NAMES = new HeaderNameIndex[HEADER_NAMES_TABLE_SIZE];
151 static {
152
153
154 for (int index = STATIC_TABLE.size(); index > 0; index--) {
155 HpackHeaderField entry = getEntry(index);
156 int bucket = headerNameBucket(entry.name);
157 HeaderNameIndex tableEntry = HEADER_NAMES[bucket];
158 if (tableEntry != null && !equalsVariableTime(tableEntry.name, entry.name)) {
159
160 throw new IllegalStateException("Hash bucket collision between " +
161 tableEntry.name + " and " + entry.name);
162 }
163 HEADER_NAMES[bucket] = new HeaderNameIndex(entry.name, index, entry.value.length() == 0);
164 }
165 }
166
167
168 private static final int HEADERS_WITH_NON_EMPTY_VALUES_TABLE_SIZE = 1 << 6;
169
170 private static final int HEADERS_WITH_NON_EMPTY_VALUES_TABLE_SHIFT =
171 PlatformDependent.BIG_ENDIAN_NATIVE_ORDER ? 0 : 6;
172
173
174 private static final HeaderIndex[] HEADERS_WITH_NON_EMPTY_VALUES =
175 new HeaderIndex[HEADERS_WITH_NON_EMPTY_VALUES_TABLE_SIZE];
176 static {
177 for (int index = STATIC_TABLE.size(); index > 0; index--) {
178 HpackHeaderField entry = getEntry(index);
179 if (entry.value.length() > 0) {
180 int bucket = headerBucket(entry.value);
181 HeaderIndex tableEntry = HEADERS_WITH_NON_EMPTY_VALUES[bucket];
182 if (tableEntry != null) {
183
184 throw new IllegalStateException("Hash bucket collision between " +
185 tableEntry.value + " and " + entry.value);
186 }
187 HEADERS_WITH_NON_EMPTY_VALUES[bucket] = new HeaderIndex(entry.name, entry.value, index);
188 }
189 }
190 }
191
192
193
194
195 static final int length = STATIC_TABLE.size();
196
197
198
199
200 static HpackHeaderField getEntry(int index) {
201 return STATIC_TABLE.get(index - 1);
202 }
203
204
205
206
207
208 static int getIndex(CharSequence name) {
209 HeaderNameIndex entry = getEntry(name);
210 return entry == null ? NOT_FOUND : entry.index;
211 }
212
213
214
215
216
217 static int getIndexInsensitive(CharSequence name, CharSequence value) {
218 if (value.length() == 0) {
219 HeaderNameIndex entry = getEntry(name);
220 return entry == null || !entry.emptyValue ? NOT_FOUND : entry.index;
221 }
222 int bucket = headerBucket(value);
223 HeaderIndex header = HEADERS_WITH_NON_EMPTY_VALUES[bucket];
224 if (header == null) {
225 return NOT_FOUND;
226 }
227 if (equalsVariableTime(header.name, name) && equalsVariableTime(header.value, value)) {
228 return header.index;
229 }
230 return NOT_FOUND;
231 }
232
233 private static HeaderNameIndex getEntry(CharSequence name) {
234 int bucket = headerNameBucket(name);
235 HeaderNameIndex entry = HEADER_NAMES[bucket];
236 if (entry == null) {
237 return null;
238 }
239 return equalsVariableTime(entry.name, name) ? entry : null;
240 }
241
242 private static int headerNameBucket(CharSequence name) {
243 return bucket(name, HEADER_NAMES_TABLE_SHIFT, HEADER_NAMES_TABLE_SIZE - 1);
244 }
245
246 private static int headerBucket(CharSequence value) {
247 return bucket(value, HEADERS_WITH_NON_EMPTY_VALUES_TABLE_SHIFT, HEADERS_WITH_NON_EMPTY_VALUES_TABLE_SIZE - 1);
248 }
249
250 private static int bucket(CharSequence s, int shift, int mask) {
251 return (AsciiString.hashCode(s) >> shift) & mask;
252 }
253
254 private static final class HeaderNameIndex {
255 final CharSequence name;
256 final int index;
257 final boolean emptyValue;
258
259 HeaderNameIndex(CharSequence name, int index, boolean emptyValue) {
260 this.name = name;
261 this.index = index;
262 this.emptyValue = emptyValue;
263 }
264 }
265
266 private static final class HeaderIndex {
267 final CharSequence name;
268 final CharSequence value;
269 final int index;
270
271 HeaderIndex(CharSequence name, CharSequence value, int index) {
272 this.name = name;
273 this.value = value;
274 this.index = index;
275 }
276 }
277
278
279 private HpackStaticTable() {
280 }
281 }