781a85520c
Motivation: Adding an example that showcases Netty’s HTTP/2 codec and that is slightly more complex than the existing hello-world example. It is based on the Gopher tiles example available here: https://http2.golang.org/gophertiles?latency=0 Modifications: Moved current http2 example to http2/helloworld. Added http2 tiles example under http2/tiles. Result: A Netty tiles example is available.
102 lines
4.0 KiB
Java
102 lines
4.0 KiB
Java
/*
|
|
* Copyright 2014 The Netty Project
|
|
*
|
|
* The Netty Project licenses this file to you under the Apache License, version 2.0 (the
|
|
* "License"); you may not use this file except in compliance with the License. You may obtain a
|
|
* copy of the License at:
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software distributed under the License
|
|
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
|
|
* or implied. See the License for the specific language governing permissions and limitations under
|
|
* the License.
|
|
*/
|
|
package io.netty.example.http2.helloworld.client;
|
|
|
|
import io.netty.buffer.ByteBuf;
|
|
import io.netty.channel.ChannelHandlerContext;
|
|
import io.netty.channel.ChannelPromise;
|
|
import io.netty.channel.SimpleChannelInboundHandler;
|
|
import io.netty.handler.codec.http.FullHttpResponse;
|
|
import io.netty.handler.codec.http2.HttpUtil;
|
|
import io.netty.util.CharsetUtil;
|
|
|
|
import java.util.Iterator;
|
|
import java.util.Map.Entry;
|
|
import java.util.SortedMap;
|
|
import java.util.TreeMap;
|
|
import java.util.concurrent.TimeUnit;
|
|
|
|
/**
|
|
* Process {@link io.netty.handler.codec.http.FullHttpResponse} translated from HTTP/2 frames
|
|
*/
|
|
public class HttpResponseHandler extends SimpleChannelInboundHandler<FullHttpResponse> {
|
|
|
|
private SortedMap<Integer, ChannelPromise> streamidPromiseMap;
|
|
|
|
public HttpResponseHandler() {
|
|
streamidPromiseMap = new TreeMap<Integer, ChannelPromise>();
|
|
}
|
|
|
|
/**
|
|
* Create an association between an anticipated response stream id and a {@link io.netty.channel.ChannelPromise}
|
|
*
|
|
* @param streamId The stream for which a response is expected
|
|
* @param promise The promise object that will be used to wait/notify events
|
|
* @return The previous object associated with {@code streamId}
|
|
* @see HttpResponseHandler#awaitResponses(long, java.util.concurrent.TimeUnit)
|
|
*/
|
|
public ChannelPromise put(int streamId, ChannelPromise promise) {
|
|
return streamidPromiseMap.put(streamId, promise);
|
|
}
|
|
|
|
/**
|
|
* Wait (sequentially) for a time duration for each anticipated response
|
|
*
|
|
* @param timeout Value of time to wait for each response
|
|
* @param unit Units associated with {@code timeout}
|
|
* @see HttpResponseHandler#put(int, io.netty.channel.ChannelPromise)
|
|
*/
|
|
public void awaitResponses(long timeout, TimeUnit unit) {
|
|
Iterator<Entry<Integer, ChannelPromise>> itr = streamidPromiseMap.entrySet().iterator();
|
|
while (itr.hasNext()) {
|
|
Entry<Integer, ChannelPromise> entry = itr.next();
|
|
ChannelPromise promise = entry.getValue();
|
|
if (!promise.awaitUninterruptibly(timeout, unit)) {
|
|
throw new IllegalStateException("Timed out waiting for response on stream id " + entry.getKey());
|
|
}
|
|
if (!promise.isSuccess()) {
|
|
throw new RuntimeException(promise.cause());
|
|
}
|
|
System.out.println("---Stream id: " + entry.getKey() + " received---");
|
|
itr.remove();
|
|
}
|
|
}
|
|
|
|
@Override
|
|
protected void channelRead0(ChannelHandlerContext ctx, FullHttpResponse msg) throws Exception {
|
|
Integer streamId = msg.headers().getInt(HttpUtil.ExtensionHeaderNames.STREAM_ID.text());
|
|
if (streamId == null) {
|
|
System.err.println("HttpResponseHandler unexpected message received: " + msg);
|
|
return;
|
|
}
|
|
|
|
ChannelPromise promise = streamidPromiseMap.get(streamId);
|
|
if (promise == null) {
|
|
System.err.println("Message received for unknown stream id " + streamId);
|
|
} else {
|
|
// Do stuff with the message (for now just print it)
|
|
ByteBuf content = msg.content();
|
|
if (content.isReadable()) {
|
|
int contentLength = content.readableBytes();
|
|
byte[] arr = new byte[contentLength];
|
|
content.readBytes(arr);
|
|
System.out.println(new String(arr, 0, contentLength, CharsetUtil.UTF_8));
|
|
}
|
|
|
|
promise.setSuccess();
|
|
}
|
|
}
|
|
}
|