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