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.quic;
17
18 import io.netty.channel.Channel;
19 import io.netty.channel.ChannelFuture;
20 import io.netty.channel.ChannelHandler;
21 import io.netty.channel.ChannelOption;
22 import io.netty.channel.EventLoop;
23 import io.netty.util.AttributeKey;
24 import io.netty.util.concurrent.Future;
25 import io.netty.util.concurrent.Promise;
26 import io.netty.util.internal.ObjectUtil;
27 import io.netty.util.internal.logging.InternalLogger;
28 import io.netty.util.internal.logging.InternalLoggerFactory;
29 import org.jetbrains.annotations.Nullable;
30
31 import java.net.InetSocketAddress;
32 import java.net.SocketAddress;
33 import java.util.HashMap;
34 import java.util.LinkedHashMap;
35 import java.util.Map;
36
37 /**
38 * Bootstrap that helps to bootstrap {@link QuicChannel}s and connecting these to remote peers.
39 */
40 public final class QuicChannelBootstrap {
41 private static final InternalLogger logger = InternalLoggerFactory.getInstance(QuicChannelBootstrap.class);
42
43 private final Channel parent;
44 // The order in which ChannelOptions are applied is important they may depend on each other for validation
45 // purposes.
46 private final Map<ChannelOption<?>, Object> options = new LinkedHashMap<>();
47 private final Map<AttributeKey<?>, Object> attrs = new HashMap<>();
48 private final Map<ChannelOption<?>, Object> streamOptions = new LinkedHashMap<>();
49 private final Map<AttributeKey<?>, Object> streamAttrs = new HashMap<>();
50
51 private SocketAddress local;
52 private SocketAddress remote;
53 private QuicConnectionAddress localAddress = QuicConnectionAddress.EPHEMERAL;
54 private QuicConnectionAddress remoteAddress = QuicConnectionAddress.EPHEMERAL;
55 private ChannelHandler handler;
56 private ChannelHandler streamHandler;
57
58 /**
59 * Creates a new instance which uses the given {@link Channel} to bootstrap the {@link QuicChannel}.
60 * This {@link io.netty.channel.ChannelPipeline} of the {@link Channel} needs to have the quic codec in the
61 * pipeline.
62 *
63 * @param parent the {@link Channel} that is used as the transport layer.
64 * @deprecated Use QuicChannel.newBootstrap() instead.
65 */
66 @Deprecated
67 public QuicChannelBootstrap(Channel parent) {
68 Quic.ensureAvailability();
69 this.parent = ObjectUtil.checkNotNull(parent, "parent");
70 }
71
72 /**
73 * Allow to specify a {@link ChannelOption} which is used for the {@link QuicChannel} instances once they got
74 * created. Use a value of {@code null} to remove a previous set {@link ChannelOption}.
75 *
76 * @param option the {@link ChannelOption} to apply to the {@link QuicChannel}.
77 * @param value the value of the option.
78 * @param <T> the type of the value.
79 * @return this instance.
80 */
81 public <T> QuicChannelBootstrap option(ChannelOption<T> option, @Nullable T value) {
82 Quic.updateOptions(options, option, value);
83 return this;
84 }
85
86 /**
87 * Allow to specify an initial attribute of the newly created {@link QuicChannel}. If the {@code value} is
88 * {@code null}, the attribute of the specified {@code key} is removed.
89 *
90 * @param key the {@link AttributeKey} to apply to the {@link QuicChannel}.
91 * @param value the value of the attribute.
92 * @param <T> the type of the value.
93 * @return this instance.
94 */
95 public <T> QuicChannelBootstrap attr(AttributeKey<T> key, @Nullable T value) {
96 Quic.updateAttributes(attrs, key, value);
97 return this;
98 }
99
100 /**
101 * Set the {@link ChannelHandler} that is added to the {@link io.netty.channel.ChannelPipeline} of the
102 * {@link QuicChannel} once created.
103 *
104 * @param handler the {@link ChannelHandler} that is added to the {@link QuicChannel}s
105 * {@link io.netty.channel.ChannelPipeline}.
106 * @return this instance.
107 */
108 public QuicChannelBootstrap handler(ChannelHandler handler) {
109 this.handler = ObjectUtil.checkNotNull(handler, "handler");
110 return this;
111 }
112
113 /**
114 * Allow to specify a {@link ChannelOption} which is used for the {@link QuicStreamChannel} instances once they got
115 * created. Use a value of {@code null} to remove a previous set {@link ChannelOption}.
116 *
117 * @param option the {@link ChannelOption} to apply to the {@link QuicStreamChannel}s.
118 * @param value the value of the option.
119 * @param <T> the type of the value.
120 * @return this instance.
121 */
122 public <T> QuicChannelBootstrap streamOption(ChannelOption<T> option, @Nullable T value) {
123 Quic.updateOptions(streamOptions, option, value);
124 return this;
125 }
126
127 /**
128 * Allow to specify an initial attribute of the newly created {@link QuicStreamChannel}. If the {@code value} is
129 * {@code null}, the attribute of the specified {@code key} is removed.
130 *
131 * @param key the {@link AttributeKey} to apply to the {@link QuicStreamChannel}s.
132 * @param value the value of the attribute.
133 * @param <T> the type of the value.
134 * @return this instance.
135 */
136 public <T> QuicChannelBootstrap streamAttr(AttributeKey<T> key, @Nullable T value) {
137 Quic.updateAttributes(streamAttrs, key, value);
138 return this;
139 }
140
141 /**
142 * Set the {@link ChannelHandler} that is added to the {@link io.netty.channel.ChannelPipeline} of the
143 * {@link QuicStreamChannel} once created.
144 *
145 * @param streamHandler the {@link ChannelHandler} that is added to the {@link QuicStreamChannel}s
146 * {@link io.netty.channel.ChannelPipeline}.
147 * @return this instance.
148 */
149 public QuicChannelBootstrap streamHandler(ChannelHandler streamHandler) {
150 this.streamHandler = ObjectUtil.checkNotNull(streamHandler, "streamHandler");
151 return this;
152 }
153
154 /**
155 * Set the local address.
156 *
157 * @param local the {@link SocketAddress} of the local peer.
158 * @return this instance.
159 */
160 public QuicChannelBootstrap localAddress(SocketAddress local) {
161 this.local = ObjectUtil.checkNotNull(local, "local");
162 return this;
163 }
164
165 /**
166 * Set the remote address of the host to talk to.
167 *
168 * @param remote the {@link SocketAddress} of the remote peer.
169 * @return this instance.
170 */
171 public QuicChannelBootstrap remoteAddress(SocketAddress remote) {
172 this.remote = ObjectUtil.checkNotNull(remote, "remote");
173 return this;
174 }
175
176 /**
177 * Set the {@link QuicConnectionAddress} to use. If none is specified a random address is generated on your
178 * behalf.
179 *
180 * @param connectionAddress the {@link QuicConnectionAddress} to use.
181 * @return this instance.
182 * @deprecated use {@link #localConnectionAddress(QuicConnectionAddress)}.
183 */
184 @Deprecated
185 public QuicChannelBootstrap connectionAddress(QuicConnectionAddress connectionAddress) {
186 this.localAddress = ObjectUtil.checkNotNull(connectionAddress, "connectionAddress");
187 return this;
188 }
189
190 /**
191 * Set the {@link QuicConnectionAddress} to use. If none is specified a random address is generated on your
192 * behalf.
193 *
194 * @param localConnectionAddress the {@link QuicConnectionAddress} to use.
195 * @return this instance.
196 */
197 public QuicChannelBootstrap localConnectionAddress(QuicConnectionAddress localConnectionAddress) {
198 this.localAddress = ObjectUtil.checkNotNull(localConnectionAddress, "localConnectionAddress");
199 return this;
200 }
201
202 /**
203 * Set the {@link QuicConnectionAddress} to use. If none is specified a random address is generated on your
204 * behalf. Be aware that the provided {@link QuicConnectionAddress} should be generated carefully in a random way
205 * that makes it impossible to predict it as otherwise the security of the quic connection might be too weak.
206 * Fore more details see
207 * <a href="https://datatracker.ietf.org/doc/html/rfc9000#section-7.2">RFC9000 Section 7.2</a>.
208 *
209 * @param remoteConnectionAddress the {@link QuicConnectionAddress} to use.
210 * @return this instance.
211 */
212 public QuicChannelBootstrap remoteConnectionAddress(QuicConnectionAddress remoteConnectionAddress) {
213 this.remoteAddress = ObjectUtil.checkNotNull(remoteConnectionAddress, "remoteConnectionAddress");
214 return this;
215 }
216
217 /**
218 * Connects a {@link QuicChannel} to the remote peer and notifies the future once done.
219 *
220 * @return {@link Future} which is notified once the operation completes.
221 */
222 public Future<QuicChannel> connect() {
223 return connect(parent.eventLoop().newPromise());
224 }
225
226 /**
227 * Connects a {@link QuicChannel} to the remote peer and notifies the promise once done.
228 *
229 * @param promise the {@link Promise} which is notified once the operations completes.
230 * @return {@link Future} which is notified once the operation completes.
231
232 */
233 public Future<QuicChannel> connect(Promise<QuicChannel> promise) {
234 if (handler == null && streamHandler == null) {
235 throw new IllegalStateException("handler and streamHandler not set");
236 }
237 SocketAddress local = this.local;
238 if (local == null) {
239 local = parent.localAddress();
240 }
241 if (local == null) {
242 local = new InetSocketAddress(0);
243 }
244
245 SocketAddress remote = this.remote;
246 if (remote == null) {
247 remote = parent.remoteAddress();
248 }
249 if (remote == null) {
250 throw new IllegalStateException("remote not set");
251 }
252
253 final QuicConnectionAddress localaddress = localAddress;
254 final QuicConnectionAddress remoteaddress = remoteAddress;
255 QuicChannel channel = QuicheQuicChannel.forClient(parent, (InetSocketAddress) local,
256 (InetSocketAddress) remote,
257 streamHandler, Quic.toOptionsArray(streamOptions), Quic.toAttributesArray(streamAttrs));
258
259 Quic.setupChannel(channel, Quic.toOptionsArray(options), Quic.toAttributesArray(attrs), handler, logger);
260 EventLoop eventLoop = parent.eventLoop();
261 eventLoop.register(channel).addListener((ChannelFuture future) -> {
262 Throwable cause = future.cause();
263 if (cause != null) {
264 promise.setFailure(cause);
265 } else {
266 channel.connect(remoteaddress, localaddress).addListener(f -> {
267 Throwable error = f.cause();
268 if (error != null) {
269 promise.setFailure(error);
270 } else {
271 promise.setSuccess(channel);
272 }
273 });
274 }
275 });
276 return promise;
277 }
278 }