/******************************************************************************* * Copyright (c) 2011 Intalio, Inc. * ====================================================================== * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * and Apache License v2.0 which accompanies this distribution. * * The Eclipse Public License is available at * http://www.eclipse.org/legal/epl-v10.html * * The Apache License v2.0 is available at * http://www.opensource.org/licenses/apache2.0.php * * You may elect to redistribute this code under either of these licenses. *******************************************************************************/ package org.eclipse.jetty.websocket; import java.io.IOException; import java.util.concurrent.ConcurrentLinkedQueue; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; import javax.servlet.http.HttpServletRequest; import org.eclipse.jetty.server.Server; import org.eclipse.jetty.server.handler.ResourceHandler; import org.eclipse.jetty.server.nio.SelectChannelConnector; import org.eclipse.jetty.util.StringUtil; import org.eclipse.jetty.util.TypeUtil; import org.eclipse.jetty.util.log.Log; import org.eclipse.jetty.util.log.Logger; public class TestServer extends Server { private static final Logger LOG = Log.getLogger(TestServer.class); boolean _verbose; WebSocket _websocket; SelectChannelConnector _connector; WebSocketHandler _wsHandler; ResourceHandler _rHandler; ConcurrentLinkedQueue<TestWebSocket> _broadcast = new ConcurrentLinkedQueue<TestWebSocket>(); public TestServer(int port) { _connector = new SelectChannelConnector(); _connector.setPort(port); addConnector(_connector); _wsHandler = new WebSocketHandler() { public WebSocket doWebSocketConnect(HttpServletRequest request, String protocol) { if ("org.ietf.websocket.test-echo".equals(protocol) || "echo".equals(protocol) || "lws-mirror-protocol".equals(protocol)) { _websocket = new TestEchoWebSocket(); } else if ("org.ietf.websocket.test-echo-broadcast".equals(protocol) || "echo-broadcast".equals(protocol)) { _websocket = new TestEchoBroadcastWebSocket(); } else if ("echo-broadcast-ping".equals(protocol)) { _websocket = new TestEchoBroadcastPingWebSocket(); } else if ("org.ietf.websocket.test-echo-assemble".equals(protocol) || "echo-assemble".equals(protocol)) { _websocket = new TestEchoAssembleWebSocket(); } else if ("org.ietf.websocket.test-echo-fragment".equals(protocol) || "echo-fragment".equals(protocol)) { _websocket = new TestEchoFragmentWebSocket(); } else if (protocol==null) { _websocket = new TestWebSocket(); } return _websocket; } }; setHandler(_wsHandler); _rHandler=new ResourceHandler(); _rHandler.setDirectoriesListed(true); _rHandler.setResourceBase("src/test/webapp"); _wsHandler.setHandler(_rHandler); } /* ------------------------------------------------------------ */ public boolean isVerbose() { return _verbose; } /* ------------------------------------------------------------ */ public void setVerbose(boolean verbose) { _verbose = verbose; } /* ------------------------------------------------------------ */ public void setResourceBase(String dir) { _rHandler.setResourceBase(dir); } /* ------------------------------------------------------------ */ public String getResourceBase() { return _rHandler.getResourceBase(); } /* ------------------------------------------------------------ */ /* ------------------------------------------------------------ */ class TestWebSocket implements WebSocket, WebSocket.OnFrame, WebSocket.OnBinaryMessage, WebSocket.OnTextMessage, WebSocket.OnControl { protected FrameConnection _connection; public FrameConnection getConnection() { return _connection; } public void onOpen(Connection connection) { if (_verbose) System.err.printf("%s#onOpen %s %s\n",this.getClass().getSimpleName(),connection,connection.getProtocol()); } public void onHandshake(FrameConnection connection) { if (_verbose) System.err.printf("%s#onHandshake %s %s\n",this.getClass().getSimpleName(),connection,connection.getClass().getSimpleName()); _connection = connection; } public void onClose(int code,String message) { if (_verbose) System.err.printf("%s#onDisonnect %d %s\n",this.getClass().getSimpleName(),code,message); } public boolean onFrame(byte flags, byte opcode, byte[] data, int offset, int length) { if (_verbose) System.err.printf("%s#onFrame %s|%s %s\n",this.getClass().getSimpleName(),TypeUtil.toHexString(flags),TypeUtil.toHexString(opcode),TypeUtil.toHexString(data,offset,length)); return false; } public boolean onControl(byte controlCode, byte[] data, int offset, int length) { if (_verbose) System.err.printf("%s#onControl %s %s\n",this.getClass().getSimpleName(),TypeUtil.toHexString(controlCode),TypeUtil.toHexString(data,offset,length)); return false; } public void onMessage(String data) { if (_verbose) System.err.printf("%s#onMessage %s\n",this.getClass().getSimpleName(),data); } public void onMessage(byte[] data, int offset, int length) { if (_verbose) System.err.printf("%s#onMessage %s\n",this.getClass().getSimpleName(),TypeUtil.toHexString(data,offset,length)); } } /* ------------------------------------------------------------ */ /* ------------------------------------------------------------ */ class TestEchoWebSocket extends TestWebSocket { @Override public void onOpen(Connection connection) { super.onOpen(connection); connection.setMaxTextMessageSize(-1); connection.setMaxBinaryMessageSize(-1); } @Override public boolean onFrame(byte flags, byte opcode, byte[] data, int offset, int length) { super.onFrame(flags,opcode,data,offset,length); try { if (!getConnection().isControl(opcode)) getConnection().sendFrame(flags,opcode,data,offset,length); } catch (IOException e) { e.printStackTrace(); } return false; } } /* ------------------------------------------------------------ */ /* ------------------------------------------------------------ */ class TestEchoBroadcastPingWebSocket extends TestEchoBroadcastWebSocket { Thread _keepAlive; // A dedicated thread is not a good way to do this CountDownLatch _latch = new CountDownLatch(1); @Override public void onHandshake(final FrameConnection connection) { super.onHandshake(connection); _keepAlive=new Thread() { @Override public void run() { try { while(!_latch.await(10,TimeUnit.SECONDS)) { System.err.println("Ping "+connection); byte[] data = { (byte)1, (byte) 2, (byte) 3 }; connection.sendControl(WebSocketConnectionRFC6455.OP_PING,data,0,data.length); } } catch (Exception e) { e.printStackTrace(); } } }; _keepAlive.start(); } @Override public boolean onControl(byte controlCode, byte[] data, int offset, int length) { if (controlCode==WebSocketConnectionRFC6455.OP_PONG) System.err.println("Pong "+getConnection()); return super.onControl(controlCode,data,offset,length); } @Override public void onClose(int code, String message) { _latch.countDown(); super.onClose(code,message); } } /* ------------------------------------------------------------ */ /* ------------------------------------------------------------ */ class TestEchoBroadcastWebSocket extends TestWebSocket { @Override public void onOpen(Connection connection) { super.onOpen(connection); _broadcast.add(this); } @Override public void onClose(int code,String message) { super.onClose(code,message); _broadcast.remove(this); } @Override public void onMessage(byte[] data, int offset, int length) { super.onMessage(data,offset,length); for (TestWebSocket ws : _broadcast) { try { ws.getConnection().sendMessage(data,offset,length); } catch (IOException e) { _broadcast.remove(ws); e.printStackTrace(); } } } @Override public void onMessage(final String data) { super.onMessage(data); for (TestWebSocket ws : _broadcast) { try { ws.getConnection().sendMessage(data); } catch (IOException e) { _broadcast.remove(ws); e.printStackTrace(); } } } } /* ------------------------------------------------------------ */ /* ------------------------------------------------------------ */ class TestEchoAssembleWebSocket extends TestWebSocket { @Override public void onOpen(Connection connection) { super.onOpen(connection); connection.setMaxTextMessageSize(64*1024); connection.setMaxBinaryMessageSize(64*1024); } @Override public void onMessage(byte[] data, int offset, int length) { super.onMessage(data,offset,length); try { getConnection().sendMessage(data,offset,length); } catch (IOException e) { e.printStackTrace(); } } @Override public void onMessage(final String data) { super.onMessage(data); try { getConnection().sendMessage(data); } catch (IOException e) { e.printStackTrace(); } } } /* ------------------------------------------------------------ */ /* ------------------------------------------------------------ */ class TestEchoFragmentWebSocket extends TestWebSocket { @Override public void onOpen(Connection connection) { super.onOpen(connection); connection.setMaxTextMessageSize(64*1024); connection.setMaxBinaryMessageSize(64*1024); } @Override public void onMessage(byte[] data, int offset, int length) { super.onMessage(data,offset,length); try { getConnection().sendFrame((byte)0x0,getConnection().binaryOpcode(),data,offset,length/2); getConnection().sendFrame((byte)0x8,getConnection().binaryOpcode(),data,offset+length/2,length-length/2); } catch (IOException e) { e.printStackTrace(); } } @Override public void onMessage(final String message) { super.onMessage(message); try { byte[] data = message.getBytes(StringUtil.__UTF8); int offset=0; int length=data.length; getConnection().sendFrame((byte)0x0,getConnection().textOpcode(),data,offset,length/2); getConnection().sendFrame((byte)0x8,getConnection().textOpcode(),data,offset+length/2,length-length/2); } catch (IOException e) { e.printStackTrace(); } } } private static void usage() { System.err.println("java -cp CLASSPATH "+TestServer.class+" [ OPTIONS ]"); System.err.println(" -p|--port PORT (default 8080)"); System.err.println(" -v|--verbose "); System.err.println(" -d|--docroot file (default 'src/test/webapp')"); System.exit(1); } public static void main(String... args) { try { int port=8080; boolean verbose=false; String docroot="src/test/webapp"; for (int i=0;i<args.length;i++) { String a=args[i]; if ("-p".equals(a)||"--port".equals(a)) port=Integer.parseInt(args[++i]); else if ("-v".equals(a)||"--verbose".equals(a)) verbose=true; else if ("-d".equals(a)||"--docroot".equals(a)) docroot=args[++i]; else if (a.startsWith("-")) usage(); } TestServer server = new TestServer(port); server.setVerbose(verbose); server.setResourceBase(docroot); server.start(); server.join(); } catch (Exception e) { LOG.warn(e); } } }