1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package io.netty5.testsuite.autobahn;
17
18 import io.netty5.channel.ChannelFutureListeners;
19 import io.netty5.channel.ChannelHandler;
20 import io.netty5.channel.ChannelHandlerContext;
21 import io.netty5.handler.codec.http.DefaultFullHttpResponse;
22 import io.netty5.handler.codec.http.FullHttpResponse;
23 import io.netty5.handler.codec.http.HttpHeaderNames;
24 import io.netty5.handler.codec.http.HttpRequest;
25 import io.netty5.handler.codec.http.websocketx.BinaryWebSocketFrame;
26 import io.netty5.handler.codec.http.websocketx.CloseWebSocketFrame;
27 import io.netty5.handler.codec.http.websocketx.ContinuationWebSocketFrame;
28 import io.netty5.handler.codec.http.websocketx.PingWebSocketFrame;
29 import io.netty5.handler.codec.http.websocketx.PongWebSocketFrame;
30 import io.netty5.handler.codec.http.websocketx.TextWebSocketFrame;
31 import io.netty5.handler.codec.http.websocketx.WebSocketFrame;
32 import io.netty5.handler.codec.http.websocketx.WebSocketServerHandshaker;
33 import io.netty5.handler.codec.http.websocketx.WebSocketServerHandshakerFactory;
34 import io.netty5.util.CharsetUtil;
35 import io.netty5.util.concurrent.Future;
36 import io.netty5.util.internal.StringUtil;
37
38 import java.util.logging.Level;
39 import java.util.logging.Logger;
40
41 import static io.netty5.handler.codec.http.HttpMethod.GET;
42 import static io.netty5.handler.codec.http.HttpResponseStatus.BAD_REQUEST;
43 import static io.netty5.handler.codec.http.HttpResponseStatus.FORBIDDEN;
44 import static io.netty5.handler.codec.http.HttpUtil.isKeepAlive;
45 import static io.netty5.handler.codec.http.HttpUtil.setContentLength;
46 import static io.netty5.handler.codec.http.HttpVersion.HTTP_1_1;
47
48
49
50
51 public class AutobahnServerHandler implements ChannelHandler {
52 private static final Logger logger = Logger.getLogger(AutobahnServerHandler.class.getName());
53
54 private WebSocketServerHandshaker handshaker;
55
56 @Override
57 public void channelRead(ChannelHandlerContext ctx, Object msg) {
58 if (msg instanceof HttpRequest) {
59 handleHttpRequest(ctx, (HttpRequest) msg);
60 } else if (msg instanceof WebSocketFrame) {
61 handleWebSocketFrame(ctx, (WebSocketFrame) msg);
62 } else {
63 throw new IllegalStateException("unknown message: " + msg);
64 }
65 }
66
67 @Override
68 public void channelReadComplete(ChannelHandlerContext ctx) {
69 ctx.flush();
70 }
71
72 private void handleHttpRequest(ChannelHandlerContext ctx, HttpRequest req) {
73
74 if (!req.decoderResult().isSuccess()) {
75 sendHttpResponse(ctx, req, new DefaultFullHttpResponse(HTTP_1_1, BAD_REQUEST,
76 ctx.bufferAllocator().allocate(0)));
77 return;
78 }
79
80
81 if (!GET.equals(req.method())) {
82 sendHttpResponse(ctx, req, new DefaultFullHttpResponse(HTTP_1_1, FORBIDDEN,
83 ctx.bufferAllocator().allocate(0)));
84 return;
85 }
86
87
88 WebSocketServerHandshakerFactory wsFactory = new WebSocketServerHandshakerFactory(
89 getWebSocketLocation(req), null, false, Integer.MAX_VALUE);
90 handshaker = wsFactory.newHandshaker(req);
91 if (handshaker == null) {
92 WebSocketServerHandshakerFactory.sendUnsupportedVersionResponse(ctx.channel());
93 } else {
94 handshaker.handshake(ctx.channel(), req);
95 }
96 }
97
98 private void handleWebSocketFrame(ChannelHandlerContext ctx, WebSocketFrame frame) {
99 if (logger.isLoggable(Level.FINE)) {
100 logger.fine(String.format(
101 "Channel %s received %s", ctx.channel().hashCode(), StringUtil.simpleClassName(frame)));
102 }
103
104 if (frame instanceof CloseWebSocketFrame) {
105 handshaker.close(ctx, (CloseWebSocketFrame) frame);
106 } else if (frame instanceof PingWebSocketFrame) {
107 ctx.write(new PongWebSocketFrame(frame.isFinalFragment(), frame.rsv(), frame.binaryData()));
108 } else if (frame instanceof TextWebSocketFrame ||
109 frame instanceof BinaryWebSocketFrame ||
110 frame instanceof ContinuationWebSocketFrame) {
111 ctx.write(frame);
112 } else if (frame instanceof PongWebSocketFrame) {
113 frame.close();
114
115 } else {
116 throw new UnsupportedOperationException(String.format("%s frame types not supported", frame.getClass()
117 .getName()));
118 }
119 }
120
121 private static void sendHttpResponse(
122 ChannelHandlerContext ctx, HttpRequest req, FullHttpResponse res) {
123
124 if (res.status().code() != 200) {
125 res.payload().writeCharSequence(res.status().toString(), CharsetUtil.UTF_8);
126 setContentLength(res, res.payload().readableBytes());
127 }
128
129
130 Future<Void> f = ctx.writeAndFlush(res);
131 if (!isKeepAlive(req) || res.status().code() != 200) {
132 f.addListener(ctx, ChannelFutureListeners.CLOSE);
133 }
134 }
135
136 @Override
137 public void channelExceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
138 ctx.close();
139 }
140
141 private static String getWebSocketLocation(HttpRequest req) {
142 return "ws://" + req.headers().get(HttpHeaderNames.HOST);
143 }
144 }