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     /**
480      * Returns {@code true} if and only if the connection can remain open and
481      * thus 'kept alive'.  This methods respects the value of the
482      * {@code "Connection"} header first and then the return value of
483      * {@link HttpVersion#isKeepAliveDefault()}.
484      */
485     public static boolean isKeepAlive(HttpMessage message) {
486         String connection = message.getHeader(Names.CONNECTION);
487         if (Values.CLOSE.equalsIgnoreCase(connection)) {
488             return false;
489         }
490 
491         if (message.getProtocolVersion().isKeepAliveDefault()) {
492             return !Values.CLOSE.equalsIgnoreCase(connection);
493         } else {
494             return Values.KEEP_ALIVE.equalsIgnoreCase(connection);
495         }
496     }
497 
498     /**
499      * Sets the value of the {@code "Connection"} header depending on the
500      * protocol version of the specified message.  This method sets or removes
501      * the {@code "Connection"} header depending on what the default keep alive
502      * mode of the message's protocol version is, as specified by
503      * {@link HttpVersion#isKeepAliveDefault()}.
504      * <ul>
505      * <li>If the connection is kept alive by default:
506      *     <ul>
507      *     <li>set to {@code "close"} if {@code keepAlive} is {@code false}.</li>
508      *     <li>remove otherwise.</li>
509      *     </ul></li>
510      * <li>If the connection is closed by default:
511      *     <ul>
512      *     <li>set to {@code "keep-alive"} if {@code keepAlive} is {@code true}.</li>
513      *     <li>remove otherwise.</li>
514      *     </ul></li>
515      * </ul>
516      */
517     public static void setKeepAlive(HttpMessage message, boolean keepAlive) {
518         if (message.getProtocolVersion().isKeepAliveDefault()) {
519             if (keepAlive) {
520                 message.removeHeader(Names.CONNECTION);
521             } else {
522                 message.setHeader(Names.CONNECTION, Values.CLOSE);
523             }
524         } else {
525             if (keepAlive) {
526                 message.setHeader(Names.CONNECTION, Values.KEEP_ALIVE);
527             } else {
528                 message.removeHeader(Names.CONNECTION);
529             }
530         }
531     }
532 
533     /**
534      * Returns the header value with the specified header name.  If there are
535      * more than one header value for the specified header name, the first
536      * value is returned.
537      *
538      * @return the header value or {@code null} if there is no such header
539      */
540     public static String getHeader(HttpMessage message, String name) {
541         return message.getHeader(name);
542     }
543 
544     /**
545      * Returns the header value with the specified header name.  If there are
546      * more than one header value for the specified header name, the first
547      * value is returned.
548      *
549      * @return the header value or the {@code defaultValue} if there is no such
550      *         header
551      */
552     public static String getHeader(HttpMessage message, String name, String defaultValue) {
553         String value = message.getHeader(name);
554         if (value == null) {
555             return defaultValue;
556         }
557         return value;
558     }
559 
560     /**
561      * Sets a new header with the specified name and value.  If there is an
562      * existing header with the same name, the existing header is removed.
563      */
564     public static void setHeader(HttpMessage message, String name, Object value) {
565         message.setHeader(name, value);
566     }
567 
568     /**
569      * Sets a new header with the specified name and values.  If there is an
570      * existing header with the same name, the existing header is removed.
571      */
572     public static void setHeader(HttpMessage message, String name, Iterable<?> values) {
573         message.setHeader(name, values);
574     }
575 
576     /**
577      * Adds a new header with the specified name and value.
578      */
579     public static void addHeader(HttpMessage message, String name, Object value) {
580         message.addHeader(name, value);
581     }
582 
583     /**
584      * Returns the integer header value with the specified header name.  If
585      * there are more than one header value for the specified header name, the
586      * first value is returned.
587      *
588      * @return the header value
589      * @throws NumberFormatException
590      *         if there is no such header or the header value is not a number
591      */
592     public static int getIntHeader(HttpMessage message, String name) {
593         String value = getHeader(message, name);
594         if (value == null) {
595             throw new NumberFormatException("null");
596         }
597         return Integer.parseInt(value);
598     }
599 
600     /**
601      * Returns the integer header value with the specified header name.  If
602      * there are more than one header value for the specified header name, the
603      * first value is returned.
604      *
605      * @return the header value or the {@code defaultValue} if there is no such
606      *         header or the header value is not a number
607      */
608     public static int getIntHeader(HttpMessage message, String name, int defaultValue) {
609         String value = getHeader(message, name);
610         if (value == null) {
611             return defaultValue;
612         }
613 
614         try {
615             return Integer.parseInt(value);
616         } catch (NumberFormatException e) {
617             return defaultValue;
618         }
619     }
620 
621     /**
622      * Sets a new integer header with the specified name and value.  If there
623      * is an existing header with the same name, the existing header is removed.
624      */
625     public static void setIntHeader(HttpMessage message, String name, int value) {
626         message.setHeader(name, value);
627     }
628 
629     /**
630      * Sets a new integer header with the specified name and values.  If there
631      * is an existing header with the same name, the existing header is removed.
632      */
633     public static void setIntHeader(HttpMessage message, String name, Iterable<Integer> values) {
634         message.setHeader(name, values);
635     }
636 
637     /**
638      * Adds a new integer header with the specified name and value.
639      */
640     public static void addIntHeader(HttpMessage message, String name, int value) {
641         message.addHeader(name, value);
642     }
643 
644     /**
645      * Returns the length of the content.  Please note that this value is
646      * not retrieved from {@link HttpMessage#getContent()} but from the
647      * {@code "Content-Length"} header, and thus they are independent from each
648      * other.
649      *
650      * @return the content length or {@code 0} if this message does not have
651      *         the {@code "Content-Length"} header
652      */
653     public static long getContentLength(HttpMessage message) {
654         return getContentLength(message, 0L);
655     }
656 
657     /**
658      * Returns the length of the content.  Please note that this value is
659      * not retrieved from {@link HttpMessage#getContent()} but from the
660      * {@code "Content-Length"} header, and thus they are independent from each
661      * other.
662      *
663      * @return the content length or {@code defaultValue} if this message does
664      *         not have the {@code "Content-Length"} header
665      */
666     public static long getContentLength(HttpMessage message, long defaultValue) {
667         String contentLength = message.getHeader(Names.CONTENT_LENGTH);
668         if (contentLength != null) {
669             return Long.parseLong(contentLength);
670         }
671 
672         // WebSockset messages have constant content-lengths.
673         if (message instanceof HttpRequest) {
674             HttpRequest req = (HttpRequest) message;
675             if (HttpMethod.GET.equals(req.getMethod()) &&
676                 req.containsHeader(Names.SEC_WEBSOCKET_KEY1) &&
677                 req.containsHeader(Names.SEC_WEBSOCKET_KEY2)) {
678                 return 8;
679             }
680         } else if (message instanceof HttpResponse) {
681             HttpResponse res = (HttpResponse) message;
682             if (res.getStatus().getCode() == 101 &&
683                 res.containsHeader(Names.SEC_WEBSOCKET_ORIGIN) &&
684                 res.containsHeader(Names.SEC_WEBSOCKET_LOCATION)) {
685                 return 16;
686             }
687         }
688 
689         return defaultValue;
690     }
691 
692     /**
693      * Sets the {@code "Content-Length"} header.
694      */
695     public static void setContentLength(HttpMessage message, long length) {
696         message.setHeader(Names.CONTENT_LENGTH, length);
697     }
698 
699     /**
700      * Returns the value of the {@code "Host"} header.
701      */
702     public static String getHost(HttpMessage message) {
703         return message.getHeader(Names.HOST);
704     }
705 
706     /**
707      * Returns the value of the {@code "Host"} header.  If there is no such
708      * header, the {@code defaultValue} is returned.
709      */
710     public static String getHost(HttpMessage message, String defaultValue) {
711         return getHeader(message, Names.HOST, defaultValue);
712     }
713 
714     /**
715      * Sets the {@code "Host"} header.
716      */
717     public static void setHost(HttpMessage message, String value) {
718         message.setHeader(Names.HOST, value);
719     }
720 
721     /**
722      * Returns {@code true} if and only if the specified message contains the
723      * {@code "Expect: 100-continue"} header.
724      */
725     public static boolean is100ContinueExpected(HttpMessage message) {
726         // Expect: 100-continue is for requests only.
727         if (!(message instanceof HttpRequest)) {
728             return false;
729         }
730 
731         // It works only on HTTP/1.1 or later.
732         if (message.getProtocolVersion().compareTo(HttpVersion.HTTP_1_1) < 0) {
733             return false;
734         }
735 
736         // In most cases, there will be one or zero 'Expect' header.
737         String value = message.getHeader(Names.EXPECT);
738         if (value == null) {
739             return false;
740         }
741         if (Values.CONTINUE.equalsIgnoreCase(value)) {
742             return true;
743         }
744 
745         // Multiple 'Expect' headers.  Search through them.
746         for (String v: message.getHeaders(Names.EXPECT)) {
747             if (Values.CONTINUE.equalsIgnoreCase(v)) {
748                 return true;
749             }
750         }
751         return false;
752     }
753 
754     /**
755      * Sets the {@code "Expect: 100-continue"} header to the specified message.
756      * If there is any existing {@code "Expect"} header, they are replaced with
757      * the new one.
758      */
759     public static void set100ContinueExpected(HttpMessage message) {
760         set100ContinueExpected(message, true);
761     }
762 
763     /**
764      * Sets or removes the {@code "Expect: 100-continue"} header to / from the
765      * specified message.  If the specified {@code value} is {@code true},
766      * the {@code "Expect: 100-continue"} header is set and all other previous
767      * {@code "Expect"} headers are removed.  Otherwise, all {@code "Expect"}
768      * headers are removed completely.
769      */
770     public static void set100ContinueExpected(HttpMessage message, boolean set) {
771         if (set) {
772             message.setHeader(Names.EXPECT, Values.CONTINUE);
773         } else {
774             message.removeHeader(Names.EXPECT);
775         }
776     }
777 
778     private static final int BUCKET_SIZE = 17;
779 
780     private static int hash(String name) {
781         int h = 0;
782         for (int i = name.length() - 1; i >= 0; i --) {
783             char c = name.charAt(i);
784             if (c >= 'A' && c <= 'Z') {
785                 c += 32;
786             }
787             h = 31 * h + c;
788         }
789 
790         if (h > 0) {
791             return h;
792         } else if (h == Integer.MIN_VALUE) {
793             return Integer.MAX_VALUE;
794         } else {
795             return -h;
796         }
797     }
798 
799     private static boolean eq(String name1, String name2) {
800         int nameLen = name1.length();
801         if (nameLen != name2.length()) {
802             return false;
803         }
804 
805         for (int i = nameLen - 1; i >= 0; i --) {
806             char c1 = name1.charAt(i);
807             char c2 = name2.charAt(i);
808             if (c1 != c2) {
809                 if (c1 >= 'A' && c1 <= 'Z') {
810                     c1 += 32;
811                 }
812                 if (c2 >= 'A' && c2 <= 'Z') {
813                     c2 += 32;
814                 }
815                 if (c1 != c2) {
816                     return false;
817                 }
818             }
819         }
820         return true;
821     }
822 
823     private static int index(int hash) {
824         return hash % BUCKET_SIZE;
825     }
826 
827     private final Entry[] entries = new Entry[BUCKET_SIZE];
828     private final Entry head = new Entry(-1, null, null);
829 
830     HttpHeaders() {
831         head.before = head.after = head;
832     }
833 
834     void validateHeaderName(String name) {
835         HttpCodecUtil.validateHeaderName(name);
836     }
837 
838     void addHeader(final String name, final Object value) {
839         validateHeaderName(name);
840         String strVal = toString(value);
841         HttpCodecUtil.validateHeaderValue(strVal);
842         int h = hash(name);
843         int i = index(h);
844         addHeader0(h, i, name, strVal);
845     }
846 
847     private void addHeader0(int h, int i, final String name, final String value) {
848         // Update the hash table.
849         Entry e = entries[i];
850         Entry newEntry;
851         entries[i] = newEntry = new Entry(h, name, value);
852         newEntry.next = e;
853 
854         // Update the linked list.
855         newEntry.addBefore(head);
856     }
857 
858     void removeHeader(final String name) {
859         if (name == null) {
860             throw new NullPointerException("name");
861         }
862         int h = hash(name);
863         int i = index(h);
864         removeHeader0(h, i, name);
865     }
866 
867     private void removeHeader0(int h, int i, String name) {
868         Entry e = entries[i];
869         if (e == null) {
870             return;
871         }
872 
873         for (;;) {
874             if (e.hash == h && eq(name, e.key)) {
875                 e.remove();
876                 Entry next = e.next;
877                 if (next != null) {
878                     entries[i] = next;
879                     e = next;
880                 } else {
881                     entries[i] = null;
882                     return;
883                 }
884             } else {
885                 break;
886             }
887         }
888 
889         for (;;) {
890             Entry next = e.next;
891             if (next == null) {
892                 break;
893             }
894             if (next.hash == h && eq(name, next.key)) {
895                 e.next = next.next;
896                 next.remove();
897             } else {
898                 e = next;
899             }
900         }
901     }
902 
903     void setHeader(final String name, final Object value) {
904         validateHeaderName(name);
905         String strVal = toString(value);
906         HttpCodecUtil.validateHeaderValue(strVal);
907         int h = hash(name);
908         int i = index(h);
909         removeHeader0(h, i, name);
910         addHeader0(h, i, name, strVal);
911     }
912 
913     void setHeader(final String name, final Iterable<?> values) {
914         if (values == null) {
915             throw new NullPointerException("values");
916         }
917 
918         validateHeaderName(name);
919 
920         int h = hash(name);
921         int i = index(h);
922 
923         removeHeader0(h, i, name);
924         for (Object v: values) {
925             if (v == null) {
926                 break;
927             }
928             String strVal = toString(v);
929             HttpCodecUtil.validateHeaderValue(strVal);
930             addHeader0(h, i, name, strVal);
931         }
932     }
933 
934     void clearHeaders() {
935         for (int i = 0; i < entries.length; i ++) {
936             entries[i] = null;
937         }
938         head.before = head.after = head;
939     }
940 
941     String getHeader(final String name) {
942         if (name == null) {
943             throw new NullPointerException("name");
944         }
945 
946         int h = hash(name);
947         int i = index(h);
948         Entry e = entries[i];
949         while (e != null) {
950             if (e.hash == h && eq(name, e.key)) {
951                 return e.value;
952             }
953 
954             e = e.next;
955         }
956         return null;
957     }
958 
959     List<String> getHeaders(final String name) {
960         if (name == null) {
961             throw new NullPointerException("name");
962         }
963 
964         LinkedList<String> values = new LinkedList<String>();
965 
966         int h = hash(name);
967         int i = index(h);
968         Entry e = entries[i];
969         while (e != null) {
970             if (e.hash == h && eq(name, e.key)) {
971                 values.addFirst(e.value);
972             }
973             e = e.next;
974         }
975         return values;
976     }
977 
978     List<Map.Entry<String, String>> getHeaders() {
979         List<Map.Entry<String, String>> all =
980             new LinkedList<Map.Entry<String, String>>();
981 
982         Entry e = head.after;
983         while (e != head) {
984             all.add(e);
985             e = e.after;
986         }
987         return all;
988     }
989 
990     boolean containsHeader(String name) {
991         return getHeader(name) != null;
992     }
993 
994     Set<String> getHeaderNames() {
995         Set<String> names =
996             new TreeSet<String>(CaseIgnoringComparator.INSTANCE);
997 
998         Entry e = head.after;
999         while (e != head) {
1000             names.add(e.key);
1001             e = e.after;
1002         }
1003         return names;
1004     }
1005 
1006     private static String toString(Object value) {
1007         if (value == null) {
1008             return null;
1009         }
1010         return value.toString();
1011     }
1012 
1013     private static final class Entry implements Map.Entry<String, String> {
1014         final int hash;
1015         final String key;
1016         String value;
1017         Entry next;
1018         Entry before, after;
1019 
1020         Entry(int hash, String key, String value) {
1021             this.hash = hash;
1022             this.key = key;
1023             this.value = value;
1024         }
1025 
1026         void remove() {
1027             before.after = after;
1028             after.before = before;
1029         }
1030 
1031         void addBefore(Entry e) {
1032             after  = e;
1033             before = e.before;
1034             before.after = this;
1035             after.before = this;
1036         }
1037 
1038         public String getKey() {
1039             return key;
1040         }
1041 
1042         public String getValue() {
1043             return value;
1044         }
1045 
1046         public String setValue(String value) {
1047             if (value == null) {
1048                 throw new NullPointerException("value");
1049             }
1050             HttpCodecUtil.validateHeaderValue(value);
1051             String oldValue = this.value;
1052             this.value = value;
1053             return oldValue;
1054         }
1055 
1056         @Override
1057         public String toString() {
1058             return key + '=' + value;
1059         }
1060     }
1061 }