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 io.netty.buffer.ByteBuf;
19 import io.netty.buffer.ByteBufAllocator;
20 import io.netty.buffer.Unpooled;
21 import io.netty.buffer.UnpooledByteBufAllocator;
22 import io.netty.util.CharsetUtil;
23 import io.netty.util.internal.StringUtil;
24
25 /**
26 * Web Socket Frame for closing the connection.
27 */
28 public class CloseWebSocketFrame extends WebSocketFrame {
29
30 /**
31 * Creates a new empty close frame.
32 */
33 public CloseWebSocketFrame() {
34 super(Unpooled.buffer(0));
35 }
36
37 /**
38 * Creates a new empty close frame with closing status code and reason text
39 *
40 * @param status
41 * Status code as per <a href="https://tools.ietf.org/html/rfc6455#section-7.4">RFC 6455</a>. For
42 * example, <tt>1000</tt> indicates normal closure.
43 */
44 public CloseWebSocketFrame(WebSocketCloseStatus status) {
45 this(requireValidStatusCode(status.code()), status.reasonText());
46 }
47
48 /**
49 * Creates a new empty close frame with closing status code and reason text, using the provided ByteBuf allocator.
50 *
51 * @param status
52 * Status code as per <a href="https://tools.ietf.org/html/rfc6455#section-7.4">RFC 6455</a>. For
53 * example, <tt>1000</tt> indicates normal closure.
54 */
55 public CloseWebSocketFrame(WebSocketCloseStatus status, ByteBufAllocator allocator) {
56 this(true, 0, allocator, requireValidStatusCode(status.code()), status.reasonText());
57 }
58
59 /**
60 * Creates a new empty close frame with closing status code and reason text
61 *
62 * @param status
63 * Status code as per <a href="https://tools.ietf.org/html/rfc6455#section-7.4">RFC 6455</a>. For
64 * example, <tt>1000</tt> indicates normal closure.
65 * @param reasonText
66 * Reason text. Set to null if no text.
67 */
68 public CloseWebSocketFrame(WebSocketCloseStatus status, String reasonText) {
69 this(requireValidStatusCode(status.code()), reasonText);
70 }
71
72 /**
73 * Creates a new empty close frame with closing status code and reason text
74 *
75 * @param statusCode
76 * Integer status code as per <a href="https://tools.ietf.org/html/rfc6455#section-7.4">RFC 6455</a>. For
77 * example, <tt>1000</tt> indicates normal closure.
78 * @param reasonText
79 * Reason text. Set to null if no text.
80 */
81 public CloseWebSocketFrame(int statusCode, String reasonText) {
82 this(true, 0, requireValidStatusCode(statusCode), reasonText);
83 }
84
85 /**
86 * Creates a new close frame with no losing status code and no reason text
87 *
88 * @param finalFragment
89 * flag indicating if this frame is the final fragment
90 * @param rsv
91 * reserved bits used for protocol extensions.
92 */
93 public CloseWebSocketFrame(boolean finalFragment, int rsv) {
94 this(finalFragment, rsv, Unpooled.buffer(0));
95 }
96
97 /**
98 * Creates a new close frame with closing status code and reason text
99 *
100 * @param finalFragment
101 * flag indicating if this frame is the final fragment
102 * @param rsv
103 * reserved bits used for protocol extensions
104 * @param statusCode
105 * Integer status code as per <a href="https://tools.ietf.org/html/rfc6455#section-7.4">RFC 6455</a>. For
106 * example, <tt>1000</tt> indicates normal closure.
107 * @param reasonText
108 * Reason text. Set to null if no text.
109 */
110 public CloseWebSocketFrame(boolean finalFragment, int rsv, int statusCode, String reasonText) {
111 this(finalFragment, rsv, UnpooledByteBufAllocator.DEFAULT, statusCode, reasonText);
112 }
113
114 /**
115 * Creates a new close frame with closing status code and reason text
116 *
117 * @param finalFragment
118 * flag indicating if this frame is the final fragment
119 * @param rsv
120 * reserved bits used for protocol extensions
121 * @param allocator
122 * the ByteBuf allocator to use to build the binary data.
123 * @param statusCode
124 * Integer status code as per <a href="https://tools.ietf.org/html/rfc6455#section-7.4">RFC 6455</a>. For
125 * example, <tt>1000</tt> indicates normal closure.
126 * @param reasonText
127 * Reason text. Set to null if no text.
128 */
129 public CloseWebSocketFrame(boolean finalFragment, int rsv, ByteBufAllocator allocator,
130 int statusCode, String reasonText) {
131 super(finalFragment, rsv, newBinaryData(allocator, requireValidStatusCode(statusCode), reasonText));
132 }
133
134 private static ByteBuf newBinaryData(ByteBufAllocator allocator, int statusCode, String reasonText) {
135 if (reasonText == null) {
136 reasonText = StringUtil.EMPTY_STRING;
137 }
138
139 ByteBuf binaryData = allocator.buffer(2 + reasonText.length());
140 binaryData.writeShort(statusCode);
141 if (!reasonText.isEmpty()) {
142 binaryData.writeCharSequence(reasonText, CharsetUtil.UTF_8);
143 }
144 return binaryData;
145 }
146
147 /**
148 * Creates a new close frame
149 *
150 * @param finalFragment
151 * flag indicating if this frame is the final fragment
152 * @param rsv
153 * reserved bits used for protocol extensions
154 * @param binaryData
155 * the content of the frame. Must be 2 byte integer followed by optional UTF-8 encoded string.
156 */
157 public CloseWebSocketFrame(boolean finalFragment, int rsv, ByteBuf binaryData) {
158 super(finalFragment, rsv, binaryData);
159 }
160
161 /**
162 * Returns the closing status code as per <a href="https://tools.ietf.org/html/rfc6455#section-7.4">RFC 6455</a>. If
163 * a status code is set, -1 is returned.
164 */
165 public int statusCode() {
166 ByteBuf binaryData = content();
167 if (binaryData == null || binaryData.readableBytes() < 2) {
168 return -1;
169 }
170
171 return binaryData.getUnsignedShort(binaryData.readerIndex());
172 }
173
174 /**
175 * Returns the reason text as per <a href="https://tools.ietf.org/html/rfc6455#section-7.4">RFC 6455</a> If a reason
176 * text is not supplied, an empty string is returned.
177 */
178 public String reasonText() {
179 ByteBuf binaryData = content();
180 if (binaryData == null || binaryData.readableBytes() <= 2) {
181 return "";
182 }
183
184 return binaryData.toString(binaryData.readerIndex() + 2, binaryData.readableBytes() - 2, CharsetUtil.UTF_8);
185 }
186
187 @Override
188 public CloseWebSocketFrame copy() {
189 return (CloseWebSocketFrame) super.copy();
190 }
191
192 @Override
193 public CloseWebSocketFrame duplicate() {
194 return (CloseWebSocketFrame) super.duplicate();
195 }
196
197 @Override
198 public CloseWebSocketFrame retainedDuplicate() {
199 return (CloseWebSocketFrame) super.retainedDuplicate();
200 }
201
202 @Override
203 public CloseWebSocketFrame replace(ByteBuf content) {
204 return new CloseWebSocketFrame(isFinalFragment(), rsv(), content);
205 }
206
207 @Override
208 public CloseWebSocketFrame retain() {
209 super.retain();
210 return this;
211 }
212
213 @Override
214 public CloseWebSocketFrame retain(int increment) {
215 super.retain(increment);
216 return this;
217 }
218
219 @Override
220 public CloseWebSocketFrame touch() {
221 super.touch();
222 return this;
223 }
224
225 @Override
226 public CloseWebSocketFrame touch(Object hint) {
227 super.touch(hint);
228 return this;
229 }
230
231 static int requireValidStatusCode(int statusCode) {
232 if (WebSocketCloseStatus.isValidStatusCode(statusCode)) {
233 return statusCode;
234 } else {
235 throw new IllegalArgumentException("WebSocket close status code does NOT comply with RFC-6455: " +
236 statusCode);
237 }
238 }
239 }