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    *   http://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     //public static final WebSocketCloseStatus EMPTY = register(1005, "Empty");
212     //public static final WebSocketCloseStatus ABNORMAL_CLOSURE = register(1006, "Abnormal closure");
213     //public static final WebSocketCloseStatus TLS_HANDSHAKE_FAILED(1015, "TLS handshake failed");
214 
215     private final int statusCode;
216     private final String reasonText;
217     private String text;
218 
219     public WebSocketCloseStatus(int statusCode, String reasonText) {
220         if (!isValidStatusCode(statusCode)) {
221             throw new IllegalArgumentException(
222                 "WebSocket close status code does NOT comply with RFC-6455: " + statusCode);
223         }
224         this.statusCode = statusCode;
225         this.reasonText = checkNotNull(reasonText, "reasonText");
226     }
227 
228     public int code() {
229         return statusCode;
230     }
231 
232     public String reasonText() {
233         return reasonText;
234     }
235 
236     /**
237      * Order of {@link WebSocketCloseStatus} only depends on {@link #code()}.
238      */
239     @Override
240     public int compareTo(WebSocketCloseStatus o) {
241         return code() - o.code();
242     }
243 
244     /**
245      * Equality of {@link WebSocketCloseStatus} only depends on {@link #code()}.
246      */
247     @Override
248     public boolean equals(Object o) {
249         if (this == o) {
250             return true;
251         }
252         if (null == o || getClass() != o.getClass()) {
253             return false;
254         }
255 
256         WebSocketCloseStatus that = (WebSocketCloseStatus) o;
257 
258         return statusCode == that.statusCode;
259     }
260 
261     @Override
262     public int hashCode() {
263         return statusCode;
264     }
265 
266     @Override
267     public String toString() {
268         String text = this.text;
269         if (text == null) {
270             // E.g.: "1000 Bye", "1009 Message too big"
271             this.text = text = code() + " " + reasonText();
272         }
273         return text;
274     }
275 
276     public static boolean isValidStatusCode(int code) {
277         return code < 0 ||
278             1000 <= code && code <= 1003 ||
279             1007 <= code && code <= 1014 ||
280             3000 <= code;
281     }
282 
283     public static WebSocketCloseStatus valueOf(int code) {
284         switch (code) {
285             case 1000:
286                 return NORMAL_CLOSURE;
287             case 1001:
288                 return ENDPOINT_UNAVAILABLE;
289             case 1002:
290                 return PROTOCOL_ERROR;
291             case 1003:
292                 return INVALID_MESSAGE_TYPE;
293             case 1007:
294                 return INVALID_PAYLOAD_DATA;
295             case 1008:
296                 return POLICY_VIOLATION;
297             case 1009:
298                 return MESSAGE_TOO_BIG;
299             case 1010:
300                 return MANDATORY_EXTENSION;
301             case 1011:
302                 return INTERNAL_SERVER_ERROR;
303             case 1012:
304                 return SERVICE_RESTART;
305             case 1013:
306                 return TRY_AGAIN_LATER;
307             case 1014:
308                 return BAD_GATEWAY;
309             default:
310                 return new WebSocketCloseStatus(code, "Close status #" + code);
311         }
312     }
313 
314 }