1 /*
2 * Copyright 2014 The Netty Project
3 *
4 * The Netty Project licenses this file to you under the Apache License, version 2.0 (the
5 * "License"); you may not use this file except in compliance with the License. You may obtain a
6 * copy of the License at:
7 *
8 * https://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software distributed under the License
11 * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
12 * or implied. See the License for the specific language governing permissions and limitations under
13 * the License.
14 */
15
16 package io.netty.handler.codec.http2;
17
18 import io.netty.handler.codec.DefaultHeaders;
19 import io.netty.handler.codec.Headers;
20 import io.netty.util.AsciiString;
21
22 import java.util.Iterator;
23 import java.util.Map.Entry;
24
25 /**
26 * A collection of headers sent or received via HTTP/2.
27 */
28 public interface Http2Headers extends Headers<CharSequence, CharSequence, Http2Headers> {
29
30 /**
31 * HTTP/2 pseudo-headers names.
32 */
33 enum PseudoHeaderName {
34 /**
35 * {@code :method}.
36 */
37 METHOD(":method", true),
38
39 /**
40 * {@code :scheme}.
41 */
42 SCHEME(":scheme", true),
43
44 /**
45 * {@code :authority}.
46 */
47 AUTHORITY(":authority", true),
48
49 /**
50 * {@code :path}.
51 */
52 PATH(":path", true),
53
54 /**
55 * {@code :status}.
56 */
57 STATUS(":status", false),
58
59 /**
60 * {@code :protocol}, as defined in <a href="https://datatracker.ietf.org/doc/rfc8441/">RFC 8441,
61 * Bootstrapping WebSockets with HTTP/2</a>.
62 */
63 PROTOCOL(":protocol", true);
64
65 private static final char PSEUDO_HEADER_PREFIX = ':';
66 private static final byte PSEUDO_HEADER_PREFIX_BYTE = (byte) PSEUDO_HEADER_PREFIX;
67
68 private final AsciiString value;
69 private final boolean requestOnly;
70
71 PseudoHeaderName(String value, boolean requestOnly) {
72 this.value = AsciiString.cached(value);
73 this.requestOnly = requestOnly;
74 }
75
76 public AsciiString value() {
77 // Return a slice so that the buffer gets its own reader index.
78 return value;
79 }
80
81 /**
82 * Indicates whether the specified header follows the pseudo-header format (begins with ':' character)
83 *
84 * @return {@code true} if the header follow the pseudo-header format
85 */
86 public static boolean hasPseudoHeaderFormat(CharSequence headerName) {
87 if (headerName instanceof AsciiString) {
88 final AsciiString asciiHeaderName = (AsciiString) headerName;
89 return asciiHeaderName.length() > 0 && asciiHeaderName.byteAt(0) == PSEUDO_HEADER_PREFIX_BYTE;
90 } else {
91 return headerName.length() > 0 && headerName.charAt(0) == PSEUDO_HEADER_PREFIX;
92 }
93 }
94
95 /**
96 * Indicates whether the given header name is a valid HTTP/2 pseudo header.
97 */
98 public static boolean isPseudoHeader(CharSequence header) {
99 return getPseudoHeader(header) != null;
100 }
101
102 /**
103 * Indicates whether the given header name is a valid HTTP/2 pseudo header.
104 */
105 public static boolean isPseudoHeader(AsciiString header) {
106 return getPseudoHeader(header) != null;
107 }
108
109 /**
110 * Indicates whether the given header name is a valid HTTP/2 pseudo header.
111 */
112 public static boolean isPseudoHeader(String header) {
113 return getPseudoHeader(header) != null;
114 }
115
116 /**
117 * Returns the {@link PseudoHeaderName} corresponding to the specified header name.
118 *
119 * @return corresponding {@link PseudoHeaderName} if any, {@code null} otherwise.
120 */
121 public static PseudoHeaderName getPseudoHeader(CharSequence header) {
122 if (header instanceof AsciiString) {
123 return getPseudoHeader((AsciiString) header);
124 }
125 return getPseudoHeaderName(header);
126 }
127
128 private static PseudoHeaderName getPseudoHeaderName(CharSequence header) {
129 int length = header.length();
130 if (length > 0 && header.charAt(0) == PSEUDO_HEADER_PREFIX) {
131 switch (length) {
132 case 5:
133 // :path
134 return ":path".contentEquals(header)? PATH : null;
135 case 7:
136 // :method, :scheme, :status
137 if (":method" == header) {
138 return METHOD;
139 }
140 if (":scheme" == header) {
141 return SCHEME;
142 }
143 if (":status" == header) {
144 return STATUS;
145 }
146 if (":method".contentEquals(header)) {
147 return METHOD;
148 }
149 if (":scheme".contentEquals(header)) {
150 return SCHEME;
151 }
152 return ":status".contentEquals(header)? STATUS : null;
153 case 9:
154 // :protocol
155 return ":protocol".contentEquals(header)? PROTOCOL : null;
156 case 10:
157 // :authority
158 return ":authority".contentEquals(header)? AUTHORITY : null;
159 }
160 }
161 return null;
162 }
163
164 /**
165 * Returns the {@link PseudoHeaderName} corresponding to the specified header name.
166 *
167 * @return corresponding {@link PseudoHeaderName} if any, {@code null} otherwise.
168 */
169 public static PseudoHeaderName getPseudoHeader(AsciiString header) {
170 int length = header.length();
171 if (length > 0 && header.charAt(0) == PSEUDO_HEADER_PREFIX) {
172 switch (length) {
173 case 5:
174 // :path
175 return PATH.value().equals(header) ? PATH : null;
176 case 7:
177 if (header == METHOD.value()) {
178 return METHOD;
179 }
180 if (header == SCHEME.value()) {
181 return SCHEME;
182 }
183 if (header == STATUS.value()) {
184 return STATUS;
185 }
186 // :method, :scheme, :status
187 if (METHOD.value().equals(header)) {
188 return METHOD;
189 }
190 if (SCHEME.value().equals(header)) {
191 return SCHEME;
192 }
193 return STATUS.value().equals(header)? STATUS : null;
194 case 9:
195 // :protocol
196 return PROTOCOL.value().equals(header)? PROTOCOL : null;
197 case 10:
198 // :authority
199 return AUTHORITY.value().equals(header)? AUTHORITY : null;
200 }
201 }
202 return null;
203 }
204
205 /**
206 * Indicates whether the pseudo-header is to be used in a request context.
207 *
208 * @return {@code true} if the pseudo-header is to be used in a request context
209 */
210 public boolean isRequestOnly() {
211 return requestOnly;
212 }
213 }
214
215 /**
216 * Returns an iterator over all HTTP/2 headers. The iteration order is as follows:
217 * 1. All pseudo headers (order not specified).
218 * 2. All non-pseudo headers (in insertion order).
219 */
220 @Override
221 Iterator<Entry<CharSequence, CharSequence>> iterator();
222
223 /**
224 * Equivalent to {@link #getAll(Object)} but no intermediate list is generated.
225 * @param name the name of the header to retrieve
226 * @return an {@link Iterator} of header values corresponding to {@code name}.
227 */
228 Iterator<CharSequence> valueIterator(CharSequence name);
229
230 /**
231 * Sets the {@link PseudoHeaderName#METHOD} header
232 */
233 Http2Headers method(CharSequence value);
234
235 /**
236 * Sets the {@link PseudoHeaderName#SCHEME} header
237 */
238 Http2Headers scheme(CharSequence value);
239
240 /**
241 * Sets the {@link PseudoHeaderName#AUTHORITY} header
242 */
243 Http2Headers authority(CharSequence value);
244
245 /**
246 * Sets the {@link PseudoHeaderName#PATH} header
247 */
248 Http2Headers path(CharSequence value);
249
250 /**
251 * Sets the {@link PseudoHeaderName#STATUS} header
252 */
253 Http2Headers status(CharSequence value);
254
255 /**
256 * Gets the {@link PseudoHeaderName#METHOD} header or {@code null} if there is no such header
257 */
258 CharSequence method();
259
260 /**
261 * Gets the {@link PseudoHeaderName#SCHEME} header or {@code null} if there is no such header
262 */
263 CharSequence scheme();
264
265 /**
266 * Gets the {@link PseudoHeaderName#AUTHORITY} header or {@code null} if there is no such header
267 */
268 CharSequence authority();
269
270 /**
271 * Gets the {@link PseudoHeaderName#PATH} header or {@code null} if there is no such header
272 */
273 CharSequence path();
274
275 /**
276 * Gets the {@link PseudoHeaderName#STATUS} header or {@code null} if there is no such header
277 */
278 CharSequence status();
279
280 /**
281 * Returns {@code true} if a header with the {@code name} and {@code value} exists, {@code false} otherwise.
282 * <p>
283 * If {@code caseInsensitive} is {@code true} then a case insensitive compare is done on the value.
284 *
285 * @param name the name of the header to find
286 * @param value the value of the header to find
287 * @param caseInsensitive {@code true} then a case insensitive compare is run to compare values.
288 * otherwise a case sensitive compare is run to compare values.
289 */
290 boolean contains(CharSequence name, CharSequence value, boolean caseInsensitive);
291 }