Merge branch 'jetty-9' of ssh://git.eclipse.org/gitroot/jetty/org.eclipse.jetty.project into jetty-9

This commit is contained in:
Joakim Erdfelt 2012-07-02 15:59:44 -07:00
commit 7d4da60d05
9 changed files with 275 additions and 28 deletions

View File

@ -14,7 +14,7 @@
<modules> <modules>
<module>websocket-core</module> <module>websocket-core</module>
<module>websocket-client</module> <!-- <module>websocket-client</module> -->
<module>websocket-server</module> <module>websocket-server</module>
</modules> </modules>

View File

@ -3,27 +3,27 @@ package org.eclipse.jetty.websocket.api;
@SuppressWarnings("serial") @SuppressWarnings("serial")
public class CloseException extends WebSocketException public class CloseException extends WebSocketException
{ {
private short statusCode; private int statusCode;
public CloseException(short closeCode, String message) public CloseException(int closeCode, String message)
{ {
super(message); super(message);
this.statusCode = closeCode; this.statusCode = closeCode;
} }
public CloseException(short closeCode, String message, Throwable cause) public CloseException(int closeCode, String message, Throwable cause)
{ {
super(message,cause); super(message,cause);
this.statusCode = closeCode; this.statusCode = closeCode;
} }
public CloseException(short closeCode, Throwable cause) public CloseException(int closeCode, Throwable cause)
{ {
super(cause); super(cause);
this.statusCode = closeCode; this.statusCode = closeCode;
} }
public short getStatusCode() public int getStatusCode()
{ {
return statusCode; return statusCode;
} }

View File

@ -10,53 +10,53 @@ public class StatusCode
* <p> * <p>
* See <a href="https://tools.ietf.org/html/rfc6455#section-7.4.1">RFC 6455, Section 7.4.1 Defined Status Codes</a>. * See <a href="https://tools.ietf.org/html/rfc6455#section-7.4.1">RFC 6455, Section 7.4.1 Defined Status Codes</a>.
*/ */
public final static short NORMAL = 1000; public final static int NORMAL = 1000;
/** /**
* 1001 indicates that an endpoint is "going away", such as a server going down or a browser having navigated away from a page. * 1001 indicates that an endpoint is "going away", such as a server going down or a browser having navigated away from a page.
* <p> * <p>
* See <a href="https://tools.ietf.org/html/rfc6455#section-7.4.1">RFC 6455, Section 7.4.1 Defined Status Codes</a>. * See <a href="https://tools.ietf.org/html/rfc6455#section-7.4.1">RFC 6455, Section 7.4.1 Defined Status Codes</a>.
*/ */
public final static short SHUTDOWN = 1001; public final static int SHUTDOWN = 1001;
/** /**
* 1002 indicates that an endpoint is terminating the connection due to a protocol error. * 1002 indicates that an endpoint is terminating the connection due to a protocol error.
* <p> * <p>
* See <a href="https://tools.ietf.org/html/rfc6455#section-7.4.1">RFC 6455, Section 7.4.1 Defined Status Codes</a>. * See <a href="https://tools.ietf.org/html/rfc6455#section-7.4.1">RFC 6455, Section 7.4.1 Defined Status Codes</a>.
*/ */
public final static short PROTOCOL = 1002; public final static int PROTOCOL = 1002;
/** /**
* 1003 indicates that an endpoint is terminating the connection because it has received a type of data it cannot accept (e.g., an endpoint that understands * 1003 indicates that an endpoint is terminating the connection because it has received a type of data it cannot accept (e.g., an endpoint that understands
* only text data MAY send this if it receives a binary message). * only text data MAY send this if it receives a binary message).
* <p> * <p>
* See <a href="https://tools.ietf.org/html/rfc6455#section-7.4.1">RFC 6455, Section 7.4.1 Defined Status Codes</a>. * See <a href="https://tools.ietf.org/html/rfc6455#section-7.4.1">RFC 6455, Section 7.4.1 Defined Status Codes</a>.
*/ */
public final static short BAD_DATA = 1003; public final static int BAD_DATA = 1003;
/** /**
* Reserved. The specific meaning might be defined in the future. * Reserved. The specific meaning might be defined in the future.
* <p> * <p>
* See <a href="https://tools.ietf.org/html/rfc6455#section-7.4.1">RFC 6455, Section 7.4.1 Defined Status Codes</a>. * See <a href="https://tools.ietf.org/html/rfc6455#section-7.4.1">RFC 6455, Section 7.4.1 Defined Status Codes</a>.
*/ */
public final static short UNDEFINED = 1004; public final static int UNDEFINED = 1004;
/** /**
* 1005 is a reserved value and MUST NOT be set as a status code in a Close control frame by an endpoint. It is designated for use in applications expecting * 1005 is a reserved value and MUST NOT be set as a status code in a Close control frame by an endpoint. It is designated for use in applications expecting
* a status code to indicate that no status code was actually present. * a status code to indicate that no status code was actually present.
* <p> * <p>
* See <a href="https://tools.ietf.org/html/rfc6455#section-7.4.1">RFC 6455, Section 7.4.1 Defined Status Codes</a>. * See <a href="https://tools.ietf.org/html/rfc6455#section-7.4.1">RFC 6455, Section 7.4.1 Defined Status Codes</a>.
*/ */
public final static short NO_CODE = 1005; public final static int NO_CODE = 1005;
/** /**
* 1006 is a reserved value and MUST NOT be set as a status code in a Close control frame by an endpoint. It is designated for use in applications expecting * 1006 is a reserved value and MUST NOT be set as a status code in a Close control frame by an endpoint. It is designated for use in applications expecting
* a status code to indicate that the connection was closed abnormally, e.g., without sending or receiving a Close control frame. * a status code to indicate that the connection was closed abnormally, e.g., without sending or receiving a Close control frame.
* <p> * <p>
* See <a href="https://tools.ietf.org/html/rfc6455#section-7.4.1">RFC 6455, Section 7.4.1 Defined Status Codes</a>. * See <a href="https://tools.ietf.org/html/rfc6455#section-7.4.1">RFC 6455, Section 7.4.1 Defined Status Codes</a>.
*/ */
public final static short NO_CLOSE = 1006; public final static int NO_CLOSE = 1006;
/** /**
* 1007 indicates that an endpoint is terminating the connection because it has received data within a message that was not consistent with the type of the * 1007 indicates that an endpoint is terminating the connection because it has received data within a message that was not consistent with the type of the
* message (e.g., non-UTF-8 [<a href="https://tools.ietf.org/html/rfc3629">RFC3629</a>] data within a text message). * message (e.g., non-UTF-8 [<a href="https://tools.ietf.org/html/rfc3629">RFC3629</a>] data within a text message).
* <p> * <p>
* See <a href="https://tools.ietf.org/html/rfc6455#section-7.4.1">RFC 6455, Section 7.4.1 Defined Status Codes</a>. * See <a href="https://tools.ietf.org/html/rfc6455#section-7.4.1">RFC 6455, Section 7.4.1 Defined Status Codes</a>.
*/ */
public final static short BAD_PAYLOAD = 1007; public final static int BAD_PAYLOAD = 1007;
/** /**
* 1008 indicates that an endpoint is terminating the connection because it has received a message that violates its policy. This is a generic status code * 1008 indicates that an endpoint is terminating the connection because it has received a message that violates its policy. This is a generic status code
* that can be returned when there is no other more suitable status code (e.g., 1003 or 1009) or if there is a need to hide specific details about the * that can be returned when there is no other more suitable status code (e.g., 1003 or 1009) or if there is a need to hide specific details about the
@ -64,13 +64,13 @@ public class StatusCode
* <p> * <p>
* See <a href="https://tools.ietf.org/html/rfc6455#section-7.4.1">RFC 6455, Section 7.4.1 Defined Status Codes</a>. * See <a href="https://tools.ietf.org/html/rfc6455#section-7.4.1">RFC 6455, Section 7.4.1 Defined Status Codes</a>.
*/ */
public final static short POLICY_VIOLATION = 1008; public final static int POLICY_VIOLATION = 1008;
/** /**
* 1009 indicates that an endpoint is terminating the connection because it has received a message that is too big for it to process. * 1009 indicates that an endpoint is terminating the connection because it has received a message that is too big for it to process.
* <p> * <p>
* See <a href="https://tools.ietf.org/html/rfc6455#section-7.4.1">RFC 6455, Section 7.4.1 Defined Status Codes</a>. * See <a href="https://tools.ietf.org/html/rfc6455#section-7.4.1">RFC 6455, Section 7.4.1 Defined Status Codes</a>.
*/ */
public final static short MESSAGE_TOO_LARGE = 1009; public final static int MESSAGE_TOO_LARGE = 1009;
/** /**
* 1010 indicates that an endpoint (client) is terminating the connection because it has expected the server to negotiate one or more extension, but the * 1010 indicates that an endpoint (client) is terminating the connection because it has expected the server to negotiate one or more extension, but the
* server didn't return them in the response message of the WebSocket handshake. The list of extensions that are needed SHOULD appear in the /reason/ part * server didn't return them in the response message of the WebSocket handshake. The list of extensions that are needed SHOULD appear in the /reason/ part
@ -78,18 +78,18 @@ public class StatusCode
* <p> * <p>
* See <a href="https://tools.ietf.org/html/rfc6455#section-7.4.1">RFC 6455, Section 7.4.1 Defined Status Codes</a>. * See <a href="https://tools.ietf.org/html/rfc6455#section-7.4.1">RFC 6455, Section 7.4.1 Defined Status Codes</a>.
*/ */
public final static short REQUIRED_EXTENSION = 1010; public final static int REQUIRED_EXTENSION = 1010;
/** /**
* 1011 indicates that a server is terminating the connection because it encountered an unexpected condition that prevented it from fulfilling the request. * 1011 indicates that a server is terminating the connection because it encountered an unexpected condition that prevented it from fulfilling the request.
* <p> * <p>
* See <a href="https://tools.ietf.org/html/rfc6455#section-7.4.1">RFC 6455, Section 7.4.1 Defined Status Codes</a>. * See <a href="https://tools.ietf.org/html/rfc6455#section-7.4.1">RFC 6455, Section 7.4.1 Defined Status Codes</a>.
*/ */
public final static short SERVER_ERROR = 1011; public final static int SERVER_ERROR = 1011;
/** /**
* 1015 is a reserved value and MUST NOT be set as a status code in a Close control frame by an endpoint. It is designated for use in applications expecting * 1015 is a reserved value and MUST NOT be set as a status code in a Close control frame by an endpoint. It is designated for use in applications expecting
* a status code to indicate that the connection was closed due to a failure to perform a TLS handshake (e.g., the server certificate can't be verified). * a status code to indicate that the connection was closed due to a failure to perform a TLS handshake (e.g., the server certificate can't be verified).
* <p> * <p>
* See <a href="https://tools.ietf.org/html/rfc6455#section-7.4.1">RFC 6455, Section 7.4.1 Defined Status Codes</a>. * See <a href="https://tools.ietf.org/html/rfc6455#section-7.4.1">RFC 6455, Section 7.4.1 Defined Status Codes</a>.
*/ */
public final static short FAILED_TLS_HANDSHAKE = 1015; public final static int FAILED_TLS_HANDSHAKE = 1015;
} }

View File

@ -201,7 +201,7 @@ public class WebSocketEventDriver implements Parser.Listener
reason = reason.substring(0,CloseFrame.MAX_REASON); reason = reason.substring(0,CloseFrame.MAX_REASON);
} }
} }
LOG.debug("terminateConnection({},{})",statusCode,reason); LOG.debug("terminateConnection({},{})",statusCode,rawreason);
connection.close(statusCode,reason); connection.close(statusCode,reason);
} }
catch (IOException e) catch (IOException e)

View File

@ -4,7 +4,6 @@ import java.nio.ByteBuffer;
import org.eclipse.jetty.websocket.api.OpCode; import org.eclipse.jetty.websocket.api.OpCode;
import org.eclipse.jetty.websocket.api.ProtocolException; import org.eclipse.jetty.websocket.api.ProtocolException;
import org.eclipse.jetty.websocket.api.WebSocketException;
public abstract class ControlFrame extends BaseFrame public abstract class ControlFrame extends BaseFrame
{ {
@ -40,9 +39,9 @@ public abstract class ControlFrame extends BaseFrame
@Override @Override
public void setPayload(byte[] buf) public void setPayload(byte[] buf)
{ {
if ( buf.length > 125 ) if (buf.length > MAX_PAYLOAD)
{ {
throw new WebSocketException("Control Payloads can not exceed 125 bytes in length."); throw new ProtocolException("Control Payloads can not exceed 125 bytes in length.");
} }
super.setPayload(buf); super.setPayload(buf);

View File

@ -17,7 +17,9 @@ import org.eclipse.jetty.util.Callback;
import org.eclipse.jetty.util.FutureCallback; import org.eclipse.jetty.util.FutureCallback;
import org.eclipse.jetty.util.log.Log; import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger; import org.eclipse.jetty.util.log.Logger;
import org.eclipse.jetty.websocket.api.CloseException;
import org.eclipse.jetty.websocket.api.ExtensionConfig; import org.eclipse.jetty.websocket.api.ExtensionConfig;
import org.eclipse.jetty.websocket.api.ProtocolException;
import org.eclipse.jetty.websocket.api.StatusCode; import org.eclipse.jetty.websocket.api.StatusCode;
import org.eclipse.jetty.websocket.api.WebSocketConnection; import org.eclipse.jetty.websocket.api.WebSocketConnection;
import org.eclipse.jetty.websocket.api.WebSocketPolicy; import org.eclipse.jetty.websocket.api.WebSocketPolicy;
@ -186,6 +188,7 @@ public class WebSocketAsyncConnection extends AbstractAsyncConnection implements
terminateConnection(StatusCode.PROTOCOL,null); terminateConnection(StatusCode.PROTOCOL,null);
break; break;
} }
parser.parse(buffer); parser.parse(buffer);
} }
} }

View File

@ -9,6 +9,7 @@ import java.util.concurrent.CopyOnWriteArrayList;
import org.eclipse.jetty.util.log.Log; import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger; import org.eclipse.jetty.util.log.Logger;
import org.eclipse.jetty.websocket.api.OpCode; import org.eclipse.jetty.websocket.api.OpCode;
import org.eclipse.jetty.websocket.api.ProtocolException;
import org.eclipse.jetty.websocket.api.WebSocketException; import org.eclipse.jetty.websocket.api.WebSocketException;
import org.eclipse.jetty.websocket.api.WebSocketPolicy; import org.eclipse.jetty.websocket.api.WebSocketPolicy;
import org.eclipse.jetty.websocket.frames.BaseFrame; import org.eclipse.jetty.websocket.frames.BaseFrame;
@ -125,14 +126,14 @@ public class Parser
if (opcode.isControlFrame() && !fin) if (opcode.isControlFrame() && !fin)
{ {
throw new WebSocketException("Fragmented Control Frame [" + opcode.name() + "]"); throw new ProtocolException("Fragmented Control Frame [" + opcode.name() + "]");
} }
if (opcode == OpCode.CONTINUATION) if (opcode == OpCode.CONTINUATION)
{ {
if (parser == null) if (parser == null)
{ {
throw new WebSocketException("Fragment continuation frame without prior !FIN"); throw new ProtocolException("Fragment continuation frame without prior !FIN");
} }
} }

View File

@ -1,6 +1,6 @@
package org.eclipse.jetty.websocket.parser; package org.eclipse.jetty.websocket.parser;
import static org.hamcrest.Matchers.*; import static org.hamcrest.Matchers.is;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
@ -22,7 +22,7 @@ public class ClosePayloadParserTest
byte utf[] = expectedReason.getBytes(StringUtil.__UTF8_CHARSET); byte utf[] = expectedReason.getBytes(StringUtil.__UTF8_CHARSET);
ByteBuffer payload = ByteBuffer.allocate(utf.length + 2); ByteBuffer payload = ByteBuffer.allocate(utf.length + 2);
payload.putShort(StatusCode.NORMAL); payload.putChar((char)StatusCode.NORMAL);
payload.put(utf,0,utf.length); payload.put(utf,0,utf.length);
payload.flip(); payload.flip();
@ -42,7 +42,7 @@ public class ClosePayloadParserTest
capture.assertNoErrors(); capture.assertNoErrors();
capture.assertHasFrame(CloseFrame.class,1); capture.assertHasFrame(CloseFrame.class,1);
CloseFrame close = (CloseFrame)capture.getFrames().get(0); CloseFrame close = (CloseFrame)capture.getFrames().get(0);
Assert.assertThat("CloseFrame.statusCode",(short)close.getStatusCode(),is(StatusCode.NORMAL)); Assert.assertThat("CloseFrame.statusCode",close.getStatusCode(),is(StatusCode.NORMAL));
Assert.assertThat("CloseFrame.data",close.getReason(),is(expectedReason)); Assert.assertThat("CloseFrame.data",close.getReason(),is(expectedReason));
} }
} }

View File

@ -0,0 +1,244 @@
package org.eclipse.jetty.websocket.server.ab;
import static org.hamcrest.Matchers.*;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Queue;
import java.util.concurrent.TimeUnit;
import org.eclipse.jetty.util.BufferUtil;
import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger;
import org.eclipse.jetty.websocket.api.OpCode;
import org.eclipse.jetty.websocket.api.WebSocketAdapter;
import org.eclipse.jetty.websocket.frames.BaseFrame;
import org.eclipse.jetty.websocket.frames.CloseFrame;
import org.eclipse.jetty.websocket.frames.PingFrame;
import org.eclipse.jetty.websocket.frames.TextFrame;
import org.eclipse.jetty.websocket.generator.FrameGenerator;
import org.eclipse.jetty.websocket.server.SimpleServletServer;
import org.eclipse.jetty.websocket.server.WebSocketServerFactory;
import org.eclipse.jetty.websocket.server.WebSocketServlet;
import org.eclipse.jetty.websocket.server.blockhead.BlockheadClient;
import org.eclipse.jetty.websocket.server.examples.MyEchoServlet;
import org.junit.AfterClass;
import org.junit.Assert;
import org.junit.BeforeClass;
import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
import org.junit.runners.Parameterized.Parameters;
public class TestABCase5
{
@SuppressWarnings("serial")
public static class RFCServlet extends WebSocketServlet
{
@Override
public void registerWebSockets(WebSocketServerFactory factory)
{
factory.register(RFCSocket.class);
}
}
public static class RFCSocket extends WebSocketAdapter
{
private static Logger LOG = Log.getLogger(RFCSocket.class);
@Override
public void onWebSocketText(String message)
{
LOG.debug("onWebSocketText({})",message);
// Test the RFC 6455 close code 1011 that should close
// trigger a WebSocket server terminated close.
if (message.equals("CRASH"))
{
System.out.printf("Got OnTextMessage");
throw new RuntimeException("Something bad happened");
}
// echo the message back.
try
{
getConnection().write(message);
}
catch (IOException e)
{
e.printStackTrace(System.err);
}
}
}
private static SimpleServletServer server;
@BeforeClass
public static void startServer() throws Exception
{
server = new SimpleServletServer(new MyEchoServlet());
server.start();
}
@AfterClass
public static void stopServer()
{
server.stop();
}
@Test
public void testCase5_1PingIn2Packets() throws Exception
{
BlockheadClient client = new BlockheadClient(server.getServerUri());
try
{
client.connect();
client.sendStandardRequest();
client.expectUpgradeResponse();
ByteBuffer buf = ByteBuffer.allocate(FrameGenerator.OVERHEAD + 2);
BufferUtil.clearToFill(buf);
String fragment1 = "fragment1";
buf.put((byte)(0x00 | OpCode.PING.getCode()));
byte b = 0x00; // no masking
b |= fragment1.length() & 0x7F;
buf.put(b);
buf.put(fragment1.getBytes());
BufferUtil.flipToFlush(buf,0);
client.writeRaw(buf);
ByteBuffer buf2 = ByteBuffer.allocate(FrameGenerator.OVERHEAD + 2);
BufferUtil.clearToFill(buf2);
String fragment2 = "fragment2";
buf2.put((byte)(0x80 | OpCode.PING.getCode()));
b = 0x00; // no masking
b |= fragment2.length() & 0x7F;
buf2.put(b);
buf2.put(fragment2.getBytes());
BufferUtil.flipToFlush(buf2,0);
client.writeRaw(buf2);
// Read frame (hopefully text frame)
Queue<BaseFrame> frames = client.readFrames(1,TimeUnit.MILLISECONDS,500);
CloseFrame closeFrame = (CloseFrame)frames.remove();
Assert.assertThat("CloseFrame.status code",closeFrame.getStatusCode(),is(1002));
}
finally
{
client.close();
}
}
@Test
public void testCase5_2PongIn2Packets() throws Exception
{
BlockheadClient client = new BlockheadClient(server.getServerUri());
try
{
client.connect();
client.sendStandardRequest();
client.expectUpgradeResponse();
ByteBuffer buf = ByteBuffer.allocate(FrameGenerator.OVERHEAD + 2);
BufferUtil.clearToFill(buf);
String fragment1 = "fragment1";
buf.put((byte)(0x00 | OpCode.PONG.getCode()));
byte b = 0x00; // no masking
b |= fragment1.length() & 0x7F;
buf.put(b);
buf.put(fragment1.getBytes());
BufferUtil.flipToFlush(buf,0);
client.writeRaw(buf);
ByteBuffer buf2 = ByteBuffer.allocate(FrameGenerator.OVERHEAD + 2);
BufferUtil.clearToFill(buf2);
String fragment2 = "fragment2";
buf2.put((byte)(0x80 | OpCode.CONTINUATION.getCode()));
b = 0x00; // no masking
b |= fragment2.length() & 0x7F;
buf2.put(b);
buf2.put(fragment2.getBytes());
BufferUtil.flipToFlush(buf2,0);
client.writeRaw(buf2);
// Read frame (hopefully text frame)
Queue<BaseFrame> frames = client.readFrames(1,TimeUnit.MILLISECONDS,500);
CloseFrame closeFrame = (CloseFrame)frames.remove();
Assert.assertThat("CloseFrame.status code",closeFrame.getStatusCode(),is(1002));
}
finally
{
client.close();
}
}
@Test
@Ignore ("not re-assembling the strings, something odd with echo socket")
public void testCase5_3TextIn2Packets() throws Exception
{
BlockheadClient client = new BlockheadClient(server.getServerUri());
try
{
client.connect();
client.sendStandardRequest();
client.expectUpgradeResponse();
ByteBuffer buf = ByteBuffer.allocate(FrameGenerator.OVERHEAD + 2);
BufferUtil.clearToFill(buf);
String fragment1 = "fragment1";
buf.put((byte)(0x00 | OpCode.TEXT.getCode()));
byte b = 0x00; // no masking
b |= fragment1.length() & 0x7F;
buf.put(b);
buf.put(fragment1.getBytes());
BufferUtil.flipToFlush(buf,0);
client.writeRaw(buf);
ByteBuffer buf2 = ByteBuffer.allocate(FrameGenerator.OVERHEAD + 2);
BufferUtil.clearToFill(buf2);
String fragment2 = "fragment2";
buf2.put((byte)(0x80 | OpCode.CONTINUATION.getCode()));
b = 0x00; // no masking
b |= fragment2.length() & 0x7F;
buf2.put(b);
buf2.put(fragment2.getBytes());
BufferUtil.flipToFlush(buf2,0);
client.writeRaw(buf2);
// Read frame (hopefully text frame)
Queue<BaseFrame> frames = client.readFrames(1,TimeUnit.MILLISECONDS,500);
TextFrame textFrame = (TextFrame)frames.remove();
Assert.assertThat("TextFrame.payload",textFrame.getPayloadUTF8(),is(fragment1 + fragment2));
}
finally
{
client.close();
}
}
}