1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.springframework.http;
18
19 import java.io.Serializable;
20 import java.net.URI;
21 import java.nio.charset.Charset;
22 import java.text.ParseException;
23 import java.text.SimpleDateFormat;
24 import java.util.ArrayList;
25 import java.util.Collection;
26 import java.util.Collections;
27 import java.util.Date;
28 import java.util.EnumSet;
29 import java.util.Iterator;
30 import java.util.LinkedHashMap;
31 import java.util.LinkedList;
32 import java.util.List;
33 import java.util.Locale;
34 import java.util.Map;
35 import java.util.Set;
36 import java.util.TimeZone;
37
38 import org.springframework.util.Assert;
39 import org.springframework.util.LinkedCaseInsensitiveMap;
40 import org.springframework.util.MultiValueMap;
41 import org.springframework.util.StringUtils;
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60 public class HttpHeaders implements MultiValueMap<String, String>, Serializable {
61
62 private static final long serialVersionUID = -8578554704772377436L;
63
64
65
66
67
68 public static final String ACCEPT = "Accept";
69
70
71
72
73 public static final String ACCEPT_CHARSET = "Accept-Charset";
74
75
76
77
78 public static final String ACCEPT_ENCODING = "Accept-Encoding";
79
80
81
82
83 public static final String ACCEPT_LANGUAGE = "Accept-Language";
84
85
86
87
88 public static final String ACCEPT_RANGES = "Accept-Ranges";
89
90
91
92
93 public static final String AGE = "Age";
94
95
96
97
98 public static final String ALLOW = "Allow";
99
100
101
102
103 public static final String AUTHORIZATION = "Authorization";
104
105
106
107
108 public static final String CACHE_CONTROL = "Cache-Control";
109
110
111
112
113 public static final String CONNECTION = "Connection";
114
115
116
117
118 public static final String CONTENT_ENCODING = "Content-Encoding";
119
120
121
122
123 public static final String CONTENT_DISPOSITION = "Content-Disposition";
124
125
126
127
128 public static final String CONTENT_LANGUAGE = "Content-Language";
129
130
131
132
133 public static final String CONTENT_LENGTH = "Content-Length";
134
135
136
137
138 public static final String CONTENT_LOCATION = "Content-Location";
139
140
141
142
143 public static final String CONTENT_RANGE = "Content-Range";
144
145
146
147
148 public static final String CONTENT_TYPE = "Content-Type";
149
150
151
152
153 public static final String COOKIE = "Cookie";
154
155
156
157
158 public static final String DATE = "Date";
159
160
161
162
163 public static final String ETAG = "ETag";
164
165
166
167
168 public static final String EXPECT = "Expect";
169
170
171
172
173 public static final String EXPIRES = "Expires";
174
175
176
177
178 public static final String FROM = "From";
179
180
181
182
183 public static final String HOST = "Host";
184
185
186
187
188 public static final String IF_MATCH = "If-Match";
189
190
191
192
193 public static final String IF_MODIFIED_SINCE = "If-Modified-Since";
194
195
196
197
198 public static final String IF_NONE_MATCH = "If-None-Match";
199
200
201
202
203 public static final String IF_RANGE = "If-Range";
204
205
206
207
208 public static final String IF_UNMODIFIED_SINCE = "If-Unmodified-Since";
209
210
211
212
213 public static final String LAST_MODIFIED = "Last-Modified";
214
215
216
217
218 public static final String LINK = "Link";
219
220
221
222
223 public static final String LOCATION = "Location";
224
225
226
227
228 public static final String MAX_FORWARDS = "Max-Forwards";
229
230
231
232
233 public static final String ORIGIN = "Origin";
234
235
236
237
238 public static final String PRAGMA = "Pragma";
239
240
241
242
243 public static final String PROXY_AUTHENTICATE = "Proxy-Authenticate";
244
245
246
247
248 public static final String PROXY_AUTHORIZATION = "Proxy-Authorization";
249
250
251
252
253 public static final String RANGE = "Range";
254
255
256
257
258 public static final String REFERER = "Referer";
259
260
261
262
263 public static final String RETRY_AFTER = "Retry-After";
264
265
266
267
268 public static final String SERVER = "Server";
269
270
271
272
273 public static final String SET_COOKIE = "Set-Cookie";
274
275
276
277
278 public static final String SET_COOKIE2 = "Set-Cookie2";
279
280
281
282
283 public static final String TE = "TE";
284
285
286
287
288 public static final String TRAILER = "Trailer";
289
290
291
292
293 public static final String TRANSFER_ENCODING = "Transfer-Encoding";
294
295
296
297
298 public static final String UPGRADE = "Upgrade";
299
300
301
302
303 public static final String USER_AGENT = "User-Agent";
304
305
306
307
308 public static final String VARY = "Vary";
309
310
311
312
313 public static final String VIA = "Via";
314
315
316
317
318 public static final String WARNING = "Warning";
319
320
321
322
323 public static final String WWW_AUTHENTICATE = "WWW-Authenticate";
324
325 private static final String[] DATE_FORMATS = new String[] {
326 "EEE, dd MMM yyyy HH:mm:ss zzz",
327 "EEE, dd-MMM-yy HH:mm:ss zzz",
328 "EEE MMM dd HH:mm:ss yyyy"
329 };
330
331 private static TimeZone GMT = TimeZone.getTimeZone("GMT");
332
333
334 private final Map<String, List<String>> headers;
335
336
337
338
339
340 public HttpHeaders() {
341 this(new LinkedCaseInsensitiveMap<List<String>>(8, Locale.ENGLISH), false);
342 }
343
344
345
346
347 private HttpHeaders(Map<String, List<String>> headers, boolean readOnly) {
348 Assert.notNull(headers, "'headers' must not be null");
349 if (readOnly) {
350 Map<String, List<String>> map =
351 new LinkedCaseInsensitiveMap<List<String>>(headers.size(), Locale.ENGLISH);
352 for (Entry<String, List<String>> entry : headers.entrySet()) {
353 List<String> values = Collections.unmodifiableList(entry.getValue());
354 map.put(entry.getKey(), values);
355 }
356 this.headers = Collections.unmodifiableMap(map);
357 }
358 else {
359 this.headers = headers;
360 }
361 }
362
363
364
365
366
367
368 public void setAccept(List<MediaType> acceptableMediaTypes) {
369 set(ACCEPT, MediaType.toString(acceptableMediaTypes));
370 }
371
372
373
374
375
376
377 public List<MediaType> getAccept() {
378 String value = getFirst(ACCEPT);
379 List<MediaType> result = (value != null ? MediaType.parseMediaTypes(value) : Collections.<MediaType>emptyList());
380
381
382 if (result.size() == 1) {
383 List<String> acceptHeader = get(ACCEPT);
384 if (acceptHeader.size() > 1) {
385 value = StringUtils.collectionToCommaDelimitedString(acceptHeader);
386 result = MediaType.parseMediaTypes(value);
387 }
388 }
389
390 return result;
391 }
392
393
394
395
396
397 public void setAcceptCharset(List<Charset> acceptableCharsets) {
398 StringBuilder builder = new StringBuilder();
399 for (Iterator<Charset> iterator = acceptableCharsets.iterator(); iterator.hasNext();) {
400 Charset charset = iterator.next();
401 builder.append(charset.name().toLowerCase(Locale.ENGLISH));
402 if (iterator.hasNext()) {
403 builder.append(", ");
404 }
405 }
406 set(ACCEPT_CHARSET, builder.toString());
407 }
408
409
410
411
412
413 public List<Charset> getAcceptCharset() {
414 List<Charset> result = new ArrayList<Charset>();
415 String value = getFirst(ACCEPT_CHARSET);
416 if (value != null) {
417 String[] tokens = value.split(",\\s*");
418 for (String token : tokens) {
419 int paramIdx = token.indexOf(';');
420 String charsetName;
421 if (paramIdx == -1) {
422 charsetName = token;
423 }
424 else {
425 charsetName = token.substring(0, paramIdx);
426 }
427 if (!charsetName.equals("*")) {
428 result.add(Charset.forName(charsetName));
429 }
430 }
431 }
432 return result;
433 }
434
435
436
437
438
439 public void setAllow(Set<HttpMethod> allowedMethods) {
440 set(ALLOW, StringUtils.collectionToCommaDelimitedString(allowedMethods));
441 }
442
443
444
445
446
447
448 public Set<HttpMethod> getAllow() {
449 String value = getFirst(ALLOW);
450 if (!StringUtils.isEmpty(value)) {
451 List<HttpMethod> allowedMethod = new ArrayList<HttpMethod>(5);
452 String[] tokens = value.split(",\\s*");
453 for (String token : tokens) {
454 allowedMethod.add(HttpMethod.valueOf(token));
455 }
456 return EnumSet.copyOf(allowedMethod);
457 }
458 else {
459 return EnumSet.noneOf(HttpMethod.class);
460 }
461 }
462
463
464
465
466 public void setCacheControl(String cacheControl) {
467 set(CACHE_CONTROL, cacheControl);
468 }
469
470
471
472
473 public String getCacheControl() {
474 return getFirst(CACHE_CONTROL);
475 }
476
477
478
479
480 public void setConnection(String connection) {
481 set(CONNECTION, connection);
482 }
483
484
485
486
487 public void setConnection(List<String> connection) {
488 set(CONNECTION, toCommaDelimitedString(connection));
489 }
490
491
492
493
494 public List<String> getConnection() {
495 return getFirstValueAsList(CONNECTION);
496 }
497
498
499
500
501
502
503
504 public void setContentDispositionFormData(String name, String filename) {
505 Assert.notNull(name, "'name' must not be null");
506 StringBuilder builder = new StringBuilder("form-data; name=\"");
507 builder.append(name).append('\"');
508 if (filename != null) {
509 builder.append("; filename=\"");
510 builder.append(filename).append('\"');
511 }
512 set(CONTENT_DISPOSITION, builder.toString());
513 }
514
515
516
517
518
519 public void setContentLength(long contentLength) {
520 set(CONTENT_LENGTH, Long.toString(contentLength));
521 }
522
523
524
525
526
527
528 public long getContentLength() {
529 String value = getFirst(CONTENT_LENGTH);
530 return (value != null ? Long.parseLong(value) : -1);
531 }
532
533
534
535
536
537 public void setContentType(MediaType mediaType) {
538 Assert.isTrue(!mediaType.isWildcardType(), "'Content-Type' cannot contain wildcard type '*'");
539 Assert.isTrue(!mediaType.isWildcardSubtype(), "'Content-Type' cannot contain wildcard subtype '*'");
540 set(CONTENT_TYPE, mediaType.toString());
541 }
542
543
544
545
546
547
548 public MediaType getContentType() {
549 String value = getFirst(CONTENT_TYPE);
550 return (StringUtils.hasLength(value) ? MediaType.parseMediaType(value) : null);
551 }
552
553
554
555
556
557
558
559 public void setDate(long date) {
560 setDate(DATE, date);
561 }
562
563
564
565
566
567
568
569
570 public long getDate() {
571 return getFirstDate(DATE);
572 }
573
574
575
576
577 public void setETag(String eTag) {
578 if (eTag != null) {
579 Assert.isTrue(eTag.startsWith("\"") || eTag.startsWith("W/"),
580 "Invalid eTag, does not start with W/ or \"");
581 Assert.isTrue(eTag.endsWith("\""), "Invalid eTag, does not end with \"");
582 }
583 set(ETAG, eTag);
584 }
585
586
587
588
589 public String getETag() {
590 return getFirst(ETAG);
591 }
592
593
594
595
596
597
598
599 public void setExpires(long expires) {
600 setDate(EXPIRES, expires);
601 }
602
603
604
605
606
607
608
609 public long getExpires() {
610 try {
611 return getFirstDate(EXPIRES);
612 }
613 catch (IllegalArgumentException ex) {
614 return -1;
615 }
616 }
617
618
619
620
621
622
623 public void setIfModifiedSince(long ifModifiedSince) {
624 setDate(IF_MODIFIED_SINCE, ifModifiedSince);
625 }
626
627
628
629
630
631
632
633 @Deprecated
634 public long getIfNotModifiedSince() {
635 return getIfModifiedSince();
636 }
637
638
639
640
641
642
643 public long getIfModifiedSince() {
644 return getFirstDate(IF_MODIFIED_SINCE);
645 }
646
647
648
649
650 public void setIfNoneMatch(String ifNoneMatch) {
651 set(IF_NONE_MATCH, ifNoneMatch);
652 }
653
654
655
656
657 public void setIfNoneMatch(List<String> ifNoneMatchList) {
658 set(IF_NONE_MATCH, toCommaDelimitedString(ifNoneMatchList));
659 }
660
661 protected String toCommaDelimitedString(List<String> list) {
662 StringBuilder builder = new StringBuilder();
663 for (Iterator<String> iterator = list.iterator(); iterator.hasNext();) {
664 String ifNoneMatch = iterator.next();
665 builder.append(ifNoneMatch);
666 if (iterator.hasNext()) {
667 builder.append(", ");
668 }
669 }
670 return builder.toString();
671 }
672
673
674
675
676 public List<String> getIfNoneMatch() {
677 return getFirstValueAsList(IF_NONE_MATCH);
678 }
679
680 protected List<String> getFirstValueAsList(String header) {
681 List<String> result = new ArrayList<String>();
682 String value = getFirst(header);
683 if (value != null) {
684 String[] tokens = value.split(",\\s*");
685 for (String token : tokens) {
686 result.add(token);
687 }
688 }
689 return result;
690 }
691
692
693
694
695
696
697
698 public void setLastModified(long lastModified) {
699 setDate(LAST_MODIFIED, lastModified);
700 }
701
702
703
704
705
706
707
708 public long getLastModified() {
709 return getFirstDate(LAST_MODIFIED);
710 }
711
712
713
714
715
716 public void setLocation(URI location) {
717 set(LOCATION, location.toASCIIString());
718 }
719
720
721
722
723
724
725 public URI getLocation() {
726 String value = getFirst(LOCATION);
727 return (value != null ? URI.create(value) : null);
728 }
729
730
731
732
733 public void setOrigin(String origin) {
734 set(ORIGIN, origin);
735 }
736
737
738
739
740 public String getOrigin() {
741 return getFirst(ORIGIN);
742 }
743
744
745
746
747 public void setPragma(String pragma) {
748 set(PRAGMA, pragma);
749 }
750
751
752
753
754 public String getPragma() {
755 return getFirst(PRAGMA);
756 }
757
758
759
760
761 public void setUpgrade(String upgrade) {
762 set(UPGRADE, upgrade);
763 }
764
765
766
767
768 public String getUpgrade() {
769 return getFirst(UPGRADE);
770 }
771
772
773
774
775
776
777 public long getFirstDate(String headerName) {
778 String headerValue = getFirst(headerName);
779 if (headerValue == null) {
780 return -1;
781 }
782 for (String dateFormat : DATE_FORMATS) {
783 SimpleDateFormat simpleDateFormat = new SimpleDateFormat(dateFormat, Locale.US);
784 simpleDateFormat.setTimeZone(GMT);
785 try {
786 return simpleDateFormat.parse(headerValue).getTime();
787 }
788 catch (ParseException e) {
789
790 }
791 }
792 throw new IllegalArgumentException("Cannot parse date value \"" + headerValue +
793 "\" for \"" + headerName + "\" header");
794 }
795
796
797
798
799
800
801 public void setDate(String headerName, long date) {
802 SimpleDateFormat dateFormat = new SimpleDateFormat(DATE_FORMATS[0], Locale.US);
803 dateFormat.setTimeZone(GMT);
804 set(headerName, dateFormat.format(new Date(date)));
805 }
806
807
808
809
810
811
812 @Override
813 public String getFirst(String headerName) {
814 List<String> headerValues = headers.get(headerName);
815 return headerValues != null ? headerValues.get(0) : null;
816 }
817
818
819
820
821
822
823
824
825
826 @Override
827 public void add(String headerName, String headerValue) {
828 List<String> headerValues = headers.get(headerName);
829 if (headerValues == null) {
830 headerValues = new LinkedList<String>();
831 this.headers.put(headerName, headerValues);
832 }
833 headerValues.add(headerValue);
834 }
835
836
837
838
839
840
841
842
843
844 @Override
845 public void set(String headerName, String headerValue) {
846 List<String> headerValues = new LinkedList<String>();
847 headerValues.add(headerValue);
848 headers.put(headerName, headerValues);
849 }
850
851 @Override
852 public void setAll(Map<String, String> values) {
853 for (Entry<String, String> entry : values.entrySet()) {
854 set(entry.getKey(), entry.getValue());
855 }
856 }
857
858 @Override
859 public Map<String, String> toSingleValueMap() {
860 LinkedHashMap<String, String> singleValueMap = new LinkedHashMap<String,String>(this.headers.size());
861 for (Entry<String, List<String>> entry : headers.entrySet()) {
862 singleValueMap.put(entry.getKey(), entry.getValue().get(0));
863 }
864 return singleValueMap;
865 }
866
867
868
869
870 @Override
871 public int size() {
872 return this.headers.size();
873 }
874
875 @Override
876 public boolean isEmpty() {
877 return this.headers.isEmpty();
878 }
879
880 @Override
881 public boolean containsKey(Object key) {
882 return this.headers.containsKey(key);
883 }
884
885 @Override
886 public boolean containsValue(Object value) {
887 return this.headers.containsValue(value);
888 }
889
890 @Override
891 public List<String> get(Object key) {
892 return this.headers.get(key);
893 }
894
895 @Override
896 public List<String> put(String key, List<String> value) {
897 return this.headers.put(key, value);
898 }
899
900 @Override
901 public List<String> remove(Object key) {
902 return this.headers.remove(key);
903 }
904
905 @Override
906 public void putAll(Map<? extends String, ? extends List<String>> map) {
907 this.headers.putAll(map);
908 }
909
910 @Override
911 public void clear() {
912 this.headers.clear();
913 }
914
915 @Override
916 public Set<String> keySet() {
917 return this.headers.keySet();
918 }
919
920 @Override
921 public Collection<List<String>> values() {
922 return this.headers.values();
923 }
924
925 @Override
926 public Set<Entry<String, List<String>>> entrySet() {
927 return this.headers.entrySet();
928 }
929
930
931 @Override
932 public boolean equals(Object other) {
933 if (this == other) {
934 return true;
935 }
936 if (!(other instanceof HttpHeaders)) {
937 return false;
938 }
939 HttpHeaders otherHeaders = (HttpHeaders) other;
940 return this.headers.equals(otherHeaders.headers);
941 }
942
943 @Override
944 public int hashCode() {
945 return this.headers.hashCode();
946 }
947
948 @Override
949 public String toString() {
950 return this.headers.toString();
951 }
952
953
954
955
956
957 public static HttpHeaders readOnlyHttpHeaders(HttpHeaders headers) {
958 return new HttpHeaders(headers, true);
959 }
960
961 }