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                     if (session.getConnection().getIOState().onCloseHandshake(true))
107                     {
108                         // handshake resolved, disconnect.
109                         session.getConnection().disconnect();
110                     }
111                     else
112                     {
113                         // respond
114                         session.close(close.getStatusCode(),close.getReason());
115                     }
116 
117                     return;
118                 }
119                 case OpCode.PING:
120                 {
121                     byte pongBuf[] = new byte[0];
122                     if (frame.hasPayload())
123                     {
124                         pongBuf = BufferUtil.toArray(frame.getPayload());
125                     }
126                     session.getRemote().sendPong(ByteBuffer.wrap(pongBuf));
127                     break;
128                 }
129                 case OpCode.BINARY:
130                 {
131                     onBinaryFrame(frame.getPayload(),frame.isFin());
132                     return;
133                 }
134                 case OpCode.TEXT:
135                 {
136                     onTextFrame(frame.getPayload(),frame.isFin());
137                     return;
138                 }
139             }
140         }
141         catch (NotUtf8Exception e)
142         {
143             terminateConnection(StatusCode.BAD_PAYLOAD,e.getMessage());
144         }
145         catch (CloseException e)
146         {
147             terminateConnection(e.getStatusCode(),e.getMessage());
148         }
149         catch (Throwable t)
150         {
151             unhandled(t);
152         }
153     }
154 
155     public abstract void onBinaryFrame(ByteBuffer buffer, boolean fin) throws IOException;
156 
157     public abstract void onBinaryMessage(byte[] data);
158 
159     public abstract void onClose(CloseInfo close);
160 
161     public abstract void onConnect();
162 
163     public abstract void onError(Throwable t);
164 
165     public abstract void onFrame(Frame frame);
166 
167     public abstract void onTextFrame(ByteBuffer buffer, boolean fin) throws IOException;
168 
169     public abstract void onTextMessage(String message);
170 
171     public void openSession(WebSocketSession session)
172     {
173         LOG.debug("openSession({})",session);
174         this.session = session;
175         this.onConnect();
176     }
177 
178     protected void terminateConnection(int statusCode, String rawreason)
179     {
180         String reason = rawreason;
181         reason = StringUtil.truncate(reason,(WebSocketFrame.MAX_CONTROL_PAYLOAD - 2));
182         LOG.debug("terminateConnection({},{})",statusCode,rawreason);
183         session.close(statusCode,reason);
184     }
185 
186     private void unhandled(Throwable t)
187     {
188         LOG.warn("Unhandled Error (closing connection)",t);
189 
190         // Unhandled Error, close the connection.
191         switch (policy.getBehavior())
192         {
193             case SERVER:
194                 terminateConnection(StatusCode.SERVER_ERROR,t.getClass().getSimpleName());
195                 break;
196             case CLIENT:
197                 terminateConnection(StatusCode.POLICY_VIOLATION,t.getClass().getSimpleName());
198                 break;
199         }
200     }
201 }