View Javadoc
1   /*
2    * Copyright 2016 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 io.netty.handler.codec.http2;
17  
18  import io.netty.handler.codec.Headers;
19  import io.netty.util.AsciiString;
20  import io.netty.util.HashingStrategy;
21  
22  import java.util.ArrayList;
23  import java.util.Collections;
24  import java.util.Iterator;
25  import java.util.LinkedHashSet;
26  import java.util.List;
27  import java.util.Map;
28  import java.util.NoSuchElementException;
29  import java.util.Set;
30  
31  import static io.netty.handler.codec.CharSequenceValueConverter.*;
32  import static io.netty.handler.codec.http2.DefaultHttp2Headers.*;
33  import static io.netty.util.AsciiString.*;
34  import static io.netty.util.internal.EmptyArrays.*;
35  
36  /**
37   * A variant of {@link Http2Headers} which only supports read-only methods.
38   * <p>
39   * Any array passed to this class may be used directly in the underlying data structures of this class. If these
40   * arrays may be modified it is the caller's responsibility to supply this class with a copy of the array.
41   * <p>
42   * This may be a good alternative to {@link DefaultHttp2Headers} if your have a fixed set of headers which will not
43   * change.
44   */
45  public final class ReadOnlyHttp2Headers implements Http2Headers {
46      private static final byte PSEUDO_HEADER_TOKEN = (byte) ':';
47      private final AsciiString[] pseudoHeaders;
48      private final AsciiString[] otherHeaders;
49  
50      /**
51       * Used to create read only object designed to represent trailers.
52       * <p>
53       * If this is used for a purpose other than trailers you may violate the header serialization ordering defined by
54       * <a href="https://tools.ietf.org/html/rfc7540#section-8.1.2.1">RFC 7540, 8.1.2.1</a>.
55       * @param validateHeaders {@code true} will run validation on each header name/value pair to ensure protocol
56       *                        compliance.
57       * @param otherHeaders A an array of key:value pairs. Must not contain any
58       *                     <a href="https://tools.ietf.org/html/rfc7540#section-8.1.2.1">pseudo headers</a>
59       *                     or {@code null} names/values.
60       *                     A copy will <strong>NOT</strong> be made of this array. If the contents of this array
61       *                     may be modified externally you are responsible for passing in a copy.
62       * @return A read only representation of the headers.
63       */
64      public static ReadOnlyHttp2Headers trailers(boolean validateHeaders, AsciiString... otherHeaders) {
65          return new ReadOnlyHttp2Headers(validateHeaders, EMPTY_ASCII_STRINGS, otherHeaders);
66      }
67  
68      /**
69       * Create a new read only representation of headers used by clients.
70       * @param validateHeaders {@code true} will run validation on each header name/value pair to ensure protocol
71       *                        compliance.
72       * @param method The value for {@link PseudoHeaderName#METHOD}.
73       * @param path The value for {@link PseudoHeaderName#PATH}.
74       * @param scheme The value for {@link PseudoHeaderName#SCHEME}.
75       * @param authority The value for {@link PseudoHeaderName#AUTHORITY}.
76       * @param otherHeaders A an array of key:value pairs. Must not contain any
77       *                     <a href="https://tools.ietf.org/html/rfc7540#section-8.1.2.1">pseudo headers</a>
78       *                     or {@code null} names/values.
79       *                     A copy will <strong>NOT</strong> be made of this array. If the contents of this array
80       *                     may be modified externally you are responsible for passing in a copy.
81       * @return a new read only representation of headers used by clients.
82       */
83      public static ReadOnlyHttp2Headers clientHeaders(boolean validateHeaders,
84                                                       AsciiString method, AsciiString path,
85                                                       AsciiString scheme, AsciiString authority,
86                                                       AsciiString... otherHeaders) {
87          return new ReadOnlyHttp2Headers(validateHeaders,
88                  new AsciiString[] {
89                    PseudoHeaderName.METHOD.value(), method, PseudoHeaderName.PATH.value(), path,
90                    PseudoHeaderName.SCHEME.value(), scheme, PseudoHeaderName.AUTHORITY.value(), authority
91                  },
92                  otherHeaders);
93      }
94  
95      /**
96       * Create a new read only representation of headers used by servers.
97       * @param validateHeaders {@code true} will run validation on each header name/value pair to ensure protocol
98       *                        compliance.
99       * @param status The value for {@link PseudoHeaderName#STATUS}.
100      * @param otherHeaders A an array of key:value pairs. Must not contain any
101      *                     <a href="https://tools.ietf.org/html/rfc7540#section-8.1.2.1">pseudo headers</a>
102      *                     or {@code null} names/values.
103      *                     A copy will <strong>NOT</strong> be made of this array. If the contents of this array
104      *                     may be modified externally you are responsible for passing in a copy.
105      * @return a new read only representation of headers used by servers.
106      */
107     public static ReadOnlyHttp2Headers serverHeaders(boolean validateHeaders,
108                                                      AsciiString status,
109                                                      AsciiString... otherHeaders) {
110         return new ReadOnlyHttp2Headers(validateHeaders,
111                                         new AsciiString[] { PseudoHeaderName.STATUS.value(), status },
112                                         otherHeaders);
113     }
114 
115     private ReadOnlyHttp2Headers(boolean validateHeaders, AsciiString[] pseudoHeaders, AsciiString... otherHeaders) {
116         assert (pseudoHeaders.length & 1) == 0; // pseudoHeaders are only set internally so assert should be enough.
117         if ((otherHeaders.length & 1) != 0) {
118             throw newInvalidArraySizeException();
119         }
120         if (validateHeaders) {
121             validateHeaders(pseudoHeaders, otherHeaders);
122         }
123         this.pseudoHeaders = pseudoHeaders;
124         this.otherHeaders = otherHeaders;
125     }
126 
127     private static IllegalArgumentException newInvalidArraySizeException() {
128         return new IllegalArgumentException("pseudoHeaders and otherHeaders must be arrays of [name, value] pairs");
129     }
130 
131     private static void validateHeaders(AsciiString[] pseudoHeaders, AsciiString... otherHeaders) {
132         // We are only validating values... so start at 1 and go until end.
133         for (int i = 1; i < pseudoHeaders.length; i += 2) {
134             // pseudoHeaders names are only set internally so they are assumed to be valid.
135             if (pseudoHeaders[i] == null) {
136                 throw new IllegalArgumentException("pseudoHeaders value at index " + i + " is null");
137             }
138         }
139 
140         boolean seenNonPseudoHeader = false;
141         final int otherHeadersEnd = otherHeaders.length - 1;
142         for (int i = 0; i < otherHeadersEnd; i += 2) {
143             AsciiString name = otherHeaders[i];
144             HTTP2_NAME_VALIDATOR.validateName(name);
145             if (!seenNonPseudoHeader && !name.isEmpty() && name.byteAt(0) != PSEUDO_HEADER_TOKEN) {
146                 seenNonPseudoHeader = true;
147             } else if (seenNonPseudoHeader && !name.isEmpty() && name.byteAt(0) == PSEUDO_HEADER_TOKEN) {
148                 throw new IllegalArgumentException(
149                      "otherHeaders name at index " + i + " is a pseudo header that appears after non-pseudo headers.");
150             }
151             if (otherHeaders[i + 1] == null) {
152                 throw new IllegalArgumentException("otherHeaders value at index " + (i + 1) + " is null");
153             }
154         }
155     }
156 
157     private AsciiString get0(CharSequence name) {
158         final int nameHash = AsciiString.hashCode(name);
159 
160         final int pseudoHeadersEnd = pseudoHeaders.length - 1;
161         for (int i = 0; i < pseudoHeadersEnd; i += 2) {
162             AsciiString roName = pseudoHeaders[i];
163             if (roName.hashCode() == nameHash && roName.contentEqualsIgnoreCase(name)) {
164                 return pseudoHeaders[i + 1];
165             }
166         }
167 
168         final int otherHeadersEnd = otherHeaders.length - 1;
169         for (int i = 0; i < otherHeadersEnd; i += 2) {
170             AsciiString roName = otherHeaders[i];
171             if (roName.hashCode() == nameHash && roName.contentEqualsIgnoreCase(name)) {
172                 return otherHeaders[i + 1];
173             }
174         }
175         return null;
176     }
177 
178     @Override
179     public CharSequence get(CharSequence name) {
180         return get0(name);
181     }
182 
183     @Override
184     public CharSequence get(CharSequence name, CharSequence defaultValue) {
185         CharSequence value = get(name);
186         return value != null ? value : defaultValue;
187     }
188 
189     @Override
190     public CharSequence getAndRemove(CharSequence name) {
191         throw new UnsupportedOperationException("read only");
192     }
193 
194     @Override
195     public CharSequence getAndRemove(CharSequence name, CharSequence defaultValue) {
196         throw new UnsupportedOperationException("read only");
197     }
198 
199     @Override
200     public List<CharSequence> getAll(CharSequence name) {
201         final int nameHash = AsciiString.hashCode(name);
202         List<CharSequence> values = new ArrayList<CharSequence>();
203 
204         final int pseudoHeadersEnd = pseudoHeaders.length - 1;
205         for (int i = 0; i < pseudoHeadersEnd; i += 2) {
206             AsciiString roName = pseudoHeaders[i];
207             if (roName.hashCode() == nameHash && roName.contentEqualsIgnoreCase(name)) {
208                 values.add(pseudoHeaders[i + 1]);
209             }
210         }
211 
212         final int otherHeadersEnd = otherHeaders.length - 1;
213         for (int i = 0; i < otherHeadersEnd; i += 2) {
214             AsciiString roName = otherHeaders[i];
215             if (roName.hashCode() == nameHash && roName.contentEqualsIgnoreCase(name)) {
216                 values.add(otherHeaders[i + 1]);
217             }
218         }
219 
220         return values;
221     }
222 
223     @Override
224     public List<CharSequence> getAllAndRemove(CharSequence name) {
225         throw new UnsupportedOperationException("read only");
226     }
227 
228     @Override
229     public Boolean getBoolean(CharSequence name) {
230         AsciiString value = get0(name);
231         return value != null ? INSTANCE.convertToBoolean(value) : null;
232     }
233 
234     @Override
235     public boolean getBoolean(CharSequence name, boolean defaultValue) {
236         Boolean value = getBoolean(name);
237         return value != null ? value : defaultValue;
238     }
239 
240     @Override
241     public Byte getByte(CharSequence name) {
242         AsciiString value = get0(name);
243         return value != null ? INSTANCE.convertToByte(value) : null;
244     }
245 
246     @Override
247     public byte getByte(CharSequence name, byte defaultValue) {
248         Byte value = getByte(name);
249         return value != null ? value : defaultValue;
250     }
251 
252     @Override
253     public Character getChar(CharSequence name) {
254         AsciiString value = get0(name);
255         return value != null ? INSTANCE.convertToChar(value) : null;
256     }
257 
258     @Override
259     public char getChar(CharSequence name, char defaultValue) {
260         Character value = getChar(name);
261         return value != null ? value : defaultValue;
262     }
263 
264     @Override
265     public Short getShort(CharSequence name) {
266         AsciiString value = get0(name);
267         return value != null ? INSTANCE.convertToShort(value) : null;
268     }
269 
270     @Override
271     public short getShort(CharSequence name, short defaultValue) {
272         Short value = getShort(name);
273         return value != null ? value : defaultValue;
274     }
275 
276     @Override
277     public Integer getInt(CharSequence name) {
278         AsciiString value = get0(name);
279         return value != null ? INSTANCE.convertToInt(value) : null;
280     }
281 
282     @Override
283     public int getInt(CharSequence name, int defaultValue) {
284         Integer value = getInt(name);
285         return value != null ? value : defaultValue;
286     }
287 
288     @Override
289     public Long getLong(CharSequence name) {
290         AsciiString value = get0(name);
291         return value != null ? INSTANCE.convertToLong(value) : null;
292     }
293 
294     @Override
295     public long getLong(CharSequence name, long defaultValue) {
296         Long value = getLong(name);
297         return value != null ? value : defaultValue;
298     }
299 
300     @Override
301     public Float getFloat(CharSequence name) {
302         AsciiString value = get0(name);
303         return value != null ? INSTANCE.convertToFloat(value) : null;
304     }
305 
306     @Override
307     public float getFloat(CharSequence name, float defaultValue) {
308         Float value = getFloat(name);
309         return value != null ? value : defaultValue;
310     }
311 
312     @Override
313     public Double getDouble(CharSequence name) {
314         AsciiString value = get0(name);
315         return value != null ? INSTANCE.convertToDouble(value) : null;
316     }
317 
318     @Override
319     public double getDouble(CharSequence name, double defaultValue) {
320         Double value = getDouble(name);
321         return value != null ? value : defaultValue;
322     }
323 
324     @Override
325     public Long getTimeMillis(CharSequence name) {
326         AsciiString value = get0(name);
327         return value != null ? INSTANCE.convertToTimeMillis(value) : null;
328     }
329 
330     @Override
331     public long getTimeMillis(CharSequence name, long defaultValue) {
332         Long value = getTimeMillis(name);
333         return value != null ? value : defaultValue;
334     }
335 
336     @Override
337     public Boolean getBooleanAndRemove(CharSequence name) {
338         throw new UnsupportedOperationException("read only");
339     }
340 
341     @Override
342     public boolean getBooleanAndRemove(CharSequence name, boolean defaultValue) {
343         throw new UnsupportedOperationException("read only");
344     }
345 
346     @Override
347     public Byte getByteAndRemove(CharSequence name) {
348         throw new UnsupportedOperationException("read only");
349     }
350 
351     @Override
352     public byte getByteAndRemove(CharSequence name, byte defaultValue) {
353         throw new UnsupportedOperationException("read only");
354     }
355 
356     @Override
357     public Character getCharAndRemove(CharSequence name) {
358         throw new UnsupportedOperationException("read only");
359     }
360 
361     @Override
362     public char getCharAndRemove(CharSequence name, char defaultValue) {
363         throw new UnsupportedOperationException("read only");
364     }
365 
366     @Override
367     public Short getShortAndRemove(CharSequence name) {
368         throw new UnsupportedOperationException("read only");
369     }
370 
371     @Override
372     public short getShortAndRemove(CharSequence name, short defaultValue) {
373         throw new UnsupportedOperationException("read only");
374     }
375 
376     @Override
377     public Integer getIntAndRemove(CharSequence name) {
378         throw new UnsupportedOperationException("read only");
379     }
380 
381     @Override
382     public int getIntAndRemove(CharSequence name, int defaultValue) {
383         throw new UnsupportedOperationException("read only");
384     }
385 
386     @Override
387     public Long getLongAndRemove(CharSequence name) {
388         throw new UnsupportedOperationException("read only");
389     }
390 
391     @Override
392     public long getLongAndRemove(CharSequence name, long defaultValue) {
393         throw new UnsupportedOperationException("read only");
394     }
395 
396     @Override
397     public Float getFloatAndRemove(CharSequence name) {
398         throw new UnsupportedOperationException("read only");
399     }
400 
401     @Override
402     public float getFloatAndRemove(CharSequence name, float defaultValue) {
403         throw new UnsupportedOperationException("read only");
404     }
405 
406     @Override
407     public Double getDoubleAndRemove(CharSequence name) {
408         throw new UnsupportedOperationException("read only");
409     }
410 
411     @Override
412     public double getDoubleAndRemove(CharSequence name, double defaultValue) {
413         throw new UnsupportedOperationException("read only");
414     }
415 
416     @Override
417     public Long getTimeMillisAndRemove(CharSequence name) {
418         throw new UnsupportedOperationException("read only");
419     }
420 
421     @Override
422     public long getTimeMillisAndRemove(CharSequence name, long defaultValue) {
423         throw new UnsupportedOperationException("read only");
424     }
425 
426     @Override
427     public boolean contains(CharSequence name) {
428         return get(name) != null;
429     }
430 
431     @Override
432     public boolean contains(CharSequence name, CharSequence value) {
433         return contains(name, value, false);
434     }
435 
436     @Override
437     public boolean containsObject(CharSequence name, Object value) {
438         if (value instanceof CharSequence) {
439             return contains(name, (CharSequence) value);
440         }
441         return contains(name, value.toString());
442     }
443 
444     @Override
445     public boolean containsBoolean(CharSequence name, boolean value) {
446         return contains(name, String.valueOf(value));
447     }
448 
449     @Override
450     public boolean containsByte(CharSequence name, byte value) {
451         return contains(name, String.valueOf(value));
452     }
453 
454     @Override
455     public boolean containsChar(CharSequence name, char value) {
456         return contains(name, String.valueOf(value));
457     }
458 
459     @Override
460     public boolean containsShort(CharSequence name, short value) {
461         return contains(name, String.valueOf(value));
462     }
463 
464     @Override
465     public boolean containsInt(CharSequence name, int value) {
466         return contains(name, String.valueOf(value));
467     }
468 
469     @Override
470     public boolean containsLong(CharSequence name, long value) {
471         return contains(name, String.valueOf(value));
472     }
473 
474     @Override
475     public boolean containsFloat(CharSequence name, float value) {
476         return false;
477     }
478 
479     @Override
480     public boolean containsDouble(CharSequence name, double value) {
481         return contains(name, String.valueOf(value));
482     }
483 
484     @Override
485     public boolean containsTimeMillis(CharSequence name, long value) {
486         return contains(name, String.valueOf(value));
487     }
488 
489     @Override
490     public int size() {
491         return (pseudoHeaders.length + otherHeaders.length) >>> 1;
492     }
493 
494     @Override
495     public boolean isEmpty() {
496         return pseudoHeaders.length == 0 && otherHeaders.length == 0;
497     }
498 
499     @Override
500     public Set<CharSequence> names() {
501         if (isEmpty()) {
502             return Collections.emptySet();
503         }
504         Set<CharSequence> names = new LinkedHashSet<CharSequence>(size());
505         final int pseudoHeadersEnd = pseudoHeaders.length - 1;
506         for (int i = 0; i < pseudoHeadersEnd; i += 2) {
507             names.add(pseudoHeaders[i]);
508         }
509 
510         final int otherHeadersEnd = otherHeaders.length - 1;
511         for (int i = 0; i < otherHeadersEnd; i += 2) {
512             names.add(otherHeaders[i]);
513         }
514         return names;
515     }
516 
517     @Override
518     public Http2Headers add(CharSequence name, CharSequence value) {
519         throw new UnsupportedOperationException("read only");
520     }
521 
522     @Override
523     public Http2Headers add(CharSequence name, Iterable<? extends CharSequence> values) {
524         throw new UnsupportedOperationException("read only");
525     }
526 
527     @Override
528     public Http2Headers add(CharSequence name, CharSequence... values) {
529         throw new UnsupportedOperationException("read only");
530     }
531 
532     @Override
533     public Http2Headers addObject(CharSequence name, Object value) {
534         throw new UnsupportedOperationException("read only");
535     }
536 
537     @Override
538     public Http2Headers addObject(CharSequence name, Iterable<?> values) {
539         throw new UnsupportedOperationException("read only");
540     }
541 
542     @Override
543     public Http2Headers addObject(CharSequence name, Object... values) {
544         throw new UnsupportedOperationException("read only");
545     }
546 
547     @Override
548     public Http2Headers addBoolean(CharSequence name, boolean value) {
549         throw new UnsupportedOperationException("read only");
550     }
551 
552     @Override
553     public Http2Headers addByte(CharSequence name, byte value) {
554         throw new UnsupportedOperationException("read only");
555     }
556 
557     @Override
558     public Http2Headers addChar(CharSequence name, char value) {
559         throw new UnsupportedOperationException("read only");
560     }
561 
562     @Override
563     public Http2Headers addShort(CharSequence name, short value) {
564         throw new UnsupportedOperationException("read only");
565     }
566 
567     @Override
568     public Http2Headers addInt(CharSequence name, int value) {
569         throw new UnsupportedOperationException("read only");
570     }
571 
572     @Override
573     public Http2Headers addLong(CharSequence name, long value) {
574         throw new UnsupportedOperationException("read only");
575     }
576 
577     @Override
578     public Http2Headers addFloat(CharSequence name, float value) {
579         throw new UnsupportedOperationException("read only");
580     }
581 
582     @Override
583     public Http2Headers addDouble(CharSequence name, double value) {
584         throw new UnsupportedOperationException("read only");
585     }
586 
587     @Override
588     public Http2Headers addTimeMillis(CharSequence name, long value) {
589         throw new UnsupportedOperationException("read only");
590     }
591 
592     @Override
593     public Http2Headers add(Headers<? extends CharSequence, ? extends CharSequence, ?> headers) {
594         throw new UnsupportedOperationException("read only");
595     }
596 
597     @Override
598     public Http2Headers set(CharSequence name, CharSequence value) {
599         throw new UnsupportedOperationException("read only");
600     }
601 
602     @Override
603     public Http2Headers set(CharSequence name, Iterable<? extends CharSequence> values) {
604         throw new UnsupportedOperationException("read only");
605     }
606 
607     @Override
608     public Http2Headers set(CharSequence name, CharSequence... values) {
609         throw new UnsupportedOperationException("read only");
610     }
611 
612     @Override
613     public Http2Headers setObject(CharSequence name, Object value) {
614         throw new UnsupportedOperationException("read only");
615     }
616 
617     @Override
618     public Http2Headers setObject(CharSequence name, Iterable<?> values) {
619         throw new UnsupportedOperationException("read only");
620     }
621 
622     @Override
623     public Http2Headers setObject(CharSequence name, Object... values) {
624         throw new UnsupportedOperationException("read only");
625     }
626 
627     @Override
628     public Http2Headers setBoolean(CharSequence name, boolean value) {
629         throw new UnsupportedOperationException("read only");
630     }
631 
632     @Override
633     public Http2Headers setByte(CharSequence name, byte value) {
634         throw new UnsupportedOperationException("read only");
635     }
636 
637     @Override
638     public Http2Headers setChar(CharSequence name, char value) {
639         throw new UnsupportedOperationException("read only");
640     }
641 
642     @Override
643     public Http2Headers setShort(CharSequence name, short value) {
644         throw new UnsupportedOperationException("read only");
645     }
646 
647     @Override
648     public Http2Headers setInt(CharSequence name, int value) {
649         throw new UnsupportedOperationException("read only");
650     }
651 
652     @Override
653     public Http2Headers setLong(CharSequence name, long value) {
654         throw new UnsupportedOperationException("read only");
655     }
656 
657     @Override
658     public Http2Headers setFloat(CharSequence name, float value) {
659         throw new UnsupportedOperationException("read only");
660     }
661 
662     @Override
663     public Http2Headers setDouble(CharSequence name, double value) {
664         throw new UnsupportedOperationException("read only");
665     }
666 
667     @Override
668     public Http2Headers setTimeMillis(CharSequence name, long value) {
669         throw new UnsupportedOperationException("read only");
670     }
671 
672     @Override
673     public Http2Headers set(Headers<? extends CharSequence, ? extends CharSequence, ?> headers) {
674         throw new UnsupportedOperationException("read only");
675     }
676 
677     @Override
678     public Http2Headers setAll(Headers<? extends CharSequence, ? extends CharSequence, ?> headers) {
679         throw new UnsupportedOperationException("read only");
680     }
681 
682     @Override
683     public boolean remove(CharSequence name) {
684         throw new UnsupportedOperationException("read only");
685     }
686 
687     @Override
688     public Http2Headers clear() {
689         throw new UnsupportedOperationException("read only");
690     }
691 
692     @Override
693     public Iterator<Map.Entry<CharSequence, CharSequence>> iterator() {
694         return new ReadOnlyIterator();
695     }
696 
697     @Override
698     public Iterator<CharSequence> valueIterator(CharSequence name) {
699         return new ReadOnlyValueIterator(name);
700     }
701 
702     @Override
703     public Http2Headers method(CharSequence value) {
704         throw new UnsupportedOperationException("read only");
705     }
706 
707     @Override
708     public Http2Headers scheme(CharSequence value) {
709         throw new UnsupportedOperationException("read only");
710     }
711 
712     @Override
713     public Http2Headers authority(CharSequence value) {
714         throw new UnsupportedOperationException("read only");
715     }
716 
717     @Override
718     public Http2Headers path(CharSequence value) {
719         throw new UnsupportedOperationException("read only");
720     }
721 
722     @Override
723     public Http2Headers status(CharSequence value) {
724         throw new UnsupportedOperationException("read only");
725     }
726 
727     @Override
728     public CharSequence method() {
729         return get(PseudoHeaderName.METHOD.value());
730     }
731 
732     @Override
733     public CharSequence scheme() {
734         return get(PseudoHeaderName.SCHEME.value());
735     }
736 
737     @Override
738     public CharSequence authority() {
739         return get(PseudoHeaderName.AUTHORITY.value());
740     }
741 
742     @Override
743     public CharSequence path() {
744         return get(PseudoHeaderName.PATH.value());
745     }
746 
747     @Override
748     public CharSequence status() {
749         return get(PseudoHeaderName.STATUS.value());
750     }
751 
752     @Override
753     public boolean contains(CharSequence name, CharSequence value, boolean caseInsensitive) {
754         final int nameHash = AsciiString.hashCode(name);
755         final HashingStrategy<CharSequence> strategy =
756                 caseInsensitive ? CASE_INSENSITIVE_HASHER : CASE_SENSITIVE_HASHER;
757         final int valueHash = strategy.hashCode(value);
758 
759         return contains(name, nameHash, value, valueHash, strategy, otherHeaders)
760                 || contains(name, nameHash, value, valueHash, strategy, pseudoHeaders);
761     }
762 
763     private static boolean contains(CharSequence name, int nameHash, CharSequence value, int valueHash,
764                                     HashingStrategy<CharSequence> hashingStrategy, AsciiString[] headers) {
765         final int headersEnd = headers.length - 1;
766         for (int i = 0; i < headersEnd; i += 2) {
767             AsciiString roName = headers[i];
768             AsciiString roValue = headers[i + 1];
769             if (roName.hashCode() == nameHash && roValue.hashCode() == valueHash &&
770                 roName.contentEqualsIgnoreCase(name) && hashingStrategy.equals(roValue, value)) {
771                 return true;
772             }
773         }
774         return false;
775     }
776 
777     @Override
778     public String toString() {
779         StringBuilder builder = new StringBuilder(getClass().getSimpleName()).append('[');
780         String separator = "";
781         for (Map.Entry<CharSequence, CharSequence> entry : this) {
782             builder.append(separator);
783             builder.append(entry.getKey()).append(": ").append(entry.getValue());
784             separator = ", ";
785         }
786         return builder.append(']').toString();
787     }
788 
789     private final class ReadOnlyValueIterator implements Iterator<CharSequence> {
790         private int i;
791         private final int nameHash;
792         private final CharSequence name;
793         private AsciiString[] current = pseudoHeaders.length != 0 ? pseudoHeaders : otherHeaders;
794         private AsciiString next;
795 
796         ReadOnlyValueIterator(CharSequence name) {
797             nameHash = AsciiString.hashCode(name);
798             this.name = name;
799             calculateNext();
800         }
801 
802         @Override
803         public boolean hasNext() {
804             return next != null;
805         }
806 
807         @Override
808         public CharSequence next() {
809             if (!hasNext()) {
810                 throw new NoSuchElementException();
811             }
812             CharSequence current = next;
813             calculateNext();
814             return current;
815         }
816 
817         @Override
818         public void remove() {
819             throw new UnsupportedOperationException("read only");
820         }
821 
822         private void calculateNext() {
823             for (; i < current.length; i += 2) {
824                 AsciiString roName = current[i];
825                 if (roName.hashCode() == nameHash && roName.contentEqualsIgnoreCase(name)) {
826                     next = current[i + 1];
827                     i += 2;
828                     return;
829                 }
830             }
831             if (i >= current.length && current == pseudoHeaders) {
832                 i = 0;
833                 current = otherHeaders;
834                 calculateNext();
835             } else {
836                 next = null;
837             }
838         }
839     }
840 
841     private final class ReadOnlyIterator implements Map.Entry<CharSequence, CharSequence>,
842                                                     Iterator<Map.Entry<CharSequence, CharSequence>> {
843         private int i;
844         private AsciiString[] current = pseudoHeaders.length != 0 ? pseudoHeaders : otherHeaders;
845         private AsciiString key;
846         private AsciiString value;
847 
848         @Override
849         public boolean hasNext() {
850             return i != current.length;
851         }
852 
853         @Override
854         public Map.Entry<CharSequence, CharSequence> next() {
855             if (!hasNext()) {
856                 throw new NoSuchElementException();
857             }
858             key = current[i];
859             value = current[i + 1];
860             i += 2;
861             if (i == current.length && current == pseudoHeaders) {
862                 current = otherHeaders;
863                 i = 0;
864             }
865             return this;
866         }
867 
868         @Override
869         public CharSequence getKey() {
870             return key;
871         }
872 
873         @Override
874         public CharSequence getValue() {
875             return value;
876         }
877 
878         @Override
879         public CharSequence setValue(CharSequence value) {
880             throw new UnsupportedOperationException("read only");
881         }
882 
883         @Override
884         public void remove() {
885             throw new UnsupportedOperationException("read only");
886         }
887 
888         @Override
889         public String toString() {
890             return key.toString() + '=' + value.toString();
891         }
892     }
893 }