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