View Javadoc
1   /*
2    * Copyright 2017 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    * https://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.netty5.testsuite.http2;
17  
18  import io.netty5.buffer.api.Buffer;
19  import io.netty5.channel.ChannelHandlerContext;
20  import io.netty5.handler.codec.http.FullHttpRequest;
21  import io.netty5.handler.codec.http.HttpHeaderNames;
22  import io.netty5.handler.codec.http.HttpMethod;
23  import io.netty5.handler.codec.http.HttpScheme;
24  import io.netty5.handler.codec.http.HttpServerUpgradeHandler;
25  import io.netty5.handler.codec.http2.DefaultHttp2Headers;
26  import io.netty5.handler.codec.http2.Http2ConnectionDecoder;
27  import io.netty5.handler.codec.http2.Http2ConnectionEncoder;
28  import io.netty5.handler.codec.http2.Http2ConnectionHandler;
29  import io.netty5.handler.codec.http2.Http2Flags;
30  import io.netty5.handler.codec.http2.Http2FrameListener;
31  import io.netty5.handler.codec.http2.Http2Headers;
32  import io.netty5.handler.codec.http2.Http2Settings;
33  
34  import java.util.function.Supplier;
35  
36  import static io.netty5.buffer.api.DefaultBufferAllocators.preferredAllocator;
37  import static io.netty5.handler.codec.http.HttpResponseStatus.OK;
38  import static io.netty5.util.CharsetUtil.US_ASCII;
39  import static io.netty5.util.CharsetUtil.UTF_8;
40  
41  /**
42   * A simple handler that responds with the message "Hello World!".
43   */
44  public final class HelloWorldHttp2Handler extends Http2ConnectionHandler implements Http2FrameListener {
45  
46      static final Supplier<Buffer> RESPONSE_BYTES_SUPPLIER =
47              preferredAllocator().constBufferSupplier("Hello World".getBytes(UTF_8));
48  
49      HelloWorldHttp2Handler(Http2ConnectionDecoder decoder, Http2ConnectionEncoder encoder,
50                             Http2Settings initialSettings) {
51          super(decoder, encoder, initialSettings);
52      }
53  
54      private static Http2Headers http1HeadersToHttp2Headers(FullHttpRequest request) {
55          CharSequence host = request.headers().get(HttpHeaderNames.HOST);
56          Http2Headers http2Headers = new DefaultHttp2Headers()
57                  .method(HttpMethod.GET.asciiName())
58                  .path(request.uri())
59                  .scheme(HttpScheme.HTTP.name());
60          if (host != null) {
61              http2Headers.authority(host);
62          }
63          return http2Headers;
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 channelInboundEvent(ChannelHandlerContext ctx, Object evt) throws Exception {
72          if (evt instanceof HttpServerUpgradeHandler.UpgradeEvent) {
73              HttpServerUpgradeHandler.UpgradeEvent upgradeEvent =
74                      (HttpServerUpgradeHandler.UpgradeEvent) evt;
75              onHeadersRead(ctx, 1, http1HeadersToHttp2Headers(upgradeEvent.upgradeRequest()), 0 , true);
76          }
77          super.channelInboundEvent(ctx, evt);
78      }
79  
80      @Override
81      public void channelExceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
82          super.channelExceptionCaught(ctx, cause);
83          cause.printStackTrace();
84          ctx.close();
85      }
86  
87      /**
88       * Sends a "Hello World" DATA frame to the client.
89       */
90      private void sendResponse(ChannelHandlerContext ctx, int streamId, Buffer payload) {
91          // Send a frame for the response status
92          Http2Headers headers = new DefaultHttp2Headers().status(OK.codeAsText());
93          encoder().writeHeaders(ctx, streamId, headers, 0, false);
94          encoder().writeData(ctx, streamId, payload, 0, true);
95  
96          // no need to call flush as channelReadComplete(...) will take care of it.
97      }
98  
99      @Override
100     public int onDataRead(ChannelHandlerContext ctx, int streamId, Buffer data, int padding, boolean endOfStream) {
101         int processed = data.readableBytes() + padding;
102         if (endOfStream) {
103             sendResponse(ctx, streamId, data.copy());
104         }
105         return processed;
106     }
107 
108     @Override
109     public void onHeadersRead(ChannelHandlerContext ctx, int streamId,
110                               Http2Headers headers, int padding, boolean endOfStream) {
111         if (endOfStream) {
112             final Buffer content = RESPONSE_BYTES_SUPPLIER.get().copy()
113                     .writeCharSequence(" - via HTTP/2", US_ASCII);
114             sendResponse(ctx, streamId, content);
115         }
116     }
117 
118     @Override
119     public void onHeadersRead(ChannelHandlerContext ctx, int streamId, Http2Headers headers, int streamDependency,
120                               short weight, boolean exclusive, int padding, boolean endOfStream) {
121         onHeadersRead(ctx, streamId, headers, padding, endOfStream);
122     }
123 
124     @Override
125     public void onPriorityRead(ChannelHandlerContext ctx, int streamId, int streamDependency,
126                                short weight, boolean exclusive) {
127     }
128 
129     @Override
130     public void onRstStreamRead(ChannelHandlerContext ctx, int streamId, long errorCode) {
131     }
132 
133     @Override
134     public void onSettingsAckRead(ChannelHandlerContext ctx) {
135     }
136 
137     @Override
138     public void onSettingsRead(ChannelHandlerContext ctx, Http2Settings settings) {
139     }
140 
141     @Override
142     public void onPingRead(ChannelHandlerContext ctx, long data) {
143     }
144 
145     @Override
146     public void onPingAckRead(ChannelHandlerContext ctx, long data) {
147     }
148 
149     @Override
150     public void onPushPromiseRead(ChannelHandlerContext ctx, int streamId, int promisedStreamId,
151                                   Http2Headers headers, int padding) {
152     }
153 
154     @Override
155     public void onGoAwayRead(ChannelHandlerContext ctx, int lastStreamId, long errorCode, Buffer debugData) {
156     }
157 
158     @Override
159     public void onWindowUpdateRead(ChannelHandlerContext ctx, int streamId, int windowSizeIncrement) {
160     }
161 
162     @Override
163     public void onUnknownFrame(ChannelHandlerContext ctx, byte frameType, int streamId,
164                                Http2Flags flags, Buffer payload) {
165     }
166 }