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 }