1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.eclipse.jetty.http;
20
21 import java.io.IOException;
22 import java.io.UnsupportedEncodingException;
23 import java.text.SimpleDateFormat;
24 import java.util.ArrayList;
25 import java.util.Calendar;
26 import java.util.Collections;
27 import java.util.Date;
28 import java.util.Enumeration;
29 import java.util.GregorianCalendar;
30 import java.util.HashMap;
31 import java.util.List;
32 import java.util.Locale;
33 import java.util.Map;
34 import java.util.NoSuchElementException;
35 import java.util.StringTokenizer;
36 import java.util.TimeZone;
37 import java.util.concurrent.ConcurrentHashMap;
38 import java.util.concurrent.ConcurrentMap;
39
40 import org.eclipse.jetty.io.Buffer;
41 import org.eclipse.jetty.io.BufferCache;
42 import org.eclipse.jetty.io.BufferCache.CachedBuffer;
43 import org.eclipse.jetty.io.BufferDateCache;
44 import org.eclipse.jetty.io.BufferUtil;
45 import org.eclipse.jetty.io.ByteArrayBuffer;
46 import org.eclipse.jetty.util.LazyList;
47 import org.eclipse.jetty.util.QuotedStringTokenizer;
48 import org.eclipse.jetty.util.StringMap;
49 import org.eclipse.jetty.util.StringUtil;
50 import org.eclipse.jetty.util.log.Log;
51 import org.eclipse.jetty.util.log.Logger;
52
53
54
55
56
57
58
59
60
61
62 public class HttpFields
63 {
64 private static final Logger LOG = Log.getLogger(HttpFields.class);
65
66
67 public static final String __COOKIE_DELIM="\"\\\n\r\t\f\b%+ ;=";
68 public static final TimeZone __GMT = TimeZone.getTimeZone("GMT");
69 public static final BufferDateCache __dateCache = new BufferDateCache("EEE, dd MMM yyyy HH:mm:ss 'GMT'", Locale.US);
70
71
72 static
73 {
74 __GMT.setID("GMT");
75 __dateCache.setTimeZone(__GMT);
76 }
77
78
79 public final static String __separators = ", \t";
80
81
82 private static final String[] DAYS =
83 { "Sat", "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"};
84 private static final String[] MONTHS =
85 { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec", "Jan"};
86
87
88
89 private static class DateGenerator
90 {
91 private final StringBuilder buf = new StringBuilder(32);
92 private final GregorianCalendar gc = new GregorianCalendar(__GMT);
93
94
95
96
97 public String formatDate(long date)
98 {
99 buf.setLength(0);
100 gc.setTimeInMillis(date);
101
102 int day_of_week = gc.get(Calendar.DAY_OF_WEEK);
103 int day_of_month = gc.get(Calendar.DAY_OF_MONTH);
104 int month = gc.get(Calendar.MONTH);
105 int year = gc.get(Calendar.YEAR);
106 int century = year / 100;
107 year = year % 100;
108
109 int hours = gc.get(Calendar.HOUR_OF_DAY);
110 int minutes = gc.get(Calendar.MINUTE);
111 int seconds = gc.get(Calendar.SECOND);
112
113 buf.append(DAYS[day_of_week]);
114 buf.append(',');
115 buf.append(' ');
116 StringUtil.append2digits(buf, day_of_month);
117
118 buf.append(' ');
119 buf.append(MONTHS[month]);
120 buf.append(' ');
121 StringUtil.append2digits(buf, century);
122 StringUtil.append2digits(buf, year);
123
124 buf.append(' ');
125 StringUtil.append2digits(buf, hours);
126 buf.append(':');
127 StringUtil.append2digits(buf, minutes);
128 buf.append(':');
129 StringUtil.append2digits(buf, seconds);
130 buf.append(" GMT");
131 return buf.toString();
132 }
133
134
135
136
137
138 public void formatCookieDate(StringBuilder buf, long date)
139 {
140 gc.setTimeInMillis(date);
141
142 int day_of_week = gc.get(Calendar.DAY_OF_WEEK);
143 int day_of_month = gc.get(Calendar.DAY_OF_MONTH);
144 int month = gc.get(Calendar.MONTH);
145 int year = gc.get(Calendar.YEAR);
146 year = year % 10000;
147
148 int epoch = (int) ((date / 1000) % (60 * 60 * 24));
149 int seconds = epoch % 60;
150 epoch = epoch / 60;
151 int minutes = epoch % 60;
152 int hours = epoch / 60;
153
154 buf.append(DAYS[day_of_week]);
155 buf.append(',');
156 buf.append(' ');
157 StringUtil.append2digits(buf, day_of_month);
158
159 buf.append('-');
160 buf.append(MONTHS[month]);
161 buf.append('-');
162 StringUtil.append2digits(buf, year/100);
163 StringUtil.append2digits(buf, year%100);
164
165 buf.append(' ');
166 StringUtil.append2digits(buf, hours);
167 buf.append(':');
168 StringUtil.append2digits(buf, minutes);
169 buf.append(':');
170 StringUtil.append2digits(buf, seconds);
171 buf.append(" GMT");
172 }
173 }
174
175
176 private static final ThreadLocal<DateGenerator> __dateGenerator =new ThreadLocal<DateGenerator>()
177 {
178 @Override
179 protected DateGenerator initialValue()
180 {
181 return new DateGenerator();
182 }
183 };
184
185
186
187
188
189 public static String formatDate(long date)
190 {
191 return __dateGenerator.get().formatDate(date);
192 }
193
194
195
196
197
198 public static void formatCookieDate(StringBuilder buf, long date)
199 {
200 __dateGenerator.get().formatCookieDate(buf,date);
201 }
202
203
204
205
206
207 public static String formatCookieDate(long date)
208 {
209 StringBuilder buf = new StringBuilder(28);
210 formatCookieDate(buf, date);
211 return buf.toString();
212 }
213
214
215 private final static String __dateReceiveFmt[] =
216 {
217 "EEE, dd MMM yyyy HH:mm:ss zzz",
218 "EEE, dd-MMM-yy HH:mm:ss",
219 "EEE MMM dd HH:mm:ss yyyy",
220
221 "EEE, dd MMM yyyy HH:mm:ss", "EEE dd MMM yyyy HH:mm:ss zzz",
222 "EEE dd MMM yyyy HH:mm:ss", "EEE MMM dd yyyy HH:mm:ss zzz", "EEE MMM dd yyyy HH:mm:ss",
223 "EEE MMM-dd-yyyy HH:mm:ss zzz", "EEE MMM-dd-yyyy HH:mm:ss", "dd MMM yyyy HH:mm:ss zzz",
224 "dd MMM yyyy HH:mm:ss", "dd-MMM-yy HH:mm:ss zzz", "dd-MMM-yy HH:mm:ss", "MMM dd HH:mm:ss yyyy zzz",
225 "MMM dd HH:mm:ss yyyy", "EEE MMM dd HH:mm:ss yyyy zzz",
226 "EEE, MMM dd HH:mm:ss yyyy zzz", "EEE, MMM dd HH:mm:ss yyyy", "EEE, dd-MMM-yy HH:mm:ss zzz",
227 "EEE dd-MMM-yy HH:mm:ss zzz", "EEE dd-MMM-yy HH:mm:ss",
228 };
229
230
231 private static class DateParser
232 {
233 final SimpleDateFormat _dateReceive[]= new SimpleDateFormat[__dateReceiveFmt.length];
234
235 long parse(final String dateVal)
236 {
237 for (int i = 0; i < _dateReceive.length; i++)
238 {
239 if (_dateReceive[i] == null)
240 {
241 _dateReceive[i] = new SimpleDateFormat(__dateReceiveFmt[i], Locale.US);
242 _dateReceive[i].setTimeZone(__GMT);
243 }
244
245 try
246 {
247 Date date = (Date) _dateReceive[i].parseObject(dateVal);
248 return date.getTime();
249 }
250 catch (java.lang.Exception e)
251 {
252
253 }
254 }
255
256 if (dateVal.endsWith(" GMT"))
257 {
258 final String val = dateVal.substring(0, dateVal.length() - 4);
259
260 for (int i = 0; i < _dateReceive.length; i++)
261 {
262 try
263 {
264 Date date = (Date) _dateReceive[i].parseObject(val);
265 return date.getTime();
266 }
267 catch (java.lang.Exception e)
268 {
269
270 }
271 }
272 }
273 return -1;
274 }
275 }
276
277
278 public static long parseDate(String date)
279 {
280 return __dateParser.get().parse(date);
281 }
282
283
284 private static final ThreadLocal<DateParser> __dateParser =new ThreadLocal<DateParser>()
285 {
286 @Override
287 protected DateParser initialValue()
288 {
289 return new DateParser();
290 }
291 };
292
293
294 public final static String __01Jan1970=formatDate(0);
295 public final static Buffer __01Jan1970_BUFFER=new ByteArrayBuffer(__01Jan1970);
296 public final static String __01Jan1970_COOKIE = formatCookieDate(0).trim();
297
298
299 private final ArrayList<Field> _fields = new ArrayList<Field>(20);
300 private final HashMap<Buffer,Field> _names = new HashMap<Buffer,Field>(32);
301
302
303
304
305
306 public HttpFields()
307 {
308 }
309
310
311 private static ConcurrentMap<String, Buffer> __cache = new ConcurrentHashMap<String, Buffer>();
312 private static int __cacheSize = Integer.getInteger("org.eclipse.jetty.http.HttpFields.CACHE",2000);
313
314
315 private Buffer convertValue(String value)
316 {
317 Buffer buffer = __cache.get(value);
318 if (buffer!=null)
319 return buffer;
320
321 try
322 {
323 buffer = new ByteArrayBuffer(value,StringUtil.__ISO_8859_1);
324
325 if (__cacheSize>0)
326 {
327 if (__cache.size()>__cacheSize)
328 __cache.clear();
329 Buffer b=__cache.putIfAbsent(value,buffer);
330 if (b!=null)
331 buffer=b;
332 }
333
334 return buffer;
335 }
336 catch (UnsupportedEncodingException e)
337 {
338 throw new RuntimeException(e);
339 }
340 }
341
342
343
344
345
346
347 public Enumeration<String> getFieldNames()
348 {
349 final Enumeration<?> buffers = Collections.enumeration(_names.keySet());
350 return new Enumeration<String>()
351 {
352 public String nextElement()
353 {
354 return buffers.nextElement().toString();
355 }
356
357 public boolean hasMoreElements()
358 {
359 return buffers.hasMoreElements();
360 }
361 };
362 }
363
364
365 public int size()
366 {
367 return _fields.size();
368 }
369
370
371
372
373
374
375
376 public Field getField(int i)
377 {
378 return _fields.get(i);
379 }
380
381
382 private Field getField(String name)
383 {
384 return _names.get(HttpHeaders.CACHE.lookup(name));
385 }
386
387
388 private Field getField(Buffer name)
389 {
390 return _names.get(HttpHeaders.CACHE.lookup(name));
391 }
392
393
394 public boolean containsKey(Buffer name)
395 {
396 return _names.containsKey(HttpHeaders.CACHE.lookup(name));
397 }
398
399
400 public boolean containsKey(String name)
401 {
402 return _names.containsKey(HttpHeaders.CACHE.lookup(name));
403 }
404
405
406
407
408
409
410
411 public String getStringField(String name)
412 {
413 Field field = getField(name);
414 return field==null?null:field.getValue();
415 }
416
417
418
419
420
421
422
423 public String getStringField(Buffer name)
424 {
425 Field field = getField(name);
426 return field==null?null:field.getValue();
427 }
428
429
430
431
432
433
434
435 public Buffer get(Buffer name)
436 {
437 Field field = getField(name);
438 return field==null?null:field._value;
439 }
440
441
442
443
444
445
446
447
448 public Enumeration<String> getValues(String name)
449 {
450 final Field field = getField(name);
451 if (field == null)
452 {
453 List<String> empty=Collections.emptyList();
454 return Collections.enumeration(empty);
455 }
456
457 return new Enumeration<String>()
458 {
459 Field f = field;
460
461 public boolean hasMoreElements()
462 {
463 return f != null;
464 }
465
466 public String nextElement() throws NoSuchElementException
467 {
468 if (f == null) throw new NoSuchElementException();
469 Field n = f;
470 f = f._next;
471 return n.getValue();
472 }
473 };
474 }
475
476
477
478
479
480
481
482
483 public Enumeration<String> getValues(Buffer name)
484 {
485 final Field field = getField(name);
486 if (field == null)
487 {
488 List<String> empty=Collections.emptyList();
489 return Collections.enumeration(empty);
490 }
491
492 return new Enumeration<String>()
493 {
494 Field f = field;
495
496 public boolean hasMoreElements()
497 {
498 return f != null;
499 }
500
501 public String nextElement() throws NoSuchElementException
502 {
503 if (f == null) throw new NoSuchElementException();
504 Field n = f;
505 f = f._next;
506 return n.getValue();
507 }
508 };
509 }
510
511
512
513
514
515
516
517
518
519
520
521 public Enumeration<String> getValues(String name, final String separators)
522 {
523 final Enumeration<String> e = getValues(name);
524 if (e == null)
525 return null;
526 return new Enumeration<String>()
527 {
528 QuotedStringTokenizer tok = null;
529
530 public boolean hasMoreElements()
531 {
532 if (tok != null && tok.hasMoreElements()) return true;
533 while (e.hasMoreElements())
534 {
535 String value = e.nextElement();
536 tok = new QuotedStringTokenizer(value, separators, false, false);
537 if (tok.hasMoreElements()) return true;
538 }
539 tok = null;
540 return false;
541 }
542
543 public String nextElement() throws NoSuchElementException
544 {
545 if (!hasMoreElements()) throw new NoSuchElementException();
546 String next = (String) tok.nextElement();
547 if (next != null) next = next.trim();
548 return next;
549 }
550 };
551 }
552
553
554
555
556
557
558
559
560
561 public void put(String name, String value)
562 {
563 if (value==null)
564 remove(name);
565 else
566 {
567 Buffer n = HttpHeaders.CACHE.lookup(name);
568 Buffer v = convertValue(value);
569 put(n, v);
570 }
571 }
572
573
574
575
576
577
578
579
580 public void put(Buffer name, String value)
581 {
582 Buffer n = HttpHeaders.CACHE.lookup(name);
583 Buffer v = convertValue(value);
584 put(n, v);
585 }
586
587
588
589
590
591
592
593
594 public void put(Buffer name, Buffer value)
595 {
596 remove(name);
597 if (value == null)
598 return;
599
600 if (!(name instanceof BufferCache.CachedBuffer))
601 name = HttpHeaders.CACHE.lookup(name);
602 if (!(value instanceof CachedBuffer))
603 value= HttpHeaderValues.CACHE.lookup(value).asImmutableBuffer();
604
605
606 Field field = new Field(name, value);
607 _fields.add(field);
608 _names.put(name, field);
609 }
610
611
612
613
614
615
616
617
618 public void put(String name, List<?> list)
619 {
620 if (list == null || list.size() == 0)
621 {
622 remove(name);
623 return;
624 }
625 Buffer n = HttpHeaders.CACHE.lookup(name);
626
627 Object v = list.get(0);
628 if (v != null)
629 put(n, HttpHeaderValues.CACHE.lookup(v.toString()));
630 else
631 remove(n);
632
633 if (list.size() > 1)
634 {
635 java.util.Iterator<?> iter = list.iterator();
636 iter.next();
637 while (iter.hasNext())
638 {
639 v = iter.next();
640 if (v != null) put(n, HttpHeaderValues.CACHE.lookup(v.toString()));
641 }
642 }
643 }
644
645
646
647
648
649
650
651
652
653
654
655 public void add(String name, String value) throws IllegalArgumentException
656 {
657 if (value==null)
658 return;
659 Buffer n = HttpHeaders.CACHE.lookup(name);
660 Buffer v = convertValue(value);
661 add(n, v);
662 }
663
664
665
666
667
668
669
670
671
672
673
674 public void add(Buffer name, Buffer value) throws IllegalArgumentException
675 {
676 if (value == null) throw new IllegalArgumentException("null value");
677
678 if (!(name instanceof CachedBuffer))
679 name = HttpHeaders.CACHE.lookup(name);
680 name=name.asImmutableBuffer();
681
682 if (!(value instanceof CachedBuffer) && HttpHeaderValues.hasKnownValues(HttpHeaders.CACHE.getOrdinal(name)))
683 value= HttpHeaderValues.CACHE.lookup(value);
684 value=value.asImmutableBuffer();
685
686 Field field = _names.get(name);
687 Field last = null;
688 while (field != null)
689 {
690 last = field;
691 field = field._next;
692 }
693
694
695 field = new Field(name, value);
696 _fields.add(field);
697
698
699 if (last != null)
700 last._next = field;
701 else
702 _names.put(name, field);
703 }
704
705
706
707
708
709
710
711 public void remove(String name)
712 {
713 remove(HttpHeaders.CACHE.lookup(name));
714 }
715
716
717
718
719
720
721
722 public void remove(Buffer name)
723 {
724 if (!(name instanceof BufferCache.CachedBuffer))
725 name = HttpHeaders.CACHE.lookup(name);
726 Field field = _names.remove(name);
727 while (field != null)
728 {
729 _fields.remove(field);
730 field = field._next;
731 }
732 }
733
734
735
736
737
738
739
740
741
742 public long getLongField(String name) throws NumberFormatException
743 {
744 Field field = getField(name);
745 return field==null?-1L:field.getLongValue();
746 }
747
748
749
750
751
752
753
754
755
756 public long getLongField(Buffer name) throws NumberFormatException
757 {
758 Field field = getField(name);
759 return field==null?-1L:field.getLongValue();
760 }
761
762
763
764
765
766
767
768
769 public long getDateField(String name)
770 {
771 Field field = getField(name);
772 if (field == null)
773 return -1;
774
775 String val = valueParameters(BufferUtil.to8859_1_String(field._value), null);
776 if (val == null)
777 return -1;
778
779 final long date = __dateParser.get().parse(val);
780 if (date==-1)
781 throw new IllegalArgumentException("Cannot convert date: " + val);
782 return date;
783 }
784
785
786
787
788
789
790
791
792 public void putLongField(Buffer name, long value)
793 {
794 Buffer v = BufferUtil.toBuffer(value);
795 put(name, v);
796 }
797
798
799
800
801
802
803
804
805 public void putLongField(String name, long value)
806 {
807 Buffer n = HttpHeaders.CACHE.lookup(name);
808 Buffer v = BufferUtil.toBuffer(value);
809 put(n, v);
810 }
811
812
813
814
815
816
817
818
819 public void addLongField(String name, long value)
820 {
821 Buffer n = HttpHeaders.CACHE.lookup(name);
822 Buffer v = BufferUtil.toBuffer(value);
823 add(n, v);
824 }
825
826
827
828
829
830
831
832
833 public void addLongField(Buffer name, long value)
834 {
835 Buffer v = BufferUtil.toBuffer(value);
836 add(name, v);
837 }
838
839
840
841
842
843
844
845
846 public void putDateField(Buffer name, long date)
847 {
848 String d=formatDate(date);
849 Buffer v = new ByteArrayBuffer(d);
850 put(name, v);
851 }
852
853
854
855
856
857
858
859
860 public void putDateField(String name, long date)
861 {
862 Buffer n = HttpHeaders.CACHE.lookup(name);
863 putDateField(n,date);
864 }
865
866
867
868
869
870
871
872
873 public void addDateField(String name, long date)
874 {
875 String d=formatDate(date);
876 Buffer n = HttpHeaders.CACHE.lookup(name);
877 Buffer v = new ByteArrayBuffer(d);
878 add(n, v);
879 }
880
881
882
883
884
885
886
887 public void addSetCookie(HttpCookie cookie)
888 {
889 addSetCookie(
890 cookie.getName(),
891 cookie.getValue(),
892 cookie.getDomain(),
893 cookie.getPath(),
894 cookie.getMaxAge(),
895 cookie.getComment(),
896 cookie.isSecure(),
897 cookie.isHttpOnly(),
898 cookie.getVersion());
899 }
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914 public void addSetCookie(
915 final String name,
916 final String value,
917 final String domain,
918 final String path,
919 final long maxAge,
920 final String comment,
921 final boolean isSecure,
922 final boolean isHttpOnly,
923 int version)
924 {
925 String delim=__COOKIE_DELIM;
926
927
928 if (name == null || name.length() == 0)
929 throw new IllegalArgumentException("Bad cookie name");
930
931
932 StringBuilder buf = new StringBuilder(128);
933 String name_value_params;
934 QuotedStringTokenizer.quoteIfNeeded(buf, name, delim);
935 buf.append('=');
936 String start=buf.toString();
937 boolean hasDomain = false;
938 boolean hasPath = false;
939
940 if (value != null && value.length() > 0)
941 QuotedStringTokenizer.quoteIfNeeded(buf, value, delim);
942
943 if (comment != null && comment.length() > 0)
944 {
945 buf.append(";Comment=");
946 QuotedStringTokenizer.quoteIfNeeded(buf, comment, delim);
947 }
948
949 if (path != null && path.length() > 0)
950 {
951 hasPath = true;
952 buf.append(";Path=");
953 if (path.trim().startsWith("\""))
954 buf.append(path);
955 else
956 QuotedStringTokenizer.quoteIfNeeded(buf,path,delim);
957 }
958 if (domain != null && domain.length() > 0)
959 {
960 hasDomain = true;
961 buf.append(";Domain=");
962 QuotedStringTokenizer.quoteIfNeeded(buf,domain.toLowerCase(Locale.ENGLISH),delim);
963 }
964
965 if (maxAge >= 0)
966 {
967
968 buf.append(";Expires=");
969 if (maxAge == 0)
970 buf.append(__01Jan1970_COOKIE);
971 else
972 formatCookieDate(buf, System.currentTimeMillis() + 1000L * maxAge);
973
974 if (version >0)
975 {
976 buf.append(";Max-Age=");
977 buf.append(maxAge);
978 }
979 }
980
981 if (isSecure)
982 buf.append(";Secure");
983 if (isHttpOnly)
984 buf.append(";HttpOnly");
985
986 name_value_params = buf.toString();
987
988
989 Field field = getField(HttpHeaders.SET_COOKIE);
990 Field last=null;
991 while (field!=null)
992 {
993 String val = (field._value == null ? null : field._value.toString());
994 if (val!=null && val.startsWith(start))
995 {
996
997 if (((!hasDomain && !val.contains("Domain")) || (hasDomain && val.contains("Domain="+domain))) &&
998 ((!hasPath && !val.contains("Path")) || (hasPath && val.contains("Path="+path))))
999 {
1000 _fields.remove(field);
1001 if (last==null)
1002 _names.put(HttpHeaders.SET_COOKIE_BUFFER,field._next);
1003 else
1004 last._next=field._next;
1005 break;
1006 }
1007 }
1008 last=field;
1009 field=field._next;
1010 }
1011
1012 add(HttpHeaders.SET_COOKIE_BUFFER, new ByteArrayBuffer(name_value_params));
1013
1014
1015 put(HttpHeaders.EXPIRES_BUFFER, __01Jan1970_BUFFER);
1016 }
1017
1018
1019 public void putTo(Buffer buffer) throws IOException
1020 {
1021 for (int i = 0; i < _fields.size(); i++)
1022 {
1023 Field field = _fields.get(i);
1024 if (field != null)
1025 field.putTo(buffer);
1026 }
1027 BufferUtil.putCRLF(buffer);
1028 }
1029
1030
1031 public String toString()
1032 {
1033 try
1034 {
1035 StringBuffer buffer = new StringBuffer();
1036 for (int i = 0; i < _fields.size(); i++)
1037 {
1038 Field field = (Field) _fields.get(i);
1039 if (field != null)
1040 {
1041 String tmp = field.getName();
1042 if (tmp != null) buffer.append(tmp);
1043 buffer.append(": ");
1044 tmp = field.getValue();
1045 if (tmp != null) buffer.append(tmp);
1046 buffer.append("\r\n");
1047 }
1048 }
1049 buffer.append("\r\n");
1050 return buffer.toString();
1051 }
1052 catch (Exception e)
1053 {
1054 LOG.warn(e);
1055 return e.toString();
1056 }
1057 }
1058
1059
1060
1061
1062
1063 public void clear()
1064 {
1065 _fields.clear();
1066 _names.clear();
1067 }
1068
1069
1070
1071
1072
1073
1074
1075
1076 public void add(HttpFields fields)
1077 {
1078 if (fields == null) return;
1079
1080 Enumeration e = fields.getFieldNames();
1081 while (e.hasMoreElements())
1082 {
1083 String name = (String) e.nextElement();
1084 Enumeration values = fields.getValues(name);
1085 while (values.hasMoreElements())
1086 add(name, (String) values.nextElement());
1087 }
1088 }
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105 public static String valueParameters(String value, Map<String,String> parameters)
1106 {
1107 if (value == null) return null;
1108
1109 int i = value.indexOf(';');
1110 if (i < 0) return value;
1111 if (parameters == null) return value.substring(0, i).trim();
1112
1113 StringTokenizer tok1 = new QuotedStringTokenizer(value.substring(i), ";", false, true);
1114 while (tok1.hasMoreTokens())
1115 {
1116 String token = tok1.nextToken();
1117 StringTokenizer tok2 = new QuotedStringTokenizer(token, "= ");
1118 if (tok2.hasMoreTokens())
1119 {
1120 String paramName = tok2.nextToken();
1121 String paramVal = null;
1122 if (tok2.hasMoreTokens()) paramVal = tok2.nextToken();
1123 parameters.put(paramName, paramVal);
1124 }
1125 }
1126
1127 return value.substring(0, i).trim();
1128 }
1129
1130
1131 private static final Float __one = new Float("1.0");
1132 private static final Float __zero = new Float("0.0");
1133 private static final StringMap __qualities = new StringMap();
1134 static
1135 {
1136 __qualities.put(null, __one);
1137 __qualities.put("1.0", __one);
1138 __qualities.put("1", __one);
1139 __qualities.put("0.9", new Float("0.9"));
1140 __qualities.put("0.8", new Float("0.8"));
1141 __qualities.put("0.7", new Float("0.7"));
1142 __qualities.put("0.66", new Float("0.66"));
1143 __qualities.put("0.6", new Float("0.6"));
1144 __qualities.put("0.5", new Float("0.5"));
1145 __qualities.put("0.4", new Float("0.4"));
1146 __qualities.put("0.33", new Float("0.33"));
1147 __qualities.put("0.3", new Float("0.3"));
1148 __qualities.put("0.2", new Float("0.2"));
1149 __qualities.put("0.1", new Float("0.1"));
1150 __qualities.put("0", __zero);
1151 __qualities.put("0.0", __zero);
1152 }
1153
1154
1155 public static Float getQuality(String value)
1156 {
1157 if (value == null) return __zero;
1158
1159 int qe = value.indexOf(";");
1160 if (qe++ < 0 || qe == value.length()) return __one;
1161
1162 if (value.charAt(qe++) == 'q')
1163 {
1164 qe++;
1165 Map.Entry entry = __qualities.getEntry(value, qe, value.length() - qe);
1166 if (entry != null) return (Float) entry.getValue();
1167 }
1168
1169 HashMap params = new HashMap(3);
1170 valueParameters(value, params);
1171 String qs = (String) params.get("q");
1172 Float q = (Float) __qualities.get(qs);
1173 if (q == null)
1174 {
1175 try
1176 {
1177 q = new Float(qs);
1178 }
1179 catch (Exception e)
1180 {
1181 q = __one;
1182 }
1183 }
1184 return q;
1185 }
1186
1187
1188
1189
1190
1191
1192
1193
1194 public static List qualityList(Enumeration e)
1195 {
1196 if (e == null || !e.hasMoreElements()) return Collections.EMPTY_LIST;
1197
1198 Object list = null;
1199 Object qual = null;
1200
1201
1202 while (e.hasMoreElements())
1203 {
1204 String v = e.nextElement().toString();
1205 Float q = getQuality(v);
1206
1207 if (q.floatValue() >= 0.001)
1208 {
1209 list = LazyList.add(list, v);
1210 qual = LazyList.add(qual, q);
1211 }
1212 }
1213
1214 List vl = LazyList.getList(list, false);
1215 if (vl.size() < 2) return vl;
1216
1217 List ql = LazyList.getList(qual, false);
1218
1219
1220 Float last = __zero;
1221 for (int i = vl.size(); i-- > 0;)
1222 {
1223 Float q = (Float) ql.get(i);
1224 if (last.compareTo(q) > 0)
1225 {
1226 Object tmp = vl.get(i);
1227 vl.set(i, vl.get(i + 1));
1228 vl.set(i + 1, tmp);
1229 ql.set(i, ql.get(i + 1));
1230 ql.set(i + 1, q);
1231 last = __zero;
1232 i = vl.size();
1233 continue;
1234 }
1235 last = q;
1236 }
1237 ql.clear();
1238 return vl;
1239 }
1240
1241
1242
1243
1244 public static final class Field
1245 {
1246 private Buffer _name;
1247 private Buffer _value;
1248 private Field _next;
1249
1250
1251 private Field(Buffer name, Buffer value)
1252 {
1253 _name = name;
1254 _value = value;
1255 _next = null;
1256 }
1257
1258
1259 public void putTo(Buffer buffer) throws IOException
1260 {
1261 int o=(_name instanceof CachedBuffer)?((CachedBuffer)_name).getOrdinal():-1;
1262 if (o>=0)
1263 buffer.put(_name);
1264 else
1265 {
1266 int s=_name.getIndex();
1267 int e=_name.putIndex();
1268 while (s<e)
1269 {
1270 byte b=_name.peek(s++);
1271 switch(b)
1272 {
1273 case '\r':
1274 case '\n':
1275 case ':' :
1276 continue;
1277 default:
1278 buffer.put(b);
1279 }
1280 }
1281 }
1282
1283 buffer.put((byte) ':');
1284 buffer.put((byte) ' ');
1285
1286 o=(_value instanceof CachedBuffer)?((CachedBuffer)_value).getOrdinal():-1;
1287 if (o>=0)
1288 buffer.put(_value);
1289 else
1290 {
1291 int s=_value.getIndex();
1292 int e=_value.putIndex();
1293 while (s<e)
1294 {
1295 byte b=_value.peek(s++);
1296 switch(b)
1297 {
1298 case '\r':
1299 case '\n':
1300 continue;
1301 default:
1302 buffer.put(b);
1303 }
1304 }
1305 }
1306
1307 BufferUtil.putCRLF(buffer);
1308 }
1309
1310
1311 public String getName()
1312 {
1313 return BufferUtil.to8859_1_String(_name);
1314 }
1315
1316
1317 Buffer getNameBuffer()
1318 {
1319 return _name;
1320 }
1321
1322
1323 public int getNameOrdinal()
1324 {
1325 return HttpHeaders.CACHE.getOrdinal(_name);
1326 }
1327
1328
1329 public String getValue()
1330 {
1331 return BufferUtil.to8859_1_String(_value);
1332 }
1333
1334
1335 public Buffer getValueBuffer()
1336 {
1337 return _value;
1338 }
1339
1340
1341 public int getValueOrdinal()
1342 {
1343 return HttpHeaderValues.CACHE.getOrdinal(_value);
1344 }
1345
1346
1347 public int getIntValue()
1348 {
1349 return (int) getLongValue();
1350 }
1351
1352
1353 public long getLongValue()
1354 {
1355 return BufferUtil.toLong(_value);
1356 }
1357
1358
1359 public String toString()
1360 {
1361 return ("[" + getName() + "=" + _value + (_next == null ? "" : "->") + "]");
1362 }
1363 }
1364
1365 }