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.netty5.handler.codec.http.websocketx;
17
18 import io.netty5.channel.Channel;
19 import io.netty5.handler.codec.http.DefaultFullHttpResponse;
20 import io.netty5.handler.codec.http.HttpHeaderNames;
21 import io.netty5.handler.codec.http.HttpRequest;
22 import io.netty5.handler.codec.http.HttpResponse;
23 import io.netty5.handler.codec.http.HttpResponseStatus;
24 import io.netty5.handler.codec.http.HttpUtil;
25 import io.netty5.handler.codec.http.HttpVersion;
26 import io.netty5.util.concurrent.Future;
27
28 import java.util.Objects;
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 = Objects.requireNonNull(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 && version.equals(WebSocketVersion.V13.toHttpHeaderValue())) {
133 // Version 13 of the wire protocol - RFC 6455 (version 17 of the draft hybi specification).
134 return new WebSocketServerHandshaker13(webSocketURL, subprotocols, decoderConfig);
135 }
136 return null;
137 }
138
139 /**
140 * @deprecated use {@link #sendUnsupportedVersionResponse(Channel)}
141 */
142 @Deprecated
143 public static void sendUnsupportedWebSocketVersionResponse(Channel channel) {
144 sendUnsupportedVersionResponse(channel);
145 }
146
147 /**
148 * Return that we need cannot not support the web socket version
149 */
150 public static Future<Void> sendUnsupportedVersionResponse(Channel channel) {
151 HttpResponse res = new DefaultFullHttpResponse(
152 HttpVersion.HTTP_1_1,
153 HttpResponseStatus.UPGRADE_REQUIRED, channel.bufferAllocator().allocate(0));
154 res.headers().set(HttpHeaderNames.SEC_WEBSOCKET_VERSION, WebSocketVersion.V13.toHttpHeaderValue());
155 HttpUtil.setContentLength(res, 0);
156 return channel.writeAndFlush(res);
157 }
158 }