More work towards API (this time on the server side)

This commit is contained in:
Joakim Erdfelt 2012-06-27 12:42:17 -07:00
parent 984166641e
commit 1e0752f5c8
41 changed files with 1566 additions and 1043 deletions

View File

@ -0,0 +1,56 @@
package org.eclipse.jetty.websocket.api;
/**
* Default implementation of the {@link WebSocketListener}.
* <p>
* Convenient abstract class to base standard WebSocket implementations off of.
*/
public class WebSocketAdapter implements WebSocketListener
{
private WebSocketConnection connection;
public WebSocketConnection getConnection()
{
return connection;
}
public boolean isConnected()
{
return (connection != null) && (connection.isOpen());
}
public boolean isNotConnected()
{
return (connection == null) || (!connection.isOpen());
}
@Override
public void onWebSocketBinary(byte[] payload, int offset, int len)
{
/* do nothing */
}
@Override
public void onWebSocketClose(int statusCode, String reason)
{
this.connection = null;
}
@Override
public void onWebSocketConnect(WebSocketConnection connection)
{
this.connection = connection;
}
@Override
public void onWebSocketException(WebSocketException error)
{
/* do nothing */
}
@Override
public void onWebSocketText(String message)
{
/* do nothing */
}
}

View File

@ -5,6 +5,7 @@ import java.net.InetAddress;
import java.nio.ByteBuffer;
import org.eclipse.jetty.util.Callback;
import org.eclipse.jetty.websocket.frames.BaseFrame;
/**
* Connection interface for WebSocket protocol <a href="https://tools.ietf.org/html/rfc6455">RFC-6455</a>.
@ -59,6 +60,13 @@ public interface WebSocketConnection
*/
boolean isOpen();
/**
* Send a frame.
* <p>
* Basic usage, results in a series of non-blocking async writes.
*/
void write(BaseFrame frame) throws IOException;
/**
* Send a binary message.
* <p>
@ -75,6 +83,14 @@ public interface WebSocketConnection
*/
void write(ByteBuffer... buffers) throws IOException;
/**
* Send a series of frames.
* <p>
* Advanced usage, with callbacks, allows for concurrent NIO style results of the entire write operation. (Callback is only called once at the end of
* processing all of the frames)
*/
<C> void write(C context, Callback<C> callback, BaseFrame ... frames) throws IOException;
/**
* Send a series of binary messages.
* <p>
@ -91,7 +107,7 @@ public interface WebSocketConnection
* Note: each messages results in its own text message frame.
* <p>
* Advanced usage, with callbacks, allows for concurrent NIO style results of the entire write operation. (Callback is only called once at the end of
* processing all of the buffers)
* processing all of the messages)
*/
<C> void write(C context, Callback<C> callback, String... messages) throws IOException;

View File

@ -0,0 +1,77 @@
package org.eclipse.jetty.websocket.api;
import org.eclipse.jetty.websocket.annotations.WebSocket;
import org.eclipse.jetty.websocket.frames.BaseFrame;
/**
* Responsible for routing the internally generated events destined for a specific WebSocket instance to whatever choice of development style the developer has
* used to wireup their specific WebSocket implementation.
* <p>
* Supports WebSocket instances that either implement {@link WebSocketListener} or have used the {@link WebSocket &#064;WebSocket} annotation.
* <p>
* There will be an instance of the WebSocketEventDriver per connection.
*/
public class WebSocketEventDriver
{
private Object websocket;
private WebSocketConnection connection;
/**
* Establish the driver for the Websocket POJO
*
* @param websocket
*/
public WebSocketEventDriver(Object websocket)
{
this.websocket = websocket;
// TODO Discover and bind what routing is available in the POJO
}
/**
* Get the Websocket POJO in use
*
* @return the Websocket POJO
*/
public Object getWebSocketObject()
{
return websocket;
}
/**
* Internal entry point for connection established
*/
public void onConnect()
{
// TODO Auto-generated method stub
}
/**
* Internal entry point for connection disconnected
*/
public void onDisconnect()
{
// TODO Auto-generated method stub
}
/**
* Internal entry point for incoming frames
*
* @param frame
* the frame that appeared
*/
public void onFrame(BaseFrame frame)
{
// TODO Auto-generated method stub
}
/**
* Set the connection to use for this driver
*
* @param conn
* the connection
*/
public void setConnection(WebSocketConnection conn)
{
this.connection = conn;
}
}

View File

@ -2,7 +2,7 @@ package org.eclipse.jetty.websocket.frames;
import org.eclipse.jetty.websocket.api.OpCode;
public abstract class DataFrame extends BaseFrame
public class DataFrame extends BaseFrame
{
// internal tracking
private int continuationIndex = 0;

View File

@ -0,0 +1,53 @@
package org.eclipse.jetty.websocket.annotations;
import java.util.ArrayList;
import java.util.List;
import org.eclipse.jetty.websocket.api.WebSocketConnection;
@WebSocket
public class CaptureSocket
{
private List<String> events = new ArrayList<>();
private void addEvent(String format, Object ... args)
{
events.add(String.format(format,args));
}
public void clear()
{
events.clear();
}
public List<String> getEvents()
{
return events;
}
@OnWebSocketClose
public void onClose(int statusCode, String reason)
{
addEvent("OnWebSocketClose(%d, %s)",statusCode,qoute(reason));
}
@OnWebSocketConnect
public void onConnect(WebSocketConnection conn)
{
addEvent("OnWebSocketConnect(conn)");
}
@OnWebSocketText
public void onText(String message) {
addEvent("@OnWebSocketText(%s)", qoute(message));
}
private String qoute(String str)
{
if (str == null)
{
return "<null>";
}
return '"' + str + '"';
}
}

View File

@ -0,0 +1,48 @@
package org.eclipse.jetty.websocket.annotations;
import java.io.IOException;
import org.eclipse.jetty.websocket.api.WebSocketConnection;
/**
* The most common websocket implementation.
* <p>
* This version tracks the connection per socket instance and will
*/
@WebSocket
public class MyEchoSocket
{
private WebSocketConnection conn;
@OnWebSocketClose
public void onClose(int statusCode, String reason)
{
this.conn = null;
}
@OnWebSocketConnect
public void onConnect(WebSocketConnection conn)
{
this.conn = conn;
}
@OnWebSocketText
public void onText(String message)
{
if (conn == null)
{
// no connection, do nothing.
// this is possible due to async behavior
return;
}
try
{
conn.write(message);
}
catch (IOException e)
{
e.printStackTrace();
}
}
}

View File

@ -0,0 +1,30 @@
package org.eclipse.jetty.websocket.annotations;
import java.io.IOException;
import org.eclipse.jetty.websocket.api.WebSocketConnection;
/**
* Example of a stateless websocket implementation.
* <p>
* Useful for websockets that only reply to incoming requests.
* <p>
* Note: that for this style of websocket to be viable on the server side be sure that you only create 1 instance of this socket, as more instances would be
* wasteful of resources and memory.
*/
@WebSocket
public class MyStatelessEchoSocket
{
@OnWebSocketText
public void onText(WebSocketConnection conn, String text)
{
try
{
conn.write(text);
}
catch (IOException e)
{
e.printStackTrace();
}
}
}

View File

@ -0,0 +1,10 @@
package org.eclipse.jetty.websocket.annotations;
/**
* The most basic websocket declaration.
*/
@WebSocket
public class NoopSocket
{
/* intentionally do nothing */
}

View File

@ -0,0 +1,44 @@
package org.eclipse.jetty.websocket.annotations;
import org.eclipse.jetty.websocket.api.LocalWebSocketConnection;
import org.eclipse.jetty.websocket.api.StatusCode;
import org.eclipse.jetty.websocket.api.WebSocketConnection;
import org.eclipse.jetty.websocket.api.WebSocketEventDriver;
import org.eclipse.jetty.websocket.frames.CloseFrame;
import org.eclipse.jetty.websocket.frames.TextFrame;
import org.junit.Test;
public class WebSocketAnnotationTest
{
/**
* Test Case for no exceptions
*/
@Test
public void testCapture()
{
WebSocketEventDriver driver = new WebSocketEventDriver(new NoopSocket());
WebSocketConnection conn = new LocalWebSocketConnection();
driver.setConnection(conn);
driver.onConnect();
driver.onFrame(new TextFrame("Hello World"));
driver.onFrame(new CloseFrame(StatusCode.NORMAL));
driver.onDisconnect();
}
/**
* Test Case for no exceptions
*/
@Test
public void testNoop()
{
WebSocketEventDriver driver = new WebSocketEventDriver(new NoopSocket());
WebSocketConnection conn = new LocalWebSocketConnection();
driver.setConnection(conn);
driver.onConnect();
driver.onFrame(new TextFrame("Hello World"));
driver.onFrame(new CloseFrame(StatusCode.NORMAL));
driver.onDisconnect();
}
}

View File

@ -0,0 +1,102 @@
package org.eclipse.jetty.websocket.api;
import java.io.IOException;
import java.net.InetAddress;
import java.nio.ByteBuffer;
import org.eclipse.jetty.util.Callback;
import org.eclipse.jetty.websocket.frames.BaseFrame;
public class LocalWebSocketConnection implements WebSocketConnection
{
@Override
public void close()
{
// TODO Auto-generated method stub
}
@Override
public void close(int statusCode, String reason)
{
// TODO Auto-generated method stub
}
@Override
public WebSocketPolicy getPolicy()
{
// TODO Auto-generated method stub
return null;
}
@Override
public InetAddress getRemoteAddress()
{
// TODO Auto-generated method stub
return null;
}
@Override
public String getSubProtocol()
{
// TODO Auto-generated method stub
return null;
}
@Override
public boolean isOpen()
{
// TODO Auto-generated method stub
return false;
}
@Override
public void write(BaseFrame frame) throws IOException
{
// TODO Auto-generated method stub
}
@Override
public void write(byte[] data, int offset, int length) throws IOException
{
// TODO Auto-generated method stub
}
@Override
public void write(ByteBuffer... buffers) throws IOException
{
// TODO Auto-generated method stub
}
@Override
public <C> void write(C context, Callback<C> callback, BaseFrame... frames) throws IOException
{
// TODO Auto-generated method stub
}
@Override
public <C> void write(C context, Callback<C> callback, ByteBuffer... buffers) throws IOException
{
// TODO Auto-generated method stub
}
@Override
public <C> void write(C context, Callback<C> callback, String... messages) throws IOException
{
// TODO Auto-generated method stub
}
@Override
public void write(String message) throws IOException
{
// TODO Auto-generated method stub
}
}

View File

@ -0,0 +1,117 @@
package org.eclipse.jetty.websocket.server;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import org.eclipse.jetty.util.QuotedStringTokenizer;
import org.eclipse.jetty.websocket.api.ExtensionConfig;
public class ServletWebSocketRequest extends HttpServletRequestWrapper implements WebSocketRequest
{
private List<String> subProtocols = new ArrayList<>();
private List<ExtensionConfig> extensions;
public ServletWebSocketRequest(HttpServletRequest request)
{
super(request);
Enumeration<String> protocols = request.getHeaders("Sec-WebSocket-Protocol");
String protocol = null;
while ((protocol == null) && (protocols != null) && protocols.hasMoreElements())
{
String candidate = protocols.nextElement();
for (String p : parseProtocols(candidate))
{
subProtocols.add(p);
}
}
extensions = new ArrayList<>();
Enumeration<String> e = request.getHeaders("Sec-WebSocket-Extensions");
while (e.hasMoreElements())
{
QuotedStringTokenizer tok = new QuotedStringTokenizer(e.nextElement(),",");
while (tok.hasMoreTokens())
{
extensions.add(ExtensionConfig.parse(tok.nextToken()));
}
}
}
@Override
public List<ExtensionConfig> getExtensions()
{
return extensions;
}
@Override
public String getHost()
{
return getHeader("Host");
}
/**
* Get the endpoint of the WebSocket connection.
* <p>
* Per the <a href="https://tools.ietf.org/html/rfc6455#section-1.3">Opening Handshake (RFC 6455)</a>
*/
@Override
public String getHttpEndPointName()
{
return getRequestURI();
}
@Override
public String getOrigin()
{
String origin = getHeader("Origin");
if (origin == null)
{
// Fall back to older version
origin = getHeader("Sec-WebSocket-Origin");
}
return origin;
}
@Override
public List<String> getSubProtocols()
{
return subProtocols;
}
@Override
public boolean hasSubProtocol(String test)
{
return subProtocols.contains(test);
}
@Override
public boolean isOrigin(String test)
{
return test.equalsIgnoreCase(getOrigin());
}
protected String[] parseProtocols(String protocol)
{
if (protocol == null)
{
return new String[]
{ null };
}
protocol = protocol.trim();
if ((protocol == null) || (protocol.length() == 0))
{
return new String[]
{ null };
}
String[] passed = protocol.split("\\s*,\\s*");
String[] protocols = new String[passed.length + 1];
System.arraycopy(passed,0,protocols,0,passed.length);
return protocols;
}
}

View File

@ -0,0 +1,51 @@
package org.eclipse.jetty.websocket.server;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpServletResponseWrapper;
import org.eclipse.jetty.websocket.api.ExtensionConfig;
public class ServletWebSocketResponse extends HttpServletResponseWrapper implements WebSocketResponse
{
private String acceptedProtocol;
private List<ExtensionConfig> extensions = new ArrayList<>();
public ServletWebSocketResponse(HttpServletResponse resp)
{
super(resp);
}
@Override
public String getAcceptedSubProtocol()
{
return acceptedProtocol;
}
@Override
public List<ExtensionConfig> getExtensions()
{
return this.extensions;
}
@Override
public void sendForbidden(String message) throws IOException
{
sendError(HttpServletResponse.SC_FORBIDDEN,message);
}
@Override
public void setAcceptedSubProtocol(String protocol)
{
this.acceptedProtocol = protocol;
}
@Override
public void setExtensions(List<ExtensionConfig> extensions)
{
this.extensions = extensions;
}
}

View File

@ -0,0 +1,24 @@
package org.eclipse.jetty.websocket.server;
import org.eclipse.jetty.websocket.extensions.Extension;
/**
* Abstract WebSocket creator interface.
* <p>
* Should you desire filtering of the WebSocket object creation due to criteria such as origin or sub-protocol, then you will be required to implement a custom
* WebSocketCreator implementation.
* <p>
* This has been moved from the WebSocketServlet to a standalone class managed by the {@link WebSocketServerFactory} due to need of WebSocket {@link Extension}s
* that require the ability to create new websockets (such as the mux extension)
*/
public interface WebSocketCreator
{
/**
* Create a websocket from the incoming request.
*
* @param req
* the request details
* @return a websocket object to use, or null if no websocket should be created from this request.
*/
Object createWebSocket(WebSocketRequest req, WebSocketResponse resp);
}

View File

@ -39,20 +39,40 @@ import org.eclipse.jetty.server.handler.HandlerWrapper;
import org.eclipse.jetty.websocket.api.WebSocketBehavior;
import org.eclipse.jetty.websocket.api.WebSocketPolicy;
public abstract class WebSocketHandler extends HandlerWrapper implements WebSocketServer.Acceptor
public abstract class WebSocketHandler extends HandlerWrapper
{
/**
* Create a simple WebSocketHandler that registers a single WebSocket POJO that is created on every upgrade request.
*/
public static class Simple extends WebSocketHandler
{
private Class<?> websocketPojo;
public Simple(Class<?> websocketClass)
{
this.websocketPojo = websocketClass;
}
@Override
public void registerWebSockets(WebSocketServerFactory factory)
{
factory.register(websocketPojo);
}
}
private final WebSocketServerFactory webSocketFactory;
public WebSocketHandler()
{
WebSocketPolicy policy = new WebSocketPolicy(WebSocketBehavior.SERVER);
webSocketFactory = new WebSocketServerFactory(this,policy);
configurePolicy(policy);
webSocketFactory = new WebSocketServerFactory(policy);
registerWebSockets(webSocketFactory);
}
@Override
public boolean checkOrigin(HttpServletRequest request, String origin)
public void configurePolicy(WebSocketPolicy policy)
{
return true;
/* leave at default */
}
public WebSocketServerFactory getWebSocketFactory()
@ -63,10 +83,25 @@ public abstract class WebSocketHandler extends HandlerWrapper implements WebSock
@Override
public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
{
if (webSocketFactory.acceptWebSocket(request,response) || response.isCommitted())
if (webSocketFactory.isUpgradeRequest(request,response))
{
return;
// We have an upgrade request
if (webSocketFactory.acceptWebSocket(request,response))
{
// We have a socket instance created
return;
}
// If we reach this point, it means we had an incoming request to upgrade
// but it was either not a proper websocket upgrade, or it was possibly rejected
// due to incoming request constraints (controlled by WebSocketCreator)
if (response.isCommitted())
{
// not much we can do at this point.
return;
}
}
super.handle(target,baseRequest,request,response);
}
public abstract void registerWebSockets(WebSocketServerFactory factory);
}

View File

@ -0,0 +1,19 @@
package org.eclipse.jetty.websocket.server;
import java.io.IOException;
import java.util.List;
import org.eclipse.jetty.websocket.extensions.Extension;
public interface WebSocketHandshake
{
/**
* Formulate a WebSocket upgrade handshake response.
*
* @param request
* @param response
* @param extensions
* @param acceptedSubProtocol
*/
public void doHandshakeResponse(ServletWebSocketRequest request, ServletWebSocketResponse response, List<Extension> extensions) throws IOException;
}

View File

@ -0,0 +1,24 @@
package org.eclipse.jetty.websocket.server;
import java.util.List;
import org.eclipse.jetty.websocket.api.ExtensionConfig;
public interface WebSocketRequest
{
public List<ExtensionConfig> getExtensions();
public String getHeader(String name);
public String getHost();
public String getHttpEndPointName();
public String getOrigin();
public List<String> getSubProtocols();
public boolean hasSubProtocol(String test);
public boolean isOrigin(String test);
}

View File

@ -0,0 +1,61 @@
package org.eclipse.jetty.websocket.server;
import java.io.IOException;
import java.util.List;
import org.eclipse.jetty.websocket.api.ExtensionConfig;
public interface WebSocketResponse
{
/**
* Get the accepted WebSocket protocol.
*
* @return the accepted WebSocket protocol.
*/
public String getAcceptedSubProtocol();
/**
* Get the list of extensions that should be used for the websocket.
*
* @return the list of negotiated extensions to use.
*/
public List<ExtensionConfig> getExtensions();
/**
* Issue a forbidden upgrade response.
* <p>
* This means that the websocket endpoint was valid, but the conditions to use a WebSocket resulted in a forbidden access.
* <p>
* Use this when the origin or authentication is invalid.
*
* @param message
* the short 1 line detail message about the forbidden response
* @throws IOException
*/
public void sendForbidden(String message) throws IOException;
/**
* Set the accepted WebSocket Protocol.
*
* @param protocol
* the protocol to list as accepted
*/
public void setAcceptedSubProtocol(String protocol);
/**
* Set the list of extensions that are approved for use with this websocket.
* <p>
* This is Advanced usage of the {@link WebSocketCreator} to allow for a custom set of negotiated extensions.
* <p>
* Notes:
* <ul>
* <li>Per the spec you cannot add extensions that have not been seen in the {@link WebSocketRequest}, just remove entries you don't want to use</li>
* <li>If this is unused, or a null is passed, then the list negotiation will follow default behavior and use the complete list of extensions that are
* available in this WebSocket server implementation.</li>
* </ul>
*
* @param extensions
* the list of extensions to use.
*/
public void setExtensions(List<ExtensionConfig> extensions);
}

View File

@ -1,60 +0,0 @@
package org.eclipse.jetty.websocket.server;
import java.io.IOException;
import java.util.List;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.eclipse.jetty.websocket.WebSocket;
import org.eclipse.jetty.websocket.extensions.Extension;
/**
* Main API class for WebSocket servers
*/
public interface WebSocketServer
{
public static interface Acceptor
{
/**
* <p>
* Checks the origin of an incoming WebSocket handshake request.
* </p>
*
* @param request
* the incoming HTTP upgrade request
* @param origin
* the origin URI
* @return boolean to indicate that the origin is acceptable.
*/
boolean checkOrigin(HttpServletRequest request, String origin);
/* ------------------------------------------------------------ */
/**
* <p>
* Factory method that applications needs to implement to return a {@link WebSocket} object.
* </p>
*
* @param request
* the incoming HTTP upgrade request
* @param protocol
* the websocket sub protocol
* @return a new {@link WebSocket} object that will handle websocket events.
*/
WebSocket doWebSocketConnect(HttpServletRequest request, String protocol);
}
public static interface Handshake
{
/**
* Formulate a WebSocket upgrade handshake response.
*
* @param request
* @param response
* @param extensions
* @param acceptedSubProtocol
*/
public void doHandshakeResponse(HttpServletRequest request, HttpServletResponse response, List<Extension> extensions, String acceptedSubProtocol)
throws IOException;
}
}

View File

@ -16,7 +16,6 @@ package org.eclipse.jetty.websocket.server;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@ -30,12 +29,11 @@ import javax.servlet.http.HttpServletResponse;
import org.eclipse.jetty.http.HttpStatus;
import org.eclipse.jetty.io.AsyncEndPoint;
import org.eclipse.jetty.server.HttpConnection;
import org.eclipse.jetty.util.QuotedStringTokenizer;
import org.eclipse.jetty.util.component.AbstractLifeCycle;
import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger;
import org.eclipse.jetty.websocket.WebSocket;
import org.eclipse.jetty.websocket.api.ExtensionConfig;
import org.eclipse.jetty.websocket.api.WebSocketEventDriver;
import org.eclipse.jetty.websocket.api.WebSocketPolicy;
import org.eclipse.jetty.websocket.extensions.Extension;
import org.eclipse.jetty.websocket.extensions.deflate.DeflateFrameExtension;
@ -60,19 +58,17 @@ public class WebSocketServerFactory extends AbstractLifeCycle
extensionClasses.put("x-deflate-frame",DeflateFrameExtension.class);
}
private final Map<Integer, WebSocketServer.Handshake> handshakes = new HashMap<>();
private final Map<Integer, WebSocketHandshake> handshakes = new HashMap<>();
{
handshakes.put(HandshakeRFC6455.VERSION,new HandshakeRFC6455());
handshakes.put(HandshakeHixie76.VERSION,new HandshakeHixie76());
}
private final WebSocketServer.Acceptor acceptor;
private final String supportedVersions;
private WebSocketPolicy policy;
public WebSocketServerFactory(WebSocketServer.Acceptor acceptor, WebSocketPolicy policy)
public WebSocketServerFactory(WebSocketPolicy policy)
{
this.acceptor = acceptor;
this.policy = policy;
// Create supportedVersions
@ -96,57 +92,25 @@ public class WebSocketServerFactory extends AbstractLifeCycle
public boolean acceptWebSocket(HttpServletRequest request, HttpServletResponse response) throws IOException
{
if ("websocket".equalsIgnoreCase(request.getHeader("Upgrade")))
ServletWebSocketRequest sockreq = new ServletWebSocketRequest(request);
ServletWebSocketResponse sockresp = new ServletWebSocketResponse(response);
WebSocketCreator creator = getCreator();
Object websocketPojo = creator.createWebSocket(sockreq,sockresp);
if (websocketPojo == null)
{
String origin = request.getHeader("Origin");
if (origin == null)
{
origin = request.getHeader("Sec-WebSocket-Origin");
}
if (!acceptor.checkOrigin(request,origin))
{
response.sendError(HttpServletResponse.SC_FORBIDDEN);
return false;
}
// Try each requested protocol
WebSocket websocket = null;
Enumeration<String> protocols = request.getHeaders("Sec-WebSocket-Protocol");
String protocol = null;
while ((protocol == null) && (protocols != null) && protocols.hasMoreElements())
{
String candidate = protocols.nextElement();
for (String p : parseProtocols(candidate))
{
websocket = acceptor.doWebSocketConnect(request,p);
if (websocket != null)
{
protocol = p;
break;
}
}
}
// Did we get a websocket?
if (websocket == null)
{
// Try with no protocol
websocket = acceptor.doWebSocketConnect(request,null);
if (websocket == null)
{
response.sendError(HttpServletResponse.SC_SERVICE_UNAVAILABLE);
return false;
}
}
// Send the upgrade
upgrade(request,response,websocket,protocol);
return true;
// no creation, sorry
response.sendError(HttpServletResponse.SC_SERVICE_UNAVAILABLE);
return false;
}
return false;
// TODO: discover type, create proxy
// Send the upgrade
WebSocketEventDriver websocket = new WebSocketEventDriver(websocketPojo);
return upgrade(sockreq,sockresp,websocket);
}
protected boolean addConnection(AsyncWebSocketConnection connection)
@ -168,6 +132,12 @@ public class WebSocketServerFactory extends AbstractLifeCycle
closeConnections();
}
public WebSocketCreator getCreator()
{
// TODO: implement
return null;
}
/**
* @return A modifiable map of extension name to extension class
*/
@ -207,6 +177,12 @@ public class WebSocketServerFactory extends AbstractLifeCycle
return extensions;
}
public boolean isUpgradeRequest(HttpServletRequest request, HttpServletResponse response)
{
// TODO: other checks against the spec?
return ("websocket".equalsIgnoreCase(request.getHeader("Upgrade")));
}
private Extension newExtension(String name)
{
try
@ -244,11 +220,21 @@ public class WebSocketServerFactory extends AbstractLifeCycle
return protocols;
}
public void register(Class<?> websocketClass)
{
// TODO: implement
}
protected boolean removeConnection(AsyncWebSocketConnection connection)
{
return connections.remove(connection);
}
public void setCreator(WebSocketCreator creator)
{
// TODO: implement
}
/**
* Upgrade the request/response to a WebSocket Connection.
* <p>
@ -261,16 +247,14 @@ public class WebSocketServerFactory extends AbstractLifeCycle
* The response to upgrade
* @param websocket
* The websocket handler implementation to use
* @param acceptedSubProtocol
* The accepted websocket sub protocol
* @throws IOException
* in case of I/O errors
*/
public void upgrade(HttpServletRequest request, HttpServletResponse response, WebSocket websocket, String acceptedSubProtocol) throws IOException
public boolean upgrade(ServletWebSocketRequest request, ServletWebSocketResponse response, WebSocketEventDriver websocket) throws IOException
{
if (!"websocket".equalsIgnoreCase(request.getHeader("Upgrade")))
{
throw new IllegalStateException("Not a 'WebSocket: Ugprade' request");
throw new IllegalStateException("Not a 'WebSocket: Upgrade' request");
}
if (!"HTTP/1.1".equals(request.getProtocol()))
{
@ -284,18 +268,7 @@ public class WebSocketServerFactory extends AbstractLifeCycle
version = request.getIntHeader("Sec-WebSocket-Draft");
}
List<ExtensionConfig> extensionsRequested = new ArrayList<>();
Enumeration<String> e = request.getHeaders("Sec-WebSocket-Extensions");
while (e.hasMoreElements())
{
QuotedStringTokenizer tok = new QuotedStringTokenizer(e.nextElement(),",");
while (tok.hasMoreTokens())
{
extensionsRequested.add(ExtensionConfig.parse(tok.nextToken()));
}
}
WebSocketServer.Handshake handshaker = handshakes.get(version);
WebSocketHandshake handshaker = handshakes.get(version);
if (handshaker == null)
{
LOG.warn("Unsupported Websocket version: " + version);
@ -303,7 +276,7 @@ public class WebSocketServerFactory extends AbstractLifeCycle
// Using the examples as outlined
response.setHeader("Sec-WebSocket-Version",supportedVersions);
response.sendError(HttpStatus.BAD_REQUEST_400,"Unsupported websocket version specification");
return;
return false;
}
// Create connection
@ -314,16 +287,17 @@ public class WebSocketServerFactory extends AbstractLifeCycle
endp.setAsyncConnection(connection);
// Initialize / Negotiate Extensions
List<Extension> extensions = initExtensions(extensionsRequested);
List<Extension> extensions = initExtensions(response.getExtensions());
// Process (version specific) handshake response
handshaker.doHandshakeResponse(request,response,extensions,acceptedSubProtocol);
handshaker.doHandshakeResponse(request,response,extensions);
// Add connection
addConnection(connection);
// Tell jetty about the new connection
LOG.debug("Websocket upgrade {} {} {} {}",request.getRequestURI(),version,acceptedSubProtocol,connection);
LOG.debug("Websocket upgrade {} {} {} {}",request.getRequestURI(),version,response.getAcceptedSubProtocol(),connection);
request.setAttribute("org.eclipse.jetty.io.Connection",connection); // TODO: this still needed?
return true;
}
}

View File

@ -25,15 +25,15 @@ import javax.servlet.http.HttpServletResponse;
import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger;
import org.eclipse.jetty.websocket.annotations.WebSocket;
import org.eclipse.jetty.websocket.api.WebSocketBehavior;
import org.eclipse.jetty.websocket.api.WebSocketPolicy;
/**
* Abstract Servlet used to bridge the Servlet API to the WebSocket API.
* <p>
* This servlet implements the {@link WebSocketServer.Acceptor}, with a default implementation of
* {@link WebSocketServer.Acceptor#checkOrigin(HttpServletRequest, String)} leaving you to implement the
* {@link WebSocketServer.Acceptor#doWebSocketConnect(HttpServletRequest, String)}.
* To use this servlet, you will be required to register your websockets with the {@link WebSocketServerFactory} so that it can create your websockets under the
* appropriate conditions.
* <p>
* The most basic implementation would be as follows.
*
@ -47,19 +47,21 @@ import org.eclipse.jetty.websocket.api.WebSocketPolicy;
* public class MyEchoServlet extends WebSocketServlet
* {
* &#064;Override
* public WebSocket doWebSocketConnect(HttpServletRequest request, String protocol)
* public void registerWebSockets(WebSocketServerFactory factory)
* {
* return new MyEchoSocket();
* factory.register(MyEchoSocket.class);
* }
* }
* </pre>
*
* Note: this servlet will only forward on a incoming request that hits this servlet to the
* {@link WebSocketServer.Acceptor#doWebSocketConnect(HttpServletRequest, String)} if it conforms to a "WebSocket: Upgrade" handshake request. <br>
* All other requests are treated as normal servlet requets.
* Note: that only request that conforms to a "WebSocket: Upgrade" handshake request will trigger the {@link WebSocketServerFactory} handling of creating
* WebSockets.<br>
* All other requests are treated as normal servlet requests.
*
* <p>
* <b>Configuration / Init-Parameters:</b>
* <b>Configuration / Init-Parameters:</b><br>
* Note: If you use the {@link WebSocket &#064;WebSocket} annotation, these configuration settings can be specified on a per WebSocket basis, vs a per Servlet
* basis.
*
* <dl>
* <dt>bufferSize</dt>
@ -80,17 +82,11 @@ import org.eclipse.jetty.websocket.api.WebSocketPolicy;
* </dl>
*/
@SuppressWarnings("serial")
public abstract class WebSocketServlet extends HttpServlet implements WebSocketServer.Acceptor
public abstract class WebSocketServlet extends HttpServlet
{
private final Logger LOG = Log.getLogger(getClass());
private WebSocketServerFactory webSocketFactory;
@Override
public boolean checkOrigin(HttpServletRequest request, String origin)
{
return true;
}
@Override
public void destroy()
{
@ -137,7 +133,9 @@ public abstract class WebSocketServlet extends HttpServlet implements WebSocketS
policy.setMaxBinaryMessageSize(Integer.parseInt(max));
}
webSocketFactory = new WebSocketServerFactory(this,policy);
webSocketFactory = new WebSocketServerFactory(policy);
registerWebSockets(webSocketFactory);
}
catch (Exception x)
{
@ -145,16 +143,33 @@ public abstract class WebSocketServlet extends HttpServlet implements WebSocketS
}
}
public abstract void registerWebSockets(WebSocketServerFactory factory);
/**
* @see javax.servlet.http.HttpServlet#service(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse)
*/
@Override
protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
{
if (webSocketFactory.acceptWebSocket(request,response) || response.isCommitted())
if (webSocketFactory.isUpgradeRequest(request,response))
{
return;
// We have an upgrade request
if (webSocketFactory.acceptWebSocket(request,response))
{
// We have a socket instance created
return;
}
// If we reach this point, it means we had an incoming request to upgrade
// but it was either not a proper websocket upgrade, or it was possibly rejected
// due to incoming request constraints (controlled by WebSocketCreator)
if (response.isCommitted())
{
// not much we can do at this point.
return;
}
}
// All other processing
super.service(request,response);
}
}

View File

@ -3,25 +3,23 @@ package org.eclipse.jetty.websocket.server.handshake;
import java.io.IOException;
import java.util.List;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.eclipse.jetty.websocket.extensions.Extension;
import org.eclipse.jetty.websocket.server.WebSocketServer;
import org.eclipse.jetty.websocket.server.ServletWebSocketRequest;
import org.eclipse.jetty.websocket.server.ServletWebSocketResponse;
import org.eclipse.jetty.websocket.server.WebSocketHandshake;
/**
* WebSocket Handshake for spec <a href="https://tools.ietf.org/html/draft-hixie-thewebsocketprotocol-76">Hixie-76 Draft</a>.
* <p>
* Most often seen in use by Safari/OSX
*/
public class HandshakeHixie76 implements WebSocketServer.Handshake
public class HandshakeHixie76 implements WebSocketHandshake
{
/** draft-hixie-thewebsocketprotocol-76 - Sec-WebSocket-Draft */
public static final int VERSION = 0;
@Override
public void doHandshakeResponse(HttpServletRequest request, HttpServletResponse response, List<Extension> extensions, String acceptedSubProtocol)
throws IOException
public void doHandshakeResponse(ServletWebSocketRequest request, ServletWebSocketResponse response, List<Extension> extensions) throws IOException
{
// TODO: implement the Hixie76 handshake?
throw new IOException("Not implemented yet");

View File

@ -1,24 +1,26 @@
package org.eclipse.jetty.websocket.server.handshake;
import java.io.IOException;
import java.util.List;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.eclipse.jetty.websocket.api.AcceptHash;
import org.eclipse.jetty.websocket.extensions.Extension;
import org.eclipse.jetty.websocket.server.WebSocketServer;
import org.eclipse.jetty.websocket.server.ServletWebSocketRequest;
import org.eclipse.jetty.websocket.server.ServletWebSocketResponse;
import org.eclipse.jetty.websocket.server.WebSocketHandshake;
/**
* WebSocket Handshake for <a href="https://tools.ietf.org/html/rfc6455">RFC 6455</a>.
*/
public class HandshakeRFC6455 implements WebSocketServer.Handshake
public class HandshakeRFC6455 implements WebSocketHandshake
{
/** RFC 6455 - Sec-WebSocket-Version */
public static final int VERSION = 13;
@Override
public void doHandshakeResponse(HttpServletRequest request, HttpServletResponse response, List<Extension> extensions, String acceptedSubProtocol)
public void doHandshakeResponse(ServletWebSocketRequest request, ServletWebSocketResponse response, List<Extension> extensions) throws IOException
{
String key = request.getHeader("Sec-WebSocket-Key");
@ -32,9 +34,9 @@ public class HandshakeRFC6455 implements WebSocketServer.Handshake
response.addHeader("Connection","Upgrade");
response.addHeader("Sec-WebSocket-Accept",AcceptHash.hashKey(key));
if (acceptedSubProtocol != null)
if (response.getAcceptedSubProtocol() != null)
{
response.addHeader("Sec-WebSocket-Protocol",acceptedSubProtocol);
response.addHeader("Sec-WebSocket-Protocol",response.getAcceptedSubProtocol());
}
if (extensions != null)

View File

@ -1,477 +0,0 @@
/*******************************************************************************
* 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.server;
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.SelectChannelConnector;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.server.handler.ResourceHandler;
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;
import org.eclipse.jetty.websocket.WebSocket;
import org.eclipse.jetty.websocket.api.OpCode;
public class TestServer extends Server
{
class TestEchoAssembleWebSocket extends TestWebSocket
{
@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();
}
}
@Override
public void onOpen(Connection connection)
{
super.onOpen(connection);
connection.setMaxTextMessageSize(64*1024);
connection.setMaxBinaryMessageSize(64*1024);
}
}
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 onClose(int code, String message)
{
_latch.countDown();
super.onClose(code,message);
}
@Override
public boolean onControl(byte controlCode, byte[] data, int offset, int length)
{
if (controlCode==OpCode.PONG.getCode())
{
System.err.println("Pong "+getConnection());
}
return super.onControl(controlCode,data,offset,length);
}
@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(OpCode.PING.getCode(),data,0,data.length);
}
}
catch (Exception e)
{
e.printStackTrace();
}
}
};
_keepAlive.start();
}
}
/* ------------------------------------------------------------ */
/* ------------------------------------------------------------ */
class TestEchoBroadcastWebSocket extends TestWebSocket
{
@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();
}
}
}
@Override
public void onOpen(Connection connection)
{
super.onOpen(connection);
_broadcast.add(this);
}
}
/* ------------------------------------------------------------ */
/* ------------------------------------------------------------ */
class TestEchoFragmentWebSocket extends TestWebSocket
{
@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();
}
}
@Override
public void onOpen(Connection connection)
{
super.onOpen(connection);
connection.setMaxTextMessageSize(64*1024);
connection.setMaxBinaryMessageSize(64*1024);
}
}
/* ------------------------------------------------------------ */
/* ------------------------------------------------------------ */
class TestEchoWebSocket extends TestWebSocket
{
@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;
}
@Override
public void onOpen(Connection connection)
{
super.onOpen(connection);
connection.setMaxTextMessageSize(-1);
connection.setMaxBinaryMessageSize(-1);
}
}
/* ------------------------------------------------------------ */
/* ------------------------------------------------------------ */
class TestWebSocket implements WebSocket, WebSocket.OnFrame, WebSocket.OnBinaryMessage, WebSocket.OnTextMessage, WebSocket.OnControl
{
protected FrameConnection _connection;
public FrameConnection getConnection()
{
return _connection;
}
@Override
public void onClose(int code,String message)
{
if (_verbose)
{
System.err.printf("%s#onDisonnect %d %s\n",this.getClass().getSimpleName(),code,message);
}
}
@Override
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;
}
@Override
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;
}
@Override
public void onHandshake(FrameConnection connection)
{
if (_verbose)
{
System.err.printf("%s#onHandshake %s %s\n",this.getClass().getSimpleName(),connection,connection.getClass().getSimpleName());
}
_connection = connection;
}
@Override
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));
}
}
@Override
public void onMessage(String data)
{
if (_verbose)
{
System.err.printf("%s#onMessage %s\n",this.getClass().getSimpleName(),data);
}
}
@Override
public void onOpen(Connection connection)
{
if (_verbose)
{
System.err.printf("%s#onOpen %s %s\n",this.getClass().getSimpleName(),connection,connection.getProtocol());
}
}
}
private static final Logger LOG = Log.getLogger(TestServer.class);
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);
}
}
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);
}
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()
{
@Override
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 String getResourceBase()
{
return _rHandler.getResourceBase();
}
/* ------------------------------------------------------------ */
public boolean isVerbose()
{
return _verbose;
}
/* ------------------------------------------------------------ */
public void setResourceBase(String dir)
{
_rHandler.setResourceBase(dir);
}
/* ------------------------------------------------------------ */
public void setVerbose(boolean verbose)
{
_verbose = verbose;
}
}

View File

@ -15,8 +15,6 @@
*******************************************************************************/
package org.eclipse.jetty.websocket.server;
import static org.hamcrest.Matchers.*;
import java.net.URI;
import java.util.concurrent.TimeUnit;
@ -24,17 +22,14 @@ import org.eclipse.jetty.server.SelectChannelConnector;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.servlet.ServletContextHandler;
import org.eclipse.jetty.servlet.ServletHolder;
import org.eclipse.jetty.websocket.server.helper.CaptureSocket;
import org.eclipse.jetty.websocket.server.helper.MessageSender;
import org.eclipse.jetty.websocket.server.helper.WebSocketCaptureServlet;
import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
/**
* WebSocketCommTest - to test reported undelivered messages in bug <a
* href="https://jira.codehaus.org/browse/JETTY-1463">JETTY-1463</a>
* WebSocketCommTest - to test reported undelivered messages in bug <a href="https://jira.codehaus.org/browse/JETTY-1463">JETTY-1463</a>
*/
public class WebSocketCommTest
{
@ -107,17 +102,18 @@ public class WebSocketCommTest
}
// Servlet should show only 1 connection.
Assert.assertThat("Servlet.captureSockets.size",servlet.captures.size(),is(1));
// TODO: use factory to ask about use (tie this use into MBeans?)
// Assert.assertThat("Servlet.captureSockets.size",servlet.captures.size(),is(1));
CaptureSocket socket = servlet.captures.get(0);
Assert.assertThat("CaptureSocket",socket,notNullValue());
Assert.assertThat("CaptureSocket.isConnected",socket.awaitConnected(1000),is(true));
// CaptureSocket socket = servlet.captures.get(0);
// Assert.assertThat("CaptureSocket",socket,notNullValue());
// Assert.assertThat("CaptureSocket.isConnected",socket.awaitConnected(1000),is(true));
// Give servlet time to process messages
TimeUnit.MILLISECONDS.sleep(500);
// Should have captured 5 messages.
Assert.assertThat("CaptureSocket.messages.size",socket.messages.size(),is(5));
// Assert.assertThat("CaptureSocket.messages.size",socket.messages.size(),is(5));
}
finally
{

View File

@ -30,8 +30,6 @@ import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import javax.servlet.http.HttpServletRequest;
import junit.framework.Assert;
import org.eclipse.jetty.io.AsyncEndPoint;
@ -200,14 +198,7 @@ public class WebSocketLoadRFC6455Test
threadPool.setMaxStopTimeMs(1000);
_server.setThreadPool(threadPool);
WebSocketHandler wsHandler = new WebSocketHandler()
{
@Override
public WebSocket doWebSocketConnect(HttpServletRequest request, String protocol)
{
return new EchoWebSocket();
}
};
WebSocketHandler wsHandler = new WebSocketHandler.Simple(EchoWebSocket.class);
wsHandler.setHandler(new DefaultHandler());
_server.setHandler(wsHandler);

View File

@ -31,8 +31,6 @@ import java.util.concurrent.atomic.AtomicLong;
import java.util.zip.Deflater;
import java.util.zip.Inflater;
import javax.servlet.http.HttpServletRequest;
import org.eclipse.jetty.server.SelectChannelConnector;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.server.handler.DefaultHandler;
@ -195,19 +193,28 @@ public class WebSocketMessageRFC6455Test
WebSocketHandler wsHandler = new WebSocketHandler()
{
@Override
public WebSocket doWebSocketConnect(HttpServletRequest request, String protocol)
public void registerWebSockets(WebSocketServerFactory factory)
{
__textCount.set(0);
__serverWebSocket = new TestWebSocket();
__serverWebSocket._onConnect = ("onConnect".equals(protocol));
__serverWebSocket._echo = ("echo".equals(protocol));
__serverWebSocket._aggregate = ("aggregate".equals(protocol));
__serverWebSocket._latch = ("latch".equals(protocol));
if (__serverWebSocket._latch)
factory.register(TestWebSocket.class);
factory.setCreator(new WebSocketCreator()
{
__latch = new CountDownLatch(1);
}
return __serverWebSocket;
@Override
public Object createWebSocket(WebSocketRequest req, WebSocketResponse resp)
{
__textCount.set(0);
__serverWebSocket = new TestWebSocket();
__serverWebSocket._onConnect = req.hasSubProtocol("onConnect");
__serverWebSocket._echo = req.hasSubProtocol("echo");
__serverWebSocket._aggregate = req.hasSubProtocol("aggregate");
__serverWebSocket._latch = req.hasSubProtocol("latch");
if (__serverWebSocket._latch)
{
__latch = new CountDownLatch(1);
}
return __serverWebSocket;
}
});
}
};
wsHandler.getWebSocketFactory().getPolicy().setBufferSize(8192);

View File

@ -20,14 +20,13 @@ import java.util.Arrays;
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.ssl.SslSelectChannelConnector;
import org.eclipse.jetty.toolchain.test.MavenTestingUtils;
import org.eclipse.jetty.util.ssl.SslContextFactory;
import org.eclipse.jetty.util.thread.QueuedThreadPool;
import org.eclipse.jetty.websocket.WebSocket;
import org.eclipse.jetty.websocket.annotations.MyEchoSocket;
import org.junit.After;
import org.junit.Assert;
import org.junit.Test;
@ -37,7 +36,7 @@ public class WebSocketOverSSLTest
private Server _server;
private int _port;
private QueuedThreadPool _threadPool;
// private WebSocketClientFactory _wsFactory;
// private WebSocketClientFactory _wsFactory;
private WebSocket.Connection _connection;
@After
@ -48,8 +47,8 @@ public class WebSocketOverSSLTest
_connection.close();
}
// if (_wsFactory != null)
// _wsFactory.stop();
// if (_wsFactory != null)
// _wsFactory.stop();
if (_threadPool != null)
{
@ -71,18 +70,18 @@ public class WebSocketOverSSLTest
_threadPool.setName("wsc-" + _threadPool.getName());
_threadPool.start();
// _wsFactory = new WebSocketClientFactory(_threadPool, new ZeroMasker());
// SslContextFactory cf = _wsFactory.getSslContextFactory();
// cf.setKeyStorePath(MavenTestingUtils.getTestResourceFile("keystore").getAbsolutePath());
// cf.setKeyStorePassword("storepwd");
// cf.setKeyManagerPassword("keypwd");
// _wsFactory.start();
// _wsFactory = new WebSocketClientFactory(_threadPool, new ZeroMasker());
// SslContextFactory cf = _wsFactory.getSslContextFactory();
// cf.setKeyStorePath(MavenTestingUtils.getTestResourceFile("keystore").getAbsolutePath());
// cf.setKeyStorePassword("storepwd");
// cf.setKeyManagerPassword("keypwd");
// _wsFactory.start();
// WebSocketClient client = new WebSocketClient(_wsFactory);
// _connection = client.open(new URI("wss://localhost:" + _port), webSocket).get(5, TimeUnit.SECONDS);
// WebSocketClient client = new WebSocketClient(_wsFactory);
// _connection = client.open(new URI("wss://localhost:" + _port), webSocket).get(5, TimeUnit.SECONDS);
}
private void startServer(final WebSocket webSocket) throws Exception
private void startServer(final Class<?> websocketPojo) throws Exception
{
_server = new Server();
SslSelectChannelConnector connector = new SslSelectChannelConnector();
@ -91,14 +90,7 @@ public class WebSocketOverSSLTest
cf.setKeyStorePath(MavenTestingUtils.getTestResourceFile("keystore").getAbsolutePath());
cf.setKeyStorePassword("storepwd");
cf.setKeyManagerPassword("keypwd");
_server.setHandler(new WebSocketHandler()
{
@Override
public WebSocket doWebSocketConnect(HttpServletRequest request, String protocol)
{
return webSocket;
}
});
_server.setHandler(new WebSocketHandler.Simple(websocketPojo));
_server.start();
_port = connector.getLocalPort();
}
@ -106,34 +98,7 @@ public class WebSocketOverSSLTest
@Test
public void testManyMessages() throws Exception
{
startServer(new WebSocket.OnTextMessage()
{
private Connection connection;
@Override
public void onClose(int closeCode, String message)
{
}
@Override
public void onMessage(String data)
{
try
{
connection.sendMessage(data);
}
catch (IOException x)
{
x.printStackTrace();
}
}
@Override
public void onOpen(Connection connection)
{
this.connection = connection;
}
});
startServer(MyEchoSocket.class);
int count = 1000;
final CountDownLatch clientLatch = new CountDownLatch(count);
startClient(new WebSocket.OnTextMessage()
@ -156,14 +121,14 @@ public class WebSocketOverSSLTest
});
char[] chars = new char[256];
Arrays.fill(chars, 'x');
Arrays.fill(chars,'x');
String message = new String(chars);
for (int i = 0; i < count; ++i)
{
_connection.sendMessage(message);
}
Assert.assertTrue(clientLatch.await(20, TimeUnit.SECONDS));
Assert.assertTrue(clientLatch.await(20,TimeUnit.SECONDS));
// While messages may have all arrived, the SSL close alert
// may be in the way so give some time for it to be processed.
@ -173,61 +138,61 @@ public class WebSocketOverSSLTest
@Test
public void testWebSocketOverSSL() throws Exception
{
final String message = "message";
final CountDownLatch serverLatch = new CountDownLatch(1);
startServer(new WebSocket.OnTextMessage()
{
private Connection connection;
@Override
public void onClose(int closeCode, String message)
{
}
@Override
public void onMessage(String data)
{
try
{
Assert.assertEquals(message, data);
connection.sendMessage(data);
serverLatch.countDown();
}
catch (IOException x)
{
x.printStackTrace();
}
}
@Override
public void onOpen(Connection connection)
{
this.connection = connection;
}
});
final CountDownLatch clientLatch = new CountDownLatch(1);
startClient(new WebSocket.OnTextMessage()
{
@Override
public void onClose(int closeCode, String message)
{
}
@Override
public void onMessage(String data)
{
Assert.assertEquals(message, data);
clientLatch.countDown();
}
@Override
public void onOpen(Connection connection)
{
}
});
_connection.sendMessage(message);
Assert.assertTrue(serverLatch.await(5, TimeUnit.SECONDS));
Assert.assertTrue(clientLatch.await(5, TimeUnit.SECONDS));
// final String message = "message";
// final CountDownLatch serverLatch = new CountDownLatch(1);
// startServer(new WebSocket.OnTextMessage()
// {
// private Connection connection;
//
// @Override
// public void onClose(int closeCode, String message)
// {
// }
//
// @Override
// public void onMessage(String data)
// {
// try
// {
// Assert.assertEquals(message,data);
// connection.sendMessage(data);
// serverLatch.countDown();
// }
// catch (IOException x)
// {
// x.printStackTrace();
// }
// }
//
// @Override
// public void onOpen(Connection connection)
// {
// this.connection = connection;
// }
// });
// final CountDownLatch clientLatch = new CountDownLatch(1);
// startClient(new WebSocket.OnTextMessage()
// {
// @Override
// public void onClose(int closeCode, String message)
// {
// }
//
// @Override
// public void onMessage(String data)
// {
// Assert.assertEquals(message,data);
// clientLatch.countDown();
// }
//
// @Override
// public void onOpen(Connection connection)
// {
// }
// });
// _connection.sendMessage(message);
//
// Assert.assertTrue(serverLatch.await(5,TimeUnit.SECONDS));
// Assert.assertTrue(clientLatch.await(5,TimeUnit.SECONDS));
}
}

View File

@ -1,193 +0,0 @@
/*******************************************************************************
* 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.server;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import javax.servlet.http.HttpServletRequest;
import org.eclipse.jetty.server.SelectChannelConnector;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.server.handler.HandlerCollection;
import org.eclipse.jetty.servlet.ServletContextHandler;
import org.eclipse.jetty.servlet.ServletHolder;
import org.eclipse.jetty.websocket.WebSocket;
import org.junit.After;
import org.junit.Assert;
import org.junit.Test;
public class WebSocketRedeployTest
{
private Server server;
private ServletContextHandler context;
private String uri;
// private WebSocketClientFactory wsFactory;
@After
public void destroy() throws Exception
{
// if (wsFactory != null)
// {
// wsFactory.stop();
// }
if (server != null)
{
server.stop();
server.join();
}
}
public void init(final WebSocket webSocket) throws Exception
{
server = new Server();
SelectChannelConnector connector = new SelectChannelConnector();
// connector.setPort(8080);
server.addConnector(connector);
HandlerCollection handlers = new HandlerCollection();
server.setHandler(handlers);
String contextPath = "/test_context";
context = new ServletContextHandler(handlers, contextPath, ServletContextHandler.SESSIONS);
WebSocketServlet servlet = new WebSocketServlet()
{
@Override
public WebSocket doWebSocketConnect(HttpServletRequest request, String protocol)
{
return webSocket;
}
};
String servletPath = "/websocket";
context.addServlet(new ServletHolder(servlet), servletPath);
server.start();
uri = "ws://localhost:" + connector.getLocalPort() + contextPath + servletPath;
// wsFactory = new WebSocketClientFactory();
// wsFactory.start();
}
@Test
public void testStoppingClientFactoryClosesConnections() throws Exception
{
final CountDownLatch openLatch = new CountDownLatch(2);
final CountDownLatch closeLatch = new CountDownLatch(2);
init(new WebSocket.OnTextMessage()
{
@Override
public void onClose(int closeCode, String message)
{
closeLatch.countDown();
}
@Override
public void onMessage(String data)
{
}
@Override
public void onOpen(Connection connection)
{
openLatch.countDown();
}
});
// WebSocketClient client = wsFactory.newWebSocketClient();
// client.open(new URI(uri), new WebSocket.OnTextMessage()
// {
// @Override
// public void onClose(int closeCode, String message)
// {
// closeLatch.countDown();
// }
//
// @Override
// public void onMessage(String data)
// {
// }
//
// @Override
// public void onOpen(Connection connection)
// {
// openLatch.countDown();
// }
// }, 5, TimeUnit.SECONDS);
Assert.assertTrue(openLatch.await(5, TimeUnit.SECONDS));
// wsFactory.stop();
Assert.assertTrue(closeLatch.await(5, TimeUnit.SECONDS));
}
@Test
public void testStoppingContextClosesConnections() throws Exception
{
final CountDownLatch openLatch = new CountDownLatch(2);
final CountDownLatch closeLatch = new CountDownLatch(2);
init(new WebSocket.OnTextMessage()
{
@Override
public void onClose(int closeCode, String message)
{
closeLatch.countDown();
}
@Override
public void onMessage(String data)
{
}
@Override
public void onOpen(Connection connection)
{
openLatch.countDown();
}
});
// WebSocketClient client = wsFactory.newWebSocketClient();
// client.open(new URI(uri), new WebSocket.OnTextMessage()
// {
// @Override
// public void onClose(int closeCode, String message)
// {
// closeLatch.countDown();
// }
//
// @Override
// public void onMessage(String data)
// {
// }
//
// @Override
// public void onOpen(Connection connection)
// {
// openLatch.countDown();
// }
// }, 5, TimeUnit.SECONDS);
Assert.assertTrue(openLatch.await(5, TimeUnit.SECONDS));
context.stop();
Assert.assertTrue(closeLatch.await(5, TimeUnit.SECONDS));
}
}

View File

@ -13,8 +13,6 @@ import java.net.SocketAddress;
import java.net.URI;
import java.nio.ByteBuffer;
import javax.servlet.http.HttpServletRequest;
import org.eclipse.jetty.io.ByteBufferPool;
import org.eclipse.jetty.io.StandardByteBufferPool;
import org.eclipse.jetty.server.SelectChannelConnector;
@ -23,9 +21,9 @@ import org.eclipse.jetty.servlet.ServletContextHandler;
import org.eclipse.jetty.servlet.ServletHolder;
import org.eclipse.jetty.util.BufferUtil;
import org.eclipse.jetty.util.IO;
import org.eclipse.jetty.websocket.WebSocket;
import org.eclipse.jetty.websocket.WebSocketGeneratorRFC6455Test;
import org.eclipse.jetty.websocket.api.StatusCode;
import org.eclipse.jetty.websocket.api.WebSocketAdapter;
import org.eclipse.jetty.websocket.api.WebSocketPolicy;
import org.eclipse.jetty.websocket.frames.CloseFrame;
import org.eclipse.jetty.websocket.frames.TextFrame;
@ -49,28 +47,20 @@ public class WebSocketServletRFCTest
private static class RFCServlet extends WebSocketServlet
{
@Override
public WebSocket doWebSocketConnect(HttpServletRequest request, String protocol)
public void registerWebSockets(WebSocketServerFactory factory)
{
return new RFCSocket();
factory.register(RFCSocket.class);
}
}
private static class RFCSocket implements WebSocket, WebSocket.OnTextMessage
private static class RFCSocket extends WebSocketAdapter
{
private Connection conn;
@Override
public void onClose(int closeCode, String message)
{
this.conn = null;
}
@Override
public void onMessage(String data)
public void onWebSocketText(String message)
{
// Test the RFC 6455 close code 1011 that should close
// trigger a WebSocket server terminated close.
if (data.equals("CRASH"))
if (message.equals("CRASH"))
{
System.out.printf("Got OnTextMessage");
throw new RuntimeException("Something bad happened");
@ -79,20 +69,13 @@ public class WebSocketServletRFCTest
// echo the message back.
try
{
conn.sendMessage(data);
getConnection().write(message);
}
catch (IOException e)
{
e.printStackTrace(System.err);
}
}
@Override
public void onOpen(Connection connection)
{
this.conn = connection;
}
}
private static Server server;

View File

@ -0,0 +1,42 @@
package org.eclipse.jetty.websocket.server.examples;
import java.io.IOException;
import org.eclipse.jetty.websocket.api.WebSocketAdapter;
public class BasicEchoSocket extends WebSocketAdapter
{
@Override
public void onWebSocketBinary(byte[] payload, int offset, int len)
{
if (isNotConnected())
{
return;
}
try
{
getConnection().write(payload,offset,len);
}
catch (IOException e)
{
e.printStackTrace();
}
}
@Override
public void onWebSocketText(String message)
{
if (isNotConnected())
{
return;
}
try
{
getConnection().write(message);
}
catch (IOException e)
{
e.printStackTrace();
}
}
}

View File

@ -1,8 +1,6 @@
package org.eclipse.jetty.websocket.server.examples;
import javax.servlet.http.HttpServletRequest;
import org.eclipse.jetty.websocket.WebSocket;
import org.eclipse.jetty.websocket.server.WebSocketServerFactory;
import org.eclipse.jetty.websocket.server.WebSocketServlet;
/**
@ -12,8 +10,8 @@ import org.eclipse.jetty.websocket.server.WebSocketServlet;
public class MyEchoServlet extends WebSocketServlet
{
@Override
public WebSocket doWebSocketConnect(HttpServletRequest request, String protocol)
public void registerWebSockets(WebSocketServerFactory factory)
{
return new MyEchoSocket();
factory.register(MyEchoSocket.class);
}
}

View File

@ -2,35 +2,26 @@ package org.eclipse.jetty.websocket.server.examples;
import java.io.IOException;
import org.eclipse.jetty.websocket.WebSocket;
import org.eclipse.jetty.websocket.api.WebSocketAdapter;
public class MyEchoSocket implements WebSocket, WebSocket.OnTextMessage
public class MyEchoSocket extends WebSocketAdapter
{
private Connection conn;
@Override
public void onClose(int closeCode, String message)
public void onWebSocketText(String message)
{
/* do nothing */
}
if (isNotConnected())
{
return;
}
@Override
public void onMessage(String data)
{
try
{
// echo the data back
conn.sendMessage(data);
getConnection().write(message);
}
catch (IOException e)
{
e.printStackTrace();
}
}
@Override
public void onOpen(Connection connection)
{
this.conn = connection;
}
}

View File

@ -0,0 +1,51 @@
package org.eclipse.jetty.websocket.server.examples.echo;
import java.io.IOException;
import java.nio.ByteBuffer;
import org.eclipse.jetty.websocket.annotations.OnWebSocketBinary;
import org.eclipse.jetty.websocket.annotations.OnWebSocketText;
import org.eclipse.jetty.websocket.annotations.WebSocket;
import org.eclipse.jetty.websocket.api.WebSocketConnection;
/**
* Example Socket for echoing back Big data using the Annotation techniques along with stateless techniques.
*/
@WebSocket(maxTextSize = 64 * 1024, maxBinarySize = 64 * 1024)
public class BigEchoSocket
{
@OnWebSocketBinary
public void onBinary(WebSocketConnection conn, ByteBuffer buffer)
{
if (conn.isOpen())
{
return;
}
try
{
buffer.flip(); // flip the incoming buffer to write mode
conn.write(buffer);
}
catch (IOException e)
{
e.printStackTrace();
}
}
@OnWebSocketText
public void onText(WebSocketConnection conn, String message)
{
if (conn.isOpen())
{
return;
}
try
{
conn.write(message);
}
catch (IOException e)
{
e.printStackTrace();
}
}
}

View File

@ -0,0 +1,82 @@
package org.eclipse.jetty.websocket.server.examples.echo;
import java.nio.ByteBuffer;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import org.eclipse.jetty.websocket.annotations.WebSocket;
import org.eclipse.jetty.websocket.api.WebSocketConnection;
import org.eclipse.jetty.websocket.frames.PingFrame;
@WebSocket
public class EchoBroadcastPingSocket extends EchoBroadcastSocket
{
private class KeepAlive extends Thread
{
private CountDownLatch latch;
private WebSocketConnection getConnection()
{
return EchoBroadcastPingSocket.this.conn;
}
@Override
public void run()
{
try
{
while (!latch.await(10,TimeUnit.SECONDS))
{
System.err.println("Ping " + getConnection());
PingFrame ping = new PingFrame();
ByteBuffer payload = ByteBuffer.allocate(3);
payload.put((byte)1);
payload.put((byte)2);
payload.put((byte)3);
ping.setPayload(payload);
getConnection().write(ping);
}
}
catch (Exception e)
{
e.printStackTrace();
}
}
public void shutdown()
{
if (latch != null)
{
latch.countDown();
}
}
@Override
public synchronized void start()
{
latch = new CountDownLatch(1);
super.start();
}
}
private final KeepAlive keepAlive; // A dedicated thread is not a good way to do this
public EchoBroadcastPingSocket()
{
keepAlive = new KeepAlive();
}
@Override
public void onClose(int statusCode, String reason)
{
keepAlive.shutdown();
super.onClose(statusCode,reason);
}
@Override
public void onOpen(WebSocketConnection conn)
{
keepAlive.start();
super.onOpen(conn);
}
}

View File

@ -0,0 +1,66 @@
package org.eclipse.jetty.websocket.server.examples.echo;
import java.io.IOException;
import java.util.concurrent.ConcurrentLinkedQueue;
import org.eclipse.jetty.websocket.annotations.OnWebSocketBinary;
import org.eclipse.jetty.websocket.annotations.OnWebSocketClose;
import org.eclipse.jetty.websocket.annotations.OnWebSocketConnect;
import org.eclipse.jetty.websocket.annotations.OnWebSocketText;
import org.eclipse.jetty.websocket.annotations.WebSocket;
import org.eclipse.jetty.websocket.api.WebSocketConnection;
@WebSocket
public class EchoBroadcastSocket
{
private static final ConcurrentLinkedQueue<EchoBroadcastSocket> BROADCAST = new ConcurrentLinkedQueue<EchoBroadcastSocket>();
protected WebSocketConnection conn;
@OnWebSocketBinary
public void onBinary(byte buf[], int offset, int len)
{
for (EchoBroadcastSocket sock : BROADCAST)
{
try
{
sock.conn.write(buf,offset,len);
}
catch (IOException e)
{
BROADCAST.remove(sock);
e.printStackTrace();
}
}
}
@OnWebSocketClose
public void onClose(int statusCode, String reason)
{
BROADCAST.remove(this);
}
@OnWebSocketConnect
public void onOpen(WebSocketConnection conn)
{
this.conn = conn;
BROADCAST.add(this);
}
@OnWebSocketText
public void onText(String text)
{
for (EchoBroadcastSocket sock : BROADCAST)
{
try
{
sock.conn.write(text);
}
catch (IOException e)
{
BROADCAST.remove(sock);
e.printStackTrace();
}
}
}
}

View File

@ -0,0 +1,50 @@
package org.eclipse.jetty.websocket.server.examples.echo;
import org.eclipse.jetty.websocket.server.WebSocketCreator;
import org.eclipse.jetty.websocket.server.WebSocketRequest;
import org.eclipse.jetty.websocket.server.WebSocketResponse;
/**
* Example of setting up a creator to create appropriately via the proposed and negotiated protocols.
*/
public class EchoCreator implements WebSocketCreator
{
private BigEchoSocket bigEchoSocket = new BigEchoSocket();
private EchoFragmentSocket echoFragmentSocket = new EchoFragmentSocket();
private LogSocket logSocket = new LogSocket();
@Override
public Object createWebSocket(WebSocketRequest req, WebSocketResponse resp)
{
for (String protocol : req.getSubProtocols())
{
switch (protocol)
{
case "org.ietf.websocket.test-echo":
case "echo":
resp.setAcceptedSubProtocol(protocol);
// TODO: how is this different than "echo-assemble"?
return bigEchoSocket;
case "org.ietf.websocket.test-echo-broadcast":
case "echo-broadcast":
resp.setAcceptedSubProtocol(protocol);
return new EchoBroadcastSocket();
case "echo-broadcast-ping":
resp.setAcceptedSubProtocol(protocol);
return new EchoBroadcastPingSocket();
case "org.ietf.websocket.test-echo-assemble":
case "echo-assemble":
resp.setAcceptedSubProtocol(protocol);
// TODO: how is this different than "test-echo"?
return bigEchoSocket;
case "org.ietf.websocket.test-echo-fragment":
case "echo-fragment":
resp.setAcceptedSubProtocol(protocol);
return echoFragmentSocket;
default:
return logSocket;
}
}
return null;
}
}

View File

@ -0,0 +1,51 @@
package org.eclipse.jetty.websocket.server.examples.echo;
import java.io.IOException;
import java.nio.ByteBuffer;
import org.eclipse.jetty.util.BufferUtil;
import org.eclipse.jetty.util.Callback;
import org.eclipse.jetty.util.FutureCallback;
import org.eclipse.jetty.websocket.annotations.OnWebSocketFrame;
import org.eclipse.jetty.websocket.annotations.WebSocket;
import org.eclipse.jetty.websocket.api.WebSocketConnection;
import org.eclipse.jetty.websocket.frames.DataFrame;
/**
* Echo back the incoming text or binary as 2 frames of (roughly) equal size.
*/
@WebSocket
public class EchoFragmentSocket
{
@OnWebSocketFrame
public void onFrame(WebSocketConnection conn, DataFrame data)
{
ByteBuffer payload = data.getPayload();
BufferUtil.flipToFlush(payload,0);
int half = payload.remaining() / 2;
ByteBuffer buf1 = payload.slice();
ByteBuffer buf2 = payload.slice();
buf1.limit(half);
buf2.position(half);
DataFrame d1 = new DataFrame(data.getOpCode());
d1.setFin(false);
d1.setPayload(buf1);
DataFrame d2 = new DataFrame(data.getOpCode());
d2.setFin(true);
d2.setPayload(buf2);
Callback<Void> nop = new FutureCallback<>();
try
{
conn.write(null,nop,d1,d2);
}
catch (IOException e)
{
e.printStackTrace(System.err);
}
}
}

View File

@ -0,0 +1,30 @@
package org.eclipse.jetty.websocket.server.examples.echo;
import java.io.IOException;
import org.eclipse.jetty.websocket.annotations.OnWebSocketFrame;
import org.eclipse.jetty.websocket.annotations.WebSocket;
import org.eclipse.jetty.websocket.api.WebSocketConnection;
import org.eclipse.jetty.websocket.frames.BaseFrame;
@WebSocket
public class EchoFrameSocket
{
@OnWebSocketFrame
public void onFrame(WebSocketConnection conn, BaseFrame frame)
{
if (!conn.isOpen())
{
return;
}
try
{
conn.write(frame);
}
catch (IOException e)
{
e.printStackTrace(System.err);
}
}
}

View File

@ -0,0 +1,124 @@
package org.eclipse.jetty.websocket.server.examples.echo;
import org.eclipse.jetty.server.SelectChannelConnector;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.server.handler.ResourceHandler;
import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger;
import org.eclipse.jetty.websocket.server.WebSocketHandler;
import org.eclipse.jetty.websocket.server.WebSocketServerFactory;
/**
* Example server using WebSocket and core Jetty Handlers
*/
public class ExampleEchoServer
{
private static final Logger LOG = Log.getLogger(ExampleEchoServer.class);
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();
}
}
ExampleEchoServer server = new ExampleEchoServer(port);
server.setVerbose(verbose);
server.setResourceBase(docroot);
server.runForever();
}
catch (Exception e)
{
LOG.warn(e);
}
}
private static void usage()
{
System.err.println("java -cp{CLASSPATH} " + ExampleEchoServer.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);
}
private Server server;
private SelectChannelConnector _connector;
private boolean _verbose;
private WebSocketHandler _wsHandler;
private ResourceHandler _rHandler;
public ExampleEchoServer(int port)
{
server = new Server();
_connector = new SelectChannelConnector();
_connector.setPort(port);
server.addConnector(_connector);
_wsHandler = new WebSocketHandler()
{
@Override
public void registerWebSockets(WebSocketServerFactory factory)
{
factory.register(BigEchoSocket.class);
factory.setCreator(new EchoCreator());
}
};
server.setHandler(_wsHandler);
_rHandler = new ResourceHandler();
_rHandler.setDirectoriesListed(true);
_rHandler.setResourceBase("src/test/webapp");
_wsHandler.setHandler(_rHandler);
}
public String getResourceBase()
{
return _rHandler.getResourceBase();
}
public boolean isVerbose()
{
return _verbose;
}
public void runForever() throws Exception
{
server.start();
server.join();
}
public void setResourceBase(String dir)
{
_rHandler.setResourceBase(dir);
}
public void setVerbose(boolean verbose)
{
_verbose = verbose;
}
}

View File

@ -0,0 +1,76 @@
package org.eclipse.jetty.websocket.server.examples.echo;
import org.eclipse.jetty.websocket.api.WebSocketConnection;
import org.eclipse.jetty.websocket.api.WebSocketException;
import org.eclipse.jetty.websocket.api.WebSocketListener;
public class LogSocket implements WebSocketListener
{
private boolean verbose = false;
public boolean isVerbose()
{
return verbose;
}
@Override
public void onWebSocketBinary(byte[] payload, int offset, int len)
{
if (verbose)
{
System.err.printf("onWebSocketBinary(byte[%d] payload, %d, %d)%n",payload.length,offset,len);
}
}
@Override
public void onWebSocketClose(int statusCode, String reason)
{
if (verbose)
{
System.err.printf("onWebSocketClose(%d, %s)%n",statusCode,quote(reason));
}
}
@Override
public void onWebSocketConnect(WebSocketConnection connection)
{
if (verbose)
{
System.err.printf("onWebSocketConnect(%s)%n",connection);
}
}
@Override
public void onWebSocketException(WebSocketException error)
{
if (verbose)
{
System.err.printf("onWebSocketException((%s) %s)%n",error.getClass().getName(),error.getMessage());
error.printStackTrace(System.err);
}
}
@Override
public void onWebSocketText(String message)
{
if (verbose)
{
System.err.printf("onWebSocketText(%s)%n",quote(message));
}
}
private String quote(String str)
{
if (str == null)
{
return "<null>";
}
return '"' + str + '"';
}
public void setVerbose(boolean verbose)
{
this.verbose = verbose;
}
}

View File

@ -16,21 +16,17 @@
package org.eclipse.jetty.websocket.server.helper;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.eclipse.jetty.websocket.WebSocket;
import org.eclipse.jetty.websocket.server.WebSocketServerFactory;
import org.eclipse.jetty.websocket.server.WebSocketServlet;
@SuppressWarnings("serial")
public class WebSocketCaptureServlet extends WebSocketServlet
{
public List<CaptureSocket> captures = new ArrayList<CaptureSocket>();;
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException
{
@ -38,10 +34,8 @@ public class WebSocketCaptureServlet extends WebSocketServlet
}
@Override
public WebSocket doWebSocketConnect(HttpServletRequest request, String protocol)
public void registerWebSockets(WebSocketServerFactory factory)
{
CaptureSocket capture = new CaptureSocket();
captures.add(capture);
return capture;
factory.register(CaptureSocket.class);
}
}