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.events;
20  
21  import java.io.IOException;
22  import java.nio.ByteBuffer;
23  
24  import org.eclipse.jetty.util.BufferUtil;
25  import org.eclipse.jetty.util.StringUtil;
26  import org.eclipse.jetty.util.Utf8Appendable.NotUtf8Exception;
27  import org.eclipse.jetty.util.log.Log;
28  import org.eclipse.jetty.util.log.Logger;
29  import org.eclipse.jetty.websocket.api.CloseException;
30  import org.eclipse.jetty.websocket.api.StatusCode;
31  import org.eclipse.jetty.websocket.api.WebSocketException;
32  import org.eclipse.jetty.websocket.api.WebSocketPolicy;
33  import org.eclipse.jetty.websocket.api.extensions.Frame;
34  import org.eclipse.jetty.websocket.api.extensions.IncomingFrames;
35  import org.eclipse.jetty.websocket.common.CloseInfo;
36  import org.eclipse.jetty.websocket.common.OpCode;
37  import org.eclipse.jetty.websocket.common.WebSocketFrame;
38  import org.eclipse.jetty.websocket.common.WebSocketSession;
39  
40  /**
41   * EventDriver is the main interface between the User's WebSocket POJO and the internal jetty implementation of WebSocket.
42   */
43  public abstract class EventDriver implements IncomingFrames
44  {
45      private static final Logger LOG = Log.getLogger(EventDriver.class);
46      protected final WebSocketPolicy policy;
47      protected final Object websocket;
48      protected WebSocketSession session;
49  
50      public EventDriver(WebSocketPolicy policy, Object websocket)
51      {
52          this.policy = policy;
53          this.websocket = websocket;
54      }
55  
56      public WebSocketPolicy getPolicy()
57      {
58          return policy;
59      }
60  
61      public WebSocketSession getSession()
62      {
63          return session;
64      }
65  
66      @Override
67      public final void incomingError(WebSocketException e)
68      {
69          if (LOG.isDebugEnabled())
70          {
71              LOG.debug("incoming(WebSocketException)",e);
72          }
73  
74          if (e instanceof CloseException)
75          {
76              CloseException close = (CloseException)e;
77              terminateConnection(close.getStatusCode(),close.getMessage());
78          }
79  
80          onError(e);
81      }
82  
83      @Override
84      public void incomingFrame(Frame frame)
85      {
86          if (LOG.isDebugEnabled())
87          {
88              LOG.debug("{}.onFrame({})",websocket.getClass().getSimpleName(),frame);
89          }
90  
91          onFrame(frame);
92  
93          try
94          {
95              switch (frame.getType().getOpCode())
96              {
97                  case OpCode.CLOSE:
98                  {
99                      boolean validate = true;
100                     CloseInfo close = new CloseInfo(frame,validate);
101 
102                     // notify user websocket pojo
103                     onClose(close);
104 
105                     // process handshake
106                     session.getConnection().getIOState().onCloseRemote(close);
107 
108                     return;
109                 }
110                 case OpCode.PING:
111                 {
112                     byte pongBuf[] = new byte[0];
113                     if (frame.hasPayload())
114                     {
115                         pongBuf = BufferUtil.toArray(frame.getPayload());
116                     }
117                     session.getRemote().sendPong(ByteBuffer.wrap(pongBuf));
118                     break;
119                 }
120                 case OpCode.BINARY:
121                 {
122                     onBinaryFrame(frame.getPayload(),frame.isFin());
123                     return;
124                 }
125                 case OpCode.TEXT:
126                 {
127                     onTextFrame(frame.getPayload(),frame.isFin());
128                     return;
129                 }
130             }
131         }
132         catch (NotUtf8Exception e)
133         {
134             terminateConnection(StatusCode.BAD_PAYLOAD,e.getMessage());
135         }
136         catch (CloseException e)
137         {
138             terminateConnection(e.getStatusCode(),e.getMessage());
139         }
140         catch (Throwable t)
141         {
142             unhandled(t);
143         }
144     }
145 
146     public abstract void onBinaryFrame(ByteBuffer buffer, boolean fin) throws IOException;
147 
148     public abstract void onBinaryMessage(byte[] data);
149 
150     public abstract void onClose(CloseInfo close);
151 
152     public abstract void onConnect();
153 
154     public abstract void onError(Throwable t);
155 
156     public abstract void onFrame(Frame frame);
157 
158     public abstract void onTextFrame(ByteBuffer buffer, boolean fin) throws IOException;
159 
160     public abstract void onTextMessage(String message);
161 
162     public void openSession(WebSocketSession session)
163     {
164         LOG.debug("openSession({})",session);
165         this.session = session;
166         try
167         {
168             this.onConnect();
169         }
170         catch (Throwable t)
171         {
172             unhandled(t);
173         }
174     }
175 
176     protected void terminateConnection(int statusCode, String rawreason)
177     {
178         String reason = rawreason;
179         reason = StringUtil.truncate(reason,(WebSocketFrame.MAX_CONTROL_PAYLOAD - 2));
180         LOG.debug("terminateConnection({},{})",statusCode,rawreason);
181         session.close(statusCode,reason);
182     }
183 
184     private void unhandled(Throwable t)
185     {
186         LOG.warn("Unhandled Error (closing connection)",t);
187         onError(t);
188 
189         // Unhandled Error, close the connection.
190         switch (policy.getBehavior())
191         {
192             case SERVER:
193                 terminateConnection(StatusCode.SERVER_ERROR,t.getClass().getSimpleName());
194                 break;
195             case CLIENT:
196                 terminateConnection(StatusCode.POLICY_VIOLATION,t.getClass().getSimpleName());
197                 break;
198         }
199     }
200 }