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 }