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  
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; // nothing to do
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              // Status Code
63              statusCode = 0; // start with 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                  // Reason
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             // codes that are not allowed to be used in endpoint.
136             return null;
137         }
138 
139         int len = 2; // status code
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 }