Issue #207 - Support javax.websocket version 1.1

WIP
This commit is contained in:
Joakim Erdfelt 2016-04-27 15:05:05 -07:00
parent 31328b8f66
commit 8784a1f850
21 changed files with 183 additions and 88 deletions

View File

@ -299,6 +299,8 @@ public class WebSocketPolicy
*/ */
public void setIdleTimeout(long ms) public void setIdleTimeout(long ms)
{ {
if(ms < -1) return; // no change (likely came from annotation)
boolean dirty = (this.idleTimeout != ms); boolean dirty = (this.idleTimeout != ms);
assertGreaterThan("IdleTimeout",ms,0); assertGreaterThan("IdleTimeout",ms,0);
this.idleTimeout = ms; this.idleTimeout = ms;
@ -314,6 +316,8 @@ public class WebSocketPolicy
*/ */
public void setInputBufferSize(int size) public void setInputBufferSize(int size)
{ {
if(size < 0) return; // no change (likely came from annotation)
boolean dirty = (this.inputBufferSize != size); boolean dirty = (this.inputBufferSize != size);
assertGreaterThan("InputBufferSize",size,1); assertGreaterThan("InputBufferSize",size,1);
assertLessThan("InputBufferSize",size,"MaxTextMessageBufferSize",maxTextMessageBufferSize); assertLessThan("InputBufferSize",size,"MaxTextMessageBufferSize",maxTextMessageBufferSize);
@ -352,6 +356,8 @@ public class WebSocketPolicy
*/ */
public void setMaxBinaryMessageSize(int size) public void setMaxBinaryMessageSize(int size)
{ {
if(size < 0) return; // no change (likely came from annotation)
boolean dirty = (this.maxBinaryMessageSize != size); boolean dirty = (this.maxBinaryMessageSize != size);
assertGreaterThan("MaxBinaryMessageSize",size,1); assertGreaterThan("MaxBinaryMessageSize",size,1);
@ -388,6 +394,8 @@ public class WebSocketPolicy
*/ */
public void setMaxTextMessageSize(int size) public void setMaxTextMessageSize(int size)
{ {
if(size < 0) return; // no change (likely came from annotation)
boolean dirty = (this.maxTextMessageSize != size); boolean dirty = (this.maxTextMessageSize != size);
assertGreaterThan("MaxTextMessageSize",size,1); assertGreaterThan("MaxTextMessageSize",size,1);

View File

@ -36,42 +36,29 @@ import org.eclipse.jetty.websocket.api.StatusCode;
{ ElementType.TYPE }) { ElementType.TYPE })
public @interface WebSocket public @interface WebSocket
{ {
/* NOTE TO OTHER DEVELOPERS:
* If you change any of these default values,
* make sure you sync the values with WebSocketPolicy
*/
/** /**
* The size of the buffer used to read from the network layer. * The size of the buffer (in bytes) used to read from the network layer.
* <p>
* Default: 4096 (4 K)
*/ */
int inputBufferSize() default 4 * 1024; int inputBufferSize() default -2;
/** /**
* The maximum size of a binary message during parsing/generating. * The maximum size of a binary message (in bytes) during parsing/generating.
* <p> * <p>
* Binary messages over this maximum will result in a close code 1009 {@link StatusCode#MESSAGE_TOO_LARGE} * Binary messages over this maximum will result in a close code 1009 {@link StatusCode#MESSAGE_TOO_LARGE}
* <p>
* Default: 65536 (64 K)
*/ */
int maxBinaryMessageSize() default 64 * 1024; int maxBinaryMessageSize() default -2;
/** /**
* The time in ms (milliseconds) that a websocket may be idle before closing. * The time in ms (milliseconds) that a websocket may be idle before closing.
* <p>
* Default: 300000 (ms)
*/ */
int maxIdleTime() default 300_000; int maxIdleTime() default -2;
/** /**
* The maximum size of a text message during parsing/generating. * The maximum size of a text message during parsing/generating.
* <p> * <p>
* Text messages over this maximum will result in a close code 1009 {@link StatusCode#MESSAGE_TOO_LARGE} * Text messages over this maximum will result in a close code 1009 {@link StatusCode#MESSAGE_TOO_LARGE}
* <p>
* Default: 65536 (64 K)
*/ */
int maxTextMessageSize() default 64 * 1024; int maxTextMessageSize() default -2;
/** /**
* The output frame buffering mode. * The output frame buffering mode.

View File

@ -0,0 +1,46 @@
//
// ========================================================================
// Copyright (c) 1995-2016 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.websocket.common;
import java.lang.reflect.InvocationTargetException;
import org.eclipse.jetty.websocket.api.WebSocketException;
public class FunctionCallException extends WebSocketException
{
public FunctionCallException(String message, Throwable cause)
{
super(message, cause);
}
public FunctionCallException(Throwable cause)
{
super(cause);
}
public Throwable getInvokedCause()
{
Throwable cause = getCause();
if (cause instanceof InvocationTargetException)
{
return cause.getCause();
}
return cause;
}
}

View File

@ -367,6 +367,11 @@ public class Generator
} }
} }
public WebSocketBehavior getBehavior()
{
return behavior;
}
public ByteBufferPool getBufferPool() public ByteBufferPool getBufferPool()
{ {
return bufferPool; return bufferPool;

View File

@ -595,8 +595,9 @@ public class Parser
buffer.limit(limit); buffer.limit(limit);
buffer.position(buffer.position() + window.remaining()); buffer.position(buffer.position() + window.remaining());
if (LOG.isDebugEnabled()) { if (LOG.isDebugEnabled())
LOG.debug("{} Window: {}",policy.getBehavior(),BufferUtil.toDetailString(window)); {
LOG.debug("{} Raw Payload: {}",policy.getBehavior(),BufferUtil.toDetailString(window));
} }
maskProcessor.process(window); maskProcessor.process(window);

View File

@ -94,6 +94,7 @@ import org.eclipse.jetty.websocket.common.message.ReaderMessageSink;
import org.eclipse.jetty.websocket.common.message.StringMessageSink; import org.eclipse.jetty.websocket.common.message.StringMessageSink;
import org.eclipse.jetty.websocket.common.scopes.WebSocketContainerScope; import org.eclipse.jetty.websocket.common.scopes.WebSocketContainerScope;
import org.eclipse.jetty.websocket.common.scopes.WebSocketSessionScope; import org.eclipse.jetty.websocket.common.scopes.WebSocketSessionScope;
import org.eclipse.jetty.websocket.common.util.DynamicArgsException;
import org.eclipse.jetty.websocket.common.util.ReflectUtils; import org.eclipse.jetty.websocket.common.util.ReflectUtils;
@ManagedObject("A Jetty WebSocket Session") @ManagedObject("A Jetty WebSocket Session")
@ -254,7 +255,7 @@ public class WebSocketSession extends ContainerLifeCycle implements Session, Rem
policy.setMaxBinaryMessageSize(websocket.maxBinaryMessageSize()); policy.setMaxBinaryMessageSize(websocket.maxBinaryMessageSize());
policy.setMaxTextMessageSize(websocket.maxTextMessageSize()); policy.setMaxTextMessageSize(websocket.maxTextMessageSize());
policy.setIdleTimeout(websocket.maxIdleTime()); policy.setIdleTimeout(websocket.maxIdleTime());
this.batchmode = websocket.batchMode(); this.batchmode = websocket.batchMode();
Method onmethod = null; Method onmethod = null;
@ -510,6 +511,22 @@ public class WebSocketSession extends ContainerLifeCycle implements Session, Rem
return connection.getMaxIdleTimeout(); return connection.getMaxIdleTimeout();
} }
private Throwable getInvokedCause(Throwable t)
{
if (t instanceof FunctionCallException)
{
return ((FunctionCallException) t).getInvokedCause();
}
else if (t instanceof DynamicArgsException)
{
Throwable cause = ((DynamicArgsException) t).getInvokedCause();
if (cause != null)
return cause;
}
return t;
}
@Override @Override
public InetSocketAddress getLocalAddress() public InetSocketAddress getLocalAddress()
{ {
@ -691,6 +708,11 @@ public class WebSocketSession extends ContainerLifeCycle implements Session, Rem
} }
} }
} }
else
{
if (LOG.isDebugEnabled())
LOG.debug("Discarding post EOF frame - {}", frame);
}
} }
catch (NotUtf8Exception e) catch (NotUtf8Exception e)
{ {
@ -703,25 +725,27 @@ public class WebSocketSession extends ContainerLifeCycle implements Session, Rem
} }
catch (Throwable t) catch (Throwable t)
{ {
LOG.warn("Unhandled Error (closing connection)",t); Throwable cause = getInvokedCause(t);
notifyError(t); LOG.warn("Unhandled Error (closing connection)",cause);
notifyError(cause);
// Unhandled Error, close the connection. // Unhandled Error, close the connection.
switch (policy.getBehavior()) switch (policy.getBehavior())
{ {
case SERVER: case SERVER:
close(StatusCode.SERVER_ERROR,t.getClass().getSimpleName()); close(StatusCode.SERVER_ERROR,cause.getClass().getSimpleName());
break; break;
case CLIENT: case CLIENT:
close(StatusCode.POLICY_VIOLATION,t.getClass().getSimpleName()); close(StatusCode.POLICY_VIOLATION,cause.getClass().getSimpleName());
break; break;
} }
} }
finally finally
{ {
// Unset active MessageSink if this was a fin frame // Unset active MessageSink if this was a fin frame
if (frame.isFin() && activeMessageSink != null) if (frame.getType().isData() && frame.isFin() && activeMessageSink != null)
activeMessageSink = null; activeMessageSink = null;
Thread.currentThread().setContextClassLoader(old); Thread.currentThread().setContextClassLoader(old);
@ -823,7 +847,7 @@ public class WebSocketSession extends ContainerLifeCycle implements Session, Rem
public void open() public void open()
{ {
if (LOG_OPEN.isDebugEnabled()) if (LOG_OPEN.isDebugEnabled())
LOG_OPEN.debug("[{}] {}.open()",policy.getBehavior(),this.getClass().getSimpleName()); LOG_OPEN.debug("[{}] {}.open()", policy.getBehavior(), this.getClass().getSimpleName());
if (remote != null) if (remote != null)
{ {
@ -837,9 +861,9 @@ public class WebSocketSession extends ContainerLifeCycle implements Session, Rem
connection.getIOState().onConnected(); connection.getIOState().onConnected();
// Connect remote // Connect remote
remote = new WebSocketRemoteEndpoint(connection,outgoingHandler,getBatchMode()); remote = new WebSocketRemoteEndpoint(connection, outgoingHandler, getBatchMode());
if (LOG_OPEN.isDebugEnabled()) if (LOG_OPEN.isDebugEnabled())
LOG_OPEN.debug("[{}] {}.open() remote={}",policy.getBehavior(),this.getClass().getSimpleName(),remote); LOG_OPEN.debug("[{}] {}.open() remote={}", policy.getBehavior(), this.getClass().getSimpleName(), remote);
// Open WebSocket // Open WebSocket
if (onOpenFunction != null) if (onOpenFunction != null)
@ -850,17 +874,21 @@ public class WebSocketSession extends ContainerLifeCycle implements Session, Rem
if (LOG.isDebugEnabled()) if (LOG.isDebugEnabled())
{ {
LOG.debug("open -> {}",dump()); LOG.debug("open -> {}", dump());
} }
} }
catch (CloseException ce) catch (CloseException ce)
{ {
LOG.warn(ce); LOG.warn(ce);
close(ce.getStatusCode(),ce.getMessage()); notifyError(ce.getCause());
close(ce.getStatusCode(), ce.getMessage());
} }
catch (Throwable t) catch (Throwable t)
{ {
LOG.warn(t); Throwable cause = getInvokedCause(t);
LOG.warn(cause);
notifyError(cause);
// Exception on end-user WS-Endpoint. // Exception on end-user WS-Endpoint.
// Fast-fail & close connection with reason. // Fast-fail & close connection with reason.
int statusCode = StatusCode.SERVER_ERROR; int statusCode = StatusCode.SERVER_ERROR;
@ -868,7 +896,7 @@ public class WebSocketSession extends ContainerLifeCycle implements Session, Rem
{ {
statusCode = StatusCode.POLICY_VIOLATION; statusCode = StatusCode.POLICY_VIOLATION;
} }
close(statusCode,t.getMessage()); close(statusCode,cause.getMessage());
} }
} }

View File

@ -23,9 +23,9 @@ import java.lang.reflect.Method;
import java.util.function.Function; import java.util.function.Function;
import org.eclipse.jetty.websocket.api.Session; import org.eclipse.jetty.websocket.api.Session;
import org.eclipse.jetty.websocket.api.WebSocketException;
import org.eclipse.jetty.websocket.api.annotations.OnWebSocketMessage; import org.eclipse.jetty.websocket.api.annotations.OnWebSocketMessage;
import org.eclipse.jetty.websocket.api.annotations.WebSocket; import org.eclipse.jetty.websocket.api.annotations.WebSocket;
import org.eclipse.jetty.websocket.common.FunctionCallException;
import org.eclipse.jetty.websocket.common.InvalidSignatureException; import org.eclipse.jetty.websocket.common.InvalidSignatureException;
import org.eclipse.jetty.websocket.common.util.DynamicArgs; import org.eclipse.jetty.websocket.common.util.DynamicArgs;
import org.eclipse.jetty.websocket.common.util.DynamicArgs.Arg; import org.eclipse.jetty.websocket.common.util.DynamicArgs.Arg;
@ -93,7 +93,7 @@ public class OnByteArrayFunction implements Function<byte[], Void>
} }
catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e)
{ {
throw new WebSocketException("Unable to call text message method " + ReflectUtils.toString(endpoint.getClass(), method), e); throw new FunctionCallException("Unable to call text message method " + ReflectUtils.toString(endpoint.getClass(), method), e);
} }
return null; return null;
} }

View File

@ -24,9 +24,9 @@ import java.nio.ByteBuffer;
import java.util.function.Function; import java.util.function.Function;
import org.eclipse.jetty.websocket.api.Session; import org.eclipse.jetty.websocket.api.Session;
import org.eclipse.jetty.websocket.api.WebSocketException;
import org.eclipse.jetty.websocket.api.annotations.OnWebSocketMessage; import org.eclipse.jetty.websocket.api.annotations.OnWebSocketMessage;
import org.eclipse.jetty.websocket.api.annotations.WebSocket; import org.eclipse.jetty.websocket.api.annotations.WebSocket;
import org.eclipse.jetty.websocket.common.FunctionCallException;
import org.eclipse.jetty.websocket.common.InvalidSignatureException; import org.eclipse.jetty.websocket.common.InvalidSignatureException;
import org.eclipse.jetty.websocket.common.util.DynamicArgs; import org.eclipse.jetty.websocket.common.util.DynamicArgs;
import org.eclipse.jetty.websocket.common.util.DynamicArgs.Arg; import org.eclipse.jetty.websocket.common.util.DynamicArgs.Arg;
@ -90,7 +90,7 @@ public class OnByteBufferFunction implements Function<ByteBuffer, Void>
} }
catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e)
{ {
throw new WebSocketException("Unable to call text message method " + ReflectUtils.toString(endpoint.getClass(), method), e); throw new FunctionCallException("Unable to call text message method " + ReflectUtils.toString(endpoint.getClass(), method), e);
} }
return null; return null;
} }

View File

@ -23,10 +23,10 @@ import java.lang.reflect.Method;
import java.util.function.Function; import java.util.function.Function;
import org.eclipse.jetty.websocket.api.Session; import org.eclipse.jetty.websocket.api.Session;
import org.eclipse.jetty.websocket.api.WebSocketException;
import org.eclipse.jetty.websocket.api.annotations.OnWebSocketClose; import org.eclipse.jetty.websocket.api.annotations.OnWebSocketClose;
import org.eclipse.jetty.websocket.api.annotations.WebSocket; import org.eclipse.jetty.websocket.api.annotations.WebSocket;
import org.eclipse.jetty.websocket.common.CloseInfo; import org.eclipse.jetty.websocket.common.CloseInfo;
import org.eclipse.jetty.websocket.common.FunctionCallException;
import org.eclipse.jetty.websocket.common.InvalidSignatureException; import org.eclipse.jetty.websocket.common.InvalidSignatureException;
import org.eclipse.jetty.websocket.common.util.DynamicArgs; import org.eclipse.jetty.websocket.common.util.DynamicArgs;
import org.eclipse.jetty.websocket.common.util.DynamicArgs.Arg; import org.eclipse.jetty.websocket.common.util.DynamicArgs.Arg;
@ -83,7 +83,7 @@ public class OnCloseFunction implements Function<CloseInfo, Void>
} }
catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e)
{ {
throw new WebSocketException("Unable to call close method " + ReflectUtils.toString(endpoint.getClass(), method), e); throw new FunctionCallException("Unable to call close method " + ReflectUtils.toString(endpoint.getClass(), method), e);
} }
return null; return null;
} }

View File

@ -23,9 +23,9 @@ import java.lang.reflect.Method;
import java.util.function.Function; import java.util.function.Function;
import org.eclipse.jetty.websocket.api.Session; import org.eclipse.jetty.websocket.api.Session;
import org.eclipse.jetty.websocket.api.WebSocketException;
import org.eclipse.jetty.websocket.api.annotations.OnWebSocketError; import org.eclipse.jetty.websocket.api.annotations.OnWebSocketError;
import org.eclipse.jetty.websocket.api.annotations.WebSocket; import org.eclipse.jetty.websocket.api.annotations.WebSocket;
import org.eclipse.jetty.websocket.common.FunctionCallException;
import org.eclipse.jetty.websocket.common.InvalidSignatureException; import org.eclipse.jetty.websocket.common.InvalidSignatureException;
import org.eclipse.jetty.websocket.common.util.DynamicArgs; import org.eclipse.jetty.websocket.common.util.DynamicArgs;
import org.eclipse.jetty.websocket.common.util.DynamicArgs.Arg; import org.eclipse.jetty.websocket.common.util.DynamicArgs.Arg;
@ -79,7 +79,7 @@ public class OnErrorFunction implements Function<Throwable, Void>
} }
catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e)
{ {
throw new WebSocketException("Unable to call error method " + ReflectUtils.toString(endpoint.getClass(), method), e); throw new FunctionCallException("Unable to call error method " + ReflectUtils.toString(endpoint.getClass(), method), e);
} }
return null; return null;
} }

View File

@ -23,10 +23,10 @@ import java.lang.reflect.Method;
import java.util.function.Function; import java.util.function.Function;
import org.eclipse.jetty.websocket.api.Session; import org.eclipse.jetty.websocket.api.Session;
import org.eclipse.jetty.websocket.api.WebSocketException;
import org.eclipse.jetty.websocket.api.annotations.OnWebSocketFrame; import org.eclipse.jetty.websocket.api.annotations.OnWebSocketFrame;
import org.eclipse.jetty.websocket.api.annotations.WebSocket; import org.eclipse.jetty.websocket.api.annotations.WebSocket;
import org.eclipse.jetty.websocket.api.extensions.Frame; import org.eclipse.jetty.websocket.api.extensions.Frame;
import org.eclipse.jetty.websocket.common.FunctionCallException;
import org.eclipse.jetty.websocket.common.InvalidSignatureException; import org.eclipse.jetty.websocket.common.InvalidSignatureException;
import org.eclipse.jetty.websocket.common.WebSocketFrame; import org.eclipse.jetty.websocket.common.WebSocketFrame;
import org.eclipse.jetty.websocket.common.util.DynamicArgs; import org.eclipse.jetty.websocket.common.util.DynamicArgs;
@ -82,7 +82,7 @@ public class OnFrameFunction implements Function<Frame, Void>
} }
catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e)
{ {
throw new WebSocketException("Unable to call frame method " + ReflectUtils.toString(endpoint.getClass(), method), e); throw new FunctionCallException("Unable to call frame method " + ReflectUtils.toString(endpoint.getClass(), method), e);
} }
return null; return null;
} }

View File

@ -24,9 +24,9 @@ import java.lang.reflect.Method;
import java.util.function.Function; import java.util.function.Function;
import org.eclipse.jetty.websocket.api.Session; import org.eclipse.jetty.websocket.api.Session;
import org.eclipse.jetty.websocket.api.WebSocketException;
import org.eclipse.jetty.websocket.api.annotations.OnWebSocketMessage; import org.eclipse.jetty.websocket.api.annotations.OnWebSocketMessage;
import org.eclipse.jetty.websocket.api.annotations.WebSocket; import org.eclipse.jetty.websocket.api.annotations.WebSocket;
import org.eclipse.jetty.websocket.common.FunctionCallException;
import org.eclipse.jetty.websocket.common.InvalidSignatureException; import org.eclipse.jetty.websocket.common.InvalidSignatureException;
import org.eclipse.jetty.websocket.common.util.DynamicArgs; import org.eclipse.jetty.websocket.common.util.DynamicArgs;
import org.eclipse.jetty.websocket.common.util.DynamicArgs.Arg; import org.eclipse.jetty.websocket.common.util.DynamicArgs.Arg;
@ -91,7 +91,7 @@ public class OnInputStreamFunction implements Function<InputStream, Void>
} }
catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e)
{ {
throw new WebSocketException("Unable to call text message method " + ReflectUtils.toString(endpoint.getClass(), method), e); throw new FunctionCallException("Unable to call text message method " + ReflectUtils.toString(endpoint.getClass(), method), e);
} }
return null; return null;
} }

View File

@ -23,9 +23,9 @@ import java.lang.reflect.Method;
import java.util.function.Function; import java.util.function.Function;
import org.eclipse.jetty.websocket.api.Session; import org.eclipse.jetty.websocket.api.Session;
import org.eclipse.jetty.websocket.api.WebSocketException;
import org.eclipse.jetty.websocket.api.annotations.OnWebSocketConnect; import org.eclipse.jetty.websocket.api.annotations.OnWebSocketConnect;
import org.eclipse.jetty.websocket.api.annotations.WebSocket; import org.eclipse.jetty.websocket.api.annotations.WebSocket;
import org.eclipse.jetty.websocket.common.FunctionCallException;
import org.eclipse.jetty.websocket.common.InvalidSignatureException; import org.eclipse.jetty.websocket.common.InvalidSignatureException;
import org.eclipse.jetty.websocket.common.util.DynamicArgs; import org.eclipse.jetty.websocket.common.util.DynamicArgs;
import org.eclipse.jetty.websocket.common.util.DynamicArgs.Arg; import org.eclipse.jetty.websocket.common.util.DynamicArgs.Arg;
@ -76,7 +76,7 @@ public class OnOpenFunction implements Function<Session, Void>
} }
catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e)
{ {
throw new WebSocketException("Unable to call method " + ReflectUtils.toString(endpoint.getClass(), method), e); throw new FunctionCallException("Unable to call method " + ReflectUtils.toString(endpoint.getClass(), method), e);
} }
return null; return null;
} }

View File

@ -24,9 +24,9 @@ import java.lang.reflect.Method;
import java.util.function.Function; import java.util.function.Function;
import org.eclipse.jetty.websocket.api.Session; import org.eclipse.jetty.websocket.api.Session;
import org.eclipse.jetty.websocket.api.WebSocketException;
import org.eclipse.jetty.websocket.api.annotations.OnWebSocketMessage; import org.eclipse.jetty.websocket.api.annotations.OnWebSocketMessage;
import org.eclipse.jetty.websocket.api.annotations.WebSocket; import org.eclipse.jetty.websocket.api.annotations.WebSocket;
import org.eclipse.jetty.websocket.common.FunctionCallException;
import org.eclipse.jetty.websocket.common.InvalidSignatureException; import org.eclipse.jetty.websocket.common.InvalidSignatureException;
import org.eclipse.jetty.websocket.common.util.DynamicArgs; import org.eclipse.jetty.websocket.common.util.DynamicArgs;
import org.eclipse.jetty.websocket.common.util.DynamicArgs.Arg; import org.eclipse.jetty.websocket.common.util.DynamicArgs.Arg;
@ -90,7 +90,7 @@ public class OnReaderFunction implements Function<Reader, Void>
} }
catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e)
{ {
throw new WebSocketException("Unable to call text message method " + ReflectUtils.toString(endpoint.getClass(), method), e); throw new FunctionCallException("Unable to call text message method " + ReflectUtils.toString(endpoint.getClass(), method), e);
} }
return null; return null;
} }

View File

@ -23,9 +23,9 @@ import java.lang.reflect.Method;
import java.util.function.Function; import java.util.function.Function;
import org.eclipse.jetty.websocket.api.Session; import org.eclipse.jetty.websocket.api.Session;
import org.eclipse.jetty.websocket.api.WebSocketException;
import org.eclipse.jetty.websocket.api.annotations.OnWebSocketMessage; import org.eclipse.jetty.websocket.api.annotations.OnWebSocketMessage;
import org.eclipse.jetty.websocket.api.annotations.WebSocket; import org.eclipse.jetty.websocket.api.annotations.WebSocket;
import org.eclipse.jetty.websocket.common.FunctionCallException;
import org.eclipse.jetty.websocket.common.InvalidSignatureException; import org.eclipse.jetty.websocket.common.InvalidSignatureException;
import org.eclipse.jetty.websocket.common.util.DynamicArgs; import org.eclipse.jetty.websocket.common.util.DynamicArgs;
import org.eclipse.jetty.websocket.common.util.DynamicArgs.Arg; import org.eclipse.jetty.websocket.common.util.DynamicArgs.Arg;
@ -89,7 +89,7 @@ public class OnTextFunction implements Function<String, Void>
} }
catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e)
{ {
throw new WebSocketException("Unable to call text message method " + ReflectUtils.toString(endpoint.getClass(), method), e); throw new FunctionCallException("Unable to call text message method " + ReflectUtils.toString(endpoint.getClass(), method), e);
} }
return null; return null;
} }

View File

@ -423,7 +423,7 @@ public class FrameFlusher
public String toString() public String toString()
{ {
ByteBuffer aggregate = flusher.aggregate; ByteBuffer aggregate = flusher.aggregate;
return String.format("%s[queueSize=%d,aggregateSize=%d,failure=%s]",getClass().getSimpleName(),queue.size(),aggregate == null?0:aggregate.position(), return String.format("%s[%s,queueSize=%d,aggregateSize=%d,failure=%s]",getClass().getSimpleName(),generator.getBehavior(),queue.size(),aggregate == null?0:aggregate.position(),
failure); failure);
} }
} }

View File

@ -21,11 +21,15 @@ package org.eclipse.jetty.websocket.common.message;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
import java.util.function.Function; import java.util.function.Function;
import org.eclipse.jetty.util.BufferUtil;
import org.eclipse.jetty.util.Utf8StringBuilder; import org.eclipse.jetty.util.Utf8StringBuilder;
import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger;
import org.eclipse.jetty.websocket.api.WebSocketPolicy; import org.eclipse.jetty.websocket.api.WebSocketPolicy;
public class StringMessageSink implements MessageSink public class StringMessageSink implements MessageSink
{ {
private static final Logger LOG = Log.getLogger(StringMessageSink.class);
private final WebSocketPolicy policy; private final WebSocketPolicy policy;
private final Function<String, Void> onMessageFunction; private final Function<String, Void> onMessageFunction;
private Utf8StringBuilder utf; private Utf8StringBuilder utf;
@ -42,33 +46,31 @@ public class StringMessageSink implements MessageSink
@Override @Override
public void accept(ByteBuffer payload, Boolean fin) public void accept(ByteBuffer payload, Boolean fin)
{ {
try if (payload != null)
{ {
if (payload != null) policy.assertValidTextMessageSize(size + payload.remaining());
{ size += payload.remaining();
policy.assertValidTextMessageSize(size + payload.remaining());
size += payload.remaining();
if (utf == null) if (utf == null)
utf = new Utf8StringBuilder(1024); utf = new Utf8StringBuilder(1024);
// allow for fast fail of BAD utf (incomplete utf will trigger on messageComplete) if(LOG.isDebugEnabled())
utf.append(payload); LOG.debug("Raw Payload {}", BufferUtil.toDetailString(payload));
}
// allow for fast fail of BAD utf (incomplete utf will trigger on messageComplete)
utf.append(payload);
} }
finally
if (fin)
{ {
if (fin) // notify event
{ if (utf != null)
// notify event onMessageFunction.apply(utf.toString());
if (utf != null) else
onMessageFunction.apply(utf.toString()); onMessageFunction.apply("");
else // reset
onMessageFunction.apply(""); size = 0;
// reset utf = null;
size = 0;
utf = null;
}
} }
} }
} }

View File

@ -18,16 +18,31 @@
package org.eclipse.jetty.websocket.common.util; package org.eclipse.jetty.websocket.common.util;
import java.lang.reflect.InvocationTargetException;
@SuppressWarnings("serial") @SuppressWarnings("serial")
public class DynamicArgsException extends RuntimeException public class DynamicArgsException extends RuntimeException
{ {
public DynamicArgsException(String message, Throwable cause) public DynamicArgsException(String message, Throwable cause)
{ {
super(message,cause); super(message, cause);
} }
public DynamicArgsException(String message) public DynamicArgsException(String message)
{ {
super(message); super(message);
} }
public Throwable getInvokedCause()
{
Throwable cause = getCause();
if (cause == null)
return null;
if (cause instanceof InvocationTargetException)
{
return cause.getCause();
}
return cause;
}
} }

View File

@ -25,6 +25,7 @@ import java.util.concurrent.TimeUnit;
import org.eclipse.jetty.toolchain.test.EventQueue; import org.eclipse.jetty.toolchain.test.EventQueue;
import org.eclipse.jetty.websocket.api.StatusCode; import org.eclipse.jetty.websocket.api.StatusCode;
import org.eclipse.jetty.websocket.api.annotations.WebSocket;
import org.eclipse.jetty.websocket.common.CloseInfo; import org.eclipse.jetty.websocket.common.CloseInfo;
import org.eclipse.jetty.websocket.common.OpCode; import org.eclipse.jetty.websocket.common.OpCode;
import org.eclipse.jetty.websocket.common.WebSocketFrame; import org.eclipse.jetty.websocket.common.WebSocketFrame;
@ -40,14 +41,18 @@ import org.junit.Test;
public class IdleTimeoutTest public class IdleTimeoutTest
{ {
@WebSocket(maxIdleTime = 500)
public static class FastTimeoutRFCSocket extends RFCSocket
{
}
@SuppressWarnings("serial") @SuppressWarnings("serial")
public static class TimeoutServlet extends WebSocketServlet public static class TimeoutServlet extends WebSocketServlet
{ {
@Override @Override
public void configure(WebSocketServletFactory factory) public void configure(WebSocketServletFactory factory)
{ {
factory.getPolicy().setIdleTimeout(500); factory.register(FastTimeoutRFCSocket.class);
factory.register(RFCSocket.class);
} }
} }

View File

@ -557,7 +557,7 @@ public class TestABCase5 extends AbstractABCase
} }
/** /**
* Send text fragmented in 2 packets, with ping between them (framewise) * Send text fragmented in 2 packets, with ping between them (frame wise)
* @throws Exception on test failure * @throws Exception on test failure
*/ */
@Test @Test

View File

@ -46,7 +46,7 @@ public class TestABCase6_BadUTF extends AbstractABCase
{ {
private static final Logger LOG = Log.getLogger(TestABCase6_BadUTF.class); private static final Logger LOG = Log.getLogger(TestABCase6_BadUTF.class);
@Parameters @Parameters(name = "{0} - {1}")
public static Collection<String[]> data() public static Collection<String[]> data()
{ {
// The various Good UTF8 sequences as a String (hex form) // The various Good UTF8 sequences as a String (hex form)
@ -163,15 +163,13 @@ public class TestABCase6_BadUTF extends AbstractABCase
List<WebSocketFrame> expect = new ArrayList<>(); List<WebSocketFrame> expect = new ArrayList<>();
expect.add(new CloseInfo(StatusCode.BAD_PAYLOAD).asFrame()); expect.add(new CloseInfo(StatusCode.BAD_PAYLOAD).asFrame());
try (Fuzzer fuzzer = new Fuzzer(this)) try (Fuzzer fuzzer = new Fuzzer(this);
StacklessLogging ignored = new StacklessLogging(Parser.class) )
{ {
try (StacklessLogging supress = new StacklessLogging(Parser.class)) fuzzer.connect();
{ fuzzer.setSendMode(Fuzzer.SendMode.BULK);
fuzzer.connect(); fuzzer.send(send);
fuzzer.setSendMode(Fuzzer.SendMode.BULK); fuzzer.expect(expect);
fuzzer.send(send);
fuzzer.expect(expect);
}
} }
} }
} }