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