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