1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package io.netty.handler.codec.http;
17
18 import io.netty.buffer.ByteBuf;
19
20 import java.text.ParseException;
21 import java.util.Calendar;
22 import java.util.Collections;
23 import java.util.Date;
24 import java.util.Iterator;
25 import java.util.List;
26 import java.util.Map;
27 import java.util.Map.Entry;
28 import java.util.Set;
29
30 import static io.netty.handler.codec.http.HttpConstants.*;
31
32
33
34
35
36 public abstract class HttpHeaders implements Iterable<Map.Entry<String, String>> {
37
38 private static final byte[] HEADER_SEPERATOR = { COLON, SP };
39 private static final byte[] CRLF = { CR, LF };
40 private static final CharSequence CONTENT_LENGTH_ENTITY = newEntity(Names.CONTENT_LENGTH);
41 private static final CharSequence CONNECTION_ENTITY = newEntity(Names.CONNECTION);
42 private static final CharSequence CLOSE_ENTITY = newEntity(Values.CLOSE);
43 private static final CharSequence KEEP_ALIVE_ENTITY = newEntity(Values.KEEP_ALIVE);
44 private static final CharSequence HOST_ENTITY = newEntity(Names.HOST);
45 private static final CharSequence DATE_ENTITY = newEntity(Names.DATE);
46 private static final CharSequence EXPECT_ENTITY = newEntity(Names.EXPECT);
47 private static final CharSequence CONTINUE_ENTITY = newEntity(Values.CONTINUE);
48 private static final CharSequence TRANSFER_ENCODING_ENTITY = newEntity(Names.TRANSFER_ENCODING);
49 private static final CharSequence CHUNKED_ENTITY = newEntity(Values.CHUNKED);
50 private static final CharSequence SEC_WEBSOCKET_KEY1_ENTITY = newEntity(Names.SEC_WEBSOCKET_KEY1);
51 private static final CharSequence SEC_WEBSOCKET_KEY2_ENTITY = newEntity(Names.SEC_WEBSOCKET_KEY2);
52 private static final CharSequence SEC_WEBSOCKET_ORIGIN_ENTITY = newEntity(Names.SEC_WEBSOCKET_ORIGIN);
53 private static final CharSequence SEC_WEBSOCKET_LOCATION_ENTITY = newEntity(Names.SEC_WEBSOCKET_LOCATION);
54
55 public static final HttpHeaders EMPTY_HEADERS = new HttpHeaders() {
56 @Override
57 public String get(String name) {
58 return null;
59 }
60
61 @Override
62 public List<String> getAll(String name) {
63 return Collections.emptyList();
64 }
65
66 @Override
67 public List<Entry<String, String>> entries() {
68 return Collections.emptyList();
69 }
70
71 @Override
72 public boolean contains(String name) {
73 return false;
74 }
75
76 @Override
77 public boolean isEmpty() {
78 return true;
79 }
80
81 @Override
82 public Set<String> names() {
83 return Collections.emptySet();
84 }
85
86 @Override
87 public HttpHeaders add(String name, Object value) {
88 throw new UnsupportedOperationException("read only");
89 }
90
91 @Override
92 public HttpHeaders add(String name, Iterable<?> values) {
93 throw new UnsupportedOperationException("read only");
94 }
95
96 @Override
97 public HttpHeaders set(String name, Object value) {
98 throw new UnsupportedOperationException("read only");
99 }
100
101 @Override
102 public HttpHeaders set(String name, Iterable<?> values) {
103 throw new UnsupportedOperationException("read only");
104 }
105
106 @Override
107 public HttpHeaders remove(String name) {
108 throw new UnsupportedOperationException("read only");
109 }
110
111 @Override
112 public HttpHeaders clear() {
113 throw new UnsupportedOperationException("read only");
114 }
115
116 @Override
117 public Iterator<Entry<String, String>> iterator() {
118 return entries().iterator();
119 }
120 };
121
122
123
124
125 public static final class Names {
126
127
128
129 public static final String ACCEPT = "Accept";
130
131
132
133 public static final String ACCEPT_CHARSET = "Accept-Charset";
134
135
136
137 public static final String ACCEPT_ENCODING = "Accept-Encoding";
138
139
140
141 public static final String ACCEPT_LANGUAGE = "Accept-Language";
142
143
144
145 public static final String ACCEPT_RANGES = "Accept-Ranges";
146
147
148
149 public static final String ACCEPT_PATCH = "Accept-Patch";
150
151
152
153 public static final String ACCESS_CONTROL_ALLOW_CREDENTIALS = "Access-Control-Allow-Credentials";
154
155
156
157 public static final String ACCESS_CONTROL_ALLOW_HEADERS = "Access-Control-Allow-Headers";
158
159
160
161 public static final String ACCESS_CONTROL_ALLOW_METHODS = "Access-Control-Allow-Methods";
162
163
164
165 public static final String ACCESS_CONTROL_ALLOW_ORIGIN = "Access-Control-Allow-Origin";
166
167
168
169 public static final String ACCESS_CONTROL_EXPOSE_HEADERS = "Access-Control-Expose-Headers";
170
171
172
173 public static final String ACCESS_CONTROL_MAX_AGE = "Access-Control-Max-Age";
174
175
176
177 public static final String ACCESS_CONTROL_REQUEST_HEADERS = "Access-Control-Request-Headers";
178
179
180
181 public static final String ACCESS_CONTROL_REQUEST_METHOD = "Access-Control-Request-Method";
182
183
184
185 public static final String AGE = "Age";
186
187
188
189 public static final String ALLOW = "Allow";
190
191
192
193 public static final String AUTHORIZATION = "Authorization";
194
195
196
197 public static final String CACHE_CONTROL = "Cache-Control";
198
199
200
201 public static final String CONNECTION = "Connection";
202
203
204
205 public static final String CONTENT_BASE = "Content-Base";
206
207
208
209 public static final String CONTENT_ENCODING = "Content-Encoding";
210
211
212
213 public static final String CONTENT_LANGUAGE = "Content-Language";
214
215
216
217 public static final String CONTENT_LENGTH = "Content-Length";
218
219
220
221 public static final String CONTENT_LOCATION = "Content-Location";
222
223
224
225 public static final String CONTENT_TRANSFER_ENCODING = "Content-Transfer-Encoding";
226
227
228
229 public static final String CONTENT_MD5 = "Content-MD5";
230
231
232
233 public static final String CONTENT_RANGE = "Content-Range";
234
235
236
237 public static final String CONTENT_TYPE = "Content-Type";
238
239
240
241 public static final String COOKIE = "Cookie";
242
243
244
245 public static final String DATE = "Date";
246
247
248
249 public static final String ETAG = "ETag";
250
251
252
253 public static final String EXPECT = "Expect";
254
255
256
257 public static final String EXPIRES = "Expires";
258
259
260
261 public static final String FROM = "From";
262
263
264
265 public static final String HOST = "Host";
266
267
268
269 public static final String IF_MATCH = "If-Match";
270
271
272
273 public static final String IF_MODIFIED_SINCE = "If-Modified-Since";
274
275
276
277 public static final String IF_NONE_MATCH = "If-None-Match";
278
279
280
281 public static final String IF_RANGE = "If-Range";
282
283
284
285 public static final String IF_UNMODIFIED_SINCE = "If-Unmodified-Since";
286
287
288
289 public static final String LAST_MODIFIED = "Last-Modified";
290
291
292
293 public static final String LOCATION = "Location";
294
295
296
297 public static final String MAX_FORWARDS = "Max-Forwards";
298
299
300
301 public static final String ORIGIN = "Origin";
302
303
304
305 public static final String PRAGMA = "Pragma";
306
307
308
309 public static final String PROXY_AUTHENTICATE = "Proxy-Authenticate";
310
311
312
313 public static final String PROXY_AUTHORIZATION = "Proxy-Authorization";
314
315
316
317 public static final String RANGE = "Range";
318
319
320
321 public static final String REFERER = "Referer";
322
323
324
325 public static final String RETRY_AFTER = "Retry-After";
326
327
328
329 public static final String SEC_WEBSOCKET_KEY1 = "Sec-WebSocket-Key1";
330
331
332
333 public static final String SEC_WEBSOCKET_KEY2 = "Sec-WebSocket-Key2";
334
335
336
337 public static final String SEC_WEBSOCKET_LOCATION = "Sec-WebSocket-Location";
338
339
340
341 public static final String SEC_WEBSOCKET_ORIGIN = "Sec-WebSocket-Origin";
342
343
344
345 public static final String SEC_WEBSOCKET_PROTOCOL = "Sec-WebSocket-Protocol";
346
347
348
349 public static final String SEC_WEBSOCKET_VERSION = "Sec-WebSocket-Version";
350
351
352
353 public static final String SEC_WEBSOCKET_KEY = "Sec-WebSocket-Key";
354
355
356
357 public static final String SEC_WEBSOCKET_ACCEPT = "Sec-WebSocket-Accept";
358
359
360
361 public static final String SERVER = "Server";
362
363
364
365 public static final String SET_COOKIE = "Set-Cookie";
366
367
368
369 public static final String SET_COOKIE2 = "Set-Cookie2";
370
371
372
373 public static final String TE = "TE";
374
375
376
377 public static final String TRAILER = "Trailer";
378
379
380
381 public static final String TRANSFER_ENCODING = "Transfer-Encoding";
382
383
384
385 public static final String UPGRADE = "Upgrade";
386
387
388
389 public static final String USER_AGENT = "User-Agent";
390
391
392
393 public static final String VARY = "Vary";
394
395
396
397 public static final String VIA = "Via";
398
399
400
401 public static final String WARNING = "Warning";
402
403
404
405 public static final String WEBSOCKET_LOCATION = "WebSocket-Location";
406
407
408
409 public static final String WEBSOCKET_ORIGIN = "WebSocket-Origin";
410
411
412
413 public static final String WEBSOCKET_PROTOCOL = "WebSocket-Protocol";
414
415
416
417 public static final String WWW_AUTHENTICATE = "WWW-Authenticate";
418
419 private Names() {
420 }
421 }
422
423
424
425
426 public static final class Values {
427
428
429
430 public static final String APPLICATION_JSON = "application/json";
431
432
433
434 public static final String APPLICATION_X_WWW_FORM_URLENCODED =
435 "application/x-www-form-urlencoded";
436
437
438
439 public static final String BASE64 = "base64";
440
441
442
443 public static final String BINARY = "binary";
444
445
446
447 public static final String BOUNDARY = "boundary";
448
449
450
451 public static final String BYTES = "bytes";
452
453
454
455 public static final String CHARSET = "charset";
456
457
458
459 public static final String CHUNKED = "chunked";
460
461
462
463 public static final String CLOSE = "close";
464
465
466
467 public static final String COMPRESS = "compress";
468
469
470
471 public static final String CONTINUE = "100-continue";
472
473
474
475 public static final String DEFLATE = "deflate";
476
477
478
479 public static final String GZIP = "gzip";
480
481
482
483 public static final String GZIP_DEFLATE = "gzip,deflate";
484
485
486
487 public static final String IDENTITY = "identity";
488
489
490
491 public static final String KEEP_ALIVE = "keep-alive";
492
493
494
495 public static final String MAX_AGE = "max-age";
496
497
498
499 public static final String MAX_STALE = "max-stale";
500
501
502
503 public static final String MIN_FRESH = "min-fresh";
504
505
506
507 public static final String MULTIPART_FORM_DATA = "multipart/form-data";
508
509
510
511 public static final String MUST_REVALIDATE = "must-revalidate";
512
513
514
515 public static final String NO_CACHE = "no-cache";
516
517
518
519 public static final String NO_STORE = "no-store";
520
521
522
523 public static final String NO_TRANSFORM = "no-transform";
524
525
526
527 public static final String NONE = "none";
528
529
530
531 public static final String ONLY_IF_CACHED = "only-if-cached";
532
533
534
535 public static final String PRIVATE = "private";
536
537
538
539 public static final String PROXY_REVALIDATE = "proxy-revalidate";
540
541
542
543 public static final String PUBLIC = "public";
544
545
546
547 public static final String QUOTED_PRINTABLE = "quoted-printable";
548
549
550
551 public static final String S_MAXAGE = "s-maxage";
552
553
554
555 public static final String TRAILERS = "trailers";
556
557
558
559 public static final String UPGRADE = "Upgrade";
560
561
562
563 public static final String WEBSOCKET = "WebSocket";
564
565 private Values() {
566 }
567 }
568
569
570
571
572
573
574
575 public static boolean isKeepAlive(HttpMessage message) {
576 String connection = message.headers().get(CONNECTION_ENTITY);
577 if (connection != null && equalsIgnoreCase(CLOSE_ENTITY, connection)) {
578 return false;
579 }
580
581 if (message.getProtocolVersion().isKeepAliveDefault()) {
582 return !equalsIgnoreCase(CLOSE_ENTITY, connection);
583 } else {
584 return equalsIgnoreCase(KEEP_ALIVE_ENTITY, connection);
585 }
586 }
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607 public static void setKeepAlive(HttpMessage message, boolean keepAlive) {
608 HttpHeaders h = message.headers();
609 if (message.getProtocolVersion().isKeepAliveDefault()) {
610 if (keepAlive) {
611 h.remove(CONNECTION_ENTITY);
612 } else {
613 h.set(CONNECTION_ENTITY, CLOSE_ENTITY);
614 }
615 } else {
616 if (keepAlive) {
617 h.set(CONNECTION_ENTITY, KEEP_ALIVE_ENTITY);
618 } else {
619 h.remove(CONNECTION_ENTITY);
620 }
621 }
622 }
623
624
625
626
627 public static String getHeader(HttpMessage message, String name) {
628 return message.headers().get(name);
629 }
630
631
632
633
634
635
636
637
638 public static String getHeader(HttpMessage message, CharSequence name) {
639 return message.headers().get(name);
640 }
641
642
643
644
645 public static String getHeader(HttpMessage message, String name, String defaultValue) {
646 return getHeader(message, (CharSequence) name, defaultValue);
647 }
648
649
650
651
652
653
654
655
656
657 public static String getHeader(HttpMessage message, CharSequence name, String defaultValue) {
658 String value = message.headers().get(name);
659 if (value == null) {
660 return defaultValue;
661 }
662 return value;
663 }
664
665
666
667
668 public static void setHeader(HttpMessage message, String name, Object value) {
669 message.headers().set(name, value);
670 }
671
672
673
674
675
676
677
678
679
680 public static void setHeader(HttpMessage message, CharSequence name, Object value) {
681 message.headers().set(name, value);
682 }
683
684
685
686
687
688 public static void setHeader(HttpMessage message, String name, Iterable<?> values) {
689 message.headers().set(name, values);
690 }
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706 public static void setHeader(HttpMessage message, CharSequence name, Iterable<?> values) {
707 message.headers().set(name, values);
708 }
709
710
711
712
713 public static void addHeader(HttpMessage message, String name, Object value) {
714 message.headers().add(name, value);
715 }
716
717
718
719
720
721
722
723
724 public static void addHeader(HttpMessage message, CharSequence name, Object value) {
725 message.headers().add(name, value);
726 }
727
728
729
730
731 public static void removeHeader(HttpMessage message, String name) {
732 message.headers().remove(name);
733 }
734
735
736
737
738 public static void removeHeader(HttpMessage message, CharSequence name) {
739 message.headers().remove(name);
740 }
741
742
743
744
745 public static void clearHeaders(HttpMessage message) {
746 message.headers().clear();
747 }
748
749
750
751
752 public static int getIntHeader(HttpMessage message, String name) {
753 return getIntHeader(message, (CharSequence) name);
754 }
755
756
757
758
759
760
761
762
763
764
765 public static int getIntHeader(HttpMessage message, CharSequence name) {
766 String value = getHeader(message, name);
767 if (value == null) {
768 throw new NumberFormatException("header not found: " + name);
769 }
770 return Integer.parseInt(value);
771 }
772
773
774
775
776 public static int getIntHeader(HttpMessage message, String name, int defaultValue) {
777 return getIntHeader(message, (CharSequence) name, defaultValue);
778 }
779
780
781
782
783
784
785
786
787
788 public static int getIntHeader(HttpMessage message, CharSequence name, int defaultValue) {
789 String value = getHeader(message, name);
790 if (value == null) {
791 return defaultValue;
792 }
793
794 try {
795 return Integer.parseInt(value);
796 } catch (NumberFormatException ignored) {
797 return defaultValue;
798 }
799 }
800
801
802
803
804 public static void setIntHeader(HttpMessage message, String name, int value) {
805 message.headers().set(name, value);
806 }
807
808
809
810
811
812 public static void setIntHeader(HttpMessage message, CharSequence name, int value) {
813 message.headers().set(name, value);
814 }
815
816
817
818
819 public static void setIntHeader(HttpMessage message, String name, Iterable<Integer> values) {
820 message.headers().set(name, values);
821 }
822
823
824
825
826
827 public static void setIntHeader(HttpMessage message, CharSequence name, Iterable<Integer> values) {
828 message.headers().set(name, values);
829 }
830
831
832
833
834
835 public static void addIntHeader(HttpMessage message, String name, int value) {
836 message.headers().add(name, value);
837 }
838
839
840
841
842 public static void addIntHeader(HttpMessage message, CharSequence name, int value) {
843 message.headers().add(name, value);
844 }
845
846
847
848
849 public static Date getDateHeader(HttpMessage message, String name) throws ParseException {
850 return getDateHeader(message, (CharSequence) name);
851 }
852
853
854
855
856
857
858
859
860
861
862 public static Date getDateHeader(HttpMessage message, CharSequence name) throws ParseException {
863 String value = getHeader(message, name);
864 if (value == null) {
865 throw new ParseException("header not found: " + name, 0);
866 }
867 return HttpHeaderDateFormat.get().parse(value);
868 }
869
870
871
872
873 public static Date getDateHeader(HttpMessage message, String name, Date defaultValue) {
874 return getDateHeader(message, (CharSequence) name, defaultValue);
875 }
876
877
878
879
880
881
882
883
884
885 public static Date getDateHeader(HttpMessage message, CharSequence name, Date defaultValue) {
886 final String value = getHeader(message, name);
887 if (value == null) {
888 return defaultValue;
889 }
890
891 try {
892 return HttpHeaderDateFormat.get().parse(value);
893 } catch (ParseException ignored) {
894 return defaultValue;
895 }
896 }
897
898
899
900
901 public static void setDateHeader(HttpMessage message, String name, Date value) {
902 setDateHeader(message, (CharSequence) name, value);
903 }
904
905
906
907
908
909
910
911 public static void setDateHeader(HttpMessage message, CharSequence name, Date value) {
912 if (value != null) {
913 message.headers().set(name, HttpHeaderDateFormat.get().format(value));
914 } else {
915 message.headers().set(name, null);
916 }
917 }
918
919
920
921
922 public static void setDateHeader(HttpMessage message, String name, Iterable<Date> values) {
923 message.headers().set(name, values);
924 }
925
926
927
928
929
930
931
932 public static void setDateHeader(HttpMessage message, CharSequence name, Iterable<Date> values) {
933 message.headers().set(name, values);
934 }
935
936
937
938
939 public static void addDateHeader(HttpMessage message, String name, Date value) {
940 message.headers().add(name, value);
941 }
942
943
944
945
946
947
948 public static void addDateHeader(HttpMessage message, CharSequence name, Date value) {
949 message.headers().add(name, value);
950 }
951
952
953
954
955
956
957
958
959
960
961
962
963
964 public static long getContentLength(HttpMessage message) {
965 String value = getHeader(message, CONTENT_LENGTH_ENTITY);
966 if (value != null) {
967 return Long.parseLong(value);
968 }
969
970
971
972 long webSocketContentLength = getWebSocketContentLength(message);
973 if (webSocketContentLength >= 0) {
974 return webSocketContentLength;
975 }
976
977
978 throw new NumberFormatException("header not found: " + Names.CONTENT_LENGTH);
979 }
980
981
982
983
984
985
986
987
988
989
990
991 public static long getContentLength(HttpMessage message, long defaultValue) {
992 String contentLength = message.headers().get(CONTENT_LENGTH_ENTITY);
993 if (contentLength != null) {
994 try {
995 return Long.parseLong(contentLength);
996 } catch (NumberFormatException ignored) {
997 return defaultValue;
998 }
999 }
1000
1001
1002
1003 long webSocketContentLength = getWebSocketContentLength(message);
1004 if (webSocketContentLength >= 0) {
1005 return webSocketContentLength;
1006 }
1007
1008
1009 return defaultValue;
1010 }
1011
1012
1013
1014
1015
1016 private static int getWebSocketContentLength(HttpMessage message) {
1017
1018 HttpHeaders h = message.headers();
1019 if (message instanceof HttpRequest) {
1020 HttpRequest req = (HttpRequest) message;
1021 if (HttpMethod.GET.equals(req.getMethod()) &&
1022 h.contains(SEC_WEBSOCKET_KEY1_ENTITY) &&
1023 h.contains(SEC_WEBSOCKET_KEY2_ENTITY)) {
1024 return 8;
1025 }
1026 } else if (message instanceof HttpResponse) {
1027 HttpResponse res = (HttpResponse) message;
1028 if (res.getStatus().code() == 101 &&
1029 h.contains(SEC_WEBSOCKET_ORIGIN_ENTITY) &&
1030 h.contains(SEC_WEBSOCKET_LOCATION_ENTITY)) {
1031 return 16;
1032 }
1033 }
1034
1035
1036 return -1;
1037 }
1038
1039
1040
1041
1042 public static void setContentLength(HttpMessage message, long length) {
1043 message.headers().set(CONTENT_LENGTH_ENTITY, length);
1044 }
1045
1046
1047
1048
1049 public static String getHost(HttpMessage message) {
1050 return message.headers().get(HOST_ENTITY);
1051 }
1052
1053
1054
1055
1056
1057 public static String getHost(HttpMessage message, String defaultValue) {
1058 return getHeader(message, HOST_ENTITY, defaultValue);
1059 }
1060
1061
1062
1063
1064 public static void setHost(HttpMessage message, String value) {
1065 message.headers().set(HOST_ENTITY, value);
1066 }
1067
1068
1069
1070
1071 public static void setHost(HttpMessage message, CharSequence value) {
1072 message.headers().set(HOST_ENTITY, value);
1073 }
1074
1075
1076
1077
1078
1079
1080
1081 public static Date getDate(HttpMessage message) throws ParseException {
1082 return getDateHeader(message, DATE_ENTITY);
1083 }
1084
1085
1086
1087
1088
1089
1090 public static Date getDate(HttpMessage message, Date defaultValue) {
1091 return getDateHeader(message, DATE_ENTITY, defaultValue);
1092 }
1093
1094
1095
1096
1097 public static void setDate(HttpMessage message, Date value) {
1098 if (value != null) {
1099 message.headers().set(DATE_ENTITY, HttpHeaderDateFormat.get().format(value));
1100 } else {
1101 message.headers().set(DATE_ENTITY, null);
1102 }
1103 }
1104
1105
1106
1107
1108
1109 public static boolean is100ContinueExpected(HttpMessage message) {
1110
1111 if (!(message instanceof HttpRequest)) {
1112 return false;
1113 }
1114
1115
1116 if (message.getProtocolVersion().compareTo(HttpVersion.HTTP_1_1) < 0) {
1117 return false;
1118 }
1119
1120
1121 String value = message.headers().get(EXPECT_ENTITY);
1122 if (value == null) {
1123 return false;
1124 }
1125 if (equalsIgnoreCase(CONTINUE_ENTITY, value)) {
1126 return true;
1127 }
1128
1129
1130 return message.headers().contains(EXPECT_ENTITY, CONTINUE_ENTITY, true);
1131 }
1132
1133
1134
1135
1136
1137
1138 public static void set100ContinueExpected(HttpMessage message) {
1139 set100ContinueExpected(message, true);
1140 }
1141
1142
1143
1144
1145
1146
1147
1148
1149 public static void set100ContinueExpected(HttpMessage message, boolean set) {
1150 if (set) {
1151 message.headers().set(EXPECT_ENTITY, CONTINUE_ENTITY);
1152 } else {
1153 message.headers().remove(EXPECT_ENTITY);
1154 }
1155 }
1156
1157
1158
1159
1160
1161
1162 static void validateHeaderName(CharSequence headerName) {
1163
1164 if (headerName == null) {
1165 throw new NullPointerException("Header names cannot be null");
1166 }
1167
1168 for (int index = 0; index < headerName.length(); index ++) {
1169
1170 char character = headerName.charAt(index);
1171
1172
1173 if (character > 127) {
1174 throw new IllegalArgumentException(
1175 "Header name cannot contain non-ASCII characters: " + headerName);
1176 }
1177
1178
1179 switch (character) {
1180 case '\t': case '\n': case 0x0b: case '\f': case '\r':
1181 case ' ': case ',': case ':': case ';': case '=':
1182 throw new IllegalArgumentException(
1183 "Header name cannot contain the following prohibited characters: " +
1184 "=,;: \\t\\r\\n\\v\\f: " + headerName);
1185 }
1186 }
1187 }
1188
1189
1190
1191
1192
1193
1194 static void validateHeaderValue(CharSequence headerValue) {
1195
1196 if (headerValue == null) {
1197 throw new NullPointerException("Header values cannot be null");
1198 }
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209 int state = 0;
1210
1211
1212
1213 for (int index = 0; index < headerValue.length(); index ++) {
1214 char character = headerValue.charAt(index);
1215
1216
1217 switch (character) {
1218 case 0x0b:
1219 throw new IllegalArgumentException(
1220 "Header value contains a prohibited character '\\v': " + headerValue);
1221 case '\f':
1222 throw new IllegalArgumentException(
1223 "Header value contains a prohibited character '\\f': " + headerValue);
1224 }
1225
1226
1227 switch (state) {
1228 case 0:
1229 switch (character) {
1230 case '\r':
1231 state = 1;
1232 break;
1233 case '\n':
1234 state = 2;
1235 break;
1236 }
1237 break;
1238 case 1:
1239 switch (character) {
1240 case '\n':
1241 state = 2;
1242 break;
1243 default:
1244 throw new IllegalArgumentException(
1245 "Only '\\n' is allowed after '\\r': " + headerValue);
1246 }
1247 break;
1248 case 2:
1249 switch (character) {
1250 case '\t': case ' ':
1251 state = 0;
1252 break;
1253 default:
1254 throw new IllegalArgumentException(
1255 "Only ' ' and '\\t' are allowed after '\\n': " + headerValue);
1256 }
1257 }
1258 }
1259
1260 if (state != 0) {
1261 throw new IllegalArgumentException(
1262 "Header value must not end with '\\r' or '\\n':" + headerValue);
1263 }
1264 }
1265
1266
1267
1268
1269
1270
1271
1272 public static boolean isTransferEncodingChunked(HttpMessage message) {
1273 return message.headers().contains(TRANSFER_ENCODING_ENTITY, CHUNKED_ENTITY, true);
1274 }
1275
1276 public static void removeTransferEncodingChunked(HttpMessage m) {
1277 List<String> values = m.headers().getAll(TRANSFER_ENCODING_ENTITY);
1278 if (values.isEmpty()) {
1279 return;
1280 }
1281 Iterator<String> valuesIt = values.iterator();
1282 while (valuesIt.hasNext()) {
1283 String value = valuesIt.next();
1284 if (equalsIgnoreCase(value, CHUNKED_ENTITY)) {
1285 valuesIt.remove();
1286 }
1287 }
1288 if (values.isEmpty()) {
1289 m.headers().remove(TRANSFER_ENCODING_ENTITY);
1290 } else {
1291 m.headers().set(TRANSFER_ENCODING_ENTITY, values);
1292 }
1293 }
1294
1295 public static void setTransferEncodingChunked(HttpMessage m) {
1296 setHeader(m, TRANSFER_ENCODING_ENTITY, CHUNKED_ENTITY);
1297 removeHeader(m, CONTENT_LENGTH_ENTITY);
1298 }
1299
1300 public static boolean isContentLengthSet(HttpMessage m) {
1301 return m.headers().contains(CONTENT_LENGTH_ENTITY);
1302 }
1303
1304
1305
1306
1307
1308 public static boolean equalsIgnoreCase(CharSequence name1, CharSequence name2) {
1309 if (name1 == name2) {
1310 return true;
1311 }
1312
1313 if (name1 == null || name2 == null) {
1314 return false;
1315 }
1316
1317 int nameLen = name1.length();
1318 if (nameLen != name2.length()) {
1319 return false;
1320 }
1321
1322 for (int i = nameLen - 1; i >= 0; i --) {
1323 char c1 = name1.charAt(i);
1324 char c2 = name2.charAt(i);
1325 if (c1 != c2) {
1326 if (c1 >= 'A' && c1 <= 'Z') {
1327 c1 += 32;
1328 }
1329 if (c2 >= 'A' && c2 <= 'Z') {
1330 c2 += 32;
1331 }
1332 if (c1 != c2) {
1333 return false;
1334 }
1335 }
1336 }
1337 return true;
1338 }
1339
1340 static int hash(CharSequence name) {
1341 if (name instanceof HttpHeaderEntity) {
1342 return ((HttpHeaderEntity) name).hash();
1343 }
1344 int h = 0;
1345 for (int i = name.length() - 1; i >= 0; i --) {
1346 char c = name.charAt(i);
1347 if (c >= 'A' && c <= 'Z') {
1348 c += 32;
1349 }
1350 h = 31 * h + c;
1351 }
1352
1353 if (h > 0) {
1354 return h;
1355 } else if (h == Integer.MIN_VALUE) {
1356 return Integer.MAX_VALUE;
1357 } else {
1358 return -h;
1359 }
1360 }
1361
1362 static void encode(HttpHeaders headers, ByteBuf buf) {
1363 if (headers instanceof DefaultHttpHeaders) {
1364 ((DefaultHttpHeaders) headers).encode(buf);
1365 } else {
1366 for (Entry<String, String> header: headers) {
1367 encode(header.getKey(), header.getValue(), buf);
1368 }
1369 }
1370 }
1371
1372 @SuppressWarnings("deprecation")
1373 static void encode(CharSequence key, CharSequence value, ByteBuf buf) {
1374 if (!encodeAscii(key, buf)) {
1375 buf.writeBytes(HEADER_SEPERATOR);
1376 }
1377 if (!encodeAscii(value, buf)) {
1378 buf.writeBytes(CRLF);
1379 }
1380 }
1381
1382 public static boolean encodeAscii(CharSequence seq, ByteBuf buf) {
1383 if (seq instanceof HttpHeaderEntity) {
1384 return ((HttpHeaderEntity) seq).encode(buf);
1385 } else {
1386 encodeAscii0(seq, buf);
1387 return false;
1388 }
1389 }
1390
1391 static void encodeAscii0(CharSequence seq, ByteBuf buf) {
1392 int length = seq.length();
1393 for (int i = 0 ; i < length; i++) {
1394 buf.writeByte(c2b(seq.charAt(i)));
1395 }
1396 }
1397
1398 private static byte c2b(char c) {
1399 if (c > 255) {
1400 return '?';
1401 }
1402 return (byte) c;
1403 }
1404
1405
1406
1407
1408
1409 public static CharSequence newEntity(String name) {
1410 if (name == null) {
1411 throw new NullPointerException("name");
1412 }
1413 return new HttpHeaderEntity(name);
1414 }
1415
1416
1417
1418
1419
1420 public static CharSequence newNameEntity(String name) {
1421 if (name == null) {
1422 throw new NullPointerException("name");
1423 }
1424 return new HttpHeaderEntity(name, HEADER_SEPERATOR);
1425 }
1426
1427
1428
1429
1430
1431 public static CharSequence newValueEntity(String name) {
1432 if (name == null) {
1433 throw new NullPointerException("name");
1434 }
1435 return new HttpHeaderEntity(name, CRLF);
1436 }
1437
1438 protected HttpHeaders() { }
1439
1440
1441
1442
1443 public abstract String get(String name);
1444
1445
1446
1447
1448
1449
1450
1451
1452 public String get(CharSequence name) {
1453 return get(name.toString());
1454 }
1455
1456
1457
1458
1459 public abstract List<String> getAll(String name);
1460
1461
1462
1463
1464
1465
1466
1467
1468 public List<String> getAll(CharSequence name) {
1469 return getAll(name.toString());
1470 }
1471
1472
1473
1474
1475
1476
1477 public abstract List<Map.Entry<String, String>> entries();
1478
1479
1480
1481
1482 public abstract boolean contains(String name);
1483
1484
1485
1486
1487
1488
1489
1490 public boolean contains(CharSequence name) {
1491 return contains(name.toString());
1492 }
1493
1494
1495
1496
1497 public abstract boolean isEmpty();
1498
1499
1500
1501
1502
1503
1504 public abstract Set<String> names();
1505
1506
1507
1508
1509 public abstract HttpHeaders add(String name, Object value);
1510
1511
1512
1513
1514
1515
1516
1517
1518
1519
1520
1521
1522
1523
1524 public HttpHeaders add(CharSequence name, Object value) {
1525 return add(name.toString(), value);
1526 }
1527
1528
1529
1530
1531 public abstract HttpHeaders add(String name, Iterable<?> values);
1532
1533
1534
1535
1536
1537
1538
1539
1540
1541
1542
1543
1544
1545
1546
1547
1548
1549
1550 public HttpHeaders add(CharSequence name, Iterable<?> values) {
1551 return add(name.toString(), values);
1552 }
1553
1554
1555
1556
1557
1558
1559 public HttpHeaders add(HttpHeaders headers) {
1560 if (headers == null) {
1561 throw new NullPointerException("headers");
1562 }
1563 for (Map.Entry<String, String> e: headers) {
1564 add(e.getKey(), e.getValue());
1565 }
1566 return this;
1567 }
1568
1569
1570
1571
1572 public abstract HttpHeaders set(String name, Object value);
1573
1574
1575
1576
1577
1578
1579
1580
1581
1582
1583
1584
1585
1586
1587 public HttpHeaders set(CharSequence name, Object value) {
1588 return set(name.toString(), value);
1589 }
1590
1591
1592
1593
1594 public abstract HttpHeaders set(String name, Iterable<?> values);
1595
1596
1597
1598
1599
1600
1601
1602
1603
1604
1605
1606
1607
1608
1609
1610
1611
1612
1613
1614
1615 public HttpHeaders set(CharSequence name, Iterable<?> values) {
1616 return set(name.toString(), values);
1617 }
1618
1619
1620
1621
1622
1623
1624 public HttpHeaders set(HttpHeaders headers) {
1625 if (headers == null) {
1626 throw new NullPointerException("headers");
1627 }
1628 if (headers != this) {
1629 clear();
1630 for (Map.Entry<String, String> e : headers) {
1631 add(e.getKey(), e.getValue());
1632 }
1633 }
1634 return this;
1635 }
1636
1637
1638
1639
1640 public abstract HttpHeaders remove(String name);
1641
1642
1643
1644
1645
1646
1647
1648 public HttpHeaders remove(CharSequence name) {
1649 return remove(name.toString());
1650 }
1651
1652
1653
1654
1655
1656
1657 public abstract HttpHeaders clear();
1658
1659
1660
1661
1662 public boolean contains(String name, String value, boolean ignoreCaseValue) {
1663 List<String> values = getAll(name);
1664 if (values.isEmpty()) {
1665 return false;
1666 }
1667
1668 for (String v: values) {
1669 if (ignoreCaseValue) {
1670 if (equalsIgnoreCase(v, value)) {
1671 return true;
1672 }
1673 } else {
1674 if (v.equals(value)) {
1675 return true;
1676 }
1677 }
1678 }
1679 return false;
1680 }
1681
1682
1683
1684
1685
1686
1687
1688
1689
1690
1691
1692 public boolean containsValue(CharSequence name, CharSequence value, boolean ignoreCase) {
1693 List<String> values = getAll(name);
1694 if (values.isEmpty()) {
1695 return false;
1696 }
1697
1698 for (String v: values) {
1699 if (contains(v, value, ignoreCase)) {
1700 return true;
1701 }
1702 }
1703 return false;
1704 }
1705
1706 private static boolean contains(String value, CharSequence expected, boolean ignoreCase) {
1707 String[] parts = value.split(",");
1708 if (ignoreCase) {
1709 for (String s: parts) {
1710 if (equalsIgnoreCase(expected, s.trim())) {
1711 return true;
1712 }
1713 }
1714 } else {
1715 for (String s: parts) {
1716 if (s.trim().contentEquals(expected)) {
1717 return true;
1718 }
1719 }
1720 }
1721 return false;
1722 }
1723
1724
1725
1726
1727
1728
1729
1730
1731
1732 public boolean contains(CharSequence name, CharSequence value, boolean ignoreCaseValue) {
1733 return contains(name.toString(), value.toString(), ignoreCaseValue);
1734 }
1735 }