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.buffer.ByteBufAllocator;
19 import io.netty.channel.ChannelHandlerContext;
20 import io.netty.handler.codec.http.FullHttpMessage;
21 import io.netty.handler.codec.http.FullHttpRequest;
22 import io.netty.handler.codec.http.FullHttpResponse;
23 import io.netty.handler.codec.http.HttpHeaderNames;
24 import io.netty.handler.codec.http.HttpStatusClass;
25 import io.netty.handler.codec.http.HttpUtil;
26
27 import static io.netty.handler.codec.http2.Http2Error.INTERNAL_ERROR;
28 import static io.netty.handler.codec.http2.Http2Error.PROTOCOL_ERROR;
29 import static io.netty.handler.codec.http2.Http2Exception.connectionError;
30 import static io.netty.handler.codec.http.HttpResponseStatus.OK;
31 import static io.netty.util.internal.ObjectUtil.checkNotNull;
32 import static io.netty.util.internal.ObjectUtil.checkPositive;
33
34
35
36
37
38
39
40 public class InboundHttp2ToHttpAdapter extends Http2EventAdapter {
41 private static final ImmediateSendDetector DEFAULT_SEND_DETECTOR = new ImmediateSendDetector() {
42 @Override
43 public boolean mustSendImmediately(FullHttpMessage msg) {
44 if (msg instanceof FullHttpResponse) {
45 return ((FullHttpResponse) msg).status().codeClass() == HttpStatusClass.INFORMATIONAL;
46 }
47 if (msg instanceof FullHttpRequest) {
48 return msg.headers().contains(HttpHeaderNames.EXPECT);
49 }
50 return false;
51 }
52
53 @Override
54 public FullHttpMessage copyIfNeeded(ByteBufAllocator allocator, FullHttpMessage msg) {
55 if (msg instanceof FullHttpRequest) {
56 FullHttpRequest copy = ((FullHttpRequest) msg).replace(allocator.buffer(0));
57 copy.headers().remove(HttpHeaderNames.EXPECT);
58 return copy;
59 }
60 return null;
61 }
62 };
63
64 private final int maxContentLength;
65 private final ImmediateSendDetector sendDetector;
66 private final Http2Connection.PropertyKey messageKey;
67 private final boolean propagateSettings;
68 protected final Http2Connection connection;
69 protected final boolean validateHttpHeaders;
70
71 protected InboundHttp2ToHttpAdapter(Http2Connection connection, int maxContentLength,
72 boolean validateHttpHeaders, boolean propagateSettings) {
73 this.connection = checkNotNull(connection, "connection");
74 this.maxContentLength = checkPositive(maxContentLength, "maxContentLength");
75 this.validateHttpHeaders = validateHttpHeaders;
76 this.propagateSettings = propagateSettings;
77 sendDetector = DEFAULT_SEND_DETECTOR;
78 messageKey = connection.newKey();
79 }
80
81
82
83
84
85
86 protected final void removeMessage(Http2Stream stream, boolean release) {
87 FullHttpMessage msg = stream.removeProperty(messageKey);
88 if (release && msg != null) {
89 msg.release();
90 }
91 }
92
93
94
95
96
97
98 protected final FullHttpMessage getMessage(Http2Stream stream) {
99 return (FullHttpMessage) stream.getProperty(messageKey);
100 }
101
102
103
104
105
106
107 protected final void putMessage(Http2Stream stream, FullHttpMessage message) {
108 FullHttpMessage previous = stream.setProperty(messageKey, message);
109 if (previous != message && previous != null) {
110 previous.release();
111 }
112 }
113
114 @Override
115 public void onStreamRemoved(Http2Stream stream) {
116 removeMessage(stream, true);
117 }
118
119
120
121
122
123
124
125
126
127 protected void fireChannelRead(ChannelHandlerContext ctx, FullHttpMessage msg, boolean release,
128 Http2Stream stream) {
129 removeMessage(stream, release);
130 HttpUtil.setContentLength(msg, msg.content().readableBytes());
131 ctx.fireChannelRead(msg);
132 }
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148 protected FullHttpMessage newMessage(Http2Stream stream, Http2Headers headers, boolean validateHttpHeaders,
149 ByteBufAllocator alloc) throws Http2Exception {
150 return connection.isServer() ? HttpConversionUtil.toFullHttpRequest(stream.id(), headers, alloc,
151 validateHttpHeaders) : HttpConversionUtil.toFullHttpResponse(stream.id(), headers, alloc,
152 validateHttpHeaders);
153 }
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179 protected FullHttpMessage processHeadersBegin(ChannelHandlerContext ctx, Http2Stream stream, Http2Headers headers,
180 boolean endOfStream, boolean allowAppend, boolean appendToTrailer)
181 throws Http2Exception {
182 FullHttpMessage msg = getMessage(stream);
183 boolean release = true;
184 if (msg == null) {
185 msg = newMessage(stream, headers, validateHttpHeaders, ctx.alloc());
186 } else if (allowAppend) {
187 release = false;
188 HttpConversionUtil.addHttp2ToHttpHeaders(stream.id(), headers, msg, appendToTrailer);
189 } else {
190 release = false;
191 msg = null;
192 }
193
194 if (sendDetector.mustSendImmediately(msg)) {
195
196
197 final FullHttpMessage copy = endOfStream ? null : sendDetector.copyIfNeeded(ctx.alloc(), msg);
198 fireChannelRead(ctx, msg, release, stream);
199 return copy;
200 }
201
202 return msg;
203 }
204
205
206
207
208
209
210
211
212
213
214 private void processHeadersEnd(ChannelHandlerContext ctx, Http2Stream stream, FullHttpMessage msg,
215 boolean endOfStream) {
216 if (endOfStream) {
217
218 fireChannelRead(ctx, msg, getMessage(stream) != msg, stream);
219 } else {
220 putMessage(stream, msg);
221 }
222 }
223
224 @Override
225 public int onDataRead(ChannelHandlerContext ctx, int streamId, ByteBuf data, int padding, boolean endOfStream)
226 throws Http2Exception {
227 Http2Stream stream = connection.stream(streamId);
228 FullHttpMessage msg = getMessage(stream);
229 if (msg == null) {
230 throw connectionError(PROTOCOL_ERROR, "Data Frame received for unknown stream id %d", streamId);
231 }
232
233 ByteBuf content = msg.content();
234 final int dataReadableBytes = data.readableBytes();
235 if (content.readableBytes() > maxContentLength - dataReadableBytes) {
236 throw connectionError(INTERNAL_ERROR,
237 "Content length exceeded max of %d for stream id %d", maxContentLength, streamId);
238 }
239
240 content.writeBytes(data, data.readerIndex(), dataReadableBytes);
241
242 if (endOfStream) {
243 fireChannelRead(ctx, msg, false, stream);
244 }
245
246
247 return dataReadableBytes + padding;
248 }
249
250 @Override
251 public void onHeadersRead(ChannelHandlerContext ctx, int streamId, Http2Headers headers, int padding,
252 boolean endOfStream) throws Http2Exception {
253 Http2Stream stream = connection.stream(streamId);
254 FullHttpMessage msg = processHeadersBegin(ctx, stream, headers, endOfStream, true, true);
255 if (msg != null) {
256 processHeadersEnd(ctx, stream, msg, endOfStream);
257 }
258 }
259
260 @Override
261 public void onHeadersRead(ChannelHandlerContext ctx, int streamId, Http2Headers headers, int streamDependency,
262 short weight, boolean exclusive, int padding, boolean endOfStream)
263 throws Http2Exception {
264 Http2Stream stream = connection.stream(streamId);
265 FullHttpMessage msg = processHeadersBegin(ctx, stream, headers, endOfStream, true, true);
266 if (msg != null) {
267
268
269 if (streamDependency != Http2CodecUtil.CONNECTION_STREAM_ID) {
270 msg.headers().setInt(HttpConversionUtil.ExtensionHeaderNames.STREAM_DEPENDENCY_ID.text(),
271 streamDependency);
272 }
273 msg.headers().setShort(HttpConversionUtil.ExtensionHeaderNames.STREAM_WEIGHT.text(), weight);
274
275 processHeadersEnd(ctx, stream, msg, endOfStream);
276 }
277 }
278
279 @Override
280 public void onRstStreamRead(ChannelHandlerContext ctx, int streamId, long errorCode) throws Http2Exception {
281 Http2Stream stream = connection.stream(streamId);
282 FullHttpMessage msg = getMessage(stream);
283 if (msg != null) {
284 onRstStreamRead(stream, msg);
285 }
286 ctx.fireExceptionCaught(Http2Exception.streamError(streamId, Http2Error.valueOf(errorCode),
287 "HTTP/2 to HTTP layer caught stream reset"));
288 }
289
290 @Override
291 public void onPushPromiseRead(ChannelHandlerContext ctx, int streamId, int promisedStreamId,
292 Http2Headers headers, int padding) throws Http2Exception {
293
294 Http2Stream promisedStream = connection.stream(promisedStreamId);
295 if (headers.status() == null) {
296
297
298
299
300
301 headers.status(OK.codeAsText());
302 }
303 FullHttpMessage msg = processHeadersBegin(ctx, promisedStream, headers, false, false, false);
304 if (msg == null) {
305 throw connectionError(PROTOCOL_ERROR, "Push Promise Frame received for pre-existing stream id %d",
306 promisedStreamId);
307 }
308
309 msg.headers().setInt(HttpConversionUtil.ExtensionHeaderNames.STREAM_PROMISE_ID.text(), streamId);
310 msg.headers().setShort(HttpConversionUtil.ExtensionHeaderNames.STREAM_WEIGHT.text(),
311 Http2CodecUtil.DEFAULT_PRIORITY_WEIGHT);
312
313 processHeadersEnd(ctx, promisedStream, msg, false);
314 }
315
316 @Override
317 public void onSettingsRead(ChannelHandlerContext ctx, Http2Settings settings) throws Http2Exception {
318 if (propagateSettings) {
319
320 ctx.fireChannelRead(settings);
321 }
322 }
323
324
325
326
327 protected void onRstStreamRead(Http2Stream stream, FullHttpMessage msg) {
328 removeMessage(stream, true);
329 }
330
331
332
333
334
335 private interface ImmediateSendDetector {
336
337
338
339
340
341
342
343 boolean mustSendImmediately(FullHttpMessage msg);
344
345
346
347
348
349
350
351
352
353
354
355
356 FullHttpMessage copyIfNeeded(ByteBufAllocator allocator, FullHttpMessage msg);
357 }
358 }