1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package org.jboss.netty.example.http.websocket;
17
18 import static org.jboss.netty.handler.codec.http.HttpHeaders.*;
19 import static org.jboss.netty.handler.codec.http.HttpHeaders.Names.*;
20 import static org.jboss.netty.handler.codec.http.HttpHeaders.Values.*;
21 import static org.jboss.netty.handler.codec.http.HttpMethod.*;
22 import static org.jboss.netty.handler.codec.http.HttpResponseStatus.*;
23 import static org.jboss.netty.handler.codec.http.HttpVersion.*;
24
25 import java.security.MessageDigest;
26
27 import org.jboss.netty.buffer.ChannelBuffer;
28 import org.jboss.netty.buffer.ChannelBuffers;
29 import org.jboss.netty.channel.ChannelFuture;
30 import org.jboss.netty.channel.ChannelFutureListener;
31 import org.jboss.netty.channel.ChannelHandlerContext;
32 import org.jboss.netty.channel.ChannelPipeline;
33 import org.jboss.netty.channel.ExceptionEvent;
34 import org.jboss.netty.channel.MessageEvent;
35 import org.jboss.netty.channel.SimpleChannelUpstreamHandler;
36 import org.jboss.netty.handler.codec.http.DefaultHttpResponse;
37 import org.jboss.netty.handler.codec.http.HttpHeaders;
38 import org.jboss.netty.handler.codec.http.HttpRequest;
39 import org.jboss.netty.handler.codec.http.HttpResponse;
40 import org.jboss.netty.handler.codec.http.HttpResponseStatus;
41 import org.jboss.netty.handler.codec.http.HttpHeaders.Names;
42 import org.jboss.netty.handler.codec.http.HttpHeaders.Values;
43 import org.jboss.netty.handler.codec.http.websocket.DefaultWebSocketFrame;
44 import org.jboss.netty.handler.codec.http.websocket.WebSocketFrame;
45 import org.jboss.netty.handler.codec.http.websocket.WebSocketFrameDecoder;
46 import org.jboss.netty.handler.codec.http.websocket.WebSocketFrameEncoder;
47 import org.jboss.netty.util.CharsetUtil;
48
49
50
51
52
53
54
55 public class WebSocketServerHandler extends SimpleChannelUpstreamHandler {
56
57 private static final String WEBSOCKET_PATH = "/websocket";
58
59 @Override
60 public void messageReceived(ChannelHandlerContext ctx, MessageEvent e) throws Exception {
61 Object msg = e.getMessage();
62 if (msg instanceof HttpRequest) {
63 handleHttpRequest(ctx, (HttpRequest) msg);
64 } else if (msg instanceof WebSocketFrame) {
65 handleWebSocketFrame(ctx, (WebSocketFrame) msg);
66 }
67 }
68
69 private void handleHttpRequest(ChannelHandlerContext ctx, HttpRequest req) throws Exception {
70
71 if (req.getMethod() != GET) {
72 sendHttpResponse(
73 ctx, req, new DefaultHttpResponse(HTTP_1_1, FORBIDDEN));
74 return;
75 }
76
77
78 if (req.getUri().equals("/")) {
79 HttpResponse res = new DefaultHttpResponse(HTTP_1_1, OK);
80
81 ChannelBuffer content =
82 WebSocketServerIndexPage.getContent(getWebSocketLocation(req));
83
84 res.setHeader(CONTENT_TYPE, "text/html; charset=UTF-8");
85 setContentLength(res, content.readableBytes());
86
87 res.setContent(content);
88 sendHttpResponse(ctx, req, res);
89 return;
90 }
91
92
93 if (req.getUri().equals(WEBSOCKET_PATH) &&
94 Values.UPGRADE.equalsIgnoreCase(req.getHeader(CONNECTION)) &&
95 WEBSOCKET.equalsIgnoreCase(req.getHeader(Names.UPGRADE))) {
96
97
98 HttpResponse res = new DefaultHttpResponse(
99 HTTP_1_1,
100 new HttpResponseStatus(101, "Web Socket Protocol Handshake"));
101 res.addHeader(Names.UPGRADE, WEBSOCKET);
102 res.addHeader(CONNECTION, Values.UPGRADE);
103
104
105 if (req.containsHeader(SEC_WEBSOCKET_KEY1) &&
106 req.containsHeader(SEC_WEBSOCKET_KEY2)) {
107
108 res.addHeader(SEC_WEBSOCKET_ORIGIN, req.getHeader(ORIGIN));
109 res.addHeader(SEC_WEBSOCKET_LOCATION, getWebSocketLocation(req));
110 String protocol = req.getHeader(SEC_WEBSOCKET_PROTOCOL);
111 if (protocol != null) {
112 res.addHeader(SEC_WEBSOCKET_PROTOCOL, protocol);
113 }
114
115
116 String key1 = req.getHeader(SEC_WEBSOCKET_KEY1);
117 String key2 = req.getHeader(SEC_WEBSOCKET_KEY2);
118 int a = (int) (Long.parseLong(key1.replaceAll("[^0-9]", "")) / key1.replaceAll("[^ ]", "").length());
119 int b = (int) (Long.parseLong(key2.replaceAll("[^0-9]", "")) / key2.replaceAll("[^ ]", "").length());
120 long c = req.getContent().readLong();
121 ChannelBuffer input = ChannelBuffers.buffer(16);
122 input.writeInt(a);
123 input.writeInt(b);
124 input.writeLong(c);
125 ChannelBuffer output = ChannelBuffers.wrappedBuffer(
126 MessageDigest.getInstance("MD5").digest(input.array()));
127 res.setContent(output);
128 } else {
129
130 res.addHeader(WEBSOCKET_ORIGIN, req.getHeader(ORIGIN));
131 res.addHeader(WEBSOCKET_LOCATION, getWebSocketLocation(req));
132 String protocol = req.getHeader(WEBSOCKET_PROTOCOL);
133 if (protocol != null) {
134 res.addHeader(WEBSOCKET_PROTOCOL, protocol);
135 }
136 }
137
138
139 ChannelPipeline p = ctx.getChannel().getPipeline();
140 p.remove("aggregator");
141 p.replace("decoder", "wsdecoder", new WebSocketFrameDecoder());
142
143 ctx.getChannel().write(res);
144
145 p.replace("encoder", "wsencoder", new WebSocketFrameEncoder());
146 return;
147 }
148
149
150 sendHttpResponse(
151 ctx, req, new DefaultHttpResponse(HTTP_1_1, FORBIDDEN));
152 }
153
154 private void handleWebSocketFrame(ChannelHandlerContext ctx, WebSocketFrame frame) {
155
156 ctx.getChannel().write(
157 new DefaultWebSocketFrame(frame.getTextData().toUpperCase()));
158 }
159
160 private void sendHttpResponse(ChannelHandlerContext ctx, HttpRequest req, HttpResponse res) {
161
162 if (res.getStatus().getCode() != 200) {
163 res.setContent(
164 ChannelBuffers.copiedBuffer(
165 res.getStatus().toString(), CharsetUtil.UTF_8));
166 setContentLength(res, res.getContent().readableBytes());
167 }
168
169
170 ChannelFuture f = ctx.getChannel().write(res);
171 if (!isKeepAlive(req) || res.getStatus().getCode() != 200) {
172 f.addListener(ChannelFutureListener.CLOSE);
173 }
174 }
175
176 @Override
177 public void exceptionCaught(ChannelHandlerContext ctx, ExceptionEvent e)
178 throws Exception {
179 e.getCause().printStackTrace();
180 e.getChannel().close();
181 }
182
183 private String getWebSocketLocation(HttpRequest req) {
184 return "ws://" + req.getHeader(HttpHeaders.Names.HOST) + WEBSOCKET_PATH;
185 }
186 }