]> WPIA git - gigi.git/blob - lib/jetty/org/eclipse/jetty/http/HttpFields.java
Importing upstream Jetty jetty-9.2.1.v20140609
[gigi.git] / lib / jetty / org / eclipse / jetty / http / HttpFields.java
1 //
2 //  ========================================================================
3 //  Copyright (c) 1995-2014 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.Collection;
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.regex.Pattern;
34
35 import org.eclipse.jetty.util.ArrayTernaryTrie;
36 import org.eclipse.jetty.util.LazyList;
37 import org.eclipse.jetty.util.QuotedStringTokenizer;
38 import org.eclipse.jetty.util.StringUtil;
39 import org.eclipse.jetty.util.Trie;
40 import org.eclipse.jetty.util.log.Log;
41 import org.eclipse.jetty.util.log.Logger;
42
43
44 /**
45  * HTTP Fields. A collection of HTTP header and or Trailer fields.
46  *
47  * <p>This class is not synchronized as it is expected that modifications will only be performed by a
48  * single thread.
49  * 
50  * <p>The cookie handling provided by this class is guided by the Servlet specification and RFC6265.
51  *
52  */
53 public class HttpFields implements Iterable<HttpField>
54 {
55     private static final Logger LOG = Log.getLogger(HttpFields.class);
56     private final static Pattern __splitter = Pattern.compile("\\s*,\\s*");     
57     public final static String __separators = ", \t";
58
59     private final ArrayList<HttpField> _fields = new ArrayList<>(20);
60
61     /**
62      * Constructor.
63      */
64     public HttpFields()
65     {
66     }
67
68     /**
69      * Get Collection of header names.
70      */
71     public Collection<String> getFieldNamesCollection()
72     {
73         final Set<String> list = new HashSet<>(_fields.size());
74         for (HttpField f : _fields)
75         {
76             if (f!=null)
77                 list.add(f.getName());
78         }
79         return list;
80     }
81
82     /**
83      * Get enumeration of header _names. Returns an enumeration of strings representing the header
84      * _names for this request.
85      */
86     public Enumeration<String> getFieldNames()
87     {
88         return Collections.enumeration(getFieldNamesCollection());
89     }
90
91     public int size()
92     {
93         return _fields.size();
94     }
95
96     /**
97      * Get a Field by index.
98      * @return A Field value or null if the Field value has not been set
99      *
100      */
101     public HttpField getField(int i)
102     {
103         return _fields.get(i);
104     }
105
106     @Override
107     public Iterator<HttpField> iterator()
108     {
109         return _fields.iterator();
110     }
111
112     public HttpField getField(HttpHeader header)
113     {
114         for (int i=0;i<_fields.size();i++)
115         {
116             HttpField f=_fields.get(i);
117             if (f.getHeader()==header)
118                 return f;
119         }
120         return null;
121     }
122
123     public HttpField getField(String name)
124     {
125         for (int i=0;i<_fields.size();i++)
126         {
127             HttpField f=_fields.get(i);
128             if (f.getName().equalsIgnoreCase(name))
129                 return f;
130         }
131         return null;
132     }
133     
134     public boolean contains(HttpHeader header, String value)
135     {
136         for (int i=0;i<_fields.size();i++)
137         {
138             HttpField f=_fields.get(i);
139             if (f.getHeader()==header && contains(f,value))
140                 return true;
141         }
142         return false;
143     }
144     
145     public boolean contains(String name, String value)
146     {
147         for (int i=0;i<_fields.size();i++)
148         {
149             HttpField f=_fields.get(i);
150             if (f.getName().equalsIgnoreCase(name) && contains(f,value))
151                 return true;
152         }
153         return false;
154     }
155     
156     private boolean contains(HttpField field,String value)
157     {
158         String v = field.getValue();
159         if (v==null)
160             return false;
161
162         if (value.equalsIgnoreCase(v))
163             return true;
164
165         String[] split = __splitter.split(v);
166         for (int i = 0; split!=null && i < split.length; i++) 
167         {
168             if (value.equals(split[i]))
169                 return true;
170         }
171
172         return false;
173     }
174     
175     public boolean containsKey(String name)
176     {
177         for (int i=0;i<_fields.size();i++)
178         {
179             HttpField f=_fields.get(i);
180             if (f.getName().equalsIgnoreCase(name))
181                 return true;
182         }
183         return false;
184     }
185
186     public String getStringField(HttpHeader header)
187     {
188         return getStringField(header.asString());
189     }
190
191     public String get(HttpHeader header)
192     {
193         return getStringField(header.asString());
194     }
195
196     public String get(String header)
197     {
198         return getStringField(header);
199     }
200
201     /**
202      * @return the value of a field, or null if not found. For multiple fields of the same name,
203      *         only the first is returned.
204      * @param name the case-insensitive field name
205      */
206     public String getStringField(String name)
207     {
208         HttpField field = getField(name);
209         return field==null?null:field.getValue();
210     }
211
212     /**
213      * Get multi headers
214      *
215      * @return List the values
216      * @param name the case-insensitive field name
217      */
218     public List<String> getValuesList(String name)
219     {
220         final List<String> list = new ArrayList<>();
221         for (HttpField f : _fields)
222             if (f.getName().equalsIgnoreCase(name))
223                 list.add(f.getValue());
224         return list;
225     }
226
227     /**
228      * Get multi headers
229      *
230      * @return Enumeration of the values
231      * @param name the case-insensitive field name
232      */
233     public Enumeration<String> getValues(final String name)
234     {
235         for (int i=0;i<_fields.size();i++)
236         {
237             final HttpField f = _fields.get(i);
238             
239             if (f.getName().equalsIgnoreCase(name) && f.getValue()!=null)
240             {
241                 final int first=i;
242                 return new Enumeration<String>()
243                 {
244                     HttpField field=f;
245                     int i = first+1;
246
247                     @Override
248                     public boolean hasMoreElements()
249                     {
250                         if (field==null)
251                         {
252                             while (i<_fields.size()) 
253                             {
254                                 field=_fields.get(i++);
255                                 if (field.getName().equalsIgnoreCase(name) && field.getValue()!=null)
256                                     return true;
257                             }
258                             field=null;
259                             return false;
260                         }
261                         return true;
262                     }
263
264                     @Override
265                     public String nextElement() throws NoSuchElementException
266                     {
267                         if (hasMoreElements())
268                         {
269                             String value=field.getValue();
270                             field=null;
271                             return value;
272                         }
273                         throw new NoSuchElementException();
274                     }
275
276                 };
277             }
278         }
279
280         List<String> empty=Collections.emptyList();
281         return Collections.enumeration(empty);
282     }
283
284     /**
285      * Get multi field values with separator. The multiple values can be represented as separate
286      * headers of the same name, or by a single header using the separator(s), or a combination of
287      * both. Separators may be quoted.
288      *
289      * @param name the case-insensitive field name
290      * @param separators String of separators.
291      * @return Enumeration of the values, or null if no such header.
292      */
293     public Enumeration<String> getValues(String name, final String separators)
294     {
295         final Enumeration<String> e = getValues(name);
296         if (e == null)
297             return null;
298         return new Enumeration<String>()
299         {
300             QuotedStringTokenizer tok = null;
301
302             @Override
303             public boolean hasMoreElements()
304             {
305                 if (tok != null && tok.hasMoreElements()) return true;
306                 while (e.hasMoreElements())
307                 {
308                     String value = e.nextElement();
309                     if (value!=null)
310                     {
311                         tok = new QuotedStringTokenizer(value, separators, false, false);
312                         if (tok.hasMoreElements()) return true;
313                     }
314                 }
315                 tok = null;
316                 return false;
317             }
318
319             @Override
320             public String nextElement() throws NoSuchElementException
321             {
322                 if (!hasMoreElements()) throw new NoSuchElementException();
323                 String next = (String) tok.nextElement();
324                 if (next != null) next = next.trim();
325                 return next;
326             }
327         };
328     }
329
330     public void put(HttpField field)
331     {
332         boolean put=false;
333         for (int i=_fields.size();i-->0;)
334         {
335             HttpField f=_fields.get(i);
336             if (f.isSame(field))
337             {
338                 if (put)
339                     _fields.remove(i);
340                 else
341                 {
342                     _fields.set(i,field);
343                     put=true;
344                 }
345             }
346         }
347         if (!put)
348             _fields.add(field);
349     }
350     
351     /**
352      * Set a field.
353      *
354      * @param name the name of the field
355      * @param value the value of the field. If null the field is cleared.
356      */
357     public void put(String name, String value)
358     {
359         if (value == null)
360             remove(name);
361         else
362             put(new HttpField(name, value));
363     }
364
365     public void put(HttpHeader header, HttpHeaderValue value)
366     {
367         put(header,value.toString());
368     }
369
370     /**
371      * Set a field.
372      *
373      * @param header the header name of the field
374      * @param value the value of the field. If null the field is cleared.
375      */
376     public void put(HttpHeader header, String value)
377     {
378         if (value == null)
379             remove(header);
380         else
381             put(new HttpField(header, value));
382     }
383
384     /**
385      * Set a field.
386      *
387      * @param name the name of the field
388      * @param list the List value of the field. If null the field is cleared.
389      */
390     public void put(String name, List<String> list)
391     {
392         remove(name);
393         for (String v : list)
394             if (v!=null)
395                 add(name,v);
396     }
397
398     /**
399      * Add to or set a field. If the field is allowed to have multiple values, add will add multiple
400      * headers of the same name.
401      *
402      * @param name the name of the field
403      * @param value the value of the field.
404      * @exception IllegalArgumentException If the name is a single valued field and already has a
405      *                value.
406      */
407     public void add(String name, String value) throws IllegalArgumentException
408     {
409         if (value == null)
410             return;
411
412         HttpField field = new HttpField(name, value);
413         _fields.add(field);
414     }
415
416     public void add(HttpHeader header, HttpHeaderValue value) throws IllegalArgumentException
417     {
418         add(header,value.toString());
419     }
420
421     /**
422      * Add to or set a field. If the field is allowed to have multiple values, add will add multiple
423      * headers of the same name.
424      *
425      * @param header the header
426      * @param value the value of the field.
427      * @exception IllegalArgumentException 
428      */
429     public void add(HttpHeader header, String value) throws IllegalArgumentException
430     {
431         if (value == null) throw new IllegalArgumentException("null value");
432
433         HttpField field = new HttpField(header, value);
434         _fields.add(field);
435     }
436
437     /**
438      * Remove a field.
439      *
440      * @param name the field to remove
441      */
442     public HttpField remove(HttpHeader name)
443     {
444         for (int i=_fields.size();i-->0;)
445         {
446             HttpField f=_fields.get(i);
447             if (f.getHeader()==name)
448                 return _fields.remove(i);
449         }
450         return null;
451     }
452
453     /**
454      * Remove a field.
455      *
456      * @param name the field to remove
457      */
458     public HttpField remove(String name)
459     {
460         for (int i=_fields.size();i-->0;)
461         {
462             HttpField f=_fields.get(i);
463             if (f.getName().equalsIgnoreCase(name))
464                 return _fields.remove(i);
465         }
466         return null;
467     }
468
469     /**
470      * Get a header as an long value. Returns the value of an integer field or -1 if not found. The
471      * case of the field name is ignored.
472      *
473      * @param name the case-insensitive field name
474      * @exception NumberFormatException If bad long found
475      */
476     public long getLongField(String name) throws NumberFormatException
477     {
478         HttpField field = getField(name);
479         return field==null?-1L:StringUtil.toLong(field.getValue());
480     }
481
482     /**
483      * Get a header as a date value. Returns the value of a date field, or -1 if not found. The case
484      * of the field name is ignored.
485      *
486      * @param name the case-insensitive field name
487      */
488     public long getDateField(String name)
489     {
490         HttpField field = getField(name);
491         if (field == null)
492             return -1;
493
494         String val = valueParameters(field.getValue(), null);
495         if (val == null)
496             return -1;
497
498         final long date = DateParser.parseDate(val);
499         if (date==-1)
500             throw new IllegalArgumentException("Cannot convert date: " + val);
501         return date;
502     }
503
504
505     /**
506      * Sets the value of an long field.
507      *
508      * @param name the field name
509      * @param value the field long value
510      */
511     public void putLongField(HttpHeader name, long value)
512     {
513         String v = Long.toString(value);
514         put(name, v);
515     }
516
517     /**
518      * Sets the value of an long field.
519      *
520      * @param name the field name
521      * @param value the field long value
522      */
523     public void putLongField(String name, long value)
524     {
525         String v = Long.toString(value);
526         put(name, v);
527     }
528
529
530     /**
531      * Sets the value of a date field.
532      *
533      * @param name the field name
534      * @param date the field date value
535      */
536     public void putDateField(HttpHeader name, long date)
537     {
538         String d=DateGenerator.formatDate(date);
539         put(name, d);
540     }
541
542     /**
543      * Sets the value of a date field.
544      *
545      * @param name the field name
546      * @param date the field date value
547      */
548     public void putDateField(String name, long date)
549     {
550         String d=DateGenerator.formatDate(date);
551         put(name, d);
552     }
553
554     /**
555      * Sets the value of a date field.
556      *
557      * @param name the field name
558      * @param date the field date value
559      */
560     public void addDateField(String name, long date)
561     {
562         String d=DateGenerator.formatDate(date);
563         add(name,d);
564     }
565
566     @Override
567     public String
568     toString()
569     {
570         try
571         {
572             StringBuilder buffer = new StringBuilder();
573             for (HttpField field : _fields)
574             {
575                 if (field != null)
576                 {
577                     String tmp = field.getName();
578                     if (tmp != null) buffer.append(tmp);
579                     buffer.append(": ");
580                     tmp = field.getValue();
581                     if (tmp != null) buffer.append(tmp);
582                     buffer.append("\r\n");
583                 }
584             }
585             buffer.append("\r\n");
586             return buffer.toString();
587         }
588         catch (Exception e)
589         {
590             LOG.warn(e);
591             return e.toString();
592         }
593     }
594
595     /**
596      * Clear the header.
597      */
598     public void clear()
599     {
600         _fields.clear();
601     }
602
603     public void add(HttpField field)
604     {
605         _fields.add(field);
606     }
607
608     
609     
610     /**
611      * Add fields from another HttpFields instance. Single valued fields are replaced, while all
612      * others are added.
613      *
614      * @param fields the fields to add
615      */
616     public void add(HttpFields fields)
617     {
618         if (fields == null) return;
619
620         Enumeration<String> e = fields.getFieldNames();
621         while (e.hasMoreElements())
622         {
623             String name = e.nextElement();
624             Enumeration<String> values = fields.getValues(name);
625             while (values.hasMoreElements())
626                 add(name, values.nextElement());
627         }
628     }
629
630     /**
631      * Get field value parameters. Some field values can have parameters. This method separates the
632      * value from the parameters and optionally populates a map with the parameters. For example:
633      *
634      * <PRE>
635      *
636      * FieldName : Value ; param1=val1 ; param2=val2
637      *
638      * </PRE>
639      *
640      * @param value The Field value, possibly with parameteres.
641      * @param parameters A map to populate with the parameters, or null
642      * @return The value.
643      */
644     public static String valueParameters(String value, Map<String,String> parameters)
645     {
646         if (value == null) return null;
647
648         int i = value.indexOf(';');
649         if (i < 0) return value;
650         if (parameters == null) return value.substring(0, i).trim();
651
652         StringTokenizer tok1 = new QuotedStringTokenizer(value.substring(i), ";", false, true);
653         while (tok1.hasMoreTokens())
654         {
655             String token = tok1.nextToken();
656             StringTokenizer tok2 = new QuotedStringTokenizer(token, "= ");
657             if (tok2.hasMoreTokens())
658             {
659                 String paramName = tok2.nextToken();
660                 String paramVal = null;
661                 if (tok2.hasMoreTokens()) paramVal = tok2.nextToken();
662                 parameters.put(paramName, paramVal);
663             }
664         }
665
666         return value.substring(0, i).trim();
667     }
668
669     private static final Float __one = new Float("1.0");
670     private static final Float __zero = new Float("0.0");
671     private static final Trie<Float> __qualities = new ArrayTernaryTrie<>();
672     static
673     {
674         __qualities.put("*", __one);
675         __qualities.put("1.0", __one);
676         __qualities.put("1", __one);
677         __qualities.put("0.9", new Float("0.9"));
678         __qualities.put("0.8", new Float("0.8"));
679         __qualities.put("0.7", new Float("0.7"));
680         __qualities.put("0.66", new Float("0.66"));
681         __qualities.put("0.6", new Float("0.6"));
682         __qualities.put("0.5", new Float("0.5"));
683         __qualities.put("0.4", new Float("0.4"));
684         __qualities.put("0.33", new Float("0.33"));
685         __qualities.put("0.3", new Float("0.3"));
686         __qualities.put("0.2", new Float("0.2"));
687         __qualities.put("0.1", new Float("0.1"));
688         __qualities.put("0", __zero);
689         __qualities.put("0.0", __zero);
690     }
691
692     public static Float getQuality(String value)
693     {
694         if (value == null) return __zero;
695
696         int qe = value.indexOf(";");
697         if (qe++ < 0 || qe == value.length()) return __one;
698
699         if (value.charAt(qe++) == 'q')
700         {
701             qe++;
702             Float q = __qualities.get(value, qe, value.length() - qe);
703             if (q != null)
704                 return q;
705         }
706
707         Map<String,String> params = new HashMap<>(4);
708         valueParameters(value, params);
709         String qs = params.get("q");
710         if (qs==null)
711             qs="*";
712         Float q = __qualities.get(qs);
713         if (q == null)
714         {
715             try
716             {
717                 q = new Float(qs);
718             }
719             catch (Exception e)
720             {
721                 q = __one;
722             }
723         }
724         return q;
725     }
726
727     /**
728      * List values in quality order.
729      *
730      * @param e Enumeration of values with quality parameters
731      * @return values in quality order.
732      */
733     public static List<String> qualityList(Enumeration<String> e)
734     {
735         if (e == null || !e.hasMoreElements())
736             return Collections.emptyList();
737
738         Object list = null;
739         Object qual = null;
740
741         // Assume list will be well ordered and just add nonzero
742         while (e.hasMoreElements())
743         {
744             String v = e.nextElement();
745             Float q = getQuality(v);
746
747             if (q >= 0.001)
748             {
749                 list = LazyList.add(list, v);
750                 qual = LazyList.add(qual, q);
751             }
752         }
753
754         List<String> vl = LazyList.getList(list, false);
755         if (vl.size() < 2) 
756             return vl;
757
758         List<Float> ql = LazyList.getList(qual, false);
759
760         // sort list with swaps
761         Float last = __zero;
762         for (int i = vl.size(); i-- > 0;)
763         {
764             Float q = ql.get(i);
765             if (last.compareTo(q) > 0)
766             {
767                 String tmp = vl.get(i);
768                 vl.set(i, vl.get(i + 1));
769                 vl.set(i + 1, tmp);
770                 ql.set(i, ql.get(i + 1));
771                 ql.set(i + 1, q);
772                 last = __zero;
773                 i = vl.size();
774                 continue;
775             }
776             last = q;
777         }
778         ql.clear();
779         return vl;
780     }
781
782
783
784 }