Merge branch 'jetty-9.2.x'
Conflicts: jetty-util/src/main/java/org/eclipse/jetty/util/BufferUtil.java jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/WebSocketSession.java
This commit is contained in:
commit
5312f2c0d7
|
@ -86,6 +86,19 @@ public class HttpParserTest
|
|||
assertEquals(HttpMethod.GET,HttpMethod.lookAheadGet(b));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLineParse_Mock_IP() throws Exception
|
||||
{
|
||||
ByteBuffer buffer= BufferUtil.toBuffer("POST /mock/127.0.0.1 HTTP/1.1\015\012" + "\015\012");
|
||||
|
||||
HttpParser.RequestHandler handler = new Handler();
|
||||
HttpParser parser= new HttpParser(handler);
|
||||
parseAll(parser,buffer);
|
||||
assertEquals("POST", _methodOrVersion);
|
||||
assertEquals("/mock/127.0.0.1", _uriOrStatus);
|
||||
assertEquals("HTTP/1.1", _versionOrReason);
|
||||
assertEquals(-1, _headers);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLineParse0() throws Exception
|
||||
|
|
|
@ -18,33 +18,38 @@
|
|||
|
||||
package org.eclipse.jetty.websocket.api;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
public class CloseStatus
|
||||
{
|
||||
private static final int MAX_CONTROL_PAYLOAD = 125;
|
||||
private static final int MAX_REASON_PHRASE = MAX_CONTROL_PAYLOAD - 2;
|
||||
public static final int MAX_REASON_PHRASE = MAX_CONTROL_PAYLOAD - 2;
|
||||
|
||||
/**
|
||||
* Convenience method for trimming a long reason phrase at the maximum reason phrase length.
|
||||
* Convenience method for trimming a long reason phrase at the maximum reason phrase length of 123 UTF-8 bytes (per WebSocket spec).
|
||||
*
|
||||
* @param reason
|
||||
* the proposed reason phrase
|
||||
* @return the reason phrase (trimmed if needed)
|
||||
* @deprecated use of this method is strongly discouraged, as it creates too many new objects that are just thrown away to accomplish its goals.
|
||||
*/
|
||||
@Deprecated
|
||||
public static String trimMaxReasonLength(String reason)
|
||||
{
|
||||
if (reason == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
byte[] reasonBytes = reason.getBytes(StandardCharsets.UTF_8);
|
||||
if (reasonBytes.length > MAX_REASON_PHRASE)
|
||||
{
|
||||
byte[] trimmed = new byte[MAX_REASON_PHRASE];
|
||||
System.arraycopy(reasonBytes,0,trimmed,0,MAX_REASON_PHRASE);
|
||||
return new String(trimmed,StandardCharsets.UTF_8);
|
||||
}
|
||||
|
||||
if (reason.length() > MAX_REASON_PHRASE)
|
||||
{
|
||||
return reason.substring(0,MAX_REASON_PHRASE);
|
||||
}
|
||||
else
|
||||
{
|
||||
return reason;
|
||||
}
|
||||
return reason;
|
||||
}
|
||||
|
||||
private int code;
|
||||
|
|
|
@ -152,6 +152,15 @@ public interface RemoteEndpoint
|
|||
*/
|
||||
BatchMode getBatchMode();
|
||||
|
||||
/**
|
||||
* Set the batch mode with which messages are sent.
|
||||
*
|
||||
* @param mode
|
||||
* the batch mode to use
|
||||
* @see #flush()
|
||||
*/
|
||||
void setBatchMode(BatchMode mode);
|
||||
|
||||
/**
|
||||
* Flushes messages that may have been batched by the implementation.
|
||||
*
|
||||
|
|
|
@ -24,6 +24,8 @@ import java.lang.annotation.Retention;
|
|||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
import org.eclipse.jetty.websocket.api.BatchMode;
|
||||
|
||||
/**
|
||||
* Tags a POJO as being a WebSocket class.
|
||||
*/
|
||||
|
@ -40,4 +42,6 @@ public @interface WebSocket
|
|||
int maxIdleTime() default -2;
|
||||
|
||||
int maxTextMessageSize() default -2;
|
||||
|
||||
BatchMode batchMode() default BatchMode.AUTO;
|
||||
}
|
||||
|
|
|
@ -19,14 +19,13 @@
|
|||
package org.eclipse.jetty.websocket.common;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
import org.eclipse.jetty.util.BufferUtil;
|
||||
import org.eclipse.jetty.util.StringUtil;
|
||||
import org.eclipse.jetty.util.Utf8Appendable.NotUtf8Exception;
|
||||
import org.eclipse.jetty.util.Utf8StringBuilder;
|
||||
import org.eclipse.jetty.util.log.Log;
|
||||
import org.eclipse.jetty.util.log.Logger;
|
||||
import org.eclipse.jetty.util.Utf8Appendable.NotUtf8Exception;
|
||||
import org.eclipse.jetty.websocket.api.BadPayloadException;
|
||||
import org.eclipse.jetty.websocket.api.CloseStatus;
|
||||
import org.eclipse.jetty.websocket.api.ProtocolException;
|
||||
import org.eclipse.jetty.websocket.api.StatusCode;
|
||||
import org.eclipse.jetty.websocket.api.extensions.Frame;
|
||||
|
@ -34,19 +33,23 @@ import org.eclipse.jetty.websocket.common.frames.CloseFrame;
|
|||
|
||||
public class CloseInfo
|
||||
{
|
||||
private static final Logger LOG = Log.getLogger(CloseInfo.class);
|
||||
private int statusCode;
|
||||
private String reason;
|
||||
private byte[] reasonBytes;
|
||||
|
||||
public CloseInfo()
|
||||
{
|
||||
this(StatusCode.NO_CODE,null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse the Close Frame payload.
|
||||
*
|
||||
* @param payload the raw close frame payload.
|
||||
* @param validate true if payload should be validated per WebSocket spec.
|
||||
*/
|
||||
public CloseInfo(ByteBuffer payload, boolean validate)
|
||||
{
|
||||
this.statusCode = StatusCode.NO_CODE;
|
||||
this.reason = null;
|
||||
|
||||
if ((payload == null) || (payload.remaining() == 0))
|
||||
{
|
||||
|
@ -77,34 +80,24 @@ public class CloseInfo
|
|||
|
||||
if (data.remaining() > 0)
|
||||
{
|
||||
// Reason
|
||||
try
|
||||
// Reason (trimmed to max reason size)
|
||||
int len = Math.min(data.remaining(), CloseStatus.MAX_REASON_PHRASE);
|
||||
reasonBytes = new byte[len];
|
||||
data.get(reasonBytes,0,len);
|
||||
|
||||
// Spec Requirement : throw BadPayloadException on invalid UTF8
|
||||
if(validate)
|
||||
{
|
||||
Utf8StringBuilder utf = new Utf8StringBuilder();
|
||||
utf.append(data);
|
||||
reason = utf.toString();
|
||||
}
|
||||
catch (NotUtf8Exception e)
|
||||
{
|
||||
if (validate)
|
||||
try
|
||||
{
|
||||
Utf8StringBuilder utf = new Utf8StringBuilder();
|
||||
// if this throws, we know we have bad UTF8
|
||||
utf.append(reasonBytes,0,reasonBytes.length);
|
||||
}
|
||||
catch (NotUtf8Exception e)
|
||||
{
|
||||
throw new BadPayloadException("Invalid Close Reason",e);
|
||||
}
|
||||
else
|
||||
{
|
||||
LOG.warn(e);
|
||||
}
|
||||
}
|
||||
catch (RuntimeException e)
|
||||
{
|
||||
if (validate)
|
||||
{
|
||||
throw new ProtocolException("Invalid Close Reason",e);
|
||||
}
|
||||
else
|
||||
{
|
||||
LOG.warn(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -125,10 +118,28 @@ public class CloseInfo
|
|||
this(statusCode,null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a CloseInfo, trimming the reason to {@link CloseStatus#MAX_REASON_PHRASE} UTF-8 bytes if needed.
|
||||
*
|
||||
* @param statusCode the status code
|
||||
* @param reason the raw reason code
|
||||
*/
|
||||
public CloseInfo(int statusCode, String reason)
|
||||
{
|
||||
this.statusCode = statusCode;
|
||||
this.reason = reason;
|
||||
if (reason != null)
|
||||
{
|
||||
byte[] utf8Bytes = reason.getBytes(StandardCharsets.UTF_8);
|
||||
if (utf8Bytes.length > CloseStatus.MAX_REASON_PHRASE)
|
||||
{
|
||||
this.reasonBytes = new byte[CloseStatus.MAX_REASON_PHRASE];
|
||||
System.arraycopy(utf8Bytes,0,this.reasonBytes,0,CloseStatus.MAX_REASON_PHRASE);
|
||||
}
|
||||
else
|
||||
{
|
||||
this.reasonBytes = utf8Bytes;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private ByteBuffer asByteBuffer()
|
||||
|
@ -140,11 +151,10 @@ public class CloseInfo
|
|||
}
|
||||
|
||||
int len = 2; // status code
|
||||
byte utf[] = null;
|
||||
if (StringUtil.isNotBlank(reason))
|
||||
boolean hasReason = (this.reasonBytes != null) && (this.reasonBytes.length > 0);
|
||||
if (hasReason)
|
||||
{
|
||||
utf = StringUtil.getUtf8Bytes(reason);
|
||||
len += utf.length;
|
||||
len += this.reasonBytes.length;
|
||||
}
|
||||
|
||||
ByteBuffer buf = BufferUtil.allocate(len);
|
||||
|
@ -152,9 +162,9 @@ public class CloseInfo
|
|||
buf.put((byte)((statusCode >>> 8) & 0xFF));
|
||||
buf.put((byte)((statusCode >>> 0) & 0xFF));
|
||||
|
||||
if (utf != null)
|
||||
if (hasReason)
|
||||
{
|
||||
buf.put(utf,0,utf.length);
|
||||
buf.put(this.reasonBytes,0,this.reasonBytes.length);
|
||||
}
|
||||
BufferUtil.flipToFlush(buf,0);
|
||||
|
||||
|
@ -178,7 +188,11 @@ public class CloseInfo
|
|||
|
||||
public String getReason()
|
||||
{
|
||||
return reason;
|
||||
if (this.reasonBytes == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
return new String(this.reasonBytes,StandardCharsets.UTF_8);
|
||||
}
|
||||
|
||||
public int getStatusCode()
|
||||
|
@ -199,6 +213,6 @@ public class CloseInfo
|
|||
@Override
|
||||
public String toString()
|
||||
{
|
||||
return String.format("CloseInfo[code=%d,reason=%s]",statusCode,reason);
|
||||
return String.format("CloseInfo[code=%d,reason=%s]",statusCode,getReason());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -436,9 +436,7 @@ public class WebSocketRemoteEndpoint implements RemoteEndpoint
|
|||
return batchMode;
|
||||
}
|
||||
|
||||
// Only the JSR needs to have this method exposed.
|
||||
// In the Jetty implementation the batching is set
|
||||
// at the moment of opening the session.
|
||||
@Override
|
||||
public void setBatchMode(BatchMode batchMode)
|
||||
{
|
||||
this.batchMode = batchMode;
|
||||
|
|
|
@ -25,6 +25,7 @@ import org.eclipse.jetty.util.BufferUtil;
|
|||
import org.eclipse.jetty.util.Utf8Appendable.NotUtf8Exception;
|
||||
import org.eclipse.jetty.util.log.Log;
|
||||
import org.eclipse.jetty.util.log.Logger;
|
||||
import org.eclipse.jetty.websocket.api.BatchMode;
|
||||
import org.eclipse.jetty.websocket.api.CloseException;
|
||||
import org.eclipse.jetty.websocket.api.StatusCode;
|
||||
import org.eclipse.jetty.websocket.api.WebSocketPolicy;
|
||||
|
@ -208,6 +209,12 @@ public abstract class AbstractEventDriver implements IncomingFrames, EventDriver
|
|||
{
|
||||
/* TODO: provide annotation in future */
|
||||
}
|
||||
|
||||
@Override
|
||||
public BatchMode getBatchMode()
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void openSession(WebSocketSession session)
|
||||
|
|
|
@ -23,6 +23,7 @@ import java.io.InputStream;
|
|||
import java.io.Reader;
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
import org.eclipse.jetty.websocket.api.BatchMode;
|
||||
import org.eclipse.jetty.websocket.api.WebSocketPolicy;
|
||||
import org.eclipse.jetty.websocket.api.extensions.Frame;
|
||||
import org.eclipse.jetty.websocket.api.extensions.IncomingFrames;
|
||||
|
@ -34,6 +35,8 @@ public interface EventDriver extends IncomingFrames
|
|||
public WebSocketPolicy getPolicy();
|
||||
|
||||
public WebSocketSession getSession();
|
||||
|
||||
public BatchMode getBatchMode();
|
||||
|
||||
public void onBinaryFrame(ByteBuffer buffer, boolean fin) throws IOException;
|
||||
|
||||
|
|
|
@ -23,6 +23,7 @@ import java.io.InputStream;
|
|||
import java.io.Reader;
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
import org.eclipse.jetty.websocket.api.BatchMode;
|
||||
import org.eclipse.jetty.websocket.api.WebSocketPolicy;
|
||||
import org.eclipse.jetty.websocket.api.annotations.WebSocket;
|
||||
import org.eclipse.jetty.websocket.api.extensions.Frame;
|
||||
|
@ -40,6 +41,7 @@ public class JettyAnnotatedEventDriver extends AbstractEventDriver
|
|||
{
|
||||
private final JettyAnnotatedMetadata events;
|
||||
private boolean hasCloseBeenCalled = false;
|
||||
private BatchMode batchMode;
|
||||
|
||||
public JettyAnnotatedEventDriver(WebSocketPolicy policy, Object websocket, JettyAnnotatedMetadata events)
|
||||
{
|
||||
|
@ -64,6 +66,13 @@ public class JettyAnnotatedEventDriver extends AbstractEventDriver
|
|||
{
|
||||
this.policy.setIdleTimeout(anno.maxIdleTime());
|
||||
}
|
||||
this.batchMode = anno.batchMode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public BatchMode getBatchMode()
|
||||
{
|
||||
return this.batchMode;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -27,7 +27,6 @@ import java.util.concurrent.atomic.AtomicReference;
|
|||
import org.eclipse.jetty.util.StringUtil;
|
||||
import org.eclipse.jetty.util.log.Log;
|
||||
import org.eclipse.jetty.util.log.Logger;
|
||||
import org.eclipse.jetty.websocket.api.CloseStatus;
|
||||
import org.eclipse.jetty.websocket.api.StatusCode;
|
||||
import org.eclipse.jetty.websocket.common.CloseInfo;
|
||||
import org.eclipse.jetty.websocket.common.ConnectionState;
|
||||
|
@ -462,7 +461,6 @@ public class IOState
|
|||
}
|
||||
}
|
||||
|
||||
reason = CloseStatus.trimMaxReasonLength(reason);
|
||||
CloseInfo close = new CloseInfo(StatusCode.ABNORMAL,reason);
|
||||
|
||||
finalClose.compareAndSet(null,close);
|
||||
|
@ -514,7 +512,6 @@ public class IOState
|
|||
}
|
||||
}
|
||||
|
||||
reason = CloseStatus.trimMaxReasonLength(reason);
|
||||
CloseInfo close = new CloseInfo(StatusCode.ABNORMAL,reason);
|
||||
|
||||
finalClose.compareAndSet(null,close);
|
||||
|
|
Loading…
Reference in New Issue