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