View Javadoc

1   //
2   //  ========================================================================
3   //  Copyright (c) 1995-2016 Mort Bay Consulting Pty. Ltd.
4   //  ------------------------------------------------------------------------
5   //  All rights reserved. This program and the accompanying materials
6   //  are made available under the terms of the Eclipse Public License v1.0
7   //  and Apache License v2.0 which accompanies this distribution.
8   //
9   //      The Eclipse Public License is available at
10  //      http://www.eclipse.org/legal/epl-v10.html
11  //
12  //      The Apache License v2.0 is available at
13  //      http://www.opensource.org/licenses/apache2.0.php
14  //
15  //  You may elect to redistribute this code under either of these licenses.
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   * HTTP Fields. A collection of HTTP header and or Trailer fields.
45   *
46   * <p>This class is not synchronized as it is expected that modifications will only be performed by a
47   * single thread.
48   * 
49   * <p>The cookie handling provided by this class is guided by the Servlet specification and RFC6265.
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       * Initialize an empty HttpFields.
64       */
65      public HttpFields()
66      {
67          _fields=new HttpField[20];
68      }
69      
70      /**
71       * Initialize an empty HttpFields.
72       * 
73       * @param capacity the capacity of the http fields
74       */
75      public HttpFields(int capacity)
76      {
77          _fields=new HttpField[capacity];
78      }
79      
80      /**
81       * Initialize HttpFields from copy.
82       * 
83       * @param fields the fields to copy data from
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      * Get Collection of header names.
109      * @return the unique set of field names.
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      * Get enumeration of header _names. Returns an enumeration of strings representing the header
124      * _names for this request.
125      * @return an enumeration of field names
126      */
127     public Enumeration<String> getFieldNames()
128     {
129         return Collections.enumeration(getFieldNamesCollection());
130     }
131 
132     /**
133      * Get a Field by index.
134      * @param index the field index 
135      * @return A Field value or null if the Field value has not been set
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      * Get multiple header of the same name
257      *
258      * @return List the values
259      * @param header the header
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      * Get multiple header of the same name
272      *    
273      * @return List the header values
274      * @param name the case-insensitive field name
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      * Add comma separated values, but only if not already
288      * present.
289      * @param header The header to add the value(s) to
290      * @param values The value(s) to add
291      * @return True if headers were modified
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      * Add comma separated values, but only if not already
317      * present.
318      * @param name The header to add the value(s) to
319      * @param values The value(s) to add
320      * @return True if headers were modified
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         // remove any existing values from the new values
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      * Get multiple field values of the same name, split 
381      * as a {@link QuotedCSV}
382      *
383      * @return List the values with OWS stripped
384      * @param header The header
385      * @param keepQuotes True if the fields are kept quoted
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      * Get multiple field values of the same name
404      * as a {@link QuotedCSV}
405      *
406      * @return List the values with OWS stripped
407      * @param name the case-insensitive field name
408      * @param keepQuotes True if the fields are kept quoted
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      * Get multiple field values of the same name, split and
427      * sorted as a {@link QuotedQualityCSV}
428      *
429      * @return List the values in quality order with the q param and OWS stripped
430      * @param header The header
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      * Get multiple field values of the same name, split and
450      * sorted as a {@link QuotedQualityCSV}
451      *
452      * @return List the values in quality order with the q param and OWS stripped
453      * @param name the case-insensitive field name
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      * Get multi headers
472      *
473      * @return Enumeration of the values
474      * @param name the case-insensitive field name
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      * Get multi field values with separator. The multiple values can be represented as separate
528      * headers of the same name, or by a single header using the separator(s), or a combination of
529      * both. Separators may be quoted.
530      *
531      * @param name the case-insensitive field name
532      * @param separators String of separators.
533      * @return Enumeration of the values, or null if no such header.
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      * Set a field.
598      *
599      * @param name the name of the field
600      * @param value the value of the field. If null the field is cleared.
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      * Set a field.
617      *
618      * @param header the header name of the field
619      * @param value the value of the field. If null the field is cleared.
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      * Set a field.
631      *
632      * @param name the name of the field
633      * @param list the List value of the field. If null the field is cleared.
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      * Add to or set a field. If the field is allowed to have multiple values, add will add multiple
645      * headers of the same name.
646      *
647      * @param name the name of the field
648      * @param value the value of the field.
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      * Add to or set a field. If the field is allowed to have multiple values, add will add multiple
666      * headers of the same name.
667      *
668      * @param header the header
669      * @param value the value of the field.
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      * Remove a field.
681      *
682      * @param name the field to remove
683      * @return the header that was removed
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      * Remove a field.
702      *
703      * @param name the field to remove
704      * @return the header that was removed
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      * Get a header as an long value. Returns the value of an integer field or -1 if not found. The
723      * case of the field name is ignored.
724      *
725      * @param name the case-insensitive field name
726      * @return the value of the field as a long
727      * @exception NumberFormatException If bad long found
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      * Get a header as a date value. Returns the value of a date field, or -1 if not found. The case
737      * of the field name is ignored.
738      *
739      * @param name the case-insensitive field name
740      * @return the value of the field as a number of milliseconds since unix epoch
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      * Sets the value of an long field.
761      *
762      * @param name the field name
763      * @param value the field long value
764      */
765     public void putLongField(HttpHeader name, long value)
766     {
767         String v = Long.toString(value);
768         put(name, v);
769     }
770 
771     /**
772      * Sets the value of an long field.
773      *
774      * @param name the field name
775      * @param value the field long value
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      * Sets the value of a date field.
786      *
787      * @param name the field name
788      * @param date the field date value
789      */
790     public void putDateField(HttpHeader name, long date)
791     {
792         String d=DateGenerator.formatDate(date);
793         put(name, d);
794     }
795 
796     /**
797      * Sets the value of a date field.
798      *
799      * @param name the field name
800      * @param date the field date value
801      */
802     public void putDateField(String name, long date)
803     {
804         String d=DateGenerator.formatDate(date);
805         put(name, d);
806     }
807 
808     /**
809      * Sets the value of a date field.
810      *
811      * @param name the field name
812      * @param date the field date value
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         // Order is not important, so we cannot rely on List.equals().
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      * Add fields from another HttpFields instance. Single valued fields are replaced, while all
906      * others are added.
907      *
908      * @param fields the fields to add
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      * Get field value without parameters. Some field values can have parameters. This method separates the
926      * value from the parameters and optionally populates a map with the parameters. For example:
927      *
928      * <PRE>
929      *
930      * FieldName : Value ; param1=val1 ; param2=val2
931      *
932      * </PRE>
933      *
934      * @param value The Field value, possibly with parameters.
935      * @return The value.
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      * Get field value parameters. Some field values can have parameters. This method separates the
948      * value from the parameters and optionally populates a map with the parameters. For example:
949      *
950      * <PRE>
951      *
952      * FieldName : Value ; param1=val1 ; param2=val2
953      *
954      * </PRE>
955      *
956      * @param value The Field value, possibly with parameters.
957      * @param parameters A map to populate with the parameters, or null
958      * @return The value.
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      * List values in quality order.
1049      *
1050      * @param e Enumeration of values with quality parameters
1051      * @return values in quality order.
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;       // index of next element to return
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 }