View Javadoc

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