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 org.jboss.netty.handler.codec.http;
17  
18  import org.jboss.netty.util.internal.CaseIgnoringComparator;
19  
20  import java.util.LinkedList;
21  import java.util.List;
22  import java.util.Map;
23  import java.util.Set;
24  import java.util.TreeSet;
25  
26  
27  /**
28   * Provides the constants for the standard HTTP header names and values and
29   * commonly used utility methods that accesses an {@link HttpMessage}.
30   * @apiviz.landmark
31   * @apiviz.stereotype static
32   */
33  public class HttpHeaders {
34  
35      /**
36       * Standard and CORS HTTP header names.
37       * For CORS headers, see
38       * https://developer.mozilla.org/en-US/docs/HTTP_access_control
39       *
40       * @apiviz.stereotype static
41       */
42      public static final class Names {
43          /**
44           * {@code "Accept"}
45           */
46          public static final String ACCEPT = "Accept";
47          /**
48           * {@code "Accept-Charset"}
49           */
50          public static final String ACCEPT_CHARSET = "Accept-Charset";
51          /**
52           * {@code "Accept-Encoding"}
53           */
54          public static final String ACCEPT_ENCODING = "Accept-Encoding";
55          /**
56           * {@code "Accept-Language"}
57           */
58          public static final String ACCEPT_LANGUAGE = "Accept-Language";
59          /**
60           * {@code "Accept-Ranges"}
61           */
62          public static final String ACCEPT_RANGES = "Accept-Ranges";
63          /**
64           * {@code "Accept-Patch"}
65           */
66          public static final String ACCEPT_PATCH = "Accept-Patch";
67          /**
68           * {@code "Access-Control-Allow-Credentials"}
69           */
70          public static final String ACCESS_CONTROL_ALLOW_CREDENTIALS = "Access-Control-Allow-Credentials";
71          /**
72           * {@code "Access-Control-Allow-Headers"}
73           */
74          public static final String ACCESS_CONTROL_ALLOW_HEADERS = "Access-Control-Allow-Headers";
75          /**
76           * {@code "Access-Control-Allow-Methods"}
77           */
78          public static final String ACCESS_CONTROL_ALLOW_METHODS = "Access-Control-Allow-Methods";
79          /**
80           * {@code "Access-Control-Allow-Origin"}
81           */
82          public static final String ACCESS_CONTROL_ALLOW_ORIGIN = "Access-Control-Allow-Origin";
83          /**
84           * {@code "Access-Control-Expose-Headers"}
85           */
86          public static final String ACCESS_CONTROL_EXPOSE_HEADERS = "Access-Control-Expose-Headers";
87          /**
88           * {@code "Access-Control-Max-Age"}
89           */
90          public static final String ACCESS_CONTROL_MAX_AGE = "Access-Control-Max-Age";
91          /**
92           * {@code "Access-Control-Request-Headers"}
93           */
94          public static final String ACCESS_CONTROL_REQUEST_HEADERS = "Access-Control-Request-Headers";
95          /**
96           * {@code "Access-Control-Request-Method"}
97           */
98          public static final String ACCESS_CONTROL_REQUEST_METHOD = "Access-Control-Request-Method";
99          /**
100          * {@code "Age"}
101          */
102         public static final String AGE = "Age";
103         /**
104          * {@code "Allow"}
105          */
106         public static final String ALLOW = "Allow";
107         /**
108          * {@code "Authorization"}
109          */
110         public static final String AUTHORIZATION = "Authorization";
111         /**
112          * {@code "Cache-Control"}
113          */
114         public static final String CACHE_CONTROL = "Cache-Control";
115         /**
116          * {@code "Connection"}
117          */
118         public static final String CONNECTION = "Connection";
119         /**
120          * {@code "Content-Base"}
121          */
122         public static final String CONTENT_BASE = "Content-Base";
123         /**
124          * {@code "Content-Encoding"}
125          */
126         public static final String CONTENT_ENCODING = "Content-Encoding";
127         /**
128          * {@code "Content-Language"}
129          */
130         public static final String CONTENT_LANGUAGE = "Content-Language";
131         /**
132          * {@code "Content-Length"}
133          */
134         public static final String CONTENT_LENGTH = "Content-Length";
135         /**
136          * {@code "Content-Location"}
137          */
138         public static final String CONTENT_LOCATION = "Content-Location";
139         /**
140          * {@code "Content-Transfer-Encoding"}
141          */
142         public static final String CONTENT_TRANSFER_ENCODING = "Content-Transfer-Encoding";
143         /**
144          * {@code "Content-MD5"}
145          */
146         public static final String CONTENT_MD5 = "Content-MD5";
147         /**
148          * {@code "Content-Range"}
149          */
150         public static final String CONTENT_RANGE = "Content-Range";
151         /**
152          * {@code "Content-Type"}
153          */
154         public static final String CONTENT_TYPE = "Content-Type";
155         /**
156          * {@code "Cookie"}
157          */
158         public static final String COOKIE = "Cookie";
159         /**
160          * {@code "Date"}
161          */
162         public static final String DATE = "Date";
163         /**
164          * {@code "ETag"}
165          */
166         public static final String ETAG = "ETag";
167         /**
168          * {@code "Expect"}
169          */
170         public static final String EXPECT = "Expect";
171         /**
172          * {@code "Expires"}
173          */
174         public static final String EXPIRES = "Expires";
175         /**
176          * {@code "From"}
177          */
178         public static final String FROM = "From";
179         /**
180          * {@code "Host"}
181          */
182         public static final String HOST = "Host";
183         /**
184          * {@code "If-Match"}
185          */
186         public static final String IF_MATCH = "If-Match";
187         /**
188          * {@code "If-Modified-Since"}
189          */
190         public static final String IF_MODIFIED_SINCE = "If-Modified-Since";
191         /**
192          * {@code "If-None-Match"}
193          */
194         public static final String IF_NONE_MATCH = "If-None-Match";
195         /**
196          * {@code "If-Range"}
197          */
198         public static final String IF_RANGE = "If-Range";
199         /**
200          * {@code "If-Unmodified-Since"}
201          */
202         public static final String IF_UNMODIFIED_SINCE = "If-Unmodified-Since";
203         /**
204          * {@code "Last-Modified"}
205          */
206         public static final String LAST_MODIFIED = "Last-Modified";
207         /**
208          * {@code "Location"}
209          */
210         public static final String LOCATION = "Location";
211         /**
212          * {@code "Max-Forwards"}
213          */
214         public static final String MAX_FORWARDS = "Max-Forwards";
215         /**
216          * {@code "Origin"}
217          */
218         public static final String ORIGIN = "Origin";
219         /**
220          * {@code "Pragma"}
221          */
222         public static final String PRAGMA = "Pragma";
223         /**
224          * {@code "Proxy-Authenticate"}
225          */
226         public static final String PROXY_AUTHENTICATE = "Proxy-Authenticate";
227         /**
228          * {@code "Proxy-Authorization"}
229          */
230         public static final String PROXY_AUTHORIZATION = "Proxy-Authorization";
231         /**
232          * {@code "Range"}
233          */
234         public static final String RANGE = "Range";
235         /**
236          * {@code "Referer"}
237          */
238         public static final String REFERER = "Referer";
239         /**
240          * {@code "Retry-After"}
241          */
242         public static final String RETRY_AFTER = "Retry-After";
243         /**
244          * {@code "Sec-WebSocket-Key1"}
245          */
246         public static final String SEC_WEBSOCKET_KEY1 = "Sec-WebSocket-Key1";
247         /**
248          * {@code "Sec-WebSocket-Key2"}
249          */
250         public static final String SEC_WEBSOCKET_KEY2 = "Sec-WebSocket-Key2";
251         /**
252          * {@code "Sec-WebSocket-Location"}
253          */
254         public static final String SEC_WEBSOCKET_LOCATION = "Sec-WebSocket-Location";
255         /**
256          * {@code "Sec-WebSocket-Origin"}
257          */
258         public static final String SEC_WEBSOCKET_ORIGIN = "Sec-WebSocket-Origin";
259         /**
260          * {@code "Sec-WebSocket-Protocol"}
261          */
262         public static final String SEC_WEBSOCKET_PROTOCOL = "Sec-WebSocket-Protocol";
263         /**
264          * {@code "Sec-WebSocket-Version"}
265          */
266         public static final String SEC_WEBSOCKET_VERSION = "Sec-WebSocket-Version";
267         /**
268          * {@code "Sec-WebSocket-Key"}
269          */
270         public static final String SEC_WEBSOCKET_KEY = "Sec-WebSocket-Key";
271         /**
272          * {@code "Sec-WebSocket-Accept"}
273          */
274         public static final String SEC_WEBSOCKET_ACCEPT = "Sec-WebSocket-Accept";
275         /**
276          * {@code "Server"}
277          */
278         public static final String SERVER = "Server";
279         /**
280          * {@code "Set-Cookie"}
281          */
282         public static final String SET_COOKIE = "Set-Cookie";
283         /**
284          * {@code "Set-Cookie2"}
285          */
286         public static final String SET_COOKIE2 = "Set-Cookie2";
287         /**
288          * {@code "TE"}
289          */
290         public static final String TE = "TE";
291         /**
292          * {@code "Trailer"}
293          */
294         public static final String TRAILER = "Trailer";
295         /**
296          * {@code "Transfer-Encoding"}
297          */
298         public static final String TRANSFER_ENCODING = "Transfer-Encoding";
299         /**
300          * {@code "Upgrade"}
301          */
302         public static final String UPGRADE = "Upgrade";
303         /**
304          * {@code "User-Agent"}
305          */
306         public static final String USER_AGENT = "User-Agent";
307         /**
308          * {@code "Vary"}
309          */
310         public static final String VARY = "Vary";
311         /**
312          * {@code "Via"}
313          */
314         public static final String VIA = "Via";
315         /**
316          * {@code "Warning"}
317          */
318         public static final String WARNING = "Warning";
319         /**
320          * {@code "WebSocket-Location"}
321          */
322         public static final String WEBSOCKET_LOCATION = "WebSocket-Location";
323         /**
324          * {@code "WebSocket-Origin"}
325          */
326         public static final String WEBSOCKET_ORIGIN = "WebSocket-Origin";
327         /**
328          * {@code "WebSocket-Protocol"}
329          */
330         public static final String WEBSOCKET_PROTOCOL = "WebSocket-Protocol";
331         /**
332          * {@code "WWW-Authenticate"}
333          */
334         public static final String WWW_AUTHENTICATE = "WWW-Authenticate";
335 
336         private Names() {
337         }
338     }
339 
340     /**
341      * Standard HTTP header values.
342      * @apiviz.stereotype static
343      */
344     public static final class Values {
345         /**
346          * {@code "application/x-www-form-urlencoded"}
347          */
348         public static final String APPLICATION_X_WWW_FORM_URLENCODED = "application/x-www-form-urlencoded";
349         /**
350          * {@code "base64"}
351          */
352         public static final String BASE64 = "base64";
353         /**
354          * {@code "binary"}
355          */
356         public static final String BINARY = "binary";
357         /**
358          * {@code "boundary"}
359          */
360         public static final String BOUNDARY = "boundary";
361         /**
362          * {@code "bytes"}
363          */
364         public static final String BYTES = "bytes";
365         /**
366          * {@code "charset"}
367          */
368         public static final String CHARSET = "charset";
369         /**
370          * {@code "chunked"}
371          */
372         public static final String CHUNKED = "chunked";
373         /**
374          * {@code "close"}
375          */
376         public static final String CLOSE = "close";
377         /**
378          * {@code "compress"}
379          */
380         public static final String COMPRESS = "compress";
381         /**
382          * {@code "100-continue"}
383          */
384         public static final String CONTINUE =  "100-continue";
385         /**
386          * {@code "deflate"}
387          */
388         public static final String DEFLATE = "deflate";
389         /**
390          * {@code "gzip"}
391          */
392         public static final String GZIP = "gzip";
393         /**
394          * {@code "identity"}
395          */
396         public static final String IDENTITY = "identity";
397         /**
398          * {@code "keep-alive"}
399          */
400         public static final String KEEP_ALIVE = "keep-alive";
401         /**
402          * {@code "max-age"}
403          */
404         public static final String MAX_AGE = "max-age";
405         /**
406          * {@code "max-stale"}
407          */
408         public static final String MAX_STALE = "max-stale";
409         /**
410          * {@code "min-fresh"}
411          */
412         public static final String MIN_FRESH = "min-fresh";
413         /**
414          * {@code "multipart/form-data"}
415          */
416         public static final String MULTIPART_FORM_DATA = "multipart/form-data";
417         /**
418          * {@code "must-revalidate"}
419          */
420         public static final String MUST_REVALIDATE = "must-revalidate";
421         /**
422          * {@code "no-cache"}
423          */
424         public static final String NO_CACHE = "no-cache";
425         /**
426          * {@code "no-store"}
427          */
428         public static final String NO_STORE = "no-store";
429         /**
430          * {@code "no-transform"}
431          */
432         public static final String NO_TRANSFORM = "no-transform";
433         /**
434          * {@code "none"}
435          */
436         public static final String NONE = "none";
437         /**
438          * {@code "only-if-cached"}
439          */
440         public static final String ONLY_IF_CACHED = "only-if-cached";
441         /**
442          * {@code "private"}
443          */
444         public static final String PRIVATE = "private";
445         /**
446          * {@code "proxy-revalidate"}
447          */
448         public static final String PROXY_REVALIDATE = "proxy-revalidate";
449         /**
450          * {@code "public"}
451          */
452         public static final String PUBLIC = "public";
453         /**
454          * {@code "quoted-printable"}
455          */
456         public static final String QUOTED_PRINTABLE = "quoted-printable";
457         /**
458          * {@code "s-maxage"}
459          */
460         public static final String S_MAXAGE = "s-maxage";
461         /**
462          * {@code "trailers"}
463          */
464         public static final String TRAILERS = "trailers";
465         /**
466          * {@code "Upgrade"}
467          */
468         public static final String UPGRADE = "Upgrade";
469         /**
470          * {@code "WebSocket"}
471          */
472         public static final String WEBSOCKET = "WebSocket";
473 
474         private Values() {
475         }
476     }
477 
478     /**
479      * Returns {@code true} if and only if the connection can remain open and
480      * thus 'kept alive'.  This methods respects the value of the
481      * {@code "Connection"} header first and then the return value of
482      * {@link HttpVersion#isKeepAliveDefault()}.
483      */
484     public static boolean isKeepAlive(HttpMessage message) {
485         String connection = message.getHeader(Names.CONNECTION);
486         if (Values.CLOSE.equalsIgnoreCase(connection)) {
487             return false;
488         }
489 
490         if (message.getProtocolVersion().isKeepAliveDefault()) {
491             return !Values.CLOSE.equalsIgnoreCase(connection);
492         } else {
493             return Values.KEEP_ALIVE.equalsIgnoreCase(connection);
494         }
495     }
496 
497     /**
498      * Sets the value of the {@code "Connection"} header depending on the
499      * protocol version of the specified message.  This method sets or removes
500      * the {@code "Connection"} header depending on what the default keep alive
501      * mode of the message's protocol version is, as specified by
502      * {@link HttpVersion#isKeepAliveDefault()}.
503      * <ul>
504      * <li>If the connection is kept alive by default:
505      *     <ul>
506      *     <li>set to {@code "close"} if {@code keepAlive} is {@code false}.</li>
507      *     <li>remove otherwise.</li>
508      *     </ul></li>
509      * <li>If the connection is closed by default:
510      *     <ul>
511      *     <li>set to {@code "keep-alive"} if {@code keepAlive} is {@code true}.</li>
512      *     <li>remove otherwise.</li>
513      *     </ul></li>
514      * </ul>
515      */
516     public static void setKeepAlive(HttpMessage message, boolean keepAlive) {
517         if (message.getProtocolVersion().isKeepAliveDefault()) {
518             if (keepAlive) {
519                 message.removeHeader(Names.CONNECTION);
520             } else {
521                 message.setHeader(Names.CONNECTION, Values.CLOSE);
522             }
523         } else {
524             if (keepAlive) {
525                 message.setHeader(Names.CONNECTION, Values.KEEP_ALIVE);
526             } else {
527                 message.removeHeader(Names.CONNECTION);
528             }
529         }
530     }
531 
532     /**
533      * Returns the header value with the specified header name.  If there are
534      * more than one header value for the specified header name, the first
535      * value is returned.
536      *
537      * @return the header value or {@code null} if there is no such header
538      */
539     public static String getHeader(HttpMessage message, String name) {
540         return message.getHeader(name);
541     }
542 
543     /**
544      * Returns the header value with the specified header name.  If there are
545      * more than one header value for the specified header name, the first
546      * value is returned.
547      *
548      * @return the header value or the {@code defaultValue} if there is no such
549      *         header
550      */
551     public static String getHeader(HttpMessage message, String name, String defaultValue) {
552         String value = message.getHeader(name);
553         if (value == null) {
554             return defaultValue;
555         }
556         return value;
557     }
558 
559     /**
560      * Sets a new header with the specified name and value.  If there is an
561      * existing header with the same name, the existing header is removed.
562      */
563     public static void setHeader(HttpMessage message, String name, Object value) {
564         message.setHeader(name, value);
565     }
566 
567     /**
568      * Sets a new header with the specified name and values.  If there is an
569      * existing header with the same name, the existing header is removed.
570      */
571     public static void setHeader(HttpMessage message, String name, Iterable<?> values) {
572         message.setHeader(name, values);
573     }
574 
575     /**
576      * Adds a new header with the specified name and value.
577      */
578     public static void addHeader(HttpMessage message, String name, Object value) {
579         message.addHeader(name, value);
580     }
581 
582     /**
583      * Returns the integer header value with the specified header name.  If
584      * there are more than one header value for the specified header name, the
585      * first value is returned.
586      *
587      * @return the header value
588      * @throws NumberFormatException
589      *         if there is no such header or the header value is not a number
590      */
591     public static int getIntHeader(HttpMessage message, String name) {
592         String value = getHeader(message, name);
593         if (value == null) {
594             throw new NumberFormatException("null");
595         }
596         return Integer.parseInt(value);
597     }
598 
599     /**
600      * Returns the integer header value with the specified header name.  If
601      * there are more than one header value for the specified header name, the
602      * first value is returned.
603      *
604      * @return the header value or the {@code defaultValue} if there is no such
605      *         header or the header value is not a number
606      */
607     public static int getIntHeader(HttpMessage message, String name, int defaultValue) {
608         String value = getHeader(message, name);
609         if (value == null) {
610             return defaultValue;
611         }
612 
613         try {
614             return Integer.parseInt(value);
615         } catch (NumberFormatException e) {
616             return defaultValue;
617         }
618     }
619 
620     /**
621      * Sets a new integer header with the specified name and value.  If there
622      * is an existing header with the same name, the existing header is removed.
623      */
624     public static void setIntHeader(HttpMessage message, String name, int value) {
625         message.setHeader(name, value);
626     }
627 
628     /**
629      * Sets a new integer header with the specified name and values.  If there
630      * is an existing header with the same name, the existing header is removed.
631      */
632     public static void setIntHeader(HttpMessage message, String name, Iterable<Integer> values) {
633         message.setHeader(name, values);
634     }
635 
636     /**
637      * Adds a new integer header with the specified name and value.
638      */
639     public static void addIntHeader(HttpMessage message, String name, int value) {
640         message.addHeader(name, value);
641     }
642 
643     /**
644      * Returns the length of the content.  Please note that this value is
645      * not retrieved from {@link HttpMessage#getContent()} but from the
646      * {@code "Content-Length"} header, and thus they are independent from each
647      * other.
648      *
649      * @return the content length or {@code 0} if this message does not have
650      *         the {@code "Content-Length"} header
651      */
652     public static long getContentLength(HttpMessage message) {
653         return getContentLength(message, 0L);
654     }
655 
656     /**
657      * Returns the length of the content.  Please note that this value is
658      * not retrieved from {@link HttpMessage#getContent()} but from the
659      * {@code "Content-Length"} header, and thus they are independent from each
660      * other.
661      *
662      * @return the content length or {@code defaultValue} if this message does
663      *         not have the {@code "Content-Length"} header
664      */
665     public static long getContentLength(HttpMessage message, long defaultValue) {
666         String contentLength = message.getHeader(Names.CONTENT_LENGTH);
667         if (contentLength != null) {
668             return Long.parseLong(contentLength);
669         }
670 
671         // WebSockset messages have constant content-lengths.
672         if (message instanceof HttpRequest) {
673             HttpRequest req = (HttpRequest) message;
674             if (HttpMethod.GET.equals(req.getMethod()) &&
675                 req.containsHeader(Names.SEC_WEBSOCKET_KEY1) &&
676                 req.containsHeader(Names.SEC_WEBSOCKET_KEY2)) {
677                 return 8;
678             }
679         } else if (message instanceof HttpResponse) {
680             HttpResponse res = (HttpResponse) message;
681             if (res.getStatus().getCode() == 101 &&
682                 res.containsHeader(Names.SEC_WEBSOCKET_ORIGIN) &&
683                 res.containsHeader(Names.SEC_WEBSOCKET_LOCATION)) {
684                 return 16;
685             }
686         }
687 
688         return defaultValue;
689     }
690 
691     /**
692      * Sets the {@code "Content-Length"} header.
693      */
694     public static void setContentLength(HttpMessage message, long length) {
695         message.setHeader(Names.CONTENT_LENGTH, length);
696     }
697 
698     /**
699      * Returns the value of the {@code "Host"} header.
700      */
701     public static String getHost(HttpMessage message) {
702         return message.getHeader(Names.HOST);
703     }
704 
705     /**
706      * Returns the value of the {@code "Host"} header.  If there is no such
707      * header, the {@code defaultValue} is returned.
708      */
709     public static String getHost(HttpMessage message, String defaultValue) {
710         return getHeader(message, Names.HOST, defaultValue);
711     }
712 
713     /**
714      * Sets the {@code "Host"} header.
715      */
716     public static void setHost(HttpMessage message, String value) {
717         message.setHeader(Names.HOST, value);
718     }
719 
720     /**
721      * Returns {@code true} if and only if the specified message contains the
722      * {@code "Expect: 100-continue"} header.
723      */
724     public static boolean is100ContinueExpected(HttpMessage message) {
725         // Expect: 100-continue is for requests only.
726         if (!(message instanceof HttpRequest)) {
727             return false;
728         }
729 
730         // It works only on HTTP/1.1 or later.
731         if (message.getProtocolVersion().compareTo(HttpVersion.HTTP_1_1) < 0) {
732             return false;
733         }
734 
735         // In most cases, there will be one or zero 'Expect' header.
736         String value = message.getHeader(Names.EXPECT);
737         if (value == null) {
738             return false;
739         }
740         if (Values.CONTINUE.equalsIgnoreCase(value)) {
741             return true;
742         }
743 
744         // Multiple 'Expect' headers.  Search through them.
745         for (String v: message.getHeaders(Names.EXPECT)) {
746             if (Values.CONTINUE.equalsIgnoreCase(v)) {
747                 return true;
748             }
749         }
750         return false;
751     }
752 
753     /**
754      * Sets the {@code "Expect: 100-continue"} header to the specified message.
755      * If there is any existing {@code "Expect"} header, they are replaced with
756      * the new one.
757      */
758     public static void set100ContinueExpected(HttpMessage message) {
759         set100ContinueExpected(message, true);
760     }
761 
762     /**
763      * Sets or removes the {@code "Expect: 100-continue"} header to / from the
764      * specified message.  If the specified {@code value} is {@code true},
765      * the {@code "Expect: 100-continue"} header is set and all other previous
766      * {@code "Expect"} headers are removed.  Otherwise, all {@code "Expect"}
767      * headers are removed completely.
768      */
769     public static void set100ContinueExpected(HttpMessage message, boolean set) {
770         if (set) {
771             message.setHeader(Names.EXPECT, Values.CONTINUE);
772         } else {
773             message.removeHeader(Names.EXPECT);
774         }
775     }
776 
777     private static final int BUCKET_SIZE = 17;
778 
779     private static int hash(String name) {
780         int h = 0;
781         for (int i = name.length() - 1; i >= 0; i --) {
782             char c = name.charAt(i);
783             if (c >= 'A' && c <= 'Z') {
784                 c += 32;
785             }
786             h = 31 * h + c;
787         }
788 
789         if (h > 0) {
790             return h;
791         } else if (h == Integer.MIN_VALUE) {
792             return Integer.MAX_VALUE;
793         } else {
794             return -h;
795         }
796     }
797 
798     private static boolean eq(String name1, String name2) {
799         int nameLen = name1.length();
800         if (nameLen != name2.length()) {
801             return false;
802         }
803 
804         for (int i = nameLen - 1; i >= 0; i --) {
805             char c1 = name1.charAt(i);
806             char c2 = name2.charAt(i);
807             if (c1 != c2) {
808                 if (c1 >= 'A' && c1 <= 'Z') {
809                     c1 += 32;
810                 }
811                 if (c2 >= 'A' && c2 <= 'Z') {
812                     c2 += 32;
813                 }
814                 if (c1 != c2) {
815                     return false;
816                 }
817             }
818         }
819         return true;
820     }
821 
822     private static int index(int hash) {
823         return hash % BUCKET_SIZE;
824     }
825 
826     private final HeaderEntry[] entries = new HeaderEntry[BUCKET_SIZE];
827     private final HeaderEntry head = new HeaderEntry(-1, null, null);
828 
829     HttpHeaders() {
830         head.before = head.after = head;
831     }
832 
833     void validateHeaderName(String name) {
834         HttpCodecUtil.validateHeaderName(name);
835     }
836 
837     void addHeader(final String name, final Object value) {
838         validateHeaderName(name);
839         String strVal = toString(value);
840         HttpCodecUtil.validateHeaderValue(strVal);
841         int h = hash(name);
842         int i = index(h);
843         addHeader0(h, i, name, strVal);
844     }
845 
846     private void addHeader0(int h, int i, final String name, final String value) {
847         // Update the hash table.
848         HeaderEntry e = entries[i];
849         HeaderEntry newEntry;
850         entries[i] = newEntry = new HeaderEntry(h, name, value);
851         newEntry.next = e;
852 
853         // Update the linked list.
854         newEntry.addBefore(head);
855     }
856 
857     void removeHeader(final String name) {
858         if (name == null) {
859             throw new NullPointerException("name");
860         }
861         int h = hash(name);
862         int i = index(h);
863         removeHeader0(h, i, name);
864     }
865 
866     private void removeHeader0(int h, int i, String name) {
867         HeaderEntry e = entries[i];
868         if (e == null) {
869             return;
870         }
871 
872         for (;;) {
873             if (e.hash == h && eq(name, e.key)) {
874                 e.remove();
875                 HeaderEntry next = e.next;
876                 if (next != null) {
877                     entries[i] = next;
878                     e = next;
879                 } else {
880                     entries[i] = null;
881                     return;
882                 }
883             } else {
884                 break;
885             }
886         }
887 
888         for (;;) {
889             HeaderEntry next = e.next;
890             if (next == null) {
891                 break;
892             }
893             if (next.hash == h && eq(name, next.key)) {
894                 e.next = next.next;
895                 next.remove();
896             } else {
897                 e = next;
898             }
899         }
900     }
901 
902     void setHeader(final String name, final Object value) {
903         validateHeaderName(name);
904         String strVal = toString(value);
905         HttpCodecUtil.validateHeaderValue(strVal);
906         int h = hash(name);
907         int i = index(h);
908         removeHeader0(h, i, name);
909         addHeader0(h, i, name, strVal);
910     }
911 
912     void setHeader(final String name, final Iterable<?> values) {
913         if (values == null) {
914             throw new NullPointerException("values");
915         }
916 
917         validateHeaderName(name);
918 
919         int h = hash(name);
920         int i = index(h);
921 
922         removeHeader0(h, i, name);
923         for (Object v: values) {
924             if (v == null) {
925                 break;
926             }
927             String strVal = toString(v);
928             HttpCodecUtil.validateHeaderValue(strVal);
929             addHeader0(h, i, name, strVal);
930         }
931     }
932 
933     void clearHeaders() {
934         for (int i = 0; i < entries.length; i ++) {
935             entries[i] = null;
936         }
937         head.before = head.after = head;
938     }
939 
940     String getHeader(final String name) {
941         if (name == null) {
942             throw new NullPointerException("name");
943         }
944 
945         int h = hash(name);
946         int i = index(h);
947         HeaderEntry e = entries[i];
948         while (e != null) {
949             if (e.hash == h && eq(name, e.key)) {
950                 return e.value;
951             }
952 
953             e = e.next;
954         }
955         return null;
956     }
957 
958     List<String> getHeaders(final String name) {
959         if (name == null) {
960             throw new NullPointerException("name");
961         }
962 
963         LinkedList<String> values = new LinkedList<String>();
964 
965         int h = hash(name);
966         int i = index(h);
967         HeaderEntry e = entries[i];
968         while (e != null) {
969             if (e.hash == h && eq(name, e.key)) {
970                 values.addFirst(e.value);
971             }
972             e = e.next;
973         }
974         return values;
975     }
976 
977     List<Map.Entry<String, String>> getHeaders() {
978         List<Map.Entry<String, String>> all =
979             new LinkedList<Map.Entry<String, String>>();
980 
981         HeaderEntry e = head.after;
982         while (e != head) {
983             all.add(e);
984             e = e.after;
985         }
986         return all;
987     }
988 
989     boolean containsHeader(String name) {
990         return getHeader(name) != null;
991     }
992 
993     Set<String> getHeaderNames() {
994         Set<String> names =
995             new TreeSet<String>(CaseIgnoringComparator.INSTANCE);
996 
997         HeaderEntry e = head.after;
998         while (e != head) {
999             names.add(e.key);
1000             e = e.after;
1001         }
1002         return names;
1003     }
1004 
1005     private static String toString(Object value) {
1006         if (value == null) {
1007             return null;
1008         }
1009         return value.toString();
1010     }
1011 
1012     private static final class HeaderEntry implements Map.Entry<String, String> {
1013         final int hash;
1014         final String key;
1015         String value;
1016         HeaderEntry next;
1017         HeaderEntry before, after;
1018 
1019         HeaderEntry(int hash, String key, String value) {
1020             this.hash = hash;
1021             this.key = key;
1022             this.value = value;
1023         }
1024 
1025         void remove() {
1026             before.after = after;
1027             after.before = before;
1028         }
1029 
1030         void addBefore(HeaderEntry e) {
1031             after  = e;
1032             before = e.before;
1033             before.after = this;
1034             after.before = this;
1035         }
1036 
1037         public String getKey() {
1038             return key;
1039         }
1040 
1041         public String getValue() {
1042             return value;
1043         }
1044 
1045         public String setValue(String value) {
1046             if (value == null) {
1047                 throw new NullPointerException("value");
1048             }
1049             HttpCodecUtil.validateHeaderValue(value);
1050             String oldValue = this.value;
1051             this.value = value;
1052             return oldValue;
1053         }
1054 
1055         @Override
1056         public String toString() {
1057             return key + '=' + value;
1058         }
1059     }
1060 }