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.example.http2.server;
17  
18  import static io.netty.buffer.Unpooled.copiedBuffer;
19  import static io.netty.buffer.Unpooled.unreleasableBuffer;
20  import static io.netty.example.http2.Http2ExampleUtil.UPGRADE_RESPONSE_HEADER;
21  import static io.netty.handler.codec.http.HttpResponseStatus.OK;
22  import static io.netty.util.internal.logging.InternalLogLevel.INFO;
23  import io.netty.buffer.ByteBuf;
24  import io.netty.channel.ChannelHandlerContext;
25  import io.netty.handler.codec.AsciiString;
26  import io.netty.handler.codec.http.HttpServerUpgradeHandler;
27  import io.netty.handler.codec.http2.DefaultHttp2Connection;
28  import io.netty.handler.codec.http2.DefaultHttp2FrameReader;
29  import io.netty.handler.codec.http2.DefaultHttp2FrameWriter;
30  import io.netty.handler.codec.http2.DefaultHttp2Headers;
31  import io.netty.handler.codec.http2.Http2Connection;
32  import io.netty.handler.codec.http2.Http2ConnectionEncoder;
33  import io.netty.handler.codec.http2.Http2ConnectionHandler;
34  import io.netty.handler.codec.http2.Http2Exception;
35  import io.netty.handler.codec.http2.Http2FrameAdapter;
36  import io.netty.handler.codec.http2.Http2FrameLogger;
37  import io.netty.handler.codec.http2.Http2FrameReader;
38  import io.netty.handler.codec.http2.Http2FrameWriter;
39  import io.netty.handler.codec.http2.Http2Headers;
40  import io.netty.handler.codec.http2.Http2InboundFrameLogger;
41  import io.netty.handler.codec.http2.Http2OutboundFrameLogger;
42  import io.netty.util.CharsetUtil;
43  import io.netty.util.internal.logging.InternalLoggerFactory;
44  
45  /**
46   * A simple handler that responds with the message "Hello World!".
47   */
48  public class HelloWorldHttp2Handler extends Http2ConnectionHandler {
49  
50      private static final Http2FrameLogger logger = new Http2FrameLogger(INFO,
51              InternalLoggerFactory.getInstance(HelloWorldHttp2Handler.class));
52      static final ByteBuf RESPONSE_BYTES = unreleasableBuffer(copiedBuffer("Hello World", CharsetUtil.UTF_8));
53  
54      public HelloWorldHttp2Handler() {
55          this(new DefaultHttp2Connection(true), new Http2InboundFrameLogger(
56                  new DefaultHttp2FrameReader(), logger), new Http2OutboundFrameLogger(
57                  new DefaultHttp2FrameWriter(), logger), new SimpleHttp2FrameListener());
58      }
59  
60      private HelloWorldHttp2Handler(Http2Connection connection, Http2FrameReader frameReader,
61              Http2FrameWriter frameWriter, SimpleHttp2FrameListener listener) {
62          super(connection, frameReader, frameWriter, listener);
63          listener.encoder(encoder());
64      }
65  
66      /**
67       * Handles the cleartext HTTP upgrade event. If an upgrade occurred, sends a simple response via HTTP/2
68       * on stream 1 (the stream specifically reserved for cleartext HTTP upgrade).
69       */
70      @Override
71      public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {
72          if (evt instanceof HttpServerUpgradeHandler.UpgradeEvent) {
73              // Write an HTTP/2 response to the upgrade request
74              Http2Headers headers =
75                      new DefaultHttp2Headers().status(OK.codeAsText())
76                      .set(new AsciiString(UPGRADE_RESPONSE_HEADER), new AsciiString("true"));
77              encoder().writeHeaders(ctx, 1, headers, 0, true, ctx.newPromise());
78          }
79          super.userEventTriggered(ctx, evt);
80      }
81  
82      @Override
83      public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
84          cause.printStackTrace();
85          ctx.close();
86      }
87  
88      private static class SimpleHttp2FrameListener extends Http2FrameAdapter {
89          private Http2ConnectionEncoder encoder;
90  
91          public void encoder(Http2ConnectionEncoder encoder) {
92              this.encoder = encoder;
93          }
94  
95          /**
96           * If receive a frame with end-of-stream set, send a pre-canned response.
97           */
98          @Override
99          public int onDataRead(ChannelHandlerContext ctx, int streamId, ByteBuf data, int padding,
100                 boolean endOfStream) throws Http2Exception {
101             int processed = data.readableBytes() + padding;
102             if (endOfStream) {
103                 sendResponse(ctx, streamId, data.retain());
104             }
105             return processed;
106         }
107 
108         /**
109          * If receive a frame with end-of-stream set, send a pre-canned response.
110          */
111         @Override
112         public void onHeadersRead(ChannelHandlerContext ctx, int streamId,
113                 Http2Headers headers, int streamDependency, short weight,
114                 boolean exclusive, int padding, boolean endStream) throws Http2Exception {
115             if (endStream) {
116                 sendResponse(ctx, streamId, RESPONSE_BYTES.duplicate());
117             }
118         }
119 
120         /**
121          * Sends a "Hello World" DATA frame to the client.
122          */
123         private void sendResponse(ChannelHandlerContext ctx, int streamId, ByteBuf payload) {
124             // Send a frame for the response status
125             Http2Headers headers = new DefaultHttp2Headers().status(OK.codeAsText());
126             encoder.writeHeaders(ctx, streamId, headers, 0, false, ctx.newPromise());
127             encoder.writeData(ctx, streamId, payload, 0, true, ctx.newPromise());
128             ctx.flush();
129         }
130     }
131 }