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.util.LinkedList;
19 import java.util.List;
20 import java.util.Map;
21 import java.util.Set;
22 import java.util.TreeSet;
23
24
25
26
27
28
29
30
31
32
33
34
35
36 public class HttpHeaders {
37
38
39
40
41
42
43
44
45
46
47 public static final class Names {
48
49
50
51 public static final String ACCEPT = "Accept";
52
53
54
55 public static final String ACCEPT_CHARSET = "Accept-Charset";
56
57
58
59 public static final String ACCEPT_ENCODING= "Accept-Encoding";
60
61
62
63 public static final String ACCEPT_LANGUAGE = "Accept-Language";
64
65
66
67 public static final String ACCEPT_RANGES= "Accept-Ranges";
68
69
70
71 public static final String ACCEPT_PATCH= "Accept-Patch";
72
73
74
75 public static final String AGE = "Age";
76
77
78
79 public static final String ALLOW = "Allow";
80
81
82
83 public static final String AUTHORIZATION = "Authorization";
84
85
86
87 public static final String CACHE_CONTROL = "Cache-Control";
88
89
90
91 public static final String CONNECTION = "Connection";
92
93
94
95 public static final String CONTENT_BASE = "Content-Base";
96
97
98
99 public static final String CONTENT_ENCODING = "Content-Encoding";
100
101
102
103 public static final String CONTENT_LANGUAGE= "Content-Language";
104
105
106
107 public static final String CONTENT_LENGTH = "Content-Length";
108
109
110
111 public static final String CONTENT_LOCATION = "Content-Location";
112
113
114
115 public static final String CONTENT_TRANSFER_ENCODING = "Content-Transfer-Encoding";
116
117
118
119 public static final String CONTENT_MD5 = "Content-MD5";
120
121
122
123 public static final String CONTENT_RANGE = "Content-Range";
124
125
126
127 public static final String CONTENT_TYPE= "Content-Type";
128
129
130
131 public static final String COOKIE = "Cookie";
132
133
134
135 public static final String DATE = "Date";
136
137
138
139 public static final String ETAG = "ETag";
140
141
142
143 public static final String EXPECT = "Expect";
144
145
146
147 public static final String EXPIRES = "Expires";
148
149
150
151 public static final String FROM = "From";
152
153
154
155 public static final String HOST = "Host";
156
157
158
159 public static final String IF_MATCH = "If-Match";
160
161
162
163 public static final String IF_MODIFIED_SINCE = "If-Modified-Since";
164
165
166
167 public static final String IF_NONE_MATCH = "If-None-Match";
168
169
170
171 public static final String IF_RANGE= "If-Range";
172
173
174
175 public static final String IF_UNMODIFIED_SINCE = "If-Unmodified-Since";
176
177
178
179 public static final String LAST_MODIFIED = "Last-Modified";
180
181
182
183 public static final String LOCATION = "Location";
184
185
186
187 public static final String MAX_FORWARDS = "Max-Forwards";
188
189
190
191 public static final String ORIGIN = "Origin";
192
193
194
195 public static final String PRAGMA = "Pragma";
196
197
198
199 public static final String PROXY_AUTHENTICATE = "Proxy-Authenticate";
200
201
202
203 public static final String PROXY_AUTHORIZATION = "Proxy-Authorization";
204
205
206
207 public static final String RANGE = "Range";
208
209
210
211 public static final String REFERER = "Referer";
212
213
214
215 public static final String RETRY_AFTER = "Retry-After";
216
217
218
219 public static final String SEC_WEBSOCKET_KEY1 = "Sec-WebSocket-Key1";
220
221
222
223 public static final String SEC_WEBSOCKET_KEY2 = "Sec-WebSocket-Key2";
224
225
226
227 public static final String SEC_WEBSOCKET_LOCATION = "Sec-WebSocket-Location";
228
229
230
231 public static final String SEC_WEBSOCKET_ORIGIN = "Sec-WebSocket-Origin";
232
233
234
235 public static final String SEC_WEBSOCKET_PROTOCOL = "Sec-WebSocket-Protocol";
236
237
238
239 public static final String SERVER = "Server";
240
241
242
243 public static final String SET_COOKIE = "Set-Cookie";
244
245
246
247 public static final String SET_COOKIE2 = "Set-Cookie2";
248
249
250
251 public static final String TE = "TE";
252
253
254
255 public static final String TRAILER = "Trailer";
256
257
258
259 public static final String TRANSFER_ENCODING = "Transfer-Encoding";
260
261
262
263 public static final String UPGRADE = "Upgrade";
264
265
266
267 public static final String USER_AGENT = "User-Agent";
268
269
270
271 public static final String VARY = "Vary";
272
273
274
275 public static final String VIA = "Via";
276
277
278
279 public static final String WARNING = "Warning";
280
281
282
283 public static final String WEBSOCKET_LOCATION = "WebSocket-Location";
284
285
286
287 public static final String WEBSOCKET_ORIGIN = "WebSocket-Origin";
288
289
290
291 public static final String WEBSOCKET_PROTOCOL = "WebSocket-Protocol";
292
293
294
295 public static final String WWW_AUTHENTICATE = "WWW-Authenticate";
296
297 private Names() {
298 super();
299 }
300 }
301
302
303
304
305
306
307
308
309
310
311 public static final class Values {
312
313
314
315 public static final String BASE64 = "base64";
316
317
318
319 public static final String BINARY = "binary";
320
321
322
323 public static final String BYTES = "bytes";
324
325
326
327 public static final String CHARSET = "charset";
328
329
330
331 public static final String CHUNKED = "chunked";
332
333
334
335 public static final String CLOSE = "close";
336
337
338
339 public static final String COMPRESS = "compress";
340
341
342
343 public static final String CONTINUE = "100-continue";
344
345
346
347 public static final String DEFLATE = "deflate";
348
349
350
351 public static final String GZIP = "gzip";
352
353
354
355 public static final String IDENTITY = "identity";
356
357
358
359 public static final String KEEP_ALIVE = "keep-alive";
360
361
362
363 public static final String MAX_AGE = "max-age";
364
365
366
367 public static final String MAX_STALE = "max-stale";
368
369
370
371 public static final String MIN_FRESH = "min-fresh";
372
373
374
375 public static final String MUST_REVALIDATE = "must-revalidate";
376
377
378
379 public static final String NO_CACHE = "no-cache";
380
381
382
383 public static final String NO_STORE = "no-store";
384
385
386
387 public static final String NO_TRANSFORM = "no-transform";
388
389
390
391 public static final String NONE = "none";
392
393
394
395 public static final String ONLY_IF_CACHED = "only-if-cached";
396
397
398
399 public static final String PRIVATE = "private";
400
401
402
403 public static final String PROXY_REVALIDATE = "proxy-revalidate";
404
405
406
407 public static final String PUBLIC = "public";
408
409
410
411 public static final String QUOTED_PRINTABLE = "quoted-printable";
412
413
414
415 public static final String S_MAXAGE = "s-maxage";
416
417
418
419 public static final String TRAILERS = "trailers";
420
421
422
423 public static final String UPGRADE = "Upgrade";
424
425
426
427 public static final String WEBSOCKET = "WebSocket";
428
429 private Values() {
430 super();
431 }
432 }
433
434
435
436
437
438
439
440
441 public static boolean isKeepAlive(HttpMessage message) {
442 String connection = message.getHeader(Names.CONNECTION);
443 if (Values.CLOSE.equalsIgnoreCase(connection)) {
444 return false;
445 }
446
447 if (message.getProtocolVersion().isKeepAliveDefault()) {
448 return !Values.CLOSE.equalsIgnoreCase(connection);
449 } else {
450 return Values.KEEP_ALIVE.equalsIgnoreCase(connection);
451 }
452 }
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473 public static void setKeepAlive(HttpMessage message, boolean keepAlive) {
474 if (message.getProtocolVersion().isKeepAliveDefault()) {
475 if (keepAlive) {
476 message.removeHeader(Names.CONNECTION);
477 } else {
478 message.setHeader(Names.CONNECTION, Values.CLOSE);
479 }
480 } else {
481 if (keepAlive) {
482 message.setHeader(Names.CONNECTION, Values.KEEP_ALIVE);
483 } else {
484 message.removeHeader(Names.CONNECTION);
485 }
486 }
487 }
488
489
490
491
492
493
494
495
496 public static String getHeader(HttpMessage message, String name) {
497 return message.getHeader(name);
498 }
499
500
501
502
503
504
505
506
507
508 public static String getHeader(HttpMessage message, String name, String defaultValue) {
509 String value = message.getHeader(name);
510 if (value == null) {
511 return defaultValue;
512 }
513 return value;
514 }
515
516
517
518
519
520 public static void setHeader(HttpMessage message, String name, Object value) {
521 message.setHeader(name, value);
522 }
523
524
525
526
527
528 public static void setHeader(HttpMessage message, String name, Iterable<?> values) {
529 message.setHeader(name, values);
530 }
531
532
533
534
535 public static void addHeader(HttpMessage message, String name, Object value) {
536 message.addHeader(name, value);
537 }
538
539
540
541
542
543
544
545
546
547
548 public static int getIntHeader(HttpMessage message, String name) {
549 String value = getHeader(message, name);
550 if (value == null) {
551 throw new NumberFormatException("null");
552 }
553 return Integer.parseInt(value);
554 }
555
556
557
558
559
560
561
562
563
564 public static int getIntHeader(HttpMessage message, String name, int defaultValue) {
565 String value = getHeader(message, name);
566 if (value == null) {
567 return defaultValue;
568 }
569
570 try {
571 return Integer.parseInt(value);
572 } catch (NumberFormatException e) {
573 return defaultValue;
574 }
575 }
576
577
578
579
580
581 public static void setIntHeader(HttpMessage message, String name, int value) {
582 message.setHeader(name, value);
583 }
584
585
586
587
588
589 public static void setIntHeader(HttpMessage message, String name, Iterable<Integer> values) {
590 message.setHeader(name, values);
591 }
592
593
594
595
596 public static void addIntHeader(HttpMessage message, String name, int value) {
597 message.addHeader(name, value);
598 }
599
600
601
602
603
604
605
606
607
608
609 public static long getContentLength(HttpMessage message) {
610 return getContentLength(message, 0L);
611 }
612
613
614
615
616
617
618
619
620
621
622 public static long getContentLength(HttpMessage message, long defaultValue) {
623 String contentLength = message.getHeader(Names.CONTENT_LENGTH);
624 if (contentLength != null) {
625 return Long.parseLong(contentLength);
626 }
627
628
629 if (message instanceof HttpRequest) {
630 HttpRequest req = (HttpRequest) message;
631 if (HttpMethod.GET.equals(req.getMethod()) &&
632 req.containsHeader(Names.SEC_WEBSOCKET_KEY1) &&
633 req.containsHeader(Names.SEC_WEBSOCKET_KEY2)) {
634 return 8;
635 }
636 } else if (message instanceof HttpResponse) {
637 HttpResponse res = (HttpResponse) message;
638 if (res.getStatus().getCode() == 101 &&
639 res.containsHeader(Names.SEC_WEBSOCKET_ORIGIN) &&
640 res.containsHeader(Names.SEC_WEBSOCKET_LOCATION)) {
641 return 16;
642 }
643 }
644
645 return defaultValue;
646 }
647
648
649
650
651 public static void setContentLength(HttpMessage message, long length) {
652 message.setHeader(Names.CONTENT_LENGTH, length);
653 }
654
655
656
657
658 public static String getHost(HttpMessage message) {
659 return message.getHeader(Names.HOST);
660 }
661
662
663
664
665
666 public static String getHost(HttpMessage message, String defaultValue) {
667 return getHeader(message, Names.HOST, defaultValue);
668 }
669
670
671
672
673 public static void setHost(HttpMessage message, String value) {
674 message.setHeader(Names.HOST, value);
675 }
676
677
678
679
680
681 public static boolean is100ContinueExpected(HttpMessage message) {
682
683 if (!(message instanceof HttpRequest)) {
684 return false;
685 }
686
687
688 if (message.getProtocolVersion().compareTo(HttpVersion.HTTP_1_1) < 0) {
689 return false;
690 }
691
692
693 String value = message.getHeader(Names.EXPECT);
694 if (value == null) {
695 return false;
696 }
697 if (Values.CONTINUE.equalsIgnoreCase(value)) {
698 return true;
699 }
700
701
702 for (String v: message.getHeaders(Names.EXPECT)) {
703 if (Values.CONTINUE.equalsIgnoreCase(v)) {
704 return true;
705 }
706 }
707 return false;
708 }
709
710
711
712
713
714
715 public static void set100ContinueExpected(HttpMessage message) {
716 set100ContinueExpected(message, true);
717 }
718
719
720
721
722
723
724
725
726 public static void set100ContinueExpected(HttpMessage message, boolean set) {
727 if (set) {
728 message.setHeader(Names.EXPECT, Values.CONTINUE);
729 } else {
730 message.removeHeader(Names.EXPECT);
731 }
732 }
733
734 private static final int BUCKET_SIZE = 17;
735
736 private static int hash(String name) {
737 int h = 0;
738 for (int i = name.length() - 1; i >= 0; i --) {
739 char c = name.charAt(i);
740 if (c >= 'A' && c <= 'Z') {
741 c += 32;
742 }
743 h = 31 * h + c;
744 }
745
746 if (h > 0) {
747 return h;
748 } else if (h == Integer.MIN_VALUE) {
749 return Integer.MAX_VALUE;
750 } else {
751 return -h;
752 }
753 }
754
755 private static boolean eq(String name1, String name2) {
756 int nameLen = name1.length();
757 if (nameLen != name2.length()) {
758 return false;
759 }
760
761 for (int i = nameLen - 1; i >= 0; i --) {
762 char c1 = name1.charAt(i);
763 char c2 = name2.charAt(i);
764 if (c1 != c2) {
765 if (c1 >= 'A' && c1 <= 'Z') {
766 c1 += 32;
767 }
768 if (c2 >= 'A' && c2 <= 'Z') {
769 c2 += 32;
770 }
771 if (c1 != c2) {
772 return false;
773 }
774 }
775 }
776 return true;
777 }
778
779 private static int index(int hash) {
780 return hash % BUCKET_SIZE;
781 }
782
783 private final Entry[] entries = new Entry[BUCKET_SIZE];
784 private final Entry head = new Entry(-1, null, null);
785
786 HttpHeaders() {
787 head.before = head.after = head;
788 }
789
790 void validateHeaderName(String name) {
791 HttpCodecUtil.validateHeaderName(name);
792 }
793
794 void addHeader(final String name, final Object value) {
795 validateHeaderName(name);
796 String strVal = toString(value);
797 HttpCodecUtil.validateHeaderValue(strVal);
798 int h = hash(name);
799 int i = index(h);
800 addHeader0(h, i, name, strVal);
801 }
802
803 private void addHeader0(int h, int i, final String name, final String value) {
804
805 Entry e = entries[i];
806 Entry newEntry;
807 entries[i] = newEntry = new Entry(h, name, value);
808 newEntry.next = e;
809
810
811 newEntry.addBefore(head);
812 }
813
814 void removeHeader(final String name) {
815 if (name == null) {
816 throw new NullPointerException("name");
817 }
818 int h = hash(name);
819 int i = index(h);
820 removeHeader0(h, i, name);
821 }
822
823 private void removeHeader0(int h, int i, String name) {
824 Entry e = entries[i];
825 if (e == null) {
826 return;
827 }
828
829 for (;;) {
830 if (e.hash == h && eq(name, e.key)) {
831 e.remove();
832 Entry next = e.next;
833 if (next != null) {
834 entries[i] = next;
835 e = next;
836 } else {
837 entries[i] = null;
838 return;
839 }
840 } else {
841 break;
842 }
843 }
844
845 for (;;) {
846 Entry next = e.next;
847 if (next == null) {
848 break;
849 }
850 if (next.hash == h && eq(name, next.key)) {
851 e.next = next.next;
852 next.remove();
853 } else {
854 e = next;
855 }
856 }
857 }
858
859 void setHeader(final String name, final Object value) {
860 validateHeaderName(name);
861 String strVal = toString(value);
862 HttpCodecUtil.validateHeaderValue(strVal);
863 int h = hash(name);
864 int i = index(h);
865 removeHeader0(h, i, name);
866 addHeader0(h, i, name, strVal);
867 }
868
869 void setHeader(final String name, final Iterable<?> values) {
870 if (values == null) {
871 throw new NullPointerException("values");
872 }
873
874 validateHeaderName(name);
875
876 int h = hash(name);
877 int i = index(h);
878
879 removeHeader0(h, i, name);
880 for (Object v: values) {
881 if (v == null) {
882 break;
883 }
884 String strVal = toString(v);
885 HttpCodecUtil.validateHeaderValue(strVal);
886 addHeader0(h, i, name, strVal);
887 }
888 }
889
890 void clearHeaders() {
891 for (int i = 0; i < entries.length; i ++) {
892 entries[i] = null;
893 }
894 head.before = head.after = head;
895 }
896
897 String getHeader(final String name) {
898 if (name == null) {
899 throw new NullPointerException("name");
900 }
901
902 int h = hash(name);
903 int i = index(h);
904 Entry e = entries[i];
905 while (e != null) {
906 if (e.hash == h && eq(name, e.key)) {
907 return e.value;
908 }
909
910 e = e.next;
911 }
912 return null;
913 }
914
915 List<String> getHeaders(final String name) {
916 if (name == null) {
917 throw new NullPointerException("name");
918 }
919
920 LinkedList<String> values = new LinkedList<String>();
921
922 int h = hash(name);
923 int i = index(h);
924 Entry e = entries[i];
925 while (e != null) {
926 if (e.hash == h && eq(name, e.key)) {
927 values.addFirst(e.value);
928 }
929 e = e.next;
930 }
931 return values;
932 }
933
934 List<Map.Entry<String, String>> getHeaders() {
935 List<Map.Entry<String, String>> all =
936 new LinkedList<Map.Entry<String, String>>();
937
938 Entry e = head.after;
939 while (e != head) {
940 all.add(e);
941 e = e.after;
942 }
943 return all;
944 }
945
946 boolean containsHeader(String name) {
947 return getHeader(name) != null;
948 }
949
950 Set<String> getHeaderNames() {
951 Set<String> names =
952 new TreeSet<String>(CaseIgnoringComparator.INSTANCE);
953
954 Entry e = head.after;
955 while (e != head) {
956 names.add(e.key);
957 e = e.after;
958 }
959 return names;
960 }
961
962 private static String toString(Object value) {
963 if (value == null) {
964 return null;
965 }
966 return value.toString();
967 }
968
969 private static final class Entry implements Map.Entry<String, String> {
970 final int hash;
971 final String key;
972 String value;
973 Entry next;
974 Entry before, after;
975
976 Entry(int hash, String key, String value) {
977 this.hash = hash;
978 this.key = key;
979 this.value = value;
980 }
981
982 void remove() {
983 before.after = after;
984 after.before = before;
985 }
986
987 void addBefore(Entry e) {
988 after = e;
989 before = e.before;
990 before.after = this;
991 after.before = this;
992 }
993
994 public String getKey() {
995 return key;
996 }
997
998 public String getValue() {
999 return value;
1000 }
1001
1002 public String setValue(String value) {
1003 if (value == null) {
1004 throw new NullPointerException("value");
1005 }
1006 HttpCodecUtil.validateHeaderValue(value);
1007 String oldValue = this.value;
1008 this.value = value;
1009 return oldValue;
1010 }
1011
1012 @Override
1013 public String toString() {
1014 return key + "=" + value;
1015 }
1016 }
1017 }