查看本类的 API文档回源码主页即时通讯网 - 即时通讯开发者社区!
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 org.jboss.netty.handler.codec.http;
17  
18  import java.text.ParseException;
19  import java.util.Calendar;
20  import java.util.Collections;
21  import java.util.Date;
22  import java.util.Iterator;
23  import java.util.List;
24  import java.util.Map;
25  import java.util.Map.Entry;
26  import java.util.Set;
27  
28  
29  /**
30   * Provides the constants for the standard HTTP header names and values and
31   * commonly used utility methods that accesses an {@link HttpMessage}.
32   */
33  public abstract class HttpHeaders implements Iterable<Map.Entry<String, String>> {
34  
35      public static final HttpHeaders EMPTY_HEADERS = new HttpHeaders() {
36          @Override
37          public String get(String name) {
38              return null;
39          }
40  
41          @Override
42          public List<String> getAll(String name) {
43              return Collections.emptyList();
44          }
45  
46          @Override
47          public List<Entry<String, String>> entries() {
48              return Collections.emptyList();
49          }
50  
51          @Override
52          public boolean contains(String name) {
53              return false;
54          }
55  
56          @Override
57          public boolean isEmpty() {
58              return true;
59          }
60  
61          @Override
62          public Set<String> names() {
63              return Collections.emptySet();
64          }
65  
66          @Override
67          public HttpHeaders add(String name, Object value) {
68              throw new UnsupportedOperationException("read only");
69          }
70  
71          @Override
72          public HttpHeaders add(String name, Iterable<?> values) {
73              throw new UnsupportedOperationException("read only");
74          }
75  
76          @Override
77          public HttpHeaders set(String name, Object value) {
78              throw new UnsupportedOperationException("read only");
79          }
80  
81          @Override
82          public HttpHeaders set(String name, Iterable<?> values) {
83              throw new UnsupportedOperationException("read only");
84          }
85  
86          @Override
87          public HttpHeaders remove(String name) {
88              throw new UnsupportedOperationException("read only");
89          }
90  
91          @Override
92          public HttpHeaders clear() {
93              throw new UnsupportedOperationException("read only");
94          }
95  
96          public Iterator<Entry<String, String>> iterator() {
97              return entries().iterator();
98          }
99      };
100 
101     /**
102      * Standard HTTP header names.
103      */
104     public static final class Names {
105         /**
106          * {@code "Accept"}
107          */
108         public static final String ACCEPT = "Accept";
109         /**
110          * {@code "Accept-Charset"}
111          */
112         public static final String ACCEPT_CHARSET = "Accept-Charset";
113         /**
114          * {@code "Accept-Encoding"}
115          */
116         public static final String ACCEPT_ENCODING = "Accept-Encoding";
117         /**
118          * {@code "Accept-Language"}
119          */
120         public static final String ACCEPT_LANGUAGE = "Accept-Language";
121         /**
122          * {@code "Accept-Ranges"}
123          */
124         public static final String ACCEPT_RANGES = "Accept-Ranges";
125         /**
126          * {@code "Accept-Patch"}
127          */
128         public static final String ACCEPT_PATCH = "Accept-Patch";
129         /**
130          * {@code "Access-Control-Allow-Credentials"}
131          */
132         public static final String ACCESS_CONTROL_ALLOW_CREDENTIALS = "Access-Control-Allow-Credentials";
133         /**
134          * {@code "Access-Control-Allow-Headers"}
135          */
136         public static final String ACCESS_CONTROL_ALLOW_HEADERS = "Access-Control-Allow-Headers";
137         /**
138          * {@code "Access-Control-Allow-Methods"}
139          */
140         public static final String ACCESS_CONTROL_ALLOW_METHODS = "Access-Control-Allow-Methods";
141         /**
142          * {@code "Access-Control-Allow-Origin"}
143          */
144         public static final String ACCESS_CONTROL_ALLOW_ORIGIN = "Access-Control-Allow-Origin";
145         /**
146          * {@code "Access-Control-Expose-Headers"}
147          */
148         public static final String ACCESS_CONTROL_EXPOSE_HEADERS = "Access-Control-Expose-Headers";
149         /**
150          * {@code "Access-Control-Max-Age"}
151          */
152         public static final String ACCESS_CONTROL_MAX_AGE = "Access-Control-Max-Age";
153         /**
154          * {@code "Access-Control-Request-Headers"}
155          */
156         public static final String ACCESS_CONTROL_REQUEST_HEADERS = "Access-Control-Request-Headers";
157         /**
158          * {@code "Access-Control-Request-Method"}
159          */
160         public static final String ACCESS_CONTROL_REQUEST_METHOD = "Access-Control-Request-Method";
161         /**
162          * {@code "Age"}
163          */
164         public static final String AGE = "Age";
165         /**
166          * {@code "Allow"}
167          */
168         public static final String ALLOW = "Allow";
169         /**
170          * {@code "Authorization"}
171          */
172         public static final String AUTHORIZATION = "Authorization";
173         /**
174          * {@code "Cache-Control"}
175          */
176         public static final String CACHE_CONTROL = "Cache-Control";
177         /**
178          * {@code "Connection"}
179          */
180         public static final String CONNECTION = "Connection";
181         /**
182          * {@code "Content-Base"}
183          */
184         public static final String CONTENT_BASE = "Content-Base";
185         /**
186          * {@code "Content-Encoding"}
187          */
188         public static final String CONTENT_ENCODING = "Content-Encoding";
189         /**
190          * {@code "Content-Language"}
191          */
192         public static final String CONTENT_LANGUAGE = "Content-Language";
193         /**
194          * {@code "Content-Length"}
195          */
196         public static final String CONTENT_LENGTH = "Content-Length";
197         /**
198          * {@code "Content-Location"}
199          */
200         public static final String CONTENT_LOCATION = "Content-Location";
201         /**
202          * {@code "Content-Transfer-Encoding"}
203          */
204         public static final String CONTENT_TRANSFER_ENCODING = "Content-Transfer-Encoding";
205         /**
206          * {@code "Content-MD5"}
207          */
208         public static final String CONTENT_MD5 = "Content-MD5";
209         /**
210          * {@code "Content-Range"}
211          */
212         public static final String CONTENT_RANGE = "Content-Range";
213         /**
214          * {@code "Content-Type"}
215          */
216         public static final String CONTENT_TYPE = "Content-Type";
217         /**
218          * {@code "Cookie"}
219          */
220         public static final String COOKIE = "Cookie";
221         /**
222          * {@code "Date"}
223          */
224         public static final String DATE = "Date";
225         /**
226          * {@code "ETag"}
227          */
228         public static final String ETAG = "ETag";
229         /**
230          * {@code "Expect"}
231          */
232         public static final String EXPECT = "Expect";
233         /**
234          * {@code "Expires"}
235          */
236         public static final String EXPIRES = "Expires";
237         /**
238          * {@code "From"}
239          */
240         public static final String FROM = "From";
241         /**
242          * {@code "Host"}
243          */
244         public static final String HOST = "Host";
245         /**
246          * {@code "If-Match"}
247          */
248         public static final String IF_MATCH = "If-Match";
249         /**
250          * {@code "If-Modified-Since"}
251          */
252         public static final String IF_MODIFIED_SINCE = "If-Modified-Since";
253         /**
254          * {@code "If-None-Match"}
255          */
256         public static final String IF_NONE_MATCH = "If-None-Match";
257         /**
258          * {@code "If-Range"}
259          */
260         public static final String IF_RANGE = "If-Range";
261         /**
262          * {@code "If-Unmodified-Since"}
263          */
264         public static final String IF_UNMODIFIED_SINCE = "If-Unmodified-Since";
265         /**
266          * {@code "Last-Modified"}
267          */
268         public static final String LAST_MODIFIED = "Last-Modified";
269         /**
270          * {@code "Location"}
271          */
272         public static final String LOCATION = "Location";
273         /**
274          * {@code "Max-Forwards"}
275          */
276         public static final String MAX_FORWARDS = "Max-Forwards";
277         /**
278          * {@code "Origin"}
279          */
280         public static final String ORIGIN = "Origin";
281         /**
282          * {@code "Pragma"}
283          */
284         public static final String PRAGMA = "Pragma";
285         /**
286          * {@code "Proxy-Authenticate"}
287          */
288         public static final String PROXY_AUTHENTICATE = "Proxy-Authenticate";
289         /**
290          * {@code "Proxy-Authorization"}
291          */
292         public static final String PROXY_AUTHORIZATION = "Proxy-Authorization";
293         /**
294          * {@code "Range"}
295          */
296         public static final String RANGE = "Range";
297         /**
298          * {@code "Referer"}
299          */
300         public static final String REFERER = "Referer";
301         /**
302          * {@code "Retry-After"}
303          */
304         public static final String RETRY_AFTER = "Retry-After";
305         /**
306          * {@code "Sec-WebSocket-Key1"}
307          */
308         public static final String SEC_WEBSOCKET_KEY1 = "Sec-WebSocket-Key1";
309         /**
310          * {@code "Sec-WebSocket-Key2"}
311          */
312         public static final String SEC_WEBSOCKET_KEY2 = "Sec-WebSocket-Key2";
313         /**
314          * {@code "Sec-WebSocket-Location"}
315          */
316         public static final String SEC_WEBSOCKET_LOCATION = "Sec-WebSocket-Location";
317         /**
318          * {@code "Sec-WebSocket-Origin"}
319          */
320         public static final String SEC_WEBSOCKET_ORIGIN = "Sec-WebSocket-Origin";
321         /**
322          * {@code "Sec-WebSocket-Protocol"}
323          */
324         public static final String SEC_WEBSOCKET_PROTOCOL = "Sec-WebSocket-Protocol";
325         /**
326          * {@code "Sec-WebSocket-Version"}
327          */
328         public static final String SEC_WEBSOCKET_VERSION = "Sec-WebSocket-Version";
329         /**
330          * {@code "Sec-WebSocket-Key"}
331          */
332         public static final String SEC_WEBSOCKET_KEY = "Sec-WebSocket-Key";
333         /**
334          * {@code "Sec-WebSocket-Accept"}
335          */
336         public static final String SEC_WEBSOCKET_ACCEPT = "Sec-WebSocket-Accept";
337         /**
338          * {@code "Server"}
339          */
340         public static final String SERVER = "Server";
341         /**
342          * {@code "Set-Cookie"}
343          */
344         public static final String SET_COOKIE = "Set-Cookie";
345         /**
346          * {@code "Set-Cookie2"}
347          */
348         public static final String SET_COOKIE2 = "Set-Cookie2";
349         /**
350          * {@code "TE"}
351          */
352         public static final String TE = "TE";
353         /**
354          * {@code "Trailer"}
355          */
356         public static final String TRAILER = "Trailer";
357         /**
358          * {@code "Transfer-Encoding"}
359          */
360         public static final String TRANSFER_ENCODING = "Transfer-Encoding";
361         /**
362          * {@code "Upgrade"}
363          */
364         public static final String UPGRADE = "Upgrade";
365         /**
366          * {@code "User-Agent"}
367          */
368         public static final String USER_AGENT = "User-Agent";
369         /**
370          * {@code "Vary"}
371          */
372         public static final String VARY = "Vary";
373         /**
374          * {@code "Via"}
375          */
376         public static final String VIA = "Via";
377         /**
378          * {@code "Warning"}
379          */
380         public static final String WARNING = "Warning";
381         /**
382          * {@code "WebSocket-Location"}
383          */
384         public static final String WEBSOCKET_LOCATION = "WebSocket-Location";
385         /**
386          * {@code "WebSocket-Origin"}
387          */
388         public static final String WEBSOCKET_ORIGIN = "WebSocket-Origin";
389         /**
390          * {@code "WebSocket-Protocol"}
391          */
392         public static final String WEBSOCKET_PROTOCOL = "WebSocket-Protocol";
393         /**
394          * {@code "WWW-Authenticate"}
395          */
396         public static final String WWW_AUTHENTICATE = "WWW-Authenticate";
397 
398         private Names() {
399         }
400     }
401 
402     /**
403      * Standard HTTP header values.
404      */
405     public static final class Values {
406         /**
407          * {@code "application/x-www-form-urlencoded"}
408          */
409         public static final String APPLICATION_X_WWW_FORM_URLENCODED = "application/x-www-form-urlencoded";
410         /**
411          * {@code "base64"}
412          */
413         public static final String BASE64 = "base64";
414         /**
415          * {@code "binary"}
416          */
417         public static final String BINARY = "binary";
418         /**
419          * {@code "boundary"}
420          */
421         public static final String BOUNDARY = "boundary";
422         /**
423          * {@code "bytes"}
424          */
425         public static final String BYTES = "bytes";
426         /**
427          * {@code "charset"}
428          */
429         public static final String CHARSET = "charset";
430         /**
431          * {@code "chunked"}
432          */
433         public static final String CHUNKED = "chunked";
434         /**
435          * {@code "close"}
436          */
437         public static final String CLOSE = "close";
438         /**
439          * {@code "compress"}
440          */
441         public static final String COMPRESS = "compress";
442         /**
443          * {@code "100-continue"}
444          */
445         public static final String CONTINUE =  "100-continue";
446         /**
447          * {@code "deflate"}
448          */
449         public static final String DEFLATE = "deflate";
450         /**
451          * {@code "gzip"}
452          */
453         public static final String GZIP = "gzip";
454         /**
455          * {@code "identity"}
456          */
457         public static final String IDENTITY = "identity";
458         /**
459          * {@code "keep-alive"}
460          */
461         public static final String KEEP_ALIVE = "keep-alive";
462         /**
463          * {@code "max-age"}
464          */
465         public static final String MAX_AGE = "max-age";
466         /**
467          * {@code "max-stale"}
468          */
469         public static final String MAX_STALE = "max-stale";
470         /**
471          * {@code "min-fresh"}
472          */
473         public static final String MIN_FRESH = "min-fresh";
474         /**
475          * {@code "multipart/form-data"}
476          */
477         public static final String MULTIPART_FORM_DATA = "multipart/form-data";
478         /**
479          * {@code "must-revalidate"}
480          */
481         public static final String MUST_REVALIDATE = "must-revalidate";
482         /**
483          * {@code "no-cache"}
484          */
485         public static final String NO_CACHE = "no-cache";
486         /**
487          * {@code "no-store"}
488          */
489         public static final String NO_STORE = "no-store";
490         /**
491          * {@code "no-transform"}
492          */
493         public static final String NO_TRANSFORM = "no-transform";
494         /**
495          * {@code "none"}
496          */
497         public static final String NONE = "none";
498         /**
499          * {@code "only-if-cached"}
500          */
501         public static final String ONLY_IF_CACHED = "only-if-cached";
502         /**
503          * {@code "private"}
504          */
505         public static final String PRIVATE = "private";
506         /**
507          * {@code "proxy-revalidate"}
508          */
509         public static final String PROXY_REVALIDATE = "proxy-revalidate";
510         /**
511          * {@code "public"}
512          */
513         public static final String PUBLIC = "public";
514         /**
515          * {@code "quoted-printable"}
516          */
517         public static final String QUOTED_PRINTABLE = "quoted-printable";
518         /**
519          * {@code "s-maxage"}
520          */
521         public static final String S_MAXAGE = "s-maxage";
522         /**
523          * {@code "trailers"}
524          */
525         public static final String TRAILERS = "trailers";
526         /**
527          * {@code "Upgrade"}
528          */
529         public static final String UPGRADE = "Upgrade";
530         /**
531          * {@code "WebSocket"}
532          */
533         public static final String WEBSOCKET = "WebSocket";
534 
535         private Values() {
536         }
537     }
538 
539     /**
540      * Returns {@code true} if and only if the connection can remain open and
541      * thus 'kept alive'.  This methods respects the value of the
542      * {@code "Connection"} header first and then the return value of
543      * {@link HttpVersion#isKeepAliveDefault()}.
544      */
545     public static boolean isKeepAlive(HttpMessage message) {
546         String connection = message.headers().get(Names.CONNECTION);
547 
548         boolean close = Values.CLOSE.equalsIgnoreCase(connection);
549         if (close) {
550             return false;
551         }
552 
553         if (message.getProtocolVersion().isKeepAliveDefault()) {
554             return !close;
555         } else {
556             return Values.KEEP_ALIVE.equalsIgnoreCase(connection);
557         }
558     }
559 
560     /**
561      * Sets the value of the {@code "Connection"} header depending on the
562      * protocol version of the specified message.  This method sets or removes
563      * the {@code "Connection"} header depending on what the default keep alive
564      * mode of the message's protocol version is, as specified by
565      * {@link HttpVersion#isKeepAliveDefault()}.
566      * <ul>
567      * <li>If the connection is kept alive by default:
568      *     <ul>
569      *     <li>set to {@code "close"} if {@code keepAlive} is {@code false}.</li>
570      *     <li>remove otherwise.</li>
571      *     </ul></li>
572      * <li>If the connection is closed by default:
573      *     <ul>
574      *     <li>set to {@code "keep-alive"} if {@code keepAlive} is {@code true}.</li>
575      *     <li>remove otherwise.</li>
576      *     </ul></li>
577      * </ul>
578      */
579     public static void setKeepAlive(HttpMessage message, boolean keepAlive) {
580         HttpHeaders h = message.headers();
581         if (message.getProtocolVersion().isKeepAliveDefault()) {
582             if (keepAlive) {
583                 h.remove(Names.CONNECTION);
584             } else {
585                 h.set(Names.CONNECTION, Values.CLOSE);
586             }
587         } else {
588             if (keepAlive) {
589                 h.set(Names.CONNECTION, Values.KEEP_ALIVE);
590             } else {
591                 h.remove(Names.CONNECTION);
592             }
593         }
594     }
595 
596     /**
597      * Returns the header value with the specified header name.  If there are
598      * more than one header value for the specified header name, the first
599      * value is returned.
600      *
601      * @return the header value or {@code null} if there is no such header
602      */
603     public static String getHeader(HttpMessage message, String name) {
604         return message.headers().get(name);
605     }
606 
607     /**
608      * Returns the header value with the specified header name.  If there are
609      * more than one header value for the specified header name, the first
610      * value is returned.
611      *
612      * @return the header value or the {@code defaultValue} if there is no such
613      *         header
614      */
615     public static String getHeader(HttpMessage message, String name, String defaultValue) {
616         String value = message.headers().get(name);
617         if (value == null) {
618             return defaultValue;
619         }
620         return value;
621     }
622 
623     /**
624      * Sets a new header with the specified name and value.  If there is an
625      * existing header with the same name, the existing header is removed.
626      * If the specified value is not a {@link String}, it is converted into a
627      * {@link String} by {@link Object#toString()}, except for {@link Date}
628      * and {@link Calendar} which are formatted to the date format defined in
629      * <a href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html#sec3.3.1">RFC2616</a>.
630      */
631     public static void setHeader(HttpMessage message, String name, Object value) {
632         message.headers().set(name, value);
633     }
634 
635     /**
636      * Sets a new header with the specified name and values.  If there is an
637      * existing header with the same name, the existing header is removed.
638      * This getMethod can be represented approximately as the following code:
639      * <pre>
640      * removeHeader(message, name);
641      * for (Object v: values) {
642      *     if (v == null) {
643      *         break;
644      *     }
645      *     addHeader(message, name, v);
646      * }
647      * </pre>
648      */
649     public static void setHeader(HttpMessage message, String name, Iterable<?> values) {
650         message.headers().set(name, values);
651     }
652 
653     /**
654      * Adds a new header with the specified name and value.
655      * If the specified value is not a {@link String}, it is converted into a
656      * {@link String} by {@link Object#toString()}, except for {@link Date}
657      * and {@link Calendar} which are formatted to the date format defined in
658      * <a href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html#sec3.3.1">RFC2616</a>.
659      */
660     public static void addHeader(HttpMessage message, String name, Object value) {
661         message.headers().add(name, value);
662     }
663 
664     /**
665      * Removes the header with the specified name.
666      */
667     public static void removeHeader(HttpMessage message, String name) {
668         message.headers().remove(name);
669     }
670 
671     /**
672      * Removes all headers from the specified message.
673      */
674     public static void clearHeaders(HttpMessage message) {
675         message.headers().clear();
676     }
677 
678     /**
679      * Returns the integer header value with the specified header name.  If
680      * there are more than one header value for the specified header name, the
681      * first value is returned.
682      *
683      * @return the header value
684      * @throws NumberFormatException
685      *         if there is no such header or the header value is not a number
686      */
687     public static int getIntHeader(HttpMessage message, String name) {
688         String value = getHeader(message, name);
689         if (value == null) {
690             throw new NumberFormatException("header not found: " + name);
691         }
692         return Integer.parseInt(value);
693     }
694 
695     /**
696      * Returns the integer header value with the specified header name.  If
697      * there are more than one header value for the specified header name, the
698      * first value is returned.
699      *
700      * @return the header value or the {@code defaultValue} if there is no such
701      *         header or the header value is not a number
702      */
703     public static int getIntHeader(HttpMessage message, String name, int defaultValue) {
704         String value = getHeader(message, name);
705         if (value == null) {
706             return defaultValue;
707         }
708 
709         try {
710             return Integer.parseInt(value);
711         } catch (NumberFormatException e) {
712             // Not an integer header - use the default.
713             return defaultValue;
714         }
715     }
716 
717     /**
718      * Sets a new integer header with the specified name and value.  If there
719      * is an existing header with the same name, the existing header is removed.
720      */
721     public static void setIntHeader(HttpMessage message, String name, int value) {
722         message.headers().set(name, value);
723     }
724 
725     /**
726      * Sets a new integer header with the specified name and values.  If there
727      * is an existing header with the same name, the existing header is removed.
728      */
729     public static void setIntHeader(HttpMessage message, String name, Iterable<Integer> values) {
730         message.headers().set(name, values);
731     }
732 
733     /**
734      * Adds a new integer header with the specified name and value.
735      */
736     public static void addIntHeader(HttpMessage message, String name, int value) {
737         message.headers().add(name, value);
738     }
739 
740     /**
741      * Returns the date header value with the specified header name.  If
742      * there are more than one header value for the specified header name, the
743      * first value is returned.
744      *
745      * @return the header value
746      * @throws ParseException
747      *         if there is no such header or the header value is not a formatted date
748      */
749     public static Date getDateHeader(HttpMessage message, String name) throws ParseException {
750         String value = getHeader(message, name);
751         if (value == null) {
752             throw new ParseException("header not found: " + name, 0);
753         }
754         return HttpHeaderDateFormat.get().parse(value);
755     }
756 
757     /**
758      * Returns the date header value with the specified header name.  If
759      * there are more than one header value for the specified header name, the
760      * first value is returned.
761      *
762      * @return the header value or the {@code defaultValue} if there is no such
763      *         header or the header value is not a formatted date
764      */
765     public static Date getDateHeader(HttpMessage message, String name, Date defaultValue) {
766         final String value = getHeader(message, name);
767         if (value == null) {
768             return defaultValue;
769         }
770 
771         try {
772             return HttpHeaderDateFormat.get().parse(value);
773         } catch (ParseException e) {
774             // Not a date header - use the default.
775             return defaultValue;
776         }
777     }
778 
779     /**
780      * Sets a new date header with the specified name and value.  If there
781      * is an existing header with the same name, the existing header is removed.
782      * The specified value is formatted as defined in
783      * <a href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html#sec3.3.1">RFC2616</a>
784      */
785     public static void setDateHeader(HttpMessage message, String name, Date value) {
786         if (value != null) {
787             message.headers().set(name, HttpHeaderDateFormat.get().format(value));
788         } else {
789             message.headers().set(name, null);
790         }
791     }
792 
793     /**
794      * Sets a new date header with the specified name and values.  If there
795      * is an existing header with the same name, the existing header is removed.
796      * The specified values are formatted as defined in
797      * <a href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html#sec3.3.1">RFC2616</a>
798      */
799     public static void setDateHeader(HttpMessage message, String name, Iterable<Date> values) {
800         message.headers().set(name, values);
801     }
802 
803     /**
804      * Adds a new date header with the specified name and value.  The specified
805      * value is formatted as defined in
806      * <a href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html#sec3.3.1">RFC2616</a>
807      */
808     public static void addDateHeader(HttpMessage message, String name, Date value) {
809         message.headers().add(name, value);
810     }
811 
812     /**
813      * Returns the length of the content.  Please note that this value is
814      * not retrieved from {@link HttpMessage#getContent()} but from the
815      * {@code "Content-Length"} header, and thus they are independent from each
816      * other.
817      *
818      * @return the content length
819      *
820      * @throws NumberFormatException
821      *         if the message does not have the {@code "Content-Length"} header
822      *         or its value is not a number
823      */
824     public static long getContentLength(HttpMessage message) {
825         String value = getHeader(message, Names.CONTENT_LENGTH);
826         if (value != null) {
827             return Long.parseLong(value);
828         }
829 
830         // We know the content length if it's a Web Socket message even if
831         // Content-Length header is missing.
832         long webSocketContentLength = getWebSocketContentLength(message);
833         if (webSocketContentLength >= 0) {
834             return webSocketContentLength;
835         }
836 
837         // Otherwise we don't.
838         throw new NumberFormatException("header not found: " + Names.CONTENT_LENGTH);
839     }
840 
841     /**
842      * Returns the length of the content.  Please note that this value is
843      * not retrieved from {@link HttpMessage#getContent()} but from the
844      * {@code "Content-Length"} header, and thus they are independent from each
845      * other.
846      *
847      * @return the content length or {@code defaultValue} if this message does
848      *         not have the {@code "Content-Length"} header or its value is not
849      *         a number
850      */
851     public static long getContentLength(HttpMessage message, long defaultValue) {
852         String contentLength = message.headers().get(Names.CONTENT_LENGTH);
853         if (contentLength != null) {
854             try {
855                 return Long.parseLong(contentLength);
856             } catch (NumberFormatException e) {
857                 // Not a long integer header - use the default.
858                 return defaultValue;
859             }
860         }
861 
862         // We know the content length if it's a Web Socket message even if
863         // Content-Length header is missing.
864         long webSocketContentLength = getWebSocketContentLength(message);
865         if (webSocketContentLength >= 0) {
866             return webSocketContentLength;
867         }
868 
869         // Otherwise we don't.
870         return defaultValue;
871     }
872 
873     /**
874      * Returns the content length of the specified web socket message.  If the
875      * specified message is not a web socket message, {@code -1} is returned.
876      */
877     private static int getWebSocketContentLength(HttpMessage message) {
878         // WebSockset messages have constant content-lengths.
879         HttpHeaders h = message.headers();
880         if (message instanceof HttpRequest) {
881             HttpRequest req = (HttpRequest) message;
882             if (HttpMethod.GET.equals(req.getMethod()) &&
883                 h.contains(Names.SEC_WEBSOCKET_KEY1) &&
884                 h.contains(Names.SEC_WEBSOCKET_KEY2)) {
885                 return 8;
886             }
887         } else if (message instanceof HttpResponse) {
888             HttpResponse res = (HttpResponse) message;
889             if (res.getStatus().getCode() == 101 &&
890                 h.contains(Names.SEC_WEBSOCKET_ORIGIN) &&
891                 h.contains(Names.SEC_WEBSOCKET_LOCATION)) {
892                 return 16;
893             }
894         }
895 
896         // Not a web socket message
897         return -1;
898     }
899 
900     /**
901      * Sets the {@code "Content-Length"} header.
902      */
903     public static void setContentLength(HttpMessage message, long length) {
904         message.headers().set(Names.CONTENT_LENGTH, length);
905     }
906 
907     /**
908      * Returns the value of the {@code "Host"} header.
909      */
910     public static String getHost(HttpMessage message) {
911         return message.headers().get(Names.HOST);
912     }
913 
914     /**
915      * Returns the value of the {@code "Host"} header.  If there is no such
916      * header, the {@code defaultValue} is returned.
917      */
918     public static String getHost(HttpMessage message, String defaultValue) {
919         return getHeader(message, Names.HOST, defaultValue);
920     }
921 
922     /**
923      * Sets the {@code "Host"} header.
924      */
925     public static void setHost(HttpMessage message, String value) {
926         message.headers().set(Names.HOST, value);
927     }
928 
929     /**
930      * Returns the value of the {@code "Date"} header.
931      *
932      * @throws ParseException
933      *         if there is no such header or the header value is not a formatted date
934      */
935     public static Date getDate(HttpMessage message) throws ParseException {
936         return getDateHeader(message, Names.DATE);
937     }
938 
939     /**
940      * Returns the value of the {@code "Date"} header. If there is no such
941      * header or the header is not a formatted date, the {@code defaultValue}
942      * is returned.
943      */
944     public static Date getDate(HttpMessage message, Date defaultValue) {
945         return getDateHeader(message, Names.DATE, defaultValue);
946     }
947 
948     /**
949      * Sets the {@code "Date"} header.
950      */
951     public static void setDate(HttpMessage message, Date value) {
952         if (value != null) {
953             message.headers().set(Names.DATE, HttpHeaderDateFormat.get().format(value));
954         } else {
955             message.headers().set(Names.DATE, null);
956         }
957     }
958 
959     /**
960      * Returns {@code true} if and only if the specified message contains the
961      * {@code "Expect: 100-continue"} header.
962      */
963     public static boolean is100ContinueExpected(HttpMessage message) {
964         // Expect: 100-continue is for requests only.
965         if (!(message instanceof HttpRequest)) {
966             return false;
967         }
968 
969         // It works only on HTTP/1.1 or later.
970         if (message.getProtocolVersion().compareTo(HttpVersion.HTTP_1_1) < 0) {
971             return false;
972         }
973 
974         // In most cases, there will be one or zero 'Expect' header.
975         String value = message.headers().get(Names.EXPECT);
976         if (value == null) {
977             return false;
978         }
979         if (Values.CONTINUE.equalsIgnoreCase(value)) {
980             return true;
981         }
982 
983         // Multiple 'Expect' headers.  Search through them.
984         return message.headers().contains(Names.EXPECT, Values.CONTINUE, true);
985     }
986 
987     /**
988      * Sets the {@code "Expect: 100-continue"} header to the specified message.
989      * If there is any existing {@code "Expect"} header, they are replaced with
990      * the new one.
991      */
992     public static void set100ContinueExpected(HttpMessage message) {
993         set100ContinueExpected(message, true);
994     }
995 
996     /**
997      * Sets or removes the {@code "Expect: 100-continue"} header to / from the
998      * specified message.  If the specified {@code value} is {@code true},
999      * the {@code "Expect: 100-continue"} header is set and all other previous
1000      * {@code "Expect"} headers are removed.  Otherwise, all {@code "Expect"}
1001      * headers are removed completely.
1002      */
1003     public static void set100ContinueExpected(HttpMessage message, boolean set) {
1004         if (set) {
1005             message.headers().set(Names.EXPECT, Values.CONTINUE);
1006         } else {
1007             message.headers().remove(Names.EXPECT);
1008         }
1009     }
1010 
1011     /**
1012      * Validates the name of a header
1013      *
1014      * @param headerName The header name being validated
1015      */
1016     static void validateHeaderName(String headerName) {
1017         //Check to see if the name is null
1018         if (headerName == null) {
1019             throw new NullPointerException("Header names cannot be null");
1020         }
1021         //Go through each of the characters in the name
1022         for (int index = 0; index < headerName.length(); index ++) {
1023             //Actually get the character
1024             char character = headerName.charAt(index);
1025             valideHeaderNameChar(character);
1026         }
1027     }
1028 
1029     static void valideHeaderNameChar(char c) {
1030         //Check to see if the character is not an ASCII character
1031         if (c > 127) {
1032             throw new IllegalArgumentException(
1033                     "Header name cannot contain non-ASCII characters: " + c);
1034         }
1035 
1036         //Check for prohibited characters.
1037         switch (c) {
1038             case '\t': case '\n': case 0x0b: case '\f': case '\r':
1039             case ' ':  case ',':  case ':':  case ';':  case '=':
1040                 throw new IllegalArgumentException(
1041                         "Header name cannot contain the following prohibited characters: " +
1042                                 "=,;: \\t\\r\\n\\v\\f ");
1043         }
1044     }
1045 
1046     /**
1047      * Validates the specified header value
1048      *
1049      * @param headerValue The value being validated
1050      */
1051     static void validateHeaderValue(String headerValue) {
1052         //Check to see if the value is null
1053         if (headerValue == null) {
1054             throw new NullPointerException("Header values cannot be null");
1055         }
1056 
1057         /*
1058          * Set up the state of the validation
1059          *
1060          * States are as follows:
1061          *
1062          * 0: Previous character was neither CR nor LF
1063          * 1: The previous character was CR
1064          * 2: The previous character was LF
1065          */
1066         int state = 0;
1067 
1068         //Start looping through each of the character
1069 
1070         for (int index = 0; index < headerValue.length(); index ++) {
1071             char character = headerValue.charAt(index);
1072 
1073             //Check the absolutely prohibited characters.
1074             switch (character) {
1075                 case 0x0b: // Vertical tab
1076                     throw new IllegalArgumentException(
1077                             "Header value contains a prohibited character '\\v': " + headerValue);
1078                 case '\f':
1079                     throw new IllegalArgumentException(
1080                             "Header value contains a prohibited character '\\f': " + headerValue);
1081             }
1082 
1083             // Check the CRLF (HT | SP) pattern
1084             switch (state) {
1085                 case 0:
1086                     switch (character) {
1087                         case '\r':
1088                             state = 1;
1089                             break;
1090                         case '\n':
1091                             state = 2;
1092                             break;
1093                     }
1094                     break;
1095                 case 1:
1096                     switch (character) {
1097                         case '\n':
1098                             state = 2;
1099                             break;
1100                         default:
1101                             throw new IllegalArgumentException(
1102                                     "Only '\\n' is allowed after '\\r': " + headerValue);
1103                     }
1104                     break;
1105                 case 2:
1106                     switch (character) {
1107                         case '\t': case ' ':
1108                             state = 0;
1109                             break;
1110                         default:
1111                             throw new IllegalArgumentException(
1112                                     "Only ' ' and '\\t' are allowed after '\\n': " + headerValue);
1113                     }
1114             }
1115         }
1116 
1117         if (state != 0) {
1118             throw new IllegalArgumentException(
1119                     "Header value must not end with '\\r' or '\\n':" + headerValue);
1120         }
1121     }
1122 
1123     /**
1124      * Checks to see if the transfer encoding in a specified {@link HttpMessage} is chunked
1125      *
1126      * @param message The message to check
1127      * @return True if transfer encoding is chunked, otherwise false
1128      */
1129     public static boolean isTransferEncodingChunked(HttpMessage message) {
1130         return message.headers().contains(Names.TRANSFER_ENCODING, Values.CHUNKED, true);
1131     }
1132 
1133     public static void removeTransferEncodingChunked(HttpMessage m) {
1134         List<String> values = m.headers().getAll(Names.TRANSFER_ENCODING);
1135         if (values.isEmpty()) {
1136             return;
1137         }
1138         Iterator<String> valuesIt = values.iterator();
1139         while (valuesIt.hasNext()) {
1140             String value = valuesIt.next();
1141             if (value.equalsIgnoreCase(Values.CHUNKED)) {
1142                 valuesIt.remove();
1143             }
1144         }
1145         if (values.isEmpty()) {
1146             m.headers().remove(Names.TRANSFER_ENCODING);
1147         } else {
1148             m.headers().set(Names.TRANSFER_ENCODING, values);
1149         }
1150     }
1151 
1152     public static void setTransferEncodingChunked(HttpMessage m) {
1153         addHeader(m, Names.TRANSFER_ENCODING, Values.CHUNKED);
1154         removeHeader(m, Names.CONTENT_LENGTH);
1155     }
1156 
1157     public static boolean isContentLengthSet(HttpMessage m) {
1158         return m.headers().contains(Names.CONTENT_LENGTH);
1159     }
1160 
1161     protected HttpHeaders() { }
1162 
1163     /**
1164      * Returns the value of a header with the specified name.  If there are
1165      * more than one values for the specified name, the first value is returned.
1166      *
1167      * @param name The name of the header to search
1168      * @return The first header value or {@code null} if there is no such header
1169      */
1170     public abstract String get(String name);
1171 
1172     /**
1173      * Returns the values of headers with the specified name
1174      *
1175      * @param name The name of the headers to search
1176      * @return A {@link List} of header values which will be empty if no values
1177      *         are found
1178      */
1179     public abstract List<String> getAll(String name);
1180 
1181     /**
1182      * Returns a new {@link List} that contains all headers in this object.  Note that modifying the
1183      * returned {@link List} will not affect the state of this object.  If you intend to enumerate over the header
1184      * entries only, use {@link #iterator()} instead, which has much less overhead.
1185      */
1186     public abstract List<Map.Entry<String, String>> entries();
1187 
1188     /**
1189      * Checks to see if there is a header with the specified name
1190      *
1191      * @param name The name of the header to search for
1192      * @return True if at least one header is found
1193      */
1194     public abstract boolean contains(String name);
1195 
1196     /**
1197      * Checks if no header exists.
1198      */
1199     public abstract boolean isEmpty();
1200 
1201     /**
1202      * Returns a new {@link Set} that contains the names of all headers in this object.  Note that modifying the
1203      * returned {@link Set} will not affect the state of this object.  If you intend to enumerate over the header
1204      * entries only, use {@link #iterator()} instead, which has much less overhead.
1205      */
1206     public abstract Set<String> names();
1207 
1208     /**
1209      * Adds a new header with the specified name and value.
1210      *
1211      * If the specified value is not a {@link String}, it is converted
1212      * into a {@link String} by {@link Object#toString()}, except in the cases
1213      * of {@link Date} and {@link Calendar}, which are formatted to the date
1214      * format defined in <a href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html#sec3.3.1">RFC2616</a>.
1215      *
1216      * @param name The name of the header being added
1217      * @param value The value of the header being added
1218      *
1219      * @return {@code this}
1220      */
1221     public abstract HttpHeaders add(String name, Object value);
1222 
1223     /**
1224      * Adds a new header with the specified name and values.
1225      *
1226      * This getMethod can be represented approximately as the following code:
1227      * <pre>
1228      * for (Object v: values) {
1229      *     if (v == null) {
1230      *         break;
1231      *     }
1232      *     headers.add(name, v);
1233      * }
1234      * </pre>
1235      *
1236      * @param name The name of the headers being set
1237      * @param values The values of the headers being set
1238      * @return {@code this}
1239      */
1240     public abstract HttpHeaders add(String name, Iterable<?> values);
1241 
1242     /**
1243      * Adds all header entries of the specified {@code headers}.
1244      *
1245      * @return {@code this}
1246      */
1247     public HttpHeaders add(HttpHeaders headers) {
1248         if (headers == null) {
1249             throw new NullPointerException("headers");
1250         }
1251         for (Map.Entry<String, String> e: headers) {
1252             add(e.getKey(), e.getValue());
1253         }
1254         return this;
1255     }
1256 
1257     /**
1258      * Sets a header with the specified name and value.
1259      *
1260      * If there is an existing header with the same name, it is removed.
1261      * If the specified value is not a {@link String}, it is converted into a
1262      * {@link String} by {@link Object#toString()}, except for {@link Date}
1263      * and {@link Calendar}, which are formatted to the date format defined in
1264      * <a href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html#sec3.3.1">RFC2616</a>.
1265      *
1266      * @param name The name of the header being set
1267      * @param value The value of the header being set
1268      * @return {@code this}
1269      */
1270     public abstract HttpHeaders set(String name, Object value);
1271 
1272     /**
1273      * Sets a header with the specified name and values.
1274      *
1275      * If there is an existing header with the same name, it is removed.
1276      * This getMethod can be represented approximately as the following code:
1277      * <pre>
1278      * headers.remove(name);
1279      * for (Object v: values) {
1280      *     if (v == null) {
1281      *         break;
1282      *     }
1283      *     headers.add(name, v);
1284      * }
1285      * </pre>
1286      *
1287      * @param name The name of the headers being set
1288      * @param values The values of the headers being set
1289      * @return {@code this}
1290      */
1291     public abstract HttpHeaders set(String name, Iterable<?> values);
1292 
1293     /**
1294      * Cleans the current header entries and copies all header entries of the specified {@code headers}.
1295      *
1296      * @return {@code this}
1297      */
1298     public HttpHeaders set(HttpHeaders headers) {
1299         if (headers == null) {
1300             throw new NullPointerException("headers");
1301         }
1302         clear();
1303         for (Map.Entry<String, String> e: headers) {
1304             add(e.getKey(), e.getValue());
1305         }
1306         return this;
1307     }
1308 
1309     /**
1310      * Removes the header with the specified name.
1311      *
1312      * @param name The name of the header to remove
1313      * @return {@code this}
1314      */
1315     public abstract HttpHeaders remove(String name);
1316 
1317     /**
1318      * Removes all headers from this {@link HttpMessage}.
1319      *
1320      * @return {@code this}
1321      */
1322     public abstract HttpHeaders clear();
1323 
1324     /**
1325      * Returns {@code true} if a header with the name and value exists.
1326      *
1327      * @param name              the headername
1328      * @param value             the value
1329      * @param ignoreCaseValue   {@code true} if case should be ignored
1330      * @return contains         {@code true} if it contains it {@code false} otherwise
1331      */
1332     public boolean contains(String name, String value, boolean ignoreCaseValue) {
1333         List<String> values = getAll(name);
1334         if (values.isEmpty()) {
1335             return false;
1336         }
1337 
1338         for (String v: values) {
1339             if (ignoreCaseValue) {
1340                 if (v.equalsIgnoreCase(value)) {
1341                     return true;
1342                 }
1343             } else {
1344                 if (v.equals(value)) {
1345                     return true;
1346                 }
1347             }
1348         }
1349         return false;
1350     }
1351 }