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