1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package io.netty5.example.http2.tiles;
18
19 import io.netty5.buffer.api.Buffer;
20 import io.netty5.channel.ChannelHandlerContext;
21 import io.netty5.channel.SimpleChannelInboundHandler;
22 import io.netty5.handler.codec.http.DefaultFullHttpResponse;
23 import io.netty5.handler.codec.http.FullHttpRequest;
24 import io.netty5.handler.codec.http.FullHttpResponse;
25 import io.netty5.handler.codec.http.QueryStringDecoder;
26 import io.netty5.handler.codec.http2.HttpConversionUtil;
27 import io.netty5.handler.codec.http2.InboundHttp2ToHttpAdapter;
28
29 import java.util.concurrent.TimeUnit;
30
31 import static io.netty5.example.http2.Http2ExampleUtil.firstValue;
32 import static io.netty5.example.http2.Http2ExampleUtil.toInt;
33 import static io.netty5.handler.codec.http.HttpHeaderNames.CONTENT_TYPE;
34 import static io.netty5.handler.codec.http.HttpResponseStatus.BAD_REQUEST;
35 import static io.netty5.handler.codec.http.HttpResponseStatus.OK;
36 import static io.netty5.handler.codec.http.HttpUtil.setContentLength;
37 import static io.netty5.handler.codec.http.HttpVersion.HTTP_1_1;
38 import static java.lang.Integer.parseInt;
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 messageReceived(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,
74 ctx.bufferAllocator().allocate(0));
75 streamId(response, streamId);
76 ctx.writeAndFlush(response);
77 }
78
79 private void handleImage(String x, String y, ChannelHandlerContext ctx, String streamId, int latency,
80 FullHttpRequest request) {
81 Buffer image = ImageCache.INSTANCE.image(parseInt(x), parseInt(y));
82 FullHttpResponse response = new DefaultFullHttpResponse(HTTP_1_1, OK, image);
83 response.headers().set(CONTENT_TYPE, "image/jpeg");
84 sendResponse(ctx, streamId, latency, response, request);
85 }
86
87 private void handlePage(ChannelHandlerContext ctx, String streamId, int latency, FullHttpRequest request) {
88 byte[] body = Html.body(latency);
89 Buffer content = ctx.bufferAllocator().allocate(Html.HEADER.length + body.length + Html.FOOTER.length);
90 content.writeBytes(Html.HEADER);
91 content.writeBytes(body);
92 content.writeBytes(Html.FOOTER);
93 FullHttpResponse response = new DefaultFullHttpResponse(HTTP_1_1, OK, content);
94 response.headers().set(CONTENT_TYPE, "text/html; charset=UTF-8");
95 sendResponse(ctx, streamId, latency, response, request);
96 }
97
98 protected void sendResponse(final ChannelHandlerContext ctx, String streamId, int latency,
99 final FullHttpResponse response, final FullHttpRequest request) {
100 setContentLength(response, response.payload().readableBytes());
101 streamId(response, streamId);
102 ctx.executor().schedule(() -> {
103 ctx.writeAndFlush(response);
104 }, latency, TimeUnit.MILLISECONDS);
105 }
106
107 private static String streamId(FullHttpRequest request) {
108 return request.headers().get(HttpConversionUtil.ExtensionHeaderNames.STREAM_ID.text());
109 }
110
111 private static void streamId(FullHttpResponse response, String streamId) {
112 response.headers().set(HttpConversionUtil.ExtensionHeaderNames.STREAM_ID.text(), streamId);
113 }
114
115 @Override
116 public void channelExceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
117 cause.printStackTrace();
118 ctx.close();
119 }
120 }