1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package org.jboss.netty.handler.codec.http;
17
18 import java.lang.reflect.Field;
19 import java.lang.reflect.Modifier;
20 import java.util.Arrays;
21 import java.util.Calendar;
22 import java.util.Date;
23 import java.util.HashSet;
24 import java.util.Iterator;
25 import java.util.LinkedHashSet;
26 import java.util.LinkedList;
27 import java.util.List;
28 import java.util.Map;
29 import java.util.Map.Entry;
30 import java.util.NoSuchElementException;
31 import java.util.Set;
32
33 public class DefaultHttpHeaders extends HttpHeaders {
34
35 private static final int BUCKET_SIZE = 17;
36
37 private static final Set<String> KNOWN_NAMES = createSet(Names.class);
38 private static final Set<String> KNOWN_VALUES = createSet(Values.class);
39
40 private static Set<String> createSet(Class<?> clazz) {
41 Set<String> set = new HashSet<String>();
42 Field[] fields = clazz.getDeclaredFields();
43
44 for (Field f: fields) {
45 int m = f.getModifiers();
46 if (Modifier.isPublic(m) && Modifier.isStatic(m) && Modifier.isFinal(m)
47 && f.getType().isAssignableFrom(String.class)) {
48 try {
49 set.add((String) f.get(null));
50 } catch (Throwable cause) {
51
52 }
53 }
54 }
55 return set;
56 }
57
58 private static int hash(String name, boolean validate) {
59 int h = 0;
60 for (int i = name.length() - 1; i >= 0; i --) {
61 char c = name.charAt(i);
62 if (validate) {
63 valideHeaderNameChar(c);
64 }
65 c = toLowerCase(c);
66 h = 31 * h + c;
67 }
68
69 if (h > 0) {
70 return h;
71 } else if (h == Integer.MIN_VALUE) {
72 return Integer.MAX_VALUE;
73 } else {
74 return -h;
75 }
76 }
77
78 private static boolean eq(String name1, String name2) {
79 if (name1 == name2) {
80
81 return true;
82 }
83 int nameLen = name1.length();
84 if (nameLen != name2.length()) {
85 return false;
86 }
87
88 for (int i = nameLen - 1; i >= 0; i --) {
89 char c1 = name1.charAt(i);
90 char c2 = name2.charAt(i);
91 if (c1 != c2) {
92 if (toLowerCase(c1) != toLowerCase(c2)) {
93 return false;
94 }
95 }
96 }
97 return true;
98 }
99
100 private static char toLowerCase(char c) {
101 if (c >= 'A' && c <= 'Z') {
102 c += 32;
103 }
104 return c;
105 }
106
107 private static int index(int hash) {
108 return hash % BUCKET_SIZE;
109 }
110
111 private final HeaderEntry[] entries = new HeaderEntry[BUCKET_SIZE];
112 private final HeaderEntry head = new HeaderEntry(-1, null, null);
113 protected final boolean validate;
114
115 public DefaultHttpHeaders() {
116 this(true);
117 }
118
119 public DefaultHttpHeaders(boolean validate) {
120 head.before = head.after = head;
121 this.validate = validate;
122 }
123
124 void validateHeaderValue0(String headerValue) {
125 if (KNOWN_VALUES.contains(headerValue)) {
126 return;
127 }
128 validateHeaderValue(headerValue);
129 }
130
131 @Override
132 public HttpHeaders add(final String name, final Object value) {
133 String strVal = toString(value);
134 boolean validateName = false;
135 if (validate) {
136 validateHeaderValue0(strVal);
137 validateName = !KNOWN_NAMES.contains(name);
138 }
139
140 int h = hash(name, validateName);
141 int i = index(h);
142 add0(h, i, name, strVal);
143 return this;
144 }
145
146 @Override
147 public HttpHeaders add(String name, Iterable<?> values) {
148 boolean validateName = false;
149 if (validate) {
150 validateName = !KNOWN_NAMES.contains(name);
151 }
152
153 int h = hash(name, validateName);
154 int i = index(h);
155 for (Object v: values) {
156 String vstr = toString(v);
157 if (validate) {
158 validateHeaderValue0(vstr);
159 }
160 add0(h, i, name, vstr);
161 }
162 return this;
163 }
164
165 private void add0(int h, int i, final String name, final String value) {
166
167 HeaderEntry e = entries[i];
168 HeaderEntry newEntry;
169 entries[i] = newEntry = new HeaderEntry(h, name, value);
170 newEntry.next = e;
171
172
173 newEntry.addBefore(head);
174 }
175
176 @Override
177 public HttpHeaders remove(final String name) {
178 if (name == null) {
179 throw new NullPointerException("name");
180 }
181 int h = hash(name, false);
182 int i = index(h);
183 remove0(h, i, name);
184 return this;
185 }
186
187 private void remove0(int h, int i, String name) {
188 HeaderEntry e = entries[i];
189 if (e == null) {
190 return;
191 }
192
193 for (;;) {
194 if (e.hash == h && eq(name, e.key)) {
195 e.remove();
196 HeaderEntry next = e.next;
197 if (next != null) {
198 entries[i] = next;
199 e = next;
200 } else {
201 entries[i] = null;
202 return;
203 }
204 } else {
205 break;
206 }
207 }
208
209 for (;;) {
210 HeaderEntry next = e.next;
211 if (next == null) {
212 break;
213 }
214 if (next.hash == h && eq(name, next.key)) {
215 e.next = next.next;
216 next.remove();
217 } else {
218 e = next;
219 }
220 }
221 }
222
223 @Override
224 public HttpHeaders set(final String name, final Object value) {
225 String strVal = toString(value);
226 boolean validateName = false;
227 if (validate) {
228 validateHeaderValue0(strVal);
229 validateName = !KNOWN_NAMES.contains(name);
230 }
231
232 int h = hash(name, validateName);
233 int i = index(h);
234 remove0(h, i, name);
235 add0(h, i, name, strVal);
236 return this;
237 }
238
239 @Override
240 public HttpHeaders set(final String name, final Iterable<?> values) {
241 if (values == null) {
242 throw new NullPointerException("values");
243 }
244
245 boolean validateName = false;
246 if (validate) {
247 validateName = !KNOWN_NAMES.contains(name);
248 }
249
250 int h = hash(name, validateName);
251 int i = index(h);
252
253 remove0(h, i, name);
254 for (Object v: values) {
255 if (v == null) {
256 break;
257 }
258 String strVal = toString(v);
259 if (validate) {
260 validateHeaderValue0(strVal);
261 }
262 add0(h, i, name, strVal);
263 }
264
265 return this;
266 }
267
268 @Override
269 public HttpHeaders clear() {
270 Arrays.fill(entries, null);
271 head.before = head.after = head;
272 return this;
273 }
274
275 @Override
276 public String get(final String name) {
277 return get(name, false);
278 }
279
280 private String get(final String name, boolean last) {
281 if (name == null) {
282 throw new NullPointerException("name");
283 }
284
285 int h = hash(name, false);
286 int i = index(h);
287 HeaderEntry e = entries[i];
288 String value = null;
289
290 while (e != null) {
291 if (e.hash == h && eq(name, e.key)) {
292 value = e.value;
293 if (last) {
294 break;
295 }
296 }
297
298 e = e.next;
299 }
300 return value;
301 }
302
303 @Override
304 public List<String> getAll(final String name) {
305 if (name == null) {
306 throw new NullPointerException("name");
307 }
308
309 LinkedList<String> values = new LinkedList<String>();
310
311 int h = hash(name, false);
312 int i = index(h);
313 HeaderEntry e = entries[i];
314 while (e != null) {
315 if (e.hash == h && eq(name, e.key)) {
316 values.addFirst(e.value);
317 }
318 e = e.next;
319 }
320 return values;
321 }
322
323 @Override
324 public List<Map.Entry<String, String>> entries() {
325 List<Map.Entry<String, String>> all =
326 new LinkedList<Map.Entry<String, String>>();
327
328 HeaderEntry e = head.after;
329 while (e != head) {
330 all.add(e);
331 e = e.after;
332 }
333 return all;
334 }
335
336 public Iterator<Map.Entry<String, String>> iterator() {
337 return new HeaderIterator();
338 }
339
340 @Override
341 public boolean contains(String name) {
342 return get(name, true) != null;
343 }
344
345 @Override
346 public boolean isEmpty() {
347 return head == head.after;
348 }
349
350 @Override
351 public boolean contains(String name, String value, boolean ignoreCaseValue) {
352 if (name == null) {
353 throw new NullPointerException("name");
354 }
355
356 int h = hash(name, false);
357 int i = index(h);
358 HeaderEntry e = entries[i];
359 while (e != null) {
360 if (e.hash == h && eq(name, e.key)) {
361 if (ignoreCaseValue) {
362 if (e.value.equalsIgnoreCase(value)) {
363 return true;
364 }
365 } else {
366 if (e.value.equals(value)) {
367 return true;
368 }
369 }
370 }
371 e = e.next;
372 }
373 return false;
374 }
375
376 @Override
377 public Set<String> names() {
378 Set<String> names = new LinkedHashSet<String>();
379 HeaderEntry e = head.after;
380 while (e != head) {
381 names.add(e.key);
382 e = e.after;
383 }
384 return names;
385 }
386
387 private static String toString(Object value) {
388 if (value == null) {
389 return null;
390 }
391 if (value instanceof String) {
392 return (String) value;
393 }
394 if (value instanceof Number) {
395 return value.toString();
396 }
397 if (value instanceof Date) {
398 return HttpHeaderDateFormat.get().format((Date) value);
399 }
400 if (value instanceof Calendar) {
401 return HttpHeaderDateFormat.get().format(((Calendar) value).getTime());
402 }
403 return value.toString();
404 }
405
406 private final class HeaderIterator implements Iterator<Map.Entry<String, String>> {
407
408 private HeaderEntry current = head;
409
410 public boolean hasNext() {
411 return current.after != head;
412 }
413
414 public Entry<String, String> next() {
415 current = current.after;
416
417 if (current == head) {
418 throw new NoSuchElementException();
419 }
420
421 return current;
422 }
423
424 public void remove() {
425 throw new UnsupportedOperationException();
426 }
427 }
428
429 private final class HeaderEntry implements Map.Entry<String, String> {
430 final int hash;
431 final String key;
432 String value;
433 HeaderEntry next;
434 HeaderEntry before, after;
435
436 HeaderEntry(int hash, String key, String value) {
437 this.hash = hash;
438 this.key = key;
439 this.value = value;
440 }
441
442 void remove() {
443 before.after = after;
444 after.before = before;
445 }
446
447 void addBefore(HeaderEntry e) {
448 after = e;
449 before = e.before;
450 before.after = this;
451 after.before = this;
452 }
453
454 public String getKey() {
455 return key;
456 }
457
458 public String getValue() {
459 return value;
460 }
461
462 public String setValue(String value) {
463 if (value == null) {
464 throw new NullPointerException("value");
465 }
466 if (validate) {
467 validateHeaderValue0(value);
468 }
469 String oldValue = this.value;
470 this.value = value;
471 return oldValue;
472 }
473
474 @Override
475 public String toString() {
476 return key + '=' + value;
477 }
478 }
479 }