357178 websockets draft 14 support
This commit is contained in:
parent
675464e82d
commit
4c885ca3ad
|
@ -42,7 +42,8 @@ import org.eclipse.jetty.websocket.WebSocket.OnTextMessage;
|
||||||
public class WebSocketConnectionD13 extends AbstractConnection implements WebSocketConnection
|
public class WebSocketConnectionD13 extends AbstractConnection implements WebSocketConnection
|
||||||
{
|
{
|
||||||
private static final Logger LOG = Log.getLogger(WebSocketConnectionD13.class);
|
private static final Logger LOG = Log.getLogger(WebSocketConnectionD13.class);
|
||||||
private static final boolean STRICT=true;
|
private static final boolean STRICT=Boolean.getBoolean("org.eclipse.jetty.websocket.STRICT");
|
||||||
|
private static final boolean BRUTAL=Boolean.getBoolean("org.eclipse.jetty.websocket.BRUTAL");
|
||||||
|
|
||||||
final static byte OP_CONTINUATION = 0x00;
|
final static byte OP_CONTINUATION = 0x00;
|
||||||
final static byte OP_TEXT = 0x01;
|
final static byte OP_TEXT = 0x01;
|
||||||
|
@ -62,7 +63,7 @@ public class WebSocketConnectionD13 extends AbstractConnection implements WebSoc
|
||||||
final static int CLOSE_UNDEFINED=1004;
|
final static int CLOSE_UNDEFINED=1004;
|
||||||
final static int CLOSE_NO_CODE=1005;
|
final static int CLOSE_NO_CODE=1005;
|
||||||
final static int CLOSE_NO_CLOSE=1006;
|
final static int CLOSE_NO_CLOSE=1006;
|
||||||
final static int CLOSE_NOT_UTF8=1007;
|
final static int CLOSE_BAD_PAYLOAD=1007;
|
||||||
final static int CLOSE_POLICY_VIOLATION=1008;
|
final static int CLOSE_POLICY_VIOLATION=1008;
|
||||||
final static int CLOSE_MESSAGE_TOO_LARGE=1009;
|
final static int CLOSE_MESSAGE_TOO_LARGE=1009;
|
||||||
final static int CLOSE_REQUIRED_EXTENSION=1010;
|
final static int CLOSE_REQUIRED_EXTENSION=1010;
|
||||||
|
@ -641,19 +642,13 @@ public class WebSocketConnectionD13 extends AbstractConnection implements WebSoc
|
||||||
{
|
{
|
||||||
if (isControlFrame(opcode) && buffer.length()>125)
|
if (isControlFrame(opcode) && buffer.length()>125)
|
||||||
{
|
{
|
||||||
_connection.close(WebSocketConnectionD13.CLOSE_PROTOCOL,"Control frame too large");
|
errorClose(WebSocketConnectionD13.CLOSE_PROTOCOL,"Control frame too large");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((flags&0x7)!=0)
|
if ((flags&0x7)!=0)
|
||||||
{
|
{
|
||||||
_connection.close(WebSocketConnectionD13.CLOSE_PROTOCOL,"RSV bits set 0x"+Integer.toHexString(flags));
|
errorClose(WebSocketConnectionD13.CLOSE_PROTOCOL,"RSV bits set 0x"+Integer.toHexString(flags));
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (_opcode!=-1 && opcode!=WebSocketConnectionD13.OP_CONTINUATION)
|
|
||||||
{
|
|
||||||
_connection.close(WebSocketConnectionD13.CLOSE_PROTOCOL,"Bad continuation"+Integer.toHexString(opcode));
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -681,7 +676,7 @@ public class WebSocketConnectionD13 extends AbstractConnection implements WebSoc
|
||||||
{
|
{
|
||||||
if (_opcode==-1)
|
if (_opcode==-1)
|
||||||
{
|
{
|
||||||
_connection.close(WebSocketConnectionD13.CLOSE_PROTOCOL,"Bad Continuation");
|
errorClose(WebSocketConnectionD13.CLOSE_PROTOCOL,"Bad Continuation");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -756,6 +751,12 @@ public class WebSocketConnectionD13 extends AbstractConnection implements WebSoc
|
||||||
|
|
||||||
case WebSocketConnectionD13.OP_TEXT:
|
case WebSocketConnectionD13.OP_TEXT:
|
||||||
{
|
{
|
||||||
|
if (STRICT && _opcode!=-1)
|
||||||
|
{
|
||||||
|
errorClose(WebSocketConnectionD13.CLOSE_PROTOCOL,"Expected Continuation"+Integer.toHexString(opcode));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if(_onTextMessage!=null)
|
if(_onTextMessage!=null)
|
||||||
{
|
{
|
||||||
if (_connection.getMaxTextMessageSize()<=0)
|
if (_connection.getMaxTextMessageSize()<=0)
|
||||||
|
@ -766,7 +767,7 @@ public class WebSocketConnectionD13 extends AbstractConnection implements WebSoc
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
LOG.warn("Frame discarded. Text aggregation disabled for {}",_endp);
|
LOG.warn("Frame discarded. Text aggregation disabled for {}",_endp);
|
||||||
_connection.close(WebSocketConnectionD13.CLOSE_POLICY_VIOLATION,"Text frame aggregation disabled");
|
errorClose(WebSocketConnectionD13.CLOSE_POLICY_VIOLATION,"Text frame aggregation disabled");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// append bytes to message buffer (if they fit)
|
// append bytes to message buffer (if they fit)
|
||||||
|
@ -791,6 +792,12 @@ public class WebSocketConnectionD13 extends AbstractConnection implements WebSoc
|
||||||
|
|
||||||
case WebSocketConnectionD13.OP_BINARY:
|
case WebSocketConnectionD13.OP_BINARY:
|
||||||
{
|
{
|
||||||
|
if (STRICT && _opcode!=-1)
|
||||||
|
{
|
||||||
|
errorClose(WebSocketConnectionD13.CLOSE_PROTOCOL,"Expected Continuation"+Integer.toHexString(opcode));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (_onBinaryMessage!=null && checkBinaryMessageSize(0,buffer.length()))
|
if (_onBinaryMessage!=null && checkBinaryMessageSize(0,buffer.length()))
|
||||||
{
|
{
|
||||||
if (lastFrame)
|
if (lastFrame)
|
||||||
|
@ -808,7 +815,7 @@ public class WebSocketConnectionD13 extends AbstractConnection implements WebSoc
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
LOG.warn("Frame discarded. Binary aggregation disabed for {}",_endp);
|
LOG.warn("Frame discarded. Binary aggregation disabed for {}",_endp);
|
||||||
_connection.close(WebSocketConnectionD13.CLOSE_POLICY_VIOLATION,"Binary frame aggregation disabled");
|
errorClose(WebSocketConnectionD13.CLOSE_POLICY_VIOLATION,"Binary frame aggregation disabled");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
@ -816,17 +823,15 @@ public class WebSocketConnectionD13 extends AbstractConnection implements WebSoc
|
||||||
|
|
||||||
default:
|
default:
|
||||||
if (STRICT)
|
if (STRICT)
|
||||||
_connection.close(WebSocketConnectionD13.CLOSE_PROTOCOL,"Bad opcode 0x"+Integer.toHexString(opcode));
|
errorClose(WebSocketConnectionD13.CLOSE_PROTOCOL,"Bad opcode 0x"+Integer.toHexString(opcode));
|
||||||
return;
|
return;
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch(Utf8Appendable.NotUtf8Exception notUtf8)
|
catch(Utf8Appendable.NotUtf8Exception notUtf8)
|
||||||
{
|
{
|
||||||
LOG.warn(notUtf8);
|
|
||||||
LOG.warn("{} for {}",notUtf8,_endp);
|
LOG.warn("{} for {}",notUtf8,_endp);
|
||||||
LOG.debug(notUtf8);
|
LOG.debug(notUtf8);
|
||||||
_connection.close(WebSocketConnectionD13.CLOSE_NOT_UTF8,"Invalid UTF-8");
|
errorClose(WebSocketConnectionD13.CLOSE_BAD_PAYLOAD,"Invalid UTF-8");
|
||||||
}
|
}
|
||||||
catch(ThreadDeath th)
|
catch(ThreadDeath th)
|
||||||
{
|
{
|
||||||
|
@ -838,6 +843,23 @@ public class WebSocketConnectionD13 extends AbstractConnection implements WebSoc
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void errorClose(int code, String message)
|
||||||
|
{
|
||||||
|
_connection.close(code,message);
|
||||||
|
if (BRUTAL)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
_endp.close();
|
||||||
|
}
|
||||||
|
catch (IOException e)
|
||||||
|
{
|
||||||
|
LOG.warn(e.toString());
|
||||||
|
LOG.debug(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private boolean checkBinaryMessageSize(int bufferLen, int length)
|
private boolean checkBinaryMessageSize(int bufferLen, int length)
|
||||||
{
|
{
|
||||||
int max = _connection.getMaxBinaryMessageSize();
|
int max = _connection.getMaxBinaryMessageSize();
|
||||||
|
|
|
@ -227,11 +227,13 @@ public class WebSocketFactory
|
||||||
connection = new WebSocketConnectionD12(websocket, endp, _buffers, http.getTimeStamp(), _maxIdleTime, protocol,extensions,draft);
|
connection = new WebSocketConnectionD12(websocket, endp, _buffers, http.getTimeStamp(), _maxIdleTime, protocol,extensions,draft);
|
||||||
break;
|
break;
|
||||||
case 13:
|
case 13:
|
||||||
|
case 14:
|
||||||
extensions= initExtensions(extensions_requested,8-WebSocketConnectionD13.OP_EXT_DATA, 16-WebSocketConnectionD13.OP_EXT_CTRL,3);
|
extensions= initExtensions(extensions_requested,8-WebSocketConnectionD13.OP_EXT_DATA, 16-WebSocketConnectionD13.OP_EXT_CTRL,3);
|
||||||
connection = new WebSocketConnectionD13(websocket, endp, _buffers, http.getTimeStamp(), _maxIdleTime, protocol,extensions,draft);
|
connection = new WebSocketConnectionD13(websocket, endp, _buffers, http.getTimeStamp(), _maxIdleTime, protocol,extensions,draft);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
LOG.warn("Unsupported Websocket version: "+draft);
|
LOG.warn("Unsupported Websocket version: "+draft);
|
||||||
|
response.setHeader("Sec-WebSocket-Version","0,6,12,13,14");
|
||||||
throw new HttpException(400, "Unsupported draft specification: " + draft);
|
throw new HttpException(400, "Unsupported draft specification: " + draft);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -251,6 +253,8 @@ public class WebSocketFactory
|
||||||
request.setAttribute("org.eclipse.jetty.io.Connection", connection);
|
request.setAttribute("org.eclipse.jetty.io.Connection", connection);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*/
|
||||||
protected String[] parseProtocols(String protocol)
|
protected String[] parseProtocols(String protocol)
|
||||||
{
|
{
|
||||||
if (protocol == null)
|
if (protocol == null)
|
||||||
|
@ -264,6 +268,8 @@ public class WebSocketFactory
|
||||||
return protocols;
|
return protocols;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*/
|
||||||
public boolean acceptWebSocket(HttpServletRequest request, HttpServletResponse response)
|
public boolean acceptWebSocket(HttpServletRequest request, HttpServletResponse response)
|
||||||
throws IOException
|
throws IOException
|
||||||
{
|
{
|
||||||
|
@ -280,10 +286,13 @@ public class WebSocketFactory
|
||||||
|
|
||||||
// Try each requested protocol
|
// Try each requested protocol
|
||||||
WebSocket websocket = null;
|
WebSocket websocket = null;
|
||||||
String protocol = request.getHeader("Sec-WebSocket-Protocol");
|
|
||||||
if (protocol == null) // TODO remove once draft period is over
|
Enumeration<String> protocols = request.getHeaders("Sec-WebSocket-Protocol");
|
||||||
protocol = request.getHeader("WebSocket-Protocol");
|
String protocol=null;
|
||||||
for (String p : parseProtocols(protocol))
|
while (protocol==null && protocols!=null && protocols.hasMoreElements())
|
||||||
|
{
|
||||||
|
String candidate = protocols.nextElement();
|
||||||
|
for (String p : parseProtocols(candidate))
|
||||||
{
|
{
|
||||||
websocket = _acceptor.doWebSocketConnect(request, p);
|
websocket = _acceptor.doWebSocketConnect(request, p);
|
||||||
if (websocket != null)
|
if (websocket != null)
|
||||||
|
@ -292,13 +301,20 @@ public class WebSocketFactory
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Did we get a websocket?
|
// Did we get a websocket?
|
||||||
|
if (websocket == null)
|
||||||
|
{
|
||||||
|
// Try with no protocol
|
||||||
|
websocket = _acceptor.doWebSocketConnect(request, null);
|
||||||
|
|
||||||
if (websocket==null)
|
if (websocket==null)
|
||||||
{
|
{
|
||||||
response.sendError(HttpServletResponse.SC_SERVICE_UNAVAILABLE);
|
response.sendError(HttpServletResponse.SC_SERVICE_UNAVAILABLE);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Send the upgrade
|
// Send the upgrade
|
||||||
upgrade(request, response, websocket, protocol);
|
upgrade(request, response, websocket, protocol);
|
||||||
|
@ -308,6 +324,8 @@ public class WebSocketFactory
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*/
|
||||||
public List<Extension> initExtensions(List<String> requested,int maxDataOpcodes,int maxControlOpcodes,int maxReservedBits)
|
public List<Extension> initExtensions(List<String> requested,int maxDataOpcodes,int maxControlOpcodes,int maxReservedBits)
|
||||||
{
|
{
|
||||||
List<Extension> extensions = new ArrayList<Extension>();
|
List<Extension> extensions = new ArrayList<Extension>();
|
||||||
|
@ -339,6 +357,8 @@ public class WebSocketFactory
|
||||||
return extensions;
|
return extensions;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*/
|
||||||
private Extension newExtension(String name)
|
private Extension newExtension(String name)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
|
@ -354,6 +374,4 @@ public class WebSocketFactory
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -84,6 +84,7 @@ public class WebSocketMessageD00Test
|
||||||
InputStream input = socket.getInputStream();
|
InputStream input = socket.getInputStream();
|
||||||
BufferedReader reader = new BufferedReader(new InputStreamReader(input, "ISO-8859-1"));
|
BufferedReader reader = new BufferedReader(new InputStreamReader(input, "ISO-8859-1"));
|
||||||
String responseLine = reader.readLine();
|
String responseLine = reader.readLine();
|
||||||
|
System.err.println(responseLine);
|
||||||
assertTrue(responseLine.startsWith("HTTP/1.1 101 WebSocket Protocol Handshake"));
|
assertTrue(responseLine.startsWith("HTTP/1.1 101 WebSocket Protocol Handshake"));
|
||||||
// Read until we find an empty line, which signals the end of the http response
|
// Read until we find an empty line, which signals the end of the http response
|
||||||
String line;
|
String line;
|
||||||
|
|
|
@ -998,7 +998,7 @@ public class WebSocketMessageD13Test
|
||||||
assertEquals(0x80|WebSocketConnectionD13.OP_CLOSE,input.read());
|
assertEquals(0x80|WebSocketConnectionD13.OP_CLOSE,input.read());
|
||||||
assertEquals(15,input.read());
|
assertEquals(15,input.read());
|
||||||
int code=(0xff&input.read())*0x100+(0xff&input.read());
|
int code=(0xff&input.read())*0x100+(0xff&input.read());
|
||||||
assertEquals(WebSocketConnectionD13.CLOSE_NOT_UTF8,code);
|
assertEquals(WebSocketConnectionD13.CLOSE_BAD_PAYLOAD,code);
|
||||||
lookFor("Invalid UTF-8",input);
|
lookFor("Invalid UTF-8",input);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue