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    *   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  package io.netty.handler.codec.http;
17  
18  import io.netty.buffer.ByteBuf;
19  
20  import java.text.ParseException;
21  import java.util.Calendar;
22  import java.util.Collections;
23  import java.util.Date;
24  import java.util.Iterator;
25  import java.util.List;
26  import java.util.Map;
27  import java.util.Map.Entry;
28  import java.util.Set;
29  
30  import static io.netty.handler.codec.http.HttpConstants.*;
31  
32  /**
33   * Provides the constants for the standard HTTP header names and values and
34   * commonly used utility methods that accesses an {@link HttpMessage}.
35   */
36  public abstract class HttpHeaders implements Iterable<Map.Entry<String, String>> {
37  
38      private static final byte[] HEADER_SEPERATOR = { COLON, SP };
39      private static final byte[] CRLF = { CR, LF };
40      private static final CharSequence CONTENT_LENGTH_ENTITY = newEntity(Names.CONTENT_LENGTH);
41      private static final CharSequence CONNECTION_ENTITY = newEntity(Names.CONNECTION);
42      private static final CharSequence CLOSE_ENTITY = newEntity(Values.CLOSE);
43      private static final CharSequence KEEP_ALIVE_ENTITY = newEntity(Values.KEEP_ALIVE);
44      private static final CharSequence HOST_ENTITY = newEntity(Names.HOST);
45      private static final CharSequence DATE_ENTITY = newEntity(Names.DATE);
46      private static final CharSequence EXPECT_ENTITY = newEntity(Names.EXPECT);
47      private static final CharSequence CONTINUE_ENTITY = newEntity(Values.CONTINUE);
48      private static final CharSequence TRANSFER_ENCODING_ENTITY = newEntity(Names.TRANSFER_ENCODING);
49      private static final CharSequence CHUNKED_ENTITY = newEntity(Values.CHUNKED);
50      private static final CharSequence SEC_WEBSOCKET_KEY1_ENTITY = newEntity(Names.SEC_WEBSOCKET_KEY1);
51      private static final CharSequence SEC_WEBSOCKET_KEY2_ENTITY = newEntity(Names.SEC_WEBSOCKET_KEY2);
52      private static final CharSequence SEC_WEBSOCKET_ORIGIN_ENTITY = newEntity(Names.SEC_WEBSOCKET_ORIGIN);
53      private static final CharSequence SEC_WEBSOCKET_LOCATION_ENTITY = newEntity(Names.SEC_WEBSOCKET_LOCATION);
54  
55      public static final HttpHeaders EMPTY_HEADERS = new HttpHeaders() {
56          @Override
57          public String get(String name) {
58              return null;
59          }
60  
61          @Override
62          public List<String> getAll(String name) {
63              return Collections.emptyList();
64          }
65  
66          @Override
67          public List<Entry<String, String>> entries() {
68              return Collections.emptyList();
69          }
70  
71          @Override
72          public boolean contains(String name) {
73              return false;
74          }
75  
76          @Override
77          public boolean isEmpty() {
78              return true;
79          }
80  
81          @Override
82          public Set<String> names() {
83              return Collections.emptySet();
84          }
85  
86          @Override
87          public HttpHeaders add(String name, Object value) {
88              throw new UnsupportedOperationException("read only");
89          }
90  
91          @Override
92          public HttpHeaders add(String name, Iterable<?> values) {
93              throw new UnsupportedOperationException("read only");
94          }
95  
96          @Override
97          public HttpHeaders set(String name, Object value) {
98              throw new UnsupportedOperationException("read only");
99          }
100 
101         @Override
102         public HttpHeaders set(String name, Iterable<?> values) {
103             throw new UnsupportedOperationException("read only");
104         }
105 
106         @Override
107         public HttpHeaders remove(String name) {
108             throw new UnsupportedOperationException("read only");
109         }
110 
111         @Override
112         public HttpHeaders clear() {
113             throw new UnsupportedOperationException("read only");
114         }
115 
116         @Override
117         public Iterator<Entry<String, String>> iterator() {
118             return entries().iterator();
119         }
120     };
121 
122     /**
123      * Standard HTTP header names.
124      */
125     public static final class Names {
126         /**
127          * {@code "Accept"}
128          */
129         public static final String ACCEPT = "Accept";
130         /**
131          * {@code "Accept-Charset"}
132          */
133         public static final String ACCEPT_CHARSET = "Accept-Charset";
134         /**
135          * {@code "Accept-Encoding"}
136          */
137         public static final String ACCEPT_ENCODING = "Accept-Encoding";
138         /**
139          * {@code "Accept-Language"}
140          */
141         public static final String ACCEPT_LANGUAGE = "Accept-Language";
142         /**
143          * {@code "Accept-Ranges"}
144          */
145         public static final String ACCEPT_RANGES = "Accept-Ranges";
146         /**
147          * {@code "Accept-Patch"}
148          */
149         public static final String ACCEPT_PATCH = "Accept-Patch";
150         /**
151          * {@code "Access-Control-Allow-Credentials"}
152          */
153         public static final String ACCESS_CONTROL_ALLOW_CREDENTIALS = "Access-Control-Allow-Credentials";
154         /**
155          * {@code "Access-Control-Allow-Headers"}
156          */
157         public static final String ACCESS_CONTROL_ALLOW_HEADERS = "Access-Control-Allow-Headers";
158         /**
159          * {@code "Access-Control-Allow-Methods"}
160          */
161         public static final String ACCESS_CONTROL_ALLOW_METHODS = "Access-Control-Allow-Methods";
162         /**
163          * {@code "Access-Control-Allow-Origin"}
164          */
165         public static final String ACCESS_CONTROL_ALLOW_ORIGIN = "Access-Control-Allow-Origin";
166         /**
167          * {@code "Access-Control-Expose-Headers"}
168          */
169         public static final String ACCESS_CONTROL_EXPOSE_HEADERS = "Access-Control-Expose-Headers";
170         /**
171          * {@code "Access-Control-Max-Age"}
172          */
173         public static final String ACCESS_CONTROL_MAX_AGE = "Access-Control-Max-Age";
174         /**
175          * {@code "Access-Control-Request-Headers"}
176          */
177         public static final String ACCESS_CONTROL_REQUEST_HEADERS = "Access-Control-Request-Headers";
178         /**
179          * {@code "Access-Control-Request-Method"}
180          */
181         public static final String ACCESS_CONTROL_REQUEST_METHOD = "Access-Control-Request-Method";
182         /**
183          * {@code "Age"}
184          */
185         public static final String AGE = "Age";
186         /**
187          * {@code "Allow"}
188          */
189         public static final String ALLOW = "Allow";
190         /**
191          * {@code "Authorization"}
192          */
193         public static final String AUTHORIZATION = "Authorization";
194         /**
195          * {@code "Cache-Control"}
196          */
197         public static final String CACHE_CONTROL = "Cache-Control";
198         /**
199          * {@code "Connection"}
200          */
201         public static final String CONNECTION = "Connection";
202         /**
203          * {@code "Content-Base"}
204          */
205         public static final String CONTENT_BASE = "Content-Base";
206         /**
207          * {@code "Content-Encoding"}
208          */
209         public static final String CONTENT_ENCODING = "Content-Encoding";
210         /**
211          * {@code "Content-Language"}
212          */
213         public static final String CONTENT_LANGUAGE = "Content-Language";
214         /**
215          * {@code "Content-Length"}
216          */
217         public static final String CONTENT_LENGTH = "Content-Length";
218         /**
219          * {@code "Content-Location"}
220          */
221         public static final String CONTENT_LOCATION = "Content-Location";
222         /**
223          * {@code "Content-Transfer-Encoding"}
224          */
225         public static final String CONTENT_TRANSFER_ENCODING = "Content-Transfer-Encoding";
226         /**
227          * {@code "Content-MD5"}
228          */
229         public static final String CONTENT_MD5 = "Content-MD5";
230         /**
231          * {@code "Content-Range"}
232          */
233         public static final String CONTENT_RANGE = "Content-Range";
234         /**
235          * {@code "Content-Type"}
236          */
237         public static final String CONTENT_TYPE = "Content-Type";
238         /**
239          * {@code "Cookie"}
240          */
241         public static final String COOKIE = "Cookie";
242         /**
243          * {@code "Date"}
244          */
245         public static final String DATE = "Date";
246         /**
247          * {@code "ETag"}
248          */
249         public static final String ETAG = "ETag";
250         /**
251          * {@code "Expect"}
252          */
253         public static final String EXPECT = "Expect";
254         /**
255          * {@code "Expires"}
256          */
257         public static final String EXPIRES = "Expires";
258         /**
259          * {@code "From"}
260          */
261         public static final String FROM = "From";
262         /**
263          * {@code "Host"}
264          */
265         public static final String HOST = "Host";
266         /**
267          * {@code "If-Match"}
268          */
269         public static final String IF_MATCH = "If-Match";
270         /**
271          * {@code "If-Modified-Since"}
272          */
273         public static final String IF_MODIFIED_SINCE = "If-Modified-Since";
274         /**
275          * {@code "If-None-Match"}
276          */
277         public static final String IF_NONE_MATCH = "If-None-Match";
278         /**
279          * {@code "If-Range"}
280          */
281         public static final String IF_RANGE = "If-Range";
282         /**
283          * {@code "If-Unmodified-Since"}
284          */
285         public static final String IF_UNMODIFIED_SINCE = "If-Unmodified-Since";
286         /**
287          * {@code "Last-Modified"}
288          */
289         public static final String LAST_MODIFIED = "Last-Modified";
290         /**
291          * {@code "Location"}
292          */
293         public static final String LOCATION = "Location";
294         /**
295          * {@code "Max-Forwards"}
296          */
297         public static final String MAX_FORWARDS = "Max-Forwards";
298         /**
299          * {@code "Origin"}
300          */
301         public static final String ORIGIN = "Origin";
302         /**
303          * {@code "Pragma"}
304          */
305         public static final String PRAGMA = "Pragma";
306         /**
307          * {@code "Proxy-Authenticate"}
308          */
309         public static final String PROXY_AUTHENTICATE = "Proxy-Authenticate";
310         /**
311          * {@code "Proxy-Authorization"}
312          */
313         public static final String PROXY_AUTHORIZATION = "Proxy-Authorization";
314         /**
315          * {@code "Range"}
316          */
317         public static final String RANGE = "Range";
318         /**
319          * {@code "Referer"}
320          */
321         public static final String REFERER = "Referer";
322         /**
323          * {@code "Retry-After"}
324          */
325         public static final String RETRY_AFTER = "Retry-After";
326         /**
327          * {@code "Sec-WebSocket-Key1"}
328          */
329         public static final String SEC_WEBSOCKET_KEY1 = "Sec-WebSocket-Key1";
330         /**
331          * {@code "Sec-WebSocket-Key2"}
332          */
333         public static final String SEC_WEBSOCKET_KEY2 = "Sec-WebSocket-Key2";
334         /**
335          * {@code "Sec-WebSocket-Location"}
336          */
337         public static final String SEC_WEBSOCKET_LOCATION = "Sec-WebSocket-Location";
338         /**
339          * {@code "Sec-WebSocket-Origin"}
340          */
341         public static final String SEC_WEBSOCKET_ORIGIN = "Sec-WebSocket-Origin";
342         /**
343          * {@code "Sec-WebSocket-Protocol"}
344          */
345         public static final String SEC_WEBSOCKET_PROTOCOL = "Sec-WebSocket-Protocol";
346         /**
347          * {@code "Sec-WebSocket-Version"}
348          */
349         public static final String SEC_WEBSOCKET_VERSION = "Sec-WebSocket-Version";
350         /**
351          * {@code "Sec-WebSocket-Key"}
352          */
353         public static final String SEC_WEBSOCKET_KEY = "Sec-WebSocket-Key";
354         /**
355          * {@code "Sec-WebSocket-Accept"}
356          */
357         public static final String SEC_WEBSOCKET_ACCEPT = "Sec-WebSocket-Accept";
358         /**
359          * {@code "Server"}
360          */
361         public static final String SERVER = "Server";
362         /**
363          * {@code "Set-Cookie"}
364          */
365         public static final String SET_COOKIE = "Set-Cookie";
366         /**
367          * {@code "Set-Cookie2"}
368          */
369         public static final String SET_COOKIE2 = "Set-Cookie2";
370         /**
371          * {@code "TE"}
372          */
373         public static final String TE = "TE";
374         /**
375          * {@code "Trailer"}
376          */
377         public static final String TRAILER = "Trailer";
378         /**
379          * {@code "Transfer-Encoding"}
380          */
381         public static final String TRANSFER_ENCODING = "Transfer-Encoding";
382         /**
383          * {@code "Upgrade"}
384          */
385         public static final String UPGRADE = "Upgrade";
386         /**
387          * {@code "User-Agent"}
388          */
389         public static final String USER_AGENT = "User-Agent";
390         /**
391          * {@code "Vary"}
392          */
393         public static final String VARY = "Vary";
394         /**
395          * {@code "Via"}
396          */
397         public static final String VIA = "Via";
398         /**
399          * {@code "Warning"}
400          */
401         public static final String WARNING = "Warning";
402         /**
403          * {@code "WebSocket-Location"}
404          */
405         public static final String WEBSOCKET_LOCATION = "WebSocket-Location";
406         /**
407          * {@code "WebSocket-Origin"}
408          */
409         public static final String WEBSOCKET_ORIGIN = "WebSocket-Origin";
410         /**
411          * {@code "WebSocket-Protocol"}
412          */
413         public static final String WEBSOCKET_PROTOCOL = "WebSocket-Protocol";
414         /**
415          * {@code "WWW-Authenticate"}
416          */
417         public static final String WWW_AUTHENTICATE = "WWW-Authenticate";
418 
419         private Names() {
420         }
421     }
422 
423     /**
424      * Standard HTTP header values.
425      */
426     public static final class Values {
427         /**
428          * {@code "application/json"}
429          */
430         public static final String APPLICATION_JSON = "application/json";
431         /**
432          * {@code "application/x-www-form-urlencoded"}
433          */
434          public static final String APPLICATION_X_WWW_FORM_URLENCODED =
435              "application/x-www-form-urlencoded";
436         /**
437          * {@code "base64"}
438          */
439         public static final String BASE64 = "base64";
440         /**
441          * {@code "binary"}
442          */
443         public static final String BINARY = "binary";
444         /**
445          * {@code "boundary"}
446          */
447         public static final String BOUNDARY = "boundary";
448         /**
449          * {@code "bytes"}
450          */
451         public static final String BYTES = "bytes";
452         /**
453          * {@code "charset"}
454          */
455         public static final String CHARSET = "charset";
456         /**
457          * {@code "chunked"}
458          */
459         public static final String CHUNKED = "chunked";
460         /**
461          * {@code "close"}
462          */
463         public static final String CLOSE = "close";
464         /**
465          * {@code "compress"}
466          */
467         public static final String COMPRESS = "compress";
468         /**
469          * {@code "100-continue"}
470          */
471         public static final String CONTINUE =  "100-continue";
472         /**
473          * {@code "deflate"}
474          */
475         public static final String DEFLATE = "deflate";
476         /**
477          * {@code "gzip"}
478          */
479         public static final String GZIP = "gzip";
480         /**
481          * {@code "gzip,deflate"}
482          */
483         public static final String GZIP_DEFLATE = "gzip,deflate";
484         /**
485          * {@code "identity"}
486          */
487         public static final String IDENTITY = "identity";
488         /**
489          * {@code "keep-alive"}
490          */
491         public static final String KEEP_ALIVE = "keep-alive";
492         /**
493          * {@code "max-age"}
494          */
495         public static final String MAX_AGE = "max-age";
496         /**
497          * {@code "max-stale"}
498          */
499         public static final String MAX_STALE = "max-stale";
500         /**
501          * {@code "min-fresh"}
502          */
503         public static final String MIN_FRESH = "min-fresh";
504         /**
505          * {@code "multipart/form-data"}
506          */
507         public static final String MULTIPART_FORM_DATA = "multipart/form-data";
508         /**
509          * {@code "must-revalidate"}
510          */
511         public static final String MUST_REVALIDATE = "must-revalidate";
512         /**
513          * {@code "no-cache"}
514          */
515         public static final String NO_CACHE = "no-cache";
516         /**
517          * {@code "no-store"}
518          */
519         public static final String NO_STORE = "no-store";
520         /**
521          * {@code "no-transform"}
522          */
523         public static final String NO_TRANSFORM = "no-transform";
524         /**
525          * {@code "none"}
526          */
527         public static final String NONE = "none";
528         /**
529          * {@code "only-if-cached"}
530          */
531         public static final String ONLY_IF_CACHED = "only-if-cached";
532         /**
533          * {@code "private"}
534          */
535         public static final String PRIVATE = "private";
536         /**
537          * {@code "proxy-revalidate"}
538          */
539         public static final String PROXY_REVALIDATE = "proxy-revalidate";
540         /**
541          * {@code "public"}
542          */
543         public static final String PUBLIC = "public";
544         /**
545          * {@code "quoted-printable"}
546          */
547         public static final String QUOTED_PRINTABLE = "quoted-printable";
548         /**
549          * {@code "s-maxage"}
550          */
551         public static final String S_MAXAGE = "s-maxage";
552         /**
553          * {@code "trailers"}
554          */
555         public static final String TRAILERS = "trailers";
556         /**
557          * {@code "Upgrade"}
558          */
559         public static final String UPGRADE = "Upgrade";
560         /**
561          * {@code "WebSocket"}
562          */
563         public static final String WEBSOCKET = "WebSocket";
564 
565         private Values() {
566         }
567     }
568 
569     /**
570      * Returns {@code true} if and only if the connection can remain open and
571      * thus 'kept alive'.  This methods respects the value of the
572      * {@code "Connection"} header first and then the return value of
573      * {@link HttpVersion#isKeepAliveDefault()}.
574      */
575     public static boolean isKeepAlive(HttpMessage message) {
576         String connection = message.headers().get(CONNECTION_ENTITY);
577         if (connection != null && equalsIgnoreCase(CLOSE_ENTITY, connection)) {
578             return false;
579         }
580 
581         if (message.getProtocolVersion().isKeepAliveDefault()) {
582             return !equalsIgnoreCase(CLOSE_ENTITY, connection);
583         } else {
584             return equalsIgnoreCase(KEEP_ALIVE_ENTITY, connection);
585         }
586     }
587 
588     /**
589      * Sets the value of the {@code "Connection"} header depending on the
590      * protocol version of the specified message.  This getMethod sets or removes
591      * the {@code "Connection"} header depending on what the default keep alive
592      * mode of the message's protocol version is, as specified by
593      * {@link HttpVersion#isKeepAliveDefault()}.
594      * <ul>
595      * <li>If the connection is kept alive by default:
596      *     <ul>
597      *     <li>set to {@code "close"} if {@code keepAlive} is {@code false}.</li>
598      *     <li>remove otherwise.</li>
599      *     </ul></li>
600      * <li>If the connection is closed by default:
601      *     <ul>
602      *     <li>set to {@code "keep-alive"} if {@code keepAlive} is {@code true}.</li>
603      *     <li>remove otherwise.</li>
604      *     </ul></li>
605      * </ul>
606      */
607     public static void setKeepAlive(HttpMessage message, boolean keepAlive) {
608         HttpHeaders h = message.headers();
609         if (message.getProtocolVersion().isKeepAliveDefault()) {
610             if (keepAlive) {
611                 h.remove(CONNECTION_ENTITY);
612             } else {
613                 h.set(CONNECTION_ENTITY, CLOSE_ENTITY);
614             }
615         } else {
616             if (keepAlive) {
617                 h.set(CONNECTION_ENTITY, KEEP_ALIVE_ENTITY);
618             } else {
619                 h.remove(CONNECTION_ENTITY);
620             }
621         }
622     }
623 
624     /**
625      * @see {@link #getHeader(HttpMessage, CharSequence)}
626      */
627     public static String getHeader(HttpMessage message, String name) {
628         return message.headers().get(name);
629     }
630 
631     /**
632      * Returns the header value with the specified header name.  If there are
633      * more than one header value for the specified header name, the first
634      * value is returned.
635      *
636      * @return the header value or {@code null} if there is no such header
637      */
638     public static String getHeader(HttpMessage message, CharSequence name) {
639         return message.headers().get(name);
640     }
641 
642     /**
643      * @see #getHeader(HttpMessage, CharSequence, String)
644      */
645     public static String getHeader(HttpMessage message, String name, String defaultValue) {
646         return getHeader(message, (CharSequence) name, defaultValue);
647     }
648 
649     /**
650      * Returns the header value with the specified header name.  If there are
651      * more than one header value for the specified header name, the first
652      * value is returned.
653      *
654      * @return the header value or the {@code defaultValue} if there is no such
655      *         header
656      */
657     public static String getHeader(HttpMessage message, CharSequence name, String defaultValue) {
658         String value = message.headers().get(name);
659         if (value == null) {
660             return defaultValue;
661         }
662         return value;
663     }
664 
665     /**
666      * @see #setHeader(HttpMessage, CharSequence, Object)
667      */
668     public static void setHeader(HttpMessage message, String name, Object value) {
669         message.headers().set(name, value);
670     }
671 
672     /**
673      * Sets a new header with the specified name and value.  If there is an
674      * existing header with the same name, the existing header is removed.
675      * If the specified value is not a {@link String}, it is converted into a
676      * {@link String} by {@link Object#toString()}, except for {@link Date}
677      * and {@link Calendar} which are formatted to the date format defined in
678      * <a href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html#sec3.3.1">RFC2616</a>.
679      */
680     public static void setHeader(HttpMessage message, CharSequence name, Object value) {
681         message.headers().set(name, value);
682     }
683 
684     /**
685      *
686      * @see #setHeader(HttpMessage, CharSequence, Iterable)
687      */
688     public static void setHeader(HttpMessage message, String name, Iterable<?> values) {
689         message.headers().set(name, values);
690     }
691 
692     /**
693      * Sets a new header with the specified name and values.  If there is an
694      * existing header with the same name, the existing header is removed.
695      * This getMethod can be represented approximately as the following code:
696      * <pre>
697      * removeHeader(message, name);
698      * for (Object v: values) {
699      *     if (v == null) {
700      *         break;
701      *     }
702      *     addHeader(message, name, v);
703      * }
704      * </pre>
705      */
706     public static void setHeader(HttpMessage message, CharSequence name, Iterable<?> values) {
707         message.headers().set(name, values);
708     }
709 
710     /**
711      * @see #addHeader(HttpMessage, CharSequence, Object)
712      */
713     public static void addHeader(HttpMessage message, String name, Object value) {
714         message.headers().add(name, value);
715     }
716 
717     /**
718      * Adds a new header with the specified name and value.
719      * If the specified value is not a {@link String}, it is converted into a
720      * {@link String} by {@link Object#toString()}, except for {@link Date}
721      * and {@link Calendar} which are formatted to the date format defined in
722      * <a href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html#sec3.3.1">RFC2616</a>.
723      */
724     public static void addHeader(HttpMessage message, CharSequence name, Object value) {
725         message.headers().add(name, value);
726     }
727 
728     /**
729      * @see #removeHeader(HttpMessage, CharSequence)
730      */
731     public static void removeHeader(HttpMessage message, String name) {
732         message.headers().remove(name);
733     }
734 
735     /**
736      * Removes the header with the specified name.
737      */
738     public static void removeHeader(HttpMessage message, CharSequence name) {
739         message.headers().remove(name);
740     }
741 
742     /**
743      * Removes all headers from the specified message.
744      */
745     public static void clearHeaders(HttpMessage message) {
746         message.headers().clear();
747     }
748 
749     /**
750      * @see #getIntHeader(HttpMessage, CharSequence)
751      */
752     public static int getIntHeader(HttpMessage message, String name) {
753         return getIntHeader(message, (CharSequence) name);
754     }
755 
756     /**
757      * Returns the integer header value with the specified header name.  If
758      * there are more than one header value for the specified header name, the
759      * first value is returned.
760      *
761      * @return the header value
762      * @throws NumberFormatException
763      *         if there is no such header or the header value is not a number
764      */
765     public static int getIntHeader(HttpMessage message, CharSequence name) {
766         String value = getHeader(message, name);
767         if (value == null) {
768             throw new NumberFormatException("header not found: " + name);
769         }
770         return Integer.parseInt(value);
771     }
772 
773     /**
774      * @see #getIntHeader(HttpMessage, CharSequence, int)
775      */
776     public static int getIntHeader(HttpMessage message, String name, int defaultValue) {
777         return getIntHeader(message, (CharSequence) name, defaultValue);
778     }
779 
780     /**
781      * Returns the integer header value with the specified header name.  If
782      * there are more than one header value for the specified header name, the
783      * first value is returned.
784      *
785      * @return the header value or the {@code defaultValue} if there is no such
786      *         header or the header value is not a number
787      */
788     public static int getIntHeader(HttpMessage message, CharSequence name, int defaultValue) {
789         String value = getHeader(message, name);
790         if (value == null) {
791             return defaultValue;
792         }
793 
794         try {
795             return Integer.parseInt(value);
796         } catch (NumberFormatException ignored) {
797             return defaultValue;
798         }
799     }
800 
801     /**
802      * @see #setIntHeader(HttpMessage, CharSequence, int)
803      */
804     public static void setIntHeader(HttpMessage message, String name, int value) {
805         message.headers().set(name, value);
806     }
807 
808     /**
809      * Sets a new integer header with the specified name and value.  If there
810      * is an existing header with the same name, the existing header is removed.
811      */
812     public static void setIntHeader(HttpMessage message, CharSequence name, int value) {
813         message.headers().set(name, value);
814     }
815 
816     /**
817      * @see #setIntHeader(HttpMessage, CharSequence, Iterable)
818      */
819     public static void setIntHeader(HttpMessage message, String name, Iterable<Integer> values) {
820         message.headers().set(name, values);
821     }
822 
823     /**
824      * Sets a new integer header with the specified name and values.  If there
825      * is an existing header with the same name, the existing header is removed.
826      */
827     public static void setIntHeader(HttpMessage message, CharSequence name, Iterable<Integer> values) {
828         message.headers().set(name, values);
829     }
830 
831     /**
832      *
833      * @see #addIntHeader(HttpMessage, CharSequence, int)
834      */
835     public static void addIntHeader(HttpMessage message, String name, int value) {
836         message.headers().add(name, value);
837     }
838 
839     /**
840      * Adds a new integer header with the specified name and value.
841      */
842     public static void addIntHeader(HttpMessage message, CharSequence name, int value) {
843         message.headers().add(name, value);
844     }
845 
846     /**
847      * @see #getDateHeader(HttpMessage, CharSequence)
848      */
849     public static Date getDateHeader(HttpMessage message, String name) throws ParseException {
850         return getDateHeader(message, (CharSequence) name);
851     }
852 
853     /**
854      * Returns the date header value with the specified header name.  If
855      * there are more than one header value for the specified header name, the
856      * first value is returned.
857      *
858      * @return the header value
859      * @throws ParseException
860      *         if there is no such header or the header value is not a formatted date
861      */
862     public static Date getDateHeader(HttpMessage message, CharSequence name) throws ParseException {
863         String value = getHeader(message, name);
864         if (value == null) {
865             throw new ParseException("header not found: " + name, 0);
866         }
867         return HttpHeaderDateFormat.get().parse(value);
868     }
869 
870     /**
871      * @see #getDateHeader(HttpMessage, CharSequence, Date)
872      */
873     public static Date getDateHeader(HttpMessage message, String name, Date defaultValue) {
874         return getDateHeader(message, (CharSequence) name, defaultValue);
875     }
876 
877     /**
878      * Returns the date header value with the specified header name.  If
879      * there are more than one header value for the specified header name, the
880      * first value is returned.
881      *
882      * @return the header value or the {@code defaultValue} if there is no such
883      *         header or the header value is not a formatted date
884      */
885     public static Date getDateHeader(HttpMessage message, CharSequence name, Date defaultValue) {
886         final String value = getHeader(message, name);
887         if (value == null) {
888             return defaultValue;
889         }
890 
891         try {
892             return HttpHeaderDateFormat.get().parse(value);
893         } catch (ParseException ignored) {
894             return defaultValue;
895         }
896     }
897 
898     /**
899      * @see #setDateHeader(HttpMessage, CharSequence, Date)
900      */
901     public static void setDateHeader(HttpMessage message, String name, Date value) {
902         setDateHeader(message, (CharSequence) name, value);
903     }
904 
905     /**
906      * Sets a new date header with the specified name and value.  If there
907      * is an existing header with the same name, the existing header is removed.
908      * The specified value is formatted as defined in
909      * <a href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html#sec3.3.1">RFC2616</a>
910      */
911     public static void setDateHeader(HttpMessage message, CharSequence name, Date value) {
912         if (value != null) {
913             message.headers().set(name, HttpHeaderDateFormat.get().format(value));
914         } else {
915             message.headers().set(name, null);
916         }
917     }
918 
919     /**
920      * @see #setDateHeader(HttpMessage, CharSequence, Iterable)
921      */
922     public static void setDateHeader(HttpMessage message, String name, Iterable<Date> values) {
923         message.headers().set(name, values);
924     }
925 
926     /**
927      * Sets a new date header with the specified name and values.  If there
928      * is an existing header with the same name, the existing header is removed.
929      * The specified values are formatted as defined in
930      * <a href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html#sec3.3.1">RFC2616</a>
931      */
932     public static void setDateHeader(HttpMessage message, CharSequence name, Iterable<Date> values) {
933         message.headers().set(name, values);
934     }
935 
936     /**
937      * @see #addDateHeader(HttpMessage, CharSequence, Date)
938      */
939     public static void addDateHeader(HttpMessage message, String name, Date value) {
940         message.headers().add(name, value);
941     }
942 
943     /**
944      * Adds a new date header with the specified name and value.  The specified
945      * value is formatted as defined in
946      * <a href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html#sec3.3.1">RFC2616</a>
947      */
948     public static void addDateHeader(HttpMessage message, CharSequence name, Date value) {
949         message.headers().add(name, value);
950     }
951 
952     /**
953      * Returns the length of the content.  Please note that this value is
954      * not retrieved from {@link HttpContent#content()} but from the
955      * {@code "Content-Length"} header, and thus they are independent from each
956      * other.
957      *
958      * @return the content length
959      *
960      * @throws NumberFormatException
961      *         if the message does not have the {@code "Content-Length"} header
962      *         or its value is not a number
963      */
964     public static long getContentLength(HttpMessage message) {
965         String value = getHeader(message, CONTENT_LENGTH_ENTITY);
966         if (value != null) {
967             return Long.parseLong(value);
968         }
969 
970         // We know the content length if it's a Web Socket message even if
971         // Content-Length header is missing.
972         long webSocketContentLength = getWebSocketContentLength(message);
973         if (webSocketContentLength >= 0) {
974             return webSocketContentLength;
975         }
976 
977         // Otherwise we don't.
978         throw new NumberFormatException("header not found: " + Names.CONTENT_LENGTH);
979     }
980 
981     /**
982      * Returns the length of the content.  Please note that this value is
983      * not retrieved from {@link HttpContent#content()} but from the
984      * {@code "Content-Length"} header, and thus they are independent from each
985      * other.
986      *
987      * @return the content length or {@code defaultValue} if this message does
988      *         not have the {@code "Content-Length"} header or its value is not
989      *         a number
990      */
991     public static long getContentLength(HttpMessage message, long defaultValue) {
992         String contentLength = message.headers().get(CONTENT_LENGTH_ENTITY);
993         if (contentLength != null) {
994             try {
995                 return Long.parseLong(contentLength);
996             } catch (NumberFormatException ignored) {
997                 return defaultValue;
998             }
999         }
1000 
1001         // We know the content length if it's a Web Socket message even if
1002         // Content-Length header is missing.
1003         long webSocketContentLength = getWebSocketContentLength(message);
1004         if (webSocketContentLength >= 0) {
1005             return webSocketContentLength;
1006         }
1007 
1008         // Otherwise we don't.
1009         return defaultValue;
1010     }
1011 
1012     /**
1013      * Returns the content length of the specified web socket message.  If the
1014      * specified message is not a web socket message, {@code -1} is returned.
1015      */
1016     private static int getWebSocketContentLength(HttpMessage message) {
1017         // WebSockset messages have constant content-lengths.
1018         HttpHeaders h = message.headers();
1019         if (message instanceof HttpRequest) {
1020             HttpRequest req = (HttpRequest) message;
1021             if (HttpMethod.GET.equals(req.getMethod()) &&
1022                 h.contains(SEC_WEBSOCKET_KEY1_ENTITY) &&
1023                 h.contains(SEC_WEBSOCKET_KEY2_ENTITY)) {
1024                 return 8;
1025             }
1026         } else if (message instanceof HttpResponse) {
1027             HttpResponse res = (HttpResponse) message;
1028             if (res.getStatus().code() == 101 &&
1029                 h.contains(SEC_WEBSOCKET_ORIGIN_ENTITY) &&
1030                 h.contains(SEC_WEBSOCKET_LOCATION_ENTITY)) {
1031                 return 16;
1032             }
1033         }
1034 
1035         // Not a web socket message
1036         return -1;
1037     }
1038 
1039     /**
1040      * Sets the {@code "Content-Length"} header.
1041      */
1042     public static void setContentLength(HttpMessage message, long length) {
1043         message.headers().set(CONTENT_LENGTH_ENTITY, length);
1044     }
1045 
1046     /**
1047      * Returns the value of the {@code "Host"} header.
1048      */
1049     public static String getHost(HttpMessage message) {
1050         return message.headers().get(HOST_ENTITY);
1051     }
1052 
1053     /**
1054      * Returns the value of the {@code "Host"} header.  If there is no such
1055      * header, the {@code defaultValue} is returned.
1056      */
1057     public static String getHost(HttpMessage message, String defaultValue) {
1058         return getHeader(message, HOST_ENTITY, defaultValue);
1059     }
1060 
1061     /**
1062      * @see #setHost(HttpMessage, CharSequence)
1063      */
1064     public static void setHost(HttpMessage message, String value) {
1065         message.headers().set(HOST_ENTITY, value);
1066     }
1067 
1068     /**
1069      * Sets the {@code "Host"} header.
1070      */
1071     public static void setHost(HttpMessage message, CharSequence value) {
1072         message.headers().set(HOST_ENTITY, value);
1073     }
1074 
1075     /**
1076      * Returns the value of the {@code "Date"} header.
1077      *
1078      * @throws ParseException
1079      *         if there is no such header or the header value is not a formatted date
1080      */
1081     public static Date getDate(HttpMessage message) throws ParseException {
1082         return getDateHeader(message, DATE_ENTITY);
1083     }
1084 
1085     /**
1086      * Returns the value of the {@code "Date"} header. If there is no such
1087      * header or the header is not a formatted date, the {@code defaultValue}
1088      * is returned.
1089      */
1090     public static Date getDate(HttpMessage message, Date defaultValue) {
1091         return getDateHeader(message, DATE_ENTITY, defaultValue);
1092     }
1093 
1094     /**
1095      * Sets the {@code "Date"} header.
1096      */
1097     public static void setDate(HttpMessage message, Date value) {
1098         if (value != null) {
1099             message.headers().set(DATE_ENTITY, HttpHeaderDateFormat.get().format(value));
1100         } else {
1101             message.headers().set(DATE_ENTITY, null);
1102         }
1103     }
1104 
1105     /**
1106      * Returns {@code true} if and only if the specified message contains the
1107      * {@code "Expect: 100-continue"} header.
1108      */
1109     public static boolean is100ContinueExpected(HttpMessage message) {
1110         // Expect: 100-continue is for requests only.
1111         if (!(message instanceof HttpRequest)) {
1112             return false;
1113         }
1114 
1115         // It works only on HTTP/1.1 or later.
1116         if (message.getProtocolVersion().compareTo(HttpVersion.HTTP_1_1) < 0) {
1117             return false;
1118         }
1119 
1120         // In most cases, there will be one or zero 'Expect' header.
1121         String value = message.headers().get(EXPECT_ENTITY);
1122         if (value == null) {
1123             return false;
1124         }
1125         if (equalsIgnoreCase(CONTINUE_ENTITY, value)) {
1126             return true;
1127         }
1128 
1129         // Multiple 'Expect' headers.  Search through them.
1130         return message.headers().contains(EXPECT_ENTITY, CONTINUE_ENTITY, true);
1131     }
1132 
1133     /**
1134      * Sets the {@code "Expect: 100-continue"} header to the specified message.
1135      * If there is any existing {@code "Expect"} header, they are replaced with
1136      * the new one.
1137      */
1138     public static void set100ContinueExpected(HttpMessage message) {
1139         set100ContinueExpected(message, true);
1140     }
1141 
1142     /**
1143      * Sets or removes the {@code "Expect: 100-continue"} header to / from the
1144      * specified message.  If {@code set} is {@code true},
1145      * the {@code "Expect: 100-continue"} header is set and all other previous
1146      * {@code "Expect"} headers are removed.  Otherwise, all {@code "Expect"}
1147      * headers are removed completely.
1148      */
1149     public static void set100ContinueExpected(HttpMessage message, boolean set) {
1150         if (set) {
1151             message.headers().set(EXPECT_ENTITY, CONTINUE_ENTITY);
1152         } else {
1153             message.headers().remove(EXPECT_ENTITY);
1154         }
1155     }
1156 
1157     /**
1158      * Validates the name of a header
1159      *
1160      * @param headerName The header name being validated
1161      */
1162     static void validateHeaderName(CharSequence headerName) {
1163         //Check to see if the name is null
1164         if (headerName == null) {
1165             throw new NullPointerException("Header names cannot be null");
1166         }
1167         //Go through each of the characters in the name
1168         for (int index = 0; index < headerName.length(); index ++) {
1169             //Actually get the character
1170             char character = headerName.charAt(index);
1171 
1172             //Check to see if the character is not an ASCII character
1173             if (character > 127) {
1174                 throw new IllegalArgumentException(
1175                         "Header name cannot contain non-ASCII characters: " + headerName);
1176             }
1177 
1178             //Check for prohibited characters.
1179             switch (character) {
1180                 case '\t': case '\n': case 0x0b: case '\f': case '\r':
1181                 case ' ':  case ',':  case ':':  case ';':  case '=':
1182                     throw new IllegalArgumentException(
1183                             "Header name cannot contain the following prohibited characters: " +
1184                                     "=,;: \\t\\r\\n\\v\\f: " + headerName);
1185             }
1186         }
1187     }
1188 
1189     /**
1190      * Validates the specified header value
1191      *
1192      * @param headerValue The value being validated
1193      */
1194     static void validateHeaderValue(CharSequence headerValue) {
1195         //Check to see if the value is null
1196         if (headerValue == null) {
1197             throw new NullPointerException("Header values cannot be null");
1198         }
1199 
1200         /*
1201          * Set up the state of the validation
1202          *
1203          * States are as follows:
1204          *
1205          * 0: Previous character was neither CR nor LF
1206          * 1: The previous character was CR
1207          * 2: The previous character was LF
1208          */
1209         int state = 0;
1210 
1211         //Start looping through each of the character
1212 
1213         for (int index = 0; index < headerValue.length(); index ++) {
1214             char character = headerValue.charAt(index);
1215 
1216             //Check the absolutely prohibited characters.
1217             switch (character) {
1218                 case 0x0b: // Vertical tab
1219                     throw new IllegalArgumentException(
1220                             "Header value contains a prohibited character '\\v': " + headerValue);
1221                 case '\f':
1222                     throw new IllegalArgumentException(
1223                             "Header value contains a prohibited character '\\f': " + headerValue);
1224             }
1225 
1226             // Check the CRLF (HT | SP) pattern
1227             switch (state) {
1228                 case 0:
1229                     switch (character) {
1230                         case '\r':
1231                             state = 1;
1232                             break;
1233                         case '\n':
1234                             state = 2;
1235                             break;
1236                     }
1237                     break;
1238                 case 1:
1239                     switch (character) {
1240                         case '\n':
1241                             state = 2;
1242                             break;
1243                         default:
1244                             throw new IllegalArgumentException(
1245                                     "Only '\\n' is allowed after '\\r': " + headerValue);
1246                     }
1247                     break;
1248                 case 2:
1249                     switch (character) {
1250                         case '\t': case ' ':
1251                             state = 0;
1252                             break;
1253                         default:
1254                             throw new IllegalArgumentException(
1255                                     "Only ' ' and '\\t' are allowed after '\\n': " + headerValue);
1256                     }
1257             }
1258         }
1259 
1260         if (state != 0) {
1261             throw new IllegalArgumentException(
1262                     "Header value must not end with '\\r' or '\\n':" + headerValue);
1263         }
1264     }
1265 
1266     /**
1267      * Checks to see if the transfer encoding in a specified {@link HttpMessage} is chunked
1268      *
1269      * @param message The message to check
1270      * @return True if transfer encoding is chunked, otherwise false
1271      */
1272     public static boolean isTransferEncodingChunked(HttpMessage message) {
1273         return message.headers().contains(TRANSFER_ENCODING_ENTITY, CHUNKED_ENTITY, true);
1274     }
1275 
1276     public static void removeTransferEncodingChunked(HttpMessage m) {
1277         List<String> values = m.headers().getAll(TRANSFER_ENCODING_ENTITY);
1278         if (values.isEmpty()) {
1279             return;
1280         }
1281         Iterator<String> valuesIt = values.iterator();
1282         while (valuesIt.hasNext()) {
1283             String value = valuesIt.next();
1284             if (equalsIgnoreCase(value, CHUNKED_ENTITY)) {
1285                 valuesIt.remove();
1286             }
1287         }
1288         if (values.isEmpty()) {
1289             m.headers().remove(TRANSFER_ENCODING_ENTITY);
1290         } else {
1291             m.headers().set(TRANSFER_ENCODING_ENTITY, values);
1292         }
1293     }
1294 
1295     public static void setTransferEncodingChunked(HttpMessage m) {
1296         setHeader(m, TRANSFER_ENCODING_ENTITY, CHUNKED_ENTITY);
1297         removeHeader(m, CONTENT_LENGTH_ENTITY);
1298     }
1299 
1300     public static boolean isContentLengthSet(HttpMessage m) {
1301         return m.headers().contains(CONTENT_LENGTH_ENTITY);
1302     }
1303 
1304     /**
1305      * Returns {@code true} if both {@link CharSequence}'s are equals when ignore the case.
1306      * This only supports US_ASCII.
1307      */
1308     public static boolean equalsIgnoreCase(CharSequence name1, CharSequence name2) {
1309         if (name1 == name2) {
1310             return true;
1311         }
1312 
1313         if (name1 == null || name2 == null) {
1314             return false;
1315         }
1316 
1317         int nameLen = name1.length();
1318         if (nameLen != name2.length()) {
1319             return false;
1320         }
1321 
1322         for (int i = nameLen - 1; i >= 0; i --) {
1323             char c1 = name1.charAt(i);
1324             char c2 = name2.charAt(i);
1325             if (c1 != c2) {
1326                 if (c1 >= 'A' && c1 <= 'Z') {
1327                     c1 += 32;
1328                 }
1329                 if (c2 >= 'A' && c2 <= 'Z') {
1330                     c2 += 32;
1331                 }
1332                 if (c1 != c2) {
1333                     return false;
1334                 }
1335             }
1336         }
1337         return true;
1338     }
1339 
1340     static int hash(CharSequence name) {
1341         if (name instanceof HttpHeaderEntity) {
1342             return ((HttpHeaderEntity) name).hash();
1343         }
1344         int h = 0;
1345         for (int i = name.length() - 1; i >= 0; i --) {
1346             char c = name.charAt(i);
1347             if (c >= 'A' && c <= 'Z') {
1348                 c += 32;
1349             }
1350             h = 31 * h + c;
1351         }
1352 
1353         if (h > 0) {
1354             return h;
1355         } else if (h == Integer.MIN_VALUE) {
1356             return Integer.MAX_VALUE;
1357         } else {
1358             return -h;
1359         }
1360     }
1361 
1362     static void encode(HttpHeaders headers, ByteBuf buf) {
1363         if (headers instanceof DefaultHttpHeaders) {
1364             ((DefaultHttpHeaders) headers).encode(buf);
1365         } else {
1366             for (Entry<String, String> header: headers) {
1367                 encode(header.getKey(), header.getValue(), buf);
1368             }
1369         }
1370     }
1371 
1372     @SuppressWarnings("deprecation")
1373     static void encode(CharSequence key, CharSequence value, ByteBuf buf) {
1374         if (!encodeAscii(key, buf)) {
1375             buf.writeBytes(HEADER_SEPERATOR);
1376         }
1377         if (!encodeAscii(value, buf)) {
1378             buf.writeBytes(CRLF);
1379         }
1380     }
1381 
1382     public static boolean encodeAscii(CharSequence seq, ByteBuf buf) {
1383         if (seq instanceof HttpHeaderEntity) {
1384             return ((HttpHeaderEntity) seq).encode(buf);
1385         } else {
1386             encodeAscii0(seq, buf);
1387             return false;
1388         }
1389     }
1390 
1391     static void encodeAscii0(CharSequence seq, ByteBuf buf) {
1392         int length = seq.length();
1393         for (int i = 0 ; i < length; i++) {
1394             buf.writeByte(c2b(seq.charAt(i)));
1395         }
1396     }
1397 
1398     private static byte c2b(char c) {
1399         if (c > 255) {
1400             return '?';
1401         }
1402         return (byte) c;
1403     }
1404 
1405     /**
1406      * Create a new {@link CharSequence} which is optimized for reuse as {@link HttpHeaders} name or value.
1407      * So if you have a Header name or value that you want to reuse you should make use of this.
1408      */
1409     public static CharSequence newEntity(String name) {
1410         if (name == null) {
1411             throw new NullPointerException("name");
1412         }
1413         return new HttpHeaderEntity(name);
1414     }
1415 
1416     /**
1417      * Create a new {@link CharSequence} which is optimized for reuse as {@link HttpHeaders} name.
1418      * So if you have a Header name that you want to reuse you should make use of this.
1419      */
1420     public static CharSequence newNameEntity(String name) {
1421         if (name == null) {
1422             throw new NullPointerException("name");
1423         }
1424         return new HttpHeaderEntity(name, HEADER_SEPERATOR);
1425     }
1426 
1427     /**
1428      * Create a new {@link CharSequence} which is optimized for reuse as {@link HttpHeaders} value.
1429      * So if you have a Header value that you want to reuse you should make use of this.
1430      */
1431     public static CharSequence newValueEntity(String name) {
1432         if (name == null) {
1433             throw new NullPointerException("name");
1434         }
1435         return new HttpHeaderEntity(name, CRLF);
1436     }
1437 
1438     protected HttpHeaders() { }
1439 
1440     /**
1441      * @see {@link #get(CharSequence)}
1442      */
1443     public abstract String get(String name);
1444 
1445     /**
1446      * Returns the value of a header with the specified name.  If there are
1447      * more than one values for the specified name, the first value is returned.
1448      *
1449      * @param name The name of the header to search
1450      * @return The first header value or {@code null} if there is no such header
1451      */
1452     public String get(CharSequence name) {
1453         return get(name.toString());
1454     }
1455 
1456     /**
1457      * @see {@link #getAll(CharSequence)}
1458      */
1459     public abstract List<String> getAll(String name);
1460 
1461     /**
1462      * Returns the values of headers with the specified name
1463      *
1464      * @param name The name of the headers to search
1465      * @return A {@link List} of header values which will be empty if no values
1466      *         are found
1467      */
1468     public List<String> getAll(CharSequence name) {
1469         return getAll(name.toString());
1470     }
1471 
1472     /**
1473      * Returns a new {@link List} that contains all headers in this object.  Note that modifying the
1474      * returned {@link List} will not affect the state of this object.  If you intend to enumerate over the header
1475      * entries only, use {@link #iterator()} instead, which has much less overhead.
1476      */
1477     public abstract List<Map.Entry<String, String>> entries();
1478 
1479     /**
1480      * @see #contains(CharSequence)
1481      */
1482     public abstract boolean contains(String name);
1483 
1484     /**
1485      * Checks to see if there is a header with the specified name
1486      *
1487      * @param name The name of the header to search for
1488      * @return True if at least one header is found
1489      */
1490     public boolean contains(CharSequence name) {
1491         return contains(name.toString());
1492     }
1493 
1494     /**
1495      * Checks if no header exists.
1496      */
1497     public abstract boolean isEmpty();
1498 
1499     /**
1500      * Returns a new {@link Set} that contains the names of all headers in this object.  Note that modifying the
1501      * returned {@link Set} will not affect the state of this object.  If you intend to enumerate over the header
1502      * entries only, use {@link #iterator()} instead, which has much less overhead.
1503      */
1504     public abstract Set<String> names();
1505 
1506     /**
1507      * @see {@link #add(CharSequence, Object)}
1508      */
1509     public abstract HttpHeaders add(String name, Object value);
1510 
1511     /**
1512      * Adds a new header with the specified name and value.
1513      *
1514      * If the specified value is not a {@link String}, it is converted
1515      * into a {@link String} by {@link Object#toString()}, except in the cases
1516      * of {@link Date} and {@link Calendar}, which are formatted to the date
1517      * format defined in <a href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html#sec3.3.1">RFC2616</a>.
1518      *
1519      * @param name The name of the header being added
1520      * @param value The value of the header being added
1521      *
1522      * @return {@code this}
1523      */
1524     public HttpHeaders add(CharSequence name, Object value) {
1525         return add(name.toString(), value);
1526     }
1527 
1528     /**
1529      * @see {@link #add(CharSequence, Iterable)}
1530      */
1531     public abstract HttpHeaders add(String name, Iterable<?> values);
1532 
1533     /**
1534      * Adds a new header with the specified name and values.
1535      *
1536      * This getMethod can be represented approximately as the following code:
1537      * <pre>
1538      * for (Object v: values) {
1539      *     if (v == null) {
1540      *         break;
1541      *     }
1542      *     headers.add(name, v);
1543      * }
1544      * </pre>
1545      *
1546      * @param name The name of the headers being set
1547      * @param values The values of the headers being set
1548      * @return {@code this}
1549      */
1550     public HttpHeaders add(CharSequence name, Iterable<?> values) {
1551         return add(name.toString(), values);
1552     }
1553 
1554     /**
1555      * Adds all header entries of the specified {@code headers}.
1556      *
1557      * @return {@code this}
1558      */
1559     public HttpHeaders add(HttpHeaders headers) {
1560         if (headers == null) {
1561             throw new NullPointerException("headers");
1562         }
1563         for (Map.Entry<String, String> e: headers) {
1564             add(e.getKey(), e.getValue());
1565         }
1566         return this;
1567     }
1568 
1569     /**
1570      * @see {@link #set(CharSequence, Object)}
1571      */
1572     public abstract HttpHeaders set(String name, Object value);
1573 
1574     /**
1575      * Sets a header with the specified name and value.
1576      *
1577      * If there is an existing header with the same name, it is removed.
1578      * If the specified value is not a {@link String}, it is converted into a
1579      * {@link String} by {@link Object#toString()}, except for {@link Date}
1580      * and {@link Calendar}, which are formatted to the date format defined in
1581      * <a href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html#sec3.3.1">RFC2616</a>.
1582      *
1583      * @param name The name of the header being set
1584      * @param value The value of the header being set
1585      * @return {@code this}
1586      */
1587     public HttpHeaders set(CharSequence name, Object value) {
1588         return set(name.toString(), value);
1589     }
1590 
1591     /**
1592      * @see {@link #set(CharSequence, Iterable)}
1593      */
1594     public abstract HttpHeaders set(String name, Iterable<?> values);
1595 
1596     /**
1597      * Sets a header with the specified name and values.
1598      *
1599      * If there is an existing header with the same name, it is removed.
1600      * This getMethod can be represented approximately as the following code:
1601      * <pre>
1602      * headers.remove(name);
1603      * for (Object v: values) {
1604      *     if (v == null) {
1605      *         break;
1606      *     }
1607      *     headers.add(name, v);
1608      * }
1609      * </pre>
1610      *
1611      * @param name The name of the headers being set
1612      * @param values The values of the headers being set
1613      * @return {@code this}
1614      */
1615     public HttpHeaders set(CharSequence name, Iterable<?> values) {
1616         return set(name.toString(), values);
1617     }
1618 
1619     /**
1620      * Cleans the current header entries and copies all header entries of the specified {@code headers}.
1621      *
1622      * @return {@code this}
1623      */
1624     public HttpHeaders set(HttpHeaders headers) {
1625         if (headers == null) {
1626             throw new NullPointerException("headers");
1627         }
1628         if (headers != this) {
1629             clear();
1630             for (Map.Entry<String, String> e : headers) {
1631                 add(e.getKey(), e.getValue());
1632             }
1633         }
1634         return this;
1635     }
1636 
1637     /**
1638      * @see {@link #remove(CharSequence)}
1639      */
1640     public abstract HttpHeaders remove(String name);
1641 
1642     /**
1643      * Removes the header with the specified name.
1644      *
1645      * @param name The name of the header to remove
1646      * @return {@code this}
1647      */
1648     public HttpHeaders remove(CharSequence name) {
1649         return remove(name.toString());
1650     }
1651 
1652     /**
1653      * Removes all headers from this {@link HttpMessage}.
1654      *
1655      * @return {@code this}
1656      */
1657     public abstract HttpHeaders clear();
1658 
1659     /**
1660      * @see {@link #contains(CharSequence, CharSequence, boolean)}
1661      */
1662     public boolean contains(String name, String value, boolean ignoreCaseValue) {
1663         List<String> values = getAll(name);
1664         if (values.isEmpty()) {
1665             return false;
1666         }
1667 
1668         for (String v: values) {
1669             if (ignoreCaseValue) {
1670                 if (equalsIgnoreCase(v, value)) {
1671                     return true;
1672                 }
1673             } else {
1674                 if (v.equals(value)) {
1675                     return true;
1676                 }
1677             }
1678         }
1679         return false;
1680     }
1681 
1682     /**
1683      * Returns {@code true} if a header with the {@code name} and {@code value} exists, {@code false} otherwise.
1684      * This also handles multiple values that are seperated with a {@code ,}.
1685      * <p>
1686      * If {@code ignoreCase} is {@code true} then a case insensitive compare is done on the value.
1687      * @param name the name of the header to find
1688      * @param value the value of the header to find
1689      * @param ignoreCase {@code true} then a case insensitive compare is run to compare values.
1690      * otherwise a case sensitive compare is run to compare values.
1691      */
1692     public boolean containsValue(CharSequence name, CharSequence value, boolean ignoreCase) {
1693         List<String> values = getAll(name);
1694         if (values.isEmpty()) {
1695             return false;
1696         }
1697 
1698         for (String v: values) {
1699             if (contains(v, value, ignoreCase)) {
1700                 return true;
1701             }
1702         }
1703         return false;
1704     }
1705 
1706     private static boolean contains(String value, CharSequence expected, boolean ignoreCase) {
1707         String[] parts = value.split(",");
1708         if (ignoreCase) {
1709             for (String s: parts) {
1710                 if (equalsIgnoreCase(expected, s.trim())) {
1711                     return true;
1712                 }
1713             }
1714         } else {
1715             for (String s: parts) {
1716                 if (s.trim().contentEquals(expected)) {
1717                     return true;
1718                 }
1719             }
1720         }
1721         return false;
1722     }
1723 
1724     /**
1725      * Returns {@code true} if a header with the name and value exists.
1726      *
1727      * @param name              the headername
1728      * @param value             the value
1729      * @param ignoreCaseValue   {@code true} if case should be ignored
1730      * @return contains         {@code true} if it contains it {@code false} otherwise
1731      */
1732     public boolean contains(CharSequence name, CharSequence value, boolean ignoreCaseValue) {
1733         return contains(name.toString(), value.toString(), ignoreCaseValue);
1734     }
1735 }