1
2
3
4
5
6
7
8
9
10
11
12
13
14
15 package io.netty5.handler.codec.http2;
16
17 import io.netty5.handler.codec.CharSequenceValueConverter;
18 import io.netty5.handler.codec.DefaultHeaders;
19 import io.netty5.util.AsciiString;
20 import io.netty5.util.ByteProcessor;
21 import io.netty5.util.internal.PlatformDependent;
22 import io.netty5.util.internal.UnstableApi;
23
24 import static io.netty5.handler.codec.http2.Http2Error.PROTOCOL_ERROR;
25 import static io.netty5.handler.codec.http2.Http2Exception.connectionError;
26 import static io.netty5.handler.codec.http2.Http2Headers.PseudoHeaderName.hasPseudoHeaderFormat;
27 import static io.netty5.util.AsciiString.CASE_INSENSITIVE_HASHER;
28 import static io.netty5.util.AsciiString.CASE_SENSITIVE_HASHER;
29 import static io.netty5.util.AsciiString.isUpperCase;
30
31 @UnstableApi
32 public class DefaultHttp2Headers
33 extends DefaultHeaders<CharSequence, CharSequence, Http2Headers> implements Http2Headers {
34 private static final ByteProcessor HTTP2_NAME_VALIDATOR_PROCESSOR = value -> !isUpperCase(value);
35 static final NameValidator<CharSequence> HTTP2_NAME_VALIDATOR = name -> {
36 if (name == null || name.length() == 0) {
37 PlatformDependent.throwException(connectionError(PROTOCOL_ERROR,
38 "empty headers are not allowed [%s]", name));
39 }
40 if (name instanceof AsciiString) {
41 int index = ((AsciiString) name).forEachByte(HTTP2_NAME_VALIDATOR_PROCESSOR);
42 if (index != -1) {
43 PlatformDependent.throwException(connectionError(PROTOCOL_ERROR,
44 "invalid header name [%s]", name));
45 }
46 } else {
47 for (int i = 0; i < name.length(); ++i) {
48 if (isUpperCase(name.charAt(i))) {
49 PlatformDependent.throwException(connectionError(PROTOCOL_ERROR,
50 "invalid header name [%s]", name));
51 }
52 }
53 }
54 };
55
56 private HeaderEntry<CharSequence, CharSequence> firstNonPseudo = head;
57
58
59
60
61
62
63
64 public DefaultHttp2Headers() {
65 this(true);
66 }
67
68
69
70
71
72
73 @SuppressWarnings("unchecked")
74 public DefaultHttp2Headers(boolean validate) {
75
76
77 super(CASE_SENSITIVE_HASHER,
78 CharSequenceValueConverter.INSTANCE,
79 validate ? HTTP2_NAME_VALIDATOR : NameValidator.NOT_NULL);
80 }
81
82
83
84
85
86
87
88
89 @SuppressWarnings("unchecked")
90 public DefaultHttp2Headers(boolean validate, int arraySizeHint) {
91
92
93 super(CASE_SENSITIVE_HASHER,
94 CharSequenceValueConverter.INSTANCE,
95 validate ? HTTP2_NAME_VALIDATOR : NameValidator.NOT_NULL,
96 arraySizeHint);
97 }
98
99 @Override
100 public Http2Headers clear() {
101 firstNonPseudo = head;
102 return super.clear();
103 }
104
105 @Override
106 public boolean equals(Object o) {
107 return o instanceof Http2Headers && equals((Http2Headers) o, CASE_SENSITIVE_HASHER);
108 }
109
110 @Override
111 public int hashCode() {
112 return hashCode(CASE_SENSITIVE_HASHER);
113 }
114
115 @Override
116 public Http2Headers method(CharSequence value) {
117 set(PseudoHeaderName.METHOD.value(), value);
118 return this;
119 }
120
121 @Override
122 public Http2Headers scheme(CharSequence value) {
123 set(PseudoHeaderName.SCHEME.value(), value);
124 return this;
125 }
126
127 @Override
128 public Http2Headers authority(CharSequence value) {
129 set(PseudoHeaderName.AUTHORITY.value(), value);
130 return this;
131 }
132
133 @Override
134 public Http2Headers path(CharSequence value) {
135 set(PseudoHeaderName.PATH.value(), value);
136 return this;
137 }
138
139 @Override
140 public Http2Headers status(CharSequence value) {
141 set(PseudoHeaderName.STATUS.value(), value);
142 return this;
143 }
144
145 @Override
146 public CharSequence method() {
147 return get(PseudoHeaderName.METHOD.value());
148 }
149
150 @Override
151 public CharSequence scheme() {
152 return get(PseudoHeaderName.SCHEME.value());
153 }
154
155 @Override
156 public CharSequence authority() {
157 return get(PseudoHeaderName.AUTHORITY.value());
158 }
159
160 @Override
161 public CharSequence path() {
162 return get(PseudoHeaderName.PATH.value());
163 }
164
165 @Override
166 public CharSequence status() {
167 return get(PseudoHeaderName.STATUS.value());
168 }
169
170 @Override
171 public boolean contains(CharSequence name, CharSequence value) {
172 return contains(name, value, false);
173 }
174
175 @Override
176 public boolean contains(CharSequence name, CharSequence value, boolean caseInsensitive) {
177 return contains(name, value, caseInsensitive ? CASE_INSENSITIVE_HASHER : CASE_SENSITIVE_HASHER);
178 }
179
180 @Override
181 protected final HeaderEntry<CharSequence, CharSequence> newHeaderEntry(int h, CharSequence name, CharSequence value,
182 HeaderEntry<CharSequence, CharSequence> next) {
183 return new Http2HeaderEntry(h, name, value, next);
184 }
185
186 private final class Http2HeaderEntry extends HeaderEntry<CharSequence, CharSequence> {
187 Http2HeaderEntry(int hash, CharSequence key, CharSequence value,
188 HeaderEntry<CharSequence, CharSequence> next) {
189 super(hash, key);
190 this.value = value;
191 this.next = next;
192
193
194 if (hasPseudoHeaderFormat(key)) {
195 after = firstNonPseudo;
196 before = firstNonPseudo.before();
197 } else {
198 after = head;
199 before = head.before();
200 if (firstNonPseudo == head) {
201 firstNonPseudo = this;
202 }
203 }
204 pointNeighborsToThis();
205 }
206
207 @Override
208 protected void remove() {
209 if (this == firstNonPseudo) {
210 firstNonPseudo = firstNonPseudo.after();
211 }
212 super.remove();
213 }
214 }
215 }