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