View Javadoc
1   /*
2    * Copyright 2014 The Netty Project
3    *
4    * The Netty Project licenses this file to you under the Apache License,
5    * version 2.0 (the "License"); you may not use this file except in compliance
6    * with the License. You may obtain a copy of the License at:
7    *
8    *   http://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12   * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13   * License for the specific language governing permissions and limitations
14   * under the License.
15   */
16  
17  package io.netty.handler.codec;
18  
19  import io.netty.util.internal.PlatformDependent;
20  import io.netty.util.internal.StringUtil;
21  
22  import java.text.ParseException;
23  import java.util.Comparator;
24  import java.util.Iterator;
25  
26  import static io.netty.handler.codec.AsciiString.*;
27  import static io.netty.util.internal.StringUtil.COMMA;
28  
29  public class DefaultTextHeaders extends DefaultConvertibleHeaders<CharSequence, String> implements TextHeaders {
30      private static final HashCodeGenerator<CharSequence> CHARSEQUECE_CASE_INSENSITIVE_HASH_CODE_GENERATOR =
31              new HashCodeGenerator<CharSequence>() {
32          @Override
33          public int generateHashCode(CharSequence name) {
34              return AsciiString.caseInsensitiveHashCode(name);
35          }
36      };
37  
38      private static final HashCodeGenerator<CharSequence> CHARSEQUECE_CASE_SENSITIVE_HASH_CODE_GENERATOR =
39              new HashCodeGenerator<CharSequence>() {
40          @Override
41          public int generateHashCode(CharSequence name) {
42              return name.hashCode();
43          }
44      };
45  
46      public static class DefaultTextValueTypeConverter implements ValueConverter<CharSequence> {
47          @Override
48          public CharSequence convertObject(Object value) {
49              if (value instanceof CharSequence) {
50                  return (CharSequence) value;
51              }
52              return value.toString();
53          }
54  
55          @Override
56          public CharSequence convertInt(int value) {
57              return String.valueOf(value);
58          }
59  
60          @Override
61          public CharSequence convertLong(long value) {
62              return String.valueOf(value);
63          }
64  
65          @Override
66          public CharSequence convertDouble(double value) {
67              return String.valueOf(value);
68          }
69  
70          @Override
71          public CharSequence convertChar(char value) {
72              return String.valueOf(value);
73          }
74  
75          @Override
76          public CharSequence convertBoolean(boolean value) {
77              return String.valueOf(value);
78          }
79  
80          @Override
81          public CharSequence convertFloat(float value) {
82              return String.valueOf(value);
83          }
84  
85          @Override
86          public boolean convertToBoolean(CharSequence value) {
87              return Boolean.parseBoolean(value.toString());
88          }
89  
90          @Override
91          public CharSequence convertByte(byte value) {
92              return String.valueOf(value);
93          }
94  
95          @Override
96          public byte convertToByte(CharSequence value) {
97              return Byte.valueOf(value.toString());
98          }
99  
100         @Override
101         public char convertToChar(CharSequence value) {
102             return value.charAt(0);
103         }
104 
105         @Override
106         public CharSequence convertShort(short value) {
107             return String.valueOf(value);
108         }
109 
110         @Override
111         public short convertToShort(CharSequence value) {
112             return Short.valueOf(value.toString());
113         }
114 
115         @Override
116         public int convertToInt(CharSequence value) {
117             return Integer.parseInt(value.toString());
118         }
119 
120         @Override
121         public long convertToLong(CharSequence value) {
122             return Long.parseLong(value.toString());
123         }
124 
125         @Override
126         public AsciiString convertTimeMillis(long value) {
127             return new AsciiString(String.valueOf(value));
128         }
129 
130         @Override
131         public long convertToTimeMillis(CharSequence value) {
132             try {
133                 return HeaderDateFormat.get().parse(value.toString());
134             } catch (ParseException e) {
135                 PlatformDependent.throwException(e);
136             }
137             return 0;
138         }
139 
140         @Override
141         public float convertToFloat(CharSequence value) {
142             return Float.valueOf(value.toString());
143         }
144 
145         @Override
146         public double convertToDouble(CharSequence value) {
147             return Double.valueOf(value.toString());
148         }
149     }
150 
151     private static final ValueConverter<CharSequence> CHARSEQUENCE_FROM_OBJECT_CONVERTER =
152             new DefaultTextValueTypeConverter();
153     private static final TypeConverter<CharSequence, String> CHARSEQUENCE_TO_STRING_CONVERTER =
154             new TypeConverter<CharSequence, String>() {
155         @Override
156         public String toConvertedType(CharSequence value) {
157             return value.toString();
158         }
159 
160         @Override
161         public CharSequence toUnconvertedType(String value) {
162             return value;
163         }
164     };
165 
166     private static final NameConverter<CharSequence> CHARSEQUENCE_IDENTITY_CONVERTER =
167             new IdentityNameConverter<CharSequence>();
168     /**
169      * An estimate of the size of a header value.
170      */
171     private static final int DEFAULT_VALUE_SIZE = 10;
172 
173     private final ValuesComposer valuesComposer;
174 
175     public DefaultTextHeaders() {
176         this(true);
177     }
178 
179     public DefaultTextHeaders(boolean ignoreCase) {
180         this(ignoreCase, CHARSEQUENCE_FROM_OBJECT_CONVERTER, CHARSEQUENCE_IDENTITY_CONVERTER, false);
181     }
182 
183     public DefaultTextHeaders(boolean ignoreCase, boolean singleHeaderFields) {
184         this(ignoreCase, CHARSEQUENCE_FROM_OBJECT_CONVERTER, CHARSEQUENCE_IDENTITY_CONVERTER, singleHeaderFields);
185     }
186 
187     protected DefaultTextHeaders(boolean ignoreCase, Headers.ValueConverter<CharSequence> valueConverter,
188             NameConverter<CharSequence> nameConverter) {
189         this(ignoreCase, valueConverter, nameConverter, false);
190     }
191 
192     public DefaultTextHeaders(boolean ignoreCase, ValueConverter<CharSequence> valueConverter,
193                               NameConverter<CharSequence> nameConverter, boolean singleHeaderFields) {
194         super(comparator(ignoreCase), comparator(ignoreCase),
195                 ignoreCase ? CHARSEQUECE_CASE_INSENSITIVE_HASH_CODE_GENERATOR
196                         : CHARSEQUECE_CASE_SENSITIVE_HASH_CODE_GENERATOR, valueConverter,
197                 CHARSEQUENCE_TO_STRING_CONVERTER, nameConverter);
198         valuesComposer = singleHeaderFields ? new SingleHeaderValuesComposer() : new MultipleFieldsValueComposer();
199     }
200 
201     @Override
202     public boolean contains(CharSequence name, CharSequence value, boolean ignoreCase) {
203         return contains(name, value, comparator(ignoreCase));
204     }
205 
206     @Override
207     public boolean containsObject(CharSequence name, Object value, boolean ignoreCase) {
208         return containsObject(name, value, comparator(ignoreCase));
209     }
210 
211     @Override
212     public TextHeaders add(CharSequence name, CharSequence value) {
213         return valuesComposer.add(name, value);
214     }
215 
216     @Override
217     public TextHeaders add(CharSequence name, Iterable<? extends CharSequence> values) {
218         return valuesComposer.add(name, values);
219     }
220 
221     @Override
222     public TextHeaders add(CharSequence name, CharSequence... values) {
223         return valuesComposer.add(name, values);
224     }
225 
226     @Override
227     public TextHeaders addObject(CharSequence name, Object value) {
228         return valuesComposer.addObject(name, value);
229     }
230 
231     @Override
232     public TextHeaders addObject(CharSequence name, Iterable<?> values) {
233         return valuesComposer.addObject(name, values);
234     }
235 
236     @Override
237     public TextHeaders addObject(CharSequence name, Object... values) {
238         return valuesComposer.addObject(name, values);
239     }
240 
241     @Override
242     public TextHeaders addBoolean(CharSequence name, boolean value) {
243         super.addBoolean(name, value);
244         return this;
245     }
246 
247     @Override
248     public TextHeaders addChar(CharSequence name, char value) {
249         super.addChar(name, value);
250         return this;
251     }
252 
253     @Override
254     public TextHeaders addByte(CharSequence name, byte value) {
255         super.addByte(name, value);
256         return this;
257     }
258 
259     @Override
260     public TextHeaders addShort(CharSequence name, short value) {
261         super.addShort(name, value);
262         return this;
263     }
264 
265     @Override
266     public TextHeaders addInt(CharSequence name, int value) {
267         super.addInt(name, value);
268         return this;
269     }
270 
271     @Override
272     public TextHeaders addLong(CharSequence name, long value) {
273         super.addLong(name, value);
274         return this;
275     }
276 
277     @Override
278     public TextHeaders addFloat(CharSequence name, float value) {
279         super.addFloat(name, value);
280         return this;
281     }
282 
283     @Override
284     public TextHeaders addDouble(CharSequence name, double value) {
285         super.addDouble(name, value);
286         return this;
287     }
288 
289     @Override
290     public TextHeaders addTimeMillis(CharSequence name, long value) {
291         super.addTimeMillis(name, value);
292         return this;
293     }
294 
295     @Override
296     public TextHeaders add(TextHeaders headers) {
297         super.add(headers);
298         return this;
299     }
300 
301     @Override
302     public TextHeaders set(CharSequence name, CharSequence value) {
303         super.set(name, value);
304         return this;
305     }
306 
307     @Override
308     public TextHeaders set(CharSequence name, Iterable<? extends CharSequence> values) {
309         return valuesComposer.set(name, values);
310     }
311 
312     @Override
313     public TextHeaders set(CharSequence name, CharSequence... values) {
314         return valuesComposer.set(name, values);
315     }
316 
317     @Override
318     public TextHeaders setObject(CharSequence name, Object value) {
319         super.setObject(name, value);
320         return this;
321     }
322 
323     @Override
324     public TextHeaders setObject(CharSequence name, Iterable<?> values) {
325         return valuesComposer.setObject(name, values);
326     }
327 
328     @Override
329     public TextHeaders setObject(CharSequence name, Object... values) {
330         return valuesComposer.setObject(name, values);
331     }
332 
333     @Override
334     public TextHeaders setBoolean(CharSequence name, boolean value) {
335         super.setBoolean(name, value);
336         return this;
337     }
338 
339     @Override
340     public TextHeaders setChar(CharSequence name, char value) {
341         super.setChar(name, value);
342         return this;
343     }
344 
345     @Override
346     public TextHeaders setByte(CharSequence name, byte value) {
347         super.setByte(name, value);
348         return this;
349     }
350 
351     @Override
352     public TextHeaders setShort(CharSequence name, short value) {
353         super.setShort(name, value);
354         return this;
355     }
356 
357     @Override
358     public TextHeaders setInt(CharSequence name, int value) {
359         super.setInt(name, value);
360         return this;
361     }
362 
363     @Override
364     public TextHeaders setLong(CharSequence name, long value) {
365         super.setLong(name, value);
366         return this;
367     }
368 
369     @Override
370     public TextHeaders setFloat(CharSequence name, float value) {
371         super.setFloat(name, value);
372         return this;
373     }
374 
375     @Override
376     public TextHeaders setDouble(CharSequence name, double value) {
377         super.setDouble(name, value);
378         return this;
379     }
380 
381     @Override
382     public TextHeaders setTimeMillis(CharSequence name, long value) {
383         super.setTimeMillis(name, value);
384         return this;
385     }
386 
387     @Override
388     public TextHeaders set(TextHeaders headers) {
389         super.set(headers);
390         return this;
391     }
392 
393     @Override
394     public TextHeaders setAll(TextHeaders headers) {
395         super.setAll(headers);
396         return this;
397     }
398 
399     @Override
400     public TextHeaders clear() {
401         super.clear();
402         return this;
403     }
404 
405     private static Comparator<CharSequence> comparator(boolean ignoreCase) {
406         return ignoreCase ? CHARSEQUENCE_CASE_INSENSITIVE_ORDER : CHARSEQUENCE_CASE_SENSITIVE_ORDER;
407     }
408 
409     /*
410      * This interface enables different implementations for adding/setting header values.
411      * Concrete implementations can control how values are added, for example to add all
412      * values for a header as a comma separated string instead of adding them as multiple
413      * headers with a single value.
414      */
415     private interface ValuesComposer {
416         TextHeaders add(CharSequence name, CharSequence value);
417         TextHeaders add(CharSequence name, CharSequence... values);
418         TextHeaders add(CharSequence name, Iterable<? extends CharSequence> values);
419 
420         TextHeaders addObject(CharSequence name, Iterable<?> values);
421         TextHeaders addObject(CharSequence name, Object... values);
422 
423         TextHeaders set(CharSequence name, CharSequence... values);
424         TextHeaders set(CharSequence name, Iterable<? extends CharSequence> values);
425 
426         TextHeaders setObject(CharSequence name, Object... values);
427         TextHeaders setObject(CharSequence name, Iterable<?> values);
428     }
429 
430     /*
431      * Will add multiple values for the same header as multiple separate headers.
432      */
433     private final class MultipleFieldsValueComposer implements ValuesComposer {
434 
435         @Override
436         public TextHeaders add(CharSequence name, CharSequence value) {
437             DefaultTextHeaders.super.add(name, value);
438             return DefaultTextHeaders.this;
439         }
440 
441         @Override
442         public TextHeaders add(CharSequence name, CharSequence... values) {
443             DefaultTextHeaders.super.add(name, values);
444             return DefaultTextHeaders.this;
445         }
446 
447         @Override
448         public TextHeaders add(CharSequence name, Iterable<? extends CharSequence> values) {
449             DefaultTextHeaders.super.add(name, values);
450             return DefaultTextHeaders.this;
451         }
452 
453         @Override
454         public TextHeaders addObject(CharSequence name, Iterable<?> values) {
455             DefaultTextHeaders.super.addObject(name, values);
456             return DefaultTextHeaders.this;
457         }
458 
459         @Override
460         public TextHeaders addObject(CharSequence name, Object... values) {
461             DefaultTextHeaders.super.addObject(name, values);
462             return DefaultTextHeaders.this;
463         }
464 
465         @Override
466         public TextHeaders set(CharSequence name, CharSequence... values) {
467             DefaultTextHeaders.super.set(name, values);
468             return DefaultTextHeaders.this;
469         }
470 
471         @Override
472         public TextHeaders set(CharSequence name, Iterable<? extends CharSequence> values) {
473             DefaultTextHeaders.super.set(name, values);
474             return DefaultTextHeaders.this;
475         }
476 
477         @Override
478         public TextHeaders setObject(CharSequence name, Object... values) {
479             DefaultTextHeaders.super.setObject(name, values);
480             return DefaultTextHeaders.this;
481         }
482 
483         @Override
484         public TextHeaders setObject(CharSequence name, Iterable<?> values) {
485             DefaultTextHeaders.super.setObject(name, values);
486             return DefaultTextHeaders.this;
487         }
488     }
489 
490     /**
491      * Will add multiple values for the same header as single header with a comma separated list of values.
492      *
493      * Please refer to section <a href="https://tools.ietf.org/html/rfc7230#section-3.2.2">3.2.2 Field Order</a>
494      * of RFC-7230 for details.
495      */
496     private final class SingleHeaderValuesComposer implements ValuesComposer {
497 
498         private final ValueConverter<CharSequence> valueConverter = valueConverter();
499         private CsvValueEscaper<Object> objectEscaper;
500         private CsvValueEscaper<CharSequence> charSequenceEscaper;
501 
502         private CsvValueEscaper<Object> objectEscaper() {
503             if (objectEscaper == null) {
504                 objectEscaper = new CsvValueEscaper<Object>() {
505                     @Override
506                     public CharSequence escape(Object value) {
507                         return StringUtil.escapeCsv(valueConverter.convertObject(value));
508                     }
509                 };
510             }
511             return objectEscaper;
512         }
513 
514         private CsvValueEscaper<CharSequence> charSequenceEscaper() {
515             if (charSequenceEscaper == null) {
516                 charSequenceEscaper = new CsvValueEscaper<CharSequence>() {
517                     @Override
518                     public CharSequence escape(CharSequence value) {
519                         return StringUtil.escapeCsv(value);
520                     }
521                 };
522             }
523             return charSequenceEscaper;
524         }
525 
526         @Override
527         public TextHeaders add(CharSequence name, CharSequence value) {
528             return addEscapedValue(name, StringUtil.escapeCsv(value));
529         }
530 
531         @Override
532         public TextHeaders add(CharSequence name, CharSequence... values) {
533             return addEscapedValue(name, commaSeparate(charSequenceEscaper(), values));
534         }
535 
536         @Override
537         public TextHeaders add(CharSequence name, Iterable<? extends CharSequence> values) {
538             return addEscapedValue(name, commaSeparate(charSequenceEscaper(), values));
539         }
540 
541         @Override
542         public TextHeaders addObject(CharSequence name, Iterable<?> values) {
543             return addEscapedValue(name, commaSeparate(objectEscaper(), values));
544         }
545 
546         @Override
547         public TextHeaders addObject(CharSequence name, Object... values) {
548             return addEscapedValue(name, commaSeparate(objectEscaper(), values));
549         }
550 
551         @Override
552         public TextHeaders set(CharSequence name, CharSequence... values) {
553             DefaultTextHeaders.super.set(name, commaSeparate(charSequenceEscaper(), values));
554             return DefaultTextHeaders.this;
555         }
556 
557         @Override
558         public TextHeaders set(CharSequence name, Iterable<? extends CharSequence> values) {
559             DefaultTextHeaders.super.set(name, commaSeparate(charSequenceEscaper(), values));
560             return DefaultTextHeaders.this;
561         }
562 
563         @Override
564         public TextHeaders setObject(CharSequence name, Object... values) {
565             DefaultTextHeaders.super.set(name, commaSeparate(objectEscaper(), values));
566             return DefaultTextHeaders.this;
567         }
568 
569         @Override
570         public TextHeaders setObject(CharSequence name, Iterable<?> values) {
571             DefaultTextHeaders.super.set(name, commaSeparate(objectEscaper(), values));
572             return DefaultTextHeaders.this;
573         }
574 
575         private TextHeaders addEscapedValue(CharSequence name, CharSequence escapedValue) {
576             CharSequence currentValue = DefaultTextHeaders.super.get(name);
577             if (currentValue == null) {
578                 DefaultTextHeaders.super.add(name, escapedValue);
579             } else {
580                 DefaultTextHeaders.super.set(name, commaSeparateEscapedValues(currentValue, escapedValue));
581             }
582             return DefaultTextHeaders.this;
583         }
584 
585         private <T> CharSequence commaSeparate(CsvValueEscaper<T> escaper, T... values) {
586             StringBuilder sb = new StringBuilder(values.length * DEFAULT_VALUE_SIZE);
587             if (values.length > 0) {
588                 int end = values.length - 1;
589                 for (int i = 0; i < end; i++) {
590                     sb.append(escaper.escape(values[i])).append(COMMA);
591                 }
592                 sb.append(escaper.escape(values[end]));
593             }
594             return sb;
595         }
596 
597         private <T> CharSequence commaSeparate(CsvValueEscaper<T> escaper, Iterable<? extends T> values) {
598             StringBuilder sb = new StringBuilder();
599             Iterator<? extends T> iterator = values.iterator();
600             if (iterator.hasNext()) {
601                 T next = iterator.next();
602                 while (iterator.hasNext()) {
603                     sb.append(escaper.escape(next)).append(COMMA);
604                     next = iterator.next();
605                 }
606                 sb.append(escaper.escape(next));
607             }
608             return sb;
609         }
610 
611         private CharSequence commaSeparateEscapedValues(CharSequence currentValue, CharSequence value) {
612             return new StringBuilder(currentValue.length() + 1 + value.length())
613                     .append(currentValue)
614                     .append(COMMA)
615                     .append(value);
616         }
617     }
618 
619     /**
620      * Escapes comma separated values (CSV).
621      *
622      * @param <T> The type that a concrete implementation handles
623      */
624     private interface CsvValueEscaper<T> {
625         /**
626          * Appends the value to the specified {@link StringBuilder}, escaping if necessary.
627          *
628          * @param value the value to be appended, escaped if necessary
629          */
630         CharSequence escape(T value);
631     }
632 }