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