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