1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.eclipse.jetty.websocket.common;
20
21 import java.nio.ByteBuffer;
22
23 import org.eclipse.jetty.util.StringUtil;
24 import org.eclipse.jetty.util.Utf8Appendable.NotUtf8Exception;
25 import org.eclipse.jetty.util.Utf8StringBuilder;
26 import org.eclipse.jetty.util.log.Log;
27 import org.eclipse.jetty.util.log.Logger;
28 import org.eclipse.jetty.websocket.api.BadPayloadException;
29 import org.eclipse.jetty.websocket.api.ProtocolException;
30 import org.eclipse.jetty.websocket.api.StatusCode;
31 import org.eclipse.jetty.websocket.api.extensions.Frame;
32
33 public class CloseInfo
34 {
35 private static final Logger LOG = Log.getLogger(CloseInfo.class);
36 private int statusCode;
37 private String reason;
38
39 public CloseInfo()
40 {
41 this(StatusCode.NO_CODE,null);
42 }
43
44 public CloseInfo(ByteBuffer payload, boolean validate)
45 {
46 this.statusCode = StatusCode.NO_CODE;
47 this.reason = null;
48
49 if ((payload == null) || (payload.remaining() == 0))
50 {
51 return;
52 }
53
54 ByteBuffer data = payload.slice();
55 if ((data.remaining() == 1) && (validate))
56 {
57 throw new ProtocolException("Invalid 1 byte payload");
58 }
59
60 if (data.remaining() >= 2)
61 {
62
63 statusCode = 0;
64 statusCode |= (data.get() & 0xFF) << 8;
65 statusCode |= (data.get() & 0xFF);
66
67 if(validate) {
68 if ((statusCode < StatusCode.NORMAL) || (statusCode == StatusCode.UNDEFINED) || (statusCode == StatusCode.NO_CLOSE)
69 || (statusCode == StatusCode.NO_CODE) || ((statusCode > 1011) && (statusCode <= 2999)) || (statusCode >= 5000))
70 {
71 throw new ProtocolException("Invalid close code: " + statusCode);
72 }
73 }
74
75 if (data.remaining() > 0)
76 {
77
78 try
79 {
80 Utf8StringBuilder utf = new Utf8StringBuilder();
81 utf.append(data);
82 reason = utf.toString();
83 }
84 catch (NotUtf8Exception e)
85 {
86 if (validate)
87 {
88 throw new BadPayloadException("Invalid Close Reason",e);
89 }
90 else
91 {
92 LOG.warn(e);
93 }
94 }
95 catch (RuntimeException e)
96 {
97 if (validate)
98 {
99 throw new ProtocolException("Invalid Close Reason",e);
100 }
101 else
102 {
103 LOG.warn(e);
104 }
105 }
106 }
107 }
108 }
109
110 public CloseInfo(Frame frame)
111 {
112 this(frame.getPayload(),false);
113 }
114
115 public CloseInfo(Frame frame, boolean validate)
116 {
117 this(frame.getPayload(),validate);
118 }
119
120 public CloseInfo(int statusCode)
121 {
122 this(statusCode, null);
123 }
124
125 public CloseInfo(int statusCode, String reason)
126 {
127 this.statusCode = statusCode;
128 this.reason = reason;
129 }
130
131 private byte[] asByteBuffer()
132 {
133 if ((statusCode == StatusCode.NO_CLOSE) || (statusCode == StatusCode.NO_CODE) || (statusCode == (-1)))
134 {
135
136 return null;
137 }
138
139 int len = 2;
140 byte utf[] = null;
141 if (StringUtil.isNotBlank(reason))
142 {
143 utf = StringUtil.getUtf8Bytes(reason);
144 len += utf.length;
145 }
146
147 byte buf[] = new byte[len];
148 buf[0] = (byte)((statusCode >>> 8) & 0xFF);
149 buf[1] = (byte)((statusCode >>> 0) & 0xFF);
150
151 if (utf != null)
152 {
153 System.arraycopy(utf,0,buf,2,utf.length);
154 }
155
156 return buf;
157 }
158
159 public WebSocketFrame asFrame()
160 {
161 WebSocketFrame frame = new WebSocketFrame(OpCode.CLOSE);
162 frame.setFin(true);
163 frame.setPayload(asByteBuffer());
164 return frame;
165 }
166
167 public String getReason()
168 {
169 return reason;
170 }
171
172 public int getStatusCode()
173 {
174 return statusCode;
175 }
176
177 public boolean isHarsh()
178 {
179 return !((statusCode == StatusCode.NORMAL) || (statusCode == StatusCode.NO_CODE));
180 }
181
182 @Override
183 public String toString()
184 {
185 return String.format("CloseInfo[code=%d,reason=%s]",statusCode,reason);
186 }
187 }