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.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 }