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