1
2
3
4
5
6
7
8
9
10
11
12
13
14
15 package io.netty.handler.codec.http2;
16
17 import io.netty.buffer.ByteBuf;
18 import io.netty.channel.ChannelHandler;
19 import io.netty.channel.ChannelHandlerContext;
20 import io.netty.handler.codec.base64.Base64;
21 import io.netty.handler.codec.http.FullHttpResponse;
22 import io.netty.handler.codec.http.HttpClientUpgradeHandler;
23 import io.netty.handler.codec.http.HttpRequest;
24 import io.netty.util.collection.CharObjectMap;
25
26 import java.util.Collection;
27 import java.util.Collections;
28 import java.util.List;
29
30 import static io.netty.handler.codec.base64.Base64Dialect.URL_SAFE;
31 import static io.netty.handler.codec.http2.Http2CodecUtil.HTTP_UPGRADE_PROTOCOL_NAME;
32 import static io.netty.handler.codec.http2.Http2CodecUtil.HTTP_UPGRADE_SETTINGS_HEADER;
33 import static io.netty.handler.codec.http2.Http2CodecUtil.SETTING_ENTRY_LENGTH;
34 import static io.netty.util.CharsetUtil.UTF_8;
35 import static io.netty.util.ReferenceCountUtil.release;
36 import static io.netty.util.internal.ObjectUtil.checkNotNull;
37
38
39
40
41 public class Http2ClientUpgradeCodec implements HttpClientUpgradeHandler.UpgradeCodec {
42
43 private static final List<CharSequence> UPGRADE_HEADERS = Collections.singletonList(HTTP_UPGRADE_SETTINGS_HEADER);
44
45 private final String handlerName;
46 private final Http2ConnectionHandler connectionHandler;
47 private final ChannelHandler upgradeToHandler;
48 private final ChannelHandler http2MultiplexHandler;
49
50 public Http2ClientUpgradeCodec(Http2FrameCodec frameCodec, ChannelHandler upgradeToHandler) {
51 this(null, frameCodec, upgradeToHandler);
52 }
53
54 public Http2ClientUpgradeCodec(String handlerName, Http2FrameCodec frameCodec, ChannelHandler upgradeToHandler) {
55 this(handlerName, (Http2ConnectionHandler) frameCodec, upgradeToHandler, null);
56 }
57
58
59
60
61
62
63
64 public Http2ClientUpgradeCodec(Http2ConnectionHandler connectionHandler) {
65 this((String) null, connectionHandler);
66 }
67
68
69
70
71
72
73
74
75 public Http2ClientUpgradeCodec(Http2ConnectionHandler connectionHandler,
76 Http2MultiplexHandler http2MultiplexHandler) {
77 this((String) null, connectionHandler, http2MultiplexHandler);
78 }
79
80
81
82
83
84
85
86
87 public Http2ClientUpgradeCodec(String handlerName, Http2ConnectionHandler connectionHandler) {
88 this(handlerName, connectionHandler, connectionHandler, null);
89 }
90
91
92
93
94
95
96
97
98 public Http2ClientUpgradeCodec(String handlerName, Http2ConnectionHandler connectionHandler,
99 Http2MultiplexHandler http2MultiplexHandler) {
100 this(handlerName, connectionHandler, connectionHandler, http2MultiplexHandler);
101 }
102
103 private Http2ClientUpgradeCodec(String handlerName, Http2ConnectionHandler connectionHandler, ChannelHandler
104 upgradeToHandler, Http2MultiplexHandler http2MultiplexHandler) {
105 this.handlerName = handlerName;
106 this.connectionHandler = checkNotNull(connectionHandler, "connectionHandler");
107 this.upgradeToHandler = checkNotNull(upgradeToHandler, "upgradeToHandler");
108 this.http2MultiplexHandler = http2MultiplexHandler;
109 }
110
111 @Override
112 public CharSequence protocol() {
113 return HTTP_UPGRADE_PROTOCOL_NAME;
114 }
115
116 @Override
117 public Collection<CharSequence> setUpgradeHeaders(ChannelHandlerContext ctx,
118 HttpRequest upgradeRequest) {
119 CharSequence settingsValue = getSettingsHeaderValue(ctx);
120 upgradeRequest.headers().set(HTTP_UPGRADE_SETTINGS_HEADER, settingsValue);
121 return UPGRADE_HEADERS;
122 }
123
124 @Override
125 public void upgradeTo(ChannelHandlerContext ctx, FullHttpResponse upgradeResponse)
126 throws Exception {
127 try {
128
129 ctx.pipeline().addAfter(ctx.name(), handlerName, upgradeToHandler);
130
131
132
133 if (http2MultiplexHandler != null) {
134 final String name = ctx.pipeline().context(connectionHandler).name();
135 ctx.pipeline().addAfter(name, null, http2MultiplexHandler);
136 }
137
138
139 connectionHandler.onHttpClientUpgrade();
140 } catch (Http2Exception e) {
141 ctx.fireExceptionCaught(e);
142 ctx.close();
143 }
144 }
145
146
147
148
149
150 private CharSequence getSettingsHeaderValue(ChannelHandlerContext ctx) {
151 ByteBuf buf = null;
152 ByteBuf encodedBuf = null;
153 try {
154
155 Http2Settings settings = connectionHandler.decoder().localSettings();
156
157
158 int payloadLength = SETTING_ENTRY_LENGTH * settings.size();
159 buf = ctx.alloc().buffer(payloadLength);
160 for (CharObjectMap.PrimitiveEntry<Long> entry : settings.entries()) {
161 buf.writeChar(entry.key());
162 buf.writeInt(entry.value().intValue());
163 }
164
165
166 encodedBuf = Base64.encode(buf, URL_SAFE);
167 return encodedBuf.toString(UTF_8);
168 } finally {
169 release(buf);
170 release(encodedBuf);
171 }
172 }
173 }