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