1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package io.netty.handler.codec.http.websocketx;
17
18 import io.netty.handler.codec.http.DefaultFullHttpRequest;
19 import io.netty.handler.codec.http.FullHttpRequest;
20 import io.netty.handler.codec.http.FullHttpResponse;
21 import io.netty.handler.codec.http.HttpHeaders;
22 import io.netty.handler.codec.http.HttpHeaders.Names;
23 import io.netty.handler.codec.http.HttpHeaders.Values;
24 import io.netty.handler.codec.http.HttpMethod;
25 import io.netty.handler.codec.http.HttpResponseStatus;
26 import io.netty.handler.codec.http.HttpVersion;
27 import io.netty.util.CharsetUtil;
28 import io.netty.util.internal.logging.InternalLogger;
29 import io.netty.util.internal.logging.InternalLoggerFactory;
30
31 import java.net.URI;
32
33
34
35
36
37
38
39
40 public class WebSocketClientHandshaker13 extends WebSocketClientHandshaker {
41
42 private static final InternalLogger logger = InternalLoggerFactory.getInstance(WebSocketClientHandshaker13.class);
43
44 public static final String MAGIC_GUID = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
45
46 private String expectedChallengeResponseString;
47
48 private final boolean allowExtensions;
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67 public WebSocketClientHandshaker13(URI webSocketURL, WebSocketVersion version, String subprotocol,
68 boolean allowExtensions, HttpHeaders customHeaders, int maxFramePayloadLength) {
69 super(webSocketURL, version, subprotocol, customHeaders, maxFramePayloadLength);
70 this.allowExtensions = allowExtensions;
71 }
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91 @Override
92 protected FullHttpRequest newHandshakeRequest() {
93
94 URI wsURL = uri();
95 String path = rawPath(wsURL);
96
97
98 byte[] nonce = WebSocketUtil.randomBytes(16);
99 String key = WebSocketUtil.base64(nonce);
100
101 String acceptSeed = key + MAGIC_GUID;
102 byte[] sha1 = WebSocketUtil.sha1(acceptSeed.getBytes(CharsetUtil.US_ASCII));
103 expectedChallengeResponseString = WebSocketUtil.base64(sha1);
104
105 if (logger.isDebugEnabled()) {
106 logger.debug(
107 "WebSocket version 13 client handshake key: {}, expected response: {}",
108 key, expectedChallengeResponseString);
109 }
110
111
112 FullHttpRequest request = new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.GET, path);
113 HttpHeaders headers = request.headers();
114
115 headers.add(HttpHeaders.Names.UPGRADE, HttpHeaders.Values.WEBSOCKET)
116 .add(HttpHeaders.Names.CONNECTION, HttpHeaders.Values.UPGRADE)
117 .add(HttpHeaders.Names.SEC_WEBSOCKET_KEY, key)
118 .add(HttpHeaders.Names.HOST, websocketHostValue(wsURL))
119 .add(HttpHeaders.Names.SEC_WEBSOCKET_ORIGIN, websocketOriginValue(wsURL));
120
121 String expectedSubprotocol = expectedSubprotocol();
122 if (expectedSubprotocol != null && !expectedSubprotocol.isEmpty()) {
123 headers.add(Names.SEC_WEBSOCKET_PROTOCOL, expectedSubprotocol);
124 }
125
126 headers.add(Names.SEC_WEBSOCKET_VERSION, "13");
127
128 if (customHeaders != null) {
129 headers.add(customHeaders);
130 }
131 return request;
132 }
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151 @Override
152 protected void verify(FullHttpResponse response) {
153 final HttpResponseStatus status = HttpResponseStatus.SWITCHING_PROTOCOLS;
154 final HttpHeaders headers = response.headers();
155
156 if (!response.getStatus().equals(status)) {
157 throw new WebSocketHandshakeException("Invalid handshake response getStatus: " + response.getStatus());
158 }
159
160 String upgrade = headers.get(Names.UPGRADE);
161 if (!Values.WEBSOCKET.equalsIgnoreCase(upgrade)) {
162 throw new WebSocketHandshakeException("Invalid handshake response upgrade: " + upgrade);
163 }
164
165 if (!headers.containsValue(Names.CONNECTION, Values.UPGRADE, true)) {
166 throw new WebSocketHandshakeException("Invalid handshake response connection: "
167 + headers.get(Names.CONNECTION));
168 }
169
170 String accept = headers.get(Names.SEC_WEBSOCKET_ACCEPT);
171 if (accept == null || !accept.equals(expectedChallengeResponseString)) {
172 throw new WebSocketHandshakeException(String.format(
173 "Invalid challenge. Actual: %s. Expected: %s", accept, expectedChallengeResponseString));
174 }
175 }
176
177 @Override
178 protected WebSocketFrameDecoder newWebsocketDecoder() {
179 return new WebSocket13FrameDecoder(false, allowExtensions, maxFramePayloadLength());
180 }
181
182 @Override
183 protected WebSocketFrameEncoder newWebSocketEncoder() {
184 return new WebSocket13FrameEncoder(true);
185 }
186 }