1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package io.netty.handler.codec.http2;
17
18 import io.netty.channel.Channel;
19 import io.netty.channel.ChannelFuture;
20 import io.netty.channel.ChannelFutureListener;
21 import io.netty.channel.ChannelHandler;
22 import io.netty.channel.ChannelHandlerContext;
23 import io.netty.channel.ChannelOption;
24 import io.netty.channel.ChannelPipeline;
25 import io.netty.util.AttributeKey;
26 import io.netty.util.concurrent.EventExecutor;
27 import io.netty.util.concurrent.Future;
28 import io.netty.util.concurrent.Promise;
29 import io.netty.util.internal.ObjectUtil;
30 import io.netty.util.internal.StringUtil;
31 import io.netty.util.internal.UnstableApi;
32 import io.netty.util.internal.logging.InternalLogger;
33 import io.netty.util.internal.logging.InternalLoggerFactory;
34
35 import java.nio.channels.ClosedChannelException;
36 import java.util.LinkedHashMap;
37 import java.util.Map;
38 import java.util.concurrent.ConcurrentHashMap;
39
40 @UnstableApi
41 public final class Http2StreamChannelBootstrap {
42 private static final InternalLogger logger = InternalLoggerFactory.getInstance(Http2StreamChannelBootstrap.class);
43 @SuppressWarnings("unchecked")
44 private static final Map.Entry<ChannelOption<?>, Object>[] EMPTY_OPTION_ARRAY = new Map.Entry[0];
45 @SuppressWarnings("unchecked")
46 private static final Map.Entry<AttributeKey<?>, Object>[] EMPTY_ATTRIBUTE_ARRAY = new Map.Entry[0];
47
48
49
50 private final Map<ChannelOption<?>, Object> options = new LinkedHashMap<ChannelOption<?>, Object>();
51 private final Map<AttributeKey<?>, Object> attrs = new ConcurrentHashMap<AttributeKey<?>, Object>();
52 private final Channel channel;
53 private volatile ChannelHandler handler;
54
55
56 private volatile ChannelHandlerContext multiplexCtx;
57
58 public Http2StreamChannelBootstrap(Channel channel) {
59 this.channel = ObjectUtil.checkNotNull(channel, "channel");
60 }
61
62
63
64
65
66 public <T> Http2StreamChannelBootstrap option(ChannelOption<T> option, T value) {
67 ObjectUtil.checkNotNull(option, "option");
68
69 synchronized (options) {
70 if (value == null) {
71 options.remove(option);
72 } else {
73 options.put(option, value);
74 }
75 }
76 return this;
77 }
78
79
80
81
82
83 public <T> Http2StreamChannelBootstrap attr(AttributeKey<T> key, T value) {
84 ObjectUtil.checkNotNull(key, "key");
85 if (value == null) {
86 attrs.remove(key);
87 } else {
88 attrs.put(key, value);
89 }
90 return this;
91 }
92
93
94
95
96 public Http2StreamChannelBootstrap handler(ChannelHandler handler) {
97 this.handler = ObjectUtil.checkNotNull(handler, "handler");
98 return this;
99 }
100
101
102
103
104
105 public Future<Http2StreamChannel> open() {
106 return open(channel.eventLoop().<Http2StreamChannel>newPromise());
107 }
108
109
110
111
112
113 public Future<Http2StreamChannel> open(final Promise<Http2StreamChannel> promise) {
114 try {
115 ChannelHandlerContext ctx = findCtx();
116 EventExecutor executor = ctx.executor();
117 if (executor.inEventLoop()) {
118 open0(ctx, promise);
119 } else {
120 final ChannelHandlerContext finalCtx = ctx;
121 executor.execute(new Runnable() {
122 @Override
123 public void run() {
124 if (channel.isActive()) {
125 open0(finalCtx, promise);
126 } else {
127 promise.setFailure(new ClosedChannelException());
128 }
129 }
130 });
131 }
132 } catch (Throwable cause) {
133 promise.setFailure(cause);
134 }
135 return promise;
136 }
137
138 @SuppressWarnings("deprecation")
139 private ChannelHandlerContext findCtx() throws ClosedChannelException {
140
141 ChannelHandlerContext ctx = multiplexCtx;
142 if (ctx != null && !ctx.isRemoved()) {
143 return ctx;
144 }
145 ChannelPipeline pipeline = channel.pipeline();
146 ctx = pipeline.context(Http2MultiplexCodec.class);
147 if (ctx == null) {
148 ctx = pipeline.context(Http2MultiplexHandler.class);
149 }
150 if (ctx == null) {
151 if (channel.isActive()) {
152 throw new IllegalStateException(StringUtil.simpleClassName(Http2MultiplexCodec.class) + " or "
153 + StringUtil.simpleClassName(Http2MultiplexHandler.class)
154 + " must be in the ChannelPipeline of Channel " + channel);
155 } else {
156 throw new ClosedChannelException();
157 }
158 }
159 multiplexCtx = ctx;
160 return ctx;
161 }
162
163
164
165
166 @Deprecated
167 public void open0(ChannelHandlerContext ctx, final Promise<Http2StreamChannel> promise) {
168 assert ctx.executor().inEventLoop();
169 if (!promise.setUncancellable()) {
170 return;
171 }
172 final Http2StreamChannel streamChannel;
173 try {
174 if (ctx.handler() instanceof Http2MultiplexCodec) {
175 streamChannel = ((Http2MultiplexCodec) ctx.handler()).newOutboundStream();
176 } else {
177 streamChannel = ((Http2MultiplexHandler) ctx.handler()).newOutboundStream();
178 }
179 } catch (Exception e) {
180 promise.setFailure(e);
181 return;
182 }
183 try {
184 init(streamChannel);
185 } catch (Exception e) {
186 streamChannel.unsafe().closeForcibly();
187 promise.setFailure(e);
188 return;
189 }
190
191 ChannelFuture future = ctx.channel().eventLoop().register(streamChannel);
192 future.addListener(new ChannelFutureListener() {
193 @Override
194 public void operationComplete(ChannelFuture future) {
195 if (future.isSuccess()) {
196 promise.setSuccess(streamChannel);
197 } else if (future.isCancelled()) {
198 promise.cancel(false);
199 } else {
200 if (streamChannel.isRegistered()) {
201 streamChannel.close();
202 } else {
203 streamChannel.unsafe().closeForcibly();
204 }
205
206 promise.setFailure(future.cause());
207 }
208 }
209 });
210 }
211
212 private void init(Channel channel) {
213 ChannelPipeline p = channel.pipeline();
214 ChannelHandler handler = this.handler;
215 if (handler != null) {
216 p.addLast(handler);
217 }
218 final Map.Entry<ChannelOption<?>, Object> [] optionArray;
219 synchronized (options) {
220 optionArray = options.entrySet().toArray(EMPTY_OPTION_ARRAY);
221 }
222
223 setChannelOptions(channel, optionArray);
224 setAttributes(channel, attrs.entrySet().toArray(EMPTY_ATTRIBUTE_ARRAY));
225 }
226
227 private static void setChannelOptions(
228 Channel channel, Map.Entry<ChannelOption<?>, Object>[] options) {
229 for (Map.Entry<ChannelOption<?>, Object> e: options) {
230 setChannelOption(channel, e.getKey(), e.getValue());
231 }
232 }
233
234 private static void setChannelOption(
235 Channel channel, ChannelOption<?> option, Object value) {
236 try {
237 @SuppressWarnings("unchecked")
238 ChannelOption<Object> opt = (ChannelOption<Object>) option;
239 if (!channel.config().setOption(opt, value)) {
240 logger.warn("Unknown channel option '{}' for channel '{}'", option, channel);
241 }
242 } catch (Throwable t) {
243 logger.warn(
244 "Failed to set channel option '{}' with value '{}' for channel '{}'", option, value, channel, t);
245 }
246 }
247
248 private static void setAttributes(
249 Channel channel, Map.Entry<AttributeKey<?>, Object>[] options) {
250 for (Map.Entry<AttributeKey<?>, Object> e: options) {
251 @SuppressWarnings("unchecked")
252 AttributeKey<Object> key = (AttributeKey<Object>) e.getKey();
253 channel.attr(key).set(e.getValue());
254 }
255 }
256 }