From a7fb7f5acc054b51635a6470e4679d9648aa481c Mon Sep 17 00:00:00 2001 From: Eli Hamburger Date: Thu, 17 Dec 2015 13:14:34 -0500 Subject: [PATCH 1/8] * Fixing a state bug that results in NPE's and thread death. The connection needs to be set to null on each iteration to avoid the wrong connection being closed when a new connection fails. --- src/main/java/org/java_websocket/server/WebSocketServer.java | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main/java/org/java_websocket/server/WebSocketServer.java b/src/main/java/org/java_websocket/server/WebSocketServer.java index 12cea2f1d..6404d84eb 100644 --- a/src/main/java/org/java_websocket/server/WebSocketServer.java +++ b/src/main/java/org/java_websocket/server/WebSocketServer.java @@ -292,6 +292,7 @@ public void run() { Iterator i = keys.iterator(); while ( i.hasNext() ) { + conn = null; //Make sure to reset the connection on each iteration to avoid state bugs. key = i.next(); if( !key.isValid() ) { From 76a1a3f7ed586db5ceb149c2197d50377d949fab Mon Sep 17 00:00:00 2001 From: bendem Date: Sat, 8 Aug 2015 11:23:28 +0200 Subject: [PATCH 2/8] Don't shutdown the ssl executor service too early The ExecutorService held by the DefaultSSLWebSocketServerFactory was being shutdown when the first SSLSocketChannel2 got closed, making it unusable for further connections. It's now being closed on server stop instead. --- src/main/java/org/java_websocket/SSLSocketChannel2.java | 1 - .../server/DefaultSSLWebSocketServerFactory.java | 4 ++++ .../java_websocket/server/DefaultWebSocketServerFactory.java | 3 +++ src/main/java/org/java_websocket/server/WebSocketServer.java | 4 ++++ 4 files changed, 11 insertions(+), 1 deletion(-) diff --git a/src/main/java/org/java_websocket/SSLSocketChannel2.java b/src/main/java/org/java_websocket/SSLSocketChannel2.java index e540612bb..404e2c016 100644 --- a/src/main/java/org/java_websocket/SSLSocketChannel2.java +++ b/src/main/java/org/java_websocket/SSLSocketChannel2.java @@ -308,7 +308,6 @@ public void close() throws IOException { if( socketChannel.isOpen() ) socketChannel.write( wrap( emptybuffer ) );// FIXME what if not all bytes can be written socketChannel.close(); - exec.shutdownNow(); } private boolean isHandShakeComplete() { diff --git a/src/main/java/org/java_websocket/server/DefaultSSLWebSocketServerFactory.java b/src/main/java/org/java_websocket/server/DefaultSSLWebSocketServerFactory.java index b871260f8..32896f9a0 100644 --- a/src/main/java/org/java_websocket/server/DefaultSSLWebSocketServerFactory.java +++ b/src/main/java/org/java_websocket/server/DefaultSSLWebSocketServerFactory.java @@ -48,4 +48,8 @@ public WebSocketImpl createWebSocket( WebSocketAdapter a, Draft d, Socket c ) { public WebSocketImpl createWebSocket( WebSocketAdapter a, List d, Socket s ) { return new WebSocketImpl( a, d ); } + @Override + public void close() { + exec.shutdown(); + } } \ No newline at end of file diff --git a/src/main/java/org/java_websocket/server/DefaultWebSocketServerFactory.java b/src/main/java/org/java_websocket/server/DefaultWebSocketServerFactory.java index 3b89cdc2f..59d0e3963 100644 --- a/src/main/java/org/java_websocket/server/DefaultWebSocketServerFactory.java +++ b/src/main/java/org/java_websocket/server/DefaultWebSocketServerFactory.java @@ -23,4 +23,7 @@ public WebSocketImpl createWebSocket( WebSocketAdapter a, List d, Socket public SocketChannel wrapChannel( SocketChannel channel, SelectionKey key ) { return (SocketChannel) channel; } + @Override + public void close() { + } } \ No newline at end of file diff --git a/src/main/java/org/java_websocket/server/WebSocketServer.java b/src/main/java/org/java_websocket/server/WebSocketServer.java index 6404d84eb..04c086aa0 100644 --- a/src/main/java/org/java_websocket/server/WebSocketServer.java +++ b/src/main/java/org/java_websocket/server/WebSocketServer.java @@ -215,6 +215,8 @@ public void stop( int timeout ) throws InterruptedException { ws.close( CloseFrame.GOING_AWAY ); } + wsf.close(); + synchronized ( this ) { if( selectorthread != null && selectorthread != Thread.currentThread() ) { selector.wakeup(); @@ -730,5 +732,7 @@ public interface WebSocketServerFactory extends WebSocketFactory { * @return The channel on which the read and write operations will be performed.
*/ public ByteChannel wrapChannel( SocketChannel channel, SelectionKey key ) throws IOException; + + public void close(); } } From f90c663fc3d1965922cb9ea39cb7f7e8c158dd7a Mon Sep 17 00:00:00 2001 From: genuineparts Date: Sun, 26 Jun 2016 00:21:42 +0200 Subject: [PATCH 3/8] Fix for NPE on WSS Portscan. Fixes #388 & #349 --- .../server/WebSocketServer.java | 1483 +++++++++-------- 1 file changed, 750 insertions(+), 733 deletions(-) diff --git a/src/main/java/org/java_websocket/server/WebSocketServer.java b/src/main/java/org/java_websocket/server/WebSocketServer.java index 12cea2f1d..140b42a89 100644 --- a/src/main/java/org/java_websocket/server/WebSocketServer.java +++ b/src/main/java/org/java_websocket/server/WebSocketServer.java @@ -1,733 +1,750 @@ -package org.java_websocket.server; - -import java.io.IOException; -import java.net.InetSocketAddress; -import java.net.ServerSocket; -import java.net.Socket; -import java.net.UnknownHostException; -import java.nio.ByteBuffer; -import java.nio.channels.ByteChannel; -import java.nio.channels.CancelledKeyException; -import java.nio.channels.ClosedByInterruptException; -import java.nio.channels.SelectableChannel; -import java.nio.channels.SelectionKey; -import java.nio.channels.Selector; -import java.nio.channels.ServerSocketChannel; -import java.nio.channels.SocketChannel; -import java.util.ArrayList; -import java.util.Collection; -import java.util.Collections; -import java.util.HashSet; -import java.util.Iterator; -import java.util.LinkedList; -import java.util.List; -import java.util.Set; -import java.util.concurrent.BlockingQueue; -import java.util.concurrent.CopyOnWriteArraySet; -import java.util.concurrent.LinkedBlockingQueue; -import java.util.concurrent.atomic.AtomicBoolean; -import java.util.concurrent.atomic.AtomicInteger; - -import org.java_websocket.SocketChannelIOHelper; -import org.java_websocket.WebSocket; -import org.java_websocket.WebSocketAdapter; -import org.java_websocket.WebSocketFactory; -import org.java_websocket.WebSocketImpl; -import org.java_websocket.WrappedByteChannel; -import org.java_websocket.drafts.Draft; -import org.java_websocket.exceptions.InvalidDataException; -import org.java_websocket.framing.CloseFrame; -import org.java_websocket.framing.Framedata; -import org.java_websocket.handshake.ClientHandshake; -import org.java_websocket.handshake.Handshakedata; -import org.java_websocket.handshake.ServerHandshakeBuilder; - -/** - * WebSocketServer is an abstract class that only takes care of the - * HTTP handshake portion of WebSockets. It's up to a subclass to add - * functionality/purpose to the server. - * - */ -public abstract class WebSocketServer extends WebSocketAdapter implements Runnable { - - public static int DECODERS = Runtime.getRuntime().availableProcessors(); - - /** - * Holds the list of active WebSocket connections. "Active" means WebSocket - * handshake is complete and socket can be written to, or read from. - */ - private final Collection connections; - /** - * The port number that this WebSocket server should listen on. Default is - * WebSocket.DEFAULT_PORT. - */ - private final InetSocketAddress address; - /** - * The socket channel for this WebSocket server. - */ - private ServerSocketChannel server; - /** - * The 'Selector' used to get event keys from the underlying socket. - */ - private Selector selector; - /** - * The Draft of the WebSocket protocol the Server is adhering to. - */ - private List drafts; - - private Thread selectorthread; - - private final AtomicBoolean isclosed = new AtomicBoolean( false ); - - private List decoders; - - private List iqueue; - private BlockingQueue buffers; - private int queueinvokes = 0; - private final AtomicInteger queuesize = new AtomicInteger( 0 ); - - private WebSocketServerFactory wsf = new DefaultWebSocketServerFactory(); - - /** - * Creates a WebSocketServer that will attempt to - * listen on port WebSocket.DEFAULT_PORT. - * - * @see #WebSocketServer(InetSocketAddress, int, List, Collection) more details here - */ - public WebSocketServer() throws UnknownHostException { - this( new InetSocketAddress( WebSocket.DEFAULT_PORT ), DECODERS, null ); - } - - /** - * Creates a WebSocketServer that will attempt to bind/listen on the given address. - * - * @see #WebSocketServer(InetSocketAddress, int, List, Collection) more details here - */ - public WebSocketServer( InetSocketAddress address ) { - this( address, DECODERS, null ); - } - - /** - * @see #WebSocketServer(InetSocketAddress, int, List, Collection) more details here - */ - public WebSocketServer( InetSocketAddress address , int decoders ) { - this( address, decoders, null ); - } - - /** - * @see #WebSocketServer(InetSocketAddress, int, List, Collection) more details here - */ - public WebSocketServer( InetSocketAddress address , List drafts ) { - this( address, DECODERS, drafts ); - } - - /** - * @see #WebSocketServer(InetSocketAddress, int, List, Collection) more details here - */ - public WebSocketServer( InetSocketAddress address , int decodercount , List drafts ) { - this( address, decodercount, drafts, new HashSet() ); - } - - /** - * Creates a WebSocketServer that will attempt to bind/listen on the given address, - * and comply with Draft version draft. - * - * @param address - * The address (host:port) this server should listen on. - * @param decodercount - * The number of {@link WebSocketWorker}s that will be used to process the incoming network data. By default this will be Runtime.getRuntime().availableProcessors() - * @param drafts - * The versions of the WebSocket protocol that this server - * instance should comply to. Clients that use an other protocol version will be rejected. - * - * @param connectionscontainer - * Allows to specify a collection that will be used to store the websockets in.
- * If you plan to often iterate through the currently connected websockets you may want to use a collection that does not require synchronization like a {@link CopyOnWriteArraySet}. In that case make sure that you overload {@link #removeConnection(WebSocket)} and {@link #addConnection(WebSocket)}.
- * By default a {@link HashSet} will be used. - * - * @see #removeConnection(WebSocket) for more control over syncronized operation - * @see more about drafts - */ - public WebSocketServer( InetSocketAddress address , int decodercount , List drafts , Collection connectionscontainer ) { - if( address == null || decodercount < 1 || connectionscontainer == null ) { - throw new IllegalArgumentException( "address and connectionscontainer must not be null and you need at least 1 decoder" ); - } - - if( drafts == null ) - this.drafts = Collections.emptyList(); - else - this.drafts = drafts; - - this.address = address; - this.connections = connectionscontainer; - - iqueue = new LinkedList(); - - decoders = new ArrayList( decodercount ); - buffers = new LinkedBlockingQueue(); - for( int i = 0 ; i < decodercount ; i++ ) { - WebSocketWorker ex = new WebSocketWorker(); - decoders.add( ex ); - ex.start(); - } - } - - /** - * Starts the server selectorthread that binds to the currently set port number and - * listeners for WebSocket connection requests. Creates a fixed thread pool with the size {@link WebSocketServer#DECODERS}
- * May only be called once. - * - * Alternatively you can call {@link WebSocketServer#run()} directly. - * - * @throws IllegalStateException - */ - public void start() { - if( selectorthread != null ) - throw new IllegalStateException( getClass().getName() + " can only be started once." ); - new Thread( this ).start(); - } - - /** - * Closes all connected clients sockets, then closes the underlying - * ServerSocketChannel, effectively killing the server socket selectorthread, - * freeing the port the server was bound to and stops all internal workerthreads. - * - * If this method is called before the server is started it will never start. - * - * @param timeout - * Specifies how many milliseconds the overall close handshaking may take altogether before the connections are closed without proper close handshaking.
- * - * @throws InterruptedException - */ - public void stop( int timeout ) throws InterruptedException { - if( !isclosed.compareAndSet( false, true ) ) { // this also makes sure that no further connections will be added to this.connections - return; - } - - List socketsToClose = null; - - // copy the connections in a list (prevent callback deadlocks) - synchronized ( connections ) { - socketsToClose = new ArrayList( connections ); - } - - for( WebSocket ws : socketsToClose ) { - ws.close( CloseFrame.GOING_AWAY ); - } - - synchronized ( this ) { - if( selectorthread != null && selectorthread != Thread.currentThread() ) { - selector.wakeup(); - selectorthread.interrupt(); - selectorthread.join( timeout ); - } - } - } - public void stop() throws IOException , InterruptedException { - stop( 0 ); - } - - /** - * Returns a WebSocket[] of currently connected clients. - * Its iterators will be failfast and its not judicious - * to modify it. - * - * @return The currently connected clients. - */ - public Collection connections() { - return this.connections; - } - - public InetSocketAddress getAddress() { - return this.address; - } - - /** - * Gets the port number that this server listens on. - * - * @return The port number. - */ - public int getPort() { - int port = getAddress().getPort(); - if( port == 0 && server != null ) { - port = server.socket().getLocalPort(); - } - return port; - } - - public List getDraft() { - return Collections.unmodifiableList( drafts ); - } - - // Runnable IMPLEMENTATION ///////////////////////////////////////////////// - public void run() { - synchronized ( this ) { - if( selectorthread != null ) - throw new IllegalStateException( getClass().getName() + " can only be started once." ); - selectorthread = Thread.currentThread(); - if( isclosed.get() ) { - return; - } - } - selectorthread.setName( "WebsocketSelector" + selectorthread.getId() ); - try { - server = ServerSocketChannel.open(); - server.configureBlocking( false ); - ServerSocket socket = server.socket(); - socket.setReceiveBufferSize( WebSocketImpl.RCVBUF ); - socket.bind( address ); - selector = Selector.open(); - server.register( selector, server.validOps() ); - } catch ( IOException ex ) { - handleFatal( null, ex ); - return; - } - try { - while ( !selectorthread.isInterrupted() ) { - SelectionKey key = null; - WebSocketImpl conn = null; - try { - selector.select(); - Set keys = selector.selectedKeys(); - Iterator i = keys.iterator(); - - while ( i.hasNext() ) { - key = i.next(); - - if( !key.isValid() ) { - // Object o = key.attachment(); - continue; - } - - if( key.isAcceptable() ) { - if( !onConnect( key ) ) { - key.cancel(); - continue; - } - - SocketChannel channel = server.accept(); - channel.configureBlocking( false ); - WebSocketImpl w = wsf.createWebSocket( this, drafts, channel.socket() ); - w.key = channel.register( selector, SelectionKey.OP_READ, w ); - w.channel = wsf.wrapChannel( channel, w.key ); - i.remove(); - allocateBuffers( w ); - continue; - } - - if( key.isReadable() ) { - conn = (WebSocketImpl) key.attachment(); - ByteBuffer buf = takeBuffer(); - try { - if( SocketChannelIOHelper.read( buf, conn, conn.channel ) ) { - if( buf.hasRemaining() ) { - conn.inQueue.put( buf ); - queue( conn ); - i.remove(); - if( conn.channel instanceof WrappedByteChannel ) { - if( ( (WrappedByteChannel) conn.channel ).isNeedRead() ) { - iqueue.add( conn ); - } - } - } else - pushBuffer( buf ); - } else { - pushBuffer( buf ); - } - } catch ( IOException e ) { - pushBuffer( buf ); - throw e; - } - } - if( key.isWritable() ) { - conn = (WebSocketImpl) key.attachment(); - if( SocketChannelIOHelper.batch( conn, conn.channel ) ) { - if( key.isValid() ) - key.interestOps( SelectionKey.OP_READ ); - } - } - } - while ( !iqueue.isEmpty() ) { - conn = iqueue.remove( 0 ); - WrappedByteChannel c = ( (WrappedByteChannel) conn.channel ); - ByteBuffer buf = takeBuffer(); - try { - if( SocketChannelIOHelper.readMore( buf, conn, c ) ) - iqueue.add( conn ); - if( buf.hasRemaining() ) { - conn.inQueue.put( buf ); - queue( conn ); - } else { - pushBuffer( buf ); - } - } catch ( IOException e ) { - pushBuffer( buf ); - throw e; - } - - } - } catch ( CancelledKeyException e ) { - // an other thread may cancel the key - } catch ( ClosedByInterruptException e ) { - return; // do the same stuff as when InterruptedException is thrown - } catch ( IOException ex ) { - if( key != null ) - key.cancel(); - handleIOException( key, conn, ex ); - } catch ( InterruptedException e ) { - return;// FIXME controlled shutdown (e.g. take care of buffermanagement) - } - } - - } catch ( RuntimeException e ) { - // should hopefully never occur - handleFatal( null, e ); - } finally { - if( decoders != null ) { - for( WebSocketWorker w : decoders ) { - w.interrupt(); - } - } - if( server != null ) { - try { - server.close(); - } catch ( IOException e ) { - onError( null, e ); - } - } - } - } - protected void allocateBuffers( WebSocket c ) throws InterruptedException { - if( queuesize.get() >= 2 * decoders.size() + 1 ) { - return; - } - queuesize.incrementAndGet(); - buffers.put( createBuffer() ); - } - - protected void releaseBuffers( WebSocket c ) throws InterruptedException { - // queuesize.decrementAndGet(); - // takeBuffer(); - } - - public ByteBuffer createBuffer() { - return ByteBuffer.allocate( WebSocketImpl.RCVBUF ); - } - - private void queue( WebSocketImpl ws ) throws InterruptedException { - if( ws.workerThread == null ) { - ws.workerThread = decoders.get( queueinvokes % decoders.size() ); - queueinvokes++; - } - ws.workerThread.put( ws ); - } - - private ByteBuffer takeBuffer() throws InterruptedException { - return buffers.take(); - } - - private void pushBuffer( ByteBuffer buf ) throws InterruptedException { - if( buffers.size() > queuesize.intValue() ) - return; - buffers.put( buf ); - } - - private void handleIOException( SelectionKey key, WebSocket conn, IOException ex ) { - // onWebsocketError( conn, ex );// conn may be null here - if( conn != null ) { - conn.closeConnection( CloseFrame.ABNORMAL_CLOSE, ex.getMessage() ); - } else if( key != null ) { - SelectableChannel channel = key.channel(); - if( channel != null && channel.isOpen() ) { // this could be the case if the IOException ex is a SSLException - try { - channel.close(); - } catch ( IOException e ) { - // there is nothing that must be done here - } - if( WebSocketImpl.DEBUG ) - System.out.println( "Connection closed because of" + ex ); - } - } - } - - private void handleFatal( WebSocket conn, Exception e ) { - onError( conn, e ); - try { - stop(); - } catch ( IOException e1 ) { - onError( null, e1 ); - } catch ( InterruptedException e1 ) { - Thread.currentThread().interrupt(); - onError( null, e1 ); - } - } - - /** - * Gets the XML string that should be returned if a client requests a Flash - * security policy. - * - * The default implementation allows access from all remote domains, but - * only on the port that this WebSocketServer is listening on. - * - * This is specifically implemented for gitime's WebSocket client for Flash: - * http://github.com/gimite/web-socket-js - * - * @return An XML String that comforms to Flash's security policy. You MUST - * not include the null char at the end, it is appended automatically. - */ - protected String getFlashSecurityPolicy() { - return ""; - } - - @Override - public final void onWebsocketMessage( WebSocket conn, String message ) { - onMessage( conn, message ); - } - - @Override - @Deprecated - public/*final*/void onWebsocketMessageFragment( WebSocket conn, Framedata frame ) {// onFragment should be overloaded instead - onFragment( conn, frame ); - } - - @Override - public final void onWebsocketMessage( WebSocket conn, ByteBuffer blob ) { - onMessage( conn, blob ); - } - - @Override - public final void onWebsocketOpen( WebSocket conn, Handshakedata handshake ) { - if( addConnection( conn ) ) { - onOpen( conn, (ClientHandshake) handshake ); - } - } - - @Override - public final void onWebsocketClose( WebSocket conn, int code, String reason, boolean remote ) { - selector.wakeup(); - try { - if( removeConnection( conn ) ) { - onClose( conn, code, reason, remote ); - } - } finally { - try { - releaseBuffers( conn ); - } catch ( InterruptedException e ) { - Thread.currentThread().interrupt(); - } - } - - } - - /** - * This method performs remove operations on the connection and therefore also gives control over whether the operation shall be synchronized - *

- * {@link #WebSocketServer(InetSocketAddress, int, List, Collection)} allows to specify a collection which will be used to store current connections in.
- * Depending on the type on the connection, modifications of that collection may have to be synchronized. - **/ - protected boolean removeConnection( WebSocket ws ) { - boolean removed; - synchronized ( connections ) { - removed = this.connections.remove( ws ); - assert ( removed ); - } - if( isclosed.get() && connections.size() == 0 ) { - selectorthread.interrupt(); - } - return removed; - } - @Override - public ServerHandshakeBuilder onWebsocketHandshakeReceivedAsServer( WebSocket conn, Draft draft, ClientHandshake request ) throws InvalidDataException { - return super.onWebsocketHandshakeReceivedAsServer( conn, draft, request ); - } - - /** @see #removeConnection(WebSocket) */ - protected boolean addConnection( WebSocket ws ) { - if( !isclosed.get() ) { - synchronized ( connections ) { - boolean succ = this.connections.add( ws ); - assert ( succ ); - return succ; - } - } else { - // This case will happen when a new connection gets ready while the server is already stopping. - ws.close( CloseFrame.GOING_AWAY ); - return true;// for consistency sake we will make sure that both onOpen will be called - } - } - /** - * @param conn - * may be null if the error does not belong to a single connection - */ - @Override - public final void onWebsocketError( WebSocket conn, Exception ex ) { - onError( conn, ex ); - } - - @Override - public final void onWriteDemand( WebSocket w ) { - WebSocketImpl conn = (WebSocketImpl) w; - try { - conn.key.interestOps( SelectionKey.OP_READ | SelectionKey.OP_WRITE ); - } catch ( CancelledKeyException e ) { - // the thread which cancels key is responsible for possible cleanup - conn.outQueue.clear(); - } - selector.wakeup(); - } - - @Override - public void onWebsocketCloseInitiated( WebSocket conn, int code, String reason ) { - onCloseInitiated( conn, code, reason ); - } - - @Override - public void onWebsocketClosing( WebSocket conn, int code, String reason, boolean remote ) { - onClosing( conn, code, reason, remote ); - - } - - public void onCloseInitiated( WebSocket conn, int code, String reason ) { - } - - public void onClosing( WebSocket conn, int code, String reason, boolean remote ) { - - } - - public final void setWebSocketFactory( WebSocketServerFactory wsf ) { - this.wsf = wsf; - } - - public final WebSocketFactory getWebSocketFactory() { - return wsf; - } - - /** - * Returns whether a new connection shall be accepted or not.
- * Therefore method is well suited to implement some kind of connection limitation.
- * - * @see #onOpen(WebSocket, ClientHandshake) - * @see #onWebsocketHandshakeReceivedAsServer(WebSocket, Draft, ClientHandshake) - **/ - protected boolean onConnect( SelectionKey key ) { - return true; - } - - private Socket getSocket( WebSocket conn ) { - WebSocketImpl impl = (WebSocketImpl) conn; - return ( (SocketChannel) impl.key.channel() ).socket(); - } - - @Override - public InetSocketAddress getLocalSocketAddress( WebSocket conn ) { - return (InetSocketAddress) getSocket( conn ).getLocalSocketAddress(); - } - - @Override - public InetSocketAddress getRemoteSocketAddress( WebSocket conn ) { - return (InetSocketAddress) getSocket( conn ).getRemoteSocketAddress(); - } - - /** Called after an opening handshake has been performed and the given websocket is ready to be written on. */ - public abstract void onOpen( WebSocket conn, ClientHandshake handshake ); - /** - * Called after the websocket connection has been closed. - * - * @param code - * The codes can be looked up here: {@link CloseFrame} - * @param reason - * Additional information string - * @param remote - * Returns whether or not the closing of the connection was initiated by the remote host. - **/ - public abstract void onClose( WebSocket conn, int code, String reason, boolean remote ); - /** - * Callback for string messages received from the remote host - * - * @see #onMessage(WebSocket, ByteBuffer) - **/ - public abstract void onMessage( WebSocket conn, String message ); - /** - * Called when errors occurs. If an error causes the websocket connection to fail {@link #onClose(WebSocket, int, String, boolean)} will be called additionally.
- * This method will be called primarily because of IO or protocol errors.
- * If the given exception is an RuntimeException that probably means that you encountered a bug.
- * - * @param conn - * Can be null if there error does not belong to one specific websocket. For example if the servers port could not be bound. - **/ - public abstract void onError( WebSocket conn, Exception ex ); - /** - * Callback for binary messages received from the remote host - * - * @see #onMessage(WebSocket, String) - **/ - public void onMessage( WebSocket conn, ByteBuffer message ) { - } - - /** - * @see WebSocket#sendFragmentedFrame(org.java_websocket.framing.Framedata.Opcode, ByteBuffer, boolean) - */ - public void onFragment( WebSocket conn, Framedata fragment ) { - } - - public class WebSocketWorker extends Thread { - - private BlockingQueue iqueue; - - public WebSocketWorker() { - iqueue = new LinkedBlockingQueue(); - setName( "WebSocketWorker-" + getId() ); - setUncaughtExceptionHandler( new UncaughtExceptionHandler() { - @Override - public void uncaughtException( Thread t, Throwable e ) { - getDefaultUncaughtExceptionHandler().uncaughtException( t, e ); - } - } ); - } - - public void put( WebSocketImpl ws ) throws InterruptedException { - iqueue.put( ws ); - } - - @Override - public void run() { - WebSocketImpl ws = null; - try { - while ( true ) { - ByteBuffer buf = null; - ws = iqueue.take(); - buf = ws.inQueue.poll(); - assert ( buf != null ); - try { - ws.decode( buf ); - } catch(Exception e){ - System.err.println("Error while reading from remote connection: " + e); - } - - finally { - pushBuffer( buf ); - } - } - } catch ( InterruptedException e ) { - } catch ( RuntimeException e ) { - handleFatal( ws, e ); - } - } - } - - public interface WebSocketServerFactory extends WebSocketFactory { - @Override - public WebSocketImpl createWebSocket( WebSocketAdapter a, Draft d, Socket s ); - - public WebSocketImpl createWebSocket( WebSocketAdapter a, List drafts, Socket s ); - - /** - * Allows to wrap the Socketchannel( key.channel() ) to insert a protocol layer( like ssl or proxy authentication) beyond the ws layer. - * - * @param key - * a SelectionKey of an open SocketChannel. - * @return The channel on which the read and write operations will be performed.
- */ - public ByteChannel wrapChannel( SocketChannel channel, SelectionKey key ) throws IOException; - } -} +package org.java_websocket.server; + +import java.io.IOException; +import java.net.InetSocketAddress; +import java.net.ServerSocket; +import java.net.Socket; +import java.net.UnknownHostException; +import java.nio.ByteBuffer; +import java.nio.channels.ByteChannel; +import java.nio.channels.CancelledKeyException; +import java.nio.channels.ClosedByInterruptException; +import java.nio.channels.SelectableChannel; +import java.nio.channels.SelectionKey; +import java.nio.channels.Selector; +import java.nio.channels.ServerSocketChannel; +import java.nio.channels.SocketChannel; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.HashSet; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.List; +import java.util.Set; +import java.util.concurrent.BlockingQueue; +import java.util.concurrent.CopyOnWriteArraySet; +import java.util.concurrent.LinkedBlockingQueue; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicInteger; + +import org.java_websocket.SocketChannelIOHelper; +import org.java_websocket.WebSocket; +import org.java_websocket.WebSocketAdapter; +import org.java_websocket.WebSocketFactory; +import org.java_websocket.WebSocketImpl; +import org.java_websocket.WrappedByteChannel; +import org.java_websocket.drafts.Draft; +import org.java_websocket.exceptions.InvalidDataException; +import org.java_websocket.framing.CloseFrame; +import org.java_websocket.framing.Framedata; +import org.java_websocket.handshake.ClientHandshake; +import org.java_websocket.handshake.Handshakedata; +import org.java_websocket.handshake.ServerHandshakeBuilder; + +/** + * WebSocketServer is an abstract class that only takes care of the + * HTTP handshake portion of WebSockets. It's up to a subclass to add + * functionality/purpose to the server. + * + */ +public abstract class WebSocketServer extends WebSocketAdapter implements Runnable { + + public static int DECODERS = Runtime.getRuntime().availableProcessors(); + + /** + * Holds the list of active WebSocket connections. "Active" means WebSocket + * handshake is complete and socket can be written to, or read from. + */ + private final Collection connections; + /** + * The port number that this WebSocket server should listen on. Default is + * WebSocket.DEFAULT_PORT. + */ + private final InetSocketAddress address; + /** + * The socket channel for this WebSocket server. + */ + private ServerSocketChannel server; + /** + * The 'Selector' used to get event keys from the underlying socket. + */ + private Selector selector; + /** + * The Draft of the WebSocket protocol the Server is adhering to. + */ + private List drafts; + + private Thread selectorthread; + + private final AtomicBoolean isclosed = new AtomicBoolean( false ); + + private List decoders; + + private List iqueue; + private BlockingQueue buffers; + private int queueinvokes = 0; + private final AtomicInteger queuesize = new AtomicInteger( 0 ); + + private WebSocketServerFactory wsf = new DefaultWebSocketServerFactory(); + + /** + * Creates a WebSocketServer that will attempt to + * listen on port WebSocket.DEFAULT_PORT. + * + * @see #WebSocketServer(InetSocketAddress, int, List, Collection) more details here + */ + public WebSocketServer() throws UnknownHostException { + this( new InetSocketAddress( WebSocket.DEFAULT_PORT ), DECODERS, null ); + } + + /** + * Creates a WebSocketServer that will attempt to bind/listen on the given address. + * + * @see #WebSocketServer(InetSocketAddress, int, List, Collection) more details here + */ + public WebSocketServer( InetSocketAddress address ) { + this( address, DECODERS, null ); + } + + /** + * @see #WebSocketServer(InetSocketAddress, int, List, Collection) more details here + */ + public WebSocketServer( InetSocketAddress address , int decoders ) { + this( address, decoders, null ); + } + + /** + * @see #WebSocketServer(InetSocketAddress, int, List, Collection) more details here + */ + public WebSocketServer( InetSocketAddress address , List drafts ) { + this( address, DECODERS, drafts ); + } + + /** + * @see #WebSocketServer(InetSocketAddress, int, List, Collection) more details here + */ + public WebSocketServer( InetSocketAddress address , int decodercount , List drafts ) { + this( address, decodercount, drafts, new HashSet() ); + } + + /** + * Creates a WebSocketServer that will attempt to bind/listen on the given address, + * and comply with Draft version draft. + * + * @param address + * The address (host:port) this server should listen on. + * @param decodercount + * The number of {@link WebSocketWorker}s that will be used to process the incoming network data. By default this will be Runtime.getRuntime().availableProcessors() + * @param drafts + * The versions of the WebSocket protocol that this server + * instance should comply to. Clients that use an other protocol version will be rejected. + * + * @param connectionscontainer + * Allows to specify a collection that will be used to store the websockets in.
+ * If you plan to often iterate through the currently connected websockets you may want to use a collection that does not require synchronization like a {@link CopyOnWriteArraySet}. In that case make sure that you overload {@link #removeConnection(WebSocket)} and {@link #addConnection(WebSocket)}.
+ * By default a {@link HashSet} will be used. + * + * @see #removeConnection(WebSocket) for more control over syncronized operation + * @see more about drafts + */ + public WebSocketServer( InetSocketAddress address , int decodercount , List drafts , Collection connectionscontainer ) { + if( address == null || decodercount < 1 || connectionscontainer == null ) { + throw new IllegalArgumentException( "address and connectionscontainer must not be null and you need at least 1 decoder" ); + } + + if( drafts == null ) + this.drafts = Collections.emptyList(); + else + this.drafts = drafts; + + this.address = address; + this.connections = connectionscontainer; + + iqueue = new LinkedList(); + + decoders = new ArrayList( decodercount ); + buffers = new LinkedBlockingQueue(); + for( int i = 0 ; i < decodercount ; i++ ) { + WebSocketWorker ex = new WebSocketWorker(); + decoders.add( ex ); + ex.start(); + } + } + + /** + * Starts the server selectorthread that binds to the currently set port number and + * listeners for WebSocket connection requests. Creates a fixed thread pool with the size {@link WebSocketServer#DECODERS}
+ * May only be called once. + * + * Alternatively you can call {@link WebSocketServer#run()} directly. + * + * @throws IllegalStateException + */ + public void start() { + if( selectorthread != null ) + throw new IllegalStateException( getClass().getName() + " can only be started once." ); + new Thread( this ).start(); + } + + /** + * Closes all connected clients sockets, then closes the underlying + * ServerSocketChannel, effectively killing the server socket selectorthread, + * freeing the port the server was bound to and stops all internal workerthreads. + * + * If this method is called before the server is started it will never start. + * + * @param timeout + * Specifies how many milliseconds the overall close handshaking may take altogether before the connections are closed without proper close handshaking.
+ * + * @throws InterruptedException + */ + public void stop( int timeout ) throws InterruptedException { + if( !isclosed.compareAndSet( false, true ) ) { // this also makes sure that no further connections will be added to this.connections + return; + } + + List socketsToClose = null; + + // copy the connections in a list (prevent callback deadlocks) + synchronized ( connections ) { + socketsToClose = new ArrayList( connections ); + } + + for( WebSocket ws : socketsToClose ) { + ws.close( CloseFrame.GOING_AWAY ); + } + + synchronized ( this ) { + if( selectorthread != null && selectorthread != Thread.currentThread() ) { + selector.wakeup(); + selectorthread.interrupt(); + selectorthread.join( timeout ); + } + } + } + public void stop() throws IOException , InterruptedException { + stop( 0 ); + } + + /** + * Returns a WebSocket[] of currently connected clients. + * Its iterators will be failfast and its not judicious + * to modify it. + * + * @return The currently connected clients. + */ + public Collection connections() { + return this.connections; + } + + public InetSocketAddress getAddress() { + return this.address; + } + + /** + * Gets the port number that this server listens on. + * + * @return The port number. + */ + public int getPort() { + int port = getAddress().getPort(); + if( port == 0 && server != null ) { + port = server.socket().getLocalPort(); + } + return port; + } + + public List getDraft() { + return Collections.unmodifiableList( drafts ); + } + + // Runnable IMPLEMENTATION ///////////////////////////////////////////////// + public void run() { + synchronized ( this ) { + if( selectorthread != null ) + throw new IllegalStateException( getClass().getName() + " can only be started once." ); + selectorthread = Thread.currentThread(); + if( isclosed.get() ) { + return; + } + } + selectorthread.setName( "WebsocketSelector" + selectorthread.getId() ); + try { + server = ServerSocketChannel.open(); + server.configureBlocking( false ); + ServerSocket socket = server.socket(); + socket.setReceiveBufferSize( WebSocketImpl.RCVBUF ); + socket.bind( address ); + selector = Selector.open(); + server.register( selector, server.validOps() ); + } catch ( IOException ex ) { + handleFatal( null, ex ); + return; + } + try { + while ( !selectorthread.isInterrupted() ) { + SelectionKey key = null; + WebSocketImpl conn = null; + try { + selector.select(); + Set keys = selector.selectedKeys(); + Iterator i = keys.iterator(); + + while ( i.hasNext() ) { + key = i.next(); + conn = null; + + if( !key.isValid() ) { + // Object o = key.attachment(); + continue; + } + + if( key.isAcceptable() ) { + if( !onConnect( key ) ) { + key.cancel(); + continue; + } + + SocketChannel channel = server.accept(); + if(channel==null){ + continue; + } + channel.configureBlocking( false ); + WebSocketImpl w = wsf.createWebSocket( this, drafts, channel.socket() ); + w.key = channel.register( selector, SelectionKey.OP_READ, w ); + try { + w.channel = wsf.wrapChannel( channel, w.key ); + i.remove(); + allocateBuffers( w ); + continue; + } catch (IOException ex) { + if (w.key != null) + w.key.cancel(); + } + continue; + } + + if( key.isReadable() ) { + conn = (WebSocketImpl) key.attachment(); + ByteBuffer buf = takeBuffer(); + if(conn.channel == null){ + if( key != null ) + key.cancel(); + + handleIOException( key, conn, new IOException() ); + continue; + } + try { + if( SocketChannelIOHelper.read( buf, conn, conn.channel ) ) { + if( buf.hasRemaining() ) { + conn.inQueue.put( buf ); + queue( conn ); + i.remove(); + if( conn.channel instanceof WrappedByteChannel ) { + if( ( (WrappedByteChannel) conn.channel ).isNeedRead() ) { + iqueue.add( conn ); + } + } + } else + pushBuffer( buf ); + } else { + pushBuffer( buf ); + } + } catch ( IOException e ) { + pushBuffer( buf ); + throw e; + } + } + if( key.isWritable() ) { + conn = (WebSocketImpl) key.attachment(); + if( SocketChannelIOHelper.batch( conn, conn.channel ) ) { + if( key.isValid() ) + key.interestOps( SelectionKey.OP_READ ); + } + } + } + while ( !iqueue.isEmpty() ) { + conn = iqueue.remove( 0 ); + WrappedByteChannel c = ( (WrappedByteChannel) conn.channel ); + ByteBuffer buf = takeBuffer(); + try { + if( SocketChannelIOHelper.readMore( buf, conn, c ) ) + iqueue.add( conn ); + if( buf.hasRemaining() ) { + conn.inQueue.put( buf ); + queue( conn ); + } else { + pushBuffer( buf ); + } + } catch ( IOException e ) { + pushBuffer( buf ); + throw e; + } + + } + } catch ( CancelledKeyException e ) { + // an other thread may cancel the key + } catch ( ClosedByInterruptException e ) { + return; // do the same stuff as when InterruptedException is thrown + } catch ( IOException ex ) { + if( key != null ) + key.cancel(); + handleIOException( key, conn, ex ); + } catch ( InterruptedException e ) { + return;// FIXME controlled shutdown (e.g. take care of buffermanagement) + } + } + + } catch ( RuntimeException e ) { + // should hopefully never occur + handleFatal( null, e ); + } finally { + if( decoders != null ) { + for( WebSocketWorker w : decoders ) { + w.interrupt(); + } + } + if( server != null ) { + try { + server.close(); + } catch ( IOException e ) { + onError( null, e ); + } + } + } + } + protected void allocateBuffers( WebSocket c ) throws InterruptedException { + if( queuesize.get() >= 2 * decoders.size() + 1 ) { + return; + } + queuesize.incrementAndGet(); + buffers.put( createBuffer() ); + } + + protected void releaseBuffers( WebSocket c ) throws InterruptedException { + // queuesize.decrementAndGet(); + // takeBuffer(); + } + + public ByteBuffer createBuffer() { + return ByteBuffer.allocate( WebSocketImpl.RCVBUF ); + } + + private void queue( WebSocketImpl ws ) throws InterruptedException { + if( ws.workerThread == null ) { + ws.workerThread = decoders.get( queueinvokes % decoders.size() ); + queueinvokes++; + } + ws.workerThread.put( ws ); + } + + private ByteBuffer takeBuffer() throws InterruptedException { + return buffers.take(); + } + + private void pushBuffer( ByteBuffer buf ) throws InterruptedException { + if( buffers.size() > queuesize.intValue() ) + return; + buffers.put( buf ); + } + + private void handleIOException( SelectionKey key, WebSocket conn, IOException ex ) { + // onWebsocketError( conn, ex );// conn may be null here + if( conn != null ) { + conn.closeConnection( CloseFrame.ABNORMAL_CLOSE, ex.getMessage() ); + } else if( key != null ) { + SelectableChannel channel = key.channel(); + if( channel != null && channel.isOpen() ) { // this could be the case if the IOException ex is a SSLException + try { + channel.close(); + } catch ( IOException e ) { + // there is nothing that must be done here + } + if( WebSocketImpl.DEBUG ) + System.out.println( "Connection closed because of" + ex ); + } + } + } + + private void handleFatal( WebSocket conn, Exception e ) { + onError( conn, e ); + try { + stop(); + } catch ( IOException e1 ) { + onError( null, e1 ); + } catch ( InterruptedException e1 ) { + Thread.currentThread().interrupt(); + onError( null, e1 ); + } + } + + /** + * Gets the XML string that should be returned if a client requests a Flash + * security policy. + * + * The default implementation allows access from all remote domains, but + * only on the port that this WebSocketServer is listening on. + * + * This is specifically implemented for gitime's WebSocket client for Flash: + * http://github.com/gimite/web-socket-js + * + * @return An XML String that comforms to Flash's security policy. You MUST + * not include the null char at the end, it is appended automatically. + */ + protected String getFlashSecurityPolicy() { + return ""; + } + + @Override + public final void onWebsocketMessage( WebSocket conn, String message ) { + onMessage( conn, message ); + } + + @Override + @Deprecated + public/*final*/void onWebsocketMessageFragment( WebSocket conn, Framedata frame ) {// onFragment should be overloaded instead + onFragment( conn, frame ); + } + + @Override + public final void onWebsocketMessage( WebSocket conn, ByteBuffer blob ) { + onMessage( conn, blob ); + } + + @Override + public final void onWebsocketOpen( WebSocket conn, Handshakedata handshake ) { + if( addConnection( conn ) ) { + onOpen( conn, (ClientHandshake) handshake ); + } + } + + @Override + public final void onWebsocketClose( WebSocket conn, int code, String reason, boolean remote ) { + selector.wakeup(); + try { + if( removeConnection( conn ) ) { + onClose( conn, code, reason, remote ); + } + } finally { + try { + releaseBuffers( conn ); + } catch ( InterruptedException e ) { + Thread.currentThread().interrupt(); + } + } + + } + + /** + * This method performs remove operations on the connection and therefore also gives control over whether the operation shall be synchronized + *

+ * {@link #WebSocketServer(InetSocketAddress, int, List, Collection)} allows to specify a collection which will be used to store current connections in.
+ * Depending on the type on the connection, modifications of that collection may have to be synchronized. + **/ + protected boolean removeConnection( WebSocket ws ) { + boolean removed; + synchronized ( connections ) { + removed = this.connections.remove( ws ); + assert ( removed ); + } + if( isclosed.get() && connections.size() == 0 ) { + selectorthread.interrupt(); + } + return removed; + } + @Override + public ServerHandshakeBuilder onWebsocketHandshakeReceivedAsServer( WebSocket conn, Draft draft, ClientHandshake request ) throws InvalidDataException { + return super.onWebsocketHandshakeReceivedAsServer( conn, draft, request ); + } + + /** @see #removeConnection(WebSocket) */ + protected boolean addConnection( WebSocket ws ) { + if( !isclosed.get() ) { + synchronized ( connections ) { + boolean succ = this.connections.add( ws ); + assert ( succ ); + return succ; + } + } else { + // This case will happen when a new connection gets ready while the server is already stopping. + ws.close( CloseFrame.GOING_AWAY ); + return true;// for consistency sake we will make sure that both onOpen will be called + } + } + /** + * @param conn + * may be null if the error does not belong to a single connection + */ + @Override + public final void onWebsocketError( WebSocket conn, Exception ex ) { + onError( conn, ex ); + } + + @Override + public final void onWriteDemand( WebSocket w ) { + WebSocketImpl conn = (WebSocketImpl) w; + try { + conn.key.interestOps( SelectionKey.OP_READ | SelectionKey.OP_WRITE ); + } catch ( CancelledKeyException e ) { + // the thread which cancels key is responsible for possible cleanup + conn.outQueue.clear(); + } + selector.wakeup(); + } + + @Override + public void onWebsocketCloseInitiated( WebSocket conn, int code, String reason ) { + onCloseInitiated( conn, code, reason ); + } + + @Override + public void onWebsocketClosing( WebSocket conn, int code, String reason, boolean remote ) { + onClosing( conn, code, reason, remote ); + + } + + public void onCloseInitiated( WebSocket conn, int code, String reason ) { + } + + public void onClosing( WebSocket conn, int code, String reason, boolean remote ) { + + } + + public final void setWebSocketFactory( WebSocketServerFactory wsf ) { + this.wsf = wsf; + } + + public final WebSocketFactory getWebSocketFactory() { + return wsf; + } + + /** + * Returns whether a new connection shall be accepted or not.
+ * Therefore method is well suited to implement some kind of connection limitation.
+ * + * @see #onOpen(WebSocket, ClientHandshake) + * @see #onWebsocketHandshakeReceivedAsServer(WebSocket, Draft, ClientHandshake) + **/ + protected boolean onConnect( SelectionKey key ) { + return true; + } + + private Socket getSocket( WebSocket conn ) { + WebSocketImpl impl = (WebSocketImpl) conn; + return ( (SocketChannel) impl.key.channel() ).socket(); + } + + @Override + public InetSocketAddress getLocalSocketAddress( WebSocket conn ) { + return (InetSocketAddress) getSocket( conn ).getLocalSocketAddress(); + } + + @Override + public InetSocketAddress getRemoteSocketAddress( WebSocket conn ) { + return (InetSocketAddress) getSocket( conn ).getRemoteSocketAddress(); + } + + /** Called after an opening handshake has been performed and the given websocket is ready to be written on. */ + public abstract void onOpen( WebSocket conn, ClientHandshake handshake ); + /** + * Called after the websocket connection has been closed. + * + * @param code + * The codes can be looked up here: {@link CloseFrame} + * @param reason + * Additional information string + * @param remote + * Returns whether or not the closing of the connection was initiated by the remote host. + **/ + public abstract void onClose( WebSocket conn, int code, String reason, boolean remote ); + /** + * Callback for string messages received from the remote host + * + * @see #onMessage(WebSocket, ByteBuffer) + **/ + public abstract void onMessage( WebSocket conn, String message ); + /** + * Called when errors occurs. If an error causes the websocket connection to fail {@link #onClose(WebSocket, int, String, boolean)} will be called additionally.
+ * This method will be called primarily because of IO or protocol errors.
+ * If the given exception is an RuntimeException that probably means that you encountered a bug.
+ * + * @param conn + * Can be null if there error does not belong to one specific websocket. For example if the servers port could not be bound. + **/ + public abstract void onError( WebSocket conn, Exception ex ); + /** + * Callback for binary messages received from the remote host + * + * @see #onMessage(WebSocket, String) + **/ + public void onMessage( WebSocket conn, ByteBuffer message ) { + } + + /** + * @see WebSocket#sendFragmentedFrame(org.java_websocket.framing.Framedata.Opcode, ByteBuffer, boolean) + */ + public void onFragment( WebSocket conn, Framedata fragment ) { + } + + public class WebSocketWorker extends Thread { + + private BlockingQueue iqueue; + + public WebSocketWorker() { + iqueue = new LinkedBlockingQueue(); + setName( "WebSocketWorker-" + getId() ); + setUncaughtExceptionHandler( new UncaughtExceptionHandler() { + @Override + public void uncaughtException( Thread t, Throwable e ) { + getDefaultUncaughtExceptionHandler().uncaughtException( t, e ); + } + } ); + } + + public void put( WebSocketImpl ws ) throws InterruptedException { + iqueue.put( ws ); + } + + @Override + public void run() { + WebSocketImpl ws = null; + try { + while ( true ) { + ByteBuffer buf = null; + ws = iqueue.take(); + buf = ws.inQueue.poll(); + assert ( buf != null ); + try { + ws.decode( buf ); + } catch(Exception e){ + System.err.println("Error while reading from remote connection: " + e); + } + + finally { + pushBuffer( buf ); + } + } + } catch ( InterruptedException e ) { + } catch ( RuntimeException e ) { + handleFatal( ws, e ); + } + } + } + + public interface WebSocketServerFactory extends WebSocketFactory { + @Override + public WebSocketImpl createWebSocket( WebSocketAdapter a, Draft d, Socket s ); + + public WebSocketImpl createWebSocket( WebSocketAdapter a, List drafts, Socket s ); + + /** + * Allows to wrap the Socketchannel( key.channel() ) to insert a protocol layer( like ssl or proxy authentication) beyond the ws layer. + * + * @param key + * a SelectionKey of an open SocketChannel. + * @return The channel on which the read and write operations will be performed.
+ */ + public ByteChannel wrapChannel( SocketChannel channel, SelectionKey key ) throws IOException; + } +} From 6c673a3b0f48b10d201a7b818fac488ada1a1d16 Mon Sep 17 00:00:00 2001 From: genuineparts Date: Wed, 4 Jan 2017 01:27:08 +0100 Subject: [PATCH 4/8] line endings --- .../server/WebSocketServer.java | 1500 ++++++++--------- 1 file changed, 750 insertions(+), 750 deletions(-) diff --git a/src/main/java/org/java_websocket/server/WebSocketServer.java b/src/main/java/org/java_websocket/server/WebSocketServer.java index 140b42a89..be2d856af 100644 --- a/src/main/java/org/java_websocket/server/WebSocketServer.java +++ b/src/main/java/org/java_websocket/server/WebSocketServer.java @@ -1,750 +1,750 @@ -package org.java_websocket.server; - -import java.io.IOException; -import java.net.InetSocketAddress; -import java.net.ServerSocket; -import java.net.Socket; -import java.net.UnknownHostException; -import java.nio.ByteBuffer; -import java.nio.channels.ByteChannel; -import java.nio.channels.CancelledKeyException; -import java.nio.channels.ClosedByInterruptException; -import java.nio.channels.SelectableChannel; -import java.nio.channels.SelectionKey; -import java.nio.channels.Selector; -import java.nio.channels.ServerSocketChannel; -import java.nio.channels.SocketChannel; -import java.util.ArrayList; -import java.util.Collection; -import java.util.Collections; -import java.util.HashSet; -import java.util.Iterator; -import java.util.LinkedList; -import java.util.List; -import java.util.Set; -import java.util.concurrent.BlockingQueue; -import java.util.concurrent.CopyOnWriteArraySet; -import java.util.concurrent.LinkedBlockingQueue; -import java.util.concurrent.atomic.AtomicBoolean; -import java.util.concurrent.atomic.AtomicInteger; - -import org.java_websocket.SocketChannelIOHelper; -import org.java_websocket.WebSocket; -import org.java_websocket.WebSocketAdapter; -import org.java_websocket.WebSocketFactory; -import org.java_websocket.WebSocketImpl; -import org.java_websocket.WrappedByteChannel; -import org.java_websocket.drafts.Draft; -import org.java_websocket.exceptions.InvalidDataException; -import org.java_websocket.framing.CloseFrame; -import org.java_websocket.framing.Framedata; -import org.java_websocket.handshake.ClientHandshake; -import org.java_websocket.handshake.Handshakedata; -import org.java_websocket.handshake.ServerHandshakeBuilder; - -/** - * WebSocketServer is an abstract class that only takes care of the - * HTTP handshake portion of WebSockets. It's up to a subclass to add - * functionality/purpose to the server. - * - */ -public abstract class WebSocketServer extends WebSocketAdapter implements Runnable { - - public static int DECODERS = Runtime.getRuntime().availableProcessors(); - - /** - * Holds the list of active WebSocket connections. "Active" means WebSocket - * handshake is complete and socket can be written to, or read from. - */ - private final Collection connections; - /** - * The port number that this WebSocket server should listen on. Default is - * WebSocket.DEFAULT_PORT. - */ - private final InetSocketAddress address; - /** - * The socket channel for this WebSocket server. - */ - private ServerSocketChannel server; - /** - * The 'Selector' used to get event keys from the underlying socket. - */ - private Selector selector; - /** - * The Draft of the WebSocket protocol the Server is adhering to. - */ - private List drafts; - - private Thread selectorthread; - - private final AtomicBoolean isclosed = new AtomicBoolean( false ); - - private List decoders; - - private List iqueue; - private BlockingQueue buffers; - private int queueinvokes = 0; - private final AtomicInteger queuesize = new AtomicInteger( 0 ); - - private WebSocketServerFactory wsf = new DefaultWebSocketServerFactory(); - - /** - * Creates a WebSocketServer that will attempt to - * listen on port WebSocket.DEFAULT_PORT. - * - * @see #WebSocketServer(InetSocketAddress, int, List, Collection) more details here - */ - public WebSocketServer() throws UnknownHostException { - this( new InetSocketAddress( WebSocket.DEFAULT_PORT ), DECODERS, null ); - } - - /** - * Creates a WebSocketServer that will attempt to bind/listen on the given address. - * - * @see #WebSocketServer(InetSocketAddress, int, List, Collection) more details here - */ - public WebSocketServer( InetSocketAddress address ) { - this( address, DECODERS, null ); - } - - /** - * @see #WebSocketServer(InetSocketAddress, int, List, Collection) more details here - */ - public WebSocketServer( InetSocketAddress address , int decoders ) { - this( address, decoders, null ); - } - - /** - * @see #WebSocketServer(InetSocketAddress, int, List, Collection) more details here - */ - public WebSocketServer( InetSocketAddress address , List drafts ) { - this( address, DECODERS, drafts ); - } - - /** - * @see #WebSocketServer(InetSocketAddress, int, List, Collection) more details here - */ - public WebSocketServer( InetSocketAddress address , int decodercount , List drafts ) { - this( address, decodercount, drafts, new HashSet() ); - } - - /** - * Creates a WebSocketServer that will attempt to bind/listen on the given address, - * and comply with Draft version draft. - * - * @param address - * The address (host:port) this server should listen on. - * @param decodercount - * The number of {@link WebSocketWorker}s that will be used to process the incoming network data. By default this will be Runtime.getRuntime().availableProcessors() - * @param drafts - * The versions of the WebSocket protocol that this server - * instance should comply to. Clients that use an other protocol version will be rejected. - * - * @param connectionscontainer - * Allows to specify a collection that will be used to store the websockets in.
- * If you plan to often iterate through the currently connected websockets you may want to use a collection that does not require synchronization like a {@link CopyOnWriteArraySet}. In that case make sure that you overload {@link #removeConnection(WebSocket)} and {@link #addConnection(WebSocket)}.
- * By default a {@link HashSet} will be used. - * - * @see #removeConnection(WebSocket) for more control over syncronized operation - * @see more about drafts - */ - public WebSocketServer( InetSocketAddress address , int decodercount , List drafts , Collection connectionscontainer ) { - if( address == null || decodercount < 1 || connectionscontainer == null ) { - throw new IllegalArgumentException( "address and connectionscontainer must not be null and you need at least 1 decoder" ); - } - - if( drafts == null ) - this.drafts = Collections.emptyList(); - else - this.drafts = drafts; - - this.address = address; - this.connections = connectionscontainer; - - iqueue = new LinkedList(); - - decoders = new ArrayList( decodercount ); - buffers = new LinkedBlockingQueue(); - for( int i = 0 ; i < decodercount ; i++ ) { - WebSocketWorker ex = new WebSocketWorker(); - decoders.add( ex ); - ex.start(); - } - } - - /** - * Starts the server selectorthread that binds to the currently set port number and - * listeners for WebSocket connection requests. Creates a fixed thread pool with the size {@link WebSocketServer#DECODERS}
- * May only be called once. - * - * Alternatively you can call {@link WebSocketServer#run()} directly. - * - * @throws IllegalStateException - */ - public void start() { - if( selectorthread != null ) - throw new IllegalStateException( getClass().getName() + " can only be started once." ); - new Thread( this ).start(); - } - - /** - * Closes all connected clients sockets, then closes the underlying - * ServerSocketChannel, effectively killing the server socket selectorthread, - * freeing the port the server was bound to and stops all internal workerthreads. - * - * If this method is called before the server is started it will never start. - * - * @param timeout - * Specifies how many milliseconds the overall close handshaking may take altogether before the connections are closed without proper close handshaking.
- * - * @throws InterruptedException - */ - public void stop( int timeout ) throws InterruptedException { - if( !isclosed.compareAndSet( false, true ) ) { // this also makes sure that no further connections will be added to this.connections - return; - } - - List socketsToClose = null; - - // copy the connections in a list (prevent callback deadlocks) - synchronized ( connections ) { - socketsToClose = new ArrayList( connections ); - } - - for( WebSocket ws : socketsToClose ) { - ws.close( CloseFrame.GOING_AWAY ); - } - - synchronized ( this ) { - if( selectorthread != null && selectorthread != Thread.currentThread() ) { - selector.wakeup(); - selectorthread.interrupt(); - selectorthread.join( timeout ); - } - } - } - public void stop() throws IOException , InterruptedException { - stop( 0 ); - } - - /** - * Returns a WebSocket[] of currently connected clients. - * Its iterators will be failfast and its not judicious - * to modify it. - * - * @return The currently connected clients. - */ - public Collection connections() { - return this.connections; - } - - public InetSocketAddress getAddress() { - return this.address; - } - - /** - * Gets the port number that this server listens on. - * - * @return The port number. - */ - public int getPort() { - int port = getAddress().getPort(); - if( port == 0 && server != null ) { - port = server.socket().getLocalPort(); - } - return port; - } - - public List getDraft() { - return Collections.unmodifiableList( drafts ); - } - - // Runnable IMPLEMENTATION ///////////////////////////////////////////////// - public void run() { - synchronized ( this ) { - if( selectorthread != null ) - throw new IllegalStateException( getClass().getName() + " can only be started once." ); - selectorthread = Thread.currentThread(); - if( isclosed.get() ) { - return; - } - } - selectorthread.setName( "WebsocketSelector" + selectorthread.getId() ); - try { - server = ServerSocketChannel.open(); - server.configureBlocking( false ); - ServerSocket socket = server.socket(); - socket.setReceiveBufferSize( WebSocketImpl.RCVBUF ); - socket.bind( address ); - selector = Selector.open(); - server.register( selector, server.validOps() ); - } catch ( IOException ex ) { - handleFatal( null, ex ); - return; - } - try { - while ( !selectorthread.isInterrupted() ) { - SelectionKey key = null; - WebSocketImpl conn = null; - try { - selector.select(); - Set keys = selector.selectedKeys(); - Iterator i = keys.iterator(); - - while ( i.hasNext() ) { - key = i.next(); - conn = null; - - if( !key.isValid() ) { - // Object o = key.attachment(); - continue; - } - - if( key.isAcceptable() ) { - if( !onConnect( key ) ) { - key.cancel(); - continue; - } - - SocketChannel channel = server.accept(); - if(channel==null){ - continue; - } - channel.configureBlocking( false ); - WebSocketImpl w = wsf.createWebSocket( this, drafts, channel.socket() ); - w.key = channel.register( selector, SelectionKey.OP_READ, w ); - try { - w.channel = wsf.wrapChannel( channel, w.key ); - i.remove(); - allocateBuffers( w ); - continue; - } catch (IOException ex) { - if (w.key != null) - w.key.cancel(); - } - continue; - } - - if( key.isReadable() ) { - conn = (WebSocketImpl) key.attachment(); - ByteBuffer buf = takeBuffer(); - if(conn.channel == null){ - if( key != null ) - key.cancel(); - - handleIOException( key, conn, new IOException() ); - continue; - } - try { - if( SocketChannelIOHelper.read( buf, conn, conn.channel ) ) { - if( buf.hasRemaining() ) { - conn.inQueue.put( buf ); - queue( conn ); - i.remove(); - if( conn.channel instanceof WrappedByteChannel ) { - if( ( (WrappedByteChannel) conn.channel ).isNeedRead() ) { - iqueue.add( conn ); - } - } - } else - pushBuffer( buf ); - } else { - pushBuffer( buf ); - } - } catch ( IOException e ) { - pushBuffer( buf ); - throw e; - } - } - if( key.isWritable() ) { - conn = (WebSocketImpl) key.attachment(); - if( SocketChannelIOHelper.batch( conn, conn.channel ) ) { - if( key.isValid() ) - key.interestOps( SelectionKey.OP_READ ); - } - } - } - while ( !iqueue.isEmpty() ) { - conn = iqueue.remove( 0 ); - WrappedByteChannel c = ( (WrappedByteChannel) conn.channel ); - ByteBuffer buf = takeBuffer(); - try { - if( SocketChannelIOHelper.readMore( buf, conn, c ) ) - iqueue.add( conn ); - if( buf.hasRemaining() ) { - conn.inQueue.put( buf ); - queue( conn ); - } else { - pushBuffer( buf ); - } - } catch ( IOException e ) { - pushBuffer( buf ); - throw e; - } - - } - } catch ( CancelledKeyException e ) { - // an other thread may cancel the key - } catch ( ClosedByInterruptException e ) { - return; // do the same stuff as when InterruptedException is thrown - } catch ( IOException ex ) { - if( key != null ) - key.cancel(); - handleIOException( key, conn, ex ); - } catch ( InterruptedException e ) { - return;// FIXME controlled shutdown (e.g. take care of buffermanagement) - } - } - - } catch ( RuntimeException e ) { - // should hopefully never occur - handleFatal( null, e ); - } finally { - if( decoders != null ) { - for( WebSocketWorker w : decoders ) { - w.interrupt(); - } - } - if( server != null ) { - try { - server.close(); - } catch ( IOException e ) { - onError( null, e ); - } - } - } - } - protected void allocateBuffers( WebSocket c ) throws InterruptedException { - if( queuesize.get() >= 2 * decoders.size() + 1 ) { - return; - } - queuesize.incrementAndGet(); - buffers.put( createBuffer() ); - } - - protected void releaseBuffers( WebSocket c ) throws InterruptedException { - // queuesize.decrementAndGet(); - // takeBuffer(); - } - - public ByteBuffer createBuffer() { - return ByteBuffer.allocate( WebSocketImpl.RCVBUF ); - } - - private void queue( WebSocketImpl ws ) throws InterruptedException { - if( ws.workerThread == null ) { - ws.workerThread = decoders.get( queueinvokes % decoders.size() ); - queueinvokes++; - } - ws.workerThread.put( ws ); - } - - private ByteBuffer takeBuffer() throws InterruptedException { - return buffers.take(); - } - - private void pushBuffer( ByteBuffer buf ) throws InterruptedException { - if( buffers.size() > queuesize.intValue() ) - return; - buffers.put( buf ); - } - - private void handleIOException( SelectionKey key, WebSocket conn, IOException ex ) { - // onWebsocketError( conn, ex );// conn may be null here - if( conn != null ) { - conn.closeConnection( CloseFrame.ABNORMAL_CLOSE, ex.getMessage() ); - } else if( key != null ) { - SelectableChannel channel = key.channel(); - if( channel != null && channel.isOpen() ) { // this could be the case if the IOException ex is a SSLException - try { - channel.close(); - } catch ( IOException e ) { - // there is nothing that must be done here - } - if( WebSocketImpl.DEBUG ) - System.out.println( "Connection closed because of" + ex ); - } - } - } - - private void handleFatal( WebSocket conn, Exception e ) { - onError( conn, e ); - try { - stop(); - } catch ( IOException e1 ) { - onError( null, e1 ); - } catch ( InterruptedException e1 ) { - Thread.currentThread().interrupt(); - onError( null, e1 ); - } - } - - /** - * Gets the XML string that should be returned if a client requests a Flash - * security policy. - * - * The default implementation allows access from all remote domains, but - * only on the port that this WebSocketServer is listening on. - * - * This is specifically implemented for gitime's WebSocket client for Flash: - * http://github.com/gimite/web-socket-js - * - * @return An XML String that comforms to Flash's security policy. You MUST - * not include the null char at the end, it is appended automatically. - */ - protected String getFlashSecurityPolicy() { - return ""; - } - - @Override - public final void onWebsocketMessage( WebSocket conn, String message ) { - onMessage( conn, message ); - } - - @Override - @Deprecated - public/*final*/void onWebsocketMessageFragment( WebSocket conn, Framedata frame ) {// onFragment should be overloaded instead - onFragment( conn, frame ); - } - - @Override - public final void onWebsocketMessage( WebSocket conn, ByteBuffer blob ) { - onMessage( conn, blob ); - } - - @Override - public final void onWebsocketOpen( WebSocket conn, Handshakedata handshake ) { - if( addConnection( conn ) ) { - onOpen( conn, (ClientHandshake) handshake ); - } - } - - @Override - public final void onWebsocketClose( WebSocket conn, int code, String reason, boolean remote ) { - selector.wakeup(); - try { - if( removeConnection( conn ) ) { - onClose( conn, code, reason, remote ); - } - } finally { - try { - releaseBuffers( conn ); - } catch ( InterruptedException e ) { - Thread.currentThread().interrupt(); - } - } - - } - - /** - * This method performs remove operations on the connection and therefore also gives control over whether the operation shall be synchronized - *

- * {@link #WebSocketServer(InetSocketAddress, int, List, Collection)} allows to specify a collection which will be used to store current connections in.
- * Depending on the type on the connection, modifications of that collection may have to be synchronized. - **/ - protected boolean removeConnection( WebSocket ws ) { - boolean removed; - synchronized ( connections ) { - removed = this.connections.remove( ws ); - assert ( removed ); - } - if( isclosed.get() && connections.size() == 0 ) { - selectorthread.interrupt(); - } - return removed; - } - @Override - public ServerHandshakeBuilder onWebsocketHandshakeReceivedAsServer( WebSocket conn, Draft draft, ClientHandshake request ) throws InvalidDataException { - return super.onWebsocketHandshakeReceivedAsServer( conn, draft, request ); - } - - /** @see #removeConnection(WebSocket) */ - protected boolean addConnection( WebSocket ws ) { - if( !isclosed.get() ) { - synchronized ( connections ) { - boolean succ = this.connections.add( ws ); - assert ( succ ); - return succ; - } - } else { - // This case will happen when a new connection gets ready while the server is already stopping. - ws.close( CloseFrame.GOING_AWAY ); - return true;// for consistency sake we will make sure that both onOpen will be called - } - } - /** - * @param conn - * may be null if the error does not belong to a single connection - */ - @Override - public final void onWebsocketError( WebSocket conn, Exception ex ) { - onError( conn, ex ); - } - - @Override - public final void onWriteDemand( WebSocket w ) { - WebSocketImpl conn = (WebSocketImpl) w; - try { - conn.key.interestOps( SelectionKey.OP_READ | SelectionKey.OP_WRITE ); - } catch ( CancelledKeyException e ) { - // the thread which cancels key is responsible for possible cleanup - conn.outQueue.clear(); - } - selector.wakeup(); - } - - @Override - public void onWebsocketCloseInitiated( WebSocket conn, int code, String reason ) { - onCloseInitiated( conn, code, reason ); - } - - @Override - public void onWebsocketClosing( WebSocket conn, int code, String reason, boolean remote ) { - onClosing( conn, code, reason, remote ); - - } - - public void onCloseInitiated( WebSocket conn, int code, String reason ) { - } - - public void onClosing( WebSocket conn, int code, String reason, boolean remote ) { - - } - - public final void setWebSocketFactory( WebSocketServerFactory wsf ) { - this.wsf = wsf; - } - - public final WebSocketFactory getWebSocketFactory() { - return wsf; - } - - /** - * Returns whether a new connection shall be accepted or not.
- * Therefore method is well suited to implement some kind of connection limitation.
- * - * @see #onOpen(WebSocket, ClientHandshake) - * @see #onWebsocketHandshakeReceivedAsServer(WebSocket, Draft, ClientHandshake) - **/ - protected boolean onConnect( SelectionKey key ) { - return true; - } - - private Socket getSocket( WebSocket conn ) { - WebSocketImpl impl = (WebSocketImpl) conn; - return ( (SocketChannel) impl.key.channel() ).socket(); - } - - @Override - public InetSocketAddress getLocalSocketAddress( WebSocket conn ) { - return (InetSocketAddress) getSocket( conn ).getLocalSocketAddress(); - } - - @Override - public InetSocketAddress getRemoteSocketAddress( WebSocket conn ) { - return (InetSocketAddress) getSocket( conn ).getRemoteSocketAddress(); - } - - /** Called after an opening handshake has been performed and the given websocket is ready to be written on. */ - public abstract void onOpen( WebSocket conn, ClientHandshake handshake ); - /** - * Called after the websocket connection has been closed. - * - * @param code - * The codes can be looked up here: {@link CloseFrame} - * @param reason - * Additional information string - * @param remote - * Returns whether or not the closing of the connection was initiated by the remote host. - **/ - public abstract void onClose( WebSocket conn, int code, String reason, boolean remote ); - /** - * Callback for string messages received from the remote host - * - * @see #onMessage(WebSocket, ByteBuffer) - **/ - public abstract void onMessage( WebSocket conn, String message ); - /** - * Called when errors occurs. If an error causes the websocket connection to fail {@link #onClose(WebSocket, int, String, boolean)} will be called additionally.
- * This method will be called primarily because of IO or protocol errors.
- * If the given exception is an RuntimeException that probably means that you encountered a bug.
- * - * @param conn - * Can be null if there error does not belong to one specific websocket. For example if the servers port could not be bound. - **/ - public abstract void onError( WebSocket conn, Exception ex ); - /** - * Callback for binary messages received from the remote host - * - * @see #onMessage(WebSocket, String) - **/ - public void onMessage( WebSocket conn, ByteBuffer message ) { - } - - /** - * @see WebSocket#sendFragmentedFrame(org.java_websocket.framing.Framedata.Opcode, ByteBuffer, boolean) - */ - public void onFragment( WebSocket conn, Framedata fragment ) { - } - - public class WebSocketWorker extends Thread { - - private BlockingQueue iqueue; - - public WebSocketWorker() { - iqueue = new LinkedBlockingQueue(); - setName( "WebSocketWorker-" + getId() ); - setUncaughtExceptionHandler( new UncaughtExceptionHandler() { - @Override - public void uncaughtException( Thread t, Throwable e ) { - getDefaultUncaughtExceptionHandler().uncaughtException( t, e ); - } - } ); - } - - public void put( WebSocketImpl ws ) throws InterruptedException { - iqueue.put( ws ); - } - - @Override - public void run() { - WebSocketImpl ws = null; - try { - while ( true ) { - ByteBuffer buf = null; - ws = iqueue.take(); - buf = ws.inQueue.poll(); - assert ( buf != null ); - try { - ws.decode( buf ); - } catch(Exception e){ - System.err.println("Error while reading from remote connection: " + e); - } - - finally { - pushBuffer( buf ); - } - } - } catch ( InterruptedException e ) { - } catch ( RuntimeException e ) { - handleFatal( ws, e ); - } - } - } - - public interface WebSocketServerFactory extends WebSocketFactory { - @Override - public WebSocketImpl createWebSocket( WebSocketAdapter a, Draft d, Socket s ); - - public WebSocketImpl createWebSocket( WebSocketAdapter a, List drafts, Socket s ); - - /** - * Allows to wrap the Socketchannel( key.channel() ) to insert a protocol layer( like ssl or proxy authentication) beyond the ws layer. - * - * @param key - * a SelectionKey of an open SocketChannel. - * @return The channel on which the read and write operations will be performed.
- */ - public ByteChannel wrapChannel( SocketChannel channel, SelectionKey key ) throws IOException; - } -} +package org.java_websocket.server; + +import java.io.IOException; +import java.net.InetSocketAddress; +import java.net.ServerSocket; +import java.net.Socket; +import java.net.UnknownHostException; +import java.nio.ByteBuffer; +import java.nio.channels.ByteChannel; +import java.nio.channels.CancelledKeyException; +import java.nio.channels.ClosedByInterruptException; +import java.nio.channels.SelectableChannel; +import java.nio.channels.SelectionKey; +import java.nio.channels.Selector; +import java.nio.channels.ServerSocketChannel; +import java.nio.channels.SocketChannel; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.HashSet; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.List; +import java.util.Set; +import java.util.concurrent.BlockingQueue; +import java.util.concurrent.CopyOnWriteArraySet; +import java.util.concurrent.LinkedBlockingQueue; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicInteger; + +import org.java_websocket.SocketChannelIOHelper; +import org.java_websocket.WebSocket; +import org.java_websocket.WebSocketAdapter; +import org.java_websocket.WebSocketFactory; +import org.java_websocket.WebSocketImpl; +import org.java_websocket.WrappedByteChannel; +import org.java_websocket.drafts.Draft; +import org.java_websocket.exceptions.InvalidDataException; +import org.java_websocket.framing.CloseFrame; +import org.java_websocket.framing.Framedata; +import org.java_websocket.handshake.ClientHandshake; +import org.java_websocket.handshake.Handshakedata; +import org.java_websocket.handshake.ServerHandshakeBuilder; + +/** + * WebSocketServer is an abstract class that only takes care of the + * HTTP handshake portion of WebSockets. It's up to a subclass to add + * functionality/purpose to the server. + * + */ +public abstract class WebSocketServer extends WebSocketAdapter implements Runnable { + + public static int DECODERS = Runtime.getRuntime().availableProcessors(); + + /** + * Holds the list of active WebSocket connections. "Active" means WebSocket + * handshake is complete and socket can be written to, or read from. + */ + private final Collection connections; + /** + * The port number that this WebSocket server should listen on. Default is + * WebSocket.DEFAULT_PORT. + */ + private final InetSocketAddress address; + /** + * The socket channel for this WebSocket server. + */ + private ServerSocketChannel server; + /** + * The 'Selector' used to get event keys from the underlying socket. + */ + private Selector selector; + /** + * The Draft of the WebSocket protocol the Server is adhering to. + */ + private List drafts; + + private Thread selectorthread; + + private final AtomicBoolean isclosed = new AtomicBoolean( false ); + + private List decoders; + + private List iqueue; + private BlockingQueue buffers; + private int queueinvokes = 0; + private final AtomicInteger queuesize = new AtomicInteger( 0 ); + + private WebSocketServerFactory wsf = new DefaultWebSocketServerFactory(); + + /** + * Creates a WebSocketServer that will attempt to + * listen on port WebSocket.DEFAULT_PORT. + * + * @see #WebSocketServer(InetSocketAddress, int, List, Collection) more details here + */ + public WebSocketServer() throws UnknownHostException { + this( new InetSocketAddress( WebSocket.DEFAULT_PORT ), DECODERS, null ); + } + + /** + * Creates a WebSocketServer that will attempt to bind/listen on the given address. + * + * @see #WebSocketServer(InetSocketAddress, int, List, Collection) more details here + */ + public WebSocketServer( InetSocketAddress address ) { + this( address, DECODERS, null ); + } + + /** + * @see #WebSocketServer(InetSocketAddress, int, List, Collection) more details here + */ + public WebSocketServer( InetSocketAddress address , int decoders ) { + this( address, decoders, null ); + } + + /** + * @see #WebSocketServer(InetSocketAddress, int, List, Collection) more details here + */ + public WebSocketServer( InetSocketAddress address , List drafts ) { + this( address, DECODERS, drafts ); + } + + /** + * @see #WebSocketServer(InetSocketAddress, int, List, Collection) more details here + */ + public WebSocketServer( InetSocketAddress address , int decodercount , List drafts ) { + this( address, decodercount, drafts, new HashSet() ); + } + + /** + * Creates a WebSocketServer that will attempt to bind/listen on the given address, + * and comply with Draft version draft. + * + * @param address + * The address (host:port) this server should listen on. + * @param decodercount + * The number of {@link WebSocketWorker}s that will be used to process the incoming network data. By default this will be Runtime.getRuntime().availableProcessors() + * @param drafts + * The versions of the WebSocket protocol that this server + * instance should comply to. Clients that use an other protocol version will be rejected. + * + * @param connectionscontainer + * Allows to specify a collection that will be used to store the websockets in.
+ * If you plan to often iterate through the currently connected websockets you may want to use a collection that does not require synchronization like a {@link CopyOnWriteArraySet}. In that case make sure that you overload {@link #removeConnection(WebSocket)} and {@link #addConnection(WebSocket)}.
+ * By default a {@link HashSet} will be used. + * + * @see #removeConnection(WebSocket) for more control over syncronized operation + * @see more about drafts + */ + public WebSocketServer( InetSocketAddress address , int decodercount , List drafts , Collection connectionscontainer ) { + if( address == null || decodercount < 1 || connectionscontainer == null ) { + throw new IllegalArgumentException( "address and connectionscontainer must not be null and you need at least 1 decoder" ); + } + + if( drafts == null ) + this.drafts = Collections.emptyList(); + else + this.drafts = drafts; + + this.address = address; + this.connections = connectionscontainer; + + iqueue = new LinkedList(); + + decoders = new ArrayList( decodercount ); + buffers = new LinkedBlockingQueue(); + for( int i = 0 ; i < decodercount ; i++ ) { + WebSocketWorker ex = new WebSocketWorker(); + decoders.add( ex ); + ex.start(); + } + } + + /** + * Starts the server selectorthread that binds to the currently set port number and + * listeners for WebSocket connection requests. Creates a fixed thread pool with the size {@link WebSocketServer#DECODERS}
+ * May only be called once. + * + * Alternatively you can call {@link WebSocketServer#run()} directly. + * + * @throws IllegalStateException + */ + public void start() { + if( selectorthread != null ) + throw new IllegalStateException( getClass().getName() + " can only be started once." ); + new Thread( this ).start(); + } + + /** + * Closes all connected clients sockets, then closes the underlying + * ServerSocketChannel, effectively killing the server socket selectorthread, + * freeing the port the server was bound to and stops all internal workerthreads. + * + * If this method is called before the server is started it will never start. + * + * @param timeout + * Specifies how many milliseconds the overall close handshaking may take altogether before the connections are closed without proper close handshaking.
+ * + * @throws InterruptedException + */ + public void stop( int timeout ) throws InterruptedException { + if( !isclosed.compareAndSet( false, true ) ) { // this also makes sure that no further connections will be added to this.connections + return; + } + + List socketsToClose = null; + + // copy the connections in a list (prevent callback deadlocks) + synchronized ( connections ) { + socketsToClose = new ArrayList( connections ); + } + + for( WebSocket ws : socketsToClose ) { + ws.close( CloseFrame.GOING_AWAY ); + } + + synchronized ( this ) { + if( selectorthread != null && selectorthread != Thread.currentThread() ) { + selector.wakeup(); + selectorthread.interrupt(); + selectorthread.join( timeout ); + } + } + } + public void stop() throws IOException , InterruptedException { + stop( 0 ); + } + + /** + * Returns a WebSocket[] of currently connected clients. + * Its iterators will be failfast and its not judicious + * to modify it. + * + * @return The currently connected clients. + */ + public Collection connections() { + return this.connections; + } + + public InetSocketAddress getAddress() { + return this.address; + } + + /** + * Gets the port number that this server listens on. + * + * @return The port number. + */ + public int getPort() { + int port = getAddress().getPort(); + if( port == 0 && server != null ) { + port = server.socket().getLocalPort(); + } + return port; + } + + public List getDraft() { + return Collections.unmodifiableList( drafts ); + } + + // Runnable IMPLEMENTATION ///////////////////////////////////////////////// + public void run() { + synchronized ( this ) { + if( selectorthread != null ) + throw new IllegalStateException( getClass().getName() + " can only be started once." ); + selectorthread = Thread.currentThread(); + if( isclosed.get() ) { + return; + } + } + selectorthread.setName( "WebsocketSelector" + selectorthread.getId() ); + try { + server = ServerSocketChannel.open(); + server.configureBlocking( false ); + ServerSocket socket = server.socket(); + socket.setReceiveBufferSize( WebSocketImpl.RCVBUF ); + socket.bind( address ); + selector = Selector.open(); + server.register( selector, server.validOps() ); + } catch ( IOException ex ) { + handleFatal( null, ex ); + return; + } + try { + while ( !selectorthread.isInterrupted() ) { + SelectionKey key = null; + WebSocketImpl conn = null; + try { + selector.select(); + Set keys = selector.selectedKeys(); + Iterator i = keys.iterator(); + + while ( i.hasNext() ) { + key = i.next(); + conn = null; + + if( !key.isValid() ) { + // Object o = key.attachment(); + continue; + } + + if( key.isAcceptable() ) { + if( !onConnect( key ) ) { + key.cancel(); + continue; + } + + SocketChannel channel = server.accept(); + if(channel==null){ + continue; + } + channel.configureBlocking( false ); + WebSocketImpl w = wsf.createWebSocket( this, drafts, channel.socket() ); + w.key = channel.register( selector, SelectionKey.OP_READ, w ); + try { + w.channel = wsf.wrapChannel( channel, w.key ); + i.remove(); + allocateBuffers( w ); + continue; + } catch (IOException ex) { + if (w.key != null) + w.key.cancel(); + } + continue; + } + + if( key.isReadable() ) { + conn = (WebSocketImpl) key.attachment(); + ByteBuffer buf = takeBuffer(); + if(conn.channel == null){ + if( key != null ) + key.cancel(); + + handleIOException( key, conn, new IOException() ); + continue; + } + try { + if( SocketChannelIOHelper.read( buf, conn, conn.channel ) ) { + if( buf.hasRemaining() ) { + conn.inQueue.put( buf ); + queue( conn ); + i.remove(); + if( conn.channel instanceof WrappedByteChannel ) { + if( ( (WrappedByteChannel) conn.channel ).isNeedRead() ) { + iqueue.add( conn ); + } + } + } else + pushBuffer( buf ); + } else { + pushBuffer( buf ); + } + } catch ( IOException e ) { + pushBuffer( buf ); + throw e; + } + } + if( key.isWritable() ) { + conn = (WebSocketImpl) key.attachment(); + if( SocketChannelIOHelper.batch( conn, conn.channel ) ) { + if( key.isValid() ) + key.interestOps( SelectionKey.OP_READ ); + } + } + } + while ( !iqueue.isEmpty() ) { + conn = iqueue.remove( 0 ); + WrappedByteChannel c = ( (WrappedByteChannel) conn.channel ); + ByteBuffer buf = takeBuffer(); + try { + if( SocketChannelIOHelper.readMore( buf, conn, c ) ) + iqueue.add( conn ); + if( buf.hasRemaining() ) { + conn.inQueue.put( buf ); + queue( conn ); + } else { + pushBuffer( buf ); + } + } catch ( IOException e ) { + pushBuffer( buf ); + throw e; + } + + } + } catch ( CancelledKeyException e ) { + // an other thread may cancel the key + } catch ( ClosedByInterruptException e ) { + return; // do the same stuff as when InterruptedException is thrown + } catch ( IOException ex ) { + if( key != null ) + key.cancel(); + handleIOException( key, conn, ex ); + } catch ( InterruptedException e ) { + return;// FIXME controlled shutdown (e.g. take care of buffermanagement) + } + } + + } catch ( RuntimeException e ) { + // should hopefully never occur + handleFatal( null, e ); + } finally { + if( decoders != null ) { + for( WebSocketWorker w : decoders ) { + w.interrupt(); + } + } + if( server != null ) { + try { + server.close(); + } catch ( IOException e ) { + onError( null, e ); + } + } + } + } + protected void allocateBuffers( WebSocket c ) throws InterruptedException { + if( queuesize.get() >= 2 * decoders.size() + 1 ) { + return; + } + queuesize.incrementAndGet(); + buffers.put( createBuffer() ); + } + + protected void releaseBuffers( WebSocket c ) throws InterruptedException { + // queuesize.decrementAndGet(); + // takeBuffer(); + } + + public ByteBuffer createBuffer() { + return ByteBuffer.allocate( WebSocketImpl.RCVBUF ); + } + + private void queue( WebSocketImpl ws ) throws InterruptedException { + if( ws.workerThread == null ) { + ws.workerThread = decoders.get( queueinvokes % decoders.size() ); + queueinvokes++; + } + ws.workerThread.put( ws ); + } + + private ByteBuffer takeBuffer() throws InterruptedException { + return buffers.take(); + } + + private void pushBuffer( ByteBuffer buf ) throws InterruptedException { + if( buffers.size() > queuesize.intValue() ) + return; + buffers.put( buf ); + } + + private void handleIOException( SelectionKey key, WebSocket conn, IOException ex ) { + // onWebsocketError( conn, ex );// conn may be null here + if( conn != null ) { + conn.closeConnection( CloseFrame.ABNORMAL_CLOSE, ex.getMessage() ); + } else if( key != null ) { + SelectableChannel channel = key.channel(); + if( channel != null && channel.isOpen() ) { // this could be the case if the IOException ex is a SSLException + try { + channel.close(); + } catch ( IOException e ) { + // there is nothing that must be done here + } + if( WebSocketImpl.DEBUG ) + System.out.println( "Connection closed because of" + ex ); + } + } + } + + private void handleFatal( WebSocket conn, Exception e ) { + onError( conn, e ); + try { + stop(); + } catch ( IOException e1 ) { + onError( null, e1 ); + } catch ( InterruptedException e1 ) { + Thread.currentThread().interrupt(); + onError( null, e1 ); + } + } + + /** + * Gets the XML string that should be returned if a client requests a Flash + * security policy. + * + * The default implementation allows access from all remote domains, but + * only on the port that this WebSocketServer is listening on. + * + * This is specifically implemented for gitime's WebSocket client for Flash: + * http://github.com/gimite/web-socket-js + * + * @return An XML String that comforms to Flash's security policy. You MUST + * not include the null char at the end, it is appended automatically. + */ + protected String getFlashSecurityPolicy() { + return ""; + } + + @Override + public final void onWebsocketMessage( WebSocket conn, String message ) { + onMessage( conn, message ); + } + + @Override + @Deprecated + public/*final*/void onWebsocketMessageFragment( WebSocket conn, Framedata frame ) {// onFragment should be overloaded instead + onFragment( conn, frame ); + } + + @Override + public final void onWebsocketMessage( WebSocket conn, ByteBuffer blob ) { + onMessage( conn, blob ); + } + + @Override + public final void onWebsocketOpen( WebSocket conn, Handshakedata handshake ) { + if( addConnection( conn ) ) { + onOpen( conn, (ClientHandshake) handshake ); + } + } + + @Override + public final void onWebsocketClose( WebSocket conn, int code, String reason, boolean remote ) { + selector.wakeup(); + try { + if( removeConnection( conn ) ) { + onClose( conn, code, reason, remote ); + } + } finally { + try { + releaseBuffers( conn ); + } catch ( InterruptedException e ) { + Thread.currentThread().interrupt(); + } + } + + } + + /** + * This method performs remove operations on the connection and therefore also gives control over whether the operation shall be synchronized + *

+ * {@link #WebSocketServer(InetSocketAddress, int, List, Collection)} allows to specify a collection which will be used to store current connections in.
+ * Depending on the type on the connection, modifications of that collection may have to be synchronized. + **/ + protected boolean removeConnection( WebSocket ws ) { + boolean removed; + synchronized ( connections ) { + removed = this.connections.remove( ws ); + assert ( removed ); + } + if( isclosed.get() && connections.size() == 0 ) { + selectorthread.interrupt(); + } + return removed; + } + @Override + public ServerHandshakeBuilder onWebsocketHandshakeReceivedAsServer( WebSocket conn, Draft draft, ClientHandshake request ) throws InvalidDataException { + return super.onWebsocketHandshakeReceivedAsServer( conn, draft, request ); + } + + /** @see #removeConnection(WebSocket) */ + protected boolean addConnection( WebSocket ws ) { + if( !isclosed.get() ) { + synchronized ( connections ) { + boolean succ = this.connections.add( ws ); + assert ( succ ); + return succ; + } + } else { + // This case will happen when a new connection gets ready while the server is already stopping. + ws.close( CloseFrame.GOING_AWAY ); + return true;// for consistency sake we will make sure that both onOpen will be called + } + } + /** + * @param conn + * may be null if the error does not belong to a single connection + */ + @Override + public final void onWebsocketError( WebSocket conn, Exception ex ) { + onError( conn, ex ); + } + + @Override + public final void onWriteDemand( WebSocket w ) { + WebSocketImpl conn = (WebSocketImpl) w; + try { + conn.key.interestOps( SelectionKey.OP_READ | SelectionKey.OP_WRITE ); + } catch ( CancelledKeyException e ) { + // the thread which cancels key is responsible for possible cleanup + conn.outQueue.clear(); + } + selector.wakeup(); + } + + @Override + public void onWebsocketCloseInitiated( WebSocket conn, int code, String reason ) { + onCloseInitiated( conn, code, reason ); + } + + @Override + public void onWebsocketClosing( WebSocket conn, int code, String reason, boolean remote ) { + onClosing( conn, code, reason, remote ); + + } + + public void onCloseInitiated( WebSocket conn, int code, String reason ) { + } + + public void onClosing( WebSocket conn, int code, String reason, boolean remote ) { + + } + + public final void setWebSocketFactory( WebSocketServerFactory wsf ) { + this.wsf = wsf; + } + + public final WebSocketFactory getWebSocketFactory() { + return wsf; + } + + /** + * Returns whether a new connection shall be accepted or not.
+ * Therefore method is well suited to implement some kind of connection limitation.
+ * + * @see #onOpen(WebSocket, ClientHandshake) + * @see #onWebsocketHandshakeReceivedAsServer(WebSocket, Draft, ClientHandshake) + **/ + protected boolean onConnect( SelectionKey key ) { + return true; + } + + private Socket getSocket( WebSocket conn ) { + WebSocketImpl impl = (WebSocketImpl) conn; + return ( (SocketChannel) impl.key.channel() ).socket(); + } + + @Override + public InetSocketAddress getLocalSocketAddress( WebSocket conn ) { + return (InetSocketAddress) getSocket( conn ).getLocalSocketAddress(); + } + + @Override + public InetSocketAddress getRemoteSocketAddress( WebSocket conn ) { + return (InetSocketAddress) getSocket( conn ).getRemoteSocketAddress(); + } + + /** Called after an opening handshake has been performed and the given websocket is ready to be written on. */ + public abstract void onOpen( WebSocket conn, ClientHandshake handshake ); + /** + * Called after the websocket connection has been closed. + * + * @param code + * The codes can be looked up here: {@link CloseFrame} + * @param reason + * Additional information string + * @param remote + * Returns whether or not the closing of the connection was initiated by the remote host. + **/ + public abstract void onClose( WebSocket conn, int code, String reason, boolean remote ); + /** + * Callback for string messages received from the remote host + * + * @see #onMessage(WebSocket, ByteBuffer) + **/ + public abstract void onMessage( WebSocket conn, String message ); + /** + * Called when errors occurs. If an error causes the websocket connection to fail {@link #onClose(WebSocket, int, String, boolean)} will be called additionally.
+ * This method will be called primarily because of IO or protocol errors.
+ * If the given exception is an RuntimeException that probably means that you encountered a bug.
+ * + * @param conn + * Can be null if there error does not belong to one specific websocket. For example if the servers port could not be bound. + **/ + public abstract void onError( WebSocket conn, Exception ex ); + /** + * Callback for binary messages received from the remote host + * + * @see #onMessage(WebSocket, String) + **/ + public void onMessage( WebSocket conn, ByteBuffer message ) { + } + + /** + * @see WebSocket#sendFragmentedFrame(org.java_websocket.framing.Framedata.Opcode, ByteBuffer, boolean) + */ + public void onFragment( WebSocket conn, Framedata fragment ) { + } + + public class WebSocketWorker extends Thread { + + private BlockingQueue iqueue; + + public WebSocketWorker() { + iqueue = new LinkedBlockingQueue(); + setName( "WebSocketWorker-" + getId() ); + setUncaughtExceptionHandler( new UncaughtExceptionHandler() { + @Override + public void uncaughtException( Thread t, Throwable e ) { + getDefaultUncaughtExceptionHandler().uncaughtException( t, e ); + } + } ); + } + + public void put( WebSocketImpl ws ) throws InterruptedException { + iqueue.put( ws ); + } + + @Override + public void run() { + WebSocketImpl ws = null; + try { + while ( true ) { + ByteBuffer buf = null; + ws = iqueue.take(); + buf = ws.inQueue.poll(); + assert ( buf != null ); + try { + ws.decode( buf ); + } catch(Exception e){ + System.err.println("Error while reading from remote connection: " + e); + } + + finally { + pushBuffer( buf ); + } + } + } catch ( InterruptedException e ) { + } catch ( RuntimeException e ) { + handleFatal( ws, e ); + } + } + } + + public interface WebSocketServerFactory extends WebSocketFactory { + @Override + public WebSocketImpl createWebSocket( WebSocketAdapter a, Draft d, Socket s ); + + public WebSocketImpl createWebSocket( WebSocketAdapter a, List drafts, Socket s ); + + /** + * Allows to wrap the Socketchannel( key.channel() ) to insert a protocol layer( like ssl or proxy authentication) beyond the ws layer. + * + * @param key + * a SelectionKey of an open SocketChannel. + * @return The channel on which the read and write operations will be performed.
+ */ + public ByteChannel wrapChannel( SocketChannel channel, SelectionKey key ) throws IOException; + } +} From 98a8d620590bfed93ebf675e37f5cf9ea2f1d544 Mon Sep 17 00:00:00 2001 From: Sudipan Mishra Date: Thu, 5 Jan 2017 14:14:58 -0500 Subject: [PATCH 5/8] Merged fixes from PR #389 for NPE during port scanning --- src/main/java/org/java_websocket/server/WebSocketServer.java | 1 - 1 file changed, 1 deletion(-) diff --git a/src/main/java/org/java_websocket/server/WebSocketServer.java b/src/main/java/org/java_websocket/server/WebSocketServer.java index 737226922..d9c6fdaf4 100644 --- a/src/main/java/org/java_websocket/server/WebSocketServer.java +++ b/src/main/java/org/java_websocket/server/WebSocketServer.java @@ -294,7 +294,6 @@ public void run() { while ( i.hasNext() ) { conn = null; //Make sure to reset the connection on each iteration to avoid state bugs. key = i.next(); - conn = null; if( !key.isValid() ) { // Object o = key.attachment(); From c90a02d6e349d67fc71f50703e7c253cfc7d4867 Mon Sep 17 00:00:00 2001 From: Sudipan Mishra Date: Thu, 5 Jan 2017 14:17:15 -0500 Subject: [PATCH 6/8] Updated cucumber and junit dependencies to newer versions --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 0316b921c..a962ac63f 100644 --- a/pom.xml +++ b/pom.xml @@ -21,7 +21,7 @@ UTF-8 1.6 - 1.0.0.RC24 + 1.0.2 @@ -74,7 +74,7 @@ info.cukes cucumber-junit - 1.0.0 + 1.0.2 test From e26cbeb86076863c5e8ad1cfa6d46fbf9f45426b Mon Sep 17 00:00:00 2001 From: Sudipan Mishra Date: Thu, 5 Jan 2017 18:01:15 -0500 Subject: [PATCH 7/8] Updated the ignore file. --- .gitignore | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index d7e41aae5..f7c5794dd 100644 --- a/.gitignore +++ b/.gitignore @@ -13,4 +13,6 @@ target .classpath bin -/doc \ No newline at end of file +/doc +/dist/ +/gradle/ From 651e57e0b10f989967fd8be557a81210e93a2132 Mon Sep 17 00:00:00 2001 From: Sudipan Mishra Date: Thu, 5 Jan 2017 19:02:54 -0500 Subject: [PATCH 8/8] Removed dist and gradle folders --- dist/java_websocket.jar | Bin 88934 -> 0 bytes gradle/wrapper/gradle-wrapper.jar | Bin 51348 -> 0 bytes gradle/wrapper/gradle-wrapper.properties | 6 ------ 3 files changed, 6 deletions(-) delete mode 100644 dist/java_websocket.jar delete mode 100644 gradle/wrapper/gradle-wrapper.jar delete mode 100644 gradle/wrapper/gradle-wrapper.properties diff --git a/dist/java_websocket.jar b/dist/java_websocket.jar deleted file mode 100644 index bb5caeb4e80a53b60a83868128ac5fde26d7ac64..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 88934 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

CflOl1i4(-2^1KXo)&I5QlgjRKFQgM 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 z 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 diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties deleted file mode 100644 index 7dfc31e8e..000000000 --- a/gradle/wrapper/gradle-wrapper.properties +++ /dev/null @@ -1,6 +0,0 @@ -#Wed Jul 02 20:43:27 CEST 2014 -distributionBase=GRADLE_USER_HOME -distributionPath=wrapper/dists -zipStoreBase=GRADLE_USER_HOME -zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-1.4-bin.zip