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