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