View Javadoc

1   //
2   //  ========================================================================
3   //  Copyright (c) 1995-2013 Mort Bay Consulting Pty. Ltd.
4   //  ------------------------------------------------------------------------
5   //  All rights reserved. This program and the accompanying materials
6   //  are made available under the terms of the Eclipse Public License v1.0
7   //  and Apache License v2.0 which accompanies this distribution.
8   //
9   //      The Eclipse Public License is available at
10  //      http://www.eclipse.org/legal/epl-v10.html
11  //
12  //      The Apache License v2.0 is available at
13  //      http://www.opensource.org/licenses/apache2.0.php
14  //
15  //  You may elect to redistribute this code under either of these licenses.
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; // nothing to do
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              // Status Code
64              statusCode = 0; // start with 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                  // Reason
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             // codes that are not allowed to be used in endpoint.
137             return null;
138         }
139 
140         int len = 2; // status code
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 }