View Javadoc
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 java.util.Objects;
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 = Objects.requireNonNull(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 }