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