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.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 }