View Javadoc

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