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