View Javadoc
1   /*
2    * Copyright 2014 The Netty Project
3    *
4    * The Netty Project licenses this file to you under the Apache License, version 2.0 (the
5    * "License"); you may not use this file except in compliance with the License. You may obtain a
6    * copy of the License at:
7    *
8    * http://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software distributed under the License
11   * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
12   * or implied. See the License for the specific language governing permissions and limitations under
13   * the License.
14   */
15  
16  package io.netty.handler.codec.http2;
17  
18  import io.netty.channel.ChannelHandlerContext;
19  import io.netty.channel.ChannelPromise;
20  import io.netty.handler.codec.http.FullHttpMessage;
21  import io.netty.handler.codec.http.HttpHeaders;
22  import io.netty.handler.codec.http2.Http2CodecUtil.SimpleChannelPromiseAggregator;
23  
24  /**
25   * Translates HTTP/1.x object writes into HTTP/2 frames.
26   * <p>
27   * See {@link InboundHttp2ToHttpAdapter} to get translation from HTTP/2 frames to HTTP/1.x objects.
28   */
29  public class HttpToHttp2ConnectionHandler extends Http2ConnectionHandler {
30      public HttpToHttp2ConnectionHandler(boolean server, Http2FrameListener listener) {
31          super(server, listener);
32      }
33  
34      public HttpToHttp2ConnectionHandler(Http2Connection connection, Http2FrameListener listener) {
35          super(connection, listener);
36      }
37  
38      public HttpToHttp2ConnectionHandler(Http2Connection connection, Http2FrameReader frameReader,
39              Http2FrameWriter frameWriter, Http2FrameListener listener) {
40          super(connection, frameReader, frameWriter, listener);
41      }
42  
43      public HttpToHttp2ConnectionHandler(Http2ConnectionDecoder.Builder decoderBuilder,
44              Http2ConnectionEncoder.Builder encoderBuilder) {
45          super(decoderBuilder, encoderBuilder);
46      }
47  
48      /**
49       * Get the next stream id either from the {@link HttpHeaders} object or HTTP/2 codec
50       *
51       * @param httpHeaders The HTTP/1.x headers object to look for the stream id
52       * @return The stream id to use with this {@link HttpHeaders} object
53       * @throws Exception If the {@code httpHeaders} object specifies an invalid stream id
54       */
55      private int getStreamId(HttpHeaders httpHeaders) throws Exception {
56          return httpHeaders.getInt(HttpUtil.ExtensionHeaderNames.STREAM_ID.text(), connection().local().nextStreamId());
57      }
58  
59      /**
60       * Handles conversion of a {@link FullHttpMessage} to HTTP/2 frames.
61       */
62      @Override
63      public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) {
64          if (msg instanceof FullHttpMessage) {
65              FullHttpMessage httpMsg = (FullHttpMessage) msg;
66              boolean hasData = httpMsg.content().isReadable();
67              boolean httpMsgNeedRelease = true;
68              SimpleChannelPromiseAggregator promiseAggregator = null;
69              try {
70                  // Provide the user the opportunity to specify the streamId
71                  int streamId = getStreamId(httpMsg.headers());
72  
73                  // Convert and write the headers.
74                  Http2Headers http2Headers = HttpUtil.toHttp2Headers(httpMsg);
75                  Http2ConnectionEncoder encoder = encoder();
76  
77                  if (hasData) {
78                      promiseAggregator = new SimpleChannelPromiseAggregator(promise, ctx.channel(), ctx.executor());
79                      encoder.writeHeaders(ctx, streamId, http2Headers, 0, false, promiseAggregator.newPromise());
80                      httpMsgNeedRelease = false;
81                      encoder.writeData(ctx, streamId, httpMsg.content(), 0, true, promiseAggregator.newPromise());
82                      promiseAggregator.doneAllocatingPromises();
83                  } else {
84                      encoder.writeHeaders(ctx, streamId, http2Headers, 0, true, promise);
85                  }
86              } catch (Throwable t) {
87                  if (promiseAggregator == null) {
88                      promise.tryFailure(t);
89                  } else {
90                      promiseAggregator.setFailure(t);
91                  }
92              } finally {
93                  if (httpMsgNeedRelease) {
94                      httpMsg.release();
95                  }
96              }
97          } else {
98              ctx.write(msg, promise);
99          }
100     }
101 }