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