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
131 CharSequence version = req.headers().get(HttpHeaderNames.SEC_WEBSOCKET_VERSION);
132 if (version != null) {
133 if (version.equals(WebSocketVersion.V13.toHttpHeaderValue())) {
134 // Version 13 of the wire protocol - RFC 6455 (version 17 of the draft hybi specification).
135 return new WebSocketServerHandshaker13(
136 webSocketURL, subprotocols, decoderConfig);
137 } else if (version.equals(WebSocketVersion.V08.toHttpHeaderValue())) {
138 // Version 8 of the wire protocol - version 10 of the draft hybi specification.
139 return new WebSocketServerHandshaker08(
140 webSocketURL, subprotocols, decoderConfig);
141 } else if (version.equals(WebSocketVersion.V07.toHttpHeaderValue())) {
142 // Version 8 of the wire protocol - version 07 of the draft hybi specification.
143 return new WebSocketServerHandshaker07(
144 webSocketURL, subprotocols, decoderConfig);
145 } else {
146 return null;
147 }
148 } else {
149 // Assume version 00 where version header was not specified
150 return new WebSocketServerHandshaker00(webSocketURL, subprotocols, decoderConfig);
151 }
152 }
153
154 /**
155 * @deprecated use {@link #sendUnsupportedVersionResponse(Channel)}
156 */
157 @Deprecated
158 public static void sendUnsupportedWebSocketVersionResponse(Channel channel) {
159 sendUnsupportedVersionResponse(channel);
160 }
161
162 /**
163 * Return that we need cannot support the web socket version
164 */
165 public static ChannelFuture sendUnsupportedVersionResponse(Channel channel) {
166 return sendUnsupportedVersionResponse(channel, channel.newPromise());
167 }
168
169 /**
170 * Return that we need cannot support the web socket version
171 */
172 public static ChannelFuture sendUnsupportedVersionResponse(Channel channel, ChannelPromise promise) {
173 HttpResponse res = new DefaultFullHttpResponse(
174 HttpVersion.HTTP_1_1,
175 HttpResponseStatus.UPGRADE_REQUIRED, channel.alloc().buffer(0));
176 res.headers().set(HttpHeaderNames.SEC_WEBSOCKET_VERSION, WebSocketVersion.V13.toHttpHeaderValue());
177 HttpUtil.setContentLength(res, 0);
178 return channel.writeAndFlush(res, promise);
179 }
180 }