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 public static boolean isKeepAlive(HttpMessage message) {
485 String connection = message.getHeader(Names.CONNECTION);
486 if (Values.CLOSE.equalsIgnoreCase(connection)) {
487 return false;
488 }
489
490 if (message.getProtocolVersion().isKeepAliveDefault()) {
491 return !Values.CLOSE.equalsIgnoreCase(connection);
492 } else {
493 return Values.KEEP_ALIVE.equalsIgnoreCase(connection);
494 }
495 }
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516 public static void setKeepAlive(HttpMessage message, boolean keepAlive) {
517 if (message.getProtocolVersion().isKeepAliveDefault()) {
518 if (keepAlive) {
519 message.removeHeader(Names.CONNECTION);
520 } else {
521 message.setHeader(Names.CONNECTION, Values.CLOSE);
522 }
523 } else {
524 if (keepAlive) {
525 message.setHeader(Names.CONNECTION, Values.KEEP_ALIVE);
526 } else {
527 message.removeHeader(Names.CONNECTION);
528 }
529 }
530 }
531
532
533
534
535
536
537
538
539 public static String getHeader(HttpMessage message, String name) {
540 return message.getHeader(name);
541 }
542
543
544
545
546
547
548
549
550
551 public static String getHeader(HttpMessage message, String name, String defaultValue) {
552 String value = message.getHeader(name);
553 if (value == null) {
554 return defaultValue;
555 }
556 return value;
557 }
558
559
560
561
562
563 public static void setHeader(HttpMessage message, String name, Object value) {
564 message.setHeader(name, value);
565 }
566
567
568
569
570
571 public static void setHeader(HttpMessage message, String name, Iterable<?> values) {
572 message.setHeader(name, values);
573 }
574
575
576
577
578 public static void addHeader(HttpMessage message, String name, Object value) {
579 message.addHeader(name, value);
580 }
581
582
583
584
585
586
587
588
589
590
591 public static int getIntHeader(HttpMessage message, String name) {
592 String value = getHeader(message, name);
593 if (value == null) {
594 throw new NumberFormatException("null");
595 }
596 return Integer.parseInt(value);
597 }
598
599
600
601
602
603
604
605
606
607 public static int getIntHeader(HttpMessage message, String name, int defaultValue) {
608 String value = getHeader(message, name);
609 if (value == null) {
610 return defaultValue;
611 }
612
613 try {
614 return Integer.parseInt(value);
615 } catch (NumberFormatException e) {
616 return defaultValue;
617 }
618 }
619
620
621
622
623
624 public static void setIntHeader(HttpMessage message, String name, int value) {
625 message.setHeader(name, value);
626 }
627
628
629
630
631
632 public static void setIntHeader(HttpMessage message, String name, Iterable<Integer> values) {
633 message.setHeader(name, values);
634 }
635
636
637
638
639 public static void addIntHeader(HttpMessage message, String name, int value) {
640 message.addHeader(name, value);
641 }
642
643
644
645
646
647
648
649
650
651
652 public static long getContentLength(HttpMessage message) {
653 return getContentLength(message, 0L);
654 }
655
656
657
658
659
660
661
662
663
664
665 public static long getContentLength(HttpMessage message, long defaultValue) {
666 String contentLength = message.getHeader(Names.CONTENT_LENGTH);
667 if (contentLength != null) {
668 return Long.parseLong(contentLength);
669 }
670
671
672 if (message instanceof HttpRequest) {
673 HttpRequest req = (HttpRequest) message;
674 if (HttpMethod.GET.equals(req.getMethod()) &&
675 req.containsHeader(Names.SEC_WEBSOCKET_KEY1) &&
676 req.containsHeader(Names.SEC_WEBSOCKET_KEY2)) {
677 return 8;
678 }
679 } else if (message instanceof HttpResponse) {
680 HttpResponse res = (HttpResponse) message;
681 if (res.getStatus().getCode() == 101 &&
682 res.containsHeader(Names.SEC_WEBSOCKET_ORIGIN) &&
683 res.containsHeader(Names.SEC_WEBSOCKET_LOCATION)) {
684 return 16;
685 }
686 }
687
688 return defaultValue;
689 }
690
691
692
693
694 public static void setContentLength(HttpMessage message, long length) {
695 message.setHeader(Names.CONTENT_LENGTH, length);
696 }
697
698
699
700
701 public static String getHost(HttpMessage message) {
702 return message.getHeader(Names.HOST);
703 }
704
705
706
707
708
709 public static String getHost(HttpMessage message, String defaultValue) {
710 return getHeader(message, Names.HOST, defaultValue);
711 }
712
713
714
715
716 public static void setHost(HttpMessage message, String value) {
717 message.setHeader(Names.HOST, value);
718 }
719
720
721
722
723
724 public static boolean is100ContinueExpected(HttpMessage message) {
725
726 if (!(message instanceof HttpRequest)) {
727 return false;
728 }
729
730
731 if (message.getProtocolVersion().compareTo(HttpVersion.HTTP_1_1) < 0) {
732 return false;
733 }
734
735
736 String value = message.getHeader(Names.EXPECT);
737 if (value == null) {
738 return false;
739 }
740 if (Values.CONTINUE.equalsIgnoreCase(value)) {
741 return true;
742 }
743
744
745 for (String v: message.getHeaders(Names.EXPECT)) {
746 if (Values.CONTINUE.equalsIgnoreCase(v)) {
747 return true;
748 }
749 }
750 return false;
751 }
752
753
754
755
756
757
758 public static void set100ContinueExpected(HttpMessage message) {
759 set100ContinueExpected(message, true);
760 }
761
762
763
764
765
766
767
768
769 public static void set100ContinueExpected(HttpMessage message, boolean set) {
770 if (set) {
771 message.setHeader(Names.EXPECT, Values.CONTINUE);
772 } else {
773 message.removeHeader(Names.EXPECT);
774 }
775 }
776
777 private static final int BUCKET_SIZE = 17;
778
779 private static int hash(String name) {
780 int h = 0;
781 for (int i = name.length() - 1; i >= 0; i --) {
782 char c = name.charAt(i);
783 if (c >= 'A' && c <= 'Z') {
784 c += 32;
785 }
786 h = 31 * h + c;
787 }
788
789 if (h > 0) {
790 return h;
791 } else if (h == Integer.MIN_VALUE) {
792 return Integer.MAX_VALUE;
793 } else {
794 return -h;
795 }
796 }
797
798 private static boolean eq(String name1, String name2) {
799 int nameLen = name1.length();
800 if (nameLen != name2.length()) {
801 return false;
802 }
803
804 for (int i = nameLen - 1; i >= 0; i --) {
805 char c1 = name1.charAt(i);
806 char c2 = name2.charAt(i);
807 if (c1 != c2) {
808 if (c1 >= 'A' && c1 <= 'Z') {
809 c1 += 32;
810 }
811 if (c2 >= 'A' && c2 <= 'Z') {
812 c2 += 32;
813 }
814 if (c1 != c2) {
815 return false;
816 }
817 }
818 }
819 return true;
820 }
821
822 private static int index(int hash) {
823 return hash % BUCKET_SIZE;
824 }
825
826 private final HeaderEntry[] entries = new HeaderEntry[BUCKET_SIZE];
827 private final HeaderEntry head = new HeaderEntry(-1, null, null);
828
829 HttpHeaders() {
830 head.before = head.after = head;
831 }
832
833 void validateHeaderName(String name) {
834 HttpCodecUtil.validateHeaderName(name);
835 }
836
837 void addHeader(final String name, final Object value) {
838 validateHeaderName(name);
839 String strVal = toString(value);
840 HttpCodecUtil.validateHeaderValue(strVal);
841 int h = hash(name);
842 int i = index(h);
843 addHeader0(h, i, name, strVal);
844 }
845
846 private void addHeader0(int h, int i, final String name, final String value) {
847
848 HeaderEntry e = entries[i];
849 HeaderEntry newEntry;
850 entries[i] = newEntry = new HeaderEntry(h, name, value);
851 newEntry.next = e;
852
853
854 newEntry.addBefore(head);
855 }
856
857 void removeHeader(final String name) {
858 if (name == null) {
859 throw new NullPointerException("name");
860 }
861 int h = hash(name);
862 int i = index(h);
863 removeHeader0(h, i, name);
864 }
865
866 private void removeHeader0(int h, int i, String name) {
867 HeaderEntry e = entries[i];
868 if (e == null) {
869 return;
870 }
871
872 for (;;) {
873 if (e.hash == h && eq(name, e.key)) {
874 e.remove();
875 HeaderEntry next = e.next;
876 if (next != null) {
877 entries[i] = next;
878 e = next;
879 } else {
880 entries[i] = null;
881 return;
882 }
883 } else {
884 break;
885 }
886 }
887
888 for (;;) {
889 HeaderEntry next = e.next;
890 if (next == null) {
891 break;
892 }
893 if (next.hash == h && eq(name, next.key)) {
894 e.next = next.next;
895 next.remove();
896 } else {
897 e = next;
898 }
899 }
900 }
901
902 void setHeader(final String name, final Object value) {
903 validateHeaderName(name);
904 String strVal = toString(value);
905 HttpCodecUtil.validateHeaderValue(strVal);
906 int h = hash(name);
907 int i = index(h);
908 removeHeader0(h, i, name);
909 addHeader0(h, i, name, strVal);
910 }
911
912 void setHeader(final String name, final Iterable<?> values) {
913 if (values == null) {
914 throw new NullPointerException("values");
915 }
916
917 validateHeaderName(name);
918
919 int h = hash(name);
920 int i = index(h);
921
922 removeHeader0(h, i, name);
923 for (Object v: values) {
924 if (v == null) {
925 break;
926 }
927 String strVal = toString(v);
928 HttpCodecUtil.validateHeaderValue(strVal);
929 addHeader0(h, i, name, strVal);
930 }
931 }
932
933 void clearHeaders() {
934 for (int i = 0; i < entries.length; i ++) {
935 entries[i] = null;
936 }
937 head.before = head.after = head;
938 }
939
940 String getHeader(final String name) {
941 if (name == null) {
942 throw new NullPointerException("name");
943 }
944
945 int h = hash(name);
946 int i = index(h);
947 HeaderEntry e = entries[i];
948 while (e != null) {
949 if (e.hash == h && eq(name, e.key)) {
950 return e.value;
951 }
952
953 e = e.next;
954 }
955 return null;
956 }
957
958 List<String> getHeaders(final String name) {
959 if (name == null) {
960 throw new NullPointerException("name");
961 }
962
963 LinkedList<String> values = new LinkedList<String>();
964
965 int h = hash(name);
966 int i = index(h);
967 HeaderEntry e = entries[i];
968 while (e != null) {
969 if (e.hash == h && eq(name, e.key)) {
970 values.addFirst(e.value);
971 }
972 e = e.next;
973 }
974 return values;
975 }
976
977 List<Map.Entry<String, String>> getHeaders() {
978 List<Map.Entry<String, String>> all =
979 new LinkedList<Map.Entry<String, String>>();
980
981 HeaderEntry e = head.after;
982 while (e != head) {
983 all.add(e);
984 e = e.after;
985 }
986 return all;
987 }
988
989 boolean containsHeader(String name) {
990 return getHeader(name) != null;
991 }
992
993 Set<String> getHeaderNames() {
994 Set<String> names =
995 new TreeSet<String>(CaseIgnoringComparator.INSTANCE);
996
997 HeaderEntry e = head.after;
998 while (e != head) {
999 names.add(e.key);
1000 e = e.after;
1001 }
1002 return names;
1003 }
1004
1005 private static String toString(Object value) {
1006 if (value == null) {
1007 return null;
1008 }
1009 return value.toString();
1010 }
1011
1012 private static final class HeaderEntry implements Map.Entry<String, String> {
1013 final int hash;
1014 final String key;
1015 String value;
1016 HeaderEntry next;
1017 HeaderEntry before, after;
1018
1019 HeaderEntry(int hash, String key, String value) {
1020 this.hash = hash;
1021 this.key = key;
1022 this.value = value;
1023 }
1024
1025 void remove() {
1026 before.after = after;
1027 after.before = before;
1028 }
1029
1030 void addBefore(HeaderEntry e) {
1031 after = e;
1032 before = e.before;
1033 before.after = this;
1034 after.before = this;
1035 }
1036
1037 public String getKey() {
1038 return key;
1039 }
1040
1041 public String getValue() {
1042 return value;
1043 }
1044
1045 public String setValue(String value) {
1046 if (value == null) {
1047 throw new NullPointerException("value");
1048 }
1049 HttpCodecUtil.validateHeaderValue(value);
1050 String oldValue = this.value;
1051 this.value = value;
1052 return oldValue;
1053 }
1054
1055 @Override
1056 public String toString() {
1057 return key + '=' + value;
1058 }
1059 }
1060 }