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.DefaultHeadersImpl;
23  import io.netty.handler.codec.HeadersUtils;
24  import io.netty.handler.codec.ValueConverter;
25  
26  import java.util.ArrayList;
27  import java.util.Calendar;
28  import java.util.Collections;
29  import java.util.Date;
30  import java.util.Iterator;
31  import java.util.List;
32  import java.util.Map;
33  import java.util.Map.Entry;
34  import java.util.Set;
35  
36  import static io.netty.util.AsciiString.CASE_INSENSITIVE_HASHER;
37  import static io.netty.util.AsciiString.CASE_SENSITIVE_HASHER;
38  
39  /**
40   * Default implementation of {@link HttpHeaders}.
41   */
42  public class DefaultHttpHeaders extends HttpHeaders {
43      static final NameValidator<CharSequence> HttpNameValidator = new NameValidator<CharSequence>() {
44          @Override
45          public void validateName(CharSequence name) {
46              if (name == null || name.length() == 0) {
47                  throw new IllegalArgumentException("empty headers are not allowed [" + name + ']');
48              }
49              int index = HttpHeaderValidationUtil.validateToken(name);
50              if (index != -1) {
51                  throw new IllegalArgumentException("a header name can only contain \"token\" characters, " +
52                          "but found invalid character 0x" + Integer.toHexString(name.charAt(index)) +
53                          " at index " + index + " of header '" + name + "'.");
54              }
55          }
56      };
57  
58      private final DefaultHeaders<CharSequence, CharSequence, ?> headers;
59  
60      public DefaultHttpHeaders() {
61          this(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       */
76      public DefaultHttpHeaders(boolean validate) {
77          this(validate, nameValidator(validate));
78      }
79  
80      protected DefaultHttpHeaders(boolean validate, NameValidator<CharSequence> nameValidator) {
81          this(new DefaultHeadersImpl<CharSequence, CharSequence>(
82                  CASE_INSENSITIVE_HASHER,
83                  HeaderValueConverter.INSTANCE,
84                  nameValidator,
85                  16,
86                  valueValidator(validate)));
87      }
88  
89      protected DefaultHttpHeaders(DefaultHeaders<CharSequence, CharSequence, ?> headers) {
90          this.headers = headers;
91      }
92  
93      @Override
94      public HttpHeaders add(HttpHeaders headers) {
95          if (headers instanceof DefaultHttpHeaders) {
96              this.headers.add(((DefaultHttpHeaders) headers).headers);
97              return this;
98          } else {
99              return super.add(headers);
100         }
101     }
102 
103     @Override
104     public HttpHeaders set(HttpHeaders headers) {
105         if (headers instanceof DefaultHttpHeaders) {
106             this.headers.set(((DefaultHttpHeaders) headers).headers);
107             return this;
108         } else {
109             return super.set(headers);
110         }
111     }
112 
113     @Override
114     public HttpHeaders add(String name, Object value) {
115         headers.addObject(name, value);
116         return this;
117     }
118 
119     @Override
120     public HttpHeaders add(CharSequence name, Object value) {
121         headers.addObject(name, value);
122         return this;
123     }
124 
125     @Override
126     public HttpHeaders add(String name, Iterable<?> values) {
127         headers.addObject(name, values);
128         return this;
129     }
130 
131     @Override
132     public HttpHeaders add(CharSequence name, Iterable<?> values) {
133         headers.addObject(name, values);
134         return this;
135     }
136 
137     @Override
138     public HttpHeaders addInt(CharSequence name, int value) {
139         headers.addInt(name, value);
140         return this;
141     }
142 
143     @Override
144     public HttpHeaders addShort(CharSequence name, short value) {
145         headers.addShort(name, value);
146         return this;
147     }
148 
149     @Override
150     public HttpHeaders remove(String name) {
151         headers.remove(name);
152         return this;
153     }
154 
155     @Override
156     public HttpHeaders remove(CharSequence name) {
157         headers.remove(name);
158         return this;
159     }
160 
161     @Override
162     public HttpHeaders set(String name, Object value) {
163         headers.setObject(name, value);
164         return this;
165     }
166 
167     @Override
168     public HttpHeaders set(CharSequence name, Object value) {
169         headers.setObject(name, value);
170         return this;
171     }
172 
173     @Override
174     public HttpHeaders set(String name, Iterable<?> values) {
175         headers.setObject(name, values);
176         return this;
177     }
178 
179     @Override
180     public HttpHeaders set(CharSequence name, Iterable<?> values) {
181         headers.setObject(name, values);
182         return this;
183     }
184 
185     @Override
186     public HttpHeaders setInt(CharSequence name, int value) {
187         headers.setInt(name, value);
188         return this;
189     }
190 
191     @Override
192     public HttpHeaders setShort(CharSequence name, short value) {
193         headers.setShort(name, value);
194         return this;
195     }
196 
197     @Override
198     public HttpHeaders clear() {
199         headers.clear();
200         return this;
201     }
202 
203     @Override
204     public String get(String name) {
205         return get((CharSequence) name);
206     }
207 
208     @Override
209     public String get(CharSequence name) {
210         return HeadersUtils.getAsString(headers, name);
211     }
212 
213     @Override
214     public Integer getInt(CharSequence name) {
215         return headers.getInt(name);
216     }
217 
218     @Override
219     public int getInt(CharSequence name, int defaultValue) {
220         return headers.getInt(name, defaultValue);
221     }
222 
223     @Override
224     public Short getShort(CharSequence name) {
225         return headers.getShort(name);
226     }
227 
228     @Override
229     public short getShort(CharSequence name, short defaultValue) {
230         return headers.getShort(name, defaultValue);
231     }
232 
233     @Override
234     public Long getTimeMillis(CharSequence name) {
235         return headers.getTimeMillis(name);
236     }
237 
238     @Override
239     public long getTimeMillis(CharSequence name, long defaultValue) {
240         return headers.getTimeMillis(name, defaultValue);
241     }
242 
243     @Override
244     public List<String> getAll(String name) {
245         return getAll((CharSequence) name);
246     }
247 
248     @Override
249     public List<String> getAll(CharSequence name) {
250         return HeadersUtils.getAllAsString(headers, name);
251     }
252 
253     @Override
254     public List<Entry<String, String>> entries() {
255         if (isEmpty()) {
256             return Collections.emptyList();
257         }
258         List<Entry<String, String>> entriesConverted = new ArrayList<Entry<String, String>>(
259                 headers.size());
260         for (Entry<String, String> entry : this) {
261             entriesConverted.add(entry);
262         }
263         return entriesConverted;
264     }
265 
266     @Deprecated
267     @Override
268     public Iterator<Map.Entry<String, String>> iterator() {
269         return HeadersUtils.iteratorAsString(headers);
270     }
271 
272     @Override
273     public Iterator<Entry<CharSequence, CharSequence>> iteratorCharSequence() {
274         return headers.iterator();
275     }
276 
277     @Override
278     public Iterator<String> valueStringIterator(CharSequence name) {
279         final Iterator<CharSequence> itr = valueCharSequenceIterator(name);
280         return new Iterator<String>() {
281             @Override
282             public boolean hasNext() {
283                 return itr.hasNext();
284             }
285 
286             @Override
287             public String next() {
288                 return itr.next().toString();
289             }
290 
291             @Override
292             public void remove() {
293                 itr.remove();
294             }
295         };
296     }
297 
298     @Override
299     public Iterator<CharSequence> valueCharSequenceIterator(CharSequence name) {
300         return headers.valueIterator(name);
301     }
302 
303     @Override
304     public boolean contains(String name) {
305         return contains((CharSequence) name);
306     }
307 
308     @Override
309     public boolean contains(CharSequence name) {
310         return headers.contains(name);
311     }
312 
313     @Override
314     public boolean isEmpty() {
315         return headers.isEmpty();
316     }
317 
318     @Override
319     public int size() {
320         return headers.size();
321     }
322 
323     @Override
324     public boolean contains(String name, String value, boolean ignoreCase) {
325         return contains((CharSequence) name, (CharSequence) value, ignoreCase);
326     }
327 
328     @Override
329     public boolean contains(CharSequence name, CharSequence value, boolean ignoreCase) {
330         return headers.contains(name, value, ignoreCase ? CASE_INSENSITIVE_HASHER : CASE_SENSITIVE_HASHER);
331     }
332 
333     @Override
334     public Set<String> names() {
335         return HeadersUtils.namesAsString(headers);
336     }
337 
338     @Override
339     public boolean equals(Object o) {
340         return o instanceof DefaultHttpHeaders
341                 && headers.equals(((DefaultHttpHeaders) o).headers, CASE_SENSITIVE_HASHER);
342     }
343 
344     @Override
345     public int hashCode() {
346         return headers.hashCode(CASE_SENSITIVE_HASHER);
347     }
348 
349     @Override
350     public HttpHeaders copy() {
351         return new DefaultHttpHeaders(headers.copy());
352     }
353 
354     static ValueConverter<CharSequence> valueConverter() {
355         return HeaderValueConverter.INSTANCE;
356     }
357 
358     @SuppressWarnings("unchecked")
359     static DefaultHeaders.ValueValidator<CharSequence> valueValidator(boolean validate) {
360         return validate ? HeaderValueValidator.INSTANCE :
361                 (DefaultHeaders.ValueValidator<CharSequence>) DefaultHeaders.ValueValidator.NO_VALIDATION;
362     }
363 
364     @SuppressWarnings("unchecked")
365     static NameValidator<CharSequence> nameValidator(boolean validate) {
366         return validate ? HttpNameValidator : NameValidator.NOT_NULL;
367     }
368 
369     private static class HeaderValueConverter extends CharSequenceValueConverter {
370         static final HeaderValueConverter INSTANCE = new HeaderValueConverter();
371 
372         @Override
373         public CharSequence convertObject(Object value) {
374             if (value instanceof CharSequence) {
375                 return (CharSequence) value;
376             }
377             if (value instanceof Date) {
378                 return DateFormatter.format((Date) value);
379             }
380             if (value instanceof Calendar) {
381                 return DateFormatter.format(((Calendar) value).getTime());
382             }
383             return value.toString();
384         }
385     }
386 
387     private static final class HeaderValueValidator implements DefaultHeaders.ValueValidator<CharSequence> {
388         static final HeaderValueValidator INSTANCE = new HeaderValueValidator();
389 
390         @Override
391         public void validate(CharSequence value) {
392             int index = HttpHeaderValidationUtil.validateValidHeaderValue(value);
393             if (index != -1) {
394                 throw new IllegalArgumentException("a header value contains prohibited character 0x" +
395                         Integer.toHexString(value.charAt(index)) + " at index " + index + '.');
396             }
397         }
398     }
399 }