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 static io.netty.util.internal.ObjectUtil.checkNotNull;
19
20 /**
21 * WebSocket status codes specified in RFC-6455.
22 * <pre>
23 *
24 * RFC-6455 The WebSocket Protocol, December 2011:
25 * <a href="https://tools.ietf.org/html/rfc6455#section-7.4.1"
26 * >https://tools.ietf.org/html/rfc6455#section-7.4.1</a>
27 *
28 * WebSocket Protocol Registries, April 2019:
29 * <a href="https://www.iana.org/assignments/websocket/websocket.xhtml#close-code-number"
30 * >https://www.iana.org/assignments/websocket/websocket.xhtml</a>
31 *
32 * 7.4.1. Defined Status Codes
33 *
34 * Endpoints MAY use the following pre-defined status codes when sending
35 * a Close frame.
36 *
37 * 1000
38 *
39 * 1000 indicates a normal closure, meaning that the purpose for
40 * which the connection was established has been fulfilled.
41 *
42 * 1001
43 *
44 * 1001 indicates that an endpoint is "going away", such as a server
45 * going down or a browser having navigated away from a page.
46 *
47 * 1002
48 *
49 * 1002 indicates that an endpoint is terminating the connection due
50 * to a protocol error.
51 *
52 * 1003
53 *
54 * 1003 indicates that an endpoint is terminating the connection
55 * because it has received a type of data it cannot accept (e.g., an
56 * endpoint that understands only text data MAY send this if it
57 * receives a binary message).
58 *
59 * 1004
60 *
61 * Reserved. The specific meaning might be defined in the future.
62 *
63 * 1005
64 *
65 * 1005 is a reserved value and MUST NOT be set as a status code in a
66 * Close control frame by an endpoint. It is designated for use in
67 * applications expecting a status code to indicate that no status
68 * code was actually present.
69 *
70 * 1006
71 *
72 * 1006 is a reserved value and MUST NOT be set as a status code in a
73 * Close control frame by an endpoint. It is designated for use in
74 * applications expecting a status code to indicate that the
75 * connection was closed abnormally, e.g., without sending or
76 * receiving a Close control frame.
77 *
78 * 1007
79 *
80 * 1007 indicates that an endpoint is terminating the connection
81 * because it has received data within a message that was not
82 * consistent with the type of the message (e.g., non-UTF-8 [RFC3629]
83 * data within a text message).
84 *
85 * 1008
86 *
87 * 1008 indicates that an endpoint is terminating the connection
88 * because it has received a message that violates its policy. This
89 * is a generic status code that can be returned when there is no
90 * other more suitable status code (e.g., 1003 or 1009) or if there
91 * is a need to hide specific details about the policy.
92 *
93 * 1009
94 *
95 * 1009 indicates that an endpoint is terminating the connection
96 * because it has received a message that is too big for it to
97 * process.
98 *
99 * 1010
100 *
101 * 1010 indicates that an endpoint (client) is terminating the
102 * connection because it has expected the server to negotiate one or
103 * more extension, but the server didn't return them in the response
104 * message of the WebSocket handshake. The list of extensions that
105 * are needed SHOULD appear in the /reason/ part of the Close frame.
106 * Note that this status code is not used by the server, because it
107 * can fail the WebSocket handshake instead.
108 *
109 * 1011
110 *
111 * 1011 indicates that a server is terminating the connection because
112 * it encountered an unexpected condition that prevented it from
113 * fulfilling the request.
114 *
115 * 1012 (IANA Registry, Non RFC-6455)
116 *
117 * 1012 indicates that the service is restarted. a client may reconnect,
118 * and if it choses to do, should reconnect using a randomized delay
119 * of 5 - 30 seconds.
120 *
121 * 1013 (IANA Registry, Non RFC-6455)
122 *
123 * 1013 indicates that the service is experiencing overload. a client
124 * should only connect to a different IP (when there are multiple for the
125 * target) or reconnect to the same IP upon user action.
126 *
127 * 1014 (IANA Registry, Non RFC-6455)
128 *
129 * The server was acting as a gateway or proxy and received an invalid
130 * response from the upstream server. This is similar to 502 HTTP Status Code.
131 *
132 * 1015
133 *
134 * 1015 is a reserved value and MUST NOT be set as a status code in a
135 * Close control frame by an endpoint. It is designated for use in
136 * applications expecting a status code to indicate that the
137 * connection was closed due to a failure to perform a TLS handshake
138 * (e.g., the server certificate can't be verified).
139 *
140 *
141 * 7.4.2. Reserved Status Code Ranges
142 *
143 * 0-999
144 *
145 * Status codes in the range 0-999 are not used.
146 *
147 * 1000-2999
148 *
149 * Status codes in the range 1000-2999 are reserved for definition by
150 * this protocol, its future revisions, and extensions specified in a
151 * permanent and readily available public specification.
152 *
153 * 3000-3999
154 *
155 * Status codes in the range 3000-3999 are reserved for use by
156 * libraries, frameworks, and applications. These status codes are
157 * registered directly with IANA. The interpretation of these codes
158 * is undefined by this protocol.
159 *
160 * 4000-4999
161 *
162 * Status codes in the range 4000-4999 are reserved for private use
163 * and thus can't be registered. Such codes can be used by prior
164 * agreements between WebSocket applications. The interpretation of
165 * these codes is undefined by this protocol.
166 * </pre>
167 * <p>
168 * While {@link WebSocketCloseStatus} is enum-like structure, its instances should NOT be compared by reference.
169 * Instead, either {@link #equals(Object)} should be used or direct comparison of {@link #code()} value.
170 */
171 public final class WebSocketCloseStatus implements Comparable<WebSocketCloseStatus> {
172
173 public static final WebSocketCloseStatus NORMAL_CLOSURE =
174 new WebSocketCloseStatus(1000, "Bye");
175
176 public static final WebSocketCloseStatus ENDPOINT_UNAVAILABLE =
177 new WebSocketCloseStatus(1001, "Endpoint unavailable");
178
179 public static final WebSocketCloseStatus PROTOCOL_ERROR =
180 new WebSocketCloseStatus(1002, "Protocol error");
181
182 public static final WebSocketCloseStatus INVALID_MESSAGE_TYPE =
183 new WebSocketCloseStatus(1003, "Invalid message type");
184
185 public static final WebSocketCloseStatus INVALID_PAYLOAD_DATA =
186 new WebSocketCloseStatus(1007, "Invalid payload data");
187
188 public static final WebSocketCloseStatus POLICY_VIOLATION =
189 new WebSocketCloseStatus(1008, "Policy violation");
190
191 public static final WebSocketCloseStatus MESSAGE_TOO_BIG =
192 new WebSocketCloseStatus(1009, "Message too big");
193
194 public static final WebSocketCloseStatus MANDATORY_EXTENSION =
195 new WebSocketCloseStatus(1010, "Mandatory extension");
196
197 public static final WebSocketCloseStatus INTERNAL_SERVER_ERROR =
198 new WebSocketCloseStatus(1011, "Internal server error");
199
200 public static final WebSocketCloseStatus SERVICE_RESTART =
201 new WebSocketCloseStatus(1012, "Service Restart");
202
203 public static final WebSocketCloseStatus TRY_AGAIN_LATER =
204 new WebSocketCloseStatus(1013, "Try Again Later");
205
206 public static final WebSocketCloseStatus BAD_GATEWAY =
207 new WebSocketCloseStatus(1014, "Bad Gateway");
208
209 // 1004, 1005, 1006, 1015 are reserved and should never be used by user
210 //public static final WebSocketCloseStatus SPECIFIC_MEANING = register(1004, "...");
211
212 public static final WebSocketCloseStatus EMPTY =
213 new WebSocketCloseStatus(1005, "Empty", false);
214
215 public static final WebSocketCloseStatus ABNORMAL_CLOSURE =
216 new WebSocketCloseStatus(1006, "Abnormal closure", false);
217
218 public static final WebSocketCloseStatus TLS_HANDSHAKE_FAILED =
219 new WebSocketCloseStatus(1015, "TLS handshake failed", false);
220
221 private final int statusCode;
222 private final String reasonText;
223 private String text;
224
225 public WebSocketCloseStatus(int statusCode, String reasonText) {
226 this(statusCode, reasonText, true);
227 }
228
229 public WebSocketCloseStatus(int statusCode, String reasonText, boolean validate) {
230 if (validate && !isValidStatusCode(statusCode)) {
231 throw new IllegalArgumentException(
232 "WebSocket close status code does NOT comply with RFC-6455: " + statusCode);
233 }
234 this.statusCode = statusCode;
235 this.reasonText = checkNotNull(reasonText, "reasonText");
236 }
237
238 public int code() {
239 return statusCode;
240 }
241
242 public String reasonText() {
243 return reasonText;
244 }
245
246 /**
247 * Order of {@link WebSocketCloseStatus} only depends on {@link #code()}.
248 */
249 @Override
250 public int compareTo(WebSocketCloseStatus o) {
251 return code() - o.code();
252 }
253
254 /**
255 * Equality of {@link WebSocketCloseStatus} only depends on {@link #code()}.
256 */
257 @Override
258 public boolean equals(Object o) {
259 if (this == o) {
260 return true;
261 }
262 if (null == o || getClass() != o.getClass()) {
263 return false;
264 }
265
266 WebSocketCloseStatus that = (WebSocketCloseStatus) o;
267
268 return statusCode == that.statusCode;
269 }
270
271 @Override
272 public int hashCode() {
273 return statusCode;
274 }
275
276 @Override
277 public String toString() {
278 String text = this.text;
279 if (text == null) {
280 // E.g.: "1000 Bye", "1009 Message too big"
281 this.text = text = code() + " " + reasonText();
282 }
283 return text;
284 }
285
286 public static boolean isValidStatusCode(int code) {
287 return code < 0 ||
288 1000 <= code && code <= 1003 ||
289 1007 <= code && code <= 1014 ||
290 3000 <= code;
291 }
292
293 public static WebSocketCloseStatus valueOf(int code) {
294 switch (code) {
295 case 1000:
296 return NORMAL_CLOSURE;
297 case 1001:
298 return ENDPOINT_UNAVAILABLE;
299 case 1002:
300 return PROTOCOL_ERROR;
301 case 1003:
302 return INVALID_MESSAGE_TYPE;
303 case 1005:
304 return EMPTY;
305 case 1006:
306 return ABNORMAL_CLOSURE;
307 case 1007:
308 return INVALID_PAYLOAD_DATA;
309 case 1008:
310 return POLICY_VIOLATION;
311 case 1009:
312 return MESSAGE_TOO_BIG;
313 case 1010:
314 return MANDATORY_EXTENSION;
315 case 1011:
316 return INTERNAL_SERVER_ERROR;
317 case 1012:
318 return SERVICE_RESTART;
319 case 1013:
320 return TRY_AGAIN_LATER;
321 case 1014:
322 return BAD_GATEWAY;
323 case 1015:
324 return TLS_HANDSHAKE_FAILED;
325 default:
326 return new WebSocketCloseStatus(code, "Close status #" + code);
327 }
328 }
329
330 }