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.util.ArrayList;
22 import java.util.Arrays;
23 import java.util.Collections;
24 import java.util.Enumeration;
25 import java.util.HashMap;
26 import java.util.HashSet;
27 import java.util.Iterator;
28 import java.util.List;
29 import java.util.Map;
30 import java.util.NoSuchElementException;
31 import java.util.Set;
32 import java.util.StringTokenizer;
33 import java.util.stream.Stream;
34 import java.util.stream.StreamSupport;
35
36 import org.eclipse.jetty.util.ArrayTernaryTrie;
37 import org.eclipse.jetty.util.QuotedStringTokenizer;
38 import org.eclipse.jetty.util.Trie;
39 import org.eclipse.jetty.util.log.Log;
40 import org.eclipse.jetty.util.log.Logger;
41
42
43
44
45
46
47
48
49
50
51
52 public class HttpFields implements Iterable<HttpField>
53 {
54 @Deprecated
55 public static final String __separators = ", \t";
56
57 private static final Logger LOG = Log.getLogger(HttpFields.class);
58
59 private HttpField[] _fields;
60 private int _size;
61
62
63
64
65 public HttpFields()
66 {
67 _fields=new HttpField[20];
68 }
69
70
71
72
73
74
75 public HttpFields(int capacity)
76 {
77 _fields=new HttpField[capacity];
78 }
79
80
81
82
83
84
85 public HttpFields(HttpFields fields)
86 {
87 _fields=Arrays.copyOf(fields._fields,fields._fields.length+10);
88 _size=fields._size;
89 }
90
91 public int size()
92 {
93 return _size;
94 }
95
96 @Override
97 public Iterator<HttpField> iterator()
98 {
99 return new Itr();
100 }
101
102 public Stream<HttpField> stream()
103 {
104 return StreamSupport.stream(Arrays.spliterator(_fields,0,_size),false);
105 }
106
107
108
109
110
111 public Set<String> getFieldNamesCollection()
112 {
113 final Set<String> set = new HashSet<>(_size);
114 for (HttpField f : this)
115 {
116 if (f!=null)
117 set.add(f.getName());
118 }
119 return set;
120 }
121
122
123
124
125
126
127 public Enumeration<String> getFieldNames()
128 {
129 return Collections.enumeration(getFieldNamesCollection());
130 }
131
132
133
134
135
136
137 public HttpField getField(int index)
138 {
139 if (index>=_size)
140 throw new NoSuchElementException();
141 return _fields[index];
142 }
143
144 public HttpField getField(HttpHeader header)
145 {
146 for (int i=0;i<_size;i++)
147 {
148 HttpField f=_fields[i];
149 if (f.getHeader()==header)
150 return f;
151 }
152 return null;
153 }
154
155 public HttpField getField(String name)
156 {
157 for (int i=0;i<_size;i++)
158 {
159 HttpField f=_fields[i];
160 if (f.getName().equalsIgnoreCase(name))
161 return f;
162 }
163 return null;
164 }
165
166 public boolean contains(HttpField field)
167 {
168 for (int i=_size;i-->0;)
169 {
170 HttpField f=_fields[i];
171 if (f.isSameName(field) && (f.equals(field)||f.contains(field.getValue())))
172 return true;
173 }
174 return false;
175 }
176
177 public boolean contains(HttpHeader header, String value)
178 {
179 for (int i=_size;i-->0;)
180 {
181 HttpField f=_fields[i];
182 if (f.getHeader()==header && f.contains(value))
183 return true;
184 }
185 return false;
186 }
187
188 public boolean contains(String name, String value)
189 {
190 for (int i=_size;i-->0;)
191 {
192 HttpField f=_fields[i];
193 if (f.getName().equalsIgnoreCase(name) && f.contains(value))
194 return true;
195 }
196 return false;
197 }
198
199 public boolean contains(HttpHeader header)
200 {
201 for (int i=_size;i-->0;)
202 {
203 HttpField f=_fields[i];
204 if (f.getHeader()==header)
205 return true;
206 }
207 return false;
208 }
209
210 public boolean containsKey(String name)
211 {
212 for (int i=_size;i-->0;)
213 {
214 HttpField f=_fields[i];
215 if (f.getName().equalsIgnoreCase(name))
216 return true;
217 }
218 return false;
219 }
220
221 @Deprecated
222 public String getStringField(HttpHeader header)
223 {
224 return get(header);
225 }
226
227 public String get(HttpHeader header)
228 {
229 for (int i=0;i<_size;i++)
230 {
231 HttpField f=_fields[i];
232 if (f.getHeader()==header)
233 return f.getValue();
234 }
235 return null;
236 }
237
238 @Deprecated
239 public String getStringField(String name)
240 {
241 return get(name);
242 }
243
244 public String get(String header)
245 {
246 for (int i=0;i<_size;i++)
247 {
248 HttpField f=_fields[i];
249 if (f.getName().equalsIgnoreCase(header))
250 return f.getValue();
251 }
252 return null;
253 }
254
255
256
257
258
259
260
261 public List<String> getValuesList(HttpHeader header)
262 {
263 final List<String> list = new ArrayList<>();
264 for (HttpField f : this)
265 if (f.getHeader()==header)
266 list.add(f.getValue());
267 return list;
268 }
269
270
271
272
273
274
275
276 public List<String> getValuesList(String name)
277 {
278 final List<String> list = new ArrayList<>();
279 for (HttpField f : this)
280 if (f.getName().equalsIgnoreCase(name))
281 list.add(f.getValue());
282 return list;
283 }
284
285
286
287
288
289
290
291
292
293 public boolean addCSV(HttpHeader header,String... values)
294 {
295 QuotedCSV existing = null;
296 for (HttpField f : this)
297 {
298 if (f.getHeader()==header)
299 {
300 if (existing==null)
301 existing = new QuotedCSV(false);
302 existing.addValue(f.getValue());
303 }
304 }
305
306 String value = addCSV(existing,values);
307 if (value!=null)
308 {
309 add(header,value);
310 return true;
311 }
312 return false;
313 }
314
315
316
317
318
319
320
321
322 public boolean addCSV(String name,String... values)
323 {
324 QuotedCSV existing = null;
325 for (HttpField f : this)
326 {
327 if (f.getName().equalsIgnoreCase(name))
328 {
329 if (existing==null)
330 existing = new QuotedCSV(false);
331 existing.addValue(f.getValue());
332 }
333 }
334 String value = addCSV(existing,values);
335 if (value!=null)
336 {
337 add(name,value);
338 return true;
339 }
340 return false;
341 }
342
343 protected String addCSV(QuotedCSV existing,String... values)
344 {
345
346 boolean add = true;
347 if (existing!=null && !existing.isEmpty())
348 {
349 add = false;
350
351 for (int i=values.length;i-->0;)
352 {
353 String unquoted = QuotedCSV.unquote(values[i]);
354 if (existing.getValues().contains(unquoted))
355 values[i] = null;
356 else
357 add = true;
358 }
359 }
360
361 if (add)
362 {
363 StringBuilder value = new StringBuilder();
364 for (String v:values)
365 {
366 if (v==null)
367 continue;
368 if (value.length()>0)
369 value.append(", ");
370 value.append(v);
371 }
372 if (value.length()>0)
373 return value.toString();
374 }
375
376 return null;
377 }
378
379
380
381
382
383
384
385
386
387 public List<String> getCSV(HttpHeader header,boolean keepQuotes)
388 {
389 QuotedCSV values = null;
390 for (HttpField f : this)
391 {
392 if (f.getHeader()==header)
393 {
394 if (values==null)
395 values = new QuotedCSV(keepQuotes);
396 values.addValue(f.getValue());
397 }
398 }
399 return values==null?Collections.emptyList():values.getValues();
400 }
401
402
403
404
405
406
407
408
409
410 public List<String> getCSV(String name,boolean keepQuotes)
411 {
412 QuotedCSV values = null;
413 for (HttpField f : this)
414 {
415 if (f.getName().equalsIgnoreCase(name))
416 {
417 if (values==null)
418 values = new QuotedCSV(keepQuotes);
419 values.addValue(f.getValue());
420 }
421 }
422 return values==null?Collections.emptyList():values.getValues();
423 }
424
425
426
427
428
429
430
431
432 public List<String> getQualityCSV(HttpHeader header)
433 {
434 QuotedQualityCSV values = null;
435 for (HttpField f : this)
436 {
437 if (f.getHeader()==header)
438 {
439 if (values==null)
440 values = new QuotedQualityCSV();
441 values.addValue(f.getValue());
442 }
443 }
444
445 return values==null?Collections.emptyList():values.getValues();
446 }
447
448
449
450
451
452
453
454
455 public List<String> getQualityCSV(String name)
456 {
457 QuotedQualityCSV values = null;
458 for (HttpField f : this)
459 {
460 if (f.getName().equalsIgnoreCase(name))
461 {
462 if (values==null)
463 values = new QuotedQualityCSV();
464 values.addValue(f.getValue());
465 }
466 }
467 return values==null?Collections.emptyList():values.getValues();
468 }
469
470
471
472
473
474
475
476 public Enumeration<String> getValues(final String name)
477 {
478 for (int i=0;i<_size;i++)
479 {
480 final HttpField f = _fields[i];
481
482 if (f.getName().equalsIgnoreCase(name) && f.getValue()!=null)
483 {
484 final int first=i;
485 return new Enumeration<String>()
486 {
487 HttpField field=f;
488 int i = first+1;
489
490 @Override
491 public boolean hasMoreElements()
492 {
493 if (field==null)
494 {
495 while (i<_size)
496 {
497 field=_fields[i++];
498 if (field.getName().equalsIgnoreCase(name) && field.getValue()!=null)
499 return true;
500 }
501 field=null;
502 return false;
503 }
504 return true;
505 }
506
507 @Override
508 public String nextElement() throws NoSuchElementException
509 {
510 if (hasMoreElements())
511 {
512 String value=field.getValue();
513 field=null;
514 return value;
515 }
516 throw new NoSuchElementException();
517 }
518 };
519 }
520 }
521
522 List<String> empty=Collections.emptyList();
523 return Collections.enumeration(empty);
524 }
525
526
527
528
529
530
531
532
533
534
535 @Deprecated
536 public Enumeration<String> getValues(String name, final String separators)
537 {
538 final Enumeration<String> e = getValues(name);
539 if (e == null)
540 return null;
541 return new Enumeration<String>()
542 {
543 QuotedStringTokenizer tok = null;
544
545 @Override
546 public boolean hasMoreElements()
547 {
548 if (tok != null && tok.hasMoreElements()) return true;
549 while (e.hasMoreElements())
550 {
551 String value = e.nextElement();
552 if (value!=null)
553 {
554 tok = new QuotedStringTokenizer(value, separators, false, false);
555 if (tok.hasMoreElements()) return true;
556 }
557 }
558 tok = null;
559 return false;
560 }
561
562 @Override
563 public String nextElement() throws NoSuchElementException
564 {
565 if (!hasMoreElements()) throw new NoSuchElementException();
566 String next = (String) tok.nextElement();
567 if (next != null) next = next.trim();
568 return next;
569 }
570 };
571 }
572
573 public void put(HttpField field)
574 {
575 boolean put=false;
576 for (int i=_size;i-->0;)
577 {
578 HttpField f=_fields[i];
579 if (f.isSameName(field))
580 {
581 if (put)
582 {
583 System.arraycopy(_fields,i+1,_fields,i,--_size-i);
584 }
585 else
586 {
587 _fields[i]=field;
588 put=true;
589 }
590 }
591 }
592 if (!put)
593 add(field);
594 }
595
596
597
598
599
600
601
602 public void put(String name, String value)
603 {
604 if (value == null)
605 remove(name);
606 else
607 put(new HttpField(name, value));
608 }
609
610 public void put(HttpHeader header, HttpHeaderValue value)
611 {
612 put(header,value.toString());
613 }
614
615
616
617
618
619
620
621 public void put(HttpHeader header, String value)
622 {
623 if (value == null)
624 remove(header);
625 else
626 put(new HttpField(header, value));
627 }
628
629
630
631
632
633
634
635 public void put(String name, List<String> list)
636 {
637 remove(name);
638 for (String v : list)
639 if (v!=null)
640 add(name,v);
641 }
642
643
644
645
646
647
648
649
650 public void add(String name, String value)
651 {
652 if (value == null)
653 return;
654
655 HttpField field = new HttpField(name, value);
656 add(field);
657 }
658
659 public void add(HttpHeader header, HttpHeaderValue value)
660 {
661 add(header,value.toString());
662 }
663
664
665
666
667
668
669
670
671 public void add(HttpHeader header, String value)
672 {
673 if (value == null) throw new IllegalArgumentException("null value");
674
675 HttpField field = new HttpField(header, value);
676 add(field);
677 }
678
679
680
681
682
683
684
685 public HttpField remove(HttpHeader name)
686 {
687 HttpField removed=null;
688 for (int i=_size;i-->0;)
689 {
690 HttpField f=_fields[i];
691 if (f.getHeader()==name)
692 {
693 removed=f;
694 System.arraycopy(_fields,i+1,_fields,i,--_size-i);
695 }
696 }
697 return removed;
698 }
699
700
701
702
703
704
705
706 public HttpField remove(String name)
707 {
708 HttpField removed=null;
709 for (int i=_size;i-->0;)
710 {
711 HttpField f=_fields[i];
712 if (f.getName().equalsIgnoreCase(name))
713 {
714 removed=f;
715 System.arraycopy(_fields,i+1,_fields,i,--_size-i);
716 }
717 }
718 return removed;
719 }
720
721
722
723
724
725
726
727
728
729 public long getLongField(String name) throws NumberFormatException
730 {
731 HttpField field = getField(name);
732 return field==null?-1L:field.getLongValue();
733 }
734
735
736
737
738
739
740
741
742 public long getDateField(String name)
743 {
744 HttpField field = getField(name);
745 if (field == null)
746 return -1;
747
748 String val = valueParameters(field.getValue(), null);
749 if (val == null)
750 return -1;
751
752 final long date = DateParser.parseDate(val);
753 if (date==-1)
754 throw new IllegalArgumentException("Cannot convert date: " + val);
755 return date;
756 }
757
758
759
760
761
762
763
764
765 public void putLongField(HttpHeader name, long value)
766 {
767 String v = Long.toString(value);
768 put(name, v);
769 }
770
771
772
773
774
775
776
777 public void putLongField(String name, long value)
778 {
779 String v = Long.toString(value);
780 put(name, v);
781 }
782
783
784
785
786
787
788
789
790 public void putDateField(HttpHeader name, long date)
791 {
792 String d=DateGenerator.formatDate(date);
793 put(name, d);
794 }
795
796
797
798
799
800
801
802 public void putDateField(String name, long date)
803 {
804 String d=DateGenerator.formatDate(date);
805 put(name, d);
806 }
807
808
809
810
811
812
813
814 public void addDateField(String name, long date)
815 {
816 String d=DateGenerator.formatDate(date);
817 add(name,d);
818 }
819
820 @Override
821 public int hashCode()
822 {
823 int hash=0;
824 for (HttpField field:_fields)
825 hash+=field.hashCode();
826 return hash;
827 }
828
829 @Override
830 public boolean equals(Object o)
831 {
832 if (this == o)
833 return true;
834 if (!(o instanceof HttpFields))
835 return false;
836
837 HttpFields that = (HttpFields)o;
838
839
840 if (size() != that.size())
841 return false;
842
843 loop: for (HttpField fi : this)
844 {
845 for (HttpField fa : that)
846 {
847 if (fi.equals(fa))
848 continue loop;
849 }
850 return false;
851 }
852 return true;
853 }
854
855 @Override
856 public String toString()
857 {
858 try
859 {
860 StringBuilder buffer = new StringBuilder();
861 for (HttpField field : this)
862 {
863 if (field != null)
864 {
865 String tmp = field.getName();
866 if (tmp != null) buffer.append(tmp);
867 buffer.append(": ");
868 tmp = field.getValue();
869 if (tmp != null) buffer.append(tmp);
870 buffer.append("\r\n");
871 }
872 }
873 buffer.append("\r\n");
874 return buffer.toString();
875 }
876 catch (Exception e)
877 {
878 LOG.warn(e);
879 return e.toString();
880 }
881 }
882
883 public void clear()
884 {
885 _size=0;
886 }
887
888 public void add(HttpField field)
889 {
890 if (field!=null)
891 {
892 if (_size==_fields.length)
893 _fields=Arrays.copyOf(_fields,_size*2);
894 _fields[_size++]=field;
895 }
896 }
897
898 public void addAll(HttpFields fields)
899 {
900 for (int i=0;i<fields._size;i++)
901 add(fields._fields[i]);
902 }
903
904
905
906
907
908
909
910 public void add(HttpFields fields)
911 {
912 if (fields == null) return;
913
914 Enumeration<String> e = fields.getFieldNames();
915 while (e.hasMoreElements())
916 {
917 String name = e.nextElement();
918 Enumeration<String> values = fields.getValues(name);
919 while (values.hasMoreElements())
920 add(name, values.nextElement());
921 }
922 }
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937 public static String stripParameters(String value)
938 {
939 if (value == null) return null;
940
941 int i = value.indexOf(';');
942 if (i < 0) return value;
943 return value.substring(0, i).trim();
944 }
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960 public static String valueParameters(String value, Map<String,String> parameters)
961 {
962 if (value == null) return null;
963
964 int i = value.indexOf(';');
965 if (i < 0) return value;
966 if (parameters == null) return value.substring(0, i).trim();
967
968 StringTokenizer tok1 = new QuotedStringTokenizer(value.substring(i), ";", false, true);
969 while (tok1.hasMoreTokens())
970 {
971 String token = tok1.nextToken();
972 StringTokenizer tok2 = new QuotedStringTokenizer(token, "= ");
973 if (tok2.hasMoreTokens())
974 {
975 String paramName = tok2.nextToken();
976 String paramVal = null;
977 if (tok2.hasMoreTokens()) paramVal = tok2.nextToken();
978 parameters.put(paramName, paramVal);
979 }
980 }
981
982 return value.substring(0, i).trim();
983 }
984
985 @Deprecated
986 private static final Float __one = new Float("1.0");
987 @Deprecated
988 private static final Float __zero = new Float("0.0");
989 @Deprecated
990 private static final Trie<Float> __qualities = new ArrayTernaryTrie<>();
991 static
992 {
993 __qualities.put("*", __one);
994 __qualities.put("1.0", __one);
995 __qualities.put("1", __one);
996 __qualities.put("0.9", new Float("0.9"));
997 __qualities.put("0.8", new Float("0.8"));
998 __qualities.put("0.7", new Float("0.7"));
999 __qualities.put("0.66", new Float("0.66"));
1000 __qualities.put("0.6", new Float("0.6"));
1001 __qualities.put("0.5", new Float("0.5"));
1002 __qualities.put("0.4", new Float("0.4"));
1003 __qualities.put("0.33", new Float("0.33"));
1004 __qualities.put("0.3", new Float("0.3"));
1005 __qualities.put("0.2", new Float("0.2"));
1006 __qualities.put("0.1", new Float("0.1"));
1007 __qualities.put("0", __zero);
1008 __qualities.put("0.0", __zero);
1009 }
1010
1011 @Deprecated
1012 public static Float getQuality(String value)
1013 {
1014 if (value == null) return __zero;
1015
1016 int qe = value.indexOf(";");
1017 if (qe++ < 0 || qe == value.length()) return __one;
1018
1019 if (value.charAt(qe++) == 'q')
1020 {
1021 qe++;
1022 Float q = __qualities.get(value, qe, value.length() - qe);
1023 if (q != null)
1024 return q;
1025 }
1026
1027 Map<String,String> params = new HashMap<>(4);
1028 valueParameters(value, params);
1029 String qs = params.get("q");
1030 if (qs==null)
1031 qs="*";
1032 Float q = __qualities.get(qs);
1033 if (q == null)
1034 {
1035 try
1036 {
1037 q = new Float(qs);
1038 }
1039 catch (Exception e)
1040 {
1041 q = __one;
1042 }
1043 }
1044 return q;
1045 }
1046
1047
1048
1049
1050
1051
1052
1053 @Deprecated
1054 public static List<String> qualityList(Enumeration<String> e)
1055 {
1056 if (e == null || !e.hasMoreElements())
1057 return Collections.emptyList();
1058
1059 QuotedQualityCSV values = new QuotedQualityCSV();
1060 while(e.hasMoreElements())
1061 values.addValue(e.nextElement());
1062 return values.getValues();
1063 }
1064
1065
1066 private class Itr implements Iterator<HttpField>
1067 {
1068 int _cursor;
1069 int _last=-1;
1070
1071 public boolean hasNext()
1072 {
1073 return _cursor != _size;
1074 }
1075
1076 public HttpField next()
1077 {
1078 int i = _cursor;
1079 if (i >= _size)
1080 throw new NoSuchElementException();
1081 _cursor = i + 1;
1082 return _fields[_last=i];
1083 }
1084
1085 public void remove()
1086 {
1087 if (_last<0)
1088 throw new IllegalStateException();
1089
1090 System.arraycopy(_fields,_last+1,_fields,_last,--_size-_last);
1091 _cursor=_last;
1092 _last=-1;
1093 }
1094 }
1095
1096 }