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