1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package io.netty.handler.codec.http2;
17
18 import io.netty.buffer.ByteBuf;
19 import io.netty.channel.ChannelHandlerContext;
20 import io.netty.channel.ChannelPromise;
21 import io.netty.handler.codec.http.EmptyHttpHeaders;
22 import io.netty.handler.codec.http.FullHttpMessage;
23 import io.netty.handler.codec.http.HttpContent;
24 import io.netty.handler.codec.http.HttpHeaders;
25 import io.netty.handler.codec.http.HttpMessage;
26 import io.netty.handler.codec.http.HttpScheme;
27 import io.netty.handler.codec.http.LastHttpContent;
28 import io.netty.handler.codec.http2.Http2CodecUtil.SimpleChannelPromiseAggregator;
29 import io.netty.util.ReferenceCountUtil;
30
31
32
33
34
35
36 public class HttpToHttp2ConnectionHandler extends Http2ConnectionHandler {
37
38 private final boolean validateHeaders;
39 private int currentStreamId;
40 private HttpScheme httpScheme;
41
42 protected HttpToHttp2ConnectionHandler(Http2ConnectionDecoder decoder, Http2ConnectionEncoder encoder,
43 Http2Settings initialSettings, boolean validateHeaders) {
44 super(decoder, encoder, initialSettings);
45 this.validateHeaders = validateHeaders;
46 }
47
48 protected HttpToHttp2ConnectionHandler(Http2ConnectionDecoder decoder, Http2ConnectionEncoder encoder,
49 Http2Settings initialSettings, boolean validateHeaders,
50 boolean decoupleCloseAndGoAway) {
51 this(decoder, encoder, initialSettings, validateHeaders, decoupleCloseAndGoAway, null);
52 }
53
54 protected HttpToHttp2ConnectionHandler(Http2ConnectionDecoder decoder, Http2ConnectionEncoder encoder,
55 Http2Settings initialSettings, boolean validateHeaders,
56 boolean decoupleCloseAndGoAway, HttpScheme httpScheme) {
57 super(decoder, encoder, initialSettings, decoupleCloseAndGoAway);
58 this.validateHeaders = validateHeaders;
59 this.httpScheme = httpScheme;
60 }
61
62 protected HttpToHttp2ConnectionHandler(Http2ConnectionDecoder decoder, Http2ConnectionEncoder encoder,
63 Http2Settings initialSettings, boolean validateHeaders,
64 boolean decoupleCloseAndGoAway, boolean flushPreface,
65 HttpScheme httpScheme) {
66 super(decoder, encoder, initialSettings, decoupleCloseAndGoAway, flushPreface);
67 this.validateHeaders = validateHeaders;
68 this.httpScheme = httpScheme;
69 }
70
71
72
73
74
75
76
77
78 private int getStreamId(HttpHeaders httpHeaders) throws Exception {
79 return httpHeaders.getInt(HttpConversionUtil.ExtensionHeaderNames.STREAM_ID.text(),
80 connection().local().incrementAndGetNextStreamId());
81 }
82
83
84
85
86 @Override
87 public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) {
88
89 if (!(msg instanceof HttpMessage || msg instanceof HttpContent)) {
90 ctx.write(msg, promise);
91 return;
92 }
93
94 boolean release = true;
95 SimpleChannelPromiseAggregator promiseAggregator =
96 new SimpleChannelPromiseAggregator(promise, ctx.channel(), ctx.executor());
97 try {
98 Http2ConnectionEncoder encoder = encoder();
99 boolean endStream = false;
100 if (msg instanceof HttpMessage) {
101 final HttpMessage httpMsg = (HttpMessage) msg;
102
103
104 currentStreamId = getStreamId(httpMsg.headers());
105
106
107 if (httpScheme != null &&
108 !httpMsg.headers().contains(HttpConversionUtil.ExtensionHeaderNames.SCHEME.text())) {
109 httpMsg.headers().set(HttpConversionUtil.ExtensionHeaderNames.SCHEME.text(), httpScheme.name());
110 }
111
112
113 Http2Headers http2Headers = HttpConversionUtil.toHttp2Headers(httpMsg, validateHeaders);
114 endStream = msg instanceof FullHttpMessage && !((FullHttpMessage) msg).content().isReadable();
115 writeHeaders(ctx, encoder, currentStreamId, httpMsg.headers(), http2Headers,
116 endStream, promiseAggregator);
117 }
118
119 if (!endStream && msg instanceof HttpContent) {
120 boolean isLastContent = false;
121 HttpHeaders trailers = EmptyHttpHeaders.INSTANCE;
122 Http2Headers http2Trailers = EmptyHttp2Headers.INSTANCE;
123 if (msg instanceof LastHttpContent) {
124 isLastContent = true;
125
126
127 final LastHttpContent lastContent = (LastHttpContent) msg;
128 trailers = lastContent.trailingHeaders();
129 http2Trailers = HttpConversionUtil.toHttp2Headers(trailers, validateHeaders);
130 }
131
132
133 final ByteBuf content = ((HttpContent) msg).content();
134 endStream = isLastContent && trailers.isEmpty();
135 encoder.writeData(ctx, currentStreamId, content, 0, endStream, promiseAggregator.newPromise());
136 release = false;
137
138 if (!trailers.isEmpty()) {
139
140 writeHeaders(ctx, encoder, currentStreamId, trailers, http2Trailers, true, promiseAggregator);
141 }
142 }
143 } catch (Throwable t) {
144 onError(ctx, true, t);
145 promiseAggregator.setFailure(t);
146 } finally {
147 if (release) {
148 ReferenceCountUtil.release(msg);
149 }
150 promiseAggregator.doneAllocatingPromises();
151 }
152 }
153
154 private static void writeHeaders(ChannelHandlerContext ctx, Http2ConnectionEncoder encoder, int streamId,
155 HttpHeaders headers, Http2Headers http2Headers, boolean endStream,
156 SimpleChannelPromiseAggregator promiseAggregator) {
157 int dependencyId = headers.getInt(
158 HttpConversionUtil.ExtensionHeaderNames.STREAM_DEPENDENCY_ID.text(), 0);
159 short weight = headers.getShort(
160 HttpConversionUtil.ExtensionHeaderNames.STREAM_WEIGHT.text(), Http2CodecUtil.DEFAULT_PRIORITY_WEIGHT);
161 encoder.writeHeaders(ctx, streamId, http2Headers, dependencyId, weight, false,
162 0, endStream, promiseAggregator.newPromise());
163 }
164 }