View Javadoc
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 }