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.channel.Channel;
19  import io.netty.channel.ChannelHandler;
20  import io.netty.handler.codec.quic.QuicChannel;
21  import io.netty.handler.codec.quic.QuicClientCodecBuilder;
22  import io.netty.handler.codec.quic.QuicCodecBuilder;
23  import io.netty.handler.codec.quic.QuicServerCodecBuilder;
24  import io.netty.handler.codec.quic.QuicStreamChannel;
25  import io.netty.handler.codec.quic.QuicStreamChannelBootstrap;
26  import io.netty.handler.codec.quic.QuicStreamType;
27  import io.netty.util.AttributeKey;
28  import io.netty.util.concurrent.Future;
29  import org.jetbrains.annotations.Nullable;
30  
31  /**
32   * Contains utility methods that help to bootstrap server / clients with HTTP3 support.
33   */
34  public final class Http3 {
35  
36      private Http3() {  }
37  
38      private static final String[] H3_PROTOS = new String[] {
39              "h3-29",
40              "h3-30",
41              "h3-31",
42              "h3-32",
43              "h3"
44      };
45  
46      private static final AttributeKey<QuicStreamChannel> HTTP3_CONTROL_STREAM_KEY =
47              AttributeKey.valueOf(Http3.class, "HTTP3ControlStream");
48  
49      private static final AttributeKey<QpackAttributes> QPACK_ATTRIBUTES_KEY =
50              AttributeKey.valueOf(Http3.class, "QpackAttributes");
51  
52      /**
53       * Returns the local initiated control stream for the HTTP/3 connection.
54       * @param channel   the channel for the HTTP/3 connection.
55       * @return          the control stream.
56       */
57      @Nullable
58      public static QuicStreamChannel getLocalControlStream(Channel channel) {
59          return channel.attr(HTTP3_CONTROL_STREAM_KEY).get();
60      }
61  
62      /**
63       * Returns the value of the <a
64       * href="https://quicwg.org/base-drafts/draft-ietf-quic-http.html#name-max_push_id">max push ID</a> received for
65       * this connection.
66       *
67       * @return Received <a
68       * href="https://quicwg.org/base-drafts/draft-ietf-quic-http.html#name-max_push_id">max push ID</a> for this
69       * connection.
70       */
71      static long maxPushIdReceived(QuicChannel channel) {
72          final Http3ConnectionHandler connectionHandler = Http3CodecUtils.getConnectionHandlerOrClose(channel);
73          if (connectionHandler == null) {
74              throw new IllegalStateException("Connection handler not found.");
75          }
76          return connectionHandler.localControlStreamHandler.maxPushIdReceived();
77      }
78  
79      static void setLocalControlStream(Channel channel, QuicStreamChannel controlStreamChannel) {
80          channel.attr(HTTP3_CONTROL_STREAM_KEY).set(controlStreamChannel);
81      }
82  
83      @Nullable
84      static QpackAttributes getQpackAttributes(Channel channel) {
85          return channel.attr(QPACK_ATTRIBUTES_KEY).get();
86      }
87  
88      static void setQpackAttributes(Channel channel, QpackAttributes attributes) {
89          channel.attr(QPACK_ATTRIBUTES_KEY).set(attributes);
90      }
91  
92      /**
93       * Returns a new HTTP/3 request-stream that will use the given {@link ChannelHandler}
94       * to dispatch {@link Http3RequestStreamFrame}s too. The needed HTTP/3 codecs are automatically added to the
95       * pipeline as well.
96       *
97       * If you need more control you can also use the {@link Http3RequestStreamInitializer} directly.
98       *
99       * @param channel   the {@link QuicChannel} for which we create the request-stream.
100      * @param handler   the {@link ChannelHandler} to add.
101      * @return          the {@link Future} that will be notified once the request-stream was opened.
102      */
103     public static Future<QuicStreamChannel> newRequestStream(QuicChannel channel, ChannelHandler handler) {
104         return channel.createStream(QuicStreamType.BIDIRECTIONAL, requestStreamInitializer(handler));
105     }
106 
107     /**
108      * Returns a new HTTP/3 request-stream bootstrap that will use the given {@link ChannelHandler}
109      * to dispatch {@link Http3RequestStreamFrame}s too. The needed HTTP/3 codecs are automatically added to the
110      * pipeline as well.
111      *
112      * If you need more control you can also use the {@link Http3RequestStreamInitializer} directly.
113      *
114      * @param channel   the {@link QuicChannel} for which we create the request-stream.
115      * @param handler   the {@link ChannelHandler} to add.
116      * @return          the {@link QuicStreamChannelBootstrap} that should be used.
117      */
118     public static QuicStreamChannelBootstrap newRequestStreamBootstrap(QuicChannel channel, ChannelHandler handler) {
119         return channel.newStreamBootstrap().handler(requestStreamInitializer(handler))
120                 .type(QuicStreamType.BIDIRECTIONAL);
121     }
122 
123     /**
124      * Returns the supported protocols for H3.
125      *
126      * @return the supported protocols.
127      */
128     public static String[] supportedApplicationProtocols() {
129         return H3_PROTOS.clone();
130     }
131 
132     /**
133      * <a href="https://tools.ietf.org/html/draft-ietf-quic-http-32#section-6.2">
134      *     Minimum number max unidirectional streams</a>.
135      */
136     // control-stream, qpack decoder stream, qpack encoder stream
137     public static final int MIN_INITIAL_MAX_STREAMS_UNIDIRECTIONAL = 3;
138 
139     /**
140      * <a href="https://tools.ietf.org/html/draft-ietf-quic-http-32#section-6.2">
141      *     Minimum max data for unidirectional streams</a>.
142      */
143     public static final int MIN_INITIAL_MAX_STREAM_DATA_UNIDIRECTIONAL = 1024;
144 
145     /**
146      * Returns a new {@link QuicServerCodecBuilder} that has preconfigured for HTTP3.
147      *
148      * @return a pre-configured builder for HTTP3.
149      */
150     public static QuicServerCodecBuilder newQuicServerCodecBuilder() {
151         return configure(new QuicServerCodecBuilder());
152     }
153 
154     /**
155      * Returns a new {@link QuicClientCodecBuilder} that has preconfigured for HTTP3.
156      *
157      * @return a pre-configured builder for HTTP3.
158      */
159     public static QuicClientCodecBuilder newQuicClientCodecBuilder() {
160         return configure(new QuicClientCodecBuilder());
161     }
162 
163     private static <T extends QuicCodecBuilder<T>> T configure(T builder) {
164         return builder.initialMaxStreamsUnidirectional(MIN_INITIAL_MAX_STREAMS_UNIDIRECTIONAL)
165                 .initialMaxStreamDataUnidirectional(MIN_INITIAL_MAX_STREAM_DATA_UNIDIRECTIONAL);
166     }
167 
168     private static Http3RequestStreamInitializer requestStreamInitializer(ChannelHandler handler) {
169         if (handler instanceof Http3RequestStreamInitializer) {
170             return (Http3RequestStreamInitializer) handler;
171         }
172         return new Http3RequestStreamInitializer() {
173             @Override
174             protected void initRequestStream(QuicStreamChannel ch) {
175                 ch.pipeline().addLast(handler);
176             }
177         };
178     }
179 }