001/*
002 * Copyright 2002-2017 the original author or authors.
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 *      https://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 */
016
017package org.springframework.web.socket;
018
019import org.springframework.lang.Nullable;
020import org.springframework.util.Assert;
021import org.springframework.util.ObjectUtils;
022
023/**
024 * Represents a WebSocket close status code and reason. Status codes in the 1xxx range are
025 * pre-defined by the protocol. Optionally, a status code may be sent with a reason.
026 *
027 * <p>See <a href="https://tools.ietf.org/html/rfc6455#section-7.4.1">RFC 6455, Section 7.4.1
028 * "Defined Status Codes"</a>.
029 *
030 * @author Rossen Stoyanchev
031 * @since 4.0
032 */
033public final class CloseStatus {
034
035        /**
036         * "1000 indicates a normal closure, meaning that the purpose for which the connection
037         * was established has been fulfilled."
038         */
039        public static final CloseStatus NORMAL = new CloseStatus(1000);
040
041        /**
042         * "1001 indicates that an endpoint is "going away", such as a server going down or a
043         * browser having navigated away from a page."
044         */
045        public static final CloseStatus GOING_AWAY = new CloseStatus(1001);
046
047        /**
048         * "1002 indicates that an endpoint is terminating the connection due to a protocol
049         * error."
050         */
051        public static final CloseStatus PROTOCOL_ERROR  = new CloseStatus(1002);
052
053        /**
054         * "1003 indicates that an endpoint is terminating the connection because it has
055         * received a type of data it cannot accept (e.g., an endpoint that understands only
056         * text data MAY send this if it receives a binary message)."
057         */
058        public static final CloseStatus NOT_ACCEPTABLE = new CloseStatus(1003);
059
060        // 10004: Reserved.
061        // The specific meaning might be defined in the future.
062
063        /**
064         * "1005 is a reserved value and MUST NOT be set as a status code in a Close control
065         * frame by an endpoint. It is designated for use in applications expecting a status
066         * code to indicate that no status code was actually present."
067         */
068        public static final CloseStatus NO_STATUS_CODE = new CloseStatus(1005);
069
070        /**
071         * "1006 is a reserved value and MUST NOT be set as a status code in a Close control
072         * frame by an endpoint. It is designated for use in applications expecting a status
073         * code to indicate that the connection was closed abnormally, e.g., without sending
074         * or receiving a Close control frame."
075         */
076        public static final CloseStatus NO_CLOSE_FRAME = new CloseStatus(1006);
077
078        /**
079         * "1007 indicates that an endpoint is terminating the connection because it has
080         * received data within a message that was not consistent with the type of the message
081         * (e.g., non-UTF-8 [RFC3629] data within a text message)."
082         */
083        public static final CloseStatus BAD_DATA = new CloseStatus(1007);
084
085        /**
086         * "1008 indicates that an endpoint is terminating the connection because it has
087         * received a message that violates its policy. This is a generic status code that can
088         * be returned when there is no other more suitable status code (e.g., 1003 or 1009)
089         * or if there is a need to hide specific details about the policy."
090         */
091        public static final CloseStatus POLICY_VIOLATION = new CloseStatus(1008);
092
093        /**
094         * "1009 indicates that an endpoint is terminating the connection because it has
095         * received a message that is too big for it to process."
096         */
097        public static final CloseStatus TOO_BIG_TO_PROCESS = new CloseStatus(1009);
098
099        /**
100         * "1010 indicates that an endpoint (client) is terminating the connection because it
101         * has expected the server to negotiate one or more extension, but the server didn't
102         * return them in the response message of the WebSocket handshake. The list of
103         * extensions that are needed SHOULD appear in the /reason/ part of the Close frame.
104         * Note that this status code is not used by the server, because it can fail the
105         * WebSocket handshake instead."
106         */
107        public static final CloseStatus REQUIRED_EXTENSION = new CloseStatus(1010);
108
109        /**
110         * "1011 indicates that a server is terminating the connection because it encountered
111         * an unexpected condition that prevented it from fulfilling the request."
112         */
113        public static final CloseStatus SERVER_ERROR = new CloseStatus(1011);
114
115        /**
116         * "1012 indicates that the service is restarted. A client may reconnect, and if it
117         * chooses to do, should reconnect using a randomized delay of 5 - 30s."
118         */
119        public static final CloseStatus SERVICE_RESTARTED = new CloseStatus(1012);
120
121        /**
122         * "1013 indicates that the service is experiencing overload. A client should only
123         * connect to a different IP (when there are multiple for the target) or reconnect to
124         * the same IP upon user action."
125         */
126        public static final CloseStatus SERVICE_OVERLOAD = new CloseStatus(1013);
127
128        /**
129         * "1015 is a reserved value and MUST NOT be set as a status code in a Close control
130         * frame by an endpoint. It is designated for use in applications expecting a status
131         * code to indicate that the connection was closed due to a failure to perform a TLS
132         * handshake (e.g., the server certificate can't be verified)."
133         */
134        public static final CloseStatus TLS_HANDSHAKE_FAILURE = new CloseStatus(1015);
135
136        /**
137         * A status code for use within the framework the indicate a session has
138         * become unreliable (e.g. timed out while sending a message) and extra
139         * care should be exercised, e.g. avoid sending any further data to the
140         * client that may be done during normal shutdown.
141         * @since 4.0.3
142         */
143        public static final CloseStatus SESSION_NOT_RELIABLE = new CloseStatus(4500);
144
145
146        private final int code;
147
148        @Nullable
149        private final String reason;
150
151
152        /**
153         * Create a new {@link CloseStatus} instance.
154         * @param code the status code
155         */
156        public CloseStatus(int code) {
157                this(code, null);
158        }
159
160        /**
161         * Create a new {@link CloseStatus} instance.
162         * @param code the status code
163         * @param reason the reason
164         */
165        public CloseStatus(int code, @Nullable String reason) {
166                Assert.isTrue((code >= 1000 && code < 5000), "Invalid status code");
167                this.code = code;
168                this.reason = reason;
169        }
170
171
172        /**
173         * Return the status code.
174         */
175        public int getCode() {
176                return this.code;
177        }
178
179        /**
180         * Return the reason, or {@code null} if none.
181         */
182        @Nullable
183        public String getReason() {
184                return this.reason;
185        }
186
187        /**
188         * Create a new {@link CloseStatus} from this one with the specified reason.
189         * @param reason the reason
190         * @return a new {@link CloseStatus} instance
191         */
192        public CloseStatus withReason(String reason) {
193                Assert.hasText(reason, "Reason must not be empty");
194                return new CloseStatus(this.code, reason);
195        }
196
197
198        public boolean equalsCode(CloseStatus other) {
199                return (this.code == other.code);
200        }
201
202        @Override
203        public boolean equals(@Nullable Object other) {
204                if (this == other) {
205                        return true;
206                }
207                if (!(other instanceof CloseStatus)) {
208                        return false;
209                }
210                CloseStatus otherStatus = (CloseStatus) other;
211                return (this.code == otherStatus.code && ObjectUtils.nullSafeEquals(this.reason, otherStatus.reason));
212        }
213
214        @Override
215        public int hashCode() {
216                return this.code * 29 + ObjectUtils.nullSafeHashCode(this.reason);
217        }
218
219        @Override
220        public String toString() {
221                return "CloseStatus[code=" + this.code + ", reason=" + this.reason + "]";
222        }
223
224}