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