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