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