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.extensions.mux;
20  
21  import java.io.IOException;
22  import java.net.InetSocketAddress;
23  import java.net.URI;
24  import java.nio.ByteBuffer;
25  import java.util.concurrent.Future;
26  import java.util.concurrent.atomic.AtomicBoolean;
27  
28  import org.eclipse.jetty.util.BufferUtil;
29  import org.eclipse.jetty.util.log.Log;
30  import org.eclipse.jetty.util.log.Logger;
31  import org.eclipse.jetty.websocket.api.StatusCode;
32  import org.eclipse.jetty.websocket.api.SuspendToken;
33  import org.eclipse.jetty.websocket.api.WebSocketConnection;
34  import org.eclipse.jetty.websocket.api.WebSocketException;
35  import org.eclipse.jetty.websocket.api.WebSocketPolicy;
36  import org.eclipse.jetty.websocket.api.WriteCallback;
37  import org.eclipse.jetty.websocket.api.extensions.Frame;
38  import org.eclipse.jetty.websocket.api.extensions.IncomingFrames;
39  import org.eclipse.jetty.websocket.common.CloseInfo;
40  import org.eclipse.jetty.websocket.common.ConnectionState;
41  import org.eclipse.jetty.websocket.common.LogicalConnection;
42  import org.eclipse.jetty.websocket.common.WebSocketFrame;
43  import org.eclipse.jetty.websocket.common.WebSocketSession;
44  import org.eclipse.jetty.websocket.common.io.FutureWriteCallback;
45  import org.eclipse.jetty.websocket.common.io.IOState;
46  
47  /**
48   * MuxChannel, acts as WebSocketConnection for specific sub-channel.
49   */
50  public class MuxChannel implements WebSocketConnection, LogicalConnection, IncomingFrames, SuspendToken
51  {
52      private static final Logger LOG = Log.getLogger(MuxChannel.class);
53  
54      private final long channelId;
55      private final Muxer muxer;
56      private final AtomicBoolean inputClosed;
57      private final AtomicBoolean outputClosed;
58      private final AtomicBoolean suspendToken;
59      private IOState ioState;
60      private WebSocketPolicy policy;
61      private WebSocketSession session;
62      private IncomingFrames incoming;
63      private String subProtocol;
64  
65      public MuxChannel(long channelId, Muxer muxer)
66      {
67          this.channelId = channelId;
68          this.muxer = muxer;
69          this.policy = muxer.getPolicy().clonePolicy();
70  
71          this.suspendToken = new AtomicBoolean(false);
72          this.ioState = new IOState();
73          ioState.setState(ConnectionState.CONNECTING);
74  
75          this.inputClosed = new AtomicBoolean(false);
76          this.outputClosed = new AtomicBoolean(false);
77      }
78  
79      @Override
80      public void close()
81      {
82          close(StatusCode.NORMAL,null);
83      }
84  
85      @Override
86      public void close(int statusCode, String reason)
87      {
88          CloseInfo close = new CloseInfo(statusCode,reason);
89          // TODO: disconnect callback?
90          outgoingFrame(close.asFrame(),null);
91      }
92  
93      @Override
94      public void disconnect()
95      {
96          this.ioState.setState(ConnectionState.CLOSED);
97          // TODO: disconnect the virtual end-point?
98      }
99  
100     public long getChannelId()
101     {
102         return channelId;
103     }
104 
105     @Override
106     public IOState getIOState()
107     {
108         // TODO Auto-generated method stub
109         return null;
110     }
111 
112     @Override
113     public InetSocketAddress getLocalAddress()
114     {
115         // TODO Auto-generated method stub
116         return null;
117     }
118 
119     @Override
120     public WebSocketPolicy getPolicy()
121     {
122         return policy;
123     }
124 
125     @Override
126     public InetSocketAddress getRemoteAddress()
127     {
128         return muxer.getRemoteAddress();
129     }
130 
131     @Override
132     public URI getRequestURI()
133     {
134         // TODO Auto-generated method stub
135         return null;
136     }
137 
138     @Override
139     public WebSocketSession getSession()
140     {
141         return session;
142     }
143 
144     @Override
145     public String getSubProtocol()
146     {
147         return this.subProtocol;
148     }
149 
150     /**
151      * Incoming exceptions from Muxer.
152      */
153     @Override
154     public void incomingError(WebSocketException e)
155     {
156         incoming.incomingError(e);
157     }
158 
159     /**
160      * Incoming frames from Muxer
161      */
162     @Override
163     public void incomingFrame(Frame frame)
164     {
165         incoming.incomingFrame(frame);
166     }
167 
168     public boolean isActive()
169     {
170         return (ioState.isOpen());
171     }
172 
173     @Override
174     public boolean isOpen()
175     {
176         return isActive() && muxer.isOpen();
177     }
178 
179     @Override
180     public boolean isReading()
181     {
182         return true;
183     }
184 
185     public void onClose()
186     {
187         this.ioState.setState(ConnectionState.CLOSED);
188     }
189 
190     public void onOpen()
191     {
192         this.ioState.setState(ConnectionState.OPEN);
193     }
194 
195     /**
196      * Internal
197      * 
198      * @param frame the frame to write
199      * @return the future for the network write of the frame
200      */
201     private Future<Void> outgoingAsyncFrame(WebSocketFrame frame)
202     {
203         FutureWriteCallback future = new FutureWriteCallback();
204         outgoingFrame(frame,future);
205         return future;
206     }
207 
208     /**
209      * Frames destined for the Muxer
210      */
211     @Override
212     public void outgoingFrame(Frame frame, WriteCallback callback)
213     {
214         muxer.output(channelId,frame,callback);
215     }
216 
217     /**
218      * Ping frame destined for the Muxer
219      */
220     @Override
221     public void ping(ByteBuffer buf) throws IOException
222     {
223         outgoingFrame(WebSocketFrame.ping().setPayload(buf),null);
224     }
225 
226     @Override
227     public void resume()
228     {
229         if (suspendToken.getAndSet(false))
230         {
231             // TODO: Start reading again. (how?)
232         }
233     }
234 
235     @Override
236     public void setNextIncomingFrames(IncomingFrames incoming)
237     {
238         this.incoming = incoming;
239     }
240 
241     @Override
242     public void setSession(WebSocketSession session)
243     {
244         this.session = session;
245         // session.setOutgoing(this);
246     }
247 
248     public void setSubProtocol(String subProtocol)
249     {
250         this.subProtocol = subProtocol;
251     }
252 
253     @Override
254     public SuspendToken suspend()
255     {
256         suspendToken.set(true);
257         // TODO: how to suspend reading?
258         return this;
259     }
260 
261     /**
262      * Generate a binary message, destined for Muxer
263      */
264     @Override
265     public Future<Void> write(byte[] buf, int offset, int len)
266     {
267         ByteBuffer bb = ByteBuffer.wrap(buf,offset,len);
268         return write(bb);
269     }
270 
271     /**
272      * Generate a binary message, destined for Muxer
273      */
274     @Override
275     public Future<Void> write(ByteBuffer buffer)
276     {
277         if (LOG.isDebugEnabled())
278         {
279             LOG.debug("write with {}",BufferUtil.toDetailString(buffer));
280         }
281         WebSocketFrame frame = WebSocketFrame.binary().setPayload(buffer);
282         return outgoingAsyncFrame(frame);
283     }
284 
285     /**
286      * Generate a text message, destined for Muxer
287      */
288     @Override
289     public Future<Void> write(String message)
290     {
291         return outgoingAsyncFrame(WebSocketFrame.text(message));
292     }
293 }