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 org.jboss.netty.buffer.ChannelBuffer;
19 import org.jboss.netty.buffer.ChannelBuffers;
20 import org.jboss.netty.channel.Channel;
21 import org.jboss.netty.channel.ChannelFuture;
22 import org.jboss.netty.channel.ChannelFutureListener;
23 import org.jboss.netty.channel.ChannelPipeline;
24 import org.jboss.netty.channel.DefaultChannelFuture;
25 import org.jboss.netty.handler.codec.http.DefaultHttpRequest;
26 import org.jboss.netty.handler.codec.http.HttpHeaders.Names;
27 import org.jboss.netty.handler.codec.http.HttpHeaders.Values;
28 import org.jboss.netty.handler.codec.http.HttpMethod;
29 import org.jboss.netty.handler.codec.http.HttpRequest;
30 import org.jboss.netty.handler.codec.http.HttpRequestEncoder;
31 import org.jboss.netty.handler.codec.http.HttpResponse;
32 import org.jboss.netty.handler.codec.http.HttpResponseDecoder;
33 import org.jboss.netty.handler.codec.http.HttpResponseStatus;
34 import org.jboss.netty.handler.codec.http.HttpVersion;
35 import org.jboss.netty.logging.InternalLogger;
36 import org.jboss.netty.logging.InternalLoggerFactory;
37 import org.jboss.netty.util.CharsetUtil;
38
39 import java.net.URI;
40 import java.util.Map;
41
42
43
44
45
46
47
48
49 public class WebSocketClientHandshaker07 extends WebSocketClientHandshaker {
50
51 private static final InternalLogger logger = InternalLoggerFactory.getInstance(WebSocketClientHandshaker07.class);
52
53 public static final String MAGIC_GUID = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
54
55 private String expectedChallengeResponseString;
56
57 private final boolean allowExtensions;
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76 public WebSocketClientHandshaker07(URI webSocketURL, WebSocketVersion version, String subprotocol,
77 boolean allowExtensions, Map<String, String> customHeaders,
78 long maxFramePayloadLength) {
79 super(webSocketURL, version, subprotocol, customHeaders, maxFramePayloadLength);
80 this.allowExtensions = allowExtensions;
81 }
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103 @Override
104 public ChannelFuture handshake(Channel channel) {
105
106 URI wsURL = getWebSocketUrl();
107 String path = wsURL.getPath();
108 if (wsURL.getQuery() != null && wsURL.getQuery().length() > 0) {
109 path = wsURL.getPath() + '?' + wsURL.getQuery();
110 }
111
112 if (path == null || path.length() == 0) {
113 path = "/";
114 }
115
116
117 byte[] nonce = WebSocketUtil.randomBytes(16);
118 String key = WebSocketUtil.base64(ChannelBuffers.wrappedBuffer(nonce));
119
120 String acceptSeed = key + MAGIC_GUID;
121 ChannelBuffer sha1 = WebSocketUtil.sha1(ChannelBuffers.copiedBuffer(acceptSeed, CharsetUtil.US_ASCII));
122 expectedChallengeResponseString = WebSocketUtil.base64(sha1);
123
124 if (logger.isDebugEnabled()) {
125 logger.debug(String.format("WS Version 07 Client Handshake key: %s. Expected response: %s.", key,
126 expectedChallengeResponseString));
127 }
128
129
130 HttpRequest request = new DefaultHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.GET, path);
131 request.addHeader(Names.UPGRADE, Values.WEBSOCKET.toLowerCase());
132 request.addHeader(Names.CONNECTION, Values.UPGRADE);
133 request.addHeader(Names.SEC_WEBSOCKET_KEY, key);
134 request.addHeader(Names.HOST, wsURL.getHost());
135
136 int wsPort = wsURL.getPort();
137 String originValue = "http://" + wsURL.getHost();
138 if (wsPort != 80 && wsPort != 443) {
139
140
141 originValue = originValue + ':' + wsPort;
142 }
143 request.addHeader(Names.SEC_WEBSOCKET_ORIGIN, originValue);
144
145 String expectedSubprotocol = getExpectedSubprotocol();
146 if (expectedSubprotocol != null && expectedSubprotocol.length() > 0) {
147 request.addHeader(Names.SEC_WEBSOCKET_PROTOCOL, expectedSubprotocol);
148 }
149
150 request.addHeader(Names.SEC_WEBSOCKET_VERSION, "7");
151
152 if (customHeaders != null) {
153 for (Map.Entry<String, String> e : customHeaders.entrySet()) {
154 request.addHeader(e.getKey(), e.getValue());
155 }
156 }
157
158 final ChannelFuture handshakeFuture = new DefaultChannelFuture(channel, false);
159 ChannelFuture future = channel.write(request);
160 future.addListener(new ChannelFutureListener() {
161
162 public void operationComplete(ChannelFuture future) {
163 ChannelPipeline p = future.getChannel().getPipeline();
164 p.addAfter(
165 p.getContext(HttpRequestEncoder.class).getName(),
166 "ws-encoder", new WebSocket07FrameEncoder(true));
167
168 if (future.isSuccess()) {
169 handshakeFuture.setSuccess();
170 } else {
171 handshakeFuture.setFailure(future.getCause());
172 }
173 }
174 });
175
176 return handshakeFuture;
177 }
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198 @Override
199 public void finishHandshake(Channel channel, HttpResponse response) {
200 final HttpResponseStatus status = HttpResponseStatus.SWITCHING_PROTOCOLS;
201
202 if (!response.getStatus().equals(status)) {
203 throw new WebSocketHandshakeException("Invalid handshake response status: " + response.getStatus());
204 }
205
206 String upgrade = response.getHeader(Names.UPGRADE);
207 if (!Values.WEBSOCKET.equalsIgnoreCase(upgrade)) {
208 throw new WebSocketHandshakeException("Invalid handshake response upgrade: "
209 + response.getHeader(Names.UPGRADE));
210 }
211
212 String connection = response.getHeader(Names.CONNECTION);
213 if (!Values.UPGRADE.equalsIgnoreCase(connection)) {
214 throw new WebSocketHandshakeException("Invalid handshake response connection: "
215 + response.getHeader(Names.CONNECTION));
216 }
217
218 String accept = response.getHeader(Names.SEC_WEBSOCKET_ACCEPT);
219 if (accept == null || !accept.equals(expectedChallengeResponseString)) {
220 throw new WebSocketHandshakeException(String.format("Invalid challenge. Actual: %s. Expected: %s", accept,
221 expectedChallengeResponseString));
222 }
223
224 String subprotocol = response.getHeader(Names.SEC_WEBSOCKET_PROTOCOL);
225 setActualSubprotocol(subprotocol);
226
227 setHandshakeComplete();
228
229 ChannelPipeline p = channel.getPipeline();
230 p.get(HttpResponseDecoder.class).replace(
231 "ws-decoder",
232 new WebSocket07FrameDecoder(false, allowExtensions, getMaxFramePayloadLength()));
233 }
234 }