Merge branch 'jetty-9.3.x' into jetty-9.4.x

This commit is contained in:
Joakim Erdfelt 2016-06-20 17:56:17 -07:00
commit 1418892d4f
13 changed files with 198 additions and 104 deletions

View File

@ -172,5 +172,9 @@ public abstract class JsrCallable extends CallableMethod
}
}
public abstract void setDecoderClass(Class<? extends Decoder> decoderClass);
/**
* The Type of Class a {@link Decoder} should be created to produce.
* @param decodingType the type of class a Decoder should be created to produce
*/
public abstract void setDecodingType(Class<?> decodingType);
}

View File

@ -25,12 +25,9 @@ import javax.websocket.OnMessage;
import org.eclipse.jetty.websocket.common.events.annotated.InvalidSignatureException;
import org.eclipse.jetty.websocket.jsr356.annotations.Param.Role;
import org.eclipse.jetty.websocket.jsr356.decoders.ByteArrayDecoder;
import org.eclipse.jetty.websocket.jsr356.decoders.ByteBufferDecoder;
import org.eclipse.jetty.websocket.jsr356.decoders.InputStreamDecoder;
/**
* Param handling for static Binary &#064;{@link OnMessage} parameters.
* Param handling for static Binary &#064;{@link javax.websocket.OnMessage} parameters.
*/
public class JsrParamIdBinary extends JsrParamIdOnMessage implements IJsrParamId
{
@ -48,14 +45,14 @@ public class JsrParamIdBinary extends JsrParamIdOnMessage implements IJsrParamId
if (param.type.isAssignableFrom(ByteBuffer.class))
{
param.bind(Role.MESSAGE_BINARY);
callable.setDecoderClass(ByteBufferDecoder.class);
callable.setDecodingType(ByteBuffer.class);
return true;
}
if (param.type.isAssignableFrom(byte[].class))
{
param.bind(Role.MESSAGE_BINARY);
callable.setDecoderClass(ByteArrayDecoder.class);
callable.setDecodingType(byte[].class);
return true;
}
@ -64,7 +61,7 @@ public class JsrParamIdBinary extends JsrParamIdOnMessage implements IJsrParamId
{
assertPartialMessageSupportDisabled(param,callable);
param.bind(Role.MESSAGE_BINARY_STREAM);
callable.setDecoderClass(InputStreamDecoder.class);
callable.setDecodingType(InputStream.class);
return true;
}

View File

@ -25,7 +25,7 @@ import org.eclipse.jetty.websocket.jsr356.annotations.Param.Role;
import org.eclipse.jetty.websocket.jsr356.metadata.DecoderMetadata;
/**
* Param handling for Text or Binary &#064;{@link OnMessage} parameters declared as {@link javax.websocket.Decoder}s
* Param handling for Text or Binary &#064;{@link javax.websocket.OnMessage} parameters declared as {@link javax.websocket.Decoder}s
*/
public class JsrParamIdDecoder extends JsrParamIdOnMessage implements IJsrParamId
{
@ -69,7 +69,8 @@ public class JsrParamIdDecoder extends JsrParamIdOnMessage implements IJsrParamI
param.bind(Role.MESSAGE_PONG);
break;
}
callable.setDecoderClass(metadata.getCoderClass());
callable.setDecodingType(metadata.getObjectType());
return true;
}
return false;

View File

@ -22,7 +22,6 @@ import javax.websocket.PongMessage;
import org.eclipse.jetty.websocket.common.events.annotated.InvalidSignatureException;
import org.eclipse.jetty.websocket.jsr356.annotations.Param.Role;
import org.eclipse.jetty.websocket.jsr356.decoders.PongMessageDecoder;
public class JsrParamIdPong extends JsrParamIdOnMessage implements IJsrParamId
{
@ -41,7 +40,7 @@ public class JsrParamIdPong extends JsrParamIdOnMessage implements IJsrParamId
{
assertPartialMessageSupportDisabled(param,callable);
param.bind(Role.MESSAGE_PONG);
callable.setDecoderClass(PongMessageDecoder.class);
callable.setDecodingType(PongMessage.class);
return true;
}
return false;

View File

@ -24,19 +24,9 @@ import javax.websocket.OnMessage;
import org.eclipse.jetty.websocket.common.events.annotated.InvalidSignatureException;
import org.eclipse.jetty.websocket.jsr356.annotations.Param.Role;
import org.eclipse.jetty.websocket.jsr356.decoders.BooleanDecoder;
import org.eclipse.jetty.websocket.jsr356.decoders.ByteDecoder;
import org.eclipse.jetty.websocket.jsr356.decoders.CharacterDecoder;
import org.eclipse.jetty.websocket.jsr356.decoders.DoubleDecoder;
import org.eclipse.jetty.websocket.jsr356.decoders.FloatDecoder;
import org.eclipse.jetty.websocket.jsr356.decoders.IntegerDecoder;
import org.eclipse.jetty.websocket.jsr356.decoders.LongDecoder;
import org.eclipse.jetty.websocket.jsr356.decoders.ReaderDecoder;
import org.eclipse.jetty.websocket.jsr356.decoders.ShortDecoder;
import org.eclipse.jetty.websocket.jsr356.decoders.StringDecoder;
/**
* Param handling for static Text &#064;{@link OnMessage} parameters
* Param handling for static Text &#064;{@link javax.websocket.OnMessage} parameters
*/
public class JsrParamIdText extends JsrParamIdOnMessage implements IJsrParamId
{
@ -65,7 +55,7 @@ public class JsrParamIdText extends JsrParamIdOnMessage implements IJsrParamId
if (param.type.isAssignableFrom(String.class))
{
param.bind(Role.MESSAGE_TEXT);
callable.setDecoderClass(StringDecoder.class);
callable.setDecodingType(String.class);
return true;
}
@ -74,56 +64,56 @@ public class JsrParamIdText extends JsrParamIdOnMessage implements IJsrParamId
{
assertPartialMessageSupportDisabled(param,callable);
param.bind(Role.MESSAGE_TEXT);
callable.setDecoderClass(BooleanDecoder.class);
callable.setDecodingType(Boolean.class);
return true;
}
if (param.type.isAssignableFrom(Byte.class) || (param.type == Byte.TYPE))
{
assertPartialMessageSupportDisabled(param,callable);
param.bind(Role.MESSAGE_TEXT);
callable.setDecoderClass(ByteDecoder.class);
callable.setDecodingType(Byte.class);
return true;
}
if (param.type.isAssignableFrom(Character.class) || (param.type == Character.TYPE))
{
assertPartialMessageSupportDisabled(param,callable);
param.bind(Role.MESSAGE_TEXT);
callable.setDecoderClass(CharacterDecoder.class);
callable.setDecodingType(Character.class);
return true;
}
if (param.type.isAssignableFrom(Double.class) || (param.type == Double.TYPE))
{
assertPartialMessageSupportDisabled(param,callable);
param.bind(Role.MESSAGE_TEXT);
callable.setDecoderClass(DoubleDecoder.class);
callable.setDecodingType(Double.class);
return true;
}
if (param.type.isAssignableFrom(Float.class) || (param.type == Float.TYPE))
{
assertPartialMessageSupportDisabled(param,callable);
param.bind(Role.MESSAGE_TEXT);
callable.setDecoderClass(FloatDecoder.class);
callable.setDecodingType(Float.class);
return true;
}
if (param.type.isAssignableFrom(Integer.class) || (param.type == Integer.TYPE))
{
assertPartialMessageSupportDisabled(param,callable);
param.bind(Role.MESSAGE_TEXT);
callable.setDecoderClass(IntegerDecoder.class);
callable.setDecodingType(Integer.class);
return true;
}
if (param.type.isAssignableFrom(Long.class) || (param.type == Long.TYPE))
{
assertPartialMessageSupportDisabled(param,callable);
param.bind(Role.MESSAGE_TEXT);
callable.setDecoderClass(LongDecoder.class);
callable.setDecodingType(Long.class);
return true;
}
if (param.type.isAssignableFrom(Short.class) || (param.type == Short.TYPE))
{
assertPartialMessageSupportDisabled(param,callable);
param.bind(Role.MESSAGE_TEXT);
callable.setDecoderClass(ShortDecoder.class);
callable.setDecodingType(Short.class);
return true;
}
@ -132,7 +122,7 @@ public class JsrParamIdText extends JsrParamIdOnMessage implements IJsrParamId
{
assertPartialMessageSupportDisabled(param,callable);
param.bind(Role.MESSAGE_TEXT_STREAM);
callable.setDecoderClass(ReaderDecoder.class);
callable.setDecodingType(Reader.class);
return true;
}
@ -150,7 +140,7 @@ public class JsrParamIdText extends JsrParamIdOnMessage implements IJsrParamId
else
{
param.bind(Role.MESSAGE_TEXT);
callable.setDecoderClass(BooleanDecoder.class);
callable.setDecodingType(Boolean.class);
}
return true;
}

View File

@ -22,7 +22,6 @@ import java.lang.reflect.Method;
import javax.websocket.CloseReason;
import javax.websocket.CloseReason.CloseCodes;
import javax.websocket.Decoder;
import javax.websocket.OnClose;
import org.eclipse.jetty.websocket.common.CloseInfo;
@ -30,7 +29,7 @@ import org.eclipse.jetty.websocket.jsr356.JsrSession;
import org.eclipse.jetty.websocket.jsr356.annotations.Param.Role;
/**
* Callable for {@link OnClose} annotated methods
* Callable for {@link javax.websocket.OnClose} annotated methods
*/
public class OnCloseCallable extends JsrCallable
{
@ -83,7 +82,7 @@ public class OnCloseCallable extends JsrCallable
}
@Override
public void setDecoderClass(Class<? extends Decoder> decoderClass)
public void setDecodingType(Class<?> decodingType)
{
/* ignore, not relevant for onClose */
}

View File

@ -67,9 +67,8 @@ public class OnErrorCallable extends JsrCallable
}
@Override
public void setDecoderClass(Class<? extends Decoder> decoderClass)
public void setDecodingType(Class<?> decodingType)
{
/* ignore, not relevant for onClose */
}
}

View File

@ -29,9 +29,9 @@ import org.eclipse.jetty.websocket.jsr356.JsrSession;
import org.eclipse.jetty.websocket.jsr356.annotations.Param.Role;
/**
* Callable for {@link OnMessage} annotated methods with a whole or partial binary messages.
* Callable for {@link javax.websocket.OnMessage} annotated methods with a whole or partial binary messages.
* <p>
* Not for use with {@link java.io.InputStream} based {@link OnMessage} method objects.
* Not for use with {@link java.io.InputStream} based {@link javax.websocket.OnMessage} method objects.
*
* @see javax.websocket.Decoder.Binary
*/

View File

@ -26,7 +26,6 @@ import javax.websocket.Encoder;
import org.eclipse.jetty.websocket.common.events.annotated.InvalidSignatureException;
import org.eclipse.jetty.websocket.common.util.ReflectUtils;
import org.eclipse.jetty.websocket.jsr356.EncoderFactory;
import org.eclipse.jetty.websocket.jsr356.InitException;
import org.eclipse.jetty.websocket.jsr356.JsrSession;
import org.eclipse.jetty.websocket.jsr356.annotations.Param.Role;
@ -34,7 +33,7 @@ public class OnMessageCallable extends JsrCallable
{
protected final Class<?> returnType;
protected Encoder returnEncoder;
protected Class<? extends Decoder> decoderClass;
protected Class<?> decodingType;
protected Decoder decoder;
protected int idxPartialMessageFlag = -1;
protected int idxMessageObject = -1;
@ -50,7 +49,7 @@ public class OnMessageCallable extends JsrCallable
{
super(copy);
this.returnType = copy.returnType;
this.decoderClass = copy.decoderClass;
this.decodingType = copy.decodingType;
this.decoder = copy.decoder;
this.idxPartialMessageFlag = copy.idxPartialMessageFlag;
this.idxMessageObject = copy.idxMessageObject;
@ -93,11 +92,6 @@ public class OnMessageCallable extends JsrCallable
return decoder;
}
public Class<? extends Decoder> getDecoderClass()
{
return decoderClass;
}
public Param getMessageObjectParam()
{
if (idxMessageObject < 0)
@ -138,16 +132,9 @@ public class OnMessageCallable extends JsrCallable
this.returnEncoder = encoderWrapper.getEncoder();
}
if (decoderClass != null)
if (decodingType != null)
{
try
{
this.decoder = decoderClass.newInstance();
}
catch (InstantiationException | IllegalAccessException e)
{
throw new InitException("Unable to create decoder: " + decoderClass.getName(),e);
}
this.decoder = session.getDecoderFactory().getDecoderFor(decodingType);
}
}
@ -162,9 +149,9 @@ public class OnMessageCallable extends JsrCallable
}
@Override
public void setDecoderClass(Class<? extends Decoder> decoderClass)
public void setDecodingType(Class<?> decodingType)
{
this.decoderClass = decoderClass;
this.decodingType = decodingType;
messageRoleAssigned = true;
}

View File

@ -29,9 +29,9 @@ import org.eclipse.jetty.websocket.jsr356.JsrSession;
import org.eclipse.jetty.websocket.jsr356.annotations.Param.Role;
/**
* Callable for {@link OnMessage} annotated methods with a whole or partial text messages.
* Callable for {@link javax.websocket.OnMessage} annotated methods with a whole or partial text messages.
* <p>
* Not for use with {@link Reader} based {@link OnMessage} method objects.
* Not for use with {@link java.io.Reader} based {@link javax.websocket.OnMessage} method objects.
*
* @see javax.websocket.Decoder.Text
*/

View File

@ -20,7 +20,6 @@ package org.eclipse.jetty.websocket.jsr356.annotations;
import java.lang.reflect.Method;
import javax.websocket.Decoder;
import javax.websocket.EndpointConfig;
import javax.websocket.OnOpen;
@ -28,7 +27,7 @@ import org.eclipse.jetty.websocket.jsr356.JsrSession;
import org.eclipse.jetty.websocket.jsr356.annotations.Param.Role;
/**
* Callable for {@link OnOpen} annotated methods
* Callable for {@link javax.websocket.OnOpen} annotated methods
*/
public class OnOpenCallable extends JsrCallable
{
@ -63,7 +62,7 @@ public class OnOpenCallable extends JsrCallable
}
@Override
public void setDecoderClass(Class<? extends Decoder> decoderClass)
public void setDecodingType(Class<?> decodingType)
{
/* ignore, not relevant for onClose */
}

View File

@ -52,6 +52,7 @@ public class TimeDecoder implements Decoder.Text<Date>
@Override
public void init(EndpointConfig config)
{
System.out.println("#### INIT ####");
}
@Override

View File

@ -25,13 +25,21 @@ import java.io.PrintWriter;
import java.io.StringWriter;
import java.net.InetSocketAddress;
import java.net.URI;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Collections;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.TimeZone;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReference;
import javax.websocket.DecodeException;
import javax.websocket.Decoder;
import javax.websocket.EndpointConfig;
import javax.websocket.Extension;
import javax.websocket.HandshakeResponse;
import javax.websocket.OnMessage;
@ -178,11 +186,20 @@ public class ConfiguratorTest
int upgradeNum = upgradeCount.addAndGet(1);
LOG.debug("Upgrade Num: {}", upgradeNum);
sec.getUserProperties().put("upgradeNum", Integer.toString(upgradeNum));
switch(upgradeNum) {
case 1: sec.getUserProperties().put("apple", "fruit from tree"); break;
case 2: sec.getUserProperties().put("blueberry", "fruit from bush"); break;
case 3: sec.getUserProperties().put("strawberry", "fruit from annual"); break;
default: sec.getUserProperties().put("fruit"+upgradeNum, "placeholder"); break;
switch (upgradeNum)
{
case 1:
sec.getUserProperties().put("apple", "fruit from tree");
break;
case 2:
sec.getUserProperties().put("blueberry", "fruit from bush");
break;
case 3:
sec.getUserProperties().put("strawberry", "fruit from annual");
break;
default:
sec.getUserProperties().put("fruit" + upgradeNum, "placeholder");
break;
}
super.modifyHandshake(sec, request, response);
@ -249,6 +266,84 @@ public class ConfiguratorTest
}
}
public static class SelectedProtocolConfigurator extends ServerEndpointConfig.Configurator
{
@Override
public void modifyHandshake(ServerEndpointConfig config, HandshakeRequest request, HandshakeResponse response)
{
List<String> selectedProtocol = response.getHeaders().get("Sec-WebSocket-Protocol");
String protocol = "<>";
if (selectedProtocol != null || !selectedProtocol.isEmpty())
protocol = selectedProtocol.get(0);
config.getUserProperties().put("selected-subprotocol", protocol);
}
}
public static class GmtTimeDecoder implements Decoder.Text<Calendar>
{
private TimeZone TZ;
@Override
public Calendar decode(String s) throws DecodeException
{
if (TZ == null)
throw new DecodeException(s, ".init() not called");
try
{
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss");
dateFormat.setTimeZone(TZ);
Date time = dateFormat.parse(s);
Calendar cal = Calendar.getInstance();
cal.setTimeZone(TZ);
cal.setTime(time);
return cal;
}
catch (ParseException e)
{
throw new DecodeException(s, "Unable to decode Time", e);
}
}
@Override
public void init(EndpointConfig config)
{
TZ = TimeZone.getTimeZone("GMT+0");
}
@Override
public void destroy()
{
}
@Override
public boolean willDecode(String s)
{
return true;
}
}
@ServerEndpoint(value = "/timedecoder",
subprotocols = { "time", "gmt" },
configurator = SelectedProtocolConfigurator.class,
decoders = {GmtTimeDecoder.class})
public static class TimeDecoderSocket
{
private TimeZone TZ = TimeZone.getTimeZone("GMT+0");
@OnMessage
public String onMessage(Calendar cal)
{
return String.format("cal=%s", newDateFormat().format(cal.getTime()));
}
private SimpleDateFormat newDateFormat()
{
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy.MM.dd G 'at' HH:mm:ss Z");
dateFormat.setTimeZone(TZ);
return dateFormat;
}
}
private static Server server;
private static URI baseServerUri;
@ -271,6 +366,7 @@ public class ConfiguratorTest
container.addEndpoint(ProtocolsSocket.class);
container.addEndpoint(UniqueUserPropsSocket.class);
container.addEndpoint(AddressSocket.class);
container.addEndpoint(TimeDecoderSocket.class);
server.start();
String host = connector.getHost();
@ -511,4 +607,26 @@ public class ConfiguratorTest
Assert.assertThat("Frame Response", frame.getPayloadAsUTF8(), is("Requested Protocols: [\"echo\",\"chat\",\"status\"]"));
}
}
/**
* Test of Sec-WebSocket-Protocol, using non-spec case header
*/
@Test
public void testDecoderWithProtocol() throws Exception
{
URI uri = baseServerUri.resolve("/timedecoder");
try (BlockheadClient client = new BlockheadClient(uri))
{
client.addHeader("Sec-Websocket-Protocol: gmt\r\n");
client.connect();
client.sendStandardRequest();
client.expectUpgradeResponse();
client.write(new TextFrame().setPayload("2016-06-20T14:27:44"));
EventQueue<WebSocketFrame> frames = client.readFrames(1, 1, TimeUnit.SECONDS);
WebSocketFrame frame = frames.poll();
Assert.assertThat("Frame Response", frame.getPayloadAsUTF8(), is("cal=2016.06.20 AD at 14:27:44 +0000"));
}
}
}