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