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