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