View Javadoc
1   /*
2    * Copyright 2012 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    *   https://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  package io.netty.handler.codec.http;
17  
18  import io.netty.handler.codec.CharSequenceValueConverter;
19  import io.netty.handler.codec.DateFormatter;
20  import io.netty.handler.codec.DefaultHeaders;
21  import io.netty.handler.codec.DefaultHeaders.NameValidator;
22  import io.netty.handler.codec.DefaultHeaders.ValueValidator;
23  import io.netty.handler.codec.DefaultHeadersImpl;
24  import io.netty.handler.codec.HeadersUtils;
25  import io.netty.handler.codec.ValueConverter;
26  
27  import java.util.ArrayList;
28  import java.util.Calendar;
29  import java.util.Collections;
30  import java.util.Date;
31  import java.util.Iterator;
32  import java.util.List;
33  import java.util.Map;
34  import java.util.Map.Entry;
35  import java.util.Set;
36  
37  import static io.netty.util.AsciiString.CASE_INSENSITIVE_HASHER;
38  import static io.netty.util.AsciiString.CASE_SENSITIVE_HASHER;
39  
40  /**
41   * Default implementation of {@link HttpHeaders}.
42   */
43  public class DefaultHttpHeaders extends HttpHeaders {
44      private final DefaultHeaders<CharSequence, CharSequence, ?> headers;
45  
46      /**
47       * Create a new, empty HTTP headers object.
48       * <p>
49       * Header names and values are validated as they are added, to ensure they are compliant with the HTTP protocol.
50       */
51      public DefaultHttpHeaders() {
52          this(nameValidator(true), valueValidator(true));
53      }
54  
55      /**
56       * <b>Warning!</b> Setting {@code validate} to {@code false} will mean that Netty won't
57       * validate & protect against user-supplied header values that are malicious.
58       * This can leave your server implementation vulnerable to
59       * <a href="https://cwe.mitre.org/data/definitions/113.html">
60       *     CWE-113: Improper Neutralization of CRLF Sequences in HTTP Headers ('HTTP Response Splitting')
61       * </a>.
62       * When disabling this validation, it is the responsibility of the caller to ensure that the values supplied
63       * do not contain a non-url-escaped carriage return (CR) and/or line feed (LF) characters.
64       *
65       * @param validate Should Netty validate header values to ensure they aren't malicious.
66       * @deprecated Prefer using the {@link #DefaultHttpHeaders()} constructor instead,
67       * to always have validation enabled.
68       */
69      @Deprecated
70      public DefaultHttpHeaders(boolean validate) {
71          this(nameValidator(validate), valueValidator(validate));
72      }
73  
74      /**
75       * Create an HTTP headers object with the given name validator.
76       * <p>
77       * <b>Warning!</b> It is strongly recommended that the name validator implement validation that is at least as
78       * strict as {@link HttpHeaderValidationUtil#validateToken(CharSequence)}.
79       * It is also strongly recommended that {@code validateValues} is enabled.
80       * <p>
81       * Without these validations in place, your code can be susceptible to
82       * <a href="https://cwe.mitre.org/data/definitions/113.html">
83       *     CWE-113: Improper Neutralization of CRLF Sequences in HTTP Headers ('HTTP Response Splitting')
84       * </a>.
85       * It is the responsibility of the caller to ensure that the values supplied
86       * do not contain a non-url-escaped carriage return (CR) and/or line feed (LF) characters.
87       *
88       * @param validateValues Should Netty validate header values to ensure they aren't malicious.
89       * @param nameValidator The {@link NameValidator} to use, never {@code null.
90       */
91      protected DefaultHttpHeaders(boolean validateValues, NameValidator<CharSequence> nameValidator) {
92          this(nameValidator, valueValidator(validateValues));
93      }
94  
95      /**
96       * Create an HTTP headers object with the given name and value validators.
97       * <p>
98       * <b>Warning!</b> It is strongly recommended that the name validator implement validation that is at least as
99       * strict as {@link HttpHeaderValidationUtil#validateToken(CharSequence)}.
100      * And that the value validator is at least as strict as
101      * {@link HttpHeaderValidationUtil#validateValidHeaderValue(CharSequence)}.
102      * <p>
103      * Without these validations in place, your code can be susceptible to
104      * <a href="https://cwe.mitre.org/data/definitions/113.html">
105      *     CWE-113: Improper Neutralization of CRLF Sequences in HTTP Headers ('HTTP Response Splitting')
106      * </a>.
107      * It is the responsibility of the caller to ensure that the values supplied
108      * do not contain a non-url-escaped carriage return (CR) and/or line feed (LF) characters.
109      *
110      * @param nameValidator The {@link NameValidator} to use, never {@code null}.
111      * @param valueValidator The {@link ValueValidator} to use, never {@code null}.
112      */
113     protected DefaultHttpHeaders(
114             NameValidator<CharSequence> nameValidator,
115             ValueValidator<CharSequence> valueValidator) {
116         this(nameValidator, valueValidator, 16);
117     }
118 
119     /**
120      * Create an HTTP headers object with the given name and value validators.
121      * <p>
122      * <b>Warning!</b> It is strongly recommended that the name validator implement validation that is at least as
123      * strict as {@link HttpHeaderValidationUtil#validateToken(CharSequence)}.
124      * And that the value validator is at least as strict as
125      * {@link HttpHeaderValidationUtil#validateValidHeaderValue(CharSequence)}.
126      * <p>
127      * Without these validations in place, your code can be susceptible to
128      * <a href="https://cwe.mitre.org/data/definitions/113.html">
129      *     CWE-113: Improper Neutralization of CRLF Sequences in HTTP Headers ('HTTP Response Splitting')
130      * </a>.
131      * It is the responsibility of the caller to ensure that the values supplied
132      * do not contain a non-url-escaped carriage return (CR) and/or line feed (LF) characters.
133      *
134      * @param nameValidator The {@link NameValidator} to use, never {@code null}.
135      * @param valueValidator The {@link ValueValidator} to use, never {@code null}.
136      * @param sizeHint A hint about the anticipated number of entries.
137      */
138     protected DefaultHttpHeaders(
139             NameValidator<CharSequence> nameValidator,
140             ValueValidator<CharSequence> valueValidator,
141             int sizeHint) {
142         this(new DefaultHeadersImpl<CharSequence, CharSequence>(
143                 CASE_INSENSITIVE_HASHER,
144                 HeaderValueConverter.INSTANCE,
145                 nameValidator,
146                 sizeHint,
147                 valueValidator));
148     }
149 
150     protected DefaultHttpHeaders(DefaultHeaders<CharSequence, CharSequence, ?> headers) {
151         this.headers = headers;
152     }
153 
154     @Override
155     public HttpHeaders add(HttpHeaders headers) {
156         if (headers instanceof DefaultHttpHeaders) {
157             this.headers.add(((DefaultHttpHeaders) headers).headers);
158             return this;
159         } else {
160             return super.add(headers);
161         }
162     }
163 
164     @Override
165     public HttpHeaders set(HttpHeaders headers) {
166         if (headers instanceof DefaultHttpHeaders) {
167             this.headers.set(((DefaultHttpHeaders) headers).headers);
168             return this;
169         } else {
170             return super.set(headers);
171         }
172     }
173 
174     @Override
175     public HttpHeaders add(String name, Object value) {
176         headers.addObject(name, value);
177         return this;
178     }
179 
180     @Override
181     public HttpHeaders add(CharSequence name, Object value) {
182         headers.addObject(name, value);
183         return this;
184     }
185 
186     @Override
187     public HttpHeaders add(String name, Iterable<?> values) {
188         headers.addObject(name, values);
189         return this;
190     }
191 
192     @Override
193     public HttpHeaders add(CharSequence name, Iterable<?> values) {
194         headers.addObject(name, values);
195         return this;
196     }
197 
198     @Override
199     public HttpHeaders addInt(CharSequence name, int value) {
200         headers.addInt(name, value);
201         return this;
202     }
203 
204     @Override
205     public HttpHeaders addShort(CharSequence name, short value) {
206         headers.addShort(name, value);
207         return this;
208     }
209 
210     @Override
211     public HttpHeaders remove(String name) {
212         headers.remove(name);
213         return this;
214     }
215 
216     @Override
217     public HttpHeaders remove(CharSequence name) {
218         headers.remove(name);
219         return this;
220     }
221 
222     @Override
223     public HttpHeaders set(String name, Object value) {
224         headers.setObject(name, value);
225         return this;
226     }
227 
228     @Override
229     public HttpHeaders set(CharSequence name, Object value) {
230         headers.setObject(name, value);
231         return this;
232     }
233 
234     @Override
235     public HttpHeaders set(String name, Iterable<?> values) {
236         headers.setObject(name, values);
237         return this;
238     }
239 
240     @Override
241     public HttpHeaders set(CharSequence name, Iterable<?> values) {
242         headers.setObject(name, values);
243         return this;
244     }
245 
246     @Override
247     public HttpHeaders setInt(CharSequence name, int value) {
248         headers.setInt(name, value);
249         return this;
250     }
251 
252     @Override
253     public HttpHeaders setShort(CharSequence name, short value) {
254         headers.setShort(name, value);
255         return this;
256     }
257 
258     @Override
259     public HttpHeaders clear() {
260         headers.clear();
261         return this;
262     }
263 
264     @Override
265     public String get(String name) {
266         return get((CharSequence) name);
267     }
268 
269     @Override
270     public String get(CharSequence name) {
271         return HeadersUtils.getAsString(headers, name);
272     }
273 
274     @Override
275     public Integer getInt(CharSequence name) {
276         return headers.getInt(name);
277     }
278 
279     @Override
280     public int getInt(CharSequence name, int defaultValue) {
281         return headers.getInt(name, defaultValue);
282     }
283 
284     @Override
285     public Short getShort(CharSequence name) {
286         return headers.getShort(name);
287     }
288 
289     @Override
290     public short getShort(CharSequence name, short defaultValue) {
291         return headers.getShort(name, defaultValue);
292     }
293 
294     @Override
295     public Long getTimeMillis(CharSequence name) {
296         return headers.getTimeMillis(name);
297     }
298 
299     @Override
300     public long getTimeMillis(CharSequence name, long defaultValue) {
301         return headers.getTimeMillis(name, defaultValue);
302     }
303 
304     @Override
305     public List<String> getAll(String name) {
306         return getAll((CharSequence) name);
307     }
308 
309     @Override
310     public List<String> getAll(CharSequence name) {
311         return HeadersUtils.getAllAsString(headers, name);
312     }
313 
314     @Override
315     public List<Entry<String, String>> entries() {
316         if (isEmpty()) {
317             return Collections.emptyList();
318         }
319         List<Entry<String, String>> entriesConverted = new ArrayList<Entry<String, String>>(
320                 headers.size());
321         for (Entry<String, String> entry : this) {
322             entriesConverted.add(entry);
323         }
324         return entriesConverted;
325     }
326 
327     @Deprecated
328     @Override
329     public Iterator<Map.Entry<String, String>> iterator() {
330         return HeadersUtils.iteratorAsString(headers);
331     }
332 
333     @Override
334     public Iterator<Entry<CharSequence, CharSequence>> iteratorCharSequence() {
335         return headers.iterator();
336     }
337 
338     @Override
339     public Iterator<String> valueStringIterator(CharSequence name) {
340         final Iterator<CharSequence> itr = valueCharSequenceIterator(name);
341         return new Iterator<String>() {
342             @Override
343             public boolean hasNext() {
344                 return itr.hasNext();
345             }
346 
347             @Override
348             public String next() {
349                 return itr.next().toString();
350             }
351 
352             @Override
353             public void remove() {
354                 itr.remove();
355             }
356         };
357     }
358 
359     @Override
360     public Iterator<CharSequence> valueCharSequenceIterator(CharSequence name) {
361         return headers.valueIterator(name);
362     }
363 
364     @Override
365     public boolean contains(String name) {
366         return contains((CharSequence) name);
367     }
368 
369     @Override
370     public boolean contains(CharSequence name) {
371         return headers.contains(name);
372     }
373 
374     @Override
375     public boolean isEmpty() {
376         return headers.isEmpty();
377     }
378 
379     @Override
380     public int size() {
381         return headers.size();
382     }
383 
384     @Override
385     public boolean contains(String name, String value, boolean ignoreCase) {
386         return contains((CharSequence) name, (CharSequence) value, ignoreCase);
387     }
388 
389     @Override
390     public boolean contains(CharSequence name, CharSequence value, boolean ignoreCase) {
391         return headers.contains(name, value, ignoreCase ? CASE_INSENSITIVE_HASHER : CASE_SENSITIVE_HASHER);
392     }
393 
394     @Override
395     public Set<String> names() {
396         return HeadersUtils.namesAsString(headers);
397     }
398 
399     @Override
400     public boolean equals(Object o) {
401         return o instanceof DefaultHttpHeaders
402                 && headers.equals(((DefaultHttpHeaders) o).headers, CASE_SENSITIVE_HASHER);
403     }
404 
405     @Override
406     public int hashCode() {
407         return headers.hashCode(CASE_SENSITIVE_HASHER);
408     }
409 
410     @Override
411     public HttpHeaders copy() {
412         return new DefaultHttpHeaders(headers.copy());
413     }
414 
415     static ValueConverter<CharSequence> valueConverter() {
416         return HeaderValueConverter.INSTANCE;
417     }
418 
419     static ValueValidator<CharSequence> valueValidator(boolean validate) {
420         return validate ? DefaultHttpHeadersFactory.headersFactory().getValueValidator() :
421                 DefaultHttpHeadersFactory.headersFactory().withValidation(false).getValueValidator();
422     }
423 
424     static NameValidator<CharSequence> nameValidator(boolean validate) {
425         return validate ? DefaultHttpHeadersFactory.headersFactory().getNameValidator() :
426                 DefaultHttpHeadersFactory.headersFactory().withNameValidation(false).getNameValidator();
427     }
428 
429     private static class HeaderValueConverter extends CharSequenceValueConverter {
430         static final HeaderValueConverter INSTANCE = new HeaderValueConverter();
431 
432         @Override
433         public CharSequence convertObject(Object value) {
434             if (value instanceof CharSequence) {
435                 return (CharSequence) value;
436             }
437             if (value instanceof Date) {
438                 return DateFormatter.format((Date) value);
439             }
440             if (value instanceof Calendar) {
441                 return DateFormatter.format(((Calendar) value).getTime());
442             }
443             return value.toString();
444         }
445     }
446 }