diff --git a/example-jetty-embedded/pom.xml b/example-jetty-embedded/pom.xml index e8ead3eb08e..f90da7f4eeb 100644 --- a/example-jetty-embedded/pom.xml +++ b/example-jetty-embedded/pom.xml @@ -38,5 +38,10 @@ jetty-jmx ${project.version} + + org.eclipse.jetty + jetty-websocket + ${project.version} + diff --git a/jetty-distribution/pom.xml b/jetty-distribution/pom.xml index 2eb2273acdb..57fa08fc8a3 100644 --- a/jetty-distribution/pom.xml +++ b/jetty-distribution/pom.xml @@ -377,6 +377,15 @@ ${assembly.directory} start.jar + + org.eclipse.jetty + jetty-websocket + ${project.version} + jar + true + ** + ${assembly.directory}/lib + @@ -500,5 +509,10 @@ jetty-policy ${project.version} + + org.eclipse.jetty + jetty-websocket + ${project.version} + diff --git a/jetty-distribution/src/main/resources/start.ini b/jetty-distribution/src/main/resources/start.ini index 904f8ae8404..4c9a5a5836e 100644 --- a/jetty-distribution/src/main/resources/start.ini +++ b/jetty-distribution/src/main/resources/start.ini @@ -14,6 +14,8 @@ # #=========================================================== +OPTIONS=Server,jmx,resources,websocket + #=========================================================== # The following is an example of the ini args to run the # server with a heap size, JMX remote enabled, JMX mbeans diff --git a/jetty-http/src/main/java/org/eclipse/jetty/http/HttpGenerator.java b/jetty-http/src/main/java/org/eclipse/jetty/http/HttpGenerator.java index 45b965f50ed..14210297c04 100644 --- a/jetty-http/src/main/java/org/eclipse/jetty/http/HttpGenerator.java +++ b/jetty-http/src/main/java/org/eclipse/jetty/http/HttpGenerator.java @@ -485,6 +485,7 @@ public class HttpGenerator extends AbstractGenerator HttpFields.Field field = fields.getField(f); if (field==null) continue; + switch (field.getNameOrdinal()) { case HttpHeaders.CONTENT_LENGTH_ORDINAL: @@ -506,7 +507,8 @@ public class HttpGenerator extends AbstractGenerator break; case HttpHeaders.TRANSFER_ENCODING_ORDINAL: - if (_version == HttpVersions.HTTP_1_1_ORDINAL) transfer_encoding = field; + if (_version == HttpVersions.HTTP_1_1_ORDINAL) + transfer_encoding = field; // Do NOT add yet! break; @@ -566,6 +568,15 @@ public class HttpGenerator extends AbstractGenerator break; } + case HttpHeaderValues.UPGRADE_ORDINAL: + { + // special case for websocket connection ordering + if (_method==null) + { + field.put(_header); + continue; + } + } case HttpHeaderValues.CLOSE_ORDINAL: { close=true; diff --git a/jetty-http/src/main/java/org/eclipse/jetty/http/HttpHeaderValues.java b/jetty-http/src/main/java/org/eclipse/jetty/http/HttpHeaderValues.java index 1c500b0b548..ba228911082 100644 --- a/jetty-http/src/main/java/org/eclipse/jetty/http/HttpHeaderValues.java +++ b/jetty-http/src/main/java/org/eclipse/jetty/http/HttpHeaderValues.java @@ -42,7 +42,8 @@ public class HttpHeaderValues extends BufferCache PROCESSING="102-processing", TE="TE", BYTES="bytes", - NO_CACHE="no-cache"; + NO_CACHE="no-cache", + UPGRADE="Upgrade"; public final static int CLOSE_ORDINAL=1, @@ -54,7 +55,8 @@ public class HttpHeaderValues extends BufferCache PROCESSING_ORDINAL=7, TE_ORDINAL=8, BYTES_ORDINAL=9, - NO_CACHE_ORDINAL=10; + NO_CACHE_ORDINAL=10, + UPGRADE_ORDINAL=11; public final static HttpHeaderValues CACHE= new HttpHeaderValues(); @@ -68,7 +70,8 @@ public class HttpHeaderValues extends BufferCache PROCESSING_BUFFER=CACHE.add(PROCESSING, PROCESSING_ORDINAL), TE_BUFFER=CACHE.add(TE,TE_ORDINAL), BYTES_BUFFER=CACHE.add(BYTES,BYTES_ORDINAL), - NO_CACHE_BUFFER=CACHE.add(NO_CACHE,NO_CACHE_ORDINAL); + NO_CACHE_BUFFER=CACHE.add(NO_CACHE,NO_CACHE_ORDINAL), + UPGRADE_BUFFER=CACHE.add(UPGRADE,UPGRADE_ORDINAL); static { diff --git a/jetty-io/src/main/java/org/eclipse/jetty/io/nio/SelectChannelEndPoint.java b/jetty-io/src/main/java/org/eclipse/jetty/io/nio/SelectChannelEndPoint.java index ab0a8b20ee2..fd11d780a1f 100644 --- a/jetty-io/src/main/java/org/eclipse/jetty/io/nio/SelectChannelEndPoint.java +++ b/jetty-io/src/main/java/org/eclipse/jetty/io/nio/SelectChannelEndPoint.java @@ -32,9 +32,6 @@ import org.eclipse.jetty.util.thread.Timeout; /* ------------------------------------------------------------ */ /** * An Endpoint that can be scheduled by {@link SelectorManager}. - * - * - * */ public class SelectChannelEndPoint extends ChannelEndPoint implements Runnable, AsyncEndPoint, ConnectedEndPoint { diff --git a/jetty-jmx/src/main/java/org/eclipse/jetty/jmx/ObjectMBean.java b/jetty-jmx/src/main/java/org/eclipse/jetty/jmx/ObjectMBean.java index 59526b76791..6cba39de597 100644 --- a/jetty-jmx/src/main/java/org/eclipse/jetty/jmx/ObjectMBean.java +++ b/jetty-jmx/src/main/java/org/eclipse/jetty/jmx/ObjectMBean.java @@ -648,7 +648,7 @@ public class ObjectMBean implements DynamicMBean } catch (Exception e) { - Log.warn(Log.EXCEPTION, e); + Log.warn(name+": "+metaData, e); throw new IllegalArgumentException(e.toString()); } } diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/Server.java b/jetty-server/src/main/java/org/eclipse/jetty/server/Server.java index e62fb0baa88..14cf632ea31 100644 --- a/jetty-server/src/main/java/org/eclipse/jetty/server/Server.java +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/Server.java @@ -61,7 +61,7 @@ public class Server extends HandlerWrapper implements Attributes if (Server.class.getPackage()!=null && Server.class.getPackage().getImplementationVersion()!=null) _version=Server.class.getPackage().getImplementationVersion(); else - _version=System.getProperty("jetty.version","7.0.y.z-SNAPSHOT"); + _version=System.getProperty("jetty.version","7.0.2-SNAPSHOT"); } private final Container _container=new Container(); private final AttributesMap _attributes = new AttributesMap(); diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/nio/SelectChannelConnector.java b/jetty-server/src/main/java/org/eclipse/jetty/server/nio/SelectChannelConnector.java index 1f1e0dcd19a..69f7d555ae8 100644 --- a/jetty-server/src/main/java/org/eclipse/jetty/server/nio/SelectChannelConnector.java +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/nio/SelectChannelConnector.java @@ -63,8 +63,8 @@ import org.eclipse.jetty.util.thread.Timeout.Task; public class SelectChannelConnector extends AbstractNIOConnector { protected ServerSocketChannel _acceptChannel; - private long _lowResourcesConnections; - private long _lowResourcesMaxIdleTime; + private int _lowResourcesConnections; + private int _lowResourcesMaxIdleTime; private final SelectorManager _manager = new SelectorManager() { @@ -226,7 +226,7 @@ public class SelectChannelConnector extends AbstractNIOConnector /** * @return the lowResourcesConnections */ - public long getLowResourcesConnections() + public int getLowResourcesConnections() { return _lowResourcesConnections; } @@ -236,9 +236,9 @@ public class SelectChannelConnector extends AbstractNIOConnector * Set the number of connections, which if exceeded places this manager in low resources state. * This is not an exact measure as the connection count is averaged over the select sets. * @param lowResourcesConnections the number of connections - * @see {@link #setLowResourcesMaxIdleTime(long)} + * @see {@link #setLowResourcesMaxIdleTime(int)} */ - public void setLowResourcesConnections(long lowResourcesConnections) + public void setLowResourcesConnections(int lowResourcesConnections) { _lowResourcesConnections=lowResourcesConnections; } @@ -247,27 +247,11 @@ public class SelectChannelConnector extends AbstractNIOConnector /** * @return the lowResourcesMaxIdleTime */ - public long getLowResourcesMaxIdleTime() + public int getLowResourcesMaxIdleTime() { return _lowResourcesMaxIdleTime; } - /* ------------------------------------------------------------ */ - /** - * Set the period in ms that a connection is allowed to be idle when this there are more - * than {@link #getLowResourcesConnections()} connections. This allows the server to rapidly close idle connections - * in order to gracefully handle high load situations. - * @param lowResourcesMaxIdleTime the period in ms that a connection is allowed to be idle when resources are low. - * @see {@link #setMaxIdleTime(long)} - * @deprecated use {@link #setLowResourceMaxIdleTime(int)} - */ - @Deprecated - public void setLowResourcesMaxIdleTime(long lowResourcesMaxIdleTime) - { - _lowResourcesMaxIdleTime=lowResourcesMaxIdleTime; - super.setLowResourceMaxIdleTime((int)lowResourcesMaxIdleTime); // TODO fix the name duplications - } - /* ------------------------------------------------------------ */ /** * Set the period in ms that a connection is allowed to be idle when this there are more diff --git a/jetty-start/src/main/resources/org/eclipse/jetty/start/start.config b/jetty-start/src/main/resources/org/eclipse/jetty/start/start.config index 092be487c5c..2d659f5464b 100644 --- a/jetty-start/src/main/resources/org/eclipse/jetty/start/start.config +++ b/jetty-start/src/main/resources/org/eclipse/jetty/start/start.config @@ -143,6 +143,9 @@ $(jetty.home)/lib/policy/jetty.policy $(jetty.home)/lib/jetty-http-$(version).jar ! available org.eclipse.jetty.http.HttpParser $(jetty.home)/lib/jetty-client-$(version).jar ! available org.eclipse.jetty.client.HttpClient +[All,websocket] +$(jetty.home)/lib/jetty-websocket-$(version).jar ! available org.eclipse.jetty.websocket.WebSocket + # Add ext if it exists [All,default,=$(jetty.home)/lib/ext] diff --git a/jetty-util/src/main/java/org/eclipse/jetty/util/StringMap.java b/jetty-util/src/main/java/org/eclipse/jetty/util/StringMap.java index c76bfced9b2..ca923ea878c 100644 --- a/jetty-util/src/main/java/org/eclipse/jetty/util/StringMap.java +++ b/jetty-util/src/main/java/org/eclipse/jetty/util/StringMap.java @@ -46,8 +46,8 @@ public class StringMap extends AbstractMap implements Externalizable protected boolean _ignoreCase=false; protected NullEntry _nullEntry=null; protected Object _nullValue=null; - protected HashSet _entrySet=new HashSet(3); - protected Set _umEntrySet=Collections.unmodifiableSet(_entrySet); + protected HashSet _entrySet=new HashSet(3); + protected Set _umEntrySet=Collections.unmodifiableSet(_entrySet); /* ------------------------------------------------------------ */ /** Constructor. @@ -135,6 +135,9 @@ public class StringMap extends AbstractMap implements Externalizable return oldValue; } + if (_ignoreCase) + key=key.toUpperCase(); + Node node = _root; int ni=-1; Node prev = null; diff --git a/jetty-websocket/src/main/java/org/eclipse/jetty/websocket/WebSocket.java b/jetty-websocket/src/main/java/org/eclipse/jetty/websocket/WebSocket.java index eabeb74b8f9..525dddf39b0 100644 --- a/jetty-websocket/src/main/java/org/eclipse/jetty/websocket/WebSocket.java +++ b/jetty-websocket/src/main/java/org/eclipse/jetty/websocket/WebSocket.java @@ -16,7 +16,7 @@ public interface WebSocket void sendMessage(byte frame,String data) throws IOException; void sendMessage(byte frame,byte[] data) throws IOException; void sendMessage(byte frame,byte[] data, int offset, int length) throws IOException; - void disconnect() throws IOException; + void disconnect(); boolean isOpen(); } } diff --git a/jetty-websocket/src/main/java/org/eclipse/jetty/websocket/WebSocketBuffers.java b/jetty-websocket/src/main/java/org/eclipse/jetty/websocket/WebSocketBuffers.java index 729ec83bb39..fbfc94dbdd9 100644 --- a/jetty-websocket/src/main/java/org/eclipse/jetty/websocket/WebSocketBuffers.java +++ b/jetty-websocket/src/main/java/org/eclipse/jetty/websocket/WebSocketBuffers.java @@ -4,6 +4,7 @@ import org.eclipse.jetty.io.Buffer; import org.eclipse.jetty.io.ByteArrayBuffer; import org.eclipse.jetty.io.ThreadLocalBuffers; import org.eclipse.jetty.io.nio.DirectNIOBuffer; +import org.eclipse.jetty.io.nio.IndirectNIOBuffer; /* ------------------------------------------------------------ */ @@ -31,7 +32,7 @@ public class WebSocketBuffers @Override protected Buffer newBuffer(int size) { - return new ByteArrayBuffer(bufferSize); + return new IndirectNIOBuffer(bufferSize); } @Override diff --git a/jetty-websocket/src/main/java/org/eclipse/jetty/websocket/WebSocketConnection.java b/jetty-websocket/src/main/java/org/eclipse/jetty/websocket/WebSocketConnection.java index 0696b9edce0..3b51c57f015 100644 --- a/jetty-websocket/src/main/java/org/eclipse/jetty/websocket/WebSocketConnection.java +++ b/jetty-websocket/src/main/java/org/eclipse/jetty/websocket/WebSocketConnection.java @@ -5,10 +5,12 @@ import java.io.IOException; import org.eclipse.jetty.io.Buffer; import org.eclipse.jetty.io.Connection; import org.eclipse.jetty.io.EndPoint; +import org.eclipse.jetty.server.Connector; import org.eclipse.jetty.util.log.Log; public class WebSocketConnection implements Connection, WebSocket.Outbound { + final Connector _connector; final EndPoint _endp; final WebSocketParser _parser; final WebSocketGenerator _generator; @@ -16,8 +18,9 @@ public class WebSocketConnection implements Connection, WebSocket.Outbound final WebSocket _websocket; final int _maxIdleTimeMs=30000; - public WebSocketConnection(WebSocketBuffers buffers, EndPoint endpoint, long timestamp, WebSocket websocket) + public WebSocketConnection(Connector connector, WebSocketBuffers buffers, EndPoint endpoint, long timestamp, WebSocket websocket) { + _connector=connector; _endp = endpoint; _timestamp = timestamp; _websocket = websocket; @@ -72,21 +75,28 @@ public class WebSocketConnection implements Connection, WebSocket.Outbound int filled=_parser.parseNext(); more = flushed>0 || filled>0 || !_parser.isBufferEmpty() || !_generator.isBufferEmpty(); - if (filled<0 || flushed<0) - _endp.close(); - // System.err.println("flushed="+flushed+" filled="+filled+" more="+more+" endp="+_endp.isOpen()); + // System.err.println("flushed="+flushed+" filled="+filled+" more="+more+" p.e="+_parser.isBufferEmpty()+" g.e="+_generator.isBufferEmpty()); + + if (filled<0 || flushed<0) + { + _endp.close(); + break; + } + } } catch(IOException e) { - System.err.println(e); + e.printStackTrace(); throw e; } finally { - // TODO - not really the best way - if (!_endp.isOpen()) + if (_endp.isOpen()) + _connector.persist(_endp); + else + // TODO - not really the best way _websocket.onDisconnect(); } } @@ -115,24 +125,34 @@ public class WebSocketConnection implements Connection, WebSocket.Outbound { _generator.addFrame(frame,content,_maxIdleTimeMs); _generator.flush(); + _connector.persist(_endp); } public void sendMessage(byte frame, byte[] content) throws IOException { _generator.addFrame(frame,content,_maxIdleTimeMs); _generator.flush(); + _connector.persist(_endp); } public void sendMessage(byte frame, byte[] content, int offset, int length) throws IOException { _generator.addFrame(frame,content,offset,length,_maxIdleTimeMs); _generator.flush(); + _connector.persist(_endp); } - public void disconnect() throws IOException + public void disconnect() { - _generator.flush(_maxIdleTimeMs); - _endp.close(); + try + { + _generator.flush(_maxIdleTimeMs); + _endp.close(); + } + catch(IOException e) + { + Log.ignore(e); + } } public void fill(Buffer buffer) diff --git a/jetty-websocket/src/main/java/org/eclipse/jetty/websocket/WebSocketHandler.java b/jetty-websocket/src/main/java/org/eclipse/jetty/websocket/WebSocketHandler.java index 77961b9adfe..e621d7118ff 100644 --- a/jetty-websocket/src/main/java/org/eclipse/jetty/websocket/WebSocketHandler.java +++ b/jetty-websocket/src/main/java/org/eclipse/jetty/websocket/WebSocketHandler.java @@ -73,12 +73,17 @@ public abstract class WebSocketHandler extends HandlerWrapper { HttpConnection http = HttpConnection.getCurrentConnection(); ConnectedEndPoint endp = (ConnectedEndPoint)http.getEndPoint(); - WebSocketConnection connection = new WebSocketConnection(_buffers,endp,http.getTimeStamp(),websocket); + WebSocketConnection connection = new WebSocketConnection(http.getConnector(),_buffers,endp,http.getTimeStamp(),websocket); + String uri=request.getRequestURI(); + String host=request.getHeader("Host"); + String origin=request.getHeader("Origin"); + origin=checkOrigin(request,host,origin); + response.setHeader("Upgrade","WebSocket"); response.addHeader("Connection","Upgrade"); - response.addHeader("WebSocket-Origin",request.getScheme()+"://"+request.getServerName()); - response.addHeader("WebSocket-Location","ws://"+request.getHeader("Host")+request.getRequestURI()); + response.addHeader("WebSocket-Origin",origin); + response.addHeader("WebSocket-Location","ws://"+host+uri); if (protocol!=null) response.addHeader("WebSocket-Protocol",protocol); response.sendError(101,"Web Socket Protocol Handshake"); @@ -101,6 +106,13 @@ public abstract class WebSocketHandler extends HandlerWrapper } } + protected String checkOrigin(HttpServletRequest request, String host, String origin) + { + if (origin==null) + origin=host; + return origin; + } + abstract protected WebSocket doWebSocketConnect(HttpServletRequest request,String protocol); } diff --git a/jetty-websocket/src/main/java/org/eclipse/jetty/websocket/WebSocketParser.java b/jetty-websocket/src/main/java/org/eclipse/jetty/websocket/WebSocketParser.java index d118fcc207c..a463b507dc7 100644 --- a/jetty-websocket/src/main/java/org/eclipse/jetty/websocket/WebSocketParser.java +++ b/jetty-websocket/src/main/java/org/eclipse/jetty/websocket/WebSocketParser.java @@ -73,6 +73,7 @@ public class WebSocketParser int total_filled=0; // Loop until an datagram call back or can't fill anymore + boolean progress=true; while(true) { int length=_buffer.length(); @@ -92,7 +93,7 @@ public class WebSocketParser { int filled=_endp.isOpen()?_endp.fill(_buffer):-1; if (filled<=0) - return total_filled>0?total_filled:-1; + return total_filled; total_filled+=filled; length=_buffer.length(); } diff --git a/jetty-websocket/src/main/java/org/eclipse/jetty/websocket/WebSocketServlet.java b/jetty-websocket/src/main/java/org/eclipse/jetty/websocket/WebSocketServlet.java index 3d774403675..070453aa756 100644 --- a/jetty-websocket/src/main/java/org/eclipse/jetty/websocket/WebSocketServlet.java +++ b/jetty-websocket/src/main/java/org/eclipse/jetty/websocket/WebSocketServlet.java @@ -15,19 +15,19 @@ import org.eclipse.jetty.server.HttpConnection; /* ------------------------------------------------------------ */ /** - * Servlet to ugrade connections to WebSocket + * Servlet to upgrade connections to WebSocket *

* The request must have the correct upgrade headers, else it is * handled as a normal servlet request. *

* The initParameter "bufferSize" can be used to set the buffer size, * which is also the max frame byte size (default 8192). + * */ public abstract class WebSocketServlet extends HttpServlet { WebSocketBuffers _buffers; - /* ------------------------------------------------------------ */ /** * @see javax.servlet.GenericServlet#init() @@ -56,12 +56,17 @@ public abstract class WebSocketServlet extends HttpServlet { HttpConnection http = HttpConnection.getCurrentConnection(); ConnectedEndPoint endp = (ConnectedEndPoint)http.getEndPoint(); - WebSocketConnection connection = new WebSocketConnection(_buffers,endp,http.getTimeStamp(),websocket); + WebSocketConnection connection = new WebSocketConnection(http.getConnector(),_buffers,endp,http.getTimeStamp(),websocket); + String uri=request.getRequestURI(); + String host=request.getHeader("Host"); + String origin=request.getHeader("Origin"); + origin=checkOrigin(request,host,origin); + response.setHeader("Upgrade","WebSocket"); response.addHeader("Connection","Upgrade"); - response.addHeader("WebSocket-Origin",request.getScheme()+"://"+request.getServerName()); - response.addHeader("WebSocket-Location","ws://"+request.getHeader("Host")+request.getRequestURI()); + response.addHeader("WebSocket-Origin",origin); + response.addHeader("WebSocket-Location","ws://"+host+uri); if (protocol!=null) response.addHeader("WebSocket-Protocol",protocol); response.sendError(101,"Web Socket Protocol Handshake"); @@ -81,6 +86,13 @@ public abstract class WebSocketServlet extends HttpServlet else super.service(request,response); } + + protected String checkOrigin(HttpServletRequest request, String host, String origin) + { + if (origin==null) + origin=host; + return origin; + } abstract protected WebSocket doWebSocketConnect(HttpServletRequest request,String protocol); diff --git a/jetty-websocket/src/test/java/org/eclipse/jetty/websocket/SimpleWebSocketServer.java b/jetty-websocket/src/test/java/org/eclipse/jetty/websocket/SimpleWebSocketServer.java deleted file mode 100644 index 017c3c79588..00000000000 --- a/jetty-websocket/src/test/java/org/eclipse/jetty/websocket/SimpleWebSocketServer.java +++ /dev/null @@ -1,110 +0,0 @@ -package org.eclipse.jetty.websocket; - -import java.io.IOException; - -import javax.servlet.http.HttpServletRequest; - -import org.eclipse.jetty.server.LocalConnector; -import org.eclipse.jetty.server.Server; -import org.eclipse.jetty.server.handler.DefaultHandler; -import org.eclipse.jetty.server.handler.ResourceHandler; -import org.eclipse.jetty.server.nio.SelectChannelConnector; -import org.eclipse.jetty.util.TypeUtil; -import org.eclipse.jetty.util.log.Log; -import org.eclipse.jetty.websocket.WebSocketTest.TestWebSocket; - -public class SimpleWebSocketServer extends Server -{ - TestWebSocket _websocket; - SelectChannelConnector _connector; - WebSocketHandler _handler; - - public SimpleWebSocketServer() - { - _connector = new SelectChannelConnector(); - _connector.setPort(8080); - - addConnector(_connector); - _handler= new WebSocketHandler() - { - @Override - protected WebSocket doWebSocketConnect(HttpServletRequest request, String protocol) - { - _websocket = new TestWebSocket(); - return _websocket; - } - }; - ResourceHandler rh=new ResourceHandler(); - _handler.setHandler(rh); - rh.setDirectoriesListed(true); - rh.setResourceBase("./src/test/resources"); - - setHandler(_handler); - } - - /* ------------------------------------------------------------ */ - /* ------------------------------------------------------------ */ - class TestWebSocket implements WebSocket - { - Outbound _outbound; - - public void onConnect(Outbound outbound) - { - System.err.println("onConnect"); - _outbound=outbound; - - new Thread() - { - public void run() - { - for (int i=0;_outbound.isOpen()&& i<10;i++) - { - try - { - Thread.sleep(1000); - System.err.println("send "+i); - _outbound.sendMessage(SENTINEL_FRAME,"Roger That "+i); - } - catch (Exception e) - { - Log.warn(e); - } - } - } - }.start(); - } - - public void onMessage(byte frame, byte[] data,int offset, int length) - { - System.err.println("onMessage: "+TypeUtil.toHexString(data,offset,length)); - } - - public void onMessage(byte frame, String data) - { - System.err.println("onMessage: "+data); - } - - public void onDisconnect() - { - System.err.println("onDisconnect"); - } - } - - - public static void main(String[] args) - { - try - { - SimpleWebSocketServer server = new SimpleWebSocketServer(); - server.start(); - server.join(); - } - catch(Exception e) - { - Log.warn(e); - } - } - - - -} diff --git a/jetty-websocket/src/test/resources/index.html b/jetty-websocket/src/test/resources/index.html deleted file mode 100644 index 1a15f24e18e..00000000000 --- a/jetty-websocket/src/test/resources/index.html +++ /dev/null @@ -1,17 +0,0 @@ -

WebSocket Test

- - \ No newline at end of file diff --git a/test-jetty-webapp/pom.xml b/test-jetty-webapp/pom.xml index 8c23bfc559a..b500b1c4041 100644 --- a/test-jetty-webapp/pom.xml +++ b/test-jetty-webapp/pom.xml @@ -117,6 +117,12 @@ servlet-api provided + + org.eclipse.jetty + jetty-websocket + ${project.version} + provided + org.eclipse.jetty jetty-webapp diff --git a/test-jetty-webapp/src/main/java/com/acme/ChatServlet.java b/test-jetty-webapp/src/main/java/com/acme/ChatServlet.java index 432925162ca..f9f789ac9f8 100644 --- a/test-jetty-webapp/src/main/java/com/acme/ChatServlet.java +++ b/test-jetty-webapp/src/main/java/com/acme/ChatServlet.java @@ -28,7 +28,6 @@ import org.eclipse.jetty.continuation.Continuation; import org.eclipse.jetty.continuation.ContinuationSupport; - // Simple asynchronous Chat room. // This does not handle duplicate usernames or multiple frames/tabs from the same browser // Some code is duplicated for clarity. @@ -90,7 +89,7 @@ public class ChatServlet extends HttpServlet return; } Member member = room.get(username); - if (room==null) + if (member==null) { response.sendError(503); return; @@ -147,19 +146,22 @@ public class ChatServlet extends HttpServlet throws IOException { Map room=_rooms.get(request.getPathInfo()); - // Post chat to all members - for (Member m:room.values()) + if (room!=null) { - synchronized (m) + // Post chat to all members + for (Member m:room.values()) { - m._queue.add(username); // from - m._queue.add(message); // chat - - // wakeup member if polling - if (m._continuation!=null) + synchronized (m) { - m._continuation.resume(); - m._continuation=null; + m._queue.add(username); // from + m._queue.add(message); // chat + + // wakeup member if polling + if (m._continuation!=null) + { + m._continuation.resume(); + m._continuation=null; + } } } } @@ -168,111 +170,16 @@ public class ChatServlet extends HttpServlet PrintWriter out=response.getWriter(); out.print("{action:\"chat\"}"); } - // Serve the HTML with embedded CSS and Javascript. // This should be static content and should use real JS libraries. @Override protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { - if (!request.getRequestURI().endsWith("/")) - { - response.sendRedirect(request.getRequestURI()+"/"); - return; - } if (request.getParameter("action")!=null) - { doPost(request,response); - return; - } - - response.setContentType("text/html"); - PrintWriter out=response.getWriter(); - out.println(""); - out.println(" async chat"); - out.println(" "); - out.println(" "); - out.println(""); - out.println("
"); - out.println("
"); - out.println("
"); - out.println(" Username: "); - out.println("
"); - out.println(" "); - out.println("
"); - out.println(""); - out.println(""); + else + getServletContext().getNamedDispatcher("default").forward(request,response); } } diff --git a/test-jetty-webapp/src/main/java/com/acme/WebSocketChatServlet.java b/test-jetty-webapp/src/main/java/com/acme/WebSocketChatServlet.java new file mode 100644 index 00000000000..4ba4b542042 --- /dev/null +++ b/test-jetty-webapp/src/main/java/com/acme/WebSocketChatServlet.java @@ -0,0 +1,73 @@ +package com.acme; + +import java.io.IOException; +import java.util.Set; +import java.util.concurrent.CopyOnWriteArraySet; + +import javax.servlet.RequestDispatcher; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.eclipse.jetty.util.TypeUtil; +import org.eclipse.jetty.util.log.Log; +import org.eclipse.jetty.websocket.WebSocket; +import org.eclipse.jetty.websocket.WebSocketServlet; + +public class WebSocketChatServlet extends WebSocketServlet +{ + private final Set _members = new CopyOnWriteArraySet(); + + @Override + protected void doGet(HttpServletRequest request, HttpServletResponse response) + throws javax.servlet.ServletException ,IOException + { + getServletContext().getNamedDispatcher("default").forward(request,response); + }; + + @Override + protected WebSocket doWebSocketConnect(HttpServletRequest request, String protocol) + { + return new ChatWebSocket(); + } + + /* ------------------------------------------------------------ */ + /* ------------------------------------------------------------ */ + class ChatWebSocket implements WebSocket + { + Outbound _outbound; + + public void onConnect(Outbound outbound) + { + // Log.info(this+" onConnect"); + _outbound=outbound; + _members.add(this); + } + + public void onMessage(byte frame, byte[] data,int offset, int length) + { + // Log.info(this+" onMessage: "+TypeUtil.toHexString(data,offset,length)); + } + + public void onMessage(byte frame, String data) + { + // Log.info(this+" onMessage: "+data); + for (ChatWebSocket member : _members) + { + try + { + member._outbound.sendMessage(frame,data); + } + catch(IOException e) + { + Log.warn(e); + } + } + } + + public void onDisconnect() + { + // Log.info(this+" onDisconnect"); + _members.remove(this); + } + } +} diff --git a/test-jetty-webapp/src/main/webapp/WEB-INF/web.xml b/test-jetty-webapp/src/main/webapp/WEB-INF/web.xml index d2abb3cb8e5..c962d53c99f 100644 --- a/test-jetty-webapp/src/main/webapp/WEB-INF/web.xml +++ b/test-jetty-webapp/src/main/webapp/WEB-INF/web.xml @@ -176,6 +176,17 @@ /chat/* + + WSChat + com.acme.WebSocketChatServlet + 1 + + + + WSChat + /ws/* + + Rewrite diff --git a/test-jetty-webapp/src/main/webapp/chat/index.html b/test-jetty-webapp/src/main/webapp/chat/index.html new file mode 100644 index 00000000000..adc33ef30d1 --- /dev/null +++ b/test-jetty-webapp/src/main/webapp/chat/index.html @@ -0,0 +1,85 @@ + + Async Chat + + + +
+
+
+ Username:  +
+ +
+ + diff --git a/test-jetty-webapp/src/main/webapp/index.html b/test-jetty-webapp/src/main/webapp/index.html index 0d6734f4520..efa81dc6be6 100644 --- a/test-jetty-webapp/src/main/webapp/index.html +++ b/test-jetty-webapp/src/main/webapp/index.html @@ -18,21 +18,19 @@ Commercial support for Jetty is available via w This is a test context that serves:

diff --git a/test-jetty-webapp/src/main/webapp/ws/index.html b/test-jetty-webapp/src/main/webapp/ws/index.html new file mode 100644 index 00000000000..bb239c2366d --- /dev/null +++ b/test-jetty-webapp/src/main/webapp/ws/index.html @@ -0,0 +1,110 @@ + + + WebSocket Chat + + + +

+
+
+ Username:  +
+ +
+ + +

+This is a demonstration of the Jetty websocket server. +

+ + + + diff --git a/test-jetty-webapp/src/test/java/org/eclipse/jetty/DemoServer.java b/test-jetty-webapp/src/test/java/org/eclipse/jetty/DemoServer.java deleted file mode 100644 index ef1d2452969..00000000000 --- a/test-jetty-webapp/src/test/java/org/eclipse/jetty/DemoServer.java +++ /dev/null @@ -1,61 +0,0 @@ -package org.eclipse.jetty; - -import java.lang.management.ManagementFactory; - -import javax.management.MBeanServer; - -import org.eclipse.jetty.http.security.Password; -import org.eclipse.jetty.jmx.MBeanContainer; -import org.eclipse.jetty.security.HashLoginService; -import org.eclipse.jetty.server.Server; -import org.eclipse.jetty.server.handler.ContextHandlerCollection; -import org.eclipse.jetty.server.nio.SelectChannelConnector; -import org.eclipse.jetty.webapp.WebAppContext; - -public class DemoServer -{ - public static void main(String[] args) - throws Exception - { - if (args.length!=1) - { - System.err.println("Usage - java "+DemoServer.class+" webappdir|war"); - System.exit(1); - } - - Server server = new Server(); - - // setup JMX - MBeanServer mbeanS = ManagementFactory.getPlatformMBeanServer(); - MBeanContainer mbeanC = new MBeanContainer(mbeanS); - server.getContainer().addEventListener(mbeanC); - server.addBean(mbeanC); - - // setup connector - SelectChannelConnector connector = new SelectChannelConnector(); - connector.setPort(8080); - server.addConnector(connector); - - // setup Login service - HashLoginService login = new HashLoginService(); - login.putUser("jetty",new Password("password"),new String[]{"user"}); - login.putUser("admin",new Password("password"),new String[]{"user","admin"}); - server.addBean(login); - - // ContextHandlerCollection - ContextHandlerCollection contexts = new ContextHandlerCollection(); - server.setHandler(contexts); - - // setup webapp - WebAppContext context = new WebAppContext(); - context.setWar(args[0]); - context.setDefaultsDescriptor("../jetty-webapp/src/main/config/etc/webdefault.xml"); - contexts.addHandler(context); - - - // start the server - server.start(); - System.err.println(server.dump()); - server.join(); - } -} diff --git a/test-jetty-webapp/src/test/java/com/acme/DispatchServletTest.java b/test-jetty-webapp/src/test/java/org/eclipse/jetty/DispatchServletTest.java similarity index 98% rename from test-jetty-webapp/src/test/java/com/acme/DispatchServletTest.java rename to test-jetty-webapp/src/test/java/org/eclipse/jetty/DispatchServletTest.java index 07e1a40e4b8..113b07e86e9 100644 --- a/test-jetty-webapp/src/test/java/com/acme/DispatchServletTest.java +++ b/test-jetty-webapp/src/test/java/org/eclipse/jetty/DispatchServletTest.java @@ -11,7 +11,7 @@ // See the License for the specific language governing permissions and // limitations under the License. // ======================================================================== -package com.acme; +package org.eclipse.jetty; import junit.framework.TestCase; @@ -19,6 +19,8 @@ import org.eclipse.jetty.servlet.DefaultServlet; import org.eclipse.jetty.servlet.ServletHolder; import org.eclipse.jetty.testing.ServletTester; +import com.acme.DispatchServlet; + /** * Simple tests against DispatchServlet. */ diff --git a/test-jetty-webapp/src/test/java/org/eclipse/jetty/DumpServer.java b/test-jetty-webapp/src/test/java/org/eclipse/jetty/DumpServer.java deleted file mode 100644 index 827792e7761..00000000000 --- a/test-jetty-webapp/src/test/java/org/eclipse/jetty/DumpServer.java +++ /dev/null @@ -1,32 +0,0 @@ -package org.eclipse.jetty; - -import org.eclipse.jetty.server.Server; -import org.eclipse.jetty.server.handler.DebugHandler; -import org.eclipse.jetty.servlet.FilterHolder; -import org.eclipse.jetty.servlet.ServletContextHandler; -import org.eclipse.jetty.servlet.ServletHolder; -import org.eclipse.jetty.servlets.IncludableGzipFilter; - -import com.acme.Dump; - - -public class DumpServer -{ - public static void main(String[] args) - throws Exception - { - Server server = new Server(8080); - DebugHandler debug = new DebugHandler(); - debug.setOutputStream(System.err); - server.setHandler(debug); - - ServletContextHandler context = new ServletContextHandler(debug,"/",ServletContextHandler.SESSIONS); - FilterHolder gzip=context.addFilter(IncludableGzipFilter.class,"/*",0); - gzip.setInitParameter("uncheckedPrintWriter","true"); - context.addServlet(new ServletHolder(new Dump()), "/*"); - - server.start(); - server.join(); - } - -} diff --git a/test-jetty-webapp/src/test/java/org/eclipse/jetty/TestServer.java b/test-jetty-webapp/src/test/java/org/eclipse/jetty/TestServer.java new file mode 100644 index 00000000000..c613c2ecc28 --- /dev/null +++ b/test-jetty-webapp/src/test/java/org/eclipse/jetty/TestServer.java @@ -0,0 +1,106 @@ +// ======================================================================== +// Copyright (c) 2006-2009 Mort Bay Consulting Pty. Ltd. +// ------------------------------------------------------------------------ +// 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; + +import java.lang.management.ManagementFactory; + +import org.eclipse.jetty.jmx.MBeanContainer; +import org.eclipse.jetty.security.HashLoginService; +import org.eclipse.jetty.server.Connector; +import org.eclipse.jetty.server.Handler; +import org.eclipse.jetty.server.NCSARequestLog; +import org.eclipse.jetty.server.Server; +import org.eclipse.jetty.server.handler.ContextHandlerCollection; +import org.eclipse.jetty.server.handler.DefaultHandler; +import org.eclipse.jetty.server.handler.HandlerCollection; +import org.eclipse.jetty.server.handler.RequestLogHandler; +import org.eclipse.jetty.server.nio.SelectChannelConnector; +import org.eclipse.jetty.server.ssl.SslSelectChannelConnector; +import org.eclipse.jetty.util.log.Log; +import org.eclipse.jetty.util.thread.QueuedThreadPool; +import org.eclipse.jetty.webapp.WebAppContext; + +public class TestServer +{ + public static void main(String[] args) throws Exception + { + String jetty_home = System.getProperty("jetty.home","../jetty-distribution/target/distribution"); + System.setProperty("jetty.home",jetty_home); + + Server server = new Server(); + + // Setup JMX + MBeanContainer mbContainer=new MBeanContainer(ManagementFactory.getPlatformMBeanServer()); + server.getContainer().addEventListener(mbContainer); + server.addBean(mbContainer); + mbContainer.addBean(Log.getLog()); + + + // Setup Threadpool + QueuedThreadPool threadPool = new QueuedThreadPool(); + threadPool.setMaxThreads(100); + server.setThreadPool(threadPool); + + // Setup Connectors + SelectChannelConnector connector = new SelectChannelConnector(); + connector.setPort(8080); + connector.setMaxIdleTime(30000); + connector.setConfidentialPort(8443); + server.setConnectors(new Connector[] + { connector }); + + SslSelectChannelConnector ssl_connector = new SslSelectChannelConnector(); + ssl_connector.setPort(8443); + ssl_connector.setKeystore(jetty_home + "/etc/keystore"); + ssl_connector.setPassword("OBF:1vny1zlo1x8e1vnw1vn61x8g1zlu1vn4"); + ssl_connector.setKeyPassword("OBF:1u2u1wml1z7s1z7a1wnl1u2g"); + ssl_connector.setTruststore(jetty_home + "/etc/keystore"); + ssl_connector.setTrustPassword("OBF:1vny1zlo1x8e1vnw1vn61x8g1zlu1vn4"); + server.addConnector(ssl_connector); + + HandlerCollection handlers = new HandlerCollection(); + ContextHandlerCollection contexts = new ContextHandlerCollection(); + RequestLogHandler requestLogHandler = new RequestLogHandler(); + handlers.setHandlers(new Handler[] + { contexts, new DefaultHandler(), requestLogHandler }); + server.setHandler(handlers); + + + // Setup deployers + + HashLoginService login = new HashLoginService(); + login.setName("Test Realm"); + login.setConfig(jetty_home + "/etc/realm.properties"); + server.addBean(login); + + NCSARequestLog requestLog = new NCSARequestLog(jetty_home + "/logs/jetty-yyyy_mm_dd.log"); + requestLog.setExtended(false); + requestLogHandler.setRequestLog(requestLog); + + server.setStopAtShutdown(true); + server.setSendServerVersion(true); + + + WebAppContext webapp = new WebAppContext(); + webapp.setParentLoaderPriority(true); + webapp.setResourceBase("./src/main/webapp"); + + contexts.addHandler(webapp); + + + server.start(); + server.join(); + } + +}