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