1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package io.netty.example.http2.tiles;
18
19 import static io.netty.buffer.Unpooled.EMPTY_BUFFER;
20 import static io.netty.example.http2.Http2ExampleUtil.firstValue;
21 import static io.netty.example.http2.Http2ExampleUtil.toInt;
22 import static io.netty.handler.codec.http.HttpHeaderNames.CONTENT_TYPE;
23 import static io.netty.handler.codec.http.HttpUtil.setContentLength;
24 import static io.netty.handler.codec.http.HttpResponseStatus.BAD_REQUEST;
25 import static io.netty.handler.codec.http.HttpResponseStatus.OK;
26 import static io.netty.handler.codec.http.HttpVersion.HTTP_1_1;
27 import static java.lang.Integer.parseInt;
28 import io.netty.buffer.ByteBuf;
29 import io.netty.channel.ChannelHandlerContext;
30 import io.netty.channel.SimpleChannelInboundHandler;
31 import io.netty.handler.codec.http.DefaultFullHttpResponse;
32 import io.netty.handler.codec.http.FullHttpRequest;
33 import io.netty.handler.codec.http.FullHttpResponse;
34 import io.netty.handler.codec.http.QueryStringDecoder;
35 import io.netty.handler.codec.http2.HttpConversionUtil;
36 import io.netty.handler.codec.http2.InboundHttp2ToHttpAdapter;
37
38 import java.util.concurrent.TimeUnit;
39
40
41
42
43
44
45
46 public class Http2RequestHandler extends SimpleChannelInboundHandler<FullHttpRequest> {
47
48 private static final String LATENCY_FIELD_NAME = "latency";
49 private static final int MIN_LATENCY = 0;
50 private static final int MAX_LATENCY = 1000;
51 private static final String IMAGE_COORDINATE_Y = "y";
52 private static final String IMAGE_COORDINATE_X = "x";
53
54 @Override
55 protected void channelRead0(ChannelHandlerContext ctx, FullHttpRequest request) throws Exception {
56 QueryStringDecoder queryString = new QueryStringDecoder(request.uri());
57 String streamId = streamId(request);
58 int latency = toInt(firstValue(queryString, LATENCY_FIELD_NAME), 0);
59 if (latency < MIN_LATENCY || latency > MAX_LATENCY) {
60 sendBadRequest(ctx, streamId);
61 return;
62 }
63 String x = firstValue(queryString, IMAGE_COORDINATE_X);
64 String y = firstValue(queryString, IMAGE_COORDINATE_Y);
65 if (x == null || y == null) {
66 handlePage(ctx, streamId, latency, request);
67 } else {
68 handleImage(x, y, ctx, streamId, latency, request);
69 }
70 }
71
72 private static void sendBadRequest(ChannelHandlerContext ctx, String streamId) {
73 FullHttpResponse response = new DefaultFullHttpResponse(HTTP_1_1, BAD_REQUEST, EMPTY_BUFFER);
74 streamId(response, streamId);
75 ctx.writeAndFlush(response);
76 }
77
78 private void handleImage(String x, String y, ChannelHandlerContext ctx, String streamId, int latency,
79 FullHttpRequest request) {
80 ByteBuf image = ImageCache.INSTANCE.image(parseInt(x), parseInt(y));
81 FullHttpResponse response = new DefaultFullHttpResponse(HTTP_1_1, OK, image.duplicate());
82 response.headers().set(CONTENT_TYPE, "image/jpeg");
83 sendResponse(ctx, streamId, latency, response, request);
84 }
85
86 private void handlePage(ChannelHandlerContext ctx, String streamId, int latency, FullHttpRequest request) {
87 byte[] body = Html.body(latency);
88 ByteBuf content = ctx.alloc().buffer(Html.HEADER.length + body.length + Html.FOOTER.length);
89 content.writeBytes(Html.HEADER);
90 content.writeBytes(body);
91 content.writeBytes(Html.FOOTER);
92 FullHttpResponse response = new DefaultFullHttpResponse(HTTP_1_1, OK, content);
93 response.headers().set(CONTENT_TYPE, "text/html; charset=UTF-8");
94 sendResponse(ctx, streamId, latency, response, request);
95 }
96
97 protected void sendResponse(final ChannelHandlerContext ctx, String streamId, int latency,
98 final FullHttpResponse response, final FullHttpRequest request) {
99 setContentLength(response, response.content().readableBytes());
100 streamId(response, streamId);
101 ctx.executor().schedule(new Runnable() {
102 @Override
103 public void run() {
104 ctx.writeAndFlush(response);
105 }
106 }, latency, TimeUnit.MILLISECONDS);
107 }
108
109 private static String streamId(FullHttpRequest request) {
110 return request.headers().get(HttpConversionUtil.ExtensionHeaderNames.STREAM_ID.text());
111 }
112
113 private static void streamId(FullHttpResponse response, String streamId) {
114 response.headers().set(HttpConversionUtil.ExtensionHeaderNames.STREAM_ID.text(), streamId);
115 }
116
117 @Override
118 public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
119 cause.printStackTrace();
120 ctx.close();
121 }
122 }