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