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