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