1
2
3
4
5
6
7
8
9
10
11
12
13
14
15 package io.netty5.handler.codec.http2;
16
17 import io.netty5.buffer.api.Buffer;
18 import io.netty5.util.Send;
19 import io.netty5.channel.ChannelHandler;
20 import io.netty5.channel.ChannelHandlerContext;
21 import io.netty5.handler.codec.base64.Base64;
22 import io.netty5.handler.codec.http.FullHttpResponse;
23 import io.netty5.handler.codec.http.HttpClientUpgradeHandler;
24 import io.netty5.handler.codec.http.HttpRequest;
25 import io.netty5.util.collection.CharObjectMap;
26 import io.netty5.util.internal.UnstableApi;
27
28 import java.util.Collection;
29 import java.util.Collections;
30 import java.util.List;
31
32 import static io.netty5.handler.codec.base64.Base64Dialect.URL_SAFE;
33 import static io.netty5.handler.codec.http2.Http2CodecUtil.HTTP_UPGRADE_PROTOCOL_NAME;
34 import static io.netty5.handler.codec.http2.Http2CodecUtil.HTTP_UPGRADE_SETTINGS_HEADER;
35 import static io.netty5.handler.codec.http2.Http2CodecUtil.SETTING_ENTRY_LENGTH;
36 import static io.netty5.util.CharsetUtil.UTF_8;
37 import static java.util.Objects.requireNonNull;
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, 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(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 = requireNonNull(connectionHandler, "connectionHandler");
109 this.upgradeToHandler = requireNonNull(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, Send<FullHttpResponse> upgradeResponse)
128 throws Exception {
129 upgradeResponse.close();
130 try {
131
132 ctx.pipeline().addAfter(ctx.name(), handlerName, upgradeToHandler);
133
134
135
136 if (http2MultiplexHandler != null) {
137 final String name = ctx.pipeline().context(connectionHandler).name();
138 ctx.pipeline().addAfter(name, null, http2MultiplexHandler);
139 }
140
141
142 connectionHandler.onHttpClientUpgrade();
143 } catch (Http2Exception e) {
144 ctx.fireChannelExceptionCaught(e);
145 ctx.close();
146 }
147 }
148
149
150
151
152
153 private CharSequence getSettingsHeaderValue(ChannelHandlerContext ctx) {
154
155 Http2Settings settings = connectionHandler.decoder().localSettings();
156 int payloadLength = SETTING_ENTRY_LENGTH * settings.size();
157
158 try (Buffer buf = ctx.bufferAllocator().allocate(payloadLength)) {
159 for (CharObjectMap.PrimitiveEntry<Long> entry : settings.entries()) {
160 buf.writeChar(entry.key());
161 buf.writeInt(entry.value().intValue());
162 }
163
164
165 try (Buffer encodedBuf = Base64.encode(buf, URL_SAFE)) {
166 return encodedBuf.toString(UTF_8);
167 }
168 }
169 }
170 }