1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package org.jboss.netty.handler.codec.spdy;
17
18 import java.util.LinkedList;
19 import java.util.List;
20 import java.util.Map;
21 import java.util.Set;
22 import java.util.TreeSet;
23
24 import org.jboss.netty.handler.codec.http.HttpMethod;
25 import org.jboss.netty.handler.codec.http.HttpResponseStatus;
26 import org.jboss.netty.handler.codec.http.HttpVersion;
27
28
29
30
31
32
33 public class SpdyHeaders {
34
35
36
37
38
39 public static final class HttpNames {
40
41
42
43 public static final String HOST = ":host";
44
45
46
47 public static final String METHOD = ":method";
48
49
50
51 public static final String PATH = ":path";
52
53
54
55 public static final String SCHEME = ":scheme";
56
57
58
59 public static final String STATUS = ":status";
60
61
62
63 public static final String VERSION = ":version";
64
65 private HttpNames() {
66 super();
67 }
68 }
69
70
71
72
73
74 public static final class Spdy2HttpNames {
75
76
77
78 public static final String METHOD = "method";
79
80
81
82 public static final String SCHEME = "scheme";
83
84
85
86 public static final String STATUS = "status";
87
88
89
90 public static final String URL = "url";
91
92
93
94 public static final String VERSION = "version";
95
96 private Spdy2HttpNames() {
97 super();
98 }
99 }
100
101
102
103
104
105
106
107
108
109 public static String getHeader(SpdyHeaderBlock block, String name) {
110 return block.getHeader(name);
111 }
112
113
114
115
116
117
118
119
120
121 public static String getHeader(SpdyHeaderBlock block, String name, String defaultValue) {
122 String value = block.getHeader(name);
123 if (value == null) {
124 return defaultValue;
125 }
126 return value;
127 }
128
129
130
131
132
133 public static void setHeader(SpdyHeaderBlock block, String name, Object value) {
134 block.setHeader(name, value);
135 }
136
137
138
139
140
141 public static void setHeader(SpdyHeaderBlock block, String name, Iterable<?> values) {
142 block.setHeader(name, values);
143 }
144
145
146
147
148 public static void addHeader(SpdyHeaderBlock block, String name, Object value) {
149 block.addHeader(name, value);
150 }
151
152
153
154
155 public static void removeHost(SpdyHeaderBlock block) {
156 block.removeHeader(HttpNames.HOST);
157 }
158
159
160
161
162 public static String getHost(SpdyHeaderBlock block) {
163 return block.getHeader(HttpNames.HOST);
164 }
165
166
167
168
169 public static void setHost(SpdyHeaderBlock block, String host) {
170 block.setHeader(HttpNames.HOST, host);
171 }
172
173
174
175
176 @Deprecated
177 public static void removeMethod(SpdyHeaderBlock block) {
178 removeMethod(2, block);
179 }
180
181
182
183
184 public static void removeMethod(int spdyVersion, SpdyHeaderBlock block) {
185 if (spdyVersion < 3) {
186 block.removeHeader(Spdy2HttpNames.METHOD);
187 } else {
188 block.removeHeader(HttpNames.METHOD);
189 }
190 }
191
192
193
194
195 @Deprecated
196 public static HttpMethod getMethod(SpdyHeaderBlock block) {
197 return getMethod(2, block);
198 }
199
200
201
202
203 public static HttpMethod getMethod(int spdyVersion, SpdyHeaderBlock block) {
204 try {
205 if (spdyVersion < 3) {
206 return HttpMethod.valueOf(block.getHeader(Spdy2HttpNames.METHOD));
207 } else {
208 return HttpMethod.valueOf(block.getHeader(HttpNames.METHOD));
209 }
210 } catch (Exception e) {
211 return null;
212 }
213 }
214
215
216
217
218 @Deprecated
219 public static void setMethod(SpdyHeaderBlock block, HttpMethod method) {
220 setMethod(2, block, method);
221 }
222
223
224
225
226 public static void setMethod(int spdyVersion, SpdyHeaderBlock block, HttpMethod method) {
227 if (spdyVersion < 3) {
228 block.setHeader(Spdy2HttpNames.METHOD, method.getName());
229 } else {
230 block.setHeader(HttpNames.METHOD, method.getName());
231 }
232 }
233
234
235
236
237 @Deprecated
238 public static void removeScheme(SpdyHeaderBlock block) {
239 removeMethod(2, block);
240 }
241
242
243
244
245 public static void removeScheme(int spdyVersion, SpdyHeaderBlock block) {
246 if (spdyVersion < 2) {
247 block.removeHeader(Spdy2HttpNames.SCHEME);
248 } else {
249 block.removeHeader(HttpNames.SCHEME);
250 }
251 }
252
253
254
255
256 @Deprecated
257 public static String getScheme(SpdyHeaderBlock block) {
258 return getScheme(2, block);
259 }
260
261
262
263
264 public static String getScheme(int spdyVersion, SpdyHeaderBlock block) {
265 if (spdyVersion < 3) {
266 return block.getHeader(Spdy2HttpNames.SCHEME);
267 } else {
268 return block.getHeader(HttpNames.SCHEME);
269 }
270 }
271
272
273
274
275 @Deprecated
276 public static void setScheme(SpdyHeaderBlock block, String scheme) {
277 setScheme(2, block, scheme);
278 }
279
280
281
282
283 public static void setScheme(int spdyVersion, SpdyHeaderBlock block, String scheme) {
284 if (spdyVersion < 3) {
285 block.setHeader(Spdy2HttpNames.SCHEME, scheme);
286 } else {
287 block.setHeader(HttpNames.SCHEME, scheme);
288 }
289 }
290
291
292
293
294 @Deprecated
295 public static void removeStatus(SpdyHeaderBlock block) {
296 removeMethod(2, block);
297 }
298
299
300
301
302 public static void removeStatus(int spdyVersion, SpdyHeaderBlock block) {
303 if (spdyVersion < 3) {
304 block.removeHeader(Spdy2HttpNames.STATUS);
305 } else {
306 block.removeHeader(HttpNames.STATUS);
307 }
308 }
309
310
311
312
313 @Deprecated
314 public static HttpResponseStatus getStatus(SpdyHeaderBlock block) {
315 return getStatus(2, block);
316 }
317
318
319
320
321 public static HttpResponseStatus getStatus(int spdyVersion, SpdyHeaderBlock block) {
322 try {
323 String status;
324 if (spdyVersion < 3) {
325 status = block.getHeader(Spdy2HttpNames.STATUS);
326 } else {
327 status = block.getHeader(HttpNames.STATUS);
328 }
329 int space = status.indexOf(' ');
330 if (space == -1) {
331 return HttpResponseStatus.valueOf(Integer.parseInt(status));
332 } else {
333 int code = Integer.parseInt(status.substring(0, space));
334 String reasonPhrase = status.substring(space + 1);
335 HttpResponseStatus responseStatus = HttpResponseStatus.valueOf(code);
336 if (responseStatus.getReasonPhrase().equals(reasonPhrase)) {
337 return responseStatus;
338 } else {
339 return new HttpResponseStatus(code, reasonPhrase);
340 }
341 }
342 } catch (Exception e) {
343 return null;
344 }
345 }
346
347
348
349
350 @Deprecated
351 public static void setStatus(SpdyHeaderBlock block, HttpResponseStatus status) {
352 setStatus(2, block, status);
353 }
354
355
356
357
358 public static void setStatus(int spdyVersion, SpdyHeaderBlock block, HttpResponseStatus status) {
359 if (spdyVersion < 3) {
360 block.setHeader(Spdy2HttpNames.STATUS, status.toString());
361 } else {
362 block.setHeader(HttpNames.STATUS, status.toString());
363 }
364 }
365
366
367
368
369 @Deprecated
370 public static void removeUrl(SpdyHeaderBlock block) {
371 removeUrl(2, block);
372 }
373
374
375
376
377 public static void removeUrl(int spdyVersion, SpdyHeaderBlock block) {
378 if (spdyVersion < 3) {
379 block.removeHeader(Spdy2HttpNames.URL);
380 } else {
381 block.removeHeader(HttpNames.PATH);
382 }
383 }
384
385
386
387
388 @Deprecated
389 public static String getUrl(SpdyHeaderBlock block) {
390 return getUrl(2, block);
391 }
392
393
394
395
396 public static String getUrl(int spdyVersion, SpdyHeaderBlock block) {
397 if (spdyVersion < 3) {
398 return block.getHeader(Spdy2HttpNames.URL);
399 } else {
400 return block.getHeader(HttpNames.PATH);
401 }
402 }
403
404
405
406
407 @Deprecated
408 public static void setUrl(SpdyHeaderBlock block, String path) {
409 setUrl(2, block, path);
410 }
411
412
413
414
415 public static void setUrl(int spdyVersion, SpdyHeaderBlock block, String path) {
416 if (spdyVersion < 3) {
417 block.setHeader(Spdy2HttpNames.URL, path);
418 } else {
419 block.setHeader(HttpNames.PATH, path);
420 }
421 }
422
423
424
425
426 @Deprecated
427 public static void removeVersion(SpdyHeaderBlock block) {
428 removeVersion(2, block);
429 }
430
431
432
433
434 public static void removeVersion(int spdyVersion, SpdyHeaderBlock block) {
435 if (spdyVersion < 3) {
436 block.removeHeader(Spdy2HttpNames.VERSION);
437 } else {
438 block.removeHeader(HttpNames.VERSION);
439 }
440 }
441
442
443
444
445 @Deprecated
446 public static HttpVersion getVersion(SpdyHeaderBlock block) {
447 return getVersion(2, block);
448 }
449
450
451
452
453 public static HttpVersion getVersion(int spdyVersion, SpdyHeaderBlock block) {
454 try {
455 if (spdyVersion < 3) {
456 return HttpVersion.valueOf(block.getHeader(Spdy2HttpNames.VERSION));
457 } else {
458 return HttpVersion.valueOf(block.getHeader(HttpNames.VERSION));
459 }
460 } catch (Exception e) {
461 return null;
462 }
463 }
464
465
466
467
468 @Deprecated
469 public static void setVersion(SpdyHeaderBlock block, HttpVersion httpVersion) {
470 setVersion(2, block, httpVersion);
471 }
472
473
474
475
476 public static void setVersion(int spdyVersion, SpdyHeaderBlock block, HttpVersion httpVersion) {
477 if (spdyVersion < 3) {
478 block.setHeader(Spdy2HttpNames.VERSION, httpVersion.getText());
479 } else {
480 block.setHeader(HttpNames.VERSION, httpVersion.getText());
481 }
482 }
483
484
485 private static final int BUCKET_SIZE = 17;
486
487 private static int hash(String name) {
488 int h = 0;
489 for (int i = name.length() - 1; i >= 0; i --) {
490 char c = name.charAt(i);
491 if (c >= 'A' && c <= 'Z') {
492 c += 32;
493 }
494 h = 31 * h + c;
495 }
496
497 if (h > 0) {
498 return h;
499 } else if (h == Integer.MIN_VALUE) {
500 return Integer.MAX_VALUE;
501 } else {
502 return -h;
503 }
504 }
505
506 private static boolean eq(String name1, String name2) {
507 int nameLen = name1.length();
508 if (nameLen != name2.length()) {
509 return false;
510 }
511
512 for (int i = nameLen - 1; i >= 0; i --) {
513 char c1 = name1.charAt(i);
514 char c2 = name2.charAt(i);
515 if (c1 != c2) {
516 if (c1 >= 'A' && c1 <= 'Z') {
517 c1 += 32;
518 }
519 if (c2 >= 'A' && c2 <= 'Z') {
520 c2 += 32;
521 }
522 if (c1 != c2) {
523 return false;
524 }
525 }
526 }
527 return true;
528 }
529
530 private static int index(int hash) {
531 return hash % BUCKET_SIZE;
532 }
533
534 private final Entry[] entries = new Entry[BUCKET_SIZE];
535 private final Entry head = new Entry(-1, null, null);
536
537 SpdyHeaders() {
538 head.before = head.after = head;
539 }
540
541 void addHeader(final String name, final Object value) {
542 String lowerCaseName = name.toLowerCase();
543 SpdyCodecUtil.validateHeaderName(lowerCaseName);
544 String strVal = toString(value);
545 SpdyCodecUtil.validateHeaderValue(strVal);
546 int h = hash(lowerCaseName);
547 int i = index(h);
548 addHeader0(h, i, lowerCaseName, strVal);
549 }
550
551 private void addHeader0(int h, int i, final String name, final String value) {
552
553 Entry e = entries[i];
554 Entry newEntry;
555 entries[i] = newEntry = new Entry(h, name, value);
556 newEntry.next = e;
557
558
559 newEntry.addBefore(head);
560 }
561
562 void removeHeader(final String name) {
563 if (name == null) {
564 throw new NullPointerException("name");
565 }
566 String lowerCaseName = name.toLowerCase();
567 int h = hash(lowerCaseName);
568 int i = index(h);
569 removeHeader0(h, i, lowerCaseName);
570 }
571
572 private void removeHeader0(int h, int i, String name) {
573 Entry e = entries[i];
574 if (e == null) {
575 return;
576 }
577
578 for (;;) {
579 if (e.hash == h && eq(name, e.key)) {
580 e.remove();
581 Entry next = e.next;
582 if (next != null) {
583 entries[i] = next;
584 e = next;
585 } else {
586 entries[i] = null;
587 return;
588 }
589 } else {
590 break;
591 }
592 }
593
594 for (;;) {
595 Entry next = e.next;
596 if (next == null) {
597 break;
598 }
599 if (next.hash == h && eq(name, next.key)) {
600 e.next = next.next;
601 next.remove();
602 } else {
603 e = next;
604 }
605 }
606 }
607
608 void setHeader(final String name, final Object value) {
609 String lowerCaseName = name.toLowerCase();
610 SpdyCodecUtil.validateHeaderName(lowerCaseName);
611 String strVal = toString(value);
612 SpdyCodecUtil.validateHeaderValue(strVal);
613 int h = hash(lowerCaseName);
614 int i = index(h);
615 removeHeader0(h, i, lowerCaseName);
616 addHeader0(h, i, lowerCaseName, strVal);
617 }
618
619 void setHeader(final String name, final Iterable<?> values) {
620 if (values == null) {
621 throw new NullPointerException("values");
622 }
623
624 String lowerCaseName = name.toLowerCase();
625 SpdyCodecUtil.validateHeaderName(lowerCaseName);
626
627 int h = hash(lowerCaseName);
628 int i = index(h);
629
630 removeHeader0(h, i, lowerCaseName);
631 for (Object v: values) {
632 if (v == null) {
633 break;
634 }
635 String strVal = toString(v);
636 SpdyCodecUtil.validateHeaderValue(strVal);
637 addHeader0(h, i, lowerCaseName, strVal);
638 }
639 }
640
641 void clearHeaders() {
642 for (int i = 0; i < entries.length; i ++) {
643 entries[i] = null;
644 }
645 head.before = head.after = head;
646 }
647
648 String getHeader(final String name) {
649 if (name == null) {
650 throw new NullPointerException("name");
651 }
652
653 int h = hash(name);
654 int i = index(h);
655 Entry e = entries[i];
656 while (e != null) {
657 if (e.hash == h && eq(name, e.key)) {
658 return e.value;
659 }
660
661 e = e.next;
662 }
663 return null;
664 }
665
666 List<String> getHeaders(final String name) {
667 if (name == null) {
668 throw new NullPointerException("name");
669 }
670
671 LinkedList<String> values = new LinkedList<String>();
672
673 int h = hash(name);
674 int i = index(h);
675 Entry e = entries[i];
676 while (e != null) {
677 if (e.hash == h && eq(name, e.key)) {
678 values.addFirst(e.value);
679 }
680 e = e.next;
681 }
682 return values;
683 }
684
685 List<Map.Entry<String, String>> getHeaders() {
686 List<Map.Entry<String, String>> all =
687 new LinkedList<Map.Entry<String, String>>();
688
689 Entry e = head.after;
690 while (e != head) {
691 all.add(e);
692 e = e.after;
693 }
694 return all;
695 }
696
697 boolean containsHeader(String name) {
698 return getHeader(name) != null;
699 }
700
701 Set<String> getHeaderNames() {
702 Set<String> names = new TreeSet<String>();
703
704 Entry e = head.after;
705 while (e != head) {
706 names.add(e.key);
707 e = e.after;
708 }
709 return names;
710 }
711
712 private static String toString(Object value) {
713 if (value == null) {
714 return null;
715 }
716 return value.toString();
717 }
718
719 private static final class Entry implements Map.Entry<String, String> {
720 final int hash;
721 final String key;
722 String value;
723 Entry next;
724 Entry before, after;
725
726 Entry(int hash, String key, String value) {
727 this.hash = hash;
728 this.key = key;
729 this.value = value;
730 }
731
732 void remove() {
733 before.after = after;
734 after.before = before;
735 }
736
737 void addBefore(Entry e) {
738 after = e;
739 before = e.before;
740 before.after = this;
741 after.before = this;
742 }
743
744 public String getKey() {
745 return key;
746 }
747
748 public String getValue() {
749 return value;
750 }
751
752 public String setValue(String value) {
753 if (value == null) {
754 throw new NullPointerException("value");
755 }
756 SpdyCodecUtil.validateHeaderValue(value);
757 String oldValue = this.value;
758 this.value = value;
759 return oldValue;
760 }
761
762 @Override
763 public String toString() {
764 return key + "=" + value;
765 }
766 }
767 }