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 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 }