1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package io.netty5.handler.codec.http2;
17
18 import io.netty5.buffer.api.Buffer;
19 import io.netty5.buffer.api.BufferAllocator;
20 import io.netty5.channel.Channel;
21 import io.netty5.channel.ChannelHandler;
22 import io.netty5.channel.ChannelHandlerContext;
23 import io.netty5.handler.codec.EncoderException;
24 import io.netty5.handler.codec.MessageToMessageCodec;
25 import io.netty5.handler.codec.http.DefaultHttpContent;
26 import io.netty5.handler.codec.http.DefaultLastHttpContent;
27 import io.netty5.handler.codec.http.FullHttpMessage;
28 import io.netty5.handler.codec.http.FullHttpResponse;
29 import io.netty5.handler.codec.http.HttpContent;
30 import io.netty5.handler.codec.http.HttpHeaderNames;
31 import io.netty5.handler.codec.http.HttpHeaderValues;
32 import io.netty5.handler.codec.http.HttpMessage;
33 import io.netty5.handler.codec.http.HttpObject;
34 import io.netty5.handler.codec.http.HttpRequest;
35 import io.netty5.handler.codec.http.HttpResponse;
36 import io.netty5.handler.codec.http.HttpResponseStatus;
37 import io.netty5.handler.codec.http.HttpScheme;
38 import io.netty5.handler.codec.http.HttpUtil;
39 import io.netty5.handler.codec.http.HttpVersion;
40 import io.netty5.handler.codec.http.LastHttpContent;
41 import io.netty5.handler.ssl.SslHandler;
42 import io.netty5.util.Attribute;
43 import io.netty5.util.AttributeKey;
44 import io.netty5.util.internal.UnstableApi;
45
46 import java.util.List;
47
48
49
50
51
52
53
54
55
56
57 @UnstableApi
58 public class Http2StreamFrameToHttpObjectCodec extends MessageToMessageCodec<Http2StreamFrame, HttpObject> {
59
60 private static final AttributeKey<HttpScheme> SCHEME_ATTR_KEY =
61 AttributeKey.valueOf(HttpScheme.class, "STREAMFRAMECODEC_SCHEME");
62
63 private final boolean isServer;
64 private final boolean validateHeaders;
65
66 public Http2StreamFrameToHttpObjectCodec(final boolean isServer,
67 final boolean validateHeaders) {
68 this.isServer = isServer;
69 this.validateHeaders = validateHeaders;
70 }
71
72 public Http2StreamFrameToHttpObjectCodec(final boolean isServer) {
73 this(isServer, true);
74 }
75
76 @Override
77 public boolean isSharable() {
78 return true;
79 }
80
81 @Override
82 public boolean acceptInboundMessage(Object msg) throws Exception {
83 return msg instanceof Http2HeadersFrame || msg instanceof Http2DataFrame;
84 }
85
86 @Override
87 protected void decodeAndClose(ChannelHandlerContext ctx, Http2StreamFrame frame) throws Exception {
88 if (frame instanceof Http2HeadersFrame) {
89 Http2HeadersFrame headersFrame = (Http2HeadersFrame) frame;
90 Http2Headers headers = headersFrame.headers();
91 Http2FrameStream stream = headersFrame.stream();
92 int id = stream == null ? 0 : stream.id();
93
94 final CharSequence status = headers.status();
95
96
97
98 if (null != status && HttpResponseStatus.CONTINUE.codeAsText().contentEquals(status)) {
99 final FullHttpMessage<?> fullMsg = newFullMessage(id, headers, ctx.bufferAllocator());
100 ctx.fireChannelRead(fullMsg);
101 return;
102 }
103
104 if (headersFrame.isEndStream()) {
105 if (headers.method() == null && status == null) {
106 LastHttpContent<?> last = new DefaultLastHttpContent(ctx.bufferAllocator().allocate(0),
107 validateHeaders);
108 HttpConversionUtil.addHttp2ToHttpHeaders(id, headers, last.trailingHeaders(),
109 HttpVersion.HTTP_1_1, true, true);
110 ctx.fireChannelRead(last);
111 } else {
112 FullHttpMessage<?> full = newFullMessage(id, headers, ctx.bufferAllocator());
113 ctx.fireChannelRead(full);
114 }
115 } else {
116 HttpMessage req = newMessage(id, headers);
117 if (!HttpUtil.isContentLengthSet(req)) {
118 req.headers().add(HttpHeaderNames.TRANSFER_ENCODING, HttpHeaderValues.CHUNKED);
119 }
120 ctx.fireChannelRead(req);
121 }
122 } else if (frame instanceof Http2DataFrame) {
123 Http2DataFrame dataFrame = (Http2DataFrame) frame;
124 if (dataFrame.isEndStream()) {
125 ctx.fireChannelRead(new DefaultLastHttpContent(dataFrame.content(), validateHeaders));
126 } else {
127 ctx.fireChannelRead(new DefaultHttpContent(dataFrame.content()));
128 }
129 }
130 }
131
132 private void encodeLastContent(LastHttpContent<?> last, List<Object> out) {
133 boolean needFiller = !(last instanceof FullHttpMessage) && last.trailingHeaders().isEmpty();
134 final Buffer payload = last.payload();
135 if (payload.readableBytes() > 0 || needFiller) {
136 out.add(new DefaultHttp2DataFrame(payload.send(), last.trailingHeaders().isEmpty()));
137 }
138 if (!last.trailingHeaders().isEmpty()) {
139 Http2Headers headers = HttpConversionUtil.toHttp2Headers(last.trailingHeaders(), validateHeaders);
140 out.add(new DefaultHttp2HeadersFrame(headers, true));
141 }
142 }
143
144
145
146
147
148
149
150
151
152
153
154
155
156 @Override
157 protected void encodeAndClose(ChannelHandlerContext ctx, HttpObject obj, List<Object> out) throws Exception {
158
159
160 if (obj instanceof HttpResponse) {
161 final HttpResponse res = (HttpResponse) obj;
162 if (res.status().equals(HttpResponseStatus.CONTINUE)) {
163 if (res instanceof FullHttpResponse) {
164 final Http2Headers headers = toHttp2Headers(ctx, res);
165 out.add(new DefaultHttp2HeadersFrame(headers, false));
166 return;
167 } else {
168 throw new EncoderException(
169 HttpResponseStatus.CONTINUE + " must be a FullHttpResponse");
170 }
171 }
172 }
173
174 if (obj instanceof HttpMessage) {
175 Http2Headers headers = toHttp2Headers(ctx, (HttpMessage) obj);
176 boolean noMoreFrames = false;
177 if (obj instanceof FullHttpMessage) {
178 FullHttpMessage<?> full = (FullHttpMessage<?>) obj;
179 noMoreFrames = full.payload().readableBytes() == 0 && full.trailingHeaders().isEmpty();
180 }
181
182 out.add(new DefaultHttp2HeadersFrame(headers, noMoreFrames));
183 }
184
185 if (obj instanceof LastHttpContent) {
186 LastHttpContent<?> last = (LastHttpContent<?>) obj;
187 encodeLastContent(last, out);
188 } else if (obj instanceof HttpContent) {
189 HttpContent<?> cont = (HttpContent<?>) obj;
190 final Buffer payload = cont.payload();
191 out.add(new DefaultHttp2DataFrame(payload.send(), false));
192 }
193 }
194
195 private Http2Headers toHttp2Headers(final ChannelHandlerContext ctx, final HttpMessage msg) {
196 if (msg instanceof HttpRequest) {
197 msg.headers().set(
198 HttpConversionUtil.ExtensionHeaderNames.SCHEME.text(),
199 connectionScheme(ctx));
200 }
201
202 return HttpConversionUtil.toHttp2Headers(msg, validateHeaders);
203 }
204
205 private HttpMessage newMessage(final int id,
206 final Http2Headers headers) throws Http2Exception {
207 return isServer ?
208 HttpConversionUtil.toHttpRequest(id, headers, validateHeaders) :
209 HttpConversionUtil.toHttpResponse(id, headers, validateHeaders);
210 }
211
212 private FullHttpMessage<?> newFullMessage(final int id,
213 final Http2Headers headers,
214 final BufferAllocator alloc) throws Http2Exception {
215 return isServer ?
216 HttpConversionUtil.toFullHttpRequest(id, headers, alloc, validateHeaders) :
217 HttpConversionUtil.toFullHttpResponse(id, headers, alloc, validateHeaders);
218 }
219
220 @Override
221 public void handlerAdded(final ChannelHandlerContext ctx) throws Exception {
222 super.handlerAdded(ctx);
223
224
225
226
227
228
229 final Attribute<HttpScheme> schemeAttribute = connectionSchemeAttribute(ctx);
230 if (schemeAttribute.get() == null) {
231 final HttpScheme scheme = isSsl(ctx) ? HttpScheme.HTTPS : HttpScheme.HTTP;
232 schemeAttribute.set(scheme);
233 }
234 }
235
236 protected boolean isSsl(final ChannelHandlerContext ctx) {
237 final Channel connChannel = connectionChannel(ctx);
238 return null != connChannel.pipeline().get(SslHandler.class);
239 }
240
241 private static HttpScheme connectionScheme(ChannelHandlerContext ctx) {
242 final HttpScheme scheme = connectionSchemeAttribute(ctx).get();
243 return scheme == null ? HttpScheme.HTTP : scheme;
244 }
245
246 private static Attribute<HttpScheme> connectionSchemeAttribute(ChannelHandlerContext ctx) {
247 final Channel ch = connectionChannel(ctx);
248 return ch.attr(SCHEME_ATTR_KEY);
249 }
250
251 private static Channel connectionChannel(ChannelHandlerContext ctx) {
252 final Channel ch = ctx.channel();
253 return ch instanceof Http2StreamChannel ? ch.parent() : ch;
254 }
255 }