1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package org.jboss.netty.handler.codec.http.websocketx;
17
18 import static org.jboss.netty.handler.codec.http.HttpHeaders.Values.*;
19 import static org.jboss.netty.handler.codec.http.HttpVersion.*;
20
21 import java.io.UnsupportedEncodingException;
22
23 import org.jboss.netty.channel.Channel;
24 import org.jboss.netty.channel.ChannelFuture;
25 import org.jboss.netty.channel.ChannelFutureListener;
26 import org.jboss.netty.channel.ChannelPipeline;
27 import org.jboss.netty.channel.Channels;
28 import org.jboss.netty.handler.codec.http.DefaultHttpResponse;
29 import org.jboss.netty.handler.codec.http.HttpChunkAggregator;
30 import org.jboss.netty.handler.codec.http.HttpHeaders.Names;
31 import org.jboss.netty.handler.codec.http.HttpRequest;
32 import org.jboss.netty.handler.codec.http.HttpRequestDecoder;
33 import org.jboss.netty.handler.codec.http.HttpResponse;
34 import org.jboss.netty.handler.codec.http.HttpResponseEncoder;
35 import org.jboss.netty.handler.codec.http.HttpResponseStatus;
36 import org.jboss.netty.logging.InternalLogger;
37 import org.jboss.netty.logging.InternalLoggerFactory;
38 import org.jboss.netty.util.CharsetUtil;
39
40
41
42
43
44
45
46
47
48
49 public class WebSocketServerHandshaker13 extends WebSocketServerHandshaker {
50
51 private static final InternalLogger logger = InternalLoggerFactory.getInstance(WebSocketServerHandshaker13.class);
52
53 public static final String WEBSOCKET_13_ACCEPT_GUID = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
54
55 private final boolean allowExtensions;
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70 public WebSocketServerHandshaker13(String webSocketURL, String subprotocols, boolean allowExtensions) {
71 this(webSocketURL, subprotocols, allowExtensions, Long.MAX_VALUE);
72 }
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91 public WebSocketServerHandshaker13(String webSocketURL, String subprotocols, boolean allowExtensions,
92 long maxFramePayloadLength) {
93 super(WebSocketVersion.V13, webSocketURL, subprotocols, maxFramePayloadLength);
94 this.allowExtensions = allowExtensions;
95 }
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136 @Override
137 public ChannelFuture handshake(Channel channel, HttpRequest req) {
138
139 if (logger.isDebugEnabled()) {
140 logger.debug(String.format("Channel %s WS Version 13 server handshake", channel.getId()));
141 }
142
143 HttpResponse res = new DefaultHttpResponse(HTTP_1_1, HttpResponseStatus.SWITCHING_PROTOCOLS);
144
145 String key = req.getHeader(Names.SEC_WEBSOCKET_KEY);
146 if (key == null) {
147 throw new WebSocketHandshakeException("not a WebSocket request: missing key");
148 }
149 String acceptSeed = key + WEBSOCKET_13_ACCEPT_GUID;
150 byte[] sha1;
151 try {
152 sha1 = WebSocketUtil.sha1(acceptSeed.getBytes(CharsetUtil.US_ASCII.name()));
153 } catch (UnsupportedEncodingException e) {
154 return Channels.failedFuture(channel, e);
155 }
156 String accept = WebSocketUtil.base64(sha1);
157
158 if (logger.isDebugEnabled()) {
159 logger.debug(String.format("WS Version 13 Server Handshake key: %s. Response: %s.", key, accept));
160 }
161
162 res.setStatus(HttpResponseStatus.SWITCHING_PROTOCOLS);
163 res.addHeader(Names.UPGRADE, WEBSOCKET.toLowerCase());
164 res.addHeader(Names.CONNECTION, Names.UPGRADE);
165 res.addHeader(Names.SEC_WEBSOCKET_ACCEPT, accept);
166 String subprotocols = req.getHeader(Names.SEC_WEBSOCKET_PROTOCOL);
167 if (subprotocols != null) {
168 String selectedSubprotocol = selectSubprotocol(subprotocols);
169 if (selectedSubprotocol == null) {
170 throw new WebSocketHandshakeException("Requested subprotocol(s) not supported: " + subprotocols);
171 } else {
172 res.addHeader(Names.SEC_WEBSOCKET_PROTOCOL, selectedSubprotocol);
173 setSelectedSubprotocol(selectedSubprotocol);
174 }
175 }
176
177 ChannelFuture future = channel.write(res);
178
179
180 ChannelPipeline p = channel.getPipeline();
181 if (p.get(HttpChunkAggregator.class) != null) {
182 p.remove(HttpChunkAggregator.class);
183 }
184
185 p.replace(HttpRequestDecoder.class, "wsdecoder",
186 new WebSocket13FrameDecoder(true, allowExtensions, getMaxFramePayloadLength()));
187 p.replace(HttpResponseEncoder.class, "wsencoder", new WebSocket13FrameEncoder(false));
188
189 return future;
190 }
191
192
193
194
195
196
197
198
199
200 @Override
201 public ChannelFuture close(Channel channel, CloseWebSocketFrame frame) {
202 ChannelFuture f = channel.write(frame);
203 f.addListener(ChannelFutureListener.CLOSE);
204 return f;
205 }
206
207 }