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         if (value != null) {
245             try {
246                 return INSTANCE.convertToByte(value);
247             } catch (RuntimeException ignore) {
248                 return null;
249             }
250         }
251         return null;
252     }
253 
254     @Override
255     public byte getByte(CharSequence name, byte defaultValue) {
256         Byte value = getByte(name);
257         return value != null ? value : defaultValue;
258     }
259 
260     @Override
261     public Character getChar(CharSequence name) {
262         AsciiString value = get0(name);
263         if (value != null) {
264             try {
265                 return INSTANCE.convertToChar(value);
266             } catch (RuntimeException ignore) {
267                 return null;
268             }
269         }
270         return null;
271     }
272 
273     @Override
274     public char getChar(CharSequence name, char defaultValue) {
275         Character value = getChar(name);
276         return value != null ? value : defaultValue;
277     }
278 
279     @Override
280     public Short getShort(CharSequence name) {
281         AsciiString value = get0(name);
282         if (value != null) {
283             try {
284                 return INSTANCE.convertToShort(value);
285             } catch (RuntimeException ignore) {
286                 return null;
287             }
288         }
289         return null;
290     }
291 
292     @Override
293     public short getShort(CharSequence name, short defaultValue) {
294         Short value = getShort(name);
295         return value != null ? value : defaultValue;
296     }
297 
298     @Override
299     public Integer getInt(CharSequence name) {
300         AsciiString value = get0(name);
301         if (value != null) {
302             try {
303                 return INSTANCE.convertToInt(value);
304             } catch (RuntimeException ignore) {
305                 return null;
306             }
307         }
308         return null;
309     }
310 
311     @Override
312     public int getInt(CharSequence name, int defaultValue) {
313         Integer value = getInt(name);
314         return value != null ? value : defaultValue;
315     }
316 
317     @Override
318     public Long getLong(CharSequence name) {
319         AsciiString value = get0(name);
320         if (value != null) {
321             try {
322                 return INSTANCE.convertToLong(value);
323             } catch (RuntimeException ignore) {
324                 return null;
325             }
326         }
327         return null;
328     }
329 
330     @Override
331     public long getLong(CharSequence name, long defaultValue) {
332         Long value = getLong(name);
333         return value != null ? value : defaultValue;
334     }
335 
336     @Override
337     public Float getFloat(CharSequence name) {
338         AsciiString value = get0(name);
339         if (value != null) {
340             try {
341                 return INSTANCE.convertToFloat(value);
342             } catch (RuntimeException ignore) {
343                 return null;
344             }
345         }
346         return null;
347     }
348 
349     @Override
350     public float getFloat(CharSequence name, float defaultValue) {
351         Float value = getFloat(name);
352         return value != null ? value : defaultValue;
353     }
354 
355     @Override
356     public Double getDouble(CharSequence name) {
357         AsciiString value = get0(name);
358         if (value != null) {
359             try {
360                 return INSTANCE.convertToDouble(value);
361             } catch (RuntimeException ignore) {
362                 return null;
363             }
364         }
365         return null;
366     }
367 
368     @Override
369     public double getDouble(CharSequence name, double defaultValue) {
370         Double value = getDouble(name);
371         return value != null ? value : defaultValue;
372     }
373 
374     @Override
375     public Long getTimeMillis(CharSequence name) {
376         AsciiString value = get0(name);
377         return value != null ? INSTANCE.convertToTimeMillis(value) : null;
378     }
379 
380     @Override
381     public long getTimeMillis(CharSequence name, long defaultValue) {
382         Long value = getTimeMillis(name);
383         return value != null ? value : defaultValue;
384     }
385 
386     @Override
387     public Boolean getBooleanAndRemove(CharSequence name) {
388         throw new UnsupportedOperationException("read only");
389     }
390 
391     @Override
392     public boolean getBooleanAndRemove(CharSequence name, boolean defaultValue) {
393         throw new UnsupportedOperationException("read only");
394     }
395 
396     @Override
397     public Byte getByteAndRemove(CharSequence name) {
398         throw new UnsupportedOperationException("read only");
399     }
400 
401     @Override
402     public byte getByteAndRemove(CharSequence name, byte defaultValue) {
403         throw new UnsupportedOperationException("read only");
404     }
405 
406     @Override
407     public Character getCharAndRemove(CharSequence name) {
408         throw new UnsupportedOperationException("read only");
409     }
410 
411     @Override
412     public char getCharAndRemove(CharSequence name, char defaultValue) {
413         throw new UnsupportedOperationException("read only");
414     }
415 
416     @Override
417     public Short getShortAndRemove(CharSequence name) {
418         throw new UnsupportedOperationException("read only");
419     }
420 
421     @Override
422     public short getShortAndRemove(CharSequence name, short defaultValue) {
423         throw new UnsupportedOperationException("read only");
424     }
425 
426     @Override
427     public Integer getIntAndRemove(CharSequence name) {
428         throw new UnsupportedOperationException("read only");
429     }
430 
431     @Override
432     public int getIntAndRemove(CharSequence name, int defaultValue) {
433         throw new UnsupportedOperationException("read only");
434     }
435 
436     @Override
437     public Long getLongAndRemove(CharSequence name) {
438         throw new UnsupportedOperationException("read only");
439     }
440 
441     @Override
442     public long getLongAndRemove(CharSequence name, long defaultValue) {
443         throw new UnsupportedOperationException("read only");
444     }
445 
446     @Override
447     public Float getFloatAndRemove(CharSequence name) {
448         throw new UnsupportedOperationException("read only");
449     }
450 
451     @Override
452     public float getFloatAndRemove(CharSequence name, float defaultValue) {
453         throw new UnsupportedOperationException("read only");
454     }
455 
456     @Override
457     public Double getDoubleAndRemove(CharSequence name) {
458         throw new UnsupportedOperationException("read only");
459     }
460 
461     @Override
462     public double getDoubleAndRemove(CharSequence name, double defaultValue) {
463         throw new UnsupportedOperationException("read only");
464     }
465 
466     @Override
467     public Long getTimeMillisAndRemove(CharSequence name) {
468         throw new UnsupportedOperationException("read only");
469     }
470 
471     @Override
472     public long getTimeMillisAndRemove(CharSequence name, long defaultValue) {
473         throw new UnsupportedOperationException("read only");
474     }
475 
476     @Override
477     public boolean contains(CharSequence name) {
478         return get(name) != null;
479     }
480 
481     @Override
482     public boolean contains(CharSequence name, CharSequence value) {
483         return contains(name, value, false);
484     }
485 
486     @Override
487     public boolean containsObject(CharSequence name, Object value) {
488         if (value instanceof CharSequence) {
489             return contains(name, (CharSequence) value);
490         }
491         return contains(name, value.toString());
492     }
493 
494     @Override
495     public boolean containsBoolean(CharSequence name, boolean value) {
496         return contains(name, String.valueOf(value));
497     }
498 
499     @Override
500     public boolean containsByte(CharSequence name, byte value) {
501         return contains(name, String.valueOf(value));
502     }
503 
504     @Override
505     public boolean containsChar(CharSequence name, char value) {
506         return contains(name, String.valueOf(value));
507     }
508 
509     @Override
510     public boolean containsShort(CharSequence name, short value) {
511         return contains(name, String.valueOf(value));
512     }
513 
514     @Override
515     public boolean containsInt(CharSequence name, int value) {
516         return contains(name, String.valueOf(value));
517     }
518 
519     @Override
520     public boolean containsLong(CharSequence name, long value) {
521         return contains(name, String.valueOf(value));
522     }
523 
524     @Override
525     public boolean containsFloat(CharSequence name, float value) {
526         return false;
527     }
528 
529     @Override
530     public boolean containsDouble(CharSequence name, double value) {
531         return contains(name, String.valueOf(value));
532     }
533 
534     @Override
535     public boolean containsTimeMillis(CharSequence name, long value) {
536         return contains(name, String.valueOf(value));
537     }
538 
539     @Override
540     public int size() {
541         return pseudoHeaders.length + otherHeaders.length >>> 1;
542     }
543 
544     @Override
545     public boolean isEmpty() {
546         return pseudoHeaders.length == 0 && otherHeaders.length == 0;
547     }
548 
549     @Override
550     public Set<CharSequence> names() {
551         if (isEmpty()) {
552             return Collections.emptySet();
553         }
554         Set<CharSequence> names = new LinkedHashSet<CharSequence>(size());
555         final int pseudoHeadersEnd = pseudoHeaders.length - 1;
556         for (int i = 0; i < pseudoHeadersEnd; i += 2) {
557             names.add(pseudoHeaders[i]);
558         }
559 
560         final int otherHeadersEnd = otherHeaders.length - 1;
561         for (int i = 0; i < otherHeadersEnd; i += 2) {
562             names.add(otherHeaders[i]);
563         }
564         return names;
565     }
566 
567     @Override
568     public Http2Headers add(CharSequence name, CharSequence value) {
569         throw new UnsupportedOperationException("read only");
570     }
571 
572     @Override
573     public Http2Headers add(CharSequence name, Iterable<? extends CharSequence> values) {
574         throw new UnsupportedOperationException("read only");
575     }
576 
577     @Override
578     public Http2Headers add(CharSequence name, CharSequence... values) {
579         throw new UnsupportedOperationException("read only");
580     }
581 
582     @Override
583     public Http2Headers addObject(CharSequence name, Object value) {
584         throw new UnsupportedOperationException("read only");
585     }
586 
587     @Override
588     public Http2Headers addObject(CharSequence name, Iterable<?> values) {
589         throw new UnsupportedOperationException("read only");
590     }
591 
592     @Override
593     public Http2Headers addObject(CharSequence name, Object... values) {
594         throw new UnsupportedOperationException("read only");
595     }
596 
597     @Override
598     public Http2Headers addBoolean(CharSequence name, boolean value) {
599         throw new UnsupportedOperationException("read only");
600     }
601 
602     @Override
603     public Http2Headers addByte(CharSequence name, byte value) {
604         throw new UnsupportedOperationException("read only");
605     }
606 
607     @Override
608     public Http2Headers addChar(CharSequence name, char value) {
609         throw new UnsupportedOperationException("read only");
610     }
611 
612     @Override
613     public Http2Headers addShort(CharSequence name, short value) {
614         throw new UnsupportedOperationException("read only");
615     }
616 
617     @Override
618     public Http2Headers addInt(CharSequence name, int value) {
619         throw new UnsupportedOperationException("read only");
620     }
621 
622     @Override
623     public Http2Headers addLong(CharSequence name, long value) {
624         throw new UnsupportedOperationException("read only");
625     }
626 
627     @Override
628     public Http2Headers addFloat(CharSequence name, float value) {
629         throw new UnsupportedOperationException("read only");
630     }
631 
632     @Override
633     public Http2Headers addDouble(CharSequence name, double value) {
634         throw new UnsupportedOperationException("read only");
635     }
636 
637     @Override
638     public Http2Headers addTimeMillis(CharSequence name, long value) {
639         throw new UnsupportedOperationException("read only");
640     }
641 
642     @Override
643     public Http2Headers add(Headers<? extends CharSequence, ? extends CharSequence, ?> headers) {
644         throw new UnsupportedOperationException("read only");
645     }
646 
647     @Override
648     public Http2Headers set(CharSequence name, CharSequence value) {
649         throw new UnsupportedOperationException("read only");
650     }
651 
652     @Override
653     public Http2Headers set(CharSequence name, Iterable<? extends CharSequence> values) {
654         throw new UnsupportedOperationException("read only");
655     }
656 
657     @Override
658     public Http2Headers set(CharSequence name, CharSequence... values) {
659         throw new UnsupportedOperationException("read only");
660     }
661 
662     @Override
663     public Http2Headers setObject(CharSequence name, Object value) {
664         throw new UnsupportedOperationException("read only");
665     }
666 
667     @Override
668     public Http2Headers setObject(CharSequence name, Iterable<?> values) {
669         throw new UnsupportedOperationException("read only");
670     }
671 
672     @Override
673     public Http2Headers setObject(CharSequence name, Object... values) {
674         throw new UnsupportedOperationException("read only");
675     }
676 
677     @Override
678     public Http2Headers setBoolean(CharSequence name, boolean value) {
679         throw new UnsupportedOperationException("read only");
680     }
681 
682     @Override
683     public Http2Headers setByte(CharSequence name, byte value) {
684         throw new UnsupportedOperationException("read only");
685     }
686 
687     @Override
688     public Http2Headers setChar(CharSequence name, char value) {
689         throw new UnsupportedOperationException("read only");
690     }
691 
692     @Override
693     public Http2Headers setShort(CharSequence name, short value) {
694         throw new UnsupportedOperationException("read only");
695     }
696 
697     @Override
698     public Http2Headers setInt(CharSequence name, int value) {
699         throw new UnsupportedOperationException("read only");
700     }
701 
702     @Override
703     public Http2Headers setLong(CharSequence name, long value) {
704         throw new UnsupportedOperationException("read only");
705     }
706 
707     @Override
708     public Http2Headers setFloat(CharSequence name, float value) {
709         throw new UnsupportedOperationException("read only");
710     }
711 
712     @Override
713     public Http2Headers setDouble(CharSequence name, double value) {
714         throw new UnsupportedOperationException("read only");
715     }
716 
717     @Override
718     public Http2Headers setTimeMillis(CharSequence name, long value) {
719         throw new UnsupportedOperationException("read only");
720     }
721 
722     @Override
723     public Http2Headers set(Headers<? extends CharSequence, ? extends CharSequence, ?> headers) {
724         throw new UnsupportedOperationException("read only");
725     }
726 
727     @Override
728     public Http2Headers setAll(Headers<? extends CharSequence, ? extends CharSequence, ?> headers) {
729         throw new UnsupportedOperationException("read only");
730     }
731 
732     @Override
733     public boolean remove(CharSequence name) {
734         throw new UnsupportedOperationException("read only");
735     }
736 
737     @Override
738     public Http2Headers clear() {
739         throw new UnsupportedOperationException("read only");
740     }
741 
742     @Override
743     public Iterator<Map.Entry<CharSequence, CharSequence>> iterator() {
744         return new ReadOnlyIterator();
745     }
746 
747     @Override
748     public Iterator<CharSequence> valueIterator(CharSequence name) {
749         return new ReadOnlyValueIterator(name);
750     }
751 
752     @Override
753     public Http2Headers method(CharSequence value) {
754         throw new UnsupportedOperationException("read only");
755     }
756 
757     @Override
758     public Http2Headers scheme(CharSequence value) {
759         throw new UnsupportedOperationException("read only");
760     }
761 
762     @Override
763     public Http2Headers authority(CharSequence value) {
764         throw new UnsupportedOperationException("read only");
765     }
766 
767     @Override
768     public Http2Headers path(CharSequence value) {
769         throw new UnsupportedOperationException("read only");
770     }
771 
772     @Override
773     public Http2Headers status(CharSequence value) {
774         throw new UnsupportedOperationException("read only");
775     }
776 
777     @Override
778     public CharSequence method() {
779         return get(PseudoHeaderName.METHOD.value());
780     }
781 
782     @Override
783     public CharSequence scheme() {
784         return get(PseudoHeaderName.SCHEME.value());
785     }
786 
787     @Override
788     public CharSequence authority() {
789         return get(PseudoHeaderName.AUTHORITY.value());
790     }
791 
792     @Override
793     public CharSequence path() {
794         return get(PseudoHeaderName.PATH.value());
795     }
796 
797     @Override
798     public CharSequence status() {
799         return get(PseudoHeaderName.STATUS.value());
800     }
801 
802     @Override
803     public boolean contains(CharSequence name, CharSequence value, boolean caseInsensitive) {
804         final int nameHash = AsciiString.hashCode(name);
805         final HashingStrategy<CharSequence> strategy =
806                 caseInsensitive ? CASE_INSENSITIVE_HASHER : CASE_SENSITIVE_HASHER;
807         final int valueHash = strategy.hashCode(value);
808 
809         return contains(name, nameHash, value, valueHash, strategy, otherHeaders)
810                 || contains(name, nameHash, value, valueHash, strategy, pseudoHeaders);
811     }
812 
813     private static boolean contains(CharSequence name, int nameHash, CharSequence value, int valueHash,
814                                     HashingStrategy<CharSequence> hashingStrategy, AsciiString[] headers) {
815         final int headersEnd = headers.length - 1;
816         for (int i = 0; i < headersEnd; i += 2) {
817             AsciiString roName = headers[i];
818             AsciiString roValue = headers[i + 1];
819             if (roName.hashCode() == nameHash && roValue.hashCode() == valueHash &&
820                 roName.contentEqualsIgnoreCase(name) && hashingStrategy.equals(roValue, value)) {
821                 return true;
822             }
823         }
824         return false;
825     }
826 
827     @Override
828     public String toString() {
829         StringBuilder builder = new StringBuilder(getClass().getSimpleName()).append('[');
830         String separator = "";
831         for (Map.Entry<CharSequence, CharSequence> entry : this) {
832             builder.append(separator);
833             builder.append(entry.getKey()).append(": ").append(entry.getValue());
834             separator = ", ";
835         }
836         return builder.append(']').toString();
837     }
838 
839     private final class ReadOnlyValueIterator implements Iterator<CharSequence> {
840         private int i;
841         private final int nameHash;
842         private final CharSequence name;
843         private AsciiString[] current = pseudoHeaders.length != 0 ? pseudoHeaders : otherHeaders;
844         private AsciiString next;
845 
846         ReadOnlyValueIterator(CharSequence name) {
847             nameHash = AsciiString.hashCode(name);
848             this.name = name;
849             calculateNext();
850         }
851 
852         @Override
853         public boolean hasNext() {
854             return next != null;
855         }
856 
857         @Override
858         public CharSequence next() {
859             if (!hasNext()) {
860                 throw new NoSuchElementException();
861             }
862             CharSequence current = next;
863             calculateNext();
864             return current;
865         }
866 
867         @Override
868         public void remove() {
869             throw new UnsupportedOperationException("read only");
870         }
871 
872         private void calculateNext() {
873             for (; i < current.length; i += 2) {
874                 AsciiString roName = current[i];
875                 if (roName.hashCode() == nameHash && roName.contentEqualsIgnoreCase(name)) {
876                     if (i + 1 < current.length) {
877                         next = current[i + 1];
878                         i += 2;
879                     }
880                     return;
881                 }
882             }
883             if (current == pseudoHeaders) {
884                 i = 0;
885                 current = otherHeaders;
886                 calculateNext();
887             } else {
888                 next = null;
889             }
890         }
891     }
892 
893     private final class ReadOnlyIterator implements Map.Entry<CharSequence, CharSequence>,
894                                                     Iterator<Map.Entry<CharSequence, CharSequence>> {
895         private int i;
896         private AsciiString[] current = pseudoHeaders.length != 0 ? pseudoHeaders : otherHeaders;
897         private AsciiString key;
898         private AsciiString value;
899 
900         @Override
901         public boolean hasNext() {
902             return i != current.length;
903         }
904 
905         @Override
906         public Map.Entry<CharSequence, CharSequence> next() {
907             if (!hasNext()) {
908                 throw new NoSuchElementException();
909             }
910             key = current[i];
911             value = current[i + 1];
912             i += 2;
913             if (i == current.length && current == pseudoHeaders) {
914                 current = otherHeaders;
915                 i = 0;
916             }
917             return this;
918         }
919 
920         @Override
921         public CharSequence getKey() {
922             return key;
923         }
924 
925         @Override
926         public CharSequence getValue() {
927             return value;
928         }
929 
930         @Override
931         public CharSequence setValue(CharSequence value) {
932             throw new UnsupportedOperationException("read only");
933         }
934 
935         @Override
936         public void remove() {
937             throw new UnsupportedOperationException("read only");
938         }
939 
940         @Override
941         public String toString() {
942             return key.toString() + '=' + value.toString();
943         }
944     }
945 }