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