1 /*
2 * Copyright 2019 The Netty Project
3 *
4 * The Netty Project licenses this file to you under the Apache License,
5 * version 2.0 (the "License"); you may not use this file except in compliance
6 * with the License. You may obtain a copy of the License at:
7 *
8 * https://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13 * License for the specific language governing permissions and limitations
14 * under the License.
15 */
16 package io.netty.handler.codec.http.websocketx;
17
18 import io.netty.channel.Channel;
19 import io.netty.channel.ChannelFuture;
20 import io.netty.channel.ChannelPromise;
21 import io.netty.handler.codec.http.DefaultFullHttpResponse;
22 import io.netty.handler.codec.http.HttpHeaderNames;
23 import io.netty.handler.codec.http.HttpUtil;
24 import io.netty.handler.codec.http.HttpRequest;
25 import io.netty.handler.codec.http.HttpResponse;
26 import io.netty.handler.codec.http.HttpResponseStatus;
27 import io.netty.handler.codec.http.HttpVersion;
28 import io.netty.util.internal.ObjectUtil;
29
30 /**
31 * Auto-detects the version of the Web Socket protocol in use and creates a new proper
32 * {@link WebSocketServerHandshaker}.
33 */
34 public class WebSocketServerHandshakerFactory {
35
36 private final String webSocketURL;
37
38 private final String subprotocols;
39
40 private final WebSocketDecoderConfig decoderConfig;
41
42 /**
43 * Constructor specifying the destination web socket location
44 *
45 * @param webSocketURL
46 * URL for web socket communications. e.g "ws://myhost.com/mypath".
47 * Subsequent web socket frames will be sent to this URL.
48 * @param subprotocols
49 * CSV of supported protocols. Null if sub protocols not supported.
50 * @param allowExtensions
51 * Allow extensions to be used in the reserved bits of the web socket frame
52 */
53 public WebSocketServerHandshakerFactory(
54 String webSocketURL, String subprotocols, boolean allowExtensions) {
55 this(webSocketURL, subprotocols, allowExtensions, 65536);
56 }
57
58 /**
59 * Constructor specifying the destination web socket location
60 *
61 * @param webSocketURL
62 * URL for web socket communications. e.g "ws://myhost.com/mypath".
63 * Subsequent web socket frames will be sent to this URL.
64 * @param subprotocols
65 * CSV of supported protocols. Null if sub protocols not supported.
66 * @param allowExtensions
67 * Allow extensions to be used in the reserved bits of the web socket frame
68 * @param maxFramePayloadLength
69 * Maximum allowable frame payload length. Setting this value to your application's
70 * requirement may reduce denial of service attacks using long data frames.
71 */
72 public WebSocketServerHandshakerFactory(
73 String webSocketURL, String subprotocols, boolean allowExtensions,
74 int maxFramePayloadLength) {
75 this(webSocketURL, subprotocols, allowExtensions, maxFramePayloadLength, false);
76 }
77
78 /**
79 * Constructor specifying the destination web socket location
80 *
81 * @param webSocketURL
82 * URL for web socket communications. e.g "ws://myhost.com/mypath".
83 * Subsequent web socket frames will be sent to this URL.
84 * @param subprotocols
85 * CSV of supported protocols. Null if sub protocols not supported.
86 * @param allowExtensions
87 * Allow extensions to be used in the reserved bits of the web socket frame
88 * @param maxFramePayloadLength
89 * Maximum allowable frame payload length. Setting this value to your application's
90 * requirement may reduce denial of service attacks using long data frames.
91 * @param allowMaskMismatch
92 * When set to true, frames which are not masked properly according to the standard will still be
93 * accepted.
94 */
95 public WebSocketServerHandshakerFactory(
96 String webSocketURL, String subprotocols, boolean allowExtensions,
97 int maxFramePayloadLength, boolean allowMaskMismatch) {
98 this(webSocketURL, subprotocols, WebSocketDecoderConfig.newBuilder()
99 .allowExtensions(allowExtensions)
100 .maxFramePayloadLength(maxFramePayloadLength)
101 .allowMaskMismatch(allowMaskMismatch)
102 .build());
103 }
104
105 /**
106 * Constructor specifying the destination web socket location
107 *
108 * @param webSocketURL
109 * URL for web socket communications. e.g "ws://myhost.com/mypath".
110 * Subsequent web socket frames will be sent to this URL.
111 * @param subprotocols
112 * CSV of supported protocols. Null if sub protocols not supported.
113 * @param decoderConfig
114 * Frames decoder options.
115 */
116 public WebSocketServerHandshakerFactory(
117 String webSocketURL, String subprotocols, WebSocketDecoderConfig decoderConfig) {
118 this.webSocketURL = webSocketURL;
119 this.subprotocols = subprotocols;
120 this.decoderConfig = ObjectUtil.checkNotNull(decoderConfig, "decoderConfig");
121 }
122
123 /**
124 * Instances a new handshaker
125 *
126 * @return A new WebSocketServerHandshaker for the requested web socket version. Null if web
127 * socket version is not supported.
128 */
129 public WebSocketServerHandshaker newHandshaker(HttpRequest req) {
130 return resolveHandshaker0(req, webSocketURL, subprotocols, decoderConfig);
131 }
132
133 /**
134 * Resolves the client's WebSocket protocol version from the HTTP request header,
135 * and creates the corresponding handshaker.
136 * This method is identical to {@link #newHandshaker(HttpRequest)},
137 * however it does not require a {@link WebSocketServerHandshakerFactory} instance allocation.
138 *
139 * @param req
140 * The HTTP request that came from the client to upgrade the protocol to WebSocket.
141 * @param webSocketURL
142 * URL for web socket communications. e.g "ws://myhost.com/mypath".
143 * Subsequent web socket frames will be sent to this URL.
144 * @param subprotocols
145 * CSV of supported protocols. Null if sub protocols not supported.
146 * @param decoderConfig
147 * Frames decoder options.
148 * @return A new {@link WebSocketServerHandshaker} for the requested web socket version. Null if web
149 * socket version is not supported.
150 * @throws NullPointerException if the {@code decoderConfig} is {@code null}.
151 */
152 public static WebSocketServerHandshaker resolveHandshaker(HttpRequest req, String webSocketURL, String subprotocols,
153 WebSocketDecoderConfig decoderConfig) {
154 ObjectUtil.checkNotNull(decoderConfig, "decoderConfig");
155 return resolveHandshaker0(req, webSocketURL, subprotocols, decoderConfig);
156 }
157
158 private static WebSocketServerHandshaker resolveHandshaker0(HttpRequest req,
159 String webSocketURL,
160 String subprotocols,
161 WebSocketDecoderConfig decoderConfig) {
162 CharSequence version = req.headers().get(HttpHeaderNames.SEC_WEBSOCKET_VERSION);
163 if (version != null) {
164 if (version.equals(WebSocketVersion.V13.toHttpHeaderValue())) {
165 // Version 13 of the wire protocol - RFC 6455 (version 17 of the draft hybi specification).
166 return new WebSocketServerHandshaker13(
167 webSocketURL, subprotocols, decoderConfig);
168 } else if (version.equals(WebSocketVersion.V08.toHttpHeaderValue())) {
169 // Version 8 of the wire protocol - version 10 of the draft hybi specification.
170 return new WebSocketServerHandshaker08(
171 webSocketURL, subprotocols, decoderConfig);
172 } else if (version.equals(WebSocketVersion.V07.toHttpHeaderValue())) {
173 // Version 8 of the wire protocol - version 07 of the draft hybi specification.
174 return new WebSocketServerHandshaker07(
175 webSocketURL, subprotocols, decoderConfig);
176 } else {
177 return null;
178 }
179 } else {
180 // Assume version 00 where version header was not specified
181 return new WebSocketServerHandshaker00(webSocketURL, subprotocols, decoderConfig);
182 }
183 }
184
185 /**
186 * @deprecated use {@link #sendUnsupportedVersionResponse(Channel)}
187 */
188 @Deprecated
189 public static void sendUnsupportedWebSocketVersionResponse(Channel channel) {
190 sendUnsupportedVersionResponse(channel);
191 }
192
193 /**
194 * Return that we need cannot support the web socket version
195 */
196 public static ChannelFuture sendUnsupportedVersionResponse(Channel channel) {
197 return sendUnsupportedVersionResponse(channel, channel.newPromise());
198 }
199
200 /**
201 * Return that we need cannot support the web socket version
202 */
203 public static ChannelFuture sendUnsupportedVersionResponse(Channel channel, ChannelPromise promise) {
204 HttpResponse res = new DefaultFullHttpResponse(
205 HttpVersion.HTTP_1_1,
206 HttpResponseStatus.UPGRADE_REQUIRED, channel.alloc().buffer(0));
207 res.headers().set(HttpHeaderNames.SEC_WEBSOCKET_VERSION, WebSocketVersion.V13.toHttpHeaderValue());
208 HttpUtil.setContentLength(res, 0);
209 return channel.writeAndFlush(res, promise);
210 }
211 }