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 connectionAddress = QuicConnectionAddress.EPHEMERAL;
54 private ChannelHandler handler;
55 private ChannelHandler streamHandler;
56
57 /**
58 * Creates a new instance which uses the given {@link Channel} to bootstrap the {@link QuicChannel}.
59 * This {@link io.netty.channel.ChannelPipeline} of the {@link Channel} needs to have the quic codec in the
60 * pipeline.
61 *
62 * @param parent the {@link Channel} that is used as the transport layer.
63 * @deprecated Use QuicChannel.newBootstrap() instead.
64 */
65 @Deprecated
66 public QuicChannelBootstrap(Channel parent) {
67 Quic.ensureAvailability();
68 this.parent = ObjectUtil.checkNotNull(parent, "parent");
69 }
70
71 /**
72 * Allow to specify a {@link ChannelOption} which is used for the {@link QuicChannel} instances once they got
73 * created. Use a value of {@code null} to remove a previous set {@link ChannelOption}.
74 *
75 * @param option the {@link ChannelOption} to apply to the {@link QuicChannel}.
76 * @param value the value of the option.
77 * @param <T> the type of the value.
78 * @return this instance.
79 */
80 public <T> QuicChannelBootstrap option(ChannelOption<T> option, @Nullable T value) {
81 Quic.updateOptions(options, option, value);
82 return this;
83 }
84
85 /**
86 * Allow to specify an initial attribute of the newly created {@link QuicChannel}. If the {@code value} is
87 * {@code null}, the attribute of the specified {@code key} is removed.
88 *
89 * @param key the {@link AttributeKey} to apply to the {@link QuicChannel}.
90 * @param value the value of the attribute.
91 * @param <T> the type of the value.
92 * @return this instance.
93 */
94 public <T> QuicChannelBootstrap attr(AttributeKey<T> key, @Nullable T value) {
95 Quic.updateAttributes(attrs, key, value);
96 return this;
97 }
98
99 /**
100 * Set the {@link ChannelHandler} that is added to the {@link io.netty.channel.ChannelPipeline} of the
101 * {@link QuicChannel} once created.
102 *
103 * @param handler the {@link ChannelHandler} that is added to the {@link QuicChannel}s
104 * {@link io.netty.channel.ChannelPipeline}.
105 * @return this instance.
106 */
107 public QuicChannelBootstrap handler(ChannelHandler handler) {
108 this.handler = ObjectUtil.checkNotNull(handler, "handler");
109 return this;
110 }
111
112 /**
113 * Allow to specify a {@link ChannelOption} which is used for the {@link QuicStreamChannel} instances once they got
114 * created. Use a value of {@code null} to remove a previous set {@link ChannelOption}.
115 *
116 * @param option the {@link ChannelOption} to apply to the {@link QuicStreamChannel}s.
117 * @param value the value of the option.
118 * @param <T> the type of the value.
119 * @return this instance.
120 */
121 public <T> QuicChannelBootstrap streamOption(ChannelOption<T> option, @Nullable T value) {
122 Quic.updateOptions(streamOptions, option, value);
123 return this;
124 }
125
126 /**
127 * Allow to specify an initial attribute of the newly created {@link QuicStreamChannel}. If the {@code value} is
128 * {@code null}, the attribute of the specified {@code key} is removed.
129 *
130 * @param key the {@link AttributeKey} to apply to the {@link QuicStreamChannel}s.
131 * @param value the value of the attribute.
132 * @param <T> the type of the value.
133 * @return this instance.
134 */
135 public <T> QuicChannelBootstrap streamAttr(AttributeKey<T> key, @Nullable T value) {
136 Quic.updateAttributes(streamAttrs, key, value);
137 return this;
138 }
139
140 /**
141 * Set the {@link ChannelHandler} that is added to the {@link io.netty.channel.ChannelPipeline} of the
142 * {@link QuicStreamChannel} once created.
143 *
144 * @param streamHandler the {@link ChannelHandler} that is added to the {@link QuicStreamChannel}s
145 * {@link io.netty.channel.ChannelPipeline}.
146 * @return this instance.
147 */
148 public QuicChannelBootstrap streamHandler(ChannelHandler streamHandler) {
149 this.streamHandler = ObjectUtil.checkNotNull(streamHandler, "streamHandler");
150 return this;
151 }
152
153 /**
154 * Set the local address.
155 *
156 * @param local the {@link SocketAddress} of the local peer.
157 * @return this instance.
158 */
159 public QuicChannelBootstrap localAddress(SocketAddress local) {
160 this.local = ObjectUtil.checkNotNull(local, "local");
161 return this;
162 }
163
164 /**
165 * Set the remote address of the host to talk to.
166 *
167 * @param remote the {@link SocketAddress} of the remote peer.
168 * @return this instance.
169 */
170 public QuicChannelBootstrap remoteAddress(SocketAddress remote) {
171 this.remote = ObjectUtil.checkNotNull(remote, "remote");
172 return this;
173 }
174
175 /**
176 * Set the {@link QuicConnectionAddress} to use. If none is specified a random address is generated on your
177 * behalf.
178 *
179 * @param connectionAddress the {@link QuicConnectionAddress} to use.
180 * @return this instance.
181 */
182 public QuicChannelBootstrap connectionAddress(QuicConnectionAddress connectionAddress) {
183 this.connectionAddress = ObjectUtil.checkNotNull(connectionAddress, "connectionAddress");
184 return this;
185 }
186
187 /**
188 * Connects a {@link QuicChannel} to the remote peer and notifies the future once done.
189 *
190 * @return {@link Future} which is notified once the operation completes.
191 */
192 public Future<QuicChannel> connect() {
193 return connect(parent.eventLoop().newPromise());
194 }
195
196 /**
197 * Connects a {@link QuicChannel} to the remote peer and notifies the promise once done.
198 *
199 * @param promise the {@link Promise} which is notified once the operations completes.
200 * @return {@link Future} which is notified once the operation completes.
201
202 */
203 public Future<QuicChannel> connect(Promise<QuicChannel> promise) {
204 if (handler == null && streamHandler == null) {
205 throw new IllegalStateException("handler and streamHandler not set");
206 }
207 SocketAddress local = this.local;
208 if (local == null) {
209 local = parent.localAddress();
210 }
211 if (local == null) {
212 local = new InetSocketAddress(0);
213 }
214
215 SocketAddress remote = this.remote;
216 if (remote == null) {
217 remote = parent.remoteAddress();
218 }
219 if (remote == null) {
220 throw new IllegalStateException("remote not set");
221 }
222
223 final QuicConnectionAddress address = connectionAddress;
224 QuicChannel channel = QuicheQuicChannel.forClient(parent, (InetSocketAddress) local,
225 (InetSocketAddress) remote,
226 streamHandler, Quic.toOptionsArray(streamOptions), Quic.toAttributesArray(streamAttrs));
227
228 Quic.setupChannel(channel, Quic.toOptionsArray(options), Quic.toAttributesArray(attrs), handler, logger);
229 EventLoop eventLoop = parent.eventLoop();
230 eventLoop.register(channel).addListener((ChannelFuture future) -> {
231 Throwable cause = future.cause();
232 if (cause != null) {
233 promise.setFailure(cause);
234 } else {
235 channel.connect(address).addListener(f -> {
236 Throwable error = f.cause();
237 if (error != null) {
238 promise.setFailure(error);
239 } else {
240 promise.setSuccess(channel);
241 }
242 });
243 }
244 });
245 return promise;
246 }
247 }